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

commit4096751e1cb7a4d045f607d0ac213031531b2bed
parent7c88e35f69
authorIsaac Freund <[email protected]>
date2021-07-15 00:13
command: make args type 0-terminated

Since we often need to pass these args back C code, keeping the 0 byte
around saves some allocations.

 river/Control.zig                      |  4 ++--
 river/Mapping.zig                      |  6 +++---
 river/command.zig                      |  8 ++++----
 river/command/attach_mode.zig          |  2 +-
 river/command/close.zig                |  2 +-
 river/command/config.zig               | 10 +++++-----
 river/command/declare_mode.zig         |  2 +-
 river/command/enter_mode.zig           |  2 +-
 river/command/exit.zig                 |  2 +-
 river/command/filter.zig               |  8 ++++----
 river/command/focus_follows_cursor.zig |  2 +-
 river/command/focus_view.zig           |  2 +-
 river/command/input.zig                | 10 ++++------
 river/command/layout.zig               |  8 ++++----
 river/command/map.zig                  | 28 ++++++++++++----------------
 river/command/move.zig                 |  6 +++---
 river/command/opacity.zig              |  2 +-
 river/command/output.zig               |  4 ++--
 river/command/set_repeat.zig           |  2 +-
 river/command/spawn.zig                |  8 +++-----
 river/command/swap.zig                 |  2 +-
 river/command/tags.zig                 | 12 ++++++------
 river/command/toggle_float.zig         |  2 +-
 river/command/toggle_fullscreen.zig    |  2 +-
 river/command/xcursor_theme.zig        |  6 ++----
 river/command/zoom.zig                 |  2 +-
 26 files changed, 67 insertions(+), 77 deletions(-)

diff --git a/river/Control.zig b/river/Control.zig
index ff7ca3f..0865fac 100644
--- a/river/Control.zig
+++ b/river/Control.zig
@@ -31,7 +31,7 @@ const util = @import("util.zig");
 const Seat = @import("Seat.zig");
 const Server = @import("Server.zig");
 
-const ArgMap = std.AutoHashMap(struct { client: *wl.Client, id: u32 }, std.ArrayListUnmanaged([]const u8));
+const ArgMap = std.AutoHashMap(struct { client: *wl.Client, id: u32 }, std.ArrayListUnmanaged([:0]const u8));
 
 global: *wl.Global,
 
