sh.c

来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 1,098 行 · 第 1/2 页

C
1,098
字号
#ifndef lintstatic  char    *sccsid = "@(#)sh.c	4.2  (ULTRIX)        8/13/90";#endif lint/************************************************************************ *									* *			Copyright (c) 1988 by				* *		Digital Equipment Corporation, Maynard, MA		* *			All rights reserved.				* *									* *   This software is furnished under a license and may be used and	* *   copied  only  in accordance with the terms of such license and	* *   with the  inclusion  of  the  above  copyright  notice.   This	* *   software  or  any  other copies thereof may not be provided or	* *   otherwise made available to any other person.  No title to and	* *   ownership of the software is hereby transferred.			* *									* *   This software is  derived  from  software  received  from  the	* *   University    of   California,   Berkeley,   and   from   Bell	* *   Laboratories.  Use, duplication, or disclosure is  subject  to	* *   restrictions  under  license  agreements  with  University  of	* *   California and with AT&T.						* *									* *   The information in this software is subject to change  without	* *   notice  and should not be construed as a commitment by Digital	* *   Equipment Corporation.						* *									* *   Digital assumes no responsibility for the use  or  reliability	* *   of its software on equipment which is not supplied by Digital.	* *									* ************************************************************************//* * C Shell * * Bill Joy, UC Berkeley, California, USA * October 1978, May 1980 * * Jim Kulp, IIASA, Laxenburg, Austria * April 1980 * * Modification History: * * 005 - Bob Fontaine - Thu Jun 21 10:22:20 EDT 1990 *	change name of csh internal printf function to csh_printf to avoid *	conflicts with printf function in stdio library. * * 004 - Bob Fontaine - Fri May 25 10:00:14 EDT 1990 *	Stat argument to source command and make sure file exists and *	that it is a regular file (ie that it is not a directory, pipe ...). *	Fix for QAR# 3835 * * 003 - Gary A. Gaudet - Thu Dec 28 17:31:15 EST 1989 *	Added comments; removed code not compiled. * * 02	12-Nov-88, Al Delorey (afd). *	Use "ifdef CSHEDIT" around the cmd line edit code. * * 01	20-Sep-88, Al Delorey (afd) *	Added command line edit capability (search for "edit" to find changes). *	Deleted Mips tsh_init call. */#include "sh.h"#include <sys/ioctl.h>char 	*word();#ifdef CSHEDITchar 	*editword();#endifchar	*pathlist[] =	{ ".", "/usr/ucb", "/bin", "/usr/bin", 0 };char	*dumphist[] =	{ "history", "-h", 0, 0 };char	*loadhist[] =	{ "source", "-h", "~/.history", 0 };char	HIST = '!';char	HISTSUB = '^';bool	nofile;bool	reenter;bool	nverbose;bool	nexececho;bool	quitit;bool	fast;bool	batch;bool	prompt = 1;bool	enterhist = 0;extern	gid_t getegid(), getgid();extern	uid_t geteuid(), getuid();main(c, av)	int c;	char **av;{	register char **v, *cp;	register int f;	struct sigvec osv;/* *	immediately establish a timing base */	settimes();	v = av;/* *	a.out's are quittable */	if (eq(v[0], "a.out"))		quitit = 1;/* *	get user id */	uid = getuid();/* *	login shell if "-csh" with no arguments */	loginsh = **v == '-' && c == 1;	if (loginsh)		(void) time(&chktim);	/*	 * Move the descriptors to safe places.	 * The variable didfds is 0 while we have only FSH* to work with.	 * When didfds is true, we have 0,1,2 and prefer to use these.	 */	initdesc();	/*	 * Initialize the shell variables.	 * ARGV and PROMPT are initialized later.	 * STATUS is also munged in several places.	 * CHILD is munged when forking/waiting	 */	set("status", "0");	dinit(cp = getenv("HOME"));	/* dinit thinks that HOME == cwd in a					 * login shell */	if (cp == NOSTR)		fast++;			/* No home -> can't read scripts */	else		set("home", savestr(cp));	/*	 * Grab other useful things from the environment.	 * Should we grab everything??	 */	if ((cp = getenv("USER")) != NOSTR)		set("user", savestr(cp));	if ((cp = getenv("TERM")) != NOSTR)		set("term", savestr(cp));	/*	 * Re-initialize path if set in environment	 */	if ((cp = getenv("PATH")) == NOSTR)		set1("path", saveblk(pathlist), &shvhed);	else		importpath(cp);	set("shell", SHELLPATH);	doldol = putn(getpid());		/* For $$ */	shtemp = strspl("/tmp/sh", doldol);	/* For << */	/*	 * Record the interrupt states from the parent process.	 * If the parent is non-interruptible our hand must be forced	 * or we (and our children) won't be either.	 * Our children inherit termination from our parent.	 * We catch it only if we are the login shell.	 */		/* parents interruptibility */	(void) sigvec(SIGINT, (struct sigvec *)0, &osv);	parintr = osv.sv_handler;		/* parents terminability */	(void) sigvec(SIGTERM, (struct sigvec *)0, &osv);	parterm = osv.sv_handler;	if (loginsh) {		(void) signal(SIGHUP, phup);	/* exit processing on HUP */		(void) signal(SIGXCPU, phup);	/* ...and on XCPU */		(void) signal(SIGXFSZ, phup);	/* ...and on XFSZ */	}	/*	 * Process the arguments.	 *	 * Note that processing of -v/-x is actually delayed till after	 * script processing.	 */	c--, v++;	while (c > 0 && (cp = v[0])[0] == '-' && *++cp != '\0' && !batch) {		do switch (*cp++) {		case 'b':		/* -b	Next arg is input file */			batch++;			break;		case 'c':		/* -c	Command input from arg */			if (c == 1) {				exit(0);			}			c--, v++;			arginp = v[0];			prompt = 0;			nofile++;			break;		case 'e':		/* -e	Exit on any error */			exiterr++;			break;		case 'f':		/* -f	Fast start */			fast++;			break;		case 'i':		/* -i	Interactive, even if !intty */			intact++;			nofile++;			break;		case 'n':		/* -n	Don't execute */			noexec++;			break;		case 'q':		/* -q	(Undoc'd) ... die on quit */			quitit = 1;			break;		case 's':		/* -s	Read from std input */			nofile++;			break;		case 't':		/* -t	Read one line from input */			onelflg = 2;			prompt = 0;			nofile++;			break;		case 'v':		/* -v	Echo hist expanded input */			nverbose = 1;			/* ... later */			break;		case 'x':		/* -x	Echo just before execution */			nexececho = 1;			/* ... later */			break;		case 'V':		/* -V	Echo hist expanded input */			setNS("verbose");		/* NOW! */			break;		case 'X':		/* -X	Echo just before execution */			setNS("echo");			/* NOW! */			break;		} while (*cp);		v++, c--;	}	if (quitit)			/* With all due haste, for debugging */		(void) signal(SIGQUIT, SIG_DFL);	/*	 * Unless prevented by -c, -i, -s, or -t, if there	 * are remaining arguments the first of them is the name	 * of a shell file from which to read commands.	 */	if (nofile == 0 && c > 0) {		nofile = open(v[0], 0);		if (nofile < 0) {			child++;		/* So this ... */			Perror(v[0]);		/* ... doesn't return */		}		file = v[0];		SHIN = dmove(nofile, FSHIN);	/* Replace FSHIN */		prompt = 0;		c--, v++;	}	if (!batch && (uid != geteuid() || getgid() != getegid())) {		errno = EACCES;		child++;			/* So this ... */		Perror("csh");			/* ... doesn't return */	}	/*	 * Consider input a tty if it really is or we are interactive.	 */	intty = intact || isatty(SHIN);	/*	 * Decide whether we should play with signals or not.	 * If we are explicitly told (via -i, or -) or we are a login	 * shell (arg0 starts with -) or the input and output are both	 * the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")	 * Note that in only the login shell is it likely that parent	 * may have set signals to be ignored	 */	if (loginsh || intact || intty && isatty(SHOUT))		setintr = 1;#ifdef TELL	settell();#endif	/*	 * Save the remaining arguments in argv.	 */	setq("argv", v, &shvhed);	/*	 * Set up the prompt.	 */	if (prompt)		set("prompt", uid == 0 ? "# " : "% ");	/*	 * If we are an interactive shell, then start fiddling	 * with the signals; this is a tricky game.	 */	shpgrp = getpgrp(0);	opgrp = tpgrp = -1;	oldisc = -1;	if (setintr) {		**av = '-';		if (!quitit)		/* Wary! */			(void) signal(SIGQUIT, SIG_IGN);		(void) signal(SIGINT, pintr);		(void) sigblock(sigmask(SIGINT));		(void) signal(SIGTERM, SIG_IGN);		if (quitit == 0 && arginp == 0) {			(void) signal(SIGTSTP, SIG_IGN);			(void) signal(SIGTTIN, SIG_IGN);			(void) signal(SIGTTOU, SIG_IGN);			/*			 * Wait till in foreground, in case someone			 * stupidly runs			 *	csh &			 * dont want to try to grab away the tty.			 */			if (isatty(FSHDIAG))				f = FSHDIAG;			else if (isatty(FSHOUT))				f = FSHOUT;			else if (isatty(OLDSTD))				f = OLDSTD;			else				f = -1;retry:			if (ioctl(f, TIOCGPGRP, (char *)&tpgrp) == 0 &&			    tpgrp != -1) {				int ldisc;				if (tpgrp != shpgrp) {					void (*old)() = signal(SIGTTIN,SIG_DFL);					(void) kill(0, SIGTTIN);					(void) signal(SIGTTIN, old);					goto retry;				}				if (ioctl(f, TIOCGETD, (char *)&oldisc) != 0) 					goto notty;				if (oldisc != NTTYDISC) {#ifdef DEBUG					csh_printf("Switching to new tty driver...\n"); /* 005 RNF */#endif DEBUG					ldisc = NTTYDISC;					(void) ioctl(f, TIOCSETD,						(char *)&ldisc);				} else					oldisc = -1;				opgrp = shpgrp;				shpgrp = getpid();				tpgrp = shpgrp;				(void) ioctl(f, TIOCSPGRP, (char *)&shpgrp);				(void) setpgrp(0, shpgrp);				dcopy(f, FSHTTY);				ioctl(FSHTTY, FIOCLEX, 0);			} else {notty:  				csh_printf("Warning: no access to tty; thus no job control in this shell...\n"); /* 005 RNF */				tpgrp = -1;			}		}	}	if (setintr == 0 && parintr == SIG_DFL)		setintr++;	(void) signal(SIGCHLD, pchild);	/* while signals not ready */	/*	 * Set an exit here in case of an interrupt or error reading	 * the shell start-up scripts.	 */	setexit();	haderr = 0;		/* In case second time through */	if (!fast && reenter == 0) {		reenter++;		/* Will have value("home") here because set fast if don't */		srccat(value("home"), "/.cshrc");		if (!fast && !arginp && !onelflg && !havhash)			dohash();		if (loginsh) {			srccat(value("home"), "/.login");		}		dosource(loadhist);	}	/*	 * Now are ready for the -v and -x flags	 */	if (nverbose)		setNS("verbose");	if (nexececho)		setNS("echo");	/*	 * All the rest of the world is inside this call.	 * The argument to process indicates whether it should	 * catch "error unwinds".  Thus if we are a interactive shell	 * our call here will never return by being blown past on an error.	 */	process(setintr);	/*	 * Mop-up.	 */	if (loginsh) {		csh_printf("logout\n");  		/* 005 RNF */		(void) close(SHIN);		child++;		goodbye();	}	rechist();	exitstat();}untty(){	if (tpgrp > 0) {		(void) setpgrp(0, opgrp);		(void) ioctl(FSHTTY, TIOCSPGRP, (char *)&opgrp);		if (oldisc != -1 && oldisc != NTTYDISC) {#ifdef DEBUG			csh_printf("\nReverting to old tty driver...\n"); /* 005 RNF */#endif DEBUG			(void) ioctl(FSHTTY, TIOCSETD, (char *)&oldisc);		}	}}importpath(cp)	char *cp;{	register int i = 0;	register char *dp;	register char **pv;	int c;	static char dot[2] = {'.', 0};	for (dp = cp; *dp; dp++)		if (*dp == ':')			i++;	/*	 * i+2 where i is the number of colons in the path.	 * There are i+1 directories in the path plus we need	 * room for a zero terminator.	 */	pv = (char **) calloc((unsigned) (i + 2), sizeof (char **));	dp = cp;	i = 0;	if (*dp)	for (;;) {		if ((c = *dp) == ':' || c == 0) {			*dp = 0;			pv[i++] = savestr(*cp ? cp : dot);			if (c) {				cp = dp + 1;				*dp = ':';			} else				break;		}		dp++;	}	pv[i] = 0;	set1("path", pv, &shvhed);}/* * Source to the file which is the catenation of the argument names. */srccat(cp, dp)	char *cp, *dp;{	register char *ep = strspl(cp, dp);	register int unit = dmove(open(ep, 0), -1);	xfree(ep);/* * There is code here to prevent from automatically source'ing $home/.cshrc * and $home/.login if you don't own them. This probably isn't a good * idea in two cases: * *	1. A program may exist whose sole purpose is to change your uid *	   and groups without changing you to that user (for example, su -e *	   in some versions of Unix). * *	2. You may be sharing a home directory with someone else. This was *	   the case with a group I worked in once. */#ifdef notdef	srcunit(unit, 1, 0);#else	srcunit(unit, 0, 0);#endif}/* * Source to a unit.  If onlyown it must be our file or our group or * we don't chance it.	This occurs on ".cshrc"s and the like. */srcunit(unit, onlyown, hflg)	register int unit;	bool onlyown;	bool hflg;{	/* We have to push down a lot of state here */	/* All this could go into a structure */	int oSHIN = -1, oldintty = intty;	struct whyle *oldwhyl = whyles;	char *ogointr = gointr, *oarginp = arginp;	char *oevalp = evalp, **oevalvec = evalvec;	int oonelflg = onelflg;	bool oenterhist = enterhist;	char OHIST = HIST;#ifdef TELL	bool otell = cantell;#endif	struct Bin saveB;	/* The (few) real local variables */	jmp_buf oldexit;	int reenter, omask;	if (unit < 0)		return;	if (didfds)		donefds();	if (onlyown) {		struct stat stb;		if (fstat(unit, &stb) < 0 ||		    (stb.st_uid != uid && stb.st_gid != getgid())) {			(void) close(unit);			return;		}	}	/*	 * There is a critical section here while we are pushing down the	 * input stream since we have stuff in different structures.	 * If we weren't careful an interrupt could corrupt SHIN's Bin	 * structure and kill the shell.	 *

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?