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