ztmux 3.7.0

A Rust port of tmux — the full terminal multiplexer, server and client
Documentation
// Copyright (c) 1989, 1993
// The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the University nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.

// generated using c2rust on unvis.c
// TODO refactor

#[expect(
    clippy::char_lit_as_u8,
    reason = "TODO investigate, originally generated by c2rust"
)]
/// C `vendor/tmux/compat/unvis.c:53`: `int unvis(char *cp, char c, int *astate, int flag)`
pub unsafe fn unvis(cp: *mut u8, c: u8, astate: *mut i32, flag: i32) -> i32 {
    unsafe {
        if flag & 1 != 0 {
            if *astate == 5 || *astate == 6 {
                *astate = 0;
                return 1;
            }
            return if *astate == 0 { 3 } else { -1 };
        }
        match *astate {
            0 => {
                *cp = 0;
                if c == b'\\' {
                    *astate = 1;
                    return 0;
                }
                *cp = c;
                1
            }
            1 => {
                match c as libc::c_int {
                    92 => {
                        *cp = c;
                        *astate = 0;
                        return 1;
                    }
                    48..=55 => {
                        *cp = c - b'0';
                        *astate = 5;
                        return 0;
                    }
                    77 => {
                        *cp = 0o200i32 as u8;
                        *astate = 2;
                        return 0;
                    }
                    94 => {
                        *astate = 4;
                        return 0;
                    }
                    110 => {
                        *cp = b'\n';
                        *astate = 0;
                        return 1;
                    }
                    114 => {
                        *cp = b'\r';
                        *astate = 0;
                        return 1;
                    }
                    98 => {
                        *cp = '\u{8}' as u8;
                        *astate = 0;
                        return 1;
                    }
                    97 => {
                        *cp = '\u{7}' as u8;
                        *astate = 0;
                        return 1;
                    }
                    118 => {
                        *cp = '\u{b}' as u8;
                        *astate = 0;
                        return 1;
                    }
                    116 => {
                        *cp = b'\t';
                        *astate = 0;
                        return 1;
                    }
                    102 => {
                        *cp = '\u{c}' as u8;
                        *astate = 0;
                        return 1;
                    }
                    115 => {
                        *cp = b' ';
                        *astate = 0;
                        return 1;
                    }
                    69 => {
                        *cp = '\u{1b}' as u8;
                        *astate = 0;
                        return 1;
                    }
                    10 | 36 => {
                        *astate = 0;
                        return 3;
                    }
                    _ => {}
                }
                *astate = 0;
                -1
            }
            2 => {
                if c == b'-' {
                    *astate = 3;
                } else if c == b'^' {
                    *astate = 4;
                } else {
                    *astate = 0;
                    return -1;
                }
                0
            }
            3 => {
                *astate = 0;
                *cp |= c;
                1
            }
            4 => {
                if c == b'?' {
                    *cp = (*cp as libc::c_int | 0o177 as libc::c_int) as u8;
                } else {
                    *cp = (*cp as libc::c_int | c as libc::c_int & 0o37 as libc::c_int) as u8;
                }
                *astate = 0;
                1
            }
            5 => {
                if (b'0'..=b'7').contains(&c) {
                    *cp = (((*cp as libc::c_int) << 3 as libc::c_int) + (c - b'0') as i32) as u8;
                    *astate = 6;
                    return 0;
                }
                *astate = 0;
                2
            }
            6 => {
                *astate = 0 as libc::c_int;
                if (b'0'..=b'7').contains(&c) {
                    *cp = (((*cp as libc::c_int) << 3 as libc::c_int) + (c - b'0') as i32) as u8;
                    return 1;
                }
                2
            }
            _ => {
                *astate = 0;
                -1
            }
        }
    }
}

