From a2a63025884dadb0e92c2f3bfbdde2463743014c Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Thu, 6 Jan 2022 12:16:22 -0800 Subject: [PATCH] implement clipboard set, both windows and linux --- src/download.zig | 50 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/download.zig b/src/download.zig index 59f2dc8..4638bcc 100644 --- a/src/download.zig +++ b/src/download.zig @@ -56,6 +56,7 @@ fn addToClipboard(allocator: std.mem.Allocator, data: []const u8) !void { } }; + std.log.debug("exec done", .{}); try std.io.getStdErr().writer().writeAll(result.stderr); switch (result.term) { .Exited => |code| if (code != 0) return error.NonZeroExit, @@ -66,8 +67,17 @@ fn addToClipboard(allocator: std.mem.Allocator, data: []const u8) !void { } fn execLinux(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.ExecResult { - const xclip_cmd = try std.fmt.allocPrint(allocator, "echo -n '{s}'| xclip -selection c", .{data}); + // we need to do this (maybe) because there is no real way to close stdin cleanly, which is what will + // kill xclip. + // + // Also, it seems xclip doesn't close stdout, which triggers ChildProcess.exec to know + // that the process is done. Redirecting stdout/stderr as below will get the job + // done, though + const escaped_data = try escape(allocator, data, "'", "'\"", "\"'"); + defer allocator.free(escaped_data); + const xclip_cmd = try std.fmt.allocPrint(allocator, "echo -n '{s}'| xclip -selection c >/dev/null 2>&1", .{escaped_data}); defer allocator.free(xclip_cmd); + std.log.debug("cmd: {s}", .{xclip_cmd}); return std.ChildProcess.exec(.{ .allocator = allocator, @@ -81,15 +91,43 @@ fn execLinux(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.E } fn execWindows(allocator: std.mem.Allocator, data: []const u8) !std.ChildProcess.ExecResult { - const clip_cmd = try std.fmt.allocPrint(allocator, "echo '{s}'| clip", .{data}); - defer allocator.free(clip_cmd); + // we need to do this (maybe) because there is no real way to close stdin cleanly, which is what will + // kill clip.exe + // TODO: Create minimal repro and submit zig stdlib bug + const escaped_data = try escape(allocator, data, "'", "'", ""); + defer allocator.free(escaped_data); + const cmd = try std.fmt.allocPrint(allocator, "write-output '{s}'| set-clipboard", .{escaped_data}); + defer allocator.free(cmd); + std.log.debug("cmd: {s}", .{cmd}); return std.ChildProcess.exec(.{ .allocator = allocator, .argv = &[_][]const u8{ - "c:\\windows\\system32\\cmd.exe", // TODO: use Comspec - "/c", - clip_cmd, + "powershell", + "-Command", + cmd, }, }); } + +fn escape(allocator: std.mem.Allocator, data: []const u8, danger_chars: []const u8, prefix: []const u8, suffix: []const u8) ![]const u8 { + var escaped = try std.ArrayList(u8).initCapacity(allocator, data.len); + defer escaped.deinit(); + for (data) |c| { + var needs_escape = false; + for (danger_chars) |dc| { + if (c == dc) { + needs_escape = true; + break; + } + } + if (needs_escape) { + try escaped.appendSlice(prefix); + try escaped.append(c); + try escaped.appendSlice(suffix); + } else { + try escaped.append(c); + } + } + return escaped.toOwnedSlice(); +}