[VFD] Import the VFD project (Virtual Floppy Drive) which allows creating virtual
[reactos.git] / modules / rosapps / applications / cmdutils / vfdcmd / vfdcmd.c
1 /*
2 vfdcmd.c
3
4 Virtual Floppy Drive for Windows
5 Driver control program (console version)
6
7 Copyright (C) 2003-2008 Ken Kato
8 */
9
10 #ifdef __cplusplus
11 #pragma message(__FILE__": Compiled as C++ for testing purpose.")
12 #endif // __cplusplus
13
14 #define WIN32_LEAN_AND_MEAN
15 #define _CRTDBG_MAP_ALLOC
16 #include <windows.h>
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <crtdbg.h>
21
22 #ifndef INVALID_FILE_ATTRIBUTES
23 #define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
24 #endif // INVALID_FILE_ATTRIBUTES
25
26 #include "vfdtypes.h"
27 #include "vfdapi.h"
28 #include "vfdver.h"
29 #include "vfdmsg.h"
30
31 //
32 // current driver state
33 //
34 static DWORD driver_state = VFD_NOT_INSTALLED;
35
36 //
37 // interactive flag
38 //
39 static const char *help_progname = "VFD.EXE ";
40
41 //
42 // command functions return value
43 //
44 #define VFD_OK 0
45 #define VFD_NG 1
46
47 //
48 // operation mode
49 //
50 #define OPERATION_ASK 0 // ask user on error
51 #define OPERATION_QUIT 1 // quits on error
52 #define OPERATION_FORCE 2 // force on error
53
54 //
55 // invalid target number
56 //
57 #define TARGET_NONE (ULONG)-1
58
59 //
60 // command processing functions
61 //
62 typedef int (*cmdfnc)(const char **args);
63
64 static int Install(const char **args);
65 static int Remove(const char **args);
66 static int Config(const char **args);
67 static int Start(const char **args);
68 static int Stop(const char **args);
69 static int Shell(const char **args);
70 static int Open(const char **args);
71 static int Close(const char **args);
72 static int Save(const char **args);
73 static int Protect(const char **args);
74 static int Format(const char **args);
75 static int Link(const char **args);
76 static int Unlink(const char **args);
77 static int Status(const char **args);
78 static int Help(const char **args);
79 static int Version(const char **args);
80
81 //
82 // Command table
83 //
84 static const struct {
85 char *cmd; // command string
86 int max_args; // maximum allowed number of argc
87 cmdfnc func; // command processing function
88 DWORD hint; // command hint message id
89 }
90 Commands[] = {
91 {"INSTALL", 2, Install, MSG_HINT_INSTALL},
92 {"REMOVE", 1, Remove, MSG_HINT_REMOVE },
93 {"CONFIG", 1, Config, MSG_HINT_CONFIG },
94 {"START", 0, Start, MSG_HINT_START },
95 {"STOP", 1, Stop, MSG_HINT_STOP },
96 {"SHELL", 1, Shell, MSG_HINT_SHELL },
97 {"OPEN", 6, Open, MSG_HINT_OPEN },
98 {"CLOSE", 2, Close, MSG_HINT_CLOSE },
99 {"SAVE", 3, Save, MSG_HINT_SAVE, },
100 {"PROTECT", 2, Protect, MSG_HINT_PROTECT},
101 {"FORMAT", 2, Format, MSG_HINT_FORMAT },
102 {"LINK", 3, Link, MSG_HINT_LINK },
103 {"ULINK", 1, Unlink, MSG_HINT_ULINK },
104 {"STATUS", 0, Status, MSG_HINT_STATUS },
105 {"HELP", 1, Help, MSG_HELP_HELP },
106 {"?", 1, Help, MSG_HELP_HELP },
107 {"VERSION", 0, Version, MSG_HINT_VERSION},
108 {0, 0, 0, 0}
109 };
110
111 //
112 // Help message table
113 //
114 static const struct {
115 char *keyword; // help keyword
116 DWORD help; // help message id
117 }
118 HelpMsg[] = {
119 {"GENERAL", MSG_HELP_GENERAL},
120 {"CONSOLE", MSG_HELP_CONSOLE},
121 {"INSTALL", MSG_HELP_INSTALL},
122 {"REMOVE", MSG_HELP_REMOVE },
123 {"CONFIG", MSG_HELP_CONFIG },
124 {"START", MSG_HELP_START },
125 {"STOP", MSG_HELP_STOP },
126 {"SHELL", MSG_HELP_SHELL },
127 {"OPEN", MSG_HELP_OPEN },
128 {"CLOSE", MSG_HELP_CLOSE },
129 {"SAVE", MSG_HELP_SAVE },
130 {"PROTECT", MSG_HELP_PROTECT},
131 {"FORMAT", MSG_HELP_FORMAT },
132 {"LINK", MSG_HELP_LINK },
133 {"ULINK", MSG_HELP_ULINK },
134 {"STATUS", MSG_HELP_STATUS },
135 {"HELP", MSG_HELP_HELP },
136 {"VERSION", MSG_HINT_VERSION},
137 {0, 0}
138 };
139
140 //
141 // local functions
142 //
143 static int InteractiveConsole();
144 static int ProcessCommandLine(int argc, const char **args);
145 static int ParseCommand(const char *cmd);
146 static int ParseHelpTopic(const char *topic);
147 static int CheckDriver();
148 static int InputChar(ULONG msg, PCSTR ans);
149 static void PrintImageInfo(HANDLE hDevice);
150 static void PrintDriveLetter(HANDLE hDevice, ULONG nDrive);
151 static void PrintMessage(UINT msg, ...);
152 static BOOL ConsolePager(char *pBuffer, BOOL bReset);
153 static const char *SystemError(DWORD err);
154 static void ConvertPathCase(char *src, char *dst);
155
156 //
157 // utility macro
158 //
159 #define IS_WINDOWS_NT() ((GetVersion() & 0xff) < 5)
160
161 //
162 // main
163 //
164 int main(int argc, const char **argv)
165 {
166 #ifdef _DEBUG
167
168 // output vfd.exe command reference text
169
170 if (*(argv + 1) && !_stricmp(*(argv + 1), "doc")) {
171 int idx = 0;
172 char *buf = "";
173
174 printf("\r\n VFD.EXE Command Reference\r\n");
175
176 while (HelpMsg[idx].keyword) {
177 int len = strlen(HelpMsg[idx].keyword);
178
179 printf(
180 "\r\n\r\n"
181 "====================\r\n"
182 "%*s\r\n"
183 "====================\r\n"
184 "\r\n",
185 (20 + len) / 2, HelpMsg[idx].keyword);
186
187 FormatMessage(
188 FORMAT_MESSAGE_FROM_HMODULE |
189 FORMAT_MESSAGE_ALLOCATE_BUFFER |
190 FORMAT_MESSAGE_ARGUMENT_ARRAY,
191 NULL, HelpMsg[idx].help, 0,
192 (LPTSTR)&buf, 0, (va_list *)&help_progname);
193
194 printf("%s", buf);
195
196 LocalFree(buf);
197
198 idx++;
199 }
200
201 return 0;
202 }
203 #endif
204
205 // Reports memory leaks at process termination
206
207 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
208
209 // Check the operating system version
210
211 if (!VfdIsValidPlatform()) {
212 PrintMessage(MSG_WRONG_PLATFORM);
213 return VFD_NG;
214 }
215
216 if (argc < 2) {
217 // If no parameter is given, enter the interactive mode
218
219 return InteractiveConsole();
220 }
221 else {
222 // Perform a single operation
223
224 return ProcessCommandLine(argc - 1, argv + 1);
225 }
226 }
227
228 //
229 // VFD interactive console
230 //
231 int InteractiveConsole()
232 {
233 char input[1024]; // user input buffer
234
235 int argc; // number of args in the user input
236 char *args[10]; // args to pass to command functions
237
238 char sepa; // argument separator
239 char *p; // work pointer
240
241 // Disable the system default Ctrl+C handler
242
243 SetConsoleCtrlHandler(NULL, TRUE);
244
245 // Set the console title
246
247 SetConsoleTitle(VFD_PRODUCT_DESC);
248
249 // print version information and the console hint text
250
251 Version(NULL);
252
253 PrintMessage(MSG_CONSOLE_HINT);
254
255 // set interactive flag to exclude "VFD.EXE" from help text
256
257 help_progname = "";
258
259 // process user input
260
261 for (;;) {
262
263 // print the prompt
264
265 printf("[VFD] ");
266 fflush(stdout);
267
268 // read user input
269
270 fflush(stdin);
271 p = fgets(input, sizeof(input), stdin);
272
273 if (p == NULL) {
274
275 // most likely <ctrl+c>
276
277 printf("exit\n");
278 break;
279 }
280
281 // skip leading blank characters
282
283 while (*p == ' ' || *p == '\t' || *p == '\n') {
284 p++;
285 }
286
287 if (*p == '\0') {
288
289 // empty input
290
291 continue;
292 }
293
294 // handle external commands
295
296 if (!_strnicmp(p, "dir", 3) ||
297 !_strnicmp(p, "attrib", 6)) {
298
299 // special cases - frequently used commands
300 // pass these to system() even without '.'
301
302 system(p);
303 printf("\n");
304 continue;
305 }
306 else if (*p == '.') {
307
308 // external command
309
310 system(p + 1);
311 printf("\n");
312 continue;
313 }
314
315 // split the input line into parameters (10 parameters max)
316
317 argc = 0;
318 ZeroMemory(args, sizeof(args));
319
320 do {
321 // top of a parameter
322
323 args[argc++] = p;
324
325 // is the parameter quoted?
326
327 if (*p == '\"' || *p == '\'') {
328 sepa = *(p++);
329 }
330 else {
331 sepa = ' ';
332 }
333
334 // search the end of the parameter
335
336 while (*p && *p != '\n') {
337 if (sepa == ' ') {
338 if (*p == '\t' || *p == ' ') {
339 break; // tail of a non-quoted parameter
340 }
341 }
342 else {
343 if (*p == sepa) {
344 sepa = ' '; // close quote
345 }
346 }
347 p++;
348 }
349
350 // terminate the parameter
351
352 if (*p) {
353 *(p++) = '\0';
354 }
355
356 // skip trailing blank characters
357
358 while (*p == ' ' || *p == '\t' || *p == '\n') {
359 p++;
360 }
361
362 if (*p == '\0') {
363
364 // end of the input line - no more args
365
366 break;
367 }
368 }
369 while (argc < sizeof(args) / sizeof(args[0]));
370
371 // check the first parameter for special commands
372
373 if (!_stricmp(args[0], "exit") ||
374 !_stricmp(args[0], "quit") ||
375 !_stricmp(args[0], "bye")) {
376
377 // exit command
378
379 break;
380 }
381 else if (!_stricmp(args[0], "cd") ||
382 !_stricmp(args[0], "chdir")) {
383
384 // internal change directory command
385
386 if (args[1]) {
387 char path[MAX_PATH];
388 int i;
389
390 // ignore the /d option (of the standard cd command)
391
392 if (_stricmp(args[1], "/d")) {
393 i = 1;
394 }
395 else {
396 i = 2;
397 }
398
399 p = args[i];
400
401 if (*p == '\"' || *p == '\'') {
402
403 // the parameter is quoted -- remove quotations
404
405 p++;
406
407 while (*p && *p != *args[i]) {
408 p++;
409 }
410
411 args[i]++; // skip a leading quote
412 *p = '\0'; // remove a trailing quote
413 }
414 else {
415
416 // the parameter is not quoted
417 // -- concatenate params to allow spaces in unquoted path
418
419 while (i < argc - 1) {
420 *(args[i] + strlen(args[i])) = ' ';
421 i++;
422 }
423 }
424
425 // Match the case of the path to the name on the disk
426
427 ConvertPathCase(p, path);
428
429 if (!SetCurrentDirectory(path)) {
430 DWORD ret = GetLastError();
431
432 if (ret == ERROR_FILE_NOT_FOUND) {
433 ret = ERROR_PATH_NOT_FOUND;
434 }
435
436 printf("%s", SystemError(ret));
437 }
438 }
439 else {
440 if (!GetCurrentDirectory(sizeof(input), input)) {
441 printf("%s", SystemError(GetLastError()));
442 }
443 else {
444 printf("%s\n", input);
445 }
446 }
447 }
448 else if (isalpha(*args[0]) &&
449 *(args[0] + 1) == ':' &&
450 *(args[0] + 2) == '\0') {
451
452 // internal change drive command
453
454 *args[0] = (char)toupper(*args[0]);
455 *(args[0] + 2) = '\\';
456 *(args[0] + 3) = '\0';
457
458 if (!SetCurrentDirectory(args[0])) {
459 printf("%s", SystemError(GetLastError()));
460 }
461 }
462 else {
463
464 // perform the requested VFD command
465
466 ProcessCommandLine(argc, (const char **)args);
467 }
468
469 printf("\n");
470 }
471
472 return VFD_OK;
473 }
474
475 //
476 // process a single command
477 //
478 int ProcessCommandLine(int argc, const char **args)
479 {
480 int cmd;
481 DWORD ret;
482
483 //
484 // Decide a command to perform
485 //
486 cmd = ParseCommand(*args);
487
488 if (cmd < 0) {
489
490 // no matching command
491
492 return VFD_NG;
493 }
494
495 if (*(++args) &&
496 (!strcmp(*args, "/?") ||
497 !_stricmp(*args, "/h"))) {
498
499 // print a short hint for the command
500
501 PrintMessage(Commands[cmd].hint);
502 return VFD_NG;
503 }
504
505 if (--argc > Commands[cmd].max_args) {
506
507 // too many parameters for the command
508
509 PrintMessage(MSG_TOO_MANY_ARGS);
510 PrintMessage(Commands[cmd].hint);
511 return VFD_NG;
512 }
513
514 // Get the current driver state
515
516 ret = VfdGetDriverState(&driver_state);
517
518 if (ret != ERROR_SUCCESS) {
519 PrintMessage(MSG_GET_STAT_NG);
520 printf("%s", SystemError(ret));
521 return VFD_NG;
522 }
523
524 // Perform the requested operation
525
526 return (*Commands[cmd].func)(args);
527 }
528
529 //
530 // Install the Virtual Floppy Driver
531 // Command Line Parameters:
532 // (optional) driver file path - default to executive's dir
533 // (optional) auto start switch - default to demand start
534 //
535 int Install(const char **args)
536 {
537 const char *install_path = NULL;
538 DWORD start_type = SERVICE_DEMAND_START;
539
540 DWORD ret;
541
542 // process parameters
543
544 while (args && *args) {
545
546 if (!_stricmp(*args, "/a") ||
547 !_stricmp(*args, "/auto")) {
548
549 if (start_type != SERVICE_DEMAND_START) {
550 PrintMessage(MSG_DUPLICATE_ARGS, *args);
551 return VFD_NG;
552 }
553 /*
554 if (IS_WINDOWS_NT()) {
555
556 // On Windows NT, SYSTEM start drivers must be placed
557 // under the winnt\system32 directory. Since I don't
558 // care to handle driver file copying, I use the AUTO
559 // start method for Windows NT.
560
561 start_type = SERVICE_AUTO_START;
562 }
563 else {
564
565 // On Windows XP, the VFD driver must be running when
566 // the shell starts -- otherwise the shell doesn't
567 // recognize the VFD drives. Since Windows XP allows
568 // SYSTEM start drivers to be placed in any local
569 // directories, I use the SYSTEM start method here.
570 //
571 // This is not an issue when the driver is started
572 // manually because in that case VFD.EXE and VFDWIN.EXE
573 // notify the shell of the VFD drives.
574 //
575 // On Windows 2000 both SYSTEM and AUTO work fine.
576
577 start_type = SERVICE_SYSTEM_START;
578 }
579 */
580 // On second thought -- Win2K / XP mount manager assigns
581 // arbitrary drive letters to all drives it finds during
582 // the system start up. There is no way to prevent it
583 // until the driver is fully PnP compatible, so I'd settle
584 // for AUTO start for the time being.
585
586 start_type = SERVICE_AUTO_START;
587 }
588 else if (**args == '/') {
589 PrintMessage(MSG_UNKNOWN_OPTION, *args);
590 PrintMessage(MSG_HINT_INSTALL, help_progname);
591 return VFD_NG;
592 }
593 else {
594 if (install_path) {
595 PrintMessage(MSG_DUPLICATE_ARGS, "path");
596 return VFD_NG;
597 }
598
599 install_path = *args;
600 }
601
602 args++;
603 }
604
605 // already installed?
606
607 if (driver_state != VFD_NOT_INSTALLED) {
608 PrintMessage(MSG_DRIVER_EXISTS);
609 return VFD_NG;
610 }
611
612 // install the driver
613
614 ret = VfdInstallDriver(
615 install_path,
616 start_type);
617
618 if (ret != ERROR_SUCCESS) {
619 PrintMessage(MSG_INSTALL_NG);
620 printf("%s", SystemError(ret));
621 return VFD_NG;
622 }
623
624 // Get the latest driver state
625
626 ret = VfdGetDriverState(&driver_state);
627
628 if (ret != ERROR_SUCCESS) {
629 PrintMessage(MSG_GET_STAT_NG);
630 printf("%s", SystemError(ret));
631 return VFD_NG;
632 }
633
634 // operation successfull
635
636 PrintMessage(MSG_INSTALL_OK);
637
638 return VFD_OK;
639 }
640
641 //
642 // Remove Virtual Floppy Driver from system
643 // Command Line Parameters:
644 // [/F | /FORCE | /Q | /QUIT]
645 // /F forces remove operation if the driver cannot be stopped
646 // /Q quits remove operation if the driver cannot be stopped
647 //
648 int Remove(const char **args)
649 {
650 int mode = OPERATION_ASK;
651 const char *stop_params[] = { NULL, NULL };
652 DWORD ret;
653 int idx;
654
655 // parse parameters
656
657 while (args && *args) {
658
659 if (!_stricmp(*args, "/f") ||
660 !_stricmp(*args, "/force")) {
661
662 if (mode != OPERATION_ASK) {
663 PrintMessage(MSG_DUPLICATE_ARGS, *args);
664 return VFD_NG;
665 }
666
667 mode = OPERATION_FORCE;
668 stop_params[0] = *args;
669 }
670 else if (!_stricmp(*args, "/q") ||
671 !_stricmp(*args, "/quit")) {
672
673 if (mode != OPERATION_ASK) {
674 PrintMessage(MSG_DUPLICATE_ARGS, *args);
675 return VFD_NG;
676 }
677
678 mode = OPERATION_QUIT;
679 stop_params[0] = *args;
680 }
681 else {
682 PrintMessage(MSG_UNKNOWN_OPTION, *args);
683 PrintMessage(MSG_HINT_REMOVE, help_progname);
684 return VFD_NG;
685 }
686
687 args++;
688 }
689
690 // ensure the driver is installed
691
692 if (driver_state == VFD_NOT_INSTALLED) {
693 PrintMessage(MSG_NOT_INSTALLED);
694 return VFD_NG;
695 }
696
697 // ensure the driver is stopped
698
699 if (driver_state == SERVICE_RUNNING) {
700
701 // Try to stop with the same command line option (/F or /Q)
702
703 while (Stop(stop_params) != VFD_OK) {
704
705 // stop failed
706
707 if (mode == OPERATION_FORCE) {
708 PrintMessage(MSG_REMOVE_FORCE);
709 break;
710 }
711 else if (mode == OPERATION_QUIT) {
712 PrintMessage(MSG_REMOVE_QUIT);
713 return VFD_NG;
714 }
715 else {
716 int c;
717
718 PrintMessage(MSG_REMOVE_WARN);
719
720 c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
721
722 if (c == 'f') { // force
723 break;
724 }
725 else if (c == 'c') { // cancel
726 return VFD_NG;
727 }
728 }
729 }
730 }
731
732 // remove the driver
733
734 ret = VfdRemoveDriver();
735
736 if (ret != ERROR_SUCCESS) {
737 PrintMessage(MSG_REMOVE_NG);
738 printf("%s", SystemError(ret));
739 return VFD_NG;
740 }
741
742 // Wait for the driver to be actually removed for 3 secs Max.
743
744 for (idx = 0; idx < 10; idx++) {
745
746 ret = VfdGetDriverState(&driver_state);
747
748 if (ret != ERROR_SUCCESS) {
749 PrintMessage(MSG_GET_STAT_NG);
750 printf("%s", SystemError(ret));
751 return VFD_NG;
752 }
753
754 if (driver_state == VFD_NOT_INSTALLED) {
755 break;
756 }
757
758 Sleep(300);
759 }
760
761 if (driver_state != VFD_NOT_INSTALLED) {
762 PrintMessage(MSG_REMOVE_PENDING);
763 return VFD_NG;
764 }
765
766 // operation successful
767
768 PrintMessage(MSG_REMOVE_OK);
769
770 return VFD_OK;
771 }
772
773 //
774 // Configure the Virtual Floppy Driver
775 // Command Line Parameters:
776 // /auto, /manual
777 //
778 int Config(const char **args)
779 {
780 DWORD start_type = SERVICE_DISABLED;
781 DWORD ret;
782
783 while (args && *args) {
784 if (!_stricmp(*args, "/a") ||
785 !_stricmp(*args, "/auto")) {
786
787 if (start_type != SERVICE_DISABLED) {
788 PrintMessage(MSG_DUPLICATE_ARGS, *args);
789 return VFD_NG;
790 }
791
792 start_type = SERVICE_AUTO_START;
793 }
794 else if (!_stricmp(*args, "/m") ||
795 !_stricmp(*args, "/manual")) {
796
797 if (start_type != SERVICE_DISABLED) {
798 PrintMessage(MSG_DUPLICATE_ARGS, *args);
799 return VFD_NG;
800 }
801
802 start_type = SERVICE_DEMAND_START;
803 }
804 else {
805 PrintMessage(MSG_UNKNOWN_OPTION, *args);
806 PrintMessage(MSG_HINT_CONFIG, help_progname);
807 return VFD_NG;
808 }
809
810 args++;
811 }
812
813 if (start_type == SERVICE_DISABLED) {
814 // no parameter is specified
815 PrintMessage(MSG_HINT_CONFIG, help_progname);
816 return VFD_NG;
817 }
818
819 // ensure that the driver is installed
820
821 if (driver_state == VFD_NOT_INSTALLED) {
822 PrintMessage(MSG_NOT_INSTALLED);
823 return VFD_NG;
824 }
825
826 // ensure that the driver is up to date
827
828 if (CheckDriver() != VFD_OK) {
829 return VFD_NG;
830 }
831
832 // configure the driver
833
834 ret = VfdConfigDriver(start_type);
835
836 if (ret != ERROR_SUCCESS) {
837 PrintMessage(MSG_CONFIG_NG);
838 printf("%s", SystemError(ret));
839 return VFD_NG;
840 }
841
842 // operation successfull
843
844 PrintMessage(MSG_CONFIG_OK);
845
846 return VFD_OK;
847 }
848
849 //
850 // Start the Virtual Floppy Driver
851 // Command Line Parameters: None
852 //
853 int Start(const char **args)
854 {
855 DWORD ret;
856
857 UNREFERENCED_PARAMETER(args);
858
859 // ensure that the driver is installed
860
861 if (driver_state == VFD_NOT_INSTALLED &&
862 Install(NULL) != VFD_OK) {
863 return VFD_NG;
864 }
865
866 // ensure that the driver is up to date
867
868 if (CheckDriver() != VFD_OK) {
869 return VFD_NG;
870 }
871
872 // ensure that the driver is not running
873
874 if (driver_state == SERVICE_RUNNING) {
875 PrintMessage(MSG_ALREADY_RUNNING);
876 return VFD_NG;
877 }
878
879 // start the driver
880
881 ret = VfdStartDriver(&driver_state);
882
883 if (ret != ERROR_SUCCESS) {
884 PrintMessage(MSG_START_NG);
885 printf("%s", SystemError(ret));
886 return VFD_NG;
887 }
888
889 // operation successfull
890
891 PrintMessage(MSG_START_OK);
892
893 return VFD_OK;
894 }
895
896 //
897 // Stop the Virtual Floppy Driver
898 // Command Line Parameters:
899 // /FORCE | /F Forces the operation on error
900 // /QUIT | /Q Quits the operation on error
901 //
902 int Stop(const char **args)
903 {
904 int mode = OPERATION_ASK;
905 const char *close_params[] = { "*", NULL, NULL };
906 DWORD ret;
907
908 while (args && *args) {
909 if (!_stricmp(*args, "/f") ||
910 !_stricmp(*args, "/force")) {
911
912 if (mode != OPERATION_ASK) {
913 PrintMessage(MSG_DUPLICATE_ARGS, *args);
914 return VFD_NG;
915 }
916
917 mode = OPERATION_FORCE;
918
919 // parameter to pass to the Close() function
920 close_params[1] = *args;
921 }
922 else if (!_stricmp(*args, "/q") ||
923 !_stricmp(*args, "/quit")) {
924
925 if (mode != OPERATION_ASK) {
926 PrintMessage(MSG_DUPLICATE_ARGS, *args);
927 return VFD_NG;
928 }
929
930 mode = OPERATION_QUIT;
931
932 // parameter to pass to the Close() function
933 close_params[1] = *args;
934 }
935 else {
936 PrintMessage(MSG_UNKNOWN_OPTION, *args);
937 PrintMessage(MSG_HINT_STOP, help_progname);
938 return VFD_NG;
939 }
940
941 args++;
942 }
943
944 // ensure that the driver is installed
945
946 if (driver_state == VFD_NOT_INSTALLED) {
947 PrintMessage(MSG_NOT_INSTALLED);
948 return VFD_NG;
949 }
950
951 // ensure that the driver is running
952
953 if (driver_state == SERVICE_STOPPED) {
954 PrintMessage(MSG_NOT_STARTED);
955 return VFD_NG;
956 }
957
958 // ensure that all drives are empty
959
960 if (driver_state == SERVICE_RUNNING) {
961
962 // Try to close drives with the same operation mode (/F or /Q)
963
964 while (Close(close_params) != VFD_OK) {
965
966 // close failed
967
968 if (mode == OPERATION_FORCE) {
969 PrintMessage(MSG_STOP_FORCE);
970 break;
971 }
972 else if (mode == OPERATION_QUIT) {
973 PrintMessage(MSG_STOP_QUIT);
974 return VFD_NG;
975 }
976 else {
977 int c;
978
979 PrintMessage(MSG_STOP_WARN);
980
981 c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
982
983 if (c == 'f') { // force
984 break;
985 }
986 else if (c == 'c') { // cancel
987 return VFD_NG;
988 }
989 }
990 }
991 }
992
993 // stop the driver
994
995 ret = VfdStopDriver(&driver_state);
996
997 if (ret != ERROR_SUCCESS) {
998 PrintMessage(MSG_STOP_NG);
999 printf("%s", SystemError(ret));
1000 return VFD_NG;
1001 }
1002
1003 if (driver_state != SERVICE_STOPPED) {
1004 PrintMessage(MSG_STOP_PENDING);
1005 return VFD_NG;
1006 }
1007
1008 // operation successful
1009
1010 PrintMessage(MSG_STOP_OK);
1011
1012 return VFD_OK;
1013 }
1014
1015 //
1016 // Enable / Disable the shell extension
1017 // Command Line Parameters:
1018 // (optional) /ON or /OFF
1019 //
1020 int Shell(const char **args)
1021 {
1022 DWORD ret;
1023
1024 ret = VfdCheckHandlers();
1025
1026 if (ret != ERROR_SUCCESS &&
1027 ret != ERROR_PATH_NOT_FOUND &&
1028 ret != ERROR_FILE_NOT_FOUND) {
1029 PrintMessage(MSG_GET_SHELLEXT_NG);
1030 printf("%s", SystemError(ret));
1031 return VFD_NG;
1032 }
1033
1034 if (args && *args) {
1035 if (_stricmp(*args, "/on") == 0) {
1036 if (ret != ERROR_SUCCESS) {
1037 ret = VfdRegisterHandlers();
1038
1039 if (ret != ERROR_SUCCESS) {
1040 PrintMessage(MSG_SET_SHELLEXT_NG);
1041 printf("%s", SystemError(ret));
1042 return VFD_NG;
1043 }
1044 }
1045 }
1046 else if (_stricmp(*args, "/off") == 0) {
1047 if (ret == ERROR_SUCCESS) {
1048 ret = VfdUnregisterHandlers();
1049
1050 if (ret != ERROR_SUCCESS) {
1051 PrintMessage(MSG_SET_SHELLEXT_NG);
1052 printf("%s", SystemError(ret));
1053 return VFD_NG;
1054 }
1055 }
1056 }
1057 else {
1058 PrintMessage(MSG_UNKNOWN_OPTION, *args);
1059 PrintMessage(MSG_HINT_SHELL, help_progname);
1060 return VFD_NG;
1061 }
1062
1063 ret = VfdCheckHandlers();
1064 }
1065
1066 if (ret == ERROR_PATH_NOT_FOUND ||
1067 ret == ERROR_FILE_NOT_FOUND) {
1068 PrintMessage(MSG_SHELLEXT_DISABLED);
1069 }
1070 else if (ret == ERROR_SUCCESS) {
1071 PrintMessage(MSG_SHELLEXT_ENABLED);
1072 }
1073 else {
1074 PrintMessage(MSG_GET_SHELLEXT_NG);
1075 printf("%s", SystemError(ret));
1076 return VFD_NG;
1077 }
1078
1079 return VFD_OK;
1080 }
1081
1082 //
1083 // Open an image file to a Virtual Floppy Drive
1084 // Command Line Parameters:
1085 // [drive:] [file] [/NEW] [/RAM] [/P | /W]
1086 // [/size] [/media] [/F | /FORCE | /Q | /QUIT]
1087
1088 int Open(const char **args)
1089 {
1090 int mode = OPERATION_ASK;
1091 BOOL create = FALSE;
1092 ULONG target = TARGET_NONE;
1093 PCSTR file_name = NULL;
1094 VFD_DISKTYPE disk_type = VFD_DISKTYPE_FILE;
1095 CHAR protect = '\0';
1096 VFD_MEDIA media_type = VFD_MEDIA_NONE;
1097 BOOL five_inch = FALSE;
1098 VFD_FLAGS media_flags = 0;
1099 HANDLE hDevice;
1100 CHAR letter;
1101 DWORD ret;
1102
1103 // process parameters
1104
1105 while (args && *args) {
1106
1107 if (!_stricmp(*args, "/f") ||
1108 !_stricmp(*args, "/force")) {
1109
1110 if (mode != OPERATION_ASK) {
1111 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1112 return VFD_NG;
1113 }
1114
1115 mode = OPERATION_FORCE;
1116 }
1117 else if (!_stricmp(*args, "/q") ||
1118 !_stricmp(*args, "/quit")) {
1119
1120 if (mode != OPERATION_ASK) {
1121 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1122 return VFD_NG;
1123 }
1124
1125 mode = OPERATION_QUIT;
1126 }
1127
1128 else if (!_stricmp(*args, "/new")) {
1129
1130 if (create) {
1131 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1132 return VFD_NG;
1133 }
1134
1135 create = TRUE;
1136 }
1137
1138 // Disk type options
1139
1140 else if (_stricmp(*args, "/ram") == 0) {
1141
1142 if (disk_type != VFD_DISKTYPE_FILE) {
1143 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1144 return VFD_NG;
1145 }
1146
1147 disk_type = VFD_DISKTYPE_RAM;
1148 }
1149
1150 // Protect options
1151 else if (_stricmp(*args, "/p") == 0 ||
1152 _stricmp(*args, "/w") == 0) {
1153
1154 if (protect) {
1155 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1156 return VFD_NG;
1157 }
1158
1159 protect = (CHAR)toupper(*(*args + 1));
1160 }
1161
1162 // media size options
1163
1164 else if (strcmp(*args, "/160") == 0) {
1165 if (media_type) {
1166 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1167 return VFD_NG;
1168 }
1169
1170 media_type = VFD_MEDIA_F5_160;
1171 }
1172 else if (strcmp(*args, "/180") == 0) {
1173 if (media_type) {
1174 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1175 return VFD_NG;
1176 }
1177
1178 media_type = VFD_MEDIA_F5_180;
1179 }
1180 else if (strcmp(*args, "/320") == 0) {
1181 if (media_type) {
1182 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1183 return VFD_NG;
1184 }
1185
1186 media_type = VFD_MEDIA_F5_320;
1187 }
1188 else if (strcmp(*args, "/360") == 0) {
1189 if (media_type) {
1190 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1191 return VFD_NG;
1192 }
1193
1194 media_type = VFD_MEDIA_F5_360;
1195 }
1196 else if (strcmp(*args, "/640") == 0) {
1197 if (media_type) {
1198 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1199 return VFD_NG;
1200 }
1201
1202 media_type = VFD_MEDIA_F3_640;
1203 }
1204 else if (strcmp(*args, "/720") == 0) {
1205 if (media_type) {
1206 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1207 return VFD_NG;
1208 }
1209
1210 media_type = VFD_MEDIA_F3_720;
1211 }
1212 else if (strcmp(*args, "/820") == 0) {
1213 if (media_type) {
1214 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1215 return VFD_NG;
1216 }
1217
1218 media_type = VFD_MEDIA_F3_820;
1219 }
1220 else if (strcmp(*args, "/120") == 0 ||
1221 strcmp(*args, "/1.20") == 0) {
1222 if (media_type) {
1223 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1224 return VFD_NG;
1225 }
1226
1227 media_type = VFD_MEDIA_F3_1P2;
1228 }
1229 else if (strcmp(*args, "/144") == 0 ||
1230 strcmp(*args, "/1.44") == 0) {
1231 if (media_type) {
1232 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1233 return VFD_NG;
1234 }
1235
1236 media_type = VFD_MEDIA_F3_1P4;
1237 }
1238 else if (strcmp(*args, "/168") == 0 ||
1239 strcmp(*args, "/1.68") == 0) {
1240 if (media_type) {
1241 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1242 return VFD_NG;
1243 }
1244
1245 media_type = VFD_MEDIA_F3_1P6;
1246 }
1247 else if (strcmp(*args, "/172") == 0 ||
1248 strcmp(*args, "/1.72") == 0) {
1249 if (media_type) {
1250 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1251 return VFD_NG;
1252 }
1253
1254 media_type = VFD_MEDIA_F3_1P7;
1255 }
1256 else if (strcmp(*args, "/288") == 0 ||
1257 strcmp(*args, "/2.88") == 0) {
1258 if (media_type) {
1259 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1260 return VFD_NG;
1261 }
1262
1263 media_type = VFD_MEDIA_F3_2P8;
1264 }
1265
1266 // 5.25 inch media
1267
1268 else if (strcmp(*args, "/5") == 0 ||
1269 strcmp(*args, "/525") == 0 ||
1270 strcmp(*args, "/5.25") == 0) {
1271
1272 if (five_inch) {
1273 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1274 return VFD_NG;
1275 }
1276
1277 five_inch = TRUE;
1278 }
1279
1280 // target option
1281
1282 else if (isalnum(**args) &&
1283 *(*args + 1) == ':' &&
1284 *(*args + 2) == '\0') {
1285
1286 if (target != TARGET_NONE) {
1287 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1288 return VFD_NG;
1289 }
1290
1291 target = toupper(**args);
1292 }
1293
1294 // filename
1295
1296 else if (**args != '/') {
1297 if (file_name) {
1298 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1299 return VFD_NG;
1300 }
1301
1302 file_name = *args;
1303 }
1304 else {
1305 PrintMessage(MSG_UNKNOWN_OPTION, *args);
1306 PrintMessage(MSG_HINT_OPEN, help_progname);
1307 return VFD_NG;
1308 }
1309
1310 args++;
1311 }
1312
1313 if (target == TARGET_NONE) {
1314 // default target
1315 target = '0';
1316 PrintMessage(MSG_TARGET_NOTICE, target);
1317 }
1318
1319 // check target file
1320
1321 if (file_name) {
1322 DWORD file_attr;
1323 VFD_FILETYPE file_type;
1324 ULONG image_size;
1325 BOOL overwrite = FALSE;
1326
1327 ret = VfdCheckImageFile(
1328 file_name, &file_attr, &file_type, &image_size);
1329
1330 if (ret == ERROR_FILE_NOT_FOUND) {
1331
1332 // the target file does not exist
1333
1334 if (!create) { // create option not specified
1335
1336 if (mode == OPERATION_FORCE) {
1337 PrintMessage(MSG_CREATE_NOTICE);
1338 }
1339 else {
1340 printf("%s", SystemError(ret));
1341
1342 if (mode == OPERATION_QUIT ||
1343 InputChar(MSG_CREATE_CONFIRM, "yn") == 'n') {
1344 return VFD_NG;
1345 }
1346 }
1347
1348 create = TRUE;
1349 }
1350 }
1351 else if (ret == ERROR_SUCCESS) {
1352
1353 // the target file exists
1354
1355 if (create) { // create option is specified
1356
1357 if (mode == OPERATION_FORCE) {
1358 PrintMessage(MSG_OVERWRITE_NOTICE);
1359 }
1360 else {
1361 printf("%s", SystemError(ERROR_FILE_EXISTS));
1362
1363 if (mode == OPERATION_QUIT ||
1364 InputChar(MSG_OVERWRITE_CONFIRM, "yn") == 'n') {
1365 return VFD_NG;
1366 }
1367 }
1368
1369 overwrite = TRUE;
1370 }
1371 }
1372 else {
1373 PrintMessage(MSG_OPEN_NG, file_name);
1374 printf("%s", SystemError(ret));
1375 return VFD_NG;
1376 }
1377
1378 //
1379 // create or overwrite the target file
1380 //
1381
1382 if (create) {
1383
1384 if (media_type == VFD_MEDIA_NONE) {
1385
1386 if (mode == OPERATION_FORCE) {
1387 PrintMessage(MSG_CREATE144_NOTICE);
1388 }
1389 else {
1390 PrintMessage(MSG_FILE_MEDIA_UNKNOWN);
1391
1392 if (mode == OPERATION_QUIT ||
1393 InputChar(MSG_CREATE144_CONFIRM, "yn") == 'n') {
1394 return VFD_NG;
1395 }
1396 }
1397
1398 media_type = VFD_MEDIA_F3_1P4;
1399 }
1400
1401 ret = VfdCreateImageFile(
1402 file_name, media_type, VFD_FILETYPE_RAW, overwrite);
1403
1404 if (ret != ERROR_SUCCESS) {
1405 PrintMessage(MSG_CREATE_NG, file_name);
1406 printf("%s", SystemError(ret));
1407 return VFD_NG;
1408 }
1409
1410 PrintMessage(MSG_FILE_CREATED);
1411
1412 ret = VfdCheckImageFile(
1413 file_name, &file_attr, &file_type, &image_size);
1414
1415 if (ret != ERROR_SUCCESS) {
1416 PrintMessage(MSG_OPEN_NG, file_name);
1417 printf("%s", SystemError(ret));
1418 return VFD_NG;
1419 }
1420 }
1421 else {
1422 //
1423 // use the existing target file
1424 // check image size and the media type
1425 //
1426
1427 VFD_MEDIA def_media; // default media for image size
1428 ULONG media_size; // specified media size
1429
1430 media_size = VfdGetMediaSize(media_type);
1431
1432 if (media_size > image_size) {
1433
1434 // specified media is too large for the image
1435
1436 PrintMessage(MSG_IMAGE_TOO_SMALL);
1437 return VFD_NG;
1438 }
1439
1440 def_media = VfdLookupMedia(image_size);
1441
1442 if (def_media == VFD_MEDIA_NONE) {
1443
1444 // image is too small for the smallest media
1445
1446 PrintMessage(MSG_IMAGE_TOO_SMALL);
1447 return VFD_NG;
1448 }
1449
1450 if (media_type == VFD_MEDIA_NONE) {
1451
1452 // media type is not specified
1453
1454 ULONG def_size = VfdGetMediaSize(def_media);
1455
1456 if (def_size != image_size) {
1457
1458 // image size does not match the largest media size
1459
1460 PrintMessage(MSG_NO_MATCHING_MEDIA, image_size);
1461
1462 if (mode == OPERATION_FORCE) {
1463 PrintMessage(MSG_MEDIATYPE_NOTICE,
1464 VfdMediaTypeName(def_media), def_size);
1465 }
1466 else if (mode == OPERATION_QUIT) {
1467 return VFD_NG;
1468 }
1469 else {
1470 PrintMessage(MSG_MEDIATYPE_SUGGEST,
1471 VfdMediaTypeName(def_media), def_size);
1472
1473 if (InputChar(MSG_MEDIATYPE_CONFIRM, "yn") == 'n') {
1474 return VFD_NG;
1475 }
1476 }
1477 }
1478
1479 media_type = def_media;
1480 }
1481 }
1482
1483 // check file attributes against the disk type
1484
1485 if (file_type == VFD_FILETYPE_ZIP ||
1486 (file_attr & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_ENCRYPTED))) {
1487
1488 if (disk_type != VFD_DISKTYPE_RAM) {
1489
1490 if (mode == OPERATION_FORCE) {
1491 PrintMessage(MSG_RAM_MODE_NOTICE);
1492 }
1493 else {
1494 PrintMessage(MSG_RAM_MODE_ONLY);
1495
1496 if (mode == OPERATION_QUIT ||
1497 InputChar(MSG_RAM_MODE_CONFIRM, "yn") == 'n') {
1498 return VFD_NG;
1499 }
1500 }
1501
1502 disk_type = VFD_DISKTYPE_RAM;
1503 }
1504 }
1505
1506 if (disk_type != VFD_DISKTYPE_FILE) {
1507 if (!protect) {
1508 PrintMessage(MSG_DEFAULT_PROTECT);
1509 protect = 'P';
1510 }
1511 }
1512 }
1513 else {
1514 //
1515 // pure RAM disk
1516 //
1517 disk_type = VFD_DISKTYPE_RAM;
1518
1519 if (media_type == VFD_MEDIA_NONE) {
1520
1521 if (mode == OPERATION_FORCE) {
1522 PrintMessage(MSG_CREATE144_NOTICE);
1523 }
1524 else {
1525 PrintMessage(MSG_RAM_MEDIA_UNKNOWN);
1526
1527 if (mode == OPERATION_QUIT ||
1528 InputChar(MSG_CREATE144_CONFIRM, "yn") == 'n') {
1529 return VFD_NG;
1530 }
1531 }
1532
1533 media_type = VFD_MEDIA_F3_1P4;
1534 }
1535 }
1536
1537 if (protect == 'P') {
1538 media_flags |= VFD_FLAG_WRITE_PROTECTED;
1539 }
1540
1541 if (five_inch &&
1542 VfdGetMediaSize(media_type) ==
1543 VfdGetMediaSize((VFD_MEDIA)(media_type + 1))) {
1544 media_type = (VFD_MEDIA)(media_type + 1);
1545 }
1546
1547 // ensure that the driver is installed
1548
1549 if (driver_state == VFD_NOT_INSTALLED &&
1550 Install(NULL) != VFD_OK) {
1551 return VFD_NG;
1552 }
1553
1554 // ensure that the driver is up to date
1555
1556 if (CheckDriver() != VFD_OK) {
1557 return VFD_NG;
1558 }
1559
1560 // ensure that the driver is running
1561
1562 if (driver_state != SERVICE_RUNNING &&
1563 Start(NULL) != VFD_OK) {
1564 return VFD_NG;
1565 }
1566
1567 // Open the target device
1568
1569 hDevice = VfdOpenDevice(target);
1570
1571 if (hDevice == INVALID_HANDLE_VALUE) {
1572 ret = GetLastError();
1573 PrintMessage(MSG_ACCESS_NG, target);
1574 printf("%s", SystemError(ret));
1575 return VFD_NG;
1576 }
1577
1578 // Ensure that the drive is empty
1579
1580 ret = VfdGetMediaState(hDevice);
1581
1582 if (ret != ERROR_NOT_READY) {
1583 if (ret == ERROR_SUCCESS ||
1584 ret == ERROR_WRITE_PROTECT) {
1585 PrintMessage(MSG_DRIVE_BUSY);
1586 }
1587 else {
1588 PrintMessage(MSG_GET_MEDIA_NG);
1589 printf("%s", SystemError(ret));
1590 }
1591
1592 CloseHandle(hDevice);
1593 return VFD_NG;
1594 }
1595
1596 // Open the image file
1597
1598 ret = VfdOpenImage(hDevice, file_name,
1599 disk_type, media_type, media_flags);
1600
1601 if (ret != ERROR_SUCCESS) {
1602 PrintMessage(MSG_OPEN_NG, file_name ? file_name : "<RAM>");
1603 printf("%s", SystemError(ret));
1604
1605 CloseHandle(hDevice);
1606 return VFD_NG;
1607 }
1608
1609 // assign a drive letter if the drive has none
1610
1611 VfdGetGlobalLink(hDevice, &letter);
1612
1613 if (!isalpha(letter)) {
1614 VfdGetLocalLink(hDevice, &letter);
1615 }
1616
1617 if (!isalpha(letter)) {
1618 VfdSetLocalLink(hDevice, VfdChooseLetter());
1619 }
1620
1621 // Get the actually opened image information.
1622
1623 PrintImageInfo(hDevice);
1624
1625 CloseHandle(hDevice);
1626
1627 return VFD_OK;
1628 }
1629
1630 //
1631 // Close the current virtual floppy image
1632 // Command Line Parameters:
1633 // drive number or drive letter
1634 // /F | /FORCE | /Q | /QUIT
1635 //
1636 int Close(const char **args)
1637 {
1638 ULONG mode = OPERATION_ASK;
1639
1640 ULONG target_min = TARGET_NONE;
1641 ULONG target_max = TARGET_NONE;
1642 HANDLE hDevice;
1643
1644 VFD_MEDIA media_type;
1645 VFD_FLAGS media_flags;
1646
1647 DWORD ret;
1648
1649 // check parameterS
1650
1651 while (args && *args) {
1652
1653 if (!_stricmp(*args, "/f") ||
1654 !_stricmp(*args, "/force")) {
1655
1656 if (mode != OPERATION_ASK) {
1657 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1658 return VFD_NG;
1659 }
1660
1661 mode = OPERATION_FORCE;
1662 }
1663 else if (!_stricmp(*args, "/q") ||
1664 !_stricmp(*args, "/quit")) {
1665
1666 if (mode != OPERATION_ASK) {
1667 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1668 return VFD_NG;
1669 }
1670
1671 mode = OPERATION_QUIT;
1672 }
1673 else if ((isalnum(**args) || **args == '*') &&
1674 (*(*args + 1) == ':' || *(*args + 1) == '\0')) {
1675
1676 if (target_min != TARGET_NONE) {
1677 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1678 return VFD_NG;
1679 }
1680
1681 if (**args == '*') {
1682 target_min = '0';
1683 target_max = '0' + VFD_MAXIMUM_DEVICES;
1684 }
1685 else {
1686 target_min = toupper(**args);
1687 target_max = target_min + 1;
1688 }
1689 }
1690 else {
1691 PrintMessage(MSG_UNKNOWN_OPTION, *args);
1692 PrintMessage(MSG_HINT_CLOSE, help_progname);
1693 return VFD_NG;
1694 }
1695
1696 args++;
1697 }
1698
1699 if (target_min == TARGET_NONE) {
1700 // default target = drive 0
1701 target_min = '0';
1702 target_max = '1';
1703 PrintMessage(MSG_TARGET_NOTICE, target_min);
1704 }
1705
1706 // ensure that the driver is installed
1707
1708 if (driver_state == VFD_NOT_INSTALLED) {
1709 PrintMessage(MSG_NOT_INSTALLED);
1710 return VFD_NG;
1711 }
1712
1713 // ensure that the driver is running
1714
1715 if (driver_state != SERVICE_RUNNING) {
1716 PrintMessage(MSG_NOT_STARTED);
1717 return VFD_NG;
1718 }
1719
1720 // Close the drive(s)
1721
1722 while (target_min < target_max) {
1723
1724 // open the target device
1725
1726 hDevice = VfdOpenDevice(target_min);
1727
1728 if (hDevice == INVALID_HANDLE_VALUE) {
1729 ret = GetLastError();
1730
1731 PrintMessage(MSG_ACCESS_NG, target_min);
1732 printf("%s", SystemError(ret));
1733
1734 if (mode != OPERATION_FORCE) {
1735 return VFD_NG;
1736 }
1737
1738 target_min++;
1739 continue;
1740 }
1741
1742 // get the current image information
1743
1744 ret = VfdGetImageInfo(hDevice, NULL, NULL,
1745 &media_type, &media_flags, NULL, NULL);
1746
1747 if (ret != ERROR_SUCCESS) {
1748 PrintMessage(MSG_ACCESS_NG, target_min);
1749 printf("%s", SystemError(ret));
1750
1751 CloseHandle(hDevice);
1752
1753 if (mode != OPERATION_FORCE) {
1754 return VFD_NG;
1755 }
1756
1757 target_min++;
1758 continue;
1759 }
1760
1761 if (media_type == VFD_MEDIA_NONE) {
1762
1763 // drive is empty
1764
1765 CloseHandle(hDevice);
1766 target_min++;
1767 continue;
1768 }
1769
1770 if (media_flags & VFD_FLAG_DATA_MODIFIED) {
1771
1772 // RAM disk data is modified
1773
1774 PrintMessage(MSG_MEDIA_MODIFIED, target_min);
1775
1776 if (mode == OPERATION_FORCE) {
1777 PrintMessage(MSG_CLOSE_FORCE);
1778 }
1779 else if (mode == OPERATION_QUIT) {
1780 PrintMessage(MSG_CLOSE_QUIT);
1781 CloseHandle(hDevice);
1782 return VFD_NG;
1783 }
1784 else {
1785 if (InputChar(MSG_CLOSE_CONFIRM, "yn") == 'n') {
1786 CloseHandle(hDevice);
1787 return VFD_NG;
1788 }
1789 }
1790 }
1791
1792 retry:
1793 ret = VfdCloseImage(
1794 hDevice, (mode == OPERATION_FORCE));
1795
1796 if (ret == ERROR_ACCESS_DENIED) {
1797
1798 PrintMessage(MSG_LOCK_NG, target_min);
1799
1800 if (mode == OPERATION_QUIT) {
1801 CloseHandle(hDevice);
1802 return VFD_NG;
1803 }
1804 else if (mode == OPERATION_ASK) {
1805
1806 int c;
1807
1808 if (IS_WINDOWS_NT()) {
1809 c = InputChar(MSG_RETRY_CANCEL, "rc");
1810 }
1811 else {
1812 c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
1813 }
1814
1815 if (c == 'f') { // force
1816 ret = VfdCloseImage(hDevice, TRUE);
1817 }
1818 else if (c == 'c') { // cancel
1819 CloseHandle(hDevice);
1820 return VFD_NG;
1821 }
1822 else {
1823 goto retry;
1824 }
1825 }
1826 }
1827
1828 CloseHandle(hDevice);
1829
1830 if (ret == ERROR_SUCCESS) {
1831 PrintMessage(MSG_CLOSE_OK, target_min);
1832 }
1833 else if (ret != ERROR_NOT_READY) {
1834 PrintMessage(MSG_CLOSE_NG, target_min);
1835 printf("%s", SystemError(ret));
1836
1837 if (mode != OPERATION_FORCE) {
1838 return VFD_NG;
1839 }
1840 }
1841
1842 target_min++;
1843 }
1844
1845 return VFD_OK;
1846 }
1847
1848 //
1849 // Save the current image into a file
1850 //
1851 int Save(const char **args)
1852 {
1853 int mode = OPERATION_ASK;
1854 ULONG target = TARGET_NONE;
1855 CHAR file_name[MAX_PATH] = {0};
1856 BOOL overwrite = FALSE;
1857 BOOL truncate = FALSE;
1858
1859 HANDLE hDevice;
1860 CHAR current[MAX_PATH] = {0};
1861 VFD_MEDIA media_type;
1862 VFD_FLAGS media_flags;
1863 VFD_FILETYPE file_type;
1864 DWORD file_attr;
1865 ULONG image_size;
1866 DWORD ret;
1867
1868 // check parameters
1869
1870 while (args && *args) {
1871
1872 if (!_stricmp(*args, "/f") ||
1873 !_stricmp(*args, "/force")) {
1874
1875 if (mode != OPERATION_ASK) {
1876 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1877 return VFD_NG;
1878 }
1879
1880 mode = OPERATION_FORCE;
1881 }
1882 else if (!_stricmp(*args, "/q") ||
1883 !_stricmp(*args, "/quit")) {
1884
1885 if (mode != OPERATION_ASK) {
1886 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1887 return VFD_NG;
1888 }
1889
1890 mode = OPERATION_QUIT;
1891 }
1892 else if (!_stricmp(*args, "/o") ||
1893 !_stricmp(*args, "/over")) {
1894
1895 if (truncate || overwrite) {
1896 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1897 return VFD_NG;
1898 }
1899
1900 overwrite = TRUE;
1901 }
1902 else if (!_stricmp(*args, "/t") ||
1903 !_stricmp(*args, "/trunc")) {
1904
1905 if (truncate || overwrite) {
1906 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1907 return VFD_NG;
1908 }
1909
1910 truncate = TRUE;
1911 }
1912 else if (isalnum(**args) &&
1913 *(*args + 1) == ':' &&
1914 *(*args + 2) == '\0') {
1915
1916 if (target != TARGET_NONE) {
1917 PrintMessage(MSG_DUPLICATE_ARGS, *args);
1918 return VFD_NG;
1919 }
1920
1921 target = toupper(**args);
1922 }
1923 else if (**args == '/') {
1924 PrintMessage(MSG_UNKNOWN_OPTION, *args);
1925 PrintMessage(MSG_HINT_SAVE, help_progname);
1926 return VFD_NG;
1927 }
1928 else {
1929 strcpy(file_name, *args);
1930 }
1931
1932 args++;
1933 }
1934
1935 if (target == TARGET_NONE) {
1936 target = '0';
1937 PrintMessage(MSG_TARGET_NOTICE, target);
1938 }
1939
1940 // ensure that the driver is installed
1941
1942 if (driver_state == VFD_NOT_INSTALLED) {
1943 PrintMessage(MSG_NOT_INSTALLED);
1944 return VFD_NG;
1945 }
1946
1947 // ensure that the driver is up to date
1948
1949 if (CheckDriver() != VFD_OK) {
1950 return VFD_NG;
1951 }
1952
1953 // ensure that the driver is running
1954
1955 if (driver_state != SERVICE_RUNNING) {
1956 PrintMessage(MSG_NOT_STARTED);
1957 return VFD_NG;
1958 }
1959
1960 // Open the target device
1961
1962 hDevice = VfdOpenDevice(target);
1963
1964 if (hDevice == INVALID_HANDLE_VALUE) {
1965 ret = GetLastError();
1966 PrintMessage(MSG_ACCESS_NG, target);
1967 printf("%s", SystemError(ret));
1968 return VFD_NG;
1969 }
1970
1971 // Get the current image info
1972
1973 ret = VfdGetImageInfo(hDevice, current, NULL,
1974 &media_type, &media_flags, NULL, NULL);
1975
1976 if (ret != ERROR_SUCCESS) {
1977 printf("%s", SystemError(ret));
1978 CloseHandle(hDevice);
1979 return VFD_NG;
1980 }
1981
1982 if (media_type == VFD_MEDIA_NONE) {
1983 printf("%s", SystemError(ERROR_NOT_READY));
1984 CloseHandle(hDevice);
1985 return VFD_NG;
1986 }
1987
1988 if (file_name[0] == '\0') {
1989
1990 if (current[0] == '\0') {
1991
1992 PrintMessage(MSG_TARGET_REQUIRED);
1993 CloseHandle(hDevice);
1994
1995 return VFD_NG;
1996 }
1997
1998 strcpy(file_name, current);
1999 }
2000
2001 if (!_stricmp(file_name, current)) {
2002
2003 // target is the current image file
2004
2005 if (!(media_flags & VFD_FLAG_DATA_MODIFIED)) {
2006
2007 // FILE disk (always up to date) or RAM disk is not modified
2008
2009 PrintMessage(MSG_TARGET_UP_TO_DATE);
2010 CloseHandle(hDevice);
2011
2012 return VFD_OK;
2013 }
2014
2015 overwrite = TRUE;
2016 }
2017
2018 // check target file
2019
2020 ret = VfdCheckImageFile(file_name,
2021 &file_attr, &file_type, &image_size);
2022
2023 if (ret == ERROR_SUCCESS) {
2024
2025 if (!overwrite && !truncate) {
2026
2027 if (mode == OPERATION_FORCE) {
2028 PrintMessage(MSG_OVERWRITE_NOTICE);
2029 overwrite = TRUE;
2030 }
2031 else if (mode == OPERATION_QUIT) {
2032 printf("%s", SystemError(ERROR_FILE_EXISTS));
2033 CloseHandle(hDevice);
2034
2035 return VFD_NG;
2036 }
2037 else {
2038 int c;
2039
2040 printf("%s", SystemError(ERROR_FILE_EXISTS));
2041
2042 c = InputChar(MSG_OVERWRITE_PROMPT, "otc");
2043
2044 if (c == 'o') {
2045 overwrite = TRUE;
2046 }
2047 else if (c == 't') {
2048 truncate = TRUE;
2049 }
2050 else {
2051 CloseHandle(hDevice);
2052 return VFD_NG;
2053 }
2054 }
2055 }
2056 }
2057 else if (ret != ERROR_FILE_NOT_FOUND) {
2058
2059 printf("%s", SystemError(ret));
2060 CloseHandle(hDevice);
2061
2062 return VFD_NG;
2063 }
2064
2065 if (file_type == VFD_FILETYPE_ZIP) {
2066
2067 // Cannot update a zip file
2068
2069 PrintMessage(MSG_TARGET_IS_ZIP);
2070 CloseHandle(hDevice);
2071
2072 return VFD_NG;
2073 }
2074
2075 retry:
2076 ret = VfdDismountVolume(
2077 hDevice, (mode == OPERATION_FORCE));
2078
2079 if (ret == ERROR_ACCESS_DENIED) {
2080
2081 PrintMessage(MSG_LOCK_NG, target);
2082
2083 if (mode == OPERATION_FORCE) {
2084 PrintMessage(MSG_SAVE_FORCE);
2085 }
2086 else if (mode == OPERATION_QUIT) {
2087 PrintMessage(MSG_SAVE_QUIT);
2088 CloseHandle(hDevice);
2089 return VFD_NG;
2090 }
2091 else {
2092 int c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
2093
2094 if (c == 'r') { // retry
2095 goto retry;
2096 }
2097 else if (c == 'f') { // force
2098 VfdDismountVolume(hDevice, TRUE);
2099 }
2100 else { // cancel
2101 CloseHandle(hDevice);
2102 return VFD_NG;
2103 }
2104 }
2105 }
2106 else if (ret != ERROR_SUCCESS) {
2107 printf("%s", SystemError(ret));
2108 CloseHandle(hDevice);
2109 return VFD_NG;
2110 }
2111
2112 ret = VfdSaveImage(hDevice, file_name,
2113 (overwrite || truncate), truncate);
2114
2115 CloseHandle(hDevice);
2116
2117 if (ret != ERROR_SUCCESS) {
2118 PrintMessage(MSG_SAVE_NG, target, file_name);
2119 printf("%s", SystemError(ret));
2120
2121 return VFD_NG;
2122 }
2123
2124 PrintMessage(MSG_SAVE_OK, target, file_name);
2125
2126 return VFD_OK;
2127 }
2128
2129 //
2130 // Enable/disable virtual media write protection
2131 //
2132 int Protect(const char **args)
2133 {
2134 #define PROTECT_NONE 0
2135 #define PROTECT_ON 1
2136 #define PROTECT_OFF 2
2137 ULONG protect = PROTECT_NONE;
2138 ULONG target = TARGET_NONE;
2139 HANDLE hDevice;
2140 DWORD ret;
2141
2142 // check parameters
2143
2144 while (args && *args) {
2145
2146 // Disk type options
2147
2148 if (_stricmp(*args, "/on") == 0) {
2149
2150 if (protect) {
2151 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2152 return VFD_NG;
2153 }
2154
2155 protect = PROTECT_ON;
2156 }
2157 else if (_stricmp(*args, "/off") == 0) {
2158
2159 if (protect) {
2160 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2161 return VFD_NG;
2162 }
2163
2164 protect = PROTECT_OFF;
2165 }
2166 else if (isalnum(**args)) {
2167
2168 if (target != TARGET_NONE) {
2169 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2170 return VFD_NG;
2171 }
2172
2173 target = toupper(**args);
2174 }
2175 else {
2176 PrintMessage(MSG_UNKNOWN_OPTION, *args);
2177 PrintMessage(MSG_HINT_PROTECT, help_progname);
2178 return VFD_NG;
2179 }
2180
2181 args++;
2182 }
2183
2184 if (target == TARGET_NONE) {
2185 target = '0';
2186 PrintMessage(MSG_TARGET_NOTICE, target);
2187 }
2188
2189 // ensure that the driver is installed
2190
2191 if (driver_state == VFD_NOT_INSTALLED) {
2192 PrintMessage(MSG_NOT_INSTALLED);
2193 return VFD_NG;
2194 }
2195
2196 // ensure that the driver is up to date
2197
2198 if (CheckDriver() != VFD_OK) {
2199 return VFD_NG;
2200 }
2201
2202 // ensure that the driver is running
2203
2204 if (driver_state != SERVICE_RUNNING) {
2205 PrintMessage(MSG_NOT_STARTED);
2206 return VFD_NG;
2207 }
2208
2209 // open the target drive
2210
2211 hDevice = VfdOpenDevice(target);
2212
2213 if (hDevice == INVALID_HANDLE_VALUE) {
2214 ret = GetLastError();
2215 PrintMessage(MSG_ACCESS_NG, target);
2216 printf("%s", SystemError(ret));
2217 return VFD_NG;
2218 }
2219
2220 if (protect) {
2221 // change protect state
2222
2223 ret = VfdWriteProtect(
2224 hDevice, (protect == PROTECT_ON));
2225
2226 if (ret != ERROR_SUCCESS) {
2227 PrintMessage(MSG_PROTECT_NG, target);
2228 printf("%s", SystemError(ret));
2229
2230 CloseHandle(hDevice);
2231 return VFD_NG;
2232 }
2233 }
2234
2235 // get the current protect state
2236
2237 ret = VfdGetMediaState(hDevice);
2238
2239 CloseHandle(hDevice);
2240
2241 if (ret == ERROR_SUCCESS) {
2242 PrintMessage(MSG_MEDIA_WRITABLE);
2243 }
2244 else if (ret == ERROR_WRITE_PROTECT) {
2245 PrintMessage(MSG_MEDIA_PROTECTED);
2246 }
2247 else {
2248 PrintMessage(MSG_GET_MEDIA_NG);
2249 printf("%s", SystemError(ret));
2250 return VFD_NG;
2251 }
2252
2253 return VFD_OK;
2254 }
2255
2256 //
2257 // Format the virtual media with FAT12
2258 //
2259 int Format(const char **args)
2260 {
2261 int mode = OPERATION_ASK;
2262 ULONG target = TARGET_NONE;
2263 HANDLE hDevice;
2264 DWORD ret;
2265
2266 // check parameters
2267
2268 while (args && *args) {
2269
2270 if (!_stricmp(*args, "/f") ||
2271 !_stricmp(*args, "/force")) {
2272
2273 if (mode != OPERATION_ASK) {
2274 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2275 return VFD_NG;
2276 }
2277
2278 mode = OPERATION_FORCE;
2279 }
2280 else if (!_stricmp(*args, "/q") ||
2281 !_stricmp(*args, "/quit")) {
2282
2283 if (mode != OPERATION_ASK) {
2284 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2285 return VFD_NG;
2286 }
2287
2288 mode = OPERATION_QUIT;
2289 }
2290 else if (isalnum(**args)) {
2291 if (target != TARGET_NONE) {
2292 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2293 return VFD_NG;
2294 }
2295
2296 target = toupper(**args);
2297 }
2298 else {
2299 PrintMessage(MSG_UNKNOWN_OPTION, *args);
2300 PrintMessage(MSG_HINT_FORMAT, help_progname);
2301 return VFD_NG;
2302 }
2303
2304 args++;
2305 }
2306
2307 if (target == TARGET_NONE) {
2308 target = '0';
2309 PrintMessage(MSG_TARGET_NOTICE, target);
2310 }
2311
2312 // ensure that the driver is installed
2313
2314 if (driver_state == VFD_NOT_INSTALLED) {
2315 PrintMessage(MSG_NOT_INSTALLED);
2316 return VFD_NG;
2317 }
2318
2319 // ensure that the driver is up to date
2320
2321 if (CheckDriver() != VFD_OK) {
2322 return VFD_NG;
2323 }
2324
2325 // ensure that the driver is running
2326
2327 if (driver_state != SERVICE_RUNNING) {
2328 PrintMessage(MSG_NOT_STARTED);
2329 return VFD_NG;
2330 }
2331
2332 // Open the device
2333
2334 hDevice = VfdOpenDevice(target);
2335
2336 if (hDevice == INVALID_HANDLE_VALUE) {
2337 ret = GetLastError();
2338 PrintMessage(MSG_ACCESS_NG, target);
2339 printf("%s", SystemError(ret));
2340 return VFD_NG;
2341 }
2342
2343 // check if the media is writable
2344
2345 ret = VfdGetMediaState(hDevice);
2346
2347 if (ret != ERROR_SUCCESS) {
2348 PrintMessage(MSG_FORMAT_NG, target);
2349 printf("%s", SystemError(ret));
2350
2351 CloseHandle(hDevice);
2352 return VFD_NG;
2353 }
2354
2355 // format the media
2356
2357 retry:
2358 ret = VfdDismountVolume(
2359 hDevice, (mode == OPERATION_FORCE));
2360
2361 if (ret == ERROR_ACCESS_DENIED) {
2362
2363 PrintMessage(MSG_LOCK_NG, target);
2364
2365 if (mode == OPERATION_FORCE) {
2366 PrintMessage(MSG_FORMAT_FORCE);
2367 }
2368 else if (mode == OPERATION_QUIT) {
2369 PrintMessage(MSG_FORMAT_QUIT);
2370 CloseHandle(hDevice);
2371 return VFD_NG;
2372 }
2373 else {
2374 int c = InputChar(MSG_RETRY_FORCE_CANCEL, "rfc");
2375
2376 if (c == 'r') { // retry
2377 goto retry;
2378 }
2379 else if (c == 'f') { // force
2380 VfdDismountVolume(hDevice, TRUE);
2381 }
2382 else { // cancel
2383 CloseHandle(hDevice);
2384 return VFD_NG;
2385 }
2386 }
2387 }
2388 else if (ret != ERROR_SUCCESS) {
2389 PrintMessage(MSG_LOCK_NG, target);
2390 CloseHandle(hDevice);
2391 return VFD_NG;
2392 }
2393
2394 ret = VfdFormatMedia(hDevice);
2395
2396 CloseHandle(hDevice);
2397
2398 if (ret != ERROR_SUCCESS) {
2399 PrintMessage(MSG_FORMAT_NG, target);
2400 printf("%s", SystemError(ret));
2401 return VFD_NG;
2402 }
2403
2404 // successful operation
2405
2406 PrintMessage(MSG_FORMAT_OK);
2407
2408 return VFD_OK;
2409 }
2410
2411 //
2412 // Assign a drive letter to a Virtual Floppy Drive
2413 //
2414 int Link(const char **args)
2415 {
2416 ULONG target_min = TARGET_NONE;
2417 ULONG target_max = TARGET_NONE;
2418 PCSTR letters = NULL;
2419 BOOL global = TRUE;
2420 HANDLE hDevice;
2421 DWORD ret;
2422
2423 while (args && *args) {
2424 if (!_stricmp(*args, "/g")) {
2425 global = TRUE;
2426 }
2427 else if (!_stricmp(*args, "/l")) {
2428 global = FALSE;
2429 }
2430 else if (isdigit(**args) || **args == '*') {
2431 if (target_min != TARGET_NONE) {
2432 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2433 return VFD_NG;
2434 }
2435
2436 if (**args == '*') {
2437 target_min = '0';
2438 target_max = '0' + VFD_MAXIMUM_DEVICES;
2439 }
2440 else {
2441 target_min = **args;
2442 target_max = target_min + 1;
2443 }
2444 }
2445 else if (isalpha(**args)) {
2446 if (letters) {
2447 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2448 return VFD_NG;
2449 }
2450 letters = *args;
2451 }
2452 else {
2453 PrintMessage(MSG_UNKNOWN_OPTION, *args);
2454 PrintMessage(MSG_HINT_LINK, help_progname);
2455 return VFD_NG;
2456 }
2457
2458 args++;
2459 }
2460
2461 if (target_min == TARGET_NONE) {
2462 // default: drive 0
2463 target_min = '0';
2464 target_max = '1';
2465 PrintMessage(MSG_TARGET_NOTICE, target_min);
2466 }
2467
2468 // ensure that the driver is installed
2469
2470 if (driver_state == VFD_NOT_INSTALLED) {
2471 PrintMessage(MSG_NOT_INSTALLED);
2472 return VFD_NG;
2473 }
2474
2475 // ensure that the driver is up to date
2476
2477 if (CheckDriver() != VFD_OK) {
2478 return VFD_NG;
2479 }
2480
2481 // ensure that the driver is running
2482
2483 if (driver_state != SERVICE_RUNNING) {
2484 PrintMessage(MSG_NOT_STARTED);
2485 return VFD_NG;
2486 }
2487
2488 while (target_min < target_max) {
2489 ULONG number;
2490 CHAR letter;
2491
2492 hDevice = VfdOpenDevice(target_min);
2493
2494 if (hDevice == INVALID_HANDLE_VALUE) {
2495 ret = GetLastError();
2496 PrintMessage(MSG_ACCESS_NG, target_min);
2497 printf("%s", SystemError(ret));
2498 target_min++;
2499 continue;
2500 }
2501
2502 ret = VfdGetDeviceNumber(hDevice, &number);
2503
2504 if (ret != ERROR_SUCCESS) {
2505 PrintMessage(MSG_ACCESS_NG, target_min);
2506 printf("%s", SystemError(ret));
2507 CloseHandle(hDevice);
2508 target_min++;
2509 continue;
2510 }
2511
2512 if (letters && isalpha(*letters)) {
2513 letter = (CHAR)toupper(*(letters++));
2514 }
2515 else {
2516 letter = VfdChooseLetter();
2517 }
2518
2519 if (letter) {
2520 if (global) {
2521 ret = VfdSetGlobalLink(hDevice, letter);
2522 }
2523 else {
2524 ret = VfdSetLocalLink(hDevice, letter);
2525 }
2526
2527 if (ret != ERROR_SUCCESS) {
2528 PrintMessage(MSG_LINK_NG, number, letter);
2529 printf("%s", SystemError(ret));
2530 }
2531 }
2532 else {
2533 PrintMessage(MSG_LINK_FULL);
2534 }
2535
2536 PrintDriveLetter(hDevice, number);
2537
2538 CloseHandle(hDevice);
2539
2540 target_min++;
2541 }
2542
2543 return VFD_OK;
2544 }
2545
2546 //
2547 // Remove a drive letter from a Virtual Floppy Drive
2548 //
2549 int Unlink(const char **args)
2550 {
2551 ULONG target_min = TARGET_NONE;
2552 ULONG target_max = TARGET_NONE;
2553 HANDLE hDevice;
2554 DWORD ret;
2555
2556 while (args && *args) {
2557 if ((isalnum(**args) || **args == '*') &&
2558 (*(*args + 1) == ':' || *(*args + 1) == '\0')) {
2559
2560 if (target_min != TARGET_NONE) {
2561 PrintMessage(MSG_DUPLICATE_ARGS, *args);
2562 return VFD_NG;
2563 }
2564
2565 if (**args == '*') {
2566 target_min = '0';
2567 target_max = '0' + VFD_MAXIMUM_DEVICES;
2568 }
2569 else {
2570 target_min = **args;
2571 target_max = target_min + 1;
2572 }
2573 }
2574 else {
2575 PrintMessage(MSG_UNKNOWN_OPTION, *args);
2576 PrintMessage(MSG_HINT_ULINK, help_progname);
2577 return VFD_NG;
2578 }
2579
2580 args++;
2581 }
2582
2583 if (target_min == TARGET_NONE) {
2584 // default: drive 0
2585 target_min = '0';
2586 target_max = '1';
2587 PrintMessage(MSG_TARGET_NOTICE, target_min);
2588 }
2589
2590 // ensure that the driver is installed
2591
2592 if (driver_state == VFD_NOT_INSTALLED) {
2593 PrintMessage(MSG_NOT_INSTALLED);
2594 return VFD_NG;
2595 }
2596
2597 // ensure that the driver is up to date
2598
2599 if (CheckDriver() != VFD_OK) {
2600 return VFD_NG;
2601 }
2602
2603 // ensure that the driver is running
2604
2605 if (driver_state != SERVICE_RUNNING) {
2606 PrintMessage(MSG_NOT_STARTED);
2607 return VFD_NG;
2608 }
2609
2610 while (target_min < target_max) {
2611 ULONG number;
2612
2613 hDevice = VfdOpenDevice(target_min);
2614
2615 if (hDevice == INVALID_HANDLE_VALUE) {
2616 ret = GetLastError();
2617 PrintMessage(MSG_ACCESS_NG, target_min);
2618 printf("%s", SystemError(ret));
2619 target_min++;
2620 continue;
2621 }
2622
2623 ret = VfdGetDeviceNumber(hDevice, &number);
2624
2625 if (ret != ERROR_SUCCESS) {
2626 PrintMessage(MSG_ACCESS_NG, target_min);
2627 printf("%s", SystemError(ret));
2628 CloseHandle(hDevice);
2629 target_min++;
2630 continue;
2631 }
2632
2633 VfdSetGlobalLink(hDevice, 0);
2634 VfdSetLocalLink(hDevice, 0);
2635
2636 PrintDriveLetter(hDevice, number);
2637
2638 CloseHandle(hDevice);
2639
2640 target_min++;
2641 }
2642
2643 return VFD_OK;
2644 }
2645
2646 //
2647 // Print current driver state
2648 // Command Line Parameters: None
2649 //
2650 int Status(const char **args)
2651 {
2652 HANDLE hDevice;
2653 TCHAR path[MAX_PATH];
2654 DWORD start_type;
2655 DWORD version;
2656 ULONG target;
2657 DWORD ret;
2658
2659 UNREFERENCED_PARAMETER(args);
2660
2661 if (driver_state == VFD_NOT_INSTALLED) {
2662 PrintMessage(MSG_NOT_INSTALLED);
2663 }
2664 else {
2665
2666 // get current driver config
2667
2668 ret = VfdGetDriverConfig(path, &start_type);
2669
2670 if (ret != ERROR_SUCCESS) {
2671 PrintMessage(MSG_GET_CONFIG_NG);
2672 printf("%s", SystemError(ret));
2673 return VFD_NG;
2674 }
2675
2676 // print driver file path
2677
2678 PrintMessage(MSG_DRIVER_FILE, path);
2679
2680 // print driver version
2681 version = 0;
2682
2683 if (driver_state == SERVICE_RUNNING) {
2684
2685 hDevice = VfdOpenDevice(0);
2686
2687 if (hDevice != INVALID_HANDLE_VALUE) {
2688 ret = VfdGetDriverVersion(hDevice, &version);
2689
2690 CloseHandle(hDevice);
2691 }
2692
2693 }
2694
2695 if (version == 0) {
2696 ret = VfdCheckDriverFile(path, &version);
2697 }
2698
2699 if (ret == ERROR_SUCCESS) {
2700 PrintMessage(MSG_DRIVER_VERSION,
2701 HIWORD(version) & 0x7fff,
2702 LOWORD(version),
2703 (version & 0x80000000) ? "(debug)" : "");
2704 }
2705 else {
2706 PrintMessage(MSG_GET_VERSION_NG);
2707 printf("%s", SystemError(ret));
2708 }
2709
2710
2711 // print driver start type
2712
2713 PrintMessage(MSG_START_TYPE);
2714
2715 switch (start_type) {
2716 case SERVICE_AUTO_START:
2717 PrintMessage(MSG_START_AUTO);
2718 break;
2719
2720 case SERVICE_BOOT_START:
2721 PrintMessage(MSG_START_BOOT);
2722 break;
2723
2724 case SERVICE_DEMAND_START:
2725 PrintMessage(MSG_START_DEMAND);
2726 break;
2727
2728 case SERVICE_DISABLED:
2729 PrintMessage(MSG_START_DISABLED);
2730 break;
2731
2732 case SERVICE_SYSTEM_START :
2733 PrintMessage(MSG_START_SYSTEM);
2734 break;
2735
2736 default:
2737 PrintMessage(MSG_UNKNOWN_LONG, start_type);
2738 break;
2739 }
2740
2741 // print current driver state
2742
2743 PrintMessage(MSG_DRIVER_STATUS);
2744
2745 switch (driver_state) {
2746 case SERVICE_STOPPED:
2747 PrintMessage(MSG_STATUS_STOPPED);
2748 break;
2749
2750 case SERVICE_START_PENDING:
2751 PrintMessage(MSG_STATUS_START_P);
2752 break;
2753
2754 case SERVICE_STOP_PENDING:
2755 PrintMessage(MSG_STATUS_STOP_P);
2756 break;
2757
2758 case SERVICE_RUNNING:
2759 PrintMessage(MSG_STATUS_RUNNING);
2760 break;
2761
2762 case SERVICE_CONTINUE_PENDING:
2763 PrintMessage(MSG_STATUS_CONT_P);
2764 break;
2765
2766 case SERVICE_PAUSE_PENDING:
2767 PrintMessage(MSG_STATUS_PAUSE_P);
2768 break;
2769
2770 case SERVICE_PAUSED:
2771 PrintMessage(MSG_STATUS_PAUSED);
2772 break;
2773
2774 default:
2775 PrintMessage(MSG_UNKNOWN_LONG, driver_state);
2776 break;
2777 }
2778 }
2779
2780 // print shell extension status
2781
2782 printf("\n");
2783
2784 if (VfdCheckHandlers() == ERROR_SUCCESS) {
2785 PrintMessage(MSG_SHELLEXT_ENABLED);
2786 }
2787 else {
2788 PrintMessage(MSG_SHELLEXT_DISABLED);
2789 }
2790
2791 // if driver is not running, no more info
2792
2793 if (driver_state != SERVICE_RUNNING) {
2794 return VFD_OK;
2795 }
2796
2797 // print image information
2798
2799 for (target = 0; target < VFD_MAXIMUM_DEVICES; target++) {
2800 HANDLE hDevice = VfdOpenDevice(target);
2801
2802 if (hDevice == INVALID_HANDLE_VALUE) {
2803 ret = GetLastError();
2804 PrintMessage(MSG_ACCESS_NG, target + '0');
2805 printf("%s", SystemError(ret));
2806 return VFD_NG;
2807 }
2808
2809 PrintImageInfo(hDevice);
2810
2811 CloseHandle(hDevice);
2812 }
2813
2814 return VFD_OK;
2815 }
2816
2817 //
2818 // Print usage help
2819 //
2820 int Help(const char **args)
2821 {
2822 DWORD msg = MSG_HELP_GENERAL;
2823 char *buf = NULL;
2824
2825 if (args && *args) {
2826 int cmd = ParseHelpTopic(*args);
2827
2828 if (cmd < 0) {
2829 msg = MSG_HELP_HELP;
2830 }
2831 else {
2832 msg = HelpMsg[cmd].help;
2833 }
2834 }
2835
2836 FormatMessage(
2837 FORMAT_MESSAGE_FROM_HMODULE |
2838 FORMAT_MESSAGE_ALLOCATE_BUFFER |
2839 FORMAT_MESSAGE_ARGUMENT_ARRAY,
2840 NULL, msg, 0, (LPTSTR)&buf, 0,
2841 (va_list *)&help_progname);
2842
2843 if (buf == NULL) {
2844 printf("%s", SystemError(GetLastError()));
2845 return VFD_NG;
2846 }
2847
2848 ConsolePager(buf, TRUE);
2849 LocalFree(buf);
2850
2851 return VFD_OK;
2852 }
2853
2854 //
2855 // Print version information
2856 //
2857 int Version(const char **args)
2858 {
2859 UNREFERENCED_PARAMETER(args);
2860
2861 printf(VFD_PRODUCT_DESC "\n" VFD_COPYRIGHT_STR "\n"
2862 "http://chitchat.at.infoseek.co.jp/vmware/vfd.html\n");
2863
2864 return VFD_OK;
2865 }
2866
2867 //
2868 // Parse command parameter
2869 //
2870 int ParseCommand(const char *cmd)
2871 {
2872 #define CMD_MATCH_NONE -1
2873 #define CMD_MATCH_MULTI -2
2874
2875 size_t len;
2876 int idx;
2877 int match;
2878
2879 // skip a leading '/'
2880
2881 if (*cmd == '/') {
2882 cmd++;
2883 }
2884
2885 if (*cmd == '\0') {
2886
2887 // empty command
2888
2889 return CMD_MATCH_NONE;
2890 }
2891
2892 // find a match
2893 len = strlen(cmd);
2894 idx = 0;
2895 match = CMD_MATCH_NONE;
2896
2897 while (Commands[idx].cmd) {
2898
2899 if (strlen(Commands[idx].cmd) >= len &&
2900 !_strnicmp(cmd, Commands[idx].cmd, len)) {
2901
2902 if (match == CMD_MATCH_NONE) { // first match
2903 match = idx;
2904 }
2905 else { // multiple matches
2906 if (match != CMD_MATCH_MULTI) { // first time
2907 PrintMessage(MSG_AMBIGUOUS_COMMAND, cmd);
2908 printf("> %s ", Commands[match].cmd);
2909 match = CMD_MATCH_MULTI;
2910 }
2911
2912 printf("%s ", Commands[idx].cmd);
2913 }
2914 }
2915
2916 idx++;
2917 }
2918
2919 if (match == CMD_MATCH_NONE) { // match not found
2920 PrintMessage(MSG_UNKNOWN_COMMAND, cmd);
2921 }
2922 else if (match == CMD_MATCH_MULTI) { // multiple matches
2923 printf("\n");
2924 }
2925
2926 return match;
2927 }
2928
2929 int ParseHelpTopic(const char *topic)
2930 {
2931 size_t len;
2932 int idx;
2933 int match;
2934
2935 if (*topic == '\0') {
2936
2937 // empty command
2938
2939 return CMD_MATCH_NONE;
2940 }
2941
2942 // find a match
2943 len = strlen(topic);
2944 idx = 0;
2945 match = CMD_MATCH_NONE;
2946
2947 while (HelpMsg[idx].keyword) {
2948
2949 if (strlen(HelpMsg[idx].keyword) >= len &&
2950 !_strnicmp(topic, HelpMsg[idx].keyword, len)) {
2951
2952 if (match == CMD_MATCH_NONE) { // first match
2953 match = idx;
2954 }
2955 else { // multiple matches
2956 if (match != CMD_MATCH_MULTI) { // first time
2957 PrintMessage(MSG_AMBIGUOUS_COMMAND, topic);
2958 printf("> %s ", HelpMsg[match].keyword);
2959 match = CMD_MATCH_MULTI;
2960 }
2961
2962 printf("%s ", HelpMsg[idx].keyword);
2963 }
2964 }
2965
2966 idx++;
2967 }
2968
2969 if (match == CMD_MATCH_NONE) { // match not found
2970 PrintMessage(MSG_UNKNOWN_COMMAND, topic);
2971 }
2972 else if (match == CMD_MATCH_MULTI) { // multiple matches
2973 printf("\n");
2974 }
2975
2976 return match;
2977 }
2978
2979 //
2980 // Check driver version and update if necessary
2981 //
2982 int CheckDriver()
2983 {
2984 char path[MAX_PATH];
2985 DWORD start;
2986
2987 // check installed driver file version
2988
2989 if (VfdGetDriverConfig(path, &start) == ERROR_SUCCESS &&
2990 VfdCheckDriverFile(path, NULL) == ERROR_SUCCESS) {
2991
2992 HANDLE hDevice;
2993
2994 if (driver_state != SERVICE_RUNNING) {
2995 return VFD_OK;
2996 }
2997
2998 // check running driver version
2999
3000 hDevice = VfdOpenDevice(0);
3001
3002 if (hDevice != INVALID_HANDLE_VALUE) {
3003 CloseHandle(hDevice);
3004 return VFD_OK;
3005 }
3006 }
3007
3008 PrintMessage(MSG_WRONG_DRIVER);
3009 return VFD_NG;
3010 }
3011
3012 //
3013 // Print a prompt message and accept the reply input
3014 //
3015 int InputChar(ULONG msg, PCSTR ans)
3016 {
3017 HANDLE hStdIn;
3018 INPUT_RECORD input;
3019 DWORD result;
3020 int reply;
3021
3022 PrintMessage(msg);
3023 fflush(NULL);
3024
3025 hStdIn = GetStdHandle(STD_INPUT_HANDLE);
3026
3027 FlushConsoleInputBuffer(hStdIn);
3028
3029 for (;;) {
3030 ReadConsoleInput(hStdIn, &input, sizeof(input), &result);
3031
3032 if (input.EventType == KEY_EVENT &&
3033 input.Event.KeyEvent.bKeyDown) {
3034
3035 reply = tolower(input.Event.KeyEvent.uChar.AsciiChar);
3036
3037 if (strchr(ans, reply)) {
3038 break;
3039 }
3040 }
3041 }
3042
3043 printf("%c\n", reply);
3044
3045 return reply;
3046 }
3047
3048 //
3049 // Print image information on a Virtual Floppy Drive
3050 //
3051 void PrintImageInfo(
3052 HANDLE hDevice)
3053 {
3054 ULONG device_number;
3055 CHAR file_name[MAX_PATH];
3056 CHAR file_desc[MAX_PATH];
3057 VFD_DISKTYPE disk_type;
3058 VFD_MEDIA media_type;
3059 VFD_FLAGS media_flags;
3060 VFD_FILETYPE file_type;
3061 ULONG image_size;
3062 DWORD ret;
3063
3064 printf("\n");
3065
3066 // get current device number
3067
3068 ret = VfdGetDeviceNumber(hDevice, &device_number);
3069
3070 if (ret != ERROR_SUCCESS) {
3071 PrintMessage(MSG_GET_LINK_NG);
3072 printf("%s", SystemError(ret));
3073 device_number = (ULONG)-1;
3074 }
3075
3076 // get current drive letters
3077
3078 PrintDriveLetter(hDevice, device_number);
3079
3080 // image file information
3081
3082 ret = VfdGetImageInfo(hDevice, file_name, &disk_type,
3083 &media_type, &media_flags, &file_type, &image_size);
3084
3085 if (ret != ERROR_SUCCESS) {
3086 PrintMessage(MSG_GET_FILE_NG);
3087 printf("%s", SystemError(ret));
3088 return;
3089 }
3090
3091 // print image file information
3092 if (media_type == VFD_MEDIA_NONE) {
3093 PrintMessage(MSG_IMAGE_NONE);
3094 return;
3095 }
3096
3097 if (file_name[0]) {
3098 PrintMessage(MSG_IMAGE_NAME, file_name);
3099
3100 VfdMakeFileDesc(file_desc, sizeof(file_desc),
3101 file_type, image_size, GetFileAttributes(file_name));
3102 }
3103 else {
3104 PrintMessage(MSG_IMAGE_NAME, "<RAM>");
3105
3106 VfdMakeFileDesc(file_desc, sizeof(file_desc),
3107 VFD_FILETYPE_NONE, image_size, 0);
3108 }
3109
3110 PrintMessage(MSG_FILE_DESC, file_desc);
3111
3112 if (disk_type == VFD_DISKTYPE_FILE) {
3113 PrintMessage(MSG_DISKTYPE_FILE);
3114 }
3115 else {
3116 if (media_flags & VFD_FLAG_DATA_MODIFIED) {
3117 PrintMessage(MSG_DISKTYPE_RAM_DIRTY);
3118 }
3119 else {
3120 PrintMessage(MSG_DISKTYPE_RAM_CLEAN);
3121 }
3122 }
3123
3124 // print other file info
3125
3126 PrintMessage(MSG_MEDIA_TYPE, VfdMediaTypeName(media_type));
3127
3128 if (media_flags & VFD_FLAG_WRITE_PROTECTED) {
3129 PrintMessage(MSG_MEDIA_PROTECTED);
3130 }
3131 else {
3132 PrintMessage(MSG_MEDIA_WRITABLE);
3133 }
3134 }
3135
3136 //
3137 // Print drive letters on a virtual floppy drive
3138 //
3139 void PrintDriveLetter(
3140 HANDLE hDevice,
3141 ULONG nDrive)
3142 {
3143 CHAR letter;
3144
3145 PrintMessage(MSG_DRIVE_LETTER, nDrive);
3146
3147 VfdGetGlobalLink(hDevice, &letter);
3148
3149 if (isalpha(letter)) {
3150 PrintMessage(MSG_PERSISTENT, toupper(letter));
3151 }
3152
3153 while (VfdGetLocalLink(hDevice, &letter) == ERROR_SUCCESS &&
3154 isalpha(letter)) {
3155 PrintMessage(MSG_EPHEMERAL, toupper(letter));
3156 }
3157
3158 printf("\n");
3159 }
3160
3161 //
3162 // Prints a text on screen a page a time
3163 //
3164 BOOL ConsolePager(char *pBuffer, BOOL bReset)
3165 {
3166 static int rows = 0;
3167 char prompt[80];
3168 int prompt_len = 0;
3169 HANDLE hStdOut;
3170 HANDLE hStdIn;
3171
3172 //
3173 // prepare the console input and output handles
3174 //
3175 hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
3176 hStdIn = GetStdHandle(STD_INPUT_HANDLE);
3177
3178 for (;;) {
3179 CONSOLE_SCREEN_BUFFER_INFO info;
3180 INPUT_RECORD input;
3181 DWORD result;
3182 DWORD mode;
3183 int cols;
3184 char *cur;
3185 char save;
3186
3187 //
3188 // Get the current console screen information
3189 //
3190 GetConsoleScreenBufferInfo(hStdOut, &info);
3191
3192 if (bReset || rows <= 0) {
3193 rows = info.srWindow.Bottom - info.srWindow.Top - 1;
3194 }
3195
3196 cols = info.dwSize.X;
3197
3198 // console window is too small for paging
3199
3200 if (rows <= 0) {
3201 // print all text and exit
3202 printf("%s", pBuffer);
3203 break;
3204 }
3205
3206 //
3207 // find the tail of the text to be printed this time
3208 //
3209 cur = pBuffer;
3210 save = '\0';
3211
3212 while (*cur) {
3213 if (*(cur++) == '\n' || (cols--) == 0) {
3214 // reached the end of a line
3215 if (--rows == 0) {
3216 // reached the end of a page
3217 // insert a terminating NULL char
3218 save = *cur;
3219 *cur = '\0';
3220 break;
3221 }
3222
3223 cols = info.dwSize.X;
3224 }
3225 }
3226
3227 // print the current page
3228 printf("%s", pBuffer);
3229
3230 // end of the whole text?
3231 if (save == '\0') {
3232 break;
3233 }
3234
3235 //
3236 // prompt for the next page
3237 //
3238
3239 // prepare the prompt text
3240
3241 if (prompt_len == 0) {
3242
3243 prompt_len = FormatMessage(
3244 FORMAT_MESSAGE_FROM_HMODULE |
3245 FORMAT_MESSAGE_IGNORE_INSERTS,
3246 NULL, MSG_PAGER_PROMPT, 0,
3247 prompt, sizeof(prompt), NULL);
3248
3249 if (prompt_len == 0) {
3250 strcpy(prompt, "Press any key to continue...");
3251 prompt_len = strlen(prompt);
3252 }
3253 }
3254
3255 // get the current console input mode
3256
3257 GetConsoleMode(hStdIn, &mode);
3258
3259 // change the mode to receive Ctrl+C as a regular input
3260
3261 SetConsoleMode(hStdIn, (mode & ~ENABLE_PROCESSED_INPUT));
3262
3263 // get the current cursor position
3264
3265 GetConsoleScreenBufferInfo(hStdOut, &info);
3266
3267 // print the prompt text
3268
3269 WriteConsoleOutputCharacter(hStdOut, prompt,
3270 prompt_len, info.dwCursorPosition, &result);
3271
3272 // reverse the text color
3273
3274 FillConsoleOutputAttribute(hStdOut,
3275 (WORD)(info.wAttributes | COMMON_LVB_REVERSE_VIDEO),
3276 prompt_len, info.dwCursorPosition, &result);
3277
3278 // move cursor to the end of the prompt text
3279
3280 info.dwCursorPosition.X =
3281 (short)(info.dwCursorPosition.X + prompt_len);
3282
3283 SetConsoleCursorPosition(hStdOut, info.dwCursorPosition);
3284
3285 // wait for a key press event
3286
3287 FlushConsoleInputBuffer(hStdIn);
3288
3289 do {
3290 ReadConsoleInput(hStdIn, &input, sizeof(input), &result);
3291 }
3292 while (input.EventType != KEY_EVENT ||
3293 !input.Event.KeyEvent.bKeyDown ||
3294 !input.Event.KeyEvent.uChar.AsciiChar);
3295
3296 // restore the original cursor position
3297
3298 info.dwCursorPosition.X =
3299 (short)(info.dwCursorPosition.X - prompt_len);
3300
3301 SetConsoleCursorPosition(hStdOut, info.dwCursorPosition);
3302
3303 // delete the prompt text
3304
3305 FillConsoleOutputCharacter(hStdOut, ' ',
3306 prompt_len, info.dwCursorPosition, &result);
3307
3308 // restore the text attribute to norml
3309
3310 FillConsoleOutputAttribute(hStdOut, info.wAttributes,
3311 prompt_len, info.dwCursorPosition, &result);
3312
3313 // restore the original console mode
3314
3315 SetConsoleMode(hStdIn, mode);
3316
3317 // check if the input was 'q', <esc> or <Ctrl+C> ?
3318
3319 if (input.Event.KeyEvent.uChar.AsciiChar == VK_CANCEL ||
3320 input.Event.KeyEvent.uChar.AsciiChar == VK_ESCAPE ||
3321 tolower(input.Event.KeyEvent.uChar.AsciiChar) == 'q') {
3322
3323 // cancelled by the user
3324 return FALSE;
3325 }
3326
3327 //
3328 // process the next page
3329 //
3330 *cur = save;
3331 pBuffer = cur;
3332 }
3333
3334 return TRUE;
3335 }
3336
3337 //
3338 // Format and print a message text
3339 //
3340 void PrintMessage(UINT msg, ...)
3341 {
3342 char *buf = NULL;
3343 va_list list;
3344
3345 va_start(list, msg);
3346
3347 if (FormatMessage(
3348 FORMAT_MESSAGE_FROM_HMODULE |
3349 FORMAT_MESSAGE_ALLOCATE_BUFFER,
3350 NULL, msg, 0, (LPTSTR)&buf, 0, &list)) {
3351
3352 printf("%s", buf);
3353 }
3354 else {
3355 printf("Unknown Message ID %u\n", msg);
3356 }
3357
3358 va_end(list);
3359
3360 if (buf) {
3361 LocalFree(buf);
3362 }
3363 }
3364
3365 //
3366 // Return a system error message text
3367 //
3368 const char *SystemError(DWORD err)
3369 {
3370 static char msg[256];
3371
3372 if (!FormatMessage(
3373 FORMAT_MESSAGE_FROM_SYSTEM |
3374 FORMAT_MESSAGE_IGNORE_INSERTS,
3375 NULL, err, 0, msg, sizeof(msg), NULL)) {
3376 #ifndef __REACTOS__
3377 sprintf(msg, "Unknown system error %lu (0x%08x)\n", err, err);
3378 #else
3379 sprintf(msg, "Unknown system error %lu (0x%08lx)\n", err, err);
3380 #endif
3381 }
3382
3383 return msg;
3384 }
3385
3386 //
3387 // Convert a path to match the case of names on the disk
3388 //
3389 void ConvertPathCase(char *src, char *dst)
3390 {
3391 HANDLE hFind;
3392 WIN32_FIND_DATA find;
3393 char *p;
3394
3395 p = dst;
3396
3397 if (*src == '\"') {
3398 src++;
3399 }
3400
3401 if (*(src + strlen(src) - 1) == '\"') {
3402 *(src + strlen(src) - 1) = '\0';
3403 }
3404
3405 //
3406 // handle drive / remote server name
3407 //
3408 if (isalpha(*src) && *(src + 1) == ':') {
3409
3410 // drive name
3411
3412 *(p++) = (char)toupper(*src);
3413 strcpy(p++, ":\\");
3414
3415 src += 2;
3416 }
3417 else if (*src == '\\' || *src == '/') {
3418
3419 // absolute path or remote name
3420
3421 if ((*(src + 1) == '\\' || *(src + 1) == '/') &&
3422 *(src + 2) && *(src + 2) != '\\' && *(src + 2) != '/') {
3423
3424 // remote path
3425
3426 *(p++) = '\\';
3427 *(p++) = '\\';
3428 src += 2;
3429
3430 while (*src && *src != '\\' && *src != '/') {
3431 *(p++) = *(src++);
3432 }
3433 }
3434
3435 strcpy(p, "\\");
3436 }
3437 else {
3438 *p = '\0';
3439 }
3440
3441 // skip redundant '\'
3442
3443 while (*src == '\\' || *src == '/') {
3444 src++;
3445 }
3446
3447 // process the path
3448
3449 while (*src) {
3450
3451 char *q = src;
3452
3453 // separate the next part
3454
3455 while (*q && *q != '\\' && *q != '/') {
3456 q++;
3457 }
3458
3459 if ((q - src) == 2 && !strncmp(src, "..", 2)) {
3460 // parent dir - copy as it is
3461 if (p != dst) {
3462 *p++ = '\\';
3463 }
3464
3465 strcpy(p, "..");
3466 p += 2;
3467 }
3468 else if ((q - src) > 1 || *src != '.') {
3469 // path name other than "."
3470 if (p != dst) {
3471 *(p++) = '\\';
3472 }
3473
3474 strncpy(p, src, (q - src));
3475 *(p + (q - src)) = '\0';
3476
3477 hFind = FindFirstFile(dst, &find);
3478
3479 if (hFind == INVALID_HANDLE_VALUE) {
3480 strcpy(p, src);
3481 break;
3482 }
3483
3484 FindClose(hFind);
3485
3486 strcpy(p, find.cFileName);
3487 p += strlen(p);
3488 }
3489
3490 // skip trailing '\'s
3491
3492 while (*q == '\\' || *q == '/') {
3493 q++;
3494 }
3495
3496 src = q;
3497 }
3498 }