update for HEAD-2003050101
[reactos.git] / 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
97 #include "config.h"
98
99 #include <windows.h>
100 #include <tchar.h>
101 #include <string.h>
102
103 #include "cmd.h"
104 #include "batch.h"
105
106
107 SHORT maxx;
108 SHORT maxy;
109
110 /*
111  * global command line insert/overwrite flag
112  */
113 static BOOL bInsert = TRUE;
114
115
116 static VOID
117 ClearCommandLine (LPTSTR str, INT maxlen, SHORT orgx, SHORT orgy)
118 {
119         INT count;
120
121         SetCursorXY (orgx, orgy);
122         for (count = 0; count < (INT)_tcslen (str); count++)
123                 ConOutChar (_T(' '));
124         _tcsnset (str, _T('\0'), maxlen);
125         SetCursorXY (orgx, orgy);
126 }
127
128
129 /* read in a command line */
130 VOID ReadCommand (LPTSTR str, INT maxlen)
131 {
132         SHORT orgx;                     /* origin x/y */
133         SHORT orgy;
134         SHORT curx;                     /*current x/y cursor position*/
135         SHORT cury;
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
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
153         memset (str, 0, maxlen * sizeof (TCHAR));
154
155         SetCursorType (bInsert, TRUE);
156
157         do
158         {
159                 ConInKey (&ir);
160
161                 if (ir.Event.KeyEvent.dwControlKeyState &
162                         (RIGHT_ALT_PRESSED|RIGHT_ALT_PRESSED|
163                         RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) )
164                 {
165
166                         switch (ir.Event.KeyEvent.wVirtualKeyCode)
167                         {
168
169 #ifdef FEATURE_HISTORY
170         
171                                 case 'K':
172                                         /*add the current command line to the history*/
173                                         if (ir.Event.KeyEvent.dwControlKeyState &
174                                                 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
175                                         {
176                                 
177                                                 if (str[0])
178                                                         History(0,str);
179
180                                                 ClearCommandLine (str, maxlen, orgx, orgy);
181                                                 current = charcount = 0;
182                                                 bContinue=TRUE;
183                                                 break;
184                                         }
185
186                                 case 'D':
187                                         /*delete current history entry*/
188                                         if (ir.Event.KeyEvent.dwControlKeyState &
189                                                 (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
190                                         {
191                                                 ClearCommandLine (str, maxlen, orgx, orgy);
192                                                 History_del_current_entry(str);                                 
193                                                 current = charcount = _tcslen (str);
194                                                 ConOutPrintf (_T("%s"), str);
195                                                 bContinue=TRUE;
196                                                 break;
197                                         }
198
199 #endif/*FEATURE_HISTORY*/
200                         }
201
202
203                         
204
205                 }
206
207                 //if (bContinue)
208                 //      continue;
209
210
211
212                 switch (ir.Event.KeyEvent.wVirtualKeyCode)
213                 {
214                         case VK_BACK:
215                                 /* <BACKSPACE> - delete character to left of cursor */
216                                 if (current > 0 && charcount > 0)
217                                 {
218                                         if (current == charcount)
219                                         {
220                                                 /* if at end of line */
221                                                 str[current - 1] = _T('\0');
222                                                 if (GetCursorX () != 0)
223                                                 {
224                                                         ConOutPrintf ("\b \b");
225                                                 }
226                                                 else
227                                                 {
228                                                         SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
229                                                         ConOutChar (_T(' '));
230                                                         SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
231                                                 }
232                                         }
233                                         else
234                                         {
235                                                 for (count = current - 1; count < charcount; count++)
236                                                         str[count] = str[count + 1];
237                                                 if (GetCursorX () != 0)
238                                                         SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
239                                                 else
240                                                         SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
241                                                 GetCursorXY (&curx, &cury);
242                                                 ConOutPrintf (_T("%s "), &str[current - 1]);
243                                                 SetCursorXY (curx, cury);
244                                         }
245                                         charcount--;
246                                         current--;
247                                 }
248                                 break;
249
250                         case VK_INSERT:
251                                 /* toggle insert/overstrike mode */
252                                 bInsert ^= TRUE;
253                                 SetCursorType (bInsert, TRUE);
254                                 break;
255
256                         case VK_DELETE:
257                                 /* delete character under cursor */
258                                 if (current != charcount && charcount > 0)
259                                 {
260                                         for (count = current; count < charcount; count++)
261                                                 str[count] = str[count + 1];
262                                         charcount--;
263                                         GetCursorXY (&curx, &cury);
264                                         ConOutPrintf (_T("%s "), &str[current]);
265                                         SetCursorXY (curx, cury);
266                                 }
267                                 break;
268
269                         case VK_HOME:
270                                 /* goto beginning of string */
271                                 if (current != 0)
272                                 {
273                                         SetCursorXY (orgx, orgy);
274                                         current = 0;
275                                 }
276                                 break;
277
278                         case VK_END:
279                                 /* goto end of string */
280                                 if (current != charcount)
281                                 {
282                                         SetCursorXY (orgx, orgy);
283                                         ConOutPrintf (_T("%s"), str);
284                                         current = charcount;
285                                 }
286                                 break;
287
288                         case VK_TAB:
289 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
290                                 /* expand current file name */
291                                 if ((current == charcount) ||
292                                     (current == charcount - 1 &&
293                                      str[current] == _T('"'))) /* only works at end of line*/
294                                 {
295                                         if (wLastKey != VK_TAB)
296                                         {
297                                                 /* if first TAB, complete filename*/
298                                                 CompleteFilename (str, charcount);
299                                                 charcount = _tcslen (str);
300                                                 current = charcount;
301
302                                                 if (current > 0 &&
303                                                     str[current-1] == _T('"'))
304                                                         current--;
305
306                                                 SetCursorXY (orgx, orgy);
307                                                 ConOutPrintf (_T("%s"), str);
308                                                 if ((charcount > (USHORT)(maxx - orgx)) && (orgy == maxy + 1))
309                                                         orgy--;
310
311                                                 /* set cursor position */
312                                                 SetCursorXY ((orgx + current) % maxx,
313                                                              orgy + (orgx + current) / maxx);
314                                         }
315                                         else
316                                         {
317                                                 /*if second TAB, list matches*/
318                                                 if (ShowCompletionMatches (str, charcount))
319                                                 {
320                                                         PrintPrompt ();
321                                                         GetCursorXY (&orgx, &orgy);
322                                                         ConOutPrintf (_T("%s"), str);
323
324                                                         /* set cursor position */
325                                                         SetCursorXY ((orgx + current) % maxx,
326                                                                      orgy + (orgx + current) / maxx);
327                                                 }
328                                                 
329                                         }
330                                 }
331                                 else
332                                 {
333 #ifdef __REACTOS__
334                                         Beep (440, 50);
335 #else
336                                         MessageBeep (-1);
337 #endif
338                                 }
339 #endif
340 #ifdef FEATURE_4NT_FILENAME_COMPLETION
341                                 /* this is not implemented yet */
342                                 if (ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
343                                 {
344                                         /* get previous match */
345
346                                 }
347                                 else
348                                 {
349                                         /* get next match */
350
351                                 }
352 #endif
353                                 break;
354
355                         case VK_RETURN:
356                                 /* end input, return to main */
357 #ifdef FEATURE_HISTORY
358                                 /* add to the history */
359                                 if (str[0])
360                                         History (0, str);
361 #endif
362                                 ConInDummy ();
363                                 ConOutChar (_T('\n'));
364                                 break;
365
366                         case VK_ESCAPE:
367                                 /* clear str  Make this callable! */
368                                 ClearCommandLine (str, maxlen, orgx, orgy);
369                                 current = charcount = 0;
370                                 break;
371
372 #ifdef FEATURE_HISTORY
373                         case VK_F3:
374                                 History_move_to_bottom();
375 #endif
376                         case VK_UP:
377 #ifdef FEATURE_HISTORY
378                                 /* get previous command from buffer */
379                                 ClearCommandLine (str, maxlen, orgx, orgy);
380                                 History (-1, str);
381                                 current = charcount = _tcslen (str);
382                                 ConOutPrintf (_T("%s"), str);
383 #endif
384                                 break;
385
386                         case VK_DOWN:
387 #ifdef FEATURE_HISTORY
388                                 /* get next command from buffer */
389                                 ClearCommandLine (str, maxlen, orgx, orgy);
390                                 History (1, str);
391                                 current = charcount = _tcslen (str);
392                                 ConOutPrintf (_T("%s"), str);
393 #endif
394                                 break;
395
396                         case VK_LEFT:
397                                 /* move cursor left */
398                                 if (current > 0)
399                                 {
400                                         current--;
401                                         if (GetCursorX () == 0)
402                                                 SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
403                                         else
404                                                 SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
405                                 }
406                                 else
407                                 {
408 #ifdef __REACTOS__
409                                         Beep (440, 50);
410 #else
411                                         MessageBeep (-1);
412 #endif
413                                 }
414                                 break;
415
416                         case VK_RIGHT:
417                                 /* move cursor right */
418                                 if (current != charcount)
419                                 {
420                                         current++;
421                                         if (GetCursorX () == maxx - 1)
422                                                 SetCursorXY (0, (SHORT)(GetCursorY () + 1));
423                                         else
424                                                 SetCursorXY ((SHORT)(GetCursorX () + 1), GetCursorY ());
425                                 }
426                                 break;
427
428 #if 0
429
430 #ifdef FEATURE_HISTORY
431                         
432
433                         /*!!!WARNING!!!*/
434                                 /*this will only work as long as the two if statement 
435                                 evaluates the same expression and a break is included
436                                 in each if statement.
437                                 This can be used for any combination using CTRL.
438                                 For other combinations is needed another system*/
439         
440                         case 'K':
441                                 /*add the current command line to the history*/
442                                 if (ir.Event.KeyEvent.dwControlKeyState &
443                                         (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
444                                 {
445                                 
446                                         if (str[0])
447                                                 History(0,str);
448
449                                         ClearCommandLine (str, maxlen, orgx, orgy);
450                                         current = charcount = 0;
451                                         break;
452                                 }
453
454                         case 'D':
455                                 if (ir.Event.KeyEvent.dwControlKeyState &
456                                         (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
457                                 {
458                                         ClearCommandLine (str, maxlen, orgx, orgy);
459                                         History_del_current_entry(str);                                 
460                                         current = charcount = _tcslen (str);
461                                         ConOutPrintf (_T("%s"), str);
462                                         break;
463                                 }
464
465 #endif/*FEATURE_HISTORY*/
466 #endif/*0*/
467
468                         default:
469 #ifdef _UNICODE
470                                 ch = ir.Event.KeyEvent.uChar.UnicodeChar;
471                                 if ((ch >= 32 && ch <= 255) && (charcount != (maxlen - 2)))
472 #else
473                                 ch = ir.Event.KeyEvent.uChar.AsciiChar;
474                                 if (ch >= 32 && (charcount != (maxlen - 2)))
475 #endif /* _UNICODE */
476                                 {
477                                         /* insert character into string... */
478                                         if (bInsert && current != charcount)
479                                         {
480                                                 for (count = charcount; count > current; count--)
481                                                         str[count] = str[count - 1];
482                                                 str[current++] = ch;
483                                                 if (GetCursorX () == maxx - 1)
484                                                 {
485                                                         curx = 0;
486                                                         cury = GetCursorY () + 1;
487                                                 }
488                                                 else
489                                                 {
490                                                         GetCursorXY (&curx, &cury);
491                                                         curx++;
492                                                 }
493                                                 ConOutPrintf (_T("%s"), &str[current - 1]);
494                                                 if ((_tcslen (str) > (USHORT)(maxx - orgx)) && (orgy == maxy + 1))
495                                                         cury--;
496                                                 SetCursorXY (curx, cury);
497                                                 charcount++;
498                                         }
499                                         else
500                                         {
501                                                 if (current == charcount)
502                                                         charcount++;
503                                                 str[current++] = ch;
504                                                 ConOutChar (ch);
505                                         }
506                                         if ((_tcslen (str) > (USHORT)(maxx - orgx)) && (orgy == maxy + 1))
507                                                 orgy--;
508                                 }
509 #if 0
510                                 else
511                                 {
512 #ifdef __REACTOS__
513                                         Beep (440, 100);
514 #else
515                                         MessageBeep (-1);
516 #endif
517                                 }
518 #endif
519                                 break;
520
521                 }
522                 wLastKey = ir.Event.KeyEvent.wVirtualKeyCode;
523         }
524         while (ir.Event.KeyEvent.wVirtualKeyCode != VK_RETURN);
525
526         SetCursorType (bInsert, TRUE);
527 }