4.4BSD/usr/src/contrib/calc-1.26.4/input.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1993 David I. Bell
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 *
 * Nested input source file reader.
 * For terminal input, this also provides a simple command stack.
 */

#include <ctype.h>
#include <pwd.h>
#include "calc.h"
#include "config.h"
#include "hist.h"

#define MAXSAVE		255	/* number of saved terminal lines */
#define TTYSIZE		100	/* reallocation size for terminal buffers */
#define DEPTH		10	/* maximum depth of input */
#define IS_READ		1	/* reading normally */
#define IS_REREAD	2	/* reread current character */
#define chartoint(ch)	((ch) & 0xff)	/* make sure char is not negative */


typedef struct {
	short i_state;		/* state (read, reread) */
	short i_char;		/* currently read char */
	long i_line;		/* line number */
	char *i_str;		/* current string for input (if not NULL) */
	char *i_origstr;	/* original string so it can be freed */
	char *i_ttystr;		/* current character of tty line (or NULL) */
	FILE *i_fp;		/* current file for input (if not NULL) */
	char *i_name;		/* file name if known */
} INPUT;


static int stacksize;		/* number of elements in command stack */
static int stackindex;		/* current index into command stack */
static int cmdsize;		/* current max size of terminal buffer */
static int editsize;		/* current max size of edit buffer */
static int linesize;		/* current max size of input line */
static char *linebuf;		/* current input line buffer */
static char *cmdbuf;		/* current command line buffer */
static char *editbuf;		/* edit buffer */
static char **cmdstack;		/* command stack */
static char *prompt;		/* current prompt for terminal */
static BOOL noprompt;		/* TRUE if should not print prompt */

static int depth;		/* current input depth */
static INPUT *cip;		/* current input source */
static INPUT inputs[DEPTH];	/* input sources */


static int openfile();
static int ttychar();

extern struct passwd *getpwnam();


/*
 * Open an input file by possibly searching through a path list
 * and also possibly applying the specified extension.  For example:
 * opensearchfile("barf", ".:/tmp", ".c") searches in order for the
 * files "./barf", "./barf.c", "/tmp/barf", and "/tmp/barf.c".
 *
 * Returns -1 if all specified files cannot be opened.
 */
opensearchfile(name, pathlist, extension)
	char *name;		/* file name to be read */
	char *pathlist;		/* list of colon separated paths (or NULL) */
	char *extension;	/* extra extension to try (or NULL) */
{
	int i;
	char *cp;
	char path[PATHSIZE+1];	/* name being searched for */

	/*
	 * Don't try the extension if the filename already contains it.
	 */
	if (extension) {
		i = strlen(name) - strlen(extension);
		if ((i >= 0) && (strcmp(&name[i], extension) == 0))
			extension = NULL;
	}
	/*
	 * If the name is absolute, or if there is no path list, then
	 * make one which just searches for the name straight.  Then
	 * search through the path list for the file, without and with
	 * the specified extension.
	 */
	if (name[0] == PATHCHAR || 
	    name[0] == HOMECHAR || 
	    (name[0] == DOTCHAR && name[1] == PATHCHAR) || 
	    pathlist == NULL) {
		pathlist = "";
	}
	pathlist--;
	do {
		pathlist++;
		cp = path;
		while (*pathlist && (*pathlist != LISTCHAR))
			*cp++ = *pathlist++;
		if (cp != path)
			*cp++ = PATHCHAR;
		strcpy(cp, name);
		i = openfile(path);
		if ((i < 0) && extension) {
			strcat(path, extension);
			i = openfile(path);
		}
	} while ((i < 0) && *pathlist);
	return i;
}


