Code indentation.
[midnight-commander.git] / src / filemanager / command.c
blobdc7385290bc3d0513bbc6e3098f33dc93b69685a
1 /*
2 Command line widget.
3 This widget is derived from the WInput widget, it's used to cope
4 with all the magic of the command input line, we depend on some
5 help from the program's callback.
7 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
8 2007, 2011
9 The Free Software Foundation, Inc.
11 This file is part of the Midnight Commander.
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.
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.
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/>.
27 /** \file command.c
28 * \brief Source: command line widget
31 #include <config.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <string.h>
37 #include "lib/global.h" /* home_dir */
38 #include "lib/tty/tty.h"
39 #include "lib/vfs/vfs.h"
40 #include "lib/strescape.h"
41 #include "lib/skin.h" /* DEFAULT_COLOR */
42 #include "lib/util.h"
43 #include "lib/widget.h"
45 #include "src/main.h" /* do_cd */
46 #include "src/subshell.h" /* SUBSHELL_EXIT */
47 #include "src/execute.h" /* shell_execute */
49 #include "midnight.h" /* current_panel */
50 #include "layout.h" /* for command_prompt variable */
51 #include "usermenu.h" /* expand_format */
52 #include "tree.h" /* for tree_chdir */
54 #include "command.h"
56 /*** global variables ****************************************************************************/
58 /* This holds the command line */
59 WInput *cmdline;
61 /*** file scope macro definitions ****************************************************************/
63 #define CD_OPERAND_OFFSET 3
65 /*** file scope type declarations ****************************************************************/
67 /*** file scope variables ************************************************************************/
69 /*** file scope functions ************************************************************************/
70 /* --------------------------------------------------------------------------------------------- */
72 /**
73 * Expand the argument to "cd" and change directory. First try tilde
74 * expansion, then variable substitution. If the CDPATH variable is set
75 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
76 * We do not support such rare substitutions as ${var:-value} etc.
77 * No quoting is implemented here, so ${VAR} and $VAR will be always
78 * substituted. Wildcards are not supported either.
79 * Advanced users should be encouraged to use "\cd" instead of "cd" if
80 * they want the behavior they are used to in the shell.
83 static char *
84 examine_cd (const char *_path)
86 typedef enum
87 { copy_sym, subst_var } state_t;
89 state_t state = copy_sym;
90 char *q;
91 size_t qlen;
92 char *path_tilde, *path;
93 char *p, *r;
95 /* Tilde expansion */
96 path = strutils_shell_unescape (_path);
97 path_tilde = tilde_expand (path);
98 g_free (path);
100 /* Leave space for further expansion */
101 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
102 q = g_malloc (qlen);
104 /* Variable expansion */
105 for (p = path_tilde, r = q; *p != '\0' && r < q + MC_MAXPATHLEN;)
108 switch (state)
110 case copy_sym:
111 if (p[0] == '\\' && p[1] == '$')
113 /* skip backslash */
114 p++;
115 /* copy dollar */
116 *(r++) = *(p++);
118 else if (p[0] != '$' || p[1] == '[' || p[1] == '(')
119 *(r++) = *(p++);
120 else
121 state = subst_var;
122 break;
124 case subst_var:
126 char *s;
127 char c;
128 const char *t;
130 /* skip dollar */
131 p++;
133 if (p[0] != '{')
134 s = NULL;
135 else
137 p++;
138 s = strchr (p, '}');
140 if (s == NULL)
141 s = strchr (p, PATH_SEP);
142 if (s == NULL)
143 s = strchr (p, '\0');
144 c = *s;
145 *s = '\0';
146 t = getenv (p);
147 *s = c;
148 if (t == NULL)
150 *(r++) = '$';
151 if (p[-1] != '$')
152 *(r++) = '{';
154 else
156 size_t tlen;
158 tlen = strlen (t);
160 if (r + tlen < q + MC_MAXPATHLEN)
162 strncpy (r, t, tlen + 1);
163 r += tlen;
165 p = s;
166 if (*s == '}')
167 p++;
170 state = copy_sym;
171 break;
176 g_free (path_tilde);
178 *r = '\0';
180 return q;
183 /* --------------------------------------------------------------------------------------------- */
185 /* CDPATH handling */
186 static gboolean
187 handle_cdpath (const char *path)
189 gboolean result = FALSE;
191 /* CDPATH handling */
192 if (*path != PATH_SEP)
194 char *cdpath, *p;
195 char c;
197 cdpath = g_strdup (getenv ("CDPATH"));
198 p = cdpath;
199 c = (p == NULL) ? '\0' : ':';
201 while (!result && c == ':')
203 char *s;
205 s = strchr (p, ':');
206 if (s == NULL)
207 s = strchr (p, '\0');
208 c = *s;
209 *s = '\0';
210 if (*p != '\0')
212 vfs_path_t *r_vpath;
214 r_vpath = vfs_path_build_filename (p, path, NULL);
215 result = do_cd (r_vpath, cd_parse_command);
216 vfs_path_free (r_vpath);
218 *s = c;
219 p = s + 1;
221 g_free (cdpath);
224 return result;
227 /* --------------------------------------------------------------------------------------------- */
228 /** Handle Enter on the command line */
230 static cb_ret_t
231 enter (WInput * lc_cmdline)
233 char *cmd = lc_cmdline->buffer;
235 if (!command_prompt)
236 return MSG_HANDLED;
238 /* Any initial whitespace should be removed at this point */
239 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
240 cmd++;
242 if (!*cmd)
243 return MSG_HANDLED;
245 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0)
247 do_cd_command (cmd);
248 input_clean (lc_cmdline);
249 return MSG_HANDLED;
251 else if (strcmp (cmd, "exit") == 0)
253 input_assign_text (lc_cmdline, "");
254 if (!quiet_quit_cmd ())
255 return MSG_NOT_HANDLED;
257 else
259 char *command, *s;
260 size_t i, j, cmd_len;
262 if (!vfs_current_is_local ())
264 message (D_ERROR, MSG_ERROR, _("Cannot execute commands on non-local filesystems"));
265 return MSG_NOT_HANDLED;
267 #ifdef HAVE_SUBSHELL_SUPPORT
268 /* Check this early before we clean command line
269 * (will be checked again by shell_execute) */
270 if (mc_global.tty.use_subshell && subshell_state != INACTIVE)
272 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
273 return MSG_NOT_HANDLED;
275 #endif
276 cmd_len = strlen (cmd);
277 command = g_malloc (cmd_len + 1);
278 command[0] = 0;
279 for (i = j = 0; i < cmd_len; i++)
281 if (cmd[i] == '%')
283 i++;
284 s = expand_format (NULL, cmd[i], TRUE);
285 command = g_realloc (command, j + strlen (s) + cmd_len - i + 1);
286 strcpy (command + j, s);
287 g_free (s);
288 j = strlen (command);
290 else
292 command[j] = cmd[i];
293 j++;
295 command[j] = 0;
297 input_clean (lc_cmdline);
298 shell_execute (command, 0);
299 g_free (command);
301 #ifdef HAVE_SUBSHELL_SUPPORT
302 if ((quit & SUBSHELL_EXIT) != 0)
304 if (quiet_quit_cmd ())
305 return MSG_HANDLED;
307 quit = 0;
308 /* restart subshell */
309 if (mc_global.tty.use_subshell)
310 init_subshell ();
313 if (mc_global.tty.use_subshell)
314 do_load_prompt ();
315 #endif
317 return MSG_HANDLED;
320 /* --------------------------------------------------------------------------------------------- */
322 static cb_ret_t
323 command_callback (Widget * w, widget_msg_t msg, int parm)
325 WInput *cmd = (WInput *) w;
327 switch (msg)
329 case WIDGET_FOCUS:
330 /* Never accept focus, otherwise panels will be unselected */
331 return MSG_NOT_HANDLED;
333 case WIDGET_KEY:
334 /* Special case: we handle the enter key */
335 if (parm == '\n')
337 return enter (cmd);
339 /* fall through */
341 default:
342 return input_callback (w, msg, parm);
346 /* --------------------------------------------------------------------------------------------- */
347 /*** public functions ****************************************************************************/
348 /* --------------------------------------------------------------------------------------------- */
350 /* --------------------------------------------------------------------------------------------- */
351 /** Execute the cd command on the command line */
352 void
353 do_cd_command (char *orig_cmd)
355 int len;
356 int operand_pos = CD_OPERAND_OFFSET;
357 const char *cmd;
359 /* Any final whitespace should be removed here
360 (to see why, try "cd fred "). */
361 /* NOTE: I think we should not remove the extra space,
362 that way, we can cd into hidden directories */
363 /* FIXME: what about interpreting quoted strings like the shell.
364 so one could type "cd <tab> M-a <enter>" and it would work. */
365 len = strlen (orig_cmd) - 1;
366 while (len >= 0 && (orig_cmd[len] == ' ' || orig_cmd[len] == '\t' || orig_cmd[len] == '\n'))
368 orig_cmd[len] = 0;
369 len--;
372 cmd = orig_cmd;
373 if (cmd[CD_OPERAND_OFFSET - 1] == 0)
374 cmd = "cd "; /* 0..2 => given text, 3 => \0 */
376 /* allow any amount of white space in front of the path operand */
377 while (cmd[operand_pos] == ' ' || cmd[operand_pos] == '\t')
378 operand_pos++;
380 if (get_current_type () == view_tree)
382 if (cmd[0] == 0)
384 sync_tree (mc_config_get_home_dir ());
386 else if (strcmp (cmd + operand_pos, "..") == 0)
388 char *str_path;
390 if (vfs_path_elements_count (current_panel->cwd_vpath) != 1 ||
391 strlen (vfs_path_get_by_index (current_panel->cwd_vpath, 0)->path) > 1)
393 vfs_path_t *tmp_vpath = current_panel->cwd_vpath;
395 current_panel->cwd_vpath =
396 vfs_path_vtokens_get (tmp_vpath, 0, vfs_path_tokens_count (tmp_vpath) - 1);
397 vfs_path_free (tmp_vpath);
399 str_path = vfs_path_to_str (current_panel->cwd_vpath);
400 sync_tree (str_path);
401 g_free (str_path);
403 else if (cmd[operand_pos] == PATH_SEP)
405 sync_tree (cmd + operand_pos);
407 else
409 char *str_path;
410 vfs_path_t *new_vpath;
412 new_vpath = vfs_path_append_new (current_panel->cwd_vpath, cmd + operand_pos, NULL);
413 str_path = vfs_path_to_str (new_vpath);
414 vfs_path_free (new_vpath);
415 sync_tree (str_path);
416 g_free (str_path);
419 else
421 char *path;
422 vfs_path_t *q_vpath;
423 gboolean ok;
425 path = examine_cd (&cmd[operand_pos]);
427 if (*path == '\0')
428 q_vpath = vfs_path_from_str (mc_config_get_home_dir ());
429 else
430 q_vpath = vfs_path_from_str_flags (path, VPF_NO_CANON);
432 ok = do_cd (q_vpath, cd_parse_command);
433 if (!ok)
434 ok = handle_cdpath (path);
436 if (!ok)
438 char *d;
440 d = vfs_path_to_str_flags (q_vpath, 0, VPF_STRIP_PASSWORD);
441 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"), d,
442 unix_error_string (errno));
443 g_free (d);
446 vfs_path_free (q_vpath);
447 g_free (path);
451 /* --------------------------------------------------------------------------------------------- */
453 WInput *
454 command_new (int y, int x, int cols)
456 WInput *cmd;
457 const input_colors_t command_colors = {
458 DEFAULT_COLOR,
459 COMMAND_MARK_COLOR,
460 DEFAULT_COLOR,
461 COMMAND_HISTORY_COLOR
464 cmd = input_new (y, x, (int *) command_colors, cols, "", "cmdline",
465 INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS |
466 INPUT_COMPLETE_SHELL_ESC);
468 /* Add our hooks */
469 cmd->widget.callback = command_callback;
471 return cmd;
474 /* --------------------------------------------------------------------------------------------- */
476 * Insert quoted text in input line. The function is meant for the
477 * command line, so the percent sign is quoted as well.
480 void
481 command_insert (WInput * in, const char *text, gboolean insert_extra_space)
483 char *quoted_text;
485 quoted_text = name_quote (text, 1);
486 input_insert (in, quoted_text, insert_extra_space);
487 g_free (quoted_text);
490 /* --------------------------------------------------------------------------------------------- */