+/* Compute the relative path to get to DATA_WD (absolute directory name)
+ from CWD (another absolute directory name). E.g. for
+ DATA_WD of "/tmp/foo/bar" and CWD of "/tmp/baz/qux" return
+ "../../foo/bar". Returned string should be freed by the caller.
+ Return NULL if absolute file name needs to be used. */
+
+static char *
+relative_path_prefix (const char *data_wd, const char *cwd)
+{
+ const char *d = data_wd;
+ const char *c = cwd;
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+ if (d[1] == ':')
+ {
+ if (!IS_DIR_SEPARATOR (d[2]))
+ return NULL;
+ if (c[0] == d[0] && c[1] == ':' && IS_DIR_SEPARATOR (c[2]))
+ {
+ c += 3;
+ d += 3;
+ }
+ else
+ return NULL;
+ }
+ else if (c[1] == ':')
+ return NULL;
+#endif
+ do
+ {
+ while (IS_DIR_SEPARATOR (*d))
+ d++;
+ while (IS_DIR_SEPARATOR (*c))
+ c++;
+ size_t i;
+ for (i = 0; c[i] && !IS_DIR_SEPARATOR (c[i]) && c[i] == d[i]; i++)
+ ;
+ if ((c[i] == '\0' || IS_DIR_SEPARATOR (c[i]))
+ && (d[i] == '\0' || IS_DIR_SEPARATOR (d[i])))
+ {
+ c += i;
+ d += i;
+ if (*c == '\0' || *d == '\0')
+ break;
+ }
+ else
+ break;
+ }
+ while (1);
+ size_t num_up = 0;
+ do
+ {
+ while (IS_DIR_SEPARATOR (*c))
+ c++;
+ if (*c == '\0')
+ break;
+ num_up++;
+ while (*c && !IS_DIR_SEPARATOR (*c))
+ c++;
+ }
+ while (1);
+ while (IS_DIR_SEPARATOR (*d))
+ d++;
+ size_t len = strlen (d);
+ if (len == 0 && num_up == 0)
+ return xstrdup (".");
+ char *ret = XNEWVEC (char, num_up * 3 + len + 1);
+ char *p = ret;
+ for (; num_up; num_up--)
+ {
+ const char dir_up[3] = { '.', '.', DIR_SEPARATOR };
+ memcpy (p, dir_up, 3);
+ p += 3;
+ }
+ memcpy (p, d, len + 1);
+ return ret;
+}
+
+/* Look up DATA_WD in hash table of relative prefixes. If found,
+ return relative path from CWD to DATA_WD from the hash table,
+ otherwise create it. */
+
+static const char *
+canon_relative_path_prefix (const char *data_wd, const char *cwd)
+{
+ if (!IS_ABSOLUTE_PATH (data_wd) || !IS_ABSOLUTE_PATH (cwd))
+ return NULL;
+
+ if (!path_name_pair_hash_table)
+ {
+ path_name_pair_hash_table
+ = new hash_table<string_pair_map_hasher> (37);
+ string_pair_map_allocator
+ = new object_allocator <struct string_pair_map>
+ ("line map string pair map hash");
+ }
+
+ inchash::hash h;
+ h.add_ptr (cwd);
+ h.merge_hash (htab_hash_string (data_wd));
+ h.add_int (true);
+
+ string_pair_map s_slot;
+ s_slot.str1 = cwd;
+ s_slot.str2 = data_wd;
+ s_slot.str3 = NULL;
+ s_slot.hash = h.end ();
+ s_slot.prefix = true;
+
+ string_pair_map **slot
+ = path_name_pair_hash_table->find_slot (&s_slot, INSERT);
+ if (*slot == NULL)
+ {
+ /* Compute relative path from cwd directory to data_wd directory.
+ E.g. if cwd is /tmp/foo/bar and data_wd is /tmp/baz/qux ,
+ it will return ../../baz/qux . */
+ char *relative_path = relative_path_prefix (data_wd, cwd);
+ const char *relative = relative_path ? relative_path : data_wd;
+ size_t relative_len = strlen (relative);
+ gcc_assert (relative_len);
+
+ size_t data_wd_len = strlen (data_wd);
+ bool add_separator = false;
+ if (!IS_DIR_SEPARATOR (relative[relative_len - 1]))
+ add_separator = true;
+
+ size_t len = relative_len + 1 + data_wd_len + 1 + add_separator;
+
+ char *saved_string = XOBNEWVEC (&file_name_obstack, char, len);
+ struct string_pair_map *new_slot
+ = string_pair_map_allocator->allocate ();
+ memcpy (saved_string, data_wd, data_wd_len + 1);
+ memcpy (saved_string + data_wd_len + 1, relative, relative_len);
+ if (add_separator)
+ saved_string[len - 2] = DIR_SEPARATOR;
+ saved_string[len - 1] = '\0';
+ new_slot->str1 = cwd;
+ new_slot->str2 = saved_string;
+ new_slot->str3 = saved_string + data_wd_len + 1;
+ if (relative_len == 1 && relative[0] == '.')
+ new_slot->str3 = NULL;
+ new_slot->hash = s_slot.hash;
+ new_slot->prefix = true;
+ *slot = new_slot;
+ free (relative_path);
+ return new_slot->str3;
+ }
+ else
+ {
+ string_pair_map *old_slot = *slot;
+ return old_slot->str3;
+ }
+}
+
+/* Look up the pair of RELATIVE_PREFIX and STRING strings in a hash table.
+ If found, return the concatenation of those from the hash table,
+ otherwise concatenate them. */
+
+static const char *
+canon_relative_file_name (const char *relative_prefix, const char *string)
+{
+ inchash::hash h;
+ h.add_ptr (relative_prefix);
+ h.merge_hash (htab_hash_string (string));
+
+ string_pair_map s_slot;
+ s_slot.str1 = relative_prefix;
+ s_slot.str2 = string;
+ s_slot.str3 = NULL;
+ s_slot.hash = h.end ();
+ s_slot.prefix = false;
+
+ string_pair_map **slot
+ = path_name_pair_hash_table->find_slot (&s_slot, INSERT);
+ if (*slot == NULL)
+ {
+ size_t relative_prefix_len = strlen (relative_prefix);
+ size_t string_len = strlen (string);
+ size_t len = relative_prefix_len + string_len + 1;
+
+ char *saved_string = XOBNEWVEC (&file_name_obstack, char, len);
+ struct string_pair_map *new_slot
+ = string_pair_map_allocator->allocate ();
+ memcpy (saved_string, relative_prefix, relative_prefix_len);
+ memcpy (saved_string + relative_prefix_len, string, string_len + 1);
+ new_slot->str1 = relative_prefix;
+ new_slot->str2 = saved_string + relative_prefix_len;
+ new_slot->str3 = saved_string;
+ new_slot->hash = s_slot.hash;
+ new_slot->prefix = false;
+ *slot = new_slot;
+ return new_slot->str3;
+ }
+ else
+ {
+ string_pair_map *old_slot = *slot;
+ return old_slot->str3;
+ }
+}