This source file includes following definitions.
- vfprintf
- fprintf
- printf
- fail
- warn
- canon_filename
- skip_space
- skip_nonspace
- get_next_token
- batch_file_p
- search_dir
- make_absolute
- try_dequote_cmdline
- setup_argv
- console_event_handler
- spawn
- get_env_size
- main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 #define DEFER_MS_W32_H
31 #include <config.h>
32
33 #include <windows.h>
34
35 #include <stdarg.h>
36 #include <malloc.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40
41
42
43 extern int _snprintf (char *buffer, size_t count, const char *format, ...);
44
45
46
47
48
49 #define stdout GetStdHandle (STD_OUTPUT_HANDLE)
50 #define stderr GetStdHandle (STD_ERROR_HANDLE)
51
52 #if __GNUC__ + (__GNUC_MINOR__ >= 4) >= 5
53 void fail (const char *, ...) __attribute__((noreturn));
54 #else
55 void fail (const char *, ...);
56 #endif
57 int vfprintf (HANDLE, const char *, va_list);
58 int fprintf (HANDLE, const char *, ...);
59 int printf (const char *, ...);
60 void warn (const char *, ...);
61
62 int
63 vfprintf (HANDLE hnd, const char * msg, va_list args)
64 {
65 DWORD bytes_written;
66 char buf[1024];
67
68 wvsprintf (buf, msg, args);
69 return WriteFile (hnd, buf, strlen (buf), &bytes_written, NULL);
70 }
71
72 int
73 fprintf (HANDLE hnd, const char * msg, ...)
74 {
75 va_list args;
76 int rc;
77
78 va_start (args, msg);
79 rc = vfprintf (hnd, msg, args);
80 va_end (args);
81
82 return rc;
83 }
84
85 int
86 printf (const char * msg, ...)
87 {
88 va_list args;
89 int rc;
90
91 va_start (args, msg);
92 rc = vfprintf (stdout, msg, args);
93 va_end (args);
94
95 return rc;
96 }
97
98 void
99 fail (const char * msg, ...)
100 {
101 va_list args;
102
103 va_start (args, msg);
104 vfprintf (stderr, msg, args);
105 va_end (args);
106
107 exit (-1);
108 }
109
110 void
111 warn (const char * msg, ...)
112 {
113 va_list args;
114
115 va_start (args, msg);
116 vfprintf (stderr, msg, args);
117 va_end (args);
118 }
119
120
121
122 static char *
123 canon_filename (char *fname)
124 {
125 char *p = fname;
126
127 while (*p)
128 {
129 if (*p == '/')
130 *p = '\\';
131 p++;
132 }
133
134 return fname;
135 }
136
137 static const char *
138 skip_space (const char *str)
139 {
140 while (isspace (*str)) str++;
141 return str;
142 }
143
144 static const char *
145 skip_nonspace (const char *str)
146 {
147 while (*str && !isspace (*str)) str++;
148 return str;
149 }
150
151
152
153
154 const int escape_char = '\\';
155
156
157 static int
158 get_next_token (char * buf, const char ** pSrc)
159 {
160 const char * p = *pSrc;
161 char * o = buf;
162
163 p = skip_space (p);
164 if (*p == '"')
165 {
166 int escape_char_run = 0;
167
168
169
170
171
172 p++;
173 while (1)
174 {
175 if (p[0] == escape_char && escape_char != '"')
176 {
177 escape_char_run++;
178 p++;
179 continue;
180 }
181 else if (p[0] == '"')
182 {
183 while (escape_char_run > 1)
184 {
185 *o++ = escape_char;
186 escape_char_run -= 2;
187 }
188
189 if (escape_char_run > 0)
190 {
191
192 *o++ = *p++;
193 escape_char_run = 0;
194 }
195 else if (p[1] == escape_char && escape_char == '"')
196 {
197
198 *o++ = *p;
199 p += 2;
200 }
201 else
202 {
203
204 *o = '\0';
205
206 p++;
207 break;
208 }
209 }
210 else if (p[0] == '\0')
211 {
212
213
214
215 if (escape_char == '\\')
216 {
217
218
219
220
221 while (escape_char_run > 1)
222 {
223 *o++ = escape_char;
224 escape_char_run -= 2;
225 }
226 }
227 *o = '\0';
228 break;
229 }
230 else
231 {
232 if (escape_char == '\\')
233 {
234
235
236
237 for ( ; escape_char_run > 0; escape_char_run--)
238 *o++ = escape_char;
239 }
240 *o++ = *p++;
241 }
242 }
243 }
244 else
245 {
246
247 const char * p1 = skip_nonspace (p);
248 memcpy (o, p, p1 - p);
249 o += (p1 - p);
250 *o = '\0';
251 p = p1;
252 }
253
254 *pSrc = p;
255
256 return o - buf;
257 }
258
259
260 static BOOL
261 batch_file_p (const char *progname)
262 {
263 const char *exts[] = {".bat", ".cmd"};
264 int n_exts = sizeof (exts) / sizeof (char *);
265 int i;
266
267 const char *ext = strrchr (progname, '.');
268
269 if (ext)
270 {
271 for (i = 0; i < n_exts; i++)
272 {
273 if (stricmp (ext, exts[i]) == 0)
274 return TRUE;
275 }
276 }
277
278 return FALSE;
279 }
280
281
282
283 static int
284 search_dir (const char *dir, const char *exec, int bufsize, char *buffer)
285 {
286 const char *exts[] = {".bat", ".cmd", ".exe", ".com"};
287 int n_exts = sizeof (exts) / sizeof (char *);
288 char *dummy;
289 int i, rc;
290 const char *pext = strrchr (exec, '\\');
291
292
293 if (!pext)
294 pext = exec;
295 pext = strchr (pext, '.');
296
297
298 if (pext)
299 {
300
301
302 char exec_ext[MAX_PATH], *p;
303
304 p = strcpy (exec_ext, exec) + strlen (exec);
305
306
307 rc = SearchPath (dir, exec_ext, NULL, bufsize, buffer, &dummy);
308 if (rc > 0)
309 return rc;
310
311
312 for (i = 0; i < n_exts; i++)
313 {
314 strcpy (p, exts[i]);
315 rc = SearchPath (dir, exec_ext, NULL, bufsize, buffer, &dummy);
316 if (rc > 0)
317 return rc;
318 }
319 }
320 else
321 {
322 for (i = 0; i < n_exts; i++)
323 {
324 rc = SearchPath (dir, exec, exts[i], bufsize, buffer, &dummy);
325 if (rc > 0)
326 return rc;
327 }
328 }
329
330 return 0;
331 }
332
333
334
335
336 static char *
337 make_absolute (const char *prog)
338 {
339 char absname[MAX_PATH];
340 char dir[MAX_PATH];
341 char curdir[MAX_PATH];
342 char *p, *path;
343 const char *fname;
344
345
346 if ((isalpha (prog[0]) && prog[1] == ':') ||
347 (prog[0] == '\\'))
348 {
349
350 fname = strrchr (prog, '\\');
351 if (!fname)
352
353 fname = prog + 2;
354 strncpy (dir, prog, fname - prog);
355 dir[fname - prog] = '\0';
356
357
358 if (search_dir (dir, prog, MAX_PATH, absname) > 0)
359 return strdup (absname);
360 else
361 return NULL;
362 }
363
364 if (GetCurrentDirectory (MAX_PATH, curdir) <= 0)
365 return NULL;
366
367
368 if (strpbrk (prog, "\\"))
369 {
370 if (search_dir (curdir, prog, MAX_PATH, absname) > 0)
371 return strdup (absname);
372 else
373 return NULL;
374 }
375
376
377 path = alloca (strlen (getenv ("PATH")) + strlen (curdir) + 2);
378 strcpy (path, curdir);
379 strcat (path, ";");
380 strcat (path, getenv ("PATH"));
381
382 while (*path)
383 {
384 size_t len;
385
386
387 p = path;
388 while (*p && *p != ';') p++;
389
390 len = min (p - path, sizeof (dir) - 1);
391 strncpy (dir, path, len);
392 dir[len] = '\0';
393
394
395 if (search_dir (dir, prog, MAX_PATH, absname) > 0)
396 return strdup (absname);
397
398
399 path = p + 1;
400 }
401
402 return NULL;
403 }
404
405
406
407
408
409 static int
410 try_dequote_cmdline (char* cmdline)
411 {
412
413
414
415
416 char * old_pos = cmdline;
417 char * new_cmdline = alloca (strlen(cmdline));
418 char * new_pos = new_cmdline;
419 char c;
420
421 enum {
422 NORMAL,
423 AFTER_CARET,
424 INSIDE_QUOTE
425 } state = NORMAL;
426
427 while ((c = *old_pos++))
428 {
429 switch (state)
430 {
431 case NORMAL:
432 switch(c)
433 {
434 case '"':
435 *new_pos++ = c;
436 state = INSIDE_QUOTE;
437 break;
438 case '^':
439 state = AFTER_CARET;
440 break;
441 case '<': case '>':
442 case '&': case '|':
443 case '(': case ')':
444 case '%': case '!':
445
446
447 return 0;
448 default:
449 *new_pos++ = c;
450 break;
451 }
452 break;
453 case AFTER_CARET:
454 *new_pos++ = c;
455 state = NORMAL;
456 break;
457 case INSIDE_QUOTE:
458 switch (c)
459 {
460 case '"':
461 *new_pos++ = c;
462 state = NORMAL;
463 break;
464 case '%':
465 case '!':
466
467 return 0;
468 default:
469 *new_pos++ = c;
470 break;
471 }
472 break;
473 }
474 }
475
476
477
478 memcpy (cmdline, new_cmdline, new_pos - new_cmdline);
479 cmdline[new_pos - new_cmdline] = '\0';
480 return 1;
481 }
482
483
484
485 #if 0
486 char ** _argv;
487 int _argc;
488
489
490 void
491 setup_argv (void)
492 {
493 char * cmdline = GetCommandLine ();
494 int arg_bytes = 0;
495
496
497 }
498 #endif
499
500
501
502
503
504 PROCESS_INFORMATION child;
505 int interactive = TRUE;
506
507 BOOL console_event_handler (DWORD);
508
509 BOOL
510 console_event_handler (DWORD event)
511 {
512 switch (event)
513 {
514 case CTRL_C_EVENT:
515 case CTRL_BREAK_EVENT:
516 if (!interactive)
517 {
518
519
520
521
522
523
524 if (child.hProcess &&
525 WaitForSingleObject (child.hProcess, 500) != WAIT_OBJECT_0)
526 TerminateProcess (child.hProcess, 0);
527 exit (STATUS_CONTROL_C_EXIT);
528 }
529 break;
530
531 #if 0
532 default:
533
534
535 fail ("cmdproxy: received %d event\n", event);
536 if (child.hProcess)
537 TerminateProcess (child.hProcess, 0);
538 #endif
539 }
540 return TRUE;
541 }
542
543
544
545 static int
546 spawn (const char *progname, char *cmdline, const char *dir, int *retcode)
547 {
548 BOOL success = FALSE;
549 SECURITY_ATTRIBUTES sec_attrs;
550 STARTUPINFO start;
551
552
553
554
555 char * envblock = GetEnvironmentStrings ();
556
557 sec_attrs.nLength = sizeof (sec_attrs);
558 sec_attrs.lpSecurityDescriptor = NULL;
559 sec_attrs.bInheritHandle = FALSE;
560
561 memset (&start, 0, sizeof (start));
562 start.cb = sizeof (start);
563
564
565
566
567
568 if (progname != NULL && cmdline[0] == '"' && batch_file_p (progname))
569 progname = NULL;
570
571 if (CreateProcess (progname, cmdline, &sec_attrs, NULL, TRUE,
572 0, envblock, dir, &start, &child))
573 {
574 success = TRUE;
575
576 WaitForSingleObject (child.hProcess, INFINITE);
577 if (retcode)
578 GetExitCodeProcess (child.hProcess, (DWORD *)retcode);
579 CloseHandle (child.hThread);
580 CloseHandle (child.hProcess);
581 child.hProcess = NULL;
582 }
583
584 FreeEnvironmentStrings (envblock);
585
586 return success;
587 }
588
589
590 static int
591 get_env_size (void)
592 {
593 char * start = GetEnvironmentStrings ();
594 char * tmp = start;
595
596 while (tmp[0] || tmp[1])
597 ++tmp;
598 FreeEnvironmentStrings (start);
599 return tmp + 2 - start;
600 }
601
602
603
604 int
605 main (int argc, char ** argv)
606 {
607 int rc;
608 int need_shell;
609 char * cmdline;
610 char * progname;
611 int envsize;
612 char **pass_through_args;
613 int num_pass_through_args;
614 char modname[MAX_PATH];
615 char path[MAX_PATH];
616 char dir[MAX_PATH];
617 int status;
618
619 interactive = TRUE;
620
621 SetConsoleCtrlHandler ((PHANDLER_ROUTINE) console_event_handler, TRUE);
622
623 if (!GetCurrentDirectory (sizeof (dir), dir))
624 fail ("error: GetCurrentDirectory failed\n");
625
626
627
628
629
630
631
632
633
634
635
636
637
638 if (!GetModuleFileName (NULL, modname, sizeof (modname)))
639 fail ("error: GetModuleFileName failed\n");
640
641
642
643 progname = strrchr (modname, '\\');
644 *progname = '\0';
645 SetCurrentDirectory (modname);
646 *progname = '\\';
647
648
649
650
651 SetConsoleCP (GetACP ());
652 SetConsoleOutputCP (GetACP ());
653
654
655
656
657 path[0] = '\0';
658
659
660
661 status = SearchPath (NULL, argv[0], ".exe", sizeof (path), path,
662 &progname);
663 if (!(status > 0 && stricmp (modname, path) == 0))
664 {
665 if (status <= 0)
666 {
667 char *s;
668
669
670
671 strcpy (path, argv[0]);
672
673
674 for (s = path; *s; s++)
675 if (*s == '/')
676 *s = '\\';
677 }
678
679 if (!(GetShortPathName (modname, modname, sizeof (modname))
680 && GetShortPathName (path, path, sizeof (path))
681 && stricmp (modname, path) == 0))
682 {
683
684
685
686
687
688
689
690 char *p = strrchr (path, '\\');
691 char *q = strrchr (modname, '\\');
692 char *pdot, *qdot;
693
694 if (!p)
695 p = strchr (path, ':');
696 if (!p)
697 p = path;
698 else
699 p++;
700 if (!q)
701 q = strchr (modname, ':');
702 if (!q)
703 q = modname;
704 else
705 q++;
706
707 pdot = strrchr (p, '.');
708 if (!pdot || stricmp (pdot, ".exe") != 0)
709 pdot = p + strlen (p);
710 qdot = strrchr (q, '.');
711 if (!qdot || stricmp (qdot, ".exe") != 0)
712 qdot = q + strlen (q);
713 if (pdot - p != qdot - q || strnicmp (p, q, pdot - p) != 0)
714 {
715
716
717
718 if (spawn (NULL, GetCommandLine (), dir, &rc))
719 return rc;
720 fail ("Could not run %s\n", GetCommandLine ());
721 }
722 }
723 }
724
725
726
727
728
729
730
731
732
733
734
735 progname = NULL;
736 cmdline = NULL;
737
738 need_shell = TRUE;
739 interactive = TRUE;
740
741
742 envsize = get_env_size () + 300;
743 pass_through_args = (char **) alloca (argc * sizeof (char *));
744 num_pass_through_args = 0;
745
746 while (--argc > 0)
747 {
748 ++argv;
749
750
751
752
753
754 if (((*argv)[0] == '-' || (*argv)[0] == '/') && (*argv)[1] != '\0')
755 {
756 if (((*argv)[1] == 'c' || (*argv)[1] == 'C') && ((*argv)[2] == '\0'))
757 {
758 if (--argc == 0)
759 fail ("error: expecting arg for %s\n", *argv);
760 cmdline = *(++argv);
761 interactive = FALSE;
762 }
763 else if (((*argv)[1] == 'i' || (*argv)[1] == 'I') && ((*argv)[2] == '\0'))
764 {
765 if (cmdline)
766 warn ("warning: %s ignored because of -c\n", *argv);
767 }
768 else if (((*argv)[1] == 'e' || (*argv)[1] == 'E') && ((*argv)[2] == ':'))
769 {
770 int requested_envsize = atoi (*argv + 3);
771
772 if (requested_envsize > envsize)
773 envsize = requested_envsize;
774
775 if (envsize > 32768)
776 envsize = 32768;
777 }
778 else
779 {
780
781 pass_through_args[num_pass_through_args++] = *argv;
782 }
783 }
784 else
785 break;
786 }
787
788 #if 0
789
790
791
792
793
794 while (argc-- > 0)
795 {
796 pass_through_args[num_pass_through_args++] = *argv++;
797 }
798 #else
799
800 if (argc > 0)
801 warn ("warning: extra args ignored after '%s'\n", argv[-1]);
802 #endif
803
804 pass_through_args[num_pass_through_args] = NULL;
805
806
807
808 if (cmdline)
809 {
810 const char *args;
811
812
813
814
815 args = cmdline;
816 if (!get_next_token (path, &args))
817 fail ("error: no program name specified.\n");
818
819 canon_filename (path);
820 progname = make_absolute (path);
821
822
823
824
825
826 if (progname != NULL && try_dequote_cmdline (cmdline))
827 need_shell = FALSE;
828 else
829 progname = NULL;
830 }
831
832 pass_to_shell:
833 if (need_shell)
834 {
835 char * p;
836 int extra_arg_space = 0;
837 int maxlen, remlen;
838 int run_command_dot_com;
839
840 progname = getenv ("COMSPEC");
841 if (!progname)
842 fail ("error: COMSPEC is not set\n");
843
844 canon_filename (progname);
845 progname = make_absolute (progname);
846
847 if (progname == NULL || strchr (progname, '\\') == NULL)
848 fail ("error: the program %s could not be found.\n", getenv ("COMSPEC"));
849
850
851 run_command_dot_com =
852 (stricmp (strrchr (progname, '\\'), "command.com") == 0);
853
854
855
856 for (argv = pass_through_args; *argv != NULL; ++argv)
857
858 extra_arg_space += strlen (*argv) + 2;
859
860 if (cmdline)
861 {
862 char * buf;
863
864
865
866
867
868
869 remlen = maxlen =
870 strlen (progname) + extra_arg_space + strlen (cmdline) + 16 + 2;
871 buf = p = alloca (maxlen + 1);
872
873
874 p += _snprintf (p, remlen, "\"%s\"", progname);
875 remlen = maxlen - (p - buf);
876
877
878
879 for (argv = pass_through_args; *argv != NULL; ++argv)
880 {
881 p += _snprintf (p, remlen, " %s", *argv);
882 remlen = maxlen - (p - buf);
883 }
884
885
886
887
888
889
890
891 if (run_command_dot_com)
892 _snprintf (p, remlen, " /e:%d /c \"%s\"", envsize, cmdline);
893 else
894 _snprintf (p, remlen, " /c \"%s\"", cmdline);
895 cmdline = buf;
896 }
897 else
898 {
899 if (run_command_dot_com)
900 {
901
902
903
904
905
906 GetShortPathName (progname, path, sizeof (path));
907 p = strrchr (path, '\\');
908
909 *(++p) = '\0';
910 }
911 else
912 path[0] = '\0';
913
914 remlen = maxlen =
915 strlen (progname) + extra_arg_space + strlen (path) + 13;
916 cmdline = p = alloca (maxlen + 1);
917
918
919 p += _snprintf (p, remlen, "\"%s\" %s", progname, path);
920 remlen = maxlen - (p - cmdline);
921
922
923
924 for (argv = pass_through_args; *argv != NULL; ++argv)
925 {
926 p += _snprintf (p, remlen, " %s", *argv);
927 remlen = maxlen - (p - cmdline);
928 }
929
930 if (run_command_dot_com)
931 _snprintf (p, remlen, " /e:%d", envsize);
932 }
933 }
934
935 if (!progname)
936 fail ("Internal error: program name not defined\n");
937
938 if (!cmdline)
939 cmdline = progname;
940
941 if (spawn (progname, cmdline, dir, &rc))
942 return rc;
943
944 if (!need_shell)
945 {
946 need_shell = TRUE;
947 goto pass_to_shell;
948 }
949
950 fail ("Could not run %s\n", progname);
951
952 return 0;
953 }