#include #include #include #include #include #include #include #include #include #include #include #include "keys.h" int freeLineCLI(linebuffer_t * __restrict buffer); static inline void clrscrEOL(linebuffer_t * __restrict buf) { register int i; if (buf) { write(buf->line_out, K_CR, 1); for (i = 0; i < buf->line_len; i++) write(buf->line_out, K_SPACE, 1); } } static inline void printfEOL(linebuffer_t * __restrict buf, int len, int prompt) { if (buf) { write(buf->line_out, K_CR, 1); if (prompt && buf->line_prompt) write(buf->line_out, buf->line_prompt, buf->line_bol); write(buf->line_out, buf->line_buf, len == -1 ? buf->line_eol - buf->line_bol: len); } } static inline void printfCR(linebuffer_t * __restrict buf, int prompt) { if (buf) { write(buf->line_out, K_CR, 1); if (prompt) if (prompt && buf->line_prompt) write(buf->line_out, buf->line_prompt, buf->line_bol); } } static inline void printfCLI(linebuffer_t * __restrict buf, const unsigned char *text, int textlen, int prompt) { if (buf && text && textlen) { if (prompt && buf->line_prompt) write(buf->line_out, buf->line_prompt, buf->line_bol); write(buf->line_out, text, textlen); } } static int catCh2Buf(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; int pos; unsigned char b[BUFSIZ]; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; pos = buf->line_eol - buf->line_bol; if (buf->line_mode == LINEMODE_INS) memmove(buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_buf + pos, buf->line_len - buf->line_eol); if (buf->line_mode == LINEMODE_INS || buf->line_eol == buf->line_len - 1) buf->line_len += buf->line_keys[idx].key_len; buf->line_eol += buf->line_keys[idx].key_len; memcpy(buf->line_buf + pos, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len); buf->line_buf[buf->line_len - 1] = 0; write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len); if (buf->line_mode == LINEMODE_INS) { printfCLI(buf, (const u_char*) buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_len - buf->line_eol, 0); printfEOL(buf, -1, 1); } return RETCODE_OK; } static int bufEOL(int idx, void * __restrict buffer) { if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; printfCR(buffer, 1); return RETCODE_EOL; } static int bufEOF(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len); return RETCODE_EOF; } static int bufTab(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; /* show */ write(buf->line_out, "shmink", 6); return RETCODE_OK; } static int bufUP(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; int pos; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (!buf->line_h) buf->line_h = TAILQ_FIRST(&buf->line_history); else buf->line_h = TAILQ_NEXT(buf->line_h, hist_next); if (!buf->line_h) return RETCODE_OK; clrscrEOL(buf); freeLineCLI(buf); pos = buf->line_eol - buf->line_bol; buf->line_len += buf->line_h->hist_len; buf->line_eol += buf->line_h->hist_len; memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len); buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, -1, 1); return RETCODE_OK; } static int bufDOWN(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; int pos; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (!buf->line_h) buf->line_h = TAILQ_LAST(&buf->line_history, tqHistoryHead); else buf->line_h = TAILQ_PREV(buf->line_h, tqHistoryHead, hist_next); if (!buf->line_h) return RETCODE_OK; clrscrEOL(buf); freeLineCLI(buf); pos = buf->line_eol - buf->line_bol; buf->line_len += buf->line_h->hist_len; buf->line_eol += buf->line_h->hist_len; memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len); buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, -1, 1); return RETCODE_OK; } static int bufClr(int idx, void * __restrict buffer) { if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; clrscrEOL(buffer); freeLineCLI(buffer); printfCR(buffer, 1); return RETCODE_OK; } static int bufBS(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (buf->line_bol < buf->line_eol) { clrscrEOL(buf); buf->line_eol--; buf->line_len--; memmove(buf->line_buf + buf->line_eol - buf->line_bol, buf->line_buf + buf->line_eol - buf->line_bol + 1, buf->line_len - buf->line_eol); buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, buf->line_len - 1, 1); printfEOL(buf, -1, 1); } return RETCODE_OK; } static int bufBTAB(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (buf->line_bol < buf->line_eol) { clrscrEOL(buf); buf->line_len = buf->line_eol - buf->line_bol + 1; buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, -1, 1); } return RETCODE_OK; } static int bufMode(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; buf->line_mode = !buf->line_mode ? LINEMODE_OVER : LINEMODE_INS; return RETCODE_OK; } static int bufBegin(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; buf->line_eol = buf->line_bol; printfCR(buf, 1); return RETCODE_OK; } static int bufEnd(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; buf->line_eol = buf->line_len - 1; printfEOL(buf, -1, 1); return RETCODE_OK; } static int bufLEFT(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (buf->line_bol < buf->line_eol) printfEOL(buf, --buf->line_eol - buf->line_bol, 1); return RETCODE_OK; } static int bufRIGHT(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (buf->line_eol < buf->line_len - 1) printfEOL(buf, ++buf->line_eol - buf->line_bol, 1); return RETCODE_OK; } static int bufDel(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; clrscrEOL(buf); buf->line_len--; memmove(buf->line_buf + buf->line_eol - buf->line_bol, buf->line_buf + buf->line_eol - buf->line_bol + 1, buf->line_len - buf->line_eol); buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, buf->line_len - 1, 1); printfEOL(buf, -1, 1); return RETCODE_OK; } // --------------------------------------------------------------- int bindKeyCLI(bindkey_t * __restrict key, linebuffer_t * __restrict buffer) { register int i; if (!key || !buffer) return RETCODE_ERR; for (i = 0; i < MAX_BINDKEY; i++) if (key->key_len == buffer->line_keys[i].key_len && !memcmp(key->key_ch, buffer->line_keys[i].key_ch, key->key_len)) { buffer->line_keys[i].key_func = key->key_func; return i; } return RETCODE_OK; } linebuffer_t * initCLI(int fin, int fout, const char *prompt) { linebuffer_t *buffer; bindkey_t *keys; register int i; struct termios t; memset(&t, 0, sizeof t); /* init buffer */ buffer = malloc(sizeof (linebuffer_t)); if (!buffer) return NULL; else { memset(buffer, 0, sizeof(linebuffer_t)); buffer->line_in = fin; buffer->line_out = fout; TAILQ_INIT(&buffer->line_history); if (prompt) { buffer->line_prompt = strdup(prompt); if (!buffer->line_prompt) { free(buffer); return NULL; } else { buffer->line_bol = strlen(buffer->line_prompt); buffer->line_eol = buffer->line_bol; } } } buffer->line_buf = malloc(BUFSIZ); if (!buffer->line_buf) { if (buffer->line_prompt) free(buffer->line_prompt); free(buffer); return NULL; } else { memset(buffer->line_buf, 0, BUFSIZ); buffer->line_len = 1 + buffer->line_eol; } keys = calloc(MAX_BINDKEY + 1, sizeof(bindkey_t)); if (!keys) { if (buffer->line_prompt) free(buffer->line_prompt); free(buffer->line_buf); free(buffer); return NULL; } else memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1)); /* fill key bindings */ // ascii chars & ctrl+chars for (i = 0; i < 256; i++) { *keys[i].key_ch = (unsigned char) i; keys[i].key_len = 1; if (!i || i == *K_CTRL_D) keys[i].key_func = bufEOF; if (i == *K_CTRL_M || i == *K_CTRL_J) keys[i].key_func = bufEOL; if (i == *K_CTRL_H || i == *K_BACKSPACE) keys[i].key_func = bufBS; if (i == *K_CTRL_C) keys[i].key_func = bufClr; if (i == *K_CTRL_A) keys[i].key_func = bufBegin; if (i == *K_CTRL_E) keys[i].key_func = bufEnd; if (i >= *K_SPACE && i < *K_BACKSPACE) keys[i].key_func = catCh2Buf; if (i > *K_BACKSPACE && i < 0xff) keys[i].key_func = catCh2Buf; } // alt+chars for (i = 256; i < 512; i++) { keys[i].key_ch[0] = 0x1b; keys[i].key_ch[1] = (unsigned char) i - 256; keys[i].key_len = 2; } // 3 bytes keys[i].key_len = sizeof K_F1 - 1; memcpy(keys[i].key_ch, K_F1, keys[i++].key_len); keys[i].key_len = sizeof K_F2 - 1; memcpy(keys[i].key_ch, K_F2, keys[i++].key_len); keys[i].key_len = sizeof K_F3 - 1; memcpy(keys[i].key_ch, K_F3, keys[i++].key_len); keys[i].key_len = sizeof K_F4 - 1; memcpy(keys[i].key_ch, K_F4, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F1 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F1, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F2 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F2, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F3 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F3, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F4 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F4, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F5 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F5, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F6 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F6, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F7 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F7, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F8 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F8, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F9 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F9, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F10 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F10, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F11 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F11, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_SH_F12 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F12, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F1 - 1; memcpy(keys[i].key_ch, K_CTRL_F1, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F2 - 1; memcpy(keys[i].key_ch, K_CTRL_F2, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F3 - 1; memcpy(keys[i].key_ch, K_CTRL_F3, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F4 - 1; memcpy(keys[i].key_ch, K_CTRL_F4, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F5 - 1; memcpy(keys[i].key_ch, K_CTRL_F5, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F6 - 1; memcpy(keys[i].key_ch, K_CTRL_F6, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F7 - 1; memcpy(keys[i].key_ch, K_CTRL_F7, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F8 - 1; memcpy(keys[i].key_ch, K_CTRL_F8, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F9 - 1; memcpy(keys[i].key_ch, K_CTRL_F9, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F10 - 1; memcpy(keys[i].key_ch, K_CTRL_F10, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F11 - 1; memcpy(keys[i].key_ch, K_CTRL_F11, keys[i++].key_len); keys[i].key_len = sizeof K_CTRL_F12 - 1; memcpy(keys[i].key_ch, K_CTRL_F12, keys[i++].key_len); keys[i].key_len = sizeof K_HOME - 1; keys[i].key_func = bufBegin; memcpy(keys[i].key_ch, K_HOME, keys[i++].key_len); keys[i].key_len = sizeof K_END - 1; keys[i].key_func = bufEnd; memcpy(keys[i].key_ch, K_END, keys[i++].key_len); keys[i].key_len = sizeof K_UP - 1; keys[i].key_func = bufUP; memcpy(keys[i].key_ch, K_UP, keys[i++].key_len); keys[i].key_len = sizeof K_DOWN - 1; keys[i].key_func = bufDOWN; memcpy(keys[i].key_ch, K_DOWN, keys[i++].key_len); keys[i].key_len = sizeof K_RIGHT - 1; keys[i].key_func = bufRIGHT; memcpy(keys[i].key_ch, K_RIGHT, keys[i++].key_len); keys[i].key_len = sizeof K_LEFT - 1; keys[i].key_func = bufLEFT; memcpy(keys[i].key_ch, K_LEFT, keys[i++].key_len); keys[i].key_len = sizeof K_BTAB - 1; keys[i].key_func = bufBTAB; memcpy(keys[i].key_ch, K_BTAB, keys[i++].key_len); // 4 bytes keys[i].key_len = sizeof K_INS - 1; keys[i].key_func = bufMode; memcpy(keys[i].key_ch, K_INS, keys[i++].key_len); keys[i].key_len = sizeof K_DEL - 1; keys[i].key_func = bufDel; memcpy(keys[i].key_ch, K_DEL, keys[i++].key_len); keys[i].key_len = sizeof K_PGUP - 1; memcpy(keys[i].key_ch, K_PGUP, keys[i++].key_len); keys[i].key_len = sizeof K_PGDN - 1; memcpy(keys[i].key_ch, K_PGDN, keys[i++].key_len); // 5 bytes keys[i].key_len = sizeof K_F5 - 1; memcpy(keys[i].key_ch, K_F5, keys[i++].key_len); keys[i].key_len = sizeof K_F6 - 1; memcpy(keys[i].key_ch, K_F6, keys[i++].key_len); keys[i].key_len = sizeof K_F7 - 1; memcpy(keys[i].key_ch, K_F7, keys[i++].key_len); keys[i].key_len = sizeof K_F8 - 1; memcpy(keys[i].key_ch, K_F8, keys[i++].key_len); keys[i].key_len = sizeof K_F9 - 1; memcpy(keys[i].key_ch, K_F9, keys[i++].key_len); keys[i].key_len = sizeof K_F10 - 1; memcpy(keys[i].key_ch, K_F10, keys[i++].key_len); keys[i].key_len = sizeof K_F11 - 1; memcpy(keys[i].key_ch, K_F11, keys[i++].key_len); keys[i].key_len = sizeof K_F12 - 1; memcpy(keys[i].key_ch, K_F12, keys[i++].key_len); tcgetattr(buffer->line_in, &t); t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT); t.c_iflag |= IGNBRK; t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; tcsetattr(buffer->line_in, TCSANOW, &t); buffer->line_keys = keys; return buffer; } void endCLI(linebuffer_t * __restrict buffer) { struct tagHistory *h; if (buffer) { while ((h = TAILQ_FIRST(&buffer->line_history))) { TAILQ_REMOVE(&buffer->line_history, h, hist_next); free(h); } if (buffer->line_prompt) free(buffer->line_prompt); if (buffer->line_keys) free(buffer->line_keys); if (buffer->line_buf) free(buffer->line_buf); free(buffer); buffer = NULL; } } void setPromptCLI(linebuffer_t * __restrict buffer, const char *prompt) { if (buffer) { if (buffer->line_prompt) { free(buffer->line_prompt); buffer->line_prompt = NULL; buffer->line_bol = 0; } if (prompt) { buffer->line_prompt = strdup(prompt); if (buffer->line_prompt) { buffer->line_bol = strlen(buffer->line_prompt); buffer->line_eol = buffer->line_bol; buffer->line_len = 1 + buffer->line_eol; } } } } int freeLineCLI(linebuffer_t * __restrict buffer) { int code = RETCODE_ERR; if (buffer) { if (buffer->line_buf) free(buffer->line_buf); buffer->line_buf = malloc(BUFSIZ); if (buffer->line_buf) { memset(buffer->line_buf, 0, BUFSIZ); buffer->line_eol = buffer->line_bol; buffer->line_len = 1 + buffer->line_eol; code = RETCODE_OK; } } return code; } int readLineCLI(linebuffer_t * __restrict buffer) { int code, readLen; register int i; char buf[BUFSIZ]; struct pollfd fds; if (!buffer) return RETCODE_ERR; memset(&fds, 0, sizeof fds); fds.fd = buffer->line_in; fds.events = POLLIN; printfCR(buffer, 1); while (42) { if (poll(&fds, 1, -1) < 1) return RETCODE_ERR; memset(buf, 0, sizeof buf); readLen = read(buffer->line_in, buf, BUFSIZ); if (readLen == -1) return RETCODE_ERR; if (!readLen) return RETCODE_EOF; recheck: // for (i = 0; i < readLen; i++) // printf("i=%d readLen=%d buf=%x\n", i, readLen, (u_char) buf[i]); for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--) if (readLen >= buffer->line_keys[i].key_len && !memcmp(buffer->line_keys[i].key_ch, buf, buffer->line_keys[i].key_len)) { readLen -= buffer->line_keys[i].key_len; if (readLen) memmove(buf, buf + buffer->line_keys[i].key_len, readLen); else memset(buf, 0, buffer->line_keys[i].key_len); if (buffer->line_keys[i].key_func) if ((code = buffer->line_keys[i].key_func(i, buffer))) readLen = 0; if (readLen) goto recheck; else break; } if (code) break; } return code; } int addHistoryCLI(linebuffer_t * __restrict buffer, const char * __restrict str) { struct tagHistory *h; if (!buffer) return RETCODE_ERR; if (!(h = malloc(sizeof(struct tagHistory)))) { return RETCODE_ERR; } else memset(h, 0, sizeof(struct tagHistory)); if (str) { if (!*str) { free(h); return RETCODE_OK; } h->hist_len = strlcpy(h->hist_line, str, BUFSIZ); } else { if (!*buffer->line_buf || buffer->line_len < 2) { free(h); return RETCODE_OK; } memcpy(h->hist_line, buffer->line_buf, (h->hist_len = buffer->line_len)); io_TrimStr((unsigned char*) h->hist_line); h->hist_len = strlen(h->hist_line); } TAILQ_INSERT_HEAD(&buffer->line_history, h, hist_next); return h->hist_len; } int saveHistoryCLI(linebuffer_t * __restrict buffer, const char *histfile, int lines) { FILE *f; mode_t mode; char szFName[MAXPATHLEN]; struct tagHistory *h; if (!buffer) return RETCODE_ERR; if (!histfile) strlcpy(szFName, HISTORY_FILE, MAXPATHLEN); else strlcpy(szFName, histfile, MAXPATHLEN); mode = umask(0177); f = fopen(szFName, "w"); if (!f) return RETCODE_ERR; TAILQ_FOREACH(h, &buffer->line_history, hist_next) { fprintf(f, "%s\n", h->hist_line); if (lines) lines--; else break; } fclose(f); umask(mode); return RETCODE_OK; } int loadHistoryCLI(linebuffer_t * __restrict buffer, const char *histfile) { FILE *f; char szFName[MAXPATHLEN], buf[BUFSIZ]; struct tagHistory *h; if (!buffer) return RETCODE_ERR; if (!histfile) strlcpy(szFName, HISTORY_FILE, MAXPATHLEN); else strlcpy(szFName, histfile, MAXPATHLEN); f = fopen(szFName, "r"); if (!f) return RETCODE_ERR; while (fgets(buf, BUFSIZ, f)) { if (!*buf || *buf == '#') continue; else io_TrimStr((unsigned char*) buf); if (!(h = malloc(sizeof(struct tagHistory)))) { fclose(f); return RETCODE_ERR; } else memset(h, 0, sizeof(struct tagHistory)); h->hist_len = strlcpy(h->hist_line, buf, BUFSIZ); TAILQ_INSERT_TAIL(&buffer->line_history, h, hist_next); } fclose(f); return RETCODE_OK; } inline void resetHistoryCLI(linebuffer_t * __restrict buffer) { buffer->line_h = NULL; } // ------------------------------------------------------------ int main() { int ret; bindkey_t key = { sizeof K_TAB - 1, K_TAB, bufTab }; linebuffer_t *buffer = initCLI(STDIN_FILENO, STDOUT_FILENO, CLI_PROMPT); bindKeyCLI(&key, buffer); loadHistoryCLI(buffer, NULL); while (42) { ret = readLineCLI(buffer); addHistoryCLI(buffer, NULL); printf("LINE=%s (%d)/%d/%d CODE=%d\n", buffer->line_buf, buffer->line_len, buffer->line_eol, buffer->line_bol, ret); freeLineCLI(buffer); resetHistoryCLI(buffer); if (ret == RETCODE_EOF) break; } saveHistoryCLI(buffer, NULL, HISTORY_LINES); endCLI(buffer); return 0; }