#include "precomp.h" -> #include <precomp.h>
[reactos.git] / reactos / subsys / system / cmd / cmdinput.c
1 /*
2 * CMDINPUT.C - handles command input (tab completion, history, etc.).
3 *
4 *
5 * History:
6 *
7 * 01/14/95 (Tim Norman)
8 * started.
9 *
10 * 08/08/95 (Matt Rains)
11 * i have cleaned up the source code. changes now bring this source
12 * into guidelines for recommended programming practice.
13 * i have added some constants to help making changes easier.
14 *
15 * 12/12/95 (Tim Norman)
16 * added findxy() function to get max x/y coordinates to display
17 * correctly on larger screens
18 *
19 * 12/14/95 (Tim Norman)
20 * fixed the Tab completion code that Matt Rains broke by moving local
21 * variables to a more global scope and forgetting to initialize them
22 * when needed
23 *
24 * 8/1/96 (Tim Norman)
25 * fixed a bug in tab completion that caused filenames at the beginning
26 * of the command-line to have their first letter truncated
27 *
28 * 9/1/96 (Tim Norman)
29 * fixed a silly bug using printf instead of fputs, where typing "%i"
30 * confused printf :)
31 *
32 * 6/14/97 (Steffan Kaiser)
33 * ctrl-break checking
34 *
35 * 6/7/97 (Marc Desrochers)
36 * recoded everything! now properly adjusts when text font is changed.
37 * removed findxy(), reposition(), and reprint(), as these functions
38 * were inefficient. added goxy() function as gotoxy() was buggy when
39 * the screen font was changed. the printf() problem with %i on the
40 * command line was fixed by doing printf("%s",str) instead of
41 * printf(str). Don't ask how I find em just be glad I do :)
42 *
43 * 7/12/97 (Tim Norman)
44 * Note: above changes pre-empted Steffan's ctrl-break checking.
45 *
46 * 7/7/97 (Marc Desrochers)
47 * rewrote a new findxy() because the new dir() used it. This
48 * findxy() simply returns the values of *maxx *maxy. In the
49 * future, please use the pointers, they will always be correct
50 * since they point to BIOS values.
51 *
52 * 7/8/97 (Marc Desrochers)
53 * once again removed findxy(), moved the *maxx, *maxy pointers
54 * global and included them as externs in command.h. Also added
55 * insert/overstrike capability
56 *
57 * 7/13/97 (Tim Norman)
58 * added different cursor appearance for insert/overstrike mode
59 *
60 * 7/13/97 (Tim Norman)
61 * changed my code to use _setcursortype until I can figure out why
62 * my code is crashing on some machines. It doesn't crash on mine :)
63 *
64 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
65 * added config.h include
66 *
67 * 28-Jul-1998 (John P Price <linux-guru@gcfl.net>)
68 * put ifdef's around filename completion code.
69 *
70 * 30-Jul-1998 (John P Price <linux-guru@gcfl.net>)
71 * moved filename completion code to filecomp.c
72 * made second TAB display list of filename matches
73 *
74 * 31-Jul-1998 (John P Price <linux-guru@gcfl.net>)
75 * Fixed bug where if you typed something, then hit HOME, then tried
76 * to type something else in insert mode, it crashed.
77 *
78 * 07-Aug-1998 (John P Price <linux-guru@gcfl.net>)
79 * Fixed carrage return output to better match MSDOS with echo
80 * on or off.(marked with "JPP 19980708")
81 *
82 * 13-Dec-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
83 * Added insert/overwrite cursor.
84 *
85 * 25-Jan-1998 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
86 * Replaced CRT io functions by Win32 console io functions.
87 * This can handle <Shift>-<Tab> for 4NT filename completion.
88 * Unicode and redirection safe!
89 *
90 * 04-Feb-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
91 * Fixed input bug. A "line feed" character remained in the keyboard
92 * input queue when you pressed <RETURN>. This sometimes caused
93 * some very strange effects.
94 * Fixed some command line editing annoyances.
95 *
96 * 30-Apr-2004 (Filip Navara <xnavara@volny.cz>)
97 * Fixed problems when the screen was scrolled away.
98 */
99
100 #include <precomp.h>
101
102
103 SHORT maxx;
104 SHORT maxy;
105
106 /*
107 * global command line insert/overwrite flag
108 */
109 static BOOL bInsert = TRUE;
110
111
112 static VOID
113 ClearCommandLine (LPTSTR str, INT maxlen, SHORT orgx, SHORT orgy)
114 {
115 INT count;
116
117 SetCursorXY (orgx, orgy);
118 for (count = 0; count < (INT)_tcslen (str); count++)
119 ConOutChar (_T(' '));
120 _tcsnset (str, _T('\0'), maxlen);
121 SetCursorXY (orgx, orgy);
122 }
123
124
125 /* read in a command line */
126 VOID ReadCommand (LPTSTR str, INT maxlen)
127 {
128 SHORT orgx; /* origin x/y */
129 SHORT orgy;
130 SHORT curx; /*current x/y cursor position*/
131 SHORT cury;
132 SHORT tempscreen;
133 INT count; /*used in some for loops*/
134 INT current = 0; /*the position of the cursor in the string (str)*/
135 INT charcount = 0;/*chars in the string (str)*/
136 INPUT_RECORD ir;
137 WORD wLastKey = 0;
138 TCHAR ch;
139 BOOL bContinue=FALSE;/*is TRUE the second case will not be executed*/
140
141 /* get screen size */
142 GetScreenSize (&maxx, &maxy);
143
144 /* JPP 19980807 - if echo off, don't print prompt */
145 if (bEcho)
146 PrintPrompt();
147
148 GetCursorXY (&orgx, &orgy);
149 GetCursorXY (&curx, &cury);
150
151 memset (str, 0, maxlen * sizeof (TCHAR));
152
153 SetCursorType (bInsert, TRUE);
154
155 do
156 {
157 ConInKey (&ir);
158
159 if (ir.Event.KeyEvent.dwControlKeyState &
160 (RIGHT_ALT_PRESSED|RIGHT_ALT_PRESSED|
161 RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) )
162 {
163
164 switch (ir.Event.KeyEvent.wVirtualKeyCode)
165 {
166
167 #ifdef FEATURE_HISTORY
168
169 case 'K':
170 /*add the current command line to the history*/
171 if (ir.Event.KeyEvent.dwControlKeyState &
172 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
173 {
174
175 if (str[0])
176 History(0,str);
177
178 ClearCommandLine (str, maxlen, orgx, orgy);
179 current = charcount = 0;
180 curx = orgx;
181 cury = orgy;
182 bContinue=TRUE;
183 break;
184 }
185
186 case 'D':
187 /*delete current history entry*/
188 if (ir.Event.KeyEvent.dwControlKeyState &
189 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
190 {
191 ClearCommandLine (str, maxlen, orgx, orgy);
192 History_del_current_entry(str);
193 current = charcount = _tcslen (str);
194 ConOutPrintf (_T("%s"), str);
195 GetCursorXY (&curx, &cury);
196 bContinue=TRUE;
197 break;
198 }
199
200 #endif/*FEATURE_HISTORY*/
201 }
202
203
204
205
206 }
207
208 //if (bContinue)
209 // continue;
210
211
212
213 switch (ir.Event.KeyEvent.wVirtualKeyCode)
214 {
215 case VK_BACK:
216 /* <BACKSPACE> - delete character to left of cursor */
217 if (current > 0 && charcount > 0)
218 {
219 if (current == charcount)
220 {
221 /* if at end of line */
222 str[current - 1] = _T('\0');
223 if (GetCursorX () != 0)
224 {
225 ConOutPrintf (_T("\b \b"));
226 curx--;
227 }
228 else
229 {
230 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
231 ConOutChar (_T(' '));
232 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
233 cury--;
234 curx = maxx - 1;
235 }
236 }
237 else
238 {
239 for (count = current - 1; count < charcount; count++)
240 str[count] = str[count + 1];
241 if (GetCursorX () != 0)
242 {
243 SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
244 curx--;
245 }
246 else
247 {
248 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
249 cury--;
250 curx = maxx - 1;
251 }
252 GetCursorXY (&curx, &cury);
253 ConOutPrintf (_T("%s "), &str[current - 1]);
254 SetCursorXY (curx, cury);
255 }
256 charcount--;
257 current--;
258 }
259 break;
260
261 case VK_INSERT:
262 /* toggle insert/overstrike mode */
263 bInsert ^= TRUE;
264 SetCursorType (bInsert, TRUE);
265 break;
266
267 case VK_DELETE:
268 /* delete character under cursor */
269 if (current != charcount && charcount > 0)
270 {
271 for (count = current; count < charcount; count++)
272 str[count] = str[count + 1];
273 charcount--;
274 GetCursorXY (&curx, &cury);
275 ConOutPrintf (_T("%s "), &str[current]);
276 SetCursorXY (curx, cury);
277 }
278 break;
279
280 case VK_HOME:
281 /* goto beginning of string */
282 if (current != 0)
283 {
284 SetCursorXY (orgx, orgy);
285 curx = orgx;
286 cury = orgy;
287 current = 0;
288 }
289 break;
290
291 case VK_END:
292 /* goto end of string */
293 if (current != charcount)
294 {
295 SetCursorXY (orgx, orgy);
296 ConOutPrintf (_T("%s"), str);
297 GetCursorXY (&curx, &cury);
298 current = charcount;
299 }
300 break;
301
302 case VK_TAB:
303 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
304 /* expand current file name */
305 if ((current == charcount) ||
306 (current == charcount - 1 &&
307 str[current] == _T('"'))) /* only works at end of line*/
308 {
309 if (wLastKey != VK_TAB)
310 {
311 /* if first TAB, complete filename*/
312 tempscreen = charcount;
313 CompleteFilename (str, charcount);
314 charcount = _tcslen (str);
315 current = charcount;
316
317 if (current > 0 &&
318 str[current-1] == _T('"'))
319 current--;
320
321 SetCursorXY (orgx, orgy);
322 ConOutPrintf (_T("%s"), str);
323
324 if (tempscreen > charcount)
325 {
326 GetCursorXY (&curx, &cury);
327 for (count = tempscreen - charcount; count--; )
328 ConOutChar (_T(' '));
329 SetCursorXY (curx, cury);
330 }
331 else
332 {
333 if (((charcount + orgx) / maxx) + orgy > maxy - 1)
334 orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
335 }
336
337 /* set cursor position */
338 SetCursorXY ((orgx + current) % maxx,
339 orgy + (orgx + current) / maxx);
340 GetCursorXY (&curx, &cury);
341 }
342 else
343 {
344 /*if second TAB, list matches*/
345 if (ShowCompletionMatches (str, charcount))
346 {
347 PrintPrompt ();
348 GetCursorXY (&orgx, &orgy);
349 ConOutPrintf (_T("%s"), str);
350
351 /* set cursor position */
352 SetCursorXY ((orgx + current) % maxx,
353 orgy + (orgx + current) / maxx);
354 GetCursorXY (&curx, &cury);
355 }
356
357 }
358 }
359 else
360 {
361 #ifdef __REACTOS__
362 Beep (440, 50);
363 #else
364 MessageBeep (-1);
365 #endif
366 }
367 #endif
368 #ifdef FEATURE_4NT_FILENAME_COMPLETION
369 /* this is not implemented yet */
370 if (ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
371 {
372 /* get previous match */
373
374 }
375 else
376 {
377 /* get next match */
378
379 }
380 #endif
381 break;
382
383 case VK_RETURN:
384 /* end input, return to main */
385 #ifdef FEATURE_HISTORY
386 /* add to the history */
387 if (str[0])
388 History (0, str);
389 #endif
390 ConInDummy ();
391 ConOutChar (_T('\n'));
392 break;
393
394 case VK_ESCAPE:
395 /* clear str Make this callable! */
396 ClearCommandLine (str, maxlen, orgx, orgy);
397 curx = orgx;
398 cury = orgy;
399 current = charcount = 0;
400 break;
401
402 #ifdef FEATURE_HISTORY
403 case VK_F3:
404 History_move_to_bottom();
405 #endif
406 case VK_UP:
407 #ifdef FEATURE_HISTORY
408 /* get previous command from buffer */
409 ClearCommandLine (str, maxlen, orgx, orgy);
410 History (-1, str);
411 current = charcount = _tcslen (str);
412 if (((charcount + orgx) / maxx) + orgy > maxy - 1)
413 orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
414 ConOutPrintf (_T("%s"), str);
415 GetCursorXY (&curx, &cury);
416 #endif
417 break;
418
419 case VK_DOWN:
420 #ifdef FEATURE_HISTORY
421 /* get next command from buffer */
422 ClearCommandLine (str, maxlen, orgx, orgy);
423 History (1, str);
424 current = charcount = _tcslen (str);
425 if (((charcount + orgx) / maxx) + orgy > maxy - 1)
426 orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
427 ConOutPrintf (_T("%s"), str);
428 GetCursorXY (&curx, &cury);
429 #endif
430 break;
431
432 case VK_LEFT:
433 /* move cursor left */
434 if (current > 0)
435 {
436 current--;
437 if (GetCursorX () == 0)
438 {
439 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
440 curx = maxx - 1;
441 cury--;
442 }
443 else
444 {
445 SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
446 curx--;
447 }
448 }
449 else
450 {
451 #ifdef __REACTOS__
452 Beep (440, 50);
453 #else
454 MessageBeep (-1);
455 #endif
456 }
457 break;
458
459 case VK_RIGHT:
460 /* move cursor right */
461 if (current != charcount)
462 {
463 current++;
464 if (GetCursorX () == maxx - 1)
465 {
466 SetCursorXY (0, (SHORT)(GetCursorY () + 1));
467 curx = 0;
468 cury++;
469 }
470 else
471 {
472 SetCursorXY ((SHORT)(GetCursorX () + 1), GetCursorY ());
473 curx++;
474 }
475 }
476 break;
477
478 default:
479 #ifdef _UNICODE
480 ch = ir.Event.KeyEvent.uChar.UnicodeChar;
481 if ((ch >= 32 && ch <= 255) && (charcount != (maxlen - 2)))
482 #else
483 ch = ir.Event.KeyEvent.uChar.AsciiChar;
484 if ((UCHAR)ch >= 32 && (charcount != (maxlen - 2)))
485 #endif /* _UNICODE */
486 {
487 /* insert character into string... */
488 if (bInsert && current != charcount)
489 {
490 /* If this character insertion will cause screen scrolling,
491 * adjust the saved origin of the command prompt. */
492 tempscreen = _tcslen(str + current) + curx;
493 if ((tempscreen % maxx) == (maxx - 1) &&
494 (tempscreen / maxx) + cury == (maxy - 1))
495 {
496 orgy--;
497 cury--;
498 }
499
500 for (count = charcount; count > current; count--)
501 str[count] = str[count - 1];
502 str[current++] = ch;
503 if (curx == maxx - 1)
504 curx = 0, cury++;
505 else
506 curx++;
507 ConOutPrintf (_T("%s"), &str[current - 1]);
508 SetCursorXY (curx, cury);
509 charcount++;
510 }
511 else
512 {
513 if (current == charcount)
514 charcount++;
515 str[current++] = ch;
516 if (GetCursorX () == maxx - 1 && GetCursorY () == maxy - 1)
517 orgy--, cury--;
518 if (GetCursorX () == maxx - 1)
519 curx = 0, cury++;
520 else
521 curx++;
522 ConOutChar (ch);
523 }
524 }
525 #if 0
526 else
527 {
528 #ifdef __REACTOS__
529 Beep (440, 100);
530 #else
531 MessageBeep (-1);
532 #endif
533 }
534 #endif
535 break;
536
537 }
538 wLastKey = ir.Event.KeyEvent.wVirtualKeyCode;
539 }
540 while (ir.Event.KeyEvent.wVirtualKeyCode != VK_RETURN);
541
542 SetCursorType (bInsert, TRUE);
543 }