1 /***********************************************************
\r
3 ***********************************************************/
\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
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
21 /***********************************************************
\r
23 Structure of archive block (low order byte first):
\r
26 = 25 + strlen(filename) (= 0 if end of archive)
\r
27 1 basic header algebraic sum (mod 256)
\r
29 5 method ("-lh0-" = stored, "-lh5-" = compressed)
\r
30 4 compressed size (including extended headers)
\r
35 1 filename length (x)
\r
37 2 original file's CRC
\r
39 2 first extended header size (0 if none)
\r
40 -----first extended header, etc.
\r
41 -----compressed file
\r
43 ***********************************************************/
\r
50 #define FNAME_MAX (255 - 25) /* max strlen(filename) */
\r
51 #define namelen header[19]
\r
52 #define filename ((char *)&header[20])
\r
54 int unpackable; /* global, set in io.c */
\r
55 ulong compsize, origsize; /* global */
\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
63 static uint ratio(ulong a, ulong b) /* [(1000a + [b/2]) / b] */
\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
74 static void put_to_header(int i, int n, ulong x)
\r
77 header[i++] = (uchar)((uint)x & 0xFF); x >>= 8;
\r
81 static ulong get_from_header(int i, int n)
\r
86 while (--n >= 0) s = (s << 8) + header[i + n]; /* little endian */
\r
90 static uint calc_headersum(void)
\r
96 for (i = 0; i < headersize; i++) s += header[i];
\r
100 static int read_header(void)
\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
114 static void write_header(void)
\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
123 static void skip(void)
\r
125 fseek(arcfile, compsize, SEEK_CUR);
\r
128 static void copy(void)
\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
143 static void store(void)
\r
149 while ((n = fread((char *)buffer, 1, DICSIZ, infile)) != 0) {
\r
150 fwrite_crc(buffer, n, outfile); origsize += n;
\r
152 compsize = origsize;
\r
155 static int add(int replace_flag)
\r
157 long headerpos, arcpos;
\r
160 if ((infile = fopen(filename, "rb")) == NULL) {
\r
161 fprintf(stderr, "Can't open %s\n", filename);
\r
162 return 0; /* failure */
\r
164 if (replace_flag) {
\r
165 printf("Replacing %s ", filename); skip();
\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
177 header[3] = '0'; /* store */
\r
179 fseek(outfile, arcpos, SEEK_SET);
\r
182 file_crc = crc ^ INIT_CRC;
\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
196 int get_line(char *s, int n)
\r
201 while ((c = getchar()) != EOF && c != '\n')
\r
202 if (i < n) s[i++] = (char)c;
\r
207 static void extract(int to_file)
\r
210 uint ext_headersize;
\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
219 namelen = strlen(filename);
\r
221 printf("Extracting %s ", filename);
\r
224 printf("===== %s =====\n", filename);
\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
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
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
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
254 if (to_file) fclose(outfile); else outfile = NULL;
\r
256 if ((crc ^ INIT_CRC) != file_crc)
\r
257 fprintf(stderr, "CRC error\n");
\r
260 static void list_start(void)
\r
262 printf("Filename Original Compressed Ratio CRC Method\n");
\r
265 static void list(void)
\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
276 static int match(char *s1, char *s2)
\r
279 while (*s2 == '*' || *s2 == '?') {
\r
281 while (*s1 && *s1 != *s2) s1++;
\r
286 if (*s1 != *s2) return 0;
\r
287 if (*s1 == 0 ) return 1;
\r
292 static int search(int argc, char *argv[])
\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
302 static void exitfunc(void)
\r
304 fclose(outfile); remove(temp_name);
\r
307 int main(int argc, char *argv[])
\r
309 int i, j, cmd, count, nfiles, found, done;
\r
311 /* Check command line arguments. */
\r
313 || argv[1][1] != '\0'
\r
314 || ! strchr("AXRDPL", cmd = toupper(argv[1][0]))
\r
315 || (argc == 3 && strchr("AD", cmd)))
\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
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
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
338 } else temp_name = NULL;
\r
340 make_crctable(); count = done = 0;
\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
347 strcpy(filename, argv[i]);
\r
348 if (add(0)) count++; else argv[i][0] = 0;
\r
351 if (count == 0 || arcfile == NULL) done = 1;
\r
354 while (! done && read_header()) {
\r
355 found = search(argc, argv);
\r
359 if (add(1)) count++; else copy();
\r
362 case 'A': case 'D':
\r
364 count += (cmd == 'D'); skip();
\r
367 case 'X': case 'P':
\r
369 extract(cmd == 'X');
\r
370 if (++count == nfiles) done = 1;
\r
375 if (count == 0) list_start();
\r
377 if (++count == nfiles) done = 1;
\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
390 printf(" %d files\n", count);
\r
391 return EXIT_SUCCESS;
\r