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