[comp.sources.misc] v06i036: JCL emulator

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (02/04/89)

Posting-number: Volume 6, Issue 36
Submitted-by: lupton@uhccux.uhcc.Hawaii.Edu (Robert Lupton)
Archive-name: jcl

[You always knew someone would do this...!  ;-)  ++bsa]

I wrote this a while ago, and have just found it on an old tape, so
here it is. It emulates our favourite operating system, JCL. To use
it, unpack the shar file, run make, then "JCL < deck" as a demo.

As it says in the README, I bequeath this code to the net. In particular
I don't intend to handle any bugfixes/improvements.

			Robert Lupton

: =-=-=-=-=-=-=-=-=-=-= Cut Here =-=-=-=-=-=-=-=-=-=-=
PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH
export PATH
echo Extracting CC
if [ -w CC ]; then
	echo File already exists - saving as CC.old
	mv CC CC.old
	chmod 444 CC.old
fi
sed 's/^X//' <<'//go.sysin dd *' >CC
mv $SYSIN $SYSIN.c
cc -c $* $SYSIN.c
mv $SYSIN.o $SYSOUT
mv $SYSIN.c $SYSIN
//go.sysin dd *
if [ `wc -c < CC` != 76 ]; then
made=FALSE
echo error transmitting CC --
echo length should be 76, not `wc -c < CC`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 755 CC
	echo -n  ; ls -ld CC
fi
echo Extracting LKED
if [ -w LKED ]; then
	echo File already exists - saving as LKED.old
	mv LKED LKED.old
	chmod 444 LKED.old
fi
sed 's/^X//' <<'//go.sysin dd *' >LKED
mv $SYSIN $SYSIN.o
cc $SYSIN.o $*
mv a.out $SYSOUT
mv $SYSIN.o $SYSIN
//go.sysin dd *
if [ `wc -c < LKED` != 70 ]; then
made=FALSE
echo error transmitting LKED --
echo length should be 70, not `wc -c < LKED`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 755 LKED
	echo -n  ; ls -ld LKED
fi
echo Extracting Makefile
if [ -w Makefile ]; then
	echo File already exists - saving as Makefile.old
	mv Makefile Makefile.old
	chmod 444 Makefile.old
fi
sed 's/^X//' <<'//go.sysin dd *' >Makefile
#
# Default rules for compiling code.
#
.c.o :
	cc $(CFLAGS) $*.c
#
CFLAGS = -c -g
#
# linker flags
#
LDFLAGS = -lg -lm
#
# define macros for the source files
#
SOURCE = \
#
OBJECT = \
	parser.o data_defs.o keyword.o main.o yylex.o
#
# cmp is used in issame (needed by VMS)
#
#cmp : cmp.o
#	cc cmp.o -o cmp
#
# Make JCL
#
JCL : $(OBJECT)
	cc -o JCL $(OBJECT) $(LDFLAGS)
#
# make parser.c from parser.y
# issame sees if keyword.h is changed. if not, don't update it
#
parser.c : parser.y
	@- echo Expect 4 unreduced rules
	yacc -d parser.y
	@ mv y.tab.c parser.c
	@ csh -f issame y.tab.h keyword.h
#
# make keyword.c from the list of tokens in keyword.h
#
make_keyword : make_keyword.o
	  cc make_keyword.o -o make_keyword
#
keyword.c : keyword.h make_keyword
	make_keyword keyword.h
#
clean :
	- rm *.o JCL cmp make_keyword parser.c keyword.h keyword.c \
	y.output *~ core
#
# Here are all the dependencies:
#
keyword.o :		keyword.h
data_defs.o :		jcl.h
main.o :		yaccun.h
parser.o :		yaccun.h jcl.h
yylex.o :		yaccun.h keyword.h jcl.h
//go.sysin dd *
if [ `wc -c < Makefile` != 1036 ]; then
made=FALSE
echo error transmitting Makefile --
echo length should be 1036, not `wc -c < Makefile`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 Makefile
	echo -n  ; ls -ld Makefile
fi
echo Extracting README
if [ -w README ]; then
	echo File already exists - saving as README.old
	mv README README.old
	chmod 444 README.old
