home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / gnuawk.zip / node.c < prev    next >
C/C++ Source or Header  |  1997-05-01  |  10KB  |  516 lines

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