home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume26
/
calc
/
part05
/
file.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-09
|
10KB
|
528 lines
/*
* Copyright (c) 1992 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);
setmode(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);
setmode(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(' ');
}
setmode(oldmode);
setdigits(olddigits);
}
setfp(stdout);
}
/* END CODE */