[alt.sources] mawk0.97.shar 3 of 6

brennan@ssc-vax.UUCP (Mike Brennan) (05/11/91)

------------------cut here----------------
  register FCALL_REC *q = &dummy ; /* trails p */

  q->link = p ;
  while ( p )
  {
    if ( ! p->callee->code )
    { /* callee never defined */
      errmsg(0, "line %u: function %s never defined" ,
          p->line_no, p->callee->name) ;
      if ( ++compile_error_count == MAX_COMPILE_ERRORS ) 
              mawk_exit(1) ;
      /* delete p from list */
      q->link = p->link ;
      /* don't worry about freeing memory, we'll exit soon */
    }
    else  /* note p->arg_list starts with last argument */
    if ( ! p->arg_list  /* nothing to do */  ||
         ! p->arg_cnt_checked && 
         ! arg_cnt_ok(p->callee, p->arg_list, p->line_no) )
    {  q->link = p->link ; /* delete p */ 
       /* the ! arg_list case is not an error so free memory */
       zfree(p, sizeof(FCALL_REC)) ;
    }
    else
    { /* keep p and set call_start */
      q = p ;
      switch ( p->call_scope )
      {
        case SCOPE_MAIN  :
                p->call_start = main_start ;
                break ;

        case SCOPE_BEGIN :
                p->call_start = begin_start ;
                break ;

        case SCOPE_END :
                p->call_start = end_start ;
                break ;

        case SCOPE_FUNCT :
                p->call_start = p->call->code ;
                break ;
      }
    }
    p = q->link ;
  }
  return  dummy.link ;
}

/* continuously walk the resolve_list making type deductions
   until this list goes empty or no more progress can be made
   (An example where no more progress can be made is at end of file
*/

void  resolve_fcalls()
{ register FCALL_REC *p, *old_list , *new_list ;
  int progress ; /* a flag */

  old_list = first_pass(resolve_list) ;
  new_list = (FCALL_REC *) 0 ;
  progress = 0 ;

  while ( 1 )
  {
    if ( !(p = old_list) )
    { /* flop the lists */
      if ( !(p = old_list = new_list)  /* nothing left */
          || ! progress    /* can't do any more */ )  return ;

      /* reset after flop */
      new_list = (FCALL_REC *) 0 ;  progress = 0 ;
    }

    old_list = p->link ;

    if ( p->arg_list = call_arg_check(p->callee, p->arg_list ,
              p->call_start, p->line_no)  )
    {
       /* still have work to do , put on new_list   */
       progress  |= check_progress ;
       p->link = new_list ;  new_list = p ;
    }
    else  /* done with p */
    {  progress = 1 ;  zfree(p, sizeof(FCALL_REC)) ; }
  }
}

/* the parser has just reduced a function call ;
   the info needed to type check is passed in.  If type checking
   can not be done yet (most common reason -- function referenced
   but not defined), a node is added to the resolve list.
*/
void check_fcall( callee, call_scope, call, arg_list, line_no )
  FBLOCK *callee ;
  int call_scope ;
  FBLOCK  *call ;
  CA_REC  *arg_list ;
  unsigned line_no ;
{
  FCALL_REC *p ;
  INST *call_start ;

  if ( ! callee->code ) 
  { /* forward reference to a function to be defined later */
    p = (FCALL_REC *) zmalloc(sizeof(FCALL_REC)) ;
    p->callee = callee ;
    p->call_scope = call_scope ;
    p->call = call ;
    p->arg_list = arg_list ;
    p->arg_cnt_checked = 0 ;
    p->line_no = line_no ;
    /* add to resolve list */
    p->link = resolve_list ; resolve_list = p ;
  }
  else
  if ( arg_list && arg_cnt_ok( callee, arg_list, line_no ) )
  {
      switch ( call_scope )
      {
        case SCOPE_MAIN  :
                call_start = main_start ;
                break ;

        case SCOPE_BEGIN :
                call_start = begin_start ;
                break ;

        case SCOPE_END :
                call_start = end_start ;
                break ;

        case SCOPE_FUNCT :
                call_start = call->code ;
                break ;
      }

      /* usually arg_list disappears here and all is well
         otherwise add to resolve list */

      if ( arg_list = call_arg_check(callee, arg_list,
                call_start, line_no) )
      {
            p = (FCALL_REC *) zmalloc(sizeof(FCALL_REC)) ;
            p->callee = callee ;
            p->call_scope = call_scope ;
            p->call = call ;
            p->arg_list = arg_list ;
            p->arg_cnt_checked = 1 ;
            p->line_no = line_no ;
            /* add to resolve list */
            p->link = resolve_list ; resolve_list = p ;
      }
  }
}



/*  example where typing cannot progress

{ f(z) }

function f(x) { print NR }

# this is legal, does something useful, but absurdly written
# We have to design so this works
*/

@//E*O*F mawk0.97/fcall.c//
chmod u=rw,g=r,o=r mawk0.97/fcall.c
 
echo x - mawk0.97/field.c
sed 's/^@//' > "mawk0.97/field.c" <<'@//E*O*F mawk0.97/field.c//'

/********************************************
field.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/* $Log:	field.c,v $
 * Revision 2.2  91/04/09  12:39:00  brennan
 * added static to funct decls to satisfy STARDENT compiler
 * 
 * Revision 2.1  91/04/08  08:23:01  brennan
 * VERSION 0.97
 * 
*/


/* field.c */

#include "mawk.h"
#include "field.h"
#include "init.h"
#include "memory.h"
#include "scan.h"
#include "bi_vars.h"
#include "repl.h"
#include "regexp.h"

CELL  field[NUM_FIELDS] ;

/* statics */
static void PROTO( build_field0, (void) ) ;
static void PROTO( xcast, (CELL *) ) ;
static void PROTO( set_rs_shadow, (void) ) ;

/* the order is important here, must be the same as
   postion after field[MAX_FIELD] */

static char *biv_field_names[] = {
"NF" , "RS" , "FS", "OFMT" } ;

/* a description of how to split based on RS.
   If RS is changed, so is rs_shadow */
SEPARATOR rs_shadow = {SEP_CHAR, '\n'} ;
/* a splitting CELL version of FS */
CELL fs_shadow = {C_SPACE} ;
int   nf ;  
  /* nf holds the true value of NF.  If nf < 0 , then
     NF has not been computed, i.e., $0 has not been split
  */

static void set_rs_shadow()
{ CELL c ;
  STRING  *sval ;
  char *s ;
  unsigned len ;

  if ( rs_shadow.type == SEP_STR )  free_STRING((STRING*) rs_shadow.ptr) ;

  cast_for_split( cellcpy(&c, field+RS) ) ;
  switch( c.type )
  {
    case C_RE :
        if ( s = is_string_split(c.ptr, &len) )
            if ( len == 1 )
            { rs_shadow.type = SEP_CHAR ;
              rs_shadow.c = s[0] ;
            }
            else
            { rs_shadow.type = SEP_STR ;
              rs_shadow.ptr = (PTR) new_STRING(s) ;
            }
        else
        { rs_shadow.type = SEP_RE ;
          rs_shadow.ptr = c.ptr ;
        }
        break ;

    case C_SPACE :
        rs_shadow.type = SEP_CHAR ;
        rs_shadow.c = ' ' ;
        break ;

    case C_SNULL : /* RS becomes one or more blank lines */
        rs_shadow.type = SEP_RE ;
        sval = new_STRING( "\n([ \t]*\n)+" ) ;
        rs_shadow.ptr = re_compile(sval) ;
        free_STRING(sval) ;
        break ;

    default : bozo("bad cell in set_rs_shadow") ;
  }
}

void  field_init()
{ char **p = biv_field_names ;
  SYMTAB *st_p ;
  int i ;

  for(i = NF  ; i < NUM_FIELDS ; p++ , i++ )
  {
    st_p = insert( *p ) ;
    st_p->type = ST_FIELD ;
    st_p->stval.cp = &field[i] ;
  }

  field[NF].type = C_DOUBLE ;
  field[NF].dval = 0.0 ;
  field[0].type = C_STRING ;
  field[0].ptr = (PTR) & null_str ;
  null_str.ref_cnt++ ;

  field[RS].type = C_STRING ;
  field[RS].ptr =  (PTR) new_STRING( "\n" ) ;
  /* rs_shadow already set */

  field[FS].type = C_STRING ;
  field[FS].ptr = (PTR) new_STRING( " " ) ;
  /* fs_shadow is already set */

  field[OFMT].type = C_STRING ;
  field[OFMT].ptr = (PTR) new_STRING( "%.6g" ) ;
}



