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