+/* Describe the current state of carsym entries while building the archive
+ table of content. Things are simple with Alpha archives as the number
+ of entries is known, but with IA64 archives a entry can make a reference
+ to severals members. Therefore we must be able to extend the table on the
+ fly, but it should be allocated on the bfd - which doesn't support realloc.
+ To reduce the overhead, the table is initially allocated in the BFD's
+ objalloc and extended if necessary on the heap. In the later case, it
+ is finally copied to the BFD's objalloc so that it will automatically be
+ freed. */
+
+struct carsym_mem
+{
+ /* The table of content. */
+ struct carsym *idx;
+
+ /* Number of entries used in the table. */
+ unsigned int nbr;
+
+ /* Maximum number of entries. */
+ unsigned int max;
+
+ /* Do not allocate more that this number of entries. */
+ unsigned int limit;
+
+ /* If true, the table was reallocated on the heap. If false, it is still
+ in the BFD's objalloc. */
+ bool realloced;
+};
+
+/* Simply add a name to the index. */
+
+static bool
+vms_add_index (struct carsym_mem *cs, char *name,
+ unsigned int idx_vbn, unsigned int idx_off)
+{
+ if (cs->nbr == cs->max)
+ {
+ struct carsym *n;
+ size_t amt;
+
+ if (cs->max > -33u / 2 || cs->max >= cs->limit)
+ {
+ bfd_set_error (bfd_error_file_too_big);
+ return false;
+ }
+ cs->max = 2 * cs->max + 32;
+ if (cs->max > cs->limit)
+ cs->max = cs->limit;
+ if (_bfd_mul_overflow (cs->max, sizeof (struct carsym), &amt))
+ {
+ bfd_set_error (bfd_error_file_too_big);
+ return false;
+ }
+
+ if (!cs->realloced)
+ {
+ n = bfd_malloc (amt);
+ if (n == NULL)
+ return false;
+ memcpy (n, cs->idx, cs->nbr * sizeof (struct carsym));
+ /* And unfortunately we can't free cs->idx. */
+ }
+ else
+ {
+ n = bfd_realloc_or_free (cs->idx, amt);
+ if (n == NULL)
+ return false;
+ }
+ cs->idx = n;
+ cs->realloced = true;
+ }
+ cs->idx[cs->nbr].file_offset = (idx_vbn - 1) * VMS_BLOCK_SIZE + idx_off;
+ cs->idx[cs->nbr].name = name;
+ cs->nbr++;
+ return true;
+}
+
+/* Follow all member of a lns list (pointed by RFA) and add indexes for
+ NAME. Return FALSE in case of error. */
+
+static bool
+vms_add_indexes_from_list (bfd *abfd, struct carsym_mem *cs, char *name,
+ struct vms_rfa *rfa)
+{
+ struct vms_lns lns;
+ unsigned int vbn;
+ file_ptr off;
+
+ while (1)
+ {
+ vbn = bfd_getl32 (rfa->vbn);
+ if (vbn == 0)
+ return true;
+
+ /* Read the LHS. */
+ off = (vbn - 1) * VMS_BLOCK_SIZE + bfd_getl16 (rfa->offset);
+ if (bfd_seek (abfd, off, SEEK_SET) != 0
+ || bfd_bread (&lns, sizeof (lns), abfd) != sizeof (lns))
+ return false;
+
+ if (!vms_add_index (cs, name,
+ bfd_getl32 (lns.modrfa.vbn),
+ bfd_getl16 (lns.modrfa.offset)))
+ return false;
+
+ rfa = &lns.nxtrfa;
+ }
+}
+
+/* Read block VBN from ABFD and store it into BLK. Return FALSE in case of error. */
+
+static bool
+vms_read_block (bfd *abfd, unsigned int vbn, void *blk)
+{
+ file_ptr off;
+
+ off = (vbn - 1) * VMS_BLOCK_SIZE;
+ if (bfd_seek (abfd, off, SEEK_SET) != 0
+ || bfd_bread (blk, VMS_BLOCK_SIZE, abfd) != VMS_BLOCK_SIZE)
+ return false;
+
+ return true;
+}
+
+/* Write the content of BLK to block VBN of ABFD. Return FALSE in case of error. */
+
+static bool
+vms_write_block (bfd *abfd, unsigned int vbn, void *blk)
+{
+ file_ptr off;
+
+ off = (vbn - 1) * VMS_BLOCK_SIZE;
+ if (bfd_seek (abfd, off, SEEK_SET) != 0
+ || bfd_bwrite (blk, VMS_BLOCK_SIZE, abfd) != VMS_BLOCK_SIZE)
+ return false;
+
+ return true;
+}
+