summaryrefslogtreecommitdiffstats
path: root/contrib/awk/node.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/awk/node.c')
-rw-r--r--contrib/awk/node.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/contrib/awk/node.c b/contrib/awk/node.c
new file mode 100644
index 0000000..6f10b9f
--- /dev/null
+++ b/contrib/awk/node.c
@@ -0,0 +1,515 @@
+/*
+ * node.c -- routines for node management
+ */
+
+/*
+ * 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"
+
+/* r_force_number --- force a value to be numeric */
+
+AWKNUM
+r_force_number(n)
+register NODE *n;
+{
+ register char *cp;
+ register char *cpend;
+ char save;
+ char *ptr;
+ unsigned int newflags;
+
+#ifdef DEBUG
+ if (n == NULL)
+ cant_happen();
+ if (n->type != Node_val)
+ cant_happen();
+ if(n->flags == 0)
+ cant_happen();
+ if (n->flags & NUM)
+ return n->numbr;
+#endif
+
+ /* all the conditionals are an attempt to avoid the expensive strtod */
+
+ n->numbr = 0.0;
+ n->flags |= NUM;
+
+ if (n->stlen == 0)
+ return 0.0;
+
+ cp = n->stptr;
+ if (ISALPHA(*cp))
+ return 0.0;
+
+ cpend = cp + n->stlen;
+ while (cp < cpend && isspace(*cp))
+ cp++;
+ if (cp == cpend || isalpha(*cp))
+ return 0.0;
+
+ if (n->flags & MAYBE_NUM) {
+ newflags = NUMBER;
+ n->flags &= ~MAYBE_NUM;
+ } else
+ newflags = 0;
+ if (cpend - cp == 1) {
+ if (ISDIGIT(*cp)) {
+ n->numbr = (AWKNUM)(*cp - '0');
+ n->flags |= newflags;
+ }
+ return n->numbr;
+ }
+
+#ifdef NONDECDATA
+ errno = 0;
+ if (! do_traditional && isnondecimal(cp)) {
+ n->numbr = nondec2awknum(cp, cpend - cp);
+ goto finish;
+ }
+#endif /* NONDECDATA */
+
+ errno = 0;
+ save = *cpend;
+ *cpend = '\0';
+ n->numbr = (AWKNUM) strtod((const char *) cp, &ptr);
+
+ /* POSIX says trailing space is OK for NUMBER */
+ while (ISSPACE(*ptr))
+ ptr++;
+ *cpend = save;
+finish:
+ /* the >= should be ==, but for SunOS 3.5 strtod() */
+ if (errno == 0 && ptr >= cpend)
+ n->flags |= newflags;
+ else
+ errno = 0;
+
+ return n->numbr;
+}
+
+/*
+ * the following lookup table is used as an optimization in force_string
+ * (more complicated) variations on this theme didn't seem to pay off, but
+ * systematic testing might be in order at some point
+ */
+static const char *values[] = {
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+};
+#define NVAL (sizeof(values)/sizeof(values[0]))
+
+/* format_val --- format a numeric value based on format */
+
+NODE *
+format_val(format, index, s)
+char *format;
+int index;
+register NODE *s;
+{
+ char buf[128];
+ register char *sp = buf;
+ double val;
+
+ /* not an integral value, or out of range */
+ if ((val = double_to_int(s->numbr)) != s->numbr
+ || val < LONG_MIN || val > LONG_MAX) {
+#ifdef GFMT_WORKAROUND
+ NODE *dummy, *r;
+ unsigned short oflags;
+ extern NODE *format_tree P((const char *, int, NODE *));
+ extern NODE **fmt_list; /* declared in eval.c */
+
+ /* create dummy node for a sole use of format_tree */
+ getnode(dummy);
+ dummy->lnode = s;
+ dummy->rnode = NULL;
+ oflags = s->flags;
+ s->flags |= PERM; /* prevent from freeing by format_tree() */
+ r = format_tree(format, fmt_list[index]->stlen, dummy);
+ s->flags = oflags;
+ s->stfmt = (char) index;
+ s->stlen = r->stlen;
+ s->stptr = r->stptr;
+ freenode(r); /* Do not free_temp(r)! We want */
+ freenode(dummy); /* to keep s->stptr == r->stpr. */
+
+ goto no_malloc;
+#else
+ /*
+ * no need for a "replacement" formatting by gawk,
+ * just use sprintf
+ */
+ sprintf(sp, format, s->numbr);
+ s->stlen = strlen(sp);
+ s->stfmt = (char) index;
+#endif /* GFMT_WORKAROUND */
+ } else {
+ /* integral value */
+ /* force conversion to long only once */
+ register long num = (long) val;
+ if (num < NVAL && num >= 0) {
+ sp = (char *) values[num];
+ s->stlen = 1;
+ } else {
+ (void) sprintf(sp, "%ld", num);
+ s->stlen = strlen(sp);
+ }
+ s->stfmt = -1;
+ }
+ emalloc(s->stptr, char *, s->stlen + 2, "force_string");
+ memcpy(s->stptr, sp, s->stlen+1);
+#ifdef GFMT_WORKAROUND
+no_malloc:
+#endif /* GFMT_WORKAROUND */
+ s->stref = 1;
+ s->flags |= STR;
+ return s;
+}
+
+/* r_force_string --- force a value to be a string */
+
+NODE *
+r_force_string(s)
+register NODE *s;
+{
+#ifdef DEBUG
+ if (s == NULL)
+ cant_happen();
+ if (s->type != Node_val)
+ cant_happen();
+ if ((s->flags & NUM) == 0)
+ cant_happen();
+ if (s->stref <= 0)
+ cant_happen();
+ if ((s->flags & STR) != 0
+ && (s->stfmt == -1 || s->stfmt == CONVFMTidx))
+ return s;
+#endif
+
+ return format_val(CONVFMT, CONVFMTidx, s);
+}
+
+/*
+ * dupnode:
+ * Duplicate a node. (For strings, "duplicate" means crank up the
+ * reference count.)
+ */
+
+NODE *
+dupnode(n)
+NODE *n;
+{
+ register NODE *r;
+
+ if ((n->flags & TEMP) != 0) {
+ n->flags &= ~TEMP;
+ n->flags |= MALLOC;
+ return n;
+ }
+ if ((n->flags & (MALLOC|STR)) == (MALLOC|STR)) {
+ if (n->stref < LONG_MAX)
+ n->stref++;
+ return n;
+ }
+ getnode(r);
+ *r = *n;
+ r->flags &= ~(PERM|TEMP);
+ r->flags |= MALLOC;
+ if (n->type == Node_val && (n->flags & STR) != 0) {
+ r->stref = 1;
+ emalloc(r->stptr, char *, r->stlen + 2, "dupnode");
+ memcpy(r->stptr, n->stptr, r->stlen);
+ r->stptr[r->stlen] = '\0';
+ }
+ return r;
+}
+
+/* mk_number --- allocate a node with defined number */
+
+NODE *
+mk_number(x, flags)
+AWKNUM x;
+unsigned int flags;
+{
+ register NODE *r;
+
+ getnode(r);
+ r->type = Node_val;
+ r->numbr = x;
+ r->flags = flags | SCALAR;
+#ifdef DEBUG
+ r->stref = 1;
+ r->stptr = NULL;
+ r->stlen = 0;
+#endif
+ return r;
+}
+
+/* make_str_node --- make a string node */
+
+NODE *
+make_str_node(s, len, flags)
+char *s;
+size_t len;
+int flags;
+{
+ register NODE *r;
+
+ getnode(r);
+ r->type = Node_val;
+ r->flags = (STRING|STR|MALLOC|SCALAR);
+ if (flags & ALREADY_MALLOCED)
+ r->stptr = s;
+ else {
+ emalloc(r->stptr, char *, len + 2, s);
+ memcpy(r->stptr, s, len);
+ }
+ r->stptr[len] = '\0';
+
+ if ((flags & SCAN) != 0) { /* scan for escape sequences */
+ char *pf;
+ register char *ptm;
+ register int c;
+ register char *end;
+
+ end = &(r->stptr[len]);
+ for (pf = ptm = r->stptr; pf < end;) {
+ c = *pf++;
+ if (c == '\\') {
+ c = parse_escape(&pf);
+ if (c < 0) {
+ if (do_lint)
+ warning("backslash at end of string");
+ c = '\\';
+ }
+ *ptm++ = c;
+ } else
+ *ptm++ = c;
+ }
+ len = ptm - r->stptr;
+ erealloc(r->stptr, char *, len + 1, "make_str_node");
+ r->stptr[len] = '\0';
+ r->flags |= PERM;
+ }
+ r->stlen = len;
+ r->stref = 1;
+ r->stfmt = -1;
+
+ return r;
+}
+
+/* tmp_string --- allocate a temporary string */
+
+NODE *
+tmp_string(s, len)
+char *s;
+size_t len;
+{
+ register NODE *r;
+
+ r = make_string(s, len);
+ r->flags |= TEMP;
+ return r;
+}
+
+/* more_nodes --- allocate more nodes */
+
+#define NODECHUNK 100
+
+NODE *nextfree = NULL;
+
+NODE *
+more_nodes()
+{
+ register NODE *np;
+
+ /* get more nodes and initialize list */
+ emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "newnode");
+ for (np = nextfree; np <= &nextfree[NODECHUNK - 1]; np++) {
+ np->flags = 0;
+ np->nextp = np + 1;
+ }
+ --np;
+ np->nextp = NULL;
+ np = nextfree;
+ nextfree = nextfree->nextp;
+ return np;
+}
+
+#ifdef DEBUG
+/* freenode --- release a node back to the pool */
+
+void
+freenode(it)
+NODE *it;
+{
+ it->flags &= ~SCALAR;
+#ifdef MPROF
+ it->stref = 0;
+ free((char *) it);
+#else /* not MPROF */
+ /* add it to head of freelist */
+ it->nextp = nextfree;
+ nextfree = it;
+#endif /* not MPROF */
+}
+#endif /* DEBUG */
+
+/* unref --- remove reference to a particular node */
+
+void
+unref(tmp)
+register NODE *tmp;
+{
+ if (tmp == NULL)
+ return;
+ if ((tmp->flags & PERM) != 0)
+ return;
+ if ((tmp->flags & (MALLOC|TEMP)) != 0) {
+ tmp->flags &= ~TEMP;
+ if ((tmp->flags & STR) != 0) {
+ if (tmp->stref > 1) {
+ if (tmp->stref != LONG_MAX)
+ tmp->stref--;
+ return;
+ }
+ free(tmp->stptr);
+ }
+ freenode(tmp);
+ return;
+ }
+ if ((tmp->flags & FIELD) != 0) {
+ freenode(tmp);
+ return;
+ }
+}
+
+/*
+ * parse_escape:
+ *
+ * Parse a C escape sequence. STRING_PTR points to a variable containing a
+ * pointer to the string to parse. That pointer is updated past the
+ * characters we use. The value of the escape sequence is returned.
+ *
+ * A negative value means the sequence \ newline was seen, which is supposed to
+ * be equivalent to nothing at all.
+ *
+ * If \ is followed by a null character, we return a negative value and leave
+ * the string pointer pointing at the null character.
+ *
+ * If \ is followed by 000, we return 0 and leave the string pointer after the
+ * zeros. A value of 0 does not mean end of string.
+ *
+ * Posix doesn't allow \x.
+ */
+
+int
+parse_escape(string_ptr)
+char **string_ptr;
+{
+ register int c = *(*string_ptr)++;
+ register int i;
+ register int count;
+
+ switch (c) {
+ case 'a':
+ return BELL;
+ case 'b':
+ return '\b';
+ case 'f':
+ return '\f';
+ case 'n':
+ return '\n';
+ case 'r':
+ return '\r';
+ case 't':
+ return '\t';
+ case 'v':
+ return '\v';
+ case '\n':
+ return -2;
+ case 0:
+ (*string_ptr)--;
+ return -1;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ i = c - '0';
+ count = 0;
+ while (++count < 3) {
+ if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
+ i *= 8;
+ i += c - '0';
+ } else {
+ (*string_ptr)--;
+ break;
+ }
+ }
+ return i;
+ case 'x':
+ if (do_lint) {
+ static int didwarn = FALSE;
+
+ if (! didwarn) {
+ didwarn = TRUE;
+ warning("POSIX does not allow \"\\x\" escapes");
+ }
+ }
+ if (do_posix)
+ return ('x');
+ if (! isxdigit((*string_ptr)[0])) {
+ warning("no hex digits in \\x escape sequence");
+ return ('x');
+ }
+ i = 0;
+ for (;;) {
+ if (ISXDIGIT((c = *(*string_ptr)++))) {
+ i *= 16;
+ if (ISDIGIT(c))
+ i += c - '0';
+ else if (ISUPPER(c))
+ i += c - 'A' + 10;
+ else
+ i += c - 'a' + 10;
+ } else {
+ (*string_ptr)--;
+ break;
+ }
+ }
+ return i;
+ default:
+ return c;
+ }
+}
OpenPOWER on IntegriCloud