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