summaryrefslogtreecommitdiffstats
path: root/console/name.c
diff options
context:
space:
mode:
Diffstat (limited to 'console/name.c')
-rw-r--r--console/name.c1067
1 files changed, 1067 insertions, 0 deletions
diff --git a/console/name.c b/console/name.c
new file mode 100644
index 00000000..4e9d7134
--- /dev/null
+++ b/console/name.c
@@ -0,0 +1,1067 @@
+/* $Id$ */
+#include "bbs.h"
+
+static word_t *current = NULL;
+static char * const msg_more = "-- More --";
+
+typedef char (*arrptr)[];
+/* name complete for user ID */
+
+//-----------------------------------------------------------------------
+
+void NameList_init(struct NameList *self)
+{
+ self->size = 0;
+ self->capacity = 0;
+ self->base = NULL;
+}
+
+void NameList_delete(struct NameList *self)
+{
+ self->size = 0;
+ self->capacity = 0;
+ if(self->base)
+ free(self->base);
+ self->base = NULL;
+}
+
+void NameList_clear(struct NameList *self)
+{
+ NameList_delete(self);
+ NameList_init(self);
+}
+
+void NameList_resizefor(struct NameList *self, int size)
+{
+ int capacity = size * (IDLEN+1);
+#define MIN_CAPACITY 4096
+ if (capacity == 0) {
+ if(self->base) free(self->base);
+ self->base = NULL;
+ self->capacity = 0;
+ } else {
+ int old_capacity = self->capacity;
+ assert(capacity > 0);
+ if (self->capacity == 0)
+ self->capacity = MIN_CAPACITY;
+ //if (self->capacity > capacity && self->capacity > MIN_CAPACITY)
+ // self->capacity /= 2;
+ if (self->capacity < capacity)
+ self->capacity *= 2;
+
+ if(old_capacity != self->capacity || self->base == NULL) {
+ char (*tmp)[IDLEN+1] = (char(*)[IDLEN+1])malloc((IDLEN+1)*self->capacity);
+ assert(tmp);
+ if (self->size)
+ memcpy(tmp, self->base, (IDLEN+1)*self->size);
+ if (self->base)
+ free(self->base);
+ self->base = tmp;
+ }
+ }
+}
+
+void NameList_add(struct NameList *self, const char *name)
+{
+ NameList_resizefor(self, self->size+1);
+ strlcpy(self->base[self->size], name, IDLEN+1);
+ self->size++;
+}
+
+const char* NameList_get(struct NameList *self, int idx)
+{
+ assert(0<=idx && idx<self->size);
+ return self->base[idx];
+}
+
+static int NameList_MaxLen(const struct NameList *list, int offset, int count)
+{
+ int i;
+ int maxlen = 0;
+
+ for(i=offset; i<list->size; i++) {
+ int len = strlen(list->base[i]);
+ if (len > maxlen)
+ maxlen = len;
+ }
+ assert(maxlen <= IDLEN);
+ return maxlen;
+}
+
+int NameList_match(const struct NameList *src, struct NameList *dst, int key, int pos)
+{
+ int uckey, lckey;
+ int i;
+
+ NameList_clear(dst);
+
+ uckey = chartoupper(key);
+ if (key >= 'A' && key <= 'Z')
+ lckey = key | 0x20;
+ else
+ lckey = key;
+
+ for(i=0; i<src->size; i++) {
+ int ch = src->base[i][pos];
+ if (ch == lckey || ch == uckey)
+ NameList_add(dst, src->base[i]);
+ }
+
+ return dst->size;
+}
+
+int NameList_length(struct NameList *self)
+{
+ return self->size;
+}
+
+void NameList_sublist(struct NameList *src, struct NameList *dst, char *tag)
+{
+ int i;
+ int len;
+ NameList_clear(dst);
+
+ len = strlen(tag);
+ for(i=0; i<src->size; i++)
+ if(len==0 || strncasecmp(src->base[i], tag, len)==0)
+ NameList_add(dst, src->base[i]);
+}
+
+int NameList_remove(struct NameList *self, const char *name)
+{
+ int i;
+ for(i=0; i<self->size; i++)
+ if(strcasecmp(self->base[i], name)==0) {
+ strcpy(self->base[i], self->base[self->size-1]);
+
+ self->size--;
+ NameList_resizefor(self, self->size);
+ return 1;
+ }
+ return 0;
+}
+
+int NameList_search(const struct NameList *self, const char *name)
+{
+ int i;
+ for(i=0; i<self->size; i++)
+ if (strcasecmp(self->base[i], name)==0)
+ return 1;
+ return 0;
+}
+
+//-----------------------------------------------------------------------
+
+static int
+UserMaxLen(char cwlist[][IDLEN + 1], int cwnum, int morenum,
+ int count)
+{
+ int len, max = 0;
+
+ while (count-- > 0 && morenum < cwnum) {
+ len = strlen(cwlist[morenum++]);
+ if (len > max)
+ max = len;
+ }
+ /* assert max IDLEN */
+ if(max > IDLEN)
+ max = IDLEN+1;
+ return max;
+}
+
+static int
+UserSubArray(char cwbuf[][IDLEN + 1], char cwlist[][IDLEN + 1],
+ int cwnum, int key, int pos)
+{
+ int key2, num = 0;
+ int n, ch;
+
+ key = chartoupper(key);
+
+ if (key >= 'A' && key <= 'Z')
+ key2 = key | 0x20;
+ else
+ key2 = key;
+
+ for (n = 0; n < cwnum; n++) {
+ ch = cwlist[n][pos];
+ if (ch == key || ch == key2)
+ strlcpy(cwbuf[num++], cwlist[n], sizeof(cwbuf[num]));
+ }
+ return num;
+}
+
+void
+FreeNameList(void)
+{
+ word_t *p, *temp;
+
+ for (p = toplev; p; p = temp) {
+ temp = p->next;
+ free(p->word);
+ free(p);
+ }
+}
+
+void
+CreateNameList(void)
+{
+ if (toplev)
+ FreeNameList();
+ toplev = current = NULL;
+}
+
+void
+AddNameList(const char *name)
+{
+ word_t *node;
+
+ node = (word_t *) malloc(sizeof(word_t));
+ node->next = NULL;
+ node->word = (char *)malloc(strlen(name) + 1);
+ strcpy(node->word, name);
+
+ if (toplev)
+ current = current->next = node;
+ else
+ current = toplev = node;
+}
+
+int
+RemoveNameList(const char *name)
+{
+ word_t *curr, *prev = NULL;
+
+ for (curr = toplev; curr; curr = curr->next) {
+ if (!strcmp(curr->word, name)) {
+ if (prev == NULL)
+ toplev = curr->next;
+ else
+ prev->next = curr->next;
+
+ if (curr == current)
+ current = prev;
+ free(curr->word);
+ free(curr);
+ return 1;
+ }
+ prev = curr;
+ }
+ return 0;
+}
+
+static inline int
+InList(const word_t * list, const char *name)
+{
+ const word_t *p;
+
+ for (p = list; p; p = p->next)
+ if (!strcasecmp(p->word, name))
+ return 1;
+ return 0;
+}
+
+int
+InNameList(const char *name)
+{
+ return InList(toplev, name);
+}
+
+void
+ShowNameList(int row, int column, const char *prompt)
+{
+ word_t *p;
+
+ move(row, column);
+ clrtobot();
+ outs(prompt);
+
+ column = 80;
+ for (p = toplev; p; p = p->next) {
+ row = strlen(p->word) + 1;
+ if (column + row > 76) {
+ column = row;
+ outc('\n');
+ } else {
+ column += row;
+ outc(' ');
+ }
+ outs(p->word);
+ }
+}
+
+void
+ToggleNameList(int *reciper, const char *listfile, const char *msg)
+{
+ FILE *fp;
+ char genbuf[200];
+
+ if ((fp = fopen(listfile, "r"))) {
+ while (fgets(genbuf, STRLEN, fp)) {
+ char *space = strpbrk(genbuf, str_space);
+ if (space) *space = '\0';
+ if (!genbuf[0])
+ continue;
+ if (!InNameList(genbuf)) {
+ AddNameList(genbuf);
+ (*reciper)++;
+ } else {
+ RemoveNameList(genbuf);
+ (*reciper)--;
+ }
+ }
+ fclose(fp);
+ ShowNameList(3, 0, msg);
+ }
+}
+
+static int
+NumInList(const word_t * list)
+{
+ register int i;
+
+ for (i = 0; list; i++)
+ list = list->next;
+ return i;
+}
+
+int
+chkstr(char *otag, const char *tag, const char *name)
+{
+ char ch;
+ const char *oname = name;
+
+ while (*tag) {
+ ch = *name++;
+ if (*tag != chartoupper(ch))
+ return 0;
+ tag++;
+ }
+ if (*tag && *name == '\0')
+ strcpy(otag, oname);
+ return 1;
+}
+
+static word_t *
+GetSubList(char *tag, word_t * list)
+{
+ word_t *wlist, *wcurr;
+ char tagbuf[STRLEN];
+ int n;
+
+ wlist = wcurr = NULL;
+ for (n = 0; tag[n]; n++)
+ tagbuf[n] = chartoupper(tag[n]);
+ tagbuf[n] = '\0';
+
+ while (list) {
+ if (chkstr(tag, tagbuf, list->word)) {
+ register word_t *node;
+
+ node = (word_t *) malloc(sizeof(word_t));
+ node->word = list->word;
+ node->next = NULL;
+ if (wlist)
+ wcurr->next = node;
+ else
+ wlist = node;
+ wcurr = node;
+ }
+ list = list->next;
+ }
+ return wlist;
+}
+
+static void
+ClearSubList(word_t * list)
+{
+ struct word_t *tmp_list;
+
+ while (list) {
+ tmp_list = list->next;
+ free(list);
+ list = tmp_list;
+ }
+}
+
+static int
+MaxLen(const word_t * list, int count)
+{
+ int len = strlen(list->word);
+ int t;
+
+ while (list && count) {
+ if ((t = strlen(list->word)) > len)
+ len = t;
+ list = list->next;
+ count--;
+ }
+ return len;
+}
+
+/* TODO use namecomplete2() instead */
+void
+namecomplete(const char *prompt, char *data)
+{
+ char *temp;
+ word_t *cwlist, *morelist;
+ int x, y, origx, scrx;
+ int ch;
+ int count = 0;
+ int clearbot = NA;
+
+ if (toplev == NULL)
+ AddNameList("");
+ cwlist = GetSubList("", toplev);
+ morelist = NULL;
+ temp = data;
+
+ outs(prompt);
+ clrtoeol();
+ getyx(&y, &x);
+ scrx = origx = x;
+ data[count] = 0;
+
+ while (1)
+ {
+ // print input field again
+ move(y, scrx); outc(' '); clrtoeol(); move(y, scrx);
+ outs(ANSI_COLOR(7));
+ prints("%-*s", IDLEN + 1, data);
+ outs(ANSI_RESET);
+ move(y, scrx + count);
+
+ // get input
+ if ((ch = igetch()) == EOF)
+ break;
+
+ if (ch == '\n' || ch == '\r') {
+ *temp = '\0';
+ // outc('\n');
+ if (NumInList(cwlist) == 1)
+ strcpy(data, cwlist->word);
+ else if (!InList(cwlist, data))
+ data[0] = '\0';
+ ClearSubList(cwlist);
+ break;
+ }
+ if (ch == ' ') {
+ int col, len;
+
+ if (NumInList(cwlist) == 1) {
+ strcpy(data, cwlist->word);
+ count = strlen(data);
+ temp = data + count;
+ continue;
+ }
+ clearbot = YEA;
+ col = 0;
+ if (!morelist)
+ morelist = cwlist;
+ len = MaxLen(morelist, p_lines);
+ move(2, 0);
+ clrtobot();
+ printdash("相關資訊一覽表", 0);
+ while (len + col < t_columns) {
+ int i;
+
+ for (i = p_lines; (morelist) && (i > 0); i--) {
+ move(3 + (p_lines - i), col);
+ outs(morelist->word);
+ morelist = morelist->next;
+ }
+ col += len + 2;
+ if (!morelist)
+ break;
+ len = MaxLen(morelist, p_lines);
+ }
+ if (morelist) {
+ vmsg(msg_more);
+ }
+ continue;
+ }
+ if (ch == '\177' || ch == '\010') {
+ if (temp == data)
+ continue;
+ temp--;
+ count--;
+ *temp = '\0';
+ ClearSubList(cwlist);
+ cwlist = GetSubList(data, toplev);
+ morelist = NULL;
+ continue;
+ }
+ if (count < STRLEN && isprint(ch)) {
+ word_t *node;
+
+ *temp++ = ch;
+ count++;
+ *temp = '\0';
+ node = GetSubList(data, cwlist);
+ if (node == NULL) {
+ temp--;
+ *temp = '\0';
+ count--;
+ continue;
+ }
+ ClearSubList(cwlist);
+ cwlist = node;
+ morelist = NULL;
+ }
+ }
+ if (ch == EOF)
+ /* longjmp(byebye, -1); */
+ raise(SIGHUP); /* jochang: don't know if this is
+ * necessary... */
+ outc('\n');
+ if (clearbot) {
+ move(2, 0);
+ clrtobot();
+ }
+ if (*data) {
+ move(y, origx);
+ outs(data);
+ outc('\n');
+ }
+}
+
+void
+namecomplete2(struct NameList *namelist, const char *prompt, char *data)
+{
+ char *temp;
+ int x, y, origx, scrx;
+ int ch;
+ int count = 0;
+ int clearbot = NA;
+ struct NameList sublist;
+ int viewoffset = 0;
+
+ NameList_init(&sublist);
+
+ NameList_sublist(namelist, &sublist, "");
+ temp = data;
+
+ outs(prompt);
+ clrtoeol();
+ getyx(&y, &x);
+ scrx = origx = x;
+ data[count] = 0;
+ viewoffset = 0;
+
+ while (1)
+ {
+ // print input field
+ move(y, scrx); outc(' '); clrtoeol(); move(y, scrx);
+ outs(ANSI_COLOR(7));
+ prints("%-*s", IDLEN + 1, data);
+ outs(ANSI_RESET);
+ move(y, scrx + count);
+
+ // get input
+ if ((ch = igetch()) == EOF)
+ break;
+
+ if (ch == '\n' || ch == '\r') {
+ *temp = '\0';
+ if (NameList_length(&sublist)==1)
+ strcpy(data, NameList_get(&sublist, 0));
+ else if (!NameList_search(&sublist, data))
+ data[0] = '\0';
+ NameList_delete(&sublist);
+ break;
+ }
+ if (ch == ' ') {
+ int col, len;
+
+ if (NameList_length(&sublist) == 1) {
+ strcpy(data, NameList_get(&sublist, 0));
+ count = strlen(data);
+ temp = data + count;
+ continue;
+ }
+ clearbot = YEA;
+ col = 0;
+ len = NameList_MaxLen(&sublist, viewoffset, p_lines);
+ move(2, 0);
+ clrtobot();
+ printdash("相關資訊一覽表", 0);
+ while (len + col < t_columns) {
+ int i;
+
+ for (i = p_lines; viewoffset < NameList_length(&sublist) && (i > 0); i--) {
+ move(3 + (p_lines - i), col);
+ outs(NameList_get(&sublist, viewoffset));
+ viewoffset++;
+ }
+ col += len + 2;
+ if (viewoffset == NameList_length(&sublist)) {
+ viewoffset = 0;
+ break;
+ }
+ len = NameList_MaxLen(&sublist, viewoffset, p_lines);
+ }
+ if (viewoffset < NameList_length(&sublist)) {
+ vmsg(msg_more);
+ }
+ continue;
+ }
+ if (ch == '\177' || ch == '\010') {
+ if (temp == data)
+ continue;
+ temp--;
+ count--;
+ *temp = '\0';
+ NameList_sublist(namelist, &sublist, data);
+ viewoffset = 0;
+ continue;
+ }
+ if (count < STRLEN && isprint(ch)) {
+ struct NameList tmplist;
+ NameList_init(&tmplist);
+
+ *temp++ = ch;
+ count++;
+ *temp = '\0';
+
+ NameList_sublist(&sublist, &tmplist, data);
+ if (NameList_length(&tmplist)==0) {
+ NameList_delete(&tmplist);
+ temp--;
+ *temp = '\0';
+ count--;
+ continue;
+ }
+ NameList_delete(&sublist);
+ sublist = tmplist;
+ viewoffset = 0;
+ }
+ }
+ if (ch == EOF)
+ /* longjmp(byebye, -1); */
+ raise(SIGHUP); /* jochang: don't know if this is
+ * necessary... */
+ outc('\n');
+ if (clearbot) {
+ move(2, 0);
+ clrtobot();
+ }
+ if (*data) {
+ move(y, origx);
+ outs(data);
+ outc('\n');
+ }
+}
+
+void
+usercomplete(const char *prompt, char *data)
+{
+ char *temp;
+ char *cwbuf, *cwlist;
+ int cwnum, x, y, origx, scrx;
+ int clearbot = NA, count = 0, morenum = 0;
+ char ch;
+ int dashdirty = 0;
+
+ /* TODO 節省記憶體. (不過這個 function 不常占記憶體...) */
+ cwbuf = malloc(MAX_USERS * (IDLEN + 1));
+ cwlist = u_namearray((arrptr) cwbuf, &cwnum, "");
+ temp = data;
+
+ outs(prompt);
+ clrtoeol();
+ getyx(&y, &x);
+ scrx = origx = x;
+ data[count] = 0;
+
+ while (1)
+ {
+ // print input field again
+ move(y, scrx); outc(' '); clrtoeol(); move(y, scrx);
+ outs(ANSI_COLOR(7));
+ prints("%-*s", IDLEN + 1, data);
+ outs(ANSI_RESET);
+ move(y, scrx + count);
+
+ // get input
+ if ((ch = igetch()) == EOF)
+ break;
+
+ if (ch == '\n' || ch == '\r') {
+ int i;
+ char *ptr;
+
+ *temp = '\0';
+ outc('\n');
+ ptr = cwlist;
+ for (i = 0; i < cwnum; i++) {
+ if (strncasecmp(data, ptr, IDLEN + 1) == 0) {
+ strcpy(data, ptr);
+ break;
+ }
+ ptr += IDLEN + 1;
+ }
+ if (i == cwnum)
+ data[0] = '\0';
+ break;
+
+ } else if (ch == '\177' || ch == '\010') {
+ if (temp == data)
+ continue;
+ temp--;
+ count--;
+ *temp = '\0';
+ cwlist = u_namearray((arrptr) cwbuf, &cwnum, data);
+ morenum = 0;
+ continue;
+
+ } else if (!(count <= IDLEN && isprint((int)ch))) {
+
+ /* invalid input */
+ continue;
+
+ } else if (ch != ' ') {
+
+ int n;
+
+ *temp++ = ch;
+ *temp = '\0';
+ n = UserSubArray((arrptr) cwbuf, (arrptr) cwlist, cwnum, ch, count);
+
+ if (n > 0) {
+ /* found something */
+ cwlist = cwbuf;
+ count++;
+ cwnum = n;
+ morenum = 0;
+ continue;
+ }
+ /* no break, no continue, list later. */
+ }
+
+ /* finally, list available users. */
+ {
+ int col, len;
+
+ if (ch == ' ' && cwnum == 1) {
+ if(dashdirty)
+ {
+ move(2,0);
+ clrtoeol();
+ printdash(cwlist, 0);
+ }
+ strcpy(data, cwlist);
+ count = strlen(data);
+ temp = data + count;
+ continue;
+ }
+
+ clearbot = YEA;
+ col = 0;
+
+ len = UserMaxLen((arrptr) cwlist, cwnum, morenum, p_lines);
+ move(2, 0);
+ clrtobot();
+ printdash("使用者代號一覽表", 0);
+ dashdirty = 0;
+
+ if(ch != ' ')
+ {
+ /* no such user */
+ move(2,0);
+ outs("- 目前無使用者 ");
+ outs(data);
+ outs(" ");
+ temp--;
+ *temp = '\0';
+ dashdirty = 1;
+ }
+
+ while (len + col < t_columns-1) {
+
+ int i;
+
+ for (i = 0; morenum < cwnum && i < p_lines; i++) {
+ move(3 + i, col);
+ prints("%.*s ", IDLEN,
+ cwlist + (IDLEN + 1) * morenum++);
+ }
+ col += len + 2;
+ if (morenum >= cwnum)
+ break;
+ len = UserMaxLen((arrptr) cwlist, cwnum, morenum, p_lines);
+ }
+ if (morenum < cwnum) {
+ move(b_lines, 0); clrtoeol();
+ outs(msg_more);
+ // vmsg(msg_more);
+ } else
+ morenum = 0;
+
+ continue;
+ }
+ }
+ free(cwbuf);
+ if (ch == EOF)
+ /* longjmp(byebye, -1); */
+ raise(SIGHUP); /* jochang: don't know if this is necessary */
+ outc('\n');
+ if (clearbot) {
+ move(2, 0);
+ clrtobot();
+ }
+ if (*data) {
+ move(y, origx);
+ outs(data);
+ outc('\n');
+ }
+}
+
+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_COLOR(7));
+ // data[ptr] = 0;
+ prints("%-*s", len, data);
+ outs(ANSI_RESET);
+ move(y, scrx + ptr);
+
+ // get input
+ if ((ch = igetch()) == EOF)
+ break;
+
+ if (ch == '\n' || ch == '\r') {
+ 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] = '\n';
+ 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) {
+ vmsg(msg_more);
+ }
+ continue;
+
+ } else if (ch == '\177' || ch == '\010') { /* 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;
+ }
+ }
+ }
+
+ outc('\n');
+ if (clearbot) {
+ move(2, 0);
+ clrtobot();
+ }
+ if (*data) {
+ move(y, origx);
+ outs(data);
+ outc('\n');
+ }
+ 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;
+}