home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume17 / finddup / part01 next >
Encoding:
Internet Message Format  |  1991-02-24  |  18.5 KB

  1. From: davidsen@crdos1.crd.ge.com (Bill Davidsen)
  2. Newsgroups: comp.sources.misc
  3. Subject: v17i013:  finddup - Find duplicate files, Part01/01
  4. Message-ID: <1991Feb24.041419.20094@sparky.IMD.Sterling.COM>
  5. Date: 24 Feb 91 04:14:19 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: 82482af9 35a55990 18851344 93d80b3e
  8.  
  9. Submitted-by: Bill Davidsen <davidsen@crdos1.crd.ge.com>
  10. Posting-number: Volume 17, Issue 13
  11. Archive-name: finddup/part01
  12. Supersedes: finddup: Volume 16, Issue 95
  13.  
  14. #!/bin/sh
  15. # shar:    Shell Archiver  (v1.29)
  16. #
  17. #    Run the following text with /bin/sh to create:
  18. #      finddup.c
  19. #      finddup.1C
  20. #      makefile
  21. #      getopt.c
  22. #
  23. echo "x - extracting finddup.c (Text)"
  24. sed 's/^X//' << 'SHAR_EOF' > finddup.c &&
  25. X/****************************************************************\
  26. X|  finddup.c - the one true find duplicate files program
  27. X|----------------------------------------------------------------
  28. X|  Bill Davidsen, last hacked 2/22/91
  29. X|  Copyright (c) 1991 by Bill Davidsen, all rights reserved. This
  30. X|  program may be freely used and distributed in its original
  31. X|  form. Modified versions must be distributed with the original
  32. X|  unmodified source code, and redistribution of the original code
  33. X|  or any derivative program may not be restricted.
  34. X|----------------------------------------------------------------
  35. X|  Calling sequence:
  36. X|   finddup [-l] checklist
  37. X|
  38. X|  where checklist is the name of a file containing filenames to 
  39. X|  be checked, such as produced by "find . -type f -print >file"
  40. X|  returns a list of linked and duplicated files.
  41. X|
  42. X|  If the -l option is used the hard links will not be displayed.
  43. X\***************************************************************/
  44. X
  45. X#include <stdio.h>
  46. X#include <sys/types.h>
  47. X#include <sys/stat.h>
  48. X#include <malloc.h>
  49. X
  50. X/* parameters */
  51. X#define MAXFN    120             /* max filename length */
  52. X
  53. X/* constants */
  54. X#define EOS        ((char) '\0')    /* end of string */
  55. X#define FL_CRC    0x0001            /* flag if CRC valid */
  56. X#define FL_DUP    0x0002            /* files are duplicates */
  57. X#define FL_LNK    0x0004            /* file is a link */
  58. X
  59. X/* macros */
  60. X#ifdef DEBUG
  61. X#define debug(X) if (DebugFlg) printf X
  62. X#define OPTSTR    "lhd"
  63. X#else
  64. X#define debug(X)
  65. X#define OPTSTR    "lh"
  66. X#endif
  67. X#define SORT qsort((char *)filelist, n_files, sizeof(filedesc), comp1);
  68. X#define GetFlag(x,f) ((filelist[x].flags & (f)) != 0)
  69. X#define SetFlag(x,f) (filelist[x].flags |= (f))
  70. X
  71. Xtypedef struct {
  72. X    off_t length;                /* file length */
  73. X    unsigned long crc32;        /* CRC for same length */
  74. X    dev_t device;                /* physical device # */
  75. X    ino_t inode;                /* inode for link detect */
  76. X    off_t nameloc;                /* name loc in names file */
  77. X    char flags;                    /* flags for compare */
  78. X} filedesc;
  79. X
  80. Xfiledesc *filelist;                /* master sorted list of files */
  81. Xlong n_files = 0;                /* # files in the array */
  82. Xlong max_files = 0;                /* entries allocated in the array */
  83. Xint linkflag = 1;                /* show links */
  84. Xint DebugFlg = 0;                /* inline debug flag */
  85. XFILE *namefd;                    /* file for names */
  86. Xextern int
  87. X    opterr,                        /* error control flag */
  88. X    optind;                        /* index for next arg */
  89. X
  90. X/* help message, in a table format */
  91. Xstatic char *HelpMsg[] = {
  92. X    "Calling sequence:",
  93. X    "",
  94. X    "  finddup [options] list",
  95. X    "",
  96. X    "where list is a list of files to check, such as generated",
  97. X    "by \"find . -type f -print > file\"",
  98. X    "",
  99. X    "Options:",
  100. X    "  -l - don't list hard links",
  101. X#ifdef DEBUG
  102. X    "  -d - debug (must compile with DEBUG)"
  103. X#endif /* ?DEBUG */
  104. X};
  105. Xstatic HelpLen = sizeof(HelpMsg)/sizeof(char *);
  106. X
  107. X#ifndef    lint
  108. Xstatic char *SCCSid[] = {
  109. X    "@(#)finddup.c v1.13 - 2/22/91",
  110. X    "Copyright (c) 1991 by Bill Davidsen, all rights reserved"
  111. X};
  112. X#endif
  113. X
  114. Xint comp1();                    /* compare two filedesc's */
  115. Xvoid scan1();                    /* make the CRC scan */
  116. Xvoid scan2();                    /* do full compare if needed */
  117. Xvoid scan3();                    /* print the results */
  118. Xunsigned long get_crc();        /* get crc32 on a file */
  119. Xchar *getfn();                    /* get a filename by index */
  120. X
  121. Xmain(argc, argv)
  122. Xint argc;
  123. Xchar *argv[];
  124. X{
  125. X    char curfile[MAXFN];
  126. X    struct stat statbuf;
  127. X    int ch;
  128. X    int firsterr = 0;            /* flag on 1st error for format */
  129. X    int firsttrace = 0;            /* flag for 1st trace output */
  130. X    off_t loc;                    /* location of name in the file */
  131. X    int zl_hdr = 1;                /* need header for zero-length files list */
  132. X    filedesc *curptr;            /* pointer to current storage loc */
  133. X
  134. X    /* parse options, if any */
  135. X    opterr = 0;
  136. X    while ((ch = getopt(argc, argv, OPTSTR)) != EOF) {
  137. X        switch (ch) {
  138. X        case 'l': /* set link flag */
  139. X            linkflag = 0;
  140. X            break;
  141. X#ifdef DEBUG
  142. X        case 'd': /* debug */
  143. X            ++DebugFlg;
  144. X            break;
  145. X#endif /* ?DEBUG */
  146. X        case 'h': /* help */
  147. X        case '?':
  148. X            for (ch = 0; ch < HelpLen; ++ch) {
  149. X                printf("%s\n", HelpMsg[ch]);
  150. X            }
  151. X            exit(1);
  152. X        }
  153. X    }
  154. X
  155. X    /* correct for the options */
  156. X    argc -= (optind-1);
  157. X    argv += (optind-1);
  158. X
  159. X    /* check for filename given, and open it */
  160. X    if (argc != 2) {
  161. X        fprintf(stderr, "Needs name of file with filenames\n");
  162. X        exit(1);
  163. X    }
  164. X    namefd = fopen(argv[1], "r");
  165. X    if (namefd == NULL) {
  166. X        perror("Can't open names file");
  167. X        exit(1);
  168. X    }
  169. X
  170. X    /* start the list of name info's */
  171. X    filelist = (filedesc *) malloc(50 * sizeof(filedesc));
  172. X    if (filelist == NULL) {
  173. X        perror("Can't start files vector");
  174. X        exit(1);
  175. X    }
  176. X    /* finish the pointers */
  177. X    max_files = 50;
  178. X    debug(("First vector allocated @ %08lx, size %d bytes\n",
  179. X        (long) filelist, 50*sizeof(filedesc)
  180. X    ));
  181. X    fprintf(stderr, "build list...");
  182. X
  183. X    /* this is the build loop */
  184. X    while (loc = ftell(namefd), fgets(curfile, MAXFN, namefd) != NULL) {
  185. X        /* check for room in the buffer */
  186. X        if (n_files == max_files) {
  187. X            /* allocate more space */
  188. X            max_files += 50;
  189. X            filelist =
  190. X                (filedesc *) realloc(filelist, (max_files)*sizeof(filedesc));
  191. X            if (filelist == NULL) {
  192. X                perror("Out of memory!");
  193. X                exit(1);
  194. X            }
  195. X            debug(("Got more memory!\n"));
  196. X        }
  197. X        curfile[strlen(curfile)-1] = EOS;
  198. X
  199. X        /* add the data for this one */
  200. X        if (stat(curfile, &statbuf)) {
  201. X            fprintf(stderr, "%c  %s - ", 
  202. X                (firsterr++ == 0 ? '\n' : '\r'), curfile
  203. X            );
  204. X            perror("ignored");
  205. X            continue;
  206. X        }
  207. X
  208. X        /* check for zero length files */
  209. X        if ( statbuf.st_size == 0) {
  210. X            if (zl_hdr) {
  211. X                zl_hdr = 0;
  212. X                printf("Zero length files:\n\n");
  213. X            }
  214. X            printf("%s\n", curfile);
  215. X            continue;
  216. X        }
  217. X
  218. X        curptr = filelist + n_files++;
  219. X        curptr->nameloc = loc;
  220. X        curptr->length = statbuf.st_size;
  221. X        curptr->device = statbuf.st_dev;
  222. X        curptr->inode = statbuf.st_ino;
  223. X        curptr->flags = 0;
  224. X        debug(("%cName[%d] %s, size %ld, inode %d\n",
  225. X            (firsttrace++ == 0 ? '\n' : '\r'), n_files, curfile,
  226. X            (long) statbuf.st_size, statbuf.st_ino
  227. X        ));
  228. X    }
  229. X
  230. X    /* sort the list by size, device, and inode */
  231. X    fprintf(stderr, "sort...");
  232. X    SORT;
  233. X
  234. X    /* make the first scan for equal lengths */
  235. X    fprintf(stderr, "scan1...");
  236. X    scan1();
  237. X
  238. X    /* make the second scan for dup CRC also */
  239. X    fprintf(stderr, "scan2...");
  240. X    scan2();
  241. X
  242. X    fprintf(stderr, "done\n");
  243. X
  244. X#ifdef DEBUG
  245. X    for (loc = 0; DebugFlg > 1 && loc < n_files; ++loc) {
  246. X        curptr = filelist + loc;
  247. X        printf("%8ld %08lx %6d %6d %02x\n",
  248. X            curptr->length, curptr->crc32,
  249. X            curptr->device, curptr->inode,
  250. X            curptr->flags
  251. X        );
  252. X    }
  253. X#endif
  254. X
  255. X    /* now scan and output dups */
  256. X    scan3();
  257. X
  258. X    exit(0);
  259. X}
  260. X
  261. X/* comp1 - compare two values */
  262. Xint
  263. Xcomp1(p1, p2)
  264. Xchar *p1, *p2;
  265. X{
  266. X    register filedesc *p1a = (filedesc *)p1, *p2a = (filedesc *)p2;
  267. X    register int retval;
  268. X
  269. X    (retval = p1a->length - p2a->length) ||
  270. X    (retval = p1a->crc32 - p2a->crc32) ||
  271. X    (retval = p1a->device - p2a->device) ||
  272. X    (retval = p1a->inode - p2a->inode);
  273. X    
  274. X    return retval;
  275. X}
  276. X
  277. X/* scan1 - get a CRC32 for files of equal length */
  278. X
  279. Xvoid
  280. Xscan1() {
  281. X    FILE *fp;
  282. X    int ix, needsort = 0;
  283. X
  284. X    for (ix = 1; ix < n_files; ++ix) {
  285. X        if (filelist[ix-1].length == filelist[ix].length) {
  286. X            /* get a CRC for each */
  287. X            if (! GetFlag(ix-1, FL_CRC)) {
  288. X                filelist[ix-1].crc32 = get_crc(ix-1);
  289. X                SetFlag(ix-1, FL_CRC);
  290. X            }
  291. X            if (! GetFlag(ix, FL_CRC)) {
  292. X                filelist[ix].crc32 = get_crc(ix);
  293. X                SetFlag(ix, FL_CRC);
  294. X            }
  295. X            needsort = 1;
  296. X        }
  297. X    }
  298. X
  299. X    if (needsort) SORT;
  300. X}
  301. X
  302. X/* scan2 - full compare if CRC is equal */
  303. X
  304. Xvoid
  305. Xscan2() {
  306. X    int ix, ix2, lastix;
  307. X    int inmatch;                /* 1st filename has been printed */
  308. X    int need_hdr = 1;            /* Need a hdr for the hard link list */
  309. X    int lnkmatch;                /* flag for matching links */
  310. X    register filedesc *p1, *p2;
  311. X    filedesc wkdesc;
  312. X
  313. X    /* mark links and output before dup check */
  314. X    for (ix = 0; ix < n_files; ix = ix2) {
  315. X        p1 = filelist + ix;
  316. X        for (ix2 = ix+1, p2 = p1+1, inmatch = 0;
  317. X            ix2 < n_files
  318. X                && p1->device == p2->device
  319. X                && p1->inode == p2->inode;
  320. X            ++ix2, ++p2
  321. X        ) {
  322. X            SetFlag(ix2, FL_LNK);
  323. X            if (linkflag) {
  324. X                if (need_hdr) {
  325. X                    need_hdr = 0;
  326. X                    printf("\n\nHard link summary:\n\n");
  327. X                }
  328. X
  329. X                if (!inmatch) {
  330. X                    inmatch = 1;
  331. X                    printf("\nFILE: %s\n", getfn(ix));
  332. X                }
  333. X                printf("LINK: %s\n", getfn(ix2));
  334. X            }
  335. X        }
  336. X    }
  337. X    debug(("\nStart dupscan"));
  338. X
  339. X    /* now really scan for duplicates */
  340. X    for (ix = 0; ix < n_files; ix = lastix) {
  341. X        p1 = filelist + ix;
  342. X        for (lastix = ix2 = ix+1, p2 = p1+1, lnkmatch = 1;
  343. X            ix2 < n_files
  344. X                && p1->length == p2->length
  345. X                && p1->crc32 == p2->crc32;
  346. X            ++ix2, ++p2
  347. X        ) {
  348. X            if ((GetFlag(ix2, FL_LNK) && lnkmatch)
  349. X                || fullcmp(ix, ix2) == 0
  350. X            ) {
  351. X                SetFlag(ix2, FL_DUP);
  352. X                /* move if needed */
  353. X                if (lastix != ix2) {
  354. X                    int n1, n2;
  355. X
  356. X                    debug(("\n  swap %d and %d", lastix, ix2));
  357. X                    wkdesc = filelist[ix2];
  358. X                    for (n1 = ix2; n1 > lastix; --n1) {
  359. X                        filelist[n1] = filelist[n1-1];
  360. X                    }
  361. X                    filelist[lastix++] = wkdesc;
  362. X                }
  363. X                lnkmatch = 1;
  364. X            }
  365. X            else {
  366. X                /* other links don't match */
  367. X                lnkmatch = 0;
  368. X            }
  369. X        }
  370. X    }
  371. X}
  372. X
  373. X/* scan3 - output dups */
  374. X
  375. Xvoid
  376. Xscan3()
  377. X{
  378. X    register filedesc *p1, *p2;
  379. X    int ix, ix2, inmatch, need_hdr = 1;
  380. X    char *fnptr;                /* pointer to the filename for sups */
  381. X    char headfn[132];            /* temp holder of the filename */
  382. X
  383. X    /* now repeat for duplicates, links or not */
  384. X    for (ix = 0; ix < n_files; ++ix) {
  385. X        if (GetFlag(ix, FL_DUP)) {
  386. X            /* put out a header if you haven't */
  387. X            if (!inmatch) {
  388. X                strcpy(headfn, getfn(ix-1));
  389. X                fnptr = headfn;
  390. X            }
  391. X            inmatch = 1;
  392. X            if (linkflag || !GetFlag(ix, FL_LNK)) {
  393. X                /* header on the very first */
  394. X                if (need_hdr) {
  395. X                    need_hdr = 0;
  396. X                    printf("\n\nList of files with duplicate contents");
  397. X                    if (linkflag) printf(" (includes hard links)");
  398. X                    putchar('\n');
  399. X                }
  400. X
  401. X                /* 1st filename if any dups */
  402. X                if (fnptr != NULL) {
  403. X                    printf("\nFILE: %s\n", fnptr);
  404. X                    fnptr = NULL;
  405. X                }
  406. X                printf("DUP:  %s\n", getfn(ix));
  407. X            }
  408. X        }
  409. X        else inmatch = 0;
  410. X    }
  411. X}
  412. X
  413. X/* get_crc - get a CRC32 for a file */
  414. X
  415. Xunsigned long
  416. Xget_crc(ix)
  417. Xint ix;
  418. X{
  419. X    FILE *fp;
  420. X    register unsigned long val1 = 0x90909090, val2 = 0xeaeaeaea;
  421. X    register int carry;
  422. X    int ch;
  423. X    char fname[MAXFN];
  424. X
  425. X    /* open the file */
  426. X    fseek(namefd, filelist[ix].nameloc, 0);
  427. X    fgets(fname, MAXFN, namefd);
  428. X    fname[strlen(fname)-1] = EOS;
  429. X    debug(("\nCRC start - %s ", fname));
  430. X    if ((fp = fopen(fname, "r")) == NULL) {
  431. X        fprintf(stderr, "Can't read file %s\n", fname);
  432. X        exit(1);
  433. X    }
  434. X
  435. X    /* build the CRC values */
  436. X    while ((ch = fgetc(fp)) != EOF) {
  437. X        carry = (val1 & 0x8000000) != 0;
  438. X        val1 = (val1 << 1) ^ ch + carry;
  439. X        val2 += ch << (ch & 003);
  440. X    }
  441. X    fclose(fp);
  442. X    debug(("v1: %08lx v2: %08lx ", val1, val2));
  443. X
  444. X    return ((val1 & 0xffff) << 12) ^ (val2 && 0xffffff);
  445. X}
  446. X
  447. X/* getfn - get filename from index */
  448. X
  449. Xchar *
  450. Xgetfn(ix)
  451. Xoff_t ix;
  452. X{
  453. X    static char fnbuf[MAXFN];
  454. X
  455. X    fseek(namefd, filelist[ix].nameloc, 0);
  456. X    fgets(fnbuf, MAXFN, namefd);
  457. X    fnbuf[strlen(fnbuf)-1] = EOS;
  458. X
  459. X    return fnbuf;
  460. X}
  461. X
  462. X/* fullcmp - compare two files, bit for bit */
  463. X
  464. Xint
  465. Xfullcmp(v1, v2)
  466. Xint v1, v2;
  467. X{
  468. X    FILE *fp1, *fp2;
  469. X    char filename[MAXFN];
  470. X    register int ch;
  471. X
  472. X    /* open the files */
  473. X    strcpy(filename, getfn(v1));
  474. X    fp1 = fopen(filename, "r");
  475. X    if (fp1 == NULL) {
  476. X        fprintf(stderr, "%s: ", filename);
  477. X        perror("can't access for read");
  478. X        exit(1);
  479. X    }
  480. X    debug(("\nFull compare %s\n         and", filename));
  481. X
  482. X    strcpy(filename, getfn(v2));
  483. X    fp2 = fopen(filename, "r");
  484. X    if (fp2 == NULL) {
  485. X        fprintf(stderr, "%s: ", filename);
  486. X        perror("can't access for read");
  487. X        exit(1);
  488. X    }
  489. X    debug(("%s", filename));
  490. X
  491. X    /* now do the compare */
  492. X    while ((ch = getc(fp1)) != EOF) {
  493. X        if (ch - getc(fp2)) break;
  494. X    }
  495. X
  496. X    /* close files and return value */
  497. X    fclose(fp1);
  498. X    fclose(fp2);
  499. X    debug(("\n      return %d", !(ch == EOF)));
  500. X    return (!(ch == EOF));
  501. X}
  502. SHAR_EOF
  503. chmod 0444 finddup.c || echo "restore of finddup.c fails"
  504. echo "x - extracting finddup.1C (Text)"
  505. sed 's/^X//' << 'SHAR_EOF' > finddup.1C &&
  506. X.TH FINDDUP 1 LOCAL
  507. X.SH NAME
  508. Xfinddup - find duplicate files in a list
  509. X.SH SYNOPSIS
  510. Xfinddup [options] filename
  511. X.SH DESCRIPTION
  512. X.ds fd \fBfinddup\fP
  513. X\*(fd reads a list of filenames from the named file and scans them,
  514. Xbuilding a list of duplicate files and hard links. These are then
  515. Xwritten to stdout for the user's information. This can be used to reduce
  516. Xdisk usage, etc.
  517. X.SS OPTIONS
  518. X  -l - don't show info on hard links
  519. X  -d - debug. May be used more than once for more info
  520. X.SS How it works
  521. X\*(fd stats each name and saves the file length, device, and inode. It
  522. Xthen sorts the list and builds a CRC for each file which has the same
  523. Xlength as another file. For files which have the same length and CRC, a
  524. Xbyte by byte comparison is done to be sure that they are duplicates.
  525. X.sp
  526. XThe CRC step for N files of size S bytes requires reading n*S total
  527. Xbytes, while the byte by byte check must be done for every file against
  528. Xevery other, and read S*N*(N-1) bytes. Thus the CRC is a large timesaver
  529. Xin most cases.
  530. X.SH EXAMPLES
  531. X $ find /u -type f -print > file.list.tmp
  532. X $ finddup file.list.tmp
  533. X.SH FILES
  534. XOnly the file with the filenames.
  535. X.SH SEE ALSO
  536. Xfind(1).
  537. X.SH DIAGNOSTICS
  538. XIf files are named in the specification file but not present they will
  539. Xbe ignored. If an existing file can not be read the program will
  540. Xterminate rather than generate an incomplete list of duplicates.
  541. X.SH LIMITATIONS
  542. XAn option to generate a partial list could be added when a file can't be
  543. Xaccessed. An option to list only duplites which are not hard links could
  544. Xbe added.
  545. X.SH AUTHOR
  546. XBill Davidsen, davidsen@crdos1.crd.ge.com
  547. X.SH Copyright
  548. XCopyright (c) 1991 by Bill Davidsen, all rights reserved. This
  549. Xprogram may be freely used and distributed in its original
  550. Xform. Modified versions must be distributed with the original
  551. Xunmodified source code, and redistribution of the original code
  552. Xor any derivative program may not be restricted.
  553. SHAR_EOF
  554. chmod 0666 finddup.1C || echo "restore of finddup.1C fails"
  555. echo "x - extracting makefile (Text)"
  556. sed 's/^X//' << 'SHAR_EOF' > makefile &&
  557. X# makefile for finddup
  558. X
  559. X# ================> custom here
  560. XCC        = /bin/cc
  561. XCFLAGS        = -O
  562. XLFLAGS        =
  563. XLIBS        =
  564. X# if you don't have getopt on your library, add getopt.o here
  565. XOPBLST        = finddup.o
  566. X# your favorite shar prog
  567. XSHAR        = shar
  568. XSHARLST        = finddup.c finddup.1C makefile getopt.c
  569. XBACKLST        = s.finddup.c finddup.1C makefile getopt.c
  570. X# ================> custom ends
  571. X
  572. Xfinddup:    $(OBJLST)
  573. X    $(CC) -o finddup $(LFLAGS) $(OBJLST) -s
  574. X
  575. Xdebug:        rfinddup
  576. X
  577. X# note that this does NOT leave an object file
  578. Xrfinddup:    finddup.c
  579. X    $(CC) -o rfinddup -g -DDEBUG finddup.c
  580. X
  581. Xshar:        finddup.shar
  582. X
  583. Xfinddup.shar:    $(SHARLST)
  584. X    $(SHAR) $(SHARLST) > finddup.shar
  585. X
  586. Xclean:
  587. X    if [ -f p.finddup.c -o ! -f s.finddup.c ]; then exit 1; fi
  588. X    rm -f finddup.[co] finddup rfinddup finddup.shar
  589. X
  590. Xbackup:        clean finddup.zoo
  591. X
  592. Xfinddup.zoo:    $(BACKLST)
  593. X    zoo aunM finddup $(BACKLST)
  594. SHAR_EOF
  595. chmod 0644 makefile || echo "restore of makefile fails"
  596. echo "x - extracting getopt.c (Text)"
  597. sed 's/^X//' << 'SHAR_EOF' > getopt.c &&
  598. X/*
  599. X**    @(#)getopt.c    2.5 (smail) 9/15/87
  600. X*/
  601. X
  602. X/*
  603. X * Here's something you've all been waiting for:  the AT&T public domain
  604. X * source for getopt(3).  It is the code which was given out at the 1985
  605. X * UNIFORUM conference in Dallas.  I obtained it by electronic mail
  606. X * directly from AT&T.  The people there assure me that it is indeed
  607. X * in the public domain.
  608. X * 
  609. X * There is no manual page.  That is because the one they gave out at
  610. X * UNIFORUM was slightly different from the current System V Release 2
  611. X * manual page.  The difference apparently involved a note about the
  612. X * famous rules 5 and 6, recommending using white space between an option
  613. X * and its first argument, and not grouping options that have arguments.
  614. X * Getopt itself is currently lenient about both of these things White
  615. X * space is allowed, but not mandatory, and the last option in a group can
  616. X * have an argument.  That particular version of the man page evidently
  617. X * has no official existence, and my source at AT&T did not send a copy.
  618. X * The current SVR2 man page reflects the actual behavor of this getopt.
  619. X * However, I am not about to post a copy of anything licensed by AT&T.
  620. X */
  621. X
  622. X
  623. X/*LINTLIBRARY*/
  624. X#define NULL    0
  625. X#define EOF    (-1)
  626. X#define ERR(s, c)    if(opterr){\
  627. X    extern int write();\
  628. X    char errbuf[2];\
  629. X    errbuf[0] = c; errbuf[1] = '\n';\
  630. X    (void) write(2, argv[0], (unsigned)strlen(argv[0]));\
  631. X    (void) write(2, s, (unsigned)strlen(s));\
  632. X    (void) write(2, errbuf, 2);}
  633. X
  634. Xextern char *strchr();
  635. X
  636. Xint    opterr = 1;
  637. Xint    optind = 1;
  638. Xint    optopt;
  639. Xchar    *optarg;
  640. X
  641. Xint
  642. Xgetopt(argc, argv, opts)
  643. Xint    argc;
  644. Xchar    **argv, *opts;
  645. X{
  646. X    static int sp = 1;
  647. X    register int c;
  648. X    register char *cp;
  649. X
  650. X    if(sp == 1)
  651. X        if(optind >= argc ||
  652. X           argv[optind][0] != '-' || argv[optind][1] == '\0')
  653. X            return(EOF);
  654. X        else if(strcmp(argv[optind], "--") == NULL) {
  655. X            optind++;
  656. X            return(EOF);
  657. X        }
  658. X    optopt = c = argv[optind][sp];
  659. X    if(c == ':' || (cp=strchr(opts, c)) == NULL) {
  660. X        ERR(": illegal option -- ", c);
  661. X        if(argv[optind][++sp] == '\0') {
  662. X            optind++;
  663. X            sp = 1;
  664. X        }
  665. X        return('?');
  666. X    }
  667. X    if(*++cp == ':') {
  668. X        if(argv[optind][sp+1] != '\0')
  669. X            optarg = &argv[optind++][sp+1];
  670. X        else if(++optind >= argc) {
  671. X            ERR(": option requires an argument -- ", c);
  672. X            sp = 1;
  673. X            return('?');
  674. X        } else
  675. X            optarg = argv[optind++];
  676. X        sp = 1;
  677. X    } else {
  678. X        if(argv[optind][++sp] == '\0') {
  679. X            sp = 1;
  680. X            optind++;
  681. X        }
  682. X        optarg = NULL;
  683. X    }
  684. X    return(c);
  685. X}
  686. SHAR_EOF
  687. chmod 0644 getopt.c || echo "restore of getopt.c fails"
  688. exit 0
  689. --
  690. bill davidsen (ibmbin-request@crdgw1.crd.ge.com, uunet!crdgw1!ibmbin-request)
  691.   moderator c.b.i.p newsgroup and 386users mailing list.
  692.   binary submissions to ibmbin@crdgw1.crd.ge.com (uunet!crdgw1!ibmbin)
  693.             "Stupidity, like virtue, is its own reward" -me
  694.  
  695. exit 0 # Just in case...
  696. -- 
  697. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  698. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  699. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  700. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  701.