xref: /reactos/base/shell/cmd/misc.c (revision 075894bc)
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
cgetchar(VOID)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  */
GetPathCase(IN LPCTSTR Path,OUT LPTSTR OutPath)86 VOID GetPathCase(IN LPCTSTR Path, OUT LPTSTR OutPath)
87 {
88     SIZE_T i;
89     SIZE_T cchPath = _tcslen(Path);
90     TCHAR TempPath[MAX_PATH];
91     LPTSTR pchTemp = TempPath;
92     LPTSTR pchTempEnd = TempPath + _countof(TempPath);
93     WIN32_FIND_DATA FindFileData;
94     HANDLE hFind;
95 
96     *pchTemp = OutPath[0] = 0;
97 
98     for (i = 0; i < cchPath; ++i)
99     {
100         if (pchTemp + 1 >= pchTempEnd)
101         {
102             // On failure, copy the original path for an error message
103             StringCchCopy(OutPath, MAX_PATH, Path);
104             return;
105         }
106 
107         if (Path[i] != _T('\\') && Path[i] != _T('/'))
108         {
109             *pchTemp++ = Path[i];
110             *pchTemp = 0;
111             if (i != cchPath - 1)
112                 continue;
113         }
114 
115         /* Handle the base part of the path different.
116            Because if you put it into findfirstfile, it will
117            return your current folder */
118         if (TempPath[0] && TempPath[1] == _T(':') && !TempPath[2]) /* "C:", "D:" etc. */
119         {
120             StringCchCat(OutPath, MAX_PATH, TempPath);
121             StringCchCat(OutPath, MAX_PATH, _T("\\"));
122             StringCchCat(TempPath, _countof(TempPath), _T("\\"));
123         }
124         else
125         {
126             hFind = FindFirstFile(TempPath, &FindFileData);
127             if (hFind == INVALID_HANDLE_VALUE)
128             {
129                 StringCchCopy(OutPath, MAX_PATH, Path);
130                 return;
131             }
132             FindClose(hFind);
133             StringCchCat(OutPath, MAX_PATH, _T("\\"));
134             StringCchCat(OutPath, MAX_PATH, FindFileData.cFileName);
135             StringCchCat(OutPath, MAX_PATH, _T("\\"));
136             StringCchCopy(TempPath, _countof(TempPath), OutPath);
137         }
138         pchTemp = TempPath + _tcslen(TempPath);
139     }
140 }
141 
142 /*
143  * Check if Ctrl-Break was pressed during the last calls
144  */
145 
CheckCtrlBreak(INT mode)146 BOOL CheckCtrlBreak(INT mode)
147 {
148     static BOOL bLeaveAll = FALSE; /* leave all batch files */
149     TCHAR options[4]; /* Yes, No, All */
150     TCHAR c;
151 
152     switch (mode)
153     {
154         case BREAK_OUTOFBATCH:
155             bLeaveAll = FALSE;
156             return FALSE;
157 
158         case BREAK_BATCHFILE:
159         {
160             if (bLeaveAll)
161                 return TRUE;
162 
163             if (!bCtrlBreak)
164                 return FALSE;
165 
166             LoadString(CMD_ModuleHandle, STRING_COPY_OPTION, options, ARRAYSIZE(options));
167 
168             ConOutResPuts(STRING_CANCEL_BATCH_FILE);
169             do
170             {
171                 c = _totupper(cgetchar());
172             } while (!(_tcschr(options, c) || c == _T('\3')) || !c);
173 
174             ConOutChar(_T('\n'));
175 
176             if (c == options[1])
177             {
178                 bCtrlBreak = FALSE; /* ignore */
179                 return FALSE;
180             }
181 
182             /* leave all batch files */
183             bLeaveAll = ((c == options[2]) || (c == _T('\3')));
184             break;
185         }
186 
187         case BREAK_INPUT:
188             if (!bCtrlBreak)
189                 return FALSE;
190             break;
191     }
192 
193     /* state processed */
194     return TRUE;
195 }
196 
197 /* add new entry for new argument */
add_entry(LPINT ac,LPTSTR ** arg,LPCTSTR entry)198 BOOL add_entry (LPINT ac, LPTSTR **arg, LPCTSTR entry)
199 {
200     LPTSTR q;
201     LPTSTR *oldarg;
202 
203     q = cmd_alloc ((_tcslen(entry) + 1) * sizeof (TCHAR));
204     if (!q)
205     {
206         WARN("Cannot allocate memory for q!\n");
207         return FALSE;
208     }
209 
210     _tcscpy (q, entry);
211     oldarg = *arg;
212     *arg = cmd_realloc (oldarg, (*ac + 2) * sizeof (LPTSTR));
213     if (!*arg)
214     {
215         WARN("Cannot reallocate memory for arg!\n");
216         *arg = oldarg;
217         cmd_free (q);
218         return FALSE;
219     }
220 
221     /* save new entry */
222     (*arg)[*ac] = q;
223     (*arg)[++(*ac)] = NULL;
224 
225     return TRUE;
226 }
227 
expand(LPINT ac,LPTSTR ** arg,LPCTSTR pattern)228 static BOOL expand (LPINT ac, LPTSTR **arg, LPCTSTR pattern)
229 {
230     HANDLE hFind;
231     WIN32_FIND_DATA FindData;
232     BOOL ok;
233     LPCTSTR pathend;
234     LPTSTR dirpart, fullname;
235 
236     pathend = _tcsrchr (pattern, _T('\\'));
237     if (NULL != pathend)
238     {
239         dirpart = cmd_alloc((pathend - pattern + 2) * sizeof(TCHAR));
240         if (!dirpart)
241         {
242             WARN("Cannot allocate memory for dirpart!\n");
243             return FALSE;
244         }
245         memcpy(dirpart, pattern, pathend - pattern + 1);
246         dirpart[pathend - pattern + 1] = _T('\0');
247     }
248     else
249     {
250         dirpart = NULL;
251     }
252     hFind = FindFirstFile (pattern, &FindData);
253     if (INVALID_HANDLE_VALUE != hFind)
254     {
255         do
256         {
257             if (NULL != dirpart)
258             {
259                 fullname = cmd_alloc((_tcslen(dirpart) + _tcslen(FindData.cFileName) + 1) * sizeof(TCHAR));
260                 if (!fullname)
261                 {
262                     WARN("Cannot allocate memory for fullname!\n");
263                     ok = FALSE;
264                 }
265                 else
266                 {
267                     _tcscat (_tcscpy (fullname, dirpart), FindData.cFileName);
268                     ok = add_entry(ac, arg, fullname);
269                     cmd_free (fullname);
270                 }
271             }
272             else
273             {
274                 ok = add_entry(ac, arg, FindData.cFileName);
275             }
276         } while (FindNextFile (hFind, &FindData) && ok);
277         FindClose (hFind);
278     }
279     else
280     {
281         ok = add_entry(ac, arg, pattern);
282     }
283 
284     if (NULL != dirpart)
285     {
286         cmd_free (dirpart);
287     }
288 
289     return ok;
290 }
291 
292 /*
293  * split - splits a line up into separate arguments, delimiters
294  *         are spaces and slashes ('/').
295  */
split(LPTSTR s,LPINT args,BOOL expand_wildcards,BOOL handle_plus)296 LPTSTR *split (LPTSTR s, LPINT args, BOOL expand_wildcards, BOOL handle_plus)
297 {
298     LPTSTR *arg;
299     LPTSTR start;
300     LPTSTR q;
301     INT  ac;
302     INT_PTR  len;
303 
304     arg = cmd_alloc (sizeof (LPTSTR));
305     if (!arg)
306     {
307         WARN("Cannot allocate memory for arg!\n");
308         return NULL;
309     }
310     *arg = NULL;
311 
312     ac = 0;
313     while (*s)
314     {
315         BOOL bQuoted = FALSE;
316 
317         /* skip leading spaces */
318         while (*s && (_istspace(*s) || _istcntrl(*s)))
319             ++s;
320 
321         start = s;
322 
323         /* the first character can be '/' */
324         if (*s == _T('/'))
325             ++s;
326 
327         /* skip to next word delimiter or start of next option */
328         while (_istprint(*s))
329         {
330             /* if quote (") then set bQuoted */
331             bQuoted ^= (*s == _T('\"'));
332 
333             /* Check if we have unquoted text */
334             if (!bQuoted)
335             {
336                 /* check for separators */
337                 if (_istspace(*s) ||
338                     (*s == _T('/')) ||
339                     (handle_plus && (*s == _T('+'))))
340                 {
341                     /* Make length at least one character */
342                     if (s == start) s++;
343                     break;
344                 }
345             }
346 
347             ++s;
348         }
349 
350         /* a word was found */
351         if (s != start)
352         {
353             q = cmd_alloc (((len = s - start) + 1) * sizeof (TCHAR));
354             if (!q)
355             {
356                 WARN("Cannot allocate memory for q!\n");
357                 return NULL;
358             }
359             memcpy (q, start, len * sizeof (TCHAR));
360             q[len] = _T('\0');
361             StripQuotes(q);
362             if (expand_wildcards && (_T('/') != *start) &&
363                 (NULL != _tcschr(q, _T('*')) || NULL != _tcschr(q, _T('?'))))
364             {
365                 if (! expand(&ac, &arg, q))
366                 {
367                     cmd_free (q);
368                     freep (arg);
369                     return NULL;
370                 }
371             }
372             else
373             {
374                 if (! add_entry(&ac, &arg, q))
375                 {
376                     cmd_free (q);
377                     freep (arg);
378                     return NULL;
379                 }
380             }
381             cmd_free (q);
382         }
383     }
384 
385     *args = ac;
386 
387     return arg;
388 }
389 
390 /*
391  * splitspace() is a function which uses JUST spaces as delimiters. split() uses "/" AND spaces.
392  * The way it works is real similar to split(), search the difference ;)
393  * splitspace is needed for commands such as "move" where paths as C:\this/is\allowed/ are allowed
394  */
splitspace(LPTSTR s,LPINT args)395 LPTSTR *splitspace (LPTSTR s, LPINT args)
396 {
397     LPTSTR *arg;
398     LPTSTR start;
399     LPTSTR q;
400     INT  ac;
401     INT_PTR  len;
402 
403     arg = cmd_alloc (sizeof (LPTSTR));
404     if (!arg)
405     {
406         WARN("Cannot allocate memory for arg!\n");
407         return NULL;
408     }
409     *arg = NULL;
410 
411     ac = 0;
412     while (*s)
413     {
414         BOOL bQuoted = FALSE;
415 
416         /* skip leading spaces */
417         while (*s && (_istspace (*s) || _istcntrl (*s)))
418             ++s;
419 
420         start = s;
421 
422         /* skip to next word delimiter or start of next option */
423         while (_istprint(*s) && (bQuoted || !_istspace(*s)))
424         {
425             /* if quote (") then set bQuoted */
426             bQuoted ^= (*s == _T('\"'));
427             ++s;
428         }
429 
430         /* a word was found */
431         if (s != start)
432         {
433             q = cmd_alloc (((len = s - start) + 1) * sizeof (TCHAR));
434             if (!q)
435             {
436                 WARN("Cannot allocate memory for q!\n");
437                 return NULL;
438             }
439             memcpy (q, start, len * sizeof (TCHAR));
440             q[len] = _T('\0');
441             StripQuotes(q);
442             if (! add_entry(&ac, &arg, q))
443             {
444                 cmd_free (q);
445                 freep (arg);
446                 return NULL;
447             }
448             cmd_free (q);
449         }
450     }
451 
452     *args = ac;
453 
454     return arg;
455 }
456 
457 /*
458  * freep -- frees memory used for a call to split
459  */
freep(LPTSTR * p)460 VOID freep (LPTSTR *p)
461 {
462     LPTSTR *q;
463 
464     if (!p)
465         return;
466 
467     q = p;
468     while (*q)
469         cmd_free(*q++);
470 
471     cmd_free(p);
472 }
473 
_stpcpy(LPTSTR dest,LPCTSTR src)474 LPTSTR _stpcpy (LPTSTR dest, LPCTSTR src)
475 {
476     _tcscpy (dest, src);
477     return (dest + _tcslen (src));
478 }
479 
480 VOID
StripQuotes(TCHAR * in)481 StripQuotes(TCHAR *in)
482 {
483     TCHAR *out = in;
484     for (; *in; in++)
485     {
486         if (*in != _T('"'))
487             *out++ = *in;
488     }
489     *out = _T('\0');
490 }
491 
492 
493 /*
494  * Checks if a path is valid (is accessible)
495  */
IsValidPathName(IN LPCTSTR pszPath)496 BOOL IsValidPathName(IN LPCTSTR pszPath)
497 {
498     BOOL  bResult;
499     TCHAR szOldPath[MAX_PATH];
500 
501     GetCurrentDirectory(ARRAYSIZE(szOldPath), szOldPath);
502     bResult = SetCurrentDirectory(pszPath);
503 
504     SetCurrentDirectory(szOldPath);
505 
506     return bResult;
507 }
508 
509 /*
510  * Checks if a file exists (is accessible)
511  */
IsExistingFile(IN LPCTSTR pszPath)512 BOOL IsExistingFile(IN LPCTSTR pszPath)
513 {
514     DWORD attr = GetFileAttributes(pszPath);
515     return ((attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_DIRECTORY));
516 }
517 
IsExistingDirectory(IN LPCTSTR pszPath)518 BOOL IsExistingDirectory(IN LPCTSTR pszPath)
519 {
520     DWORD attr = GetFileAttributes(pszPath);
521     return ((attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY));
522 }
523 
524 
525 // See r874
PagePrompt(PCON_PAGER Pager,DWORD Done,DWORD Total)526 BOOL __stdcall PagePrompt(PCON_PAGER Pager, DWORD Done, DWORD Total)
527 {
528     SHORT iScreenWidth, iCursorY;
529     INPUT_RECORD ir;
530 
531     ConOutResPuts(STRING_MISC_HELP1);
532 
533     RemoveBreakHandler();
534     ConInDisable();
535 
536     do
537     {
538         ConInKey(&ir);
539     }
540     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
541            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
542            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
543 
544     AddBreakHandler();
545     ConInEnable();
546 
547     /*
548      * Get the screen width, erase the full line where the cursor is,
549      * and move the cursor back to the beginning of the line.
550      */
551     GetScreenSize(&iScreenWidth, NULL);
552     iCursorY = GetCursorY();
553     SetCursorXY(0, iCursorY);
554     while (iScreenWidth-- > 0) // Or call FillConsoleOutputCharacter ?
555         ConOutChar(_T(' '));
556     SetCursorXY(0, iCursorY);
557 
558     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
559         ((ir.Event.KeyEvent.wVirtualKeyCode == _T('C')) &&
560          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
561     {
562         /* We break, output a newline */
563         ConOutChar(_T('\n'));
564 
565         bCtrlBreak = TRUE;
566         return FALSE;
567     }
568 
569     return TRUE;
570 }
571 
572 
FilePromptYN(UINT resID)573 INT FilePromptYN (UINT resID)
574 {
575     TCHAR szMsg[RC_STRING_MAX_SIZE];
576 //  TCHAR cKey = 0;
577 //  LPTSTR szKeys = _T("yna");
578 
579     TCHAR szIn[10];
580     LPTSTR p;
581 
582     if (resID != 0)
583         ConOutResPrintf (resID);
584 
585     /* preliminary fix */
586     ConInString(szIn, 10);
587 
588     _tcsupr (szIn);
589     for (p = szIn; _istspace (*p); p++)
590         ;
591 
592     LoadString(CMD_ModuleHandle, STRING_CHOICE_OPTION, szMsg, ARRAYSIZE(szMsg));
593 
594     if (_tcsncmp(p, &szMsg[0], 1) == 0)
595         return PROMPT_YES;
596     else if (_tcsncmp(p, &szMsg[1], 1) == 0)
597         return PROMPT_NO;
598 #if 0
599     else if (*p == _T('\03'))
600         return PROMPT_BREAK;
601 #endif
602 
603     return PROMPT_NO;
604 
605     /* unfinished solution */
606 #if 0
607     RemoveBreakHandler();
608     ConInDisable();
609 
610     do
611     {
612         ConInKey (&ir);
613         cKey = _totlower (ir.Event.KeyEvent.uChar.AsciiChar);
614         if (_tcschr (szKeys, cKey[0]) == NULL)
615             cKey = 0;
616 
617 
618     }
619     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
620            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
621            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
622 
623     AddBreakHandler();
624     ConInEnable();
625 
626     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
627         ((ir.Event.KeyEvent.wVirtualKeyCode == 'C') &&
628          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
629         return PROMPT_BREAK;
630 
631     return PROMPT_YES;
632 #endif
633 }
634 
635 
FilePromptYNA(UINT resID)636 INT FilePromptYNA (UINT resID)
637 {
638     TCHAR szMsg[RC_STRING_MAX_SIZE];
639 //  TCHAR cKey = 0;
640 //  LPTSTR szKeys = _T("yna");
641 
642     TCHAR szIn[10];
643     LPTSTR p;
644 
645     if (resID != 0)
646         ConOutResPrintf (resID);
647 
648     /* preliminary fix */
649     ConInString(szIn, 10);
650 
651     _tcsupr (szIn);
652     for (p = szIn; _istspace (*p); p++)
653         ;
654 
655     LoadString(CMD_ModuleHandle, STRING_COPY_OPTION, szMsg, ARRAYSIZE(szMsg));
656 
657     if (_tcsncmp(p, &szMsg[0], 1) == 0)
658         return PROMPT_YES;
659     else if (_tcsncmp(p, &szMsg[1], 1) == 0)
660         return PROMPT_NO;
661     else if (_tcsncmp(p, &szMsg[2], 1) == 0)
662         return PROMPT_ALL;
663 #if 0
664     else if (*p == _T('\03'))
665         return PROMPT_BREAK;
666 #endif
667 
668     return PROMPT_NO;
669 
670     /* unfinished solution */
671 #if 0
672     RemoveBreakHandler();
673     ConInDisable();
674 
675     do
676     {
677         ConInKey (&ir);
678         cKey = _totlower (ir.Event.KeyEvent.uChar.AsciiChar);
679         if (_tcschr (szKeys, cKey[0]) == NULL)
680             cKey = 0;
681     }
682     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
683            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
684            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
685 
686     AddBreakHandler();
687     ConInEnable();
688 
689     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
690         ((ir.Event.KeyEvent.wVirtualKeyCode == _T('C')) &&
691          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
692         return PROMPT_BREAK;
693 
694     return PROMPT_YES;
695 #endif
696 }
697 
698 /* EOF */
699