OSDN Git Service

fb0d988264559404ea44dce6e5c20083feb4ebb1
[lha/olha.git] / ar.c
1 /***********************************************************\r
2         ar.c -- main file\r
3 ***********************************************************/\r
4 \r
5 static char *usage =\r
6         "ar -- compression archiver -- written by Haruhiko Okumura\n"\r
7         "  PC-VAN:SCIENCE        CompuServe:74050,1022\n"\r
8         "  NIFTY-Serve:PAF01022  INTERNET:74050.1022@compuserve.com\n"\r
9         "Usage: ar command archive [file ...]\n"\r
10         "Commands:\n"\r
11         "   a: Add files to archive (replace if present)\n"\r
12         "   x: Extract files from archive\n"\r
13         "   r: Replace files in archive\n"\r
14         "   d: Delete files from archive\n"\r
15         "   p: Print files on standard output\n"\r
16         "   l: List contents of archive\n"\r
17         "If no files are named, all files in archive are processed,\n"\r
18         "   except for commands 'a' and 'd'.\n"\r
19         "You may copy, distribute, and rewrite this program freely.\n";\r
20 \r
21 /***********************************************************\r
22 \r
23 Structure of archive block (low order byte first):\r
24 -----preheader\r
25  1      basic header size\r
26                 = 25 + strlen(filename) (= 0 if end of archive)\r
27  1      basic header algebraic sum (mod 256)\r
28 -----basic header\r
29  5      method ("-lh0-" = stored, "-lh5-" = compressed)\r
30  4      compressed size (including extended headers)\r
31  4      original size\r
32  4      not used\r
33  1      0x20\r
34  1      0x01\r
35  1      filename length (x)\r
36  x      filename\r
37  2      original file's CRC\r
38  1      0x20\r
39  2      first extended header size (0 if none)\r
40 -----first extended header, etc.\r
41 -----compressed file\r
42 \r
43 ***********************************************************/\r
44 \r
45 #include <stdlib.h>\r
46 #include <string.h>\r
47 #include <ctype.h>\r
48 #include "ar.h"\r
49 \r
50 #define FNAME_MAX (255 - 25) /* max strlen(filename) */\r
51 #define namelen  header[19]\r
52 #define filename ((char *)&header[20])\r
53 \r
54 int unpackable;            /* global, set in io.c */\r
55 ulong compsize, origsize;  /* global */\r
56 \r
57 static uchar buffer[DICSIZ];\r
58 static uchar header[255];\r
59 static uchar headersize, headersum;\r
60 static uint  file_crc;\r
61 static char  *temp_name;\r
62 \r
63 static uint ratio(ulong a, ulong b)  /* [(1000a + [b/2]) / b] */\r
64 {\r
65         int i;\r
66 \r
67         for (i = 0; i < 3; i++)\r
68                 if (a <= ULONG_MAX / 10) a *= 10;  else b /= 10;\r
69         if ((ulong)(a + (b >> 1)) < a) {  a >>= 1;  b >>= 1;  }\r
70         if (b == 0) return 0;\r
71         return (uint)((a + (b >> 1)) / b);\r
72 }\r
73 \r
74 static void put_to_header(int i, int n, ulong x)\r
75 {\r
76         while (--n >= 0) {\r
77                 header[i++] = (uchar)((uint)x & 0xFF);  x >>= 8;\r
78         }\r
79 }\r
80 \r
81 static ulong get_from_header(int i, int n)\r
82 {\r
83         ulong s;\r
84 \r
85         s = 0;\r
86         while (--n >= 0) s = (s << 8) + header[i + n];  /* little endian */\r
87         return s;\r
88 }\r
89 \r
90 static uint calc_headersum(void)\r
91 {\r
92         int i;\r
93         uint s;\r
94 \r
95         s = 0;\r
96         for (i = 0; i < headersize; i++) s += header[i];\r
97         return s & 0xFF;\r
98 }\r
99 \r
100 static int read_header(void)\r
101 {\r
102         headersize = (uchar) fgetc(arcfile);\r
103         if (headersize == 0) return 0;  /* end of archive */\r
104         headersum  = (uchar) fgetc(arcfile);\r
105         fread_crc(header, headersize, arcfile);  /* CRC not used */\r
106         if (calc_headersum() != headersum) error("Header sum error");\r
107         compsize = get_from_header(5, 4);\r
108         origsize = get_from_header(9, 4);\r
109         file_crc = (uint)get_from_header(headersize - 5, 2);\r
110         filename[namelen] = '\0';\r
111         return 1;  /* success */\r
112 }\r
113 \r
114 static void write_header(void)\r
115 {\r
116         fputc(headersize, outfile);\r
117         /* We've destroyed file_crc by null-terminating filename. */\r
118         put_to_header(headersize - 5, 2, (ulong)file_crc);\r
119         fputc(calc_headersum(), outfile);\r
120         fwrite_crc(header, headersize, outfile);  /* CRC not used */\r
121 }\r
122 \r
123 static void skip(void)\r
124 {\r
125         fseek(arcfile, compsize, SEEK_CUR);\r
126 }\r
127 \r
128 static void copy(void)\r
129 {\r
130         uint n;\r
131 \r
132         write_header();\r
133         while (compsize != 0) {\r
134                 n = (uint)((compsize > DICSIZ) ? DICSIZ : compsize);\r
135                 if (fread ((char *)buffer, 1, n, arcfile) != n)\r
136                         error("Can't read");\r
137                 if (fwrite((char *)buffer, 1, n, outfile) != n)\r
138                         error("Can't write");\r
139                 compsize -= n;\r
140         }\r
141 }\r
142 \r
143 static void store(void)\r
144 {\r
145         uint n;\r
146 \r
147         origsize = 0;\r
148         crc = INIT_CRC;\r
149         while ((n = fread((char *)buffer, 1, DICSIZ, infile)) != 0) {\r
150                 fwrite_crc(buffer, n, outfile);  origsize += n;\r
151         }\r
152         compsize = origsize;\r
153 }\r
154 \r
155 static int add(int replace_flag)\r
156 {\r
157         long headerpos, arcpos;\r
158         uint r;\r
159 \r
160         if ((infile = fopen(filename, "rb")) == NULL) {\r
161                 fprintf(stderr, "Can't open %s\n", filename);\r
162                 return 0;  /* failure */\r
163         }\r
164         if (replace_flag) {\r
165                 printf("Replacing %s ", filename);  skip();\r
166         } else\r
167                 printf("Adding %s ", filename);\r
168         headerpos = ftell(outfile);\r
169         namelen = strlen(filename);\r
170         headersize = 25 + namelen;\r
171         memcpy(header, "-lh5-", 5);  /* compress */\r
172         write_header();  /* temporarily */\r
173         arcpos = ftell(outfile);\r
174         origsize = compsize = 0;  unpackable = 0;\r
175         crc = INIT_CRC;  encode();\r
176         if (unpackable) {\r
177                 header[3] = '0';  /* store */\r
178                 rewind(infile);\r
179                 fseek(outfile, arcpos, SEEK_SET);\r
180                 store();\r
181         }\r
182         file_crc = crc ^ INIT_CRC;\r
183         fclose(infile);\r
184         put_to_header(5, 4, compsize);\r
185         put_to_header(9, 4, origsize);\r
186         memcpy(header + 13, "\0\0\0\0\x20\x01", 6);\r
187         memcpy(header + headersize - 3, "\x20\0\0", 3);\r
188         fseek(outfile, headerpos, SEEK_SET);\r
189         write_header();  /* true header */\r
190         fseek(outfile, 0L, SEEK_END);\r
191         r = ratio(compsize, origsize);\r
192         printf(" %d.%d%%\n", r / 10, r % 10);\r
193         return 1;  /* success */\r
194 }\r
195 \r
196 int get_line(char *s, int n)\r
197 {\r
198         int i, c;\r
199 \r
200         i = 0;\r
201         while ((c = getchar()) != EOF && c != '\n')\r
202                 if (i < n) s[i++] = (char)c;\r
203         s[i] = '\0';\r
204         return i;\r
205 }\r
206 \r
207 static void extract(int to_file)\r
208 {\r
209         int n, method;\r
210         uint ext_headersize;\r
211 \r
212         if (to_file) {\r
213                 while ((outfile = fopen(filename, "wb")) == NULL) {\r
214                         fprintf(stderr, "Can't open %s\nNew filename: ", filename);\r
215                         if (get_line(filename, FNAME_MAX) == 0) {\r
216                                 fprintf(stderr, "Not extracted\n");\r
217                                 skip();  return;\r
218                         }\r
219                         namelen = strlen(filename);\r
220                 }\r
221                 printf("Extracting %s ", filename);\r
222         } else {\r
223                 outfile = stdout;\r
224                 printf("===== %s =====\n", filename);\r
225         }\r
226         crc = INIT_CRC;\r
227         method = header[3];  header[3] = ' ';\r
228         if (! strchr("045", method) || memcmp("-lh -", header, 5)) {\r
229                 fprintf(stderr, "Unknown method: %u\n", method);\r
230                 skip();\r
231         } else {\r
232                 ext_headersize = (uint)get_from_header(headersize - 2, 2);\r
233                 while (ext_headersize != 0) {\r
234                         fprintf(stderr, "There's an extended header of size %u.\n",\r
235                                 ext_headersize);\r
236                         compsize -= ext_headersize;\r
237                         if (fseek(arcfile, ext_headersize - 2, SEEK_CUR))\r
238                                 error("Can't read");\r
239                         ext_headersize = fgetc(arcfile);\r
240                         ext_headersize += (uint)fgetc(arcfile) << 8;\r
241                 }\r
242                 crc = INIT_CRC;\r
243                 if (method != '0') decode_start();\r
244                 while (origsize != 0) {\r
245                         n = (uint)((origsize > DICSIZ) ? DICSIZ : origsize);\r
246                         if (method != '0') decode(n, buffer);\r
247                         else if (fread((char *)buffer, 1, n, arcfile) != n)\r
248                                 error("Can't read");\r
249                         fwrite_crc(buffer, n, outfile);\r
250                         if (outfile != stdout) putc('.', stderr);\r
251                         origsize -= n;\r
252                 }\r
253         }\r
254         if (to_file) fclose(outfile);  else outfile = NULL;\r
255         printf("\n");\r
256         if ((crc ^ INIT_CRC) != file_crc)\r
257                 fprintf(stderr, "CRC error\n");\r
258 }\r
259 \r
260 static void list_start(void)\r
261 {\r
262         printf("Filename         Original Compressed Ratio CRC Method\n");\r
263 }\r
264 \r
265 static void list(void)\r
266 {\r
267         uint r;\r
268 \r
269         printf("%-14s", filename);\r
270         if (namelen > 14) printf("\n              ");\r
271         r = ratio(compsize, origsize);\r
272         printf(" %10lu %10lu %u.%03u %04X %5.5s\n",\r
273                 origsize, compsize, r / 1000, r % 1000, file_crc, header);\r
274 }\r
275 \r
276 static int match(char *s1, char *s2)\r
277 {\r
278         for ( ; ; ) {\r
279                 while (*s2 == '*' || *s2 == '?') {\r
280                         if (*s2++ == '*')\r
281                                 while (*s1 && *s1 != *s2) s1++;\r
282                         else if (*s1 == 0)\r
283                                 return 0;\r
284                         else s1++;\r
285                 }\r
286                 if (*s1 != *s2) return 0;\r
287                 if (*s1 == 0  ) return 1;\r
288                 s1++;  s2++;\r
289         }\r
290 }\r
291 \r
292 static int search(int argc, char *argv[])\r
293 {\r
294         int i;\r
295 \r
296         if (argc == 3) return 1;\r
297         for (i = 3; i < argc; i++)\r
298                 if (match(filename, argv[i])) return 1;\r
299         return 0;\r
300 }\r
301 \r
302 static void exitfunc(void)\r
303 {\r
304         fclose(outfile);  remove(temp_name);\r
305 }\r
306 \r
307 int main(int argc, char *argv[])\r
308 {\r
309         int i, j, cmd, count, nfiles, found, done;\r
310 \r
311         /* Check command line arguments. */\r
312         if (argc < 3\r
313          || argv[1][1] != '\0'\r
314          || ! strchr("AXRDPL", cmd = toupper(argv[1][0]))\r
315          || (argc == 3 && strchr("AD", cmd)))\r
316                 error(usage);\r
317 \r
318         /* Wildcards used? */\r
319         for (i = 3; i < argc; i++)\r
320                 if (strpbrk(argv[i], "*?")) break;\r
321         if (cmd == 'A' && i < argc)\r
322                 error("Filenames may not contain '*' and '?'");\r
323         if (i < argc) nfiles = -1;  /* contains wildcards */\r
324         else nfiles = argc - 3;     /* number of files to process */\r
325 \r
326         /* Open archive. */\r
327         arcfile = fopen(argv[2], "rb");\r
328         if (arcfile == NULL && cmd != 'A')\r
329                 error("Can't open archive '%s'", argv[2]);\r
330 \r
331         /* Open temporary file. */\r
332         if (strchr("ARD", cmd)) {\r
333                 temp_name = tmpnam(NULL);\r
334                 outfile = fopen(temp_name, "wb");\r
335                 if (outfile == NULL)\r
336                         error("Can't open temporary file");\r
337                 atexit(exitfunc);\r
338         } else temp_name = NULL;\r
339 \r
340         make_crctable();  count = done = 0;\r
341 \r
342         if (cmd == 'A') {\r
343                 for (i = 3; i < argc; i++) {\r
344                         for (j = 3; j < i; j++)\r
345                                 if (strcmp(argv[j], argv[i]) == 0) break;\r
346                         if (j == i) {\r
347                                 strcpy(filename, argv[i]);\r
348                                 if (add(0)) count++;  else argv[i][0] = 0;\r
349                         } else nfiles--;\r
350                 }\r
351                 if (count == 0 || arcfile == NULL) done = 1;\r
352         }\r
353 \r
354         while (! done && read_header()) {\r
355                 found = search(argc, argv);\r
356                 switch (cmd) {\r
357                 case 'R':\r
358                         if (found) {\r
359                                 if (add(1)) count++;  else copy();\r
360                         } else copy();\r
361                         break;\r
362                 case 'A':  case 'D':\r
363                         if (found) {\r
364                                 count += (cmd == 'D');  skip();\r
365                         } else copy();\r
366                         break;\r
367                 case 'X':  case 'P':\r
368                         if (found) {\r
369                                 extract(cmd == 'X');\r
370                                 if (++count == nfiles) done = 1;\r
371                         } else skip();\r
372                         break;\r
373                 case 'L':\r
374                         if (found) {\r
375                                 if (count == 0) list_start();\r
376                                 list();\r
377                                 if (++count == nfiles) done = 1;\r
378                         }\r
379                         skip();  break;\r
380                 }\r
381         }\r
382 \r
383         if (temp_name != NULL && count != 0) {\r
384                 fputc(0, outfile);  /* end of archive */\r
385                 if (ferror(outfile) || fclose(outfile) == EOF)\r
386                         error("Can't write");\r
387                 remove(argv[2]);  rename(temp_name, argv[2]);\r
388         }\r
389 \r
390         printf("  %d files\n", count);\r
391         return EXIT_SUCCESS;\r
392 }\r