/* $Id: name.c,v 1.6 2002/05/24 16:45:39 in2 Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include "config.h"
#include "pttstruct.h"
#include "common.h"
#include "proto.h"
#include "perm.h"
extern struct bcache_t *brdshm;
extern struct utmpfile_t *utmpshm;
extern userec_t cuser;
extern char *str_space;
extern int p_lines; /* a Page of Screen line numbers: tlines-4 */
extern int b_lines; /* Screen bottom line number: t_lines-1 */
word_t *toplev = NULL;
static word_t *current = NULL;
static char *msg_more = "\033[7m-- More --\033[m";
typedef char (*arrptr)[];
/* name complete for user ID */
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;
}
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)
strcpy(cwbuf[num++], cwlist[n]);
}
return num;
}
static void FreeNameList() {
word_t *p, *temp;
for(p = toplev; p; p = temp) {
temp = p->next;
free(p->word);
free(p);
}
}
void CreateNameList() {
if(toplev)
FreeNameList();
toplev = current = NULL;
}
void AddNameList(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(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;
}
int InNameList(char *name) {
word_t *p;
for(p = toplev; p; p = p->next)
if(!strcmp(p->word, name))
return 1;
return 0;
}
void ShowNameList(int row, int column, 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, char *listfile, char *msg) {
FILE *fp;
char genbuf[200];
if((fp = fopen(listfile, "r"))) {
while(fgets(genbuf, STRLEN, fp)) {
strtok(genbuf, str_space);
if(!InNameList(genbuf)) {
AddNameList(genbuf);
(*reciper)++;
} else {
RemoveNameList(genbuf);
(*reciper)--;
}
}
fclose(fp);
ShowNameList(3, 0, msg);
}
}
static int NumInList(word_t *list) {
register int i;
for(i = 0; list; i++)
list = list->next;
return i;
}
int chkstr(char *otag, char *tag, char *name) {
char ch, *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(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;
}
void namecomplete(char *prompt, char *data) {
char *temp;
word_t *cwlist, *morelist;
int x, y, origx, origy;
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);
getyx(&origy, &origx);
standout();
prints("%*s", IDLEN + 1, "");
standend();
move(y, x);
refresh();
while((ch = igetch()) != EOF) {
if(ch == '\n' || ch == '\r') {
*temp = '\0';
outc('\n');
if(NumInList(cwlist) == 1)
strcpy(data, cwlist->word);
ClearSubList(cwlist);
break;
}
if(ch == ' ') {
int col, len;
if(NumInList(cwlist) == 1) {
strcpy(data, cwlist->word);
move(y, x);
outs(data + count);
count = strlen(data);
temp = data + count;
getyx(&y, &x);
continue;
}
clearbot = YEA;
col = 0;
if(!morelist)
morelist = cwlist;
len = MaxLen(morelist, p_lines);
move(2, 0);
clrtobot();
printdash("相關資訊一覽表");
while(len + col < 80) {
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) {
move(b_lines, 0);
outs(msg_more);
}
move(y, x);
continue;
}
if(ch == '\177' || ch == '\010') {
if(temp == data)
continue;
temp--;
count--;
*temp = '\0';
ClearSubList(cwlist);
cwlist = GetSubList(data, toplev);
morelist = NULL;
x--;
move(y, x);
outc(' ');
move(y, x);
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;
move(y, x);
outc(ch);
x++;
}
}
if(ch == EOF)
/* longjmp(byebye, -1); */
raise(SIGHUP); /* jochang: don't know if this is necessary... */
outc('\n');
refresh();
if(clearbot) {
move(2, 0);
clrtobot();
}
if(*data) {
move(origy, origx);
outs(data);
outc('\n');
}
}
void usercomplete(char *prompt, char *data) {
char *temp;
char *cwbuf, *cwlist;
int cwnum, x, y, origx, origy;
int clearbot = NA, count = 0, morenum = 0;
char ch;
cwbuf = malloc(MAX_USERS * (IDLEN + 1));
cwlist = u_namearray((arrptr)cwbuf, &cwnum, "");
temp = data;
outs(prompt);
clrtoeol();
getyx(&y, &x);
getyx(&origy, &origx);
standout();
prints("%*s", IDLEN + 1, "");
standend();
move(y, x);
while((ch = igetch()) != EOF) {
if(ch == '\n' || ch == '\r') {
int i;
char *ptr;
*temp = '\0';
outc('\n');
ptr = (char *)cwlist;
for(i = 0; i < cwnum; i++) {
if(strncasecmp(data, ptr, IDLEN + 1) == 0)
strcpy(data, ptr);
ptr += IDLEN + 1;
}
break;
} else if(ch == ' ') {
int col, len;
if(cwnum == 1) {
strcpy(data, (char *)cwlist);
move(y, x);
outs(data + count);
count = strlen(data);
temp = data + count;
getyx(&y, &x);
continue;
}
clearbot = YEA;
col = 0;
len = UserMaxLen((arrptr)cwlist, cwnum, morenum, p_lines);
move(2, 0);
clrtobot();
printdash("使用者代號一覽表");
while(len + col < 79) {
int i;
for(i = 0; morenum < cwnum && i < p_lines; i++) {
move(3 + i, col);
prints("%s ", 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);
outs(msg_more);
} else
morenum = 0;
move(y, x);
continue;
} else if(ch == '\177' || ch == '\010') {
if(temp == data)
continue;
temp--;
count--;
*temp = '\0';
cwlist = u_namearray((arrptr)cwbuf, &cwnum, data);
morenum = 0;
x--;
move(y, x);
outc(' ');
move(y, x);
continue;
} else if(count < STRLEN && isprint(ch)) {
int n;
*temp++ = ch;
*temp = '\0';
n = UserSubArray((arrptr)cwbuf, (arrptr)cwlist, cwnum, ch, count);
if(n == 0) {
temp--;
*temp = '\0';
continue;
}
cwlist = cwbuf;
count++;
cwnum = n;
morenum = 0;
move(y, x);
outc(ch);
x++;
}
}
free(cwbuf);
if(ch == EOF)
/* longjmp(byebye, -1); */
raise(SIGHUP); /* jochang: don't know if this is necessary */
outc('\n');
refresh();
if(clearbot) {
move(2, 0);
clrtobot();
}
if(*data) {
move(origy, origx);
outs(data);
outc('\n');
}
}
int gnc_findbound(char *str, int *START, int *END,
size_t nmemb, int (*compar)(int, char *, int))
{
int start, end, mid, cmp, strl;
strl = strlen(str);
start = 0, end = nmemb - 1;
while( start != end && ((mid = (start + end) / 2) != start) ){
cmp = compar(mid, str, strl);
//cmp = strncasecmp(brdshm->sorted[0][mid]->brdname, str, strl);
if( cmp >= 0 )
end = mid;
else
start = mid;
}
++start;
//if( strncasecmp(brdshm->sorted[0][start]->brdname, str, strl) != 0 ){
if( compar(start, str, strl) != 0 ){
*START = *END = -1;
return -1;
}
*START = start;
end = nmemb - 1;
while( start != end && ((mid = (start + end) / 2) != start) ){
cmp = compar(mid, str, strl);
//cmp = strncasecmp(brdshm->sorted[0][mid]->brdname, str, strl);
if( cmp <= 0 )
start = mid;
else
end = mid;
}
*END = start;
return 0;
}
int gnc_completeone(char *data, int start, int end,
int (*permission)(int), char* (*getname)(int))
{
int i, count, at;
for( i = start, at = count = 0 ; i <= end && count < 2 ; ++i )
if( permission(i) ){
at = i;
++count;
}
if( count == 1 ){
strcpy(data, getname(at));
return at;
}
return -1;
}
int generalnamecomplete(char *prompt, char *data, int len, size_t nmemb,
int (*compar)(int, char *, int),
int (*permission)(int), char* (*getname)(int))
{
int x, y, origx, origy, ch, i, morelist = -1, col, ret = -1;
int start, end, ptr;
int clearbot = NA;
outs(prompt);
clrtoeol();
getyx(&y, &x);
getyx(&origy, &origx);
standout();
prints("%*s", IDLEN + 1, "");
standend();
move(y, x);
refresh();
ptr = 0;
data[ptr] = 0;
while( (ch = igetch()) != EOF ){
if( ch == '\n' || ch == '\r' ){
data[ptr] = 0;
outc('\n');
if( ptr != 0 ){
gnc_findbound(data, &start, &end, nmemb, compar);
ret = gnc_completeone(data, start, end, permission, getname);
}
else
ptr = -1;
break;
}
else if( ch == ' ' ){
if( ptr == 0 )
continue;
if( morelist == -1 ){
if( gnc_findbound(data, &start, &end, nmemb, compar) == -1 )
continue;
if( gnc_completeone(data, start, end,
permission, getname) >= 0 ){
move(origy, origx);
outs(data);
ptr = strlen(data);
getyx(&y, &x);
continue;
}
morelist = start;
}
else if( morelist > end )
continue;
clearbot = YEA;
move(2, 0);
clrtobot();
printdash("相關資訊一覽表");
col = 0;
while(len + col < 79) {
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 ){
move(b_lines, 0);
outs(msg_more);
}
move(y, x);
continue;
}
else if(ch == '\177' || ch == '\010') { /* backspace */
if( ptr == 0 )
continue;
morelist = -1;
--ptr; --x;
data[ptr] = 0;
move(y, x);
outc(' ');
move(y, x);
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{
move(y, x);
outc(ch);
x++;
}
}
}
}
outc('\n');
refresh();
if(clearbot) {
move(2, 0);
clrtobot();
}
if(*data) {
move(origy, origx);
outs(data);
outc('\n');
}
return ret;
}
/* general complete functions (brdshm) */
int completeboard_compar(int where, char *str, int len)
{
return strncasecmp(brdshm->sorted[0][where]->brdname, str, len);
}
int completeboard_permission(int where)
{
return Ben_Perm(brdshm->sorted[0][where]);
}
char *completeboard_getname(int where)
{
return brdshm->sorted[0][where]->brdname;
}
/* general complete functions (utmpshm) */
int completeutmp_compar(int where, char *str, int len)
{
return strncasecmp(utmpshm->sorted[utmpshm->currsorted][0][where]->userid,
str, len);
}
int completeutmp_permission(int where)
{
return (HAS_PERM(PERM_SYSOP) || HAS_PERM(PERM_SEECLOAK) ||
!utmpshm->sorted[utmpshm->currsorted][0][where]->invisible);
}
char *completeutmp_getname(int where)
{
return utmpshm->sorted[utmpshm->currsorted][0][where]->userid;
}