Main Page | Class List | Directories | File List | Class Members | File Members

node.c

Go to the documentation of this file.
00001 /*
00002  * node.c -- routines for node management
00003  */
00004 
00005 /* 
00006  * Copyright (C) 1986, 1988, 1989, 1991-2001, 2003 the Free Software Foundation, Inc.
00007  * 
00008  * This file is part of GAWK, the GNU implementation of the
00009  * AWK Programming Language.
00010  * 
00011  * GAWK is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  * 
00016  * GAWK is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  * 
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
00024  */
00025 
00026 #include "awk.h"
00027 
00028 /* r_force_number --- force a value to be numeric */
00029 
00030 AWKNUM
00031 r_force_number(register NODE *n)
00032 {
00033         register char *cp;
00034         register char *cpend;
00035         char save;
00036         char *ptr;
00037         unsigned int newflags;
00038         extern double strtod();
00039 
00040 #ifdef GAWKDEBUG
00041         if (n == NULL)
00042                 cant_happen();
00043         if (n->type != Node_val)
00044                 cant_happen();
00045         if (n->flags == 0)
00046                 cant_happen();
00047         if (n->flags & NUMCUR)
00048                 return n->numbr;
00049 #endif
00050 
00051         /* all the conditionals are an attempt to avoid the expensive strtod */
00052 
00053         n->numbr = 0.0;
00054         n->flags |= NUMCUR;
00055 
00056         if (n->stlen == 0) {
00057                 if (0 && do_lint)
00058                         lintwarn(_("can't convert string to float"));
00059                 return 0.0;
00060         }
00061 
00062         cp = n->stptr;
00063         if (ISALPHA(*cp)) {
00064                 if (0 && do_lint)
00065                         lintwarn(_("can't convert string to float"));
00066                 return 0.0;
00067         }
00068 
00069         cpend = cp + n->stlen;
00070         while (cp < cpend && ISSPACE(*cp))
00071                 cp++;
00072         if (cp == cpend || ISALPHA(*cp)) {
00073                 if (0 && do_lint)
00074                         lintwarn(_("can't convert string to float"));
00075                 return 0.0;
00076         }
00077 
00078         if (n->flags & MAYBE_NUM) {
00079                 newflags = NUMBER;
00080                 n->flags &= ~MAYBE_NUM;
00081         } else
00082                 newflags = 0;
00083         if (cpend - cp == 1) {
00084                 if (ISDIGIT(*cp)) {
00085                         n->numbr = (AWKNUM)(*cp - '0');
00086                         n->flags |= newflags;
00087                 } else if (0 && do_lint)
00088                         lintwarn(_("can't convert string to float"));
00089                 return n->numbr;
00090         }
00091 
00092         if (do_non_decimal_data) {
00093                 errno = 0;
00094                 if (! do_traditional && isnondecimal(cp)) {
00095                         n->numbr = nondec2awknum(cp, cpend - cp);
00096                         goto finish;
00097                 }
00098         }
00099 
00100         errno = 0;
00101         save = *cpend;
00102         *cpend = '\0';
00103         n->numbr = (AWKNUM) strtod((const char *) cp, &ptr);
00104 
00105         /* POSIX says trailing space is OK for NUMBER */
00106         while (ISSPACE(*ptr))
00107                 ptr++;
00108         *cpend = save;
00109 finish:
00110         /* the >= should be ==, but for SunOS 3.5 strtod() */
00111         if (errno == 0 && ptr >= cpend) {
00112                 n->flags |= newflags;
00113         } else {
00114                 if (0 && do_lint && ptr < cpend)
00115                         lintwarn(_("can't convert string to float"));
00116                 errno = 0;
00117         }
00118 
00119         return n->numbr;
00120 }
00121 
00122 /*
00123  * the following lookup table is used as an optimization in force_string
00124  * (more complicated) variations on this theme didn't seem to pay off, but 
00125  * systematic testing might be in order at some point
00126  */
00127 static const char *const values[] = {
00128         "0",
00129         "1",
00130         "2",
00131         "3",
00132         "4",
00133         "5",
00134         "6",
00135         "7",
00136         "8",
00137         "9",
00138 };
00139 #define NVAL    (sizeof(values)/sizeof(values[0]))
00140 
00141 /* format_val --- format a numeric value based on format */
00142 
00143 NODE *
00144 format_val(const char *format, int index, register NODE *s)
00145 {
00146         char buf[BUFSIZ];
00147         register char *sp = buf;
00148         double val;
00149         char *orig, *trans, save;
00150 
00151         if (! do_traditional && (s->flags & INTLSTR) != 0) {
00152                 save = s->stptr[s->stlen];
00153                 s->stptr[s->stlen] = '\0';
00154 
00155                 orig = s->stptr;
00156                 trans = dgettext(TEXTDOMAIN, orig);
00157 
00158                 s->stptr[s->stlen] = save;
00159                 return tmp_string(trans, strlen(trans));
00160         }
00161 
00162         /* not an integral value, or out of range */
00163         if ((val = double_to_int(s->numbr)) != s->numbr
00164             || val < LONG_MIN || val > LONG_MAX) {
00165                 /*
00166                  * Once upon a time, if GFMT_WORKAROUND wasn't defined,
00167                  * we just blindly did this:
00168                  *      sprintf(sp, format, s->numbr);
00169                  *      s->stlen = strlen(sp);
00170                  *      s->stfmt = (char) index;
00171                  * but that's no good if, e.g., OFMT is %s. So we punt,
00172                  * and just always format the value ourselves.
00173                  */
00174 
00175                 NODE *dummy, *r;
00176                 unsigned short oflags;
00177                 extern NODE **fmt_list;          /* declared in eval.c */
00178 
00179                 /* create dummy node for a sole use of format_tree */
00180                 getnode(dummy);
00181                 dummy->type = Node_expression_list;
00182                 dummy->lnode = s;
00183                 dummy->rnode = NULL;
00184                 oflags = s->flags;
00185                 s->flags |= PERM; /* prevent from freeing by format_tree() */
00186                 r = format_tree(format, fmt_list[index]->stlen, dummy, 2);
00187                 s->flags = oflags;
00188                 s->stfmt = (char) index;
00189                 s->stlen = r->stlen;
00190                 s->stptr = r->stptr;
00191                 freenode(r);            /* Do not free_temp(r)!  We want */
00192                 freenode(dummy);        /* to keep s->stptr == r->stpr.  */
00193 
00194                 goto no_malloc;
00195         } else {
00196                 /* integral value */
00197                 /* force conversion to long only once */
00198                 register long num = (long) val;
00199                 if (num < NVAL && num >= 0) {
00200                         sp = (char *) values[num];
00201                         s->stlen = 1;
00202                 } else {
00203                         (void) sprintf(sp, "%ld", num);
00204                         s->stlen = strlen(sp);
00205                 }
00206                 s->stfmt = -1;
00207         }
00208         emalloc(s->stptr, char *, s->stlen + 2, "format_val");
00209         memcpy(s->stptr, sp, s->stlen+1);
00210 no_malloc:
00211         s->stref = 1;
00212         s->flags |= STRCUR;
00213         return s;
00214 }
00215 
00216 /* r_force_string --- force a value to be a string */
00217 
00218 NODE *
00219 r_force_string(register NODE *s)
00220 {
00221         NODE *ret;
00222 #ifdef GAWKDEBUG
00223         if (s == NULL)
00224                 cant_happen();
00225         if (s->type != Node_val)
00226                 cant_happen();
00227         if (s->stref <= 0)
00228                 cant_happen();
00229         if ((s->flags & STRCUR) != 0
00230             && (s->stfmt == -1 || s->stfmt == CONVFMTidx))
00231                 return s;
00232 #endif
00233 
00234         ret = format_val(CONVFMT, CONVFMTidx, s);
00235         return ret;
00236 }
00237 
00238 /*
00239  * dupnode:
00240  * Duplicate a node.  (For strings, "duplicate" means crank up the
00241  * reference count.)
00242  */
00243 
00244 NODE *
00245 r_dupnode(NODE *n)
00246 {
00247         register NODE *r;
00248 
00249 #ifndef DUPNODE_MACRO
00250         if ((n->flags & TEMP) != 0) {
00251                 n->flags &= ~TEMP;
00252                 n->flags |= MALLOC;
00253                 return n;
00254         }
00255         if ((n->flags & PERM) != 0)
00256                 return n;
00257 #endif
00258         if ((n->flags & (MALLOC|STRCUR)) == (MALLOC|STRCUR)) {
00259                 if (n->stref < LONG_MAX)
00260                         n->stref++;
00261                 else
00262                         n->flags |= PERM;
00263                 return n;
00264         } else if ((n->flags & MALLOC) != 0 && n->type == Node_ahash) {
00265                 if (n->ahname_ref < LONG_MAX)
00266                         n->ahname_ref++;
00267                 else
00268                         n->flags |= PERM;
00269                 return n;
00270         }
00271         getnode(r);
00272         *r = *n;
00273         r->flags &= ~(PERM|TEMP|FIELD);
00274         r->flags |= MALLOC;
00275         if (n->type == Node_val && (n->flags & STRCUR) != 0) {
00276                 r->stref = 1;
00277                 emalloc(r->stptr, char *, r->stlen + 2, "dupnode");
00278                 memcpy(r->stptr, n->stptr, r->stlen);
00279                 r->stptr[r->stlen] = '\0';
00280         } else if (n->type == Node_ahash && (n->flags & MALLOC) != 0) {
00281                 r->ahname_ref = 1;
00282                 emalloc(r->ahname_str, char *, r->ahname_len + 2, "dupnode");
00283                 memcpy(r->ahname_str, n->ahname_str, r->ahname_len);
00284                 r->ahname_str[r->ahname_len] = '\0';
00285         }
00286         return r;
00287 }
00288 
00289 /* copy_node --- force a brand new copy of a node to be allocated */
00290 
00291 NODE *
00292 copynode(NODE *old)
00293 {
00294         NODE *new;
00295         int saveflags;
00296 
00297         assert(old != NULL);
00298         saveflags = old->flags;
00299         old->flags &= ~(MALLOC|PERM);
00300         new = dupnode(old);
00301         old->flags = saveflags;
00302         return new;
00303 }
00304 
00305 /* mk_number --- allocate a node with defined number */
00306 
00307 NODE *
00308 mk_number(AWKNUM x, unsigned int flags)
00309 {
00310         register NODE *r;
00311 
00312         getnode(r);
00313         r->type = Node_val;
00314         r->numbr = x;
00315         r->flags = flags;
00316 #ifdef GAWKDEBUG
00317         r->stref = 1;
00318         r->stptr = NULL;
00319         r->stlen = 0;
00320 #endif
00321         return r;
00322 }
00323 
00324 /* make_str_node --- make a string node */
00325 
00326 NODE *
00327 make_str_node(char *s, unsigned long len, int flags)
00328 {
00329         register NODE *r;
00330 
00331         getnode(r);
00332         r->type = Node_val;
00333         r->flags = (STRING|STRCUR|MALLOC);
00334         if (flags & ALREADY_MALLOCED)
00335                 r->stptr = s;
00336         else {
00337                 emalloc(r->stptr, char *, len + 2, s);
00338                 memcpy(r->stptr, s, len);
00339         }
00340         r->stptr[len] = '\0';
00341                
00342         if ((flags & SCAN) != 0) {      /* scan for escape sequences */
00343                 const char *pf;
00344                 register char *ptm;
00345                 register int c;
00346                 register const char *end;
00347 
00348                 end = &(r->stptr[len]);
00349                 for (pf = ptm = r->stptr; pf < end;) {
00350                         c = *pf++;
00351                         if (c == '\\') {
00352                                 c = parse_escape(&pf);
00353                                 if (c < 0) {
00354                                         if (do_lint)
00355                                                 lintwarn(_("backslash at end of string"));
00356                                         c = '\\';
00357                                 }
00358                                 *ptm++ = c;
00359                         } else
00360                                 *ptm++ = c;
00361                 }
00362                 len = ptm - r->stptr;
00363                 erealloc(r->stptr, char *, len + 1, "make_str_node");
00364                 r->stptr[len] = '\0';
00365                 r->flags |= PERM;
00366         }
00367         r->stlen = len;
00368         r->stref = 1;
00369         r->stfmt = -1;
00370 
00371         return r;
00372 }
00373 
00374 /* tmp_string --- allocate a temporary string */
00375 
00376 NODE *
00377 tmp_string(char *s, size_t len)
00378 {
00379         register NODE *r;
00380 
00381         r = make_string(s, len);
00382         r->flags |= TEMP;
00383         return r;
00384 }
00385 
00386 /* more_nodes --- allocate more nodes */
00387 
00388 #define NODECHUNK       100
00389 
00390 NODE *nextfree = NULL;
00391 
00392 NODE *
00393 more_nodes()
00394 {
00395         register NODE *np;
00396 
00397         /* get more nodes and initialize list */
00398         emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "more_nodes");
00399         for (np = nextfree; np <= &nextfree[NODECHUNK - 1]; np++) {
00400                 np->flags = 0;
00401 #ifndef NO_PROFILING
00402                 np->exec_count = 0;
00403 #endif
00404                 np->nextp = np + 1;
00405         }
00406         --np;
00407         np->nextp = NULL;
00408         np = nextfree;
00409         nextfree = nextfree->nextp;
00410         return np;
00411 }
00412 
00413 #ifdef MEMDEBUG
00414 #undef freenode
00415 /* freenode --- release a node back to the pool */
00416 
00417 void
00418 freenode(NODE *it)
00419 {
00420 #ifdef MPROF
00421         it->stref = 0;
00422         free((char *) it);
00423 #else   /* not MPROF */
00424 #ifndef NO_PROFILING
00425         it->exec_count = 0;
00426 #endif
00427         /* add it to head of freelist */
00428         it->nextp = nextfree;
00429         nextfree = it;
00430 #endif  /* not MPROF */
00431 }
00432 #endif  /* GAWKDEBUG */
00433 
00434 /* unref --- remove reference to a particular node */
00435 
00436 void
00437 unref(register NODE *tmp)
00438 {
00439         if (tmp == NULL)
00440                 return;
00441         if ((tmp->flags & PERM) != 0)
00442                 return;
00443         tmp->flags &= ~TEMP;
00444         if ((tmp->flags & MALLOC) != 0) {
00445                 if (tmp->type == Node_ahash) {
00446                         if (tmp->ahname_ref > 1) {
00447                                 tmp->ahname_ref--;
00448                                 return;
00449                         }
00450                         free(tmp->ahname_str);
00451                 } else if ((tmp->flags & STRCUR) != 0) {
00452                         if (tmp->stref > 1) {
00453                                 tmp->stref--;
00454                                 return;
00455                         }
00456                         free(tmp->stptr);
00457                 }
00458                 freenode(tmp);
00459                 return;
00460         }
00461         if ((tmp->flags & FIELD) != 0) {
00462                 freenode(tmp);
00463                 return;
00464         }
00465 }
00466 
00467 /*
00468  * parse_escape:
00469  *
00470  * Parse a C escape sequence.  STRING_PTR points to a variable containing a
00471  * pointer to the string to parse.  That pointer is updated past the
00472  * characters we use.  The value of the escape sequence is returned. 
00473  *
00474  * A negative value means the sequence \ newline was seen, which is supposed to
00475  * be equivalent to nothing at all. 
00476  *
00477  * If \ is followed by a null character, we return a negative value and leave
00478  * the string pointer pointing at the null character. 
00479  *
00480  * If \ is followed by 000, we return 0 and leave the string pointer after the
00481  * zeros.  A value of 0 does not mean end of string.  
00482  *
00483  * Posix doesn't allow \x.
00484  */
00485 
00486 int
00487 parse_escape(const char **string_ptr)
00488 {
00489         register int c = *(*string_ptr)++;
00490         register int i;
00491         register int count;
00492 
00493         switch (c) {
00494         case 'a':
00495                 return BELL;
00496         case 'b':
00497                 return '\b';
00498         case 'f':
00499                 return '\f';
00500         case 'n':
00501                 return '\n';
00502         case 'r':
00503                 return '\r';
00504         case 't':
00505                 return '\t';
00506         case 'v':
00507                 return '\v';
00508         case '\n':
00509                 return -2;
00510         case 0:
00511                 (*string_ptr)--;
00512                 return -1;
00513         case '0':
00514         case '1':
00515         case '2':
00516         case '3':
00517         case '4':
00518         case '5':
00519         case '6':
00520         case '7':
00521                 i = c - '0';
00522                 count = 0;
00523                 while (++count < 3) {
00524                         if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
00525                                 i *= 8;
00526                                 i += c - '0';
00527                         } else {
00528                                 (*string_ptr)--;
00529                                 break;
00530                         }
00531                 }
00532                 return i;
00533         case 'x':
00534                 if (do_lint) {
00535                         static int didwarn = FALSE;
00536 
00537                         if (! didwarn) {
00538                                 didwarn = TRUE;
00539                                 lintwarn(_("POSIX does not allow `\\x' escapes"));
00540                         }
00541                 }
00542                 if (do_posix)
00543                         return ('x');
00544                 if (! ISXDIGIT((*string_ptr)[0])) {
00545                         warning(_("no hex digits in `\\x' escape sequence"));
00546                         return ('x');
00547                 }
00548                 i = 0;
00549                 for (;;) {
00550                         /* do outside test to avoid multiple side effects */
00551                         c = *(*string_ptr)++;
00552                         if (ISXDIGIT(c)) {
00553                                 i *= 16;
00554                                 if (ISDIGIT(c))
00555                                         i += c - '0';
00556                                 else if (ISUPPER(c))
00557                                         i += c - 'A' + 10;
00558                                 else
00559                                         i += c - 'a' + 10;
00560                         } else {
00561                                 (*string_ptr)--;
00562                                 break;
00563                         }
00564                 }
00565                 return i;
00566         case '\\':
00567         case '"':
00568                 return c;
00569         default:
00570         {
00571                 static short warned[256];
00572                 unsigned char uc = (unsigned char) c;
00573 
00574                 /* N.B.: use unsigned char here to avoid Latin-1 problems */
00575 
00576                 if (! warned[uc]) {
00577                         warned[uc] = TRUE;
00578 
00579                         warning(_("escape sequence `\\%c' treated as plain `%c'"), uc, uc);
00580                 }
00581         }
00582                 return c;
00583         }
00584 }

© sourcejam.com 2005-2008