/// C `vendor/tmux/compat/unvis.c:212`: `int strunvis(char *dst, const char *src)`
pub unsafe fn strunvis(mut dst: *mut u8, mut src: *const u8) -> i32 {
    unsafe {
        let mut c: u8;
        let start: *mut u8 = dst;
        let mut state: i32 = 0;
        loop {
            let fresh0 = src;
            src = src.add(1);
            c = *fresh0;
            if c == 0 {
                break;
            }
            loop {
                match unvis(dst, c, &mut state, 0 as libc::c_int) {
                    1 => {
                        dst = dst.add(1);
                        break;
                    }
                    2 => {
                        dst = dst.add(1);
                    }
                    0 | 3 => {
                        break;
                    }
                    _ => {
                        *dst = b'\0';
                        return -1;
                    }
                }
            }
        }
        if unvis(dst, c, &mut state, 1 as libc::c_int) == 1 as libc::c_int {
            dst = dst.add(1);
        }
        *dst = b'\0';
        dst.offset_from(start) as i32
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_strunvis_literals() {
        // Plain bytes pass through unchanged.
        let src = crate::c!("abc");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 3);
        assert_eq!(&dst[..4], b"abc\0");
    }

    #[test]
    fn test_strunvis_escaped_tab() {
        // "\t" (backslash-t) decodes to a single 0x09 byte.
        let src = crate::c!("\\t");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 1);
        assert_eq!(dst[0], b'\t');
        assert_eq!(dst[1], b'\0');
    }

    #[test]
    fn test_strunvis_escaped_newline() {
        // "\n" (backslash-n) decodes to 0x0a.
        let src = crate::c!("a\\nb");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 3);
        assert_eq!(&dst[..4], b"a\nb\0");
    }

    #[test]
    fn test_strunvis_octal() {
        // "\101" octal 101 == 0x41 == 'A'.
        let src = crate::c!("\\101");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 1);
        assert_eq!(dst[0], b'A');
        assert_eq!(dst[1], b'\0');
    }

    #[test]
    fn test_strunvis_escaped_backslash() {
        // "\\" (backslash-backslash) decodes to a single backslash.
        let src = crate::c!("\\\\");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 1);
        assert_eq!(dst[0], b'\\');
        assert_eq!(dst[1], b'\0');
    }

    #[test]
    fn test_strunvis_mixed() {
        // Mix of literals, a named escape, and an octal escape.
        // "a\tbAc": 'a', TAB, 'b', 'A' (from \101), 'c'.
        let src = crate::c!("a\\tb\\101c");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 5);
        assert_eq!(&dst[..6], b"a\tbAc\0");
    }

    #[test]
    fn test_strunvis_named_escapes() {
        // Every remaining S_START named escape (unvis.c:97-128):
        // \r \b \a \v \f \s \E -> CR BS BEL VT FF SP ESC.
        let src = crate::c!("\\r\\b\\a\\v\\f\\s\\E");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 7);
        assert_eq!(&dst[..8], &[0x0d, 0x08, 0x07, 0x0b, 0x0c, 0x20, 0x1b, 0x00]);
    }

    #[test]
    fn test_strunvis_octal_three_digit() {
        // "\377" -> 0xFF (three octal digits, unvis.c:169-193).
        let src = crate::c!("\\377");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 1);
        assert_eq!(dst[0], 0xFF);
        assert_eq!(dst[1], b'\0');
    }

    #[test]
    fn test_strunvis_octal_flushed_at_end() {
        // A one-digit octal escape at end of input is flushed by the UNVIS_END
        // pass (unvis.c:56-59, S_OCTAL2 -> UNVIS_VALID).
        let src = crate::c!("\\7");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 1);
        assert_eq!(dst[0], 0x07);
    }

    #[test]
    fn test_strunvis_octal_pushback() {
        // "\77x": two octal digits complete the byte (0o77 = 0x3F), then the
        // non-octal 'x' is pushed back (UNVIS_VALIDPUSH, unvis.c:182) and
        // reprocessed as a literal.
        let src = crate::c!("\\77x");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 2);
        assert_eq!(&dst[..3], &[0x3F, b'x', 0x00]);
    }

    #[test]
    fn test_strunvis_meta() {
        // Meta forms set bit 0x80 (unvis.c:86-89, 145-159).
        // \M-A -> 0x80 | 'A' = 0xC1 ; \M^A -> 0x80 | ('A' & 0o37) = 0x81.
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), crate::c!("\\M-A")) };
        assert_eq!(ret, 1);
        assert_eq!(dst[0], 0xC1);

        let mut dst2 = [0u8; 16];
        let ret2 = unsafe { strunvis(dst2.as_mut_ptr(), crate::c!("\\M^A")) };
        assert_eq!(ret2, 1);
        assert_eq!(dst2[0], 0x81);
    }

    #[test]
    fn test_strunvis_control() {
        // \^A -> control-A (0x01); \^? -> DEL (0x7F). (unvis.c:161-167)
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), crate::c!("\\^A")) };
        assert_eq!(ret, 1);
        assert_eq!(dst[0], 0x01);

        let mut dst2 = [0u8; 16];
        let ret2 = unsafe { strunvis(dst2.as_mut_ptr(), crate::c!("\\^?")) };
        assert_eq!(ret2, 1);
        assert_eq!(dst2[0], 0x7F);
    }

    #[test]
    fn test_strunvis_hidden_markers() {
        // "\<newline>" and "\$" are hidden markers producing no output byte
        // (UNVIS_NOCHAR, unvis.c:129-140).
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), crate::c!("a\\\nb")) };
        assert_eq!(ret, 2);
        assert_eq!(&dst[..3], b"ab\0");

        let mut dst2 = [0u8; 16];
        let ret2 = unsafe { strunvis(dst2.as_mut_ptr(), crate::c!("a\\$b")) };
        assert_eq!(ret2, 2);
        assert_eq!(&dst2[..3], b"ab\0");
    }

    #[test]
    fn test_strunvis_invalid_escape() {
        // "\z" is not a recognized escape -> S_START default -> UNVIS_SYNBAD,
        // and strunvis reports the error as -1 (unvis.c:142-143, 230-232).
        let src = crate::c!("\\z");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, -1);
    }

    #[test]
    fn test_strunvis_trailing_backslash() {
        // A dangling backslash leaves the machine in S_START; the UNVIS_END
        // pass returns UNVIS_SYNBAD (not VALID), so no extra byte is emitted
        // and the preceding literal is the only output.
        let src = crate::c!("a\\");
        let mut dst = [0u8; 16];
        let ret = unsafe { strunvis(dst.as_mut_ptr(), src) };
        assert_eq!(ret, 1);
        assert_eq!(&dst[..2], b"a\0");
    }
}