ce987c6a7ea65a65f1a665fda678d9382ea18218
[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 #include "resource.h"
102
103
104 SHORT maxx;
105 SHORT maxy;
106
107 /*
108 * global command line insert/overwrite flag
109 */
110 static BOOL bInsert = TRUE;
111
112
113 static VOID
114 ClearCommandLine (LPTSTR str, INT maxlen, SHORT orgx, SHORT orgy)
115 {
116 INT count;
117
118 SetCursorXY (orgx, orgy);
119 for (count = 0; count < (INT)_tcslen (str); count++)
120 ConOutChar (_T(' '));
121 _tcsnset (str, _T('\0'), maxlen);
122 SetCursorXY (orgx, orgy);
123 }
124
125
126 /* read in a command line */
127 VOID ReadCommand (LPTSTR str, INT maxlen)
128 {
129 SHORT orgx; /* origin x/y */
130 SHORT orgy;
131 SHORT curx; /*current x/y cursor position*/
132 SHORT cury;
133 SHORT tempscreen;
134 INT count; /*used in some for loops*/
135 INT current = 0; /*the position of the cursor in the string (str)*/
136 INT charcount = 0;/*chars in the string (str)*/
137 INPUT_RECORD ir;
138 WORD wLastKey = 0;
139 TCHAR ch;
140 BOOL bContinue=FALSE;/*is TRUE the second case will not be executed*/
141 BOOL bReturn = FALSE;
142 TCHAR szPath[MAX_PATH];
143 BOOL bCharInput;
144
145 /* get screen size */
146 GetScreenSize (&maxx, &maxy);
147
148 /* JPP 19980807 - if echo off, don't print prompt */
149 if (bEcho)
150 PrintPrompt();
151
152 GetCursorXY (&orgx, &orgy);
153 GetCursorXY (&curx, &cury);
154
155 memset (str, 0, maxlen * sizeof (TCHAR));
156
157 SetCursorType (bInsert, TRUE);
158
159 do
160 {
161
162 bReturn = FALSE;
163
164 ConInKey (&ir);
165
166 if (ir.Event.KeyEvent.dwControlKeyState &
167 (RIGHT_ALT_PRESSED|RIGHT_ALT_PRESSED|
168 RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) )
169 {
170
171 switch (ir.Event.KeyEvent.wVirtualKeyCode)
172 {
173
174 #ifdef FEATURE_HISTORY
175
176 case 'K':
177 /*add the current command line to the history*/
178 if (ir.Event.KeyEvent.dwControlKeyState &
179 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
180 {
181
182 if (str[0])
183 History(0,str);
184
185 ClearCommandLine (str, maxlen, orgx, orgy);
186 current = charcount = 0;
187 curx = orgx;
188 cury = orgy;
189 bContinue=TRUE;
190 break;
191 }
192
193 case 'D':
194 /*delete current history entry*/
195 if (ir.Event.KeyEvent.dwControlKeyState &
196 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
197 {
198 ClearCommandLine (str, maxlen, orgx, orgy);
199 History_del_current_entry(str);
200 current = charcount = _tcslen (str);
201 ConOutPrintf (_T("%s"), str);
202 GetCursorXY (&curx, &cury);
203 bContinue=TRUE;
204 break;
205 }
206
207 #endif/*FEATURE_HISTORY*/
208 }
209
210
211
212
213 }
214
215 //if (bContinue)
216 // continue;
217
218
219 bCharInput = FALSE;
220
221 switch (ir.Event.KeyEvent.wVirtualKeyCode)
222 {
223 case VK_BACK:
224 /* <BACKSPACE> - delete character to left of cursor */
225 if (current > 0 && charcount > 0)
226 {
227 if (current == charcount)
228 {
229 /* if at end of line */
230 str[current - 1] = _T('\0');
231 if (GetCursorX () != 0)
232 {
233 ConOutPrintf (_T("\b \b"));
234 curx--;
235 }
236 else
237 {
238 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
239 ConOutChar (_T(' '));
240 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
241 cury--;
242 curx = maxx - 1;
243 }
244 }
245 else
246 {
247 for (count = current - 1; count < charcount; count++)
248 str[count] = str[count + 1];
249 if (GetCursorX () != 0)
250 {
251 SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
252 curx--;
253 }
254 else
255 {
256 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
257 cury--;
258 curx = maxx - 1;
259 }
260 GetCursorXY (&curx, &cury);
261 ConOutPrintf (_T("%s "), &str[current - 1]);
262 SetCursorXY (curx, cury);
263 }
264 charcount--;
265 current--;
266 }
267 break;
268
269 case VK_INSERT:
270 /* toggle insert/overstrike mode */
271 bInsert ^= TRUE;
272 SetCursorType (bInsert, TRUE);
273 break;
274
275 case VK_DELETE:
276 /* delete character under cursor */
277 if (current != charcount && charcount > 0)
278 {
279 for (count = current; count < charcount; count++)
280 str[count] = str[count + 1];
281 charcount--;
282 GetCursorXY (&curx, &cury);
283 ConOutPrintf (_T("%s "), &str[current]);
284 SetCursorXY (curx, cury);
285 }
286 break;
287
288 case VK_HOME:
289 /* goto beginning of string */
290 if (current != 0)
291 {
292 SetCursorXY (orgx, orgy);
293 curx = orgx;
294 cury = orgy;
295 current = 0;
296 }
297 break;
298
299 case VK_END:
300 /* goto end of string */
301 if (current != charcount)
302 {
303 SetCursorXY (orgx, orgy);
304 ConOutPrintf (_T("%s"), str);
305 GetCursorXY (&curx, &cury);
306 current = charcount;
307 }
308 break;
309
310 case VK_TAB:
311 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
312 /* expand current file name */
313 if ((current == charcount) ||
314 (current == charcount - 1 &&
315 str[current] == _T('"'))) /* only works at end of line*/
316 {
317 if (wLastKey != VK_TAB)
318 {
319 /* if first TAB, complete filename*/
320 tempscreen = charcount;
321 CompleteFilename (str, charcount);
322 charcount = _tcslen (str);
323 current = charcount;
324
325 SetCursorXY (orgx, orgy);
326 ConOutPrintf (_T("%s"), str);
327
328 if (tempscreen > charcount)
329 {
330 GetCursorXY (&curx, &cury);
331 for (count = tempscreen - charcount; count--; )
332 ConOutChar (_T(' '));
333 SetCursorXY (curx, cury);
334 }
335 else
336 {
337 if (((charcount + orgx) / maxx) + orgy > maxy - 1)
338 orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
339 }
340
341 /* set cursor position */
342 SetCursorXY ((orgx + current) % maxx,
343 orgy + (orgx + current) / maxx);
344 GetCursorXY (&curx, &cury);
345 }
346 else
347 {
348 /*if second TAB, list matches*/
349 if (ShowCompletionMatches (str, charcount))
350 {
351 PrintPrompt ();
352 GetCursorXY (&orgx, &orgy);
353 ConOutPrintf (_T("%s"), str);
354
355 /* set cursor position */
356 SetCursorXY ((orgx + current) % maxx,
357 orgy + (orgx + current) / maxx);
358 GetCursorXY (&curx, &cury);
359 }
360
361 }
362 }
363 else
364 {
365 MessageBeep (-1);
366 }
367 #endif
368 #ifdef FEATURE_4NT_FILENAME_COMPLETION
369
370 /* used to later see if we went down to the next line */
371 tempscreen = charcount;
372 szPath[0]=_T('\0');
373
374 /* str is the whole things that is on the current line
375 that is and and out. arg 2 is weather it goes back
376 one file or forward one file */
377 CompleteFilename(str, !(ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED), szPath, current);
378 /* Attempt to clear the line */
379 ClearCommandLine (str, maxlen, orgx, orgy);
380 curx = orgx;
381 cury = orgy;
382 current = charcount = 0;
383 //str[0]=_T('\0');
384
385 /* Everything is deleted, lets add it back in */
386 _tcscpy(str,szPath);
387
388 /* Figure out where cusor is going to be after we print it */
389 charcount = _tcslen (str);
390 current = charcount;
391
392 SetCursorXY (orgx, orgy);
393 /* Print out what we have now */
394 ConOutPrintf (_T("%s"), str);
395
396 /* Move cursor accordingly */
397 if(tempscreen > charcount)
398 {
399 GetCursorXY (&curx, &cury);
400 for(count = tempscreen - charcount; count--; )
401 ConOutChar (_T(' '));
402 SetCursorXY (curx, cury);
403 }
404 else
405 {
406 if(((charcount + orgx) / maxx) + orgy > maxy - 1)
407 orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
408 }
409 SetCursorXY((short)(((int)orgx + current) % maxx), (short)((int)orgy + ((int)orgx + current) / maxx));
410 GetCursorXY(&curx, &cury);
411
412 #endif
413 break;
414
415 case _T('M'):
416 case _T('C'):
417 /* ^M does the same as return */
418 if(!(ir.Event.KeyEvent.dwControlKeyState &
419 (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)))
420 {
421 break;
422 }
423
424 case VK_RETURN:
425 /* end input, return to main */
426 #ifdef FEATURE_HISTORY
427 /* add to the history */
428 if (str[0])
429 History (0, str);
430 #endif
431 ConInDummy ();
432 ConOutChar (_T('\n'));
433 bReturn = TRUE;
434 break;
435
436 case VK_ESCAPE:
437 /* clear str Make this callable! */
438 ClearCommandLine (str, maxlen, orgx, orgy);
439 curx = orgx;
440 cury = orgy;
441 current = charcount = 0;
442 break;
443
444 #ifdef FEATURE_HISTORY
445 case VK_F3:
446 History_move_to_bottom();
447 #endif
448 case VK_UP:
449 #ifdef FEATURE_HISTORY
450 /* get previous command from buffer */
451 ClearCommandLine (str, maxlen, orgx, orgy);
452 History (-1, str);
453 current = charcount = _tcslen (str);
454 if (((charcount + orgx) / maxx) + orgy > maxy - 1)
455 orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
456 ConOutPrintf (_T("%s"), str);
457 GetCursorXY (&curx, &cury);
458 #endif
459 break;
460
461 case VK_DOWN:
462 #ifdef FEATURE_HISTORY
463 /* get next command from buffer */
464 ClearCommandLine (str, maxlen, orgx, orgy);
465 History (1, str);
466 current = charcount = _tcslen (str);
467 if (((charcount + orgx) / maxx) + orgy > maxy - 1)
468 orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
469 ConOutPrintf (_T("%s"), str);
470 GetCursorXY (&curx, &cury);
471 #endif
472 break;
473
474 case VK_LEFT:
475 /* move cursor left */
476 if (current > 0)
477 {
478 current--;
479 if (GetCursorX () == 0)
480 {
481 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
482 curx = maxx - 1;
483 cury--;
484 }
485 else
486 {
487 SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
488 curx--;
489 }
490 }
491 else
492 {
493 MessageBeep (-1);
494 }
495 break;
496
497 case VK_RIGHT:
498 /* move cursor right */
499 if (current != charcount)
500 {
501 current++;
502 if (GetCursorX () == maxx - 1)
503 {
504 SetCursorXY (0, (SHORT)(GetCursorY () + 1));
505 curx = 0;
506 cury++;
507 }
508 else
509 {
510 SetCursorXY ((SHORT)(GetCursorX () + 1), GetCursorY ());
511 curx++;
512 }
513 }
514 break;
515
516 default:
517 /* This input is just a normal char */
518 bCharInput = TRUE;
519
520 }
521 #ifdef _UNICODE
522 ch = ir.Event.KeyEvent.uChar.UnicodeChar;
523 if ((ch >= 32 && (charcount != (maxlen - 2)) && bCharInput)
524 #else
525 ch = ir.Event.KeyEvent.uChar.AsciiChar;
526 if ((UCHAR)ch >= 32 && (charcount != (maxlen - 2)) && bCharInput)
527 #endif /* _UNICODE */
528 {
529 /* insert character into string... */
530 if (bInsert && current != charcount)
531 {
532 /* If this character insertion will cause screen scrolling,
533 * adjust the saved origin of the command prompt. */
534 tempscreen = _tcslen(str + current) + curx;
535 if ((tempscreen % maxx) == (maxx - 1) &&
536 (tempscreen / maxx) + cury == (maxy - 1))
537 {
538 orgy--;
539 cury--;
540 }
541
542 for (count = charcount; count > current; count--)
543 str[count] = str[count - 1];
544 str[current++] = ch;
545 if (curx == maxx - 1)
546 curx = 0, cury++;
547 else
548 curx++;
549 ConOutPrintf (_T("%s"), &str[current - 1]);
550 SetCursorXY (curx, cury);
551 charcount++;
552 }
553 else
554 {
555 if (current == charcount)
556 charcount++;
557 str[current++] = ch;
558 if (GetCursorX () == maxx - 1 && GetCursorY () == maxy - 1)
559 orgy--, cury--;
560 if (GetCursorX () == maxx - 1)
561 curx = 0, cury++;
562 else
563 curx++;
564 ConOutChar (ch);
565 }
566 }
567
568 wLastKey = ir.Event.KeyEvent.wVirtualKeyCode;
569 }
570 while (!bReturn);
571
572 SetCursorType (bInsert, TRUE);
573 }