void  set_field0( s, len)
  char *s ;
  unsigned len ;
{ 
  cell_destroy( & field[0] ) ;
  nf = -1 ;

  if ( len )
  {
    field[0].type = C_MBSTRN ;
    field[0].ptr = (PTR) new_STRING( (char *) 0, len) ;
    (void) memcpy( string(&field[0])->str, s, len ) ;
  }
  else
  {
    field[0].type = C_STRING ;
    field[0].ptr = (PTR) &null_str ;
    null_str.ref_cnt++ ;
  }
}



/* split field[0] into $1, $2 ... and set NF  */

void  split_field0()
{ register int i ;
  CELL  c ;
  int  cast_flag ; /* we had to cast field[0] */
  char *s ;
  unsigned len ;

  if ( fs_shadow.type == C_SNULL ) /* FS == "" */
  { cell_destroy(field+1) ;
    (void) cellcpy(field+1, field+0) ;
    cell_destroy(field+NF) ;
    field[NF].type = C_DOUBLE ; field[NF].dval = 1.0 ;
    return ;
  }

  if ( field[0].type < C_STRING )
  { cast1_to_s(cellcpy(&c, field+0)) ;
    s = string(&c)->str ;
    len = string(&c)->len ;
    cast_flag = 1 ;
  }
  else  
  { s = string(field)->str ; 
    len = string(field)->len ;
    cast_flag = 0 ; 
  }

  nf = len == 0 ? 0 :
       fs_shadow.type == C_SPACE
       ? space_split(s) : re_split(s, fs_shadow.ptr) ;

  cell_destroy(field+NF) ;
  field[NF].type = C_DOUBLE ;
  field[NF].dval = (double) nf ;

  for( i = 1 ; i <= nf ; i++ )
  {
    cell_destroy(field+i) ;
    field[i].ptr = temp_buff.ptr_buff[i-1] ;
    field[i].type = C_MBSTRN ;
  }

  if ( cast_flag )  free_STRING( string(&c) ) ;
}

/*
  assign CELL *cp to field[i]
  and take care of all side effects
*/

void  field_assign( i, cp)
  register int i ;
  CELL *cp ;
{ register int j ;
  CELL c ;

  /* update fields not up to date */
  if ( nf < 0 )  split_field0() ;

  switch( i )
  {
    case 0 :
        cell_destroy(field) ;
        nf = -1 ;
        (void) cellcpy(field, cp) ;
        break ; ;

    case  NF :
        cell_destroy(field+NF) ;
        (void) cellcpy(field+NF, cellcpy(&c,cp) ) ;
        if ( c.type != C_DOUBLE )  cast1_to_d(&c) ;

        if ( (j = (int) c.dval) < 0 )
            rt_error("negative value assigned to NF") ;
        if ( j > MAX_FIELD )
            rt_overflow("MAX_FIELD", MAX_FIELD) ;

        if ( j > nf )
            for ( i = nf+1 ; i <= j ; i++ )
            { cell_destroy(field+i) ;
              field[i].type = C_STRING ;
              field[i].ptr = (PTR) &null_str ;
              null_str.ref_cnt++ ;
            }

        nf = j ;
        build_field0() ;
        break ;

    case  RS :
        cell_destroy(field+RS) ;
        (void) cellcpy(field+RS, cp) ;
        set_rs_shadow() ;
        break ;

    case  FS :
        cell_destroy(field+FS) ;
        cast_for_split( cellcpy(&fs_shadow, cellcpy(field+FS, cp)) ) ;
        break ;

    case OFMT : 
        /* If the user does something stupid with OFMT, we could crash.
           We'll make an attempt to protect ourselves here.  This is
           why OFMT is made a field.

           The ptr of OFMT always has a valid STRING, even if assigned
           a DOUBLE or NOINIT
        */

        free_STRING( string(field+OFMT) ) ;
        (void) cellcpy(field+OFMT, cp) ;
        if ( field[OFMT].type < C_STRING ) /* !! */
             field[OFMT].ptr = (PTR) new_STRING( "%.6g" ) ;
        else
        {
          /* It's a string, but if it's really goofy it could still
             damage us. Test it . */
          temp_buff.string_buff[256] = 0 ;
          (void) sprintf(temp_buff.string_buff, 
             string(field+OFMT)->str, 3.1459) ;
          if ( temp_buff.string_buff[256] )
                rt_error("OFMT assigned unusable value") ;
        }
        break ;
           

    default:

#ifdef DEBUG 
        if ( i < 0 )
            bozo("negative field index in field_assign") ;
        if ( i > MAX_FIELD )
            bozo("large field index in field_assign") ;
#endif

        cell_destroy(field+i) ;
        (void) cellcpy(field+i, cp) ;
        if ( i > nf )
        { for ( j = nf+1 ; j < i ; j++ )
          { cell_destroy(field+j) ;
            field[j].type = C_STRING ;
            field[j].ptr = (PTR) &null_str ;
            null_str.ref_cnt++ ;
          }
          nf = i ;
          cell_destroy(field+NF) ;
          field[NF].type = C_DOUBLE ;
          field[NF].dval = (double) i ;
        }

        build_field0() ;

  }
}


/* get the string rep of a double without changing its
   type */

static void  xcast( cp )
  register CELL *cp ;
{
  switch ( cp->type )
  {
    case C_NOINIT :
        cp->ptr = (PTR) &null_str ;
        null_str.ref_cnt++ ;
        break ;

    case C_DOUBLE :
        (void) sprintf(temp_buff.string_buff, 
          string(field+OFMT)->str, cp->dval) ;
        cp->ptr = (PTR) new_STRING(temp_buff.string_buff) ;
        break ;
  }
}


/* construct field[0] from the other fields */

static void  build_field0()
{ 


#ifdef DEBUG
  if ( nf < 0 )  
      bozo("nf <0 in build_field0") ;
#endif

  cell_destroy( field+0 ) ;

  if ( nf == 0 )
  { field[0].type = C_STRING ;
    field[0].ptr = (PTR) &null_str ;
    null_str.ref_cnt++ ;
  }
  else
  if ( nf == 1 )  (void) cellcpy(field, field+1) ;

  else
  { CELL  ofs ;
    char *ofs_str ;
    unsigned ofs_len , len0 = 0 ;
    register int i ;
    register char *p, *q ;


    (void) cellcpy(& ofs , bi_vars + OFS) ;
    if ( ofs.type < C_STRING )  cast1_to_s(&ofs) ;
    ofs_str = string(&ofs)->str ;
    ofs_len = string(&ofs)->len ;


    for( i = 1 ; i <= nf ; i++ )
    {
      if ( field[i].type < C_STRING )  xcast(field+i) ;
      len0 += string(field+i)->len + ofs_len ;
    }
    len0 -= ofs_len ;

    field[0].type = C_STRING ;
    field[0].ptr = (PTR) new_STRING((char *) 0, len0) ;

    p = string(field)->str ;
    for( i = 1 ; i < nf ; i++ )
    {
      (void) memcpy(p, string(field+i)->str, string(field+i)->len) ;
      p += string(field+i)->len ;

      /* add the separator */
      q = ofs_str ;
      while ( *p++ = *q++ ) ;  p-- ;

      /* if not really string undo the xcast */
      if ( field[i].type < C_STRING )
              free_STRING( string(field+i) ) ;
    }
    /* do the last piece */
    (void) memcpy(p, string(field+i)->str, string(field+i)->len) ;
    if ( field[i].type < C_STRING )
              free_STRING( string(field+i) ) ;
    
    free_STRING( string(&ofs) ) ;
  } 
}
@//E*O*F mawk0.97/field.c//
chmod u=rw,g=r,o=r mawk0.97/field.c
 
echo x - mawk0.97/field.h
sed 's/^@//' > "mawk0.97/field.h" <<'@//E*O*F mawk0.97/field.h//'

/********************************************
field.h
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/* $Log:	field.h,v $
 * Revision 2.1  91/04/08  08:23:03  brennan
 * VERSION 0.97
 * 
*/

/* field.h */


#ifndef  FIELD_H
#define  FIELD_H   1

void  PROTO( set_field0, (char *, unsigned) ) ;
void  PROTO( split_field0, (void) ) ;
int   PROTO( is_strnum, (char *, unsigned, double *) ) ;
void  PROTO( field_assign, (int, CELL *) ) ;
char *PROTO( is_string_split, (PTR , unsigned *) ) ;

#define   NF            (MAX_FIELD+1)
#define   RS            (MAX_FIELD+2)
#define   FS            (MAX_FIELD+3)
#define   OFMT          (MAX_FIELD+4)
#define   NUM_FIELDS    (MAX_FIELD+5)


extern  CELL  field[NUM_FIELDS] ;

extern  int  nf ; /* shadows NF */

/* a shadow type for RS and FS */
#define  SEP_SPACE      0
#define  SEP_CHAR       1
#define  SEP_STR        2
#define  SEP_RE         3

typedef  struct {
char  type ;
char  c ;
PTR ptr ; /* STRING* or RE machine* */
} SEPARATOR ;

