4.4BSD/usr/src/contrib/calc-1.26.4/file.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.
 *
 * File I/O routines callable by users.
 */

#include "stdarg.h"
#include "calc.h"


#define	READSIZE	1024	/* buffer size for reading */

/*
 * Definition of opened files.
 */
typedef struct {
	FILEID id;		/* id to identify this file */
	FILE *fp;		/* real file structure for I/O */
	char *name;		/* file name */
	BOOL reading;		/* TRUE if opened for reading */
	BOOL writing;		/* TRUE if opened for writing */
	char *mode;		/* open mode */
} FILEIO;


/*
 * Table of opened files.
 * The first three entries always correspond to stdin, stdout, and stderr,
 * and cannot be closed.  Their file ids are always 0, 1, and 2.
 */
static FILEIO files[MAXFILES] = {
	FILEID_STDIN,  stdin,  "(stdin)",  TRUE, FALSE, "reading",
	FILEID_STDOUT, stdout, "(stdout)", FALSE, TRUE, "writing",
	FILEID_STDERR, stderr, "(stderr)", FALSE, TRUE, "writing"
};

static FILEID lastid = FILEID_STDERR;		/* last allocated file id */



/*
 * Open the specified file name for reading or writing as determined by
 * the specified mode ("r", "w", or "a").  Returns a file id which can be
 * used to do I/O to the file, or else FILEID_NONE if the open failed.
 * Aborts with an error if too many files are opened or the mode is illegal.
 */
FILEID
openid(name, mode)
	char *name;		/* file name */
	char *mode;		/* open mode */
{
	FILEIO *fiop;		/* file structure */
	FILEID id;		/* new file id */
	int count;

	if (((*mode != 'r') && (*mode != 'w') && (*mode != 'a')) || mode[1])
		error("Illegal mode for fopen");

	count = MAXFILES;
	do {
		if (--count < 0)
			error("Too many open files");
		id = ++lastid;
		fiop = &files[id % MAXFILES];

	} while (fiop->reading || fiop->writing);

	fiop->name = (char *)malloc(strlen(name) + 1);
	if (fiop->name == NULL) {
		lastid--;
		error("No memory for filename");
	}
	strcpy(fiop->name, name);

	fiop->fp = f_open(name, mode);
	if (fiop->fp == NULL) {
		free(fiop->name);
		fiop->name = NULL;
		lastid--;
		return FILEID_NONE;
	}

	switch (*mode) {
		case 'r':
			fiop->mode = "reading";
			fiop->reading = TRUE;
			break;
		case 'w':
			fiop->mode = "writing";
			fiop->writing = TRUE;
			break;
		case 'a':
			fiop->mode = "appending";
			fiop->writing = TRUE;
			break;
	}

	fiop->id = id;

	return id;
}


/*
 * Find the file I/O structure for the specified file id, and verify that
 * it is opened in the required manner ('r' for reading or 'w' for writing).
 * If mode is 0, then no open checks are made at all, and NULL is then
 * returned if the id represents a closed file.
 */
static FILEIO *
findid(id, mode)
	FILEID id;
{
	FILEIO *fiop;		/* file structure */
	char *msg;
	BOOL flag;

	if ((id < 0) || (id > lastid))
		error("Illegal file id");

	fiop = &files[id % MAXFILES];

	switch (mode) {
		case 'r':
			msg = "Reading from";
			flag = fiop->reading;
			break;
		case 'w':
			msg = "Writing to";
			flag = fiop->writing;
			break;
		case 0:
			msg = NULL;
			break;
		default:
			error("Unknown findid mode");
	}

	if (fiop->id != id) {
		if (msg)
			error("%s closed file", msg);
		return NULL;
	}

	if (msg && !flag)
		error("%s file not opened that way", msg);
	
	return fiop;
}


/*
 * Return whether or not a file id is valid.  This is used for if tests.
 */
BOOL
validid(id)
	FILEID id;
{
	return (findid(id, 0) != NULL);
}


/*
 * Return the file id for the entry in the file table at the specified index.
 * Returns FILEID_NONE if the index is illegal or the file is closed.
 */
