commit a2e85fdc57c1322c16d120d1865acc8f13f4c8bd
Author:     Tom Schwindl <[email protected]>
AuthorDate: Thu Dec 29 13:17:48 2022 +0100
Commit:     Jan Klemkow <[email protected]>
CommitDate: Mon Jan 2 19:48:17 2023 +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.

diff --git a/Makefile b/Makefile
index ee6839e..5c05d88 100644
--- 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
diff --git a/lchat.c b/lchat.c
index 00a1f95..35c5463 100644
--- 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;
diff --git a/slackline.c b/slackline.c
index 6e24786..f23671e 100644
--- 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 */
diff --git a/slackline_emacs.c b/slackline_emacs.c
new file mode 100644
index 0000000..bd51042
--- /dev/null
+++ 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;
+       }
+}

Reply via email to