diff options
-rw-r--r-- | include/mingw.h | 3 | ||||
-rw-r--r-- | libbb/Config.in | 9 | ||||
-rw-r--r-- | libbb/termios.c | 218 | ||||
-rw-r--r-- | libbb/winansi.c | 5 | ||||
-rwxr-xr-x | scripts/trylink | 1 |
5 files changed, 233 insertions, 3 deletions
diff --git a/include/mingw.h b/include/mingw.h index b39d94a33..500308947 100644 --- a/include/mingw.h +++ b/include/mingw.h | |||
@@ -191,6 +191,9 @@ int mingw_getpagesize(void); | |||
191 | #define getpagesize mingw_getpagesize | 191 | #define getpagesize mingw_getpagesize |
192 | #endif | 192 | #endif |
193 | 193 | ||
194 | int mingw_isatty(int fd); | ||
195 | #define isatty(fd) mingw_isatty(fd) | ||
196 | |||
194 | /* Use mingw_lstat() instead of lstat()/stat() and | 197 | /* Use mingw_lstat() instead of lstat()/stat() and |
195 | * mingw_fstat() instead of fstat() on Windows. | 198 | * mingw_fstat() instead of fstat() on Windows. |
196 | */ | 199 | */ |
diff --git a/libbb/Config.in b/libbb/Config.in index 112a3d658..a75fb58c0 100644 --- a/libbb/Config.in +++ b/libbb/Config.in | |||
@@ -26,6 +26,15 @@ config MD5_SIZE_VS_SPEED | |||
26 | 2 3.0 5088 | 26 | 2 3.0 5088 |
27 | 3 (smallest) 5.1 4912 | 27 | 3 (smallest) 5.1 4912 |
28 | 28 | ||
29 | config FEATURE_CYGWIN_TTY | ||
30 | bool "Support Cygwin tty" | ||
31 | default n | ||
32 | depends on MINGW32 | ||
33 | help | ||
34 | Try to sneak in Cygwin shared structure to determine if | ||
35 | a file handle is Cygwin tty, useful if you want to use | ||
36 | a Cygwin-based terminal emulator. | ||
37 | |||
29 | config FEATURE_EDITING | 38 | config FEATURE_EDITING |
30 | bool "Command line editing" | 39 | bool "Command line editing" |
31 | default n | 40 | default n |
diff --git a/libbb/termios.c b/libbb/termios.c index f47593a49..5695e9ba2 100644 --- a/libbb/termios.c +++ b/libbb/termios.c | |||
@@ -4,8 +4,212 @@ | |||
4 | #include "strbuf.h" | 4 | #include "strbuf.h" |
5 | #include "cygwin_termios.h" | 5 | #include "cygwin_termios.h" |
6 | 6 | ||
7 | #if ENABLE_FEATURE_CYGWIN_TTY | ||
8 | #include <ntdef.h> | ||
9 | |||
10 | /* NtCreateDirectoryObject */ | ||
11 | #define DIRECTORY_QUERY 1 | ||
12 | #define DIRECTORY_TRAVERSE 2 | ||
13 | #define DIRECTORY_CREATE_OBJECT 4 | ||
14 | #define DIRECTORY_CREATE_SUBDIRECTORY 8 | ||
15 | |||
16 | #define CYG_SHARED_DIR_ACCESS (DIRECTORY_QUERY \ | ||
17 | | DIRECTORY_TRAVERSE \ | ||
18 | | DIRECTORY_CREATE_SUBDIRECTORY \ | ||
19 | | DIRECTORY_CREATE_OBJECT \ | ||
20 | | READ_CONTROL) | ||
21 | |||
22 | typedef enum _OBJECT_INFORMATION_CLASS { | ||
23 | ObjectNameInformation = 1, | ||
24 | } OBJECT_INFORMATION_CLASS; | ||
25 | |||
26 | typedef struct _OBJECT_NAME_INFORMATION { | ||
27 | UNICODE_STRING Name; | ||
28 | } OBJECT_NAME_INFORMATION; | ||
29 | |||
30 | VOID NTAPI RtlInitUnicodeString (PUNICODE_STRING, PCWSTR); | ||
31 | NTSTATUS NTAPI NtOpenSection (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); | ||
32 | NTSTATUS NTAPI NtCreateDirectoryObject (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); | ||
33 | NTSTATUS NTAPI NtQueryObject (HANDLE, OBJECT_INFORMATION_CLASS, VOID *, ULONG, ULONG *); | ||
34 | |||
35 | /* Cygwin's shared_info */ | ||
36 | |||
37 | struct winsize { | ||
38 | unsigned short ws_row, ws_col; | ||
39 | unsigned short ws_xpixel, ws_ypixel; | ||
40 | }; | ||
41 | |||
42 | struct tty_min { | ||
43 | pid_t sid; /* Session ID of tty */ | ||
44 | struct status_flags { | ||
45 | unsigned initialized : 1; /* Set if tty is initialized */ | ||
46 | unsigned rstcons : 1; /* Set if console needs to be set to "non-cooked" */ | ||
47 | } status; | ||
48 | |||
49 | pid_t pgid; | ||
50 | int output_stopped; | ||
51 | int ntty; | ||
52 | DWORD last_ctrl_c; /* tick count of last ctrl-c */ | ||
53 | HWND hwnd; /* Console window handle tty belongs to */ | ||
54 | |||
55 | struct termios ti; | ||
56 | struct winsize winsize; | ||
57 | |||
58 | /* ioctl requests buffer */ | ||
59 | int cmd; | ||
60 | union { | ||
61 | struct termios termios; | ||
62 | struct winsize winsize; | ||
63 | int value; | ||
64 | pid_t pid; | ||
65 | } arg; | ||
66 | /* XXX_retval variables holds master's completion codes. Error are stored as | ||
67 | * -ERRNO | ||
68 | */ | ||
69 | int ioctl_retval; | ||
70 | int write_error; | ||
71 | }; | ||
72 | |||
73 | struct tty { | ||
74 | struct tty_min tty_min; | ||
75 | pid_t master_pid; /* PID of tty master process */ | ||
76 | |||
77 | HANDLE from_master, to_master; | ||
78 | |||
79 | int read_retval; | ||
80 | int was_opened; /* True if opened at least once. */ | ||
81 | }; | ||
82 | |||
83 | #define NTTYS 128 | ||
84 | struct tty_list { | ||
85 | struct tty ttys[NTTYS]; | ||
86 | }; | ||
87 | |||
88 | #define CYGWIN_PATH_MAX 4096 | ||
89 | struct shared_info { | ||
90 | DWORD version; | ||
91 | DWORD cb; | ||
92 | unsigned heap_chunk; | ||
93 | int heap_slop_inited; | ||
94 | unsigned heap_slop; | ||
95 | DWORD sys_mount_table_counter; | ||
96 | struct tty_list tty; | ||
97 | LONG last_used_bindresvport; | ||
98 | WCHAR installation_root[CYGWIN_PATH_MAX]; | ||
99 | DWORD obcaseinsensitive; | ||
100 | /* mtinfo mt; */ | ||
101 | }; | ||
102 | |||
103 | /* Some magic to recognize shared_info */ | ||
104 | #define CYGWIN_VERSION_SHARED_DATA 5 | ||
105 | #define CYGWIN_VERSION_DLL_IDENTIFIER L"cygwin1" | ||
106 | #define CYGWIN_VERSION_API_MAJOR 0 | ||
107 | #define CYGWIN_VERSION_API_MINOR 210 | ||
108 | #define SHARED_INFO_CB 39328 | ||
109 | #define CURR_SHARED_MAGIC 0x22f9ff0bU | ||
110 | |||
111 | #define CYGWIN_VERSION_MAGIC(a, b) ((unsigned) ((((unsigned short) a) << 16) | (unsigned short) b)) | ||
112 | #define SHARED_VERSION (unsigned)(CYGWIN_VERSION_API_MAJOR << 8 | CYGWIN_VERSION_API_MINOR) | ||
113 | #define SHARED_VERSION_MAGIC CYGWIN_VERSION_MAGIC (CURR_SHARED_MAGIC, SHARED_VERSION) | ||
114 | |||
115 | static struct shared_info *get_shared_info() | ||
116 | { | ||
117 | WCHAR buf[32]; | ||
118 | HANDLE sh; | ||
119 | WCHAR base[MAX_PATH]; | ||
120 | UNICODE_STRING uname; | ||
121 | HANDLE dir; | ||
122 | NTSTATUS status; | ||
123 | OBJECT_ATTRIBUTES attr; | ||
124 | static struct shared_info *si = NULL; | ||
125 | |||
126 | if (si) | ||
127 | return si; | ||
128 | |||
129 | swprintf(base, L"\\BaseNamedObjects\\%sS%d", | ||
130 | CYGWIN_VERSION_DLL_IDENTIFIER, | ||
131 | CYGWIN_VERSION_SHARED_DATA); | ||
132 | RtlInitUnicodeString (&uname, base); | ||
133 | InitializeObjectAttributes (&attr, &uname, OBJ_INHERIT | OBJ_OPENIF, NULL, NULL); | ||
134 | status = NtCreateDirectoryObject (&dir, CYG_SHARED_DIR_ACCESS, &attr); | ||
135 | |||
136 | swprintf(buf, L"%s.%d", L"shared", CYGWIN_VERSION_SHARED_DATA); | ||
137 | RtlInitUnicodeString(&uname, buf); | ||
138 | InitializeObjectAttributes (&attr, &uname, OBJ_CASE_INSENSITIVE, dir, NULL); | ||
139 | status = NtOpenSection (&sh, FILE_MAP_READ | FILE_MAP_WRITE, &attr); | ||
140 | si = MapViewOfFileEx(sh, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0, NULL); | ||
141 | //if (si->version == SHARED_VERSION_MAGIC && si->cb == SHARED_INFO_CB) | ||
142 | return si; | ||
143 | } | ||
144 | |||
145 | static int fd_to_tty(int fd) | ||
146 | { | ||
147 | ULONG len = 0; | ||
148 | OBJECT_NAME_INFORMATION dummy_oni, *ntfn; | ||
149 | HANDLE h = _get_osfhandle(fd); | ||
150 | char buf[PATH_MAX]; | ||
151 | int tty; | ||
152 | |||
153 | NtQueryObject (h, ObjectNameInformation, &dummy_oni, sizeof (dummy_oni), &len); | ||
154 | ntfn = malloc (len + sizeof (WCHAR)); | ||
155 | NtQueryObject (h, ObjectNameInformation, ntfn, len, NULL); | ||
156 | wcstombs(buf, ntfn->Name.Buffer, 100); | ||
157 | tty = -1; | ||
158 | swscanf(ntfn->Name.Buffer, L"\\Device\\NamedPipe\\cygwin-tty%d-from-master", &tty); | ||
159 | free(ntfn); | ||
160 | return tty; | ||
161 | } | ||
162 | |||
163 | int is_cygwin_tty(int fd) | ||
164 | { | ||
165 | return fd_to_tty(fd) >= 0 && get_shared_info() != NULL; | ||
166 | } | ||
167 | |||
168 | static int cygwin_tcgetattr(int fd, struct termios *t) | ||
169 | { | ||
170 | int tty = fd_to_tty(fd); | ||
171 | struct shared_info *si = get_shared_info(); | ||
172 | *t = si->tty.ttys[tty].tty_min.ti; | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | |||
177 | static int cygwin_tcsetattr(int fd, int mode, const struct termios *t) | ||
178 | { | ||
179 | int tty = fd_to_tty(fd); | ||
180 | struct shared_info *si; | ||
181 | |||
182 | if (tty < 0) | ||
183 | return -1; | ||
184 | si = get_shared_info(); | ||
185 | si->tty.ttys[tty].tty_min.ti = *t; | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | #else | ||
190 | |||
191 | int is_cygwin_tty(int fd) | ||
192 | { | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | #define cygwin_tcgetattr(fd,t) -1 | ||
197 | #define cygwin_tcsetattr(fd,mode,t) -1 | ||
198 | |||
199 | #endif | ||
200 | |||
201 | #undef isatty | ||
202 | int mingw_isatty(int fd) | ||
203 | { | ||
204 | return isatty(fd) || is_cygwin_tty(fd); | ||
205 | } | ||
206 | |||
7 | int tcgetattr(int fd, struct termios *t) | 207 | int tcgetattr(int fd, struct termios *t) |
8 | { | 208 | { |
209 | int ret = cygwin_tcgetattr(fd, t); | ||
210 | if (ret >= 0) | ||
211 | return ret; | ||
212 | /* not cygwin tty, likely windows console */ | ||
9 | t->c_lflag = ECHO; | 213 | t->c_lflag = ECHO; |
10 | return 0; | 214 | return 0; |
11 | } | 215 | } |
@@ -13,7 +217,7 @@ int tcgetattr(int fd, struct termios *t) | |||
13 | 217 | ||
14 | int tcsetattr(int fd, int mode, const struct termios *t) | 218 | int tcsetattr(int fd, int mode, const struct termios *t) |
15 | { | 219 | { |
16 | return 0; | 220 | return cygwin_tcsetattr(fd, mode, t); |
17 | } | 221 | } |
18 | 222 | ||
19 | static int get_wincon_width_height(const int fd, int *width, int *height) | 223 | static int get_wincon_width_height(const int fd, int *width, int *height) |
@@ -36,6 +240,18 @@ static int get_wincon_width_height(const int fd, int *width, int *height) | |||
36 | 240 | ||
37 | int get_terminal_width_height(const int fd, int *width, int *height) | 241 | int get_terminal_width_height(const int fd, int *width, int *height) |
38 | { | 242 | { |
243 | #if ENABLE_FEATURE_CYGWIN_TTY | ||
244 | int tty = fd_to_tty(fd); | ||
245 | struct shared_info *si = get_shared_info(); | ||
246 | |||
247 | if (tty >= 0 && si) { | ||
248 | if (width) | ||
249 | *width = si->tty.ttys[tty].tty_min.winsize.ws_col; | ||
250 | if (height) | ||
251 | *height = si->tty.ttys[tty].tty_min.winsize.ws_row; | ||
252 | return 0; | ||
253 | } | ||
254 | #endif | ||
39 | return get_wincon_width_height(fd, width, height); | 255 | return get_wincon_width_height(fd, width, height); |
40 | } | 256 | } |
41 | 257 | ||
diff --git a/libbb/winansi.c b/libbb/winansi.c index f3a304436..0e139432d 100644 --- a/libbb/winansi.c +++ b/libbb/winansi.c | |||
@@ -4,6 +4,7 @@ | |||
4 | 4 | ||
5 | #include <windows.h> | 5 | #include <windows.h> |
6 | #include "libbb.h" | 6 | #include "libbb.h" |
7 | #include "cygwin_termios.h" | ||
7 | 8 | ||
8 | /* | 9 | /* |
9 | Functions to be wrapped: | 10 | Functions to be wrapped: |
@@ -278,7 +279,7 @@ int winansi_fputs(const char *str, FILE *stream) | |||
278 | { | 279 | { |
279 | int rv; | 280 | int rv; |
280 | 281 | ||
281 | if (!isatty(fileno(stream))) | 282 | if (!isatty(fileno(stream)) || is_cygwin_tty(fileno(stream))) |
282 | return fputs(str, stream); | 283 | return fputs(str, stream); |
283 | 284 | ||
284 | init(); | 285 | init(); |
@@ -301,7 +302,7 @@ static int winansi_vfprintf(FILE *stream, const char *format, va_list list) | |||
301 | char *buf = small_buf; | 302 | char *buf = small_buf; |
302 | va_list cp; | 303 | va_list cp; |
303 | 304 | ||
304 | if (!isatty(fileno(stream))) | 305 | if (!isatty(fileno(stream)) || is_cygwin_tty(fileno(stream))) |
305 | goto abort; | 306 | goto abort; |
306 | 307 | ||
307 | init(); | 308 | init(); |
diff --git a/scripts/trylink b/scripts/trylink index 63132eda9..6cca80c11 100755 --- a/scripts/trylink +++ b/scripts/trylink | |||
@@ -11,6 +11,7 @@ try() { | |||
11 | } | 11 | } |
12 | 12 | ||
13 | try "-lws2_32" "$@" | 13 | try "-lws2_32" "$@" |
14 | try "-lws2_32 -lntdll" "$@" | ||
14 | try "" "$@" | 15 | try "" "$@" |
15 | try "-lm" "$@" | 16 | try "-lm" "$@" |
16 | try "-lcrypt" "$@" | 17 | try "-lcrypt" "$@" |