/* * 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 */ #define WHITESMITH #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 DEVICENAME "*.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 2048 #define EMPTY_FILE 512 #define PERMANENT_FILE 1024 #define PROTECTED -32768 typedef struct { int status; int filename[3]; /* name + extension */ int filelen; char jobno; char channelno; 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; unsigned long blockcount; unsigned long freeblocks; } STATNODE; #define RTyear(x) (( x & 037 ) + 1972) #define RTday(x) (( x >> 5 ) & 037) #define RTmonth(x) (( x >> 10 ) & 017) char *scat(); char *r50toascii(); DIR_ENTRY *mkentry(); DIR_ENTRY *getentry(); SEGMENT *readseg(); char *split(); long lseek(); /* for UNIXLIKE */ STATNODE *makestatnode(); char *Path[MAXLEVEL] = { NULL }; long Base[MAXLEVEL] = { NULL }; int Nextsegno[MAXLEVEL] = { 0 }; long Position[MAXLEVEL] = { NULL }; int Level = 0; char *Cmdname = " Dummy value for Whitesmith's C" ; char Usage[80] = { '\0' }; STATNODE *Statlist = NULL; int Rootfile = 0; 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 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[]; { char dev[DSIZE]; #ifdef WHITESMITH scopy( Cmdname, "dfind" ); #else Cmdname = argv[0]; /* save command name */ #endif scopy( Usage, "usage: " ); scat( Usage, Cmdname ); scat( Usage, "[-adpqv][ -f dfault ] [ [device][filename] ]..." ); while ( argc > 1 && ( *argv[1] == '-' || *argv[1] == '/' ) ) { /* get argument flags */ register char *vpt; 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 'v': case 'V': Vflag = TRUE; break; case 'q': case 'Q': Vflag = FALSE; 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 = open( dev, 0 ); #endif #ifdef WHITESMITH Rootfile = open( dev, 0, 1 ); #endif if ( Rootfile < 0 ) { 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 = 0L; unsigned long blockcount = 0L; unsigned long freeblocks = 0L; if ( ++Level >= MAXLEVEL ) { warn( "MAXLEVEL EXCEEDED--file skipped" ); --Level; return; } d_entry = NULL; Path[ Level -1 ] = dev; Base[Level] = baseblock; 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 */ printpath( name ); Found++ ; filecount++ ; blockcount += d_entry->filelen; } if ( d_entry->status == EMPTY_FILE ) freeblocks += d_entry->filelen; if ( nmatch( DEVICENAME, name ) ) search( Position[Level], name ); Position[Level] += d_entry->filelen; } if (Vflag) enterstats( Level-1, dev, filecount, blockcount, freeblocks ); --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; { static SEGMENT seghold[ MAXLEVEL ]; /* save data for each level */ register SEGMENT *seg; seg = &seghold[ 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 ) 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 ); } /* * readseg copies a directory segment starting * at block into . */ SEGMENT *readseg( block, seg ) long block; register SEGMENT *seg; { lseek( Rootfile, block * BLOCKSIZE, 0 ); if ( read( Rootfile, (char *)seg, sizeof(SEGMENT)) != sizeof(SEGMENT) ) 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 = *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; int extlen = 0; DIRNAME *n; 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 = 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 = 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( name ) 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; for (j=0; j< DIR_NAMESIZE; j++) { if ( name[j] != ' ' ) putchar( name[j] ); if ( j == 5 ) /*extension part starts next*/ putchar('.'); } } /* * 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; unsigned long blockcount; unsigned 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; unsigned long blockcount; unsigned long freeblocks; { register int i; char indent[LINESIZE]; register char *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 warn[LINESIZE]; scopy(warn, "warning: " ); err( scat( warn, 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); }