git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commite2b36593555a15afed228996c75b8937681b526a
parent89708b3d73
authorIsaac Freund <[email protected]>
date2021-07-24 16:44
river: use common CLI arg parsing code

This makes river's main() function quite a bit cleaner.

 build.zig                      |  3 ++
 {rivertile => common}/args.zig |  4 +--
 river/main.zig                 | 79 +++++++++++++++++++-----------------------
 rivertile/main.zig             |  4 +--
 4 files changed, 42 insertions(+), 48 deletions(-)

diff --git a/build.zig b/build.zig
index b432ca3..b792964 100644
--- a/build.zig
+++ b/build.zig
@@ -116,6 +116,7 @@ pub fn build(b: *zbs.Builder) !void {
 
         rivertile.step.dependOn(&scanner.step);
         rivertile.addPackage(scanner.getPkg());
+        rivertile.addPackagePath("args", "common/args.zig");
         rivertile.linkLibC();
         rivertile.linkSystemLibrary("wayland-client");
 
@@ -208,6 +209,8 @@ fn addServerDeps(exe: *zbs.LibExeObjStep, scanner: *ScanProtocolsStep) void {
     exe.addPackage(wlroots);
     exe.linkSystemLibrary("wlroots");
 
+    exe.addPackagePath("args", "common/args.zig");
+
     // TODO: remove when zig issue #131 is implemented
     scanner.addCSource(exe);
 }
diff --git a/rivertile/args.zig b/common/args.zig
similarity index 98%
rename from rivertile/args.zig
rename to common/args.zig
index 31b57bc..669f9f9 100644
--- a/rivertile/args.zig
+++ b/common/args.zig
@@ -113,14 +113,14 @@ pub fn Args(comptime num_positionals: comptime_int, comptime flag_defs: []const
             for (self.flags) |flag| {
                 if (cstr.cmp(flag.name, flag_name) == 0) return flag.value.boolean;
             }
-            unreachable;
+            unreachable; // Invalid flag_name
         }
 
         pub fn argFlag(self: Self, flag_name: [*:0]const u8) ?[*:0]const u8 {
             for (self.flags) |flag| {
                 if (cstr.cmp(flag.name, flag_name) == 0) return flag.value.arg;
             }
-            unreachable;
+            unreachable; // Invalid flag_name
         }
     };
 }
diff --git a/river/main.zig b/river/main.zig
index 212cc69..56a9477 100644
--- a/river/main.zig
+++ b/river/main.zig
@@ -15,12 +15,14 @@
 // You should have received a copy of the GNU General Public License
 // along with this program. If not, see <https://www.gnu.org/licenses/>.
 
+const build_options = @import("build_options");
 const std = @import("std");
 const fs = std.fs;
+const io = std.io;
 const os = std.os;
 const wlr = @import("wlroots");
-
-const build_options = @import("build_options");
+const Args = @import("args").Args;
+const FlagDef = @import("args").FlagDef;
 
 const c = @import("c.zig");
 const util = @import("util.zig");
