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