Original patch as attached on the bugreport
[midnight-commander.git] / src / command.c
blob7764e4530aa9ad5e2fc7acf29804d18c6213b422
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 #include <config.h>
27 #include <errno.h>
28 #include <string.h>
30 #include "global.h" /* home_dir */
31 #include "tty.h"
32 #include "widget.h" /* WInput */
33 #include "command.h"
34 #include "complete.h" /* completion constants */
35 #include "wtools.h" /* message () */
36 #include "panel.h" /* view_tree enum. Also, needed by main.h */
37 #include "main.h" /* do_cd */
38 #include "layout.h" /* for command_prompt variable */
39 #include "user.h" /* expand_format */
40 #include "subshell.h" /* SUBSHELL_EXIT */
41 #include "tree.h" /* for tree_chdir */
42 #include "color.h" /* DEFAULT_COLOR */
43 #include "execute.h" /* shell_execute */
45 /* This holds the command line */
46 WInput *cmdline;
49 * Expand the argument to "cd" and change directory. First try tilde
50 * expansion, then variable substitution. If the CDPATH variable is set
51 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
52 * We do not support such rare substitutions as ${var:-value} etc.
53 * No quoting is implemented here, so ${VAR} and $VAR will be always
54 * substituted. Wildcards are not supported either.
55 * Advanced users should be encouraged to use "\cd" instead of "cd" if
56 * they want the behavior they are used to in the shell.
58 static int
59 examine_cd (char *path)
61 int result, qlen;
62 char *path_tilde;
63 char *p, *q, *r, *s, c;
64 const char *t;
66 /* Tilde expansion */
67 path = unescape_string(path);
68 path_tilde = tilde_expand (path);
70 /* Leave space for further expansion */
71 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
72 q = g_malloc (qlen);
74 /* Variable expansion */
75 for (p = path_tilde, r = q; *p && r < q + MC_MAXPATHLEN;) {
76 if (*p != '$' || (p[1] == '[' || p[1] == '('))
77 *(r++) = *(p++);
78 else {
79 p++;
80 if (*p == '{') {
81 p++;
82 s = strchr (p, '}');
83 } else
84 s = NULL;
85 if (s == NULL)
86 s = strchr (p, PATH_SEP);
87 if (s == NULL)
88 s = strchr (p, 0);
89 c = *s;
90 *s = 0;
91 t = getenv (p);
92 *s = c;
93 if (t == NULL) {
94 *(r++) = '$';
95 if (*(p - 1) != '$')
96 *(r++) = '{';
97 } else {
98 if (r + strlen (t) < q + MC_MAXPATHLEN) {
99 strcpy (r, t);
100 r = strchr (r, 0);
102 if (*s == '}')
103 p = s + 1;
104 else
105 p = s;
109 *r = 0;
111 result = do_cd (q, cd_parse_command);
113 /* CDPATH handling */
114 if (*q != PATH_SEP && !result) {
115 char * const cdpath = g_strdup (getenv ("CDPATH"));
116 char *p = cdpath;
117 if (p == NULL)
118 c = 0;
119 else
120 c = ':';
121 while (!result && c == ':') {
122 s = strchr (p, ':');
123 if (s == NULL)
124 s = strchr (p, 0);
125 c = *s;
126 *s = 0;
127 if (*p) {
128 r = concat_dir_and_file (p, q);
129 result = do_cd (r, cd_parse_command);
130 g_free (r);
132 *s = c;
133 p = s + 1;
135 g_free (cdpath);
137 g_free (q);
138 g_free (path_tilde);
139 return result;
142 /* Execute the cd command on the command line */
143 void do_cd_command (char *cmd)
145 int len;
147 /* Any final whitespace should be removed here
148 (to see why, try "cd fred "). */
149 /* NOTE: I think we should not remove the extra space,
150 that way, we can cd into hidden directories */
151 /* FIXME: what about interpreting quoted strings like the shell.
152 so one could type "cd <tab> M-a <enter>" and it would work. */
153 len = strlen (cmd) - 1;
154 while (len >= 0 &&
155 (cmd [len] == ' ' || cmd [len] == '\t' || cmd [len] == '\n')){
156 cmd [len] = 0;
157 len --;
160 if (cmd [2] == 0)
161 cmd = "cd ";
163 if (get_current_type () == view_tree){
164 if (cmd [0] == 0){
165 sync_tree (home_dir);
166 } else if (strcmp (cmd+3, "..") == 0){
167 char *dir = current_panel->cwd;
168 int len = strlen (dir);
169 while (len && dir [--len] != PATH_SEP);
170 dir [len] = 0;
171 if (len)
172 sync_tree (dir);
173 else
174 sync_tree (PATH_SEP_STR);
175 } else if (cmd [3] == PATH_SEP){
176 sync_tree (cmd+3);
177 } else {
178 char *old = current_panel->cwd;
179 char *new;
180 new = concat_dir_and_file (old, cmd+3);
181 sync_tree (new);
182 g_free (new);
184 } else
185 if (!examine_cd (&cmd [3])) {
186 char *d = strip_password (g_strdup (&cmd [3]), 1);
187 message (1, MSG_ERROR, _(" Cannot chdir to \"%s\" \n %s "),
188 d, unix_error_string (errno));
189 g_free (d);
190 return;
194 /* Handle Enter on the command line */
195 static cb_ret_t
196 enter (WInput *cmdline)
198 char *cmd = cmdline->buffer;
200 if (!command_prompt)
201 return MSG_HANDLED;
203 /* Any initial whitespace should be removed at this point */
204 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
205 cmd++;
207 if (!*cmd)
208 return MSG_HANDLED;
210 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0) {
211 do_cd_command (cmd);
212 new_input (cmdline);
213 return MSG_HANDLED;
214 } else {
215 char *command, *s;
216 size_t i, j, cmd_len;
218 if (!vfs_current_is_local ()) {
219 message (1, MSG_ERROR,
221 (" Cannot execute commands on non-local filesystems"));
223 return MSG_NOT_HANDLED;
225 #ifdef HAVE_SUBSHELL_SUPPORT
226 /* Check this early before we clean command line
227 * (will be checked again by shell_execute) */
228 if (use_subshell && subshell_state != INACTIVE) {
229 message (1, MSG_ERROR,
230 _(" The shell is already running a command "));
231 return MSG_NOT_HANDLED;
233 #endif
234 cmd_len = strlen (cmd);
235 command = g_malloc (cmd_len + 1);
236 command[0] = 0;
237 for (i = j = 0; i < cmd_len; i++) {
238 if (cmd[i] == '%') {
239 i++;
240 s = expand_format (NULL, cmd[i], 1);
241 command = g_realloc (command, j + strlen (s) + cmd_len - i + 1);
242 strcpy (command + j, s);
243 g_free (s);
244 j = strlen (command);
245 } else {
246 command[j] = cmd[i];
247 j++;
249 command[j] = 0;
251 new_input (cmdline);
252 shell_execute (command, 0);
253 g_free (command);
255 #ifdef HAVE_SUBSHELL_SUPPORT
256 if (quit & SUBSHELL_EXIT) {
257 quiet_quit_cmd ();
258 return MSG_HANDLED;
260 if (use_subshell)
261 load_prompt (0, 0);
262 #endif
264 return MSG_HANDLED;
267 static cb_ret_t
268 command_callback (Widget *w, widget_msg_t msg, int parm)
270 WInput *cmd = (WInput *) w;
272 switch (msg) {
273 case WIDGET_FOCUS:
274 /* Never accept focus, otherwise panels will be unselected */
275 return MSG_NOT_HANDLED;
277 case WIDGET_KEY:
278 /* Special case: we handle the enter key */
279 if (parm == '\n') {
280 return enter (cmd);
282 /* fall through */
284 default:
285 return input_callback (w, msg, parm);
289 WInput *
290 command_new (int y, int x, int cols)
292 WInput *cmd;
294 cmd = input_new (y, x, DEFAULT_COLOR, cols, "", "cmdline");
296 /* Add our hooks */
297 cmd->widget.callback = command_callback;
298 cmd->completion_flags |= INPUT_COMPLETE_COMMANDS;
300 return cmd;
304 * Insert quoted text in input line. The function is meant for the
305 * command line, so the percent sign is quoted as well.
307 void
308 command_insert (WInput * in, const char *text, int insert_extra_space)
310 char *quoted_text;
312 quoted_text = name_quote (text, 1);
313 stuff (in, quoted_text, insert_extra_space);
314 g_free (quoted_text);