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