fi
sed 's/^X//' <<'//go.sysin dd *' >README
   This is something I wrote a few years ago in a fit of nostalgia, but
   never really quite got finished. It was intended to run as a login 
   shell on my new shiny sun, to discourage other users from taking my
   CPU cycles.

   It is an emulator for for favourite operating system, JCL on an IBM 360.
   It works as far as it goes, but I never got around to implementing 
   libraries (I was going to use "ar" via system() calls). The grammar 
   is a bit of a hack, with the lex analysis doing a good deal of the work.

   To try it out, type "JCL < deck" after running make. I don't really want
   to deal with any enhancements or bug fixes (although I'd be happy to
   see the code improved). I therefore bequeath this code to the net, with
   no strings attached. On the other hand, I doubt if there is much money
   to be made out of it.

   One thing that I intended to do, but never did, was to make JCL accept
   input produced by "bcd" (i.e. real card images). I have a programme 
   somewhere that's like bcd but writes paper tapes, if there were enough
   interest I could post it.

			
				Robert Lupton
//go.sysin dd *
if [ `wc -c < README` != 1121 ]; then
made=FALSE
echo error transmitting README --
echo length should be 1121, not `wc -c < README`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 README
	echo -n  ; ls -ld README
fi
echo Extracting cmp.c
if [ -w cmp.c ]; then
	echo File already exists - saving as cmp.c.old
	mv cmp.c cmp.c.old
	chmod 444 cmp.c.old
fi
sed 's/^X//' <<'//go.sysin dd *' >cmp.c
X/*
 * This programme returns 1 (true) if the two files given as
 * arguments are identical, otherwise it returns 2 (false)
 */
#include <stdio.h>


main(ac,av)
int ac;
char *av[];
{
   char c1,c2;				/* characters read from files */
   int fil1,fil2;			/* fd's for two files */

   if(ac < 3 || (fil1 = open(av[1],0)) < 0 || (fil2 = open(av[2],0)) < 0) {
      exit(2);
   }

   while(1) {
      if(read(fil1,&c1,1) == 1) {
         if(read(fil2,&c2,1) == 1) {
            if(c1 != c2) exit(2);
            else ;
         } else {
            exit(2);
         }
      } else if(read(fil2,&c2,1) == 1) {
         exit(2);
      } else {
         exit(1);
      }
   }
}
//go.sysin dd *
if [ `wc -c < cmp.c` != 671 ]; then
made=FALSE
echo error transmitting cmp.c --
echo length should be 671, not `wc -c < cmp.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 755 cmp.c
	echo -n  ; ls -ld cmp.c
fi
echo Extracting data_defs.c
if [ -w data_defs.c ]; then
	echo File already exists - saving as data_defs.c.old
	mv data_defs.c data_defs.c.old
	chmod 444 data_defs.c.old
fi
sed 's/^X//' <<'//go.sysin dd *' >data_defs.c
X/*
 * Maintain information about DD statements
 */
#include <stdio.h>
#include "jcl.h"

#define NDD 20				/* Max. number of current DD sets */
#define SIZE 80				/* length of character strings */

typedef struct {
   char name[SIZE],			/* name of DD set */
	file[SIZE],			/* name of associated file */
	step[SIZE];			/* name of step */
   int disp[3];				/* DISP modes */
} DD_SET;

static DD_SET dds[NDD];			/* the available DDs */

init_dd()
{
   int i;

   for(i = 0;i < NDD;i++) {
      dds[i].name[0] = '\0';
   }
}

create_dd(name,file,step,disp)
char *name,
     *file,
     *step;
int disp[];
{
   char msg[40];
   int i;

   for(i = 0;i < NDD;i++) {
      if(!strcmp(name,dds[i].name)) {
	 sprintf(msg,"DD set %s already exists",name);
	 yyerror(msg);
	 break;
      } else if(dds[i].name[0] == '\0') {
	 break;
      }
   }
   if(i == NDD) {
      yyerror("Too many DD sets");
      return(-1);
   }

   strcpy(dds[i].name,name);
   strcpy(dds[i].file,file);
   strcpy(dds[i].step,step);

   if(disp[0] != UNKNOWN) {
      dds[i].disp[0] = disp[0];
   } else {
      if(access(file,0) == -1) {		/* file dosn't exist */
	 dds[i].disp[0] = NEW;
      } else {
	 dds[i].disp[0] = OLD;
      }
   }

   if(disp[1] != UNKNOWN) {
      dds[i].disp[1] = disp[1];
   } else {
      if(dds[i].disp[0] == NEW) {
	 dds[i].disp[1] = DELETE;
      } else {
	 dds[i].disp[1] = KEEP;
      }
   }

   if(disp[2] != UNKNOWN) {
      dds[i].disp[2] = disp[2];
   } else {
      if(dds[i].disp[0] == NEW) {
	 dds[i].disp[2] = DELETE;
      } else {
	 dds[i].disp[2] = KEEP;
      }
   }
}

X/******************************************************/
X/*
 * Cleanup after a job step
 */
step_clean_dd(step)
char *step;				/* name of step */
{
   int i;

   for(i = 0;i < NDD;i++) {
      if(dds[i].name[0] != '\0' && !strcmp(step,dds[i].step)) {
	 if(dds[i].disp[1] == DELETE) {
	    unlink(dds[i].file);
	 }
      }
   }
}

X/******************************************************/
X/*
 * Cleanup after job terminates
 */
job_clean_dd()
{
   int i;

   for(i = 0;i < NDD;i++) {
      if(dds[i].name[0] != '\0') {
	 if(dds[i].disp[2] == DELETE) {
	    unlink(dds[i].file);
	 }
      }
   }
}

X/******************************************************/
X/*
 * Return a string defining all units used in a job step
 */
char *
define_dd(step)
char *step;				/* name of step */
{
   static char string[200],
	       *sptr;
   int i;

   sptr = string;
   for(i = 0;i < NDD;i++) {
      if(dds[i].name[0] != '\0' && !strcmp(step,dds[i].step)) {
	 sprintf(sptr,"%s=%s;export %s;",dds[i].name,dds[i].file,dds[i].name);
	 sptr += 10 + 2*strlen(dds[i].name) + strlen(dds[i].file);
      }
   }
   return(string);
}

//go.sysin dd *
if [ `wc -c < data_defs.c` != 2689 ]; then
made=FALSE
echo error transmitting data_defs.c --
echo length should be 2689, not `wc -c < data_defs.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 data_defs.c
	echo -n  ; ls -ld data_defs.c
