e4e4faf75c6aa14fa195bcd0416a10036481262d
[reactos.git] / base / applications / cmdutils / mode / mode.c
1 /*
2 * ReactOS mode console command
3 *
4 * mode.c
5 *
6 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 /*
23 * COPYRIGHT: See COPYING in the top level directory
24 * PROJECT: ReactOS Mode Utility
25 * FILE: base/applications/cmdutils/mode/mode.c
26 * PURPOSE: Provides fast mode setup for DOS devices.
27 * PROGRAMMERS: Robert Dickenson
28 * Hermes Belusca-Maito
29 */
30
31 #include <stdio.h>
32
33 #include <windef.h>
34 #include <winbase.h>
35 #include <winuser.h>
36 #include <wincon.h>
37
38 #include <conutils.h>
39
40 #include "resource.h"
41
42 #define MAX_PORTNAME_LEN 20
43 #define MAX_COMPORT_NUM 10
44
45 #define ASSERT(a)
46
47 /*** For fixes, see also network/net/main.c ***/
48 // VOID PrintPadding(...)
49 INT
50 __cdecl
51 UnderlinedResPrintf(
52 IN PCON_STREAM Stream,
53 IN UINT uID,
54 ...)
55 {
56 INT Len;
57 va_list args;
58
59 #define MAX_BUFFER_SIZE 4096
60 INT i;
61 WCHAR szMsgBuffer[MAX_BUFFER_SIZE];
62
63 va_start(args, uID);
64 Len = ConResPrintfV(Stream, uID, args);
65 va_end(args);
66
67 ConPuts(Stream, L"\n");
68 for (i = 0; i < Len; i++)
69 szMsgBuffer[i] = L'-';
70 szMsgBuffer[Len] = UNICODE_NULL;
71
72 // FIXME: Use ConStreamWrite instead.
73 ConPuts(Stream, szMsgBuffer);
74
75 return Len;
76 }
77
78 int QueryDevices(VOID)
79 {
80 WCHAR buffer[20240];
81 WCHAR* ptr = buffer;
82
83 *ptr = L'\0';
84 // FIXME: Dynamically allocate 'buffer' in a loop.
85 if (QueryDosDeviceW(NULL, buffer, ARRAYSIZE(buffer)))
86 {
87 while (*ptr != L'\0')
88 {
89 if (wcsstr(ptr, L"COM"))
90 {
91 ConResPrintf(StdOut, IDS_QUERY_SERIAL_FOUND, ptr);
92 }
93 else if (wcsstr(ptr, L"PRN"))
94 {
95 ConResPrintf(StdOut, IDS_QUERY_PRINTER_FOUND, ptr);
96 }
97 else if (wcsstr(ptr, L"LPT"))
98 {
99 ConResPrintf(StdOut, IDS_QUERY_PARALLEL_FOUND, ptr);
100 }
101 else if (wcsstr(ptr, L"AUX") || wcsstr(ptr, L"NUL"))
102 {
103 ConResPrintf(StdOut, IDS_QUERY_DOSDEV_FOUND, ptr);
104 }
105 else
106 {
107 // ConResPrintf(StdOut, IDS_QUERY_MISC_FOUND, ptr);
108 }
109 ptr += (wcslen(ptr) + 1);
110 }
111 }
112 else
113 {
114 wprintf(L"ERROR: QueryDosDeviceW(...) failed: 0x%lx\n", GetLastError());
115 }
116 return 1;
117 }
118
119 int ShowParallelStatus(INT nPortNum)
120 {
121 WCHAR buffer[250];
122 WCHAR szPortName[MAX_PORTNAME_LEN];
123
124 swprintf(szPortName, L"LPT%d", nPortNum);
125
126 ConPuts(StdOut, L"\n");
127 UnderlinedResPrintf(StdOut, IDS_DEVICE_STATUS_HEADER, szPortName);
128 ConPuts(StdOut, L"\n");
129
130 if (QueryDosDeviceW(szPortName, buffer, ARRAYSIZE(buffer)))
131 {
132 WCHAR* ptr = wcsrchr(buffer, L'\\');
133 if (ptr != NULL)
134 {
135 if (_wcsicmp(szPortName, ++ptr) == 0)
136 ConResPuts(StdOut, IDS_PRINTER_OUTPUT_NOT_REROUTED);
137 else
138 ConResPrintf(StdOut, IDS_PRINTER_OUTPUT_REROUTED_SERIAL, ptr);
139
140 return 0;
141 }
142 else
143 {
144 wprintf(L" QueryDosDeviceW(%s) returned unrecognised form %s.\n", szPortName, buffer);
145 }
146 }
147 else
148 {
149 wprintf(L"ERROR: QueryDosDeviceW(%s) failed: 0x%lx\n", szPortName, GetLastError());
150 }
151
152 return 1;
153 }
154
155 int SetParallelState(INT nPortNum)
156 {
157 WCHAR szPortName[MAX_PORTNAME_LEN];
158 WCHAR szTargetPath[MAX_PORTNAME_LEN];
159
160 swprintf(szPortName, L"LPT%d", nPortNum);
161 swprintf(szTargetPath, L"COM%d", nPortNum);
162 if (!DefineDosDeviceW(DDD_REMOVE_DEFINITION, szPortName, szTargetPath))
163 {
164 wprintf(L"SetParallelState(%d) - DefineDosDevice(%s) failed: 0x%lx\n", nPortNum, szPortName, GetLastError());
165 }
166
167 ShowParallelStatus(nPortNum);
168 return 0;
169 }
170
171
172 static PCWSTR
173 ParseNumber(PCWSTR argStr, PDWORD Number)
174 {
175 INT value, skip = 0;
176
177 value = swscanf(argStr, L"%lu%n", Number, &skip);
178 if (!value) return NULL;
179 argStr += skip;
180 return argStr;
181 }
182
183
184 /*
185 \??\COM1
186 \Device\NamedPipe\Spooler\LPT1
187 BOOL DefineDosDevice(
188 DWORD dwFlags, // options
189 LPCTSTR lpDeviceName, // device name
190 LPCTSTR lpTargetPath // path string
191 );
192 DWORD QueryDosDevice(
193 LPCTSTR lpDeviceName, // MS-DOS device name string
194 LPTSTR lpTargetPath, // query results buffer
195 DWORD ucchMax // maximum size of buffer
196 );
197 */
198
199
200 /*****************************************************************************\
201 ** C O N S O L E H E L P E R S **
202 \*****************************************************************************/
203
204 int ShowConsoleStatus(VOID)
205 {
206 HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
207 CONSOLE_SCREEN_BUFFER_INFO csbi;
208 DWORD dwKbdDelay, dwKbdSpeed;
209
210 ConPuts(StdOut, L"\n");
211 UnderlinedResPrintf(StdOut, IDS_DEVICE_STATUS_HEADER, L"CON");
212 ConPuts(StdOut, L"\n");
213
214 if (GetConsoleScreenBufferInfo(hConOut, &csbi))
215 {
216 ConResPrintf(StdOut, IDS_CONSOLE_STATUS_LINES, csbi.dwSize.Y);
217 ConResPrintf(StdOut, IDS_CONSOLE_STATUS_COLS , csbi.dwSize.X);
218 }
219 if (SystemParametersInfoW(SPI_GETKEYBOARDSPEED, 0, &dwKbdSpeed, 0))
220 {
221 ConResPrintf(StdOut, IDS_CONSOLE_KBD_RATE, dwKbdSpeed);
222 }
223 if (SystemParametersInfoW(SPI_GETKEYBOARDDELAY, 0, &dwKbdDelay, 0))
224 {
225 ConResPrintf(StdOut, IDS_CONSOLE_KBD_DELAY, dwKbdDelay);
226 }
227 ConResPrintf(StdOut, IDS_CONSOLE_CODEPAGE, GetConsoleOutputCP());
228 return 0;
229 }
230
231 int ShowConsoleCPStatus(VOID)
232 {
233 ConPuts(StdOut, L"\n");
234 UnderlinedResPrintf(StdOut, IDS_DEVICE_STATUS_HEADER, L"CON");
235 ConPuts(StdOut, L"\n");
236
237 ConResPrintf(StdOut, IDS_CONSOLE_CODEPAGE, GetConsoleOutputCP());
238 return 0;
239 }
240
241 static VOID
242 ClearScreen(
243 IN HANDLE hConOut,
244 IN PCONSOLE_SCREEN_BUFFER_INFO pcsbi)
245 {
246 COORD coPos;
247 DWORD dwWritten;
248
249 coPos.X = 0;
250 coPos.Y = 0;
251 FillConsoleOutputAttribute(hConOut, pcsbi->wAttributes,
252 pcsbi->dwSize.X * pcsbi->dwSize.Y,
253 coPos, &dwWritten);
254 FillConsoleOutputCharacterW(hConOut, L' ',
255 pcsbi->dwSize.X * pcsbi->dwSize.Y,
256 coPos, &dwWritten);
257 SetConsoleCursorPosition(hConOut, coPos);
258 }
259
260 /*
261 * See, or adjust if needed, subsystems/mvdm/ntvdm/console/video.c!ResizeTextConsole()
262 * for more information.
263 */
264 static BOOL
265 ResizeTextConsole(
266 IN HANDLE hConOut,
267 IN OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi,
268 IN COORD Resolution)
269 {
270 BOOL Success;
271 SHORT Width, Height;
272 SMALL_RECT ConRect;
273
274 /*
275 * Use this trick to effectively resize the console buffer and window,
276 * because:
277 * - SetConsoleScreenBufferSize fails if the new console screen buffer size
278 * is smaller than the current console window size, and:
279 * - SetConsoleWindowInfo fails if the new console window size is larger
280 * than the current console screen buffer size.
281 */
282
283 /* Resize the screen buffer only if needed */
284 if (Resolution.X != pcsbi->dwSize.X || Resolution.Y != pcsbi->dwSize.Y)
285 {
286 Width = pcsbi->srWindow.Right - pcsbi->srWindow.Left + 1;
287 Height = pcsbi->srWindow.Bottom - pcsbi->srWindow.Top + 1;
288
289 /*
290 * If the current console window is too large for
291 * the new screen buffer, resize it first.
292 */
293 if (Width > Resolution.X || Height > Resolution.Y)
294 {
295 /*
296 * NOTE: This is not a problem if we move the window back to (0,0)
297 * because when we resize the screen buffer, the window will move back
298 * to where the cursor is. Or, if the screen buffer is not resized,
299 * when we readjust again the window, we will move back to a correct
300 * position. This is what we wanted after all...
301 */
302 ConRect.Left = ConRect.Top = 0;
303 ConRect.Right = ConRect.Left + min(Width , Resolution.X) - 1;
304 ConRect.Bottom = ConRect.Top + min(Height, Resolution.Y) - 1;
305
306 Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
307 if (!Success) return FALSE;
308 }
309
310 /*
311 * Now resize the screen buffer.
312 *
313 * SetConsoleScreenBufferSize automatically takes into account the current
314 * cursor position when it computes starting which row it should copy text
315 * when resizing the screen buffer, and scrolls the console window such that
316 * the cursor is placed in it again. We therefore do not need to care about
317 * the cursor position and do the maths ourselves.
318 */
319 Success = SetConsoleScreenBufferSize(hConOut, Resolution);
320 if (!Success) return FALSE;
321
322 /*
323 * Setting a new screen buffer size can change other information,
324 * so update the console screen buffer information.
325 */
326 GetConsoleScreenBufferInfo(hConOut, pcsbi);
327 }
328
329 /* Always resize the console window within the permitted maximum size */
330 Width = min(Resolution.X, pcsbi->dwMaximumWindowSize.X);
331 Height = min(Resolution.Y, pcsbi->dwMaximumWindowSize.Y);
332 ConRect.Left = 0;
333 ConRect.Right = ConRect.Left + Width - 1;
334 ConRect.Bottom = max(pcsbi->dwCursorPosition.Y, Height - 1);
335 ConRect.Top = ConRect.Bottom - Height + 1;
336
337 SetConsoleWindowInfo(hConOut, TRUE, &ConRect);
338
339 /* Update the console screen buffer information */
340 GetConsoleScreenBufferInfo(hConOut, pcsbi);
341 return TRUE;
342 }
343
344 int SetConsoleStateOld(IN PCWSTR ArgStr)
345 {
346 PCWSTR argStr = ArgStr;
347
348 HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
349 CONSOLE_SCREEN_BUFFER_INFO csbi;
350 COORD Resolution;
351 DWORD value;
352
353 if (!GetConsoleScreenBufferInfo(hConOut, &csbi))
354 {
355 // TODO: Error message?
356 return 0;
357 }
358
359 Resolution = csbi.dwSize;
360
361 /* Parse the column number (only MANDATORY argument) */
362 value = 0;
363 argStr = ParseNumber(argStr, &value);
364 if (!argStr) goto invalid_parameter;
365 Resolution.X = (SHORT)value;
366
367 /* Parse the line number (OPTIONAL argument) */
368 while (*argStr == L' ') argStr++;
369 if (!*argStr) goto Quit;
370 if (*argStr++ != L',') goto invalid_parameter;
371 while (*argStr == L' ') argStr++;
372
373 value = 0;
374 argStr = ParseNumber(argStr, &value);
375 if (!argStr) goto invalid_parameter;
376 Resolution.Y = (SHORT)value;
377
378 /* This should be the end of the string */
379 while (*argStr == L' ') argStr++;
380 if (*argStr) goto invalid_parameter;
381
382 Quit:
383 ClearScreen(hConOut, &csbi);
384 if (!ResizeTextConsole(hConOut, &csbi, Resolution))
385 wprintf(L"The screen cannot be set to the number of lines and columns specified.\n");
386
387 return 0;
388
389 invalid_parameter:
390 wprintf(L"Invalid parameter - %s\n", ArgStr);
391 return 1;
392 }
393
394 int SetConsoleState(IN PCWSTR ArgStr)
395 {
396 PCWSTR argStr = ArgStr;
397 BOOL dispMode = FALSE, kbdMode = FALSE;
398
399 HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
400 CONSOLE_SCREEN_BUFFER_INFO csbi;
401 COORD Resolution;
402 DWORD dwKbdDelay, dwKbdSpeed;
403 DWORD value;
404
405 if (!GetConsoleScreenBufferInfo(hConOut, &csbi))
406 {
407 // TODO: Error message?
408 return 0;
409 }
410 if (!SystemParametersInfoW(SPI_GETKEYBOARDDELAY, 0, &dwKbdDelay, 0))
411 {
412 // TODO: Error message?
413 return 0;
414 }
415 if (!SystemParametersInfoW(SPI_GETKEYBOARDSPEED, 0, &dwKbdSpeed, 0))
416 {
417 // TODO: Error message?
418 return 0;
419 }
420
421 Resolution = csbi.dwSize;
422
423 while (argStr && *argStr)
424 {
425 while (*argStr == L' ') argStr++;
426 if (!*argStr) break;
427
428 if (!kbdMode && _wcsnicmp(argStr, L"COLS=", 5) == 0)
429 {
430 dispMode = TRUE;
431
432 value = 0;
433 argStr = ParseNumber(argStr+5, &value);
434 if (!argStr) goto invalid_parameter;
435 Resolution.X = (SHORT)value;
436 }
437 else if (!kbdMode && _wcsnicmp(argStr, L"LINES=", 6) == 0)
438 {
439 dispMode = TRUE;
440
441 value = 0;
442 argStr = ParseNumber(argStr+6, &value);
443 if (!argStr) goto invalid_parameter;
444 Resolution.Y = (SHORT)value;
445 }
446 else if (!dispMode && _wcsnicmp(argStr, L"RATE=", 5) == 0)
447 {
448 kbdMode = TRUE;
449
450 argStr = ParseNumber(argStr+5, &dwKbdSpeed);
451 if (!argStr) goto invalid_parameter;
452 }
453 else if (!dispMode && _wcsnicmp(argStr, L"DELAY=", 6) == 0)
454 {
455 kbdMode = TRUE;
456
457 argStr = ParseNumber(argStr+6, &dwKbdDelay);
458 if (!argStr) goto invalid_parameter;
459 }
460 else
461 {
462 invalid_parameter:
463 wprintf(L"Invalid parameter - %s\n", ArgStr);
464 return 1;
465 }
466 }
467
468 if (dispMode)
469 {
470 ClearScreen(hConOut, &csbi);
471 if (!ResizeTextConsole(hConOut, &csbi, Resolution))
472 wprintf(L"The screen cannot be set to the number of lines and columns specified.\n");
473 }
474 else if (kbdMode)
475 {
476 /*
477 * Set the new keyboard settings. If those values are greater than
478 * their allowed range, they are automatically corrected as follows:
479 * dwKbdSpeed = min(dwKbdSpeed, 31);
480 * dwKbdDelay = (dwKbdDelay % 4);
481 */
482 SystemParametersInfoW(SPI_SETKEYBOARDDELAY, dwKbdDelay, NULL, 0);
483 // "Invalid keyboard delay."
484 SystemParametersInfoW(SPI_SETKEYBOARDSPEED, dwKbdSpeed, NULL, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
485 // "Invalid keyboard rate."
486 }
487
488 return 0;
489 }
490
491 int SetConsoleCPState(IN PCWSTR ArgStr)
492 {
493 PCWSTR argStr = ArgStr;
494 DWORD CodePage = 0;
495
496 if ( (_wcsnicmp(argStr, L"SELECT=", 7) == 0 && (argStr += 7)) ||
497 (_wcsnicmp(argStr, L"SEL=", 4) == 0 && (argStr += 4)) )
498 {
499 argStr = ParseNumber(argStr, &CodePage);
500 if (!argStr) goto invalid_parameter;
501
502 /* This should be the end of the string */
503 while (*argStr == L' ') argStr++;
504 if (*argStr) goto invalid_parameter;
505
506 SetConsoleCP(CodePage);
507 SetConsoleOutputCP(CodePage);
508 // "The code page specified is not valid."
509 ShowConsoleCPStatus();
510 }
511 else
512 {
513 invalid_parameter:
514 wprintf(L"Invalid parameter - %s\n", ArgStr);
515 return 1;
516 }
517
518 return 0;
519 }
520
521
522 /*****************************************************************************\
523 ** S E R I A L P O R T H E L P E R S **
524 \*****************************************************************************/
525
526 static BOOL
527 SerialPortQuery(INT nPortNum, LPDCB pDCB, LPCOMMTIMEOUTS pCommTimeouts, BOOL bWrite)
528 {
529 BOOL Success;
530 HANDLE hPort;
531 WCHAR szPortName[MAX_PORTNAME_LEN];
532
533 ASSERT(pDCB);
534 ASSERT(pCommTimeouts);
535
536 swprintf(szPortName, L"COM%d", nPortNum);
537 hPort = CreateFileW(szPortName,
538 bWrite ? GENERIC_WRITE : GENERIC_READ,
539 0, // exclusive
540 NULL, // sec attr
541 OPEN_EXISTING,
542 0, // no attributes
543 NULL); // no template
544
545 if (hPort == INVALID_HANDLE_VALUE)
546 {
547 wprintf(L"Illegal device name - %s\n", szPortName);
548 wprintf(L"Last error = 0x%lx\n", GetLastError());
549 return FALSE;
550 }
551
552 Success = bWrite ? SetCommState(hPort, pDCB)
553 : GetCommState(hPort, pDCB);
554 if (!Success)
555 {
556 wprintf(L"Failed to %s the status for device COM%d:\n", bWrite ? L"set" : L"get", nPortNum);
557 goto Quit;
558 }
559
560 Success = bWrite ? SetCommTimeouts(hPort, pCommTimeouts)
561 : GetCommTimeouts(hPort, pCommTimeouts);
562 if (!Success)
563 {
564 wprintf(L"Failed to %s timeout status for device COM%d:\n", bWrite ? L"set" : L"get", nPortNum);
565 goto Quit;
566 }
567
568 Quit:
569 CloseHandle(hPort);
570 return Success;
571 }
572
573 int ShowSerialStatus(INT nPortNum)
574 {
575 static const LPCWSTR parity_strings[] =
576 {
577 L"None", // NOPARITY
578 L"Odd", // ODDPARITY
579 L"Even", // EVENPARITY
580 L"Mark", // MARKPARITY
581 L"Space" // SPACEPARITY
582 };
583 static const LPCWSTR control_strings[] = { L"OFF", L"ON", L"HANDSHAKE", L"TOGGLE" };
584 static const LPCWSTR stopbit_strings[] = { L"1", L"1.5", L"2" };
585
586 DCB dcb;
587 COMMTIMEOUTS CommTimeouts;
588 WCHAR szPortName[MAX_PORTNAME_LEN];
589
590 if (!SerialPortQuery(nPortNum, &dcb, &CommTimeouts, FALSE))
591 {
592 return 1;
593 }
594 if (dcb.Parity >= ARRAYSIZE(parity_strings))
595 {
596 wprintf(L"ERROR: Invalid value for Parity Bits %d:\n", dcb.Parity);
597 dcb.Parity = 0;
598 }
599 if (dcb.StopBits >= ARRAYSIZE(stopbit_strings))
600 {
601 wprintf(L"ERROR: Invalid value for Stop Bits %d:\n", dcb.StopBits);
602 dcb.StopBits = 0;
603 }
604
605 swprintf(szPortName, L"COM%d", nPortNum);
606
607 ConPuts(StdOut, L"\n");
608 UnderlinedResPrintf(StdOut, IDS_DEVICE_STATUS_HEADER, szPortName);
609 ConPuts(StdOut, L"\n");
610
611 ConResPrintf(StdOut, IDS_COM_STATUS_BAUD, dcb.BaudRate);
612 ConResPrintf(StdOut, IDS_COM_STATUS_PARITY, parity_strings[dcb.Parity]);
613 ConResPrintf(StdOut, IDS_COM_STATUS_DATA_BITS, dcb.ByteSize);
614 ConResPrintf(StdOut, IDS_COM_STATUS_STOP_BITS, stopbit_strings[dcb.StopBits]);
615 ConResPrintf(StdOut, IDS_COM_STATUS_TIMEOUT,
616 control_strings[(CommTimeouts.ReadTotalTimeoutConstant != 0) ||
617 (CommTimeouts.WriteTotalTimeoutConstant != 0) ? 1 : 0]);
618 ConResPrintf(StdOut, IDS_COM_STATUS_XON_XOFF,
619 control_strings[dcb.fOutX ? 1 : 0]);
620 ConResPrintf(StdOut, IDS_COM_STATUS_CTS_HANDSHAKING,
621 control_strings[dcb.fOutxCtsFlow ? 1 : 0]);
622 ConResPrintf(StdOut, IDS_COM_STATUS_DSR_HANDSHAKING,
623 control_strings[dcb.fOutxDsrFlow ? 1 : 0]);
624 ConResPrintf(StdOut, IDS_COM_STATUS_DSR_SENSITIVITY,
625 control_strings[dcb.fDsrSensitivity ? 1 : 0]);
626 ConResPrintf(StdOut, IDS_COM_STATUS_DTR_CIRCUIT, control_strings[dcb.fDtrControl]);
627 ConResPrintf(StdOut, IDS_COM_STATUS_RTS_CIRCUIT, control_strings[dcb.fRtsControl]);
628 return 0;
629 }
630
631
632 /*
633 * Those procedures are inspired from Wine's dll/win32/kernel32/wine/comm.c
634 * Copyright 1996 Erik Bos and Marcus Meissner.
635 */
636
637 static PCWSTR
638 ParseModes(PCWSTR argStr, PBYTE Mode)
639 {
640 if (_wcsnicmp(argStr, L"OFF", 3) == 0)
641 {
642 argStr += 3;
643 *Mode = 0;
644 }
645 else if (_wcsnicmp(argStr, L"ON", 2) == 0)
646 {
647 argStr += 2;
648 *Mode = 1;
649 }
650 else if (_wcsnicmp(argStr, L"HS", 2) == 0)
651 {
652 argStr += 2;
653 *Mode = 2;
654 }
655 else if (_wcsnicmp(argStr, L"TG", 2) == 0)
656 {
657 argStr += 2;
658 *Mode = 3;
659 }
660
661 return NULL;
662 }
663
664 static PCWSTR
665 ParseBaudRate(PCWSTR argStr, PDWORD BaudRate)
666 {
667 argStr = ParseNumber(argStr, BaudRate);
668 if (!argStr) return NULL;
669
670 /*
671 * Check for Baud Rate abbreviations. This means that using
672 * those values as real baud rates is impossible using MODE.
673 */
674 switch (*BaudRate)
675 {
676 /* BaudRate = 110, 150, 300, 600 */
677 case 11: case 15: case 30: case 60:
678 *BaudRate *= 10;
679 break;
680
681 /* BaudRate = 1200, 2400, 4800, 9600 */
682 case 12: case 24: case 48: case 96:
683 *BaudRate *= 100;
684 break;
685
686 case 19:
687 *BaudRate = 19200;
688 break;
689 }
690
691 return argStr;
692 }
693
694 static PCWSTR
695 ParseParity(PCWSTR argStr, PBYTE Parity)
696 {
697 switch (towupper(*argStr++))
698 {
699 case L'N':
700 *Parity = NOPARITY;
701 break;
702
703 case L'O':
704 *Parity = ODDPARITY;
705 break;
706
707 case L'E':
708 *Parity = EVENPARITY;
709 break;
710
711 case L'M':
712 *Parity = MARKPARITY;
713 break;
714
715 case L'S':
716 *Parity = SPACEPARITY;
717 break;
718
719 default:
720 return NULL;
721 }
722
723 return argStr;
724 }
725
726 static PCWSTR
727 ParseByteSize(PCWSTR argStr, PBYTE ByteSize)
728 {
729 DWORD value = 0;
730
731 argStr = ParseNumber(argStr, &value);
732 if (!argStr) return NULL;
733
734 *ByteSize = (BYTE)value;
735 if (*ByteSize < 5 || *ByteSize > 8)
736 return NULL;
737
738 return argStr;
739 }
740
741 static PCWSTR
742 ParseStopBits(PCWSTR argStr, PBYTE StopBits)
743 {
744 if (_wcsnicmp(argStr, L"1.5", 3) == 0)
745 {
746 argStr += 3;
747 *StopBits = ONE5STOPBITS;
748 }
749 else
750 {
751 if (*argStr == L'1')
752 *StopBits = ONESTOPBIT;
753 else if (*argStr == L'2')
754 *StopBits = TWOSTOPBITS;
755 else
756 return NULL;
757
758 argStr++;
759 }
760
761 return argStr;
762 }
763
764 /*
765 * Build a DCB using the old style settings string eg: "96,n,8,1"
766 *
767 * See dll/win32/kernel32/wine/comm.c!COMM_BuildOldCommDCB()
768 * for more information.
769 */
770 static BOOL
771 BuildOldCommDCB(
772 OUT LPDCB pDCB,
773 IN PCWSTR ArgStr)
774 {
775 PCWSTR argStr = ArgStr;
776 BOOL stop = FALSE;
777
778 /*
779 * Parse the baud rate (only MANDATORY argument)
780 */
781 argStr = ParseBaudRate(argStr, &pDCB->BaudRate);
782 if (!argStr) return FALSE;
783
784
785 /*
786 * Now parse the rest (OPTIONAL arguments)
787 */
788
789 while (*argStr == L' ') argStr++;
790 if (!*argStr) goto Quit;
791 if (*argStr++ != L',') return FALSE;
792 while (*argStr == L' ') argStr++;
793 if (!*argStr) goto Quit;
794
795 /* Parse the parity */
796 // Default: EVENPARITY
797 pDCB->Parity = EVENPARITY;
798 if (*argStr != L',')
799 {
800 argStr = ParseParity(argStr, &pDCB->Parity);
801 if (!argStr) return FALSE;
802 }
803
804 while (*argStr == L' ') argStr++;
805 if (!*argStr) goto Quit;
806 if (*argStr++ != L',') return FALSE;
807 while (*argStr == L' ') argStr++;
808 if (!*argStr) goto Quit;
809
810 /* Parse the data bits */
811 // Default: 7
812 pDCB->ByteSize = 7;
813 if (*argStr != L',')
814 {
815 argStr = ParseByteSize(argStr, &pDCB->ByteSize);
816 if (!argStr) return FALSE;
817 }
818
819 while (*argStr == L' ') argStr++;
820 if (!*argStr) goto Quit;
821 if (*argStr++ != L',') return FALSE;
822 while (*argStr == L' ') argStr++;
823 if (!*argStr) goto Quit;
824
825 /* Parse the stop bits */
826 // Default: 1, or 2 for BAUD=110
827 // pDCB->StopBits = ONESTOPBIT;
828 if (*argStr != L',')
829 {
830 stop = TRUE;
831 argStr = ParseStopBits(argStr, &pDCB->StopBits);
832 if (!argStr) return FALSE;
833 }
834
835 /* The last parameter (flow control "retry") is really optional */
836 while (*argStr == L' ') argStr++;
837 if (!*argStr) goto Quit;
838 if (*argStr++ != L',') return FALSE;
839 while (*argStr == L' ') argStr++;
840 if (!*argStr) goto Quit;
841
842 Quit:
843 switch (towupper(*argStr))
844 {
845 case L'\0':
846 pDCB->fInX = FALSE;
847 pDCB->fOutX = FALSE;
848 pDCB->fOutxCtsFlow = FALSE;
849 pDCB->fOutxDsrFlow = FALSE;
850 pDCB->fDtrControl = DTR_CONTROL_ENABLE;
851 pDCB->fRtsControl = RTS_CONTROL_ENABLE;
852 break;
853
854 case L'X':
855 pDCB->fInX = TRUE;
856 pDCB->fOutX = TRUE;
857 pDCB->fOutxCtsFlow = FALSE;
858 pDCB->fOutxDsrFlow = FALSE;
859 pDCB->fDtrControl = DTR_CONTROL_ENABLE;
860 pDCB->fRtsControl = RTS_CONTROL_ENABLE;
861 break;
862
863 case L'P':
864 pDCB->fInX = FALSE;
865 pDCB->fOutX = FALSE;
866 pDCB->fOutxCtsFlow = TRUE;
867 pDCB->fOutxDsrFlow = TRUE;
868 pDCB->fDtrControl = DTR_CONTROL_HANDSHAKE;
869 pDCB->fRtsControl = RTS_CONTROL_HANDSHAKE;
870 break;
871
872 default:
873 /* Unsupported */
874 return FALSE;
875 }
876 if (*argStr) argStr++;
877
878 /* This should be the end of the string */
879 while (*argStr == L' ') argStr++;
880 if (*argStr) return FALSE;
881
882 /* If stop bits were not specified, a default is always supplied */
883 if (!stop)
884 {
885 if (pDCB->BaudRate == 110)
886 pDCB->StopBits = TWOSTOPBITS;
887 else
888 pDCB->StopBits = ONESTOPBIT;
889 }
890 return TRUE;
891 }
892
893 /*
894 * Build a DCB using the new style settings string.
895 * eg: "baud=9600 parity=n data=8 stop=1 xon=on to=on"
896 *
897 * See dll/win32/kernel32/wine/comm.c!COMM_BuildNewCommDCB()
898 * for more information.
899 */
900 static BOOL
901 BuildNewCommDCB(
902 OUT LPDCB pDCB,
903 OUT LPCOMMTIMEOUTS pCommTimeouts,
904 IN PCWSTR ArgStr)
905 {
906 PCWSTR argStr = ArgStr;
907 BOOL baud = FALSE, stop = FALSE;
908 BYTE value;
909
910 while (argStr && *argStr)
911 {
912 while (*argStr == L' ') argStr++;
913 if (!*argStr) break;
914
915 if (_wcsnicmp(argStr, L"BAUD=", 5) == 0)
916 {
917 baud = TRUE;
918 argStr = ParseBaudRate(argStr+5, &pDCB->BaudRate);
919 if (!argStr) return FALSE;
920 }
921 else if (_wcsnicmp(argStr, L"PARITY=", 7) == 0)
922 {
923 // Default: EVENPARITY
924 argStr = ParseParity(argStr+7, &pDCB->Parity);
925 if (!argStr) return FALSE;
926 }
927 else if (_wcsnicmp(argStr, L"DATA=", 5) == 0)
928 {
929 // Default: 7
930 argStr = ParseByteSize(argStr+5, &pDCB->ByteSize);
931 if (!argStr) return FALSE;
932 }
933 else if (_wcsnicmp(argStr, L"STOP=", 5) == 0)
934 {
935 // Default: 1, or 2 for BAUD=110
936 stop = TRUE;
937 argStr = ParseStopBits(argStr+5, &pDCB->StopBits);
938 if (!argStr) return FALSE;
939 }
940 else if (_wcsnicmp(argStr, L"TO=", 3) == 0) // TO=ON|OFF
941 {
942 /* Only total time-outs are get/set by Windows' MODE.COM */
943 argStr = ParseModes(argStr+3, &value);
944 if (!argStr) return FALSE;
945 if (value == 0) // OFF
946 {
947 pCommTimeouts->ReadTotalTimeoutConstant = 0;
948 pCommTimeouts->WriteTotalTimeoutConstant = 0;
949 }
950 else if (value == 1) // ON
951 {
952 pCommTimeouts->ReadTotalTimeoutConstant = 60000;
953 pCommTimeouts->WriteTotalTimeoutConstant = 60000;
954 }
955 else
956 {
957 return FALSE;
958 }
959 }
960 else if (_wcsnicmp(argStr, L"XON=", 4) == 0) // XON=ON|OFF
961 {
962 argStr = ParseModes(argStr+4, &value);
963 if (!argStr) return FALSE;
964 if ((value == 0) || (value == 1))
965 {
966 pDCB->fOutX = value;
967 pDCB->fInX = value;
968 }
969 else
970 {
971 return FALSE;
972 }
973 }
974 else if (_wcsnicmp(argStr, L"ODSR=", 5) == 0) // ODSR=ON|OFF
975 {
976 value = 0;
977 argStr = ParseModes(argStr+5, &value);
978 if (!argStr) return FALSE;
979 if ((value == 0) || (value == 1))
980 pDCB->fOutxDsrFlow = value;
981 else
982 return FALSE;
983 }
984 else if (_wcsnicmp(argStr, L"OCTS=", 5) == 0) // OCTS=ON|OFF
985 {
986 value = 0;
987 argStr = ParseModes(argStr+5, &value);
988 if (!argStr) return FALSE;
989 if ((value == 0) || (value == 1))
990 pDCB->fOutxCtsFlow = value;
991 else
992 return FALSE;
993 }
994 else if (_wcsnicmp(argStr, L"DTR=", 4) == 0) // DTR=ON|OFF|HS
995 {
996 value = 0;
997 argStr = ParseModes(argStr+4, &value);
998 if (!argStr) return FALSE;
999 if ((value == 0) || (value == 1) || (value == 2))
1000 pDCB->fDtrControl = value;
1001 else
1002 return FALSE;
1003 }
1004 else if (_wcsnicmp(argStr, L"RTS=", 4) == 0) // RTS=ON|OFF|HS|TG
1005 {
1006 value = 0;
1007 argStr = ParseModes(argStr+4, &value);
1008 if (!argStr) return FALSE;
1009 if ((value == 0) || (value == 1) || (value == 2) || (value == 3))
1010 pDCB->fRtsControl = value;
1011 else
1012 return FALSE;
1013 }
1014 else if (_wcsnicmp(argStr, L"IDSR=", 5) == 0) // IDSR=ON|OFF
1015 {
1016 value = 0;
1017 argStr = ParseModes(argStr+5, &value);
1018 if (!argStr) return FALSE;
1019 if ((value == 0) || (value == 1))
1020 pDCB->fDsrSensitivity = value;
1021 else
1022 return FALSE;
1023 }
1024 else
1025 {
1026 return FALSE;
1027 }
1028 }
1029
1030 /* If stop bits were not specified, a default is always supplied */
1031 if (!stop)
1032 {
1033 if (baud && pDCB->BaudRate == 110)
1034 pDCB->StopBits = TWOSTOPBITS;
1035 else
1036 pDCB->StopBits = ONESTOPBIT;
1037 }
1038 return TRUE;
1039 }
1040
1041 int SetSerialState(INT nPortNum, IN PCWSTR ArgStr)
1042 {
1043 BOOL Success;
1044 DCB dcb;
1045 COMMTIMEOUTS CommTimeouts;
1046
1047 if (!SerialPortQuery(nPortNum, &dcb, &CommTimeouts, FALSE))
1048 {
1049 // TODO: Error message?
1050 return 0;
1051 }
1052
1053 /*
1054 * Check whether we should use the old or the new MODE syntax:
1055 * in the old syntax, the separators are both spaces and commas.
1056 */
1057 if (wcschr(ArgStr, L','))
1058 Success = BuildOldCommDCB(&dcb, ArgStr);
1059 else
1060 Success = BuildNewCommDCB(&dcb, &CommTimeouts, ArgStr);
1061
1062 if (!Success)
1063 {
1064 wprintf(L"Invalid parameter - %s\n", ArgStr);
1065 return 1;
1066 }
1067
1068 SerialPortQuery(nPortNum, &dcb, &CommTimeouts, TRUE);
1069 ShowSerialStatus(nPortNum);
1070
1071 return 0;
1072 }
1073
1074
1075 /*****************************************************************************\
1076 ** E N T R Y P O I N T **
1077 \*****************************************************************************/
1078
1079 static PCWSTR
1080 FindPortNum(PCWSTR argStr, PINT PortNum)
1081 {
1082 *PortNum = -1;
1083
1084 if (*argStr >= L'0' && *argStr <= L'9')
1085 {
1086 *PortNum = *argStr - L'0';
1087 argStr++;
1088 if (*argStr >= L'0' && *argStr <= L'9')
1089 {
1090 *PortNum *= 10;
1091 *PortNum += *argStr - L'0';
1092 }
1093 }
1094 else
1095 {
1096 return NULL;
1097 }
1098
1099 return argStr;
1100 }
1101
1102 int wmain(int argc, WCHAR* argv[])
1103 {
1104 int ret = 0;
1105 int arg;
1106 SIZE_T ArgStrSize;
1107 PCWSTR ArgStr, argStr;
1108
1109 INT nPortNum;
1110
1111 /* Initialize the Console Standard Streams */
1112 ConInitStdStreams();
1113
1114 /*
1115 * MODE.COM has a very peculiar way of parsing its arguments,
1116 * as they can be even not separated by any space. This extreme
1117 * behaviour certainly is present for backwards compatibility
1118 * with the oldest versions of the utility present on MS-DOS.
1119 *
1120 * For example, such a command:
1121 * "MODE.COM COM1baud=9600parity=ndata=8stop=1xon=onto=on"
1122 * will be correctly understood as:
1123 * "MODE.COM COM1 baud=9600 parity=n data=8 stop=1 xon=on to=on"
1124 *
1125 * Note also that the "/STATUS" switch is actually really "/STA".
1126 *
1127 * However we will not use GetCommandLine() because we do not want
1128 * to deal with the prepended application path and try to find
1129 * where the arguments start. Our approach here will consist in
1130 * flattening the arguments vector.
1131 */
1132 ArgStrSize = 0;
1133
1134 /* Compute space needed for the new string, and allocate it */
1135 for (arg = 1; arg < argc; arg++)
1136 {
1137 ArgStrSize += wcslen(argv[arg]) + 1; // 1 for space
1138 }
1139 ArgStr = HeapAlloc(GetProcessHeap(), 0, (ArgStrSize + 1) * sizeof(WCHAR));
1140 if (ArgStr == NULL)
1141 {
1142 wprintf(L"ERROR: Not enough memory\n");
1143 return 1;
1144 }
1145
1146 /* Copy the contents and NULL-terminate the string */
1147 argStr = ArgStr;
1148 for (arg = 1; arg < argc; arg++)
1149 {
1150 wcscpy((PWSTR)argStr, argv[arg]);
1151 argStr += wcslen(argv[arg]);
1152 *(PWSTR)argStr++ = L' ';
1153 }
1154 *(PWSTR)argStr = L'\0';
1155
1156 /* Parse the command line */
1157 argStr = ArgStr;
1158
1159 while (*argStr == L' ') argStr++;
1160 if (!*argStr) goto show_status;
1161
1162 if (wcsstr(argStr, L"/?") || wcsstr(argStr, L"-?"))
1163 {
1164 ConResPuts(StdOut, IDS_USAGE);
1165 goto Quit;
1166 }
1167 else if (_wcsnicmp(argStr, L"/STA", 4) == 0)
1168 {
1169 // FIXME: Check if there are other "parameters" after the status,
1170 // in which case this is invalid.
1171 goto show_status;
1172 }
1173 else if (_wcsnicmp(argStr, L"LPT", 3) == 0)
1174 {
1175 argStr = FindPortNum(argStr+3, &nPortNum);
1176 if (!argStr || nPortNum == -1)
1177 goto invalid_parameter;
1178
1179 if (*argStr == L':') argStr++;
1180 while (*argStr == L' ') argStr++;
1181
1182 ret = ShowParallelStatus(nPortNum);
1183 goto Quit;
1184 }
1185 else if (_wcsnicmp(argStr, L"COM", 3) == 0)
1186 {
1187 argStr = FindPortNum(argStr+3, &nPortNum);
1188 if (!argStr || nPortNum == -1)
1189 goto invalid_parameter;
1190
1191 if (*argStr == L':') argStr++;
1192 while (*argStr == L' ') argStr++;
1193
1194 if (!*argStr || _wcsnicmp(argStr, L"/STA", 4) == 0)
1195 ret = ShowSerialStatus(nPortNum);
1196 else
1197 ret = SetSerialState(nPortNum, argStr);
1198 goto Quit;
1199 }
1200 else if (_wcsnicmp(argStr, L"CON", 3) == 0)
1201 {
1202 argStr += 3;
1203
1204 if (*argStr == L':') argStr++;
1205 while (*argStr == L' ') argStr++;
1206
1207 if (!*argStr || _wcsnicmp(argStr, L"/STA", 4) == 0)
1208 {
1209 ret = ShowConsoleStatus();
1210 }
1211 else if ( (_wcsnicmp(argStr, L"CP", 2) == 0 && (argStr += 2)) ||
1212 (_wcsnicmp(argStr, L"CODEPAGE", 8) == 0 && (argStr += 8)) )
1213 {
1214 while (*argStr == L' ') argStr++;
1215
1216 if (!*argStr || _wcsnicmp(argStr, L"/STA", 4) == 0)
1217 ret = ShowConsoleCPStatus();
1218 else
1219 ret = SetConsoleCPState(argStr);
1220 }
1221 else
1222 {
1223 ret = SetConsoleState(argStr);
1224 }
1225 goto Quit;
1226 }
1227 // else if (wcschr(argStr, L','))
1228 else
1229 {
1230 /* Old syntax: MODE [COLS],[LINES] */
1231 ret = SetConsoleStateOld(argStr);
1232 goto Quit;
1233 }
1234
1235 show_status:
1236 QueryDevices();
1237 /*
1238 ShowParallelStatus(1);
1239 for (nPortNum = 0; nPortNum < MAX_COMPORT_NUM; nPortNum++)
1240 {
1241 ShowSerialStatus(nPortNum + 1);
1242 }
1243 ShowConsoleStatus();
1244 */
1245 goto Quit;
1246
1247 invalid_parameter:
1248 wprintf(L"Invalid parameter - %s\n", ArgStr);
1249 goto Quit;
1250
1251 Quit:
1252 /* Free the string and quit */
1253 HeapFree(GetProcessHeap(), 0, (PWSTR)ArgStr);
1254 return ret;
1255 }