@@ -72,7 +72,7 @@ fn handleRequest(control: *zriver.ControlV1, request: zriver.ControlV1.Request,
     switch (request) {
         .destroy => control.destroy(),
         .add_argument => |add_argument| {
-            const owned_slice = mem.dupe(util.gpa, u8, mem.span(add_argument.argument)) catch {
+            const owned_slice = util.gpa.dupeZ(u8, mem.span(add_argument.argument)) catch {
                 control.getClient().postNoMemory();
                 return;
             };
diff --git a/river/Mapping.zig b/river/Mapping.zig
index 03a9b45..280c79f 100644
--- a/river/Mapping.zig
+++ b/river/Mapping.zig
@@ -25,7 +25,7 @@ const util = @import("util.zig");
 
 keysym: xkb.Keysym,
 modifiers: wlr.Keyboard.ModifierMask,
-command_args: []const []const u8,
+command_args: []const [:0]const u8,
 
 /// When set to true the mapping will be executed on key release rather than on press
 release: bool,
@@ -36,11 +36,11 @@ pub fn init(
     release: bool,
     command_args: []const []const u8,
 ) !Self {
-    const owned_args = try util.gpa.alloc([]u8, command_args.len);
+    const owned_args = try util.gpa.alloc([:0]u8, command_args.len);
     errdefer util.gpa.free(owned_args);
     for (command_args) |arg, i| {
         errdefer for (owned_args[0..i]) |a| util.gpa.free(a);
-        owned_args[i] = try std.mem.dupe(util.gpa, u8, arg);
+        owned_args[i] = try std.mem.dupeZ(util.gpa, u8, arg);
     }
     return Self{
         .keysym = keysym,
diff --git a/river/command.zig b/river/command.zig
index e033b00..b4f1097 100644
--- a/river/command.zig
+++ b/river/command.zig
@@ -40,7 +40,7 @@ pub const Orientation = enum {
 // zig fmt: off
 const str_to_impl_fn = [_]struct {
     name: []const u8,
-    impl: fn (*std.mem.Allocator, *Seat, []const []const u8, *?[]const u8) Error!void,
+    impl: fn (*std.mem.Allocator, *Seat, []const [:0]const u8, *?[]const u8) Error!void,
 }{
     .{ .name = "attach-mode",            .impl = @import("command/attach_mode.zig").attachMode },
     .{ .name = "background-color",       .impl = @import("command/config.zig").backgroundColor },
@@ -99,9 +99,9 @@ pub const Error = error{
     InvalidButton,
     InvalidCharacter,
     InvalidDirection,
+    InvalidType,
     InvalidPhysicalDirection,
     InvalidOrientation,
-    InvalidType,
     InvalidRgba,
     InvalidValue,
     UnknownOption,
@@ -119,7 +119,7 @@ pub const Error = error{
 pub fn run(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     std.debug.assert(out.* == null);
@@ -144,9 +144,9 @@ pub fn errToMsg(err: Error) [:0]const u8 {
         Error.InvalidButton => "invalid button",
         Error.InvalidCharacter => "invalid character in argument",
         Error.InvalidDirection => "invalid direction. Must be 'next' or 'previous'",
+        Error.InvalidType => "invalid type",
         Error.InvalidPhysicalDirection => "invalid direction. Must be 'up', 'down', 'left' or 'right'",
         Error.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'",
-        Error.InvalidType => "invalid type",
         Error.InvalidRgba => "invalid color format, must be #RRGGBB or #RRGGBBAA",
         Error.InvalidValue => "invalid value",
         Error.OutOfMemory => "out of memory",
diff --git a/river/command/attach_mode.zig b/river/command/attach_mode.zig
index f9505b4..8fc0dbb 100644
--- a/river/command/attach_mode.zig
+++ b/river/command/attach_mode.zig
@@ -27,7 +27,7 @@ const Seat = @import("../Seat.zig");
 pub fn attachMode(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/close.zig b/river/command/close.zig
index 340fc0d..ff527a3 100644
--- a/river/command/close.zig
+++ b/river/command/close.zig
@@ -24,7 +24,7 @@ const Seat = @import("../Seat.zig");
 pub fn close(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     // Note: we don't call arrange() here as it will be called
diff --git a/river/command/config.zig b/river/command/config.zig
index 80539d4..0d93002 100644
--- a/river/command/config.zig
+++ b/river/command/config.zig
@@ -26,7 +26,7 @@ const Config = @import("../Config.zig");
 pub fn borderWidth(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -40,7 +40,7 @@ pub fn borderWidth(
 pub fn backgroundColor(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -55,7 +55,7 @@ pub fn backgroundColor(
 pub fn borderColorFocused(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -70,7 +70,7 @@ pub fn borderColorFocused(
 pub fn borderColorUnfocused(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -85,7 +85,7 @@ pub fn borderColorUnfocused(
 pub fn setCursorWarp(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/declare_mode.zig b/river/command/declare_mode.zig
index a889bdc..964d97c 100644
--- a/river/command/declare_mode.zig
+++ b/river/command/declare_mode.zig
@@ -29,7 +29,7 @@ const Seat = @import("../Seat.zig");
 pub fn declareMode(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/enter_mode.zig b/river/command/enter_mode.zig
index fe279f2..00f1bcd 100644
--- a/river/command/enter_mode.zig
+++ b/river/command/enter_mode.zig
@@ -26,7 +26,7 @@ const Seat = @import("../Seat.zig");
 pub fn enterMode(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/exit.zig b/river/command/exit.zig
index eb0d74d..6c694f4 100644
--- a/river/command/exit.zig
+++ b/river/command/exit.zig
@@ -26,7 +26,7 @@ const Seat = @import("../Seat.zig");
 pub fn exit(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len > 1) return Error.TooManyArguments;
diff --git a/river/command/filter.zig b/river/command/filter.zig
index 911eb09..4886aed 100644
--- a/river/command/filter.zig
+++ b/river/command/filter.zig
@@ -30,7 +30,7 @@ const Seat = @import("../Seat.zig");
 pub fn floatFilterAdd(
     allocator: *mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -45,7 +45,7 @@ pub fn floatFilterAdd(
 pub fn floatFilterRemove(
     allocator: *mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -57,7 +57,7 @@ pub fn floatFilterRemove(
 pub fn csdFilterAdd(
     allocator: *mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -74,7 +74,7 @@ pub fn csdFilterAdd(
 pub fn csdFilterRemove(
     allocator: *mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/focus_follows_cursor.zig b/river/command/focus_follows_cursor.zig
index fbc1500..c8494ac 100644
--- a/river/command/focus_follows_cursor.zig
+++ b/river/command/focus_follows_cursor.zig
@@ -27,7 +27,7 @@ const Seat = @import("../Seat.zig");
 pub fn focusFollowsCursor(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/focus_view.zig b/river/command/focus_view.zig
index 984df82..140bdda 100644
--- a/river/command/focus_view.zig
+++ b/river/command/focus_view.zig
@@ -30,7 +30,7 @@ const ViewStack = @import("../view_stack.zig").ViewStack;
 pub fn focusView(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/input.zig b/river/command/input.zig
index 5869ffe..6aa91bf 100644
--- a/river/command/input.zig
+++ b/river/command/input.zig
@@ -30,7 +30,7 @@ const InputManager = @import("../InputManager.zig");
 pub fn listInputs(
     allocator: *mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     var input_list = std.ArrayList(u8).init(allocator);
@@ -61,7 +61,7 @@ pub fn listInputs(
 pub fn listInputConfigs(
     allocator: *mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     var input_list = std.ArrayList(u8).init(allocator);
@@ -124,7 +124,7 @@ pub fn listInputConfigs(
 pub fn input(
     allocator: *mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 4) return Error.NotEnoughArguments;
@@ -197,9 +197,7 @@ pub fn input(
         input_config.scroll_method = std.meta.stringToEnum(InputConfig.ScrollMethod, args[3]) orelse
             return Error.UnknownOption;
     } else if (mem.eql(u8, "scroll-button", args[2])) {
-        const event_code_name = try std.cstr.addNullByte(allocator, args[3]);
-        defer allocator.free(event_code_name);
-        const ret = c.libevdev_event_code_from_name(c.EV_KEY, event_code_name);
+        const ret = c.libevdev_event_code_from_name(c.EV_KEY, args[3]);
         if (ret < 1) return Error.InvalidButton;
         input_config.scroll_button = InputConfig.ScrollButton{ .button = @intCast(u32, ret) };
     } else {
diff --git a/river/command/layout.zig b/river/command/layout.zig
index 710b59a..5fcf432 100644
--- a/river/command/layout.zig
+++ b/river/command/layout.zig
@@ -28,7 +28,7 @@ const Seat = @import("../Seat.zig");
 pub fn outputLayout(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -42,7 +42,7 @@ pub fn outputLayout(
 pub fn defaultLayout(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -68,7 +68,7 @@ const SetType = enum {
 pub fn setLayoutValue(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 5) return Error.NotEnoughArguments;
@@ -117,7 +117,7 @@ const ModType = enum {
 pub fn modLayoutValue(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 5) return Error.NotEnoughArguments;
diff --git a/river/command/map.zig b/river/command/map.zig
index ab8a53a..96e1d25 100644
--- a/river/command/map.zig
+++ b/river/command/map.zig
@@ -36,7 +36,7 @@ const Seat = @import("../Seat.zig");
 pub fn map(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     const optionals = parseOptionalArgs(args[1..]);
@@ -68,7 +68,7 @@ pub fn map(
 pub fn mapPointer(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 5) return Error.NotEnoughArguments;
@@ -148,24 +148,20 @@ fn pointerMappingExists(
     return null;
 }
 
-fn parseEventCode(allocator: *std.mem.Allocator, event_code_str: []const u8, out: *?[]const u8) !u32 {
-    const event_code_name = try std.cstr.addNullByte(allocator, event_code_str);
-    defer allocator.free(event_code_name);
-    const ret = c.libevdev_event_code_from_name(c.EV_KEY, event_code_name);
-    if (ret < 1) {
-        out.* = try std.fmt.allocPrint(allocator, "unknown button {s}", .{event_code_str});
+fn parseEventCode(allocator: *std.mem.Allocator, name: [:0]const u8, out: *?[]const u8) !u32 {
+    const event_code = c.libevdev_event_code_from_name(c.EV_KEY, name);
+    if (event_code < 1) {
+        out.* = try std.fmt.allocPrint(allocator, "unknown button {s}", .{name});
         return Error.Other;
     }
 
-    return @intCast(u32, ret);
+    return @intCast(u32, event_code);
 }
 
-fn parseKeysym(allocator: *std.mem.Allocator, keysym_str: []const u8, out: *?[]const u8) !xkb.Keysym {
-    const keysym_name = try std.cstr.addNullByte(allocator, keysym_str);
-    defer allocator.free(keysym_name);
-    const keysym = xkb.Keysym.fromName(keysym_name, .case_insensitive);
+fn parseKeysym(allocator: *std.mem.Allocator, name: [:0]const u8, out: *?[]const u8) !xkb.Keysym {
+    const keysym = xkb.Keysym.fromName(name, .case_insensitive);
     if (keysym == .NoSymbol) {
-        out.* = try std.fmt.allocPrint(allocator, "invalid keysym '{s}'", .{keysym_str});
+        out.* = try std.fmt.allocPrint(allocator, "invalid keysym '{s}'", .{name});
         return Error.Other;
     }
     return keysym;
@@ -240,7 +236,7 @@ fn parseOptionalArgs(args: []const []const u8) OptionalArgsContainer {
 pub fn unmap(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     const optionals = parseOptionalArgs(args[1..]);
@@ -266,7 +262,7 @@ pub fn unmap(
 pub fn unmapPointer(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 4) return Error.NotEnoughArguments;
diff --git a/river/command/move.zig b/river/command/move.zig
index cb8e771..5ed11f4 100644
--- a/river/command/move.zig
+++ b/river/command/move.zig
@@ -29,7 +29,7 @@ const Box = @import("../Box.zig");
 pub fn move(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
@@ -53,7 +53,7 @@ pub fn move(
 pub fn snap(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -80,7 +80,7 @@ pub fn snap(
 pub fn resize(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
diff --git a/river/command/opacity.zig b/river/command/opacity.zig
index 92dd5c7..c4de30b 100644
--- a/river/command/opacity.zig
+++ b/river/command/opacity.zig
@@ -32,7 +32,7 @@ fn opacityUpdateFilter(view: *View, context: void) bool {
 pub fn opacity(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 6) return Error.NotEnoughArguments;
diff --git a/river/command/output.zig b/river/command/output.zig
index 5535b88..564c478 100644
--- a/river/command/output.zig
+++ b/river/command/output.zig
@@ -30,7 +30,7 @@ const Seat = @import("../Seat.zig");
 pub fn focusOutput(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
@@ -50,7 +50,7 @@ pub fn focusOutput(
 pub fn sendToOutput(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/set_repeat.zig b/river/command/set_repeat.zig
index a72ae66..e7e3519 100644
--- a/river/command/set_repeat.zig
+++ b/river/command/set_repeat.zig
@@ -26,7 +26,7 @@ const Seat = @import("../Seat.zig");
 pub fn setRepeat(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
diff --git a/river/command/spawn.zig b/river/command/spawn.zig
index 8daa303..26047c4 100644
--- a/river/command/spawn.zig
+++ b/river/command/spawn.zig
@@ -26,17 +26,15 @@ const Seat = @import("../Seat.zig");
 pub fn spawn(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
 
-    const cmd = try std.mem.join(allocator, " ", args[1..]);
+    const cmd = try std.mem.joinZ(allocator, " ", args[1..]);
     defer allocator.free(cmd);
-    const cmdZ = try std.cstr.addNullByte(allocator, cmd);
-    defer allocator.free(cmdZ);
 
-    const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmdZ, null };
+    const child_args = [_:null]?[*:0]const u8{ "/bin/sh", "-c", cmd, null };
 
     const pid = std.os.fork() catch {
         out.* = try std.fmt.allocPrint(allocator, "fork/execve failed", .{});
diff --git a/river/command/swap.zig b/river/command/swap.zig
index 99b2d6d..120bdef 100644
--- a/river/command/swap.zig
+++ b/river/command/swap.zig
@@ -29,7 +29,7 @@ const ViewStack = @import("../view_stack.zig").ViewStack;
 pub fn swap(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/tags.zig b/river/command/tags.zig
index d056299..308dc2a 100644
--- a/river/command/tags.zig
+++ b/river/command/tags.zig
@@ -26,7 +26,7 @@ const Seat = @import("../Seat.zig");
 pub fn setFocusedTags(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     const tags = try parseTags(allocator, args, out);
@@ -42,7 +42,7 @@ pub fn setFocusedTags(
 pub fn spawnTagmask(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     const tags = try parseTags(allocator, args, out);
@@ -53,7 +53,7 @@ pub fn spawnTagmask(
 pub fn setViewTags(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     const tags = try parseTags(allocator, args, out);
@@ -69,7 +69,7 @@ pub fn setViewTags(
 pub fn toggleFocusedTags(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     const tags = try parseTags(allocator, args, out);
@@ -87,7 +87,7 @@ pub fn toggleFocusedTags(
 pub fn toggleViewTags(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     const tags = try parseTags(allocator, args, out);
@@ -104,7 +104,7 @@ pub fn toggleViewTags(
 
 fn parseTags(
     allocator: *std.mem.Allocator,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!u32 {
     if (args.len < 2) return Error.NotEnoughArguments;
diff --git a/river/command/toggle_float.zig b/river/command/toggle_float.zig
index c6bc9eb..bdcc20d 100644
--- a/river/command/toggle_float.zig
+++ b/river/command/toggle_float.zig
@@ -27,7 +27,7 @@ const Seat = @import("../Seat.zig");
 pub fn toggleFloat(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len > 1) return Error.TooManyArguments;
diff --git a/river/command/toggle_fullscreen.zig b/river/command/toggle_fullscreen.zig
index f6a32d2..8e5ec62 100644
--- a/river/command/toggle_fullscreen.zig
+++ b/river/command/toggle_fullscreen.zig
@@ -27,7 +27,7 @@ const Seat = @import("../Seat.zig");
 pub fn toggleFullscreen(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len > 1) return Error.TooManyArguments;
diff --git a/river/command/xcursor_theme.zig b/river/command/xcursor_theme.zig
index cb8178f..b224990 100644
--- a/river/command/xcursor_theme.zig
+++ b/river/command/xcursor_theme.zig
@@ -23,15 +23,13 @@ const Seat = @import("../Seat.zig");
 pub fn xcursorTheme(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
 
-    // TODO: get rid of this allocation
-    const name = try std.cstr.addNullByte(allocator, args[1]);
-    defer allocator.free(name);
+    const name = args[1];
     const size = if (args.len == 3) try std.fmt.parseInt(u32, args[2], 10) else null;
 
     try seat.cursor.setTheme(name, size);
diff --git a/river/command/zoom.zig b/river/command/zoom.zig
index e5e2a49..6993820 100644
--- a/river/command/zoom.zig
+++ b/river/command/zoom.zig
@@ -29,7 +29,7 @@ const ViewStack = @import("../view_stack.zig").ViewStack;
 pub fn zoom(
     allocator: *std.mem.Allocator,
     seat: *Seat,
-    args: []const []const u8,
+    args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len > 1) return Error.TooManyArguments;