/* $Id$ */
#include "bbs.h"
#define MORE_MSG "按空白鍵可列出更多項目"
void
ToggleVector(struct Vector *list, int *recipient, const char *listfile, const char *msg)
{
FILE *fp;
char genbuf[STRLEN];
if ((fp = fopen(listfile, "r"))) {
while (fgets(genbuf, sizeof(genbuf), fp)) {
char *space = strpbrk(genbuf, str_space);
if (space) *space = '\0';
if (!genbuf[0])
continue;
if (Vector_search(list, genbuf) < 0) {
Vector_add(list, genbuf);
(*recipient)++;
} else {
Vector_remove(list, genbuf);
(*recipient)--;
}
}
fclose(fp);
ShowVector(list, 3, 0, msg, 0);
}
}
int
ShowVector(struct Vector *list, int row, int col, const char * msg, int idx)
{
int i, len;
move(row, col);
clrtobot();
if (msg) {
outs(msg);
row++;
}
col = 0;
len = Vector_MaxLen(list, idx, b_lines - row);
while (len + col < t_columns) {
for (i = row; idx < Vector_length(list) && (i < b_lines); i++) {
move(i, col);
outs(Vector_get(list, idx));
idx++;
}
col += len + 2;
if (idx == Vector_length(list)) {
idx = 0;
break;
}
len = Vector_MaxLen(list, idx, b_lines - row);
}
return idx;
}
struct namecomplete_int {
const struct Vector * base;
struct Vector sublist;
int idx, dirty;
};
static int
nc_cb_peek(int key, VGET_RUNTIME *prt, void *instance)
{
struct namecomplete_int * nc_int = (struct namecomplete_int *) instance;
int tmp;
prt->buf[prt->iend] = 0;
if (nc_int->dirty < 0) {
Vector_sublist(nc_int->base, &nc_int->sublist, prt->buf);
nc_int->idx = 0;
nc_int->dirty = 0;
}
switch (key) {
case KEY_ENTER:
if (Vector_length(&nc_int->sublist) == 1)
strlcpy(prt->buf, Vector_get(&nc_int->sublist, 0), prt->len);
else if ((tmp = Vector_search(&nc_int->sublist, prt->buf)) >= 0)
strlcpy(prt->buf, Vector_get(&nc_int->sublist, tmp), prt->len);
else
prt->buf[0] = '\0';
prt->icurr = prt->iend = strlen(prt->buf);
break;
case ' ':
if (Vector_length(&nc_int->sublist) == 1) {
strlcpy(prt->buf, Vector_get(&nc_int->sublist, 0), prt->len);
prt->icurr = prt->iend = strlen(prt->buf);
return VGETCB_NEXT;
}
move(2, 0);
clrtobot();
printdash("相關資訊一覽表", 0);
nc_int->idx = ShowVector(&nc_int->sublist, 3, 0, NULL, nc_int->idx);
if (nc_int->idx < Vector_length(&nc_int->sublist))
vshowmsg(MORE_MSG);
return VGETCB_NEXT;
break;
case KEY_BS2: case KEY_BS: /* backspace */
nc_int->dirty = -1;
break;
case KEY_HOME: case Ctrl('A'):
case KEY_END: case Ctrl('E'):
case KEY_LEFT: case Ctrl('B'):
case KEY_RIGHT: case Ctrl('F'):
case KEY_DEL: case Ctrl('D'):
case Ctrl('Y'):
case Ctrl('K'):
return VGETCB_NEXT;
break;
default:
if (isprint(key)) {
struct Vector tmplist;
prt->buf[prt->iend] = key;
prt->buf[prt->iend + 1] = 0;
Vector_init(&tmplist, IDLEN + 1);
Vector_sublist(&nc_int->sublist, &tmplist, prt->buf);
if (Vector_length(&tmplist) == 0) {
Vector_delete(&tmplist);
prt->buf[prt->iend] = 0;
return VGETCB_NEXT;
} else {
Vector_delete(&nc_int->sublist);
nc_int->sublist = tmplist;
nc_int->idx = 0;
prt->buf[prt->iend] = 0;
}
}
}
return VGETCB_NONE;
}
void
namecomplete2(const struct Vector *namelist, const char *prompt, char *data)
{
struct namecomplete_int nc_int = {
.base = namelist,
.dirty = 0,
};
VGET_CALLBACKS vcb = {
.peek = nc_cb_peek,
.data = NULL,
.post = NULL,
};
outs(prompt);
clrtoeol();
Vector_init(&nc_int.sublist, IDLEN+1);
Vector_sublist(namelist, &nc_int.sublist, "");
vgetstring(data, IDLEN + 1, VGET_ASCII_ONLY, NULL, &vcb, &nc_int);
Vector_delete(&nc_int.sublist);
}
void
usercomplete(const char *prompt, char *data)
{
struct Vector namelist;
Vector_init_const(&namelist, SHM->userid[0], MAX_USERS, IDLEN+1);
namecomplete2(&namelist, prompt, data);
}
static int
gnc_findbound(char *str, int *START, int *END,
size_t nmemb, gnc_comp_func compar)
{
int start, end, mid, cmp, strl;
strl = strlen(str);
start = -1, end = nmemb - 1;
/* The first available element is always in the half-open interval
* (start, end]. (or `end'-th it self if start == end) */
while (end > start + 1) {
mid = (start + end) / 2;
cmp = (*compar)(mid, str, strl);
if (cmp >= 0)
end = mid;
else
start = mid;
}
if ((*compar)(end, str, strl) != 0) {
*START = *END = -1;
return -1;
}
*START = end;
start = end;
end = nmemb;
/* The last available element is always in the half-open interval
* [start, end). (or `start'-th it self if start == end) */
while (end > start + 1) {
mid = (start + end) / 2;
cmp = (*compar)(mid, str, strl);
if (cmp <= 0)
start = mid;
else
end = mid;
}
*END = start;
return 0;
}
static int
gnc_complete(char *data, int *start, int *end,
gnc_perm_func permission, gnc_getname_func getname)
{
int i, count, first = -1, last = *end;
if (*start < 0 || *end < 0)
return 0;
for (i = *start, count = 0; i <= *end; ++i)
if ((*permission)(i)) {
if (first == -1)
first = i;
last = i;
++count;
}
if (count == 1)
strcpy(data, (*getname)(first));
*start = first;
*end = last;
return count;
}
int
generalnamecomplete(const char *prompt, char *data, int len, size_t nmemb,
gnc_comp_func compar, gnc_perm_func permission,
gnc_getname_func getname)
{
int x, y, origx, scrx, ch, i, morelist = -1, col, ret = -1;
int start, end, ptr;
int clearbot = NA;
outs(prompt);
clrtoeol();
getyx(&y, &x);
scrx = origx = x;
ptr = 0;
data[ptr] = 0;
start = 0; end = nmemb - 1;
while (1)
{
// print input field again
move(y, scrx); outc(' '); clrtoeol(); move(y, scrx);
outs(ANSI_REVERSE);
// data[ptr] = 0;
prints("%-*s", len, data);
outs(ANSI_RESET);
move(y, scrx + ptr);
// get input
if ((ch = igetch()) == EOF)
break;
if (ch == KEY_ENTER) {
data[ptr] = 0;
outc('\n');
if (ptr != 0) {
gnc_findbound(data, &start, &end, nmemb, compar);
if (gnc_complete(data, &start, &end, permission, getname)
== 1 || (*compar)(start, data, len) == 0) {
strcpy(data, (*getname)(start));
ret = start;
} else {
data[0] = '\0';
ret = -1;
}
} else
ptr = -1;
break;
} else if (ch == ' ') {
if (morelist == -1) {
if (gnc_findbound(data, &start, &end, nmemb, compar) == -1)
continue;
i = gnc_complete(data, &start, &end, permission, getname);
if (i == 1) {
ptr = strlen(data);
continue;
} else {
char* first = (*getname)(start);
i = ptr;
while (first[i] && (*compar)(end, first, i + 1) == 0) {
data[i] = first[i];
++i;
}
data[i] = '\0';
if (i != ptr) { /* did complete several words */
ptr = i;
}
}
morelist = start;
} else if (morelist > end)
continue;
clearbot = YEA;
move(2, 0);
clrtobot();
printdash("相關資訊一覽表", 0);
col = 0;
while (len + col < t_columns-1) {
for (i = 0; morelist <= end && i < p_lines; ++morelist) {
if ((*permission)(morelist)) {
move(3 + i, col);
prints("%s ", (*getname)(morelist));
++i;
}
}
col += len + 2;
}
if (morelist != end + 1) {
vshowmsg(MORE_MSG);
}
continue;
} else if (ch == KEY_BS2 || ch == KEY_BS) { /* backspace */
if (ptr == 0)
continue;
morelist = -1;
--ptr;
data[ptr] = 0;
continue;
} else if (isprint(ch) && ptr <= (len - 2)) {
morelist = -1;
data[ptr] = ch;
++ptr;
data[ptr] = 0;
if (gnc_findbound(data, &start, &end, nmemb, compar) < 0)
data[--ptr] = 0;
else {
for (i = start; i <= end; ++i)
if ((*permission)(i))
break;
if (i == end + 1)
data[--ptr] = 0;
}
} else if (ch == KEY_UP || ch == KEY_DOWN) {
if (!InputHistoryExists(data))
InputHistoryAdd(data);
if (ch == KEY_DOWN)
InputHistoryNext(data, len);
else
InputHistoryPrev(data, len);
ptr = strlen(data);
}
}
outc('\n');
if (clearbot) {
move(2, 0);
clrtobot();
}
if (*data) {
move(y, origx);
outs(data);
outc('\n');
// save the history
InputHistoryAdd(data);
}
return ret;
}
/* general complete functions (brdshm) */
int
completeboard_compar(int where, const char *str, int len)
{
boardheader_t *bh = &bcache[SHM->bsorted[0][where]];
return strncasecmp(bh->brdname, str, len);
}
int
completeboard_permission(int where)
{
boardheader_t *bptr = &bcache[SHM->bsorted[0][where]];
return (!(bptr->brdattr & BRD_SYMBOLIC) &&
(GROUPOP() || HasBoardPerm(bptr)) &&
!(bptr->brdattr & BRD_GROUPBOARD));
}
int
complete_board_and_group_permission(int where)
{
boardheader_t *bptr = &bcache[SHM->bsorted[0][where]];
return (!(bptr->brdattr & BRD_SYMBOLIC) &&
(GROUPOP() || HasBoardPerm(bptr)));
}
char *
completeboard_getname(int where)
{
return bcache[SHM->bsorted[0][where]].brdname;
}
/* general complete functions (utmpshm) */
int
completeutmp_compar(int where, const char *str, int len)
{
userinfo_t *u = &SHM->uinfo[SHM->sorted[SHM->currsorted][0][where]];
return strncasecmp(u->userid, str, len);
}
int
completeutmp_permission(int where)
{
userinfo_t *u = &SHM->uinfo[SHM->sorted[SHM->currsorted][0][where]];
return (unlikely(HasUserPerm(PERM_SYSOP)) ||
unlikely(HasUserPerm(PERM_SEECLOAK)) ||
// !SHM->sorted[SHM->currsorted][0][where]->invisible);
isvisible(currutmp, u));
}
char *
completeutmp_getname(int where)
{
return SHM->uinfo[SHM->sorted[SHM->currsorted][0][where]].userid;
}