root/src/filemanager/cd.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. examine_cd
  2. handle_cdpath
  3. cd_to
  4. cd_error_message

   1 /*
   2    cd_to() function.
   3 
   4    Copyright (C) 1995-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Slava Zanko <slavazanko@gmail.com>, 2013
   9    Andrew Borodin <aborodin@vmail.ru>, 2020
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  25  */
  26 
  27 /** \file cd.c
  28  *  \brief Source: cd_to() function
  29  */
  30 
  31 #include <config.h>
  32 
  33 #include <stdlib.h>
  34 #include <errno.h>
  35 #include <string.h>
  36 
  37 #include "lib/global.h"
  38 #include "lib/vfs/vfs.h"
  39 #include "lib/strutil.h"
  40 #include "lib/util.h"    // whitespace()
  41 #include "lib/widget.h"  // message()
  42 
  43 #include "filemanager.h"  // current_panel, panel.h, layout.h
  44 #include "tree.h"         // sync_tree()
  45 
  46 #include "cd.h"
  47 
  48 /*** global variables ****************************************************************************/
  49 
  50 /*** file scope macro definitions ****************************************************************/
  51 
  52 /*** file scope type declarations ****************************************************************/
  53 
  54 /*** forward declarations (file scope functions) *************************************************/
  55 
  56 /*** file scope variables ************************************************************************/
  57 
  58 /* --------------------------------------------------------------------------------------------- */
  59 /*** file scope functions ************************************************************************/
  60 /* --------------------------------------------------------------------------------------------- */
  61 
  62 /**
  63  * Expand the argument to "cd" and change directory.  First try tilde
  64  * expansion, then variable substitution.  If the CDPATH variable is set
  65  * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
  66  * We do not support such rare substitutions as ${var:-value} etc.
  67  * No quoting is implemented here, so ${VAR} and $VAR will be always
  68  * substituted.  Wildcards are not supported either.
  69  * Advanced users should be encouraged to use "\cd" instead of "cd" if
  70  * they want the behavior they are used to in the shell.
  71  *
  72  * @param _path string to examine
  73  * @return newly allocated string
  74  */
  75 
  76 static GString *
  77 examine_cd (const char *_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
  78 {
  79     typedef enum
  80     {
  81         copy_sym,
  82         subst_var
  83     } state_t;
  84 
  85     state_t state = copy_sym;
  86     GString *q;
  87     char *path_tilde, *path;
  88     char *p;
  89 
  90     // Tilde expansion
  91     path = str_shell_unescape (_path);
  92     path_tilde = tilde_expand (path);
  93     g_free (path);
  94 
  95     q = g_string_sized_new (32);
  96 
  97     // Variable expansion
  98     for (p = path_tilde; *p != '\0';)
  99     {
 100         switch (state)
 101         {
 102         case copy_sym:
 103             if (p[0] == '\\' && p[1] == '$')
 104             {
 105                 g_string_append_c (q, '$');
 106                 p += 2;
 107             }
 108             else if (p[0] != '$' || p[1] == '[' || p[1] == '(')
 109             {
 110                 g_string_append_c (q, *p);
 111                 p++;
 112             }
 113             else
 114                 state = subst_var;
 115             break;
 116 
 117         case subst_var:
 118         {
 119             char *s = NULL;
 120             char c;
 121             const char *t = NULL;
 122 
 123             // skip dollar
 124             p++;
 125 
 126             if (p[0] == '{')
 127             {
 128                 p++;
 129                 s = strchr (p, '}');
 130             }
 131             if (s == NULL)
 132                 s = strchr (p, PATH_SEP);
 133             if (s == NULL)
 134                 s = strchr (p, '\0');
 135             c = *s;
 136             *s = '\0';
 137             t = getenv (p);
 138             *s = c;
 139             if (t == NULL)
 140             {
 141                 g_string_append_c (q, '$');
 142                 if (p[-1] != '$')
 143                     g_string_append_c (q, '{');
 144             }
 145             else
 146             {
 147                 g_string_append (q, t);
 148                 p = s;
 149                 if (*s == '}')
 150                     p++;
 151             }
 152 
 153             state = copy_sym;
 154             break;
 155         }
 156 
 157         default:
 158             break;
 159         }
 160     }
 161 
 162     g_free (path_tilde);
 163 
 164     return q;
 165 }
 166 
 167 /* --------------------------------------------------------------------------------------------- */
 168 
 169 /* CDPATH handling */
 170 static gboolean
 171 handle_cdpath (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 172 {
 173     gboolean result = FALSE;
 174 
 175     // CDPATH handling
 176     if (!IS_PATH_SEP (*path))
 177     {
 178         char *cdpath, *p;
 179         char c;
 180 
 181         cdpath = g_strdup (getenv ("CDPATH"));
 182         p = cdpath;
 183         c = (p == NULL) ? '\0' : ':';
 184 
 185         while (!result && c == ':')
 186         {
 187             char *s;
 188 
 189             s = strchr (p, ':');
 190             if (s == NULL)
 191                 s = strchr (p, '\0');
 192             c = *s;
 193             *s = '\0';
 194             if (*p != '\0')
 195             {
 196                 vfs_path_t *r_vpath;
 197 
 198                 r_vpath = vfs_path_build_filename (p, path, (char *) NULL);
 199                 result = panel_cd (current_panel, r_vpath, cd_parse_command);
 200                 vfs_path_free (r_vpath, TRUE);
 201             }
 202             *s = c;
 203             p = s + 1;
 204         }
 205         g_free (cdpath);
 206     }
 207 
 208     return result;
 209 }
 210 
 211 /* --------------------------------------------------------------------------------------------- */
 212 /*** public functions ****************************************************************************/
 213 /* --------------------------------------------------------------------------------------------- */
 214 
 215 /** Execute the cd command to specified path
 216  *
 217  * @param path path to cd
 218  */
 219 
 220 void
 221 cd_to (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 222 {
 223     char *p;
 224 
 225     // Remove leading whitespaces.
 226     // Any final whitespace should be removed here (to see why, try "cd fred ").
 227     /* NOTE: I think we should not remove the extra space,
 228        that way, we can cd into hidden directories */
 229     /* FIXME: what about interpreting quoted strings like the shell.
 230        so one could type "cd <tab> M-a <enter>" and it would work. */
 231     p = g_strstrip (g_strdup (path));
 232 
 233     if (get_current_type () == view_tree)
 234     {
 235         vfs_path_t *new_vpath = NULL;
 236 
 237         if (p[0] == '\0')
 238         {
 239             new_vpath = vfs_path_from_str (mc_config_get_home_dir ());
 240             sync_tree (new_vpath);
 241         }
 242         else if (DIR_IS_DOTDOT (p))
 243         {
 244             if (vfs_path_elements_count (current_panel->cwd_vpath) != 1
 245                 || strlen (vfs_path_get_by_index (current_panel->cwd_vpath, 0)->path) > 1)
 246             {
 247                 vfs_path_t *tmp_vpath = current_panel->cwd_vpath;
 248 
 249                 current_panel->cwd_vpath =
 250                     vfs_path_vtokens_get (tmp_vpath, 0, vfs_path_tokens_count (tmp_vpath) - 1);
 251                 vfs_path_free (tmp_vpath, TRUE);
 252             }
 253             sync_tree (current_panel->cwd_vpath);
 254         }
 255         else
 256         {
 257             if (IS_PATH_SEP (*p))
 258                 new_vpath = vfs_path_from_str (p);
 259             else
 260                 new_vpath = vfs_path_append_new (current_panel->cwd_vpath, p, (char *) NULL);
 261 
 262             sync_tree (new_vpath);
 263         }
 264 
 265         vfs_path_free (new_vpath, TRUE);
 266     }
 267     else
 268     {
 269         GString *s_path;
 270         vfs_path_t *q_vpath;
 271         gboolean ok;
 272 
 273         s_path = examine_cd (p);
 274 
 275         if (s_path->len == 0)
 276             q_vpath = vfs_path_from_str (mc_config_get_home_dir ());
 277         else
 278             q_vpath = vfs_path_from_str_flags (s_path->str, VPF_NO_CANON);
 279 
 280         ok = panel_cd (current_panel, q_vpath, cd_parse_command);
 281         if (!ok)
 282             ok = handle_cdpath (s_path->str);
 283         if (!ok)
 284         {
 285             char *d;
 286 
 287             d = vfs_path_to_str_flags (q_vpath, 0, VPF_STRIP_PASSWORD);
 288             cd_error_message (d);
 289             g_free (d);
 290         }
 291 
 292         vfs_path_free (q_vpath, TRUE);
 293         g_string_free (s_path, TRUE);
 294     }
 295 
 296     g_free (p);
 297 }
 298 
 299 /* --------------------------------------------------------------------------------------------- */
 300 
 301 void
 302 cd_error_message (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 303 {
 304     message (D_ERROR, MSG_ERROR, _ ("Cannot change directory to\n%s\n%s"), path,
 305              unix_error_string (errno));
 306 }
 307 
 308 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */