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