ztmux 3.7.20

A Rust port of tmux — the full terminal multiplexer, server and client
Documentation
// Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use crate::compat::queue::tailq_foreach;
use crate::*;
use crate::options_::*;

pub static CMD_SET_OPTION_ENTRY: cmd_entry = cmd_entry {
    name: "set-option",
    alias: Some("set"),

    args: args_parse::new("aFgopqst:uUw", 1, 2, Some(cmd_set_option_args_parse)),
    usage: "[-aFgopqsuUw] [-t target-pane] option [value]",

    target: cmd_entry_flag::new(
        b't',
        cmd_find_type::CMD_FIND_PANE,
        cmd_find_flags::CMD_FIND_CANFAIL,
    ),

    flags: cmd_flag::CMD_AFTERHOOK,
    exec: cmd_set_option_exec,
    source: cmd_entry_flag::zeroed(),
};

pub static CMD_SET_WINDOW_OPTION_ENTRY: cmd_entry = cmd_entry {
    name: "set-window-option",
    alias: Some("setw"),

    args: args_parse::new("aFgoqt:u", 1, 2, Some(cmd_set_option_args_parse)),
    usage: "[-aFgoqu] [-t target-window] option [value]",

    target: cmd_entry_flag::new(
        b't',
        cmd_find_type::CMD_FIND_WINDOW,
        cmd_find_flags::CMD_FIND_CANFAIL,
    ),

    flags: cmd_flag::CMD_AFTERHOOK,
    exec: cmd_set_option_exec,
    source: cmd_entry_flag::zeroed(),
};

pub static CMD_SET_HOOK_ENTRY: cmd_entry = cmd_entry {
    name: "set-hook",
    alias: None,

    args: args_parse::new("agpRt:uw", 1, 2, Some(cmd_set_option_args_parse)),
    usage: "[-agpRuw] [-t target-pane] hook [command]",

    target: cmd_entry_flag::new(
        b't',
        cmd_find_type::CMD_FIND_PANE,
        cmd_find_flags::CMD_FIND_CANFAIL,
    ),

    flags: cmd_flag::CMD_AFTERHOOK,
    exec: cmd_set_option_exec,
    source: cmd_entry_flag::zeroed(),
};

/// C `vendor/tmux/cmd-set-option.c:75`: `static enum args_parse_type cmd_set_option_args_parse(__unused struct args *args, u_int idx, __unused char **cause)`
pub fn cmd_set_option_args_parse(
    _args: *mut args,
    idx: u32,
    _cause: *mut *mut u8,
) -> args_parse_type {
    match idx {
        1 => args_parse_type::ARGS_PARSE_COMMANDS_OR_STRING,
        _ => args_parse_type::ARGS_PARSE_STRING,
    }
}

