1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module iv
.vt100
.vt100emu
;
24 import iv
.vt100
.scrbuf
;
25 import iv
.vt100
.vt100buf
;
28 // ////////////////////////////////////////////////////////////////////////// //
29 // VT-100 emulator with Pty
30 public class VT100Emu
: VT100Buf
{
33 uint wrbufpos
, wrbufsize
;
43 void wrbufFree () nothrow @trusted @nogc {
45 import core
.stdc
.stdlib
: free
;
49 wrbufpos
= wrbufsize
= 0;
52 // ////////////////////////////////////////////////////////////////////// //
53 public final @property bool canWriteData () nothrow @trusted @nogc {
54 import core
.sys
.posix
.sys
.select
;
55 import core
.sys
.posix
.sys
.time
: timeval
;
56 if (!ptyi
.valid
) return false;
60 FD_SET(ptyi
.masterfd
, &wrs
);
64 int sres
= select(ptyi
.masterfd
+1, &wrs
, null, null, &tv
);
66 import core
.stdc
.errno
;
67 if (errno
== EINTR
) continue;
74 final void writeBuf () nothrow @trusted @nogc {
75 if (writeFucked || wrbufpos
== 0 ||
!ptyi
.valid
) return;
77 import core
.sys
.posix
.unistd
: write
;
78 auto wr
= write(ptyi
.masterfd
, wrbuf
, wrbufpos
);
80 import core
.stdc
.errno
;
81 if (errno
== EINTR
) continue;
89 import core
.stdc
.string
: memmove
;
90 uint left
= wrbufpos
-cast(uint)wr
;
91 memmove(wrbuf
, wrbuf
+wr
, left
);
99 public final @property usize
dataBufUsed () const pure nothrow @safe @nogc { pragma(inline
, true); return wrbufpos
; }
100 public final @property bool wasWriteError () const pure nothrow @safe @nogc { pragma(inline
, true); return writeFucked
; }
101 public final void resetWriteError () pure nothrow @safe @nogc { pragma(inline
, true); writeFucked
= false; }
103 public override void putData (const(void)[] buf
) nothrow @trusted @nogc {
104 if (!ptyi
.valid
) return;
105 if (wrbufpos
&& wrbufpos
== wrbufsize
&& canWriteData
) writeBuf();
106 auto data
= cast(const(ubyte)[])buf
;
107 while (data
.length
> 0) {
108 import core
.stdc
.string
: memcpy
;
109 if (wrbufpos
== wrbufsize
) {
110 if (canWriteData
) writeBuf();
111 if (wrbufpos
== wrbufsize
) {
112 import core
.stdc
.stdlib
: realloc
;
113 if (wrbufsize
+buf
.length
<= wrbufsize
) assert(0, "fuck!");
114 uint newsz
= wrbufsize
+cast(uint)data
.length
;
115 if (newsz
>= int.max
/1024) {
120 if (newsz
&0x3ff) newsz
= (newsz|
0x3ff)+1;
121 auto nbuf
= cast(char*)realloc(wrbuf
, newsz
);
122 if (nbuf
is null) assert(0, "out of memory");
127 uint chunk
= cast(uint)(data
.length
> wrbufsize
-wrbufpos ? wrbufsize
-wrbufpos
: data
.length
);
128 memcpy(wrbuf
+wrbufpos
, data
.ptr
, chunk
);
130 data
= data
[chunk
..$];
134 final @property bool canReadData () nothrow @trusted @nogc {
135 import core
.sys
.posix
.sys
.select
;
136 import core
.sys
.posix
.sys
.time
: timeval
;
137 if (!ptyi
.valid
) return false;
141 FD_SET(ptyi
.masterfd
, &rds
);
145 int sres
= select(ptyi
.masterfd
+1, &rds
, null, null, &tv
);
147 import core
.stdc
.errno
;
148 if (errno
== EINTR
) continue;
155 final void readData () nothrow {
156 if (!ptyi
.valid
) return;
158 while (canReadData
) {
159 import core
.sys
.posix
.unistd
: read
;
160 auto rd
= read(ptyi
.masterfd
, rdbuf
.ptr
, rdbuf
.length
);
162 import core
.stdc
.errno
;
163 if (errno
== EINTR
) continue;
167 version(dump_output
) {
169 import std
.stdio
: File
;
170 auto fo
= File("zdump.log", "a");
171 fo
.rawWrite(rdbuf
[0..rd
]);
172 } catch (Exception
) {}
174 putstr(rdbuf
[0..rd
]);
175 total
+= cast(int)rd
;
176 if (total
> 65535) break;
181 this (int aw
, int ah
, bool mIsUtfuck
=true) nothrow @safe {
182 super(aw
, ah
, mIsUtfuck
);
185 ~this () { wrbufFree(); }
188 override void intrClear () nothrow @trusted {
193 // for terminal emulator
194 final @property int masterFD () nothrow @trusted @nogc { return ptyi
.masterfd
; }
196 final const(int)[] getReadFDs () nothrow @trusted @nogc {
197 if (!ptyi
.valid
) return null;
198 readfds
[0] = ptyi
.masterfd
;
199 return readfds
[0..1];
202 final const(int)[] getWriteFDs () nothrow @trusted @nogc {
203 if (!ptyi
.valid
) return null;
205 { import core
.stdc
.stdio
; stderr
.fprintf("WARNING! write error occured for masterfd %d!\n", ptyi
.masterfd
); }
206 if (onBell
!is null) onBell(this);
209 if (!dataBufUsed
) return null;
210 writefds
[0] = ptyi
.masterfd
;
211 return writefds
[0..1];
214 final void canWriteTo (int fd
) {
215 if (fd
< 0 || fd
!= ptyi
.masterfd
) return;
219 final void canReadFrom (int fd
) {
220 if (fd
< 0 || fd
!= ptyi
.masterfd
) return;
224 final bool checkDeadChild (int pid
, int exitcode
) {
225 if (!ptyi
.valid
) return false;
226 if (ptyi
.pid
== pid
) {
228 //putstr("\r\n\x1b[0;33;1;41mDEAD CHILD");
235 override void sendTTYResizeSignal () {
236 if (ptyi
.valid
) .sendTTYResizeSignal(ptyi
.masterfd
, mWidth
, mHeight
);
239 // ////////////////////////////////////////////////////////////////////// //
240 final @property bool isPtyActive () const pure nothrow @safe @nogc { pragma(inline
, true); return ptyi
.valid
; }
242 final bool execPty (const(char[])[] args
) nothrow @trusted @nogc {
243 if (isPtyActive
) return false;
244 ptyi
= executeInNewPty(args
, mWidth
, mHeight
);
248 final char[] getProcessName(bool fullname
=false) (char[] obuf
) nothrow @trusted @nogc {
249 return .getProcessName(masterFD
, obuf
);
252 final char[] getProcessCwd (char[] obuf
) nothrow @trusted @nogc {
253 return .getProcessCwd(masterFD
, obuf
);
256 final char[] getFullProcessName (char[] obuf
) nothrow @trusted @nogc {
257 return .getFullProcessName(masterFD
, obuf
);
262 // ////////////////////////////////////////////////////////////////////////// //
266 import core
.sys
.posix
.sys
.ioctl
: winsize
;
267 import core
.sys
.posix
.sys
.types
: pid_t
;
268 import core
.sys
.posix
.termios
: termios
;
270 extern(C
) nothrow @nogc {
271 int openpty (int* amaster
, int* aslave
, char* name
, const(termios
)* termp
, const(winsize
)* winp
);
272 pid_t
forkpty (int* amaster
, char* name
, const(termios
)* termp
, const(winsize
)* winp
);
273 int login_tty (int fd
);
276 //enum O_CLOEXEC = 0o2000000; /* set close_on_exec */
277 enum O_CLOEXEC
= 524288; /* set close_on_exec */
279 int pipe2 (int* pipefd
, int flags
);
283 // ////////////////////////////////////////////////////////////////////////// //
284 private void closeAllFDs () nothrow @trusted @nogc {
286 import core
.sys
.posix
.sys
.resource
: getrlimit
, rlimit
, rlim_t
, RLIMIT_NOFILE
;
287 import core
.sys
.posix
.unistd
: close
;
289 getrlimit(RLIMIT_NOFILE
, &r
);
290 foreach (rlim_t idx
; 3..r
.rlim_cur
) close(cast(int)idx
);
294 void exec (const(char[])[] args
) nothrow @trusted @nogc {
295 import core
.sys
.posix
.stdlib
: getenv
;
296 static char[65536] cmdline
= void;
297 static const(char)*[32768] argv
= void;
301 // if no args or first arg is empty, get default shell
302 if (args
.length
== 0 || args
[0].length
== 0) {
303 const(char)* envshell
= getenv("SHELL");
304 if (envshell
is null ||
!envshell
[0]) envshell
= "/bin/sh";
305 //setenv("TERM", "rxvt", 1); // should be done on program start
307 if (args
.length
== 0) {
316 foreach (immutable idx
; stidx
..args
.length
) {
317 import core
.stdc
.string
: memcpy
;
318 if (args
[idx
].length
>= cmdline
.length
-cmpos
) break;
319 if (argc
+1 >= argv
.length
) break;
320 argv
[argc
++] = cmdline
.ptr
+cmpos
;
321 if (args
[idx
].length
) {
322 memcpy(cmdline
.ptr
+cmpos
, args
[idx
].ptr
, args
[idx
].length
);
323 cmpos
+= args
[idx
].length
;
325 cmdline
[cmpos
++] = 0;
329 import core
.sys
.posix
.unistd
: execvp
;
330 execvp(argv
[0], argv
.ptr
);
332 import core
.stdc
.stdlib
: exit
, EXIT_FAILURE
;
338 // ////////////////////////////////////////////////////////////////////////// //
339 void sendTTYResizeSignal (int masterfd
, int width
, int height
) nothrow @trusted @nogc {
340 if (masterfd
< 0) return;
341 width
= ScreenBuffer
.max(1, ScreenBuffer
.min(width
, ushort.max
-1));
342 height
= ScreenBuffer
.max(1, ScreenBuffer
.min(height
, ushort.max
-1));
343 import core
.sys
.posix
.sys
.ioctl
: winsize
, ioctl
, TIOCSWINSZ
;
345 w
.ws_row
= cast(ushort)height
;
346 w
.ws_col
= cast(ushort)width
;
347 w
.ws_xpixel
= w
.ws_ypixel
= 0;
348 if (ioctl(masterfd
, TIOCSWINSZ
, &w
) < 0) {
349 import core
.stdc
.errno
;
350 import core
.stdc
.stdio
;
351 import core
.stdc
.string
;
352 fprintf(stderr
, "Warning: couldn't set window size: %s\n", strerror(errno
));
357 // ////////////////////////////////////////////////////////////////////////// //
362 @property bool valid () const pure nothrow @safe @nogc { pragma(inline
, true); return (masterfd
>= 0 && pid
>= 0); }
366 import core
.sys
.posix
.unistd
: close
;
375 PtyInfo
executeInNewPty (const(char[])[] args
, int width
, int height
) nothrow @trusted @nogc {
376 import core
.sys
.posix
.stdlib
: setenv
;
377 import core
.sys
.posix
.sys
.ioctl
: winsize
;
379 width
= ScreenBuffer
.max(1, ScreenBuffer
.min(width
, ushort.max
-1));
380 height
= ScreenBuffer
.max(1, ScreenBuffer
.min(height
, ushort.max
-1));
382 w
.ws_col
= cast(ushort)width
;
383 w
.ws_row
= cast(ushort)height
;
384 w
.ws_xpixel
= w
.ws_ypixel
= 0;
385 auto mPId
= forkpty(&ptyi
.masterfd
, null, null, &w
);
388 if (ptyi
.masterfd
>= 0) {
389 import core
.sys
.posix
.unistd
: close
;
390 close(ptyi
.masterfd
);
398 import core
.sys
.posix
.unistd
: setsid
;
399 setenv("TERM", "rxvt", 1);
400 setsid(); // create a new process group
407 // no need to set terminal size here, as `forkpty()` did that for us
412 // ////////////////////////////////////////////////////////////////////////// //
413 char[] getProcessName(bool fullname
=false) (int masterfd
, char[] obuf
) nothrow @trusted @nogc {
414 import core
.stdc
.stdio
: snprintf
;
415 import core
.sys
.posix
.fcntl
: open
, O_RDONLY
;
416 import core
.sys
.posix
.unistd
: close
, read
, tcgetpgrp
;
417 import core
.sys
.posix
.sys
.types
: pid_t
;
418 char[128] path
= void;
419 char[4096] res
= void;
420 //static char path[256], res[4097], *c;
421 if (masterfd
< 0 || obuf
.length
< 1) return null;
422 pid_t pgrp
= tcgetpgrp(masterfd
);
423 if (pgrp
== -1) return null;
424 snprintf(path
.ptr
, cast(uint)path
.length
, "/proc/%d/cmdline", pgrp
);
425 int fd
= open(path
.ptr
, O_RDONLY
);
426 if (fd
< 0) return null;
427 auto rd
= read(fd
, res
.ptr
, res
.length
);
429 if (rd
<= 0) return null;
430 static if (fullname
) {
432 while (pos
< rd
&& res
[pos
]) ++pos
;
433 auto sb
= res
[0..pos
];
435 while (sb
.length
> obuf
.length
) sb
= sb
[1..$];
436 obuf
[0..sb
.length
] = sb
[];
437 return obuf
[0..sb
.length
];
440 while (pos
> 0 && res
[pos
-1] != '/') --pos
;
441 if (pos
>= rd
) return null;
442 if (rd
-pos
> obuf
.length
) pos
= rd
-obuf
.length
;
443 obuf
[0..rd
-pos
] = res
[pos
..rd
];
444 return obuf
[0..rd
-pos
];
449 char[] getFullProcessName (int masterfd
, char[] obuf
) nothrow @trusted @nogc {
450 import core
.stdc
.stdio
: snprintf
;
451 import core
.sys
.posix
.fcntl
: open
, O_RDONLY
;
452 import core
.sys
.posix
.unistd
: close
, read
, tcgetpgrp
, readlink
;
453 import core
.sys
.posix
.sys
.types
: pid_t
;
454 char[128] path
= void;
455 char[4096] res
= void;
456 if (masterfd
< 0 || obuf
.length
< 1) return null;
457 pid_t pgrp
= tcgetpgrp(masterfd
);
458 if (pgrp
== -1) return null;
459 snprintf(path
.ptr
, cast(uint)path
.length
, "/proc/%d/exe", pgrp
);
460 auto rd
= readlink(path
.ptr
, res
.ptr
, res
.length
);
461 if (rd
<= 0) return null;
463 while (pos
< rd
&& res
[pos
]) ++pos
;
464 auto sb
= res
[0..pos
];
466 while (sb
.length
> obuf
.length
) sb
= sb
[1..$];
467 obuf
[0..sb
.length
] = sb
[];
468 return obuf
[0..sb
.length
];
472 char[] getProcessCwd (int masterfd
, char[] obuf
) nothrow @trusted @nogc {
473 import core
.stdc
.stdio
: snprintf
;
474 import core
.sys
.posix
.fcntl
: open
, O_RDONLY
;
475 import core
.sys
.posix
.unistd
: close
, read
, tcgetpgrp
, readlink
;
476 import core
.sys
.posix
.sys
.types
: pid_t
;
477 char[128] path
= void;
478 char[4096] res
= void;
479 if (masterfd
< 0 || obuf
.length
< 1) return null;
480 pid_t pgrp
= tcgetpgrp(masterfd
);
481 if (pgrp
== -1) return null;
482 snprintf(path
.ptr
, cast(uint)path
.length
, "/proc/%d/cwd", pgrp
);
483 auto rd
= readlink(path
.ptr
, res
.ptr
, res
.length
);
484 if (rd
<= 0) return null;
486 while (pos
< rd
&& res
[pos
]) ++pos
;
487 auto sb
= res
[0..pos
];
489 while (sb
.length
> obuf
.length
) sb
= sb
[1..$];
490 obuf
[0..sb
.length
] = sb
[];
491 return obuf
[0..sb
.length
];