/* * fdf.c * * find duplicates. searches a given path and its sub-directories for * duplicate files. Duplicate files have the same name, size, date and * contents. However, the definition used by this program can be almost any * user-specified combination of the above. * * Roy Bixler (original development and Atari ST version, maintenance) * Ayman Barakat (idea) * David Oertel (MS-DOS version) * * Version 1.0: March 11, 1991 (known as 'mfd - Monk find duplicates') * Version 1.01: April 12, 1992 (now 'fdf - find duplicate files') * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include "fdfcomm.h" #include "fdfstat.h" #include "elib.h" #include "fdf.h" HASH_LIST *H_list[HASH_TAB_SIZE]; /* * print_help * * prints out the help message for the program */ void print_help() { printf(FDF_USAGE, PROG_NAME, PROG_NAME); } /* * show_doc * * show full documentation */ void show_doc() { printf(FDF_SCHPIEL); } /* * add_to_hash_table * * given a file and a hash table index, add the given file to the hash table * at the given index. Addition is done by putting the new entry at the * head of a linked list. */ void add_to_hash_table(FILE_LIST *f_name, int h_idx) { HASH_LIST *new_entry = malloc((size_t) sizeof(HASH_LIST)); if (new_entry != NULL) { new_entry->f_name = f_name; new_entry->f_name->printed = 0; new_entry->next = H_list[h_idx]; H_list[h_idx] = new_entry; } } /* * gen_hash * * given a linked list repesenting a list of files, generate a hash table for * it. */ void gen_hash(FILE_LIST *flist) { for (;flist != NULL; flist = flist->next) { if (!flist->added) { if (v_flag) { update_total_bytes(flist->dta.dta_size); update_num_files(); } if ((Match_criteria) & (NAMES_MATCH)) add_to_hash_table(flist, hashpjw(flist->dta.dta_name)); else if ((Match_criteria) & (SIZES_MATCH)) add_to_hash_table(flist, flist->dta.dta_size % HASH_TAB_SIZE); else if ((Match_criteria) & (TIMES_MATCH)) add_to_hash_table(flist, ((unsigned long) (flist->dta.dta_date << 16) + (unsigned long) (flist->dta.dta_time)) % HASH_TAB_SIZE); flist->added = 1; } } } /* * find_duplicated_name * * scans the generated hash table for names which occur twice (or more). * Takes a pointer to hash table index to start the search at, modifies this * on return to indicate where it stopped looking (either because of end of * hash table or because a duplicated name found). Return pointer to first * occurrence of duplicated name found or NULL if none found. */ HASH_LIST *find_duplicated_name(int *h_idx, HASH_LIST *last_found) { int i; HASH_LIST *anchor, *cur; anchor = last_found; for (i = *h_idx; i < HASH_TAB_SIZE; i++) if (H_list[i] != NULL) { if (anchor == NULL) anchor = H_list[i]; for (; anchor != NULL; anchor = anchor->next) if (!anchor->f_name->printed) for (cur = anchor->next; cur != NULL; cur = cur->next) if ((!cur->f_name->printed) && (cmpflist_eq(anchor->f_name, cur->f_name))) { *h_idx = i; return anchor; } } *h_idx = i; return NULL; } /* * gen_id_menu * * given a starting point in the file list, generate an interactive delete * menu. Return number of items put into the menu. */ int gen_id_menu(HASH_LIST *name_duped, FILE_LIST **menu, int max_items) { int n_found = 0; long n_bytes = 0L; HASH_LIST *cur; for (cur = name_duped->next; ((cur != NULL) && (n_found < max_items)); cur = cur->next) if ((!cur->f_name->printed) && (files_match(name_duped->f_name, cur->f_name))) { if (n_found == 0) { if (v_flag) n_bytes += name_duped->f_name->dta.dta_size; menu[n_found++] = name_duped->f_name; } if (v_flag) n_bytes += cur->f_name->dta.dta_size; menu[n_found++] = cur->f_name; } if ((n_found) && (v_flag)) { update_num_which_dupd(); update_num_dups(n_found, n_bytes); } return n_found; } /* * id_dups * * given a pointer to a name which has been determined to be duplicated, print * all the names and their path's out and ask the user which ones to delete. */ void id_dups(HASH_LIST *start) { int n_found, i, n_del; FILE_LIST *cur, *menu[N_INTERACTIVE]; char menu_sel[MAX_STR], which_del[N_INTERACTIVE]; if (!(n_found = gen_id_menu(start, menu, N_INTERACTIVE))) return; while (1) { print_id_menu(menu, n_found); printf("\nEnter list of files to delete (hit CR for none)\n"); fgets(menu_sel, MAX_STR-1, stdin); zap_trailing_nl(menu_sel, MAX_STR-1, stdin); if (!mark_list(menu_sel, which_del, n_found)) { for (n_del=0, i=0; i < n_found; i++) if (which_del[i]) if (!delete_path_name_file(menu[i]->path, menu[i]->dta.dta_name, '\0')) { n_del++; if (v_flag) { update_total_del_bytes(menu[i]->dta.dta_size); if (v_flag > 1) { printf("Deleted "); print_fpath(menu[i]->path, menu[i]->dta.dta_name); printf("\n"); } } } break; } } if (n_del) printf("\n"); } /* * print_dups * * given a pointer to a name which has been determined to be duplicated, print * all the names and their path's out. */ void print_dups(HASH_LIST *name_duped) /* now, who's being duped here? */ { HASH_LIST *cur; for (cur = name_duped->next; cur != NULL; cur = cur->next) if ((!cur->f_name->printed) && (files_match(name_duped->f_name, cur->f_name))) { if (!name_duped->f_name->printed) { if (v_flag) { update_num_which_dupd(); update_num_dups(1U, name_duped->f_name->dta.dta_size); } print_match_header(name_duped->f_name); print_next_match(name_duped->f_name, -1); } if (v_flag) update_num_dups(1U, cur->f_name->dta.dta_size); print_next_match(cur->f_name, -1); } if (name_duped->f_name->printed) printf("\n"); } /* * find_non_printed * * given a pointer to a FILE_LIST, return the pointer to the next element which * has not been printed yet. */ HASH_LIST *find_non_printed(HASH_LIST *file) { for (; ((file != NULL) && (file->f_name->printed)); file = file->next); return file; } /* * find_dups * * given a path, find the duplicate files and dump them to the standard output. */ void find_dups() { HASH_LIST *last_found, *f_found; int i; gen_hash(F_list); i = 0; last_found = NULL; while ((i < HASH_TAB_SIZE) && ((f_found = find_duplicated_name(&i, last_found)) != NULL)) { if (i_flag) id_dups(f_found); else print_dups(f_found); last_found = find_non_printed(f_found->next); } } /* * init_hash * * insure the hash table is empty */ void init_hash() { int i; for (i=0; i 1) printf("finding duplicates under %s:\n\n", form_path); list_files(PROG_NAME, form_path, (char) 0); } find_dups(); if (v_flag) print_stats(); exit(0); }