fi
echo Extracting deck
if [ -w deck ]; then
	echo File already exists - saving as deck.old
	mv deck deck.old
	chmod 444 deck.old
fi
sed 's/^X//' <<'//go.sysin dd *' >deck
X//NAME JOB ROBERT.H.LUPTON,MSGLEVEL=(2,2),         comments       78901   
X//         MSGCLASS=A
X//COMP EXEC PGM=CC,PARM='-g'
X//SYSOUT DD DSN=TEMP,DISP=KEEP
X//SYSIN DD *
#include <stdio.h>
main()
{
   printf("Hello World\n");
}
X/*
X//LKED EXEC PGM=LKED,PARM='-lc'
X//SYSIN DD DSN=TEMP,DISP=(,KEEP,DELETE)
X//SYSOUT DD DSN=TST,DISP=(,KEEP,DELETE)
X//GO EXEC PGM=TST
X//                          
//go.sysin dd *
if [ `wc -c < deck` != 390 ]; then
made=FALSE
echo error transmitting deck --
echo length should be 390, not `wc -c < deck`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 deck
	echo -n  ; ls -ld deck
fi
echo Extracting issame
if [ -w issame ]; then
	echo File already exists - saving as issame.old
	mv issame issame.old
	chmod 444 issame.old
fi
sed 's/^X//' <<'//go.sysin dd *' >issame
# /bin/csh -f
#
# $1 contains a new version of $2
# this programme compares the two files $1 and $2
# if they are different, then replace $1 by $2
# The programme is called by make
#
if({ cmp -s $1 $2 })then	# The same
	/bin/rm $1
else				# different
	/bin/mv $1 $2
endif
//go.sysin dd *
if [ `wc -c < issame` != 272 ]; then
made=FALSE
echo error transmitting issame --
echo length should be 272, not `wc -c < issame`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 755 issame
	echo -n  ; ls -ld issame
fi
echo Extracting jcl.h
if [ -w jcl.h ]; then
	echo File already exists - saving as jcl.h.old
	mv jcl.h jcl.h.old
	chmod 444 jcl.h.old
fi
sed 's/^X//' <<'//go.sysin dd *' >jcl.h
X/*
 * Parameters defined for the `scheduler'
 */