/// C `vendor/tmux/cmd-set-option.c:84`: `static enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)`
pub unsafe fn cmd_set_option_exec(self_: *mut cmd, item: *mut cmdq_item) -> cmd_retval {
    unsafe {
        let args = cmd_get_args(self_);
        let append = args_has(args, 'a');
        let target = cmdq_get_target(item);
        let mut oo: *mut options = null_mut();
        let parent: *mut options_entry;
        let mut o: *mut options_entry;
        let argument: *mut u8;
        let mut expanded: *mut u8 = null_mut();
        let mut value: *const u8;
        let mut idx: i32 = 0;
        let already: i32;
        let mut ambiguous: i32 = 0;
        let scope: i32;

        'fail: {
            'out: {
                let window =
                    std::ptr::eq(cmd_get_entry(self_), &CMD_SET_WINDOW_OPTION_ENTRY) as i32;

                // Expand argument.
                argument = format_single_from_target(item, args_string(args, 0));

                // If set-hook -R, fire the hook straight away.
                if std::ptr::eq(cmd_get_entry(self_), &CMD_SET_HOOK_ENTRY) && args_has(args, 'R') {
                    notify_hook(item, argument);
                    free_(argument);
                    return cmd_retval::CMD_RETURN_NORMAL;
                }

                // Parse option name and index.
                let Some(name) = options_match(cstr_to_str(argument), &raw mut idx, &raw mut ambiguous) else {
                    if args_has(args, 'q') {
                        break 'out;
                    }
                    if ambiguous != 0 {
                        cmdq_error!(item, "ambiguous option: {}", _s(argument));
                    } else {
                        cmdq_error!(item, "invalid option: {}", _s(argument));
                    }
                    break 'fail;
                };
                if args_count(args) < 2 {
                    value = null_mut();
                } else {
                    value = args_string(args, 1);
                }
                if !value.is_null() && args_has(args, 'F') {
                    expanded = format_single_from_target(item, value);
                    value = expanded;
                }

                let mut cause = null_mut();
                // Get the scope and table for the option .
                scope = options_scope_from_name(
                    args,
                    window,
                    &name,
                    target,
                    &raw mut oo,
                    &raw mut cause,
                );
                if scope == OPTIONS_TABLE_NONE {
                    if args_has(args, 'q') {
                        break 'out;
                    }
                    cmdq_error!(item, "{}", _s(cause));
                    free_(cause);
                    break 'fail;
                }
                o = options_get_only(oo, &name);
                parent = options_get(&mut *oo, &name);

                // Check that array options and indexes match up.
                if idx != -1 && (name.starts_with('@') || !options_is_array(parent)) {
                    cmdq_error!(item, "not an array: {}", _s(argument));
                    break 'fail;
                }

                // With -o, check this option is not already set.
                if !args_has(args, 'u') && args_has(args, 'o') {
                    if idx == -1 {
                        already = !o.is_null() as i32;
                    } else if o.is_null() {
                        already = 0;
                    } else {
                        already = (!options_array_get(o, idx as u32).is_null()) as i32;
                    }
                    if already != 0 {
                        if args_has(args, 'q') {
                            break 'out;
                        }
                        cmdq_error!(item, "already set: {}", _s(argument));
                        break 'fail;
                    }
                }

                // Change the option.
                if args_has(args, 'U') && scope == OPTIONS_TABLE_WINDOW {
                    for loop_ in tailq_foreach::<_, discr_entry>(&raw mut (*(*target).w).panes)
                        .map(NonNull::as_ptr)
                    {
                        let po = options_get_only((*loop_).options, &name);
                        if po.is_null() {
                            continue;
                        }
                        if let Err(cause) = options_remove_or_default(po, idx) {
                            cmdq_error!(item, "{}", cause.to_str().unwrap());
                            break 'fail;
                        }
                    }
                }
                if args_has(args, 'u') || args_has(args, 'U') {
                    if o.is_null() {
                        break 'out;
                    }
                    if let Err(cause) = options_remove_or_default(o, idx) {
                        cmdq_error!(item, "{}", cause.to_str().unwrap());
                        break 'fail;
                    }
                } else if name.starts_with('@') {
                    if value.is_null() {
                        cmdq_error!(item, "empty value");
                        break 'fail;
                    }
                    options_set_string!(oo, &name, append, "{}", _s(value));
                } else if idx == -1 && !options_is_array(parent) {
                    if let Err(cause) = options_from_string(
                        oo,
                        options_table_entry(parent),
                        (*options_table_entry(parent)).name,
                        value,
                        args_has(args, 'a'),
                    ) {
                        cmdq_error!(item, "{}", cause.to_str().unwrap());
                        break 'fail;
                    }
                } else {
                    if value.is_null() {
                        cmdq_error!(item, "empty value");
                        break 'fail;
                    }
                    if o.is_null() {
                        o = options_empty(oo, options_table_entry(parent));
                    }
                    if idx == -1 {
                        if !append {
                            options_array_clear(o);
                        }
                        if let Err(cause) = options_array_assign(o, cstr_to_str(value)) {
                            cmdq_error!(item, "{}", cause.to_str().unwrap());
                            break 'fail;
                        }
                    } else if let Err(cause) =
                        options_array_set(o, idx as u32, Some(cstr_to_str(value)), append)
                    {
                        cmdq_error!(item, "{}", cause.to_str().unwrap());
                        break 'fail;
                    }
                }

                options_push_changes(&name);

                // ztmux: toggling zellij mode changes pane sizes (the frame
                // inset), so relayout every window immediately - the window size
                // is unchanged, so recalculate_sizes() alone would not re-fix it.
                // @ztmux-ratatui gates the whole ratatui renderer (and, via the
                // inset, pane sizes too), so it relayouts on the same path.
                if name == "@ztmux-zellij-mode"
                    || name == "@ztmux-pane-names"
                    || name == "@ztmux-ratatui"
                {
                    for w in rb_foreach(&raw mut WINDOWS).map(NonNull::as_ptr) {
                        layout_fix_panes(w, null_mut());
                        server_redraw_window(w);
                    }
                }
            }
            // out:
            free_(argument);
            free_(expanded);
            // free_(name);
            return cmd_retval::CMD_RETURN_NORMAL;
        }
        // fail:
        free_(argument);
        free_(expanded);
        // free_(name);
        cmd_retval::CMD_RETURN_ERROR
    }
}