/* dfind.c10 */ /* * Dfind - search for a file on an RT-11 structured disk * with subdevices. * * Lachman Associates, Inc. * 645 Blackhawk Drive * Westmont, IL 60559 * (312) 986-8840 * * Written by Kris A. Kugel, April, 1982. * Submitted by Joe Lachman * * Various improvements by: Robert Lawhead * Carl Lowenstein * Marine Physical Lab., UC San Diego * La Jolla, CA 92093 * * Bug fix 21-Apr-86 rml: readseg() was checking wrong value * returned by fread & producing erroneous error message. * Enhancement 22-Apr-86 cdl: added printdate() to decode and * print RT-11 file creation dates. * Enhancement 22-Apr-86 cdl: removed 'file not found anywhere' message. * Enhancement 22-Apr-86 cdl: print RT-11 file sizes. * Bug fix 22-Apr-86 cdl: search() was trying to recursively enter * and search() deleted files with name *.DSK * Enhancement 22-Apr-86 cdl: use both *.DSK and *.DEV as subdevices. * Bug fix 19-Jun-86 cdl: check that a disk image is a valid file * system (word 0 == 0240) before trying to search it. * Refix 13-Oct-86 cdl: check that disk image is valid file system * by looking for "DECRT11A" in block 1. This rejects * images of XXDP+ disks, which passed previous check. * Enhancement 13-Oct-86 rml: output line now looks like * FILNAM.EXT size date path * so it can be sorted by name without extra flap. * Enhancement 14-Oct-86 cdl: use 'malloc' to allocate expandable * storage during file tree traversal. * Enhancement 24-Oct-86 cdl: -t option to display TSX file times. * Enhancement 26-Oct-86 cdl: make user interface slightly cleaner. * avoid typing "argv:" */ #define UNIXLIKE #ifdef UNIXLIKE #include #include #endif #ifdef WHITESMITH #include #define putchar(c) putch(c) #endif #define MAXLEVEL 7 /* Maximum depth of subdevice search */ #define DSIZE 16 #define DIR_NAMESIZE 9 /* # of characters needed to store filename */ #define MATCHALL '*' #define MATCHONE '?' #define MATCH_ONE '%' #define DSKNAME "*.DSK" /* Pattern for subdevice name */ #define DEVNAME "*.DEV" /* Pattern for subdevice name */ #define BLOCKSIZE 512 /* size of block in bytes */ #define SEGSIZE BLOCKSIZE *2 #define BYTESINWORD 2 #define FIRSTSEG 6 /* position of first segment block for device */ #define LINESIZE 72 #define TRUE (0==0) #define FALSE (0!=0) #ifndef NULL #define NULL 0 #endif #define ENDOFSEG 04000 #define EMPTY_FILE 01000 #define PERMANENT_FILE 02000 #define PROTECTED 0100000 typedef struct { int status; int filename[3]; /* name + extension */ int filelen; /* char jobno; char channelno; */ /* unused */ unsigned file_time; /* TSX, ignored by RT */ int creation_date; } DIR_ENTRY; typedef struct { int total_segs; int next_seg; int last_seg; int extra_bytes; int start_of_data; char filler[ SEGSIZE -(5 * BYTESINWORD) ]; } SEGMENT; typedef struct { char name[6]; char extension[3]; } DIRNAME ; typedef struct statnode { struct statnode *next; int dirlevel; char fname[DSIZE]; long filecount; long blockcount; long freeblocks; } STATNODE; /* Decus C doesn't have macros with arguments #define RTyear(x) (( x & 037 ) + 1972) #define RTday(x) (( x >> 5 ) & 037) #define RTmonth(x) (( x >> 10 ) & 017) */ SEGMENT *Segptr[MAXLEVEL] = { NULL }; char *Path[MAXLEVEL] = { NULL }; long Base[MAXLEVEL] = { NULL }; int Nextsegno[MAXLEVEL] = { 0 }; long Position[MAXLEVEL] = { NULL }; int Level = 0; #ifdef rt11 char *$$prmt = "dfind: "; /* run-time prompt line */ #endif char *Cmdname = "dfind"; char Usage[80] = { '\0' }; STATNODE *Statlist = NULL; FILE *Rootfile; char Dk[DSIZE] = "DK:"; char Fpattern[80] = "*"; int Dflag = 0; /* print deleted files */ int Aflag = 0; /* print deleted and nondeleted files */ int Pflag = 0; /* print protected files */ int Tflag = 0; /* print file creation times */ int Vflag = 0; /* flag for printing out file statistics */ unsigned Found = 0; /* flag for any occurrences found */ /* * dfind -- search for a file on an rt-11 structured disk * with subdevices. * */ main( argc, argv ) int argc; char *argv[]; { register char *vpt; char dev[DSIZE]; char *scat(); char *r50toascii(); DIR_ENTRY *mkentry(); DIR_ENTRY *getentry(); SEGMENT *readseg(); char *split(); STATNODE *makestatnode(); FILE *fopen(); char *malloc(); scopy( Usage, "usage: " ); /* scat( Usage, Cmdname );*/ scat( Usage, " [-adptv][ -f dfault ] [ [device][filename] ]..." ); while ( argc > 1 && ( *argv[1] == '-' || *argv[1] == '/' ) ) { /* get argument flags */ for ( vpt = &argv[1][1]; *vpt; vpt++ ) { switch ( *vpt ) { case 'a': case 'A': Aflag = TRUE; break; case 'd': case 'D': Dflag = TRUE; break; case 'p': case 'P': Pflag = TRUE; break; case 't': case 'T': Tflag = TRUE; break; case 'v': case 'V': Vflag = TRUE; break; case 'f': case 'F': if ( argc < 3 ) fatal( Usage ); scopy( Dk, argv[2] ); --argc; argv++; vpt[1] = '\0'; break; case '/': break; default: fatal( Usage ); } } --argc; argv++; } if ( --argc <= 0 ) { if ( mkrootfile( Dk ) ) { scopy( Fpattern, "*" ); search( 0L, Dk ); if ( !Found ) /* warn(scat(scat(Fpattern, " not found anywhere on "), dev ) )*/ ; else if (Vflag) { printstats( Statlist ); freelist( Statlist ); } } } else while ( argc-- ) { Found = 0; getdev( dev, ++argv ); getpattern( Fpattern, *argv ); if ( mkrootfile( dev ) ) { search( 0L, dev ); if ( !Found ) /* warn(scat(scat(Fpattern, " not found anywhere on "), dev ) ) */ ; else if (Vflag) { printstats( Statlist ); freelist( Statlist ); } } } } /* * mkrootfile opens --Rootfile--, the base device. */ int mkrootfile( dev ) char *dev; { #ifdef UNIXLIKE Rootfile = fopen( dev, "rn"); #endif #ifdef WHITESMITH Rootfile = open( dev, 0, 1 ); #endif if ( Rootfile == NULL ) { err( "unable to open device file" ); return( FALSE ); } return( TRUE ); } /* * search -- look for -Fpattern- on device named and the * subdevices on it. (recursive decent) */ search( baseblock, dev ) long baseblock; char *dev; { DIR_ENTRY *d_entry ; char name[DSIZE]; long filecount, blockcount, freeblocks; filecount = blockcount = freeblocks = 0L; if ( ++Level >= MAXLEVEL ) { warn( "MAXLEVEL EXCEEDED--file skipped" ); --Level; return; } d_entry = NULL; Path[ Level -1 ] = dev; Base[Level] = baseblock; if ((Segptr[Level] = malloc (SEGSIZE)) == NULL) fatal ("no room for segment"); while ( (d_entry = getentry( d_entry )) != NULL ) { r50toascii( d_entry->filename, name, DIR_NAMESIZE ); name[DIR_NAMESIZE] = '\0'; if ( match( d_entry ) ) { /* date and blocksize printing would go here */ printname( name); printf ("%6d ", d_entry -> filelen); printdate(d_entry -> creation_date); printf(" "); if (Tflag) { printtime(d_entry -> file_time); printf(" "); } printpath( ); putchar('\n'); Found++ ; filecount++ ; blockcount += d_entry->filelen; } if ( d_entry->status == EMPTY_FILE ) { freeblocks += d_entry->filelen; } else { if ( nmatch( DSKNAME, name ) || nmatch( DEVNAME, name ) ) search( Position[Level], name ); } Position[Level] += d_entry->filelen; } if (Vflag) enterstats( Level-1, dev, filecount, blockcount, freeblocks ); free (Segptr[Level]); --Level; } /* * mkentry produces returns a pointer to the first directory entry * int the segment starting at . mkentry also updates the * global variables associated with each directory segment. */ DIR_ENTRY *mkentry( block ) long block; { register SEGMENT *seg; seg = Segptr[ Level ]; readseg( block, seg ); #ifdef WDEBUG putfmt("segment:%l\n\t%i\n\t%i\n\t%i\n\t%i\n\t%i\n", block, seg->total_segs,seg->next_seg, seg->last_seg, seg->extra_bytes, seg->start_of_data ); #endif Position[Level] = seg->start_of_data + Base[Level] ; Nextsegno[Level] = seg->next_seg; return( (DIR_ENTRY *)(seg->filler) ); } /* * getentry returns the next entry on a [virtual] device or NULL if * there is none. is the previous entry returned. */ DIR_ENTRY *getentry( d_entry ) DIR_ENTRY *d_entry; { if ( d_entry == NULL ) { if (!validchk(Base[Level])) return (NULL); /* not a valid file system */ else return( mkentry( Base[Level] + FIRSTSEG ) ); } if ( (int)((++d_entry)->status) == ENDOFSEG ) { #ifdef WDEBUG putfmt("request block:%l\n\tNextsegno:\t%i\n\tbase:\t\t%l\n", Base[Level] + FIRSTSEG + Nextsegno[Level]*2, Nextsegno[Level],Base[Level]); #endif if ( Nextsegno[Level] == 0 ) return( NULL ); else return( mkentry( Base[Level] + FIRSTSEG + (Nextsegno[Level] -1)*2 ) ); } return( d_entry ); } /* * validchk reads the first two blocks of an alleged file system * and verifies that it has been 'init'ed with an RT-11 directory */ validchk(block) long block; { char *bp; int iret; if ((bp = malloc(SEGSIZE)) == NULL) fatal ("no room for boot block"); fseek (Rootfile, block*BLOCKSIZE, 0); if (fread (bp, SEGSIZE, 1, Rootfile) != 1) fatal ("can't read boot block"); iret = (strncmp(bp+01760, "DECRT11A", 8) == 0); free (bp); return (iret); } /* * readseg copies a directory segment starting * at block into . */ SEGMENT *readseg( block, seg ) long block; register SEGMENT *seg; { fseek( Rootfile, block * BLOCKSIZE, 0 ); if ( fread( (char *)seg, sizeof(SEGMENT), 1, Rootfile) != 1 ) err("directory segment read botched"); return( seg ); } /* * fold causes lower case letters in to become upper case. */ fold( string ) register char *string; { for ( ; *string; string++ ) if ( islower( *string ) ) *string = toupper( *string ); } /* * getdev determines which file should be searched from * ane returns the answer in */ getdev( dev, arg ) register char *dev; register char **arg; { register char *cpt; cpt = *arg; while ( *cpt && *cpt != ':' ) cpt++; if ( *cpt ) { while ( *arg <= cpt ) *dev++ = *(*arg)++ ; *dev = '\0'; } else scopy( dev, Dk ); } /* * getpattern determines the pattern to be matched * from and returns it in . */ getpattern( pattern, arg ) char *pattern; char *arg; { if (*arg) scopy( pattern, arg ); else scopy( pattern, "*" ); fold( pattern ); } /* * r50toascii converts radix50 characters in to ascii * characters and puts the generated characters in . The * number of characters generated will be the first int evenly * divisible by three >= , so the buffer points to * should be sized accordingly. */ char *r50toascii( radix, ascii, count ) int *radix; register char *ascii; int count; { int i; for ( i=0; i< count; i+=3,radix++ ) convasc( *radix, ascii +i ); return( ascii ); } /* * convasc converts in radix050 form to 3 ascii chars, * and puts these in . * * ( from rtpip.c ) */ convasc(radname,ascii) unsigned radname; register char *ascii; { register int i; for (i=2,ascii+=2; i>=0; --i,--ascii) { *ascii = radname % 050; radname /= 050; if (*ascii == 0) *ascii = 040; else if (*ascii <= 032) *ascii += 0100; else if (*ascii == 033) *ascii = 044; else if (*ascii == 034) *ascii = 056; else if (*ascii == 035 ) *ascii = '?'; /* undefined value */ else *ascii += 022; } return; } /* * scopy copys one string onto another. "string" is defined * as ascii characters followed by null. * ( This is used to avoid a UNIX-dependent system call. ) */ scopy( to, from ) register char *to; register char *from; { while ( (*to++ = *from++ ) != '\0' ) ; } /* * scat concatinates onto the end of . * length is NOT checked. */ char *scat( string1, string2 ) register char *string1; register char *string2; { char *stringstart; stringstart = string1; for (; *string1 ; string1++ ) ; while ( *string1++ = *string2++ ) ; return( stringstart ); } /* * match checks to see if is matched by -Fpattern- * ( checks against flags, ect. ) */ match( d_entry ) register DIR_ENTRY *d_entry; { char name[DSIZE]; r50toascii( d_entry->filename, name, DIR_NAMESIZE ); name[DIR_NAMESIZE] = '\0'; if ( nmatch( Fpattern, name ) ) { if (!Dflag && (d_entry->status & PERMANENT_FILE)) { if (Pflag) return( d_entry->status & PROTECTED ); else return( TRUE ); } else if ( (Dflag || Aflag) && d_entry->status == EMPTY_FILE ) return( TRUE ); } return( FALSE ); } /* * nmatch determines whether matches . * returns true/false. */ nmatch( pattern, name ) char *pattern; char *name; { char *pextn; int namlen, extlen; DIRNAME *n; extlen = 0; n = name; pextn = split( pattern ); namlen = nlength( n->name, 6 ); extlen = nlength( n->extension, 3 ); if ( !try( pattern, n->name, namlen, 6-non_expand(pattern,6) ) ) return( FALSE ); if ( pextn == NULL ) return( TRUE ); if ( try( pextn, n->extension, extlen, 3 -non_expand(pextn,3) ) ) return( TRUE ); return( FALSE ); } /* * Split returns a pointer to the extention part of . */ char *split( pattern ) register char *pattern; { while ( *pattern && *pattern != '.' ) pattern++ ; if ( *pattern ) return( ++pattern ); return( NULL ); } /* * nlength returns the number of contiguous non-period * characters starting at its pointer argument up to * a maximum of . */ nlength( sptr, lmax ) register char *sptr; register int lmax; { int count; count = 0; while ( *sptr && *sptr != ' ' && *sptr != '.' && count < lmax ) { sptr++ ; count++ ; } return( count ); } /* * non_expand returns the number of non-expansion charaters * (equal to the minimum length needed by a string to match * the pattern) of the pattern part starting at with * a maximum length . */ non_expand( sptr, lmax ) register char *sptr; int lmax; { int count; count = 0; while ( *sptr && *sptr != '.' && *sptr != ' ' && lmax-- ) { if ( *sptr != MATCHALL ) count++ ; sptr++ ; } return( count ); } /* * try matches trys to match with of length * knowing that it can expand MATCHALL characters to a maximum * of places. */ try( pattern, name, len, expand ) char *name; char *pattern; int len; int expand; { int i; while ( *pattern && *pattern != '.' && len +1 ) { switch ( *pattern ) { case MATCHALL: i = expand; while ( i+1 ) { if ( try( pattern+1, name+i, len-i, expand-i ) ) return( TRUE ); --i; } return( FALSE ); case MATCHONE: case MATCH_ONE: if ( !len ) return( FALSE ); break; default: if ( !len || *pattern != *name ) return( FALSE ); } pattern++ ; name++ ; --len ; } if ( *pattern && *pattern != '.' /* pattern not finished */ || len > 0 ) return( FALSE ); return( TRUE ); } /* * printpath prints out the "path" of the file named as * determined from the -Path- stack. */ printpath( ) /* char *name;*/ { int i; #ifdef WHITESMITH putfmt( "%p", Path[0] ); #else printf( "%s", Path[0] ); #endif for (i=1; i in a nice format. */ printname( name ) register char *name; { register int j, k; k = 0; for (j=0; j< DIR_NAMESIZE; j++) { if ( name[j] != ' ' ) putchar( name[j] ); else k++; /* count blanks */ if ( j == 5 ) /*extension part starts next*/ putchar('.'); } for (j=0; j12. */ static char *montbl[] = {"BAD", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; printdate (rtdate) register int rtdate; { int year, day; register int month; if (rtdate == 0) { printf (" -BAD- "); } else { year = (rtdate & 037) +1972; day = (rtdate >> 5) & 037; month = (rtdate >> 10) & 017; if (month > 12) month = 0; printf ("%2d-%s-%4d", day, montbl[month], year); } } /* * printtime prints the TSX file_time */ printtime(file_time) unsigned file_time; { register int hrs,min,sec; sec = (file_time % 20) * 3; /* file_time in 3-sec. ticks */ file_time /= 20; min = file_time % 60; hrs = file_time / 60; printf("%2d:%02d:%02d", hrs,min,sec); } /* * clear fills of byte size with '\0's. */ clear( buffer, size ) register char *buffer; register int size; { while( size-- ) *buffer++ = '\0'; } /* * enterstats puts the statistics <...> in the front of the list * pointed to by --Statlist--. */ enterstats( dirlevel, name, filecount, blockcount, freeblocks ) int dirlevel; char *name; long filecount; long blockcount; long freeblocks; { register STATNODE *ptr; ptr = makestatnode(); ptr->dirlevel = dirlevel; ptr->filecount = filecount; ptr->blockcount = blockcount; ptr->freeblocks = freeblocks; clear( ptr->fname, DSIZE ); scopy( ptr->fname, name ); ptr->next = Statlist; Statlist = ptr; } /* * printstats prints out a list of STATNODES starting with * . */ printstats( statptr ) register STATNODE *statptr; { while (statptr != NULL) { statprint( statptr->dirlevel, statptr->fname, statptr->filecount, statptr->blockcount, statptr->freeblocks ); statptr = statptr->next; } } /* * freestats clears the list of STATNODES pointed to by . */ freelist( statlist ) register STATNODE **statlist; { #ifdef WHITESMITH while ((*statlist = free(*statlist, (*statlist)->next)) != NULL ) ; #else register STATNODE *sptr; while ((sptr = *statlist) != NULL) { statlist = statlist->next; free( sptr ); } #endif } /* * makestatnode allocates space for a new STATNODE and returns * a pointer to it. */ STATNODE *makestatnode() { #ifdef UNIXLIKE return( malloc( sizeof( STATNODE ) ) ); #endif #ifdef WHITESMITH return( alloc( sizeof( STATNODE ), NULL ) ); #endif } /* * statprint prints , , and * in a nice format, using for indentation . */ statprint( dirlevel, name, filecount, blockcount, freeblocks ) int dirlevel; char *name; long filecount; long blockcount; long freeblocks; { register int i; char indent[LINESIZE]; register char *a; a = indent; for (i=0; i 0) printname( name ); else putfmt("%p", name ); putfmt(":\n%p%l files, %ul blocks\n%p%ul free blocks\n", indent, filecount, blockcount, indent, freeblocks ); #else printf("%s", indent); if (dirlevel > 0) printname( name ); else printf("%s", name); printf(":\n%s%ld files, %lu blocks\n%s%lu free blocks\n", indent, filecount, blockcount, indent, freeblocks ); #endif } /* * warn prints out and returns. */ warn( warning ) char *warning; { char warnbuf[LINESIZE]; scopy(warnbuf, "warning: " ); err( scat( warnbuf, warning ) ); } /* * err prints out . */ err( errormessage ) char *errormessage; { #ifdef WHITESMITH errfmt( "%p: %p\n", Cmdname, errormessage ); #else fprintf( stderr, "%s: %s\n", Cmdname, errormessage ); #endif } /* * fatal prints out and exits. */ fatal( errormessage ) char *errormessage; { err( errormessage ); exit(1); }