Listing 2 (no_dup.c)
/* Find multiple copies of the same file on a single disk drive. Compiled under Microsoft C 6.0 using: cl /Fs /W4 /Ox /AC no_dup.c Compiles without modifications undder TURBO C integrated environment. In both cases requires compact memory model libraries. */ #include "no_dup.h" Flag first_call = True; GlobalOpt globalopt = { False, False, 0, 0L }; int main( int argc, char **argv ) { DirEntry *current_file, *next_file; DirList *path, *root, *new_path; char current_path[MAX_PATH], file_spec[MAX_FLEN]; PtrList *ptrbase, *ptrlist; Flag duplicate = False; for( ; argc > 1; argc-- ) { if(argv[argc-1][1] == 'f' || argv[argc-1][1] == 'F') globalopt.file = True; else if( argv[argc-1][1] == 'd' || argv[argc-1] [1] == 'D' ) globalopt.dir = True; else fprintf( stderr, "Invalid command line switch: %s\n", argv[argc-l] ); } strcpy( file_spec, "*.*" ); current_file = NULL; (void) getcwd( current_path, MAX_PATH ); fprintf( stderr, "%s\n", VERSION ); printf( "Processing %s\n", current_path ); path = make_path( current_path, "", NULL ); new_path = root = path; if( (ptrbase = malloc( sizeof( PtrList ) * MAX_FILES )) == NULL ) { fprintf( stderr, "Insufficient near memory.\n" ); exit( EXIT_FAILURE ); } ptrlist = ptrbase; while( (path = get_path( root ) ) != NULL ) { strcpy( current_path, path->pathname ); strcat( current_path, file_spec ); first_call = True; while( (current_file = get_direntry(current_path)) ! = NULL ) { current_file->path = path->pathname; if( current_file->attrib & FA_DIREC ) { new_path = make_path( path->pathname, current_file->name, new_path ); } else { ++globalopt.file_count; globalopt.total_file_size += current_file->size; if( globalopt.file_count == MAX_FILES ) { fprintf( stderr, "Too many files\n" "Program is limited to %u files.\n", MAX_FILES ); exit( EXIT_FAILURE ); } *ptrlist = ( char * ) current_file; ++ptrlist; } } path->dir_processed = True; } *ptrlist = NULL; printf( "Processed %d files occupying %lu bytes.\n", globalopt.file_count, globalopt.total_file_size ); qsort( (void *) ptrbase, (size_t) globalopt.file_count, (size_t) sizeof( PtrList ), name_comp ); if( globalopt.file ) /* optionally list files */ { printf( SEPARATOR ); printf( "Listing of all files below %s\n",root); for( ptrlist = ptrbase; *ptrlist; ptrlist++ ) fprint_direntry( stdout, (DirEntry *) *ptrlist ); } printf( SEPARATOR ); printf( "Duplicate files below %s\n", root ); for( ptrlist = ptrbase, duplicate = False; *ptrlist; ptrlist++ ) { current_file = ( DirEntry * ) *ptrlist; next_file = ( DirEntry * ) *(ptrlist+1); if( !strcmp(current_file->name,next_file->name)) { duplicate = True; fprint_direntry( stdout, current_file ); { else if( duplicate == True ) { /* print the last file in a group */ /* otherwise we'll miss it */ current_file = ( DirEntry * ) *ptrlist; fprint_direntry( stdout, current_file ); printf( SEPARATOR ); duplicate = False; } } return EXIT_SUCCESS; } /* get file info from DOS directory and allocate memory */ DirEntry *get_direntry( char *path ) { static struct ffblk file_info; DirEntry *new_entry; if( first_call == True ) { if( findfirst( path, &file_info, FA_DIREC ) ) return NULL; first_call = False; } else { if( findnext( &file_info ) ) { return NULL; } } if( (new_entry = malloc(sizeof(DirEntry))) == NULL ) { fprintf( stderr, "Insufficient far memory.\n" ); exit( EXIT_FAILURE ); } strcpy( new_entry->name, file_info.ff_name ); new_entry->attrib = file_info.ff_attrib; if( new_entry->attrib & FA_DIREC ) new_entry->dir_processed = False; new_entry->time = file_info.ff_ftime; new_entry->date = file_info.ff_fdate; new_entry->size = file_info.ff_fsize; return new_entry; } /* build a directory path by appending subdir to basedir */ DirList *make_path( char *basedir, char *subdir, DirList *prev ) { DirList *new_dir; if( (new_dir = malloc( sizeof( DirList ) ) ) == NULL ) { fprintf( stderr, "Insufficient near memory.\n" ); exit( EXIT_FAILURE ); } strcpy( new_dir->pathname, basedir ); strcat( new_dir->pathname, subdir ); if( *(new_dir->pathname + strlen(new_dir->pathname) - 1) != '\\' ) strcat( new_dir->pathname, "\\" ); if( strcmp( subdir, "." ) && strcmp( subdir, ".." ) ) { /* only "real" directories meet the condition */ new_dir->dir_processed = False; if( globalopt.dir ) printf( "Directory:%s\n", new_dir->pathname ); } else new_dir->dir_processed = True; if( prev ) { new_dir->prev = prev; new_dir->next = NULL; prev->next = new_dir; } else { new_dir->prev = NULL; new_dir->next = NULL; } return new_dir; } /* find an unprocessed directory */ DirList *get_path( DirList *root ) { DirList *path; path = root; while( path->next && (path->dir_processed == True) ) { path = path->next; } if( path->dir_processed == True) return NULL; else return path; } /* DirEntry comparison function */ int name_comp( const void *p1, const void *p2 ) { PtrList *ptr1, *ptr2; DirEntry *f1, *f2; int status; ptr1 = (PtrList *) p1; ptr2 = (PtrList *) p2; f1 = (DirEntry *) *ptr1; f2 = (DirEntry *) *ptr2; if( status = strcmp( f1->name, f2->name )) return( status ); else if( status = f1->date - f2->date ) return( status ); else if( status = f1->time - f2->time ) return( status ); else return( strcmp( f1->path, f2->path ) ); } /* print file information to the ouptut stream */ void fprint_direntry( FILE *fout, DirEntry *current_file ) { char date_buf[10], time_buf[10]; fprintf( fout, "%-14s %6ld %c%c %s %s %s\n", current_file->name, current_file->size, (current_file->attrib & FA_RDONLY ) ? 'R' : '.', (current_file->attrib & FA_ARCH ) ? 'A' : '.', datestr( current_file->date, date_buf ), timestr( current_file->time, time_buf ), current_file->path ); } /* The following functions are copied from QC 2.0 online help. These functions convert wr_time and wr_date into strings. */ char *timestr( unsigned t, char *buf ) { int h = (t >> 11) & 0x1f, m = (t >> 5) & 0x3f; sprintf( buf, "%2.2d:%02.2d", h, m ); return buf; } char *datestr( unsigned d, char *buf ) { sprintf( buf, "%2.2d/%02.2d/%02.2d", (d >> 5) & 0x0f, d & 0x1f, (d >> 9) + 80 ); return buf; }