📄 shell.c
字号:
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -