4 /* ar.c - Archive modify and extract. */
6 Bugs: should use getopt the way tar does (complete w/optional -) and
7 should have long options too. GNU ar used to check file against filesystem
8 in quick_update and replace operations (would check mtime). Doesn't warn
9 when name truncated. No way to specify pos_end. Error messages should be
19 /* Not great to have these here. Should they be exported or not? */
20 PROTO(size_t, bfd_read
, (void *ptr
, size_t size
, size_t nitems
, bfd
* abfd
));
21 PROTO(size_t, bfd_write
, (void *ptr
, size_t size
, size_t nitems
, bfd
* abfd
));
22 /* PROTO (void, open_inarch, (char *archive_filename)); */
24 static void open_inarch(char *archive_filename
);
26 static void open_inarch();
29 PROTO(void, map_over_members
, (void (*function
) (), char **files
, int count
));
30 PROTO(void, print_contents
, (bfd
* member
));
31 PROTO(void, extract_file
, (bfd
* abfd
));
32 PROTO(void, delete_members
, (char **files_to_delete
));
33 PROTO(void, do_quick_append
, (char *archive_filename
, char **files_to_append
));
34 PROTO(void, move_members
, (char **files_to_move
));
35 PROTO(void, replace_members
, (char **files_to_replace
));
36 PROTO(void, print_descr
, (bfd
* abfd
));
37 PROTO(void, ranlib_only
, (char *archname
));
39 /** Globals and flags */
41 char *program_name
= NULL
;
43 bfd
*inarch
; /* The input arch we're manipulating */
45 /* Nonzero means don't warn about creating the archive file if necessary. */
46 int silent_create
= 0;
47 /* Nonzero means describe each action performed. */
49 /* Nonzero means preserve dates of members when extracting them. */
50 int preserve_dates
= 0;
52 Nonzero means don't replace existing members whose dates are more recent
53 than the corresponding files.
56 /* write a __.SYMDEF member into the modified archive. */
57 boolean write_armap
= false;
59 Nonzero means don't update __.SYMDEF unless command line explicitly
62 int ignore_symdef
= 0;
64 Nonzero means it's the name of an existing member; position new or moved
65 files with respect to this one.
69 Sez how to use `posname': pos_before means position before that member.
70 pos_after means position after that member. pos_end means always at end.
71 pos_default means default appropriately. For the latter two, `posname'
75 pos_default
, pos_before
, pos_after
, pos_end
76 } postype
= pos_default
;
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");
139 operation
= print_files
;
142 operation
= quick_append
;
148 operation
= print_table
;
175 postype
= pos_before
;
178 postype
= pos_before
;
181 fatal("invalid option %c", c
);
185 if (operation
== none
&& write_armap
)
186 ranlib_only(argv
[2]);
188 if (operation
== none
)
189 fatal("no operation specified");
191 if (newer_only
&& operation
!= replace
)
192 fatal("'u' only meaningful with 'r' option.");
196 if (postype
!= pos_default
)
197 posname
= argv
[arg_index
++];
199 inarch_filename
= argv
[arg_index
++];
201 if (arg_index
< argc
) {
202 files
= argv
+ arg_index
;
203 while (arg_index
< argc
)
204 if (!strcmp(argv
[arg_index
++], "__.SYMDEF")) {
212 if (operation
== quick_append
) {
214 do_quick_append(inarch_filename
, files
);
219 open_inarch(inarch_filename
);
221 If we have no archive, and we've been asked to replace then create one
224 if (operation
== replace
&&
225 inarch
== &bogus_archive
) {
227 do_quick_append(inarch_filename
, 0);
228 open_inarch(inarch_filename
);
234 map_over_members(print_descr
, files
, argc
- 3);
238 map_over_members(print_contents
, files
, argc
- 3);
242 map_over_members(extract_file
, files
, argc
- 3);
247 delete_members(files
);
256 if (files
!= NULL
|| write_armap
)
257 replace_members(files
);
260 /* Shouldn't happen! */
262 fprintf(stderr
, "Sorry; this option not implemented.\n");
269 char *normalize(file
)
272 char * filename
= strrchr(file
, '/');
273 if (filename
!= (char *)NULL
) {
283 open_inarch(archive_filename
)
284 char *archive_filename
;
289 bfd_error
= no_error
;
290 if (stat(archive_filename
, &sbuf
) != 0) {
292 bfd_fatal(archive_filename
);
295 "%s: creating %s\n", program_name
, archive_filename
);
297 inarch
= &bogus_archive
;
298 inarch
->filename
= archive_filename
;
299 inarch
->has_armap
= true;
303 inarch
= bfd_openr(archive_filename
, NULL
);
304 if (inarch
== NULL
) {
306 bfd_perror(archive_filename
);
310 if (bfd_check_format(inarch
, bfd_archive
) != true)
311 fatal("File %s is not an archive.", archive_filename
);
312 last_one
= &(inarch
->next
);
313 /* Read all the contents right away, regardless. */
314 for (next_one
= bfd_openr_next_archived_file(inarch
, NULL
);
316 next_one
= bfd_openr_next_archived_file(inarch
, next_one
)) {
317 *last_one
= next_one
;
318 last_one
= &next_one
->next
;
320 *last_one
= (bfd
*) NULL
;
321 if (bfd_error
!= no_more_archived_files
)
329 If count is 0, then function is called once on each entry. if nonzero,
330 count is the length of the files chain; function is called on each entry
331 whose name matches one in files
334 map_over_members(function
, files
, count
)
345 for (head
= inarch
->next
; head
; head
= head
->next
)
350 This may appear to be a baroque way of accomplishing what we want.
351 however we have to iterate over the filenames in order to notice where
352 a filename is requested but does not exist in the archive. Ditto
353 mapping over each file each time -- we want to hack multiple
357 for (; count
> 0; files
++, count
--) {
358 boolean found
= false;
359 for (head
= inarch
->next
; head
; head
= head
->next
)
360 if ((head
->filename
!= NULL
) &&
361 (!strcmp(*files
, head
->filename
))) {
366 fprintf(stderr
, "No entry %s in archive.\n", *files
);
371 /* Things which are interesting to map over all or some of the files: */
377 print_arelt_descr(abfd
, verbose
);
387 if (bfd_stat_arch_elt(abfd
, &buf
) != 0)
388 fatal("Internal stat error on %s", abfd
->filename
);
391 printf("\n<member %s>\n\n", abfd
->filename
);
393 bfd_seek(abfd
, 0, SEEK_SET
);
396 while (ncopied
< size
) {
399 int tocopy
= size
- ncopied
;
400 if (tocopy
> BUFSIZE
)
403 nread
= bfd_read(cbuf
, 1, tocopy
, abfd
); /* oops -- broke
407 fatal("file %s not a valid archive", abfd
->my_archive
->filename
);
408 fwrite(cbuf
, 1, nread
, stdout
);
415 Extract a member of the archive into its own file.
417 We defer opening the new file until after we have read a BUFSIZ chunk of the
418 old one, since we know we have just read the archive header for the old
419 one. Since most members are shorter than BUFSIZ, this means we will read
420 the old header, read the old data, write a new inode for the new file, and
421 write the new data, and be done. This 'optimization' is what comes from
422 sitting next to a bare disk and hearing it every time it seeks. -- Gnu
437 if (bfd_stat_arch_elt(abfd
, &buf
) != 0)
438 fatal("Internal stat error on %s", abfd
->filename
);
442 printf("x - %s\n", abfd
->filename
);
444 bfd_seek(abfd
, 0, SEEK_SET
);
447 while (ncopied
< size
) {
448 tocopy
= size
- ncopied
;
449 if (tocopy
> BUFSIZE
)
452 nread
= bfd_read(cbuf
, 1, tocopy
, abfd
);
454 fatal("file %s not a valid archive", abfd
->my_archive
->filename
);
456 /* See comment above; this saves disk arm motion */
458 /* Seems like an abstraction violation, eh? Well it's OK! */
459 ostream
= fopen(abfd
->filename
, "w");
461 perror(abfd
->filename
);
465 /* no need to byte-swap; the two formats are presumably compatible(!) */
466 fwrite(cbuf
, 1, nread
, ostream
);
471 chmod(abfd
->filename
, buf
.st_mode
);
473 if (preserve_dates
) {
476 tb
[0] = buf
.st_mtime
;
477 tb
[1] = buf
.st_mtime
;
478 utime(abfd
->filename
, tb
); /* FIXME check result */
480 struct timeval tv
[2];
481 tv
[0].tv_sec
= buf
.st_mtime
;
483 tv
[1].tv_sec
= buf
.st_mtime
;
485 utimes(abfd
->filename
, tv
); /* FIXME check result */
491 /* Just do it quickly; don't worry about dups, armap, or anything like that */
493 /* This is ugly! XXX */
495 PROTO(struct ar_hdr
*, bfd_special_undocumented_glue
, (char *filename
));
498 do_quick_append(archive_filename
, files_to_append
)
499 char *archive_filename
;
500 char **files_to_append
;
510 boolean newfile
= false;
511 bfd_error
= no_error
;
513 if (stat(archive_filename
, &sbuf
) != 0) {
515 bfd_fatal(archive_filename
);
520 ofile
= fopen(archive_filename
, "a+");
522 perror(program_name
);
527 temp
= bfd_openr(archive_filename
, NULL
);
529 bfd_perror(archive_filename
);
532 if (newfile
== false) {
533 if (bfd_check_format(temp
, bfd_archive
) != true)
534 fatal("File %s is not an archive.", archive_filename
);
537 fwrite(ARMAG
, 1, SARMAG
, ofile
);
539 fprintf(stderr
, "%s: creating %s\n", program_name
, archive_filename
);
542 /* assume it's an achive, go straight to the end, sans $200 */
545 for (; files_to_append
&& *files_to_append
; ++files_to_append
) {
546 struct ar_hdr
*hdr
= bfd_special_undocumented_glue(*files_to_append
);
548 bfd_perror(*files_to_append
);
552 BFD_SEND(temp
, _bfd_truncate_arname
, (temp
, *files_to_append
, (char *) hdr
));
554 ifile
= fopen(*files_to_append
, "r");
556 bfd_perror(program_name
);
558 if (stat(*files_to_append
, &sbuf
) != 0)
559 bfd_perror(*files_to_append
);
561 tocopy
= sbuf
.st_size
;
563 /* XXX should do error-checking! */
564 fwrite(hdr
, 1, sizeof(struct ar_hdr
), ofile
);
569 if (thistime
> BUFSIZE
)
571 fread(buf
, 1, thistime
, ifile
);
572 fwrite(buf
, 1, thistime
, ofile
);
576 if ((sbuf
.st_size
% 2) == 1)
589 int namelen
= strlen(inarch
->filename
);
590 char *new_name
= xmalloc(namelen
+ 6);
591 bfd
*contents_head
= inarch
->next
;
592 if (inarch
== &bogus_archive
) {
593 /* How can this be ? */
598 strcpy(new_name
, inarch
->filename
);
599 strcpy(new_name
+ namelen
, ".art");
600 obfd
= bfd_openw(new_name
, bfd_get_target(inarch
));
603 bfd_fatal(inarch
->filename
);
605 bfd_set_format(obfd
, bfd_archive
);
606 obfd
->has_armap
= write_armap
;
608 if (bfd_set_archive_head(obfd
, contents_head
) != true)
609 bfd_fatal(inarch
->filename
);
611 if (!bfd_close(obfd
))
612 bfd_fatal(inarch
->filename
);
613 if (rename(new_name
, inarch
->filename
) != 0)
614 bfd_fatal(inarch
->filename
);
621 returns a pointer to the pointer to the entry which should be rplacd'd
622 into when altering. default_pos should be how to interpret pos_default,
623 and should be a pos value.
627 get_pos_bfd(contents
, default_pos
)
629 enum pos default_pos
;
633 enum pos realpos
= (postype
== pos_default
? default_pos
: postype
);
637 after_bfd
= contents
;
639 after_bfd
= &((*after_bfd
)->next
);
645 for (after_bfd
= contents
; after_bfd
; after_bfd
= after_bfd
->next
)
646 if (!strcpy(after_bfd
->filename
, posname
))
650 for (after_bfd
= contents
; after_bfd
; after_bfd
= after_bfd
->next
)
651 if (after_bfd
->next
&& (!strcpy(after_bfd
->next
->filename
, posname
)))
661 delete_members(files_to_delete
)
662 char **files_to_delete
;
664 bfd
**current_ptr_ptr
;
666 boolean something_changed
= false;
667 for (; *files_to_delete
!= NULL
; ++files_to_delete
) {
669 In a.out systems, the armap is optional. It's also called
670 __.SYMDEF. So if the user asked to delete it, we should remember
671 that fact. The name is NULL in COFF archives, so using this as a
672 key is as good as anything I suppose
674 if (!strcmp(*files_to_delete
, "__.SYMDEF")) {
675 inarch
->has_armap
= false;
681 current_ptr_ptr
= &(inarch
->next
);
682 while (*current_ptr_ptr
) {
683 if (strcmp(*files_to_delete
, (*current_ptr_ptr
)->filename
) == 0) {
685 something_changed
= true;
689 *current_ptr_ptr
= ((*current_ptr_ptr
)->next
);
694 current_ptr_ptr
= &((*current_ptr_ptr
)->next
);
698 if (verbose
&& found
== false) {
699 printf("No member named `%s'\n", *files_to_delete
);
705 if (something_changed
== true) {
711 /* Reposition existing members within an archive */
714 move_members(files_to_move
)
715 char **files_to_move
;
717 bfd
**after_bfd
; /* New entries go after this one */
718 bfd
**current_ptr_ptr
; /* cdr pointer into contents */
723 for (; *files_to_move
; ++files_to_move
) {
724 current_ptr_ptr
= &(inarch
->next
);
725 while (*current_ptr_ptr
) {
726 bfd
*current_ptr
= *current_ptr_ptr
;
727 if (strcmp(normalize(*files_to_move
), current_ptr
->filename
) == 0) {
729 Move this file to the end of the list - first cut from
732 *current_ptr_ptr
= current_ptr
->next
;
734 /* Now glue to end */
735 after_bfd
= get_pos_bfd(&inarch
->next
, pos_end
);
736 *after_bfd
= current_ptr
;
737 current_ptr
->next
= (bfd
*) NULL
;
740 printf("m - %s\n", *files_to_move
);
744 current_ptr_ptr
= &((*current_ptr_ptr
)->next
);
746 fprintf(stderr
, "No entry %s in archive %s!\n",
747 *files_to_move
, inarch
->filename
);
756 /* Ought to default to replacing in place, but this is existing practice! */
759 replace_members(files_to_move
)
760 char **files_to_move
;
762 bfd
**after_bfd
; /* New entries go after this one */
767 If the first item in the archive is an __.SYMDEF then remove it
770 strcmp(inarch
->next
->filename
, "__.SYMDEF") == 0) {
771 inarch
->next
= inarch
->next
->next
;
776 while (files_to_move
&& *files_to_move
) {
777 current_ptr
= &inarch
->next
;
778 while (*current_ptr
) {
779 current
= *current_ptr
;
781 if (!strcmp(normalize(*files_to_move
), current
->filename
)) {
782 /* snip out this entry from the chain */
783 *current_ptr
= current
->next
;
787 if (stat(*files_to_move
, &fsbuf
) != 0) {
789 bfd_fatal(*files_to_move
);
792 if (bfd_stat_arch_elt(current
, &asbuf
) != 0)
793 fatal("Internal stat error on %s", current
->filename
);
795 if (fsbuf
.st_mtime
<= asbuf
.st_mtime
)
800 after_bfd
= get_pos_bfd(&inarch
->next
, pos_end
);
802 *after_bfd
= bfd_openr(*files_to_move
, NULL
);
803 if (*after_bfd
== (bfd
*) NULL
) {
804 fprintf(stderr
, "Can't open file %s\n", *files_to_move
);
807 (*after_bfd
)->next
= temp
;
810 printf("%c - %s\n", (postype
== pos_after
? 'r' : 'a'),
815 current_ptr
= &(current
->next
);
818 /* It isn't in there, so add to end */
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
);
828 printf("c - %s\n", *files_to_move
);
831 (*after_bfd
)->next
= temp
;
843 ranlib_only(archname
)
847 open_inarch(archname
);