Removed type SHELL_ESCAPED_STR in favour of plain char*
[midnight-commander.git] / src / command.c
blob07f66dcf64df3bfae2b17e03d170b7a4ef22363e
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 <mhl/memory.h>
31 #include <mhl/string.h>
32 #include <mhl/escape.h>
34 #include "global.h" /* home_dir */
35 #include "tty.h"
36 #include "widget.h" /* WInput */
37 #include "command.h"
38 #include "wtools.h" /* message () */
39 #include "panel.h" /* view_tree enum. Also, needed by main.h */
40 #include "main.h" /* do_cd */
41 #include "layout.h" /* for command_prompt variable */
42 #include "user.h" /* expand_format */
43 #include "subshell.h" /* SUBSHELL_EXIT */
44 #include "tree.h" /* for tree_chdir */
45 #include "color.h" /* DEFAULT_COLOR */
46 #include "execute.h" /* shell_execute */
48 /* This holds the command line */
49 WInput *cmdline;
52 * Expand the argument to "cd" and change directory. First try tilde
53 * expansion, then variable substitution. If the CDPATH variable is set
54 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
55 * We do not support such rare substitutions as ${var:-value} etc.
56 * No quoting is implemented here, so ${VAR} and $VAR will be always
57 * substituted. Wildcards are not supported either.
58 * Advanced users should be encouraged to use "\cd" instead of "cd" if
59 * they want the behavior they are used to in the shell.
61 static int
62 examine_cd (char *path)
64 int result, qlen;
65 char *path_tilde;
66 char *p, *q, *r, *s, c;
67 const char *t;
69 /* Tilde expansion */
70 path = mhl_shell_unescape_buf(path);
71 path_tilde = tilde_expand (path);
73 /* Leave space for further expansion */
74 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
75 q = g_malloc (qlen);
77 /* Variable expansion */
78 for (p = path_tilde, r = q; *p && r < q + MC_MAXPATHLEN;) {
79 if (*p != '$' || (p[1] == '[' || p[1] == '('))
80 *(r++) = *(p++);
81 else {
82 p++;
83 if (*p == '{') {
84 p++;
85 s = strchr (p, '}');
86 } else
87 s = NULL;
88 if (s == NULL)
89 s = strchr (p, PATH_SEP);
90 if (s == NULL)
91 s = strchr (p, 0);
92 c = *s;
93 *s = 0;
94 t = getenv (p);
95 *s = c;
96 if (t == NULL) {
97 *(r++) = '$';
98 if (*(p - 1) != '$')
99 *(r++) = '{';
100 } else {
101 if (r + strlen (t) < q + MC_MAXPATHLEN) {
102 strcpy (r, t);
103 r = strchr (r, 0);
105 if (*s == '}')
106 p = s + 1;
107 else
108 p = s;
112 *r = 0;
114 result = do_cd (q, cd_parse_command);
116 /* CDPATH handling */
117 if (*q != PATH_SEP && !result) {
118 char * const cdpath = g_strdup (getenv ("CDPATH"));
119 char *p = cdpath;
120 if (p == NULL)
121 c = 0;
122 else
123 c = ':';
124 while (!result && c == ':') {
125 s = strchr (p, ':');
126 if (s == NULL)
127 s = strchr (p, 0);
128 c = *s;
129 *s = 0;
130 if (*p) {
131 r = mhl_str_dir_plus_file (p, q);
132 result = do_cd (r, cd_parse_command);
133 g_free (r);
135 *s = c;
136 p = s + 1;
138 g_free (cdpath);
140 g_free (q);
141 g_free (path_tilde);
142 return result;
145 /* Execute the cd command on the command line */
146 void do_cd_command (char *cmd)
148 int len;
150 /* Any final whitespace should be removed here
151 (to see why, try "cd fred "). */
152 /* NOTE: I think we should not remove the extra space,
153 that way, we can cd into hidden directories */
154 /* FIXME: what about interpreting quoted strings like the shell.
155 so one could type "cd <tab> M-a <enter>" and it would work. */
156 len = strlen (cmd) - 1;
157 while (len >= 0 &&
158 (cmd [len] == ' ' || cmd [len] == '\t' || cmd [len] == '\n')){
159 cmd [len] = 0;
160 len --;
163 if (cmd [2] == 0)
164 cmd = "cd ";
166 if (get_current_type () == view_tree){
167 if (cmd [0] == 0){
168 sync_tree (home_dir);
169 } else if (strcmp (cmd+3, "..") == 0){
170 char *dir = current_panel->cwd;
171 int len = strlen (dir);
172 while (len && dir [--len] != PATH_SEP);
173 dir [len] = 0;
174 if (len)
175 sync_tree (dir);
176 else
177 sync_tree (PATH_SEP_STR);
178 } else if (cmd [3] == PATH_SEP){
179 sync_tree (cmd+3);
180 } else {
181 char *old = current_panel->cwd;
182 char *new;
183 new = mhl_str_dir_plus_file (old, cmd+3);
184 sync_tree (new);
185 g_free (new);
187 } else
188 if (!examine_cd (&cmd [3])) {
189 char *d = strip_password (g_strdup (&cmd [3]), 1);
190 message (D_ERROR, MSG_ERROR, _(" Cannot chdir to \"%s\" \n %s "),
191 d, unix_error_string (errno));
192 g_free (d);
193 return;
197 /* Handle Enter on the command line */
198 static cb_ret_t
199 enter (WInput *cmdline)
201 char *cmd = cmdline->buffer;
203 if (!command_prompt)
204 return MSG_HANDLED;
206 /* Any initial whitespace should be removed at this point */
207 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
208 cmd++;
210 if (!*cmd)
211 return MSG_HANDLED;
213 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0) {
214 do_cd_command (cmd);
215 new_input (cmdline);
216 return MSG_HANDLED;
217 } else {
218 char *command, *s;
219 size_t i, j, cmd_len;
221 if (!vfs_current_is_local ()) {
222 message (D_ERROR, MSG_ERROR,
224 (" Cannot execute commands on non-local filesystems"));
226 return MSG_NOT_HANDLED;
228 #ifdef HAVE_SUBSHELL_SUPPORT
229 /* Check this early before we clean command line
230 * (will be checked again by shell_execute) */
231 if (use_subshell && subshell_state != INACTIVE) {
232 message (D_ERROR, MSG_ERROR,
233 _(" The shell is already running a command "));
234 return MSG_NOT_HANDLED;
236 #endif
237 cmd_len = strlen (cmd);
238 command = g_malloc (cmd_len + 1);
239 command[0] = 0;
240 for (i = j = 0; i < cmd_len; i++) {
241 if (cmd[i] == '%') {
242 i++;
243 s = expand_format (NULL, cmd[i], 1);
244 command = g_realloc (command, j + strlen (s) + cmd_len - i + 1);
245 strcpy (command + j, s);
246 g_free (s);
247 j = strlen (command);
248 } else {
249 command[j] = cmd[i];
250 j++;
252 command[j] = 0;
254 new_input (cmdline);
255 shell_execute (command, 0);
256 g_free (command);
258 #ifdef HAVE_SUBSHELL_SUPPORT
259 if (quit & SUBSHELL_EXIT) {
260 quiet_quit_cmd ();
261 return MSG_HANDLED;
263 if (use_subshell)
264 load_prompt (0, 0);
265 #endif
267 return MSG_HANDLED;
270 static cb_ret_t
271 command_callback (Widget *w, widget_msg_t msg, int parm)
273 WInput *cmd = (WInput *) w;
275 switch (msg) {
276 case WIDGET_FOCUS:
277 /* Never accept focus, otherwise panels will be unselected */
278 return MSG_NOT_HANDLED;
280 case WIDGET_KEY:
281 /* Special case: we handle the enter key */
282 if (parm == '\n') {
283 return enter (cmd);
285 /* fall through */
287 default:
288 return input_callback (w, msg, parm);
292 WInput *
293 command_new (int y, int x, int cols)
295 WInput *cmd;
297 cmd = input_new (y, x, DEFAULT_COLOR, cols, "", "cmdline",
298 INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC);
300 /* Add our hooks */
301 cmd->widget.callback = command_callback;
302 cmd->completion_flags |= INPUT_COMPLETE_COMMANDS;
304 return cmd;
308 * Insert quoted text in input line. The function is meant for the
309 * command line, so the percent sign is quoted as well.
311 void
312 command_insert (WInput * in, const char *text, int insert_extra_space)
314 char *quoted_text;
316 quoted_text = name_quote (text, 1);
317 stuff (in, quoted_text, insert_extra_space);
318 g_free (quoted_text);