+static const char *
+get_basename (const char * pathname)
+{
+ const char * file;
+
+ file = lbasename (pathname);
+ /* Don't make empty string from / or A: from A:/ . */
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+ if (file <= pathname + 3)
+ file = pathname;
+#else
+ if (file == pathname + 1)
+ file = pathname;
+#endif
+ return file;
+}
+
+static unsigned int
+get_directory_table_entry (const char * dirname,
+ size_t dirlen,
+ bfd_boolean can_use_zero)
+{
+ unsigned int d;
+
+ if (dirlen == 0)
+ return 0;
+
+#ifndef DWARF2_DIR_SHOULD_END_WITH_SEPARATOR
+ if (IS_DIR_SEPARATOR (dirname[dirlen - 1]))
+ {
+ -- dirlen;
+ if (dirlen == 0)
+ return 0;
+ }
+#endif
+
+ for (d = 0; d < dirs_in_use; ++d)
+ {
+ if (dirs[d] != NULL
+ && filename_ncmp (dirname, dirs[d], dirlen) == 0
+ && dirs[d][dirlen] == '\0')
+ return d;
+ }
+
+ if (can_use_zero)
+ {
+ if (dirs == NULL || dirs[0] == NULL)
+ d = 0;
+ }
+ else if (d == 0)
+ d = 1;
+
+ if (d >= dirs_allocated)
+ {
+ unsigned int old = dirs_allocated;
+
+ dirs_allocated = d + 32;
+ dirs = XRESIZEVEC (char *, dirs, dirs_allocated);
+ memset (dirs + old, 0, (dirs_allocated - old) * sizeof (char *));
+ }
+
+ dirs[d] = xmemdup0 (dirname, dirlen);
+ if (dirs_in_use <= d)
+ dirs_in_use = d + 1;
+
+ return d;
+}
+
+static bfd_boolean
+assign_file_to_slot (unsigned long i, const char *file, unsigned int dir, bfd_boolean auto_assign)
+{
+ if (i >= files_allocated)
+ {
+ unsigned int old = files_allocated;
+
+ files_allocated = i + 32;
+ /* Catch wraparound. */
+ if (files_allocated <= old)
+ {
+ as_bad (_("file number %lu is too big"), (unsigned long) i);
+ return FALSE;
+ }
+
+ files = XRESIZEVEC (struct file_entry, files, files_allocated);
+ memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry));
+ }
+
+ files[i].filename = file;
+ files[i].dir = dir;
+ files[i].auto_assigned = auto_assign;
+ memset (files[i].md5, 0, NUM_MD5_BYTES);
+
+ if (files_in_use < i + 1)
+ files_in_use = i + 1;
+
+ return TRUE;
+}
+
+/* Get a .debug_line file number for PATHNAME. If there is a
+ directory component to PATHNAME, then this will be stored
+ in the directory table, if it is not already present.
+ Returns the slot number allocated to that filename or -1
+ if there was a problem. */
+
+static signed int
+allocate_filenum (const char * pathname)
+{
+ static signed int last_used = -1, last_used_dir_len = 0;
+ const char *file;
+ size_t dir_len;
+ unsigned int i, dir;
+
+ /* Short circuit the common case of adding the same pathname
+ as last time. */
+ if (last_used != -1)
+ {
+ const char * dirname = NULL;
+
+ if (dirs != NULL)
+ dirname = dirs[files[last_used].dir];
+
+ if (dirname == NULL)
+ {
+ if (filename_cmp (pathname, files[last_used].filename) == 0)
+ return last_used;
+ }
+ else
+ {
+ if (filename_ncmp (pathname, dirname, last_used_dir_len) == 0
+ && IS_DIR_SEPARATOR (pathname [last_used_dir_len])
+ && filename_cmp (pathname + last_used_dir_len + 1,
+ files[last_used].filename) == 0)
+ return last_used;
+ }
+ }
+
+ file = get_basename (pathname);
+ dir_len = file - pathname;
+
+ dir = get_directory_table_entry (pathname, dir_len, FALSE);
+
+ /* Do not use slot-0. That is specifically reserved for use by
+ the '.file 0 "name"' directive. */
+ for (i = 1; i < files_in_use; ++i)
+ if (files[i].dir == dir
+ && files[i].filename
+ && filename_cmp (file, files[i].filename) == 0)
+ {
+ last_used = i;
+ last_used_dir_len = dir_len;
+ return i;
+ }
+
+ if (!assign_file_to_slot (i, file, dir, TRUE))
+ return -1;
+
+ last_used = i;
+ last_used_dir_len = dir_len;
+
+ return i;
+}
+
+/* Allocate slot NUM in the .debug_line file table to FILENAME.
+ If DIRNAME is not NULL or there is a directory component to FILENAME
+ then this will be stored in the directory table, if not already present.
+ if WITH_MD5 is TRUE then there is a md5 value in generic_bignum.
+ Returns TRUE if allocation succeeded, FALSE otherwise. */
+
+static bfd_boolean
+allocate_filename_to_slot (const char * dirname,
+ const char * filename,
+ unsigned int num,
+ bfd_boolean with_md5)
+{
+ const char *file;
+ size_t dirlen;
+ unsigned int i, d;
+
+ /* Short circuit the common case of adding the same pathname
+ as last time. */
+ if (num < files_allocated && files[num].filename != NULL)
+ {
+ const char * dir = NULL;
+
+ if (dirs)
+ dir = dirs[files[num].dir];
+
+ if (with_md5
+ && memcmp (generic_bignum, files[num].md5, NUM_MD5_BYTES) != 0)
+ goto fail;
+
+ if (dirname != NULL)
+ {
+ if (dir != NULL && filename_cmp (dir, dirname) != 0)
+ goto fail;
+
+ if (filename_cmp (filename, files[num].filename) != 0)
+ goto fail;
+
+ /* If the filenames match, but the directory table entry was
+ empty, then fill it with the provided directory name. */
+ if (dir == NULL)
+ dirs[files[num].dir] = xmemdup0 (dirname, strlen (dirname));
+
+ return TRUE;
+ }
+ else if (dir != NULL)
+ {
+ dirlen = strlen (dir);
+ if (filename_ncmp (filename, dir, dirlen) == 0
+ && IS_DIR_SEPARATOR (filename [dirlen])
+ && filename_cmp (filename + dirlen + 1, files[num].filename) == 0)
+ return TRUE;
+ }
+ else /* dir == NULL */
+ {
+ file = get_basename (filename);
+ if (filename_cmp (file, files[num].filename) == 0)
+ {
+ if (file > filename)
+ /* The filenames match, but the directory table entry is empty.
+ Fill it with the provided directory name. */
+ dirs[files[num].dir] = xmemdup0 (filename, file - filename);
+ return TRUE;
+ }
+ }
+
+ fail:
+ /* If NUM was previously allocated automatically then
+ choose another slot for it, so that we can reuse NUM. */
+ if (files[num].auto_assigned)
+ {
+ /* Find an unused slot. */
+ for (i = 1; i < files_in_use; ++i)
+ if (files[i].filename == NULL)
+ break;
+ if (! assign_file_to_slot (i, files[num].filename, files[num].dir, TRUE))
+ return FALSE;
+ files[num].filename = NULL;
+ }
+ else
+ {
+ as_bad (_("file table slot %u is already occupied by a different file (%s%s%s vs %s%s%s)"),
+ num,
+ dir == NULL ? "" : dir,
+ dir == NULL ? "" : "/",
+ files[num].filename,
+ dirname == NULL ? "" : dirname,
+ dirname == NULL ? "" : "/",
+ filename);
+ return FALSE;
+ }
+ }
+
+ if (dirname == NULL)
+ {
+ dirname = filename;
+ file = get_basename (filename);
+ dirlen = file - filename;
+ }
+ else
+ {
+ dirlen = strlen (dirname);
+ file = filename;
+ }
+
+ d = get_directory_table_entry (dirname, dirlen, num == 0);
+ i = num;
+
+ if (! assign_file_to_slot (i, file, d, FALSE))
+ return FALSE;
+
+ if (with_md5)
+ {
+ if (target_big_endian)
+ {
+ /* md5's are stored in litte endian format. */
+ unsigned int bits_remaining = NUM_MD5_BYTES * BITS_PER_CHAR;
+ unsigned int byte = NUM_MD5_BYTES;
+ unsigned int bignum_index = 0;
+
+ while (bits_remaining)
+ {
+ unsigned int bignum_bits_remaining = LITTLENUM_NUMBER_OF_BITS;
+ valueT bignum_value = generic_bignum [bignum_index];
+ bignum_index ++;
+
+ while (bignum_bits_remaining)
+ {
+ files[i].md5[--byte] = bignum_value & 0xff;
+ bignum_value >>= 8;
+ bignum_bits_remaining -= 8;
+ bits_remaining -= 8;
+ }
+ }
+ }
+ else
+ {
+ unsigned int bits_remaining = NUM_MD5_BYTES * BITS_PER_CHAR;
+ unsigned int byte = 0;
+ unsigned int bignum_index = 0;
+
+ while (bits_remaining)
+ {
+ unsigned int bignum_bits_remaining = LITTLENUM_NUMBER_OF_BITS;
+ valueT bignum_value = generic_bignum [bignum_index];
+
+ bignum_index ++;
+
+ while (bignum_bits_remaining)
+ {
+ files[i].md5[byte++] = bignum_value & 0xff;
+ bignum_value >>= 8;
+ bignum_bits_remaining -= 8;
+ bits_remaining -= 8;
+ }
+ }
+ }
+ }
+ else
+ memset (files[i].md5, 0, NUM_MD5_BYTES);
+
+ return TRUE;
+}
+