FILEID
indexid(index)
	long index;
{
	FILEIO *fiop;		/* file structure */

	if ((index < 0) || (index >= MAXFILES))
		return FILEID_NONE;

	fiop = &files[index];
	if (fiop->reading || fiop->writing)
		return fiop->id;

	return FILEID_NONE;
}


/*
 * Close the specified file id.  Returns TRUE if there was an error.
 * Closing of stdin, stdout, or stderr is illegal, but closing of already
 * closed files is allowed.
 */
BOOL
closeid(id)
	FILEID id;
{
	FILEIO *fiop;		/* file structure */
	int err;

	if ((id == FILEID_STDIN) || (id == FILEID_STDOUT) ||
		(id == FILEID_STDERR))
			error("Cannot close stdin, stdout, or stderr");

	fiop = findid(id, 0);
	if (fiop == NULL)
		return FALSE;

	fiop->id = FILEID_NONE;
	if (!fiop->reading && !fiop->writing)
		error("Closing non-opened file");
	fiop->reading = FALSE;
	fiop->writing = FALSE;

	if (fiop->name)
		free(fiop->name);
	fiop->name = NULL;

	err = ferror(fiop->fp);
	err |= fclose(fiop->fp);
	fiop->fp = NULL;

	return (err != 0);
}


/*
 * Return whether or not an error occurred to a file.
 */
BOOL
errorid(id)
	FILEID id;
{
	FILEIO *fiop;		/* file structure */

	fiop = findid(id, 0);
	if (fiop == NULL)
		error("Closed file for ferror");
	return (ferror(fiop->fp) != 0);
}


/*
 * Return whether or not end of file occurred to a file.
 */
BOOL
eofid(id)
	FILEID id;
{
	FILEIO *fiop;		/* file structure */

	fiop = findid(id, 0);
	if (fiop == NULL)
		error("Closed file for feof");
	return (feof(fiop->fp) != 0);
}


/*
 * Flush output to an opened file.
 */
void
flushid(id)
	FILEID id;
{
	FILEIO *fiop;		/* file structure */

	fiop = findid(id, 'w');
	fflush(fiop->fp);
}


/*
 * Read the next line from an opened file.
 * Returns a pointer to an allocated string holding the null-terminated
 * line (without any terminating newline), or else a NULL pointer on an
 * end of file or error.
 */
void
readid(id, retptr)
	FILEID	id;		/* file to read from */
	char **retptr;		/* returned pointer to string */
{
	FILEIO *fiop;		/* file structure */
	char *str;		/* current string */
	int len;		/* current length of string */
	int totlen;		/* total length of string */
	char buf[READSIZE];	/* temporary buffer */

	totlen = 0;
	str = NULL;

	fiop = findid(id, 'r');

	while (fgets(buf, READSIZE, fiop->fp) && buf[0]) {
		len = strlen(buf);
		if (totlen)
			str = (char *)realloc(str, totlen + len + 1);
		else
			str = (char *)malloc(len + 1);
		if (str == NULL)
			error("No memory in freadline");
		strcpy(&str[totlen], buf);
		totlen += len;
		if (buf[len - 1] == '\n') {
			str[totlen - 1] = '\0';
			*retptr = str;
			return;
		}
	}
	if (totlen && ferror(fiop->fp)) {
		free(str);
		str = NULL;
	}
	*retptr = str;
}


/*
 * Return the next character from an opened file.
 * Returns EOF if there was an error or end of file.
 */
int
getcharid(id)
	FILEID id;
{
	return fgetc(findid(id, 'r')->fp);
}


/*
 * Print out the name of an opened file.
 * If the file has been closed, a null name is printed.
 * If flags contain PRINT_UNAMBIG then extra information is printed
 * identifying the output as a file and some data about it.
 */
void
printid(id, flags)
	FILEID id;
{
	FILEIO *fiop;		/* file structure */
	FILE *fp;

	fiop = findid(id, 0);
	if (fiop == NULL) {
		math_str((flags & PRINT_UNAMBIG) ? "FILE (closed)" : "\"\"");
		return;
	}
	if ((flags & PRINT_UNAMBIG) == 0) {
		math_chr('"');
		math_str(fiop->name);
		math_chr('"');
		return;
	}

	fp = fiop->fp;
	math_fmt("FILE \"%s\" (%s, pos %ld", fiop->name,  fiop->mode,
		ftell(fp));
	if (ferror(fp))
		math_str(", error");
	if (feof(fp))
		math_str(", eof");
	math_chr(')');
}


