xref: /reactos/base/shell/cmd/misc.c (revision 4561998a)
1 /*
2  *  MISC.C - misc. functions.
3  *
4  *
5  *  History:
6  *
7  *    07/12/98 (Rob Lake)
8  *        started
9  *
10  *    07/13/98 (Rob Lake)
11  *        moved functions in here
12  *
13  *    27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
14  *        added config.h include
15  *
16  *    18-Dec-1998 (Eric Kohl)
17  *        Changed split() to accept quoted arguments.
18  *        Removed parse_firstarg().
19  *
20  *    23-Jan-1999 (Eric Kohl)
21  *        Fixed an ugly bug in split(). In rare cases (last character
22  *        of the string is a space) it ignored the NULL character and
23  *        tried to add the following to the argument list.
24  *
25  *    28-Jan-1999 (Eric Kohl)
26  *        FileGetString() seems to be working now.
27  *
28  *    06-Nov-1999 (Eric Kohl)
29  *        Added PagePrompt() and FilePrompt().
30  *
31  *    30-Apr-2005 (Magnus Olsen <magnus@greatlord.com>)
32  *        Remove all hardcoded strings in En.rc
33  */
34 
35 #include "precomp.h"
36 
37 /*
38  * get a character out-of-band and honor Ctrl-Break characters
39  */
40 TCHAR
41 cgetchar (VOID)
42 {
43     HANDLE hInput = GetStdHandle (STD_INPUT_HANDLE);
44     INPUT_RECORD irBuffer;
45     DWORD  dwRead;
46 
47     do
48     {
49         ReadConsoleInput (hInput, &irBuffer, 1, &dwRead);
50         if ((irBuffer.EventType == KEY_EVENT) &&
51             (irBuffer.Event.KeyEvent.bKeyDown != FALSE))
52         {
53             if (irBuffer.Event.KeyEvent.dwControlKeyState &
54                  (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
55             {
56                 if (irBuffer.Event.KeyEvent.wVirtualKeyCode == 'C')
57                 {
58                     bCtrlBreak = TRUE;
59                     break;
60                 }
61             }
62             else if ((irBuffer.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
63                      (irBuffer.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
64                      (irBuffer.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL))
65             {
66                 // Nothing to do
67             }
68             else
69             {
70                 break;
71             }
72         }
73     }
74     while (TRUE);
75 
76 #ifndef _UNICODE
77     return irBuffer.Event.KeyEvent.uChar.AsciiChar;
78 #else
79     return irBuffer.Event.KeyEvent.uChar.UnicodeChar;
80 #endif /* _UNICODE */
81 }
82 
83 /*
84  * Takes a path in and returns it with the correct case of the letters
85  */
86 VOID GetPathCase( TCHAR * Path, TCHAR * OutPath)
87 {
88     UINT i = 0;
89     TCHAR TempPath[MAX_PATH];
90     WIN32_FIND_DATA FindFileData;
91     HANDLE hFind;
92     _tcscpy(TempPath, _T(""));
93     _tcscpy(OutPath, _T(""));
94 
95     for(i = 0; i < _tcslen(Path); i++)
96     {
97         if (Path[i] != _T('\\'))
98         {
99             _tcsncat(TempPath, &Path[i], 1);
100             if (i != _tcslen(Path) - 1)
101                 continue;
102         }
103         /* Handle the base part of the path different.
104            Because if you put it into findfirstfile, it will
105            return your current folder */
106         if (_tcslen(TempPath) == 2 && TempPath[1] == _T(':'))
107         {
108             _tcscat(OutPath, TempPath);
109             _tcscat(OutPath, _T("\\"));
110             _tcscat(TempPath, _T("\\"));
111         }
112         else
113         {
114             hFind = FindFirstFile(TempPath,&FindFileData);
115             if (hFind == INVALID_HANDLE_VALUE)
116             {
117                 _tcscpy(OutPath, Path);
118                 return;
119             }
120             _tcscat(TempPath, _T("\\"));
121             _tcscat(OutPath, FindFileData.cFileName);
122             _tcscat(OutPath, _T("\\"));
123             FindClose(hFind);
124         }
125     }
126 }
127 
128 /*
129  * Check if Ctrl-Break was pressed during the last calls
130  */
131 
132 BOOL CheckCtrlBreak(INT mode)
133 {
134     static BOOL bLeaveAll = FALSE; /* leave all batch files */
135     TCHAR options[4]; /* Yes, No, All */
136     TCHAR c;
137 
138     switch (mode)
139     {
140         case BREAK_OUTOFBATCH:
141             bLeaveAll = FALSE;
142             return FALSE;
143 
144         case BREAK_BATCHFILE:
145         {
146             if (bLeaveAll)
147                 return TRUE;
148 
149             if (!bCtrlBreak)
150                 return FALSE;
151 
152             LoadString(CMD_ModuleHandle, STRING_COPY_OPTION, options, ARRAYSIZE(options));
153 
154             /* we need to be sure the string arrives on the screen! */
155             do
156             {
157                 ConOutResPuts(STRING_CANCEL_BATCH_FILE);
158                 c = _totupper(cgetchar());
159             } while (!(_tcschr(options, c) || c == _T('\3')) || !c);
160 
161             ConOutChar(_T('\n'));
162 
163             if (c == options[1])
164             {
165                 bCtrlBreak = FALSE; /* ignore */
166                 return FALSE;
167             }
168 
169             /* leave all batch files */
170             bLeaveAll = ((c == options[2]) || (c == _T('\3')));
171             break;
172         }
173 
174         case BREAK_INPUT:
175             if (!bCtrlBreak)
176                 return FALSE;
177             break;
178     }
179 
180     /* state processed */
181     return TRUE;
182 }
183 
184 /* add new entry for new argument */
185 BOOL add_entry (LPINT ac, LPTSTR **arg, LPCTSTR entry)
186 {
187     LPTSTR q;
188     LPTSTR *oldarg;
189 
190     q = cmd_alloc ((_tcslen(entry) + 1) * sizeof (TCHAR));
191     if (!q)
192     {
193         WARN("Cannot allocate memory for q!\n");
194         return FALSE;
195     }
196 
197     _tcscpy (q, entry);
198     oldarg = *arg;
199     *arg = cmd_realloc (oldarg, (*ac + 2) * sizeof (LPTSTR));
200     if (!*arg)
201     {
202         WARN("Cannot reallocate memory for arg!\n");
203         *arg = oldarg;
204         cmd_free (q);
205         return FALSE;
206     }
207 
208     /* save new entry */
209     (*arg)[*ac] = q;
210     (*arg)[++(*ac)] = NULL;
211 
212     return TRUE;
213 }
214 
215 static BOOL expand (LPINT ac, LPTSTR **arg, LPCTSTR pattern)
216 {
217     HANDLE hFind;
218     WIN32_FIND_DATA FindData;
219     BOOL ok;
220     LPCTSTR pathend;
221     LPTSTR dirpart, fullname;
222 
223     pathend = _tcsrchr (pattern, _T('\\'));
224     if (NULL != pathend)
225     {
226         dirpart = cmd_alloc((pathend - pattern + 2) * sizeof(TCHAR));
227         if (!dirpart)
228         {
229             WARN("Cannot allocate memory for dirpart!\n");
230             return FALSE;
231         }
232         memcpy(dirpart, pattern, pathend - pattern + 1);
233         dirpart[pathend - pattern + 1] = _T('\0');
234     }
235     else
236     {
237         dirpart = NULL;
238     }
239     hFind = FindFirstFile (pattern, &FindData);
240     if (INVALID_HANDLE_VALUE != hFind)
241     {
242         do
243         {
244             if (NULL != dirpart)
245             {
246                 fullname = cmd_alloc((_tcslen(dirpart) + _tcslen(FindData.cFileName) + 1) * sizeof(TCHAR));
247                 if (!fullname)
248                 {
249                     WARN("Cannot allocate memory for fullname!\n");
250                     ok = FALSE;
251                 }
252                 else
253                 {
254                     _tcscat (_tcscpy (fullname, dirpart), FindData.cFileName);
255                     ok = add_entry(ac, arg, fullname);
256                     cmd_free (fullname);
257                 }
258             }
259             else
260             {
261                 ok = add_entry(ac, arg, FindData.cFileName);
262             }
263         } while (FindNextFile (hFind, &FindData) && ok);
264         FindClose (hFind);
265     }
266     else
267     {
268         ok = add_entry(ac, arg, pattern);
269     }
270 
271     if (NULL != dirpart)
272     {
273         cmd_free (dirpart);
274     }
275 
276     return ok;
277 }
278 
279 /*
280  * split - splits a line up into separate arguments, delimiters
281  *         are spaces and slashes ('/').
282  */
283 LPTSTR *split (LPTSTR s, LPINT args, BOOL expand_wildcards, BOOL handle_plus)
284 {
285     LPTSTR *arg;
286     LPTSTR start;
287     LPTSTR q;
288     INT  ac;
289     INT_PTR  len;
290 
291     arg = cmd_alloc (sizeof (LPTSTR));
292     if (!arg)
293     {
294         WARN("Cannot allocate memory for arg!\n");
295         return NULL;
296     }
297     *arg = NULL;
298 
299     ac = 0;
300     while (*s)
301     {
302         BOOL bQuoted = FALSE;
303 
304         /* skip leading spaces */
305         while (*s && (_istspace(*s) || _istcntrl(*s)))
306             ++s;
307 
308         start = s;
309 
310         /* the first character can be '/' */
311         if (*s == _T('/'))
312             ++s;
313 
314         /* skip to next word delimiter or start of next option */
315         while (_istprint(*s))
316         {
317             /* if quote (") then set bQuoted */
318             bQuoted ^= (*s == _T('\"'));
319 
320             /* Check if we have unquoted text */
321             if (!bQuoted)
322             {
323                 /* check for separators */
324                 if (_istspace(*s) ||
325                     (*s == _T('/')) ||
326                     (handle_plus && (*s == _T('+'))))
327                 {
328                     /* Make length at least one character */
329                     if (s == start) s++;
330                     break;
331                 }
332             }
333 
334             ++s;
335         }
336 
337         /* a word was found */
338         if (s != start)
339         {
340             q = cmd_alloc (((len = s - start) + 1) * sizeof (TCHAR));
341             if (!q)
342             {
343                 WARN("Cannot allocate memory for q!\n");
344                 return NULL;
345             }
346             memcpy (q, start, len * sizeof (TCHAR));
347             q[len] = _T('\0');
348             StripQuotes(q);
349             if (expand_wildcards && (_T('/') != *start) &&
350                 (NULL != _tcschr(q, _T('*')) || NULL != _tcschr(q, _T('?'))))
351             {
352                 if (! expand(&ac, &arg, q))
353                 {
354                     cmd_free (q);
355                     freep (arg);
356                     return NULL;
357                 }
358             }
359             else
360             {
361                 if (! add_entry(&ac, &arg, q))
362                 {
363                     cmd_free (q);
364                     freep (arg);
365                     return NULL;
366                 }
367             }
368             cmd_free (q);
369         }
370     }
371 
372     *args = ac;
373 
374     return arg;
375 }
376 
377 /*
378  * splitspace() is a function which uses JUST spaces as delimiters. split() uses "/" AND spaces.
379  * The way it works is real similar to split(), search the difference ;)
380  * splitspace is needed for commands such as "move" where paths as C:\this/is\allowed/ are allowed
381  */
382 LPTSTR *splitspace (LPTSTR s, LPINT args)
383 {
384     LPTSTR *arg;
385     LPTSTR start;
386     LPTSTR q;
387     INT  ac;
388     INT_PTR  len;
389 
390     arg = cmd_alloc (sizeof (LPTSTR));
391     if (!arg)
392     {
393         WARN("Cannot allocate memory for arg!\n");
394         return NULL;
395     }
396     *arg = NULL;
397 
398     ac = 0;
399     while (*s)
400     {
401         BOOL bQuoted = FALSE;
402 
403         /* skip leading spaces */
404         while (*s && (_istspace (*s) || _istcntrl (*s)))
405             ++s;
406 
407         start = s;
408 
409         /* skip to next word delimiter or start of next option */
410         while (_istprint(*s) && (bQuoted || !_istspace(*s)))
411         {
412             /* if quote (") then set bQuoted */
413             bQuoted ^= (*s == _T('\"'));
414             ++s;
415         }
416 
417         /* a word was found */
418         if (s != start)
419         {
420             q = cmd_alloc (((len = s - start) + 1) * sizeof (TCHAR));
421             if (!q)
422             {
423                 WARN("Cannot allocate memory for q!\n");
424                 return NULL;
425             }
426             memcpy (q, start, len * sizeof (TCHAR));
427             q[len] = _T('\0');
428             StripQuotes(q);
429             if (! add_entry(&ac, &arg, q))
430             {
431                 cmd_free (q);
432                 freep (arg);
433                 return NULL;
434             }
435             cmd_free (q);
436         }
437     }
438 
439     *args = ac;
440 
441     return arg;
442 }
443 
444 /*
445  * freep -- frees memory used for a call to split
446  */
447 VOID freep (LPTSTR *p)
448 {
449     LPTSTR *q;
450 
451     if (!p)
452         return;
453 
454     q = p;
455     while (*q)
456         cmd_free(*q++);
457 
458     cmd_free(p);
459 }
460 
461 LPTSTR _stpcpy (LPTSTR dest, LPCTSTR src)
462 {
463     _tcscpy (dest, src);
464     return (dest + _tcslen (src));
465 }
466 
467 VOID
468 StripQuotes(TCHAR *in)
469 {
470     TCHAR *out = in;
471     for (; *in; in++)
472     {
473         if (*in != _T('"'))
474             *out++ = *in;
475     }
476     *out = _T('\0');
477 }
478 
479 
480 /*
481  * Checks if a path is valid (accessible)
482  */
483 BOOL IsValidPathName (LPCTSTR pszPath)
484 {
485     TCHAR szOldPath[MAX_PATH];
486     BOOL  bResult;
487 
488     GetCurrentDirectory (MAX_PATH, szOldPath);
489     bResult = SetCurrentDirectory (pszPath);
490 
491     SetCurrentDirectory (szOldPath);
492 
493     return bResult;
494 }
495 
496 
497 /*
498  * Checks if a file exists (accessible)
499  */
500 BOOL IsExistingFile (LPCTSTR pszPath)
501 {
502     DWORD attr = GetFileAttributes (pszPath);
503     return (attr != 0xFFFFFFFF && (! (attr & FILE_ATTRIBUTE_DIRECTORY)) );
504 }
505 
506 
507 BOOL IsExistingDirectory (LPCTSTR pszPath)
508 {
509     DWORD attr = GetFileAttributes (pszPath);
510     return (attr != 0xFFFFFFFF && (attr & FILE_ATTRIBUTE_DIRECTORY) );
511 }
512 
513 
514 BOOL FileGetString (HANDLE hFile, LPTSTR lpBuffer, INT nBufferLength)
515 {
516     LPSTR lpString;
517     DWORD  dwRead;
518     INT len = 0;
519 #ifdef _UNICODE
520     lpString = cmd_alloc(nBufferLength);
521 #else
522     lpString = lpBuffer;
523 #endif
524 
525     if (ReadFile(hFile, lpString, nBufferLength - 1, &dwRead, NULL))
526     {
527         /* break at new line*/
528         CHAR *end = memchr(lpString, '\n', dwRead);
529         len = dwRead;
530         if (end)
531         {
532             len = (INT)(end - lpString) + 1;
533             SetFilePointer(hFile, len - dwRead, NULL, FILE_CURRENT);
534         }
535     }
536 
537     if (!len)
538     {
539 #ifdef _UNICODE
540         cmd_free(lpString);
541 #endif
542         return FALSE;
543     }
544 
545     lpString[len++] = '\0';
546 #ifdef _UNICODE
547     MultiByteToWideChar(OutputCodePage, 0, lpString, -1, lpBuffer, len);
548     cmd_free(lpString);
549 #endif
550     return TRUE;
551 }
552 
553 // See r874
554 BOOL __stdcall PagePrompt(PCON_PAGER Pager, DWORD Done, DWORD Total)
555 {
556     SHORT iScreenWidth, iCursorY;
557     INPUT_RECORD ir;
558 
559     ConOutResPuts(STRING_MISC_HELP1);
560 
561     RemoveBreakHandler();
562     ConInDisable();
563 
564     do
565     {
566         ConInKey(&ir);
567     }
568     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
569            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
570            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
571 
572     AddBreakHandler();
573     ConInEnable();
574 
575     /*
576      * Get the screen width, erase the full line where the cursor is,
577      * and move the cursor back to the beginning of the line.
578      */
579     GetScreenSize(&iScreenWidth, NULL);
580     iCursorY = GetCursorY();
581     SetCursorXY(0, iCursorY);
582     while (iScreenWidth-- > 0) // Or call FillConsoleOutputCharacter ?
583         ConOutChar(_T(' '));
584     SetCursorXY(0, iCursorY);
585 
586     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
587         ((ir.Event.KeyEvent.wVirtualKeyCode == _T('C')) &&
588          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
589     {
590         /* We break, output a newline */
591         ConOutChar(_T('\n'));
592 
593         bCtrlBreak = TRUE;
594         return FALSE;
595     }
596 
597     return TRUE;
598 }
599 
600 
601 INT FilePromptYN (UINT resID)
602 {
603     TCHAR szMsg[RC_STRING_MAX_SIZE];
604 //  TCHAR cKey = 0;
605 //  LPTSTR szKeys = _T("yna");
606 
607     TCHAR szIn[10];
608     LPTSTR p;
609 
610     if (resID != 0)
611         ConOutResPrintf (resID);
612 
613     /* preliminary fix */
614     ConInString(szIn, 10);
615     ConOutChar(_T('\n'));
616 
617     _tcsupr (szIn);
618     for (p = szIn; _istspace (*p); p++)
619         ;
620 
621     LoadString(CMD_ModuleHandle, STRING_CHOICE_OPTION, szMsg, ARRAYSIZE(szMsg));
622 
623     if (_tcsncmp(p, &szMsg[0], 1) == 0)
624         return PROMPT_YES;
625     else if (_tcsncmp(p, &szMsg[1], 1) == 0)
626         return PROMPT_NO;
627 #if 0
628     else if (*p == _T('\03'))
629         return PROMPT_BREAK;
630 #endif
631 
632     return PROMPT_NO;
633 
634     /* unfinished solution */
635 #if 0
636     RemoveBreakHandler();
637     ConInDisable();
638 
639     do
640     {
641         ConInKey (&ir);
642         cKey = _totlower (ir.Event.KeyEvent.uChar.AsciiChar);
643         if (_tcschr (szKeys, cKey[0]) == NULL)
644             cKey = 0;
645 
646 
647     }
648     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
649            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
650            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
651 
652     AddBreakHandler();
653     ConInEnable();
654 
655     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
656         ((ir.Event.KeyEvent.wVirtualKeyCode == 'C') &&
657          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
658         return PROMPT_BREAK;
659 
660     return PROMPT_YES;
661 #endif
662 }
663 
664 
665 INT FilePromptYNA (UINT resID)
666 {
667     TCHAR szMsg[RC_STRING_MAX_SIZE];
668 //  TCHAR cKey = 0;
669 //  LPTSTR szKeys = _T("yna");
670 
671     TCHAR szIn[10];
672     LPTSTR p;
673 
674     if (resID != 0)
675         ConOutResPrintf (resID);
676 
677     /* preliminary fix */
678     ConInString(szIn, 10);
679     ConOutChar(_T('\n'));
680 
681     _tcsupr (szIn);
682     for (p = szIn; _istspace (*p); p++)
683         ;
684 
685     LoadString( CMD_ModuleHandle, STRING_COPY_OPTION, szMsg, ARRAYSIZE(szMsg));
686 
687     if (_tcsncmp(p, &szMsg[0], 1) == 0)
688         return PROMPT_YES;
689     else if (_tcsncmp(p, &szMsg[1], 1) == 0)
690         return PROMPT_NO;
691     else if (_tcsncmp(p, &szMsg[2], 1) == 0)
692         return PROMPT_ALL;
693 #if 0
694     else if (*p == _T('\03'))
695         return PROMPT_BREAK;
696 #endif
697 
698     return PROMPT_NO;
699 
700     /* unfinished solution */
701 #if 0
702     RemoveBreakHandler();
703     ConInDisable();
704 
705     do
706     {
707         ConInKey (&ir);
708         cKey = _totlower (ir.Event.KeyEvent.uChar.AsciiChar);
709         if (_tcschr (szKeys, cKey[0]) == NULL)
710             cKey = 0;
711     }
712     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
713            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
714            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
715 
716     AddBreakHandler();
717     ConInEnable();
718 
719     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
720         ((ir.Event.KeyEvent.wVirtualKeyCode == _T('C')) &&
721          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
722         return PROMPT_BREAK;
723 
724     return PROMPT_YES;
725 #endif
726 }
727 
728 /* EOF */
729