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-2016
8 Free Software Foundation, Inc.
11 Slava Zanko <slavazanko@gmail.com>, 2013
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 * \brief Source: command line widget
39 #include "lib/global.h" /* home_dir */
40 #include "lib/tty/tty.h"
41 #include "lib/vfs/vfs.h"
42 #include "lib/strescape.h"
43 #include "lib/skin.h" /* DEFAULT_COLOR */
45 #include "lib/widget.h"
47 #include "src/setup.h" /* quit */
48 #ifdef ENABLE_SUBSHELL
49 #include "src/subshell/subshell.h"
51 #include "src/execute.h" /* shell_execute */
53 #include "midnight.h" /* current_panel */
54 #include "layout.h" /* for command_prompt variable */
55 #include "usermenu.h" /* expand_format */
56 #include "tree.h" /* sync_tree() */
60 /*** global variables ****************************************************************************/
62 /* This holds the command line */
65 /*** file scope macro definitions ****************************************************************/
67 #define CD_OPERAND_OFFSET 3
69 /*** file scope type declarations ****************************************************************/
71 /*** file scope variables ************************************************************************/
73 /* Color styles command line */
74 static input_colors_t command_colors
;
76 /* --------------------------------------------------------------------------------------------- */
77 /*** file scope functions ************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
81 * Expand the argument to "cd" and change directory. First try tilde
82 * expansion, then variable substitution. If the CDPATH variable is set
83 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
84 * We do not support such rare substitutions as ${var:-value} etc.
85 * No quoting is implemented here, so ${VAR} and $VAR will be always
86 * substituted. Wildcards are not supported either.
87 * Advanced users should be encouraged to use "\cd" instead of "cd" if
88 * they want the behavior they are used to in the shell.
90 * @param _path string to examine
91 * @return newly allocated string
95 examine_cd (const char *_path
)
98 { copy_sym
, subst_var
} state_t
;
100 state_t state
= copy_sym
;
103 char *path_tilde
, *path
;
106 /* Tilde expansion */
107 path
= strutils_shell_unescape (_path
);
108 path_tilde
= tilde_expand (path
);
111 /* Leave space for further expansion */
112 qlen
= strlen (path_tilde
) + MC_MAXPATHLEN
;
115 /* Variable expansion */
116 for (p
= path_tilde
, r
= q
; *p
!= '\0' && r
< q
+ MC_MAXPATHLEN
;)
121 if (p
[0] == '\\' && p
[1] == '$')
128 else if (p
[0] != '$' || p
[1] == '[' || p
[1] == '(')
151 s
= strchr (p
, PATH_SEP
);
153 s
= strchr (p
, '\0');
170 if (r
+ tlen
< q
+ MC_MAXPATHLEN
)
172 strncpy (r
, t
, tlen
+ 1);
196 /* --------------------------------------------------------------------------------------------- */
198 /* CDPATH handling */
200 handle_cdpath (const char *path
)
202 gboolean result
= FALSE
;
204 /* CDPATH handling */
205 if (!IS_PATH_SEP (*path
))
210 cdpath
= g_strdup (getenv ("CDPATH"));
212 c
= (p
== NULL
) ? '\0' : ':';
214 while (!result
&& c
== ':')
220 s
= strchr (p
, '\0');
227 r_vpath
= vfs_path_build_filename (p
, path
, (char *) NULL
);
228 result
= do_cd (r_vpath
, cd_parse_command
);
229 vfs_path_free (r_vpath
);
240 /* --------------------------------------------------------------------------------------------- */
242 /** Handle Enter on the command line
244 * @param lc_cmdline string for handling
245 * @return MSG_HANDLED on sucsess else MSG_NOT_HANDLED
249 enter (WInput
* lc_cmdline
)
251 char *cmd
= lc_cmdline
->buffer
;
256 /* Any initial whitespace should be removed at this point */
257 while (*cmd
== ' ' || *cmd
== '\t' || *cmd
== '\n')
263 if (strncmp (cmd
, "cd ", 3) == 0 || strcmp (cmd
, "cd") == 0)
266 input_clean (lc_cmdline
);
269 else if (strcmp (cmd
, "exit") == 0)
271 input_assign_text (lc_cmdline
, "");
272 if (!quiet_quit_cmd ())
273 return MSG_NOT_HANDLED
;
280 if (!vfs_current_is_local ())
282 message (D_ERROR
, MSG_ERROR
, _("Cannot execute commands on non-local filesystems"));
283 return MSG_NOT_HANDLED
;
285 #ifdef ENABLE_SUBSHELL
286 /* Check this early before we clean command line
287 * (will be checked again by shell_execute) */
288 if (mc_global
.tty
.use_subshell
&& subshell_state
!= INACTIVE
)
290 message (D_ERROR
, MSG_ERROR
, _("The shell is already running a command"));
291 return MSG_NOT_HANDLED
;
294 command
= g_string_sized_new (32);
296 for (i
= 0; cmd
[i
] != '\0'; i
++)
299 g_string_append_c (command
, cmd
[i
]);
304 s
= expand_format (NULL
, cmd
[++i
], TRUE
);
305 g_string_append (command
, s
);
310 input_clean (lc_cmdline
);
311 shell_execute (command
->str
, 0);
312 g_string_free (command
, TRUE
);
314 #ifdef ENABLE_SUBSHELL
315 if ((quit
& SUBSHELL_EXIT
) != 0)
317 if (quiet_quit_cmd ())
321 /* restart subshell */
322 if (mc_global
.tty
.use_subshell
)
326 if (mc_global
.tty
.use_subshell
)
333 /* --------------------------------------------------------------------------------------------- */
336 * Default command line callback
338 * @param w Widget object
339 * @param msg message for handling
340 * @param parm extra parameter such as key code
342 * @return MSG_NOT_HANDLED on fail else MSG_HANDLED
346 command_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
351 /* Never accept focus, otherwise panels will be unselected */
352 return MSG_NOT_HANDLED
;
355 /* Special case: we handle the enter key */
357 return enter (INPUT (w
));
361 return input_callback (w
, sender
, msg
, parm
, data
);
365 /* --------------------------------------------------------------------------------------------- */
366 /*** public functions ****************************************************************************/
367 /* --------------------------------------------------------------------------------------------- */
369 /** Execute the cd command on the command line
371 * @param orig_cmd command for execution
375 do_cd_command (char *orig_cmd
)
378 int operand_pos
= CD_OPERAND_OFFSET
;
381 /* Any final whitespace should be removed here
382 (to see why, try "cd fred "). */
383 /* NOTE: I think we should not remove the extra space,
384 that way, we can cd into hidden directories */
385 /* FIXME: what about interpreting quoted strings like the shell.
386 so one could type "cd <tab> M-a <enter>" and it would work. */
387 len
= strlen (orig_cmd
) - 1;
388 while (len
>= 0 && (orig_cmd
[len
] == ' ' || orig_cmd
[len
] == '\t' || orig_cmd
[len
] == '\n'))
395 if (cmd
[CD_OPERAND_OFFSET
- 1] == 0)
396 cmd
= "cd "; /* 0..2 => given text, 3 => \0 */
398 /* allow any amount of white space in front of the path operand */
399 while (cmd
[operand_pos
] == ' ' || cmd
[operand_pos
] == '\t')
402 if (get_current_type () == view_tree
)
404 vfs_path_t
*new_vpath
= NULL
;
408 new_vpath
= vfs_path_from_str (mc_config_get_home_dir ());
409 sync_tree (new_vpath
);
411 else if (DIR_IS_DOTDOT (cmd
+ operand_pos
))
413 if (vfs_path_elements_count (current_panel
->cwd_vpath
) != 1 ||
414 strlen (vfs_path_get_by_index (current_panel
->cwd_vpath
, 0)->path
) > 1)
416 vfs_path_t
*tmp_vpath
= current_panel
->cwd_vpath
;
418 current_panel
->cwd_vpath
=
419 vfs_path_vtokens_get (tmp_vpath
, 0, vfs_path_tokens_count (tmp_vpath
) - 1);
420 vfs_path_free (tmp_vpath
);
422 sync_tree (current_panel
->cwd_vpath
);
426 if (IS_PATH_SEP (cmd
[operand_pos
]))
427 new_vpath
= vfs_path_from_str (cmd
+ operand_pos
);
430 vfs_path_append_new (current_panel
->cwd_vpath
, cmd
+ operand_pos
,
433 sync_tree (new_vpath
);
436 vfs_path_free (new_vpath
);
444 path
= examine_cd (&cmd
[operand_pos
]);
447 q_vpath
= vfs_path_from_str (mc_config_get_home_dir ());
449 q_vpath
= vfs_path_from_str_flags (path
, VPF_NO_CANON
);
451 ok
= do_cd (q_vpath
, cd_parse_command
);
453 ok
= handle_cdpath (path
);
459 d
= vfs_path_to_str_flags (q_vpath
, 0, VPF_STRIP_PASSWORD
);
460 message (D_ERROR
, MSG_ERROR
, _("Cannot chdir to \"%s\"\n%s"), d
,
461 unix_error_string (errno
));
465 vfs_path_free (q_vpath
);
470 /* --------------------------------------------------------------------------------------------- */
473 command_new (int y
, int x
, int cols
)
477 cmd
= input_new (y
, x
, command_colors
, cols
, "", "cmdline",
478 INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_VARIABLES
| INPUT_COMPLETE_USERNAMES
479 | INPUT_COMPLETE_HOSTNAMES
| INPUT_COMPLETE_CD
| INPUT_COMPLETE_COMMANDS
|
480 INPUT_COMPLETE_SHELL_ESC
);
483 WIDGET (cmd
)->callback
= command_callback
;
488 /* --------------------------------------------------------------------------------------------- */
490 * Set colors for the command line.
494 command_set_default_colors (void)
496 command_colors
[WINPUTC_MAIN
] = DEFAULT_COLOR
;
497 command_colors
[WINPUTC_MARK
] = COMMAND_MARK_COLOR
;
498 command_colors
[WINPUTC_UNCHANGED
] = DEFAULT_COLOR
;
499 command_colors
[WINPUTC_HISTORY
] = COMMAND_HISTORY_COLOR
;
502 /* --------------------------------------------------------------------------------------------- */
504 * Insert quoted text in input line. The function is meant for the
505 * command line, so the percent sign is quoted as well.
507 * @param in WInput object
508 * @param text string for insertion
509 * @param insert_extra_space add extra space
513 command_insert (WInput
* in
, const char *text
, gboolean insert_extra_space
)
517 quoted_text
= name_quote (text
, TRUE
);
518 input_insert (in
, quoted_text
, insert_extra_space
);
519 g_free (quoted_text
);
522 /* --------------------------------------------------------------------------------------------- */