2 * PROJECT: ReactOS Mode Utility
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Provides fast mode setup for DOS devices.
5 * COPYRIGHT: Copyright 2002 Robert Dickenson
6 * Copyright 2016-2021 Hermes Belusca-Maito
9 * ReactOS mode console command
13 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org>
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
42 #define MAX_PORTNAME_LEN 20
46 /*** For fixes, see also network/net/main.c ***/
47 // VOID PrintPadding(...)
51 IN PCON_STREAM Stream
,
58 #define MAX_BUFFER_SIZE 4096
60 WCHAR szMsgBuffer
[MAX_BUFFER_SIZE
];
63 Len
= ConResPrintfV(Stream
, uID
, args
);
66 ConPuts(Stream
, L
"\n");
67 for (i
= 0; i
< Len
; i
++)
68 szMsgBuffer
[i
] = L
'-';
69 szMsgBuffer
[Len
] = UNICODE_NULL
;
71 ConStreamWrite(Stream
, szMsgBuffer
, Len
);
74 int ShowParallelStatus(INT nPortNum
)
77 WCHAR szPortName
[MAX_PORTNAME_LEN
];
79 swprintf(szPortName
, L
"LPT%d", nPortNum
);
81 ConPuts(StdOut
, L
"\n");
82 UnderlinedResPrintf(StdOut
, IDS_DEVICE_STATUS_HEADER
, szPortName
);
83 ConPuts(StdOut
, L
"\n");
85 if (QueryDosDeviceW(szPortName
, buffer
, ARRAYSIZE(buffer
)))
87 PWSTR ptr
= wcsrchr(buffer
, L
'\\');
90 if (_wcsicmp(szPortName
, ++ptr
) == 0)
91 ConResPuts(StdOut
, IDS_PRINTER_OUTPUT_NOT_REROUTED
);
93 ConResPrintf(StdOut
, IDS_PRINTER_OUTPUT_REROUTED_SERIAL
, ptr
);
99 ConResPrintf(StdErr
, IDS_ERROR_QUERY_DEVICES_FORM
, szPortName
, buffer
);
104 ConPrintf(StdErr
, L
"ERROR: QueryDosDeviceW(%s) failed: 0x%lx\n", szPortName
, GetLastError());
106 ConPuts(StdOut
, L
"\n");
111 int SetParallelState(INT nPortNum
)
113 WCHAR szPortName
[MAX_PORTNAME_LEN
];
114 WCHAR szTargetPath
[MAX_PORTNAME_LEN
];
116 swprintf(szPortName
, L
"LPT%d", nPortNum
);
117 swprintf(szTargetPath
, L
"COM%d", nPortNum
);
118 if (!DefineDosDeviceW(DDD_REMOVE_DEFINITION
, szPortName
, szTargetPath
))
120 ConPrintf(StdErr
, L
"ERROR: SetParallelState(%d) - DefineDosDevice(%s) failed: 0x%lx\n", nPortNum
, szPortName
, GetLastError());
123 ShowParallelStatus(nPortNum
);
129 ParseNumber(PCWSTR argStr
, PDWORD Number
)
133 value
= swscanf(argStr
, L
"%lu%n", Number
, &skip
);
134 if (!value
) return NULL
;
142 \Device\NamedPipe\Spooler\LPT1
143 BOOL DefineDosDevice(
144 DWORD dwFlags, // options
145 LPCTSTR lpDeviceName, // device name
146 LPCTSTR lpTargetPath // path string
148 DWORD QueryDosDevice(
149 LPCTSTR lpDeviceName, // MS-DOS device name string
150 LPTSTR lpTargetPath, // query results buffer
151 DWORD ucchMax // maximum size of buffer
156 /*****************************************************************************\
157 ** C O N S O L E H E L P E R S **
158 \*****************************************************************************/
160 int ShowConsoleStatus(VOID
)
162 HANDLE hConOut
= GetStdHandle(STD_OUTPUT_HANDLE
);
163 CONSOLE_SCREEN_BUFFER_INFO csbi
;
164 DWORD dwKbdDelay
, dwKbdSpeed
;
166 ConPuts(StdOut
, L
"\n");
167 UnderlinedResPrintf(StdOut
, IDS_DEVICE_STATUS_HEADER
, L
"CON");
168 ConPuts(StdOut
, L
"\n");
170 if (GetConsoleScreenBufferInfo(hConOut
, &csbi
))
172 ConResPrintf(StdOut
, IDS_CONSOLE_STATUS_LINES
, csbi
.dwSize
.Y
);
173 ConResPrintf(StdOut
, IDS_CONSOLE_STATUS_COLS
, csbi
.dwSize
.X
);
175 if (SystemParametersInfoW(SPI_GETKEYBOARDSPEED
, 0, &dwKbdSpeed
, 0))
177 ConResPrintf(StdOut
, IDS_CONSOLE_KBD_RATE
, dwKbdSpeed
);
179 if (SystemParametersInfoW(SPI_GETKEYBOARDDELAY
, 0, &dwKbdDelay
, 0))
181 ConResPrintf(StdOut
, IDS_CONSOLE_KBD_DELAY
, dwKbdDelay
);
183 ConResPrintf(StdOut
, IDS_CONSOLE_CODEPAGE
, GetConsoleOutputCP());
184 ConPuts(StdOut
, L
"\n");
189 int ShowConsoleCPStatus(VOID
)
191 ConPuts(StdOut
, L
"\n");
192 UnderlinedResPrintf(StdOut
, IDS_DEVICE_STATUS_HEADER
, L
"CON");
193 ConPuts(StdOut
, L
"\n");
195 ConResPrintf(StdOut
, IDS_CONSOLE_CODEPAGE
, GetConsoleOutputCP());
196 ConPuts(StdOut
, L
"\n");
204 IN PCONSOLE_SCREEN_BUFFER_INFO pcsbi
)
211 FillConsoleOutputAttribute(hConOut
, pcsbi
->wAttributes
,
212 pcsbi
->dwSize
.X
* pcsbi
->dwSize
.Y
,
214 FillConsoleOutputCharacterW(hConOut
, L
' ',
215 pcsbi
->dwSize
.X
* pcsbi
->dwSize
.Y
,
217 SetConsoleCursorPosition(hConOut
, coPos
);
221 * See, or adjust if needed, subsystems/mvdm/ntvdm/console/video.c!ResizeTextConsole()
222 * for more information.
227 IN OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi
,
235 * Use this trick to effectively resize the console buffer and window,
237 * - SetConsoleScreenBufferSize fails if the new console screen buffer size
238 * is smaller than the current console window size, and:
239 * - SetConsoleWindowInfo fails if the new console window size is larger
240 * than the current console screen buffer size.
243 /* Resize the screen buffer only if needed */
244 if (Resolution
.X
!= pcsbi
->dwSize
.X
|| Resolution
.Y
!= pcsbi
->dwSize
.Y
)
246 Width
= pcsbi
->srWindow
.Right
- pcsbi
->srWindow
.Left
+ 1;
247 Height
= pcsbi
->srWindow
.Bottom
- pcsbi
->srWindow
.Top
+ 1;
250 * If the current console window is too large for
251 * the new screen buffer, resize it first.
253 if (Width
> Resolution
.X
|| Height
> Resolution
.Y
)
256 * NOTE: This is not a problem if we move the window back to (0,0)
257 * because when we resize the screen buffer, the window will move back
258 * to where the cursor is. Or, if the screen buffer is not resized,
259 * when we readjust again the window, we will move back to a correct
260 * position. This is what we wanted after all...
262 ConRect
.Left
= ConRect
.Top
= 0;
263 ConRect
.Right
= ConRect
.Left
+ min(Width
, Resolution
.X
) - 1;
264 ConRect
.Bottom
= ConRect
.Top
+ min(Height
, Resolution
.Y
) - 1;
266 Success
= SetConsoleWindowInfo(hConOut
, TRUE
, &ConRect
);
267 if (!Success
) return FALSE
;
271 * Now resize the screen buffer.
273 * SetConsoleScreenBufferSize automatically takes into account the current
274 * cursor position when it computes starting which row it should copy text
275 * when resizing the screen buffer, and scrolls the console window such that
276 * the cursor is placed in it again. We therefore do not need to care about
277 * the cursor position and do the maths ourselves.
279 Success
= SetConsoleScreenBufferSize(hConOut
, Resolution
);
280 if (!Success
) return FALSE
;
283 * Setting a new screen buffer size can change other information,
284 * so update the console screen buffer information.
286 GetConsoleScreenBufferInfo(hConOut
, pcsbi
);
289 /* Always resize the console window within the permitted maximum size */
290 Width
= min(Resolution
.X
, pcsbi
->dwMaximumWindowSize
.X
);
291 Height
= min(Resolution
.Y
, pcsbi
->dwMaximumWindowSize
.Y
);
293 ConRect
.Right
= ConRect
.Left
+ Width
- 1;
294 ConRect
.Bottom
= max(pcsbi
->dwCursorPosition
.Y
, Height
- 1);
295 ConRect
.Top
= ConRect
.Bottom
- Height
+ 1;
297 SetConsoleWindowInfo(hConOut
, TRUE
, &ConRect
);
299 /* Update the console screen buffer information */
300 GetConsoleScreenBufferInfo(hConOut
, pcsbi
);
305 int SetConsoleStateOld(IN PCWSTR ArgStr
)
307 PCWSTR argStr
= ArgStr
;
309 HANDLE hConOut
= GetStdHandle(STD_OUTPUT_HANDLE
);
310 CONSOLE_SCREEN_BUFFER_INFO csbi
;
314 if (!GetConsoleScreenBufferInfo(hConOut
, &csbi
))
316 // TODO: Error message?
320 Resolution
= csbi
.dwSize
;
322 /* Parse the column number (only MANDATORY argument) */
324 argStr
= ParseNumber(argStr
, &value
);
325 if (!argStr
) goto invalid_parameter
;
326 Resolution
.X
= (SHORT
)value
;
328 /* Parse the line number (OPTIONAL argument) */
329 while (*argStr
== L
' ') argStr
++;
330 if (!*argStr
) goto Quit
;
331 if (*argStr
++ != L
',') goto invalid_parameter
;
332 while (*argStr
== L
' ') argStr
++;
335 argStr
= ParseNumber(argStr
, &value
);
336 if (!argStr
) goto invalid_parameter
;
337 Resolution
.Y
= (SHORT
)value
;
339 /* This should be the end of the string */
340 while (*argStr
== L
' ') argStr
++;
341 if (*argStr
) goto invalid_parameter
;
344 ClearScreen(hConOut
, &csbi
);
345 if (!ResizeTextConsole(hConOut
, &csbi
, Resolution
))
346 ConResPuts(StdErr
, IDS_ERROR_SCREEN_LINES_COL
);
351 ConResPrintf(StdErr
, IDS_ERROR_INVALID_PARAMETER
, ArgStr
);
355 int SetConsoleState(IN PCWSTR ArgStr
)
357 PCWSTR argStr
= ArgStr
;
358 BOOL dispMode
= FALSE
, kbdMode
= FALSE
;
360 HANDLE hConOut
= GetStdHandle(STD_OUTPUT_HANDLE
);
361 CONSOLE_SCREEN_BUFFER_INFO csbi
;
363 DWORD dwKbdDelay
, dwKbdSpeed
;
366 if (!GetConsoleScreenBufferInfo(hConOut
, &csbi
))
368 // TODO: Error message?
371 if (!SystemParametersInfoW(SPI_GETKEYBOARDDELAY
, 0, &dwKbdDelay
, 0))
373 // TODO: Error message?
376 if (!SystemParametersInfoW(SPI_GETKEYBOARDSPEED
, 0, &dwKbdSpeed
, 0))
378 // TODO: Error message?
382 Resolution
= csbi
.dwSize
;
384 while (argStr
&& *argStr
)
386 while (*argStr
== L
' ') argStr
++;
389 if (!kbdMode
&& _wcsnicmp(argStr
, L
"COLS=", 5) == 0)
394 argStr
= ParseNumber(argStr
+5, &value
);
395 if (!argStr
) goto invalid_parameter
;
396 Resolution
.X
= (SHORT
)value
;
398 else if (!kbdMode
&& _wcsnicmp(argStr
, L
"LINES=", 6) == 0)
403 argStr
= ParseNumber(argStr
+6, &value
);
404 if (!argStr
) goto invalid_parameter
;
405 Resolution
.Y
= (SHORT
)value
;
407 else if (!dispMode
&& _wcsnicmp(argStr
, L
"RATE=", 5) == 0)
411 argStr
= ParseNumber(argStr
+5, &dwKbdSpeed
);
412 if (!argStr
) goto invalid_parameter
;
414 else if (!dispMode
&& _wcsnicmp(argStr
, L
"DELAY=", 6) == 0)
418 argStr
= ParseNumber(argStr
+6, &dwKbdDelay
);
419 if (!argStr
) goto invalid_parameter
;
424 ConResPrintf(StdErr
, IDS_ERROR_INVALID_PARAMETER
, ArgStr
);
431 ClearScreen(hConOut
, &csbi
);
432 if (!ResizeTextConsole(hConOut
, &csbi
, Resolution
))
433 ConResPuts(StdErr
, IDS_ERROR_SCREEN_LINES_COL
);
438 * Set the new keyboard settings. If those values are greater than
439 * their allowed range, they are automatically corrected as follows:
440 * dwKbdSpeed = min(dwKbdSpeed, 31);
441 * dwKbdDelay = (dwKbdDelay % 4);
443 SystemParametersInfoW(SPI_SETKEYBOARDDELAY
, dwKbdDelay
, NULL
, 0);
444 // "Invalid keyboard delay."
445 SystemParametersInfoW(SPI_SETKEYBOARDSPEED
, dwKbdSpeed
, NULL
, SPIF_UPDATEINIFILE
| SPIF_SENDCHANGE
);
446 // "Invalid keyboard rate."
452 int SetConsoleCPState(IN PCWSTR ArgStr
)
454 PCWSTR argStr
= ArgStr
;
456 UINT uOldCodePage
, uNewCodePage
;
458 if ( (_wcsnicmp(argStr
, L
"SELECT=", 7) == 0 && (argStr
+= 7)) ||
459 (_wcsnicmp(argStr
, L
"SEL=", 4) == 0 && (argStr
+= 4)) )
461 argStr
= ParseNumber(argStr
, &value
);
462 if (!argStr
) goto invalid_parameter
;
464 /* This should be the end of the string */
465 while (*argStr
== L
' ') argStr
++;
466 if (*argStr
) goto invalid_parameter
;
471 ConResPrintf(StdErr
, IDS_ERROR_INVALID_PARAMETER
, ArgStr
);
475 uNewCodePage
= value
;
478 ** IMPORTANT NOTE: This code must be kept synchronized with CHCP.COM
482 * Save the original console code page to be restored
483 * in case SetConsoleCP() or SetConsoleOutputCP() fails.
485 uOldCodePage
= GetConsoleCP();
488 * Try changing the console input and output code pages.
489 * If it succeeds, refresh the local code page information.
491 if (SetConsoleCP(uNewCodePage
))
493 if (SetConsoleOutputCP(uNewCodePage
))
495 /* Success, reset the current thread UI language
496 * and update the streams cached code page. */
497 ConSetThreadUILanguage(0);
498 ConStdStreamsSetCacheCodePage(uNewCodePage
, uNewCodePage
);
500 /* Display the current console status */
506 /* Failure, restore the original console code page */
507 SetConsoleCP(uOldCodePage
);
511 /* An error happened, display an error and bail out */
512 ConResPuts(StdErr
, IDS_ERROR_INVALID_CODEPAGE
);
517 /*****************************************************************************\
518 ** S E R I A L P O R T H E L P E R S **
519 \*****************************************************************************/
522 SerialPortQuery(INT nPortNum
, LPDCB pDCB
, LPCOMMTIMEOUTS pCommTimeouts
, BOOL bWrite
)
526 WCHAR szPortName
[MAX_PORTNAME_LEN
];
529 ASSERT(pCommTimeouts
);
531 swprintf(szPortName
, L
"COM%d", nPortNum
);
532 hPort
= CreateFileW(szPortName
,
533 bWrite
? GENERIC_WRITE
: GENERIC_READ
,
538 NULL
); // no template
540 if (hPort
== INVALID_HANDLE_VALUE
)
542 DWORD dwLastError
= GetLastError();
543 if (dwLastError
== ERROR_ACCESS_DENIED
)
544 ConResPrintf(StdErr
, IDS_ERROR_DEVICE_NOT_AVAILABLE
, szPortName
);
546 ConResPrintf(StdErr
, IDS_ERROR_ILLEGAL_DEVICE_NAME
, szPortName
, dwLastError
);
550 Success
= bWrite
? SetCommState(hPort
, pDCB
)
551 : GetCommState(hPort
, pDCB
);
555 bWrite
? IDS_ERROR_STATUS_SET_DEVICE
: IDS_ERROR_STATUS_GET_DEVICE
,
560 Success
= bWrite
? SetCommTimeouts(hPort
, pCommTimeouts
)
561 : GetCommTimeouts(hPort
, pCommTimeouts
);
565 bWrite
? IDS_ERROR_TIMEOUT_SET_DEVICE
: IDS_ERROR_TIMEOUT_GET_DEVICE
,
575 int ShowSerialStatus(INT nPortNum
)
577 static const LPCWSTR parity_strings
[] =
581 L
"Even", // EVENPARITY
582 L
"Mark", // MARKPARITY
583 L
"Space" // SPACEPARITY
585 static const LPCWSTR control_strings
[] = { L
"OFF", L
"ON", L
"HANDSHAKE", L
"TOGGLE" };
586 static const LPCWSTR stopbit_strings
[] = { L
"1", L
"1.5", L
"2" };
589 COMMTIMEOUTS CommTimeouts
;
590 WCHAR szPortName
[MAX_PORTNAME_LEN
];
592 if (!SerialPortQuery(nPortNum
, &dcb
, &CommTimeouts
, FALSE
))
596 if (dcb
.Parity
>= ARRAYSIZE(parity_strings
))
598 ConResPrintf(StdErr
, IDS_ERROR_INVALID_PARITY_BITS
, dcb
.Parity
);
601 if (dcb
.StopBits
>= ARRAYSIZE(stopbit_strings
))
603 ConResPrintf(StdErr
, IDS_ERROR_INVALID_STOP_BITS
, dcb
.StopBits
);
607 swprintf(szPortName
, L
"COM%d", nPortNum
);
609 ConPuts(StdOut
, L
"\n");
610 UnderlinedResPrintf(StdOut
, IDS_DEVICE_STATUS_HEADER
, szPortName
);
611 ConPuts(StdOut
, L
"\n");
613 ConResPrintf(StdOut
, IDS_COM_STATUS_BAUD
, dcb
.BaudRate
);
614 ConResPrintf(StdOut
, IDS_COM_STATUS_PARITY
, parity_strings
[dcb
.Parity
]);
615 ConResPrintf(StdOut
, IDS_COM_STATUS_DATA_BITS
, dcb
.ByteSize
);
616 ConResPrintf(StdOut
, IDS_COM_STATUS_STOP_BITS
, stopbit_strings
[dcb
.StopBits
]);
617 ConResPrintf(StdOut
, IDS_COM_STATUS_TIMEOUT
,
618 control_strings
[(CommTimeouts
.ReadTotalTimeoutConstant
!= 0) ||
619 (CommTimeouts
.WriteTotalTimeoutConstant
!= 0) ? 1 : 0]);
620 ConResPrintf(StdOut
, IDS_COM_STATUS_XON_XOFF
,
621 control_strings
[dcb
.fOutX
? 1 : 0]);
622 ConResPrintf(StdOut
, IDS_COM_STATUS_CTS_HANDSHAKING
,
623 control_strings
[dcb
.fOutxCtsFlow
? 1 : 0]);
624 ConResPrintf(StdOut
, IDS_COM_STATUS_DSR_HANDSHAKING
,
625 control_strings
[dcb
.fOutxDsrFlow
? 1 : 0]);
626 ConResPrintf(StdOut
, IDS_COM_STATUS_DSR_SENSITIVITY
,
627 control_strings
[dcb
.fDsrSensitivity
? 1 : 0]);
628 ConResPrintf(StdOut
, IDS_COM_STATUS_DTR_CIRCUIT
, control_strings
[dcb
.fDtrControl
]);
629 ConResPrintf(StdOut
, IDS_COM_STATUS_RTS_CIRCUIT
, control_strings
[dcb
.fRtsControl
]);
630 ConPuts(StdOut
, L
"\n");
637 * Those procedures are inspired from Wine's dll/win32/kernel32/wine/comm.c
638 * Copyright 1996 Erik Bos and Marcus Meissner.
642 ParseModes(PCWSTR argStr
, PBYTE Mode
)
644 if (_wcsnicmp(argStr
, L
"OFF", 3) == 0)
649 else if (_wcsnicmp(argStr
, L
"ON", 2) == 0)
654 else if (_wcsnicmp(argStr
, L
"HS", 2) == 0)
659 else if (_wcsnicmp(argStr
, L
"TG", 2) == 0)
669 ParseBaudRate(PCWSTR argStr
, PDWORD BaudRate
)
671 argStr
= ParseNumber(argStr
, BaudRate
);
672 if (!argStr
) return NULL
;
675 * Check for Baud Rate abbreviations. This means that using
676 * those values as real baud rates is impossible using MODE.
680 /* BaudRate = 110, 150, 300, 600 */
681 case 11: case 15: case 30: case 60:
685 /* BaudRate = 1200, 2400, 4800, 9600 */
686 case 12: case 24: case 48: case 96:
699 ParseParity(PCWSTR argStr
, PBYTE Parity
)
701 switch (towupper(*argStr
++))
712 *Parity
= EVENPARITY
;
716 *Parity
= MARKPARITY
;
720 *Parity
= SPACEPARITY
;
731 ParseByteSize(PCWSTR argStr
, PBYTE ByteSize
)
735 argStr
= ParseNumber(argStr
, &value
);
736 if (!argStr
) return NULL
;
738 *ByteSize
= (BYTE
)value
;
739 if (*ByteSize
< 5 || *ByteSize
> 8)
746 ParseStopBits(PCWSTR argStr
, PBYTE StopBits
)
748 if (_wcsnicmp(argStr
, L
"1.5", 3) == 0)
751 *StopBits
= ONE5STOPBITS
;
756 *StopBits
= ONESTOPBIT
;
757 else if (*argStr
== L
'2')
758 *StopBits
= TWOSTOPBITS
;
769 * Build a DCB using the old style settings string eg: "96,n,8,1"
771 * See dll/win32/kernel32/wine/comm.c!COMM_BuildOldCommDCB()
772 * for more information.
779 PCWSTR argStr
= ArgStr
;
783 * Parse the baud rate (only MANDATORY argument)
785 argStr
= ParseBaudRate(argStr
, &pDCB
->BaudRate
);
786 if (!argStr
) return FALSE
;
790 * Now parse the rest (OPTIONAL arguments)
793 while (*argStr
== L
' ') argStr
++;
794 if (!*argStr
) goto Quit
;
795 if (*argStr
++ != L
',') return FALSE
;
796 while (*argStr
== L
' ') argStr
++;
797 if (!*argStr
) goto Quit
;
799 /* Parse the parity */
800 // Default: EVENPARITY
801 pDCB
->Parity
= EVENPARITY
;
804 argStr
= ParseParity(argStr
, &pDCB
->Parity
);
805 if (!argStr
) return FALSE
;
808 while (*argStr
== L
' ') argStr
++;
809 if (!*argStr
) goto Quit
;
810 if (*argStr
++ != L
',') return FALSE
;
811 while (*argStr
== L
' ') argStr
++;
812 if (!*argStr
) goto Quit
;
814 /* Parse the data bits */
819 argStr
= ParseByteSize(argStr
, &pDCB
->ByteSize
);
820 if (!argStr
) return FALSE
;
823 while (*argStr
== L
' ') argStr
++;
824 if (!*argStr
) goto Quit
;
825 if (*argStr
++ != L
',') return FALSE
;
826 while (*argStr
== L
' ') argStr
++;
827 if (!*argStr
) goto Quit
;
829 /* Parse the stop bits */
830 // Default: 1, or 2 for BAUD=110
831 // pDCB->StopBits = ONESTOPBIT;
835 argStr
= ParseStopBits(argStr
, &pDCB
->StopBits
);
836 if (!argStr
) return FALSE
;
839 /* The last parameter (flow control "retry") is really optional */
840 while (*argStr
== L
' ') argStr
++;
841 if (!*argStr
) goto Quit
;
842 if (*argStr
++ != L
',') return FALSE
;
843 while (*argStr
== L
' ') argStr
++;
844 if (!*argStr
) goto Quit
;
847 switch (towupper(*argStr
))
852 pDCB
->fOutxCtsFlow
= FALSE
;
853 pDCB
->fOutxDsrFlow
= FALSE
;
854 pDCB
->fDtrControl
= DTR_CONTROL_ENABLE
;
855 pDCB
->fRtsControl
= RTS_CONTROL_ENABLE
;
861 pDCB
->fOutxCtsFlow
= FALSE
;
862 pDCB
->fOutxDsrFlow
= FALSE
;
863 pDCB
->fDtrControl
= DTR_CONTROL_ENABLE
;
864 pDCB
->fRtsControl
= RTS_CONTROL_ENABLE
;
870 pDCB
->fOutxCtsFlow
= TRUE
;
871 pDCB
->fOutxDsrFlow
= TRUE
;
872 pDCB
->fDtrControl
= DTR_CONTROL_HANDSHAKE
;
873 pDCB
->fRtsControl
= RTS_CONTROL_HANDSHAKE
;
880 if (*argStr
) argStr
++;
882 /* This should be the end of the string */
883 while (*argStr
== L
' ') argStr
++;
884 if (*argStr
) return FALSE
;
886 /* If stop bits were not specified, a default is always supplied */
889 if (pDCB
->BaudRate
== 110)
890 pDCB
->StopBits
= TWOSTOPBITS
;
892 pDCB
->StopBits
= ONESTOPBIT
;
898 * Build a DCB using the new style settings string.
899 * eg: "baud=9600 parity=n data=8 stop=1 xon=on to=on"
901 * See dll/win32/kernel32/wine/comm.c!COMM_BuildNewCommDCB()
902 * for more information.
907 OUT LPCOMMTIMEOUTS pCommTimeouts
,
910 PCWSTR argStr
= ArgStr
;
911 BOOL baud
= FALSE
, stop
= FALSE
;
914 while (argStr
&& *argStr
)
916 while (*argStr
== L
' ') argStr
++;
919 if (_wcsnicmp(argStr
, L
"BAUD=", 5) == 0)
922 argStr
= ParseBaudRate(argStr
+5, &pDCB
->BaudRate
);
923 if (!argStr
) return FALSE
;
925 else if (_wcsnicmp(argStr
, L
"PARITY=", 7) == 0)
927 // Default: EVENPARITY
928 argStr
= ParseParity(argStr
+7, &pDCB
->Parity
);
929 if (!argStr
) return FALSE
;
931 else if (_wcsnicmp(argStr
, L
"DATA=", 5) == 0)
934 argStr
= ParseByteSize(argStr
+5, &pDCB
->ByteSize
);
935 if (!argStr
) return FALSE
;
937 else if (_wcsnicmp(argStr
, L
"STOP=", 5) == 0)
939 // Default: 1, or 2 for BAUD=110
941 argStr
= ParseStopBits(argStr
+5, &pDCB
->StopBits
);
942 if (!argStr
) return FALSE
;
944 else if (_wcsnicmp(argStr
, L
"TO=", 3) == 0) // TO=ON|OFF
946 /* Only total time-outs are get/set by Windows' MODE.COM */
947 argStr
= ParseModes(argStr
+3, &value
);
948 if (!argStr
) return FALSE
;
949 if (value
== 0) // OFF
951 pCommTimeouts
->ReadTotalTimeoutConstant
= 0;
952 pCommTimeouts
->WriteTotalTimeoutConstant
= 0;
954 else if (value
== 1) // ON
956 pCommTimeouts
->ReadTotalTimeoutConstant
= 60000;
957 pCommTimeouts
->WriteTotalTimeoutConstant
= 60000;
964 else if (_wcsnicmp(argStr
, L
"XON=", 4) == 0) // XON=ON|OFF
966 argStr
= ParseModes(argStr
+4, &value
);
967 if (!argStr
) return FALSE
;
968 if ((value
== 0) || (value
== 1))
978 else if (_wcsnicmp(argStr
, L
"ODSR=", 5) == 0) // ODSR=ON|OFF
981 argStr
= ParseModes(argStr
+5, &value
);
982 if (!argStr
) return FALSE
;
983 if ((value
== 0) || (value
== 1))
984 pDCB
->fOutxDsrFlow
= value
;
988 else if (_wcsnicmp(argStr
, L
"OCTS=", 5) == 0) // OCTS=ON|OFF
991 argStr
= ParseModes(argStr
+5, &value
);
992 if (!argStr
) return FALSE
;
993 if ((value
== 0) || (value
== 1))
994 pDCB
->fOutxCtsFlow
= value
;
998 else if (_wcsnicmp(argStr
, L
"DTR=", 4) == 0) // DTR=ON|OFF|HS
1001 argStr
= ParseModes(argStr
+4, &value
);
1002 if (!argStr
) return FALSE
;
1003 if ((value
== 0) || (value
== 1) || (value
== 2))
1004 pDCB
->fDtrControl
= value
;
1008 else if (_wcsnicmp(argStr
, L
"RTS=", 4) == 0) // RTS=ON|OFF|HS|TG
1011 argStr
= ParseModes(argStr
+4, &value
);
1012 if (!argStr
) return FALSE
;
1013 if ((value
== 0) || (value
== 1) || (value
== 2) || (value
== 3))
1014 pDCB
->fRtsControl
= value
;
1018 else if (_wcsnicmp(argStr
, L
"IDSR=", 5) == 0) // IDSR=ON|OFF
1021 argStr
= ParseModes(argStr
+5, &value
);
1022 if (!argStr
) return FALSE
;
1023 if ((value
== 0) || (value
== 1))
1024 pDCB
->fDsrSensitivity
= value
;
1034 /* If stop bits were not specified, a default is always supplied */
1037 if (baud
&& pDCB
->BaudRate
== 110)
1038 pDCB
->StopBits
= TWOSTOPBITS
;
1040 pDCB
->StopBits
= ONESTOPBIT
;
1045 int SetSerialState(INT nPortNum
, IN PCWSTR ArgStr
)
1049 COMMTIMEOUTS CommTimeouts
;
1051 if (!SerialPortQuery(nPortNum
, &dcb
, &CommTimeouts
, FALSE
))
1053 // TODO: Error message?
1058 * Check whether we should use the old or the new MODE syntax:
1059 * in the old syntax, the separators are both spaces and commas.
1061 if (wcschr(ArgStr
, L
','))
1062 Success
= BuildOldCommDCB(&dcb
, ArgStr
);
1064 Success
= BuildNewCommDCB(&dcb
, &CommTimeouts
, ArgStr
);
1068 ConResPrintf(StdErr
, IDS_ERROR_INVALID_PARAMETER
, ArgStr
);
1072 SerialPortQuery(nPortNum
, &dcb
, &CommTimeouts
, TRUE
);
1073 ShowSerialStatus(nPortNum
);
1079 /*****************************************************************************\
1080 ** E N T R Y P O I N T **
1081 \*****************************************************************************/
1084 FindPortNum(PCWSTR argStr
, PINT PortNum
)
1086 PWSTR endptr
= NULL
;
1088 *PortNum
= wcstol(argStr
, &endptr
, 10);
1089 if (endptr
== argStr
)
1098 int EnumerateDevices(VOID
)
1102 DWORD dwLen
= MAX_PATH
;
1105 /* Pre-allocate a buffer for QueryDosDeviceW() */
1106 Buffer
= HeapAlloc(GetProcessHeap(), 0, dwLen
* sizeof(WCHAR
));
1109 /* We failed, bail out */
1110 ConPuts(StdErr
, L
"ERROR: Not enough memory\n");
1116 *Buffer
= UNICODE_NULL
;
1117 if (QueryDosDeviceW(NULL
, Buffer
, dwLen
))
1120 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
1122 /* We failed, bail out */
1123 ConPrintf(StdErr
, L
"ERROR: QueryDosDeviceW(...) failed: 0x%lx\n", GetLastError());
1124 HeapFree(GetProcessHeap(), 0, Buffer
);
1128 /* The buffer was too small, try to re-allocate it */
1130 ptr
= HeapReAlloc(GetProcessHeap(), 0, Buffer
, dwLen
* sizeof(WCHAR
));
1133 /* We failed, bail out */
1134 ConPuts(StdErr
, L
"ERROR: Not enough memory\n");
1135 HeapFree(GetProcessHeap(), 0, Buffer
);
1141 for (ptr
= Buffer
; *ptr
!= UNICODE_NULL
; ptr
+= wcslen(ptr
) + 1)
1143 if (_wcsnicmp(ptr
, L
"COM", 3) == 0)
1145 argStr
= FindPortNum(ptr
+3, &nPortNum
);
1146 if (!argStr
|| *argStr
|| nPortNum
== -1)
1149 // ConResPrintf(StdOut, IDS_QUERY_SERIAL_FOUND, ptr);
1150 ShowSerialStatus(nPortNum
);
1152 else if (_wcsicmp(ptr
, L
"PRN") == 0)
1154 ConResPrintf(StdOut
, IDS_QUERY_PRINTER_FOUND
, ptr
);
1156 else if (_wcsnicmp(ptr
, L
"LPT", 3) == 0)
1158 argStr
= FindPortNum(ptr
+3, &nPortNum
);
1159 if (!argStr
|| *argStr
|| nPortNum
== -1)
1162 // ConResPrintf(StdOut, IDS_QUERY_PARALLEL_FOUND, ptr);
1163 ShowParallelStatus(nPortNum
);
1165 else if (_wcsicmp(ptr
, L
"AUX") == 0 || _wcsicmp(ptr
, L
"NUL") == 0)
1167 ConResPrintf(StdOut
, IDS_QUERY_DOSDEV_FOUND
, ptr
);
1171 // ConResPrintf(StdOut, IDS_QUERY_MISC_FOUND, ptr);
1175 ShowConsoleStatus();
1177 /* Free the buffer and return success */
1178 HeapFree(GetProcessHeap(), 0, Buffer
);
1182 int wmain(int argc
, WCHAR
* argv
[])
1187 PCWSTR ArgStr
, argStr
;
1191 /* Initialize the Console Standard Streams */
1192 ConInitStdStreams();
1195 * MODE.COM has a very peculiar way of parsing its arguments,
1196 * as they can be even not separated by any space. This extreme
1197 * behaviour certainly is present for backwards compatibility
1198 * with the oldest versions of the utility present on MS-DOS.
1200 * For example, such a command:
1201 * "MODE.COM COM1baud=9600parity=ndata=8stop=1xon=onto=on"
1202 * will be correctly understood as:
1203 * "MODE.COM COM1 baud=9600 parity=n data=8 stop=1 xon=on to=on"
1205 * Note also that the "/STATUS" switch is actually really "/STA".
1207 * However we will not use GetCommandLine() because we do not want
1208 * to deal with the prepended application path and try to find
1209 * where the arguments start. Our approach here will consist in
1210 * flattening the arguments vector.
1214 /* Compute the space needed for the new string, and allocate it */
1215 for (arg
= 1; arg
< argc
; arg
++)
1217 ArgStrSize
+= wcslen(argv
[arg
]) + 1; // 1 for space
1219 ArgStr
= HeapAlloc(GetProcessHeap(), 0, (ArgStrSize
+ 1) * sizeof(WCHAR
));
1222 ConPuts(StdErr
, L
"ERROR: Not enough memory\n");
1226 /* Copy the contents and NULL-terminate the string */
1228 for (arg
= 1; arg
< argc
; arg
++)
1230 wcscpy((PWSTR
)argStr
, argv
[arg
]);
1231 argStr
+= wcslen(argv
[arg
]);
1232 *(PWSTR
)argStr
++ = L
' ';
1234 *(PWSTR
)argStr
= L
'\0';
1236 /* Parse the command line */
1239 while (*argStr
== L
' ') argStr
++;
1240 if (!*argStr
) goto show_status
;
1242 if (wcsstr(argStr
, L
"/?") || wcsstr(argStr
, L
"-?"))
1244 ConResPuts(StdOut
, IDS_USAGE
);
1247 else if (_wcsnicmp(argStr
, L
"/STA", 4) == 0)
1249 /* Skip this parameter */
1250 while (*argStr
!= L
' ') argStr
++;
1251 /* Skip any delimiter */
1252 while (*argStr
== L
' ') argStr
++;
1254 /* The presence of any other parameter is invalid */
1256 goto invalid_parameter
;
1260 else if (_wcsnicmp(argStr
, L
"LPT", 3) == 0)
1262 argStr
= FindPortNum(argStr
+3, &nPortNum
);
1263 if (!argStr
|| nPortNum
== -1)
1264 goto invalid_parameter
;
1266 if (*argStr
== L
':') argStr
++;
1267 while (*argStr
== L
' ') argStr
++;
1269 if (!*argStr
|| _wcsnicmp(argStr
, L
"/STA", 4) == 0)
1270 ret
= ShowParallelStatus(nPortNum
);
1272 ConPuts(StdErr
, L
"ERROR: LPT port redirection is not implemented!\n");
1273 // TODO: Implement setting LPT port redirection using SetParallelState().
1276 else if (_wcsnicmp(argStr
, L
"COM", 3) == 0)
1278 argStr
= FindPortNum(argStr
+3, &nPortNum
);
1279 if (!argStr
|| nPortNum
== -1)
1280 goto invalid_parameter
;
1282 if (*argStr
== L
':') argStr
++;
1283 while (*argStr
== L
' ') argStr
++;
1285 if (!*argStr
|| _wcsnicmp(argStr
, L
"/STA", 4) == 0)
1286 ret
= ShowSerialStatus(nPortNum
);
1288 ret
= SetSerialState(nPortNum
, argStr
);
1291 else if (_wcsnicmp(argStr
, L
"CON", 3) == 0)
1295 if (*argStr
== L
':') argStr
++;
1296 while (*argStr
== L
' ') argStr
++;
1298 if (!*argStr
|| _wcsnicmp(argStr
, L
"/STA", 4) == 0)
1300 ret
= ShowConsoleStatus();
1302 else if ( (_wcsnicmp(argStr
, L
"CP", 2) == 0 && (argStr
+= 2)) ||
1303 (_wcsnicmp(argStr
, L
"CODEPAGE", 8) == 0 && (argStr
+= 8)) )
1305 while (*argStr
== L
' ') argStr
++;
1307 if (!*argStr
|| _wcsnicmp(argStr
, L
"/STA", 4) == 0)
1308 ret
= ShowConsoleCPStatus();
1310 ret
= SetConsoleCPState(argStr
);
1314 ret
= SetConsoleState(argStr
);
1318 // else if (wcschr(argStr, L','))
1321 /* Old syntax: MODE [COLS],[LINES] */
1322 ret
= SetConsoleStateOld(argStr
);
1331 ConResPrintf(StdErr
, IDS_ERROR_INVALID_PARAMETER
, ArgStr
);
1335 /* Free the string and quit */
1336 HeapFree(GetProcessHeap(), 0, (PWSTR
)ArgStr
);