m4: Sync with FreeBSD.
[dragonfly.git] / usr.bin / m4 / eval.c
index 09c9c1b..09d0acf 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $ */
+/*     $OpenBSD: eval.c,v 1.69 2011/03/24 11:23:08 espie Exp $ */
 /*     $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $      */
 
 /*
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * @(#)eval.c  8.2 (Berkeley) 4/27/95
- * $OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $
- * $FreeBSD: src/usr.bin/m4/eval.c,v 1.22 2004/08/16 14:18:21 tjr Exp $
- * $DragonFly: src/usr.bin/m4/eval.c,v 1.3 2006/12/27 21:29:02 pavalos Exp $
+ * $FreeBSD: src/usr.bin/m4/eval.c,v 1.28 2012/11/17 01:54:24 svnexp Exp $
  */
 
+
 /*
  * eval.c
  * Facility: m4 macro processor
  */
 
 #include <sys/types.h>
+#include <err.h>
 #include <errno.h>
+#include <limits.h>
 #include <unistd.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
 #include <fcntl.h>
-#include <err.h>
 #include "mdef.h"
 #include "stdd.h"
 #include "extern.h"
 #include "pathnames.h"
 
-#define BUILTIN_MARKER "__builtin_"
-
 static void    dodefn(const char *);
 static void    dopushdef(const char *, const char *);
 static void    dodump(const char *[], int);
@@ -71,10 +65,9 @@ static void  dotrace(const char *[], int, int);
 static void    doifelse(const char *[], int);
 static int     doincl(const char *);
 static int     dopaste(const char *);
-static void    gnu_dochq(const char *[], int);
 static void    dochq(const char *[], int);
-static void    gnu_dochc(const char *[], int);
 static void    dochc(const char *[], int);
+static void    dom4wrap(const char *);
 static void    dodiv(int);
 static void    doundiv(const char *[], int);
 static void    dosub(const char *[], int);
@@ -82,7 +75,7 @@ static void   map(char *, const char *, const char *, const char *);
 static const char *handledash(char *, char *, const char *);
 static void    expand_builtin(const char *[], int, int);
 static void    expand_macro(const char *[], int);
-static void    dump_one_def(ndptr);
+static void    dump_one_def(const char *, struct macro_definition *);
 
 unsigned long  expansion_id;
 
@@ -91,7 +84,7 @@ unsigned long expansion_id;
  *       argc - number of elements in argv.
  *       argv - element vector :
  *                     argv[0] = definition of a user
- *                               macro or nil if built-in.
+ *                               macro or NULL if built-in.
  *                     argv[1] = name of the macro or
  *                               built-in.
  *                     argv[2] = parameters to user-defined
@@ -106,21 +99,20 @@ unsigned long      expansion_id;
  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
  */
 void
-eval(const char *argv[], int argc, int td)
+eval(const char *argv[], int argc, int td, int is_traced)
 {
-       ssize_t mark = -1;
+       size_t mark = SIZE_MAX;
 
        expansion_id++;
        if (td & RECDEF)
-               errx(1, "%s at line %lu: expanding recursive definition for %s",
-                       CURRENT_NAME, CURRENT_LINE, argv[1]);
-       if (traced_macros && is_traced(argv[1]))
-               mark = trace(argv, argc, infile+ilevel);
+               m4errx(1, "expanding recursive definition for %s.", argv[1]);
+       if (is_traced)
+               mark = trace(argv, argc, infile + ilevel);
        if (td == MACRTYPE)
                expand_macro(argv, argc);
        else
                expand_builtin(argv, argc, td);
-       if (mark != -1)
+       if (mark != SIZE_MAX)
                finish_trace(mark);
 }
 
@@ -144,11 +136,14 @@ expand_builtin(const char *argv[], int argc, int td)
  /*
   * if argc == 3 and argv[2] is null, then we
   * have macro-or-builtin() type call. We adjust
-  * argc to avoid further checking..
+  * argc to avoid further checking.
+  */
+ /* we keep the initial value for those built-ins that differentiate
+  * between builtin() and builtin.
   */
-       ac = argc;
+       ac = argc;
 
