├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── build.zig └── src └── main.zig /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push] 3 | jobs: 4 | build: 5 | strategy: 6 | matrix: 7 | os: [ubuntu-latest, macos-latest, windows-latest] 8 | runs-on: ${{matrix.os}} 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: goto-bus-stop/setup-zig@v2 12 | with: 13 | version: 0.12.0 14 | - run: zig build 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache/ 2 | zig-out/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute 4 | this software, either in source code form or as a compiled binary, for any 5 | purpose, commercial or non-commercial, and by any means. 6 | 7 | In jurisdictions that recognize copyright laws, the author or authors of this 8 | software dedicate any and all copyright interest in the software to the public 9 | domain. We make this dedication for the benefit of the public at large and 10 | to the detriment of our heirs and 11 | 12 | successors. We intend this dedication to be an overt act of relinquishment 13 | in perpetuity of all present and future rights to this software under copyright 14 | law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 19 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 21 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | For more information, please refer to 24 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const Builder = @import("std").Build; 2 | 3 | pub fn build(b: *Builder) void { 4 | const target = b.standardTargetOptions(.{}); 5 | const optimize = b.standardOptimizeOption(.{}); 6 | 7 | const exe = b.addExecutable(.{ 8 | .name = "ziget", 9 | .root_source_file = .{ .path = "src/main.zig" }, 10 | .target = target, 11 | .optimize = optimize, 12 | }); 13 | 14 | b.installArtifact(exe); 15 | 16 | const run_cmd = b.addRunArtifact(exe); 17 | run_cmd.step.dependOn(b.getInstallStep()); 18 | if (b.args) |args| { 19 | run_cmd.addArgs(args); 20 | } 21 | 22 | const run_step = b.step("run", "Run the app"); 23 | run_step.dependOn(&run_cmd.step); 24 | } 25 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const os = std.os; 3 | 4 | const ZigetError = error{ 5 | CreateSockFail, 6 | InvalidAddr, 7 | ConnectError, 8 | SendError, 9 | RecvError, 10 | }; 11 | 12 | pub fn main() anyerror!void { 13 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 14 | defer _ = gpa.deinit(); 15 | 16 | const allocator = gpa.allocator(); 17 | var args_it = try std.process.argsWithAllocator(allocator); 18 | defer args_it.deinit(); 19 | 20 | // skip args[0] 21 | 22 | _ = args_it.skip(); 23 | 24 | const host = (args_it.next() orelse { 25 | std.debug.print("no host provided\n", .{}); 26 | return error.InvalidArgs; 27 | }); 28 | 29 | const remote_path = (args_it.next() orelse { 30 | std.debug.print("no remote path provided\n", .{}); 31 | return error.InvalidArgs; 32 | }); 33 | 34 | const output_path = (args_it.next() orelse { 35 | std.debug.print("no path provided\n", .{}); 36 | return error.InvalidArgs; 37 | }); 38 | 39 | std.debug.print("host: {s} remote: {s} output path: {s}\n", .{ host, remote_path, output_path }); 40 | 41 | var conn = try std.net.tcpConnectToHost(allocator, host, 80); 42 | defer conn.close(); 43 | 44 | var buffer: [256]u8 = undefined; 45 | const base_http = "GET {s} HTTP/1.1\r\nHost: {s}\r\nConnection: close\r\n\r\n"; 46 | const msg = try std.fmt.bufPrint(&buffer, base_http, .{ remote_path, host }); 47 | 48 | _ = try conn.write(msg); 49 | 50 | var buf: [1024]u8 = undefined; 51 | var total_bytes: usize = 0; 52 | 53 | var file = try std.fs.cwd().createFile(output_path, .{}); 54 | defer file.close(); 55 | 56 | while (true) { 57 | const byte_count = try conn.read(&buf); 58 | if (byte_count == 0) break; 59 | 60 | _ = try file.write(&buf); 61 | total_bytes += byte_count; 62 | } 63 | 64 | std.debug.print("written {d} bytes to file '{s}'\n", .{ total_bytes, output_path }); 65 | } 66 | --------------------------------------------------------------------------------