Ticket #1787: remove obsolete checks for <stdlib.h>
[free-mc.git] / src / command.c
blobd95f06982e4bf5fe3fe381be975ce4b83a55ef2c
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 "global.h" /* home_dir */
36 #include "../src/tty/tty.h"
37 #include "widget.h" /* WInput */
38 #include "command.h"
39 #include "wtools.h" /* message () */
40 #include "panel.h" /* view_tree enum. Also, needed by main.h */
41 #include "main.h" /* do_cd */
42 #include "layout.h" /* for command_prompt variable */
43 #include "user.h" /* expand_format */
44 #include "subshell.h" /* SUBSHELL_EXIT */
45 #include "tree.h" /* for tree_chdir */
46 #include "../src/skin/skin.h" /* DEFAULT_COLOR */
47 #include "execute.h" /* shell_execute */
48 #include "../src/strescape.h"
50 /* This holds the command line */
51 WInput *cmdline;
54 * Expand the argument to "cd" and change directory. First try tilde
55 * expansion, then variable substitution. If the CDPATH variable is set
56 * (e.g. CDPATH=".:~:/usr"), try all the paths contained there.
57 * We do not support such rare substitutions as ${var:-value} etc.
58 * No quoting is implemented here, so ${VAR} and $VAR will be always
59 * substituted. Wildcards are not supported either.
60 * Advanced users should be encouraged to use "\cd" instead of "cd" if
61 * they want the behavior they are used to in the shell.
63 static int
64 examine_cd (const char *_path)
66 int result, qlen;
67 char *path_tilde, *path;
68 char *p, *q, *r, *s, c;
69 const char *t;
71 /* Tilde expansion */
72 path = strutils_shell_unescape(_path);
73 path_tilde = tilde_expand (path);
75 /* Leave space for further expansion */
76 qlen = strlen (path_tilde) + MC_MAXPATHLEN;
77 q = g_malloc (qlen);
79 /* Variable expansion */
80 for (p = path_tilde, r = q; *p && r < q + MC_MAXPATHLEN;) {
81 if (*p != '$' || (p[1] == '[' || p[1] == '('))
82 *(r++) = *(p++);
83 else {
84 p++;
85 if (*p == '{') {
86 p++;
87 s = strchr (p, '}');
88 } else
89 s = NULL;
90 if (s == NULL)
91 s = strchr (p, PATH_SEP);
92 if (s == NULL)
93 s = strchr (p, 0);
94 c = *s;
95 *s = 0;
96 t = getenv (p);
97 *s = c;
98 if (t == NULL) {
99 *(r++) = '$';
100 if (*(p - 1) != '$')
101 *(r++) = '{';
102 } else {
103 if (r + strlen (t) < q + MC_MAXPATHLEN) {
104 strcpy (r, t);
105 r = strchr (r, 0);
107 if (*s == '}')
108 p = s + 1;
109 else
110 p = s;
114 *r = 0;
116 result = do_cd (q, cd_parse_command);
118 /* CDPATH handling */
119 if (*q != PATH_SEP && !result) {
120 char * const cdpath = g_strdup (getenv ("CDPATH"));
121 p = cdpath;
122 if (p == NULL)
123 c = 0;
124 else
125 c = ':';
126 while (!result && c == ':') {
127 s = strchr (p, ':');
128 if (s == NULL)
129 s = strchr (p, 0);
130 c = *s;
131 *s = 0;
132 if (*p) {
133 r = concat_dir_and_file (p, q);
134 result = do_cd (r, cd_parse_command);
135 g_free (r);
137 *s = c;
138 p = s + 1;
140 g_free (cdpath);
142 g_free (q);
143 g_free (path_tilde);
144 g_free (path);
145 return result;
148 /* Execute the cd command on the command line */
149 void do_cd_command (char * orig_cmd)
151 int len;
152 const char * cmd;
154 /* Any final whitespace should be removed here
155 (to see why, try "cd fred "). */
156 /* NOTE: I think we should not remove the extra space,
157 that way, we can cd into hidden directories */
158 /* FIXME: what about interpreting quoted strings like the shell.
159 so one could type "cd <tab> M-a <enter>" and it would work. */
160 len = strlen (orig_cmd) - 1;
161 while (len >= 0 &&
162 (orig_cmd [len] == ' ' || orig_cmd [len] == '\t' || orig_cmd [len] == '\n')){
163 orig_cmd [len] = 0;
164 len --;
167 cmd = orig_cmd;
168 if (cmd [2] == 0)
169 cmd = "cd ";
171 if (get_current_type () == view_tree){
172 if (cmd [0] == 0){
173 sync_tree (home_dir);
174 } else if (strcmp (cmd+3, "..") == 0){
175 char *dir = current_panel->cwd;
176 len = strlen (dir);
177 while (len && dir [--len] != PATH_SEP);
178 dir [len] = 0;
179 if (len)
180 sync_tree (dir);
181 else
182 sync_tree (PATH_SEP_STR);
183 } else if (cmd [3] == PATH_SEP){
184 sync_tree (cmd+3);
185 } else {
186 char *old = current_panel->cwd;
187 char *new;
188 new = concat_dir_and_file (old, cmd+3);
189 sync_tree (new);
190 g_free (new);
192 } else
193 if (!examine_cd (&cmd [3])) {
194 char *d = strip_password (g_strdup (&cmd [3]), 1);
195 message (D_ERROR, MSG_ERROR, _(" Cannot chdir to \"%s\" \n %s "),
196 d, unix_error_string (errno));
197 g_free (d);
198 return;
202 /* Handle Enter on the command line */
203 static cb_ret_t
204 enter (WInput *lc_cmdline)
206 char *cmd = lc_cmdline->buffer;
208 if (!command_prompt)
209 return MSG_HANDLED;
211 /* Any initial whitespace should be removed at this point */
212 while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n')
213 cmd++;
215 if (!*cmd)
216 return MSG_HANDLED;
218 if (strncmp (cmd, "cd ", 3) == 0 || strcmp (cmd, "cd") == 0) {
219 do_cd_command (cmd);
220 new_input (lc_cmdline);
221 return MSG_HANDLED;
222 } else {
223 char *command, *s;
224 size_t i, j, cmd_len;
226 if (!vfs_current_is_local ()) {
227 if (strcmp (cmd, "exit") == 0) {
228 quiet_quit_cmd ();
229 return MSG_HANDLED;
232 message (D_ERROR, MSG_ERROR,
234 (" Cannot execute commands on non-local filesystems"));
236 return MSG_NOT_HANDLED;
238 #ifdef HAVE_SUBSHELL_SUPPORT
239 /* Check this early before we clean command line
240 * (will be checked again by shell_execute) */
241 if (use_subshell && subshell_state != INACTIVE) {
242 message (D_ERROR, MSG_ERROR,
243 _(" The shell is already running a command "));
244 return MSG_NOT_HANDLED;
246 #endif
247 cmd_len = strlen (cmd);
248 command = g_malloc (cmd_len + 1);
249 command[0] = 0;
250 for (i = j = 0; i < cmd_len; i++) {
251 if (cmd[i] == '%') {
252 i++;
253 s = expand_format (NULL, cmd[i], 1);
254 command = g_realloc (command, j + strlen (s) + cmd_len - i + 1);
255 strcpy (command + j, s);
256 g_free (s);
257 j = strlen (command);
258 } else {
259 command[j] = cmd[i];
260 j++;
262 command[j] = 0;
264 new_input (lc_cmdline);
265 shell_execute (command, 0);
266 g_free (command);
268 #ifdef HAVE_SUBSHELL_SUPPORT
269 if (quit & SUBSHELL_EXIT) {
270 quiet_quit_cmd ();
271 return MSG_HANDLED;
273 if (use_subshell)
274 load_prompt (0, 0);
275 #endif
277 return MSG_HANDLED;
280 static cb_ret_t
281 command_callback (Widget *w, widget_msg_t msg, int parm)
283 WInput *cmd = (WInput *) w;
285 switch (msg) {
286 case WIDGET_FOCUS:
287 /* Never accept focus, otherwise panels will be unselected */
288 return MSG_NOT_HANDLED;
290 case WIDGET_KEY:
291 /* Special case: we handle the enter key */
292 if (parm == '\n') {
293 return enter (cmd);
295 /* fall through */
297 default:
298 return input_callback (w, msg, parm);
302 WInput *
303 command_new (int y, int x, int cols)
305 WInput *cmd;
307 cmd = input_new (y, x, DEFAULT_COLOR, cols, "", "cmdline",
308 INPUT_COMPLETE_DEFAULT | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC);
310 /* Add our hooks */
311 cmd->widget.callback = command_callback;
312 cmd->completion_flags |= INPUT_COMPLETE_COMMANDS;
314 return cmd;
318 * Insert quoted text in input line. The function is meant for the
319 * command line, so the percent sign is quoted as well.
321 void
322 command_insert (WInput * in, const char *text, int insert_extra_space)
324 char *quoted_text;
326 quoted_text = name_quote (text, 1);
327 stuff (in, quoted_text, insert_extra_space);
328 g_free (quoted_text);