343 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
 | |
|  *
 | |
|  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
 | |
|  */
 | |
| /*
 | |
|  * Copyright (c) 1980, 1993
 | |
|  *	The Regents of the University of California.  All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. 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.
 | |
|  * 3. All advertising materials mentioning features or use of this software
 | |
|  *    must display the following acknowledgement:
 | |
|  *	This product includes software developed by the University of
 | |
|  *	California, Berkeley and its contributors.
 | |
|  * 4. Neither the name of the University nor the names of its contributors
 | |
|  *    may be used to endorse or promote products derived from this software
 | |
|  *    without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
 | |
|  */
 | |
| 
 | |
| #ifndef lint
 | |
| #ifdef	DOSCCS
 | |
| static char sccsid[] = "@(#)lex.c	2.86 (gritter) 12/25/06";
 | |
| #endif
 | |
| #endif /* not lint */
 | |
| 
 | |
| #include "rcv.h"
 | |
| #include "extern.h"
 | |
| #include <errno.h>
 | |
| #include <sys/stat.h>
 | |
| #include <fcntl.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include "mail.h"
 | |
| /*
 | |
|  * Mail -- a mail program
 | |
|  *
 | |
|  * Lexical processing of commands.
 | |
|  */
 | |
| 
 | |
| static char	*prompt;
 | |
| static sighandler_type	oldpipe;
 | |
| 
 | |
| static const struct cmd *lex(char *Word);
 | |
| static void stop(int s);
 | |
| static void hangup(int s);
 | |
| 
 | |
| static int	*msgvec;
 | |
| static int	reset_on_stop;			/* do a reset() if stopped */
 | |
| 
 | |
| /*
 | |
|  * Interpret user commands one by one.  If standard input is not a tty,
 | |
|  * print no prompt.
 | |
|  */
 | |
| void 
 | |
| commands(void)
 | |
