Merge pull request #9 from ccawley2011/buildsystems
[debian-nspark.git] / unarc.c
blobd06020ea9741f1eea2589fd0ba646c2729634500
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);
106 #ifndef __MSDOS__
108 do_unsquash()
110 SqshHeader sqsh_header;
111 Header header;
112 FILE *ifp, *ofp;
113 Status r;
114 long offset;
115 long datastart;
116 char *ofname = NULL;
117 char *p;
119 ifp = fopen(archive, "rb");
120 if (!ifp)
122 error("Can not open %s for reading\n", archive);
123 return 1;
126 if (!to_stdout)
128 ofname = calloc(1, strlen(archive) + sizeof(",xxx"));
129 /* Output filename is the same as the input filename with
130 * the correct filetype appended */
131 strcpy(ofname, archive);
132 /* Strip RISCOS filetype from output filename */
133 p = ofname + (strlen(ofname) - 4);
134 if (*p == ',' || *p == '.')
136 *p = '\0';
140 r = read_sqsh_header(ifp, &sqsh_header);
141 if (r != NOERR)
143 error("Invalid squash file");
144 return 1;
147 memset(&header, 0, sizeof(header));
148 datastart = ftell(ifp);
149 fseek(ifp, 0, SEEK_END);
150 offset = ftell(ifp);
152 /* The uncompress code uses Spark entry header structures.
153 * Use the squash header and other info to set up the Spark header
154 * with needed info. */
155 header.complen = (Word)(offset - datastart);
156 sqsh_header_to_header(&sqsh_header, &header);
157 fseek(ifp, datastart, SEEK_SET);
159 crcsize = 0;
160 writesize = header.origlen;
162 if (!to_stdout)
164 /* Append the correct filetype to the output filename */
165 append_type(&header, ofname);
168 if (!force && !to_stdout)
170 ofp = fopen(ofname, "rb");
171 if (ofp)
173 char c;
174 char *newname;
176 fclose(ofp);
177 c = prompt_user(ofname);
178 switch (c)
180 case 'n':
181 exit(1);
182 case 'r':
183 newname = get_newname();
184 free(ofname);
185 ofname = strdup(newname);
186 break;
187 case 'a':
188 case 'y':
189 /* overwrite file */
190 break;
195 if (to_stdout)
197 ofp = stdout;
199 else
201 ofp = fopen(ofname, "wb");
202 if (!ofp)
204 error("Can not open %s for writing\n", ofname);
205 return 1;
209 r = uncompress(&header, ifp, ofp, UNIX_COMPRESS);
210 fclose(ifp);
211 if (!to_stdout)
213 fclose(ofp);
215 if (r == NOERR)
217 if (stamp && !to_stdout)
219 filestamp(&header, ofname);
222 else
224 error("Failed to decompress file");
225 return 1;
228 return 0;
230 #endif
233 do_unarc()
235 FILE *ifp, *ofp = NULL, *lfp = NULL;
236 Header *header = NULL;
237 char *pathname = NULL;
238 char fullname[PATHNAMELEN];
239 int level = 0, ret = 0, retrying = 0;
240 Word arcsize;
241 Word nbytes = 0;
242 int nfiles = 0;
243 Status status;
245 ifp = fopen(archive, R_OPENMODE);
246 if (!ifp)
248 ifp = fopen(name_dot_arc(archive), R_OPENMODE);
249 if (!ifp)
251 if (!quiet)
252 error("cannot open archive \"%s\"", archive);
253 return (1);
255 archive = name_dot_arc(archive);
258 arcsize = filesize(archive);
259 if (!arcsize && !quiet)
261 error("cannot get size of archive file");
262 fclose(ifp);
263 return (1);
266 /* MU changed to accomodate the -I option */
267 if (!inffiles)
269 if (!testing && !listing && logfile)
271 lfp = fopen(logfile, W_OPENMODE);
272 if (!lfp)
273 warning("unable to open logfile \"%s\"", logfile);
277 if (!quiet && verbose)
279 msg("filename size load/date exec/time type storage");
280 msg("-------- ---- ----------- --------- ---- -------");
284 * read compressed files from archive until end of file...
286 while (1)
288 static Header dirheader;
289 Byte comptype, first;
291 header = NULL;
292 if ((arcfs == 0) && ((first = read_byte(ifp)) != STARTBYTE))
294 if ((first == 'A') &&
295 (read_byte(ifp) == 'r') &&
296 (read_byte(ifp) == 'c') &&
297 (read_byte(ifp) == 'h') &&
298 (read_byte(ifp) == 'i') &&
299 (read_byte(ifp) == 'v') &&
300 (read_byte(ifp) == 'e') && (read_byte(ifp) == '\0'))
302 debug("ArcFS format archive\n");
303 arcfs = 1;
305 else
306 break;
308 retry_loop:
309 header = read_header(ifp);
310 comptype = header->comptype & 0x7f;
311 #ifdef DEBUGGING
312 debug("archive file length = %ld level = %d\n", arcsize,
313 level);
314 print_header(header);
315 #endif /* DEBUGGING */
318 * If this is a compress _file_ then check archive size against
319 * compress-file length...
321 if (comptype && !(comptype == CT_NOTCOMP2 &&
322 /* BB changed constants in next line to long */
323 (header->load & 0xffffff00l) == 0xfffddc00l))
325 if (header->complen > arcsize)
327 debug("compressed len > archive file len");
328 header = NULL;
329 break;
331 else
332 arcsize -= header->complen;
335 if (!comptype)
336 { /* end of archive ? */
337 if (!level)
338 break;
339 level--;
341 * stamp directory now that all files have
342 * been written into it
344 if (!testing && !listing && !to_stdout && stamp && inlist(pathname))
345 if (filestamp(&dirheader, pathname) < 0 && !quiet)
346 error("error stamping %s", pathname);
347 pathname = uplevel();
348 continue;
352 * test for directory or file (file type = &DDC = archive)
354 if (comptype == CT_NOTCOMP2 &&
355 /* BB changed constants in next line to long */
356 (header->load & 0xffffff00l) == 0xfffddc00l)
358 level++;
359 pathname = downlevel(header->name);
360 dirheader = *header; /* make copy of header */
363 * create directory
365 if (!testing && !listing && !to_stdout && inlist(pathname))
366 switch (exist(pathname))
368 case NOEXIST:
369 if (makedir(pathname) < 0 && !quiet)
371 msg("error making %s... aborting", pathname);
372 ret = 4;
373 break;
375 break;
377 case ISFILE:
378 if (!quiet)
379 msg("%s exists as a file... aborting",
380 pathname);
381 ret = 4;
382 goto unarc_exit;
383 case ISDIR:
384 default:
385 break;
388 else
389 { /* file */
390 if (pathname)
391 sprintf(fullname, "%s%c%s", pathname, PATHSEP,
392 header->name);
393 else
394 strcpy(fullname, header->name);
396 if (!inlist(fullname))
398 fseek(ifp, (long) header->complen, 1);
399 continue;
403 * print the archive file details...
405 if (!quiet)
407 msg("%-30s", fullname);
408 if (verbose)
409 print_details(header);
412 /* add to totals */
413 nbytes += header->origlen;
414 nfiles++;
416 if (listing)
418 /* if listing, nothing more to do */
419 if (!quiet)
420 putc('\n', stderr);
421 fseek(ifp, (long) header->complen, 1);
422 continue;
424 else if (!quiet)
425 putc(' ', stderr);
428 * append the filetype to the name
430 if (apptype && !to_stdout)
431 append_type(header, fullname);
434 * if actually unarchiving then check if the file already exists
436 if (!testing && !to_stdout)
438 test_exist:
439 switch (exist(fullname))
441 case ISFILE:
442 if (!force)
444 char c = prompt_user(fullname);
445 if (c == 'a')
446 force = 1;
447 else if (c == 'n')
449 fseek(ifp, (long) header->complen, 1);
450 continue;
452 else if (c == 'r')
454 char *newname = get_newname();
455 if (pathname)
456 sprintf(fullname, "%s%c%s", pathname,
457 PATHSEP, newname);
458 else
459 strcpy(fullname, newname);
460 goto test_exist;
462 /* if (c == 'y') FALLTHROUGH */
464 break;
465 case ISDIR:
466 if (!quiet)
467 msg("exists as a directory... skipping");
468 continue;
469 case NOEXIST:
470 default:
471 break;
473 ofp = fopen(fullname, W_OPENMODE);
474 if (!ofp)
476 if (!quiet)
477 msg("unable to create");
478 continue;
481 else if (!testing)
483 ofp = stdout;
487 * do the unpacking
489 crcsize = writesize = header->origlen;
490 switch (comptype)
492 case CT_NOTCOMP:
493 case CT_NOTCOMP2:
494 status = unstore(header, ifp, ofp);
495 break;
496 case CT_CRUNCH:
497 status = uncompress(header, ifp, ofp, CRUNCH);
498 break;
499 case CT_PACK:
500 status = unpack(header, ifp, ofp);
501 break;
502 case CT_SQUASH:
503 status = uncompress(header, ifp, ofp, SQUASH);
504 break;
505 case CT_COMP:
506 status = uncompress(header, ifp, ofp, COMPRESS);
507 break;
508 default:
509 error("unsupported archive type %d", comptype);
510 if (retrying)
511 goto do_retry;
512 fseek(ifp, (long) header->complen, 1);
513 continue;
516 if (!testing && !to_stdout && ofp)
518 fclose(ofp);
519 ofp = NULL;
523 * check if unarchiving failed.
524 * (RERR check is not in switch() because `break'
525 * needs to escape from while())
527 if (status == RERR)
529 if (!quiet)
530 error("error reading archive");
531 ret = 3;
532 break;
534 switch (status)
536 case WERR:
537 if (!quiet)
538 msg("error writing file");
539 break;
540 case CRCERR:
541 if (!quiet)
542 msg("failed CRC check");
544 /* BB changed format in next line to long hex */
545 /* debug(" calculated CRC=0x%x", crc); */
546 #ifdef __MSDOS__
547 debug(" calculated CRC=0X%lX", crc);
548 #else
549 debug(" calculated CRC=0X%X", crc);
550 #endif /* __MSDOS__ */
551 break;
552 case NOERR:
553 if (!testing && !to_stdout && stamp)
555 if (filestamp(header, fullname) < 0 && !quiet)
556 msg("\nerror stamping %s", fullname);
559 * XXX: if the filename has had it's filetype appended to
560 * it, it may be longer than 10 characters long making this
561 * file useless. We could truncate the filename but there is
562 * no need to under UNIX... bit of a mismatch!
564 if (!testing)
566 if (inffiles)
568 strcpy(logfile, fullname);
569 strcat(logfile, ".inf");
570 lfp = fopen(logfile, W_OPENMODE);
572 if (lfp)
576 // BASIC
577 if ((header->load => 0xFFFFFB41) && (header->load <= 0xFFFFFB46))
579 header->load = 0xFFFF1900;
580 header->exec = 0xFFFF802B;
583 // *EXEC ie !BOOT
584 if (header->load == 0xFFFFFE41)
586 header->load = 0x00000000;
587 header->exec = 0xFFFFFFFF;
590 fprintf(lfp, "%s %08lX %08lX\n",
591 riscos_path(fullname), (long)header->load,
592 (long)header->exec);
593 fclose(lfp);
598 else
600 if (lfp)
602 fprintf(lfp,
603 "SYS \"OS_File\", 1, \"%s\", &%08lX, &%08lX,, &%X\n",
604 riscos_path(fullname), (long)header->load,
605 (long)header->exec, header->attr);
610 break;
611 default:
612 break;
615 if (!quiet)
616 putc('\n', stderr);
617 if (ret)
618 break;
623 * find out why header wasn't found
625 if (!header)
626 switch (check_stream(ifp))
628 case FNOERR:
629 if (!quiet)
630 msg("bad archive header");
631 if (retry && !listing)
633 msg("... retrying");
634 do_retry:
635 retrying++;
636 while (check_stream(ifp) == FNOERR)
637 if (read_byte(ifp) == STARTBYTE)
639 Byte byte = read_byte(ifp);
640 switch (byte & 0x7f)
642 case (CT_NOTCOMP):
643 case (CT_NOTCOMP2):
644 case (CT_CRUNCH):
645 case (CT_PACK):
646 case (CT_SQUASH):
647 case (CT_COMP):
648 ungetc((int) byte, ifp);
649 goto retry_loop;
650 /* NOTREACHED */
651 default:
652 break;
656 else
658 retrying = 0;
659 if (!quiet)
660 putc('\n', stderr);
662 ret = 2;
663 break;
664 case FRWERR:
665 if (!quiet)
666 msg("error reading archive");
667 ret = 3;
668 break;
669 case FEND:
670 default:
671 break;
674 unarc_exit:
676 if (verbose)
677 msg("total of %ld bytes in %d files\n", (long)nbytes, nfiles);
679 if (ofp && !to_stdout)
680 fclose(ofp);
681 if (lfp)
682 fclose(lfp);
683 if (ifp)
684 fclose(ifp);
685 return (ret);
689 * the file being extracted already exists, so ask user what to do...
691 char
692 prompt_user(char *filename)
694 int c;
695 char buffer[80];
697 while (1)
699 fprintf(stderr, "\n\"%s\" exists, overwrite ? (Yes/No/All/Rename): ",
700 filename);
701 fflush(stderr);
702 if (read(0, buffer, sizeof(buffer) - 1)<1) {
703 c = 'n';
704 break;
706 if (isupper(*buffer))
707 c = tolower(*buffer);
708 else
709 c = *buffer;
710 if (c == 'y' || c == 'n' || c == 'a' || c == 'r')
711 break;
713 return (c);
717 * user wants to rename file, so get the leaf name of the new file...
719 char *
720 get_newname(void)
722 int c;
723 static char buffer[80];
725 while (1)
727 fprintf(stderr, "enter new filename: ");
728 fflush(stderr);
729 c = read(0, buffer, sizeof(buffer) - 1);
730 buffer[c ? c - 1 : 0] = '\0';
731 if (!*buffer)
732 continue;
733 for (c = 0; buffer[c]; c++)
734 if (buffer[c] == PATHSEP)
736 msg("*** new file must extract into this directory ***");
737 continue;
739 return (buffer);