Ticket #2170: Color collisions
[midnight-commander.git] / src / command.c
blobef2272389b4b48834785c51c86f505bb15273daf
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"
40 #include "widget.h" /* WInput */
41 #include "command.h"
42 #include "wtools.h" /* message () */
43 #include "panel.h" /* view_tree enum. Also, needed by main.h */
44 #include "main.h" /* do_cd */
45 #include "layout.h" /* for command_prompt variable */
46 #include "user.h" /* expand_format */
47 #include "subshell.h" /* SUBSHELL_EXIT */
48 #include "tree.h" /* for tree_chdir */
49 #include "lib/skin.h" /* DEFAULT_COLOR */
50 #include "execute.h" /* shell_execute */
52 /* This holds the command line */
53 WInput *cmdline;
56 * Expand the argument to "cd" and change directory. First try tilde
57 * expansion, then variable substitution. If the CDPATH variable is set
58 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
59 * We do not support such rare substitutions as ${var:-value} etc.
60 * No quoting is implemented here, so ${VAR} and $VAR will be always
61 * substituted. Wildcards are not supported either.
62 * Advanced users should be encouraged to use "\cd" instead of "cd" if
63 * they want the behavior they are used to in the shell.
65 static int
66 examine_cd (const char *_path)
68 int result, qlen;
69 char *path_tilde, *path;
70 char *p, *q, *r, *s, c;
71 const char *t;
73 /* Tilde expansion */
74 path = strutils_shell_unescape(_path);
75 path_tilde = tilde_expand (path);
77 /* Leave space for further expansion */
78 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
79 q = g_malloc (qlen);
81 /* Variable expansion */
82 for (p = path_tilde, r = q; *p && r < q + MC_MAXPATHLEN;) {
83 if (*p != '$' || (p[1] == '[' || p[1] == '('))
84 *(r++) = *(p++);
85 else {
86 p++;
87 if (*p == '{') {
88 p++;
89 s = strchr (p, '}');
90 } else
91 s = NULL;
92 if (s == NULL)
93 s = strchr (p, PATH_SEP);
94 if (s == NULL)
95 s = strchr (p, 0);
96 c = *s;
97 *s = 0;
98 t = getenv (p);
99 *s = c;
100 if (t == NULL) {
101 *(r++) = '$';
102 if (*(p - 1) != '$')
103 *(r++) = '{';
104 } else {
105 if (r + strlen (t) < q + MC_MAXPATHLEN) {
106 strcpy (r, t);
107 r = strchr (r, 0);
109 if (*s == '}')
110 p = s + 1;
111 else
112 p = s;
116 *r = 0;
118 result = do_cd (q, cd_parse_command);
120 /* CDPATH handling */
121 if (*q != PATH_SEP && !result) {
122 char * const cdpath = g_strdup (getenv ("CDPATH"));
123 p = cdpath;
124 if (p == NULL)
125 c = 0;
126 else
127 c = ':';
128 while (!result && c == ':') {
129 s = strchr (p, ':');
130 if (s == NULL)
131 s = strchr (p, 0);
132 c = *s;
133 *s = 0;
134 if (*p) {
135 r = concat_dir_and_file (p, q);
136 result = do_cd (r, cd_parse_command);
137 g_free (r);
139 *s = c;
140 p = s + 1;
142 g_free (cdpath);
144 g_free (q);
145 g_free (path_tilde);
146 g_free (path);
147 return result;
150 /* Execute the cd command on the command line */
151 void do_cd_command (char * orig_cmd)
153 int len;
154 const char * cmd;
156 /* Any final whitespace should be removed here
157 (to see why, try "cd fred "). */
158 /* NOTE: I think we should not remove the extra space,
159 that way, we can cd into hidden directories */
160 /* FIXME: what about interpreting quoted strings like the shell.
161 so one could type "cd <tab> M-a <enter>" and it would work. */
162 len = strlen (orig_cmd) - 1;
163 while (len >= 0 &&
164 (orig_cmd [len] == ' ' || orig_cmd [len] == '\t' || orig_cmd [len] == '\n')){
165 orig_cmd [len] = 0;
166 len --;
169 cmd = orig_cmd;
170 if (cmd [2] == 0)
171 cmd = "cd ";
173 if (get_current_type () == view_tree){
174 if (cmd [0] == 0){
175 sync_tree (home_dir);
176 } else if (strcmp (cmd+3, "..") == 0){
177 char *dir = current_panel->cwd;
178 len = strlen (dir);
179 while (len && dir [--len] != PATH_SEP);
180 dir [len] = 0;
181 if (len)
182 sync_tree (dir);
183 else
184 sync_tree (PATH_SEP_STR);
185 } else if (cmd [3] == PATH_SEP){
186 sync_tree (cmd+3);
187 } else {
188 char *old = current_panel->cwd;
189 char *new;
190 new = concat_dir_and_file (old, cmd+3);
191 sync_tree (new);
192 g_free (new);
194 } else
195 if (!examine_cd (&cmd [3])) {
196 char *d = strip_password (g_strdup (&cmd [3]), 1);
197 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
198 d, unix_error_string (errno));
199 g_free (d);
200 return;
204 /* Handle Enter on the command line */
205 static cb_ret_t
206 enter (WInput *lc_cmdline)
208 char *cmd = lc_cmdline->buffer;
210 if (!command_prompt)
211 return MSG_HANDLED;
213 /* Any initial whitespace should be removed at this point */
214 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
215 cmd++;
217 if (!*cmd)
218 return MSG_HANDLED;
220 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0) {
221 do_cd_command (cmd);
222 new_input (lc_cmdline);
223 return MSG_HANDLED;
224 } else if (strcmp (cmd, "exit") == 0) {
225 assign_text (lc_cmdline, "");
226 if (!quiet_quit_cmd ())
227 return MSG_NOT_HANDLED;
228 } else {
229 char *command, *s;
230 size_t i, j, cmd_len;
232 if (!vfs_current_is_local ()) {
233 message (D_ERROR, MSG_ERROR,
234 _("Cannot execute commands on non-local filesystems"));
235 return MSG_NOT_HANDLED;
237 #ifdef HAVE_SUBSHELL_SUPPORT
238 /* Check this early before we clean command line
239 * (will be checked again by shell_execute) */
240 if (use_subshell && subshell_state != INACTIVE) {
241 message (D_ERROR, MSG_ERROR,
242 _("The shell is already running a command"));
243 return MSG_NOT_HANDLED;
245 #endif
246 cmd_len = strlen (cmd);
247 command = g_malloc (cmd_len + 1);
248 command[0] = 0;
249 for (i = j = 0; i < cmd_len; i++) {
250 if (cmd[i] == '%') {
251 i++;
252 s = expand_format (NULL, cmd[i], TRUE);
253 command = g_realloc (command, j + strlen (s) + cmd_len - i + 1);
254 strcpy (command + j, s);
255 g_free (s);
256 j = strlen (command);
257 } else {
258 command[j] = cmd[i];
259 j++;
261 command[j] = 0;
263 new_input (lc_cmdline);
264 shell_execute (command, 0);
265 g_free (command);
267 #ifdef HAVE_SUBSHELL_SUPPORT
268 if ((quit & SUBSHELL_EXIT) != 0) {
269 if (quiet_quit_cmd ())
270 return MSG_HANDLED;
272 quit = 0;
273 /* restart subshell */
274 if (use_subshell)
275 init_subshell ();
278 if (use_subshell)
279 load_prompt (0, 0);
280 #endif
282 return MSG_HANDLED;
285 static cb_ret_t
286 command_callback (Widget *w, widget_msg_t msg, int parm)
288 WInput *cmd = (WInput *) w;
290 switch (msg) {
291 case WIDGET_FOCUS:
292 /* Never accept focus, otherwise panels will be unselected */
293 return MSG_NOT_HANDLED;
295 case WIDGET_KEY:
296 /* Special case: we handle the enter key */
297 if (parm == '\n') {
298 return enter (cmd);
300 /* fall through */
302 default:
303 return input_callback (w, msg, parm);
307 WInput *
308 command_new (int y, int x, int cols)
310 WInput *cmd;
311 const input_colors_t command_colors =
313 DEFAULT_COLOR,
314 DEFAULT_COLOR,
315 DEFAULT_COLOR,
316 COMMAND_HISTORY_COLOR
319 cmd = input_new (y, x, (int *) command_colors, cols, "", "cmdline",
320 INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC);
322 /* Add our hooks */
323 cmd->widget.callback = command_callback;
325 return cmd;
329 * Insert quoted text in input line. The function is meant for the
330 * command line, so the percent sign is quoted as well.
332 void
333 command_insert (WInput * in, const char *text, int insert_extra_space)
335 char *quoted_text;
337 quoted_text = name_quote (text, 1);
338 stuff (in, quoted_text, insert_extra_space);
339 g_free (quoted_text);