|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T b
Length: 20701 (0x50dd) Types: TextFile Names: »builtin.c«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89 └─⟦f68d31fd9⟧ »./gawk-2.11.tar.Z« └─⟦2fc192871⟧ └─⟦this⟧ »gawk-2.11/builtin.c«
/* * builtin.c - Builtin functions and various utility procedures */ /* * 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 void srandom(); extern char *initstate(); extern char *setstate(); extern long random(); extern NODE **fields_arr; static void get_one(); static void get_two(); static int get_three(); /* Builtin functions */ NODE * do_exp(tree) NODE *tree; { NODE *tmp; double d, res; double exp(); get_one(tree, &tmp); d = force_number(tmp); free_temp(tmp); errno = 0; res = exp(d); if (errno == ERANGE) warning("exp argument %g is out of range", d); return tmp_number((AWKNUM) res); } NODE * do_index(tree) NODE *tree; { NODE *s1, *s2; register char *p1, *p2; register int l1, l2; long ret; get_two(tree, &s1, &s2); force_string(s1); force_string(s2); p1 = s1->stptr; p2 = s2->stptr; l1 = s1->stlen; l2 = s2->stlen; ret = 0; if (! strict && IGNORECASE_node->var_value->numbr != 0.0) { while (l1) { if (casetable[*p1] == casetable[*p2] && strncasecmp(p1, p2, l2) == 0) { ret = 1 + s1->stlen - l1; break; } l1--; p1++; } } else { while (l1) { if (STREQN(p1, p2, l2)) { ret = 1 + s1->stlen - l1; break; } l1--; p1++; } } free_temp(s1); free_temp(s2); return tmp_number((AWKNUM) ret); } NODE * do_int(tree) NODE *tree; { NODE *tmp; double floor(); double d; get_one(tree, &tmp); d = floor((double)force_number(tmp)); free_temp(tmp); return tmp_number((AWKNUM) d); } NODE * do_length(tree) NODE *tree; { NODE *tmp; int len; get_one(tree, &tmp); len = force_string(tmp)->stlen; free_temp(tmp); return tmp_number((AWKNUM) len); } NODE * do_log(tree) NODE *tree; { NODE *tmp; double log(); double d, arg; get_one(tree, &tmp); arg = (double) force_number(tmp); if (arg < 0.0) warning("log called with negative argument %g", arg); d = log(arg); free_temp(tmp); return tmp_number((AWKNUM) d); } /* * Note that the output buffer cannot be static because sprintf may get * called recursively by force_string. Hence the wasteful alloca calls */ /* %e and %f formats are not properly implemented. Someone should fix them */ NODE * do_sprintf(tree) NODE *tree; { #define bchunk(s,l) if(l) {\ while((l)>ofre) {\ char *tmp;\ tmp=(char *)alloca(osiz*2);\ memcpy(tmp,obuf,olen);\ obuf=tmp;\ ofre+=osiz;\ osiz*=2;\ }\ memcpy(obuf+olen,s,(l));\ olen+=(l);\ ofre-=(l);\ } /* Is there space for something L big in the buffer? */ #define chksize(l) if((l)>ofre) {\ char *tmp;\ tmp=(char *)alloca(osiz*2);\ memcpy(tmp,obuf,olen);\ obuf=tmp;\ ofre+=osiz;\ osiz*=2;\ } /* * Get the next arg to be formatted. If we've run out of args, * return "" (Null string) */ #define parse_next_arg() {\ if(!carg) arg= Nnull_string;\ else {\ get_one(carg,&arg);\ carg=carg->rnode;\ }\ } char *obuf; int osiz, ofre, olen; static char chbuf[] = "0123456789abcdef"; static char sp[] = " "; char *s0, *s1; int n0; NODE *sfmt, *arg; register NODE *carg; long fw, prec, lj, alt, big; long *cur; long val; #ifdef sun386 /* Can't cast unsigned (int/long) from ptr->value */ long tmp_uval; /* on 386i 4.0.1 C compiler -- it just hangs */ #endif unsigned long uval; int sgn; int base; char cpbuf[30]; /* if we have numbers bigger than 30 */ char *cend = &cpbuf[30];/* chars, we lose, but seems unlikely */ char *cp; char *fill; double tmpval; char *pr_str; int ucasehex = 0; extern char *gcvt(); obuf = (char *) alloca(120); osiz = 120; ofre = osiz; olen = 0; get_one(tree, &sfmt); sfmt = force_string(sfmt); carg = tree->rnode; for (s0 = s1 = sfmt->stptr, n0 = sfmt->stlen; n0-- > 0;) { if (*s1 != '%') { s1++; continue; } bchunk(s0, s1 - s0); s0 = s1; cur = &fw; fw = 0; prec = 0; lj = alt = big = 0; fill = sp; cp = cend; s1++; retry: --n0; switch (*s1++) { case '%': bchunk("%", 1); s0 = s1; break; case '0': if (fill != sp || lj) goto lose; if (cur == &fw) fill = "0"; /* FALL through */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (cur == 0) goto lose; *cur = s1[-1] - '0'; while (n0 > 0 && *s1 >= '0' && *s1 <= '9') { --n0; *cur = *cur * 10 + *s1++ - '0'; } goto retry; #ifdef not_yet case ' ': /* print ' ' or '-' */ case '+': /* print '+' or '-' */ #endif case '-': if (lj || fill != sp) goto lose; lj++; goto retry; case '.': if (cur != &fw) goto lose; cur = ≺ goto retry; case '#': if (alt) goto lose; alt++; goto retry; case 'l': if (big) goto lose; big++; goto retry; case 'c': parse_next_arg(); if (arg->flags & NUMERIC) { #ifdef sun386 tmp_uval = arg->numbr; uval= (unsigned long) tmp_uval; #else uval = (unsigned long) arg->numbr; #endif cpbuf[0] = uval; prec = 1; pr_str = cpbuf; goto dopr_string; } if (! prec) prec = 1; else if (prec > arg->stlen) prec = arg->stlen; pr_str = arg->stptr; goto dopr_string; case 's': parse_next_arg(); arg = force_string(arg); if (!prec || prec > arg->stlen) prec = arg->stlen; pr_str = arg->stptr; dopr_string: if (fw > prec && !lj) { while (fw > prec) { bchunk(sp, 1); fw--; } } bchunk(pr_str, (int) prec); if (fw > prec) { while (fw > prec) { bchunk(sp, 1); fw--; } } s0 = s1; free_temp(arg); break; case 'd': case 'i': parse_next_arg(); val = (long) force_number(arg); free_temp(arg); if (val < 0) { sgn = 1; val = -val; } else sgn = 0; do { *--cp = '0' + val % 10; val /= 10; } while (val); if (sgn) *--cp = '-'; if (prec > fw) fw = prec; prec = cend - cp; if (fw > prec && !lj) { if (fill != sp && *cp == '-') { bchunk(cp, 1); cp++; prec--; fw--; } while (fw > prec) { bchunk(fill, 1); fw--; } } bchunk(cp, (int) prec); if (fw > prec) { while (fw > prec) { bchunk(fill, 1); fw--; } } s0 = s1; break; case 'u': base = 10; goto pr_unsigned; case 'o': base = 8; goto pr_unsigned; case 'X': ucasehex = 1; case 'x': base = 16; goto pr_unsigned; pr_unsigned: parse_next_arg(); uval = (unsigned long) force_number(arg); free_temp(arg); do { *--cp = chbuf[uval % base]; if (ucasehex && isalpha(*cp)) *cp = toupper(*cp); uval /= base; } while (uval); if (alt && (base == 8 || base == 16)) { if (base == 16) { if (ucasehex) *--cp = 'X'; else *--cp = 'x'; } *--cp = '0'; } prec = cend - cp; if (fw > prec && !lj) { while (fw > prec) { bchunk(fill, 1); fw--; } } bchunk(cp, (int) prec); if (fw > prec) { while (fw > prec) { bchunk(fill, 1); fw--; } } s0 = s1; break; case 'g': parse_next_arg(); tmpval = force_number(arg); free_temp(arg); if (prec == 0) prec = 13; (void) gcvt(tmpval, (int) prec, cpbuf); prec = strlen(cpbuf); cp = cpbuf; if (fw > prec && !lj) { if (fill != sp && *cp == '-') { bchunk(cp, 1); cp++; prec--; } /* Deal with .5 as 0.5 */ if (fill == sp && *cp == '.') { --fw; while (--fw >= prec) { bchunk(fill, 1); } bchunk("0", 1); } else while (fw-- > prec) bchunk(fill, 1); } else {/* Turn .5 into 0.5 */ /* FOO */ if (*cp == '.' && fill == sp) { bchunk("0", 1); --fw; } } bchunk(cp, (int) prec); if (fw > prec) while (fw-- > prec) bchunk(fill, 1); s0 = s1; break; case 'f': parse_next_arg(); tmpval = force_number(arg); free_temp(arg); chksize(fw + prec + 5); /* 5==slop */ cp = cpbuf; *cp++ = '%'; if (lj) *cp++ = '-'; if (fill != sp) *cp++ = '0'; if (cur != &fw) { (void) strcpy(cp, "*.*f"); (void) sprintf(obuf + olen, cpbuf, (int) fw, (int) prec, (double) tmpval); } else { (void) strcpy(cp, "*f"); (void) sprintf(obuf + olen, cpbuf, (int) fw, (double) tmpval); } cp = obuf + olen; ofre -= strlen(obuf + olen); olen += strlen(obuf + olen); /* There may be nulls */ s0 = s1; break; case 'e': parse_next_arg(); tmpval = force_number(arg); free_temp(arg); chksize(fw + prec + 5); /* 5==slop */ cp = cpbuf; *cp++ = '%'; if (lj) *cp++ = '-'; if (fill != sp) *cp++ = '0'; if (cur != &fw) { (void) strcpy(cp, "*.*e"); (void) sprintf(obuf + olen, cpbuf, (int) fw, (int) prec, (double) tmpval); } else { (void) strcpy(cp, "*e"); (void) sprintf(obuf + olen, cpbuf, (int) fw, (double) tmpval); } cp = obuf + olen; ofre -= strlen(obuf + olen); olen += strlen(obuf + olen); /* There may be nulls */ s0 = s1; break; default: lose: break; } } bchunk(s0, s1 - s0); free_temp(sfmt); return tmp_string(obuf, olen); } void do_printf(tree) NODE *tree; { struct redirect *rp = NULL; register FILE *fp = stdout; int errflg = 0; /* not used, sigh */ if (tree->rnode) { rp = redirect(tree->rnode, &errflg); if (rp) fp = rp->fp; } if (fp) print_simple(do_sprintf(tree->lnode), fp); if (rp && (rp->flag & RED_NOBUF)) fflush(fp); } NODE * do_sqrt(tree) NODE *tree; { NODE *tmp; double sqrt(); double d, arg; get_one(tree, &tmp); arg = (double) force_number(tmp); if (arg < 0.0) warning("sqrt called with negative argument %g", arg); d = sqrt(arg); free_temp(tmp); return tmp_number((AWKNUM) d); } NODE * do_substr(tree) NODE *tree; { NODE *t1, *t2, *t3; NODE *r; register int indx, length; t1 = t2 = t3 = NULL; length = -1; if (get_three(tree, &t1, &t2, &t3) == 3) length = (int) force_number(t3); indx = (int) force_number(t2) - 1; t1 = force_string(t1); if (length == -1) length = t1->stlen; if (indx < 0) indx = 0; if (indx >= t1->stlen || length <= 0) { if (t3) free_temp(t3); free_temp(t2); free_temp(t1); return Nnull_string; } if (indx + length > t1->stlen) length = t1->stlen - indx; if (t3) free_temp(t3); free_temp(t2); r = tmp_string(t1->stptr + indx, length); free_temp(t1); return r; } NODE * do_system(tree) NODE *tree; { #if defined(unix) || defined(MSDOS) /* || defined(gnu) */ NODE *tmp; int ret; (void) flush_io (); /* so output is synchronous with gawk's */ get_one(tree, &tmp); ret = system(force_string(tmp)->stptr); ret = (ret >> 8) & 0xff; free_temp(tmp); return tmp_number((AWKNUM) ret); #else fatal("the \"system\" function is not supported."); /* NOTREACHED */ #endif } void do_print(tree) register NODE *tree; { struct redirect *rp = NULL; register FILE *fp = stdout; int errflg = 0; /* not used, sigh */ if (tree->rnode) { rp = redirect(tree->rnode, &errflg); if (rp) fp = rp->fp; } if (!fp) return; tree = tree->lnode; if (!tree) tree = WHOLELINE; if (tree->type != Node_expression_list) { if (!(tree->flags & STR)) cant_happen(); print_simple(tree, fp); } else { while (tree) { print_simple(force_string(tree_eval(tree->lnode)), fp); tree = tree->rnode; if (tree) print_simple(OFS_node->var_value, fp); } } print_simple(ORS_node->var_value, fp); if (rp && (rp->flag & RED_NOBUF)) fflush(fp); } NODE * do_tolower(tree) NODE *tree; { NODE *t1, *t2; register char *cp, *cp2; get_one(tree, &t1); t1 = force_string(t1); t2 = tmp_string(t1->stptr, t1->stlen); for (cp = t2->stptr, cp2 = t2->stptr + t2->stlen; cp < cp2; cp++) if (isupper(*cp)) *cp = tolower(*cp); free_temp(t1); return t2; } NODE * do_toupper(tree) NODE *tree; { NODE *t1, *t2; register char *cp; get_one(tree, &t1); t1 = force_string(t1); t2 = tmp_string(t1->stptr, t1->stlen); for (cp = t2->stptr; cp < t2->stptr + t2->stlen; cp++) if (islower(*cp)) *cp = toupper(*cp); free_temp(t1); return t2; } /* * Get the arguments to functions. No function cares if you give it too many * args (they're ignored). Only a few fuctions complain about being given * too few args. The rest have defaults. */ static void get_one(tree, res) NODE *tree, **res; { if (!tree) { *res = WHOLELINE; return; } *res = tree_eval(tree->lnode); } static void get_two(tree, res1, res2) NODE *tree, **res1, **res2; { if (!tree) { *res1 = WHOLELINE; return; } *res1 = tree_eval(tree->lnode); if (!tree->rnode) return; tree = tree->rnode; *res2 = tree_eval(tree->lnode); } static int get_three(tree, res1, res2, res3) NODE *tree, **res1, **res2, **res3; { if (!tree) { *res1 = WHOLELINE; return 0; } *res1 = tree_eval(tree->lnode); if (!tree->rnode) return 1; tree = tree->rnode; *res2 = tree_eval(tree->lnode); if (!tree->rnode) return 2; tree = tree->rnode; *res3 = tree_eval(tree->lnode); return 3; } int a_get_three(tree, res1, res2, res3) NODE *tree, **res1, **res2, **res3; { if (!tree) { *res1 = WHOLELINE; return 0; } *res1 = tree_eval(tree->lnode); if (!tree->rnode) return 1; tree = tree->rnode; *res2 = tree->lnode; if (!tree->rnode) return 2; tree = tree->rnode; *res3 = tree_eval(tree->lnode); return 3; } void print_simple(tree, fp) NODE *tree; FILE *fp; { if (fwrite(tree->stptr, sizeof(char), tree->stlen, fp) != tree->stlen) warning("fwrite: %s", strerror(errno)); free_temp(tree); } NODE * do_atan2(tree) NODE *tree; { NODE *t1, *t2; extern double atan2(); double d1, d2; get_two(tree, &t1, &t2); d1 = force_number(t1); d2 = force_number(t2); free_temp(t1); free_temp(t2); return tmp_number((AWKNUM) atan2(d1, d2)); } NODE * do_sin(tree) NODE *tree; { NODE *tmp; extern double sin(); double d; get_one(tree, &tmp); d = sin((double)force_number(tmp)); free_temp(tmp); return tmp_number((AWKNUM) d); } NODE * do_cos(tree) NODE *tree; { NODE *tmp; extern double cos(); double d; get_one(tree, &tmp); d = cos((double)force_number(tmp)); free_temp(tmp); return tmp_number((AWKNUM) d); } static int firstrand = 1; static char state[256]; #define MAXLONG 2147483647 /* maximum value for long int */ /* ARGSUSED */ NODE * do_rand(tree) NODE *tree; { if (firstrand) { (void) initstate((unsigned) 1, state, sizeof state); srandom(1); firstrand = 0; } return tmp_number((AWKNUM) random() / MAXLONG); } NODE * do_srand(tree) NODE *tree; { NODE *tmp; static long save_seed = 1; long ret = save_seed; /* SVR4 awk srand returns previous seed */ extern long time(); if (firstrand) (void) initstate((unsigned) 1, state, sizeof state); else (void) setstate(state); if (!tree) srandom((int) (save_seed = time((long *) 0))); else { get_one(tree, &tmp); srandom((int) (save_seed = (long) force_number(tmp))); free_temp(tmp); } firstrand = 0; return tmp_number((AWKNUM) ret); } NODE * do_match(tree) NODE *tree; { NODE *t1; int rstart; struct re_registers reregs; struct re_pattern_buffer *rp; int need_to_free = 0; t1 = force_string(tree_eval(tree->lnode)); tree = tree->rnode; if (tree == NULL || tree->lnode == NULL) fatal("match called with only one argument"); tree = tree->lnode; if (tree->type == Node_regex) { rp = tree->rereg; if (!strict && ((IGNORECASE_node->var_value->numbr != 0) ^ (tree->re_case != 0))) { /* recompile since case sensitivity differs */ rp = tree->rereg = mk_re_parse(tree->re_text, (IGNORECASE_node->var_value->numbr != 0)); tree->re_case = (IGNORECASE_node->var_value->numbr != 0); } } else { need_to_free = 1; rp = make_regexp(force_string(tree_eval(tree)), (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; if (need_to_free) { free(rp->buffer); free(rp->fastmap); free((char *) rp); } return tmp_number((AWKNUM) rstart); } static NODE * sub_common(tree, global) NODE *tree; int global; { register int len; register char *scan; register char *bp, *cp; int search_start = 0; int match_length; int matches = 0; char *buf; 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; int need_to_free = 0; if (tree == NULL) fatal("sub or gsub called with 0 arguments"); tmp = tree->lnode; if (tmp->type == Node_regex) { rp = tmp->rereg; if (! strict && ((IGNORECASE_node->var_value->numbr != 0) ^ (tmp->re_case != 0))) { /* recompile since case sensitivity differs */ rp = tmp->rereg = mk_re_parse(tmp->re_text, (IGNORECASE_node->var_value->numbr != 0)); tmp->re_case = (IGNORECASE_node->var_value->numbr != 0); } } else { need_to_free = 1; rp = make_regexp(force_string(tree_eval(tmp)), (IGNORECASE_node->var_value->numbr != 0)); if (rp == NULL) cant_happen(); } tree = tree->rnode; if (tree == NULL) fatal("sub or gsub called with only 1 argument"); 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 (need_to_free) { free(rp->buffer); free(rp->fastmap); free((char *) rp); } if (matches > 0) { if (field_num == 0) set_record(fields_arr[0]->stptr, fields_arr[0]->stlen); t->flags &= ~(NUM|NUMERIC); } field_num = -1; return tmp_number((AWKNUM) matches); } NODE * do_gsub(tree) NODE *tree; { return sub_common(tree, 1); } NODE * do_sub(tree) NODE *tree; { return sub_common(tree, 0); }