#define PSIZE 60			/* size of parm */
#define UNKNOWN 0			/* DISP parameters */
#define DELETE 1
#define KEEP 2
#define NEW 3
#define OLD 4

extern char parm[];			/* parameters for EXEC */
extern FILE *msgout;			/* output from `scheduler' */
extern int msglevel1,			/* MSGLEVEL=(msglevel1,msglevel2) */
	   msglevel2;
//go.sysin dd *
if [ `wc -c < jcl.h` != 367 ]; then
made=FALSE
echo error transmitting jcl.h --
echo length should be 367, not `wc -c < jcl.h`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 jcl.h
	echo -n  ; ls -ld jcl.h
fi
echo Extracting main.c
if [ -w main.c ]; then
	echo File already exists - saving as main.c.old
	mv main.c main.c.old
	chmod 444 main.c.old
fi
sed 's/^X//' <<'//go.sysin dd *' >main.c
#include <stdio.h>
#include "yaccun.h"

YYSTYPE yylval,yyval;
int verbose = 0;

main(ac,av)
int ac;
char *av[];
{
   if(ac > 1) {
      sscanf(av[1],"-v=%d",&verbose);
   }

   while(yyparse());
}
//go.sysin dd *
if [ `wc -c < main.c` != 197 ]; then
made=FALSE
echo error transmitting main.c --
echo length should be 197, not `wc -c < main.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 main.c
	echo -n  ; ls -ld main.c
fi
echo Extracting make_keyword.c
if [ -w make_keyword.c ]; then
	echo File already exists - saving as make_keyword.c.old
	mv make_keyword.c make_keyword.c.old
	chmod 444 make_keyword.c.old
fi
sed 's/^X//' <<'//go.sysin dd *' >make_keyword.c
X/*
 * Syntax: av[0] inc_file
 *
 * This programme uses the file inc_file to construct a second stage lex
 * analyser called keyword() in file keyword.c.
 */
#include <stdio.h>
#include <ctype.h>
#define NTOKEN 200		/* maximum number of tokens */
#define OUTFILE "keyword.c"	/* name of output file */
#define POUT fprintf(outfil	/* save space */
#define SIZE 40			/* maximum size of token */

static char token[NTOKEN][SIZE];

main(ac,av)
int ac;
char *av[];
{
   char  c;
   FILE  *infil,			/* file descriptor for *.h */
	 *outfil;
   int   i,
	 num_token;                    /* number of tokens */
   extern int *strcmp();

   if(ac < 2) {
      fprintf(stderr,"Syntax: make_keyword inc_file\n");
      exit(-2);
   }
   if((infil = fopen(av[1],"r")) == NULL) {
      fprintf(stderr,"Can't open %s\n",av[1]);
      exit(-2);
   }

   if((outfil = fopen(OUTFILE,"w")) == NULL) {
      fprintf(stderr,"Can't open %s\n",OUTFILE);
      fclose(infil);
      exit(-2);
   }
X/*
 * Read in the tokens from av[1].
 * Use the val_tok field to give their value in each file
 */
   for(i = 0;i < NTOKEN;i++) {
      if(fscanf(infil,"%*s %*s %s %*d",token[i]) != 1) {
	 break;
      }
   }
   fclose(infil);
   num_token = i;

   qsort(token,num_token,SIZE,strcmp);       /* sort tokens */

   POUT,"/*\n");
   POUT," */\n");
   POUT,"#include <stdio.h>\n");
   POUT,"#include \"%s\"\n",av[1]);
   POUT,"\n");
   POUT,"extern int strcmp();\n");
   POUT,"\n");
   POUT,"keyword(word)\n");
   POUT,"char word[];		/* word to look for */\n");
   POUT,"{\n");
   POUT,"\n");
   POUT,"  switch (word[0]) {\n");
   for(i = 0,c = 'A';c <= 'Z' && i < num_token;c++) {    /* assumes ascii */
      POUT,"  case '%c' :\n",c);
      while(token[i][0] <= c && i < num_token) {
	 POUT,"      if(!strcmp(word,\"%s\")) {\n",token[i]);
	 POUT,"         return(%s);\n",token[i]);
	 POUT,"      } else\n");
	 i++;
      }
      POUT,"         break;\n");
   }

   POUT,"   default : break;\n");
   POUT,"   }\n");
   POUT,"   return(WORD);\n");
   POUT,"\n");
   POUT,"}\n");

   fclose(outfil);
#ifdef unix
   exit(0);			/* success */
#else
   exit(1);			/* success in vmsese */
#endif UniX
}
//go.sysin dd *
if [ `wc -c < make_keyword.c` != 2164 ]; then
made=FALSE
echo error transmitting make_keyword.c --
echo length should be 2164, not `wc -c < make_keyword.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 755 make_keyword.c
	echo -n  ; ls -ld make_keyword.c
