/* RSXSUB.C - Subroutines for the RTRSX program 19-May-83 */ /* Placed in the public domain by: Bryan Kattwinkel * 520 Palm Springs Blvd #502 * Indian Harbour Beach, FL 32937 * * Adapted from the Unix program GETRSX written by Mark Bartelt, with * structures and putch from an earlier attempt by Rob Pike. */ #include extern FILE *rsx; /* rsx file vector from RTRSX.C */ extern int nflag; /* no newlines flag for putch */ typedef unsigned ushort; /* 16 bits unsigned */ #define TEXT 0 #define IMAGE 1 struct filnam { ushort fnam[3]; /* File name (radix-50) */ ushort ftyp; /* File type (radix-50) */ ushort fver; /* Version number */ }; struct uic { char u_prog; /* Programmer number */ char u_proj; /* Project number */ }; struct fileid { ushort f_num; /* File number */ ushort f_seq; /* File sequence number (worthless concept) */ ushort f_rvn; /* Relative volume number (ditto and MBZ) */ }; struct fcs { char f_rtyp; /* Record type */ char f_ratt; /* Record attributes */ ushort f_rsiz; /* Record size */ ushort f_hibk[2]; /* Highest VBN allocated */ ushort f_efbk[2]; /* End of file block */ ushort f_ffby; /* First free byte */ }; struct header { char h_idof; /* Ident area offset */ char h_mpof; /* Map area offset */ ushort h_fnum; /* File number */ ushort h_fseq; /* File sequence number (why no RVN?) */ ushort h_flev; /* File structure level */ struct uic h_fown; /* File owner UIC */ ushort h_fpro; /* File protection code */ char h_fcha[2]; /* File characteristics */ struct fcs h_fcs; /* FCS area */ char h_ufat[18]; /* User attribute area */ ushort h_data[256-23]; /* Rest of block used for maps, etc. */ }; struct ident { struct filnam i_fnam; /* File name, type, version number */ ushort i_rvno; /* Revision number */ char i_rvdat[7]; /* Revision date (ASCII) */ char i_rvti[6]; /* Revision time (ASCII) */ char i_crdt[7]; /* Creation time (ASCII) */ char i_crti[6]; /* Creation time (ASCII) */ char i_exdt[7]; /* Expiration date (ASCII) */ char i_UU; /* Unused */ }; struct rtrvp { char lbn_hi; /* High order 8 bits of LBN */ char count; /* Count field */ ushort lbn_lo; /* Low order 16 bits of LBN */ }; struct map { char m_esqn; /* Extension segment number */ char m_ervn; /* Extension relative volume number */ ushort m_efnu; /* Extension file number */ ushort m_efsq; /* Extension file sequence number */ char m_ctsz; /* Block count field size */ char m_lbsz; /* LBN Field size */ char m_use; /* Map words in use */ char m_max; /* Map words available */ struct rtrvp m_rtr6[1]; /* Retrieval pointers */ }; static struct homeblock { ushort H_ibsz; /* Index file bitmap size */ ushort H_iblb[2]; /* Index file bitmap LBN */ ushort H_fmax; /* Maximum number of files */ ushort H_sbcl; /* Storage bitmap cluster factor */ ushort H_dvty; /* Disk device type */ ushort H_vlev; /* Volume structure level */ char H_vnam[12]; /* Volume name */ char H_UU1[4]; /* Unused 1 */ struct uic H_vown; /* Volume owner UIC */ ushort H_vpro; /* Volume protection code */ char H_vcha[2]; /* Volume characteristics */ ushort H_fpro; /* Default file protection */ char H_UU2[6]; /* Unused 2 */ char H_wisz; /* Default window size */ char H_fiex; /* Default file extend */ char H_lruc; /* Directory pre-access limit */ char H_UU3[11]; /* Unused 3 */ ushort H_chk1; /* First checksum */ char H_vdat[14]; /* Volume creation date */ char H_UU4[398]; /* Unused 4 */ char H_indn[12]; /* Volume name (don't you believe H_vnam?) */ char H_indo[12]; /* Volume owner (in a different format!) */ char H_indf[12]; /* Format type == "DECFILE11A " */ char H_UU5[2]; /* Unused 5 */ ushort H_chk2; /* Final checksum */ } hblock; struct directory { struct fileid d_fid; /* File id */ struct filnam d_fname; /* File name, type, version number */ }; #define DEPB 32 /* Number of directory entries per block */ /* File headers for index file and MFD */ static struct header indexh, mfdh, dirh, fileh; /* Pointers to their index areas */ static struct ident *indexi, *mfdi, *diri; /* Pointers to their map areas */ static struct map *indexm, *mfdm, *dirm, *filem; static char r50c[128] = { /* ASCII to Radix-50 conversion table */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 000, -1, -1, -1, 033, -1, -1, -1, -1, -1, -1, -1, -1, -1, 034, -1, 036, 037, 040, 041, 042, 043, 044, 045, 046, 047, -1, -1, -1, -1, -1, -1, -1, 001, 002, 003, 004, 005, 006, 007, 010, 011, 012, 013, 014, 015, 016, 017, 020, 021, 022, 023, 024, 025, 026, 027, 030, 031, 032, -1, -1, -1, -1, -1, -1, 001, 002, 003, 004, 005, 006, 007, 010, 011, 012, 013, 014, 015, 016, 017, 020, 021, 022, 023, 024, 025, 026, 027, 030, 031, 032, -1, -1, -1, -1, -1 }; static char r50a[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$.?0123456789"; static int state = 0; /* state of the putch routine */ /* * Open a disk containing an RSX filesystem */ rsxdev (devn) char *devn; { register ushort devr50; char *p; long ifhbn; ushort ator50 (); if (rsx != NULL) fclose (rsx); /* close previous device */ p = devn; devr50 = ator50 (&p); /* convert to rad50 */ if (*p != '\0') err1("Invalid device name (%s)",devn); if ((rsx = dopen (devr50, "rn")) == NULL) /* special dopen */ err1("Can't open device %s:", devn); if ( !getlb(1L,&hblock) ) err1("Can't read homeblock on %s:", devn); hblock.H_indf[12] = '\0'; /* terminate ident str */ if (!streq (hblock.H_indf, "DECFILE11A ")) err1 ("Not a DECFILE11A volume (%s)", hblock.H_indf); ifhbn = ((long)hblock.H_iblb[0]<<16) + (long)hblock.H_iblb[1] + hblock.H_ibsz; if ( !getlb(ifhbn,&indexh) ) err1("Can't read index file header on %s:", devn); indexi = (struct ident *) &indexh.h_data[indexh.h_idof-23]; indexm = (struct map *) &indexh.h_data[indexh.h_mpof-23]; if ( !getlb(ifhbn+3,&mfdh) ) err1("Can't read mfd header on %s:", devn); mfdi = (struct ident *) &mfdh.h_data[mfdh.h_idof-23]; mfdm = (struct map *) &mfdh.h_data[mfdh.h_mpof-23]; } /* * Locate the directory whose name is pointed to by "d" */ rsxdir (d, dirname) /* return 6-character directory name */ char *d, *dirname; { char dum; int proj, pgmr; struct filnam r50dir; ushort dirfnum; ushort search(); if (sscanf (d, "%o,%o%c", &proj, &pgmr, &dum) != 2 || proj < 0 || proj > 0377 || pgmr < 0 || pgmr > 0377) err1 ("Invalid UFD ([%s])", d); sprintf (dirname, "%03o%03o", proj, pgmr); diri = (struct ident *) 0; dirm = (struct map *) 0; if ( !r50fn(dirname,"dir","1",&r50dir) ) err1("Invalid UFD ([%s])",d); if ( !(dirfnum=search(mfdm,&r50dir)) ) err1("UFD [%s] does not exist",d); if ( !gethdr(dirfnum,&dirh) ) err1("Can't get file header for UFD [%s]",d); diri = (struct ident *) &dirh.h_data[dirh.h_idof-23]; dirm = (struct map * ) &dirh.h_data[dirh.h_mpof-23]; } /* * Open an rsx file for input */ rsxfile (name, type, vers) char *name, type, vers; { struct filnam r50fil; ushort fn, search (); r50fn (name, type, vers, &r50fil); /* convert to rad50 */ if ( !(fn = search (dirm, &r50fil)) ) /* search directory */ err0 ("Input file not found"); if ( !gethdr (fn, &fileh) ) /* get header block */ err0 ("Cant read input file header"); filem = (struct map *) &fileh.h_data [fileh.h_mpof-23]; /* map area */ } /* * Convert file name, type, and version number to "struct filnam" format */ static r50fn(fl,tp,vr,r50f) char *fl; char *tp; char *vr; struct filnam *r50f; { register int i; ushort ator50(); if ( strlen(fl) > 9 ) err0("Filename longer than 9 characters"); if ( strlen(tp) > 3 ) err0("File type longer than 3 characters"); for ( i=0; i<3; ++i ) if ( (r50f->fnam[i]=ator50(&fl)) == -1 ) return(0); if ( (r50f->ftyp=ator50(&tp)) == -1 ) return(0); r50f->fver = 0; while ( *vr ) { if (*vr < '0' || *vr > '7') err0("Non-octal character in version number"); r50f->fver <<= 3; r50f->fver += *vr++ - '0'; } return(1); } /* * Convert up to three ascii characters to a 16-bit radix-50 value; note * type of s (char **, not char *); *s is left pointing to the null byte, * if encountered */ static ushort ator50(s) register char **s; { int i; ushort scale, r; i = 3; scale = 050*050; r = 0; while ( **s && i-- ) { if ( r50c[**s] == -1 ) err1 ("Bad radix-50 character (%c)",**s); r += r50c[*(*s)++] * scale; scale /= 050; } return(r); } /* * Search a directory (identified by dmap) for a radix50 filename */ static ushort search(dmap,file) register struct map *dmap; register struct filnam *file; { register ushort hldver; register ushort hldfnum; register long vb; struct directory dirbuf[DEPB]; struct directory *de; for ( hldver=0, hldfnum=0, vb=0; getvb(++vb,(char *)dirbuf,dmap); ) { for ( de=dirbuf; ded_fid.f_num == 0 ) continue; if ( file->fver && match(file,&de->d_fname,5) ) return(de->d_fid.f_num); if ( !file->fver && de->d_fname.fver>hldver && match(file,&de->d_fname,4) ) { hldfnum = de->d_fid.f_num; hldver = de->d_fname.fver; } } } return(hldfnum); } /* * Check whether rad50 filenames (possibly including version number) match */ static match(a,b,n) register ushort *a; register ushort *b; int n; { while ( n-- ) if ( *a++ != *b++ ) return(0); return(1); } /* * Copy input file to output destination */ copyfile (xfermode) int xfermode; { long eofblk; register long block; register long b; char buf[512]; char *limit; register char *p; block = 0; b = 0; limit = &buf[512]; rputch (); if ( xfermode != IMAGE ) eofblk = ( (long)fileh.h_fcs.f_efbk[0] << 16 ) + fileh.h_fcs.f_efbk[1]; while ( getvb(++block,buf,filem) ) { if ( xfermode == IMAGE ) { if ( fwrite(buf,1,512,stdout) != 512 ) err0("write error"); continue; } if ( ++b > eofblk ) return(1); if ( b == eofblk ) limit = &buf[fileh.h_fcs.f_ffby]; for ( p=buf; pd_fid.f_num != 0 ) prtfn(&de->d_fname); } /* * Convert a radix-50 filename to ASCII, and write to standard output. */ static prtfn(r50fil) struct filnam *r50fil; { register int i; for ( i=0; i<3; ++i ) if ( !r50out(r50fil->fnam[i]) ) break; putchar('.'); r50out(r50fil->ftyp); fprintf(stdout,";%o\r\n",r50fil->fver); /* note CR LF */ } /* * Convert up to three nonblank radix-50 characters to ASCII, and write to * standard output. Returns 1 if three characters converted, 0 otherwise. */ static r50out(x) ushort x; { ushort div; char c; div = 050*050; while ( (c=r50a[(x/div)%050]) != ' ' ) { putchar(c); if ( !(div/=050) ) return(1); } return(0); } /* * Get a file header, given the file number */ static gethdr(fn,buf) ushort fn; char buf[512]; { long bn; bn = (long)fn + (long)hblock.H_ibsz + 2; return(getvb(bn,buf,indexm)); } /* * Routine to get specified virtual block from a file. Returns 0 * on EOF, 1 otherwise. Note that vbn is 1-based, not 0-based. */ static getvb(vbn,buf,map) register long vbn; char buf[512]; register struct map *map; { register struct rtrvp *rp; register struct rtrvp *limit; register long block; register long lbn; rp = map->m_rtrv; block = 1; limit = (struct rtrvp *) ((ushort *)map->m_rtrv + map->m_use); while ( vbn >= ( block += (rp->count&0377)+1 ) ) if ( ++rp >= limit ) return(0); lbn = (long)rp->lbn_lo + (((long)rp->lbn_hi & 0377)<<16) + vbn - block + (rp->count&0377) + 1; return(getlb(lbn,buf)); } /* * Get block from the filesystem, given the logical block number * HACKED FOR DECUS C FSEEK VALUES */ static getlb(lbn,buf) long lbn; char buf[512]; { register unsigned bn; bn = lbn; /* truncate, max 65536 blocks */ if (bn == 0 || lbn>>16 != 0L) err0 ("Corrupt file structure"); if (fseek (rsx, (long)bn<<16, 0) == EOF || /* seek block */ fread (buf, 1, 512, rsx) != 512) /* and read it */ err1 ("Read error on block %u", bn); return(1); }