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