aboutsummaryrefslogtreecommitdiff
path: root/tools/gen_macos_headers_c.zig
blob: f95023adb75649b3154d2413148b6b75d34598c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
const std = @import("std");
const assert = std.debug.assert;
const info = std.log.info;
const fatal = std.process.fatal;

const Allocator = std.mem.Allocator;

var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
const gpa = general_purpose_allocator.allocator();

const usage =
    \\gen_macos_headers_c [dir]
    \\
    \\General Options:
    \\-h, --help                    Print this help and exit
;

pub fn main() anyerror!void {
    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
    defer arena_allocator.deinit();
    const arena = arena_allocator.allocator();

    const args = try std.process.argsAlloc(arena);
    if (args.len == 1) fatal("no command or option specified", .{});

    var positionals = std.array_list.Managed([]const u8).init(arena);

    for (args[1..]) |arg| {
        if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
            return info(usage, .{});
        } else try positionals.append(arg);
    }

    if (positionals.items.len != 1) fatal("expected one positional argument: [dir]", .{});

    var dir = try std.fs.cwd().openDir(positionals.items[0], .{ .no_follow = true });
    defer dir.close();
    var paths = std.array_list.Managed([]const u8).init(arena);
    try findHeaders(arena, dir, "", &paths);

    const SortFn = struct {
        pub fn lessThan(ctx: void, lhs: []const u8, rhs: []const u8) bool {
            _ = ctx;
            return std.mem.lessThan(u8, lhs, rhs);
        }
    };

    std.mem.sort([]const u8, paths.items, {}, SortFn.lessThan);

    var buffer: [2000]u8 = undefined;
    var stdout_writer = std.fs.File.stdout().writerStreaming(&buffer);
    const w = &stdout_writer.interface;
    try w.writeAll("#define _XOPEN_SOURCE\n");
    for (paths.items) |path| {
        try w.print("#include <{s}>\n", .{path});
    }
    try w.writeAll(
        \\int main(int argc, char **argv) {
        \\    return 0;
        \\}
    );
    try w.flush();
}

fn findHeaders(
    arena: Allocator,
    dir: std.fs.Dir,
    prefix: []const u8,
    paths: *std.array_list.Managed([]const u8),
) anyerror!void {
    var it = dir.iterate();
    while (try it.next()) |entry| {
        switch (entry.kind) {
            .directory => {
                const path = try std.fs.path.join(arena, &.{ prefix, entry.name });
                var subdir = try dir.openDir(entry.name, .{ .no_follow = true });
                defer subdir.close();
                try findHeaders(arena, subdir, path, paths);
            },
            .file, .sym_link => {
                const ext = std.fs.path.extension(entry.name);
                if (!std.mem.eql(u8, ext, ".h")) continue;
                const path = try std.fs.path.join(arena, &.{ prefix, entry.name });
                try paths.append(path);
            },
            else => {},
        }
    }
}