- }
-
- if (fd < 0) {
- if (make_dirs(skipcrud + current_file_name))
- goto again_file;
- msg_perror("Could not create file %s",
- skipcrud + current_file_name);
- if (head->header.isextended)
- skip_extended_headers();
- skip_file((long)hstat.st_size);
- goto quit;
- }
-
- extract_file:
- if (head->header.linkflag == LF_SPARSE) {
- char *name;
- int namelen;
-
- /*
- * Kludge alert. NAME is assigned to header.name
- * because during the extraction, the space that
- * contains the header will get scribbled on, and
- * the name will get munged, so any error messages
- * that happen to contain the filename will look
- * REAL interesting unless we do this.
- */
- namelen = strlen(skipcrud + current_file_name);
- name = (char *) malloc((sizeof(char)) * namelen);
- bcopy(skipcrud+current_file_name, name, namelen);
- size = hstat.st_size;
- extract_sparse_file(fd, &size, hstat.st_size, name);
- }
- else
- for (size = hstat.st_size;
- size > 0;
- size -= written) {
-
- /* long offset,
- numbytes;*/
-
- if(f_multivol) {
- save_name=current_file_name;
- save_totsize=hstat.st_size;
- save_sizeleft=size;
- }
-
- /*
- * Locate data, determine max length
- * writeable, write it, record that
- * we have used the data, then check
- * if the write worked.
- */
- data = findrec()->charptr;
- if (data == NULL) { /* Check it... */
- msg("Unexpected EOF on archive file");
- break;
- }
- /*
- * JK - If the file is sparse, use the sparsearray
- * that we created before to lseek into the new
- * file the proper amount, and to see how many
- * bytes we want to write at that position.
- */
- /* if (head->header.linkflag == LF_SPARSE) {
- off_t pos;
-
- pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0);
- printf("%d at %d\n", (int) pos, sparse_ind);
- written = sparsearray[sparse_ind++].numbytes;
- } else*/
- written = endofrecs()->charptr - data;
- if (written > size)
- written = size;
- errno = 0;
- check = write(fd, data, written);
- /*
- * The following is in violation of strict
- * typing, since the arg to userec
- * should be a struct rec *. FIXME.
- */
- userec((union record *)(data + written - 1));
- if (check == written) continue;
- /*
- * Error in writing to file.
- * Print it, skip to next file in archive.
- */
- if(check<0)
- msg_perror("couldn't write to file %s",
- skipcrud + current_file_name);
- else
- msg("could only write %d of %d bytes to file %s",
- written,check,skipcrud + current_file_name);
- skip_file((long)(size - written));
- break; /* Still do the close, mod time, chmod, etc */
- }
-
- if(f_multivol)
- save_name = 0;
-
- /* If writing to stdout, don't try to do anything
- to the filename; it doesn't exist, or we don't
- want to touch it anyway */
- if(f_exstdout)
- break;
-
- /* if (head->header.isextended) {
- register union record *exhdr;
- register int i;
-
- for (i = 0; i < 21; i++) {
- long offset;
-
- if (!exhdr->ext_hdr.sp[i].numbytes)
- break;
- offset = from_oct(1+12,
- exhdr->ext_hdr.sp[i].offset);
- written = from_oct(1+12,
- exhdr->ext_hdr.sp[i].numbytes);
- lseek(fd, offset, 0);
- check = write(fd, data, written);
- if (check == written) continue;
-
- }
-
-
- }*/
- check = close(fd);
- if (check < 0) {
- msg_perror("Error while closing %s",
- skipcrud + current_file_name);
- }
-
-
- set_filestat:
-
- /*
- * If we are root, set the owner and group of the extracted
- * file. This does what is wanted both on real Unix and on
- * System V. If we are running as a user, we extract as that
- * user; if running as root, we extract as the original owner.
- */
- if (we_are_root || f_do_chown) {
- if (chown(skipcrud + current_file_name,
- hstat.st_uid, hstat.st_gid) < 0) {
- msg_perror("cannot chown file %s to uid %d gid %d",
- skipcrud + current_file_name,
- hstat.st_uid,hstat.st_gid);
- }
- }
-
- /*
- * Set the modified time of the file.
- *
- * Note that we set the accessed time to "now", which
- * is really "the time we started extracting files".
- * unless f_gnudump is used, in which case .st_atime is used
- */
- if (!f_modified) {
- /* fixme if f_gnudump should set ctime too, but how? */
- if(f_gnudump)
- acc_upd_times[0]=hstat.st_atime;
- else acc_upd_times[0] = now; /* Accessed now */
- acc_upd_times[1] = hstat.st_mtime; /* Mod'd */
- if (utime(skipcrud + current_file_name,
- acc_upd_times) < 0) {
- msg_perror("couldn't change access and modification times of %s",skipcrud + current_file_name);
- }
+ union block *exhdr;
+ struct delayed_set_stat *data;
+
+#define CURRENT_FILE_NAME (skipcrud + current_file_name)
+
+ set_next_block_after (current_header);
+ decode_header (current_header, ¤t_stat, ¤t_format, 1);
+
+ if (interactive_option && !confirm ("extract", current_file_name))
+ {
+ if (current_header->oldgnu_header.isextended)
+ skip_extended_headers ();
+ skip_file (current_stat.st_size);
+ return;
+ }
+
+ /* Print the block from `current_header' and `current_stat'. */
+
+ if (verbose_option)
+ print_header ();
+
+ /* Check for fully specified file names and other atrocities. */
+
+ skipcrud = 0;
+ while (!absolute_names_option && CURRENT_FILE_NAME[0] == '/')
+ {
+ static int warned_once = 0;
+
+ skipcrud++; /* force relative path */
+ if (!warned_once)
+ {
+ warned_once = 1;
+ WARN ((0, 0, _("\
+Removing leading `/' from absolute path names in the archive")));
+ }
+ }
+
+ /* Take a safety backup of a previously existing file. */
+
+ if (backup_option && !to_stdout_option)
+ if (!maybe_backup_file (CURRENT_FILE_NAME, 0))
+ {
+ ERROR ((0, errno, _("%s: Was unable to backup this file"),
+ CURRENT_FILE_NAME));
+ if (current_header->oldgnu_header.isextended)
+ skip_extended_headers ();
+ skip_file (current_stat.st_size);
+ return;
+ }
+
+ /* Extract the archive entry according to its type. */
+
+ typeflag = current_header->header.typeflag;
+ switch (typeflag)
+ {
+ /* JK - What we want to do if the file is sparse is loop through
+ the array of sparse structures in the header and read in and
+ translate the character strings representing 1) the offset at
+ which to write and 2) how many bytes to write into numbers,
+ which we store into the scratch array, "sparsearray". This
+ array makes our life easier the same way it did in creating the
+ tar file that had to deal with a sparse file.
+
+ After we read in the first five (at most) sparse structures, we
+ check to see if the file has an extended header, i.e., if more
+ sparse structures are needed to describe the contents of the new
+ file. If so, we read in the extended headers and continue to
+ store their contents into the sparsearray. */
+
+ case GNUTYPE_SPARSE:
+ sp_array_size = 10;
+ sparsearray = (struct sp_array *)
+ xmalloc (sp_array_size * sizeof (struct sp_array));
+
+ for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
+ {
+ sparsearray[counter].offset =
+ OFF_FROM_OCT (current_header->oldgnu_header.sp[counter].offset);
+ sparsearray[counter].numbytes =
+ SIZE_FROM_OCT (current_header->oldgnu_header.sp[counter].numbytes);
+ if (!sparsearray[counter].numbytes)
+ break;
+ }
+
+ if (current_header->oldgnu_header.isextended)
+ {
+ /* Read in the list of extended headers and translate them
+ into the sparsearray as before. Note that this
+ invalidates current_header. */
+
+ /* static */ int ind = SPARSES_IN_OLDGNU_HEADER;
+
+ while (1)
+ {
+ exhdr = find_next_block ();
+ for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
+ {
+ if (counter + ind > sp_array_size - 1)
+ {
+ /* Realloc the scratch area since we've run out of
+ room. */
+
+ sp_array_size *= 2;
+ sparsearray = (struct sp_array *)
+ xrealloc (sparsearray,
+ sp_array_size * (sizeof (struct sp_array)));
+ }
+ /* Compare to 0, or use !(int)..., for Pyramid's dumb
+ compiler. */
+ if (exhdr->sparse_header.sp[counter].numbytes == 0)
+ break;
+ sparsearray[counter + ind].offset =
+ OFF_FROM_OCT (exhdr->sparse_header.sp[counter].offset);
+ sparsearray[counter + ind].numbytes =
+ SIZE_FROM_OCT (exhdr->sparse_header.sp[counter].numbytes);