fix two small bugs: quick-append no longer used to create fresh archives;
[platform/upstream/binutils.git] / binutils / ar.c
1 /* ar.c - Archive modify and extract. */
2 /*
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
7    more consistant.
8 */
9 #include "sysdep.h"
10 #include "bfd.h"
11 #include "ar.h"
12 #include <stdio.h>
13 #include <sys/time.h>
14 #include <errno.h>
15 #define BUFSIZE 8192
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)); */
20 #ifdef __STDC__
21 static void     open_inarch(char *archive_filename);
22 #else
23 static void     open_inarch();
24 #endif                          /* __STDC__ */
25
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));
35
36 /** Globals and flags */
37
38 char           *program_name = NULL;
39 bfd             bogus_archive;
40 bfd            *inarch;         /* The input arch we're manipulating */
41
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.  */
45 int             verbose = 0;
46 /* Nonzero means preserve dates of members when extracting them.  */
47 int             preserve_dates = 0;
48 /*
49    Nonzero means don't replace existing members whose dates are more recent
50    than the corresponding files.
51 */
52 int             newer_only = 0;
53 /* write a __.SYMDEF member into the modified archive.  */
54 boolean         write_armap = false;
55 /*
56    Nonzero means don't update __.SYMDEF unless command line explicitly
57    requested it
58 */
59 int             ignore_symdef = 0;
60 /*
61    Nonzero means it's the name of an existing member; position new or moved
62    files with respect to this one.
63 */
64 char           *posname = NULL;
65 /*
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'
69    should also be zero.
70 */
71 enum pos {
72     pos_default, pos_before, pos_after, pos_end
73 }               postype = pos_default;
74
75
76 boolean operation_alters_arch = false;
77
78 /*
79    The option parsing should be in its own function.  It will be when I have
80    getopt working.
81 */
82 int
83 main(argc, argv)
84     int             argc;
85     char          **argv;
86 {
87     char           *arg_ptr;
88     char            c;
89     enum {
90         none = 0, delete, replace, print_table,
91         print_files, extract, move, quick_append
92     }               operation = none;
93     int             arg_index;
94     char          **files;
95     char           *inarch_filename;
96     char           *temp;
97     program_name = argv[0];
98
99
100     temp = strrchr(program_name, '/');
101     if (temp == (char *) NULL)
102         temp = program_name;    /* shouldn't happen, but... */
103     else
104         ++temp;
105     if (!strcmp(temp, "ranlib")) {
106         if (argc < 2)
107             fatal("Too few command arguments.");
108         ranlib_only(argv[1]);
109     }
110
111
112     if (argc < 3)
113         fatal("Too few command arguments.");
114
115     arg_ptr = argv[1];
116
117     if (*arg_ptr == '-')
118         ++arg_ptr;              /* compatibility */
119
120     while (c = *arg_ptr++) {
121         switch (c) {
122         case 'd':
123         case 'm':
124         case 'p':
125         case 'q':
126         case 'r':
127         case 't':
128         case 'x':
129             if (operation != none)
130                 fatal("two different operation switches specified");
131             switch (c) {
132             case 'd':
133                 operation = delete;
134                 operation_alters_arch = true;
135                 break;
136             case 'm':
137                 operation = move;
138                 operation_alters_arch = true;
139                 break;
140             case 'p':
141                 operation = print_files;
142                 break;
143             case 'q':
144                 operation = quick_append;
145                 operation_alters_arch = true;
146                 break;
147             case 'r':
148                 operation = replace;
149                 operation_alters_arch = true;
150                 break;
151             case 't':
152                 operation = print_table;
153                 break;
154             case 'x':
155                 operation = extract;
156                 break;
157             }
158         case 'l':
159             break;
160         case 'c':
161             silent_create = 1;
162             break;
163         case 'o':
164             preserve_dates = 1;
165             break;
166         case 's':
167             write_armap = true;
168             break;
169         case 'u':
170             newer_only = 1;
171             break;
172         case 'v':
173             verbose = 1;
174             break;
175         case 'a':
176             postype = pos_after;
177             break;
178         case 'b':
179             postype = pos_before;
180             break;
181         case 'i':
182             postype = pos_before;
183             break;
184         default:
185             fatal("invalid option %c", c);
186         }
187     }
188
189     if (operation == none && write_armap)
190         ranlib_only(argv[2]);
191
192     if (operation == none)
193         fatal("no operation specified");
194
195     if (newer_only && operation != replace)
196         fatal("'u' only meaningful with 'r' option.");
197
198     arg_index = 2;
199
200     if (postype != pos_default)
201         posname = argv[arg_index++];
202
203     inarch_filename = argv[arg_index++];
204
205     if (arg_index < argc) {
206         files = argv + arg_index;
207         while (arg_index < argc)
208             if (!strcmp(argv[arg_index++], "__.SYMDEF")) {
209                 ignore_symdef = 1;
210                 break;
211             }
212     }
213     else
214         files = NULL;
215
216     if (operation == quick_append) {
217         if (files != NULL)
218             do_quick_append(inarch_filename, files);
219         exit(0);
220     }
221
222
223     open_inarch(inarch_filename);
224     /*
225        If we have no archive, and we've been asked to replace then create one
226     */
227 #if 0
228     if (operation == replace && inarch == &bogus_archive) {
229       silent_create = 1;
230       do_quick_append(inarch_filename, 0);
231       open_inarch(inarch_filename);
232     }
233 #endif
234     switch (operation) {
235
236     case print_table:
237         map_over_members(print_descr, files, argc - 3);
238         break;
239
240     case print_files:
241         map_over_members(print_contents, files, argc - 3);
242         break;
243
244     case extract:
245         map_over_members(extract_file, files, argc - 3);
246         break;
247
248     case delete:
249         if (files != NULL)
250             delete_members(files);
251         break;
252
253     case move:
254         if (files != NULL)
255             move_members(files);
256         break;
257
258     case replace:
259         if (files != NULL || write_armap)
260             replace_members(files);
261         break;
262
263         /* Shouldn't happen! */
264     default:
265         fprintf(stderr, "Sorry; this option not implemented.\n");
266     }
267
268     return (0);
269 }                               /* main() */
270
271 static
272 char *normalize(file)
273 char *file;
274 {
275     char *    filename = strrchr(file, '/');
276     if (filename != (char *)NULL) {
277         filename ++;
278     }
279     else {
280         filename = file;
281     }
282     return filename;
283 }
284
285 static void
286 open_inarch(archive_filename)
287     char           *archive_filename;
288 {
289     bfd           **last_one;
290     bfd            *next_one;
291     struct stat     sbuf;
292     bfd_error = no_error;
293     if (stat(archive_filename, &sbuf) != 0) {
294         if (errno != ENOENT)
295             bfd_fatal(archive_filename);
296         if (!operation_alters_arch) {
297           fprintf (stderr, "%s: %s not found.\n", program_name,
298                    archive_filename);
299           exit (1);
300         }       
301         if (!silent_create)
302             fprintf(stderr,
303                     "%s: creating %s\n", program_name, archive_filename);
304
305         inarch = &bogus_archive;
306         inarch->filename = archive_filename;
307         inarch->has_armap = true;
308
309     }
310     else {
311         inarch = bfd_openr(archive_filename, NULL);
312         if (inarch == NULL) {
313     bloser:
314             bfd_perror(archive_filename);
315             exit(1);
316         }
317
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);
323              next_one;
324              next_one = bfd_openr_next_archived_file(inarch, next_one)) {
325             *last_one = next_one;
326             last_one = &next_one->next;
327         }
328         *last_one = (bfd *) NULL;
329         if (bfd_error != no_more_archived_files)
330             goto bloser;
331     }
332 }
333
334
335
336 /*
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
340 */
341 void
342 map_over_members(function, files, count)
343     void            (*function) ();
344     char          **files;
345     int             count;
346 {
347     bfd            *head;
348
349
350
351
352     if (count == 0) {
353         for (head = inarch->next; head; head = head->next)
354             function(head);
355         return;
356     }
357     /*
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
362        references.
363     */
364
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))) {
370                 found = true;
371                 function(head);
372             }
373         if (!found)
374             fprintf(stderr, "No entry %s in archive.\n", *files);
375     }
376 }
377
378
379 /* Things which are interesting to map over all or some of the files: */
380
381 void
382 print_descr(abfd)
383     bfd            *abfd;
384 {
385     print_arelt_descr(abfd, verbose);
386 }
387
388 void
389 print_contents(abfd)
390     bfd            *abfd;
391 {
392     int             ncopied = 0;
393     struct stat     buf;
394     long            size;
395     if (bfd_stat_arch_elt(abfd, &buf) != 0)
396         fatal("Internal stat error on %s", abfd->filename);
397
398     if (verbose)
399         printf("\n<member %s>\n\n", abfd->filename);
400
401     bfd_seek(abfd, 0, SEEK_SET);
402
403     size = buf.st_size;
404     while (ncopied < size) {
405         char            cbuf[BUFSIZE];
406         int             nread;
407         int             tocopy = size - ncopied;
408         if (tocopy > BUFSIZE)
409             tocopy = BUFSIZE;
410
411         nread = bfd_read(cbuf, 1, tocopy, abfd);        /* oops -- broke
412                                                            abstraction!  */
413
414         if (nread != tocopy)
415             fatal("file %s not a valid archive", abfd->my_archive->filename);
416         fwrite(cbuf, 1, nread, stdout);
417         ncopied += tocopy;
418     }
419 }
420
421
422 /*
423    Extract a member of the archive into its own file.
424
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
431    Gilmore
432 */
433
434 void
435 extract_file(abfd)
436     bfd            *abfd;
437 {
438     FILE           *ostream;
439     char            cbuf[BUFSIZE];
440     int             nread,
441                     tocopy;
442     int             ncopied = 0;
443     long            size;
444     struct stat     buf;
445     if (bfd_stat_arch_elt(abfd, &buf) != 0)
446         fatal("Internal stat error on %s", abfd->filename);
447     size = buf.st_size;
448
449     if (verbose)
450         printf("x - %s\n", abfd->filename);
451
452     bfd_seek(abfd, 0, SEEK_SET);
453
454     ostream = 0;
455     if (size == 0) {
456       /* Seems like an abstraction violation, eh?  Well it's OK! */
457       ostream = fopen(abfd->filename, "w");
458       if (!ostream) {
459         perror(abfd->filename);
460         exit(1);
461       }
462     } else
463     while (ncopied < size) {
464         tocopy = size - ncopied;
465         if (tocopy > BUFSIZE)
466             tocopy = BUFSIZE;
467
468         nread = bfd_read(cbuf, 1, tocopy, abfd);
469         if (nread != tocopy)
470             fatal("file %s not a valid archive", abfd->my_archive->filename);
471
472         /* See comment above; this saves disk arm motion */
473         if (!ostream) {
474             /* Seems like an abstraction violation, eh?  Well it's OK! */
475             ostream = fopen(abfd->filename, "w");
476             if (!ostream) {
477                 perror(abfd->filename);
478                 exit(1);
479             }
480         }
481         fwrite(cbuf, 1, nread, ostream);
482         ncopied += tocopy;
483     }
484
485     fclose(ostream);
486     chmod(abfd->filename, buf.st_mode);
487
488     if (preserve_dates) {
489 #ifdef USG
490         long            tb[2];
491         tb[0] = buf.st_mtime;
492         tb[1] = buf.st_mtime;
493         utime(abfd->filename, tb);      /* FIXME check result */
494 #else
495         struct timeval  tv[2];
496         tv[0].tv_sec = buf.st_mtime;
497         tv[0].tv_usec = 0;
498         tv[1].tv_sec = buf.st_mtime;
499         tv[1].tv_usec = 0;
500         utimes(abfd->filename, tv);     /* FIXME check result */
501 #endif
502     }
503 }
504
505
506 /* Just do it quickly; don't worry about dups, armap, or anything like that */
507
508 /* This is ugly! XXX */
509
510 PROTO(struct ar_hdr *, bfd_special_undocumented_glue, (char *filename));
511
512 void
513 do_quick_append(archive_filename, files_to_append)
514     char           *archive_filename;
515     char          **files_to_append;
516
517 {
518     FILE           *ofile,
519                    *ifile;
520     char            buf[BUFSIZE];
521     long            tocopy,
522                     thistime;
523     bfd            *temp;
524     struct stat     sbuf;
525     boolean         newfile = false;
526     bfd_error = no_error;
527
528     if (stat(archive_filename, &sbuf) != 0) {
529         if (errno != ENOENT)
530             bfd_fatal(archive_filename);
531         newfile = true;
532     }
533
534
535     ofile = fopen(archive_filename, "a+");
536     if (ofile == NULL) {
537         perror(program_name);
538         exit(1);
539     }
540
541     /* bletch */
542     temp = bfd_openr(archive_filename, NULL);
543     if (temp == NULL) {
544         bfd_perror(archive_filename);
545         exit(1);
546     }
547     if (newfile == false) {
548         if (bfd_check_format(temp, bfd_archive) != true)
549             fatal("File %s is not an archive.", archive_filename);
550     }
551     else {
552         fwrite(ARMAG, 1, SARMAG, ofile);
553         if (!silent_create)
554             fprintf(stderr, "%s: creating %s\n", program_name, archive_filename);
555     }
556
557     /* assume it's an achive, go straight to the end, sans $200 */
558     fseek(ofile, 0, 2);
559
560     for (; files_to_append && *files_to_append; ++files_to_append) {
561         struct ar_hdr  *hdr = bfd_special_undocumented_glue(*files_to_append);
562         if (hdr == NULL) {
563             bfd_perror(*files_to_append);
564             exit(1);
565         }
566
567         BFD_SEND(temp, _bfd_truncate_arname, (temp, *files_to_append, (char *) hdr));
568
569         ifile = fopen(*files_to_append, "r");
570         if (ifile == NULL)
571             bfd_perror(program_name);
572
573         if (stat(*files_to_append, &sbuf) != 0)
574             bfd_perror(*files_to_append);
575
576         tocopy = sbuf.st_size;
577
578         /* XXX should do error-checking! */
579         fwrite(hdr, 1, sizeof(struct ar_hdr), ofile);
580
581
582         while (tocopy > 0) {
583             thistime = tocopy;
584             if (thistime > BUFSIZE)
585                 thistime = BUFSIZE;
586             fread(buf, 1, thistime, ifile);
587             fwrite(buf, 1, thistime, ofile);
588             tocopy -= thistime;
589         }
590         fclose(ifile);
591         if ((sbuf.st_size % 2) == 1)
592             putc('\n', ofile);
593     }
594     fclose(ofile);
595     bfd_close(temp);
596 }
597
598
599 void
600 write_archive()
601 {
602     bfd            *obfd;
603     char           *xmalloc();
604     int             namelen = strlen(inarch->filename);
605     char           *new_name = xmalloc(namelen + 6);
606     bfd            *contents_head = inarch->next;
607 #if 0
608     if (inarch == &bogus_archive) {
609         /* How can this be ? */
610         return;
611     }
612     else {
613 #endif
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));
619
620         if (obfd == NULL)
621             bfd_fatal(inarch->filename);
622
623         bfd_set_format(obfd, bfd_archive);
624         obfd->has_armap = write_armap;
625
626         if (bfd_set_archive_head(obfd, contents_head) != true)
627             bfd_fatal(inarch->filename);
628
629         if (!bfd_close(obfd))
630             bfd_fatal(inarch->filename);
631         if (rename(new_name, inarch->filename) != 0)
632             bfd_fatal(inarch->filename);
633 #if 0
634     }
635 #endif
636 }
637
638
639
640 /*
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.
644 */
645
646 bfd **
647 get_pos_bfd(contents, default_pos)
648     bfd           **contents;
649     enum pos        default_pos;
650 {
651     bfd           **after_bfd;
652
653     enum pos        realpos = (postype == pos_default ? default_pos : postype);
654     switch (realpos) {
655
656     case pos_end:
657         after_bfd = contents;
658         while (*after_bfd) {
659             after_bfd = &((*after_bfd)->next);
660         }
661
662         break;
663 #if 0
664     case pos_after:
665         for (after_bfd = contents; after_bfd; after_bfd = after_bfd->next)
666             if (!strcpy(after_bfd->filename, posname))
667                 break;
668         break;
669     case pos_before:
670         for (after_bfd = contents; after_bfd; after_bfd = after_bfd->next)
671             if (after_bfd->next && (!strcpy(after_bfd->next->filename, posname)))
672                 break;
673 #endif
674     }
675
676     return after_bfd;
677 }
678
679
680 void
681 delete_members(files_to_delete)
682     char          **files_to_delete;
683 {
684     bfd           **current_ptr_ptr;
685     boolean         found;
686     boolean         something_changed = false;
687     for (; *files_to_delete != NULL; ++files_to_delete) {
688         /*
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
693         */
694         if (!strcmp(*files_to_delete, "__.SYMDEF")) {
695             inarch->has_armap = false;
696             write_armap = false;
697             continue;
698         }
699
700         found = false;
701         current_ptr_ptr = &(inarch->next);
702         while (*current_ptr_ptr) {
703             if (strcmp(*files_to_delete, (*current_ptr_ptr)->filename) == 0) {
704                 found = true;
705                 something_changed = true;
706                 if (verbose)
707                     printf("d - %s\n",
708                            *files_to_delete);
709                 *current_ptr_ptr = ((*current_ptr_ptr)->next);
710                 goto next_file;
711
712             }
713             else {
714                 current_ptr_ptr = &((*current_ptr_ptr)->next);
715             }
716         }
717
718         if (verbose && found == false) {
719             printf("No member named `%s'\n", *files_to_delete);
720         }
721 next_file:;
722
723     }
724
725     if (something_changed == true) {
726         write_archive();
727     }
728 }
729
730
731 /* Reposition existing members within an archive */
732
733 void
734 move_members(files_to_move)
735     char          **files_to_move;
736 {
737     bfd           **after_bfd;  /* New entries go after this one */
738     bfd           **current_ptr_ptr;    /* cdr pointer into contents */
739
740
741
742
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) {
748                 /*
749                    Move this file to the end of the list - first cut from
750                    where it is.
751                 */
752                 *current_ptr_ptr = current_ptr->next;
753
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;
758
759                 if (verbose)
760                     printf("m - %s\n", *files_to_move);
761
762                 goto next_file;
763             }
764             current_ptr_ptr = &((*current_ptr_ptr)->next);
765         }
766         fprintf(stderr, "No entry %s in archive %s!\n",
767                 *files_to_move, inarch->filename);
768         exit(1);
769 next_file:;
770     }
771
772     write_archive();
773 }
774
775
776 /* Ought to default to replacing in place, but this is existing practice! */
777
778 void
779 replace_members(files_to_move)
780     char          **files_to_move;
781 {
782     bfd           **after_bfd;  /* New entries go after this one */
783     bfd            *current;
784     bfd           **current_ptr;
785     bfd            *temp;
786     /*
787        If the first item in the archive is an __.SYMDEF then remove it
788     */
789     if (inarch->next &&
790         strcmp(inarch->next->filename, "__.SYMDEF") == 0) {
791         inarch->next = inarch->next->next;
792     }
793
794
795
796     while (files_to_move && *files_to_move) {
797         current_ptr = &inarch->next;
798         while (*current_ptr) {
799             current = *current_ptr;
800             
801             if (!strcmp(normalize(*files_to_move), current->filename)) {
802                 /* snip out this entry from the chain */
803                 *current_ptr = current->next;
804                 if (newer_only) {
805                     struct stat     fsbuf,
806                                     asbuf;
807                     if (stat(*files_to_move, &fsbuf) != 0) {
808                         if (errno != ENOENT)
809                             bfd_fatal(*files_to_move);
810                         goto next_file;
811                     }
812                     if (bfd_stat_arch_elt(current, &asbuf) != 0)
813                         fatal("Internal stat error on %s", current->filename);
814
815                     if (fsbuf.st_mtime <= asbuf.st_mtime)
816                         goto next_file;
817                 }
818
819
820                 after_bfd = get_pos_bfd(&inarch->next, pos_end);
821                 temp = *after_bfd;
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);
825                     exit(1);
826                 }
827                 (*after_bfd)->next = temp;
828
829                 if (verbose) {
830                     printf("%c - %s\n", (postype == pos_after ? 'r' : 'a'),
831                            *files_to_move);
832                 }
833                 goto next_file;
834             }
835             current_ptr = &(current->next);
836         }
837
838         /* It isn't in there, so add to end */
839
840         after_bfd = get_pos_bfd(&inarch->next, pos_end);
841         temp = *after_bfd;
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);
845             exit(1);
846         }
847         if (verbose) {
848             printf("c - %s\n", *files_to_move);
849         }
850
851         (*after_bfd)->next = temp;
852
853 next_file:;
854
855         files_to_move++;
856     }
857
858
859     write_archive();
860 }
861
862 void
863 ranlib_only(archname)
864     char           *archname;
865 {
866     write_armap = true;
867     open_inarch(archname);
868     write_archive();
869     exit(0);
870 }