extern   SEPARATOR  rs_shadow  ;
extern   CELL  fs_shadow ;






#endif   /* FIELD_H  */
@//E*O*F mawk0.97/field.h//
chmod u=rw,g=r,o=r mawk0.97/field.h
 
echo x - mawk0.97/files.c
sed 's/^@//' > "mawk0.97/files.c" <<'@//E*O*F mawk0.97/files.c//'

/********************************************
files.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/*$Log:	files.c,v $
 * Revision 2.1  91/04/08  08:23:05  brennan
 * VERSION 0.97
 * 
*/

/* files.c */

#include "mawk.h"
#include "files.h"
#include "memory.h"
#include <stdio.h>
#include "fin.h"

#if  ! DOS
#include <fcntl.h>
#endif

/* We store dynamically created files on a linked linear
   list with move to the front (big surprise)  */

typedef struct file {
struct file *link ;
STRING  *name ;
short type ;
#if   ! DOS
int pid ;  /* we need to wait() when we close an out pipe */
#endif
PTR   ptr ;  /* FIN*   or  FILE*   */
}  FILE_NODE ;

static FILE_NODE *file_list ;

PTR  file_find( sval, type )
  STRING *sval ;
  int type ;
{ register FILE_NODE *p = file_list ;
  FILE_NODE *q = (FILE_NODE *) 0 ;
  char *name = sval->str ;

  while (1)
  {
    if ( !p )   /* open a new one */
    {
      p = (FILE_NODE*) zmalloc(sizeof(FILE_NODE)) ;
      switch( p->type = type )
      {
        case  F_TRUNC :
            if ( !(p->ptr = (PTR) fopen(name, "w")) )
                goto out_failure ;
            break ;

        case  F_APPEND :
            if ( !(p->ptr = (PTR) fopen(name, "a")) )
                goto out_failure ;
            break ;

        case  F_IN  :
            if ( !(p->ptr = (PTR) FINopen(name, 0)) )
            { zfree(p, sizeof(FILE_NODE)) ; return (PTR) 0 ; }
            break ;

        case  PIPE_OUT :
        case  PIPE_IN :
#if   DOS
            rt_error("pipes not supported under MsDOS") ;
#else
            if ( !(p->ptr = get_pipe(name, type, &p->pid)) )
                if ( type == PIPE_OUT ) goto out_failure ;
                else
                { zfree(p, sizeof(FILE_NODE) ) ;
                  return (PTR) 0 ;
                }
#endif
            break ;

#ifdef  DEBUG
        default :
            bozo("bad file type") ;
#endif
      }
      /* successful open */
      p->name = sval ;
      sval->ref_cnt++ ;
      break ;
    }

    if ( strcmp(name, p->name->str) == 0 )
    { 
      if ( p->type != type )  goto type_failure ;
      if ( !q )  /*at front of list */
          return  p->ptr ;
      /* delete from list for move to front */
      q->link = p->link ;
      break ;
    }
    q = p ; p = p->link ;
  }

  /* put p at the front of the list */
  p->link = file_list ;
  return  (PTR) (file_list = p)->ptr ;

out_failure:
  errmsg(errno, "cannot open \"%s\" for output", name) ;
  mawk_exit(1) ;

type_failure :
  rt_error("use of file \"%s\"\n\tis inconsistent with previous use",
           name) ;
}


/* close a file and delete it's node from the file_list */

int  file_close( sval )
  STRING *sval ;
{ register FILE_NODE *p = file_list ;
  FILE_NODE *q = (FILE_NODE *) 0 ; /* trails p */
  char *name = sval->str ;

  while ( p )
        if ( strcmp(name,p->name->str) == 0 ) /* found */
        { 
          switch( p->type )
          {
            case  F_TRUNC :
            case  F_APPEND :    
                (void) fclose((FILE *) p->ptr) ;
                break ;

            case  PIPE_OUT :
                (void) fclose((FILE *) p->ptr) ;
#if  ! DOS
                (void) wait_for(p->pid) ;
#endif
                break ;

            case F_IN  :
            case PIPE_IN :
                FINclose((FIN *) p->ptr) ;
                break ;
          }

          free_STRING(p->name) ;
          if ( q )  q->link = p->link ;
          else  file_list = p->link ;

          zfree(p, sizeof(FILE_NODE)) ;
          return 0 ;
        }
        else { q = p ; p = p->link ; }

  /* its not on the list */
  return -1 ;
}

/* When we exit, we need to close and wait for all output pipes */

#if   !DOS
void close_out_pipes()
{ register FILE_NODE *p = file_list ;

  while ( p )
  { if ( p->type == PIPE_OUT )
    { (void) fclose((FILE *) p->ptr) ;  (void) wait_for(p->pid) ; }
    p = p->link ;
  }
}
#endif


char *shell ;

#if  !  DOS
PTR get_pipe( name, type, pid_ptr)
  char *name ;
  int type ;
  int *pid_ptr ;
{ int the_pipe[2], local_fd, remote_fd ;

  if ( ! shell ) shell = (shell = getenv("SHELL")) ? shell :"/bin/sh" ;
  
  if ( pipe(the_pipe) == -1 )  return (PTR) 0 ;
  local_fd = the_pipe[type == PIPE_OUT] ;
  remote_fd = the_pipe[type == PIPE_IN ] ;

  switch( *pid_ptr = fork() )
  { case -1 :  
      (void) close(local_fd) ;
      (void) close(remote_fd) ;
      return (PTR) 0 ;

    case  0 :
        (void) close(local_fd) ;
        (void) close(type == PIPE_IN) ;
        (void) dup( remote_fd ) ;
        (void) close( remote_fd ) ;
        (void) execl(shell, shell, "-c", name, (char *) 0 ) ;
        errmsg(errno, "failed to exec %s -c %s" , shell, name) ;
	fflush(stderr) ;
        _exit(128) ;

    default :
        (void) close(remote_fd) ;
        /* we could deadlock if future child inherit the local fd ,
           set close on exec flag */
        (void) fcntl(local_fd, F_SETFD, 1) ;
        break ;
  }

  return  type == PIPE_IN ? (PTR) FINdopen(local_fd, 0) : 
                            (PTR)  fdopen(local_fd, "w")  ;
}
  


/*------------ children ------------------*/

/* we need to wait for children at the end of output pipes to
   complete so we know any files they have created are complete */

/* dead children are kept on this list */

static struct child {
int pid ;
int exit_status ;
struct child *link ;
}  *child_list ;

static  void  add_to_child_list(pid, exit_status)
  int pid, exit_status ;
{ register struct child *p = 
          (struct child *) zmalloc(sizeof(struct child)) ;

  p->pid = pid ; p->exit_status = exit_status ;
  p->link = child_list ; child_list = p ;
}

static struct child *remove_from_child_list(pid)
  int pid ;
{ register struct child *p = child_list ;
  struct child *q = (struct child *) 0 ;

  while ( p )
    if ( p->pid == pid )
    {
        if ( q ) q->link = p->link ;
        else child_list = p->link ;
        break ;
    }
    else { q = p ; p = p->link ; }

  return p ;  /* null return if not in the list */
}
    

/* wait for a specific child to complete and return its 
   exit status */

int wait_for(pid)
  int pid ;
{ int exit_status ;
  struct child *p ;
  int id ;

  /* see if an earlier wait() caught our child */
  if ( p = remove_from_child_list(pid) ) 
  { exit_status = p->exit_status ;
    zfree(p, sizeof(struct child)) ;
  }
  else /* need to really wait */
    while ( (id = wait(&exit_status)) != pid )
        if ( id == -1 ) /* can't happen */  bozo("wait_for") ;
        else
        { /* we got the exit status of another child
             put it on the child list and try again */
          add_to_child_list(id, exit_status ) ;
        }

  return exit_status ;
}
        
#endif
@//E*O*F mawk0.97/files.c//
chmod u=rw,g=r,o=r mawk0.97/files.c
 
echo x - mawk0.97/files.h
sed 's/^@//' > "mawk0.97/files.h" <<'@//E*O*F mawk0.97/files.h//'

/********************************************
files.h
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/*$Log:	files.h,v $
 * Revision 2.1  91/04/08  08:23:07  brennan
 * VERSION 0.97
 * 
*/

#ifndef   FILES_H
#define   FILES_H

/* IO redirection types */
#define  F_IN           (-5)
#define  PIPE_IN        (-4)
#define  PIPE_OUT       (-3)
#define  F_APPEND       (-2)
#define  F_TRUNC        (-1)

extern char *shell ; /* for pipes and system() */

PTR  PROTO(file_find, (STRING *, int)) ;
int  PROTO(file_close, (STRING *)) ;
PTR  PROTO(get_pipe, (char *, int) ) ;
int  PROTO(wait_for, (int) ) ;
void  PROTO( close_out_pipes, (void) ) ;


