[SHELL32] Fix Control_RunDLLW (#5400)
[reactos.git] / base / shell / cmd / misc.c
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 */