2 * node.c -- routines for node management
6 * Copyright (C) 1986, 1988, 1989, 1991-2000 the Free Software Foundation, Inc.
8 * This file is part of GAWK, the GNU implementation of the
9 * AWK Programming Language.
11 * GAWK is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * GAWK is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
25 * $FreeBSD: src/contrib/awk/node.c,v 1.4.2.1 2001/01/23 22:08:31 asmodai Exp $
26 * $DragonFly: src/contrib/awk/Attic/node.c,v 1.2 2003/06/17 04:23:58 dillon Exp $
31 /* r_force_number --- force a value to be numeric */
41 unsigned int newflags;
42 extern double strtod();
47 if (n->type != Node_val)
55 /* all the conditionals are an attempt to avoid the expensive strtod */
67 cpend = cp + n->stlen;
68 while (cp < cpend && isspace(*cp))
70 if (cp == cpend || isalpha(*cp))
73 if (n->flags & MAYBE_NUM) {
75 n->flags &= ~MAYBE_NUM;
78 if (cpend - cp == 1) {
80 n->numbr = (AWKNUM)(*cp - '0');
88 if (! do_traditional && isnondecimal(cp)) {
89 n->numbr = nondec2awknum(cp, cpend - cp);
92 #endif /* NONDECDATA */
97 n->numbr = (AWKNUM) strtod((const char *) cp, &ptr);
99 /* POSIX says trailing space is OK for NUMBER */
100 while (ISSPACE(*ptr))
104 /* the >= should be ==, but for SunOS 3.5 strtod() */
105 if (errno == 0 && ptr >= cpend)
106 n->flags |= newflags;
114 * the following lookup table is used as an optimization in force_string
115 * (more complicated) variations on this theme didn't seem to pay off, but
116 * systematic testing might be in order at some point
118 static const char *values[] = {
130 #define NVAL (sizeof(values)/sizeof(values[0]))
132 /* format_val --- format a numeric value based on format */
135 format_val(format, index, s)
141 register char *sp = buf;
144 /* not an integral value, or out of range */
145 if ((val = double_to_int(s->numbr)) != s->numbr
146 || val < LONG_MIN || val > LONG_MAX) {
148 * Once upon a time, if GFMT_WORKAROUND wasn't defined,
149 * we just blindly did this:
150 * sprintf(sp, format, s->numbr);
151 * s->stlen = strlen(sp);
152 * s->stfmt = (char) index;
153 * but that's no good if, e.g., OFMT is %s. So we punt,
154 * and just always format the value ourselves.
158 unsigned short oflags;
159 extern NODE *format_tree P((const char *, int, NODE *));
160 extern NODE **fmt_list; /* declared in eval.c */
162 /* create dummy node for a sole use of format_tree */
167 s->flags |= PERM; /* prevent from freeing by format_tree() */
168 r = format_tree(format, fmt_list[index]->stlen, dummy);
170 s->stfmt = (char) index;
173 freenode(r); /* Do not free_temp(r)! We want */
174 freenode(dummy); /* to keep s->stptr == r->stpr. */
179 /* force conversion to long only once */
180 register long num = (long) val;
181 if (num < NVAL && num >= 0) {
182 sp = (char *) values[num];
185 (void) sprintf(sp, "%ld", num);
186 s->stlen = strlen(sp);
190 emalloc(s->stptr, char *, s->stlen + 2, "format_val");
191 memcpy(s->stptr, sp, s->stlen+1);
198 /* r_force_string --- force a value to be a string */
207 if (s->type != Node_val)
210 if ((s->flags & NUM) == 0)
215 if ((s->flags & STR) != 0
216 && (s->stfmt == -1 || s->stfmt == CONVFMTidx))
220 return format_val(CONVFMT, CONVFMTidx, s);
225 * Duplicate a node. (For strings, "duplicate" means crank up the
235 if ((n->flags & TEMP) != 0) {
240 if ((n->flags & (MALLOC|STR)) == (MALLOC|STR)) {
241 if (n->stref < LONG_MAX)
247 r->flags &= ~(PERM|TEMP|FIELD);
249 if (n->type == Node_val && (n->flags & STR) != 0) {
251 emalloc(r->stptr, char *, r->stlen + 2, "dupnode");
252 memcpy(r->stptr, n->stptr, r->stlen);
253 r->stptr[r->stlen] = '\0';
258 /* mk_number --- allocate a node with defined number */
270 r->flags = flags | SCALAR;
279 /* make_str_node --- make a string node */
282 make_str_node(s, len, flags)
291 r->flags = (STRING|STR|MALLOC|SCALAR);
292 if (flags & ALREADY_MALLOCED)
295 emalloc(r->stptr, char *, len + 2, s);
296 memcpy(r->stptr, s, len);
298 r->stptr[len] = '\0';
300 if ((flags & SCAN) != 0) { /* scan for escape sequences */
306 end = &(r->stptr[len]);
307 for (pf = ptm = r->stptr; pf < end;) {
310 c = parse_escape(&pf);
313 warning("backslash at end of string");
320 len = ptm - r->stptr;
321 erealloc(r->stptr, char *, len + 1, "make_str_node");
322 r->stptr[len] = '\0';
332 /* tmp_string --- allocate a temporary string */
341 r = make_string(s, len);
346 /* more_nodes --- allocate more nodes */
348 #define NODECHUNK 100
350 NODE *nextfree = NULL;
357 /* get more nodes and initialize list */
358 emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "newnode");
359 for (np = nextfree; np <= &nextfree[NODECHUNK - 1]; np++) {
366 nextfree = nextfree->nextp;
371 /* freenode --- release a node back to the pool */
377 it->flags &= ~SCALAR;
381 #else /* not MPROF */
382 /* add it to head of freelist */
383 it->nextp = nextfree;
385 #endif /* not MPROF */
389 /* unref --- remove reference to a particular node */
397 if ((tmp->flags & PERM) != 0)
399 if ((tmp->flags & (MALLOC|TEMP)) != 0) {
401 if ((tmp->flags & STR) != 0) {
402 if (tmp->stref > 1) {
403 if (tmp->stref != LONG_MAX)
412 if ((tmp->flags & FIELD) != 0) {
421 * Parse a C escape sequence. STRING_PTR points to a variable containing a
422 * pointer to the string to parse. That pointer is updated past the
423 * characters we use. The value of the escape sequence is returned.
425 * A negative value means the sequence \ newline was seen, which is supposed to
426 * be equivalent to nothing at all.
428 * If \ is followed by a null character, we return a negative value and leave
429 * the string pointer pointing at the null character.
431 * If \ is followed by 000, we return 0 and leave the string pointer after the
432 * zeros. A value of 0 does not mean end of string.
434 * Posix doesn't allow \x.
438 parse_escape(string_ptr)
441 register int c = *(*string_ptr)++;
475 while (++count < 3) {
476 if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
487 static int didwarn = FALSE;
491 warning("POSIX does not allow \"\\x\" escapes");
496 if (! isxdigit((*string_ptr)[0])) {
497 warning("no hex digits in \\x escape sequence");
502 /* do outside test to avoid multiple side effects */
503 c = *(*string_ptr)++;
520 static short warned[256];
521 unsigned char uc = (unsigned char) c;
523 /* N.B.: use unsigned char here to avoid Latin-1 problems */
528 warning("escape sequence `\\%c' treated as plain `%c'", uc, uc);