Merge commit '315633ecce31cd5af92112ab9903f6e7e8ae8df7'
[free-mc.git] / src / command.c
blobb204b8d693cfd64ef6b6116df2bde6548624cb16
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_tilde = tilde_expand (path);
69 /* Leave space for further expansion */
70 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
71 q = g_malloc (qlen);
73 /* Variable expansion */
74 for (p = path_tilde, r = q; *p && r < q + MC_MAXPATHLEN;) {
75 if (*p != '$' || (p[1] == '[' || p[1] == '('))
76 *(r++) = *(p++);
77 else {
78 p++;
79 if (*p == '{') {
80 p++;
81 s = strchr (p, '}');
82 } else
83 s = NULL;
84 if (s == NULL)
85 s = strchr (p, PATH_SEP);
86 if (s == NULL)
87 s = strchr (p, 0);
88 c = *s;
89 *s = 0;
90 t = getenv (p);
91 *s = c;
92 if (t == NULL) {
93 *(r++) = '$';
94 if (*(p - 1) != '$')
95 *(r++) = '{';
96 } else {
97 if (r + strlen (t) < q + MC_MAXPATHLEN) {
98 strcpy (r, t);
99 r = strchr (r, 0);
101 if (*s == '}')
102 p = s + 1;
103 else
104 p = s;
108 *r = 0;
110 result = do_cd (q, cd_parse_command);
112 /* CDPATH handling */
113 if (*q != PATH_SEP && !result) {
114 char * const cdpath = g_strdup (getenv ("CDPATH"));
115 char *p = cdpath;
116 if (p == NULL)
117 c = 0;
118 else
119 c = ':';
120 while (!result && c == ':') {
121 s = strchr (p, ':');
122 if (s == NULL)
123 s = strchr (p, 0);
124 c = *s;
125 *s = 0;
126 if (*p) {
127 r = concat_dir_and_file (p, q);
128 result = do_cd (r, cd_parse_command);
129 g_free (r);
131 *s = c;
132 p = s + 1;
134 g_free (cdpath);
136 g_free (q);
137 g_free (path_tilde);
138 return result;
141 /* Execute the cd command on the command line */
142 void do_cd_command (char *cmd)
144 int len;
146 /* Any final whitespace should be removed here
147 (to see why, try "cd fred "). */
148 /* NOTE: I think we should not remove the extra space,
149 that way, we can cd into hidden directories */
150 /* FIXME: what about interpreting quoted strings like the shell.
151 so one could type "cd <tab> M-a <enter>" and it would work. */
152 len = strlen (cmd) - 1;
153 while (len >= 0 &&
154 (cmd [len] == ' ' || cmd [len] == '\t' || cmd [len] == '\n')){
155 cmd [len] = 0;
156 len --;
159 if (cmd [2] == 0)
160 cmd = "cd ";
162 if (get_current_type () == view_tree){
163 if (cmd [0] == 0){
164 sync_tree (home_dir);
165 } else if (strcmp (cmd+3, "..") == 0){
166 char *dir = current_panel->cwd;
167 int len = strlen (dir);
168 while (len && dir [--len] != PATH_SEP);
169 dir [len] = 0;
170 if (len)
171 sync_tree (dir);
172 else
173 sync_tree (PATH_SEP_STR);
174 } else if (cmd [3] == PATH_SEP){
175 sync_tree (cmd+3);
176 } else {
177 char *old = current_panel->cwd;
178 char *new;
179 new = concat_dir_and_file (old, cmd+3);
180 sync_tree (new);
181 g_free (new);
183 } else
184 if (!examine_cd (&cmd [3])) {
185 char *d = strip_password (g_strdup (&cmd [3]), 1);
186 message (1, MSG_ERROR, _(" Cannot chdir to \"%s\" \n %s "),
187 d, unix_error_string (errno));
188 g_free (d);
189 return;
193 /* Handle Enter on the command line */
194 static cb_ret_t
195 enter (WInput *cmdline)
197 char *cmd = cmdline->buffer;
199 if (!command_prompt)
200 return MSG_HANDLED;
202 /* Any initial whitespace should be removed at this point */
203 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
204 cmd++;
206 if (!*cmd)
207 return MSG_HANDLED;
209 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0) {
210 do_cd_command (cmd);
211 new_input (cmdline);
212 return MSG_HANDLED;
213 } else {
214 char *command, *s;
215 size_t i, j, cmd_len;
217 if (!vfs_current_is_local ()) {
218 message (1, MSG_ERROR,
220 (" Cannot execute commands on non-local filesystems"));
222 return MSG_NOT_HANDLED;
224 #ifdef HAVE_SUBSHELL_SUPPORT
225 /* Check this early before we clean command line
226 * (will be checked again by shell_execute) */
227 if (use_subshell && subshell_state != INACTIVE) {
228 message (1, MSG_ERROR,
229 _(" The shell is already running a command "));
230 return MSG_NOT_HANDLED;
232 #endif
233 cmd_len = strlen (cmd);
234 command = g_malloc (cmd_len + 1);
235 command[0] = 0;
236 for (i = j = 0; i < cmd_len; i++) {
237 if (cmd[i] == '%') {
238 i++;
239 s = expand_format (NULL, cmd[i], 1);
240 command = g_realloc (command, j + strlen (s) + cmd_len - i + 1);
241 strcpy (command + j, s);
242 g_free (s);
243 j = strlen (command);
244 } else {
245 command[j] = cmd[i];
246 j++;
248 command[j] = 0;
250 new_input (cmdline);
251 shell_execute (command, 0);
252 g_free (command);
254 #ifdef HAVE_SUBSHELL_SUPPORT
255 if (quit & SUBSHELL_EXIT) {
256 quiet_quit_cmd ();
257 return MSG_HANDLED;
259 if (use_subshell)
260 load_prompt (0, 0);
261 #endif
263 return MSG_HANDLED;
266 static cb_ret_t
267 command_callback (Widget *w, widget_msg_t msg, int parm)
269 WInput *cmd = (WInput *) w;
271 switch (msg) {
272 case WIDGET_FOCUS:
273 /* Never accept focus, otherwise panels will be unselected */
274 return MSG_NOT_HANDLED;
276 case WIDGET_KEY:
277 /* Special case: we handle the enter key */
278 if (parm == '\n') {
279 return enter (cmd);
281 /* fall through */
283 default:
284 return input_callback (w, msg, parm);
288 WInput *
289 command_new (int y, int x, int cols)
291 WInput *cmd;
293 cmd = input_new (y, x, DEFAULT_COLOR, cols, "", "cmdline");
295 /* Add our hooks */
296 cmd->widget.callback = command_callback;
297 cmd->completion_flags |= INPUT_COMPLETE_COMMANDS;
299 return cmd;
303 * Insert quoted text in input line. The function is meant for the
304 * command line, so the percent sign is quoted as well.
306 void
307 command_insert (WInput * in, const char *text, int insert_extra_space)
309 char *quoted_text;
311 quoted_text = name_quote (text, 1);
312 stuff (in, quoted_text, insert_extra_space);
313 g_free (quoted_text);