| {
 | |
| 	int eofloop = 0;
 | |
| 	int n, x;
 | |
| 	char *linebuf = NULL, *av, *nv;
 | |
| 	size_t linesize = 0;
 | |
| 
 | |
| 	(void)&eofloop;
 | |
| 	if (!sourcing) {
 | |
| 		if (safe_signal(SIGHUP, SIG_IGN) != SIG_IGN)
 | |
| 			safe_signal(SIGHUP, hangup);
 | |
| 		safe_signal(SIGTSTP, stop);
 | |
| 		safe_signal(SIGTTOU, stop);
 | |
| 		safe_signal(SIGTTIN, stop);
 | |
| 	}
 | |
| 	oldpipe = safe_signal(SIGPIPE, SIG_IGN);
 | |
| 	safe_signal(SIGPIPE, oldpipe);
 | |
| 	setexit();
 | |
| 	for (;;) {
 | |
| 		interrupts = 0;
 | |
| 		handlerstacktop = NULL; 
 | |
| 		fflush(stdout);
 | |
| 		sreset();
 | |
| 		/*
 | |
| 		 * Read a line of commands from the current input
 | |
| 		 * and handle end of file specially.
 | |
| 		 */
 | |
| 		n = 0;
 | |
| 		
 | |
| 		for (;;) {
 | |
| 			n = readline_restart(input, &linebuf, &linesize, n);
 | |
| 			if (n < 0)
 | |
| 				break;
 | |
| 			if (n == 0 || linebuf[n - 1] != '\\')
 | |
| 				break;
 | |
| 			linebuf[n - 1] = ' ';
 | |
| 		}
 | |
| 		
 | |
| 		reset_on_stop = 0;
 | |
| 		if (n < 0) {
 | |
| 				/* eof */
 | |
| 			if (loading)
 | |
| 				break;
 | |
| 			if (sourcing) {
 | |
| 				unstack();
 | |
| 				continue;
 | |
| 			} 
 | |
| 			break;
 | |
| 		}
 | |
| 		eofloop = 0;
 | |
| 		inhook = 0;
 | |
| 		if (execute(linebuf, 0, n))
 | |
| 			break;
 | |
| 	}
 | |
| 	if (linebuf)
 | |
| 		free(linebuf);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Execute a single command.
 | |
|  * Command functions return 0 for success, 1 for error, and -1
 | |
|  * for abort.  A 1 or -1 aborts a load or source.  A -1 aborts
 | |
|  * the interactive command loop.
 | |
|  * Contxt is non-zero if called while composing mail.
 | |
|  */
 | |
| int
 | |
| execute(char *linebuf, int contxt, size_t linesize)
 | |
| {
 | |
| 	char *word;
 | |
| 	char *arglist[MAXARGC];
 | |
| 	const struct cmd *com = (struct cmd *)NULL;
 | |
| 	char *cp, *cp2;
 | |
| 	int c;
 | |
| 	int muvec[2];
 | |
| 	int e = 1;
 | |
| 
 | |
| 	/*
 | |
| 	 * Strip the white space away from the beginning
 | |
| 	 * of the command, then scan out a word, which
 | |
| 	 * consists of anything except digits and white space.
 | |
| 	 *
 | |
| 	 * Handle ! escapes differently to get the correct
 | |
| 	 * lexical conventions.
 | |
| 	 */
 | |
| 	word = ac_alloc(linesize + 1);
 | |
| 	for (cp = linebuf; whitechar(*cp & 0377); cp++);
 | |
| 	cp2 = word;
 | |
| 	if (*cp != '|') {
 | |
| 		while (*cp && strchr(" \t0123456789$^.:/-+*'\",;(`", *cp)
 | |
| 				== NULL)
 | |
| 			*cp2++ = *cp++;
 | |
| 	} else
 | |
| 		*cp2++ = *cp++;
 | |
| 	*cp2 = '\0';
 | |
| 
 | |
| 	/*
 | |
| 	 * Look up the command; if not found, bitch.
 | |
| 	 * Normally, a blank command would map to the
 | |
| 	 * first command in the table; while sourcing,
 | |
| 	 * however, we ignore blank lines to eliminate
 | |
| 	 * confusion.
 | |
| 	 */
 | |
| 
 | |
| 	if (sourcing && *word == '\0') {
 | |
| 		ac_free(word);
 | |
| 		return(0);
 | |
| 	}
 | |
| 	
 | |
| 	com = lex(word);
 | |
| 	if (com == NULL) {
 | |
| 		DEBUG_PRINT(catgets(catd, CATSET, 91,
 | |
| 				"Unknown command: \"%s\"\n"), word);
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * See if we should execute the command -- if a conditional
 | |
| 	 * we always execute it, otherwise, check the state of cond.
 | |
| 	 */
 | |
| 
 | |
| 	if ((com->c_argtype & F) == 0) {
 | |
| 		if ((cond == CRCV && !rcvmode) ||
 | |
| 				(cond == CSEND && rcvmode) ||
 | |
| 				(cond == CTERM)) {
 | |
| 			ac_free(word);
 | |
| 			return(0);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Process the arguments to the command, depending
 | |
| 	 * on the type he expects.  Default to an error.
 | |
| 	 * If we are sourcing an interactive command, it's
 | |
| 	 * an error.
 | |
| 	 */
 | |
| 
 | |
| 	if (!rcvmode && (com->c_argtype & M) == 0) {
 | |
| 		DEBUG_PRINT(catgets(catd, CATSET, 92,
 | |
| 			"May not execute \"%s\" while sending\n"), com->c_name);
 | |
| 		goto out;
 | |
| 	}
 | |
| 	switch (com->c_argtype & ~(F|P|I|M|T|W|R|A)) {
 | |
| 		/* only command 'set' supported */
 | |
| 		case RAWLIST:
 | |
| 			/*
 | |
| 			 * A vector of strings, in shell style.
 | |
| 			 */
 | |
| 			if ((c = getrawlist(cp, linesize, arglist,
 | |
| 					sizeof arglist / sizeof *arglist,
 | |
| 					(com->c_argtype&~(F|P|I|M|T|W|R|A))==ECHOLIST))
 | |
| 						< 0)
 | |
| 				break;
 | |
| 			if (c < com->c_minargs) {
 | |
| 				DEBUG_PRINT(catgets(catd, CATSET, 99,
 | |
| 					"%s requires at least %d arg(s)\n"),
 | |
| 					com->c_name, com->c_minargs);
 | |
| 				break;
 | |
| 			}
 | |
| 			if (c > com->c_maxargs) {
 | |
| 				DEBUG_PRINT(catgets(catd, CATSET, 100,
 | |
| 					"%s takes no more than %d arg(s)\n"),
 | |
| 					com->c_name, com->c_maxargs);
 | |
| 				break;
 | |
| 			}
 | |
| 			e = (*com->c_func)(arglist);
 | |
| 			break; 
 | |
| 		default:
 | |
| 			panic(catgets(catd, CATSET, 101, "Unknown argtype"));
 | |
| 	}
 | |
| out:
 | |
| 	ac_free(word);
 | |
| 	/*
 | |
| 	 * Exit the current source file on
 | |
| 	 * error.
 | |
| 	 */
 | |
| 	if (e) {
 | |
| 		if (e < 0)
 | |
| 			return 1;
 | |
| 		if (loading)
 | |
| 			return 1;
 | |
| 		if (sourcing)
 | |
| 			unstack();
 | |
| 		return 0;
 | |
| 	}
 | |
| 	if (com == (struct cmd *)NULL)
 | |
| 		return(0);
 | |
| 	if (!sourcing && !inhook && (com->c_argtype & T) == 0)
 | |
| 		sawcom = 1;
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Find the correct command in the command table corresponding
 | |
|  * to the passed command "word"
 | |
|  */
 | |
| 
 | |
| static const struct cmd *
 | |
| lex(char *Word)
 | |
| {
 | |
| 	extern const struct cmd cmdtab[];
 | |
| 	const struct cmd *cp;
 | |
| 
 | |
| 	for (cp = &cmdtab[0]; cp->c_name != NULL; cp++)
 | |
| 		if (is_prefix(Word, cp->c_name))
 | |
| 			return(cp);
 | |
| 	return(NULL);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * The following gets called on receipt of an interrupt.  This is
 | |
|  * to abort printout of a command, mainly.
 | |
|  * Dispatching here when command() is inactive crashes rcv.
 | |
|  * Close all open files except 0, 1, 2, and the temporary.
 | |
|  * Also, unstack all source files.
 | |
|  */
 | |
| 
 | |
| static int	inithdr;		/* am printing startup headers */
 | |
| /*
 | |
|  * When we wake up after ^Z, reprint the prompt.
 | |
|  */
 | |
| static void 
 | |
| stop(int s)
 | |
| {
 | |
| 	sighandler_type old_action = safe_signal(s, SIG_DFL);
 | |
| 	sigset_t nset;
 | |
| 
 | |
| 	sigemptyset(&nset);
 | |
| 	sigaddset(&nset, s);
 | |
| 	sigprocmask(SIG_UNBLOCK, &nset, (sigset_t *)NULL);
 | |
| 	kill(0, s);
 | |
| 	sigprocmask(SIG_BLOCK, &nset, (sigset_t *)NULL);
 | |
| 	safe_signal(s, old_action);
 | |
| 	if (reset_on_stop) {
 | |
| 		reset_on_stop = 0;
 | |
| 		reset(0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Branch here on hangup signal and simulate "exit".
 | |
|  */
 | |
| /*ARGSUSED*/
 | |
| static void 
 | |
| hangup(int s)
 | |
| {
 | |
| 
 | |
| 	/* nothing to do? */
 | |
| 	mail_exit(MAIL_STATUS_ERR_GENERAL);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Load a file of user definitions.
 | |
|  */
 | |
| void 
 | |
| load(char *name)
 | |
| {
 | |
| 	FILE *in, *oldin;
 | |
| 	if ((in = fopen(name, "r")) == NULL)
 | |
| 		return;
 | |
| 	oldin = input;
 | |
| 	input = in;
 | |
| 	loading = 1;
 | |
| 	sourcing = 1;
 | |
| 	commands();
 | |
| 	loading = 0;
 | |
| 	sourcing = 0;
 | |
| 	input = oldin;
 | |
| 	fclose(in);
 | |
| }
 |