1 /*
2 Command line widget.
3 This widget is derived from the WInput widget, it's used to cope
4 with all the magic of the command input line, we depend on some
5 help from the program's callback.
6
7 Copyright (C) 1995-2025
8 Free Software Foundation, Inc.
9
10 Written by:
11 Slava Zanko <slavazanko@gmail.com>, 2013
12 Andrew Borodin <aborodin@vmail.ru>, 2011-2022
13
14 This file is part of the Midnight Commander.
15
16 The Midnight Commander is free software: you can redistribute it
17 and/or modify it under the terms of the GNU General Public License as
18 published by the Free Software Foundation, either version 3 of the License,
19 or (at your option) any later version.
20
21 The Midnight Commander is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <https://www.gnu.org/licenses/>.
28 */
29
30 /** \file command.c
31 * \brief Source: command line widget
32 */
33
34 #include <config.h>
35
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "lib/global.h"
40 #include "lib/vfs/vfs.h" // vfs_current_is_local()
41 #include "lib/skin.h"
42 #include "lib/util.h" // whitespace()
43 #include "lib/widget.h"
44
45 #include "src/setup.h" // quit
46 #ifdef ENABLE_SUBSHELL
47 #include "src/subshell/subshell.h"
48 #endif
49 #include "src/execute.h" // shell_execute()
50 #include "src/usermenu.h" // expand_format()
51
52 #include "filemanager.h" // quiet_quit_cmd(), layout.h
53 #include "cd.h" // cd_to()
54
55 #include "command.h"
56
57 /*** global variables ****************************************************************************/
58
59 /* This holds the command line */
60 WInput *cmdline;
61
62 /*** file scope macro definitions ****************************************************************/
63
64 /*** file scope type declarations ****************************************************************/
65
66 /*** forward declarations (file scope functions) *************************************************/
67
68 /*** file scope variables ************************************************************************/
69
70 /* Color styles command line */
71 static const input_colors_t command_colors = {
72 [INPUT_COLOR_MAIN] = CORE_COMMANDLINE_COLOR,
73 [INPUT_COLOR_MARK] = CORE_COMMANDLINE_MARK_COLOR,
74 [INPUT_COLOR_UNCHANGED] = CORE_COMMANDLINE_COLOR,
75 [INPUT_COLOR_HISTORY] = CORE_COMMAND_HISTORY_COLOR,
76 };
77
78 /* --------------------------------------------------------------------------------------------- */
79 /*** file scope functions ************************************************************************/
80 /* --------------------------------------------------------------------------------------------- */
81
82 /** Handle Enter on the command line
83 *
84 * @param lc_cmdline string for handling
85 * @return MSG_HANDLED on success else MSG_NOT_HANDLED
86 */
87
88 static cb_ret_t
89 enter (WInput *lc_cmdline)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
90 {
91 const char *cmd;
92
93 if (!command_prompt)
94 return MSG_HANDLED;
95
96 cmd = input_get_ctext (lc_cmdline);
97
98 // Any initial whitespace should be removed at this point
99 while (whiteness (*cmd))
100 cmd++;
101
102 if (*cmd == '\0')
103 return MSG_HANDLED;
104
105 if (strncmp (cmd, "cd", 2) == 0 && (cmd[2] == '\0' || whitespace (cmd[2])))
106 {
107 cd_to (cmd + 2);
108 input_clean (lc_cmdline);
109 return MSG_HANDLED;
110 }
111 else if (strcmp (cmd, "exit") == 0)
112 {
113 input_assign_text (lc_cmdline, "");
114 if (!quiet_quit_cmd (FALSE))
115 return MSG_NOT_HANDLED;
116 }
117 else
118 {
119 GString *command;
120 size_t i;
121
122 if (!vfs_current_is_local ())
123 {
124 message (D_ERROR, MSG_ERROR, _ ("Cannot execute commands on non-local filesystems"));
125 return MSG_NOT_HANDLED;
126 }
127 #ifdef ENABLE_SUBSHELL
128 /* Check this early before we clean command line
129 * (will be checked again by shell_execute) */
130 if (mc_global.tty.use_subshell && subshell_state != INACTIVE)
131 {
132 message (D_ERROR, MSG_ERROR, _ ("The shell is already running a command"));
133 return MSG_NOT_HANDLED;
134 }
135 #endif
136 command = g_string_sized_new (32);
137
138 for (i = 0; cmd[i] != '\0'; i++)
139 {
140 if (cmd[i] != '%')
141 g_string_append_c (command, cmd[i]);
142 else
143 {
144 char *s;
145
146 s = expand_format (NULL, cmd[++i], TRUE);
147 if (s != NULL)
148 {
149 g_string_append (command, s);
150 g_free (s);
151 }
152 }
153 }
154
155 input_clean (lc_cmdline);
156 shell_execute (command->str, 0);
157 g_string_free (command, TRUE);
158
159 #ifdef ENABLE_SUBSHELL
160 if ((quit & SUBSHELL_EXIT) != 0)
161 {
162 if (quiet_quit_cmd (FALSE))
163 return MSG_HANDLED;
164
165 quit = 0;
166 // restart subshell
167 if (mc_global.tty.use_subshell)
168 init_subshell ();
169 }
170
171 if (mc_global.tty.use_subshell)
172 do_load_prompt ();
173 #endif
174 }
175 return MSG_HANDLED;
176 }
177
178 /* --------------------------------------------------------------------------------------------- */
179
180 /**
181 * Default command line callback
182 *
183 * @param w Widget object
184 * @param msg message for handling
185 * @param parm extra parameter such as key code
186 *
187 * @return MSG_NOT_HANDLED on fail else MSG_HANDLED
188 */
189
190 static cb_ret_t
191 command_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
192 {
193 switch (msg)
194 {
195 case MSG_KEY:
196 // Special case: we handle the enter key
197 if (parm == '\n')
198 return enter (INPUT (w));
199 MC_FALLTHROUGH;
200
201 default:
202 return input_callback (w, sender, msg, parm, data);
203 }
204 }
205
206 /* --------------------------------------------------------------------------------------------- */
207 /*** public functions ****************************************************************************/
208 /* --------------------------------------------------------------------------------------------- */
209
210 WInput *
211 command_new (int y, int x, int cols)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
212 {
213 WInput *cmd;
214 Widget *w;
215
216 cmd = input_new (y, x, command_colors, cols, "", "cmdline",
217 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES
218 | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS
219 | INPUT_COMPLETE_SHELL_ESC);
220 w = WIDGET (cmd);
221 // Don't set WOP_SELECTABLE up, otherwise panels will be unselected
222 widget_set_options (w, WOP_SELECTABLE, FALSE);
223 // Add our hooks
224 w->callback = command_callback;
225
226 return cmd;
227 }
228
229 /* --------------------------------------------------------------------------------------------- */
230 /**
231 * Insert quoted text in input line. The function is meant for the
232 * command line, so the percent sign is quoted as well.
233 *
234 * @param in WInput object
235 * @param text string for insertion
236 * @param insert_extra_space add extra space
237 */
238
239 void
240 command_insert (WInput *in, const char *text, gboolean insert_extra_space)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
241 {
242 char *quoted_text;
243
244 quoted_text = name_quote (text, TRUE);
245 if (quoted_text != NULL)
246 {
247 input_insert (in, quoted_text, insert_extra_space);
248 g_free (quoted_text);
249 }
250 }
251
252 /* --------------------------------------------------------------------------------------------- */