diff -Naur vim82.orig/src/evalfunc.c vim82+userenc/src/evalfunc.c
--- vim82.orig/src/evalfunc.c	2019-12-01 20:10:25.000000000 +0000
+++ vim82+userenc/src/evalfunc.c	2021-11-21 04:18:31.241958000 +0000
@@ -3631,6 +3631,9 @@
 #endif
 	"user-commands",    // was accidentally included in 5.4
 	"user_commands",
+#ifdef FEAT_USERENC
+	"userenc",
+#endif
 #ifdef FEAT_VARTABS
 	"vartabs",
 #endif
diff -Naur vim82.orig/src/ex_cmdidxs.h vim82+userenc/src/ex_cmdidxs.h
--- vim82.orig/src/ex_cmdidxs.h	2019-08-16 20:40:07.000000000 +0000
+++ vim82+userenc/src/ex_cmdidxs.h	2021-11-21 04:18:31.241958000 +0000
@@ -10,27 +10,27 @@
   /* c */ 42,
   /* d */ 108,
   /* e */ 130,
-  /* f */ 151,
-  /* g */ 167,
-  /* h */ 173,
-  /* i */ 182,
-  /* j */ 200,
-  /* k */ 202,
-  /* l */ 207,
-  /* m */ 269,
-  /* n */ 287,
-  /* o */ 307,
-  /* p */ 319,
-  /* q */ 358,
-  /* r */ 361,
-  /* s */ 381,
-  /* t */ 450,
-  /* u */ 495,
-  /* v */ 506,
-  /* w */ 524,
-  /* x */ 538,
-  /* y */ 548,
-  /* z */ 549
+  /* f */ 153,
+  /* g */ 169,
+  /* h */ 175,
+  /* i */ 184,
+  /* j */ 202,
+  /* k */ 204,
+  /* l */ 209,
+  /* m */ 271,
+  /* n */ 289,
+  /* o */ 309,
+  /* p */ 321,
+  /* q */ 360,
+  /* r */ 363,
+  /* s */ 383,
+  /* t */ 452,
+  /* u */ 497,
+  /* v */ 508,
+  /* w */ 526,
+  /* x */ 540,
+  /* y */ 550,
+  /* z */ 551
 };
 
 /*
@@ -45,7 +45,7 @@
   /* b */ {  2,  0,  0,  4,  5,  7,  0,  0,  0,  0,  0,  8,  9, 10, 11, 12,  0, 13,  0,  0,  0,  0, 22,  0,  0,  0 },
   /* c */ {  3, 12, 16, 18, 20, 22, 25,  0,  0,  0,  0, 33, 37, 40, 46, 56, 58, 59, 60,  0, 62,  0, 65,  0,  0,  0 },
   /* d */ {  0,  0,  0,  0,  0,  0,  0,  0,  6, 15,  0, 16,  0,  0, 17,  0,  0, 19, 20,  0,  0,  0,  0,  0,  0,  0 },
-  /* e */ {  1,  0,  2,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9, 10,  0,  0,  0,  0,  0,  0,  0, 16,  0, 17,  0,  0 },
+  /* e */ {  1,  0,  2,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9, 10,  0,  0,  0,  0,  0,  0,  0, 18,  0, 19,  0,  0 },
   /* f */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  9,  0,  0,  0,  0,  0, 15,  0,  0,  0,  0,  0 },
   /* g */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  2,  0,  0,  4,  5,  0,  0,  0,  0 },
   /* h */ {  5,  0,  0,  0,  0,  0,  0,  0,  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
@@ -69,4 +69,4 @@
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };
 
-static const int command_count = 562;
+static const int command_count = 564;
diff -Naur vim82.orig/src/ex_cmds.h vim82+userenc/src/ex_cmds.h
--- vim82.orig/src/ex_cmds.h	2019-12-06 20:02:00.000000000 +0000
+++ vim82+userenc/src/ex_cmds.h	2021-11-21 04:18:31.241958000 +0000
@@ -526,6 +526,12 @@
 EXCMD(CMD_emenu,	"emenu",	ex_emenu,
 	EX_NEEDARG|EX_EXTRA|EX_TRLBAR|EX_NOTRLCOM|EX_RANGE|EX_CMDWIN,
 	ADDR_OTHER),
