This is io.c in view mode; [Download] [Up]
/* io.c */
/* Copyright 1995 by Steve Kirkendall */
char id_io[] = "$Id: io.c,v 2.21 1996/08/27 00:59:40 steve Exp $";
#include "elvis.h"
/* This file contains some generic I/O functions. They can read/write to
* either a file or a filter. They perform efficient character-at-a-time
* semantics by buffering the I/O requests.
*/
static BOOLEAN reading; /* True if file/program open for reading */
static BOOLEAN writing; /* True if file/program open for writing */
static BOOLEAN forfile; /* True if I/O to file; False for program */
static BOOLEAN usestdio;/* True if using stdin/stdout; False otherwise */
static BOOLEAN beautify;/* True if we're supposed to strip control chars */
/* This function opens a file or program for either input or output.
* It returns True if successful. If there are any errors, it issues an
* error message and returns False.
*
* "name" is the name of the file to open. If the name begins with a '!'
* then the rest of the name is interpretted as a program to be executed
* instead of a program name.
*
* "rwa" can be 'r' to open the file/program for reading, 'w' to open the
* file/program for writing, or 'a' to open a file for appending. 'a' can't
* be used with programs.
*
* "prgsafe" is only significant if the "safer" option is set. When "safer"
* is set and "prgsafe" is False, this function will fail if "name" refers
* to a program.
*
* "force" can cause a file to be overwritten even if "name" and "oldname"
* don't match.
*/
BOOLEAN ioopen(name, rwa, prgsafe, force, binary)
char *name; /* name of file, or "!program" */
_char_ rwa; /* 'r' to read, 'w' to write, or 'a' to append */
BOOLEAN prgsafe;/* If True, allow "!program"; else refuse */
BOOLEAN force; /* if True, allow files to be clobbered */
BOOLEAN binary; /* If True, use binary I/O; else use text I/O */
{
DIRPERM perms;
assert(!reading && !writing);
/* are we going to beautify this text? */
beautify = (BOOLEAN)(o_beautify && !binary);
/* Is this stdin/stdout? */
if (!name || !*name)
{
usestdio = True;
reading = (BOOLEAN)(rwa == 'r');
writing = (BOOLEAN)!reading;
return True;
}
/* Is this a program? */
else if (*name == '!')
{
/* check safety */
if (o_safer && !prgsafe)
{
msg(MSG_ERROR, "unsafe filter");
return False;
}
/* will we be reading or writing? */
forfile = usestdio = False;
switch (rwa)
{
case 'r':
/* Open the program for reading, using the GUI-
* dependent version of prgopen() if there is one.
* Then call prggo() right away, since we aren't going
* to be calling prgwrite().
*/
if ((gui->prgopen
? (*gui->prgopen)(name + 1, False, True)
: prgopen(name + 1, False, True))
&& prggo())
{
/* success! */
reading = True;
return True;
}
else
{
msg(MSG_ERROR, "[s]can't run $1", name + 1);
}
return False;
case 'w':
/* Open the program. Note that if there is no GUI-
* dependent version of prgopen(), then we'll need to
* explicitly read the program's output and display it
* in the window, so we call the generic prgopen()
* for both writing and reading.
*/
if (gui->prgopen
? (*gui->prgopen)(name + 1, True, False)
: prgopen(name + 1, True, True))
{
writing = True;
return True;
}
msg(MSG_ERROR, "[s]can't run $1", name + 1);
return False;
default:
msg(MSG_ERROR, "can't append to filter");
return False;
}
}
else /* I/O to file */
{
/* try to open the file */
forfile = True;
perms = dirperm(name);
switch (rwa)
{
case 'r':
if ((perms == DIR_READONLY || perms == DIR_READWRITE)
&& txtopen(name, 'r', binary) == 0)
{
reading = True;
return True;
}
break;
case 'w':
if ((perms == DIR_NEW || perms == DIR_NOTFILE ||
(perms == DIR_READWRITE && (force || o_writeany)))
&& txtopen(name, 'w', binary) == 0)
{
writing = True;
return True;
}
break;
default:
if (((perms == DIR_NEW && force) || perms == DIR_READWRITE)
&& txtopen(name, 'a', binary) == 0)
{
writing = True;
return True;
}
}
/* If we get here, we failed. "perms" gives clue as to why */
switch (perms)
{
case DIR_INVALID: msg(MSG_ERROR, "[s]malformed file name $1", name); break;
case DIR_BADPATH: msg(MSG_ERROR, "[s]bad path $1", name); break;
case DIR_NOTFILE: msg(MSG_ERROR, "[s]$1 is not a file", name); break;
case DIR_NEW: msg(MSG_ERROR, "[s]$1 doesn't exist", name); break;
case DIR_UNREADABLE: msg(MSG_ERROR, "[s]$1 unreadable", name); break;
case DIR_READONLY: msg(MSG_ERROR, "[s]$1 unwritable", name); break;
case DIR_READWRITE: msg(MSG_ERROR, "[s]$1 exists", name); break;
}
return False;
}
/*NOTREACHED*/
}
/* This function writes the contents of a given I/O buffer. "iobuf" points
* to the buffer, and "len" is the number of CHARs in that buffer. This
* function returns the number of CHARs actually written; values less than
* "len" indicate trouble.
*/
int iowrite(iobuf, len)
CHAR *iobuf; /* RAM buffer containing text */
int len; /* number of CHARs in iobuf */
{
assert(writing);
/* write to the file/program */
if (usestdio)
{
return fwrite(iobuf, sizeof(CHAR), (size_t)len, stdout);
}
else if (forfile)
{
return txtwrite(iobuf, len);
}
else
{
return prgwrite(iobuf, len);
}
}
/* This function fills a given I/O buffer with text read from the file/program.
* "iobuf" points to the buffer, and "len" is the maximum number of CHARs that
* it can hold. This function returns the number of CHARs actually read, or
* 0 at the end of the file. If the "beautify" option is True, it strips
* control characters.
*/
int ioread(iobuf, len)
CHAR *iobuf; /* RAM buffer to receive input text */
int len; /* maximum number of bytes to read */
{
int nread; /* number of CHARs read */
int i, j;
assert(reading);
/* read from the file/program */
if (usestdio)
{
nread = fread(iobuf, sizeof(CHAR), (size_t)len, stdin);
}
else if (forfile)
{
nread = txtread(iobuf, len);
}
else
{
nread = prgread(iobuf, len);
}
/* maybe strip control characters */
if (beautify && nread > 0)
{
for (i = j = 0; i < nread; i++)
{
if (iobuf[i] >= ' ' || iobuf[i] == '\t' || iobuf[i] == '\n' || iobuf[i] == '\f')
{
iobuf[j++] = iobuf[i];
}
}
nread = j;
}
/* return number of bytes read */
return nread;
}
/* Close a file that was opened via ioopen() */
BOOLEAN ioclose()
{
CHAR *rdbuf;
int nbytes;
BOOLEAN origrefresh;
assert(reading || writing);
/* don't really close stdin/stdout; just reset variables */
if (usestdio)
{
usestdio = reading = writing = False;
return True;
}
/* completing I/O for a file is easy */
if (forfile)
{
txtclose();
reading = writing = False;
return True;
}
/* Writing to a program; we need to call prggo() now. Also, if there
* is no gui->prgopen() function then we need to explicitly copy the
* program's output to the current window, so the user can see error
* messages and other results.
*/
if (writing && prggo() && !gui->prgopen && windefault)
{
drawopencomplete(windefault);
origrefresh = o_exrefresh;
o_exrefresh = True;
rdbuf = (CHAR *)safealloc(1024, sizeof(CHAR));
while ((nbytes = prgread(rdbuf, 1024)) > 0)
{
drawextext(windefault, rdbuf, nbytes);
}
safefree(rdbuf);
o_exrefresh = origrefresh;
}
/* if we wrote to a program with an explicit prgopen() command, then
* there may have been output that we don't know about. Force the
* window into DRAW_OPENOUTPUT mode so we have to hit <Enter> to
* continue.
*/
if (writing && gui->prgopen && windefault)
{
drawopencomplete(windefault);
}
/* wait for the program to exit. Succeed only if its exit code is 0 */
reading = writing = False;
return (BOOLEAN)((gui->prgclose ? (*gui->prgclose)() : prgclose()) == 0);
}
/* This function parses a "path" string into directory names, and checks each
* directory until finds a readable file named "filename". If the "usefile"
* flag is True, then if an element of the path happens to be a file name
* instead of a directory name, then it will also use that as the file name.
*
* If NULL is passed instead of a "path" string, then this function will
* continue the previous path search instead of starting a new one.
*
* Returns the full pathname of the first readable file that it finds, or
* NULL if it reaches the end of the path without finding one.
*/
char *iopath(path, filename, usefile)
char *path; /* list of directories to search */
char *filename; /* name of file to look for in each directory */
BOOLEAN usefile; /* allow path to contain filenames */
{
static char *next; /* the directory after this one */
static char name[256]; /* full pathname, possibly of a readable file */
int i; /* used for building name */
DIRPERM perms; /* permissions of the file */
/* if path was named, start from there; else continue previous search */
if (path)
{
next = path;
}
/* repeat until we find a readable file... */
do
{
/* if no place left to try, then quit */
if (!next)
{
return (char *)0;
}
/* if next path element starts with ~, then use HOME instead */
if (next[0] == '~' && !isalnum(next[1]))
{
/* copy HOME into name buffer */
strcpy(name, tochar8(o_home));
i = strlen(name);
next++;
}
else
{
i = 0;
}
/* copy characters up to next delimiter or end */
while (*next && *next != OSPATHDELIM)
{
name[i++] = *next++;
}
name[i] = '\0';
/* if this was the end of the path, then there is no "next" */
if (!*next)
{
next = (char *)0;
}
else
{
/* skip delimiter */
next++;
}
/* If files are allowed and this is a readable file, use it.
* Otherwise we'll need to append the filename and try that.
*/
if (!usefile ||
((perms = dirperm(name)) != DIR_READONLY && perms != DIR_READWRITE))
{
/* append the filename */
path = dirpath(*name ? name : ".", filename);
strcpy(name, path);
perms = dirperm(name);
}
} while (perms != DIR_READONLY && perms != DIR_READWRITE);
/* return the found name */
return name;
}
/* This function implements filename completion. You pass it a partial
* filename and it uses dirfirst()/dirnext() to extend the name. If you've
* given enough to uniquely identify a file, then it will also append a
* tab after the filename.
*/
char *iofilename(partial, endchar)
char *partial; /* a partial filenam to expand */
_char_ endchar; /* char to append if match found */
{
char homed[256]; /* partial, with "~" replaced by home */
static char match[256]; /* the matching text */
int matchlen; /* # of leading identical characters */
int nmatches; /* number of matches */
char *fname; /* name of a matching file */
CHAR slash[1]; /* this system's directory separator */
int col; /* width of directory listing */
/* Find the directory separator character */
slash[0] = dirpath("a", "b")[1];
/* replace ~ with home directory name */
if (partial[0] == '~' && partial[1] == (char)slash[0])
{
strcpy(homed, tochar8(o_home));
strcat(homed, &partial[1]);
partial = homed;
}
/* count the matching filenames */
for (nmatches = matchlen = 0, fname = dirfirst(partial, True);
fname;
nmatches++, fname = dirnext())
{
if (nmatches == 0)
{
strcpy(match, fname);
matchlen = strlen(match);
}
else
{
while (matchlen > 0 && strncmp(match, fname, (size_t)matchlen))
{
matchlen--;
}
}
}
/* so what did we come up with? */
if (nmatches == 1)
{
/* unique match -- append a tab or slash */
match[matchlen] = (dirperm(match)==DIR_NOTFILE) ? *slash : endchar;
match[matchlen + 1] = '\0';
return match;
}
else if (matchlen > 0)
{
/* can we add any chars to the partial name? */
if ((unsigned)matchlen <= strlen(partial) && !strncmp(partial, match, matchlen))
{
/* No - list all matches */
for (fname = dirfirst(partial, True), col = 0;
fname;
fname = dirnext())
{
/* space between names */
if (col + strlen(fname) + 2 >= (unsigned)o_columns(windefault))
{
drawextext(windefault, toCHAR("\n"), 1);
col = 0;
}
else if (col > 0)
{
drawextext(windefault, toCHAR(" "), 1);
col++;
}
/* show the name */
drawextext(windefault, toCHAR(fname), strlen(fname));
col += strlen(fname);
/* if directory, then append a slash */
if (dirperm(fname) == DIR_NOTFILE)
{
drawextext(windefault, slash, 1);
col++;
}
}
if (col > 0)
{
drawextext(windefault, toCHAR("\n"), 1);
}
}
/* Either way - return the common part of all matches */
match[matchlen] = '\0';
return match;
}
else
{
/* no match, or matches have nothing in common */
return (char *)0;
}
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.