Merge pull request #12 from davel/davel/sqsh
[debian-nspark.git] / unarc.c
blobb25f74a434eb75d3c750846089d855d26aaf7b2f
2 /*
3 * unarchive files
5 * Revision 1.21 99/03/17 MU
6 * Added support for extraction with .inf files
7 * Also capitilised the hexadecimal output strings.
9 * $Header: unarc.c 1.19 95/08/01 $
10 * $Log: unarc.c,v $
11 * Revision 1.20 95/08/01 xx:xx:xx BB
12 * Fixed for Borland C/C++
14 * Revision 1.19 94/12/12 17:29:30 arb
15 * Added support for ArcFS writesize
17 * Revision 1.18 93/08/20 12:30:02 arb
18 * Added support for ArcFS archive detection
20 * Revision 1.17 93/08/20 11:54:55 arb
21 * Prevent printing of spaces and LF if in quiet mode
23 * Revision 1.16 93/03/05 14:45:43 arb
24 * Added <string.h> for RISCOS, needed for strcpy
26 * Revision 1.15 92/12/23 13:26:05 duplain
27 * Added total-printing if in verbose mode.
29 * Revision 1.14 92/12/08 10:20:33 duplain
30 * Added call to append_type() if apptype non zero.
32 * Revision 1.13 92/12/07 17:19:21 duplain
33 * reformatted source.
35 * Revision 1.12 92/11/04 16:56:35 duplain
36 * Added printing of CRC value if debugging.
38 * Revision 1.11 92/10/29 13:40:05 duplain
39 * Fixed prompt_user function definition.
41 * Revision 1.10 92/10/23 14:31:28 duplain
42 * Changed prompt_user() output text.
44 * Revision 1.9 92/10/06 12:17:49 duplain
45 * Changed header used with verbose option.
47 * Revision 1.8 92/10/05 11:00:37 duplain
48 * Recoded user-prompt when files already exists during unarchiving.
50 * Revision 1.7 92/10/02 17:41:14 duplain
51 * Fix "end-of-subarchive" problem.
53 * Revision 1.6 92/10/02 10:03:20 duplain
54 * Changed "OS_FILE" to "OS_File" for log file entry.
56 * Revision 1.5 92/10/01 13:39:30 duplain
57 * Removed "goto do_retry" when header->complen > arcsize.
59 * Revision 1.4 92/10/01 12:17:16 duplain
60 * Fixed calculation of archive file size when unpacking/testing.
62 * Revision 1.3 92/10/01 11:22:59 duplain
63 * Added retry processing.
65 * Revision 1.2 92/09/30 10:27:51 duplain
66 * Added logfile processing.
68 * Revision 1.1 92/09/29 18:02:27 duplain
69 * Initial revision
73 #include <stdio.h>
74 #include <ctype.h>
75 #include <string.h>
76 #include "spark.h"
77 #include "store.h"
78 #include "pack.h"
79 #include "compress.h"
80 #include "main.h"
82 /* BB changed next line because of conflict with Borland's io.h */
84 /* #include "io.h" */
85 #include "nsparkio.h"
87 /* BB added next line */
88 #if defined(__MSDOS__) || defined(_WIN32)
89 #include <io.h> /* for read() */
90 #else
91 #ifndef RISCOS
92 #include <unistd.h>
93 #endif
94 #endif /* __MSDOS__ */
95 #include <stdlib.h>
96 #include "misc.h"
97 #include "os.h"
98 #include "error.h"
99 #include "crc.h"
100 #include "arcfs.h"
102 char prompt_user(char *filename);
103 char *get_newname(void);
107 do_unsquash()
109 SqshHeader sqsh_header;
110 Header header;
111 FILE *ifp, *ofp;
112 Status r;
113 long offset;
114 long datastart;
115 char *ofname = NULL;
116 char *p;
118 ifp = fopen(archive, "rb");
119 if (!ifp)
121 error("Can not open %s for reading\n", archive);
122 return 1;
125 if (!to_stdout)
127 ofname = calloc(1, strlen(archive) + sizeof(",xxx"));
128 /* Output filename is the same as the input filename with
129 * the correct filetype appended */
130 strcpy(ofname, archive);
131 /* Strip RISCOS filetype from output filename */
132 p = ofname + (strlen(ofname) - 4);
133 if (*p == ',' || *p == '.')
135 *p = '\0';
139 r = read_sqsh_header(ifp, &sqsh_header);
140 if (r != NOERR)
142 error("Invalid squash file");
143 return 1;
146 memset(&header, 0, sizeof(header));
147 datastart = ftell(ifp);
148 fseek(ifp, 0, SEEK_END);
149 offset = ftell(ifp);
151 /* The uncompress code uses Spark entry header structures.
152 * Use the squash header and other info to set up the Spark header
153 * with needed info. */
154 header.complen = (Word)(offset - datastart);
155 sqsh_header_to_header(&sqsh_header, &header);
156 fseek(ifp, datastart, SEEK_SET);
158 crcsize = 0;
159 writesize = header.origlen;
161 if (!to_stdout)
163 /* Append the correct filetype to the output filename */
164 append_type(&header, ofname);
167 if (!force && !to_stdout)
169 ofp = fopen(ofname, "rb");
170 if (ofp)
172 char c;
173 char *newname;
175 fclose(ofp);
176 c = prompt_user(ofname);
177 switch (c)
179 case 'n':
180 exit(1);
181 case 'r':
182 newname = get_newname();
183 free(ofname);
184 ofname = strdup(newname);
185 break;
186 case 'a':
187 case 'y':
188 /* overwrite file */
189 break;
194 if (to_stdout)
196 ofp = stdout;
198 else
200 ofp = fopen(ofname, "wb");
201 if (!ofp)
203 error("Can not open %s for writing\n", ofname);
204 return 1;
208 r = uncompress(&header, ifp, ofp, UNIX_COMPRESS);
209 fclose(ifp);
210 if (!to_stdout)
212 fclose(ofp);
214 if (r == NOERR)
216 if (stamp && !to_stdout)
218 filestamp(&header, ofname);
221 else
223 error("Failed to decompress file");
224 return 1;
227 return 0;
231 do_unarc()
233 FILE *ifp, *ofp = NULL, *lfp = NULL;
234 Header *header = NULL;
235 char *pathname = NULL;
236 char fullname[PATHNAMELEN];
237 int level = 0, ret = 0, retrying = 0;
238 Word arcsize;
239 Word nbytes = 0;
240 int nfiles = 0;
241 Status status;
243 ifp = fopen(archive, R_OPENMODE);
244 if (!ifp)
246 ifp = fopen(name_dot_arc(archive), R_OPENMODE);
247 if (!ifp)
249 if (!quiet)
250 error("cannot open archive \"%s\"", archive);
251 return (1);
253 archive = name_dot_arc(archive);
256 arcsize = filesize(archive);
257 if (!arcsize && !quiet)
259 error("cannot get size of archive file");
260 fclose(ifp);
261 return (1);
264 /* MU changed to accomodate the -I option */
265 if (!inffiles)
267 if (!testing && !listing && logfile)
269 lfp = fopen(logfile, W_OPENMODE);
270 if (!lfp)
271 warning("unable to open logfile \"%s\"", logfile);
275 if (!quiet && verbose)
277 msg("filename size load/date exec/time type storage");
278 msg("-------- ---- ----------- --------- ---- -------");
282 * read compressed files from archive until end of file...
284 while (1)
286 static Header dirheader;
287 Byte comptype, first;
289 header = NULL;
290 if ((arcfs == 0) && ((first = read_byte(ifp)) != STARTBYTE))
292 if ((first == 'A') &&
293 (read_byte(ifp) == 'r') &&
294 (read_byte(ifp) == 'c') &&
295 (read_byte(ifp) == 'h') &&
296 (read_byte(ifp) == 'i') &&
297 (read_byte(ifp) == 'v') &&
298 (read_byte(ifp) == 'e') && (read_byte(ifp) == '\0'))
300 debug("ArcFS format archive\n");
301 arcfs = 1;
303 else
304 break;
306 retry_loop:
307 header = read_header(ifp);
308 comptype = header->comptype & 0x7f;
309 #ifdef DEBUGGING
310 debug("archive file length = %ld level = %d\n", arcsize,
311 level);
312 print_header(header);
313 #endif /* DEBUGGING */
316 * If this is a compress _file_ then check archive size against
317 * compress-file length...
319 if (comptype && !(comptype == CT_NOTCOMP2 &&
320 /* BB changed constants in next line to long */
321 (header->load & 0xffffff00l) == 0xfffddc00l))
323 if (header->complen > arcsize)
325 debug("compressed len > archive file len");
326 header = NULL;
327 break;
329 else
330 arcsize -= header->complen;
333 if (!comptype)
334 { /* end of archive ? */
335 if (!level)
336 break;
337 level--;
339 * stamp directory now that all files have
340 * been written into it
342 if (!testing && !listing && !to_stdout && stamp && inlist(pathname))
343 if (filestamp(&dirheader, pathname) < 0 && !quiet)
344 error("error stamping %s", pathname);
345 pathname = uplevel();
346 continue;
350 * test for directory or file (file type = &DDC = archive)
352 if (comptype == CT_NOTCOMP2 &&
353 /* BB changed constants in next line to long */
354 (header->load & 0xffffff00l) == 0xfffddc00l)
356 level++;
357 pathname = downlevel(header->name);
358 dirheader = *header; /* make copy of header */
361 * create directory
363 if (!testing && !listing && !to_stdout && inlist(pathname))
364 switch (exist(pathname))
366 case NOEXIST:
367 if (makedir(pathname) < 0 && !quiet)
369 msg("error making %s... aborting", pathname);
370 ret = 4;
371 break;
373 break;
375 case ISFILE:
376 if (!quiet)
377 msg("%s exists as a file... aborting",
378 pathname);
379 ret = 4;
380 goto unarc_exit;
381 case ISDIR:
382 default:
383 break;
386 else
387 { /* file */
388 if (pathname)
389 sprintf(fullname, "%s%c%s", pathname, PATHSEP,
390 header->name);
391 else
392 strcpy(fullname, header->name);
394 if (!inlist(fullname))
396 fseek(ifp, (long) header->complen, 1);
397 continue;
401 * print the archive file details...
403 if (!quiet)
405 msg("%-30s", fullname);
406 if (verbose)
407 print_details(header);
410 /* add to totals */
411 nbytes += header->origlen;
412 nfiles++;
414 if (listing)
416 /* if listing, nothing more to do */
417 if (!quiet)
418 putc('\n', stderr);
419 fseek(ifp, (long) header->complen, 1);
420 continue;
422 else if (!quiet)
423 putc(' ', stderr);
426 * append the filetype to the name
428 if (apptype && !to_stdout)
429 append_type(header, fullname);
432 * if actually unarchiving then check if the file already exists
434 if (!testing && !to_stdout)
436 test_exist:
437 switch (exist(fullname))
439 case ISFILE:
440 if (!force)
442 char c = prompt_user(fullname);
443 if (c == 'a')
444 force = 1;
445 else if (c == 'n')
447 fseek(ifp, (long) header->complen, 1);
448 continue;
450 else if (c == 'r')
452 char *newname = get_newname();
453 if (pathname)
454 sprintf(fullname, "%s%c%s", pathname,
455 PATHSEP, newname);
456 else
457 strcpy(fullname, newname);
458 goto test_exist;
460 /* if (c == 'y') FALLTHROUGH */
462 break;
463 case ISDIR:
464 if (!quiet)
465 msg("exists as a directory... skipping");
466 continue;
467 case NOEXIST:
468 default:
469 break;
471 ofp = fopen(fullname, W_OPENMODE);
472 if (!ofp)
474 if (!quiet)
475 msg("unable to create");
476 continue;
479 else if (!testing)
481 ofp = stdout;
485 * do the unpacking
487 crcsize = writesize = header->origlen;
488 switch (comptype)
490 case CT_NOTCOMP:
491 case CT_NOTCOMP2:
492 status = unstore(header, ifp, ofp);
493 break;
494 case CT_CRUNCH:
495 status = uncompress(header, ifp, ofp, CRUNCH);
496 break;
497 case CT_PACK:
498 status = unpack(header, ifp, ofp);
499 break;
500 case CT_SQUASH:
501 status = uncompress(header, ifp, ofp, SQUASH);
502 break;
503 case CT_COMP:
504 status = uncompress(header, ifp, ofp, COMPRESS);
505 break;
506 default:
507 error("unsupported archive type %d", comptype);
508 if (retrying)
509 goto do_retry;
510 fseek(ifp, (long) header->complen, 1);
511 continue;
514 if (!testing && !to_stdout && ofp)
516 fclose(ofp);
517 ofp = NULL;
521 * check if unarchiving failed.
522 * (RERR check is not in switch() because `break'
523 * needs to escape from while())
525 if (status == RERR)
527 if (!quiet)
528 error("error reading archive");
529 ret = 3;
530 break;
532 switch (status)
534 case WERR:
535 if (!quiet)
536 msg("error writing file");
537 break;
538 case CRCERR:
539 if (!quiet)
540 msg("failed CRC check");
542 /* BB changed format in next line to long hex */
543 /* debug(" calculated CRC=0x%x", crc); */
544 #ifdef __MSDOS__
545 debug(" calculated CRC=0X%lX", crc);
546 #else
547 debug(" calculated CRC=0X%X", crc);
548 #endif /* __MSDOS__ */
549 break;
550 case NOERR:
551 if (!testing && !to_stdout && stamp)
553 if (filestamp(header, fullname) < 0 && !quiet)
554 msg("\nerror stamping %s", fullname);
557 * XXX: if the filename has had it's filetype appended to
558 * it, it may be longer than 10 characters long making this
559 * file useless. We could truncate the filename but there is
560 * no need to under UNIX... bit of a mismatch!
562 if (!testing)
564 if (inffiles)
566 strcpy(logfile, fullname);
567 strcat(logfile, ".inf");
568 lfp = fopen(logfile, W_OPENMODE);
570 if (lfp)
574 // BASIC
575 if ((header->load => 0xFFFFFB41) && (header->load <= 0xFFFFFB46))
577 header->load = 0xFFFF1900;
578 header->exec = 0xFFFF802B;
581 // *EXEC ie !BOOT
582 if (header->load == 0xFFFFFE41)
584 header->load = 0x00000000;
585 header->exec = 0xFFFFFFFF;
588 fprintf(lfp, "%s %08lX %08lX\n",
589 riscos_path(fullname), (long)header->load,
590 (long)header->exec);
591 fclose(lfp);
596 else
598 if (lfp)
600 fprintf(lfp,
601 "SYS \"OS_File\", 1, \"%s\", &%08lX, &%08lX,, &%X\n",
602 riscos_path(fullname), (long)header->load,
603 (long)header->exec, header->attr);
608 break;
609 default:
610 break;
613 if (!quiet)
614 putc('\n', stderr);
615 if (ret)
616 break;
621 * find out why header wasn't found
623 if (!header)
624 switch (check_stream(ifp))
626 case FNOERR:
627 if (!quiet)
628 msg("bad archive header");
629 if (retry && !listing)
631 msg("... retrying");
632 do_retry:
633 retrying++;
634 while (check_stream(ifp) == FNOERR)
635 if (read_byte(ifp) == STARTBYTE)
637 Byte byte = read_byte(ifp);
638 switch (byte & 0x7f)
640 case (CT_NOTCOMP):
641 case (CT_NOTCOMP2):
642 case (CT_CRUNCH):
643 case (CT_PACK):
644 case (CT_SQUASH):
645 case (CT_COMP):
646 ungetc((int) byte, ifp);
647 goto retry_loop;
648 /* NOTREACHED */
649 default:
650 break;
654 else
656 retrying = 0;
657 if (!quiet)
658 putc('\n', stderr);
660 ret = 2;
661 break;
662 case FRWERR:
663 if (!quiet)
664 msg("error reading archive");
665 ret = 3;
666 break;
667 case FEND:
668 default:
669 break;
672 unarc_exit:
674 if (verbose)
675 msg("total of %ld bytes in %d files\n", (long)nbytes, nfiles);
677 if (ofp && !to_stdout)
678 fclose(ofp);
679 if (lfp)
680 fclose(lfp);
681 if (ifp)
682 fclose(ifp);
683 return (ret);
687 * the file being extracted already exists, so ask user what to do...
689 char
690 prompt_user(char *filename)
692 int c;
693 char buffer[80];
695 while (1)
697 fprintf(stderr, "\n\"%s\" exists, overwrite ? (Yes/No/All/Rename): ",
698 filename);
699 fflush(stderr);
700 if (read(0, buffer, sizeof(buffer) - 1)<1) {
701 c = 'n';
702 break;
704 if (isupper(*buffer))
705 c = tolower(*buffer);
706 else
707 c = *buffer;
708 if (c == 'y' || c == 'n' || c == 'a' || c == 'r')
709 break;
711 return (c);
715 * user wants to rename file, so get the leaf name of the new file...
717 char *
718 get_newname(void)
720 int c;
721 static char buffer[80];
723 while (1)
725 fprintf(stderr, "enter new filename: ");
726 fflush(stderr);
727 c = read(0, buffer, sizeof(buffer) - 1);
728 buffer[c ? c - 1 : 0] = '\0';
729 if (!*buffer)
730 continue;
731 for (c = 0; buffer[c]; c++)
732 if (buffer[c] == PATHSEP)
734 msg("*** new file must extract into this directory ***");
735 continue;
737 return (buffer);