+EXCMD(CMD_encdef,	"encdef",	ex_encdef,
+	EX_NEEDARG|EX_EXTRA|EX_TRLBAR|EX_CMDWIN,
+	ADDR_NONE),
+EXCMD(CMD_encalias,	"encalias",	ex_encalias,
+	EX_NEEDARG|EX_EXTRA|EX_TRLBAR|EX_CMDWIN,
+	ADDR_NONE),
 EXCMD(CMD_endif,	"endif",	ex_endif,
 	EX_TRLBAR|EX_SBOXOK|EX_CMDWIN,
 	ADDR_NONE),
diff -Naur vim82.orig/src/feature.h vim82+userenc/src/feature.h
--- vim82.orig/src/feature.h	2019-12-01 14:23:54.000000000 +0000
+++ vim82+userenc/src/feature.h	2021-11-21 04:18:31.257550400 +0000
@@ -525,6 +525,10 @@
 # define USE_ICONV
 #endif
 
+#if defined(USE_ICONV)
+# define FEAT_USERENC
+#endif
+
 /*
  * +xim			X Input Method.  For entering special languages like
  *			chinese and Japanese.
diff -Naur vim82.orig/src/mbyte.c vim82+userenc/src/mbyte.c
--- vim82.orig/src/mbyte.c	2019-12-04 20:50:22.000000000 +0000
+++ vim82+userenc/src/mbyte.c	2021-11-21 04:18:31.257550400 +0000
@@ -215,7 +215,7 @@
  * Canonical encoding names and their properties.
  * "iso-8859-n" is handled by enc_canonize() directly.
  */
-static struct
+static struct enc_canon_table_item
 {   char *name;		int prop;		int codepage;}
 enc_canon_table[] =
 {
@@ -348,12 +348,13 @@
 #define IDX_HPROMAN8	59
     {"hp-roman8",	ENC_8BIT,		0},	// HP Roman8
 #define IDX_COUNT	60
+#define IDX_USERENC	IDX_COUNT
 };
 
 /*
  * Aliases for encoding names.
  */
-static struct
+static struct enc_alias_table_item
 {   char *name;		int canon;}
 enc_alias_table[] =
 {
@@ -434,6 +435,17 @@
     {NULL,		0}
 };
 
+#ifdef FEAT_USERENC
+static garray_T user_encs =
+{ 0, 0, sizeof(struct enc_canon_table_item), 4, NULL};
+static garray_T user_enc_aliases =
+{ 0, 0, sizeof(struct enc_alias_table_item), 4, NULL};
+#define user_enc_canon_table \
+    ((struct enc_canon_table_item *)user_encs.ga_data)
+#define user_enc_alias_table \
+    ((struct enc_alias_table_item *)user_enc_aliases.ga_data)
+#endif
+
 #ifndef CP_UTF8
 # define CP_UTF8 65001	// magic number from winnls.h
 #endif
@@ -450,10 +462,26 @@
     for (i = 0; i < IDX_COUNT; ++i)
 	if (STRCMP(name, enc_canon_table[i].name) == 0)
 	    return i;
+#ifdef FEAT_USERENC
+    for (i = 0; i < user_encs.ga_len; ++i)
+	if (STRCMP(name, user_enc_canon_table[i].name) == 0)
+	    return i + IDX_USERENC;
+#endif
     return -1;
 }
 
 