-       if (argc == 3 && !*(argv[2]))
+       if (argc == 3 && !*(argv[2]) && !mimic_gnu)
                argc--;
 
        switch (td & TYPEMASK) {
@@ -180,9 +175,27 @@ expand_builtin(const char *argv[], int argc, int td)
         * doexpr - evaluate arithmetic
         * expression
         */
+       {
+               int base = 10;
+               int maxdigits = 0;
+               const char *errstr;
+
+               if (argc > 3) {
+                       base = strtonum(argv[3], 2, 36, &errstr);
+                       if (errstr) {
+                               m4errx(1, "expr: base %s invalid.", argv[3]);
+                       }
+               }
+               if (argc > 4) {
+                       maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
+                       if (errstr) {
+                               m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
+                       }
+               }
                if (argc > 2)
-                       pbnum(expr(argv[2]));
+                       pbnumbase(expr(argv[2]), base, maxdigits);
                break;
+       }
 
        case IFELTYPE:
                if (argc > 4)
@@ -196,7 +209,7 @@ expand_builtin(const char *argv[], int argc, int td)
         * another definition
         */
                if (argc > 3) {
-                       if (lookup(argv[2]) != nil)
+                       if (lookup_macro_definition(argv[2]) != NULL)
                                pbstr(argv[3]);
                        else if (argc > 4)
                                pbstr(argv[4]);
@@ -234,7 +247,7 @@ expand_builtin(const char *argv[], int argc, int td)
         * dosys - execute system command
         */
                if (argc > 2) {
-                       fflush(NULL);
+                       fflush(stdout);
                        sysval = system(argv[2]);
                }
                break;
@@ -251,17 +264,23 @@ expand_builtin(const char *argv[], int argc, int td)
        case ESYSCMDTYPE:
                if (argc > 2)
                        doesyscmd(argv[2]);
-               break;
+               break;
        case INCLTYPE:
                if (argc > 2)
-                       if (!doincl(argv[2]))
-                               err(1, "%s at line %lu: include(%s)",
-                                   CURRENT_NAME, CURRENT_LINE, argv[2]);
+                       if (!doincl(argv[2])) {
+                               if (mimic_gnu) {
+                                       warn("%s at line %lu: include(%s)",
+                                           CURRENT_NAME, CURRENT_LINE, argv[2]);
+                                       exit_code = 1;
+                               } else
+                                       err(1, "%s at line %lu: include(%s)",
+                                           CURRENT_NAME, CURRENT_LINE, argv[2]);
+                       }
                break;
 
        case SINCTYPE:
                if (argc > 2)
-                       (void) doincl(argv[2]);
+                       doincl(argv[2]);
                break;
 #ifdef EXTENDED
        case PASTTYPE:
@@ -273,21 +292,18 @@ expand_builtin(const char *argv[], int argc, int td)
 
        case SPASTYPE:
                if (argc > 2)
-                       (void) dopaste(argv[2]);
+                       dopaste(argv[2]);
+               break;
+       case FORMATTYPE:
+               doformat(argv, argc);
                break;
 #endif
        case CHNQTYPE:
-               if (mimic_gnu)
-                       gnu_dochq(argv, ac);
-               else
-                       dochq(argv, argc);
+               dochq(argv, ac);
                break;
 
        case CHNCTYPE:
-               if (mimic_gnu)
-                       gnu_dochc(argv, ac);
-               else
-                       dochc(argv, argc);
+               dochc(argv, argc);
                break;
 
        case SUBSTYPE:
@@ -310,7 +326,7 @@ expand_builtin(const char *argv[], int argc, int td)
                                pbstr(rquote);
                                pbstr(argv[n]);
                                pbstr(lquote);
-                               putback(COMMA);
+                               pushback(COMMA);
                        }
                        pbstr(rquote);
                        pbstr(argv[3]);
@@ -346,7 +362,7 @@ expand_builtin(const char *argv[], int argc, int td)
         */
                if (argc > 2)
                        for (n = 2; n < argc; n++)
-                               remhash(argv[n], ALL);
+                               macro_undefine(argv[n]);
                break;
 
        case POPDTYPE:
@@ -357,7 +373,7 @@ expand_builtin(const char *argv[], int argc, int td)
         */
                if (argc > 2)
                        for (n = 2; n < argc; n++)
-                               remhash(argv[n], TOP);
+                               macro_popdef(argv[n]);
                break;
 
        case MKTMTYPE:
@@ -391,7 +407,7 @@ expand_builtin(const char *argv[], int argc, int td)
                if (argc > 3) {
                        char *temp;
 
-                       temp = xalloc(strlen(argv[2])+1);
+                       temp = xalloc(strlen(argv[2]) + 1, NULL);
                        if (argc > 4)
                                map(temp, argv[2], argv[3], argv[4]);
                        else
@@ -437,7 +453,8 @@ expand_builtin(const char *argv[], int argc, int td)
         * dom4wrap - set up for
         * wrap-up/wind-down activity
         */
-               m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
+               if (argc > 2)
+                       dom4wrap(argv[2]);
                break;
 
        case EXITTYPE:
@@ -484,8 +501,7 @@ expand_builtin(const char *argv[], int argc, int td)
                pbstr(lquote);
                break;
        default:
-               errx(1, "%s at line %lu: eval: major botch.",
-                       CURRENT_NAME, CURRENT_LINE);
+               m4errx(1, "eval: major botch.");
                break;
        }
 }
