xref: /reactos/base/shell/cmd/cmdinput.c (revision 22561d50)
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 preempted 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 carriage 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  * See https://technet.microsoft.com/en-us/library/cc978715.aspx
107  * and https://technet.microsoft.com/en-us/library/cc940805.aspx
108  * to know the differences between those two settings.
109  * Values 0x00, 0x0D (carriage return) and >= 0x20 (space) disable completion.
110  */
111 TCHAR AutoCompletionChar = 0x20; // Disabled by default
112 TCHAR PathCompletionChar = 0x20; // Disabled by default
113 
114 
115 SHORT maxx;
116 SHORT maxy;
117 
118 /*
119  * global command line insert/overwrite flag
120  */
121 static BOOL bInsert = TRUE;
122 
123 
124 static VOID
ClearCommandLine(LPTSTR str,INT maxlen,SHORT orgx,SHORT orgy)125 ClearCommandLine(LPTSTR str, INT maxlen, SHORT orgx, SHORT orgy)
126 {
127     INT count;
128 
129     SetCursorXY (orgx, orgy);
130     for (count = 0; count < (INT)_tcslen (str); count++)
131         ConOutChar (_T(' '));
132     _tcsnset (str, _T('\0'), maxlen);
133     SetCursorXY (orgx, orgy);
134 }
135 
136 
137 /* read in a command line */
ReadCommand(LPTSTR str,INT maxlen)138 BOOL ReadCommand(LPTSTR str, INT maxlen)
139 {
140     CONSOLE_SCREEN_BUFFER_INFO csbi;
141     SHORT orgx;     /* origin x/y */
142     SHORT orgy;
143     SHORT curx;     /*current x/y cursor position*/
144     SHORT cury;
145     SIZE_T tempscreen;
146     INT   count;    /*used in some for loops*/
147     INT   current = 0;  /*the position of the cursor in the string (str)*/
148     INT   charcount = 0;/*chars in the string (str)*/
149     INPUT_RECORD ir;
150     DWORD dwControlKeyState;
151 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
152     WORD   wLastKey = 0;
153 #endif
154     TCHAR  ch;
155     BOOL bReturn = FALSE;
156     BOOL bCharInput;
157 #ifdef FEATURE_4NT_FILENAME_COMPLETION
158     TCHAR szPath[MAX_PATH];
159 #endif
160 #ifdef FEATURE_HISTORY
161     //BOOL bContinue=FALSE;/*is TRUE the second case will not be executed*/
162     TCHAR PreviousChar;
163 #endif
164 
165     if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
166     {
167         /* No console */
168         HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
169         DWORD dwRead;
170         CHAR chr;
171         do
172         {
173             if (!ReadFile(hStdin, &chr, 1, &dwRead, NULL) || !dwRead)
174                 return FALSE;
175 #ifdef _UNICODE
176             MultiByteToWideChar(InputCodePage, 0, &chr, 1, &str[charcount++], 1);
177 #endif
178         } while (chr != '\n' && charcount < maxlen);
179         str[charcount] = _T('\0');
180         return TRUE;
181     }
182 
183     /* get screen size */
184     maxx = csbi.dwSize.X;
185     maxy = csbi.dwSize.Y;
186 
187     curx = orgx = csbi.dwCursorPosition.X;
188     cury = orgy = csbi.dwCursorPosition.Y;
189 
190     memset (str, 0, maxlen * sizeof (TCHAR));
191 
192     SetCursorType (bInsert, TRUE);
193 
194     do
195     {
196         bReturn = FALSE;
197         ConInKey (&ir);
198 
199         dwControlKeyState = ir.Event.KeyEvent.dwControlKeyState;
200 
201         if (dwControlKeyState &
202             (RIGHT_ALT_PRESSED |LEFT_ALT_PRESSED|
203              RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) )
204         {
205             switch (ir.Event.KeyEvent.wVirtualKeyCode)
206             {
207 #ifdef FEATURE_HISTORY
208                 case _T('K'):
209                     /* add the current command line to the history */
210                     if (dwControlKeyState &
211                         (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
212                     {
213                         if (str[0])
214                             History(0,str);
215 
216                         ClearCommandLine (str, maxlen, orgx, orgy);
217                         current = charcount = 0;
218                         curx = orgx;
219                         cury = orgy;
220                         //bContinue=TRUE;
221                     }
222                     break;
223 
224                 case _T('D'):
225                     /* delete current history entry */
226                     if (dwControlKeyState &
227                         (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
228                     {
229                         ClearCommandLine (str, maxlen, orgx, orgy);
230                         History_del_current_entry(str);
231                         current = charcount = _tcslen (str);
232                         ConOutPrintf (_T("%s"), str);
233                         GetCursorXY (&curx, &cury);
234                         //bContinue=TRUE;
235                     }
236                     break;
237 #endif /*FEATURE_HISTORY*/
238 
239                 case _T('M'):
240                     /* ^M does the same as return */
241                     if (dwControlKeyState &
242                         (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED))
243                     {
244                         /* end input, return to main */
245 #ifdef FEATURE_HISTORY
246                         /* add to the history */
247                         if (str[0])
248                             History(0, str);
249 #endif /*FEATURE_HISTORY*/
250                         str[charcount++] = _T('\n');
251                         str[charcount] = _T('\0');
252                         ConOutChar (_T('\n'));
253                         bReturn = TRUE;
254                     }
255                     break;
256 
257                 case _T('H'): /* ^H does the same as VK_BACK */
258                     if (dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
259                     {
260                         bCharInput = FALSE;
261                         goto DoBackSpace;
262                     }
263                     break;
264             }
265         }
266 
267         bCharInput = FALSE;
268 
269         switch (ir.Event.KeyEvent.wVirtualKeyCode)
270         {
271             case VK_BACK:
272             DoBackSpace:
273                 /* <BACKSPACE> - delete character to left of cursor */
274                 if (current > 0 && charcount > 0)
275                 {
276                     if (current == charcount)
277                     {
278                         /* if at end of line */
279                         str[current - 1] = _T('\0');
280                         if (GetCursorX () != 0)
281                         {
282                             ConOutPrintf (_T("\b \b"));
283                             curx--;
284                         }
285                         else
286                         {
287                             SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
288                             ConOutChar (_T(' '));
289                             SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
290                             cury--;
291                             curx = maxx - 1;
292                         }
293                     }
294                     else
295                     {
296                         for (count = current - 1; count < charcount; count++)
297                             str[count] = str[count + 1];
298                         if (GetCursorX () != 0)
299                         {
300                             SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
301                             curx--;
302                         }
303                         else
304                         {
305                             SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
306                             cury--;
307                             curx = maxx - 1;
308                         }
309                         GetCursorXY (&curx, &cury);
310                         ConOutPrintf (_T("%s "), &str[current - 1]);
311                         SetCursorXY (curx, cury);
312                     }
313                     charcount--;
314                     current--;
315                 }
316                 break;
317 
318             case VK_INSERT:
319                 /* toggle insert/overstrike mode */
320                 bInsert ^= TRUE;
321                 SetCursorType (bInsert, TRUE);
322                 break;
323 
324             case VK_DELETE:
325                 /* delete character under cursor */
326                 if (current != charcount && charcount > 0)
327                 {
328                     for (count = current; count < charcount; count++)
329                         str[count] = str[count + 1];
330                     charcount--;
331                     GetCursorXY (&curx, &cury);
332                     ConOutPrintf (_T("%s "), &str[current]);
333                     SetCursorXY (curx, cury);
334                 }
335                 break;
336 
337             case VK_HOME:
338                 /* goto beginning of string */
339                 if (current != 0)
340                 {
341                     SetCursorXY (orgx, orgy);
342                     curx = orgx;
343                     cury = orgy;
344                     current = 0;
345                 }
346                 break;
347 
348             case VK_END:
349                 /* goto end of string */
350                 if (current != charcount)
351                 {
352                     SetCursorXY (orgx, orgy);
353                     ConOutPrintf (_T("%s"), str);
354                     GetCursorXY (&curx, &cury);
355                     current = charcount;
356                 }
357                 break;
358 
359             case VK_TAB:
360 #ifdef FEATURE_UNIX_FILENAME_COMPLETION
361                 /* expand current file name */
362                 if ((current == charcount) ||
363                     (current == charcount - 1 &&
364                      str[current] == _T('"'))) /* only works at end of line*/
365                 {
366                     if (wLastKey != VK_TAB)
367                     {
368                         /* if first TAB, complete filename*/
369                         tempscreen = charcount;
370                         CompleteFilename (str, charcount);
371                         charcount = _tcslen (str);
372                         current = charcount;
373 
374                         SetCursorXY (orgx, orgy);
375                         ConOutPrintf (_T("%s"), str);
376 
377                         if (tempscreen > charcount)
378                         {
379                             GetCursorXY (&curx, &cury);
380                             for (count = tempscreen - charcount; count--; )
381                                 ConOutChar (_T(' '));
382                             SetCursorXY (curx, cury);
383                         }
384                         else
385                         {
386                             if (((charcount + orgx) / maxx) + orgy > maxy - 1)
387                                 orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
388                         }
389 
390                         /* set cursor position */
391                         SetCursorXY ((orgx + current) % maxx,
392                                  orgy + (orgx + current) / maxx);
393                         GetCursorXY (&curx, &cury);
394                     }
395                     else
396                     {
397                         /*if second TAB, list matches*/
398                         if (ShowCompletionMatches (str, charcount))
399                         {
400                             PrintPrompt();
401                             GetCursorXY(&orgx, &orgy);
402                             ConOutPrintf(_T("%s"), str);
403 
404                             /* set cursor position */
405                             SetCursorXY((orgx + current) % maxx,
406                                          orgy + (orgx + current) / maxx);
407                             GetCursorXY(&curx, &cury);
408                         }
409 
410                     }
411                 }
412                 else
413                 {
414                     MessageBeep(-1);
415                 }
416 #endif
417 #ifdef FEATURE_4NT_FILENAME_COMPLETION
418                 /* used to later see if we went down to the next line */
419                 tempscreen = charcount;
420                 szPath[0]=_T('\0');
421 
422                 /* str is the whole things that is on the current line
423                    that is and and out.  arg 2 is weather it goes back
424                     one file or forward one file */
425                 CompleteFilename(str, !(ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED), szPath, current);
426                 /* Attempt to clear the line */
427                 ClearCommandLine (str, maxlen, orgx, orgy);
428                 curx = orgx;
429                 cury = orgy;
430                 current = charcount = 0;
431 
432                 /* Everything is deleted, lets add it back in */
433                 _tcscpy(str,szPath);
434 
435                 /* Figure out where cusor is going to be after we print it */
436                 charcount = _tcslen(str);
437                 current = charcount;
438 
439                 SetCursorXY(orgx, orgy);
440                 /* Print out what we have now */
441                 ConOutPrintf(_T("%s"), str);
442 
443                 /* Move cursor accordingly */
444                 if (tempscreen > charcount)
445                 {
446                     GetCursorXY(&curx, &cury);
447                     for(count = tempscreen - charcount; count--; )
448                         ConOutChar(_T(' '));
449                     SetCursorXY(curx, cury);
450                 }
451                 else
452                 {
453                     if (((charcount + orgx) / maxx) + orgy > maxy - 1)
454                         orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
455                 }
456                 SetCursorXY((short)(((int)orgx + current) % maxx), (short)((int)orgy + ((int)orgx + current) / maxx));
457                 GetCursorXY(&curx, &cury);
458 #endif
459                 break;
460 
461             case _T('C'):
462                 if ((ir.Event.KeyEvent.dwControlKeyState &
463                     (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)))
464                 {
465                     /* Ignore the Ctrl-C key event if it has already been handled */
466                     if (!bCtrlBreak)
467                         break;
468 
469                     /*
470                      * Fully print the entered string
471                      * so the command prompt would not overwrite it.
472                      */
473                     SetCursorXY(orgx, orgy);
474                     ConOutPrintf(_T("%s"), str);
475 
476                     /*
477                      * A Ctrl-C. Do not clear the command line,
478                      * but return an empty string in str.
479                      */
480                     str[0] = _T('\0');
481                     curx = orgx;
482                     cury = orgy;
483                     current = charcount = 0;
484                     bReturn = TRUE;
485                 }
486                 else
487                 {
488                     /* Just a normal 'C' character */
489                     bCharInput = TRUE;
490                 }
491                 break;
492 
493             case VK_RETURN:
494                 /* end input, return to main */
495 #ifdef FEATURE_HISTORY
496                 /* add to the history */
497                 if (str[0])
498                     History(0, str);
499 #endif
500                 str[charcount++] = _T('\n');
501                 str[charcount] = _T('\0');
502                 ConOutChar(_T('\n'));
503                 bReturn = TRUE;
504                 break;
505 
506             case VK_ESCAPE:
507                 /* clear str  Make this callable! */
508                 ClearCommandLine (str, maxlen, orgx, orgy);
509                 curx = orgx;
510                 cury = orgy;
511                 current = charcount = 0;
512                 break;
513 
514 #ifdef FEATURE_HISTORY
515             case VK_F3:
516                 History_move_to_bottom();
517 #endif
518             case VK_UP:
519 #ifdef FEATURE_HISTORY
520                 /* get previous command from buffer */
521                 ClearCommandLine (str, maxlen, orgx, orgy);
522                 History(-1, str);
523                 current = charcount = _tcslen (str);
524                 if (((charcount + orgx) / maxx) + orgy > maxy - 1)
525                     orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
526                 ConOutPrintf (_T("%s"), str);
527                 GetCursorXY (&curx, &cury);
528 #endif
529                 break;
530 
531             case VK_DOWN:
532 #ifdef FEATURE_HISTORY
533                 /* get next command from buffer */
534                 ClearCommandLine (str, maxlen, orgx, orgy);
535                 History(1, str);
536                 current = charcount = _tcslen (str);
537                 if (((charcount + orgx) / maxx) + orgy > maxy - 1)
538                     orgy += maxy - ((charcount + orgx) / maxx + orgy + 1);
539                 ConOutPrintf (_T("%s"), str);
540                 GetCursorXY (&curx, &cury);
541 #endif
542                 break;
543 
544             case VK_LEFT:
545                 if (dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
546                 {
547                     /* move cursor to the previous word */
548                     if (current > 0)
549                     {
550                         while (current > 0 && str[current - 1] == _T(' '))
551                         {
552                             current--;
553                             if (curx == 0)
554                             {
555                                 cury--;
556                                 curx = maxx -1;
557                             }
558                             else
559                             {
560                                 curx--;
561                             }
562                         }
563 
564                         while (current > 0 && str[current -1] != _T(' '))
565                         {
566                             current--;
567                             if (curx == 0)
568                             {
569                                 cury--;
570                                 curx = maxx -1;
571                             }
572                             else
573                             {
574                                 curx--;
575                             }
576                         }
577 
578                         SetCursorXY(curx, cury);
579                     }
580                 }
581                 else
582                 {
583                     /* move cursor left */
584                     if (current > 0)
585                     {
586                         current--;
587                         if (GetCursorX () == 0)
588                         {
589                             SetCursorXY ((SHORT)(maxx - 1), (SHORT)(GetCursorY () - 1));
590                             curx = maxx - 1;
591                             cury--;
592                         }
593                         else
594                         {
595                             SetCursorXY ((SHORT)(GetCursorX () - 1), GetCursorY ());
596                             curx--;
597                         }
598                     }
599                     else
600                     {
601                         MessageBeep (-1);
602                     }
603                 }
604                 break;
605 
606             case VK_RIGHT:
607                 if (dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
608                 {
609                     /* move cursor to the next word */
610                     if (current != charcount)
611                     {
612                         while (current != charcount && str[current] != _T(' '))
613                         {
614                             current++;
615                             if (curx == maxx - 1)
616                             {
617                                 cury++;
618                                 curx = 0;
619                             }
620                             else
621                             {
622                                 curx++;
623                             }
624                         }
625 
626                         while (current != charcount && str[current] == _T(' '))
627                         {
628                             current++;
629                             if (curx == maxx - 1)
630                             {
631                                 cury++;
632                                 curx = 0;
633                             }
634                             else
635                             {
636                                 curx++;
637                             }
638                         }
639 
640                         SetCursorXY(curx, cury);
641                     }
642                 }
643                 else
644                 {
645                     /* move cursor right */
646                     if (current != charcount)
647                     {
648                         current++;
649                         if (GetCursorX () == maxx - 1)
650                         {
651                             SetCursorXY (0, (SHORT)(GetCursorY () + 1));
652                             curx = 0;
653                             cury++;
654                         }
655                         else
656                         {
657                             SetCursorXY ((SHORT)(GetCursorX () + 1), GetCursorY ());
658                             curx++;
659                         }
660                     }
661 #ifdef FEATURE_HISTORY
662                     else
663                     {
664                         LPCTSTR last = PeekHistory(-1);
665                         if (last && charcount < (INT)_tcslen (last))
666                         {
667                             PreviousChar = last[current];
668                             ConOutChar(PreviousChar);
669                             GetCursorXY(&curx, &cury);
670                             str[current++] = PreviousChar;
671                             charcount++;
672                         }
673                     }
674 #endif
675                 }
676                 break;
677 
678             default:
679                 /* This input is just a normal char */
680                 bCharInput = TRUE;
681 
682             }
683 #ifdef _UNICODE
684             ch = ir.Event.KeyEvent.uChar.UnicodeChar;
685             if (ch >= 32 && (charcount != (maxlen - 2)) && bCharInput)
686 #else
687             ch = ir.Event.KeyEvent.uChar.AsciiChar;
688             if ((UCHAR)ch >= 32 && (charcount != (maxlen - 2)) && bCharInput)
689 #endif /* _UNICODE */
690             {
691                 /* insert character into string... */
692                 if (bInsert && current != charcount)
693                 {
694                     /* If this character insertion will cause screen scrolling,
695                      * adjust the saved origin of the command prompt. */
696                     tempscreen = _tcslen(str + current) + curx;
697                     if ((tempscreen % maxx) == (maxx - 1) &&
698                         (tempscreen / maxx) + cury == (maxy - 1))
699                     {
700                         orgy--;
701                         cury--;
702                     }
703 
704                     for (count = charcount; count > current; count--)
705                         str[count] = str[count - 1];
706                     str[current++] = ch;
707                     if (curx == maxx - 1)
708                         curx = 0, cury++;
709                     else
710                         curx++;
711                     ConOutPrintf (_T("%s"), &str[current - 1]);
712                     SetCursorXY (curx, cury);
713                     charcount++;
714                 }
715                 else
716                 {
717                     if (current == charcount)
718                         charcount++;
719                     str[current++] = ch;
720                     if (GetCursorX () == maxx - 1 && GetCursorY () == maxy - 1)
721                         orgy--, cury--;
722                     if (GetCursorX () == maxx - 1)
723                         curx = 0, cury++;
724                     else
725                         curx++;
726                     ConOutChar (ch);
727                 }
728             }
729 
730         //wLastKey = ir.Event.KeyEvent.wVirtualKeyCode;
731     }
732     while (!bReturn);
733 
734     SetCursorType (bInsert, TRUE);
735 
736 #ifdef FEATURE_ALIASES
737     /* expand all aliases */
738     ExpandAlias (str, maxlen);
739 #endif /* FEATURE_ALIAS */
740     return TRUE;
741 }
742