+#ifdef FEAT_USERENC
+    static struct enc_canon_table_item *
+enc_index_to_item(idx)
+    int		idx;
+{
+    if (idx >= IDX_USERENC)
+	return user_enc_canon_table + (idx - IDX_USERENC);
+    return enc_canon_table + idx;
+}
+#endif
+
 /*
  * Find canonical encoding "name" in the list and return its properties.
  * Returns 0 if not found.
@@ -465,7 +493,13 @@
 
     i = enc_canon_search(name);
     if (i >= 0)
+    {
+#ifdef FEAT_USERENC
+	return enc_index_to_item(i)->prop;
+#else
 	return enc_canon_table[i].prop;
+#endif
+    }
 #ifdef MSWIN
     if (name[0] == 'c' && name[1] == 'p' && VIM_ISDIGIT(name[2]))
     {
@@ -579,7 +613,11 @@
     }
     else if ((idx = enc_canon_search(p_enc)) >= 0)
     {
+#ifdef FEAT_USERENC
+	i = enc_index_to_item(idx)->prop;
+#else
 	i = enc_canon_table[idx].prop;
+#endif
 	if (i & ENC_UNICODE)
 	{
 	    // Unicode
@@ -594,7 +632,11 @@
 	else if (i & ENC_DBCS)
 	{
 	    // 2byte, handle below
+#ifdef FEAT_USERENC
+	    enc_dbcs_new = enc_index_to_item(idx)->codepage;
+#else
 	    enc_dbcs_new = enc_canon_table[idx].codepage;
+#endif
 	}
 	else
 	{
@@ -781,7 +823,8 @@
     // GNU gettext 0.10.37 supports this feature: set the codeset used for
     // translated messages independently from the current locale.
     (void)bind_textdomain_codeset(VIMPACKAGE,
-					  enc_utf8 ? "utf-8" : (char *)p_enc);
+					  enc_utf8 ? "utf-8"
+					  : (char *)enc_skip(p_enc));
 #endif
 
 #ifdef MSWIN
@@ -4290,6 +4333,10 @@
 	return p + 6;
     if (STRNCMP(p, "8bit-", 5) == 0)
 	return p + 5;
+#ifdef FEAT_USERENC
+    if (STRNCMP(p, "iconv-", 6) == 0)
+	return p + 6;
+#endif
     return p;
 }
 
@@ -4319,6 +4366,16 @@
     r = alloc(STRLEN(enc) + 3);
     if (r != NULL)
     {
+#ifdef FEAT_USERENC
+	if (STRNICMP(enc, "iconv-", 6) == 0
+		|| STRNICMP(enc, "iconv_", 6) == 0)
+	{
+	    STRCPY(r, "iconv-");
+	    STRCAT(r, enc + 6);
+	    p = r;
+	    goto trans_done;
+	}
+#endif
 	// Make it all lower case and replace '_' with '-'.
 	p = r;
 	for (s = enc; *s != NUL; ++s)
@@ -4355,6 +4412,9 @@
 	if (STRNCMP(p, "latin-", 6) == 0)
 	    STRMOVE(p + 5, p + 6);
 
+#ifdef FEAT_USERENC
+trans_done:
+#endif
 	if (enc_canon_search(p) >= 0)
 	{
 	    // canonical name can be used unmodified
@@ -4365,7 +4425,11 @@
 	{
 	    // alias recognized, get canonical name
 	    vim_free(r);
+#ifdef FEAT_USERENC
+	    r = vim_strsave((char_u *)enc_index_to_item(i)->name);
+#else
 	    r = vim_strsave((char_u *)enc_canon_table[i].name);
+#endif
 	}
     }
     return r;
@@ -4383,6 +4447,11 @@
     for (i = 0; enc_alias_table[i].name != NULL; ++i)
 	if (STRCMP(name, enc_alias_table[i].name) == 0)
 	    return enc_alias_table[i].canon;
+#ifdef FEAT_USERENC
+    for (i = 0; i < user_enc_aliases.ga_len; ++i)
+	if (STRCMP(name, user_enc_alias_table[i].name) == 0)
+	    return user_enc_alias_table[i].canon;
+#endif
     return -1;
 }
 
@@ -6731,8 +6800,10 @@
     int		l;
     int		c;
 
+    // To flush pending characters, we include the last NUL.
+    // (for euc-jisx0213)
     if (lenp == NULL)
-	len = (int)STRLEN(ptr);
+	len = (int)STRLEN(ptr) + 1;
     else
 	len = *lenp;
     if (len == 0)
@@ -6954,3 +7025,161 @@
 
     return retval;
 }
+
+#if defined(FEAT_USERENC) || defined(PROTO)
+    static char *
+do_encdef(origenc, userenc)
+    char_u	*origenc;
+    char_u	*userenc;
+{
+    char_u	*origcanon = NULL, *usercanon = NULL;
+    char	*msg = NULL;
+    int		origidx;
+    struct enc_canon_table_item *origitem;
+    struct enc_canon_table_item *useritem;
+
+    origcanon = enc_canonize(origenc);
+    usercanon = enc_canonize(userenc);
+    if (origcanon == NULL || usercanon == NULL)
+	goto fail;
+    
+    origidx = enc_canon_search(origcanon);
+    if (origidx < 0)
+    {
+	msg = e_invarg;
+	goto fail;
+    }
+    if (enc_canon_search(usercanon) >= 0)
+    {
+	msg = N_("Already defined encoding name");
+	goto fail;
+    }
+
+    if (ga_grow(&user_encs, user_encs.ga_len + 1) == FAIL)
+	goto fail;
+    origitem = enc_index_to_item(origidx);
+    useritem = user_enc_canon_table + user_encs.ga_len;
+
+    useritem->name = (char *)usercanon;
+    useritem->prop = origitem->prop;
+    useritem->codepage = origitem->codepage;
+    ++user_encs.ga_len;
+    goto finish;
+
+fail:
+    vim_free(usercanon);
+finish:
+    vim_free(origcanon);
+    return msg;
+}
+
+    static char *
+do_encalias(origenc, userenc)
+    char_u	*origenc;
+    char_u	*userenc;
+{
+    char_u	*origcanon = NULL, *usercanon = NULL;
+    char	*msg = NULL;
+    int		origidx;
+    struct enc_alias_table_item *useritem;
+
+    /*
+     * enc_canonize() canonizes passed encoding names before invoking
+     * enc_alias_search(), so user-defined aliases also must be canonized
+     * here.
+     */
+    origcanon = enc_canonize(origenc);
+    usercanon = enc_canonize(userenc);
+    if (origcanon == NULL || usercanon == NULL)
+	goto fail;
+    
+    origidx = enc_canon_search(origcanon);
+    if (origidx < 0)
+    {
+	msg = e_invarg;
+	goto fail;
+    }
+    if (enc_canon_search(usercanon) >= 0)
+    {
+	msg = N_("Already defined encoding name");
+	goto fail;
+    }
+
+    if (ga_grow(&user_enc_aliases, user_enc_aliases.ga_len + 1) == FAIL)
+	goto fail;
+    useritem = user_enc_alias_table + user_enc_aliases.ga_len;
+
+    useritem->name = (char *)usercanon;
+    useritem->canon = origidx;
+    ++user_enc_aliases.ga_len;
+    goto finish;
+
+fail:
+    vim_free(usercanon);
+finish:
+    vim_free(origcanon);
+    return msg;
+}
+
+    void
+ex_encdef(eap)
+    exarg_T	*eap;
+{
+    char_u	*origenc = eap->arg, *userenc;
+    char_u	*p;
+
+    if (*origenc == NUL)
+	abort();
+    p = skiptowhite(origenc);
+    if (*p == NUL)
+	goto invarg;
+    *p++ = NUL;
+    userenc = skipwhite(p);
+    if (*userenc == NUL)
+	goto invarg;
+    p = skiptowhite(userenc);
+    if (*p != NUL)
+    {
+	*p++ = NUL;
+	if (*skipwhite(p) != NUL)
+	    goto invarg;
+    }
+    eap->errmsg = do_encdef(origenc, userenc);
+    return;
+
+invarg:
+    eap->errmsg = e_invarg;
+}
+
+    void
+ex_encalias(eap)
+    exarg_T	*eap;
+{
+    char_u	*origenc = eap->arg, *userenc;
+    char_u	*p;
+
+    if (*origenc == NUL)
+	abort();
+    p = skiptowhite(origenc);
+    if (*p == NUL)
+	goto invarg;
+    *p++ = NUL;
+    userenc = skipwhite(p);
+    if (*userenc == NUL)
+	goto invarg;
+    p = skiptowhite(userenc);
+    if (*p != NUL)
+    {
+	*p++ = NUL;
+	if (*skipwhite(p) != NUL)
+	    goto invarg;
+    }
+    eap->errmsg = do_encalias(origenc, userenc);
+    return;
+
+invarg:
+    eap->errmsg = e_invarg;
+}
+
+#endif	/* FEAT_USERENC || PROTO */
+
diff -Naur vim82.orig/src/os_mac.h vim82+userenc/src/os_mac.h
--- vim82.orig/src/os_mac.h	2019-11-30 17:50:43.000000000 +0000
+++ vim82+userenc/src/os_mac.h	2021-11-21 04:18:31.257550400 +0000
@@ -84,7 +84,9 @@
 #define FEAT_SOURCE_FF_MAC
 
 #define USE_EXE_NAME		    // to find  $VIM
