Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
c/lib5/usys/urt.c
There are 9 other files named urt.c in the archive. Click here to see a list.
/* URT - USYS Run Time support and startup
**
** Copyright (c) 1987 by Ken Harrenstien & Ian Macky, SRI International
** applies to edits made since name changed to URT.C.
** Edits for ITS: Copyright (C) 1988 Alan Bawden
**
** This code is invoked by the CRT (C Run Time) startup and does
** various things (such as parsing any JCL) before invoking the user's
** "main" function.
** The URT startup should, insofar as possible, limit itself to
** only USYS functions; that is, to simulating the UN*X startup. This
** code should NOT use the higher-level library functions such as
** printf or malloc.
*/
#include "c-env.h" /* Include environment/config defs */
#include "stdlib.h" /* standard library definitions */
#include "urtsud.h" /* URT StartUp Definition structure */
#include "stdio.h"
#include "errno.h"
#include "frkxec.h" /* New KCC forkexec() call */
#include "sys/usysio.h"
#include "sys/usytty.h" /* Internal TTY defs */
#include "sys/usysig.h"
#include "sys/file.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "ctype.h"
#include "string.h" /* For str- and mem- defs */
#if SYS_T20+SYS_10X
#include "jsys.h"
#elif SYS_ITS
#include "sysits.h"
#endif
/* Exported functions (defined here) */
void _runtm(); /* URT startup entry point */
/* Imported functions (external) */
extern main(); /* From user program */
extern char *sbrk(); /* See the WALIGN_sbrk macro below */
extern void exit(), _exit();
/* Internal system-dependent functions */
static void init_uio(), setfil(), tty_set(), abortmsg();
static char *getjcl();
/* Internal functions */
static int shell_parse(), exec_parse();
static void argfset(), arg_set(); /* use these to set args in argv */
/* Global URT data. Only "errno" is intended to be user-visible. */
int errno = 0; /* Error # from last failing UN*X syscall */
int _vfrkf = 0; /* Non-zero if memory shared as result of vfork() */
int _nfork = 0; /* Count of inferiors created by this process.
** Only used to determine whether a fork or vfork
** was ever done, i.e. if exit() should wait().
*/
/* Define macro so that sbrk()'s idea of the break is always word-aligned
** upon entry to the user program.
*/
static char *wdsbrk(); /* Use routine now, not macro */
#define WS (sizeof(int)) /* Word size */
#define WALIGN_sbrk(n) wdsbrk(n) /* Word-aligned sbrk() */
/* Macro to reduce some typing; used with abortmsg(). */
#define LASTERR strerror(_ERRNO_LASTSYSERR)
#define MAX_ARG 32 /* # words of argv space to increment each time */
static int argc; /* argument count (finally given to main()) */
static char **argv; /* pointer to argv vector (ditto) */
static int argv_size; /* current max size of argv */
/*
** _RUNTM() UN*X Run Time C program startup!
*/
void
_runtm()
{
int (*parser)(); /* parser function to call */
char *p, *arg_vector[MAX_ARG];
init_uio(); /* initialize uio layer */
argc = 0; /* no args on command line yet */
argv = arg_vector; /* start off using this array */
argv_size = MAX_ARG; /* size of argv array */
if (_urtsud.su_parser_type != _URTSUD_NO_PARSE && (p = getjcl())) {
switch (_urtsud.su_parser_type) {
default:
case _URTSUD_SHELL_PARSE: parser = shell_parse; break;
case _URTSUD_EXEC_PARSE: parser = exec_parse; break;
}
(*parser)(p); /* invoke the parser */
}
tty_set(); /* Set up TTY stuff if any. If so, it must */
/* register a cleanup rtn with onexit()! */
onexit(_cleanup); /* Register STDIO cleanup rtn for exit() */
exit(main(argc, argv)); /* Call user program, return value! */
}
/* EXEC_PARSE
* Parser for EXEC-style command lines. takes a string of the form
* "<command>[<space><text>]<CRLF>" and breaks it into one or two
* parts. if argc=1, then there's just a command. if argc=2, then
* there a command plus an arg. argv[0] points to the start of the
* command, argv[1] points to the start of the arg (maybe). both
* skip initial whitespace and null-terminate their parts in place
* in the string buffer.
*
* also, that CRLF might just be a CR, or maube just an LF. be
* sure and handle all cases.
*/
static int
exec_parse(p)
char *p;
{
int c;
char *original_p;
while ((c = *p) == ' ' || c == '\t')
p++; /* skip leading whitespace */
original_p = p; /* save this to see if it changes */
while (c && c != ' ' && c != '\t' && c != '\r' && c != '\n')
c = *++p; /* skip text of command, if any */
if (p == original_p) return; /* oops, nothing really there. */
*p = '\0'; /* tie off command. */
arg_set(original_p); /* save start of command */
if (c != ' ' && c != '\t') return 1;/* if not a space, no arg. */
while ((c = *++p) == ' ' || c == '\t')
; /* else, skip whitespace b4 arg. */
if (c == '\r' || c == '\n') /* if lots of blankspace but no */
return; /* real arg, then so be it. */
arg_set(p); /* else save start of arg */
while ((c = *++p) && c != '\r' && c != '\n') ;
if (c) *p = '\0'; /* skip to EOL, clobber CR or LF. */
return;
}
/* SHELL_PARSE - unix-style shell parser.
*/
static char *indfparse();
static int argscan();
static int runpipe();
#if SYS_T20+SYS_10X
static char *mangle();
#endif
static int haswild = 0; /* Set by argscan() if arg had unquoted wild chars */
#ifndef SYSFIL_QTCH
#if SYS_T20+SYS_10X
#define SYSFIL_QTCH (026) /* CTRL-V - T20 quoting convention */
#elif SYS_ITS
#define SYSFIL_QTCH (021) /* CTRL-Q - ITS quoting convention */
#else
#define SYSFIL_QTCH (-1)
#endif
#endif
static int
shell_parse(p)
char *p;
{
register int c;
char *cp, *ap;
int pipeout = -1;
int lev = 0;
int append = 0; /* No append for stdout redirect */
char *in = NULL, *out = NULL; /* No redirection */
/*
* Read command line into a string. We later drop some nulls into
* it to separate it into arguments, to save space. We allocate
* space using sbrk() for the string; if there are no wildcard
* arguments this will be all we need to store arguments.
*/
lev = 0;
c = *(cp = p); /* Initialize */
while (c) {
switch (c) {
default:
if (!isgraph(c)) /* Ignore whitespace/cntrls */
break;
ap = cp; /* Have start of an arg, note it. */
c = argscan(ap, &cp, lev); /* Gobble the arg */
argfset(ap); /* Store the arg! */
continue; /* Loop, don't increment cp */
case '@': /* Handle indirect file spec */
cp = indfparse(cp, lev);
c = *cp;
continue; /* Loop, don't increment cp */
case ';': /* Ignore ;-commented lines */
while (*++cp && *cp != '\n');
if (!*cp) --cp; /* Stop on next loop */
break;
#if SYS_T20+SYS_10X
case '!': /* Ignore !-commented phrases/lines */
while (*++cp && *cp != '!' && *cp != '\n');
if (!*cp) --cp; /* Stop on next loop */
break;
case '-': /* Check for T20 "line continuation" */
if (cp[1] == '\n' || cp[1] == '\r')
break; /* Ignore it */
ap = cp; /* Have start of an arg, note it. */
c = argscan(ap, &cp, lev); /* Gobble the arg */
argfset(ap); /* Store the arg! */
continue; /* Loop, don't increment cp */
#endif /* T20+10X */
/*
* an ampersand means run in the background. this is implemented
* with the usual EXEC PRARG% hack, which if you don't have, you
* should. it always allows a fork to request being reset, or to
* kept.
*/
case '&':
#if !SYS_T20
abortmsg("Background (&) not implemented on this system", NULL);
#else
if (procamper() == -1) /* tell exec we need '&' action */
abortmsg("Couldn't continue ourselves in background - ",
LASTERR, NULL);
/* Now running in background... */
#endif /* T20 */
c = 0; /* We shouldn't have anything after this */
continue; /* Stop loop. */
case '|': /* Handle pipe setup, <prog> | <prog2> */
#if !SYS_T20
abortmsg("Pipes not supported on this system", NULL);
#else
if (out != NULL || pipeout >= 0)
abortmsg("Multiple redirection of output", NULL);
while (isspace(*++cp)); /* Move over '|' and flush wsp */
pipeout = runpipe(cp);
#endif
c = 0; /* Remaining JCL went to child */
continue; /* Stop loop */
/* '<' means take the following file as primary input.
* Must not confuse "foo <dir>name" with a redirection request!
*/
case '<':
#if SYS_T20+SYS_10X
if (lev || mangle(cp)) { /* Matched brackets? */
ap = cp; /* Have start of an arg, note it. */
c = argscan(ap, &cp, lev); /* Gobble the arg */
argfset(ap); /* Store the arg! */
continue; /* Loop, don't increment cp */
}
#endif
if (in != NULL)
abortmsg("Multiple redirection of input", NULL);
while (isspace(c = *++cp)); /* Get next, flush wsp */
in = cp; /* Remember ptr to filename */
c = argscan(in, &cp, lev); /* Gobble past file name */
continue; /* Loop, don't increment cp */
case '>': /* output redirection? */
if (cp[1] == '>') { /* another one, for append? */
++append; /* Yep, set flag */
++cp; /* and skip over first bracket */
}
if (out != NULL || pipeout >= 0)
abortmsg("Multiple redirection of output", NULL);
while (isspace(c = *++cp)); /* Get next, flush wsp */
out = cp; /* Remember ptr to filename */
c = argscan(out, &cp, lev); /* Gobble past file name */
continue; /* Loop, don't increment cp */
}
c = *++cp; /* breaking from switch goes to next char */
}
if (pipeout >= 0) { /* If we are piping output, */
dup2(pipeout, STDOUT_CH); /* redirect std output to pipe. */
close(pipeout); /* And flush this FD now */
}
if (in) /* If desired, */
setfil(in, STDIN_CH, 0); /* redirect stdin to file */
if (out) /* and ditto for stdout */
setfil(out, STDOUT_CH, append);
}
/* RUNPIPE - Set up pipe process
*/
#define PIPEIN 0 /* Indices into array used by pipe() call */
#define PIPEOUT 1
static int
runpipe(cp)
char *cp;
{
#if SYS_T20
struct frkxec f;
int pipes[2]; /* For pipe FDs */
char *av[3]; /* Temporary argv array */
char pgmnam[1000]; /* Very big program name */
argscan(pgmnam, &cp, 0); /* Get program name (always top level) */
if (pipe(pipes) == -1)
abortmsg("Couldn't make pipe - ", LASTERR, NULL);
av[0] = pgmnam; /* Arg 0 is prog name */
av[1] = cp; /* Rest is remaining JCL */
av[2] = NULL;
f.fx_flags = FX_PGMSRCH | FX_FDMAP;
f.fx_name = pgmnam; /* Program name to run */
f.fx_argv = &av[0];
f.fx_envp = NULL;
f.fx_fdin = pipes[PIPEIN]; /* Map fork's input to this */
f.fx_fdout = -1; /* Leave output alone */
if (forkexec(&f) < 0)
abortmsg("Couldn't get next program in pipe - ", LASTERR, NULL);
/* Special hack: forget about pipe's input UFX so we don't close it
* by accident; there is no mechanism for sharing FD-use count
* between forks! Sigh. If there was, we could just
* dispense with the following two statements.
*/
_uionopen[_uioufx[pipes[PIPEIN]]] = 0; /* Can re-use UFX */
_uioufx[pipes[PIPEIN]] = 0; /* Quietly forget pipe-input UFX */
return pipes[PIPEOUT]; /* Return FD for output to pipe */
#endif /* T20 */
}
/* ARGFSET - Add a filename arg to the argv array, bumping argc.
** Expands wildcards if needed and performs other checking.
*/
static void
argfset(arg)
char *arg;
{
/* Watch out for EXEC stupidness! Make sure we don't have an
** EXEC noise keyword at start of JCL. Time to check this is when
** we're about to add a 2nd arg -- check the first, and if it's bad,
** replace it with this one.
*/
#if SYS_ITS==0
if (argc == 1) {
static int caseall();
static char *fktab[] = { "RUN", "RU", "R", "START", "ST", "S", 0 };
register char **p = fktab;
while (*p)
if (caseall(argv[0], *p++)) {
--argc; /* Barfo, flush the 1st arg */
break;
}
}
#endif /* not ITS */
#if SYS_T20+SYS_10X
if (argc > 0 && haswild) { /* Expand wildcard filename? */
static int wfopen(), wfnext();
static char *wfname();
int wjfn;
if ((wjfn = wfopen(arg)) == 0) /* open wildcard spec */
abortmsg("No match for \"", arg, "\"", NULL);
do
arg_set(wfname(wjfn)); /* Get new name and set it */
while (wfnext(wjfn));
return;
}
#endif /* T20+10X */
arg_set(arg); /* Just set the arg */
}
/* ARG_SET - add an arg to the argv array, bumping argc.
** If argv becomes full, it is expanded (with extra space so
** we don't have to do it too often). Note that an extra word is
** kept on the end so that the array always ends with a NULL pointer.
*/
static char *lastsbrk = 0; /* Last return value from sbrk */
static void
arg_set(p)
char *p;
{
int i, new_size; /* new size, if need to expand */
char **newv; /* New array pointer, if ditto */
if (argc >= argv_size-1) { /* need to expand? */
new_size = argv_size + MAX_ARG; /* increase by this much each time */
if (lastsbrk == sbrk(0)) { /* Can we just expand it? */
WALIGN_sbrk(MAX_ARG * sizeof(char *)); /* Yes, do so */
}
else { /* No, must get new array, and copy old into it. */
newv = (char **)WALIGN_sbrk(new_size * sizeof(char *));
memcpy((char *)newv, (char *)argv, argv_size*sizeof(char *));
argv = newv;
}
argv_size = new_size; /* new max # of args */
lastsbrk = sbrk(0); /* New value of last break */
}
argv[argc] = p; /* set this arg. */
argv[++argc] = NULL; /* Ensure array always ends in 0 */
}
/* WDSBRK - word-aligned interface to sbrk() that checks for failure.
*/
static char *
wdsbrk(n)
int n; /* # bytes to allocate */
{
register char *ret;
if ((ret = sbrk(((n+WS-1)/WS)*WS)) != (char *)-1)
return ret;
abortmsg("Out of memory during URT startup", NULL);
}
/* CASEALL - auxilary for ARGFSET. Does uppercase string compare.
*/
static int
caseall(s,t)
char *s,*t;
{
while (toupper(*s) == *t++)
if (!*s++) return 1;
return 0;
}
/* INDFPARSE - Parse indirect file.
** Arg is pointer to start of "@filespec", returns updated pointer.
** The contents of the file, on TOPS-20/TENEX, are parsed in a fashion
** similar to that of COMND% indirect files.
*/
static char *
indfparse(p, lev)
char *p;
int lev;
{
register int c;
register char *cp;
int fd;
struct stat statb;
char *buf, *newp;
/* Scan over filename */
cp = p++; /* Copy arg, skipping initial '@' */
c = argscan(cp, &p, lev); /* Do it */
if (lev > 10) { /* Prevent infinite recursion */
abortmsg("Indirect files nested too deep (10 max) - \"",
cp, "\"", NULL);
}
if (!*cp)
abortmsg("No filename for @ indirect file", NULL);
if ((fd = open(cp, O_RDONLY|O_BINARY)) < 0)
abortmsg("Cannot open indirect file \"", cp, "\" - ", LASTERR, NULL);
if (fstat(fd, &statb) != 0)
abortmsg("Cannot fstat indirect file \"", cp, "\" - ", LASTERR, NULL);
buf = WALIGN_sbrk(statb.st_size+1); /* Get room for file */
if (read(fd, buf, statb.st_size) != statb.st_size)
abortmsg("Cannot read indirect file \"", cp, "\" - ", LASTERR, NULL);
close(fd);
buf[statb.st_size] = '\0'; /* Ensure ends with null */
*p = c; /* No need to preserve filename now */
/* Now scan the acquired buffer, adding args as we encounter them. */
++lev; /* Increment level */
c = *(cp = buf);
for (;;) {
switch (c) {
case '\0': /* NUL means all done, return */
return p;
case '@':
cp = indfparse(cp, lev);
c = *cp;
continue; /* Loop, don't increment cp */
case ';': /* Ignore ;-commented lines */
while (*++cp && *cp != '\n');
if (!*cp) return p;
break;
#if SYS_T20+SYS_10X
case '!': /* Ignore !-commented phrases/lines */
while (*++cp && *cp != '!' && *cp != '\n');
if (!*cp) return p;
break;
case '-': /* Check for T20 "line continuation" */
if (cp[1] == '\n' || cp[1] == '\r')
break; /* Ignore it */
#endif /* T20+10X */
default:
if (!isgraph(c)) /* Ignore whitespace/cntrls */
break;
/* Start scanning over an argument */
arg_set(newp = cp);
c = argscan(cp, &newp, lev);
cp = newp;
continue; /* Loop, don't increment cp */
}
c = *++cp; /* breaking from switch goes to next char */
}
}
/* ARGSCAN - scan over an argument, copying it.
** Assumes "from" points to first char of argument.
** The copy is necessary in order to flush embedded quotes and
** backslashes. Some chars may not be terminators depending on whether parsing
** at top level or not.
** Updates the source pointer ("from") and returns as value the char
** which it supposedly points to -- this is the next char that should be
** examined by the higher level scanner. The pointer itself should not
** be used to examine that char, because it can happen that the NUL char
** written to terminate the arg string was written on top of the "next char".
** Also (hack!) sets the flag "haswild" if any unquoted wildcard
** characters were seen in the arg ('%', '*')
*/
static int
argscan(to, from, lev)
char *to, **from; /* Note "from" is addr of char ptr */
int lev; /* 0 == top level */
{
register char *wp = to-1, *cp = (*from)-1; /* Set up for ILDB/IDPB */
register int c;
haswild = 0; /* No wildcard chars seen so far */
for (;;) {
/* Check for special terminators. */
switch (*++wp = *++cp) {
default:
if (isgraph(*cp)) /* Collect printing chars */
continue;
break; /* Stop if hit any non-printing char */
case '%': /* File wildcard chars */
case '*':
haswild++; /* Report an unquoted wildcard seen */
continue;
case '-': /* Check for - at EOL */
if (!lev) continue; /* Not special at top level */
if (cp[1] == '\r' || cp[1] == '\n') {
--wp; /* Flush '-' from end of arg */
} /* Next pass of loop will terminate */
continue;
case '\\':
if (!(*wp = *++cp)) /* Quote next char (overwrite \) */
break; /* don't allow quote of NUL */
if (*wp == '\r' && cp[1] == '\n')
*wp = *++cp; /* \ CR LF becomes just LF */
continue; /* and keep going */
/* The following filename quote chars differ from '\' in that
** they are KEPT in the argument, for passing on to the
** operating system calls.
*/
case SYSFIL_QTCH:
if (!(*++wp = *++cp)) /* Quote next ch<ar (keep quote!) */
break; /* don't allow quote of NUL */
continue; /* and keep going */
case '"': /* Permit embedding of quoted strings */
case '\'':
c = *cp; /* Remember terminator char */
--wp; /* Back up to flush it */
while (*++wp = *++cp) {
if (*cp == c) break;
if (*cp == '\\') { /* Allow backslash quotes */
if (!(*wp = *++cp))
break; /* don't allow quote of NUL */
} else if (*cp == SYSFIL_QTCH)
if (!(*++wp = *++cp))
break; /* don't allow quote of NUL */
}
if (!*cp) break;
--wp; /* Found terminator, back up so term char not stored */
continue;
case '&':
case '|':
case '>':
if (lev) continue; /* Special only at top level */
break;
case '<':
if (lev) continue; /* Special only at top level */
#if SYS_T20+SYS_10X
if (to = mangle(cp)) { /* Matching angle brackets? */
while (to != cp) { /* Yes, move over them */
*++wp = *++cp;
if (*cp == '%' || *cp == '*')
haswild++; /* Check for wildcards */
}
continue;
}
#endif
break;
}
break; /* Breaking out of switch breaks out of loop */
}
*from = cp; /* Update "from" pointer */
c = *cp; /* Save next char that should be processed */
*wp = '\0'; /* Tie off copied arg (may clobber *cp) */
return c; /* Return saved next-char */
}
/* SETFIL - redirect a standard in/out/err file descriptor.
** Note that the STDIO streams must also be changed; a fd and a stream
** are not the same thing. For now we punt on trying to
** change the buffering or stdio flags.
*/
/* This routine doesn't seem very system-dependent to me, so I moved it
* out here into the open so that everybody could use it. -Alan
*/
static void
setfil(name, fd, append)
char *name;
int fd, append;
{
FILE *fp;
int tmpf, flags;
if (!name) return; /* nothing to do. */
switch (fd) {
case STDIN_CH:
fp = stdin;
flags = O_RDONLY;
break;
case STDOUT_CH:
fp = stdout;
flags = (O_WRONLY | O_CREAT | O_TRUNC | (append ? O_APPEND : 0));
break;
case STDERR_CH:
fp = stderr;
flags = (O_WRONLY | O_CREAT | O_TRUNC | (append ? O_APPEND : 0));
break;
default:
abortmsg("Bad redirection file fd", NULL);
}
if ((tmpf = open(name, flags, 0644)) < 0)
abortmsg("Couldn't open redirection file \"", name, "\" - ",
LASTERR, NULL);
dup2(tmpf, fd); /* Make temp FD be a STDxx FD */
close(tmpf); /* Then flush the temp fd */
if (flags & O_APPEND) /* If appending, */
fseek(fp, 0L, SEEK_END); /* ensure STDIO knows about it */
if (fp == stderr)
setbuf(stderr, (char *)NULL); /* No buffering for stderr */
#if SYS_T20+SYS_10X+SYS_ITS
else if (fp == stdout /* If stdout to non-TTY, */
&& _uiotype[_uioufx[fd]] != _DVTTY)
setvbuf(fp, (char *)NULL, _IOFBF, 0); /* then use full buffering */
#else
#error setfil() needs OS-dependent test for TTYness
#endif
}
#if SYS_T20+SYS_10X
/* MANGLE - Match Angle brackets, try to determine if '<' starts a
** filename with a directory spec or not.
** Returns NULL if no matching '>', else pointer to it.
*/
static char *
mangle(p)
char *p;
{
for (;;) /* until we find a special char */
switch (*++p) { /* check char (ignoring first open) */
case ' ': case '\t': case '\n': case '\r':
case ';': case '<': case ':': case '\0':
return NULL; /* no close or second open, redirect */
case '>':
return p; /* found an angle, matched */
}
}
#endif /* T20+10X */
/****************** System-Dependent Routines ****************************/
#if 0
The system-dependent portion of URT should provide the
following routines:
init_uio() - Initialize I/O at startup
getjcl() - Read JCL to program
setfil(fname, fd, appf) - Redirect FD to given file (append if appf set)
tty_set() - Init TTY at startup; register reset rtn for exit.
abortmsg() - Print message and halt
#endif
#if SYS_T20+SYS_10X /* TOPS-20 and TENEX: */
/*
* TOPS-20/TENEX specific portions of higher-level runtimes
*/
/* INIT_UIO - initialize UIO data structures.
** For now we always set FD 2 (std err) to .CTTRM, and
** and check .PRIIN and .PRIOU to set up FDs 0 and 1.
** Later we may inherit a data page from previous process.
*/
static void
init_uio()
{
int ifx, ofx, efx;
union {
int acs[5];
struct { int junk[2];
unsigned pin : 18; /* LH of AC 2 */
unsigned pout : 18; /* RH of AC 2 */
} res;
} a;
/* Initialize FD 2 (error output) as that is always constant */
efx = _uiofx(); /* Get a free UFX */
_uioch[efx] = _CTTRM; /* Use this JFN */
_uioflgs[efx] = _UIO_CONVERTED; /* With this extra flag */
_openufx(efx, O_RDWR);
_uioufx[STDERR_CH] = efx; /* FD 2 now open for business! */
/* See what our primary JFNs really are */
a.acs[1] = _FHSLF;
jsys(GPJFN, a.acs);
if (a.res.pin == _uioch[efx]) { /* If stdin same as stderr, */
_uioufx[STDIN_CH] = efx; /* just point there. */
_uionopen[efx]++;
} else {
ifx = _uiofx();
_uioch[ifx] = a.res.pin; /* Must set up with this JFN */
_uioflgs[ifx] = _UIO_CONVERTED; /* With this extra flag */
_openufx(ifx, O_RDONLY); /* Take care of rest of vars */
_uioufx[STDIN_CH] = ifx; /* Then make FD 0 open for business */
}
if (a.res.pout == _uioch[efx]) { /* If stdout same as stderr, */
_uioufx[STDOUT_CH] = efx; /* just point there. */
_uionopen[efx]++;
} else {
ofx = _uiofx();
_uioch[ofx] = a.res.pout; /* Must set up with this JFN */
_uioflgs[ofx] = _UIO_CONVERTED; /* With this extra flag */
_openufx(ofx, O_WRONLY); /* Take care of rest of vars */
_uioufx[STDOUT_CH] = ofx; /* Then make FD 1 open for business */
}
}
/* ---------------------------------------- */
/* set terminal mode word */
/* ---------------------------------------- */
#define CCOC_MAGIC 0525252525252
static int ccoc[2];
static void tty_reset();
static void
tty_set()
{
int ablock[5];
ablock[1] = _CTTRM;
if (!jsys(RFCOC, ablock)) return;
ccoc[0] = ablock[2];
ccoc[1] = ablock[3]; /* save ccoc words */
if (ccoc[0] != CCOC_MAGIC
|| ccoc[1] != CCOC_MAGIC) {
ablock[2] = ablock[3] = CCOC_MAGIC;
if (!jsys(SFCOC, ablock)) return;
}
onexit(tty_reset); /* Won, register reset rtn for exit */
}
static void /* Called to reset TTY upon normal exit() */
tty_reset()
{
int ablock[5];
ablock[1] = _CTTRM;
ablock[2] = ccoc[0];
ablock[3] = ccoc[1];
jsys(SFCOC, ablock);
}
/* ABORTMSG(s1, s2, s3, s4, s5) - outputs string composed of given
** concatenated strings, and exits. A NULL argument terminates the
** list of strings.
*/
static void
abortmsg(s, a,b,c,d,e)
char *s, *a,*b,*c,*d,*e;
{
char tmpbuf[1000];
int ac[5];
tty_reset();
strcpy(tmpbuf, s);
if (a) { strcat(tmpbuf, a);
if (b) { strcat(tmpbuf, b);
if (c) { strcat(tmpbuf, c);
if (d) { strcat(tmpbuf, d);
if (e) { strcat(tmpbuf, e);
} } } } }
strcat(tmpbuf, "\r\n"); /* Terminate error string with CRLF */
ac[1] = (int) (tmpbuf-1);
jsys(ESOUT, ac); /* Use ESOUT% to output it */
_exit(1); /* Then stop program with prejudice */
}
/* Wildcard filename routines */
/* WFOPEN - open wild card filename
** Returns wild JFN for filespec, 0 if failure.
*/
static int
wfopen(name)
char *name;
{
return _gtjfn(name, O_RDONLY | O_T20_WILD);
}
/* WFNAME - Return filename for wild JFN
** Returns pointer to dynamically allocated filename string
*/
static char *
wfname(jfn)
int jfn;
{
char *fp, fname[200];
int ablock[5];
ablock[1] = (int) (fname - 1);
ablock[2] = jfn & 0777777; /* jfn, no flags */
ablock[3] = 0111110000001; /* DEV+DIR+NAME+TYPE+VERS, punctuate */
if (!jsys(JFNS, ablock))
return NULL; /* something bad happened */
fp = WALIGN_sbrk(strlen(fname) + 1);
strcpy(fp, fname); /* copy the file name here */
return fp;
}
/* WFNEXT - Make wild JFN point to next real file
** Returns success or failure (not JFN)
*/
static int
wfnext(jfn)
int jfn;
{
int ablock[5];
ablock[1] = jfn; /* save jfn and flags */
return jsys(GNJFN, ablock);
}
/* GETJCL - Return command line
*/
static char *
getjcl()
{
char *buf;
#if SYS_T20
int n, acs[5];
/* Ask to rescan command line, see if anything there. */
acs[1] = _RSINI;
if (jsys(RSCAN, acs) > 0) n = acs[1]; /* Find # chars avail */
else n = 0;
if (n <= 0)
return NULL; /* Nothing there */
buf = WALIGN_sbrk(n + 1); /* make room for chars and null */
/* Have to gobble the RSCAN line directly since only way to access
** the rscan buffer is by using a JFN equivalent to the controlling TTY.
** If our .PRIIN has been redirected, a normal SIN or read() will not
** get the rscan stuff at all.
** TENEX is probably out of luck in this respect.
*/
acs[1] = _CTTRM;
acs[2] = (int) (buf-1);
acs[3] = -n;
jsys(SIN, acs); /* Read the stuff */
buf[n] = 0; /* null-terminate */
#endif /* SYS_T20 */
#if SYS_10X
char *cp;
int c, n;
char tmpbuf[1000];
for (cp = tmpbuf; read(STDIN_CH, cp, 1) == 1; ++cp)
if ((c = *cp) == '\037' || c == '\r' || c == '\n') {
*cp++ = '\n';
break;
}
*cp = 0;
n = strlen(tmpbuf);
buf = WALIGN_sbrk(n + 1);
strcpy(buf, tmpbuf);
#endif /* SYS_10X */
return buf;
}
/* PROCAMPER -
* Tell EXEC via termination PRARG that we want to be continued in
* the background (e.g. we were invoked by foo &) requires slight
* EXEC modification
*/
static int
procamper()
{
int arg_block[5], temp_arg;
arg_block[1] = (_PRAST << 18) + _FHSLF;
temp_arg = PRA_BACK << 18;
arg_block[2] = (int) &temp_arg;
arg_block[3] = 1;
if (!jsys(PRARG, arg_block)) return -1; /* some lossage */
jsys(HALTF, arg_block); /* stop and let exec continue us */
return 0; /* return to caller in background */
}
#endif /* SYS_T20+SYS_10X */
#if SYS_WAITS
/*
** WAITS specific portions of higher-level runtimes
*/
/* Old comments:
* . ch[fd] contains a channel number (usually fd)
* . channel 0 is unredirected TTY input, channel 1 output
* . command line is of the form
* . .RUN FOO ; arg1 arg2 arg3 ...
* . redirection to/from files is supported
*/
/* ---------------------- */
/* set up files */
/* ---------------------- */
static void
setup(in,out)
char *in, *out;
{
_uioch[STDIN_CH] = (in != NULL) ? _uioch[open(in,0)] : STDIN_CH;
_uioch[STDOUT_CH] = (out != NULL) ? _uioch[creat(out,0)] : STDOUT_CH;
_uioch[STDERR_CH] = STDERR_CH;
_uioeof[STDIN_CH] = 0; /* no eof on terminal yet */
}
/* --------------------------------------- */
/* output error message and exit */
/* --------------------------------------- */
static void
abortmsg(s, a,b,c,d,e)
char *s;
{
while (*s) _putty(*s++); /* send it out a bit at a time */
_putty('\n'); /* with a CR to make a new line */
_exit(1); /* in either case, stop program */
}
/* ----------------------------- */
/* return command line */
/* ----------------------------- */
static char *
getjcl()
{
char *argbuf, *p;
int n;
n = _rscan(); /* Rescan command line */
argbuf = WALIGN_sbrk(n + 1); /* make room for chars and null */
p = argbuf; /* point to start of buffer */
while (--n) *p++ = _getty(); /* read the number of chars */
_getty(); /* plus one trailer */
*p = '\0'; /* terminate with a null */
return argbuf;
}
static int
_rscan()
{
asm(" RESCAN 1\n"); /* Rescan cmd line, get # chars avail in 1 */
}
#endif /* SYS_WAITS */
#if SYS_ITS
/*
* Runtime stuff for ITS
*/
static void tty_set() {}
static void init_uio()
{
char *iname = " TTY:_CPROG INPUT";
char *oname = " TTY:_CPROG OUTPUT";
int ufx = _uiofx(); /* UFX for input */
/* If TTY is not available, these just quietly fail to open the */
/* channel since we set %TBNVR. Later an channel not open error */
/* will tell you that this happened... */
SYSCALL3("sopen", SC_IMC(_UAI), _uioch[ufx], &iname);
_uiobsize[ufx] = 7;
_uioflgs[ufx] = _UIO_CONVERTED;
_openufx(ufx, O_RDONLY);
_uioufx[STDIN_CH] = ufx;
ufx = _uiofx(); /* get another UFX for output */
SYSCALL3("sopen", SC_IMC(_UAO), _uioch[ufx], &oname);
_uiobsize[ufx] = 7;
_uioflgs[ufx] = _UIO_CONVERTED;
_openufx(ufx, (O_WRONLY | O_CREAT | O_TRUNC));
_uioufx[STDOUT_CH] = ufx;
dup2(STDOUT_CH, STDERR_CH); /* stderr is just a dup */
}
/* GETJCL() - Return a char pointer to (simulated) JCL string */
static int jclsiz = 100; /* Start with buffer of this many wds */
static char *getjcl()
{
#asm
extern $retz
push 17,[8] ; 2 words for xjname + ^M + null
pushj 17,sbrk
adjsp 17,-1
CAMN 1,[-1] ; If not enough memory,
JRST $RETZ ; just return a null pointer!
push 17,1 ; Else, remember the start of it
hrli 1,440700
push 17,1 ; And a 7-bit byte pointer
.suset [.rxjname,,2]
.rjcl0: setzi 1,
lshc 1,6
addi 1,40
idpb 1,(17)
jumpn 2,.rjcl0
.suset [.roption,,1]
tlnn 1,%opcmd ; If superior has no command
jrst .rjclx ; then that's all.
movei 1,40
idpb 1,(17)
MOVE 1,jclsiz ; Start off with this many words!
.RJCL1: LSH 1,2 ; Multiply by 4 to get # bytes
PUSH 17,1 ; Get this many bytes
PUSHJ 17,sbrk ; From low-level sbrk() call.
ADJSP 17,-1 ; Remove stack arg.
CAMN 1,[-1] ; If not enough memory,
JRST [ adjsp 17,-2 ; just return a null pointer!
JRST $RETZ]
HRRZ 2,-1(17) ; Get word address of block start
HRLZI 3,2(2) ; Put it in LH of 3
HRRI 3,3(2) ; and addr+1 in RH
ADD 2,jclsiz ; Now in 2 get 1st addr past end
SETZM -1(3) ; Clear 1st word
BLT 3,(2) ; Zap all remaining words but one
SETOM 1(2) ; Set up non-zero fence at last word.
HRRZ 3,-1(17) ; Now get start of block again.
ADD 3,[..RJCL,,2] ; Read JCL from superior
.BREAK 12,3 ; Try to read command string.
SKIPE (2) ; See if clobbered last zero word.
JRST [ MOVEI 1,100 ; Add this many more words
ADDB 1,jclsiz
JRST .RJCL1] ; and go try again!
hrli 3,440700 ; Now we copy it down
.rjcl2: ildb 1,3 ; and standardize terminator
caie 1,15 ; ^M
cain 1,3 ; ^C
jrst .rjclx
caie 1,37 ; ^_
cain 1,0 ; ^@
jrst .rjclx
idpb 1,(17)
jrst .rjcl2
.rjclx: movei 1,15 ; CR at the end for parsers
idpb 1,(17)
setzi 1, ; Then a null for C
idpb 1,(17)
move 1,-1(17) ; Make 7-bit b.p.
hrli 1,350700
adjsp 17,-2
#endasm
}
/* ABORTMSG() - Print message and halt */
static void abortmsg(msg) /* more strings until NULL */
char *msg;
{
#asm
.iopush 1,
.open 1,[sixbit / !TTY/
setz
setz]
.lose
movei 10,-1(17)
abrtm0: setoi 3,
adjbp 3,(10)
move 4,3
setzi 5,
abrtm1: ildb 6,4
skipe 6
aoja 5,abrtm1
.call [ setz
sixbit /SIOT/
movei 1
move 3
setz 5 ]
.lose
skipe -1(10)
soja 10,abrtm0
.iopop 1,
#endasm
_exit(1);
}
#endif /* ITS */
#if 0
/*
* Old Runtime stuff for ITS
*/
#include "sysits.h"
static void
setup(in,out)
{
setfil(0, in ? in : "TTY:", 0);
setfil(1, out?out : "TTY:", 1);
setfil(2, "TTY:", 1);
}
static void
setfil(fd, name, umode)
char *name;
{
int fdx;
if ((fdx = _ofile(name, umode, 7)) >= 0) {
if (fdx != fd)
dup2(fdx, fd);
} else abortmsg("Couldn't open redirection file", NULL);
}
static void
abortmsg(msg)
char *msg;
{
char tmpbuf[1000];
strcat(strcat(strcpy(tmpbuf,":\033 "),msg)," \033\r\n\033p");
_valret(tmpbuf);
_exit(1);
}
static char *
getjcl()
{
char *_rjcl();
return _rjcl();
}
/* _RJCL() - Return a char pointer to JCL */
static int jclsiz = 100; /* Start with buffer of this many wds */
static char *_rjcl()
{
#asm
.SUSET [.ROPTIO,,1]
TLNN 1,%OPCMD ; Has our superior said it has a cmd?
JRST $RETZ ; No.
PUSH 17,[0] ; Find out current break
PUSHJ 17,sbrk ; by calling low-level sbrk()
MOVEM 1,(17) ; Save result on stack
MOVE 1,jclsiz ; Start off with this many words!
.RJCL1: LSH 1,2 ; Multiply by 4 to get # bytes
PUSH 17,1 ; Get this many bytes
PUSHJ 17,sbrk ; From low-level sbrk() call.
ADJSP 17,-1 ; Remove stack arg.
CAMN 1,[-1] ; If not enough memory,
JRST [ POP 17,1 ; just return a null pointer!
JRST $RETZ]
HRRZ 2,(17) ; Get word address of block start
HRLZI 3,(2) ; Put it in LH of 3
HRRI 3,1(2) ; and addr+1 in RH
ADD 2,jclsiz ; Now in 2 get 1st addr past end
SETZM -1(3) ; Clear 1st word
BLT 3,-2(2) ; Zap all remaining words but one
SETOM -1(2) ; Set up non-zero fence at last word.
HRRZ 3,(17) ; Now get start of block again.
HRLI 3,5 ; 5 = Read JCL from superior
.BREAK 12,3 ; Try to read command string.
SKIPE -2(2) ; See if clobbered last zero word.
JRST [ MOVEI 1,100 ; Add this many more words
ADDB 1,jclsiz
JRST .RJCL1] ; and go try again!
POP 17,1 ; Won! Get ptr to beg of block.
HRLI 1,350700 ; Return 7-bit char pointer to ASCIZ.
#endasm
}
/* _VALRET() - Pass a word-aligned string to DDT.
** We have to just hope the string starts on a word boundary.
*/
static void
_valret(str)
char *str;
{
#asm
XMOVEI 1,@-1(17) ; We just hope it starts on wd boundary.
.VALUE (1)
#endasm
}
#endif /* 0 (Old Runtime stuff for ITS) */