/*
 * Print a formatted string similar to printf.  Various formats of output
 * are possible, depending on the format string AND the actual types of the
 * values.  Mismatches do not cause errors, instead something reasonable is
 * printed instead.  The output goes to the file with the specified id.
 */
void
idprintf(id, fmt, count, vals)
	FILEID id;			/* file id to print to */
	char *fmt;			/* standard format string */
	VALUE **vals;			/* table of values to print */
{
	FILEIO *fiop;
	VALUE *vp;
	char *str;
	int ch, len;
	int oldmode, newmode;
	long olddigits, newdigits;
	long width, precision;
	BOOL didneg, didprecision;

	fiop = findid(id, 'w');

	setfp(fiop->fp);

	while ((ch = *fmt++) != '\0') {
		if (ch == '\\') {
			ch = *fmt++;
			switch (ch) {
				case 'n': ch = '\n'; break;
				case 'r': ch = '\r'; break;
				case 't': ch = '\t'; break;
				case 'f': ch = '\f'; break;
				case 'v': ch = '\v'; break;
				case 'b': ch = '\b'; break;
				case 0:
					setfp(stdout);
					return;
			}
			math_chr(ch);
			continue;
		}

		if (ch != '%') {
			math_chr(ch);
			continue;
		}

		/*
		 * Here to handle formats.
		 */
		didneg = FALSE;
		didprecision = FALSE;
		width = 0;
		precision = 0;

		ch = *fmt++;
		if (ch == '-') {
			didneg = TRUE;
			ch = *fmt++;
		}
		while ((ch >= '0') && (ch <= '9')) {
			width = width * 10 + (ch - '0');
			ch = *fmt++;
		}
		if (ch == '.') {
			didprecision = TRUE;
			ch = *fmt++;
			while ((ch >= '0') && (ch <= '9')) {
				precision = precision * 10 + (ch - '0');
				ch = *fmt++;
			}
		}
		if (ch == 'l')
			ch = *fmt++;

		oldmode = _outmode_;
		newmode = oldmode;
		olddigits = _outdigits_;
		newdigits = olddigits;
		if (didprecision)
			newdigits = precision;

		switch (ch) {
			case 'd':
			case 's':
			case 'c':
				break;
			case 'f':
				newmode = MODE_REAL;
				break;
			case 'e':
				newmode = MODE_EXP;
				break;
			case 'r':
				newmode = MODE_FRAC;
				break;
			case 'o':
				newmode = MODE_OCTAL;
				break;
			case 'x':
				newmode = MODE_HEX;
				break;
			case 'b':
				newmode = MODE_BINARY;
				break;
			case 0:
				setfp(stdout);
				return;
			default:
				math_chr(ch);
				continue;
		}

		if (--count < 0)
			error("Not enough arguments for fprintf");
		vp = *vals++;

		setdigits(newdigits);
		set_mode(newmode);

		/*
		 * If there is no width specification, or if the type of
		 * value requires multiple lines, then just output the
		 * value directly.
		 */
		if ((width == 0) ||
			(vp->v_type == V_MAT) || (vp->v_type == V_LIST))
		{
			printvalue(vp, PRINT_NORMAL);
			set_mode(oldmode);
			setdigits(olddigits);
			continue;
		}

		/*
		 * There is a field width.  Collect the output in a string,
		 * print it padded appropriately with spaces, and free it.
		 * However, if the output contains a newline, then ignore
		 * the field width.
		 */
		divertio();
		printvalue(vp, PRINT_NORMAL);
		str = getdivertedio();
		if (strchr(str, '\n'))
			width = 0;
		len = strlen(str);
		while (!didneg && (width > len)) {
			width--;
			math_chr(' ');
		}
		math_str(str);
		free(str);
		while (didneg && (width > len)) {
			width--;
			math_chr(' ');
		}
		set_mode(oldmode);
		setdigits(olddigits);
	}
	setfp(stdout);
}

/* END CODE */