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

   1 /*
   2    cd_to() function.
   3 
   4    Copyright (C) 1995-2021
   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 
  42 #include "filemanager.h"        /* current_panel, panel.h, layout.h */
  43 #include "tree.h"               /* sync_tree() */
  44 
  45 #include "cd.h"
  46 
  47 /*** global variables ****************************************************************************/
  48 
  49 /*** file scope macro definitions ****************************************************************/
  50 
  51 /*** file scope type declarations ****************************************************************/
  52 
  53 /*** file scope variables ************************************************************************/
  54 
  55 /* --------------------------------------------------------------------------------------------- */
  56 /*** file scope functions ************************************************************************/
  57 /* --------------------------------------------------------------------------------------------- */
  58 
  59 /**
  60  * Expand the argument to "cd" and change directory.  First try tilde
  61  * expansion, then variable substitution.  If the CDPATH variable is set
  62  * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
  63  * We do not support such rare substitutions as ${var:-value} etc.
  64  * No quoting is implemented here, so ${VAR} and $VAR will be always
  65  * substituted.  Wildcards are not supported either.
  66  * Advanced users should be encouraged to use "\cd" instead of "cd" if
  67  * they want the behavior they are used to in the shell.
  68  *
  69  * @param _path string to examine
  70  * @return newly allocated string
  71  */
  72 
  73 static GString *
  74 examine_cd (const char *_path)
     /* [previous][next][first][last][top][bottom][index][help]  */
  75 {
  76     /* *INDENT-OFF* */
  77     typedef enum
  78     {
  79         copy_sym,
  80         subst_var
  81     } state_t;
  82     /* *INDENT-ON* */
  83 
  84     state_t state = copy_sym;
  85     GString *q;
  86     char *path_tilde, *path;
  87     char *p;
  88 
  89     /* Tilde expansion */
  90     path = strutils_shell_unescape (_path);
  91     path_tilde = tilde_expand (path);
  92     g_free (path);
  93 
  94     q = g_string_sized_new (32);
  95 
  96     /* Variable expansion */
  97     for (p = path_tilde; *p != '\0';)
  98     {
  99         switch (state)
 100         {
 101         case copy_sym:
 102             if (p[0] == '\\' && p[1] == '$')
 103             {
 104                 g_string_append_c (q, '$');
 105                 p += 2;
 106             }
 107             else if (p[0] != '$' || p[1] == '[' || p[1] == '(')
 108             {
 109                 g_string_append_c (q, *p);
 110                 p++;
 111             }
 112             else
 113                 state = subst_var;
 114             break;
 115 
 116         case subst_var:
 117             {
 118                 char *s = NULL;
 119                 char c;
 120                 const char *t = NULL;
 121 
 122                 /* skip dollar */
 123                 p++;
 124 
 125                 if (p[0] == '{')
 126                 {
 127                     p++;
 128                     s = strchr (p, '}');
 129                 }
 130                 if (s == NULL)
 131                     s = strchr (p, PATH_SEP);
 132                 if (s == NULL)
 133                     s = strchr (p, '\0');
 134                 c = *s;
 135                 *s = '\0';
 136                 t = getenv (p);
 137                 *s = c;
 138                 if (t == NULL)
 139                 {
 140                     g_string_append_c (q, '$');
 141                     if (p[-1] != '$')
 142                         g_string_append_c (q, '{');
 143                 }
 144                 else
 145                 {
 146                     g_string_append (q, t);
 147                     p = s;
 148                     if (*s == '}')
 149                         p++;
 150                 }
 151 
 152                 state = copy_sym;
 153                 break;
 154             }
 155 
 156         default:
 157             break;
 158         }
 159     }
 160 
 161     g_free (path_tilde);
 162 
 163     return q;
 164 }
 165 
 166 /* --------------------------------------------------------------------------------------------- */
 167 
 168 /* CDPATH handling */
 169 static gboolean
 170 handle_cdpath (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 171 {
 172     gboolean result = FALSE;
 173 
 174     /* CDPATH handling */
 175     if (!IS_PATH_SEP (*path))
 176     {
 177         char *cdpath, *p;
 178         char c;
 179 
 180         cdpath = g_strdup (getenv ("CDPATH"));
 181         p = cdpath;
 182         c = (p == NULL) ? '\0' : ':';
 183 
 184         while (!result && c == ':')
 185         {
 186             char *s;
 187 
 188             s = strchr (p, ':');
 189             if (s == NULL)
 190                 s = strchr (p, '\0');
 191             c = *s;
 192             *s = '\0';
 193             if (*p != '\0')
 194             {
 195                 vfs_path_t *r_vpath;
 196 
 197                 r_vpath = vfs_path_build_filename (p, path, (char *) NULL);
 198                 result = panel_cd (current_panel, r_vpath, cd_parse_command);
 199                 vfs_path_free (r_vpath, TRUE);
 200             }
 201             *s = c;
 202             p = s + 1;
 203         }
 204         g_free (cdpath);
 205     }
 206 
 207     return result;
 208 }
 209 
 210 /* --------------------------------------------------------------------------------------------- */
 211 /*** public functions ****************************************************************************/
 212 /* --------------------------------------------------------------------------------------------- */
 213 
 214 /** Execute the cd command to specified path
 215  *
 216  * @param path path to cd
 217  */
 218 
 219 void
 220 cd_to (const char *path)
     /* [previous][next][first][last][top][bottom][index][help]  */
 221 {
 222     char *p;
 223 
 224     /* Remove leading whitespaces. */
 225     /* Any final whitespace should be removed here (to see why, try "cd fred "). */
 226     /* NOTE: I think we should not remove the extra space,
 227        that way, we can cd into hidden directories */
 228     /* FIXME: what about interpreting quoted strings like the shell.
 229        so one could type "cd <tab> M-a <enter>" and it would work. */
 230     p = g_strstrip (g_strdup (path));
 231 
 232     if (get_current_type () == view_tree)
 233     {
 234         vfs_path_t *new_vpath = NULL;
 235 
 236         if (p[0] == '\0')
 237         {
 238             new_vpath = vfs_path_from_str (mc_config_get_home_dir ());
 239             sync_tree (new_vpath);
 240         }
 241         else if (DIR_IS_DOTDOT (p))
 242         {
 243             if (vfs_path_elements_count (current_panel->cwd_vpath) != 1 ||
 244                 strlen (vfs_path_get_by_index (current_panel->cwd_vpath, 0)->path) > 1)
 245             {
 246                 vfs_path_t *tmp_vpath = current_panel->cwd_vpath;
 247 
 248                 current_panel->cwd_vpath =
 249                     vfs_path_vtokens_get (tmp_vpath, 0, vfs_path_tokens_count (tmp_vpath) - 1);
 250                 vfs_path_free (tmp_vpath, TRUE);
 251             }
 252             sync_tree (current_panel->cwd_vpath);
 253         }
 254         else
 255         {
 256             if (IS_PATH_SEP (*p))
 257                 new_vpath = vfs_path_from_str (p);
 258             else
 259                 new_vpath = vfs_path_append_new (current_panel->cwd_vpath, p, (char *) NULL);
 260 
 261             sync_tree (new_vpath);
 262         }
 263 
 264         vfs_path_free (new_vpath, TRUE);
 265     }
 266     else
 267     {
 268         GString *s_path;
 269         vfs_path_t *q_vpath;
 270         gboolean ok;
 271 
 272         s_path = examine_cd (p);
 273 
 274         if (s_path->len == 0)
 275             q_vpath = vfs_path_from_str (mc_config_get_home_dir ());
 276         else
 277             q_vpath = vfs_path_from_str_flags (s_path->str, VPF_NO_CANON);
 278 
 279         ok = panel_cd (current_panel, q_vpath, cd_parse_command);
 280         if (!ok)
 281             ok = handle_cdpath (s_path->str);
 282         if (!ok)
 283         {
 284             char *d;
 285 
 286             d = vfs_path_to_str_flags (q_vpath, 0, VPF_STRIP_PASSWORD);
 287             message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"), d,
 288                      unix_error_string (errno));
 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 /* --------------------------------------------------------------------------------------------- */

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