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