Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / m4 / eval.c
CommitLineData
984263bc
MD
1/* $OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $ */
2/* $NetBSD: eval.c,v 1.7 1996/11/10 21:21:29 pk Exp $ */
3
4/*
5 * Copyright (c) 1989, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Ozan Yigit at York University.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 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#ifndef lint
41#if 0
42static char sccsid[] = "@(#)eval.c 8.2 (Berkeley) 4/27/95";
43#else
44#if 0
45static char rcsid[] = "$OpenBSD: eval.c,v 1.44 2002/04/26 16:15:16 espie Exp $";
46#endif
47#endif
48#endif /* not lint */
49
50#include <sys/cdefs.h>
51__FBSDID("$FreeBSD: src/usr.bin/m4/eval.c,v 1.10.2.5 2002/07/15 02:06:15 jmallett Exp $");
52
53/*
54 * eval.c
55 * Facility: m4 macro processor
56 * by: oz
57 */
58
59#include <sys/types.h>
60#include <errno.h>
61#include <unistd.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <stddef.h>
65#include <string.h>
66#include <fcntl.h>
67#include <err.h>
68#include "mdef.h"
69#include "stdd.h"
70#include "extern.h"
71#include "pathnames.h"
72
73#define BUILTIN_MARKER "__builtin_"
74
75static void dodefn(const char *);
76static void dopushdef(const char *, const char *);
77static void dodump(const char *[], int);
78static void dotrace(const char *[], int, int);
79static void doifelse(const char *[], int);
80static int doincl(const char *);
81static int dopaste(const char *);
82static void gnu_dochq(const char *[], int);
83static void dochq(const char *[], int);
84static void gnu_dochc(const char *[], int);
85static void dochc(const char *[], int);
86static void dodiv(int);
87static void doundiv(const char *[], int);
88static void dosub(const char *[], int);
89static void map(char *, const char *, const char *, const char *);
90static const char *handledash(char *, char *, const char *);
91static void expand_builtin(const char *[], int, int);
92static void expand_macro(const char *[], int);
93static void dump_one_def(ndptr);
94
95unsigned long expansion_id;
96
97/*
98 * eval - eval all macros and builtins calls
99 * argc - number of elements in argv.
100 * argv - element vector :
101 * argv[0] = definition of a user
102 * macro or nil if built-in.
103 * argv[1] = name of the macro or
104 * built-in.
105 * argv[2] = parameters to user-defined
106 * . macro or built-in.
107 * .
108 *
109 * A call in the form of macro-or-builtin() will result in:
110 * argv[0] = nullstr
111 * argv[1] = macro-or-builtin
112 * argv[2] = nullstr
113 *
114 * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin
115 */
116void
117eval(const char *argv[], int argc, int td)
118{
119 ssize_t mark = -1;
120
121 expansion_id++;
122 if (td & RECDEF)
123 errx(1, "%s at line %lu: expanding recursive definition for %s",
124 CURRENT_NAME, CURRENT_LINE, argv[1]);
125 if (traced_macros && is_traced(argv[1]))
126 mark = trace(argv, argc, infile+ilevel);
127 if (td == MACRTYPE)
128 expand_macro(argv, argc);
129 else
130 expand_builtin(argv, argc, td);
131 if (mark != -1)
132 finish_trace(mark);
133}
134
135/*
136 * expand_builtin - evaluate built-in macros.
137 */
138void
139expand_builtin(const char *argv[], int argc, 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 fflush(stdout);
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;
399
400 temp = xalloc(strlen(argv[2])+1);
401 if (argc > 4)
402 map(temp, argv[2], argv[3], argv[4]);
403 else
404 map(temp, argv[2], argv[3], null);
405 pbstr(temp);
406 free(temp);
407 } else if (argc > 2)
408 pbstr(argv[2]);
409 break;
410
411 case INDXTYPE:
412 /*
413 * doindex - find the index of the second
414 * argument string in the first argument
415 * string. -1 if not present.
416 */
417 pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1);
418 break;
419
420 case ERRPTYPE:
421 /*
422 * doerrp - print the arguments to stderr
423 * file
424 */
425 if (argc > 2) {
426 for (n = 2; n < argc; n++)
427 fprintf(stderr, "%s ", argv[n]);
428 fprintf(stderr, "\n");
429 }
430 break;
431
432 case DNLNTYPE:
433 /*
434 * dodnl - eat-up-to and including
435 * newline
436 */
437 while ((c = gpbc()) != '\n' && c != EOF)
438 ;
439 break;
440
441 case M4WRTYPE:
442 /*
443 * dom4wrap - set up for
444 * wrap-up/wind-down activity
445 */
446 m4wraps = (argc > 2) ? xstrdup(argv[2]) : null;
447 break;
448
449 case EXITTYPE:
450 /*
451 * doexit - immediate exit from m4.
452 */
453 killdiv();
454 exit((argc > 2) ? atoi(argv[2]) : 0);
455 break;
456
457 case DEFNTYPE:
458 if (argc > 2)
459 for (n = 2; n < argc; n++)
460 dodefn(argv[n]);
461 break;
462
463 case INDIRTYPE: /* Indirect call */
464 if (argc > 2)
465 doindir(argv, argc);
466 break;
467
468 case BUILTINTYPE: /* Builtins only */
469 if (argc > 2)
470 dobuiltin(argv, argc);
471 break;
472
473 case PATSTYPE:
474 if (argc > 2)
475 dopatsubst(argv, argc);
476 break;
477 case REGEXPTYPE:
478 if (argc > 2)
479 doregexp(argv, argc);
480 break;
481 case LINETYPE:
482 doprintlineno(infile+ilevel);
483 break;
484 case FILENAMETYPE:
485 doprintfilename(infile+ilevel);
486 break;
487 case SELFTYPE:
488 pbstr(rquote);
489 pbstr(argv[1]);
490 pbstr(lquote);
491 break;
492 default:
493 errx(1, "%s at line %lu: eval: major botch.",
494 CURRENT_NAME, CURRENT_LINE);
495 break;
496 }
497}
498
499/*
500 * expand_macro - user-defined macro expansion
501 */
502void
503expand_macro(const char *argv[], int argc)
504{
505 const char *t;
506 const char *p;
507 int n;
508 int argno;
509
510 t = argv[0]; /* defn string as a whole */
511 p = t;
512 while (*p)
513 p++;
514 p--; /* last character of defn */
515 while (p > t) {
516 if (*(p - 1) != ARGFLAG)
517 PUTBACK(*p);
518 else {
519 switch (*p) {
520
521 case '#':
522 pbnum(argc - 2);
523 break;
524 case '0':
525 case '1':
526 case '2':
527 case '3':
528 case '4':
529 case '5':
530 case '6':
531 case '7':
532 case '8':
533 case '9':
534 if ((argno = *p - '0') < argc - 1)
535 pbstr(argv[argno + 1]);
536 break;
537 case '*':
538 if (argc > 2) {
539 for (n = argc - 1; n > 2; n--) {
540 pbstr(argv[n]);
541 putback(COMMA);
542 }
543 pbstr(argv[2]);
544 }
545 break;
546 case '@':
547 if (argc > 2) {
548 for (n = argc - 1; n > 2; n--) {
549 pbstr(rquote);
550 pbstr(argv[n]);
551 pbstr(lquote);
552 putback(COMMA);
553 }
554 pbstr(rquote);
555 pbstr(argv[2]);
556 pbstr(lquote);
557 }
558 break;
559 default:
560 PUTBACK(*p);
561 PUTBACK('$');
562 break;
563 }
564 p--;
565 }
566 p--;
567 }
568 if (p == t) /* do last character */
569 PUTBACK(*p);
570}
571
572/*
573 * dodefine - install definition in the table
574 */
575void
576dodefine(const char *name, 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 = xstrdup(null);
595 return;
596 }
597 }
598 if (!*defn)
599 p->defn = xstrdup(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 */
611static void
612dodefn(const char *name)
613{
614 ndptr p;
615 const char *real;
616
617 if ((p = lookup(name)) != nil) {
618 if (p->defn != null) {
619 pbstr(rquote);
620 pbstr(p->defn);
621 pbstr(lquote);
622 } else if ((real = builtin_realname(p->type)) != NULL) {
623 pbstr(real);
624 pbstr(BUILTIN_MARKER);
625 }
626 }
627}
628
629/*
630 * dopushdef - install a definition in the hash table
631 * without removing a previous definition. Since
632 * each new entry is entered in *front* of the
633 * hash bucket, it hides a previous definition from
634 * lookup.
635 */
636static void
637dopushdef(const char *name, const char *defn)
638{
639 ndptr p;
640
641 if (!*name)
642 errx(1, "%s at line %lu: null definition", CURRENT_NAME,
643 CURRENT_LINE);
644 p = addent(name);
645 if (!*defn)
646 p->defn = xstrdup(null);
647 else
648 p->defn = xstrdup(defn);
649 p->type = MACRTYPE;
650 if (STREQ(name, defn))
651 p->type |= RECDEF;
652}
653
654/*
655 * dump_one_def - dump the specified definition.
656 */
657static void
658dump_one_def(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 */
680static void
681dodump(const char *argv[], int argc)
682{
683 int n;
684 ndptr p;
685
686 if (argc > 2) {
687 for (n = 2; n < argc; n++)
688 if ((p = lookup(argv[n])) != nil)
689 dump_one_def(p);
690 } else {
691 for (n = 0; n < HASHSIZE; n++)
692 for (p = hashtab[n]; p != nil; p = p->nxtptr)
693 dump_one_def(p);
694 }
695}
696
697/*
698 * dotrace - mark some macros as traced/untraced depending upon on.
699 */
700static void
701dotrace(const char *argv[], int argc, int on)
702{
703 int n;
704
705 if (argc > 2) {
706 for (n = 2; n < argc; n++)
707 mark_traced(argv[n], on);
708 } else
709 mark_traced(NULL, on);
710}
711
712/*
713 * doifelse - select one of two alternatives - loop.
714 */
715static void
716doifelse(const char *argv[], int argc)
717{
718 cycle {
719 if (STREQ(argv[2], argv[3]))
720 pbstr(argv[4]);
721 else if (argc == 6)
722 pbstr(argv[5]);
723 else if (argc > 6) {
724 argv += 3;
725 argc -= 3;
726 continue;
727 }
728 break;
729 }
730}
731
732/*
733 * doinclude - include a given file.
734 */
735static int
736doincl(const char *ifile)
737{
738 if (ilevel + 1 == MAXINP)
739 errx(1, "%s at line %lu: too many include files.",
740 CURRENT_NAME, CURRENT_LINE);
741 if (fopen_trypath(infile+ilevel+1, ifile) != NULL) {
742 ilevel++;
743 if ((inname[ilevel] = strdup(ifile)) == NULL)
744 err(1, NULL);
745 inlineno[ilevel] = 1;
746 bbase[ilevel] = bufbase = bp;
747 emitline();
748 return (1);
749 } else
750 return (0);
751}
752
753#ifdef EXTENDED
754/*
755 * dopaste - include a given file without any
756 * macro processing.
757 */
758static int
759dopaste(const char *pfile)
760{
761 FILE *pf;
762 int c;
763
764 if ((pf = fopen(pfile, "r")) != NULL) {
765 fprintf(active, "#line 1 \"%s\"\n", pfile);
766 while ((c = getc(pf)) != EOF)
767 putc(c, active);
768 (void) fclose(pf);
769 emitline();
770 return (1);
771 } else
772 return (0);
773}
774#endif
775
776static void
777gnu_dochq(const char *argv[], int ac)
778{
779 /* In gnu-m4 mode, the only way to restore quotes is to have no
780 * arguments at all. */
781 if (ac == 2) {
782 lquote[0] = LQUOTE, lquote[1] = EOS;
783 rquote[0] = RQUOTE, rquote[1] = EOS;
784 } else {
785 strlcpy(lquote, argv[2], sizeof(lquote));
786 if(ac > 3)
787 strlcpy(rquote, argv[3], sizeof(rquote));
788 else
789 rquote[0] = EOS;
790 }
791}
792
793/*
794 * dochq - change quote characters
795 */
796static void
797dochq(const char *argv[], int argc)
798{
799 if (argc > 2) {
800 if (*argv[2])
801 strlcpy(lquote, argv[2], sizeof(lquote));
802 else {
803 lquote[0] = LQUOTE;
804 lquote[1] = EOS;
805 }
806 if (argc > 3) {
807 if (*argv[3])
808 strlcpy(rquote, argv[3], sizeof(rquote));
809 } else
810 strcpy(rquote, lquote);
811 } else {
812 lquote[0] = LQUOTE, lquote[1] = EOS;
813 rquote[0] = RQUOTE, rquote[1] = EOS;
814 }
815}
816
817static void
818gnu_dochc(const char *argv[], int ac)
819{
820 /* In gnu-m4 mode, no arguments mean no comment
821 * arguments at all. */
822 if (ac == 2) {
823 scommt[0] = EOS;
824 ecommt[0] = EOS;
825 } else {
826 if (*argv[2])
827 strlcpy(scommt, argv[2], sizeof(scommt));
828 else
829 scommt[0] = SCOMMT, scommt[1] = EOS;
830 if(ac > 3 && *argv[3])
831 strlcpy(ecommt, argv[3], sizeof(ecommt));
832 else
833 ecommt[0] = ECOMMT, ecommt[1] = EOS;
834 }
835}
836/*
837 * dochc - change comment characters
838 */
839static void
840dochc(const char *argv[], int argc)
841{
842 if (argc > 2) {
843 if (*argv[2])
844 strlcpy(scommt, argv[2], sizeof(scommt));
845 if (argc > 3) {
846 if (*argv[3])
847 strlcpy(ecommt, argv[3], sizeof(ecommt));
848 }
849 else
850 ecommt[0] = ECOMMT, ecommt[1] = EOS;
851 }
852 else {
853 scommt[0] = SCOMMT, scommt[1] = EOS;
854 ecommt[0] = ECOMMT, ecommt[1] = EOS;
855 }
856}
857
858/*
859 * dodivert - divert the output to a temporary file
860 */
861static void
862dodiv(int n)
863{
864 int fd;
865
866 oindex = n;
867 if (n >= maxout) {
868 if (mimic_gnu)
869 resizedivs(n + 10);
870 else
871 n = 0; /* bitbucket */
872 }
873
874 if (n < 0)
875 n = 0; /* bitbucket */
876 if (outfile[n] == NULL) {
877 char fname[] = _PATH_DIVNAME;
878
879 if ((fd = mkstemp(fname)) < 0 ||
880 (outfile[n] = fdopen(fd, "w+")) == NULL)
881 err(1, "%s: cannot divert", fname);
882 if (unlink(fname) == -1)
883 err(1, "%s: cannot unlink", fname);
884 }
885 active = outfile[n];
886}
887
888/*
889 * doundivert - undivert a specified output, or all
890 * other outputs, in numerical order.
891 */
892static void
893doundiv(const char *argv[], int argc)
894{
895 int ind;
896 int n;
897
898 if (argc > 2) {
899 for (ind = 2; ind < argc; ind++) {
900 n = atoi(argv[ind]);
901 if (n > 0 && n < maxout && outfile[n] != NULL)
902 getdiv(n);
903
904 }
905 }
906 else
907 for (n = 1; n < maxout; n++)
908 if (outfile[n] != NULL)
909 getdiv(n);
910}
911
912/*
913 * dosub - select substring
914 */
915static void
916dosub(const char *argv[], int argc)
917{
918 const char *ap, *fc, *k;
919 int nc;
920
921 ap = argv[2]; /* target string */
922#ifdef EXPR
923 fc = ap + expr(argv[3]); /* first char */
924#else
925 fc = ap + atoi(argv[3]); /* first char */
926#endif
927 nc = strlen(fc);
928 if (argc >= 5)
929#ifdef EXPR
930 nc = min(nc, expr(argv[4]));
931#else
932 nc = min(nc, atoi(argv[4]));
933#endif
934 if (fc >= ap && fc < ap + strlen(ap))
935 for (k = fc + nc - 1; k >= fc; k--)
936 putback(*k);
937}
938
939/*
940 * map:
941 * map every character of s1 that is specified in from
942 * into s3 and replace in s. (source s1 remains untouched)
943 *
944 * This is a standard implementation of map(s,from,to) function of ICON
945 * language. Within mapvec, we replace every character of "from" with
946 * the corresponding character in "to". If "to" is shorter than "from",
947 * than the corresponding entries are null, which means that those
948 * characters dissapear altogether. Furthermore, imagine
949 * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case,
950 * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s'
951 * ultimately maps to `*'. In order to achieve this effect in an efficient
952 * manner (i.e. without multiple passes over the destination string), we
953 * loop over mapvec, starting with the initial source character. if the
954 * character value (dch) in this location is different than the source
955 * character (sch), sch becomes dch, once again to index into mapvec, until
956 * the character value stabilizes (i.e. sch = dch, in other words
957 * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary
958 * character, it will stabilize, since mapvec[0] == 0 at all times. At the
959 * end, we restore mapvec* back to normal where mapvec[n] == n for
960 * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is
961 * about 5 times faster than any algorithm that makes multiple passes over
962 * destination string.
963 */
964static void
965map(char *dest, const char *src, const char *from, const char *to)
966{
967 const char *tmp;
968 unsigned char sch, dch;
969 static char frombis[257];
970 static char tobis[257];
971 static unsigned char mapvec[256] = {
972 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
973 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
974 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
975 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
976 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
977 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
978 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
979 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
980 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
981 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
982 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
983 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
984 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193,
985 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
986 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
987 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
988 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
989 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
990 };
991
992 if (*src) {
993 if (mimic_gnu) {
994 /*
995 * expand character ranges on the fly
996 */
997 from = handledash(frombis, frombis + 256, from);
998 to = handledash(tobis, tobis + 256, to);
999 }
1000 tmp = from;
1001 /*
1002 * create a mapping between "from" and
1003 * "to"
1004 */
1005 while (*from)
1006 mapvec[(unsigned char)(*from++)] = (*to) ?
1007 (unsigned char)(*to++) : 0;
1008
1009 while (*src) {
1010 sch = (unsigned char)(*src++);
1011 dch = mapvec[sch];
1012 while (dch != sch) {
1013 sch = dch;
1014 dch = mapvec[sch];
1015 }
1016 if ((*dest = (char)dch))
1017 dest++;
1018 }
1019 /*
1020 * restore all the changed characters
1021 */
1022 while (*tmp) {
1023 mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp);
1024 tmp++;
1025 }
1026 }
1027 *dest = '\0';
1028}
1029
1030
1031/*
1032 * handledash:
1033 * use buffer to copy the src string, expanding character ranges
1034 * on the way.
1035 */
1036static const char *
1037handledash(char *buffer, char *end, const char *src)
1038{
1039 char *p;
1040
1041 p = buffer;
1042 while(*src) {
1043 if (src[1] == '-' && src[2]) {
1044 unsigned char i;
1045 for (i = (unsigned char)src[0];
1046 i <= (unsigned char)src[2]; i++) {
1047 *p++ = i;
1048 if (p == end) {
1049 *p = '\0';
1050 return buffer;
1051 }
1052 }
1053 src += 3;
1054 } else
1055 *p++ = *src++;
1056 if (p == end)
1057 break;
1058 }
1059 *p = '\0';
1060 return buffer;
1061}