]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.bin/m4/eval.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / usr.bin / m4 / eval.c
1 /*      $OpenBSD: eval.c,v 1.70 2012/04/12 17:00:11 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
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 <stdint.h>
52 #include <stdio.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                 break;
281
282         case SINCTYPE:
283                 if (argc > 2)
284                         (void) doincl(argv[2]);
285                 break;
286 #ifdef EXTENDED
287         case PASTTYPE:
288                 if (argc > 2)
289                         if (!dopaste(argv[2]))
290                                 err(1, "%s at line %lu: paste(%s)", 
291                                     CURRENT_NAME, CURRENT_LINE, argv[2]);
292                 break;
293
294         case SPASTYPE:
295                 if (argc > 2)
296                         (void) dopaste(argv[2]);
297                 break;
298         case FORMATTYPE:
299                 doformat(argv, argc);
300                 break;
301 #endif
302         case CHNQTYPE:
303                 dochq(argv, ac);
304                 break;
305
306         case CHNCTYPE:
307                 dochc(argv, argc);
308                 break;
309
310         case SUBSTYPE:
311         /*
312          * dosub - select substring
313          *
314          */
315                 if (argc > 3)
316                         dosub(argv, argc);
317                 break;
318
319         case SHIFTYPE:
320         /*
321          * doshift - push back all arguments
322          * except the first one (i.e. skip
323          * argv[2])
324          */
325                 if (argc > 3) {
326                         for (n = argc - 1; n > 3; n--) {
327                                 pbstr(rquote);
328                                 pbstr(argv[n]);
329                                 pbstr(lquote);
330                                 pushback(COMMA);
331                         }
332                         pbstr(rquote);
333                         pbstr(argv[3]);
334                         pbstr(lquote);
335                 }
336                 break;
337
338         case DIVRTYPE:
339                 if (argc > 2 && (n = atoi(argv[2])) != 0)
340                         dodiv(n);
341                 else {
342                         active = stdout;
343                         oindex = 0;
344                 }
345                 break;
346
347         case UNDVTYPE:
348                 doundiv(argv, argc);
349                 break;
350
351         case DIVNTYPE:
352         /*
353          * dodivnum - return the number of
354          * current output diversion
355          */
356                 pbnum(oindex);
357                 break;
358
359         case UNDFTYPE:
360         /*
361          * doundefine - undefine a previously
362          * defined macro(s) or m4 keyword(s).
363          */
364                 if (argc > 2)
365                         for (n = 2; n < argc; n++)
366                                 macro_undefine(argv[n]);
367                 break;
368
369         case POPDTYPE:
370         /*
371          * dopopdef - remove the topmost
372          * definitions of macro(s) or m4
373          * keyword(s).
374          */
375                 if (argc > 2)
376                         for (n = 2; n < argc; n++)
377                                 macro_popdef(argv[n]);
378                 break;
379
380         case MKTMTYPE:
381         /*
382          * dotemp - create a temporary file
383          */
384                 if (argc > 2) {
385                         int fd;
386                         char *temp;
387
388                         temp = xstrdup(argv[2]);
389
390                         fd = mkstemp(temp);
391                         if (fd == -1)
392                                 err(1,
393             "%s at line %lu: couldn't make temp file %s",
394             CURRENT_NAME, CURRENT_LINE, argv[2]);
395                         close(fd);
396                         pbstr(temp);
397                         free(temp);
398                 }
399                 break;
400
401         case TRNLTYPE:
402         /*
403          * dotranslit - replace all characters in
404          * the source string that appears in the
405          * "from" string with the corresponding
406          * characters in the "to" string.
407          */
408                 if (argc > 3) {
409                         char *temp;
410
411                         temp = xalloc(strlen(argv[2])+1, NULL);
412                         if (argc > 4)
413                                 map(temp, argv[2], argv[3], argv[4]);
414                         else
415                                 map(temp, argv[2], argv[3], null);
416                         pbstr(temp);
417                         free(temp);
418                 } else if (argc > 2)
419                         pbstr(argv[2]);
420                 break;
421
422         case INDXTYPE:
423         /*
424          * doindex - find the index of the second
425          * argument string in the first argument
426          * string. -1 if not present.
427          */
428                 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
429                 break;
430
431         case ERRPTYPE:
432         /*
433          * doerrp - print the arguments to stderr
434          * file
435          */
436                 if (argc > 2) {
437                         for (n = 2; n < argc; n++)
438                                 fprintf(stderr, "%s ", argv[n]);
439                         fprintf(stderr, "\n");
440                 }
441                 break;
442
443         case DNLNTYPE:
444         /*
445          * dodnl - eat-up-to and including
446          * newline
447          */
448                 while ((c = gpbc()) != '\n' && c != EOF)
449                         ;
450                 break;
451
452         case M4WRTYPE:
453         /*
454          * dom4wrap - set up for
455          * wrap-up/wind-down activity
456          */
457                 if (argc > 2)
458                         dom4wrap(argv[2]);
459                 break;
460
461         case EXITTYPE:
462         /*
463          * doexit - immediate exit from m4.
464          */
465                 killdiv();
466                 exit((argc > 2) ? atoi(argv[2]) : 0);
467                 break;
468
469         case DEFNTYPE:
470                 if (argc > 2)
471                         for (n = 2; n < argc; n++)
472                                 dodefn(argv[n]);
473                 break;
474
475         case INDIRTYPE: /* Indirect call */
476                 if (argc > 2)
477                         doindir(argv, argc);
478                 break;
479
480         case BUILTINTYPE: /* Builtins only */
481                 if (argc > 2)
482                         dobuiltin(argv, argc);
483                 break;
484
485         case PATSTYPE:
486                 if (argc > 2)
487                         dopatsubst(argv, argc);
488                 break;
489         case REGEXPTYPE:
490                 if (argc > 2)
491                         doregexp(argv, argc);
492                 break;
493         case LINETYPE:
494                 doprintlineno(infile+ilevel);
495                 break;
496         case FILENAMETYPE:
497                 doprintfilename(infile+ilevel);
498                 break;
499         case SELFTYPE:
500                 pbstr(rquote);
501                 pbstr(argv[1]);
502                 pbstr(lquote);
503                 break;
504         default:
505                 m4errx(1, "eval: major botch.");
506                 break;
507         }
508 }
509
510 /*
511  * expand_macro - user-defined macro expansion
512  */
513 void
514 expand_macro(const char *argv[], int argc)
515 {
516         const char *t;
517         const char *p;
518         int n;
519         int argno;
520
521         t = argv[0];                   /* defn string as a whole */
522         p = t;
523         while (*p)
524                 p++;
525         p--;                           /* last character of defn */
526         while (p > t) {
527                 if (*(p - 1) != ARGFLAG)
528                         PUSHBACK(*p);
529                 else {
530                         switch (*p) {
531
532                         case '#':
533                                 pbnum(argc - 2);
534                                 break;
535                         case '0':
536                         case '1':
537                         case '2':
538                         case '3':
539                         case '4':
540                         case '5':
541                         case '6':
542                         case '7':
543                         case '8':
544                         case '9':
545                                 if ((argno = *p - '0') < argc - 1)
546                                         pbstr(argv[argno + 1]);
547                                 break;
548                         case '*':
549                                 if (argc > 2) {
550                                         for (n = argc - 1; n > 2; n--) {
551                                                 pbstr(argv[n]);
552                                                 pushback(COMMA);
553                                         }
554                                         pbstr(argv[2]);
555                                 }
556                                 break;
557                         case '@':
558                                 if (argc > 2) {
559                                         for (n = argc - 1; n > 2; n--) {
560                                                 pbstr(rquote);
561                                                 pbstr(argv[n]);
562                                                 pbstr(lquote);
563                                                 pushback(COMMA);
564                                         }
565                                         pbstr(rquote);
566                                         pbstr(argv[2]);
567                                         pbstr(lquote);
568                                 }
569                                 break;
570                         default:
571                                 PUSHBACK(*p);
572                                 PUSHBACK('$');
573                                 break;
574                         }
575                         p--;
576                 }
577                 p--;
578         }
579         if (p == t)                    /* do last character */
580                 PUSHBACK(*p);
581 }
582
583
584 /*
585  * dodefine - install definition in the table
586  */
587 void
588 dodefine(const char *name, const char *defn)
589 {
590         if (!*name && !mimic_gnu)
591                 m4errx(1, "null definition.");
592         else
593                 macro_define(name, defn);
594 }
595
596 /*
597  * dodefn - push back a quoted definition of
598  *      the given name.
599  */
600 static void
601 dodefn(const char *name)
602 {
603         struct macro_definition *p;
604
605         if ((p = lookup_macro_definition(name)) != NULL) {
606                 if ((p->type & TYPEMASK) == MACRTYPE) {
607                         pbstr(rquote);
608                         pbstr(p->defn);
609                         pbstr(lquote);
610                 } else {
611                         pbstr(p->defn);
612                         pbstr(BUILTIN_MARKER);
613                 }
614         }
615 }
616
617 /*
618  * dopushdef - install a definition in the hash table
619  *      without removing a previous definition. Since
620  *      each new entry is entered in *front* of the
621  *      hash bucket, it hides a previous definition from
622  *      lookup.
623  */
624 static void
625 dopushdef(const char *name, const char *defn)
626 {
627         if (!*name && !mimic_gnu)
628                 m4errx(1, "null definition.");
629         else
630                 macro_pushdef(name, defn);
631 }
632
633 /*
634  * dump_one_def - dump the specified definition.
635  */
636 static void
637 dump_one_def(const char *name, struct macro_definition *p)
638 {
639         if (!traceout)
640                 traceout = stderr;
641         if (mimic_gnu) {
642                 if ((p->type & TYPEMASK) == MACRTYPE)
643                         fprintf(traceout, "%s:\t%s\n", name, p->defn);
644                 else {
645                         fprintf(traceout, "%s:\t<%s>\n", name, p->defn);
646                 }
647         } else
648                 fprintf(traceout, "`%s'\t`%s'\n", name, p->defn);
649 }
650
651 /*
652  * dodumpdef - dump the specified definitions in the hash
653  *      table to stderr. If nothing is specified, the entire
654  *      hash table is dumped.
655  */
656 static void
657 dodump(const char *argv[], int argc)
658 {
659         int n;
660         struct macro_definition *p;
661
662         if (argc > 2) {
663                 for (n = 2; n < argc; n++)
664                         if ((p = lookup_macro_definition(argv[n])) != NULL)
665                                 dump_one_def(argv[n], p);
666         } else
667                 macro_for_all(dump_one_def);
668 }
669
670 /*
671  * dotrace - mark some macros as traced/untraced depending upon on.
672  */
673 static void
674 dotrace(const char *argv[], int argc, int on)
675 {
676         int n;
677
678         if (argc > 2) {
679                 for (n = 2; n < argc; n++)
680                         mark_traced(argv[n], on);
681         } else
682                 mark_traced(NULL, on);
683 }
684
685 /*
686  * doifelse - select one of two alternatives - loop.
687  */
688 static void
689 doifelse(const char *argv[], int argc)
690 {
691         cycle {
692                 if (STREQ(argv[2], argv[3]))
693                         pbstr(argv[4]);
694                 else if (argc == 6)
695                         pbstr(argv[5]);
696                 else if (argc > 6) {
697                         argv += 3;
698                         argc -= 3;
699                         continue;
700                 }
701                 break;
702         }
703 }
704
705 /*
706  * doinclude - include a given file.
707  */
708 static int
709 doincl(const char *ifile)
710 {
711         if (ilevel + 1 == MAXINP)
712                 m4errx(1, "too many include files.");
713         if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
714                 ilevel++;
715                 bbase[ilevel] = bufbase = bp;
716                 return (1);
717         } else
718                 return (0);
719 }
720
721 #ifdef EXTENDED
722 /*
723  * dopaste - include a given file without any
724  *           macro processing.
725  */
726 static int
727 dopaste(const char *pfile)
728 {
729         FILE *pf;
730         int c;
731
732         if ((pf = fopen(pfile, "r")) != NULL) {
733                 if (synch_lines)
734                     fprintf(active, "#line 1 \"%s\"\n", pfile);
735                 while ((c = getc(pf)) != EOF)
736                         putc(c, active);
737                 (void) fclose(pf);
738                 emit_synchline();
739                 return (1);
740         } else
741                 return (0);
742 }
743 #endif
744
745 /*
746  * dochq - change quote characters
747  */
748 static void
749 dochq(const char *argv[], int ac)
750 {
751         if (ac == 2) {
752                 lquote[0] = LQUOTE; lquote[1] = EOS;
753                 rquote[0] = RQUOTE; rquote[1] = EOS;
754         } else {
755                 strlcpy(lquote, argv[2], sizeof(lquote));
756                 if (ac > 3) {
757                         strlcpy(rquote, argv[3], sizeof(rquote));
758                 } else {
759                         rquote[0] = ECOMMT; rquote[1] = EOS;
760                 }
761         }
762 }
763
764 /*
765  * dochc - change comment characters
766  */
767 static void
768 dochc(const char *argv[], int argc)
769 {
770 /* XXX Note that there is no difference between no argument and a single
771  * empty argument.
772  */
773         if (argc == 2) {
774                 scommt[0] = EOS;
775                 ecommt[0] = EOS;
776         } else {
777                 strlcpy(scommt, argv[2], sizeof(scommt));
778                 if (argc == 3) {
779                         ecommt[0] = ECOMMT; ecommt[1] = EOS;
780                 } else {
781                         strlcpy(ecommt, argv[3], sizeof(ecommt));
782                 }
783         }
784 }
785
786 /*
787  * dom4wrap - expand text at EOF
788  */
789 static void
790 dom4wrap(const char *text)
791 {
792         if (wrapindex >= maxwraps) {
793                 if (maxwraps == 0)
794                         maxwraps = 16;
795                 else
796                         maxwraps *= 2;
797                 m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps),
798                    "too many m4wraps");
799         }
800         m4wraps[wrapindex++] = xstrdup(text);
801 }
802
803 /*
804  * dodivert - divert the output to a temporary file
805  */
806 static void
807 dodiv(int n)
808 {
809         int fd;
810
811         oindex = n;
812         if (n >= maxout) {
813                 if (mimic_gnu)
814                         resizedivs(n + 10);
815                 else
816                         n = 0;          /* bitbucket */
817         }
818
819         if (n < 0)
820                 n = 0;                 /* bitbucket */
821         if (outfile[n] == NULL) {
822                 char fname[] = _PATH_DIVNAME;
823
824                 if ((fd = mkstemp(fname)) < 0 || 
825                         (outfile[n] = fdopen(fd, "w+")) == NULL)
826                                 err(1, "%s: cannot divert", fname);
827                 if (unlink(fname) == -1)
828                         err(1, "%s: cannot unlink", 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 dissapear 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 }