Commit | Line | Data |
---|---|---|
8c4ebc23 JH |
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 | diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY); | |
197 | ||
198 | if (diff_fd < 0 && !f_absolute_paths) | |
199 | { | |
200 | char tmpbuf[NAMSIZ + 2]; | |
201 | ||
202 | tmpbuf[0] = '/'; | |
203 | strcpy (&tmpbuf[1], current_file_name); | |
204 | diff_fd = open (tmpbuf, O_NDELAY | O_RDONLY); | |
205 | } | |
206 | if (diff_fd < 0) | |
207 | { | |
208 | msg_perror ("cannot open %s", current_file_name); | |
209 | if (head->header.isextended) | |
210 | skip_extended_headers (); | |
211 | skip_file ((long) hstat.st_size); | |
212 | different++; | |
213 | goto quit; | |
214 | } | |
215 | /* | |
216 | * Need to treat sparse files completely differently here. | |
217 | */ | |
218 | if (head->header.linkflag == LF_SPARSE) | |
219 | diff_sparse_files (hstat.st_size); | |
220 | else | |
221 | wantbytes ((long) (hstat.st_size), compare_chunk); | |
222 | ||
223 | check = close (diff_fd); | |
224 | if (check < 0) | |
225 | msg_perror ("Error while closing %s", current_file_name); | |
226 | ||
227 | quit: | |
228 | break; | |
229 | ||
230 | #ifndef __MSDOS__ | |
231 | case LF_LINK: | |
232 | if (do_stat (&filestat)) | |
233 | break; | |
234 | dev = filestat.st_dev; | |
235 | ino = filestat.st_ino; | |
236 | err = stat (current_link_name, &filestat); | |
237 | if (err < 0) | |
238 | { | |
239 | if (errno == ENOENT) | |
240 | { | |
241 | fprintf (msg_file, "%s: does not exist\n", current_file_name); | |
242 | } | |
243 | else | |
244 | { | |
245 | msg_perror ("cannot stat file %s", current_file_name); | |
246 | } | |
247 | different++; | |
248 | break; | |
249 | } | |
250 | if (filestat.st_dev != dev || filestat.st_ino != ino) | |
251 | { | |
252 | fprintf (msg_file, "%s not linked to %s\n", current_file_name, current_link_name); | |
253 | break; | |
254 | } | |
255 | break; | |
256 | #endif | |
257 | ||
258 | #ifdef S_ISLNK | |
259 | case LF_SYMLINK: | |
260 | { | |
261 | char linkbuf[NAMSIZ + 3]; | |
262 | check = readlink (current_file_name, linkbuf, | |
263 | (sizeof linkbuf) - 1); | |
264 | ||
265 | if (check < 0) | |
266 | { | |
267 | if (errno == ENOENT) | |
268 | { | |
269 | fprintf (msg_file, | |
270 | "%s: no such file or directory\n", | |
271 | current_file_name); | |
272 | } | |
273 | else | |
274 | { | |
275 | msg_perror ("cannot read link %s", current_file_name); | |
276 | } | |
277 | different++; | |
278 | break; | |
279 | } | |
280 | ||
281 | linkbuf[check] = '\0'; /* Null-terminate it */ | |
282 | if (strncmp (current_link_name, linkbuf, check) != 0) | |
283 | { | |
284 | fprintf (msg_file, "%s: symlink differs\n", | |
285 | current_link_name); | |
286 | different++; | |
287 | } | |
288 | } | |
289 | break; | |
290 | #endif | |
291 | ||
292 | #ifdef S_IFCHR | |
293 | case LF_CHR: | |
294 | hstat.st_mode |= S_IFCHR; | |
295 | goto check_node; | |
296 | #endif | |
297 | ||
298 | #ifdef S_IFBLK | |
299 | /* If local system doesn't support block devices, use default case */ | |
300 | case LF_BLK: | |
301 | hstat.st_mode |= S_IFBLK; | |
302 | goto check_node; | |
303 | #endif | |
304 | ||
305 | #ifdef S_ISFIFO | |
306 | /* If local system doesn't support FIFOs, use default case */ | |
307 | case LF_FIFO: | |
308 | #ifdef S_IFIFO | |
309 | hstat.st_mode |= S_IFIFO; | |
310 | #endif | |
311 | hstat.st_rdev = 0; /* FIXME, do we need this? */ | |
312 | goto check_node; | |
313 | #endif | |
314 | ||
315 | check_node: | |
316 | /* FIXME, deal with umask */ | |
317 | if (do_stat (&filestat)) | |
318 | break; | |
319 | if (hstat.st_rdev != filestat.st_rdev) | |
320 | { | |
321 | fprintf (msg_file, "%s: device numbers changed\n", current_file_name); | |
322 | different++; | |
323 | break; | |
324 | } | |
325 | #ifdef S_IFMT | |
326 | if (hstat.st_mode != filestat.st_mode) | |
327 | #else /* POSIX lossage */ | |
328 | if ((hstat.st_mode & 07777) != (filestat.st_mode & 07777)) | |
329 | #endif | |
330 | { | |
331 | fprintf (msg_file, "%s: mode or device-type changed\n", current_file_name); | |
332 | different++; | |
333 | break; | |
334 | } | |
335 | break; | |
336 | ||
337 | case LF_DUMPDIR: | |
338 | data = diff_dir = get_dir_contents (current_file_name, 0); | |
339 | if (data) | |
340 | { | |
341 | wantbytes ((long) (hstat.st_size), compare_dir); | |
342 | free (data); | |
343 | } | |
344 | else | |
345 | wantbytes ((long) (hstat.st_size), no_op); | |
346 | /* FALL THROUGH */ | |
347 | ||
348 | case LF_DIR: | |
349 | /* Check for trailing / */ | |
350 | namelen = strlen (current_file_name) - 1; | |
351 | really_dir: | |
352 | while (namelen && current_file_name[namelen] == '/') | |
353 | current_file_name[namelen--] = '\0'; /* Zap / */ | |
354 | ||
355 | if (do_stat (&filestat)) | |
356 | break; | |
357 | if (!S_ISDIR (filestat.st_mode)) | |
358 | { | |
359 | fprintf (msg_file, "%s is no longer a directory\n", current_file_name); | |
360 | different++; | |
361 | break; | |
362 | } | |
363 | if ((filestat.st_mode & 07777) != (hstat.st_mode & 07777)) | |
364 | sigh ("mode"); | |
365 | break; | |
366 | ||
367 | case LF_VOLHDR: | |
368 | break; | |
369 | ||
370 | case LF_MULTIVOL: | |
371 | namelen = strlen (current_file_name) - 1; | |
372 | if (current_file_name[namelen] == '/') | |
373 | goto really_dir; | |
374 | ||
375 | if (do_stat (&filestat)) | |
376 | break; | |
377 | ||
378 | if (!S_ISREG (filestat.st_mode)) | |
379 | { | |
380 | fprintf (msg_file, "%s: not a regular file\n", | |
381 | current_file_name); | |
382 | skip_file ((long) hstat.st_size); | |
383 | different++; | |
384 | break; | |
385 | } | |
386 | ||
387 | filestat.st_mode &= 07777; | |
388 | offset = from_oct (1 + 12, head->header.offset); | |
389 | if (filestat.st_size != hstat.st_size + offset) | |
390 | { | |
391 | sigh ("size"); | |
392 | skip_file ((long) hstat.st_size); | |
393 | different++; | |
394 | break; | |
395 | } | |
396 | ||
397 | diff_fd = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY); | |
398 | ||
399 | if (diff_fd < 0) | |
400 | { | |
401 | msg_perror ("cannot open file %s", current_file_name); | |
402 | skip_file ((long) hstat.st_size); | |
403 | different++; | |
404 | break; | |
405 | } | |
406 | err = lseek (diff_fd, offset, 0); | |
407 | if (err != offset) | |
408 | { | |
409 | msg_perror ("cannot seek to %ld in file %s", offset, current_file_name); | |
410 | different++; | |
411 | break; | |
412 | } | |
413 | ||
414 | wantbytes ((long) (hstat.st_size), compare_chunk); | |
415 | ||
416 | check = close (diff_fd); | |
417 | if (check < 0) | |
418 | { | |
419 | msg_perror ("Error while closing %s", current_file_name); | |
420 | } | |
421 | break; | |
422 | ||
423 | } | |
424 | ||
425 | /* We don't need to save it any longer. */ | |
426 | saverec ((union record **) 0);/* Unsave it */ | |
427 | } | |
428 | ||
429 | int | |
430 | compare_chunk (bytes, buffer) | |
431 | long bytes; | |
432 | char *buffer; | |
433 | { | |
434 | int err; | |
435 | ||
436 | err = read (diff_fd, diff_buf, bytes); | |
437 | if (err != bytes) | |
438 | { | |
439 | if (err < 0) | |
440 | { | |
441 | msg_perror ("can't read %s", current_file_name); | |
442 | } | |
443 | else | |
444 | { | |
445 | fprintf (msg_file, "%s: could only read %d of %d bytes\n", current_file_name, err, bytes); | |
446 | } | |
447 | different++; | |
448 | return -1; | |
449 | } | |
450 | if (bcmp (buffer, diff_buf, bytes)) | |
451 | { | |
452 | fprintf (msg_file, "%s: data differs\n", current_file_name); | |
453 | different++; | |
454 | return -1; | |
455 | } | |
456 | return 0; | |
457 | } | |
458 | ||
459 | int | |
460 | compare_dir (bytes, buffer) | |
461 | long bytes; | |
462 | char *buffer; | |
463 | { | |
464 | if (bcmp (buffer, diff_dir, bytes)) | |
465 | { | |
466 | fprintf (msg_file, "%s: data differs\n", current_file_name); | |
467 | different++; | |
468 | return -1; | |
469 | } | |
470 | diff_dir += bytes; | |
471 | return 0; | |
472 | } | |
473 | ||
474 | /* | |
475 | * Sigh about something that differs. | |
476 | */ | |
477 | void | |
478 | sigh (what) | |
479 | char *what; | |
480 | { | |
481 | ||
482 | fprintf (msg_file, "%s: %s differs\n", | |
483 | current_file_name, what); | |
484 | } | |
485 | ||
486 | void | |
487 | verify_volume () | |
488 | { | |
489 | int status; | |
490 | #ifdef MTIOCTOP | |
491 | struct mtop t; | |
492 | int er; | |
493 | #endif | |
494 | ||
495 | if (!diff_buf) | |
496 | diff_init (); | |
497 | #ifdef MTIOCTOP | |
498 | t.mt_op = MTBSF; | |
499 | t.mt_count = 1; | |
500 | if ((er = rmtioctl (archive, MTIOCTOP, &t)) < 0) | |
501 | { | |
502 | if (errno != EIO || (er = rmtioctl (archive, MTIOCTOP, &t)) < 0) | |
503 | { | |
504 | #endif | |
505 | if (rmtlseek (archive, 0L, 0) != 0) | |
506 | { | |
507 | /* Lseek failed. Try a different method */ | |
508 | msg_perror ("Couldn't rewind archive file for verify"); | |
509 | return; | |
510 | } | |
511 | #ifdef MTIOCTOP | |
512 | } | |
513 | } | |
514 | #endif | |
515 | ar_reading = 1; | |
516 | now_verifying = 1; | |
517 | fl_read (); | |
518 | for (;;) | |
519 | { | |
520 | status = read_header (); | |
521 | if (status == 0) | |
522 | { | |
523 | unsigned n; | |
524 | ||
525 | n = 0; | |
526 | do | |
527 | { | |
528 | n++; | |
529 | status = read_header (); | |
530 | } | |
531 | while (status == 0); | |
532 | msg ("VERIFY FAILURE: %d invalid header%s detected!", n, n == 1 ? "" : "s"); | |
533 | } | |
534 | if (status == 2 || status == EOF) | |
535 | break; | |
536 | diff_archive (); | |
537 | } | |
538 | ar_reading = 0; | |
539 | now_verifying = 0; | |
540 | ||
541 | } | |
542 | ||
543 | int | |
544 | do_stat (statp) | |
545 | struct stat *statp; | |
546 | { | |
547 | int err; | |
548 | ||
549 | err = f_follow_links ? stat (current_file_name, statp) : lstat (current_file_name, statp); | |
550 | if (err < 0) | |
551 | { | |
552 | if (errno == ENOENT) | |
553 | { | |
554 | fprintf (msg_file, "%s: does not exist\n", current_file_name); | |
555 | } | |
556 | else | |
557 | msg_perror ("can't stat file %s", current_file_name); | |
558 | /* skip_file((long)hstat.st_size); | |
559 | different++;*/ | |
560 | return 1; | |
561 | } | |
562 | else | |
563 | return 0; | |
564 | } | |
565 | ||
566 | /* | |
567 | * JK | |
568 | * Diff'ing a sparse file with its counterpart on the tar file is a | |
569 | * bit of a different story than a normal file. First, we must know | |
570 | * what areas of the file to skip through, i.e., we need to contruct | |
571 | * a sparsearray, which will hold all the information we need. We must | |
572 | * compare small amounts of data at a time as we find it. | |
573 | */ | |
574 | ||
575 | void | |
576 | diff_sparse_files (filesize) | |
577 | int filesize; | |
578 | ||
579 | { | |
580 | int sparse_ind = 0; | |
581 | char *buf; | |
582 | int buf_size = RECORDSIZE; | |
583 | union record *datarec; | |
584 | int err; | |
585 | long numbytes; | |
586 | /* int amt_read = 0;*/ | |
587 | int size = filesize; | |
588 | ||
589 | buf = (char *) ck_malloc (buf_size * sizeof (char)); | |
590 | ||
591 | fill_in_sparse_array (); | |
592 | ||
593 | ||
594 | while (size > 0) | |
595 | { | |
596 | datarec = findrec (); | |
597 | if (!sparsearray[sparse_ind].numbytes) | |
598 | break; | |
599 | ||
600 | /* | |
601 | * 'numbytes' is nicer to write than | |
602 | * 'sparsearray[sparse_ind].numbytes' all the time ... | |
603 | */ | |
604 | numbytes = sparsearray[sparse_ind].numbytes; | |
605 | ||
606 | lseek (diff_fd, sparsearray[sparse_ind].offset, 0); | |
607 | /* | |
608 | * take care to not run out of room in our buffer | |
609 | */ | |
610 | while (buf_size < numbytes) | |
611 | { | |
612 | buf = (char *) ck_realloc (buf, buf_size * 2 * sizeof (char)); | |
613 | buf_size *= 2; | |
614 | } | |
615 | while (numbytes > RECORDSIZE) | |
616 | { | |
617 | if ((err = read (diff_fd, buf, RECORDSIZE)) != RECORDSIZE) | |
618 | { | |
619 | if (err < 0) | |
620 | msg_perror ("can't read %s", current_file_name); | |
621 | else | |
622 | fprintf (msg_file, "%s: could only read %d of %d bytes\n", | |
623 | current_file_name, err, numbytes); | |
624 | break; | |
625 | } | |
626 | if (bcmp (buf, datarec->charptr, RECORDSIZE)) | |
627 | { | |
628 | different++; | |
629 | break; | |
630 | } | |
631 | numbytes -= err; | |
632 | size -= err; | |
633 | userec (datarec); | |
634 | datarec = findrec (); | |
635 | } | |
636 | if ((err = read (diff_fd, buf, numbytes)) != numbytes) | |
637 | { | |
638 | if (err < 0) | |
639 | msg_perror ("can't read %s", current_file_name); | |
640 | else | |
641 | fprintf (msg_file, "%s: could only read %d of %d bytes\n", | |
642 | current_file_name, err, numbytes); | |
643 | break; | |
644 | } | |
645 | ||
646 | if (bcmp (buf, datarec->charptr, numbytes)) | |
647 | { | |
648 | different++; | |
649 | break; | |
650 | } | |
651 | /* amt_read += numbytes; | |
652 | if (amt_read >= RECORDSIZE) { | |
653 | amt_read = 0; | |
654 | userec(datarec); | |
655 | datarec = findrec(); | |
656 | }*/ | |
657 | userec (datarec); | |
658 | sparse_ind++; | |
659 | size -= numbytes; | |
660 | } | |
661 | /* | |
662 | * if the number of bytes read isn't the | |
663 | * number of bytes supposedly in the file, | |
664 | * they're different | |
665 | */ | |
666 | /* if (amt_read != filesize) | |
667 | different++;*/ | |
668 | userec (datarec); | |
669 | free (sparsearray); | |
670 | if (different) | |
671 | fprintf (msg_file, "%s: data differs\n", current_file_name); | |
672 | ||
673 | } | |
674 | ||
675 | /* | |
676 | * JK | |
677 | * This routine should be used more often than it is ... look into | |
678 | * that. Anyhow, what it does is translate the sparse information | |
679 | * on the header, and in any subsequent extended headers, into an | |
680 | * array of structures with true numbers, as opposed to character | |
681 | * strings. It simply makes our life much easier, doing so many | |
682 | * comparisong and such. | |
683 | */ | |
684 | void | |
685 | fill_in_sparse_array () | |
686 | { | |
687 | int ind; | |
688 | ||
689 | /* | |
690 | * allocate space for our scratch space; it's initially | |
691 | * 10 elements long, but can change in this routine if | |
692 | * necessary | |
693 | */ | |
694 | sp_array_size = 10; | |
695 | sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array)); | |
696 | ||
697 | /* | |
698 | * there are at most five of these structures in the header | |
699 | * itself; read these in first | |
700 | */ | |
701 | for (ind = 0; ind < SPARSE_IN_HDR; ind++) | |
702 | { | |
703 | if (!head->header.sp[ind].numbytes) | |
704 | break; | |
705 | sparsearray[ind].offset = | |
706 | from_oct (1 + 12, head->header.sp[ind].offset); | |
707 | sparsearray[ind].numbytes = | |
708 | from_oct (1 + 12, head->header.sp[ind].numbytes); | |
709 | } | |
710 | /* | |
711 | * if the header's extended, we gotta read in exhdr's till | |
712 | * we're done | |
713 | */ | |
714 | if (head->header.isextended) | |
715 | { | |
716 | /* how far into the sparsearray we are 'so far' */ | |
717 | static int so_far_ind = SPARSE_IN_HDR; | |
718 | union record *exhdr; | |
719 | ||
720 | for (;;) | |
721 | { | |
722 | exhdr = findrec (); | |
723 | for (ind = 0; ind < SPARSE_EXT_HDR; ind++) | |
724 | { | |
725 | if (ind + so_far_ind > sp_array_size - 1) | |
726 | { | |
727 | /* | |
728 | * we just ran out of room in our | |
729 | * scratch area - realloc it | |
730 | */ | |
731 | sparsearray = (struct sp_array *) | |
732 | ck_realloc (sparsearray, | |
733 | sp_array_size * 2 * sizeof (struct sp_array)); | |
734 | sp_array_size *= 2; | |
735 | } | |
736 | /* | |
737 | * convert the character strings into longs | |
738 | */ | |
739 | sparsearray[ind + so_far_ind].offset = | |
740 | from_oct (1 + 12, exhdr->ext_hdr.sp[ind].offset); | |
741 | sparsearray[ind + so_far_ind].numbytes = | |
742 | from_oct (1 + 12, exhdr->ext_hdr.sp[ind].numbytes); | |
743 | } | |
744 | /* | |
745 | * if this is the last extended header for this | |
746 | * file, we can stop | |
747 | */ | |
748 | if (!exhdr->ext_hdr.isextended) | |
749 | break; | |
750 | else | |
751 | { | |
752 | so_far_ind += SPARSE_EXT_HDR; | |
753 | userec (exhdr); | |
754 | } | |
755 | } | |
756 | /* be sure to skip past the last one */ | |
757 | userec (exhdr); | |
758 | } | |
759 | } |