#endif
@//E*O*F mawk0.97/files.h//
chmod u=rw,g=r,o=r mawk0.97/files.h
 
echo x - mawk0.97/fin.c
sed 's/^@//' > "mawk0.97/fin.c" <<'@//E*O*F mawk0.97/fin.c//'

/********************************************
fin.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/*$Log:	fin.c,v $
 * Revision 2.1  91/04/08  08:23:09  brennan
 * VERSION 0.97
 * 
*/

/* fin.c */

#include "mawk.h"
#include "fin.h"
#include "memory.h"
#include "bi_vars.h"
#include "field.h"
#include "symtype.h"
#include "scan.h"
#include <fcntl.h>

extern int errno ;
int PROTO(isatty, (int) ) ;
FILE *PROTO(fdopen, (int, char*) ) ;

/* statics */
int  PROTO( next_main, (void) ) ;
int  PROTO( is_cmdline_assign, (char *) ) ;

FIN  *FINdopen( fd, main_flag )
  int fd , main_flag ;
{ register FIN *fin = (FIN *) zmalloc( sizeof(FIN) ) ;

  fin->fd = fd ;

  if ( main_flag )
  { fin->flags = MAIN_FLAG ;
    fin->buffp = fin->buff = main_buff ;
  }
  else
  { 
    fin->flags = 0 ;
    fin->buffp = fin->buff = (char *) zmalloc(BUFFSZ+1) ;
  }
  *fin->buffp = 0 ;

  if ( isatty(fd) && rs_shadow.type == SEP_CHAR 
       && rs_shadow.c == '\n' )
  {
    /* interactive, i.e., line buffer this file */
    if ( fd == 0 ) fin->fp = stdin ;
    else
    if ( !(fin->fp = fdopen(fd, "r")) )
    { errmsg(errno, "fdopen failed") ; exit(1) ; }
  }
  else  fin->fp = (FILE *) 0 ;

  return fin ;
}

FIN  *FINopen( filename, main_flag )
  char *filename ;
  int main_flag ;
{ int fd ;

  if ( (fd = open( filename , O_RDONLY, 0 )) == -1 )
  { errmsg( errno, "cannot open %s" , filename ) ;
    return (FIN *) 0 ; }

  else  return  FINdopen( fd, main_flag ) ;
}

void  FINclose( fin )
  register FIN *fin ;
{
  if ( ! (fin->flags & MAIN_FLAG) )  
        zfree(fin->buff, BUFFSZ+1) ;

  if ( fin->fd )  
        if ( fin->fp )  (void) fclose(fin->fp) ;
        else  (void) close(fin->fd) ;

  zfree( fin , sizeof(FIN) ) ;
}

/* return one input record as determined by RS,
   from input file (FIN)  fin
*/

char *FINgets( fin, len_p )
  FIN *fin ;
  unsigned *len_p ;
{ register char *p, *q ;
  unsigned match_len ;
  unsigned r ;

restart :

  if ( ! (p = fin->buffp)[0] )  /* need a refill */
  { 
    if ( fin->flags & EOF_FLAG )
        if ( (fin->flags & MAIN_FLAG) && next_main() )  goto restart ;
        else
        { *len_p = 0 ; return (char *) 0 ; }
        
    if ( fin->fp ) /* line buffering */
      if ( ! fgets(fin->buff, BUFFSZ+1, fin->fp) )
      {
        fin->flags |= EOF_FLAG ;
        fin->buff[0] = 0 ;
        fin->buffp = fin->buff ;
        goto restart ;  /* might be main_fin */
      }
      else  /* return this line */
      {
        if ( !(p = strchr(fin->buff, '\n')) )
             p = fin->buff + BUFFSZ + 1 ; /* unlikely to occur */

        *p = 0 ; *len_p = p - fin->buff ;
        fin->buffp = p ;
        return  fin->buff ;
      }
    else  /* block buffering */
    {
      if ( (r = fillbuff(fin->fd, fin->buff, BUFFSZ)) == 0 )
      {
        fin->flags |= EOF_FLAG ;
        fin->buffp = fin->buff ;
        goto restart ; /* might be main */
      }
      else
      if ( r < BUFFSZ )  fin->flags |= EOF_FLAG ;

      p = fin->buffp = fin->buff ;
    }
  }

retry: 

  switch( rs_shadow.type )
  {
    case SEP_CHAR :
            q = strchr(p, rs_shadow.c) ;
            match_len = 1 ;
            break ;

    case SEP_STR  :
            q = str_str(p, ((STRING *) rs_shadow.ptr)->str,
                match_len = ((STRING *) rs_shadow.ptr)->len ) ;
            break ;

    case SEP_RE :
            q = re_pos_match(p, rs_shadow.ptr, &match_len) ;
            /* if the match is at the end, there might be more
               still to be read */
            if ( q && q[match_len] == 0 &&
                 p != fin->buff )  q = (char *) 0 ;
            break ;
            
    default :
            bozo("type of rs_shadow") ;
  }
  if ( q )
  {  /* the easy and normal case */
     *q = 0 ;  *len_p = q - p ;
     fin->buffp = q + match_len  ;
     return p ;
  }

  if ( p == fin->buff )  /* last line or one huge (truncated) line */
  { *len_p = r = strlen(p) ;  fin->buffp = p + r ;
    /* treat truncated case as overflow */
    if ( r == BUFFSZ ) 
    { /* overflow, update NR and FNR */
      cast2_to_d(bi_vars+NR) ;
      bi_vars[NR].dval += 1.0 ;
      bi_vars[FNR].dval += 1.0 ;
      rt_overflow("maximum record length" , BUFFSZ) ;
    }
     return p ;
  }

  /* move a partial line to front of buffer and try again */
  p = (char *) memcpy( fin->buff, p, r = strlen(p) ) ;
  q = p+r ; 
  if ( fin->flags & EOF_FLAG ) *q = 0 ;
  else 
  { unsigned rr = BUFFSZ - r ;

    if ( (r = fillbuff(fin->fd, q, rr)) < rr ) fin->flags |= EOF_FLAG ;
  }
  goto retry ;
}

/*--------
  target is big enough to hold size + 1 chars 
  on exit the back of the target is zero terminated
 *--------------*/
unsigned  fillbuff(fd, target, size)
  int fd ;
  register char *target ;
  unsigned size ;
{ register int r ;
  unsigned entry_size = size ;

  while ( size )
    switch( r = read(fd, target, size) )
    { case -1 :
        errmsg(errno, "read error on file") ;
        exit(1) ;

      case 0 :
        goto out ;

      default :
        target += r ; size -= r ;
        break ;
    }

out :
  *target = 0 ;
  return  entry_size - size ;
}

/* main_fin is a handle to the main input stream
   == -1 if never tried to open 
   == 0  if end of stream
      otherwise active    */
FIN *main_fin = (FIN *) -1 ;
ARRAY   Argv ;   /* to the user this is ARGV  */
static int argi = 1 ;  /* index of next ARGV[argi] to try to open */

int  open_main()  /* boolean return, true if main is open */
{ 
  if ( bi_vars[ARGC].type == C_DOUBLE && bi_vars[ARGC].dval == 1.0 )
  { cell_destroy( bi_vars + FILENAME ) ;
    bi_vars[FILENAME].type = C_STRING ;
    bi_vars[FILENAME].ptr = (PTR) new_STRING( "-") ;
    main_fin = FINdopen(0, 1) ;
    return  1 ;
  }
  else    return next_main() ;
}

static  int  next_main()
{ char xbuff[16] ;
  register CELL *cp ;
  STRING *sval ;
  CELL   argc ;  /* temp copy of ARGC */
  CELL   tc ;    /* copy of ARGV[argi] */
  double d_argi ;

#ifdef  DEBUG
  if ( ! main_fin ) bozo("call to next_main with dead main") ;
#endif

  tc.type = C_NOINIT ;

  if ( main_fin != (FIN *)-1 )  FINclose(main_fin) ;
  cell_destroy( bi_vars + FILENAME ) ;
  cell_destroy( bi_vars + FNR ) ;
  bi_vars[FNR].type = C_DOUBLE ;
  bi_vars[FNR].dval = 0.0 ;

  if ( cellcpy(&argc, &bi_vars[ARGC])->type != C_DOUBLE )
          cast1_to_d(&argc) ;
  xbuff[1] = 0 ;
  d_argi = (double) argi ;

  while ( d_argi < argc.dval )
  {
    if ( argi < 10 )  xbuff[0] = argi + '0' ;
    else  (void) sprintf(xbuff, "%u", argi) ;

    argi++ ; d_argi += 1.0 ;
    sval = new_STRING(xbuff) ;

    /* the user might have changed ARGC or deleted 
       ARGV[argi] -- test for existence without side effects */
    
    if ( ! array_test(Argv, sval) )
    { free_STRING(sval) ; continue ; }

    cp = array_find( Argv, sval, 0) ;
    free_STRING(sval) ;

    /* make a copy so we can cast w/o side effect */
    cell_destroy(&tc) ;
    cp = cellcpy(&tc, cp) ;
    if ( cp->type < C_STRING )  cast1_to_s(cp) ;
    if ( string(cp)->len == 0 )  continue ;

    if ( string(cp)->len == 1 && string(cp)->str[0] == '-' )
    { /* input from stdin */
      main_fin = FINdopen(0,1) ;
    }
    else  /* it might be a command line assignment */
    if ( is_cmdline_assign(string(cp)->str) )  continue ;

    else /* try to open it */
    if ( ! (main_fin = FINopen( string(cp)->str, 1 )) ) continue ;

    /* success */
    (void) cellcpy( &bi_vars[FILENAME] , cp ) ;
    free_STRING( string(cp) ) ;
    return 1 ;
  }
  /* failure */
  bi_vars[FILENAME].type = C_STRING ;
  bi_vars[FILENAME].ptr = (PTR) new_STRING( "" ) ;
  main_fin = (FIN *) 0 ;
  cell_destroy(&tc) ;
  return 0 ;
}
    

