Home | History | Annotate | Line # | Download | only in gdbsupport
pathstuff.cc revision 1.1.1.4
      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, const char *cwd)
    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) || cwd == NULL)
    135     return path;
    136 
    137   return path_join (cwd, 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 (!ret.empty ())
    202 	{
    203 	  /* If RET doesn't already end with a separator then add one.  */
    204 	  if (!IS_DIR_SEPARATOR (ret.back ()))
    205 	    ret += '/';
    206 
    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;
    211 	}
    212 
    213       ret.append (path);
    214     }
    215 
    216   return ret;
    217 }
    218 
    219 /* See gdbsupport/pathstuff.h.  */
    220 
    221 bool
    222 contains_dir_separator (const char *path)
    223 {
    224   for (; *path != '\0'; path++)
    225     {
    226       if (IS_DIR_SEPARATOR (*path))
    227 	return true;
    228     }
    229 
    230   return false;
    231 }
    232 
    233 /* See gdbsupport/pathstuff.h.  */
    234 
    235 std::string
    236 get_standard_cache_dir ()
    237 {
    238 #ifdef __APPLE__
    239 #define HOME_CACHE_DIR "Library/Caches"
    240 #else
    241 #define HOME_CACHE_DIR ".cache"
    242 #endif
    243 
    244 #ifndef __APPLE__
    245   const char *xdg_cache_home = getenv ("XDG_CACHE_HOME");
    246   if (xdg_cache_home != NULL && xdg_cache_home[0] != '\0')
    247     {
    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");
    251     }
    252 #endif
    253 
    254   const char *home = getenv ("HOME");
    255   if (home != NULL && home[0] != '\0')
    256     {
    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");
    260     }
    261 
    262 #ifdef WIN32
    263   const char *win_home = getenv ("LOCALAPPDATA");
    264   if (win_home != NULL && win_home[0] != '\0')
    265     {
    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");
    269     }
    270 #endif
    271 
    272   return {};
    273 }
    274 
    275 /* See gdbsupport/pathstuff.h.  */
    276 
    277 std::string
    278 get_standard_temp_dir ()
    279 {
    280 #ifdef WIN32
    281   const char *tmp = getenv ("TMP");
    282   if (tmp != nullptr)
    283     return tmp;
    284 
    285   tmp = getenv ("TEMP");
    286   if (tmp != nullptr)
    287     return tmp;
    288 
    289   error (_("Couldn't find temp dir path, both TMP and TEMP are unset."));
    290 
    291 #else
    292   const char *tmp = getenv ("TMPDIR");
    293   if (tmp != nullptr)
    294     return tmp;
    295 
    296   return "/tmp";
    297 #endif
    298 }
    299 
    300 /* See pathstuff.h.  */
    301 
    302 std::string
    303 get_standard_config_dir ()
    304 {
    305 #ifdef __APPLE__
    306 #define HOME_CONFIG_DIR "Library/Preferences"
    307 #else
    308 #define HOME_CONFIG_DIR ".config"
    309 #endif
    310 
    311 #ifndef __APPLE__
    312   const char *xdg_config_home = getenv ("XDG_CONFIG_HOME");
    313   if (xdg_config_home != NULL && xdg_config_home[0] != '\0')
    314     {
    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");
    318     }
    319 #endif
    320 
    321   const char *home = getenv ("HOME");
    322   if (home != NULL && home[0] != '\0')
    323     {
    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");
    327     }
    328 
    329   return {};
    330 }
    331 
    332 /* See pathstuff.h. */
    333 
    334 std::string
    335 get_standard_config_filename (const char *filename)
    336 {
    337   std::string config_dir = get_standard_config_dir ();
    338   if (config_dir != "")
    339     {
    340       const char *tmp = (*filename == '.') ? (filename + 1) : filename;
    341       std::string path = config_dir + SLASH_STRING + std::string (tmp);
    342       return path;
    343     }
    344 
    345   return {};
    346 }
    347 
    348 /* See pathstuff.h.  */
    349 
    350 std::string
    351 find_gdb_home_config_file (const char *name, struct stat *buf)
    352 {
    353   gdb_assert (name != nullptr);
    354   gdb_assert (*name != '\0');
    355 
    356   std::string config_dir_file = get_standard_config_filename (name);
    357   if (!config_dir_file.empty ())
    358     {
    359       if (stat (config_dir_file.c_str (), buf) == 0)
    360 	return config_dir_file;
    361     }
    362 
    363   const char *homedir = getenv ("HOME");
    364   if (homedir != nullptr && homedir[0] != '\0')
    365     {
    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;
    371     }
    372 
    373   return {};
    374 }
    375 
    376 /* See gdbsupport/pathstuff.h.  */
    377 
    378 const char *
    379 get_shell ()
    380 {
    381   const char *ret = getenv ("SHELL");
    382   if (ret == NULL)
    383     ret = "/bin/sh";
    384 
    385   return ret;
    386 }
    387 
    388 /* See gdbsupport/pathstuff.h.  */
    389 
    390 gdb::char_vector
    391 make_temp_filename (const std::string &f)
    392 {
    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;
    397 }
    398