Merge pull request #7 from ccawley2011/msvc
[debian-nspark.git] / unarc.c
blob9170b4cb5fdecd1136248d692c868b7adc49bae6
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 #if defined(RISCOS) || defined(__MSDOS__)
103 #include <string.h> /* for strcpy */
104 #endif /* RISCOS || __MSDOS__ */
106 char prompt_user(char *filename);
107 char *get_newname(void);
110 #ifndef __MSDOS__
112 do_unsquash()
114 SqshHeader sqsh_header;
115 Header header;
116 FILE *ifp, *ofp;
117 Status r;
118 long offset;
119 long datastart;
120 char *ofname = NULL;
121 char *p;
123 ifp = fopen(archive, "rb");
124 if (!ifp)
126 error("Can not open %s for reading\n", archive);
127 return 1;
130 if (!to_stdout)
132 ofname = calloc(1, strlen(archive) + sizeof(",xxx"));
133 /* Output filename is the same as the input filename with
134 * the correct filetype appended */
135 strcpy(ofname, archive);
136 /* Strip RISCOS filetype from output filename */
137 p = ofname + (strlen(ofname) - 4);
138 if (*p == ',' || *p == '.')
140 *p = '\0';
144 r = read_sqsh_header(ifp, &sqsh_header);
145 if (r != NOERR)
147 error("Invalid squash file");
148 return 1;
151 memset(&header, 0, sizeof(header));
152 datastart = ftell(ifp);
153 fseek(ifp, 0, SEEK_END);
154 offset = ftell(ifp);
156 /* The uncompress code uses Spark entry header structures.
157 * Use the squash header and other info to set up the Spark header
158 * with needed info. */
159 header.complen = (Word)(offset - datastart);
160 sqsh_header_to_header(&sqsh_header, &header);
161 fseek(ifp, datastart, SEEK_SET);
163 crcsize = 0;
164 writesize = header.origlen;
166 if (!to_stdout)
168 /* Append the correct filetype to the output filename */
169 append_type(&header, ofname);
172 if (!force && !to_stdout)
174 ofp = fopen(ofname, "rb");
175 if (ofp)
177 char c;
178 char *newname;
180 fclose(ofp);
181 c = prompt_user(ofname);
182 switch (c)
184 case 'n':
185 exit(1);
186 case 'r':
187 newname = get_newname();
188 free(ofname);
189 ofname = strdup(newname);
190 break;
191 case 'a':
192 case 'y':
193 /* overwrite file */
194 break;
199 if (to_stdout)
201 ofp = stdout;
203 else
205 ofp = fopen(ofname, "wb");
206 if (!ofp)
208 error("Can not open %s for writing\n", ofname);
209 return 1;
213 r = uncompress(&header, ifp, ofp, UNIX_COMPRESS);
214 fclose(ifp);
215 if (!to_stdout)
217 fclose(ofp);
219 if (r == NOERR)
221 if (stamp && !to_stdout)
223 filestamp(&header, ofname);
226 else
228 error("Failed to decompress file");
229 return 1;
232 return 0;
234 #endif
237 do_unarc()
239 FILE *ifp, *ofp = NULL, *lfp = NULL;
240 Header *header = NULL;
241 char *pathname = NULL;
242 char fullname[PATHNAMELEN];
243 int level = 0, ret = 0, retrying = 0;
244 Word arcsize;
245 Word nbytes = 0;
246 int nfiles = 0;
247 Status status;
249 ifp = fopen(archive, R_OPENMODE);
250 if (!ifp)
252 ifp = fopen(name_dot_arc(archive), R_OPENMODE);
253 if (!ifp)
255 if (!quiet)
256 error("cannot open archive \"%s\"", archive);
257 return (1);
259 archive = name_dot_arc(archive);
262 arcsize = filesize(archive);
263 if (!arcsize && !quiet)
265 error("cannot get size of archive file");
266 fclose(ifp);
267 return (1);
270 /* MU changed to accomodate the -I option */
271 if (!inffiles)
273 if (!testing && !listing && logfile)
275 lfp = fopen(logfile, W_OPENMODE);
276 if (!lfp)
277 warning("unable to open logfile \"%s\"", logfile);
281 if (!quiet && verbose)
283 msg("filename size load/date exec/time type storage");
284 msg("-------- ---- ----------- --------- ---- -------");
288 * read compressed files from archive until end of file...
290 while (1)
292 static Header dirheader;
293 Byte comptype, first;
295 header = NULL;
296 if ((arcfs == 0) && ((first = read_byte(ifp)) != STARTBYTE))
298 if ((first == 'A') &&
299 (read_byte(ifp) == 'r') &&
300 (read_byte(ifp) == 'c') &&
301 (read_byte(ifp) == 'h') &&
302 (read_byte(ifp) == 'i') &&
303 (read_byte(ifp) == 'v') &&
304 (read_byte(ifp) == 'e') && (read_byte(ifp) == '\0'))
306 debug("ArcFS format archive\n");
307 arcfs = 1;
309 else
310 break;
312 retry_loop:
313 header = read_header(ifp);
314 comptype = header->comptype & 0x7f;
315 #ifdef DEBUGGING
316 debug("archive file length = %ld level = %d\n", arcsize,
317 level);
318 print_header(header);
319 #endif /* DEBUGGING */
322 * If this is a compress _file_ then check archive size against
323 * compress-file length...
325 if (comptype && !(comptype == CT_NOTCOMP2 &&
326 /* BB changed constants in next line to long */
327 (header->load & 0xffffff00l) == 0xfffddc00l))
329 if (header->complen > arcsize)
331 debug("compressed len > archive file len");
332 header = NULL;
333 break;
335 else
336 arcsize -= header->complen;
339 if (!comptype)
340 { /* end of archive ? */
341 if (!level)
342 break;
343 level--;
345 * stamp directory now that all files have
346 * been written into it
348 if (!testing && !listing && !to_stdout && stamp && inlist(pathname))
349 if (filestamp(&dirheader, pathname) < 0 && !quiet)
350 error("error stamping %s", pathname);
351 pathname = uplevel();
352 continue;
356 * test for directory or file (file type = &DDC = archive)
358 if (comptype == CT_NOTCOMP2 &&
359 /* BB changed constants in next line to long */
360 (header->load & 0xffffff00l) == 0xfffddc00l)
362 level++;
363 pathname = downlevel(header->name);
364 dirheader = *header; /* make copy of header */
367 * create directory
369 if (!testing && !listing && !to_stdout && inlist(pathname))
370 switch (exist(pathname))
372 case NOEXIST:
373 if (makedir(pathname) < 0 && !quiet)
375 msg("error making %s... aborting", pathname);
376 ret = 4;
377 break;
379 break;
381 case ISFILE:
382 if (!quiet)
383 msg("%s exists as a file... aborting",
384 pathname);
385 ret = 4;
386 goto unarc_exit;
387 case ISDIR:
388 default:
389 break;
392 else
393 { /* file */
394 if (pathname)
395 sprintf(fullname, "%s%c%s", pathname, PATHSEP,
396 header->name);
397 else
398 strcpy(fullname, header->name);
400 if (!inlist(fullname))
402 fseek(ifp, (long) header->complen, 1);
403 continue;
407 * print the archive file details...
409 if (!quiet)
411 msg("%-30s", fullname);
412 if (verbose)
413 print_details(header);
416 /* add to totals */
417 nbytes += header->origlen;
418 nfiles++;
420 if (listing)
422 /* if listing, nothing more to do */
423 if (!quiet)
424 putc('\n', stderr);
425 fseek(ifp, (long) header->complen, 1);
426 continue;
428 else if (!quiet)
429 putc(' ', stderr);
432 * append the filetype to the name
434 if (apptype && !to_stdout)
435 append_type(header, fullname);
438 * if actually unarchiving then check if the file already exists
440 if (!testing && !to_stdout)
442 test_exist:
443 switch (exist(fullname))
445 case ISFILE:
446 if (!force)
448 char c = prompt_user(fullname);
449 if (c == 'a')
450 force = 1;
451 else if (c == 'n')
453 fseek(ifp, (long) header->complen, 1);
454 continue;
456 else if (c == 'r')
458 char *newname = get_newname();
459 if (pathname)
460 sprintf(fullname, "%s%c%s", pathname,
461 PATHSEP, newname);
462 else
463 strcpy(fullname, newname);
464 goto test_exist;
466 /* if (c == 'y') FALLTHROUGH */
468 break;
469 case ISDIR:
470 if (!quiet)
471 msg("exists as a directory... skipping");
472 continue;
473 case NOEXIST:
474 default:
475 break;
477 ofp = fopen(fullname, W_OPENMODE);
478 if (!ofp)
480 if (!quiet)
481 msg("unable to create");
482 continue;
485 else if (!testing)
487 ofp = stdout;
491 * do the unpacking
493 crcsize = writesize = header->origlen;
494 switch (comptype)
496 case CT_NOTCOMP:
497 case CT_NOTCOMP2:
498 status = unstore(header, ifp, ofp);
499 break;
500 case CT_CRUNCH:
501 status = uncompress(header, ifp, ofp, CRUNCH);
502 break;
503 case CT_PACK:
504 status = unpack(header, ifp, ofp);
505 break;
506 case CT_SQUASH:
507 status = uncompress(header, ifp, ofp, SQUASH);
508 break;
509 case CT_COMP:
510 status = uncompress(header, ifp, ofp, COMPRESS);
511 break;
512 default:
513 error("unsupported archive type %d", comptype);
514 if (retrying)
515 goto do_retry;
516 fseek(ifp, (long) header->complen, 1);
517 continue;
520 if (!testing && !to_stdout && ofp)
522 fclose(ofp);
523 ofp = NULL;
527 * check if unarchiving failed.
528 * (RERR check is not in switch() because `break'
529 * needs to escape from while())
531 if (status == RERR)
533 if (!quiet)
534 error("error reading archive");
535 ret = 3;
536 break;
538 switch (status)
540 case WERR:
541 if (!quiet)
542 msg("error writing file");
543 break;
544 case CRCERR:
545 if (!quiet)
546 msg("failed CRC check");
548 /* BB changed format in next line to long hex */
549 /* debug(" calculated CRC=0x%x", crc); */
550 #ifdef __MSDOS__
551 debug(" calculated CRC=0X%lX", crc);
552 #else
553 debug(" calculated CRC=0X%X", crc);
554 #endif /* __MSDOS__ */
555 break;
556 case NOERR:
557 if (!testing && !to_stdout && stamp)
559 if (filestamp(header, fullname) < 0 && !quiet)
560 msg("\nerror stamping %s", fullname);
563 * XXX: if the filename has had it's filetype appended to
564 * it, it may be longer than 10 characters long making this
565 * file useless. We could truncate the filename but there is
566 * no need to under UNIX... bit of a mismatch!
568 if (!testing)
570 if (inffiles)
572 strcpy(logfile, fullname);
573 strcat(logfile, ".inf");
574 lfp = fopen(logfile, W_OPENMODE);
576 if (lfp)
580 // BASIC
581 if ((header->load => 0xFFFFFB41) && (header->load <= 0xFFFFFB46))
583 header->load = 0xFFFF1900;
584 header->exec = 0xFFFF802B;
587 // *EXEC ie !BOOT
588 if (header->load == 0xFFFFFE41)
590 header->load = 0x00000000;
591 header->exec = 0xFFFFFFFF;
594 fprintf(lfp, "%s %08lX %08lX\n",
595 riscos_path(fullname), (long)header->load,
596 (long)header->exec);
597 fclose(lfp);
602 else
604 if (lfp)
606 fprintf(lfp,
607 "SYS \"OS_File\", 1, \"%s\", &%08lX, &%08lX,, &%X\n",
608 riscos_path(fullname), (long)header->load,
609 (long)header->exec, header->attr);
614 break;
615 default:
616 break;
619 if (!quiet)
620 putc('\n', stderr);
621 if (ret)
622 break;
627 * find out why header wasn't found
629 if (!header)
630 switch (check_stream(ifp))
632 case FNOERR:
633 if (!quiet)
634 msg("bad archive header");
635 if (retry && !listing)
637 msg("... retrying");
638 do_retry:
639 retrying++;
640 while (check_stream(ifp) == FNOERR)
641 if (read_byte(ifp) == STARTBYTE)
643 Byte byte = read_byte(ifp);
644 switch (byte & 0x7f)
646 case (CT_NOTCOMP):
647 case (CT_NOTCOMP2):
648 case (CT_CRUNCH):
649 case (CT_PACK):
650 case (CT_SQUASH):
651 case (CT_COMP):
652 ungetc((int) byte, ifp);
653 goto retry_loop;
654 /* NOTREACHED */
655 default:
656 break;
660 else
662 retrying = 0;
663 if (!quiet)
664 putc('\n', stderr);
666 ret = 2;
667 break;
668 case FRWERR:
669 if (!quiet)
670 msg("error reading archive");
671 ret = 3;
672 break;
673 case FEND:
674 default:
675 break;
678 unarc_exit:
680 if (verbose)
681 msg("total of %ld bytes in %d files\n", (long)nbytes, nfiles);
683 if (ofp && !to_stdout)
684 fclose(ofp);
685 if (lfp)
686 fclose(lfp);
687 if (ifp)
688 fclose(ifp);
689 return (ret);
693 * the file being extracted already exists, so ask user what to do...
695 char
696 prompt_user(char *filename)
698 int c;
699 char buffer[80];
701 while (1)
703 fprintf(stderr, "\n\"%s\" exists, overwrite ? (Yes/No/All/Rename): ",
704 filename);
705 fflush(stderr);
706 if (read(0, buffer, sizeof(buffer) - 1)<1) {
707 c = 'n';
708 break;
710 if (isupper(*buffer))
711 c = tolower(*buffer);
712 else
713 c = *buffer;
714 if (c == 'y' || c == 'n' || c == 'a' || c == 'r')
715 break;
717 return (c);
721 * user wants to rename file, so get the leaf name of the new file...
723 char *
724 get_newname(void)
726 int c;
727 static char buffer[80];
729 while (1)
731 fprintf(stderr, "enter new filename: ");
732 fflush(stderr);
733 c = read(0, buffer, sizeof(buffer) - 1);
734 buffer[c ? c - 1 : 0] = '\0';
735 if (!*buffer)
736 continue;
737 for (c = 0; buffer[c]; c++)
738 if (buffer[c] == PATHSEP)
740 msg("*** new file must extract into this directory ***");
741 continue;
743 return (buffer);