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