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