314 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /* Copyright (c) 1985 Ceriel J.H. Jacobs */
 | |
| 
 | |
| /*
 | |
|  * Command reader, also executes shell escapes
 | |
|  */
 | |
| 
 | |
| # ifndef lint
 | |
| static char rcsid[] = "$Header$";
 | |
| # endif
 | |
| 
 | |
| # define _GETCOMM_
 | |
| 
 | |
| # include <ctype.h>
 | |
| # include "in_all.h"
 | |
| # include "term.h"
 | |
| # include "process.h"
 | |
| # include "getcomm.h"
 | |
| # include "commands.h"
 | |
| # include "prompt.h"
 | |
| # include "main.h"
 | |
| # include "output.h"
 | |
| # include "getline.h"
 | |
| # include "machine.h"
 | |
| # include "keys.h"
 | |
| # include "display.h"
 | |
| # include "assert.h"
 | |
| 
 | |
| #if USG_OPEN
 | |
| #include <fcntl.h>
 | |
| #endif
 | |
| #if POSIX_OPEN
 | |
| #include <sys/types.h>
 | |
| #include <fcntl.h>
 | |
| #endif
 | |
| 
 | |
| char	*strcpy(),
 | |
| 	*getenv();
 | |
| 
 | |
| STATIC int	killchar();
 | |
| 
 | |
| /*
 | |
|  * Read a line from the terminal, doing line editing.
 | |
|  * The parameter s contains the prompt for the line.
 | |
|  */
 | |
| 
 | |
| char *
 | |
