home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / m / mawk11as.zip / BI_FUNCT.C < prev    next >
C/C++ Source or Header  |  1991-12-18  |  20KB  |  867 lines

  1.  
  2. /********************************************
  3. bi_funct.c
  4. copyright 1991, Michael D. Brennan
  5.  
  6. This is a source file for mawk, an implementation of
  7. the AWK programming language.
  8.  
  9. Mawk is distributed without warranty under the terms of
  10. the GNU General Public License, version 2, 1991.
  11. ********************************************/
  12.  
  13. /* $Log:    bi_funct.c,v $
  14.  * Revision 5.1  91/12/05  07:55:35  brennan
  15.  * 1.1 pre-release
  16.  * 
  17. */
  18.  
  19.  
  20. #include "mawk.h"
  21. #include "bi_funct.h"
  22. #include "bi_vars.h"
  23. #include "memory.h"
  24. #include "init.h"
  25. #include "files.h"
  26. #include "fin.h"
  27. #include "field.h"
  28. #include "regexp.h"
  29. #include "repl.h"
  30. #include <math.h>
  31.  
  32.  
  33. /* statics */
  34. static STRING *PROTO(gsub, (PTR, CELL *, char *, int) ) ;
  35. static void  PROTO( fplib_err, (char *, double, char *) ) ;
  36.  
  37.  
  38. /* global for the disassembler */
  39. BI_REC  bi_funct[] = { /* info to load builtins */
  40.  
  41. "index" , bi_index , 2, 2 ,
  42. "substr" , bi_substr, 2, 3,
  43. "length" , bi_length, 0, 1,
  44. "sprintf" , bi_sprintf, 1, 255,
  45. "sin", bi_sin , 1, 1 ,
  46. "cos", bi_cos , 1, 1 ,
  47. "atan2", bi_atan2, 2,2,
  48. "exp", bi_exp, 1, 1,
  49. "log", bi_log , 1, 1 ,
  50. "int", bi_int, 1, 1,
  51. "sqrt", bi_sqrt, 1, 1,
  52. "rand" , bi_rand, 0, 0,
  53. "srand", bi_srand, 0, 1,
  54. "close", bi_close, 1, 1,
  55. "system", bi_system, 1, 1,
  56. "toupper", bi_toupper, 1, 1,
  57. "tolower", bi_tolower, 1, 1,
  58.  
  59. (char *) 0, (PF_CP) 0, 0, 0 } ;
  60.  
  61.  
  62.  
  63. void bi_funct_init()
  64. { register BI_REC *p = bi_funct ;
  65.   register SYMTAB *stp ;
  66.  
  67.   while ( p->name )
  68.   { stp = insert( p->name ) ;
  69.     stp->type = ST_BUILTIN ;
  70.     stp->stval.bip = p++ ;
  71.   }
  72.   /* seed rand() off the clock */
  73.   { CELL c ;
  74.  
  75.     c.type = 0 ; (void) bi_srand(&c) ;
  76.   }
  77.  
  78. }
  79.  
  80. /**************************************************
  81.  string builtins (except split (in split.c) and [g]sub (at end))
  82.  **************************************************/
  83.  
  84. CELL *bi_length(sp)
  85.   register  CELL *sp ;
  86. { unsigned len ;
  87.  
  88.   if ( sp->type == 0 )  cellcpy(sp, field) ;
  89.   else sp-- ;
  90.  
  91.   if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  92.   len = string(sp)->len ;
  93.  
  94.   free_STRING( string(sp) ) ;
  95.   sp->type = C_DOUBLE ;
  96.   sp->dval = (double) len ;
  97.  
  98.   return sp ;
  99. }
  100.  
  101. char *str_str(target, key , key_len)
  102.   register char *target, *key ;
  103.   unsigned key_len ;
  104.   switch( key_len )
  105.   { case 0 :  return (char *) 0 ;
  106.     case 1 :  return strchr( target, *key) ;
  107.     case 2 :
  108.         while ( target = strchr(target, *key) )
  109.           if ( target[1] == key[1] )  return  target ;
  110.           else target++ ;
  111.         /*failed*/
  112.         return (char *) 0 ;
  113.   }
  114.   key_len-- ;
  115.   while ( target = strchr(target, *key) )
  116.         if ( memcmp(target+1, key+1, SIZE_T(key_len)) == 0 ) return target ;
  117.         else target++ ;
  118.   /*failed*/
  119.   return (char *) 0 ;
  120. }
  121.  
  122.  
  123.  
  124. CELL *bi_index(sp)
  125.   register CELL *sp ;
  126. { register int idx ;
  127.   unsigned len ;
  128.   char *p ;
  129.  
  130.   sp-- ;
  131.   if ( TEST2(sp) != TWO_STRINGS )
  132.         cast2_to_s(sp) ;
  133.  
  134.   if ( len = string(sp+1)->len )
  135.     idx = (p = str_str(string(sp)->str,string(sp+1)->str,len))
  136.           ? p - string(sp)->str + 1 : 0 ;
  137.  
  138.   else  /* index of the empty string */
  139.     idx = 1 ;
  140.   
  141.   free_STRING( string(sp) ) ;
  142.   free_STRING( string(sp+1) ) ;
  143.   sp->type = C_DOUBLE ;
  144.   sp->dval = (double) idx ;
  145.   return sp ;
  146. }
  147.  
  148. /*  substr(s, i, n)
  149.     if l = length(s)
  150.     then get the characters
  151.     from  max(1,i) to min(l,n-i-1) inclusive */
  152.  
  153. CELL *bi_substr(sp)
  154.   CELL *sp ;
  155. { int n_args, len ;
  156.   register int i, n ;
  157.   STRING *sval ;  /* substr(sval->str, i, n) */
  158.  
  159.   n_args = sp->type ;
  160.   sp -= n_args ;
  161.   if ( sp->type != C_STRING )  cast1_to_s(sp) ;
  162.       /* don't use < C_STRING shortcut */
  163.   sval = string(sp) ;
  164.  
  165.   if ( (len = sval->len) == 0 )  /* substr on null string */
  166.   {  if ( n_args == 3 )  cell_destroy(sp+2) ;
  167.      cell_destroy(sp+1) ;
  168.      return sp ;
  169.   }
  170.  
  171.   if ( n_args == 2 )  
  172.   { n = MAX__INT  ;  
  173.     if ( sp[1].type != C_DOUBLE ) cast1_to_d(sp+1) ; 
  174.   }
  175.   else
  176.   { if ( TEST2(sp+1) != TWO_DOUBLES ) cast2_to_d(sp+1) ;
  177.     n = (int) sp[2].dval ;
  178.   }
  179.   i = (int) sp[1].dval - 1 ; /* i now indexes into string */
  180.  
  181.   if ( i < 0 ) { n += i ; i = 0 ; }
  182.   if ( n > len - i )  n = len - i ;
  183.  
  184.   if ( n <= 0 )  /* the null string */
  185.   { 
  186.     sp->ptr = (PTR) &null_str ;
  187.     null_str.ref_cnt++ ;
  188.   }
  189.   else  /* got something */
  190.   { 
  191.     sp->ptr = (PTR) new_STRING((char *)0, n) ;
  192.     (void) memcpy(string(sp)->str, sval->str + i, SIZE_T(n)) ;
  193.   }
  194.  
  195.   free_STRING(sval) ;
  196.   return sp ;
  197.  
  198. /*
  199.   match(s,r)
  200.   sp[0] holds r, sp[-1] holds s
  201. */
  202.  
  203. CELL *bi_match(sp)
  204.   register CELL *sp ;
  205.   char *p ;
  206.   unsigned length ;
  207.  
  208.   if ( sp->type != C_RE )  cast_to_RE(sp) ;
  209.   if ( (--sp)->type < C_STRING )  cast1_to_s(sp) ;
  210.  
  211.   cell_destroy(RSTART) ;
  212.   cell_destroy(RLENGTH) ;
  213.   RSTART->type = C_DOUBLE ;
  214.   RLENGTH->type = C_DOUBLE ;
  215.  
  216.   p = REmatch(string(sp)->str, (sp+1)->ptr, &length) ;
  217.  
  218.   if ( p )
  219.   { sp->dval = (double) ( p - string(sp)->str + 1 ) ;
  220.     RLENGTH->dval = (double) length ;
  221.   }
  222.   else
  223.   { sp->dval = 0.0 ;
  224.     RLENGTH->dval = -1.0 ; /* posix */
  225.   }
  226.  
  227.   free_STRING(string(sp)) ;
  228.   sp->type = C_DOUBLE ;
  229.  
  230.   RSTART->dval = sp->dval ;
  231.  
  232.   return sp ;
  233. }
  234.  
  235. CELL *bi_toupper(sp)
  236.   CELL *sp ;
  237. { STRING *old ;
  238.   register char *p, *q ;
  239.  
  240.   if ( sp->type != C_STRING )  cast1_to_s(sp) ;
  241.   old = string(sp) ;
  242.   sp->ptr = (PTR) new_STRING((char *) 0, old->len) ;
  243.  
  244.   q = string(sp)->str ; p = old->str ;
  245.  
  246.   while ( *p )
  247.   {
  248.     *q = *p++ ;
  249.     if ( *q >= 'a' && *q <= 'z' )  *q += 'A' - 'a' ;
  250.     q++ ;
  251.   }
  252.   free_STRING(old) ;
  253.   return sp ;
  254. }
  255.  
  256. CELL *bi_tolower(sp)
  257.   CELL *sp ;
  258. { STRING *old ;
  259.   register char *p, *q ;
  260.  
  261.   if ( sp->type != C_STRING )  cast1_to_s(sp) ;
  262.   old = string(sp) ;
  263.   sp->ptr = (PTR) new_STRING((char *) 0, old->len) ;
  264.  
  265.   q = string(sp)->str ; p = old->str ;
  266.  
  267.   while ( *p )
  268.   {
  269.     *q = *p++ ;
  270.     if ( *q >= 'A' && *q <= 'Z' )  *q += 'a' - 'A' ;
  271.     q++ ;
  272.   }
  273.   free_STRING(old) ;
  274.   return sp ;
  275. }
  276.  
  277.  
  278. /************************************************
  279.   arithemetic builtins
  280.  ************************************************/
  281.  
  282. static void fplib_err( fname, val, error)
  283.   char *fname ;
  284.   double val ;
  285.   char *error ;
  286. {
  287.   rt_error("%s(%g) : %s" , fname, val, error) ;
  288. }
  289.  
  290.  
  291. CELL *bi_sin(sp)
  292.   register CELL *sp ;
  293. #if ! STDC_MATHERR
  294.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  295.   sp->dval = sin( sp->dval ) ;
  296.   return sp ;
  297. #else
  298.   double x ;
  299.  
  300.   errno = 0 ;
  301.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  302.   x = sp->dval ;
  303.   sp->dval = sin( sp->dval ) ;
  304.   if ( errno )  fplib_err("sin", x, "loss of precision") ;
  305.   return sp ;
  306. #endif
  307. }
  308.  
  309. CELL *bi_cos(sp)
  310.   register CELL *sp ;
  311. #if ! STDC_MATHERR
  312.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  313.   sp->dval = cos( sp->dval ) ;
  314.   return sp ;
  315. #else
  316.   double x ;
  317.  
  318.   errno = 0 ;
  319.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  320.   x = sp->dval ;
  321.   sp->dval = cos( sp->dval ) ;
  322.   if ( errno )  fplib_err("cos", x, "loss of precision") ;
  323.   return sp ;
  324. #endif
  325. }
  326.  
  327. CELL *bi_atan2(sp)
  328.   register CELL *sp ;
  329. #if  !  STDC_MATHERR
  330.   sp-- ;
  331.   if ( TEST2(sp) != TWO_DOUBLES ) cast2_to_d(sp) ;
  332.   sp->dval = atan2(sp->dval, (sp+1)->dval) ;
  333.   return sp ;
  334. #else
  335.  
  336.   errno = 0 ;
  337.   sp-- ;
  338.   if ( TEST2(sp) != TWO_DOUBLES ) cast2_to_d(sp) ;
  339.   sp->dval = atan2(sp->dval, (sp+1)->dval) ;
  340.   if ( errno ) rt_error("atan2(0,0) : domain error") ;
  341.   return sp ;
  342. #endif
  343. }
  344.  
  345. CELL *bi_log(sp)
  346.   register CELL *sp ;
  347. #if ! STDC_MATHERR
  348.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  349.   sp->dval = log( sp->dval ) ;
  350.   return sp ;
  351. #else
  352.   double  x ;
  353.  
  354.   errno = 0 ;
  355.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  356.   x = sp->dval ;
  357.   sp->dval = log( sp->dval ) ;
  358.   if ( errno )  fplib_err("log", x, "domain error") ;
  359.   return sp ;
  360. #endif
  361. }
  362.  
  363. CELL *bi_exp(sp)
  364.   register CELL *sp ;
  365. #if  ! STDC_MATHERR
  366.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  367.   sp->dval = exp(sp->dval) ;
  368.   return sp ;
  369. #else
  370.   double  x ;
  371.  
  372.   errno = 0 ;
  373.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  374.   x = sp->dval ;
  375.   sp->dval = exp(sp->dval) ;
  376.   if ( errno && sp->dval)  fplib_err("exp", x, "overflow") ;
  377.      /* on underflow sp->dval==0, ignore */
  378.   return sp ;
  379. #endif
  380. }
  381.  
  382. CELL *bi_int(sp)
  383.   register CELL *sp ;
  384. { if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  385.   sp->dval = sp->dval >= 0.0 ? floor( sp->dval ) : ceil(sp->dval)  ;
  386.   return sp ;
  387. }
  388.  
  389. CELL *bi_sqrt(sp)
  390.   register CELL *sp ;
  391. #if  ! STDC_MATHERR
  392.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  393.   sp->dval = sqrt( sp->dval ) ;
  394.   return sp ;
  395. #else
  396.   double x ;
  397.  
  398.   errno = 0 ;
  399.   if ( sp->type != C_DOUBLE )  cast1_to_d(sp) ;
  400.   x = sp->dval ;
  401.   sp->dval = sqrt( sp->dval ) ;
  402.   if ( errno )  fplib_err("sqrt", x, "domain error") ;
  403.   return sp ;
  404. #endif
  405. }
  406.  
  407. #ifdef  __TURBOC__
  408. long  biostime(int, long) ;
  409. #define  time(x)  biostime(0,0L)
  410. #else
  411. #ifdef THINK_C
  412. #include <time.h>
  413. #else
  414. #include <sys/types.h>
  415. #endif
  416. #endif
  417.  
  418.  
  419. /* For portability, we'll use our own random number generator , taken
  420.    from:  Park, SK and Miller KW, "Random Number Generators:
  421.    Good Ones are Hard to Find", CACM, 31, 1192-1201, 1988.
  422. */
  423.  
  424. static long seed ;  /* must be >=1 and <= 2^31-1 */
  425. static CELL cseed ; /* argument of last call to srand() */
  426.  
  427. #define         M       0x7fffffff   /* 2^31-1 */
  428.  
  429. CELL *bi_srand(sp)
  430.   register CELL *sp ;
  431. { CELL c ;
  432.  
  433.   if ( sp->type == 0 ) /* seed off clock */
  434.   { (void) cellcpy(sp, &cseed) ;
  435.     cell_destroy(&cseed) ;
  436.     cseed.type = C_DOUBLE ;
  437.     cseed.dval = (double) time((time_t*) 0) ;
  438.   }
  439.   else /* user seed */
  440.   { sp-- ;
  441.     /* swap cseed and *sp ; don't need to adjust ref_cnts */
  442.     c = *sp ; *sp = cseed ; cseed = c ;
  443.   }
  444.  
  445.   /* The old seed is now in *sp ; move the value in cseed to
  446.      seed in range 1 to M */
  447.  
  448.   (void) cellcpy(&c, &cseed) ;
  449.   if ( c.type == C_NOINIT )  cast1_to_d(&c) ;
  450.  
  451.   seed =  c.type == C_DOUBLE ? ((int)c.dval & M) % M + 1 :
  452.                         hash(string(&c)->str) % M + 1 ;
  453.  
  454.   cell_destroy(&c) ;
  455.  
  456.   /* crank it once so close seeds don't give a close 
  457.        first result  */
  458. #define   A     16807
  459. #define   Q     127773   /* M/A */
  460. #define   R     2836     /* M%A */
  461.   seed = A * (seed%Q) - R * (seed/Q) ;
  462.   if ( seed <= 0 )  seed += M ;
  463.  
  464.   return sp ;
  465. }
  466.     
  467. CELL *bi_rand(sp)
  468.   register CELL *sp ;
  469.   register long test ;
  470.  
  471.   test = A * (seed%Q) - R * (seed/Q) ;
  472.   if ( test <= 0 )  test += M ;
  473.  
  474.   (++sp)->type = C_DOUBLE ;
  475.   sp->dval = (double)( seed = test ) / (double) M ;
  476.   return sp ;
  477.  
  478. #undef   A
  479. #undef   M
  480. #undef   Q
  481. #undef   R
  482. }
  483.  
  484. /*************************************************
  485.  miscellaneous builtins
  486.  close, system and getline
  487.  *************************************************/
  488.  
  489. CELL *bi_close(sp)
  490.   register CELL *sp ;
  491. { int x ;
  492.  
  493.   if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  494.   x = file_close( (STRING *) sp->ptr) ;
  495.   free_STRING( string(sp) ) ;
  496.   sp->type = C_DOUBLE ;
  497.   sp->dval = (double) x ;
  498.   return sp ;
  499. }
  500.  
  501. #if   HAVE_REAL_PIPES
  502.  
  503. CELL *bi_system(sp)
  504.   CELL *sp ;
  505. { int pid ;
  506.   unsigned ret_val ;
  507.  
  508.   if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  509.  
  510.   fflush(stdout) ; fflush(stderr) ;
  511.  
  512.   switch( pid = fork() )
  513.   { case -1 :  /* fork failed */
  514.  
  515.        errmsg(errno, "could not create a new process") ;
  516.        ret_val = 127 ;
  517.        break ;
  518.  
  519.     case  0  :  /* the child */
  520.        (void) execl(shell, shell, "-c", string(sp)->str, (char *) 0) ;
  521.        /* if get here, execl() failed */
  522.        errmsg(errno, "execute of %s failed", shell) ;
  523.        fflush(stderr) ;
  524.        _exit(127) ;
  525.  
  526.     default   :  /* wait for the child */
  527.        ret_val = wait_for(pid) ;
  528.        break ;
  529.   }
  530.  
  531.   cell_destroy(sp) ;
  532.   sp->type = C_DOUBLE ;
  533.   sp->dval = (double) ret_val ;
  534.   return sp ;
  535. }
  536.  
  537. #endif /* HAVE_REAL_PIPES */
  538.  
  539. #ifdef  THINK_C
  540.  
  541. CELL *bi_system( sp )
  542.   register CELL *sp ;
  543. { rt_error("no system call for the Macintosh Toy Operating System!!!") ;
  544.   return sp ;
  545. }
  546.  
  547. #endif
  548.  
  549.  
  550. #if   MSDOS
  551.  
  552.  
  553. CELL *bi_system( sp )
  554.   register CELL *sp ;
  555. { int retval ;
  556.  
  557.   if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  558.   retval = DOSexec(string(sp)->str) ;
  559.   free_STRING(string(sp)) ;
  560.   sp->type = C_DOUBLE ;
  561.   sp->dval = (double) retval ;
  562.   return sp ;
  563. }
  564.  
  565. #endif
  566.  
  567.  
  568. /*  getline()  */
  569.  
  570. /*  if type == 0 :  stack is 0 , target address
  571.  
  572.     if type == F_IN : stack is F_IN, expr(filename), target address
  573.  
  574.     if type == PIPE_IN : stack is PIPE_IN, target address, expr(pipename)
  575. */
  576.  
  577. CELL *bi_getline(sp)
  578.   register CELL *sp ;
  579.   CELL tc , *cp ;
  580.   char *p ;
  581.   unsigned len ;
  582.   FIN *fin_p ;
  583.  
  584.  
  585.   switch( sp->type )
  586.   { 
  587.     case 0 :
  588.         sp-- ;
  589.         if ( ! main_fin )  open_main() ;
  590.     
  591.         if ( ! (p = FINgets(main_fin, &len)) )
  592.                 goto  eof ;
  593.  
  594.         cp = (CELL *) sp->ptr ;
  595.         if ( TEST2(NR) != TWO_DOUBLES ) cast2_to_d(NR) ;
  596.         NR->dval += 1.0 ;
  597.         FNR->dval += 1.0 ;
  598.         break ;
  599.  
  600.     case  F_IN :
  601.         sp-- ;
  602.         if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  603.         fin_p = (FIN *) file_find(sp->ptr, F_IN) ;
  604.         free_STRING(string(sp) ) ;
  605.         sp-- ;
  606.  
  607.         if ( ! fin_p )   goto open_failure ;
  608.         if ( ! (p = FINgets(fin_p, &len)) )  
  609.         {
  610.           FINsemi_close(fin_p) ;
  611.           goto eof ; 
  612.         }
  613.         cp = (CELL *) sp->ptr ;
  614.         break ;
  615.  
  616.     case PIPE_IN :
  617.         sp -= 2 ;
  618.         if ( sp->type < C_STRING ) cast1_to_s(sp) ;
  619.         fin_p = (FIN *) file_find(sp->ptr, PIPE_IN) ;
  620.         free_STRING(string(sp)) ;
  621.  
  622.         if ( ! fin_p )   goto open_failure ;
  623.         if ( ! (p = FINgets(fin_p, &len)) ) 
  624.         { 
  625.           FINsemi_close(fin_p) ;
  626. #if  HAVE_REAL_PIPES
  627.           /* reclaim process slot */
  628.           (void) wait_for(0) ;
  629. #endif
  630.           goto eof ; 
  631.         }
  632.         cp = (CELL *) (sp+1)->ptr ;
  633.         break ;
  634.  
  635.     default : bozo("type in bi_getline") ;
  636.  
  637.   }
  638.  
  639.   /* we've read a line , store it */
  640.  
  641.     if ( len == 0 )
  642.     { tc.type = C_STRING ; 
  643.       tc.ptr = (PTR) &null_str ; 
  644.       null_str.ref_cnt++ ;
  645.     }
  646.     else
  647.     { tc.type = C_MBSTRN ;
  648.       tc.ptr = (PTR) new_STRING((char *) 0, len) ;
  649.       (void) memcpy( string(&tc)->str, p, SIZE_T(len)) ;
  650.     }
  651.  
  652.     slow_cell_assign(cp, &tc) ;
  653.  
  654.     cell_destroy(&tc) ;
  655.  
  656.   sp->dval = 1.0  ;  goto done ;
  657.  
  658. open_failure :
  659.   sp->dval = -1.0  ; goto done ;
  660.  
  661. eof :
  662.   sp->dval = 0.0  ;  /* fall thru to done  */
  663.  
  664. done :
  665.   sp->type = C_DOUBLE  ;
  666.   return sp ;
  667. }
  668.  
  669. /**********************************************
  670.  sub() and gsub()
  671.  **********************************************/
  672.  
  673. /* entry:  sp[0] = address of CELL to sub on
  674.            sp[-1] = substitution CELL
  675.            sp[-2] = regular expression to match
  676. */
  677.  
  678. CELL *bi_sub( sp )
  679.   register CELL *sp ;
  680. { CELL *cp ; /* pointer to the replacement target */
  681.   CELL tc ;  /* build the new string here */
  682.   CELL sc ;  /* copy of the target CELL */
  683.   char *front, *middle, *back ; /* pieces */
  684.   unsigned front_len, middle_len, back_len ;
  685.  
  686.   sp -= 2 ;
  687.   if ( sp->type != C_RE )  cast_to_RE(sp) ;
  688.   if ( sp[1].type != C_REPL && sp[1].type != C_REPLV )
  689.               cast_to_REPL(sp+1) ;
  690.   cp = (CELL *) (sp+2)->ptr ;
  691.   /* make a copy of the target, because we won't change anything
  692.      including type unless the match works */
  693.   (void) cellcpy(&sc, cp) ;
  694.   if ( sc.type < C_STRING ) cast1_to_s(&sc) ;
  695.   front = string(&sc)->str ;
  696.  
  697.   if ( middle = REmatch(front, sp->ptr, &middle_len) )
  698.   { 
  699.     front_len = middle - front ;
  700.     back = middle + middle_len ; 
  701.     back_len = string(&sc)->len - front_len - middle_len ;
  702.  
  703.     if ( (sp+1)->type == C_REPLV ) 
  704.     { STRING *sval = new_STRING((char *) 0, middle_len) ;
  705.  
  706.       (void) memcpy(sval->str, middle, SIZE_T(middle_len)) ;
  707.       (void) replv_to_repl(sp+1, sval) ;
  708.       free_STRING(sval) ;
  709.     }
  710.  
  711.     tc.type = C_STRING ;
  712.     tc.ptr = (PTR) new_STRING((char *) 0, 
  713.              front_len + string(sp+1)->len + back_len ) ;
  714.  
  715.     { char *p = string(&tc)->str ;
  716.  
  717.       if ( front_len )
  718.       { (void) memcpy(p, front, SIZE_T(front_len)) ;
  719.         p += front_len ;
  720.       }
  721.       if ( string(sp+1)->len )
  722.       { (void) memcpy(p, string(sp+1)->str, SIZE_T(string(sp+1)->len)) ;
  723.         p += string(sp+1)->len ;
  724.       }
  725.       if ( back_len )  (void) memcpy(p, back, SIZE_T(back_len)) ;
  726.     }
  727.  
  728.     slow_cell_assign(cp, &tc) ;
  729.  
  730.     free_STRING(string(&tc)) ;
  731.   }
  732.  
  733.   free_STRING(string(&sc)) ;
  734.   repl_destroy(sp+1) ;
  735.   sp->type = C_DOUBLE ;
  736.   sp->dval = middle != (char *) 0 ? 1.0 : 0.0 ;
  737.   return sp ;
  738. }
  739.  
  740. static  unsigned repl_cnt ;  /* number of global replacements */
  741.  
  742. /* recursive global subsitution 
  743.    dealing with empty matches makes this mildly painful
  744. */
  745.  
  746. static STRING *gsub( re, repl, target, flag)
  747.   PTR  re ;
  748.   CELL *repl ;  /* always of type REPL or REPLV, 
  749.        destroyed by caller */
  750.   char *target ;
  751.   int flag ; /* if on, match of empty string at front is OK */
  752. { char *front, *middle ;
  753.   STRING *back ;
  754.   unsigned front_len, middle_len ;
  755.   STRING  *ret_val ;
  756.   CELL xrepl ; /* a copy of repl so we can change repl */
  757.  
  758.   if ( ! (middle = REmatch(target, re, &middle_len)) )
  759.       return  new_STRING(target) ; /* no match */
  760.  
  761.   (void) cellcpy(&xrepl, repl) ;
  762.  
  763.   if ( !flag && middle_len == 0 && middle == target ) 
  764.   { /* match at front that's not allowed */
  765.  
  766.     if ( *target == 0 )  /* target is empty string */
  767.     { repl_destroy(&xrepl) ;
  768.       null_str.ref_cnt++ ;
  769.       return & null_str ;
  770.     }
  771.     else
  772.     { char xbuff[2] ;
  773.  
  774.       front_len = 0 ;
  775.       /* make new repl with target[0] */
  776.       repl_destroy(repl) ;
  777.       xbuff[0] = *target++ ;  xbuff[1] = 0 ;
  778.       repl->type = C_REPL ;
  779.       repl->ptr = (PTR) new_STRING( xbuff ) ;
  780.       back = gsub(re, &xrepl, target, 1) ;
  781.     }
  782.   }
  783.   else  /* a match that counts */
  784.   { repl_cnt++ ;
  785.  
  786.     front = target ;
  787.     front_len = middle - target ;
  788.  
  789.     if ( *middle == 0 )  /* matched back of target */
  790.     { back = &null_str ; null_str.ref_cnt++ ; }
  791.     else back = gsub(re, &xrepl, middle + middle_len, 0) ;
  792.       
  793.     /* patch the &'s if needed */
  794.     if ( repl->type == C_REPLV )
  795.     { STRING *sval = new_STRING((char *) 0, middle_len) ;
  796.  
  797.       (void) memcpy(sval->str, middle, SIZE_T(middle_len)) ;
  798.       (void) replv_to_repl(repl, sval) ;
  799.       free_STRING(sval) ;
  800.     }
  801.   }
  802.  
  803.   /* put the three pieces together */
  804.   ret_val = new_STRING((char *)0,
  805.               front_len + string(repl)->len + back->len); 
  806.   { char *p = ret_val->str ;
  807.  
  808.     if ( front_len )
  809.     { (void) memcpy(p, front, SIZE_T(front_len)) ; p += front_len ; }
  810.     if ( string(repl)->len )
  811.     { (void) memcpy(p, string(repl)->str, SIZE_T(string(repl)->len)) ;
  812.       p += string(repl)->len ;
  813.     }
  814.     if ( back->len ) (void) memcpy(p, back->str, SIZE_T(back->len)) ;
  815.   }
  816.  
  817.   /* cleanup, repl is freed by the caller */
  818.   repl_destroy(&xrepl) ;
  819.   free_STRING(back) ;
  820.  
  821.   return ret_val ;
  822. }
  823.  
  824. /* set up for call to gsub() */
  825. CELL *bi_gsub( sp )
  826.   register CELL *sp ;
  827. { CELL *cp ;  /* pts at the replacement target */
  828.   CELL sc  ;  /* copy of replacement target */
  829.   CELL tc  ;  /* build the result here */
  830.  
  831.   sp -= 2 ;
  832.   if ( sp->type != C_RE ) cast_to_RE(sp) ;
  833.   if ( (sp+1)->type != C_REPL && (sp+1)->type != C_REPLV )
  834.           cast_to_REPL(sp+1) ;
  835.  
  836.   (void) cellcpy(&sc, cp = (CELL *)(sp+2)->ptr) ;
  837.   if ( sc.type < C_STRING ) cast1_to_s(&sc) ;
  838.  
  839.   repl_cnt = 0 ;
  840.   tc.ptr = (PTR) gsub(sp->ptr, sp+1, string(&sc)->str, 1) ;
  841.  
  842.   if ( repl_cnt )
  843.   { 
  844.     tc.type = C_STRING ;
  845.     slow_cell_assign(cp, &tc) ;
  846.   }
  847.  
  848.   /* cleanup */
  849.   free_STRING(string(&sc)) ; free_STRING(string(&tc)) ;
  850.   repl_destroy(sp+1) ;
  851.  
  852.   sp->type = C_DOUBLE ;
  853.   sp->dval = (double) repl_cnt ;
  854.   return sp ;
  855. }
  856.