static int is_cmdline_assign(s)
  char *s ;
{ char *q;
  unsigned char *p ;
  int c ;
  SYMTAB *stp ;
  CELL *cp ;

  if ( scan_code[*(unsigned char *)s] != SC_IDCHAR 
       || !(q = strchr(s,'=')) ) return 0 ;

  p = (unsigned char *)s+1 ;
  while ( (c = scan_code[*p]) == SC_IDCHAR || c == SC_DIGIT ) p++ ;

  if ( (char *)p < q )  return 0 ;

  *q = 0 ;
  stp = find(s) ;

  switch( stp->type )
  {
    case  ST_NONE :
        stp->type = ST_VAR ;
        stp->stval.cp = cp = new_CELL() ;
        break ;

    case  ST_VAR :
        cp = stp->stval.cp ;
        break ;

    default :
        rt_error(
          "cannot command line assign to %s\n\t- type clash or keyword" 
          , s ) ;
  }
  
  *q++ = '=' ;
  cp->ptr = (PTR) new_STRING(q) ;
  check_strnum(cp) ;
  return 1 ;
}
@//E*O*F mawk0.97/fin.c//
chmod u=rw,g=r,o=r mawk0.97/fin.c
 
echo x - mawk0.97/fin.h
sed 's/^@//' > "mawk0.97/fin.h" <<'@//E*O*F mawk0.97/fin.h//'

/********************************************
fin.h
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/*$Log:	fin.h,v $
 * Revision 2.1  91/04/08  08:23:11  brennan
 * VERSION 0.97
 * 
*/

/* fin.h */

#ifndef  FIN_H
#define  FIN_H
/* structure to control input files */

typedef struct {
int  fd ;
FILE *fp ;   /* NULL unless interactive */
char *buff ;
char *buffp ;
short flags ;
}  FIN ;

#define  MAIN_FLAG    1   /* part of main input stream if on */
#define  EOF_FLAG     2

FIN *  PROTO (FINdopen, (int, int) );
FIN *  PROTO (FINopen, (char *, int) );
void   PROTO (FINclose, (FIN *) ) ;
char*  PROTO (FINgets, (FIN *, unsigned *) ) ;
unsigned PROTO ( fillbuff, (int, char *, unsigned) ) ;


extern  FIN  *main_fin ;  /* for the main input stream */
int   PROTO( open_main, (void) ) ;
#endif  /* FIN_H */
@//E*O*F mawk0.97/fin.h//
chmod u=rw,g=r,o=r mawk0.97/fin.h
 
echo x - mawk0.97/hash.c
sed 's/^@//' > "mawk0.97/hash.c" <<'@//E*O*F mawk0.97/hash.c//'

/********************************************
hash.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/


/* $Log:	hash.c,v $
 * Revision 2.1  91/04/08  08:23:13  brennan
 * VERSION 0.97
 * 
*/


/* hash.c */

#include "mawk.h"
#include "memory.h"
#include "symtype.h"
#include <string.h>


unsigned hash(s)
  register char *s ;
{ register unsigned h = 0 ;

  while ( *s )  h += h + *s++ ;
  return  h  ;
}

typedef struct hash {
struct hash *link ;
SYMTAB  symtab ;
}  HASHNODE ;

static  HASHNODE *PROTO( delete, (char *) ) ;

#define  new_HASHNODE() (HASHNODE *) zmalloc(sizeof(HASHNODE))

static HASHNODE *hash_table[HASH_PRIME] ;

/*
 *   insert -- s is not there and need not be duplicated
 *   -- used during initialization
 */

SYMTAB *insert(s) 
  char *s ;
{ register HASHNODE *p = new_HASHNODE();
  register unsigned h ;
  
  p->link = hash_table[h = hash(s) % HASH_PRIME ] ;
  p->symtab.name = s ;
  hash_table[h] = p ;
  return &p->symtab ;
}

/*
 *  find --  s might be there, find it else insert and dup
 *  s 
 */

SYMTAB *find(s)
  char *s ;
{ register HASHNODE *p ;
  HASHNODE *q ;
  unsigned h ;

  p = hash_table[h = hash(s) % HASH_PRIME ] ;
  q = (HASHNODE *) 0 ;
  while ( 1 )
  { if ( !p )
    { p = new_HASHNODE() ;
      p->symtab.type = ST_NONE ;
      p->symtab.name = strcpy(zmalloc( strlen(s)+1 ), s) ;
      break ;
    }

    if ( strcmp(p->symtab.name, s) == 0 ) /* found */
      if ( !q )  /* already at the front */
        return  &p->symtab ;
      else /* delete from the list */
      { q->link = p->link ;  break ; }

    q = p ; p = p->link ;
  }
  /* put p on front of the list */
  p->link = hash_table[h] ;
  hash_table[h] = p ;
  return & p->symtab ;
}


/* remove a node from the hash table
   return a ptr to the node */

static unsigned last_hash ;

static  HASHNODE  *delete( s )
  char *s ;
{ register HASHNODE *p ;
  HASHNODE *q = (HASHNODE *) 0 ;
  unsigned h ;

  p = hash_table[ last_hash = h = hash(s) % HASH_PRIME ] ;
  while ( p )
      if ( strcmp(p->symtab.name, s) == 0 )  /* found */
      {
        if ( q )  q->link = p->link ;
        else  hash_table[h] = p->link ;
        return p ;
      }
      else { q = p ; p = p->link ; }

#ifdef  DEBUG   /* we should not ever get here */
  bozo("delete") ;
#endif
  return (HASHNODE *) 0 ;
}

/* when processing user functions,  global ids which are
   replaced by local ids are saved on this list */

static HASHNODE  *save_list ;

/* store a global id on the save list,
   return a ptr to the local symtab  */
SYMTAB *save_id( s )
  char *s ;
{ HASHNODE *p, *q ;
  unsigned h ;

  p = delete(s) ;
  q = new_HASHNODE() ;
  q->symtab.type = ST_LOCAL_NONE ;
  q->symtab.name = p->symtab.name ;
  /* put q in the hash table */
  q->link = hash_table[ h = last_hash ] ;
  hash_table[h] = q ;

  /* save p */
  p->link = save_list ; save_list = p ;

  return & q->symtab ;
}

/* restore all global indentifiers */
void  restore_ids()
{ register HASHNODE *p, *q ;
  register unsigned h ;

  q = save_list ; save_list = (HASHNODE *) 0 ;
  while ( q )
  {
    p = q ; q = q->link ;
    zfree( delete(p->symtab.name) , sizeof(HASHNODE) ) ;
    p->link = hash_table[h = last_hash ] ; 
    hash_table[h] = p ;
  }
}
@//E*O*F mawk0.97/hash.c//
chmod u=rw,g=r,o=r mawk0.97/hash.c
 
echo x - mawk0.97/init.c
sed 's/^@//' > "mawk0.97/init.c" <<'@//E*O*F mawk0.97/init.c//'

/********************************************
init.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/


/* $Log:	init.c,v $
 * Revision 2.2  91/04/09  12:39:08  brennan
 * added static to funct decls to satisfy STARDENT compiler
 * 
 * Revision 2.1  91/04/08  08:23:15  brennan
 * VERSION 0.97
 * 
*/


/* init.c */
#include "mawk.h"
#include "code.h"
#include "init.h"
#include "memory.h"
#include "symtype.h"
#include "bi_vars.h"
#include "field.h"

#define PROGRAM_FROM_CMDLINE   1

/* static protos */
static void PROTO( no_program, (void) ) ;
static void PROTO( process_cmdline , (int, char **) ) ;
static void PROTO( set_FS, (char *) ) ;
static void PROTO( set_dump, (char *) ) ;

