src/filemanager/command.c: add DOXYGEN doc little little code cleanup.
[midnight-commander.git] / src / filemanager / command.c
blob4deefddf0d52236f3a8ab52dfd79986f083ba5d2
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/setup.h" /* quit */
46 #ifdef ENABLE_SUBSHELL
47 #include "src/subshell.h"
48 #endif
49 #include "src/execute.h" /* shell_execute */
51 #include "midnight.h" /* current_panel */
52 #include "layout.h" /* for command_prompt variable */
53 #include "usermenu.h" /* expand_format */
54 #include "tree.h" /* for tree_chdir */
56 #include "command.h"
58 /*** global variables ****************************************************************************/
60 /* This holds the command line */
61 WInput *cmdline;
63 /*** file scope macro definitions ****************************************************************/
65 #define CD_OPERAND_OFFSET 3
67 /*** file scope type declarations ****************************************************************/
69 /*** file scope variables ************************************************************************/
71 /*** file scope functions ************************************************************************/
72 /* --------------------------------------------------------------------------------------------- */
74 /**
75 * Expand the argument to "cd" and change directory. First try tilde
76 * expansion, then variable substitution. If the CDPATH variable is set
77 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
78 * We do not support such rare substitutions as ${var:-value} etc.
79 * No quoting is implemented here, so ${VAR} and $VAR will be always
80 * substituted. Wildcards are not supported either.
81 * Advanced users should be encouraged to use "\cd" instead of "cd" if
82 * they want the behavior they are used to in the shell.
84 * @param _path string to examine
85 * @return newly allocated string
88 static char *
89 examine_cd (const char *_path)
91 typedef enum
92 { copy_sym, subst_var } state_t;
94 state_t state = copy_sym;
95 char *q;
96 size_t qlen;
97 char *path_tilde, *path;
98 char *p, *r;
100 /* Tilde expansion */
101 path = strutils_shell_unescape (_path);
102 path_tilde = tilde_expand (path);
103 g_free (path);
105 /* Leave space for further expansion */
106 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
107 q = g_malloc (qlen);
109 /* Variable expansion */
110 for (p = path_tilde, r = q; *p != '\0' && r < q + MC_MAXPATHLEN;)
113 switch (state)
115 case copy_sym:
116 if (p[0] == '\\' && p[1] == '$')
118 /* skip backslash */
119 p++;
120 /* copy dollar */
121 *(r++) = *(p++);
123 else if (p[0] != '$' || p[1] == '[' || p[1] == '(')
124 *(r++) = *(p++);
125 else
126 state = subst_var;
127 break;
129 case subst_var:
131 char *s;
132 char c;
133 const char *t;
135 /* skip dollar */
136 p++;
138 if (p[0] != '{')
139 s = NULL;
140 else
142 p++;
143 s = strchr (p, '}');
145 if (s == NULL)
146 s = strchr (p, PATH_SEP);
147 if (s == NULL)
148 s = strchr (p, '\0');
149 c = *s;
150 *s = '\0';
151 t = getenv (p);
152 *s = c;
153 if (t == NULL)
155 *(r++) = '$';
156 if (p[-1] != '$')
157 *(r++) = '{';
159 else
161 size_t tlen;
163 tlen = strlen (t);
165 if (r + tlen < q + MC_MAXPATHLEN)
167 strncpy (r, t, tlen + 1);
168 r += tlen;
170 p = s;
171 if (*s == '}')
172 p++;
175 state = copy_sym;
176 break;
181 g_free (path_tilde);
183 *r = '\0';
185 return q;
188 /* --------------------------------------------------------------------------------------------- */
190 /* CDPATH handling */
191 static gboolean
192 handle_cdpath (const char *path)
194 gboolean result = FALSE;
196 /* CDPATH handling */
197 if (*path != PATH_SEP)
199 char *cdpath, *p;
200 char c;
202 cdpath = g_strdup (getenv ("CDPATH"));
203 p = cdpath;
204 c = (p == NULL) ? '\0' : ':';
206 while (!result && c == ':')
208 char *s;
210 s = strchr (p, ':');
211 if (s == NULL)
212 s = strchr (p, '\0');
213 c = *s;
214 *s = '\0';
215 if (*p != '\0')
217 vfs_path_t *r_vpath;
219 r_vpath = vfs_path_build_filename (p, path, NULL);
220 result = do_cd (r_vpath, cd_parse_command);
221 vfs_path_free (r_vpath);
223 *s = c;
224 p = s + 1;
226 g_free (cdpath);
229 return result;
232 /* --------------------------------------------------------------------------------------------- */
234 /** Handle Enter on the command line
236 * @param lc_cmdline string for handling
237 * @return MSG_HANDLED on sucsess else MSG_NOT_HANDLED
240 static cb_ret_t
241 enter (WInput * lc_cmdline)
243 char *cmd = lc_cmdline->buffer;
245 if (!command_prompt)
246 return MSG_HANDLED;
248 /* Any initial whitespace should be removed at this point */
249 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
250 cmd++;
252 if (!*cmd)
253 return MSG_HANDLED;
255 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0)
257 do_cd_command (cmd);
258 input_clean (lc_cmdline);
259 return MSG_HANDLED;
261 else if (strcmp (cmd, "exit") == 0)
263 input_assign_text (lc_cmdline, "");
264 if (!quiet_quit_cmd ())
265 return MSG_NOT_HANDLED;
267 else
269 GString *command;
270 size_t i;
272 if (!vfs_current_is_local ())
274 message (D_ERROR, MSG_ERROR, _("Cannot execute commands on non-local filesystems"));
275 return MSG_NOT_HANDLED;
277 #ifdef ENABLE_SUBSHELL
278 /* Check this early before we clean command line
279 * (will be checked again by shell_execute) */
280 if (mc_global.tty.use_subshell && subshell_state != INACTIVE)
282 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
283 return MSG_NOT_HANDLED;
285 #endif
286 command = g_string_sized_new (32);
288 for (i = 0; cmd[i] != '\0'; i++)
290 if (cmd[i] != '%')
291 g_string_append_c (command, cmd[i]);
292 else
294 char *s;
296 s = expand_format (NULL, cmd[++i], TRUE);
297 g_string_append (command, s);
298 g_free (s);
302 input_clean (lc_cmdline);
303 shell_execute (command->str, 0);
304 g_string_free (command, TRUE);
306 #ifdef ENABLE_SUBSHELL
307 if ((quit & SUBSHELL_EXIT) != 0)
309 if (quiet_quit_cmd ())
310 return MSG_HANDLED;
312 quit = 0;
313 /* restart subshell */
314 if (mc_global.tty.use_subshell)
315 init_subshell ();
318 if (mc_global.tty.use_subshell)
319 do_load_prompt ();
320 #endif
322 return MSG_HANDLED;
325 /* --------------------------------------------------------------------------------------------- */
328 * Default command line callback
330 * @param w Widget object
331 * @param msg message for handling
332 * @param parm extra parameter such as key code
334 * @return MSG_NOT_HANDLED on fail else MSG_HANDLED
337 static cb_ret_t
338 command_callback (Widget * w, widget_msg_t msg, int parm)
340 WInput *cmd = (WInput *) w;
342 switch (msg)
344 case WIDGET_FOCUS:
345 /* Never accept focus, otherwise panels will be unselected */
346 return MSG_NOT_HANDLED;
348 case WIDGET_KEY:
349 /* Special case: we handle the enter key */
350 if (parm == '\n')
351 return enter (cmd);
352 /* fall through */
354 default:
355 return input_callback (w, msg, parm);
359 /* --------------------------------------------------------------------------------------------- */
360 /*** public functions ****************************************************************************/
361 /* --------------------------------------------------------------------------------------------- */
363 /** Execute the cd command on the command line
365 * @param orig_cmd command for execution
368 void
369 do_cd_command (char *orig_cmd)
371 int len;
372 int operand_pos = CD_OPERAND_OFFSET;
373 const char *cmd;
375 /* Any final whitespace should be removed here
376 (to see why, try "cd fred "). */
377 /* NOTE: I think we should not remove the extra space,
378 that way, we can cd into hidden directories */
379 /* FIXME: what about interpreting quoted strings like the shell.
380 so one could type "cd <tab> M-a <enter>" and it would work. */
381 len = strlen (orig_cmd) - 1;
382 while (len >= 0 && (orig_cmd[len] == ' ' || orig_cmd[len] == '\t' || orig_cmd[len] == '\n'))
384 orig_cmd[len] = 0;
385 len--;
388 cmd = orig_cmd;
389 if (cmd[CD_OPERAND_OFFSET - 1] == 0)
390 cmd = "cd "; /* 0..2 => given text, 3 => \0 */
392 /* allow any amount of white space in front of the path operand */
393 while (cmd[operand_pos] == ' ' || cmd[operand_pos] == '\t')
394 operand_pos++;
396 if (get_current_type () == view_tree)
398 if (cmd[0] == 0)
400 sync_tree (mc_config_get_home_dir ());
402 else if (strcmp (cmd + operand_pos, "..") == 0)
404 char *str_path;
406 if (vfs_path_elements_count (current_panel->cwd_vpath) != 1 ||
407 strlen (vfs_path_get_by_index (current_panel->cwd_vpath, 0)->path) > 1)
409 vfs_path_t *tmp_vpath = current_panel->cwd_vpath;
411 current_panel->cwd_vpath =
412 vfs_path_vtokens_get (tmp_vpath, 0, vfs_path_tokens_count (tmp_vpath) - 1);
413 vfs_path_free (tmp_vpath);
415 str_path = vfs_path_to_str (current_panel->cwd_vpath);
416 sync_tree (str_path);
417 g_free (str_path);
419 else if (cmd[operand_pos] == PATH_SEP)
421 sync_tree (cmd + operand_pos);
423 else
425 char *str_path;
426 vfs_path_t *new_vpath;
428 new_vpath = vfs_path_append_new (current_panel->cwd_vpath, cmd + operand_pos, NULL);
429 str_path = vfs_path_to_str (new_vpath);
430 vfs_path_free (new_vpath);
431 sync_tree (str_path);
432 g_free (str_path);
435 else
437 char *path;
438 vfs_path_t *q_vpath;
439 gboolean ok;
441 path = examine_cd (&cmd[operand_pos]);
443 if (*path == '\0')
444 q_vpath = vfs_path_from_str (mc_config_get_home_dir ());
445 else
446 q_vpath = vfs_path_from_str_flags (path, VPF_NO_CANON);
448 ok = do_cd (q_vpath, cd_parse_command);
449 if (!ok)
450 ok = handle_cdpath (path);
452 if (!ok)
454 char *d;
456 d = vfs_path_to_str_flags (q_vpath, 0, VPF_STRIP_PASSWORD);
457 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"), d,
458 unix_error_string (errno));
459 g_free (d);
462 vfs_path_free (q_vpath);
463 g_free (path);
467 /* --------------------------------------------------------------------------------------------- */
469 WInput *
470 command_new (int y, int x, int cols)
472 WInput *cmd;
473 const input_colors_t command_colors = {
474 DEFAULT_COLOR,
475 COMMAND_MARK_COLOR,
476 DEFAULT_COLOR,
477 COMMAND_HISTORY_COLOR
480 cmd = input_new (y, x, (int *) command_colors, cols, "", "cmdline",
481 INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS |
482 INPUT_COMPLETE_SHELL_ESC);
484 /* Add our hooks */
485 cmd->widget.callback = command_callback;
487 return cmd;
490 /* --------------------------------------------------------------------------------------------- */
492 * Insert quoted text in input line. The function is meant for the
493 * command line, so the percent sign is quoted as well.
495 * @param in WInput object
496 * @param text string for insertion
497 * @param insert_extra_space add extra space
500 void
501 command_insert (WInput * in, const char *text, gboolean insert_extra_space)
503 char *quoted_text;
505 quoted_text = name_quote (text, 1);
506 input_insert (in, quoted_text, insert_extra_space);
507 g_free (quoted_text);
510 /* --------------------------------------------------------------------------------------------- */