home *** CD-ROM | disk | FTP | other *** search
/ M.u.C.S. Disc 2000 / MUCS2000.iso / gnuc / util-41s.lzh / util-41 / ar.c next >
C/C++ Source or Header  |  1998-10-05  |  52KB  |  2,029 lines

  1. /* ar.c - Archive modify and extract.
  2.    Copyright (C) 1988 Free Software Foundation, Inc.
  3.  
  4.                NO WARRANTY
  5.  
  6.   BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
  7. NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
  8. WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
  9. RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
  10. WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
  11. BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  12. FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
  13. AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
  14. DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
  15. CORRECTION.
  16.  
  17.  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
  18. STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
  19. WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
  20. LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
  21. OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
  22. USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
  23. DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
  24. A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
  25. PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
  26. DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
  27.  
  28.         GENERAL PUBLIC LICENSE TO COPY
  29.  
  30.   1. You may copy and distribute verbatim copies of this source file
  31. as you receive it, in any medium, provided that you conspicuously
  32. and appropriately publish on each copy a valid copyright notice
  33. "Copyright (C) 1987 Free Software Foundation, Inc.", and include
  34. following the copyright notice a verbatim copy of the above disclaimer
  35. of warranty and of this License.
  36.  
  37.   2. You may modify your copy or copies of this source file or
  38. any portion of it, and copy and distribute such modifications under
  39. the terms of Paragraph 1 above, provided that you also do the following:
  40.  
  41.     a) cause the modified files to carry prominent notices stating
  42.     that you changed the files and the date of any change; and
  43.  
  44.     b) cause the whole of any work that you distribute or publish,
  45.     that in whole or in part contains or is a derivative of this
  46.     program or any part thereof, to be licensed at no charge to all
  47.     third parties on terms identical to those contained in this
  48.     License Agreement (except that you may choose to grant more
  49.     extensive warranty protection to third parties, at your option).
  50.  
  51.     c) You may charge a distribution fee for the physical act of
  52.     transferring a copy, and you may at your option offer warranty
  53.     protection in exchange for a fee.
  54.  
  55.   3. You may copy and distribute this program or any portion of it in
  56. compiled, executable or object code form under the terms of Paragraphs
  57. 1 and 2 above provided that you do the following:
  58.  
  59.     a) cause each such copy to be accompanied by the
  60.     corresponding machine-readable source code, which must
  61.     be distributed under the terms of Paragraphs 1 and 2 above; or,
  62.  
  63.     b) cause each such copy to be accompanied by a
  64.     written offer, with no time limit, to give any third party
  65.     free (except for a nominal shipping charge) a machine readable
  66.     copy of the corresponding source code, to be distributed
  67.     under the terms of Paragraphs 1 and 2 above; or,
  68.  
  69.     c) in the case of a recipient of this program in compiled, executable
  70.     or object code form (without the corresponding source code) you
  71.     shall cause copies you distribute to be accompanied by a copy
  72.     of the written offer of source code which you received along
  73.     with the copy you received.
  74.  
  75.   4. You may not copy, sublicense, distribute or transfer this program
  76. except as expressly provided under this License Agreement.  Any attempt
  77. otherwise to copy, sublicense, distribute or transfer this program is void and
  78. your rights to use the program under this License agreement shall be
  79. automatically terminated.  However, parties who have received computer
  80. software programs from you with this License Agreement will not have
  81. their licenses terminated so long as such parties remain in full compliance.
  82.  
  83.   5. If you wish to incorporate parts of this program into other free
  84. programs whose distribution conditions are different, write to the Free
  85. Software Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
  86. worked out a simple rule that can be stated here, but we will often permit
  87. this.  We will be guided by the two goals of preserving the free status of
  88. all derivatives of our free software and of promoting the sharing and reuse of
  89. software.
  90.  
  91.  In other words, you are welcome to use, share and improve this program.
  92.  You are forbidden to forbid anyone else to use, share and improve
  93.  what you give them.   Help stamp out software-hoarding!  */
  94.  
  95. #ifdef BYTE_SWAP
  96.  
  97. #define SWAP4(y) (((unsigned)(y)>>24) + (((unsigned)(y)>>8)&0xff00) + \
  98.          (((unsigned)(y)<<8)&0xff0000) + ((unsigned)(y)<<24)) 
  99. #define SWAP2(y) ((((unsigned)(y)&0xff00)>>8) + (((unsigned)(y)&0x00ff)<<8))
  100.  
  101. #endif /* BYTE_SWAP */
  102.  
  103. #ifdef CROSSATARI
  104.  
  105. #ifdef USG
  106. #define bcopy(a,b,c) memcpy (b,a,c)
  107. #define bzero(a,b) memset (a,0,b)
  108. #define bcmp(a,b,c) memcmp (a,b,c)
  109. #endif /* USG */
  110. #include <stdio.h>
  111. #include <errno.h>
  112. #include <sys/file.h>
  113. #include <sys/time.h>
  114. #include <sys/types.h>
  115. #include <sys/stat.h>
  116. #include "cross-inc/gnu-ar.h"
  117. #include "cross-inc/gnu-out.h"
  118.  
  119. #else
  120.  
  121. #include <stdio.h>
  122. #include <gnu-ar.h>
  123. #include <gnu-out.h>
  124. #include <errno.h>
  125. #include <file.h>
  126. #include <time.h>
  127. #include <types.h>
  128. #include <stat.h>
  129.  
  130. #endif /* cross atari */
  131.  
  132.   /* Locking is normally disabled because fcntl hangs on the Sun
  133.      and it isn't supported properly across NFS anyhow.  */
  134.  
  135. #ifdef LOCKS
  136. #include <sys/fcntl.h>
  137. #endif
  138.  
  139. /* Define a name for the length of member name an archive can store.  */
  140.  
  141. struct ar_hdr foo;
  142.  
  143. #define ARNAMESIZE sizeof(foo.ar_name)
  144.  
  145. /* This structure is used internally to represent the info
  146.  on a member of an archive.  This is to make it easier to change format.  */
  147.  
  148. struct member_desc
  149.   {
  150.     char *name;        /* Name of member */
  151. /* The following fields are stored in the member header
  152.    as decimal or octal numerals,
  153.    but in this structure they are stored as machine numbers.  */
  154.     int mode;        /* Protection mode from member header */
  155.  
  156. #ifdef mips            /* TUHH CF */
  157.      time_t date;
  158. #else
  159.     int date;        /* Last modify date as stored in member header */
  160. #endif
  161.  
  162.     int size;        /* size of member's data in bytes, from member header */
  163.     int uid, gid;    /* uid and gid fields copied from member header */
  164.     int offset;        /* Offset in archive of the header of this member */
  165.     int data_offset;    /* Offset of first data byte of the member */
  166. /* The next field does not describe where the member was in the old archive,
  167.  but rather where it will be in the modified archive.
  168.  It is set up by write_archive.  */
  169.     int new_offset;    /* Offset of this member in new archive */
  170.     struct symdef *symdefs;    /* Symdef data for member.
  171.                    Used only for files being inserted.  */
  172.     int nsymdefs;    /* Number of entries of symdef data */
  173.     int string_size;    /* Size of strings needed by symdef data */
  174.   };
  175.  
  176. /* Each symbol is recorded by something like this.  */
  177.  
  178. struct symdef
  179.   {
  180.     union
  181.       {
  182.     long stringoffset;
  183.     char *name;
  184.       } s;
  185.     long offset;
  186.   };
  187.  
  188. /* Nonzero means it's the name of an existing member;
  189.    position new or moved files with respect to this one.  */
  190.  
  191. char *posname;
  192.  
  193.  
  194. /* How to use `posname':
  195.    POS_BEFORE means position before that member.
  196.    POS_AFTER means position after that member.
  197.    POS_DEFAULT if position by default; then `posname' should also be zero. */
  198.  
  199. enum { POS_DEFAULT, POS_BEFORE, POS_AFTER } postype;
  200.  
  201. /* Nonzero means describe each action performed.  */
  202.  
  203. int verbose;
  204.  
  205. /* Nonzero means don't warn about creating the archive file if necessary.  */
  206.  
  207. int silent_create;
  208.  
  209. /* Nonzero means don't replace existing members
  210.  whose dates are more recent than the corresponding files.  */
  211.  
  212. int newer_only;
  213.  
  214. /* Nonzero means preserve dates of members when extracting them.  */
  215.  
  216. int preserve_dates;
  217.  
  218. /* Operation to be performed.  */
  219.  
  220. #define DELETE 1
  221. #define REPLACE 2
  222. #define PRINT_TABLE 3
  223. #define PRINT_FILES 4
  224. #define EXTRACT 5
  225. #define MOVE 6
  226. #define QUICK_APPEND 7
  227.  
  228. int operation;
  229.  
  230. /* Name of archive file.  */
  231.  
  232. char *archive;
  233.  
  234. /* Descriptor on which we have locked the original archive file,
  235.    or -1 if this has not been done.  */
  236.  
  237. int lock_indesc;
  238.  
  239. /* Pointer to tail of `argv', at first subfile name argument,
  240.  or zero if no such were specified.  */
  241.  
  242. char **files;
  243.  
  244. /* Nonzero means write a __.SYMDEF member into the modified archive.  */
  245.  
  246. int symdef_flag;
  247.  
  248. /* Nonzero means __.SYMDEF member exists in old archive.  */
  249.  
  250. int symdef_exists;
  251.  
  252. /* Total number of symdef entries we will have. */
  253.  
  254. long nsymdefs;
  255.  
  256. /* Symdef data from old archive (set up only if we need it) */
  257.  
  258. struct symdef *old_symdefs;
  259.  
  260. /* Number of symdefs in remaining in old_symdefs.  */
  261.  
  262. int num_old_symdefs;
  263.  
  264. /* Number of symdefs old_symdefs had when it was read in.  */
  265.  
  266. long original_num_symdefs;
  267.  
  268. /* String table from old __.SYMDEF member.  */
  269.  
  270. char *old_strings;
  271.  
  272. /* Size of old_strings */
  273.  
  274. long old_strings_size;
  275.  
  276. /* String table to be written into __.SYMDEF member.  */
  277.  
  278. char *new_strings;
  279.  
  280. /* Size of new_strings */
  281.  
  282. long new_strings_size;
  283.  
  284. /* An archive map is a chain of these structures.
  285.   Each structure describes one member of the archive.
  286.   The chain is in the same order as the members are.  */
  287.  
  288. struct mapelt
  289.   {
  290.     struct member_desc info;
  291.     struct mapelt *next;
  292.   };
  293.  
  294. struct mapelt *maplast;
  295.  
  296. /* If nonzero, this is the map-element for the __.SYMDEF member
  297.    and we should update the time of that member just before finishing.  */
  298.  
  299. struct mapelt *symdef_mapelt;
  300.  
  301. /* Header that we wrote for the __.SYMDEF member.  */
  302.  
  303. struct ar_hdr symdef_header;
  304.  
  305. extern void dump_version(char *prog);
  306. char *progname = "ar";
  307.  
  308. void add_to_map ();
  309. void print_descr ();
  310. char *concat ();
  311. void scan ();
  312. char *requestedp ();
  313. void extract_member ();
  314. void print_contents ();
  315. void write_symdef_member ();
  316. #if 0
  317. void read_old_symdefs ();
  318. #endif
  319.  
  320. main (argc, argv)
  321.      int argc;
  322.      char **argv;
  323. {
  324.   int i;
  325.  
  326.   operation = 0;
  327.   verbose = 0;
  328.   newer_only = 0;
  329.   silent_create = 0;
  330.   posname = 0;
  331.   postype = POS_DEFAULT;
  332.   preserve_dates = 0;
  333.   symdef_flag = 0;
  334.   symdef_exists = 0;
  335.   symdef_mapelt = 0;
  336.   files = 0;
  337.   lock_indesc = -1;
  338.  
  339.   if (argv[0][0] != '\0')
  340.     progname = argv[0];
  341.  
  342.   if (argc < 2)
  343.     fatal ("too few command arguments", 0);
  344.   {
  345.     char *key = argv[1];
  346.     char *p = key;
  347.     char c;
  348.  
  349.     if (strcmp(key, "-v") == 0)
  350.     {
  351.       dump_version(progname);
  352.       exit(0);
  353.     }
  354.     
  355.     while (c = *p++)
  356.       {
  357.     switch (c)
  358.       {
  359.       case 'a':
  360.         postype = POS_AFTER;
  361.         break;
  362.  
  363.       case 'b':
  364.         postype = POS_BEFORE;
  365.         break;
  366.  
  367.       case 'c':
  368.         silent_create = 1;
  369.         break;
  370.  
  371.       case 'd':
  372.         if (operation)
  373.           two_operations ();
  374.  
  375.         operation = DELETE;
  376.         break;
  377.  
  378.       case 'i':
  379.         postype = POS_BEFORE;
  380.         break;
  381.  
  382.       case 'l':
  383.         break;
  384.  
  385.       case 'm':
  386.         if (operation) two_operations ();
  387.         operation = MOVE;
  388.         break;
  389.  
  390.       case 'o':
  391.         preserve_dates = 1;
  392.         break;
  393.  
  394.       case 'p':
  395.         if (operation) two_operations ();
  396.         operation = PRINT_FILES;
  397.         break;
  398.  
  399.       case 'q':
  400.         if (operation) two_operations ();
  401.         operation = QUICK_APPEND;
  402.         break;
  403.  
  404.       case 'r':
  405.         if (operation) two_operations ();;
  406.         operation = REPLACE;
  407.         break;
  408.  
  409.       case 's':
  410.         symdef_flag = 1;
  411.         break;
  412.  
  413.       case 't':
  414.         if (operation) two_operations ();
  415.         operation = PRINT_TABLE;
  416.         break;
  417.  
  418.       case 'u':
  419.         newer_only = 1;
  420.         break;
  421.  
  422.       case 'v':
  423.         verbose = 1;
  424.         break;
  425.  
  426.       case 'x':
  427.         if (operation) two_operations ();
  428.         operation = EXTRACT;
  429.         break;
  430.       }
  431.       }
  432.   
  433.   }
  434.  
  435.   if (!operation && symdef_flag)
  436.     operation = REPLACE;
  437.  
  438.   if (!operation)
  439.     fatal ("no operation specified", 0);
  440.  
  441.   i = 2;
  442.  
  443.   if (postype != POS_DEFAULT)
  444.     posname = argv[i++];
  445.  
  446.   archive = argv[i++];
  447.  
  448.   if (i < argc)
  449.     files = &argv[i];
  450.  
  451.   switch (operation)
  452.     {
  453.     case EXTRACT:
  454.     extract_members (extract_member);
  455.     break;
  456.  
  457.     case PRINT_TABLE:
  458.     extract_members (print_descr);
  459.     break;
  460.  
  461.     case PRINT_FILES:
  462.     extract_members (print_contents);
  463.     break;
  464.  
  465.     case DELETE:
  466.     if (!files) break;
  467.     delete_members ();
  468.     break;
  469.  
  470.     case MOVE:
  471.     if (!files) break;
  472.     move_members ();
  473.     break;
  474.  
  475.     case REPLACE:
  476.     if (!files && !symdef_flag) break;
  477.     replace_members ();
  478.     break;
  479.  
  480.     case QUICK_APPEND:
  481.     if (!files) break;
  482.     quick_append ();
  483.     break;
  484.  
  485.     default:
  486.     fatal ("operation not implemented yet", 0);
  487.     }
  488.     exit(0);
  489. }
  490.  
  491. two_operations ()
  492. {
  493.   fatal ("two different operation switches specified", 0);
  494. }
  495.  
  496. void
  497. scan (function, crflag)
  498.      void (*function) ();
  499.      int crflag;
  500. {
  501.   int desc = open (archive, 0, 0);
  502.  
  503.   if (desc < 0 && crflag)
  504.     /* Creation-warning, if desired, will happen later.  */
  505.     return;
  506.  
  507.   if (desc < 0)
  508.     {
  509.       perror_with_name (archive);
  510.       exit (1);
  511.     }
  512.   {
  513.     char buf[SARMAG];
  514.     int nread = read (desc, buf, SARMAG);
  515.     if (nread != SARMAG || bcmp (buf, ARMAG, SARMAG))
  516.       fatal ("file %s not a valid archive", archive);
  517.   }
  518.  
  519.   /* Now find the members one by one.  */
  520.   {
  521.     int member_offset = SARMAG;
  522.     while (1)
  523.       {
  524.     int nread;
  525.     struct ar_hdr member_header;
  526.     struct member_desc member_desc;
  527.     char name [1 + sizeof member_header.ar_name];
  528.  
  529.     if (lseek (desc, member_offset, 0) < 0)
  530.       perror_with_name (archive);
  531.  
  532. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  533.     nread = read (desc, &member_header.ar_name, 
  534.               sizeof (member_header.ar_name));
  535.     nread += read (desc, &member_header.ar_size, 
  536.                sizeof (member_header.ar_size));
  537.     nread += read (desc, &member_header.ar_date, 
  538.                sizeof (member_header.ar_date));
  539.     nread += read (desc, &member_header.ar_mode, 
  540.                sizeof (member_header.ar_mode));
  541.     nread += read (desc, &member_header.ar_uid, 
  542.                sizeof (member_header.ar_uid));
  543.     nread += read (desc, &member_header.ar_gid, 
  544.                sizeof (member_header.ar_gid));
  545.     nread += read (desc, &member_header.ar_fmag, 
  546.                sizeof (member_header.ar_fmag));
  547. #else
  548.     nread = read (desc, &member_header, sizeof (struct ar_hdr));
  549. #endif
  550.     if (!nread) break;    /* No data left means end of file; that is ok */
  551.  
  552. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  553.     if (nread != (sizeof (member_header.ar_name) +
  554.               sizeof (member_header.ar_size) +
  555.               sizeof (member_header.ar_date) +
  556.               sizeof (member_header.ar_mode) +
  557.               sizeof (member_header.ar_uid) +
  558.               sizeof (member_header.ar_gid) +
  559.               sizeof (member_header.ar_fmag))
  560.         || bcmp (member_header.ar_fmag, ARFMAG, 2))
  561.       fatal ("file %s not a valid archive", archive);
  562. #else
  563.     if (nread != sizeof (member_header)
  564.         || bcmp (member_header.ar_fmag, ARFMAG, 2))
  565.       fatal ("file %s not a valid archive", archive);
  566. #endif
  567.     bcopy (member_header.ar_name, name, sizeof member_header.ar_name);
  568.     {
  569.       char *p = name + sizeof member_header.ar_name;
  570.       while (p > name && *--p == ' ') *p = 0;
  571.     }
  572.     bzero(&member_desc, sizeof(member_desc));
  573.     member_desc.name = name;
  574.     member_desc.date = atoi (member_header.ar_date);
  575.     member_desc.size = atoi (member_header.ar_size);
  576.     sscanf (member_header.ar_mode, "%o", &member_desc.mode);
  577.     member_desc.uid = atoi (member_header.ar_uid);
  578.     member_desc.gid = atoi (member_header.ar_gid);
  579.     member_desc.offset = member_offset;
  580. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  581.     member_desc.data_offset = member_offset + 
  582.       (sizeof (member_header.ar_name) +
  583.        sizeof (member_header.ar_size) +
  584.        sizeof (member_header.ar_date) +
  585.        sizeof (member_header.ar_mode) +
  586.        sizeof (member_header.ar_uid) +
  587.        sizeof (member_header.ar_gid) +
  588.        sizeof (member_header.ar_fmag));
  589. #else
  590.     member_desc.data_offset = member_offset + sizeof (member_header);
  591. #endif 
  592.  
  593.     if (!strcmp (name, "__.SYMDEF"))
  594.       symdef_exists = 1;
  595.  
  596.     function (member_desc, desc);
  597.  
  598. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  599.     member_offset += (sizeof (member_header.ar_name) +
  600.               sizeof (member_header.ar_size) +
  601.               sizeof (member_header.ar_date) +
  602.               sizeof (member_header.ar_mode) +
  603.               sizeof (member_header.ar_uid) +
  604.               sizeof (member_header.ar_gid) +
  605.               sizeof (member_header.ar_fmag)) + member_desc.size;
  606. #else
  607.     member_offset += sizeof (member_header) + member_desc.size;
  608. #endif
  609.     if (member_offset & 1) member_offset++;
  610.       }
  611.   }
  612.  
  613.   close (desc);
  614. }
  615.  
  616. /* If the string `name' matches one of the member-name arguments
  617.    from the command line, return nonzero.  Otherwise return 0.
  618.    Only the first `len' characters of `name' need to match.  */
  619. /* In fact, the value is the command argument that `name' matched.  */
  620.  
  621. char *
  622. requestedp (name, len)
  623.      char *name;
  624.      int len;
  625. {
  626.   char **fp = files;
  627.   char *fn;
  628.  
  629.   if (fp == 0)
  630.     return 0;
  631.  
  632.   while (fn = *fp++)
  633.     if (!strncmp (name, fn, len))
  634.       return fn;
  635.  
  636.   return 0;
  637. }
  638.  
  639. void
  640. print_descr (member)
  641.      struct member_desc member;
  642. {
  643.   char *timestring;
  644.   extern char *ctime();
  645.   
  646.   if (!verbose)
  647.     {
  648.       printf ("%s\n", member.name);
  649.       return;
  650.     }
  651.   print_modes (member.mode);
  652.   timestring = ctime (&member.date);
  653.   printf (" %2d/%2d %6d %12.12s %4.4s %s\n",
  654.       member.uid, member.gid,
  655.       member.size, timestring + 4, timestring + 20,
  656.       member.name);
  657. }
  658.  
  659. print_modes (modes)
  660.      int modes;
  661. {
  662.   putchar (modes & 0400 ? 'r' : '-');
  663.   putchar (modes & 0200 ? 'w' : '-');
  664.   putchar (modes & 0100 ? 'x' : '-');
  665.   putchar (modes & 040 ? 'r' : '-');
  666.   putchar (modes & 020 ? 'w' : '-');
  667.   putchar (modes & 010 ? 'x' : '-');
  668.   putchar (modes & 04 ? 'r' : '-');
  669.   putchar (modes & 02 ? 'w' : '-');
  670.   putchar (modes & 01 ? 'x' : '-');
  671. }
  672.  
  673. #define BUFSIZE 1024
  674. void
  675. extract_member (member, arcdesc)
  676.      struct member_desc member;
  677.      int arcdesc;
  678. {
  679.   int ncopied = 0;
  680.   FILE  *ostream;
  681.   extern FILE *fopen();
  682.   long pos;
  683.   
  684.   pos = lseek (arcdesc, member.data_offset, 0);
  685. #ifdef atarist
  686.   ostream = fopen (member.name, "wb");
  687. #else
  688.   ostream = fopen (member.name, "w");
  689. #endif
  690.   if (!ostream)
  691.     {
  692.       perror_with_name (member.name);
  693.       return;
  694.     }
  695.  
  696.   if (verbose)
  697.     printf ("extracting %s\n", member.name);
  698.  
  699.   while (ncopied < member.size)
  700.     {
  701.       char buf [BUFSIZE];
  702.       int tocopy = member.size - ncopied;
  703.       int nread;
  704.       if (tocopy > BUFSIZE) tocopy = BUFSIZE;
  705.       nread = read (arcdesc, buf, tocopy);
  706.       if (nread != tocopy)
  707.     fatal ("file %s not a valid archive", archive);
  708.       fwrite (buf, 1, nread, ostream);
  709.       ncopied += tocopy;
  710.     }
  711.   lseek(arcdesc, pos, 0);
  712.  
  713. #ifdef USG
  714.   fclose (ostream);
  715.   chmod (member.name, member.mode);
  716. #else
  717. #ifndef atarist
  718.   fchmod (fileno (ostream), member.mode);
  719. #endif
  720.   fclose (ostream);
  721. #endif
  722.   if (preserve_dates)
  723.     {
  724. #if (defined(atarist) || defined(USG) || defined(CROSSHPUX))
  725. #if defined(USG) || defined(CROSSHPUX)
  726.       struct utimbuf
  727.     {
  728.       time_t actime;
  729.       time_t modtime;
  730.      } tv;
  731.       tv.actime = member.date;
  732.       tv.modtime = member.date;
  733.       utime (member.name, &tv);
  734. #else
  735.       time_t tv[2];
  736.       tv[0] = member.date;
  737.       tv[1] = member.date;
  738.       utime (member.name, tv);
  739. #endif
  740. #else
  741.       struct timeval tv[2];
  742.       tv[0].tv_sec = member.date;
  743.       tv[0].tv_usec = 0;
  744.       tv[1].tv_sec = member.date;
  745.       tv[1].tv_usec = 0;
  746.       utimes (member.name, tv);
  747. #endif
  748.     }
  749. }
  750.  
  751.  
  752. void
  753. print_contents (member, arcdesc)
  754.      struct member_desc member;
  755.      int arcdesc;
  756. {
  757.   int ncopied = 0;
  758.   long pos;
  759.   
  760.   pos = lseek (arcdesc, member.data_offset, 0);
  761.  
  762.   if (verbose)
  763.   printf ("\n<member %s>\n\n", member.name);
  764.  
  765.   while (ncopied < member.size)
  766.     {
  767.       char buf [BUFSIZE];
  768.       int tocopy = member.size - ncopied;
  769.       int nread;
  770.       if (tocopy > BUFSIZE) tocopy = BUFSIZE;
  771.       nread = read (arcdesc, buf, tocopy);
  772.       if (nread != tocopy)
  773.     fatal ("file %s not a valid archive", archive);
  774.       fwrite (buf, 1, nread, stdout);
  775.       ncopied += tocopy;
  776.     }
  777.     lseek(arcdesc, pos, 0);
  778. }
  779.  
  780.  
  781. /* Make a map of the existing members of the archive: their names,
  782.  positions and sizes.  */
  783.  
  784. /* If `nonexistent_ok' is nonzero,
  785.  just return 0 for an archive that does not exist.
  786.  This will cause the ordinary supersede procedure to
  787.  create a new archive.  */
  788.  
  789. struct mapelt *
  790. make_map (nonexistent_ok)
  791.      int nonexistent_ok;
  792. {
  793.   struct mapelt mapstart;
  794.   mapstart.next = 0;
  795.   maplast = &mapstart;
  796.   scan (add_to_map, nonexistent_ok);
  797.   return mapstart.next;
  798. }
  799.  
  800. void
  801. add_to_map (member)
  802.      struct member_desc member;
  803. {
  804.   struct mapelt *mapelt = (struct mapelt *) xmalloc (sizeof (struct mapelt));
  805.   mapelt->info = member;
  806.   mapelt->info.name = concat (mapelt->info.name, "", "");
  807.   maplast->next = mapelt;
  808.   mapelt->next = 0;
  809.   maplast = mapelt;
  810. }
  811.  
  812. /* Return the last element of the specified map.  */
  813.  
  814. struct mapelt *
  815. last_mapelt (map)
  816.      struct mapelt *map;
  817. {
  818.   struct mapelt *tail = map;
  819.   while (tail->next) tail = tail->next;
  820.   return tail;
  821. }
  822.  
  823. /* Return the element of the specified map which precedes elt.  */
  824.  
  825. struct mapelt *
  826. prev_mapelt (map, elt)
  827.      struct mapelt *map, *elt;
  828. {
  829.   struct mapelt *tail = map;
  830.   while (tail->next && tail->next != elt)
  831.     tail = tail->next;
  832.   if (tail->next) return tail;
  833.   return 0;
  834. }
  835.  
  836. /* Return the element of the specified map which has the specified name.  */
  837.  
  838. struct mapelt *
  839. find_mapelt (map, name)
  840.      struct mapelt *map;
  841.      char *name;
  842. {
  843.   struct mapelt *tail = map;
  844.   for (;tail; tail = tail->next)
  845.     if (tail->info.name && !strcmp (tail->info.name, name))
  846.       return tail;
  847.   error ("no member named %s", name);
  848.   return 0;
  849. }
  850.  
  851. struct mapelt *
  852. find_mapelt_noerror (map, name)
  853.      struct mapelt *map;
  854.      char *name;
  855. {
  856.   struct mapelt *tail = map;
  857.   for (;tail; tail = tail->next)
  858.     if (tail->info.name && !strcmp (tail->info.name, name))
  859.       return tail;
  860.   return 0;
  861. }
  862.  
  863. /* Before looking at the archive, if we are going to update it
  864.    based on looking at its current contents, make an exclusive lock on it.
  865.    The lock is released when `write_archive' is called.  */
  866.  
  867. void
  868. lock_for_update ()
  869. {
  870.   /* Open the existing archive file; if that fails, create an empty one.  */
  871.  
  872.   lock_indesc = open (archive, O_RDWR, 0);
  873.  
  874.   if (lock_indesc < 0)
  875.     {
  876.       int outdesc;
  877.  
  878.       if (!silent_create)
  879.     printf ("Creating archive file `%s'\n", archive);
  880.       outdesc = open (archive, O_WRONLY | O_APPEND | O_CREAT, 0666);
  881.       if (outdesc < 0)
  882.     pfatal_with_name (archive);
  883.       write (outdesc, ARMAG, SARMAG);
  884.       close (outdesc);
  885.  
  886.       /* Now we had better be able to open for update!  */
  887.  
  888.       lock_indesc = open (archive, O_RDWR, 0);
  889.       if (lock_indesc < 0)
  890.       {
  891.       unlink(archive);
  892.       pfatal_with_name (archive);
  893.       }
  894.     }
  895.  
  896. #ifdef LOCKS
  897.   /* Lock the old file so that it won't be updated by two programs at once.
  898.      This uses the fcntl locking facility found on Sun systems
  899.      which is also in POSIX.  (Perhaps it comes from sysV.)
  900.  
  901.      Note that merely reading an archive does not require a lock,
  902.      because we use `rename' to update the whole file atomically.  */
  903.  
  904.   {
  905.     struct flock lock;
  906.  
  907.     lock.l_type = F_WRLCK;
  908.     lock.l_whence = 0;
  909.     lock.l_start = 0;
  910.     lock.l_len = 0;
  911.  
  912.     while (1)
  913.       {
  914.     int value = fcntl (lock_indesc, F_SETLKW, &lock);
  915.     if (value >= 0)
  916.       break;
  917.     else if (errno == EINTR)
  918.       continue;
  919.     else
  920.       pfatal_with_name ("locking archive");
  921.       }
  922.   }
  923. #endif
  924. }
  925.  
  926. /* Write a new archive file from a given map.  */
  927. /* When a map is used as the pattern for a new archive,
  928.  each element represents one member to put in it, and
  929.  the order of elements controls the order of writing.
  930.  
  931.  Ordinarily, the element describes a member of the old
  932.  archive, to be copied into the new one.
  933.  
  934.  If the `offset' field of the element's info is 0,
  935.  then the element describes a file to be copied into the
  936.  new archive.  The `name' field is the file's name.
  937.  
  938.  If the `name' field of an element is 0, the element is ignored.
  939.  This makes it easy to specify deletion of archive members.
  940.  
  941.  Every operation that will eventually call `write_archive'
  942.  should call `lock_for_update' before beginning
  943.  to do any I/O on the archive file.
  944. */
  945.  
  946. char *make_tempname ();
  947. void copy_out_member ();
  948.  
  949. write_archive (map, appendflag)
  950.      struct mapelt *map;
  951.      int appendflag;
  952. {
  953.   char *tempname = make_tempname (archive);
  954.   int indesc = lock_indesc;
  955.   int outdesc;
  956.   struct mapelt *tail;
  957.  
  958.   /* Now open the output.  */
  959.  
  960.   if (!appendflag)
  961.     {
  962.       /* Updating an existing archive normally.
  963.      Write output as TEMPNAME and rename at the end.
  964.      There can never be two invocations trying to do this at once,
  965.      because of the lock made on the old archive file.  */
  966.  
  967.       outdesc = open (tempname, O_WRONLY | O_CREAT, 0666);
  968.       if (outdesc < 0)
  969.     pfatal_with_name (tempname);
  970.       write (outdesc, ARMAG, SARMAG);
  971.     }
  972.   else
  973.     {
  974.       /* Fast-append to existing archive.  */
  975.  
  976.       outdesc = open (archive, O_WRONLY | O_APPEND, 0);
  977.     }
  978.  
  979.   /* If archive has or should have a __.SYMDEF member,
  980.      compute the contents for it.  */
  981.  
  982.   if (symdef_flag || symdef_exists)
  983.     {
  984.       if (symdef_exists)
  985.     {
  986. #if 0
  987.       /* This is turned off because there seems to be a bug
  988.          in deleting the symdefs for members that are deleted.
  989.          The easiest way to fix it
  990.          is to regenerate the symdefs from scratch each time,
  991.          which happens if this is not done.  */
  992.       read_old_symdefs (map, indesc);
  993. #endif
  994.     /* needs re-initing ++jrb */
  995.       struct mapelt *this;
  996.  
  997.       /* dont assume symdef is first -- find it */
  998.       for(this = map; this; this = this->next) {
  999.         if(this->info.name && !strcmp(this->info.name, "__.SYMDEF"))
  1000.           break;
  1001.       }
  1002.       if(this == NULL)
  1003.         fatal("Inconsistency, SYMDEF not found", "");
  1004.  
  1005.       this->info.offset = 0;
  1006.       this->info.data_offset = 0;
  1007.       this->info.date = 0;
  1008.       this->info.size = 0;
  1009.       this->info.uid = 0;
  1010.       this->info.gid = 0;
  1011.       this->info.mode = 0666;
  1012.       this->info.symdefs = (struct symdef *)NULL;
  1013.       this->info.nsymdefs = 0;
  1014.       this->info.string_size = 0;
  1015.     }
  1016.       else
  1017.     {
  1018.       struct mapelt *this = (struct mapelt *) xmalloc (sizeof (struct mapelt));
  1019.  
  1020.       this->info.name = "__.SYMDEF";
  1021.       this->info.offset = 0;
  1022.       this->info.data_offset = 0;
  1023.       this->info.date = 0;
  1024.       this->info.size = 0;
  1025.       this->info.uid = 0;
  1026.       this->info.gid = 0;
  1027.       this->info.mode = 0666;
  1028.       this->next = map;
  1029.       map = this;
  1030.     }
  1031.  
  1032.       original_num_symdefs = 0;
  1033.       old_strings_size = 0;
  1034.  
  1035.       update_symdefs (map, indesc);
  1036.     }
  1037.  
  1038.   /* Copy the members into the output, either from the old archive
  1039.      or from specified files.  */
  1040.  
  1041.   for (tail = map; tail; tail = tail->next)
  1042.     {
  1043. /* ERS -- added test for tail->info.name */
  1044.       if (tail->info.name && !strcmp (tail->info.name, "__.SYMDEF")
  1045.       &&  tail->info.date==0)
  1046.     write_symdef_member (tail, map, outdesc);
  1047.       else
  1048.     copy_out_member (tail, indesc, outdesc);
  1049.     }
  1050.  
  1051.   /* Mark the __.SYMDEF member as up to date.  */
  1052.  
  1053.   if (symdef_mapelt)
  1054.     touch_symdef_member (outdesc);
  1055.  
  1056.   /* Install the new output under the intended name.  */
  1057.  
  1058. #if (!(defined(atarist) || defined(USG)))
  1059.   fsync (outdesc);
  1060. #endif
  1061.   close (outdesc);
  1062.  
  1063. #ifdef atarist
  1064.   /* Close the archive.  If we renamed a new one, the old one disappears.  */
  1065.   close (lock_indesc);
  1066. #endif
  1067.  
  1068. #ifdef USG
  1069.   if (!appendflag)
  1070.     {
  1071.       unlink (archive);
  1072.       if ((link (tempname, archive) != 0) || (unlink (tempname) != 0))
  1073.         pfatal_with_name (tempname);
  1074.     }
  1075. #else
  1076.   if (!appendflag)
  1077.     if (rename (tempname, archive))
  1078.       pfatal_with_name (tempname);
  1079. #endif
  1080.  
  1081. #ifdef LOCKS
  1082.   {
  1083.     struct flock lock;
  1084.  
  1085.     /* Unlock the old archive.  */
  1086.  
  1087.     lock.l_type = F_UNLCK;
  1088.     lock.l_whence = 0;
  1089.     lock.l_start = 0;
  1090.     lock.l_len = 0;
  1091.  
  1092.     fcntl (lock_indesc, F_SETLK, &lock);
  1093.   }
  1094. #endif
  1095.  
  1096. #ifndef atarist
  1097.   /* Close the archive.  If we renamed a new one, the old one disappears.  */
  1098.   close (lock_indesc);
  1099. #endif
  1100. }
  1101.  
  1102. void
  1103. copy_out_member (mapelt, archive_indesc, outdesc)
  1104.      struct mapelt *mapelt;
  1105.      int archive_indesc;
  1106.      int outdesc;
  1107. {
  1108.   struct ar_hdr header;
  1109.   int indesc;
  1110.  
  1111.   if (!mapelt->info.name)
  1112.     return;    /* This element was cancelled.  */
  1113.  
  1114.   /* Zero the header, then store in the data as text.  */
  1115.   bzero (&header, sizeof (header));
  1116.  
  1117.   strncpy (header.ar_name, mapelt->info.name, sizeof (header.ar_name));
  1118.  
  1119.   sprintf (header.ar_date, "%d", mapelt->info.date);
  1120.   sprintf (header.ar_size, "%d", mapelt->info.size);
  1121.   sprintf (header.ar_uid, "%d", mapelt->info.uid);
  1122.   sprintf (header.ar_gid, "%d", mapelt->info.gid);
  1123.   sprintf (header.ar_mode, "%o", mapelt->info.mode);
  1124.   strncpy (header.ar_fmag, ARFMAG, sizeof (header.ar_fmag));
  1125.  
  1126.   /* Change all remaining nulls in the header into spaces.  */
  1127.  
  1128.   {
  1129.     char *tem = (char *) &header;
  1130. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  1131.     char *end = (char *) &header + (sizeof (header.ar_name) +
  1132.                     sizeof (header.ar_size) +
  1133.                     sizeof (header.ar_date) +
  1134.                     sizeof (header.ar_mode) +
  1135.                     sizeof (header.ar_uid) +
  1136.                     sizeof (header.ar_gid) +
  1137.                     sizeof (header.ar_fmag));
  1138. #else
  1139.     char *end = (char *) &header + sizeof (header);
  1140. #endif
  1141.     while (tem < end)
  1142.       {
  1143.     if (*tem == 0)
  1144.       *tem = ' ';
  1145.     tem++;
  1146.       }
  1147.   }
  1148.  
  1149.   if (mapelt->info.data_offset)
  1150.     {
  1151.       indesc = archive_indesc;
  1152.       lseek (indesc, mapelt->info.data_offset, 0);
  1153.     }
  1154.   else
  1155.     {
  1156.       indesc = open (mapelt->info.name, 0, 0);
  1157.       if (indesc < 0)
  1158.     {
  1159.       perror_with_name (mapelt->info.name);
  1160.       return;
  1161.     }
  1162.     }
  1163.  
  1164. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  1165.   write (outdesc, &header.ar_name, sizeof (header.ar_name));
  1166.   write (outdesc, &header.ar_size, sizeof (header.ar_size));
  1167.   write (outdesc, &header.ar_date, sizeof (header.ar_date));
  1168.   write (outdesc, &header.ar_mode, sizeof (header.ar_mode));
  1169.   write (outdesc, &header.ar_uid, sizeof (header.ar_uid));
  1170.   write (outdesc, &header.ar_gid, sizeof (header.ar_gid));
  1171.   write (outdesc, &header.ar_fmag, sizeof (header.ar_fmag));
  1172. #else
  1173.   write (outdesc, &header, sizeof (header));
  1174. #endif
  1175.  
  1176.   {
  1177.     char buf[BUFSIZE];
  1178.     int tocopy = mapelt->info.size;
  1179.     while (tocopy > 0)
  1180.       {
  1181.     int thistime = tocopy;
  1182.     if (thistime > BUFSIZE) thistime = BUFSIZE;
  1183.         read (indesc, buf, thistime);
  1184.     write (outdesc, buf, thistime);
  1185.     tocopy -= thistime;
  1186.       }
  1187.   }
  1188.  
  1189.   if (indesc != archive_indesc)
  1190.     close (indesc);
  1191.  
  1192.   if (mapelt->info.size & 1)
  1193.     write (outdesc, "", 1);      /* write (outdesc, "\n", 1); maybe?? jrb */
  1194.  
  1195.   if (verbose)
  1196.     printf ("member %s copied to new archive\n", mapelt->info.name);
  1197. }
  1198.  
  1199. /* Update the time of the __.SYMDEF member; done when we updated
  1200.    that member, just before we close the new archive file.
  1201.    It is open on OUTDESC.  */
  1202.  
  1203. touch_symdef_member (outdesc)
  1204.      int outdesc;
  1205. {
  1206. #ifndef atarist
  1207.   struct stat statbuf;
  1208.   int i;
  1209.  
  1210.   /* See what mtime the archive file has as a result of our writing it.  */
  1211.   fstat (outdesc, &statbuf);
  1212.  
  1213.   /* Advance member's time to that time */
  1214.   bzero (symdef_header.ar_date, sizeof symdef_header.ar_date);
  1215.   sprintf (symdef_header.ar_date, "%d", statbuf.st_mtime);
  1216.   for (i = 0; i < sizeof symdef_header.ar_date; i++)
  1217.     if (symdef_header.ar_date[i] == 0)
  1218.       symdef_header.ar_date[i] = ' ';
  1219.  
  1220.   /* Write back this member's header with the new time.  */
  1221.   if (lseek (outdesc, symdef_mapelt->info.new_offset, 0) >= 0)
  1222.     write (outdesc, &symdef_header, sizeof symdef_header);
  1223. #endif
  1224. }
  1225.  
  1226. char *
  1227. make_tempname (name)
  1228.      char *name;
  1229. {
  1230. #ifdef atarist        /* ERS */
  1231.   char *new, *s;
  1232.   s = new = concat (name, "", "");
  1233.   while (*s) s++;
  1234.   s--;
  1235.   if (*s == '$')
  1236.      *s = '_';
  1237.   else
  1238.      *s = '$';        /* change e.g. GNU.OLB -> GNU.OL$ */
  1239.   return new;
  1240. #else
  1241.   return concat (name, "", "_supersede");
  1242. #endif
  1243. }
  1244.  
  1245. delete_members ()
  1246. {
  1247.   struct mapelt *map = make_map (0);
  1248.   struct mapelt *tail;
  1249.   struct mapelt mapstart;
  1250.   char **p;
  1251.  
  1252.   mapstart.info.name = 0;
  1253.   mapstart.next = map;
  1254.   map = &mapstart;
  1255.  
  1256.   lock_for_update ();
  1257.  
  1258.   if (files)
  1259.     for (p = files; *p; p++)
  1260.       {
  1261.     /* If user says to delete the __.SYMDEF member,
  1262.        don't make a new one to replace it.  */
  1263.     if (!strcmp (*p, "__.SYMDEF"))
  1264.       symdef_exists = 0;
  1265.     delete_from_map (*p, map);
  1266.       }
  1267.  
  1268.   write_archive (map->next, 0);
  1269. }
  1270.  
  1271. delete_from_map (name, map)
  1272.      char *name;
  1273.      struct mapelt *map;
  1274. {
  1275.   struct mapelt *this = find_mapelt (map, name);
  1276.   struct mapelt *prev;
  1277.  
  1278.   if (!this) return;
  1279.   prev = prev_mapelt (map, this);
  1280.   prev->next = this->next;
  1281.   if (verbose)
  1282.     printf ("deleting member %s\n", name);
  1283. }
  1284.  
  1285. move_members ()
  1286. {
  1287.   struct mapelt *map = make_map (0);
  1288.   char **p;
  1289.   struct mapelt *after_mapelt;
  1290.   struct mapelt mapstart;
  1291.   struct mapelt *change_map;
  1292.  
  1293.   mapstart.info.name = 0;
  1294.   mapstart.next = map;
  1295.   change_map = &mapstart;
  1296.  
  1297.   lock_for_update ();
  1298.  
  1299.   switch (postype)
  1300.     {
  1301.     case POS_DEFAULT:
  1302.       after_mapelt = last_mapelt (change_map);
  1303.       break;
  1304.  
  1305.     case POS_AFTER:
  1306.       after_mapelt = find_mapelt (map, posname);
  1307.       break;
  1308.  
  1309.     case POS_BEFORE:
  1310.       after_mapelt = prev_mapelt (change_map, find_mapelt (map, posname));
  1311.     }
  1312.  
  1313.   /* Failure to find specified "before" or "after" member
  1314.      is a fatal error; message has already been printed.  */
  1315.  
  1316.   if (!after_mapelt) exit (1);
  1317.  
  1318.   if (files)
  1319.     for (p = files; *p; p++)
  1320.       {
  1321.     if (move_in_map (*p, change_map, after_mapelt))
  1322.       after_mapelt = after_mapelt->next;
  1323.       }
  1324.  
  1325.   write_archive (map, 0);
  1326. }
  1327.  
  1328. int
  1329. move_in_map (name, map, after)
  1330.      char *name;
  1331.      struct mapelt *map, *after;
  1332. {
  1333.   struct mapelt *this = find_mapelt (map, name);
  1334.   struct mapelt *prev;
  1335.  
  1336.   if (!this) return 0;
  1337.   prev = prev_mapelt (map, this);
  1338.   prev->next = this->next;
  1339.   this->next = after->next;
  1340.   after->next = this;
  1341.   if (verbose)
  1342.     printf ("moving member %s\n", name);
  1343.   return 1;
  1344. }
  1345.  
  1346. /* Insert files into the archive.  */
  1347.  
  1348. replace_members ()
  1349. {
  1350.   struct mapelt *map = make_map (1);
  1351.   struct mapelt mapstart;
  1352.   struct mapelt *after_mapelt;
  1353.   struct mapelt *change_map;
  1354.   char **p;
  1355.  
  1356.   mapstart.info.name = 0;
  1357.   mapstart.next = map;
  1358.   change_map = &mapstart;
  1359.  
  1360.   lock_for_update ();
  1361.  
  1362.   switch (postype)
  1363.     {
  1364.     case POS_DEFAULT:
  1365.       after_mapelt = last_mapelt (change_map);
  1366.       break;
  1367.  
  1368.     case POS_AFTER:
  1369.       after_mapelt = find_mapelt (map, posname);
  1370.       break;
  1371.  
  1372.     case POS_BEFORE:
  1373.       after_mapelt = prev_mapelt (change_map, find_mapelt (map, posname));
  1374.     }
  1375.  
  1376.   /* Failure to find specified "before" or "after" member
  1377.      is a fatal error; message has already been printed.  */
  1378.   if (!after_mapelt) exit (1);
  1379.  
  1380.   if (files)
  1381.     for (p = files; *p; p++)
  1382.       {
  1383.     if (insert_in_map (*p, change_map, after_mapelt))
  1384.       after_mapelt = after_mapelt->next;
  1385.       }
  1386.  
  1387.   write_archive (change_map->next, 0);
  1388. }
  1389.  
  1390. /* Handle the "quick insert" operation.  */
  1391.  
  1392. quick_append ()
  1393. {
  1394.   struct mapelt *map;
  1395.   struct mapelt *after;
  1396.   struct mapelt mapstart;
  1397.   char **p;
  1398.  
  1399.   mapstart.info.name = 0;
  1400.   mapstart.next = 0;
  1401.   map = &mapstart;
  1402.   after = map;
  1403.  
  1404.   lock_for_update ();
  1405.  
  1406.   /* Insert the specified files into the "map",
  1407.      but is a map of the inserted files only,
  1408.      and starts out empty.  */
  1409.   if (files)
  1410.     for (p = files; *p; p++)
  1411.       {
  1412.     if (insert_in_map (*p, map, after))
  1413.       after = after->next;
  1414.       }
  1415.  
  1416.   /* Append these files to the end of the existing archive file.  */
  1417.  
  1418.   write_archive (map->next, 1);
  1419. }
  1420.  
  1421. /* Insert an entry for name NAME into the map MAP after the map entry AFTER.
  1422.    Delete an old entry for NAME.
  1423.    MAP is assumed to start with a dummy entry, which facilitates
  1424.    insertion at the beginning of the list.
  1425.    Return 1 if successful, 0 if did nothing because file NAME doesn't
  1426.    exist or (optionally) is older.  */
  1427.  
  1428. insert_in_map (name, map, after)
  1429.      char *name;
  1430.      struct mapelt *map, *after;
  1431. {
  1432.   struct mapelt *old = find_mapelt_noerror (map, name);
  1433.   struct mapelt *this;
  1434.   struct stat status;
  1435.  
  1436.   if (stat (name, &status))
  1437.     {
  1438.       perror_with_name (name);
  1439.       return 0;
  1440.     }
  1441.   if (old && newer_only && status.st_mtime <= old->info.date)
  1442.     return 0;
  1443.   if (old && verbose)
  1444.     printf ("replacing old member %s\n", old->info.name);
  1445.   if (old) old->info.name = 0;    /* Delete the old one.  */
  1446.   this = (struct mapelt *) xmalloc (sizeof (struct mapelt));
  1447.   this->info.name = name;
  1448.   this->info.offset = 0;
  1449.   this->info.data_offset = 0;
  1450.   this->info.date = status.st_mtime;
  1451.   this->info.size = status.st_size;
  1452.   this->info.uid = status.st_uid;
  1453.   this->info.gid = status.st_gid;
  1454.   this->info.mode = status.st_mode;
  1455.   this->next = after->next;
  1456.   after->next = this;
  1457.   if (verbose)
  1458.     printf ("inserting file %s\n", name);
  1459.   return 1;
  1460. }
  1461.  
  1462. /* Apply a function to each of the specified members.
  1463. */
  1464.  
  1465. extract_members (function)
  1466.      void (*function) ();
  1467. {
  1468.   struct mapelt *map;
  1469.   int arcdesc;
  1470.   char **p;
  1471.  
  1472.   if (!files)
  1473.     {
  1474.       /* Handle case where we want to operate on every member.
  1475.      No need to make a map and search it for this.  */
  1476.       scan (function, 0);
  1477.       return;
  1478.     }
  1479.  
  1480.   arcdesc = open (archive, 0, 0);
  1481.   if (!arcdesc)
  1482.     fatal ("failure opening archive %s for the second time", archive);
  1483.   map = make_map (0);
  1484.  
  1485.   for (p = files; *p; p++)
  1486.     {
  1487.       struct mapelt *this = find_mapelt (map, *p);
  1488.       if (!this) continue;
  1489.       function (this->info, arcdesc);
  1490.     }
  1491.  
  1492.   close (arcdesc);
  1493. }
  1494.  
  1495. /* Write the __.SYMDEF member from data in core.  */
  1496.  
  1497. void
  1498. write_symdef_member (mapelt, map, outdesc)
  1499.      struct mapelt *mapelt;
  1500.      struct mapelt *map;
  1501.      int outdesc;
  1502. {
  1503.   struct ar_hdr header;
  1504.   int indesc;
  1505.   struct mapelt *mapptr;
  1506.   int symdefs_size;
  1507.  
  1508.   if (!mapelt->info.name)
  1509.     return;    /* This element was cancelled.  */
  1510.  
  1511.   /* Clear the header, then store in the data as text.  */
  1512.  
  1513.   bzero (&header, sizeof header);
  1514.  
  1515.   strncpy (header.ar_name, mapelt->info.name, sizeof (header.ar_name));
  1516.  
  1517.   sprintf (header.ar_date, "%d", mapelt->info.date);
  1518.   sprintf (header.ar_size, "%d", mapelt->info.size);
  1519.   sprintf (header.ar_uid, "%d", mapelt->info.uid);
  1520.   sprintf (header.ar_gid, "%d", mapelt->info.gid);
  1521.   sprintf (header.ar_mode, "%o", mapelt->info.mode);
  1522.   strncpy (header.ar_fmag, ARFMAG, sizeof (header.ar_fmag));
  1523.  
  1524.   /* Change all remaining nulls in the header into spaces.  */
  1525.  
  1526.   {
  1527.     char *tem = (char *) &header;
  1528. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  1529.     char *end = (char *) &header + (sizeof (header.ar_name) +
  1530.                     sizeof (header.ar_size) +
  1531.                     sizeof (header.ar_date) +
  1532.                     sizeof (header.ar_mode) +
  1533.                     sizeof (header.ar_uid) +
  1534.                     sizeof (header.ar_gid) +
  1535.                     sizeof (header.ar_fmag));
  1536. #else
  1537.     char *end = (char *) &header + sizeof (header);
  1538. #endif
  1539.     while (tem < end)
  1540.       {
  1541.     if (*tem == 0)
  1542.       *tem = ' ';
  1543.     tem++;
  1544.       }
  1545.   }
  1546.  
  1547. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  1548.   bcopy (&header, &symdef_header, (sizeof (header.ar_name) +
  1549.                    sizeof (header.ar_size) +
  1550.                    sizeof (header.ar_date) +
  1551.                    sizeof (header.ar_mode) +
  1552.                    sizeof (header.ar_uid) +
  1553.                    sizeof (header.ar_gid) +
  1554.                    sizeof (header.ar_fmag)));
  1555.  
  1556.   write (outdesc, &header.ar_name, sizeof (header.ar_name));
  1557.   write (outdesc, &header.ar_size, sizeof (header.ar_size));
  1558.   write (outdesc, &header.ar_date, sizeof (header.ar_date));
  1559.   write (outdesc, &header.ar_mode, sizeof (header.ar_mode));
  1560.   write (outdesc, &header.ar_uid, sizeof (header.ar_uid));
  1561.   write (outdesc, &header.ar_gid, sizeof (header.ar_gid));
  1562.   write (outdesc, &header.ar_fmag, sizeof (header.ar_fmag));
  1563. #else
  1564.   bcopy (&header, &symdef_header, sizeof header);
  1565.  
  1566.   write (outdesc, &header, sizeof (header));
  1567. #endif 
  1568.  
  1569.   /* Write long containing number of symdefs.  */
  1570.  
  1571.   symdefs_size = nsymdefs * sizeof (struct symdef);
  1572. #ifdef BYTE_SWAP
  1573.   symdefs_size = SWAP4(symdefs_size);
  1574. #endif
  1575.   write (outdesc, &symdefs_size, sizeof symdefs_size);
  1576.  
  1577.   /* Write symdefs surviving from old archive.  */
  1578.  
  1579.   write (outdesc, old_symdefs, num_old_symdefs * sizeof (struct symdef));
  1580.  
  1581.   /* Write symdefs for new members.  */
  1582.  
  1583.   for (mapptr = map; mapptr; mapptr = mapptr->next)
  1584.     {
  1585.       if (mapptr->info.nsymdefs)
  1586.     {
  1587. #ifdef BYTE_SWAP
  1588.       { register struct symdef *s = mapptr->info.symdefs;
  1589.         register int i;
  1590.         for(i = mapptr->info.nsymdefs; i; i--, s++) {
  1591.           s->s.stringoffset  = SWAP4(s->s.stringoffset);
  1592.           s->offset  = SWAP4(s->offset);
  1593.         }
  1594.       }
  1595. #endif
  1596.       write (outdesc, mapptr->info.symdefs,
  1597.          mapptr->info.nsymdefs * sizeof (struct symdef));
  1598.     }
  1599.     }
  1600.  
  1601.   /* Write long containing string table size.  */
  1602.  
  1603. #ifdef BYTE_SWAP
  1604.   new_strings_size = SWAP4(new_strings_size);
  1605. #endif
  1606.   write (outdesc, &new_strings_size, sizeof new_strings_size);
  1607. #ifdef BYTE_SWAP
  1608.   new_strings_size = SWAP4(new_strings_size);
  1609. #endif
  1610.  
  1611.   /* Write string table  */
  1612.  
  1613.   write (outdesc, new_strings, new_strings_size);
  1614.  
  1615.   if (mapelt->info.size & 1)
  1616.     write (outdesc, "", 1);
  1617.  
  1618.   if (verbose)
  1619.     printf ("member %s copied to new archive\n", mapelt->info.name);
  1620. }
  1621.  
  1622. #if 0
  1623. void
  1624. read_old_symdefs (map, archive_indesc)
  1625.      int archive_indesc;
  1626. {
  1627.   struct mapelt *mapelt;
  1628.   char *data;
  1629.   int val;
  1630.   int symdefs_size;
  1631.  
  1632.   mapelt = find_mapelt_noerror (map, "__.SYMDEF");
  1633.   if (!mapelt)
  1634.     abort ();            /* Only call here if an old one exists */
  1635.  
  1636.   data  = (char *) xmalloc (mapelt->info.size);
  1637.   lseek (archive_indesc, mapelt->info.data_offset, 0);
  1638.   val = read (archive_indesc, data, mapelt->info.size);
  1639.  
  1640.   symdefs_size = * (long *) data;
  1641.   original_num_symdefs = symdefs_size / sizeof (struct symdef);
  1642.   old_symdefs = (struct symdef *) (data + sizeof (long));
  1643.   old_strings = (char *) (old_symdefs + original_num_symdefs) + sizeof (long);
  1644.   old_strings_size = * (long *) (old_strings - sizeof (long));
  1645. }
  1646. #endif
  1647.  
  1648. /* Create the info.symdefs for a new member
  1649.    by reading the file it is coming from.
  1650.    This code was taken from the GNU nm.c.  */
  1651.  
  1652. void
  1653. make_new_symdefs (mapelt, archive_indesc)
  1654.      struct mapelt *mapelt;
  1655.      int archive_indesc;
  1656. {
  1657.   int indesc;
  1658.   int len;
  1659.   char *name = mapelt->info.name;
  1660.   struct exec header;   /* file header read in here */
  1661.   int string_size;
  1662.   struct nlist *symbols_and_strings;
  1663.   int symcount;
  1664.   int totalsize;
  1665.   char *strings;
  1666.   int i;
  1667.   int offset;
  1668.  
  1669.   if (mapelt->info.data_offset)
  1670.     {
  1671.       indesc = archive_indesc;
  1672.       lseek (indesc, mapelt->info.data_offset, 0);
  1673.       offset = mapelt->info.data_offset;
  1674.     }
  1675.   else
  1676.     {
  1677.       indesc = open (mapelt->info.name, 0, 0);
  1678.       if (indesc < 0)
  1679.     {
  1680.       perror_with_name (mapelt->info.name);
  1681.       return;
  1682.     }
  1683.       offset = 0;
  1684.     }
  1685.  
  1686.   len = read (indesc, &header, sizeof header);
  1687. #ifdef BYTE_SWAP
  1688.    header.a_info   = SWAP4(header.a_info);
  1689.    header.a_text   = SWAP4(header.a_text);
  1690.    header.a_data   = SWAP4(header.a_data);
  1691.    header.a_bss    = SWAP4(header.a_bss);
  1692.    header.a_syms   = SWAP4(header.a_syms);
  1693.    header.a_entry  = SWAP4(header.a_entry);
  1694.    header.a_trsize = SWAP4(header.a_trsize);
  1695.    header.a_drsize = SWAP4(header.a_drsize);
  1696. #endif
  1697.   if (len != sizeof header)
  1698.     error_with_file ("failure reading header of ", mapelt);
  1699.   else if (N_BADMAG(header))
  1700.     error_with_file ("bad format (not an object file) in ", mapelt);
  1701.  
  1702.   /* read the string-table-length out of the file */
  1703.  
  1704.   lseek (indesc, N_STROFF(header) + offset, 0);
  1705.   if (sizeof string_size != read (indesc, &string_size, sizeof string_size))
  1706.     {
  1707.       error_with_file ("bad string table in ", mapelt);
  1708.       if (mapelt->info.data_offset) /* ++ */
  1709.     close (indesc);    /* We just opened it.  Give up */
  1710.       return;
  1711.     }
  1712. #ifdef BYTE_SWAP
  1713.   string_size = SWAP4(string_size);
  1714. #endif
  1715.   /* number of symbol entries in the file */
  1716.   symcount = header.a_syms / sizeof (struct nlist);
  1717.  
  1718.   totalsize = string_size + header.a_syms;
  1719.   /* allocate space for symbol entries and string table */
  1720.   symbols_and_strings = (struct nlist *) xmalloc (totalsize);
  1721.   strings = (char *) symbols_and_strings + header.a_syms;
  1722.  
  1723.   /* read them both in all at once */
  1724.   lseek (indesc, N_SYMOFF(header) + offset, 0);
  1725.   if (totalsize != read (indesc, symbols_and_strings, totalsize))
  1726.     {
  1727.       error_with_file ("premature end of file in symbols/strings of ");
  1728.       if (mapelt->info.data_offset)
  1729.     close (indesc);    /* Give up! */
  1730.       return;
  1731.     }
  1732.  
  1733.   if (indesc != archive_indesc)
  1734.     close (indesc);
  1735.  
  1736. #ifdef BYTE_SWAP
  1737.    { register struct nlist *from = symbols_and_strings;
  1738.      register struct nlist *end = symbols_and_strings + symcount;
  1739.      for(;from < end; from++) {
  1740.       from->n_un.n_strx = SWAP4(from->n_un.n_strx); 
  1741.       from->n_desc      = SWAP2(from->n_desc);
  1742.      }
  1743.    }
  1744. #endif /* BYTE_SWAP */
  1745.   /* discard the symbols we don't want to mention; compact the rest down */
  1746.  
  1747.   symcount = filter_symbols (symbols_and_strings, symcount, strings);
  1748.  
  1749.   /* We have a vector of struct nlist; we want a vector of struct symdef.
  1750.      Convert it in place, reusing the space.
  1751.      This works since a struct nlist is longer than a struct symdef.
  1752.  
  1753.      Also make each symdef point directly at the symbol name string.  */
  1754.  
  1755.   mapelt->info.symdefs = (struct symdef *) symbols_and_strings;
  1756.   mapelt->info.nsymdefs = symcount;
  1757.   mapelt->info.string_size = 0;
  1758.  
  1759.   for (i = 0; i < symcount; i++)
  1760.     {
  1761.       mapelt->info.symdefs[i].s.name = strings + symbols_and_strings[i].n_un.n_strx;
  1762.       mapelt->info.string_size += strlen (mapelt->info.symdefs[i].s.name) + 1;
  1763.     }
  1764. }
  1765.  
  1766. /* Choose which symbol entries to mention in __.SYMDEF;
  1767.   compact them downward to get rid of the rest.
  1768.   Return the number of symbols left.  */
  1769.  
  1770. int
  1771. filter_symbols (syms, symcount, strings)
  1772.      struct nlist *syms;
  1773.      int symcount;
  1774.      char *strings;
  1775. {
  1776.   struct nlist *from = syms, *to = syms;
  1777.   struct nlist *end = syms + symcount;
  1778.  
  1779.   while (from < end)
  1780.     {
  1781.       if ((from->n_type & N_EXT)
  1782.       && (from->n_type != N_EXT || from->n_value))
  1783.     *to++ = *from;
  1784.       from++;
  1785.     }
  1786.  
  1787.   return to - syms;
  1788. }
  1789.  
  1790.  
  1791. /* Update the __.SYMDEF data before writing a new archive.  */
  1792.  
  1793. update_symdefs (map, archive_indesc)
  1794.      struct mapelt *map;
  1795.      int archive_indesc;
  1796. {
  1797.   struct mapelt *tail;
  1798.   int pos;
  1799.   int i,j;
  1800.   int len;
  1801.   struct symdef *s;
  1802. /*  struct timeval timeofday; */
  1803.  
  1804.   nsymdefs = original_num_symdefs;
  1805.   num_old_symdefs = original_num_symdefs;
  1806.   new_strings_size = old_strings_size;
  1807.  
  1808.   if (nsymdefs != 0)
  1809.     {
  1810.       /* We already had a __.SYMDEF member, so just update it.  */
  1811.  
  1812.       /* Mark as canceled any old symdefs for members being deleted.  */
  1813.  
  1814.       for (tail = map; tail; tail = tail->next)
  1815.     {
  1816.       if (tail->info.name == 0)
  1817.         /* Old member being deleted.  Delete its symdef entries too.  */
  1818.         {
  1819.           for (i = 0; i < nsymdefs; i++)
  1820.         if (old_symdefs[i].offset == tail->info.offset)
  1821.           {
  1822.             old_symdefs[i].offset = 0;
  1823.             nsymdefs--;
  1824.             num_old_symdefs--;
  1825.             new_strings_size
  1826.               -= strlen (old_strings + old_symdefs[i].s.stringoffset);
  1827.           }
  1828.         }
  1829.     }
  1830.  
  1831.       /* Now compactify old_symdefs */
  1832.  
  1833.       for (i = 0, j = 0; i < num_old_symdefs; i++)
  1834.     if (old_symdefs[i].offset)
  1835.       old_symdefs[j++] = old_symdefs[i];
  1836.  
  1837.       /* Create symdef data for any new members.  */
  1838.  
  1839.       for (tail = map; tail; tail = tail->next)
  1840.     {
  1841.     /* ERS -- added check for tail->info.name != 0 */
  1842.       if ((tail->info.offset) ||
  1843.           !(tail->info.name) || !strcmp (tail->info.name, "__.SYMDEF"))
  1844.         continue;
  1845.       make_new_symdefs (tail, archive_indesc);
  1846.       nsymdefs += tail->info.nsymdefs;
  1847.       new_strings_size += tail->info.string_size;
  1848.     }
  1849.     }
  1850.   else
  1851.     {
  1852.       /* Create symdef data for all existing members.  */
  1853.  
  1854.       for (tail = map; tail; tail = tail->next)
  1855.     { /* added tail->info.name as per other ERS changes  ++jrb */
  1856.       if (!(tail->info.name) || !strcmp (tail->info.name, "__.SYMDEF"))
  1857.         continue;
  1858.       make_new_symdefs (tail, archive_indesc);
  1859.       nsymdefs += tail->info.nsymdefs;
  1860.       new_strings_size += tail->info.string_size;
  1861.     }
  1862.     }
  1863.  
  1864.   /* Now we know the size of __.SYMDEF,
  1865.      so assign the positions of all the members. */
  1866.  
  1867.   tail = find_mapelt_noerror (map, "__.SYMDEF");
  1868.   tail->info.size = nsymdefs * sizeof (struct symdef) + sizeof (long) + sizeof (long) + new_strings_size;
  1869.   symdef_mapelt = tail;
  1870.  
  1871.   pos = SARMAG;
  1872.   for (tail = map; tail; tail = tail->next)
  1873.     {
  1874.       if (!tail->info.name) continue;    /* Ignore deleted members */
  1875.       tail->info.new_offset = pos;
  1876. #if (defined(WORD_ALIGNED) && defined(CROSSATARI))
  1877.       pos += tail->info.size + (sizeof (foo.ar_name) +
  1878.                 sizeof (foo.ar_size) +
  1879.                 sizeof (foo.ar_date) +
  1880.                 sizeof (foo.ar_mode) +
  1881.                 sizeof (foo.ar_uid) +
  1882.                 sizeof (foo.ar_gid) +
  1883.                 sizeof (foo.ar_fmag));
  1884. #else
  1885.       pos += tail->info.size + sizeof (struct ar_hdr);
  1886. #endif
  1887.       if (tail->info.size & 1) pos++;
  1888.     }
  1889.  
  1890.   /* Now update the offsets in the symdef data
  1891.      to be the new offsets rather than the old ones.  */
  1892.  
  1893.   for (tail = map; tail; tail = tail->next)
  1894.     {
  1895.       if (!tail->info.name) continue;
  1896.       if (!tail->info.symdefs)
  1897.     /* Member without new symdef data.
  1898.        Check the old symdef data; it may be included there. */
  1899.     for (i = 0; i < num_old_symdefs; i++)
  1900.       {
  1901.         if (old_symdefs[i].offset == tail->info.offset)
  1902.           old_symdefs[i].offset = tail->info.new_offset;
  1903.       }
  1904.       else
  1905.     for (i = 0; i < tail->info.nsymdefs; i++)
  1906.       tail->info.symdefs[i].offset = tail->info.new_offset;
  1907.     }
  1908.  
  1909.   /* Generate new, combined string table
  1910.      and put each string's offset into the symdef that refers to it.
  1911.      Note that old symdefs ref their strings by offsets into old_strings
  1912.      but new symdefs contain addresses of strings.  */
  1913.  
  1914.   new_strings = (char *) xmalloc (new_strings_size);
  1915.   pos = 0;
  1916.  
  1917.   for (i = 0; i < num_old_symdefs; i++)
  1918.     {
  1919.       if (old_symdefs[i].offset == 0) continue;
  1920.       strcpy (new_strings + pos, old_strings + old_symdefs[i].s.stringoffset);
  1921.       old_symdefs[i].s.stringoffset = pos;
  1922.       pos += strlen (new_strings + pos) + 1;
  1923.     }
  1924.   for (tail = map; tail; tail = tail->next)
  1925.     {
  1926.       len = tail->info.nsymdefs;
  1927.       s = tail->info.symdefs;
  1928.  
  1929.       for (i = 0; i < len; i++)
  1930.     {
  1931.       strcpy (new_strings + pos, s[i].s.name);
  1932.       s[i].s.stringoffset = pos;
  1933.       pos += strlen (new_strings + pos) + 1;
  1934.     }
  1935.     }
  1936.   if (pos != new_strings_size)
  1937.     fatal ("inconsistency in new_strings_size", 0);
  1938. }
  1939.  
  1940. /* Print error message and exit.  */
  1941.  
  1942. fatal (s1, s2)
  1943.      char *s1, *s2;
  1944. {
  1945.   error (s1, s2);
  1946.   exit (1);
  1947. }
  1948.  
  1949. /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
  1950.  
  1951. error (s1, s2)
  1952.      char *s1, *s2;
  1953. {
  1954.   fprintf (stderr, "ar: ");
  1955.   fprintf (stderr, s1, s2);
  1956.   fprintf (stderr, "\n");
  1957. }
  1958.  
  1959. error_with_file (string, mapelt)
  1960.      char *string;
  1961.      struct mapelt *mapelt;
  1962. {
  1963.   fprintf (stderr, "ar: ");
  1964.   fprintf (stderr, string);
  1965.   if (mapelt->info.offset)
  1966.     fprintf (stderr, "%s(%s)", archive, mapelt->info.name);
  1967.   else
  1968.     fprintf (stderr, "%s", mapelt->info.name);
  1969.   fprintf (stderr, "\n");
  1970. }
  1971.  
  1972. perror_with_name (name)
  1973.      char *name;
  1974. {
  1975.   extern int errno, sys_nerr;
  1976.   extern char *sys_errlist[];
  1977.   char *s;
  1978.  
  1979.   if (errno < sys_nerr)
  1980.     s = concat ("", sys_errlist[errno], " for %s");
  1981.   else
  1982.     s = "cannot open %s";
  1983.   error (s, name);
  1984. }
  1985.  
  1986. pfatal_with_name (name)
  1987.      char *name;
  1988. {
  1989.   extern int errno, sys_nerr;
  1990.   extern char *sys_errlist[];
  1991.   char *s;
  1992.  
  1993.   if (errno < sys_nerr)
  1994.     s = concat ("", sys_errlist[errno], " for %s");
  1995.   else
  1996.     s = "cannot open %s";
  1997.   fatal (s, name);
  1998. }
  1999.  
  2000. /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
  2001.  
  2002. char *
  2003. concat (s1, s2, s3)
  2004.      char *s1, *s2, *s3;
  2005. {
  2006.   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  2007.   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
  2008.  
  2009.   strcpy (result, s1);
  2010.   strcpy (result + len1, s2);
  2011.   strcpy (result + len1 + len2, s3);
  2012.   *(result + len1 + len2 + len3) = 0;
  2013.  
  2014.   return result;
  2015. }
  2016.  
  2017. /* Like malloc but get fatal error if memory is exhausted.  */
  2018.  
  2019. int
  2020. xmalloc (size)
  2021.      int size;
  2022. {
  2023. /*   int result = malloc (size);  */
  2024.   int result = calloc (1, size);
  2025.   if (!result)
  2026.     fatal ("virtual memory exhausted", 0);
  2027.   return result;
  2028. }
  2029.