Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / contrib / awk / node.c
CommitLineData
984263bc
MD
1/*
2 * node.c -- routines for node management
3 */
4
5/*
6 * Copyright (C) 1986, 1988, 1989, 1991-2000 the Free Software Foundation, Inc.
7 *
8 * This file is part of GAWK, the GNU implementation of the
9 * AWK Programming Language.
10 *
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.
15 *
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.
20 *
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
24 *
25 * $FreeBSD: src/contrib/awk/node.c,v 1.4.2.1 2001/01/23 22:08:31 asmodai Exp $
1de703da 26 * $DragonFly: src/contrib/awk/Attic/node.c,v 1.2 2003/06/17 04:23:58 dillon Exp $
984263bc
MD
27 */
28
29#include "awk.h"
30
31/* r_force_number --- force a value to be numeric */
32
33AWKNUM
34r_force_number(n)
35register NODE *n;
36{
37 register char *cp;
38 register char *cpend;
39 char save;
40 char *ptr;
41 unsigned int newflags;
42 extern double strtod();
43
44#ifdef DEBUG
45 if (n == NULL)
46 cant_happen();
47 if (n->type != Node_val)
48 cant_happen();
49 if(n->flags == 0)
50 cant_happen();
51 if (n->flags & NUM)
52 return n->numbr;
53#endif
54
55 /* all the conditionals are an attempt to avoid the expensive strtod */
56
57 n->numbr = 0.0;
58 n->flags |= NUM;
59
60 if (n->stlen == 0)
61 return 0.0;
62
63 cp = n->stptr;
64 if (ISALPHA(*cp))
65 return 0.0;
66
67 cpend = cp + n->stlen;
68 while (cp < cpend && isspace(*cp))
69 cp++;
70 if (cp == cpend || isalpha(*cp))
71 return 0.0;
72
73 if (n->flags & MAYBE_NUM) {
74 newflags = NUMBER;
75 n->flags &= ~MAYBE_NUM;
76 } else
77 newflags = 0;
78 if (cpend - cp == 1) {
79 if (ISDIGIT(*cp)) {
80 n->numbr = (AWKNUM)(*cp - '0');
81 n->flags |= newflags;
82 }
83 return n->numbr;
84 }
85
86#ifdef NONDECDATA
87 errno = 0;
88 if (! do_traditional && isnondecimal(cp)) {
89 n->numbr = nondec2awknum(cp, cpend - cp);
90 goto finish;
91 }
92#endif /* NONDECDATA */
93
94 errno = 0;
95 save = *cpend;
96 *cpend = '\0';
97 n->numbr = (AWKNUM) strtod((const char *) cp, &ptr);
98
99 /* POSIX says trailing space is OK for NUMBER */
100 while (ISSPACE(*ptr))
101 ptr++;
102 *cpend = save;
103finish:
104 /* the >= should be ==, but for SunOS 3.5 strtod() */
105 if (errno == 0 && ptr >= cpend)
106 n->flags |= newflags;
107 else
108 errno = 0;
109
110 return n->numbr;
111}
112
113/*
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
117 */
118static const char *values[] = {
119 "0",
120 "1",
121 "2",
122 "3",
123 "4",
124 "5",
125 "6",
126 "7",
127 "8",
128 "9",
129};
130#define NVAL (sizeof(values)/sizeof(values[0]))
131
132/* format_val --- format a numeric value based on format */
133
134NODE *
135format_val(format, index, s)
136char *format;
137int index;
138register NODE *s;
139{
140 char buf[128];
141 register char *sp = buf;
142 double val;
143
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) {
147 /*
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.
155 */
156
157 NODE *dummy, *r;
158 unsigned short oflags;
159 extern NODE *format_tree P((const char *, int, NODE *));
160 extern NODE **fmt_list; /* declared in eval.c */
161
162 /* create dummy node for a sole use of format_tree */
163 getnode(dummy);
164 dummy->lnode = s;
165 dummy->rnode = NULL;
166 oflags = s->flags;
167 s->flags |= PERM; /* prevent from freeing by format_tree() */
168 r = format_tree(format, fmt_list[index]->stlen, dummy);
169 s->flags = oflags;
170 s->stfmt = (char) index;
171 s->stlen = r->stlen;
172 s->stptr = r->stptr;
173 freenode(r); /* Do not free_temp(r)! We want */
174 freenode(dummy); /* to keep s->stptr == r->stpr. */
175
176 goto no_malloc;
177 } else {
178 /* integral value */
179 /* force conversion to long only once */
180 register long num = (long) val;
181 if (num < NVAL && num >= 0) {
182 sp = (char *) values[num];
183 s->stlen = 1;
184 } else {
185 (void) sprintf(sp, "%ld", num);
186 s->stlen = strlen(sp);
187 }
188 s->stfmt = -1;
189 }
190 emalloc(s->stptr, char *, s->stlen + 2, "format_val");
191 memcpy(s->stptr, sp, s->stlen+1);
192no_malloc:
193 s->stref = 1;
194 s->flags |= STR;
195 return s;
196}
197
198/* r_force_string --- force a value to be a string */
199
200NODE *
201r_force_string(s)
202register NODE *s;
203{
204#ifdef DEBUG
205 if (s == NULL)
206 cant_happen();
207 if (s->type != Node_val)
208 cant_happen();
209/*
210 if ((s->flags & NUM) == 0)
211 cant_happen();
212*/
213 if (s->stref <= 0)
214 cant_happen();
215 if ((s->flags & STR) != 0
216 && (s->stfmt == -1 || s->stfmt == CONVFMTidx))
217 return s;
218#endif
219
220 return format_val(CONVFMT, CONVFMTidx, s);
221}
222
223/*
224 * dupnode:
225 * Duplicate a node. (For strings, "duplicate" means crank up the
226 * reference count.)
227 */
228
229NODE *
230dupnode(n)
231NODE *n;
232{
233 register NODE *r;
234
235 if ((n->flags & TEMP) != 0) {
236 n->flags &= ~TEMP;
237 n->flags |= MALLOC;
238 return n;
239 }
240 if ((n->flags & (MALLOC|STR)) == (MALLOC|STR)) {
241 if (n->stref < LONG_MAX)
242 n->stref++;
243 return n;
244 }
245 getnode(r);
246 *r = *n;
247 r->flags &= ~(PERM|TEMP|FIELD);
248 r->flags |= MALLOC;
249 if (n->type == Node_val && (n->flags & STR) != 0) {
250 r->stref = 1;
251 emalloc(r->stptr, char *, r->stlen + 2, "dupnode");
252 memcpy(r->stptr, n->stptr, r->stlen);
253 r->stptr[r->stlen] = '\0';
254 }
255 return r;
256}
257
258/* mk_number --- allocate a node with defined number */
259
260NODE *
261mk_number(x, flags)
262AWKNUM x;
263unsigned int flags;
264{
265 register NODE *r;
266
267 getnode(r);
268 r->type = Node_val;
269 r->numbr = x;
270 r->flags = flags | SCALAR;
271#ifdef DEBUG
272 r->stref = 1;
273 r->stptr = NULL;
274 r->stlen = 0;
275#endif
276 return r;
277}
278
279/* make_str_node --- make a string node */
280
281NODE *
282make_str_node(s, len, flags)
283char *s;
284size_t len;
285int flags;
286{
287 register NODE *r;
288
289 getnode(r);
290 r->type = Node_val;
291 r->flags = (STRING|STR|MALLOC|SCALAR);
292 if (flags & ALREADY_MALLOCED)
293 r->stptr = s;
294 else {
295 emalloc(r->stptr, char *, len + 2, s);
296 memcpy(r->stptr, s, len);
297 }
298 r->stptr[len] = '\0';
299
300 if ((flags & SCAN) != 0) { /* scan for escape sequences */
301 char *pf;
302 register char *ptm;
303 register int c;
304 register char *end;
305
306 end = &(r->stptr[len]);
307 for (pf = ptm = r->stptr; pf < end;) {
308 c = *pf++;
309 if (c == '\\') {
310 c = parse_escape(&pf);
311 if (c < 0) {
312 if (do_lint)
313 warning("backslash at end of string");
314 c = '\\';
315 }
316 *ptm++ = c;
317 } else
318 *ptm++ = c;
319 }
320 len = ptm - r->stptr;
321 erealloc(r->stptr, char *, len + 1, "make_str_node");
322 r->stptr[len] = '\0';
323 r->flags |= PERM;
324 }
325 r->stlen = len;
326 r->stref = 1;
327 r->stfmt = -1;
328
329 return r;
330}
331
332/* tmp_string --- allocate a temporary string */
333
334NODE *
335tmp_string(s, len)
336char *s;
337size_t len;
338{
339 register NODE *r;
340
341 r = make_string(s, len);
342 r->flags |= TEMP;
343 return r;
344}
345
346/* more_nodes --- allocate more nodes */
347
348#define NODECHUNK 100
349
350NODE *nextfree = NULL;
351
352NODE *
353more_nodes()
354{
355 register NODE *np;
356
357 /* get more nodes and initialize list */
358 emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "newnode");
359 for (np = nextfree; np <= &nextfree[NODECHUNK - 1]; np++) {
360 np->flags = 0;
361 np->nextp = np + 1;
362 }
363 --np;
364 np->nextp = NULL;
365 np = nextfree;
366 nextfree = nextfree->nextp;
367 return np;
368}
369
370#ifdef DEBUG
371/* freenode --- release a node back to the pool */
372
373void
374freenode(it)
375NODE *it;
376{
377 it->flags &= ~SCALAR;
378#ifdef MPROF
379 it->stref = 0;
380 free((char *) it);
381#else /* not MPROF */
382 /* add it to head of freelist */
383 it->nextp = nextfree;
384 nextfree = it;
385#endif /* not MPROF */
386}
387#endif /* DEBUG */
388
389/* unref --- remove reference to a particular node */
390
391void
392unref(tmp)
393register NODE *tmp;
394{
395 if (tmp == NULL)
396 return;
397 if ((tmp->flags & PERM) != 0)
398 return;
399 if ((tmp->flags & (MALLOC|TEMP)) != 0) {
400 tmp->flags &= ~TEMP;
401 if ((tmp->flags & STR) != 0) {
402 if (tmp->stref > 1) {
403 if (tmp->stref != LONG_MAX)
404 tmp->stref--;
405 return;
406 }
407 free(tmp->stptr);
408 }
409 freenode(tmp);
410 return;
411 }
412 if ((tmp->flags & FIELD) != 0) {
413 freenode(tmp);
414 return;
415 }
416}
417
418/*
419 * parse_escape:
420 *
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.
424 *
425 * A negative value means the sequence \ newline was seen, which is supposed to
426 * be equivalent to nothing at all.
427 *
428 * If \ is followed by a null character, we return a negative value and leave
429 * the string pointer pointing at the null character.
430 *
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.
433 *
434 * Posix doesn't allow \x.
435 */
436
437int
438parse_escape(string_ptr)
439char **string_ptr;
440{
441 register int c = *(*string_ptr)++;
442 register int i;
443 register int count;
444
445 switch (c) {
446 case 'a':
447 return BELL;
448 case 'b':
449 return '\b';
450 case 'f':
451 return '\f';
452 case 'n':
453 return '\n';
454 case 'r':
455 return '\r';
456 case 't':
457 return '\t';
458 case 'v':
459 return '\v';
460 case '\n':
461 return -2;
462 case 0:
463 (*string_ptr)--;
464 return -1;
465 case '0':
466 case '1':
467 case '2':
468 case '3':
469 case '4':
470 case '5':
471 case '6':
472 case '7':
473 i = c - '0';
474 count = 0;
475 while (++count < 3) {
476 if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
477 i *= 8;
478 i += c - '0';
479 } else {
480 (*string_ptr)--;
481 break;
482 }
483 }
484 return i;
485 case 'x':
486 if (do_lint) {
487 static int didwarn = FALSE;
488
489 if (! didwarn) {
490 didwarn = TRUE;
491 warning("POSIX does not allow \"\\x\" escapes");
492 }
493 }
494 if (do_posix)
495 return ('x');
496 if (! isxdigit((*string_ptr)[0])) {
497 warning("no hex digits in \\x escape sequence");
498 return ('x');
499 }
500 i = 0;
501 for (;;) {
502 /* do outside test to avoid multiple side effects */
503 c = *(*string_ptr)++;
504 if (ISXDIGIT(c)) {
505 i *= 16;
506 if (ISDIGIT(c))
507 i += c - '0';
508 else if (ISUPPER(c))
509 i += c - 'A' + 10;
510 else
511 i += c - 'a' + 10;
512 } else {
513 (*string_ptr)--;
514 break;
515 }
516 }
517 return i;
518 default:
519 if (do_lint) {
520 static short warned[256];
521 unsigned char uc = (unsigned char) c;
522
523 /* N.B.: use unsigned char here to avoid Latin-1 problems */
524
525 if (! warned[uc]) {
526 warned[uc] = TRUE;
527
528 warning("escape sequence `\\%c' treated as plain `%c'", uc, uc);
529 }
530 }
531 return c;
532 }
533}