http://upload.cn-dos.net/img/2311.zip
/*
* sh.c is an implementation of a shell interpreter under UNIX V6. The source code is located at http://minnie.tuhs.org/Archive/PDP-11/Distributions/research/Dennis_v6/
* UNIX v6 is free software protected by the BSD license. The original author of sh.c, Ken Thompson (1943 - ), initially implemented it in 1971 and ported it to Unix V6 in 1975.
* My main work is to re-annotate the source code and convert K&R C to ANSI C, with no other changes.
* This file compiles under gcc-4.4.3, but I cannot guarantee any problems caused by this shell in any interaction.
* Anyone can use it for learning, rewriting, or other purposes, please follow the BSD license agreement.
* Recent revision date: 2013-4-15
* Revisor: Leo Ma
* Contact information: begeekmyfriend@gmail.com
*/
#include <stdlib.h>
#define NULL ((void *)0)
#define INTR 2 /* Process termination signal */
#define QUIT 3 /* Same as above */
#define LINSIZ 1000 /* Command parameter line buffer space */
#define ARGSIZ 50 /* Token pointer list */
#define TRESIZ 100 /* All node space of the syntax tree */
#define QUOTE 0200 /* Quote character flag bit, indicating that the character prefix is a slash or quote, restricting the character set to ASCII, octal */
/* The following are the attribute identifiers of the syntax tree nodes, used for the DFLG field */
#define FAND 1 /* And: This command is executed in the background asynchronous process mode */
#define FCAT 2 /* Catenate: Redirect output in append mode, equivalent to the >> symbol */
#define FPIN 4 /* Pipe in: The command input is redirected to the pipeline, that is, flowing in from the left subtree */
#define FPOU 8 /* Pipe out: The command output is redirected to the pipeline, that is, flowing out to the right subtree */
#define FPAR 16 /* Parentheses: Identify the last simple command in the compound command, that is, the command before the right parenthesis. The shell will fork a child process for each external command (non-built-in) to execute, but the last sub-command in the compound command is still executed in the original process. */
#define FINT 32 /* Interrupt: If the command is executed asynchronously in the background, ignore the process termination signal. */
#define FPRS 64 /* Print string: Print the pid of the background process in the form of a string */
/* The following are the type declarations of the syntax tree nodes, used for the DTYP field */
#define TCOM 1 /* Command: Simple command */
#define TPAR 2 /* Parentheses: Compound command, a set of command sequences contained in round parentheses */
#define TFIL 3 /* Filter: Filter or pipeline, indicated by a single '|' or '^' symbol */
#define TLST 4 /* List: Command sequence, a set of multiple simple commands, separated by a single; or '&' or '\n' symbol */
/* The following are the fields of the syntax tree nodes, specifying the node size */
#define DTYP 0 /* Node type, unique identifier */
#define DLEF 1 /* Left subtree node, depending on the type of the current node */
#define DRIT 2 /* Right subtree node, depending on the type of the current node */
#define DFLG 3 /* Node attribute, affecting the state and execution mode of the command it belongs to */
#define DSPR 4 /* If it is a compound command type, this field is the sub-syntax tree node */
#define DCOM 5 /* Command parameter string */
#define ENOMEM 12 /* Command execution failed, insufficient memory space */
#define ENOEXEC 8 /* Command execution failed, executable file not found */
char *dolp; /* Dollar: Points to the special variable starting with '$' */
char pidp; /* Process pid stored in string form */
char **dolv; /* Command line parameter sequence separated by spaces */
int dolc; /* Number of command line parameters */
char *promp; /* Input prompt, not empty means interactive mode, empty means non-interactive mode */
char *linep; /* Command parameter buffer pointer, stores the position of the next character */
char *elinep; /* End of command parameter buffer */
char **argp; /* Token list pointer, stores the position of the next token */
char **eargp; /* End of token list */
int *treep; /* Syntax tree list pointer, stores the position of the next node */
int *treeend; /* End of syntax tree list */
char peekc; /* Pre-read a character buffer */
char gflg; /* Two uses: list pointer overflow flag, wildcard flag */
char error; /* Error count */
char acctf; /* */
char uid; /* User id */
char setintr; /* Ignore interrupt signal flag */
char *arginp; /* When there is option '-c', scan command parameters from the string pointed to by this pointer */
int onelflg; /* When there is option '-t', scan command parameters from standard input */
/* Interrupt signal message list */
char *mesg = {
0,
"Hangup",
0,
"Quit",
"Illegal instruction",
"Trace/BPT trap",
"IOT trap",
"EMT trap",
"Floating exception",
"Killed",
"Bus error",
"Memory fault",
"Bad system call",
0,
"Sig 14",
"Sig 15",
"Sig 16",
"Sig 17",
"Sig 18",
"Sig 19",
};
/* Record system time */
struct stime {
int proct;
int cputim;
int systim;
} timeb;
char line; /* Command parameter buffer */
char *args; /* Token list buffer */
int trebuf; /* Syntax tree node buffer */
void main1();
void word();
char getc();
int *syntax(char **p1, char **p2);
int *syn1(char **p1, char **p2);
int *syn2(char **p1, char **p2);
int *syn3(char **p1, char **p2);
void execute(int *t, int *pf1, int *pf2);
void texec(char *f, int *at);
void err(char *s);
void prs(char *as);
void prn(int n);
void pwait(int i, int *t);
void acct(int *t);
void enacct(char *as);
void put(int c);
/* Shell entry */
void main(int c, char **av)
{
int f;
char *acname, **v;
for(f=2; f<15; f++) /* close_on_exec, because the shell may come from an external process or its own fork */
close(f);
if((f=dup(1)) != 2) /* Redirect stdout to stderr, dup() will return the smallest file handle that is not yet open */
close(f);
dolc = getpid(); /* Record current process pid */
for(f=4; f>=0; f--) {
pidp = dolc%10+'0'; /* itoa */
dolc = dolc/10;
}
v = av;
acname = "/usr/adm/sha"; /* SHA encryption sequence, currently deprecated in Unix versions */
promp = "% "; /* Normal user interactive prompt */
if(((uid = getuid())&0377) == 0)
promp = "# "; /* Root user interactive prompt */
acctf = open(acname, 1);
if(c > 1) {
promp = 0; /* Enter non-interactive mode */
if (*v=='-') { /* There are options */
**v = '-'; /* Use the first sequence character as a temporary mark */
if (v=='c' && c>2) /* '-c' option, scan from arginp */
arginp = v;
else if (v=='t') /* '-t' option, scan from stdin */
onelflg = 2;
} else { /* Directly scan from the command file */
close(0);
f = open(v, 0); /* Redirect stdin to the file */
if(f < 0) {
prs(v);
err(": cannot open");
}
}
}
if(**v == '-') {
setintr++; /* In non-interactive mode with options, initialize to ignore all signals */
signal(QUIT, 1);
signal(INTR, 1);
}
dolv = v+1;
dolc = c-1;
loop: /* Each loop executes a shell statement interpretation, until a newline is encountered, and loops indefinitely unless the entire shell process terminates */
if(promp != 0)
prs(promp); /* Print interactive prompt */
peekc = getc(); /* For I/O stream, pre-read a character for future reading again */
main1(); /* Enter interpreter */
goto loop;
}
/*
* This function contains the entire interpretation process of a shell interaction
* Preprocessor: Preprocess the input command parameters
* Lexical scanning: Lexically analyze the command parameters and split into token lists
* Syntax parser: Build syntax tree
* Semantic analyzer: Set syntax tree node attributes, I/O, etc.
* Executor: Allocate processes for each command and execute
*/
void main1()
{
char c, *cp;
int *t;
argp = args;
eargp = args+ARGSIZ-5; /* Initialize token list */
linep = line;
elinep = line+LINSIZ-5; /* Initialize command parameter buffer */
error = 0; /* Error count cleared */
gflg = 0; /* Overflow flag cleared */
do {
cp = linep;
word();
} while(*cp != '\n'); /* Scan all command parameters, generate token list, terminate when newline is encountered */
treep = trebuf;
treeend = &trebuf; /* Syntax tree buffer cleared */
if(gflg == 0) {
if(error == 0) {
setexit();
if (error)
return;
t = syntax(args, argp); /* Scanner has no buffer overflow, no error count, build syntax tree from token list */
}
if(error != 0)
err("syntax error"); else /* Syntax analysis error, prompt and exit shell */
execute(t, NULL, NULL); /* No error count, start semantic analysis from root node */
}
}
/*
* Scan and split tokens from command parameters
* readc directly reads a character from the stream, getc preprocesses the former character
*/
void word()
{
char c, c1;
*argp++ = linep; /* Take the current command parameter as a token into the list, end with '\0', argp points to the next token position */
loop:
switch(c = getc()) {
case ' ':
case '\t':
goto loop; /* Filter spaces and tab characters */
case '\'':
case '"':
c1 = c;
while((c=readc()) != c1) { /* Read one more character, judge if it is paired (such as quote) */
if(c == '\n') {
error++; /* Quote not ended, encounter newline, error count */
peekc = c; /* Push back pre-read character */
return;
}
*linep++ = c|QUOTE; /* Add quote flag */
}
goto pack; /* Quote ended, jump to pack */
case '&':
case ';':
case '<':
case '>':
case '(':
case ')':
case '|':
case '^':
case '\n':
*linep++ = c;
*linep++ = '\0'; /* Special characters such as meta-characters are returned as a single token */
return;
}
peekc = c; /* If it is a normal character, push back pre-read */
pack:
for(;;) {
c = getc();
if(any(c, " '\"\t;&<>()|^\n")) { /* Encounter meta-characters, separators, etc. */
peekc = c; /* Push back pre-read character */
if(any(c, "\"'"))
goto loop; /* Encounter quote character, jump to loop to continue scanning */
*linep++ = '\0'; /* Split out a token */
return;
}
*linep++ = c;
}
}
/* Allocate a syntax tree node, the size is specified by n, which is n*4 bytes */
int* tree(int n)
{
int *t;
t = treep;
treep += n;
if (treep>treeend) {
prs("Command line overflow\n"); /* Insufficient space */
error++;
reset();
}
return(t);
}
/*
* Read a character and do some preprocessing
* 1. Read from pre-read cache
* 2. Read directly from the stream
*/
char getc()
{
char c;
if(peekc) {
c = peekc; /* If there is a pre-read cache, read the cache and return */
peekc = 0; /* Clear pre-read cache */
return(c);
}
if(argp > eargp) { /* Token list overflow */
argp -= 10;
while((c=getc()) != '\n'); /* Take all characters before the newline */
argp += 10;
err("Too many args");
gflg++;
return(c);
}
if(linep > elinep) { /* Command parameter buffer overflow */
linep -= 10;
while((c=getc()) != '\n'); /* Take all characters before the newline */
linep += 10;
err("Too many characters");
gflg++;
return(c);
}
getd:
if(dolp) { /* There are command parameter characters prefixed with '$' */
c = *dolp++;
if(c != '\0')
return(c);
dolp = 0; /* The parameter prefixed with '$' is scanned, dolp is cleared */
}
c = readc(); /* Read a character from the stream */
if(c == '\\') {
c = readc(); /* Encounter escape character, continue to read the next character */
if(c == '\n')
return(' '); /* Slash at the end of the line, return space to continue the next line */
return(c|QUOTE); /* Quote this character */
}
if(c == '$') {
c = readc(); /* Encounter '$', continue to read a character */
if(c>='0' && c<='9') { /* Positional variable */
if(c-'0' < dolc) /* Positional parameter is within legal range */
dolp = dolv; /* dolp points to the substituted command parameter character */
goto getd; /* Jump to getd processing */
}
if(c == '$') { /* Current process pid */
dolp = pidp; /* dolp points to the pid string recorded earlier */
goto getd; /* Jump to getd processing */
}
}
return(c&0177); /* Normal character is returned directly, limited to ASCII */
}
/*
* Read a character from the stream
* arginp!=NULL (onelflg==0): With "-c" option, scan from the appended string
* arginp==1 (oneflg==0): With "-c" option, scanning ends, exit shell process
* onelflg==2(arginp==NULL): With "-t" option, scan from stdin
* onelflg==1(arginp==NULL): With "-t" option, scanning ends, exit shell process
*/
int readc()
{
char cc;
int c;
if (arginp) { /* "-c" option */
if (arginp == (void *)1)
exit(-1); /* No new command parameters, exit shell process */
if ((c = *arginp++) == 0) {
arginp = (void *)1; /* Scanning ends */
c = '\n'; /* Append newline to return */
}
return(c);
}
if (onelflg==1)
exit(0); /* "-t" option, scanning ends, exit shell process */
if(read(0, &cc, 1) != 1) /* Read a character from standard input */
exit(-1);
if (cc=='\n' && onelflg) /* onelflg == 2, "-t" option */
onelflg--; /* Scanning of one line is completed */
return(cc);
}
/*
* syntax
* empty
* syn1
*
* Start building the syntax tree from here, traverse the previously scanned token list
* p1 points to the head of the token list, p2 points to the next of the end of the token list
* Top-down three-level structure: syn1, syn2, syn3, function call relationship is as follows
* 1. Left subtree: The left list is advanced by one layer
* 2. Right subtree: The right list is backtracked or recursive
* 3. No symbol found: All lists are advanced by one layer
* 4. Generate node: Return directly
*/
int *syntax(char **p1, char **p2)
{
while(p1 != p2) {
if(any(**p1, ";&\n")) /* The list prefix is a command sequence separator character, filter out this token, encountered in syntax backtracking */
p1++; else
return(syn1(p1, p2)); /* All lists are advanced by one layer to syn1 */
}
return(0);
}
/*
* syn1
* syn2
* syn2 & syntax
* syn2 ; syntax
*
* syn1 generates a command sequence node, finds the separator,
* generates the left subtree from top to bottom, and backtracks to generate the right subtree.
* p1 points to the head of the token list, p2 points to the next of the end of the token list.
*/
int *syn1(char **p1, char **p2)
{
char **p;
int *t, *t1;
int l;
l = 0;
for(p=p1; p!=p2; p++)
switch(**p) {
case '(':
l++;
continue; /* Equivalent to break here */
case ')':
l--;
if(l < 0)
error++;
continue;
case '&':
case ';':
case '\n':
if(l == 0) { /* No parentheses nesting */
l = **p;
t = tree(4);
t = TLST; /* Generate command sequence node */
t = (int)syn2(p1, p); /* The left list is advanced by one layer to generate the left subtree node */
t = 0; /* Command sequence node does not set any flags */
if(l == '&') {
t1 = (int *)t;
t1 |= FAND|FPRS|FINT; /* If there is a background process, its attributes are pushed down to the left subtree node */
}
t = (int)syntax(p+1, p2); /* The right list backtracks to syntax to generate the right subtree node */
return(t);
}
}
if(l == 0)
return(syn2(p1, p2)); /* No command separator found, all lists are advanced by one layer to syn2 */
error++; /* If nesting is not zero, an error occurs */
}
/*
* syn2
* syn3
* syn3 | syn2
*
* syn2 is responsible for building the pipeline node, searching for pipeline symbols,
* generates the left subtree from top to bottom, and recursively generates the right subtree.
* p1 points to the head of the token list, p2 points to the next of the end of the token list.
*/
int *syn2(char **p1, char **p2)
{
char **p;
int l, *t;
l = 0;
for(p=p1; p!=p2; p++)
switch(**p) {
case '(':
l++;
continue; /* Equivalent to break here */
case ')':
l--;
continue;
case '|':
case '^':
if(l == 0) { /* No parentheses nesting */
t = tree(4);
t = TFIL; /* Build pipeline node */
t = (int)syn3(p1, p); /* The left list is advanced by one layer to generate the left subtree node */
t = (int)syn2(p+1, p2); /* The right is recursively generated to the right subtree node */
t = 0; /* Flags are inherited from the upper node */
return(t);
}
}
return(syn3(p1, p2)); /* No pipeline found, all lists are advanced by one layer, no need to judge whether nesting is zero */
}
/*
* syn3
* ( syn1 )
* word word*
*
* syn3 is responsible for building compound commands and simple command nodes. The former is marked by parentheses, and all token lists inside the parentheses are backtracked to syn1 to build sub-command nodes;
* A simple command is a leaf of the syntax tree. Its left and right subtree nodes are I/O redirect files, and store command parameter strings. After generating the node, return directly.
* p1 points to the head of the token list, p2 points to the next of the end of the token list.
*/
int *syn3(char **p1, char **p2)
{
char **p;
char **lp, **rp;
int *t;
int n, l, i, o, c, flg;
flg = 0;
if(**p2 == ')')
flg |= FPAR; /* The last sub-command in the compound command does not need to fork a child process */
lp = 0; /* Start of sub-command */
rp = 0; /* End of sub-command */
i = 0; /* Input file path */
o = 0; /* Output file path */
n = 0; /* Character count */
l = 0; /* Nesting level */
for(p=p1; p!=p2; p++)
switch(c = **p) {
case '(':
if(l == 0) {
if(lp != 0)
error++;
lp = p+1; /* The outermost layer of the current nesting, lp points to the first character of the sub-command */
}
l++;
continue; /* Same as break */
case ')':
l--;
if(l == 0)
rp = p; /* The outermost layer of the current nesting, rp points to the ')' after the sub-command */
continue;
case '>':
p++;
if(p!=p2 && **p=='>') /* Append mode FCAT flag */
flg |= FCAT; else
p--;
/* Note that there is no break here, continue to go down */
case '<':
if(l == 0) {
p++;
if(p == p2) {
error++; /* No character behind, error occurs */
p--;
}
if(any(**p, "<>(")) /* Illegal character */
error++;
if(c == '<') {
if(i != 0)
error++;
i = (int)*p; /* Redirect to input file, only valid for simple commands */
continue;
}
if(o != 0)
error++;
o = (int)*p; /* Redirect to output file, only valid for simple commands */
}
continue;
default:
if(l == 0)
p1 = *p; /* Command parameters are moved forward to clear previous records, and counted with n */
}
if(lp != 0) {
if(n != 0)
error++; /* For compound commands, n must be 0 */
t = tree(5);
t = TPAR; /* Allocate compound command type node */
t = (int)syn1(lp, rp); /* All token lists in parentheses are backtracked to syn1 to build sub-command nodes */
goto out;
}
if(n == 0)
error++; /* For simple commands, n must not be 0 */
p1 = 0; /* String ends */
t = tree(n+5); /* Allocate simple command node, size is 5 fields (DSPR field is empty) plus the number of command characters */
t = TCOM;
for(l=0; l<n; l++)
t = (int)p1;
out:
t = flg; /* Inherent attributes or attributes inherited from upper nodes */
t = i; /* For simple commands, the left and right subtrees are redirected I/O files */
t = o; /* For compound commands, the left and right subtrees are empty */
return(t);
}
/* Scan all characters with function pointer f */
void scan(int *at, int (*f)())
{
char *p, c;
int *t;
t = at+DCOM;
while(p = (char *)*t++)
while(c = *p)
*p++ = (*f)(c);
}
/* Find wildcards */
int tglob(int c)
{
if(any(c, "
: Pipeline input end handle
* pf1: Idle
* pf2: Idle
* pf2: Pipeline output end handle
*/
void execute(int *t, int *pf1, int *pf2)
{
int i, f, pv;
int *t1;
char *cp1, *cp2;
extern int errno;
if(t != 0)
switch(t) {
case TCOM:
cp1 = (char *)t;
/* The following are built-in commands */
if(equal(cp1, "chdir")) {
if(t != 0) {
if(chdir(t) < 0)
err("chdir: bad directory");
} else
err("chdir: arg count");
return;
}
if(equal(cp1, "shift")) {
if(dolc < 1) {
prs("shift: no args\n");
return;
}
dolv = dolv;
dolv++;
dolc--;
return;
}
if(equal(cp1, "login")) {
if(promp != 0) {
close(acctf);
execv("/bin/login", t+DCOM);
}
prs("login: cannot execute\n");
return;
}
if(equal(cp1, "newgrp")) {
if(promp != 0) {
close(acctf);
execv("/bin/newgrp", t+DCOM);
}
prs("newgrp: cannot execute\n");
return;
}
if(equal(cp1, "wait")) {
pwait(-1, 0);
return;
}
if(equal(cp1, ":"))
return;
/* Note that there is no break here, after filtering out built-in commands, external commands will continue to go down */
case TPAR:
f = t;
i = 0;
if((f&FPAR) == 0) /* Except for the last sub-command in the compound command, other commands all fork child processes */
i = fork();
if(i == -1) {
err("try again");
return;
}
if(i != 0) { /* Parent process code */
if((f&FPIN) != 0) {
close(pf1); /* After fork, the child process gets a copy of the parent process's pipeline handle, we need to close it in the parent process to avoid resource leakage */
close(pf1); /* Note that here only close pf1, because pf1 and pf2 are both mirrors of pv, so closing pf1 is closing pf2 */
}
if((f&FPRS) != 0) {/* Print child process pid */
prn(i);
prs("\n");
}
if((f&FAND) != 0) /* Background asynchronous process does not need to wait */
return;
if((f&FPOU) == 0) /* The node is the end of the pipeline, wait for the child process to terminate */
pwait(i, t);
return;
}
/* The following is the child process code, or the command process of the FPAR attribute node */
if(t != 0) {
close(0);
i = open(t, 0); /* Redirect stdin to the left subtree node */
if(i < 0) {
prs((char *)t);
err(": cannot open");
exit(-1);
}
}
if(t != 0) {
if((f&FCAT) != 0) {
i = open(t, 1);
if(i >= 0) {
seek(i, 0, 2); /* Append mode */
goto f1;
}
}
i = creat(t, 0666); /* Create new file handle */
if(i < 0) {
prs((char *)t);
err(": cannot create");
exit(-1);
}
f1:
close(1);
dup(i); /* Redirect stdout to the right subtree node */
close(i);
}
if((f&FPIN) != 0) {
close(0);
dup(pf1); /* Redirect stdin to the pipeline input end */
close(pf1);
close(pf1); /* Child process closes pipeline handle */
}
if((f&FPOU) != 0) {
close(1);
dup(pf2); /* Redirect stdout to the pipeline output */
close(pf2);
close(pf2); /* Child process closes pipeline handle */
}
if((f&FINT)!=0 && t==0 && (f&FPIN)==0) {
close(0); /* If interrupt is ignored and there is no pipeline input, close stdin and redirect to /dev/null */
open("/dev/null", 0); /* To avoid interference from other processes' stdin, but stdout is reserved */
}
if((f&FINT) == 0 && setintr) {
signal(INTR, 0); /* Without FINT flag, restore interrupt signal response (default is ignored) */
signal(QUIT, 0);
}
if(t == TPAR) { /* If it is a compound type, execute the sub-command, and push down the FINT flag at the same time */
if(t1 = (int *)t)
t1 |= f&FINT;
execute(t1, NULL, NULL);
exit(0);
}
close(acctf);
gflg = 0;
scan(t, &tglob); /* Scan wildcards */
if(gflg) {
t = (int)"/etc/glob";
execv(t, t+DSPR); /* Commands containing wildcards are executed in /etc/glob */
prs("glob: cannot execute\n");
exit(-1);
}
scan(t, &trim); /* Remove quote flag */
*linep = 0;
texec((char *)t, t); /* Execute the command in the current directory first */
cp1 = linep;
cp2 = "/usr/bin/";
while(*cp1 = *cp2++) /* strcpy, cp1 stops at '\0' position */
cp1++;
cp2 = (char *)t;
while(*cp1++ = *cp2++); /* strcpy, '\0' ends */
texec(linep+4, t); /* Then execute the command in the /bin directory */
texec(linep, t); /* Finally execute the command in the /usr/bin directory */
prs((char *)t); /* If the process exits abnormally, it means the command is not found, exit the shell */
err(": not found");
exit(-1);
case TFIL:
f = t;
pipe(pv); /* Create pipeline, pv is input handle, pv is output handle */
t1 = (int *)t;
t1 |= FPOU | (f&(FPIN|FINT|FPRS));
execute(t1, pf1, pv); /* For the left subtree node, pv is passed in as the pipeline output handle, pv is idle */
t1 = (int *)t;
t1 |= FPIN | (f&(FPOU|FINT|FAND|FPRS)); /* Only the command at the end of the pipeline can inherit the FAND attribute, which is exclusive to FPOU */
execute(t1, pv, pf2); /* For the right subtree node, pv is passed in as the pipeline input handle, pv is idle */
return;
case TLST:
f = t&FINT; /* For the command sequence node, push down the FINT attribute to the left and right subtrees */
if(t1 = (int *)t)
t1 |= f;
execute(t1, NULL, NULL); /* Execute left first, then right in turn */
if(t1 = (int *)t)
t1 |= f;
execute(t1, NULL, NULL);
return;
}
}
/*
* Execute command
* f is the path of the executable file
* at is the syntax tree node
*/
void texec(char *f, int *at)
{
extern int errno;
int *t;
t = at;
execv(f, t+DCOM); /* Reload and execute the command, no need to set environment */
if (errno==ENOEXEC) {
if (*linep)
t = (int)linep;
t = (int)"/bin/sh";
execv(t, t+DSPR); /* Executable file not found for the command */
prs("No shell!\n");
exit(-1);
}
if (errno==ENOMEM) {
prs((char *)t);
err(": too large"); /* Insufficient memory */
exit(-1);
}
}
/* Print error, in non-interactive mode, clear stdin cache and exit shell process */
void err(char *s)
{
prs(s);
prs("\n");
if(promp == 0) {
seek(0, 0, 2);
exit(-1);
}
}
/* Print string */
void prs(char *as)
{
char *s;
s = as;
while(*s)
put(*s++);
}
/* Output a character */
void put(int c)
{
write(2, &c, 1);
}
/* itoa */
void prn(int n)
{
int a;
if(a=n/10)
prn(a);
put(n%10+'0');
}
/* Whether a character is contained in the string */
int any(int c, char *as)
{
char *s;
s = as;
while(*s)
if(*s++ == c)
return(1);
return(0);
}
/* String comparison */
int equal(char *as1, char *as2)
{
char *s1, *s2;
s1 = as1;
s2 = as2;
while(*s1++ == *s2)
if(*s2++ == '\0')
return(1);
return(0);
}
/*
* Parent process waits for child process to terminate
* i is the child process pid, -1 means wait for all child processes
* t is the syntax tree node
*/
void pwait(int i, int *t)
{
int p, e;
int s;
if(i != 0)
for(;;) {
times(&timeb);
time(timeb.proct);
p = wait(&s);
if(p == -1) /* Wait failed */
break;
e = s&0177; /* Reserve state environment */
if(mesg != 0) { /* Child process terminated abnormally */
if(p != i) {
prn(p);
prs(": ");
}
prs(mesg); /* Print corresponding message */
if(s&0200) /* Kernel crash */
prs(" -- Core dumped");
}
if(e != 0)
err(""); /* Wait for normal termination */
if(i == p) {
acct(t);
break;
} else
acct(0);
}
}
void acct(int *t)
{
if(t == 0)
enacct("**gok"); else
if(*t == TPAR)
enacct("()"); else
enacct((char *)t);
}
void enacct(char *as)
{
struct stime timbuf;
struct {
char cname;
char shf;
char uid;
int datet;
int realt;
int bcput;
int bsyst;
} tbuf;
int i;
char *np, *s;
s = as;
times(&timbuf);
time(timbuf.proct);
lsub(tbuf.realt, timbuf.proct, timeb.proct);
lsub(tbuf.bcput, timbuf.cputim, timeb.cputim);
lsub(tbuf.bsyst, timbuf.systim, timeb.systim);
do {
np = s;
while (*s != '\0' && *s != '/')
s++;
} while (*s++ != '\0');
for (i=0; i<14; i++) {
tbuf.cname = *np;
if (*np)
np++;
}
tbuf.datet = timbuf.proct;
tbuf.datet = timbuf.proct;
tbuf.uid = uid;
tbuf.shf = 0;
if (promp==0)
tbuf.shf = 1;
seek(acctf, 0, 2);
write(acctf, &tbuf, sizeof(tbuf));
}
[ Last edited by zzz19760225 on 2016-12-11 at 00:32 ]
/*
* sh.c is an implementation of a shell interpreter under UNIX V6. The source code is located at http://minnie.tuhs.org/Archive/PDP-11/Distributions/research/Dennis_v6/
* UNIX v6 is free software protected by the BSD license. The original author of sh.c, Ken Thompson (1943 - ), initially implemented it in 1971 and ported it to Unix V6 in 1975.
* My main work is to re-annotate the source code and convert K&R C to ANSI C, with no other changes.
* This file compiles under gcc-4.4.3, but I cannot guarantee any problems caused by this shell in any interaction.
* Anyone can use it for learning, rewriting, or other purposes, please follow the BSD license agreement.
* Recent revision date: 2013-4-15
* Revisor: Leo Ma
* Contact information: begeekmyfriend@gmail.com
*/
#include <stdlib.h>
#define NULL ((void *)0)
#define INTR 2 /* Process termination signal */
#define QUIT 3 /* Same as above */
#define LINSIZ 1000 /* Command parameter line buffer space */
#define ARGSIZ 50 /* Token pointer list */
#define TRESIZ 100 /* All node space of the syntax tree */
#define QUOTE 0200 /* Quote character flag bit, indicating that the character prefix is a slash or quote, restricting the character set to ASCII, octal */
/* The following are the attribute identifiers of the syntax tree nodes, used for the DFLG field */
#define FAND 1 /* And: This command is executed in the background asynchronous process mode */
#define FCAT 2 /* Catenate: Redirect output in append mode, equivalent to the >> symbol */
#define FPIN 4 /* Pipe in: The command input is redirected to the pipeline, that is, flowing in from the left subtree */
#define FPOU 8 /* Pipe out: The command output is redirected to the pipeline, that is, flowing out to the right subtree */
#define FPAR 16 /* Parentheses: Identify the last simple command in the compound command, that is, the command before the right parenthesis. The shell will fork a child process for each external command (non-built-in) to execute, but the last sub-command in the compound command is still executed in the original process. */
#define FINT 32 /* Interrupt: If the command is executed asynchronously in the background, ignore the process termination signal. */
#define FPRS 64 /* Print string: Print the pid of the background process in the form of a string */
/* The following are the type declarations of the syntax tree nodes, used for the DTYP field */
#define TCOM 1 /* Command: Simple command */
#define TPAR 2 /* Parentheses: Compound command, a set of command sequences contained in round parentheses */
#define TFIL 3 /* Filter: Filter or pipeline, indicated by a single '|' or '^' symbol */
#define TLST 4 /* List: Command sequence, a set of multiple simple commands, separated by a single; or '&' or '\n' symbol */
/* The following are the fields of the syntax tree nodes, specifying the node size */
#define DTYP 0 /* Node type, unique identifier */
#define DLEF 1 /* Left subtree node, depending on the type of the current node */
#define DRIT 2 /* Right subtree node, depending on the type of the current node */
#define DFLG 3 /* Node attribute, affecting the state and execution mode of the command it belongs to */
#define DSPR 4 /* If it is a compound command type, this field is the sub-syntax tree node */
#define DCOM 5 /* Command parameter string */
#define ENOMEM 12 /* Command execution failed, insufficient memory space */
#define ENOEXEC 8 /* Command execution failed, executable file not found */
char *dolp; /* Dollar: Points to the special variable starting with '$' */
char pidp; /* Process pid stored in string form */
char **dolv; /* Command line parameter sequence separated by spaces */
int dolc; /* Number of command line parameters */
char *promp; /* Input prompt, not empty means interactive mode, empty means non-interactive mode */
char *linep; /* Command parameter buffer pointer, stores the position of the next character */
char *elinep; /* End of command parameter buffer */
char **argp; /* Token list pointer, stores the position of the next token */
char **eargp; /* End of token list */
int *treep; /* Syntax tree list pointer, stores the position of the next node */
int *treeend; /* End of syntax tree list */
char peekc; /* Pre-read a character buffer */
char gflg; /* Two uses: list pointer overflow flag, wildcard flag */
char error; /* Error count */
char acctf; /* */
char uid; /* User id */
char setintr; /* Ignore interrupt signal flag */
char *arginp; /* When there is option '-c', scan command parameters from the string pointed to by this pointer */
int onelflg; /* When there is option '-t', scan command parameters from standard input */
/* Interrupt signal message list */
char *mesg = {
0,
"Hangup",
0,
"Quit",
"Illegal instruction",
"Trace/BPT trap",
"IOT trap",
"EMT trap",
"Floating exception",
"Killed",
"Bus error",
"Memory fault",
"Bad system call",
0,
"Sig 14",
"Sig 15",
"Sig 16",
"Sig 17",
"Sig 18",
"Sig 19",
};
/* Record system time */
struct stime {
int proct;
int cputim;
int systim;
} timeb;
char line; /* Command parameter buffer */
char *args; /* Token list buffer */
int trebuf; /* Syntax tree node buffer */
void main1();
void word();
char getc();
int *syntax(char **p1, char **p2);
int *syn1(char **p1, char **p2);
int *syn2(char **p1, char **p2);
int *syn3(char **p1, char **p2);
void execute(int *t, int *pf1, int *pf2);
void texec(char *f, int *at);
void err(char *s);
void prs(char *as);
void prn(int n);
void pwait(int i, int *t);
void acct(int *t);
void enacct(char *as);
void put(int c);
/* Shell entry */
void main(int c, char **av)
{
int f;
char *acname, **v;
for(f=2; f<15; f++) /* close_on_exec, because the shell may come from an external process or its own fork */
close(f);
if((f=dup(1)) != 2) /* Redirect stdout to stderr, dup() will return the smallest file handle that is not yet open */
close(f);
dolc = getpid(); /* Record current process pid */
for(f=4; f>=0; f--) {
pidp = dolc%10+'0'; /* itoa */
dolc = dolc/10;
}
v = av;
acname = "/usr/adm/sha"; /* SHA encryption sequence, currently deprecated in Unix versions */
promp = "% "; /* Normal user interactive prompt */
if(((uid = getuid())&0377) == 0)
promp = "# "; /* Root user interactive prompt */
acctf = open(acname, 1);
if(c > 1) {
promp = 0; /* Enter non-interactive mode */
if (*v=='-') { /* There are options */
**v = '-'; /* Use the first sequence character as a temporary mark */
if (v=='c' && c>2) /* '-c' option, scan from arginp */
arginp = v;
else if (v=='t') /* '-t' option, scan from stdin */
onelflg = 2;
} else { /* Directly scan from the command file */
close(0);
f = open(v, 0); /* Redirect stdin to the file */
if(f < 0) {
prs(v);
err(": cannot open");
}
}
}
if(**v == '-') {
setintr++; /* In non-interactive mode with options, initialize to ignore all signals */
signal(QUIT, 1);
signal(INTR, 1);
}
dolv = v+1;
dolc = c-1;
loop: /* Each loop executes a shell statement interpretation, until a newline is encountered, and loops indefinitely unless the entire shell process terminates */
if(promp != 0)
prs(promp); /* Print interactive prompt */
peekc = getc(); /* For I/O stream, pre-read a character for future reading again */
main1(); /* Enter interpreter */
goto loop;
}
/*
* This function contains the entire interpretation process of a shell interaction
* Preprocessor: Preprocess the input command parameters
* Lexical scanning: Lexically analyze the command parameters and split into token lists
* Syntax parser: Build syntax tree
* Semantic analyzer: Set syntax tree node attributes, I/O, etc.
* Executor: Allocate processes for each command and execute
*/
void main1()
{
char c, *cp;
int *t;
argp = args;
eargp = args+ARGSIZ-5; /* Initialize token list */
linep = line;
elinep = line+LINSIZ-5; /* Initialize command parameter buffer */
error = 0; /* Error count cleared */
gflg = 0; /* Overflow flag cleared */
do {
cp = linep;
word();
} while(*cp != '\n'); /* Scan all command parameters, generate token list, terminate when newline is encountered */
treep = trebuf;
treeend = &trebuf; /* Syntax tree buffer cleared */
if(gflg == 0) {
if(error == 0) {
setexit();
if (error)
return;
t = syntax(args, argp); /* Scanner has no buffer overflow, no error count, build syntax tree from token list */
}
if(error != 0)
err("syntax error"); else /* Syntax analysis error, prompt and exit shell */
execute(t, NULL, NULL); /* No error count, start semantic analysis from root node */
}
}
/*
* Scan and split tokens from command parameters
* readc directly reads a character from the stream, getc preprocesses the former character
*/
void word()
{
char c, c1;
*argp++ = linep; /* Take the current command parameter as a token into the list, end with '\0', argp points to the next token position */
loop:
switch(c = getc()) {
case ' ':
case '\t':
goto loop; /* Filter spaces and tab characters */
case '\'':
case '"':
c1 = c;
while((c=readc()) != c1) { /* Read one more character, judge if it is paired (such as quote) */
if(c == '\n') {
error++; /* Quote not ended, encounter newline, error count */
peekc = c; /* Push back pre-read character */
return;
}
*linep++ = c|QUOTE; /* Add quote flag */
}
goto pack; /* Quote ended, jump to pack */
case '&':
case ';':
case '<':
case '>':
case '(':
case ')':
case '|':
case '^':
case '\n':
*linep++ = c;
*linep++ = '\0'; /* Special characters such as meta-characters are returned as a single token */
return;
}
peekc = c; /* If it is a normal character, push back pre-read */
pack:
for(;;) {
c = getc();
if(any(c, " '\"\t;&<>()|^\n")) { /* Encounter meta-characters, separators, etc. */
peekc = c; /* Push back pre-read character */
if(any(c, "\"'"))
goto loop; /* Encounter quote character, jump to loop to continue scanning */
*linep++ = '\0'; /* Split out a token */
return;
}
*linep++ = c;
}
}
/* Allocate a syntax tree node, the size is specified by n, which is n*4 bytes */
int* tree(int n)
{
int *t;
t = treep;
treep += n;
if (treep>treeend) {
prs("Command line overflow\n"); /* Insufficient space */
error++;
reset();
}
return(t);
}
/*
* Read a character and do some preprocessing
* 1. Read from pre-read cache
* 2. Read directly from the stream
*/
char getc()
{
char c;
if(peekc) {
c = peekc; /* If there is a pre-read cache, read the cache and return */
peekc = 0; /* Clear pre-read cache */
return(c);
}
if(argp > eargp) { /* Token list overflow */
argp -= 10;
while((c=getc()) != '\n'); /* Take all characters before the newline */
argp += 10;
err("Too many args");
gflg++;
return(c);
}
if(linep > elinep) { /* Command parameter buffer overflow */
linep -= 10;
while((c=getc()) != '\n'); /* Take all characters before the newline */
linep += 10;
err("Too many characters");
gflg++;
return(c);
}
getd:
if(dolp) { /* There are command parameter characters prefixed with '$' */
c = *dolp++;
if(c != '\0')
return(c);
dolp = 0; /* The parameter prefixed with '$' is scanned, dolp is cleared */
}
c = readc(); /* Read a character from the stream */
if(c == '\\') {
c = readc(); /* Encounter escape character, continue to read the next character */
if(c == '\n')
return(' '); /* Slash at the end of the line, return space to continue the next line */
return(c|QUOTE); /* Quote this character */
}
if(c == '$') {
c = readc(); /* Encounter '$', continue to read a character */
if(c>='0' && c<='9') { /* Positional variable */
if(c-'0' < dolc) /* Positional parameter is within legal range */
dolp = dolv; /* dolp points to the substituted command parameter character */
goto getd; /* Jump to getd processing */
}
if(c == '$') { /* Current process pid */
dolp = pidp; /* dolp points to the pid string recorded earlier */
goto getd; /* Jump to getd processing */
}
}
return(c&0177); /* Normal character is returned directly, limited to ASCII */
}
/*
* Read a character from the stream
* arginp!=NULL (onelflg==0): With "-c" option, scan from the appended string
* arginp==1 (oneflg==0): With "-c" option, scanning ends, exit shell process
* onelflg==2(arginp==NULL): With "-t" option, scan from stdin
* onelflg==1(arginp==NULL): With "-t" option, scanning ends, exit shell process
*/
int readc()
{
char cc;
int c;
if (arginp) { /* "-c" option */
if (arginp == (void *)1)
exit(-1); /* No new command parameters, exit shell process */
if ((c = *arginp++) == 0) {
arginp = (void *)1; /* Scanning ends */
c = '\n'; /* Append newline to return */
}
return(c);
}
if (onelflg==1)
exit(0); /* "-t" option, scanning ends, exit shell process */
if(read(0, &cc, 1) != 1) /* Read a character from standard input */
exit(-1);
if (cc=='\n' && onelflg) /* onelflg == 2, "-t" option */
onelflg--; /* Scanning of one line is completed */
return(cc);
}
/*
* syntax
* empty
* syn1
*
* Start building the syntax tree from here, traverse the previously scanned token list
* p1 points to the head of the token list, p2 points to the next of the end of the token list
* Top-down three-level structure: syn1, syn2, syn3, function call relationship is as follows
* 1. Left subtree: The left list is advanced by one layer
* 2. Right subtree: The right list is backtracked or recursive
* 3. No symbol found: All lists are advanced by one layer
* 4. Generate node: Return directly
*/
int *syntax(char **p1, char **p2)
{
while(p1 != p2) {
if(any(**p1, ";&\n")) /* The list prefix is a command sequence separator character, filter out this token, encountered in syntax backtracking */
p1++; else
return(syn1(p1, p2)); /* All lists are advanced by one layer to syn1 */
}
return(0);
}
/*
* syn1
* syn2
* syn2 & syntax
* syn2 ; syntax
*
* syn1 generates a command sequence node, finds the separator,
* generates the left subtree from top to bottom, and backtracks to generate the right subtree.
* p1 points to the head of the token list, p2 points to the next of the end of the token list.
*/
int *syn1(char **p1, char **p2)
{
char **p;
int *t, *t1;
int l;
l = 0;
for(p=p1; p!=p2; p++)
switch(**p) {
case '(':
l++;
continue; /* Equivalent to break here */
case ')':
l--;
if(l < 0)
error++;
continue;
case '&':
case ';':
case '\n':
if(l == 0) { /* No parentheses nesting */
l = **p;
t = tree(4);
t = TLST; /* Generate command sequence node */
t = (int)syn2(p1, p); /* The left list is advanced by one layer to generate the left subtree node */
t = 0; /* Command sequence node does not set any flags */
if(l == '&') {
t1 = (int *)t;
t1 |= FAND|FPRS|FINT; /* If there is a background process, its attributes are pushed down to the left subtree node */
}
t = (int)syntax(p+1, p2); /* The right list backtracks to syntax to generate the right subtree node */
return(t);
}
}
if(l == 0)
return(syn2(p1, p2)); /* No command separator found, all lists are advanced by one layer to syn2 */
error++; /* If nesting is not zero, an error occurs */
}
/*
* syn2
* syn3
* syn3 | syn2
*
* syn2 is responsible for building the pipeline node, searching for pipeline symbols,
* generates the left subtree from top to bottom, and recursively generates the right subtree.
* p1 points to the head of the token list, p2 points to the next of the end of the token list.
*/
int *syn2(char **p1, char **p2)
{
char **p;
int l, *t;
l = 0;
for(p=p1; p!=p2; p++)
switch(**p) {
case '(':
l++;
continue; /* Equivalent to break here */
case ')':
l--;
continue;
case '|':
case '^':
if(l == 0) { /* No parentheses nesting */
t = tree(4);
t = TFIL; /* Build pipeline node */
t = (int)syn3(p1, p); /* The left list is advanced by one layer to generate the left subtree node */
t = (int)syn2(p+1, p2); /* The right is recursively generated to the right subtree node */
t = 0; /* Flags are inherited from the upper node */
return(t);
}
}
return(syn3(p1, p2)); /* No pipeline found, all lists are advanced by one layer, no need to judge whether nesting is zero */
}
/*
* syn3
* ( syn1 )
* word word*
*
* syn3 is responsible for building compound commands and simple command nodes. The former is marked by parentheses, and all token lists inside the parentheses are backtracked to syn1 to build sub-command nodes;
* A simple command is a leaf of the syntax tree. Its left and right subtree nodes are I/O redirect files, and store command parameter strings. After generating the node, return directly.
* p1 points to the head of the token list, p2 points to the next of the end of the token list.
*/
int *syn3(char **p1, char **p2)
{
char **p;
char **lp, **rp;
int *t;
int n, l, i, o, c, flg;
flg = 0;
if(**p2 == ')')
flg |= FPAR; /* The last sub-command in the compound command does not need to fork a child process */
lp = 0; /* Start of sub-command */
rp = 0; /* End of sub-command */
i = 0; /* Input file path */
o = 0; /* Output file path */
n = 0; /* Character count */
l = 0; /* Nesting level */
for(p=p1; p!=p2; p++)
switch(c = **p) {
case '(':
if(l == 0) {
if(lp != 0)
error++;
lp = p+1; /* The outermost layer of the current nesting, lp points to the first character of the sub-command */
}
l++;
continue; /* Same as break */
case ')':
l--;
if(l == 0)
rp = p; /* The outermost layer of the current nesting, rp points to the ')' after the sub-command */
continue;
case '>':
p++;
if(p!=p2 && **p=='>') /* Append mode FCAT flag */
flg |= FCAT; else
p--;
/* Note that there is no break here, continue to go down */
case '<':
if(l == 0) {
p++;
if(p == p2) {
error++; /* No character behind, error occurs */
p--;
}
if(any(**p, "<>(")) /* Illegal character */
error++;
if(c == '<') {
if(i != 0)
error++;
i = (int)*p; /* Redirect to input file, only valid for simple commands */
continue;
}
if(o != 0)
error++;
o = (int)*p; /* Redirect to output file, only valid for simple commands */
}
continue;
default:
if(l == 0)
p1 = *p; /* Command parameters are moved forward to clear previous records, and counted with n */
}
if(lp != 0) {
if(n != 0)
error++; /* For compound commands, n must be 0 */
t = tree(5);
t = TPAR; /* Allocate compound command type node */
t = (int)syn1(lp, rp); /* All token lists in parentheses are backtracked to syn1 to build sub-command nodes */
goto out;
}
if(n == 0)
error++; /* For simple commands, n must not be 0 */
p1 = 0; /* String ends */
t = tree(n+5); /* Allocate simple command node, size is 5 fields (DSPR field is empty) plus the number of command characters */
t = TCOM;
for(l=0; l<n; l++)
t = (int)p1;
out:
t = flg; /* Inherent attributes or attributes inherited from upper nodes */
t = i; /* For simple commands, the left and right subtrees are redirected I/O files */
t = o; /* For compound commands, the left and right subtrees are empty */
return(t);
}
/* Scan all characters with function pointer f */
void scan(int *at, int (*f)())
{
char *p, c;
int *t;
t = at+DCOM;
while(p = (char *)*t++)
while(c = *p)
*p++ = (*f)(c);
}
/* Find wildcards */
int tglob(int c)
{
if(any(c, "
: Pipeline input end handle
* pf1: Idle
* pf2: Idle
* pf2: Pipeline output end handle
*/
void execute(int *t, int *pf1, int *pf2)
{
int i, f, pv;
int *t1;
char *cp1, *cp2;
extern int errno;
if(t != 0)
switch(t) {
case TCOM:
cp1 = (char *)t;
/* The following are built-in commands */
if(equal(cp1, "chdir")) {
if(t != 0) {
if(chdir(t) < 0)
err("chdir: bad directory");
} else
err("chdir: arg count");
return;
}
if(equal(cp1, "shift")) {
if(dolc < 1) {
prs("shift: no args\n");
return;
}
dolv = dolv;
dolv++;
dolc--;
return;
}
if(equal(cp1, "login")) {
if(promp != 0) {
close(acctf);
execv("/bin/login", t+DCOM);
}
prs("login: cannot execute\n");
return;
}
if(equal(cp1, "newgrp")) {
if(promp != 0) {
close(acctf);
execv("/bin/newgrp", t+DCOM);
}
prs("newgrp: cannot execute\n");
return;
}
if(equal(cp1, "wait")) {
pwait(-1, 0);
return;
}
if(equal(cp1, ":"))
return;
/* Note that there is no break here, after filtering out built-in commands, external commands will continue to go down */
case TPAR:
f = t;
i = 0;
if((f&FPAR) == 0) /* Except for the last sub-command in the compound command, other commands all fork child processes */
i = fork();
if(i == -1) {
err("try again");
return;
}
if(i != 0) { /* Parent process code */
if((f&FPIN) != 0) {
close(pf1); /* After fork, the child process gets a copy of the parent process's pipeline handle, we need to close it in the parent process to avoid resource leakage */
close(pf1); /* Note that here only close pf1, because pf1 and pf2 are both mirrors of pv, so closing pf1 is closing pf2 */
}
if((f&FPRS) != 0) {/* Print child process pid */
prn(i);
prs("\n");
}
if((f&FAND) != 0) /* Background asynchronous process does not need to wait */
return;
if((f&FPOU) == 0) /* The node is the end of the pipeline, wait for the child process to terminate */
pwait(i, t);
return;
}
/* The following is the child process code, or the command process of the FPAR attribute node */
if(t != 0) {
close(0);
i = open(t, 0); /* Redirect stdin to the left subtree node */
if(i < 0) {
prs((char *)t);
err(": cannot open");
exit(-1);
}
}
if(t != 0) {
if((f&FCAT) != 0) {
i = open(t, 1);
if(i >= 0) {
seek(i, 0, 2); /* Append mode */
goto f1;
}
}
i = creat(t, 0666); /* Create new file handle */
if(i < 0) {
prs((char *)t);
err(": cannot create");
exit(-1);
}
f1:
close(1);
dup(i); /* Redirect stdout to the right subtree node */
close(i);
}
if((f&FPIN) != 0) {
close(0);
dup(pf1); /* Redirect stdin to the pipeline input end */
close(pf1);
close(pf1); /* Child process closes pipeline handle */
}
if((f&FPOU) != 0) {
close(1);
dup(pf2); /* Redirect stdout to the pipeline output */
close(pf2);
close(pf2); /* Child process closes pipeline handle */
}
if((f&FINT)!=0 && t==0 && (f&FPIN)==0) {
close(0); /* If interrupt is ignored and there is no pipeline input, close stdin and redirect to /dev/null */
open("/dev/null", 0); /* To avoid interference from other processes' stdin, but stdout is reserved */
}
if((f&FINT) == 0 && setintr) {
signal(INTR, 0); /* Without FINT flag, restore interrupt signal response (default is ignored) */
signal(QUIT, 0);
}
if(t == TPAR) { /* If it is a compound type, execute the sub-command, and push down the FINT flag at the same time */
if(t1 = (int *)t)
t1 |= f&FINT;
execute(t1, NULL, NULL);
exit(0);
}
close(acctf);
gflg = 0;
scan(t, &tglob); /* Scan wildcards */
if(gflg) {
t = (int)"/etc/glob";
execv(t, t+DSPR); /* Commands containing wildcards are executed in /etc/glob */
prs("glob: cannot execute\n");
exit(-1);
}
scan(t, &trim); /* Remove quote flag */
*linep = 0;
texec((char *)t, t); /* Execute the command in the current directory first */
cp1 = linep;
cp2 = "/usr/bin/";
while(*cp1 = *cp2++) /* strcpy, cp1 stops at '\0' position */
cp1++;
cp2 = (char *)t;
while(*cp1++ = *cp2++); /* strcpy, '\0' ends */
texec(linep+4, t); /* Then execute the command in the /bin directory */
texec(linep, t); /* Finally execute the command in the /usr/bin directory */
prs((char *)t); /* If the process exits abnormally, it means the command is not found, exit the shell */
err(": not found");
exit(-1);
case TFIL:
f = t;
pipe(pv); /* Create pipeline, pv is input handle, pv is output handle */
t1 = (int *)t;
t1 |= FPOU | (f&(FPIN|FINT|FPRS));
execute(t1, pf1, pv); /* For the left subtree node, pv is passed in as the pipeline output handle, pv is idle */
t1 = (int *)t;
t1 |= FPIN | (f&(FPOU|FINT|FAND|FPRS)); /* Only the command at the end of the pipeline can inherit the FAND attribute, which is exclusive to FPOU */
execute(t1, pv, pf2); /* For the right subtree node, pv is passed in as the pipeline input handle, pv is idle */
return;
case TLST:
f = t&FINT; /* For the command sequence node, push down the FINT attribute to the left and right subtrees */
if(t1 = (int *)t)
t1 |= f;
execute(t1, NULL, NULL); /* Execute left first, then right in turn */
if(t1 = (int *)t)
t1 |= f;
execute(t1, NULL, NULL);
return;
}
}
/*
* Execute command
* f is the path of the executable file
* at is the syntax tree node
*/
void texec(char *f, int *at)
{
extern int errno;
int *t;
t = at;
execv(f, t+DCOM); /* Reload and execute the command, no need to set environment */
if (errno==ENOEXEC) {
if (*linep)
t = (int)linep;
t = (int)"/bin/sh";
execv(t, t+DSPR); /* Executable file not found for the command */
prs("No shell!\n");
exit(-1);
}
if (errno==ENOMEM) {
prs((char *)t);
err(": too large"); /* Insufficient memory */
exit(-1);
}
}
/* Print error, in non-interactive mode, clear stdin cache and exit shell process */
void err(char *s)
{
prs(s);
prs("\n");
if(promp == 0) {
seek(0, 0, 2);
exit(-1);
}
}
/* Print string */
void prs(char *as)
{
char *s;
s = as;
while(*s)
put(*s++);
}
/* Output a character */
void put(int c)
{
write(2, &c, 1);
}
/* itoa */
void prn(int n)
{
int a;
if(a=n/10)
prn(a);
put(n%10+'0');
}
/* Whether a character is contained in the string */
int any(int c, char *as)
{
char *s;
s = as;
while(*s)
if(*s++ == c)
return(1);
return(0);
}
/* String comparison */
int equal(char *as1, char *as2)
{
char *s1, *s2;
s1 = as1;
s2 = as2;
while(*s1++ == *s2)
if(*s2++ == '\0')
return(1);
return(0);
}
/*
* Parent process waits for child process to terminate
* i is the child process pid, -1 means wait for all child processes
* t is the syntax tree node
*/
void pwait(int i, int *t)
{
int p, e;
int s;
if(i != 0)
for(;;) {
times(&timeb);
time(timeb.proct);
p = wait(&s);
if(p == -1) /* Wait failed */
break;
e = s&0177; /* Reserve state environment */
if(mesg != 0) { /* Child process terminated abnormally */
if(p != i) {
prn(p);
prs(": ");
}
prs(mesg); /* Print corresponding message */
if(s&0200) /* Kernel crash */
prs(" -- Core dumped");
}
if(e != 0)
err(""); /* Wait for normal termination */
if(i == p) {
acct(t);
break;
} else
acct(0);
}
}
void acct(int *t)
{
if(t == 0)
enacct("**gok"); else
if(*t == TPAR)
enacct("()"); else
enacct((char *)t);
}
void enacct(char *as)
{
struct stime timbuf;
struct {
char cname;
char shf;
char uid;
int datet;
int realt;
int bcput;
int bsyst;
} tbuf;
int i;
char *np, *s;
s = as;
times(&timbuf);
time(timbuf.proct);
lsub(tbuf.realt, timbuf.proct, timeb.proct);
lsub(tbuf.bcput, timbuf.cputim, timeb.cputim);
lsub(tbuf.bsyst, timbuf.systim, timeb.systim);
do {
np = s;
while (*s != '\0' && *s != '/')
s++;
} while (*s++ != '\0');
for (i=0; i<14; i++) {
tbuf.cname = *np;
if (*np)
np++;
}
tbuf.datet = timbuf.proct;
tbuf.datet = timbuf.proct;
tbuf.uid = uid;
tbuf.shf = 0;
if (promp==0)
tbuf.shf = 1;
seek(acctf, 0, 2);
write(acctf, &tbuf, sizeof(tbuf));
}
[ Last edited by zzz19760225 on 2016-12-11 at 00:32 ]
1<词>,2,3/段\,4{节},5(章)。
