How do I handle ansi escape sequences from a file?

142 views Asked by At

I'm writing my own shell from scratch (shark - shell of ark). Ark is just my initials.

Most of things are really good working, e.c. arrows / history / command execution, input buffer timeout with VTIME and VMIN to handle ascii esc sequences.

Most of the time I use a simple prompt hardcoded, just to have one, but with color:

&tty.stdout.write(b"\x1b[1;32m[ shark ]\x1b[0m $");

And this works well, but I want to get my environment from the rc file, .sharkrc.

In this file I've the simple code:

PS1='\x1b[1;32m[ shark ]\x1b[0m $ '

At the start of the shell the rc file gets read and parsed and then the prompt looks like:

\x1b[1;32m[ shark ]\x1b[0m $

No colors, and I know what the problem is actually: it is a pure string and will printed as a string, even it is: prompt.as_bytes(),

There is no \x1b[.. sequence, just the chars \ then x then 1 then b and so on.

But here is the problem, I want to do this from scratch so I need a little help, to handle the ansi esc codes when the strings come from a file.

My last idea was to use regex and seek all the ansi codes out of a string and then write it separate.

But is there a simpler method?

1

There are 1 answers

8
Erich Zann On

I solved it by myself, the code is still bit messie, but it was from try and error. Now it works and i know how tho handle ANSI EscCodes.

Here the code:

pub fn parse_from_str( text: &str, tty: &Terminal ) {

    let mut ansi_code    = false;
    let mut ansi_blk     : Vec<char> = Vec::new();  
    let mut non_ansi_blk : Vec<char> = Vec::new();

    let ansi_scan : [char; 4 ] = [ '\\', 'x', 'b', 'm' ];

    for (index, byte) in text.chars().enumerate() {

        if byte == '\\' {
            ansi_code = true;
            //println!("\rfound \\");
            if !non_ansi_blk.is_empty() {
                write!(&tty.stdout, "{}", non_ansi_blk.iter().collect::<String>());
                non_ansi_blk.clear();
            }

        } else if ansi_code == true && !ansi_scan.contains(&byte)  {

            if text.as_bytes()[index - 1] == b'x' { continue; }

            //println!("\rPush ANSI: {}", byte);
            ansi_blk.push(byte);
        
        } else if ansi_code == true && byte == 'm' {
            ansi_code = false;
        
            write!(&tty.stdout, "\x1b{}m", ansi_blk.iter().collect::<String>());
            ansi_blk.clear();

        } else if ansi_code == false  {
            //println!("\rPush non ANSI: {}", byte);
            non_ansi_blk.push(byte);
        }
    }

    if !non_ansi_blk.is_empty() {
        write!(&tty.stdout, "{}", non_ansi_blk.iter().collect::<String>());
        non_ansi_blk.clear();
    }
}