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