summaryrefslogtreecommitdiffstats
path: root/contrib/awk/field.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/awk/field.c')
-rw-r--r--contrib/awk/field.c915
1 files changed, 915 insertions, 0 deletions
diff --git a/contrib/awk/field.c b/contrib/awk/field.c
new file mode 100644
index 0000000..31c9628
--- /dev/null
+++ b/contrib/awk/field.c
@@ -0,0 +1,915 @@
+/*
+ * field.c - routines for dealing with fields and record parsing
+ */
+
+/*
+ * Copyright (C) 1986, 1988, 1989, 1991-1997 the Free Software Foundation, Inc.
+ *
+ * This file is part of GAWK, the GNU implementation of the
+ * AWK Programming 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 2 of the License, 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "awk.h"
+#include <assert.h>
+
+typedef void (* Setfunc) P((long, char *, long, NODE *));
+
+static long (*parse_field) P((long, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static void rebuild_record P((void));
+static long re_parse_field P((long, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static long def_parse_field P((long, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static long posix_def_parse_field P((long, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static long null_parse_field P((long, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static long sc_parse_field P((long, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static long fw_parse_field P((long, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+static void set_element P((long num, char * str, long len, NODE *arr));
+static void grow_fields_arr P((long num));
+static void set_field P((long num, char *str, long len, NODE *dummy));
+
+
+static char *parse_extent; /* marks where to restart parse of record */
+static long parse_high_water = 0; /* field number that we have parsed so far */
+static long nf_high_water = 0; /* size of fields_arr */
+static int resave_fs;
+static NODE *save_FS; /* save current value of FS when line is read,
+ * to be used in deferred parsing
+ */
+static int *FIELDWIDTHS = NULL;
+
+NODE **fields_arr; /* array of pointers to the field nodes */
+int field0_valid; /* $(>0) has not been changed yet */
+int default_FS; /* TRUE when FS == " " */
+Regexp *FS_regexp = NULL;
+static NODE *Null_field = NULL;
+
+/* init_fields --- set up the fields array to start with */
+
+void
+init_fields()
+{
+ NODE *n;
+
+ emalloc(fields_arr, NODE **, sizeof(NODE *), "init_fields");
+ getnode(n);
+ *n = *Nnull_string;
+ n->flags |= (SCALAR|FIELD);
+ n->flags &= ~PERM;
+ fields_arr[0] = n;
+ parse_extent = fields_arr[0]->stptr;
+ save_FS = dupnode(FS_node->var_value);
+ getnode(Null_field);
+ *Null_field = *Nnull_string;
+ Null_field->flags |= (SCALAR|FIELD);
+ Null_field->flags &= ~(NUM|NUMBER|MAYBE_NUM|PERM);
+ field0_valid = TRUE;
+}
+
+/* grow_fields --- acquire new fields as needed */
+
+static void
+grow_fields_arr(num)
+long num;
+{
+ register int t;
+ register NODE *n;
+
+ erealloc(fields_arr, NODE **, (num + 1) * sizeof(NODE *), "grow_fields_arr");
+ for (t = nf_high_water + 1; t <= num; t++) {
+ getnode(n);
+ *n = *Null_field;
+ fields_arr[t] = n;
+ }
+ nf_high_water = num;
+}
+
+/* set_field --- set the value of a particular field */
+
+/*ARGSUSED*/
+static void
+set_field(num, str, len, dummy)
+long num;
+char *str;
+long len;
+NODE *dummy; /* not used -- just to make interface same as set_element */
+{
+ register NODE *n;
+
+ if (num > nf_high_water)
+ grow_fields_arr(num);
+ n = fields_arr[num];
+ n->stptr = str;
+ n->stlen = len;
+ n->flags = (STR|STRING|MAYBE_NUM|SCALAR|FIELD);
+}
+
+/* rebuild_record --- Someone assigned a value to $(something).
+ Fix up $0 to be right */
+
+static void
+rebuild_record()
+{
+ /*
+ * use explicit unsigned longs for lengths, in case
+ * a size_t isn't big enough.
+ */
+ register unsigned long tlen;
+ register unsigned long ofslen;
+ register NODE *tmp;
+ NODE *ofs;
+ char *ops;
+ register char *cops;
+ long i;
+ char *f0start, *f0end;
+
+ assert(NF != -1);
+
+ tlen = 0;
+ ofs = force_string(OFS_node->var_value);
+ ofslen = ofs->stlen;
+ for (i = NF; i > 0; i--) {
+ tmp = fields_arr[i];
+ tmp = force_string(tmp);
+ tlen += tmp->stlen;
+ }
+ tlen += (NF - 1) * ofslen;
+ if ((long) tlen < 0)
+ tlen = 0;
+ emalloc(ops, char *, tlen + 2, "rebuild_record");
+ cops = ops;
+ ops[0] = '\0';
+ for (i = 1; i <= NF; i++) {
+ tmp = fields_arr[i];
+ /* copy field */
+ if (tmp->stlen == 1)
+ *cops++ = tmp->stptr[0];
+ else if (tmp->stlen != 0) {
+ memcpy(cops, tmp->stptr, tmp->stlen);
+ cops += tmp->stlen;
+ }
+ /* copy OFS */
+ if (i != 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);
+
+ /*
+ * Since we are about to unref fields_arr[0], we want to find
+ * any fields that still point into it, and have them point
+ * into the new field zero.
+ */
+ f0start = fields_arr[0]->stptr;
+ f0end = fields_arr[0]->stptr + fields_arr[0]->stlen;
+ for (cops = ops, i = 1; i <= NF; i++) {
+ char *field_data = fields_arr[i]->stptr;
+
+ if (fields_arr[i]->stlen > 0
+ && f0start <= field_data && field_data < f0end)
+ fields_arr[i]->stptr = cops;
+
+ cops += fields_arr[i]->stlen + ofslen;
+ }
+
+ unref(fields_arr[0]);
+
+ fields_arr[0] = tmp;
+ field0_valid = TRUE;
+}
+
+/*
+ * set_record:
+ * 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; /* ignored if ! freeold */
+int cnt; /* ignored if ! freeold */
+int freeold;
+{
+ register int i;
+ NODE *n;
+
+ NF = -1;
+ for (i = 1; i <= parse_high_water; i++) {
+ unref(fields_arr[i]);
+ getnode(n);
+ *n = *Null_field;
+ fields_arr[i] = n;
+ }
+
+ parse_high_water = 0;
+ /*
+ * $0 = $0 should resplit using the current value of FS, thus,
+ * this is executed orthogonally to the value of freeold.
+ */
+ if (resave_fs) {
+ resave_fs = FALSE;
+ unref(save_FS);
+ save_FS = dupnode(FS_node->var_value);
+ }
+ if (freeold) {
+ unref(fields_arr[0]);
+ getnode(n);
+ n->stptr = buf;
+ n->stlen = cnt;
+ n->stref = 1;
+ n->type = Node_val;
+ n->stfmt = -1;
+ n->flags = (STRING|STR|MAYBE_NUM|SCALAR|FIELD);
+ fields_arr[0] = n;
+ }
+ fields_arr[0]->flags |= MAYBE_NUM;
+ field0_valid = TRUE;
+}
+
+/* reset_record --- start over again with current $0 */
+
+void
+reset_record()
+{
+ (void) force_string(fields_arr[0]);
+ set_record(fields_arr[0]->stptr, fields_arr[0]->stlen, FALSE);
+}
+
+/* set_NF --- handle what happens to $0 and fields when NF is changed */
+
+void
+set_NF()
+{
+ register int i;
+ NODE *n;
+
+ assert(NF != -1);
+
+ NF = (long) force_number(NF_node->var_value);
+ if (NF > nf_high_water)
+ grow_fields_arr(NF);
+ if (parse_high_water < NF) {
+ for (i = parse_high_water + 1; i <= NF; i++) {
+ unref(fields_arr[i]);
+ getnode(n);
+ *n = *Null_field;
+ fields_arr[i] = n;
+ }
+ } else if (parse_high_water > 0) {
+ for (i = NF + 1; i <= parse_high_water; i++) {
+ unref(fields_arr[i]);
+ getnode(n);
+ *n = *Null_field;
+ fields_arr[i] = n;
+ }
+ parse_high_water = NF;
+ }
+ field0_valid = FALSE;
+}
+
+/*
+ * re_parse_field --- parse fields using a regexp.
+ *
+ * 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 long
+re_parse_field(up_to, buf, len, fs, rp, set, n)
+long up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register long nf = parse_high_water;
+ register char *field;
+ register char *end = scan + len;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+
+ if (RS_is_null && default_FS)
+ while (scan < end && (*scan == ' ' || *scan == '\t' || *scan == '\n'))
+ scan++;
+ field = scan;
+ while (scan < end
+ && research(rp, scan, 0, (end - scan), TRUE) != -1
+ && nf < up_to) {
+ if (REEND(rp, scan) == RESTART(rp, scan)) { /* null match */
+ scan++;
+ if (scan == end) {
+ (*set)(++nf, field, (long)(scan - field), n);
+ up_to = nf;
+ break;
+ }
+ continue;
+ }
+ (*set)(++nf, field,
+ (long)(scan + RESTART(rp, scan) - field), n);
+ scan += REEND(rp, scan);
+ field = scan;
+ if (scan == end) /* FS at end of record */
+ (*set)(++nf, field, 0L, n);
+ }
+ if (nf != up_to && scan < end) {
+ (*set)(++nf, scan, (long)(end - scan), n);
+ scan = end;
+ }
+ *buf = scan;
+ return (nf);
+}
+
+/*
+ * def_parse_field --- default field parsing.
+ *
+ * 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 long
+def_parse_field(up_to, buf, len, fs, rp, set, n)
+long up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register long nf = parse_high_water;
+ register char *field;
+ register char *end = scan + len;
+ char sav;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+
+ /*
+ * Nasty special case. If FS set to "", return whole record
+ * as first field. This is not worth a separate function.
+ */
+ if (fs->stlen == 0) {
+ (*set)(++nf, *buf, len, n);
+ *buf += len;
+ return nf;
+ }
+
+ /* before doing anything save the char at *end */
+ sav = *end;
+ /* because it will be destroyed now: */
+
+ *end = ' '; /* sentinel character */
+ for (; nf < up_to; scan++) {
+ /*
+ * special case: fs is single space, strip leading whitespace
+ */
+ while (scan < end && (*scan == ' ' || *scan == '\t' || *scan == '\n'))
+ scan++;
+ if (scan >= end)
+ break;
+ field = scan;
+ while (*scan != ' ' && *scan != '\t' && *scan != '\n')
+ scan++;
+ (*set)(++nf, field, (long)(scan - field), n);
+ if (scan == end)
+ break;
+ }
+
+ /* everything done, restore original char at *end */
+ *end = sav;
+
+ *buf = scan;
+ return nf;
+}
+
+/*
+ * posix_def_parse_field --- default field parsing.
+ *
+ * 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. The only difference between this and def_parse_field()
+ * is that this one does not allow newlines to separate fields.
+ */
+
+static long
+posix_def_parse_field(up_to, buf, len, fs, rp, set, n)
+long up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register long nf = parse_high_water;
+ register char *field;
+ register char *end = scan + len;
+ char sav;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+
+ /*
+ * Nasty special case. If FS set to "", return whole record
+ * as first field. This is not worth a separate function.
+ */
+ if (fs->stlen == 0) {
+ (*set)(++nf, *buf, len, n);
+ *buf += len;
+ return nf;
+ }
+
+ /* before doing anything save the char at *end */
+ sav = *end;
+ /* because it will be destroyed now: */
+
+ *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, (long)(scan - field), n);
+ if (scan == end)
+ break;
+ }
+
+ /* everything done, restore original char at *end */
+ *end = sav;
+
+ *buf = scan;
+ return nf;
+}
+
+/*
+ * null_parse_field --- each character is a separate field
+ *
+ * This is called both from get_field() and from do_split()
+ * via (*parse_field)(). This variation is for when FS is the null string.
+ */
+static long
+null_parse_field(up_to, buf, len, fs, rp, set, n)
+long up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register long nf = parse_high_water;
+ register char *end = scan + len;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+
+ for (; nf < up_to && scan < end; scan++)
+ (*set)(++nf, scan, 1L, n);
+
+ *buf = scan;
+ return nf;
+}
+
+/*
+ * sc_parse_field --- single character field separator
+ *
+ * This is called both from get_field() and from do_split()
+ * via (*parse_field)(). This variation is for when FS is a single character
+ * other than space.
+ */
+static long
+sc_parse_field(up_to, buf, len, fs, rp, set, n)
+long up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register char fschar;
+ register long nf = parse_high_water;
+ register char *field;
+ register char *end = scan + len;
+ int onecase;
+ char sav;
+
+ if (up_to == HUGE)
+ nf = 0;
+ if (len == 0)
+ return nf;
+
+ if (RS_is_null && fs->stlen == 0)
+ fschar = '\n';
+ else
+ fschar = fs->stptr[0];
+
+ onecase = (IGNORECASE && isalpha(fschar));
+ if (onecase)
+ fschar = casetable[(int) fschar];
+
+ /* before doing anything save the char at *end */
+ sav = *end;
+ /* because it will be destroyed now: */
+ *end = fschar; /* sentinel character */
+
+ for (; nf < up_to;) {
+ field = scan;
+ if (onecase) {
+ while (casetable[(int) *scan] != fschar)
+ scan++;
+ } else {
+ while (*scan != fschar)
+ scan++;
+ }
+ (*set)(++nf, field, (long)(scan - field), n);
+ if (scan == end)
+ break;
+ scan++;
+ if (scan == end) { /* FS at end of record */
+ (*set)(++nf, field, 0L, n);
+ break;
+ }
+ }
+
+ /* everything done, restore original char at *end */
+ *end = sav;
+
+ *buf = scan;
+ return nf;
+}
+
+/*
+ * fw_parse_field --- field parsing using FIELDWIDTHS spec
+ *
+ * This is called both from get_field() and from do_split()
+ * via (*parse_field)(). This variation is for fields are fixed widths.
+ */
+static long
+fw_parse_field(up_to, buf, len, fs, rp, set, n)
+long up_to; /* parse only up to this field number */
+char **buf; /* on input: string to parse; on output: point to start next */
+int len;
+NODE *fs;
+Regexp *rp;
+Setfunc set; /* routine to set the value of the parsed field */
+NODE *n;
+{
+ register char *scan = *buf;
+ register long 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, (long) len, n);
+ scan += len;
+ }
+ if (len == -1)
+ *buf = end;
+ else
+ *buf = scan;
+ return nf;
+}
+
+/* get_field --- return a particular $n */
+
+NODE **
+get_field(requested, assign)
+register long requested;
+Func_ptr *assign; /* this field is on the LHS of an assign */
+{
+ /*
+ * if requesting whole line but some other field has been altered,
+ * then the whole line must be rebuilt
+ */
+ if (requested == 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 != NULL)
+ *assign = reset_record;
+ return &fields_arr[0];
+ }
+
+ /* assert(requested > 0); */
+
+ if (assign != NULL)
+ field0_valid = FALSE; /* $0 needs reconstruction */
+
+ if (requested <= parse_high_water) /* already parsed this field */
+ return &fields_arr[requested];
+
+ if (NF == -1) { /* have not yet parsed to end of record */
+ /*
+ * parse up to requested fields, calling set_field() for each,
+ * saving in parse_extent the point where the parse left off
+ */
+ if (parse_high_water == 0) /* starting at the beginning */
+ parse_extent = fields_arr[0]->stptr;
+ parse_high_water = (*parse_field)(requested, &parse_extent,
+ fields_arr[0]->stlen - (parse_extent - fields_arr[0]->stptr),
+ save_FS, FS_regexp, set_field, (NODE *) NULL);
+
+ /*
+ * if we reached the end of the record, set NF to the number of
+ * fields so far. Note that requested 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 -- this case is handled below
+ */
+ if (parse_extent == fields_arr[0]->stptr + fields_arr[0]->stlen)
+ NF = parse_high_water;
+ if (requested == HUGE-1) /* HUGE-1 means set NF */
+ requested = parse_high_water;
+ }
+ if (parse_high_water < requested) { /* requested beyond end of record */
+ if (assign != NULL) { /* expand record */
+ if (requested > nf_high_water)
+ grow_fields_arr(requested);
+
+ NF = requested;
+ parse_high_water = requested;
+ } else
+ return &Null_field;
+ }
+
+ return &fields_arr[requested];
+}
+
+/* set_element --- set an array element, used by do_split() */
+
+static void
+set_element(num, s, len, n)
+long num;
+char *s;
+long len;
+NODE *n;
+{
+ register NODE *it;
+
+ it = make_string(s, len);
+ it->flags |= MAYBE_NUM;
+ *assoc_lookup(n, tmp_number((AWKNUM) (num))) = it;
+}
+
+/* do_split --- implement split(), semantics are same as for field splitting */
+
+NODE *
+do_split(tree)
+NODE *tree;
+{
+ NODE *src, *arr, *sep, *tmp;
+ NODE *fs;
+ char *s;
+ long (*parseit) P((long, char **, int, NODE *,
+ Regexp *, Setfunc, NODE *));
+ Regexp *rp = NULL;
+
+ /*
+ * do dupnode(), to avoid problems like
+ * x = split(a[1], a, "blah")
+ * since we assoc_clear the array. gack.
+ * this also gives us complete call by value semantics.
+ */
+ tmp = tree_eval(tree->lnode);
+ src = dupnode(tmp);
+ free_temp(tmp);
+
+ arr = tree->rnode->lnode;
+ if (tree->rnode->rnode != NULL)
+ sep = tree->rnode->rnode->lnode; /* 3rd arg */
+ else
+ sep = NULL;
+
+ (void) force_string(src);
+
+ if (arr->type == Node_param_list)
+ arr = stack_ptr[arr->param_cnt];
+ if (arr->type != Node_var && arr->type != Node_var_array)
+ fatal("second argument of split is not an array");
+ arr->type = Node_var_array;
+ assoc_clear(arr);
+
+ if (sep->re_flags & FS_DFLT) {
+ parseit = parse_field;
+ fs = force_string(FS_node->var_value);
+ rp = FS_regexp;
+ } else {
+ tmp = force_string(tree_eval(sep->re_exp));
+ if (tmp->stlen == 0)
+ parseit = null_parse_field;
+ else if (tmp->stlen == 1 && (sep->re_flags & CONST) == 0) {
+ if (tmp->stptr[0] == ' ') {
+ if (do_posix)
+ parseit = posix_def_parse_field;
+ else
+ parseit = def_parse_field;
+ } else
+ parseit = sc_parse_field;
+ } else {
+ parseit = re_parse_field;
+ rp = re_update(sep);
+ }
+ fs = tmp;
+ }
+
+ s = src->stptr;
+ tmp = tmp_number((AWKNUM) (*parseit)(HUGE, &s, (int) src->stlen,
+ fs, rp, set_element, arr));
+ unref(src);
+ free_temp(sep);
+ return tmp;
+}
+
+/* set_FIELDWIDTHS --- handle an assignment to FIELDWIDTHS */
+
+void
+set_FIELDWIDTHS()
+{
+ register char *scan;
+ char *end;
+ register int i;
+ static int fw_alloc = 1;
+ static int warned = FALSE;
+ extern double strtod();
+
+ if (do_lint && ! warned) {
+ warned = TRUE;
+ warning("use of FIELDWIDTHS is a gawk extension");
+ }
+ if (do_traditional) /* quick and dirty, does the trick */
+ return;
+
+ /*
+ * If changing the way fields are split, obey least-suprise
+ * semantics, and force $0 to be split totally.
+ */
+ if (fields_arr != NULL)
+ (void) get_field(HUGE - 1, 0);
+
+ 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) strtod(scan, &end);
+ if (end == scan)
+ break;
+ scan = end;
+ }
+ FIELDWIDTHS[i] = -1;
+}
+
+void
+set_FS_if_not_FIELDWIDTHS()
+{
+ if (parse_field != fw_parse_field)
+ set_FS();
+}
+
+/* set_FS --- handle things when FS is assigned to */
+
+void
+set_FS()
+{
+ char buf[10];
+ NODE *fs;
+ static NODE *save_fs = NULL;
+ static NODE *save_rs = NULL;
+
+ /*
+ * If changing the way fields are split, obey least-suprise
+ * semantics, and force $0 to be split totally.
+ */
+ if (fields_arr != NULL)
+ (void) get_field(HUGE - 1, 0);
+
+ if (save_fs && cmp_nodes(FS_node->var_value, save_fs) == 0
+ && save_rs && cmp_nodes(RS_node->var_value, save_rs) == 0)
+ return;
+ unref(save_fs);
+ save_fs = dupnode(FS_node->var_value);
+ unref(save_rs);
+ save_rs = dupnode(RS_node->var_value);
+ resave_fs = TRUE;
+ buf[0] = '\0';
+ default_FS = FALSE;
+ if (FS_regexp) {
+ refree(FS_regexp);
+ FS_regexp = NULL;
+ }
+ fs = force_string(FS_node->var_value);
+ if (! do_traditional && fs->stlen == 0)
+ parse_field = null_parse_field;
+ else if (fs->stlen > 1)
+ parse_field = re_parse_field;
+ else if (RS_is_null) {
+ parse_field = sc_parse_field;
+ if (fs->stlen == 1) {
+ if (fs->stptr[0] == ' ') {
+ default_FS = TRUE;
+ strcpy(buf, "[ \t\n]+");
+ } else if (fs->stptr[0] != '\n')
+ sprintf(buf, "[%c\n]", fs->stptr[0]);
+ }
+ } else {
+ if (do_posix)
+ parse_field = posix_def_parse_field;
+ else
+ parse_field = def_parse_field;
+ if (fs->stptr[0] == ' ' && fs->stlen == 1)
+ default_FS = TRUE;
+ else if (fs->stptr[0] != ' ' && fs->stlen == 1) {
+ if (! IGNORECASE)
+ parse_field = sc_parse_field;
+ else if (fs->stptr[0] == '\\')
+ /* yet another special case */
+ strcpy(buf, "[\\\\]");
+ else
+ sprintf(buf, "[%c]", fs->stptr[0]);
+ }
+ }
+ if (buf[0] != '\0') {
+ FS_regexp = make_regexp(buf, strlen(buf), IGNORECASE, TRUE);
+ parse_field = re_parse_field;
+ } else if (parse_field == re_parse_field) {
+ FS_regexp = make_regexp(fs->stptr, fs->stlen, IGNORECASE, TRUE);
+ } else
+ FS_regexp = NULL;
+}
+
+/* using_fieldwidths --- is FS or FIELDWIDTHS in use? */
+
+int
+using_fieldwidths()
+{
+ return parse_field == fw_parse_field;
+}
+
OpenPOWER on IntegriCloud