1 /* ar.c - Archive modify and extract. */
3 Bugs: should use getopt the way tar does (complete w/optional -) and
4 should have long options too. GNU ar used to check file against filesystem
5 in quick_update and replace operations (would check mtime). Doesn't warn
6 when name truncated. No way to specify pos_end. Error messages should be
16 /* Not great to have these here. Should they be exported or not? */
17 PROTO(size_t, bfd_read, (void *ptr, size_t size, size_t nitems, bfd * abfd));
18 PROTO(size_t, bfd_write, (void *ptr, size_t size, size_t nitems, bfd * abfd));
19 /* PROTO (void, open_inarch, (char *archive_filename)); */
21 static void open_inarch(char *archive_filename);
23 static void open_inarch();
26 PROTO(void, map_over_members, (void (*function) (), char **files, int count));
27 PROTO(void, print_contents, (bfd * member));
28 PROTO(void, extract_file, (bfd * abfd));
29 PROTO(void, delete_members, (char **files_to_delete));
30 PROTO(void, do_quick_append, (char *archive_filename, char **files_to_append));
31 PROTO(void, move_members, (char **files_to_move));
32 PROTO(void, replace_members, (char **files_to_replace));
33 PROTO(void, print_descr, (bfd * abfd));
34 PROTO(void, ranlib_only, (char *archname));
36 /** Globals and flags */
38 char *program_name = NULL;
40 bfd *inarch; /* The input arch we're manipulating */
42 /* Nonzero means don't warn about creating the archive file if necessary. */
43 int silent_create = 0;
44 /* Nonzero means describe each action performed. */
46 /* Nonzero means preserve dates of members when extracting them. */
47 int preserve_dates = 0;
49 Nonzero means don't replace existing members whose dates are more recent
50 than the corresponding files.
53 /* write a __.SYMDEF member into the modified archive. */
54 boolean write_armap = false;
56 Nonzero means don't update __.SYMDEF unless command line explicitly
59 int ignore_symdef = 0;
61 Nonzero means it's the name of an existing member; position new or moved
62 files with respect to this one.
66 Sez how to use `posname': pos_before means position before that member.
67 pos_after means position after that member. pos_end means always at end.
68 pos_default means default appropriately. For the latter two, `posname'
72 pos_default, pos_before, pos_after, pos_end
73 } postype = pos_default;
76 boolean operation_alters_arch = false;
79 The option parsing should be in its own function. It will be when I have
90 none = 0, delete, replace, print_table,
91 print_files, extract, move, quick_append
95 char *inarch_filename;
97 program_name = argv[0];
100 temp = strrchr(program_name, '/');
101 if (temp == (char *) NULL)
102 temp = program_name; /* shouldn't happen, but... */
105 if (!strcmp(temp, "ranlib")) {
107 fatal("Too few command arguments.");
108 ranlib_only(argv[1]);
113 fatal("Too few command arguments.");
118 ++arg_ptr; /* compatibility */
120 while (c = *arg_ptr++) {
129 if (operation != none)
130 fatal("two different operation switches specified");
134 operation_alters_arch = true;
138 operation_alters_arch = true;
141 operation = print_files;
144 operation = quick_append;
145 operation_alters_arch = true;
149 operation_alters_arch = true;
152 operation = print_table;
179 postype = pos_before;
182 postype = pos_before;
185 fatal("invalid option %c", c);
189 if (operation == none && write_armap)
190 ranlib_only(argv[2]);
192 if (operation == none)
193 fatal("no operation specified");
195 if (newer_only && operation != replace)
196 fatal("'u' only meaningful with 'r' option.");
200 if (postype != pos_default)
201 posname = argv[arg_index++];
203 inarch_filename = argv[arg_index++];
205 if (arg_index < argc) {
206 files = argv + arg_index;
207 while (arg_index < argc)
208 if (!strcmp(argv[arg_index++], "__.SYMDEF")) {
216 if (operation == quick_append) {
218 do_quick_append(inarch_filename, files);
223 open_inarch(inarch_filename);
225 If we have no archive, and we've been asked to replace then create one
228 if (operation == replace && inarch == &bogus_archive) {
230 do_quick_append(inarch_filename, 0);
231 open_inarch(inarch_filename);
237 map_over_members(print_descr, files, argc - 3);
241 map_over_members(print_contents, files, argc - 3);
245 map_over_members(extract_file, files, argc - 3);
250 delete_members(files);
259 if (files != NULL || write_armap)
260 replace_members(files);
263 /* Shouldn't happen! */
265 fprintf(stderr, "Sorry; this option not implemented.\n");
272 char *normalize(file)
275 char * filename = strrchr(file, '/');
276 if (filename != (char *)NULL) {
286 open_inarch(archive_filename)
287 char *archive_filename;
292 bfd_error = no_error;
293 if (stat(archive_filename, &sbuf) != 0) {
295 bfd_fatal(archive_filename);
296 if (!operation_alters_arch) {
297 fprintf (stderr, "%s: %s not found.\n", program_name,
303 "%s: creating %s\n", program_name, archive_filename);
305 inarch = &bogus_archive;
306 inarch->filename = archive_filename;
307 inarch->has_armap = true;
311 inarch = bfd_openr(archive_filename, NULL);
312 if (inarch == NULL) {
314 bfd_perror(archive_filename);
318 if (bfd_check_format(inarch, bfd_archive) != true)
319 fatal("File %s is not an archive.", archive_filename);
320 last_one = &(inarch->next);
321 /* Read all the contents right away, regardless. */
322 for (next_one = bfd_openr_next_archived_file(inarch, NULL);
324 next_one = bfd_openr_next_archived_file(inarch, next_one)) {
325 *last_one = next_one;
326 last_one = &next_one->next;
328 *last_one = (bfd *) NULL;
329 if (bfd_error != no_more_archived_files)
337 If count is 0, then function is called once on each entry. if nonzero,
338 count is the length of the files chain; function is called on each entry
339 whose name matches one in files
342 map_over_members(function, files, count)
353 for (head = inarch->next; head; head = head->next)
358 This may appear to be a baroque way of accomplishing what we want.
359 however we have to iterate over the filenames in order to notice where
360 a filename is requested but does not exist in the archive. Ditto
361 mapping over each file each time -- we want to hack multiple
365 for (; count > 0; files++, count--) {
366 boolean found = false;
367 for (head = inarch->next; head; head = head->next)
368 if ((head->filename != NULL) &&
369 (!strcmp(*files, head->filename))) {
374 fprintf(stderr, "No entry %s in archive.\n", *files);
379 /* Things which are interesting to map over all or some of the files: */
385 print_arelt_descr(abfd, verbose);
395 if (bfd_stat_arch_elt(abfd, &buf) != 0)
396 fatal("Internal stat error on %s", abfd->filename);
399 printf("\n<member %s>\n\n", abfd->filename);
401 bfd_seek(abfd, 0, SEEK_SET);
404 while (ncopied < size) {
407 int tocopy = size - ncopied;
408 if (tocopy > BUFSIZE)
411 nread = bfd_read(cbuf, 1, tocopy, abfd); /* oops -- broke
415 fatal("file %s not a valid archive", abfd->my_archive->filename);
416 fwrite(cbuf, 1, nread, stdout);
423 Extract a member of the archive into its own file.
425 We defer opening the new file until after we have read a BUFSIZ chunk of the
426 old one, since we know we have just read the archive header for the old
427 one. Since most members are shorter than BUFSIZ, this means we will read
428 the old header, read the old data, write a new inode for the new file, and
429 write the new data, and be done. This 'optimization' is what comes from
430 sitting next to a bare disk and hearing it every time it seeks. -- Gnu
445 if (bfd_stat_arch_elt(abfd, &buf) != 0)
446 fatal("Internal stat error on %s", abfd->filename);
450 printf("x - %s\n", abfd->filename);
452 bfd_seek(abfd, 0, SEEK_SET);
456 /* Seems like an abstraction violation, eh? Well it's OK! */
457 ostream = fopen(abfd->filename, "w");
459 perror(abfd->filename);
463 while (ncopied < size) {
464 tocopy = size - ncopied;
465 if (tocopy > BUFSIZE)
468 nread = bfd_read(cbuf, 1, tocopy, abfd);
470 fatal("file %s not a valid archive", abfd->my_archive->filename);
472 /* See comment above; this saves disk arm motion */
474 /* Seems like an abstraction violation, eh? Well it's OK! */
475 ostream = fopen(abfd->filename, "w");
477 perror(abfd->filename);
481 fwrite(cbuf, 1, nread, ostream);
486 chmod(abfd->filename, buf.st_mode);
488 if (preserve_dates) {
491 tb[0] = buf.st_mtime;
492 tb[1] = buf.st_mtime;
493 utime(abfd->filename, tb); /* FIXME check result */
495 struct timeval tv[2];
496 tv[0].tv_sec = buf.st_mtime;
498 tv[1].tv_sec = buf.st_mtime;
500 utimes(abfd->filename, tv); /* FIXME check result */
506 /* Just do it quickly; don't worry about dups, armap, or anything like that */
508 /* This is ugly! XXX */
510 PROTO(struct ar_hdr *, bfd_special_undocumented_glue, (char *filename));
513 do_quick_append(archive_filename, files_to_append)
514 char *archive_filename;
515 char **files_to_append;
525 boolean newfile = false;
526 bfd_error = no_error;
528 if (stat(archive_filename, &sbuf) != 0) {
530 bfd_fatal(archive_filename);
535 ofile = fopen(archive_filename, "a+");
537 perror(program_name);
542 temp = bfd_openr(archive_filename, NULL);
544 bfd_perror(archive_filename);
547 if (newfile == false) {
548 if (bfd_check_format(temp, bfd_archive) != true)
549 fatal("File %s is not an archive.", archive_filename);
552 fwrite(ARMAG, 1, SARMAG, ofile);
554 fprintf(stderr, "%s: creating %s\n", program_name, archive_filename);
557 /* assume it's an achive, go straight to the end, sans $200 */
560 for (; files_to_append && *files_to_append; ++files_to_append) {
561 struct ar_hdr *hdr = bfd_special_undocumented_glue(*files_to_append);
563 bfd_perror(*files_to_append);
567 BFD_SEND(temp, _bfd_truncate_arname, (temp, *files_to_append, (char *) hdr));
569 ifile = fopen(*files_to_append, "r");
571 bfd_perror(program_name);
573 if (stat(*files_to_append, &sbuf) != 0)
574 bfd_perror(*files_to_append);
576 tocopy = sbuf.st_size;
578 /* XXX should do error-checking! */
579 fwrite(hdr, 1, sizeof(struct ar_hdr), ofile);
584 if (thistime > BUFSIZE)
586 fread(buf, 1, thistime, ifile);
587 fwrite(buf, 1, thistime, ofile);
591 if ((sbuf.st_size % 2) == 1)
604 int namelen = strlen(inarch->filename);
605 char *new_name = xmalloc(namelen + 6);
606 bfd *contents_head = inarch->next;
608 if (inarch == &bogus_archive) {
609 /* How can this be ? */
614 strcpy(new_name, inarch->filename);
615 strcpy(new_name + namelen, ".art");
616 obfd = bfd_openw(new_name,
617 /* violates abstraction; need a better protocol */
618 (inarch->xvec ? bfd_get_target(inarch) : NULL));
621 bfd_fatal(inarch->filename);
623 bfd_set_format(obfd, bfd_archive);
624 obfd->has_armap = write_armap;
626 if (bfd_set_archive_head(obfd, contents_head) != true)
627 bfd_fatal(inarch->filename);
629 if (!bfd_close(obfd))
630 bfd_fatal(inarch->filename);
631 if (rename(new_name, inarch->filename) != 0)
632 bfd_fatal(inarch->filename);
641 returns a pointer to the pointer to the entry which should be rplacd'd
642 into when altering. default_pos should be how to interpret pos_default,
643 and should be a pos value.
647 get_pos_bfd(contents, default_pos)
649 enum pos default_pos;
653 enum pos realpos = (postype == pos_default ? default_pos : postype);
657 after_bfd = contents;
659 after_bfd = &((*after_bfd)->next);
665 for (after_bfd = contents; after_bfd; after_bfd = after_bfd->next)
666 if (!strcpy(after_bfd->filename, posname))
670 for (after_bfd = contents; after_bfd; after_bfd = after_bfd->next)
671 if (after_bfd->next && (!strcpy(after_bfd->next->filename, posname)))
681 delete_members(files_to_delete)
682 char **files_to_delete;
684 bfd **current_ptr_ptr;
686 boolean something_changed = false;
687 for (; *files_to_delete != NULL; ++files_to_delete) {
689 In a.out systems, the armap is optional. It's also called
690 __.SYMDEF. So if the user asked to delete it, we should remember
691 that fact. The name is NULL in COFF archives, so using this as a
692 key is as good as anything I suppose
694 if (!strcmp(*files_to_delete, "__.SYMDEF")) {
695 inarch->has_armap = false;
701 current_ptr_ptr = &(inarch->next);
702 while (*current_ptr_ptr) {
703 if (strcmp(*files_to_delete, (*current_ptr_ptr)->filename) == 0) {
705 something_changed = true;
709 *current_ptr_ptr = ((*current_ptr_ptr)->next);
714 current_ptr_ptr = &((*current_ptr_ptr)->next);
718 if (verbose && found == false) {
719 printf("No member named `%s'\n", *files_to_delete);
725 if (something_changed == true) {
731 /* Reposition existing members within an archive */
734 move_members(files_to_move)
735 char **files_to_move;
737 bfd **after_bfd; /* New entries go after this one */
738 bfd **current_ptr_ptr; /* cdr pointer into contents */
743 for (; *files_to_move; ++files_to_move) {
744 current_ptr_ptr = &(inarch->next);
745 while (*current_ptr_ptr) {
746 bfd *current_ptr = *current_ptr_ptr;
747 if (strcmp(normalize(*files_to_move), current_ptr->filename) == 0) {
749 Move this file to the end of the list - first cut from
752 *current_ptr_ptr = current_ptr->next;
754 /* Now glue to end */
755 after_bfd = get_pos_bfd(&inarch->next, pos_end);
756 *after_bfd = current_ptr;
757 current_ptr->next = (bfd *) NULL;
760 printf("m - %s\n", *files_to_move);
764 current_ptr_ptr = &((*current_ptr_ptr)->next);
766 fprintf(stderr, "No entry %s in archive %s!\n",
767 *files_to_move, inarch->filename);
776 /* Ought to default to replacing in place, but this is existing practice! */
779 replace_members(files_to_move)
780 char **files_to_move;
782 bfd **after_bfd; /* New entries go after this one */
787 If the first item in the archive is an __.SYMDEF then remove it
790 strcmp(inarch->next->filename, "__.SYMDEF") == 0) {
791 inarch->next = inarch->next->next;
796 while (files_to_move && *files_to_move) {
797 current_ptr = &inarch->next;
798 while (*current_ptr) {
799 current = *current_ptr;
801 if (!strcmp(normalize(*files_to_move), current->filename)) {
802 /* snip out this entry from the chain */
803 *current_ptr = current->next;
807 if (stat(*files_to_move, &fsbuf) != 0) {
809 bfd_fatal(*files_to_move);
812 if (bfd_stat_arch_elt(current, &asbuf) != 0)
813 fatal("Internal stat error on %s", current->filename);
815 if (fsbuf.st_mtime <= asbuf.st_mtime)
820 after_bfd = get_pos_bfd(&inarch->next, pos_end);
822 *after_bfd = bfd_openr(*files_to_move, NULL);
823 if (*after_bfd == (bfd *) NULL) {
824 fprintf(stderr, "Can't open file %s\n", *files_to_move);
827 (*after_bfd)->next = temp;
830 printf("%c - %s\n", (postype == pos_after ? 'r' : 'a'),
835 current_ptr = &(current->next);
838 /* It isn't in there, so add to end */
840 after_bfd = get_pos_bfd(&inarch->next, pos_end);
842 *after_bfd = bfd_openr(*files_to_move, NULL);
843 if (*after_bfd == (bfd *) NULL) {
844 fprintf(stderr, "Can't open file %s\n", *files_to_move);
848 printf("c - %s\n", *files_to_move);
851 (*after_bfd)->next = temp;
863 ranlib_only(archname)
867 open_inarch(archname);