Initial support for building for RISC OS using CMake
[debian-nspark.git] / unarc.c
blob33c938b8c42bfadf2c673711f48f04ea6fdd6c0d
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(int to_stdout)
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 puts("filename size load/date exec/time type storage");
284 puts("-------- ---- ----------- --------- ---- -------");
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 #ifdef DEBUGGING
307 if (debugging)
308 printf("ArcFS format archive\n");
309 #endif /* DEBUGGING */
310 arcfs = 1;
312 else
313 break;
315 retry_loop:
316 header = read_header(ifp);
317 comptype = header->comptype & 0x7f;
318 #ifdef DEBUGGING
319 if (debugging)
321 printf("archive file length = %ld level = %d\n", arcsize,
322 level);
323 print_header(header);
325 #endif /* DEBUGGING */
328 * If this is a compress _file_ then check archive size against
329 * compress-file length...
331 if (comptype && !(comptype == CT_NOTCOMP2 &&
332 /* BB changed constants in next line to long */
333 (header->load & 0xffffff00l) == 0xfffddc00l))
335 if (header->complen > arcsize)
337 #ifdef DEBUGGING
338 if (debugging)
339 puts("compressed len > archive file len");
340 #endif /* DEBUGGING */
341 header = NULL;
342 break;
344 else
345 arcsize -= header->complen;
348 if (!comptype)
349 { /* end of archive ? */
350 if (!level)
351 break;
352 level--;
354 * stamp directory now that all files have
355 * been written into it
357 if (!testing && !listing && stamp && inlist(pathname))
358 if (filestamp(&dirheader, pathname) < 0 && !quiet)
359 printf("error stamping %s\n", pathname);
360 pathname = uplevel();
361 continue;
365 * test for directory or file (file type = &DDC = archive)
367 if (comptype == CT_NOTCOMP2 &&
368 /* BB changed constants in next line to long */
369 (header->load & 0xffffff00l) == 0xfffddc00l)
371 level++;
372 pathname = downlevel(header->name);
373 dirheader = *header; /* make copy of header */
376 * create directory
378 if (!testing && !listing && inlist(pathname))
379 switch (exist(pathname))
381 case NOEXIST:
382 if (makedir(pathname) < 0 && !quiet)
384 printf("error making %s... aborting", pathname);
385 ret = 4;
386 break;
388 break;
390 case ISFILE:
391 if (!quiet)
392 printf("%s exists as a file... aborting",
393 pathname);
394 ret = 4;
395 goto unarc_exit;
396 case ISDIR:
397 default:
398 break;
401 else
402 { /* file */
403 if (pathname)
404 sprintf(fullname, "%s%c%s", pathname, PATHSEP,
405 header->name);
406 else
407 strcpy(fullname, header->name);
409 if (!inlist(fullname))
411 fseek(ifp, (long) header->complen, 1);
412 continue;
416 * print the archive file details...
418 if (!quiet)
420 printf("%-30s", fullname);
421 if (verbose)
422 print_details(header);
425 /* add to totals */
426 nbytes += header->origlen;
427 nfiles++;
429 if (listing)
431 /* if listing, nothing more to do */
432 if (!quiet)
433 putchar('\n');
434 fseek(ifp, (long) header->complen, 1);
435 continue;
437 else if (!quiet)
438 putchar(' ');
441 * append the filetype to the name
443 if (apptype)
444 append_type(header, fullname);
447 * if actually unarchiving then check if the file already exists
449 if (!testing)
451 test_exist:
452 switch (exist(fullname))
454 case ISFILE:
455 if (!force)
457 char c = prompt_user(fullname);
458 if (c == 'a')
459 force = 1;
460 else if (c == 'n')
462 fseek(ifp, (long) header->complen, 1);
463 continue;
465 else if (c == 'r')
467 char *newname = get_newname();
468 if (pathname)
469 sprintf(fullname, "%s%c%s", pathname,
470 PATHSEP, newname);
471 else
472 strcpy(fullname, newname);
473 goto test_exist;
475 /* if (c == 'y') FALLTHROUGH */
477 break;
478 case ISDIR:
479 if (!quiet)
480 puts("exists as a directory... skipping");
481 continue;
482 case NOEXIST:
483 default:
484 break;
486 ofp = fopen(fullname, W_OPENMODE);
487 if (!ofp)
489 if (!quiet)
490 printf("unable to create");
491 continue;
496 * do the unpacking
498 crcsize = writesize = header->origlen;
499 switch (comptype)
501 case CT_NOTCOMP:
502 case CT_NOTCOMP2:
503 status = unstore(header, ifp, ofp);
504 break;
505 case CT_CRUNCH:
506 status = uncompress(header, ifp, ofp, CRUNCH);
507 break;
508 case CT_PACK:
509 status = unpack(header, ifp, ofp);
510 break;
511 case CT_SQUASH:
512 status = uncompress(header, ifp, ofp, SQUASH);
513 break;
514 case CT_COMP:
515 status = uncompress(header, ifp, ofp, COMPRESS);
516 break;
517 default:
518 printf("unsupported archive type %d\n", comptype);
519 if (retrying)
520 goto do_retry;
521 fseek(ifp, (long) header->complen, 1);
522 continue;
525 if (!testing && ofp)
527 fclose(ofp);
528 ofp = NULL;
532 * check if unarchiving failed.
533 * (RERR check is not in switch() because `break'
534 * needs to escape from while())
536 if (status == RERR)
538 if (!quiet)
539 error("error reading archive");
540 ret = 3;
541 break;
543 switch (status)
545 case WERR:
546 if (!quiet)
547 printf("error writing file");
548 break;
549 case CRCERR:
550 if (!quiet)
551 printf("failed CRC check");
552 #ifdef DEBUGGING
553 if (debugging)
555 /* BB changed format in next line to long hex */
556 /* printf(" calculated CRC=0x%x", crc); */
557 #ifdef __MSDOS__
558 printf(" calculated CRC=0X%lX", crc);
559 #else
560 printf(" calculated CRC=0X%X", crc);
561 #endif /* __MSDOS__ */
563 #endif /* DEBUGGING */
564 break;
565 case NOERR:
566 if (!testing && stamp)
568 if (filestamp(header, fullname) < 0 && !quiet)
569 printf("\nerror stamping %s", fullname);
572 * XXX: if the filename has had it's filetype appended to
573 * it, it may be longer than 10 characters long making this
574 * file useless. We could truncate the filename but there is
575 * no need to under UNIX... bit of a mismatch!
577 if (!testing)
579 if (inffiles)
581 strcpy(logfile, fullname);
582 strcat(logfile, ".inf");
583 lfp = fopen(logfile, W_OPENMODE);
585 if (lfp)
589 // BASIC
590 if ((header->load => 0xFFFFFB41) && (header->load <= 0xFFFFFB46))
592 header->load = 0xFFFF1900;
593 header->exec = 0xFFFF802B;
596 // *EXEC ie !BOOT
597 if (header->load == 0xFFFFFE41)
599 header->load = 0x00000000;
600 header->exec = 0xFFFFFFFF;
603 fprintf(lfp, "%s %08lX %08lX\n",
604 riscos_path(fullname), (long)header->load,
605 (long)header->exec);
606 fclose(lfp);
611 else
613 if (lfp)
615 fprintf(lfp,
616 "SYS \"OS_File\", 1, \"%s\", &%08lX, &%08lX,, &%X\n",
617 riscos_path(fullname), (long)header->load,
618 (long)header->exec, header->attr);
623 break;
624 default:
625 break;
628 if (!quiet)
629 putchar('\n');
630 if (ret)
631 break;
636 * find out why header wasn't found
638 if (!header)
639 switch (check_stream(ifp))
641 case FNOERR:
642 if (!quiet)
643 printf("bad archive header");
644 if (retry && !listing)
646 puts("... retrying");
647 do_retry:
648 retrying++;
649 while (check_stream(ifp) == FNOERR)
650 if (read_byte(ifp) == STARTBYTE)
652 Byte byte = read_byte(ifp);
653 switch (byte & 0x7f)
655 case (CT_NOTCOMP):
656 case (CT_NOTCOMP2):
657 case (CT_CRUNCH):
658 case (CT_PACK):
659 case (CT_SQUASH):
660 case (CT_COMP):
661 ungetc((int) byte, ifp);
662 goto retry_loop;
663 /* NOTREACHED */
664 default:
665 break;
669 else
671 retrying = 0;
672 if (!quiet)
673 putchar('\n');
675 ret = 2;
676 break;
677 case FRWERR:
678 if (!quiet)
679 puts("error reading archive");
680 ret = 3;
681 break;
682 case FEND:
683 default:
684 break;
687 unarc_exit:
689 if (verbose)
690 printf("total of %ld bytes in %d files\n", (long)nbytes, nfiles);
692 if (ofp)
693 fclose(ofp);
694 if (lfp)
695 fclose(lfp);
696 if (ifp)
697 fclose(ifp);
698 return (ret);
702 * the file being extracted already exists, so ask user what to do...
704 char
705 prompt_user(char *filename)
707 int c;
708 char buffer[80];
710 while (1)
712 printf("\n\"%s\" exists, overwrite ? (Yes/No/All/Rename): ",
713 filename);
714 fflush(stdout);
715 if (read(0, buffer, sizeof(buffer) - 1)<1) {
716 c = 'n';
717 break;
719 if (isupper(*buffer))
720 c = tolower(*buffer);
721 else
722 c = *buffer;
723 if (c == 'y' || c == 'n' || c == 'a' || c == 'r')
724 break;
726 return (c);
730 * user wants to rename file, so get the leaf name of the new file...
732 char *
733 get_newname(void)
735 int c;
736 static char buffer[80];
738 while (1)
740 printf("enter new filename: ");
741 fflush(stdout);
742 c = read(0, buffer, sizeof(buffer) - 1);
743 buffer[c ? c - 1 : 0] = '\0';
744 if (!*buffer)
745 continue;
746 for (c = 0; buffer[c]; c++)
747 if (buffer[c] == PATHSEP)
749 puts("*** new file must extract into this directory ***");
750 continue;
752 return (buffer);