fi
echo Extracting parser.y
if [ -w parser.y ]; then
	echo File already exists - saving as parser.y.old
	mv parser.y parser.y.old
	chmod 444 parser.y.old
fi
sed 's/^X//' <<'//go.sysin dd *' >parser.y
%{
#include <stdio.h>
#include "yaccun.h"
#include "jcl.h"
X/*
 * declare variables in jcl.h
 */
char parm[PSIZE];			/* parameters on EXEC card */
FILE *msgout=stderr;			/* destination for output */
int disp[3],				/* 3 DISP parameters */
    msglevel1 = 1,msglevel2 = 1;	/* MSGLEVEL=(msglevel1,msglevel2) */

static char msg[50],			/* used for composing error messages */
	    step_name[40];		/* name of current step */
extern char token[];			/* text of last token read */
int pgm;				/* true if current step is a PGM */
extern int verbose;
%}

%start deck				/* the complete deck of cards */

%token <charval>
      EXEC WORD

%token					/* special characters */
      '\n' ',' '.' '/' '\'' '(' ')' '*' '&' '+' '-' '=' ' '
%token					/* control words */
      DD DSLASH ENDMARK JOB NULL_CARD SLASHSTAR
%token					/* keywords */
      COND					/* Misc. */
      MSGCLASS MSGLEVEL				/* JOB */
      PGM PROC PARM				/* EXEC */
      DATA DCB DISP DSN DUMMY UNIT		/* DD */

%type <charval>
      apost_word			/* string enclosed in ' ' */
      dd_file				/* name of file for DD statement */
      dot_name				/* name including '.'s */
      exec_card				/* PGM/PROC run by exec step */
      opt_dot_name			/* optional dot_name */
      opt_word				/* optional word */
      pgm_or_proc			/* name of PGM/PROC */

%type <intval>
      disp_par				/* DISP parameter */
      opt_disp				/* optional disp_par */
%%					/* start of rules */

deck  : job_card steps null_card
      {
	 job_clean_dd();		/* delete unwanted datasets */
	 return(0);
      }
      ;

steps : 				/* a collection of job steps */
      | steps step
      ;

step  : exec_card opt_dd_cards
      {
	 char command[200],
	      *define_dd();

	 if(pgm) {
	    sprintf(command,"%s%s %s",define_dd(step_name),$1,parm);
	    if(verbose) printf("%s\n",command);
	    system(command);
	 } else {
	    printf("Step %s runs proc %s\n",step_name,$1);
	    printf("Parameters: %s\n",parm);
	 }
	 step_clean_dd(step_name);
      }
      | error '\n'
      {
         yyerrok;
         yyclearin;
      }
      ;

opt_dd_cards :
      | opt_dd_cards dd_card
      ;

dd_card : DSLASH opt_dot_name ' ' DD ' ' dd_file '\n'
      { create_dd($2,$6,step_name,disp); }
      ;

