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