/* * awk4 -- Code for features in new AWK, System V compatibility. * * $Log: awk4.c,v $ * Revision 1.38 89/03/31 13:26:09 david * GNU license * * Revision 1.37 89/03/29 14:19:07 david * delinting and code movement * * Revision 1.36 89/03/22 22:10:23 david * a cleaner way to handle assignment to $n where n > 0 * * Revision 1.35 89/03/22 21:05:24 david * delete some obsolete code * * Revision 1.34 89/03/22 21:00:34 david * replace some free()'s with freenode() * * Revision 1.33 89/03/21 19:26:01 david * minor cleanup * * Revision 1.32 89/03/21 18:22:54 david * some function tracing debugging code * * Revision 1.31 89/03/21 10:53:21 david * cleanup * * Revision 1.30 89/03/15 22:22:03 david * fix from hack: check for null function before using it in diagnostic * * Revision 1.29 89/03/15 22:02:24 david * new case stuff added * * Revision 1.28 89/03/15 21:34:37 david * free more memory and purge obstack stuff * * Revision 1.27 88/12/14 10:53:49 david * malloc structures in func_call and free them on return * * Revision 1.26 88/12/13 22:29:15 david * minor change * * Revision 1.25 88/12/08 10:52:01 david * small correction to #ifdef'ing * * Revision 1.24 88/11/30 15:18:21 david * fooling around with memory allocation in func_call() but this new code remains * #ifdef'd out * correction to creasting private copy of string in do_sub() * * Revision 1.23 88/11/29 09:55:48 david * corrections to code that tracks value of NF -- this needs cleanup * * Revision 1.22 88/11/28 20:30:10 david * bug fix for do_sub when third arg. not specified * * Revision 1.21 88/11/22 13:51:10 david * Arnold: delinting * * Revision 1.20 88/11/15 10:25:14 david * Arnold: minor cleanup * * Revision 1.19 88/11/14 22:00:09 david * Arnold: error message on bad regexp; correction to handling of RSTART in * match() on failure; return arg handling to previous behaviour: var=val * arg is processed whne it is encountered. * * Revision 1.18 88/11/14 21:28:09 david * moved concat_exp(); update NF on assign to field > NF; temporarily aborted * mods. in func_call(); flag misnamed as TMP becomes PERM (don't free) * * Revision 1.17 88/11/01 12:19:37 david * cleanup and substantial changes to do_sub() * * Revision 1.16 88/10/19 21:59:20 david * replace malloc and realloc with error checking versions * * Revision 1.15 88/10/17 20:54:54 david * SYSV --> USG * * Revision 1.14 88/10/13 22:00:44 david * purge FAST and cleanup error messages * * Revision 1.13 88/10/11 09:29:12 david * retrieve parameters from the stack in func_call * * Revision 1.12 88/10/06 21:53:15 david * added FSF copyleft; Arnold's changes to command line processing * * */ /* * Copyright (C) 1986, 1988, 1989 the Free Software Foundation, Inc. * * This file is part of GAWK, the GNU implementation of the * AWK Progamming Language. * * GAWK is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * GAWK is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GAWK; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "awk.h" extern NODE **fields_arr; jmp_buf func_tag; NODE **stack_ptr; NODE * func_call(name, arg_list) NODE *name; /* name is a Node_val giving function name */ NODE *arg_list; /* Node_expression_list of calling args. */ { register NODE *arg, *argp, *r; NODE *n, *f; jmp_buf func_tag_stack; NODE *ret_node_stack; NODE **local_stack; NODE **sp; static int func_tag_valid = 0; int count; extern NODE *ret_node; /* * retrieve function definition node */ f = lookup(variables, name->stptr); if (!f || f->type != Node_func) fatal("function `%s' not defined", name->stptr); #ifdef FUNC_TRACE fprintf(stderr, "function %s called\n", name->stptr); #endif /* * mark stack for variables allocated during life of function */ count = f->lnode->param_cnt; emalloc(local_stack, NODE **, count * sizeof(NODE *), "func_call"); sp = local_stack; /* * for each calling arg. add NODE * on stack */ for (argp = arg_list; count && argp != NULL; argp = argp->rnode) { arg = argp->lnode; r = newnode(Node_var); /* * call by reference for arrays; see below also */ if (arg->type == Node_param_list) arg = stack_ptr[arg->param_cnt]; if (arg->type == Node_var_array) *r = *arg; else { n = dupnode(tree_eval(arg)); r->lnode = n; r->rnode = (NODE *) NULL; } *sp++ = r; count--; } if (argp != NULL) /* left over calling args. */ warning( "function `%s' called with more arguments than declared", name->stptr); /* * add remaining params. on stack with null value */ while (count-- > 0) { r = newnode(Node_var); r->lnode = Nnull_string; r->rnode = (NODE *) NULL; *sp++ = r; } /* * execute function body, saving context, as a return statement * will longjmp back here */ sp = local_stack; local_stack = stack_ptr; stack_ptr = sp; PUSH_BINDING(func_tag_stack, func_tag, func_tag_valid); ret_node_stack = ret_node; if (_setjmp(func_tag) == 0) (void) interpret(f->rnode); r = ret_node; ret_node = ret_node_stack; RESTORE_BINDING(func_tag_stack, func_tag, func_tag_valid); sp = stack_ptr; stack_ptr = local_stack; local_stack = sp; /* * here, we pop each parameter and check whether * it was an array. If so, and if the arg. passed in was * a simple variable, then the value should be copied back. * This achieves "call-by-reference" for arrays. */ count = f->lnode->param_cnt; for (argp = arg_list; count-- > 0 && argp != NULL; argp = argp->rnode) { arg = argp->lnode; n = *sp++; if (arg->type == Node_var && n->type == Node_var_array) { arg->var_array = n->var_array; arg->type = Node_var_array; } deref = n->lnode; do_deref(); freenode(n); } while (count-- > 0) { n = *sp++; deref = n->lnode; do_deref(); freenode(n); } free((char *) local_stack); return r; } NODE * do_match(tree) NODE *tree; { NODE *t1; int rstart; struct re_registers reregs; struct re_pattern_buffer *rp; t1 = force_string(tree_eval(tree->lnode)); if (tree->rnode->type == Node_regex) { rp = tree->rnode->rereg; if (!strict && ((IGNORECASE_node->var_value->numbr != 0) ^ (tree->rnode->re_case != 0))) { /* recompile since case sensitivity differs */ rp = tree->rnode->rereg = mk_re_parse(tree->rnode->re_text, (IGNORECASE_node->var_value->numbr != 0)); tree->rnode->re_case = (IGNORECASE_node->var_value->numbr != 0); } } else { rp = make_regexp(force_string(tree_eval(tree->rnode)), (IGNORECASE_node->var_value->numbr != 0)); if (rp == NULL) cant_happen(); } rstart = re_search(rp, t1->stptr, t1->stlen, 0, t1->stlen, &reregs); free_temp(t1); if (rstart >= 0) { rstart++; /* 1-based indexing */ /* RSTART set to rstart below */ RLENGTH_node->var_value->numbr = (AWKNUM) (reregs.end[0] - reregs.start[0]); } else { /* * Match failed. Set RSTART to 0, RLENGTH to -1. * Return the value of RSTART. */ rstart = 0; /* used as return value */ RLENGTH_node->var_value->numbr = -1.0; } RSTART_node->var_value->numbr = (AWKNUM) rstart; return tmp_number((AWKNUM) rstart); } NODE * do_sub(tree) NODE *tree; { register int len; register char *scan; register char *bp, *cp; int search_start = 0; int match_length; int matches = 0; char *buf; int global; struct re_pattern_buffer *rp; NODE *s; /* subst. pattern */ NODE *t; /* string to make sub. in; $0 if none given */ struct re_registers reregs; unsigned int saveflags; NODE *tmp; NODE **lhs; char *lastbuf; global = (tree->type == Node_gsub); if (tree->rnode->type == Node_regex) { rp = tree->rnode->rereg; if (! strict && ((IGNORECASE_node->var_value->numbr != 0) ^ (tree->rnode->re_case != 0))) { /* recompile since case sensitivity differs */ rp = tree->rnode->rereg = mk_re_parse(tree->rnode->re_text, (IGNORECASE_node->var_value->numbr != 0)); tree->rnode->re_case = (IGNORECASE_node->var_value->numbr != 0); } } else { rp = make_regexp(force_string(tree_eval(tree->rnode)), (IGNORECASE_node->var_value->numbr != 0)); if (rp == NULL) cant_happen(); } tree = tree->lnode; s = force_string(tree_eval(tree->lnode)); tree = tree->rnode; deref = 0; field_num = -1; if (tree == NULL) { t = node0_valid ? fields_arr[0] : *get_field(0, 0); lhs = &fields_arr[0]; field_num = 0; deref = t; } else { t = tree->lnode; lhs = get_lhs(t, 1); t = force_string(tree_eval(t)); } /* * create a private copy of the string */ if (t->stref > 1 || (t->flags & PERM)) { saveflags = t->flags; t->flags &= ~MALLOC; tmp = dupnode(t); t->flags = saveflags; do_deref(); t = tmp; if (lhs) *lhs = tmp; } lastbuf = t->stptr; do { if (re_search(rp, t->stptr, t->stlen, search_start, t->stlen-search_start, &reregs) == -1 || reregs.start[0] == reregs.end[0]) break; matches++; /* * first, make a pass through the sub. pattern, to calculate * the length of the string after substitution */ match_length = reregs.end[0] - reregs.start[0]; len = t->stlen - match_length; for (scan = s->stptr; scan < s->stptr + s->stlen; scan++) if (*scan == '&') len += match_length; else if (*scan == '\\' && *(scan+1) == '&') { scan++; len++; } else len++; emalloc(buf, char *, len + 1, "do_sub"); bp = buf; /* * now, create the result, copying in parts of the original * string */ for (scan = t->stptr; scan < t->stptr + reregs.start[0]; scan++) *bp++ = *scan; for (scan = s->stptr; scan < s->stptr + s->stlen; scan++) if (*scan == '&') for (cp = t->stptr + reregs.start[0]; cp < t->stptr + reregs.end[0]; cp++) *bp++ = *cp; else if (*scan == '\\' && *(scan+1) == '&') { scan++; *bp++ = *scan; } else *bp++ = *scan; search_start = bp - buf; for (scan = t->stptr + reregs.end[0]; scan < t->stptr + t->stlen; scan++) *bp++ = *scan; *bp = '\0'; free(lastbuf); t->stptr = buf; lastbuf = buf; t->stlen = len; } while (global && search_start < t->stlen); free_temp(s); if (matches > 0) { if (field_num == 0) set_record(fields_arr[0]->stptr, fields_arr[0]->stlen); t->flags &= ~NUM; } field_num = -1; return tmp_number((AWKNUM) matches); }