[转载]Sixth Edition Unix Shell
信息来源:mhss.nease.netUNIX V6 的 sh 代码没有什么稀罕.稀罕的是这是一个改写版,并且有很详细的中文注释.对学习很有好处:)
/*
* 这个 shell 是 UNIX v6 的 sh 在 POSIX 环境下的重新实现。
* UNIX v6 是受 BSD 许可证保护的自由软件,其中 sh 的原作者是 Ken Thompson。
* 余对这个 shell 的语法做了细微修改,对代码做了注释和重写。
* 余对这个程序和相关文档不做任何担保,放弃一切权利,不承担任何责任和义务。
*
* 最近的修订∶ 2004-09-06 寒蝉退士([url]http://mhss.nease.net[/url])
* 做的工作主要有: K&R C -> ANSI C,unix v6/v7 -> POSIX,去掉了进程记帐和 ^,
* 增加了 $?、umask 和 exec,去除了 goto 语句,增加了中文注释。
*/
下载地址:
[url]http://mhss.nease.net/shell/v6sh/v6sh.tar.gz[/url]
或(MS VC6 实验版本):
[url]http://mhss.nease.net/shell/v6sh/v6sh.vc.zip[/url]
对应手册(man page):
[url]http://mhss.nease.net/shell/v6sh/sh.html[/url]
代码有四个文件,依次是shell.c glob.c if.c goto.c
Shell.c
[code]
/*
* 这个 shell 是 UNIX v6 的 sh 在 POSIX 环境下的重新实现。
* UNIX v6 是受 BSD 许可证保护的自由软件,其中 sh 的原作者是 Ken Thompson。
* 余对这个 shell 的语法做了细微修改,对代码做了注释和重写。
* 余对这个程序和相关文档不做任何担保,放弃一切权利,不承担任何责任和义务。
*
* 最近的修订∶ 2004-09-06 寒蝉退士([url]http://mhss.nease.net[/url])
* 做的工作主要有: K&R C -> ANSI C,unix v6/v7 -> POSIX,去掉了进程记帐和 ^,
* 增加了 $?、umask 和 exec,去除了 goto 语句,增加了中文注释。
*/
/*
* Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code and documentation must retain the above
* copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
*
* This product includes software developed or owned by Caldera International, Inc.
*
* Neither the name of Caldera International, Inc. nor the names of other
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA INTERNATIONAL, INC.
* AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR
* ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <limits.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define QUOTE 0x80 /* 引用标志位,限制了字符集为 7 位 ASCII */
/* 语法树节点字段定义 */
#define DTYP 0 /* type: 节点类型 */
#define DLEF 1 /* left: 左子节点或输入重定向文件描述符 */
#define DRIT 2 /* right: 右子节点或输出重定向文件描述符 */
#define DFLG 3 /* flag: 标志位,属性文法意义上的属性 */
#define DSPR 4 /* space: 在简单命令的时候是一个空位,
* parentheses: 在复合命令时指向子语法树 */
#define DCOM 5 /* command: 命令和参数的字列表 */
/* 类型定义,用于 DTYP */
#define TCOM 1 /* command: 简单命令 */
#define TPAR 2 /* parentheses: 复合命令 */
#define TFIL 3 /* filter: 过滤器 */
#define TLST 4 /* list: 命令列表 */
/* 标志定义,用于 DFLG */
#define FAND 0x01 /* and: & 后台执行 */
#define FCAT 0x02 /* catenate: >> 添加方式的输出重定向 */
#define FPIN 0x04 /* pipe in: 命令从管道获得输入 */
#define FPOU 0x08 /* pipe out: 命令向管道发送输出 */
#define FPAR 0x10 /* parentheses: 复合命令中最后一个命令 */
#define FINT 0x20 /* interrupt: 忽略中断信号 */
#define FPRS 0x40 /* print string: 打印子进程 pid */
char *dolp = NULL; /* dollar p: 指向命令行参数的指针 */
char **dolv; /* dollar v: 命令行参数列表 */
int dolc; /* dollar c: 命令行参数个数 */
char pidp[11]; /* 保存 sh 自身进程 pid 的字符串 */
char *promp; /* 提示符 */
char *linep; /* 操纵行缓冲区的指针 */
char *elinep;
char **argp; /* 操纵字列表存储空间的指针 */
char **eargp;
int *treep; /* 操纵语法树节点存储空间的指针 */
int *treeend;
int peekc = 0; /* 预读字符 */
int gflg = 0; /* 全局标志,两种用途:缓冲区溢出,或包含通配符 */
int error; /* 语法分析发现错误标志 */
int setintr = 0; /* 设置中断信号忽略的标志 */
char *arginp = NULL; /* 指针指向包含要执行的命令的参数,执行之后退出 */
int onelflg = 0; /* one line flag: 读取一行命令执行并退出标志 */
uid_t uid; /* sh 进程的真实 uid */
jmp_buf jmpbuf; /* 跨函数跳转用来保存当前状态的缓冲区 */
#define SIGFLG 0x80 /* 异常终止标志位 */
int exitstat = 0; /* 执行命令的终止状态 */
char exitp[4]; /* 保存终止状态的字符串 */
char *arg0; /* 本 shell 的绝对路径名 */
#define LINSIZ 1000
#define ARGSIZ 50
#define TRESIZ ARGSIZ*2
char line[LINSIZ]; /* 行缓冲区 */
char *args[ARGSIZ]; /* 字列表存储空间 */
int trebuf[TRESIZ]; /* 语法树节点的存储空间 */
/* 诊断消息 */
char *mesg[] = {
NULL,
"Hangup", /* SIGHUP 1 */
NULL, /* SIGINT 2 */
"Quit", /* SIGQUIT 3 */
"Illegal instruction", /* SIGILL 4 */
"Trace trap", /* SIGTRAP 5 */
"Abort", /* SIGABRT 6 */
"Signal 7", /* SIGEMT 7 */
"Floating point exception", /* SIGFPE 8 */
"Killed", /* SIGKILL 9 */
"Signal 10", /* SIGBUS 10 */
"Segmentation violation", /* SIGSEGV 11 */
"Bad argument to system call", /* SIGSYS 12 */
NULL, /* SIGPIPE 13 */
"Alarm clock", /* SIGALRM 14 */
"Software termination signal from kill", /* SIGTERM 15 */
"Signal 16", /* 16 */
"Child process terminated or stopped", /* SIGSTOP 17 */
};
void lexscan(void);
int *parse(void);
int *command_list(char **p1, char **p2);
int *pipeline(char **p1, char **p2);
int *command(char **p1, char **p2);
void execute(int *t, int *pf1, int *pf2);
int redirect(int *t);
int execcmd(int *t);
int builtin(int *t);
int texec(char *f, int *at);
void word(void);
int *tree(int n);
void scan(int *at, int (*f)());
int tglob(int c);
int trim(int c);
int getC(void);
int readc(void);
void err(char *s);
void prs(char *as);
void putC(int c);
void prn(int n);
void sprn(int n, char *s);
int any(int c, char *as);
int equal(char *as1, char *as2);
int pwait(pid_t i);
char *shpath(char* s);
int main(int argc, char **argv)
{
register int f; /* 文件描述符 */
int *t; /* 语法树 */
/* 关闭所有打开的文件 */
for(f=STDERR_FILENO; f<sysconf(_SC_OPEN_MAX); f++)
close(f);
/* 复制标准输出到标准错误输出 */
if((f=dup(STDOUT_FILENO)) != STDERR_FILENO)
close(f);
/* 获得进程标识(32位整数),并转换成字符串,临时借用 dolc 变量 */
dolc = (int)getpid();
sprn(dolc, pidp);
/* 判断当前时候是否根用户,设置正确的提示符 */
if((uid = getuid()) == 0) /* 根用户 */
promp = "# ";
else
promp = "% ";
setuid(uid);
setgid(getgid());
/* 取得 shell 的绝对路径名 */
if (argv[0][0] == '/')
arg0 = argv[0];
else
arg0 = shpath(argv[0]);
if(argc > 1) { /* 有参数或选项 */
promp = 0; /* 设置为非交互模式 */
if (*argv[1]=='-') { /* 有选项 */
setintr++; /* 需要设置信号处理程序 */
if (argv[1][1]=='c' && argc>2) /* -c 把下一个参数作为命令执行 */
arginp = argv[2];
else if (argv[1][1]=='t') /* -t: 从标准输入读入一行执行并退出 */
onelflg = 2;
} else { /* 有命令文件 */
/* 在标准输入上打开包含要执行的命令的文件 */
close(STDIN_FILENO);
f = open(argv[1], O_RDONLY);
if(f < 0) { /* 打不开指定文件 */
prs(argv[1]);
err(": cannot open");
}
}
}
if(setintr) {
/* 设置中断信号处理程序为忽略信号 */
signal(SIGQUIT, SIG_IGN);
signal(SIGINT, SIG_IGN);
}
dolv = argv+1; /* 参数列表指针右移 */
dolc = argc-1; /* 参数数目减少 */
/* 主循环: 扫描、分析和执行命令行 */
for(;;) {
error = 0;
gflg = 0;
if(promp != 0) /* 交互模式运行 */
prs(promp);
lexscan();
if(gflg != 0) {/* 发生命令行字符溢出 */
err("Command line overflow");
continue;
}
t = parse();
if(error != 0) {/* 语法分析发现错误 */
err("syntax error");
continue;
}
execute(t,NULL,NULL);
}
return 0;
}
/* 词法扫描 */
void lexscan(void)
{
register char *cp;
register int c;
/* 初始化行缓冲区、字列表空间和相关指针 */
argp = args;
eargp = args+ARGSIZ-5;
linep = line;
elinep = line+LINSIZ-5;
/* 过滤掉注释行 */
do c = getC();
while (c == ' ' || c == '\t');
if (c == '#')
while ((c = getC()) != '\n');
peekc = (char) c; /* 送回最后的换行符 */
/* 把命令行扫描到字列表中 */
do {
cp = linep; /* cp 指向当前要读入的字在行缓冲区中的位置 */
word(); /* 读入一个字到行缓冲区中 */
} while(*cp != '\n'); /* 循环直到读到换行符 */
}
/* 语法分析 */
int * parse(void)
{
/* 初始化语法树节点空间和相关指针 */
treep = trebuf;
treeend = &trebuf[TRESIZ];
setjmp(jmpbuf);
if (error) /* 分析出语法错误或没有空间分配语法树节点的了 */
return NULL;
/* args 指向是字列表的第一个元素,
* argp 指向是字列表的最后一个元素后面的一个元素 */
return command_list(args, argp);
}
/*
* 从输入中读出一个字到行缓冲区中,并增加一个字列表元素
*/
void word(void)
{
register int c, c1;
/* 字列表的当前元素指针指向当前要读入的字在行缓冲区中的位置 */
*argp++ = linep;
/* 忽略字前空白 */
do c = getC();
while (c == ' ' || c == '\t');
/* 处理 shell 的元字符和换行符 */
if(any(c, ";&<>()|\n")) {
*linep++ = (char) c; /* 把这个元字符或换行符写到行缓冲区中 */
*linep++ = '\0'; /* 终结这个字符串 */
return;
}
/* 读到普通字符 */
peekc = c; /* 送回这个普通字符 */
for(;;) {
c = getC();
if(any(c, " \t;&<>()|\n")) { /* 读到空白、元字符或换行符 */
peekc = (char) c; /* 送回这个字符 */
*linep++ = '\0'; /* 终结这个字符串 */
return;
}
if(c == '\'' || c == '"') {/* 读到单双引号 */
c1 = c;
while((c=readc()) != c1) {
if(c == '\n') {
error++; /* 引用没有在本行完结 */
peekc = (char) c; /* 送回这个字符 */
return;
}
/* 对引号包围的字符设置引用标志位,并写到行缓冲区中 */
*linep++ = (char) c|QUOTE;
}
continue;
}
*linep++ = (char) c; /* 把这个普通字符写入行缓冲区 */
}
}
/* 分配指定大小的树结点 */
int * tree(int n)
{
int *t;
t = treep;
treep += n;
if (treep>treeend) { /* 语法树节点空间不够 */
prs("Command line overflow\n");
error++;
longjmp(jmpbuf,1);
}
return t;
}
/* 从输入中读出一个字符 */
int getC(void)
{
int c;
if(peekc) { /* 已经预读了一个字符 */
c = peekc;
peekc = 0;
return c;
}
if(argp > eargp) { /* 字列表空间溢出 */
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;
}
if(dolp == NULL) { /* 当前未处理 $ */
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[c-'0'];
}
if(c == '$') { /* 进程标识 */
dolp = pidp;
}
if(c == '?') { /* 退出状态 */
sprn(exitstat, exitp);
dolp = exitp;
}
}
}
if (dolp != NULL) { /* 当前处理 $ */
c = *dolp++;
if(c != '\0')
return c;
dolp = NULL;
}
return c&~QUOTE; /* 清除引用位 */
}
int readc(void)
{
char cc;
int c;
if (arginp) { /* 有 -c 选项,onelflg == 0 */
/* 从 arginp 中读取字符 */
if ((c = (int)*arginp++) == 0) { /* 没有要作为命令的参数 */
arginp = NULL;
onelflg++; /* 设置下次执行本函数的时候退出 */
c = '\n';
}
return c;
}
if (onelflg==1)
exit(0);
if(read(STDIN_FILENO, &cc, 1) != 1) /* 读一个字符 */
exit(0);
/* 有 -t 选项,onelflg == 2,从标准输入读入了换行符 */
if (cc=='\n' && onelflg)
onelflg--; /* 设置下次执行本函数的时候退出 */
return (int)cc;
}
/*
* command_list:
* empty
* pipeline
* pipeline & command_list
* pipeline ; command_list
*/
int * command_list(char **p1, char **p2)
{
register char **p;
int *t, *t1;
int l;
/* 忽略前导的列表分隔符 */
while(p1 != p2 && any(**p1, ";&\n"))
p1++;
if (p1 == p2)
return NULL; /* 空命令 */
l = 0; /* 嵌套层数 */
for(p=p1; p!=p2; p++)
switch(**p) {
case '(':
l++;
break;
case ')':
l--;
if(l < 0)
error++;
break;
/* 找到第一个列表分隔符 */
case '&':
case ';':
case '\n':
if(l == 0) {
l = **p;
t = tree(4);
t[DTYP] = TLST; /* 类型是命令列表 */
t[DLEF] = (int)pipeline(p1, p);
t[DFLG] = 0; /* 命令列表节点不设置任何标志位 */
if(l == '&') { /* 需要后台处理 */
t1 = (int *)t[DLEF];
/* 设置左子节点标志 */
t1[DFLG] |= FAND|FPRS|FINT;
/*
* 后台运行的这些标志位只设置到在管道线节点上,
* 所以在执行的时候需要通过继承来下传到各个命令节点上。
*/
}
t[DRIT] = (int)command_list(p+1, p2);
return t;
}
}
/* 没有找到列表分隔符,发生在命令来自参数的时候 */
if(l == 0)
return pipeline(p1, p2);
error++;
return NULL;
}
/*
* pipeline:
* command
* command | pipeline
*/
int * pipeline(char **p1, char **p2)
{
register char **p;
int l, *t;
l = 0; /* 嵌套层数 */
for(p=p1; p!=p2; p++)
switch(**p) {
case '(':
l++;
break;
case ')':
l--;
break;
/* 找到第一个管道符号 */
case '|':
if(l == 0) {
t = tree(4);
t[DTYP] = TFIL; /* 类型是管道线 */
t[DLEF] = (int)command(p1, p);
t[DRIT] = (int)pipeline(p+1, p2);
t[DFLG] = 0; /* 标志位由上级的 command_list 设置 */
return t;
}
}
/* 没有找到管道符号,是管道线末端或简单命令 */
return command(p1, p2);
}
/*
* command:
* ( command_list ) [ < in ] [ > out ]
* word word* [ < in ] [ > out ]
*/
int * command(char **p1, char **p2)
{
register char **p;
char **lp = NULL, **rp = NULL;
int *t;
int n = 0, l = 0, i = 0, o = 0, c, flg = 0;
if(**p2 == ')') /* 这个命令是在圆括号包围中的命令列表的最后一个命令 */
flg |= FPAR;
for(p=p1; p!=p2; p++)
switch(c = **p) {
case '(':
if(l == 0) {
if(lp != NULL)
error++;
lp = p+1; /* 最外层圆括号内列表的第一个字 */
}
l++;
break;
case ')':
l--;
if(l == 0)
rp = p; /* 最外层圆括号内的最后一个字后面的')' */
break;
case '>':
p++;
if(p!=p2 && **p=='>') /* >> 重定向 */
flg |= FCAT;
else
p--;
case '<':
if(l == 0) {
p++; /* 重定向的文件描述符 */
if(p == p2) { /* 重定向之后没有字符了 */
error++;
p--;
}
if(any(**p, "<>(")) /* 重定向之后的字符不是合法字符 */
error++;
if(c == '<') {
if(i != 0)
error++;
i = (int)*p; /* 输入重定向文件的全路径名 */
} else { /* > 或 >> */
if(o != 0)
error++;
o = (int)*p; /* 输出重定向文件的全路径名 */
}
break;
}
default:
if(l == 0) /* 有不在圆括号中的普通的字 */
p1[n++] = *p;
/* 形成简单命令的字列表,去掉了重定向符号和相应的文件描述符 */
}
if(lp != 0) { /* 有圆括号 */
if(n != 0) /* 有不在圆括号中的普通的字 */
error++;
t = tree(5); /* 有 DSPR 字段 */
t[DTYP] = TPAR; /* 类型是复合命令 */
t[DSPR] = (int)command_list(lp, rp);
} else { /* 简单命令 */
if(n == 0) /* 没有命令名字 */
error++;
p1[n++] = 0; /* 结束字列表 */
t = tree(n+5);
/* 在语法树节点中有 DSPR,DCOM 字段,并增加了字列表的空间 */
t[DTYP] = TCOM; /* 类型是简单命令 */
for(l=0; l<n; l++) /* 复制字列表到语法树节点 */
t[l+DCOM] = (int)p1[l];
}
t[DFLG] = flg; /* 根据情况设置的 FPAR 或 FCAT 标志 */
t[DLEF] = i; /* 输入重定向文件的全路径名 */
t[DRIT] = o; /* 输出重定向文件的全路径名 */
return t;
}
/* 扫描字列表 */
void scan(int *at, int (*f)())
{
register char *p, c;
int *t;
t = at+DCOM;
while((p = (char *)*t++) != NULL)
while((c = *p) != 0)
*p++ = (*f)(c);
}
/* 检测是否包含通配符 */
int tglob(int c)
{
if(any(c, "[?*"))
gflg = 1;
return c;
}
/* 清除引用标志位 */
int trim(int c)
{
return c&~QUOTE;
}
/*
* 管道线语法树的执行,pf1 流入管道,pf2 流出管道
* a | b | c 管道线的语法树:
* TFIL1
* / \
* TCOM1 TFIL2
* | / \
* a TCOM2 TCOM3
* | |
* b c
* 执行序列:
* execute(TFIL1, NULL, NULL);
* execute(TOCM1, NULL, pv1); TOCM1.TFLG: FPOU; 父进程打开着 pv1
* execute(TFIL2, pv1, NULL); TFIL2.TFLG: FPIN;
* execute(TCOM2, pv1, pv2); TCOM2.TFLG: FPIN, FPOU; 父进程关闭了 pv1, 打开着 pv2
* execute(TCOM3, pv2, NULL); TCOM3.TFLG: FPIN; 父进程关闭了 pv2
*/
void execute(int *t, int *pf1, int *pf2)
{
int i, f, pv[2];
register int *t1;
if(t == 0)
return;
switch(t[DTYP]) {
case TLST: /* 命令列表类型 */
f = t[DFLG];
if((t1 = (int *)t[DLEF]) != NULL)
/* 向子节点下传 FINT 标志的当前状态 */
t1[DFLG] |= f&FINT;
execute(t1,NULL,NULL);
if((t1 = (int *)t[DRIT]) != NULL)
t1[DFLG] |= f&FINT;
execute(t1,NULL,NULL);
/*
* 命令列表位于复合命令中当中的时候,TLST 节点
* 只从上层 TPAR 节点继承 FINT 标志。
*/
return;
case TFIL: /* 管道线类型 */
f = t[DFLG];
pipe(pv); /* 建立管道 */
t1 = (int *)t[DLEF];
/* 向左子节点下传 FPIN FINT FPRS 标志的当前状态,
* 并设置它的 FPOU 标志 */
t1[DFLG] |= FPOU | (f&(FPIN|FINT|FPRS));
execute(t1, pf1, pv); /* 命令输出流入新建的管道 */
t1 = (int *)t[DRIT];
/* 向右子节点下传 FPOU FINT FAND FPRS 标志的当前状态,
* 并设置它的 FPIN 标志 */
t1[DFLG] |= FPIN | (f&(FPOU|FINT|FAND|FPRS));
execute(t1, pv, pf2); /* 命令输入来自新建的管道 */
/*
* 只有管道线末端的命令能从上层节点继承到 FAND 标志
*/
return;
case TCOM: /* 简单命令类型入口 */
/* 筛选出内置命令 */
if (builtin(t))
return;
case TPAR: /* 复合命令类型切入点 */
f = t[DFLG];
i = 0;
/*
* 为简单命令、复合命令整体和复合命令除最后一个命令之外的所有命令
* 建立子进程,复合命令最后一个命令使用为复合命令整体建立的子进程
*/
if((f&FPAR) == 0)
i = fork();
if(i == -1) { /* 进程复制失败 */
err("try again");
return;
}
/*
* 父进程执行部分
*/
if(i != 0) {
if((f&FPIN) != 0) { /* 子进程有流入管道 */
/* 在读管道的子进程没有建立之前,父进程保持打开这个管道
* 当读管道的子进程已经建立之后,父进程关闭管道读出写入端 */
close(pf1[0]);
close(pf1[1]);
}
if((f&FPRS) != 0) { /* 需要打印子进程 pid */
prn(i);
prs("\n");
}
if((f&FAND) != 0) { /* 后台命令不需要等待子进程 */
exitstat = 0;
return;
}
if((f&FPOU) == 0) /* 子进程是前台的简单命令、或管道线末端的命令 */
exitstat = pwait(i); /* 等待子进程终止 */
return;
}
/*
* 子进程执行部分
*/
if (redirect(t)) /* 重定向标准输入输出 */
exit(1);
if((f&FPIN) != 0) { /* 有流入管道 */
close(STDIN_FILENO);
/* 接入管道读出端 */
dup(pf1[0]);
close(pf1[0]);
/* 关闭管道写入端 */
close(pf1[1]);
}
if((f&FPOU) != 0) { /* 有流出管道 */
/* 接入管道写入端 */
close(STDOUT_FILENO);
dup(pf2[1]);
close(pf2[0]);
/* 关闭管道读出端 */
close(pf2[1]);
}
/* 后台进程没有做重定向其标准输入 */
if((f&FINT)!=0 && t[DLEF]==0 && (f&FPIN)==0) {
close(STDIN_FILENO);
open("/dev/null", O_RDONLY);
}
/* 是未设置中断信号忽略标志的前台命令,
* 并在非交互模式下已经设置中断信号处理程序为忽略信号 */
if((f&FINT) == 0 && setintr) {
/* 恢复中断信号处理程序为缺省 */
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
}
/*
* 复合命令在子 shell 中执行,父 shell 节点中的 FAND, FPRS 标志位作用于
* 子 shell 本身的进程,子 shell 中的节点不继承 FAND, FPRS 标志位。
*/
if(t[DTYP] == TPAR) {
if((t1 = (int *)t[DSPR]) != NULL)
/* 向子节点下传 FINT 标志的当前状态 */
t1[DFLG] |= f&FINT;
execute(t1,NULL,NULL);
exit(1);
}
i=execcmd(t); /* 执行命令 */
exit(i);
}
}
/*
* 重定向标准输入输出
*/
int redirect(int *t)
{
int i;
int f = t[DFLG];
if(t[DLEF] != 0) { /* 有输入重定向 */
/* 重定向标准输入 */
close(STDIN_FILENO);
i = open((char *)t[DLEF], O_RDONLY);
if(i < 0) {
prs((char *)t[DLEF]);
err(": cannot open");
return 1;
}
}
if(t[DRIT] != 0) { /* 有输出重定向 */
if((f&FCAT) != 0) { /* >> 重定向 */
i = open((char *)t[DRIT], O_WRONLY);
if(i >= 0)
lseek(i, 0, SEEK_END);
} else {
i = creat((char *)t[DRIT], 0666);
if(i < 0) {
prs((char *)t[DRIT]);
err(": cannot create");
return 1;
}
}
/* 重定向标准输出 */
close(STDOUT_FILENO);
dup(i);
close(i);
}
return 0;
}
/*
* 执行语法树节点中的命令
*/
int execcmd(int *t)
{
register char *cp1, *cp2;
int i;
gflg = 0;
scan(t, tglob); /* 测试是否有通配符 */
if(gflg) {
extern int glob(int argc, char *argv[]);
char **cp;
t[DSPR] = (int)"/etc/glob";
for(i = 0, cp = (char **)(t+DSPR); *cp != NULL; i++, cp++);
i = glob(i, (char **)(t+DSPR));
return i;
}
/* 参数没有通配符的命令 */
scan(t, trim); /* 清除引用标志位 */
*linep = '\0';
i=texec((char *)t[DCOM], t);
if (i!=127)
return i;
/* 连接 /usr/bin/ 和 t[DCOM] 为一个字符串 */
cp1 = linep;
cp2 = "/usr/bin/";
while((*cp1 = *cp2++) != 0)
cp1++;
cp2 = (char *)t[DCOM];
while((*cp1++ = *cp2++) != 0);
i=texec(linep+4, t); /* 添加 /bin 前缀执行命令 */
if (i!=127)
return i;
i=texec(linep, t); /* 添加 /usr/bin 前缀执行命令 */
prs((char *)t[DCOM]);
err(": not found");
return i;
}
/*
* 内置命令处理
*/
int builtin(int *t)
{
char *cp1;
cp1 = (char *)t[DCOM];
if(equal(cp1, "cd") || equal(cp1, "chdir")) {
if((char *)t[DCOM+1] != NULL) {
if(chdir((char *)t[DCOM+1]) < 0)
err("chdir: bad directory");
} else
err("chdir: arg count");
return 1;
}
if(equal(cp1, "shift")) {
if(dolc < 1) {
prs("shift: no args\n");
return 1;
}
dolv[1] = dolv[0]; /* 命令文件名右移替代了第一个位置参数 */
dolv++;
dolc--;
return 1;
}
if(equal(cp1, "login")) {
if(promp != 0)
execv("/bin/login", (char **)(t+DCOM));
prs("login: cannot execute\n");
return 1;
}
if(equal(cp1, "newgrp")) {
if(promp != 0)
execv("/bin/newgrp", (char **)(t+DCOM));
prs("newgrp: cannot execute\n");
return 1;
}
if(equal(cp1, "wait")) {
pwait(-1); /* 等待所有子进程 */
return 1;
}
if(equal(cp1, ":"))
return 1;
if(equal(cp1, "exit")) {
char* cp2 = (char *)t[DCOM+1];
int c, i = 0;
if (cp2 != NULL) { /* 有参数 */
while ((c = *cp2++) >= '0' && c <= '9')
i = (i*10)+c-'0';
}
exit(i);
}
if(equal(cp1,"exec")) {
int i;
if (redirect(t)) { /* 重定向标准输入输出 */
exitstat = 1;
return 1;
}
if ((char *)t[DCOM+1] != NULL) {
i=execcmd(t+1);
exitstat = i;
return 1;
}
exitstat = 0;
return 1;
}
if(equal(cp1, "umask")) {
char* cp2 = (char *)t[DCOM+1];
if (cp2 != NULL) { /* 有参数 */
int c, i = 0;
while ((c = *cp2++) >= '0' && c <= '7')
i = (i<<3)+c-'0';
umask(i);
} else {
int i, j;
umask(i = umask(0));
putC('0');
for (j = 6; j >= 0; j-=3)
putC(((i>>j)&07)+'0');
putC('\n');
}
return 1;
}
return 0;
}
/* exec 和进行错误消息打印 */
int texec(char* f, int *at)
{
extern int errno;
register int *t;
t = at;
execv(f, (char **)(t+DCOM));
if (errno==EACCES) { /* 没有访问权限 */
prs("Permission denied\n");
return 126;
}
if (errno==ENOEXEC) { /* 不是二进制可执行文件 */
t[DCOM] = (int)f;
t[DSPR] = (int)arg0;
execv((char *)t[DSPR], (char **)(t+DSPR));
prs("No shell!\n");
return 126;
}
if (errno==ENOMEM) { /* 不能分配内存 */
prs((char *)t[DCOM]);
err(": too large");
return ENOMEM;
}
return 127;
}
/* 打印错误输出,在非交互模式下退出 */
void err(char *s)
{
prs(s);
prs("\n");
if(promp == 0) {
lseek(STDIN_FILENO, 0, SEEK_END);
exit(1);
}
}
/* 写一个字符串 */
void prs(char *as)
{
register char *s;
s = as;
while(*s)
putC(*s++);
}
/* 写一个字符 */
void putC(int c)
{
write(STDERR_FILENO, &c, 1);
}
/* 写一个数 */
void prn(int n)
{
register int a;
if((a=n/10) != 0)
prn(a);
putC((n%10)+'0');
}
/* 写一个数到字符串 */
void sprn(int n, char *s)
{
int i,j;
for (i=1000000000; n<i && i>1; i/=10); /* 32 位字长 */
for (j=0; i>0; j++,n%=i,i/=10)
s[j] =(char)(n/i + '0');
s[j] = '\0';
}
/* 判断一个字符是否在一个字符串中 */
int any(int c, char *as)
{
register char *s;
s = as;
while(*s)
if(*s++ == c)
return 1;
return 0;
}
/* 判断两个字符串是否相等 */
int equal(char *as1, char *as2)
{
register char *s1, *s2;
s1 = as1;
s2 = as2;
while(*s1++ == *s2)
if(*s2++ == '\0')
return 1;
return 0;
}
/* i>0 等待指定 pid 的进程终止,i<0 等待所有子进程终止,
* 如果异常终止,则打印诊断信息 */
int pwait(pid_t i)
{
pid_t p;
int e = 0, s;
if(i == 0)
return 0;
for(;;) {
p = wait(&s);
if(p == -1) /* 没有子进程需要等待了 */
break;
e = WTERMSIG(s); /* 取子进程终止状态 */
if(e > 0 && mesg[e] != NULL) {
if(p != i) {
prn(p);
prs(": ");
}
if (e <= 17)
prs(mesg[e]);
else
prs("Terminated abnormally");
/* if(WCOREDUMP(s))
* prs(" -- Core dumped"); */
err("");
}
if(i == p) /* 终止的子进程是要等待的子进程 */
break;
}
if (WIFEXITED(s))
return WEXITSTATUS(s);
else
return e | SIGFLG;
}
/*
* 把调用 shell 的相对路径名和当前工作路径合并成绝对路径名
*/
char *shpath(char* s)
{
#define SHPATH_MAX 80
static char shpathbuf[SHPATH_MAX+1]; /* 保存 sh 绝对路径名 */
register char *p1, *p2;
register char c;
if (getcwd(shpathbuf, SHPATH_MAX)==NULL) { /* 得到当前工作路径 */
err("shell path error");
exit(1);
}
p1 = shpathbuf;
while (*p1 != '\0')
p1++;
*p1++ = '/';
p2 = s;
c = *p2;
while (c != '\0') {
if (p2[0]== '.' && p2[1] == '/')
p2+=2;
else if (p2[0]== '.' && p2[1] == '.' && p2[2] == '/') {
p2+=3;
while(*(--p1) != '/');
}
else
do {
if (p1 > shpathbuf+SHPATH_MAX) {
err("shell path error");
exit(1);
}
*p1++ = c = *p2++;
} while (c != '/' && c != '\0');
}
return shpathbuf;
}
[/code] glob.c:
[code]
/* glob 命令 --
glob params
在 params 中的 "*" 匹配正则表达式 ".*"
在 params 中的 "?" 匹配正则表达式 "."
在 params 中的 "[...]" 匹配字符类
在 params 中的 "[!...]" 匹配字符类的补集。
在 params 中的 "[...a-z...]" 匹配 a 到 z。
执行命令并带有按如下规则构造的参数列表:
如果 param 不包含 "*","[",或 "?",则按原样使用它
如果包含,则在当前目录中找到匹配这个 param 的所有文件,
排序并使用它们。
按需要为命令名字添加 "/bin" 或 "/usr/bin" 前缀。
*/
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#define QUOTE 0x80 /* 引用标志位 */
#define STRSIZ _POSIX_ARG_MAX
extern char *arg0; /* 调用 sh 的全路径 */
static char ab[STRSIZ]; /* 生成的字符串的存储空间 */
static char *string;
static char *ava[STRSIZ/2]; /* 生成的参数列表 */
static char **av;
static int ncoll;
int glob(int argc, char *argv[]);
static int gexec(char *afile, char **aarg);
static void expand(char *as);
static void sort(char **oav);
static void toolong(void);
static int match(char *s, char *p);
static int amatch(char *as, char *ap);
static int umatch(char *s, char *p);
static int compare(char *as1, char *as2);
static char* cat(char *as1, char *as2);
int glob(int argc, char *argv[])
{
char *cp;
int r;
string = ab;
av = &ava[1]; /* 有意留出一个空位 */
if (argc < 2) {
write(STDERR_FILENO, "Arg count\n", 10);
exit (1);
}
argv++;
*av++ = *argv; /* 指向第一个参数,它是要执行的文件的名字 */
while (--argc >= 2)
expand(*++argv); /* 展开余下的所有的参数 */
if ((r = gexec(ava[1], &ava[1])))
return r;
cp = cat("/usr/bin/", ava[1]);
if ((r = gexec(cp+4, &ava[1])))
return r;
if ((r = gexec(cp, &ava[1])))
return r;
write(STDERR_FILENO, "Command not found.\n", 19);
return 127;
}
void expand(char *as)
{
register char *s, *cs;
struct dirent * dirp;
DIR *dp;
char **oav;
ncoll = 0;
s = cs = as;
/* 把 cs 定位到第一个通配符 */
while (*cs!='*' && *cs!='?' && *cs!='[') {
if (*cs++ == 0) { /* 没有找到通配符 */
*av++ = cat(s, "");
return;
}
}
for (;;) {
if (cs==s) { /* 在通配符之前没有'/' */
dp = opendir(".");
s = "";
break;
}
if (*--cs == '/') {
*cs = 0; /* 把参数分开为目录和文件名两个字符串 */
dp = opendir((s==cs)? "/": s);
*cs++ = QUOTE; /* 做标记,在后面的 cat 操作中恢复成斜杠 */
break;
}
}
if (dp == NULL) {
write(STDERR_FILENO, "No directory\n", 13);
exit (1);
}
oav = av;
while ((dirp = readdir(dp)) != NULL) {
if (match(dirp->d_name, cs)) {
*av++ = cat(s, dirp->d_name);
ncoll++;
}
}
if (!ncoll) /* 没有匹配 */
*av++ = cat(s, cs); /* 保持参数为原状 */
else /* 排序匹配的文件名字 */
sort(oav);
closedir(dp);
}
/* 排序 */
void sort(char **oav)
{
register char **p1, **p2, *c;
p1 = oav;
while (++p1 < av) {
c = *p1;
p2 = p1;
while (--p2 >= oav && compare(*p2, c) > 0)
*(p2+1) = *p2;
*(p2+1) = c;
}
}
/* 执行一个命令 */
int gexec(char *afile, char **aarg)
{
register char *file, **arg;
arg = aarg;
file = afile;
execv(file, arg);
if (errno == EACCES) {
write(STDERR_FILENO, "Permission denied\n", 18);
return 126;
}
if (errno==ENOEXEC) { /* 不是二进制可执行文件 */
arg[0] = file;
*--arg = arg0;
execv(*arg, arg); /* 作为命令文件执行 */
}
if (errno==E2BIG) {/* 在参数列表太长的时候退出 */
toolong();
return E2BIG;
}
return 0;
}
/* 打印错误消息并退出 */
void toolong(void)
{
write(2, "Arg list too long\n", 18);
}
/*匹配判断例程 */
int match(char *s, char *p)
{
if (*s=='.' && *p!='.') /* 对 . 开头的文件的处理 */
return 0;
return amatch(s, p);
}
/* 对字符的匹配 */
int amatch(char *as, char *ap)
{
register char *s, *p;
register int scc;
int c, cc, ok, lc;
int neg = 0;
s = as;
p = ap;
if ((scc = *s++) != 0) /* 文件名未终结 */
/* 传递给 glob 的参数中的字符可能设置了引用位,
这些字符在 shell 命令行中位于" "或' ' 中 */
if ((scc &= ~QUOTE) == 0) /* 如果 scc 清除了引用位之后是 0 */
scc = QUOTE; /* 重新把它设置为 QUOTE */
switch (c = *p++) {
case '[': /* 处理字符类 */
if (*p=='!') { /* 下个字符是 ! */
neg = 1;
p++;
}
ok = 0;
lc = INT_MAX;
while ((cc = *p++) != 0) {
if (cc==']') {
if ((ok && !neg) || (!ok && neg))
return amatch(s, p); /* 如果匹配,继续比较后面的字符 */
else
return 0;
} else if (cc=='-') {
if (lc<=scc && scc<=(c = *p++)) /* 在范围内 */
ok++;
} else
if (scc == (lc=cc))
ok++;
}
return 0;
default:
if (c!=scc)
return 0;
case '?':
if (scc)
return amatch(s, p);
return 0;
case '*':
return umatch(--s, p);
case '\0':
return !scc;
}
}
/* 对闭包的匹配 */
int umatch(char *s, char *p)
{
if(*p==0) /* 模式以 * 结束 */
return 1;
while(*s) /* 文件名中有字符匹配模式中 * 后面的字符 */
if (amatch(s++,p))
return 1;
return 0;
}
/* 比较两个字符串 */
int compare(char *as1, char *as2)
{
register char *s1, *s2;
s1 = as1;
s2 = as2;
while (*s1++ == *s2)
if (*s2++ == 0)
return 0;
return (*--s1 - *s2);
}
/* 联接两个字符串到 string 指向的数组中并返回它,
* 副作用是 string 指针右移,指向的数组空间减小 */
char* cat(char *as1, char *as2)
{
register char *s1, *s2;
register int c;
s2 = string;
s1 = as1;
while ((c = *s1++) != 0) {
if (s2 > &ab[STRSIZ])
toolong();
c &= ~QUOTE; /* 清除引用位 */
if (c==0) { /* 原先是路径分隔符 */
*s2++ = '/';
break;
}
*s2++ = c;
}
s1 = as2;
do {
if (s2 > &ab[STRSIZ])
toolong();
*s2++ = c = *s1++;
} while (c);
s1 = string;
string = s2;
return s1;
}
[/code]
if.c
[code]
/*
* if exprression cmd [arg]....
* if 是 test 命令的前身。在测试完表达式之后,如果为真则执行后面的命令。
* 这里的 if 命令建立在 v7 test 命令基础上。
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#define EQ(a,b) ((tmp=a)==0?0:(strcmp(tmp,b)==0))
int ac;
char **av;
int ap;
char *tmp;
#define DIR 1
#define FIL 2
char *nxtarg(int mt);
int expr(void);
int e1(void);
int e2(void);
int e3(void);
int tio(char *a, int f);
int ftype(char *f);
int fsizep(char *f);
void synbad(char *s1, char *s2);
int length(char *s);
int doex(char *earg);
int main(int argc, char**argv)
{
ac = argc;
av = argv;
ap = 1;
argv[ac] = 0;
if (ac<=1)
return 1;
if (expr())
return doex(0);
else
return 1;
}
/*
* 获得下一个参数。mt 指示在读到列表尾部的时候是否为语法错误,
* 1 返回空指针,0 提示错误并退出。
*/
char *nxtarg(int mt)
{
if (ap>=ac) {
if(mt) {
ap++;
return(0);
}
synbad("argument exprected","");
}
return(av[ap++]);
}
/*
* expr ::= * e1 -o expr | expr
*/
int expr(void)
{
int p1;
p1 = e1();
if (EQ(nxtarg(1), "-o"))
return(p1 | expr());
ap--;
return(p1);
}
/*
* e1 ::= e2 -a e1 | e1
*/
int e1(void)
{
int p1;
p1 = e2();
if (EQ(nxtarg(1), "-a"))
return (p1 & e1());
ap--;
return(p1);
}
/*
* e2 ::= e3 | ! e3
*/
int e2(void)
{
if (EQ(nxtarg(0), "!"))
return(!e3());
ap--;
return(e3());
}
/*
* e3 ::= ( expr ) | { command ... } | -op file | string -op string
*/
int e3(void)
{
int p1,r;
register char *a;
char *p2;
int int1, int2;
int ccode;
a=nxtarg(0);
if(EQ(a, "(")) {
p1 = expr();
if(!EQ(nxtarg(0), ")"))
synbad(") exprected","");
return(p1);
}
if(EQ(a, "{")) { /* 执行一个命令并等待退出状态 */
if(fork()) /* 父进程执行部分 */
wait(&ccode);
else { /* 子进程执行部分 */
if ((r=doex("}")) != 0)
if (r == 1)
synbad("} exprected","");
else
exit(r);
else
exit(0);
}
while((a=nxtarg(0)) && (!EQ(a,"}")));
return(ccode? 0 : 1);
}
if(EQ(a, "-r"))
return(tio(nxtarg(0), 0));
if(EQ(a, "-w"))
return(tio(nxtarg(0), 1));
if(EQ(a, "-d"))
return(ftype(nxtarg(0))==DIR);
if(EQ(a, "-f"))
return(ftype(nxtarg(0))==FIL);
if(EQ(a, "-s"))
return(fsizep(nxtarg(0)));
if(EQ(a, "-t")) {
if(ap>=ac)
return(isatty(1));
else
return(isatty(atoi(nxtarg(0))));
}
if(EQ(a, "-n"))
return(!EQ(nxtarg(0), ""));
if(EQ(a, "-z"))
return(EQ(nxtarg(0), ""));
p2 = nxtarg(1);
if (p2==0)
return(!EQ(a,""));
if(EQ(p2, "="))
return(EQ(nxtarg(0), a));
if(EQ(p2, "!="))
return(!EQ(nxtarg(0), a));
if(EQ(a, "-l")) {
int1=length(p2);
p2=nxtarg(0);
} else{ int1=atoi(a);
}
int2 = atoi(nxtarg(0));
if(EQ(p2, "-eq"))
return(int1==int2);
if(EQ(p2, "-ne"))
return(int1!=int2);
if(EQ(p2, "-gt"))
return(int1>int2);
if(EQ(p2, "-lt"))
return(int1<int2);
if(EQ(p2, "-ge"))
return(int1>=int2);
if(EQ(p2, "-le"))
return(int1<=int2);
synbad("unknown operator ",p2);
return 0;
}
/*
* 文件访问测试
*/
int tio(char *a, int f)
{
f = open(a, f);
if (f>=0) {
close(f);
return(1);
}
return(0);
}
/*
* 文件类型测试
*/
int ftype(char *f)
{
struct stat statb;
if(stat(f,&statb)<0)
return(0);
if((statb.st_mode&S_IFMT)==S_IFDIR)
return(DIR);
return(FIL);
}
/*
* 文件大小测试
*/
int fsizep(char *f)
{
struct stat statb;
if(stat(f,&statb)<0)
return(0);
return(statb.st_size>0);
}
/*
* 打印错误信息并退出
*/
void synbad(char *s1, char *s2)
{
write(2, "if: ", 6);
write(2, s1, strlen(s1));
write(2, s2, strlen(s2));
write(2, "\n", 1);
exit(1);
}
/*
* 字符串长度测试
*/
int length(char *s)
{
char *es=s;
while(*es++);
return((int)(es-s-1));
}
/*
* 执行命令
* earg 是终止参数
*/
int doex(char *earg)
{
static char ncom[]="/usr/bin/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
char *p=av[ap];
char **v=av+ap;
int i=0;
char c;
if (p == 0)
return 0;
if (earg != 0) {
while (*v != 0 && !EQ(*v,earg))
v++;
if (*v == 0)
return 1;
else
*v=0;
}
execv(p, av+ap);
while((c=p[i])) {
ncom[9+i++] = c;
}
ncom[9+i] = '\0';
execv(ncom+4, av+ap);
execv(ncom, av+ap);
return 127;
}
[/code]
goto.c
[code]
#include <unistd.h>
int offset=0;
int getlin(char *s);
int compar(char *s1, char *s2);
int getC(void);
int main(int argc, char **argv)
{
char line[64];
if (argc<2 || isatty(0)) {
write(1, "goto error\n", 11);
lseek(0, 0, 2);
return 1;
}
lseek(0, 0, 0);
do {
if (getlin(line)) {
write(1, "label not found\n", 16);
return 1;
}
} while (compar(line, argv[1]));
lseek(0, offset, 0);
return 0;
}
int getlin(char *s)
{
int ch, i;
i = 0;
while ((ch=getC())!='\0' && ch!=':') {
while(ch!='\n' && ch!='\0')
ch = getC();
}
if (ch=='\0')
return(1);
while ((ch=getC())==' ');
while (ch!=' ' && ch!='\n' && ch!='\0') {
s[i++] = ch;
ch = getC();
}
while(ch != '\n')
ch = getC();
s[i] = '\0';
return(0);
}
int compar(char *s1, char *s2)
{
int i;
i = 0;
while(1) {
if(s1[i] != s2[i])
return(1);
if (s1[i++] == '\0')
return(0);
}
}
int getC(void)
{
char cc;
offset++;
if(read(0, &cc, 1))
return cc;
else
return 0;
}
[/code]
页:
[1]