+#if !defined(MACOS_X) /* XXX: some filesystems are case sensitive */
 #define CASE_INSENSITIVE_FILENAME   // ignore case when comparing file names
+#endif
 #define SPACE_IN_FILENAME
 #define BREAKCHECK_SKIP	   32	    // call mch_breakcheck() each time, it's
 				    // quite fast. Did I forgot to update the
diff -Naur vim82.orig/src/po/.cvsignore vim82+userenc/src/po/.cvsignore
--- vim82.orig/src/po/.cvsignore	1970-01-01 00:00:00.000000000 +0000
+++ vim82+userenc/src/po/.cvsignore	2021-11-21 04:18:31.257550400 +0000
@@ -0,0 +1,12 @@
+cs.cp1250.po
+ja.sjis.po
+ja.euc-jp.po
+pl.cp1250.po
+pl.UTF-8.po
+ru.cp1251.po
+sk.cp1250.po
+uk.cp1251.po
+zh_CN.cp936.po
+*.mo
+*.pot
+sjiscorr
diff -Naur vim82.orig/src/po/Makefile vim82+userenc/src/po/Makefile
--- vim82.orig/src/po/Makefile	2019-11-14 20:35:21.000000000 +0000
+++ vim82+userenc/src/po/Makefile	2021-11-21 04:18:31.257550400 +0000
@@ -158,6 +158,9 @@
 checkclean:
 	rm -f *.ck
 
