#-h- hshdef 22 asc 29-apr-81 18:33:43 [002,100] define(prompt,logpmt) #-h- rshdef 22 asc 29-apr-81 18:33:44 [002,100] define(prompt,rawpmt) #-h- cpars 537 asc 29-apr-81 18:33:45 [002,100] # /cpars/ - holds token and tree info for shell # put on a file called 'cpars' # Used only by the shell common /cpars/ tkbuf(TSIZE, MAXTOK), tree(TREESIZE), stack(MAXSTACK), treend, pp, ibuf(MAXLINE) integer tkbuf #token table for parsing integer tree #parse tree integer stack #push down stack for tokens integer treend #next available tree node; init by parse integer pp #push down counter; init by parse character ibuf #input buffer #-h- csclin 156 asc 29-apr-81 18:33:46 [002,100] # csclin - scratch line for shell # put on a file named 'csclin' common / csclin / lin(MAXLINE) character lin # scratch line for use throughout the shell #-h- shcmd 533 asc 29-apr-81 18:33:47 [002,100] ## shcmd - common block holding shell commands # Put on a file called 'shcmd' # Used only by the shell common /shcmd/ logout(7), cd(3), home(5), von(4), voff(5), xon(4), xoff(5) character logout #logout (same as end-of-file) character cd # change working directory - cd character home # change working directory to home directory character von # equivalent to -v in command line character voff # turns off von character xon # equivalent to -x in command line character xoff # turns off xon #-h- shflag 827 asc 29-apr-81 18:33:49 [002,100] # shflag - common block to hold shell flags # put on a file called 'shflag' # Used only by the shell common /shflag/ exec, prlin, prcom, carg, drop, cargdn, shin, clin(MAXLINE) integer exec #flag to cause/suppress command execution #init = YES integer prlin #flag to cause printing of lines as read #init = NO integer prcom #flag to cause printing of command as executed #init = NO integer carg #flag to cause execution of shell command line as input #init = NO integer drop # flag to cause drop through to native CLI upon search # error - init = YES integer cargdn # YES if -c & arguments have been fetched integer shin #file identifier for shell input (generally STDIN) character clin #buffer to hold shell arg to be used as input #-h- stdsub 1324 asc 29-apr-81 18:33:50 [002,100] # /stdsub/ - common block holding file info for shell # put on a file named 'stdsub' # Used only by the shell common /stdsub/ in, cin(MAXSTACK), out, cout(MAXSTACK), er, cerr(MAXSTACK), aout(MAXSTACK), script, pctr, pfile(MAXSTACK), hfile(FILENAMESIZE), input(FILENAMESIZE), sh(FILENAMESIZE), spath(MAXPATHSIZE), olddir(FILENAMESIZE), homedr(FILENAMESIZE) integer in #input stack count integer cin #input substitution stack integer out #output stack count integer cout #output stack integer er #errout stack count integer cerr #errout stack integer aout #append flag integer script #flag showing if script file being processed integer pctr #running pipe count integer pfile # YES/NO whether pipe file used executing this tree character hfile #names of heredocument files character input #holds name of standard input file for script #init input(1) = EOS character sh # holds name of shell for script and background character spath # search path for loccom calls - initialized by initsh character olddir # name of previous directory character homedr # name of home directory #-h- sh.r 43097 asc 29-apr-81 18:33:59 [002,100] #-h- defns 1116 asc 27-apr-81 22:20:35 [002,100] # definitions for shell # put on a file named 'defns' # Used only by the shell define(TSIZE,4) define(MAXSHLINE,MAXLINE) #maximum command line length define(SHELL,17) #flag for shell command define(IBPTR,1) #array index for token pointers define(TMARK,2) #array index for token marks define(NODEPTR,3) #array index for node pointers define(ESCAN,4) #array index for end of scan define(MAXTOK,132) #max token size define(TREESIZE,200) #max size of tree define(ROOT,-1) #flag for beginning of tree define(PARENT,1) define(NTYPE,2) define(LCHILD,3) define(RCHILD,4) define(REDIN,5) define(REDOUT,6) define(REDERR,7) define(NENT,8) define(CMD,9) define(ARGUMENT,10) define(COM,LETC) define(SSYNTAX,0) define(SSYN1,1) define(SSYN2,2) define(SSYN3,3) define(MAXSTACK,15) define(PIPE,BAR) define(SEPCHAR,SEMICOL) # character to separate commands on line define(SCRIPT,2) define(OWNER,-1) #flag for receiving message from parent define(BUFSIZE,MAXLINE) define(MAXPATHSIZE,arith(FILENAMESIZE,*,3)) # maximum size of search path #-h- main 597 asc 23-apr-81 10:31:28 [002,102] ## sh - driver subroutine for DEH shell DRIVER(sh) character line(MAXSHLINE) integer parser, shline call query("usage: sh [-cdnvx] [file] [arguments].") call initsh repeat { if (shline(line) == EOF) break if (line(1) == NEWLINE | line(1) == SHARP) next if (line(1) == QMARK & line(2) == NEWLINE) { call remark("Type intro for an introduction to the tools.") next } if (parser(line) == ERR) call remark ('syntax error.') else call execut } call endsh DRETURN end #-h- arglin 506 asc 23-apr-81 10:31:29 [002,102] ## arglin - pick up all arguments starting with i subroutine arglin (buf, i) character buf(ARB) integer i, k, m integer getarg include csclin k = 1 for (j=i; getarg(j, lin, MAXLINE) != EOF; j=j+1) { if (lin(1) == ESCAPE & lin(2) == LESS) m = 2 else m = 1 call stcopy(lin, m, buf, k) call chcopy(BLANK, buf, k) } if (k > 1) k = k - 1 #delete last blank buf(k) = NEWLINE buf(k+1) = EOS return end #-h- atbeg 414 asc 23-apr-81 10:31:31 [002,102] ## atbeg - return YES if at beginning of new shell token integer function atbeg(c) character c integer spec if (spec(c) == YES | #special shell character c == LESS | c == GREATER | c == QMARK | #redirected IO c == BLANK | c == TAB | #arg separator c == SQUOTE | c == DQUOTE) #new quoted string atbeg = YES else atbeg = NO return end #-h- cmdtyp 524 asc 29-apr-81 18:18:31 [002,100] ## cmdtyp - check command and prepare for appropriate fetching integer function cmdtyp (comand, path) character comand(ARB), path(ARB) integer equal, shcom integer loccom include stdsub string local "local" string execut "x" string suffix BOTH_SUFFIX call scopy(comand, 1, path, 1) if (shcom(comand) == YES) cmdtyp = SHELL else if (equal(comand, execut) == YES) { call scopy(local, 1, path, 1) cmdtyp = BINARY } else cmdtyp = loccom(comand, spath, suffix, path) return end #-h- doampr 960 asc 29-apr-81 18:18:33 [002,100] ## doampr - process ampersand node of parse tree subroutine doampr (node, dir) integer node, dir, i integer spawn, getcl, loccom character desc(PIDSIZE) include stdsub include cpars include shflag string shstr "sh" string suffix IMAGE_SUFFIX string second " -c " if (dir == RCHILD | dir == PARENT) return if (loccom(shstr, spath, suffix, sh) != BINARY) { call remark("Cannot locate shell image file.") return } i = 1 call stcopy(shstr, 1, clin, i) call stcopy(second, 1, clin, i) if (getcl(node, dir, clin, i) == ERR) return call stripb(clin) # strip trailing blanks if (prcom == YES) #user wishes to see command call dspcom(sh, clin) if (exec == YES) #execute command { if (spawn(sh, clin, desc, BACKGR) == ERR) call remark ('Cannot spawn background process.') else call remark (desc) } return end #-h- docom 1867 asc 23-apr-81 10:31:35 [002,102] ## docom - process command node of parse tree integer function docom (node, dir) integer node, i, j, type, dir, status integer spawn, shellc integer cmdtyp, equal integer pickup, inf, outf, errf, length character local(6) character comand(FILENAMESIZE), desc(PIDSIZE) include shflag include shcmd data local/LETL, LETO, LETC, LETA, LETL, EOS/ #pick up command j = 1 junk = pickup(clin, j, node, CMD, junk) call fold(clin) type = cmdtyp(clin, comand) #check task and prepare command call if (type == ERR & drop == NO) { call remark ('invalid task.') return(ERR) } if (equal(comand, local) == YES | type == SHELL) j = 1 else call chcopy(BLANK, clin, j) if (type == ASCII) call scrf(node, comand, clin) else #pick up arguments { if (type == ERR) call strcpy(local, comand) for (i=1; pickup(clin,j,node,ARGUMENT,i) != ERR; i=i+1) call chcopy(BLANK, clin, j) #pick up file substitutions if (inf(node,clin,j) != ERR) call chcopy(BLANK, clin, j) if (outf(node, clin, j) != ERR) call chcopy(BLANK, clin, j) if (errf(node,clin, j) != ERR) call chcopy(BLANK, clin, j) } call stripb(clin) # strip trailing blanks if (prcom == YES & equal(comand, xoff) == NO) # user wishes to see command call dspcom(comand, clin) if (exec == YES) #execute command { if (type == SHELL) #execute shell commands status = shellc(comand, clin) else { status = spawn(comand, clin, desc, WAIT) if (status == ERR) call remark("cannot spawn process.") else if (status != OK) status = ERR } } else status = OK return(status) end #-h- dopar 1816 asc 23-apr-81 10:31:38 [002,102] ## dopar - handle parenthesized statement integer function dopar (p1,p2) character tok integer p, p1, p2, l, pnode, node, pt, ndx integer setree, mktree include cpars l = 0 for (p=p1; p 0) ) { in = in + 1 cin(in) = tree(node+REDIN) #flag substitution by setting to negative tree(node+REDIN) = -tree(node+REDIN) } } if (tree(node+REDOUT) != 0) { if (out == 0 | #output substitution (out != 0 & cout(out) > 0)) { out = out + 1 cout(out) = tree(node+REDOUT) #flag substitution tree(node+REDOUT) = -tree(node+REDOUT) } } if (tree(node+REDERR) != 0) { er = er + 1 cerr(er ) = tree(node+REDERR) #flag substitution tree(node+REDERR) = -tree(node+REDERR) } } else { if (tree(node+REDIN) < 0) { in = in - 1 tree(node+REDIN) = abs(tree(node+REDIN)) } if (tree(node+REDOUT) < 0) { out = out - 1 tree(node+REDOUT) = abs(tree(node+REDOUT)) } if (tree(node+REDERR) < 0) { er = er - 1 tree(node+REDERR) = abs(tree(node+REDERR)) } } return(OK) end #-h- dopipe 476 asc 23-apr-81 10:31:42 [002,102] ## dopipe - process pipe node of parse tree integer function dopipe (node, dir) integer node, dir include stdsub if (dir == LCHILD) { pctr = pctr + 1 pfile(pctr) = NO out = out + 1 cout(out) = -pctr aout(out) = 0 } else if (dir == RCHILD) { in = in + 1 cin(in) = cout(out) out = out - 1 } else { pctr = pctr - 1 in = in - 1 } return(OK) end #-h- dosemi 355 asc 23-apr-81 10:31:44 [002,102] ## dosemi - process semicolon node of parse tree integer function dosemi (node, dir) integer node, dir include stdsub if (dir == RCHILD) { if (out > 0) aout(out) = aout(out) + 1 } else if (dir == PARENT) { if (out > 0) aout(out) = aout(out) - 1 } return(OK) end #-h- doverb 1946 asc 23-apr-81 10:31:45 [002,102] ## doverb - handle final command syntax integer function doverb (p1,p2) character tok integer p, p1, p2, p3, i, node, ndx integer mktree, setree include cpars ndx = tkbuf(IBPTR, p1) tok = ibuf(ndx) #check token if (tok == LESS | tok == GREATER | tok == QMARK) { call stxerr ('doverb--command must preceed redirected IO.') doverb = ERR return } nargs = p2 - p1 -1 if (mktree(tkbuf(NODEPTR, p1), COM, 9+nargs, node) == ERR) #make tree entry { doverb = ERR return } i = 0 #enter pointers for (p=p1; p 0 & cerr(er) > 0) #check for parens { ### Er may not be properly set if (aout(out) != 0) #append call chcopy(QMARK, buf, i) call stcopy (ibuf(cerr(er)), 1, buf, i) errf = OK } return end #-h- execut 1133 asc 23-apr-81 10:31:51 [002,102] ## execut - process shell parse tree subroutine execut integer node, type, dir, status, i, j integer mvnext, dosemi, doampr, dopipe, doparn, docom include stdsub in = 0 #initialize file substitution stacks out = 0 er = 0 pctr = 0 hfile(1) = EOS for (i=1; i<=MAXSTACK; i=i+1) pfile(i) = NO node = ROOT while (mvnext(node, type, dir) != ROOT) #move thru tree { if (type == SEPCHAR) status = dosemi (node, dir) else if (type == AMPER) status = doampr (node, dir) else if (type == PIPE) status = dopipe (node, dir) else if (type == PAR) status = doparn (node, dir) else if (type == COM) status = docom (node, dir) else { call remark ('execut - invalid parse tree.') status = ERR } if (status == ERR) break } if (hfile(1) != EOS) call remove (hfile) for (i=1; i<=MAXSTACK; i=i+1) #remove scratch files if (pfile(i) == YES) { j = 1 call gpname(i, hfile, j) call remove(hfile) } return end #-h- getcl 912 asc 23-apr-81 10:31:53 [002,102] ## getcl - get command line for background process integer function getcl(node, dir, buf, k) integer node, junk, snode, type, dir, lastd, k character buf(ARB) integer mvnext, gtask include shflag snode = node repeat { junk = mvnext(node, type, dir) if (node == snode) #back to where we started break if (type == SEPCHAR & dir == RCHILD) call chcopy(SEPCHAR, buf, k) else if (type == AMPER) { if (dir == RCHILD | (dir == PARENT & lastd == LCHILD)) call chcopy(AMPER, buf, k) lastd = dir } else if (type == PIPE & dir == RCHILD) call chcopy(BAR, buf, k) else if (type == PAR) call gpar(node, dir, buf, k) else if (type == COM) getcl = gtask(node, buf, k) } return end #-h- gpar 613 asc 24-apr-81 13:02:25 [002,100] ## gpar - get parentheses info for script file subroutine gpar(node, dir, buf, i) integer node, dir, i, n character buf(ARB) integer pickup, length string rpst ") @@" if (dir == LCHILD) call chcopy(LPAREN, buf, i) else if (dir == PARENT) { n = i call stcopy(rpst, 1, buf, i) if( pickup(buf, i, node, REDIN, junk) != ERR) call chcopy(BLANK, buf, i) else i = n + 2 if ( pickup(buf, i, node, REDOUT, junk) != ERR) call chcopy(BLANK, buf, i) if ( pickup(buf, i, node, REDERR, junk) != ERR) call chcopy(BLANK, buf, i) } buf(i) = EOS return end #-h- gpname 372 asc 23-apr-81 10:31:56 [002,102] ## gpname - make unique pipe name for file id n subroutine gpname(n, name, i) character name(ARB) integer itoc, length integer i, junk, n include stdsub string pipef(5) "p" pfile(n) = YES # has been generated junk = itoc(n, pipef(2), 3) # generate scratf seed call scratf(pipef, name(i)) # generate name i = i + length(name(i)) # bump pointer return end #-h- gtask 918 asc 24-apr-81 13:02:26 [002,100] ## gtask - pick up command and arguments for background process integer function gtask(node, buf, j) integer node, junk, type, j, k, n integer pickup, cmdtyp character buf(ARB) include shflag n = j junk = pickup(buf, j, node, CMD, junk) k = j + 1 type = cmdtyp(buf(n), buf(k)) if (type == ERR & drop == NO) { call remark ('invalid task.') j = n buf(j) = EOS gtask = ERR return } gtask = OK #pick up arguments call chcopy(BLANK, buf, j) for (i=1; pickup(buf, j, node, ARGUMENT, i) != ERR; i=i+1) call chcopy(BLANK, buf, j) call chcopy(ESCAPE, buf, j) if( pickup(buf, j, node, REDIN, junk) != ERR) call chcopy(BLANK, buf, j) else j = j - 1 if ( pickup(buf, j, node, REDOUT, junk) != ERR) call chcopy(BLANK, buf, j) if ( pickup(buf, j, node, REDERR, junk) != ERR) call chcopy(BLANK, buf, j) buf(j) = EOS return end #-h- herdoc 697 asc 23-apr-81 10:31:59 [002,102] ## herdoc - generate 'here document' for shell subroutine herdoc(char, buf, i) character char, buf(ARB), doc(4) integer create, getlin integer int, i, n include stdsub include shflag include csclin data doc(1), doc(2), doc(3), doc(4) /LETD, LETO, LETC, EOS/ n = i call chcopy(LESS, buf, i) call scratf(doc, hfile) int = create(hfile, WRITE) if (int == ERR) { call remark ("can't open 'here document'.") hfile(1) = EOS i = n buf(i) = EOS return } call stcopy(hfile, 1, buf, i) while(getlin(lin, shin) != EOF) { if (lin(1) == char) break call putlin(lin, int) } call close(int) return end #-h- inf 817 asc 23-apr-81 10:32:00 [002,102] ## inf - pick up input substitution for command integer function inf(node, buf, i) integer node, i, n integer pickup character buf(ARB), char include stdsub include cpars n = i if (in > 0 & cin(in) < 0) #receive input from pipe { call chcopy(LESS, buf, i) call gpname (abs(cin(in)), buf, i) } else if (pickup(buf, i,node, REDIN,junk) == ERR & in > 0) call stcopy (ibuf(cin(in)), 1, buf, i) else if (script == YES & input(1) != EOS) { call chcopy(LESS, buf, i) call stcopy(input, 1, buf, i) } if (buf(n) == LESS & buf(n+1) == LESS) #check for 'here document' { char = buf(n+2) i = n call herdoc (char, buf, i) } if (buf(n) != EOS) inf = OK else inf = ERR return end #-h- initsh 2162 asc 29-apr-81 18:18:42 [002,100] ## initsh - initialize shell subroutine initsh integer getarg, open, length, loccom, index integer i include shflag include shcmd include stdsub string suffix NO_SUFFIX data logout(1), logout(2), logout(3), logout(4), logout(5), logout(6), logout(7) /LETL, LETO, LETG, LETO, LETU, LETT, EOS/ data cd/LETC, LETD, EOS/ data home/LETH, LETO, LETM, LETE, EOS/ data von/LETV, LETO, LETN, EOS/ data voff/LETV, LETO, LETF, LETF, EOS/ data xon/LETX, LETO, LETN, EOS/ data xoff/LETX, LETO, LETF, LETF, EOS/ data input(1) /EOS/ # initialize standard input file data shin /STDIN/ call pbinit # initialize push-back buffer prlin = NO exec = YES prcom = NO carg = NO drop = YES script = NO # initialize search path # search path is :~home:~usr:~bin call impath(spath) # fetch search path call homdir(homedr) # get user's home directory call gwdir(olddir, LOCAL) # previous directory is current work dir call enbint # enable kil interrupt handling for (i=1; getarg(i, clin, MAXLINE) != EOF; i=i+1) { if (i == 1 & clin(1) == MINUS) #shell flag { call fold(clin) if (index(clin, LETV) > 0) prlin = YES if (index(clin, LETX) > 0) prcom = YES if (index(clin, LETC) > 0) carg = YES if (index(clin, LETN) > 0) exec = NO if (index(clin, LETD) > 0) drop = NO call delarg(i) i = i - 1 } else if (carg == YES) { call arglin(clin, i) cargdn = NO break } else if (i == 1) { if (loccom(clin, spath, suffix, clin) != ASCII) call cant(clin) shin = open(clin, READ) if (shin == ERR) call cant(clin) script = YES } else if (clin(1) == ESCAPE) { if (clin(2) == LESS) call scopy(clin, 3, input, 1) else next call delarg (i) i = i - 1 } } return end #-h- mktoks 1175 asc 23-apr-81 10:32:05 [002,102] ## mktoks - create parse tables for shell parser integer function mktoks (line, k) integer length integer i, paren character line(ARB) integer shtok integer k, l include cpars paren = 0 i = 1 call putbak (EOS) #initialize buffer call pbstr (line) for (k=1; shtok(ibuf(i)) != EOS; k=k+1) { if (ibuf(i) != EOS) { tkbuf(IBPTR,k) = i tkbuf(TMARK,k) = 0 tkbuf(NODEPTR,k) = 0 tkbuf(ESCAN,k) = 0 if (ibuf(i) == LPAREN) paren = paren + 1 else if (ibuf(i) == RPAREN) paren = paren - 1 l = i + length(ibuf(i)) - 1 if ((ibuf(i) == SQUOTE | ibuf(i) == DQUOTE) & ibuf(l) != ibuf(i)) { call remark('unbalanced quotes.') mktoks = ERR return } i = l + 2 } } k = k - 1 ibuf(i) = EOS if (paren != 0) mktoks = ERR else mktoks = OK return end #-h- mktree 942 asc 23-apr-81 10:32:07 [002,102] ## mktree - create child node for given parent integer function mktree (pnode, type, size, cnode) integer pnode, type, size, cnode, i include cpars cnode = treend treend = treend + size #next available space if (treend >TREESIZE) { call stxerr ('mktree - tree buffer size exceeded.') cnode = ERR mktree = ERR return } for (i=1; i<=size; i=i+1) #clear entries tree(cnode+i) = 0 tree(cnode+PARENT) = pnode tree(cnode+NTYPE) = type if (pnode >= 0) #install back pointer { if (tree(pnode+LCHILD) == 0) tree(pnode+LCHILD) = cnode else if (tree(pnode+RCHILD) == 0) tree(pnode+RCHILD) = cnode else { call stxerr ('mktree--too many children.') mktree = ERR return } } mktree = cnode return end #-h- mvnext 430 asc 23-apr-81 10:32:09 [002,102] ## mvnext - move to next node in parse tree integer function mvnext (node, type, dir) integer node, dir, type integer nxtbr include cpars if (node == ROOT) #just starting { mvnext = 0 dir = LCHILD } else mvnext = tree(node+dir) if (mvnext != ROOT) { type = tree(mvnext+NTYPE) dir = nxtbr(mvnext, node) } node = mvnext return end #-h- nextp 230 asc 23-apr-81 10:32:10 [002,102] ## nextp - get next pointer from pushdown stack integer function nextp (p) integer p include cpars if (pp == 0) p = EOS else { p = stack(pp) pp = pp - 1 } nextp = p return end #-h- nxtbr 498 asc 23-apr-81 10:32:11 [002,102] ## nxtbr - determine next direction for moving in parse tree integer function nxtbr (node, lnode) integer node, lnode include cpars if (lnode == tree(node+PARENT)) #going down { if (tree(node+LCHILD) != 0) nxtbr = LCHILD else nxtbr = PARENT } else if (lnode == tree(node+LCHILD) & #going up tree(node+RCHILD) != 0) nxtbr = RCHILD else nxtbr = PARENT return end #-h- outf 742 asc 23-apr-81 10:32:12 [002,102] ## outf - pick up output file substitution for command integer function outf (node, buf, i) integer node, i integer pickup integer junk character buf(ARB) include cpars include stdsub outf = ERR if (out == 0 & pickup(buf,i,node,REDOUT,junk) != ERR) outf = OK else if (out > 0) #check for pipes and parens { if (aout(out) != 0) #append call chcopy(GREATER, buf, i) if (cout(out) > 0) #use paren substitution call stcopy (ibuf(cout(out)), 1, buf, i) else #pipe { call chcopy(GREATER, buf, i) call gpname(abs(cout(out)), buf, i) } outf = OK } return end #-h- param 638 asc 23-apr-81 10:32:14 [002,102] ## param - handle parameter substitution for the shell integer function param(c) character c, num(2), ngetch integer getarg, ctoi, i, junk include shflag include csclin if (c == DOLLAR) #handle param substitution { num(1) = ngetch(num(1), shin) num(2) = EOS i = 1 n = ctoi(num,i) if (n > 0) { if (getarg(n+1, lin, MAXLINE) != EOF) call pbstr(lin) c = ngetch(c, shin) } else c = num(1) param = YES } else param = NO return end #-h- parser 1579 asc 23-apr-81 10:32:15 [002,102] ## parser - parse shell command line # syntax : empty # | syn1 # syn1 : syn2 # | syn2 & syntax # | syn2 ; syntax # syn2 : syn3 # | syn3 | syn2 # syn3 : (syn1) [out] [?errout] # | tok tok* [out] [?errout] integer function parser (line) integer p, p1, p2, mark integer syntax, syn1, syn2, syn3 integer mktoks, nextp character line(ARB) include cpars include shflag treend = 0 #initialize tree pointer pp = 0 #initialize stack pointer for (i=1; i<=TSIZE; i=i+1) #initialize token table for (j=1; j<=MAXTOK; j=j+1) tkbuf(i,j) = 0 for (i=1; i<=TREESIZE; i=i+1) #initialize parse tree tree(i) = 0 for (i=1; i<=MAXLINE; i=i+1) #initialize input buffer ibuf(i) = EOS if (mktoks(line,p2) == ERR) { parser = ERR return } tkbuf(TMARK, 1) = SSYNTAX tkbuf(NODEPTR, 1) = -1 tkbuf(ESCAN, 1) = p2 call putbac (1) while (nextp(p1) != EOS) #generate parse tree { mark = tkbuf(TMARK, p1) p2 = tkbuf(ESCAN, p1) if (mark == SSYNTAX) parser = syntax(p1, p2) else if (mark == SSYN1) parser = syn1(p1, p2) else if (mark == SSYN2) parser = syn2(p1,p2) else if (mark == SSYN3) parser = syn3(p1,p2) if (parser == ERR) return } parser = OK return end #-h- pastbl 210 asc 23-apr-81 10:32:17 [002,102] ## pastbl - read past blanks and tabs on input subroutine pastbl (c) character c character ngetch include shflag for (c=ngetch(c, shin); c == BLANK | c == TAB; c=ngetch(c, shin)) ; return end #-h- pickup 714 asc 23-apr-81 10:32:18 [002,102] ## pickup - pick up character string from parse tree integer function pickup (array, i, node, field, arg) integer node, field, arg, i character array(ARB) include cpars pickup = OK if ( (field == REDIN | field == REDOUT | field == REDERR) & (tree(node+NTYPE) == COM | tree(node+NTYPE) == PAR) & tree(node+field) != 0 ) call stcopy (ibuf(tree(node+field)), 1, array, i) else if (field == CMD & tree(node+NTYPE) == COM) call stcopy(ibuf(tree(node+CMD)), 1, array, i) else if (field == ARGUMENT & tree(node+NTYPE) == COM & arg <= tree(node+NENT) ) call stcopy (ibuf(tree(node+CMD+arg)),1,array,i) else pickup = ERR return end #-h- putbac 224 asc 23-apr-81 10:32:19 [002,102] ## putbac - put pointer on pushdown stack subroutine putbac (p) integer p include cpars pp = pp + 1 if (pp > MAXSTACK) call stxerr ('putbac--stack size exceeded.') else stack(pp) = p return end #-h- pwdir 156 asc 23-apr-81 10:32:20 [002,102] subroutine pwdir(int) character file(FILENAMESIZE) integer int call gwdir(file, PATH) call putlin(file, int) call putch(NEWLINE, int) return end #-h- qs 584 asc 23-apr-81 10:32:21 [002,102] ## qs - handle extract quoted string token in shell subroutine qs(char, tok) character c, tok(ARB), char integer j, junk integer param character ngetch include shflag tok(1) = char j = 2 for (c=ngetch(c,shin); c != EOS; c=ngetch(c, shin)) { # if (c == DOLLAR & tok(j-1) == ESCAPE) #escape dollar # j = j - 1 # else # junk = param(c) if (c == EOS) break tok(j) = c j = j + 1 if (c == char) #done break } tok(j) = EOS return end #-h- scrf 1430 asc 29-apr-81 18:18:48 [002,100] ## scrf - prepare script file for execution by shell subroutine scrf (node, comand, args) character comand(ARB), args(ARB) integer pickup, inf, outf, errf, length, loccom integer i, j, type include shflag include stdsub string suffix IMAGE_SUFFIX string prflag "-v " string cmflag "-x " string drflag "-d " string shstr "sh" # handle scripts by spawning the shell with the script as input j = 1 call stcopy(shstr, 1, args, j) call chcopy(BLANK, args, j) if (prlin == YES) #pass along shell flags call stcopy(prflag, 1, args, j) if (prcom == YES) call stcopy(cmflag, 1, args, j) if (drop == NO) call stcopy(drflag, 1, args, j) # The shell becomes the main command and the script file name # becomes an argument to the shell call stcopy(comand, 1, args, j) if (loccom(shstr, spath, suffix, comand) != BINARY) { call remark("Cannot locate shell image file.") return } call chcopy(BLANK, args, j) for (i=1; pickup(args, j, node, ARGUMENT, i) != ERR; i=i+1) #pick up args call chcopy(BLANK, args, j) call chcopy(ESCAPE, args, j) if (inf(node, args, j) != ERR) #pick up STDIN substitution call chcopy(BLANK, args, j) else j = j - 1 if (outf(node, args, j) != ERR) #pick up STDOUT substitution call chcopy(BLANK, args, j) if (errf(node, args, j) != ERR) #pick up ERROUT substitution call chcopy(BLANK, args, j) args(j) = EOS return end #-h- setree 341 asc 23-apr-81 10:32:24 [002,102] ## setree - put 'value' in given node at given position integer function setree (node, posn, value) integer node, posn, value include cpars i = node + posn if (tree(i) != 0) { call stxerr ('setree--doubly defined argument.') setree = ERR return } tree(i) = value setree = OK return end #-h- shcom 419 asc 23-apr-81 10:32:25 [002,102] ## shcom - see if command is shell command integer function shcom(comand) character comand(ARB) integer equal include shcmd if (equal(comand, logout) == YES | equal(comand, cd) == YES | equal(comand, home) == YES | equal(comand, von) == YES | equal(comand, voff) == YES | equal(comand, xon) == YES | equal(comand, xoff) == YES) shcom = YES else shcom = NO return end #-h- shellc 1315 asc 23-apr-81 10:32:27 [002,102] ## shellc - execute shell command integer function shellc (comand, args) character comand(ARB), args(ARB), temp(FILENAMESIZE) integer equal, cwdir, i, status include shcmd include stdsub include shflag status = OK if (equal(comand, logout) == YES) call endsh else if (equal(comand, cd) == YES) { call strcpy(args, temp) call gwdir(sh, LOCAL) # fetch current working directory if (temp(1) == EOS) # bare cd => back to previous dir call strcpy(olddir, temp) if (cwdir(temp) == ERR) # oops, no such directory { call putlin(temp, ERROUT) call remark(" : directory does not exist.") status = ERR } else { call strcpy(sh, olddir) # store previous directory call pwdir(ERROUT) # verify change to user } } else if (equal(comand, home) == YES) { call gwdir(sh, LOCAL) # fetch current working directory if (cwdir(homedr) == ERR) { call remark("home command failed.") status = ERR } else { call strcpy(sh, olddir) call pwdir(ERROUT) } } else if (equal(comand, von) == YES) prlin = YES else if (equal(comand, voff) == YES) prlin = NO else if (equal(comand, xon) == YES) prcom = YES else if (equal(comand, xoff) == YES) prcom = NO else call remark ('invalid shell command.') return(status) end #-h- shline 757 asc 23-apr-81 10:32:29 [002,102] ## shline - prompt and get input line for shell integer function shline (line) character line(ARB) character clower integer length, prompt, equal character tmpara(5) integer k include shflag include shcmd string pchar "% " if (carg == YES) #get input from command line { if (cargdn == YES) { line(1) = EOS k = EOF } else { call strcpy(clin, line) cargdn = YES k = length(line) } } else k = prompt(pchar, line, shin) if (k != EOF) { for (i=1; i <= 4 & i <= k; i=i+1) tmpara(i) = clower(line(i)) tmpara(i) = EOS if (prlin == YES & equal(tmpara, voff) == NO) #user wishes to see line call putlin(line, ERROUT) } return(k) end #-h- shtok 2065 asc 23-apr-81 10:32:30 [002,102] ## shtok - extract next shell token integer function shtok (tok) character tok(ARB), c, ngetch integer spec, param, atbeg integer i, j, pstat include shflag repeat #loop until non-null token found { call pastbl(c) #skip leading blanks & tabs j = 1 if (spec(c) == YES) #single shell special character { tok(1) = c tok(2) = EOS shtok = c return } if (c == SQUOTE | c == DQUOTE) #quoted string { call qs(c, tok) shtok = tok(1) return } if (c == LESS | c == GREATER | c == QMARK) #redirected IO for (i=1; i<=2; i=i+1) { tok(j) = c j = j + 1 call pastbl(c) if (c != tok(j-1)) break } for ( ; c != EOS; n=ngetch(c, shin)) { pstat = param(c) if (c == EOS) break if (atbeg(c) == YES) { call putbak(c) break } if (c == ESCAPE) { c = ngetch(c, shin) if (spec(c) == NO & #ignore if not shell char. (c != ESCAPE & c != DOLLAR)) { call putbak(c) c = ESCAPE } } tok(j) = c j = j + 1 } tok(j) = EOS shtok = tok(1) if (pstat == NO | j < 1) return #continue if null token produced by empty parameter substitution pstat = NO } end #-h- spec 440 asc 23-apr-81 10:32:33 [002,102] ## spec - handle special characters in shell commands integer function spec (c) character c character sp(8) data sp(1), sp(2), sp(3), sp(4), sp(5), sp(6), sp(7), sp(8) /AMPER, LPAREN, RPAREN, SEPCHAR, BAR, CARET, NEWLINE, EOS/ if (index(sp, c) != 0) { spec = YES if (c == CARET) #allow CARET for PIPE c = BAR } else spec = NO return end #-h- stripb 163 asc 23-apr-81 10:32:34 [002,102] subroutine stripb(buf) integer i integer length character buf(ARB) for (i=length(buf); i > 0; i=i-1) if (buf(i) != BLANK) break buf(i+1) = EOS return end #-h- stxerr 216 asc 23-apr-81 10:32:35 [002,102] ## stxerr - report syntax error subroutine stxerr (reason) character reason(ARB) include cpars call putlin('syntax error: ', ERROUT) call putlin (reason, ERROUT) call putch (NEWLINE, ERROUT) return end #-h- syn1 1432 asc 23-apr-81 10:32:36 [002,102] ## syn1 - parse shell syntax level 1 # SYN1 -> SYN2 # -> SYN2 ; SYNTAX # -> SYN2 & SYNTAX integer function syn1 (p1,p2) character tok integer p, p1, p2, node, l, pt, ndx integer mktree include cpars l = 0 for (p=p1; p 0) call stxerr ('syn1--unbalanced left parentheses.') else { tkbuf(TMARK, p1) = SSYN2 call putbac (p1) } syn1 = OK return end #-h- syn2 1226 asc 23-apr-81 10:32:38 [002,102] ## syn2 - parse shell syntax level 2 # SYN2 -> SYN3 # -> SYN3 | SYN2 integer function syn2 (p1,p2) character tok integer p, p1, p2, l, node, pt, ndx integer mktree include cpars l = 0 for (p=p1; p (SYN1) [out] [?errout] # -> word word* [out] [?errout] integer function syn3 (p1,p2) integer p1, p2, ndx integer dopar, doverb include cpars if (p1 >= p2) { call stxerr ('syn3--empty command.') syn3 = ERR return } ndx = tkbuf(IBPTR, p1) if (ibuf(ndx) == LPAREN) syn3 = dopar(p1,p2) else syn3 = doverb(p1,p2) return end #-h- syntax 579 asc 23-apr-81 10:32:41 [002,102] ## syntax - parse shell syntax level zero # SYNTAX -> EMPTY # -> SYN1 integer function syntax (p1,p2) integer p, p1, p2, ndx character tok include cpars for (p=p1; p', '?', '>>', and '??'. An argument of the form 'name' causes file 'name' to be used as the standard output. An argument of the form '?name' causes the file 'name' to be used as the standard error output. Arguments of the form '>>name' or '??name' cause program output to be appended to 'name' for standard output or error output respectively. If 'name' does not exist, it will be created. Most tools have the capability to read their input from a series of files. In this case, the list of files overrides reading from standard input. However, many of the tools allow the user to read from both a list of files and from input by specifying the filename '-' for standard input. For example, .ce roff file1 - file2 would read its input from 'file1', then from the standard input, then from 'file2'. Filters and Pipes. .br The output from one command may be directed to the input of another. A sequence of commands separated by vertical bars (|) or carets ('^') causes the shell to arrange that the standard output of each command be delivered to the standard input of the next command in sequence. Thus in the command line: .ce sort list | uniq | crt 'sort' sorts the contents of file 'list'; its output is passed to 'uniq', which strips out duplicate lines. The output from 'uniq' is then input to 'crt', which prepares the lines for viewing on the user's crt terminal. The vertical bar is called a 'pipe'. Programs such as 'sort', 'uniq', and 'crt', which copy standard input to standard output (making some changes along the way) are called 'filters'. Command separators .br Commands need not be on different lines; instead they may be separated by semicolons: .ce ar t file; ed The above command will first list the contents of the archived file 'file', then enter the editor. The shell also allows commands to be grouped together with parentheses, where the group can then be used as a filter. For example, .ce (date; cat chocolate) | comm vanilla writes first the date and then the file 'chocolate' to standard output, which is then read as input by 'comm'. This tool compares the results with existing file 'vanilla' to see which lines the two files have in common. Multitasking .br On many systems the shell also allows processes to be executed in the background. If a command is followed by '&', the shell will not wait for the command to finish before prompting again; instead, it is ready immediately to accept a new command. For instance, .ce rat4 ambrose >george & preprocesses the file 'ambrose', putting the output on 'george'. No matter how long the compilation takes, the shell returns immediately. The identification number of the process running that command is printed. This identification may be used to wait for the completion of the command or to terminate it. The '&' may be used several times in a line. Parentheses and pipes are also allowed (within the same background process). Script files. .br The shell itself is a command, and may be called recursively, either implicitly or explicitly. This is primarily useful for executing files containing lines of shell commands. For instance, suppose you had a file named 'nbrcount' which looked like this: .in +15 .nf echo 'Counting strings of digits' tr $4 ar u loveletters $4 .fi .in -15 Then, executing the command .ce private Dan John Harold fair would merge the files 'Dan', 'John', and 'Harold', encrypt them, and store them away in an archive under the name 'fair'. Script files may be used as filters in pipelines just like regular commands. Script files sometimes require in-line data to be available to them. A special input redirection notation "<<" is used to achieve this effect. For example, the editor normally takes its commands from the standard input. However, within a shell procedure commands could be embedded this way: ed file <', where is the character one needs to type in conjunction with the CTRL key to generate the control character. !b[rowse] [n][l] This command is a synonym for history. It is included to increase the similiarity of function with the editor. .ne 30 .ti -3 2. history recall ![line_number][;line_number]... This command permits the recall of a command from the history for re-execution. The command so recalled is displayed to the user and then passed on to the shell for execution. This command is then entered at the bottom of the history. Valid line_numbers are the same as those for the editor. For example, a line_number may be the number listed next to the command in the history display, a pattern of the form "\pattern[\]", which indicates a backward search in the 25 line history window, or a pattern of the form "/pattern[/]", indicating a search forward, wrapping to the start of the 25 line window. The trailing '\' or '/' are optional when specifying a single pattern. The semi-colon syntax is the same as that in `ed', indicating that the search for the second pattern is to start at the line where the first pattern was found. If the pattern specified was illegal, or a line matching the pattern could not be found, or an invalid line_number was specified, a comment is displayed to the user # invalid line number and the user is prompted for more input. The history is not modified in this case. All sequences of patterns resolve into a single line number. It is not possible to request a range of lines from the history. It should be noted that the line_numbering is completely regular with `ed'. In particular, "!" followed by nothing maps into a fetch of the current line (last command typed). See the writeup on `ed' for more details on the specification of line_numbers. .ti -3 3. history recall and modification ![line_number]s/pat/repl[/[g]] Upon successfully recalling a command from the history, it may be modified before it is passed on to `hsh' for execution. This is performed with the 's' command, which is exactly the same as that for `ed'. The delimiters for `pat' and `repl' may be any character, the remembered pattern feature is available, and the trailing delimiter after the replacement pattern is optional. The optional trailing 'g' indicates substitution for all occurrences of 'pat' in the line. See the `ed' manual entry for more information on the substitute command. If the substitution fails for any reason, a comment is displayed to the user # illegal substitution and the user is prompted for more input. The history is not modified in this case. .ti -3 4. history archiving !w[rite] [>[>]]file This command permits the user to archive (save) the entire transcript of activity to a file. It also passes an EOF to `hsh', which causes `hsh' to terminate execution. The commands !w file .br !w >file both cause `file' to be overwritten with the transcript, while >>file causes the transcript to be appended to `file'. It should be noted that the !w command causes ALL of the input given to `hsh' in this session to be saved, not just the current 25 line window. It also passes an EOF to `hsh', which will terminate execution. .ti -3 5. history deletion !q[uit] .br ^Z These commands cause an EOF to be sent to `hsh' and the deletion of the log of activity. .in -5 .ne 30 Lines consisting solely of a carriage return are NOT logged in the history. If the user needs to perform several edits on a command before having it executed, he can exploit the fact that lines beginning with a sharp (#) are comments to the shell. For example: .in +5 .nf !\%ed\s/%/#/ !s/pat1/repl1/ .in +6 .cc * . . . . . . *cc . .in -6 !s/patn/repln/ !s/%#// .fi .in -5 All of the intermediate comment lines will be placed in the history, displacing other lines from the window which may possibly be needed. Of course, it may be simpler in such cases to just enter the command by hand. .sp 1 .ti -7 FILES .br Creates a scratch file ~tmp/pid.log for the command transcript. .sp 1 .ti -7 SEE ALSO .br .nf sh - command line interpreter rsh - shell with file recognition and RAW tty I/O ed - text editor .fi .sp 1 .ti -7 DIAGNOSTICS .br # invalid line number .br # invalid substitution .sp 1 .ti -7 AUTHORS .br Joe Sventek .sp 1 .ti -7 BUGS .br #-h- rsh.rof 2694 asc 11-may-81 13:17:15 [002,100] .bp .pl 60 .rm 70 .in 0 .he 'RSH'5/2/81'RSH' .fo ''-#-' .fi .in +7 .ti -7 NAME .br rsh - shell with file recognition and RAW tty I/O .sp 1 .ti -7 SYNOPSIS .br rsh [-cdnvx] [file] [arguments] .sp 1 .ti -7 DESCRIPTION .br `rsh' is identical to `sh' with the exception that the input to the shell is performed in RAW mode, such that word deletes and TENEX-style recognition is supported, which are described below. Consult the manual entry for `sh' for more information on the common functions. .sp `rsh' uses `rawpmt' instead of `prompt' to fetch its command lines. Since `rawpmt' does RAW io to the terminal for its input, it can apply its own semantics to control characters to provide enhanced functions to the user during terminal input. In the discussion below, the sequence ^ represents the character generated when the user hits the CTRL key in conjunction with the key. The special functions provided by `rsh' are: .sp .in +5 .ti -3 1. Both backspace (^H) and RUBOUT (RUB, DEL) may be used to delete the last character typed. .sp .ti -3 2. ^U may be used to undo the current line - i.e. delete it and re-prompt for the line. .sp .ti -3 3. ^R may be used to re-type the line. This is useful when working on a hard-copy terminal, since character deletes are done with backspaces. .sp .ti -3 4. ^W deletes the last word, where words are defined as strings of characters delimited by blanks, tabs and slashes. This permits the deletion of fields in file pathnames. .sp .ti -3 5. ^D causes the current working directory to be listed on the terminal, after which the line is re-displayed and the user may continue input on the current line. This is useful when one gets part way through a command, and then realizes that the critical file name has slipped from recent memory. .sp .ti -3 6. ^F causes file recognition to be performed on the current pathname. If the filename can be extended unambiguously, it will be; otherwise, a list of files matching the current pattern are displayed, the line re-displayed, and the user may continue input on the line. .sp .ti -3 7. ^V is similar to ^R, but any embedded control characters in the current line are displayed as ^. Only tabs (^I) and formfeeds (^L) are permitted to be embedded in command lines by `rawpmt' and are normally displayed as single carets (^), to permit the display width of each character to be 1. ^V lets the user distinguish control characters from real carets. .in -5 .sp 1 .ti -7 FILES .br .sp 1 .ti -7 SEE ALSO .br .nf sh - command line interpreter hsh - shell with history capability .fi .sp 1 .ti -7 DIAGNOSTICS .br .sp 1 .ti -7 AUTHORS .br Joe Sventek .sp 1 .ti -7 BUGS .br