#if  DOS  &&  ! HAVE_REARGV
#include <fcntl.h>
static  void  PROTO(emit_prompt, (void) ) ;
#endif

union tbuff  temp_buff ;
char *main_buff = temp_buff.string_buff + TEMP_BUFF_SZ ;

void initialize(argc, argv)
  int argc ; char **argv ;
{
  bi_vars_init() ; /* load the builtin variables */
  bi_funct_init() ; /* load the builtin functions */
  kw_init() ; /* load the keywords */
  field_init() ; 
  process_cmdline(argc, argv)  ;   

  jmp_stacks_init() ;
  code_init() ;
  fpe_init() ;

#if  NO_STRTOD
  strtod_init() ;
#endif

}

void  compile_cleanup()
/* program has parsed OK, free some memory
   we don't need anymore */
{
   scan_cleanup() ;
   jmp_stacks_cleanup() ;
   code_cleanup() ;
}


static void no_program()
{ errmsg( 0, "no program") ; mawk_exit(1) ; }

int  dump_code ;  /* if on dump internal code */
#ifdef   DEBUG
int  dump_RE   ;  /* if on dump compiled REs  */
#endif

static void set_FS(s)
  char *s ;
{
  cell_destroy(field+FS) ;
  field[FS].type = C_STRING ;
  field[FS].ptr = (PTR) new_STRING(s) ;
  cast_for_split( cellcpy(&fs_shadow, field+FS) ) ;
}


#ifdef  DEBUG
static void set_dump(s)
  char *s ;
{
  while ( 1 )
  { switch ( *s )
    { case 'p' :
      case 'P' :  yydebug = 1 ; break ;

      case  'c' :
      case  'C' :  dump_code = 1 ; break ;

      case  'r' :
      case  'R' :  dump_RE = 1 ; break ;

      case   0  :  
             if ( s[-1] == 'D' ) dump_code = 1 ;
             return ;

      default :  break ;
    }
    s++ ;
  }
}
#else
static void  set_dump(s)
  char *s ;
{ dump_code = 1 ; }
#endif

static void process_cmdline(argc, argv)
  int argc ; char **argv ;
{ extern int program_fd ; 
  int i ;  /* index to walk command line */
  char *p ;
  CELL *cp ;
  SYMTAB *st_p ;
  char xbuff[20] ;


  for( i = 1 ; i < argc && argv[i][0] == '-' ; i++ )
  { p = & argv[i][1] ;
    if ( *p == 'F' )  set_FS(p+1) ;

    else if ( *p == 'D' )  set_dump(p+1) ;
    else if ( *p == 'f' )
    { if ( i == argc - 1 )  no_program() ;
      scan_init(! PROGRAM_FROM_CMDLINE, argv[i+1] ) ;
      i += 2 ;
      goto  set_ARGV ;
    }
  }

#if  DOS   &&  ! HAVE_REARGV
/* allows short programs to be typed in without mucking stdin */
  emit_prompt() ;
  scan_init(! PROGRAM_FROM_CMDLINE, "CON") ;
#else   /* the real world */

  if ( i == argc )  no_program() ;
  scan_init(PROGRAM_FROM_CMDLINE, argv[i]) ;
  i++ ;
#endif

set_ARGV:

  /* now set up ARGC and ARGV  */
  st_p = insert( "ARGV" ) ;
  st_p->type = ST_ARRAY ;
  Argv = st_p->stval.array = new_ARRAY() ;
  xbuff[0] = '0' ; xbuff[1] = 0 ;
  cp = array_find( st_p->stval.array, xbuff, 1) ;
  cp->type = C_STRING ;
  cp->ptr = (PTR) new_STRING( progname ) ;

  /* ARGV[0] is set, do the rest 
     The type of ARGV[1] ... should be C_MBSTRN
     because the user might enter numbers from the command line */
  { int arg_count = 1 ;

    for( ; i < argc ; i++, arg_count++ )
    { 
      if ( arg_count < 10 )  xbuff[0] = arg_count + '0' ;
      else (void) sprintf(xbuff, "%u" , arg_count ) ;
      cp = array_find( st_p->stval.array, xbuff, 1) ;
      cp->type = C_MBSTRN ;
      cp->ptr = (PTR) new_STRING( argv[i] ) ;
    }
    bi_vars[ARGC].type = C_DOUBLE ;
    bi_vars[ARGC].dval = (double) arg_count ;
  }
}

#if  DOS  &&  ! HAVE_REARGV

static void  emit_prompt()
{  static char prompt[] = DOS_PROMPT ;
   int fd = open("CON", O_WRONLY, 0) ;

   (void) write(fd, prompt, strlen(prompt)) ;
   (void) close(fd) ;
}
#endif

@//E*O*F mawk0.97/init.c//
chmod u=rw,g=r,o=r mawk0.97/init.c
 
echo x - mawk0.97/init.h
sed 's/^@//' > "mawk0.97/init.h" <<'@//E*O*F mawk0.97/init.h//'

/********************************************
init.h
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/* $Log:	init.h,v $
 * Revision 2.1  91/04/08  08:23:17  brennan
 * VERSION 0.97
 * 
*/

/* init.h  */


#ifndef  INIT_H
#define  INIT_H


void  PROTO( initialize, (int, char **) ) ;
void  PROTO( code_init, (void) ) ;
void  PROTO( code_cleanup, (void) ) ;
void  PROTO( compile_cleanup, (void) ) ;
void PROTO(scan_init, (int, char *) ) ;
void PROTO(scan_cleanup, (void) ) ;
void PROTO(bi_vars_init, (void) ) ;
void PROTO(bi_funct_init, (void) ) ;
void PROTO(print_init, (void) ) ;
void PROTO(kw_init, (void) ) ;
void PROTO(jmp_stacks_init, (void) ) ;
void PROTO(jmp_stacks_cleanup, (void) ) ;
void  PROTO( field_init, (void) ) ;
void  PROTO( fpe_init, (void) ) ;

#endif   /* INIT_H  */
@//E*O*F mawk0.97/init.h//
chmod u=rw,g=r,o=r mawk0.97/init.h
 
echo x - mawk0.97/jmp.c
sed 's/^@//' > "mawk0.97/jmp.c" <<'@//E*O*F mawk0.97/jmp.c//'

/********************************************
jmp.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/* $Log:	jmp.c,v $
 * Revision 2.1  91/04/08  08:23:19  brennan
 * VERSION 0.97
 * 
*/

/* this module deals with back patching jumps, breaks and continues,
   and with save and restoring code when we move code.
   There are three stacks.  If we encounter a compile error, the
   stacks are frozen, i.e., we do not attempt error recovery
   on the stacks
*/


#include "mawk.h"
#include "jmp.h"
#include "code.h"
#include "sizes.h"
#include "init.h"
#include "memory.h"

extern unsigned compile_error_count ;
#define error_state  (compile_error_count>0)


/* a stack to hold jumps that need to be patched */

#define JMP_STK_SZ  (2*MAX_LOOP_DEPTH)

static INST **jmp_stack ; 
static INST **jmp_sp  ;

/*-------------------------------------*/
/* a stack to hold break or continue that need to be
   patched (which is all of them) */

#define  BC_SZ    MAX_LOOP_DEPTH

/* the stack holds a linked list of these */

struct BC_node { /* struct for the break/continue list */
char type ;   /*  'B' or 'C' */
INST *jmp ;   /*  the jump to patch */
struct BC_node *link ;
} ;

static   struct BC_node  **BC_stack ;
static   struct BC_node  **BC_sp ;

/*---------------------------------------*/
/* a stack to hold some pieces of code while 
   reorganizing loops */

#define  LOOP_CODE_SZ    (2*MAX_LOOP_DEPTH)

static struct loop_code {
INST *code ;
unsigned short len ;
}  *loop_code_stack , *lc_sp ;

/*--------------------------------------*/
void jmp_stacks_init()
{ jmp_stack = (INST **)  zmalloc(JMP_STK_SZ*sizeof(INST*)) ;
  jmp_sp = jmp_stack-1 ;

  BC_stack = (struct BC_node **) 
              zmalloc(BC_SZ*sizeof(struct BC_node*)) ;
  BC_sp =  BC_stack-1 ;

  loop_code_stack = (struct loop_code *)
                    zmalloc(LOOP_CODE_SZ*sizeof(struct loop_code)) ;
  lc_sp = loop_code_stack - 1 ;
}

void jmp_stacks_cleanup()
{ zfree(jmp_stack, JMP_STK_SZ*sizeof(INST*)) ;
  zfree(BC_stack, BC_SZ*sizeof(struct BC_node*)) ;
  zfree(loop_code_stack, LOOP_CODE_SZ*sizeof(struct loop_code)) ;
}
/*--------------------------------------*/
/* operations on the jmp_stack */

