aboutsummaryrefslogtreecommitdiffstats
path: root/awk1.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2010-07-02 15:46:31 +0300
committerArnold D. Robbins <arnold@skeeve.com>2010-07-02 15:46:31 +0300
commit3711eedc1b995eb1926c9ffb902d5d796cacf8d0 (patch)
tree5642fdee11499774e0b7401f195931cd3a143d18 /awk1.c
parentec6415f1ba061b2fb78808b7dba3246745a15398 (diff)
downloadegawk-3711eedc1b995eb1926c9ffb902d5d796cacf8d0.tar.gz
egawk-3711eedc1b995eb1926c9ffb902d5d796cacf8d0.tar.bz2
egawk-3711eedc1b995eb1926c9ffb902d5d796cacf8d0.zip
Now at 2.02.
Diffstat (limited to 'awk1.c')
-rw-r--r--awk1.c1275
1 files changed, 696 insertions, 579 deletions
diff --git a/awk1.c b/awk1.c
index 79963bac..b6b4c7b4 100644
--- a/awk1.c
+++ b/awk1.c
@@ -1,370 +1,460 @@
+
/*
- * awk1 -- Expression tree constructors and main program for gawk.
+ * awk1 -- Expression tree constructors and main program for gawk.
+ *
+ * Copyright (C) 1986 Free Software Foundation Written by Paul Rubin, August
+ * 1986
+ *
+ * $Log: awk1.c,v $
+ * Revision 1.30 88/12/15 12:56:18 david
+ * changes from Jay to compile under gcc and fixing a bug in treatment of
+ * input files
+ *
+ * Revision 1.29 88/12/08 15:57:41 david
+ * *** empty log message ***
+ *
+ * Revision 1.28 88/12/07 20:00:15 david
+ * changes for incorporating source filename into error messages
+ *
+ * Revision 1.27 88/12/01 15:05:26 david
+ * changes to allow source line number printing in error messages
+ *
+ * Revision 1.26 88/11/28 20:12:30 david
+ * unbuffer stdout if compiled with DEBUG
+ *
+ * Revision 1.25 88/11/23 21:39:57 david
+ * Arnold: set strict if invoked as "awk"
+ *
+ * Revision 1.24 88/11/22 13:47:40 david
+ * Arnold: changes for case-insensitive matching
+ *
+ * Revision 1.23 88/11/15 10:18:57 david
+ * Arnold: cleanup; allow multiple -f options; if invoked as awk disable
+ * -v option for compatability
+ *
+ * Revision 1.22 88/11/14 21:54:27 david
+ * Arnold: cleanup
+ *
+ * Revision 1.21 88/11/03 15:22:22 david
+ * revised flags
+ *
+ * Revision 1.20 88/11/01 11:47:24 david
+ * mostly cleanup and code movement
+ *
+ * Revision 1.19 88/10/19 21:56:00 david
+ * replace malloc with emalloc
+ *
+ * Revision 1.18 88/10/17 20:57:19 david
+ * Arnold: purge FAST
+ *
+ * Revision 1.17 88/10/14 22:11:24 david
+ * pathc from Hack to get gcc to work
+ *
+ * Revision 1.16 88/10/13 21:55:08 david
+ * purge FAST; clean up error messages
+ *
+ * Revision 1.15 88/10/06 21:55:20 david
+ * be more careful about I/O errors on exit
+ * Arnold's fixes for command line processing
+ *
+ * Revision 1.14 88/10/06 15:52:24 david
+ * changes from Arnold: use getopt; ifdef -v option; change semantics of = args.
+ *
+ * Revision 1.13 88/09/26 10:16:35 david
+ * cleanup from Arnold
+ *
+ * Revision 1.12 88/09/19 20:38:29 david
+ * added -v option
+ * set FILENAME to "-" if stdin
+ *
+ * Revision 1.11 88/08/09 14:49:23 david
+ * reorganized handling of command-line arguments and files
+ *
+ * Revision 1.10 88/06/13 18:08:25 david
+ * delete \a and -R options
+ * separate exit value from flag indicating that exit has been called
+ * [from Arnold]
+ *
+ * Revision 1.9 88/06/05 22:19:41 david
+ * func_level goes away; param_counter is used to count order of local vars.
+ *
+ * Revision 1.8 88/05/31 09:21:56 david
+ * Arnold's portability changes (vprintf)
+ * fixed handling of function parameter use inside function
+ *
+ * Revision 1.7 88/05/30 09:51:17 david
+ * exit after yyparse() if any errors were encountered
+ * mk_re_parse() parses C escapes in regexps.
+ * do panic() properly with varargs
+ * clean up and simplify pop_var()
+ *
+ * Revision 1.6 88/05/26 22:46:19 david
+ * minor changes: break out separate case for making regular expressions
+ * from parser vs. from strings
+ *
+ * Revision 1.5 88/05/13 22:02:31 david
+ * moved BEGIN and END block merging into parse-phase (sorry Arnold)
+ * if there is only a BEGIN block and nothing else, don't read any files
+ * cleaned up func_install a bit
+ *
+ * Revision 1.4 88/05/04 12:18:28 david
+ * make_for_loop() now returns a NODE *
+ * pop_var() now returns the value of the node being popped, to be used
+ * in func_call() if the variable is an array (call by reference)
+ *
+ * Revision 1.3 88/04/15 13:12:19 david
+ * small fix to arg reading code
+ *
+ * Revision 1.2 88/04/14 14:40:25 david
+ * Arnold's changes to read program from a file
+ *
+ * Revision 1.1 88/04/08 15:14:52 david
+ * Initial revision
+ * Revision 1.6 88/04/08 14:48:30 david changes from
+ * Arnold Robbins
+ *
+ * Revision 1.5 88/03/28 14:13:33 david *** empty log message ***
+ *
+ * Revision 1.4 88/03/23 22:17:33 david mostly delinting -- a couple of bug
+ * fixes
*
- * Copyright (C) 1986 Free Software Foundation
- * Written by Paul Rubin, August 1986
+ * Revision 1.3 88/03/18 21:00:09 david Baseline -- hoefully all the
+ * functionality of the new awk added. Just debugging and tuning to do.
+ *
+ * Revision 1.2 87/11/19 14:40:17 david added support for user-defined
+ * functions
+ *
+ * Revision 1.1 87/10/27 15:23:26 david Initial revision
*
*/
/*
-GAWK is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY. No author or distributor accepts responsibility to anyone
-for the consequences of using it or for whether it serves any
-particular purpose or works at all, unless he says so in writing.
-Refer to the GAWK General Public License for full details.
-
-Everyone is granted permission to copy, modify and redistribute GAWK,
-but only under the conditions described in the GAWK General Public
-License. A copy of this license is supposed to have been given to you
-along with GAWK so you can know your rights and responsibilities. It
-should be in a file named COPYING. Among other things, the copyright
-notice and this notice must be preserved on all copies.
-
-In other words, go ahead and share GAWK, but don't try to stop
-anyone else from sharing it farther. Help stamp out software hoarding!
-*/
-
-#include <stdio.h>
-#include "regex.h"
-#include "awk.h"
+ * GAWK is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY. No author or distributor accepts responsibility to anyone for
+ * the consequences of using it or for whether it serves any particular
+ * purpose or works at all, unless he says so in writing. Refer to the GAWK
+ * General Public License for full details.
+ *
+ * Everyone is granted permission to copy, modify and redistribute GAWK, but
+ * only under the conditions described in the GAWK General Public License. A
+ * copy of this license is supposed to have been given to you along with GAWK
+ * so you can know your rights and responsibilities. It should be in a file
+ * named COPYING. Among other things, the copyright notice and this notice
+ * must be preserved on all copies.
+ *
+ * In other words, go ahead and share GAWK, but don't try to stop anyone else
+ * from sharing it farther. Help stamp out software hoarding!
+ */
-/* Temporary nodes are stored here. ob_dummy is a dummy object used to
- keep the obstack library from free()ing up the entire stack. */
-struct obstack temp_strings;
-char *ob_dummy;
+#include "awk.h"
-/* The parse tree and field nodes are stored here. Parse_end is a dummy
- item used to free up unneeded fields without freeing the program being run
+/*
+ * The parse tree and field nodes are stored here. Parse_end is a dummy item
+ * used to free up unneeded fields without freeing the program being run
*/
-struct obstack other_stack;
-char *parse_end;
+int errcount = 0; /* error counter, used by yyerror() */
+int param_counter;
/* The global null string */
NODE *Nnull_string;
/* The special variable that contains the name of the current input file */
extern NODE *FILENAME_node;
+extern NODE *ARGC_node;
+extern NODE *ARGV_node;
/* The name the program was invoked under, for error messages */
char *myname;
-/* A block of gAWK code to be run before running the program */
-NODE *begin_block = 0;
+/* A block of AWK code to be run before running the program */
+NODE *begin_block = 0;
+
+/* A block of AWK code to be run after the last input file */
+NODE *end_block = 0;
-/* A block of gAWK code to be run after the last input file */
-NODE *end_block = 0;
+FILE *input_file; /* Where to read from */
-FILE *input_file; /* Where to read from */
+int exiting = 0; /* Was an "exit" statement executed? */
+int exit_val = 0; /* optional exit value */
-#ifndef FAST
+#ifdef DEBUG
/* non-zero means in debugging is enabled. Probably not very useful */
-int debugging;
+int debugging = 0;
+
#endif
-char *index();
+int tempsource = 0; /* source is in a temp file */
+char **sourcefile = NULL; /* source file name(s) */
+int numfiles = -1; /* how many source files */
+
+int ignorecase = 0; /* global flag for ignoring case */
+int strict = 0; /* turn off gnu extensions */
main(argc, argv)
- int argc;
- char **argv;
+int argc;
+char **argv;
{
- register int i;
- register NODE *tmp;
- char **do_vars;
-#ifndef FAST
+#ifdef DEBUG
/* Print out the parse tree. For debugging */
- register int dotree = 0;
- extern int yydebug;
+ register int dotree = 0;
+ extern int yydebug;
+
+#endif
+ extern char *lexptr;
+ extern char *lexptr_begin;
+ extern char *version_string;
+ extern FILE *nextfile();
+ FILE *fp, *fopen();
+ static char template[] = "/tmp/gawk.XXXXX";
+ char *mktemp ();
+ int c;
+ extern int opterr, optind, getopt();
+ extern char *optarg;
+ char *cp, *rindex();
+ /*
+ * for strict to work, legal options must be first
+ */
+#define EXTENSIONS 4 /* where to clear */
+#ifdef DEBUG
+ char *awk_opts = "F:f:ivdD";
+#else
+ char *awk_opts = "F:f:iv";
#endif
- extern char *lexptr;
- extern char *lexptr_begin;
- FILE *fp,*fopen();
- --argc;
- myname= *argv++;
- if(!argc)
- usage();
+#ifdef DEBUG
+ /*malloc_debug(2);*/
+#endif
+ myname = argv[0];
+ if (argc < 2)
+ usage();
/* Tell the regex routines how they should work. . . */
- re_set_syntax(RE_NO_BK_PARENS|RE_NO_BK_VBAR);
-
- /* Set up the stack for temporary strings */
- obstack_init (&temp_strings);
- ob_dummy=obstack_alloc(&temp_strings,0);
+ (void) re_set_syntax(RE_SYNTAX_AWK);
- /* Set up the other stack for other things */
- obstack_init(&other_stack);
/* initialize the null string */
- Nnull_string = make_string("",0);
- /* This was to keep Nnull_string from ever being free()d It didn't work */
- /* Nnull_string->stref=32000; */
- /* Set up the special variables */
- /* Note that this must be done BEFORE arg parsing else -R and -F
- break horribly */
- init_vars();
-
-
- for(;*argv && **argv=='-';argc--,argv++) {
- switch(argv[0][1]) {
-#ifndef FAST
- case 'd':
- debugging++;
- dotree++;
- break;
-
- case 'D':
- debugging++;
- yydebug=2;
- break;
+ Nnull_string = make_string("", 0);
+ Nnull_string->numbr = 0.0;
+ Nnull_string->type = Node_val;
+ Nnull_string->flags = (PERM|STR|NUM);
+
+ /* Set up the special variables */
+
+ /*
+ * Note that this must be done BEFORE arg parsing else -F
+ * breaks horribly
+ */
+ init_vars();
+
+ /* worst case */
+ emalloc(sourcefile, char **, argc * sizeof(char *), "main");
+
+
+#ifdef STRICT /* strict Unix awk compatibility */
+ strict = 1;
+#else
+ /* if invoked as 'awk', also behave strictly */
+ if ((cp = rindex(myname, '/')) != NULL)
+ cp++;
+ else
+ cp = myname;
+ if (strcmp (cp, "awk") == 0)
+ strict = 1;
#endif
- /* This feature isn't in un*x awk, but might be useful */
- case 'R':
- set_rs(&argv[0][2]);
- break;
-
- case 'F':
- set_fs(&argv[0][2]);
- break;
-
-
- /* It would be better to read the input file in as we parse
- it. Its done this way for hysterical reasons. Feel
- free to fix it. */
- case 'f':
- if(lexptr)
- panic("Can only use one -f option");
- if((fp=fopen(argv[1],"r"))==NULL)
- er_panic(argv[1]);
- else {
- char *curptr;
- int siz,nread;
-
- curptr=lexptr=malloc(2000);
- if(curptr==NULL)
- panic("Memory exhausted"); /* jfw: instead of abort() */
- siz=2000;
- i=siz-1;
- while((nread=fread(curptr,sizeof(char),i,fp)) > 0) {
- curptr+=nread;
- i-=nread;
- if(i==0) {
- lexptr=realloc(lexptr,siz*2);
- if(lexptr==NULL)
- panic("Memory exhausted"); /* jfw: instead of abort() */
- curptr=lexptr+siz-1;
- i=siz;
- siz*=2;
- }
- }
- *curptr='\0';
- fclose(fp);
- }
- argc--;
- argv++;
- break;
-
- case '\0': /* A file */
- break;
-
- default:
- panic("Unknown option %s",argv[0]);
- }
- }
- if (debugging) setbuf(stdout, 0); /* jfw: make debugging easier */
- /* No -f option, use next arg */
- if(!lexptr) {
- if(!argc) usage();
- lexptr= *argv++;
- --argc;
- }
-
- /* Read in the program */
- lexptr_begin=lexptr;
- (void)yyparse ();
-
- /* Anything allocated on the other_stack after here will be freed
- when the next input line is read.
- */
- parse_end=obstack_alloc(&other_stack,0);
-
-#ifndef FAST
- if(dotree)
- print_parse_tree(expression_value);
+
+ if (strict)
+ awk_opts[EXTENSIONS] = '\0';
+
+ while ((c = getopt (argc, argv, awk_opts)) != EOF) {
+ switch (c) {
+#ifdef DEBUG
+ case 'd':
+ debugging++;
+ dotree++;
+ break;
+
+ case 'D':
+ debugging++;
+ yydebug = 2;
+ break;
#endif
- /* Set up the field variables */
- init_fields();
-
- /* Look for BEGIN and END blocks. Only one of each allowed */
- for(tmp=expression_value;tmp;tmp=tmp->rnode) {
- if(!tmp->lnode || !tmp->lnode->lnode)
- continue;
- if(tmp->lnode->lnode->type==Node_K_BEGIN)
- begin_block=tmp->lnode->rnode;
- else if(tmp->lnode->lnode->type==Node_K_END)
- end_block=tmp->lnode->rnode;
- }
- if(begin_block && interpret(begin_block) == 0) exit(0); /* jfw */
- do_vars=argv;
- while(argc>0 && index(*argv,'=')) {
- argv++;
- --argc;
- }
- if(do_vars==argv) do_vars=0;
- if(argc==0) {
- static char *dumb[2]= { "-", 0};
-
- argc=1;
- argv= &dumb[0];
- }
- while(argc--) {
- if(!strcmp(*argv,"-")) {
- input_file=stdin;
- FILENAME_node->var_value=Nnull_string;
- ADD_ONE_REFERENCE(Nnull_string);
- } else {
- extern NODE *deref;
-
- input_file=fopen(*argv,"r");
- /* This should print the error message from errno */
- if(!input_file)
- er_panic(*argv);
- /* This is a kludge. */
- deref=FILENAME_node->var_value;
- do_deref();
- FILENAME_node->var_value=make_string(*argv,strlen(*argv));
- }
- /* This is where it spends all its time. The infamous MAIN LOOP */
- if(inrec()==0) {
- if(do_vars) {
- while(do_vars!=argv && *do_vars) {
- char *cp;
-
- cp=index(*do_vars,'=');
- *cp++='\0';
- variable(*do_vars)->var_value=make_string(cp,strlen(cp));
- do_vars++;
+
+ case 'F':
+ set_fs(optarg);
+ break;
+
+ case 'f':
+ /*
+ * a la MKS awk, allow multiple -f options.
+ * this makes function libraries real easy.
+ * most of the magic is in the scanner.
+ */
+ sourcefile[++numfiles] = optarg;
+ break;
+
+ case 'i':
+ ignorecase = 1;
+ break;
+
+ case 'v':
+ fprintf(stderr, "%s", version_string);
+ break;
+
+ case '?':
+ default:
+ /* getopt will print a message for us */
+ /* S5R4 awk ignores bad options and keeps going */
+ break;
}
- do_vars=0;
}
- do
- obstack_free(&temp_strings, ob_dummy);
- while (interpret(expression_value) && inrec() == 0);
- }
- if(input_file!=stdin) fclose(input_file);
- argv++;
- }
- if(end_block) (void)interpret(end_block);
- exit(0);
-}
+#ifdef DEBUG
+ setbuf(stdout, (char *) NULL); /* make debugging easier */
+#endif
+ /* No -f option, use next arg */
+ /* write to temp file and save sourcefile name */
+ if (numfiles == -1) {
+ int i;
+
+ if (optind > argc - 1) /* no args left */
+ usage();
+ numfiles++;
+ sourcefile[0] = mktemp (template);
+ i = strlen (argv[optind]);
+ if ((fp = fopen (sourcefile[0], "w")) == NULL)
+ fatal("could not save source prog in temp file (%s)",
+ sys_errlist[errno]);
+ if (fwrite (argv[optind], 1, i, fp) == 0)
+ fatal(
+ "could not write source program to temp file (%s)",
+ sys_errlist[errno]);
+ if (argv[optind][i-1] != '\n')
+ putc ('\n', fp);
+ (void) fclose (fp);
+ tempsource++;
+ optind++;
+ }
+ init_args(optind, argc, myname, argv);
-/* These exit values are arbitrary */
-/*VARARGS1*/
-panic(str,arg)
-char *str;
-{
- fprintf(stderr,"%s: ",myname);
- fprintf(stderr,str,arg);
- fprintf(stderr,"\n");
- exit(12);
+ /* Read in the program */
+ lexptr_begin = lexptr;
+ if (yyparse() || errcount)
+ exit(1);
+
+#ifdef DEBUG
+ if (dotree)
+ print_parse_tree(expression_value);
+#endif
+ /* Set up the field variables */
+ init_fields();
+
+ if (begin_block)
+ (void) interpret(begin_block);
+ if (!exiting && (expression_value || end_block)) {
+ if(input_file)
+ do_file(input_file);
+ while ((fp = nextfile()) != NULL) {
+ do_file(fp);
+ if (exiting)
+ break;
+ }
+ }
+ if (end_block)
+ (void) interpret(end_block);
+ if (flush_io() != 0 && exit_val == 0)
+ exit_val = 1;
+ if (close_io() != 0 && exit_val == 0)
+ exit_val = 1;
+ exit(exit_val);
}
-er_panic(str)
-char *str;
+do_file(fp)
+FILE *fp;
{
- fprintf(stderr,"%s: ",myname);
- perror(str);
- exit(15);
+ input_file = fp;
+ /* This is where it spends all its time. The infamous MAIN LOOP */
+ if (inrec() == 0) {
+ while (interpret(expression_value) && inrec() == 0)
+ ;
+ }
+ if (fp != stdin)
+ (void) fclose(fp);
}
usage()
{
- fprintf(stderr,"%s: usage: %s {-f progfile | program } [-F{c} -R{c}] file . . .\n",myname,myname);
+#ifdef STRICT
+ char *opt1 = "[ -Ffs ] -f progfile [ -- ]";
+ char *opt2 = "[ -Ffs ] [ -- ] 'program'";
+#else
+ char *opt1 = "[ -v ] [ -Ffs ] -f progfile [ -- ]";
+ char *opt2 = "[ -v ] [ -Ffs ] [ -- ] 'program'";
+#endif
+
+ fprintf(stderr, "usage: %s %s file ...\n %s %s file ...\n",
+ myname, opt1, myname, opt2);
exit(11);
}
-
-/* This allocates a new node of type ty. Note that this node will not go
- away unless freed, so don't use it for tmp storage */
NODE *
-newnode(ty)
-NODETYPE ty;
+node_common(op)
+NODETYPE op;
{
register NODE *r;
-
- r=(NODE *)malloc(sizeof(NODE));
- if(r==NULL)
- abort();
- r->type=ty;
+ extern int lineno;
+ extern int numfiles;
+ extern int tempsource;
+ extern char **sourcefile;
+ extern int curinfile;
+
+ emalloc(r, NODE *, sizeof(NODE), "node_common");
+ r->type = op;
+ r->source_line = lineno;
+ if (numfiles > 1 && !tempsource)
+ r->source_file = sourcefile[curinfile];
+ else
+ r->source_file = NULL;
return r;
}
-
-/* Duplicate a node. (For global strings, "duplicate" means crank up
- the reference count.) This creates global nodes. . .*/
+/*
+ * This allocates a node with defined lnode and rnode.
+ * This should only be used by yyparse+co while reading in the program
+ */
NODE *
-dupnode(n)
-NODE *n;
+node(left, op, right)
+NODE *left, *right;
+NODETYPE op;
{
register NODE *r;
- if(n->type==Node_string) {
- n->stref++;
- return n;
- } else if(n->type==Node_temp_string) {
- r=newnode(Node_string);
- r->stlen=n->stlen;
- r->stref=1;
- r->stptr=malloc(n->stlen+1);
- if(r->stptr==NULL)
- abort();
- bcopy (n->stptr, r->stptr, n->stlen);
- r->stptr[r->stlen]='\0'; /* JF for hackval */
- return r;
- } else {
- r=newnode(Node_illegal);
- *r= *n;
- return r;
- }
-}
-
-/* This allocates a node with defined lnode and rnode. */
-/* This should only be used by yyparse+co while
- reading in the program */
-NODE *
-node (left, op, right)
- NODE *left, *right;
- NODETYPE op;
-{
- register NODE *r;
-
- r = (NODE *)obstack_alloc(&other_stack,sizeof(NODE));
- r->type=op;
- r->lnode = left;
- r->rnode = right;
- return r;
+ r = node_common(op);
+ r->lnode = left;
+ r->rnode = right;
+ return r;
}
-/* This allocates a node with defined subnode and proc */
-/* Otherwise like node() */
+/*
+ * This allocates a node with defined subnode and proc
+ * Otherwise like node()
+ */
NODE *
snode(subn, op, procp)
NODETYPE op;
-NODE *(*procp)();
+NODE *(*procp) ();
NODE *subn;
{
register NODE *r;
- r=(NODE *)obstack_alloc(&other_stack,sizeof(NODE));
- r->type=op;
- r->subnode=subn;
- r->proc=procp;
+ r = node_common(op);
+ r->subnode = subn;
+ r->proc = procp;
return r;
}
-/* (jfw) This allocates a Node_line_range node
- * with defined condpair and zeroes the trigger word
- * to avoid the temptation of assuming that calling
- * 'node( foo, Node_line_range, 0)' will properly initialize 'triggered'.
+/*
+ * This allocates a Node_line_range node with defined condpair and
+ * zeroes the trigger word to avoid the temptation of assuming that calling
+ * 'node( foo, Node_line_range, 0)' will properly initialize 'triggered'.
*/
/* Otherwise like node() */
NODE *
@@ -373,351 +463,378 @@ NODE *cpair;
{
register NODE *r;
- r=(NODE *)obstack_alloc(&other_stack,sizeof(NODE));
- r->type=Node_line_range;
- r->condpair=cpair;
+ emalloc(r, NODE *, sizeof(NODE), "mkrangenode");
+ r->type = Node_line_range;
+ r->condpair = cpair;
r->triggered = 0;
return r;
}
-/* this allocates a node with defined numbr */
-/* This creates global nodes! */
-NODE *
-make_number (x)
- AWKNUM x;
-{
- register NODE *r;
-
- r=newnode(Node_number);
- r->numbr = x;
- return r;
-}
-
-/* This creates temporary nodes. They go away quite quicly, so
- don't use them for anything important */
-#ifndef FAST
-NODE *
-tmp_number(x)
-AWKNUM x;
-{
-#ifdef DONTDEF
- return make_number(x);
-#endif
- NODE *r;
-
- r=(NODE *)obstack_alloc(&temp_strings,sizeof(NODE));
- r->type=Node_number;
- r->numbr=x;
- return r;
-}
-#endif
-
-/* Make a string node. If len==0, the string passed in S is supposed to end
- with a double quote, but have had the beginning double quote
- already stripped off by yylex.
- If LEN!=0, we don't care what s ends with. This creates a global node */
-
-NODE *
-make_string (s,len)
- char *s;
-{
- register NODE *r;
- register char *pf,*pt;
- register int c;
-
- /* the aborts are impossible because yylex is supposed to have
- already checked for unterminated strings */
- if(len==-1) { /* Called from yyparse, find our own len */
-#ifndef FAST
- if (s[-1] != '\"') /* Didn't start with " */
- abort ();
-#endif
-
- for(pf = pt = s; *pf != '\0' && *pf!='\"';) {
- c= *pf++;
- switch(c) {
-#ifndef FAST
- case '\0':
- abort();
-#endif
-
- case '\\':
-#ifndef FAST
- if(*pf=='\0')
- abort();
-#endif
-
- c= *pf++;
- switch(c) {
- case '\\': /* no massagary needed */
- case '\'':
- case '\"':
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- c-='0';
- while(*pf && *pf>='0' && *pf<='7') {
- c=c*8+ *pf++ - '0';
- }
- break;
- case 'b':
- c='\b';
- break;
- case 'f':
- c='\f';
- break;
- case 'n':
- c='\n';
- break;
- case 'r':
- c='\r';
- break;
- case 't':
- c='\t';
- break;
- case 'v':
- c='\v';
- break;
- default:
- *pt++='\\';
- break;
- }
- /* FALL THROUGH */
- default:
- *pt++=c;
- break;
- }
- }
-#ifndef FAST
- if(*pf=='\0')
- abort(); /* JF hit the end of the buf */
-#endif
- len = pt - s; /* JF was p - s - 1 */
- }
-
- r=newnode(Node_string);
- r->stptr=(char *)malloc(len+1);
- if(r->stptr==0)
- abort();
- r->type=Node_string;
- r->stlen=len;
- r->stref=1;
- bcopy (s, r->stptr, len);
- r->stptr[len]='\0'; /* JF a hack */
-
- return r;
-}
-
-/* #ifndef FAST */
-/* This should be a macro for speed, but the C compiler chokes. */
-/* Read the warning under tmp_number */
-NODE *
-tmp_string(s,len)
+struct re_pattern_buffer *
+mk_re_parse(s)
char *s;
{
- register NODE *r;
-
-#ifdef DONTDEF
- return make_string(s,len);
-#endif
- r=(NODE *)obstack_alloc(&temp_strings,sizeof(NODE));
- r->stptr=(char *)obstack_alloc(&temp_strings,len+1);
- r->type=Node_temp_string;
- r->stlen=len;
- r->stref=1;
- bcopy (s, r->stptr, len);
- r->stptr[len]='\0'; /* JF a hack */
-
- return r;
+ register char *src, *dest;
+ int c;
+
+ for (dest = src = s; *src != '\0'; src++) {
+ if (*src == '\\') {
+ c = *++src;
+ switch (c) {
+ case 'b':
+ *dest++ = '\b';
+ break;
+ case 'f':
+ *dest++ = '\f';
+ break;
+ case 'n':
+ *dest++ = '\n';
+ break;
+ case 'r':
+ *dest++ = '\r';
+ break;
+ case 't':
+ *dest++ = '\t';
+ break;
+ case 'v':
+ *dest++ = '\v';
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ register int i = c - '0';
+ register int count = 0;
+
+ while (++count < 3) {
+ if ((c = *++src) >= '0' && c <= '7') {
+ i *= 8;
+ i += c - '0';
+ } else
+ break;
+ }
+ *dest++ = i;
+ }
+ break;
+ default:
+ *dest++ = '\\';
+ *dest++ = c;
+ break;
+ }
+ } else if (*src == '/')
+ break;
+ else
+ *dest++ = *src;
+ }
+ return make_regexp(tmp_string(s, dest-s));
}
-/* #endif */
/* Generate compiled regular expressions */
struct re_pattern_buffer *
-make_regexp (s)
- char *s;
+make_regexp(s)
+NODE *s;
{
- typedef struct re_pattern_buffer RPAT;
- RPAT *rp;
- char *p, *err;
-
- rp = (RPAT *) obstack_alloc(&other_stack, sizeof (RPAT));
- bzero((char *)rp,sizeof(RPAT));
- rp->buffer = (char *)malloc(8); /* JF I'd obstack allocate it,
- except the regex routines
- try to realloc() it, which fails. */
- /* Note that this means it may never be freed. Someone fix, please? */
-
- rp->allocated = 8;
- rp->fastmap = (char *)obstack_alloc(&other_stack, 256);
-
- for (p = s; *p != '\0'; p++) {
- if (*p == '\\')
- p++;
- else if (*p == '/')
- break;
- }
-#ifndef FAST
- if (*p != '/')
- abort (); /* impossible */
-#endif
-
- /* JF was re_compile_pattern, but that mishandles ( ) and |,
- so I had to write my own front end. Sigh. */
-
- if ((err = re_compile_pattern (s, p - s, rp)) != NULL) {
- fprintf (stderr, "illegal regexp: ");
- yyerror (err); /* fatal */
- }
-
- return rp;
+ struct re_pattern_buffer *rp;
+ char *err;
+
+ emalloc(rp, struct re_pattern_buffer *, sizeof(*rp), "make_regexp");
+ bzero((char *) rp, sizeof(*rp));
+ emalloc(rp->buffer, char *, 8, "make_regexp");
+ rp->allocated = 8;
+ emalloc(rp->fastmap, char *, 256, "make_regexp");
+
+ if ((err = re_compile_pattern(s->stptr, s->stlen, rp)) != NULL)
+ fatal("%s: /%s/", err, s->stptr);
+ free_temp(s);
+ return rp;
}
/* Build a for loop */
-FOR_LOOP_HEADER *
-make_for_loop (init, cond, incr)
- NODE *init, *cond, *incr;
+NODE *
+make_for_loop(init, cond, incr)
+NODE *init, *cond, *incr;
{
- register FOR_LOOP_HEADER *r;
-
- r = (FOR_LOOP_HEADER *)obstack_alloc(&other_stack,sizeof (FOR_LOOP_HEADER));
- r->init = init;
- r->cond = cond;
- r->incr = incr;
- return r;
+ register FOR_LOOP_HEADER *r;
+ NODE *n;
+
+ emalloc(r, FOR_LOOP_HEADER *, sizeof(FOR_LOOP_HEADER), "make_for_loop");
+ emalloc(n, NODE *, sizeof(NODE), "make_for_loop");
+ r->init = init;
+ r->cond = cond;
+ r->incr = incr;
+ n->type = Node_illegal;
+ n->sub.nodep.r.hd = r;
+ return n;
}
/* Name points to a variable name. Make sure its in the symbol table */
NODE *
-variable (name)
- char *name;
+variable(name)
+char *name;
{
- register NODE *r;
- NODE *lookup(), *install();
-
- if ((r = lookup (variables, name)) == NULL) {
- r = install (variables, name, node(Nnull_string, Node_var, (NODE *)NULL));
- /* JF make_number (0.0) is WRONG */
- }
- return r;
+ register NODE *r;
+ NODE *lookup(), *install(), *make_name();
+
+ if ((r = lookup(variables, name)) == NULL)
+ r = install(variables, name,
+ node(Nnull_string, Node_var, (NODE *) NULL));
+ return r;
}
/* Create a special variable */
NODE *
-spc_var (name,value)
+spc_var(name, value)
char *name;
NODE *value;
{
- register NODE *r;
- NODE *lookup(), *install();
+ register NODE *r;
+ NODE *lookup(), *install();
- if ((r = lookup(variables, name)) == NULL)
- r = install (variables, name, node(value, Node_var, (NODE *)NULL));
- return r;
+ if ((r = lookup(variables, name)) == NULL)
+ r = install(variables, name, node(value, Node_var, (NODE *) NULL));
+ return r;
}
-
+
+
/*
* Install a name in the hash table specified, even if it is already there.
- * Name stops with first non alphanumeric.
- * Caller must check against redefinition if that is desired.
+ * Name stops with first non alphanumeric. Caller must check against
+ * redefinition if that is desired.
*/
NODE *
-install (table, name, value)
- HASHNODE **table;
- char *name;
- NODE *value;
+install(table, name, value)
+HASHNODE **table;
+char *name;
+NODE *value;
{
- register HASHNODE *hp;
- register int i, len, bucket;
- register char *p;
-
- len = 0;
- p = name;
- while (is_identchar(*p))
- p++;
- len = p - name;
-
- i = sizeof (HASHNODE) + len + 1;
- hp = (HASHNODE *)obstack_alloc(&other_stack,i);
- bucket = hashf(name, len, HASHSIZE);
- hp->next = table[bucket];
- table[bucket] = hp;
- hp->length = len;
- hp->value = value;
- hp->name = ((char *) hp) + sizeof (HASHNODE);
- hp->length = len;
- bcopy (name, hp->name, len);
- return hp->value;
+ register HASHNODE *hp;
+ register int i, len, bucket;
+ register char *p;
+
+ len = 0;
+ p = name;
+ while (is_identchar(*p))
+ p++;
+ len = p - name;
+
+ i = sizeof(HASHNODE) + len + 1;
+ emalloc(hp, HASHNODE *, i, "install");
+ bucket = hashf(name, len, HASHSIZE);
+ hp->next = table[bucket];
+ table[bucket] = hp;
+ hp->length = len;
+ hp->value = value;
+ hp->name = ((char *) hp) + sizeof(HASHNODE);
+ hp->length = len;
+ bcopy(name, hp->name, len);
+ hp->name[len] = '\0';
+ hp->value->varname = hp->name;
+ return hp->value;
}
/*
* find the most recent hash node for name name (ending with first
- * non-identifier char) installed by install
+ * non-identifier char) installed by install
*/
NODE *
-lookup (table, name)
- HASHNODE **table;
- char *name;
+lookup(table, name)
+HASHNODE **table;
+char *name;
{
- register char *bp;
- register HASHNODE *bucket;
- register int len;
-
- for (bp = name; is_identchar(*bp); bp++)
- ;
- len = bp - name;
- bucket = table[hashf(name, len, HASHSIZE)];
- while (bucket) {
- if (bucket->length == len && strncmp(bucket->name, name, len) == 0)
- return bucket->value;
- bucket = bucket->next;
- }
- return NULL;
+ register char *bp;
+ register HASHNODE *bucket;
+ register int len;
+
+ for (bp = name; is_identchar(*bp); bp++)
+ ;
+ len = bp - name;
+ bucket = table[hashf(name, len, HASHSIZE)];
+ while (bucket) {
+ if (bucket->length == len && strncmp(bucket->name, name, len) == 0)
+ return bucket->value;
+ bucket = bucket->next;
+ }
+ return NULL;
}
#define HASHSTEP(old, c) ((old << 1) + c)
-#define MAKE_POS(v) (v & ~0x80000000) /* make number positive */
+#define MAKE_POS(v) (v & ~0x80000000) /* make number positive */
/*
- * return hash function on name. must be compatible with the one
- * computed a step at a time, elsewhere (JF: Where? I can't find it!)
+ * return hash function on name.
*/
int
hashf(name, len, hashsize)
- register char *name;
- register int len;
- int hashsize;
+register char *name;
+register int len;
+int hashsize;
{
- register int r = 0;
-
- while (len--)
- r = HASHSTEP(r, *name++);
-
- return MAKE_POS(r) % hashsize;
+ register int r = 0;
+
+ while (len--)
+ r = HASHSTEP(r, *name++);
+
+ r = MAKE_POS(r) % hashsize;
+ return r;
}
-/* Add new to the rightmost branch of LIST. This uses n^2 time, but
- doesn't get used enough to make optimizing worth it. . . */
+/*
+ * Add new to the rightmost branch of LIST. This uses n^2 time, but doesn't
+ * get used enough to make optimizing worth it. . .
+ */
/* You don't believe me? Profile it yourself! */
NODE *
-append_right(list,new)
-NODE *list,*new;
+append_right(list, new)
+NODE *list, *new;
{
register NODE *oldlist;
oldlist = list;
- while(list->rnode!=NULL)
- list=list->rnode;
+ while (list->rnode != NULL)
+ list = list->rnode;
list->rnode = new;
return oldlist;
}
+
+/*
+ * check if name is already installed; if so, it had better have Null value,
+ * in which case def is added as the value. Otherwise, install name with def
+ * as value.
+ */
+func_install(params, def)
+NODE *params;
+NODE *def;
+{
+ NODE *r;
+ NODE *lookup();
+
+ pop_params(params);
+ r = lookup(variables, params->param);
+ if (r != NULL) {
+ fatal("function name `%s' previously defined", params->param);
+ } else
+ (void) install(variables, params->param,
+ node(params, Node_func, def));
+}
+
+NODE *
+pop_var(name)
+char *name;
+{
+ register char *bp;
+ register HASHNODE *bucket, **save;
+ register int len;
+
+ for (bp = name; is_identchar(*bp); bp++)
+ ;
+ len = bp - name;
+ save = &(variables[hashf(name, len, HASHSIZE)]);
+ bucket = *save;
+ while (bucket) {
+ if (strncmp(bucket->name, name, len) == 0) {
+ *save = bucket->next;
+ return bucket->value;
+ }
+ save = &(bucket->next);
+ bucket = bucket->next;
+ }
+ return NULL;
+}
+
+pop_params(params)
+NODE *params;
+{
+ register NODE *np;
+
+ for (np = params; np != NULL; np = np->rnode)
+ pop_var(np->param);
+}
+
+NODE *
+make_name(name, type)
+char *name;
+NODETYPE type;
+{
+ register char *p;
+ register NODE *r;
+ register int len;
+
+ p = name;
+ while (is_identchar(*p))
+ p++;
+ len = p - name;
+ emalloc(r, NODE *, sizeof(NODE), "make_name");
+ emalloc(r->param, char *, len + 1, "make_name");
+ bcopy(name, r->param, len);
+ r->param[len] = '\0';
+ r->rnode = NULL;
+ r->type = type;
+ return (install(variables, name, r));
+}
+
+NODE *make_param(name)
+char *name;
+{
+ NODE *r;
+
+ r = make_name(name, Node_param_list);
+ r->param_cnt = param_counter++;
+ return r;
+}
+
+FILE *
+nextfile()
+{
+ static int i = 1;
+ static int files = 0;
+ char *arg;
+ char *cp;
+ FILE *fp;
+ extern NODE **assoc_lookup();
+
+ for (; i < (int) (ARGC_node->lnode->numbr); i++) {
+ arg = (*assoc_lookup(ARGV_node, tmp_number((AWKNUM) i)))->stptr;
+ if (*arg == '\0')
+ continue;
+ cp = index(arg, '=');
+ if (cp != NULL) {
+ *cp++ = '\0';
+ variable(arg)->var_value = make_string(cp, strlen(cp));
+ } else {
+ extern NODE *deref;
+
+ files++;
+ if (strcmp(arg, "-") == 0)
+ fp = stdin;
+ else
+ fp = fopen(arg, "r");
+ if (fp == NULL)
+ fatal("cannot open file `%s' for reading (%s)",
+ arg, sys_errlist[errno]);
+ /* NOTREACHED */
+ /* This is a kludge. */
+ deref = FILENAME_node->var_value;
+ do_deref();
+ FILENAME_node->var_value =
+ make_string(arg, strlen(arg));
+ FNR_node->var_value->numbr = 0.0;
+ i++;
+ return fp;
+ }
+ }
+ if (files == 0) {
+ files++;
+ /* no args. -- use stdin */
+ /* FILENAME is init'ed to "-" */
+ /* FNR is init'ed to 0 */
+ return stdin;
+ }
+ return NULL;
+}