@@ -508,7 +524,7 @@ expand_macro(const char *argv[], int argc)
        p--;                           /* last character of defn */
        while (p > t) {
                if (*(p - 1) != ARGFLAG)
-                       PUTBACK(*p);
+                       PUSHBACK(*p);
                else {
                        switch (*p) {
 
@@ -532,27 +548,27 @@ expand_macro(const char *argv[], int argc)
                                if (argc > 2) {
                                        for (n = argc - 1; n > 2; n--) {
                                                pbstr(argv[n]);
-                                               putback(COMMA);
+                                               pushback(COMMA);
                                        }
                                        pbstr(argv[2]);
-                               }
+                               }
                                break;
-                        case '@':
+                       case '@':
                                if (argc > 2) {
                                        for (n = argc - 1; n > 2; n--) {
                                                pbstr(rquote);
                                                pbstr(argv[n]);
                                                pbstr(lquote);
-                                               putback(COMMA);
+                                               pushback(COMMA);
                                        }
                                        pbstr(rquote);
                                        pbstr(argv[2]);
                                        pbstr(lquote);
                                }
-                                break;
+                               break;
                        default:
-                               PUTBACK(*p);
-                               PUTBACK('$');
+                               PUSHBACK(*p);
+                               PUSHBACK('$');
                                break;
                        }
                        p--;
@@ -560,42 +576,20 @@ expand_macro(const char *argv[], int argc)
                p--;
        }
        if (p == t)                    /* do last character */
-               PUTBACK(*p);
+               PUSHBACK(*p);
 }
 
+
 /*
  * dodefine - install definition in the table
  */
 void
 dodefine(const char *name, const char *defn)
 {
-       ndptr p;
-       int n;
-
-       if (!*name)
-               errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
-                   CURRENT_LINE);
-       if ((p = lookup(name)) == nil)
-               p = addent(name);
-       else if (p->defn != null)
-               free((char *) p->defn);
-       if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
-               n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
-               if (n != -1) {
-                       p->type = n & TYPEMASK;
-                       if ((n & NOARGS) == 0)
-                               p->type |= NEEDARGS;
-                       p->defn = null;
-                       return;
-               }
-       }
-       if (!*defn)
-               p->defn = null;
+       if (!*name && !mimic_gnu)
+               m4errx(1, "null definition.");
        else