void code_jmp( jtype, target)
  int jtype ; INST *target ;
{ 
  if (error_state)  return ;

  /* check if a constant expression will be at top of stack,
     if so replace conditional jump with jump */

  if ( code_ptr[-2].op == _PUSHC && jtype != _JMP )
  { int t = test( (CELL *) code_ptr[-1].ptr ) ;
    if ( jtype == _JZ && ! t ||
         jtype == _JNZ && t )
    { code_ptr -= 2 ; jtype = _JMP ; }
  }
   
  if ( ! target ) /* jump will have to be patched later ,
                     put it on the jmp_stack */
  { if ( ++jmp_sp == jmp_stack + JMP_STK_SZ )
          overflow("jmp stack" , JMP_STK_SZ ) ; 
    *jmp_sp = code_ptr ;
    code2(jtype, 0) ;
  }
  else
  { INST *source = code_ptr ;
  
    code_ptr++->op = jtype ;
    code_ptr++->op = target - source ; 
  }
}

void patch_jmp(target)  /* patch a jump on the jmp_stack */
  INST *target ;
{ register INST *source ;

  if ( ! error_state )
  {
    if ( jmp_sp <= jmp_stack-1 ) bozo("jmp stack underflow") ;
    source = *jmp_sp-- ;
    source[1].op = target - source ;
  }
}


/*---------------------------*/

/* a stack of linked lists of BC_nodes for patching 
   break and continue statements.  */


void BC_new()  /* push an empty list on the stack */
{ 
  if ( ! error_state )
  { if ( ++BC_sp == BC_stack + BC_SZ ) overflow("BC stack", BC_SZ) ;
    * BC_sp = (struct BC_node *) 0 ;
  }
}

void BC_insert(type, address)
  int type ; INST *address ;
{ register struct BC_node *p ; 

  if ( error_state )  return ;
  if ( BC_sp <= BC_stack - 1 )
  {  compile_error(  type == 'B' ?
        "break statement outside of loop" :
        "continue statement outside of loop" ) ; 
     return ;
  }
  
  p = (struct BC_node *) zmalloc( sizeof(struct BC_node) ) ;
  p->type = type ; p->jmp = address ;
  p->link = *BC_sp ; *BC_sp = p ;
}

void BC_clear(B_address, C_address)  
/* patch all break and continues on list */
INST *B_address, *C_address ;
{ register struct BC_node *p , *q ;

  if (error_state) return ;
  if ( BC_sp <= BC_stack-1) bozo("underflow on BC stack") ;
  p = *BC_sp-- ;
  while ( p )
  { p->jmp[1].op = (p->type=='B' ? B_address : C_address) - p->jmp ;
    q = p ; p = p->link ; zfree(q, sizeof(struct BC_node)) ;
  }
}

/*---------------------------------------------*/
/*  save and restore some code for reorganizing
    loops on a stack */


void code_push( p, len)
  INST *p ; unsigned len ;
{ 
  if (error_state) return ;
  if ( ++lc_sp == loop_code_stack + LOOP_CODE_SZ )
        overflow("loop_code_stack" , LOOP_CODE_SZ) ;

  if ( len )
  { lc_sp->code = (INST *) zmalloc(sizeof(INST) * len) ;
    (void) memcpy(lc_sp->code, p, sizeof(INST) * len) ; }
  else  lc_sp->code = (INST *) 0 ;
  lc_sp->len = (unsigned short) len ;
}

/* copy the code at the top of the loop code stack to target.
   return the number of bytes moved */

unsigned code_pop(target) 
  INST *target ;
{ 
  if (error_state)  return 0 ;
  if ( lc_sp <= loop_code_stack-1 )  bozo("loop code stack underflow") ;
  if ( lc_sp->len )
  { (void) memcpy(target, lc_sp->code, lc_sp->len * sizeof(INST)) ;
    zfree(lc_sp->code, sizeof(INST)*lc_sp->len) ; }
  return lc_sp-- -> len ;
}
@//E*O*F mawk0.97/jmp.c//
chmod u=rw,g=r,o=r mawk0.97/jmp.c
 
echo x - mawk0.97/jmp.h
sed 's/^@//' > "mawk0.97/jmp.h" <<'@//E*O*F mawk0.97/jmp.h//'

/********************************************
jmp.h
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/* $Log:	jmp.h,v $
 * Revision 2.1  91/04/08  08:23:21  brennan
 * VERSION 0.97
 * 
*/

#ifndef   JMP_H
#define   JMP_H

void  PROTO(BC_new, (void) ) ;
void  PROTO(BC_insert, (int, INST*) ) ;
void  PROTO(BC_clear, (INST *, INST *) ) ;
void  PROTO(code_push, (INST *, unsigned) ) ;
unsigned  PROTO(code_pop, (INST *) ) ;
void  PROTO(code_jmp, (int, INST *) ) ;
void  PROTO(patch_jmp, (INST *) ) ;


#endif  /* JMP_H  */

@//E*O*F mawk0.97/jmp.h//
chmod u=rw,g=r,o=r mawk0.97/jmp.h
 
echo x - mawk0.97/kw.c
sed 's/^@//' > "mawk0.97/kw.c" <<'@//E*O*F mawk0.97/kw.c//'

/********************************************
kw.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/


/* $Log:	kw.c,v $
 * Revision 2.1  91/04/08  08:23:23  brennan
 * VERSION 0.97
 * 
*/


/* kw.c */


#include "mawk.h"
#include "symtype.h"
#include "parse.h"
#include "init.h"


static struct kw {
char *text ;
short kw ;
}  keywords[] = {

"print", PRINT,
"printf", PRINTF,
"do" , DO ,
"while" , WHILE ,
"for" , FOR ,
"break" , BREAK ,
"continue" , CONTINUE ,
"if" , IF ,
"else", ELSE ,
"in" , IN ,
"delete", DELETE ,
"split" , SPLIT ,
"match" , MATCH_FUNC ,
"BEGIN" , BEGIN,
"END" ,   END ,
"exit" , EXIT ,
"next" , NEXT ,
"return", RETURN,
"getline", GETLINE,
"sub" , SUB,
"gsub", GSUB,
"function", FUNCTION,
(char *) 0 , 0 } ;

/* put keywords in the symbol table */
void kw_init()
{ register struct kw *p = keywords ;
  register SYMTAB *q ;

  while ( p->text )
  { q = insert( p->text ) ;
    q->type = ST_KEYWORD ;
    q->stval.kw = p++ -> kw ;
  }
}

/* find a keyword to emit an error message */
char *find_kw_str( kw_token )
  int kw_token ;
{ struct kw *p = keywords ;

  for( p = keywords ; p->text ; p++ )
        if ( p->kw == kw_token )  return p->text ;
  /* search failed */
  return (char *) 0 ;
}
@//E*O*F mawk0.97/kw.c//
chmod u=rw,g=r,o=r mawk0.97/kw.c
 
echo x - mawk0.97/machine.h
sed 's/^@//' > "mawk0.97/machine.h" <<'@//E*O*F mawk0.97/machine.h//'

/********************************************
machine.h
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/

/*$Log:	machine.h,v $
 * Revision 2.2  91/04/09  12:39:14  brennan
 * added static to funct decls to satisfy STARDENT compiler
 * 
 * Revision 2.1  91/04/08  08:23:25  brennan
 * VERSION 0.97
 * 
*/


/* I've attempted to isolate machine/system dependencies here.

   Floating point exceptions are the biggest hassle.
   If you have IEEE754 floating point, turn off floating point
   traps and let the INFs and NANs go berserk.  This should be
   the default (see page 14 of IEEE754), but ANSI C seems to imply
   they should be on (i.e., one standards committee does not talk to
   the other).  Anyway, define a macro TURNOFF_FPE_TRAPS() which will
   probably be a 1 liner.

   If you cannot turn off floating exceptions, check out
   fpe_catch() in matherr.c and modify as needed for your machine.
   Also you may need to define FPE_ZERODIVIDE and FPE_OVERFLOW.

   If you have SysV like matherr(), use it.
   If you have SysV compatible math lib , use it.
   You might need to supply a macro to replace drand48(), otherwise.
   (See BSD43 for no IEEE754, no matherr(), no fmod(),
    no strtod(), no drand48())

   If you have to be conservative with memory (e.g., small model
   MsDos), a small evaluation stack (16-32) is plenty.
   Recursive functions calls are the only reason you need a big
   stack.  The default for MsDos uses 64 which allows some
   recursion without killing too many memory CELLs.
*/

/*  MsDOS --
    If you use command.com as the shell, entering programs on the
    command line is hopeless.  Command.com will always glom onto
    | or < or > as redirection.

    If you use a Unix style shell under DOS, then you need to
    write 

        void  reargv(int *argc, char ***argv)

    which gets the arguments from your shell, and then

    #define   HAVE_REARGV	1

    See README in dos directory
    and MsDos section of manual.
*/

