// launches a shell that displays text in ROT13. written by ChatGPT.
// gcc -O2 -Wall -Wextra -Wpedantic -std=c99 rot13.c -o rot13 && ./rot13

#define _XOPEN_SOURCE 700
#include <unistd.h>
#include <pty.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <termios.h>
#include <sys/ioctl.h>

static char rot13(char c) {
    if (c >= 'a' && c <= 'z') return (c - 'a' + 13) % 26 + 'a';
    if (c >= 'A' && c <= 'Z') return (c - 'A' + 13) % 26 + 'A';
    return c;
}

enum { NORMAL, ESCAPE, CSI, OSC };

int main() {
    int m;
    pid_t pid = forkpty(&m, NULL, NULL, NULL);

    if (pid == 0) {
        execlp("sh", "sh", NULL);
        _exit(1);
    }

    struct termios t;
    tcgetattr(0, &t);
    t.c_lflag &= ~(ECHO | ECHONL);
    t.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    t.c_oflag &= ~(OPOST);
    t.c_cflag |= (CS8);
    t.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
    t.c_cc[VMIN] = 1;
    t.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &t);

    struct winsize last_ws = {0};

    fd_set fds;
    char c;
    int state = NORMAL;

    while (1) {
        FD_ZERO(&fds);
        FD_SET(0, &fds);
        FD_SET(m, &fds);

        select(m + 1, &fds, NULL, NULL, NULL);

        struct winsize ws;
        if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
            if (ws.ws_row != last_ws.ws_row || ws.ws_col != last_ws.ws_col) {
                ioctl(m, TIOCSWINSZ, &ws);
                last_ws = ws;
            }
        }

        if (FD_ISSET(0, &fds)) {
            if (read(0, &c, 1) > 0)
                write(m, &c, 1);
        }

        if (FD_ISSET(m, &fds)) {
            if (read(m, &c, 1) <= 0) break;

            switch (state) {

                case NORMAL:
                    if (c == 0x1b) {
                        state = ESCAPE;
                        write(1, &c, 1);
                    } else {
                        c = rot13(c);
                        write(1, &c, 1);
                    }
                    break;

                case ESCAPE:
                    write(1, &c, 1);
                    if (c == '[') state = CSI;
                    else if (c == ']') state = OSC;
                    else state = NORMAL;
                    break;

                case CSI:
                    write(1, &c, 1);
                    if (c >= '@' && c <= '~')
                        state = NORMAL;
                    break;

                case OSC:
                    write(1, &c, 1);
                    if (c == 0x07 || (c == 0x1b))
                        state = NORMAL;
                    break;
            }
        }
    }

    wait(NULL);
}
