Introduce modes - 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 a2e85fdc57c1322c16d120d1865acc8f13f4c8bd
 (DIR) parent 19b4d99293ad470c98406b94935565750339b716
 (HTM) Author: Tom Schwindl <schwindl@posteo.de>
       Date:   Thu, 29 Dec 2022 13:17:48 +0100
       
       Introduce modes
       
       A mode determines how keyborad input is interpreted and which keybindings
       are available. Currently, there is a default- and an emacs-mode from which
       the user can choose.
       
       Diffstat:
         M Makefile                            |       7 +++++--
         M lchat.c                             |       9 ++++++---
         M slackline.c                         |     112 +++++++++++++++++++++----------
         A slackline_emacs.c                   |      53 ++++++++++++++++++++++++++++++
       
       4 files changed, 139 insertions(+), 42 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -22,8 +22,8 @@ dist:
                tar -czf lchat-$(VERSION).tar.gz lchat-$(VERSION)
                rm -fr lchat-$(VERSION)
        
       -lchat: lchat.o slackline.o util.o
       -        $(CC) -o $@ lchat.o slackline.o util.o $(LIBS)
       +lchat: lchat.o slackline.o util.o slackline_emacs.o
       +        $(CC) -o $@ lchat.o slackline.o slackline_emacs.o util.o $(LIBS)
        
        lchat.o: lchat.c
                $(CC) -c $(CFLAGS) -D_BSD_SOURCE -D_XOPEN_SOURCE -D_GNU_SOURCE \
       @@ -42,5 +42,8 @@ sl_test: sl_test.o slackline.o slackline.h
        slackline.o: slackline.c slackline.h
                $(CC) -c $(CFLAGS) -o $@ slackline.c
        
       +slackline_emacs.o: slackline_emacs.c slackline.h
       +        $(CC) -c $(CFLAGS) -o $@ slackline_emacs.c
       +
        util.o: util.c util.h
                $(CC) -c $(CFLAGS) -D_BSD_SOURCE -o $@ util.c
 (DIR) diff --git a/lchat.c b/lchat.c
       @@ -23,6 +23,7 @@
        #include <limits.h>
        #include <poll.h>
        #include <signal.h>
       +#include <stdbool.h>
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
       @@ -183,7 +184,7 @@ main(int argc, char *argv[])
                char *in_file = NULL;
                char *out_file = NULL;
        
       -        while ((ch = getopt(argc, argv, "an:i:eo:p:t:uh")) != -1) {
       +        while ((ch = getopt(argc, argv, "an:i:eo:p:t:uhm:")) != -1) {
                        switch (ch) {
                        case 'a':
                                bell_flag = false;
       @@ -217,6 +218,10 @@ main(int argc, char *argv[])
                        case 'u':
                                ucspi = true;
                                break;
       +                case 'm':
       +                        if (strcmp(optarg, "emacs") == 0)
       +                                sl_mode(sl, SL_EMACS);
       +                        break;
                        case 'h':
                        default:
                                usage();
       @@ -342,8 +347,6 @@ main(int argc, char *argv[])
                                        return EXIT_SUCCESS;
        
                                switch (c) {
       -                        case 4:                /* eot */
       -                                return EXIT_SUCCESS;
                                case 13:        /* return */
                                        if (sl->rlen == 0 && empty_line == false)
                                                goto out;
 (DIR) diff --git a/slackline.c b/slackline.c
       @@ -22,13 +22,14 @@
        
        #include <grapheme.h>
        
       +#include "slackline_internals.h"
        #include "slackline.h"
       -
       -enum direction {LEFT, RIGHT, HOME, END};
       +#include "util.h"
        
        struct slackline *
        sl_init(void)
        {
       +        char *mode = getenv("EDITOR");
                struct slackline *sl = malloc(sizeof *sl);
        
                if (sl == NULL)
       @@ -45,6 +46,14 @@ sl_init(void)
        
                sl_reset(sl);
        
       +        sl->mode = SL_DEFAULT;
       +        if (mode != NULL) {
       +                if (strcmp(mode, "emacs") == 0)
       +                        sl->mode = SL_EMACS;
       +                else if (strcmp(mode, "vi") == 0)
       +                        sl->mode = SL_VI;
       +        }
       +
                return sl;
        }
        
       @@ -71,7 +80,13 @@ sl_reset(struct slackline *sl)
                sl->ubuf_len = 0;
        }
        
       -static size_t
       +void
       +sl_mode(struct slackline *sl, enum mode mode)
       +{
       +        sl->mode = mode;
       +}
       +
       +size_t
        sl_postobyte(struct slackline *sl, size_t pos)
        {
                char *ptr = &sl->buf[0];
       @@ -84,13 +99,13 @@ sl_postobyte(struct slackline *sl, size_t pos)
                return byte;
        }
        
       -static char *
       +char *
        sl_postoptr(struct slackline *sl, size_t pos)
        {
                return &sl->buf[sl_postobyte(sl, pos)];
        }
        
       -static void
       +void
        sl_backspace(struct slackline *sl)
        {
                char *ncur;
       @@ -114,7 +129,7 @@ sl_backspace(struct slackline *sl)
                sl->ptr = ncur;
        }
        
       -static void
       +void
        sl_move(struct slackline *sl, enum direction dir)
        {
                switch (dir) {
       @@ -139,21 +154,41 @@ sl_move(struct slackline *sl, enum direction dir)
                sl->ptr = sl->buf + sl->bcur;
        }
        
       -int
       -sl_keystroke(struct slackline *sl, int key)
       +static void
       +sl_default(struct slackline *sl, int key)
        {
       -        uint_least32_t cp;
       -
       -        if (sl == NULL || sl->rlen < sl->rcur)
       -                return -1;
       +        switch (key) {
       +        case 27:        /* Escape */
       +                sl->esc = ESC;
       +                break;
       +        case 21:
       +                sl_reset(sl);
       +                break;
       +        case 23: /* ctrl+w -- erase previous word */
       +                while (sl->rcur != 0 && isspace((unsigned char) *(sl->ptr-1)))
       +                        sl_backspace(sl);
       +                while (sl->rcur != 0 && !isspace((unsigned char) *(sl->ptr-1)))
       +                        sl_backspace(sl);
       +                break;
       +        case 127:        /* backspace */
       +        case 8:                /* backspace */
       +                sl_backspace(sl);
       +                break;
       +        default:
       +                break;
       +        }
       +}
        
       +static int
       +sl_esc(struct slackline *sl, int key)
       +{
                /* handle escape sequences */
                switch (sl->esc) {
                case ESC_NONE:
                        break;
                case ESC:
                        sl->esc = key == '[' ? ESC_BRACKET : ESC_NONE;
       -                return 0;
       +                return 1;
                case ESC_BRACKET:
                        switch (key) {
                        case 'A':        /* up    */
       @@ -189,10 +224,10 @@ sl_keystroke(struct slackline *sl, int key)
                        case '9':
                                sl->nummod = key;
                                sl->esc = ESC_BRACKET_NUM;
       -                        return 0;
       +                        return 1;
                        }
                        sl->esc = ESC_NONE;
       -                return 0;
       +                return 1;
                case ESC_BRACKET_NUM:
                        switch(key) {
                        case '~':
       @@ -213,35 +248,38 @@ sl_keystroke(struct slackline *sl, int key)
                                        break;
                                }
                                sl->esc = ESC_NONE;
       -                        return 0;
       +                        return 1;
                        }
                }
        
       -        if (!iscntrl((unsigned char) key))
       -                goto compose;
       +        return 0;
       +}
        
       -        /* handle ctl keys */
       -        switch (key) {
       -        case 27:        /* Escape */
       -                sl->esc = ESC;
       -                return 0;
       -        case 127:        /* backspace */
       -        case 8:                /* backspace */
       -                sl_backspace(sl);
       -                return 0;
       -        case 21: /* ctrl+u -- clearline */
       -                sl_reset(sl);
       -                return 0;
       -        case 23: /* ctrl+w -- erase previous word */
       -                while (sl->rcur != 0 && isspace((unsigned char) *(sl->ptr-1)))
       -                        sl_backspace(sl);
       +int
       +sl_keystroke(struct slackline *sl, int key)
       +{
       +        uint_least32_t cp;
        
       -                while (sl->rcur != 0 && !isspace((unsigned char) *(sl->ptr-1)))
       -                        sl_backspace(sl);
       -                return 0;
       -        default:
       +        if (sl == NULL || sl->rlen < sl->rcur)
       +                return -1;
       +        if (sl_esc(sl, key))
                        return 0;
       +        if (!iscntrl((unsigned char) key))
       +                goto compose;
       +
       +        switch (sl->mode) {
       +        case SL_DEFAULT:
       +                sl_default(sl, key);
       +                break;
       +        case SL_EMACS:
       +                sl_default(sl, key);
       +                sl_emacs(sl, key);
       +                break;
       +        case SL_VI:
       +                /* TODO: implement vi-mode */
       +                break;
                }
       +        return 0;
        
        compose:
                /* byte-wise composing of UTF-8 runes */
 (DIR) diff --git a/slackline_emacs.c b/slackline_emacs.c
       @@ -0,0 +1,53 @@
       +#include <stdio.h>
       +#include <ctype.h>
       +#include <stddef.h>
       +#include <stdlib.h>
       +
       +#include "slackline_internals.h"
       +
       +void
       +sl_emacs(struct slackline *sl, int key)
       +{
       +        char tmp;
       +
       +        switch (key) {
       +        case 27:        /* Escape */
       +                sl->esc = ESC;
       +                break;
       +        case 1: /* ctrl+a -- start of line */
       +                sl_move(sl, HOME);
       +                break;
       +        case 2: /* ctrl+b -- previous char */
       +                sl_move(sl, LEFT);
       +                break;
       +        case 4: /* ctrl+d -- delete char in front of the cursor or exit */
       +                if (sl->rcur < sl->rlen) {
       +                        sl_move(sl, RIGHT);
       +                        sl_backspace(sl);
       +                } else {
       +                        exit(EXIT_SUCCESS);
       +                }
       +                break;
       +        case 5: /* ctrl+e -- end of line */
       +                sl_move(sl, END);
       +                break;
       +        case 6: /* ctrl+f -- next char */
       +                sl_move(sl, RIGHT);
       +                break;
       +        case 11: /* ctrl+k -- delete line from cursor to end */
       +                for (int i = sl->rlen - sl->rcur; i > 0; --i) {
       +                        sl_move(sl, RIGHT);
       +                        sl_backspace(sl);
       +                }
       +                break;
       +        case 20: /* ctrl+t -- swap last two chars */
       +                if (sl->rcur >= 2) {
       +                        tmp = *sl_postoptr(sl, sl->rcur-1);
       +                        sl->buf[sl->rcur-1] = *sl_postoptr(sl, sl->rcur-2);
       +                        sl->buf[sl->rcur-2] = tmp;
       +                }
       +                break;
       +        default:
       +                break;
       +        }
       +}