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