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