Pristine Ack-5.5
[Ack-5.5.git] / util / arch / archiver.c
1 /*
2  * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
3  * See the copyright notice in the ACK home directory, in the file "Copyright".
4  */
5 /* ar - archiver                Author: Michiel Huisjes */
6 /* Made into arch/aal by Ceriel Jacobs
7  */
8
9 static char RcsId[] = "$Id: archiver.c,v 1.29 1995/06/28 09:46:39 ceriel Exp $";
10
11 /*
12  * Usage: [arch|aal] [qdprtx][vlcu] archive [file] ...
13  *        v: verbose
14  *        x: extract
15  *        q: append
16  *        r: replace (append when not in archive)
17  *        d: delete
18  *        t: print contents of archive
19  *        p: print named files
20  *        l: temporaries in current directory instead of /tmp
21  *        c: don't give "create" message
22  *        u: replace only if dated later than member in archive
23 #ifdef DISTRIBUTION
24  *        D: make distribution: use distr_time, uid=2, gid=2, mode=0644
25 #endif
26  */
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <signal.h>
31 #include <arch.h>
32 #include <ranlib.h>
33 #ifdef AAL
34 #include <out.h>
35 #define MAGIC_NUMBER    AALMAG
36 long    offset;
37 struct ranlib *tab;
38 unsigned int    tnum = 0;
39 char    *tstrtab;
40 unsigned int    tssiz = 0;
41 char    *malloc(), *realloc(), *strcpy(), *strncpy();
42 long    time();
43 unsigned int tabsz, strtabsz;
44 #else
45 #define MAGIC_NUMBER    ARMAG
46 #endif
47 long    lseek();
48
49 #define odd(nr)         (nr & 01)
50 #define even(nr)        (odd(nr) ? nr + 1 : nr)
51
52 typedef char BOOL;
53 #define FALSE           0
54 #define TRUE            1
55
56 #define READ            0
57 #define APPEND          2
58 #define CREATE          1
59
60 #define MEMBER          struct ar_hdr
61
62 #define NIL_PTR         ((char *) 0)
63 #define NIL_MEM         ((MEMBER *) 0)
64 #define NIL_LONG        ((long *) 0)
65
66 #define IO_SIZE         (10 * 1024)
67
68 #define equal(str1, str2)       (!strncmp((str1), (str2), 14))
69 #ifndef S_ISDIR
70 #define S_ISDIR(m)      (m & S_IFDIR)           /* is a directory */
71 #endif
72
73 /* Use Posix names if old-fashioned names are not defined. */
74
75 #ifndef S_IREAD
76 #define S_IREAD S_IRUSR
77 #endif
78 #ifndef S_IWRITE
79 #define S_IWRITE S_IWUSR
80 #endif
81 #ifndef S_IEXEC
82 #define S_IEXEC S_IXUSR
83 #endif
84
85 BOOL verbose;
86 BOOL app_fl;
87 BOOL ex_fl;
88 BOOL show_fl;
89 BOOL pr_fl;
90 BOOL u_fl;
91 BOOL rep_fl;
92 BOOL del_fl;
93 BOOL nocr_fl;
94 BOOL local_fl;
95 #ifdef DISTRIBUTION
96 BOOL distr_fl;
97 long distr_time;
98 #endif
99
100 int ar_fd;
101
102 char io_buffer[IO_SIZE];
103
104 char *progname;
105
106 char temp_buf[32];
107 char *temp_arch = &temp_buf[0];
108 extern char *mktemp();
109 extern char *ctime();
110
111 usage()
112 {
113         error(TRUE, "usage: %s [qdprtxl][vlc] archive [file] ...\n",
114                 progname
115                 );
116 }
117
118 /*VARARGS2*/
119 error(quit, str1, str2, str3, str4)
120 BOOL quit;
121 char *str1, *str2, *str3, *str4;
122 {
123         char errbuf[256];
124
125         sprint(errbuf, str1, str2, str3, str4);
126         write(2, errbuf, strlen(errbuf));
127         if (quit) {
128                 unlink(temp_arch);
129                 _exit(1);
130         }
131 }
132
133 char *basename(path)
134 char *path;
135 {
136   register char *ptr = path;
137   register char *last = NIL_PTR;
138
139   while (*ptr != '\0') {
140         if (*ptr == '/')
141                 last = ptr;
142         ptr++;
143   }
144   if (last == NIL_PTR)
145         return path;
146   if (*(last + 1) == '\0') {
147         *last = '\0';
148         return basename(path);
149   }
150   return last + 1;
151 }
152
153 extern unsigned int rd_unsigned2();
154
155 open_archive(name, mode)
156 register char *name;
157 register int mode;
158 {
159   unsigned short magic = 0;
160   int fd;
161
162   if (mode == CREATE) {
163         if ((fd = creat(name, 0666)) < 0)
164                 error(TRUE, "cannot creat %s\n", name);
165         magic = MAGIC_NUMBER;
166         wr_int2(fd, magic);
167         return fd;
168   }
169
170   if ((fd = open(name, mode)) < 0) {
171         if (mode == APPEND) {
172                 close(open_archive(name, CREATE));
173                 if (!nocr_fl) error(FALSE, "%s: creating %s\n", progname, name);
174                 return open_archive(name, APPEND);
175         }
176         error(TRUE, "cannot open %s\n", name);
177   }
178   lseek(fd, 0L, 0);
179   magic = rd_unsigned2(fd);
180   if (magic != AALMAG && magic != ARMAG)
181         error(TRUE, "%s is not in ar format\n", name);
182   
183   return fd;
184 }
185
186 void
187 catch()
188 {
189         unlink(temp_arch);
190         _exit (2);
191 }
192
193 main(argc, argv)
194 int argc;
195 char *argv[];
196 {
197   register char *ptr;
198   int needs_arg = 0;
199
200   progname = argv[0];
201
202   if (argc < 3)
203         usage();
204   
205   for (ptr = argv[1]; *ptr; ptr++) {
206         switch (*ptr) {
207                 case 't' :
208                         show_fl = TRUE;
209                         break;
210                 case 'v' :
211                         verbose = TRUE;
212                         break;
213                 case 'x' :
214                         ex_fl = TRUE;
215                         break;
216                 case 'q' :
217                         needs_arg = 1;
218                         app_fl = TRUE;
219                         break;
220                 case 'c' :
221                         nocr_fl = TRUE;
222                         break;
223                 case 'u':
224                         u_fl = TRUE;
225                         break;
226                 case 'p' :
227                         needs_arg = 1;
228                         pr_fl = TRUE;
229                         break;
230                 case 'd' :
231                         needs_arg = 1;
232                         del_fl = TRUE;
233                         break;
234                 case 'r' :
235                         needs_arg = 1;
236                         rep_fl = TRUE;
237                         break;
238                 case 'l' :
239                         local_fl = TRUE;
240                         break;
241 #ifdef DISTRIBUTION
242                 case 'D' :
243                         distr_fl = TRUE;
244                         break;
245 #endif
246                 default :
247                         usage();
248         }
249   }
250
251   if (needs_arg && argc <= 3)
252         usage();
253 #ifdef DISTRIBUTION
254   if (distr_fl) {
255         static struct stat statbuf;
256
257         stat(progname, &statbuf);
258         distr_time = statbuf.st_mtime;
259   }
260 #endif
261   if (local_fl) strcpy(temp_arch, "ar.XXXXXX");
262   else  strcpy(temp_arch, "/tmp/ar.XXXXXX");
263
264   if (app_fl + ex_fl + del_fl + rep_fl + show_fl + pr_fl != 1)
265         usage();
266   
267   if (u_fl && ! rep_fl)
268         usage();
269
270   if (rep_fl || del_fl
271 #ifdef AAL
272         || app_fl
273 #endif
274      ) {
275         mktemp(temp_arch);
276   }
277 #ifdef AAL
278   tab = (struct ranlib *) malloc(512 * sizeof(struct ranlib));
279   tstrtab = malloc(4096);
280   if (!tab || !tstrtab) error(TRUE,"Out of core\n");
281   tabsz = 512;
282   strtabsz = 4096;
283 #endif
284
285   signal(SIGINT, catch);
286   get(argc, argv);
287   
288   return 0;
289 }
290
291 MEMBER *
292 get_member()
293 {
294   static MEMBER member;
295
296 again:
297   if (rd_arhdr(ar_fd, &member) == 0)
298         return NIL_MEM;
299   if (member.ar_size < 0) {
300         error(TRUE, "archive has member with negative size\n");
301   }
302   if (equal(SYMDEF, member.ar_name)) {
303         lseek(ar_fd, member.ar_size, 1);
304         goto again;
305   }
306   return &member;
307 }
308
309 char *get_mode();
310
311 get(argc, argv)
312 int argc;
313 register char *argv[];
314 {
315   register MEMBER *member;
316   int i = 0;
317   int temp_fd, read_chars;
318
319   ar_fd = open_archive(argv[2], (show_fl || pr_fl || ex_fl) ? READ : APPEND);
320   if (rep_fl || del_fl
321 #ifdef AAL
322         || app_fl
323 #endif
324   )
325         temp_fd = open_archive(temp_arch, CREATE);
326   while ((member = get_member()) != NIL_MEM) {
327         if (argc > 3) {
328                 for (i = 3; i < argc; i++) {
329                         if (equal(basename(argv[i]), member->ar_name))
330                                 break;
331                 }
332                 if (i == argc || app_fl) {
333                         if (rep_fl || del_fl
334 #ifdef AAL
335                                 || app_fl
336 #endif
337                         ) {
338 #ifdef AAL
339                                 if (i != argc) {
340                                         print("%s: already in archive\n", argv[i]);
341                                         argv[i] = "";
342                                 }
343 #endif
344                                 wr_arhdr(temp_fd, member);
345                                 copy_member(member, ar_fd, temp_fd, 0);
346                         }
347                         else {
348 #ifndef AAL
349                                 if (app_fl && i != argc) {
350                                         print("%s: already in archive\n", argv[i]);
351                                         argv[i] = "";
352                                 }
353 #endif
354                                 lseek(ar_fd, even(member->ar_size),1);
355                         }
356                         continue;
357                 }
358         }
359         if (ex_fl || pr_fl)
360                 extract(member);
361         else {
362                 if (rep_fl)
363                         add(argv[i], temp_fd, "r - %s\n");
364                 else if (show_fl) {
365                         char buf[sizeof(member->ar_name) + 2];
366                         register char *p = buf, *q = member->ar_name;
367
368                         while (q <= &member->ar_name[sizeof(member->ar_name)-1] && *q) {
369                                 *p++ = *q++;
370                         }
371                         *p++ = '\n';
372                         *p = '\0';
373                         if (verbose) {
374                                 char *mode = get_mode(member->ar_mode);
375                                 char *date = ctime(&(member->ar_date));
376
377                                 *(date + 16) = '\0';
378                                 *(date + 24) = '\0';
379
380                                 print("%s%3u/%u%7ld %s %s %s",
381                                         mode,
382                                         (unsigned) (member->ar_uid & 0377),
383                                         (unsigned) (member->ar_gid & 0377),
384                                         member->ar_size,
385                                         date+4,
386                                         date+20,
387                                         buf);
388                         }
389                         else    print(buf);
390                 }
391                 else if (del_fl)
392                         show("d - %s\n", member->ar_name);
393                 lseek(ar_fd, even(member->ar_size), 1);
394         }
395         argv[i] = "";
396   }
397
398   if (argc > 3) {
399         for (i = 3; i < argc; i++)
400                 if (argv[i][0] != '\0') {
401 #ifndef AAL
402                         if (app_fl)
403                                 add(argv[i], ar_fd, "a - %s\n");
404                         else
405 #endif
406                         if (rep_fl
407 #ifdef AAL
408                                 || app_fl
409 #endif
410                         )
411                                 add(argv[i], temp_fd, "a - %s\n");
412                         else {
413                                 print("%s: not found\n", argv[i]);
414                         }
415                 }
416   }
417
418   if (rep_fl || del_fl
419 #ifdef AAL
420                 || app_fl
421 #endif
422   ) {
423         signal(SIGINT, SIG_IGN);
424         close(ar_fd);
425         close(temp_fd);
426         ar_fd = open_archive(argv[2], CREATE);
427         temp_fd = open_archive(temp_arch, APPEND);
428 #ifdef AAL
429         write_symdef();
430 #endif
431         while ((read_chars = read(temp_fd, io_buffer, IO_SIZE)) > 0)
432                 mwrite(ar_fd, io_buffer, read_chars);
433         close(temp_fd);
434         unlink(temp_arch);
435   }
436   close(ar_fd);
437 }
438
439 add(name, fd, mess)
440 char *name;
441 int fd;
442 char *mess;
443 {
444   static MEMBER member;
445   register int read_chars;
446   struct stat status;
447   int src_fd;
448
449   if (stat(name, &status) < 0) {
450         error(FALSE, "cannot find %s\n", name);
451         return;
452   }
453   else if (S_ISDIR(status.st_mode)) {
454         error(FALSE, "%s is a directory (ignored)\n", name);
455         return;
456   }
457   else if (u_fl && status.st_mtime <= member.ar_date) {
458         wr_arhdr(fd, member);
459         copy_member(member, ar_fd, fd, 0);
460         return;
461   }
462   else if ((src_fd = open(name, 0)) < 0) {
463         error(FALSE, "cannot open %s\n", name);
464         return;
465   }
466
467   strncpy (member.ar_name, basename (name), sizeof(member.ar_name));
468   member.ar_uid = status.st_uid;
469   member.ar_gid = status.st_gid;
470   member.ar_mode = status.st_mode;
471   member.ar_date = status.st_mtime;
472   member.ar_size = status.st_size;
473 #ifdef DISTRIBUTION
474   if (distr_fl) {
475         member.ar_uid = 2;
476         member.ar_gid = 2;
477         member.ar_mode = 0644;
478         member.ar_date = distr_time;
479   }
480 #endif
481   wr_arhdr(fd, &member);
482 #ifdef AAL
483   do_object(src_fd, member.ar_size);
484   lseek(src_fd, 0L, 0);
485   offset += AR_TOTAL + even(member.ar_size);
486 #endif
487   while (status.st_size > 0) {
488         int x = IO_SIZE;
489
490         read_chars = x;
491         if (status.st_size < x) {
492                 x = status.st_size;
493                 read_chars = x;
494                 status.st_size = 0;
495                 x = even(x);
496         }
497         else    status.st_size -= x;
498         if (read(src_fd, io_buffer, read_chars) != read_chars) {
499                 error(FALSE,"%s seems to shrink\n", name);
500                 break;
501         }
502         mwrite(fd, io_buffer, x);
503   }
504
505   if (verbose)
506         show(mess, member.ar_name);
507   close(src_fd);
508 }
509
510 extract(member)
511 register MEMBER *member;
512 {
513   int fd = 1;
514   char buf[sizeof(member->ar_name) + 1];
515
516   strncpy(buf, member->ar_name, sizeof(member->ar_name));
517   buf[sizeof(member->ar_name)] = 0;
518   if (pr_fl == FALSE && (fd = creat(buf, 0666)) < 0) {
519         error(FALSE, "cannot create %s\n", buf);
520         fd = -1;
521   }
522
523   if (verbose) {
524         if (pr_fl == FALSE) show("x - %s\n", buf);
525         else show("\n<%s>\n\n", buf);
526   }
527
528   copy_member(member, ar_fd, fd, 1);
529
530   if (fd >= 0 && fd != 1)
531         close(fd);
532   if (pr_fl == FALSE) chmod(buf, member->ar_mode);
533 }
534
535 copy_member(member, from, to, extracting)
536 register MEMBER *member;
537 int from, to;
538 {
539   register int rest;
540   long mem_size = member->ar_size;
541   BOOL is_odd = odd(mem_size) ? TRUE : FALSE;
542
543 #ifdef AAL
544   if (! extracting) {
545         long pos = lseek(from, 0L, 1);
546
547         do_object(from, mem_size);
548         offset += AR_TOTAL + even(mem_size);
549         lseek(from, pos, 0);
550   }
551 #endif
552   do {
553         rest = mem_size > (long) IO_SIZE ? IO_SIZE : (int) mem_size;
554         if (read(from, io_buffer, rest) != rest) {
555                 char buf[sizeof(member->ar_name) + 1];
556
557                 strncpy(buf, member->ar_name, sizeof(member->ar_name));
558                 buf[sizeof(member->ar_name)] = 0;
559                 error(TRUE, "read error on %s\n", buf);
560         }
561         if (to >= 0) mwrite(to, io_buffer, rest);
562         mem_size -= (long) rest;
563   } while (mem_size > 0L);
564
565   if (is_odd) {
566         lseek(from, 1L, 1);
567         if (to >= 0 && ! extracting)
568                 lseek(to, 1L, 1);
569   }
570 }
571
572 char *
573 get_mode(mode)
574 register int mode;
575 {
576   static char mode_buf[11];
577   register int tmp = mode;
578   int i;
579
580   mode_buf[9] = ' ';
581   for (i = 0; i < 3; i++) {
582         mode_buf[i * 3] = (tmp & S_IREAD) ? 'r' : '-';
583         mode_buf[i * 3 + 1] = (tmp & S_IWRITE) ? 'w' : '-';
584         mode_buf[i * 3 + 2] = (tmp & S_IEXEC) ? 'x' : '-';
585         tmp <<= 3;
586   }
587   if (mode & S_ISUID)
588         mode_buf[2] = 's';
589   if (mode & S_ISGID)
590         mode_buf[5] = 's';
591   return mode_buf;
592 }
593
594 wr_fatal()
595 {
596         error(TRUE, "write error\n");
597 }
598
599 rd_fatal()
600 {
601         error(TRUE, "read error\n");
602 }
603
604 mwrite(fd, address, bytes)
605 int fd;
606 register char *address;
607 register int bytes;
608 {
609   if (write(fd, address, bytes) != bytes)
610         error(TRUE, "write error\n");
611 }
612
613 show(s, name)
614 char *s, *name;
615 {
616   MEMBER x;
617   char buf[sizeof(x.ar_name)+1];
618   register char *p = buf, *q = name;
619
620   while (q <= &name[sizeof(x.ar_name)-1] && *q) *p++ = *q++;
621   *p++ = '\0';
622   print(s, buf);
623 }
624
625 #ifdef AAL
626 /*
627  * Write out the ranlib table: first 4 bytes telling how many ranlib structs
628  * there are, followed by the ranlib structs,
629  * then 4 bytes giving the size of the string table, followed by the string
630  * table itself.
631  */
632 write_symdef()
633 {
634         register struct ranlib  *ran;
635         register int    i;
636         register long   delta;
637         MEMBER  arbuf;
638
639         if (! tnum) return;
640         if (odd(tssiz))
641                 tstrtab[tssiz++] = '\0';
642         for (i = 0; i < sizeof(arbuf.ar_name); i++)
643                 arbuf.ar_name[i] = '\0';
644         strcpy(arbuf.ar_name, SYMDEF);
645         arbuf.ar_size = 4 + 2 * 4 * (long)tnum + 4 + (long)tssiz;
646         time(&arbuf.ar_date);
647         arbuf.ar_uid = getuid();
648         arbuf.ar_gid = getgid();
649         arbuf.ar_mode = 0444;
650 #ifdef DISTRIBUTION
651         if (distr_fl) {
652                 arbuf.ar_uid = 2;
653                 arbuf.ar_gid = 2;
654                 arbuf.ar_date = distr_time;
655         }
656 #endif
657         wr_arhdr(ar_fd,&arbuf);
658         wr_long(ar_fd, (long) tnum);
659         /*
660          * Account for the space occupied by the magic number
661          * and the ranlib table.
662          */
663         delta = 2 + AR_TOTAL + arbuf.ar_size;
664         for (ran = tab; ran < &tab[tnum]; ran++) {
665                 ran->ran_pos += delta;
666         }
667
668         wr_ranlib(ar_fd, tab, (long) tnum);
669         wr_long(ar_fd, (long) tssiz);
670         wr_bytes(ar_fd, tstrtab, (long) tssiz);
671 }
672
673 /*
674  * Return whether the bytes in `buf' form a good object header. 
675  * The header is put in `headp'.
676  */
677 int
678 is_outhead(headp)
679         register struct outhead *headp;
680 {
681
682         return !BADMAGIC(*headp) && headp->oh_nname != 0;
683 }
684
685 do_object(f, size)
686         long size;
687 {
688         struct outhead  headbuf;
689
690         if (size < SZ_HEAD) {
691                 /* It can't be an object file. */
692                 return;
693         }
694         /*
695          * Read a header to see if it is an object file.
696          */
697         if (! rd_fdopen(f)) {
698                 rd_fatal();
699         }
700         rd_ohead(&headbuf);
701         if (!is_outhead(&headbuf)) {
702                 return;
703         }
704         do_names(&headbuf);
705 }
706
707 /*
708  * First skip the names and read in the string table, then seek back to the
709  * name table and read and write the names one by one. Update the ranlib table
710  * accordingly.
711  */
712 do_names(headp)
713         struct outhead  *headp;
714 {
715         register char   *strings;
716         register int    nnames = headp->oh_nname;
717 #define NNAMES 100
718         struct outname  namebuf[NNAMES];
719         long xxx = OFF_CHAR(*headp);
720
721         if (    headp->oh_nchar != (unsigned int)headp->oh_nchar ||
722                 (strings = malloc((unsigned int)headp->oh_nchar)) == (char *)0
723            ) {
724                 error(TRUE, "string table too big\n");
725         }
726         rd_string(strings, headp->oh_nchar);
727         while (nnames) {
728                 int i = nnames >= NNAMES ? NNAMES : nnames;
729                 register struct outname *p = namebuf;
730
731                 nnames -= i;
732                 rd_name(namebuf, i);
733                 while (i--) {
734                         long off = p->on_foff - xxx;
735                         if (p->on_foff == (long)0) {
736                                 p++;
737                                 continue; /* An unrecognizable name. */
738                         }
739                         p->on_mptr = strings + off;
740                         /*
741                          * Only enter names that are exported and are really
742                          * defined. Also enter common names. Note, that
743                          * this might cause problems when the name is really
744                          * defined in a later file, with a value != 0.
745                          * However, this problem also exists on the Unix
746                          * ranlib archives.
747                          */
748                         if (    (p->on_type & S_EXT) &&
749                                 (p->on_type & S_TYP) != S_UND
750                            )
751                                 enter_name(p);
752                         p++;
753                 }
754         }
755         free(strings);
756 }
757
758 enter_name(namep)
759         struct outname *namep;
760 {
761         register char   *cp;
762
763         if (tnum >= tabsz) {
764                 tab = (struct ranlib *)
765                         realloc((char *) tab, (tabsz += 512) * sizeof(struct ranlib));
766                 if (! tab) error(TRUE, "Out of core\n");
767         }
768         tab[tnum].ran_off = tssiz;
769         tab[tnum].ran_pos = offset;
770
771         for (cp = namep->on_mptr;; cp++) {
772                 if (tssiz >= strtabsz) {
773                         tstrtab = realloc(tstrtab, (strtabsz += 4096));
774                         if (! tstrtab) error(TRUE, "string table overflow\n");
775                 }
776                 tstrtab[tssiz++]  = *cp;
777                 if (!*cp) break;
778         }
779         tnum++;
780 }
781 #endif /* AAL */