* Add this nice filesystem testing tool that I've recently
[dragonfly.git] / contrib / awk / node.c
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 $
26  * $DragonFly: src/contrib/awk/Attic/node.c,v 1.2 2003/06/17 04:23:58 dillon Exp $
27  */
28
29 #include "awk.h"
30
31 /* r_force_number --- force a value to be numeric */
32
33 AWKNUM
34 r_force_number(n)
35 register 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;
103 finish:
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  */
118 static 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
134 NODE *
135 format_val(format, index, s)
136 char *format;
137 int index;
138 register 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);
192 no_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
200 NODE *
201 r_force_string(s)
202 register 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
229 NODE *
230 dupnode(n)
231 NODE *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
260 NODE *
261 mk_number(x, flags)
262 AWKNUM x;
263 unsigned 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
281 NODE *
282 make_str_node(s, len, flags)
283 char *s;
284 size_t len;
285 int 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
334 NODE *
335 tmp_string(s, len)
336 char *s;
337 size_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
350 NODE *nextfree = NULL;
351
352 NODE *
353 more_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
373 void
374 freenode(it)
375 NODE *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
391 void
392 unref(tmp)
393 register 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
437 int
438 parse_escape(string_ptr)
439 char **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 }