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 }
3499