parse.c revision 3f54fd611f536639ec30dd53c48e5ec1897cc7d9
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 2003-2012 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* jcl step parser from the description in
*/
#include "jcllib.h"
#include <error.h>
#include <tm.h>
static char null_data[2];
/* vendor SS in the standard namespace -- great */
static char EQ[] = "=";
static char END[] = "\\\\";
static char SS[] = "//";
static char ST[] = "*";
/*
* common syntax error message
*/
static void
{
{
if (expected)
else if (token)
else
}
}
/*
* push a string on the input token stream
*/
static int
{
{
return -1;
}
return 0;
}
/*
* push back one token or entire card if tok==0
*/
static void
{
if (tok)
{
}
else
{
}
}
/*
* read the next input card
*/
static char*
{
register char* s;
size_t n;
for (;;)
{
{
break;
}
break;
{
error_info.line++;
{
if (s[n - 1] == '\r')
s[n - 1] = 0;
if (n > CARD)
s[CARD] = 0;
}
break;
}
break;
}
return s;
}
/*
* expand control-m variable s with leading %% stripped
* pointer to next char in s returned
*/
static char*
{
register char* t;
register char* v;
register int c;
int o;
if (*s == '.')
return s + 1;
tp = 0;
for (;;)
{
if (!(c = *s++) || c == '.')
{
s--;
break;
}
else if (c == '%' && *s == '%')
{
if (*++s == '.')
{
s++;
break;
}
if (!ID(*t))
break;
*t = 0;
}
else if (ID(c))
else
{
s--;
break;
}
}
else
{
else
}
if (tp)
{
sfstrclose(tp);
}
return s;
}
/*
* set *r to point to the next token in s
* pointer to next char in s returned
*/
static char*
{
char* t;
int o;
while (*s == ' ')
s++;
for (t = s; *s; s++)
if (*s == ' ')
{
*s++ = 0;
break;
}
else if (*s == '%' && *(s + 1) == '%')
{
if (set)
{
set = 0;
s++;
}
else
{
if (s - t)
if (*s != '.')
{
break;
}
t = s + 1;
}
}
*r = t;
return s;
}
/*
* set *p to point to the next number token value in s
* pointer to next char in s returned
*/
static char*
{
char* t;
char* b;
while (*s == ' ')
s++;
b = s;
*r = strtol(s, &t, 10);
return t;
}
#define NOOPERAND() do { \
if (p) \
{ \
} \
} while (0)
#define OPERAND() do { \
if (p) \
{ \
p = 0; \
} \
else \
{ \
t = ""; \
} \
} while (0)
/*
* expand control-m expression from s into sp
* pointer to next char in s returned
*/
static char*
{
char* b;
char* e;
char* f;
char* p;
char* t;
char* v;
int o;
int y;
long i;
long j;
time_t x;
p = 0;
for (;;)
{
while (*s == ' ')
s++;
if (!*s)
break;
if (*s == '%' && *(s + 1) == '%')
{
b = s += 2;
if (*s == '$')
{
s++;
y = 1;
}
else
y = 0;
for (f = s; ID(*s); s++);
if (*s)
*s++ = 0;
switch (*f)
{
case 'C':
{
NOOPERAND();
s = autonumber(jcl, s, &i);
goto done;
}
break;
case 'G':
if (streq(f, "GREG"))
{
NOOPERAND();
goto done;
}
break;
case 'J':
if (streq(f, "JULIAN"))
{
NOOPERAND();
goto done;
}
break;
case 'L':
if (streq(f, "LEAP"))
{
NOOPERAND();
goto done;
}
break;
case 'M':
if (streq(f, "MINUS"))
{
NOOPERAND();
autonumber(jcl, t, &i);
s = autonumber(jcl, s, &j);
goto done;
}
break;
case 'P':
if (streq(f, "PLUS"))
{
OPERAND();
autonumber(jcl, t, &i);
s = autonumber(jcl, s, &j);
goto done;
}
break;
case 'S':
if (streq(f, "SUBSTR"))
{
NOOPERAND();
s = autonumber(jcl, s, &i);
s = autonumber(jcl, s, &j);
while (--i > 0 && *t)
t++;
for (v = t; j-- > 0 && *v; v++);
goto done;
}
break;
case 'W':
if (streq(f, "WCALC"))
{
NOOPERAND();
goto notyet;
}
else if (streq(f, "WEEK#"))
{
NOOPERAND();
goto done;
}
else if (streq(f, "WEEKDAY"))
{
NOOPERAND();
goto done;
}
break;
}
if (p)
}
else
{
if (p)
}
}
done:
return s;
return s;
}
#define CMD_DD 2
#define CMD_EXEC 4
/*
* return the next token
*/
static char*
{
register char* s;
register char* t;
register char* p;
register int n;
register int a;
register int q;
register int x;
register int c;
register int m;
char* v;
char* w;
int j;
int e;
#if DEBUG
long line;
#endif
{
goto token;
}
{
{
{
s = END;
goto token;
}
}
{
s = END;
goto token;
}
else
{
a = 0;
e = 0;
j = 0;
n = 0;
q = 0;
x = 0;
for (;;)
{
{
return 0;
}
if (*s == '/' && (*(s + 1) == '*' || *(s + 1) == '/' && *(s + 2) == '*'))
{
if (*++s == '/')
s++;
if (*s == '*')
s++;
while (*s == ' ')
s++;
if (*s == '%' && *(s + 1) == '%')
{
for (t = s += 2; *s && *s != ' '; s++);
if (*s)
for (*s++ = 0; *s == ' '; s++);
{
if (*s == '=')
{
while (*++s == ' ');
t = 0;
}
}
}
if (x < 0)
break;
continue;
}
if (*s != '/' || *(s + 1) != '/')
{
return 0;
}
if (x)
{
if (*(s += 2))
{
if (!isspace(*s))
{
break;
}
while (isspace(*++s));
}
if (x < 1)
{
if (m)
continue;
break;
}
if (*s == 0)
{
x = 0;
continue;
}
}
p = 0;
for (;;)
{
switch (c = *s++)
{
case 0:
if (p)
{
s = p;
p = 0;
continue;
}
break;
case '=':
if (!q && *s == '\'' && *(s + 1) == '\'' && (*(s + 2) == ',' || isspace(*(s + 2)) || *(s + 2) == 0))
s += 2;
if (!q)
switch (j)
{
case CMD_DD:
*(s - 2) == 'S' && *(s - 3) == 'Y' && *(s - 4) == 'S' && *(s - 5) == 'B' && *(s - 6) == 'U' && *(s - 7) == 'T' && !ID(*(s - 8)))
e = 1;
break;
case CMD_EXEC:
e = 1;
break;
}
continue;
case '\'':
if (!p)
{
if (!q)
q = e ? -1 : 1;
else if (*s == '\'')
{
s++;
}
else
q = 0;
}
continue;
case '&':
if (p)
else if (*s == '&')
{
s++;
if (q > 0)
}
else if (!ID(*s))
else
{
for (t = s; ID(*s); s++);
c = *s;
*s = 0;
*s = c;
if (v)
{
if (c == '.')
s++;
p = s;
s = v;
}
else
for (t--; t < s; t++)
}
continue;
case '%':
if (p || *s != '%' || *(s + 1) == '#')
else
{
s = v;
}
continue;
default:
if (!q)
{
if (e && !ID(c))
e = 0;
if (isspace(c))
{
continue;
if (!n && ++a >= 3)
break;
/*
* { CMD_DD IF } don't follow the norm
*/
if (a == 2)
{
/*
* variable expansion must be delayed until the other
* procedure context is active
*/
if (*t == 'D' && *(t - 1) == 'D' && *(t - 2) == ' ')
{
j = CMD_DD;
p = null;
}
else if (*t == 'C' && *(t - 1) == 'E' && *(t - 2) == 'X' && *(t - 3) == 'E' && *(t - 4) == ' ')
j = CMD_EXEC;
else if (*t == 'F' && *(t - 1) == 'I' && *(t - 2) == ' ')
n = 1;
}
c = ' ';
}
}
continue;
}
break;
}
if (q)
x = 1;
else
{
if (a < 2 || s == t || *(s - 1) != ',')
{
if (!m)
break;
x = -1;
}
else
x = 1;
}
}
}
}
n = 0;
q = 0;
for (;;)
{
{
case 0:
if (q)
{
return 0;
}
if (n)
{
return 0;
}
goto token;
case '/':
{
{
{
goto again;
}
{
else
}
s = SS;
goto token;
}
{
goto again;
}
}
continue;
case '=':
{
if (*jcl->data == '\'' && *(jcl->data + 1) == '\'' && (*(jcl->data + 2) == ',' || isspace(*(jcl->data + 2)) || *(jcl->data + 2) == 0))
{
{
s = EQ;
goto token;
}
}
else
{
{
s = EQ;
goto token;
}
}
break;
}
continue;
case '*':
if (!n && !q && (jcl->data - s) == 1 && (*jcl->data == ',' || *jcl->data == ' ' || *jcl->data == 0))
{
s = ST;
goto token;
}
continue;
case '(':
if (!q)
n++;
continue;
case ')':
if (!q)
{
if (n > 0)
n--;
break;
}
continue;
case ',':
if (!n && !q)
{
break;
}
continue;
case ' ':
if (!n && !q)
break;
continue;
case '\'':
if (!q)
q = 1;
else
q = 0;
s++;
while (--t >= s)
*t = *(t - 1);
continue;
default:
continue;
}
break;
}
}
/*
* eat the remainder of the card and its continuations
*/
static void
{
register char* tok;
}
/*
* parse a COND [sub]expression
*/
static Jclcond_t*
{
register char* s;
register char* t;
register Jclcond_t* x;
register Jclcond_t* y;
char* e;
s = b;
y = 0;
for (;;)
{
if (*s == '(')
{
return 0;
s = e;
}
{
return 0;
}
else
{
if (*e == ',')
{
s = e + 2;
switch (*(e + 1))
{
case 'E':
switch (*s)
{
case 'Q':
x->op = JCL_COND_EQ;
break;
default:
goto bad;
}
break;
case 'G':
switch (*s)
{
case 'E':
x->op = JCL_COND_GE;
break;
case 'T':
x->op = JCL_COND_GT;
break;
default:
goto bad;
}
break;
case 'L':
switch (*s)
{
case 'E':
x->op = JCL_COND_LE;
break;
case 'T':
x->op = JCL_COND_LT;
break;
default:
goto bad;
}
break;
case 'N':
switch (*s)
{
case 'E':
x->op = JCL_COND_NE;
break;
default:
goto bad;
}
break;
default:
goto bad;
}
if (*++s == ',')
{
for (t = ++s; *s && *s != ',' && *s != ')'; s++);
{
return 0;
}
}
}
else
{
for (t = s; *s && *s != ',' && *s != ')'; s++);
if ((s - t) != 4)
goto bad;
x->op = JCL_COND_EVEN;
x->op = JCL_COND_ONLY;
else
goto bad;
}
}
if (y)
y->next = x;
else
y = x;
if (*s == 0)
break;
if (*s == ')')
{
s++;
break;
}
if (*s++ != ',')
goto bad;
}
if (p)
*p = s;
else if (*s)
goto bad;
return y;
bad:
return 0;
}
/*
* return next arg token
* *p points to optional =val
*/
static char*
{
char* tok;
char* val;
return 0;
else
{
val = 0;
}
*p = val;
return tok;
}
/*
* return next parm from single token or (...,...,...) list *p
* list modified in place and not restored
*/
char*
jclparm(char** p)
{
register char* s;
register char* t;
register int q;
register int n;
register int x;
int empty;
char* b;
s = *p;
if (*s == '(')
{
s++;
x = -1;
}
else
x = '\'';
q = 0;
n = 0;
for (b = t = s; *s; s++)
if (*s == x)
q = !q;
else if (!q)
{
if (*s == '(')
n++;
else if (*s == ')')
{
if (--n < 0)
break;
}
else if (!n && *s == ',')
break;
*t++ = *s;
}
if (*s)
{
empty = *s == ',';
*s++ = 0;
}
else
empty = 0;
*p = s;
if (*b == ' ')
{
s = b;
for (s = b; *++s == ' ';);
for (t = s; ID(*s); s++);
if (*s == '=')
b = t;
}
if (!*b && !empty)
return 0;
return b;
}
/*
* parse a DD statement
*/
static int
{
register char* tok;
register char* op;
char* val;
char* s;
int i;
int n;
int d0;
int d1;
int d2;
int change;
{
return -1;
}
change = 0;
if (*name)
{
{
change = 1;
}
{
return -1;
}
return -1;
{
}
{
}
{
}
{
}
}
{
return -1;
}
d0 = '/';
d1 = '/';
d2 = '*';
{
{
if (*op == '(')
op++;
op += 2;
{
for (;;)
{
if (*op == '*')
op++;
if (*op == '.')
op++;
if (n = *op)
*op = 0;
if (*val)
{
if (output)
{
{
return -1;
}
}
}
if (!n)
break;
*op++ = n;
if (n != ',')
break;
}
continue;
}
if (*op)
{
*op = 0;
{
return -1;
}
*op++ = '.';
}
if (*--op == ')')
*op = 0;
else
op = 0;
if (op)
*op = ')';
if (!pd)
{
continue;
}
}
else
pd = 0;
if (val && (streq(tok, "DSN") || streq(tok, "DSNAME")) || !val && (streq(tok, "DUMMY") && (val = dummy) && (dd->flags |= JCL_DD_DUMMY) || !jcl->pushed && tok != ST && (val = tok)))
{
{
}
else
{
if (pd)
return -1;
{
return -1;
}
else
{
else
}
}
}
else if (val)
{
{
{
{
}
{
}
#endif
}
}
{
if (pd)
{
}
return -1;
}
{
}
{
if (pd)
{
{
}
}
}
{
d0 = 0;
else
d2 = 0;
}
{
if (pd)
else
for (;;)
{
switch (*val++)
{
case 0:
break;
case 'A':
continue;
case 'B':
continue;
case 'D':
continue;
case 'F':
continue;
case 'M':
continue;
case 'S':
continue;
case 'U':
continue;
case 'V':
continue;
default:
continue;
}
break;
}
}
{
if (pd)
{
}
else
for (n = 0;;)
{
switch (*val++)
{
case 0:
break;
case '(':
n++;
i = 0;
continue;
case ',':
i++;
continue;
case ')':
if (n == 2)
{
if (i == 2)
break;
}
continue;
default:
continue;
}
break;
}
}
{
return -1;
}
}
{
{
break;
}
return -1;
if (tok)
}
}
return -1;
{
{
}
{
return -1;
}
}
{
}
return 0;
return 0;
}
/*
* parse an OUTPUT statement
*/
static int
{
char* s;
char* tok;
char* val;
if (!*name)
{
return -1;
}
{
}
{
return 0;
}
{
return 0;
}
return 0;
{
if (val)
}
return 0;
return 0;
}
/*
* return the next IF expression operand value
*/
static int
{
register char* t;
register char* u;
register char* v;
Rc_t* p;
int c;
int d;
int n;
int abend;
int run;
int rc;
while (*s == ' ')
s++;
if ((n = *s == '!') && *++s == ' ')
s++;
if (*s == '(')
{
return rc;
s = *e;
while (*s == ' ')
s++;
if (*s++ != ')')
{
return -1;
}
}
else if (isdigit(*s))
{
s = *e;
}
else if (*s == 0 || *s == ')')
rc = 0;
else
{
v = 0;
for (t = s; ID(*s) || *s == '.' && (v = s); s++);
if (v)
{
*v = 0;
*v = '.';
if (p)
{
run = 1;
if (p->rc < 0)
{
abend = 1;
rc = 0;
}
else
{
abend = 0;
}
}
else
{
abend = 0;
rc = 0;
run = 0;
}
t = v + 1;
}
else
{
run = 1;
}
c = *s;
*s = 0;
if (!*t)
/*ok*/;
else if (streq(t, "ABEND"))
else if (streq(t, "ABENDCC"))
rc = 0;
else if (streq(t, "FALSE"))
rc = 0;
else if (streq(t, "RUN"))
else if (streq(t, "TRUE"))
rc = 1;
/*OK*/;
/*OK*/;
else if (!streq(t, "RC"))
{
if (c)
{
u = s;
while (*++u == ' ');
if (*u == '=' || *u == '!' && *(u + 1) == '=')
{
if (n = *u == '!')
u++;
while (*++u == ' ');
v = u;
while (*++u && *u != ' ');
d = *u;
*u = 0;
*u = d;
*s = c;
s = u;
goto done;
}
}
return -1;
}
*s = c;
}
done:
if (n)
while (*s == ' ')
s++;
*e = s;
return rc;
}
/*
* evaluate an IF [sub]expression
*/
static int
{
register char* t;
int a;
int op;
int b;
#if DEBUG
int o;
static const char* opname[] = { "NOP", "ONLY", "EVEN", "LT", "LE", "EQ", "NE", "GE", "GT", "OR", "AND" };
#endif
return -1;
s = *e;
for (;;)
{
if (*s == 0 || *s == ')')
break;
op = 0;
t = s;
switch (*s++)
{
case 'A':
if (*s++ == 'N' && *s++ == 'D')
op = JCL_COND_AND;
break;
case 'E':
if (*s++ == 'Q')
op = JCL_COND_EQ;
break;
case 'G':
switch (*s)
{
case 'E':
op = JCL_COND_GE;
s++;
break;
case 'T':
op = JCL_COND_GT;
s++;
break;
}
break;
case 'L':
switch (*s)
{
case 'E':
op = JCL_COND_LE;
s++;
break;
case 'T':
op = JCL_COND_LT;
s++;
break;
}
break;
case 'N':
if (*s++ == 'E')
op = JCL_COND_NE;
break;
case 'O':
if (*s++ == 'R')
op = JCL_COND_OR;
break;
case '&':
op = JCL_COND_AND;
break;
case '=':
op = JCL_COND_EQ;
break;
case '>':
if (*s == '=')
{
op = JCL_COND_GE;
s++;
}
else
op = JCL_COND_GT;
break;
case '<':
if (*s == '=')
{
op = JCL_COND_LE;
s++;
}
else
op = JCL_COND_LT;
break;
case '!':
if (*s++ == '=')
op = JCL_COND_NE;
break;
case '|':
op = JCL_COND_OR;
break;
}
{
return -1;
}
{
s = t;
break;
}
return -1;
s = *e;
o = a;
switch (op)
{
case JCL_COND_AND:
a = a && b;
break;
case JCL_COND_OR:
a = a || b;
break;
case JCL_COND_LT:
a = a < b;
break;
case JCL_COND_LE:
a = a <= b;
break;
case JCL_COND_EQ:
a = a == b;
break;
case JCL_COND_NE:
a = a != b;
break;
case JCL_COND_GE:
a = a >= b;
break;
case JCL_COND_GT:
a = a > b;
break;
}
}
while (*s == ' ')
s++;
*e = s;
return a;
}
/*
* parse and evaluate an IF expression
* return:
* <0 fatal error
* 0 false
* >0 true
*/
static int
{
register char* s;
char* e;
int r;
if (s == END)
{
return -1;
}
else if (streq(s, "THEN"))
break;
else
return r;
if (*e)
{
return -1;
}
return r;
}
/*
* main parse loop
*/
static int
{
register char* tok;
register char* name;
register char* op;
char* val;
char* t;
char* v;
int i;
{
{
return -1;
}
{
return -1;
}
{
return -1;
}
if (!*op)
/*NOP*/;
{
return -1;
}
{
{
break;
}
{
return -1;
}
else
}
{
{
return -1;
}
{
break;
}
}
{
{
break;
}
return -1;
{
if (val && (streq(tok, "PGM") && (step->flags |= JCL_PGM) || streq(tok, "PROC")) || !val && !step->command && (val = tok))
{
return -1;
}
else if (val)
{
{
return -1;
}
{
return -1;
}
return -1;
}
}
}
{
{
break;
}
{
{
return -1;
}
else
}
return -1;
ie->flags = ie->prev && (ie->prev->flags & (IE_KEEP|IE_SKIP)) != IE_KEEP ? IE_SKIP : i ? IE_KEEP : 0;
}
{
if (val && streq(tok, "MEMBER") && (!(tok = jclfind(jcl, val, JCL_PROC, 2, &sp)) || jclpush(jcl, sp, tok, 0)))
return -1;
}
{
return -1;
}
{
return -1;
if (val)
{
{
return -1;
}
return -1;
}
}
{
return -1;
}
{
/*HERE check for PROC on line 1, check that this is last? */
}
{
{
{
{
break;
}
}
return -1;
}
else
{
return -1;
if (val)
{
{
}
return -1;
}
}
}
else
}
return 0;
}
/*
* return the next jcl job step
*/
{
char* ofile;
int oline;
int i;
errno = 0;
for (;;)
{
{
}
break;
break;
continue;
{
{
return 0;
}
error_info.line = 0;
if (i)
return 0;
}
{
{
}
}
return step;
}
return 0;
}
/*
* return >0 if condition is true
*/
int
{
if (!cond)
return !code;
while (cond)
{
{
case JCL_COND_ONLY:
return 0;
break;
case JCL_COND_LT:
return 0;
break;
case JCL_COND_LE:
return 0;
break;
case JCL_COND_EQ:
return 0;
break;
case JCL_COND_NE:
return 0;
break;
case JCL_COND_GE:
return 0;
break;
case JCL_COND_GT:
return 0;
break;
}
}
return 1;
}
/*
* set step return code
* rc<0 for abend
*/
int
{
/*
* map unix signal codes to abend
*/
rc -= 128;
rc -= 256;
{
{
{
return -1;
}
}
if (!jcl)
}
if (jcl)
{
if (rc < 0)
}
return 0;
}