xref: /reactos/base/shell/cmd/misc.c (revision 139a3d66)
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 BOOL FileGetString (HANDLE hFile, LPTSTR lpBuffer, INT nBufferLength)
514 {
515     LPSTR lpString;
516     DWORD  dwRead;
517     INT len = 0;
518 #ifdef _UNICODE
519     lpString = cmd_alloc(nBufferLength);
520 #else
521     lpString = lpBuffer;
522 #endif
523 
524     if (ReadFile(hFile, lpString, nBufferLength - 1, &dwRead, NULL))
525     {
526         /* break at new line*/
527         CHAR *end = memchr(lpString, '\n', dwRead);
528         len = dwRead;
529         if (end)
530         {
531             len = (INT)(end - lpString) + 1;
532             SetFilePointer(hFile, len - dwRead, NULL, FILE_CURRENT);
533         }
534     }
535 
536     if (!len)
537     {
538 #ifdef _UNICODE
539         cmd_free(lpString);
540 #endif
541         return FALSE;
542     }
543 
544     lpString[len++] = '\0';
545 #ifdef _UNICODE
546     MultiByteToWideChar(OutputCodePage, 0, lpString, -1, lpBuffer, len);
547     cmd_free(lpString);
548 #endif
549     return TRUE;
550 }
551 
552 // See r874
553 BOOL __stdcall PagePrompt(PCON_PAGER Pager, DWORD Done, DWORD Total)
554 {
555     SHORT iScreenWidth, iCursorY;
556     INPUT_RECORD ir;
557 
558     ConOutResPuts(STRING_MISC_HELP1);
559 
560     RemoveBreakHandler();
561     ConInDisable();
562 
563     do
564     {
565         ConInKey(&ir);
566     }
567     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
568            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
569            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
570 
571     AddBreakHandler();
572     ConInEnable();
573 
574     /*
575      * Get the screen width, erase the full line where the cursor is,
576      * and move the cursor back to the beginning of the line.
577      */
578     GetScreenSize(&iScreenWidth, NULL);
579     iCursorY = GetCursorY();
580     SetCursorXY(0, iCursorY);
581     while (iScreenWidth-- > 0) // Or call FillConsoleOutputCharacter ?
582         ConOutChar(_T(' '));
583     SetCursorXY(0, iCursorY);
584 
585     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
586         ((ir.Event.KeyEvent.wVirtualKeyCode == _T('C')) &&
587          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
588     {
589         /* We break, output a newline */
590         ConOutChar(_T('\n'));
591 
592         bCtrlBreak = TRUE;
593         return FALSE;
594     }
595 
596     return TRUE;
597 }
598 
599 
600 INT FilePromptYN (UINT resID)
601 {
602     TCHAR szMsg[RC_STRING_MAX_SIZE];
603 //  TCHAR cKey = 0;
604 //  LPTSTR szKeys = _T("yna");
605 
606     TCHAR szIn[10];
607     LPTSTR p;
608 
609     if (resID != 0)
610         ConOutResPrintf (resID);
611 
612     /* preliminary fix */
613     ConInString(szIn, 10);
614     ConOutChar(_T('\n'));
615 
616     _tcsupr (szIn);
617     for (p = szIn; _istspace (*p); p++)
618         ;
619 
620     LoadString(CMD_ModuleHandle, STRING_CHOICE_OPTION, szMsg, ARRAYSIZE(szMsg));
621 
622     if (_tcsncmp(p, &szMsg[0], 1) == 0)
623         return PROMPT_YES;
624     else if (_tcsncmp(p, &szMsg[1], 1) == 0)
625         return PROMPT_NO;
626 #if 0
627     else if (*p == _T('\03'))
628         return PROMPT_BREAK;
629 #endif
630 
631     return PROMPT_NO;
632 
633     /* unfinished solution */
634 #if 0
635     RemoveBreakHandler();
636     ConInDisable();
637 
638     do
639     {
640         ConInKey (&ir);
641         cKey = _totlower (ir.Event.KeyEvent.uChar.AsciiChar);
642         if (_tcschr (szKeys, cKey[0]) == NULL)
643             cKey = 0;
644 
645 
646     }
647     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
648            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
649            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
650 
651     AddBreakHandler();
652     ConInEnable();
653 
654     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
655         ((ir.Event.KeyEvent.wVirtualKeyCode == 'C') &&
656          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
657         return PROMPT_BREAK;
658 
659     return PROMPT_YES;
660 #endif
661 }
662 
663 
664 INT FilePromptYNA (UINT resID)
665 {
666     TCHAR szMsg[RC_STRING_MAX_SIZE];
667 //  TCHAR cKey = 0;
668 //  LPTSTR szKeys = _T("yna");
669 
670     TCHAR szIn[10];
671     LPTSTR p;
672 
673     if (resID != 0)
674         ConOutResPrintf (resID);
675 
676     /* preliminary fix */
677     ConInString(szIn, 10);
678     ConOutChar(_T('\n'));
679 
680     _tcsupr (szIn);
681     for (p = szIn; _istspace (*p); p++)
682         ;
683 
684     LoadString( CMD_ModuleHandle, STRING_COPY_OPTION, szMsg, ARRAYSIZE(szMsg));
685 
686     if (_tcsncmp(p, &szMsg[0], 1) == 0)
687         return PROMPT_YES;
688     else if (_tcsncmp(p, &szMsg[1], 1) == 0)
689         return PROMPT_NO;
690     else if (_tcsncmp(p, &szMsg[2], 1) == 0)
691         return PROMPT_ALL;
692 #if 0
693     else if (*p == _T('\03'))
694         return PROMPT_BREAK;
695 #endif
696 
697     return PROMPT_NO;
698 
699     /* unfinished solution */
700 #if 0
701     RemoveBreakHandler();
702     ConInDisable();
703 
704     do
705     {
706         ConInKey (&ir);
707         cKey = _totlower (ir.Event.KeyEvent.uChar.AsciiChar);
708         if (_tcschr (szKeys, cKey[0]) == NULL)
709             cKey = 0;
710     }
711     while ((ir.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT) ||
712            (ir.Event.KeyEvent.wVirtualKeyCode == VK_MENU) ||
713            (ir.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL));
714 
715     AddBreakHandler();
716     ConInEnable();
717 
718     if ((ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) ||
719         ((ir.Event.KeyEvent.wVirtualKeyCode == _T('C')) &&
720          (ir.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))))
721         return PROMPT_BREAK;
722 
723     return PROMPT_YES;
724 #endif
725 }
726 
727 /* EOF */
728