dd_file	: '*'
      {
	 char line[81],
	      *mktemp(),
	      *tempname;
	 FILE *fil;

	 if((tempname = mktemp("DATXXXXXX")) == NULL) {
	    yyerror("Can't create temporary file name");
	    break;
	 }
	 if((fil = fopen(tempname,"w")) == NULL) {
	    sprintf(msg,"Can't open %s",tempname);
	    break;
	 }
	 strcpy($$,tempname);

	 while(fgets(line,81,stdin) != NULL) {
	    if(!strncmp("/*\n",line,3) || !strncmp("/* ",line,3)) {
	       break;
	    }
	    fputs(line,fil);
	 }
	 fclose(fil);

	 disp[0] = NEW;
	 disp[1] = DELETE;
	 disp[2] = DELETE;
      }
      | DUMMY dd_opts
	 { strcpy($$,"/dev/null"); }
      | DSN '=' dot_name dd_opts
	 { strcpy($$,$3); }
      ;

dd_opts	:
      {
	 disp[0] = UNKNOWN;
	 disp[1] = UNKNOWN;
	 disp[2] = UNKNOWN;
      }
      | dd_opts ',' DCB '=' WORD
      | dd_opts ',' DISP '=' disp_par
      {
	 disp[0] = $5;
	 disp[1] = UNKNOWN;
	 disp[2] = UNKNOWN;
      }
      | dd_opts ',' DISP '=' '(' opt_disp ',' opt_disp ')'
      {
	 disp[0] = $6;
	 disp[1] = $8;
	 disp[2] = UNKNOWN;
      }
      | dd_opts ',' DISP '=' '(' opt_disp ',' opt_disp ',' opt_disp ')'
      {
	 disp[0] = $6;
	 disp[1] = $8;
	 disp[2] = $10;
      }
      ;

disp_par : WORD
      {
	 if(!strcmp($1,"DELETE")) $$ = DELETE;
	 else if(!strcmp($1,"KEEP")) $$ = KEEP;
	 else if(!strcmp($1,"NEW")) $$ = NEW;
	 else if(!strcmp($1,"OLD")) $$ = OLD;
	 else {
	    sprintf(msg,"Unknown DISP parameter %s",$1);
	    $$ = UNKNOWN;
	 }
      }
      ;

opt_disp :
      { $$ = UNKNOWN; }
      | disp_par
      { $$ = $1; }
      ;

exec_card : EXEC ' ' pgm_or_proc exec_opts '\n'
      {
	 strcpy($$,$3);
	 strcpy(step_name,$1);
      }
      ;

exec_opts :
      | exec_opts ',' COND '=' '(' WORD ',' WORD ',' WORD ')'
      | exec_opts ',' PARM '=' apost_word
	 { strcpy(parm,$5); }
      | exec_opts ',' PARM '=' WORD
	 { strcpy(parm,$5); }
      ;

apost_word : '\'' /* [^']' */
      {
	 int i;

	 for(i = 0;i < CHARMAX
		&& ($$[i] = get_cchar()) != '\'' && $$[i] != '\n';i++) ;
	 if($$[i] != '\'') put_cchar($$[i]);
	 $$[i] = '\0';
      }
      ;

pgm_or_proc : PGM '=' WORD
      {
	 pgm = 1;			/* it's a programme */
	 strcpy($$,$3);
      }
      | PGM '=' apost_word
      {
	 pgm = 1;			/* it's a programme */
	 strcpy($$,$3);
      }
      | PROC '=' WORD
      {
	 pgm = 0;			/* it's a procedure */
	 strcpy($$,$3);
      }
      ;

job_card : DSLASH opt_word ' ' JOB ' ' dot_name job_opts '\n'
      ;

job_opts :
      | job_opts ',' MSGCLASS '=' WORD
      {
         if($5[0] == 'A') {
            if(msgout != stderr) fclose(msgout);
            msgout = stderr;
         } else if($5[0] == 'B') {
            if(msgout != stderr) fclose(msgout);
            if((msgout = fopen("JCL.out","w")) == NULL) {
	       yyerror("Can't open JCL.out");
	       msgout = stderr;
            }
         } else {
            sprintf(msg,"Unknown MSGCLASS %s",$5);
            yyerror(msg);
         }
      }
      | job_opts ',' MSGLEVEL '=' '(' WORD ',' WORD ')'
      {
         msglevel1 = atoi($6);
         msglevel2 = atoi($8);
      }
      ;

