Introduction
Introduction Statistics Contact Development Disclaimer Help
wip - lchat - A line oriented chat front end for ii.
git clone git://git.suckless.org/lchat
Log
Files
Refs
README
---
commit 98fa5e3861a515b0b9f7b212e7eb5abf550e1f33
parent b1db0ac6e9850a858120ac43596405208a3fc581
Author: Jan Klemkow <[email protected]>
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(-)
---
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]\…
+ 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 inp…
+ }
+
+ /* 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;
}
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;
}
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
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.