| readline(s) char *s; {
 | |
| 
 | |
| 	static char buf[80];
 | |
| 	register char *p = buf;
 | |
| 	register int ch;
 | |
| 	register int pos;
 | |
| 
 | |
| 	clrbline();
 | |
| 	putline(s);
 | |
| 	pos = strlen(s);
 | |
| 	while ((ch = getch()) != '\n' && ch != '\r') {
 | |
| 		if (ch == -1) {
 | |
| 			/*
 | |
| 			 * Can only occur because of an interrupted read.
 | |
| 			 */
 | |
| 			ch = erasech;
 | |
| 			interrupt = 0;
 | |
| 		}
 | |
| 		if (ch == erasech) {
 | |
| 			/*
 | |
| 			 * Erase last char
 | |
| 			 */
 | |
| 			if (p == buf) {
 | |
| 				/*
 | |
| 				 * There was none, so return
 | |
| 				 */
 | |
| 				return (char *) 0;
 | |
| 			}
 | |
| 			pos -= killchar(*--p);
 | |
| 			if (*p != '\\') continue;
 | |
| 		}
 | |
| 		if (ch == killch) {
 | |
| 			/*
 | |
| 			 * Erase the whole line
 | |
| 			 */
 | |
| 			if (!(p > buf && *(p-1) == '\\')) {
 | |
| 				while (p > buf) {
 | |
| 					pos -= killchar(*--p);
 | |
| 				}
 | |
| 				continue;
 | |
| 			}
 | |
| 			pos -= killchar(*--p);
 | |
| 		}
 | |
| 		if (p > &buf[78] || pos >= COLS - 2) {
 | |
| 			/*
 | |
| 			 * Line does not fit.
 | |
| 			 * Simply refuse to make it any longer
 | |
| 			 */
 | |
| 			pos -= killchar(*--p);
 | |
| 		}
 | |
| 		*p++ = ch;
 | |
| 		if (ch < ' ' || ch >= 0177) {
 | |
| 			fputch('^');
 | |
| 			pos++;
 | |
| 			ch ^= 0100;
 | |
| 		}
 | |
| 		fputch(ch);
 | |
| 		pos++;
 | |
| 	}
 | |
| 	fputch('\r');
 | |
| 	*p++ = '\0';
 | |
| 	flush();
 | |
| 	return buf;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Erase a character from the command line.
 | |
|  */
 | |
| 
 | |
| STATIC int
 | |
| killchar(c) {
 | |
| 
 | |
| 	backspace();
 | |
| 	putch(' ');
 | |
| 	backspace();
 | |
| 	if (c < ' ' || c >= 0177) {
 | |
| 		(VOID) killchar(' ');
 | |
| 		return 2;
 | |
| 	}
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Do a shell escape, after expanding '%' and '!'.
 | |
|  */
 | |
| 
 | |
| VOID
 | |
| shellescape(p, esc_char) register char *p; {
 | |
| 
 | |
| 	register char *p2;	/* walks through command */
 | |
| 	register int id;	/* procid of child */
 | |
| 	register int cnt;	/* prevent array bound errors */
 | |
| 	register int lastc = 0;	/* will contain the previous char */
 | |
| # ifdef SIGTSTP
 | |
| 	VOID (*savetstp)();
 | |
| # endif
 | |
| 	static char previous[256];	/* previous command */
 | |
| 	char comm[256];			/* space for command */
 | |
| 	int piped[2];
 | |
| 
 | |
| 	p2 = comm;
 | |
| 	*p2++ = esc_char;
 | |
| 	cnt = 253;
 | |
| 	while (*p) {
 | |
| 		/*
 | |
| 		 * expand command
 | |
| 		 */
 | |
| 		switch(*p++) {
 | |
| 		  case '!':
 | |
| 			/*
 | |
| 			 * An unescaped ! expands to the previous
 | |
| 			 * command, but disappears if there is none
 | |
| 			 */
 | |
| 			if (lastc != '\\') {
 | |
| 				if (*previous) {
 | |
| 					id = strlen(previous);
 | |
| 					if ((cnt -= id) <= 0) break;
 | |
| 					(VOID) strcpy(p2,previous);
 | |
| 					p2 += id;
 | |
| 				}
 | |
| 			}
 | |
| 			else {
 | |
| 				*(p2-1) = '!';
 | |
| 			}
 | |
| 			continue;
 | |
| 		  case '%':
 | |
| 			/*
 | |
| 			 * An unescaped % will expand to the current
 | |
| 			 * filename, but disappears is there is none
 | |
| 			 */
 | |
| 			if (lastc != '\\') {
 | |
| 				if (nopipe) {
 | |
| 					id = strlen(currentfile);
 | |
| 					if ((cnt -= id) <= 0) break;
 | |
| 					(VOID) strcpy(p2,currentfile);
 | |
| 					p2 += id;
 | |
| 				}
 | |
| 			}
 | |
| 			else {
 | |
| 				*(p2-1) = '%';
 | |
| 			}
 | |
| 			continue;
 | |
| 		  default:
 | |
| 			lastc = *(p-1);
 | |
| 			if (cnt-- <= 0) break;
 | |
| 			*p2++ = lastc;
 | |
| 			continue;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 	clrbline();
 | |
| 	*p2 = '\0';
 | |
| 	if (!stupid) {
 | |
| 		/*
 | |
| 		 * Display expanded command
 | |
| 		 */
 | |
| 		cputline(comm);
 | |
| 		putline("\r\n");
 | |
| 	}
 | |
| 	flush();
 | |
| 	(VOID) strcpy(previous,comm + 1);
 | |
| 	resettty();
 | |
| 	if (esc_char == '|' && pipe(piped) < 0) {
 | |
| 		error("Cannot create pipe");
 | |
| 		return;
 | |
| 	}
 | |
| 	if ((id = fork()) < 0) {
 | |
| 		error("Cannot fork");
 | |
| 		return;
 | |
| 	}
 | |
| 	if (id == 0) {
 | |
| 		/*
 | |
| 		 * Close files, as child might need the file descriptors
 | |
| 		 */
 | |
| 		cls_files();
 | |
| 		if (esc_char == '|') {
 | |
| 			close(piped[1]);
 | |
| #if USG_OPEN || POSIX_OPEN
 | |
| 			close(0);
 | |
| 			fcntl(piped[0], F_DUPFD, 0);
 | |
| #else
 | |
| 			dup2(piped[0], 0);
 | |
| #endif
 | |
| 			close(piped[0]);
 | |
| 		}
 | |
| 		execl("/bin/sh", "sh", "-c", comm + 1, (char *) 0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	(VOID) signal(SIGINT,SIG_IGN);
 | |
| 	(VOID) signal(SIGQUIT,SIG_IGN);
 | |
| # ifdef SIGTSTP
 | |
| 	if ((savetstp = signal(SIGTSTP,SIG_IGN)) != SIG_IGN) {
 | |
| 		(VOID) signal(SIGTSTP,SIG_DFL);
 | |
| 	}
 | |
| # endif
 | |
| 	if (esc_char == '|') {
 | |
| 		(VOID) close(piped[0]);
 | |
| 		(VOID) signal(SIGPIPE, SIG_IGN);
 | |
| 		wrt_fd(piped[1]);
 | |
| 		(VOID) close(piped[1]);
 | |
| 	}
 | |
| 	while ((lastc = wait((int *) 0)) != id && lastc >= 0)  {
 | |
| 		/*
 | |
| 		 * Wait for child, making sure it is the one we expected ...
 | |
| 		 */
 | |
| 	}
 | |
| 	(VOID) signal(SIGINT,catchdel);
 | |
| 	(VOID) signal(SIGQUIT,quit);
 | |
| # ifdef SIGTSTP
 | |
| 	(VOID) signal(SIGTSTP, savetstp);
 | |
| # endif
 | |
| 	inittty();
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Get all those commands ...
 | |
|  */
 | |
| 
 | |
| int
 | |
| getcomm (plong) long   *plong; {
 | |
| 	int	c;
 | |
| 	long	count;
 | |
| 	char	*p;
 | |
| 	int	i;
 | |
| 	int	j;
 | |
| 	char	buf[10];
 | |
| 
 | |
| 	for (;;) {
 | |
| 		count = 0;
 | |
| 		give_prompt();
 | |
| 		while (isdigit((c = getch()))) {
 | |
| 			count = count * 10 + (c - '0');
 | |
| 		}
 | |
| 		*plong = count;
 | |
| 		p = buf;
 | |
| 		for (;;) {
 | |
| 			if (c == -1) {
 | |
| 				/*
 | |
| 				 * This should never happen, but it does,
 | |
| 				 * when the user gives a TSTP signal (^Z) or
 | |
| 				 * an interrupt while the program is trying
 | |
| 				 * to read a character from the terminal.
 | |
| 				 * In this case, the read is interrupted, so
 | |
| 				 * we end up here.
 | |
| 				 * Right, we will have to read again.
 | |
| 				 */
 | |
| 				if (interrupt) return 1;
 | |
| 				break;
 | |
| 			}
 | |
| 			*p++ = c;
 | |
| 			*p = 0;
 | |
| 			if ((i = match(buf, &j, currmap->k_mach)) > 0) {
 | |
| 				/*
 | |
| 				 * The key sequence matched. We have a command
 | |
| 				 */
 | |
| 				return j;
 | |
| 			}
 | |
| 			if (i == 0) return 0;
 | |
| 			/*
 | |
| 			 * We have a prefix of a command.
 | |
| 			 */
 | |
| 			assert(i == FSM_ISPREFIX);
 | |
| 			c = getch();
 | |
| 		}
 | |
| 	}
 | |
| 	/* NOTREACHED */
 | |
| }
 | 
