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