⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shell.c

📁 Unix 第 6 版的 sh 手册和源码
💻 C
📖 第 1 页 / 共 2 页
字号:
		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 + -