The illumos nawk(1) is slightly divergent from upstream:
- We allow an unlimited number of input program files
- We allow an unlimited number of "/pat/, /pat/" expressions
- Some of the code has been altered to track the length of strings
better so that we can avoid repeatedly calling strlen(3C)
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License"). You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#
# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# Copyright 2020 Joyent, Inc.
# NOTE: awk is now nawk.
PROG= nawk
LINKPROG= awk
OBJ1= b.o lib.o main.o parse.o proctab.o run.o tran.o lex.o
OBJ2= awk.g.o
OBJS= $(OBJ2) $(OBJ1)
SRCS= $(OBJ1:%.o=%.c)
include ../Makefile.cmd
include ../Makefile.essbin
# Hammerhead: awk hardlink in /bin
ROOTLINK = $(LINKPROG:%=$(ROOTESSBIN)/%)
CERRWARN += -Wno-parentheses
CERRWARN += -Wno-unused-variable
CERRWARN += $(CNOWARN_UNINIT)
# Hammerhead: Suppress pointer/int cast warnings in legacy awk code
CERRWARN += -Wno-pointer-to-int-cast
CERRWARN += -Wno-int-to-pointer-cast
# because of labels from yacc
awk.g.o : CERRWARN += -Wno-unused-label
# not linted
SMATCH=off
#
# Message catalog
#
POFILES= $(OBJS:%.o=%.po)
POFILE= awk.po
XGETFLAGS += -a -x awk.xcl
#
CPPFLAGS += -D_FILE_OFFSET_BITS=64
YFLAGS += -d
LDLIBS += -lm -lumem
CLEANFILES= maketab proctab.c awk.g.c y.tab.h
.KEEP_STATE:
all: $(PROG) $(ROOTLINK)
$(PROG): $(OBJS)
$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
$(POST_PROCESS)
$(ROOTLINK): $(ROOTPROG)
$(RM) $@; $(LN) $(ROOTPROG) $@
# Hammerhead: ROOTUSRBINLINKS removed — /bin is usr/bin (path flattening),
# so ../../bin symlinks from /usr/bin would be circular.
#
# message catalog
#
$(POFILE): y.tab.h $(POFILES)
$(RM) $@
cat $(POFILES) > $@
#
proctab.c: maketab
rm -f $@; ./maketab > $@
maketab: maketab.c
$(NATIVECC) $(NATIVE_CFLAGS) maketab.c -o $@ $(LDLIBS)
install: all $(ROOTPROG) $(ROOTLINK)
clean:
$(RM) $(OBJS) $(CLEANFILES)
# Hammerhead: GNU Make grouped target (was dmake +)
# yacc generates both .c and y.tab.h in one invocation
awk.g.c y.tab.h &: awk.g.y
$(YACC.y) awk.g.y
mv y.tab.c awk.g.c
awk.g.o: awk.g.c
proctab.o: proctab.c
$(COMPILE.c) proctab.c
$(POST_PROCESS_O)
include ../Makefile.targ
%{
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
%}
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
%{
#include "awk.h"
void checkdup(Node *list, Cell *item);
int yywrap(void) { return(1); }
Node *beginloc = NULL;
Node *endloc = NULL;
int infunc = 0; /* = 1 if in arglist or body of func */
int inloop = 0; /* = 1 if in while, for, do */
char *curfname = NULL; /* current function name */
Node *arglist = NULL; /* list of args for current function */
static void setfname(Cell *);
static int constnode(Node *);
static char *strnode(Node *);
static Node *notnull(Node *);
%}
%union {
Node *p;
Cell *cp;
int i;
char *s;
}
%token FIRSTTOKEN /* must be first */
%token
PROGRAM PASTAT PASTAT2 XBEGIN XEND
%token NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']'
%token ARRAY
%token MATCH NOTMATCH MATCHOP
%token FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS EMPTYRE
%token AND BOR APPEND EQ GE GT LE LT NE IN
%token ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC
%token SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE
%token ADD MINUS MULT DIVIDE MOD
%token ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ
%token PRINT PRINTF SPRINTF
%token
ELSE INTEST CONDEXPR
%token POSTINCR PREINCR POSTDECR PREDECR
%token VAR IVAR VARNF CALL NUMBER STRING
%token REGEXPR
%type pas pattern ppattern plist pplist patlist prarg term re
%type
pa_pat pa_stat pa_stats
%type reg_expr
%type
simple_stmt opt_simple_stmt stmt stmtlist
%type
var varname funcname varlist
%type
for if else while
%type do st
%type pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor
%type subop print
%right ASGNOP
%right '?'
%right ':'
%left BOR
%left AND
%left GETLINE
%nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|'
%left ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC
%left GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER
%left PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR
%left REGEXPR VAR VARNF IVAR WHILE '('
%left CAT
%left '+' '-'
%left '*' '/' '%'
%left NOT UMINUS UPLUS
%right POWER
%right DECR INCR
%left INDIRECT
%token LASTTOKEN /* must be last */
%%
program:
pas { if (errorflag==0)
winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); }
| error { yyclearin; bracecheck(); SYNTAX("bailing out"); }
;
and:
AND | and NL
;
bor:
BOR | bor NL
;
comma:
',' | comma NL
;
do:
DO | do NL
;
else:
ELSE | else NL
;
for:
FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
{ --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); }
| FOR '(' opt_simple_stmt ';' ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
{ --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); }
| FOR '(' varname IN varname rparen {inloop++;} stmt
{ --inloop; $$ = stat3(IN, $3, makearr($5), $8); }
;
funcname:
VAR { setfname($1); }
| CALL { setfname($1); }
;
if:
IF '(' pattern rparen { $$ = notnull($3); }
;
lbrace:
'{' | lbrace NL
;
nl:
NL | nl NL
;
opt_nl:
/* empty */ { $$ = 0; }
| nl
;
opt_pst:
/* empty */ { $$ = 0; }
| pst
;
opt_simple_stmt:
/* empty */ { $$ = 0; }
| simple_stmt
;
pas:
opt_pst { $$ = 0; }
| opt_pst pa_stats opt_pst { $$ = $2; }
;
pa_pat:
pattern { $$ = notnull($1); }
;
pa_stat:
pa_pat { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); }
| pa_pat lbrace stmtlist '}' { $$ = stat2(PASTAT, $1, $3); }
| pa_pat ',' opt_nl pa_pat { $$ = pa2stat($1, $4, stat2(PRINT, rectonode(), NIL)); }
| pa_pat ',' opt_nl pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $4, $6); }
| lbrace stmtlist '}' { $$ = stat2(PASTAT, NIL, $2); }
| XBEGIN lbrace stmtlist '}'
{ beginloc = linkum(beginloc, $3); $$ = 0; }
| XEND lbrace stmtlist '}'
{ endloc = linkum(endloc, $3); $$ = 0; }
| FUNC funcname '(' varlist rparen {infunc++;} lbrace stmtlist '}'
{ infunc--; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
;
pa_stats:
pa_stat
| pa_stats opt_pst pa_stat { $$ = linkum($1, $3); }
;
patlist:
pattern
| patlist comma pattern { $$ = linkum($1, $3); }
;
ppattern:
var ASGNOP ppattern { $$ = op2($2, $1, $3); }
| ppattern '?' ppattern ':' ppattern %prec '?'
{ $$ = op3(CONDEXPR, notnull($1), $3, $5); }
| ppattern bor ppattern %prec BOR
{ $$ = op2(BOR, notnull($1), notnull($3)); }
| ppattern and ppattern %prec AND
{ $$ = op2(AND, notnull($1), notnull($3)); }
| ppattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
| ppattern MATCHOP ppattern
{ if (constnode($3))
$$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
else
$$ = op3($2, (Node *)1, $1, $3); }
| ppattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
| '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
| ppattern term %prec CAT { $$ = op2(CAT, $1, $2); }
| re
| term
;
pattern:
var ASGNOP pattern { $$ = op2($2, $1, $3); }
| pattern '?' pattern ':' pattern %prec '?'
{ $$ = op3(CONDEXPR, notnull($1), $3, $5); }
| pattern bor pattern %prec BOR
{ $$ = op2(BOR, notnull($1), notnull($3)); }
| pattern and pattern %prec AND
{ $$ = op2(AND, notnull($1), notnull($3)); }
| pattern EQ pattern { $$ = op2($2, $1, $3); }
| pattern GE pattern { $$ = op2($2, $1, $3); }
| pattern GT pattern { $$ = op2($2, $1, $3); }
| pattern LE pattern { $$ = op2($2, $1, $3); }
| pattern LT pattern { $$ = op2($2, $1, $3); }
| pattern NE pattern { $$ = op2($2, $1, $3); }
| pattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
| pattern MATCHOP pattern
{ if (constnode($3))
$$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
else
$$ = op3($2, (Node *)1, $1, $3); }
| pattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
| '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
| pattern '|' GETLINE var {
if (safe) SYNTAX("cmd | getline is unsafe");
else $$ = op3(GETLINE, $4, itonp($2), $1); }
| pattern '|' GETLINE {
if (safe) SYNTAX("cmd | getline is unsafe");
else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); }
| pattern term %prec CAT { $$ = op2(CAT, $1, $2); }
| re
| term
;
plist:
pattern comma pattern { $$ = linkum($1, $3); }
| plist comma pattern { $$ = linkum($1, $3); }
;
pplist:
ppattern
| pplist comma ppattern { $$ = linkum($1, $3); }
;
prarg:
/* empty */ { $$ = rectonode(); }
| pplist
| '(' plist ')' { $$ = $2; }
;
print:
PRINT | PRINTF
;
pst:
NL | ';' | pst NL | pst ';'
;
rbrace:
'}' | rbrace NL
;
re:
reg_expr
{ $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); }
| NOT re { $$ = op1(NOT, notnull($2)); }
;
reg_expr:
'/' {startreg();} REGEXPR '/' { $$ = $3; }
;
rparen:
')' | rparen NL
;
simple_stmt:
print prarg '|' term {
if (safe) SYNTAX("print | is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg APPEND term {
if (safe) SYNTAX("print >> is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg GT term {
if (safe) SYNTAX("print > is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg { $$ = stat3($1, $2, NIL, NIL); }
| DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); }
| DELETE varname { $$ = stat2(DELETE, makearr($2), 0); }
| pattern { $$ = exptostat($1); }
| error { yyclearin; SYNTAX("illegal statement"); }
;
st:
nl
| ';' opt_nl
;
stmt:
BREAK st { if (!inloop) SYNTAX("break illegal outside of loops");
$$ = stat1(BREAK, NIL); }
| CONTINUE st { if (!inloop) SYNTAX("continue illegal outside of loops");
$$ = stat1(CONTINUE, NIL); }
| do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st
{ $$ = stat2(DO, $3, notnull($7)); }
| EXIT pattern st { $$ = stat1(EXIT, $2); }
| EXIT st { $$ = stat1(EXIT, NIL); }
| for
| if stmt else stmt { $$ = stat3(IF, $1, $2, $4); }
| if stmt { $$ = stat3(IF, $1, $2, NIL); }
| lbrace stmtlist rbrace { $$ = $2; }
| NEXT st { if (infunc)
SYNTAX("next is illegal inside a function");
$$ = stat1(NEXT, NIL); }
| NEXTFILE st { if (infunc)
SYNTAX("nextfile is illegal inside a function");
$$ = stat1(NEXTFILE, NIL); }
| RETURN pattern st { $$ = stat1(RETURN, $2); }
| RETURN st { $$ = stat1(RETURN, NIL); }
| simple_stmt st
| while {inloop++;} stmt { --inloop; $$ = stat2(WHILE, $1, $3); }
| ';' opt_nl { $$ = 0; }
;
stmtlist:
stmt
| stmtlist stmt { $$ = linkum($1, $2); }
;
subop:
SUB | GSUB
;
term:
term '/' ASGNOP term { $$ = op2(DIVEQ, $1, $4); }
| term '+' term { $$ = op2(ADD, $1, $3); }
| term '-' term { $$ = op2(MINUS, $1, $3); }
| term '*' term { $$ = op2(MULT, $1, $3); }
| term '/' term { $$ = op2(DIVIDE, $1, $3); }
| term '%' term { $$ = op2(MOD, $1, $3); }
| term POWER term { $$ = op2(POWER, $1, $3); }
| '-' term %prec UMINUS { $$ = op1(UMINUS, $2); }
| '+' term %prec UMINUS { $$ = op1(UPLUS, $2); }
| NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); }
| BLTIN '(' ')' { $$ = op2(BLTIN, itonp($1), rectonode()); }
| BLTIN '(' patlist ')' { $$ = op2(BLTIN, itonp($1), $3); }
| BLTIN { $$ = op2(BLTIN, itonp($1), rectonode()); }
| CALL '(' ')' { $$ = op2(CALL, celltonode($1,CVAR), NIL); }
| CALL '(' patlist ')' { $$ = op2(CALL, celltonode($1,CVAR), $3); }
| CLOSE term { $$ = op1(CLOSE, $2); }
| DECR var { $$ = op1(PREDECR, $2); }
| INCR var { $$ = op1(PREINCR, $2); }
| var DECR { $$ = op1(POSTDECR, $1); }
| var INCR { $$ = op1(POSTINCR, $1); }
| GETLINE var LT term { $$ = op3(GETLINE, $2, itonp($3), $4); }
| GETLINE LT term { $$ = op3(GETLINE, NIL, itonp($2), $3); }
| GETLINE var { $$ = op3(GETLINE, $2, NIL, NIL); }
| GETLINE { $$ = op3(GETLINE, NIL, NIL, NIL); }
| INDEX '(' pattern comma pattern ')'
{ $$ = op2(INDEX, $3, $5); }
| INDEX '(' pattern comma reg_expr ')'
{ SYNTAX("index() doesn't permit regular expressions");
$$ = op2(INDEX, $3, (Node*)$5); }
| '(' pattern ')' { $$ = $2; }
| MATCHFCN '(' pattern comma reg_expr ')'
{ $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); }
| MATCHFCN '(' pattern comma pattern ')'
{ if (constnode($5))
$$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1));
else
$$ = op3(MATCHFCN, (Node *)1, $3, $5); }
| NUMBER { $$ = celltonode($1, CCON); }
| SPLIT '(' pattern comma varname comma pattern ')' /* string */
{ $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); }
| SPLIT '(' pattern comma varname comma reg_expr ')' /* const /regexp/ */
{ $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); }
| SPLIT '(' pattern comma varname ')'
{ $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); } /* default */
| SPRINTF '(' patlist ')' { $$ = op1($1, $3); }
| STRING { $$ = celltonode($1, CCON); }
| subop '(' reg_expr comma pattern ')'
{ $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); }
| subop '(' pattern comma pattern ')'
{ if (constnode($3))
$$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode());
else
$$ = op4($1, (Node *)1, $3, $5, rectonode()); }
| subop '(' reg_expr comma pattern comma var ')'
{ $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); }
| subop '(' pattern comma pattern comma var ')'
{ if (constnode($3))
$$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7);
else
$$ = op4($1, (Node *)1, $3, $5, $7); }
| SUBSTR '(' pattern comma pattern comma pattern ')'
{ $$ = op3(SUBSTR, $3, $5, $7); }
| SUBSTR '(' pattern comma pattern ')'
{ $$ = op3(SUBSTR, $3, $5, NIL); }
| var
;
var:
varname
| varname '[' patlist ']' { $$ = op2(ARRAY, makearr($1), $3); }
| IVAR { $$ = op1(INDIRECT, celltonode($1, CVAR)); }
| INDIRECT term { $$ = op1(INDIRECT, $2); }
;
varlist:
/* nothing */ { arglist = $$ = 0; }
| VAR { arglist = $$ = celltonode($1,CVAR); }
| varlist comma VAR {
checkdup($1, $3);
arglist = $$ = linkum($1,celltonode($3,CVAR)); }
;
varname:
VAR { $$ = celltonode($1, CVAR); }
| ARG { $$ = op1(ARG, itonp($1)); }
| VARNF { $$ = op1(VARNF, (Node *) $1); }
;
while:
WHILE '(' pattern rparen { $$ = notnull($3); }
;
%%
static void
setfname(Cell *p)
{
if (isarr(p))
SYNTAX("%s is an array, not a function", p->nval);
else if (isfcn(p))
SYNTAX("you can't define function %s more than once", p->nval);
curfname = p->nval;
p->tval |= FCN;
}
static int
constnode(Node *p)
{
return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
}
static char *
strnode(Node *p)
{
return ((Cell *)(p->narg[0]))->sval;
}
static Node *
notnull(Node *n)
{
switch (n->nobj) {
case LE: case LT: case EQ: case NE: case GT: case GE:
case BOR: case AND: case NOT:
return n;
default:
return op2(NE, n, nullnode);
}
}
void
checkdup(Node *vl, Cell *cp) /* check if name already in list */
{
char *s = cp->nval;
for (; vl; vl = vl->nnext) {
if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
SYNTAX("duplicate argument %s", s);
break;
}
}
}
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#ifndef AWK_H
#define AWK_H
#include
#include
#include
#include
#include
#include
#include
#include
typedef double Awkfloat;
/* unsigned char is more trouble than it's worth */
typedef unsigned char uschar;
#define xfree(a) { if ((a) != NULL) { free((void *)(a)); (a) = NULL; } }
/* guaranteed non-null for dprintf */
#define NN(p) ((p) ? (p) : "(null)")
#define DEBUG
#ifdef DEBUG
/* uses have to be doubly parenthesized */
#define dprintf(x) if (dbg) (void) printf x
#else
#define dprintf(x)
#endif
extern int compile_time; /* 1 if compiling, 0 if running */
extern int safe; /* 0 => unsafe, 1 => safe */
#define FLD_INCR 64
#define LINE_INCR 256
#define RECSIZE (8 * 1024) /* sets limit on records, fields, etc., etc. */
extern size_t recsize; /* size of current record, orig RECSIZE */
/* ensure that there is extra 1 byte in the buffer */
#define expand_buf(p, n, r) \
if (*(n) == 0 || (r) >= (*(n) - 1)) r_expand_buf(p, n, r)
extern char **FS;
extern char **RS;
extern char **ORS;
extern char **OFS;
extern char **OFMT;
extern Awkfloat *NR;
extern Awkfloat *FNR;
extern Awkfloat *NF;
extern char **FILENAME;
extern char **SUBSEP;
extern Awkfloat *RSTART;
extern Awkfloat *RLENGTH;
extern char *record; /* points to $0 */
extern size_t recsize;
extern int errorflag; /* 1 if error has occurred */
extern int donefld; /* 1 if record broken into fields */
extern int donerec; /* 1 if record is valid (no fld has changed */
extern char *patbeg; /* beginning of pattern matched */
extern int patlen; /* length of pattern matched. set in b.c */
/* Cell: all information about a variable or constant */
typedef struct Cell {
uschar ctype; /* OCELL, OBOOL, OJUMP, etc. */
uschar csub; /* CCON, CTEMP, CFLD, etc. */
char *nval; /* name, for variables only */
char *sval; /* string value */
Awkfloat fval; /* value as number */
int tval;
/* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE|CONVC|CONVO */
char *fmt;
/* CONVFMT/OFMT value used to convert from number */
struct Cell *cnext; /* ptr to next if chained */
} Cell;
typedef struct Array { /* symbol table array */
int nelem; /* elements in table right now */
int size; /* size of tab */
Cell **tab; /* hash table pointers */
} Array;
#define NSYMTAB 50 /* initial size of a symbol table */
extern Array *symtab, *makesymtab(int);
extern Cell *setsymtab(const char *, const char *, Awkfloat,
unsigned int, Array *);
extern Cell *lookup(const char *, Array *);
extern Cell *recloc; /* location of input record */
extern Cell *nrloc; /* NR */
extern Cell *fnrloc; /* FNR */
extern Cell *fsloc; /* FS */
extern Cell *nfloc; /* NF */
extern Cell *ofsloc; /* OFS */
extern Cell *orsloc; /* ORS */
extern Cell *rsloc; /* RS */
extern Cell *rtloc; /* RT */
extern Cell *rstartloc; /* RSTART */
extern Cell *rlengthloc; /* RLENGTH */
extern Cell *subseploc; /* SUBSEP */
extern Cell *symtabloc; /* SYMTAB */
/* Cell.tval values: */
#define NUM 01 /* number value is valid */
#define STR 02 /* string value is valid */
#define DONTFREE 04 /* string space is not freeable */
#define CON 010 /* this is a constant */
#define ARR 020 /* this is an array */
#define FCN 040 /* this is a function name */
#define FLD 0100 /* this is a field $1, $2, ... */
#define REC 0200 /* this is $0 */
#define CONVC 0400 /* string was converted from number via CONVFMT */
#define CONVO 01000 /* string was converted from number via OFMT */
extern Awkfloat setfval(Cell *, Awkfloat);
extern Awkfloat getfval(Cell *);
extern char *setsval(Cell *, const char *);
extern char *getsval(Cell *);
extern char *getpssval(Cell *); /* for print */
extern char *tostring(const char *);
extern char *tokname(int);
extern char *qstring(const char *, int);
/* function types */
#define FLENGTH 1
#define FSQRT 2
#define FEXP 3
#define FLOG 4
#define FINT 5
#define FSYSTEM 6
#define FRAND 7
#define FSRAND 8
#define FSIN 9
#define FCOS 10
#define FATAN 11
#define FTOUPPER 12
#define FTOLOWER 13
#define FFLUSH 14
/* Node: parse tree is made of nodes, with Cell's at bottom */
typedef struct Node {
int ntype;
struct Node *nnext;
off_t lineno;
int nobj;
struct Node *narg[1];
/* variable: actual size set by calling malloc */
} Node;
#define NIL ((Node *)0)
extern Node *winner;
extern Node *nullstat;
extern Node *nullnode;
/* ctypes */
#define OCELL 1
#define OBOOL 2
#define OJUMP 3
/* Cell subtypes: csub */
#define CFREE 7
#define CCOPY 6
#define CCON 5
#define CTEMP 4
#define CNAME 3
#define CVAR 2
#define CFLD 1
#define CUNK 0
/* bool subtypes */
#define BTRUE 11
#define BFALSE 12
/* jump subtypes */
#define JEXIT 21
#define JNEXT 22
#define JBREAK 23
#define JCONT 24
#define JRET 25
#define JNEXTFILE 26
/* node types */
#define NVALUE 1
#define NSTAT 2
#define NEXPR 3
#define NFIELD 4
extern Cell *(*proctab[])(Node **, int);
extern Cell *nullproc(Node **, int);
extern int *pairstack, paircnt;
extern Node *stat1(int, Node *), *stat2(int, Node *, Node *);
extern Node *stat3(int, Node *, Node *, Node *);
extern Node *stat4(int, Node *, Node *, Node *, Node *);
extern Node *pa2stat(Node *, Node *, Node *);
extern Node *op1(int, Node *), *op2(int, Node *, Node *);
extern Node *op3(int, Node *, Node *, Node *);
extern Node *op4(int, Node *, Node *, Node *, Node *);
extern Node *linkum(Node *, Node *), *celltonode(Cell *, int);
extern Node *rectonode(void), *exptostat(Node *);
extern Node *makearr(Node *);
#define notlegal(n) \
(n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc)
#define isvalue(n) ((n)->ntype == NVALUE)
#define isexpr(n) ((n)->ntype == NEXPR)
#define isjump(n) ((n)->ctype == OJUMP)
#define isexit(n) ((n)->csub == JEXIT)
#define isbreak(n) ((n)->csub == JBREAK)
#define iscont(n) ((n)->csub == JCONT)
#define isnext(n) ((n)->csub == JNEXT || (n)->csub == JNEXTFILE)
#define isret(n) ((n)->csub == JRET)
#define isrec(n) ((n)->tval & REC)
#define isfld(n) ((n)->tval & FLD)
#define isstr(n) ((n)->tval & STR)
#define isnum(n) ((n)->tval & NUM)
#define isarr(n) ((n)->tval & ARR)
#define isfcn(n) ((n)->tval & FCN)
#define istrue(n) ((n)->csub == BTRUE)
#define istemp(n) ((n)->csub == CTEMP)
#define freeable(p) (((p)->tval & (STR|DONTFREE)) == STR)
/* structures used by regular expression matching machinery, mostly b.c: */
/* 256 handles 8-bit chars; 128 does 7-bit */
/* watch out in match(), etc. */
#define NCHARS (256+3)
#define NSTATES 32
typedef struct rrow {
long ltype; /* long avoids pointer warnings on 64-bit */
union {
int i;
Node *np;
uschar *up;
} lval; /* because Al stores a pointer in it! */
int *lfollow;
} rrow;
typedef struct fa {
uschar gototab[NSTATES][NCHARS];
uschar out[NSTATES];
uschar *restr;
int *posns[NSTATES];
int anchor;
int use;
int initstat;
int curstat;
int accept;
int reset;
/* re is variable: actual size set by calling malloc */
struct rrow re[1];
} fa;
/* lex.c */
extern int yylex(void);
extern void startreg(void);
extern int input(void);
extern void unput(int);
extern void unputstr(const char *);
extern int yylook(void);
extern int yyback(int *, int);
extern int yyinput(void);
/* parse.c */
extern void defn(Cell *, Node *, Node *);
extern int ptoi(void *);
extern Node *itonp(int);
extern int isarg(const char *);
/* b.c */
extern fa *makedfa(const char *, int);
extern int nematch(fa *, const char *);
extern int match(fa *, const char *);
extern int pmatch(fa *, const char *);
/* lib.c */
extern void SYNTAX(const char *, ...);
extern void FATAL(const char *, ...) __attribute__((__noreturn__));
extern void WARNING(const char *, ...);
extern void error(void);
extern void nextfile(void);
extern void savefs(void);
extern int isclvar(const char *);
extern int is_number(const char *);
extern void setclvar(char *);
extern int readrec(char **, size_t *, FILE *);
extern void bracecheck(void);
extern void recinit(unsigned int n);
extern void syminit(void);
extern int yyerror(const char *);
extern void fldbld(void);
extern void recbld(void);
extern int getrec(char **, size_t *, int);
extern Cell *fieldadr(int);
extern void newfld(int);
extern int fldidx(Cell *);
extern double errcheck(double, const char *);
extern void fpecatch(int);
extern void r_expand_buf(char **, size_t *, size_t);
extern void makefields(int, int);
extern void growfldtab(int n);
extern void setlastfld(int n);
/* main.c */
extern int dbg;
extern char *lexprog;
extern int compile_time;
extern char *cursource(void);
extern int pgetc(void);
/* tran.c */
extern void syminit(void);
extern void arginit(int, char **);
extern void envinit(char **);
extern void freesymtab(Cell *);
extern void freeelem(Cell *, const char *);
extern void funnyvar(Cell *, const char *);
extern int hash(const char *, int);
extern Awkfloat *ARGC;
/* run.c */
extern void run(Node *);
extern const char *filename(FILE *);
extern int adjbuf(char **pb, size_t *sz, size_t min, size_t q,
char **pbp, const char *what);
extern int paircnt;
extern Node *winner;
#ifndef input
extern int input(void);
#endif
extern int yyparse(void);
extern FILE *yyin;
extern off_t lineno;
/* proc */
extern Cell *nullproc(Node **, int);
extern Cell *program(Node **, int);
extern Cell *boolop(Node **, int);
extern Cell *relop(Node **, int);
extern Cell *array(Node **, int);
extern Cell *indirect(Node **, int);
extern Cell *substr(Node **, int);
extern Cell *sub(Node **, int);
extern Cell *gsub(Node **, int);
extern Cell *sindex(Node **, int);
extern Cell *awksprintf(Node **, int);
extern Cell *arith(Node **, int);
extern Cell *incrdecr(Node **, int);
extern Cell *cat(Node **, int);
extern Cell *pastat(Node **, int);
extern Cell *dopa2(Node **, int);
extern Cell *matchop(Node **, int);
extern Cell *intest(Node **, int);
extern Cell *awkprintf(Node **, int);
extern Cell *printstat(Node **, int);
extern Cell *closefile(Node **, int);
extern Cell *awkdelete(Node **, int);
extern Cell *split(Node **, int);
extern Cell *assign(Node **, int);
extern Cell *condexpr(Node **, int);
extern Cell *ifstat(Node **, int);
extern Cell *whilestat(Node **, int);
extern Cell *forstat(Node **, int);
extern Cell *dostat(Node **, int);
extern Cell *instat(Node **, int);
extern Cell *jump(Node **, int);
extern Cell *bltin(Node **, int);
extern Cell *call(Node **, int);
extern Cell *arg(Node **, int);
extern Cell *getnf(Node **, int);
extern Cell *awkgetline(Node **, int);
#endif /* AWK_H */
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (the "License"). You may not use this file except in compliance
# with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
msgid "$zero&null"
msgid "next is illegal inside a function"
msgid "syntax error"
msgid "yacc stack overflow"
msgid ""
msgid "%o"
msgid "%x"
msgid "(NF)"
msgid "Input string too long, limit %d\n"
msgid "bad switch yylook %d"
msgid "lex %s [%s]\n"
msgid ""
msgid "cclenter: in = |%s|, out = |%s|\n"
msgid "reparse <%s>\n"
msgid ""
msgid "$0"
msgid "%d"
msgid "%s"
msgid "%s\n"
msgid "-"
msgid "RS=<%s>, FS=<%s>, ARGC=%d, FILENAME=%s\n"
msgid "\n"
msgid "argno=%d, file=|%s|\n"
msgid "command line set %s to |%s|\n"
msgid "field %d: |%s|\n"
msgid "getargv(%d) returns |%s|\n"
msgid "in recbld FS=%o, recloc=%o\n"
msgid "into refldbld, rec = <%s>, pat = <%s>\n"
msgid "match %s (%d chars)\n"
msgid "no match %s\n"
msgid "opening file %s\n"
msgid "r"
msgid "readrec saw <%s>, returns %d\n"
msgid "recbld = |%s|\n"
msgid "refldbld: i=%d\n"
msgid ""
msgid "--"
msgid "#"
msgid "# file: main.c.i, line number: 1016, textdomain("")"
msgid "\t"
msgid "argc=%d, argv[0]=%s\n"
msgid "awk %s\n"
msgid "errorflag=%d\n"
msgid "program = |%s|\n"
msgid "r"
msgid "version Oct 11, 1989"
msgid "defining func %s (%d args)\n"
msgid "ADD"
msgid "ADDEQ"
msgid "ALL"
msgid "AND"
msgid "APPEND"
msgid "ARG"
msgid "ARRAY"
msgid "ASGNOP"
msgid "ASSIGN"
msgid "BLTIN"
msgid "BOR"
msgid "BREAK"
msgid "CALL"
msgid "CAT"
msgid "CCL"
msgid "CHAR"
msgid "CLOSE"
msgid "CONDEXPR"
msgid "CONTINUE"
msgid "DECR"
msgid "DELETE"
msgid "DIVEQ"
msgid "DIVIDE"
msgid "DO"
msgid "DOT"
msgid "ELSE"
msgid "EQ"
msgid "EXIT"
msgid "FIELD"
msgid "FINAL"
msgid "FIRSTTOKEN"
msgid "FOR"
msgid "FUNC"
msgid "GE"
msgid "GETLINE"
msgid "GSUB"
msgid "GT"
msgid "IF"
msgid "IN"
msgid "INCR"
msgid "INDEX"
msgid "INDIRECT"
msgid "INTEST"
msgid "IVAR"
msgid "LASTTOKEN"
msgid "LE"
msgid "LSUBSTR"
msgid "LT"
msgid "MATCH"
msgid "MATCHFCN"
msgid "MATCHOP"
msgid "MINUS"
msgid "MOD"
msgid "MODEQ"
msgid "MULT"
msgid "MULTEQ"
msgid "NCCL"
msgid "NE"
msgid "NEXT"
msgid "NL"
msgid "NOT"
msgid "NOTMATCH"
msgid "NUMBER"
msgid "OR"
msgid "PASTAT"
msgid "PASTAT2"
msgid "PLUS"
msgid "POSTDECR"
msgid "POSTINCR"
msgid "POWEQ"
msgid "POWER"
msgid "PREDECR"
msgid "PREINCR"
msgid "PRINT"
msgid "PRINTF"
msgid "PROGRAM"
msgid "QUEST"
msgid "REGEXPR"
msgid "RETURN"
msgid "SPLIT"
msgid "SPRINTF"
msgid "STAR"
msgid "STRING"
msgid "SUB"
msgid "SUBEQ"
msgid "SUBSTR"
msgid "UMINUS"
msgid "VAR"
msgid "VARNF"
msgid "WHILE"
msgid "XBEGIN"
msgid "XEND"
msgid "token %d"
msgid ""
msgid "## gtemp %.8s %06o\n"
msgid "## tfree %.8s %06o %s\n"
msgid "%d"
msgid "%s"
msgid "%s returns %g |%s| %o\n"
msgid "(array)"
msgid "(regexpr)"
msgid "-"
msgid "a"
msgid "arg(%d), fp->nargs=%d\n"
msgid "args[%d]: %s %f <%s>, t=%o\n"
msgid "call.fcn"
msgid "callargs"
msgid "calling %s, %d args (%d in defn), fp=%d\n"
msgid "close"
msgid "copycell"
msgid "evaluate args[%d], fp=%d:\n"
msgid "execute"
msgid "exp"
msgid "fcn ret"
msgid "finished exec of %s, fp=%d\n"
msgid "fp->args"
msgid "log"
msgid "making %s into an array\n"
msgid "nullargs"
msgid "oargsarr"
msgid "pow"
msgid "r"
msgid "read value of"
msgid "retval"
msgid "split: s=|%s|, a=%s, sep=|%s|\n"
msgid "sqrt"
msgid "start exec of %s, fp=%d\n"
msgid "substr: m=%d, n=%d, s=%s\n"
msgid "w"
msgid ""
msgid " "
msgid " s=\"%s\" f=%g t=%o\n"
msgid "$zero&null"
msgid "%.20g"
msgid "%.6g"
msgid "%d"
msgid "-"
msgid "0"
msgid "ARGC"
msgid "ARGV"
msgid "ENVIRON"
msgid "FILENAME"
msgid "FNR"
msgid "FS"
msgid "NF"
msgid "NR"
msgid "OFMT"
msgid "OFS"
msgid "ORS"
msgid "RLENGTH"
msgid "RS"
msgid "RSTART"
msgid "SUBSEP"
msgid "SYMTAB"
msgid "\034"
msgid "\n"
msgid "assign to"
msgid "getfval %o: %s = %g, t=%o\n"
msgid "getsval %o: %s = \"%s\", t=%o\n"
msgid "read value of"
msgid "setfval %o: %s = %g, t=%o\n"
msgid "setsval %o: %s = \"%s\", t=%o\n"
msgid "setsymtab found %o: n=%s"
msgid "setsymtab set %o: n=%s"
msgid "setting field %d to %g\n"
msgid "setting field %d to %s\n"
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* lasciate ogne speranza, voi ch'intrate. */
#define DEBUG
#include "awk.h"
#include "y.tab.h"
#define HAT (NCHARS-1) /* matches ^ in regular expr */
/* NCHARS is 2**n */
#define MAXLIN (3 * LINE_MAX)
#define type(v) (v)->nobj /* badly overloaded here */
#define info(v) (v)->ntype /* badly overloaded here */
#define left(v) (v)->narg[0]
#define right(v) (v)->narg[1]
#define parent(v) (v)->nnext
#define LEAF case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL:
#define ELEAF case EMPTYRE: /* empty string in regexp */
#define UNARY case STAR: case PLUS: case QUEST:
/*
* encoding in tree Nodes:
* leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE):
* left is index, right contains value or pointer to value
* unary (STAR, PLUS, QUEST): left is child, right is null
* binary (CAT, OR): left and right are children
* parent contains pointer to parent
*/
int *setvec;
int *tmpset;
int maxsetvec = 0;
int rtok; /* next token in current re */
int rlxval;
static uschar *rlxstr;
static uschar *prestr; /* current position in current re */
static uschar *lastre; /* origin of last re */
static int setcnt;
static int poscnt;
char *patbeg;
int patlen;
#define NFA 20 /* cache this many dynamic fa's */
fa *fatab[NFA];
int nfatab = 0; /* entries in fatab */
static fa *mkdfa(const char *, int);
static int makeinit(fa *, int);
static void penter(Node *);
static void freetr(Node *);
static void overflo(const char *);
static void growvec(const char *);
static void cfoll(fa *, Node *);
static void follow(Node *);
static Node *reparse(const char *);
static int relex(void);
static void freefa(fa *);
static int cgoto(fa *, int, int);
fa *
makedfa(const char *s, int anchor) /* returns dfa for reg expr s */
{
int i, use, nuse;
fa *pfa;
static int now = 1;
if (setvec == NULL) { /* first time through any RE */
maxsetvec = MAXLIN;
setvec = (int *)malloc(maxsetvec * sizeof (int));
tmpset = (int *)malloc(maxsetvec * sizeof (int));
if (setvec == NULL || tmpset == NULL)
overflo("out of space initializing makedfa");
}
if (compile_time) /* a constant for sure */
return (mkdfa(s, anchor));
for (i = 0; i < nfatab; i++) { /* is it there already? */
if (fatab[i]->anchor == anchor &&
strcmp((const char *)fatab[i]->restr, s) == 0) {
fatab[i]->use = now++;
return (fatab[i]);
}
}
pfa = mkdfa(s, anchor);
if (nfatab < NFA) { /* room for another */
fatab[nfatab] = pfa;
fatab[nfatab]->use = now++;
nfatab++;
return (pfa);
}
use = fatab[0]->use; /* replace least-recently used */
nuse = 0;
for (i = 1; i < nfatab; i++)
if (fatab[i]->use < use) {
use = fatab[i]->use;
nuse = i;
}
freefa(fatab[nuse]);
fatab[nuse] = pfa;
pfa->use = now++;
return (pfa);
}
/*
* does the real work of making a dfa
* anchor = 1 for anchored matches, else 0
*/
fa *
mkdfa(const char *s, int anchor)
{
Node *p, *p1;
fa *f;
p = reparse(s);
p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
/* put ALL STAR in front of reg. exp. */
p1 = op2(CAT, p1, op2(FINAL, NIL, NIL));
/* put FINAL after reg. exp. */
poscnt = 0;
penter(p1); /* enter parent pointers and leaf indices */
if ((f = (fa *)calloc(1, sizeof (fa) + poscnt * sizeof (rrow))) == NULL)
overflo("out of space for fa");
/* penter has computed number of positions in re */
f->accept = poscnt-1;
cfoll(f, p1); /* set up follow sets */
freetr(p1);
if ((f->posns[0] =
(int *)calloc(1, *(f->re[0].lfollow) * sizeof (int))) == NULL) {
overflo("out of space in makedfa");
}
if ((f->posns[1] = (int *)calloc(1, sizeof (int))) == NULL)
overflo("out of space in makedfa");
*f->posns[1] = 0;
f->initstat = makeinit(f, anchor);
f->anchor = anchor;
f->restr = (uschar *)tostring(s);
return (f);
}
static int
makeinit(fa *f, int anchor)
{
int i, k;
f->curstat = 2;
f->out[2] = 0;
f->reset = 0;
k = *(f->re[0].lfollow);
xfree(f->posns[2]);
if ((f->posns[2] = (int *)calloc(1, (k+1) * sizeof (int))) == NULL)
overflo("out of space in makeinit");
for (i = 0; i <= k; i++) {
(f->posns[2])[i] = (f->re[0].lfollow)[i];
}
if ((f->posns[2])[1] == f->accept)
f->out[2] = 1;
for (i = 0; i < NCHARS; i++)
f->gototab[2][i] = 0;
f->curstat = cgoto(f, 2, HAT);
if (anchor) {
*f->posns[2] = k-1; /* leave out position 0 */
for (i = 0; i < k; i++) {
(f->posns[0])[i] = (f->posns[2])[i];
}
f->out[0] = f->out[2];
if (f->curstat != 2)
--(*f->posns[f->curstat]);
}
return (f->curstat);
}
void
penter(Node *p) /* set up parent pointers and leaf indices */
{
switch (type(p)) {
ELEAF
LEAF
info(p) = poscnt;
poscnt++;
break;
UNARY
penter(left(p));
parent(left(p)) = p;
break;
case CAT:
case OR:
penter(left(p));
penter(right(p));
parent(left(p)) = p;
parent(right(p)) = p;
break;
default: /* can't happen */
FATAL("can't happen: unknown type %d in penter", type(p));
break;
}
}
static void
freetr(Node *p) /* free parse tree */
{
switch (type(p)) {
ELEAF
LEAF
xfree(p);
break;
UNARY
freetr(left(p));
xfree(p);
break;
case CAT:
case OR:
freetr(left(p));
freetr(right(p));
xfree(p);
break;
default: /* can't happen */
FATAL("can't happen: unknown type %d in freetr", type(p));
break;
}
}
static void
growvec(const char *msg)
{
maxsetvec *= 4;
setvec = (int *)realloc(setvec, maxsetvec * sizeof (int));
tmpset = (int *)realloc(tmpset, maxsetvec * sizeof (int));
if (setvec == NULL || tmpset == NULL)
overflo(msg);
}
/*
* in the parsing of regular expressions, metacharacters like . have
* to be seen literally; \056 is not a metacharacter.
*/
/*
* find and eval hex string at pp, return new p; only pick up one 8-bit
* byte (2 chars).
*/
int
hexstr(uschar **pp)
{
uschar *p;
int n = 0;
int i;
for (i = 0, p = (uschar *)*pp; i < 2 && isxdigit(*p); i++, p++) {
if (isdigit(*p))
n = 16 * n + *p - '0';
else if (*p >= 'a' && *p <= 'f')
n = 16 * n + *p - 'a' + 10;
else if (*p >= 'A' && *p <= 'F')
n = 16 * n + *p - 'A' + 10;
}
*pp = (uschar *)p;
return (n);
}
#define isoctdigit(c) ((c) >= '0' && (c) <= '7')
/* pick up next thing after a \\ and increment *pp */
int
quoted(uschar **pp)
{
uschar *p = *pp;
int c;
if ((c = *p++) == 't')
c = '\t';
else if (c == 'n')
c = '\n';
else if (c == 'f')
c = '\f';
else if (c == 'r')
c = '\r';
else if (c == 'b')
c = '\b';
else if (c == '\\')
c = '\\';
else if (c == 'x') { /* hexadecimal goo follows */
c = hexstr(&p); /* this adds a null if number is invalid */
} else if (isoctdigit(c)) { /* \d \dd \ddd */
int n = c - '0';
if (isoctdigit(*p)) {
n = 8 * n + *p++ - '0';
if (isoctdigit(*p))
n = 8 * n + *p++ - '0';
}
c = n;
} /* else */
/* c = c; */
*pp = p;
return (c);
}
char *
cclenter(const char *argp) /* add a character class */
{
int i, c, c2;
uschar *p = (uschar *)argp;
uschar *op, *bp;
static uschar *buf = NULL;
static size_t bufsz = 100;
op = p;
if (buf == NULL && (buf = (uschar *)malloc(bufsz)) == NULL)
FATAL("out of space for character class [%.10s...] 1", p);
bp = buf;
for (i = 0; (c = *p++) != 0; ) {
if (c == '\\') {
c = quoted(&p);
} else if (c == '-' && i > 0 && bp[-1] != 0) {
if (*p != 0) {
c = bp[-1];
c2 = *p++;
if (c2 == '\\')
c2 = quoted(&p);
if (c > c2) { /* empty; ignore */
bp--;
i--;
continue;
}
while (c < c2) {
if (!adjbuf((char **)&buf, &bufsz,
bp-buf+2, 100, (char **)&bp,
"cclenter1")) {
FATAL(
"out of space for character class [%.10s...] 2", p);
}
*bp++ = ++c;
i++;
}
continue;
}
}
if (!adjbuf((char **)&buf, &bufsz, bp-buf+2, 100, (char **)&bp,
"cclenter2"))
FATAL(
"out of space for character class [%.10s...] 3", p);
*bp++ = c;
i++;
}
*bp = '\0';
dprintf(("cclenter: in = |%s|, out = |%s|\n", op, buf));
xfree(op);
return ((char *)tostring((char *)buf));
}
static void
overflo(const char *s)
{
FATAL("regular expression too big: %.30s...", gettext((char *)s));
}
/* enter follow set of each leaf of vertex v into lfollow[leaf] */
static void
cfoll(fa *f, Node *v)
{
int i;
int *p;
switch (type(v)) {
ELEAF
LEAF
f->re[info(v)].ltype = type(v);
f->re[info(v)].lval.np = right(v);
while (f->accept >= maxsetvec) { /* guessing here! */
growvec("out of space in cfoll()");
}
for (i = 0; i <= f->accept; i++)
setvec[i] = 0;
setcnt = 0;
follow(v); /* computes setvec and setcnt */
if ((p = (int *)calloc(1, (setcnt+1) * sizeof (int))) == NULL)
overflo("out of space building follow set");
f->re[info(v)].lfollow = p;
*p = setcnt;
for (i = f->accept; i >= 0; i--) {
if (setvec[i] == 1)
*++p = i;
}
break;
UNARY
cfoll(f, left(v));
break;
case CAT:
case OR:
cfoll(f, left(v));
cfoll(f, right(v));
break;
default: /* can't happen */
FATAL("can't happen: unknown type %d in cfoll", type(v));
}
}
/*
* collects initially active leaves of p into setvec
* returns 0 or 1 depending on whether p matches empty string
*/
static int
first(Node *p)
{
int b, lp;
switch (type(p)) {
ELEAF
LEAF
lp = info(p); /* look for high-water mark of subscripts */
while (setcnt >= maxsetvec || lp >= maxsetvec) {
/* guessing here! */
growvec("out of space in first()");
}
if (type(p) == EMPTYRE) {
setvec[lp] = 0;
return (0);
}
if (setvec[lp] != 1) {
setvec[lp] = 1;
setcnt++;
}
if (type(p) == CCL && (*(char *)right(p)) == '\0')
return (0); /* empty CCL */
else
return (1);
case PLUS:
if (first(left(p)) == 0)
return (0);
return (1);
case STAR:
case QUEST:
(void) first(left(p));
return (0);
case CAT:
if (first(left(p)) == 0 && first(right(p)) == 0)
return (0);
return (1);
case OR:
b = first(right(p));
if (first(left(p)) == 0 || b == 0)
return (0);
return (1);
}
FATAL("can't happen: unknown type %d in first", type(p));
}
/* collects leaves that can follow v into setvec */
static void
follow(Node *v)
{
Node *p;
if (type(v) == FINAL)
return;
p = parent(v);
switch (type(p)) {
case STAR:
case PLUS:
(void) first(v);
follow(p);
return;
case OR:
case QUEST:
follow(p);
return;
case CAT:
if (v == left(p)) { /* v is left child of p */
if (first(right(p)) == 0) {
follow(p);
return;
}
} else /* v is right child */
follow(p);
return;
default:
FATAL("unknown type %d in follow", type(p));
break;
}
}
static int
member(int c, const char *sarg) /* is c in s? */
{
uschar *s = (uschar *)sarg;
while (*s)
if (c == *s++)
return (1);
return (0);
}
int
match(fa *f, const char *p0) /* shortest match ? */
{
int s, ns;
uschar *p = (uschar *)p0;
s = f->reset ? makeinit(f, 0) : f->initstat;
if (f->out[s])
return (1);
do {
if ((ns = f->gototab[s][*p]) != 0)
s = ns;
else
s = cgoto(f, s, *p);
if (f->out[s])
return (1);
} while (*p++ != 0);
return (0);
}
int
pmatch(fa *f, const char *p0) /* longest match, for sub */
{
int s, ns;
uschar *p = (uschar *)p0;
uschar *q;
int i, k;
if (f->reset) {
f->initstat = s = makeinit(f, 1);
} else {
s = f->initstat;
}
patbeg = (char *)p;
patlen = -1;
do {
q = p;
do {
if (f->out[s]) /* final state */
patlen = q-p;
if ((ns = f->gototab[s][*q]) != 0)
s = ns;
else
s = cgoto(f, s, *q);
if (s == 1) { /* no transition */
if (patlen >= 0) {
patbeg = (char *)p;
return (1);
} else {
goto nextin; /* no match */
}
}
} while (*q++ != 0);
if (f->out[s])
patlen = q - p - 1; /* don't count $ */
if (patlen >= 0) {
patbeg = (char *)p;
return (1);
}
nextin:
s = 2;
if (f->reset) {
for (i = 2; i <= f->curstat; i++)
xfree(f->posns[i]);
k = *f->posns[0];
if ((f->posns[2] =
(int *)calloc(k + 1, sizeof (int))) == NULL) {
overflo("out of space in pmatch");
}
for (i = 0; i <= k; i++)
(f->posns[2])[i] = (f->posns[0])[i];
f->initstat = f->curstat = 2;
f->out[2] = f->out[0];
for (i = 0; i < NCHARS; i++)
f->gototab[2][i] = 0;
}
} while (*p++ != 0);
return (0);
}
int
nematch(fa *f, const char *p0) /* non-empty match, for sub */
{
int s, ns;
uschar *p = (uschar *)p0;
uschar *q;
int i, k;
if (f->reset) {
f->initstat = s = makeinit(f, 1);
} else {
s = f->initstat;
}
patlen = -1;
while (*p) {
q = p;
do {
if (f->out[s]) /* final state */
patlen = q-p;
if ((ns = f->gototab[s][*q]) != 0)
s = ns;
else
s = cgoto(f, s, *q);
if (s == 1) { /* no transition */
if (patlen > 0) {
patbeg = (char *)p;
return (1);
} else
goto nnextin; /* no nonempty match */
}
} while (*q++ != 0);
if (f->out[s])
patlen = q - p - 1; /* don't count $ */
if (patlen > 0) {
patbeg = (char *)p;
return (1);
}
nnextin:
s = 2;
if (f->reset) {
for (i = 2; i <= f->curstat; i++)
xfree(f->posns[i]);
k = *f->posns[0];
if ((f->posns[2] =
(int *)calloc(k + 1, sizeof (int))) == NULL) {
overflo("out of state space");
}
for (i = 0; i <= k; i++)
(f->posns[2])[i] = (f->posns[0])[i];
f->initstat = f->curstat = 2;
f->out[2] = f->out[0];
for (i = 0; i < NCHARS; i++)
f->gototab[2][i] = 0;
}
p++;
}
return (0);
}
static Node *regexp(void), *primary(void), *concat(Node *);
static Node *alt(Node *), *unary(Node *);
/* parses regular expression pointed to by p */
/* uses relex() to scan regular expression */
static Node *
reparse(const char *p)
{
Node *np;
dprintf(("reparse <%s>\n", p));
/* prestr points to string to be parsed */
lastre = prestr = (uschar *)p;
rtok = relex();
/* GNU compatibility: an empty regexp matches anything */
if (rtok == '\0') {
return (op2(EMPTYRE, NIL, NIL));
}
np = regexp();
if (rtok != '\0')
FATAL("syntax error in regular expression %s at %s",
lastre, prestr);
return (np);
}
static Node *
regexp(void) /* top-level parse of reg expr */
{
return (alt(concat(primary())));
}
static Node *
primary(void)
{
Node *np;
switch (rtok) {
case CHAR:
np = op2(CHAR, NIL, itonp(rlxval));
rtok = relex();
return (unary(np));
case ALL:
rtok = relex();
return (unary(op2(ALL, NIL, NIL)));
case EMPTYRE:
rtok = relex();
return (unary(op2(ALL, NIL, NIL)));
case DOT:
rtok = relex();
return (unary(op2(DOT, NIL, NIL)));
case CCL:
/*LINTED align*/
np = op2(CCL, NIL, (Node *)cclenter((char *)rlxstr));
rtok = relex();
return (unary(np));
case NCCL:
/*LINTED align*/
np = op2(NCCL, NIL, (Node *)cclenter((char *)rlxstr));
rtok = relex();
return (unary(np));
case '^':
rtok = relex();
return (unary(op2(CHAR, NIL, itonp(HAT))));
case '$':
rtok = relex();
return (unary(op2(CHAR, NIL, NIL)));
case '(':
rtok = relex();
if (rtok == ')') { /* special pleading for () */
rtok = relex();
return (unary(op2(CCL, NIL,
/*LINTED align*/
(Node *)tostring(""))));
}
np = regexp();
if (rtok == ')') {
rtok = relex();
return (unary(np));
} else {
FATAL("syntax error in regular expression %s at %s",
lastre, prestr);
}
/* FALLTHROUGH */
default:
FATAL("illegal primary in regular expression %s at %s",
lastre, prestr);
}
/*NOTREACHED*/
return (NULL);
}
static Node *
concat(Node *np)
{
switch (rtok) {
case EMPTYRE:
case CHAR:
case DOT:
case ALL:
case CCL:
case NCCL:
case '$':
case '(':
return (concat(op2(CAT, np, primary())));
default:
return (np);
}
}
static Node *
alt(Node *np)
{
if (rtok == OR) {
rtok = relex();
return (alt(op2(OR, np, concat(primary()))));
}
return (np);
}
static Node *
unary(Node *np)
{
switch (rtok) {
case STAR:
rtok = relex();
return (unary(op2(STAR, np, NIL)));
case PLUS:
rtok = relex();
return (unary(op2(PLUS, np, NIL)));
case QUEST:
rtok = relex();
return (unary(op2(QUEST, np, NIL)));
default:
return (np);
}
}
/*
* Character class definitions conformant to the POSIX locale as
* defined in IEEE P1003.1 draft 7 of June 2001, assuming the source
* and operating character sets are both ASCII (ISO646) or supersets
* thereof.
*
* Note that to avoid overflowing the temporary buffer used in
* relex(), the expanded character class (prior to range expansion)
* must be less than twice the size of their full name.
*/
struct charclass {
const char *cc_name;
int cc_namelen;
int (*cc_func)(int);
} charclasses[] = {
{ "alnum", 5, isalnum },
{ "alpha", 5, isalpha },
{ "blank", 5, isblank },
{ "cntrl", 5, iscntrl },
{ "digit", 5, isdigit },
{ "graph", 5, isgraph },
{ "lower", 5, islower },
{ "print", 5, isprint },
{ "punct", 5, ispunct },
{ "space", 5, isspace },
{ "upper", 5, isupper },
{ "xdigit", 6, isxdigit },
{ NULL, 0, NULL },
};
static int
relex(void) /* lexical analyzer for reparse */
{
int c, n;
int cflag;
static uschar *buf = 0;
static size_t bufsz = 100;
uschar *bp;
struct charclass *cc;
int i;
switch (c = *prestr++) {
case '|': return OR;
case '*': return STAR;
case '+': return PLUS;
case '?': return QUEST;
case '.': return DOT;
case '\0': prestr--; return '\0';
case '^':
case '$':
case '(':
case ')':
return (c);
case '\\':
rlxval = quoted(&prestr);
return (CHAR);
default:
rlxval = c;
return (CHAR);
case '[':
if (buf == NULL && (buf = (uschar *)malloc(bufsz)) == NULL)
FATAL("out of space in reg expr %.10s..", lastre);
bp = buf;
if (*prestr == '^') {
cflag = 1;
prestr++;
} else
cflag = 0;
n = 2 * strlen((const char *)prestr) + 1;
if (!adjbuf((char **)&buf, &bufsz, n, n, (char **)&bp,
"relex1"))
FATAL("out of space for reg expr %.10s...", lastre);
for (;;) {
if ((c = *prestr++) == '\\') {
*bp++ = '\\';
if ((c = *prestr++) == '\0') {
FATAL("nonterminated character class "
"%.20s...", lastre);
}
*bp++ = c;
} else if (c == '[' && *prestr == ':') {
/*
* Handle POSIX character class names.
* Dag-Erling Smorgrav, des@ofug.org
*/
for (cc = charclasses; cc->cc_name; cc++)
if (strncmp((const char *)prestr + 1,
(const char *)cc->cc_name,
cc->cc_namelen) == 0)
break;
if (cc->cc_name == NULL ||
prestr[1 + cc->cc_namelen] != ':' ||
prestr[2 + cc->cc_namelen] != ']') {
*bp++ = c;
continue;
}
prestr += cc->cc_namelen + 3;
/*
* BUG: We begin at 1, instead of 0, since we
* would otherwise prematurely terminate the
* string for classes like [[:cntrl:]]. This
* means that we can't match the NUL character,
* not without first adapting the entire
* program to track each string's length.
*/
for (i = 1; i < NCHARS; i++) {
(void) adjbuf((char **)&buf, &bufsz,
bp - buf + 1, 100, (char **)&bp,
"relex2");
if (cc->cc_func(i)) {
*bp++ = i;
n++;
}
}
} else if (c == '\0') {
FATAL("nonterminated character class %.20s",
lastre);
} else if (bp == buf) { /* 1st char is special */
*bp++ = c;
} else if (c == ']') {
*bp++ = '\0';
rlxstr = (uschar *)tostring((char *)buf);
if (cflag == 0)
return (CCL);
else
return (NCCL);
} else
*bp++ = c;
}
/*NOTREACHED*/
}
return (0);
}
static int
cgoto(fa *f, int s, int c)
{
int i, j, k;
int *p, *q;
assert(c == HAT || c < NCHARS);
while (f->accept >= maxsetvec) { /* guessing here! */
growvec("out of space in cgoto()");
}
for (i = 0; i <= f->accept; i++)
setvec[i] = 0;
setcnt = 0;
/* compute positions of gototab[s,c] into setvec */
p = f->posns[s];
for (i = 1; i <= *p; i++) {
if ((k = f->re[p[i]].ltype) != FINAL) {
if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np)) ||
(k == DOT && c != 0 && c != HAT) ||
(k == ALL && c != 0) ||
(k == EMPTYRE && c != 0) ||
(k == CCL &&
member(c, (char *)f->re[p[i]].lval.up)) ||
(k == NCCL &&
!member(c, (char *)f->re[p[i]].lval.up) &&
c != 0 && c != HAT)) {
q = f->re[p[i]].lfollow;
for (j = 1; j <= *q; j++) {
if (q[j] >= maxsetvec) {
growvec("cgoto overflow");
}
if (setvec[q[j]] == 0) {
setcnt++;
setvec[q[j]] = 1;
}
}
}
}
}
/* determine if setvec is a previous state */
tmpset[0] = setcnt;
j = 1;
for (i = f->accept; i >= 0; i--)
if (setvec[i]) {
tmpset[j++] = i;
}
/* tmpset == previous state? */
for (i = 1; i <= f->curstat; i++) {
p = f->posns[i];
if ((k = tmpset[0]) != p[0])
goto different;
for (j = 1; j <= k; j++)
if (tmpset[j] != p[j])
goto different;
/* setvec is state i */
f->gototab[s][c] = i;
return (i);
different:;
}
/* add tmpset to current set of states */
if (f->curstat >= NSTATES-1) {
f->curstat = 2;
f->reset = 1;
for (i = 2; i < NSTATES; i++)
xfree(f->posns[i]);
} else
++(f->curstat);
for (i = 0; i < NCHARS; i++)
f->gototab[f->curstat][i] = 0;
xfree(f->posns[f->curstat]);
if ((p = (int *)calloc(1, (setcnt + 1) * sizeof (int))) == NULL)
overflo("out of space in cgoto");
f->posns[f->curstat] = p;
f->gototab[s][c] = f->curstat;
for (i = 0; i <= setcnt; i++)
p[i] = tmpset[i];
if (setvec[f->accept])
f->out[f->curstat] = 1;
else
f->out[f->curstat] = 0;
return (f->curstat);
}
static void
freefa(fa *f) /* free a finite automaton */
{
int i;
if (f == NULL)
return;
for (i = 0; i <= f->curstat; i++)
xfree(f->posns[i]);
for (i = 0; i <= f->accept; i++) {
xfree(f->re[i].lfollow);
if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL)
xfree((f->re[i].lval.np));
}
xfree(f->restr);
xfree(f);
}
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
#include
#include
#include
#include
#include "awk.h"
#include "y.tab.h"
extern YYSTYPE yylval;
extern int infunc;
off_t lineno = 1;
int bracecnt = 0;
int brackcnt = 0;
int parencnt = 0;
typedef struct Keyword {
const char *word;
int sub;
int type;
} Keyword;
Keyword keywords[] = { /* keep sorted: binary searched */
{ "BEGIN", XBEGIN, XBEGIN },
{ "END", XEND, XEND },
{ "NF", VARNF, VARNF },
{ "atan2", FATAN, BLTIN },
{ "break", BREAK, BREAK },
{ "close", CLOSE, CLOSE },
{ "continue", CONTINUE, CONTINUE },
{ "cos", FCOS, BLTIN },
{ "delete", DELETE, DELETE },
{ "do", DO, DO },
{ "else", ELSE, ELSE },
{ "exit", EXIT, EXIT },
{ "exp", FEXP, BLTIN },
{ "fflush", FFLUSH, BLTIN },
{ "for", FOR, FOR },
{ "func", FUNC, FUNC },
{ "function", FUNC, FUNC },
{ "getline", GETLINE, GETLINE },
{ "gsub", GSUB, GSUB },
{ "if", IF, IF },
{ "in", IN, IN },
{ "index", INDEX, INDEX },
{ "int", FINT, BLTIN },
{ "length", FLENGTH, BLTIN },
{ "log", FLOG, BLTIN },
{ "match", MATCHFCN, MATCHFCN },
{ "next", NEXT, NEXT },
{ "nextfile", NEXTFILE, NEXTFILE },
{ "print", PRINT, PRINT },
{ "printf", PRINTF, PRINTF },
{ "rand", FRAND, BLTIN },
{ "return", RETURN, RETURN },
{ "sin", FSIN, BLTIN },
{ "split", SPLIT, SPLIT },
{ "sprintf", SPRINTF, SPRINTF },
{ "sqrt", FSQRT, BLTIN },
{ "srand", FSRAND, BLTIN },
{ "sub", SUB, SUB },
{ "substr", SUBSTR, SUBSTR },
{ "system", FSYSTEM, BLTIN },
{ "tolower", FTOLOWER, BLTIN },
{ "toupper", FTOUPPER, BLTIN },
{ "while", WHILE, WHILE },
};
#define RET(x) { if (dbg) (void) printf("lex %s\n", tokname(x)); return (x); }
int
peek(void)
{
int c = input();
unput(c);
return (c);
}
int
gettok(char **pbuf, size_t *psz) /* get next input token */
{
int c, retc;
char *buf = *pbuf;
size_t sz = *psz;
char *bp = buf;
c = input();
if (c == 0)
return (0);
buf[0] = c;
buf[1] = 0;
if (!isalnum(c) && c != '.' && c != '_')
return (c);
*bp++ = c;
if (isalpha(c) || c == '_') { /* it's a varname */
for (; (c = input()) != 0; ) {
if (bp-buf >= sz &&
!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
FATAL("out of space for name %.10s...", buf);
if (isalnum(c) || c == '_')
*bp++ = c;
else {
*bp = 0;
unput(c);
break;
}
}
*bp = 0;
retc = 'a'; /* alphanumeric */
} else { /* maybe it's a number, but could be . */
char *rem;
/* read input until can't be a number */
for (; (c = input()) != 0; ) {
if (bp-buf >= sz &&
!adjbuf(&buf, &sz, bp-buf+2, 100, &bp, "gettok"))
FATAL("out of space for number %.10s...", buf);
if (isdigit(c) || c == 'e' || c == 'E' ||
c == '.' || c == '+' || c == '-')
*bp++ = c;
else {
unput(c);
break;
}
}
*bp = 0;
(void) strtod(buf, &rem); /* parse the number */
if (rem == buf) { /* it wasn't a valid number at all */
buf[1] = 0; /* return one character as token */
retc = buf[0]; /* character is its own type */
unputstr(rem+1); /* put rest back for later */
} else { /* some prefix was a number */
unputstr(rem); /* put rest back for later */
rem[0] = 0; /* truncate buf after number part */
retc = '0'; /* type is number */
}
}
*pbuf = buf;
*psz = sz;
return (retc);
}
int word(char *);
int string(void);
int regexpr(void);
int sc = 0; /* 1 => return a } right now */
int reg = 0; /* 1 => return a REGEXPR now */
int
yylex(void)
{
int c;
static char *buf = NULL;
/* BUG: setting this small causes core dump! */
static size_t bufsize = 5;
if (buf == NULL && (buf = (char *)malloc(bufsize)) == NULL)
FATAL("out of space in yylex");
if (sc) {
sc = 0;
RET('}');
}
if (reg) {
reg = 0;
return (regexpr());
}
for (;;) {
c = gettok(&buf, &bufsize);
if (c == 0)
return (0);
if (isalpha(c) || c == '_')
return (word(buf));
if (isdigit(c)) {
yylval.cp = setsymtab(
buf, tostring(buf), atof(buf), CON|NUM, symtab);
/* should this also have STR set? */
RET(NUMBER);
}
yylval.i = c;
switch (c) {
case '\n': /* {EOL} */
lineno++;
RET(NL);
case '\r': /* assume \n is coming */
case ' ': /* {WS}+ */
case '\t':
break;
case '#': /* #.* strip comments */
while ((c = input()) != '\n' && c != 0)
;
unput(c);
break;
case ';':
RET(';');
case '\\':
if (peek() == '\n') {
(void) input();
lineno++;
} else if (peek() == '\r') {
(void) input();
(void) input(); /* BUG: check for \n */
lineno++;
} else {
RET(c);
}
break;
case '&':
if (peek() == '&') {
(void) input();
RET(AND);
} else
RET('&');
case '|':
if (peek() == '|') {
(void) input();
RET(BOR);
} else
RET('|');
case '!':
if (peek() == '=') {
(void) input();
yylval.i = NE;
RET(NE);
} else if (peek() == '~') {
(void) input();
yylval.i = NOTMATCH;
RET(MATCHOP);
} else
RET(NOT);
case '~':
yylval.i = MATCH;
RET(MATCHOP);
case '<':
if (peek() == '=') {
(void) input();
yylval.i = LE;
RET(LE);
} else {
yylval.i = LT;
RET(LT);
}
case '=':
if (peek() == '=') {
(void) input();
yylval.i = EQ;
RET(EQ);
} else {
yylval.i = ASSIGN;
RET(ASGNOP);
}
case '>':
if (peek() == '=') {
(void) input();
yylval.i = GE;
RET(GE);
} else if (peek() == '>') {
(void) input();
yylval.i = APPEND;
RET(APPEND);
} else {
yylval.i = GT;
RET(GT);
}
case '+':
if (peek() == '+') {
(void) input();
yylval.i = INCR;
RET(INCR);
} else if (peek() == '=') {
(void) input();
yylval.i = ADDEQ;
RET(ASGNOP);
} else
RET('+');
case '-':
if (peek() == '-') {
(void) input();
yylval.i = DECR;
RET(DECR);
} else if (peek() == '=') {
(void) input();
yylval.i = SUBEQ;
RET(ASGNOP);
} else
RET('-');
case '*':
if (peek() == '=') { /* *= */
(void) input();
yylval.i = MULTEQ;
RET(ASGNOP);
} else if (peek() == '*') { /* ** or **= */
(void) input(); /* eat 2nd * */
if (peek() == '=') {
(void) input();
yylval.i = POWEQ;
RET(ASGNOP);
} else {
RET(POWER);
}
} else
RET('*');
case '/':
RET('/');
case '%':
if (peek() == '=') {
(void) input();
yylval.i = MODEQ;
RET(ASGNOP);
} else
RET('%');
case '^':
if (peek() == '=') {
(void) input();
yylval.i = POWEQ;
RET(ASGNOP);
} else
RET(POWER);
case '$':
/* BUG: awkward, if not wrong */
c = gettok(&buf, &bufsize);
if (isalpha(c)) {
if (strcmp(buf, "NF") == 0) {
/* very special */
unputstr("(NF)");
RET(INDIRECT);
}
c = peek();
if (c == '(' || c == '[' ||
(infunc && isarg(buf) >= 0)) {
unputstr(buf);
RET(INDIRECT);
}
yylval.cp = setsymtab(
buf, "", 0.0, STR|NUM, symtab);
RET(IVAR);
} else if (c == 0) { /* */
SYNTAX("unexpected end of input after $");
RET(';');
} else {
unputstr(buf);
RET(INDIRECT);
}
case '}':
if (--bracecnt < 0)
SYNTAX("extra }");
sc = 1;
RET(';');
case ']':
if (--brackcnt < 0)
SYNTAX("extra ]");
RET(']');
case ')':
if (--parencnt < 0)
SYNTAX("extra )");
RET(')');
case '{':
bracecnt++;
RET('{');
case '[':
brackcnt++;
RET('[');
case '(':
parencnt++;
RET('(');
case '"':
/* BUG: should be like tran.c ? */
return (string());
default:
RET(c);
}
}
}
int
string(void)
{
int c, n;
char *s, *bp;
static char *buf = NULL;
static size_t bufsz = 500;
if (buf == NULL && (buf = (char *)malloc(bufsz)) == NULL)
FATAL("out of space for strings");
for (bp = buf; (c = input()) != '"'; ) {
if (!adjbuf(&buf, &bufsz, bp-buf+2, 500, &bp, "string"))
FATAL("out of space for string %.10s...", buf);
switch (c) {
case '\n':
case '\r':
case 0:
*bp = '\0';
SYNTAX("non-terminated string %.10s...", buf);
if (c == 0) /* hopeless */
FATAL("giving up");
lineno++;
break;
case '\\':
c = input();
switch (c) {
case '"': *bp++ = '"'; break;
case 'n': *bp++ = '\n'; break;
case 't': *bp++ = '\t'; break;
case 'f': *bp++ = '\f'; break;
case 'r': *bp++ = '\r'; break;
case 'b': *bp++ = '\b'; break;
case 'v': *bp++ = '\v'; break;
case 'a': *bp++ = '\007'; break;
case '\\': *bp++ = '\\'; break;
case '0': case '1': case '2': /* octal: \d \dd \ddd */
case '3': case '4': case '5': case '6': case '7':
n = c - '0';
if ((c = peek()) >= '0' && c < '8') {
n = 8 * n + input() - '0';
if ((c = peek()) >= '0' && c < '8')
n = 8 * n + input() - '0';
}
*bp++ = n;
break;
case 'x': { /* hex \x0-9a-fA-F + */
char xbuf[100], *px;
px = xbuf;
while ((c = input()) != 0 && px-xbuf < 100-2) {
if (isdigit(c) ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'))
*px++ = c;
else
break;
}
*px = 0;
unput(c);
(void) sscanf(xbuf, "%x", (unsigned int *)&n);
*bp++ = n;
break;
}
default:
*bp++ = c;
break;
}
break;
default:
*bp++ = c;
break;
}
}
*bp = 0;
s = tostring(buf);
*bp++ = ' '; *bp++ = 0;
yylval.cp = setsymtab(buf, s, 0.0, CON|STR|DONTFREE, symtab);
RET(STRING);
}
int
binsearch(char *w, Keyword *kp, int n)
{
int cond, low, mid, high;
low = 0;
high = n - 1;
while (low <= high) {
mid = (low + high) / 2;
if ((cond = strcmp(w, kp[mid].word)) < 0)
high = mid - 1;
else if (cond > 0)
low = mid + 1;
else
return (mid);
}
return (-1);
}
int
word(char *w)
{
Keyword *kp;
int c, n;
n = binsearch(w, keywords, sizeof (keywords) / sizeof (keywords[0]));
if (n != -1) { /* found in table */
kp = keywords + n;
yylval.i = kp->sub;
switch (kp->type) { /* special handling */
case BLTIN:
if (kp->sub == FSYSTEM && safe)
SYNTAX("system is unsafe");
RET(kp->type);
case FUNC:
if (infunc)
SYNTAX("illegal nested function");
RET(kp->type);
case RETURN:
if (!infunc)
SYNTAX("return not in function");
RET(kp->type);
case VARNF:
yylval.cp = setsymtab("NF", "", 0.0, NUM, symtab);
RET(VARNF);
default:
RET(kp->type);
}
}
c = peek(); /* look for '(' */
if (c != '(' && infunc && (n = isarg(w)) >= 0) {
yylval.i = n;
RET(ARG);
} else {
yylval.cp = setsymtab(w, "", 0.0, STR|NUM|DONTFREE, symtab);
if (c == '(') {
RET(CALL);
} else {
RET(VAR);
}
}
}
void
startreg(void) /* next call to yylex will return a regular expression */
{
reg = 1;
}
int
regexpr(void)
{
int c;
static char *buf = NULL;
static size_t bufsz = 500;
char *bp;
if (buf == NULL && (buf = (char *)malloc(bufsz)) == NULL)
FATAL("out of space for rex expr");
bp = buf;
for (; (c = input()) != '/' && c != 0; ) {
if (!adjbuf(&buf, &bufsz, bp-buf+3, 500, &bp, "regexpr"))
FATAL("out of space for reg expr %.10s...", buf);
if (c == '\n') {
*bp = '\0';
SYNTAX("newline in regular expression %.10s...", buf);
unput('\n');
break;
} else if (c == '\\') {
*bp++ = '\\';
*bp++ = input();
} else {
*bp++ = c;
}
}
*bp = 0;
if (c == 0)
SYNTAX("non-terminated regular expression %.10s...", buf);
yylval.s = tostring(buf);
unput('/');
RET(REGEXPR);
}
/* low-level lexical stuff, sort of inherited from lex */
char ebuf[300];
char *ep = ebuf;
char yysbuf[100]; /* pushback buffer */
char *yysptr = yysbuf;
FILE *yyin = NULL;
int
input(void) /* get next lexical input character */
{
int c;
extern char *lexprog;
if (yysptr > yysbuf)
c = (uschar)*--yysptr;
else if (lexprog != NULL) { /* awk '...' */
if ((c = (uschar)*lexprog) != 0)
lexprog++;
} else /* awk -f ... */
c = pgetc();
if (c == EOF)
c = 0;
if (ep >= ebuf + sizeof (ebuf))
ep = ebuf;
*ep = c;
if (c != 0) {
ep++;
}
return (c);
}
void
unput(int c) /* put lexical character back on input */
{
if (yysptr >= yysbuf + sizeof (yysbuf))
FATAL("pushed back too much: %.20s...", yysbuf);
*yysptr++ = c;
if (--ep < ebuf)
ep = ebuf + sizeof (ebuf) - 1;
}
void
unputstr(const char *s) /* put a string back on input */
{
int i;
for (i = strlen(s)-1; i >= 0; i--)
unput(s[i]);
}
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) Lucent Technologies 1997 */
/* All Rights Reserved */
#include
#include
#include
#include
#include
#include
#include "awk.h"
#include "y.tab.h"
static FILE *infile = NULL;
static char *file = "";
char *record;
size_t recsize = RECSIZE;
static char *fields;
static size_t fieldssize = RECSIZE;
static char *rtbuf;
static size_t rtbufsize = RECSIZE;
Cell **fldtab; /* pointers to Cells */
char inputFS[100] = " ";
#define MAXFLD 2
int nfields = MAXFLD; /* last allocated slot for $i */
int donefld; /* 1 = implies rec broken into fields */
int donerec; /* 1 = record is valid (no flds have changed) */
static int lastfld = 0; /* last used field */
static int argno = 1; /* current input argument number */
static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
static char *getargv(int);
static void cleanfld(int, int);
static int refldbld(const char *, const char *);
static void bcheck2(int, int, int);
static void eprint(void);
static void bclass(int);
void
recinit(unsigned int n)
{
if ((record = (char *)malloc(n)) == NULL ||
(fields = (char *)malloc(n+2)) == NULL ||
(fldtab = (Cell **)malloc((nfields+1) * sizeof (Cell *))) == NULL ||
(fldtab[0] = (Cell *)malloc(sizeof (Cell))) == NULL)
FATAL("out of space for $0 and fields");
*fldtab[0] = dollar0;
fldtab[0]->sval = record;
fldtab[0]->nval = tostring("0");
makefields(1, nfields);
}
void
makefields(int n1, int n2) /* create $n1..$n2 inclusive */
{
char temp[50];
int i;
for (i = n1; i <= n2; i++) {
fldtab[i] = (Cell *)malloc(sizeof (Cell));
if (fldtab[i] == NULL)
FATAL("out of space in makefields %d", i);
*fldtab[i] = dollar1;
(void) sprintf(temp, "%d", i);
fldtab[i]->nval = tostring(temp);
}
}
static void
initgetrec(void)
{
int i;
char *p;
for (i = 1; i < *ARGC; i++) {
p = getargv(i); /* find 1st real filename */
if (p == NULL || *p == '\0') { /* deleted or zapped */
argno++;
continue;
}
if (!isclvar(p)) {
(void) setsval(lookup("FILENAME", symtab), p);
return;
}
setclvar(p); /* a commandline assignment before filename */
argno++;
}
infile = stdin; /* no filenames, so use stdin */
}
/*
* POSIX specifies that fields are supposed to be evaluated as if they were
* split using the value of FS at the time that the record's value ($0) was
* read.
*
* Since field-splitting is done lazily, we save the current value of FS
* whenever a new record is read in (implicitly or via getline), or when
* a new value is assigned to $0.
*/
void
savefs(void)
{
if (strlen(getsval(fsloc)) >= sizeof (inputFS))
FATAL("field separator %.10s... is too long", *FS);
(void) strcpy(inputFS, *FS);
}
static int firsttime = 1;
/*
* get next input record
* note: cares whether buf == record
*/
int
getrec(char **pbuf, size_t *pbufsize, int isrecord)
{
int c;
char *buf = *pbuf;
uschar saveb0;
size_t bufsize = *pbufsize, savebufsize = bufsize;
if (firsttime) {
firsttime = 0;
initgetrec();
}
dprintf(("RS=<%s>, FS=<%s>, ARGC=%f, FILENAME=%s\n",
*RS, *FS, *ARGC, *FILENAME));
if (isrecord) {
donefld = 0;
donerec = 1;
savefs();
}
saveb0 = buf[0];
buf[0] = '\0';
while (argno < *ARGC || infile == stdin) {
dprintf(("argno=%d, file=|%s|\n", argno, file));
if (infile == NULL) { /* have to open a new file */
file = getargv(argno);
if (file == NULL || *file == '\0') {
/* deleted or zapped */
argno++;
continue;
}
if (isclvar(file)) {
/* a var=value arg */
setclvar(file);
argno++;
continue;
}
*FILENAME = file;
dprintf(("opening file %s\n", file));
if (*file == '-' && *(file+1) == '\0')
infile = stdin;
else if ((infile = fopen(file, "rF")) == NULL)
FATAL("can't open file %s", file);
(void) setfval(fnrloc, 0.0);
}
c = readrec(&buf, &bufsize, infile);
if (c != 0 || buf[0] != '\0') { /* normal record */
if (isrecord) {
if (freeable(recloc))
xfree(recloc->sval);
recloc->sval = buf; /* buf == record */
recloc->tval = REC | STR | DONTFREE;
if (is_number(recloc->sval)) {
recloc->fval =
atof(recloc->sval);
recloc->tval |= NUM;
}
}
(void) setfval(nrloc, nrloc->fval+1);
(void) setfval(fnrloc, fnrloc->fval+1);
*pbuf = buf;
*pbufsize = bufsize;
return (1);
}
/* EOF arrived on this file; set up next */
if (infile != stdin)
(void) fclose(infile);
infile = NULL;
argno++;
}
buf[0] = saveb0;
*pbuf = buf;
*pbufsize = savebufsize;
return (0); /* true end of file */
}
void
nextfile(void)
{
if (infile != NULL && infile != stdin)
(void) fclose(infile);
infile = NULL;
argno++;
}
/*
* read one record into buf
*/
int
readrec(char **pbuf, size_t *pbufsize, FILE *inf)
{
int sep, c;
char *rr, *rt, *buf = *pbuf;
size_t bufsize = *pbufsize;
char *rs = getsval(rsloc);
if (rtbuf == NULL && (rtbuf = malloc(rtbufsize)) == NULL)
FATAL("out of memory in readrec");
rr = buf;
rt = rtbuf;
if ((sep = *rs) == '\0') {
sep = '\n';
/* skip leading \n's */
while ((c = getc(inf)) == '\n' && c != EOF)
;
if (c != EOF)
(void) ungetc(c, inf);
}
while ((c = getc(inf)) != EOF) {
if (c != sep) {
if (rr-buf+1 > bufsize) {
(void) adjbuf(&buf, &bufsize,
1+rr-buf, recsize, &rr, "readrec1");
}
*rr++ = c;
continue;
}
/*
* Ensure enough space for either a single separator
* character, or at least two '\n' chars (when RS is
* the empty string).
*/
(void) adjbuf(&rtbuf, &rtbufsize,
2+rt-rtbuf, recsize, &rt, "readrec2");
if (*rs == sep) {
*rt++ = sep;
break;
}
if ((c = getc(inf)) == '\n') { /* 2 in a row */
*rt++ = '\n';
*rt++ = '\n';
while ((c = getc(inf)) == '\n' && c != EOF) {
/* Read any further \n's and add them to RT. */
(void) adjbuf(&rtbuf, &rtbufsize,
1+rt-rtbuf, recsize, &rt, "readrec3");
*rt++ = '\n';
}
if (c != EOF)
(void) ungetc(c, inf);
break;
}
if (c == EOF) {
*rt++ = '\n';
break;
}
(void) adjbuf(&buf, &bufsize,
2+rr-buf, recsize, &rr, "readrec4");
*rr++ = '\n';
*rr++ = c;
}
(void) adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec5");
(void) adjbuf(&rtbuf, &rtbufsize, 1+rt-rtbuf, recsize, &rt, "readrec6");
*rr = '\0';
*rt = '\0';
dprintf(("readrec saw <%s>, returns %d\n",
buf, c == EOF && rr == buf ? 0 : 1));
*pbuf = buf;
*pbufsize = bufsize;
if (c == EOF && rr == buf) {
return (0);
} else {
(void) setsval(rtloc, rtbuf);
return (1);
}
}
/* get ARGV[n] */
static char *
getargv(int n)
{
Cell *x;
char *s, temp[50];
extern Array *ARGVtab;
(void) sprintf(temp, "%d", n);
if (lookup(temp, ARGVtab) == NULL)
return (NULL);
x = setsymtab(temp, "", 0.0, STR, ARGVtab);
s = getsval(x);
dprintf(("getargv(%d) returns |%s|\n", n, s));
return (s);
}
void
setclvar(char *s) /* set var=value from s */
{
char *p;
Cell *q;
for (p = s; *p != '='; p++)
;
*p++ = 0;
p = qstring(p, '\0');
q = setsymtab(s, p, 0.0, STR, symtab);
(void) setsval(q, p);
if (is_number(q->sval)) {
q->fval = atof(q->sval);
q->tval |= NUM;
}
dprintf(("command line set %s to |%s|\n", s, p));
free(p);
}
void
fldbld(void) /* create fields from current record */
{
/* this relies on having fields[] the same length as $0 */
/* the fields are all stored in this one array with \0's */
/* possibly with a final trailing \0 not associated with any field */
char *r, *fr, sep;
Cell *p;
int i, j, n;
if (donefld)
return;
if (!isstr(fldtab[0]))
(void) getsval(fldtab[0]);
r = fldtab[0]->sval;
n = strlen(r);
if (n > fieldssize) {
xfree(fields);
/* possibly 2 final \0s */
if ((fields = (char *)malloc(n + 2)) == NULL)
FATAL("out of space for fields in fldbld %d", n);
fieldssize = n;
}
fr = fields;
i = 0; /* number of fields accumulated here */
if (strlen(inputFS) > 1) { /* it's a regular expression */
i = refldbld(r, inputFS);
} else if ((sep = *inputFS) == ' ') { /* default whitespace */
for (i = 0; ; ) {
while (*r == ' ' || *r == '\t' || *r == '\n')
r++;
if (*r == '\0')
break;
i++;
if (i > nfields)
growfldtab(i);
if (freeable(fldtab[i]))
xfree(fldtab[i]->sval);
fldtab[i]->sval = fr;
fldtab[i]->tval = FLD | STR | DONTFREE;
do
*fr++ = *r++;
while (*r != ' ' && *r != '\t' && *r != '\n' &&
*r != '\0')
;
*fr++ = '\0';
}
*fr = '\0';
} else if ((sep = *inputFS) == '\0') {
/* new: FS="" => 1 char/field */
for (i = 0; *r != '\0'; r++) {
char buf[2];
i++;
if (i > nfields)
growfldtab(i);
if (freeable(fldtab[i]))
xfree(fldtab[i]->sval);
buf[0] = *r;
buf[1] = '\0';
fldtab[i]->sval = tostring(buf);
fldtab[i]->tval = FLD | STR;
}
*fr = '\0';
} else if (*r != '\0') { /* if 0, it's a null field */
/*
* subtlecase : if length(FS) == 1 && length(RS > 0)
* \n is NOT a field separator (cf awk book 61,84).
* this variable is tested in the inner while loop.
*/
int rtest = '\n'; /* normal case */
if (strlen(*RS) > 0)
rtest = '\0';
for (;;) {
i++;
if (i > nfields)
growfldtab(i);
if (freeable(fldtab[i]))
xfree(fldtab[i]->sval);
fldtab[i]->sval = fr;
fldtab[i]->tval = FLD | STR | DONTFREE;
/* \n is always a separator */
while (*r != sep && *r != rtest && *r != '\0')
*fr++ = *r++;
*fr++ = '\0';
if (*r++ == '\0')
break;
}
*fr = '\0';
}
if (i > nfields)
FATAL("record `%.30s...' has too many fields; can't happen", r);
/* clean out junk from previous record */
cleanfld(i+1, lastfld);
lastfld = i;
donefld = 1;
for (j = 1; j <= lastfld; j++) {
p = fldtab[j];
if (is_number(p->sval)) {
p->fval = atof(p->sval);
p->tval |= NUM;
}
}
(void) setfval(nfloc, (Awkfloat)lastfld);
donerec = 1; /* restore */
if (dbg) {
for (j = 0; j <= lastfld; j++) {
p = fldtab[j];
(void) printf("field %d (%s): |%s|\n",
j, p->nval, p->sval);
}
}
}
/* clean out fields n1 .. n2 inclusive; nvals remain intact */
static void
cleanfld(int n1, int n2)
{
Cell *p;
int i;
for (i = n1; i <= n2; i++) {
p = fldtab[i];
if (freeable(p))
xfree(p->sval);
p->sval = "";
p->tval = FLD | STR | DONTFREE;
}
}
void
newfld(int n) /* add field n after end of existing lastfld */
{
if (n > nfields)
growfldtab(n);
cleanfld(lastfld+1, n);
lastfld = n;
(void) setfval(nfloc, (Awkfloat)n);
}
void
setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
{
if (n < 0)
FATAL("cannot set NF to a negative value");
if (n > nfields)
growfldtab(n);
if (lastfld < n)
cleanfld(lastfld+1, n);
else
cleanfld(n+1, lastfld);
lastfld = n;
}
Cell *
fieldadr(int n) /* get nth field */
{
if (n < 0)
FATAL("trying to access out of range field %d", n);
if (n > nfields) /* fields after NF are empty */
growfldtab(n); /* but does not increase NF */
return (fldtab[n]);
}
void
growfldtab(int n) /* make new fields up to at least $n */
{
int nf = 2 * nfields;
size_t s;
if (n > nf)
nf = n;
s = (nf+1) * (sizeof (Cell *)); /* freebsd: how much do we need? */
if (s / sizeof (Cell *) - 1 == nf) /* didn't overflow */
fldtab = (Cell **)realloc(fldtab, s);
else /* overflow sizeof int */
xfree(fldtab); /* make it null */
if (fldtab == NULL)
FATAL("out of space creating %d fields", nf);
makefields(nfields+1, nf);
nfields = nf;
}
/* build fields from reg expr in FS */
static int
refldbld(const char *rec, const char *fs)
{
/* this relies on having fields[] the same length as $0 */
/* the fields are all stored in this one array with \0's */
char *fr;
int i, tempstat, n;
fa *pfa;
n = strlen(rec);
if (n > fieldssize) {
xfree(fields);
if ((fields = (char *)malloc(n+1)) == NULL)
FATAL("out of space for fields in refldbld %d", n);
fieldssize = n;
}
fr = fields;
*fr = '\0';
if (*rec == '\0')
return (0);
pfa = makedfa(fs, 1);
dprintf(("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs));
tempstat = pfa->initstat;
for (i = 1; ; i++) {
if (i > nfields)
growfldtab(i);
if (freeable(fldtab[i]))
xfree(fldtab[i]->sval);
fldtab[i]->tval = FLD | STR | DONTFREE;
fldtab[i]->sval = fr;
dprintf(("refldbld: i=%d\n", i));
if (nematch(pfa, rec)) {
pfa->initstat = 2; /* horrible coupling to b.c */
dprintf(("match %s (%d chars)\n", patbeg, patlen));
(void) strncpy(fr, rec, patbeg-rec);
fr += patbeg - rec + 1;
*(fr-1) = '\0';
rec = patbeg + patlen;
} else {
dprintf(("no match %s\n", rec));
(void) strcpy(fr, rec);
pfa->initstat = tempstat;
break;
}
}
return (i);
}
void
recbld(void) /* create $0 from $1..$NF if necessary */
{
int i;
char *p;
size_t cnt, len, olen;
char *sep = getsval(ofsloc);
if (donerec == 1)
return;
cnt = 0;
olen = strlen(sep);
for (i = 1; i <= *NF; i++) {
p = getsval(fldtab[i]);
len = strlen(p);
expand_buf(&record, &recsize, cnt + len + olen);
(void) memcpy(&record[cnt], p, len);
cnt += len;
if (i < *NF) {
(void) memcpy(&record[cnt], sep, olen);
cnt += olen;
}
}
record[cnt] = '\0';
dprintf(("in recbld inputFS=%s, recloc=%p\n", inputFS, (void *)recloc));
if (freeable(recloc))
xfree(recloc->sval);
recloc->tval = REC | STR | DONTFREE;
recloc->sval = record;
dprintf(("in recbld inputFS=%s, recloc=%p\n", inputFS, (void *)recloc));
dprintf(("recbld = |%s|\n", record));
donerec = 1;
}
int errorflag = 0;
int
yyerror(const char *s)
{
SYNTAX("%s", s);
return (0);
}
void
SYNTAX(const char *fmt, ...)
{
extern char *cmdname, *curfname;
static int been_here = 0;
va_list varg;
if (been_here++ > 2)
return;
(void) fprintf(stderr, "%s: ", cmdname);
va_start(varg, fmt);
(void) vfprintf(stderr, fmt, varg);
va_end(varg);
(void) fprintf(stderr, " at source line %lld", lineno);
if (curfname != NULL)
(void) fprintf(stderr, " in function %s", curfname);
if (compile_time == 1 && cursource() != NULL)
(void) fprintf(stderr, " source file %s", cursource());
(void) fprintf(stderr, "\n");
errorflag = 2;
eprint();
}
void
fpecatch(int n)
{
FATAL("floating point exception %d", n);
}
extern int bracecnt, brackcnt, parencnt;
void
bracecheck(void)
{
int c;
static int beenhere = 0;
if (beenhere++)
return;
while ((c = input()) != EOF && c != '\0')
bclass(c);
bcheck2(bracecnt, '{', '}');
bcheck2(brackcnt, '[', ']');
bcheck2(parencnt, '(', ')');
}
/*ARGSUSED*/
static void
bcheck2(int n, int c1, int c2)
{
if (n == 1)
(void) fprintf(stderr, gettext("\tmissing %c\n"), c2);
else if (n > 1)
(void) fprintf(stderr, gettext("\t%d missing %c's\n"), n, c2);
else if (n == -1)
(void) fprintf(stderr, gettext("\textra %c\n"), c2);
else if (n < -1)
(void) fprintf(stderr, gettext("\t%d extra %c's\n"), -n, c2);
}
void
FATAL(const char *fmt, ...)
{
extern char *cmdname;
va_list varg;
(void) fflush(stdout);
(void) fprintf(stderr, "%s: ", cmdname);
va_start(varg, fmt);
(void) vfprintf(stderr, fmt, varg);
va_end(varg);
error();
if (dbg > 1) /* core dump if serious debugging on */
abort();
exit(2);
}
void
WARNING(const char *fmt, ...)
{
extern char *cmdname;
va_list varg;
(void) fflush(stdout);
(void) fprintf(stderr, "%s: ", cmdname);
va_start(varg, fmt);
(void) vfprintf(stderr, fmt, varg);
va_end(varg);
error();
}
void
error(void)
{
extern Node *curnode;
(void) fprintf(stderr, "\n");
if (compile_time != 2 && NR && *NR > 0) {
(void) fprintf(stderr,
gettext(" input record number %g"), *FNR);
if (strcmp(*FILENAME, "-") != 0)
(void) fprintf(stderr, gettext(", file %s"), *FILENAME);
(void) fprintf(stderr, "\n");
}
if (compile_time != 2 && curnode)
(void) fprintf(stderr, gettext(" source line number %lld"),
curnode->lineno);
else if (compile_time != 2 && lineno) {
(void) fprintf(stderr,
gettext(" source line number %lld"), lineno);
}
if (compile_time == 1 && cursource() != NULL)
(void) fprintf(stderr, gettext(" source file %s"), cursource());
(void) fprintf(stderr, "\n");
eprint();
}
static void
eprint(void) /* try to print context around error */
{
char *p, *q;
int c;
static int been_here = 0;
extern char ebuf[], *ep;
if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
return;
if (ebuf == ep)
return;
p = ep - 1;
if (p > ebuf && *p == '\n')
p--;
for (; p > ebuf && *p != '\n' && *p != '\0'; p--)
;
while (*p == '\n')
p++;
(void) fprintf(stderr, gettext(" context is\n\t"));
for (q = ep-1; q >= p && *q != ' ' && *q != '\t' && *q != '\n'; q--)
;
for (; p < q; p++)
if (*p)
(void) putc(*p, stderr);
(void) fprintf(stderr, " >>> ");
for (; p < ep; p++)
if (*p)
(void) putc(*p, stderr);
(void) fprintf(stderr, " <<< ");
if (*ep)
while ((c = input()) != '\n' && c != '\0' && c != EOF) {
(void) putc(c, stderr);
bclass(c);
}
(void) putc('\n', stderr);
ep = ebuf;
}
static void
bclass(int c)
{
switch (c) {
case '{': bracecnt++; break;
case '}': bracecnt--; break;
case '[': brackcnt++; break;
case ']': brackcnt--; break;
case '(': parencnt++; break;
case ')': parencnt--; break;
}
}
double
errcheck(double x, const char *s)
{
if (errno == EDOM) {
errno = 0;
WARNING("%s argument out of domain", s);
x = 1;
} else if (errno == ERANGE) {
errno = 0;
WARNING("%s result out of range", s);
x = 1;
}
return (x);
}
int
isclvar(const char *s) /* is s of form var=something ? */
{
if (s != NULL) {
/* Must begin with an underscore or alphabetic character */
if (isalpha(*s) || (*s == '_')) {
for (s++; *s; s++) {
/*
* followed by a sequence of underscores,
* digits, and alphabetics
*/
if (!(isalnum(*s) || *s == '_')) {
break;
}
}
return (*s == '=' && *(s + 1) != '=');
}
}
return (0);
}
#include
int
is_number(const char *s)
{
double r;
char *ep;
errno = 0;
r = strtod(s, &ep);
if (ep == s || r == HUGE_VAL || errno == ERANGE)
return (0);
while (*ep == ' ' || *ep == '\t' || *ep == '\n')
ep++;
if (*ep == '\0')
return (1);
else
return (0);
}
void
r_expand_buf(char **optr, size_t *sizep, size_t req)
{
char *nptr;
size_t amt, size = *sizep;
if (size != 0 && req < (size - 1))
return;
amt = req + 1 - size;
amt = (amt / LINE_INCR + 1) * LINE_INCR;
if ((nptr = realloc(*optr, size + amt)) == NULL)
FATAL("out of space in expand_buf");
/* initial buffer should have NULL terminated */
if (size == 0)
*nptr = '\0';
*sizep += amt;
*optr = nptr;
}
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "awk.h"
#include "y.tab.h"
char *version = "version Aug 27, 2018";
int dbg = 0;
Awkfloat srand_seed = 1;
char *cmdname; /* gets argv[0] for error messages */
char *lexprog; /* points to program argument if it exists */
int compile_time = 2; /* for error printing: */
/* 2 = cmdline, 1 = compile, 0 = running */
static char **pfile = NULL; /* program filenames from -f's */
static int npfile = 0; /* number of filenames */
static int curpfile = 0; /* current filename */
int safe = 0; /* 1 => "safe" mode */
int
main(int argc, char *argv[], char *envp[])
{
const char *fs = NULL;
/*
* At this point, numbers are still scanned as in
* the POSIX locale.
* (POSIX.2, volume 2, P867, L4742-4757)
*/
(void) setlocale(LC_ALL, "");
(void) setlocale(LC_NUMERIC, "C");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
cmdname = argv[0];
if (argc == 1) {
(void) fprintf(stderr, gettext(
"Usage: %s [-f programfile | 'program'] [-Ffieldsep] "
"[-v var=value] [files]\n"), cmdname);
exit(1);
}
(void) signal(SIGFPE, fpecatch);
srand_seed = 1;
srand((unsigned int)srand_seed);
yyin = NULL;
symtab = makesymtab(NSYMTAB/NSYMTAB);
while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
if (strcmp(argv[1], "-version") == 0 ||
strcmp(argv[1], "--version") == 0) {
(void) printf("awk %s\n", version);
exit(0);
break;
}
if (strcmp(argv[1], "--") == 0) {
/* explicit end of args */
argc--;
argv++;
break;
}
switch (argv[1][1]) {
case 's':
if (strcmp(argv[1], "-safe") == 0)
safe = 1;
break;
case 'f': /* next argument is program filename */
if (argv[1][2] != 0) { /* arg is -fsomething */
pfile = realloc(pfile,
sizeof (char *) * (npfile + 1));
if (pfile == NULL)
FATAL("out of space in main");
pfile[npfile++] = &argv[1][2];
} else { /* arg is -f something */
argc--; argv++;
if (argc <= 1)
FATAL("no program filename");
pfile = realloc(pfile,
sizeof (char *) * (npfile + 1));
if (pfile == NULL)
FATAL("out of space in main");
pfile[npfile++] = argv[1];
}
break;
case 'F': /* set field separator */
if (argv[1][2] != 0) { /* arg is -Fsomething */
/* wart: t=>\t */
if (argv[1][2] == 't' && argv[1][3] == 0)
fs = "\t";
else if (argv[1][2] != 0)
fs = &argv[1][2];
} else { /* arg is -F something */
argc--; argv++;
if (argc > 1) {
/* wart: t=>\t */
if (argv[1][0] == 't' &&
argv[1][1] == 0)
fs = "\t";
else if (argv[1][0] != 0)
fs = &argv[1][0];
}
}
if (fs == NULL || *fs == '\0')
WARNING("field separator FS is empty");
break;
case 'v': /* -v a=1 to be done NOW. one -v for each */
if (argv[1][2] != 0) { /* arg is -vsomething */
if (isclvar(&argv[1][2]))
setclvar(&argv[1][2]);
else
FATAL("invalid -v option argument: %s",
&argv[1][2]);
} else { /* arg is -v something */
argc--; argv++;
if (argc <= 1)
FATAL("no variable name");
if (isclvar(argv[1]))
setclvar(argv[1]);
else
FATAL("invalid -v option argument: %s",
argv[1]);
}
break;
case 'd':
dbg = atoi(&argv[1][2]);
if (dbg == 0)
dbg = 1;
(void) printf("awk %s\n", version);
break;
default:
WARNING("unknown option %s ignored", argv[1]);
break;
}
argc--;
argv++;
}
/* argv[1] is now the first argument */
if (npfile == 0) { /* no -f; first argument is program */
if (argc <= 1) {
if (dbg)
exit(0);
FATAL("no program given");
}
dprintf(("program = |%s|\n", argv[1]));
lexprog = argv[1];
argc--;
argv++;
}
recinit(recsize);
syminit();
compile_time = 1;
argv[0] = cmdname; /* put prog name at front of arglist */
dprintf(("argc=%d, argv[0]=%s\n", argc, argv[0]));
arginit(argc, argv);
if (!safe)
envinit(envp);
(void) yyparse();
if (fs)
*FS = qstring(fs, '\0');
dprintf(("errorflag=%d\n", errorflag));
/*
* done parsing, so now activate the LC_NUMERIC
*/
(void) setlocale(LC_ALL, "");
if (errorflag == 0) {
compile_time = 0;
run(winner);
} else
bracecheck();
return (errorflag);
}
int
pgetc(void) /* get 1 character from awk program */
{
int c;
for (;;) {
if (yyin == NULL) {
if (curpfile >= npfile)
return (EOF);
yyin = (strcmp(pfile[curpfile], "-") == 0) ?
stdin : fopen(pfile[curpfile], "rF");
if (yyin == NULL) {
FATAL("can't open file %s", pfile[curpfile]);
}
lineno = 1;
}
if ((c = getc(yyin)) != EOF)
return (c);
if (yyin != stdin)
(void) fclose(yyin);
yyin = NULL;
curpfile++;
}
}
char *
cursource(void) /* current source file name */
{
if (curpfile < npfile)
return (pfile[curpfile]);
else
return (NULL);
}
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* this program makes the table to link function names
* and type indices that is used by execute() in run.c.
* it finds the indices in ytab.h, produced by yacc.
*/
#include
#include
#include
#include
#include "awk.h"
#include "y.tab.h"
struct xx {
int token;
const char *name;
const char *pname;
} proc[] = {
{ PROGRAM, "program", NULL },
{ BOR, "boolop", " || " },
{ AND, "boolop", " && " },
{ NOT, "boolop", " !" },
{ NE, "relop", " != " },
{ EQ, "relop", " == " },
{ LE, "relop", " <= " },
{ LT, "relop", " < " },
{ GE, "relop", " >= " },
{ GT, "relop", " > " },
{ ARRAY, "array", NULL },
{ INDIRECT, "indirect", "$(" },
{ SUBSTR, "substr", "substr" },
{ SUB, "sub", "sub" },
{ GSUB, "gsub", "gsub" },
{ INDEX, "sindex", "sindex" },
{ SPRINTF, "awksprintf", "sprintf " },
{ ADD, "arith", " + " },
{ MINUS, "arith", " - " },
{ MULT, "arith", " * " },
{ DIVIDE, "arith", " / " },
{ MOD, "arith", " % " },
{ UMINUS, "arith", " -" },
{ UPLUS, "arith", " +" },
{ POWER, "arith", " **" },
{ PREINCR, "incrdecr", "++" },
{ POSTINCR, "incrdecr", "++" },
{ PREDECR, "incrdecr", "--" },
{ POSTDECR, "incrdecr", "--" },
{ CAT, "cat", " " },
{ PASTAT, "pastat", NULL },
{ PASTAT2, "dopa2", NULL },
{ MATCH, "matchop", " ~ " },
{ NOTMATCH, "matchop", " !~ " },
{ MATCHFCN, "matchop", "matchop" },
{ INTEST, "intest", "intest" },
{ PRINTF, "awkprintf", "printf" },
{ PRINT, "printstat", "print" },
{ CLOSE, "closefile", "closefile" },
{ DELETE, "awkdelete", "awkdelete" },
{ SPLIT, "split", "split" },
{ ASSIGN, "assign", " = " },
{ ADDEQ, "assign", " += " },
{ SUBEQ, "assign", " -= " },
{ MULTEQ, "assign", " *= " },
{ DIVEQ, "assign", " /= " },
{ MODEQ, "assign", " %= " },
{ POWEQ, "assign", " ^= " },
{ CONDEXPR, "condexpr", " ?: " },
{ IF, "ifstat", "if(" },
{ WHILE, "whilestat", "while(" },
{ FOR, "forstat", "for(" },
{ DO, "dostat", "do" },
{ IN, "instat", "instat" },
{ NEXT, "jump", "next" },
{ NEXTFILE, "jump", "nextfile" },
{ EXIT, "jump", "exit" },
{ BREAK, "jump", "break" },
{ CONTINUE, "jump", "continue" },
{ RETURN, "jump", "ret" },
{ BLTIN, "bltin", "bltin" },
{ CALL, "call", "call" },
{ ARG, "arg", "arg" },
{ VARNF, "getnf", "NF" },
{ GETLINE, "awkgetline", "getline" },
{ 0, "", "" },
};
#define SIZE (LASTTOKEN - FIRSTTOKEN + 1)
const char *table[SIZE];
char *names[SIZE];
int
main(int argc, char *argv[])
{
const struct xx *p;
int i, n, tok;
char c;
FILE *fp;
char buf[200], name[200], def[200];
printf("#include \n");
printf("#include \"awk.h\"\n");
printf("#include \"y.tab.h\"\n\n");
if ((fp = fopen("y.tab.h", "r")) == NULL) {
fprintf(stderr, gettext("maketab can't open y.tab.h!\n"));
exit(1);
}
printf("static char *printname[%d] = {\n", SIZE);
i = 0;
while (fgets(buf, sizeof (buf), fp) != NULL) {
n = sscanf(buf, "%1c %s %s %d", &c, def, name, &tok);
if (c != '#' || n != 4 || strcmp(def, "define") != 0) {
/* not a valid #define with numeric token value */
continue;
}
if (tok < FIRSTTOKEN || tok > LASTTOKEN) {
/* token outside expected range — skip silently
* (bison may define extra tokens like YYEMPTY) */
continue;
}
names[tok-FIRSTTOKEN] = (char *)malloc(strlen(name)+1);
strcpy(names[tok-FIRSTTOKEN], name);
printf("\t(char *) \"%s\",\t/* %d */\n", name, tok);
i++;
}
printf("};\n\n");
for (p = proc; p->token != 0; p++)
table[p->token-FIRSTTOKEN] = p->name;
printf("\nCell *(*proctab[%d])(Node **, int) = {\n", SIZE);
for (i = 0; i < SIZE; i++)
if (table[i] == 0)
printf("\tnullproc,\t/* %s */\n", names[i]);
else
printf("\t%s,\t/* %s */\n", table[i], names[i]);
printf("};\n\n");
printf("char *\ntokname(int n)\n"); /* print a tokname() function */
printf("{\n");
printf(" static char buf[100];\n\n");
printf(" if (n < FIRSTTOKEN || n > LASTTOKEN) {\n");
printf(" (void) sprintf(buf, \"token %%d\", n);\n");
printf(" return (buf);\n");
printf(" }\n");
printf(" return printname[n-FIRSTTOKEN];\n");
printf("}\n");
return (0);
}
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#define DEBUG
#include "awk.h"
#include "y.tab.h"
Node *
nodealloc(int n)
{
Node *x;
x = (Node *)malloc(sizeof (Node) + (n - 1) * sizeof (Node *));
if (x == NULL)
FATAL("out of space in nodealloc");
x->nnext = NULL;
x->lineno = lineno;
return (x);
}
Node *
exptostat(Node *a)
{
a->ntype = NSTAT;
return (a);
}
Node *
node1(int a, Node *b)
{
Node *x;
x = nodealloc(1);
x->nobj = a;
x->narg[0] = b;
return (x);
}
Node *
node2(int a, Node *b, Node *c)
{
Node *x;
x = nodealloc(2);
x->nobj = a;
x->narg[0] = b;
x->narg[1] = c;
return (x);
}
Node *
node3(int a, Node *b, Node *c, Node *d)
{
Node *x;
x = nodealloc(3);
x->nobj = a;
x->narg[0] = b;
x->narg[1] = c;
x->narg[2] = d;
return (x);
}
Node *
node4(int a, Node *b, Node *c, Node *d, Node *e)
{
Node *x;
x = nodealloc(4);
x->nobj = a;
x->narg[0] = b;
x->narg[1] = c;
x->narg[2] = d;
x->narg[3] = e;
return (x);
}
Node *
stat1(int a, Node *b)
{
Node *x;
x = node1(a, b);
x->ntype = NSTAT;
return (x);
}
Node *
stat2(int a, Node *b, Node *c)
{
Node *x;
x = node2(a, b, c);
x->ntype = NSTAT;
return (x);
}
Node *
stat3(int a, Node *b, Node *c, Node *d)
{
Node *x;
x = node3(a, b, c, d);
x->ntype = NSTAT;
return (x);
}
Node *
stat4(int a, Node *b, Node *c, Node *d, Node *e)
{
Node *x;
x = node4(a, b, c, d, e);
x->ntype = NSTAT;
return (x);
}
Node *
op1(int a, Node *b)
{
Node *x;
x = node1(a, b);
x->ntype = NEXPR;
return (x);
}
Node *
op2(int a, Node *b, Node *c)
{
Node *x;
x = node2(a, b, c);
x->ntype = NEXPR;
return (x);
}
Node *
op3(int a, Node *b, Node *c, Node *d)
{
Node *x;
x = node3(a, b, c, d);
x->ntype = NEXPR;
return (x);
}
Node *
op4(int a, Node *b, Node *c, Node *d, Node *e)
{
Node *x;
x = node4(a, b, c, d, e);
x->ntype = NEXPR;
return (x);
}
Node *
celltonode(Cell *a, int b)
{
Node *x;
a->ctype = OCELL;
a->csub = b;
x = node1(0, (Node *)a);
x->ntype = NVALUE;
return (x);
}
Node *
rectonode(void) /* make $0 into a Node */
{
extern Cell *literal0;
return (op1(INDIRECT, celltonode(literal0, CUNK)));
}
Node *
makearr(Node *p)
{
Cell *cp;
if (isvalue(p)) {
cp = (Cell *)(p->narg[0]);
if (isfcn(cp))
SYNTAX("%s is a function, not an array", cp->nval);
else if (!isarr(cp)) {
xfree(cp->sval);
cp->sval = (char *)makesymtab(NSYMTAB);
cp->tval = ARR;
}
}
return (p);
}
int paircnt; /* number of them in use */
int *pairstack; /* state of each pat,pat */
Node *
pa2stat(Node *a, Node *b, Node *c) /* pat, pat {...} */
{
Node *x;
x = node4(PASTAT2, a, b, c, itonp(paircnt));
paircnt++;
x->ntype = NSTAT;
return (x);
}
Node *
linkum(Node *a, Node *b)
{
Node *c;
if (errorflag) /* don't link things that are wrong */
return (a);
if (a == NULL)
return (b);
else if (b == NULL)
return (a);
for (c = a; c->nnext != NULL; c = c->nnext)
;
c->nnext = b;
return (a);
}
/* turn on FCN bit in definition, */
/* body of function, arglist */
void
defn(Cell *v, Node *vl, Node *st)
{
Node *p;
int n;
if (isarr(v)) {
SYNTAX("`%s' is an array name and a function name", v->nval);
return;
}
if (isarg(v->nval) != -1) {
SYNTAX("`%s' is both function name and argument name", v->nval);
return;
}
v->tval = FCN;
v->sval = (char *)st;
n = 0; /* count arguments */
for (p = vl; p != NULL; p = p->nnext)
n++;
v->fval = n;
dprintf(("defining func %s (%d args)\n", v->nval, n));
}
/* is s in argument list for current function? */
/* return -1 if not, otherwise arg # */
int
isarg(const char *s)
{
extern Node *arglist;
Node *p = arglist;
int n;
for (n = 0; p != NULL; p = p->nnext, n++)
if (strcmp(((Cell *)(p->narg[0]))->nval, s) == 0)
return (n);
return (-1);
}
int
ptoi(void *p) /* convert pointer to integer */
{
return ((int)(long)p); /* swearing that p fits, of course */
}
Node *
itonp(int i) /* and vice versa */
{
return ((Node *)(long)i);
}
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#define DEBUG
#include
#include
#include
#include
#include
#include
#include "awk.h"
#include "y.tab.h"
#define tempfree(x) if (istemp(x)) tfree(x)
static jmp_buf env;
extern Awkfloat srand_seed;
static Cell *execute(Node *);
static Cell *gettemp(void), *copycell(Cell *);
static FILE *openfile(int, const char *), *redirect(int, Node *);
Node *winner = NULL; /* root of parse tree */
static Cell *tmps; /* free temporary cells for execution */
static Cell truecell = { OBOOL, BTRUE, NULL, NULL, 1.0, NUM, NULL };
Cell *True = &truecell;
static Cell falsecell = { OBOOL, BFALSE, NULL, NULL, 0.0, NUM, NULL };
Cell *False = &falsecell;
static Cell breakcell = { OJUMP, JBREAK, NULL, NULL, 0.0, NUM, NULL };
Cell *jbreak = &breakcell;
static Cell contcell = { OJUMP, JCONT, NULL, NULL, 0.0, NUM, NULL };
Cell *jcont = &contcell;
static Cell nextcell = { OJUMP, JNEXT, NULL, NULL, 0.0, NUM, NULL };
Cell *jnext = &nextcell;
static Cell nextfilecell = { OJUMP, JNEXTFILE, NULL, NULL, 0.0,
NUM, NULL };
Cell *jnextfile = &nextfilecell;
static Cell exitcell = { OJUMP, JEXIT, NULL, NULL, 0.0, NUM, NULL };
Cell *jexit = &exitcell;
static Cell retcell = { OJUMP, JRET, NULL, NULL, 0.0, NUM, NULL };
Cell *jret = &retcell;
static Cell tempcell = { OCELL, CTEMP, NULL, "", 0.0,
NUM|STR|DONTFREE, NULL };
Node *curnode = NULL; /* the node being executed, for debugging */
static void tfree(Cell *);
static void closeall(void);
static double ipow(double, int);
static void backsub(char **pb_ptr, char **sptr_ptr);
/*
* buffer memory management
*
* pbuf: address of pointer to buffer being managed
* psiz: address of buffer size variable
* minlen: minimum length of buffer needed
* quantum: buffer size quantum
* pbptr: address of movable pointer into buffer, or 0 if none
* whatrtn: name of the calling routine if failure should cause fatal error
*
* return 0 for realloc failure, !=0 for success
*/
int
adjbuf(char **pbuf, size_t *psiz, size_t minlen, size_t quantum, char **pbptr,
const char *whatrtn)
{
if (minlen > *psiz) {
char *tbuf;
int rminlen = quantum ? minlen % quantum : 0;
int boff = pbptr ? *pbptr - *pbuf : 0;
/* round up to next multiple of quantum */
if (rminlen)
minlen += quantum - rminlen;
tbuf = (char *)realloc(*pbuf, minlen);
dprintf(("adjbuf %s: %d %d (pbuf=%p, tbuf=%p)\n", whatrtn,
*psiz, minlen, (void *)*pbuf, (void *)tbuf));
if (tbuf == NULL) {
if (whatrtn)
FATAL("out of memory in %s", whatrtn);
return (0);
}
*pbuf = tbuf;
*psiz = minlen;
if (pbptr)
*pbptr = tbuf + boff;
}
return (1);
}
void
run(Node *a) /* execution of parse tree starts here */
{
extern void stdinit(void);
stdinit();
(void) execute(a);
closeall();
}
static Cell *
execute(Node *u) /* execute a node of the parse tree */
{
Cell *(*proc)(Node **, int);
Cell *x;
Node *a;
if (u == NULL)
return (True);
for (a = u; ; a = a->nnext) {
curnode = a;
if (isvalue(a)) {
x = (Cell *) (a->narg[0]);
if (isfld(x) && !donefld)
fldbld();
else if (isrec(x) && !donerec)
recbld();
return (x);
}
/* probably a Cell* but too risky to print */
if (notlegal(a->nobj))
FATAL("illegal statement");
proc = proctab[a->nobj-FIRSTTOKEN];
x = (*proc)(a->narg, a->nobj);
if (isfld(x) && !donefld)
fldbld();
else if (isrec(x) && !donerec)
recbld();
if (isexpr(a))
return (x);
/* a statement, goto next statement */
if (isjump(x))
return (x);
if (a->nnext == NULL)
return (x);
tempfree(x);
}
}
/* execute an awk program */
/* a[0] = BEGIN, a[1] = body, a[2] = END */
/*ARGSUSED*/
Cell *
program(Node **a, int n)
{
Cell *x;
if (setjmp(env) != 0)
goto ex;
if (a[0]) { /* BEGIN */
x = execute(a[0]);
if (isexit(x))
return (True);
if (isjump(x)) {
FATAL("illegal break, continue, next or nextfile "
"from BEGIN");
}
tempfree(x);
}
if (a[1] || a[2])
while (getrec(&record, &recsize, 1) > 0) {
x = execute(a[1]);
if (isexit(x))
break;
tempfree(x);
}
ex:
if (setjmp(env) != 0) /* handles exit within END */
goto ex1;
if (a[2]) { /* END */
x = execute(a[2]);
if (isbreak(x) || isnext(x) || iscont(x))
FATAL("illegal break, continue, next or nextfile "
"from END");
tempfree(x);
}
ex1:
return (True);
}
struct Frame { /* stack frame for awk function calls */
int nargs; /* number of arguments in this call */
Cell *fcncell; /* pointer to Cell for function */
Cell **args; /* pointer to array of arguments after execute */
Cell *retval; /* return value */
};
#define NARGS 50 /* max args in a call */
struct Frame *frame = NULL; /* base of stack frames; dynamically alloc'd */
int nframe = 0; /* number of frames allocated */
struct Frame *fp = NULL; /* frame pointer. bottom level unused */
/*ARGSUSED*/
Cell *
call(Node **a, int n) /* function call. very kludgy and fragile */
{
static Cell newcopycell =
{ OCELL, CCOPY, 0, "", 0.0, NUM|STR|DONTFREE, NULL };
int i, ncall, ndef;
/* handles potential double freeing when fcn & param share a tempcell */
int freed = 0;
Node *x;
Cell *args[NARGS], *oargs[NARGS]; /* BUG: fixed size arrays */
Cell *y, *z, *fcn;
char *s;
fcn = execute(a[0]); /* the function itself */
s = fcn->nval;
if (!isfcn(fcn))
FATAL("calling undefined function %s", s);
if (frame == NULL) {
fp = frame = (struct Frame *)calloc(nframe += 100,
sizeof (struct Frame));
if (frame == NULL) {
FATAL("out of space for stack frames calling %s", s);
}
}
for (ncall = 0, x = a[1]; x != NULL; x = x->nnext) /* args in call */
ncall++;
ndef = (int)fcn->fval; /* args in defn */
dprintf(("calling %s, %d args (%d in defn), fp=%d\n",
s, ncall, ndef, fp-frame));
if (ncall > ndef) {
WARNING("function %s called with %d args, uses only %d",
s, ncall, ndef);
}
if (ncall + ndef > NARGS) {
FATAL("function %s has %d arguments, limit %d",
s, ncall+ndef, NARGS);
}
for (i = 0, x = a[1]; x != NULL; i++, x = x->nnext) {
/* get call args */
dprintf(("evaluate args[%d], fp=%d:\n", i, fp-frame));
y = execute(x);
oargs[i] = y;
dprintf(("args[%d]: %s %f <%s>, t=%o\n",
i, NN(y->nval), y->fval,
isarr(y) ? "(array)" : NN(y->sval), y->tval));
if (isfcn(y)) {
FATAL("can't use function %s as argument in %s",
y->nval, s);
}
if (isarr(y))
args[i] = y; /* arrays by ref */
else
args[i] = copycell(y);
tempfree(y);
}
for (; i < ndef; i++) { /* add null args for ones not provided */
args[i] = gettemp();
*args[i] = newcopycell;
}
fp++; /* now ok to up frame */
if (fp >= frame + nframe) {
int dfp = fp - frame; /* old index */
frame = (struct Frame *)
realloc(frame, (nframe += 100) * sizeof (struct Frame));
if (frame == NULL)
FATAL("out of space for stack frames in %s", s);
fp = frame + dfp;
}
fp->fcncell = fcn;
fp->args = args;
fp->nargs = ndef; /* number defined with (excess are locals) */
fp->retval = gettemp();
dprintf(("start exec of %s, fp=%d\n", s, fp-frame));
/*LINTED align*/
y = execute((Node *)(fcn->sval)); /* execute body */
dprintf(("finished exec of %s, fp=%d\n", s, fp-frame));
for (i = 0; i < ndef; i++) {
Cell *t = fp->args[i];
if (isarr(t)) {
if (t->csub == CCOPY) {
if (i >= ncall) {
freesymtab(t);
t->csub = CTEMP;
tempfree(t);
} else {
oargs[i]->tval = t->tval;
oargs[i]->tval &= ~(STR|NUM|DONTFREE);
oargs[i]->sval = t->sval;
tempfree(t);
}
}
} else if (t != y) { /* kludge to prevent freeing twice */
t->csub = CTEMP;
tempfree(t);
} else if (t == y && t->csub == CCOPY) {
t->csub = CTEMP;
tempfree(t);
freed = 1;
}
}
tempfree(fcn);
if (isexit(y) || isnext(y))
return (y);
if (freed == 0) {
tempfree(y); /* don't free twice! */
}
z = fp->retval; /* return value */
dprintf(("%s returns %g |%s| %o\n",
s, getfval(z), getsval(z), z->tval));
fp--;
return (z);
}
static Cell *
copycell(Cell *x) /* make a copy of a cell in a temp */
{
Cell *y;
/* copy is not constant or field */
y = gettemp();
y->tval = x->tval & ~(CON|FLD|REC);
y->csub = CCOPY; /* prevents freeing until call is over */
y->nval = x->nval; /* BUG? */
if (isstr(x)) {
y->sval = tostring(x->sval);
y->tval &= ~DONTFREE;
} else
y->tval |= DONTFREE;
y->fval = x->fval;
return (y);
}
/*ARGSUSED*/
Cell *
arg(Node **a, int nnn) /* nth argument of a function */
{
int n;
n = ptoi(a[0]); /* argument number, counting from 0 */
dprintf(("arg(%d), fp->nargs=%d\n", n, fp->nargs));
if (n+1 > fp->nargs) {
FATAL("argument #%d of function %s was not supplied",
n+1, fp->fcncell->nval);
}
return (fp->args[n]);
}
Cell *
jump(Node **a, int n) /* break, continue, next, nextfile, return */
{
Cell *y;
switch (n) {
case EXIT:
if (a[0] != NULL) {
y = execute(a[0]);
errorflag = (int)getfval(y);
tempfree(y);
}
longjmp(env, 1);
/*NOTREACHED*/
case RETURN:
if (a[0] != NULL) {
y = execute(a[0]);
if ((y->tval & (STR|NUM)) == (STR|NUM)) {
(void) setsval(fp->retval, getsval(y));
fp->retval->fval = getfval(y);
fp->retval->tval |= NUM;
} else if (y->tval & STR)
(void) setsval(fp->retval, getsval(y));
else if (y->tval & NUM)
(void) setfval(fp->retval, getfval(y));
else /* can't happen */
FATAL("bad type variable %d", y->tval);
tempfree(y);
}
return (jret);
case NEXT:
return (jnext);
case NEXTFILE:
nextfile();
return (jnextfile);
case BREAK:
return (jbreak);
case CONTINUE:
return (jcont);
default: /* can't happen */
FATAL("illegal jump type %d", n);
}
/*NOTREACHED*/
return (NULL);
}
Cell *
awkgetline(Node **a, int n) /* get next line from specific input */
{
/* a[0] is variable, a[1] is operator, a[2] is filename */
Cell *r, *x;
FILE *fp;
char *buf;
size_t bufsize = recsize;
int mode;
if ((buf = (char *)malloc(bufsize)) == NULL)
FATAL("out of memory in getline");
(void) fflush(stdout); /* in case someone is waiting for a prompt */
r = gettemp();
if (a[1] != NULL) { /* getline < file */
x = execute(a[2]); /* filename */
mode = ptoi(a[1]);
if (mode == '|') /* input pipe */
mode = LE; /* arbitrary flag */
fp = openfile(mode, getsval(x));
tempfree(x);
if (fp == NULL)
n = -1;
else
n = readrec(&buf, &bufsize, fp);
/*LINTED if*/
if (n <= 0) {
;
} else if (a[0] != NULL) { /* getline var sval)) {
recloc->fval = atof(recloc->sval);
recloc->tval |= NUM;
}
}
} else { /* bare getline; use current input */
if (a[0] == NULL) /* getline */
n = getrec(&record, &recsize, 1);
else { /* getline var */
n = getrec(&buf, &bufsize, 0);
x = execute(a[0]);
(void) setsval(x, buf);
tempfree(x);
}
}
(void) setfval(r, (Awkfloat)n);
free(buf);
return (r);
}
/*ARGSUSED*/
Cell *
getnf(Node **a, int n) /* get NF */
{
if (donefld == 0)
fldbld();
return ((Cell *)a[0]);
}
/*ARGSUSED*/
Cell *
array(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */
{
Cell *x, *y, *z;
char *s;
Node *np;
char *buf;
size_t bufsz = recsize;
size_t tlen = 0, len, nsub;
if ((buf = (char *)malloc(bufsz)) == NULL)
FATAL("out of memory in array");
x = execute(a[0]); /* Cell* for symbol table */
buf[0] = '\0';
for (np = a[1]; np != NULL; np = np->nnext) {
y = execute(np); /* subscript */
s = getsval(y);
len = strlen(s);
nsub = strlen(getsval(subseploc));
(void) adjbuf(&buf, &bufsz, tlen + len + nsub + 1,
recsize, 0, "array");
(void) memcpy(&buf[tlen], s, len);
tlen += len;
if (np->nnext) {
(void) memcpy(&buf[tlen], *SUBSEP, nsub);
tlen += nsub;
}
buf[tlen] = '\0';
tempfree(y);
}
if (!isarr(x)) {
dprintf(("making %s into an array\n", NN(x->nval)));
if (freeable(x))
xfree(x->sval);
x->tval &= ~(STR|NUM|DONTFREE);
x->tval |= ARR;
x->sval = (char *)makesymtab(NSYMTAB);
}
/*LINTED align*/
z = setsymtab(buf, "", 0.0, STR|NUM, (Array *)x->sval);
z->ctype = OCELL;
z->csub = CVAR;
tempfree(x);
free(buf);
return (z);
}
/*ARGSUSED*/
Cell *
awkdelete(Node **a, int n) /* a[0] is symtab, a[1] is list of subscripts */
{
Cell *x, *y;
Node *np;
char *s;
size_t nsub;
size_t tlen = 0, len;
x = execute(a[0]); /* Cell* for symbol table */
if (x == symtabloc) {
FATAL("cannot delete SYMTAB or its elements");
}
if (!isarr(x)) {
dprintf(("making %s into an array\n", x->nval));
if (freeable(x))
xfree(x->sval);
x->tval &= ~(STR|NUM|DONTFREE);
x->tval |= ARR;
x->sval = (char *)makesymtab(NSYMTAB);
}
if (a[1] == NULL) { /* delete the elements, not the table */
freesymtab(x);
x->tval &= ~STR;
x->tval |= ARR;
x->sval = (char *)makesymtab(NSYMTAB);
} else {
size_t bufsz = recsize;
char *buf;
if ((buf = (char *)malloc(bufsz)) == NULL)
FATAL("out of memory in awkdelete");
buf[0] = '\0';
for (np = a[1]; np != NULL; np = np->nnext) {
y = execute(np); /* subscript */
s = getsval(y);
len = strlen(s);
nsub = strlen(getsval(subseploc));
(void) adjbuf(&buf, &bufsz, tlen + len + nsub + 1,
recsize, 0, "awkdelete");
(void) memcpy(&buf[tlen], s, len);
tlen += len;
if (np->nnext) {
(void) memcpy(&buf[tlen], *SUBSEP, nsub);
tlen += nsub;
}
buf[tlen] = '\0';
tempfree(y);
}
freeelem(x, buf);
free(buf);
}
tempfree(x);
return (True);
}
/*ARGSUSED*/
Cell *
intest(Node **a, int n) /* a[0] is index (list), a[1] is symtab */
{
Cell *x, *ap, *k;
Node *p;
char *buf;
char *s;
size_t bufsz = recsize;
size_t nsub;
size_t tlen = 0, len;
ap = execute(a[1]); /* array name */
if (!isarr(ap)) {
dprintf(("making %s into an array\n", ap->nval));
if (freeable(ap))
xfree(ap->sval);
ap->tval &= ~(STR|NUM|DONTFREE);
ap->tval |= ARR;
ap->sval = (char *)makesymtab(NSYMTAB);
}
if ((buf = (char *)malloc(bufsz)) == NULL) {
FATAL("out of memory in intest");
}
buf[0] = '\0';
for (p = a[0]; p != NULL; p = p->nnext) {
x = execute(p); /* expr */
s = getsval(x);
len = strlen(s);
nsub = strlen(getsval(subseploc));
(void) adjbuf(&buf, &bufsz, tlen + len + nsub + 1,
recsize, 0, "intest");
(void) memcpy(&buf[tlen], s, len);
tlen += len;
tempfree(x);
if (p->nnext) {
(void) memcpy(&buf[tlen], *SUBSEP, nsub);
tlen += nsub;
}
buf[tlen] = '\0';
}
/*LINTED align*/
k = lookup(buf, (Array *)ap->sval);
tempfree(ap);
free(buf);
if (k == NULL)
return (False);
else
return (True);
}
Cell *
matchop(Node **a, int n) /* ~ and match() */
{
Cell *x, *y;
char *s, *t;
int i;
fa *pfa;
int (*mf)(fa *, const char *) = match, mode = 0;
if (n == MATCHFCN) {
mf = pmatch;
mode = 1;
}
x = execute(a[1]); /* a[1] = target text */
s = getsval(x);
if (a[0] == NULL) /* a[1] == 0: already-compiled reg expr */
i = (*mf)((fa *)a[2], s);
else {
y = execute(a[2]); /* a[2] = regular expr */
t = getsval(y);
pfa = makedfa(t, mode);
i = (*mf)(pfa, s);
tempfree(y);
}
tempfree(x);
if (n == MATCHFCN) {
int start = patbeg - s + 1;
if (patlen < 0)
start = 0;
(void) setfval(rstartloc, (Awkfloat)start);
(void) setfval(rlengthloc, (Awkfloat)patlen);
x = gettemp();
x->tval = NUM;
x->fval = start;
return (x);
} else if ((n == MATCH && i == 1) || (n == NOTMATCH && i == 0))
return (True);
else
return (False);
}
Cell *
boolop(Node **a, int n) /* a[0] || a[1], a[0] && a[1], !a[0] */
{
Cell *x, *y;
int i;
x = execute(a[0]);
i = istrue(x);
tempfree(x);
switch (n) {
case BOR:
if (i)
return (True);
y = execute(a[1]);
i = istrue(y);
tempfree(y);
return (i ? True : False);
case AND:
if (!i)
return (False);
y = execute(a[1]);
i = istrue(y);
tempfree(y);
return (i ? True : False);
case NOT:
return (i ? False : True);
default: /* can't happen */
FATAL("unknown boolean operator %d", n);
}
/*NOTREACHED*/
return (NULL);
}
Cell *
relop(Node **a, int n) /* a[0] < a[1], etc. */
{
int i;
Cell *x, *y;
Awkfloat j;
x = execute(a[0]);
y = execute(a[1]);
if (x->tval&NUM && y->tval&NUM) {
j = x->fval - y->fval;
i = j < 0 ? -1: (j > 0 ? 1: 0);
} else {
i = strcmp(getsval(x), getsval(y));
}
tempfree(x);
tempfree(y);
switch (n) {
case LT: return (i < 0 ? True : False);
case LE: return (i <= 0 ? True : False);
case NE: return (i != 0 ? True : False);
case EQ: return (i == 0 ? True : False);
case GE: return (i >= 0 ? True : False);
case GT: return (i > 0 ? True : False);
default: /* can't happen */
FATAL("unknown relational operator %d", n);
}
/*NOTREACHED*/
return (False);
}
static void
tfree(Cell *a) /* free a tempcell */
{
if (freeable(a)) {
dprintf(("freeing %s %s %o\n",
NN(a->nval), NN(a->sval), a->tval));
xfree(a->sval);
}
if (a == tmps)
FATAL("tempcell list is curdled");
a->cnext = tmps;
tmps = a;
}
static Cell *
gettemp(void) /* get a tempcell */
{
int i;
Cell *x;
if (!tmps) {
tmps = (Cell *)calloc(100, sizeof (Cell));
if (!tmps)
FATAL("out of space for temporaries");
for (i = 1; i < 100; i++)
tmps[i-1].cnext = &tmps[i];
tmps[i-1].cnext = NULL;
}
x = tmps;
tmps = x->cnext;
*x = tempcell;
dprintf(("gtemp %.8s %06lo\n", NN(x->nval), (ulong_t)x));
return (x);
}
/*ARGSUSED*/
Cell *
indirect(Node **a, int n) /* $( a[0] ) */
{
Awkfloat val;
Cell *x;
int m;
char *s;
x = execute(a[0]);
/* freebsd: defend against super large field numbers */
val = getfval(x);
if ((Awkfloat)INT_MAX < val)
FATAL("trying to access out of range field %s", x->nval);
m = (int)val;
if (m == 0 && !is_number(s = getsval(x))) /* suspicion! */
FATAL("illegal field $(%s), name \"%s\"", s, x->nval);
/* BUG: can x->nval ever be null??? */
tempfree(x);
x = fieldadr(m);
x->ctype = OCELL; /* BUG? why are these needed? */
x->csub = CFLD;
return (x);
}
/*ARGSUSED*/
Cell *
substr(Node **a, int nnn) /* substr(a[0], a[1], a[2]) */
{
int k, m, n;
char *s;
int temp;
Cell *x, *y, *z = NULL;
x = execute(a[0]);
y = execute(a[1]);
if (a[2] != NULL)
z = execute(a[2]);
s = getsval(x);
k = strlen(s) + 1;
if (k <= 1) {
tempfree(x);
tempfree(y);
if (a[2] != NULL) {
tempfree(z);
}
x = gettemp();
(void) setsval(x, "");
return (x);
}
m = (int)getfval(y);
if (m <= 0)
m = 1;
else if (m > k)
m = k;
tempfree(y);
if (a[2] != NULL) {
n = (int)getfval(z);
tempfree(z);
} else
n = k - 1;
if (n < 0)
n = 0;
else if (n > k - m)
n = k - m;
dprintf(("substr: m=%d, n=%d, s=%s\n", m, n, s));
y = gettemp();
temp = s[n + m - 1]; /* with thanks to John Linderman */
s[n + m - 1] = '\0';
(void) setsval(y, s + m - 1);
s[n + m - 1] = temp;
tempfree(x);
return (y);
}
/*ARGSUSED*/
Cell *
sindex(Node **a, int nnn) /* index(a[0], a[1]) */
{
Cell *x, *y, *z;
char *s1, *s2, *p1, *p2, *q;
Awkfloat v = 0.0;
x = execute(a[0]);
s1 = getsval(x);
y = execute(a[1]);
s2 = getsval(y);
z = gettemp();
for (p1 = s1; *p1 != '\0'; p1++) {
for (q = p1, p2 = s2; *p2 != '\0' && *q == *p2; q++, p2++)
;
if (*p2 == '\0') {
v = (Awkfloat) (p1 - s1 + 1); /* origin 1 */
break;
}
}
tempfree(x);
tempfree(y);
(void) setfval(z, v);
return (z);
}
#define MAXNUMSIZE 50
/* printf-like conversions */
int
format(char **pbuf, int *pbufsize, const char *s, Node *a)
{
char *fmt;
const char *os;
Cell *x;
int flag = 0, n, len;
int fmtwd; /* format width */
char *buf = *pbuf;
size_t bufsize = *pbufsize;
size_t fmtsz = recsize;
size_t cnt, tcnt, ret;
os = s;
cnt = 0;
if ((fmt = (char *)malloc(fmtsz)) == NULL)
FATAL("out of memory in format()");
while (*s) {
if (*s != '%') {
expand_buf(&buf, &bufsize, cnt);
buf[cnt++] = *s++;
continue;
}
if (*(s+1) == '%') {
expand_buf(&buf, &bufsize, cnt);
buf[cnt++] = '%';
s += 2;
continue;
}
/*
* have to be real careful in case this is a huge number,
* eg, "%100000d".
*/
fmtwd = atoi(s+1);
if (fmtwd < 0)
fmtwd = -fmtwd;
for (tcnt = 0; ; s++) {
expand_buf(&fmt, &fmtsz, tcnt);
fmt[tcnt++] = *s;
if (*s == '\0')
break;
if (isalpha((uschar)*s) &&
*s != 'l' && *s != 'h' && *s != 'L')
break; /* the ansi panoply */
if (*s == '$') {
FATAL("'$' not permitted in awk formats");
}
if (*s == '*') {
if (a == NULL) {
FATAL("not enough args in printf(%s) "
"or sprintf(%s)", os, os);
}
x = execute(a);
a = a->nnext;
tcnt--;
expand_buf(&fmt, &fmtsz, tcnt + 12);
fmtwd = (int)getfval(x);
ret = sprintf(&fmt[tcnt], "%d", fmtwd);
if (fmtwd < 0)
fmtwd = -fmtwd;
tcnt += ret;
tempfree(x);
}
}
fmt[tcnt] = '\0';
if (fmtwd < 0)
fmtwd = -fmtwd;
switch (*s) {
case 'a': case 'A':
flag = *s;
break;
case 'f': case 'e': case 'g': case 'E': case 'G':
flag = 'f';
break;
case 'd': case 'i':
flag = 'd';
if (*(s-1) == 'l')
break;
fmt[tcnt - 1] = 'l';
expand_buf(&fmt, &fmtsz, tcnt);
fmt[tcnt++] = 'd';
fmt[tcnt] = '\0';
break;
case 'o': case 'x': case 'X': case 'u':
flag = *(s-1) == 'l' ? 'd' : 'u';
break;
case 's':
flag = 's';
break;
case 'c':
flag = 'c';
break;
default:
WARNING("weird printf conversion %s", fmt);
flag = '?';
break;
}
if (flag == '?') {
len = strlen(fmt);
expand_buf(&buf, &bufsize, cnt + len);
(void) memcpy(&buf[cnt], fmt, len);
cnt += len;
buf[cnt] = '\0';
continue;
}
if (a == NULL) {
FATAL("not enough args in printf(%s) "
"or sprintf(%s)", os, os);
}
x = execute(a);
a = a->nnext;
n = MAXNUMSIZE;
if (fmtwd > n)
n = fmtwd;
retry:
/* make sure we have at least 1 byte space */
(void) adjbuf(&buf, &bufsize, 1 + n + cnt,
recsize, NULL, "format5");
len = bufsize - cnt;
switch (flag) {
case 'a':
case 'A':
case 'f':
/*LINTED*/
ret = snprintf(&buf[cnt], len,
fmt, getfval(x));
break;
case 'd':
/*LINTED*/
ret = snprintf(&buf[cnt], len,
fmt, (long)getfval(x));
break;
case 'u':
/*LINTED*/
ret = snprintf(&buf[cnt], len,
fmt, (int)getfval(x));
break;
case 's':
/*LINTED*/
ret = snprintf(&buf[cnt], len,
fmt, getsval(x));
break;
case 'c':
if (!isnum(x)) {
/*LINTED*/
ret = snprintf(&buf[cnt], len,
fmt, getsval(x)[0]);
break;
}
if (getfval(x)) {
/*LINTED*/
ret = snprintf(&buf[cnt], len,
fmt, (int)getfval(x));
} else {
/* explicit null byte */
buf[cnt] = '\0';
/* next output will start here */
buf[cnt + 1] = '\0';
ret = 1;
}
break;
default:
FATAL("can't happen: "
"bad conversion %c in format()", flag);
}
if (ret >= len) {
(void) adjbuf(&buf, &bufsize, cnt + ret + 1,
recsize, NULL, "format6");
goto retry;
}
tempfree(x);
cnt += ret;
s++;
}
buf[cnt] = '\0';
free(fmt);
for (; a != NULL; a = a->nnext) /* evaluate any remaining args */
(void) execute(a);
*pbuf = buf;
*pbufsize = bufsize;
return (cnt);
}
/*ARGSUSED*/
Cell *
awksprintf(Node **a, int n) /* sprintf(a[0]) */
{
Cell *x;
Node *y;
char *buf;
int bufsz = 3 * recsize;
if ((buf = (char *)malloc(bufsz)) == NULL)
FATAL("out of memory in awksprintf");
y = a[0]->nnext;
x = execute(a[0]);
if (format(&buf, &bufsz, getsval(x), y) == -1)
FATAL("sprintf string %.30s... too long. can't happen.", buf);
tempfree(x);
x = gettemp();
x->sval = buf;
x->tval = STR;
return (x);
}
/*ARGSUSED*/
Cell *
awkprintf(Node **a, int n) /* printf */
{
/* a[0] is list of args, starting with format string */
/* a[1] is redirection operator, a[2] is redirection file */
FILE *fp;
Cell *x;
Node *y;
char *buf;
int len;
int bufsz = 3 * recsize;
if ((buf = (char *)malloc(bufsz)) == NULL)
FATAL("out of memory in awkprintf");
y = a[0]->nnext;
x = execute(a[0]);
if ((len = format(&buf, &bufsz, getsval(x), y)) == -1)
FATAL("printf string %.30s... too long. can't happen.", buf);
tempfree(x);
if (a[1] == NULL) {
(void) fwrite(buf, len, 1, stdout);
if (ferror(stdout))
FATAL("write error on stdout");
} else {
fp = redirect(ptoi(a[1]), a[2]);
(void) fwrite(buf, len, 1, fp);
(void) fflush(fp);
if (ferror(fp))
FATAL("write error on %s", filename(fp));
}
free(buf);
return (True);
}
Cell *
arith(Node **a, int n) /* a[0] + a[1], etc. also -a[0] */
{
Awkfloat i, j = 0;
double v;
Cell *x, *y, *z;
x = execute(a[0]);
i = getfval(x);
tempfree(x);
if (n != UMINUS && n != UPLUS) {
y = execute(a[1]);
j = getfval(y);
tempfree(y);
}
z = gettemp();
switch (n) {
case ADD:
i += j;
break;
case MINUS:
i -= j;
break;
case MULT:
i *= j;
break;
case DIVIDE:
if (j == 0)
FATAL("division by zero");
i /= j;
break;
case MOD:
if (j == 0)
FATAL("division by zero in mod");
(void) modf(i/j, &v);
i = i - j * v;
break;
case UMINUS:
i = -i;
break;
case UPLUS: /* handled by getfval(), above */
break;
case POWER:
if (j >= 0 && modf(j, &v) == 0.0) /* pos integer exponent */
i = ipow(i, (int)j);
else
i = errcheck(pow(i, j), "pow");
break;
default: /* can't happen */
FATAL("illegal arithmetic operator %d", n);
}
(void) setfval(z, i);
return (z);
}
static double
ipow(double x, int n) /* x**n. ought to be done by pow, but isn't always */
{
double v;
if (n <= 0)
return (1.0);
v = ipow(x, n/2);
if (n % 2 == 0)
return (v * v);
else
return (x * v * v);
}
Cell *
incrdecr(Node **a, int n) /* a[0]++, etc. */
{
Cell *x, *z;
int k;
Awkfloat xf;
x = execute(a[0]);
xf = getfval(x);
k = (n == PREINCR || n == POSTINCR) ? 1 : -1;
if (n == PREINCR || n == PREDECR) {
(void) setfval(x, xf + k);
return (x);
}
z = gettemp();
(void) setfval(z, xf);
(void) setfval(x, xf + k);
tempfree(x);
return (z);
}
/* a[0] = a[1], a[0] += a[1], etc. */
/* this is subtle; don't muck with it. */
Cell *
assign(Node **a, int n)
{
Cell *x, *y;
Awkfloat xf, yf;
double v;
y = execute(a[1]);
x = execute(a[0]); /* order reversed from before... */
if (n == ASSIGN) { /* ordinary assignment */
/*LINTED if*/
if (x == y && !(x->tval & (FLD|REC)) && x != nfloc) {
/*
* If this is a self-assignment, we leave things alone,
* unless it's a field or NF.
*/
} else if ((y->tval & (STR|NUM)) == (STR|NUM)) {
(void) setsval(x, getsval(y));
x->fval = getfval(y);
x->tval |= NUM;
} else if (isstr(y))
(void) setsval(x, getsval(y));
else if (isnum(y))
(void) setfval(x, getfval(y));
else
funnyvar(y, "read value of");
tempfree(y);
return (x);
}
xf = getfval(x);
yf = getfval(y);
switch (n) {
case ADDEQ:
xf += yf;
break;
case SUBEQ:
xf -= yf;
break;
case MULTEQ:
xf *= yf;
break;
case DIVEQ:
if (yf == 0)
FATAL("division by zero in /=");
xf /= yf;
break;
case MODEQ:
if (yf == 0)
FATAL("division by zero in %%=");
(void) modf(xf/yf, &v);
xf = xf - yf * v;
break;
case POWEQ:
if (yf >= 0 && modf(yf, &v) == 0.0) /* pos integer exponent */
xf = ipow(xf, (int)yf);
else
xf = errcheck(pow(xf, yf), "pow");
break;
default:
FATAL("illegal assignment operator %d", n);
break;
}
tempfree(y);
(void) setfval(x, xf);
return (x);
}
/*ARGSUSED*/
Cell *
cat(Node **a, int q) /* a[0] cat a[1] */
{
Cell *x, *y, *z;
int n1, n2;
char *s = NULL;
size_t ssz = 0;
x = execute(a[0]);
n1 = strlen(getsval(x));
(void) adjbuf(&s, &ssz, n1 + 1, recsize, 0, "cat1");
(void) strncpy(s, x->sval, ssz);
y = execute(a[1]);
n2 = strlen(getsval(y));
(void) adjbuf(&s, &ssz, n1 + n2 + 1, recsize, 0, "cat2");
(void) strncpy(s + n1, y->sval, ssz - n1);
tempfree(x);
tempfree(y);
z = gettemp();
z->sval = s;
z->tval = STR;
return (z);
}
/*ARGSUSED*/
Cell *
pastat(Node **a, int n) /* a[0] { a[1] } */
{
Cell *x;
if (a[0] == NULL)
x = execute(a[1]);
else {
x = execute(a[0]);
if (istrue(x)) {
tempfree(x);
x = execute(a[1]);
}
}
return (x);
}
/*ARGSUSED*/
Cell *
dopa2(Node **a, int n) /* a[0], a[1] { a[2] } */
{
Cell *x;
int pair;
if (!pairstack) {
/* first time */
dprintf(("paircnt: %d\n", paircnt));
pairstack = (int *)calloc(paircnt, sizeof (int));
if (pairstack == NULL)
FATAL("out of space in dopa2");
}
pair = ptoi(a[3]);
if (pairstack[pair] == 0) {
x = execute(a[0]);
if (istrue(x))
pairstack[pair] = 1;
tempfree(x);
}
if (pairstack[pair] == 1) {
x = execute(a[1]);
if (istrue(x))
pairstack[pair] = 0;
tempfree(x);
x = execute(a[2]);
return (x);
}
return (False);
}
/*ARGSUSED*/
Cell *
split(Node **a, int nnn) /* split(a[0], a[1], a[2]); a[3] is type */
{
Cell *x = NULL, *y, *ap;
char *s, *origs;
char *fs, *origfs = NULL;
int sep;
char *t, temp, num[50];
int n, tempstat, arg3type;
y = execute(a[0]); /* source string */
origs = s = tostring(getsval(y));
arg3type = ptoi(a[3]);
if (a[2] == NULL) /* fs string */
fs = getsval(fsloc);
else if (arg3type == STRING) { /* split(str,arr,"string") */
x = execute(a[2]);
origfs = fs = tostring(getsval(x));
tempfree(x);
} else if (arg3type == REGEXPR)
fs = "(regexpr)"; /* split(str,arr,/regexpr/) */
else
FATAL("illegal type of split");
sep = *fs;
ap = execute(a[1]); /* array name */
freesymtab(ap);
dprintf(("split: s=|%s|, a=%s, sep=|%s|\n", s, NN(ap->nval), fs));
ap->tval &= ~STR;
ap->tval |= ARR;
ap->sval = (char *)makesymtab(NSYMTAB);
n = 0;
if (arg3type == REGEXPR && strlen((char *)((fa*)a[2])->restr) == 0) {
/*
* split(s, a, //); have to arrange things such that it looks
* like an empty separator.
*/
arg3type = 0;
fs = "";
sep = 0;
}
if (*s != '\0' && (strlen(fs) > 1 || arg3type == REGEXPR)) {
/* reg expr */
fa *pfa;
if (arg3type == REGEXPR) { /* it's ready already */
pfa = (fa *)a[2];
} else {
pfa = makedfa(fs, 1);
}
if (nematch(pfa, s)) {
tempstat = pfa->initstat;
pfa->initstat = 2;
do {
n++;
(void) sprintf(num, "%d", n);
temp = *patbeg;
*patbeg = '\0';
if (is_number(s)) {
(void) setsymtab(num, s,
atof(s),
/*LINTED align*/
STR|NUM, (Array *)ap->sval);
} else {
(void) setsymtab(num, s, 0.0,
/*LINTED align*/
STR, (Array *)ap->sval);
}
*patbeg = temp;
s = patbeg + patlen;
if (*(patbeg+patlen-1) == 0 || *s == 0) {
n++;
(void) sprintf(num, "%d", n);
(void) setsymtab(num, "", 0.0,
/*LINTED align*/
STR, (Array *)ap->sval);
pfa->initstat = tempstat;
goto spdone;
}
} while (nematch(pfa, s));
/* bwk: has to be here to reset */
/* cf gsub and refldbld */
pfa->initstat = tempstat;
}
n++;
(void) sprintf(num, "%d", n);
if (is_number(s)) {
(void) setsymtab(num, s, atof(s),
/*LINTED align*/
STR|NUM, (Array *)ap->sval);
} else {
/*LINTED align*/
(void) setsymtab(num, s, 0.0, STR, (Array *)ap->sval);
}
spdone:
pfa = NULL;
} else if (sep == ' ') {
for (n = 0; ; ) {
while (*s == ' ' || *s == '\t' || *s == '\n')
s++;
if (*s == '\0')
break;
n++;
t = s;
do
s++;
while (*s != ' ' && *s != '\t' &&
*s != '\n' && *s != '\0')
;
temp = *s;
*s = '\0';
(void) sprintf(num, "%d", n);
if (is_number(t)) {
(void) setsymtab(num, t, atof(t),
/*LINTED align*/
STR|NUM, (Array *)ap->sval);
} else {
(void) setsymtab(num, t, 0.0,
/*LINTED align*/
STR, (Array *)ap->sval);
}
*s = temp;
if (*s != '\0')
s++;
}
} else if (sep == '\0') { /* split(s, a, "") => 1 char/elem */
for (n = 0; *s != 0; s++) {
char buf[2];
n++;
(void) sprintf(num, "%d", n);
buf[0] = *s;
buf[1] = '\0';
if (isdigit((uschar)buf[0])) {
(void) setsymtab(num, buf, atof(buf),
/*LINTED align*/
STR|NUM, (Array *)ap->sval);
} else {
(void) setsymtab(num, buf, 0.0,
/*LINTED align*/
STR, (Array *)ap->sval);
}
}
} else if (*s != '\0') {
for (;;) {
n++;
t = s;
while (*s != sep && *s != '\n' && *s != '\0')
s++;
temp = *s;
*s = '\0';
(void) sprintf(num, "%d", n);
if (is_number(t)) {
(void) setsymtab(num, t, atof(t),
/*LINTED align*/
STR|NUM, (Array *)ap->sval);
} else {
(void) setsymtab(num, t, 0.0,
/*LINTED align*/
STR, (Array *)ap->sval);
}
*s = temp;
if (*s++ == '\0')
break;
}
}
tempfree(ap);
tempfree(y);
free(origs);
free(origfs);
x = gettemp();
x->tval = NUM;
x->fval = n;
return (x);
}
/*ARGSUSED*/
Cell *
condexpr(Node **a, int n) /* a[0] ? a[1] : a[2] */
{
Cell *x;
x = execute(a[0]);
if (istrue(x)) {
tempfree(x);
x = execute(a[1]);
} else {
tempfree(x);
x = execute(a[2]);
}
return (x);
}
/*ARGSUSED*/
Cell *
ifstat(Node **a, int n) /* if (a[0]) a[1]; else a[2] */
{
Cell *x;
x = execute(a[0]);
if (istrue(x)) {
tempfree(x);
x = execute(a[1]);
} else if (a[2] != NULL) {
tempfree(x);
x = execute(a[2]);
}
return (x);
}
/*ARGSUSED*/
Cell *
whilestat(Node **a, int n) /* while (a[0]) a[1] */
{
Cell *x;
for (;;) {
x = execute(a[0]);
if (!istrue(x))
return (x);
tempfree(x);
x = execute(a[1]);
if (isbreak(x)) {
x = True;
return (x);
}
if (isnext(x) || isexit(x) || isret(x))
return (x);
tempfree(x);
}
}
/*ARGSUSED*/
Cell *
dostat(Node **a, int n) /* do a[0]; while(a[1]) */
{
Cell *x;
for (;;) {
x = execute(a[0]);
if (isbreak(x))
return (True);
if (isnext(x) || isexit(x) || isret(x))
return (x);
tempfree(x);
x = execute(a[1]);
if (!istrue(x))
return (x);
tempfree(x);
}
}
/*ARGSUSED*/
Cell *
forstat(Node **a, int n) /* for (a[0]; a[1]; a[2]) a[3] */
{
Cell *x;
x = execute(a[0]);
tempfree(x);
for (;;) {
if (a[1] != NULL) {
x = execute(a[1]);
if (!istrue(x))
return (x);
else
tempfree(x);
}
x = execute(a[3]);
if (isbreak(x)) /* turn off break */
return (True);
if (isnext(x) || isexit(x) || isret(x))
return (x);
tempfree(x);
x = execute(a[2]);
tempfree(x);
}
}
/*ARGSUSED*/
Cell *
instat(Node **a, int n) /* for (a[0] in a[1]) a[2] */
{
Cell *x, *vp, *arrayp, *cp, *ncp;
Array *tp;
int i;
vp = execute(a[0]);
arrayp = execute(a[1]);
if (!isarr(arrayp)) {
dprintf(("making %s into an array\n", arrayp->nval));
if (freeable(arrayp))
xfree(arrayp->sval);
arrayp->tval &= ~(STR|NUM|DONTFREE);
arrayp->tval |= ARR;
arrayp->sval = (char *)makesymtab(NSYMTAB);
}
/*LINTED align*/
tp = (Array *)arrayp->sval;
tempfree(arrayp);
for (i = 0; i < tp->size; i++) { /* this routine knows too much */
for (cp = tp->tab[i]; cp != NULL; cp = ncp) {
(void) setsval(vp, cp->nval);
ncp = cp->cnext;
x = execute(a[2]);
if (isbreak(x)) {
tempfree(vp);
return (True);
}
if (isnext(x) || isexit(x) || isret(x)) {
tempfree(vp);
return (x);
}
tempfree(x);
}
}
return (True);
}
/*ARGSUSED*/
Cell *
bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg list */
{
Cell *x, *y;
Awkfloat u;
int t;
Awkfloat tmp;
char *p, *buf;
Node *nextarg;
FILE *fp;
void flush_all(void);
int status = 0;
t = ptoi(a[0]);
x = execute(a[1]);
nextarg = a[1]->nnext;
switch (t) {
case FLENGTH:
if (isarr(x)) {
/* LINTED align */
u = ((Array *)x->sval)->nelem;
} else {
u = strlen(getsval(x));
}
break;
case FLOG:
u = errcheck(log(getfval(x)), "log"); break;
case FINT:
(void) modf(getfval(x), &u); break;
case FEXP:
u = errcheck(exp(getfval(x)), "exp"); break;
case FSQRT:
u = errcheck(sqrt(getfval(x)), "sqrt"); break;
case FSIN:
u = sin(getfval(x)); break;
case FCOS:
u = cos(getfval(x)); break;
case FATAN:
if (nextarg == NULL) {
WARNING("atan2 requires two arguments; returning 1.0");
u = 1.0;
} else {
y = execute(a[1]->nnext);
u = atan2(getfval(x), getfval(y));
tempfree(y);
nextarg = nextarg->nnext;
}
break;
case FSYSTEM:
/* in case something is buffered already */
(void) fflush(stdout);
status = system(getsval(x));
u = status;
if (status != -1) {
if (WIFEXITED(status)) {
u = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
u = WTERMSIG(status) + 256;
if (WCOREDUMP(status))
u += 256;
} else /* something else?!? */
u = 0;
}
break;
case FRAND:
/* in principle, rand() returns something in 0..RAND_MAX */
u = (Awkfloat) (rand() % RAND_MAX) / RAND_MAX;
break;
case FSRAND:
if (isrec(x)) /* no argument provided */
u = time((time_t *)0);
else
u = getfval(x);
tmp = u;
srand((unsigned int) u);
u = srand_seed;
srand_seed = tmp;
break;
case FTOUPPER:
case FTOLOWER:
buf = tostring(getsval(x));
if (t == FTOUPPER) {
for (p = buf; *p; p++)
if (islower((uschar)*p))
*p = toupper((uschar)*p);
} else {
for (p = buf; *p; p++)
if (isupper((uschar)*p))
*p = tolower((uschar)*p);
}
tempfree(x);
x = gettemp();
(void) setsval(x, buf);
free(buf);
return (x);
case FFLUSH:
if (isrec(x) || strlen(getsval(x)) == 0) {
flush_all(); /* fflush() or fflush("") -> all */
u = 0;
} else if ((fp = openfile(FFLUSH, getsval(x))) == NULL)
u = EOF;
else
u = fflush(fp);
break;
default: /* can't happen */
FATAL("illegal function type %d", t);
break;
}
tempfree(x);
x = gettemp();
(void) setfval(x, u);
if (nextarg != NULL) {
WARNING("warning: function has too many arguments");
for (; nextarg != NULL; nextarg = nextarg->nnext)
(void) execute(nextarg);
}
return (x);
}
/*ARGSUSED*/
Cell *
printstat(Node **a, int n) /* print a[0] */
{
Node *x;
Cell *y;
FILE *fp;
if (a[1] == NULL) /* a[1] is redirection operator, a[2] is file */
fp = stdout;
else
fp = redirect(ptoi(a[1]), a[2]);
for (x = a[0]; x != NULL; x = x->nnext) {
y = execute(x);
(void) fputs(getpssval(y), fp);
tempfree(y);
if (x->nnext == NULL)
(void) fputs(getsval(orsloc), fp);
else
(void) fputs(getsval(ofsloc), fp);
}
if (a[1] != NULL)
(void) fflush(fp);
if (ferror(fp))
FATAL("write error on %s", filename(fp));
return (True);
}
/*ARGSUSED*/
Cell *
nullproc(Node **a, int n)
{
return (0);
}
static FILE *
redirect(int a, Node *b) /* set up all i/o redirections */
{
FILE *fp;
Cell *x;
char *fname;
x = execute(b);
fname = getsval(x);
fp = openfile(a, fname);
if (fp == NULL)
FATAL("can't open file %s", fname);
tempfree(x);
return (fp);
}
struct files {
FILE *fp;
const char *fname;
int mode; /* '|', 'a', 'w' => LE/LT, GT */
} *files;
int nfiles;
void
stdinit(void) /* in case stdin, etc., are not constants */
{
nfiles = FOPEN_MAX;
files = calloc(nfiles, sizeof (*files));
if (files == NULL)
FATAL("can't allocate file memory for %u files", nfiles);
files[0].fp = stdin;
files[0].fname = "/dev/stdin";
files[0].mode = LT;
files[1].fp = stdout;
files[1].fname = "/dev/stdout";
files[1].mode = GT;
files[2].fp = stderr;
files[2].fname = "/dev/stderr";
files[2].mode = GT;
}
static FILE *
openfile(int a, const char *s)
{
int i, m;
FILE *fp = NULL;
if (*s == '\0')
FATAL("null file name in print or getline");
for (i = 0; i < nfiles; i++) {
if (files[i].fname && strcmp(s, files[i].fname) == 0) {
if (a == files[i].mode ||
(a == APPEND && files[i].mode == GT)) {
return (files[i].fp);
}
if (a == FFLUSH)
return (files[i].fp);
}
}
if (a == FFLUSH) /* didn't find it, so don't create it! */
return (NULL);
for (i = 0; i < nfiles; i++) {
if (files[i].fp == 0)
break;
}
if (i >= nfiles) {
struct files *nf;
int nnf = nfiles + FOPEN_MAX;
nf = realloc(files, nnf * sizeof (*nf));
if (nf == NULL)
FATAL("cannot grow files for %s and %d files", s, nnf);
(void) memset(&nf[nfiles], 0, FOPEN_MAX * sizeof (*nf));
nfiles = nnf;
files = nf;
}
(void) fflush(stdout); /* force a semblance of order */
m = a;
if (a == GT) {
fp = fopen(s, "wF");
} else if (a == APPEND) {
fp = fopen(s, "aF");
m = GT; /* so can mix > and >> */
} else if (a == '|') { /* output pipe */
fp = popen(s, "wF");
} else if (a == LE) { /* input pipe */
fp = popen(s, "rF");
} else if (a == LT) { /* getline sval, files[i].fname) == 0) {
if (ferror(files[i].fp)) {
WARNING("i/o error occurred on %s",
files[i].fname);
}
if (files[i].mode == '|' || files[i].mode == LE)
stat = pclose(files[i].fp);
else
stat = fclose(files[i].fp);
if (stat == EOF) {
WARNING("i/o error occurred closing %s",
files[i].fname);
}
if (i > 2) /* don't do /dev/std... */
xfree(files[i].fname);
/* watch out for ref thru this */
files[i].fname = NULL;
files[i].fp = NULL;
}
}
tempfree(x);
x = gettemp();
(void) setfval(x, (Awkfloat) stat);
return (x);
}
static void
closeall(void)
{
int i, stat;
for (i = 0; i < nfiles; i++) {
if (files[i].fp) {
if (ferror(files[i].fp)) {
WARNING("i/o error occurred on %s",
files[i].fname);
}
if (files[i].mode == '|' || files[i].mode == LE)
stat = pclose(files[i].fp);
else
stat = fclose(files[i].fp);
if (stat == EOF) {
WARNING("i/o error occurred while closing %s",
files[i].fname);
}
}
}
}
void
flush_all(void)
{
int i;
for (i = 0; i < nfiles; i++)
if (files[i].fp)
(void) fflush(files[i].fp);
}
/*ARGSUSED*/
Cell *
sub(Node **a, int nnn) /* substitute command */
{
char *sptr, *pb, *q;
Cell *x, *y, *result;
char *t, *buf;
fa *pfa;
size_t bufsz = recsize;
if ((buf = (char *)malloc(bufsz)) == NULL)
FATAL("out of memory in sub");
x = execute(a[3]); /* target string */
t = getsval(x);
if (a[0] == NULL) /* 0 => a[1] is already-compiled regexpr */
pfa = (fa *)a[1]; /* regular expression */
else {
y = execute(a[1]);
pfa = makedfa(getsval(y), 1);
tempfree(y);
}
y = execute(a[2]); /* replacement string */
result = False;
if (pmatch(pfa, t)) {
sptr = t;
(void) adjbuf(&buf, &bufsz,
1 + patbeg - sptr, recsize, 0, "sub");
pb = buf;
while (sptr < patbeg)
*pb++ = *sptr++;
sptr = getsval(y);
while (*sptr != '\0') {
(void) adjbuf(&buf, &bufsz, 5 + pb - buf,
recsize, &pb, "sub");
if (*sptr == '\\') {
backsub(&pb, &sptr);
} else if (*sptr == '&') {
sptr++;
(void) adjbuf(&buf, &bufsz,
1 + patlen + pb - buf, recsize, &pb, "sub");
for (q = patbeg; q < patbeg+patlen; )
*pb++ = *q++;
} else {
*pb++ = *sptr++;
}
}
*pb = '\0';
if (pb > buf + bufsz)
FATAL("sub result1 %.30s too big; can't happen", buf);
sptr = patbeg + patlen;
if ((patlen == 0 && *patbeg) || (patlen && *(sptr-1))) {
(void) adjbuf(&buf, &bufsz,
1 + strlen(sptr) + pb - buf, 0, &pb, "sub");
while ((*pb++ = *sptr++) != '\0')
;
}
if (pb > buf + bufsz)
FATAL("sub result2 %.30s too big; can't happen", buf);
(void) setsval(x, buf); /* BUG: should be able to avoid copy */
result = True;
}
tempfree(x);
tempfree(y);
free(buf);
return (result);
}
/*ARGSUSED*/
Cell *
gsub(Node **a, int nnn) /* global substitute */
{
Cell *x, *y;
char *rptr, *sptr, *t, *pb, *q;
char *buf;
fa *pfa;
int mflag, tempstat, num;
size_t bufsz = recsize;
if ((buf = (char *)malloc(bufsz)) == NULL)
FATAL("out of memory in gsub");
mflag = 0; /* if mflag == 0, can replace empty string */
num = 0;
x = execute(a[3]); /* target string */
t = getsval(x);
if (a[0] == NULL) /* 0 => a[1] is already-compiled regexpr */
pfa = (fa *)a[1]; /* regular expression */
else {
y = execute(a[1]);
pfa = makedfa(getsval(y), 1);
tempfree(y);
}
y = execute(a[2]); /* replacement string */
if (pmatch(pfa, t)) {
tempstat = pfa->initstat;
pfa->initstat = 2;
pb = buf;
rptr = getsval(y);
do {
if (patlen == 0 && *patbeg != '\0') {
/* matched empty string */
if (mflag == 0) { /* can replace empty */
num++;
sptr = rptr;
while (*sptr != '\0') {
(void) adjbuf(&buf, &bufsz,
5 + pb - buf, recsize,
&pb, "gsub");
if (*sptr == '\\') {
backsub(&pb, &sptr);
} else if (*sptr == '&') {
sptr++;
(void) adjbuf(&buf,
&bufsz,
1+patlen+pb-buf,
recsize,
&pb, "gsub");
for (
q = patbeg;
q < patbeg+patlen;
*pb++ = *q++)
;
} else {
*pb++ = *sptr++;
}
}
}
if (*t == '\0') /* at end */
goto done;
(void) adjbuf(&buf, &bufsz,
2 + pb - buf, recsize, &pb, "gsub");
*pb++ = *t++;
/* BUG: not sure of this test */
if (pb > buf + bufsz)
FATAL("gsub result0 %.30s too big; "
"can't happen", buf);
mflag = 0;
} else { /* matched nonempty string */
num++;
sptr = t;
(void) adjbuf(&buf, &bufsz,
1 + (patbeg - sptr) + pb - buf,
recsize, &pb, "gsub");
while (sptr < patbeg)
*pb++ = *sptr++;
sptr = rptr;
while (*sptr != '\0') {
(void) adjbuf(&buf, &bufsz,
5 + pb - buf, recsize, &pb, "gsub");
if (*sptr == '\\') {
backsub(&pb, &sptr);
} else if (*sptr == '&') {
sptr++;
(void) adjbuf(&buf, &bufsz,
1 + patlen + pb - buf,
recsize, &pb, "gsub");
for (
q = patbeg;
q < patbeg+patlen;
*pb++ = *q++)
;
} else {
*pb++ = *sptr++;
}
}
t = patbeg + patlen;
if (patlen == 0 || *(t-1) == '\0' || *t == '\0')
goto done;
if (pb > buf + bufsz)
FATAL("gsub result1 %.30s too big; "
"can't happen", buf);
mflag = 1;
}
} while (pmatch(pfa, t));
sptr = t;
(void) adjbuf(&buf, &bufsz,
1 + strlen(sptr) + pb - buf, 0, &pb, "gsub");
while ((*pb++ = *sptr++) != '\0')
;
done:
if (pb < buf + bufsz)
*pb = '\0';
else if (*(pb-1) != '\0')
FATAL("gsub result2 %.30s truncated; "
"can't happen", buf);
/* BUG: should be able to avoid copy + free */
(void) setsval(x, buf);
pfa->initstat = tempstat;
}
tempfree(x);
tempfree(y);
x = gettemp();
x->tval = NUM;
x->fval = num;
free(buf);
return (x);
}
/*
* handle \\& variations; sptr[0] == '\\'
*/
static void
backsub(char **pb_ptr, char **sptr_ptr)
{
char *pb = *pb_ptr, *sptr = *sptr_ptr;
if (sptr[1] == '\\') {
if (sptr[2] == '\\' && sptr[3] == '&') { /* \\\& -> \& */
*pb++ = '\\';
*pb++ = '&';
sptr += 4;
} else if (sptr[2] == '&') { /* \\& -> \ + matched */
*pb++ = '\\';
sptr += 2;
} else { /* \\x -> \\x */
*pb++ = *sptr++;
*pb++ = *sptr++;
}
} else if (sptr[1] == '&') { /* literal & */
sptr++;
*pb++ = *sptr++;
} else /* literal \ */
*pb++ = *sptr++;
*pb_ptr = pb;
*sptr_ptr = sptr;
}
/*
* Copyright (C) Lucent Technologies 1997
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
* granted, provided that the above copyright notice appear in all
* copies and that both that the copyright notice and this
* permission notice and warranty disclaimer appear in supporting
* documentation, and that the name Lucent Technologies or any of
* its entities not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission.
*
* LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#define DEBUG
#include
#include
#include
#include
#include
#include "awk.h"
#include "y.tab.h"
#define FULLTAB 2 /* rehash when table gets this x full */
#define GROWTAB 4 /* grow table by this factor */
Array *symtab; /* main symbol table */
char **FS; /* initial field sep */
char **RS; /* initial record sep */
char **OFS; /* output field sep */
char **ORS; /* output record sep */
char **OFMT; /* output format for numbers */
char **CONVFMT; /* format for conversions in getsval */
Awkfloat *NF; /* number of fields in current record */
Awkfloat *NR; /* number of current record */
Awkfloat *FNR; /* number of current record in current file */
char **FILENAME; /* current filename argument */
Awkfloat *ARGC; /* number of arguments from command line */
char **SUBSEP; /* subscript separator for a[i,j,k]; default \034 */
Awkfloat *RSTART; /* start of re matched with ~; origin 1 (!) */
Awkfloat *RLENGTH; /* length of same */
Cell *recloc; /* location of record */
Cell *fsloc; /* FS */
Cell *nrloc; /* NR */
Cell *nfloc; /* NF */
Cell *fnrloc; /* FNR */
Cell *ofsloc; /* OFS */
Cell *orsloc; /* ORS */
Cell *rsloc; /* RS */
Cell *rtloc; /* RT */
Array *ARGVtab; /* symbol table containing ARGV[...] */
Array *ENVtab; /* symbol table containing ENVIRON[...] */
Cell *rstartloc; /* RSTART */
Cell *rlengthloc; /* RLENGTH */
Cell *subseploc; /* SUBSEP */
Cell *symtabloc; /* SYMTAB */
Cell *nullloc; /* a guaranteed empty cell */
Node *nullnode; /* zero&null, converted into a node for comparisons */
Cell *literal0;
static void rehash(Array *);
static void
setfree(Cell *vp)
{
if (&vp->sval == FS || &vp->sval == RS ||
&vp->sval == OFS || &vp->sval == ORS ||
&vp->sval == OFMT || &vp->sval == CONVFMT ||
&vp->sval == FILENAME || &vp->sval == SUBSEP)
vp->tval |= DONTFREE;
else
vp->tval &= ~DONTFREE;
}
void
syminit(void) /* initialize symbol table with builtin vars */
{
/* initialize $0 */
recloc = fieldadr(0);
recloc->nval = "$0";
recloc->sval = record;
recloc->tval = REC|STR|DONTFREE;
literal0 = setsymtab("0", "0", 0.0, NUM|STR|CON|DONTFREE, symtab);
/* this is used for if(x)... tests: */
nullloc = setsymtab("$zero&null", "", 0.0,
NUM|STR|CON|DONTFREE, symtab);
nullnode = celltonode(nullloc, CCON);
fsloc = setsymtab("FS", " ", 0.0, STR|DONTFREE, symtab);
FS = &fsloc->sval;
rsloc = setsymtab("RS", "\n", 0.0, STR|DONTFREE, symtab);
RS = &rsloc->sval;
rtloc = setsymtab("RT", "", 0.0, STR|DONTFREE, symtab);
ofsloc = setsymtab("OFS", " ", 0.0, STR|DONTFREE, symtab);
OFS = &ofsloc->sval;
orsloc = setsymtab("ORS", "\n", 0.0, STR|DONTFREE, symtab);
ORS = &orsloc->sval;
OFMT = &setsymtab("OFMT", "%.6g", 0.0, STR|DONTFREE, symtab)->sval;
CONVFMT = &setsymtab("CONVFMT", "%.6g", 0.0,
STR|DONTFREE, symtab)->sval;
FILENAME = &setsymtab("FILENAME", "", 0.0, STR|DONTFREE, symtab)->sval;
nfloc = setsymtab("NF", "", 0.0, NUM, symtab);
NF = &nfloc->fval;
nrloc = setsymtab("NR", "", 0.0, NUM, symtab);
NR = &nrloc->fval;
fnrloc = setsymtab("FNR", "", 0.0, NUM, symtab);
FNR = &fnrloc->fval;
subseploc = setsymtab("SUBSEP", "\034", 0.0, STR|DONTFREE, symtab);
SUBSEP = &subseploc->sval;
rstartloc = setsymtab("RSTART", "", 0.0, NUM, symtab);
RSTART = &rstartloc->fval;
rlengthloc = setsymtab("RLENGTH", "", 0.0, NUM, symtab);
RLENGTH = &rlengthloc->fval;
symtabloc = setsymtab("SYMTAB", "", 0.0, ARR, symtab);
symtabloc->sval = (char *)symtab;
}
void
arginit(int ac, char **av) /* set up ARGV and ARGC */
{
Cell *cp;
int i;
char temp[50];
ARGC = &setsymtab("ARGC", "", (Awkfloat)ac, NUM, symtab)->fval;
cp = setsymtab("ARGV", "", 0.0, ARR, symtab);
ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
cp->sval = (char *)ARGVtab;
for (i = 0; i < ac; i++) {
(void) sprintf(temp, "%d", i);
if (is_number(*av)) {
(void) setsymtab(temp, *av, atof(*av),
STR|NUM, ARGVtab);
} else {
(void) setsymtab(temp, *av, 0.0, STR, ARGVtab);
}
av++;
}
}
void
envinit(char **envp) /* set up ENVIRON variable */
{
Cell *cp;
char *p;
cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);
ENVtab = makesymtab(NSYMTAB);
cp->sval = (char *)ENVtab;
for (; *envp; envp++) {
if ((p = strchr(*envp, '=')) == NULL)
continue;
if (p == *envp) /* no left hand side name in env string */
continue;
*p++ = 0; /* split into two strings at = */
if (is_number(p)) {
(void) setsymtab(*envp, p, atof(p),
STR|NUM, ENVtab);
} else {
(void) setsymtab(*envp, p, 0.0, STR, ENVtab);
}
/* restore in case env is passed down to a shell */
p[-1] = '=';
}
}
Array *
makesymtab(int n) /* make a new symbol table */
{
Array *ap;
Cell **tp;
ap = (Array *)malloc(sizeof (Array));
tp = (Cell **)calloc(n, sizeof (Cell *));
if (ap == NULL || tp == NULL)
FATAL("out of space in makesymtab");
ap->nelem = 0;
ap->size = n;
ap->tab = tp;
return (ap);
}
void
freesymtab(Cell *ap) /* free a symbol table */
{
Cell *cp, *temp;
Array *tp;
int i;
if (!isarr(ap))
return;
/*LINTED align*/
tp = (Array *)ap->sval;
if (tp == NULL)
return;
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp != NULL; cp = temp) {
xfree(cp->nval);
if (freeable(cp))
xfree(cp->sval);
temp = cp->cnext; /* avoids freeing then using */
free(cp);
tp->nelem--;
}
tp->tab[i] = 0;
}
if (tp->nelem != 0) {
WARNING("can't happen: inconsistent element count freeing %s",
ap->nval);
}
free(tp->tab);
free(tp);
}
void
freeelem(Cell *ap, const char *s) /* free elem s from ap (i.e., ap["s"] */
{
Array *tp;
Cell *p, *prev = NULL;
int h;
/*LINTED align*/
tp = (Array *)ap->sval;
h = hash(s, tp->size);
for (p = tp->tab[h]; p != NULL; prev = p, p = p->cnext)
if (strcmp(s, p->nval) == 0) {
if (prev == NULL) /* 1st one */
tp->tab[h] = p->cnext;
else /* middle somewhere */
prev->cnext = p->cnext;
if (freeable(p))
xfree(p->sval);
free(p->nval);
free(p);
tp->nelem--;
return;
}
}
Cell *
setsymtab(const char *n, const char *s, Awkfloat f, unsigned int t, Array *tp)
{
int h;
Cell *p;
if (n != NULL && (p = lookup(n, tp)) != NULL) {
dprintf(("setsymtab found %p: n=%s s=\"%s\" f=%g t=%o\n",
(void *)p, NN(p->nval), NN(p->sval), p->fval, p->tval));
return (p);
}
p = (Cell *)malloc(sizeof (Cell));
if (p == NULL)
FATAL("out of space for symbol table at %s", n);
p->nval = tostring(n);
p->sval = s ? tostring(s) : tostring("");
p->fval = f;
p->tval = t;
p->csub = CUNK;
p->ctype = OCELL;
tp->nelem++;
if (tp->nelem > FULLTAB * tp->size)
rehash(tp);
h = hash(n, tp->size);
p->cnext = tp->tab[h];
tp->tab[h] = p;
dprintf(("setsymtab set %p: n=%s s=\"%s\" f=%g t=%o\n",
(void *)p, p->nval, p->sval, p->fval, p->tval));
return (p);
}
int
hash(const char *s, int n) /* form hash value for string s */
{
unsigned int hashval;
for (hashval = 0; *s != '\0'; s++)
hashval = (*s + 31 * hashval);
return (hashval % n);
}
static void
rehash(Array *tp) /* rehash items in small table into big one */
{
int i, nh, nsz;
Cell *cp, *op, **np;
nsz = GROWTAB * tp->size;
np = (Cell **)calloc(nsz, sizeof (Cell *));
if (np == NULL) /* can't do it, but can keep running. */
return; /* someone else will run out later. */
for (i = 0; i < tp->size; i++) {
for (cp = tp->tab[i]; cp != NULL; cp = op) {
op = cp->cnext;
nh = hash(cp->nval, nsz);
cp->cnext = np[nh];
np[nh] = cp;
}
}
free(tp->tab);
tp->tab = np;
tp->size = nsz;
}
Cell *
lookup(const char *s, Array *tp) /* look for s in tp */
{
Cell *p;
int h;
h = hash(s, tp->size);
for (p = tp->tab[h]; p != NULL; p = p->cnext) {
if (strcmp(s, p->nval) == 0)
return (p); /* found it */
}
return (NULL); /* not found */
}
Awkfloat
setfval(Cell *vp, Awkfloat f) /* set float val of a Cell */
{
int fldno;
f += 0.0; /* normalise negative zero to positive zero */
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (isfld(vp)) {
donerec = 0; /* mark $0 invalid */
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
dprintf(("setting field %d to %g\n", fldno, f));
} else if (&vp->fval == NF) {
donerec = 0; /* mark $0 invalid */
setlastfld((int)f);
dprintf(("setting NF to %g\n", f));
} else if (isrec(vp)) {
donefld = 0; /* mark $1... invalid */
donerec = 1;
savefs();
} else if (vp == ofsloc) {
if (donerec == 0)
recbld();
}
if (freeable(vp))
xfree(vp->sval); /* free any previous string */
vp->tval &= ~(STR|CONVC|CONVO); /* mark string invalid */
vp->fmt = NULL;
vp->tval |= NUM; /* mark number ok */
if (f == -0) /* who would have thought this possible? */
f = 0;
dprintf(("setfval %p: %s = %g, t=%o\n", (void *)vp,
NN(vp->nval), f, vp->tval));
return (vp->fval = f);
}
void
funnyvar(Cell *vp, const char *rw)
{
if (isarr(vp))
FATAL("can't %s %s; it's an array name.", rw, vp->nval);
if (isfcn(vp))
FATAL("can't %s %s; it's a function.", rw, vp->nval);
WARNING("funny variable %p: n=%s s=\"%s\" f=%g t=%o",
vp, vp->nval, vp->sval, vp->fval, vp->tval);
}
char *
setsval(Cell *vp, const char *s) /* set string val of a Cell */
{
char *t;
int fldno;
Awkfloat f;
dprintf(("starting setsval %p: %s = \"%s\", t=%o, r,f=%d,%d\n",
(void *)vp, NN(vp->nval), s, vp->tval, donerec, donefld));
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "assign to");
if (isfld(vp)) {
donerec = 0; /* mark $0 invalid */
fldno = atoi(vp->nval);
if (fldno > *NF)
newfld(fldno);
dprintf(("setting field %d to %s (%p)\n", fldno, s, (void *)s));
} else if (isrec(vp)) {
donefld = 0; /* mark $1... invalid */
donerec = 1;
savefs();
} else if (vp == ofsloc) {
if (donerec == 0)
recbld();
}
t = s ? tostring(s) : tostring(""); /* in case it's self-assign */
if (freeable(vp))
xfree(vp->sval);
vp->tval &= ~(NUM|CONVC|CONVO);
vp->tval |= STR;
vp->fmt = NULL;
setfree(vp);
dprintf(("setsval %p: %s = \"%s (%p) \", t=%o r,f=%d,%d\n",
(void *)vp, NN(vp->nval), t, (void *)t,
vp->tval, donerec, donefld));
vp->sval = t;
if (&vp->fval == NF) {
donerec = 0; /* mark $0 invalid */
f = getfval(vp);
setlastfld((int)f);
dprintf(("setting NF to %g\n", f));
}
return (vp->sval);
}
Awkfloat
getfval(Cell *vp) /* get float val of a Cell */
{
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "read value of");
if (isfld(vp) && donefld == 0)
fldbld();
else if (isrec(vp) && donerec == 0)
recbld();
if (!isnum(vp)) { /* not a number */
vp->fval = atof(vp->sval); /* best guess */
if (is_number(vp->sval) && !(vp->tval&CON))
vp->tval |= NUM; /* make NUM only sparingly */
}
dprintf(("getfval %p: %s = %g, t=%o\n",
(void *)vp, NN(vp->nval), vp->fval, vp->tval));
return (vp->fval);
}
static char *
get_str_val(Cell *vp, char **fmt) /* get string val of a Cell */
{
char s[256];
double dtemp;
if ((vp->tval & (NUM | STR)) == 0)
funnyvar(vp, "read value of");
if (isfld(vp) && donefld == 0)
fldbld();
else if (isrec(vp) && donerec == 0)
recbld();
/*
* ADR: This is complicated and more fragile than is desirable.
* Retrieving a string value for a number associates the string
* value with the scalar. Previously, the string value was
* sticky, meaning if converted via OFMT that became the value
* (even though POSIX wants it to be via CONVFMT). Or if CONVFMT
* changed after a string value was retrieved, the original value
* was maintained and used. Also not per POSIX.
*
* We work around this design by adding two additional flags,
* CONVC and CONVO, indicating how the string value was
* obtained (via CONVFMT or OFMT) and _also_ maintaining a copy
* of the pointer to the xFMT format string used for the
* conversion. This pointer is only read, **never** dereferenced.
* The next time we do a conversion, if it's coming from the same
* xFMT as last time, and the pointer value is different, we
* know that the xFMT format string changed, and we need to
* redo the conversion. If it's the same, we don't have to.
*
* There are also several cases where we don't do a conversion,
* such as for a field (see the checks below).
*/
/* Don't duplicate the code for actually updating the value */
#define update_str_val(vp) \
{ \
if (freeable(vp)) \
xfree(vp->sval); \
if (modf(vp->fval, &dtemp) == 0) /* it's integral */ \
(void) snprintf(s, sizeof (s), "%.30g", vp->fval); \
else \
(void) snprintf(s, sizeof (s), *fmt, vp->fval); \
vp->sval = tostring(s); \
vp->tval &= ~DONTFREE; \
vp->tval |= STR; \
}
if (isstr(vp) == 0) {
/*LINTED*/
update_str_val(vp);
if (fmt == OFMT) {
vp->tval &= ~CONVC;
vp->tval |= CONVO;
} else {
/* CONVFMT */
vp->tval &= ~CONVO;
vp->tval |= CONVC;
}
vp->fmt = *fmt;
} else if ((vp->tval & DONTFREE) != 0 || !isnum(vp) || isfld(vp)) {
goto done;
} else if (isstr(vp)) {
if (fmt == OFMT) {
if ((vp->tval & CONVC) != 0 ||
((vp->tval & CONVO) != 0 && vp->fmt != *fmt)) {
/*LINTED*/
update_str_val(vp);
vp->tval &= ~CONVC;
vp->tval |= CONVO;
vp->fmt = *fmt;
}
} else {
/* CONVFMT */
if ((vp->tval & CONVO) != 0 ||
((vp->tval & CONVC) != 0 && vp->fmt != *fmt)) {
/*LINTED*/
update_str_val(vp);
vp->tval &= ~CONVO;
vp->tval |= CONVC;
vp->fmt = *fmt;
}
}
}
done:
dprintf(("getsval %p: %s = \"%s (%p)\", t=%o\n",
(void *)vp, NN(vp->nval), vp->sval, (void *)vp->sval, vp->tval));
return (vp->sval);
}
char *
getsval(Cell *vp) /* get string val of a Cell */
{
return (get_str_val(vp, CONVFMT));
}
char *
getpssval(Cell *vp) /* get string val of a Cell for print */
{
return (get_str_val(vp, OFMT));
}
char *
tostring(const char *s) /* make a copy of string s */
{
char *p = strdup(s);
if (p == NULL)
FATAL("out of space in tostring on %s", s);
return (p);
}
char *
qstring(const char *is, int delim) /* collect string up to next delim */
{
const char *os = is;
int c, n;
uschar *s = (uschar *)is;
uschar *buf, *bp;
if ((buf = (uschar *)malloc(strlen(is)+3)) == NULL)
FATAL("out of space in qstring(%s)", s);
for (bp = buf; (c = *s) != delim; s++) {
if (c == '\n') {
SYNTAX("newline in string %.20s...", os);
} else if (c != '\\')
*bp++ = c;
else { /* \something */
c = *++s;
if (c == 0) { /* \ at end */
*bp++ = '\\';
break; /* for loop */
}
switch (c) {
case '\\': *bp++ = '\\'; break;
case 'n': *bp++ = '\n'; break;
case 't': *bp++ = '\t'; break;
case 'b': *bp++ = '\b'; break;
case 'f': *bp++ = '\f'; break;
case 'r': *bp++ = '\r'; break;
default:
if (!isdigit(c)) {
*bp++ = c;
break;
}
n = c - '0';
if (isdigit(s[1])) {
n = 8 * n + *++s - '0';
if (isdigit(s[1]))
n = 8 * n + *++s - '0';
}
*bp++ = n;
break;
}
}
}
*bp++ = 0;
return ((char *)buf);
}