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