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