eval.c revision 1.14 1 /* $NetBSD: eval.c,v 1.14 2001/11/14 06:16:08 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 #ifndef 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.14 2001/11/14 06:16:08 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 <err.h>
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <stddef.h>
62 #include <string.h>
63 #include "mdef.h"
64 #include "stdd.h"
65 #include "extern.h"
66 #include "pathnames.h"
67
68 #define BUILTIN_MARKER "__builtin_"
69
70 static void dodefn __P((const char *));
71 static void dopushdef __P((const char *, const char *));
72 static void dodump __P((const char *[], int));
73 static void dotrace __P((const char *[], int, int));
74 static void doifelse __P((const char *[], int));
75 static int doincl __P((const char *));
76 static int dopaste __P((const char *));
77 static void gnu_dochq __P((const char *[], int));
78 static void dochq __P((const char *[], int));
79 static void gnu_dochc __P((const char *[], int));
80 static void dochc __P((const char *[], int));
81 static void dodiv __P((int));
82 static void doundiv __P((const char *[], int));
83 static void dosub __P((const char *[], int));
84 static void map __P((char *, const char *, const char *, const char *));
85 static const char *handledash __P((char *, char *, const char *));
86 static void expand_builtin __P((const char *[], int, int));
87 static void expand_macro __P((const char *[], int));
88 static void dump_one_def __P((ndptr));
89
90 unsigned long expansion_id;
91
92 /*
93 * eval - eval all macros and builtins calls
94 * argc - number of elements in argv.
95 * argv - element vector :
96 * argv[0] = definition of a user
97 * macro or nil if built-in.
98 * argv[1] = name of the macro or
99 * built-in.
100 * argv[2] = parameters to user-defined
101 * . macro or built-in.
102 * .
103 *
104 * A call in the form of macro-or-builtin() will result in:
105 * argv[0] = nullstr
106 * argv[1] = macro-or-builtin
107 * argv[2] = nullstr
108 *
109 * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
110 */
111 void
112 eval(argv, argc, td)
113 const char *argv[];
114 int argc;
115 int td;
116 {
117 ssize_t mark = -1;
118
119 expansion_id++;
120 if (td & RECDEF)
121 errx(1, "%s at line %lu: expanding recursive definition for %s",
122 CURRENT_NAME, CURRENT_LINE, argv[1]);
123 if (traced_macros && is_traced(argv[1]))
124 mark = trace(argv, argc, infile+ilevel);
125 if (td == MACRTYPE)
126 expand_macro(argv, argc);
127 else
128 expand_builtin(argv, argc, td);
129 if (mark != -1)
130 finish_trace(mark);
131 }
132
133 /*
134 * expand_builtin - evaluate built-in macros.
135 */
136 void
137 expand_builtin(argv, argc, td)
138 const char *argv[];
139 int argc;
140 int td;
141 {
142 int c, n;
143 int ac;
144 static int sysval = 0;
145
146 #ifdef DEBUG
147 printf("argc = %d\n", argc);
148 for (n = 0; n < argc; n++)
149 printf("argv[%d] = %s\n", n, argv[n]);
150 #endif
151
152 /*
153 * if argc == 3 and argv[2] is null, then we
154 * have macro-or-builtin() type call. We adjust
155 * argc to avoid further checking..
156 */
157 ac = argc;
158
159 if (argc == 3 && !*(argv[2]))
160 argc--;
161
162 switch (td & TYPEMASK) {
163
164 case DEFITYPE:
165 if (argc > 2)
166 dodefine(argv[2], (argc > 3) ? argv[3] : null);
167 break;
168
169 case PUSDTYPE:
170 if (argc > 2)
171 dopushdef(argv[2], (argc > 3) ? argv[3] : null);
172 break;
173
174 case DUMPTYPE:
175 dodump(argv, argc);
176 break;
177
178 case TRACEONTYPE:
179 dotrace(argv, argc, 1);
180 break;
181
182 case TRACEOFFTYPE:
183 dotrace(argv, argc, 0);
184 break;
185
186 case EXPRTYPE:
187 /*
188 * doexpr - evaluate arithmetic
189 * expression
190 */
191 if (argc > 2)
192 pbnum(expr(argv[2]));
193 break;
194
195 case IFELTYPE:
196 if (argc > 4)
197 doifelse(argv, argc);
198 break;
199
200 case IFDFTYPE:
201 /*
202 * doifdef - select one of two
203 * alternatives based on the existence of
204 * another definition
205 */
206 if (argc > 3) {
207 if (lookup(argv[2]) != nil)
208 pbstr(argv[3]);
209 else if (argc > 4)
210 pbstr(argv[4]);
211 }
212 break;
213
214 case LENGTYPE:
215 /*
216 * dolen - find the length of the
217 * argument
218 */
219 pbnum((argc > 2) ? strlen(argv[2]) : 0);
220 break;
221
222 case INCRTYPE:
223 /*
224 * doincr - increment the value of the
225 * argument
226 */
227 if (argc > 2)
228 pbnum(atoi(argv[2]) + 1);
229 break;
230
231 case DECRTYPE:
232 /*
233 * dodecr - decrement the value of the
234 * argument
235 */
236 if (argc > 2)
237 pbnum(atoi(argv[2]) - 1);
238 break;
239
240 case SYSCTYPE:
241 /*
242 * dosys - execute system command
243 */
244 if (argc > 2)
245 sysval = system(argv[2]);
246 break;
247
248 case SYSVTYPE:
249 /*
250 * dosysval - return value of the last
251 * system call.
252 *
253 */
254 pbnum(sysval);
255 break;
256
257 case ESYSCMDTYPE:
258 if (argc > 2)
259 doesyscmd(argv[2]);
260 break;
261 case INCLTYPE:
262 if (argc > 2)
263 if (!doincl(argv[2]))
264 err(1, "%s at line %lu: include(%s)",
265 CURRENT_NAME, CURRENT_LINE, argv[2]);
266 break;
267
268 case SINCTYPE:
269 if (argc > 2)
270 (void) doincl(argv[2]);
271 break;
272 #ifdef EXTENDED
273 case PASTTYPE:
274 if (argc > 2)
275 if (!dopaste(argv[2]))
276 err(1, "%s at line %lu: paste(%s)",
277 CURRENT_NAME, CURRENT_LINE, argv[2]);
278 break;
279
280 case SPASTYPE:
281 if (argc > 2)
282 (void) dopaste(argv[2]);
283 break;
284 #endif
285 case CHNQTYPE:
286 if (mimic_gnu)
287 gnu_dochq(argv, ac);
288 else
289 dochq(argv, argc);
290 break;
291
292 case CHNCTYPE:
293 if (mimic_gnu)
294 gnu_dochc(argv, ac);
295 else
296 dochc(argv, argc);
297 break;
298
299 case SUBSTYPE:
300 /*
301 * dosub - select substring
302 *
303 */
304 if (argc > 3)
305 dosub(argv, argc);
306 break;
307
308 case SHIFTYPE:
309 /*
310 * doshift - push back all arguments
311 * except the first one (i.e. skip
312 * argv[2])
313 */
314 if (argc > 3) {
315 for (n = argc - 1; n > 3; n--) {
316 pbstr(rquote);
317 pbstr(argv[n]);
318 pbstr(lquote);
319 putback(COMMA);
320 }
321 pbstr(rquote);
322 pbstr(argv[3]);
323 pbstr(lquote);
324 }
325 break;
326
327 case DIVRTYPE:
328 if (argc > 2 && (n = atoi(argv[2])) != 0)
329 dodiv(n);
330 else {
331 active = stdout;
332 oindex = 0;
333 }
334 break;
335
336 case UNDVTYPE:
337 doundiv(argv, argc);
338 break;
339
340 case DIVNTYPE:
341 /*
342 * dodivnum - return the number of
343 * current output diversion
344 */
345 pbnum(oindex);
346 break;
347
348 case UNDFTYPE:
349 /*
350 * doundefine - undefine a previously
351 * defined macro(s) or m4 keyword(s).
352 */
353 if (argc > 2)
354 for (n = 2; n < argc; n++)
355 remhash(argv[n], ALL);
356 break;
357
358 case POPDTYPE:
359 /*
360 * dopopdef - remove the topmost
361 * definitions of macro(s) or m4
362 * keyword(s).
363 */
364 if (argc > 2)
365 for (n = 2; n < argc; n++)
366 remhash(argv[n], TOP);
367 break;
368
369 case MKTMTYPE:
370 /*
371 * dotemp - create a temporary file
372 */
373 if (argc > 2) {
374 int fd;
375 char *temp;
376
377 temp = xstrdup(argv[2]);
378
379 fd = mkstemp(temp);
380 if (fd == -1)
381 err(1,
382 "%s at line %lu: couldn't make temp file %s",
383 CURRENT_NAME, CURRENT_LINE, argv[2]);
384 close(fd);
385 pbstr(temp);
386 free(temp);
387 }
388 break;
389
390 case TRNLTYPE:
391 /*
392 * dotranslit - replace all characters in
393 * the source string that appears in the
394 * "from" string with the corresponding
395 * characters in the "to" string.
396 */
397 if (argc > 3) {
398 char temp[STRSPMAX+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 } else if (argc > 2)
405 pbstr(argv[2]);
406 break;
407
408 case INDXTYPE:
409 /*
410 * doindex - find the index of the second
411 * argument string in the first argument
412 * string. -1 if not present.
413 */
414 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
415 break;
416
417 case ERRPTYPE:
418 /*
419 * doerrp - print the arguments to stderr
420 * file
421 */
422 if (argc > 2) {
423 for (n = 2; n < argc; n++)
424 fprintf(stderr, "%s ", argv[n]);
425 fprintf(stderr, "\n");
426 }
427 break;
428
429 case DNLNTYPE:
430 /*
431 * dodnl - eat-up-to and including
432 * newline
433 */
434 while ((c = gpbc()) != '\n' && c != EOF)
435 ;
436 break;
437
438 case M4WRTYPE:
439 /*
440 * dom4wrap - set up for
441 * wrap-up/wind-down activity
442 */
443 m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
444 break;
445
446 case EXITTYPE:
447 /*
448 * doexit - immediate exit from m4.
449 */
450 killdiv();
451 exit((argc > 2) ? atoi(argv[2]) : 0);
452 break;
453
454 case DEFNTYPE:
455 if (argc > 2)
456 for (n = 2; n < argc; n++)
457 dodefn(argv[n]);
458 break;
459
460 case INDIRTYPE: /* Indirect call */
461 if (argc > 2)
462 doindir(argv, argc);
463 break;
464
465 case BUILTINTYPE: /* Builtins only */
466 if (argc > 2)
467 dobuiltin(argv, argc);
468 break;
469
470 case PATSTYPE:
471 if (argc > 2)
472 dopatsubst(argv, argc);
473 break;
474 case REGEXPTYPE:
475 if (argc > 2)
476 doregexp(argv, argc);
477 break;
478 case LINETYPE:
479 doprintlineno(infile+ilevel);
480 break;
481 case FILENAMETYPE:
482 doprintfilename(infile+ilevel);
483 break;
484 case SELFTYPE:
485 pbstr(rquote);
486 pbstr(argv[1]);
487 pbstr(lquote);
488 break;
489 default:
490 errx(1, "%s at line %lu: eval: major botch.",
491 CURRENT_NAME, CURRENT_LINE);
492 break;
493 }
494 }
495
496 /*
497 * expand_macro - user-defined macro expansion
498 */
499 void
500 expand_macro(argv, argc)
501 const char *argv[];
502 int argc;
503 {
504 const char *t;
505 const char *p;
506 int n;
507 int argno;
508
509 t = argv[0]; /* defn string as a whole */
510 p = t;
511 while (*p)
512 p++;
513 p--; /* last character of defn */
514 while (p > t) {
515 if (*(p - 1) != ARGFLAG)
516 PUTBACK(*p);
517 else {
518 switch (*p) {
519
520 case '#':
521 pbnum(argc - 2);
522 break;
523 case '0':
524 case '1':
525 case '2':
526 case '3':
527 case '4':
528 case '5':
529 case '6':
530 case '7':
531 case '8':
532 case '9':
533 if ((argno = *p - '0') < argc - 1)
534 pbstr(argv[argno + 1]);
535 break;
536 case '*':
537 if (argc > 2) {
538 for (n = argc - 1; n > 2; n--) {
539 pbstr(argv[n]);
540 putback(COMMA);
541 }
542 pbstr(argv[2]);
543 }
544 break;
545 case '@':
546 if (argc > 2) {
547 for (n = argc - 1; n > 2; n--) {
548 pbstr(rquote);
549 pbstr(argv[n]);
550 pbstr(lquote);
551 putback(COMMA);
552 }
553 pbstr(rquote);
554 pbstr(argv[2]);
555 pbstr(lquote);
556 }
557 break;
558 default:
559 PUTBACK(*p);
560 PUTBACK('$');
561 break;
562 }
563 p--;
564 }
565 p--;
566 }
567 if (p == t) /* do last character */
568 PUTBACK(*p);
569 }
570
571 /*
572 * dodefine - install definition in the table
573 */
574 void
575 dodefine(name, defn)
576 const char *name;
577 const char *defn;
578 {
579 ndptr p;
580 int n;
581
582 if (!*name)
583 errx(1, "%s at line %lu: null definition.", CURRENT_NAME,
584 CURRENT_LINE);
585 if ((p = lookup(name)) == nil)
586 p = addent(name);
587 else if (p->defn != null)
588 free((char *) p->defn);
589 if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0) {
590 n = builtin_type(defn+sizeof(BUILTIN_MARKER)-1);
591 if (n != -1) {
592 p->type = n & TYPEMASK;
593 if ((n & NOARGS) == 0)
594 p->type |= NEEDARGS;
595 p->defn = null;
596 return;
597 }
598 }
599 if (!*defn)
600 p->defn = null;
601 else
602 p->defn = xstrdup(defn);
603 p->type = MACRTYPE;
604 if (STREQ(name, defn))
605 p->type |= RECDEF;
606 }
607
608 /*
609 * dodefn - push back a quoted definition of
610 * the given name.
611 */
612 static void
613 dodefn(name)
614 const char *name;
615 {
616 ndptr p;
617 const char *real;
618
619 if ((p = lookup(name)) != nil) {
620 if (p->defn != null) {
621 pbstr(rquote);
622 pbstr(p->defn);
623 pbstr(lquote);
624 } else if ((real = builtin_realname(p->type)) != NULL) {
625 pbstr(real);
626 pbstr(BUILTIN_MARKER);
627 }
628 }
629 }
630
631 /*
632 * dopushdef - install a definition in the hash table
633 * without removing a previous definition. Since
634 * each new entry is entered in *front* of the
635 * hash bucket, it hides a previous definition from
636 * lookup.
637 */
638 static void
639 dopushdef(name, defn)
640 const char *name;
641 const char *defn;
642 {
643 ndptr p;
644
645 if (!*name)
646 errx(1, "%s at line %lu: null definition", CURRENT_NAME,
647 CURRENT_LINE);
648 p = addent(name);
649 if (!*defn)
650 p->defn = null;
651 else
652 p->defn = xstrdup(defn);
653 p->type = MACRTYPE;
654 if (STREQ(name, defn))
655 p->type |= RECDEF;
656 }
657
658 /*
659 * dump_one_def - dump the specified definition.
660 */
661 static void
662 dump_one_def(p)
663 ndptr p;
664 {
665 const char *real;
666
667 if (mimic_gnu) {
668 if ((p->type & TYPEMASK) == MACRTYPE)
669 fprintf(traceout, "%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(traceout, "%s:\t<%s>\n", p->name, real);
675 }
676 } else
677 fprintf(traceout, "`%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