/*
 * Given a filename with a leading ~, expand it into a home directory for 
 * that user.  This function will malloc the space for the expanded path.
 *
 * If the path is just ~, or begins with ~/, expand it to the home
 * directory of the current user.  If the environment variable $HOME
 * is known, it will be used, otherwise the password file will be
 * consulted.
 *
 * If the path is just ~username, or ~username/, expand it to the home
 * directory of that user by looking it up in the password file.
 *
 * If the password file must be consulted and the username is not found
 * a NULL pointer is returned.
 */
static char *
homeexpand(name)
	char *name;		/* a filename with a leading ~ */
{
	struct passwd *ent;	/* password entry */
	char *home2;		/* fullpath of the home directory */
	char *fullpath;		/* the malloced expanded path */
	char *after;		/* after the ~user or ~ */
	char username[PATHSIZE+1];	/* extratced username */

	/* firewall */
	if (name[0] != HOMECHAR)
		return NULL;

	/*
	 * obtain the home directory component
	 */
	switch (name[1]) {
	case PATHCHAR:		/* ~/... */
	case '\0':		/* ~ */
		home2 = home;
		after = name+1;
		break;
	default:		/* ~username or ~username/... */

		/* extract the username after the ~ */
		after = (char *)strchr(name+2, PATHCHAR);
		if (after == NULL) {
			/* path is just ~username */
			ent = getpwnam(name+1);
			if (ent == NULL) {
				/* unknown user */
				return NULL;
			}
			/* just malloc the home directory and return it */
			fullpath = (char *)malloc(strlen(ent->pw_dir)+1);
			strcpy(fullpath, ent->pw_dir);
			return fullpath;
		}
		if (after-name > PATHSIZE+1) {
			/* username is too big */
			return NULL;
		}
		strncpy(username, name+1, after-name-1);
		username[after-name-1] = '\0';

		/* get that user's home directory */
		ent = getpwnam(username);
		if (ent == NULL) {
			/* unknown user */
			return NULL;
		}
		home2 = ent->pw_dir;
		break;
	}

	/*
	 * build the fullpath given the home directory
	 */
	fullpath = (char *)malloc(strlen(home2)+strlen(after)+1);
	sprintf(fullpath, "%s%s", home2, after);
	return fullpath;
}


/*
 * f_open - ~-expand a filename and fopen() it
 */
FILE *
f_open(name, mode)
	char *name;		/* the filename to open */
	char *mode;		/* the fopen mode to use */
{
	FILE *fp;		/* open file descriptor */
	char *fullname;		/* file name with HOMECHAR expansion */

	/*
	 * expand ~ if needed
	 */
	if (name[0] == HOMECHAR) {
		fullname = homeexpand(name);
		if (fullname == NULL)
			return NULL;
		fp = fopen(fullname, mode);
		free(fullname);
	} else {
		fp = fopen(name, mode);
	}
	return fp;
}


/*
 * Setup for reading from a input file.
 * Returns -1 if file could not be opened.
 */
static
openfile(name)
	char *name;		/* file name to be read */
{
	FILE *fp;		/* open file descriptor */

	if (depth >= DEPTH)
		 return -1;
	fp = f_open(name, "r");
	if (fp == NULL)
		 return -1;
	cip++;
	cip->i_state = IS_READ;
	cip->i_char = '\0';
	cip->i_str = NULL;
	cip->i_origstr = NULL;
	cip->i_ttystr = NULL;
	cip->i_fp = fp;
	cip->i_line = 1;
	cip->i_name = (char *)malloc(strlen(name) + 1);
	strcpy(cip->i_name, name);
	depth++;
	return 0;
}


/*
 * Open a string for scanning. String is ended by a null character.
 * String is copied into local memory so it can be trashed afterwards.
 * Returns -1 if cannot open string.
 */
openstring(str)
	char *str;		/* string to be opened */
{
	char *cp;		/* copied string */

	if ((depth >= DEPTH) || (str == NULL))
		 return -1;
	cp = (char *)malloc(strlen(str) + 1);
	if (cp == NULL)
		 return -1;
	strcpy(cp, str);
	cip++;
	cip->i_state = IS_READ;
	cip->i_char = '\0';
	cip->i_str = cp;
	cip->i_origstr = cp;
	cip->i_fp = NULL;
	cip->i_name = NULL;
	cip->i_ttystr = NULL;
	cip->i_line = 1;
	depth++;
	return 0;
}