#ifndef    MACHINE_H
#define    MACHINE_H


#ifdef  sun   /* sun3 or sun4 with SUNOS 4.0.3 */
#define  FPE_TRAPS              0
#define  TURNOFF_FPE_TRAPS()    /* empty, default is off */      
#define  HAVE_MATHERR           1
#endif

#ifdef    __TURBOC__
#define   DOS    		1
#define   SMALL_EVAL_STACK      1
#define  FPE_TRAPS              0
#define  TURNOFF_FPE_TRAPS()    _control87(0x3f,0x3f)
#define  HAVE_MATHERR           1
#endif

#ifdef   ULTRIX  /* V4.1 on a vax 3600 */
#define  HAVE_VOID_PTR          1
#define  HAVE_MATHERR           1
#define   FPE_ZERODIVIDE   FPE_FLTDIV_FAULT
#define   FPE_OVERFLOW     FPE_FLTOVF_FAULT
#endif

#ifdef    BSD43   /* on a vax */
#define   NO_STRTOD             1
#define   NO_FMOD               1
#define   srand48(x)    srandom(x)
#define   drand48() (((double)random())/((double)(unsigned)0x80000000))
#define   vfprintf(s,f,a)  _doprnt(f,a,s)
#define   FPE_ZERODIVIDE   FPE_FLTDIV_FAULT
#define   FPE_OVERFLOW     FPE_FLTOVF_FAULT
#endif

#ifdef   STARDENT  /* Stardent 3000, SysV R3.0 */
#define  HAVE_MATHERR	1
#define  FPE_TRAPS	0
#define  TURNOFF_FPE_TRAPS()    /* nothing */
#define  HAVE_VOID_PTR    1
#endif

/*  the defaults    */
#ifndef   HAVE_VOID_PTR
#define   HAVE_VOID_PTR         0  /* no void * */
#endif

#ifndef   FPE_TRAPS
#define   FPE_TRAPS             1  
    /* floating point errors generate exceptions  */
#endif

#ifndef   HAVE_MATHERR
#define   HAVE_MATHERR          0
    /*  SysV style matherr() is not available */
#endif

#ifndef  NO_STRTOD
#define  NO_STRTOD              0 /* default is have */
#endif

#ifndef  SMALL_EVAL_STACK
#define  SMALL_EVAL_STACK       0
#endif

#ifndef  NO_FMOD
#define  NO_FMOD                0 /* default is to have fmod() */
#endif

#define   STDC_MATHERR          (FPE_TRAPS && ! HAVE_MATHERR)

#ifndef   DOS
#define   DOS	0
#endif

#if   DOS 

#ifndef  HAVE_REARGV
#define  HAVE_REARGV	0
#endif

#define  DOS_PROMPT     "mawk> "
	/* change to "mawk \01 "  on a good day */
#endif





#endif   /* MACHINE_H  */
@//E*O*F mawk0.97/machine.h//
chmod u=rw,g=r,o=r mawk0.97/machine.h
 
echo x - mawk0.97/main.c
sed 's/^@//' > "mawk0.97/main.c" <<'@//E*O*F mawk0.97/main.c//'

/********************************************
main.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/


/* $Log:	main.c,v $
 * Revision 2.2  91/04/22  08:08:58  brennan
 * cannot close(3) or close(4) because of bug in TurboC++ 1.0
 * 
 * Revision 2.1  91/04/08  08:23:27  brennan
 * VERSION 0.97
 * 
*/



/*  main.c  */

#include "mawk.h"
#include "code.h"
#include "init.h"
#include "fin.h"
#include "bi_vars.h"
#include "field.h"
#include "files.h"
#include <stdio.h>

#if  DOS 
void  reargv(int *, char ***) ;
#endif


void  PROTO( process, (void) ) ;
void  PROTO( main_loop, (void) ) ;

extern int program_fd ;
char *progname ;

jmp_buf   exit_jump, next_jump ;
int  exit_code ;


main(argc , argv )
  int argc ; char **argv ;
{ 

#if   DOS
  progname = "mawk" ;
#if      HAVE_REARGV
  reargv(&argc, &argv) ;
#endif
#else
  { char *strrchr() ;
    char *p = strrchr(argv[0], '/') ;
    progname = p ? p+1 : argv[0] ; }
#endif

  initialize(argc, argv) ;

  if ( parse() || compile_error_count )  exit(1) ;

  compile_cleanup() ;
  process() ;

  mawk_exit( exit_code ) ;
  return 0 ;
}


static  void  process()
{

  if ( setjmp(exit_jump) )
  { if ( begin_start ) zfree(begin_start, begin_size) ;
    goto the_exit ;
  }

  if ( begin_start )
  { (void) execute(begin_start, eval_stack-1, 0) ;
    zfree( begin_start , begin_size ) ;
    begin_start = (INST *) 0 ;
  }

  if ( main_start || end_start )  main_loop() ;

the_exit:

  if ( setjmp(exit_jump) )  mawk_exit(exit_code) ;

  if ( main_start )  zfree(main_start, main_size) ;
  if ( end_start ) (void) execute(end_start, eval_stack-1, 0) ;
}


static void  main_loop()
{ register char *p ;
  unsigned len ;

  /* the main file stream might already be open by a call of
     getline in the BEGIN block */

  if ( main_fin == (FIN *) -1 && ! open_main()
       || ! main_fin )  return ;

  if ( main_start )
  {
     (void)  setjmp(next_jump) ;

     while ( p = FINgets( main_fin, &len ) )
     { 
       if ( TEST2(bi_vars + NR) != TWO_DOUBLES )
                    cast2_to_d(bi_vars + NR) ;

       bi_vars[NR].dval += 1.0 ;
       bi_vars[FNR].dval += 1.0 ;

       set_field0(p, len) ;
       (void) execute( main_start, eval_stack-1, 0) ;
     }
  }
  else  /* eat main to set NR and FNR before executing END */
  { long nr ;

    if ( TEST2(bi_vars+NR) != TWO_DOUBLES ) cast2_to_d(bi_vars+NR) ;
    nr = (long) bi_vars[NR].dval ;
    while ( FINgets( main_fin, &len ) )
    { nr++ ; bi_vars[FNR].dval += 1.0 ; }
    bi_vars[NR].dval = (double) nr ;
  }
}


void  mawk_exit(x)
  int x ;
{
#if  !  DOS
  close_out_pipes() ;  /* no effect, if no out pipes */
#endif
  exit(x) ;
}
@//E*O*F mawk0.97/main.c//
chmod u=rw,g=r,o=r mawk0.97/main.c
 
echo x - mawk0.97/makescan.c
sed 's/^@//' > "mawk0.97/makescan.c" <<'@//E*O*F mawk0.97/makescan.c//'

/********************************************
makescan.c
copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the Awk programming language as defined in
Aho, Kernighan and Weinberger, The AWK Programming Language,
Addison-Wesley, 1988.

See the accompaning file, LIMITATIONS, for restrictions
regarding modification and redistribution of this
program in source or binary form.
********************************************/



/*$Log:	makescan.c,v $
 * Revision 2.1  91/04/08  08:23:29  brennan
 * VERSION 0.97
 * 
*/

/* source for makescan.exe which builds the scancode[]
   via:   makescan.exe > scancode.c
*/

#define  MAKESCAN

#include  "scan.h"

char   scan_code[256] ;

void  scan_init()
{ 
  register char *p ;

  (void) memset(scan_code, SC_UNEXPECTED, sizeof(scan_code)) ;
  for( p = scan_code + '0' ; p <= scan_code + '9' ; p++ )
       *p = SC_DIGIT ;
  scan_code[0] = 0 ;
  scan_code[ ' ' ] = scan_code['\t'] = scan_code['\f'] = SC_SPACE ;
  scan_code[ '\r'] = scan_code['\013'] = SC_SPACE ;

  scan_code[';'] = SC_SEMI_COLON ;
  scan_code['\n'] = SC_NL ;
  scan_code['{'] = SC_LBRACE ;
  scan_code[ '}'] = SC_RBRACE ;
  scan_code['+'] = SC_PLUS ;
  scan_code['-'] = SC_MINUS ;
  scan_code['*'] = SC_MUL ;
  scan_code['/'] = SC_DIV ;
  scan_code['%'] = SC_MOD ;
  scan_code['^'] = SC_POW ;
  scan_code['('] = SC_LPAREN ;
  scan_code[')'] = SC_RPAREN ;
  scan_code['_'] = SC_IDCHAR ;
  scan_code['='] = SC_EQUAL ;
  scan_code['#'] = SC_COMMENT ;
  scan_code['\"'] = SC_DQUOTE ;
  scan_code[','] = SC_COMMA ;
  scan_code['!'] = SC_NOT ;
  scan_code['<'] = SC_LT ;
  scan_code['>'] = SC_GT ;
  scan_code['|'] = SC_OR ;