Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
c/lib5/usys/open.c
There are 10 other files named open.c in the archive. Click here to see a list.
/*
* OPEN - URT lowest-level file-opening
*
* Copyright (C) 1986 by Ian Macky, SRI International
* Edits for ITS: Copyright (C) 1988 Alan Bawden
*
* Also provides _gtjfn() and _rljfn().
*/
#include "c-env.h"
#include "errno.h"
#include "sys/usysio.h"
#include "sys/usysig.h"
#include "sys/usytty.h"
#include "sys/file.h"
#if SYS_T20+SYS_10X
#include "jsys.h"
#include "string.h"
#include "strung.h"
#elif SYS_ITS
#include "sysits.h"
#include "string.h"
#endif
extern int _dvtype(); /* from stat */
#if SYS_ITS
static void cvt_filename(); /* Convert filename for SOPEN */
#elif SYS_T20+SYS_10X
#define MAX_LINE (512) /* size of a GP string buffer */
struct filename { /* for C:sys/xxx.h */
char path[FILENAME_SIZE]; /* "sys" */
char device[FILENAME_SIZE]; /* "C" - could be a logical! */
char directory[FILENAME_SIZE]; /* NULL */
char filename[FILENAME_SIZE]; /* "xxx.h" */
};
extern int _gtfdb(); /* used by various routines, defined in stat */
static int _get_tricky(); /* tricky logical name expander for _get_jfn */
static int _get_jfn(); /* work-horse for getting a file */
static int __gtjfn(); /* lowest level gtjfn, which does the GTJFN% */
static void parse_filename(); /* parse up a TOPS-20 filespec */
static void make_filename(); /* construct a TOPS-20 filespec from pieces */
static int log_expand(); /* expand a logical device */
extern int _dvtype();
static int _openf();
#endif /* SYS_T20+SYS_10X */
/* See the UIODAT.C module for definitions of the internal I/O tables
** and comments on their use.
*/
int
creat(path, mode)
char *path;
int mode;
{
return open(path, (O_WRONLY | O_CREAT | O_TRUNC), mode);
}
int
open(path, flags, mode)
char *path;
int flags, mode;
{
int fd, ufx;
#if SYS_T20+SYS_10X
int jfn;
#elif SYS_ITS
int bits;
char realpath[FILENAME_SIZE+1];
char *realpath_ptr = realpath-1;
#endif
USYS_BEG();
if ((fd = _uiofd()) == -1 || !(ufx = _uiofx()))
USYS_RETERR(EMFILE); /* Failed to get FD or UFX slot */
#if SYS_T20+SYS_10X
if (flags & O_SYSFD) {
jfn = (int) path; /* "path" is actually JFN. */
} else {
/* This would be the point to translate the protection modes
* and pass to the GTJFN% as a ";Pnnnnnn" suffix for use if
* creating a new file (or version).
* tnxprot(mode, &protstr);
*/
if (!(jfn = _gtjfn(path, flags)))
USYS_RETERR(ENOENT); /* Failed to get JFN for file */
}
/*
* check to see if this is an old file, or a new one. if FB_NXF
* is set then the file is new and doesn't exist (until closed)
*/
_uioch[ufx] = jfn; /* Store the JFN */
if (!(_gtfdb(jfn, _FBCTL) & FB_NXF))
_uioflgs[ufx] |= _UIO_OLD;
/*
* now open the file
*/
if (!(_openf(ufx, flags))) {
if ((flags&O_SYSFD)==0) /* Failed, release the JFN if we created one */
_rljfn(jfn);
USYS_RETERR(ENOENT); /* and lose-return */
}
#elif SYS_ITS
/* O_NDELAY: don't hang mode ignored for now */
/* Bits other than the ones listed here are not handled. */
switch (flags & ~(O_BINARY | O_CONVERTED | O_UNCONVERTED | O_BSIZE_MASK |
O_NDELAY | O_ITS_IMAGE | O_ITS_NO_IMAGE)) {
case (O_RDONLY):
bits = SC_IMC(_UAI);
_uioflgs[ufx] |= _UIO_OLD; /* If we win, it will be old! */
break;
case (O_WRONLY | O_CREAT | O_TRUNC):
bits = SC_IMC(_UAO);
break;
default: USYS_RETERR(EINVAL);
}
switch (flags & O_BSIZE_MASK) {
case O_BSIZE_7: _uiobsize[ufx] = 7; break;
case O_BSIZE_8: _uiobsize[ufx] = 8; break;
case O_BSIZE_9: _uiobsize[ufx] = 9; break;
default: _uiobsize[ufx] = (flags & O_BINARY) ? 9 : 7; break;
}
if (_uiobsize[ufx] == 7) {
/* Assume 7-bit bytes means ASCII, unless user explicitly asks */
/* for image mode: */
if (flags & O_ITS_IMAGE) bits |= _DOIMG;
} else {
/* Assume other byte sizes mean image mode, unless user */
/* explicitly says not to. Turn on block mode and signal */
/* handpacking in either case. */
bits |= ((flags & O_ITS_NO_IMAGE) ? _DOBLK : (_DOBLK | _DOIMG));
_uioflgs[ufx] |= _UIO_HANDPACK;
}
if ((flags & O_CONVERTED) ||
(_uiobsize[ufx] == 7 && !(flags & (O_UNCONVERTED | O_BINARY))))
_uioflgs[ufx] |= _UIO_CONVERTED;
if (strlen(path) > FILENAME_SIZE)
USYS_RETERR(ENAMETOOLONG);
cvt_filename(path, realpath);
if (SYSCALL3("sopen", bits, _uioch[ufx], &realpath_ptr))
USYS_RETERR(ENOENT);
#else
#error open() not supported for this system.
#endif
_openufx(ufx, flags); /* Set up UFX with given flags */
_uioufx[fd] = ufx; /* Set up UFX mapping for the FD */
USYS_RET(fd);
}
/* _OPENUFX - Auxiliary used by open() and pipe() to share common code.
** _uioch and _uioflags must already be set.
** _uiopbuf is either NULL or points to an allocated buffer for re-use.
** Sets:
** _uioflags (OR'd in)
** _uiotype
** _uiodnum
** _uiopbuf
** _uiocp
** _uiocnt
** _uiopos
** _uioeof
** _uionopen
** _uiozcnt (on ITS)
*/
_openufx(ufx, flags)
int ufx, flags;
{
/* Set R/W flags */
if (flags & O_RDWR) _uioflgs[ufx] |= (_UIO_READ | _UIO_WRITE);
else _uioflgs[ufx] |= ((flags & O_WRONLY) ? _UIO_WRITE : _UIO_READ);
/* Set buffer variables */
#if 0
if (_uiopbuf[ufx]) {
_uiocp[ufx] = _uiopbuf[ufx] - 1;
_uiocnt[ufx] = (flags & O_WRONLY) ? UIO_BUFSIZ : 0;
} else {
#elif 0
if (_uiopbuf[ufx])
_panic("opening unreleased IOB");
#endif
/* Alan: Code in read() assumes that no ufx already has an associated
** buffer. Perhaps the error check above should be uncommented?
** It does seem best to rely on close() to always free buffers.
*/
_uiopbuf[ufx] = NULL;
_uiocp[ufx] = NULL, _uiocnt[ufx] = 0;
#if SYS_ITS
_uiozcnt[ufx] = 0;
_uiobword[ufx] = (36 / _uiobsize[ufx]);
#endif /* SYS_ITS */
_uioeof[ufx] = 0; /* Clear EOF flag */
if (flags & O_APPEND) { /* for append file, read starting position */
#if SYS_T20+SYS_10X
int ablock[5];
ablock[1] = _uioch[ufx];
_uiopos[ufx] = (jsys(RFPTR, ablock)) ? ablock[2] : 0;
#elif SYS_ITS
/* Append not supported yet. */
SYSCALL2_LOSE("lose", SC_IMC(1), SC_IMM(0));
#endif
} else _uiopos[ufx] = 0; /* else starts at 0 */
_uiotype[ufx] = _dvtype(_uioch[ufx]); /* get dev type for chan/JFN */
_uiodnum[ufx] = 0; /* Assume 0 for minor dev # */
#if SYS_ITS
if (_uiotype[ufx] == _DVUSR) {
int uind;
SYSCALL3_LOSE("usrvar", _uioch[ufx], SC_SIX("uind"), SC_VAL(&uind));
_uiodnum[ufx] = SC_IMM(_JSNUM | uind);
}
#endif /* SYS_ITS */
#if !SYS_ITS
if (_uiotype[ufx] == _DVTTY) { /* If TTY, fix up device number */
int i;
if (_uioch[ufx] == _CTTRM) /* Always use 0 for control TTY */
i = 0;
else { /* If a random TTY, pick another */
for (i = 1; _ttys[i].tt_ufx != 0;)
if (++i >= _NTTYS) {
#define TOOMANYTTYS "\npanic: trying to open too many terminal devices.\n"
write(2, TOOMANYTTYS, sizeof(TOOMANYTTYS)-1);
abort();
}
}
/* i now has index into _ttys[] */
if (_ttys[i].tt_ufx == 0) {
_ttys[i] = _deftty; /* Initialize whole struct! */
_ttys[i].tt_ufx = ufx; /* Point back to UFX */
}
_uiodnum[ufx] = i; /* Point to tty struct # */
}
#endif /* !SYS_ITS */
/* OK, all ready to go! Set confirmation that UFX is open, and map FD */
_uionopen[ufx] = 1; /* UFX is open, with only one FD */
}
/*
* int _uiofd();
*
* find a spare FD slot and return the FD, else -1 if none. this
* routine is exported, so don't make it static.
*/
int _uiofd()
{
int fd;
for (fd = 0; fd < OPEN_MAX; fd++)
if (!_uioufx[fd])
return fd;
errno = EMFILE;
return -1;
}
/*
* int _uiofx();
*
* find a spare UFX slot and return the UFX, else 0 if none. this
* routine is exported, so don't make it static.
*
* Note a ufx index of 0 is NEVER seen! The tables this index addresses
* have length OPEN_UFX_MAX+1, and a valid return value can be from 1 to
* OPEN_UFX_MAX inclusive.
*/
int _uiofx()
{
int ufx;
for (ufx = 0; ++ufx <= OPEN_UFX_MAX;)
if (_uionopen[ufx] <= 0) { /* If found a free slot */
_uionopen[ufx] = 0; /* Make sure marked not active */
_uioflgs[ufx] = 0; /* Also ensure nothing open */
#if SYS_ITS
/* On ITS there is a one-to-one correspondence between UFXs */
/* and channels, so we just store the correct SYSCALL */
/* argument for that channel in _uioch. */
_uioch[ufx] = SC_IMM(ufx-1);
#endif /* SYS_ITS */
return ufx;
}
errno = EMFILE; /* No free slots, fail */
return 0;
}
#if SYS_ITS
/*
* Convert an ITS filename to make it acceptable to SOPEN.
* "/" becomes ";"
* "." becomes space when embedded in name components
* tab becomes space
* ^Q inhibits the above
*/
static void cvt_filename(from, to)
char *from, *to;
{
char *ptr, *start;
int c, len;
strcpy(to, from);
ptr = to-1;
start = NULL;
do {
switch (c = *++ptr) {
case '/':
*ptr = ';';
case ';':
case ':':
start = NULL;
break;
case '\t':
*ptr = ' ';
case ' ':
case '\0':
if (start)
while (--len > 0)
switch (*++start) {
case '.': *start = ' '; break;
case '\021': ++start; break;
}
start = NULL;
break;
case '\021':
c = *++ptr;
default:
if (start) len++;
else { start = ptr; len = 0; }
break;
}
} while (c);
}
#endif /* SYS_ITS */
#if SYS_T20+SYS_10X
/*
* get a jfn on a pathname.
*/
int _gtjfn(path, flags)
char *path;
int flags;
{
int jfn;
struct filename file;
char tried[MAX_LINE]; /* devices already tried */
parse_filename(path, &file); /* parse up the filename */
if (!*file.path) /* if no unix-style path */
return _get_jfn(path, flags); /* spec, then this is easy */
*tried = '\0'; /* nothing tried yet */
return _get_tricky(&file, tried, 1, flags); /* alas, do tricky things */
}
/*
* tricky logical-device follower. this traces out logical devices
* and generates full paths from the different end-directories,
* keeping at it until it finds the file. this routine is not needed
* unless foo/ style filespecs are being used.
*
* tried[] is a buf that _get_tricky stores devices it has tried into,
* so that it won't re-try anything and potentially get into a loop.
*/
static int _get_tricky(file, tried, job_wide_flag, flags)
struct filename *file;
char *tried;
int job_wide_flag;
{
char expansion[MAX_LINE], o_device[FILENAME_SIZE], token[FILENAME_SIZE];
char new_path[MAX_LINE], *cp, *p, *next;
int jfn;
if (!log_expand(file->device, expansion, job_wide_flag)) {
make_filename(new_path, file); /* not a logical device */
return _get_jfn(new_path, flags); /* so just look for it. */
}
strcpy(o_device, file->device); /* save original device */
for (cp = expansion; *cp; cp = ++next) { /* loop over expansion. */
next = strchr(cp, ','); /* find separator */
if (next) *next = '\0'; /* tie this one off */
if (p = strchr(cp, '<')) { /* if got a directory */
*strchr(cp, ':') = '\0'; /* kill the colon */
strcpy(file->device, cp); /* device from expansion */
*strchr(p, '>') = '\0'; /* kill trailing > */
strcpy(file->directory, ++p); /* directory from expansion */
make_filename(new_path, file); /* make up the new path */
if (jfn = _get_jfn(new_path, flags))
return jfn; /* got it! */
} else {
*strchr(cp, ':') = '\0'; /* kill the colon */
job_wide_flag = (strCMP(o_device, cp) != 0); /* same device on */
*token = (job_wide_flag) ? '*' : '$'; /* right side means sys */
strcpy(token + 1, cp); /* construct device token */
if (!strSTR(tried, token)) { /* if haven't tried this */
strcat(tried, token); /* we're trying it now */
strcpy(file->device, cp); /* try this device now */
if (jfn = _get_tricky(file, tried, job_wide_flag, flags))
return jfn;
}
}
}
return 0; /* not found... */
}
/*
* do the real work of finding a file
*/
static int _get_jfn(path, flags)
char *path;
int flags;
{
int jfn, bits;
bits = (flags & O_T20_WILD) ? GJ_IFG : 0;
if (flags & (O_RDWR | O_WRONLY | O_APPEND)) {
/*
* Normally we always create a new version for writing.
* The O_T20_WROLD flag exists in order to override this and
* act like UNIX if such behavior is ever desirable (ugh).
* Note that if O_APPEND or O_RDWR is set, we always look
* for an existing file first.
*/
if (!(flags & (O_APPEND | O_RDWR | O_T20_WROLD)))
return __gtjfn(path, GJ_FOU);
else if (jfn = __gtjfn(path, GJ_OLD)) {
if ((flags & O_CREAT) && (flags & O_EXCL)) {
_rljfn(jfn);
errno = EEXIST;
return 0;
}
} else if (!(flags & O_CREAT) || !(jfn = __gtjfn(path, GJ_FOU))) {
errno = ENOENT;
return 0;
} else return jfn;
} else return __gtjfn(path, ((flags & O_T20_WILD) ? GJ_IFG : 0) | GJ_OLD);
}
/*
* lowest level actual GTJFN% call
*/
static int __gtjfn(path, flags)
char *path;
int flags;
{
int arg_block[5];
arg_block[1] = GJ_SHT | flags;
arg_block[2] = (int) (path - 1);
return (jsys(GTJFN, arg_block)) ? arg_block[1] : 0;
}
/*
* take apart a TOPS-20 filespec and put the pieces in the given
* (struct filename) structure.
*/
static void parse_filename(path, file)
char *path;
struct filename *file;
{
char *start, *part;
int c, length;
*file->path = *file->device = *file->directory = *file->filename = '\0';
do { /* loop over pathname */
start = path; /* cp to start of part */
length = 0; /* length of part */
part = NULL; /* what this part is */
while (!part) switch (c = *path++) {
case '\26': path++; length++; break; /* ^V quote */
case '\0': part = file->filename; break;
case ':': part = file->device; break;
case '<': start = path; continue;
case '>': part = file->directory; break;
case '/': part = file->path; break;
default: length++;
}
strncpy(part, start, length);
part[length] = '\0';
} while (c);
}
/*
* given a (struct filename) structure, construct a TOPS-20 filename
* from the pieces
*/
static void make_filename(buf, file)
char *buf;
struct filename *file;
{
*buf = '\0'; /* initialize buffer always */
if (*file->device) {
strcat(buf, file->device);
strcat(buf, ":");
}
if (*file->directory || *file->path) {
strcat(buf, "<");
if (*file->directory)
strcat(buf, file->directory);
strcat(buf, ".");
if (*file->path)
strcat(buf, file->path);
strcat(buf, ">");
}
if (*file->filename) strcat(buf, file->filename);
}
/*
* expand the logical device *from (which should not include the
* trailing colon) to *to. if (job_wide) then a local expansion is
* tried first, else just system-wide.
*/
static int log_expand(from, to, job_wide)
char *from, *to;
int job_wide;
{
int ablock[5];
char *p;
ablock[2] = (int) (from - 1);
ablock[3] = (int) (to - 1);
if (job_wide) { /* not system-logical only? */
ablock[1] = _LNSJB; /* job-wide */
if (jsys(LNMST, ablock)) return 1; /* local expansion worked */
}
ablock[1] = _LNSSY; /* system-wide */
return jsys(LNMST, ablock);
}
#define old_file(ufx) (_uioflgs[ufx] & _UIO_OLD)
#define new_file(ufx) (!(_uioflgs[ufx] & _UIO_OLD))
static int
_openf(ufx, flags)
int ufx, flags;
{
int byte_size;
int open_mode = 0;
int arg_block[5];
/*
* convert file.h flags into OPENF% bit flags
*/
switch (flags & (O_RDONLY | O_WRONLY | O_RDWR | O_APPEND)) {
case O_WRONLY: open_mode = OF_WR; break;
case (O_WRONLY | O_APPEND):
open_mode = OF_WR | OF_APP; break;
case O_RDWR: open_mode = OF_RD | OF_WR | OF_PLN; break;
case (O_RDWR | O_APPEND):
open_mode = OF_RD | OF_WR | OF_APP | OF_PLN; break;
case O_APPEND: open_mode = OF_APP; break;
default: open_mode = OF_RD | OF_PLN;
}
/*
* you can't open an existing file for write-only (not read-write),
* and not have it be truncated. sorry, kids.
*/
if (old_file(ufx) && (open_mode == OF_WR) && !(flags & O_TRUNC)
&& _dvtype(_uioch[ufx]) == _DVDSK) {
errno = ETRUNC; /* new T20 error code! */
return 0;
}
/*
* to avoid truncating an existing file, you need to open it in
* r/w mode, even if you just want write.
*/
if (old_file(ufx) && !(flags & (O_TRUNC | O_APPEND)))
open_mode |= OF_RD;
/*
* If a byte size is explicitly requested, use that.
* Otherwise:
* for a new file, use 9 if O_BINARY, else 7.
* for an old file, use the file bytesize.
* A size of 0 or 36 is handled as for a new file.
* Any other size is simply used - if this is not
* one of 7, 8, or 9 then the results are unpredictable.
*/
switch (flags & O_BSIZE_MASK) {
case O_BSIZE_7: byte_size = 7; break;
case O_BSIZE_8: byte_size = 8; break;
case O_BSIZE_9: byte_size = 9; break;
default: /* Error, but nothing we can do about it now. */
case 0:
if (new_file(ufx))
byte_size = (flags & O_BINARY) ? 9 : 7 ;
else {
byte_size = (_gtfdb(_uioch[ufx],_FBBYV) << FBBSZ_S) & FBBSZ_M;
if (byte_size == 0 || byte_size == 36)
byte_size = (flags & O_BINARY) ? 9 : 7;
}
}
_uiobsize[ufx] = byte_size; /* save for posterity */
if (flags & O_T20_THAWED) open_mode |= OF_THW; /* thawed access? */
/*
* if they explicitly request converted i/o, then propagate the flag,
* else if they DIDN'T explicitly request UNconverted (e.g. it's up to
* us to decide), and the file is being opened for text in 7-bit bytes,
* then go for conversion.
*/
if ((flags & O_CONVERTED) ||
(byte_size == 7 && !(flags & (O_UNCONVERTED | O_BINARY))))
_uioflgs[ufx] |= _UIO_CONVERTED;
arg_block[1] = _uioch[ufx];
arg_block[2] = (byte_size << 30) + open_mode;
if (!jsys(OPENF, arg_block))
return 0; /* Open failed */
/* We won! Now do special hack if dealing with a new file -- we
** quickly close and then re-open it, so that the file will then
** actually exist and can be seen by access(), stat(), GTFDB%, and
** everything else. This compensates for the T20 braindamage where
** a new file doesn't really exist (certainly not permanently) until
** it is CLOSF%'d.
*/
if (!new_file(ufx))
return 1; /* Not a new file, we're already OK */
arg_block[1] = _uioch[ufx] | CO_NRJ;
jsys(CLOSF, arg_block); /* CLOSF% it, keeping JFN (ignore error) */
arg_block[1] = _uioch[ufx]; /* Now OPENF% again (ac2 still has mode) */
return jsys(OPENF, arg_block);
}
int _rljfn(jfn)
int jfn;
{
int arg_block[5];
arg_block[1] = jfn;
return jsys(RLJFN, arg_block);
}
#endif /* SYS_T20+SYS_10X */
#if 0 /* OLD STUFF - Kept pending integration */
#if SYS_T20+SYS_10X
static char *
tnxprot(uprot, str)
int uprot;
char *str;
{
char *s = str;
*s++ = ';';
*s++ = 'P';
f = modprot(mode >> 6); /* user prots */
*s++ = (f >> 3) + '0';
*s++ = (f & 7) + '0';
f = modprot(mode >> 3); /* group prots */
*s++ = (f >> 3) + '0';
*s++ = (f & 7) + '0';
f = modprot(mode); /* other prots */
*s++ = (f >> 3) + '0';
*s++ = (f & 7) + '0';
return str;
}
/* Unix -> T20/10X file protection conversion
*/
static int
modprot(mod)
int mod;
{
int prot = 0;
if (mod & 1) prot |= 012; /* execute (and list) access */
if (mod & 2) prot |= 027; /* write and append access */
if (mod & 4) prot |= 042; /* read (and list) access */
return prot;
}
#endif
#if 0
creat(name,prot) == _cfile(name,prot,7,0); /* ASCII */
bcreat(name,prot) == _cfile(name,prot,8,0);
icreat(name,prot) == _cfile(name,prot,36,0);
open(name,mode) == _ofile(name,mode,7); /* ASCII */
bopen(name,mode) == _ofile(name,mode,8);
iopen(name,mode) == _ofile(name,mode,36);
/* Open for writing (create) */
_cfile(filename, unixprot, bytesize, appendflg) - Create file, write.
(for ITS this is just _ofile(filename, O_WRONLY, bytesize) - no append)
(for WAITS, ditto ITS)
(T20/10X basically did _ofile too, but noticed protection and bytesize)
/* Open for reading */
_ofile(filename, unixmode, bytesize) - read file ; unixmode is O_ flags.
for T20, fileparse then GTJFN/OPENF.
#endif
#if SYS_T20+SYS_10X
/* --------------------------------------------------------------------- */
/* jacket routine for GTJFN% to mung filenames into submission */
/* --------------------------------------------------------------------- */
int _gtjfn(name, flags)
char *name;
{
char dirpart[80], filpart[80], *dirptr, *filptr, *_dirst();
int anydir, indir; /* marker for dir part changed */
anydir = 0; /* dir part remains default */
indir = 0; /* assume not in directory part */
dirpart[0] = 'D'; /* start off with directory part */
dirpart[1] = 'S'; /* pointing to "DSK:" */
dirpart[2] = 'K';
dirpart[3] = ':';
dirptr = &dirpart[3]; /* point to just before the end */
filptr = &filpart[-1]; /* of dir and file parts */
while (1) {
switch (*name) {
case '\0': /* run out of chars? */
*++filptr = 0; /* yes, null terminate */
filptr = &filpart[-1]; /* start at top again */
while (*++dirptr = *++filptr) ; /* append to dir part */
return gtjfn_(dirpart, flags); /* do low level lookup */
case ':':
if (anydir) return -1; /* already have dir, lose */
anydir = 1; /* now we have one */
*++filptr = '\0'; /* terminate file part */
dirptr = &dirpart[-1]; /* start at top */
filptr = &filpart[-1]; /* top of filename part */
while (*++dirptr = *++filptr); /* copy across */
*dirptr = ':'; /* put the colon on */
filptr = &filpart[-1]; /* reinitialize file pointer */
break;
case '[':
case '<':
*++filptr = '<'; /* open bracket becomes angle */
indir = 1; /* remember we're in dir part */
break;
case '.':
if (indir < 0) *++filptr = '\026'; /* quote extra dots */
else if (indir == 0) indir = -1; /* only allow one in file */
*++filptr = '.'; /* add the dot */
break;
case '>':
case ']':
if (*dirptr != ':') return -1; /* already have dir, lose */
anydir = 1; /* remember we have a dir and dev */
indir = 0; /* no longer in dir part */
*++filptr = '\0'; /* terminate file part */
filptr = &filpart[-1]; /* top of filename part */
while (*++dirptr = *++filptr); /* copy across */
*dirptr = '>'; /* add the close bracket */
filptr = &filpart[-1]; /* reinitialize file pointer */
break;
case '\026': /* control-V? */
case '\\': /* or quoteing backslash? */
*++filptr = '\026'; /* yes, add it */
*++filptr = *++name; /* and the next char */
break;
case '/': /* slash UNIX dir delimiter? */
indir = 0; /* yes, no longer in dir part */
*(dirptr+1) = '\0'; /* yes, terminate directory part */
*++filptr = '\0'; /* and filename part */
switch (filpart[0]) { /* check out first part of name */
case '.': /* period? */
switch (filpart[1]) { /* yes, look at what follows */
case '\0': /* ./x ? */
name++; /* yes, skip slash */
filptr = &filpart[-1]; /* start again in file part */
continue; /* and ignore this dir part */
default:
return -1; /* .x/y loses */
case '\026': /* maybe ..? */
break; /* yes, handle outside switch */
}
/* dir starts with .. - hack superdirectory */
if (filpart[2] != '.' || filpart[3] != '\0') return -1;
if (*dirptr != '>') { /* do we have a real dir part? */
dirptr = _dirst(dirpart); /* no, fix it up */
if (dirptr == NULL) return -1; /* lost */
}
while (*--dirptr != '.' && *dirptr != ':') ; /* find delim */
if (*dirptr == '.') { /* found subdir start? */
*dirptr = '>'; /* turn into dir end */
name++; /* skip over slash */
filptr = &filpart[-1]; /* forget .. */
continue; /* on with the show */
}
/* no subdirs, move back to dev:<root-dir> */
case '\0': /* no name, want root dir */
if (*dirptr != ':') return -1; /* must have only dev */
filptr = "<ROOT-DIRECTORY" - 1; /* point to dir part */
break;
default:
if (*dirptr != '>') {
dirptr = _dirst(dirpart); /* fix up filename */
if (dirptr == NULL) return -1; /* lost */
}
*dirptr = '.'; /* drop period over close bracket */
filptr = &filpart[-1]; /* normal name, just use it */
}
while (*++dirptr = *++filptr) ; /* append name */
*dirptr = '>'; /* and close bracket */
filptr = &filpart[-1]; /* restart file part */
break;
default:
*++filptr = *name; /* normal char, add to file part */
break;
}
name++; /* move on to next char */
}
}
static char *
_dirst()
{
#asm
; *************************************************************
; Get channel (JFN), return in AC1
; *************************************************************
; Here to canonicalize dir name
; Not sure if this is really needed for anything on TENEX.
#if SYS_T20
MOVE 1,[RC%EMO] ;Forcing exact match
%CHRBP 2,-1(17) ;From string pointer given as arg
RCDIR% ;Get directory number in AC3
ERJMP $RETF
TDNE 1,[RC%NOM] ;Matched?
JRST $RETF ;No, fail
%CHRBP 1,-1(17) ;From string
MOVE 2,3 ;With directory number
DIRST% ;Make string for dir number
ERJMP $RETF
#endif /* T20 */
#if SYS_10X
SETZ 1, ; Force exact match
%CHRBP 2,-1(17) ; for string pointer given as arg
STDIR% ; Get directory number in AC1(RH)
ERJMPA $RETF ; No match
ERJMPA $RETF ; Ambiguous
HRRZ 2,1 ; Set up dir number
%CHRBP 1,-1(17) ; Output to this string
DIRST% ; Make string for dir number
ERJMPA $RETF
#endif /* 10X */
POPJ 17, ;Did it, return with updated pointer
#endasm
}
#endif /* T20+10X */
#if SYS_WAITS
/* ------------------------------- */
/* handler for file open */
/* ------------------------------- */
static int
_ofile(ufx, name, flags, uprot)
char *name;
int flags, uprot;
{
int channel,f,device,disk;
int out, byte_size;
struct filespec fs;
struct openspec {
int status;
int device;
int obuf : 18; /* LH */
int ibuf : 18; /* RH */
} ospec;
if (flags & O_RDWR) return -1;
else if (flags & O_WRONLY) out = 1;
else out = 0;
if (*name == ':') {
name++;
device = sixbit(name);
disk = 0;
}
else
disk = device = sixbit("DSK");
if (!convert(name,&fs)) return -1;
switch (flags & O_BSIZE_MASK) {
case O_BSIZE_7: byte_size = 7; break;
case O_BSIZE_8: byte_size = 8; break;
case O_BSIZE_9: byte_size = 9; break;
default: /* Error, but nothing we can do about it now. */
case 0:
if (out)
byte_size = (flags & O_BINARY) ? 9 : 7 ;
else /* Try to set from existing mode -- give up at moment */
byte_size = (flags & O_BINARY) ? 9 : 7 ;
}
if (byte_size == 7)
ospec.status = 0; /* data mode 0 */
else
ospec.status = 010; /* data mode 010 */
ospec.device = device;
if (out) {
ospec.ibuf = 0;
ospec.obuf = (int)&buffers[ufx];
if (disk) {
if (!muuo(MUUO_ENTER, f, &fs)) return -1;
}
_out(f);
} else {
ospec.ibuf = (int)&buffers[ufx];
ospec.obuf = 0;
if(!muuo(MUUO_OPEN, f, &ospec)) return -1;
if (disk) {
if (!muuo(MUUO_LOOKUP,f,&fs)) return -1;
_in(f);
}
}
return 0;
}
/* --------------------------------------------------- */
/* obtain a sixbit representation for string */
/* --------------------------------------------------- */
static sixbit(s)
char *s;
{
int n,w,t;
w = 0;
n = 30;
while (*s && n >= 0) {
t = *s++;
if (t & 0100) t = t | 040; else t = t & 0137;
w += (t&077) << n;
n -= 6;
}
return w;
}
/* ----------------------------------------------------------------------- */
/* convert a filename string to internal filespec representation */
/* ----------------------------------------------------------------------- */
static convert(s,t)
char *s; struct filespec *t;
{
char u[16],*v;
int state;
state = 1;
t->name = t->extension = t->date = t->PPN = 0;
while (state) {
v = u;
while (!fend(*s)) *v++ = *s++;
*v = 0;
switch (state) {
case 1:
t->name = sixbit(u);
switch (*s) {
case 0:
state = 0;
break;
case '.':
state = 2;
break;
case '[':
state = 3;
break;
default:
return 0;
}
s++;
break;
case 2:
t->extension = sixbit(u) & 0777777000000;
switch (*s) {
case 0:
state = 0;
break;
case '[':
state = 3;
break;
default:
return 0;
}
s++;
break;
case 3:
t->PPN = rightjust(sixbit(u));
switch (*s) {
case ',':
state = 4;
break;
default:
return 0;
}
s++;
break;
case 4:
t->PPN += (rightjust(sixbit(u)) >> 18);
state = 0;
break;
default:
return 0;
}
}
return 1;
}
static fend(c)
{
if (c == 0 || c == '.' || c == '[' || c == ',' || c == ']') return 1;
return 0;
}
static rightjust(n)
{
if (n = n & 0777777000000) while (!(n & 077000000)) n = n >> 6;
return n;
}
#endif /* WAITS */
#if SYS_ITS
#include "sysits.h"
_ofile(ufx, name, umode, uprot)
char *name;
int umode,bsize;
{ int fd, res, modchn;
char *bp;
if((modchn = getchn()) < 0)
return(-1); /* No ITS chans available */
switch(umode)
{ case 0: /* Read */
break; /* Leave LH zero (.UAI) */
case 1: /* Write */
modchn |= (1<<18); /* Set .UAO */
break;
case 2: /* Reading and Writing */
return(-1); /* Sorry, don't support this */
}
--name; /* Back up ptr to make proper BP */
res = SYSCAL2(SC_NAM("sopen"),SC_ARG(&modchn),SC_ARG(&name));
if(res) return(-1); /* Error of some kind */
_uioch[fd] = modchn&017;
return(fd);
}
#endif /* ITS */
#endif /* Commented-out stuff */