1/* Copyright (C) 2019 Sören Tempel2 *3 * This program is free software: you can redistribute it and/or modify4 * it under the terms of the GNU General Public License as published by5 * the Free Software Foundation, either version 3 of the License, or6 * (at your option) any later version.7 *8 * This program is distributed in the hope that it will be useful,9 * but WITHOUT ANY WARRANTY; without even the implied warranty of10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11 * GNU General Public License for more details.12 *13 * You should have received a copy of the GNU General Public License14 * along with this program. If not, see <https://www.gnu.org/licenses/>.15 */1617#include <assert.h>18#include <err.h>19#include <histedit.h>20#include <stdlib.h>21#include <string.h>22#include <unistd.h>23#include <wchar.h>2425#include <sys/types.h>2627#include "complete.h"2829typedef struct _comp comp;3031struct _comp {32 wchar_t *val;33 comp *nxt;34};3536static int wcomp;37static compfn cfn;38static comp *comps;3940#define cursoridx(LINEINFO) \41 ((LINEINFO->cursor) - (LINEINFO)->buffer)4243static size_t44getin(const wchar_t **dest, const LineInfoW *linfo)45{46 ssize_t n;47 size_t len;48 const wchar_t *ws;4950 len = linfo->lastchar - linfo->buffer;51 if (!wcomp) {52 *dest = linfo->buffer;53 return len;54 }5556 n = cursoridx(linfo);57 while (n-- && !iswspace(linfo->buffer[n]))58 ;59 ws = (n >= 0) ? &linfo->buffer[++n] : linfo->buffer;6061 *dest = ws;62 return linfo->lastchar - ws;63}6465static comp *66nxtcomp(comp *lcomp)67{68 comp *nxt;6970 assert(lcomp);7172 nxt = lcomp->nxt;73 if (!nxt && comps != lcomp)74 nxt = comps;7576 return nxt; /* NULL if there is no new completion */77}7879static void80freecomps(void)81{82 comp *c, *n;8384 c = comps;85 while (c) {86 n = c->nxt;87 free(c->val);88 free(c);89 c = n;90 }9192 comps = NULL;93}9495static void96callcomp(const wchar_t *input, size_t len)97{98 char *mbs;99 size_t mbslen;100101 if ((mbslen = wcsnrtombs(NULL, &input, len, 0, NULL)) == (size_t)-1)102 err(EXIT_FAILURE, "wcsnrtombs failed");103104 if (!(mbs = calloc(mbslen + 1, sizeof(wchar_t))))105 err(EXIT_FAILURE, "calloc failed");106 if (wcsnrtombs(mbs, &input, len, mbslen + 1, NULL) == (size_t)-1)107 err(EXIT_FAILURE, "wcsnrtombs failed");108109 cfn(mbs, mbslen);110}111112void113addcomp(char *str)114{115 comp *c;116 size_t mbslen;117118 if ((mbslen = mbstowcs(NULL, str, 0)) == (size_t)-1)119 err(EXIT_FAILURE, "mbstowcs failed");120121 if (!(c = malloc(sizeof(*c))))122 err(EXIT_FAILURE, "malloc failed");123 if (!(c->val = calloc(mbslen + 1, sizeof(wchar_t))))124 err(EXIT_FAILURE, "calloc failed");125126 if (mbstowcs(c->val, str, mbslen + 1) == (size_t)-1)127 err(EXIT_FAILURE, "mbstowcs failed");128129 if (!comps) {130 comps = c;131 comps->nxt = NULL;132 } else {133 c->nxt = comps->nxt;134 comps->nxt = c;135 }136}137138void139initcomp(compfn fn, int wordcomp)140{141 cfn = fn;142 wcomp = wordcomp;143}144145unsigned char146complete(EditLine *el, int ch)147{148 comp *c;149 size_t inlen, cidx, ccur;150 const LineInfoW *linfo;151 const wchar_t *input;152 static wchar_t *linput;153 static comp *lcomp;154 static size_t bcur;155156 (void)ch;157158 linfo = el_wline(el);159 inlen = getin(&input, linfo);160161 if (!linput || inlen != wcslen(linput) || wcsncmp(input, linput, inlen)) {162 freecomps();163 lcomp = NULL;164165 callcomp(input, inlen);166 if (!comps)167 return CC_ERROR;168 c = comps;169170 bcur = cursoridx(linfo); /* base char cursor */171 } else {172 ccur = cursoridx(linfo); /* current char cursor */173 assert(ccur >= bcur);174 el_deletestr(el, ccur - bcur);175176 if (!(c = nxtcomp(lcomp)))177 return CC_REFRESH; /* deletestr changed line */178 }179180 cidx = bcur;181 if (wcomp) {182 assert(input >= linfo->buffer);183 cidx -= input - linfo->buffer;184 }185186 if (cidx >= wcslen(c->val))187 goto next;188 if (el_winsertstr(el, &c->val[cidx]) == -1)189 errx(EXIT_FAILURE, "el_winsertstr failed");190191next:192 inlen = getin(&input, el_wline(el));193194 free(linput);195 if (!(linput = calloc(inlen + 1, sizeof(wchar_t))))196 err(EXIT_FAILURE, "calloc failed");197 memcpy(linput, input, inlen * sizeof(wchar_t));198 lcomp = c;199200 return CC_REFRESH;201}