m4: Sync with FreeBSD.
[dragonfly.git] / usr.bin / m4 / eval.c
1 /*      $OpenBSD: eval.c,v 1.69 2011/03/24 11:23:08 espie Exp $ */
2 /*      $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $      */
3
4 /*
5  * Copyright (c) 1989, 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Ozan Yigit at York University.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * $FreeBSD: src/usr.bin/m4/eval.c,v 1.28 2012/11/17 01:54:24 svnexp Exp $
36  */
37
38
39 /*
40  * eval.c
41  * Facility: m4 macro processor
42  * by: oz
43  */
44
45 #include <sys/types.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <limits.h>
49 #include <unistd.h>
50 #include <stdint.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <stddef.h>
54 #include <string.h>
55 #include <fcntl.h>
56 #include "mdef.h"
57 #include "stdd.h"
58 #include "extern.h"
59 #include "pathnames.h"
60
61 static void     dodefn(const char *);
62 static void     dopushdef(const char *, const char *);
63 static void     dodump(const char *[], int);
64 static void     dotrace(const char *[], int, int);
65 static void     doifelse(const char *[], int);
66 static int      doincl(const char *);
67 static int      dopaste(const char *);
68 static void     dochq(const char *[], int);
69 static void     dochc(const char *[], int);
70 static void     dom4wrap(const char *);
71 static void     dodiv(int);
72 static void     doundiv(const char *[], int);
73 static void     dosub(const char *[], int);
74 static void     map(char *, const char *, const char *, const char *);
75 static const char *handledash(char *, char *, const char *);
76 static void     expand_builtin(const char *[], int, int);
77 static void     expand_macro(const char *[], int);
78 static void     dump_one_def(const char *, struct macro_definition *);
79
80 unsigned long   expansion_id;
81
82 /*
83  * eval - eval all macros and builtins calls
84  *        argc - number of elements in argv.
85  *        argv - element vector :
86  *                      argv[0] = definition of a user
87  *                                macro or NULL if built-in.
88  *                      argv[1] = name of the macro or
89  *                                built-in.
90  *                      argv[2] = parameters to user-defined
91  *                         .      macro or built-in.
92  *                         .
93  *
94  * A call in the form of macro-or-builtin() will result in:
95  *                      argv[0] = nullstr
96  *                      argv[1] = macro-or-builtin
97  *                      argv[2] = nullstr
98  *
99  * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
100  */
101 void
102 eval(const char *argv[], int argc, int td, int is_traced)
103 {
104         size_t mark = SIZE_MAX;
105
106         expansion_id++;
107         if (td & RECDEF)
108                 m4errx(1, "expanding recursive definition for %s.", argv[1]);
109         if (is_traced)
110                 mark = trace(argv, argc, infile + ilevel);
111         if (td == MACRTYPE)
112                 expand_macro(argv, argc);
113         else
114                 expand_builtin(argv, argc, td);
115         if (mark != SIZE_MAX)
116                 finish_trace(mark);
117 }
118
119 /*
120  * expand_builtin - evaluate built-in macros.
121  */
122 void
123 expand_builtin(const char *argv[], int argc, int td)
124 {
125         int c, n;
126         int ac;
127         static int sysval = 0;
128
129 #ifdef DEBUG
130         printf("argc = %d\n", argc);
131         for (n = 0; n < argc; n++)
132                 printf("argv[%d] = %s\n", n, argv[n]);
133         fflush(stdout);
134 #endif
135
136  /*
137   * if argc == 3 and argv[2] is null, then we
138   * have macro-or-builtin() type call. We adjust
139   * argc to avoid further checking.
140   */
141  /* we keep the initial value for those built-ins that differentiate
142   * between builtin() and builtin.
143   */
144         ac = argc;
145
146         if (argc == 3 && !*(argv[2]) && !mimic_gnu)
147                 argc--;
148
149         switch (td & TYPEMASK) {
150
151         case DEFITYPE:
152                 if (argc > 2)
153                         dodefine(argv[2], (argc > 3) ? argv[3] : null);
154                 break;
155
156         case PUSDTYPE:
157                 if (argc > 2)
158                         dopushdef(argv[2], (argc > 3) ? argv[3] : null);
159                 break;
160
161         case DUMPTYPE:
162                 dodump(argv, argc);
163                 break;
164
165         case TRACEONTYPE:
166                 dotrace(argv, argc, 1);
167                 break;
168
169         case TRACEOFFTYPE:
170                 dotrace(argv, argc, 0);
171                 break;
172
173         case EXPRTYPE:
174         /*
175          * doexpr - evaluate arithmetic
176          * expression
177          */
178         {
179                 int base = 10;
180                 int maxdigits = 0;
181                 const char *errstr;
182
183                 if (argc > 3) {
184                         base = strtonum(argv[3], 2, 36, &errstr);
185                         if (errstr) {
186                                 m4errx(1, "expr: base %s invalid.", argv[3]);
187                         }
188                 }
189                 if (argc > 4) {
190                         maxdigits = strtonum(argv[4], 0, INT_MAX, &errstr);
191                         if (errstr) {
192                                 m4errx(1, "expr: maxdigits %s invalid.", argv[4]);
193                         }
194                 }
195                 if (argc > 2)
196                         pbnumbase(expr(argv[2]), base, maxdigits);
197                 break;
198         }
199
200         case IFELTYPE:
201                 if (argc > 4)
202                         doifelse(argv, argc);
203                 break;
204
205         case IFDFTYPE:
206         /*
207          * doifdef - select one of two
208          * alternatives based on the existence of
209          * another definition
210          */
211                 if (argc > 3) {
212                         if (lookup_macro_definition(argv[2]) != NULL)
213                                 pbstr(argv[3]);
214                         else if (argc > 4)
215                                 pbstr(argv[4]);
216                 }
217                 break;
218
219         case LENGTYPE:
220         /*
221          * dolen - find the length of the
222          * argument
223          */
224                 pbnum((argc > 2) ? strlen(argv[2]) : 0);
225                 break;
226
227         case INCRTYPE:
228         /*
229          * doincr - increment the value of the
230          * argument
231          */
232                 if (argc > 2)
233                         pbnum(atoi(argv[2]) + 1);
234                 break;
235
236         case DECRTYPE:
237         /*
238          * dodecr - decrement the value of the
239          * argument
240          */
241                 if (argc > 2)
242                         pbnum(atoi(argv[2]) - 1);
243                 break;
244
245         case SYSCTYPE:
246         /*
247          * dosys - execute system command
248          */
249                 if (argc > 2) {
250                         fflush(stdout);
251                         sysval = system(argv[2]);
252                 }
253                 break;
254
255         case SYSVTYPE:
256         /*
257          * dosysval - return value of the last
258          * system call.
259          *
260          */
261                 pbnum(sysval);
262                 break;
263
264         case ESYSCMDTYPE:
265                 if (argc > 2)
266                         doesyscmd(argv[2]);
267                 break;
268         case INCLTYPE:
269                 if (argc > 2)
270                         if (!doincl(argv[2])) {
271                                 if (mimic_gnu) {
272                                         warn("%s at line %lu: include(%s)",
273                                             CURRENT_NAME, CURRENT_LINE, argv[2]);
274                                         exit_code = 1;
275                                 } else
276                                         err(1, "%s at line %lu: include(%s)",
277                                             CURRENT_NAME, CURRENT_LINE, argv[2]);
278                         }
279                 break;
280
281         case SINCTYPE:
282                 if (argc > 2)
283                         doincl(argv[2]);
284                 break;
285 #ifdef EXTENDED
286         case PASTTYPE:
287                 if (argc > 2)
288                         if (!dopaste(argv[2]))
289                                 err(1, "%s at line %lu: paste(%s)",
290                                     CURRENT_NAME, CURRENT_LINE, argv[2]);
291                 break;
292
293         case SPASTYPE:
294                 if (argc > 2)
295                         dopaste(argv[2]);
296                 break;
297         case FORMATTYPE:
298                 doformat(argv, argc);
299                 break;
300 #endif
301         case CHNQTYPE:
302                 dochq(argv, ac);
303                 break;
304
305         case CHNCTYPE:
306                 dochc(argv, argc);
307                 break;
308
309         case SUBSTYPE:
310         /*
311          * dosub - select substring
312          *
313          */
314                 if (argc > 3)
315                         dosub(argv, argc);
316                 break;
317
318         case SHIFTYPE:
319         /*
320          * doshift - push back all arguments
321          * except the first one (i.e. skip
322          * argv[2])
323          */
324                 if (argc > 3) {
325                         for (n = argc - 1; n > 3; n--) {
326                                 pbstr(rquote);
327                                 pbstr(argv[n]);
328                                 pbstr(lquote);
329                                 pushback(COMMA);
330                         }
331                         pbstr(rquote);
332                         pbstr(argv[3]);
333                         pbstr(lquote);
334                 }
335                 break;
336
337         case DIVRTYPE:
338                 if (argc > 2 && (n = atoi(argv[2])) != 0)
339                         dodiv(n);
340                 else {
341                         active = stdout;
342                         oindex = 0;
343                 }
344                 break;
345
346         case UNDVTYPE:
347                 doundiv(argv, argc);
348                 break;
349
350         case DIVNTYPE:
351         /*
352          * dodivnum - return the number of
353          * current output diversion
354          */
355                 pbnum(oindex);
356                 break;
357
358         case UNDFTYPE:
359         /*
360          * doundefine - undefine a previously
361          * defined macro(s) or m4 keyword(s).
362          */
363                 if (argc > 2)
364                         for (n = 2; n < argc; n++)
365                                 macro_undefine(argv[n]);
366                 break;
367
368         case POPDTYPE:
369         /*
370          * dopopdef - remove the topmost
371          * definitions of macro(s) or m4
372          * keyword(s).
373          */
374                 if (argc > 2)
375                         for (n = 2; n < argc; n++)
376                                 macro_popdef(argv[n]);
377                 break;
378
379         case MKTMTYPE:
380         /*
381          * dotemp - create a temporary file
382          */
383                 if (argc > 2) {
384                         int fd;
385                         char *temp;
386
387                         temp = xstrdup(argv[2]);
388
389                         fd = mkstemp(temp);
390                         if (fd == -1)
391                                 err(1,
392             "%s at line %lu: couldn't make temp file %s",
393             CURRENT_NAME, CURRENT_LINE, argv[2]);
394                         close(fd);
395                         pbstr(temp);
396                         free(temp);
397                 }
398                 break;
399
400         case TRNLTYPE:
401         /*
402          * dotranslit - replace all characters in
403          * the source string that appears in the
404          * "from" string with the corresponding
405          * characters in the "to" string.
406          */
407                 if (argc > 3) {
408                         char *temp;
409
410                         temp = xalloc(strlen(argv[2]) + 1, NULL);
411                         if (argc > 4)
412                                 map(temp, argv[2], argv[3], argv[4]);
413                         else
414                                 map(temp, argv[2], argv[3], null);
415                         pbstr(temp);
416                         free(temp);
417                 } else if (argc > 2)
418                         pbstr(argv[2]);
419                 break;
420
421         case INDXTYPE:
422         /*
423          * doindex - find the index of the second
424          * argument string in the first argument
425          * string. -1 if not present.
426          */
427                 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
428                 break;
429
430         case ERRPTYPE:
431         /*
432          * doerrp - print the arguments to stderr
433          * file
434          */
435                 if (argc > 2) {
436                         for (n = 2; n < argc; n++)
437                                 fprintf(stderr, "%s ", argv[n]);
438                         fprintf(stderr, "\n");
439                 }
440                 break;
441
442         case DNLNTYPE:
443         /*
444          * dodnl - eat-up-to and including
445          * newline
446          */
447                 while ((c = gpbc()) != '\n' && c != EOF)
448                         ;
449                 break;
450
451         case M4WRTYPE:
452         /*
453          * dom4wrap - set up for
454          * wrap-up/wind-down activity
455          */
456                 if (argc > 2)
457                         dom4wrap(argv[2]);
458                 break;
459
460         case EXITTYPE:
461         /*
462          * doexit - immediate exit from m4.
463          */
464                 killdiv();
465                 exit((argc > 2) ? atoi(argv[2]) : 0);
466                 break;
467
468         case DEFNTYPE:
469                 if (argc > 2)
470                         for (n = 2; n < argc; n++)
471                                 dodefn(argv[n]);
472                 break;
473
474         case INDIRTYPE: /* Indirect call */
475                 if (argc > 2)
476                         doindir(argv, argc);
477                 break;
478
479         case BUILTINTYPE: /* Builtins only */
480                 if (argc > 2)
481                         dobuiltin(argv, argc);
482                 break;
483
484         case PATSTYPE:
485                 if (argc > 2)
486                         dopatsubst(argv, argc);
487                 break;
488         case REGEXPTYPE:
489                 if (argc > 2)
490                         doregexp(argv, argc);
491                 break;
492         case LINETYPE:
493                 doprintlineno(infile+ilevel);
494                 break;
495         case FILENAMETYPE:
496                 doprintfilename(infile+ilevel);
497                 break;
498         case SELFTYPE:
499                 pbstr(rquote);
500                 pbstr(argv[1]);
501                 pbstr(lquote);
502                 break;
503         default:
504                 m4errx(1, "eval: major botch.");
505                 break;
506         }
507 }
508
509 /*
510  * expand_macro - user-defined macro expansion
511  */
512 void
513 expand_macro(const char *argv[], int argc)
514 {
515         const char *t;
516         const char *p;
517         int n;
518         int argno;
519
520         t = argv[0];                   /* defn string as a whole */
521         p = t;
522         while (*p)
523                 p++;
524         p--;                           /* last character of defn */
525         while (p > t) {
526                 if (*(p - 1) != ARGFLAG)
527                         PUSHBACK(*p);
528                 else {
529                         switch (*p) {
530
531                         case '#':
532                                 pbnum(argc - 2);
533                                 break;
534                         case '0':
535                         case '1':
536                         case '2':
537                         case '3':
538                         case '4':
539                         case '5':
540                         case '6':
541                         case '7':
542                         case '8':
543                         case '9':
544                                 if ((argno = *p - '0') < argc - 1)
545                                         pbstr(argv[argno + 1]);
546                                 break;
547                         case '*':
548                                 if (argc > 2) {
549                                         for (n = argc - 1; n > 2; n--) {
550                                                 pbstr(argv[n]);
551                                                 pushback(COMMA);
552                                         }
553                                         pbstr(argv[2]);
554                                 }
555                                 break;
556                         case '@':
557                                 if (argc > 2) {
558                                         for (n = argc - 1; n > 2; n--) {
559                                                 pbstr(rquote);
560                                                 pbstr(argv[n]);
561                                                 pbstr(lquote);
562                                                 pushback(COMMA);
563                                         }
564                                         pbstr(rquote);
565                                         pbstr(argv[2]);
566                                         pbstr(lquote);
567                                 }
568                                 break;
569                         default:
570                                 PUSHBACK(*p);
571                                 PUSHBACK('$');
572                                 break;
573                         }
574                         p--;
575                 }
576                 p--;
577         }
578         if (p == t)                    /* do last character */
579                 PUSHBACK(*p);
580 }
581
582
583 /*
584  * dodefine - install definition in the table
585  */
586 void
587 dodefine(const char *name, const char *defn)
588 {
589         if (!*name && !mimic_gnu)
590                 m4errx(1, "null definition.");
591         else
592                 macro_define(name, defn);
593 }
594
595 /*
596  * dodefn - push back a quoted definition of
597  *      the given name.
598  */
599 static void
600 dodefn(const char *name)
601 {
602         struct macro_definition *p;
603
604         if ((p = lookup_macro_definition(name)) != NULL) {
605                 if ((p->type & TYPEMASK) == MACRTYPE) {
606                         pbstr(rquote);
607                         pbstr(p->defn);
608                         pbstr(lquote);
609                 } else {
610                         pbstr(p->defn);
611                         pbstr(BUILTIN_MARKER);
612                 }
613         }
614 }
615
616 /*
617  * dopushdef - install a definition in the hash table
618  *      without removing a previous definition. Since
619  *      each new entry is entered in *front* of the
620  *      hash bucket, it hides a previous definition from
621  *      lookup.
622  */
623 static void
624 dopushdef(const char *name, const char *defn)
625 {
626         if (!*name && !mimic_gnu)
627                 m4errx(1, "null definition.");
628         else
629                 macro_pushdef(name, defn);
630 }
631
632 /*
633  * dump_one_def - dump the specified definition.
634  */
635 static void
636 dump_one_def(const char *name, struct macro_definition *p)
637 {
638         if (!traceout)
639                 traceout = stderr;
640         if (mimic_gnu) {
641                 if ((p->type & TYPEMASK) == MACRTYPE)
642                         fprintf(traceout, "%s:\t%s\n", name, p->defn);
643                 else
644                         fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
645         } else
646                 fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
647 }
648
649 /*
650  * dodumpdef - dump the specified definitions in the hash
651  *      table to stderr. If nothing is specified, the entire
652  *      hash table is dumped.
653  */
654 static void
655 dodump(const char *argv[], int argc)
656 {
657         int n;
658         struct macro_definition *p;
659
660         if (argc > 2) {
661                 for (n = 2; n < argc; n++)
662                         if ((p = lookup_macro_definition(argv[n])) != NULL)
663                                 dump_one_def(argv[n], p);
664         } else
665                 macro_for_all(dump_one_def);
666 }
667
668 /*
669  * dotrace - mark some macros as traced/untraced depending upon on.
670  */
671 static void
672 dotrace(const char *argv[], int argc, int on)
673 {
674         int n;
675
676         if (argc > 2) {
677                 for (n = 2; n < argc; n++)
678                         mark_traced(argv[n], on);
679         } else
680                 mark_traced(NULL, on);
681 }
682
683 /*
684  * doifelse - select one of two alternatives - loop.
685  */
686 static void
687 doifelse(const char *argv[], int argc)
688 {
689         for(;;) {
690                 if (STREQ(argv[2], argv[3]))
691                         pbstr(argv[4]);
692                 else if (argc == 6)
693                         pbstr(argv[5]);
694                 else if (argc > 6) {
695                         argv += 3;
696                         argc -= 3;
697                         continue;
698                 }
699                 break;
700         }
701 }
702
703 /*
704  * doinclude - include a given file.
705  */
706 static int
707 doincl(const char *ifile)
708 {
709         if (ilevel + 1 == MAXINP)
710                 m4errx(1, "too many include files.");
711         if (fopen_trypath(infile + ilevel + 1, ifile) != NULL) {
712                 ilevel++;
713                 bbase[ilevel] = bufbase = bp;
714                 return (1);
715         } else
716                 return (0);
717 }
718
719 #ifdef EXTENDED
720 /*
721  * dopaste - include a given file without any
722  *           macro processing.
723  */
724 static int
725 dopaste(const char *pfile)
726 {
727         FILE *pf;
728         int c;
729
730         if ((pf = fopen(pfile, "r")) != NULL) {
731                 if (synch_lines)
732                     fprintf(active, "#line 1 \"%s\"\n", pfile);
733                 while ((c = getc(pf)) != EOF)
734                         putc(c, active);
735                 fclose(pf);
736                 emit_synchline();
737                 return (1);
738         } else
739                 return (0);
740 }
741 #endif
742
743 /*
744  * dochq - change quote characters
745  */
746 static void
747 dochq(const char *argv[], int ac)
748 {
749         if (ac == 2) {
750                 lquote[0] = LQUOTE; lquote[1] = EOS;
751                 rquote[0] = RQUOTE; rquote[1] = EOS;
752         } else {
753                 strlcpy(lquote, argv[2], sizeof(lquote));
754                 if (ac > 3) {
755                         strlcpy(rquote, argv[3], sizeof(rquote));
756                 } else {
757                         rquote[0] = ECOMMT; rquote[1] = EOS;
758                 }
759         }
760 }
761
762 /*
763  * dochc - change comment characters
764  */
765 static void
766 dochc(const char *argv[], int argc)
767 {
768 /* XXX Note that there is no difference between no argument and a single
769  * empty argument.
770  */
771         if (argc == 2) {
772                 scommt[0] = EOS;
773                 ecommt[0] = EOS;
774         } else {
775                 strlcpy(scommt, argv[2], sizeof(scommt));
776                 if (argc == 3) {
777                         ecommt[0] = ECOMMT; ecommt[1] = EOS;
778                 } else {
779                         strlcpy(ecommt, argv[3], sizeof(ecommt));
780                 }
781         }
782 }
783
784 /*
785  * dom4wrap - expand text at EOF
786  */
787 static void
788 dom4wrap(const char *text)
789 {
790         if (wrapindex >= maxwraps) {
791                 if (maxwraps == 0)
792                         maxwraps = 16;
793                 else
794                         maxwraps *= 2;
795                 m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
796                    "too many m4wraps");
797         }
798         m4wraps[wrapindex++] = xstrdup(text);
799 }
800
801 /*
802  * dodivert - divert the output to a temporary file
803  */
804 static void
805 dodiv(int n)
806 {
807         int fd;
808
809         oindex = n;
810         if (n >= maxout) {
811                 if (mimic_gnu)
812                         resizedivs(n + 10);
813                 else
814                         n = 0;          /* bitbucket */
815         }
816
817         if (n < 0)
818                 n = 0;                 /* bitbucket */
819         if (outfile[n] == NULL) {
820                 char fname[] = _PATH_DIVNAME;
821
822                 if ((fd = mkstemp(fname)) < 0 ||
823                         (outfile[n] = fdopen(fd, "w+")) == NULL)
824                                 err(1, "%s: cannot divert", fname);
825                 if (unlink(fname) == -1)
826                         err(1, "%s: cannot unlink", fname);
827         }
828         active = outfile[n];
829 }
830
831 /*
832  * doundivert - undivert a specified output, or all
833  *              other outputs, in numerical order.
834  */
835 static void
836 doundiv(const char *argv[], int argc)
837 {
838         int ind;
839         int n;
840
841         if (argc > 2) {
842                 for (ind = 2; ind < argc; ind++) {
843                         const char *errstr;
844                         n = strtonum(argv[ind], 1, INT_MAX, &errstr);
845                         if (errstr) {
846                                 if (errno == EINVAL && mimic_gnu)
847                                         getdivfile(argv[ind]);
848                         } else {
849                                 if (n < maxout && outfile[n] != NULL)
850                                         getdiv(n);
851                         }
852                 }
853         }
854         else
855                 for (n = 1; n < maxout; n++)
856                         if (outfile[n] != NULL)
857                                 getdiv(n);
858 }
859
860 /*
861  * dosub - select substring
862  */
863 static void
864 dosub(const char *argv[], int argc)
865 {
866         const char *ap, *fc, *k;
867         int nc;
868
869         ap = argv[2];                  /* target string */
870 #ifdef EXPR
871         fc = ap + expr(argv[3]);       /* first char */
872 #else
873         fc = ap + atoi(argv[3]);       /* first char */
874 #endif
875         nc = strlen(fc);
876         if (argc >= 5)
877 #ifdef EXPR
878                 nc = min(nc, expr(argv[4]));
879 #else
880                 nc = min(nc, atoi(argv[4]));
881 #endif
882         if (fc >= ap && fc < ap + strlen(ap))
883                 for (k = fc + nc - 1; k >= fc; k--)
884                         pushback(*k);
885 }
886
887 /*
888  * map:
889  * map every character of s1 that is specified in from
890  * into s3 and replace in s. (source s1 remains untouched)
891  *
892  * This is derived from the a standard implementation of map(s,from,to)
893  * function of ICON language. Within mapvec, we replace every character
894  * of "from" with the corresponding character in "to".
895  * If "to" is shorter than "from", than the corresponding entries are null,
896  * which means that those characters dissapear altogether.
897  */
898 static void
899 map(char *dest, const char *src, const char *from, const char *to)
900 {
901         const char *tmp;
902         unsigned char sch, dch;
903         static char frombis[257];
904         static char tobis[257];
905         int i;
906         char seen[256];
907         static unsigned char mapvec[256] = {
908             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
909             19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
910             36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
911             53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
912             70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
913             87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
914             103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
915             116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
916             129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
917             142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
918             155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
919             168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
920             181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
921             194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
922             207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
923             220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
924             233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
925             246, 247, 248, 249, 250, 251, 252, 253, 254, 255
926         };
927
928         if (*src) {
929                 if (mimic_gnu) {
930                         /*
931                          * expand character ranges on the fly
932                          */
933                         from = handledash(frombis, frombis + 256, from);
934                         to = handledash(tobis, tobis + 256, to);
935                 }
936                 tmp = from;
937         /*
938          * create a mapping between "from" and
939          * "to"
940          */
941                 for (i = 0; i < 256; i++)
942                         seen[i] = 0;
943                 while (*from) {
944                         if (!seen[(unsigned char)(*from)]) {
945                                 mapvec[(unsigned char)(*from)] = (unsigned char)(*to);
946                                 seen[(unsigned char)(*from)] = 1;
947                         }
948                         from++;
949                         if (*to)
950                                 to++;
951                 }
952
953                 while (*src) {
954                         sch = (unsigned char)(*src++);
955                         dch = mapvec[sch];
956                         if ((*dest = (char)dch))
957                                 dest++;
958                 }
959         /*
960          * restore all the changed characters
961          */
962                 while (*tmp) {
963                         mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
964                         tmp++;
965                 }
966         }
967         *dest = '\0';
968 }
969
970
971 /*
972  * handledash:
973  *  use buffer to copy the src string, expanding character ranges
974  * on the way.
975  */
976 static const char *
977 handledash(char *buffer, char *end, const char *src)
978 {
979         char *p;
980
981         p = buffer;
982         while(*src) {
983                 if (src[1] == '-' && src[2]) {
984                         unsigned char i;
985                         if ((unsigned char)src[0] <= (unsigned char)src[2]) {
986                                 for (i = (unsigned char)src[0];
987                                     i <= (unsigned char)src[2]; i++) {
988                                         *p++ = i;
989                                         if (p == end) {
990                                                 *p = '\0';
991                                                 return buffer;
992                                         }
993                                 }
994                         } else {
995                                 for (i = (unsigned char)src[0];
996                                     i >= (unsigned char)src[2]; i--) {
997                                         *p++ = i;
998                                         if (p == end) {
999                                                 *p = '\0';
1000                                                 return buffer;
1001                                         }
1002                                 }
1003                         }
1004                         src += 3;
1005                 } else
1006                         *p++ = *src++;
1007                 if (p == end)
1008                         break;
1009         }
1010         *p = '\0';
1011         return buffer;
1012 }