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