+ /* Cursor to left edge, then the prompt */
+ cursorToLeft(current);
+ outputChars(current, prompt, plen);
+
+ /* Now the current buffer content */
+
+ /* Need special handling for control characters.
+ * If we hit 'cols', stop.
+ */
+ b = 0; /* unwritted bytes */
+ n = 0; /* How many control chars were written */
+ for (i = 0; i < chars; i++) {
+ int ch;
+ int w = utf8_tounicode(buf + b, &ch);
+ if (ch < ' ') {
+ n++;
+ }
+ if (pchars + i + n >= current->cols) {
+ break;
+ }
+ if (ch < ' ') {
+ /* A control character, so write the buffer so far */
+ outputChars(current, buf, b);
+ buf += b + w;
+ b = 0;
+ outputControlChar(current, ch + '@');
+ if (i < pos) {
+ backup++;
+ }
+ }
+ else {
+ b += w;
+ }
+ }
+ outputChars(current, buf, b);
+
+ /* Erase to right, move cursor to original position */
+ eraseEol(current);
+ setCursorPos(current, pos + pchars + backup);
+}
+
+static void set_current(struct current *current, const char *str)
+{
+ strncpy(current->buf, str, current->bufmax);
+ current->buf[current->bufmax - 1] = 0;
+ current->len = strlen(current->buf);
+ current->pos = current->chars = utf8_strlen(current->buf, current->len);
+}
+
+static int has_room(struct current *current, int bytes)
+{
+ return current->len + bytes < current->bufmax - 1;
+}
+
+/**
+ * Removes the char at 'pos'.
+ *
+ * Returns 1 if the line needs to be refreshed, 2 if not
+ * and 0 if nothing was removed
+ */
+static int remove_char(struct current *current, int pos)
+{
+ if (pos >= 0 && pos < current->chars) {
+ int p1, p2;
+ int ret = 1;
+ p1 = utf8_index(current->buf, pos);
+ p2 = p1 + utf8_index(current->buf + p1, 1);
+
+#ifdef USE_TERMIOS
+ /* optimise remove char in the case of removing the last char */
+ if (current->pos == pos + 1 && current->pos == current->chars) {
+ if (current->buf[pos] >= ' ' && utf8_strlen(current->prompt, -1) + utf8_strlen(current->buf, current->len) < current->cols - 1) {
+ ret = 2;
+ fd_printf(current->fd, "\b \b");
+ }
+ }
+#endif
+
+ /* Move the null char too */
+ memmove(current->buf + p1, current->buf + p2, current->len - p2 + 1);
+ current->len -= (p2 - p1);
+ current->chars--;
+
+ if (current->pos > pos) {
+ current->pos--;
+ }
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * Insert 'ch' at position 'pos'
+ *
+ * Returns 1 if the line needs to be refreshed, 2 if not
+ * and 0 if nothing was inserted (no room)
+ */
+static int insert_char(struct current *current, int pos, int ch)
+{
+ char buf[3];
+ int n = utf8_getchars(buf, ch);
+
+ if (has_room(current, n) && pos >= 0 && pos <= current->chars) {
+ int p1, p2;
+ int ret = 1;
+ p1 = utf8_index(current->buf, pos);
+ p2 = p1 + n;
+
+#ifdef USE_TERMIOS
+ /* optimise the case where adding a single char to the end and no scrolling is needed */
+ if (current->pos == pos && current->chars == pos) {
+ if (ch >= ' ' && utf8_strlen(current->prompt, -1) + utf8_strlen(current->buf, current->len) < current->cols - 1) {
+ IGNORE_RC(write(current->fd, buf, n));
+ ret = 2;
+ }
+ }
+#endif
+
+ memmove(current->buf + p2, current->buf + p1, current->len - p1);
+ memcpy(current->buf + p1, buf, n);
+ current->len += n;
+
+ current->chars++;
+ if (current->pos >= pos) {
+ current->pos++;
+ }
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * Captures up to 'n' characters starting at 'pos' for the cut buffer.
+ *
+ * This replaces any existing characters in the cut buffer.
+ */
+static void capture_chars(struct current *current, int pos, int n)
+{
+ if (pos >= 0 && (pos + n - 1) < current->chars) {
+ int p1 = utf8_index(current->buf, pos);
+ int nbytes = utf8_index(current->buf + p1, n);
+
+ if (nbytes) {
+ free(current->capture);
+ /* Include space for the null terminator */
+ current->capture = (char *)malloc(nbytes + 1);
+ memcpy(current->capture, current->buf + p1, nbytes);
+ current->capture[nbytes] = '\0';
+ }
+ }
+}
+
+/**
+ * Removes up to 'n' characters at cursor position 'pos'.
+ *
+ * Returns 0 if no chars were removed or non-zero otherwise.
+ */
+static int remove_chars(struct current *current, int pos, int n)
+{
+ int removed = 0;
+
+ /* First save any chars which will be removed */
+ capture_chars(current, pos, n);
+
+ while (n-- && remove_char(current, pos)) {
+ removed++;
+ }
+ return removed;
+}
+/**
+ * Inserts the characters (string) 'chars' at the cursor position 'pos'.
+ *
+ * Returns 0 if no chars were inserted or non-zero otherwise.
+ */
+static int insert_chars(struct current *current, int pos, const char *chars)
+{
+ int inserted = 0;
+
+ while (*chars) {
+ int ch;
+ int n = utf8_tounicode(chars, &ch);
+ if (insert_char(current, pos, ch) == 0) {
+ break;
+ }
+ inserted++;
+ pos++;
+ chars += n;
+ }
+ return inserted;
+}
+
+#ifndef NO_COMPLETION
+static linenoiseCompletionCallback *completionCallback = NULL;
+
+static void beep() {
+#ifdef USE_TERMIOS
+ fprintf(stderr, "\x7");
+ fflush(stderr);
+#endif
+}
+
+static void freeCompletions(linenoiseCompletions *lc) {
+ size_t i;
+ for (i = 0; i < lc->len; i++)
+ free(lc->cvec[i]);
+ free(lc->cvec);
+}
+
+static int completeLine(struct current *current) {
+ linenoiseCompletions lc = { 0, NULL };
+ int c = 0;
+
+ completionCallback(current->buf,&lc);
+ if (lc.len == 0) {
+ beep();
+ } else {
+ size_t stop = 0, i = 0;
+
+ while(!stop) {
+ /* Show completion or original buffer */
+ if (i < lc.len) {
+ struct current tmp = *current;
+ tmp.buf = lc.cvec[i];
+ tmp.pos = tmp.len = strlen(tmp.buf);
+ tmp.chars = utf8_strlen(tmp.buf, tmp.len);
+ refreshLine(current->prompt, &tmp);
+ } else {
+ refreshLine(current->prompt, current);
+ }
+
+ c = fd_read(current);
+ if (c == -1) {
+ break;
+ }
+
+ switch(c) {
+ case '\t': /* tab */
+ i = (i+1) % (lc.len+1);
+ if (i == lc.len) beep();
+ break;
+ case 27: /* escape */
+ /* Re-show original buffer */
+ if (i < lc.len) {
+ refreshLine(current->prompt, current);
+ }
+ stop = 1;
+ break;
+ default:
+ /* Update buffer and return */
+ if (i < lc.len) {
+ set_current(current,lc.cvec[i]);
+ }
+ stop = 1;
+ break;
+ }
+ }
+ }
+
+ freeCompletions(&lc);
+ return c; /* Return last read character */
+}
+
+/* Register a callback function to be called for tab-completion. */
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
+ completionCallback = fn;
+}
+
+void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
+ lc->cvec = (char **)realloc(lc->cvec,sizeof(char*)*(lc->len+1));
+ lc->cvec[lc->len++] = strdup(str);
+}
+
+#endif
+
+static int linenoisePrompt(struct current *current) {
+ int history_index = 0;