home *** CD-ROM | disk | FTP | other *** search
/ Fish 'n' More 2 / fishmore-publicdomainlibraryvol.ii1991xetec.iso / dirs / gnuawk_406.lzh / GnuAwk / src.lzh / src / awk3.c < prev    next >
C/C++ Source or Header  |  1990-09-27  |  24KB  |  1,087 lines

  1. /*
  2.  * awk3 -- Builtin functions and various utility procedures 
  3.  *
  4.  * $Log:    awk3.c,v $
  5.  * Revision 1.40  89/03/31  13:17:20  david
  6.  * GNU license; be careful about types in sprintf calls
  7.  * 
  8.  * Revision 1.39  89/03/29  14:20:07  david
  9.  * delinting and code movement
  10.  * change redirect() to return struct redirect *
  11.  * 
  12.  * Revision 1.38  89/03/22  22:10:20  david
  13.  * a cleaner way to handle assignment to $n where n > 0
  14.  * 
  15.  * Revision 1.37  89/03/21  10:54:21  david
  16.  * cleanup and fix of handling of precision in format string of printf call
  17.  * 
  18.  * Revision 1.36  89/03/15  22:01:51  david
  19.  * ENVIRON fix from hack
  20.  * relegated -Ft to strict compatibility
  21.  * getline error return fix
  22.  * printf %c fix (only print 1 char of a string)
  23.  * tolower & toupper added
  24.  * /dev/fd/N etc special files added
  25.  * 
  26.  * Revision 1.35  89/03/15  21:34:05  david
  27.  * try to free more memory
  28.  * 
  29.  * Revision 1.34  88/12/13  22:28:10  david
  30.  * temporarily #ifdef out flush_io in redirect(); adjust atan2() for 
  31.  * force_number as a macro
  32.  * 
  33.  * Revision 1.32  88/12/01  15:03:21  david
  34.  * renamed hack_print_node to do_print (at last!)
  35.  * moved force_string() up out of print_simple for simplicity
  36.  * 
  37.  * Revision 1.31  88/11/30  15:17:27  david
  38.  * free previous value in set_fs
  39.  * 
  40.  * Revision 1.30  88/11/29  16:24:47  david
  41.  * fix bug in previous change
  42.  * 
  43.  * Revision 1.29  88/11/29  15:14:52  david
  44.  * dynamically manage open files/pipes to allow an arbitrary number of open files
  45.  * (i.e. when out of file descriptors, close the least recently used file,
  46.  * saving the current offset; if it is reused, reopen and seek to saved offset)
  47.  * 
  48.  * Revision 1.28  88/11/28  20:12:53  david
  49.  * correct previous error in cleanup of do_substr
  50.  * 
  51.  * Revision 1.27  88/11/23  21:42:13  david
  52.  * Arnold: change ENV to ENVIRON nad a further bug fix for -Ft
  53.  * ..
  54.  * 
  55.  * Revision 1.26  88/11/22  13:50:33  david
  56.  * Arnold: added ENV array and bug fix to -Ft
  57.  * 
  58.  * Revision 1.25  88/11/15  10:24:08  david
  59.  * Arnold: cleanup of comments, #include's and obsolete code
  60.  * 
  61.  * Revision 1.24  88/11/14  21:57:03  david
  62.  * Arnold:  init. FILENAME to "-" and cleanup in do_substr()
  63.  * 
  64.  * Revision 1.23  88/11/01  12:17:45  david
  65.  * cleanu and code movement; changes to reflect change to parse_fields()
  66.  * 
  67.  * Revision 1.22  88/10/19  21:58:43  david
  68.  * replace malloc and realloc with error checking versions
  69.  * 
  70.  * Revision 1.21  88/10/17  20:55:31  david
  71.  * SYSV --> USG
  72.  * 
  73.  * Revision 1.20  88/10/13  21:59:55  david
  74.  * purge FAST and cleanup error messages
  75.  * 
  76.  * Revision 1.19  88/10/06  21:54:28  david
  77.  * cleaned up I/O handling
  78.  * 
  79.  * Revision 1.18  88/10/06  15:49:01  david
  80.  * changes from Arnold: be careful about flushing I/O; warn about error on close;
  81.  * return seed from srand
  82.  * 
  83.  * Revision 1.17  88/09/19  20:39:11  david
  84.  * minor cleanup
  85.  * 
  86.  * Revision 1.16  88/08/09  14:55:16  david
  87.  * getline now gets next file properly
  88.  * stupid bug in do_split() fixed
  89.  * substr() now works if second arg. is negative (truncated to 0)
  90.  * 
  91.  * Revision 1.15  88/06/13  18:07:12  david
  92.  * delete -R option
  93.  * cleanup of redirection code [from Arnold]
  94.  * 
  95.  * Revision 1.14  88/06/07  23:41:00  david
  96.  * some paranoid typecasting plus one bug fix:
  97.  * in do_getline(), use stdin if input_file is NULL and ther is no redirection 
  98.  * 
  99.  * Revision 1.13  88/06/06  21:40:49  david
  100.  * oops! got a little overenthusiastic on that last merge
  101.  * 
  102.  * Revision 1.12  88/06/06  11:27:57  david
  103.  * get rid of some obsolete code
  104.  * merge parsing of fields for record input and split()
  105.  * 
  106.  * Revision 1.11  88/06/05  21:00:35  david
  107.  * flush I/O buffers before calling system (fix from Arnold)
  108.  * 
  109.  * Revision 1.10  88/06/05  20:59:26  david
  110.  * local vars. now come off a stack
  111.  * 
  112.  * Revision 1.9  88/06/01  22:08:24  david
  113.  * in split(), ensure that if second arg. is a local var. that the value is 
  114.  * looked up
  115.  * 
  116.  * Revision 1.8  88/05/31  09:30:16  david
  117.  * Arnold's portability fixes to last change in random() stuff
  118.  * 
  119.  * Revision 1.7  88/05/30  09:53:49  david
  120.  * clean up some fatal() calls
  121.  * de-lint the random number code
  122.  * 
  123.  * Revision 1.6  88/05/27  11:06:21  david
  124.  * input_file wasn't getting properly reset after getline
  125.  * 
  126.  * Revision 1.5  88/05/26  22:49:55  david
  127.  * fixed error message for redirection
  128.  * 
  129.  * Revision 1.4  88/05/18  18:20:02  david
  130.  * fixed case where RS==""; record was including a trailing newline
  131.  * 
  132.  * Revision 1.3  88/04/13  17:39:26  david
  133.  * fixed bug in handling of NR and FNR
  134.  * 
  135.  * Revision 1.2  88/04/12  16:04:02  david
  136.  * fixed bug: NF at end of record generated one less field than it should have
  137.  * 
  138.  * Revision 1.1  88/04/08  15:15:07  david
  139.  * Initial revision
  140.  *  Revision 1.7  88/04/08  15:08:48  david bug fix for file
  141.  * descriptor handlin 
  142.  *
  143.  * Revision 1.6  88/04/08  14:48:36  david changes from Arnold Robbins 
  144.  *
  145.  * Revision 1.5  88/03/28  14:13:54  david *** empty log message *** 
  146.  *
  147.  * Revision 1.4  88/03/23  22:17:41  david mostly delinting -- a couple of bug
  148.  * fixes 
  149.  *
  150.  * Revision 1.3  88/03/18  21:00:13  david Baseline -- hoefully all the
  151.  * functionality of the new awk added. Just debugging and tuning to do. 
  152.  *
  153.  * Revision 1.2  87/11/19  14:42:31  david expanded functionality for getline
  154.  * broke out get_a_record() from inrec() so that the former can be used from
  155.  * do_getline add system() builtin and skeletons for many other new builtins 
  156.  *
  157.  * Revision 1.1  87/10/27  15:23:33  david Initial revision 
  158.  *
  159.  */
  160.  
  161. /* 
  162.  * Copyright (C) 1986, 1988, 1989 the Free Software Foundation, Inc.
  163.  * 
  164.  * This file is part of GAWK, the GNU implementation of the
  165.  * AWK Progamming Language.
  166.  * 
  167.  * GAWK is free software; you can redistribute it and/or modify
  168.  * it under the terms of the GNU General Public License as published by
  169.  * the Free Software Foundation; either version 1, or (at your option)
  170.  * any later version.
  171.  * 
  172.  * GAWK is distributed in the hope that it will be useful,
  173.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  174.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  175.  * GNU General Public License for more details.
  176.  * 
  177.  * You should have received a copy of the GNU General Public License
  178.  * along with GAWK; see the file COPYING.  If not, write to
  179.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  180.  */
  181.  
  182. #include "awk.h"
  183.  
  184. extern int parse_fields();
  185. extern void assoc_clear();
  186. extern NODE *node();
  187.  
  188. #ifdef USG
  189. extern long lrand48();
  190. extern void srand48();
  191. #else
  192. extern void srandom();
  193. extern char *initstate();
  194. extern char *setstate();
  195. extern long random();
  196. #endif
  197.  
  198. static void set_element();
  199. static void get_one();
  200. static void get_two();
  201. static int get_three();
  202. static int a_get_three();
  203. static void close_one();
  204. static int close_fp();
  205. static NODE *spc_var();
  206.  
  207. NODE *do_sprintf();
  208.  
  209. /* These nodes store all the special variables AWK uses */
  210. NODE *FS_node, *NF_node, *RS_node, *NR_node;
  211. NODE *FILENAME_node, *OFS_node, *ORS_node, *OFMT_node;
  212. NODE *FNR_node, *RLENGTH_node, *RSTART_node, *SUBSEP_node;
  213. NODE *ENVIRON_node, *IGNORECASE_node;
  214. NODE *ARGC_node, *ARGV_node;
  215.  
  216. /*
  217.  * Set all the special variables to their initial values.
  218.  */
  219. void
  220. init_vars()
  221. {
  222. /*** APPEND AFTER 221 IN x:awk3.c ***/
  223. #ifdef AMIGA
  224.     char **environ = NULL;
  225. #else
  226. /*** APPEND AFTER 221 IN x:awk3.c ***/
  227.     extern char **environ;
  228. /*** APPEND AFTER 222 IN x:awk3.c ***/
  229. #endif
  230. /*** APPEND AFTER 222 IN x:awk3.c ***/
  231.     char *var, *val;
  232.     NODE **aptr;
  233.     int i;
  234.  
  235.     FS_node = spc_var("FS", make_string(" ", 1));
  236.     NF_node = spc_var("NF", make_number(-1.0));
  237.     RS_node = spc_var("RS", make_string("\n", 1));
  238.     NR_node = spc_var("NR", make_number(0.0));
  239.     FNR_node = spc_var("FNR", make_number(0.0));
  240.     FILENAME_node = spc_var("FILENAME", make_string("-", 1));
  241.     OFS_node = spc_var("OFS", make_string(" ", 1));
  242.     ORS_node = spc_var("ORS", make_string("\n", 1));
  243.     OFMT_node = spc_var("OFMT", make_string("%.6g", 4));
  244.     RLENGTH_node = spc_var("RLENGTH", make_number(0.0));
  245.     RSTART_node = spc_var("RSTART", make_number(0.0));
  246.     SUBSEP_node = spc_var("SUBSEP", make_string("\034", 1));
  247.     IGNORECASE_node = spc_var("IGNORECASE", make_number(0.0));
  248.  
  249.     ENVIRON_node = spc_var("ENVIRON", Nnull_string);
  250.     for (i = 0; environ[i]; i++) {
  251.         static char nullstr[] = "";
  252.  
  253.         var = environ[i];
  254.         val = index(var, '=');
  255.         if (val)
  256.             *val++ = '\0';
  257.         else
  258.             val = nullstr;
  259.         aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen (var)));
  260.         *aptr = make_string(val, strlen (val));
  261.  
  262.         /* restore '=' so that system() gets a valid environment */
  263.         if (val != nullstr)
  264.             *--val = '=';
  265.     }
  266. }
  267.  
  268. /*
  269.  * OFMT is special because we don't dare use force_string on it for fear of
  270.  * infinite loops.  Thus, if it isn't a string, we return the default "%.6g"
  271.  * This may or may not be the right thing to do, but its the easiest 
  272.  */
  273. /* This routine isn't used!  It should be.  */
  274. #ifdef notdef
  275. char *
  276. get_ofmt()
  277. {
  278.     register NODE *tmp;
  279.  
  280.     tmp = OFMT_node->var_value;
  281.     if ((tmp->type != Node_string && tmp->type != Node_str_num) || tmp->stlen == 0)
  282.         return "%.6g";
  283.     return tmp->stptr;
  284. }
  285. #endif
  286.  
  287. char *
  288. get_fs()
  289. {
  290.     register NODE *tmp;
  291.  
  292.     tmp = force_string(FS_node->var_value);
  293.     if (tmp->stlen == 0)
  294.         return 0;
  295.     return tmp->stptr;
  296. }
  297.  
  298. void
  299. set_fs(str)
  300. char *str;
  301. {
  302.     register NODE **tmp;
  303.  
  304.     tmp = get_lhs(FS_node, 0);
  305.     /*
  306.      * Only if in full compatibility mode check for the stupid special
  307.      * case so -F\t works as documented in awk even though the shell
  308.      * hands us -Ft.  Bleah!
  309.      */
  310.     if (strict && str[0] == 't' && str[1] == '\0')
  311.         str[0] = '\t';
  312.     *tmp = make_string(str, 1);
  313.     do_deref();
  314. }
  315.  
  316. /* Builtin functions */
  317. NODE *
  318. do_exp(tree)
  319. NODE *tree;
  320. {
  321.     NODE *tmp;
  322.     double exp();
  323.  
  324.     get_one(tree, &tmp);
  325.     return tmp_number((AWKNUM)exp((double)force_number(tmp)));
  326. }
  327.  
  328. NODE *
  329. do_index(tree)
  330. NODE *tree;
  331. {
  332.     NODE *s1, *s2;
  333.     register char *p1, *p2;
  334.     register int l1, l2;
  335.  
  336.     get_two(tree, &s1, &s2);
  337.     p1 = s1->stptr;
  338.     p2 = s2->stptr;
  339.     l1 = s1->stlen;
  340.     l2 = s2->stlen;
  341.     while (l1) {
  342.         if (STREQN(p1, p2, l2))
  343.             return tmp_number((AWKNUM) (1 + s1->stlen - l1));
  344.         l1--;
  345.         p1++;
  346.     }
  347.     return tmp_number((AWKNUM) 0.0);
  348. }
  349.  
  350. NODE *
  351. do_int(tree)
  352. NODE *tree;
  353. {
  354.     NODE *tmp;
  355.     double floor();
  356.  
  357.     get_one(tree, &tmp);
  358.     return tmp_number((AWKNUM)floor((double)force_number(tmp)));
  359. }
  360.  
  361. NODE *
  362. do_length(tree)
  363. NODE *tree;
  364. {
  365.     NODE *tmp;
  366.  
  367.     get_one(tree, &tmp);
  368.     return tmp_number((AWKNUM) (force_string(tmp)->stlen));
  369. }
  370.  
  371. NODE *
  372. do_log(tree)
  373. NODE *tree;
  374. {
  375.     NODE *tmp;
  376.     double log();
  377.  
  378.     get_one(tree, &tmp);
  379.     return tmp_number((AWKNUM)log((double)force_number(tmp)));
  380. }
  381.  
  382. void
  383. do_printf(tree)
  384. NODE *tree;
  385. {
  386.     register FILE *fp = stdout;
  387.     int errflg = 0;        /* not used, sigh */
  388.  
  389.     if (tree->rnode)
  390.         fp = redirect(tree->rnode, &errflg)->fp;
  391.     if (fp)
  392.         print_simple(do_sprintf(tree->lnode), fp);
  393. }
  394.  
  395. static void
  396. set_element(num, s, len, n)
  397. int num;
  398. char *s;
  399. int len;
  400. NODE *n;
  401. {
  402.     *assoc_lookup(n, tmp_number((AWKNUM) (num))) = make_string(s, len);
  403. }
  404.  
  405. NODE *
  406. do_split(tree)
  407. NODE *tree;
  408. {
  409.     NODE *t1, *t2, *t3;
  410.     register char *splitc;
  411.     char *s;
  412.     NODE *n;
  413.  
  414.     if (a_get_three(tree, &t1, &t2, &t3) < 3)
  415.         splitc = get_fs();
  416.     else
  417.         splitc = force_string(t3)->stptr;
  418.  
  419.     n = t2;
  420.     if (t2->type == Node_param_list)
  421.         n = stack_ptr[t2->param_cnt];
  422.     if (n->type != Node_var && n->type != Node_var_array)
  423.         fatal("second argument of split is not a variable");
  424.     assoc_clear(n);
  425.  
  426.     tree = force_string(t1);
  427.  
  428.     s = tree->stptr;
  429.     return tmp_number((AWKNUM)
  430.         parse_fields(HUGE, &s, tree->stlen, splitc, set_element, n));
  431. }
  432.  
  433. /*
  434.  * Note that the output buffer cannot be static because sprintf may get
  435.  * called recursively by force_string.  Hence the wasteful alloca calls 
  436.  */
  437.  
  438. /* %e and %f formats are not properly implemented.  Someone should fix them */
  439. NODE *
  440. do_sprintf(tree)
  441. NODE *tree;
  442. {
  443. #define bchunk(s,l) if(l) {\
  444.     if((l)>ofre) {\
  445.       char *tmp;\
  446.       tmp=(char *)alloca(osiz*2);\
  447.       bcopy(obuf,tmp,olen);\
  448.       obuf=tmp;\
  449.       ofre+=osiz;\
  450.       osiz*=2;\
  451.     }\
  452.     bcopy(s,obuf+olen,(l));\
  453.     olen+=(l);\
  454.     ofre-=(l);\
  455.   }
  456.  
  457.     /* Is there space for something L big in the buffer? */
  458. #define chksize(l)  if((l)>ofre) {\
  459.     char *tmp;\
  460.     tmp=(char *)alloca(osiz*2);\
  461.     bcopy(obuf,tmp,olen);\
  462.     obuf=tmp;\
  463.     ofre+=osiz;\
  464.     osiz*=2;\
  465.   }
  466.  
  467.     /*
  468.      * Get the next arg to be formatted.  If we've run out of args,
  469.      * return "" (Null string) 
  470.      */
  471. #define parse_next_arg() {\
  472.   if(!carg) arg= Nnull_string;\
  473.   else {\
  474.       get_one(carg,&arg);\
  475.     carg=carg->rnode;\
  476.   }\
  477.  }
  478.  
  479.     char *obuf;
  480.     int osiz, ofre, olen;
  481.     static char chbuf[] = "0123456789abcdef";
  482.     static char sp[] = " ";
  483.     char *s0, *s1;
  484.     int n0;
  485.     NODE *sfmt, *arg;
  486.     register NODE *carg;
  487.     long fw, prec, lj, alt, big;
  488.     long *cur;
  489.     long val;
  490.     unsigned long uval;
  491.     int sgn;
  492.     int base;
  493.     char cpbuf[30];        /* if we have numbers bigger than 30 */
  494.     char *cend = &cpbuf[30];/* chars, we lose, but seems unlikely */
  495.     char *cp;
  496.     char *fill;
  497.     double tmpval;
  498.     char *pr_str;
  499.     extern char *gcvt();
  500.  
  501.  
  502.     obuf = (char *) alloca(120);
  503.     osiz = 120;
  504.     ofre = osiz;
  505.     olen = 0;
  506.     get_one(tree, &sfmt);
  507.     sfmt = force_string(sfmt);
  508.     carg = tree->rnode;
  509.     for (s0 = s1 = sfmt->stptr, n0 = sfmt->stlen; n0-- > 0;) {
  510.         if (*s1 != '%') {
  511.             s1++;
  512.             continue;
  513.         }
  514.         bchunk(s0, s1 - s0);
  515.         s0 = s1;
  516.         cur = &fw;
  517.         fw = 0;
  518.         prec = 0;
  519.         lj = alt = big = 0;
  520.         fill = sp;
  521.         cp = cend;
  522.         s1++;
  523.  
  524. retry:
  525.         --n0;
  526.         switch (*s1++) {
  527.         case '%':
  528.             bchunk("%", 1);
  529.             s0 = s1;
  530.             break;
  531.  
  532.         case '0':
  533.             if (fill != sp || lj)
  534.                 goto lose;
  535.             if (cur == &fw)
  536.                 fill = "0";    /* FALL through */
  537.         case '1':
  538.         case '2':
  539.         case '3':
  540.         case '4':
  541.         case '5':
  542.         case '6':
  543.         case '7':
  544.         case '8':
  545.         case '9':
  546.             if (cur == 0)
  547.                 goto lose;
  548.             *cur = s1[-1] - '0';
  549.             while (n0 > 0 && *s1 >= '0' && *s1 <= '9') {
  550.                 --n0;
  551.                 *cur = *cur * 10 + *s1++ - '0';
  552.             }
  553.             goto retry;
  554.         case '-':
  555.             if (lj || fill != sp)
  556.                 goto lose;
  557.             lj++;
  558.             goto retry;
  559.         case '.':
  560.             if (cur != &fw)
  561.                 goto lose;
  562.             cur = ≺
  563.             goto retry;
  564.         case '#':
  565.             if (alt)
  566.                 goto lose;
  567.             alt++;
  568.             goto retry;
  569.         case 'l':
  570.             if (big)
  571.                 goto lose;
  572.             big++;
  573.             goto retry;
  574.         case 'c':
  575.             parse_next_arg();
  576.             if (arg->flags & NUM) {
  577.                 uval = (unsigned long) arg->numbr;
  578.                 cpbuf[0] = uval;
  579.                 prec = 1;
  580.                 pr_str = cpbuf;
  581.                 goto dopr_string;
  582.             }
  583.             if (! prec)
  584.                 prec = 1;
  585.             else if (prec > arg->stlen)
  586.                 prec = arg->stlen;
  587.             pr_str = cpbuf;
  588.             goto dopr_string;
  589.         case 's':
  590.             parse_next_arg();
  591.             arg = force_string(arg);
  592.             if (!prec || prec > arg->stlen)
  593.                 prec = arg->stlen;
  594.             pr_str = arg->stptr;
  595.  
  596.     dopr_string:
  597.             if (fw > prec && !lj) {
  598.                 while (fw > prec) {
  599.                     bchunk(sp, 1);
  600.                     fw--;
  601.                 }
  602.             }
  603.             bchunk(pr_str, (int) prec);
  604.             if (fw > prec) {
  605.                 while (fw > prec) {
  606.                     bchunk(sp, 1);
  607.                     fw--;
  608.                 }
  609.             }
  610.             s0 = s1;
  611.             break;
  612.         case 'd':
  613.             parse_next_arg();
  614.             val = (long) force_number(arg);
  615.             if (val < 0) {
  616.                 sgn = 1;
  617.                 val = -val;
  618.             } else
  619.                 sgn = 0;
  620.             do {
  621.                 *--cp = '0' + val % 10;
  622.                 val /= 10;
  623.             } while (val);
  624.             if (sgn)
  625.                 *--cp = '-';
  626.             prec = cend - cp;
  627.             if (fw > prec && !lj) {
  628.                 if (fill != sp && *cp == '-') {
  629.                     bchunk(cp, 1);
  630.                     cp++;
  631.                     prec--;
  632.                     fw--;
  633.                 }
  634.                 while (fw > prec) {
  635.                     bchunk(fill, 1);
  636.                     fw--;
  637.                 }
  638.             }
  639.             bchunk(cp, (int) prec);
  640.             if (fw > prec) {
  641.                 while (fw > prec) {
  642.                     bchunk(fill, 1);
  643.                     fw--;
  644.                 }
  645.             }
  646.             s0 = s1;
  647.             break;
  648.         case 'u':
  649.             base = 10;
  650.             goto pr_unsigned;
  651.         case 'o':
  652.             base = 8;
  653.             goto pr_unsigned;
  654.         case 'x':
  655.             base = 16;
  656.             goto pr_unsigned;
  657.     pr_unsigned:
  658.             parse_next_arg();
  659.             uval = (unsigned long) force_number(arg);
  660.             do {
  661.                 *--cp = chbuf[uval % base];
  662.                 uval /= base;
  663.             } while (uval);
  664.             prec = cend - cp;
  665.             if (fw > prec && !lj) {
  666.                 while (fw > prec) {
  667.                     bchunk(fill, 1);
  668.                     fw--;
  669.                 }
  670.             }
  671.             bchunk(cp, (int) prec);
  672.             if (fw > prec) {
  673.                 while (fw > prec) {
  674.                     bchunk(fill, 1);
  675.                     fw--;
  676.                 }
  677.             }
  678.             s0 = s1;
  679.             break;
  680.         case 'g':
  681.             parse_next_arg();
  682.             tmpval = force_number(arg);
  683.             if (prec == 0)
  684.                 prec = 13;
  685.             (void) gcvt(tmpval, (int) prec, cpbuf);
  686.             prec = strlen(cpbuf);
  687.             cp = cpbuf;
  688.             if (fw > prec && !lj) {
  689.                 if (fill != sp && *cp == '-') {
  690.                     bchunk(cp, 1);
  691.                     cp++;
  692.                     prec--;
  693.                 }    /* Deal with .5 as 0.5 */
  694.                 if (fill == sp && *cp == '.') {
  695.                     --fw;
  696.                     while (--fw >= prec) {
  697.                         bchunk(fill, 1);
  698.                     }
  699.                     bchunk("0", 1);
  700.                 } else
  701.                     while (fw-- > prec)
  702.                         bchunk(fill, 1);
  703.             } else {/* Turn .5 into 0.5 */
  704.                 /* FOO */
  705.                 if (*cp == '.' && fill == sp) {
  706.                     bchunk("0", 1);
  707.                     --fw;
  708.                 }
  709.             }
  710.             bchunk(cp, (int) prec);
  711.             if (fw > prec)
  712.                 while (fw-- > prec)
  713.                     bchunk(fill, 1);
  714.             s0 = s1;
  715.             break;
  716.         case 'f':
  717.             parse_next_arg();
  718.             tmpval = force_number(arg);
  719.             chksize(fw + prec + 5);    /* 5==slop */
  720.  
  721.             cp = cpbuf;
  722.             *cp++ = '%';
  723.             if (lj)
  724.                 *cp++ = '-';
  725.             if (fill != sp)
  726.                 *cp++ = '0';
  727.             if (cur != &fw) {
  728.                 (void) strcpy(cp, "*.*f");
  729.                 (void) sprintf(obuf + olen, cpbuf, (int) fw, (int) prec, (double) tmpval);
  730.             } else {
  731.                 (void) strcpy(cp, "*f");
  732.                 (void) sprintf(obuf + olen, cpbuf, (int) fw, (double) tmpval);
  733.             }
  734.             cp = obuf + olen;
  735.             ofre -= strlen(obuf + olen);
  736.             olen += strlen(obuf + olen);    /* There may be nulls */
  737.             s0 = s1;
  738.             break;
  739.         case 'e':
  740.             parse_next_arg();
  741.             tmpval = force_number(arg);
  742.             chksize(fw + prec + 5);    /* 5==slop */
  743.             cp = cpbuf;
  744.             *cp++ = '%';
  745.             if (lj)
  746.                 *cp++ = '-';
  747.             if (fill != sp)
  748.                 *cp++ = '0';
  749.             if (cur != &fw) {
  750.                 (void) strcpy(cp, "*.*e");
  751.                 (void) sprintf(obuf + olen, cpbuf, (int) fw, (int) prec, (double) tmpval);
  752.             } else {
  753.                 (void) strcpy(cp, "*e");
  754.                 (void) sprintf(obuf + olen, cpbuf, (int) fw, (double) tmpval);
  755.             }
  756.             cp = obuf + olen;
  757.             ofre -= strlen(obuf + olen);
  758.             olen += strlen(obuf + olen);    /* There may be nulls */
  759.             s0 = s1;
  760.             break;
  761.  
  762.         default:
  763.     lose:
  764.             break;
  765.         }
  766.     }
  767.     bchunk(s0, s1 - s0);
  768.     return tmp_string(obuf, olen);
  769. }
  770.  
  771. NODE *
  772. do_sqrt(tree)
  773. NODE *tree;
  774. {
  775.     NODE *tmp;
  776.     double sqrt();
  777.  
  778.     get_one(tree, &tmp);
  779.     return tmp_number((AWKNUM)sqrt((double)force_number(tmp)));
  780. }
  781.  
  782. NODE *
  783. do_substr(tree)
  784. NODE *tree;
  785. {
  786.     NODE *t1, *t2, *t3;
  787.     register int indx, length;
  788.  
  789.     length = -1;
  790.     if (get_three(tree, &t1, &t2, &t3) == 3)
  791.         length = (int) force_number(t3);
  792.     indx = (int) force_number(t2) - 1;
  793.     tree = force_string(t1);
  794.     if (length == -1)
  795.         length = tree->stlen;
  796.     if (indx < 0)
  797.         indx = 0;
  798.     if (indx >= tree->stlen || length <= 0)
  799.         return Nnull_string;
  800.     if (indx + length > tree->stlen)
  801.         length = tree->stlen - indx;
  802.     return tmp_string(tree->stptr + indx, length);
  803. }
  804.  
  805. NODE *
  806. do_system(tree)
  807. NODE *tree;
  808. {
  809.     NODE *tmp;
  810.     int ret;
  811.  
  812.     (void) flush_io ();    /* so output is syncrhonous with gawk's */
  813.     get_one(tree, &tmp);
  814.     ret = system(force_string(tmp)->stptr);
  815.     ret = (ret >> 8) & 0xff;
  816.     return tmp_number((AWKNUM) ret);
  817. }
  818.  
  819. void 
  820. do_print(tree)
  821. register NODE *tree;
  822. {
  823.     register FILE *fp = stdout;
  824.     int errflg = 0;        /* not used, sigh */
  825.  
  826.     if (tree->rnode)
  827.         fp = redirect(tree->rnode, &errflg)->fp;
  828.     if (!fp)
  829.         return;
  830.     tree = tree->lnode;
  831.     if (!tree)
  832.         tree = WHOLELINE;
  833.     if (tree->type != Node_expression_list) {
  834.         if (!(tree->flags & STR))
  835.             cant_happen();
  836.         print_simple(tree, fp);
  837.     } else {
  838.         while (tree) {
  839.             print_simple(force_string(tree_eval(tree->lnode)), fp);
  840.             tree = tree->rnode;
  841.             if (tree)
  842.                 print_simple(OFS_node->var_value, fp);
  843.         }
  844.     }
  845.     print_simple(ORS_node->var_value, fp);
  846. }
  847.  
  848. NODE *
  849. do_tolower(tree)
  850. NODE *tree;
  851. {
  852.     NODE *t1, *t2;
  853.     register char *cp, *cp2;
  854.  
  855.     get_one(tree, &t1);
  856.     t1 = force_string(t1);
  857.     t2 = tmp_string(t1->stptr, t1->stlen);
  858.     for (cp = t2->stptr, cp2 = t2->stptr + t2->stlen; cp < cp2; cp++)
  859.         if (isupper(*cp))
  860.             *cp = tolower(*cp);
  861.     return t2;
  862. }
  863.  
  864. NODE *
  865. do_toupper(tree)
  866. NODE *tree;
  867. {
  868.     NODE *t1, *t2;
  869.     register char *cp;
  870.  
  871.     get_one(tree, &t1);
  872.     t1 = force_string(t1);
  873.     t2 = tmp_string(t1->stptr, t1->stlen);
  874.     for (cp = t2->stptr; cp < t2->stptr + t2->stlen; cp++)
  875.         if (islower(*cp))
  876.             *cp = toupper(*cp);
  877.     return t2;
  878. }
  879.  
  880. /*
  881.  * Get the arguments to functions.  No function cares if you give it too many
  882.  * args (they're ignored).  Only a few fuctions complain about being given
  883.  * too few args.  The rest have defaults.
  884.  */
  885.  
  886. static void
  887. get_one(tree, res)
  888. NODE *tree, **res;
  889. {
  890.     if (!tree) {
  891.         *res = WHOLELINE;
  892.         return;
  893.     }
  894.     *res = tree_eval(tree->lnode);
  895. }
  896.  
  897. static void
  898. get_two(tree, res1, res2)
  899. NODE *tree, **res1, **res2;
  900. {
  901.     if (!tree) {
  902.         *res1 = WHOLELINE;
  903.         return;
  904.     }
  905.     *res1 = tree_eval(tree->lnode);
  906.     if (!tree->rnode)
  907.         return;
  908.     tree = tree->rnode;
  909.     *res2 = tree_eval(tree->lnode);
  910. }
  911.  
  912. static int
  913. get_three(tree, res1, res2, res3)
  914. NODE *tree, **res1, **res2, **res3;
  915. {
  916.     if (!tree) {
  917.         *res1 = WHOLELINE;
  918.         return 0;
  919.     }
  920.     *res1 = tree_eval(tree->lnode);
  921.     if (!tree->rnode)
  922.         return 1;
  923.     tree = tree->rnode;
  924.     *res2 = tree_eval(tree->lnode);
  925.     if (!tree->rnode)
  926.         return 2;
  927.     tree = tree->rnode;
  928.     *res3 = tree_eval(tree->lnode);
  929.     return 3;
  930. }
  931.  
  932. static int
  933. a_get_three(tree, res1, res2, res3)
  934. NODE *tree, **res1, **res2, **res3;
  935. {
  936.     if (!tree) {
  937.         *res1 = WHOLELINE;
  938.         return 0;
  939.     }
  940.     *res1 = tree_eval(tree->lnode);
  941.     if (!tree->rnode)
  942.         return 1;
  943.     tree = tree->rnode;
  944.     *res2 = tree->lnode;
  945.     if (!tree->rnode)
  946.         return 2;
  947.     tree = tree->rnode;
  948.     *res3 = tree_eval(tree->lnode);
  949.     return 3;
  950. }
  951.  
  952. void
  953. print_simple(tree, fp)
  954. NODE *tree;
  955. FILE *fp;
  956. {
  957.     if (fwrite(tree->stptr, sizeof(char), tree->stlen, fp) != tree->stlen)
  958.         warning("fwrite: %s", sys_errlist[errno]);
  959.     free_temp(tree);
  960. }
  961.  
  962. NODE *
  963. do_atan2(tree)
  964. NODE *tree;
  965. {
  966.     NODE *t1, *t2;
  967.     extern double atan2();
  968.  
  969.     get_two(tree, &t1, &t2);
  970.     (void) force_number(t1);
  971.     return tmp_number((AWKNUM) atan2((double) t1->numbr,
  972.         (double) force_number(t2)));
  973. }
  974.  
  975. NODE *
  976. do_sin(tree)
  977. NODE *tree;
  978. {
  979.     NODE *tmp;
  980.     extern double sin();
  981.  
  982.     get_one(tree, &tmp);
  983.     return tmp_number((AWKNUM) sin((double)force_number(tmp)));
  984. }
  985.  
  986. NODE *
  987. do_cos(tree)
  988. NODE *tree;
  989. {
  990.     NODE *tmp;
  991.     extern double cos();
  992.  
  993.     get_one(tree, &tmp);
  994.     return tmp_number((AWKNUM) cos((double)force_number(tmp)));
  995. }
  996.  
  997. static int firstrand = 1;
  998.  
  999. #ifndef USG
  1000. static char state[256];
  1001. #endif
  1002.  
  1003. #define    MAXLONG    2147483647    /* maximum value for long int */
  1004.  
  1005. /* ARGSUSED */
  1006. NODE *
  1007. do_rand(tree)
  1008. NODE *tree;
  1009. {
  1010. #ifdef USG
  1011.     return tmp_number((AWKNUM) lrand48() / MAXLONG);
  1012. #else
  1013.     if (firstrand) {
  1014.         (void) initstate((unsigned) 1, state, sizeof state);
  1015.         srandom(1);
  1016.         firstrand = 0;
  1017.     }
  1018.     return tmp_number((AWKNUM) random() / MAXLONG);
  1019. #endif
  1020. }
  1021.  
  1022. NODE *
  1023. do_srand(tree)
  1024. NODE *tree;
  1025. {
  1026.     NODE *tmp;
  1027.     static long save_seed = 1;
  1028.     long ret = save_seed;    /* SVR4 awk srand returns previous seed */
  1029.     extern long time();
  1030.  
  1031. #ifdef USG
  1032.     if (tree == NULL)
  1033.         srand48(save_seed = time((long *) 0));
  1034.     else {
  1035.         get_one(tree, &tmp);
  1036.         srand48(save_seed = (long) force_number(tmp));
  1037.     }
  1038. #else
  1039.     if (firstrand)
  1040.         (void) initstate((unsigned) 1, state, sizeof state);
  1041.     else
  1042.         (void) setstate(state);
  1043.  
  1044.     if (!tree)
  1045.         srandom((int) (save_seed = time((long *) 0)));
  1046.     else {
  1047.         get_one(tree, &tmp);
  1048.         srandom((int) (save_seed = (long) force_number(tmp)));
  1049.     }
  1050. #endif
  1051.     firstrand = 0;
  1052.     return tmp_number((AWKNUM) ret);
  1053. }
  1054.  
  1055. /* Create a special variable */
  1056. static NODE *
  1057. spc_var(name, value)
  1058. char *name;
  1059. NODE *value;
  1060. {
  1061.     register NODE *r;
  1062.  
  1063.     if ((r = lookup(variables, name)) == NULL)
  1064.         r = install(variables, name, node(value, Node_var, (NODE *) NULL));
  1065.     return r;
  1066. }
  1067.  
  1068. void
  1069. init_args(argc0, argc, argv0, argv)
  1070. int argc0, argc;
  1071. char *argv0;
  1072. char **argv;
  1073. {
  1074.     int i, j;
  1075.     NODE **aptr;
  1076.  
  1077.     ARGV_node = spc_var("ARGV", Nnull_string);
  1078.     aptr = assoc_lookup(ARGV_node, tmp_number(0.0));
  1079.     *aptr = make_string(argv0, strlen(argv0));
  1080.     for (i = argc0, j = 1; i < argc; i++) {
  1081.         aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j));
  1082.         *aptr = make_string(argv[i], strlen(argv[i]));
  1083.         j++;
  1084.     }
  1085.     ARGC_node = spc_var("ARGC", make_number((AWKNUM) j));
  1086. }
  1087.