mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
236 lines
6.9 KiB
Zig
236 lines
6.9 KiB
Zig
const std = @import("std");
|
|
const bun = @import("root").bun;
|
|
const uv = bun.windows.libuv;
|
|
|
|
const log = bun.Output.scoped(.PipeSource, true);
|
|
|
|
pub const Source = union(enum) {
|
|
pipe: *Pipe,
|
|
tty: *Tty,
|
|
file: *File,
|
|
sync_file: *File,
|
|
|
|
const Pipe = uv.Pipe;
|
|
const Tty = uv.uv_tty_t;
|
|
|
|
pub const File = struct {
|
|
fs: uv.fs_t,
|
|
// we need a new fs_t to close the file
|
|
// the current one is used for write/reading/canceling
|
|
// we dont wanna to free any data that is being used in uv loop
|
|
close_fs: uv.fs_t,
|
|
iov: uv.uv_buf_t,
|
|
file: uv.uv_file,
|
|
};
|
|
|
|
pub fn isClosed(this: Source) bool {
|
|
return switch (this) {
|
|
.pipe => |pipe| pipe.isClosed(),
|
|
.tty => |tty| tty.isClosed(),
|
|
.sync_file, .file => |file| file.file == -1,
|
|
};
|
|
}
|
|
|
|
pub fn isActive(this: Source) bool {
|
|
return switch (this) {
|
|
.pipe => |pipe| pipe.isActive(),
|
|
.tty => |tty| tty.isActive(),
|
|
.sync_file, .file => true,
|
|
};
|
|
}
|
|
|
|
pub fn getHandle(this: Source) *uv.Handle {
|
|
return switch (this) {
|
|
.pipe => @ptrCast(this.pipe),
|
|
.tty => @ptrCast(this.tty),
|
|
.sync_file, .file => unreachable,
|
|
};
|
|
}
|
|
pub fn toStream(this: Source) *uv.uv_stream_t {
|
|
return switch (this) {
|
|
.pipe => @ptrCast(this.pipe),
|
|
.tty => @ptrCast(this.tty),
|
|
.sync_file, .file => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn getFd(this: Source) bun.FileDescriptor {
|
|
return switch (this) {
|
|
.pipe => |pipe| pipe.fd(),
|
|
.tty => |tty| tty.fd(),
|
|
.sync_file, .file => |file| bun.FDImpl.fromUV(file.file).encode(),
|
|
};
|
|
}
|
|
|
|
pub fn setData(this: Source, data: ?*anyopaque) void {
|
|
switch (this) {
|
|
.pipe => |pipe| pipe.data = data,
|
|
.tty => |tty| tty.data = data,
|
|
.sync_file, .file => |file| file.fs.data = data,
|
|
}
|
|
}
|
|
|
|
pub fn getData(this: Source) ?*anyopaque {
|
|
return switch (this) {
|
|
.pipe => |pipe| pipe.data,
|
|
.tty => |tty| tty.data,
|
|
.sync_file, .file => |file| file.fs.data,
|
|
};
|
|
}
|
|
|
|
pub fn ref(this: Source) void {
|
|
switch (this) {
|
|
.pipe => this.pipe.ref(),
|
|
.tty => this.tty.ref(),
|
|
.sync_file, .file => return,
|
|
}
|
|
}
|
|
|
|
pub fn unref(this: Source) void {
|
|
switch (this) {
|
|
.pipe => this.pipe.unref(),
|
|
.tty => this.tty.unref(),
|
|
.sync_file, .file => return,
|
|
}
|
|
}
|
|
|
|
pub fn hasRef(this: Source) bool {
|
|
return switch (this) {
|
|
.pipe => |pipe| pipe.hasRef(),
|
|
.tty => |tty| tty.hasRef(),
|
|
.sync_file, .file => false,
|
|
};
|
|
}
|
|
|
|
pub fn openPipe(loop: *uv.Loop, fd: bun.FileDescriptor) bun.JSC.Maybe(*Source.Pipe) {
|
|
log("openPipe (fd = {})", .{fd});
|
|
const pipe = bun.default_allocator.create(Source.Pipe) catch bun.outOfMemory();
|
|
// we should never init using IPC here see ipc.zig
|
|
switch (pipe.init(loop, false)) {
|
|
.err => |err| {
|
|
return .{ .err = err };
|
|
},
|
|
else => {},
|
|
}
|
|
|
|
return switch (pipe.open(fd)) {
|
|
.err => |err| .{
|
|
.err = err,
|
|
},
|
|
.result => .{
|
|
.result = pipe,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub var stdin_tty: uv.uv_tty_t = undefined;
|
|
pub var stdin_tty_init = false;
|
|
|
|
pub fn openTty(loop: *uv.Loop, fd: bun.FileDescriptor) bun.JSC.Maybe(*Source.Tty) {
|
|
log("openTTY (fd = {})", .{fd});
|
|
|
|
const uv_fd = bun.uvfdcast(fd);
|
|
|
|
if (uv_fd == 0) {
|
|
if (!stdin_tty_init) {
|
|
switch (stdin_tty.init(loop, uv_fd)) {
|
|
.err => |err| return .{ .err = err },
|
|
.result => {},
|
|
}
|
|
stdin_tty_init = true;
|
|
}
|
|
|
|
return .{ .result = &stdin_tty };
|
|
}
|
|
|
|
const tty = bun.default_allocator.create(Source.Tty) catch bun.outOfMemory();
|
|
return switch (tty.init(loop, uv_fd)) {
|
|
.err => |err| .{ .err = err },
|
|
.result => .{ .result = tty },
|
|
};
|
|
}
|
|
|
|
pub fn openFile(fd: bun.FileDescriptor) *Source.File {
|
|
bun.assert(fd.isValid() and bun.uvfdcast(fd) != -1);
|
|
log("openFile (fd = {})", .{fd});
|
|
const file = bun.default_allocator.create(Source.File) catch bun.outOfMemory();
|
|
|
|
file.* = std.mem.zeroes(Source.File);
|
|
file.file = bun.uvfdcast(fd);
|
|
return file;
|
|
}
|
|
|
|
pub fn open(loop: *uv.Loop, fd: bun.FileDescriptor) bun.JSC.Maybe(Source) {
|
|
const rc = bun.windows.libuv.uv_guess_handle(bun.uvfdcast(fd));
|
|
log("open(fd: {}, type: {d})", .{ fd, @tagName(rc) });
|
|
|
|
switch (rc) {
|
|
.named_pipe => {
|
|
switch (openPipe(loop, fd)) {
|
|
.result => |pipe| return .{ .result = .{ .pipe = pipe } },
|
|
.err => |err| return .{ .err = err },
|
|
}
|
|
},
|
|
.tty => {
|
|
switch (openTty(loop, fd)) {
|
|
.result => |tty| return .{ .result = .{ .tty = tty } },
|
|
.err => |err| return .{ .err = err },
|
|
}
|
|
},
|
|
.file => {
|
|
return .{
|
|
.result = .{
|
|
.file = openFile(fd),
|
|
},
|
|
};
|
|
},
|
|
else => {
|
|
const errno = bun.windows.getLastErrno();
|
|
|
|
if (errno == .SUCCESS) {
|
|
return .{
|
|
.result = .{
|
|
.file = openFile(fd),
|
|
},
|
|
};
|
|
}
|
|
|
|
return .{ .err = bun.sys.Error.fromCode(errno, .open) };
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn setRawMode(this: Source, value: bool) bun.sys.Maybe(void) {
|
|
return switch (this) {
|
|
.tty => |tty| {
|
|
if (tty
|
|
.setMode(if (value) .raw else .normal)
|
|
.toError(.uv_tty_set_mode)) |err|
|
|
{
|
|
return .{ .err = err };
|
|
} else {
|
|
return .{ .result = {} };
|
|
}
|
|
},
|
|
else => .{
|
|
.err = .{
|
|
.errno = @intFromEnum(bun.C.E.NOTSUP),
|
|
.syscall = .uv_tty_set_mode,
|
|
.fd = this.getFd(),
|
|
},
|
|
},
|
|
};
|
|
}
|
|
};
|
|
|
|
export fn Source__setRawModeStdin(raw: bool) c_int {
|
|
const tty = switch (Source.openTty(bun.JSC.VirtualMachine.get().uvLoop(), bun.STDIN_FD)) {
|
|
.result => |tty| tty,
|
|
.err => |e| return e.errno,
|
|
};
|
|
if (tty.setMode(if (raw) .raw else .normal).toError(.uv_tty_set_mode)) |err| {
|
|
return err.errno;
|
|
}
|
|
return 0;
|
|
}
|