home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / gnu / tar-1.11.2-src.lha / src / amiga / tar-1.11.2 / diffarch.c < prev    next >
C/C++ Source or Header  |  1994-02-24  |  17KB  |  766 lines

  1. /* Diff files from a tar archive.
  2.    Copyright (C) 1988, 1992, 1993 Free Software Foundation
  3.  
  4. This file is part of GNU Tar.
  5.  
  6. GNU Tar is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU Tar is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Tar; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /*
  21.  * Diff files from a tar archive.
  22.  *
  23.  * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
  24.  */
  25.  
  26. #include <stdio.h>
  27. #include <errno.h>
  28. #ifndef STDC_HEADERS
  29. extern int errno;
  30. #endif
  31. #include <sys/types.h>
  32.  
  33. #ifdef BSD42
  34. #include <sys/file.h>
  35. #else
  36. #ifndef V7
  37. #include <fcntl.h>
  38. #endif
  39. #endif
  40.  
  41. #ifdef HAVE_SYS_MTIO_H
  42. #include <sys/ioctl.h>
  43. #include <sys/mtio.h>
  44. #endif
  45.  
  46. #include "tar.h"
  47. #include "port.h"
  48. #include "rmt.h"
  49.  
  50. #ifndef S_ISLNK
  51. #define lstat stat
  52. #endif
  53.  
  54. extern void *valloc ();
  55.  
  56. extern union record *head;    /* Points to current tape header */
  57. extern struct stat hstat;    /* Stat struct corresponding */
  58. extern int head_standard;    /* Tape header is in ANSI format */
  59.  
  60. void decode_header ();
  61. void diff_sparse_files ();
  62. void fill_in_sparse_array ();
  63. void fl_read ();
  64. long from_oct ();
  65. int do_stat ();
  66. extern void print_header ();
  67. int read_header ();
  68. void saverec ();
  69. void sigh ();
  70. extern void skip_file ();
  71. extern void skip_extended_headers ();
  72. int wantbytes ();
  73.  
  74. extern FILE *msg_file;
  75.  
  76. int now_verifying = 0;        /* Are we verifying at the moment? */
  77.  
  78. int diff_fd;            /* Descriptor of file we're diffing */
  79.  
  80. char *diff_buf = 0;        /* Pointer to area for reading
  81.                        file contents into */
  82.  
  83. char *diff_dir;            /* Directory contents for LF_DUMPDIR */
  84.  
  85. int different = 0;
  86.  
  87. /*struct sp_array *sparsearray;
  88. int         sp_ar_size = 10;*/
  89. /*
  90.  * Initialize for a diff operation
  91.  */
  92. void
  93. diff_init ()
  94. {
  95.   /*NOSTRICT*/
  96.   diff_buf = (char *) valloc ((unsigned) blocksize);
  97.   if (!diff_buf)
  98.     {
  99.       msg ("could not allocate memory for diff buffer of %d bytes",
  100.        blocksize);
  101.       exit (EX_ARGSBAD);
  102.     }
  103. }
  104.  
  105. /*
  106.  * Diff a file against the archive.
  107.  */
  108. void
  109. diff_archive ()
  110. {
  111.   register char *data;
  112.   int check, namelen;
  113.   int err;
  114.   long offset;
  115.   struct stat filestat;
  116.   int compare_chunk ();
  117.   int compare_dir ();
  118.   int no_op ();
  119. #ifndef __MSDOS__
  120.   dev_t dev;
  121.   ino_t ino;
  122. #endif
  123.   char *get_dir_contents ();
  124.   long from_oct ();
  125.  
  126.   errno = EPIPE;        /* FIXME, remove perrors */
  127.  
  128.   saverec (&head);        /* Make sure it sticks around */
  129.   userec (head);        /* And go past it in the archive */
  130.   decode_header (head, &hstat, &head_standard, 1);    /* Snarf fields */
  131.  
  132.   /* Print the record from 'head' and 'hstat' */
  133.   if (f_verbose)
  134.     {
  135.       if (now_verifying)
  136.     fprintf (msg_file, "Verify ");
  137.       print_header ();
  138.     }
  139.  
  140.   switch (head->header.linkflag)
  141.     {
  142.  
  143.     default:
  144.       msg ("Unknown file type '%c' for %s, diffed as normal file",
  145.        head->header.linkflag, current_file_name);
  146.       /* FALL THRU */
  147.  
  148.     case LF_OLDNORMAL:
  149.     case LF_NORMAL:
  150.     case LF_SPARSE:
  151.     case LF_CONTIG:
  152.       /*
  153.          * Appears to be a file.
  154.          * See if it's really a directory.
  155.          */
  156.       namelen = strlen (current_file_name) - 1;
  157.       if (current_file_name[namelen] == '/')
  158.     goto really_dir;
  159.  
  160.  
  161.       if (do_stat (&filestat))
  162.     {
  163.       if (head->header.isextended)
  164.         skip_extended_headers ();
  165.       skip_file ((long) hstat.st_size);
  166.       different++;
  167.       goto quit;
  168.     }
  169.  
  170.       if (!S_ISREG (filestat.st_mode))
  171.     {
  172.       fprintf (msg_file, "%s: not a regular file\n",
  173.            current_file_name);
  174.       skip_file ((long) hstat.st_size);
  175.       different++;
  176.       goto quit;
  177.     }
  178.  
  179.       filestat.st_mode &= 07777;
  180.       if (filestat.st_mode != hstat.st_mode)
  181.     sigh ("mode");
  182.       if (filestat.st_uid != hstat.st_uid)
  183.     sigh ("uid");
  184.       if (filestat.st_gid != hstat.st_gid)
  185.     sigh ("gid");
  186.       if (filestat.st_mtime != hstat.st_mtime)
  187.     sigh ("mod time");
  188.       if (head->header.linkflag != LF_SPARSE &&
  189.       filestat.st_size != hstat.st_size)
  190.     {
  191.       sigh ("size");
  192.       skip_file ((long) hstat.st_size);
  193.       goto quit;
  194.     }
  195.  
  196. #if amigados && __GNUC__
  197.       /* The current gcc environment doesn't deal with having O_NDELAY set.
  198.      An ixemul.library bug?  */
  199.       diff_fd = open (current_file_name, O_RDONLY | O_BINARY);
  200. #else
  201.       diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
  202. #endif
  203.  
  204.       if (diff_fd < 0 && !f_absolute_paths)
  205.     {
  206.       char tmpbuf[NAMSIZ + 2];
  207.  
  208.       tmpbuf[0] = '/';
  209.       strcpy (&tmpbuf[1], current_file_name);
  210.       diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY);
  211.     }
  212.       if (diff_fd < 0)
  213.     {
  214.       msg_perror ("cannot open %s", current_file_name);
  215.       if (head->header.isextended)
  216.         skip_extended_headers ();
  217.       skip_file ((long) hstat.st_size);
  218.       different++;
  219.       goto quit;
  220.     }
  221.       /*
  222.          * Need to treat sparse files completely differently here.
  223.          */
  224.       if (head->header.linkflag == LF_SPARSE)
  225.     diff_sparse_files (hstat.st_size);
  226.       else
  227.     wantbytes ((long) (hstat.st_size), compare_chunk);
  228.  
  229.       check = close (diff_fd);
  230.       if (check < 0)
  231.     msg_perror ("Error while closing %s", current_file_name);
  232.  
  233.     quit:
  234.       break;
  235.  
  236. #ifndef __MSDOS__
  237.     case LF_LINK:
  238.       if (do_stat (&filestat))
  239.     break;
  240.       dev = filestat.st_dev;
  241.       ino = filestat.st_ino;
  242.       err = stat (current_link_name, &filestat);
  243.       if (err < 0)
  244.     {
  245.       if (errno == ENOENT)
  246.         {
  247.           fprintf (msg_file, "%s: does not exist\n", current_file_name);
  248.         }
  249.       else
  250.         {
  251.           msg_perror ("cannot stat file %s", current_file_name);
  252.         }
  253.       different++;
  254.       break;
  255.     }
  256.       if (filestat.st_dev != dev || filestat.st_ino != ino)
  257.     {
  258.       fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name);
  259.       break;
  260.     }
  261.       break;
  262. #endif
  263.  
  264. #ifdef S_ISLNK
  265.     case LF_SYMLINK:
  266.       {
  267.     char linkbuf[NAMSIZ + 3];
  268.     check = readlink (current_file_name, linkbuf,
  269.               (sizeof linkbuf) - 1);
  270.  
  271.     if (check < 0)
  272.       {
  273.         if (errno == ENOENT)
  274.           {
  275.         fprintf (msg_file,
  276.              "%s: no such file or directory\n",
  277.              current_file_name);
  278.           }
  279.         else
  280.           {
  281.         msg_perror ("cannot read link %s", current_file_name);
  282.           }
  283.         different++;
  284.         break;
  285.       }
  286.  
  287.     linkbuf[check] = '\0';    /* Null-terminate it */
  288.     if (strncmp (current_link_name, linkbuf, check) != 0)
  289.       {
  290.         fprintf (msg_file, "%s: symlink differs\n",
  291.              current_link_name);
  292.         different++;
  293.       }
  294.       }
  295.       break;
  296. #endif
  297.  
  298. #ifdef S_IFCHR
  299.     case LF_CHR:
  300.       hstat.st_mode |= S_IFCHR;
  301.       goto check_node;
  302. #endif
  303.  
  304. #ifdef S_IFBLK
  305.       /* If local system doesn't support block devices, use default case */
  306.     case LF_BLK:
  307.       hstat.st_mode |= S_IFBLK;
  308.       goto check_node;
  309. #endif
  310.  
  311. #ifdef S_ISFIFO
  312.       /* If local system doesn't support FIFOs, use default case */
  313.     case LF_FIFO:
  314. #ifdef S_IFIFO
  315.       hstat.st_mode |= S_IFIFO;
  316. #endif
  317.       hstat.st_rdev = 0;    /* FIXME, do we need this? */
  318.       goto check_node;
  319. #endif
  320.  
  321.     check_node:
  322.       /* FIXME, deal with umask */
  323.       if (do_stat (&filestat))
  324.     break;
  325.       if (hstat.st_rdev != filestat.st_rdev)
  326.     {
  327.       fprintf (msg_file, "%s: device numbers changed\n", current_file_name);
  328.       different++;
  329.       break;
  330.     }
  331. #ifdef S_IFMT
  332.       if (hstat.st_mode != filestat.st_mode)
  333. #else /* POSIX lossage */
  334.       if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777))
  335. #endif
  336.     {
  337.       fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name);
  338.       different++;
  339.       break;
  340.     }
  341.       break;
  342.  
  343.     case LF_DUMPDIR:
  344.       data = diff_dir = get_dir_contents (current_file_name, 0);
  345.       if (data)
  346.     {
  347.       wantbytes ((long) (hstat.st_size), compare_dir);
  348.       free (data);
  349.     }
  350.       else
  351.     wantbytes ((long) (hstat.st_size), no_op);
  352.       /* FALL THROUGH */
  353.  
  354.     case LF_DIR:
  355.       /* Check for trailing / */
  356.       namelen = strlen (current_file_name) - 1;
  357.     really_dir:
  358.       while (namelen && current_file_name[namelen] == '/')
  359.     current_file_name[namelen--] = '\0';    /* Zap / */
  360.  
  361.       if (do_stat (&filestat))
  362.     break;
  363.       if (!S_ISDIR (filestat.st_mode))
  364.     {
  365.       fprintf (msg_file, "%s is no longer a directory\n", current_file_name);
  366.       different++;
  367.       break;
  368.     }
  369.       if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777))
  370.     sigh ("mode");
  371.       break;
  372.  
  373.     case LF_VOLHDR:
  374.       break;
  375.  
  376.     case LF_MULTIVOL:
  377.       namelen = strlen (current_file_name) - 1;
  378.       if (current_file_name[namelen] == '/')
  379.     goto really_dir;
  380.  
  381.       if (do_stat (&filestat))
  382.     break;
  383.  
  384.       if (!S_ISREG (filestat.st_mode))
  385.     {
  386.       fprintf (msg_file, "%s: not a regular file\n",
  387.            current_file_name);
  388.       skip_file ((long) hstat.st_size);
  389.       different++;
  390.       break;
  391.     }
  392.  
  393.       filestat.st_mode &= 07777;
  394.       offset = from_oct (1 + 12, head->header.offset);
  395.       if (filestat.st_size != hstat.st_size + offset)
  396.     {
  397.       sigh ("size");
  398.       skip_file ((long) hstat.st_size);
  399.       different++;
  400.       break;
  401.     }
  402.  
  403.       diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
  404.  
  405.       if (diff_fd < 0)
  406.     {
  407.       msg_perror ("cannot open file %s", current_file_name);
  408.       skip_file ((long) hstat.st_size);
  409.       different++;
  410.       break;
  411.     }
  412.       err = lseek (diff_fd, offset, 0);
  413.       if (err != offset)
  414.     {
  415.       msg_perror ("cannot seek to %ld in file %s", offset, current_file_name);
  416.       different++;
  417.       break;
  418.     }
  419.  
  420.       wantbytes ((long) (hstat.st_size), compare_chunk);
  421.  
  422.       check = close (diff_fd);
  423.       if (check < 0)
  424.     {
  425.       msg_perror ("Error while closing %s", current_file_name);
  426.     }
  427.       break;
  428.  
  429.     }
  430.  
  431.   /* We don't need to save it any longer. */
  432.   saverec ((union record **) 0);/* Unsave it */
  433. }
  434.  
  435. int
  436. compare_chunk (bytes, buffer)
  437.      long bytes;
  438.      char *buffer;
  439. {
  440.   int err;
  441.  
  442.   err = read (diff_fd, diff_buf, bytes);
  443.   if (err != bytes)
  444.     {
  445.       if (err < 0)
  446.     {
  447.       msg_perror ("can't read %s", current_file_name);
  448.     }
  449.       else
  450.     {
  451.       fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes);
  452.     }
  453.       different++;
  454.       return -1;
  455.     }
  456.   if (bcmp (buffer, diff_buf, bytes))
  457.     {
  458.       fprintf (msg_file, "%s: data differs\n", current_file_name);
  459.       different++;
  460.       return -1;
  461.     }
  462.   return 0;
  463. }
  464.  
  465. int
  466. compare_dir (bytes, buffer)
  467.      long bytes;
  468.      char *buffer;
  469. {
  470.   if (bcmp (buffer, diff_dir, bytes))
  471.     {
  472.       fprintf (msg_file, "%s: data differs\n", current_file_name);
  473.       different++;
  474.       return -1;
  475.     }
  476.   diff_dir += bytes;
  477.   return 0;
  478. }
  479.  
  480. /*
  481.  * Sigh about something that differs.
  482.  */
  483. void
  484. sigh (what)
  485.      char *what;
  486. {
  487.  
  488.   fprintf (msg_file, "%s: %s differs\n",
  489.        current_file_name, what);
  490. }
  491.  
  492. void
  493. verify_volume ()
  494. {
  495.   int status;
  496. #ifdef MTIOCTOP
  497.   struct mtop t;
  498.   int er;
  499. #endif
  500.  
  501.   if (!diff_buf)
  502.     diff_init ();
  503. #ifdef MTIOCTOP
  504.   t.mt_op = MTBSF;
  505.   t.mt_count = 1;
  506.   if ((er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
  507.     {
  508.       if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0)
  509.     {
  510. #endif
  511.       if (rmtlseek (archive, 0L, 0) != 0)
  512.         {
  513.           /* Lseek failed.  Try a different method */
  514.           msg_perror ("Couldn't rewind archive file for verify");
  515.           return;
  516.         }
  517. #ifdef MTIOCTOP
  518.     }
  519.     }
  520. #endif
  521.   ar_reading = 1;
  522.   now_verifying = 1;
  523.   fl_read ();
  524.   for (;;)
  525.     {
  526.       status = read_header ();
  527.       if (status == 0)
  528.     {
  529.       unsigned n;
  530.  
  531.       n = 0;
  532.       do
  533.         {
  534.           n++;
  535.           status = read_header ();
  536.         }
  537.       while (status == 0);
  538.       msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s");
  539.     }
  540.       if (status == 2 || status == EOF)
  541.     break;
  542.       diff_archive ();
  543.     }
  544.   ar_reading = 0;
  545.   now_verifying = 0;
  546.  
  547. }
  548.  
  549. int
  550. do_stat (statp)
  551.      struct stat *statp;
  552. {
  553.   int err;
  554.  
  555.   err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp);
  556.   if (err < 0)
  557.     {
  558.       if (errno == ENOENT)
  559.     {
  560.       fprintf (msg_file, "%s: does not exist\n", current_file_name);
  561.     }
  562.       else
  563.     msg_perror ("can't stat file %s", current_file_name);
  564.       /*        skip_file((long)hstat.st_size);
  565.         different++;*/
  566.       return 1;
  567.     }
  568.   else
  569.     return 0;
  570. }
  571.  
  572. /*
  573.  * JK
  574.  * Diff'ing a sparse file with its counterpart on the tar file is a
  575.  * bit of a different story than a normal file.  First, we must know
  576.  * what areas of the file to skip through, i.e., we need to contruct
  577.  * a sparsearray, which will hold all the information we need.  We must
  578.  * compare small amounts of data at a time as we find it.
  579.  */
  580.  
  581. void
  582. diff_sparse_files (filesize)
  583.      int filesize;
  584.  
  585. {
  586.   int sparse_ind = 0;
  587.   char *buf;
  588.   int buf_size = RECORDSIZE;
  589.   union record *datarec;
  590.   int err;
  591.   long numbytes;
  592.   /*    int        amt_read = 0;*/
  593.   int size = filesize;
  594.  
  595.   buf = (char *) ck_malloc (buf_size * sizeof (char));
  596.  
  597.   fill_in_sparse_array ();
  598.  
  599.  
  600.   while (size > 0)
  601.     {
  602.       datarec = findrec ();
  603.       if (!sparsearray[sparse_ind].numbytes)
  604.     break;
  605.  
  606.       /*
  607.          * 'numbytes' is nicer to write than
  608.          * 'sparsearray[sparse_ind].numbytes' all the time ...
  609.          */
  610.       numbytes = sparsearray[sparse_ind].numbytes;
  611.  
  612.       lseek (diff_fd, sparsearray[sparse_ind].offset, 0);
  613.       /*
  614.          * take care to not run out of room in our buffer
  615.          */
  616.       while (buf_size < numbytes)
  617.     {
  618.       buf = (char *) ck_realloc (buf, buf_size * 2 * sizeof (char));
  619.       buf_size *= 2;
  620.     }
  621.       while (numbytes > RECORDSIZE)
  622.     {
  623.       if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE)
  624.         {
  625.           if (err < 0)
  626.         msg_perror ("can't read %s", current_file_name);
  627.           else
  628.         fprintf (msg_file, "%s: could only read %d of %d bytes\n",
  629.              current_file_name, err, numbytes);
  630.           break;
  631.         }
  632.       if (bcmp (buf, datarec->charptr, RECORDSIZE))
  633.         {
  634.           different++;
  635.           break;
  636.         }
  637.       numbytes -= err;
  638.       size -= err;
  639.       userec (datarec);
  640.       datarec = findrec ();
  641.     }
  642.       if ((err = read (diff_fd, buf, numbytes)) != numbytes)
  643.     {
  644.       if (err < 0)
  645.         msg_perror ("can't read %s", current_file_name);
  646.       else
  647.         fprintf (msg_file, "%s: could only read %d of %d bytes\n",
  648.              current_file_name, err, numbytes);
  649.       break;
  650.     }
  651.  
  652.       if (bcmp (buf, datarec->charptr, numbytes))
  653.     {
  654.       different++;
  655.       break;
  656.     }
  657.       /*        amt_read += numbytes;
  658.         if (amt_read >= RECORDSIZE) {
  659.             amt_read = 0;
  660.             userec(datarec);
  661.             datarec = findrec();
  662.         }*/
  663.       userec (datarec);
  664.       sparse_ind++;
  665.       size -= numbytes;
  666.     }
  667.   /*
  668.      * if the number of bytes read isn't the
  669.      * number of bytes supposedly in the file,
  670.      * they're different
  671.      */
  672.   /*    if (amt_read != filesize)
  673.         different++;*/
  674.   userec (datarec);
  675.   free (sparsearray);
  676.   if (different)
  677.     fprintf (msg_file, "%s: data differs\n", current_file_name);
  678.  
  679. }
  680.  
  681. /*
  682.  * JK
  683.  * This routine should be used more often than it is ... look into
  684.  * that.  Anyhow, what it does is translate the sparse information
  685.  * on the header, and in any subsequent extended headers, into an
  686.  * array of structures with true numbers, as opposed to character
  687.  * strings.  It simply makes our life much easier, doing so many
  688.  * comparisong and such.
  689.  */
  690. void
  691. fill_in_sparse_array ()
  692. {
  693.   int ind;
  694.  
  695.   /*
  696.      * allocate space for our scratch space; it's initially
  697.      * 10 elements long, but can change in this routine if
  698.      * necessary
  699.      */
  700.   sp_array_size = 10;
  701.   sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
  702.  
  703.   /*
  704.      * there are at most five of these structures in the header
  705.      * itself; read these in first
  706.      */
  707.   for (ind = 0; ind < SPARSE_IN_HDR; ind++)
  708.     {
  709.       if (!head->header.sp[ind].numbytes)
  710.     break;
  711.       sparsearray[ind].offset =
  712.     from_oct (1 + 12, head->header.sp[ind].offset);
  713.       sparsearray[ind].numbytes =
  714.     from_oct (1 + 12, head->header.sp[ind].numbytes);
  715.     }
  716.   /*
  717.      * if the header's extended, we gotta read in exhdr's till
  718.      * we're done
  719.      */
  720.   if (head->header.isextended)
  721.     {
  722.       /* how far into the sparsearray we are 'so far' */
  723.       static int so_far_ind = SPARSE_IN_HDR;
  724.       union record *exhdr;
  725.  
  726.       for (;;)
  727.     {
  728.       exhdr = findrec ();
  729.       for (ind = 0; ind < SPARSE_EXT_HDR; ind++)
  730.         {
  731.           if (ind + so_far_ind > sp_array_size - 1)
  732.         {
  733.           /*
  734.                   * we just ran out of room in our
  735.                  *  scratch area - realloc it
  736.                   */
  737.           sparsearray = (struct sp_array *)
  738.             ck_realloc (sparsearray,
  739.                  sp_array_size * 2 * sizeof (struct sp_array));
  740.           sp_array_size *= 2;
  741.         }
  742.           /*
  743.              * convert the character strings into longs
  744.              */
  745.           sparsearray[ind + so_far_ind].offset =
  746.         from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset);
  747.           sparsearray[ind + so_far_ind].numbytes =
  748.         from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes);
  749.         }
  750.       /*
  751.          * if this is the last extended header for this
  752.          * file, we can stop
  753.          */
  754.       if (!exhdr->ext_hdr.isextended)
  755.         break;
  756.       else
  757.         {
  758.           so_far_ind += SPARSE_EXT_HDR;
  759.           userec (exhdr);
  760.         }
  761.     }
  762.       /* be sure to skip past the last one  */
  763.       userec (exhdr);
  764.     }
  765. }
  766.