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

  1. From: wht@n4hgf.Mt-Park.GA.US (Warren Tucker)
  2. Newsgroups: alt.sources
  3. Subject: cktar - compare tar tape against file system
  4. Message-ID: <236@n4hgf.Mt-Park.GA.US>
  5. Date: 9 Nov 90 09:49:50 GMT
  6.  
  7. Submitted-by: wht@n4hgf
  8. Archive-name: cktar/part01
  9.  
  10. This program reads a tar input file and compares each
  11. file on the volume with a matching file on disk.
  12.  
  13. If a file on the tar file has the same length as a file on the
  14. disk, the files are compared byte for byte and the byte position
  15. of the first nonmatching byte is printed.
  16.  
  17. If a file on the tar file has a different length from a file on
  18. the disk, this fact is noted.
  19.  
  20. If the -U switch is present, UIDs must match.  If the -G switch is
  21. present, gids must match.  If the -M switch is present, times of last
  22. modification must match.
  23.  
  24. With -t or -v switch, no output is produced for matching files.
  25. With -t, a title is printed for each file regardless of match.
  26. With -v, this output is expanded to show all attributes of the file
  27. on the tar file as well as the match status.
  28.  
  29. If you wish to have cktar read from stdin and do not wish to use
  30. any of the available switches, use the '--' switch.
  31.  
  32. The -A switch acts like the tar A key, causing a leading '/' in
  33. a tar input filename to be elided.
  34.  
  35. Exceptions are reported by codes appearing after filenames:
  36.    A - stat access denied
  37.    N - non-existent file
  38.    U - uid difference
  39.    G - gid difference
  40.    T - time of last modification difference
  41.    S - size difference
  42.    C - comparison failed at position shown by decimal value after C
  43. The 'S' and 'C' codes will never appear together, 'A' and 'N' will
  44. never appear together or with any other codes, but any other mixture
  45. of codes is possible.  Only the C code has a decimal file position
  46. appended.
  47.  
  48. #!/bin/sh
  49. # This is cktar, a shell archive (shar 3.46)
  50. # made 11/09/1990 09:44 UTC by wht@n4hgf
  51. # Source directory /u1/src/src
  52. #
  53. # existing files will NOT be overwritten unless -c is specified
  54. #
  55. # This shar contains:
  56. # length  mode       name
  57. # ------ ---------- ------------------------------------------
  58. #  14450 -rw-r--r-- cktar.c
  59. #
  60. # ============= cktar.c ==============
  61. if test -f 'cktar.c' -a X"$1" != X"-c"; then
  62.     echo 'x - skipping cktar.c (File already exists)'
  63. else
  64. echo 'x - extracting cktar.c (Text)'
  65. sed 's/^X//' << 'SHAR_EOF' > 'cktar.c' &&
  66. X/* CHK=0xC1F1 */
  67. X/*+-------------------------------------------------------------------------
  68. X    cktar.c - read a tar tape and compare against file system
  69. X    wht@n4hgf.Mt-Park.GA.US
  70. X--------------------------------------------------------------------------*/
  71. X/*+:EDITS:*/
  72. X/*:11-08-1990-16:34-wht-creation */
  73. X
  74. X#include <stdio.h>
  75. X#include <ctype.h>
  76. X#ifdef BSD
  77. X#include <sys/errno.h>
  78. X#include <sys/time.h>
  79. X#define memcmp(s1,s2,c) bcmp(s2,s1,c)
  80. X#else
  81. X#include <errno.h>
  82. X#include <time.h>
  83. X#include <memory.h>
  84. X#endif
  85. X#include <sys/types.h>
  86. X#include <sys/stat.h>
  87. X
  88. X#if defined(min)
  89. X#undef min
  90. X#endif
  91. X#define min(a,b) ((a < b) ? a : b)
  92. X
  93. Xextern int errno;
  94. X
  95. X#define TBLOCK 512
  96. X#define NBLOCK 20
  97. X#define NAMSIZ 100
  98. Xtypedef union tar_block
  99. X{
  100. X    char d[TBLOCK];                    /* data */
  101. X    struct header                    /* header */
  102. X    {
  103. X        char name[NAMSIZ];
  104. X        char mode[8];
  105. X        char uid[8];
  106. X        char gid[8];
  107. X        char size[12];
  108. X        char mtime[12];
  109. X        char chksum[8];
  110. X        char linkflag;
  111. X        char linkname[NAMSIZ];
  112. X        char extno[4];
  113. X        char extotal[4];
  114. X        char efsize[12];
  115. X    } h;
  116. X} TARBLOCK;
  117. X
  118. Xchar *tar_fname = "(stdin)";
  119. XFILE *fptar;
  120. Xint Tsw = 0;
  121. Xint Vsw = 0;
  122. Xint Asw = 0;
  123. Xint Usw = 0;
  124. Xint Gsw = 0;
  125. Xint Msw = 0;
  126. Xchar _tarbuf[TBLOCK * NBLOCK];
  127. Xlong now;
  128. X
  129. Xchar *usage_text[] =
  130. X{
  131. X"usage: cktar [-t] [-v] [-A] [-U] [-G] [-M] [-f tar_input_file] [--]",
  132. X"This program reads a tar input file and compares each",
  133. X"file on the volume with a matching file on disk.",
  134. X"",
  135. X"If a file on the tar file has the same length as a file on the",
  136. X"disk, the files are compared byte for byte and the byte position",
  137. X"of the first nonmatching byte is printed.",
  138. X"",
  139. X"If a file on the tar file has a different length from a file on",
  140. X"the disk, this fact is noted.",
  141. X"",
  142. X"If the -U switch is present, UIDs must match.  If the -G switch is",
  143. X"present, gids must match.  If the -M switch is present, times of last",
  144. X"modification must match.",
  145. X"",
  146. X"With -t or -v switch, no output is produced for matching files.",
  147. X"With -t, a title is printed for each file regardless of match.",
  148. X"With -v, this output is expanded to show all attributes of the file",
  149. X"on the tar file as well as the match status.",
  150. X"",
  151. X"If you wish to have cktar read from stdin and do not wish to use",
  152. X"any of the available switches, use the '--' switch.",
  153. X"",
  154. X"The -A switch acts like the tar A key, causing a leading '/' in",
  155. X"a tar input filename to be elided.",
  156. X"",
  157. X"Exceptions are reported by codes appearing after filenames:",
  158. X"   A - stat access denied",
  159. X"   N - non-existent file",
  160. X"   U - uid difference",
  161. X"   G - gid difference",
  162. X"   T - time of last modification difference",
  163. X"   S - size difference",
  164. X"   C - comparison failed at position shown by decimal value after C",
  165. X"The 'S' and 'C' codes will never appear together, 'A' and 'N' will",
  166. X"never appear together or with any other codes, but any other mixture",
  167. X"of codes is possible.  Only the C code has a decimal file position",
  168. X"appended.",
  169. X"",
  170. X(char *)0
  171. X};
  172. X
  173. X/*+-------------------------------------------------------------------------
  174. X    usage()
  175. X--------------------------------------------------------------------------*/
  176. Xvoid
  177. Xusage()
  178. X{
  179. Xchar **t = usage_text;
  180. X
  181. X    while(*t)
  182. X    {
  183. X        fputs(*t,stderr);
  184. X        fputs("\n",stderr);
  185. X        t++;
  186. X    }
  187. X    exit(1);
  188. X}    /* end of usage */
  189. X
  190. X/*+-----------------------------------------------------------------------
  191. X    mode_map(mode) - build drwxrwxrwx string
  192. X------------------------------------------------------------------------*/
  193. Xchar *
  194. Xmode_map(mode,mode_str)
  195. Xunsigned short mode;
  196. X{
  197. Xregister unsigned ftype = mode & S_IFMT;
  198. Xstatic char result[12];
  199. Xregister char *rtn = result;
  200. X
  201. X    /*          drwxrwxrwx */
  202. X    /*          0123456789 */
  203. X    strcpy(rtn,"----------");
  204. X
  205. X    switch(ftype)
  206. X    {
  207. X        case S_IFIFO:    *rtn = 'p'; break; /* FIFO (named pipe) */
  208. X        case S_IFDIR:    *rtn = 'd'; break; /* directory */
  209. X        case S_IFCHR:    *rtn = 'c'; break; /* character special */
  210. X        case S_IFBLK:    *rtn = 'b'; break; /* block special */
  211. X        case 0:
  212. X        case S_IFREG:    *rtn = '-'; break; /* regular */
  213. X
  214. X#if defined(BSD)
  215. X        case S_IFLNK:    *rtn = 'l'; break; /* symbolic link */
  216. X        case S_IFSOCK:    *rtn = 's'; break; /* socket */
  217. X#endif
  218. X#if defined (M_XENIX) || defined(M_UNIX)
  219. X        case S_IFNAM:                        /* name space entry */
  220. X            if(mode & S_INSEM)                /* semaphore */
  221. X            {
  222. X                *rtn = 's';
  223. X                break;
  224. X            }
  225. X            if(mode & S_INSHD)                /* shared memory */
  226. X            {
  227. X                *rtn = 'm';
  228. X                break;
  229. X            }
  230. X#endif
  231. X
  232. X        default:        *rtn = '?'; break;    /* ??? */
  233. X    }
  234. X
  235. X    if(mode & 000400) *(rtn + 1) = 'r';
  236. X    if(mode & 000200) *(rtn + 2) = 'w';
  237. X    if(mode & 000100) *(rtn + 3) = 'x';
  238. X    if(mode & 004000) *(rtn + 3) = 's';
  239. X    if(mode & 000040) *(rtn + 4) = 'r';
  240. X    if(mode & 000020) *(rtn + 5) = 'w';
  241. X    if(mode & 000010) *(rtn + 6) = 'x';
  242. X    if(mode & 002000) *(rtn + 6) = 's';
  243. X    if(mode & 000004) *(rtn + 7) = 'r';
  244. X    if(mode & 000002) *(rtn + 8) = 'w';
  245. X    if(mode & 000001) *(rtn + 9) = 'x';
  246. X    if(mode & 001000) *(rtn + 9) = 't';
  247. X
  248. X    return(rtn);
  249. X
  250. X}    /* end of mode_map */
  251. X
  252. X/*+-----------------------------------------------------------------------
  253. X    char *epoch_secs_to_str(epoch_secs)
  254. X------------------------------------------------------------------------*/
  255. Xchar *
  256. Xepoch_secs_to_str(epoch_secs)
  257. Xlong epoch_secs;
  258. X{
  259. Xstatic char buf[64];
  260. Xstruct tm *tod;
  261. Xchar *month_name_list = "JanFebMarAprMayJunJulAugSepOctNovDec";
  262. X
  263. X    tod = localtime(&epoch_secs);
  264. X
  265. X    if((now - epoch_secs) > (86400L * 365/2))    /* if older than 6 months */
  266. X    {
  267. X        sprintf(buf,"%02d %-3.3s %04d ",
  268. X            tod->tm_mday,month_name_list + (tod->tm_mon * 3),
  269. X            tod->tm_year + 1900);
  270. X    }
  271. X    else
  272. X    {
  273. X        sprintf(buf,"%02d %-3.3s %02d:%02d",
  274. X            tod->tm_mday,month_name_list + (tod->tm_mon * 3),
  275. X            tod->tm_hour,tod->tm_min);
  276. X    }
  277. X
  278. X    return(buf);
  279. X}    /* end of epoch_secs_to_str */
  280. X
  281. X/*+-------------------------------------------------------------------------
  282. X    report_tarblock(t)
  283. X--------------------------------------------------------------------------*/
  284. Xvoid
  285. Xreport_tarblock(t)
  286. Xregister TARBLOCK *t;
  287. X{
  288. Xlong tmode;
  289. Xlong tsize;
  290. Xlong tuid;
  291. Xlong tgid;
  292. Xlong tmtime;
  293. X
  294. X    if(Vsw)
  295. X    {
  296. X        sscanf(t->h.size,"%lo",&tsize);
  297. X        sscanf(t->h.mode,"%lo",&tmode);
  298. X        sscanf(t->h.uid,"%lo",&tuid);
  299. X        sscanf(t->h.gid,"%lo",&tgid);
  300. X        sscanf(t->h.mtime,"%lo",&tmtime);
  301. X
  302. X        printf("%s ",mode_map((unsigned short)tmode));
  303. X        printf("%4d/%04d ",(unsigned short)tuid,
  304. X            (unsigned short)tgid);
  305. X        printf("%8ld ",tsize);
  306. X        printf("%s ",epoch_secs_to_str(tmtime));
  307. X    }
  308. X    printf("%s ",t->h.name + Asw);
  309. X
  310. X}    /* end of report_tarblock */
  311. X
  312. X/*+-------------------------------------------------------------------------
  313. X    cktar(fpt,t)
  314. X--------------------------------------------------------------------------*/
  315. Xvoid
  316. Xcktar(fpt,t)
  317. XFILE *fpt;
  318. Xregister TARBLOCK *t;
  319. X{
  320. Xchar tb[TBLOCK];    /* tar file data block */
  321. Xlong tmode;            /* tar file mode (as in st_mode) */
  322. Xlong tsize;            /* tar file version of file size */
  323. Xlong tuid;            /* tar file version of uid */
  324. Xlong tgid;            /* tar file version of gid */
  325. Xlong tmtime;        /* tar file version of last mod time */
  326. Xchar db[TBLOCK];    /* disk file data block */
  327. XFILE *fpd = (FILE *)0;
  328. Xchar *dname;        /* ptr to file name with -A (Asw) considerations */
  329. Xstruct stat dstat;    /* disk file stat() result */
  330. Xint report = 0;        /* true if report_tarblock() needed */
  331. Xint do_compare = 1;    /* true if we should continue to compare tape to disk */
  332. Xint len;            /* really a scratch int, often length of read() */
  333. Xlong blks;            /* tape blocks for this file, decremented as we go */
  334. Xlong fpos = 0;        /* file position */
  335. Xint thiscount;        /* TBLOCK or residue (partial block size) */
  336. Xchar *tbp;            /* hack pointer to tape buf for final mismatch calc */
  337. Xchar *dbp;            /* hack pointer to disk buf for final mismatch calc */
  338. Xchar exceptions[32]; /* accumulator for comparison failures */
  339. X
  340. X    exceptions[0] = 0;
  341. X
  342. X    /*
  343. X     * if the tar "file" is a link and -t, say so
  344. X     */
  345. X    if(t->h.linkflag)
  346. X    {
  347. X        if(Tsw)
  348. X        {
  349. X            report_tarblock(t);
  350. X            fputs("link->",stdout);
  351. X            fputs(t->h.linkname,stdout);
  352. X        }
  353. X        return;
  354. X    }
  355. X
  356. X#ifdef BSD
  357. X    sscanf(t->h.mode,"%lo",&tmode);
  358. X    if((t->h.mode & S_IFMT) && ((t->h.mode & S_IFMT) != S_IFREG)
  359. X    {
  360. X        if(Tsw)
  361. X        {
  362. X            report_tarblock(t);
  363. X            fputs("not regular file\n",stdout);
  364. X        }
  365. X        return;
  366. X    }
  367. X#endif
  368. X
  369. X    /*
  370. X     * get the dope on the tar file for gross level comparison
  371. X     */
  372. X    sscanf(t->h.size,"%lo",&tsize);
  373. X    sscanf(t->h.uid,"%lo",&tuid);
  374. X    sscanf(t->h.gid,"%lo",&tgid);
  375. X    sscanf(t->h.mtime,"%lo",&tmtime);
  376. X
  377. X    /*
  378. X     * figger out how many data blocks for this file
  379. X     */
  380. X    blks = tsize / TBLOCK;
  381. X    if(tsize - (blks * TBLOCK))
  382. X        blks++;
  383. X
  384. X    /*
  385. X     * get file name with consideration for a possible -A switch
  386. X     * then, stat() the file; if we cannot stat the file, mark
  387. X     * it in the exceptions string and then do little else
  388. X     * other than space over the tape file
  389. X     */
  390. X    dname = t->h.name + Asw;
  391. X    if(stat(dname,&dstat))
  392. X    {
  393. X        if(errno = EACCES)
  394. X            strcat(exceptions,"A");    /* stat access denied */
  395. X        else
  396. X            strcat(exceptions,"N");    /* non-existent file */
  397. X        report = 1;
  398. X        do_compare = 0;
  399. X    }
  400. X
  401. X    /*
  402. X     * perform uid, gid and mtime comparisons; even if any
  403. X     * or all of them fail, we'll still do a file data comparison
  404. X     * provided the tape and data files have the same length
  405. X     */
  406. X    if(do_compare && Usw && (dstat.st_uid != (unsigned short)tsize))
  407. X    {
  408. X        strcat(exceptions,"U");    /* uid difference */
  409. X        report = 1;
  410. X    }
  411. X    if(do_compare && Gsw && (dstat.st_gid != (unsigned short)tsize))
  412. X    {
  413. X        strcat(exceptions,"G");    /* gid difference */
  414. X        report = 1;
  415. X    }
  416. X    if(do_compare && Msw && (dstat.st_mtime != tmtime))
  417. X    {
  418. X        strcat(exceptions,"T");    /* time of last modification different */
  419. X        report = 1;
  420. X    }
  421. X
  422. X    /*
  423. X     * if tape and disk files don't have the same length,
  424. X     * omit data comparison
  425. X     */
  426. X    if(do_compare && (dstat.st_size != tsize))
  427. X    {
  428. X        strcat(exceptions,"S");    /* size difference */
  429. X        report = 1;
  430. X        do_compare = 0;
  431. X    }
  432. X
  433. X    /*
  434. X     * if we can't open the disk file, data comparison unlikely
  435. X     */
  436. X    if(do_compare && (access(dname,4) || !(fpd = fopen(dname,"r"))))
  437. X    {
  438. X        strcat(exceptions,"R");    /* read access denied */
  439. X        report = 1;
  440. X        do_compare = 0;
  441. X    }
  442. X
  443. X    /*
  444. X     * tape advance and possible data comparison loop
  445. X     */
  446. X    while(blks)
  447. X    {
  448. X        if((len = fread(tb,1,sizeof(tb),fpt)) != sizeof(tb))
  449. X        {
  450. X            if(len < 0)
  451. X                perror(tar_fname);
  452. X            else
  453. X                fprintf(stderr,"%s: short read expecting data (%d bytes)\n",
  454. X                    tar_fname,len);
  455. X            exit(1);
  456. X        }
  457. X        if(do_compare)
  458. X        {
  459. X            thiscount = min(TBLOCK,(int)(tsize - fpos));
  460. X            if((len = fread(db,1,thiscount,fpd)) != thiscount)
  461. X            {
  462. X                if(len < 0)
  463. X                    perror(dname);
  464. X                else
  465. X                {
  466. X                    fprintf(stderr,
  467. X                        "%s: short read expecting %d bytes, got %d\n",
  468. X                        dname,thiscount,len);
  469. X                }
  470. X                exit(1);
  471. X            }
  472. X            /*
  473. X             * compare full tape block with full disk block or
  474. X             * valid portion of tape block with the last 1-511 bytes
  475. X             * of a disk file
  476. X             *
  477. X             * first do the (supposedly) fastest compare available
  478. X             * and if the compare fails, plod through slowly
  479. X             * to calculate the proper file position where the
  480. X             * compare failed
  481. X             */
  482. X            if(memcmp(tb,db,thiscount))
  483. X            {
  484. X                len = 0;
  485. X                tbp = tb;
  486. X                dbp = db;
  487. Xprintf(">>thiscount=%d\n",thiscount);
  488. Xprintf("*tb=%02x *db=%02x\n",*tbp,*dbp);
  489. X                while(len < thiscount)
  490. X                {
  491. X                    if(*tbp != *dbp)
  492. X                        break;
  493. X                    len++,tbp++,dbp++;
  494. X                }
  495. X                sprintf(tb,"C %ld",fpos + len); /* use tape buf for scratch */
  496. X                strcat(exceptions,tb); /* byte compare failure position */
  497. X                report = 1;
  498. X                do_compare = 0;
  499. X            }
  500. X        }                    /* end of do_compare==TRUE tape<->disk comparison */
  501. X        fpos += TBLOCK;
  502. X        blks--;    /* leaving this at bottom in case we ever want current
  503. X                 * block number in the loop
  504. X                 */
  505. X    }                        /* end of while(blks) loop */
  506. X
  507. X    if(fpd)
  508. X        fclose(fpd);
  509. X
  510. X    if(Tsw || report)
  511. X    {
  512. X        report_tarblock(t);
  513. X        if(report)
  514. X            fputs(exceptions,stdout);
  515. X        fputs("\n",stdout);
  516. X    }
  517. X
  518. X}    /* end of cktar */
  519. X
  520. X/*+-------------------------------------------------------------------------
  521. X    getopt(argc,argv,opts) - Thank you, Henry!
  522. X--------------------------------------------------------------------------*/
  523. X#ifdef BSD
  524. X#define strchr index    /* for BSD */
  525. Xextern int strcmp();
  526. Xextern char *strchr();
  527. X
  528. X#define NULL    (char *)0
  529. X#define EOF    (-1)
  530. X#define ERR(s,c)    if(opterr)\
  531. X{ \
  532. X    extern int strlen(),write(); \
  533. X    char errbuf[2]; \
  534. X    errbuf[0] = c; errbuf[1] = '\n'; \
  535. X    (void) write(2,argv[0],(unsigned)strlen(argv[0])); \
  536. X    (void) write(2,s,(unsigned)strlen(s)); \
  537. X    (void) write(2,errbuf,2); \
  538. X}
  539. Xint opterr = 1;
  540. Xint optind = 1;
  541. Xint optopt;
  542. Xchar *optarg;
  543. Xint
  544. Xgetopt(argc,argv,opts)
  545. Xint argc;
  546. Xchar **argv,*opts;
  547. X{
  548. X    static int sp = 1;
  549. X    register int c;
  550. X    register char *cp;
  551. X
  552. X    if(sp == 1)
  553. X    {
  554. X        if(optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
  555. X            return(EOF);
  556. X        else if(!strcmp(argv[optind],"--"))
  557. X        {
  558. X            optind++;
  559. X            return(EOF);
  560. X        }
  561. X    }
  562. X    optopt = c = argv[optind][sp];
  563. X    if(c == ':' || (cp=strchr(opts,c)) == NULL)
  564. X    {
  565. X        ERR(": unknown option, -",c);
  566. X        if(argv[optind][++sp] == '\0')
  567. X        {
  568. X            optind++;
  569. X            sp = 1;
  570. X        }
  571. X        return('?');
  572. X    }
  573. X    if(*++cp == ':')
  574. X    {
  575. X        if(argv[optind][sp+1] != '\0')
  576. X            optarg = &argv[optind++][sp+1];
  577. X        else if(++optind >= argc)
  578. X        {
  579. X            ERR(": argument missing for -",c);
  580. X            sp = 1;
  581. X            return('?');
  582. X        }
  583. X        else
  584. X            optarg = argv[optind++];
  585. X        sp = 1;
  586. X    }
  587. X    else 
  588. X    {
  589. X        if(argv[optind][++sp] == '\0')
  590. X        {
  591. X            sp = 1;
  592. X            optind++;
  593. X        }
  594. X        optarg = NULL;
  595. X    }
  596. X    return(c);
  597. X}    /* end of getopt */
  598. X#endif /* BSD */
  599. X
  600. X/*+-------------------------------------------------------------------------
  601. X    main(argc,argv)
  602. X--------------------------------------------------------------------------*/
  603. Xmain(argc,argv)
  604. Xint argc;
  605. Xchar **argv;
  606. X{
  607. Xint iargv;
  608. Xint itmp;
  609. Xint errflg = 0;
  610. Xint rdlen;
  611. XTARBLOCK tblk;
  612. Xextern char *optarg;
  613. Xextern int optind;
  614. X
  615. X    if(argc == 1)
  616. X        usage();
  617. X
  618. X    time(&now);
  619. X
  620. X    fptar = stdin;
  621. X    setvbuf(fptar,_tarbuf,_IOFBF,sizeof(_tarbuf));
  622. X
  623. X    while((itmp = getopt(argc,argv,"tvAUGMf:")) != -1)
  624. X    {
  625. X        switch(itmp)
  626. X        {
  627. X            case 't':
  628. X                Tsw = 1;
  629. X                break;
  630. X            case 'v':
  631. X                Vsw = 1;
  632. X                break;
  633. X            case 'A':
  634. X                Asw = 1;
  635. X                break;
  636. X            case 'U':
  637. X                Usw = 1;
  638. X                break;
  639. X            case 'G':
  640. X                Gsw = 1;
  641. X                break;
  642. X            case 'M':
  643. X                Msw = 1;
  644. X                break;
  645. X            case 'f':
  646. X                tar_fname = optarg;
  647. X                if(fptar = fopen(tar_fname,"r"))
  648. X                    setvbuf(fptar,_tarbuf,_IOFBF,sizeof(_tarbuf));
  649. X                break;
  650. X            case '?':
  651. X                errflg++;
  652. X        }
  653. X    }
  654. X
  655. X    if(!fptar)
  656. X    {
  657. X        perror((tar_fname && *tar_fname) ? tar_fname : "(null)");
  658. X        exit(1);
  659. X    }
  660. X
  661. X    if(errflg)
  662. X        usage();
  663. X
  664. X    while((rdlen = fread((char *)&tblk,1,sizeof(tblk),fptar)) == sizeof(tblk))
  665. X    {
  666. X        if(!tblk.h.name[0])
  667. X        {
  668. X            rdlen = 0;
  669. X            break;
  670. X        }
  671. X        cktar(fptar,&tblk);
  672. X    }
  673. X
  674. X    if(rdlen)
  675. X    {
  676. X        if(rdlen < 0)
  677. X            perror(tar_fname);
  678. X        else
  679. X        {
  680. X            fprintf(stderr,"%s: short read expecting header (%d bytes)\n",
  681. X                tar_fname,rdlen);
  682. X        }
  683. X    }
  684. X
  685. X    exit(0);
  686. X
  687. X}    /* end of main */
  688. X
  689. X/* vi: set tabstop=4 shiftwidth=4: */
  690. X/* end of cktar.c */
  691. SHAR_EOF
  692. chmod 0644 cktar.c ||
  693. echo 'restore of cktar.c failed'
  694. Wc_c="`wc -c < 'cktar.c'`"
  695. test 14451 -eq "$Wc_c" ||
  696.     echo 'cktar.c: original size 14451, current size' "$Wc_c"
  697. fi
  698. exit 0
  699.  
  700. ----------------------------------------------------------------------------
  701. Warren Tucker                     emory!n4hgf!wht or wht@n4hgf.Mt-Park.GA.US
  702. "I was 35 years old before I knew a pie was meant to be eaten." - Moe Howard
  703.