Home | History | Annotate | Line # | Download | only in gdbsupport
pathstuff.cc revision 1.1.1.2.2.1
      1 /* Path manipulation routines for GDB and gdbserver.
      2 
      3    Copyright (C) 1986-2024 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 "pathstuff.h"
     21 #include "host-defs.h"
     22 #include "filenames.h"
     23 #include "gdb_tilde_expand.h"
     24 
     25 #ifdef USE_WIN32API
     26 #include <windows.h>
     27 #endif
     28 
     29 /* See gdbsupport/pathstuff.h.  */
     30 
     31 char *current_directory;
     32 
     33 /* See gdbsupport/pathstuff.h.  */
     34 
     35 gdb::unique_xmalloc_ptr<char>
     36 gdb_realpath (const char *filename)
     37 {
     38 /* On most hosts, we rely on canonicalize_file_name to compute
     39    the FILENAME's realpath.
     40 
     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.  */
     60 
     61 #if defined (_WIN32)
     62   {
     63     char buf[MAX_PATH];
     64     DWORD len = GetFullPathName (filename, MAX_PATH, buf, NULL);
     65 
     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);
     72   }
     73 #else
     74   {
     75     char *rp = canonicalize_file_name (filename);
     76 
     77     if (rp != NULL)
     78       return gdb::unique_xmalloc_ptr<char> (rp);
     79   }
     80 #endif
     81 
     82   /* This system is a lost cause, just dup the buffer.  */
     83   return make_unique_xstrdup (filename);
     84 }
     85 
     86 /* See gdbsupport/pathstuff.h.  */
     87 
     88 std::string
     89 gdb_realpath_keepfile (const char *filename)
     90 {
     91   const char *base_name = lbasename (filename);
     92   char *dir_name;
     93 
     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;
     98 
     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';
    105 
    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] == ':')
    110     {
    111       dir_name[2] = '.';
    112       dir_name[3] = '\000';
    113     }
    114 #endif
    115 
    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);
    122 }
    123 
    124 /* See gdbsupport/pathstuff.h.  */
    125 
    126 std::string
    127 gdb_abspath (const char *path)
    128 {
    129   gdb_assert (path != NULL && path[0] != '\0');
    130 
    131   if (path[0] == '~')
    132     return gdb_tilde_expand (path);
    133 
    134   if (IS_ABSOLUTE_PATH (path) || current_directory == NULL)
    135     return path;
    136 
    137   return path_join (current_directory, path);
    138 }
    139 
    140 /* See gdbsupport/pathstuff.h.  */
    141 
    142 const char *
    143 child_path (const char *parent, const char *child)
    144 {
    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;
    149 
    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]))
    154     {
    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;
    159     }
    160   else
    161     {
    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.
    165 
    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;
    172 
    173       /* The first child component starts after the separator after the
    174 	 common prefix.  */
    175       child_component = child + parent_len + 1;
    176     }
    177 
    178   /* The child must contain at least one non-separator character after
    179      the parent.  */
    180   while (*child_component != '\0')
    181     {
    182       if (!IS_DIR_SEPARATOR (*child_component))
    183 	return child_component;
    184 
    185       child_component++;
    186     }
    187   return NULL;
    188 }
    189 
    190 /* See gdbsupport/pathstuff.h.  */
    191 
    192 std::string
    193 path_join (gdb::array_view<const char *> paths)
    194 {
    195   std::string ret;
    196 
    197   for (int i = 0; i < paths.size (); ++i)
    198     {
    199       const char *path = paths[i];
    200 
    201       if (i > 0)
    202 	gdb_assert (strlen (path) == 0 || !IS_ABSOLUTE_PATH (path));
    203 
    204       if (!ret.empty () && !IS_DIR_SEPARATOR (ret.back ()))
    205 	  ret += '/';
    206 
    207       ret.append (path);
    208     }
    209 
    210   return ret;
    211 }
    212 
    213 /* See gdbsupport/pathstuff.h.  */
    214 
    215 bool
    216 contains_dir_separator (const char *path)
    217 {
    218   for (; *path != '\0'; path++)
    219     {
    220       if (IS_DIR_SEPARATOR (*path))
    221 	return true;
    222     }
    223 
    224   return false;
    225 }
    226 
    227 /* See gdbsupport/pathstuff.h.  */
    228 
    229 std::string
    230 get_standard_cache_dir ()
    231 {
    232 #ifdef __APPLE__
    233 #define HOME_CACHE_DIR "Library/Caches"
    234 #else
    235 #define HOME_CACHE_DIR ".cache"
    236 #endif
    237 
    238 #ifndef __APPLE__
    239   const char *xdg_cache_home = getenv ("XDG_CACHE_HOME");
    240   if (xdg_cache_home != NULL && xdg_cache_home[0] != '\0')
    241     {
    242       /* Make sure the path is absolute and tilde-expanded.  */
    243       std::string abs = gdb_abspath (xdg_cache_home);
    244       return path_join (abs.c_str (), "gdb");
    245     }
    246 #endif
    247 
    248   const char *home = getenv ("HOME");
    249   if (home != NULL && home[0] != '\0')
    250     {
    251       /* Make sure the path is absolute and tilde-expanded.  */
    252       std::string abs = gdb_abspath (home);
    253       return path_join (abs.c_str (), HOME_CACHE_DIR,  "gdb");
    254     }
    255 
    256 #ifdef WIN32
    257   const char *win_home = getenv ("LOCALAPPDATA");
    258   if (win_home != NULL && win_home[0] != '\0')
    259     {
    260       /* Make sure the path is absolute and tilde-expanded.  */
    261       std::string abs = gdb_abspath (win_home);
    262       return path_join (abs.c_str (), "gdb");
    263     }
    264 #endif
    265 
    266   return {};
    267 }
    268 
    269 /* See gdbsupport/pathstuff.h.  */
    270 
    271 std::string
    272 get_standard_temp_dir ()
    273 {
    274 #ifdef WIN32
    275   const char *tmp = getenv ("TMP");
    276   if (tmp != nullptr)
    277     return tmp;
    278 
    279   tmp = getenv ("TEMP");
    280   if (tmp != nullptr)
    281     return tmp;
    282 
    283   error (_("Couldn't find temp dir path, both TMP and TEMP are unset."));
    284 
    285 #else
    286   const char *tmp = getenv ("TMPDIR");
    287   if (tmp != nullptr)
    288     return tmp;
    289 
    290   return "/tmp";
    291 #endif
    292 }
    293 
    294 /* See pathstuff.h.  */
    295 
    296 std::string
    297 get_standard_config_dir ()
    298 {
    299 #ifdef __APPLE__
    300 #define HOME_CONFIG_DIR "Library/Preferences"
    301 #else
    302 #define HOME_CONFIG_DIR ".config"
    303 #endif
    304 
    305 #ifndef __APPLE__
    306   const char *xdg_config_home = getenv ("XDG_CONFIG_HOME");
    307   if (xdg_config_home != NULL && xdg_config_home[0] != '\0')
    308     {
    309       /* Make sure the path is absolute and tilde-expanded.  */
    310       std::string abs = gdb_abspath (xdg_config_home);
    311       return path_join (abs.c_str (), "gdb");
    312     }
    313 #endif
    314 
    315   const char *home = getenv ("HOME");
    316   if (home != NULL && home[0] != '\0')
    317     {
    318       /* Make sure the path is absolute and tilde-expanded.  */
    319       std::string abs = gdb_abspath (home);
    320       return path_join (abs.c_str (), HOME_CONFIG_DIR, "gdb");
    321     }
    322 
    323   return {};
    324 }
    325 
    326 /* See pathstuff.h. */
    327 
    328 std::string
    329 get_standard_config_filename (const char *filename)
    330 {
    331   std::string config_dir = get_standard_config_dir ();
    332   if (config_dir != "")
    333     {
    334       const char *tmp = (*filename == '.') ? (filename + 1) : filename;
    335       std::string path = config_dir + SLASH_STRING + std::string (tmp);
    336       return path;
    337     }
    338 
    339   return {};
    340 }
    341 
    342 /* See pathstuff.h.  */
    343 
    344 std::string
    345 find_gdb_home_config_file (const char *name, struct stat *buf)
    346 {
    347   gdb_assert (name != nullptr);
    348   gdb_assert (*name != '\0');
    349 
    350   std::string config_dir_file = get_standard_config_filename (name);
    351   if (!config_dir_file.empty ())
    352     {
    353       if (stat (config_dir_file.c_str (), buf) == 0)
    354 	return config_dir_file;
    355     }
    356 
    357   const char *homedir = getenv ("HOME");
    358   if (homedir != nullptr && homedir[0] != '\0')
    359     {
    360       /* Make sure the path is absolute and tilde-expanded.  */
    361       std::string abs = gdb_abspath (homedir);
    362       std::string path = string_printf ("%s/%s", abs.c_str (), name);
    363       if (stat (path.c_str (), buf) == 0)
    364 	return path;
    365     }
    366 
    367   return {};
    368 }
    369 
    370 /* See gdbsupport/pathstuff.h.  */
    371 
    372 const char *
    373 get_shell ()
    374 {
    375   const char *ret = getenv ("SHELL");
    376   if (ret == NULL)
    377     ret = "/bin/sh";
    378 
    379   return ret;
    380 }
    381 
    382 /* See gdbsupport/pathstuff.h.  */
    383 
    384 gdb::char_vector
    385 make_temp_filename (const std::string &f)
    386 {
    387   gdb::char_vector filename_temp (f.length () + 8);
    388   strcpy (filename_temp.data (), f.c_str ());
    389   strcat (filename_temp.data () + f.size (), "-XXXXXX");
    390   return filename_temp;
    391 }
    392