2 * CTTY.C - ctty (Change TTY) command.
4 * This command redirects the first three standard handles
5 * stdin, stdout, stderr to another terminal.
10 * 14 Aug 1998 (John P Price)
11 * - Created dummy command.
14 * + Added to change the first three handles to the given device name
15 * + Supports only redirection of stdin and stdout, e.g.:
16 * C:\> CTTY COM1 >file
18 * C:\> echo Hallo | CTTY COM1 | echo du
19 * The CTTY command effects the commands on the _next_ line.
21 * 20 Oct 2016 (Hermes Belusca-Maito)
27 #if defined(INCLUDE_CMD_CTTY) && defined(FEATURE_REDIRECTION)
30 CheckTerminalDeviceType(IN LPCTSTR pszName
)
32 /* Console reserved "file" names */
33 static const LPCWSTR DosLPTDevice
= L
"LPT";
34 static const LPCWSTR DosCOMDevice
= L
"COM";
35 static const LPCWSTR DosPRNDevice
= L
"PRN";
36 static const LPCWSTR DosAUXDevice
= L
"AUX";
37 static const LPCWSTR DosCONDevice
= L
"CON";
38 static const LPCWSTR DosNULDevice
= L
"NUL";
42 WORD DeviceType
= 0; // 0: Unknown; 1: CON; 2: COM etc...
44 #ifndef _UNICODE // UNICODE means that TCHAR == WCHAR == UTF-16
45 /* Convert from the current process/thread's codepage to UTF-16 */
46 DWORD len
= strlen(pszName
) + 1;
47 WCHAR
*buffer
= cmd_alloc(len
* sizeof(WCHAR
));
50 // SetLastError(ERROR_NOT_ENOUGH_MEMORY);
53 len
= (DWORD
)MultiByteToWideChar(CP_THREAD_ACP
, // CP_ACP, CP_OEMCP
54 0, pszName
, (INT
)len
, buffer
, (INT
)len
);
61 * Check whether we deal with a DOS device, and if so,
62 * strip the path till the file name.
63 * Therefore, things like \\.\CON or C:\some_path\COM1
64 * are transformed into CON or COM1, for example.
66 DeviceNameInfo
= RtlIsDosDeviceName_U(DeviceName
);
67 if (DeviceNameInfo
!= 0)
69 DeviceName
= (LPCWSTR
)((ULONG_PTR
)DeviceName
+ ((DeviceNameInfo
>> 16) & 0xFFFF));
71 if (_wcsnicmp(DeviceName
, DosCONDevice
, 3) == 0)
76 if ( _wcsnicmp(DeviceName
, DosLPTDevice
, 3) == 0 ||
77 _wcsnicmp(DeviceName
, DosCOMDevice
, 3) == 0 ||
78 _wcsnicmp(DeviceName
, DosPRNDevice
, 3) == 0 ||
79 _wcsnicmp(DeviceName
, DosAUXDevice
, 3) == 0 ||
80 _wcsnicmp(DeviceName
, DosNULDevice
, 3) == 0 )
84 // else DeviceType = 0;
95 * See also redir.c!PerformRedirection().
97 * The CTTY command allows only the usage of CON, COM, AUX, LPT, PRN and NUL
98 * DOS devices as valid terminal devices. Everything else is forbidden.
100 * CTTY does not set ERRORLEVEL on error.
102 INT
cmd_ctty(LPTSTR param
)
104 static SECURITY_ATTRIBUTES SecAttr
= { sizeof(SECURITY_ATTRIBUTES
), NULL
, TRUE
};
108 HANDLE hDevice
, hStdHandles
[3]; // hStdIn, hStdOut, hStdErr;
110 /* The user asked for help */
111 if (_tcsncmp(param
, _T("/?"), 2) == 0)
113 ConOutResPaging(TRUE
, STRING_CTTY_HELP
);
119 error_req_param_missing();
123 /* Check whether this is a valid terminal device name */
124 DeviceType
= CheckTerminalDeviceType(param
);
128 * Special case for CON device.
130 * We do not open CON with GENERIC_READ or GENERIC_WRITE as is,
131 * but instead we separately open CONIN$ and CONOUT$ with both
132 * GENERIC_READ | GENERIC_WRITE access.
133 * We do so because otherwise, opening in particular CON with GENERIC_WRITE
134 * only would open CONOUT$ with an handle not passing the IsConsoleHandle()
135 * test, meaning that we could not use the full console functionalities.
141 * If we previously failed in opening handles to the console,
142 * this means the existing console is almost destroyed.
143 * Close the existing console and allocate and open a new one.
152 /* Attempt to retrieve a handle for standard input */
153 hStdHandles
[0] = CreateFile(_T("CONIN$"),
154 GENERIC_READ
| GENERIC_WRITE
,
155 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
160 if (hStdHandles
[0] == INVALID_HANDLE_VALUE
)
163 // error_no_rw_device(param);
168 goto RetryOpenConsole
;
171 /* Attempt to retrieve a handle for standard output.
172 * Note that GENERIC_READ is needed for IsConsoleHandle() to succeed afterwards. */
173 hStdHandles
[1] = CreateFile(_T("CONOUT$"),
174 GENERIC_READ
| GENERIC_WRITE
,
175 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
180 if (hStdHandles
[1] == INVALID_HANDLE_VALUE
)
183 // error_no_rw_device(param);
185 CloseHandle(hStdHandles
[0]);
190 goto RetryOpenConsole
;
193 /* Duplicate a handle for standard error */
194 Success
= DuplicateHandle(GetCurrentProcess(),
200 DUPLICATE_SAME_ACCESS
/* 0 */);
204 // error_no_rw_device(param);
205 CloseHandle(hStdHandles
[1]);
206 CloseHandle(hStdHandles
[0]);
210 else if (DeviceType
== 2)
213 * COM and the other devices can only be opened once.
214 * Since we need different handles, we need to duplicate them.
217 /* Attempt to retrieve a handle to the device for read/write access */
218 hDevice
= CreateFile(param
,
219 GENERIC_READ
| GENERIC_WRITE
,
220 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
223 0, // FILE_FLAG_OVERLAPPED, // 0,
225 if (hDevice
== INVALID_HANDLE_VALUE
)
228 // error_no_rw_device(param);
232 /* Duplicate a handle for standard input */
233 Success
= DuplicateHandle(GetCurrentProcess(),
243 // error_no_rw_device(param);
244 CloseHandle(hDevice
);
248 /* Duplicate a handle for standard output */
249 Success
= DuplicateHandle(GetCurrentProcess(),
259 // error_no_rw_device(param);
260 CloseHandle(hStdHandles
[0]);
261 CloseHandle(hDevice
);
265 /* Duplicate a handle for standard error */
266 Success
= DuplicateHandle(GetCurrentProcess(),
276 // error_no_rw_device(param);
277 CloseHandle(hStdHandles
[1]);
278 CloseHandle(hStdHandles
[0]);
279 CloseHandle(hDevice
);
283 /* Now get rid of the main device handle */
284 CloseHandle(hDevice
);
289 ConOutPrintf(L
"Invalid device '%s'\n", param
);
294 /* Now change the file descriptors:
298 if CTTY is called within a pipe or its I/O is redirected,
299 oldinfd or oldoutfd is not equal to -1. In such case the
300 old*fd is modified in order to effect the file descriptor
301 after the redirections are restored. Otherwise a pipe or
302 redirection would left CTTY in a half-made status.
305 failed
= dup2(f
, 2); /* no redirection support */
311 if((oldoutfd
= dup(f
)) == -1)
315 error_ctty_dup(param
);
320 /* Now set the standard handles */
322 hDevice
= GetHandle(0);
323 if (hDevice
!= INVALID_HANDLE_VALUE
)
324 CloseHandle(hDevice
);
325 SetHandle(0, hStdHandles
[0]);
327 hDevice
= GetHandle(1);
328 if (hDevice
!= INVALID_HANDLE_VALUE
)
329 CloseHandle(hDevice
);
330 SetHandle(1, hStdHandles
[1]);
332 hDevice
= GetHandle(2);
333 if (hDevice
!= INVALID_HANDLE_VALUE
)
334 CloseHandle(hDevice
);
335 SetHandle(2, hStdHandles
[2]);
340 #endif /* INCLUDE_CMD_CTTY && FEATURE_REDIRECTION */