Split file src/keybind.[ch] to lib/keybind.[ch] and src/keybind-defaults.[ch].
[midnight-commander.git] / src / command.c
blobdaf63293a2befcfa86c475b37edbcec6628ee425
1 /* Command line widget.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2007 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 This widget is derived from the WInput widget, it's used to cope
20 with all the magic of the command input line, we depend on some
21 help from the program's callback.
25 /** \file command.c
26 * \brief Source: command line widget
29 #include <config.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
35 #include "lib/global.h" /* home_dir */
36 #include "lib/tty/tty.h"
37 #include "lib/vfs/mc-vfs/vfs.h"
38 #include "lib/strescape.h"
39 #include "lib/skin.h" /* DEFAULT_COLOR */
40 #include "lib/util.h"
41 #include "lib/widget.h"
43 #include "command.h"
44 #include "panel.h" /* view_tree enum. Also, needed by main.h */
45 #include "main.h" /* do_cd */
46 #include "layout.h" /* for command_prompt variable */
47 #include "user.h" /* expand_format */
48 #include "subshell.h" /* SUBSHELL_EXIT */
49 #include "tree.h" /* for tree_chdir */
50 #include "execute.h" /* shell_execute */
52 /*** global variables ****************************************************************************/
54 /* This holds the command line */
55 WInput *cmdline;
57 /*** file scope macro definitions ****************************************************************/
59 /*** file scope type declarations ****************************************************************/
61 /*** file scope variables ************************************************************************/
63 /*** file scope functions ************************************************************************/
64 /* --------------------------------------------------------------------------------------------- */
66 /**
67 * Expand the argument to "cd" and change directory. First try tilde
68 * expansion, then variable substitution. If the CDPATH variable is set
69 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
70 * We do not support such rare substitutions as ${var:-value} etc.
71 * No quoting is implemented here, so ${VAR} and $VAR will be always
72 * substituted. Wildcards are not supported either.
73 * Advanced users should be encouraged to use "\cd" instead of "cd" if
74 * they want the behavior they are used to in the shell.
77 static int
78 examine_cd (const char *_path)
80 int result, qlen;
81 char *path_tilde, *path;
82 char *p, *q, *r, *s, c;
83 const char *t;
85 /* Tilde expansion */
86 path = strutils_shell_unescape (_path);
87 path_tilde = tilde_expand (path);
89 /* Leave space for further expansion */
90 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
91 q = g_malloc (qlen);
93 /* Variable expansion */
94 for (p = path_tilde, r = q; *p && r < q + MC_MAXPATHLEN;)
96 if (*p != '$' || (p[1] == '[' || p[1] == '('))
97 *(r++) = *(p++);
98 else
100 p++;
101 if (*p == '{')
103 p++;
104 s = strchr (p, '}');
106 else
107 s = NULL;
108 if (s == NULL)
109 s = strchr (p, PATH_SEP);
110 if (s == NULL)
111 s = strchr (p, 0);
112 c = *s;
113 *s = 0;
114 t = getenv (p);
115 *s = c;
116 if (t == NULL)
118 *(r++) = '$';
119 if (*(p - 1) != '$')
120 *(r++) = '{';
122 else
124 if (r + strlen (t) < q + MC_MAXPATHLEN)
126 strcpy (r, t);
127 r = strchr (r, 0);
129 if (*s == '}')
130 p = s + 1;
131 else
132 p = s;
136 *r = 0;
138 result = do_cd (q, cd_parse_command);
140 /* CDPATH handling */
141 if (*q != PATH_SEP && !result)
143 char *const cdpath = g_strdup (getenv ("CDPATH"));
144 p = cdpath;
145 if (p == NULL)
146 c = 0;
147 else
148 c = ':';
149 while (!result && c == ':')
151 s = strchr (p, ':');
152 if (s == NULL)
153 s = strchr (p, 0);
154 c = *s;
155 *s = 0;
156 if (*p)
158 r = concat_dir_and_file (p, q);
159 result = do_cd (r, cd_parse_command);
160 g_free (r);
162 *s = c;
163 p = s + 1;
165 g_free (cdpath);
167 g_free (q);
168 g_free (path_tilde);
169 g_free (path);
170 return result;
173 /* --------------------------------------------------------------------------------------------- */
174 /** Handle Enter on the command line */
176 static cb_ret_t
177 enter (WInput * lc_cmdline)
179 char *cmd = lc_cmdline->buffer;
181 if (!command_prompt)
182 return MSG_HANDLED;
184 /* Any initial whitespace should be removed at this point */
185 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
186 cmd++;
188 if (!*cmd)
189 return MSG_HANDLED;
191 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0)
193 do_cd_command (cmd);
194 input_clean (lc_cmdline);
195 return MSG_HANDLED;
197 else if (strcmp (cmd, "exit") == 0)
199 input_assign_text (lc_cmdline, "");
200 if (!quiet_quit_cmd ())
201 return MSG_NOT_HANDLED;
203 else
205 char *command, *s;
206 size_t i, j, cmd_len;
208 if (!vfs_current_is_local ())
210 message (D_ERROR, MSG_ERROR, _("Cannot execute commands on non-local filesystems"));
211 return MSG_NOT_HANDLED;
213 #ifdef HAVE_SUBSHELL_SUPPORT
214 /* Check this early before we clean command line
215 * (will be checked again by shell_execute) */
216 if (use_subshell && subshell_state != INACTIVE)
218 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
219 return MSG_NOT_HANDLED;
221 #endif
222 cmd_len = strlen (cmd);
223 command = g_malloc (cmd_len + 1);
224 command[0] = 0;
225 for (i = j = 0; i < cmd_len; i++)
227 if (cmd[i] == '%')
229 i++;
230 s = expand_format (NULL, cmd[i], TRUE);
231 command = g_realloc (command, j + strlen (s) + cmd_len - i + 1);
232 strcpy (command + j, s);
233 g_free (s);
234 j = strlen (command);
236 else
238 command[j] = cmd[i];
239 j++;
241 command[j] = 0;
243 input_clean (lc_cmdline);
244 shell_execute (command, 0);
245 g_free (command);
247 #ifdef HAVE_SUBSHELL_SUPPORT
248 if ((quit & SUBSHELL_EXIT) != 0)
250 if (quiet_quit_cmd ())
251 return MSG_HANDLED;
253 quit = 0;
254 /* restart subshell */
255 if (use_subshell)
256 init_subshell ();
259 if (use_subshell)
260 load_prompt (0, 0);
261 #endif
263 return MSG_HANDLED;
266 /* --------------------------------------------------------------------------------------------- */
268 static cb_ret_t
269 command_callback (Widget * w, widget_msg_t msg, int parm)
271 WInput *cmd = (WInput *) w;
273 switch (msg)
275 case WIDGET_FOCUS:
276 /* Never accept focus, otherwise panels will be unselected */
277 return MSG_NOT_HANDLED;
279 case WIDGET_KEY:
280 /* Special case: we handle the enter key */
281 if (parm == '\n')
283 return enter (cmd);
285 /* fall through */
287 default:
288 return input_callback (w, msg, parm);
292 /* --------------------------------------------------------------------------------------------- */
293 /*** public functions ****************************************************************************/
294 /* --------------------------------------------------------------------------------------------- */
296 /* --------------------------------------------------------------------------------------------- */
297 /** Execute the cd command on the command line */
299 void
300 do_cd_command (char *orig_cmd)
302 int len;
303 const char *cmd;
305 /* Any final whitespace should be removed here
306 (to see why, try "cd fred "). */
307 /* NOTE: I think we should not remove the extra space,
308 that way, we can cd into hidden directories */
309 /* FIXME: what about interpreting quoted strings like the shell.
310 so one could type "cd <tab> M-a <enter>" and it would work. */
311 len = strlen (orig_cmd) - 1;
312 while (len >= 0 && (orig_cmd[len] == ' ' || orig_cmd[len] == '\t' || orig_cmd[len] == '\n'))
314 orig_cmd[len] = 0;
315 len--;
318 cmd = orig_cmd;
319 if (cmd[2] == 0)
320 cmd = "cd ";
322 if (get_current_type () == view_tree)
324 if (cmd[0] == 0)
326 sync_tree (home_dir);
328 else if (strcmp (cmd + 3, "..") == 0)
330 char *dir = current_panel->cwd;
331 len = strlen (dir);
332 while (len && dir[--len] != PATH_SEP);
333 dir[len] = 0;
334 if (len)
335 sync_tree (dir);
336 else
337 sync_tree (PATH_SEP_STR);
339 else if (cmd[3] == PATH_SEP)
341 sync_tree (cmd + 3);
343 else
345 char *old = current_panel->cwd;
346 char *new;
347 new = concat_dir_and_file (old, cmd + 3);
348 sync_tree (new);
349 g_free (new);
352 else if (!examine_cd (&cmd[3]))
354 char *d = strip_password (g_strdup (&cmd[3]), 1);
355 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"), d, unix_error_string (errno));
356 g_free (d);
357 return;
361 /* --------------------------------------------------------------------------------------------- */
363 WInput *
364 command_new (int y, int x, int cols)
366 WInput *cmd;
367 const input_colors_t command_colors = {
368 DEFAULT_COLOR,
369 COMMAND_MARK_COLOR,
370 DEFAULT_COLOR,
371 COMMAND_HISTORY_COLOR
374 cmd = input_new (y, x, (int *) command_colors, cols, "", "cmdline",
375 INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS |
376 INPUT_COMPLETE_SHELL_ESC);
378 /* Add our hooks */
379 cmd->widget.callback = command_callback;
381 return cmd;
384 /* --------------------------------------------------------------------------------------------- */
386 * Insert quoted text in input line. The function is meant for the
387 * command line, so the percent sign is quoted as well.
390 void
391 command_insert (WInput * in, const char *text, gboolean insert_extra_space)
393 char *quoted_text;
395 quoted_text = name_quote (text, 1);
396 input_insert (in, quoted_text, insert_extra_space);
397 g_free (quoted_text);
400 /* --------------------------------------------------------------------------------------------- */