+maintainer-clean: distclean
+	rm -f $(CONVERTED)
+
 $(PACKAGE).pot: ../*.c ../if_perl.xs ../GvimExt/gvimext.cpp ../globals.h ../if_py_both.h ../vim.h gvim.desktop.in vim.desktop.in
 	cd ..; $(XGETTEXT) --default-domain=$(PACKAGE) \
 		--add-comments --keyword=_ --keyword=N_ --keyword=NGETTEXT:1,2 \
diff -Naur vim82.orig/src/po/ja.euc-jp.po vim82+userenc/src/po/ja.euc-jp.po
--- vim82.orig/src/po/ja.euc-jp.po	2019-11-11 14:12:01.000000000 +0000
+++ vim82+userenc/src/po/ja.euc-jp.po	2021-11-21 04:18:31.273169000 +0000
@@ -3299,6 +3299,12 @@
 msgid "E289: input method doesn't support my preedit type"
 msgstr "E289: ¥¤¥ó¥×¥Ã¥È¥á¥½¥Ã¥É¤Ï my preedit type ¤ò¥µ¥Ý¡¼¥È¤·¤Þ¤»¤ó"
 
+msgid "invalid encoding properties"
+msgstr "¥¨¥ó¥³¡¼¥Ç¥£¥ó¥°¤ÎÂ°À­¤¬Ìµ¸ú¤Ç¤¹"
+
+msgid "Already defined encoding name"
+msgstr "´û¤ËÄêµÁ¤µ¤ì¤¿¥¨¥ó¥³¡¼¥Ç¥£¥ó¥°Ì¾¤Ç¤¹"
+
 msgid "E293: block was not locked"
 msgstr "E293: ¥Ö¥í¥Ã¥¯¤¬¥í¥Ã¥¯¤µ¤ì¤Æ¤¤¤Þ¤»¤ó"
 
diff -Naur vim82.orig/src/po/ja.po vim82+userenc/src/po/ja.po
--- vim82.orig/src/po/ja.po	2019-11-11 14:12:01.000000000 +0000
+++ vim82+userenc/src/po/ja.po	2021-11-21 04:18:31.273169000 +0000
@@ -3299,6 +3299,12 @@
 msgid "E289: input method doesn't support my preedit type"
 msgstr "E289: ã‚¤ãƒ³ãƒ—ãƒƒãƒˆãƒ¡ã‚½ãƒƒãƒ‰ã�¯ my preedit type ã‚’ã‚µãƒ�ãƒ¼ãƒˆã�—ã�¾ã�›ã‚“"
 
+msgid "invalid encoding properties"
+msgstr "ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‡ã‚£ãƒ³ã‚°ã�®å±žæ€§ã�Œç„¡åŠ¹ã�§ã�™"
+
+msgid "Already defined encoding name"
+msgstr "æ—¢ã�«å®šç¾©ã�•ã‚Œã�Ÿã‚¨ãƒ³ã‚³ãƒ¼ãƒ‡ã‚£ãƒ³ã‚°å��ã�§ã�™"
+
 msgid "E293: block was not locked"
 msgstr "E293: ãƒ–ãƒ­ãƒƒã‚¯ã�Œãƒ­ãƒƒã‚¯ã�•ã‚Œã�¦ã�„ã�¾ã�›ã‚“"
 
diff -Naur vim82.orig/src/po/ja.sjis.po vim82+userenc/src/po/ja.sjis.po
--- vim82.orig/src/po/ja.sjis.po	2019-12-12 12:16:06.000000000 +0000
+++ vim82+userenc/src/po/ja.sjis.po	2021-11-21 04:18:31.273169000 +0000
@@ -3299,6 +3299,12 @@
 msgid "E289: input method doesn't support my preedit type"
 msgstr "E289: ƒCƒ“ƒvƒbƒgƒ�ƒ\\ƒbƒh‚Í my preedit type ‚ðƒTƒ|�[ƒg‚µ‚Ü‚¹‚ñ"
 
+msgid "invalid encoding properties"
+msgstr "ƒGƒ“ƒR�[ƒfƒBƒ“ƒO‚Ì‘®�«‚ª–³Œø‚Å‚·"
+
+msgid "Already defined encoding name"
+msgstr "Šù‚É’è‹`‚³‚ê‚½ƒGƒ“ƒR�[ƒfƒBƒ“ƒO–¼‚Å‚·"
+
 msgid "E293: block was not locked"
 msgstr "E293: ƒuƒ�ƒbƒN‚ªƒ�ƒbƒN‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ"
 
diff -Naur vim82.orig/src/proto/mbyte.pro vim82+userenc/src/proto/mbyte.pro
--- vim82.orig/src/proto/mbyte.pro	2019-12-12 11:55:26.000000000 +0000
+++ vim82+userenc/src/proto/mbyte.pro	2021-11-21 04:18:31.273169000 +0000
@@ -96,4 +96,6 @@
 int convert_input_safe(char_u *ptr, int len, int maxlen, char_u **restp, int *restlenp);
 char_u *string_convert(vimconv_T *vcp, char_u *ptr, int *lenp);
 char_u *string_convert_ext(vimconv_T *vcp, char_u *ptr, int *lenp, int *unconvlenp);
+void ex_encdef(exarg_T *eap);
+void ex_encalias(exarg_T *eap);
 /* vim: set ft=c : */