dot_name : WORD
         { strcpy($$,$1); }
      | dot_name '.' WORD
         { sprintf($$,"%s.%s",$1,$3); }
      ;

null_card : NULL_CARD
      | ENDMARK
      ;

opt_word : WORD
         { sprintf($$,$1); }
      |
         { sprintf($$,""); }
      ;

opt_dot_name : dot_name
         { sprintf($$,$1); }
      |
         { sprintf($$,""); }
      ;

opt_lparen :
      | '('
      ;

opt_rparen :
      | ')'
      ;
%%
yyerror(s)
char *s;
{
   if(!strcmp(s,"syntax error")) {
      fprintf(stderr,"Syntax error, last token read %s\n",token);
   } else {
      fprintf(stderr,"   %s\n",s);
   }
}
//go.sysin dd *
if [ `wc -c < parser.y` != 5908 ]; then
made=FALSE
echo error transmitting parser.y --
echo length should be 5908, not `wc -c < parser.y`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 parser.y
	echo -n  ; ls -ld parser.y
fi
echo Extracting yaccun.h
if [ -w yaccun.h ]; then
	echo File already exists - saving as yaccun.h.old
	mv yaccun.h yaccun.h.old
	chmod 444 yaccun.h.old
fi
sed 's/^X//' <<'//go.sysin dd *' >yaccun.h
X/*
 * these are the typedefs for the yacc union 
 */
#define CHARMAX 80			/* maximum size of word */

typedef union {				/* union for yacc variable stack */
   char charval[CHARMAX];
   int intval;
} YYSTYPE;

extern YYSTYPE yyval,yylval;
//go.sysin dd *
if [ `wc -c < yaccun.h` != 239 ]; then
made=FALSE
echo error transmitting yaccun.h --
echo length should be 239, not `wc -c < yaccun.h`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 755 yaccun.h
	echo -n  ; ls -ld yaccun.h
fi
echo Extracting yylex.c
if [ -w yylex.c ]; then
	echo File already exists - saving as yylex.c.old
	mv yylex.c yylex.c.old
	chmod 444 yylex.c.old
fi
sed 's/^X//' <<'//go.sysin dd *' >yylex.c
#include <stdio.h>
#include "yaccun.h"
#include "keyword.h"
#include "jcl.h"

#define FORMAT "%[ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$]"
						/* format for reading fields */
#define YYCHARVAL (yylval.charval)		/* an abbreviation */
char token[CHARMAX];				/* text of last token read */
static char card[83],				/* current card */
	    *cptr;
extern int verbose;				/* get debugging output */
static int need_card = 1;		/* Do I need to read a new card? */

