1 /* UNARJ.C, UNARJ, R JUNG, 06/05/02
2  * Main Extractor routine
3  * Copyright (c) 1991-2002 by ARJ Software, Inc.  All rights reserved.
4  *
5  *   This code may be freely used in programs that are NOT ARJ archivers
6  *   (both compress and extract ARJ archives).
7  *
8  *   If you wish to distribute a modified version of this program, you
9  *   MUST indicate that it is a modified version both in the program and
10  *   source code.
11  *
12  *   We are holding the copyright on the source code, so please do not
13  *   delete our name from the program files or from the documentation.
14  *
15  *   We wish to give credit to Haruhiko Okumura for providing the
16  *   basic ideas for ARJ and UNARJ in his program AR.  Please note
17  *   that UNARJ is significantly different from AR from an archive
18  *   structural point of view.
19  *
20  * Modification history:
21  * Date      Programmer  Description of modification.
22  * 09/27/00  R. Jung     Added additional header data checks.
23  * 04/04/98  R. Jung     Added minor comments.
24  * 04/05/91  R. Jung     Rewrote code.
25  * 04/23/91  M. Adler    Portabilized.
26  * 04/29/91  R. Jung     Added l command.  Removed 16 bit dependency in
27  *                       fillbuf().
28  * 05/19/91  R. Jung     Fixed extended header skipping code.
29  * 05/25/91  R. Jung     Improved find_header().
30  * 06/03/91  R. Jung     Changed arguments in get_mode_str() and
31  *                       set_ftime_mode().
32  * 06/19/81  R. Jung     Added two more %c in printf() in list_arc().
33  * 07/07/91  R. Jung     Added default_case_path() to extract().
34  *                       Added strlower().
35  * 07/20/91  R. Jung     Changed uint ratio() to static uint ratio().
36  * 07/21/91  R. Jung     Added #ifdef VMS.
37  * 08/28/91  R. Jung     Changed M_DIFFHOST message.
38  * 08/31/91  R. Jung     Added changes to support MAC THINK_C compiler
39  *                       per Eric Larson.
40  * 10/07/91  R. Jung     Added missing ; to THINK_C additions.
41  * 11/11/91  R. Jung     Added host_os test to fwrite_txt_crc().
42  * 11/24/91  R. Jung     Added more error_count processing.
43  * 12/03/91  R. Jung     Added backup file processing.
44  * 02/17/93  R. Jung     Added archive modified date support.
45  * 01/22/94  R. Jung     Changed copyright message.
46  * 07/29/96  R. Jung     Added "/" to list of path separators.
47  * 06/05/02  R. Jung     Changed version number.
48  *
49  */
50 
51 #include "unarj.h"
52 
53 #ifdef MODERN
54 #include <stdlib.h>
55 #include <string.h>
56 #include <ctype.h>
57 #include <sys/stat.h>
58 #include <sys/types.h>
59 #include <fcntl.h>
60 #include <unistd.h>
61 #else /* !MODERN */
62 extern void free();
63 extern void exit();
64 extern char *strcat();
65 extern char *strcpy();
66 extern char *strncpy();
67 extern char *strchr();
68 extern char *strrchr();
69 extern int strlen();
70 extern int strcmp();
71 #ifdef VMS
72 #include <ssdef.h>
73 #define EXIT_FAILURE SS$_ABORT
74 #define EXIT_SUCCESS SS$_NORMAL
75 #else
76 #define EXIT_FAILURE (1)
77 #define EXIT_SUCCESS (0)
78 #endif
79 #define toupper(c)   ((c)>='a'&&(c)<='z'?(c)-('a'-'A'):(c))
80 #define tolower(c)   ((c)>='A'&&(c)<='Z'?(c)+('a'-'A'):(c))
81 #endif /* ?MODERN */
82 
83 #ifdef THINK_C
84 #include <console.h>
85 #endif
86 
87 /* Global variables */
88 
89 UCRC   crc;
90 FILE   *arcfile;
91 FILE   *outfile;
92 ushort bitbuf;
93 long   compsize;
94 long   origsize;
95 uchar  subbitbuf;
96 uchar  header[HEADERSIZE_MAX];
97 char   arc_name[FNAME_MAX];
98 int    command;
99 int    bitcount;
100 int    file_type;
101 int    no_output;
102 int    error_count;
103 
104 /* Messages */
105 
106 static char *M_USAGE  [] =
107 {
108 "Usage:  UNARJ archive[.arj]    (list archive)\n",
109 "        UNARJ e archive        (extract archive)\n",
110 "        UNARJ l archive        (list archive)\n",
111 "        UNARJ t archive        (test archive)\n",
112 "        UNARJ x archive        (extract with pathnames)\n",
113 "\n",
114 "This is an ARJ demonstration program and ** IS NOT OPTIMIZED ** for speed.\n",
115 "You may freely use, copy and distribute this program, provided that no fee\n",
116 "is charged for such use, copying or distribution, and it is distributed\n",
117 "ONLY in its original unmodified state.  UNARJ is provided as is without\n",
118 "warranty of any kind, express or implied, including but not limited to\n",
119 "the implied warranties of merchantability and fitness for a particular\n",
120 "purpose.  Refer to UNARJ.DOC for more warranty information.\n",
121 "\n",
122 "ARJ Software, Inc.      Internet address:  robjung@world.std.com\n",
123 "P.O. Box 249                    Web site:  www.arjsoftware.com\n",
124 "Norwood MA 02062\n",
125 "USA\n",
126 NULL
127 };
128 
129 char M_VERSION [] = "UNARJ (Demo version) 2.65 Copyright (c) 1991-2002 ARJ Software, Inc.\n\n";
130 
131 char M_ARCDATE [] = "Archive created: %s";
132 char M_ARCDATEM[] = ", modified: %s";
133 char M_BADCOMND[] = "Bad UNARJ command: %s";
134 char M_BADCOMNT[] = "Invalid comment header";
135 char M_BADHEADR[] = "Bad header";
136 char M_BADTABLE[] = "Bad file data";
137 char M_CANTOPEN[] = "Can't open %s";
138 char M_CANTREAD[] = "Can't read file or unexpected end of file";
139 char M_CANTWRIT[] = "Can't write file. Disk full?";
140 char M_CRCERROR[] = "CRC error!\n";
141 char M_CRCOK   [] = "CRC OK\n";
142 char M_DIFFHOST[] = "  Binary file!";
143 char M_ENCRYPT [] = "File is password encrypted, ";
144 char M_ERRORCNT[] = "%sFound %5d error(s)!";
145 char M_EXTRACT [] = "Extracting %-25s";
146 char M_FEXISTS [] = "%-25s exists, ";
147 char M_HEADRCRC[] = "Header CRC error!";
148 char M_NBRFILES[] = "%5d file(s)\n";
149 char M_NOMEMORY[] = "Out of memory";
150 char M_NOTARJ  [] = "%s is not an ARJ archive";
151 char M_PROCARC [] = "Processing archive: %s\n";
152 char M_SKIPPED [] = "Skipped %s\n";
153 char M_SUFFIX  [] = ARJ_SUFFIX;
154 char M_TESTING [] = "Testing    %-25s";
155 char M_UNKNMETH[] = "Unsupported method: %d, ";
156 char M_UNKNTYPE[] = "Unsupported file type: %d, ";
157 char M_UNKNVERS[] = "Unsupported version: %d, ";
158 
159 #define get_crc()       get_longword()
160 #define fget_crc(f)     fget_longword(f)
161 
162 #define setup_get(PTR)  (get_ptr = (PTR))
163 #define get_byte()      ((uchar)(*get_ptr++ & 0xff))
164 
165 #define BUFFERSIZE      4096
166 
167 #define ASCII_MASK      0x7F
168 
169 #define CRCPOLY         0xEDB88320L
170 
171 #define UPDATE_CRC(r,c) r=crctable[((uchar)(r)^(uchar)(c))&0xff]^(r>>CHAR_BIT)
172 
173 /* Local functions */
174 
175 #ifdef MODERN
176 static void  make_crctable(void);
177 static void  crc_buf(char *str, int len);
178 static void  strparity(uchar *p);
179 static FILE  *fopen_msg(char *name, char *mode);
180 static int   fget_byte(FILE *f);
181 static uint  fget_word(FILE *f);
182 static ulong fget_longword(FILE *f);
183 static void  fread_crc(uchar *p, int n, FILE *f);
184 static void  decode_path(char *name);
185 static void  get_date_str(char *str, ulong tstamp);
186 static int   parse_path(char *pathname, char *path, char *entry);
187 static void  strncopy(char *to, char *from, int len);
188 static uint  get_word(void);
189 static ulong get_longword(void);
190 static long  find_header(FILE *fd);
191 static int   read_header(int first, FILE *fd, char *name);
192 static void  skip(void);
193 static void  unstore(void);
194 static int   check_flags(void);
195 static int   extract(void);
196 static int   test(void);
197 static uint  ratio(long a, long b);
198 static void  list_start(void);
199 static void  list_arc(int count);
200 static void  execute_cmd(void);
201 static void  help(void);
202 #endif /* MODERN */
203 
204 /* Local variables */
205 
206 static char   filename[FNAME_MAX];
207 static char   comment[COMMENT_MAX];
208 static char   *hdr_filename;
209 static char   *hdr_comment;
210 
211 static ushort headersize;
212 static uchar  first_hdr_size;
213 static uchar  arj_nbr;
214 static uchar  arj_x_nbr;
215 static uchar  host_os;
216 static uchar  arj_flags;
217 static short  method;
218 static uint   file_mode;
219 static ulong  time_stamp;
220 static ushort entry_pos;
221 static ushort host_data;
222 static uchar  *get_ptr;
223 static UCRC   file_crc;
224 static UCRC   header_crc;
225 
226 static long   first_hdr_pos;
227 static long   torigsize;
228 static long   tcompsize;
229 
230 static int    clock_inx;
231 
232 static char   *writemode[2]  = { "wb",  "w" };
233 
234 static UCRC   crctable[UCHAR_MAX + 1];
235 
236 /* Functions */
237 
238 void copy_path_relative(char *dest, char *src, size_t len);
239 
240 static void
make_crctable()241 make_crctable()
242 {
243     uint i, j;
244     UCRC r;
245 
246     for (i = 0; i <= UCHAR_MAX; i++)
247     {
248         r = i;
249         for (j = CHAR_BIT; j > 0; j--)
250         {
251             if (r & 1)
252                 r = (r >> 1) ^ CRCPOLY;
253             else
254                 r >>= 1;
255         }
256         crctable[i] = r;
257     }
258 }
259 
260 static void
crc_buf(str,len)261 crc_buf(str, len)
262 char *str;
263 int  len;
264 {
265     while (len--)
266         UPDATE_CRC(crc, *str++);
267 }
268 
269 void
disp_clock()270 disp_clock()
271 {
272     static char clock_str[4] = { '|', '/', '-', '\\' };
273 
274     printf("(%c)\b\b\b", clock_str[clock_inx]);
275     clock_inx = (clock_inx + 1) & 0x03;
276 }
277 
278 void
error(fmt,arg)279 error(fmt, arg)
280 char *fmt;
281 char *arg;
282 {
283     putc('\n', stdout);
284     printf(fmt, arg, error_count);
285     putc('\n', stdout);
286     exit(EXIT_FAILURE);
287 }
288 
289 static void
strparity(p)290 strparity(p)
291 uchar *p;
292 {
293     while (*p)
294     {
295         FIX_PARITY(*p);
296         p++;
297     }
298 }
299 
300 static FILE *
fopen_msg(name,mode)301 fopen_msg(name, mode)
302 char *name;
303 char *mode;
304 {
305     FILE *fd;
306 
307     fd = file_open(name, mode);
308     if (fd == NULL)
309         error(M_CANTOPEN, name);
310     return fd;
311 }
312 
313 static int
fget_byte(f)314 fget_byte(f)
315 FILE *f;
316 {
317     int c;
318 
319     if ((c = getc(f)) == EOF)
320         error(M_CANTREAD, "");
321     return c & 0xFF;
322 }
323 
324 static uint
fget_word(f)325 fget_word(f)
326 FILE *f;
327 {
328     uint b0, b1;
329 
330     b0 = fget_byte(f);
331     b1 = fget_byte(f);
332     return (b1 << 8) + b0;
333 }
334 
335 static ulong
fget_longword(f)336 fget_longword(f)
337 FILE *f;
338 {
339     ulong b0, b1, b2, b3;
340 
341     b0 = fget_byte(f);
342     b1 = fget_byte(f);
343     b2 = fget_byte(f);
344     b3 = fget_byte(f);
345     return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
346 }
347 
348 static void
fread_crc(p,n,f)349 fread_crc(p, n, f)
350 uchar *p;
351 int   n;
352 FILE  *f;
353 {
354     n = file_read((char *)p, 1, n, f);
355     origsize += n;
356     crc_buf((char *)p, n);
357 }
358 
359 void
fwrite_txt_crc(p,n)360 fwrite_txt_crc(p, n)
361 uchar *p;
362 int   n;
363 {
364     uchar c;
365 
366     crc_buf((char *)p, n);
367     if (no_output)
368         return;
369 
370     if (file_type == TEXT_TYPE)
371     {
372         while (n--)
373         {
374             c = *p++;
375             if (host_os != OS)
376             {
377                 FIX_PARITY(c);
378             }
379             if (putc((int) c, outfile) == EOF)
380                 error(M_CANTWRIT, "");
381         }
382     }
383     else
384     {
385         if (file_write((char *)p, 1, n, outfile) != n)
386             error(M_CANTWRIT, "");
387     }
388 }
389 
390 void
init_getbits()391 init_getbits()
392 {
393     bitbuf = 0;
394     subbitbuf = 0;
395     bitcount = 0;
396     fillbuf(2 * CHAR_BIT);
397 }
398 
399 void
fillbuf(n)400 fillbuf(n)                /* Shift bitbuf n bits left, read n bits */
401 int n;
402 {
403     bitbuf = (bitbuf << n) & 0xFFFF;  /* lose the first n bits */
404     while (n > bitcount)
405     {
406         bitbuf |= subbitbuf << (n -= bitcount);
407         if (compsize != 0)
408         {
409             compsize--;
410             subbitbuf = (uchar) getc(arcfile);
411         }
412         else
413             subbitbuf = 0;
414         bitcount = CHAR_BIT;
415     }
416     bitbuf |= subbitbuf >> (bitcount -= n);
417 }
418 
419 ushort
getbits(n)420 getbits(n)
421 int n;
422 {
423     ushort x;
424 
425     x = bitbuf >> (2 * CHAR_BIT - n);
426     fillbuf(n);
427     return x;
428 }
429 
430 static void
decode_path(name)431 decode_path(name)
432 char *name;
433 {
434     for ( ; *name; name++)
435     {
436         if (*name == ARJ_PATH_CHAR)
437             *name = PATH_CHAR;
438     }
439 }
440 
441 static void
get_date_str(str,tstamp)442 get_date_str(str, tstamp)
443 char  *str;
444 ulong tstamp;
445 {
446     sprintf(str, "%04u-%02u-%02u %02u:%02u:%02u",
447            ts_year(tstamp), ts_month(tstamp), ts_day(tstamp),
448            ts_hour(tstamp), ts_min(tstamp), ts_sec(tstamp));
449 }
450 
451 static int
parse_path(pathname,path,entry)452 parse_path(pathname, path, entry)
453 char *pathname;
454 char *path;
455 char *entry;
456 {
457     char *cptr, *ptr, *fptr;
458     short pos;
459 
460     fptr = NULL;
461     for (cptr = PATH_SEPARATORS; *cptr; cptr++)
462     {
463         if ((ptr = strrchr(pathname, *cptr)) != NULL &&
464                 (fptr == NULL || ptr > fptr))
465             fptr = ptr;
466     }
467     if (fptr == NULL)
468         pos = 0;
469     else
470         pos = fptr + 1 - pathname;
471     if (path != NULL)
472     {
473        strncpy(path, pathname, pos);
474        path[pos] = NULL_CHAR;
475     }
476     if (entry != NULL)
477        strcpy(entry, &pathname[pos]);
478     return pos;
479 }
480 
481 static void
strncopy(to,from,len)482 strncopy(to, from, len)
483 char *to;
484 char *from;
485 int  len;
486 {
487     int i;
488 
489     for (i = 1; i < len && *from; i++)
490         *to++ = *from++;
491     *to = NULL_CHAR;
492 }
493 
494 void
strlower(s)495 strlower(s)
496 char *s;
497 {
498     while (*s)
499     {
500         *s = (char) tolower(*s);
501         s++;
502     }
503 }
504 
505 void
strupper(s)506 strupper(s)
507 char *s;
508 {
509     while (*s)
510     {
511         *s = (char) toupper(*s);
512         s++;
513     }
514 }
515 
516 voidp *
malloc_msg(size)517 malloc_msg(size)
518 int size;
519 {
520     char *p;
521 
522     if ((p = (char *)xmalloc(size)) == NULL)
523         error(M_NOMEMORY, "");
524     return (voidp *)p;
525 }
526 
527 static uint
get_word()528 get_word()
529 {
530     uint b0, b1;
531 
532     b0 = get_byte();
533     b1 = get_byte();
534     return (b1 << 8) + b0;
535 }
536 
537 static ulong
get_longword()538 get_longword()
539 {
540     ulong b0, b1, b2, b3;
541 
542     b0 = get_byte();
543     b1 = get_byte();
544     b2 = get_byte();
545     b3 = get_byte();
546     return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
547 }
548 
549 static long
find_header(fd)550 find_header(fd)
551 FILE *fd;
552 {
553     long arcpos, lastpos;
554     int c;
555 
556     arcpos = file_tell(fd);
557     file_seek(fd, 0L, SEEK_END);
558     lastpos = file_tell(fd) - 2;
559     if (lastpos > MAXSFX)
560         lastpos = MAXSFX;
561     for ( ; arcpos < lastpos; arcpos++)
562     {
563         file_seek(fd, arcpos, SEEK_SET);
564         c = fget_byte(fd);
565         while (arcpos < lastpos)
566         {
567             if (c != HEADER_ID_LO)  /* low order first */
568                 c = fget_byte(fd);
569             else if ((c = fget_byte(fd)) == HEADER_ID_HI)
570                 break;
571             arcpos++;
572         }
573         if (arcpos >= lastpos)
574             break;
575         if ((headersize = fget_word(fd)) <= HEADERSIZE_MAX)
576         {
577             crc = CRC_MASK;
578             fread_crc(header, (int) headersize, fd);
579             if ((crc ^ CRC_MASK) == fget_crc(fd))
580             {
581                 file_seek(fd, arcpos, SEEK_SET);
582                 return arcpos;
583             }
584         }
585     }
586     return -1;          /* could not find a valid header */
587 }
588 
589 static int
read_header(first,fd,name)590 read_header(first, fd, name)
591 int  first;
592 FILE *fd;
593 char *name;
594 {
595     ushort extheadersize, header_id;
596 
597     header_id = fget_word(fd);
598     if (header_id != HEADER_ID)
599     {
600         if (first)
601             error(M_NOTARJ, name);
602         else
603             error(M_BADHEADR, "");
604     }
605 
606     headersize = fget_word(fd);
607     if (headersize == 0)
608         return 0;               /* end of archive */
609     if (headersize > HEADERSIZE_MAX)
610         error(M_BADHEADR, "");
611 
612     crc = CRC_MASK;
613     memset(header, 0, sizeof(header));
614     fread_crc(header, (int) headersize, fd);
615     header_crc = fget_crc(fd);
616     if ((crc ^ CRC_MASK) != header_crc)
617         error(M_HEADRCRC, "");
618 
619     setup_get(header);
620     first_hdr_size = get_byte();
621     arj_nbr = get_byte();
622     arj_x_nbr = get_byte();
623     host_os = get_byte();
624     arj_flags = get_byte();
625     method = get_byte();
626     file_type = get_byte();
627     (void)get_byte();
628     time_stamp = get_longword();
629     compsize = get_longword();
630     origsize = get_longword();
631     file_crc = get_crc();
632     entry_pos = get_word();
633     file_mode = get_word();
634     host_data = get_word();
635 
636     if (origsize < 0 || compsize < 0)
637         error(M_HEADRCRC, "");
638     if(first_hdr_size > headersize-2) /* need two \0 for file and comment */
639         error(M_BADHEADR, "");
640 
641     hdr_filename = (char *)&header[first_hdr_size];
642     strncopy(filename, hdr_filename, sizeof(filename));
643     if(entry_pos >= strlen(filename))
644         error(M_BADHEADR, "");
645     if (host_os != OS)
646         strparity((uchar *)filename);
647     if ((arj_flags & PATHSYM_FLAG) != 0)
648         decode_path(filename);
649 
650     hdr_comment = (char *)&header[first_hdr_size + strlen(hdr_filename) + 1];
651     strncopy(comment, hdr_comment, sizeof(comment));
652     if (host_os != OS)
653         strparity((uchar *)comment);
654 
655     /* if extheadersize == 0 then no CRC */
656     /* otherwise read extheader data and read 4 bytes for CRC */
657 
658     while ((extheadersize = fget_word(fd)) != 0)
659         file_seek(fd, (long) (extheadersize + 4), SEEK_CUR);
660 
661     return 1;                   /* success */
662 }
663 
664 static void
skip()665 skip()
666 {
667     file_seek(arcfile, compsize, SEEK_CUR);
668 }
669 
670 static void
unstore()671 unstore()
672 {
673     int n;
674     long pos;
675     char *buffer;
676 
677     buffer = (char *)malloc_msg(BUFFERSIZE);
678     pos = file_tell(arcfile);
679     disp_clock();
680     n = (int)(BUFFERSIZE - (pos % BUFFERSIZE));
681     n = compsize > (long)n ? n : (int)compsize;
682     while (compsize > 0)
683     {
684         if (file_read(buffer, 1, n, arcfile) != n)
685             error(M_CANTREAD, "");
686         disp_clock();
687         compsize -= n;
688         fwrite_txt_crc((uchar *)buffer, n);
689         n = compsize > BUFFERSIZE ? BUFFERSIZE : (int)compsize;
690     }
691     free(buffer);
692 }
693 
694 static int
check_flags()695 check_flags()
696 {
697     if (arj_x_nbr > ARJ_X_VERSION)
698     {
699         printf(M_UNKNVERS, arj_x_nbr);
700         printf(M_SKIPPED, filename);
701         skip();
702         return -1;
703     }
704     if ((arj_flags & GARBLE_FLAG) != 0)
705     {
706         printf(M_ENCRYPT);
707         printf(M_SKIPPED, filename);
708         skip();
709         return -1;
710     }
711     if (method < 0 || method > MAXMETHOD || (method == 4 && arj_nbr == 1))
712     {
713         printf(M_UNKNMETH, method);
714         printf(M_SKIPPED, filename);
715         skip();
716         return -1;
717     }
718     if (file_type != BINARY_TYPE && file_type != TEXT_TYPE)
719     {
720         printf(M_UNKNTYPE, file_type);
721         printf(M_SKIPPED, filename);
722         skip();
723         return -1;
724     }
725     return 0;
726 }
727 
728 static int
extract()729 extract()
730 {
731     char name[FNAME_MAX];
732     char dir[FNAME_MAX];
733     char *pos;
734 
735     if (check_flags())
736     {
737         error_count++;
738         return 0;
739     }
740 
741     no_output = 0;
742     if (command == 'E')
743         copy_path_relative(name, &filename[entry_pos], sizeof(name));
744     else
745     {
746         strcpy(name, DEFAULT_DIR);
747         copy_path_relative(name+strlen(name), filename, sizeof(name)-strlen(name));
748     }
749 
750     if (host_os != OS)
751         default_case_path(name);
752 
753 
754     /*
755        8/8/2000 Phil Knirsch: Bugfix to create subdirectories. Unarj didn't
756        do this for a long time, so it's finally fixed.
757     */
758     pos = strchr(name, PATH_CHAR);
759 
760     while (pos != NULL)
761     {
762         strncpy(dir, name, pos-name);
763         dir[pos-name] = '\0';
764         mkdir(dir, 0777);
765         pos = strchr(pos+1, PATH_CHAR);
766     }
767 
768     if (file_exists(name))
769     {
770         printf(M_FEXISTS, name);
771         printf(M_SKIPPED, name);
772         skip();
773         error_count++;
774         return 0;
775     }
776     outfile = file_open(name, writemode[file_type & 1]);
777     if (outfile == NULL)
778     {
779         printf(M_CANTOPEN, name);
780         putchar('\n');
781         skip();
782         error_count++;
783         return 0;
784     }
785     printf(M_EXTRACT, name);
786     if (host_os != OS && file_type == BINARY_TYPE)
787         printf(M_DIFFHOST);
788     printf("  ");
789 
790     crc = CRC_MASK;
791 
792     if (method == 0)
793         unstore();
794     else if (method == 1 || method == 2 || method == 3)
795         decode();
796     else if (method == 4)
797         decode_f();
798     fclose(outfile);
799 
800     set_ftime_mode(name, time_stamp, file_mode, (uint) host_os);
801 
802     if ((crc ^ CRC_MASK) == file_crc)
803         printf(M_CRCOK);
804     else
805     {
806         printf(M_CRCERROR);
807         error_count++;
808     }
809     return 1;
810 }
811 
812 static int
test()813 test()
814 {
815     if (check_flags())
816         return 0;
817 
818     no_output = 1;
819     printf(M_TESTING, filename);
820     printf("  ");
821 
822     crc = CRC_MASK;
823 
824     if (method == 0)
825         unstore();
826     else if (method == 1 || method == 2 || method == 3)
827         decode();
828     else if (method == 4)
829         decode_f();
830 
831     if ((crc ^ CRC_MASK) == file_crc)
832         printf(M_CRCOK);
833     else
834     {
835         printf(M_CRCERROR);
836         error_count++;
837     }
838     return 1;
839 }
840 
841 static uint
ratio(a,b)842 ratio(a, b)
843 long a, b;
844 {
845    int i;
846 
847    for (i = 0; i < 3; i++)
848        if (a <= LONG_MAX / 10)
849            a *= 10;
850        else
851            b /= 10;
852    if ((long) (a + (b >> 1)) < a)
853    {
854        a >>= 1;
855        b >>= 1;
856    }
857    if (b == 0)
858        return 0;
859    return (uint) ((a + (b >> 1)) / b);
860 }
861 
862 static void
list_start()863 list_start()
864 {
865     printf("Filename       Original Compressed Ratio DateTime modified CRC-32   AttrBTPMGVX\n");
866     printf("------------ ---------- ---------- ----- ----------------- -------- -----------\n");
867 }
868 
869 static void
list_arc(count)870 list_arc(count)
871 int count;
872 {
873     uint r;
874     int garble_mode, path_mode, volume_mode, extfil_mode, ftype, bckf_mode;
875     char date_str[20], fmode_str[10];
876     static char mode[5] = { 'B', 'T', '?', 'D', 'V' };
877     static char pthf[2] = { ' ', '+' };
878     static char pwdf[2] = { ' ', 'G' };  /* plain, encrypted */
879     static char volf[2] = { ' ', 'V' };
880     static char extf[2] = { ' ', 'X' };
881     static char bckf[2] = { ' ', '*' };
882 
883     if (count == 0)
884         list_start();
885 
886     garble_mode = ((arj_flags & GARBLE_FLAG) != 0);
887     volume_mode = ((arj_flags & VOLUME_FLAG) != 0);
888     extfil_mode = ((arj_flags & EXTFILE_FLAG) != 0);
889     bckf_mode   = ((arj_flags & BACKUP_FLAG) != 0);
890     path_mode   = (entry_pos > 0);
891     r = ratio(compsize, origsize);
892     torigsize += origsize;
893     tcompsize += compsize;
894     ftype = file_type;
895     if (ftype != BINARY_TYPE && ftype != TEXT_TYPE && ftype != DIR_TYPE &&
896             ftype != LABEL_TYPE)
897         ftype = 3;
898     get_date_str(date_str, time_stamp);
899     strcpy(fmode_str, "    ");
900     if (host_os == OS)
901         get_mode_str(fmode_str, (uint) file_mode);
902     if (strlen(&filename[entry_pos]) > 12)
903         printf("%-12s\n             ", &filename[entry_pos]);
904     else
905         printf("%-12s ", &filename[entry_pos]);
906     printf("%10ld %10ld %u.%03u %s %08lX %4s%c%c%c%u%c%c%c\n",
907         origsize, compsize, r / 1000, r % 1000, &date_str[2], file_crc,
908         fmode_str, bckf[bckf_mode], mode[ftype], pthf[path_mode], method,
909         pwdf[garble_mode], volf[volume_mode], extf[extfil_mode]);
910 }
911 
912 static void
execute_cmd()913 execute_cmd()
914 {
915     int file_count;
916     char date_str[22];
917     uint r;
918 
919     first_hdr_pos = 0;
920     time_stamp = 0;
921     first_hdr_size = FIRST_HDR_SIZE;
922 
923     arcfile = fopen_msg(arc_name, "rb");
924 
925     printf(M_PROCARC, arc_name);
926 
927     first_hdr_pos = find_header(arcfile);
928     if (first_hdr_pos < 0)
929         error(M_NOTARJ, arc_name);
930     file_seek(arcfile, first_hdr_pos, SEEK_SET);
931     if (!read_header(1, arcfile, arc_name))
932         error(M_BADCOMNT, "");
933     get_date_str(date_str, time_stamp);
934     printf(M_ARCDATE, date_str);
935     if (arj_nbr >= ARJ_M_VERSION)
936     {
937         get_date_str(date_str, (ulong) compsize);
938         printf(M_ARCDATEM, date_str);
939     }
940     printf("\n");
941 
942     file_count = 0;
943     while (read_header(0, arcfile, arc_name))
944     {
945         switch (command)
946         {
947         case 'E':
948         case 'X':
949             if (extract())
950                 file_count++;
951             break;
952         case 'L':
953             list_arc(file_count++);
954             skip();
955             break;
956         case 'T':
957             if (test())
958                 file_count++;
959             break;
960         }
961     }
962 
963     if (command == 'L')
964     {
965         printf("------------ ---------- ---------- ----- -----------------\n");
966         r = ratio(tcompsize, torigsize);
967         printf(" %5d files %10ld %10ld %u.%03u %s\n",
968             file_count, torigsize, tcompsize, r / 1000, r % 1000, &date_str[2]);
969     }
970     else
971         printf(M_NBRFILES, file_count);
972 
973     fclose(arcfile);
974 }
975 
976 static void
help()977 help()
978 {
979     int i;
980 
981     for (i = 0; M_USAGE[i] != NULL; i++)
982         printf(M_USAGE[i]);
983 }
984 
985 int
main(argc,argv)986 main(argc, argv)
987 int  argc;
988 char *argv[];
989 {
990     int i, j, lastc;
991     char *arc_p;
992 
993 #ifdef THINK_C
994     argc = ccommand(&argv);
995 #endif
996 
997     printf(M_VERSION);
998 
999     if (argc == 1)
1000     {
1001         help();
1002         return EXIT_SUCCESS;
1003     }
1004     else if (argc == 2)
1005     {
1006         command = 'L';
1007         arc_p = argv[1];
1008     }
1009     else if (argc == 3)
1010     {
1011         if (strlen(argv[1]) > 1)
1012             error(M_BADCOMND, argv[1]);
1013         command = toupper(*argv[1]);
1014         if (strchr("ELTX", command) == NULL)
1015             error(M_BADCOMND, argv[1]);
1016         arc_p = argv[2];
1017     }
1018     else
1019     {
1020         help();
1021         return EXIT_FAILURE;
1022     }
1023 
1024     strncopy(arc_name, arc_p, FNAME_MAX);
1025     case_path(arc_name);
1026     i = strlen(arc_name);
1027     j = parse_path(arc_name, (char *)NULL, (char *)NULL);
1028     lastc = arc_name[i - 1];
1029     if (lastc == ARJ_DOT)
1030         arc_name[i - 1] = NULL_CHAR;
1031     else if (strchr(&arc_name[j], ARJ_DOT) == NULL)
1032         strcat(arc_name, M_SUFFIX);
1033 
1034     make_crctable();
1035 
1036     error_count = 0;
1037     clock_inx = 0;
1038     arcfile = NULL;
1039     outfile = NULL;
1040 
1041     execute_cmd();
1042 
1043     if (error_count > 0)
1044         error(M_ERRORCNT, "");
1045 
1046     return EXIT_SUCCESS;
1047 }
1048 
1049 /* end UNARJ.C */
1050