aboutsummaryrefslogtreecommitdiffstats
path: root/builtin.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2010-07-16 12:41:09 +0300
committerArnold D. Robbins <arnold@skeeve.com>2010-07-16 12:41:09 +0300
commit8c042f99cc7465c86351d21331a129111b75345d (patch)
tree9656e653be0e42e5469cec77635c20356de152c2 /builtin.c
parent8ceb5f934787eb7be5fb452fb39179df66119954 (diff)
downloadegawk-8c042f99cc7465c86351d21331a129111b75345d.tar.gz
egawk-8c042f99cc7465c86351d21331a129111b75345d.tar.bz2
egawk-8c042f99cc7465c86351d21331a129111b75345d.zip
Move to gawk-3.0.0.
Diffstat (limited to 'builtin.c')
-rw-r--r--builtin.c768
1 files changed, 557 insertions, 211 deletions
diff --git a/builtin.c b/builtin.c
index f925ac09..d6efd78d 100644
--- a/builtin.c
+++ b/builtin.c
@@ -6,7 +6,7 @@
* Copyright (C) 1986, 1988, 1989, 1991-1995 the Free Software Foundation, Inc.
*
* This file is part of GAWK, the GNU implementation of the
- * AWK Progamming Language.
+ * AWK Programming 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
@@ -19,26 +19,31 @@
* 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, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "awk.h"
+#undef HUGE
+#undef CHARBITS
+#undef INTBITS
+#include <math.h>
-#ifndef SRANDOM_PROTO
-extern void srandom P((unsigned int seed));
-#endif
-#if defined(RANDOM_MISSING)
+#ifndef HAVE_RANDOM
extern char *initstate P((unsigned seed, char *state, int n));
extern char *setstate P((char *state));
extern long random P((void));
+#define SRANDOM_PROTO
+#endif
+#ifdef SRANDOM_PROTO
+extern void srandom P((unsigned int seed));
#endif
extern NODE **fields_arr;
extern int output_is_tty;
-static NODE *sub_common P((NODE *tree, int global));
+static NODE *sub_common P((NODE *tree, int how_many, int backdigs));
NODE *format_tree P((const char *, int, NODE *));
#ifdef _CRAY
@@ -72,15 +77,19 @@ static void sgfmt P((char *buf, const char *format, int alt,
* On the Cray (Y-MP, anyway), ints and longs are 64 bits, but
* random() does things in terms of 32 bits. So we have to chop
* LONG_MAX down.
+ * On SGI with 64 bit support (IRIX 6.*), check the size of _MIPS_SZLONG
+ * and chop.... Per limits.h.
*/
-#if (defined(__alpha) && defined(__osf__)) || defined(_CRAY)
+#if (defined(__alpha) && defined(__osf__)) || defined(_CRAY) || (_MIPS_SZLONG == 64)
#define GAWK_RANDOM_MAX (LONG_MAX & 0x7fffffff)
#else
#define GAWK_RANDOM_MAX LONG_MAX
#endif
static void efwrite P((const void *ptr, size_t size, size_t count, FILE *fp,
- const char *from, struct redirect *rp,int flush));
+ const char *from, struct redirect *rp, int flush));
+
+/* efwrite --- like fwrite, but with error checking */
static void
efwrite(ptr, size, count, fp, from, rp, flush)
@@ -103,24 +112,22 @@ int flush;
}
return;
- wrerror:
+wrerror:
fatal("%s to \"%s\" failed (%s)", from,
rp ? rp->value : "standard output",
errno ? strerror(errno) : "reason unknown");
}
-/* Builtin functions */
+/* do_exp --- exponential function */
+
NODE *
do_exp(tree)
NODE *tree;
{
NODE *tmp;
double d, res;
-#ifndef exp
- double exp P((double));
-#endif
- tmp= tree_eval(tree->lnode);
+ tmp = tree_eval(tree->lnode);
d = force_number(tmp);
free_temp(tmp);
errno = 0;
@@ -130,6 +137,84 @@ NODE *tree;
return tmp_number((AWKNUM) res);
}
+/* stdfile --- return fp for a standard file */
+
+/*
+ * This function allows `fflush("/dev/stdout")' to work.
+ * The other files will be available via getredirect().
+ * /dev/stdin is not included, since fflush is only for output.
+ */
+
+static FILE *
+stdfile(name, len)
+char *name;
+size_t len;
+{
+ if (len == 11) {
+ if (STREQN(name, "/dev/stderr", 11))
+ return stderr;
+ else if (STREQN(name, "/dev/stdout", 11))
+ return stdout;
+ }
+
+ return NULL;
+}
+
+/* do_fflush --- flush output, either named file or pipe or everything */
+
+NODE *
+do_fflush(tree)
+NODE *tree;
+{
+ extern struct redirect *getredirect();
+ struct redirect *rp;
+ NODE *tmp;
+ FILE *fp;
+ int status = 0;
+ char *file;
+
+ /* fflush() --- flush stdout */
+ if (tree == NULL) {
+ status = fflush(stdout);
+ return tmp_number((AWKNUM) status);
+ }
+
+ tmp = tree_eval(tree->lnode);
+ tmp = force_string(tmp);
+ file = tmp->stptr;
+
+ /* fflush("") --- flush all */
+ if (tmp->stlen == 0) {
+ status = flush_io();
+ free_temp(tmp);
+ return tmp_number((AWKNUM) status);
+ }
+
+ rp = getredirect(tmp->stptr, tmp->stlen);
+ status = 1;
+ if (rp != NULL) {
+ if ((rp->flag & (RED_WRITE|RED_APPEND)) == 0) {
+ /* if (do_lint) */
+ warning(
+ "fflush: cannot flush: %s `%s' opened for reading, not writing",
+ (rp->flag & RED_PIPE) ? "pipe" : "file",
+ file);
+ free_temp(tmp);
+ return tmp_number((AWKNUM) status);
+ }
+ fp = rp->fp;
+ if (fp != NULL)
+ status = fflush(fp);
+ } else if ((fp = stdfile(tmp->stptr, tmp->stlen)) != NULL) {
+ status = fflush(fp);
+ } else
+ warning("fflush: `%s' is not an open file or pipe", file);
+ free_temp(tmp);
+ return tmp_number((AWKNUM) status);
+}
+
+/* do_index --- find index of a string */
+
NODE *
do_index(tree)
NODE *tree;
@@ -149,8 +234,10 @@ NODE *tree;
l1 = s1->stlen;
l2 = s2->stlen;
ret = 0;
+
+ /* IGNORECASE will already be false if posix */
if (IGNORECASE) {
- while (l1) {
+ while (l1 > 0) {
if (l2 > l1)
break;
if (casetable[(int)*p1] == casetable[(int)*p2]
@@ -162,7 +249,7 @@ NODE *tree;
p1++;
}
} else {
- while (l1) {
+ while (l1 > 0) {
if (l2 > l1)
break;
if (*p1 == *p2
@@ -179,13 +266,12 @@ NODE *tree;
return tmp_number((AWKNUM) ret);
}
+/* double_to_int --- convert double to int, used several places */
+
double
double_to_int(d)
double d;
{
- double floor P((double));
- double ceil P((double));
-
if (d >= 0)
d = Floor(d);
else
@@ -193,6 +279,8 @@ double d;
return d;
}
+/* do_int --- convert double to int for awk */
+
NODE *
do_int(tree)
NODE *tree;
@@ -207,6 +295,8 @@ NODE *tree;
return tmp_number((AWKNUM) d);
}
+/* do_length --- length of a string or $0 */
+
NODE *
do_length(tree)
NODE *tree;
@@ -220,14 +310,13 @@ NODE *tree;
return tmp_number((AWKNUM) len);
}
+/* do_log --- the log function */
+
NODE *
do_log(tree)
NODE *tree;
{
NODE *tmp;
-#ifndef log
- double log P((double));
-#endif
double d, arg;
tmp = tree_eval(tree->lnode);
@@ -257,54 +346,57 @@ register NODE *carg;
{
/* copy 'l' bytes from 's' to 'obufout' checking for space in the process */
/* difference of pointers should be of ptrdiff_t type, but let us be kind */
-#define bchunk(s,l) if(l) {\
- while((l)>ofre) {\
- long olen = obufout - obuf;\
- erealloc(obuf, char *, osiz*2, "format_tree");\
- ofre+=osiz;\
- osiz*=2;\
- obufout = obuf + olen;\
- }\
- memcpy(obufout,s,(size_t)(l));\
- obufout+=(l);\
- ofre-=(l);\
- }
+#define bchunk(s, l) if (l) { \
+ while ((l) > ofre) { \
+ long olen = obufout - obuf; \
+ erealloc(obuf, char *, osiz * 2, "format_tree"); \
+ ofre += osiz; \
+ osiz *= 2; \
+ obufout = obuf + olen; \
+ } \
+ memcpy(obufout, s, (size_t) (l)); \
+ obufout += (l); \
+ ofre -= (l); \
+}
+
/* copy one byte from 's' to 'obufout' checking for space in the process */
-#define bchunk_one(s) {\
- if(ofre <= 0) {\
- long olen = obufout - obuf;\
- erealloc(obuf, char *, osiz*2, "format_tree");\
- ofre+=osiz;\
- osiz*=2;\
- obufout = obuf + olen;\
- }\
- *obufout++ = *s;\
- --ofre;\
- }
-
- /* Is there space for something L big in the buffer? */
-#define chksize(l) if((l)>ofre) {\
- long olen = obufout - obuf;\
- erealloc(obuf, char *, osiz*2, "format_tree");\
- obufout = obuf + olen;\
- ofre+=osiz;\
- osiz*=2;\
- }
+#define bchunk_one(s) { \
+ if (ofre <= 0) { \
+ long olen = obufout - obuf; \
+ erealloc(obuf, char *, osiz * 2, "format_tree"); \
+ ofre += osiz; \
+ osiz *= 2; \
+ obufout = obuf + olen; \
+ } \
+ *obufout++ = *s; \
+ --ofre; \
+}
- /*
- * Get the next arg to be formatted. If we've run out of args,
- * return "" (Null string)
- */
-#define parse_next_arg() {\
- if(!carg) { toofew = 1; break; }\
- else {\
- arg=tree_eval(carg->lnode);\
- carg=carg->rnode;\
- }\
- }
+/* Is there space for something L big in the buffer? */
+#define chksize(l) if ((l) > ofre) { \
+ long olen = obufout - obuf; \
+ erealloc(obuf, char *, osiz * 2, "format_tree"); \
+ obufout = obuf + olen; \
+ ofre += osiz; \
+ osiz *= 2; \
+}
+
+/*
+ * Get the next arg to be formatted. If we've run out of args,
+ * return "" (Null string)
+ */
+#define parse_next_arg() { \
+ if (carg == NULL) { \
+ toofew = TRUE; \
+ break; \
+ } else { \
+ arg = tree_eval(carg->lnode); \
+ carg = carg->rnode; \
+ } \
+}
NODE *r;
- int toofew = 0;
+ int toofew = FALSE;
char *obuf, *obufout;
size_t osiz, ofre;
char *chbuf;
@@ -312,8 +404,8 @@ register NODE *carg;
int cs1;
NODE *arg;
long fw, prec;
- int lj, alt, big, have_prec;
- long *cur;
+ int lj, alt, big, bigbig, small, have_prec, need_format;
+ long *cur = NULL;
long val;
#ifdef sun386 /* Can't cast unsigned (int/long) from ptr->value */
long tmp_uval; /* on 386i 4.0.1 C compiler -- it just hangs */
@@ -326,7 +418,7 @@ register NODE *carg;
char *cp;
char *fill;
double tmpval;
- char signchar = 0;
+ char signchar = FALSE;
size_t len;
static char sp[] = " ";
static char zero_string[] = "0";
@@ -338,26 +430,32 @@ register NODE *carg;
osiz = 120;
ofre = osiz - 1;
+ need_format = FALSE;
+
s0 = s1 = fmt_string;
while (n0-- > 0) {
if (*s1 != '%') {
s1++;
continue;
}
+ need_format = TRUE;
bchunk(s0, s1 - s0);
s0 = s1;
cur = &fw;
fw = 0;
prec = 0;
- have_prec = 0;
- lj = alt = big = 0;
+ have_prec = FALSE;
+ signchar = FALSE;
+ lj = alt = big = bigbig = small = FALSE;
fill = sp;
cp = cend;
chbuf = lchbuf;
s1++;
retry:
- --n0;
+ if (n0-- <= 0) /* ran out early! */
+ break;
+
switch (cs1 = *s1++) {
case (-1): /* dummy case to allow for checking */
check_pos:
@@ -365,6 +463,7 @@ check_pos:
break; /* reject as a valid format */
goto retry;
case '%':
+ need_format = FALSE;
bchunk_one("%");
s0 = s1;
break;
@@ -373,7 +472,8 @@ check_pos:
if (lj)
goto retry;
if (cur == &fw)
- fill = zero_string; /* FALL through */
+ fill = zero_string;
+ /* FALL through */
case '1':
case '2':
case '3':
@@ -383,37 +483,39 @@ check_pos:
case '7':
case '8':
case '9':
- if (cur == 0)
- /* goto lose; */
+ if (cur == NULL)
break;
if (prec >= 0)
*cur = cs1 - '0';
- /* with a negative precision *cur is already set */
- /* to -1, so it will remain negative, but we have */
- /* to "eat" precision digits in any case */
+ /*
+ * with a negative precision *cur is already set
+ * to -1, so it will remain negative, but we have
+ * to "eat" precision digits in any case
+ */
while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
--n0;
*cur = *cur * 10 + *s1++ - '0';
}
if (prec < 0) /* negative precision is discarded */
- have_prec = 0;
+ have_prec = FALSE;
if (cur == &prec)
- cur = 0;
+ cur = NULL;
+ if (n0 == 0) /* badly formatted control string */
+ continue;
goto retry;
case '*':
- if (cur == 0)
- /* goto lose; */
+ if (cur == NULL)
break;
parse_next_arg();
*cur = force_number(arg);
free_temp(arg);
if (cur == &prec)
- cur = 0;
+ cur = NULL;
goto retry;
case ' ': /* print ' ' or '-' */
/* 'space' flag is ignored */
/* if '+' already present */
- if (signchar != 0)
+ if (signchar != FALSE)
goto check_pos;
/* FALL THROUGH */
case '+': /* print '+' or '-' */
@@ -433,32 +535,63 @@ check_pos:
if (cur != &fw)
break;
cur = &prec;
- have_prec++;
+ have_prec = TRUE;
goto retry;
case '#':
- alt++;
+ alt = TRUE;
goto check_pos;
case 'l':
if (big)
break;
else {
- static int warned = 0;
+ static int warned = FALSE;
if (do_lint && ! warned) {
warning("`l' is meaningless in awk formats; ignored");
- warned++;
+ warned = TRUE;
}
if (do_posix)
fatal("'l' is not permitted in POSIX awk formats");
}
- big++;
+ big = TRUE;
+ goto retry;
+ case 'L':
+ if (bigbig)
+ break;
+ else {
+ static int warned = FALSE;
+
+ if (do_lint && ! warned) {
+ warning("`L' is meaningless in awk formats; ignored");
+ warned = TRUE;
+ }
+ if (do_posix)
+ fatal("'L' is not permitted in POSIX awk formats");
+ }
+ bigbig = TRUE;
+ goto retry;
+ case 'h':
+ if (small)
+ break;
+ else {
+ static int warned = FALSE;
+
+ if (do_lint && ! warned) {
+ warning("`h' is meaningless in awk formats; ignored");
+ warned = TRUE;
+ }
+ if (do_posix)
+ fatal("'h' is not permitted in POSIX awk formats");
+ }
+ small = TRUE;
goto retry;
case 'c':
+ need_format = FALSE;
parse_next_arg();
if (arg->flags & NUMBER) {
#ifdef sun386
tmp_uval = arg->numbr;
- uval= (unsigned long) tmp_uval;
+ uval = (unsigned long) tmp_uval;
#else
uval = (unsigned long) arg->numbr;
#endif
@@ -467,24 +600,28 @@ check_pos:
cp = cpbuf;
goto pr_tail;
}
- if (have_prec == 0)
+ if (have_prec == FALSE)
prec = 1;
else if (prec > arg->stlen)
prec = arg->stlen;
cp = arg->stptr;
goto pr_tail;
case 's':
+ need_format = FALSE;
parse_next_arg();
arg = force_string(arg);
- if (have_prec == 0 || prec > arg->stlen)
+ if (! have_prec || prec > arg->stlen)
prec = arg->stlen;
cp = arg->stptr;
goto pr_tail;
case 'd':
case 'i':
+ need_format = FALSE;
parse_next_arg();
tmpval = force_number(arg);
- if (tmpval > LONG_MAX || tmpval < LONG_MIN) {
+ /* this ugly cast fixes a (sunos) pcc problem. sigh. */
+ if (tmpval > (double) ((unsigned long) ULONG_MAX)
+ || tmpval < LONG_MIN) {
/* out of range - emergency use of %g format */
cs1 = 'g';
goto format_float;
@@ -492,26 +629,32 @@ check_pos:
val = (long) tmpval;
if (val < 0) {
- sgn = 1;
+ sgn = TRUE;
if (val > LONG_MIN)
uval = (unsigned long) -val;
else
- uval = (unsigned long)(-(LONG_MIN + 1))
- + (unsigned long)1;
+ uval = (unsigned long) (-(LONG_MIN + 1))
+ + (unsigned long) 1;
} else {
- sgn = 0;
+ sgn = FALSE;
uval = (unsigned long) val;
}
do {
*--cp = (char) ('0' + uval % 10);
uval /= 10;
- } while (uval);
+ } while (uval > 0);
if (sgn)
*--cp = '-';
else if (signchar)
*--cp = signchar;
- if (have_prec != 0) /* ignore '0' flag if */
- fill = sp; /* precision given */
+ /*
+ * precision overrides '0' flags. however, for
+ * integer formats, precsion is minimum number of
+ * *digits*, not characters, thus we want to fill
+ * with zeroes.
+ */
+ if (have_prec)
+ fill = zero_string;
if (prec > fw)
fw = prec;
prec = cend - cp;
@@ -531,20 +674,29 @@ check_pos:
base += 2; /* FALL THROUGH */
case 'o':
base += 8;
+ need_format = FALSE;
parse_next_arg();
tmpval = force_number(arg);
- if (tmpval > ULONG_MAX || tmpval < LONG_MIN) {
+ /* this ugly cast fixes a (sunos) pcc problem. sigh. */
+ if (tmpval > (double) ((unsigned long) ULONG_MAX)
+ || tmpval < LONG_MIN) {
/* out of range - emergency use of %g format */
cs1 = 'g';
goto format_float;
}
- uval = (unsigned long)tmpval;
- if (have_prec != 0) /* ignore '0' flag if */
- fill = sp; /* precision given */
+ uval = (unsigned long) tmpval;
+ /*
+ * precision overrides '0' flags. however, for
+ * integer formats, precsion is minimum number of
+ * *digits*, not characters, thus we want to fill
+ * with zeroes.
+ */
+ if (have_prec)
+ fill = zero_string;
do {
*--cp = chbuf[uval % base];
uval /= base;
- } while (uval);
+ } while (uval > 0);
if (alt) {
if (base == 16) {
*--cp = cs1;
@@ -558,6 +710,8 @@ check_pos:
*--cp = '0';
}
base = 0;
+ if (prec > fw)
+ fw = prec;
prec = cend - cp;
pr_tail:
if (! lj) {
@@ -579,13 +733,14 @@ check_pos:
case 'e':
case 'f':
case 'E':
+ need_format = FALSE;
parse_next_arg();
tmpval = force_number(arg);
format_float:
free_temp(arg);
- if (have_prec == 0)
+ if (! have_prec)
prec = DEFAULT_G_PRECISION;
- chksize(fw + prec + 9); /* 9==slop */
+ chksize(fw + prec + 9); /* 9 == slop */
cp = cpbuf;
*cp++ = '%';
@@ -599,7 +754,7 @@ check_pos:
*cp++ = '0';
cp = strcpy(cp, "*.*") + 3;
*cp++ = cs1;
- *cp = '\0';
+ *cp = '\0';
#ifndef GFMT_WORKAROUND
(void) sprintf(obufout, cpbuf,
(int) fw, (int) prec, (double) tmpval);
@@ -620,20 +775,28 @@ check_pos:
break;
}
if (toofew)
- fatal("%s\n\t%s\n\t%*s%s",
+ fatal("%s\n\t`%s'\n\t%*s%s",
"not enough arguments to satisfy format string",
fmt_string, s1 - fmt_string - 2, "",
"^ ran out for this one"
);
}
- if (do_lint && carg != NULL)
- warning("too many arguments supplied for format string");
+ if (do_lint) {
+ if (need_format)
+ warning(
+ "printf format specifier does not have control letter");
+ if (carg != NULL)
+ warning(
+ "too many arguments supplied for format string");
+ }
bchunk(s0, s1 - s0);
r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED);
r->flags |= TEMP;
return r;
}
+/* do_sprintf --- perform sprintf */
+
NODE *
do_sprintf(tree)
NODE *tree;
@@ -646,6 +809,7 @@ NODE *tree;
return r;
}
+/* do_printf --- perform printf, including redirection */
void
do_printf(tree)
@@ -658,26 +822,27 @@ register NODE *tree;
int errflg; /* not used, sigh */
rp = redirect(tree->rnode, &errflg);
- if (rp) {
+ if (rp != NULL) {
fp = rp->fp;
- if (!fp)
+ if (fp == NULL)
return;
} else
return;
} else
fp = stdout;
tree = do_sprintf(tree->lnode);
- efwrite(tree->stptr, sizeof(char), tree->stlen, fp, "printf", rp , 1);
+ efwrite(tree->stptr, sizeof(char), tree->stlen, fp, "printf", rp, TRUE);
free_temp(tree);
}
+/* do_sqrt --- do the sqrt function */
+
NODE *
do_sqrt(tree)
NODE *tree;
{
NODE *tmp;
double arg;
- extern double sqrt P((double));
tmp = tree_eval(tree->lnode);
arg = (double) force_number(tmp);
@@ -687,6 +852,8 @@ NODE *tree;
return tmp_number((AWKNUM) sqrt(arg));
}
+/* do_substr --- do the substr function */
+
NODE *
do_substr(tree)
NODE *tree;
@@ -697,7 +864,7 @@ NODE *tree;
size_t length;
int is_long;
- t1 = tree_eval(tree->lnode);
+ t1 = force_string(tree_eval(tree->lnode));
t2 = tree_eval(tree->rnode->lnode);
if (tree->rnode->rnode == NULL) /* third arg. missing */
length = t1->stlen;
@@ -708,10 +875,14 @@ NODE *tree;
}
indx = (int) force_number(t2) - 1;
free_temp(t2);
- t1 = force_string(t1);
if (indx < 0)
indx = 0;
if (indx >= t1->stlen || (long) length <= 0) {
+ if (do_lint && indx >= t1->stlen)
+ warning("substr: position %d is past end of string",
+ indx);
+ if (do_lint && (long) length <= 0)
+ warning("substr: length %d <= 0", (long) length);
free_temp(t1);
return Nnull_string;
}
@@ -721,34 +892,54 @@ NODE *tree;
warning("substr: length %d at position %d exceeds length of first argument",
length, indx+1);
}
- r = tmp_string(t1->stptr + indx, length);
+ r = tmp_string(t1->stptr + indx, length);
free_temp(t1);
return r;
}
+/* do_strftime --- format a time stamp */
+
NODE *
do_strftime(tree)
NODE *tree;
{
- NODE *t1, *t2;
+ NODE *t1, *t2, *ret;
struct tm *tm;
time_t fclock;
- char buf[100];
-
- t1 = force_string(tree_eval(tree->lnode));
-
- if (tree->rnode == NULL) /* second arg. missing, default */
- (void) time(&fclock);
- else {
- t2 = tree_eval(tree->rnode->lnode);
- fclock = (time_t) force_number(t2);
- free_temp(t2);
+ char buf[BUFSIZ]; /* XXX - fixed length */
+ static char def_format[] = "%a %b %d %H:%M:%S %Z %Y";
+ char *format;
+
+ /* set defaults first */
+ format = def_format; /* traditional date format */
+ (void) time(&fclock); /* current time of day */
+
+ t1 = t2 = NULL;
+ if (tree != NULL) { /* have args */
+ if (tree->lnode != NULL) {
+ t1 = force_string(tree_eval(tree->lnode));
+ format = t1->stptr;
+ if (do_lint && t1->stlen == 0)
+ warning("strftime called with empty format string");
+ }
+
+ if (tree->rnode != NULL) {
+ t2 = tree_eval(tree->rnode->lnode);
+ fclock = (time_t) force_number(t2);
+ free_temp(t2);
+ }
}
+
tm = localtime(&fclock);
- return tmp_string(buf, strftime(buf, 100, t1->stptr, tm));
+ ret = tmp_string(buf, strftime(buf, 100, format, tm));
+ if (t1)
+ free_temp(t1);
+ return ret;
}
+/* do_systime --- get the time of day */
+
NODE *
do_systime(tree)
NODE *tree;
@@ -759,6 +950,8 @@ NODE *tree;
return tmp_number((AWKNUM) lclock);
}
+/* do_system --- run an external command */
+
NODE *
do_system(tree)
NODE *tree;
@@ -768,7 +961,7 @@ NODE *tree;
char *cmd;
char save;
- (void) flush_io (); /* so output is synchronous with gawk's */
+ (void) flush_io(); /* so output is synchronous with gawk's */
tmp = tree_eval(tree->lnode);
cmd = force_string(tmp)->stptr;
@@ -776,9 +969,9 @@ NODE *tree;
/* insure arg to system is zero-terminated */
/*
- * From: David Trueman <emory!cs.dal.ca!david>
+ * From: David Trueman <david@cs.dal.ca>
* To: arnold@cc.gatech.edu (Arnold Robbins)
- * Date: Wed, 3 Nov 1993 12:49:41 -0400
+ * Date: Wed, 3 Nov 1993 12:49:41 -0400
*
* It may not be necessary to save the character, but
* I'm not sure. It would normally be the field
@@ -803,6 +996,8 @@ NODE *tree;
extern NODE **fmt_list; /* declared in eval.c */
+/* do_print --- print items, separated by OFS, terminated with ORS */
+
void
do_print(tree)
register NODE *tree;
@@ -816,50 +1011,44 @@ register NODE *tree;
int errflg; /* not used, sigh */
rp = redirect(tree->rnode, &errflg);
- if (rp) {
+ if (rp != NULL) {
fp = rp->fp;
- if (!fp)
+ if (fp == NULL)
return;
} else
return;
} else
fp = stdout;
tree = tree->lnode;
- while (tree) {
+ while (tree != NULL) {
t1 = tree_eval(tree->lnode);
if (t1->flags & NUMBER) {
if (OFMTidx == CONVFMTidx)
(void) force_string(t1);
else {
-#ifndef GFMT_WORKAROUND
- char buf[100];
-
- (void) sprintf(buf, OFMT, t1->numbr);
- free_temp(t1);
- t1 = tmp_string(buf, strlen(buf));
-#else /* GFMT_WORKAROUND */
free_temp(t1);
t1 = format_tree(OFMT,
fmt_list[OFMTidx]->stlen,
tree);
-#endif /* GFMT_WORKAROUND */
}
}
- efwrite(t1->stptr, sizeof(char), t1->stlen, fp, "print", rp, 0);
+ efwrite(t1->stptr, sizeof(char), t1->stlen, fp, "print", rp, FALSE);
free_temp(t1);
tree = tree->rnode;
- if (tree) {
+ if (tree != NULL) {
s = OFS;
- if (OFSlen)
- efwrite(s, sizeof(char), (size_t)OFSlen,
- fp, "print", rp, 0);
+ if (OFSlen > 0)
+ efwrite(s, sizeof(char), (size_t) OFSlen,
+ fp, "print", rp, FALSE);
}
}
s = ORS;
- if (ORSlen)
- efwrite(s, sizeof(char), (size_t)ORSlen, fp, "print", rp, 1);
+ if (ORSlen > 0)
+ efwrite(s, sizeof(char), (size_t) ORSlen, fp, "print", rp, TRUE);
}
+/* do_tolower --- lower case a string */
+
NODE *
do_tolower(tree)
NODE *tree;
@@ -877,6 +1066,8 @@ NODE *tree;
return t2;
}
+/* do_toupper --- upper case a string */
+
NODE *
do_toupper(tree)
NODE *tree;
@@ -894,12 +1085,13 @@ NODE *tree;
return t2;
}
+/* do_atan2 --- do the atan2 function */
+
NODE *
do_atan2(tree)
NODE *tree;
{
NODE *t1, *t2;
- extern double atan2 P((double, double));
double d1, d2;
t1 = tree_eval(tree->lnode);
@@ -911,35 +1103,39 @@ NODE *tree;
return tmp_number((AWKNUM) atan2(d1, d2));
}
+/* do_sin --- do the sin function */
+
NODE *
do_sin(tree)
NODE *tree;
{
NODE *tmp;
- extern double sin P((double));
double d;
tmp = tree_eval(tree->lnode);
- d = sin((double)force_number(tmp));
+ d = sin((double) force_number(tmp));
free_temp(tmp);
return tmp_number((AWKNUM) d);
}
+/* do_cos --- do the cos function */
+
NODE *
do_cos(tree)
NODE *tree;
{
NODE *tmp;
- extern double cos P((double));
double d;
tmp = tree_eval(tree->lnode);
- d = cos((double)force_number(tmp));
+ d = cos((double) force_number(tmp));
free_temp(tmp);
return tmp_number((AWKNUM) d);
}
-static int firstrand = 1;
+/* do_rand --- do the rand function */
+
+static int firstrand = TRUE;
static char state[512];
/* ARGSUSED */
@@ -950,35 +1146,40 @@ NODE *tree;
if (firstrand) {
(void) initstate((unsigned) 1, state, sizeof state);
srandom(1);
- firstrand = 0;
+ firstrand = FALSE;
}
return tmp_number((AWKNUM) random() / GAWK_RANDOM_MAX);
}
+/* do_srand --- seed the random number generator */
+
NODE *
do_srand(tree)
NODE *tree;
{
NODE *tmp;
- static long save_seed = 0;
+ static long save_seed = 1;
long ret = save_seed; /* SVR4 awk srand returns previous seed */
- if (firstrand)
+ if (firstrand) {
(void) initstate((unsigned) 1, state, sizeof state);
- else
+ /* don't need to srandom(1), we're changing the seed below */
+ firstrand = FALSE;
+ } else
(void) setstate(state);
- if (!tree)
+ if (tree == NULL)
srandom((unsigned int) (save_seed = (long) time((time_t *) 0)));
else {
tmp = tree_eval(tree->lnode);
srandom((unsigned int) (save_seed = (long) force_number(tmp)));
free_temp(tmp);
}
- firstrand = 0;
return tmp_number((AWKNUM) ret);
}
+/* do_match --- match a regexp, set RSTART and RLENGTH */
+
NODE *
do_match(tree)
NODE *tree;
@@ -991,7 +1192,7 @@ NODE *tree;
t1 = force_string(tree_eval(tree->lnode));
tree = tree->rnode->lnode;
rp = re_update(tree);
- rstart = research(rp, t1->stptr, 0, t1->stlen, 1);
+ rstart = research(rp, t1->stptr, 0, t1->stlen, TRUE);
if (rstart >= 0) { /* match succeded */
rstart++; /* 1-based indexing */
rlength = REEND(rp, t1->stptr) - RESTART(rp, t1->stptr);
@@ -1007,10 +1208,16 @@ NODE *tree;
return tmp_number((AWKNUM) rstart);
}
+/* sub_common --- the common code (does the work) for sub, gsub, and gensub */
+
+/*
+ * NB: `howmany' conflicts with a SunOS macro in <sys/param.h>.
+ */
+
static NODE *
-sub_common(tree, global)
+sub_common(tree, how_many, backdigs)
NODE *tree;
-int global;
+int how_many, backdigs;
{
register char *scan;
register char *bp, *cp;
@@ -1032,9 +1239,12 @@ int global;
NODE *t; /* string to make sub. in; $0 if none given */
NODE *tmp;
NODE **lhs = &tree; /* value not used -- just different from NULL */
- int priv = 0;
+ int priv = FALSE;
Func_ptr after_assign = NULL;
+ int global = (how_many == -1);
+ long current;
+
tmp = tree->lnode;
rp = re_update(tmp);
@@ -1046,7 +1256,7 @@ int global;
t = force_string(tree_eval(tmp));
/* do the search early to avoid work on non-match */
- if (research(rp, t->stptr, 0, t->stlen, 1) == -1 ||
+ if (research(rp, t->stptr, 0, t->stlen, TRUE) == -1 ||
RESTART(rp, t->stptr) > t->stlen) {
free_temp(t);
return tmp_number((AWKNUM) 0.0);
@@ -1068,7 +1278,7 @@ int global;
tmp = dupnode(t);
t->flags = saveflags;
t = tmp;
- priv = 1;
+ priv = TRUE;
}
text = t->stptr;
textlen = t->stlen;
@@ -1078,7 +1288,7 @@ int global;
repl = s->stptr;
replend = repl + s->stlen;
repllen = replend - repl;
- emalloc(buf, char *, buflen + 2, "do_sub");
+ emalloc(buf, char *, buflen + 2, "sub_common");
buf[buflen] = '\0';
buf[buflen + 1] = '\0';
ampersands = 0;
@@ -1086,15 +1296,37 @@ int global;
if (*scan == '&') {
repllen--;
ampersands++;
- } else if (*scan == '\\'
- && (*(scan+1) == '&' || *(scan+1) == '\\')) {
- repllen--;
- scan++;
+ } else if (*scan == '\\') {
+ if (backdigs) { /* gensub, behave sanely */
+ if (isdigit(scan[1])) {
+ ampersands++;
+ scan++;
+ } else { /* \q for any q --> q */
+ repllen--;
+ scan++;
+ }
+ } else { /* (proposed) posix '96 mode */
+ if (strncmp(scan, "\\\\\\&", 4) == 0) {
+ /* \\\& --> \& */
+ repllen -= 2;
+ scan += 3;
+ } else if (strncmp(scan, "\\\\&", 3) == 0) {
+ /* \\& --> \<string> */
+ ampersands++;
+ repllen--;
+ scan += 2;
+ } else if (scan[1] == '&') {
+ /* \& --> & */
+ repllen--;
+ scan++;
+ } /* else
+ leave alone, it goes into the output */
+ }
}
}
bp = buf;
- for (;;) {
+ for (current = 1;; current++) {
matches++;
matchstart = t->stptr + RESTART(rp, t->stptr);
matchend = t->stptr + REEND(rp, t->stptr);
@@ -1108,37 +1340,81 @@ int global;
sofar = bp - buf;
while (buflen < (sofar + len + 1)) {
buflen *= 2;
- erealloc(buf, char *, buflen, "do_sub");
+ erealloc(buf, char *, buflen, "sub_common");
bp = buf + sofar;
}
for (scan = text; scan < matchstart; scan++)
*bp++ = *scan;
- for (scan = repl; scan < replend; scan++)
- if (*scan == '&')
- for (cp = matchstart; cp < matchend; cp++)
- *bp++ = *cp;
- else if (*scan == '\\'
- && (*(scan+1) == '&' || *(scan+1) == '\\')) {
- scan++;
- *bp++ = *scan;
- } else
- *bp++ = *scan;
-
+ if (global || current == how_many) {
+ /*
+ * If replacing all occurrences, or this is the
+ * match we want, copy in the replacement text,
+ * making substitutions as we go.
+ */
+ for (scan = repl; scan < replend; scan++)
+ if (*scan == '&')
+ for (cp = matchstart; cp < matchend; cp++)
+ *bp++ = *cp;
+ else if (*scan == '\\') {
+ if (backdigs) { /* gensub, behave sanely */
+ if (isdigit(scan[1])) {
+ int dig = scan[1] - '0';
+ char *start, *end;
+
+ start = t->stptr
+ + SUBPATSTART(rp, t->stptr, dig);
+ end = t->stptr
+ + SUBPATEND(rp, t->stptr, dig);
+
+ for (cp = start; cp < end; cp++)
+ *bp++ = *cp;
+ scan++;
+ } else /* \q for any q --> q */
+ *bp++ = *++scan;
+ } else { /* posix '96 mode, bleah */
+ if (strncmp(scan, "\\\\\\&", 4) == 0) {
+ /* \\\& --> \& */
+ *bp++ = '\\';
+ *bp++ = '&';
+ scan += 3;
+ } else if (strncmp(scan, "\\\\&", 3) == 0) {
+ /* \\& --> \<string> */
+ *bp++ = '\\';
+ for (cp = matchstart; cp < matchend; cp++)
+ *bp++ = *cp;
+ scan += 2;
+ } else if (scan[1] == '&') {
+ /* \& --> & */
+ *bp++ = '&';
+ scan++;
+ } else
+ *bp++ = *scan;
+ }
+ } else
+ *bp++ = *scan;
+ } else {
+ /*
+ * don't want this match, skip over it by copying
+ * in current text.
+ */
+ for (cp = matchstart; cp < matchend; cp++)
+ *bp++ = *cp;
+ }
/* catch the case of gsub(//, "blah", whatever), i.e. empty regexp */
- if (global && matchstart == matchend && matchend < text + textlen) {
+ if (matchstart == matchend && matchend < text + textlen) {
*bp++ = *matchend;
matchend++;
}
textlen = text + textlen - matchend;
text = matchend;
- if (!global || (long)textlen <= 0 ||
- research(rp, t->stptr, text-t->stptr, textlen, 1) == -1)
+ if ((current >= how_many && !global) || (long) textlen <= 0
+ || research(rp, t->stptr, text - t->stptr, textlen, TRUE) == -1)
break;
}
sofar = bp - buf;
if (buflen - sofar - textlen - 1) {
buflen = sofar + textlen + 2;
- erealloc(buf, char *, buflen, "do_sub");
+ erealloc(buf, char *, buflen, "sub_common");
bp = buf + sofar;
}
for (scan = matchend; scan < text + textlen; scan++)
@@ -1155,25 +1431,92 @@ int global;
unref(*lhs);
*lhs = t;
}
- if (after_assign)
+ if (after_assign != NULL)
(*after_assign)();
t->flags &= ~(NUM|NUMBER);
}
return tmp_number((AWKNUM) matches);
}
+/* do_gsub --- global substitution */
+
NODE *
do_gsub(tree)
NODE *tree;
{
- return sub_common(tree, 1);
+ return sub_common(tree, -1, FALSE);
}
+/* do_sub --- single substitution */
+
NODE *
do_sub(tree)
NODE *tree;
{
- return sub_common(tree, 0);
+ return sub_common(tree, 1, FALSE);
+}
+
+/* do_gensub --- fix up the tree for sub_common for the gensub function */
+
+NODE *
+do_gensub(tree)
+NODE *tree;
+{
+ NODE n1, n2, n3, *t, *tmp, *target, *ret;
+ long how_many = 1; /* default is one substitution */
+ double d;
+
+ /*
+ * We have to pull out the value of the global flag, and
+ * build up a tree without the flag in it, turning it into the
+ * kind of tree that sub_common() expects. It helps to draw
+ * a picture of this ...
+ */
+ n1 = *tree;
+ n2 = *(tree->rnode);
+ n1.rnode = & n2;
+
+ t = tree_eval(n2.rnode->lnode); /* value of global flag */
+
+ tmp = force_string(tree_eval(n2.rnode->rnode->lnode)); /* target */
+
+ /*
+ * We make copy of the original target string, and pass that
+ * in to sub_common() as the target to make the substitution in.
+ * We will then return the result string as the return value of
+ * this function.
+ */
+ target = tmp_string(tmp->stptr, tmp->stlen);
+ free_temp(tmp);
+
+ n3 = *(n2.rnode->rnode);
+ n3.lnode = target;
+ n2.rnode = & n3;
+
+ if ((t->flags & (STR|STRING)) != 0) {
+ if (t->stlen > 0 && (t->stptr[0] == 'g' || t->stptr[0] == 'G'))
+ how_many = -1;
+ else
+ how_many = 1;
+ } else {
+ d = force_number(t);
+ if (d > 0)
+ how_many = d;
+ else
+ how_many = 1;
+ }
+
+ free_temp(t);
+
+ ret = sub_common(&n1, how_many, TRUE);
+ free_temp(ret);
+
+ /*
+ * Note that we don't care what sub_common() returns, since the
+ * easiest thing for the programmer is to return the string, even
+ * if no substitutions were done.
+ */
+ return target;
}
#ifdef GFMT_WORKAROUND
@@ -1194,57 +1537,60 @@ double g; /* value to format */
char dform[40];
register char *gpos;
register char *d, *e, *p;
- int again = 0;
+ int again = FALSE;
strncpy(dform, format, sizeof dform - 1);
dform[sizeof dform - 1] = '\0';
gpos = strrchr(dform, '.');
- if (g == 0.0 && alt == 0) { /* easy special case */
+ if (g == 0.0 && ! alt) { /* easy special case */
*gpos++ = 'd';
*gpos = '\0';
(void) sprintf(buf, dform, fwidth, 0);
return;
}
- gpos += 2; /* advance to location of 'g' in the format */
+
+ /* advance to location of 'g' in the format */
+ while (*gpos && *gpos != 'g' && *gpos != 'G')
+ gpos++;
if (prec <= 0) /* negative precision is ignored */
prec = (prec < 0 ? DEFAULT_G_PRECISION : 1);
if (*gpos == 'G')
- again = 1;
+ again = TRUE;
/* start with 'e' format (it'll provide nice exponent) */
*gpos = 'e';
- prec -= 1;
+ prec--;
(void) sprintf(buf, dform, fwidth, prec, g);
if ((e = strrchr(buf, 'e')) != NULL) { /* find exponent */
- int exp = atoi(e+1); /* fetch exponent */
- if (exp >= -4 && exp <= prec) { /* per K&R2, B1.2 */
+ int expn = atoi(e+1); /* fetch exponent */
+ if (expn >= -4 && expn <= prec) { /* per K&R2, B1.2 */
/* switch to 'f' format and re-do */
*gpos = 'f';
- prec -= exp; /* decimal precision */
+ prec -= expn; /* decimal precision */
(void) sprintf(buf, dform, fwidth, prec, g);
e = buf + strlen(buf);
while (*--e == ' ')
continue;
- e += 1;
+ e++;
}
- else if (again != 0)
+ else if (again)
*gpos = 'E';
/* if 'alt' in force, then trailing zeros are not removed */
- if (alt == 0 && (d = strrchr(buf, '.')) != NULL) {
+ if (! alt && (d = strrchr(buf, '.')) != NULL) {
/* throw away an excess of precision */
for (p = e; p > d && *--p == '0'; )
- prec -= 1;
+ prec--;
if (d == p)
- prec -= 1;
+ prec--;
if (prec < 0)
prec = 0;
/* and do that once again */
- again = 1;
+ again = TRUE;
}
- if (again != 0)
+ if (again)
(void) sprintf(buf, dform, fwidth, prec, g);
}
}