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