Files
open-simh.simtools/extracters/rstsflx/fileio.c
Paul Koning bf7c17ab4a Add RSTSFLX V2.6. This is a file system access utility for RSTS
file systems.  It supports reading and writing as well as a number
of other operations, such as octal dump, file system initialize,
and file system check ("clean").

This was originally maintained as a Subversion repository at
svn://akdesign.dyndns.org/flx/branches/V2.6.
as suggested by Timothe Litt on the SIMH mailing list.
2016-04-27 15:00:42 -04:00

593 lines
17 KiB
C

/* subroutines to do rsts file (virtual block) I/O */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "flx.h"
#include "fldef.h"
#include "fileio.h"
#include "filename.h"
#include "diskio.h"
#include "fip.h"
long totalbytes; /* total bytes transferred for get/put */
long curvbn; /* current file vbn (0-based) */
word curre; /* link of current RE */
word lastre; /* and last one seen */
int curreoff; /* offset into current RE */
int cluoff; /* offset into current file cluster */
int dcnperfcs; /* fcs / dcs */
static int rmseof (firqb *f, char *recp, long iocount)
{
long curblk;
int curbyt;
curblk = curvbn + (recp - iobuf - iocount) / BLKSIZE;
curbyt = (recp - iobuf) % BLKSIZE;
if (curblk > f->eofblk) return (TRUE);
return (curblk == f->eofblk && curbyt >= f->eofbyte);
}
/* in each of the following "get a record" routines (one for each record
* format defined for RMS-11) the arguments are:
* f pointer to firqb struct
* len pointer to record length return variable
* eor pointer to end of record return variable (flag)
* iocount amount of data in I/O buffer (from last seqio call)
*
* len is set to the length of the record or partial record returned.
* the pointer to the record is the return value of the function.
* eor is set true if a whole record was transferred, and false if only
* a partial record is returned. (If eor is set, then a line delimiter
* should be added by the caller if it is not contained in the record
* itself.)
* If nothing was transferred (i.e., the caller should retry) then
* these functions return NULL. Otherwise they return a record pointer.
* Note that in the latter case the length may be zero, which means
* the record was an empty line.
*/
static char *getfix (firqb *f, long *len, int *eor, long iocount)
{
long left; /* amount left in I/O buffer */
char *recp;
recp = f->currec;
left = &iobuf[iocount] - recp; /* compute what's left */
*eor = FALSE; /* default to not EOR */
if (left <= 0 || rmseof (f, recp, iocount)) {
*len = 0;
f->currec = NULL;
return (NULL);
}
if (left < f->recsiz) {
*len = left;
f->currec = NULL;
return (recp);
}
*len = f->recsiz;
f->currec += f->recsiz;
*eor = TRUE;
return (recp);
}
static char *getvar (firqb *f, long *len, int *eor, long iocount)
{
char *recp;
word16 reclen;
word16 *reclenp;
long left;
recp = f->currec;
*eor = FALSE; /* default to not EOR */
left = (&iobuf[iocount] - recp);
if (left <= 0 || rmseof (f, recp, iocount)) {
*len = 0;
f->currec = NULL;
f->currecsiz = 0;
return (NULL);
}
if ((reclen = f->currecsiz) == 0) {
reclenp = (word16 *)recp;
reclen = *reclenp;
if (reclen == 0xffff) { /* end of data in block */
left &= -BLKSIZE; /* get what's left in remaining blocks */
if (left == 0) {
*len = 0;
f->currec = NULL;
f->currecsiz = 0;
return (NULL);
} else {
recp = &iobuf[iocount - left];
reclenp = (word16 *) recp;
reclen = *reclenp;
}
}
left -= 2; /* discount length field */
recp += 2; /* and skip it */
}
if (reclen > left) {
*len = left;
f->currecsiz = reclen - left; /* do this next time */
f->currec = NULL;
return (recp);
}
f->currec = recp + UP(reclen,2); /* point to where next count is */
f->currecsiz = 0; /* no partial record left to do */
*len = reclen;
*eor = TRUE;
return (recp);
}
static char *getvfc (firqb *f, long *len, int *eor, long iocount)
{
char *recp;
word16 reclen;
word16 *reclenp;
long left, skip;
recp = f->currec;
*eor = FALSE; /* default to not EOR */
left = (&iobuf[iocount] - recp);
if ((reclen = f->currecsiz) != 0) skip = f->recskip;
else {
reclenp = (word16 *)recp;
reclen = *reclenp;
if (reclen == 0xffff) { /* end of data in block */
left &= -BLKSIZE; /* get what's left in remaining blocks */
if (left == 0) {
*len = 0;
f->currec = NULL;
f->currecsiz = 0;
return (NULL);
} else {
recp = &iobuf[iocount - left];
reclenp = (word16 *) recp;
reclen = *reclenp;
}
}
left -= 2; /* discount length field */
recp += 2; /* and skip it */
skip = f->rechdrsiz; /* skip over whole header */
}
if (reclen > left) {
f->currecsiz = reclen - left; /* do this next time */
if (left > skip) {
*len = left - skip;
f->recskip = 0; /* nothing to skip */
f->currec = NULL;
return (recp + skip);
} else {
*len = 0;
f->recskip = skip - left;
return (NULL);
}
}
f->currec = recp + UP(reclen,2); /* point to where next count is */
f->currecsiz = 0; /* no partial record left to do */
*len = reclen - skip;
*eor = TRUE;
return (recp + skip);
}
static char *getstm (firqb *f, long *len, int *eor, long iocount)
{
char *lfpos;
char *recp;
*eor = FALSE; /* default to not EOR */
while (f->currec < &iobuf[iocount])
if (*(f->currec)) break;
else f->currec++; /* skip nulls */
if (f->currec == &iobuf[iocount]) {
*len = 0;
f->currec = NULL;
return (NULL);
}
lfpos = (char *) memchr (f->currec, 012, &iobuf[iocount] - f->currec);
recp = f->currec;
if (lfpos == NULL || lfpos == f->currec) {
*len = &iobuf[iocount] - f->currec;
*eor = FALSE;
f->currec = NULL;
} else {
if (*(lfpos - 1) == '\015') *len = lfpos - f->currec - 1;
else *len = lfpos - f->currec;
*eor = TRUE;
f->currec = lfpos + 1;
}
return (recp);
}
/* note that a partial record may be returned; if so, eor is set to false */
static char *getrec (firqb *f, long *len, int *eor, long iocount) /* get next text record */
{
switch (f->recfmt & fa_rfm) {
case rf_udf:
case rf_stm:
return (getstm (f, len, eor, iocount));
case rf_fix:
return (getfix (f, len, eor, iocount));
case rf_var:
return (getvar (f, len, eor, iocount));
case rf_vfc:
return (getvfc (f, len, eor, iocount));
}
rabort (INTERNAL); /* should never get here */
return (NULL); /* to make the compiler happy */
}
/* Routine to do sequential I/O, either read or write according to the
* third argument passed.
*/
long seqio (firqb *f, long iolen, iohandler io, void *buffer)
{
long startlbn; /* lbn at which to start transfer */
long count; /* and byte count */
word prevdcn = 0; /* previous RE entry */
if (curvbn >= f->size) return (0); /* nothing left */
for (count = 0; ; ) {
if (cluoff == 0) {
if (curreoff == 0 && (f->stat & us_ufd) == 0) {
if (!readlk (curre)) rabort(BADRE);
lastre = curre;
}
if (count != 0) {
if (f->stat & us_ufd) {
if (clumap->uent[curreoff] - prevdcn != dcnperfcs)
break;
} else {
if (use(ufdre,k)->uent[curreoff] - prevdcn != dcnperfcs)
break;
}
}
if (sw.debug != 0)
printf ("seqio() RE entry %o\n", use(ufdre,k)->uent[curreoff]);
}
if (count++ == 0) {
if (f->stat & us_ufd)
startlbn = dcntolbn(clumap->uent[curreoff])
+ cluoff;
else startlbn = dcntolbn(use(ufdre,k)->uent[curreoff])
+ cluoff;
if (sw.debug != NULL)
printf ("seqio() start lbn %lo\n", startlbn);
}
/* contiguous file is treated as having one giant cluster... */
if (++cluoff == f->clusiz && ((f->stat & us_nox) == 0)) {
cluoff = 0;
prevdcn = use(ufdre,k)->uent[curreoff];
curreoff++; /* on to the next RE entry */
if (curreoff == 7) { /* time to read anothe RE */
if (f->stat & us_ufd) curre = 0;
else curre = use(ufdre,k)->ulnk;
curreoff = 0;
}
}
if (count == iolen / BLKSIZE || curvbn + count == f->size) break;
}
(*io) (startlbn, count * BLKSIZE, buffer);
curvbn += count; /* account for what we transferred */
return (count * BLKSIZE); /* and return the byte count */
}
void openfile (firqb *f) /* set up file I/O at VBN 0 */
{
curvbn = 0; /* currently at first block */
lastre = 0; /* working on first RE */
curre = f->rlink; /* set current RE link */
curreoff = 0; /* working on RE entry 0 */
cluoff = 0; /* and start of that cluster */
/* note: on big disks (dcs > 16) dcnperfcs will end up 0 for directories.
* that's fine; the result is that no transfer that crosses retrieval
* entries will be done in a single I/O -- exactly what we want.
*/
dcnperfcs = f->clusiz / dcs; /* how many dcn's in file cluster */
if (f->size) /* read first RE if non-null */
if ((f->stat & us_ufd) == 0 && !readlk (f->rlink))
rabort(BADRE);
}
word *relist = NULL; /* list of pointers to RE's */
void initrandom (firqb *f)
{
long recount;
if (relist != NULL) free (relist);
if (f->stat & us_ufd) return; /* UFDs are easy */
openfile (f); /* first do common setup */
/* Note: do not use UP() here since we don't round to
* a power of 2!
*/
recount = (f->size + 7 * f->clusiz - 1) / (7 * f->clusiz);
if ((relist = (word *) malloc (recount * sizeof (word))) == NULL)
rabort(NOMEM);
memset (relist, 0, recount * sizeof (word));
relist[0] = f->rlink;
}
void fileseek (firqb *f, long vbn) /* seek to (0-based) vbn */
{
int clu, re, renum;
word prevre;
if (vbn >= f->size) rabort(INTERNAL);
clu = vbn / f->clusiz; /* get cluster number */
if ((f->stat & us_ufd) == 0) { /* more work for non-UFDs */
renum = clu / 7; /* get RE number */
if (relist[renum] == 0) { /* load RE list if we haven't been here */
for (re = 0; re <= renum; re++) {
if (relist[re]) prevre = relist[re];
else {
if (!readlk (prevre)) rabort(CORRUPT);
relist[re] = prevre = use(ufdre,k)->ulnk;
}
}
}
readlk (curre = relist[renum]); /* read appropriate RE */
}
curreoff = clu % 7; /* set index into RE */
cluoff = vbn % f->clusiz; /* and offset into cluster */
curvbn = vbn; /* and finally, current vbn */
}
/* get a RSTS file and copy it to a specified local file. Transfers in
* binary (block) mode or ascii (record) mode according to the third
* argument. The return value is the count of bytes transferred.
*/
long getfile (FILE *to, firqb *f, int binary)
{
long reclen, iocount;
int eor;
char *recp;
if (f->size == 0) return (0); /* null file, nothing transferred */
openfile (f); /* set up file transfer */
totalbytes = 0;
if (binary) {
while ((iocount = seqio (f, iobufsize, rread, iobuf)) != 0) {
fwrite (iobuf, 1, iocount, to);
totalbytes += iocount;
}
} else {
iocount = seqio (f, iobufsize, rread, iobuf); /* do initial buffer fill */
f->currec = iobuf; /* init current record pointer */
f->currecsiz = 0; /* no current record size */
while (TRUE) {
recp = getrec (f, &reclen, &eor, iocount);
if (recp != NULL) {
totalbytes += reclen;
if (reclen) fwrite (recp, 1, reclen, to);
if (eor) {
fputc ('\n', to);
#if (defined(__MSDOS__) && !defined(__unix__))
totalbytes += 2;
#else
totalbytes++;
#endif
}
}
if (f->currec == NULL) {
if ((iocount = seqio (f, iobufsize, rread, iobuf)) == 0)
break;
else f->currec = iobuf; /* init current record pointer */
}
}
}
return (totalbytes);
}
/* extend the currently open file to the specified size, if not already
* that big.
*/
long extfile (firqb *f, long blocks)
{
int clus, reoff, clunum;
word re;
if (sw.debug != NULL)
printf ("extfile(,%ld) from %ld\n", blocks, f->size);
if (f->size >= blocks) return (TRUE); /* already big enough */
if (f->stat & us_nox) return (FALSE); /* error if contiguous */
clus = (UP(blocks,f->clusiz) - UP(f->size,f->clusiz)) / f->clusiz;
if (clus == 0) { /* no new clusters needed */
f->size = blocks; /* so just do it */
return (TRUE);
}
if (cluoff == 0) reoff = curreoff;
else {
reoff = curreoff + 1;
if (reoff > 6) reoff = 0; /* overflowed current RE */
}
f->size = UP(f->size,f->clusiz); /* round up to full cluster */
while (clus > 0) { /* allocate what we need */
if (reoff == 0) { /* need to start new RE */
if ((re = getent ()) == 0) return (FALSE);
if (NULLINK(curre)) curre = re; /* this is now current */
readlk (re); /* read it */
use(ufdre,k)->ulnk = 1; /* mark it allocated */
MARKF;
if (lastre) { /* link it to earlier RE */
readlk (lastre);
use(ufdre,k)->ulnk = re;
} else { /* link first RE to NE */
readlk (f->nlink);
use(ufdne,k)->uar = re;
f->rlink = re; /* record in firqb also */
}
MARKF;
lastre = re; /* this is now the last RE */
readlk (re); /* read the new RE */
}
if ((clunum = getclu (f->clusiz, f->clusiz)) == 0) return (FALSE);
use(ufdre,k)->uent[reoff] = clunum;
MARKF;
f->size += f->clusiz; /* adjust file size */
reoff++;
if (reoff > 6) reoff = 0; /* overflowed current RE */
clus--; /* any clusters left to do? */
}
f->size = blocks; /* it worked, all done */
readlk (curre); /* make sure we have the right RE */
return (TRUE); /* return with success */
}
/* read a local file and put it to a specified RSTS file. Transfers in
* binary (block) mode or ascii (record) mode according to the third
* argument. The return value is the count of bytes transferred if the
* copy completed, or the negative of what was transferred if the transfer
* did not finish (i.e., due to no room on the disk).
*/
/* writefile is a support routine that writes some number of bytes from iobuf
* to the rsts output file. It returns FALSE if the write did not finish, e.g.,
* due to lack of disk space.
*/
long writefile (firqb *f, long count)
{
long offset, blocks, wcount;
if (sw.debug != NULL) printf ("writefile(,%ld)\n", count);
count = UP(count,BLKSIZE);
offset = 0;
blocks = (totalbytes + count) / BLKSIZE;
extfile (f, blocks); /* extend if needed */
while (count) {
wcount = seqio (f, count, rwrite, iobuf + offset);
if (wcount == 0) {
totalbytes = -totalbytes;
return (FALSE);
}
offset += wcount;
totalbytes += wcount;
count -= wcount;
}
return (TRUE); /* write suceeded */
}
long putfile (FILE *from, firqb *f, int binary)
{
long blocks, wcount, iocount, iocount2, adjust;
long oldsize, offset;
long ilen, llen;
char *iptr, *eol;
ufdne *n;
ufdae *a;
openfile (f); /* set up file transfer */
totalbytes = 0;
if (binary) {
while ((iocount = fread (iobuf, 1, iobufsize, from)) != 0) {
iocount2 = UP(iocount, BLKSIZE);
adjust = iocount2 - iocount;
if (adjust) memset (iobuf + iocount, 0, adjust);
if (!writefile (f, iocount2)) break;
if (adjust) {
totalbytes -= adjust;
break; /* short read, done with file */
}
}
} else {
iptr = iobuf; /* start getting lines at the start */
ilen = iobufsize + 2; /* use all including 2 extra bytes */
for ( ; ; ) {
if (fgets (iptr, ilen, from) == NULL) break;
llen = strlen (iptr); /* get length of this line */
eol = iptr + llen; /* point to terminator */
if (*--eol == '\n') { /* if we got a complete line */
if (*(eol - 1) == '\r') { /* terminator = cr-lf? */
eol++; /* next line starts here */
} else { /* only \n at eol */
*eol++ = '\r'; /* put in the cr */
*eol++ = '\n'; /* and overwrite null with lf */
llen ++; /* account for cr */
}
} else eol++; /* skip to end of data */
iptr = eol; /* advance read ptr */
ilen -= llen; /* compute space left */
if (ilen <= 2) { /* full buffer */
if (!writefile (f, iobufsize)) {
ilen = 0; /* don't write more */
break; /* quit this file */
}
iptr = iobuf; /* reinit pointer */
ilen = 2 - ilen; /* # bytes to move */
eol = iobuf + iobufsize; /* move from here */
for ( ; ilen > 0; ilen--) *iptr++ = *eol++;
ilen = iobufsize + 2 - (iptr - iobuf);
}
}
ilen = iptr - iobuf; /* get amount buffered */
adjust = UP(ilen,BLKSIZE) - ilen; /* get amount to pad */
if (adjust) memset (iptr, 0, adjust); /* do it */
iptr += adjust; /* point past it */
if (iptr != iobuf) writefile (f, iptr - iobuf); /* write pending data */
totalbytes -= adjust; /* don't report pad */
}
readlk (f->alink); /* read file's AE */
a = use(ufdae,k);
oldsize = a->usiz; /* get old file size */
if (a->urts[0] == 0) oldsize += a->urts[1] >> 16;
if (f->size != oldsize ) { /* size changed */
if (f->size < oldsize) rabort(INTERNAL); /* can't shrink */
updqb (f, UP(f->size,f->clusiz) - UP(oldsize,f->clusiz));
a->usiz = f->size & 0xffff;
MARKF;
if (f->size >> 16) { /* large file */
a->urts[1] = f->size >> 16;
if ((oldsize >> 16) == 0) { /* it was small */
if (a->urts[0]) {
printf ("Warning - RTS name cleared for large file ");
printcurname (f);
printf ("\n");
a->urts[0] = 0;
}
readlk (f->nlink);
n = use(ufdne,k);
if (n->uprot & up_run) {
n->uprot &= ~up_run;
MARKF;
printf ("Warning - runnable protection bit cleared for large file ");
printcurname (f);
printf ("\n");
}
}
}
}
return (totalbytes);
}
/* return TRUE if the filename has an extension that suggests it's a text
* file, and FALSE otherwise.
*/
const char textlist[][4] =
{ "txt", "lst", "map", "sid", "log", "lis",
"rno", "doc", "mem", "bas", "b2s", "mac",
"for", "ftn", "fth", "cbl", "dbl", "com",
"cmd", "bat", "tec", "ctl", "odl", "ps ",
"c ", "h ", "ps", "c", "h", "src",
"alg", "" };
int textfile (char *name)
{
char *p;
int i;
p = strchr (name, '.');
if (p == NULL) return (FALSE); /* no extension */
p++; /* skip past the dot */
for (i = 0; textlist[i][0] != '\0'; i++)
if (strcmp (textlist[i], p) == 0) return (TRUE);
return (FALSE);
}