-               p->defn = xstrdup(defn);
-       p->type = MACRTYPE;
-       if (STREQ(name, defn))
-               p->type |= RECDEF;
+               macro_define(name, defn);
 }
 
 /*
@@ -605,16 +599,15 @@ dodefine(const char *name, const char *defn)
 static void
 dodefn(const char *name)
 {
-       ndptr p;
-       const char *real;
+       struct macro_definition *p;
 
-       if ((p = lookup(name)) != nil) {
-               if (p->defn != null) {
+       if ((p = lookup_macro_definition(name)) != NULL) {
+               if ((p->type & TYPEMASK) == MACRTYPE) {
                        pbstr(rquote);
                        pbstr(p->defn);
                        pbstr(lquote);
-               } else if ((real = builtin_realname(p->type)) != NULL) {
-                       pbstr(real);
+               } else {
+                       pbstr(p->defn);
                        pbstr(BUILTIN_MARKER);
                }
        }
@@ -630,40 +623,27 @@ dodefn(const char *name)
 static void
 dopushdef(const char *name, const char *defn)
 {
-       ndptr p;
-
-       if (!*name)
-               errx(1, "%s at line %lu: null definition", CURRENT_NAME,
-                   CURRENT_LINE);
-       p = addent(name);
-       if (!*defn)
-               p->defn = null;
+       if (!*name && !mimic_gnu)
+               m4errx(1, "null definition.");
        else
-               p->defn = xstrdup(defn);
-       p->type = MACRTYPE;
-       if (STREQ(name, defn))
-               p->type |= RECDEF;
+               macro_pushdef(name, defn);
 }
 
 /*
  * dump_one_def - dump the specified definition.
  */
 static void
-dump_one_def(ndptr p)
+dump_one_def(const char *name, struct macro_definition *p)
 {
-       const char *real;
-
+       if (!traceout)
+               traceout = stderr;
        if (mimic_gnu) {
                if ((p->type & TYPEMASK) == MACRTYPE)
-                       fprintf(traceout, "%s:\t%s\n", p->name, p->defn);
-               else {
-                       real = builtin_realname(p->type);
-                       if (real == NULL)
-                               real = null;
-                       fprintf(traceout, "%s:\t<%s>\n", p->name, real);
-               }
+                       fprintf(traceout, "%s:\t%s\n", name, p->defn);
+               else
+                       fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
        } else
-               fprintf(traceout, "`%s'\t`%s'\n", p->name, p->defn);
+               fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
 }
 
 /*
@@ -675,17 +655,14 @@ static void
 dodump(const char *argv[], int argc)
 {
        int n;
-       ndptr p;
+       struct macro_definition *p;
 
        if (argc > 2) {
                for (n = 2; n < argc; n++)
-                       if ((p = lookup(argv[n])) != nil)
-                               dump_one_def(p);
-       } else {
-               for (n = 0; n < HASHSIZE; n++)
-                       for (p = hashtab[n]; p != nil; p = p->nxtptr)
-                               dump_one_def(p);
-       }
+                       if ((p = lookup_macro_definition(argv[n])) != NULL)
+                               dump_one_def(argv[n], p);
+       } else
+               macro_for_all(dump_one_def);
 }
 
 /*
@@ -709,7 +686,7 @@ dotrace(const char *argv[], int argc, int on)
 static void
 doifelse(const char *argv[], int argc)
 {
-       cycle {
+       for(;;) {
                if (STREQ(argv[2], argv[3]))
                        pbstr(argv[4]);
                else if (argc == 6)
@@ -730,15 +707,10 @@ static int
 doincl(const char *ifile)
 {
        if (ilevel + 1 == MAXINP)
-               errx(1, "%s at line %lu: too many include files.",
-                   CURRENT_NAME, CURRENT_LINE);
-       if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
+               m4errx(1, "too many include files.");
+       if (fopen_trypath(infile + ilevel + 1, ifile) != NULL) {
                ilevel++;
-               if ((inname[ilevel] = strdup(ifile)) == NULL)
-                       err(1, NULL);
-               inlineno[ilevel] = 1;
                bbase[ilevel] = bufbase = bp;
-               emitline();
                return (1);
        } else
                return (0);
@@ -756,97 +728,74 @@ dopaste(const char *pfile)
        int c;
 
        if ((pf = fopen(pfile, "r")) != NULL) {
-               fprintf(active, "#line 1 \"%s\"\n", pfile);
+               if (synch_lines)
+                   fprintf(active, "#line 1 \"%s\"\n", pfile);
                while ((c = getc(pf)) != EOF)
                        putc(c, active);
-               (void) fclose(pf);
-               emitline();
+               fclose(pf);
+               emit_synchline();
                return (1);
        } else
                return (0);
 }
 #endif
 
+/*
+ * dochq - change quote characters
+ */
 static void