/*
 * Set to read input from the terminal.
 * Returns -1 if there is no more depth for input.
 */
openterminal()
{
	if (depth >= DEPTH)
		 return -1;
	if (cmdsize == 0) {
		cmdbuf = (char *)malloc(TTYSIZE + 1);
		if (cmdbuf == NULL)
			return -1;
		cmdsize = TTYSIZE;
	}
	if (editsize == 0) {
		editbuf = (char *)malloc(TTYSIZE + 1);
		if (editbuf == NULL)
			return -1;
		editsize = TTYSIZE;
	}
	if (stacksize == 0) {
		cmdstack = (char **) malloc(MAXSAVE * sizeof(char *));
		if (cmdstack == NULL)
			return -1;
		stacksize = MAXSAVE;
		for (stackindex = 0; stackindex < MAXSAVE; stackindex++)
			cmdstack[stackindex] = NULL;
		stackindex = 0;
	}
	cip++;
	cip->i_state = IS_READ;
	cip->i_char = '\0';
	cip->i_str = NULL;
	cip->i_origstr = NULL;
	cip->i_ttystr = NULL;
	cip->i_fp = NULL;
	cip->i_name = NULL;
	cip->i_line = 1;
	depth++;
	return 0;
}


/*
 * Close the current input source.
 */
static void
closeinput()
{
	if (depth <= 0)
		return;
	if (cip->i_origstr)
		free(cip->i_origstr);
	if (cip->i_fp)
		fclose(cip->i_fp);
	if (cip->i_name)
		free(cip->i_name);
	cip--;
	depth--;
}


/*
 * Reset the input sources back to the initial state.
 */
void
resetinput()
{
	while (depth > 0)
		closeinput();
	cip = inputs;
	noprompt = FALSE;
}


/*
 * Set the prompt for terminal input.
 */
void
setprompt(str)
	char *str;
{
	prompt = str;
	noprompt = FALSE;
}


/*
 * Read the next character from the current input source.
 * End of file returns newline character and closes current input source,
 * except for the last input source, which returns EOF.
 */
int
nextchar()
{
	int ch;			/* current input character */

	if (depth == 0)		/* input finished */
		 return EOF;
	if (cip->i_state == IS_REREAD) {	/* rereading current char */
		 ch = cip->i_char;
		 cip->i_state = IS_READ;
		 if (ch == '\n')
			cip->i_line++;
		 return ch;
	}
	if (cip->i_str) {		/* from string */
		ch = chartoint(*cip->i_str++);
		if (ch == '\0')
			ch = EOF;
	} else if (cip->i_fp) {		/* from file */
		ch = fgetc(cip->i_fp);
	} else {			/* from terminal */
		ch = ttychar();
	}
	if (ch == EOF) {		/* fix up end of file */
		closeinput();
		ch = '\n';
		if (depth <= 0)
			ch = EOF;
	}
	if (depth > 0)
		cip->i_char = (char)ch;	/* save for rereads */
	if (ch == '\n')
		cip->i_line++;
	return ch;
}


/*
 * Read in the next line of input from the current input source.
 * The line is terminated with a null character, and does not contain
 * the final newline character.  The returned string is only valid
 * until the next such call, and so must be copied if necessary.
 * Returns NULL on end of file.
 */
