home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / TOP / USR / SRC / gawk2.0.t.Z / gawk2.0.t / awk3.c < prev    next >
Text File  |  1989-04-06  |  26KB  |  1,203 lines

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