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