eval.c revision 1.19 1 /* $NetBSD: eval.c,v 1.19 2004/07/06 13:19:42 mycroft 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. 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 #if HAVE_NBTOOL_CONFIG_H
37 #include "nbtool_config.h"
38 #endif
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.19 2004/07/06 13:19:42 mycroft 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 FILE *out = traceout ? traceout : stderr;
665 const char *real;
666
667 if (mimic_gnu) {
668 if ((p->type & TYPEMASK) == MACRTYPE)
669 fprintf(out, "%s:\t%s\n", p->name, p->defn);
670 else {
671 real = builtin_realname(p->type);
672 if (real == NULL)
673 real = null;
674 fprintf(out, "%s:\t<%s>\n", p->name, real);
675 }
676 } else
677 fprintf(out, "`%s'\t`%s'\n", p->name, p->defn);
678 }
679
680 /*
681 * dodumpdef - dump the specified definitions in the hash
682 * table to stderr. If nothing is specified, the entire
683 * hash table is dumped.
684 */
685 static void
686 dodump(argv, argc)
687 const char *argv[];
688 int argc;
689 {
690 int n;
691 ndptr p;
692
693 if (argc > 2) {
694 for (n = 2; n < argc; n++)
695 if ((p = lookup(argv[n])) != nil)
696 dump_one_def(p);
697 } else {
698 for (n = 0; n < HASHSIZE; n++)
699 for (p = hashtab[n]; p != nil; p = p->nxtptr)
700 dump_one_def(p);
701 }
702 }
703
704 /*
705 * dotrace - mark some macros as traced/untraced depending upon on.
706 */
707 static void
708 dotrace(argv, argc, on)
709 const char *argv[];
710 int argc;
711 int on;
712 {
713 int n;
714
715 if (argc > 2) {
716 for (n = 2; n < argc; n++)
717 mark_traced(argv[n], on);
718 } else
719 mark_traced(NULL, on);
720 }
721
722 /*
723 * doifelse - select one of two alternatives - loop.
724 */
725 static void
726 doifelse(argv, argc)
727 const char *argv[];
728 int argc;
729 {
730 cycle {
731 if (STREQ(argv[2], argv[3]))
732 pbstr(argv[4]);
733 else if (argc == 6)
734 pbstr(argv[5]);
735 else if (argc > 6) {
736 argv += 3;
737 argc -= 3;
738 continue;
739 }
740 break;
741 }
742 }
743
744 /*
745 * doinclude - include a given file.
746 */
747 static int
748 doincl(ifile)
749 const char *ifile;
750 {
751 if (ilevel + 1 == MAXINP)
752 errx(1, "%s at line %lu: too many include files.",
753 CURRENT_NAME, CURRENT_LINE);
754 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
755 ilevel++;
756 bbase[ilevel] = bufbase = bp;
757 return (1);
758 } else
759 return (0);
760 }
761
762 #ifdef EXTENDED
763 /*
764 * dopaste - include a given file without any
765 * macro processing.
766 */
767 static int
768 dopaste(pfile)
769 const char *pfile;
770 {
771 FILE *pf;
772 int c;
773
774 if ((pf = fopen(pfile, "r")) != NULL) {
775 while ((c = getc(pf)) != EOF)
776 putc(c, active);
777 (void) fclose(pf);
778 return (1);
779 } else
780 return (0);
781 }
782 #endif
783
784 static void
785 gnu_dochq(argv, ac)
786 const char *argv[];
787 int ac;
788 {
789 /* In gnu-m4 mode, the only way to restore quotes is to have no
790 * arguments at all. */
791 if (ac == 2) {
792 lquote[0] = LQUOTE, lquote[1] = EOS;
793 rquote[0] = RQUOTE, rquote[1] = EOS;
794 } else {
795 strlcpy(lquote, argv[2], sizeof(lquote));
796 if(ac > 3)
797 strlcpy(rquote, argv[3], sizeof(rquote));
798 else
799 rquote[0] = EOS;
800 }
801 }
802
803 /*
804 * dochq - change quote characters
805 */
806 static void
807 dochq(argv, argc)
808 const char *argv[];
809 int argc;
810 {
811 if (argc > 2) {
812 if (*argv[2])
813 strlcpy(lquote, argv[2], sizeof(lquote));
814 else {
815 lquote[0] = LQUOTE;
816 lquote[1] = EOS;
817 }
818 if (argc > 3) {
819 if (*argv[3])
820 strlcpy(rquote, argv[3], sizeof(rquote));
821 } else
822 strcpy(rquote, lquote);
823 } else {
824 lquote[0] = LQUOTE, lquote[1] = EOS;
825 rquote[0] = RQUOTE, rquote[1] = EOS;
826 }
827 }
828
829 static void
830 gnu_dochc(argv, ac)
831 const char *argv[];
832 int ac;
833 {
834 /* In gnu-m4 mode, no arguments mean no comment
835 * arguments at all. */
836 if (ac == 2) {
837 scommt[0] = EOS;
838 ecommt[0] = EOS;
839 } else {
840 if (*argv[2])
841 strlcpy(scommt, argv[2], sizeof(scommt));
842 else
843 scommt[0] = SCOMMT, scommt[1] = EOS;
844 if(ac > 3 && *argv[3])
845 strlcpy(ecommt, argv[3], sizeof(ecommt));
846 else
847 ecommt[0] = ECOMMT, ecommt[1] = EOS;
848 }
849 }
850 /*
851 * dochc - change comment characters
852 */
853 static void
854 dochc(argv, argc)
855 const char *argv[];
856 int argc;
857 {
858 if (argc > 2) {
859 if (*argv[2])
860 strlcpy(scommt, argv[2], sizeof(scommt));
861 if (argc > 3) {
862 if (*argv[3])
863 strlcpy(ecommt, argv[3], sizeof(ecommt));
864 }
865 else
866 ecommt[0] = ECOMMT, ecommt[1] = EOS;
867 }
868 else {
869 scommt[0] = SCOMMT, scommt[1] = EOS;
870 ecommt[0] = ECOMMT, ecommt[1] = EOS;
871 }
872 }
873
874 /*
875 * dodivert - divert the output to a temporary file
876 */
877 static void
878 dodiv(n)
879 int n;
880 {
881 int fd;
882
883 oindex = n;
884 if (n >= maxout) {
885 if (mimic_gnu)
886 resizedivs(n + 10);
887 else
888 n = 0; /* bitbucket */
889 }
890
891 if (n < 0)
892 n = 0; /* bitbucket */
893 if (outfile[n] == NULL) {
894 char fname[] = _PATH_DIVNAME;
895
896 if ((fd = mkstemp(fname)) < 0 ||
897 (outfile[n] = fdopen(fd, "w+")) == NULL)
898 err(1, "%s: cannot divert", fname);
899 if (unlink(fname) == -1)
900 err(1, "%s: cannot unlink", fname);
901 }
902 active = outfile[n];
903 }
904
905 /*
906 * doundivert - undivert a specified output, or all
907 * other outputs, in numerical order.
908 */
909 static void
910 doundiv(argv, argc)
911 const char *argv[];
912 int argc;
913 {
914 int ind;
915 int n;
916
917 if (argc > 2) {
918 for (ind = 2; ind < argc; ind++) {
919 n = atoi(argv[ind]);
920 if (n > 0 && n < maxout && outfile[n] != NULL)
921 getdiv(n);
922
923 }
924 }
925 else
926 for (n = 1; n < maxout; n++)
927 if (outfile[n] != NULL)
928 getdiv(n);
929 }
930
931 /*
932 * dosub - select substring
933 */
934 static void
935 dosub(argv, argc)
936 const char *argv[];
937 int argc;
938 {
939 const char *ap, *fc, *k;
940 int nc;
941
942 ap = argv[2]; /* target string */
943 #ifdef EXPR
944 fc = ap + expr(argv[3]); /* first char */
945 #else
946 fc = ap + atoi(argv[3]); /* first char */
947 #endif
948 nc = strlen(fc);
949 if (argc >= 5)
950 #ifdef EXPR
951 nc = min(nc, expr(argv[4]));
952 #else
953 nc = min(nc, atoi(argv[4]));
954 #endif
955 if (fc >= ap && fc < ap + strlen(ap))
956 for (k = fc + nc - 1; k >= fc; k--)
957 putback(*k);
958 }
959
960 /*
961 * map:
962 * map every character of s1 that is specified in from
963 * into s3 and replace in s. (source s1 remains untouched)
964 *
965 * This is a standard implementation of map(s,from,to) function of ICON
966 * language. Within mapvec, we replace every character of "from" with
967 * the corresponding character in "to". If "to" is shorter than "from",
968 * than the corresponding entries are null, which means that those
969 * characters dissapear altogether. Furthermore, imagine
970 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
971 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
972 * ultimately maps to `*'. In order to achieve this effect in an efficient
973 * manner (i.e. without multiple passes over the destination string), we
974 * loop over mapvec, starting with the initial source character. if the
975 * character value (dch) in this location is different than the source
976 * character (sch), sch becomes dch, once again to index into mapvec, until
977 * the character value stabilizes (i.e. sch = dch, in other words
978 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
979 * character, it will stabilize, since mapvec[0] == 0 at all times. At the
980 * end, we restore mapvec* back to normal where mapvec[n] == n for
981 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
982 * about 5 times faster than any algorithm that makes multiple passes over
983 * destination string.
984 */
985 static void
986 map(dest, src, from, to)
987 char *dest;
988 const char *src;
989 const char *from;
990 const char *to;
991 {
992 const char *tmp;
993 unsigned char sch, dch;
994 static char frombis[257];
995 static char tobis[257];
996 static unsigned char mapvec[256] = {
997 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
998 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
999 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
1000 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
1001 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
1002 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
1003 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
1004 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
1005 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
1006 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
1007 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
1008 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
1009 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
1010 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
1011 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
1012 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
1013 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
1014 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
1015 };
1016
1017 if (*src) {
1018 if (mimic_gnu) {
1019 /*
1020 * expand character ranges on the fly
1021 */
1022 from = handledash(frombis, frombis + 256, from);
1023 to = handledash(tobis, tobis + 256, to);
1024 }
1025 tmp = from;
1026 /*
1027 * create a mapping between "from" and
1028 * "to"
1029 */
1030 while (*from)
1031 mapvec[(unsigned char)(*from++)] = (*to) ?
1032 (unsigned char)(*to++) : 0;
1033
1034 while (*src) {
1035 sch = (unsigned char)(*src++);
1036 dch = mapvec[sch];
1037 while (dch != sch) {
1038 sch = dch;
1039 dch = mapvec[sch];
1040 }
1041 if ((*dest = (char)dch))
1042 dest++;
1043 }
1044 /*
1045 * restore all the changed characters
1046 */
1047 while (*tmp) {
1048 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
1049 tmp++;
1050 }
1051 }
1052 *dest = '\0';
1053 }
1054
1055
1056 /*
1057 * handledash:
1058 * use buffer to copy the src string, expanding character ranges
1059 * on the way.
1060 */
1061 static const char *
1062 handledash(buffer, end, src)
1063 char *buffer;
1064 char *end;
1065 const char *src;
1066 {
1067 char *p;
1068
1069 p = buffer;
1070 while(*src) {
1071 if (src[1] == '-' && src[2]) {
1072 unsigned char i;
1073 for (i = (unsigned char)src[0];
1074 i <= (unsigned char)src[2]; i++) {
1075 *p++ = i;
1076 if (p == end) {
1077 *p = '\0';
1078 return buffer;
1079 }
1080 }
1081 src += 3;
1082 } else
1083 *p++ = *src++;
1084 if (p == end)
1085 break;
1086 }
1087 *p = '\0';
1088 return buffer;
1089 }
1090
1091