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