@@ -47,43 +49,35 @@ const usage: []const u8 =
 ;
 
 pub fn main() anyerror!void {
-    var startup_command: ?[:0]const u8 = null;
-    {
-        var it = std.process.args();
-        // Skip our name
-        _ = it.nextPosix();
-        while (it.nextPosix()) |arg| {
-            if (std.mem.eql(u8, arg, "-h")) {
-                const stdout = std.io.getStdOut().writer();
-                try stdout.print(usage, .{});
-                os.exit(0);
-            } else if (std.mem.eql(u8, arg, "-c")) {
-                if (it.nextPosix()) |command| {
-                    // If the user used '-c' multiple times the variable
-                    // already holds a path and needs to be freed.
-                    if (startup_command) |cmd| util.gpa.free(cmd);
-                    startup_command = try util.gpa.dupeZ(u8, command);
-                } else {
-                    printErrorExit("Error: flag '-c' requires exactly one argument", .{});
-                }
-            } else if (std.mem.eql(u8, arg, "-l")) {
-                if (it.nextPosix()) |level_str| {
-                    const log_level = std.fmt.parseInt(u3, level_str, 10) catch
-                        printErrorExit("Error: invalid log level '{s}'", .{level_str});
-                    level = @intToEnum(std.log.Level, log_level);
-                } else {
-                    printErrorExit("Error: flag '-l' requires exactly one argument", .{});
-                }
-            } else if (std.mem.eql(u8, arg, "-version")) {
-                try std.io.getStdOut().writeAll(build_options.version);
-                os.exit(0);
-            } else {
-                const stderr = std.io.getStdErr().writer();
-                try stderr.print(usage, .{});
-                os.exit(1);
-            }
-        }
+    // This line is here because of https://github.com/ziglang/zig/issues/7807
+    const argv: [][*:0]const u8 = os.argv;
+    const args = Args(0, &[_]FlagDef{
+        .{ .name = "-h", .kind = .boolean },
+        .{ .name = "-version", .kind = .boolean },
+        .{ .name = "-c", .kind = .arg },
+        .{ .name = "-l", .kind = .arg },
+    }).parse(argv[1..]);
+
+    if (args.boolFlag("-h")) {
+        try io.getStdOut().writeAll(usage);
+        os.exit(0);
+    }
+    if (args.boolFlag("-version")) {
+        try io.getStdOut().writeAll(@import("build_options").version);
+        os.exit(0);
     }
+    if (args.argFlag("-l")) |level_str| {
+        const log_level = std.fmt.parseInt(u3, std.mem.span(level_str), 10) catch
+            fatal("Error: invalid log level '{s}'", .{level_str});
+        level = @intToEnum(std.log.Level, log_level);
+    }
+    const startup_command = blk: {
+        if (args.argFlag("-c")) |command| {
+            break :blk try util.gpa.dupeZ(u8, std.mem.span(command));
+        } else {
+            break :blk try defaultInitPath();
+        }
+    };
 
     wlr.log.init(switch (level) {
         .debug => .debug,
@@ -91,8 +85,6 @@ pub fn main() anyerror!void {
         .warn, .err, .crit, .alert, .emerg => .err,
     });
 
-    if (startup_command == null) startup_command = try defaultInitPath();
-
     std.log.info("initializing server", .{});
     try server.init();
     defer server.deinit();
@@ -125,9 +117,8 @@ pub fn main() anyerror!void {
     std.log.info("shutting down", .{});
 }
 
-fn printErrorExit(comptime format: []const u8, args: anytype) noreturn {
-    const stderr = std.io.getStdErr().writer();
-    stderr.print(format ++ "\n", args) catch os.exit(1);
+pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
+    io.getStdErr().writer().print(format ++ "\n", args) catch {};
     os.exit(1);
 }
 
@@ -160,7 +151,7 @@ pub fn log(
     if (@enumToInt(message_level) <= @enumToInt(level)) {
         // Don't store/log messages in release small mode to save space
         if (std.builtin.mode != .ReleaseSmall) {
-            const stderr = std.io.getStdErr().writer();
+            const stderr = io.getStdErr().writer();
             stderr.print(@tagName(message_level) ++ ": (" ++ @tagName(scope) ++ ") " ++
                 format ++ "\n", args) catch return;
         }
diff --git a/rivertile/main.zig b/rivertile/main.zig
index ff57475..cc977b4 100644
--- a/rivertile/main.zig
+++ b/rivertile/main.zig
@@ -45,8 +45,8 @@ const wayland = @import("wayland");
 const wl = wayland.client.wl;
 const river = wayland.client.river;
 
-const Args = @import("args.zig").Args;
-const FlagDef = @import("args.zig").FlagDef;
+const Args = @import("args").Args;
+const FlagDef = @import("args").FlagDef;
 
 const usage =
     \\Usage: rivertile [options]