home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1638 < prev    next >
Internet Message Format  |  1990-12-28  |  9KB

  1. From: lee@sqarc.sq.com (Liam R. E. Quin)
  2. Newsgroups: alt.sources
  3. Subject: cmpall -- find identical (duplicated) files
  4. Message-ID: <1990Aug2.212411.3078@sq.sq.com>
  5. Date: 2 Aug 90 21:24:11 GMT
  6.  
  7. I wrote cmpall some time ago, when I found that I had lots of copies and
  8. duplicated directory hierarchies.
  9.  
  10. You say (for example)
  11.     find $HOME | cmpall
  12. and it says
  13.     same /home/lee/src/lqtext/doc/README /home/lee/doc/README
  14. and so forth.
  15.  
  16. This version only compares files that have the same name and size.
  17. You could change this to compare files that have the same size if you
  18. wanted -- I wasn't too bothered by that.  It won't (of course) detect
  19. duplicates if one is compressed.
  20.  
  21. It works unchanged on SysV and SunOS, should be OK on other Unix systems.
  22.  
  23. No manpage.  Just do
  24.     make cmpall
  25. (you don't even need a makefile).
  26.  
  27. Let me know if it helps, or if you change it...
  28.  
  29. Lee
  30.  
  31. : To unbundle, sh this file
  32. echo x - cmpall.c 1>&2
  33. sed 's/^X//' >cmpall.c <<'@@@End of cmpall.c'
  34. X/* $Header: /home/lee/src/cmpall/cmpall.c,v 1.2 90/08/02 17:22:12 lee Exp Locker: lee $
  35. X *
  36. X */
  37. X
  38. X#include <stdio.h>
  39. X#include <malloc.h>
  40. X#include <errno.h>
  41. X#include <fcntl.h>
  42. X#include <sys/types.h>
  43. X#include <sys/stat.h>
  44. X
  45. Xextern int errno;
  46. X
  47. Xtypedef struct s_listel {
  48. X    char *Name;
  49. X    struct stat *statbuf;
  50. X    int n_seen;
  51. X    struct s_listel *Next;
  52. X} t_listel;
  53. X
  54. X#define STREQ(henry,utzoo) ((*(henry)== *(utzoo))&&!strcmp(henry,utzoo))
  55. X#define new(type) ((type *) malloc(sizeof(type)))
  56. X
  57. Xint UseBytes = 0;
  58. Xchar *progname;
  59. X
  60. X/* read filenames a line at a time.
  61. X * whenever we get a file we've seen before, compare it
  62. X * with the other of the sames name.
  63. X * If they are the same, print "choose -s first second"
  64. X * otherwise, print "choose -d first second"
  65. X */
  66. Xmain(argc, argv)
  67. X    int argc;
  68. X    char *argv[];
  69. X{
  70. X    t_listel *NewEntry();
  71. X    void AddEntry();
  72. X    char *GetLine();
  73. X    char *Path;
  74. X
  75. X    progname = argv[0];
  76. X
  77. X    if (argc != 1) {
  78. X    if (argc == 2 && STREQ(argv[1], "-b")) {
  79. X        UseBytes = 1;
  80. X    } else {
  81. X        (void) fprintf(stderr, "usage: find <dir> ... -print | %s [-b]\n",
  82. X                                progname);
  83. X        exit(1);
  84. X    }
  85. X    }
  86. X
  87. X    while ((Path = GetLine(stdin, "/dev/stdin")) != (char *) 0) {
  88. X    AddEntry(stdin, "/dev/stdin", Path);
  89. X    }
  90. X
  91. X    exit(0);
  92. X}
  93. X
  94. Xt_listel *SeenSoFar = (t_listel *) 0;
  95. X
  96. Xvoid
  97. XAddEntry(fd, Name, Path)
  98. X    FILE *fd;
  99. X    char*Name;
  100. X    char *Path;
  101. X{
  102. X    extern char *strrchr();
  103. X
  104. X    t_listel *e;
  105. X    t_listel **lpp;
  106. X
  107. X    if ((e = new(t_listel)) == (t_listel *) 0) {
  108. X    (void) fprintf(stderr, "no RAM reading \"%s\" ", Name);
  109. X    /* possibly we dumped core just then... */
  110. X    (void) fprintf(stderr, "at \"%s\"\n", Path);
  111. X    exit(1);
  112. X    }
  113. X
  114. X    e->Name = Path;
  115. X    e->n_seen = 1;
  116. X    e->statbuf = (struct stat *) 0;
  117. X
  118. X    for (lpp = &SeenSoFar; *lpp; lpp = &(*lpp)->Next) {
  119. X    register char *Old = (*lpp)->Name;
  120. X    int i;
  121. X    char *PathLastSlash;
  122. X    char *OldLastSlash;
  123. X
  124. X    if ((PathLastSlash = strrchr(Path, '/')) == (char *) 0) {
  125. X        PathLastSlash = Path;
  126. X    } else {
  127. X        ++PathLastSlash;
  128. X    }
  129. X
  130. X    if ((OldLastSlash = strrchr(Old, '/')) == (char *) 0) {
  131. X        OldLastSlash = Old;
  132. X    } else {
  133. X        ++OldLastSlash;
  134. X    }
  135. X
  136. X    if (*PathLastSlash == *OldLastSlash) {
  137. X        i = strcmp(PathLastSlash, OldLastSlash);
  138. X    } else {
  139. X        i = *PathLastSlash - *OldLastSlash;
  140. X    }
  141. X
  142. X    if (i < 0) continue; /* not there yet */
  143. X    if (i == 0) {
  144. X        /* found it... */
  145. X
  146. X        /* If the files are the same, we should add them to a single
  147. X         * linked list and traverse it at the end, I suppose!
  148. X         */
  149. X        if (Compare(fd, Name, *lpp, e) == 0) {
  150. X        /* They were the same, so no point storing this one... */
  151. X        (void) printf("same '%s' '%s'\n", (*lpp)->Name, e->Name);
  152. X        if (e->statbuf) {
  153. X            (void) free((char *) e->statbuf);
  154. X        }
  155. X        (void) free((char *) e);
  156. X        (void) free(Path);
  157. X        return;
  158. X        }
  159. X        /* the files were different, so add the new one to the list */
  160. X        /* FALLTHROUGH */
  161. X    }
  162. X    break;
  163. X    }
  164. X
  165. X    /* lpp points either at the last "Next", or at the Next
  166. X     * of the element before which we must insert a new entry...
  167. X     */
  168. X
  169. X    e->Next = (*lpp);
  170. X    *lpp = e;
  171. X    return;
  172. X}
  173. X
  174. X/*ARGSUSED*/
  175. Xint
  176. XCompare(fd, Name, Old, New)
  177. X    FILE *fd;
  178. X    char *Name;
  179. X    t_listel *Old;
  180. X    t_listel *New;
  181. X{
  182. X    /* We return
  183. X     * 0 if the files are identical
  184. X     * -1 if we can't stat Path
  185. X     * 1 if the files are (or might be) different
  186. X     * 2 if they are definitely different
  187. X     *
  188. X     * If we can stat Path but not e, we return 3, and delete e
  189. X     * from the list.
  190. X     */
  191. X    if (Old->statbuf == (struct stat *) 0) {
  192. X    if ((Old->statbuf = new(struct stat)) == (struct stat *) 0) {
  193. X        (void) fprintf(stderr, "reading \"%s\", no RAM to stat \"%s\"\n",
  194. X                            Name, Old->Name);
  195. X        exit(1);
  196. X    }
  197. X    if (stat(Old->Name, Old->statbuf) < 0) {
  198. X        Delete(Old);
  199. X        return 3;
  200. X    }
  201. X    }
  202. X
  203. X    if (New->statbuf == (struct stat *) 0) {
  204. X    if ((New->statbuf = new(struct stat)) == (struct stat *) 0) {
  205. X        (void) fprintf(stderr, "reading \"%s\", no RAM to stat \"%s\"\n",
  206. X                            Name, New->Name);
  207. X        exit(1);
  208. X    }
  209. X    if (stat(New->Name, New->statbuf) < 0) {
  210. X        return -1;
  211. X    }
  212. X    }
  213. X
  214. X    /* if they are not normal files, they are the same if the major/minor
  215. X     * numbers are the same:
  216. X     */
  217. X    /* NOTDONE */
  218. X    
  219. X    /** Now compare the files: */
  220. X
  221. X    /*  first, are they the same size? */
  222. X    if (Old->statbuf->st_size != New->statbuf->st_size) {
  223. X    return 2; /* definitely different */
  224. X    }
  225. X
  226. X    if (Old->statbuf->st_ino == New->statbuf->st_ino &&
  227. X    Old->statbuf->st_dev == New->statbuf->st_dev &&
  228. X    Old->statbuf->st_nlink == New->statbuf->st_nlink) {
  229. X
  230. X    /* they are linked to each other, so they are the same! */
  231. X    return 0; /* this is only really a heuristic */
  232. X    }
  233. X
  234. X    switch (CmpFile(Old->Name, New->Name)) {
  235. X    case 0: return 0;
  236. X    case 1: return 2; /* different! */
  237. X    case -1: /* no a */
  238. X    case -2: /* no b */
  239. X    return -1;
  240. X    default:
  241. X    return -1;
  242. X    }
  243. X}
  244. X
  245. Xint
  246. XCmpFile(a, b)
  247. X    char *a, *b;
  248. X{
  249. X    /* return 0 if same, -ve on error, 1 otherwise */
  250. X    int afd = open(a, O_RDONLY, 0);
  251. X    int bfd = open(b, O_RDONLY, 0);
  252. X    char ablk[1024], bblk[1024];
  253. X    int ar, br;
  254. X
  255. X    if (afd < 0) return -1;
  256. X    if (bfd < 0) return -2;
  257. X
  258. X    while ((ar = read(afd, ablk, 1024)) == (br = read(bfd, bblk, 1024))) {
  259. X    if ((ar == 0) || memcmp(ablk, bblk, ar) != 0) {
  260. X        (void) close(afd);
  261. X        (void) close(bfd);
  262. X        /* ar == br == 0: got to EOF, they are the same */
  263. X        return (ar == 0) ? 0 : 1;
  264. X    } else if (ar < 0) {
  265. X        return -3; /* read error! */
  266. X    }
  267. X    }
  268. X    /* Since we use stat to check the size before calling this function,
  269. X     * this should never happen... except in the case of a read error.
  270. X     */
  271. X    return 1; /* different lengths */
  272. X}
  273. X
  274. Xchar *
  275. XGetLine(fd, Name)
  276. X    FILE *fd;
  277. X    char *Name;
  278. X{
  279. X    char *Result;
  280. X    register char *p;
  281. X    unsigned int len = 20;
  282. X    int ch;
  283. X
  284. X    if ((Result = malloc(len)) == (char *) 0)  {
  285. X    (void) fprintf(stderr, "out of mem reading \"%s\"\n", Name);
  286. X    exit(1);
  287. X    }
  288. X
  289. X    p = Result;
  290. X
  291. X    while ((ch = getc(fd)) != EOF) {
  292. X    if (ch == '\n') break;
  293. X    if (p - Result >= len - 1) {
  294. X        int WhereWeWere = p - Result;
  295. X
  296. X        if ((Result = realloc(Result, len += 20)) == (char *) 0) {
  297. X        (void) fprintf(stderr, "no more mem in \"%s\"\n", Name);
  298. X        exit(1);
  299. X        }
  300. X
  301. X        /* Realloc() might have returned a different address,
  302. X         * so we must ensure that p points to the new array...
  303. X         */
  304. X        p = &Result[WhereWeWere];
  305. X    }
  306. X    *p++ = ch;
  307. X    }
  308. X    *p = '\0';
  309. X
  310. X    if (ch == EOF) {
  311. X    if (Result[0] != '\0') {
  312. X        return Result;
  313. X    }
  314. X    return (char *) 0;
  315. X    }
  316. X    if (p - Result > 1) {
  317. X    Result = realloc(Result, (unsigned) (p - Result + 1));
  318. X    if (Result == (char *) 0) {
  319. X        (void) fprintf(stderr, "can't save line in \"%s\"\n", Name);
  320. X        exit(1);
  321. X    }
  322. X    }
  323. X    return Result;
  324. X}
  325. X
  326. XDelete(e)
  327. X    t_listel *e;
  328. X{
  329. X    register t_listel **lpp;
  330. X
  331. X    for (lpp = &SeenSoFar; *lpp; lpp = &(*lpp)->Next) {
  332. X    if ((*lpp)->Name == e->Name) { /* same address, NOT strcmp! */
  333. X        t_listel *next = e->Next;
  334. X
  335. X        (void) free(e->Name);
  336. X        if (e->statbuf) {
  337. X        (void) free((char *) e->statbuf);
  338. X        }
  339. X        (void) free((char *) e);
  340. X        *lpp = next;
  341. X
  342. X        break;
  343. X    }
  344. X    }
  345. X}
  346. X
  347. X/*
  348. X * $Log:    cmpall.c,v $
  349. X * Revision 1.2  90/08/02  17:22:12  lee
  350. X * delinted it a little...
  351. X * 
  352. X * Revision 1.1  90/08/02  17:04:26  lee
  353. X * Initial revision
  354. X * 
  355. X *
  356. X */
  357. @@@End of cmpall.c
  358. ls -l cmpall.c
  359. exit 0
  360.  
  361. Lee
  362. -- 
  363. Liam R. E. Quin,  lee@sq.com, {utai,utzoo}!sq!lee,  SoftQuad Inc., Toronto
  364. ``He left her a copy of his calculations [...]  Since she was a cystologist,
  365.   she might have analysed the equations, but at the moment she was occupied
  366.   with knitting a bootee.''  [John Boyd, Pollinators of Eden, 217]
  367.