char *
nextline()
{
	char *cp;
	int ch;
	int len;

	cp = linebuf;
	if (linesize == 0) {
		cp = (char *)malloc(TTYSIZE + 1);
		if (cp == NULL)
			error("Cannot allocate line buffer");
		linebuf = cp;
		linesize = TTYSIZE;
	}
	len = 0;
	for (;;) {
		noprompt = TRUE;
		ch = nextchar();
		noprompt = FALSE;
		if (ch == EOF)
			return NULL;
		if (ch == '\0')
			continue;
		if (ch == '\n')
			break;
		if (len >= linesize) {
			cp = (char *)realloc(cp, linesize + TTYSIZE + 1);
			if (cp == NULL)
				error("Cannot realloc line buffer");
			linebuf = cp;
			linesize += TTYSIZE;
		}
		cp[len++] = (char)ch;
	}
	cp[len] = '\0';
	return linebuf;
}


/*
 * Read the next character from the terminal.
 * The routines in the history module are called so that the user
 * can use a command history and emacs-like editing of the line.
 */
static int
ttychar()
{
	int ch;			/* current char */
	int len;		/* length of current command */
	static char charbuf[1000];

	/*
	 * If we have more to read from the saved command line, then do that.
	 * When we see a newline character, then clear the pointer so we will
	 * read a new line on the next call.
	 */
	if (cip->i_ttystr) {
		ch = chartoint(*cip->i_ttystr++);
		if (ch == '\n')
			cip->i_ttystr = NULL;
		return ch;
	}

	/*
	 * We need another complete line.
	 */
	abortlevel = 0;
	inputwait = TRUE;
	len = hist_getline(noprompt ? "" : prompt, charbuf, sizeof(charbuf));
	if (len == 0) {
		inputwait = FALSE;
		return EOF;
	}
	inputwait = FALSE;

	/*
	 * Handle shell escape if present
	 */
	if (charbuf[0] == '!') {		/* do a shell command */
		char *cmd;

		cmd = charbuf + 1;
		if (*cmd == '\0' || *cmd == '\n')
			cmd = shell;
		system(cmd);
		return '\n';
	}
	hist_saveline(charbuf, len);

	/*
	 * Return the first character of the line, and set up to
	 * return the rest of it with later calls.
	 */
	ch = chartoint(charbuf[0]);
	if (ch != '\n')
		cip->i_ttystr = charbuf + 1;
	return ch;
}


/*
 * Return whether or not the input source is the terminal.
 */
BOOL
inputisterminal()
{
	return ((depth <= 0) || ((cip->i_str == NULL) && (cip->i_fp == NULL)));
}


/*
 * Return the name of the current input file.
 * Returns NULL for terminal or strings.
 */
char *
inputname()
{
	if (depth <= 0)
		return NULL;
	return cip->i_name;
}


/*
 * Return the current line number.
 */
long
linenumber()
{
	if (depth > 0)
		return cip->i_line;
	return 1;
}


/*
 * Restore the next character to be read again on the next nextchar call.
 */
void
reread()
{
	if ((depth <= 0) || (cip->i_state == IS_REREAD))
		return;
	cip->i_state = IS_REREAD;
	if (cip->i_char == '\n')
		cip->i_line--;
}


/*
 * Process all startup files found in the $CALCRC path.
 */
void
runrcfiles()
{
	char path[PATHSIZE+1];	/* name being searched for */
	char *cp;
	char *newcp;
	char *p;
	int i;

	/* execute each file in the list */
	for (cp=calcrc, newcp=(char *)strchr(calcrc, LISTCHAR);
	     cp != NULL && *cp;
	     cp = newcp, 
		 newcp=(newcp) ? (char *)strchr(newcp+1, LISTCHAR) : NULL) {

		/* load file name into the path */
		if (newcp == NULL) {
			strcpy(path, cp);
		} else {
			strncpy(path, cp, newcp-cp);
			path[newcp-cp] = '\0';
		}

		/* find the start of the path */
		p = (path[0] == ':') ? path+1 : path;
		if (p[0] == '\0') {
			continue;
		}

		/* process the current file in the list */
		i = openfile(p);
		if (i < 0)
			continue;
		getcommands();
	}
}


/* END CODE */