http://upload.cn-dos.net/img/2311.zip
/*
* sh.c是UNIX V6下的一个shell解释器实现,源码位于
http://minnie.tuhs.org/Archive/PDP-11/Distributions/research/Dennis_v6/
* UNIX v6是受BSD许可证保护的自由软件,其中sh.c的原作者是Ken Thompson(1943 - )于1971年初步实现,于1975年移植到Unix V6。
* 本人主要工作是对源码进行重新注解,并将K&R C转成ANSI C,除此之外未做任何改动。
* 该文件在gcc-4.4.3下通过编译,但本人不能担保该shell应用于任何交互引起的任何问题。
* 任何人可用作学习、改写或者重新发布等其它用途,请遵循BSD许可协议。
* 最近修订时间: 2013-4-15
* 修订人: Leo Ma
* 联系方式:
begeekmyfriend@gmail.com
*/
#include <stdlib.h>
#define NULL ((void *)0)
#define INTR 2 /* 进程终止信号*/
#define QUIT 3 /* 同上*/
#define LINSIZ 1000 /* 命令参数行缓冲空间*/
#define ARGSIZ 50 /* Token指针列表 */
#define TRESIZ 100 /* 语法树所有节点空间*/
#define QUOTE 0200 /* 引用字符标志位,表明该字符前缀为斜杠或引号,限制字符集为ascii,八进制 */
/* 以下为语法树节点的属性标识,用于DFLG字段*/
#define FAND 1 /* And: 该命令以后台异步进程方式执行*/
#define FCAT 2 /* Catenate: 以追加方式重定向输出,相当于>> 符号*/
#define FPIN 4 /* Pipe in: 命令输入重定向到管道线,即从左子树流入*/
#define FPOU 8 /* Pipe out: 命令输出重定向到管道线,即向右子树流出*/
#define FPAR 16 /* Parentheses: 标识复合命令中最后一个简单命令,即右括弧前的命令。Shell会为每个外部
命令(非内置)fork一个子进程上执行,但复合命令中最后一个子命令仍在原来的进程上执行。*/
#define FINT 32 /* Interrupt: 如果命令是后台异步执行,则忽略进程终止信号。*/
#define FPRS 64 /* Print string: 以字符串的形式打印后台进程的pid */
/* 以下为语法树节点的类型声明,用于DTYP字段 */
#define TCOM 1 /* Command: 简单命令*/
#define TPAR 2 /* Parentheses: 复合命令,包含在圆形括弧中的命令序列集合 */
#define TFIL 3 /* Filter: 过滤器或者管道线,由单独的'|'或'^'符号表示 */
#define TLST 4 /* List: 命令序列,多个简单命令集合,由单独的; 或'&'或'\n'符号分隔 */
/* 以下为语法树节点的各个字段,指定了节点大小*/
#define DTYP 0 /* 节点类型,唯一标识*/
#define DLEF 1 /* 左子树节点,视当前节点的类型而定*/
#define DRIT 2 /* 右子树节点,视当前节点的类型而定*/
#define DFLG 3 /* 节点属性,影响所属命令的状态和执行方式*/
#define DSPR 4 /* 若是复合命令类型,该字段为子语法树节点*/
#define DCOM 5 /* 命令参数字符串 */
#define ENOMEM 12 /* 命令执行失败,内存空间不足*/
#define ENOEXEC 8 /* 命令执行失败,找不到可执行文件*/
char *dolp; /* Dollar: 指向以'$'开头的特殊变量*/
char pidp; /* 字符串形式存储进程pid */
char **dolv; /* 以空格分隔的命令行参数序列*/
int dolc; /* 命令行参数个数*/
char *promp; /* 输入提示符,不为空即交互模式,为空即非交互模式*/
char *linep; /* 命令参数缓冲指针,存放下一个字符位置*/
char *elinep; /* 命令参数缓冲区末端*/
char **argp; /* Token列表指针,存放下一个token的位置*/
char **eargp; /* Token列表末端*/
int *treep; /* 语法树列表指针,存放下一个节点位置*/
int *treeend; /* 语法树列表末端*/
char peekc; /* 预读一个字符缓冲*/
char gflg; /* 两种用途: 列表指针溢出标记,通配符标记*/
char error; /* 错误计数*/
char acctf; /* */
char uid; /* User id */
char setintr; /* 忽略中断信号标识*/
char *arginp; /* 当存在选项'-c'时,从该指针指向的字串中扫描命令参数*/
int onelflg; /* 当存在选项'-t'时,从标准输入中扫描命令参数*/
/* 中断信号消息列表*/
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",
};
/* 记录系统时间*/
struct stime {
int proct;
int cputim;
int systim;
} timeb;
char line; /* 命令参数缓冲区*/
char *args; /* Token列表缓冲区*/
int trebuf; /* 语法树节点缓冲区*/
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入口*/
void main(int c, char **av)
{
int f;
char *acname, **v;
for(f=2; f<15; f++) /* close_on_exec,因为shell可能来自外部进程或自身fork */
close(f);
if((f=dup(1)) != 2) /* 将stdout重定向到stderr,dup()将返回尚未打开的最小文件句柄*/
close(f);
dolc = getpid(); /* 记录当前进程pid */
for(f=4; f>=0; f--) {
pidp = dolc%10+'0'; /* itoa */
dolc = dolc/10;
}
v = av;
acname = "/usr/adm/sha"; /* SHA加密序列,目前Unix版本已废弃*/
promp = "% "; /* 普通用户交互提示符*/
if(((uid = getuid())&0377) == 0)
promp = "# "; /* root用户交互提示符*/
acctf = open(acname, 1);
if(c > 1) {
promp = 0; /* 进入非交互模式*/
if (*v=='-') { /* 存在选项*/
**v = '-'; /* 用第一个序列字符临时标记*/
if (v=='c' && c>2) /* '-c'选项,从arginp中扫描*/
arginp = v;
else if (v=='t') /* '-t'选项,从stdin中扫描*/
onelflg = 2;
} else { /* 直接从命令文件中扫描*/
close(0);
f = open(v, 0); /* 重定向stdin到文件*/
if(f < 0) {
prs(v);
err(": cannot open");
}
}
}
if(**v == '-') {
setintr++; /* 非交互模式下带选项,则初始化忽略所有信号*/
signal(QUIT, 1);
signal(INTR, 1);
}
dolv = v+1;
dolc = c-1;
loop: /* 每次loop执行一次shell语句解释,直到遇上换行符,无限循环除非整个shell进程终止 */
if(promp != 0)
prs(promp); /* 打印交互提示符*/
peekc = getc(); /* 对于I/O流,预读一个字符,以便将来再次读取*/
main1(); /* 进入解释器 */
goto loop;
}
/*
* 该函数包含一次shell交互的整个解释过程
* Preprocessor: 对输入的命令参数进行预处理
* Lexical scanning: 对命令参数进行词法分析,分割成token列表
* Syntax parser: 构建语法树
* Semantic analyzer: 设置语法树节点属性,I/O等
* Executor: 为每个命令分配进程并执行
*/
void main1()
{
char c, *cp;
int *t;
argp = args;
eargp = args+ARGSIZ-5; /* 初始化token列表*/
linep = line;
elinep = line+LINSIZ-5; /* 初始化命令参数缓冲区*/
error = 0; /* 错误计数清零*/
gflg = 0; /* 溢出标志位清零*/
do {
cp = linep;
word();
} while(*cp != '\n'); /* 扫描所有命令参数,生成token列表,遇上换行符终止*/
treep = trebuf;
treeend = &trebuf; /* 语法树缓冲区清零*/
if(gflg == 0) {
if(error == 0) {
setexit();
if (error)
return;
t = syntax(args, argp); /* Scanner没有发生缓冲溢出,没有错误计数,对token列表建立语法树 */
}
if(error != 0)
err("syntax error"); else /* 语法分析发生错误,提示并退出shell */
execute(t, NULL, NULL); /* 没有错误计数,从根节点开始进行语义分析 */
}
}
/*
* 对命令参数扫描并分割token
* readc直接从流中读取一个字符,getc对前者的字符进行预处理
*/
void word()
{
char c, c1;
*argp++ = linep; /* 将当前命令参数作为一个token放入列表,以'\0'结束,argp指向下一个token位置 */
loop:
switch(c = getc()) {
case ' ':
case '\t':
goto loop; /* 过滤空格和tab字符 */
case '\'':
case '"':
c1 = c;
while((c=readc()) != c1) { /* 多读一个字符,判断是否成对(如引号) */
if(c == '\n') {
error++; /* 引用未结束遇上换行符,错误计数 */
peekc = c; /* 推回预读字符 */
return;
}
*linep++ = c|QUOTE; /* 加上引用标识 */
}
goto pack; /* 引用结束,跳转至pack */
case '&':
case ';':
case '<':
case '>':
case '(':
case ')':
case '|':
case '^':
case '\n':
*linep++ = c;
*linep++ = '\0'; /* 元字符等特殊字符单独作为一个token返回 */
return;
}
peekc = c; /* 若为普通字符,推回预读 */
pack:
for(;;) {
c = getc();
if(any(c, " '\"\t;&<>()|^\n")) { /* 遇上元字符、分隔符等特殊字符 */
peekc = c; /* 推回预读字符 */
if(any(c, "\"'"))
goto loop; /* 遇上引用字符,跳转到loop继续扫描 */
*linep++ = '\0'; /* 分割一个token出来 */
return;
}
*linep++ = c;
}
}
/* 分配一个语法树节点,大小根据n指定,为n*4字节 */
int* tree(int n)
{
int *t;
t = treep;
treep += n;
if (treep>treeend) {
prs("Command line overflow\n"); /* 空间不足 */
error++;
reset();
}
return(t);
}
/*
* 读取一个字符,并做一些预处理
* 1.从预读缓存中读取
* 2.从流中直接读取
*/
char getc()
{
char c;
if(peekc) {
c = peekc; /* 如果有预读缓存,读取缓存并返回 */
peekc = 0; /* 清除预读缓存 */
return(c);
}
if(argp > eargp) { /* Token列表溢出 */
argp -= 10;
while((c=getc()) != '\n'); /* 取完换行符前所有字符 */
argp += 10;
err("Too many args");
gflg++;
return(c);
}
if(linep > elinep) { /* 命令参数缓存溢出 */
linep -= 10;
while((c=getc()) != '\n'); /* 取完换行符之前所有字符 */
linep += 10;
err("Too many characters");
gflg++;
return(c);
}
getd:
if(dolp) { /* 存在带'$'前缀的命令参数字符 */
c = *dolp++;
if(c != '\0')
return(c);
dolp = 0; /* 带'$'前缀的参数扫描完毕,dolp清零 */
}
c = readc(); /* 从流中读取一个字符 */
if(c == '\\') {
c = readc(); /* 遇到转义符,继续读取下一个字符 */
if(c == '\n')
return(' '); /* 斜杠在行末,返回空格接续下一行 */
return(c|QUOTE); /* 引用该字符 */
}
if(c == '$') {
c = readc(); /* 遇到'$',继续读取一个字符 */
if(c>='0' && c<='9') { /* 位置变量 */
if(c-'0' < dolc) /* 位置参数在合法范围内 */
dolp = dolv; /* dolp指向替代的命令参数字符 */
goto getd; /* 跳转getd处理 */
}
if(c == '$') { /* 当前进程pid */
dolp = pidp; /* dolp指向之前记录的pid字符串 */
goto getd; /* 跳转getd处理 */
}
}
return(c&0177); /* 普通字符直接返回,限定在ascii */
}
/*
* 从流中读取一个字符
* arginp!=NULL(onelflg==0):带"-c"选项,从后面附加的字符串中扫描
* arginp==1(oneflg==0):带"-c"选项,扫描终止,退出shell进程
* onelflg==2(arginp==NULL): 带"-t"选项,从stdin中扫描
* onelflg==1(arginp==NULL): 带"-t"选项,扫描终止,退出shell进程
*/
int readc()
{
char cc;
int c;
if (arginp) { /* "-c"选项 */
if (arginp == (void *)1)
exit(-1); /* 没有新的命令参数,退出shell进程 */
if ((c = *arginp++) == 0) {
arginp = (void *)1; /* 扫描终止 */
c = '\n'; /* 追加换行符返回 */
}
return(c);
}
if (onelflg==1)
exit(0); /* "-t"选项,扫描终止,退出shell进程 */
if(read(0, &cc, 1) != 1) /* 从标准输入中读取一个字符 */
exit(-1);
if (cc=='\n' && onelflg) /* onelflg == 2,"-t"选项 */
onelflg--; /* 扫描一行完毕 */
return(cc);
}
/*
* syntax
* empty
* syn1
*
* 从这里开始构建语法树,遍历之前扫描生成的token列表
* p1指向token列表头部,p2指向token列表末尾的下一个
* 自顶向下三级结构: syn1、syn2、syn3,函数调用关系如下
* 1.左子树:左边列表递进一层
* 2.右子树:右边列表回溯或者递归
* 3.找不到符号:所有列表递进一层
* 4.生成节点:直接返回
*/
int *syntax(char **p1, char **p2)
{
while(p1 != p2) {
if(any(**p1, ";&\n")) /* 列表前缀为命令序列分隔字符,过滤掉该token,对syntax回溯中碰到*/
p1++; else
return(syn1(p1, p2)); /* 所有列表递进一层至syn1 */
}
return(0);
}
/*
* syn1
* syn2
* syn2 & syntax
* syn2 ; syntax
*
* syn1生成命令序列节点,查找分隔符,
* 自顶向下生成左子树,回溯生成右子树。
* p1指向token列表头部,p2指向token列表末尾的下一个。
*/
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; /* 此处等同于break */
case ')':
l--;
if(l < 0)
error++;
continue;
case '&':
case ';':
case '\n':
if(l == 0) { /* 没有圆括弧嵌套 */
l = **p;
t = tree(4);
t = TLST; /* 生成命令序列节点*/
t = (int)syn2(p1, p); /* 左边列表递进一层生成左子树节点*/
t = 0; /* 命令序列节点不设置任何标志位*/
if(l == '&') {
t1 = (int *)t;
t1 |= FAND|FPRS|FINT; /* 若有后台进程,其属性下推到左子树节点*/
}
t = (int)syntax(p+1, p2); /* 右边列表回溯到syntax生成右子树节点*/
return(t);
}
}
if(l == 0)
return(syn2(p1, p2)); /* 没有找到命令分隔符,所有列表递进一层至syn2 */
error++; /* 如果嵌套不归零发生错误*/
}
/*
* syn2
* syn3
* syn3 | syn2
*
* syn2负责构建管道线节点,搜索管道线符号,
* 自顶向下生成左子树,递归生成右子树。
* p1指向token列表头部,p2指向token列表末尾的下一个。
*/
int *syn2(char **p1, char **p2)
{
char **p;
int l, *t;
l = 0;
for(p=p1; p!=p2; p++)
switch(**p) {
case '(':
l++;
continue; /* 此处等同于break */
case ')':
l--;
continue;
case '|':
case '^':
if(l == 0) { /* 没有圆括弧嵌套 */
t = tree(4);
t = TFIL; /* 建立管道线节点*/
t = (int)syn3(p1, p); /* 左边列表递进一层生成左子树节点*/
t = (int)syn2(p+1, p2); /* 右边递归生成右子树节点*/
t = 0; /* 标志位继承自上层节点*/
return(t);
}
}
return(syn3(p1, p2)); /* 没有找到管道线,所有列表递进一层,不需要判断嵌套是否归零*/
}
/*
* syn3
* ( syn1 )
* word word*
*
* syn3负责构建复合命令和简单命令节点,前者以圆括号标记,括号内所有token列表回溯至syn1构建子命令节点;
* 简单命令是语法树叶子,其左右子树节点为I/O重定向文件,并存储命令参数字符串,生成节点后直接返回。
* p1指向token列表头部,p2指向token列表末尾的下一个。
*/
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; /* 复合命令中最后一个子命令不需要fork子进程*/
lp = 0; /* 子命令开头*/
rp = 0; /* 子命令末尾*/
i = 0; /* 输入文件路径*/
o = 0; /* 输出文件路径*/
n = 0; /* 字符计数*/
l = 0; /* 嵌套层数*/
for(p=p1; p!=p2; p++)
switch(c = **p) {
case '(':
if(l == 0) {
if(lp != 0)
error++;
lp = p+1; /* 当前嵌套最外层,lp指向子命令第一个字符*/
}
l++;
continue; /* 同break */
case ')':
l--;
if(l == 0)
rp = p; /* 当前嵌套最外层,rp指向子命令后面的')' */
continue;
case '>':
p++;
if(p!=p2 && **p=='>') /* 追加模式FCAT标识*/
flg |= FCAT; else
p--;
/* 注意这里没有break,继续往下走*/
case '<':
if(l == 0) {
p++;
if(p == p2) {
error++; /* 后面没有字符,发生错误 */
p--;
}
if(any(**p, "<>(")) /* 非法字符 */
error++;
if(c == '<') {
if(i != 0)
error++;
i = (int)*p; /* 重定向到输入文件,只对简单命令有效*/
continue;
}
if(o != 0)
error++;
o = (int)*p; /* 重定向到输出文件,只对简单命令有效*/
}
continue;
default:
if(l == 0)
p1 = *p; /* 命令参数前移清除以往记录,并用n计数*/
}
if(lp != 0) {
if(n != 0)
error++; /* 复合命令,n必须为0 */
t = tree(5);
t = TPAR; /* 分配复合命令类型节点*/
t = (int)syn1(lp, rp); /* 括号内所有token列表回溯至syn1构建子命令节点 */
goto out;
}
if(n == 0)
error++; /* 简单命令,n必须不为0 */
p1 = 0; /* 字符串结束*/
t = tree(n+5); /* 分配简单命令节点,大小为5个字段(DSPR字段为空)加上命令字符数*/
t = TCOM;
for(l=0; l<n; l++)
t = (int)p1;
out:
t = flg; /* 固有属性或者继承自上层节点属性*/
t = i; /* 对于简单命令,左右子树是重定向的I/O文件*/
t = o; /* 对于复合命令,左右子树为空*/
return(t);
}
/* 用函数指针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);
}
/* 查找通配符*/
int tglob(int c)
{
if(any(c, "
: 管道线输入端句柄
* pf1: 闲置
* pf2: 闲置
* pf2: 管道线输出端句柄
*/
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;
/* 以下为内置命令*/
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;
/* 注意,这里没有break,筛选掉内置命令后,外部命令会继续往下走*/
case TPAR:
f = t;
i = 0;
if((f&FPAR) == 0) /* 除了复合命令中最后一个子命令,其它一律fork子进程*/
i = fork();
if(i == -1) {
err("try again");
return;
}
if(i != 0) { /* 父进程代码*/
if((f&FPIN) != 0) {
close(pf1); /* fork之后,子进程获得父进程的管道线句柄拷贝,我们要在父进程中关闭它,以免资源泄漏*/
close(pf1); /* 注意,这里只关闭pf1,因为pf1、pf2都是pv的镜像,因此关闭pf1就是关闭pf2 */
}
if((f&FPRS) != 0) {/* 打印子进程pid */
prn(i);
prs("\n");
}
if((f&FAND) != 0) /* 后台异步进程无需等待*/
return;
if((f&FPOU) == 0) /* 节点为管道线末端,等待子进程终止*/
pwait(i, t);
return;
}
/* 以下为子进程代码,或者FPAR属性节点命令进程*/
if(t != 0) {
close(0);
i = open(t, 0); /* 重定向stdin到左子树节点*/
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); /* 追加模式*/
goto f1;
}
}
i = creat(t, 0666); /* 创建新的文件句柄*/
if(i < 0) {
prs((char *)t);
err(": cannot create");
exit(-1);
}
f1:
close(1);
dup(i); /* 重定向stdout到右子树节点*/
close(i);
}
if((f&FPIN) != 0) {
close(0);
dup(pf1); /* 重定向stdin到管道线输入端*/
close(pf1);
close(pf1); /* 子进程关闭管道线句柄*/
}
if((f&FPOU) != 0) {
close(1);
dup(pf2); /* 重定向stdout 到管道线输出*/
close(pf2);
close(pf2); /* 子进程关闭管道线句柄*/
}
if((f&FINT)!=0 && t==0 && (f&FPIN)==0) {
close(0); /* 若忽略中断,且不存在管道线输入,则关闭stdin并重定向到位桶*/
open("/dev/null", 0); /* 以免来自其它进程stdin干扰,但stdout是保留的*/
}
if((f&FINT) == 0 && setintr) {
signal(INTR, 0); /* 无FINT标识,则恢复中断信号响应(默认是忽略的) */
signal(QUIT, 0);
}
if(t == TPAR) { /* 如果是复合类型,则执行子命令,同时下推FINT标识*/
if(t1 = (int *)t)
t1 |= f&FINT;
execute(t1, NULL, NULL);
exit(0);
}
close(acctf);
gflg = 0;
scan(t, &tglob); /* 扫描通配符*/
if(gflg) {
t = (int)"/etc/glob";
execv(t, t+DSPR); /* 包含通配符的命令在/etc/glob中执行*/
prs("glob: cannot execute\n");
exit(-1);
}
scan(t, &trim); /* 解除引用标识*/
*linep = 0;
texec((char *)t, t); /* 最先执行当前目录下的命令*/
cp1 = linep;
cp2 = "/usr/bin/";
while(*cp1 = *cp2++) /* strcpy,cp1停在'\0'位置 */
cp1++;
cp2 = (char *)t;
while(*cp1++ = *cp2++); /* strcpy,'\0'结束 */
texec(linep+4, t); /* 接着执行/bin目录下的命令*/
texec(linep, t); /* 最后执行/usr/bin目录下的命令*/
prs((char *)t); /* 若进程异常退出,表示找不到命令,退出shell */
err(": not found");
exit(-1);
case TFIL:
f = t;
pipe(pv); /* 创建管道线,pv为输入句柄,pv为输出句柄*/
t1 = (int *)t;
t1 |= FPOU | (f&(FPIN|FINT|FPRS));
execute(t1, pf1, pv); /* 对于左子树节点,pv作为管道线输出句柄传入,pv闲置*/
t1 = (int *)t;
t1 |= FPIN | (f&(FPOU|FINT|FAND|FPRS)); /* 只有管道线末端命令才能继承FAND属性,与FPOU排斥*/
execute(t1, pv, pf2); /* 对于右子树节点,pv作为管道线输入句柄传入,pv闲置*/
return;
case TLST:
f = t&FINT; /* 对于命令序列节点,下推FINT属性到左右子树*/
if(t1 = (int *)t)
t1 |= f;
execute(t1, NULL, NULL); /* 先左后右依次执行 */
if(t1 = (int *)t)
t1 |= f;
execute(t1, NULL, NULL);
return;
}
}
/*
* 执行命令
* f为可执行文件路径
* at为语法树节点
*/
void texec(char *f, int *at)
{
extern int errno;
int *t;
t = at;
execv(f, t+DCOM); /* 重新加载并执行命令,不需要设置环境*/
if (errno==ENOEXEC) {
if (*linep)
t = (int)linep;
t = (int)"/bin/sh";
execv(t, t+DSPR); /* 找不到命令可执行文件*/
prs("No shell!\n");
exit(-1);
}
if (errno==ENOMEM) {
prs((char *)t);
err(": too large"); /* 内存不足*/
exit(-1);
}
}
/* 打印错误,非交互模式则清空stdin缓存并退出shell进程*/
void err(char *s)
{
prs(s);
prs("\n");
if(promp == 0) {
seek(0, 0, 2);
exit(-1);
}
}
/* 打印字符串*/
void prs(char *as)
{
char *s;
s = as;
while(*s)
put(*s++);
}
/* 输出一个字符*/
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');
}
/* 字符串中是否包含某个字符*/
int any(int c, char *as)
{
char *s;
s = as;
while(*s)
if(*s++ == c)
return(1);
return(0);
}
/* 字符串比较*/
int equal(char *as1, char *as2)
{
char *s1, *s2;
s1 = as1;
s2 = as2;
while(*s1++ == *s2)
if(*s2++ == '\0')
return(1);
return(0);
}
/*
* 父进程等待子进程终止
* i为子进程pid,-1表示等待所有子进程
* t为语法树节点
*/
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) /* 等待失败 */
break;
e = s&0177; /* 保留状态环境 */
if(mesg != 0) { /* 子进程异常终止 */
if(p != i) {
prn(p);
prs(": ");
}
prs(mesg); /* 打印对应的消息 */
if(s&0200) /* 内核奔溃 */
prs(" -- Core dumped");
}
if(e != 0)
err(""); /* 等待正常终止 */
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 ]
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 ]