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-2024
   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 <http://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/strescape.h"      /* strutils_shell_unescape() */
  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     /* *INDENT-OFF* */
  80     typedef enum
  81     {
  82         copy_sym,
  83         subst_var
  84     } state_t;
  85     /* *INDENT-ON* */
  86 
  87     state_t state = copy_sym;
  88     GString *q;
  89     char *path_tilde, *path;
  90     char *p;
  91 
  92     /* Tilde expansion */
  93     path = strutils_shell_unescape (_path);
  94     path_tilde = tilde_expand (path);
  95     g_free (path);
  96 
  97     q = g_string_sized_new (32);
  98 
  99     /* Variable expansion */
 100     for (p = path_tilde; *p != '\0';)
 101     {
 102         switch (state)
 103         {
 104         case copy_sym:
 105             if (p[0] == '\\' && p[1] == '$')
 106             {
 107                 g_string_append_c (q, '$');
 108                 p += 2;
 109             }
 110             else if (p[0] != '$' || p[1] == '[' || p[1] == '(')
 111             {
 112                 g_string_append_c (q, *p);
 113                 p++;
 114             }
 115             else
 116                 state = subst_var;
 117             break;
 118 
 119         case subst_var:
 120             {
 121                 char *s = NULL;
 122                 char c;
 123                 const char *t = NULL;
 124 
 125                 /* skip dollar */
 126                 p++;
 127 
 128                 if (p[0] == '{')
 129                 {
 130                     p++;
 131                     s = strchr (p, '}');
 132                 }
 133                 if (s == NULL)
 134                     s = strchr (p, PATH_SEP);
 135                 if (s == NULL)
 136                     s = strchr (p, '\0');
 137                 c = *s;
 138                 *s = '\0';
 139                 t = getenv (p);
 140                 *s = c;
 141                 if (t == NULL)
 142                 {
 143                     g_string_append_c (q, '$');
 144                     if (p[-1] != '$')
 145                         g_string_append_c (q, '{');
 146                 }
 147                 else
 148                 {
 149                     g_string_append (q, t);
 150                     p = s;
 151                     if (*s == '}')
 152                         p++;
 153                 }
 154 
 155                 state = copy_sym;
 156                 break;
 157             }
 158 
 159         default:
 160             break;
 161         }
 162     }
 163 
 164     g_free (path_tilde);
 165 
 166     return q;
 167 }
 168 
 169 /* --------------------------------------------------------------------------------------------- */
 170 
 171 /* CDPATH handling */
 172 static gboolean
 173 handle_cdpath (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 174 {
 175     gboolean result = FALSE;
 176 
 177     /* CDPATH handling */
 178     if (!IS_PATH_SEP (*path))
 179     {
 180         char *cdpath, *p;
 181         char c;
 182 
 183         cdpath = g_strdup (getenv ("CDPATH"));
 184         p = cdpath;
 185         c = (p == NULL) ? '\0' : ':';
 186 
 187         while (!result && c == ':')
 188         {
 189             char *s;
 190 
 191             s = strchr (p, ':');
 192             if (s == NULL)
 193                 s = strchr (p, '\0');
 194             c = *s;
 195             *s = '\0';
 196             if (*p != '\0')
 197             {
 198                 vfs_path_t *r_vpath;
 199 
 200                 r_vpath = vfs_path_build_filename (p, path, (char *) NULL);
 201                 result = panel_cd (current_panel, r_vpath, cd_parse_command);
 202                 vfs_path_free (r_vpath, TRUE);
 203             }
 204             *s = c;
 205             p = s + 1;
 206         }
 207         g_free (cdpath);
 208     }
 209 
 210     return result;
 211 }
 212 
 213 /* --------------------------------------------------------------------------------------------- */
 214 /*** public functions ****************************************************************************/
 215 /* --------------------------------------------------------------------------------------------- */
 216 
 217 /** Execute the cd command to specified path
 218  *
 219  * @param path path to cd
 220  */
 221 
 222 void
 223 cd_to (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 224 {
 225     char *p;
 226 
 227     /* Remove leading whitespaces. */
 228     /* Any final whitespace should be removed here (to see why, try "cd fred "). */
 229     /* NOTE: I think we should not remove the extra space,
 230        that way, we can cd into hidden directories */
 231     /* FIXME: what about interpreting quoted strings like the shell.
 232        so one could type "cd <tab> M-a <enter>" and it would work. */
 233     p = g_strstrip (g_strdup (path));
 234 
 235     if (get_current_type () == view_tree)
 236     {
 237         vfs_path_t *new_vpath = NULL;
 238 
 239         if (p[0] == '\0')
 240         {
 241             new_vpath = vfs_path_from_str (mc_config_get_home_dir ());
 242             sync_tree (new_vpath);
 243         }
 244         else if (DIR_IS_DOTDOT (p))
 245         {
 246             if (vfs_path_elements_count (current_panel->cwd_vpath) != 1 ||
 247                 strlen (vfs_path_get_by_index (current_panel->cwd_vpath, 0)->path) > 1)
 248             {
 249                 vfs_path_t *tmp_vpath = current_panel->cwd_vpath;
 250 
 251                 current_panel->cwd_vpath =
 252                     vfs_path_vtokens_get (tmp_vpath, 0, vfs_path_tokens_count (tmp_vpath) - 1);
 253                 vfs_path_free (tmp_vpath, TRUE);
 254             }
 255             sync_tree (current_panel->cwd_vpath);
 256         }
 257         else
 258         {
 259             if (IS_PATH_SEP (*p))
 260                 new_vpath = vfs_path_from_str (p);
 261             else
 262                 new_vpath = vfs_path_append_new (current_panel->cwd_vpath, p, (char *) NULL);
 263 
 264             sync_tree (new_vpath);
 265         }
 266 
 267         vfs_path_free (new_vpath, TRUE);
 268     }
 269     else
 270     {
 271         GString *s_path;
 272         vfs_path_t *q_vpath;
 273         gboolean ok;
 274 
 275         s_path = examine_cd (p);
 276 
 277         if (s_path->len == 0)
 278             q_vpath = vfs_path_from_str (mc_config_get_home_dir ());
 279         else
 280             q_vpath = vfs_path_from_str_flags (s_path->str, VPF_NO_CANON);
 281 
 282         ok = panel_cd (current_panel, q_vpath, cd_parse_command);
 283         if (!ok)
 284             ok = handle_cdpath (s_path->str);
 285         if (!ok)
 286         {
 287             char *d;
 288 
 289             d = vfs_path_to_str_flags (q_vpath, 0, VPF_STRIP_PASSWORD);
 290             cd_error_message (d);
 291             g_free (d);
 292         }
 293 
 294         vfs_path_free (q_vpath, TRUE);
 295         g_string_free (s_path, TRUE);
 296     }
 297 
 298     g_free (p);
 299 }
 300 
 301 /* --------------------------------------------------------------------------------------------- */
 302 
 303 void
 304 cd_error_message (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 305 {
 306     message (D_ERROR, MSG_ERROR, _("Cannot change directory to\n%s\n%s"), path,
 307              unix_error_string (errno));
 308 }
 309 
 310 /* --------------------------------------------------------------------------------------------- */

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