blob: 77b6705f375cae37c5c330892bb162399be60662 [file] [log] [blame]
/*
* node.c -- routines for node management
*/
/*
* Copyright (C) 1986, 1988, 1989 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
* AWK Progamming Language.
*
* GAWK is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* GAWK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAWK; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "gawk.h"
extern double strtod();
/*
* We can't dereference a variable until after we've given it its new value.
* This variable points to the value we have to free up
*/
NODE *deref;
AWKNUM
r_force_number(n)
NODE *n;
{
char *ptr;
#ifdef DEBUG
if (n == NULL)
cant_happen();
if (n->type != Node_val)
cant_happen();
if(n->flags == 0)
cant_happen();
if (n->flags & NUM)
return n->numbr;
#endif
if (n->stlen == 0)
n->numbr = 0.0;
else if (n->stlen == 1) {
if (isdigit(n->stptr[0])) {
n->numbr = n->stptr[0] - '0';
n->flags |= NUMERIC;
} else
n->numbr = 0.0;
} else {
errno = 0;
n->numbr = (AWKNUM) strtod(n->stptr, &ptr);
/* the following >= should be ==, but for SunOS 3.5 strtod() */
if (errno == 0 && ptr >= n->stptr + n->stlen)
n->flags |= NUMERIC;
}
n->flags |= NUM;
return n->numbr;
}
/*
* the following lookup table is used as an optimization in force_string
* (more complicated) variations on this theme didn't seem to pay off, but
* systematic testing might be in order at some point
*/
static char *values[] = {
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
};
#define NVAL (sizeof(values)/sizeof(values[0]))
NODE *
r_force_string(s)
NODE *s;
{
char buf[128];
char *fmt;
long num;
char *sp = buf;
#ifdef DEBUG
if (s == NULL)
cant_happen();
if (s->type != Node_val)
cant_happen();
if (s->flags & STR)
return s;
if (!(s->flags & NUM))
cant_happen();
if (s->stref != 0)
cant_happen();
#endif
s->flags |= STR;
/* should check validity of user supplied OFMT */
fmt = OFMT_node->var_value->stptr;
if ((num = s->numbr) == s->numbr) {
/* integral value */
if (num < NVAL && num >= 0) {
sp = values[num];
s->stlen = 1;
} else {
(void) sprintf(sp, "%ld", num);
s->stlen = strlen(sp);
}
} else {
(void) sprintf(sp, fmt, s->numbr);
s->stlen = strlen(sp);
}
s->stref = 1;
emalloc(s->stptr, char *, s->stlen + 1, "force_string");
memcpy(s->stptr, sp, s->stlen+1);
return s;
}
/*
* Duplicate a node. (For strings, "duplicate" means crank up the
* reference count.)
*/
NODE *
dupnode(n)
NODE *n;
{
register NODE *r;
if (n->flags & TEMP) {
n->flags &= ~TEMP;
n->flags |= MALLOC;
return n;
}
if ((n->flags & (MALLOC|STR)) == (MALLOC|STR)) {
if (n->stref < 255)
n->stref++;
return n;
}
r = newnode(Node_illegal);
*r = *n;
r->flags &= ~(PERM|TEMP);
r->flags |= MALLOC;
if (n->type == Node_val && (n->flags & STR)) {
r->stref = 1;
emalloc(r->stptr, char *, r->stlen + 1, "dupnode");
memcpy(r->stptr, n->stptr, r->stlen+1);
}
return r;
}
/* this allocates a node with defined numbr */
NODE *
make_number(x)
AWKNUM x;
{
register NODE *r;
r = newnode(Node_val);
r->numbr = x;
r->flags |= (NUM|NUMERIC);
r->stref = 0;
return r;
}
/*
* This creates temporary nodes. They go away quite quickly, so don't use
* them for anything important
*/
NODE *
tmp_number(x)
AWKNUM x;
{
NODE *r;
r = make_number(x);
r->flags |= TEMP;
return r;
}
/*
* Make a string node.
*/
NODE *
make_str_node(s, len, scan)
char *s;
int len;
int scan;
{
register NODE *r;
char *pf;
register char *pt;
register int c;
register char *end;
r = newnode(Node_val);
emalloc(r->stptr, char *, len + 1, s);
memcpy(r->stptr, s, len);
r->stptr[len] = '\0';
end = &(r->stptr[len]);
if (scan) { /* scan for escape sequences */
for (pf = pt = r->stptr; pf < end;) {
c = *pf++;
if (c == '\\') {
c = parse_escape(&pf);
if (c < 0)
cant_happen();
*pt++ = c;
} else
*pt++ = c;
}
len = pt - r->stptr;
erealloc(r->stptr, char *, len + 1, "make_str_node");
r->stptr[len] = '\0';
r->flags |= PERM;
}
r->stlen = len;
r->stref = 1;
r->flags |= (STR|MALLOC);
return r;
}
/* Read the warning under tmp_number */
NODE *
tmp_string(s, len)
char *s;
int len;
{
register NODE *r;
r = make_string(s, len);
r->flags |= TEMP;
return r;
}
#define NODECHUNK 100
static NODE *nextfree = NULL;
NODE *
newnode(ty)
NODETYPE ty;
{
NODE *it;
NODE *np;
#if defined(MPROF) || defined(NOMEMOPT)
emalloc(it, NODE *, sizeof(NODE), "newnode");
#else
if (nextfree == NULL) {
/* get more nodes and initialize list */
emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "newnode");
for (np = nextfree; np < &nextfree[NODECHUNK - 1]; np++)
np->nextp = np + 1;
np->nextp = NULL;
}
/* get head of freelist */
it = nextfree;
nextfree = nextfree->nextp;
#endif
it->type = ty;
it->flags = MALLOC;
#ifdef MEMDEBUG
fprintf(stderr, "node: new: %0x\n", it);
#endif
return it;
}
void
freenode(it)
NODE *it;
{
#ifdef DEBUG
NODE *nf;
#endif
#ifdef MEMDEBUG
fprintf(stderr, "node: free: %0x\n", it);
#endif
#if defined(MPROF) || defined(NOMEMOPT)
free((char *) it);
#elif defined(IGNOREFREE)
#else
#ifdef DEBUG
for (nf = nextfree; nf; nf = nf->nextp)
if (nf == it)
fatal("attempt to free free node");
#endif
/* add it to head of freelist */
it->nextp = nextfree;
nextfree = it;
#endif
}
#ifdef DEBUG
pf()
{
NODE *nf = nextfree;
while (nf != NULL) {
fprintf(stderr, "%0x ", nf);
nf = nf->nextp;
}
}
#endif
void
do_deref()
{
if (deref == NULL)
return;
if (deref->flags & PERM) {
deref = 0;
return;
}
if ((deref->flags & MALLOC) || (deref->flags & TEMP)) {
deref->flags &= ~TEMP;
if (deref->flags & STR) {
if (deref->stref > 1 && deref->stref != 255) {
deref->stref--;
deref = 0;
return;
}
free(deref->stptr);
}
freenode(deref);
}
deref = 0;
}