home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
gnu
/
gawk213s
/
field.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-29
|
15KB
|
615 lines
/*
* field.c - routines for dealing with fields and record parsing
*/
/*
* Copyright (C) 1986, 1988, 1989, 1991 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"
static int (*parse_field) P((int, char **, int, char *,
Regexp *, void (*)(), NODE *));
static void rebuild_record P((void));
static int re_parse_field P((int, char **, int, char *,
Regexp *, void (*)(), NODE *));
static int def_parse_field P((int, char **, int, char *,
Regexp *, void (*)(), NODE *));
static int sc_parse_field P((int, char **, int, char *,
Regexp *, void (*)(), NODE *));
static int fw_parse_field P((int, char **, int, char *,
Regexp *, void (*)(), NODE *));
static void set_element P((int, char *, int, NODE *));
static Regexp *FS_regexp = NULL;
static char *parse_extent; /* marks where to restart parse of record */
static int parse_high_water=0; /* field number that we have parsed so far */
static int nf_high_water = 0; /* size of fields_arr */
static char f_empty[] = "\0";
static int resave_fs;
static NODE *save_FS;
static char *save_fs; /* save current value of FS when line is read,
* to be used in deferred parsing
*/
NODE **fields_arr; /* array of pointers to the field nodes */
int field0_valid = 1; /* $(>0) has not been changed yet */
NODE *field0;
static NODE **nodes; /* permanent repository of field nodes */
static int *FIELDWIDTHS = NULL;
void
init_fields()
{
emalloc(fields_arr, NODE **, sizeof(NODE *), "init_fields");
emalloc(nodes, NODE **, sizeof(NODE *), "init_fields");
emalloc(field0, NODE *, sizeof(NODE), "init_fields");
field0->type = Node_val;
field0->stref = 0;
field0->stptr = "";
field0->flags = (STRING|STR|PERM); /* never free buf */
fields_arr[0] = field0;
save_FS = dupnode(FS_node->var_value);
save_fs = save_FS->stptr;
}
static void
grow_fields_arr(num)
int num;
{
register int t;
register NODE *n;
erealloc(fields_arr, NODE **, (num + 1) * sizeof(NODE *), "set_field");
erealloc(nodes, NODE **, (num+1) * sizeof(NODE *), "set_field");
for (t = nf_high_water+1; t <= num; t++) {
getnode(n);
n->type = Node_val;
nodes[t] = n;
fields_arr[t] = nodes[t];
}
nf_high_water = num;
}
/*ARGSUSED*/
static void
set_field(num, str, len, dummy)
int num;
char *str;
int len;
NODE *dummy; /* not used -- just to make interface same as set_element */
{
register NODE *n;
register int t;
if (num > nf_high_water)
grow_fields_arr(num);
n = nodes[num];
n->stptr = str;
n->stlen = len;
n->flags = (PERM|STR|STRING|MAYBE_NUM);
fields_arr[num] = n;
}
/* Someone assigned a value to $(something). Fix up $0 to be right */
static void
rebuild_record()
{
register int tlen;
register NODE *tmp;
NODE *ofs;
char *ops;
register char *cops;
register NODE **ptr;
register int ofslen;
tlen = 0;
ofs = force_string(OFS_node->var_value);
ofslen = ofs->stlen;
ptr = &fields_arr[NF];
while (ptr > &fields_arr[0]) {
tmp = force_string(*ptr);
tlen += tmp->stlen;
ptr--;
}
tlen += (NF - 1) * ofslen;
emalloc(ops, char *, tlen + 2, "fix_fields");
cops = ops;
ops[0] = '\0';
for (ptr = &fields_arr[1]; ptr <= &fields_arr[NF]; ptr++) {
tmp = *ptr;
if (tmp->stlen == 1)
*cops++ = tmp->stptr[0];
else if (tmp->stlen != 0) {
memcpy(cops, tmp->stptr, tmp->stlen);
cops += tmp->stlen;
}
if (ptr != &fields_arr[NF]) {
if (ofslen == 1)
*cops++ = ofs->stptr[0];
else if (ofslen != 0) {
memcpy(cops, ofs->stptr, ofslen);
cops += ofslen;
}
}
}
tmp = make_str_node(ops, tlen, ALREADY_MALLOCED);
unref(fields_arr[0]);
fields_arr[0] = tmp;
field0_valid = 1;
}
/*
* setup $0, but defer parsing rest of line until reference is made to $(>0)
* or to NF. At that point, parse only as much as necessary.
*/
void
set_record(buf, cnt, freeold)
char *buf;
int cnt;
int freeold;
{
register int i;
NF = -1;
for (i = 1; i <= parse_high_water; i++) {
unref(fields_arr[i]);
}
parse_high_water = 0;
if (freeold) {
unref(fields_arr[0]);
if (resave_fs) {
resave_fs = 0;
unref(save_FS);
save_FS = dupnode(FS_node->var_value);
save_fs = save_FS->stptr;
}
field0->stptr = buf;
field0->stlen = cnt;
field0->stref = 1;
field0->flags = (STRING|STR|PERM|MAYBE_NUM);
fields_arr[0] = field0;
}
fields_arr[0]->flags |= MAYBE_NUM;
field0_valid = 1;
}
void
reset_record()
{
(void) force_string(fields_arr[0]);
set_record(fields_arr[0]->stptr, fields_arr[0]->stlen, 0);
}
void
set_NF()
{
NF = (int) force_number(NF_node->var_value);
field0_valid = 0;
}
/*
* this is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for when FS is a regular
* expression -- either user-defined or because RS=="" and FS==" "
*/
static int
re_parse_field(up_to, buf, len, fs, rp, set, n)
int up_to; /* parse only up to this field number */
char **buf; /* on input: string to parse; on output: point to start next */
int len;
register char *fs;
Regexp *rp;
void (*set) (); /* routine to set the value of the parsed field */
NODE *n;
{
register char *scan = *buf;
register int nf = parse_high_water;
register char *field;
register char *end = scan + len;
char *cp;
if (up_to == HUGE)
nf = 0;
if (len == 0)
return nf;
cp = FS_node->var_value->stptr;
if (*RS == 0 && *cp == ' ' && *(cp+1) == '\0') {
while (scan < end
&& (*scan == '\n' || *scan == ' ' || *scan == '\t'))
scan++;
}
field = scan;
while (scan < end
&& research(rp, scan, (int)(end - scan), 1) != -1
&& nf < up_to) {
if (REEND(rp, scan) == RESTART(rp, scan)) { /* null match */
scan++;
if (scan == end) {
(*set)(++nf, field, scan - field, n);
up_to = nf;
break;
}
continue;
}
(*set)(++nf, field, RESTART(rp, scan), n);
scan += REEND(rp, scan);
field = scan;
}
if (nf != up_to && *RS != 0 && scan < end) {
(*set)(++nf, scan, (int)(end - scan), n);
scan = end;
}
*buf = scan;
return (nf);
}
/*
* this is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for when FS is a single space
* character.
*/
static int
def_parse_field(up_to, buf, len, fs, rp, set, n)
int up_to; /* parse only up to this field number */
char **buf; /* on input: string to parse; on output: point to start next */
int len;
register char *fs;
Regexp *rp;
void (*set) (); /* routine to set the value of the parsed field */
NODE *n;
{
register char *scan = *buf;
register int nf = parse_high_water;
register char *field;
register char *end = scan + len;
if (up_to == HUGE)
nf = 0;
if (len == 0)
return nf;
*end = ' '; /* sentinel character */
for (; nf < up_to; scan++) {
/*
* special case: fs is single space, strip leading whitespace
*/
while (scan < end && (*scan == ' ' || *scan == '\t'))
scan++;
if (scan >= end)
break;
field = scan;
while (*scan != ' ' && *scan != '\t')
scan++;
(*set)(++nf, field, (int)(scan - field), n);
if (scan == end)
break;
}
*buf = scan;
return nf;
}
/*
* this is called both from get_field() and from do_split()
* via (*pase_field)(). This variation is for when FS is a single character
* other than space.
*/
static int
sc_parse_field(up_to, buf, len, fs, rp, set, n)
int up_to; /* parse only up to this field number */
char **buf; /* on input: string to parse; on output: point to start next */
int len;
register char *fs;
Regexp *rp;
void (*set) (); /* routine to set the value of the parsed field */
NODE *n;
{
register char *scan = *buf;
register char fschar = *fs;
register int nf = parse_high_water;
register char *field;
register char *end = scan + len;
if (up_to == HUGE)
nf = 0;
if (len == 0)
return nf;
*end = fschar; /* sentinel character */
for (; nf < up_to; scan++) {
field = scan;
while (*scan++ != fschar)
;
scan--;
(*set)(++nf, field, (int)(scan - field), n);
if (scan == end)
break;
}
*buf = scan;
return nf;
}
/*
* this is called both from get_field() and from do_split()
* via (*pase_field)(). This variation is for when FS is a single character
* other than space.
*/
static int
fw_parse_field(up_to, buf, len, fs, rp, set, n)
int up_to; /* parse only up to this field number */
char **buf; /* on input: string to parse; on output: point to start next */
int len;
register char *fs;
Regexp *rp;
void (*set) (); /* routine to set the value of the parsed field */
NODE *n;
{
register char *scan = *buf;
register int nf = parse_high_water;
register char *end = scan + len;
if (up_to == HUGE)
nf = 0;
if (len == 0)
return nf;
for (; nf < up_to && (len = FIELDWIDTHS[nf+1]) != -1; ) {
if (len > end - scan)
len = end - scan;
(*set)(++nf, scan, len, n);
scan += len;
}
if (len == -1)
*buf = end;
else
*buf = scan;
return nf;
}
NODE **
get_field(num, assign)
register int num;
Func_ptr *assign; /* this field is on the LHS of an assign */
{
int n;
/*
* if requesting whole line but some other field has been altered,
* then the whole line must be rebuilt
*/
if (num == 0) {
if (!field0_valid) {
/* first, parse remainder of input record */
if (NF == -1) {
NF = (*parse_field)(HUGE-1, &parse_extent,
fields_arr[0]->stlen -
(parse_extent - fields_arr[0]->stptr),
save_fs, FS_regexp, set_field,
(NODE *)NULL);
parse_high_water = NF;
}
rebuild_record();
}
if (assign)
*assign = reset_record;
return &fields_arr[0];
}
/* assert(num > 0); */
if (assign)
field0_valid = 0;
if (num <= parse_high_water) /* we have already parsed this field */
return &fields_arr[num];
if (parse_high_water == 0) /* starting at the beginning */
parse_extent = fields_arr[0]->stptr;
/*
* parse up to num fields, calling set_field() for each, and saving
* in parse_extent the point where the parse left off
*/
n = (*parse_field)(num, &parse_extent,
fields_arr[0]->stlen - (parse_extent-fields_arr[0]->stptr),
save_fs, FS_regexp, set_field, (NODE *)NULL);
parse_high_water = n;
if (num == HUGE-1)
num = n;
if (n < num) { /* requested field number beyond end of record; */
register int i;
if (num > nf_high_water)
grow_fields_arr(num);
/* fill in fields that don't exist */
for (i = n + 1; i <= num; i++)
fields_arr[i] = Nnull_string;
/*
* if this field is onthe LHS of an assignment, then we want to
* set NF to this value, below
*/
if (assign)
n = num;
}
/*
* if we reached the end of the record, set NF to the number of fields
* so far. Note that num might actually refer to a field that
* is beyond the end of the record, but we won't set NF to that value at
* this point, since this is only a reference to the field and NF
* only gets set if the field is assigned to -- in this case n has
* been set to num above
*/
if (parse_extent == fields_arr[0]->stptr + fields_arr[0]->stlen)
NF = n;
return &fields_arr[num];
}
static void
set_element(num, s, len, n)
int num;
char *s;
int len;
NODE *n;
{
register NODE *it;
it = make_string(s, len);
it->flags |= MAYBE_NUM;
*assoc_lookup(n, tmp_number((AWKNUM) (num))) = it;
}
NODE *
do_split(tree)
NODE *tree;
{
NODE *t1, *t2, *t3, *tmp;
register char *splitc = "";
char *s;
int (*parseit)();
Regexp *rp = NULL;
t1 = tree_eval(tree->lnode);
t2 = tree->rnode->lnode;
t3 = tree->rnode->rnode->lnode;
(void) force_string(t1);
if (t2->type == Node_param_list)
t2 = stack_ptr[t2->param_cnt];
if (t2->type != Node_var && t2->type != Node_var_array)
fatal("second argument of split is not a variable");
assoc_clear(t2);
if (t3->re_flags & FS_DFLT) {
parseit = parse_field;
splitc = FS;
rp = FS_regexp;
} else {
tmp = force_string(tree_eval(t3->re_exp));
if (tmp->stlen == 1) {
if (tmp->stptr[0] == ' ') {
parseit = def_parse_field;
} else {
parseit = sc_parse_field;
splitc = tmp->stptr;
}
} else {
parseit = re_parse_field;
rp = re_update(t3);
}
free_temp(tmp);
}
s = t1->stptr;
tmp = tmp_number((AWKNUM) (*parseit)(HUGE, &s, t1->stlen,
splitc, rp, set_element, t2));
free_temp(t1);
return tmp;
}
void
set_FS()
{
register NODE *tmp;
static char buf[10];
if (FS_regexp) {
refree(FS_regexp);
FS_regexp = NULL;
}
parse_field = def_parse_field;
tmp = force_string(FS_node->var_value);
FS = tmp->stptr;
if (*RS == 0) {
parse_field = re_parse_field;
FS = buf;
if (tmp->stlen == 1) {
if (tmp->stptr[0] == ' ')
(void) strcpy(buf, "[ \n]+");
else if (tmp->stptr[0] != '\n')
sprintf(buf, "[%c\n]", tmp->stptr[0]);
else {
parse_field = sc_parse_field;
FS = tmp->stptr;
}
} else if (tmp->stlen == 0) {
buf[0] = '\n';
buf[1] = '\0';
parse_field = sc_parse_field;
} else
FS = tmp->stptr;
} else {
if (tmp->stlen > 1)
parse_field = re_parse_field;
else if (*FS != ' ' && tmp->stlen == 1)
parse_field = sc_parse_field;
}
if (parse_field == re_parse_field) {
tmp = tmp_string(FS, strlen(FS));
FS_regexp = make_regexp(tmp, 0, 1);
free_temp(tmp);
} else
FS_regexp = NULL;
resave_fs = 1;
}
void
set_RS()
{
(void) force_string(RS_node->var_value);
RS = RS_node->var_value->stptr;
set_FS();
}
void
set_FIELDWIDTHS()
{
register char *scan;
char *end;
register int i;
static int fw_alloc = 1;
static int warned = 0;
if (do_lint && ! warned) {
warned = 1;
warning("use of FIELDWIDTHS is a gawk extension");
}
if (strict) /* quick and dirty, does the trick */
return;
parse_field = fw_parse_field;
scan = force_string(FIELDWIDTHS_node->var_value)->stptr;
end = scan + 1;
if (FIELDWIDTHS == NULL)
emalloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS");
FIELDWIDTHS[0] = 0;
for (i = 1; ; i++) {
if (i >= fw_alloc) {
fw_alloc *= 2;
erealloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS");
}
FIELDWIDTHS[i] = (int) strtol(scan, &end, 10);
if (end == scan)
break;
scan = end;
}
FIELDWIDTHS[i] = -1;
}