-gnu_dochq(const char *argv[], int ac)
+dochq(const char *argv[], int ac)
 {
-       /* In gnu-m4 mode, the only way to restore quotes is to have no
-        * arguments at all. */
        if (ac == 2) {
-               lquote[0] = LQUOTE, lquote[1] = EOS;
-               rquote[0] = RQUOTE, rquote[1] = EOS;
+               lquote[0] = LQUOTE; lquote[1] = EOS;
+               rquote[0] = RQUOTE; rquote[1] = EOS;
        } else {
                strlcpy(lquote, argv[2], sizeof(lquote));
-               if(ac > 3)
+               if (ac > 3) {
                        strlcpy(rquote, argv[3], sizeof(rquote));
-               else
-                       rquote[0] = EOS;
+               } else {
+                       rquote[0] = ECOMMT; rquote[1] = EOS;
+               }
        }
 }
 
 /*
- * dochq - change quote characters
+ * dochc - change comment characters
  */
 static void
-dochq(const char *argv[], int argc)
-{
-       if (argc > 2) {
-               if (*argv[2])
-                       strlcpy(lquote, argv[2], sizeof(lquote));
-               else {
-                       lquote[0] = LQUOTE;
-                       lquote[1] = EOS;
-               }
-               if (argc > 3) {
-                       if (*argv[3])
-                               strlcpy(rquote, argv[3], sizeof(rquote));
-               } else
-                       strcpy(rquote, lquote);
-       } else {
-               lquote[0] = LQUOTE, lquote[1] = EOS;
-               rquote[0] = RQUOTE, rquote[1] = EOS;
-       }
-}
-
-static void
-gnu_dochc(const char *argv[], int ac)
+dochc(const char *argv[], int argc)
 {
-       /* In gnu-m4 mode, no arguments mean no comment
-        * arguments at all. */
-       if (ac == 2) {
+/* XXX Note that there is no difference between no argument and a single
+ * empty argument.
+ */
+       if (argc == 2) {
                scommt[0] = EOS;
                ecommt[0] = EOS;
        } else {
-               if (*argv[2])
-                       strlcpy(scommt, argv[2], sizeof(scommt));
-               else
-                       scommt[0] = SCOMMT, scommt[1] = EOS;
-               if(ac > 3 && *argv[3])
+               strlcpy(scommt, argv[2], sizeof(scommt));
+               if (argc == 3) {
+                       ecommt[0] = ECOMMT; ecommt[1] = EOS;
+               } else {
                        strlcpy(ecommt, argv[3], sizeof(ecommt));
-               else
-                       ecommt[0] = ECOMMT, ecommt[1] = EOS;
+               }
        }
 }
+
 /*
- * dochc - change comment characters
+ * dom4wrap - expand text at EOF
  */
 static void
-dochc(const char *argv[], int argc)
+dom4wrap(const char *text)
 {
-       if (argc > 2) {
-               if (*argv[2])
-                       strlcpy(scommt, argv[2], sizeof(scommt));
-               if (argc > 3) {
-                       if (*argv[3])
-                               strlcpy(ecommt, argv[3], sizeof(ecommt));
-               }
+       if (wrapindex >= maxwraps) {
+               if (maxwraps == 0)
+                       maxwraps = 16;
                else
-                       ecommt[0] = ECOMMT, ecommt[1] = EOS;
-       }
-       else {
-               scommt[0] = SCOMMT, scommt[1] = EOS;
-               ecommt[0] = ECOMMT, ecommt[1] = EOS;
+                       maxwraps *= 2;
+               m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
+                  "too many m4wraps");
        }
+       m4wraps[wrapindex++] = xstrdup(text);
 }
 
 /*
@@ -863,7 +812,7 @@ dodiv(int n)
                        resizedivs(n + 10);
                else
                        n = 0;          /* bitbucket */
-       }
+       }
 
        if (n < 0)
                n = 0;                 /* bitbucket */
@@ -891,10 +840,15 @@ doundiv(const char *argv[], int argc)
 
        if (argc > 2) {
                for (ind = 2; ind < argc; ind++) {
-                       n = atoi(argv[ind]);
-                       if (n > 0 && n < maxout && outfile[n] != NULL)
-                               getdiv(n);
-
+                       const char *errstr;
+                       n = strtonum(argv[ind], 1, INT_MAX, &errstr);
+                       if (errstr) {
+                               if (errno == EINVAL && mimic_gnu)
+                                       getdivfile(argv[ind]);
+                       } else {
+                               if (n < maxout && outfile[n] != NULL)
+                                       getdiv(n);
+                       }
                }
        }
        else
