1 /* Path manipulation routines for GDB and gdbserver. 2 3 Copyright (C) 1986-2023 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 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. 11 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. 16 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/>. */ 19 20 #include "common-defs.h" 21 #include "pathstuff.h" 22 #include "host-defs.h" 23 #include "filenames.h" 24 #include "gdb_tilde_expand.h" 25 26 #ifdef USE_WIN32API 27 #include <windows.h> 28 #endif 29 30 /* See gdbsupport/pathstuff.h. */ 31 32 char *current_directory; 33 34 /* See gdbsupport/pathstuff.h. */ 35 36 gdb::unique_xmalloc_ptr<char> 37 gdb_realpath (const char *filename) 38 { 39 /* On most hosts, we rely on canonicalize_file_name to compute 40 the FILENAME's realpath. 41 42 But the situation is slightly more complex on Windows, due to some 43 versions of GCC which were reported to generate paths where 44 backlashes (the directory separator) were doubled. For instance: 45 c:\\some\\double\\slashes\\dir 46 ... instead of ... 47 c:\some\double\slashes\dir 48 Those double-slashes were getting in the way when comparing paths, 49 for instance when trying to insert a breakpoint as follow: 50 (gdb) b c:/some/double/slashes/dir/foo.c:4 51 No source file named c:/some/double/slashes/dir/foo.c:4. 52 (gdb) b c:\some\double\slashes\dir\foo.c:4 53 No source file named c:\some\double\slashes\dir\foo.c:4. 54 To prevent this from happening, we need this function to always 55 strip those extra backslashes. While canonicalize_file_name does 56 perform this simplification, it only works when the path is valid. 57 Since the simplification would be useful even if the path is not 58 valid (one can always set a breakpoint on a file, even if the file 59 does not exist locally), we rely instead on GetFullPathName to 60 perform the canonicalization. */ 61 62 #if defined (_WIN32) 63 { 64 char buf[MAX_PATH]; 65 DWORD len = GetFullPathName (filename, MAX_PATH, buf, NULL); 66 67 /* The file system is case-insensitive but case-preserving. 68 So it is important we do not lowercase the path. Otherwise, 69 we might not be able to display the original casing in a given 70 path. */ 71 if (len > 0 && len < MAX_PATH) 72 return make_unique_xstrdup (buf); 73 } 74 #else 75 { 76 char *rp = canonicalize_file_name (filename); 77 78 if (rp != NULL) 79 return gdb::unique_xmalloc_ptr<char> (rp); 80 } 81 #endif 82 83 /* This system is a lost cause, just dup the buffer. */ 84 return make_unique_xstrdup (filename); 85 } 86 87 /* See gdbsupport/pathstuff.h. */ 88 89 std::string 90 gdb_realpath_keepfile (const char *filename) 91 { 92 const char *base_name = lbasename (filename); 93 char *dir_name; 94 95 /* Extract the basename of filename, and return immediately 96 a copy of filename if it does not contain any directory prefix. */ 97 if (base_name == filename) 98 return filename; 99 100 dir_name = (char *) alloca ((size_t) (base_name - filename + 2)); 101 /* Allocate enough space to store the dir_name + plus one extra 102 character sometimes needed under Windows (see below), and 103 then the closing \000 character. */ 104 strncpy (dir_name, filename, base_name - filename); 105 dir_name[base_name - filename] = '\000'; 106 107 #ifdef HAVE_DOS_BASED_FILE_SYSTEM 108 /* We need to be careful when filename is of the form 'd:foo', which 109 is equivalent of d:./foo, which is totally different from d:/foo. */ 110 if (strlen (dir_name) == 2 && isalpha (dir_name[0]) && dir_name[1] == ':') 111 { 112 dir_name[2] = '.'; 113 dir_name[3] = '\000'; 114 } 115 #endif 116 117 /* Canonicalize the directory prefix, and build the resulting 118 filename. If the dirname realpath already contains an ending 119 directory separator, avoid doubling it. */ 120 gdb::unique_xmalloc_ptr<char> path_storage = gdb_realpath (dir_name); 121 const char *real_path = path_storage.get (); 122 return path_join (real_path, base_name); 123 } 124 125 /* See gdbsupport/pathstuff.h. */ 126 127 std::string 128 gdb_abspath (const char *path) 129 { 130 gdb_assert (path != NULL && path[0] != '\0'); 131 132 if (path[0] == '~') 133 return gdb_tilde_expand (path); 134 135 if (IS_ABSOLUTE_PATH (path) || current_directory == NULL) 136 return path; 137 138 return path_join (current_directory, path); 139 } 140 141 /* See gdbsupport/pathstuff.h. */ 142 143 const char * 144 child_path (const char *parent, const char *child) 145 { 146 /* The child path must start with the parent path. */ 147 size_t parent_len = strlen (parent); 148 if (filename_ncmp (parent, child, parent_len) != 0) 149 return NULL; 150 151 /* The parent path must be a directory and the child must contain at 152 least one component underneath the parent. */ 153 const char *child_component; 154 if (parent_len > 0 && IS_DIR_SEPARATOR (parent[parent_len - 1])) 155 { 156 /* The parent path ends in a directory separator, so it is a 157 directory. The first child component starts after the common 158 prefix. */ 159 child_component = child + parent_len; 160 } 161 else 162 { 163 /* The parent path does not end in a directory separator. The 164 first character in the child after the common prefix must be 165 a directory separator. 166 167 Note that CHILD must hold at least parent_len characters for 168 filename_ncmp to return zero. If the character at parent_len 169 is nul due to CHILD containing the same path as PARENT, the 170 IS_DIR_SEPARATOR check will fail here. */ 171 if (!IS_DIR_SEPARATOR (child[parent_len])) 172 return NULL; 173 174 /* The first child component starts after the separator after the 175 common prefix. */ 176 child_component = child + parent_len + 1; 177 } 178 179 /* The child must contain at least one non-separator character after 180 the parent. */ 181 while (*child_component != '\0') 182 { 183 if (!IS_DIR_SEPARATOR (*child_component)) 184 return child_component; 185 186 child_component++; 187 } 188 return NULL; 189 } 190 191 /* See gdbsupport/pathstuff.h. */ 192 193 std::string 194 path_join (gdb::array_view<const char *> paths) 195 { 196 std::string ret; 197 198 for (int i = 0; i < paths.size (); ++i) 199 { 200 const char *path = paths[i]; 201 202 if (i > 0) 203 gdb_assert (strlen (path) == 0 || !IS_ABSOLUTE_PATH (path)); 204 205 if (!ret.empty () && !IS_DIR_SEPARATOR (ret.back ())) 206 ret += '/'; 207 208 ret.append (path); 209 } 210 211 return ret; 212 } 213 214 /* See gdbsupport/pathstuff.h. */ 215 216 bool 217 contains_dir_separator (const char *path) 218 { 219 for (; *path != '\0'; path++) 220 { 221 if (IS_DIR_SEPARATOR (*path)) 222 return true; 223 } 224 225 return false; 226 } 227 228 /* See gdbsupport/pathstuff.h. */ 229 230 std::string 231 get_standard_cache_dir () 232 { 233 #ifdef __APPLE__ 234 #define HOME_CACHE_DIR "Library/Caches" 235 #else 236 #define HOME_CACHE_DIR ".cache" 237 #endif 238 239 #ifndef __APPLE__ 240 const char *xdg_cache_home = getenv ("XDG_CACHE_HOME"); 241 if (xdg_cache_home != NULL && xdg_cache_home[0] != '\0') 242 { 243 /* Make sure the path is absolute and tilde-expanded. */ 244 std::string abs = gdb_abspath (xdg_cache_home); 245 return path_join (abs.c_str (), "gdb"); 246 } 247 #endif 248 249 const char *home = getenv ("HOME"); 250 if (home != NULL && home[0] != '\0') 251 { 252 /* Make sure the path is absolute and tilde-expanded. */ 253 std::string abs = gdb_abspath (home); 254 return path_join (abs.c_str (), HOME_CACHE_DIR, "gdb"); 255 } 256 257 #ifdef WIN32 258 const char *win_home = getenv ("LOCALAPPDATA"); 259 if (win_home != NULL && win_home[0] != '\0') 260 { 261 /* Make sure the path is absolute and tilde-expanded. */ 262 std::string abs = gdb_abspath (win_home); 263 return path_join (abs.c_str (), "gdb"); 264 } 265 #endif 266 267 return {}; 268 } 269 270 /* See gdbsupport/pathstuff.h. */ 271 272 std::string 273 get_standard_temp_dir () 274 { 275 #ifdef WIN32 276 const char *tmp = getenv ("TMP"); 277 if (tmp != nullptr) 278 return tmp; 279 280 tmp = getenv ("TEMP"); 281 if (tmp != nullptr) 282 return tmp; 283 284 error (_("Couldn't find temp dir path, both TMP and TEMP are unset.")); 285 286 #else 287 const char *tmp = getenv ("TMPDIR"); 288 if (tmp != nullptr) 289 return tmp; 290 291 return "/tmp"; 292 #endif 293 } 294 295 /* See pathstuff.h. */ 296 297 std::string 298 get_standard_config_dir () 299 { 300 #ifdef __APPLE__ 301 #define HOME_CONFIG_DIR "Library/Preferences" 302 #else 303 #define HOME_CONFIG_DIR ".config" 304 #endif 305 306 #ifndef __APPLE__ 307 const char *xdg_config_home = getenv ("XDG_CONFIG_HOME"); 308 if (xdg_config_home != NULL && xdg_config_home[0] != '\0') 309 { 310 /* Make sure the path is absolute and tilde-expanded. */ 311 std::string abs = gdb_abspath (xdg_config_home); 312 return path_join (abs.c_str (), "gdb"); 313 } 314 #endif 315 316 const char *home = getenv ("HOME"); 317 if (home != NULL && home[0] != '\0') 318 { 319 /* Make sure the path is absolute and tilde-expanded. */ 320 std::string abs = gdb_abspath (home); 321 return path_join (abs.c_str (), HOME_CONFIG_DIR, "gdb"); 322 } 323 324 return {}; 325 } 326 327 /* See pathstuff.h. */ 328 329 std::string 330 get_standard_config_filename (const char *filename) 331 { 332 std::string config_dir = get_standard_config_dir (); 333 if (config_dir != "") 334 { 335 const char *tmp = (*filename == '.') ? (filename + 1) : filename; 336 std::string path = config_dir + SLASH_STRING + std::string (tmp); 337 return path; 338 } 339 340 return {}; 341 } 342 343 /* See pathstuff.h. */ 344 345 std::string 346 find_gdb_home_config_file (const char *name, struct stat *buf) 347 { 348 gdb_assert (name != nullptr); 349 gdb_assert (*name != '\0'); 350 351 std::string config_dir_file = get_standard_config_filename (name); 352 if (!config_dir_file.empty ()) 353 { 354 if (stat (config_dir_file.c_str (), buf) == 0) 355 return config_dir_file; 356 } 357 358 const char *homedir = getenv ("HOME"); 359 if (homedir != nullptr && homedir[0] != '\0') 360 { 361 /* Make sure the path is absolute and tilde-expanded. */ 362 std::string abs = gdb_abspath (homedir); 363 std::string path = string_printf ("%s/%s", abs.c_str (), name); 364 if (stat (path.c_str (), buf) == 0) 365 return path; 366 } 367 368 return {}; 369 } 370 371 /* See gdbsupport/pathstuff.h. */ 372 373 const char * 374 get_shell () 375 { 376 const char *ret = getenv ("SHELL"); 377 if (ret == NULL) 378 ret = "/bin/sh"; 379 380 return ret; 381 } 382 383 /* See gdbsupport/pathstuff.h. */ 384 385 gdb::char_vector 386 make_temp_filename (const std::string &f) 387 { 388 gdb::char_vector filename_temp (f.length () + 8); 389 strcpy (filename_temp.data (), f.c_str ()); 390 strcat (filename_temp.data () + f.size (), "-XXXXXX"); 391 return filename_temp; 392 } 393