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