fdupes  2.1.2
About: FDUPES finds duplicate files in a given set of directories.
  Fossies Dox: fdupes-2.1.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

fdupes.c
Go to the documentation of this file.
1 /* FDUPES Copyright (c) 1999-2018 Adrian Lopez
2 
3  Permission is hereby granted, free of charge, to any person
4  obtaining a copy of this software and associated documentation files
5  (the "Software"), to deal in the Software without restriction,
6  including without limitation the rights to use, copy, modify, merge,
7  publish, distribute, sublicense, and/or sell copies of the Software,
8  and to permit persons to whom the Software is furnished to do so,
9  subject to the following conditions:
10 
11  The above copyright notice and this permission notice shall be
12  included in all copies or substantial portions of the Software.
13 
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
21 
22 #include "config.h"
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <string.h>
26 #include <strings.h>
27 #include <sys/stat.h>
28 #include <dirent.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <time.h>
32 #ifdef HAVE_GETOPT_H
33 #include <getopt.h>
34 #endif
35 #include <errno.h>
36 #include <libgen.h>
37 #include <locale.h>
38 #ifndef NO_NCURSES
39 #ifdef HAVE_NCURSESW_CURSES_H
40  #include <ncursesw/curses.h>
41 #else
42  #include <curses.h>
43 #endif
44 #include "ncurses-interface.h"
45 #endif
46 #include "fdupes.h"
47 #include "errormsg.h"
48 #include "log.h"
49 #include "sigint.h"
50 #include "flags.h"
51 
52 long long minsize = -1;
53 long long maxsize = -1;
54 
55 typedef enum {
60 
62 
64 
65 #define CHUNK_SIZE 8192
66 
67 #define INPUT_SIZE 256
68 
69 #define PARTIAL_MD5_SIZE 4096
70 
71 #define MD5_DIGEST_LENGTH 16
72 
73 /*
74 
75 TODO: Partial sums (for working with very large files).
76 
77 typedef struct _signature
78 {
79  md5_state_t state;
80  md5_byte_t digest[16];
81 } signature_t;
82 
83 typedef struct _signatures
84 {
85  int num_signatures;
86  signature_t *signatures;
87 } signatures_t;
88 
89 */
90 
91 typedef struct _filetree {
93  struct _filetree *left;
94  struct _filetree *right;
96 
97 void escapefilename(char *escape_list, char **filename_ptr)
98 {
99  int x;
100  int tx;
101  char *tmp;
102  char *filename;
103 
104  filename = *filename_ptr;
105 
106  tmp = (char*) malloc(strlen(filename) * 2 + 1);
107  if (tmp == NULL) {
108  errormsg("out of memory!\n");
109  exit(1);
110  }
111 
112  for (x = 0, tx = 0; x < strlen(filename); x++) {
113  if (strchr(escape_list, filename[x]) != NULL) tmp[tx++] = '\\';
114  tmp[tx++] = filename[x];
115  }
116 
117  tmp[tx] = '\0';
118 
119  if (x != tx) {
120  *filename_ptr = realloc(*filename_ptr, strlen(tmp) + 1);
121  if (*filename_ptr == NULL) {
122  errormsg("out of memory!\n");
123  exit(1);
124  }
125  strcpy(*filename_ptr, tmp);
126  }
127 }
128 
129 dev_t getdevice(char *filename) {
130  struct stat s;
131 
132  if (stat(filename, &s) != 0) return 0;
133 
134  return s.st_dev;
135 }
136 
137 ino_t getinode(char *filename) {
138  struct stat s;
139 
140  if (stat(filename, &s) != 0) return 0;
141 
142  return s.st_ino;
143 }
144 
145 char *fmttime(time_t t) {
146  static char buf[64];
147 
148  strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M", localtime(&t));
149 
150  return buf;
151 }
152 
153 char **cloneargs(int argc, char **argv)
154 {
155  int x;
156  char **args;
157 
158  args = (char **) malloc(sizeof(char*) * argc);
159  if (args == NULL) {
160  errormsg("out of memory!\n");
161  exit(1);
162  }
163 
164  for (x = 0; x < argc; x++) {
165  args[x] = (char*) malloc(strlen(argv[x]) + 1);
166  if (args[x] == NULL) {
167  free(args);
168  errormsg("out of memory!\n");
169  exit(1);
170  }
171 
172  strcpy(args[x], argv[x]);
173  }
174 
175  return args;
176 }
177 
178 int findarg(char *arg, int start, int argc, char **argv)
179 {
180  int x;
181 
182  for (x = start; x < argc; x++)
183  if (strcmp(argv[x], arg) == 0)
184  return x;
185 
186  return x;
187 }
188 
189 /* Find the first non-option argument after specified option. */
190 int nonoptafter(char *option, int argc, char **oldargv,
191  char **newargv, int optind)
192 {
193  int x;
194  int targetind;
195  int testind;
196  int startat = 1;
197 
198  targetind = findarg(option, 1, argc, oldargv);
199 
200  for (x = optind; x < argc; x++) {
201  testind = findarg(newargv[x], startat, argc, oldargv);
202  if (testind > targetind) return x;
203  else startat = testind;
204  }
205 
206  return x;
207 }
208 
209 void getfilestats(file_t *file, struct stat *info, struct stat *linfo)
210 {
211  file->size = info->st_size;;
212  file->inode = info->st_ino;
213  file->device = info->st_dev;
214  file->ctime = info->st_ctime;
215  file->mtime = info->st_mtime;
216 }
217 
218 int grokdir(char *dir, file_t **filelistp, struct stat *logfile_status)
219 {
220  DIR *cd;
221  file_t *newfile;
222  struct dirent *dirinfo;
223  int lastchar;
224  int filecount = 0;
225  struct stat info;
226  struct stat linfo;
227  static int progress = 0;
228  static char indicator[] = "-\\|/";
229  char *fullname, *name;
230 
231  cd = opendir(dir);
232 
233  if (!cd) {
234  errormsg("could not chdir to %s\n", dir);
235  return 0;
236  }
237 
238  while ((dirinfo = readdir(cd)) != NULL) {
239  if (strcmp(dirinfo->d_name, ".") && strcmp(dirinfo->d_name, "..")) {
240  if (!ISFLAG(flags, F_HIDEPROGRESS)) {
241  fprintf(stderr, "\rBuilding file list %c ", indicator[progress]);
242  progress = (progress + 1) % 4;
243  }
244 
245  newfile = (file_t*) malloc(sizeof(file_t));
246 
247  if (!newfile) {
248  errormsg("out of memory!\n");
249  closedir(cd);
250  exit(1);
251  } else newfile->next = *filelistp;
252 
253  newfile->device = 0;
254  newfile->inode = 0;
255  newfile->crcsignature = NULL;
256  newfile->crcpartial = NULL;
257  newfile->duplicates = NULL;
258  newfile->hasdupes = 0;
259 
260  newfile->d_name = (char*)malloc(strlen(dir)+strlen(dirinfo->d_name)+2);
261 
262  if (!newfile->d_name) {
263  errormsg("out of memory!\n");
264  free(newfile);
265  closedir(cd);
266  exit(1);
267  }
268 
269  strcpy(newfile->d_name, dir);
270  lastchar = strlen(dir) - 1;
271  if (lastchar >= 0 && dir[lastchar] != '/')
272  strcat(newfile->d_name, "/");
273  strcat(newfile->d_name, dirinfo->d_name);
274 
275  if (ISFLAG(flags, F_EXCLUDEHIDDEN)) {
276  fullname = strdup(newfile->d_name);
277  if (fullname == 0)
278  {
279  errormsg("out of memory!\n");
280  free(newfile);
281  closedir(cd);
282  exit(1);
283  }
284  name = basename(fullname);
285  if (name[0] == '.' && strcmp(name, ".") && strcmp(name, "..") ) {
286  free(newfile->d_name);
287  free(newfile);
288  free(fullname);
289  continue;
290  }
291  free(fullname);
292  }
293 
294  if (stat(newfile->d_name, &info) == -1) {
295  free(newfile->d_name);
296  free(newfile);
297  continue;
298  }
299 
300  if (!S_ISDIR(info.st_mode) && (((info.st_size == 0 && ISFLAG(flags, F_EXCLUDEEMPTY)) || info.st_size < minsize || (info.st_size > maxsize && maxsize != -1)))) {
301  free(newfile->d_name);
302  free(newfile);
303  continue;
304  }
305 
306  /* ignore logfile */
307  if (info.st_dev == logfile_status->st_dev && info.st_ino == logfile_status->st_ino)
308  {
309  free(newfile->d_name);
310  free(newfile);
311  continue;
312  }
313 
314  if (lstat(newfile->d_name, &linfo) == -1) {
315  free(newfile->d_name);
316  free(newfile);
317  continue;
318  }
319 
320  if (S_ISDIR(info.st_mode)) {
321  if (ISFLAG(flags, F_RECURSE) && (ISFLAG(flags, F_FOLLOWLINKS) || !S_ISLNK(linfo.st_mode)))
322  filecount += grokdir(newfile->d_name, filelistp, logfile_status);
323  free(newfile->d_name);
324  free(newfile);
325  } else {
326  if (S_ISREG(linfo.st_mode) || (S_ISLNK(linfo.st_mode) && ISFLAG(flags, F_FOLLOWLINKS))) {
327  getfilestats(newfile, &info, &linfo);
328  *filelistp = newfile;
329  filecount++;
330  } else {
331  free(newfile->d_name);
332  free(newfile);
333  }
334  }
335  }
336  }
337 
338  closedir(cd);
339 
340  return filecount;
341 }
342 
343 md5_byte_t *getcrcsignatureuntil(char *filename, off_t fsize, off_t max_read)
344 {
345  off_t toread;
346  md5_state_t state;
347  static md5_byte_t digest[MD5_DIGEST_LENGTH];
348  static md5_byte_t chunk[CHUNK_SIZE];
349  FILE *file;
350 
351  md5_init(&state);
352 
353  if (max_read != 0 && fsize > max_read)
354  fsize = max_read;
355 
356  file = fopen(filename, "rb");
357  if (file == NULL) {
358  errormsg("error opening file %s\n", filename);
359  return NULL;
360  }
361 
362  while (fsize > 0) {
363  toread = (fsize >= CHUNK_SIZE) ? CHUNK_SIZE : fsize;
364  if (fread(chunk, toread, 1, file) != 1) {
365  errormsg("error reading from file %s\n", filename);
366  fclose(file);
367  return NULL;
368  }
369  md5_append(&state, chunk, toread);
370  fsize -= toread;
371  }
372 
373  md5_finish(&state, digest);
374 
375  fclose(file);
376 
377  return digest;
378 }
379 
380 md5_byte_t *getcrcsignature(char *filename, off_t fsize)
381 {
382  return getcrcsignatureuntil(filename, fsize, 0);
383 }
384 
385 md5_byte_t *getcrcpartialsignature(char *filename, off_t fsize)
386 {
387  return getcrcsignatureuntil(filename, fsize, PARTIAL_MD5_SIZE);
388 }
389 
390 int md5cmp(const md5_byte_t *a, const md5_byte_t *b)
391 {
392  int x;
393 
394  for (x = 0; x < MD5_DIGEST_LENGTH; ++x)
395  {
396  if (a[x] < b[x])
397  return -1;
398  else if (a[x] > b[x])
399  return 1;
400  }
401 
402  return 0;
403 }
404 
405 void md5copy(md5_byte_t *to, const md5_byte_t *from)
406 {
407  int x;
408 
409  for (x = 0; x < MD5_DIGEST_LENGTH; ++x)
410  to[x] = from[x];
411 }
412 
413 void purgetree(filetree_t *checktree)
414 {
415  if (checktree->left != NULL) purgetree(checktree->left);
416 
417  if (checktree->right != NULL) purgetree(checktree->right);
418 
419  free(checktree);
420 }
421 
422 int registerfile(filetree_t **branch, file_t *file)
423 {
424  *branch = (filetree_t*) malloc(sizeof(filetree_t));
425  if (*branch == NULL) {
426  errormsg("out of memory!\n");
427  exit(1);
428  }
429 
430  (*branch)->file = file;
431  (*branch)->left = NULL;
432  (*branch)->right = NULL;
433 
434  return 1;
435 }
436 
437 int same_permissions(char* name1, char* name2)
438 {
439  struct stat s1, s2;
440 
441  if (stat(name1, &s1) != 0) return -1;
442  if (stat(name2, &s2) != 0) return -1;
443 
444  return (s1.st_mode == s2.st_mode &&
445  s1.st_uid == s2.st_uid &&
446  s1.st_gid == s2.st_gid);
447 }
448 
449 int is_hardlink(filetree_t *checktree, file_t *file)
450 {
451  file_t *dupe;
452 
453  if ((file->inode == checktree->file->inode) &&
454  (file->device == checktree->file->device))
455  return 1;
456 
457  if (checktree->file->hasdupes)
458  {
459  dupe = checktree->file->duplicates;
460 
461  do {
462  if ((file->inode == dupe->inode) &&
463  (file->device == dupe->device))
464  return 1;
465 
466  dupe = dupe->duplicates;
467  } while (dupe != NULL);
468  }
469 
470  return 0;
471 }
472 
473 /* check whether two paths represent the same file (deleting one would delete the other) */
474 int is_same_file(file_t *file_a, file_t *file_b)
475 {
476  char *filename_a;
477  char *filename_b;
478  char *dirname_a;
479  char *dirname_b;
480  char *basename_a;
481  char *basename_b;
482  struct stat dirstat_a;
483  struct stat dirstat_b;
484 
485  /* if files on different devices and/or different inodes, they are not the same file */
486  if (file_a->device != file_b->device || file_a->inode != file_b->inode)
487  return 0;
488 
489  /* copy filenames (basename and dirname may modify these) */
490  filename_a = strdup(file_a->d_name);
491  if (filename_a == 0)
492  return -1;
493 
494  filename_b = strdup(file_b->d_name);
495  if (filename_b == 0)
496  return -1;
497 
498  /* get file basenames */
499  basename_a = basename(filename_a);
500  memmove(filename_a, basename_a, strlen(basename_a) + 1);
501 
502  basename_b = basename(filename_b);
503  memmove(filename_b, basename_b, strlen(basename_b) + 1);
504 
505  /* if files have different names, they are not the same file */
506  if (strcmp(filename_a, filename_b) != 0)
507  {
508  free(filename_b);
509  free(filename_a);
510  return 0;
511  }
512 
513  /* restore paths */
514  strcpy(filename_a, file_a->d_name);
515  strcpy(filename_b, file_b->d_name);
516 
517  /* get directory names */
518  dirname_a = dirname(filename_a);
519  if (stat(dirname_a, &dirstat_a) != 0)
520  {
521  free(filename_b);
522  free(filename_a);
523  return -1;
524  }
525 
526  dirname_b = dirname(filename_b);
527  if (stat(dirname_b, &dirstat_b) != 0)
528  {
529  free(filename_b);
530  free(filename_a);
531  return -1;
532  }
533 
534  free(filename_b);
535  free(filename_a);
536 
537  /* if directories on which files reside are different, they are not the same file */
538  if (dirstat_a.st_dev != dirstat_b.st_dev || dirstat_a.st_ino != dirstat_b.st_ino)
539  return 0;
540 
541  /* same device, inode, filename, and directory; therefore, same file */
542  return 1;
543 }
544 
545 /* check whether given tree node already contains a copy of given file */
546 int has_same_file(filetree_t *checktree, file_t *file)
547 {
548  file_t *dupe;
549 
550  if (is_same_file(checktree->file, file))
551  return 1;
552 
553  if (checktree->file->hasdupes)
554  {
555  dupe = checktree->file->duplicates;
556 
557  do {
558  if (is_same_file(dupe, file))
559  return 1;
560 
561  dupe = dupe->duplicates;
562  } while (dupe != NULL);
563  }
564 
565  return 0;
566 }
567 
568 file_t **checkmatch(filetree_t **root, filetree_t *checktree, file_t *file)
569 {
570  int cmpresult;
571  md5_byte_t *crcsignature;
572 
574  {
575  /* If node already contains file, we don't want to add it again.
576  */
577  if (has_same_file(checktree, file))
578  return NULL;
579  }
580  else
581  {
582  /* If device and inode fields are equal one of the files is a
583  hard link to the other or the files have been listed twice
584  unintentionally. We don't want to flag these files as
585  duplicates unless the user specifies otherwise.
586  */
587  if (is_hardlink(checktree, file))
588  return NULL;
589  }
590 
591  if (file->size < checktree->file->size)
592  cmpresult = -1;
593  else
594  if (file->size > checktree->file->size) cmpresult = 1;
595  else
596  if (ISFLAG(flags, F_PERMISSIONS) &&
597  !same_permissions(file->d_name, checktree->file->d_name))
598  cmpresult = -1;
599  else {
600  if (checktree->file->crcpartial == NULL) {
601  crcsignature = getcrcpartialsignature(checktree->file->d_name, checktree->file->size);
602  if (crcsignature == NULL) {
603  errormsg ("cannot read file %s\n", checktree->file->d_name);
604  return NULL;
605  }
606 
607  checktree->file->crcpartial = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t));
608  if (checktree->file->crcpartial == NULL) {
609  errormsg("out of memory\n");
610  exit(1);
611  }
612  md5copy(checktree->file->crcpartial, crcsignature);
613  }
614 
615  if (file->crcpartial == NULL) {
616  crcsignature = getcrcpartialsignature(file->d_name, file->size);
617  if (crcsignature == NULL) {
618  errormsg ("cannot read file %s\n", file->d_name);
619  return NULL;
620  }
621 
622  file->crcpartial = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t));
623  if (file->crcpartial == NULL) {
624  errormsg("out of memory\n");
625  exit(1);
626  }
627  md5copy(file->crcpartial, crcsignature);
628  }
629 
630  cmpresult = md5cmp(file->crcpartial, checktree->file->crcpartial);
631  /*if (cmpresult != 0) errormsg(" on %s vs %s\n", file->d_name, checktree->file->d_name);*/
632 
633  if (cmpresult == 0) {
634  if (checktree->file->crcsignature == NULL) {
635  crcsignature = getcrcsignature(checktree->file->d_name, checktree->file->size);
636  if (crcsignature == NULL) return NULL;
637 
638  checktree->file->crcsignature = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t));
639  if (checktree->file->crcsignature == NULL) {
640  errormsg("out of memory\n");
641  exit(1);
642  }
643  md5copy(checktree->file->crcsignature, crcsignature);
644  }
645 
646  if (file->crcsignature == NULL) {
647  crcsignature = getcrcsignature(file->d_name, file->size);
648  if (crcsignature == NULL) return NULL;
649 
650  file->crcsignature = (md5_byte_t*) malloc(MD5_DIGEST_LENGTH * sizeof(md5_byte_t));
651  if (file->crcsignature == NULL) {
652  errormsg("out of memory\n");
653  exit(1);
654  }
655  md5copy(file->crcsignature, crcsignature);
656  }
657 
658  cmpresult = md5cmp(file->crcsignature, checktree->file->crcsignature);
659  /*if (cmpresult != 0) errormsg("P on %s vs %s\n",
660  file->d_name, checktree->file->d_name);
661  else errormsg("P F on %s vs %s\n", file->d_name,
662  checktree->file->d_name);
663  printf("%s matches %s\n", file->d_name, checktree->file->d_name);*/
664  }
665  }
666 
667  if (cmpresult < 0) {
668  if (checktree->left != NULL) {
669  return checkmatch(root, checktree->left, file);
670  } else {
671  registerfile(&(checktree->left), file);
672  return NULL;
673  }
674  } else if (cmpresult > 0) {
675  if (checktree->right != NULL) {
676  return checkmatch(root, checktree->right, file);
677  } else {
678  registerfile(&(checktree->right), file);
679  return NULL;
680  }
681  } else
682  {
683  return &checktree->file;
684  }
685 }
686 
687 /* Do a bit-for-bit comparison in case two different files produce the
688  same signature. Unlikely, but better safe than sorry. */
689 
690 int confirmmatch(FILE *file1, FILE *file2)
691 {
692  unsigned char c1[CHUNK_SIZE];
693  unsigned char c2[CHUNK_SIZE];
694  size_t r1;
695  size_t r2;
696 
697  fseek(file1, 0, SEEK_SET);
698  fseek(file2, 0, SEEK_SET);
699 
700  do {
701  r1 = fread(c1, sizeof(unsigned char), sizeof(c1), file1);
702  r2 = fread(c2, sizeof(unsigned char), sizeof(c2), file2);
703 
704  if (r1 != r2) return 0; /* file lengths are different */
705  if (memcmp (c1, c2, r1)) return 0; /* file contents are different */
706  } while (r2);
707 
708  return 1;
709 }
710 
712 {
713  int numsets = 0;
714  double numbytes = 0.0;
715  int numfiles = 0;
716  file_t *tmpfile;
717 
718  while (files != NULL)
719  {
720  if (files->hasdupes)
721  {
722  numsets++;
723 
724  tmpfile = files->duplicates;
725  while (tmpfile != NULL)
726  {
727  numfiles++;
728  numbytes += files->size;
729  tmpfile = tmpfile->duplicates;
730  }
731  }
732 
733  files = files->next;
734  }
735 
736  if (numsets == 0)
737  printf("No duplicates found.\n\n");
738  else
739  {
740  if (numbytes < 1024.0)
741  printf("%d duplicate files (in %d sets), occupying %.0f bytes.\n\n", numfiles, numsets, numbytes);
742  else if (numbytes <= (1000.0 * 1000.0))
743  printf("%d duplicate files (in %d sets), occupying %.1f kilobytes\n\n", numfiles, numsets, numbytes / 1000.0);
744  else
745  printf("%d duplicate files (in %d sets), occupying %.1f megabytes\n\n", numfiles, numsets, numbytes / (1000.0 * 1000.0));
746 
747  }
748 }
749 
750 void printmatches(file_t *files)
751 {
752  file_t *tmpfile;
753 
754  while (files != NULL) {
755  if (files->hasdupes) {
756  if (!ISFLAG(flags, F_OMITFIRST)) {
757  if (ISFLAG(flags, F_SHOWSIZE)) printf("%lld byte%seach:\n", (long long int)files->size,
758  (files->size != 1) ? "s " : " ");
759  if (ISFLAG(flags, F_SHOWTIME))
760  printf("%s ", fmttime(files->mtime));
761  if (ISFLAG(flags, F_DSAMELINE)) escapefilename("\\ ", &files->d_name);
762  printf("%s%c", files->d_name, ISFLAG(flags, F_DSAMELINE)?' ':'\n');
763  }
764  tmpfile = files->duplicates;
765  while (tmpfile != NULL) {
766  if (ISFLAG(flags, F_SHOWTIME))
767  printf("%s ", fmttime(tmpfile->mtime));
768  if (ISFLAG(flags, F_DSAMELINE)) escapefilename("\\ ", &tmpfile->d_name);
769  printf("%s%c", tmpfile->d_name, ISFLAG(flags, F_DSAMELINE)?' ':'\n');
770  tmpfile = tmpfile->duplicates;
771  }
772  printf("\n");
773 
774  }
775 
776  files = files->next;
777  }
778 }
779 
780 /*
781 #define REVISE_APPEND "_tmp"
782 char *revisefilename(char *path, int seq)
783 {
784  int digits;
785  char *newpath;
786  char *scratch;
787  char *dot;
788 
789  digits = numdigits(seq);
790  newpath = malloc(strlen(path) + strlen(REVISE_APPEND) + digits + 1);
791  if (!newpath) return newpath;
792 
793  scratch = malloc(strlen(path) + 1);
794  if (!scratch) return newpath;
795 
796  strcpy(scratch, path);
797  dot = strrchr(scratch, '.');
798  if (dot)
799  {
800  *dot = 0;
801  sprintf(newpath, "%s%s%d.%s", scratch, REVISE_APPEND, seq, dot + 1);
802  }
803 
804  else
805  {
806  sprintf(newpath, "%s%s%d", path, REVISE_APPEND, seq);
807  }
808 
809  free(scratch);
810 
811  return newpath;
812 } */
813 
814 int relink(char *oldfile, char *newfile)
815 {
816  dev_t od;
817  dev_t nd;
818  ino_t oi;
819  ino_t ni;
820 
821  od = getdevice(oldfile);
822  oi = getinode(oldfile);
823 
824  if (link(oldfile, newfile) != 0)
825  return 0;
826 
827  /* make sure we're working with the right file (the one we created) */
828  nd = getdevice(newfile);
829  ni = getinode(newfile);
830 
831  if (nd != od || oi != ni)
832  return 0; /* file is not what we expected */
833 
834  return 1;
835 }
836 
837 void deletefiles(file_t *files, int prompt, FILE *tty, char *logfile)
838 {
839  int counter;
840  int groups = 0;
841  int curgroup = 0;
842  file_t *tmpfile;
843  file_t *curfile;
844  file_t **dupelist;
845  int *preserve;
846  char *preservestr;
847  char *token;
848  char *tstr;
849  int number;
850  int sum;
851  int max = 0;
852  int x;
853  int i;
854  struct log_info *loginfo;
855  int log_error;
856 
857  curfile = files;
858 
859  while (curfile) {
860  if (curfile->hasdupes) {
861  counter = 1;
862  groups++;
863 
864  tmpfile = curfile->duplicates;
865  while (tmpfile) {
866  counter++;
867  tmpfile = tmpfile->duplicates;
868  }
869 
870  if (counter > max) max = counter;
871  }
872 
873  curfile = curfile->next;
874  }
875 
876  max++;
877 
878  dupelist = (file_t**) malloc(sizeof(file_t*) * max);
879  preserve = (int*) malloc(sizeof(int) * max);
880  preservestr = (char*) malloc(INPUT_SIZE);
881 
882  if (!dupelist || !preserve || !preservestr) {
883  errormsg("out of memory\n");
884  exit(1);
885  }
886 
887  loginfo = 0;
888  if (logfile != 0)
889  loginfo = log_open(logfile, &log_error);
890 
892 
893  while (files) {
894  if (files->hasdupes) {
895  curgroup++;
896  counter = 1;
897  dupelist[counter] = files;
898 
899  if (prompt)
900  {
901  if (ISFLAG(flags, F_SHOWTIME))
902  printf("[%d] [%s] %s\n", counter, fmttime(files->mtime), files->d_name);
903  else
904  printf("[%d] %s\n", counter, files->d_name);
905  }
906 
907  tmpfile = files->duplicates;
908 
909  while (tmpfile) {
910  dupelist[++counter] = tmpfile;
911  if (prompt)
912  {
913  if (ISFLAG(flags, F_SHOWTIME))
914  printf("[%d] [%s] %s\n", counter, fmttime(tmpfile->mtime), tmpfile->d_name);
915  else
916  printf("[%d] %s\n", counter, tmpfile->d_name);
917  }
918  tmpfile = tmpfile->duplicates;
919  }
920 
921  if (prompt) printf("\n");
922 
923  if (!prompt) /* preserve only the first file */
924  {
925  preserve[1] = 1;
926  for (x = 2; x <= counter; x++) preserve[x] = 0;
927  }
928 
929  else /* prompt for files to preserve */
930 
931  do {
932  printf("Set %d of %d, preserve files [1 - %d, all, quit]",
933  curgroup, groups, counter);
934  if (ISFLAG(flags, F_SHOWSIZE)) printf(" (%lld byte%seach)", (long long int)files->size,
935  (files->size != 1) ? "s " : " ");
936  printf(": ");
937  fflush(stdout);
938 
939  if (!fgets(preservestr, INPUT_SIZE, tty))
940  {
941  preservestr[0] = '\n'; /* treat fgets() failure as if nothing was entered */
942  preservestr[1] = '\0';
943 
944  if (got_sigint)
945  {
946  if (loginfo)
947  log_close(loginfo);
948 
949  free(dupelist);
950  free(preserve);
951  free(preservestr);
952 
953  printf("\n");
954 
955  exit(0);
956  }
957  }
958 
959  i = strlen(preservestr) - 1;
960 
961  while (preservestr[i]!='\n'){ /* tail of buffer must be a newline */
962  tstr = (char*)
963  realloc(preservestr, strlen(preservestr) + 1 + INPUT_SIZE);
964  if (!tstr) { /* couldn't allocate memory, treat as fatal */
965  errormsg("out of memory!\n");
966  exit(1);
967  }
968 
969  preservestr = tstr;
970  if (!fgets(preservestr + i + 1, INPUT_SIZE, tty))
971  {
972  preservestr[0] = '\n'; /* treat fgets() failure as if nothing was entered */
973  preservestr[1] = '\0';
974  break;
975  }
976  i = strlen(preservestr)-1;
977  }
978 
979  if (strcmp(preservestr, "q\n") == 0 || strcmp(preservestr, "quit\n") == 0)
980  {
981  if (loginfo)
982  log_close(loginfo);
983 
984  free(dupelist);
985  free(preserve);
986  free(preservestr);
987 
988  printf("\n");
989 
990  exit(0);
991  }
992 
993  for (x = 1; x <= counter; x++) preserve[x] = 0;
994 
995  token = strtok(preservestr, " ,\n");
996 
997  while (token != NULL) {
998  if (strcasecmp(token, "all") == 0 || strcasecmp(token, "a") == 0)
999  for (x = 0; x <= counter; x++) preserve[x] = 1;
1000 
1001  number = 0;
1002  sscanf(token, "%d", &number);
1003  if (number > 0 && number <= counter) preserve[number] = 1;
1004 
1005  token = strtok(NULL, " ,\n");
1006  }
1007 
1008  for (sum = 0, x = 1; x <= counter; x++) sum += preserve[x];
1009  } while (sum < 1); /* make sure we've preserved at least one file */
1010 
1011  printf("\n");
1012 
1013  if (loginfo)
1014  log_begin_set(loginfo);
1015 
1016  for (x = 1; x <= counter; x++) {
1017  if (preserve[x])
1018  {
1019  printf(" [+] %s\n", dupelist[x]->d_name);
1020 
1021  if (loginfo)
1022  log_file_remaining(loginfo, dupelist[x]->d_name);
1023  }
1024  else {
1025  if (remove(dupelist[x]->d_name) == 0) {
1026  printf(" [-] %s\n", dupelist[x]->d_name);
1027 
1028  if (loginfo)
1029  log_file_deleted(loginfo, dupelist[x]->d_name);
1030  } else {
1031  printf(" [!] %s ", dupelist[x]->d_name);
1032  printf("-- unable to delete file!\n");
1033 
1034  if (loginfo)
1035  log_file_remaining(loginfo, dupelist[x]->d_name);
1036  }
1037  }
1038  }
1039  printf("\n");
1040 
1041  if (loginfo)
1042  log_end_set(loginfo);
1043  }
1044 
1045  files = files->next;
1046  }
1047 
1048  if (loginfo)
1049  log_close(loginfo);
1050 
1051  free(dupelist);
1052  free(preserve);
1053  free(preservestr);
1054 }
1055 
1057 {
1058  if (f2->duplicates != 0)
1059  return !ISFLAG(flags, F_REVERSE) ? 1 : -1;
1060 
1061  return !ISFLAG(flags, F_REVERSE) ? -1 : 1;
1062 }
1063 
1065 {
1066  if (f1->ctime < f2->ctime)
1067  return !ISFLAG(flags, F_REVERSE) ? -1 : 1;
1068  else if (f1->ctime > f2->ctime)
1069  return !ISFLAG(flags, F_REVERSE) ? 1 : -1;
1070 
1071  return 0;
1072 }
1073 
1075 {
1076  if (f1->mtime < f2->mtime)
1077  return !ISFLAG(flags, F_REVERSE) ? -1 : 1;
1078  else if (f1->mtime > f2->mtime)
1079  return !ISFLAG(flags, F_REVERSE) ? 1 : -1;
1080  else
1081  return sort_pairs_by_ctime(f1, f2);
1082 }
1083 
1085 {
1086  int strvalue = strcmp(f1->d_name, f2->d_name);
1087  return !ISFLAG(flags, F_REVERSE) ? strvalue : -strvalue;
1088 }
1089 
1090 void registerpair(file_t **matchlist, file_t *newmatch,
1091  int (*comparef)(file_t *f1, file_t *f2))
1092 {
1093  file_t *traverse;
1094  file_t *back;
1095 
1096  (*matchlist)->hasdupes = 1;
1097 
1098  back = 0;
1099  traverse = *matchlist;
1100  while (traverse)
1101  {
1102  if (comparef(newmatch, traverse) <= 0)
1103  {
1104  newmatch->duplicates = traverse;
1105 
1106  if (back == 0)
1107  {
1108  *matchlist = newmatch; /* update pointer to head of list */
1109 
1110  newmatch->hasdupes = 1;
1111  traverse->hasdupes = 0; /* flag is only for first file in dupe chain */
1112  }
1113  else
1114  back->duplicates = newmatch;
1115 
1116  break;
1117  }
1118  else
1119  {
1120  if (traverse->duplicates == 0)
1121  {
1122  traverse->duplicates = newmatch;
1123 
1124  if (back == 0)
1125  traverse->hasdupes = 1;
1126 
1127  break;
1128  }
1129  }
1130 
1131  back = traverse;
1132  traverse = traverse->duplicates;
1133  }
1134 }
1135 
1136 void deletesuccessor(file_t **existing, file_t *duplicate,
1137  int (*comparef)(file_t *f1, file_t *f2), struct log_info *loginfo)
1138 {
1139  file_t *to_keep;
1140  file_t *to_delete;
1141 
1142  if (comparef(duplicate, *existing) >= 0)
1143  {
1144  to_keep = *existing;
1145  to_delete = duplicate;
1146  }
1147  else
1148  {
1149  to_keep = duplicate;
1150  to_delete = *existing;
1151 
1152  *existing = duplicate;
1153  }
1154 
1155  if (!ISFLAG(flags, F_HIDEPROGRESS)) fprintf(stderr, "\r%40s\r", " ");
1156 
1157  if (loginfo)
1158  log_begin_set(loginfo);
1159 
1160  printf(" [+] %s\n", to_keep->d_name);
1161 
1162  if (loginfo)
1163  log_file_remaining(loginfo, to_keep->d_name);
1164 
1165  if (remove(to_delete->d_name) == 0) {
1166  printf(" [-] %s\n", to_delete->d_name);
1167 
1168  if (loginfo)
1169  log_file_deleted(loginfo, to_delete->d_name);
1170  } else {
1171  printf(" [!] %s ", to_delete->d_name);
1172  printf("-- unable to delete file!\n");
1173 
1174  if (loginfo)
1175  log_file_remaining(loginfo, to_delete->d_name);
1176  }
1177 
1178  if (loginfo)
1179  log_end_set(loginfo);
1180 
1181  printf("\n");
1182 }
1183 
1185 {
1186  printf("Usage: fdupes [options] DIRECTORY...\n\n");
1187 
1188  /* 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0
1189  -------"---------|---------|---------|---------|---------|---------|---------|---------|"
1190  */
1191  printf(" -r --recurse for every directory given follow subdirectories\n");
1192  printf(" encountered within\n");
1193  printf(" -R --recurse: for each directory given after this option follow\n");
1194  printf(" subdirectories encountered within (note the ':' at the\n");
1195  printf(" end of the option, manpage for more details)\n");
1196  printf(" -s --symlinks follow symlinks\n");
1197  printf(" -H --hardlinks normally, when two or more files point to the same\n");
1198  printf(" disk area they are treated as non-duplicates; this\n");
1199  printf(" option will change this behavior\n");
1200  printf(" -G --minsize=SIZE consider only files greater than or equal to SIZE bytes\n");
1201  printf(" -L --maxsize=SIZE consider only files less than or equal to SIZE bytes\n");
1202  printf(" -n --noempty exclude zero-length files from consideration\n");
1203  printf(" -A --nohidden exclude hidden files from consideration\n");
1204  printf(" -f --omitfirst omit the first file in each set of matches\n");
1205  printf(" -1 --sameline list each set of matches on a single line\n");
1206  printf(" -S --size show size of duplicate files\n");
1207  printf(" -t --time show modification time of duplicate files\n");
1208  printf(" -m --summarize summarize dupe information\n");
1209  printf(" -q --quiet hide progress indicator\n");
1210  printf(" -d --delete prompt user for files to preserve and delete all\n");
1211  printf(" others; important: under particular circumstances,\n");
1212  printf(" data may be lost when using this option together\n");
1213  printf(" with -s or --symlinks, or when specifying a\n");
1214  printf(" particular directory more than once; refer to the\n");
1215  printf(" fdupes documentation for additional information\n");
1216 #ifndef NO_NCURSES
1217  printf(" -P --plain with --delete, use line-based prompt (as with older\n");
1218  printf(" versions of fdupes) instead of screen-mode interface\n");
1219 #endif
1220  printf(" -N --noprompt together with --delete, preserve the first file in\n");
1221  printf(" each set of duplicates and delete the rest without\n");
1222  printf(" prompting the user\n");
1223  printf(" -I --immediate delete duplicates as they are encountered, without\n");
1224  printf(" grouping into sets; implies --noprompt\n");
1225  printf(" -p --permissions don't consider files with different owner/group or\n");
1226  printf(" permission bits as duplicates\n");
1227  printf(" -o --order=BY select sort order for output and deleting; by file\n");
1228  printf(" modification time (BY='time'; default), status\n");
1229  printf(" change time (BY='ctime'), or filename (BY='name')\n");
1230  printf(" -i --reverse reverse order while sorting\n");
1231  printf(" -l --log=LOGFILE log file deletion choices to LOGFILE\n");
1232  printf(" -v --version display fdupes version\n");
1233  printf(" -h --help display this help message\n\n");
1234 #ifndef HAVE_GETOPT_H
1235  printf("Note: Long options are not supported in this fdupes build.\n\n");
1236 #endif
1237 }
1238 
1239 int main(int argc, char **argv) {
1240  int x;
1241  int opt;
1242  FILE *file1;
1243  FILE *file2;
1244  file_t *files = NULL;
1245  file_t *curfile;
1246  file_t **match = NULL;
1247  filetree_t *checktree = NULL;
1248  int filecount = 0;
1249  int progress = 0;
1250  char **oldargv;
1251  int firstrecurse;
1252  char *logfile = 0;
1253  struct log_info *loginfo = NULL;
1254  int log_error;
1255  struct stat logfile_status;
1256  char *endptr;
1257 
1258 #ifdef HAVE_GETOPT_H
1259  static struct option long_options[] =
1260  {
1261  { "omitfirst", 0, 0, 'f' },
1262  { "recurse", 0, 0, 'r' },
1263  { "recurse:", 0, 0, 'R' },
1264  { "quiet", 0, 0, 'q' },
1265  { "sameline", 0, 0, '1' },
1266  { "size", 0, 0, 'S' },
1267  { "time", 0, 0, 't' },
1268  { "symlinks", 0, 0, 's' },
1269  { "hardlinks", 0, 0, 'H' },
1270  { "minsize", 1, 0, 'G' },
1271  { "maxsize", 1, 0, 'L' },
1272  { "noempty", 0, 0, 'n' },
1273  { "nohidden", 0, 0, 'A' },
1274  { "delete", 0, 0, 'd' },
1275  { "plain", 0, 0, 'P' },
1276  { "version", 0, 0, 'v' },
1277  { "help", 0, 0, 'h' },
1278  { "noprompt", 0, 0, 'N' },
1279  { "immediate", 0, 0, 'I'},
1280  { "summarize", 0, 0, 'm'},
1281  { "summary", 0, 0, 'm' },
1282  { "permissions", 0, 0, 'p' },
1283  { "order", 1, 0, 'o' },
1284  { "reverse", 0, 0, 'i' },
1285  { "log", 1, 0, 'l' },
1286  { 0, 0, 0, 0 }
1287  };
1288 #define GETOPT getopt_long
1289 #else
1290 #define GETOPT getopt
1291 #endif
1292 
1293  program_name = argv[0];
1294 
1295  setlocale(LC_CTYPE, "");
1296 
1297  oldargv = cloneargs(argc, argv);
1298 
1299  while ((opt = GETOPT(argc, argv, "frRq1StsHG:L:nAdPvhNImpo:il:"
1300 #ifdef HAVE_GETOPT_H
1301  , long_options, NULL
1302 #endif
1303  )) != EOF) {
1304  switch (opt) {
1305  case 'f':
1307  break;
1308  case 'r':
1310  break;
1311  case 'R':
1313  break;
1314  case 'q':
1316  break;
1317  case '1':
1319  break;
1320  case 'S':
1322  break;
1323  case 't':
1325  break;
1326  case 's':
1328  break;
1329  case 'H':
1331  break;
1332  case 'G':
1333  minsize = strtoll(optarg, &endptr, 10);
1334  if (optarg[0] == '\0' || *endptr != '\0' || minsize < 0)
1335  {
1336  errormsg("invalid value for --minsize: '%s'\n", optarg);
1337  exit(1);
1338  }
1339  break;
1340  case 'L':
1341  maxsize = strtoll(optarg, &endptr, 10);
1342  if (optarg[0] == '\0' || *endptr != '\0' || maxsize < 0)
1343  {
1344  errormsg("invalid value for --maxsize: '%s'\n", optarg);
1345  exit(1);
1346  }
1347  break;
1348  case 'n':
1350  break;
1351  case 'A':
1353  break;
1354  case 'd':
1356  break;
1357  case 'P':
1359  break;
1360  case 'v':
1361  printf("fdupes %s\n", VERSION);
1362  exit(0);
1363  case 'h':
1364  help_text();
1365  exit(1);
1366  case 'N':
1368  break;
1369  case 'I':
1371  break;
1372  case 'm':
1374  break;
1375  case 'p':
1377  break;
1378  case 'o':
1379  if (!strcasecmp("name", optarg)) {
1381  } else if (!strcasecmp("time", optarg)) {
1383  } else if (!strcasecmp("ctime", optarg)) {
1385  } else {
1386  errormsg("invalid value for --order: '%s'\n", optarg);
1387  exit(1);
1388  }
1389  break;
1390  case 'i':
1392  break;
1393  case 'l':
1394  logfile = optarg;
1395  break;
1396  default:
1397  fprintf(stderr, "Try `fdupes --help' for more information.\n");
1398  exit(1);
1399  }
1400  }
1401 
1402  if (optind >= argc) {
1403  errormsg("no directories specified\n");
1404  exit(1);
1405  }
1406 
1408  errormsg("options --recurse and --recurse: are not compatible\n");
1409  exit(1);
1410  }
1411 
1413  errormsg("options --summarize and --delete are not compatible\n");
1414  exit(1);
1415  }
1416 
1417  if (!ISFLAG(flags, F_DELETEFILES))
1418  logfile = 0;
1419 
1420  if (logfile != 0)
1421  {
1422  loginfo = log_open(logfile, &log_error);
1423  if (loginfo == 0)
1424  {
1425  if (log_error == LOG_ERROR_NOT_A_LOG_FILE)
1426  errormsg("%s: doesn't look like an fdupes log file\n", logfile);
1427  else
1428  errormsg("%s: could not open log file\n", logfile);
1429 
1430  exit(1);
1431  }
1432 
1433  if (stat(logfile, &logfile_status) != 0)
1434  {
1435  errormsg("could not read log file status\n");
1436  exit(1);
1437  }
1438  }
1439 
1440  if (ISFLAG(flags, F_RECURSEAFTER)) {
1441  firstrecurse = nonoptafter("--recurse:", argc, oldargv, argv, optind);
1442 
1443  if (firstrecurse == argc)
1444  firstrecurse = nonoptafter("-R", argc, oldargv, argv, optind);
1445 
1446  if (firstrecurse == argc) {
1447  errormsg("-R option must be isolated from other options\n");
1448  exit(1);
1449  }
1450 
1451  /* F_RECURSE is not set for directories before --recurse: */
1452  for (x = optind; x < firstrecurse; x++)
1453  filecount += grokdir(argv[x], &files, &logfile_status);
1454 
1455  /* Set F_RECURSE for directories after --recurse: */
1457 
1458  for (x = firstrecurse; x < argc; x++)
1459  filecount += grokdir(argv[x], &files, &logfile_status);
1460  } else {
1461  for (x = optind; x < argc; x++)
1462  filecount += grokdir(argv[x], &files, &logfile_status);
1463  }
1464 
1465  if (!files) {
1466  if (!ISFLAG(flags, F_HIDEPROGRESS)) fprintf(stderr, "\r%40s\r", " ");
1467  exit(0);
1468  }
1469 
1470  curfile = files;
1471 
1472  while (curfile) {
1473  if (!checktree)
1474  registerfile(&checktree, curfile);
1475  else
1476  match = checkmatch(&checktree, checktree, curfile);
1477 
1478  if (match != NULL) {
1479  file1 = fopen(curfile->d_name, "rb");
1480  if (!file1) {
1481  curfile = curfile->next;
1482  continue;
1483  }
1484 
1485  file2 = fopen((*match)->d_name, "rb");
1486  if (!file2) {
1487  fclose(file1);
1488  curfile = curfile->next;
1489  continue;
1490  }
1491 
1492  if (confirmmatch(file1, file2)) {
1494  deletesuccessor(match, curfile,
1497  sort_pairs_by_filename, loginfo );
1498  else
1499  registerpair(match, curfile,
1503  }
1504 
1505  fclose(file1);
1506  fclose(file2);
1507  }
1508 
1509  curfile = curfile->next;
1510 
1511  if (!ISFLAG(flags, F_HIDEPROGRESS)) {
1512  fprintf(stderr, "\rProgress [%d/%d] %d%% ", progress, filecount,
1513  (int)((float) progress / (float) filecount * 100.0));
1514  progress++;
1515  }
1516  }
1517 
1518  if (!ISFLAG(flags, F_HIDEPROGRESS)) fprintf(stderr, "\r%40s\r", " ");
1519 
1520  if (loginfo != 0)
1521  {
1522  log_close(loginfo);
1523  loginfo = 0;
1524  }
1525 
1526  if (ISFLAG(flags, F_DELETEFILES))
1527  {
1529  {
1530  deletefiles(files, 0, 0, logfile);
1531  }
1532  else
1533  {
1534 #ifndef NO_NCURSES
1535  if (!ISFLAG(flags, F_PLAINPROMPT))
1536  {
1537  if (newterm(getenv("TERM"), stdout, stdin) != 0)
1538  {
1539  deletefiles_ncurses(files, logfile);
1540  }
1541  else
1542  {
1543  errormsg("could not enter screen mode; falling back to plain mode\n\n");
1545  }
1546  }
1547 
1548  if (ISFLAG(flags, F_PLAINPROMPT))
1549  {
1550  if (freopen("/dev/tty", "r", stdin) == NULL)
1551  {
1552  errormsg("could not open terminal for input\n");
1553  exit(1);
1554  }
1555 
1556  deletefiles(files, 1, stdin, logfile);
1557  }
1558 #else
1559  if (freopen("/dev/tty", "r", stdin) == NULL)
1560  {
1561  errormsg("could not open terminal for input\n");
1562  exit(1);
1563  }
1564 
1565  deletefiles(files, 1, stdin, logfile);
1566 #endif
1567  }
1568  }
1569 
1570  else
1571 
1573  summarizematches(files);
1574 
1575  else
1576 
1577  printmatches(files);
1578 
1579  while (files) {
1580  curfile = files->next;
1581  free(files->d_name);
1582  free(files->crcsignature);
1583  free(files->crcpartial);
1584  free(files);
1585  files = curfile;
1586  }
1587 
1588  for (x = 0; x < argc; x++)
1589  free(oldargv[x]);
1590 
1591  free(oldargv);
1592 
1593  purgetree(checktree);
1594 
1595  return 0;
1596 }
getcrcsignature
md5_byte_t * getcrcsignature(char *filename, off_t fsize)
Definition: fdupes.c:380
PARTIAL_MD5_SIZE
#define PARTIAL_MD5_SIZE
Definition: fdupes.c:69
registerpair
void registerpair(file_t **matchlist, file_t *newmatch, int(*comparef)(file_t *f1, file_t *f2))
Definition: fdupes.c:1090
sort_pairs_by_filename
int sort_pairs_by_filename(file_t *f1, file_t *f2)
Definition: fdupes.c:1084
F_CONSIDERHARDLINKS
#define F_CONSIDERHARDLINKS
Definition: flags.h:13
_file
Definition: fdupes.h:28
_filetree
Definition: fdupes.c:91
log_close
void log_close(struct log_info *info)
Definition: log.c:222
findarg
int findarg(char *arg, int start, int argc, char **argv)
Definition: fdupes.c:178
log_end_set
void log_end_set(struct log_info *info)
Definition: log.c:181
ORDER_MTIME
@ ORDER_MTIME
Definition: fdupes.c:56
checkmatch
file_t ** checkmatch(filetree_t **root, filetree_t *checktree, file_t *file)
Definition: fdupes.c:568
LOG_ERROR_NOT_A_LOG_FILE
#define LOG_ERROR_NOT_A_LOG_FILE
Definition: log.h:27
program_name
char * program_name
Definition: fdupes.c:61
F_DELETEFILES
#define F_DELETEFILES
Definition: flags.h:11
_file::crcsignature
md5_byte_t * crcsignature
Definition: fdupes.h:32
F_NOPROMPT
#define F_NOPROMPT
Definition: flags.h:17
has_same_file
int has_same_file(filetree_t *checktree, file_t *file)
Definition: fdupes.c:546
errormsg
void errormsg(char *message,...)
Definition: errormsg.c:26
is_same_file
int is_same_file(file_t *file_a, file_t *file_b)
Definition: fdupes.c:474
flags
unsigned long flags
Definition: flags.c:3
md5cmp
int md5cmp(const md5_byte_t *a, const md5_byte_t *b)
Definition: fdupes.c:390
confirmmatch
int confirmmatch(FILE *file1, FILE *file2)
Definition: fdupes.c:690
log_info
Definition: log.h:36
md5_init
void md5_init(md5_state_t *pms)
Definition: md5.c:343
_filetree::file
file_t * file
Definition: fdupes.c:92
_file::mtime
time_t mtime
Definition: fdupes.h:35
md5_state_s
Definition: md5.h:58
printmatches
void printmatches(file_t *files)
Definition: fdupes.c:750
F_OMITFIRST
#define F_OMITFIRST
Definition: flags.h:15
_file::duplicates
struct _file * duplicates
Definition: fdupes.h:38
F_RECURSE
#define F_RECURSE
Definition: flags.h:7
F_REVERSE
#define F_REVERSE
Definition: flags.h:21
F_PERMISSIONS
#define F_PERMISSIONS
Definition: flags.h:20
_file::size
off_t size
Definition: fdupes.h:30
nonoptafter
int nonoptafter(char *option, int argc, char **oldargv, char **newargv, int optind)
Definition: fdupes.c:190
fmttime
char * fmttime(time_t t)
Definition: fdupes.c:145
F_IMMEDIATE
#define F_IMMEDIATE
Definition: flags.h:22
INPUT_SIZE
#define INPUT_SIZE
Definition: fdupes.c:67
F_SHOWTIME
#define F_SHOWTIME
Definition: flags.h:24
maxsize
long long maxsize
Definition: fdupes.c:53
_file::crcpartial
md5_byte_t * crcpartial
Definition: fdupes.h:31
F_FOLLOWLINKS
#define F_FOLLOWLINKS
Definition: flags.h:10
grokdir
int grokdir(char *dir, file_t **filelistp, struct stat *logfile_status)
Definition: fdupes.c:218
log_open
struct log_info * log_open(char *filename, int *error)
Definition: log.c:30
F_SUMMARIZEMATCHES
#define F_SUMMARIZEMATCHES
Definition: flags.h:18
registerfile
int registerfile(filetree_t **branch, file_t *file)
Definition: fdupes.c:422
escapefilename
void escapefilename(char *escape_list, char **filename_ptr)
Definition: fdupes.c:97
getcrcsignatureuntil
md5_byte_t * getcrcsignatureuntil(char *filename, off_t fsize, off_t max_read)
Definition: fdupes.c:343
sort_pairs_by_mtime
int sort_pairs_by_mtime(file_t *f1, file_t *f2)
Definition: fdupes.c:1074
deletefiles
void deletefiles(file_t *files, int prompt, FILE *tty, char *logfile)
Definition: fdupes.c:837
GETOPT
#define GETOPT
same_permissions
int same_permissions(char *name1, char *name2)
Definition: fdupes.c:437
errormsg.h
cloneargs
char ** cloneargs(int argc, char **argv)
Definition: fdupes.c:153
SETFLAG
#define SETFLAG(a, b)
Definition: flags.h:5
help_text
void help_text()
Definition: fdupes.c:1184
ncurses-interface.h
log_begin_set
void log_begin_set(struct log_info *info)
Definition: log.c:110
getdevice
dev_t getdevice(char *filename)
Definition: fdupes.c:129
_file::inode
ino_t inode
Definition: fdupes.h:34
sigint.h
deletesuccessor
void deletesuccessor(file_t **existing, file_t *duplicate, int(*comparef)(file_t *f1, file_t *f2), struct log_info *loginfo)
Definition: fdupes.c:1136
_file::d_name
char * d_name
Definition: fdupes.h:29
ordertype_t
ordertype_t
Definition: fdupes.c:55
_file::next
struct _file * next
Definition: fdupes.h:39
getfilestats
void getfilestats(file_t *file, struct stat *info, struct stat *linfo)
Definition: fdupes.c:209
log.h
F_RECURSEAFTER
#define F_RECURSEAFTER
Definition: flags.h:16
F_EXCLUDEHIDDEN
#define F_EXCLUDEHIDDEN
Definition: flags.h:19
main
int main(int argc, char **argv)
Definition: fdupes.c:1239
log_file_remaining
int log_file_remaining(struct log_info *info, char *name)
Definition: log.c:135
sort_pairs_by_arrival
int sort_pairs_by_arrival(file_t *f1, file_t *f2)
Definition: fdupes.c:1056
_filetree::left
struct _filetree * left
Definition: fdupes.c:93
F_EXCLUDEEMPTY
#define F_EXCLUDEEMPTY
Definition: flags.h:12
F_HIDEPROGRESS
#define F_HIDEPROGRESS
Definition: flags.h:8
F_PLAINPROMPT
#define F_PLAINPROMPT
Definition: flags.h:23
md5_append
void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
Definition: md5.c:353
relink
int relink(char *oldfile, char *newfile)
Definition: fdupes.c:814
md5_byte_t
unsigned char md5_byte_t
Definition: md5.h:54
ordertype
ordertype_t ordertype
Definition: fdupes.c:63
is_hardlink
int is_hardlink(filetree_t *checktree, file_t *file)
Definition: fdupes.c:449
summarizematches
void summarizematches(file_t *files)
Definition: fdupes.c:711
deletefiles_ncurses
void deletefiles_ncurses(file_t *files, char *logfile)
Definition: ncurses-interface.c:355
purgetree
void purgetree(filetree_t *checktree)
Definition: fdupes.c:413
ISFLAG
#define ISFLAG(a, b)
Definition: flags.h:4
CHUNK_SIZE
#define CHUNK_SIZE
Definition: fdupes.c:65
ORDER_CTIME
@ ORDER_CTIME
Definition: fdupes.c:57
got_sigint
volatile sig_atomic_t got_sigint
Definition: sigint.c:22
_filetree::right
struct _filetree * right
Definition: fdupes.c:94
_file::ctime
time_t ctime
Definition: fdupes.h:36
getinode
ino_t getinode(char *filename)
Definition: fdupes.c:137
md5_finish
void md5_finish(md5_state_t *pms, md5_byte_t digest[16])
Definition: md5.c:391
md5copy
void md5copy(md5_byte_t *to, const md5_byte_t *from)
Definition: fdupes.c:405
MD5_DIGEST_LENGTH
#define MD5_DIGEST_LENGTH
Definition: fdupes.c:71
filetree_t
struct _filetree filetree_t
_file::hasdupes
int hasdupes
Definition: fdupes.h:37
sort_pairs_by_ctime
int sort_pairs_by_ctime(file_t *f1, file_t *f2)
Definition: fdupes.c:1064
flags.h
_file::device
dev_t device
Definition: fdupes.h:33
ORDER_NAME
@ ORDER_NAME
Definition: fdupes.c:58
getcrcpartialsignature
md5_byte_t * getcrcpartialsignature(char *filename, off_t fsize)
Definition: fdupes.c:385
fdupes.h
F_DSAMELINE
#define F_DSAMELINE
Definition: flags.h:9
log_file_deleted
int log_file_deleted(struct log_info *info, char *name)
Definition: log.c:117
register_sigint_handler
void register_sigint_handler()
Definition: sigint.c:29
F_SHOWSIZE
#define F_SHOWSIZE
Definition: flags.h:14
minsize
long long minsize
Definition: fdupes.c:52