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.