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