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 */
 | 
						|
}
 |