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,
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/>.
28 * \brief Source: command line widget
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 */
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 */
56 /*** global variables ****************************************************************************/
58 /* This holds the command line */
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 /* --------------------------------------------------------------------------------------------- */
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.
84 examine_cd (const char *_path
)
87 { copy_sym
, subst_var
} state_t
;
89 state_t state
= copy_sym
;
92 char *path_tilde
, *path
;
96 path
= strutils_shell_unescape (_path
);
97 path_tilde
= tilde_expand (path
);
100 /* Leave space for further expansion */
101 qlen
= strlen (path_tilde
) + MC_MAXPATHLEN
;
104 /* Variable expansion */
105 for (p
= path_tilde
, r
= q
; *p
!= '\0' && r
< q
+ MC_MAXPATHLEN
;)
111 if (p
[0] == '\\' && p
[1] == '$')
118 else if (p
[0] != '$' || p
[1] == '[' || p
[1] == '(')
141 s
= strchr (p
, PATH_SEP
);
143 s
= strchr (p
, '\0');
160 if (r
+ tlen
< q
+ MC_MAXPATHLEN
)
162 strncpy (r
, t
, tlen
+ 1);
183 /* --------------------------------------------------------------------------------------------- */
185 /* CDPATH handling */
187 handle_cdpath (const char *path
)
189 gboolean result
= FALSE
;
191 /* CDPATH handling */
192 if (*path
!= PATH_SEP
)
197 cdpath
= g_strdup (getenv ("CDPATH"));
199 c
= (p
== NULL
) ? '\0' : ':';
201 while (!result
&& c
== ':')
207 s
= strchr (p
, '\0');
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
);
227 /* --------------------------------------------------------------------------------------------- */
228 /** Handle Enter on the command line */
231 enter (WInput
* lc_cmdline
)
233 char *cmd
= lc_cmdline
->buffer
;
238 /* Any initial whitespace should be removed at this point */
239 while (*cmd
== ' ' || *cmd
== '\t' || *cmd
== '\n')
245 if (strncmp (cmd
, "cd ", 3) == 0 || strcmp (cmd
, "cd") == 0)
248 input_clean (lc_cmdline
);
251 else if (strcmp (cmd
, "exit") == 0)
253 input_assign_text (lc_cmdline
, "");
254 if (!quiet_quit_cmd ())
255 return MSG_NOT_HANDLED
;
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
;
276 cmd_len
= strlen (cmd
);
277 command
= g_malloc (cmd_len
+ 1);
279 for (i
= j
= 0; i
< cmd_len
; 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
);
288 j
= strlen (command
);
297 input_clean (lc_cmdline
);
298 shell_execute (command
, 0);
301 #ifdef HAVE_SUBSHELL_SUPPORT
302 if ((quit
& SUBSHELL_EXIT
) != 0)
304 if (quiet_quit_cmd ())
308 /* restart subshell */
309 if (mc_global
.tty
.use_subshell
)
313 if (mc_global
.tty
.use_subshell
)
320 /* --------------------------------------------------------------------------------------------- */
323 command_callback (Widget
* w
, widget_msg_t msg
, int parm
)
325 WInput
*cmd
= (WInput
*) w
;
330 /* Never accept focus, otherwise panels will be unselected */
331 return MSG_NOT_HANDLED
;
334 /* Special case: we handle the enter key */
342 return input_callback (w
, msg
, parm
);
346 /* --------------------------------------------------------------------------------------------- */
347 /*** public functions ****************************************************************************/
348 /* --------------------------------------------------------------------------------------------- */
350 /* --------------------------------------------------------------------------------------------- */
351 /** Execute the cd command on the command line */
353 do_cd_command (char *orig_cmd
)
356 int operand_pos
= CD_OPERAND_OFFSET
;
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'))
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')
380 if (get_current_type () == view_tree
)
384 sync_tree (mc_config_get_home_dir ());
386 else if (strcmp (cmd
+ operand_pos
, "..") == 0)
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
);
403 else if (cmd
[operand_pos
] == PATH_SEP
)
405 sync_tree (cmd
+ operand_pos
);
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
);
425 path
= examine_cd (&cmd
[operand_pos
]);
428 q_vpath
= vfs_path_from_str (mc_config_get_home_dir ());
430 q_vpath
= vfs_path_from_str_flags (path
, VPF_NO_CANON
);
432 ok
= do_cd (q_vpath
, cd_parse_command
);
434 ok
= handle_cdpath (path
);
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
));
446 vfs_path_free (q_vpath
);
451 /* --------------------------------------------------------------------------------------------- */
454 command_new (int y
, int x
, int cols
)
457 const input_colors_t command_colors
= {
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
);
469 cmd
->widget
.callback
= command_callback
;
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.
481 command_insert (WInput
* in
, const char *text
, gboolean insert_extra_space
)
485 quoted_text
= name_quote (text
, 1);
486 input_insert (in
, quoted_text
, insert_extra_space
);
487 g_free (quoted_text
);
490 /* --------------------------------------------------------------------------------------------- */