]> git.saurik.com Git - wxWidgets.git/blob - utils/Install/packace/unace.c
corrections to directory management for Mac OS X
[wxWidgets.git] / utils / Install / packace / unace.c
1 /* ------------------------------------------------------------------------ */
2 /* */
3 /* Main file of public UNACE. */
4 /* */
5 /* ------------------------------------------------------------------------ */
6
7
8 //--------------- include general files ------------------------------------//
9 #include <ctype.h> // tolower()
10 #include <fcntl.h> // open()
11 #include <stdio.h> // printf() sprintf() remove()
12 #include <stdlib.h> // malloc()
13 #include <string.h> // str*()
14 #include <sys/types.h>
15 #include <sys/stat.h> // S_I* AMIGA: fstat()
16
17 #define DIRSEP '\\'
18
19 #if (!defined(__EMX__) && !defined(__OS2__) && !defined(WINNT) && !defined(WIN32)) || defined(__CYGWIN__)
20 #include <sys/errno.h>
21 #endif
22
23
24 //--------------- include unace specific header files ----------------------//
25 #include "os.h"
26
27 #include "globals.h"
28 #include "portable.h"
29 #include "uac_comm.h"
30 #include "uac_crc.h"
31 #include "uac_crt.h"
32 #include "uac_dcpr.h"
33 #include "uac_sys.h"
34
35 #ifdef CRYPT
36 #include "unace_ps.h"
37 #endif /* CRYPT */
38 int files=0;
39
40 //--------------- BEGIN OF UNACE ROUTINES ----------------------------------//
41
42 int pipeit(char *format, ...) {
43 /* Do nothing ... perhaps pipe this somewhere in the future */
44 return 0;
45 }
46
47 void init_unace(void) // initializes unace
48 {
49 buf_rd =malloc(size_rdb * sizeof(ULONG)); // Allocate buffers: increase
50 buf =malloc(size_buf); // sizes when possible to speed
51 buf_wr =malloc(size_wrb); // up the program
52 readbuf=malloc(size_headrdb);
53
54 if (buf_rd ==NULL ||
55 buf ==NULL ||
56 buf_wr ==NULL ||
57 readbuf==NULL )
58 f_err = ERR_MEM;
59
60 make_crctable(); // initialize CRC table
61 dcpr_init(); // initialize decompression
62
63 set_handler(); // ctrl+break etc.
64 }
65
66 void done_unace(void)
67 {
68 if (buf_rd ) free(buf_rd );
69 if (buf ) free(buf );
70 if (buf_wr ) free(buf_wr );
71 if (readbuf ) free(readbuf );
72 if (dcpr_text) free(dcpr_text);
73 }
74
75 INT read_header(INT print_err) // reads any header from archive
76 {
77 USHORT rd,
78 head_size,
79 crc_ok;
80 LONG crc;
81 UCHAR *tp=readbuf;
82
83 lseek(archan, skipsize, SEEK_CUR); // skip ADDSIZE block
84
85 if (read(archan, &head, 4)<4)
86 return (0); // read CRC and header size
87
88 #ifdef HI_LO_BYTE_ORDER
89 WORDswap(&head.HEAD_CRC);
90 WORDswap(&head.HEAD_SIZE);
91 #endif
92 // read size_headrdb bytes into
93 head_size = head.HEAD_SIZE; // header structure
94 rd = (head_size > size_headrdb) ? size_headrdb : head_size;
95 if (read(archan, readbuf, rd) < rd)
96 return (0);
97 head_size -= rd;
98 crc = getcrc(CRC_MASK, readbuf, rd);
99
100 while (head_size) // skip rest of header
101 {
102 rd = (head_size > size_buf) ? size_buf : head_size;
103 if (read(archan, buf, rd) < rd)
104 return (0);
105 head_size -= rd;
106 crc = getcrc(crc, (UCHAR *)buf, rd);
107 }
108
109 head.HEAD_TYPE =*tp++; // generic buffer to head conversion
110 head.HEAD_FLAGS=BUFP2WORD(tp);
111
112 if (head.HEAD_FLAGS & ACE_ADDSIZE)
113 skipsize = head.ADDSIZE = BUF2LONG(tp); // get ADDSIZE
114 else
115 skipsize = 0;
116
117 // check header CRC
118 if (!(crc_ok = head.HEAD_CRC == (crc & 0xffff)) && print_err)
119 pipeit("\nError: archive is broken\n");
120 else
121 switch (head.HEAD_TYPE) // specific buffer to head conversion
122 {
123 case MAIN_BLK:
124 memcpy(mhead.ACESIGN, tp, acesign_len); tp+=acesign_len;
125 mhead.VER_MOD=*tp++;
126 mhead.VER_CR =*tp++;
127 mhead.HOST_CR=*tp++;
128 mhead.VOL_NUM=*tp++;
129 mhead.TIME_CR=BUFP2LONG(tp);
130 mhead.RES1 =BUFP2WORD(tp);
131 mhead.RES2 =BUFP2WORD(tp);
132 mhead.RES =BUFP2LONG(tp);
133 mhead.AV_SIZE=*tp++;
134 memcpy(mhead.AV, tp, rd-(USHORT)(tp-readbuf));
135 break;
136 case FILE_BLK:
137 fhead.PSIZE =BUFP2LONG(tp);
138 fhead.SIZE =BUFP2LONG(tp);
139 fhead.FTIME =BUFP2LONG(tp);
140 fhead.ATTR =BUFP2LONG(tp);
141 fhead.CRC32 =BUFP2LONG(tp);
142 fhead.TECH.TYPE =*tp++;
143 fhead.TECH.QUAL =*tp++;
144 fhead.TECH.PARM =BUFP2WORD(tp);
145 fhead.RESERVED =BUFP2WORD(tp);
146 fhead.FNAME_SIZE=BUFP2WORD(tp);
147 memcpy(fhead.FNAME, tp, rd-(USHORT)(tp-readbuf));
148 break;
149 // default: (REC_BLK and future things):
150 // do nothing 'cause isn't needed for extraction
151 }
152
153 return (crc_ok);
154 }
155 // maximum SFX module size
156 #define max_sfx_size 65536 // (needed by read_arc_head)
157
158 INT read_arc_head(void) // searches for the archive header and reads it
159 {
160 INT i,
161 flags,
162 buf_pos = 0;
163 LONG arc_head_pos,
164 old_fpos,
165 fpos = 0;
166 struct stat st;
167
168 fstat(archan, &st);
169
170 memset(buf, 0, size_buf);
171
172 #if !defined(__EMX__) && !defined(__OS2__)
173 while (ftell(farchan)<st.st_size && fpos < max_sfx_size)
174 #else
175 while (tell(archan)<st.st_size && fpos < max_sfx_size)
176 #endif
177 {
178 old_fpos = fpos;
179 fpos += read(archan, &buf[buf_pos], size_buf - buf_pos);
180
181 for (i = 0; i < size_buf; i++) // look for the acesign
182 {
183 if (!memcmp(acesign, &buf[i], acesign_len))
184 {
185 // seek to the probable begin
186 // of the archive
187 arc_head_pos = old_fpos + i - buf_pos - bytes_before_acesign;
188 lseek(archan, arc_head_pos, SEEK_SET);
189 if (read_header(0)) // try to read archive header
190 {
191 flags = mhead.HEAD_FLAGS;
192 adat.sol = (flags & ACE_SOLID) > 0;
193 adat.vol = (flags & ACE_MULT_VOL) > 0;
194 adat.vol_num = mhead.VOL_NUM;
195 adat.time_cr = mhead.TIME_CR;
196 return (1);
197 }
198 }
199 }
200 // was no archive header,
201 // continue search
202 lseek(archan, fpos, SEEK_SET);
203 memcpy(buf, &buf[size_buf - 512], 512);
204 buf_pos = 512; // keep 512 old bytes
205 }
206 return (0);
207 }
208
209 INT open_archive(INT print_err) // opens archive (or volume)
210 {
211 CHAR av_str[80];
212
213
214 #if defined(__OS2_) || defined(__EMX__) || defined(WIN32)
215 archan = open(aname, O_RDONLY | O_BINARY); // open file
216 #else
217 archan = open(aname, O_RDONLY); // open file
218 #endif
219 #if !defined(__EMX__) && !defined(__OS2__)
220 farchan = fdopen(archan, "rb");
221 #endif
222 if (archan == -1)
223 {
224 pipeit("\nError opening file %s", aname);
225 return (0);
226 }
227 if (!read_arc_head()) // read archive header
228 {
229 if (print_err)
230 pipeit("\nInvalid archive file: %s\n", aname);
231 #if !defined(__EMX__) && !defined(__OS2__)
232 fclose(farchan);
233 #endif
234 close(archan);
235 return (0);
236 }
237
238 pipeit("\nProcessing archive: %s\n\n", aname);
239 if (head.HEAD_FLAGS & ACE_AV)
240 {
241 pipeit("Authenticity Verification:"); // print the AV
242 sprintf(av_str, "\ncreated on %d.%d.%d by ",
243 ts_day(adat.time_cr), ts_month(adat.time_cr), ts_year(adat.time_cr));
244 pipeit(av_str);
245 strncpy(av_str, (char *)mhead.AV, mhead.AV_SIZE);
246 av_str[mhead.AV_SIZE] = 0;
247 pipeit("%s\n\n", av_str);
248 }
249 comment_out("Main comment:"); // print main comment
250 return (1);
251 }
252
253 void get_next_volname(void) // get file name of next volume
254 {
255 CHAR *cp;
256 INT num;
257
258 if ((cp = (CHAR *) strrchr(aname, '.')) == NULL || !*(cp + 1))
259 num = -1;
260 else
261 {
262 cp++;
263 num = (*(cp + 1) - '0') * 10 + *(cp + 2) - '0';
264 if (!in(num, 0, 99))
265 num = -1;
266 if (in(*cp, '0', '9'))
267 num += (*cp - '0') * 100;
268 }
269 num++;
270
271 if (num < 100)
272 *cp = 'C';
273 else
274 *cp = num / 100 + '0';
275 *(cp + 1) = (num / 10) % 10 + '0';
276 *(cp + 2) = num % 10 + '0';
277 }
278
279 INT proc_vol(void) // opens volume
280 {
281 INT i;
282 CHAR s[80];
283
284 if (!fileexists(aname) || !f_allvol_pr)
285 {
286 do
287 {
288 sprintf(s, "Ready to process %s?", aname);
289 #if !defined(__MINGW32__)
290 beep();
291 #else
292 beep(500,500);
293 #endif
294 i = wrask(s); // ask whether ready or not
295 f_allvol_pr = (i == 1); // "Always" --> process all volumes
296 if (i >= 2)
297 {
298 f_err = ERR_FOUND;
299 return (0);
300 }
301 }
302 while (!fileexists(aname));
303 }
304
305 if (!open_archive(1)) // open volume
306 {
307 pipeit("\nError while opening archive. File not found or archive broken.\n");
308 f_err = ERR_OPEN;
309 return (0);
310 }
311
312 return (1);
313 }
314
315 INT proc_next_vol(void) // opens next volume to process
316 {
317 #if !defined(__EMX__) && !defined(__OS2__)
318 fclose(farchan);
319 #endif
320 close(archan); // close handle
321 get_next_volname(); // get file name of next volume
322
323 if (!proc_vol()) // try to open volume, read archive header
324 return 0;
325 if (!read_header(1)) // read 2nd header
326 {
327 f_err=ERR_READ;
328 return 0;
329 }
330 return 1;
331 }
332
333 INT read_adds_blk(CHAR * buffer, INT len) // reads part of ADD_SIZE block
334 {
335 INT rd = 0,
336 l = len;
337 LONG i;
338
339 #ifdef CRYPT
340 char *cbuffer=buffer;
341
342 if (head.HEAD_TYPE == FILE_BLK && (head.HEAD_FLAGS & ACE_PASSW))
343 len = crypt_len(len);
344 #endif /* CRYPT */
345 while (!f_err && len && skipsize)
346 {
347 i = (skipsize > len) ? len : skipsize;
348 skipsize -= i;
349
350 /* How do I check error condition when comping -mno-cygwin? */
351 #if !defined(__MINGW32__)
352 errno = 0;
353 #endif
354 rd += read(archan, buffer, i);
355 #if !defined(__MINGW32__)
356 if (errno)
357 {
358 pipeit("\nRead error\n");
359 f_err = ERR_READ;
360 }
361 #endif
362
363 buffer += i;
364 len -= i;
365
366 if (!skipsize) // if block is continued on next volume
367 if (head.HEAD_FLAGS & ACE_SP_AFTER && !proc_next_vol())
368 break;
369 }
370 #ifdef CRYPT
371 if (head.HEAD_TYPE == FILE_BLK && (head.HEAD_FLAGS & ACE_PASSW))
372 decrypt(cbuffer, rd);
373 #endif /* CRYPT */
374
375 return (rd > l ? l : rd);
376 }
377
378 void crc_print(void) // checks CRC, prints message
379 {
380 INT crc_not_ok = rd_crc != fhead.CRC32; /* check CRC of file */
381
382 if (!f_err) // print message
383 {
384 pipeit(crc_not_ok ? " CRC-check error" : " CRC OK");
385 flush;
386 }
387 }
388
389 void analyze_file(void) // analyzes one file (for solid archives)
390 {
391 pipeit("\n Analyzing");
392 flush;
393 while (!cancel() && (dcpr_adds_blk(buf_wr, size_wrb))) // decompress only
394 ;
395 crc_print();
396 }
397
398 void extract_file(void) // extracts one file
399 {
400 INT rd;
401
402 pipeit("\n Extracting");
403 flush; // decompress block
404 while (!cancel() && (rd = dcpr_adds_blk(buf_wr, size_wrb)))
405 {
406 if (write(wrhan, buf_wr, rd) != rd) // write block
407 {
408 pipeit("\nWrite error\n");
409 f_err = ERR_WRITE;
410 }
411 }
412 crc_print();
413 }
414
415 /* extracts or tests all files of the archive
416 */
417 void extract_files(int nopath, int test)
418 {
419 CHAR file[PATH_MAX];
420
421 while (!cancel() && read_header(1))
422 {
423 if (head.HEAD_TYPE == FILE_BLK)
424 {
425 comment_out("File comment:"); // show file comment
426 ace_fname(file, &head, nopath); // get file name
427 pipeit("\n%s", file);
428 flush;
429 dcpr_init_file(); // initialize decompression of file
430 if (!f_err)
431 {
432 if (test ||
433 (wrhan = create_dest_file(file, (INT) fhead.ATTR))<0)
434 {
435 if (test || adat.sol)
436 analyze_file(); // analyze file
437 }
438 else
439 {
440 extract_file(); // extract it
441 #ifdef DOS // set file time
442 _dos_setftime(wrhan, (USHORT) (fhead.FTIME >> 16), (USHORT) fhead.FTIME);
443 #endif
444 close(wrhan);
445 #ifdef DOS // set file attributes
446 _dos_setfileattr(file, (UINT) fhead.ATTR);
447 #endif
448 #ifdef AMIGA
449 { // set file date and time
450 struct DateTime dt;
451 char Date[9], Time[9];
452 ULONG tstamp=fhead.FTIME;
453
454 sprintf(Date, "%02d-%02d-%02d", ts_year(tstamp)-1900, ts_month(tstamp), ts_day(tstamp));
455 sprintf(Time, "%02d:%02d:%02d", ts_hour(tstamp), ts_min(tstamp), ts_sec(tstamp));
456
457 dt.dat_Format = FORMAT_INT;
458 dt.dat_Flags = 0;
459 dt.dat_StrDate= Date;
460 dt.dat_StrTime= Time;
461
462 if (StrToDate(&dt))
463 SetFileDate(file, &dt.dat_Stamp);
464 }
465 #endif
466 if (f_err)
467 remove(file);
468 }
469 }
470 }
471 }
472 }
473
474 unsigned percentage(ULONG p, ULONG d)
475 {
476 return (unsigned)( d ? (d/2+p*100)/d : 100 );
477 }
478
479 void list_files(int verbose)
480 {
481 ULONG size =0,
482 psize=0,
483 tpsize;
484 CHAR file[PATH_MAX];
485
486 pipeit("Date |Time |Packed |Size |Ratio|File\n");
487
488 while (!cancel() && read_header(1))
489 {
490 if (head.HEAD_TYPE == FILE_BLK)
491 {
492 ULONG ti=fhead.FTIME;
493 ace_fname(file, &head, verbose ? 0 : 1); // get file name
494
495 size += fhead.SIZE;
496 psize +=
497 tpsize = fhead.PSIZE;
498 files++;
499
500 while (head.HEAD_FLAGS & ACE_SP_AFTER)
501 {
502 skipsize=0;
503 if (!proc_next_vol())
504 break;
505 psize += fhead.PSIZE;
506 tpsize+= fhead.PSIZE;
507 }
508 if (!f_err)
509 pipeit("%02u.%02u.%02u|%02u:%02u|%c%c%9lu|%9lu|%4u%%|%c%s\n",
510 ts_day (ti), ts_month(ti), ts_year(ti)%100,
511 ts_hour(ti), ts_min (ti),
512 fhead.HEAD_FLAGS & ACE_SP_BEF ? '<' : ' ',
513 fhead.HEAD_FLAGS & ACE_SP_AFTER ? '>' : ' ',
514 tpsize, fhead.SIZE, percentage(tpsize, fhead.SIZE),
515 fhead.HEAD_FLAGS & ACE_PASSW ? '*' : ' ',
516 file
517 );
518 }
519 }
520 if (!f_err)
521 {
522 pipeit("\n %9lu|%9lu|%4u%%| %u file%s",
523 psize,
524 size,
525 percentage(psize, size),
526 files,
527 (char*)(files == 1 ? "" : "s")
528 );
529 }
530 }
531
532 void showhelp(void)
533 {
534 pipeit("\n"
535 "Usage: UNACE <command> <archive[.ace]>\n"
536 "\n"
537 "Where <command> is one of:\n"
538 "\n"
539 " e Extract files\n"
540 " l List archive\n"
541 " t Test archive integrity\n"
542 " v List archive (verbose)\n"
543 " x Extract files with full path"
544 );
545 }
546
547 int include_unpack(char *bleah) // processes the archive
548 {
549 CHAR *s;
550
551 strcpy(aname, bleah);
552
553 init_unace(); // initialize unace
554
555 if (!(s = (CHAR *) strrchr(aname, DIRSEP)))
556 s = aname;
557 if (!strrchr(s, '.'))
558 strcat(aname, ".ACE");
559
560 if (open_archive(1)) // open archive to process
561 {
562 if (adat.vol_num)
563 pipeit("\nFirst volume of archive required!\n");
564 else
565 list_files (0 );
566
567 #if !defined(__EMX__) && !defined(__OS2__)
568 fclose(farchan);
569 #endif
570 close(archan);
571 if (f_err)
572 {
573 pipeit("\nError occurred\n");
574 if (f_criterr)
575 pipeit("Critical error on drive %c\n", f_criterr);
576 }
577 }
578 else
579 f_err = ERR_CLINE;
580
581 done_unace();
582 return (f_err);
583 }
584