1 /* 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 7 */ 8 /* 9 * ReactOS mode console command 10 * 11 * mode.c 12 * 13 * Copyright (C) 2002 Robert Dickenson <robd@reactos.org> 14 * 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. 19 * 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. 24 * 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. 28 */ 29 30 #include <stdio.h> 31 #include <stdlib.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 44 #define ASSERT(a) 45 46 /*** For fixes, see also network/net/main.c ***/ 47 // VOID PrintPadding(...) 48 VOID 49 __cdecl 50 UnderlinedResPrintf( 51 IN PCON_STREAM Stream, 52 IN UINT uID, 53 ...) 54 { 55 INT Len; 56 va_list args; 57 58 #define MAX_BUFFER_SIZE 4096 59 INT i; 60 WCHAR szMsgBuffer[MAX_BUFFER_SIZE]; 61 62 va_start(args, uID); 63 Len = ConResPrintfV(Stream, uID, args); 64 va_end(args); 65 66 ConPuts(Stream, L"\n"); 67 for (i = 0; i < Len; i++) 68 szMsgBuffer[i] = L'-'; 69 szMsgBuffer[Len] = UNICODE_NULL; 70 71 ConStreamWrite(Stream, szMsgBuffer, Len); 72 } 73 74 int ShowParallelStatus(INT nPortNum) 75 { 76 WCHAR buffer[250]; 77 WCHAR szPortName[MAX_PORTNAME_LEN]; 78 79 swprintf(szPortName, L"LPT%d", nPortNum); 80 81 ConPuts(StdOut, L"\n"); 82 UnderlinedResPrintf(StdOut, IDS_DEVICE_STATUS_HEADER, szPortName); 83 ConPuts(StdOut, L"\n"); 84 85 if (QueryDosDeviceW(szPortName, buffer, ARRAYSIZE(buffer))) 86 { 87 PWSTR ptr = wcsrchr(buffer, L'\\'); 88 if (ptr != NULL) 89 { 90 if (_wcsicmp(szPortName, ++ptr) == 0) 91 ConResPuts(StdOut, IDS_PRINTER_OUTPUT_NOT_REROUTED); 92 else 93 ConResPrintf(StdOut, IDS_PRINTER_OUTPUT_REROUTED_SERIAL, ptr); 94 95 return 0; 96 } 97 else 98 { 99 ConResPrintf(StdErr, IDS_ERROR_QUERY_DEVICES_FORM, szPortName, buffer); 100 } 101 } 102 else 103 { 104 ConPrintf(StdErr, L"ERROR: QueryDosDeviceW(%s) failed: 0x%lx\n", szPortName, GetLastError()); 105 } 106 ConPuts(StdOut, L"\n"); 107 108 return 1; 109 } 110 111 int SetParallelState(INT nPortNum) 112 { 113 WCHAR szPortName[MAX_PORTNAME_LEN]; 114 WCHAR szTargetPath[MAX_PORTNAME_LEN]; 115 116 swprintf(szPortName, L"LPT%d", nPortNum); 117 swprintf(szTargetPath, L"COM%d", nPortNum); 118 if (!DefineDosDeviceW(DDD_REMOVE_DEFINITION, szPortName, szTargetPath)) 119 { 120 ConPrintf(StdErr, L"ERROR: SetParallelState(%d) - DefineDosDevice(%s) failed: 0x%lx\n", nPortNum, szPortName, GetLastError()); 121 } 122 123 ShowParallelStatus(nPortNum); 124 return 0; 125 } 126 127 128 static PCWSTR 129 ParseNumber(PCWSTR argStr, PDWORD Number) 130 { 131 INT value, skip = 0; 132 133 value = swscanf(argStr, L"%lu%n", Number, &skip); 134 if (!value) return NULL; 135 argStr += skip; 136 return argStr; 137 } 138 139 140 /* 141 \??\COM1 142 \Device\NamedPipe\Spooler\LPT1 143 BOOL DefineDosDevice( 144 DWORD dwFlags, // options 145 LPCTSTR lpDeviceName, // device name 146 LPCTSTR lpTargetPath // path string 147 ); 148 DWORD QueryDosDevice( 149 LPCTSTR lpDeviceName, // MS-DOS device name string 150 LPTSTR lpTargetPath, // query results buffer 151 DWORD ucchMax // maximum size of buffer 152 ); 153 */ 154 155 156 /*****************************************************************************\ 157 ** C O N S O L E H E L P E R S ** 158 \*****************************************************************************/ 159 160 int ShowConsoleStatus(VOID) 161 { 162 HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE); 163 CONSOLE_SCREEN_BUFFER_INFO csbi; 164 DWORD dwKbdDelay, dwKbdSpeed; 165 166 ConPuts(StdOut, L"\n"); 167 UnderlinedResPrintf(StdOut, IDS_DEVICE_STATUS_HEADER, L"CON"); 168 ConPuts(StdOut, L"\n"); 169 170 if (GetConsoleScreenBufferInfo(hConOut, &csbi)) 171 { 172 ConResPrintf(StdOut, IDS_CONSOLE_STATUS_LINES, csbi.dwSize.Y); 173 ConResPrintf(StdOut, IDS_CONSOLE_STATUS_COLS , csbi.dwSize.X); 174 } 175 if (SystemParametersInfoW(SPI_GETKEYBOARDSPEED, 0, &dwKbdSpeed, 0)) 176 { 177 ConResPrintf(StdOut, IDS_CONSOLE_KBD_RATE, dwKbdSpeed); 178 } 179 if (SystemParametersInfoW(SPI_GETKEYBOARDDELAY, 0, &dwKbdDelay, 0)) 180 { 181 ConResPrintf(StdOut, IDS_CONSOLE_KBD_DELAY, dwKbdDelay); 182 } 183 ConResPrintf(StdOut, IDS_CONSOLE_CODEPAGE, GetConsoleOutputCP()); 184 ConPuts(StdOut, L"\n"); 185 186 return 0; 187 } 188 189 int ShowConsoleCPStatus(VOID) 190 { 191 ConPuts(StdOut, L"\n"); 192 UnderlinedResPrintf(StdOut, IDS_DEVICE_STATUS_HEADER, L"CON"); 193 ConPuts(StdOut, L"\n"); 194 195 ConResPrintf(StdOut, IDS_CONSOLE_CODEPAGE, GetConsoleOutputCP()); 196 ConPuts(StdOut, L"\n"); 197 198 return 0; 199 } 200 201 static VOID 202 ClearScreen( 203 IN HANDLE hConOut, 204 IN PCONSOLE_SCREEN_BUFFER_INFO pcsbi) 205 { 206 COORD coPos; 207 DWORD dwWritten; 208 209 coPos.X = 0; 210 coPos.Y = 0; 211 FillConsoleOutputAttribute(hConOut, pcsbi->wAttributes, 212 pcsbi->dwSize.X * pcsbi->dwSize.Y, 213 coPos, &dwWritten); 214 FillConsoleOutputCharacterW(hConOut, L' ', 215 pcsbi->dwSize.X * pcsbi->dwSize.Y, 216 coPos, &dwWritten); 217 SetConsoleCursorPosition(hConOut, coPos); 218 } 219 220 /* 221 * See, or adjust if needed, subsystems/mvdm/ntvdm/console/video.c!ResizeTextConsole() 222 * for more information. 223 */ 224 static BOOL 225 ResizeTextConsole( 226 IN HANDLE hConOut, 227 IN OUT PCONSOLE_SCREEN_BUFFER_INFO pcsbi, 228 IN COORD Resolution) 229 { 230 BOOL Success; 231 SHORT Width, Height; 232 SMALL_RECT ConRect; 233 234 /* 235 * Use this trick to effectively resize the console buffer and window, 236 * because: 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. 241 */ 242 243 /* Resize the screen buffer only if needed */ 244 if (Resolution.X != pcsbi->dwSize.X || Resolution.Y != pcsbi->dwSize.Y) 245 { 246 Width = pcsbi->srWindow.Right - pcsbi->srWindow.Left + 1; 247 Height = pcsbi->srWindow.Bottom - pcsbi->srWindow.Top + 1; 248 249 /* 250 * If the current console window is too large for 251 * the new screen buffer, resize it first. 252 */ 253 if (Width > Resolution.X || Height > Resolution.Y) 254 { 255 /* 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... 261 */ 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; 265 266 Success = SetConsoleWindowInfo(hConOut, TRUE, &ConRect); 267 if (!Success) return FALSE; 268 } 269 270 /* 271 * Now resize the screen buffer. 272 * 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. 278 */ 279 Success = SetConsoleScreenBufferSize(hConOut, Resolution); 280 if (!Success) return FALSE; 281 282 /* 283 * Setting a new screen buffer size can change other information, 284 * so update the console screen buffer information. 285 */ 286 GetConsoleScreenBufferInfo(hConOut, pcsbi); 287 } 288 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); 292 ConRect.Left = 0; 293 ConRect.Right = ConRect.Left + Width - 1; 294 ConRect.Bottom = max(pcsbi->dwCursorPosition.Y, Height - 1); 295 ConRect.Top = ConRect.Bottom - Height + 1; 296 297 SetConsoleWindowInfo(hConOut, TRUE, &ConRect); 298 299 /* Update the console screen buffer information */ 300 GetConsoleScreenBufferInfo(hConOut, pcsbi); 301 302 return TRUE; 303 } 304 305 int SetConsoleStateOld(IN PCWSTR ArgStr) 306 { 307 PCWSTR argStr = ArgStr; 308 309 HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE); 310 CONSOLE_SCREEN_BUFFER_INFO csbi; 311 COORD Resolution; 312 DWORD value; 313 314 if (!GetConsoleScreenBufferInfo(hConOut, &csbi)) 315 { 316 // TODO: Error message? 317 return 0; 318 } 319 320 Resolution = csbi.dwSize; 321 322 /* Parse the column number (only MANDATORY argument) */ 323 value = 0; 324 argStr = ParseNumber(argStr, &value); 325 if (!argStr) goto invalid_parameter; 326 Resolution.X = (SHORT)value; 327 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++; 333 334 value = 0; 335 argStr = ParseNumber(argStr, &value); 336 if (!argStr) goto invalid_parameter; 337 Resolution.Y = (SHORT)value; 338 339 /* This should be the end of the string */ 340 while (*argStr == L' ') argStr++; 341 if (*argStr) goto invalid_parameter; 342 343 Quit: 344 ClearScreen(hConOut, &csbi); 345 if (!ResizeTextConsole(hConOut, &csbi, Resolution)) 346 ConResPuts(StdErr, IDS_ERROR_SCREEN_LINES_COL); 347 348 return 0; 349 350 invalid_parameter: 351 ConResPrintf(StdErr, IDS_ERROR_INVALID_PARAMETER, ArgStr); 352 return 1; 353 } 354 355 int SetConsoleState(IN PCWSTR ArgStr) 356 { 357 PCWSTR argStr = ArgStr; 358 BOOL dispMode = FALSE, kbdMode = FALSE; 359 360 HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE); 361 CONSOLE_SCREEN_BUFFER_INFO csbi; 362 COORD Resolution; 363 DWORD dwKbdDelay, dwKbdSpeed; 364 DWORD value; 365 366 if (!GetConsoleScreenBufferInfo(hConOut, &csbi)) 367 { 368 // TODO: Error message? 369 return 0; 370 } 371 if (!SystemParametersInfoW(SPI_GETKEYBOARDDELAY, 0, &dwKbdDelay, 0)) 372 { 373 // TODO: Error message? 374 return 0; 375 } 376 if (!SystemParametersInfoW(SPI_GETKEYBOARDSPEED, 0, &dwKbdSpeed, 0)) 377 { 378 // TODO: Error message? 379 return 0; 380 } 381 382 Resolution = csbi.dwSize; 383 384 while (argStr && *argStr) 385 { 386 while (*argStr == L' ') argStr++; 387 if (!*argStr) break; 388 389 if (!kbdMode && _wcsnicmp(argStr, L"COLS=", 5) == 0) 390 { 391 dispMode = TRUE; 392 393 value = 0; 394 argStr = ParseNumber(argStr+5, &value); 395 if (!argStr) goto invalid_parameter; 396 Resolution.X = (SHORT)value; 397 } 398 else if (!kbdMode && _wcsnicmp(argStr, L"LINES=", 6) == 0) 399 { 400 dispMode = TRUE; 401 402 value = 0; 403 argStr = ParseNumber(argStr+6, &value); 404 if (!argStr) goto invalid_parameter; 405 Resolution.Y = (SHORT)value; 406 } 407 else if (!dispMode && _wcsnicmp(argStr, L"RATE=", 5) == 0) 408 { 409 kbdMode = TRUE; 410 411 argStr = ParseNumber(argStr+5, &dwKbdSpeed); 412 if (!argStr) goto invalid_parameter; 413 } 414 else if (!dispMode && _wcsnicmp(argStr, L"DELAY=", 6) == 0) 415 { 416 kbdMode = TRUE; 417 418 argStr = ParseNumber(argStr+6, &dwKbdDelay); 419 if (!argStr) goto invalid_parameter; 420 } 421 else 422 { 423 invalid_parameter: 424 ConResPrintf(StdErr, IDS_ERROR_INVALID_PARAMETER, ArgStr); 425 return 1; 426 } 427 } 428 429 if (dispMode) 430 { 431 ClearScreen(hConOut, &csbi); 432 if (!ResizeTextConsole(hConOut, &csbi, Resolution)) 433 ConResPuts(StdErr, IDS_ERROR_SCREEN_LINES_COL); 434 } 435 else if (kbdMode) 436 { 437 /* 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); 442 */ 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." 447 } 448 449 return 0; 450 } 451 452 int SetConsoleCPState(IN PCWSTR ArgStr) 453 { 454 PCWSTR argStr = ArgStr; 455 DWORD value = 0; 456 UINT uOldCodePage, uNewCodePage; 457 458 if ( (_wcsnicmp(argStr, L"SELECT=", 7) == 0 && (argStr += 7)) || 459 (_wcsnicmp(argStr, L"SEL=", 4) == 0 && (argStr += 4)) ) 460 { 461 argStr = ParseNumber(argStr, &value); 462 if (!argStr) goto invalid_parameter; 463 464 /* This should be the end of the string */ 465 while (*argStr == L' ') argStr++; 466 if (*argStr) goto invalid_parameter; 467 } 468 else 469 { 470 invalid_parameter: 471 ConResPrintf(StdErr, IDS_ERROR_INVALID_PARAMETER, ArgStr); 472 return 1; 473 } 474 475 uNewCodePage = value; 476 477 /** 478 ** IMPORTANT NOTE: This code must be kept synchronized with CHCP.COM 479 **/ 480 481 /* 482 * Save the original console code page to be restored 483 * in case SetConsoleCP() or SetConsoleOutputCP() fails. 484 */ 485 uOldCodePage = GetConsoleCP(); 486 487 /* 488 * Try changing the console input and output code pages. 489 * If it succeeds, refresh the local code page information. 490 */ 491 if (SetConsoleCP(uNewCodePage)) 492 { 493 if (SetConsoleOutputCP(uNewCodePage)) 494 { 495 /* Success, reset the current thread UI language 496 * and update the streams cached code page. */ 497 ConSetThreadUILanguage(0); 498 ConStdStreamsSetCacheCodePage(uNewCodePage, uNewCodePage); 499 500 /* Display the current console status */ 501 ShowConsoleStatus(); 502 return 0; 503 } 504 else 505 { 506 /* Failure, restore the original console code page */ 507 SetConsoleCP(uOldCodePage); 508 } 509 } 510 511 /* An error happened, display an error and bail out */ 512 ConResPuts(StdErr, IDS_ERROR_INVALID_CODEPAGE); 513 return 1; 514 } 515 516 517 /*****************************************************************************\ 518 ** S E R I A L P O R T H E L P E R S ** 519 \*****************************************************************************/ 520 521 static BOOL 522 SerialPortQuery(INT nPortNum, LPDCB pDCB, LPCOMMTIMEOUTS pCommTimeouts, BOOL bWrite) 523 { 524 BOOL Success; 525 HANDLE hPort; 526 WCHAR szPortName[MAX_PORTNAME_LEN]; 527 528 ASSERT(pDCB); 529 ASSERT(pCommTimeouts); 530 531 swprintf(szPortName, L"COM%d", nPortNum); 532 hPort = CreateFileW(szPortName, 533 bWrite ? GENERIC_WRITE : GENERIC_READ, 534 0, // exclusive 535 NULL, // sec attr 536 OPEN_EXISTING, 537 0, // no attributes 538 NULL); // no template 539 540 if (hPort == INVALID_HANDLE_VALUE) 541 { 542 DWORD dwLastError = GetLastError(); 543 if (dwLastError == ERROR_ACCESS_DENIED) 544 ConResPrintf(StdErr, IDS_ERROR_DEVICE_NOT_AVAILABLE, szPortName); 545 else 546 ConResPrintf(StdErr, IDS_ERROR_ILLEGAL_DEVICE_NAME, szPortName, dwLastError); 547 return FALSE; 548 } 549 550 Success = bWrite ? SetCommState(hPort, pDCB) 551 : GetCommState(hPort, pDCB); 552 if (!Success) 553 { 554 ConResPrintf(StdErr, 555 bWrite ? IDS_ERROR_STATUS_SET_DEVICE : IDS_ERROR_STATUS_GET_DEVICE, 556 szPortName); 557 goto Quit; 558 } 559 560 Success = bWrite ? SetCommTimeouts(hPort, pCommTimeouts) 561 : GetCommTimeouts(hPort, pCommTimeouts); 562 if (!Success) 563 { 564 ConResPrintf(StdErr, 565 bWrite ? IDS_ERROR_TIMEOUT_SET_DEVICE : IDS_ERROR_TIMEOUT_GET_DEVICE, 566 szPortName); 567 goto Quit; 568 } 569 570 Quit: 571 CloseHandle(hPort); 572 return Success; 573 } 574 575 int ShowSerialStatus(INT nPortNum) 576 { 577 static const LPCWSTR parity_strings[] = 578 { 579 L"None", // NOPARITY 580 L"Odd", // ODDPARITY 581 L"Even", // EVENPARITY 582 L"Mark", // MARKPARITY 583 L"Space" // SPACEPARITY 584 }; 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" }; 587 588 DCB dcb; 589 COMMTIMEOUTS CommTimeouts; 590 WCHAR szPortName[MAX_PORTNAME_LEN]; 591 592 if (!SerialPortQuery(nPortNum, &dcb, &CommTimeouts, FALSE)) 593 { 594 return 1; 595 } 596 if (dcb.Parity >= ARRAYSIZE(parity_strings)) 597 { 598 ConResPrintf(StdErr, IDS_ERROR_INVALID_PARITY_BITS, dcb.Parity); 599 dcb.Parity = 0; 600 } 601 if (dcb.StopBits >= ARRAYSIZE(stopbit_strings)) 602 { 603 ConResPrintf(StdErr, IDS_ERROR_INVALID_STOP_BITS, dcb.StopBits); 604 dcb.StopBits = 0; 605 } 606 607 swprintf(szPortName, L"COM%d", nPortNum); 608 609 ConPuts(StdOut, L"\n"); 610 UnderlinedResPrintf(StdOut, IDS_DEVICE_STATUS_HEADER, szPortName); 611 ConPuts(StdOut, L"\n"); 612 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"); 631 632 return 0; 633 } 634 635 636 /* 637 * Those procedures are inspired from Wine's dll/win32/kernel32/wine/comm.c 638 * Copyright 1996 Erik Bos and Marcus Meissner. 639 */ 640 641 static PCWSTR 642 ParseModes(PCWSTR argStr, PBYTE Mode) 643 { 644 if (_wcsnicmp(argStr, L"OFF", 3) == 0) 645 { 646 argStr += 3; 647 *Mode = 0; 648 } 649 else if (_wcsnicmp(argStr, L"ON", 2) == 0) 650 { 651 argStr += 2; 652 *Mode = 1; 653 } 654 else if (_wcsnicmp(argStr, L"HS", 2) == 0) 655 { 656 argStr += 2; 657 *Mode = 2; 658 } 659 else if (_wcsnicmp(argStr, L"TG", 2) == 0) 660 { 661 argStr += 2; 662 *Mode = 3; 663 } 664 665 return NULL; 666 } 667 668 static PCWSTR 669 ParseBaudRate(PCWSTR argStr, PDWORD BaudRate) 670 { 671 argStr = ParseNumber(argStr, BaudRate); 672 if (!argStr) return NULL; 673 674 /* 675 * Check for Baud Rate abbreviations. This means that using 676 * those values as real baud rates is impossible using MODE. 677 */ 678 switch (*BaudRate) 679 { 680 /* BaudRate = 110, 150, 300, 600 */ 681 case 11: case 15: case 30: case 60: 682 *BaudRate *= 10; 683 break; 684 685 /* BaudRate = 1200, 2400, 4800, 9600 */ 686 case 12: case 24: case 48: case 96: 687 *BaudRate *= 100; 688 break; 689 690 case 19: 691 *BaudRate = 19200; 692 break; 693 } 694 695 return argStr; 696 } 697 698 static PCWSTR 699 ParseParity(PCWSTR argStr, PBYTE Parity) 700 { 701 switch (towupper(*argStr++)) 702 { 703 case L'N': 704 *Parity = NOPARITY; 705 break; 706 707 case L'O': 708 *Parity = ODDPARITY; 709 break; 710 711 case L'E': 712 *Parity = EVENPARITY; 713 break; 714 715 case L'M': 716 *Parity = MARKPARITY; 717 break; 718 719 case L'S': 720 *Parity = SPACEPARITY; 721 break; 722 723 default: 724 return NULL; 725 } 726 727 return argStr; 728 } 729 730 static PCWSTR 731 ParseByteSize(PCWSTR argStr, PBYTE ByteSize) 732 { 733 DWORD value = 0; 734 735 argStr = ParseNumber(argStr, &value); 736 if (!argStr) return NULL; 737 738 *ByteSize = (BYTE)value; 739 if (*ByteSize < 5 || *ByteSize > 8) 740 return NULL; 741 742 return argStr; 743 } 744 745 static PCWSTR 746 ParseStopBits(PCWSTR argStr, PBYTE StopBits) 747 { 748 if (_wcsnicmp(argStr, L"1.5", 3) == 0) 749 { 750 argStr += 3; 751 *StopBits = ONE5STOPBITS; 752 } 753 else 754 { 755 if (*argStr == L'1') 756 *StopBits = ONESTOPBIT; 757 else if (*argStr == L'2') 758 *StopBits = TWOSTOPBITS; 759 else 760 return NULL; 761 762 argStr++; 763 } 764 765 return argStr; 766 } 767 768 /* 769 * Build a DCB using the old style settings string eg: "96,n,8,1" 770 * 771 * See dll/win32/kernel32/wine/comm.c!COMM_BuildOldCommDCB() 772 * for more information. 773 */ 774 static BOOL 775 BuildOldCommDCB( 776 OUT LPDCB pDCB, 777 IN PCWSTR ArgStr) 778 { 779 PCWSTR argStr = ArgStr; 780 BOOL stop = FALSE; 781 782 /* 783 * Parse the baud rate (only MANDATORY argument) 784 */ 785 argStr = ParseBaudRate(argStr, &pDCB->BaudRate); 786 if (!argStr) return FALSE; 787 788 789 /* 790 * Now parse the rest (OPTIONAL arguments) 791 */ 792 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; 798 799 /* Parse the parity */ 800 // Default: EVENPARITY 801 pDCB->Parity = EVENPARITY; 802 if (*argStr != L',') 803 { 804 argStr = ParseParity(argStr, &pDCB->Parity); 805 if (!argStr) return FALSE; 806 } 807 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; 813 814 /* Parse the data bits */ 815 // Default: 7 816 pDCB->ByteSize = 7; 817 if (*argStr != L',') 818 { 819 argStr = ParseByteSize(argStr, &pDCB->ByteSize); 820 if (!argStr) return FALSE; 821 } 822 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; 828 829 /* Parse the stop bits */ 830 // Default: 1, or 2 for BAUD=110 831 // pDCB->StopBits = ONESTOPBIT; 832 if (*argStr != L',') 833 { 834 stop = TRUE; 835 argStr = ParseStopBits(argStr, &pDCB->StopBits); 836 if (!argStr) return FALSE; 837 } 838 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; 845 846 Quit: 847 switch (towupper(*argStr)) 848 { 849 case L'\0': 850 pDCB->fInX = FALSE; 851 pDCB->fOutX = FALSE; 852 pDCB->fOutxCtsFlow = FALSE; 853 pDCB->fOutxDsrFlow = FALSE; 854 pDCB->fDtrControl = DTR_CONTROL_ENABLE; 855 pDCB->fRtsControl = RTS_CONTROL_ENABLE; 856 break; 857 858 case L'X': 859 pDCB->fInX = TRUE; 860 pDCB->fOutX = TRUE; 861 pDCB->fOutxCtsFlow = FALSE; 862 pDCB->fOutxDsrFlow = FALSE; 863 pDCB->fDtrControl = DTR_CONTROL_ENABLE; 864 pDCB->fRtsControl = RTS_CONTROL_ENABLE; 865 break; 866 867 case L'P': 868 pDCB->fInX = FALSE; 869 pDCB->fOutX = FALSE; 870 pDCB->fOutxCtsFlow = TRUE; 871 pDCB->fOutxDsrFlow = TRUE; 872 pDCB->fDtrControl = DTR_CONTROL_HANDSHAKE; 873 pDCB->fRtsControl = RTS_CONTROL_HANDSHAKE; 874 break; 875 876 default: 877 /* Unsupported */ 878 return FALSE; 879 } 880 if (*argStr) argStr++; 881 882 /* This should be the end of the string */ 883 while (*argStr == L' ') argStr++; 884 if (*argStr) return FALSE; 885 886 /* If stop bits were not specified, a default is always supplied */ 887 if (!stop) 888 { 889 if (pDCB->BaudRate == 110) 890 pDCB->StopBits = TWOSTOPBITS; 891 else 892 pDCB->StopBits = ONESTOPBIT; 893 } 894 return TRUE; 895 } 896 897 /* 898 * Build a DCB using the new style settings string. 899 * eg: "baud=9600 parity=n data=8 stop=1 xon=on to=on" 900 * 901 * See dll/win32/kernel32/wine/comm.c!COMM_BuildNewCommDCB() 902 * for more information. 903 */ 904 static BOOL 905 BuildNewCommDCB( 906 OUT LPDCB pDCB, 907 OUT LPCOMMTIMEOUTS pCommTimeouts, 908 IN PCWSTR ArgStr) 909 { 910 PCWSTR argStr = ArgStr; 911 BOOL baud = FALSE, stop = FALSE; 912 BYTE value; 913 914 while (argStr && *argStr) 915 { 916 while (*argStr == L' ') argStr++; 917 if (!*argStr) break; 918 919 if (_wcsnicmp(argStr, L"BAUD=", 5) == 0) 920 { 921 baud = TRUE; 922 argStr = ParseBaudRate(argStr+5, &pDCB->BaudRate); 923 if (!argStr) return FALSE; 924 } 925 else if (_wcsnicmp(argStr, L"PARITY=", 7) == 0) 926 { 927 // Default: EVENPARITY 928 argStr = ParseParity(argStr+7, &pDCB->Parity); 929 if (!argStr) return FALSE; 930 } 931 else if (_wcsnicmp(argStr, L"DATA=", 5) == 0) 932 { 933 // Default: 7 934 argStr = ParseByteSize(argStr+5, &pDCB->ByteSize); 935 if (!argStr) return FALSE; 936 } 937 else if (_wcsnicmp(argStr, L"STOP=", 5) == 0) 938 { 939 // Default: 1, or 2 for BAUD=110 940 stop = TRUE; 941 argStr = ParseStopBits(argStr+5, &pDCB->StopBits); 942 if (!argStr) return FALSE; 943 } 944 else if (_wcsnicmp(argStr, L"TO=", 3) == 0) // TO=ON|OFF 945 { 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 950 { 951 pCommTimeouts->ReadTotalTimeoutConstant = 0; 952 pCommTimeouts->WriteTotalTimeoutConstant = 0; 953 } 954 else if (value == 1) // ON 955 { 956 pCommTimeouts->ReadTotalTimeoutConstant = 60000; 957 pCommTimeouts->WriteTotalTimeoutConstant = 60000; 958 } 959 else 960 { 961 return FALSE; 962 } 963 } 964 else if (_wcsnicmp(argStr, L"XON=", 4) == 0) // XON=ON|OFF 965 { 966 argStr = ParseModes(argStr+4, &value); 967 if (!argStr) return FALSE; 968 if ((value == 0) || (value == 1)) 969 { 970 pDCB->fOutX = value; 971 pDCB->fInX = value; 972 } 973 else 974 { 975 return FALSE; 976 } 977 } 978 else if (_wcsnicmp(argStr, L"ODSR=", 5) == 0) // ODSR=ON|OFF 979 { 980 value = 0; 981 argStr = ParseModes(argStr+5, &value); 982 if (!argStr) return FALSE; 983 if ((value == 0) || (value == 1)) 984 pDCB->fOutxDsrFlow = value; 985 else 986 return FALSE; 987 } 988 else if (_wcsnicmp(argStr, L"OCTS=", 5) == 0) // OCTS=ON|OFF 989 { 990 value = 0; 991 argStr = ParseModes(argStr+5, &value); 992 if (!argStr) return FALSE; 993 if ((value == 0) || (value == 1)) 994 pDCB->fOutxCtsFlow = value; 995 else 996 return FALSE; 997 } 998 else if (_wcsnicmp(argStr, L"DTR=", 4) == 0) // DTR=ON|OFF|HS 999 { 1000 value = 0; 1001 argStr = ParseModes(argStr+4, &value); 1002 if (!argStr) return FALSE; 1003 if ((value == 0) || (value == 1) || (value == 2)) 1004 pDCB->fDtrControl = value; 1005 else 1006 return FALSE; 1007 } 1008 else if (_wcsnicmp(argStr, L"RTS=", 4) == 0) // RTS=ON|OFF|HS|TG 1009 { 1010 value = 0; 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; 1015 else 1016 return FALSE; 1017 } 1018 else if (_wcsnicmp(argStr, L"IDSR=", 5) == 0) // IDSR=ON|OFF 1019 { 1020 value = 0; 1021 argStr = ParseModes(argStr+5, &value); 1022 if (!argStr) return FALSE; 1023 if ((value == 0) || (value == 1)) 1024 pDCB->fDsrSensitivity = value; 1025 else 1026 return FALSE; 1027 } 1028 else 1029 { 1030 return FALSE; 1031 } 1032 } 1033 1034 /* If stop bits were not specified, a default is always supplied */ 1035 if (!stop) 1036 { 1037 if (baud && pDCB->BaudRate == 110) 1038 pDCB->StopBits = TWOSTOPBITS; 1039 else 1040 pDCB->StopBits = ONESTOPBIT; 1041 } 1042 return TRUE; 1043 } 1044 1045 int SetSerialState(INT nPortNum, IN PCWSTR ArgStr) 1046 { 1047 BOOL Success; 1048 DCB dcb; 1049 COMMTIMEOUTS CommTimeouts; 1050 1051 if (!SerialPortQuery(nPortNum, &dcb, &CommTimeouts, FALSE)) 1052 { 1053 // TODO: Error message? 1054 return 0; 1055 } 1056 1057 /* 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. 1060 */ 1061 if (wcschr(ArgStr, L',')) 1062 Success = BuildOldCommDCB(&dcb, ArgStr); 1063 else 1064 Success = BuildNewCommDCB(&dcb, &CommTimeouts, ArgStr); 1065 1066 if (!Success) 1067 { 1068 ConResPrintf(StdErr, IDS_ERROR_INVALID_PARAMETER, ArgStr); 1069 return 1; 1070 } 1071 1072 SerialPortQuery(nPortNum, &dcb, &CommTimeouts, TRUE); 1073 ShowSerialStatus(nPortNum); 1074 1075 return 0; 1076 } 1077 1078 1079 /*****************************************************************************\ 1080 ** E N T R Y P O I N T ** 1081 \*****************************************************************************/ 1082 1083 static PCWSTR 1084 FindPortNum(PCWSTR argStr, PINT PortNum) 1085 { 1086 PWSTR endptr = NULL; 1087 1088 *PortNum = wcstol(argStr, &endptr, 10); 1089 if (endptr == argStr) 1090 { 1091 *PortNum = -1; 1092 return NULL; 1093 } 1094 1095 return endptr; 1096 } 1097 1098 int EnumerateDevices(VOID) 1099 { 1100 PWSTR Buffer, ptr; 1101 PCWSTR argStr; 1102 DWORD dwLen = MAX_PATH; 1103 INT nPortNum; 1104 1105 /* Pre-allocate a buffer for QueryDosDeviceW() */ 1106 Buffer = HeapAlloc(GetProcessHeap(), 0, dwLen * sizeof(WCHAR)); 1107 if (Buffer == NULL) 1108 { 1109 /* We failed, bail out */ 1110 ConPuts(StdErr, L"ERROR: Not enough memory\n"); 1111 return 0; 1112 } 1113 1114 for (;;) 1115 { 1116 *Buffer = UNICODE_NULL; 1117 if (QueryDosDeviceW(NULL, Buffer, dwLen)) 1118 break; 1119 1120 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 1121 { 1122 /* We failed, bail out */ 1123 ConPrintf(StdErr, L"ERROR: QueryDosDeviceW(...) failed: 0x%lx\n", GetLastError()); 1124 HeapFree(GetProcessHeap(), 0, Buffer); 1125 return 0; 1126 } 1127 1128 /* The buffer was too small, try to re-allocate it */ 1129 dwLen *= 2; 1130 ptr = HeapReAlloc(GetProcessHeap(), 0, Buffer, dwLen * sizeof(WCHAR)); 1131 if (ptr == NULL) 1132 { 1133 /* We failed, bail out */ 1134 ConPuts(StdErr, L"ERROR: Not enough memory\n"); 1135 HeapFree(GetProcessHeap(), 0, Buffer); 1136 return 0; 1137 } 1138 Buffer = ptr; 1139 } 1140 1141 for (ptr = Buffer; *ptr != UNICODE_NULL; ptr += wcslen(ptr) + 1) 1142 { 1143 if (_wcsnicmp(ptr, L"COM", 3) == 0) 1144 { 1145 argStr = FindPortNum(ptr+3, &nPortNum); 1146 if (!argStr || *argStr || nPortNum == -1) 1147 continue; 1148 1149 // ConResPrintf(StdOut, IDS_QUERY_SERIAL_FOUND, ptr); 1150 ShowSerialStatus(nPortNum); 1151 } 1152 else if (_wcsicmp(ptr, L"PRN") == 0) 1153 { 1154 ConResPrintf(StdOut, IDS_QUERY_PRINTER_FOUND, ptr); 1155 } 1156 else if (_wcsnicmp(ptr, L"LPT", 3) == 0) 1157 { 1158 argStr = FindPortNum(ptr+3, &nPortNum); 1159 if (!argStr || *argStr || nPortNum == -1) 1160 continue; 1161 1162 // ConResPrintf(StdOut, IDS_QUERY_PARALLEL_FOUND, ptr); 1163 ShowParallelStatus(nPortNum); 1164 } 1165 else if (_wcsicmp(ptr, L"AUX") == 0 || _wcsicmp(ptr, L"NUL") == 0) 1166 { 1167 ConResPrintf(StdOut, IDS_QUERY_DOSDEV_FOUND, ptr); 1168 } 1169 else 1170 { 1171 // ConResPrintf(StdOut, IDS_QUERY_MISC_FOUND, ptr); 1172 } 1173 } 1174 1175 ShowConsoleStatus(); 1176 1177 /* Free the buffer and return success */ 1178 HeapFree(GetProcessHeap(), 0, Buffer); 1179 return 1; 1180 } 1181 1182 int wmain(int argc, WCHAR* argv[]) 1183 { 1184 int ret = 0; 1185 int arg; 1186 SIZE_T ArgStrSize; 1187 PCWSTR ArgStr, argStr; 1188 1189 INT nPortNum; 1190 1191 /* Initialize the Console Standard Streams */ 1192 ConInitStdStreams(); 1193 1194 /* 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. 1199 * 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" 1204 * 1205 * Note also that the "/STATUS" switch is actually really "/STA". 1206 * 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. 1211 */ 1212 ArgStrSize = 0; 1213 1214 /* Compute the space needed for the new string, and allocate it */ 1215 for (arg = 1; arg < argc; arg++) 1216 { 1217 ArgStrSize += wcslen(argv[arg]) + 1; // 1 for space 1218 } 1219 ArgStr = HeapAlloc(GetProcessHeap(), 0, (ArgStrSize + 1) * sizeof(WCHAR)); 1220 if (ArgStr == NULL) 1221 { 1222 ConPuts(StdErr, L"ERROR: Not enough memory\n"); 1223 return 1; 1224 } 1225 1226 /* Copy the contents and NULL-terminate the string */ 1227 argStr = ArgStr; 1228 for (arg = 1; arg < argc; arg++) 1229 { 1230 wcscpy((PWSTR)argStr, argv[arg]); 1231 argStr += wcslen(argv[arg]); 1232 *(PWSTR)argStr++ = L' '; 1233 } 1234 *(PWSTR)argStr = L'\0'; 1235 1236 /* Parse the command line */ 1237 argStr = ArgStr; 1238 1239 while (*argStr == L' ') argStr++; 1240 if (!*argStr) goto show_status; 1241 1242 if (wcsstr(argStr, L"/?") || wcsstr(argStr, L"-?")) 1243 { 1244 ConResPuts(StdOut, IDS_USAGE); 1245 goto Quit; 1246 } 1247 else if (_wcsnicmp(argStr, L"/STA", 4) == 0) 1248 { 1249 /* Skip this parameter */ 1250 while (*argStr != L' ') argStr++; 1251 /* Skip any delimiter */ 1252 while (*argStr == L' ') argStr++; 1253 1254 /* The presence of any other parameter is invalid */ 1255 if (*argStr) 1256 goto invalid_parameter; 1257 1258 goto show_status; 1259 } 1260 else if (_wcsnicmp(argStr, L"LPT", 3) == 0) 1261 { 1262 argStr = FindPortNum(argStr+3, &nPortNum); 1263 if (!argStr || nPortNum == -1) 1264 goto invalid_parameter; 1265 1266 if (*argStr == L':') argStr++; 1267 while (*argStr == L' ') argStr++; 1268 1269 if (!*argStr || _wcsnicmp(argStr, L"/STA", 4) == 0) 1270 ret = ShowParallelStatus(nPortNum); 1271 else 1272 ConPuts(StdErr, L"ERROR: LPT port redirection is not implemented!\n"); 1273 // TODO: Implement setting LPT port redirection using SetParallelState(). 1274 goto Quit; 1275 } 1276 else if (_wcsnicmp(argStr, L"COM", 3) == 0) 1277 { 1278 argStr = FindPortNum(argStr+3, &nPortNum); 1279 if (!argStr || nPortNum == -1) 1280 goto invalid_parameter; 1281 1282 if (*argStr == L':') argStr++; 1283 while (*argStr == L' ') argStr++; 1284 1285 if (!*argStr || _wcsnicmp(argStr, L"/STA", 4) == 0) 1286 ret = ShowSerialStatus(nPortNum); 1287 else 1288 ret = SetSerialState(nPortNum, argStr); 1289 goto Quit; 1290 } 1291 else if (_wcsnicmp(argStr, L"CON", 3) == 0) 1292 { 1293 argStr += 3; 1294 1295 if (*argStr == L':') argStr++; 1296 while (*argStr == L' ') argStr++; 1297 1298 if (!*argStr || _wcsnicmp(argStr, L"/STA", 4) == 0) 1299 { 1300 ret = ShowConsoleStatus(); 1301 } 1302 else if ( (_wcsnicmp(argStr, L"CP", 2) == 0 && (argStr += 2)) || 1303 (_wcsnicmp(argStr, L"CODEPAGE", 8) == 0 && (argStr += 8)) ) 1304 { 1305 while (*argStr == L' ') argStr++; 1306 1307 if (!*argStr || _wcsnicmp(argStr, L"/STA", 4) == 0) 1308 ret = ShowConsoleCPStatus(); 1309 else 1310 ret = SetConsoleCPState(argStr); 1311 } 1312 else 1313 { 1314 ret = SetConsoleState(argStr); 1315 } 1316 goto Quit; 1317 } 1318 // else if (wcschr(argStr, L',')) 1319 else 1320 { 1321 /* Old syntax: MODE [COLS],[LINES] */ 1322 ret = SetConsoleStateOld(argStr); 1323 goto Quit; 1324 } 1325 1326 show_status: 1327 EnumerateDevices(); 1328 goto Quit; 1329 1330 invalid_parameter: 1331 ConResPrintf(StdErr, IDS_ERROR_INVALID_PARAMETER, ArgStr); 1332 goto Quit; 1333 1334 Quit: 1335 /* Free the string and quit */ 1336 HeapFree(GetProcessHeap(), 0, (PWSTR)ArgStr); 1337 return ret; 1338 } 1339