home *** CD-ROM | disk | FTP | other *** search
/ Acorn User 11 / AUCD11B.iso / LANGUAGES / WraithSet / AwkStuff / GawkSrc / c / node < prev    next >
Encoding:
Text File  |  1999-04-28  |  9.9 KB  |  517 lines

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