/********************************************
copyright 1991, Michael D. Brennan
This is a source file for mawk, an implementation of
the AWK programming language.
Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/
* Revision 5.1 91/12/05 07:55:35 brennan
static STRING
*PROTO(gsub
, (PTR
, CELL
*, char *, int) ) ;
static void PROTO( fplib_err
, (char *, double, char *) ) ;
/* global for the disassembler */
BI_REC bi_funct
[] = { /* info to load builtins */
"index" , bi_index
, 2, 2 ,
"substr" , bi_substr
, 2, 3,
"length" , bi_length
, 0, 1,
"sprintf" , bi_sprintf
, 1, 255,
"system", bi_system
, 1, 1,
"toupper", bi_toupper
, 1, 1,
"tolower", bi_tolower
, 1, 1,
(char *) 0, (PF_CP
) 0, 0, 0 } ;
{ register BI_REC
*p
= bi_funct
;
{ stp
= insert( p
->name
) ;
/* seed rand() off the clock */
c
.type
= 0 ; (void) bi_srand(&c
) ;
/**************************************************
string builtins (except split (in split.c) and [g]sub (at end))
**************************************************/
if ( sp
->type
== 0 ) cellcpy(sp
, field
) ;
if ( sp
->type
< C_STRING
) cast1_to_s(sp
) ;
free_STRING( string(sp
) ) ;
sp
->dval
= (double) len
;
char *str_str(target
, key
, key_len
)
register char *target
, *key
;
{ case 0 : return (char *) 0 ;
case 1 : return strchr( target
, *key
) ;
while ( target
= strchr(target
, *key
) )
if ( target
[1] == key
[1] ) return target
;
while ( target
= strchr(target
, *key
) )
if ( memcmp(target
+1, key
+1, SIZE_T(key_len
)) == 0 ) return target
;
if ( TEST2(sp
) != TWO_STRINGS
)
if ( len
= string(sp
+1)->len
)
idx
= (p
= str_str(string(sp
)->str
,string(sp
+1)->str
,len
))
? p
- string(sp
)->str
+ 1 : 0 ;
else /* index of the empty string */
free_STRING( string(sp
) ) ;
free_STRING( string(sp
+1) ) ;
sp
->dval
= (double) idx
;
from max(1,i) to min(l,n-i-1) inclusive */
STRING
*sval
; /* substr(sval->str, i, n) */
if ( sp
->type
!= C_STRING
) cast1_to_s(sp
) ;
/* don't use < C_STRING shortcut */
if ( (len
= sval
->len
) == 0 ) /* substr on null string */
{ if ( n_args
== 3 ) cell_destroy(sp
+2) ;
if ( sp
[1].type
!= C_DOUBLE
) cast1_to_d(sp
+1) ;
{ if ( TEST2(sp
+1) != TWO_DOUBLES
) cast2_to_d(sp
+1) ;
i
= (int) sp
[1].dval
- 1 ; /* i now indexes into string */
if ( i
< 0 ) { n
+= i
; i
= 0 ; }
if ( n
> len
- i
) n
= len
- i
;
if ( n
<= 0 ) /* the null string */
sp
->ptr
= (PTR
) &null_str
;
sp
->ptr
= (PTR
) new_STRING((char *)0, n
) ;
(void) memcpy(string(sp
)->str
, sval
->str
+ i
, SIZE_T(n
)) ;
sp[0] holds r, sp[-1] holds s
if ( sp
->type
!= C_RE
) cast_to_RE(sp
) ;
if ( (--sp
)->type
< C_STRING
) cast1_to_s(sp
) ;
RSTART
->type
= C_DOUBLE
;
RLENGTH
->type
= C_DOUBLE
;
p
= REmatch(string(sp
)->str
, (sp
+1)->ptr
, &length
) ;
{ sp
->dval
= (double) ( p
- string(sp
)->str
+ 1 ) ;
RLENGTH
->dval
= (double) length
;
RLENGTH
->dval
= -1.0 ; /* posix */
free_STRING(string(sp
)) ;
RSTART
->dval
= sp
->dval
;
if ( sp
->type
!= C_STRING
) cast1_to_s(sp
) ;
sp
->ptr
= (PTR
) new_STRING((char *) 0, old
->len
) ;
q
= string(sp
)->str
; p
= old
->str
;
if ( *q
>= 'a' && *q
<= 'z' ) *q
+= 'A' - 'a' ;
if ( sp
->type
!= C_STRING
) cast1_to_s(sp
) ;
sp
->ptr
= (PTR
) new_STRING((char *) 0, old
->len
) ;
q
= string(sp
)->str
; p
= old
->str
;
if ( *q
>= 'A' && *q
<= 'Z' ) *q
+= 'a' - 'A' ;
/************************************************
************************************************/
static void fplib_err( fname
, val
, error
)
rt_error("%s(%g) : %s" , fname
, val
, error
) ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= sin( sp
->dval
) ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= sin( sp
->dval
) ;
if ( errno
) fplib_err("sin", x
, "loss of precision") ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= cos( sp
->dval
) ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= cos( sp
->dval
) ;
if ( errno
) fplib_err("cos", x
, "loss of precision") ;
if ( TEST2(sp
) != TWO_DOUBLES
) cast2_to_d(sp
) ;
sp
->dval
= atan2(sp
->dval
, (sp
+1)->dval
) ;
if ( TEST2(sp
) != TWO_DOUBLES
) cast2_to_d(sp
) ;
sp
->dval
= atan2(sp
->dval
, (sp
+1)->dval
) ;
if ( errno
) rt_error("atan2(0,0) : domain error") ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= log( sp
->dval
) ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= log( sp
->dval
) ;
if ( errno
) fplib_err("log", x
, "domain error") ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= exp(sp
->dval
) ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= exp(sp
->dval
) ;
if ( errno
&& sp
->dval
) fplib_err("exp", x
, "overflow") ;
/* on underflow sp->dval==0, ignore */
{ if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= sp
->dval
>= 0.0 ? floor( sp
->dval
) : ceil(sp
->dval
) ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= sqrt( sp
->dval
) ;
if ( sp
->type
!= C_DOUBLE
) cast1_to_d(sp
) ;
sp
->dval
= sqrt( sp
->dval
) ;
if ( errno
) fplib_err("sqrt", x
, "domain error") ;
long biostime(int, long) ;
#define time(x) biostime(0,0L)
/* For portability, we'll use our own random number generator , taken
from: Park, SK and Miller KW, "Random Number Generators:
Good Ones are Hard to Find", CACM, 31, 1192-1201, 1988.
static long seed
; /* must be >=1 and <= 2^31-1 */
static CELL cseed
; /* argument of last call to srand() */
#define M 0x7fffffff /* 2^31-1 */
if ( sp
->type
== 0 ) /* seed off clock */
{ (void) cellcpy(sp
, &cseed
) ;
cseed
.dval
= (double) time((time_t*) 0) ;
/* swap cseed and *sp ; don't need to adjust ref_cnts */
c
= *sp
; *sp
= cseed
; cseed
= c
;
/* The old seed is now in *sp ; move the value in cseed to
(void) cellcpy(&c
, &cseed
) ;
if ( c
.type
== C_NOINIT
) cast1_to_d(&c
) ;
seed
= c
.type
== C_DOUBLE
? ((int)c
.dval
& M
) % M
+ 1 :
hash(string(&c
)->str
) % M
+ 1 ;
/* crank it once so close seeds don't give a close
#define Q 127773 /* M/A */
seed
= A
* (seed
%Q
) - R
* (seed
/Q
) ;
if ( seed
<= 0 ) seed
+= M
;
test
= A
* (seed
%Q
) - R
* (seed
/Q
) ;
if ( test
<= 0 ) test
+= M
;
(++sp
)->type
= C_DOUBLE
;
sp
->dval
= (double)( seed
= test
) / (double) M
;
/*************************************************
close, system and getline
*************************************************/
if ( sp
->type
< C_STRING
) cast1_to_s(sp
) ;
x
= file_close( (STRING
*) sp
->ptr
) ;
free_STRING( string(sp
) ) ;
if ( sp
->type
< C_STRING
) cast1_to_s(sp
) ;
fflush(stdout
) ; fflush(stderr
) ;
{ case -1 : /* fork failed */
errmsg(errno
, "could not create a new process") ;
(void) execl(shell
, shell
, "-c", string(sp
)->str
, (char *) 0) ;
/* if get here, execl() failed */
errmsg(errno
, "execute of %s failed", shell
) ;
default : /* wait for the child */
ret_val
= wait_for(pid
) ;
sp
->dval
= (double) ret_val
;
#endif /* HAVE_REAL_PIPES */
{ rt_error("no system call for the Macintosh Toy Operating System!!!") ;
if ( sp
->type
< C_STRING
) cast1_to_s(sp
) ;
retval
= DOSexec(string(sp
)->str
) ;
free_STRING(string(sp
)) ;
sp
->dval
= (double) retval
;
/* if type == 0 : stack is 0 , target address
if type == F_IN : stack is F_IN, expr(filename), target address
if type == PIPE_IN : stack is PIPE_IN, target address, expr(pipename)
if ( ! main_fin
) open_main() ;
if ( ! (p
= FINgets(main_fin
, &len
)) )
if ( TEST2(NR
) != TWO_DOUBLES
) cast2_to_d(NR
) ;
if ( sp
->type
< C_STRING
) cast1_to_s(sp
) ;
fin_p
= (FIN
*) file_find(sp
->ptr
, F_IN
) ;
free_STRING(string(sp
) ) ;
if ( ! fin_p
) goto open_failure
;
if ( ! (p
= FINgets(fin_p
, &len
)) )
if ( sp
->type
< C_STRING
) cast1_to_s(sp
) ;
fin_p
= (FIN
*) file_find(sp
->ptr
, PIPE_IN
) ;
free_STRING(string(sp
)) ;
if ( ! fin_p
) goto open_failure
;
if ( ! (p
= FINgets(fin_p
, &len
)) )
/* reclaim process slot */
cp
= (CELL
*) (sp
+1)->ptr
;
default : bozo("type in bi_getline") ;
/* we've read a line , store it */
tc
.ptr
= (PTR
) &null_str
;
tc
.ptr
= (PTR
) new_STRING((char *) 0, len
) ;
(void) memcpy( string(&tc
)->str
, p
, SIZE_T(len
)) ;
slow_cell_assign(cp
, &tc
) ;
sp
->dval
= 1.0 ; goto done
;
sp
->dval
= -1.0 ; goto done
;
sp
->dval
= 0.0 ; /* fall thru to done */
/**********************************************
**********************************************/
/* entry: sp[0] = address of CELL to sub on
sp[-1] = substitution CELL
sp[-2] = regular expression to match
{ CELL
*cp
; /* pointer to the replacement target */
CELL tc
; /* build the new string here */
CELL sc
; /* copy of the target CELL */
char *front
, *middle
, *back
; /* pieces */
unsigned front_len
, middle_len
, back_len
;
if ( sp
->type
!= C_RE
) cast_to_RE(sp
) ;
if ( sp
[1].type
!= C_REPL
&& sp
[1].type
!= C_REPLV
)
cp
= (CELL
*) (sp
+2)->ptr
;
/* make a copy of the target, because we won't change anything
including type unless the match works */
(void) cellcpy(&sc
, cp
) ;
if ( sc
.type
< C_STRING
) cast1_to_s(&sc
) ;
front
= string(&sc
)->str
;
if ( middle
= REmatch(front
, sp
->ptr
, &middle_len
) )
front_len
= middle
- front
;
back
= middle
+ middle_len
;
back_len
= string(&sc
)->len
- front_len
- middle_len
;
if ( (sp
+1)->type
== C_REPLV
)
{ STRING
*sval
= new_STRING((char *) 0, middle_len
) ;
(void) memcpy(sval
->str
, middle
, SIZE_T(middle_len
)) ;
(void) replv_to_repl(sp
+1, sval
) ;
tc
.ptr
= (PTR
) new_STRING((char *) 0,
front_len
+ string(sp
+1)->len
+ back_len
) ;
{ char *p
= string(&tc
)->str
;
{ (void) memcpy(p
, front
, SIZE_T(front_len
)) ;
{ (void) memcpy(p
, string(sp
+1)->str
, SIZE_T(string(sp
+1)->len
)) ;
if ( back_len
) (void) memcpy(p
, back
, SIZE_T(back_len
)) ;
slow_cell_assign(cp
, &tc
) ;
free_STRING(string(&tc
)) ;
free_STRING(string(&sc
)) ;
sp
->dval
= middle
!= (char *) 0 ? 1.0 : 0.0 ;
static unsigned repl_cnt
; /* number of global replacements */
/* recursive global subsitution
dealing with empty matches makes this mildly painful
static STRING
*gsub( re
, repl
, target
, flag
)
CELL
*repl
; /* always of type REPL or REPLV,
int flag
; /* if on, match of empty string at front is OK */
unsigned front_len
, middle_len
;
CELL xrepl
; /* a copy of repl so we can change repl */
if ( ! (middle
= REmatch(target
, re
, &middle_len
)) )
return new_STRING(target
) ; /* no match */
(void) cellcpy(&xrepl
, repl
) ;
if ( !flag
&& middle_len
== 0 && middle
== target
)
{ /* match at front that's not allowed */
if ( *target
== 0 ) /* target is empty string */
/* make new repl with target[0] */
xbuff
[0] = *target
++ ; xbuff
[1] = 0 ;
repl
->ptr
= (PTR
) new_STRING( xbuff
) ;
back
= gsub(re
, &xrepl
, target
, 1) ;
else /* a match that counts */
front_len
= middle
- target
;
if ( *middle
== 0 ) /* matched back of target */
{ back
= &null_str
; null_str
.ref_cnt
++ ; }
else back
= gsub(re
, &xrepl
, middle
+ middle_len
, 0) ;
/* patch the &'s if needed */
if ( repl
->type
== C_REPLV
)
{ STRING
*sval
= new_STRING((char *) 0, middle_len
) ;
(void) memcpy(sval
->str
, middle
, SIZE_T(middle_len
)) ;
(void) replv_to_repl(repl
, sval
) ;
/* put the three pieces together */
ret_val
= new_STRING((char *)0,
front_len
+ string(repl
)->len
+ back
->len
);
{ char *p
= ret_val
->str
;
{ (void) memcpy(p
, front
, SIZE_T(front_len
)) ; p
+= front_len
; }
{ (void) memcpy(p
, string(repl
)->str
, SIZE_T(string(repl
)->len
)) ;
if ( back
->len
) (void) memcpy(p
, back
->str
, SIZE_T(back
->len
)) ;
/* cleanup, repl is freed by the caller */
/* set up for call to gsub() */
{ CELL
*cp
; /* pts at the replacement target */
CELL sc
; /* copy of replacement target */
CELL tc
; /* build the result here */
if ( sp
->type
!= C_RE
) cast_to_RE(sp
) ;
if ( (sp
+1)->type
!= C_REPL
&& (sp
+1)->type
!= C_REPLV
)
(void) cellcpy(&sc
, cp
= (CELL
*)(sp
+2)->ptr
) ;
if ( sc
.type
< C_STRING
) cast1_to_s(&sc
) ;
tc
.ptr
= (PTR
) gsub(sp
->ptr
, sp
+1, string(&sc
)->str
, 1) ;
slow_cell_assign(cp
, &tc
) ;
free_STRING(string(&sc
)) ; free_STRING(string(&tc
)) ;
sp
->dval
= (double) repl_cnt
;