int
yylex()
{
   static char word[50];			/* error message */
   int ret;

   if(need_card) {			/* so read one */
      if(fgets(&card[1],81,stdin) == NULL) {
	 return(ENDMARK);
      }
      need_card = 0;
      if(msglevel1 > 0) {
	 fputs(&card[1],msgout);
      }
      if(strlen(&card[1]) > 72) {
	 card[0] = card[72];		/* save continuation character */
      } else {
	 card[0] = ' ';			/* no continuation character */
      }
      card[72] = '\0';			/* delete end of card */
      cptr = &card[72];
      while(*--cptr == ' ') ;		/* find trailing blanks */
      *(cptr + 1) = '\0';		/* strip them */
      cptr = &card[1];			/* now find comments */
      while(*cptr++ != ' ') ;		/* skip //name field */
      while(*cptr++ == ' ') ;		/* and first blank field */
      while(*cptr++ != ' ') ;		/* and JOB/DD/EXEC/etc field */
      while(*cptr++ == ' ') ;		/* and second blank field */
      while(*cptr++ != ' ') ;		/* and options field */
      *--cptr = '\0';			/* cut the comment off the card */
      cptr = card + strlen(card);	/* 1 past last character */
      if(*(cptr - 1) != '\n') {		/* check if card ends in \n */
	 *cptr++ = '\n';
	 *cptr = '\0';
      }
      cptr = &card[1];
   }

   if(*cptr == '\0') {
      need_card = 1;
      return(yylex());
   }

   if(cptr == &card[1]) {
      if(!strncmp(cptr,"//*",3)) {			/* comment */
	 need_card = 1;
	 return(yylex());
      } else if(!strncmp(cptr,"//",2)) {		/* May be EXEC */
	 cptr += 2;					/* skip // */
	 if(sscanf(cptr,FORMAT,word) == 1) {		/* read NAME field */
	    cptr += strlen(word);
	 } else if(*cptr == '\n') {			/* null card */
	    sprintf(token,"//\\n");
	    return(NULL_CARD);
	 } else {
	    word[0] = '\0';
	 }
	 if(yylex() == ' ') {				/* EXEC or null? */
	    if((ret = yylex()) == EXEC) {		/* yes, an EXEC card */
	       strcpy(YYCHARVAL,word);
	       sprintf(token,"//%s EXEC",word);
	       return(ret);
	    } else if(ret == '\n') {			/* null card */
	       sprintf(token,"//\\n");
	       return(NULL_CARD);
	    }
	 }
	 cptr = &card[3];			/* no, rewind and return // */
	 sprintf(token,"//");
	 ret = DSLASH;
      } else if(!strncmp(cptr,"/*",2)) {
	 sprintf(token,"/*");
	 cptr += 2;
	 ret = SLASHSTAR;
      } else {
	 sprintf(word,"Card begins %c%c",*cptr,*(cptr+1));
	 yyerror(word);
	 sprintf(YYCHARVAL,"%c%c",*cptr++,*cptr++);
	 strcpy(token,YYCHARVAL);
	 ret = WORD;
      }
   } else {
      if(sscanf(cptr,FORMAT,YYCHARVAL) == 1) {
	 strcpy(token,YYCHARVAL);
	 cptr += strlen(YYCHARVAL);
	 ret = keyword(YYCHARVAL);
      } else {
	 switch (*cptr) {
	 case ',': case '.': case '/': case '\'': case '(': case ')':
	 case '*': case '&': case '+': case '-':  case '=':
	    sprintf(token,"%c",*cptr);
	    ret = *cptr++;
	    break;
	 case ' ':
	    while(*cptr == ' ') {
	       cptr++;
	    }
	    sprintf(token,"' '");
	    ret = ' ';
	    break;
	 case '\n':
	    if(card[0] != ' ' || *(cptr - 1) == ',') {	/* Continuation */
	       need_card = 1;				/* skip newline */
	       if(yylex() != DSLASH ||			/* card starts // */
				      yylex() != ' ') {	/* then spaces */
		  sprintf(word,"Expected continuation card");
		  yyerror(word);
	       }
	       return(yylex());				/* return next */
	    } else {
	       sprintf(token,"\\n");
	       cptr++;
	       ret = '\n';
	    }
	    break;
	 default:
	    sscanf(cptr,"%[^,./'()*&+-= ]",YYCHARVAL);
	    if(YYCHARVAL[strlen(YYCHARVAL) - 1] == '\n') {
	       YYCHARVAL[strlen(YYCHARVAL) - 1] = '\0';
	    }
	    strcpy(token,YYCHARVAL);
	    cptr += strlen(YYCHARVAL);
	    sprintf(word,"Illegal character in string \"%s\"",YYCHARVAL);
	    yyerror(word);
	    ret = WORD;
	    break;
	 }
      }
   }
   if(verbose) {
      fprintf(msgout,"TOKEN %s\n",token);
   }
   return(ret);
}

get_cchar()		/* get next character off a card */
{
   if(*cptr == '\n') {		/* at end of card */
      return('\n');
   } else {
      return(*cptr++);
   }
}

put_cchar(c)
int c;
{
   if(cptr > &card[0]) {
      *--cptr = c;
   }
}
//go.sysin dd *
if [ `wc -c < yylex.c` != 4445 ]; then
made=FALSE
echo error transmitting yylex.c --
echo length should be 4445, not `wc -c < yylex.c`
else
	made=TRUE
fi
if [ $made = TRUE ]; then
	chmod 644 yylex.c
	echo -n  ; ls -ld yylex.c
fi