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" // DEFAULT_COLOR
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 input_colors_t command_colors;
72
73 /* --------------------------------------------------------------------------------------------- */
74 /*** file scope functions ************************************************************************/
75 /* --------------------------------------------------------------------------------------------- */
76
77 /** Handle Enter on the command line
78 *
79 * @param lc_cmdline string for handling
80 * @return MSG_HANDLED on success else MSG_NOT_HANDLED
81 */
82
83 static cb_ret_t
84 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)
*/
85 {
86 const char *cmd;
87
88 if (!command_prompt)
89 return MSG_HANDLED;
90
91 cmd = input_get_ctext (lc_cmdline);
92
93 // Any initial whitespace should be removed at this point
94 while (whiteness (*cmd))
95 cmd++;
96
97 if (*cmd == '\0')
98 return MSG_HANDLED;
99
100 if (strncmp (cmd, "cd", 2) == 0 && (cmd[2] == '\0' || whitespace (cmd[2])))
101 {
102 cd_to (cmd + 2);
103 input_clean (lc_cmdline);
104 return MSG_HANDLED;
105 }
106 else if (strcmp (cmd, "exit") == 0)
107 {
108 input_assign_text (lc_cmdline, "");
109 if (!quiet_quit_cmd ())
110 return MSG_NOT_HANDLED;
111 }
112 else
113 {
114 GString *command;
115 size_t i;
116
117 if (!vfs_current_is_local ())
118 {
119 message (D_ERROR, MSG_ERROR, _ ("Cannot execute commands on non-local filesystems"));
120 return MSG_NOT_HANDLED;
121 }
122 #ifdef ENABLE_SUBSHELL
123 /* Check this early before we clean command line
124 * (will be checked again by shell_execute) */
125 if (mc_global.tty.use_subshell && subshell_state != INACTIVE)
126 {
127 message (D_ERROR, MSG_ERROR, _ ("The shell is already running a command"));
128 return MSG_NOT_HANDLED;
129 }
130 #endif
131 command = g_string_sized_new (32);
132
133 for (i = 0; cmd[i] != '\0'; i++)
134 {
135 if (cmd[i] != '%')
136 g_string_append_c (command, cmd[i]);
137 else
138 {
139 char *s;
140
141 s = expand_format (NULL, cmd[++i], TRUE);
142 if (s != NULL)
143 {
144 g_string_append (command, s);
145 g_free (s);
146 }
147 }
148 }
149
150 input_clean (lc_cmdline);
151 shell_execute (command->str, 0);
152 g_string_free (command, TRUE);
153
154 #ifdef ENABLE_SUBSHELL
155 if ((quit & SUBSHELL_EXIT) != 0)
156 {
157 if (quiet_quit_cmd ())
158 return MSG_HANDLED;
159
160 quit = 0;
161 // restart subshell
162 if (mc_global.tty.use_subshell)
163 init_subshell ();
164 }
165
166 if (mc_global.tty.use_subshell)
167 do_load_prompt ();
168 #endif
169 }
170 return MSG_HANDLED;
171 }
172
173 /* --------------------------------------------------------------------------------------------- */
174
175 /**
176 * Default command line callback
177 *
178 * @param w Widget object
179 * @param msg message for handling
180 * @param parm extra parameter such as key code
181 *
182 * @return MSG_NOT_HANDLED on fail else MSG_HANDLED
183 */
184
185 static cb_ret_t
186 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)
*/
187 {
188 switch (msg)
189 {
190 case MSG_KEY:
191 // Special case: we handle the enter key
192 if (parm == '\n')
193 return enter (INPUT (w));
194 MC_FALLTHROUGH;
195
196 default:
197 return input_callback (w, sender, msg, parm, data);
198 }
199 }
200
201 /* --------------------------------------------------------------------------------------------- */
202 /*** public functions ****************************************************************************/
203 /* --------------------------------------------------------------------------------------------- */
204
205 WInput *
206 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)
*/
207 {
208 WInput *cmd;
209 Widget *w;
210
211 cmd = input_new (y, x, command_colors, cols, "", "cmdline",
212 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES
213 | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS
214 | INPUT_COMPLETE_SHELL_ESC);
215 w = WIDGET (cmd);
216 // Don't set WOP_SELECTABLE up, otherwise panels will be unselected
217 widget_set_options (w, WOP_SELECTABLE, FALSE);
218 // Add our hooks
219 w->callback = command_callback;
220
221 return cmd;
222 }
223
224 /* --------------------------------------------------------------------------------------------- */
225 /**
226 * Set colors for the command line.
227 */
228
229 void
230 command_set_default_colors (void)
/* ![[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)
*/
231 {
232 command_colors[WINPUTC_MAIN] = DEFAULT_COLOR;
233 command_colors[WINPUTC_MARK] = COMMAND_MARK_COLOR;
234 command_colors[WINPUTC_UNCHANGED] = DEFAULT_COLOR;
235 command_colors[WINPUTC_HISTORY] = COMMAND_HISTORY_COLOR;
236 }
237
238 /* --------------------------------------------------------------------------------------------- */
239 /**
240 * Insert quoted text in input line. The function is meant for the
241 * command line, so the percent sign is quoted as well.
242 *
243 * @param in WInput object
244 * @param text string for insertion
245 * @param insert_extra_space add extra space
246 */
247
248 void
249 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)
*/
250 {
251 char *quoted_text;
252
253 quoted_text = name_quote (text, TRUE);
254 if (quoted_text != NULL)
255 {
256 input_insert (in, quoted_text, insert_extra_space);
257 g_free (quoted_text);
258 }
259 }
260
261 /* --------------------------------------------------------------------------------------------- */