1
0
mirror of https://github.com/PDP-10/klh10.git synced 2026-01-11 23:52:54 +00:00
2015-04-27 22:59:12 +02:00

1537 lines
43 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Program to read Tops-20 Dumper format tapes
*
* Jim Guyton, Rand Corporation
* Original 10/20/82
* jdg: -n added 6/11/83
* jdg: can now extract 8-bit-byte files 2/9/86
*
* Lot of mods by Jay Lepreau, Univ of Utah, 1-2/87.
* More mods by Ken Harrenstien, 1992-98.
*/
/* KLH To-do:
Figure out some convention for file numbering like ss.nnn which
also accomodates designating savesets by sequential numbering from
start of dump (rather than using the saveset # in block header, which
is always 0 most of the time)
Perhaps add an interactive browse capability similar to KRSTOR.
Perhaps use a semi-permanent directory generated by initial scan.
*/
#ifdef __STDC__ /* Some compilers can't even define this correctly! */
# define READ20_STDC __STDC__
#else
# define READ20_STDC 0
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <time.h> /* For ctime */
#include <stdlib.h> /* For malloc */
#include <string.h> /* For strchr, strstr, memcpy, etc */
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#if READ20_STDC
# include <stdarg.h> /* For punt() */
#endif
#include "dump.h"
#define LOGFILE "Logfile" /* logfile should be changeable */
char *unixname();
long getfield();
time_t unixtime();
#if READ20_STDC
void punt(int, char *, ...);
#else
void punt();
#endif
int fdTape; /* File handle for Dumper-20 format tape */
int tbrecsiz; /* Tape buffer record size */
int tblen; /* Tape buffer size (max recsize) */
char *tbuffer; /* Tape buffer location */
long nblktape = 0; /* # blocks read on entire tape so far */
long nblkfile = 0; /* # blocks read in current tape file */
long nblksset = 0; /* # blocks read in current saveset */
char *prevblock = 0;
int firstblock = 0; /* We are reading first block of a file */
FILE *fpFile; /* Output file handle on extracts */
int debug = 0;
int default_bytesize = 36; /* Default byte size for binary files */
int wordflg = 0; /* Set 1 to force 36-bit size for all files */
int numflg = 0; /* Non-zero if using numeric filenames */
int keepcr = 0; /* Keep CR's in CRLF pairs in text files */
int dodir = 0; /* directory listing */
int xflg = 0; /* extract */
int verbose = 0;
int fdbflg = 0; /* Show FDB contents for files listed/extracted*/
int genflg; /* keep generation number */
int nselect; /* number of files still to be selected by number */
int doallflag; /* act on all files cause no args given */
int qicflg = 0; /* Non-zero if hacking QIC tape */
long tlocseek = 0; /* Hack: tapeloc to seek to (-s) */
int showtloc = 0; /* Non-zero to show tapelocs on listing */
int number; /* Current output file "number" */
#define TAPE "/dev/rmt8" /* Default input tape */
enum { OC_ASCII, OC_8BIT, OC_WORD, OC_PAGE }
outmode; /* File output conversion mode */
int outnfbpw; /* # of file bytes per word (0 if bytesize 0) */
int outnfw; /* # of file words remaining (set from fdb) */
int outnbpiw; /* # output bytes per input 36-bit file word */
int outnbleft; /* # of output bytes left to go */
int bytesize; /* FDB: Number of bits/byte in current file */
long numbytes; /* FDB: Number of bytes in current file */
int pgcount; /* FDB: Number of twenex pages in file */
long tapefmt; /* DUMPER tape format from saveset header */
long tapeno, ssno; /* Tape # and Saveset # from saveset header */
long pageno, filenum;
unsigned tprot; /* Tops-20 protection */
time_t timep[2];
#define atime (timep[0])
#define mtime (timep[1])
int offline, archived, invisible;
int apgcount, tp1, tp2, ss1, ss2, tf1, tf2;
#define DUMPFNAMLEN (0200*5) /* Max length of dumped filename */
char topsname[DUMPFNAMLEN];
char sunixname[300];
struct want {
unsigned short ssnum;
unsigned short fnum;
} want[10000]; /* limited by 20000 char arglist */
int cursswant;
int compwant();
#define CONTAINS_BOF 1 /* Block contains the beginning of a file */
#define CONTAINS_EOF 2 /* Block contains the end of a file */
#define NUL_AT_END 4 /* Put a null at the end */
#define STRING (CONTAINS_BOF|CONTAINS_EOF|NUL_AT_END) /* Make strings easy */
char **patterns = 0; /* Filename match patterns */
int numpats = 0; /* Number of patterns */
char *expression = 0;
char *re_comp_error; /* Error message from re_comp() */
extern char *re_comp();
void scan();
char helpstr[] = "\
Usage: read20 [switches] [patterns]\n\
Switches must be separated.\n\
Patterns are simple substrings of the filenames to select.\n\
-f <file> Specify tapefile. '-' uses stdin. Default is /dev/rmt8\n\
-x Extract files\n\
-t List contents (one of -t or -x must be given)\n\
-tl Show tape locations in listing\n\
-S <n> Only process saveset <n>\n\
-e <regexp> Only process filenames matching <regexp> (one -e only)\n\
-F <n> ... Only process files numbered <n> ...\n\
-V Show FDB info for files extracted or listed\n\
-s <loc> Start reading at this byte loc in tapefile (seeks)\n\
-q Say using QIC (1/4\") cartridge tape\n\
-v Verbose feedback\n\
-g Keep generation # in extracted filename\n\
-n <n> Use numeric filenames for extracts, starting with <n>\n\
-W Treat all files as 36-bit. Otherwise, 7-bit files\n\
are treated as ascii, 8-bit as 8-bit binary, and\n\
all others as 36-bit (direct copy of tape data).\n\
-T Treat 0 or 36-bit files as 7-bit ascii\n\
-B Treat 0 or 36-bit files as 8-bit binary\n\
-c Keep CRs in CRLF pairs for ascii files\n\
-d<n> Debug level (>0,>5,>10,>99) (default 0)\n\
";
main(argc, argv)
int argc;
char *argv[];
{
char *tape = TAPE; /* Pathname for tape device/file */
/* Do switch parsing */
signal(SIGINT, exit); /* Allow ^C to cause profiling to finish */
while(argc>1 && argv[1][0] == '-'){
switch(argv[1][1]){
case 's': /* Seek to byte (temporary hack) */
if (argc <= 2)
punt(0, "Need byteloc after -s\n%s", helpstr);
tlocseek = atoi(argv[2]);
argc--; argv++;
break;
case 'f':
if (argc <= 2)
punt(0, "Need filename after -f\n%s", helpstr);
tape = argv[2];
argc--; argv++;
break;
case 'W': /* Force all files to 36-bit word bytes */
wordflg = 1;
break;
case 'T': /* Force 0/36-bit files to 7-bit ascii */
default_bytesize = 7;
break;
case 'B': /* Force 0/36-bit files to 8-bit bin */
default_bytesize = 8;
break;
case 't': /* directory listing */
if (argv[1][2] == 'l')
showtloc = 1;
else
dodir = 1;
break;
case 'x': /* extract */
xflg = 1;
break;
case 'q': /* Using QIC (1/4") tape */
qicflg = 1;
break;
case 'v': /* verbosity */
verbose++;
break;
case 'V': /* Output FDB info */
fdbflg++;
break;
case 'g': /* keep gen number */
genflg++;
break;
case 'd':
debug = atoi(&argv[1][2]);
fprintf(stderr, "Debug value set to %d\n", debug);
break;
case 'n': /* numeric output filenames */
if (argc <= 2)
punt(0, "Need number after -n\n%s", helpstr);
number = atoi(argv[2]); /* First file name */
numflg = 1;
argc--; argv++;
break;
case 'c': /* keep CR`s in CR/LF pairs */
keepcr++;
break;
case 'e': /* regular expression */
if (argc <= 2)
punt(0, "Need expression after -e\n%s", helpstr);
if (expression)
punt(0, "Only one -e regexp allowed");
expression = argv[2];
if ((re_comp_error = re_comp(expression)) != 0)
punt(0, "error in -e from re_comp: %s",
re_comp_error);
argc--; argv++;
break;
case 'S': /* selected save set number */
if (argc <= 2)
punt(0, "Need save set number after -S\n%s", helpstr);
cursswant = atoi(argv[2]);
argc--; argv++;
break;
case 'F': /* selected file numbers */
if (argc <= 2)
punt(0, "Need file number(s) after -F\n%s", helpstr);
for (argc -= 2, argv += 2;
argc && isdigit(**argv);
argc--, argv++, nselect++) {
want[nselect].ssnum = cursswant;
want[nselect].fnum = atoi(*argv);
}
argc += 2; argv -= 2;
break;
default:
punt(0, "unknown flag %s\n%s", argv[1], helpstr);
}
argc--; argv++;
}
if (!xflg && !dodir)
punt(0, "Need either '-x' or '-t' option.\n%s", helpstr);
if (argc > 1) {
patterns = &argv[1];
numpats = argc - 1;
}
doallflag = !(patterns || expression || nselect);
if (nselect)
qsort((char *)want, nselect, sizeof (struct want), compwant);
if (!strcmp(tape, "-")) /* stdin */
fdTape = 0;
else
fdTape = open(tape, 0); /* Open tape for read */
if (fdTape == -1)
punt(1, "Can't open 'tape' file %s", tape);
if (tlocseek) {
if (lseek(fdTape, tlocseek, 0) == -1)
punt(1, "Can't seek to %ld on 'tape' file %s",
tlocseek, tape);
}
tbrecsiz = TAPEBLK * 15; /* Default - use max block factor */
tblen = ((tbrecsiz+511)/512)*512; /* Round up to modulo-512 */
if (!(tbuffer = malloc(tblen)))
punt(1, "Can't allocate %d bytes for tape buffer", tblen);
scan(); /* Now scan tape */
}
void
scan()
{
char *tapeblock;
int rc;
int rtype;
long tloc = 0;
rc = 0;
for ( ; ; ) /* Loop till end of tape */
{
/*** Read a block ***/
if (rc == 0) {
rc = getrec(fdTape, tbuffer, (qicflg ? tblen : tbrecsiz));
if (rc > 0)
tloc += rc;
if (qicflg && rc > 0) {
if (rc == tblen) rc = tbrecsiz;
else punt(1, "Ugh, QIC rec %d long, shd be %d",
rc, tblen);
}
if (debug > 99)
printf("rc=%d\n", rc);
if ((rc % (518*5)) != 0) {
if (rc != 0)
punt(1, "Oops. Read block len = %d", rc);
}
if (rc == 0) {
if (verbose)
printf("\nEnd of tape.\n");
exit(0); /* Normal exit */
}
tapeblock = tbuffer;
rc = rc - 518*5;
}
else {
tapeblock = tapeblock + 518*5;
rc = rc - 518*5;
}
prevblock = 0;
/*** Do something with it ***/
switch(rtype = -getfield(tapeblock,
WdoffRectype, BtoffRectype, BtlenRectype))
{
case RectypeData: /* Data block */
doDatablock(tapeblock);
break;
case RectypeTphd: /* Saveset header */
nblksset = 0; /* Start of saveset */
doSaveset(tapeblock, 0);
break;
case RectypeFlhd: /* File header */
doFileHeader(tapeblock);
break;
case RectypeFltr: /* File trailer */
doFileTrailer(tapeblock);
break;
case RectypeTptr: /* Tape trailer */
doTapeTrailer(tapeblock);
break;
case RectypeUsr: /* User directory info ? */
if (verbose /* >= 3 */)
fprintf(stderr, "Directory block skipped\n");
break;
case RectypeCtph: /* Continued saveset hdr */
nblksset = 0; /* Start of saveset */
doSaveset(tapeblock, 1);
break;
case RectypeFill: /* Fill block */
if (verbose /* >= 3 */)
fprintf(stderr, "Fill block skipped\n");
break;
case RectypeTonext: /* FMTV6 - to next tape */
doTonext(tapeblock);
break;
default:
fprintf(stderr, "Unknown block type 0x%x, tapeloc %ld.\n",
rtype, tloc - rc);
if (debug == 0) /* Unless debugging, */
punt(0, "Aborting."); /* stop now */
break;
}
/* Done with record, bump counts */
nblktape++;
nblkfile++;
nblksset++;
}
}
int masks[32] = /* bitmasks for different length fields */
{ 0x00000001, 0x00000003, 0x00000007,
0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
0xffffffff
};
long
getfield(block, wordoff, bitoff, bitlen)
char *block; /* Tape block */
int wordoff; /* 36-bit word offset */
register int bitoff; /* Bit offset of field (from msb) */
register int bitlen; /* Bit length of field */
{
register char *p; /* Used to point into block */
register long w32; /* First 32 bits of the 36 bit word */
int w4; /* Last 4 bits of the 36 bit word */
long w = 0; /* the word to return */
/* First, the "illegal" kludge */
if (bitoff == 0 && bitlen == 36) {
bitoff = 4;
bitlen = 32;
}
if (bitlen > 32)
punt(0, "Can't get that large a field = %d!", bitlen);
/* A PDP-10 (or 20) 36-bit word is laid out with the first 32 bits
as the first 4 bytes and the last 4 bits are the low order 4 bits
of the 5th byte. The high 4 bits of that byte should be zero */
p = block + (5*wordoff); /* Get ptr to word of interest */
w32 = *p++ & 0377; /* First byte */
w32 = (w32 << 8) | (*p++ & 0377); /* 2nd */
w32 = (w32 << 8) | (*p++ & 0377); /* 3rd */
w32 = (w32 << 8) | (*p++ & 0377); /* 4th */
w4 = *p; /* 5th */
if (w4 > 017)
punt(0, "Not a PDP-10 tape! w4 = octal %o", w4);
/* Get the field right justified in the word "w".
There are three cases that I have to handle:
[1] field is contained in w32
[2] field crosses w32 and w4
[3] field is contained in w4
*/
if (bitoff+bitlen <= 32) /* [1] field is contained in w32 */
{
w = w32 >> (32 - (bitoff+bitlen));
}
else if (bitoff <= 32) /* [2] field crosses boundary */
{
w = (w32 << (bitoff+bitlen-32))
| (w4 >> (36 - (bitoff+bitlen)));
}
else /* [3] field is contained in w4 */
{
w = w4 >> (36 - (bitoff+bitlen));
}
w = w & masks[bitlen-1]; /* Trim to proper size */
return(w);
}
/* FDB extraction stuff
** by KLH, 1992.
*/
struct fdbent {
char *fe_name; /* Symbol name */
int fe_typ; /* Value type (FBT_xxx) */
int fe_woff; /* Word offset */
int fe_boff; /* Bit offset (per MACRO, from hi-order bit) */
int fe_bits; /* Bits ((0 || 36) == full word) */
char *fe_show; /* Text to use when listing info */
long fe_lh; /* Extracted LH value */
long fe_rh; /* Extracted RH value */
long fe_val; /* Extracted value (what fits in a long) */
char *fe_str; /* String value (if one) */
time_t fe_tim; /* TAD converted to Unix timeval */
};
char festr_fname[DUMPFNAMLEN+1]; /* To hold extracted filename string */
char *festr_attrs; /* Pointer to first fn attribute if any */
char festr_aut[10*5+1]; /* To hold extracted author string */
char festr_lwr[10*5+1]; /* To hold extracted last-writer string */
enum fdbtyp {
FBT_V8, /* Octal value */
FBT_V10, /* Decimal value */
FBT_H8, /* Two octal halfwords */
FBT_H10, /* Two decimal halfwords */
FBT_FLG, /* Boolean flag */
FBT_TAD, /* T20 TAD value */
FBT_TOD, /* T20 TAD value, or interval in days */
FBT_AUT, /* Author string - at FDBend */
FBT_LWR, /* Last-Writer string - at FDBend+010 */
FBT_ACT, /* Account string - in filename */
FBT_6BT /* Possible SIXBIT value */
};
#define fedef(nam,typ,wo,bo,bs,tx) {nam,typ,wo,bo,bs,tx}
#define FBLN0 030 /* DUMPER only saves this many actual FDB words */
/* NOTE!!!! FMTV6 DUMPER writes out 037 FDB words, so all the offsets
** need to be changed if reading a format 6 tape...
*/
#define FDBAUT_Woff (WdoffFDB+FBLN0)
#define FDBLWR_Woff (WdoffFDB+FBLN0+010)
#define FDBARCF (FBLN0+010+010) /* Offset in "DumperFDB" of ARCF blk */
#define FDBARC_Woff (WdoffFDB+FDBARCF)
/* Contents of ARCF block as stored by DUMPER */
#define ARCF_AROFL 0 /* Useless flags to ARCF% */
#define ARCF_ARTP1 1 /* == .FBTP1 */
#define ARCF_ARSF1 2 /* == .FBSS1 */
#define ARCF_ARTP2 3 /* == .FBTP2 */
#define ARCF_ARSF2 4 /* == .FBSS2 */
#define ARCF_ARODT 5 /* == .FBTDT */
#define ARCF_ARPSZ 6 /* == AR%PSZ in .FBBBT */
struct fdbent fdbt[] = {
fedef(".FBHDR",FBT_H8, 0, 0,36,"Header word"),
fedef("FB%LEN",FBT_V8, 0,35, 7,"Length of FDB"),
fedef(".FBCTL",FBT_H8, 1,35,36,"Status bits"),
fedef("FB%TMP",FBT_FLG, 1, 0, 1,"File is temporary"),
fedef("FB%PRM",FBT_FLG, 1, 1, 1,"File is permanent"),
fedef("FB%NEX",FBT_FLG, 1, 2, 1,"File doesn't really exist"),
fedef("FB%DEL",FBT_FLG, 1, 3, 1,"File is deleted"),
fedef("FB%NXF",FBT_FLG, 1, 4, 1,"File is not yet closed"),
fedef("FB%LNG",FBT_FLG, 1, 5, 1,"File is longer than 512 pages"),
fedef("FB%SHT",FBT_FLG, 1, 6, 1,"DEC-reserved bit"),
fedef("FB%DIR",FBT_FLG, 1, 7, 1,"File is a directory"),
fedef("FB%NOD",FBT_FLG, 1, 8, 1,"File is not to be backed up"),
fedef("FB%BAT",FBT_FLG, 1, 9, 1,"File may have bad pages"),
fedef("FB%SDR",FBT_FLG, 1,10, 1,"Directory has subdirectories"),
fedef("FB%ARC",FBT_FLG, 1,11, 1,"File has archive status"),
fedef("FB%INV",FBT_FLG, 1,12, 1,"File is invisible"),
fedef("FB%OFF",FBT_FLG, 1,13, 1,"File is offline"),
fedef("FB%FCF",FBT_V8, 1,17, 4,"File class field"),
fedef("FB%NDL",FBT_FLG, 1,18, 1,"File is not to be deleted"),
fedef("FB%WNC",FBT_FLG, 1,19, 1,"File last write not closed"),
fedef("FB%FOR",FBT_FLG, 1,20, 1,"File has FORTRAN-style LPT chars"),
fedef(".FBEXL",FBT_V8, 02,35,36,"Link to FDB of next file type"),
fedef(".FBADR",FBT_H8, 03,35,36,"Disk address of index block"),
fedef(".FBPRT",FBT_H8, 04,35,36,"File access bits"),
fedef(".FBCRE",FBT_TAD, 05,35,36,"Time of last write"),
fedef(".FBAUT",FBT_AUT, 06,35,36,"Author of file"),
fedef(".FBGEN",FBT_H8, 07,35,36,"Generation and directory numbers"),
fedef("FB%GEN",FBT_V10, 07,17,18,"Generation number"),
fedef("FB%DRN",FBT_V8, 07,35,18,"Internal directory number"),
fedef(".FBACT",FBT_ACT,010,35,36,"Account designator"),
fedef(".FBBYV",FBT_H8, 011,35,36,"File I/O information"),
fedef("FB%RET",FBT_V10,011, 5, 6,"Retention count"),
fedef("FB%BSZ",FBT_V10,011,11, 6,"File byte size"),
fedef("FB%MOD",FBT_V10,011,17, 4,"Data mode written in"),
fedef("FB%PGC",FBT_V10,011,35,18,"Page count of file"),
fedef(".FBSIZ",FBT_V10,012,35,36,"Number of bytes in file"),
fedef(".FBCRV",FBT_TAD,013,35,36,"Creation time of file"),
fedef(".FBWRT",FBT_TAD,014,35,36,"Time of last user write"),
fedef(".FBREF",FBT_TAD,015,35,36,"Time of last nonwrite access"),
fedef(".FBCNT",FBT_H10,016,35,36,"Count of writes,,references"),
fedef(".FBBK0",FBT_H8, 017,35,36,"Backup word 1"),
fedef(".FBBK1",FBT_H8, 020,35,36,"Backup word 2"),
fedef(".FBBK2",FBT_H8, 021,35,36,"Backup word 3"),
fedef(".FBBBT",FBT_H8, 022,35,36,"Archive status bits"),
fedef("AR%RAR",FBT_FLG,022, 1, 1,"User requested archival"),
fedef("AR%RIV",FBT_FLG,022, 2, 1,"System requested migration"),
fedef("AR%NDL",FBT_FLG,022, 3, 1,"Don't delete contents"),
fedef("AR%NAR",FBT_FLG,022, 4, 1,"Resist migration"),
fedef("AR%EXM",FBT_FLG,022, 5, 1,"File is exempt from migration"),
fedef("AR%1ST",FBT_FLG,022, 6, 1,"First archive pass in progress"),
fedef("AR%RFL",FBT_FLG,022, 7, 1,"Restoral failed"),
fedef("AR%WRN",FBT_FLG,022,10, 1,"File off-line exp date approaching"),
fedef("AR%RSN",FBT_V8, 022,17, 3,"Offline reason"),
fedef("AR%PSZ",FBT_V10,022,35,18,"Page count when file went offline"),
fedef(".FBNET",FBT_TOD,023,35,36,"On-line expiration date"),
fedef(".FBUSW",FBT_H8, 024,35,36,"User-settable word"),
fedef(".FBGNL",FBT_H8, 025,35,36,"Address of FDB of next generation"),
fedef(".FBNAM",FBT_H8, 026,35,36,"Pointer to filename block"),
fedef(".FBEXT",FBT_H8, 027,35,36,"Pointer to file type block"),
/* FDB words beyond this are not actually saved by DUMPER, which instead
** uses various hacks to store the information in the rest of the block.
*/
fedef(".FBLWR",FBT_LWR,030,35,36,"Last writer to file"),
#if 0 /* Note .FBFET vanishes completely -- no equiv in DumperFDB. */
fedef(".FBTDT",FBT_TAD,031,35,36,"Date archived"),
fedef(".FBFET",FBT_TOD,032,35,36,"Offline expiration date"),
fedef(".FBTP1",FBT_6BT,033,35,36,"Tape ID for run 1"),
fedef(".FBSS1",FBT_H10,034,35,36,"Run 1 saveset number,,Tape file number"),
fedef(".FBTP2",FBT_6BT,035,35,36,"Tape ID for run 2"),
fedef(".FBSS2",FBT_H10,036,35,36,"Run 2 saveset number,,Tape file number"),
#endif
fedef(".AROFL",FBT_H8, FDBARCF+ARCF_AROFL,35,36,"ARCF% flags"),
fedef(".ARTP1",FBT_6BT,FDBARCF+ARCF_ARTP1,35,36,"Archive tape 1 ID"),
fedef(".ARSF1",FBT_H10,FDBARCF+ARCF_ARSF1,35,36,"Archive tape 1 saveset,,fileno"),
fedef(".ARTP2",FBT_6BT,FDBARCF+ARCF_ARTP2,35,36,"Archive tape 2 ID"),
fedef(".ARSF2",FBT_H10,FDBARCF+ARCF_ARSF2,35,36,"Archive tape 2 saveset,,fileno"),
fedef(".ARODT",FBT_TAD,FDBARCF+ARCF_ARODT,35,36,"Date archived"),
fedef(".ARPSZ",FBT_V10,FDBARCF+ARCF_ARPSZ,35,36,"File page count")
};
#define nfdbents ((sizeof fdbt)/(sizeof fdbt[0]))
fe_extract(blk, fe)
unsigned char *blk;
struct fdbent *fe;
{
int woff = WdoffFDB + fe->fe_woff;
switch (fe->fe_typ) {
case FBT_V8: /* Octal value */
case FBT_V10: /* Decimal value */
case FBT_FLG: /* Boolean flag */
fe->fe_val = getfield(blk, woff,
(fe->fe_boff + 1 - fe->fe_bits),
fe->fe_bits);
break;
case FBT_H8: /* Two octal halfwords */
case FBT_H10: /* Two decimal halfwords */
case FBT_6BT: /* Two SIXBIT halves */
fe->fe_lh = getfield(blk, woff, 0, 18);
fe->fe_rh = getfield(blk, woff, 18, 18);
break;
case FBT_TOD: /* T20 TAD value, or interval in days */
fe->fe_lh = getfield(blk, woff, 0, 18);
if (fe->fe_lh == 0) { /* If probably not a TAD, */
fe->fe_rh = getfield(blk, woff, 18, 18); /* get RH */
break;
} /* Else drop thru for TAD */
case FBT_TAD: /* T20 TAD value */
fe->fe_tim = unixtime(blk, woff);
break;
case FBT_AUT: /* Author string - stored elsewhere in dumped block */
fe->fe_lh = getfield(blk, woff, 0, 18);
fe->fe_rh = getfield(blk, woff, 18, 18);
getstring(blk, festr_aut, FDBAUT_Woff,
sizeof(festr_aut)-1,STRING);
fe->fe_str = festr_aut[0] ? festr_aut : NULL;
break;
case FBT_LWR: /* Last-Writer string - ditto */
fe->fe_lh = getfield(blk, woff, 0, 18);
fe->fe_rh = getfield(blk, woff, 18, 18);
getstring(blk, festr_lwr, FDBLWR_Woff,
sizeof(festr_lwr)-1,STRING);
fe->fe_str = festr_lwr[0] ? festr_lwr : NULL;
break;
case FBT_ACT: /* Account string - in filename */
fe->fe_lh = getfield(blk, woff, 0, 18);
fe->fe_rh = getfield(blk, woff, 18, 18);
{
char *ecp, *cp = festr_attrs;
while (cp) {
if (ecp = strchr(cp, ';')) /* Tie off string first */
*ecp++ = '\0';
if (*cp++ == 'A') /* If this is acct string, */
break; /* use it! */
cp = ecp; /* Else try to continue */
}
fe->fe_str = cp;
}
break;
default:
fprintf(stderr, "Internal error - Unknown FBT_ type: %d\n",
fe->fe_typ);
break;
}
}
fe_show(f, fe)
FILE *f;
struct fdbent *fe;
{
/* First check to see if value exists, and do nothing if zero */
switch (fe->fe_typ) {
case FBT_V8: /* Octal value */
case FBT_V10: /* Decimal value */
case FBT_FLG: /* Boolean flag */
if (!fe->fe_val) return;
break;
case FBT_H8: /* Two octal halfwords */
case FBT_H10: /* Two decimal halfwords */
case FBT_6BT: /* SIXBIT */
case FBT_TOD: /* T20 TAD value, or interval in days */
if (!fe->fe_lh && !fe->fe_rh) return;
break;
case FBT_TAD: /* T20 TAD value */
if (!fe->fe_tim) return;
break;
case FBT_AUT: /* Author string - stored elsewhere in dumped block */
case FBT_LWR: /* Last-Writer string - ditto */
case FBT_ACT: /* Account string - in filename */
if (!fe->fe_str) return;
break;
default:
fprintf(stderr, "Internal error - Unknown FBT_ type: %d\n",
fe->fe_typ);
return;
}
/* Now start printing out the value */
if (fe->fe_name[0] == '.') { /* Primary word def */
fprintf(f, "%2o %s - %s: ", fe->fe_woff, fe->fe_name, fe->fe_show);
} else { /* Secondary bit field */
fprintf(f, " %s (%oB%d) - %s: ",
fe->fe_name, masks[fe->fe_bits - 1], fe->fe_boff, fe->fe_show);
}
/* Now print the value itself */
switch (fe->fe_typ) {
case FBT_V8: fprintf(f, "%o", fe->fe_val); break; /* Octal */
case FBT_V10: fprintf(f, "%d.", fe->fe_val); break; /* Decimal */
case FBT_FLG: fprintf(f, "%s", fe->fe_val ? "Yes" : "No");/* Boolean */
break;
case FBT_H8: /* Two octal halfwords */
if (fe->fe_lh) fprintf(f, "%o,,", fe->fe_lh);
fprintf(f, "%o", fe->fe_rh);
break;
case FBT_H10: /* Two decimal halfwords */
if (fe->fe_lh) fprintf(f, "%d.,,", fe->fe_lh);
fprintf(f, "%d.", fe->fe_rh);
break;
case FBT_6BT: /* Two SIXBIT halves */
if (fe->fe_lh) fprintf(f, "%o,,", fe->fe_lh);
fprintf(f, "%o ==> ", fe->fe_rh);
if (hwd6show(f, fe->fe_lh)) /* If LH doesn't end, */
hwd6show(f, fe->fe_rh); /* show RH too */
break;
case FBT_TOD: /* T20 TAD value, or interval in days */
if (!fe->fe_lh) {
fprintf(f, "%d. days", fe->fe_rh);
break;
}
/* Fall thru to TAD case */
case FBT_TAD: /* Output TAD as "dd-Mon-yy hh:mm:ss" */
pftime(f, &fe->fe_tim);
break;
case FBT_AUT: /* Author string - stored elsewhere in dumped block */
case FBT_ACT: /* Account string - in filename */
if (fe->fe_lh) fprintf(f, "%o,,", fe->fe_lh);
fprintf(f, "%o ", fe->fe_rh);
case FBT_LWR: /* Last-Writer string - stored in dumped block. */
/* Must skip showing actual FDB value because this is the first
** FDB word not stored by DUMPER. This and following words are
** instead filled by the strings for FBAUT, FBLWR, and FBARC.
*/
fprintf(f, "==> %s", fe->fe_str ? fe->fe_str : "?");
break;
}
putc('\n', f);
}
fdb_show(f, blk)
FILE *f;
unsigned char *blk;
{
struct fdbent *fe;
int i;
fprintf(f, "FDB for file %d.%d: %s\n\n", ssno, filenum, festr_fname);
for (fe = fdbt; fe <= &fdbt[nfdbents-1]; ++fe) {
fe_extract(blk, fe);
fe_show(f, fe);
}
}
hwd6show(f, hwd)
FILE *f;
unsigned long hwd; /* 18-bit halfword value - 3 SIXBIT chars */
{
int ch;
if (ch = (hwd>>12) & 077) {
fputc(ch + 040, f);
if (ch = (hwd>>6) & 077) {
fputc(ch + 040, f);
if (ch = hwd & 077) {
fputc(ch + 040, f);
return 1;
}
}
}
return 0;
}
pftime(f, atim)
FILE *f;
time_t *atim;
{
char *cp = ctime(atim)+4;
fprintf(f, "%.2s-%.3s-%.2s %.8s", cp+4, cp, cp+18, cp+7);
}
doDatablock(block)
char *block;
{
/* max is 5 bytes per word */
static char buf[(512*5)+1]; /* A page of characters */
int ct, wct;
int maxperblock;
int nout, type;
if (debug > 10)
printf("*");
if (fpFile == NULL)
return;
maxperblock = 512 * outnbpiw; /* # output bytes per 512-wd block */
if (firstblock) type = CONTAINS_BOF;
else type = 0;
if (outnbleft > maxperblock)
ct = maxperblock, wct = 512;
else {
ct = outnbleft, wct = outnfw;
type |= CONTAINS_EOF;
}
switch (outmode) {
case OC_ASCII:
nout = getstring(block, buf, 6, ct, type);
fwrite(buf, 1, nout, fpFile);
break;
case OC_8BIT:
getbytes(block, buf, 6, ct);
fwrite(buf, 1, ct, fpFile);
break;
case OC_WORD: /* No conversion needed */
fwrite(block + (6*5), 1, ct, fpFile);
break;
case OC_PAGE: /* Cannot do anything for now */
break;
}
if (ferror(fpFile))
punt(1, "Error writing %s", sunixname);
firstblock = 0;
outnbleft -= ct;
outnfw -= wct;
}
/*ARGSUSED*/
doSaveset(block, contflag)
char *block;
int contflag;
{
int ssnoff;
long hwd;
char name[140]; /* Size must be mod 5 */
time_t t;
if (debug > 10) {
printf("\nSaveset header:\n");
showblk(stdout, block, 0, 100);
}
tapeno = getfield(block, WdoffTapeNum, BtoffTapeNum, BtlenTapeNum);
ssno = getfield(block, WdoffSaveSetNum, BtoffSaveSetNum,
BtlenSaveSetNum);
/* Find offset to saveset name. Use same algorithm as DUMPER here.
*/
hwd = getfield(block, WdoffSSFormat, 0, 18); /* Get LH of fmt */
if (hwd & (0177L<<(18-7))) {
tapefmt = 0; /* Format wd has ASCII - old fmt! */
ssnoff = 0; /* Starts with saveset name */
} else {
/* Else use RH as format number (actually whole wd, but so what) */
tapefmt = getfield(block, WdoffSSFormat, 18, 18);
ssnoff = getfield(block, WdoffSSNamoff, 18, 18);
if (ssnoff >= 512) { /* Sanity check */
printf("Saveset header error: name offset of %ld is > 512!\n",
ssnoff);
ssnoff = 3; /* Use usual default instead */
}
}
getstring(block, name, RecHdrlen+ssnoff, sizeof(name), STRING);
name[sizeof(name)-1] = '\0'; /* Ensure terminated */
t = unixtime(block, WdoffSSDate);
if (dodir || verbose) {
if (showtloc)
printf("B%ld: ", nblktape);
printf("Tape %d: %sSaveset %d \"%s\" ",
tapeno, (contflag ? "Continued " : ""),
ssno, name);
pftime(stdout, &t);
printf("\n");
}
}
static void folddown(to, from)
register char *to, *from;
{
for ( ; *from; ++from)
*to++ = (isupper(*from) ? tolower(*from) : *from);
*to = '\0';
}
doFileHeader(block)
char *block;
{
char *ts;
static char prt_ar[2] = {'-', 'A'};
static char prt_inv[2] = {'-', 'I'};
static char prt_off[2] = {'-', 'O'};
if (debug > 5)
printf("File Header block:\n");
filenum = getfield(block, WdoffFileNum, BtoffFileNum, BtlenFileNum);
getstring(block, festr_fname, WdoffFLName, sizeof(festr_fname), STRING);
if (festr_attrs = strchr(festr_fname, ';')) /* Chop off ;Pprot;Aacct */
*festr_attrs++ = 0;
folddown(topsname, festr_fname);
fpFile = NULL;
if ( doallflag ||
(patterns && patternmatch()) ||
(expression && expmatch()) ||
(nselect && fmatch()) ) {
getfdbinfo(block);
pageno = getfield(block, WdoffPageNum, BtoffPageNum, BtlenPageNum);
if (dodir || verbose || fdbflg) {
if (fdbflg) {
printf("\n");
fdb_show(stdout, block);
printf("\n");
}
if (showtloc)
printf("B%ld: ", nblktape);
if (verbose)
printf("%3d %5d ", ssno, filenum);
printf("%c%c%c", prt_ar[archived], prt_off[offline],
prt_inv[invisible]);
printf(" %4d %8d %2d %06o ", offline ? apgcount : pgcount,
numbytes, bytesize, tprot);
pftime(stdout, &mtime);
printf(" %s", topsname);
#if 0 /* KLH: fix this later, currently vars are always zero */
if (archived && verbose /* >= 2 */)
printf(" %x %3d %4d %x %3d %4d", tp1, ss1, tf1, tp2, ss2, tf2);
#endif
if (pageno != 0)
printf(" Split file, part 2");
}
if (xflg) {
/* Decide bytesize (mode) for file extraction */
if (bytesize > 36)
bytesize = 36;
if (bytesize == 0 && numbytes) {
/* Probably a paged holey file, but if file len set, try 36 */
bytesize = 36; /* Cross fingers */
}
if (!bytesize) { /* If size still zero, empty file! */
outnfbpw = 0; /* # file bytes/wd */
outnfw = 0; /* # file words */
} else {
outnfbpw = 36/bytesize; /* # file bytes/wd */
outnfw = (numbytes + outnfbpw-1) / outnfbpw; /* # file wds */
}
outnbpiw = 5; /* Default output bytes per input wd */
if (!bytesize) {
outmode = OC_PAGE;
outnbpiw = outnbleft = 0;
} else if (wordflg) {
outmode = OC_WORD;
outnbpiw = 5;
outnbleft = outnbpiw * outnfw;
} else switch (bytesize) {
case 7:
outmode = OC_ASCII;
outnbpiw = 5;
outnbleft = numbytes;
break;
case 8:
outmode = OC_8BIT;
outnbpiw = 4;
outnbleft = numbytes;
break;
case 36:
if (default_bytesize == 7) {
outmode = OC_ASCII;
outnbpiw = 5;
outnbleft = outnbpiw * outnfw;
break;
} else if (default_bytesize == 8) {
outmode = OC_8BIT;
outnbpiw = 4;
outnbleft = outnbpiw * outnfw;
break;
}
default:
outmode = OC_WORD;
outnbpiw = 5;
outnbleft = outnbpiw * outnfw;
break;
}
if (outmode != OC_PAGE && !offline) {
if (pageno != 0) { /* continued file */
long missiwds = pageno * 512; /* Missing input wds */
long missob = missiwds * outnbpiw; /* Missing out bytes */
outnfw -= missiwds;
outnbleft -= missob;
if (!(dodir || verbose))
printf("%s: Split file, part 2", topsname);
printf(": %ld file words (%ld output bytes) missing.",
missiwds, missob);
if (!(dodir || verbose))
putchar('\n');
}
firstblock = 1;
fpFile = fopen(unixname(topsname), "w");
if (fpFile == NULL)
punt(1, "Can't open %s for write", sunixname);
else if (verbose)
printf(" Extracted.");
if (fchmod(fileno(fpFile), t2uprot(tprot) & ~0111) < 0)
punt(1, "fchmod on %s", sunixname);
} else if (verbose)
printf(" Skipping -- %s file.",
offline ? "offline" : "paged/holey");
}
if (dodir || verbose)
putchar('\n');
}
}
/* Return 1 if topsname matches any of the "extraction" strings. */
patternmatch()
{
register int i;
for (i = 0; i < numpats; i++)
if (strstr(topsname, patterns[i]))
return (1);
return (0);
}
/* Return 1 if topsname matches the regular expression. */
expmatch()
{
register int match;
if (expression) {
if ((match = re_exec(topsname)) == -1)
punt(0, "re_exec: internal error on %s", topsname);
else
return (match);
}
return (0);
}
int widx;
/* Return 1 if current file number matches one selected by arg line. */
fmatch()
{
while (want[widx].ssnum < ssno)
widx++;
if (want[widx].ssnum > ssno)
return 0;
while (want[widx].fnum < filenum)
widx++;
if (want[widx].fnum > filenum)
return 0;
return 1;
}
/*
* Sets a bunch of global variables to info from the fdb.
* For some reason the archive tape info is garbage.
*/
getfdbinfo(block)
char *block;
{
mtime = unixtime(block, WdoffFDB_Wrt);
atime = unixtime(block, WdoffFDB_Ref);
bytesize = getfield(block, WdoffFDB_BSZ, BtoffFDB_BSZ, BtlenFDB_BSZ);
numbytes = getfield(block, WdoffFDB_Size, BtoffFDB_Size,BtlenFDB_Size);
pgcount = getfield(block, WdoffFDB_PGC, BtoffFDB_PGC, BtlenFDB_PGC);
tprot = getfield(block, WdoffFDB_PRT, BtoffFDB_PRT, BtlenFDB_PRT);
archived = getfield(block, WdoffFDB_CTL, BtoffFDB_Arc, BtlenFDB_Arc);
invisible= getfield(block, WdoffFDB_CTL, BtoffFDB_Inv, BtlenFDB_Inv);
offline = getfield(block, WdoffFDB_CTL, BtoffFDB_Off, BtlenFDB_Off);
apgcount = getfield(block, WdoffFDB_PGC_A, BtoffFDB_PGC, BtlenFDB_PGC);
/* The rest is bogus. */
tp1 = getfield(block, WdoffFDB_TP1, 0, 36);
tp2 = getfield(block, WdoffFDB_TP2, 0, 36);
ss1 = getfield(block, WdoffFDB_SS1, BtoffFDB_SS, BtlenFDB_SS);
ss2 = getfield(block, WdoffFDB_SS2, BtoffFDB_SS, BtlenFDB_SS);
tf1 = getfield(block, WdoffFDB_TF1, BtoffFDB_TF, BtlenFDB_TF);
tf2 = getfield(block, WdoffFDB_TF2, BtoffFDB_TF, BtlenFDB_TF);
}
/*ARGSUSED*/
doFileTrailer(block)
char *block;
{
if (debug > 10) printf(" File trailer\n");
if (fpFile != NULL) {
if (fclose(fpFile) == EOF)
punt(1, "fclose: write error on %s", sunixname);
fpFile = NULL;
utime(sunixname, timep);
if (outnbleft || outnfw)
printf("%s: Split file, part 1: %ld file words (%ld output bytes) left\n",
topsname, outnfw, outnbleft);
}
}
/*ARGSUSED*/
doTonext(block)
char *block;
{
if (dodir || verbose) {
if (showtloc)
printf("B%ld: ", nblktape);
printf("TONEXT record - continued on next tape\n");
}
}
/*ARGSUSED*/
doTapeTrailer(block)
char *block;
{
if (dodir || verbose) {
if (showtloc)
printf("B%ld: ", nblktape);
printf("Tape trailer - Saveset End\n");
}
}
#if READ20_STDC
void punt(int prterrno, char *fmt, ...)
#else
void punt(prterrno, fmt, a,b,c,d,e)
int prterrno; /* TRUE to print errno string */
char *fmt;
#endif
{
extern int errno, sys_nerr;
fprintf(stderr, "read20: ");
#if READ20_STDC
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
#else
fprintf(stderr, fmt, a,b,c,d,e);
#endif
if (prterrno) {
fprintf(stderr, ": ");
if (errno >= 0 && errno < sys_nerr)
perror("");
else
fprintf(stderr, "ERRNO = %d\n", errno);
}
else
fprintf(stderr, "\n");
exit(1);
}
/*
* Unpack into buffer 's' the 7 bit string stored in 'block' and
* append a null char. Optionally strip CR's from CRLF pairs. 'max'
* is the max number of 7-bit chars to unpack from 'block', not the
* max to put into 's' (that's important!). This only works if
* getstring() is always called with 'max' mod 5 == 0, except for the
* last call on "contiguous" blocks.
* Returns number of chars stored in output buffer.
*/
int
getstring(block, s, wordoff, max, type)
register unsigned char *block; /* Tape block */
register char *s; /* Destination string buffer */
int wordoff; /* 36-bit offset from start of tape block */
int max; /* Max number of characters to xfer into s */
int type; /* Info about block we are reading (first, last
or string) */
{
register int ct; /* Number of characters loaded so far */
register char *op; /* Output pointer for <CRLF> -> <LF> hacking */
register int c;
char *orig = s; /* Save for debugging */
unsigned long top4;
static int state;
#define NORMAL 1
#define LAST_WAS_CR 2
op = s;
block += wordoff * 5;
for (ct = 0; ct < max; ct += 5)
{
/* Copy 5 7-bit bytes portably from 8-bit byte frames.
** This depends on unsigned char being implemented correctly.
*/
*op++ = block[0] >> 1;
*op++ = ((block[0] << 6) | (block[1] >> 2)) & 0177;
*op++ = ((block[1] << 5) | (block[2] >> 3)) & 0177;
*op++ = ((block[2] << 4) | (block[3] >> 4)) & 0177;
*op++ = ((block[3] << 3) | ((block[4]&017) >> 1)) & 0177;
block += 5;
}
if (keepcr) return (max);
op = s;
if (type & CONTAINS_BOF) state = NORMAL;
for (ct = 0; ct < max; ct++)
{
c = *s++;
switch (state) {
case NORMAL:
if (c != '\r')
*op++ = c;
else
state = LAST_WAS_CR;
continue;
case LAST_WAS_CR:
if (c == '\n') {
*op++ = c;
state = NORMAL;
continue;
}
if (c == '\r') {
*op++ = c;
continue;
}
*op++ = '\r';
*op++ = c;
state = NORMAL;
}
}
if (type & CONTAINS_EOF) {
if (state == LAST_WAS_CR)
*op++ = '\r';
}
if (type & NUL_AT_END) *s++ = 0;
return (op - orig);
}
/* getbytes: like getstring, but ...
1) uses 8 bit bytes
2) doesn't stop on a zero
*/
getbytes(block, s, wordoff, max)
char *block; /* Tape block */
register char *s; /* Destination string buffer */
int wordoff; /* 36-bit offset from start of tape block */
int max; /* Max number of characters to xfer into s */
{
register int i; /* Counter for five characters per word */
int ct = 0; /* Number of characters loaded so far */
char *orig = s; /* Save for debugging */
block += wordoff * 5;
for (ct = 0; ct < max; ct += 4)
{
(void) memcpy(block, s, 4);
block += 5;
s += 4;
}
}
showblk(f, block, offset, nwds)
register FILE *f;
register unsigned char *block;
{
register int i;
register unsigned long lh, rh;
register int c;
char str[6];
str[5] = 0;
for (i = offset; --nwds >= 0; ++i)
{
lh = getfield(block, i, 0, 18);
rh = getfield(block, i, 18, 18);
if (!lh && !rh) {
fprintf(f, "%4o: 0\n", i);
continue;
}
/* Convert to word of ASCII */
c = (lh >> (18- 7)) & 0177;
str[0] = (isprint(c) ? c : '.');
c = (lh >> (18-14)) & 0177;
str[1] = (isprint(c) ? c : '.');
c = ((lh & 017)<<3) | (rh>>(18-3));
str[2] = (isprint(c) ? c : '.');
c = (rh >> (18-10)) & 0177;
str[3] = (isprint(c) ? c : '.');
c = (rh >> (18-17)) & 0177;
str[4] = (isprint(c) ? c : '.');
fprintf(f, "%4o: %6lo,,%6lo %s\n", i, lh, rh, str);
}
}
#define DayBaseDelta 0117213 /* Unix day 0 in Tenex format */
/*
* This screws up on some of the atime's we see, including, yielding, e.g.
* Fri Dec 23 23:28:16 1994
* Fri Dec 23 23:28:16 1994
* Tue Jan 13 07:57:03 1987
*/
time_t
unixtime(block, wordoff)
char *block;
int wordoff;
{
long int t, s;
double d;
t = getfield(block, wordoff, 0, 18); /* LH is day */
if (t == 0) return(0); /* 0 means never referenced */
t -= DayBaseDelta; /* Switch to unix base */
/* Now has # days since */
/* Jan 1, 1970 */
if (t < 0) {
fprintf(stderr, "ERROR - Date earlier than Jan 1,1970!!!\n");
}
s = getfield(block, wordoff, 18, 18); /* RH is fraction of day */
/* Note that the following calculation must be performed in the order shown
in order to preserve precision. It should also be done in double prec.
floating point to prevent overflows.
*/
d = ((double)s * (24*60*60)) /* Make it double and pre-multiply */
/ (1<<18); /* Then divide by 1<<18 to get secs */
s = d; /* Get back into integer form */
if ((d - s) >= 0.5) /* Find remainder -- round up? */
++s; /* Yup */
return s + (t*24*60*60); /* Add day base and return */
}
char *
unixname(name)
char *name;
{
static FILE *log = NULL;
register char *t, *p;
static char lastdir[128];
struct stat stb;
int dlen;
int mask;
register int newdir = 0;
if (numflg) { /* If numeric filenames */
if (log == NULL) log = fopen(LOGFILE, "a");
fprintf(log, "%d is %s\n", number, name);
sprintf(sunixname, "%d", number++);
return(sunixname);
}
strcpy(sunixname, strchr(name, '<') + 1); /* trim off device */
t = strrchr(sunixname, '>'); /* find end of directory */
*t = '.';
dlen = t - sunixname;
if (strncmp(lastdir, sunixname, dlen)) {/* maybe new dir */
strncpy(lastdir, sunixname, dlen); /* remember it */
lastdir[dlen] = '\0'; /* Ensure null terminated */
newdir = 1;
}
for (p = sunixname; p <= t; p++)
if (*p == '.' || *p == '/') {
if (newdir) {
*p = '\0'; /* temporarily null it off */
if (stat(sunixname, &stb) < 0) {
mask = umask(2);
if (mkdir(sunixname, 0777) < 0)
punt(1, "mkdir %s failed", sunixname);
umask(mask);
}
}
*p = '/';
}
for (p = t+1; *p; p++)
if (*p == '/') *p = '\\';
if (!genflg) {
t = strrchr(sunixname, '.'); /* find last . */
*t = 0; /* zap it out */
}
return(sunixname);
}
int
t2uprot(prot)
register unsigned prot;
{
register unsigned tprot, uprot;
register int tshift;
#ifdef notdef
if (f->FB_dir) { /* THIS WON'T WORK! */
punt(0, "Can't handle directory %s", topsname);
prot = gtdirprot(_dirnm(jfn)); /* returns 20 fmt protection */
for (tshift=12, uprot=0; tshift >= 0; tshift -= 6) {
tprot = prot >> tshift; /* pick up next field */
uprot <<= 3;
if (tprot & DP_rd)
uprot |= WREAD|WEXEC; /* world read, world execute */
if (tprot & (DP_cn|DP_cf)) /* allow write for either conn. */
uprot |= WWRITE; /* access or add files access */
}
}
else
#endif
{ /* do it this way so easily modified-- i know it could be faster */
for (tshift=12, uprot=0; tshift >= 0; tshift -= 6) {
tprot = prot >> tshift;
uprot <<= 3;
uprot |= (tprot >> 3) & 07; /* just r,w,x */
}
}
return uprot;
}
#ifdef notdef
#define HOUR 3600
#define DAY (HOUR*24)
#define DAY0 40587 /* number of days between tops20 0 day and Unix 0 day */
#define makeword(l, r) ( ((l) << 18) | (r) )
#define getright(b) ( (b) & 0777777 )
#define getleft(b) ( (b) >> 18 )
/* Convert Tops-20 to Unix time -- curently incomplete due to 32 < 36 bits */
int
_t2utim(t)
unsigned t;
{
register ticks, rh, secs;
ticks = t - makeword(DAY0, 0);
rh = getright(ticks) * DAY;
secs = rh >> 18;
if (rh % makeword(1,0) > 0400000)
secs++; /* round up */
return (getleft(ticks) * DAY) + secs;
}
#endif
int
compwant(w1, w2)
register struct want *w1, *w2;
{
int sdif;
if (sdif = w1->ssnum - w2->ssnum)
return sdif;
return (w1->fnum - w2->fnum);
}
getrec(fd, addr, len)
int len;
char *addr;
{
register int cc, remaining = len;
while (remaining > 0)
{
cc = read(fd, addr, remaining);
/* fprintf(stderr, "read(0, %x, %d) = %d\n", addr, remaining, cc);*/
if (cc == remaining) return(len);
if (cc == 0) return(len - remaining);
if (cc < 0) {
fprintf(stderr, "Got an error, errno=%d\n", errno);
exit(1);
}
/* Hack hack, cough... If we are reading from a tape (or a file), then don't
try to pad the record...
*/
if (fd != 0) return(cc);
remaining -= cc;
addr += cc;
}
return(len);
}