diff options
Diffstat (limited to 'console/name.c')
-rw-r--r-- | console/name.c | 1067 |
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; +} |