/* $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 && idxsize); return self->base[idx]; } static int NameList_MaxLen(const struct NameList *list, int offset, int count) { int i; int maxlen = 0; for(i=offset; isize; 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; isize; 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; isize; 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; isize; 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; isize; 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; }