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

builtin.c

Go to the documentation of this file.
00001 /*
00002  * builtin.c - Builtin functions and various utility procedures 
00003  */
00004 
00005 /* 
00006  * Copyright (C) 1986, 1988, 1989, 1991-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 
00027 #include "awk.h"
00028 #if defined(HAVE_FCNTL_H)
00029 #include <fcntl.h>
00030 #endif
00031 #undef HUGE
00032 #undef CHARBITS
00033 #undef INTBITS
00034 #if HAVE_INTTYPES_H
00035 # include <inttypes.h>
00036 #else
00037 # if HAVE_STDINT_H
00038 #  include <stdint.h>
00039 # endif
00040 #endif
00041 #include <math.h>
00042 #include "random.h"
00043 
00044 #ifndef CHAR_BIT
00045 # define CHAR_BIT 8
00046 #endif
00047 
00048 /* The extra casts work around common compiler bugs.  */
00049 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
00050 /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
00051    It is necessary at least when t == time_t.  */
00052 #define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
00053                               ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
00054 #define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))
00055 
00056 #ifndef INTMAX_MIN
00057 # define INTMAX_MIN TYPE_MINIMUM (intmax_t)
00058 #endif
00059 #ifndef UINTMAX_MAX
00060 # define UINTMAX_MAX TYPE_MAXIMUM (uintmax_t)
00061 #endif
00062 
00063 #ifndef SIZE_MAX        /* C99 constant, can't rely on it everywhere */
00064 #define SIZE_MAX ((size_t) -1)
00065 #endif
00066 
00067 /* can declare these, since we always use the random shipped with gawk */
00068 extern char *initstate P((unsigned long seed, char *state, long n));
00069 extern char *setstate P((char *state));
00070 extern long random P((void));
00071 extern void srandom P((unsigned long seed));
00072 
00073 extern NODE **fields_arr;
00074 extern int output_is_tty;
00075 
00076 static NODE *sub_common P((NODE *tree, long how_many, int backdigs));
00077 
00078 #ifdef _CRAY
00079 /* Work around a problem in conversion of doubles to exact integers. */
00080 #include <float.h>
00081 #define Floor(n) floor((n) * (1.0 + DBL_EPSILON))
00082 #define Ceil(n) ceil((n) * (1.0 + DBL_EPSILON))
00083 
00084 /* Force the standard C compiler to use the library math functions. */
00085 extern double exp(double);
00086 double (*Exp)() = exp;
00087 #define exp(x) (*Exp)(x)
00088 extern double log(double);
00089 double (*Log)() = log;
00090 #define log(x) (*Log)(x)
00091 #else
00092 #define Floor(n) floor(n)
00093 #define Ceil(n) ceil(n)
00094 #endif
00095 
00096 #define DEFAULT_G_PRECISION 6
00097 
00098 #ifdef GFMT_WORKAROUND
00099 /* semi-temporary hack, mostly to gracefully handle VMS */
00100 static void sgfmt P((char *buf, const char *format, int alt,
00101                      int fwidth, int precision, double value));
00102 #endif /* GFMT_WORKAROUND */
00103 
00104 /*
00105  * Since we supply the version of random(), we know what
00106  * value to use here.
00107  */
00108 #define GAWK_RANDOM_MAX 0x7fffffffL
00109 
00110 static void efwrite P((const void *ptr, size_t size, size_t count, FILE *fp,
00111                        const char *from, struct redirect *rp, int flush));
00112 
00113 /* efwrite --- like fwrite, but with error checking */
00114 
00115 static void
00116 efwrite(const void *ptr,
00117         size_t size,
00118         size_t count,
00119         FILE *fp,
00120         const char *from,
00121         struct redirect *rp,
00122         int flush)
00123 {
00124         errno = 0;
00125         if (fwrite(ptr, size, count, fp) != count)
00126                 goto wrerror;
00127         if (flush
00128           && ((fp == stdout && output_is_tty)
00129            || (rp != NULL && (rp->flag & RED_NOBUF)))) {
00130                 fflush(fp);
00131                 if (ferror(fp))
00132                         goto wrerror;
00133         }
00134         return;
00135 
00136 wrerror:
00137         fatal(_("%s to \"%s\" failed (%s)"), from,
00138                 rp ? rp->value : _("standard output"),
00139                 errno ? strerror(errno) : _("reason unknown"));
00140 }
00141 
00142 /* do_exp --- exponential function */
00143 
00144 NODE *
00145 do_exp(NODE *tree)
00146 {
00147         NODE *tmp;
00148         double d, res;
00149 
00150         tmp = tree_eval(tree->lnode);
00151         if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
00152                 lintwarn(_("exp: received non-numeric argument"));
00153         d = force_number(tmp);
00154         free_temp(tmp);
00155         errno = 0;
00156         res = exp(d);
00157         if (errno == ERANGE)
00158                 warning(_("exp: argument %g is out of range"), d);
00159         return tmp_number((AWKNUM) res);
00160 }
00161 
00162 /* stdfile --- return fp for a standard file */
00163 
00164 /*
00165  * This function allows `fflush("/dev/stdout")' to work.
00166  * The other files will be available via getredirect().
00167  * /dev/stdin is not included, since fflush is only for output.
00168  */
00169 
00170 static FILE *
00171 stdfile(const char *name, size_t len)
00172 {
00173         if (len == 11) {
00174                 if (STREQN(name, "/dev/stderr", 11))
00175                         return stderr;
00176                 else if (STREQN(name, "/dev/stdout", 11))
00177                         return stdout;
00178         }
00179 
00180         return NULL;
00181 }
00182 
00183 /* do_fflush --- flush output, either named file or pipe or everything */
00184 
00185 NODE *
00186 do_fflush(NODE *tree)
00187 {
00188         struct redirect *rp;
00189         NODE *tmp;
00190         FILE *fp;
00191         int status = 0;
00192         const char *file;
00193 
00194         /* fflush() --- flush stdout */
00195         if (tree == NULL) {
00196                 status = fflush(stdout);
00197                 return tmp_number((AWKNUM) status);
00198         }
00199 
00200         tmp = tree_eval(tree->lnode);
00201         tmp = force_string(tmp);
00202         file = tmp->stptr;
00203 
00204         /* fflush("") --- flush all */
00205         if (tmp->stlen == 0) {
00206                 status = flush_io();
00207                 free_temp(tmp);
00208                 return tmp_number((AWKNUM) status);
00209         }
00210 
00211         rp = getredirect(tmp->stptr, tmp->stlen);
00212         status = -1;
00213         if (rp != NULL) {
00214                 if ((rp->flag & (RED_WRITE|RED_APPEND)) == 0) {
00215                         if (rp->flag & RED_PIPE)
00216                                 warning(_("fflush: cannot flush: pipe `%s' opened for reading, not writing"),
00217                                         file);
00218                         else
00219                                 warning(_("fflush: cannot flush: file `%s' opened for reading, not writing"),
00220                                         file);
00221                         free_temp(tmp);
00222                         return tmp_number((AWKNUM) status);
00223                 }
00224                 fp = rp->fp;
00225                 if (fp != NULL)
00226                         status = fflush(fp);
00227         } else if ((fp = stdfile(tmp->stptr, tmp->stlen)) != NULL) {
00228                 status = fflush(fp);
00229         } else {
00230                 status = -1;
00231                 warning(_("fflush: `%s' is not an open file, pipe or co-process"), file);
00232         }
00233         free_temp(tmp);
00234         return tmp_number((AWKNUM) status);
00235 }
00236 
00237 #ifdef MBS_SUPPORT
00238 /* strncasecmpmbs --- like strncasecmp(multibyte string version)  */
00239 int
00240 strncasecmpmbs(const char *s1, mbstate_t mbs1, const char *s2,
00241                            mbstate_t mbs2, size_t n)
00242 {
00243         int i1, i2, mbclen1, mbclen2, gap;
00244         wchar_t wc1, wc2;
00245         for (i1 = i2 = 0 ; i1 < n && i2 < n ;i1 += mbclen1, i2 += mbclen2) {
00246                 mbclen1 = mbrtowc(&wc1, s1 + i1, n - i1, &mbs1);
00247                 if (mbclen1 == (size_t) -1 || mbclen1 == (size_t) -2 || mbclen1 == 0) {
00248                         /* We treat it as a singlebyte character.  */
00249                         mbclen1 = 1;
00250                         wc1 = s1[i1];
00251                 }
00252                 mbclen2 = mbrtowc(&wc2, s2 + i2, n - i2, &mbs2);
00253                 if (mbclen2 == (size_t) -1 || mbclen2 == (size_t) -2 || mbclen2 == 0) {
00254                         /* We treat it as a singlebyte character.  */
00255                         mbclen2 = 1;
00256                         wc2 = s2[i2];
00257                 }
00258                 if ((gap = towlower(wc1) - towlower(wc2)) != 0)
00259                         /* s1 and s2 are not equivalent.  */
00260                         return gap;
00261         }
00262         /* s1 and s2 are equivalent.  */
00263         return 0;
00264 }
00265 
00266 /* Inspect the buffer `src' and write the index of each byte to `dest'.
00267    Caller must allocate `dest'.
00268    e.g. str = <mb1(1)>, <mb1(2)>, a, b, <mb2(1)>, <mb2(2)>, <mb2(3)>, c
00269         where mb(i) means the `i'-th byte of a multibyte character.
00270                 dest =       1,        2, 1, 1,        1,        2,        3. 1
00271 */
00272 static void
00273 index_multibyte_buffer(char* src, char* dest, int len)
00274 {
00275         int idx, prev_idx;
00276         mbstate_t mbs, prevs;
00277         memset(&prevs, 0, sizeof(mbstate_t));
00278 
00279         for (idx = prev_idx = 0 ; idx < len ; idx++) {
00280                 size_t mbclen;
00281                 mbs = prevs;
00282                 mbclen = mbrlen(src + prev_idx, idx - prev_idx + 1, &mbs);
00283                 if (mbclen == (size_t) -1 || mbclen == 1 || mbclen == 0) {
00284                         /* singlebyte character.  */
00285                         mbclen = 1;
00286                         prev_idx = idx + 1;
00287                 } else if (mbclen == (size_t) -2) {
00288                         /* a part of a multibyte character.  */
00289                         mbclen = idx - prev_idx + 1;
00290                 } else if (mbclen > 1) {
00291                         /* the end of a multibyte character.  */
00292                         prev_idx = idx + 1;
00293                         prevs = mbs;
00294                 } else {
00295                         /* Can't reach.  */
00296                 }
00297                 dest[idx] = mbclen;
00298     }
00299 }
00300 #endif
00301 
00302 /* do_index --- find index of a string */
00303 
00304 NODE *
00305 do_index(NODE *tree)
00306 {
00307         NODE *s1, *s2;
00308         register const char *p1, *p2;
00309         register size_t l1, l2;
00310         long ret;
00311 #ifdef MBS_SUPPORT
00312         size_t mbclen = 0;
00313         mbstate_t mbs1, mbs2;
00314         if (gawk_mb_cur_max > 1) {
00315                 memset(&mbs1, 0, sizeof(mbstate_t));
00316                 memset(&mbs2, 0, sizeof(mbstate_t));
00317         }
00318 #endif
00319 
00320 
00321         s1 = tree_eval(tree->lnode);
00322         s2 = tree_eval(tree->rnode->lnode);
00323         if (do_lint) {
00324                 if ((s1->flags & (STRING|STRCUR)) == 0)
00325                         lintwarn(_("index: received non-string first argument"));
00326                 if ((s2->flags & (STRING|STRCUR)) == 0)
00327                         lintwarn(_("index: received non-string second argument"));
00328         }
00329         force_string(s1);
00330         force_string(s2);
00331         p1 = s1->stptr;
00332         p2 = s2->stptr;
00333         l1 = s1->stlen;
00334         l2 = s2->stlen;
00335         ret = 0;
00336 
00337         /*
00338          * Icky special case, index(foo, "") should return 1,
00339          * since both bwk awk and mawk do, and since match("foo", "")
00340          * returns 1. This makes index("", "") work, too, fwiw.
00341          */
00342         if (l2 == 0) {
00343                 ret = 1;
00344                 goto out;
00345         }
00346 
00347         /* IGNORECASE will already be false if posix */
00348         if (IGNORECASE) {
00349                 while (l1 > 0) {
00350                         if (l2 > l1)
00351                                 break;
00352 #ifdef MBS_SUPPORT
00353                         if (gawk_mb_cur_max > 1) {
00354                                 if (strncasecmpmbs(p1, mbs1, p2, mbs2, l2) == 0) {
00355                                         ret = 1 + s1->stlen - l1;
00356                                         break;
00357                                 }
00358                                 /* Update l1, and p1.  */
00359                                 mbclen = mbrlen(p1, l1, &mbs1);
00360                                 if ((mbclen == 1) || (mbclen == (size_t) -1)
00361                                         || (mbclen == (size_t) -2) || (mbclen == 0)) {
00362                                         /* We treat it as a singlebyte character.  */
00363                                         mbclen = 1;
00364                                 }
00365                                 l1 -= mbclen;
00366                                 p1 += mbclen;
00367                         } else {
00368 #endif
00369                         if (casetable[(unsigned char)*p1] == casetable[(unsigned char)*p2]
00370                             && (l2 == 1 || strncasecmp(p1, p2, l2) == 0)) {
00371                                 ret = 1 + s1->stlen - l1;
00372                                 break;
00373                         }
00374                         l1--;
00375                         p1++;
00376 #ifdef MBS_SUPPORT
00377                         }
00378 #endif
00379                 }
00380         } else {
00381                 while (l1 > 0) {
00382                         if (l2 > l1)
00383                                 break;
00384                         if (*p1 == *p2
00385                             && (l2 == 1 || STREQN(p1, p2, l2))) {
00386                                 ret = 1 + s1->stlen - l1;
00387                                 break;
00388                         }
00389 #ifdef MBS_SUPPORT
00390                         if (gawk_mb_cur_max > 1) {
00391                                 mbclen = mbrlen(p1, l1, &mbs1);
00392                                 if ((mbclen == 1) || (mbclen == (size_t) -1) ||
00393                                         (mbclen == (size_t) -2) || (mbclen == 0)) {
00394                                         /* We treat it as a singlebyte character.  */
00395                                         mbclen = 1;
00396                                 }
00397                                 l1 -= mbclen;
00398                                 p1 += mbclen;
00399                         } else {
00400                                 l1--;
00401                                 p1++;
00402                         }
00403 #else
00404                         l1--;
00405                         p1++;
00406 #endif
00407                 }
00408         }
00409 out:
00410         free_temp(s1);
00411         free_temp(s2);
00412         return tmp_number((AWKNUM) ret);
00413 }
00414 
00415 /* double_to_int --- convert double to int, used several places */
00416 
00417 double
00418 double_to_int(double d)
00419 {
00420         if (d >= 0)
00421                 d = Floor(d);
00422         else
00423                 d = Ceil(d);
00424         return d;
00425 }
00426 
00427 /* do_int --- convert double to int for awk */
00428 
00429 NODE *
00430 do_int(NODE *tree)
00431 {
00432         NODE *tmp;
00433         double d;
00434 
00435         tmp = tree_eval(tree->lnode);
00436         if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
00437                 lintwarn(_("int: received non-numeric argument"));
00438         d = force_number(tmp);
00439         d = double_to_int(d);
00440         free_temp(tmp);
00441         return tmp_number((AWKNUM) d);
00442 }
00443 
00444 /* do_length --- length of a string or $0 */
00445 
00446 NODE *
00447 do_length(NODE *tree)
00448 {
00449         NODE *tmp;
00450         size_t len;
00451 
00452         tmp = tree_eval(tree->lnode);
00453         if (do_lint && (tmp->flags & (STRING|STRCUR)) == 0)
00454                 lintwarn(_("length: received non-string argument"));
00455         len = force_string(tmp)->stlen;
00456         free_temp(tmp);
00457         return tmp_number((AWKNUM) len);
00458 }
00459 
00460 /* do_log --- the log function */
00461 
00462 NODE *
00463 do_log(NODE *tree)
00464 {
00465         NODE *tmp;
00466         double d, arg;
00467 
00468         tmp = tree_eval(tree->lnode);
00469         if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
00470                 lintwarn(_("log: received non-numeric argument"));
00471         arg = (double) force_number(tmp);
00472         if (arg < 0.0)
00473                 warning(_("log: received negative argument %g"), arg);
00474         d = log(arg);
00475         free_temp(tmp);
00476         return tmp_number((AWKNUM) d);
00477 }
00478 
00479 /*
00480  * format_tree() formats nodes of a tree, starting with a left node,
00481  * and accordingly to a fmt_string providing a format like in
00482  * printf family from C library.  Returns a string node which value
00483  * is a formatted string.  Called by  sprintf function.
00484  *
00485  * It is one of the uglier parts of gawk.  Thanks to Michal Jaegermann
00486  * for taming this beast and making it compatible with ANSI C.
00487  */
00488 
00489 NODE *
00490 format_tree(
00491         const char *fmt_string,
00492         size_t n0,
00493         register NODE *carg,
00494         long num_args)
00495 {
00496 /* copy 'l' bytes from 's' to 'obufout' checking for space in the process */
00497 /* difference of pointers should be of ptrdiff_t type, but let us be kind */
00498 #define bchunk(s, l) if (l) { \
00499         while ((l) > ofre) { \
00500                 long olen = obufout - obuf; \
00501                 erealloc(obuf, char *, osiz * 2, "format_tree"); \
00502                 ofre += osiz; \
00503                 osiz *= 2; \
00504                 obufout = obuf + olen; \
00505         } \
00506         memcpy(obufout, s, (size_t) (l)); \
00507         obufout += (l); \
00508         ofre -= (l); \
00509 }
00510 
00511 /* copy one byte from 's' to 'obufout' checking for space in the process */
00512 #define bchunk_one(s) { \
00513         if (ofre < 1) { \
00514                 long olen = obufout - obuf; \
00515                 erealloc(obuf, char *, osiz * 2, "format_tree"); \
00516                 ofre += osiz; \
00517                 osiz *= 2; \
00518                 obufout = obuf + olen; \
00519         } \
00520         *obufout++ = *s; \
00521         --ofre; \
00522 }
00523 
00524 /* Is there space for something L big in the buffer? */
00525 #define chksize(l)  if ((l) > ofre) { \
00526         long olen = obufout - obuf; \
00527         erealloc(obuf, char *, osiz * 2, "format_tree"); \
00528         obufout = obuf + olen; \
00529         ofre += osiz; \
00530         osiz *= 2; \
00531 }
00532 
00533         static NODE **the_args = 0;
00534         static size_t args_size = 0;
00535         size_t cur_arg = 0;
00536 
00537         auto NODE **save_args = 0;
00538         auto size_t save_args_size = 0;
00539         static int call_level = 0;
00540 
00541         NODE *r;
00542         int i;
00543         int toofew = FALSE;
00544         char *obuf, *obufout;
00545         size_t osiz, ofre;
00546         const char *chbuf;
00547         const char *s0, *s1;
00548         int cs1;
00549         NODE *arg;
00550         long fw, prec, argnum;
00551         int used_dollar;
00552         int lj, alt, big, bigbig, small, have_prec, need_format;
00553         long *cur = NULL;
00554 #ifdef sun386           /* Can't cast unsigned (int/long) from ptr->value */
00555         long tmp_uval;  /* on 386i 4.0.1 C compiler -- it just hangs */
00556 #endif
00557         uintmax_t uval;
00558         int sgn;
00559         int base = 0;
00560         char cpbuf[30];         /* if we have numbers bigger than 30 */
00561         char *cend = &cpbuf[30];/* chars, we lose, but seems unlikely */
00562         char *cp;
00563         const char *fill;
00564         double tmpval;
00565         char signchar = FALSE;
00566         size_t len;
00567         int zero_flag = FALSE;
00568         static const char sp[] = " ";
00569         static const char zero_string[] = "0";
00570         static const char lchbuf[] = "0123456789abcdef";
00571         static const char Uchbuf[] = "0123456789ABCDEF";
00572 
00573 #define INITIAL_OUT_SIZE        512
00574         emalloc(obuf, char *, INITIAL_OUT_SIZE, "format_tree");
00575         obufout = obuf;
00576         osiz = INITIAL_OUT_SIZE;
00577         ofre = osiz - 1;
00578 
00579         /*
00580          * Icky problem.  If the args make a nested call to printf/sprintf,
00581          * we end up clobbering the static variable `the_args'.  Not good.
00582          * We don't just malloc and free the_args each time, since most of the
00583          * time there aren't nested calls.  But if this is a nested call,
00584          * save the memory pointed to by the_args and allocate a fresh
00585          * array.  Then free it on end.
00586          */
00587         if (++call_level > 1) { /* nested */
00588                 save_args = the_args;
00589                 save_args_size = args_size;
00590 
00591                 args_size = 0;  /* force fresh allocation */
00592         }
00593 
00594         if (args_size == 0) {
00595                 /* allocate array */
00596                 emalloc(the_args, NODE **, (num_args+1) * sizeof(NODE *), "format_tree");
00597                 args_size = num_args + 1;
00598         } else if (num_args + 1 > args_size) {
00599                 /* grow it */
00600                 erealloc(the_args, NODE **, (num_args+1) * sizeof(NODE *), "format_tree");
00601                 args_size = num_args + 1;
00602         }
00603 
00604 
00605         /* fill it in */
00606         /*
00607          * We ignore the_args[0] since format strings use
00608          * 1-based numbers to indicate the arguments.  It's
00609          * easiest to just convert to int and index, without
00610          * having to remember to subtract 1.
00611          */
00612         memset(the_args, '\0', num_args * sizeof(NODE *));
00613         for (i = 1; carg != NULL; i++, carg = carg->rnode) {
00614                 NODE *tmp;
00615 
00616                 /* Here lies the wumpus's other brother. R.I.P. */
00617                 tmp = tree_eval(carg->lnode);
00618                 the_args[i] = dupnode(tmp);
00619                 free_temp(tmp);
00620         }
00621         assert(i == num_args);
00622         cur_arg = 1;
00623 
00624         /*
00625          * Check first for use of `count$'.
00626          * If plain argument retrieval was used earlier, choke.
00627          *      Otherwise, return the requested argument.
00628          * If not `count$' now, but it was used earlier, choke.
00629          * If this format is more than total number of args, choke.
00630          * Otherwise, return the current argument.
00631          */
00632 #define parse_next_arg() { \
00633         if (argnum > 0) { \
00634                 if (cur_arg > 1) \
00635                         fatal(_("must use `count$' on all formats or none")); \
00636                 arg = the_args[argnum]; \
00637         } else if (used_dollar) { \
00638                 fatal(_("must use `count$' on all formats or none")); \
00639                 arg = 0; /* shutup the compiler */ \
00640         } else if (cur_arg >= num_args) { \
00641                 arg = 0; /* shutup the compiler */ \
00642                 toofew = TRUE; \
00643                 break; \
00644         } else { \
00645                 arg = the_args[cur_arg]; \
00646                 cur_arg++; \
00647         } \
00648 }
00649 
00650         need_format = FALSE;
00651         used_dollar = FALSE;
00652 
00653         s0 = s1 = fmt_string;
00654         while (n0-- > 0) {
00655                 if (*s1 != '%') {
00656                         s1++;
00657                         continue;
00658                 }
00659                 need_format = TRUE;
00660                 bchunk(s0, s1 - s0);
00661                 s0 = s1;
00662                 cur = &fw;
00663                 fw = 0;
00664                 prec = 0;
00665                 argnum = 0;
00666                 have_prec = FALSE;
00667                 signchar = FALSE;
00668                 zero_flag = FALSE;
00669                 lj = alt = big = bigbig = small = FALSE;
00670                 fill = sp;
00671                 cp = cend;
00672                 chbuf = lchbuf;
00673                 s1++;
00674 
00675 retry:
00676                 if (n0-- == 0)  /* ran out early! */
00677                         break;
00678 
00679                 switch (cs1 = *s1++) {
00680                 case (-1):      /* dummy case to allow for checking */
00681 check_pos:
00682                         if (cur != &fw)
00683                                 break;          /* reject as a valid format */
00684                         goto retry;
00685                 case '%':
00686                         need_format = FALSE;
00687                         /*
00688                          * 29 Oct. 2002:
00689                          * The C99 standard pages 274 and 279 seem to imply that
00690                          * since there's no arg converted, the field width doesn't
00691                          * apply.  The code already was that way, but this
00692                          * comment documents it, at least in the code.
00693                          */
00694                         bchunk_one("%");
00695                         s0 = s1;
00696                         break;
00697 
00698                 case '0':
00699                         /*
00700                          * Only turn on zero_flag if we haven't seen
00701                          * the field width or precision yet.  Otherwise,
00702                          * screws up floating point formatting.
00703                          */
00704                         if (cur == & fw)
00705                                 zero_flag = TRUE;
00706                         if (lj)
00707                                 goto retry;
00708                         /* FALL through */
00709                 case '1':
00710                 case '2':
00711                 case '3':
00712                 case '4':
00713                 case '5':
00714                 case '6':
00715                 case '7':
00716                 case '8':
00717                 case '9':
00718                         if (cur == NULL)
00719                                 break;
00720                         if (prec >= 0)
00721                                 *cur = cs1 - '0';
00722                         /*
00723                          * with a negative precision *cur is already set
00724                          * to -1, so it will remain negative, but we have
00725                          * to "eat" precision digits in any case
00726                          */
00727                         while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
00728                                 --n0;
00729                                 *cur = *cur * 10 + *s1++ - '0';
00730                         }
00731                         if (prec < 0)   /* negative precision is discarded */
00732                                 have_prec = FALSE;
00733                         if (cur == &prec)
00734                                 cur = NULL;
00735                         if (n0 == 0)    /* badly formatted control string */
00736                                 continue;
00737                         goto retry;
00738                 case '$':
00739                         if (do_traditional)
00740                                 fatal(_("`$' is not permitted in awk formats"));
00741                         if (cur == &fw) {
00742                                 argnum = fw;
00743                                 fw = 0;
00744                                 used_dollar = TRUE;
00745                                 if (argnum <= 0)
00746                                         fatal(_("arg count with `$' must be > 0"));
00747                                 if (argnum >= num_args)
00748                                         fatal(_("arg count %ld greater than total number of supplied arguments"), argnum);
00749                         } else
00750                                 fatal(_("`$' not permitted after period in format"));
00751                         goto retry;
00752                 case '*':
00753                         if (cur == NULL)
00754                                 break;
00755                         if (! do_traditional && ISDIGIT(*s1)) {
00756                                 int val = 0;
00757 
00758                                 for (; n0 > 0 && *s1 && ISDIGIT(*s1); s1++, n0--) {
00759                                         val *= 10;
00760                                         val += *s1 - '0';
00761                                 }
00762                                 if (*s1 != '$') {
00763                                         fatal(_("no `$' supplied for positional field width or precision"));
00764                                 } else {
00765                                         s1++;
00766                                         n0--;
00767                                 }
00768 
00769                                 arg = the_args[val];
00770                         } else {
00771                                 parse_next_arg();
00772                         }
00773                         *cur = force_number(arg);
00774                         if (*cur < 0 && cur == &fw) {
00775                                 *cur = -*cur;
00776                                 lj++;
00777                         }
00778                         if (cur == &prec) {
00779                                 if (*cur >= 0)
00780                                         have_prec = TRUE;
00781                                 else
00782                                         have_prec = FALSE;
00783                                 cur = NULL;
00784                         }
00785                         goto retry;
00786                 case ' ':               /* print ' ' or '-' */
00787                                         /* 'space' flag is ignored */
00788                                         /* if '+' already present  */
00789                         if (signchar != FALSE) 
00790                                 goto check_pos;
00791                         /* FALL THROUGH */
00792                 case '+':               /* print '+' or '-' */
00793                         signchar = cs1;
00794                         goto check_pos;
00795                 case '-':
00796                         if (prec < 0)
00797                                 break;
00798                         if (cur == &prec) {
00799                                 prec = -1;
00800                                 goto retry;
00801                         }
00802                         fill = sp;      /* if left justified then other */
00803                         lj++;           /* filling is ignored */
00804                         goto check_pos;
00805                 case '.':
00806                         if (cur != &fw)
00807                                 break;
00808                         cur = &prec;
00809                         have_prec = TRUE;
00810                         goto retry;
00811                 case '#':
00812                         alt = TRUE;
00813                         goto check_pos;
00814                 case 'l':
00815                         if (big)
00816                                 break;
00817                         else {
00818                                 static int warned = FALSE;
00819                                 
00820                                 if (do_lint && ! warned) {
00821                                         lintwarn(_("`l' is meaningless in awk formats; ignored"));
00822                                         warned = TRUE;
00823                                 }
00824                                 if (do_posix)
00825                                         fatal(_("`l' is not permitted in POSIX awk formats"));
00826                         }
00827                         big = TRUE;
00828                         goto retry;
00829                 case 'L':
00830                         if (bigbig)
00831                                 break;
00832                         else {
00833                                 static int warned = FALSE;
00834                                 
00835                                 if (do_lint && ! warned) {
00836                                         lintwarn(_("`L' is meaningless in awk formats; ignored"));
00837                                         warned = TRUE;
00838                                 }
00839                                 if (do_posix)
00840                                         fatal(_("`L' is not permitted in POSIX awk formats"));
00841                         }
00842                         bigbig = TRUE;
00843                         goto retry;
00844                 case 'h':
00845                         if (small)
00846                                 break;
00847                         else {
00848                                 static int warned = FALSE;
00849                                 
00850                                 if (do_lint && ! warned) {
00851                                         lintwarn(_("`h' is meaningless in awk formats; ignored"));
00852                                         warned = TRUE;
00853                                 }
00854                                 if (do_posix)
00855                                         fatal(_("`h' is not permitted in POSIX awk formats"));
00856                         }
00857                         small = TRUE;
00858                         goto retry;
00859                 case 'c':
00860                         need_format = FALSE;
00861                         if (zero_flag && ! lj)
00862                                 fill = zero_string;
00863                         parse_next_arg();
00864                         /* user input that looks numeric is numeric */
00865                         if ((arg->flags & (MAYBE_NUM|NUMBER)) == MAYBE_NUM)
00866                                 (void) force_number(arg);
00867                         if (arg->flags & NUMBER) {
00868 #ifdef sun386
00869                                 tmp_uval = arg->numbr; 
00870                                 uval = (unsigned long) tmp_uval;
00871 #else
00872                                 uval = (uintmax_t) arg->numbr;
00873 #endif
00874                                 cpbuf[0] = uval;
00875                                 prec = 1;
00876                                 cp = cpbuf;
00877                                 goto pr_tail;
00878                         }
00879                         /*
00880                          * As per POSIX, only output first character of a
00881                          * string value.  Thus, we ignore any provided
00882                          * precision, forcing it to 1.  (Didn't this
00883                          * used to work? 6/2003.)
00884                          */
00885                         prec = 1;
00886                         cp = arg->stptr;
00887                         goto pr_tail;
00888                 case 's':
00889                         need_format = FALSE;
00890                         if (zero_flag && ! lj)
00891                                 fill = zero_string;
00892                         parse_next_arg();
00893                         arg = force_string(arg);
00894                         if (! have_prec || prec > arg->stlen)
00895                                 prec = arg->stlen;
00896                         cp = arg->stptr;
00897                         goto pr_tail;
00898                 case 'd':
00899                 case 'i':
00900                         need_format = FALSE;
00901                         parse_next_arg();
00902                         tmpval = force_number(arg);
00903 
00904                         /*
00905                          * ``The result of converting a zero value with a
00906                          * precision of zero is no characters.''
00907                          */
00908                         if (have_prec && prec == 0 && tmpval == 0)
00909                                 goto pr_tail;
00910 
00911                         if (tmpval < 0) {
00912                                 if (tmpval < INTMAX_MIN)
00913                                         goto out_of_range;
00914                                 sgn = TRUE;
00915                                 uval = - (uintmax_t) (intmax_t) tmpval;
00916                         } else {
00917                                 /* Use !, so that NaNs are out of range.  */
00918                                 if (! (tmpval <= UINTMAX_MAX))
00919                                         goto out_of_range;
00920                                 sgn = FALSE;
00921                                 uval = (uintmax_t) tmpval;
00922                         }
00923                         do {
00924                                 *--cp = (char) ('0' + uval % 10);
00925                                 uval /= 10;
00926                         } while (uval > 0);
00927 
00928                         /* add more output digits to match the precision */
00929                         if (have_prec) {
00930                                 while (cend - cp < prec)
00931                                         *--cp = '0';
00932                         }
00933 
00934                         if (sgn)
00935                                 *--cp = '-';
00936                         else if (signchar)
00937                                 *--cp = signchar;
00938                         /*
00939                          * When to fill with zeroes is of course not simple.
00940                          * First: No zero fill if left-justifying.
00941                          * Next: There seem to be two cases:
00942                          *      A '0' without a precision, e.g. %06d
00943                          *      A precision with no field width, e.g. %.10d
00944                          * Any other case, we don't want to fill with zeroes.
00945                          */
00946                         if (! lj
00947                             && ((zero_flag && ! have_prec)
00948                                  || (fw == 0 && have_prec)))
00949                                 fill = zero_string;
00950                         if (prec > fw)
00951                                 fw = prec;
00952                         prec = cend - cp;
00953                         if (fw > prec && ! lj && fill != sp
00954                             && (*cp == '-' || signchar)) {
00955                                 bchunk_one(cp);
00956                                 cp++;
00957                                 prec--;
00958                                 fw--;
00959                         }
00960                         goto pr_tail;
00961                 case 'X':
00962                         chbuf = Uchbuf; /* FALL THROUGH */
00963                 case 'x':
00964                         base += 6;      /* FALL THROUGH */
00965                 case 'u':
00966                         base += 2;      /* FALL THROUGH */
00967                 case 'o':
00968                         base += 8;
00969                         need_format = FALSE;
00970                         parse_next_arg();
00971                         tmpval = force_number(arg);
00972 
00973                         /*
00974                          * ``The result of converting a zero value with a
00975                          * precision of zero is no characters.''
00976                          *
00977                          * If I remember the ANSI C standard, though,
00978                          * it says that for octal conversions
00979                          * the precision is artificially increased
00980                          * to add an extra 0 if # is supplied.
00981                          * Indeed, in C,
00982                          *      printf("%#.0o\n", 0);
00983                          * prints a single 0.
00984                          */
00985                         if (! alt && have_prec && prec == 0 && tmpval == 0)
00986                                 goto pr_tail;
00987 
00988                         if (tmpval < 0) {
00989                                 if (tmpval < INTMAX_MIN)
00990                                         goto out_of_range;
00991                                 uval = (uintmax_t) (intmax_t) tmpval;
00992                         } else {
00993                                 /* Use !, so that NaNs are out of range.  */
00994                                 if (! (tmpval <= UINTMAX_MAX))
00995                                         goto out_of_range;
00996                                 uval = (uintmax_t) tmpval;
00997                         }
00998                         /*
00999                          * When to fill with zeroes is of course not simple.
01000                          * First: No zero fill if left-justifying.
01001                          * Next: There seem to be two cases:
01002                          *      A '0' without a precision, e.g. %06d
01003                          *      A precision with no field width, e.g. %.10d
01004                          * Any other case, we don't want to fill with zeroes.
01005                          */
01006                         if (! lj
01007                             && ((zero_flag && ! have_prec)
01008                                  || (fw == 0 && have_prec)))
01009                                 fill = zero_string;
01010                         do {
01011                                 *--cp = chbuf[uval % base];
01012                                 uval /= base;
01013                         } while (uval > 0);
01014 
01015                         /* add more output digits to match the precision */
01016                         if (have_prec) {
01017                                 while (cend - cp < prec)
01018                                         *--cp = '0';
01019                         }
01020 
01021                         if (alt && tmpval != 0) {
01022                                 if (base == 16) {
01023                                         *--cp = cs1;
01024                                         *--cp = '0';
01025                                         if (fill != sp) {
01026                                                 bchunk(cp, 2);
01027                                                 cp += 2;
01028                                                 fw -= 2;
01029                                         }
01030                                 } else if (base == 8)
01031                                         *--cp = '0';
01032                         }
01033                         base = 0;
01034                         if (prec > fw)
01035                                 fw = prec;
01036                         prec = cend - cp;
01037         pr_tail:
01038                         if (! lj) {
01039                                 while (fw > prec) {
01040                                         bchunk_one(fill);
01041                                         fw--;
01042                                 }
01043                         }
01044                         bchunk(cp, (int) prec);
01045                         while (fw > prec) {
01046                                 bchunk_one(fill);
01047                                 fw--;
01048                         }
01049                         s0 = s1;
01050                         break;
01051 
01052      out_of_range:
01053                         /* out of range - emergency use of %g format */
01054                         if (do_lint)
01055                                 lintwarn(_("[s]printf: value %g is out of range for `%%%c' format"),
01056                                                         tmpval, cs1);
01057                         cs1 = 'g';
01058                         goto format_float;
01059 
01060                 case 'g':
01061                 case 'G':
01062                 case 'e':
01063                 case 'f':
01064                 case 'E':
01065                         need_format = FALSE;
01066                         parse_next_arg();
01067                         tmpval = force_number(arg);
01068      format_float:
01069                         if (! have_prec)
01070                                 prec = DEFAULT_G_PRECISION;
01071                         chksize(fw + prec + 9); /* 9 == slop */
01072 
01073                         cp = cpbuf;
01074                         *cp++ = '%';
01075                         if (lj)
01076                                 *cp++ = '-';
01077                         if (signchar)
01078                                 *cp++ = signchar;
01079                         if (alt)
01080                                 *cp++ = '#';
01081                         if (zero_flag)
01082                                 *cp++ = '0';
01083                         strcpy(cp, "*.*");
01084                         cp += 3;
01085                         *cp++ = cs1;
01086                         *cp = '\0';
01087 #ifndef GFMT_WORKAROUND
01088                         (void) sprintf(obufout, cpbuf,
01089                                        (int) fw, (int) prec, (double) tmpval);
01090 #else   /* GFMT_WORKAROUND */
01091                         if (cs1 == 'g' || cs1 == 'G')
01092                                 sgfmt(obufout, cpbuf, (int) alt,
01093                                        (int) fw, (int) prec, (double) tmpval);
01094                         else
01095                                 (void) sprintf(obufout, cpbuf,
01096                                        (int) fw, (int) prec, (double) tmpval);
01097 #endif  /* GFMT_WORKAROUND */
01098                         len = strlen(obufout);
01099                         ofre -= len;
01100                         obufout += len;
01101                         s0 = s1;
01102                         break;
01103                 default:
01104                         break;
01105                 }
01106                 if (toofew)
01107                         fatal("%s\n\t`%s'\n\t%*s%s",
01108                               _("not enough arguments to satisfy format string"),
01109                               fmt_string, (int) (s1 - fmt_string - 1), "",
01110                               _("^ ran out for this one"));
01111         }
01112         if (do_lint) {
01113                 if (need_format)
01114                         lintwarn(
01115                         _("[s]printf: format specifier does not have control letter"));
01116                 if (cur_arg < num_args)
01117                         lintwarn(
01118                         _("too many arguments supplied for format string"));
01119         }
01120         bchunk(s0, s1 - s0);
01121         r = make_str_node(obuf, obufout - obuf, ALREADY_MALLOCED);
01122         r->flags |= TEMP;
01123 
01124         for (i = 1; i < num_args; i++) {
01125                 unref(the_args[i]);
01126         }
01127 
01128         if (call_level-- > 1) {
01129                 free(the_args);
01130                 the_args = save_args;
01131                 args_size = save_args_size;
01132         }
01133 
01134         return r;
01135 }
01136 
01137 /* do_sprintf --- perform sprintf */
01138 
01139 NODE *
01140 do_sprintf(NODE *tree)
01141 {
01142         NODE *r;
01143         NODE *sfmt = force_string(tree_eval(tree->lnode));
01144 
01145         r = format_tree(sfmt->stptr, sfmt->stlen, tree->rnode, tree->printf_count);
01146         free_temp(sfmt);
01147         return r;
01148 }
01149 
01150 /*
01151  * redirect_to_fp --- return fp for redirection, NULL on failure
01152  * or stdout if no redirection, used by all print routines
01153  */
01154 
01155 static inline FILE *
01156 redirect_to_fp(NODE *tree, struct redirect **rpp)
01157 {
01158         int errflg;     /* not used, sigh */
01159         struct redirect *rp;
01160 
01161         if (tree == NULL)
01162                 return stdout;
01163 
01164         rp = redirect(tree, &errflg);
01165         if (rp != NULL) {
01166                 *rpp = rp;
01167                 return  rp->fp;
01168         }
01169 
01170         return NULL;
01171 }
01172 
01173 /* do_printf --- perform printf, including redirection */
01174 
01175 void
01176 do_printf(NODE *tree)
01177 {
01178         struct redirect *rp = NULL;
01179         register FILE *fp;
01180 
01181         if (tree->lnode == NULL) {
01182                 if (do_traditional) {
01183                         if (do_lint)
01184                                 lintwarn(_("printf: no arguments"));
01185                         return; /* bwk accepts it silently */
01186                 }
01187                 fatal(_("printf: no arguments"));
01188         }
01189 
01190         fp = redirect_to_fp(tree->rnode, & rp);
01191         if (fp == NULL)
01192                 return;
01193         tree->lnode->printf_count = tree->printf_count;
01194         tree = do_sprintf(tree->lnode);
01195         efwrite(tree->stptr, sizeof(char), tree->stlen, fp, "printf", rp, TRUE);
01196         if (rp != NULL && (rp->flag & RED_TWOWAY) != 0)
01197                 fflush(rp->fp);
01198         free_temp(tree);
01199 }
01200 
01201 /* do_sqrt --- do the sqrt function */
01202 
01203 NODE *
01204 do_sqrt(NODE *tree)
01205 {
01206         NODE *tmp;
01207         double arg;
01208 
01209         tmp = tree_eval(tree->lnode);
01210         if (do_lint && (tmp->flags & (NUMCUR|NUMBER)) == 0)
01211                 lintwarn(_("sqrt: received non-numeric argument"));
01212         arg = (double) force_number(tmp);
01213         free_temp(tmp);
01214         if (arg < 0.0)
01215                 warning(_("sqrt: called with negative argument %g"), arg);
01216         return tmp_number((AWKNUM) sqrt(arg));
01217 }
01218 
01219 /* do_substr --- do the substr function */
01220 
01221 NODE *
01222 do_substr(NODE *tree)
01223 {
01224         NODE *t1, *t2, *t3;
01225         NODE *r;
01226         register size_t indx;
01227         size_t length;
01228         double d_index, d_length;
01229 
01230         t1 = force_string(tree_eval(tree->lnode));
01231         t2 = tree_eval(tree->rnode->lnode);
01232         d_index = force_number(t2);
01233