aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Czigler <37268479+mcpcpc@users.noreply.github.com>2020-10-20 15:54:05 -0400
committerGitHub <noreply@github.com>2020-10-20 15:54:05 -0400
commit1e1be13a5321dd7b12137a4af8501a9fbf279112 (patch)
tree59c190e5ae8e5b47b1e31e334efcbc76f088cc9a
parentCheck channel for NULL condition (#65) (diff)
downloadkirc-1e1be13a5321dd7b12137a4af8501a9fbf279112.tar.gz
kirc-1e1be13a5321dd7b12137a4af8501a9fbf279112.tar.bz2
kirc-1e1be13a5321dd7b12137a4af8501a9fbf279112.tar.xz
kirc-1e1be13a5321dd7b12137a4af8501a9fbf279112.tar.zst
kirc-1e1be13a5321dd7b12137a4af8501a9fbf279112.zip
0.1.7 (#68)
* bump version * remove loop from edit() * remove redundant variables * remove while loop * refactor * move cursor to col 0 before server message * cleanup * add back chan_default prefix * remove puts and add \r * add asynchronous feature set * replace \x1b[0F with \r * remove extra character F * fix ctrl+c * remove log * cleanup ctrl+c exit * Update kirc.c * remove sys/socket.h library * move duplicate code block to function * move stateSet() function * Add ctrl+c key binding * fix verbose flag * cleanup * rename default channel variable * add channel variable check * set cdef default value of "?" * new cdef default assignment * fix channel not printing with -c arg
Diffstat (limited to '')
-rw-r--r--README2
-rw-r--r--kirc.c226
2 files changed, 117 insertions, 111 deletions
diff --git a/README b/README
index 2cae88b..65f752f 100644
--- a/README
+++ b/README
@@ -25,6 +25,7 @@
## Features
* Excellent cross-platform compatibility.
+* Asynchronous user input and server messager handling.
* No dependencies other than a C99 compiler.
* Native SASL PLAIN and EXTERNAL authentication support.
* TLS/SSL protocol capable (via external TLS utilities).
@@ -85,6 +86,7 @@ kirc [-s hostname] [-p port] [-c channels] [-n nickname] [-r realname] [-u usern
* **CTRL+W** deletes the previous word.
* **CTRL+U** deletes the entire line.
* **CTRL+K** deletes the from current character to end of line.
+* **CTRL+C** Force quit kirc
## Support Documentation
diff --git a/kirc.c b/kirc.c
index b8772ae..f74e92c 100644
--- a/kirc.c
+++ b/kirc.c
@@ -10,15 +10,14 @@
#include <poll.h>
#include <errno.h>
#include <termios.h>
-#include <sys/socket.h>
#include <sys/ioctl.h>
-#define VERSION "0.1.6" /* version */
+#define VERSION "0.1.7" /* version */
#define MSG_MAX 512 /* max message length */
#define CHA_MAX 200 /* max channel length */
static int conn; /* connection socket */
-static char chan_default[MSG_MAX]; /* default PRIVMSG channel */
+static char cdef[MSG_MAX] = "?"; /* default PRIVMSG channel */
static int verb = 0; /* verbose output */
static int sasl = 0; /* SASL method */
static size_t gutl = 20; /* max printed nick chars */
@@ -54,8 +53,9 @@ struct abuf {
};
static void disableRawMode(void) {
- if (rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&orig) != -1)
+ if (rawmode && tcsetattr(STDIN_FILENO,TCSAFLUSH,&orig) != -1) {
rawmode = 0;
+ }
}
static int enableRawMode(int fd) {
@@ -108,20 +108,15 @@ static int getColumns(int ifd, int ofd) {
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
int start, cols;
- /* Get the initial position so we can restore it later. */
start = getCursorPosition(ifd,ofd);
if (start == -1) goto failed;
- /* Go to right margin and get position. */
if (write(ofd,"\x1b[999C",6) != 6) goto failed;
cols = getCursorPosition(ifd,ofd);
if (cols == -1) goto failed;
- /* Restore position. */
if (cols > start) {
char seq[32];
snprintf(seq, sizeof(seq), "\x1b[%dD",cols-start);
- if (write(ofd,seq,strlen(seq)) == -1) {
- /* Can't recover... */
- }
+ if (write(ofd,seq,strlen(seq)) == -1) {} /* Can't recover from write error. */
}
return cols;
} else {
@@ -161,7 +156,7 @@ static size_t pstrlen(const char *s) {
i++;
}
return len;
-}
+}
static void refreshLine(struct State *l) {
char seq[64];
@@ -182,16 +177,12 @@ static void refreshLine(struct State *l) {
}
abInit(&ab);
- /* Cursor to left edge */
snprintf(seq, sizeof(seq), "\r");
abAppend(&ab, seq, strlen(seq));
- /* Write the prompt and the current buffer content */
abAppend(&ab,l->prompt, strlen(l->prompt));
abAppend(&ab, buf, len);
- /* Erase to right */
snprintf(seq, sizeof(seq), "\x1b[0K");
abAppend(&ab, seq, strlen(seq));
- /* Move cursor to original position. */
snprintf(seq, sizeof(seq), "\r\x1b[%dC", (int)(pos + plen));
abAppend(&ab, seq, strlen(seq));
if (write(fd, ab.b, ab.len) == -1) {} /* Can't recover from write error. */
@@ -300,90 +291,84 @@ static void editSwapCharWithPrev(struct State *l, char *buf) {
int aux = buf[l->pos - 1];
buf[l->pos - 1] = buf[l->pos];
buf[l->pos] = aux;
- if (l->pos != l->len - 1) l->pos++;
+ if (l->pos != l->len - 1) {
+ l->pos++;
+ }
refreshLine(l);
}
}
-static int edit(char *buf, size_t buflen, const char *prompt)
-{
- struct State l;
-
- l.buf = buf;
- l.buflen = buflen;
- l.prompt = prompt;
- l.plen = pstrlen(prompt);
- l.oldpos = l.pos = 0;
- l.len = 0;
- l.cols = getColumns(STDIN_FILENO, STDOUT_FILENO);
- /* Buffer starts empty. */
- l.buf[0] = '\0';
- l.buflen--; /* Make sure there is always space for the nulterm */
-
- if (write(STDOUT_FILENO,prompt,strlen(prompt)) == -1) return -1;
- while(1) {
- char c;
- int nread;
- char seq[3];
-
- nread = read(STDIN_FILENO, &c ,1);
- if (nread <= 0) return l.len;
-
- switch(c) {
- case 13: return (int)l.len; /* enter */
- case 3: errno = EAGAIN; return -1; /* ctrl-c */
- case 127: /* backspace */
- case 8: editBackspace(&l); break; /* ctrl-h */
- case 2: editMoveLeft(&l); break; /* ctrl-b */
- case 6: editMoveRight(&l); break; /* ctrl-f */
- case 1: editMoveHome(&l); break; /* Ctrl+a */
- case 5: editMoveEnd(&l); break; /* ctrl+e */
- case 23: editDeletePrevWord(&l); break; /* ctrl+w */
- case 21: editDeleteWholeLine(&l, buf); break; /* Ctrl+u */
- case 11: editDeleteLineToEnd(&l, buf); break; /* Ctrl+k */
- case 20: editSwapCharWithPrev(&l, buf); break; /* ctrl-t */
- case 4: /* ctrl-d, remove char at right of cursor, or if the
- line is empty, act as end-of-file. */
- if (l.len > 0) {
- editDelete(&l);
- } else {
- return -1;
- }
- break;
- case 27: /* escape sequence */
- if (read(STDIN_FILENO, seq, 1) == -1) break;
- if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
- /* ESC [ sequences. */
- if (seq[0] == '[') {
- if (seq[1] >= '0' && seq[1] <= '9') {
- /* Extended escape, read additional byte. */
- if (read(STDIN_FILENO, seq + 2, 1) == -1) break;
- if (seq[2] == '~') {
- if (seq[1] == 3) editDelete(&l); /* Delete key. */
- }
- } else {
- switch(seq[1]) {
- case 'C': editMoveRight(&l); break; /* Right */
- case 'D': editMoveLeft(&l); break; /* Left */
- case 'H': editMoveHome(&l); break; /* Home */
- case 'F': editMoveEnd(&l); break; /* End*/
- }
+static int edit(struct State *l, const char *prompt) {
+ char c;
+ int nread;
+ char seq[3];
+
+ nread = read(STDIN_FILENO, &c ,1);
+ if (nread <= 0) return 1;
+
+ switch(c) {
+ case 13: return 1; /* enter */
+ case 3: errno = EAGAIN; return -1; /* ctrl-c */
+ case 127: /* backspace */
+ case 8: editBackspace(l); break; /* ctrl-h */
+ case 2: editMoveLeft(l); break; /* ctrl-b */
+ case 6: editMoveRight(l); break; /* ctrl-f */
+ case 1: editMoveHome(l); break; /* Ctrl+a */
+ case 5: editMoveEnd(l); break; /* ctrl+e */
+ case 23: editDeletePrevWord(l); break; /* ctrl+w */
+ case 21: editDeleteWholeLine(l, l->buf); break; /* Ctrl+u */
+ case 11: editDeleteLineToEnd(l, l->buf); break; /* Ctrl+k */
+ case 20: editSwapCharWithPrev(l,l->buf); break; /* ctrl-t */
+ case 4: /* ctrl-d, remove char at right of cursor, or if the
+ line is empty, act as end-of-file. */
+ if (l->len > 0) {
+ editDelete(l);
+ } else {
+ return -1;
+ }
+ break;
+ case 27: /* escape sequence */
+ if (read(STDIN_FILENO, seq, 1) == -1) break;
+ if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
+ /* ESC [ sequences. */
+ if (seq[0] == '[') {
+ if (seq[1] >= '0' && seq[1] <= '9') {
+ /* Extended escape, read additional byte. */
+ if (read(STDIN_FILENO, seq + 2, 1) == -1) break;
+ if (seq[2] == '~') {
+ if (seq[1] == 3) editDelete(l); /* Delete key. */
}
- }
- /* ESC O sequences. */
- else if (seq[0] == 'O') {
+ } else {
switch(seq[1]) {
- case 'H': editMoveHome(&l); break; /* Home */
- case 'F': editMoveEnd(&l); break; /* End*/
+ case 'C': editMoveRight(l); break; /* Right */
+ case 'D': editMoveLeft(l); break; /* Left */
+ case 'H': editMoveHome(l); break; /* Home */
+ case 'F': editMoveEnd(l); break; /* End*/
}
}
- break;
- default: if (editInsert(&l, c)) return -1; break;
-
- }
+ }
+ /* ESC O sequences. */
+ else if (seq[0] == 'O') {
+ switch(seq[1]) {
+ case 'H': editMoveHome(l); break; /* Home */
+ case 'F': editMoveEnd(l); break; /* End*/
+ }
+ }
+ break;
+ default: if (editInsert(l, c)) return -1; break;
}
- return l.len;
+ return 0;
+}
+
+static void stateSet(struct State *l, char *buf, char *prompt) {
+ l->plen = pstrlen(prompt);
+ l->oldpos = l->pos = 0;
+ l->len = 0;
+ l->cols = getColumns(STDIN_FILENO, STDOUT_FILENO);
+ l->buf[0] = '\0';
+ l->buflen--;
}
+
static void logAppend(char *str, char *path) {
FILE *out;
@@ -460,10 +445,9 @@ static int connectionInit(void) {
}
static void messageWrap(char *line, size_t offset) {
-
- char *tok;
- size_t cmax = getColumns(STDIN_FILENO, STDOUT_FILENO);
- size_t wordwidth, spaceleft = cmax - gutl - offset, spacewidth = 1;
+ char *tok;
+ size_t cmax = getColumns(STDIN_FILENO, STDOUT_FILENO);
+ size_t wordwidth, spaceleft = cmax - gutl - offset, spacewidth = 1;
for (tok = strtok(line, " "); tok != NULL; tok = strtok(NULL, " ")) {
wordwidth = strlen(tok);
@@ -478,8 +462,6 @@ static void messageWrap(char *line, size_t offset) {
}
static void rawParser(char *string) {
- if (verb) printf(">> %s", string);
-
if (!strncmp(string, "PING", 4)) {
string[1] = 'O';
raw("%s\r\n", string);
@@ -488,6 +470,9 @@ static void rawParser(char *string) {
if (string[0] != ':') return;
+ printf("\r");
+
+ if (verb) printf(">> %s", string);
if (olog) logAppend(string, olog);
char *tok;
@@ -503,28 +488,28 @@ static void rawParser(char *string) {
if (!strncmp(command, "001", 3) && chan != NULL) {
for (tok = strtok(chan, ",|"); tok != NULL; tok = strtok(NULL, ",|")) {
- strcpy(chan_default, tok);
+ strcpy(cdef, tok);
raw("JOIN #%s\r\n", tok);
} return;
} else if (!strncmp(command, "QUIT", 4)) {
printf("%*s<<< \x1b[34;1m%s\x1b[0m", g - 3, "", nickname);
} else if (!strncmp(command, "PART", 4)) {
printf("%*s<-- \x1b[34;1m%s\x1b[0m", g - 3, "", nickname);
- if (channel != NULL && strstr(channel, chan_default) == NULL) {
+ if (channel != NULL && strstr(channel, cdef) == NULL) {
printf(" [\x1b[33m%s\x1b[0m] ", channel);
}
} else if (!strncmp(command, "JOIN", 4)) {
printf("%*s--> \x1b[32;1m%s\x1b[0m", g - 3, "", nickname);
- if (channel != NULL && strstr(channel, chan_default) == NULL) {
+ if (channel != NULL && strstr(channel, cdef) == NULL) {
printf(" [\x1b[33m%s\x1b[0m] ", channel);
}
} else if (!strncmp(command, "NICK", 4)) {
printf("\x1b[35;1m%*s\x1b[0m ", g - 4, nickname);
printf("--> \x1b[35;1m%s\x1b[0m", message);
} else if (!strncmp(command, "PRIVMSG", 7)) {
- if (strcmp(channel, nick) == 0) {
+ if (channel != NULL && strcmp(channel, nick) == 0) {
printf("%*s\x1b[33;1m%-.*s\x1b[36m ", s, "", g, nickname);
- } else if (strstr(channel, chan_default) == NULL) {
+ } else if (channel != NULL && strstr(channel, cdef) == NULL) {
printf("%*s\x1b[33;1m%-.*s\x1b[0m", s, "", g, nickname);
printf(" [\x1b[33m%s\x1b[0m] ", channel);
offset += 12 + strlen(channel);
@@ -534,7 +519,7 @@ static void rawParser(char *string) {
printf("%*s\x1b[33;1m%-.*s\x1b[0m ", s, "", g, nickname);
messageWrap((message ? message : " "), offset);
}
- puts("\x1b[0m\r");
+ printf("\x1b[0m\r\n");
}
static char message_buffer[MSG_MAX + 1];
@@ -552,8 +537,8 @@ static int handleServerMessage(void) {
}
}
if (sl == 0) {
- fputs("Connection closed", stderr);
- puts("\x1b[0F\x1b[E");
+ fputs("\rconnection closed", stderr);
+ puts("\r\x1b[E");
return -1;
}
@@ -589,8 +574,8 @@ static void handleUserInput(char *usrin) {
printf("\r\x1b[0K");
if (usrin[0] == '/' && usrin[1] == '#') {
- strcpy(chan_default, usrin + 2);
- printf("\x1b[35mnew channel: #%s\x1b[0m", chan_default);
+ strcpy(cdef, usrin + 2);
+ printf("\x1b[35mnew channel: #%s\x1b[0m", cdef);
} else if (usrin[0] == '/') {
raw("%s\r\n", usrin + 1);
printf("\x1b[35m%s\x1b[0m", usrin);
@@ -599,8 +584,8 @@ static void handleUserInput(char *usrin) {
raw("privmsg %s :%s\r\n", usrin + 1, tok);
printf("\x1b[35mprivmsg %s :%s\x1b[0m", usrin + 1, tok);
} else {
- raw("privmsg #%s :%s\r\n", chan_default, usrin);
- printf("\x1b[35mprivmsg #%s :%s\x1b[0m", chan_default, usrin);
+ raw("privmsg #%s :%s\r\n", cdef, usrin);
+ printf("\x1b[35mprivmsg #%s :%s\x1b[0m", cdef, usrin);
}
printf("\r\n");
}
@@ -653,6 +638,7 @@ int main(int argc, char **argv) {
}
if (pass) raw("PASS %s\r\n", pass);
if (inic) raw("%s\r\n", inic);
+ if (cdef[0] == '\0') strcpy(cdef, "?");
struct pollfd fds[2];
fds[0].fd = STDIN_FILENO;
@@ -662,21 +648,39 @@ int main(int argc, char **argv) {
char usrin[MSG_MAX], promptc[CHA_MAX];
+ struct State l;
+
+ l.buf = usrin;
+ l.buflen = MSG_MAX;
+ l.prompt = promptc;
+ stateSet(&l, usrin, promptc);
+
+ int editReturnFlag = 0;
+
if (enableRawMode(STDIN_FILENO) == -1) return -1;
for (;;) {
int poll_res = poll(fds, 2, -1);
if (poll_res != -1) {
if (fds[0].revents & POLLIN) {
- snprintf(promptc, CHA_MAX, "[\x1b[35m#%s\x1b[0m] ", chan_default);
- edit(usrin, MSG_MAX, promptc);
- handleUserInput(usrin);
+ editReturnFlag = edit(&l, promptc);
+ if (editReturnFlag > 0) {
+ handleUserInput(l.buf);
+ snprintf(promptc, CHA_MAX, "[\x1b[35m#%s\x1b[0m] ", cdef);
+ stateSet(&l, usrin, promptc);
+ } else if (editReturnFlag < 0) {
+ printf("\r\n");
+ return EXIT_SUCCESS;
+ }
+ refreshLine(&l);
}
if (fds[1].revents & POLLIN) {
int rc = handleServerMessage();
if (rc != 0) {
if (rc == -2) return EXIT_FAILURE;
return EXIT_SUCCESS;
- };
+ }
+ snprintf(promptc, CHA_MAX, "[\x1b[35m#%s\x1b[0m] ", cdef);
+ refreshLine(&l);
}
} else {
if (errno == EAGAIN) continue;