wip - lchat - A line oriented chat front end for ii.
 (HTM) git clone git://git.suckless.org/lchat
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit 98fa5e3861a515b0b9f7b212e7eb5abf550e1f33
 (DIR) parent b1db0ac6e9850a858120ac43596405208a3fc581
 (HTM) Author: Jan Klemkow <j.klemkow@wemelug.de>
       Date:   Mon, 26 Oct 2015 22:17:57 +0100
       
       wip
       
       Diffstat:
         M lchat.c                             |     174 ++++++++++++++++++++++++++++---
         M slackline.c                         |      58 +++++++++++++++++++++++++++----
         M slackline.h                         |      16 +++++++---------
       
       3 files changed, 216 insertions(+), 32 deletions(-)
       ---
 (DIR) diff --git a/lchat.c b/lchat.c
       @@ -1,6 +1,10 @@
        #include <err.h>
       +#include <errno.h>
       +#include <fcntl.h>
       +#include <poll.h>
        #include <stdio.h>
        #include <stdlib.h>
       +#include <string.h>
        #include <term.h>
        #include <termios.h>
        #include <unistd.h>
       @@ -9,29 +13,104 @@
        
        struct termios origin_term;
        
       -void
       +static void
        exit_handler(void)
        {
                if (tcsetattr(STDIN_FILENO, TCSANOW, &origin_term) == -1)
                        err(EXIT_FAILURE, "tcsetattr");
        }
        
       +static void
       +line_output(struct slackline *sl, char *file)
       +{
       +        int fd;
       +
       +        if ((fd = open(file, O_WRONLY|O_APPEND)) == -1)
       +                err(EXIT_FAILURE, "open: %s", file);
       +
       +        /* replace NUL-terminator with newline as line separator for file */
       +        sl->buf[sl->len] = '\n';
       +
       +        if (write(fd, sl->buf, sl->len + 1) == -1)
       +                err(EXIT_FAILURE, "write");
       +
       +        if (close(fd) == -1)
       +                err(EXIT_FAILURE, "close");
       +}
       +
       +static void
       +usage(void)
       +{
       +        fprintf(stderr, "lchar [-nH] [-p prompt] [-i in] [-o out] [directory]\n");
       +        exit(EXIT_FAILURE);
       +}
       +
        int
       -main(void)
       +main(int argc, char *argv[])
        {
       +        char tail_cmd[BUFSIZ];
       +        struct pollfd pfd[2];
                struct termios term;
                struct slackline *sl = sl_init();
       -        char *term_name = getenv("TERM");
                int fd = STDIN_FILENO;
                int c;
       +        int ch;
       +        bool empty_line = true;
       +        size_t history_len = 0;
       +        char *prompt = ">";
       +        size_t prompt_len = strlen(prompt);
       +        char *dir = ".";
       +        char *in_file = NULL;
       +        char *out_file = NULL;
       +        FILE *tail_fh;
       +
       +        while ((ch = getopt(argc, argv, "H:i:no:p:h")) != -1) {
       +                switch (ch) {
       +                case 'H':
       +                        errno = 0;
       +                        history_len = strtoull(optarg, NULL, 0);
       +                        if (errno != 0)
       +                                err(EXIT_FAILURE, "strtoull");
       +                        break;
       +                case 'i':
       +                        if ((in_file = strdup(optarg)) == NULL)
       +                                err(EXIT_FAILURE, "strdup");
       +                        break;
       +                case 'n':
       +                        empty_line = false;
       +                        break;
       +                case 'o':
       +                        if ((out_file = strdup(optarg)) == NULL)
       +                                err(EXIT_FAILURE, "strdup");
       +                        break;
       +                case 'p':
       +                        if ((prompt = strdup(optarg)) == NULL)
       +                                err(EXIT_FAILURE, "strdup");
       +                        prompt_len = strlen(prompt);
       +                        break;
       +                case 'h':
       +                default:
       +                        usage();
       +                        /* NOTREACHED */
       +                }
       +        }
       +        argc -= optind;
       +        argv += optind;
        
       -        if (term_name == NULL)
       -                errx(EXIT_FAILURE, "environment TERM is not set");
       +        if (argc > 1)
       +                usage();
        
       -        switch (tgetent(NULL, term_name)) {
       -        case -1: err(EXIT_FAILURE, "tgetent");
       -        case 0: errx(EXIT_FAILURE, "no termcap entry found for %s", term_name);
       -        }
       +        if (argc == 1)
       +                if ((dir = strdup(argv[0])) == NULL)
       +                        err(EXIT_FAILURE, "strdup");
       +
       +        if (in_file == NULL)
       +                if (asprintf(&in_file, "%s/in", dir) == -1)
       +                        err(EXIT_FAILURE, "asprintf");
       +
       +        if (out_file == NULL)
       +                if (asprintf(&out_file, "%s/out", dir) == -1)
       +                        err(EXIT_FAILURE, "asprintf");
        
                if (isatty(fd) == 0)
                        err(EXIT_FAILURE, "isatty");
       @@ -47,7 +126,14 @@ main(void)
                if (tcgetattr(fd, &term) == -1)
                        err(EXIT_FAILURE, "tcgetattr");
        
       -        cfmakeraw(&term);
       +        /* TODO: clean up this block.  copied from cfmakeraw(3) */
       +        term.c_iflag &= ~(IMAXBEL|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
       +//        term.c_oflag &= ~OPOST;
       +        term.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
       +        term.c_cflag &= ~(CSIZE|PARENB);
       +        term.c_cflag |= CS8;
       +        term.c_cc[VMIN] = 1;
       +        term.c_cc[VTIME] = 0;
        
                if (tcsetattr(fd, TCSANOW, &term) == -1)
                        err(EXIT_FAILURE, "tcsetattr");
       @@ -55,13 +141,67 @@ main(void)
                setbuf(stdin, NULL);
                setbuf(stdout, NULL);
        
       -        while ((c = getchar()) != 13) {
       -                if (sl_keystroke(sl, c) == -1)
       -                        errx(EXIT_FAILURE, "sl_keystroke");
       -                printf("\r\033[2K%s", sl->buf);
       +        /* open external source */
       +        snprintf(tail_cmd, sizeof tail_cmd, "exec tail -n %zd -f %s",
       +            history_len, out_file);
       +        if ((tail_fh = popen(tail_cmd, "r")) == NULL)
       +                err(EXIT_FAILURE, "unable to open pipe to tail command");
       +
       +        pfd[0].fd = fd;
       +        pfd[0].events = POLLIN;
       +
       +        pfd[1].fd = fileno(tail_fh);
       +        pfd[1].events = POLLIN;
       +
       +        /* print initial prompt */
       +        fputs(prompt, stdout);
       +
       +        for (;;) {
       +                poll(pfd, 2, INFTIM);
       +
       +                /* carriage return and erase the whole line */
       +                fputs("\r\033[2K", stdout);
       +
       +                /* handle keyboard intput */
       +                if (pfd[0].revents & POLLIN) {
       +                        c = getchar();
       +                        if (c == 13) {        /* return */
       +                                if (sl->len == 0 && empty_line == false)
       +                                        continue;
       +                                line_output(sl, in_file);
       +                                sl_reset(sl);
       +                        }
       +                        if (sl_keystroke(sl, c) == -1)
       +                                errx(EXIT_FAILURE, "sl_keystroke");
       +                }
       +
       +                /* handle tail command error and its broken pipe */
       +                if (pfd[1].revents & POLLHUP)
       +                        break;
       +
       +                /* handle file intput */
       +                if (pfd[1].revents & POLLIN) {
       +                        char buf[BUFSIZ];
       +                        ssize_t n = read(pfd[1].fd, buf, sizeof buf);
       +                        if (n == 0)
       +                                errx(EXIT_FAILURE, "tail command exited");
       +                        if (n == -1)
       +                                err(EXIT_FAILURE, "read");
       +                        if (write(STDOUT_FILENO, buf, n) == -1)
       +                                err(EXIT_FAILURE, "write");
       +                        putchar('\a');        /* ring the bell on external input */
       +                }
       +
       +                /* show current input line */
       +                fputs(prompt, stdout);
       +                fputs(sl->buf, stdout);
       +
       +                if (sl->cur < sl->len) {        /* move the cursor */
       +                        putchar('\r');
       +                        /* HACK: because \033[0C does the same as \033[1C */
       +                        if (sl->cur + prompt_len > 0)
       +                                printf("\033[%zdC", sl->cur + prompt_len);
       +                }
                }
       -
       -        puts("\r");
       -
                return EXIT_SUCCESS;
        }
 (DIR) diff --git a/slackline.c b/slackline.c
       @@ -1,6 +1,7 @@
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
       +#include <stdbool.h>
        
        #include "slackline.h"
        
       @@ -18,9 +19,7 @@ sl_init(void)
                        return NULL;
                }
        
       -        sl->buf[0] = '\0';
       -        sl->len = 0;
       -        sl->cur = 0;
       +        sl_reset(sl);
        
                return sl;
        }
       @@ -32,14 +31,54 @@ sl_free(struct slackline *sl)
                free(sl);
        }
        
       +void
       +sl_reset(struct slackline *sl)
       +{
       +        sl->buf[0] = '\0';
       +        sl->len = 0;
       +        sl->cur = 0;
       +        sl->esc = ESC_NONE;
       +}
       +
        int
        sl_keystroke(struct slackline *sl, int key)
        {
                if (sl == NULL || sl->len < sl->cur)
                        return -1;
        
       +        /* handle escape sequences */
       +        switch (sl->esc) {
       +        case ESC_NONE:
       +                break;
       +        case ESC:
       +                sl->esc = key == '[' ? ESC_BRACKET : ESC_NONE;
       +                return 0;
       +        case ESC_BRACKET:
       +                switch (key) {
       +                case 'A':        /* up    */
       +                case 'B':        /* down  */
       +                        break;
       +                case 'C':        /* right */
       +                        if (sl->cur < sl->len)
       +                                sl->cur++;
       +                        break;
       +                case 'D':        /* left */
       +                        if (sl->cur > 0)
       +                                sl->cur--;
       +                        break;
       +                case 'H':        /* Home  */
       +                        sl->cur = 0;
       +                        break;
       +                case 'F':        /* End   */
       +                        sl->cur = sl->len;
       +                        break;
       +                }
       +                sl->esc = ESC_NONE;
       +                return 0;
       +        }
       +
                /* add character to buffer */
       -        if (key >= 32 && key <= 127) {
       +        if (key >= 32 && key < 127) {
                        if (sl->cur < sl->len) {
                                memmove(sl->buf + sl->cur + 1, sl->buf + sl->cur,
                                    sl->len - sl->cur);
       @@ -54,12 +93,19 @@ sl_keystroke(struct slackline *sl, int key)
        
                /* handle ctl keys */
                switch (key) {
       -        case 8:        /* backspace */
       +        case 27:        /* Escape */
       +                sl->esc = ESC;
       +                break;
       +        case 127:        /* backspace */
       +        case 8:                /* backspace */
                        if (sl->cur == 0)
                                break;
       +                if (sl->cur < sl->len)
       +                        memmove(sl->buf + sl->cur - 1, sl->buf + sl->cur,
       +                            sl->len - sl->cur);
                        sl->cur--;
                        sl->len--;
       -                sl->buf[sl->cur] = '\0';
       +                sl->buf[sl->len] = '\0';
                        break;
                }
        
 (DIR) diff --git a/slackline.h b/slackline.h
       @@ -1,23 +1,21 @@
        #ifndef SLACKLIINE_H
        #define SLACKLIINE_H
        
       -/*
       - * +-+-+-+-+-+
       - * |c|c|c|0|0|
       - * +-+-+-+-+-+
       - *      ^   ^
       - *     len  bufsize
       - */
       +#include <stdbool.h>
       +
       +enum esc_seq {ESC_NONE, ESC, ESC_BRACKET};
        
        struct slackline {
                char *buf;
                size_t bufsize;
                size_t len;
                size_t cur;
       +        enum esc_seq esc;
        };
        
       -struct slackline * sl_init(void);
       -void sl_free(struct slackline *);
       +struct slackline *sl_init(void);
       +void sl_free(struct slackline *sl);
       +void sl_reset(struct slackline *sl);
        int sl_keystroke(struct slackline *sl, int key);
        
        #endif