/* $Id$ */
#include "bbs.h"
/* ----------------------------------------------------- */
/* set file path for boards/user home */
/* ----------------------------------------------------- */
static const char * const str_home_file = "home/%c/%s/%s";
static const char * const str_board_file = "boards/%c/%s/%s";
static const char * const str_board_n_file = "boards/%c/%s/%s.%d";
static const char * const str_dotdir = FN_DIR;
/* XXX set*() all assume buffer size = PATHLEN */
void
sethomepath(char *buf, const char *userid)
{
assert(is_validuserid(userid));
snprintf(buf, PATHLEN, "home/%c/%s", userid[0], userid);
}
void
sethomedir(char *buf, const char *userid)
{
assert(is_validuserid(userid));
snprintf(buf, PATHLEN, str_home_file, userid[0], userid, str_dotdir);
}
void
sethomeman(char *buf, const char *userid)
{
assert(is_validuserid(userid));
snprintf(buf, PATHLEN, str_home_file, userid[0], userid, "man");
}
void
sethomefile(char *buf, const char *userid, const char *fname)
{
assert(is_validuserid(userid));
assert(fname[0]);
snprintf(buf, PATHLEN, str_home_file, userid[0], userid, fname);
}
void
setuserfile(char *buf, const char *fname)
{
assert(is_validuserid(cuser.userid));
assert(fname[0]);
snprintf(buf, PATHLEN, str_home_file, cuser.userid[0], cuser.userid, fname);
}
void
setapath(char *buf, const char *boardname)
{
//assert(boardname[0]);
snprintf(buf, PATHLEN, "man/boards/%c/%s", boardname[0], boardname);
}
void
setadir(char *buf, const char *path)
{
//assert(path[0]);
snprintf(buf, PATHLEN, "%s/%s", path, str_dotdir);
}
void
setbpath(char *buf, const char *boardname)
{
//assert(boardname[0]);
snprintf(buf, PATHLEN, "boards/%c/%s", boardname[0], boardname);
}
void
setbdir(char *buf, const char *boardname)
{
//assert(boardname[0]);
snprintf(buf, PATHLEN, str_board_file, boardname[0], boardname,
(currmode & MODE_DIGEST ? fn_mandex : str_dotdir));
}
void
setbfile(char *buf, const char *boardname, const char *fname)
{
//assert(boardname[0]);
assert(fname[0]);
snprintf(buf, PATHLEN, str_board_file, boardname[0], boardname, fname);
}
void
setbnfile(char *buf, const char *boardname, const char *fname, int n)
{
//assert(boardname[0]);
assert(fname[0]);
snprintf(buf, PATHLEN, str_board_n_file, boardname[0], boardname, fname, n);
}
/*
* input direct
* output buf: copy direct
* fname: direct 的檔名部分
*/
void
setdirpath(char *buf, const char *direct, const char *fname)
{
char *p;
strcpy(buf, direct);
p = strrchr(buf, '/');
assert(p);
strlcpy(p + 1, fname, PATHLEN-(p+1-buf));
}
/**
* 給定文章標題 title,傳回指到主題的部分的指標。
* @param title
*/
char *
subject(char *title)
{
if (!strncasecmp(title, str_reply, 3)) {
title += 3;
if (*title == ' ')
title++;
}
return title;
}
int is_validuserid(const char *id)
{
int len, i;
if(id==NULL)
return 0;
len = strlen(id);
if (len < 2 || len>IDLEN)
return 0;
if (!isalpha(id[0]))
return 0;
for (i = 1; i < len; i++)
if (!isalnum(id[i]))
return 0;
return 1;
}
int
is_uBM(const char *list, const char *id)
{
register int len;
if (list[0] == '[')
list++;
if (list[0] > ' ') {
len = strlen(id);
do {
if (!strncasecmp(list, id, len)) {
list += len;
if ((*list == 0) || (*list == '/') ||
(*list == ']') || (*list == ' '))
return 1;
}
if ((list = strchr(list, '/')) != NULL)
list++;
else
break;
} while (1);
}
return 0;
}
int
is_BM(const char *list)
{
if (is_uBM(list, cuser.userid)) {
cuser.userlevel |= PERM_BM; /* Ptt 自動加上BM的權利 */
return 1;
}
return 0;
}
int
userid_is_BM(const char *userid, const char *list)
{
register int ch, len;
// TODO merge with is_uBM
ch = list[0];
if ((ch > ' ') && (ch < 128)) {
len = strlen(userid);
do {
if (!strncasecmp(list, userid, len)) {
ch = list[len];
if ((ch == 0) || (ch == '/') || (ch == ']'))
return 1;
}
while ((ch = *list++)) {
if (ch == '/')
break;
}
} while (ch);
}
return 0;
}
int
belong(const char *filelist, const char *key)
{
return file_exist_record(filelist, key);
}
#ifndef _BBS_UTIL_C_ /* getdata_buf */
time4_t
gettime(int line, time4_t dt, const char*head)
{
char yn[7];
int i;
struct tm *ptime = localtime4(&dt), endtime;
time_t t;
memcpy(&endtime, ptime, sizeof(struct tm));
snprintf(yn, sizeof(yn), "%4d", ptime->tm_year + 1900);
move(line, 0); outs(head);
i=strlen(head);
do {
getdata_buf(line, i, " 西元年:", yn, 5, LCECHO);
// signed: limited on (2037, ...)
// unsigned: limited on (..., 1970)
// let's restrict inside the boundary.
} while ((endtime.tm_year = atoi(yn) - 1900) < 70 || endtime.tm_year > 135);
snprintf(yn, sizeof(yn), "%d", ptime->tm_mon + 1);
do {
getdata_buf(line, i+15, "月:", yn, 3, LCECHO);
} while ((endtime.tm_mon = atoi(yn) - 1) < 0 || endtime.tm_mon > 11);
snprintf(yn, sizeof(yn), "%d", ptime->tm_mday);
do {
getdata_buf(line, i+24, "日:", yn, 3, LCECHO);
} while ((endtime.tm_mday = atoi(yn)) < 1 || endtime.tm_mday > 31);
snprintf(yn, sizeof(yn), "%d", ptime->tm_hour);
do {
getdata_buf(line, i+33, "時(0-23):", yn, 3, LCECHO);
} while ((endtime.tm_hour = atoi(yn)) < 0 || endtime.tm_hour > 23);
t = mktime(&endtime);
/* saturation check */
if(t < 0)
t = 1;
if(t > INT_MAX)
t = INT_MAX;
return t;
}
// synchronize 'now'
void syncnow(void)
{
#ifdef OUTTA_TIMER
now = SHM->GV2.e.now;
#else
now = time(0);
#endif
}
#endif
#ifndef _BBS_UTIL_C_
/* 這一區都是有關於畫面處理的, 故 _BBS_UTIL_C_ 不須要 */
#ifdef PLAY_ANGEL
void
pressanykey_or_callangel(){
int ch;
outmsg(
ANSI_COLOR(1;34;44) " ▄▄▄▄ "
ANSI_COLOR(32) "H " ANSI_COLOR(36) "呼叫小天使" ANSI_COLOR(34)
" ▄▄▄▄" ANSI_COLOR(37;44) " 請按 " ANSI_COLOR(36) "空白鍵 "
ANSI_COLOR(37) "繼續 " ANSI_COLOR(1;34)
"▄▄▄▄▄▄▄▄▄▄▄▄▄▄ " ANSI_RESET);
do {
ch = igetch();
if (ch == 'h' || ch == 'H'){
CallAngel();
break;
}
} while ((ch != ' ') && (ch != KEY_LEFT) && (ch != '\r') && (ch != '\n'));
move(b_lines, 0);
clrtoeol();
refresh();
}
#endif
/**
* 給 printf format 的參數,印到最底下一行。
* 傳回使用者的選擇(char)。
*/
char
getans(const char *fmt,...)
{
char msg[256];
char ans[3];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg , sizeof(msg), fmt, ap);
va_end(ap);
getdata(b_lines, 0, msg, ans, sizeof(ans), LCECHO);
return ans[0];
}
int
getkey(const char *fmt,...)
{
char msg[256], i;
va_list ap;
va_start(ap, fmt);
i = vsnprintf(msg , sizeof(msg), fmt, ap);
va_end(ap);
return vmsg(msg);
}
static const char *msg_pressanykey_full =
ANSI_COLOR(37;44) " 請按" ANSI_COLOR(36) " 任意鍵 " ANSI_COLOR(37) "繼續 " ANSI_COLOR(34);
#define msg_pressanykey_full_len (18)
// what is 200/1431/506/201?
static const char* msg_pressanykey_trail =
ANSI_COLOR(33;46) " [按任意鍵繼續] " ANSI_RESET;
#define msg_pressanykey_trail_len (16+1+4) /* 4 for head */
int
vmsg(const char *msg)
{
int len = msg ? strlen(msg) : 0;
int i = 0;
if(len == 0) msg = NULL;
move(b_lines, 0);
clrtoeol();
if(!msg)
{
/* msg_pressanykey_full */
int w = (t_columns - msg_pressanykey_full_len - 8) / 2;
int pad = 0;
outs(ANSI_COLOR(1;34;44) " ");
pad += 1;
for (i = 0; i < w; i += 2)
outs("▄"), pad+=2;
outs(msg_pressanykey_full), pad+= msg_pressanykey_full_len;
/* pad now points to position of current cursor. */
pad = t_columns - pad -2 ;
/* pad is now those left . */
if (pad > 0)
{
for (i = 0; i <= pad-2; i += 2)
outs("▄");
if (i == pad-1)
outs(" ");
}
outs(ANSI_RESET);
} else {
/* msg_pressanykey_trail */
outs(ANSI_COLOR(1;36;44) " ◆ ");
if(len >= t_columns - msg_pressanykey_trail_len)
len = t_columns - msg_pressanykey_trail_len;
while (i++ < len)
outc(*msg++);
i--;
while (i++ < t_columns - msg_pressanykey_trail_len)
outc(' ');
outs(msg_pressanykey_trail);
}
do {
i = igetch();
} while( i == 0 );
move(b_lines, 0);
clrtoeol();
return i;
}
int
vmsgf(const char *fmt,...)
{
char msg[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
msg[sizeof(msg)-1] = 0;
return vmsg(msg);
}
/**
* 從第 y 列開始 show 出 filename 檔案中的前 lines 行。
* mode 為 output 的模式,參數同 strip_ansi。
* @param filename
* @param x
* @param lines
* @param mode: SHOWFILE_*, see modes.h
* @return 失敗傳回 0,否則為 1。
* 2 表示有 PttPrints 碼
*/
int
show_file(const char *filename, int y, int lines, int mode)
{
FILE *fp;
char buf[1024];
int ret = 1;
int strpmode = STRIP_ALL;
if (mode & SHOWFILE_ALLOW_COLOR)
strpmode = ONLY_COLOR;
if (mode & SHOWFILE_ALLOW_MOVE)
strpmode = NO_RELOAD;
if (y >= 0)
move(y, 0);
clrtoln(lines + y);
if ((fp = fopen(filename, "r"))) {
while (fgets(buf, sizeof(buf), fp) && lines--)
{
move(y++, 0);
if (mode == SHOWFILE_RAW)
{
outs(buf);
}
else if ((mode & SHOWFILE_ALLOW_STAR) && (strstr(buf, ESC_STR "*") != NULL))
{
// because Ptt_prints escapes are not so often,
// let's try harder to detect it.
outs(Ptt_prints(buf, sizeof(buf), strpmode));
ret = 2;
} else {
// ESC is very common...
strip_ansi(buf, buf, strpmode);
outs(buf);
}
}
fclose(fp);
outs(ANSI_RESET); // prevent some broken Welcome file
} else
return 0;
return ret;
}
void
bell(void)
{
char c;
c = Ctrl('G');
write(1, &c, 1);
}
int
search_num(int ch, int max)
{
int clen = 1, y = b_lines - msg_occupied;
char genbuf[10];
genbuf[0] = ch; genbuf[1] = 0;
clen = getdata_buf(y, 0,
" 跳至第幾項: ", genbuf, sizeof(genbuf)-1, NUMECHO);
move(y, 0); clrtoeol();
genbuf[clen] = '\0';
if (genbuf[0] == '\0')
return -1;
clen = atoi(genbuf);
if (clen == 0)
return 0;
if (clen > max)
return max;
return clen - 1;
}
/**
* 在螢幕左上角 show 出 "【title】"
* @param title
*/
void
stand_title(const char *title)
{
clear();
prints(ANSI_COLOR(1;37;46) "【 %s 】" ANSI_RESET "\n", title);
}
void
cursor_show(int row, int column)
{
move(row, column);
outs(STR_CURSOR);
move(row, column + 1);
}
void
cursor_clear(int row, int column)
{
move(row, column);
outs(STR_UNCUR);
}
int
cursor_key(int row, int column)
{
int ch;
cursor_show(row, column);
ch = igetch();
move(row, column);
outs(STR_UNCUR);
return ch;
}
void
printdash(const char *mesg, int msglen)
{
int head = 0, tail;
if(msglen <= 0)
msglen = strlen(mesg);
if (mesg)
head = (msglen + 1) >> 1;
tail = head;
while (head++ < t_columns/2-2)
outc('-');
if (tail) {
outc(' ');
if(mesg) outs(mesg);
outc(' ');
}
while (tail++ < t_columns/2-2)
outc('-');
outc('\n');
}
int
log_user(const char *fmt, ...)
{
char msg[256], filename[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg , sizeof(msg), fmt, ap);
va_end(ap);
sethomefile(filename, cuser.userid, "USERLOG");
return log_filef(filename, LOG_CREAT, "%s: %s %s", cuser.userid, msg, Cdate(&now));
}
void
show_help(const char * const helptext[])
{
const char *str;
int i;
clear();
for (i = 0; (str = helptext[i]); i++) {
if (*str == '\0')
prints(ANSI_COLOR(1) "【 %s 】" ANSI_COLOR(0) "\n", str + 1);
else if (*str == '\01')
prints("\n" ANSI_COLOR(36) "【 %s 】" ANSI_RESET "\n", str + 1);
else
prints(" %s\n", str);
}
#ifdef PLAY_ANGEL
if (HasUserPerm(PERM_LOGINOK))
pressanykey_or_callangel();
else
#endif
pressanykey();
}
void
show_helpfile(const char *helpfile)
{
clear();
show_file((char *)helpfile, 0, b_lines, SHOWFILE_ALLOW_ALL);
#ifdef PLAY_ANGEL
if (HasUserPerm(PERM_LOGINOK))
pressanykey_or_callangel();
else
#endif
pressanykey();
}
#endif // _BBS_UTIL_C_
/* ----------------------------------------------------- */
/* use mmap() to malloc large memory in CRITICAL_MEMORY */
/* ----------------------------------------------------- */
#ifdef CRITICAL_MEMORY
void *MALLOC(int size)
{
int *p;
p = (int *)mmap(NULL, (size + 4), PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
p[0] = size;
#if defined(DEBUG) && !defined(_BBS_UTIL_C_)
vmsgf("critical malloc %d bytes", size);
#endif
return (void *)&p[1];
}
void FREE(void *ptr)
{
int size = ((int *)ptr)[-1];
munmap((void *)(&(((int *)ptr)[-1])), size);
#if defined(DEBUG) && !defined(_BBS_UTIL_C_)
vmsgf("critical free %d bytes", size);
#endif
}
#endif
unsigned
DBCS_StringHash(const char *s)
{
return fnv1a_32_dbcs_strcase(s, FNV1_32_INIT);
}
inline int *intbsearch(int key, const int *base0, int nmemb)
{
/* 改自 /usr/src/lib/libc/stdlib/bsearch.c ,
專給搜 int array 用的, 不透過 compar function 故較快些 */
const char *base = (const char *)base0;
size_t lim;
int *p;
for (lim = nmemb; lim != 0; lim >>= 1) {
p = (int *)(base + (lim >> 1) * 4);
if( key == *p )
return p;
if( key > *p ){/* key > p: move right */
base = (char *)p + 4;
lim--;
} /* else move left */
}
return (NULL);
}
inline unsigned int *
uintbsearch(const unsigned int key, const unsigned int *base0, const int nmemb)
{
/* 改自 /usr/src/lib/libc/stdlib/bsearch.c ,
專給搜 int array 用的, 不透過 compar function 故較快些 */
const char *base = (const char *)base0;
size_t lim;
unsigned int *p;
for (lim = nmemb; lim != 0; lim >>= 1) {
p = (unsigned int *)(base + (lim >> 1) * 4);
if( key == *p )
return p;
if( key > *p ){/* key > p: move right */
base = (char *)p + 4;
lim--;
} /* else move left */
}
return (NULL);
}
/* AIDS */
aidu_t fn2aidu(char *fn)
{
aidu_t aidu = 0;
aidu_t type = 0;
aidu_t v1 = 0;
aidu_t v2 = 0;
char *sp = fn;
if(fn == NULL)
return 0;
switch(*(sp ++))
{
case 'M':
type = 0;
break;
case 'G':
type = 1;
break;
default:
return 0;
break;
}
if(*(sp ++) != '.')
return 0;
v1 = strtoul(sp, &sp, 10);
if(sp == NULL)
return 0;
if(*sp != '.' || *(sp + 1) != 'A')
return 0;
sp += 2;
if(*(sp ++) == '.')
{
v2 = strtoul(sp, &sp, 16);
if(sp == NULL)
return 0;
}
aidu = ((type & 0xf) << 44) | ((v1 & 0xffffffff) << 12) | (v2 & 0xfff);
return aidu;
}
/* IMPORTANT:
* size of buf must be at least 8+1 bytes
*/
char *aidu2aidc(char *buf, aidu_t aidu)
{
const char aidu2aidc_table[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
const int aidu2aidc_tablesize = sizeof(aidu2aidc_table) - 1;
char *sp = buf + 8;
aidu_t v;
*(sp --) = '\0';
while(sp >= buf)
{
/* FIXME: 能保證 aidu2aidc_tablesize 是 2 的冪次的話,
這裡可以改用 bitwise operation 做 */
v = aidu % aidu2aidc_tablesize;
aidu = aidu / aidu2aidc_tablesize;
*(sp --) = aidu2aidc_table[v];
}
return buf;
}
/* IMPORTANT:
* size of fn must be at least FNLEN bytes
*/
char *aidu2fn(char *fn, aidu_t aidu)
{
aidu_t type = ((aidu >> 44) & 0xf);
aidu_t v1 = ((aidu >> 12) & 0xffffffff);
aidu_t v2 = (aidu & 0xfff);
if(fn == NULL)
return NULL;
snprintf(fn, FNLEN - 1, "%c.%d.A.%03X", ((type == 0) ? 'M' : 'G'), (unsigned int)v1, (unsigned int)v2);
fn[FNLEN - 1] = '\0';
return fn;
}
aidu_t aidc2aidu(char *aidc)
{
char *sp = aidc;
aidu_t aidu = 0;
if(aidc == NULL)
return 0;
while(*sp != '\0' && /* ignore trailing spaces */ *sp != ' ')
{
aidu_t v = 0;
/* FIXME: 查表法會不會比較快? */
if(*sp >= '0' && *sp <= '9')
v = *sp - '0';
else if(*sp >= 'A' && *sp <= 'Z')
v = *sp - 'A' + 10;
else if(*sp >= 'a' && *sp <= 'z')
v = *sp - 'a' + 36;
else if(*sp == '-')
v = 62;
else if(*sp == '_')
v = 63;
else
return 0;
aidu <<= 6;
aidu |= (v & 0x3f);
sp ++;
}
return aidu;
}
int search_aidu(char *bfile, aidu_t aidu)
{
char fn[FNLEN];
int fd;
fileheader_t fhs[64];
int len, i;
int pos = 0;
int found = 0;
int lastpos = 0;
if(aidu2fn(fn, aidu) == NULL)
return -1;
if((fd = open(bfile, O_RDONLY, 0)) < 0)
return -1;
while(!found && (len = read(fd, fhs, sizeof(fhs))) > 0)
{
len /= sizeof(fileheader_t);
for(i = 0; i < len; i ++)
{
int l;
if(strcmp(fhs[i].filename, fn) == 0 ||
((aidu & 0xfff) == 0 && (l = strlen(fhs[i].filename)) > 6 &&
strncmp(fhs[i].filename, fn, l) == 0))
{
if(fhs[i].filemode & FILE_BOTTOM)
{
lastpos = pos;
}
else
{
found = 1;
break;
}
}
pos ++;
}
}
close(fd);
return (found ? pos : (lastpos ? lastpos : -1));
}
/* end of AIDS */