Add translations for various sub-directories
[binutils-gdb.git] / gdbsupport / pathstuff.cc
blobfaed79b2cfb1d63c7051245e9018c0c2be7e083f
1 /* Path manipulation routines for GDB and gdbserver.
3 Copyright (C) 1986-2024 Free Software Foundation, Inc.
5 This file is part of GDB.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "pathstuff.h"
21 #include "host-defs.h"
22 #include "filenames.h"
23 #include "gdb_tilde_expand.h"
25 #ifdef USE_WIN32API
26 #include <windows.h>
27 #endif
29 /* See gdbsupport/pathstuff.h. */
31 char *current_directory;
33 /* See gdbsupport/pathstuff.h. */
35 gdb::unique_xmalloc_ptr<char>
36 gdb_realpath (const char *filename)
38 /* On most hosts, we rely on canonicalize_file_name to compute
39 the FILENAME's realpath.
41 But the situation is slightly more complex on Windows, due to some
42 versions of GCC which were reported to generate paths where
43 backslashes (the directory separator) were doubled. For instance:
44 c:\\some\\double\\slashes\\dir
45 ... instead of ...
46 c:\some\double\slashes\dir
47 Those double-slashes were getting in the way when comparing paths,
48 for instance when trying to insert a breakpoint as follow:
49 (gdb) b c:/some/double/slashes/dir/foo.c:4
50 No source file named c:/some/double/slashes/dir/foo.c:4.
51 (gdb) b c:\some\double\slashes\dir\foo.c:4
52 No source file named c:\some\double\slashes\dir\foo.c:4.
53 To prevent this from happening, we need this function to always
54 strip those extra backslashes. While canonicalize_file_name does
55 perform this simplification, it only works when the path is valid.
56 Since the simplification would be useful even if the path is not
57 valid (one can always set a breakpoint on a file, even if the file
58 does not exist locally), we rely instead on GetFullPathName to
59 perform the canonicalization. */
61 #if defined (_WIN32)
63 char buf[MAX_PATH];
64 DWORD len = GetFullPathName (filename, MAX_PATH, buf, NULL);
66 /* The file system is case-insensitive but case-preserving.
67 So it is important we do not lowercase the path. Otherwise,
68 we might not be able to display the original casing in a given
69 path. */
70 if (len > 0 && len < MAX_PATH)
71 return make_unique_xstrdup (buf);
73 #else
75 char *rp = canonicalize_file_name (filename);
77 if (rp != NULL)
78 return gdb::unique_xmalloc_ptr<char> (rp);
80 #endif
82 /* This system is a lost cause, just dup the buffer. */
83 return make_unique_xstrdup (filename);
86 /* See gdbsupport/pathstuff.h. */
88 std::string
89 gdb_realpath_keepfile (const char *filename)
91 const char *base_name = lbasename (filename);
92 char *dir_name;
94 /* Extract the basename of filename, and return immediately
95 a copy of filename if it does not contain any directory prefix. */
96 if (base_name == filename)
97 return filename;
99 dir_name = (char *) alloca ((size_t) (base_name - filename + 2));
100 /* Allocate enough space to store the dir_name + plus one extra
101 character sometimes needed under Windows (see below), and
102 then the closing \000 character. */
103 strncpy (dir_name, filename, base_name - filename);
104 dir_name[base_name - filename] = '\000';
106 #ifdef HAVE_DOS_BASED_FILE_SYSTEM
107 /* We need to be careful when filename is of the form 'd:foo', which
108 is equivalent of d:./foo, which is totally different from d:/foo. */
109 if (strlen (dir_name) == 2 && isalpha (dir_name[0]) && dir_name[1] == ':')
111 dir_name[2] = '.';
112 dir_name[3] = '\000';
114 #endif
116 /* Canonicalize the directory prefix, and build the resulting
117 filename. If the dirname realpath already contains an ending
118 directory separator, avoid doubling it. */
119 gdb::unique_xmalloc_ptr<char> path_storage = gdb_realpath (dir_name);
120 const char *real_path = path_storage.get ();
121 return path_join (real_path, base_name);
124 /* See gdbsupport/pathstuff.h. */
126 std::string
127 gdb_abspath (const char *path, const char *cwd)
129 gdb_assert (path != NULL && path[0] != '\0');
131 if (path[0] == '~')
132 return gdb_tilde_expand (path);
134 if (IS_ABSOLUTE_PATH (path) || cwd == NULL)
135 return path;
137 return path_join (cwd, path);
140 /* See gdbsupport/pathstuff.h. */
142 const char *
143 child_path (const char *parent, const char *child)
145 /* The child path must start with the parent path. */
146 size_t parent_len = strlen (parent);
147 if (filename_ncmp (parent, child, parent_len) != 0)
148 return NULL;
150 /* The parent path must be a directory and the child must contain at
151 least one component underneath the parent. */
152 const char *child_component;
153 if (parent_len > 0 && IS_DIR_SEPARATOR (parent[parent_len - 1]))
155 /* The parent path ends in a directory separator, so it is a
156 directory. The first child component starts after the common
157 prefix. */
158 child_component = child + parent_len;
160 else
162 /* The parent path does not end in a directory separator. The
163 first character in the child after the common prefix must be
164 a directory separator.
166 Note that CHILD must hold at least parent_len characters for
167 filename_ncmp to return zero. If the character at parent_len
168 is nul due to CHILD containing the same path as PARENT, the
169 IS_DIR_SEPARATOR check will fail here. */
170 if (!IS_DIR_SEPARATOR (child[parent_len]))
171 return NULL;
173 /* The first child component starts after the separator after the
174 common prefix. */
175 child_component = child + parent_len + 1;
178 /* The child must contain at least one non-separator character after
179 the parent. */
180 while (*child_component != '\0')
182 if (!IS_DIR_SEPARATOR (*child_component))
183 return child_component;
185 child_component++;
187 return NULL;
190 /* See gdbsupport/pathstuff.h. */
192 std::string
193 path_join (gdb::array_view<const char *> paths)
195 std::string ret;
197 for (int i = 0; i < paths.size (); ++i)
199 const char *path = paths[i];
201 if (!ret.empty ())
203 /* If RET doesn't already end with a separator then add one. */
204 if (!IS_DIR_SEPARATOR (ret.back ()))
205 ret += '/';
207 /* Now that RET ends with a separator, ignore any at the start of
208 PATH. */
209 while (IS_DIR_SEPARATOR (path[0]))
210 ++path;
213 ret.append (path);
216 return ret;
219 /* See gdbsupport/pathstuff.h. */
221 bool
222 contains_dir_separator (const char *path)
224 for (; *path != '\0'; path++)
226 if (IS_DIR_SEPARATOR (*path))
227 return true;
230 return false;
233 /* See gdbsupport/pathstuff.h. */
235 std::string
236 get_standard_cache_dir ()
238 #ifdef __APPLE__
239 #define HOME_CACHE_DIR "Library/Caches"
240 #else
241 #define HOME_CACHE_DIR ".cache"
242 #endif
244 #ifndef __APPLE__
245 const char *xdg_cache_home = getenv ("XDG_CACHE_HOME");
246 if (xdg_cache_home != NULL && xdg_cache_home[0] != '\0')
248 /* Make sure the path is absolute and tilde-expanded. */
249 std::string abs = gdb_abspath (xdg_cache_home);
250 return path_join (abs.c_str (), "gdb");
252 #endif
254 const char *home = getenv ("HOME");
255 if (home != NULL && home[0] != '\0')
257 /* Make sure the path is absolute and tilde-expanded. */
258 std::string abs = gdb_abspath (home);
259 return path_join (abs.c_str (), HOME_CACHE_DIR, "gdb");
262 #ifdef WIN32
263 const char *win_home = getenv ("LOCALAPPDATA");
264 if (win_home != NULL && win_home[0] != '\0')
266 /* Make sure the path is absolute and tilde-expanded. */
267 std::string abs = gdb_abspath (win_home);
268 return path_join (abs.c_str (), "gdb");
270 #endif
272 return {};
275 /* See gdbsupport/pathstuff.h. */
277 std::string
278 get_standard_temp_dir ()
280 #ifdef WIN32
281 const char *tmp = getenv ("TMP");
282 if (tmp != nullptr)
283 return tmp;
285 tmp = getenv ("TEMP");
286 if (tmp != nullptr)
287 return tmp;
289 error (_("Couldn't find temp dir path, both TMP and TEMP are unset."));
291 #else
292 const char *tmp = getenv ("TMPDIR");
293 if (tmp != nullptr)
294 return tmp;
296 return "/tmp";
297 #endif
300 /* See pathstuff.h. */
302 std::string
303 get_standard_config_dir ()
305 #ifdef __APPLE__
306 #define HOME_CONFIG_DIR "Library/Preferences"
307 #else
308 #define HOME_CONFIG_DIR ".config"
309 #endif
311 #ifndef __APPLE__
312 const char *xdg_config_home = getenv ("XDG_CONFIG_HOME");
313 if (xdg_config_home != NULL && xdg_config_home[0] != '\0')
315 /* Make sure the path is absolute and tilde-expanded. */
316 std::string abs = gdb_abspath (xdg_config_home);
317 return path_join (abs.c_str (), "gdb");
319 #endif
321 const char *home = getenv ("HOME");
322 if (home != NULL && home[0] != '\0')
324 /* Make sure the path is absolute and tilde-expanded. */
325 std::string abs = gdb_abspath (home);
326 return path_join (abs.c_str (), HOME_CONFIG_DIR, "gdb");
329 return {};
332 /* See pathstuff.h. */
334 std::string
335 get_standard_config_filename (const char *filename)
337 std::string config_dir = get_standard_config_dir ();
338 if (config_dir != "")
340 const char *tmp = (*filename == '.') ? (filename + 1) : filename;
341 std::string path = config_dir + SLASH_STRING + std::string (tmp);
342 return path;
345 return {};
348 /* See pathstuff.h. */
350 std::string
351 find_gdb_home_config_file (const char *name, struct stat *buf)
353 gdb_assert (name != nullptr);
354 gdb_assert (*name != '\0');
356 std::string config_dir_file = get_standard_config_filename (name);
357 if (!config_dir_file.empty ())
359 if (stat (config_dir_file.c_str (), buf) == 0)
360 return config_dir_file;
363 const char *homedir = getenv ("HOME");
364 if (homedir != nullptr && homedir[0] != '\0')
366 /* Make sure the path is absolute and tilde-expanded. */
367 std::string abs = gdb_abspath (homedir);
368 std::string path = string_printf ("%s/%s", abs.c_str (), name);
369 if (stat (path.c_str (), buf) == 0)
370 return path;
373 return {};
376 /* See gdbsupport/pathstuff.h. */
378 const char *
379 get_shell ()
381 const char *ret = getenv ("SHELL");
382 if (ret == NULL)
383 ret = "/bin/sh";
385 return ret;
388 /* See gdbsupport/pathstuff.h. */
390 gdb::char_vector
391 make_temp_filename (const std::string &f)
393 gdb::char_vector filename_temp (f.length () + 8);
394 strcpy (filename_temp.data (), f.c_str ());
395 strcat (filename_temp.data () + f.size (), "-XXXXXX");
396 return filename_temp;