xref: /reactos/base/shell/cmd/misc.c (revision 0ab63f95)
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 (accessible)
481  */
482 BOOL IsValidPathName (LPCTSTR pszPath)
483 {
484     TCHAR szOldPath[MAX_PATH];
485     BOOL  bResult;
486 
487     GetCurrentDirectory (MAX_PATH, szOldPath);
488     bResult = SetCurrentDirectory (pszPath);
489 
490     SetCurrentDirectory (szOldPath);
491 
492     return bResult;
493 }
494 
495 
496 /*
497  * Checks if a file exists (accessible)
498  */
499 BOOL IsExistingFile (LPCTSTR pszPath)
500 {
501     DWORD attr = GetFileAttributes (pszPath);
502     return (attr != 0xFFFFFFFF && (! (attr & FILE_ATTRIBUTE_DIRECTORY)) );
503 }
504 
505 
506 BOOL IsExistingDirectory (LPCTSTR pszPath)
507 {
508     DWORD attr = GetFileAttributes (pszPath);
509     return (attr != 0xFFFFFFFF && (attr & FILE_ATTRIBUTE_DIRECTORY) );
510 }
511 
512 
513 // See r874
514 BOOL __stdcall PagePrompt(PCON_PAGER Pager, DWORD Done, DWORD Total)
515 {
516     SHORT iScreenWidth, iCursorY;
517     INPUT_RECORD ir;
518 
519     ConOutResPuts(STRING_MISC_HELP1);
520 
521     RemoveBreakHandler();
522     ConInDisable();
523 
524     do
525     {
526         ConInKey(&ir);
527     }
528     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
529            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
530            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
531 
532     AddBreakHandler();
533     ConInEnable();
534 
535     /*
536      * Get the screen width, erase the full line where the cursor is,
537      * and move the cursor back to the beginning of the line.
538      */
539     GetScreenSize(&iScreenWidth, NULL);
540     iCursorY = GetCursorY();
541     SetCursorXY(0, iCursorY);
542     while (iScreenWidth-- > 0) // Or call FillConsoleOutputCharacter ?
543         ConOutChar(_T(' '));
544     SetCursorXY(0, iCursorY);
545 
546     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
547         ((ir.Event.KeyEvent.wVirtualKeyCode == _T('C')) &&
548          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
549     {
550         /* We break, output a newline */
551         ConOutChar(_T('\n'));
552 
553         bCtrlBreak = TRUE;
554         return FALSE;
555     }
556 
557     return TRUE;
558 }
559 
560 
561 INT FilePromptYN (UINT resID)
562 {
563     TCHAR szMsg[RC_STRING_MAX_SIZE];
564 //  TCHAR cKey = 0;
565 //  LPTSTR szKeys = _T("yna");
566 
567     TCHAR szIn[10];
568     LPTSTR p;
569 
570     if (resID != 0)
571         ConOutResPrintf (resID);
572 
573     /* preliminary fix */
574     ConInString(szIn, 10);
575     ConOutChar(_T('\n'));
576 
577     _tcsupr (szIn);
578     for (p = szIn; _istspace (*p); p++)
579         ;
580 
581     LoadString(CMD_ModuleHandle, STRING_CHOICE_OPTION, szMsg, ARRAYSIZE(szMsg));
582 
583     if (_tcsncmp(p, &szMsg[0], 1) == 0)
584         return PROMPT_YES;
585     else if (_tcsncmp(p, &szMsg[1], 1) == 0)
586         return PROMPT_NO;
587 #if 0
588     else if (*p == _T('\03'))
589         return PROMPT_BREAK;
590 #endif
591 
592     return PROMPT_NO;
593 
594     /* unfinished solution */
595 #if 0
596     RemoveBreakHandler();
597     ConInDisable();
598 
599     do
600     {
601         ConInKey (&ir);
602         cKey = _totlower (ir.Event.KeyEvent.uChar.AsciiChar);
603         if (_tcschr (szKeys, cKey[0]) == NULL)
604             cKey = 0;
605 
606 
607     }
608     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
609            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
610            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
611 
612     AddBreakHandler();
613     ConInEnable();
614 
615     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
616         ((ir.Event.KeyEvent.wVirtualKeyCode == 'C') &&
617          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
618         return PROMPT_BREAK;
619 
620     return PROMPT_YES;
621 #endif
622 }
623 
624 
625 INT FilePromptYNA (UINT resID)
626 {
627     TCHAR szMsg[RC_STRING_MAX_SIZE];
628 //  TCHAR cKey = 0;
629 //  LPTSTR szKeys = _T("yna");
630 
631     TCHAR szIn[10];
632     LPTSTR p;
633 
634     if (resID != 0)
635         ConOutResPrintf (resID);
636 
637     /* preliminary fix */
638     ConInString(szIn, 10);
639     ConOutChar(_T('\n'));
640 
641     _tcsupr (szIn);
642     for (p = szIn; _istspace (*p); p++)
643         ;
644 
645     LoadString( CMD_ModuleHandle, STRING_COPY_OPTION, szMsg, ARRAYSIZE(szMsg));
646 
647     if (_tcsncmp(p, &szMsg[0], 1) == 0)
648         return PROMPT_YES;
649     else if (_tcsncmp(p, &szMsg[1], 1) == 0)
650         return PROMPT_NO;
651     else if (_tcsncmp(p, &szMsg[2], 1) == 0)
652         return PROMPT_ALL;
653 #if 0
654     else if (*p == _T('\03'))
655         return PROMPT_BREAK;
656 #endif
657 
658     return PROMPT_NO;
659 
660     /* unfinished solution */
661 #if 0
662     RemoveBreakHandler();
663     ConInDisable();
664 
665     do
666     {
667         ConInKey (&ir);
668         cKey = _totlower (ir.Event.KeyEvent.uChar.AsciiChar);
669         if (_tcschr (szKeys, cKey[0]) == NULL)
670             cKey = 0;
671     }
672     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
673            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
674            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
675 
676     AddBreakHandler();
677     ConInEnable();
678 
679     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
680         ((ir.Event.KeyEvent.wVirtualKeyCode == _T('C')) &&
681          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
682         return PROMPT_BREAK;
683 
684     return PROMPT_YES;
685 #endif
686 }
687 
688 /* EOF */
689