Init/deinit clean up
[midnight-commander/osp/fridrivaxxx.git] / src / command.c
blob0e6273818e429f6e11ee7402007ec3dbe6fcc6f8
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 "midnight.h" /* current_panel */
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 "execute.h" /* shell_execute */
51 #include "command.h"
53 /*** global variables ****************************************************************************/
55 /* This holds the command line */
56 WInput *cmdline;
58 /*** file scope macro definitions ****************************************************************/
60 /*** file scope type declarations ****************************************************************/
62 /*** file scope variables ************************************************************************/
64 /*** file scope functions ************************************************************************/
65 /* --------------------------------------------------------------------------------------------- */
67 /**
68 * Expand the argument to "cd" and change directory. First try tilde
69 * expansion, then variable substitution. If the CDPATH variable is set
70 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
71 * We do not support such rare substitutions as ${var:-value} etc.
72 * No quoting is implemented here, so ${VAR} and $VAR will be always
73 * substituted. Wildcards are not supported either.
74 * Advanced users should be encouraged to use "\cd" instead of "cd" if
75 * they want the behavior they are used to in the shell.
78 static int
79 examine_cd (const char *_path)
81 int result, qlen;
82 char *path_tilde, *path;
83 char *p, *q, *r, *s, c;
84 const char *t;
86 /* Tilde expansion */
87 path = strutils_shell_unescape (_path);
88 path_tilde = tilde_expand (path);
90 /* Leave space for further expansion */
91 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
92 q = g_malloc (qlen);
94 /* Variable expansion */
95 for (p = path_tilde, r = q; *p && r < q + MC_MAXPATHLEN;)
97 if (*p != '$' || (p[1] == '[' || p[1] == '('))
98 *(r++) = *(p++);
99 else
101 p++;
102 if (*p == '{')
104 p++;
105 s = strchr (p, '}');
107 else
108 s = NULL;
109 if (s == NULL)
110 s = strchr (p, PATH_SEP);
111 if (s == NULL)
112 s = strchr (p, 0);
113 c = *s;
114 *s = 0;
115 t = getenv (p);
116 *s = c;
117 if (t == NULL)
119 *(r++) = '$';
120 if (*(p - 1) != '$')
121 *(r++) = '{';
123 else
125 if (r + strlen (t) < q + MC_MAXPATHLEN)
127 strcpy (r, t);
128 r = strchr (r, 0);
130 if (*s == '}')
131 p = s + 1;
132 else
133 p = s;
137 *r = 0;
139 result = do_cd (q, cd_parse_command);
141 /* CDPATH handling */
142 if (*q != PATH_SEP && !result)
144 char *const cdpath = g_strdup (getenv ("CDPATH"));
145 p = cdpath;
146 if (p == NULL)
147 c = 0;
148 else
149 c = ':';
150 while (!result && c == ':')
152 s = strchr (p, ':');
153 if (s == NULL)
154 s = strchr (p, 0);
155 c = *s;
156 *s = 0;
157 if (*p)
159 r = concat_dir_and_file (p, q);
160 result = do_cd (r, cd_parse_command);
161 g_free (r);
163 *s = c;
164 p = s + 1;
166 g_free (cdpath);
168 g_free (q);
169 g_free (path_tilde);
170 g_free (path);
171 return result;
174 /* --------------------------------------------------------------------------------------------- */
175 /** Handle Enter on the command line */
177 static cb_ret_t
178 enter (WInput * lc_cmdline)
180 char *cmd = lc_cmdline->buffer;
182 if (!command_prompt)
183 return MSG_HANDLED;
185 /* Any initial whitespace should be removed at this point */
186 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
187 cmd++;
189 if (!*cmd)
190 return MSG_HANDLED;
192 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0)
194 do_cd_command (cmd);
195 input_clean (lc_cmdline);
196 return MSG_HANDLED;
198 else if (strcmp (cmd, "exit") == 0)
200 input_assign_text (lc_cmdline, "");
201 if (!quiet_quit_cmd ())
202 return MSG_NOT_HANDLED;
204 else
206 char *command, *s;
207 size_t i, j, cmd_len;
209 if (!vfs_current_is_local ())
211 message (D_ERROR, MSG_ERROR, _("Cannot execute commands on non-local filesystems"));
212 return MSG_NOT_HANDLED;
214 #ifdef HAVE_SUBSHELL_SUPPORT
215 /* Check this early before we clean command line
216 * (will be checked again by shell_execute) */
217 if (use_subshell && subshell_state != INACTIVE)
219 message (D_ERROR, MSG_ERROR, _("The shell is already running a command"));
220 return MSG_NOT_HANDLED;
222 #endif
223 cmd_len = strlen (cmd);
224 command = g_malloc (cmd_len + 1);
225 command[0] = 0;
226 for (i = j = 0; i < cmd_len; i++)
228 if (cmd[i] == '%')
230 i++;
231 s = expand_format (NULL, cmd[i], TRUE);
232 command = g_realloc (command, j + strlen (s) + cmd_len - i + 1);
233 strcpy (command + j, s);
234 g_free (s);
235 j = strlen (command);
237 else
239 command[j] = cmd[i];
240 j++;
242 command[j] = 0;
244 input_clean (lc_cmdline);
245 shell_execute (command, 0);
246 g_free (command);
248 #ifdef HAVE_SUBSHELL_SUPPORT
249 if ((quit & SUBSHELL_EXIT) != 0)
251 if (quiet_quit_cmd ())
252 return MSG_HANDLED;
254 quit = 0;
255 /* restart subshell */
256 if (use_subshell)
257 init_subshell ();
260 if (use_subshell)
261 load_prompt (0, NULL);
262 #endif
264 return MSG_HANDLED;
267 /* --------------------------------------------------------------------------------------------- */
269 static cb_ret_t
270 command_callback (Widget * w, widget_msg_t msg, int parm)
272 WInput *cmd = (WInput *) w;
274 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')
284 return enter (cmd);
286 /* fall through */
288 default:
289 return input_callback (w, msg, parm);
293 /* --------------------------------------------------------------------------------------------- */
294 /*** public functions ****************************************************************************/
295 /* --------------------------------------------------------------------------------------------- */
297 /* --------------------------------------------------------------------------------------------- */
298 /** Execute the cd command on the command line */
300 void
301 do_cd_command (char *orig_cmd)
303 int len;
304 const char *cmd;
306 /* Any final whitespace should be removed here
307 (to see why, try "cd fred "). */
308 /* NOTE: I think we should not remove the extra space,
309 that way, we can cd into hidden directories */
310 /* FIXME: what about interpreting quoted strings like the shell.
311 so one could type "cd <tab> M-a <enter>" and it would work. */
312 len = strlen (orig_cmd) - 1;
313 while (len >= 0 && (orig_cmd[len] == ' ' || orig_cmd[len] == '\t' || orig_cmd[len] == '\n'))
315 orig_cmd[len] = 0;
316 len--;
319 cmd = orig_cmd;
320 if (cmd[2] == 0)
321 cmd = "cd ";
323 if (get_current_type () == view_tree)
325 if (cmd[0] == 0)
327 sync_tree (home_dir);
329 else if (strcmp (cmd + 3, "..") == 0)
331 char *dir = current_panel->cwd;
332 len = strlen (dir);
333 while (len && dir[--len] != PATH_SEP);
334 dir[len] = 0;
335 if (len)
336 sync_tree (dir);
337 else
338 sync_tree (PATH_SEP_STR);
340 else if (cmd[3] == PATH_SEP)
342 sync_tree (cmd + 3);
344 else
346 char *old = current_panel->cwd;
347 char *new;
348 new = concat_dir_and_file (old, cmd + 3);
349 sync_tree (new);
350 g_free (new);
353 else if (!examine_cd (&cmd[3]))
355 char *d = strip_password (g_strdup (&cmd[3]), 1);
356 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"), d, unix_error_string (errno));
357 g_free (d);
358 return;
362 /* --------------------------------------------------------------------------------------------- */
364 WInput *
365 command_new (int y, int x, int cols)
367 WInput *cmd;
368 const input_colors_t command_colors = {
369 DEFAULT_COLOR,
370 COMMAND_MARK_COLOR,
371 DEFAULT_COLOR,
372 COMMAND_HISTORY_COLOR
375 cmd = input_new (y, x, (int *) command_colors, cols, "", "cmdline",
376 INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS |
377 INPUT_COMPLETE_SHELL_ESC);
379 /* Add our hooks */
380 cmd->widget.callback = command_callback;
382 return cmd;
385 /* --------------------------------------------------------------------------------------------- */
387 * Insert quoted text in input line. The function is meant for the
388 * command line, so the percent sign is quoted as well.
391 void
392 command_insert (WInput * in, const char *text, gboolean insert_extra_space)
394 char *quoted_text;
396 quoted_text = name_quote (text, 1);
397 input_insert (in, quoted_text, insert_extra_space);
398 g_free (quoted_text);
401 /* --------------------------------------------------------------------------------------------- */