Something went wrong. Try again.
Linux (musl ABI) build of the OpenBSD m4 program
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032/* $OpenBSD: eval.c,v 1.79 2022/12/26 19:16:01 jmc Exp $ *//* $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $ */
/* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Ozan Yigit at York University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 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. 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. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */
/* * eval.c * Facility: m4 macro processor * by: oz */
#include <sys/types.h>#include <err.h>#include <errno.h>#include <limits.h>#include <unistd.h>#include <stdio.h>#include <stdint.h>#include <stdlib.h>#include <stddef.h>#include <string.h>#include <fcntl.h>#include "mdef.h"#include "stdd.h"#include "extern.h"#include "pathnames.h"
static void dodefn(const char *);static void dopushdef(const char *, const char *);static void dodump(const char *[], int);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 dochq(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);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(const char *, struct macro_definition *);
unsigned long expansion_id;
/* * eval - eval all macros and builtins calls * argc - number of elements in argv. * argv - element vector : * argv[0] = definition of a user * macro or NULL if built-in. * argv[1] = name of the macro or * built-in. * argv[2] = parameters to user-defined * . macro or built-in. * . * * A call in the form of macro-or-builtin() will result in: * argv[0] = nullstr * argv[1] = macro-or-builtin * argv[2] = nullstr * * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin */voideval(const char *argv[], int argc, int td, int is_traced){ size_t mark = SIZE_MAX;
expansion_id++; if (td & RECDEF) 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 != SIZE_MAX) finish_trace(mark);}
/* * expand_builtin - evaluate built-in macros. */voidexpand_builtin(const char *argv[], int argc, int td){ int c, n; const char *errstr; int ac; static int sysval = 0;
#ifdef DEBUG printf("argc = %d\n", argc); for (n = 0; n < argc; n++) printf("argv[%d] = %s\n", n, argv[n]); fflush(stdout);#endif
/* * if argc == 3 and argv[2] is null, then we * have macro-or-builtin() type call. We adjust * argc to avoid further checking.. */ /* we keep the initial value for those built-ins that differentiate * between builtin() and builtin. */ ac = argc;
if (argc == 3 && !*(argv[2]) && !mimic_gnu) argc--;
switch (td & TYPEMASK) {
case DEFITYPE: if (argc > 2) dodefine(argv[2], (argc > 3) ? argv[3] : null); break;
case PUSDTYPE: if (argc > 2) dopushdef(argv[2], (argc > 3) ? argv[3] : null); break;
case DUMPTYPE: dodump(argv, argc); break;
case TRACEONTYPE: dotrace(argv, argc, 1); break;
case TRACEOFFTYPE: dotrace(argv, argc, 0); break;
case EXPRTYPE: /* * doexpr - evaluate arithmetic * expression */ { int base = 10; int maxdigits = 0;
if (argc > 3) { base = strtonum(argv[3], 2, 36, &errstr); if (errstr) { m4errx(1, "expr: base is %s: %s.", errstr, argv[3]); } } if (argc > 4) { maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr); if (errstr) { m4errx(1, "expr: maxdigits is %s: %s.", errstr, argv[4]); } } if (argc > 2) pbnumbase(expr(argv[2]), base, maxdigits); break; }
case IFELTYPE: doifelse(argv, argc); break;
case IFDFTYPE: /* * doifdef - select one of two * alternatives based on the existence of * another definition */ if (argc > 3) { if (lookup_macro_definition(argv[2]) != NULL) pbstr(argv[3]); else if (argc > 4) pbstr(argv[4]); } break;
case LENGTYPE: /* * dolen - find the length of the * argument */ pbnum((argc > 2) ? strlen(argv[2]) : 0); break;
case INCRTYPE: /* * doincr - increment the value of the * argument */ if (argc > 2) { n = strtonum(argv[2], INT_MIN, INT_MAX-1, &errstr); if (errstr != NULL) m4errx(1, "incr: argument is %s: %s.", errstr, argv[2]); pbnum(n + 1); } break;
case DECRTYPE: /* * dodecr - decrement the value of the * argument */ if (argc > 2) { n = strtonum(argv[2], INT_MIN+1, INT_MAX, &errstr); if (errstr) m4errx(1, "decr: argument is %s: %s.", errstr, argv[2]); pbnum(n - 1); } break;
case SYSCTYPE: /* * dosys - execute system command */ if (argc > 2) { fflush(stdout); sysval = system(argv[2]); } break;
case SYSVTYPE: /* * dosysval - return value of the last * system call. * */ pbnum(sysval); break;
case ESYSCMDTYPE: if (argc > 2) doesyscmd(argv[2]); break; case INCLTYPE: if (argc > 2) { if (!doincl(argv[2])) { if (mimic_gnu) { warn("%s at line %lu: include(%s)", CURRENT_NAME, CURRENT_LINE, argv[2]); exit_code = 1; if (fatal_warns) { killdiv(); exit(exit_code); } } 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]); break;#ifdef EXTENDED case PASTTYPE: if (argc > 2) if (!dopaste(argv[2])) err(1, "%s at line %lu: paste(%s)", CURRENT_NAME, CURRENT_LINE, argv[2]); break;
case SPASTYPE: if (argc > 2) (void) dopaste(argv[2]); break; case FORMATTYPE: doformat(argv, argc); break;#endif case CHNQTYPE: dochq(argv, ac); break;
case CHNCTYPE: dochc(argv, argc); break;
case SUBSTYPE: /* * dosub - select substring * */ if (argc > 3) dosub(argv, argc); break;
case SHIFTYPE: /* * doshift - push back all arguments * except the first one (i.e. skip * argv[2]) */ if (argc > 3) { for (n = argc - 1; n > 3; n--) { pbstr(rquote); pbstr(argv[n]); pbstr(lquote); pushback(COMMA); } pbstr(rquote); pbstr(argv[3]); pbstr(lquote); } break;
case DIVRTYPE: if (argc > 2) { n = strtonum(argv[2], INT_MIN, INT_MAX, &errstr); if (errstr) m4errx(1, "divert: argument is %s: %s.", errstr, argv[2]); if (n != 0) { dodiv(n); break; } } active = stdout; oindex = 0; break;
case UNDVTYPE: doundiv(argv, argc); break;
case DIVNTYPE: /* * dodivnum - return the number of * current output diversion */ pbnum(oindex); break;
case UNDFTYPE: /* * doundefine - undefine a previously * defined macro(s) or m4 keyword(s). */ if (argc > 2) for (n = 2; n < argc; n++) macro_undefine(argv[n]); break;
case POPDTYPE: /* * dopopdef - remove the topmost * definitions of macro(s) or m4 * keyword(s). */ if (argc > 2) for (n = 2; n < argc; n++) macro_popdef(argv[n]); break;
case MKTMTYPE: /* * dotemp - create a temporary file */ if (argc > 2) { int fd; char *temp;
temp = xstrdup(argv[2]);
fd = mkstemp(temp); if (fd == -1) err(1, "%s at line %lu: couldn't make temp file %s", CURRENT_NAME, CURRENT_LINE, argv[2]); close(fd); pbstr(temp); free(temp); } break;
case TRNLTYPE: /* * dotranslit - replace all characters in * the source string that appears in the * "from" string with the corresponding * characters in the "to" string. */ if (argc > 3) { char *temp;
temp = xalloc(strlen(argv[2])+1, NULL); if (argc > 4) map(temp, argv[2], argv[3], argv[4]); else map(temp, argv[2], argv[3], null); pbstr(temp); free(temp); } else if (argc > 2) pbstr(argv[2]); break;
case INDXTYPE: /* * doindex - find the index of the second * argument string in the first argument * string. -1 if not present. */ pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); break;
case ERRPTYPE: /* * doerrp - print the arguments to stderr * file */ if (argc > 2) { for (n = 2; n < argc; n++) fprintf(stderr, "%s ", argv[n]); fprintf(stderr, "\n"); } break;
case DNLNTYPE: /* * dodnl - eat-up-to and including * newline */ while ((c = gpbc()) != '\n' && c != EOF) ; break;
case M4WRTYPE: /* * dom4wrap - set up for * wrap-up/wind-down activity */ if (argc > 2) dom4wrap(argv[2]); break;
case EXITTYPE: /* * doexit - immediate exit from m4. */ killdiv(); exit((argc > 2) ? atoi(argv[2]) : 0); break;
case DEFNTYPE: if (argc > 2) for (n = 2; n < argc; n++) dodefn(argv[n]); break;
case INDIRTYPE: /* Indirect call */ if (argc > 2) doindir(argv, argc); break;
case BUILTINTYPE: /* Builtins only */ if (argc > 2) dobuiltin(argv, argc); break;
case PATSTYPE: if (argc > 2) dopatsubst(argv, argc); break; case REGEXPTYPE: if (argc > 2) doregexp(argv, argc); break; case LINETYPE: doprintlineno(infile+ilevel); break; case FILENAMETYPE: doprintfilename(infile+ilevel); break; case SELFTYPE: pbstr(rquote); pbstr(argv[1]); pbstr(lquote); break; default: m4errx(1, "eval: major botch."); break; }}
/* * expand_macro - user-defined macro expansion */voidexpand_macro(const char *argv[], int argc){ const char *t; const char *p; int n; int argno;
t = argv[0]; /* defn string as a whole */ p = t; while (*p) p++; p--; /* last character of defn */ while (p > t) { if (*(p - 1) != ARGFLAG) PUSHBACK(*p); else { switch (*p) {
case '#': pbnum(argc - 2); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if ((argno = *p - '0') < argc - 1) pbstr(argv[argno + 1]); break; case '*': if (argc > 2) { for (n = argc - 1; n > 2; n--) { pbstr(argv[n]); pushback(COMMA); } pbstr(argv[2]); } break; case '@': if (argc > 2) { for (n = argc - 1; n > 2; n--) { pbstr(rquote); pbstr(argv[n]); pbstr(lquote); pushback(COMMA); } pbstr(rquote); pbstr(argv[2]); pbstr(lquote); } break; default: PUSHBACK(*p); PUSHBACK('$'); break; } p--; } p--; } if (p == t) /* do last character */ PUSHBACK(*p);}
/* * dodefine - install definition in the table */voiddodefine(const char *name, const char *defn){ if (!*name && !mimic_gnu) m4errx(1, "null definition."); else macro_define(name, defn);}
/* * dodefn - push back a quoted definition of * the given name. */static voiddodefn(const char *name){ struct macro_definition *p;
if ((p = lookup_macro_definition(name)) != NULL) { if ((p->type & TYPEMASK) == MACRTYPE) { pbstr(rquote); pbstr(p->defn); pbstr(lquote); } else { pbstr(p->defn); pbstr(BUILTIN_MARKER); } }}
/* * dopushdef - install a definition in the hash table * without removing a previous definition. Since * each new entry is entered in *front* of the * hash bucket, it hides a previous definition from * lookup. */static voiddopushdef(const char *name, const char *defn){ if (!*name && !mimic_gnu) m4errx(1, "null definition."); else macro_pushdef(name, defn);}
/* * dump_one_def - dump the specified definition. */static voiddump_one_def(const char *name, struct macro_definition *p){ if (!traceout) traceout = stderr; if (mimic_gnu) { if ((p->type & TYPEMASK) == MACRTYPE) 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", name, p->defn);}
/* * dodumpdef - dump the specified definitions in the hash * table to stderr. If nothing is specified, the entire * hash table is dumped. */static voiddodump(const char *argv[], int argc){ int n; struct macro_definition *p;
if (argc > 2) { for (n = 2; n < argc; n++) if ((p = lookup_macro_definition(argv[n])) != NULL) dump_one_def(argv[n], p); } else macro_for_all(dump_one_def);}
/* * dotrace - mark some macros as traced/untraced depending upon on. */static voiddotrace(const char *argv[], int argc, int on){ int n;
if (argc > 2) { for (n = 2; n < argc; n++) mark_traced(argv[n], on); } else mark_traced(NULL, on);}
/* * doifelse - select one of two alternatives - loop. */static voiddoifelse(const char *argv[], int argc){ while (argc > 4) { if (STREQ(argv[2], argv[3])) { pbstr(argv[4]); break; } else if (argc == 6) { pbstr(argv[5]); break; } else { argv += 3; argc -= 3; } }}
/* * doinclude - include a given file. */static intdoincl(const char *ifile){ if (ilevel + 1 == MAXINP) m4errx(1, "too many include files."); if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { ilevel++; bbase[ilevel] = bufbase = bp; return (1); } else return (0);}
#ifdef EXTENDED/* * dopaste - include a given file without any * macro processing. */static intdopaste(const char *pfile){ FILE *pf; int c;
if ((pf = fopen(pfile, "r")) != NULL) { if (synch_lines) fprintf(active, "#line 1 \"%s\"\n", pfile); while ((c = getc(pf)) != EOF) putc(c, active); (void) fclose(pf); emit_synchline(); return (1); } else return (0);}#endif
/* * dochq - change quote characters */static voiddochq(const char *argv[], int ac){ if (ac == 2) { lquote[0] = LQUOTE; lquote[1] = EOS; rquote[0] = RQUOTE; rquote[1] = EOS; } else { strlcpy(lquote, argv[2], sizeof(lquote)); if (ac > 3) { strlcpy(rquote, argv[3], sizeof(rquote)); } else { rquote[0] = ECOMMT; rquote[1] = EOS; } }}
/* * dochc - change comment characters */static voiddochc(const char *argv[], int argc){/* 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 { strlcpy(scommt, argv[2], sizeof(scommt)); if (argc == 3) { ecommt[0] = ECOMMT; ecommt[1] = EOS; } else { strlcpy(ecommt, argv[3], sizeof(ecommt)); } }}
/* * dom4wrap - expand text at EOF */static voiddom4wrap(const char *text){ if (wrapindex >= maxwraps) { if (maxwraps == 0) maxwraps = 16; else maxwraps *= 2; m4wraps = xreallocarray(m4wraps, maxwraps, sizeof(*m4wraps), "too many m4wraps"); } m4wraps[wrapindex++] = xstrdup(text);}
/* * dodivert - divert the output to a temporary file */static voiddodiv(int n){ int fd;
oindex = n; if (n >= maxout) { if (mimic_gnu) resizedivs(n + 10); else n = 0; /* bitbucket */ }
if (n < 0) n = 0; /* bitbucket */ if (outfile[n] == NULL) { char fname[] = _PATH_DIVNAME;
if ((fd = mkstemp(fname)) == -1 || unlink(fname) == -1 || (outfile[n] = fdopen(fd, "w+")) == NULL) err(1, "%s: cannot divert", fname); } active = outfile[n];}
/* * doundivert - undivert a specified output, or all * other outputs, in numerical order. */static voiddoundiv(const char *argv[], int argc){ int ind; int n;
if (argc > 2) { for (ind = 2; ind < argc; ind++) { 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 for (n = 1; n < maxout; n++) if (outfile[n] != NULL) getdiv(n);}
/* * dosub - select substring */static voiddosub(const char *argv[], int argc){ const char *ap, *fc, *k; int nc;
ap = argv[2]; /* target string */#ifdef EXPR fc = ap + expr(argv[3]); /* first char */#else fc = ap + atoi(argv[3]); /* first char */#endif nc = strlen(fc); if (argc >= 5)#ifdef EXPR nc = min(nc, expr(argv[4]));#else nc = min(nc, atoi(argv[4]));#endif if (fc >= ap && fc < ap + strlen(ap)) for (k = fc + nc - 1; k >= fc; k--) pushback(*k);}
/* * map: * map every character of s1 that is specified in from * into s3 and replace in s. (source s1 remains untouched) * * 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 disappear altogether. */static voidmap(char *dest, const char *src, const char *from, const char *to){ const char *tmp; 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, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 };
if (*src) { if (mimic_gnu) { /* * expand character ranges on the fly */ from = handledash(frombis, frombis + 256, from); to = handledash(tobis, tobis + 256, to); } tmp = from; /* * create a mapping between "from" and * "to" */ 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]; if ((*dest = (char)dch)) dest++; } /* * restore all the changed characters */ while (*tmp) { mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); tmp++; } } *dest = '\0';}
/* * handledash: * use buffer to copy the src string, expanding character ranges * on the way. */static const char *handledash(char *buffer, char *end, const char *src){ char *p;
p = buffer; while(*src) { if (src[1] == '-' && src[2]) { unsigned char i; 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; } else *p++ = *src++; if (p == end) break; } *p = '\0'; return buffer;}