diff options
-rw-r--r-- | common/sys/string.c | 20 | ||||
-rw-r--r-- | include/cmsys.h | 8 | ||||
-rw-r--r-- | include/common.h | 2 | ||||
-rw-r--r-- | include/modes.h | 7 | ||||
-rw-r--r-- | include/proto.h | 13 | ||||
-rw-r--r-- | include/visio.h | 20 | ||||
-rw-r--r-- | mbbsd/chat.c | 10 | ||||
-rw-r--r-- | mbbsd/edit.c | 2 | ||||
-rw-r--r-- | mbbsd/io.c | 60 | ||||
-rw-r--r-- | mbbsd/talk.c | 14 | ||||
-rw-r--r-- | mbbsd/visio.c | 286 |
11 files changed, 381 insertions, 61 deletions
diff --git a/common/sys/string.c b/common/sys/string.c index 881a0e04..908f1fb4 100644 --- a/common/sys/string.c +++ b/common/sys/string.c @@ -311,6 +311,26 @@ int DBCS_RemoveIntrEscape(unsigned char *buf, int *len) return (oldl != l) ? 1 : 0; } +int DBCS_Status(const char *dbcstr, int pos) +{ + int sts = DBCS_ASCII; + const unsigned char *s = (const unsigned char*)dbcstr; + + while(pos >= 0) + { + if(sts == DBCS_LEADING) + sts = DBCS_TRAILING; + else if (*s >= 0x80) + { + sts = DBCS_LEADING; + } else { + sts = DBCS_ASCII; + } + s++, pos--; + } + return sts; +} + /* ----------------------------------------------------- */ /* 字串檢查函數:英文、數字、檔名、E-mail address */ /* ----------------------------------------------------- */ diff --git a/include/cmsys.h b/include/cmsys.h index 0401da26..879b75e9 100644 --- a/include/cmsys.h +++ b/include/cmsys.h @@ -20,10 +20,17 @@ enum STRIP_FLAG { ONLY_COLOR, // allow only colors (ESC [ .. m) NO_RELOAD // allow all known (color+move) }; + enum LOG_FLAG { LOG_CREAT = 1, }; +/* DBCS aware modes */ +enum _DBCS_STATUS { + DBCS_ASCII, + DBCS_LEADING, + DBCS_TRAILING, +}; #ifdef TIMET64 typedef int32_t time4_t; @@ -85,6 +92,7 @@ extern int strip_blank(char *cbuf, char *buf); extern int strip_ansi(char *buf, const char *str, enum STRIP_FLAG flag); extern void strip_nonebig5(unsigned char *str, int maxlen); extern int DBCS_RemoveIntrEscape(unsigned char *buf, int *len); +extern int DBCS_Status(const char *dbcstr, int pos); extern int invalid_pname(const char *str); extern int is_number(const char *p); extern char * qp_encode (char *s, size_t slen, const char *d, const char *tocode); diff --git a/include/common.h b/include/common.h index 2aabc0ff..cf390eee 100644 --- a/include/common.h +++ b/include/common.h @@ -136,6 +136,8 @@ /* 鍵盤設定 */ #define KEY_TAB 9 #define KEY_ESC 27 +#define KEY_BS (Ctrl('H')) +#define KEY_BS2 (0x7f) #define KEY_UP 0x0101 #define KEY_DOWN 0x0102 #define KEY_RIGHT 0x0103 diff --git a/include/modes.h b/include/modes.h index 178add62..7ceb7ba9 100644 --- a/include/modes.h +++ b/include/modes.h @@ -168,13 +168,6 @@ #define AUTHOR_NEXT (RS_AUTHOR | RS_FORWARD) #define AUTHOR_PREV (RS_AUTHOR) -/* DBCS aware modes */ -enum _DBCS_STATUS { - DBCS_ASCII, - DBCS_LEADING, - DBCS_TRAILING, -}; - #define SIG_PK 0 #define SIG_TALK 1 #define SIG_BROADCAST 2 diff --git a/include/proto.h b/include/proto.h index 0b2cebde..674fea53 100644 --- a/include/proto.h +++ b/include/proto.h @@ -577,12 +577,16 @@ int inansistr(char *str, int n); void move_ansi(int y, int x); void getyx_ansi(int *py, int *px); void region_scroll_up(int top, int bottom); + +#ifndef USE_PFTERM +# define SOLVE_ANSI_CACHE() {} +#else // !USE_PFTERM +# define SOLVE_ANSI_CACHE() { outs(" \b"); } +#endif // !USE_PFTERM + #define HAVE_GRAYOUT void grayout(int start, int end, int level); -void save_cursor(void); -void restore_cursor(void); - /* AIDS */ typedef uint64_t aidu_t; aidu_t fn2aidu(char *fn); @@ -706,9 +710,8 @@ int u_cloak(void); int u_list(void); #ifdef DBCSAWARE int u_detectDBCSAwareEvilClient(); -int getDBCSstatus(unsigned char *s, int pos); -#define ISDBCSAWARE() (cuser.uflag & DBCSAWARE_FLAG) #endif +#define ISDBCSAWARE() (cuser.uflag & DBCSAWARE_FLAG) /* vote */ void b_suckinfile(FILE *fp, char *fname); diff --git a/include/visio.h b/include/visio.h index b3164709..06b78907 100644 --- a/include/visio.h +++ b/include/visio.h @@ -24,6 +24,8 @@ #define VCLR_PAUSE_PAD ANSI_COLOR(1;34;44) #define VCLR_PAUSE ANSI_COLOR(1;37;44) +#define VCLR_INPUT_FIELD ANSI_COLOR(0;7) + #define VMSG_PAUSE " 請按任意鍵繼續 " #define VMSG_PAUSE_PAD "▄" #define VMSG_MSG_FLOAT " [按任意鍵繼續]" @@ -42,6 +44,13 @@ #define VFILL_HAS_BORDER VFILL_DEFAULT #define VFILL_NO_BORDER (0x08) +#define VGET_DEFAULT (0x00) +#define VGET_DOECHO (VGET_DEFAULT) +#define VGET_NOECHO (0x01) +#define VGET_LOWERCASE (0x02) +#define VGET_DIGITS (0x04) +#define VGET_GCARRY (0x08) + // DATATYPE DEFINITION ------------------------------------------------- typedef void * VREFSCR; typedef long VREFCUR; @@ -66,11 +75,16 @@ typedef struct { void prints(const char *fmt, ...) GCC_CHECK_FORMAT(1,2); void mvouts(int y, int x, const char *str); +// input history +int InputHistoryAdd (const char *s); +void InputHistoryPrev(char *s, int sz); +void InputHistoryNext(char *s, int sz); + // v*: primitive rendering void vpad (int n, const char *pattern); /// pad n fields by pattern int vgety (void); /// return cursor position (y) void vfill (int n, int flags, const char *s); /// fill n-width space with s -void vfillf (int n, int flags, const char *s, ...) GCC_CHECK_FORMAT(3,4); // formatted version of vfill +void vfillf (int n, int flags, const char *s, ...) GCC_CHECK_FORMAT(3,4); /// formatted version of vfill void vbarlr (const char *l, const char *r); /// draw a left-right expanded bar with (l,r) void vbarf (const char *s, ...) GCC_CHECK_FORMAT(1,2); /// vbarlr with formatted input (\t splits (l,r) void vshowmsg(const char *msg); /// draw standard pause/message @@ -82,6 +96,10 @@ void vshowmsg(const char *msg); /// draw standard pause/message int vans (const char *msg); /// prompt and return (lowercase) single byte input int vansf (const char *fmt,...) GCC_CHECK_FORMAT(1,2); /// formatted input of vans +// vget: (y, x, ...) +int vgets (char *buf, int len, int flags); /// input with edit box control +int vgetstr (char *buf, int len, int flags, const char *str);/// input with default value +int vget(int y, int x, const char *prompt, char *buf, int len, int mode); // vs_*: formatted and themed virtual screen layout // you cannot use ANSI escapes in these APIs. diff --git a/mbbsd/chat.c b/mbbsd/chat.c index e7b5f1dd..fe954bfc 100644 --- a/mbbsd/chat.c +++ b/mbbsd/chat.c @@ -402,7 +402,7 @@ t_chat(void) #ifdef DBCSAWARE if(currchar > 0 && ISDBCSAWARE() && - getDBCSstatus((unsigned char*)inbuf, currchar) == DBCS_TRAILING) + DBCS_Status(inbuf, currchar) == DBCS_TRAILING) currchar --; #endif } @@ -414,7 +414,7 @@ t_chat(void) #ifdef DBCSAWARE if(inbuf[currchar] && ISDBCSAWARE() && - getDBCSstatus((unsigned char*)inbuf, currchar) == DBCS_TRAILING) + DBCS_Status(inbuf, currchar) == DBCS_TRAILING) currchar++; #endif } @@ -503,12 +503,12 @@ t_chat(void) } print_chatid(chatid); move(b_lines - 1, chatid_len); - } else if (ch == Ctrl('H') || ch == '\177') { + } else if (ch == Ctrl('H') || ch == KEY_BS2) { if (currchar) { #ifdef DBCSAWARE int dbcs_off = 1; if (ISDBCSAWARE() && - getDBCSstatus((unsigned char*)inbuf, currchar-1) == DBCS_TRAILING) + DBCS_Status(inbuf, currchar-1) == DBCS_TRAILING) dbcs_off = 2; #endif currchar -= dbcs_off; @@ -532,7 +532,7 @@ t_chat(void) #ifdef DBCSAWARE int dbcs_off = 1; if (ISDBCSAWARE() && inbuf[currchar+1] && - getDBCSstatus((unsigned char*)inbuf, currchar+1) == DBCS_TRAILING) + DBCS_Status(inbuf, currchar+1) == DBCS_TRAILING) dbcs_off = 2; #endif inbuf[69] = '\0'; diff --git a/mbbsd/edit.c b/mbbsd/edit.c index 93c1735e..044517a2 100644 --- a/mbbsd/edit.c +++ b/mbbsd/edit.c @@ -3728,7 +3728,7 @@ vedit2(const char *fpath, int saveheader, int *islocal, int flags) curr_buf->insert_mode ^= 1; break; case Ctrl('H'): - case '\177': /* backspace */ + case KEY_BS2: /* backspace */ block_cancel(); if (curr_buf->ansimode) { curr_buf->ansimode = 0; @@ -757,33 +757,6 @@ peek_input(float f, int c) return 0; } - -#ifdef DBCSAWARE - -int getDBCSstatus(unsigned char *s, int pos) -{ - int sts = DBCS_ASCII; - while(pos >= 0) - { - if(sts == DBCS_LEADING) - sts = DBCS_TRAILING; - else if (*s >= 0x80) - { - sts = DBCS_LEADING; - } else { - sts = DBCS_ASCII; - } - s++, pos--; - } - return sts; -} - -#else - -#define dbcs_off (1) - -#endif - #define MAXLASTCMD 12 static int getdata_raw(int line, int col, const char *prompt, char *buf, int len, int echo) @@ -828,7 +801,7 @@ getdata_raw(int line, int col, const char *prompt, char *buf, int len, int echo) buf[1] = ch; // workaround for BBS-Lua break; } - if (ch == '\177' || ch == Ctrl('H')) { + if (ch == KEY_BS2 || ch == Ctrl('H')) { if (!clen) { bell(); continue; @@ -901,7 +874,7 @@ getdata_raw(int line, int col, const char *prompt, char *buf, int len, int echo) --currchar; #ifdef DBCSAWARE if(currchar > 0 && ISDBCSAWARE() && - getDBCSstatus((unsigned char*)buf, currchar) == DBCS_TRAILING) + DBCS_Status(buf, currchar) == DBCS_TRAILING) currchar --; #endif break; @@ -912,7 +885,7 @@ getdata_raw(int line, int col, const char *prompt, char *buf, int len, int echo) ++currchar; #ifdef DBCSAWARE if(buf[currchar] && ISDBCSAWARE() && - getDBCSstatus((unsigned char*)buf, currchar) == DBCS_TRAILING) + DBCS_Status(buf, currchar) == DBCS_TRAILING) currchar++; #endif break; @@ -937,12 +910,12 @@ getdata_raw(int line, int col, const char *prompt, char *buf, int len, int echo) clen = currchar = strlen(buf); break; - case '\177': + case KEY_BS2: case Ctrl('H'): if (!currchar) break; #ifdef DBCSAWARE - if (ISDBCSAWARE() && getDBCSstatus((unsigned char*)buf, + if (ISDBCSAWARE() && DBCS_Status(buf, currchar-1) == DBCS_TRAILING) { memmove(buf+currchar-1, buf+currchar, clen-currchar+1); @@ -958,8 +931,8 @@ getdata_raw(int line, int col, const char *prompt, char *buf, int len, int echo) if (!buf[currchar]) break; #ifdef DBCSAWARE - if (ISDBCSAWARE() && buf[currchar+1] && getDBCSstatus( - (unsigned char*)buf, currchar+1) == DBCS_TRAILING) + if (ISDBCSAWARE() && buf[currchar+1] && DBCS_Status( + buf, currchar+1) == DBCS_TRAILING) { memmove(buf+currchar, buf+currchar+1, clen-currchar); clen --; @@ -1035,25 +1008,42 @@ getdata_raw(int line, int col, const char *prompt, char *buf, int len, int echo) int getdata_buf(int line, int col, const char *prompt, char *buf, int len, int echo) { +#ifdef TRY_VGETS + move(line, col); + if(prompt && *prompts) outs(prompt); + return vgetstr(buf, len, echo ? VGET_DOECHO : VGET_NOECHO, buf); +#else return getdata_raw(line, col, prompt, buf, len, echo); +#endif } int getdata_str(int line, int col, const char *prompt, char *buf, int len, int echo, const char *defaultstr) { +#ifdef TRY_VGETS + move(line, col); + if(prompt && *prompts) outs(prompt); + return vgetstr(buf, len, echo ? VGET_DOECHO : VGET_NOECHO, defaultstr); +#else // if pointer is the same, ignore copy. if (defaultstr != buf) strlcpy(buf, defaultstr, len); - return getdata_raw(line, col, prompt, buf, len, echo); +#endif } int getdata(int line, int col, const char *prompt, char *buf, int len, int echo) { +#ifdef TRY_VGETS + move(line, col); + if(prompt) outs(prompt); + return vgets(buf, len, echo ? VGET_DOECHO : VGET_NOECHO); +#else buf[0] = 0; return getdata_raw(line, col, prompt, buf, len, echo); +#endif } int diff --git a/mbbsd/talk.c b/mbbsd/talk.c index 824b4651..778e4f04 100644 --- a/mbbsd/talk.c +++ b/mbbsd/talk.c @@ -1227,7 +1227,7 @@ do_talk_char(talkwin_t * twin, int ch, FILE *flog) #ifdef DBCSAWARE if(twin->curcol > 0 && twin->curcol < line->len && ISDBCSAWARE()) { - if(getDBCSstatus(line->data, twin->curcol) == DBCS_TRAILING) + if(DBCS_Status((char*)line->data, twin->curcol) == DBCS_TRAILING) twin->curcol --; } #endif @@ -1241,7 +1241,7 @@ do_talk_char(talkwin_t * twin, int ch, FILE *flog) #ifdef DBCSAWARE if(twin->curcol < TALK_MAXCOL && twin->curcol < line->len && ISDBCSAWARE()) { - if(getDBCSstatus(line->data, twin->curcol) == DBCS_TRAILING) + if(DBCS_Status((char*)line->data, twin->curcol) == DBCS_TRAILING) twin->curcol++; } #endif @@ -1262,7 +1262,7 @@ do_talk_char(talkwin_t * twin, int ch, FILE *flog) #ifdef DBCSAWARE // curln may be changed. if(twin->curcol > 0 && twin->curcol < line->len && - getDBCSstatus(line->data, twin->curcol) == DBCS_TRAILING) + DBCS_Status((char*)line->data, twin->curcol) == DBCS_TRAILING) twin->curcol--; #endif move(twin->curln, twin->curcol); @@ -1282,7 +1282,7 @@ do_talk_char(talkwin_t * twin, int ch, FILE *flog) // curln may be changed. line = twin->big_picture + (twin->curln -twin->sline); if(twin->curcol > 0 && twin->curcol < line->len && - getDBCSstatus(line->data, twin->curcol) == DBCS_TRAILING) + DBCS_Status((char*)line->data, twin->curcol) == DBCS_TRAILING) twin->curcol--; #endif move(twin->curln, twin->curcol); @@ -1291,14 +1291,14 @@ do_talk_char(talkwin_t * twin, int ch, FILE *flog) // complex data change case Ctrl('H'): - case '\177': + case KEY_BS2: if (twin->curcol > 0) { int delta = 1; #ifdef DBCSAWARE if (twin->curcol > 1 && ISDBCSAWARE() && - getDBCSstatus(line->data, twin->curcol-1) == DBCS_TRAILING) + DBCS_Status((char*)line->data, twin->curcol-1) == DBCS_TRAILING) delta++; #endif memmove(line->data + twin->curcol-delta, line->data + twin->curcol, @@ -1317,7 +1317,7 @@ do_talk_char(talkwin_t * twin, int ch, FILE *flog) #ifdef DBCSAWARE if (ISDBCSAWARE() && - getDBCSstatus(line->data, twin->curcol) == DBCS_LEADING) + DBCS_Status((char*)line->data, twin->curcol) == DBCS_LEADING) delta++; #endif memmove(line->data + twin->curcol, line->data + twin->curcol+delta, diff --git a/mbbsd/visio.c b/mbbsd/visio.c index 7001f23e..499467a5 100644 --- a/mbbsd/visio.c +++ b/mbbsd/visio.c @@ -674,3 +674,289 @@ vs_cols(const VCOL *cols, const VCOLW *ws, int n, ...) outs(ANSI_RESET "\n"); } +//////////////////////////////////////////////////////////////////////// +// DBCS Aware Helpers +//////////////////////////////////////////////////////////////////////// + +#ifdef DBCSAWARE +# define CHKDBCSTRAIL(_buf,_i) (/*ISDBCSAWARE() &&*/ DBCS_Status(_buf, _i) == DBCS_TRAILING) +#else // !DBCSAWARE +# define CHKDBCSTRAIL(buf,i) (0) +#endif // !DBCSAWARE + +//////////////////////////////////////////////////////////////////////// +// History Helpers +//////////////////////////////////////////////////////////////////////// +// +#define IH_MAX_ENTRIES (12) + +typedef struct { + int icurr; // current retrival pointer + int iappend; // new location to append + char buf[IH_MAX_ENTRIES][STRLEN]; +} InputHistory; + +static InputHistory ih; // everything intialized to zero. + +int +InputHistoryAdd(const char *s) +{ + int i = 0; + if (!s || !*s || !*(s+1)) + return 0; + for (i = 0; i < IH_MAX_ENTRIES; i++) + if (strcmp(s, ih.buf[i]) == 0) + return 0; + // TODO if already in queue, reject. + strlcpy(ih.buf[ih.iappend], s, sizeof(ih.buf[ih.iappend])); + ih.icurr = ih.iappend; + ih.iappend ++; + ih.iappend %= IH_MAX_ENTRIES; + return 1; +} + +static void +InputHistoryDelta(char *s, int sz, int d) +{ + int i, xcurr = 0; + for (i = 1; i <= IH_MAX_ENTRIES; i++) + { + xcurr = (ih.icurr+d*i)%IH_MAX_ENTRIES; + if (ih.buf[xcurr][0]) + { + ih.icurr = xcurr; + break; + } + } + if (ih.buf[ih.icurr][0]) + { + strlcpy(s, ih.buf[ih.icurr], sz); + // ih.icurr += d; + // ih.icurr %= IH_MAX_ENTRIES; + } +} + +void +InputHistoryPrev(char *s, int sz) +{ + InputHistoryDelta(s, sz, IH_MAX_ENTRIES-1); +} + +void +InputHistoryNext(char *s, int sz) +{ + InputHistoryDelta(s, sz, +1); +} + +//////////////////////////////////////////////////////////////////////// +// vget*: mini editbox +//////////////////////////////////////////////////////////////////////// +int +vgets(char *buf, int len, int flags) +{ + return vgetstr(buf, len, flags, ""); +} + +int +vgetstr(char *_buf, int len, int flags, const char *defstr) +{ + // iend points to NUL address, and + // icurr points to cursor. + int line, col; + int icurr = 0, iend = 0, abort = 0; + int c; + + // always use internal buffer to prevent temporary input issue. + char buf[STRLEN] = ""; + + // it is wrong to design input with larger buffer + // than STRLEN. Although we support large screen, + // inputting huge line will just make troubles... + if (len > STRLEN) len = STRLEN; + assert(len <= sizeof(buf)); + + // memset(buf, 0, len); + if (defstr && *defstr) + { + strlcpy(buf, defstr, len); + icurr = iend = strlen(buf); + } + + getyx(&line, &col); // now (line,col) is the beginning of our new fields. + + while (!abort) + { + if (!(flags & VGET_NOECHO)) + { + // print current buffer + move(line, col); + clrtoeol(); + SOLVE_ANSI_CACHE(); + outs(VCLR_INPUT_FIELD); // change color to prompt fields + vfill(len, 0, buf); + outs(ANSI_RESET); + + // move to cursor position + move(line, col+icurr); + } + c = vkey(); + + switch(c) { + // history navigation + case KEY_DOWN: case Ctrl('N'): + c = KEY_DOWN; + // let UP do the magic. + case KEY_UP: case Ctrl('P'): + if (flags & VGET_NOECHO) { + bell(); + continue; + } + InputHistoryAdd(buf); + if (c == KEY_DOWN) + InputHistoryNext(buf, len); + else + InputHistoryPrev(buf, len); + icurr = iend = strlen(buf); + break; + + // exiting keys + case '\n': case '\r': + // confirm again. + buf[iend] = 0; + abort = 1; + break; + + case Ctrl('C'): + icurr = iend = 0; + buf[1] = c; + buf[iend] = 0; + abort = 1; + break; + + // standard navigation + case KEY_HOME: case Ctrl('A'): + icurr = 0; + break; + + case KEY_END: case Ctrl('E'): + icurr = iend; + break; + + case KEY_LEFT: case Ctrl('B'): + if (icurr > 0) + icurr--; + else + bell(); + if (icurr > 0 && CHKDBCSTRAIL(buf, icurr)) + icurr--; + break; + + case KEY_RIGHT: case Ctrl('F'): + if (icurr < iend) + icurr++; + else + bell(); + if (icurr < iend && CHKDBCSTRAIL(buf, icurr)) + icurr++; + break; + + // editing keys + case KEY_DEL: case Ctrl('D'): + if (icurr+1 < iend && CHKDBCSTRAIL(buf, icurr+1)) { + // kill next one character. + memmove(buf+icurr, buf+icurr+1, iend-icurr); + iend--; + } else + bell(); + if (icurr < iend) { + // kill next one character. + memmove(buf+icurr, buf+icurr+1, iend-icurr); + iend--; + } + break; + + case Ctrl('H'): case KEY_BS2: + if (icurr > 0) { + // kill previous one charracter. + memmove(buf+icurr-1, buf+icurr, iend-icurr+1); + icurr--; iend--; + } else + bell(); + if (icurr > 0 && CHKDBCSTRAIL(buf, icurr)) { + // kill previous one charracter. + memmove(buf+icurr-1, buf+icurr, iend-icurr+1); + icurr--; iend--; + } + break; + + case Ctrl('Y'): + icurr = 0; + // reuse Ctrl-K code + case Ctrl('K'): + iend = icurr; + buf[iend] = 0; + break; + + // defaults + default: + + // content filter + if (c < ' ' || c >= 0xFF || + iend+1 >= len) + { + bell(); + continue; + } + if ((flags & VGET_DIGITS) && + ( !isascii(c) || !isdigit(c))) + { + bell(); + continue; + } + if (flags & VGET_LOWERCASE) + { + if (!isascii(c)) + { + bell(); + continue; + } + c = tolower(c); + } + + // prevent incomplete DBCS + // this check only works if DBCS-aware is active. + // Otherwise, non-DBCS-aware users will fail to + // input the final DBCS-TRAIL character. +#ifdef DBCSAWARE + if (ISDBCSAWARE() && + c > 0x80 && iend+2 >= len && + !CHKDBCSTRAIL(buf, icurr) ) + { + bell(); + continue; + } +#endif // DBCSAWARE + + // add one character. + memmove(buf+icurr+1, buf+icurr, iend-icurr+1); + buf[icurr++] = c; + iend++; + break; + } + } + + assert(iend >= 0 && iend < len); + buf[iend] = 0; + + // final filtering + if (iend && (flags & VGET_LOWERCASE)) + buf[0] = tolower(buf[0]); + + // save the history except password mode + if (!(flags & VGET_NOECHO)) + InputHistoryAdd(buf); + + // copy buffer! + memcpy(_buf, buf, len); + return iend; +} |