@@ -927,7 +881,7 @@ dosub(const char *argv[], int argc)
 #endif
        if (fc >= ap && fc < ap + strlen(ap))
                for (k = fc + nc - 1; k >= fc; k--)
-                       putback(*k);
+                       pushback(*k);
 }
 
 /*
@@ -935,25 +889,11 @@ dosub(const char *argv[], int argc)
  * map every character of s1 that is specified in from
  * into s3 and replace in s. (source s1 remains untouched)
  *
- * This is a standard implementation of map(s,from,to) function of ICON
- * language. Within mapvec, we replace every character of "from" with
- * the corresponding character in "to". If "to" is shorter than "from",
- * than the corresponding entries are null, which means that those
- * characters dissapear altogether. Furthermore, imagine
- * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
- * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
- * ultimately maps to `*'. In order to achieve this effect in an efficient
- * manner (i.e. without multiple passes over the destination string), we
- * loop over mapvec, starting with the initial source character. if the
- * character value (dch) in this location is different than the source
- * character (sch), sch becomes dch, once again to index into mapvec, until
- * the character value stabilizes (i.e. sch = dch, in other words
- * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
- * character, it will stabilize, since mapvec[0] == 0 at all times. At the
- * end, we restore mapvec* back to normal where mapvec[n] == n for
- * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
- * about 5 times faster than any algorithm that makes multiple passes over
- * destination string.
+ * This is derived from the a standard implementation of map(s,from,to)
+ * function of ICON language. Within mapvec, we replace every character
+ * of "from" with the corresponding character in "to".
+ * If "to" is shorter than "from", than the corresponding entries are null,
+ * which means that those characters dissapear altogether.
  */
 static void
 map(char *dest, const char *src, const char *from, const char *to)
@@ -962,6 +902,8 @@ map(char *dest, const char *src, const char *from, const char *to)
        unsigned char sch, dch;
        static char frombis[257];
        static char tobis[257];
+       int i;
+       char seen[256];
        static unsigned char mapvec[256] = {
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
            19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
@@ -996,17 +938,21 @@ map(char *dest, const char *src, const char *from, const char *to)
         * create a mapping between "from" and
         * "to"
         */
-               while (*from)
-                       mapvec[(unsigned char)(*from++)] = (*to) ?
-                               (unsigned char)(*to++) : 0;
+               for (i = 0; i < 256; i++)
+                       seen[i] = 0;
+               while (*from) {
+                       if (!seen[(unsigned char)(*from)]) {
+                               mapvec[(unsigned char)(*from)] = (unsigned char)(*to);
+                               seen[(unsigned char)(*from)] = 1;
+                       }
+                       from++;
+                       if (*to)
+                               to++;
+               }
 
                while (*src) {
                        sch = (unsigned char)(*src++);
                        dch = mapvec[sch];
-                       while (dch != sch) {
-                               sch = dch;
-                               dch = mapvec[sch];
-                       }
                        if ((*dest = (char)dch))
                                dest++;
                }
@@ -1036,12 +982,23 @@ handledash(char *buffer, char *end, const char *src)
        while(*src) {
                if (src[1] == '-' && src[2]) {
                        unsigned char i;
-                       for (i = (unsigned char)src[0];
-                           i <= (unsigned char)src[2]; i++) {
-                               *p++ = i;
-                               if (p == end) {
-                                       *p = '\0';
-                                       return buffer;
+                       if ((unsigned char)src[0] <= (unsigned char)src[2]) {
+                               for (i = (unsigned char)src[0];
+                                   i <= (unsigned char)src[2]; i++) {
+                                       *p++ = i;
+                                       if (p == end) {
+                                               *p = '\0';
+                                               return buffer;
+                                       }
+                               }
+                       } else {
+                               for (i = (unsigned char)src[0];
+                                   i >= (unsigned char)src[2]; i--) {
+                                       *p++ = i;
+                                       if (p == end) {
+                                               *p = '\0';
+                                               return buffer;
+                                       }
                                }
                        }
                        src += 3;