/* $Id: io.c,v 1.31 2004/06/23 13:27:28 Starfish Exp $ */ #include "bbs.h" // XXX why linux use smaller buffer? #if defined(linux) #define OBUFSIZE 2048 #define IBUFSIZE 128 #else #define OBUFSIZE 4096 #define IBUFSIZE 256 #endif static char outbuf[OBUFSIZE], inbuf[IBUFSIZE]; static int obufsize = 0, ibufsize = 0; static int icurrchar = 0; /* ----------------------------------------------------- */ /* convert routines */ /* ----------------------------------------------------- */ #ifdef CONVERT read_write_type write_type = (read_write_type)write; read_write_type read_type = read; inline static int read_wrapper(int fd, void *buf, size_t count) { return (*read_type)(fd, buf, count); } inline static int write_wrapper(int fd, void *buf, size_t count) { return (*write_type)(fd, buf, count); } #endif /* ----------------------------------------------------- */ /* output routines */ /* ----------------------------------------------------- */ void oflush() { if (obufsize) { #ifdef CONVERT write_wrapper(1, outbuf, obufsize); #else write(1, outbuf, obufsize); #endif obufsize = 0; } } void init_buf() { memset(inbuf, 0, IBUFSIZE); } void output(char *s, int len) { /* Invalid if len >= OBUFSIZE */ assert(len OBUFSIZE) { #ifdef CONVERT write_wrapper(1, outbuf, obufsize); #else write(1, outbuf, obufsize); #endif obufsize = 0; } memcpy(outbuf + obufsize, s, len); obufsize += len; } int ochar(int c) { if (obufsize > OBUFSIZE - 1) { /* suppose one byte data doesn't need to be converted. */ write(1, outbuf, obufsize); obufsize = 0; } outbuf[obufsize++] = c; return 0; } /* ----------------------------------------------------- */ /* input routines */ /* ----------------------------------------------------- */ static int i_newfd = 0; static struct timeval i_to, *i_top = NULL; static int (*flushf) () = NULL; void add_io(int fd, int timeout) { i_newfd = fd; if (timeout) { i_to.tv_sec = timeout; i_to.tv_usec = 16384; /* Ptt: 改成16384 避免不按時for loop吃cpu * time 16384 約每秒64次 */ i_top = &i_to; } else i_top = NULL; } /* To set input file descriptor Return: This function returns old input file descriptor. */ int i_set_input_fd(int newfd) { int oldfd; oldfd = i_newfd; i_newfd = newfd; return oldfd; } int num_in_buf() { return icurrchar - ibufsize; } /* * dogetch() is not reentrant-safe. SIGUSR[12] might happen at any time, and * dogetch() might be called again, and then ibufsize/icurrchar/inbuf might * be inconsistent. We try to not segfault here... */ static int dogetch() { int len; static time_t lastact; if (ibufsize <= icurrchar) { if (flushf) (*flushf) (); refresh(); if (i_newfd) { struct timeval timeout; fd_set readfds; if (i_top) timeout = *i_top; /* copy it because select() might * change it */ FD_ZERO(&readfds); FD_SET(0, &readfds); FD_SET(i_newfd, &readfds); /* jochang: modify first argument of select from FD_SETSIZE */ /* since we are only waiting input from fd 0 and i_newfd(>0) */ while ((len = select(i_newfd + 1, &readfds, NULL, NULL, i_top ? &timeout : NULL)) < 0) { if (errno != EINTR) abort_bbs(0); /* raise(SIGHUP); */ } if (len == 0) return I_TIMEOUT; if (i_newfd && FD_ISSET(i_newfd, &readfds)) return I_OTHERDATA; } #ifdef NOKILLWATERBALL if( currutmp && currutmp->msgcount && !reentrant_write_request ) write_request(1); #endif #ifdef SKIP_TELNET_CONTROL_SIGNAL do{ #endif #ifdef CONVERT while ((len = read_wrapper(0, inbuf, IBUFSIZE)) <= 0) { #else while ((len = read(0, inbuf, IBUFSIZE)) <= 0) { #endif if (len == 0 || errno != EINTR) abort_bbs(0); /* raise(SIGHUP); */ } #ifdef SKIP_TELNET_CONTROL_SIGNAL } while( inbuf[0] == -1 ); #endif ibufsize = len; icurrchar = 0; } if (currutmp) { #ifdef OUTTA_TIMER now = SHM->GV2.e.now; #else now = time(0); #endif if (now - lastact < 3) currutmp->lastact = now; lastact = now; } return (unsigned char)inbuf[icurrchar++]; } int igetch() { register int ch; while ((ch = dogetch())) { switch (ch) { #ifdef DEBUG case Ctrl('Q'):{ struct rusage ru; getrusage(RUSAGE_SELF, &ru); vmsg("sbrk: %d KB, idrss: %d KB, isrss: %d KB", ((int)sbrk(0) - 0x8048000) / 1024, (int)ru.ru_idrss, (int)ru.ru_isrss); } continue; #endif case Ctrl('L'): redoscr(); continue; case Ctrl('U'): if (currutmp != NULL && currutmp->mode != EDITING && currutmp->mode != LUSERS && currutmp->mode) { screenline_t *screen0 = calloc(t_lines, sizeof(screenline_t)); int oldroll = roll; int y, x, my_newfd; getyx(&y, &x); memcpy(screen0, big_picture, t_lines * sizeof(screenline_t)); my_newfd = i_newfd; i_newfd = 0; t_users(); i_newfd = my_newfd; memcpy(big_picture, screen0, t_lines * sizeof(screenline_t)); roll = oldroll; move(y, x); free(screen0); redoscr(); continue; } else return (ch); case Ctrl('R'): if (currutmp == NULL) return ch; if (t_reply_water()) continue; else return ch; case '\n': /* Ptt把 \n拿掉 */ continue; case IAC: // disallow user input telnet protocol leading char IAC chr(255) // TODO parse telnet protocol continue; default: return ch; } } return 0; } int strip_ansi(char *buf, char *str, int mode) { register int count = 0; static char EscapeFlag[] = { /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0, 0, 0, 0, 0, /* 20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, /* 0~9 ;= */ /* 40 */ 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, /* ABCDHIJK */ /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 */ 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 2, 0, 0, /* fhlm */ /* 70 */ 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* su */ /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #define isEscapeParam(X) (EscapeFlag[(int)(X)] & 1) #define isEscapeCommand(X) (EscapeFlag[(int)(X)] & 2) for(; *str; ++str) if( *str != '\033' ){ if( buf ) *buf++ = *str; ++count; }else{ register char* p = str + 1; if( *p != '[' ){ ++str; continue; } while(isEscapeParam(*++p)); if( (mode == NO_RELOAD && isEscapeCommand(*p)) || (mode == ONLY_COLOR && *p == 'm' )){ register int len = p - str + 1; if( buf ){ strncpy(buf, str, len); buf += len; } count += len; } str = p; } if( buf ) *buf = 0; return count; /* Rewritten by scw. * Moved from vote.c (here is a better place for this function). * register int ansi, count = 0; * for (ansi = 0; *str ; str++) { if (*str == 27) { if (mode) { if (buf) * *buf++ = *str; count++; } ansi = 1; } else if (ansi && strchr( * "[;1234567890mfHABCDnsuJKc=n", *str)) { if ((mode == NO_RELOAD && * !strchr("c=n", *str)) || (mode == ONLY_COLOR && strchr("[;1234567890m", * *str))) { if (buf) *buf++ = *str; count++; } if (strchr("mHn ", *str)) * ansi = 0; } else { ansi = 0; if (buf) *buf++ = *str; count++; } } if * (buf) *buf = '\0'; return count; */ } void getdata_core_init(struct getdata_core_t *core, int line, int col, char *buf, int len, char echo) { register int i; core->line = line; core->col = col; core->buf = buf; core->len = len; core->echo = echo; standout(); for(i=0; ilen; buf[core->len] = '\0'; move(core->line, core->col); if (core->echo) { for (i=0; core->buf[i]; ++i) outch(core->echo); } else { edit_outs(core->buf); } core->clen = core->currchar = strlen(buf); } void getdata_core_done(struct getdata_core_t *core) { outc('\n'); refresh(); } void getdata_core_redraw(struct getdata_core_t *core) { int i; move(core->line, core->col); /* clrtoeol(); */ if (core->echo) { for (i=0; iclen; ++i) outch(core->echo); } else { edit_outs(core->buf); } for (i=core->clen; ilen; ++i) outch(' '); } void getdata_core_setdata(struct getdata_core_t *core, char *data) { strncpy(core->buf, data, core->len); core->buf[core->len] = '\0'; core->clen = core->currchar = strlen(core->buf); getdata_core_redraw(core); } void getdata_core_moveleft(struct getdata_core_t *core) { if (core->currchar) --core->currchar; } void getdata_core_moveright(struct getdata_core_t *core) { if (core->buf[core->currchar]) ++core->currchar; } void getdata_core_home(struct getdata_core_t *core) { core->currchar = 0; } void getdata_core_end(struct getdata_core_t *core) { core->currchar = core->clen; } void getdata_core_backspace(struct getdata_core_t *core) { int i; if (core->currchar) { core->currchar--; core->clen--; for (i = core->currchar; i <= core->clen; i++) core->buf[i] = core->buf[i + 1]; move(core->line, core->col + core->clen); outc(' '); move(core->line, core->col); if (core->echo) { for (i=0; core->buf[i]; ++i) outch(core->echo); } else { edit_outs(core->buf); } } } void getdata_core_delete(struct getdata_core_t *core) { int i; if (core->buf[core->currchar]) { core->clen--; for (i = core->currchar; i <= core->clen; i++) core->buf[i] = core->buf[i + 1]; move(core->line, core->col + core->clen); outc(' '); move(core->line, core->col); if (core->echo) { for (i=0; core->buf[i]; ++i) outch(core->echo); } else { edit_outs(core->buf); } } } void getdata_core_insert(struct getdata_core_t *core, char ch) { int i; if (isprint2(ch) && core->clen < core->len && core->col + core->clen < scr_cols) { for (i = core->clen + 1; i > core->currchar; i--) core->buf[i] = core->buf[i - 1]; core->buf[core->currchar] = ch; /* 顯示輸入字元 */ move(core->line, core->col + core->currchar); if (core->echo) { outch(core->echo); } else { edit_outs(core->buf + core->currchar); } core->currchar++; core->clen++; } } void getdata_core_clrtoend(struct getdata_core_t *core) { int i; core->buf[core->currchar] = '\0'; move(core->line, core->col + core->currchar); for (i = core->currchar; i < core->clen; i++) outc(' '); core->clen = core->currchar; } int getdata_core_getkey(struct getdata_core_t *core) { move(core->line, core->col + core->currchar); return igetkey(); } void getdata_core_keyin(struct getdata_core_t *core, int key) { switch (key) { case KEY_LEFT: getdata_core_moveleft(core); break; case KEY_RIGHT: getdata_core_moveright(core); break; case '\177': case Ctrl('H'): getdata_core_backspace(core); break; case Ctrl('Y'): core->currchar = 0; getdata_core_end(core); case Ctrl('K'): getdata_core_clrtoend(core); break; case Ctrl('D'): case KEY_DEL: getdata_core_delete(core); break; case Ctrl('A'): case KEY_HOME: getdata_core_home(core); break; case Ctrl('E'): case KEY_END: getdata_core_end(core); break; default: getdata_core_insert(core, key); break; } } /* To get a line of input interactively. The user can edit the input line and recall storied strings. Parameters: "line" and "col" are the location of input field. "buf" is the input buffer. The input data will be storied in "*buf". "len" is the maximum length of the input buffer "buf". "echo" is the echo character. It echo the input character if "echo" is equal to zero, or the value of "echo" is displayed. "history" stores the storied strings. Every storied strings should have equal length. "histsize" is the number of the storied strings. "histlen" is the length of every storied strings. "remember" is a boolean value, which controls the function story the input value into "history" array or not. */ static int getdata_interactive(int line, int col, char *buf, int len, char echo, char *history, int histsize, int histlen, int remember) { int ch; struct getdata_core_t core; int cmdpos = 0; getdata_core_init(&core, line, col, buf, len, echo); while ((ch = getdata_core_getkey(&core)) != '\r') { if (histsize > 1) { if (ch==KEY_UP || ch==Ctrl('P')) { if (remember) strlcpy(history+(cmdpos*histlen), buf, histlen); cmdpos = (cmdpos+1) % histsize; getdata_core_setdata(&core, history+(cmdpos*histlen)); } else if (ch==KEY_DOWN || ch==Ctrl('N')) { if (remember) strlcpy(history+(cmdpos*histlen), buf, histlen); cmdpos = (cmdpos + histsize - 1) % histsize; getdata_core_setdata(&core, history+(cmdpos*histlen)); } } getdata_core_keyin(&core, ch); } if (remember && core.clen > 1) { strlcpy(history, core.buf, histlen); if (histsize > 1) memmove(history+histlen, history, (histsize-1)*histlen); } getdata_core_done(&core); return core.clen; } /* To get a line of input with assigned storied strings. This is an interface of getdata_interactive(). */ int getdata_history(int line, int col, char *buf, int len, char *history, int histsize, int histlen, int remember) { return getdata_interactive(line, col, buf, len, 0, history, histsize, histlen, remember); } /* To get a line of input interactively. No history is provided. No default string is used. This is an interface of getdata_interactive(). Parameters: "line", "col" are the position of the prompt message to print. "prompt" is a prompt message. "buf" is the space to return the input. "len" is the size of "*buf". */ int getdata_simple(int line, int col, char *prompt, char *buf, int len) { move(line, col); clrtoeol(); outs(prompt); buf[0] = '\0'; return getdata_interactive(line, col+strip_ansi(NULL, prompt, STRIP_ALL), buf, len, 0, "", 0, 0, 0); } /* To get a line of input interactively and echo "*". No history is provided. No default string is used. This is an interface of getdata_interactive(). Parameters: "line", "col" are the position of the prompt message to print. "prompt" is a prompt message. "buf" is the space to return the input. "len" is the size of "*buf". */ int getdata_masked(int line, int col, char *prompt, char *buf, int len) { move(line, col); clrtoeol(); outs(prompt); buf[0] = '\0'; return getdata_interactive(line, col+strip_ansi(NULL, prompt, STRIP_ALL), buf, len, '*', "", 0, 0, 0); } /* To get a line of input interactively with default string. No history is provided. This is an interface of getdata_interactive(). Parameters: "line", "col" are the position of the prompt message to print. "prompt" is a prompt message. "buf" is the space to return the input. "len" is the size of "*buf". */ int getdata_default(int line, int col, char *prompt, char *buf, int len, char *defaultstr) { move(line, col); clrtoeol(); outs(prompt); strlcpy(buf, defaultstr, len); return getdata_interactive(line, col+strip_ansi(NULL, prompt, STRIP_ALL), buf, len, 0, "", 0, 0, 0); } /* Core function of getdata(), getdata_str() and getdata_buf(). */ int oldgetdata(int line, int col, char *prompt, char *buf, int len, int echo) { register int ch; int clen; int x = col, y = line; #define MAXLASTCMD 12 static char lastcmd[MAXLASTCMD][80]; strip_ansi(buf, buf, STRIP_ALL); if (prompt) { move(line, col); clrtoeol(); outs(prompt); x += strip_ansi(NULL, prompt, STRIP_ALL); } if (!echo) { len--; clen = 0; while ((ch = igetch()) != '\r') { if (ch == '\177' || ch == Ctrl('H')) { if (!clen) { bell(); continue; } clen--; continue; } if (!isprint(ch)) { continue; } if (clen >= len) { continue; } buf[clen++] = ch; } buf[clen] = '\0'; outc('\n'); oflush(); } else clen = getdata_interactive(y, x, buf, len, 0, *lastcmd, MAXLASTCMD, sizeof(lastcmd[0]), 1); if ((echo == LCECHO) && isupper(buf[0])) buf[0] = tolower(buf[0]); return clen; #undef MAXLASTCMD } int getdata_buf(int line, int col, char *prompt, char *buf, int len, int echo) { return oldgetdata(line, col, prompt, buf, len, echo); } char getans(char *prompt) { char ans[5]; getdata(b_lines, 0, prompt, ans, sizeof(ans), LCECHO); return ans[0]; } int getdata_str(int line, int col, char *prompt, char *buf, int len, int echo, char *defaultstr) { strlcpy(buf, defaultstr, len); return oldgetdata(line, col, prompt, buf, len, echo); } int getdata(int line, int col, char *prompt, char *buf, int len, int echo) { buf[0] = 0; return oldgetdata(line, col, prompt, buf, len, echo); } int rget(int x, char *prompt) { register int ch; move(x, 0); clrtobot(); outs(prompt); refresh(); ch = igetch(); if (ch >= 'A' && ch <= 'Z') ch = tolower(ch); return ch; } int igetkey() { int mode; int ch, last; mode = last = 0; while (1) { if( !(ch = igetch()) ) continue; if (mode == 0) { if (ch == KEY_ESC) mode = 1; else return ch; /* Normal Key */ } else if (mode == 1) { /* Escape sequence */ if (ch == '[' || ch == 'O') mode = 2; else if (ch == '1' || ch == '4') mode = 3; else { KEY_ESC_arg = ch; return KEY_ESC; } } else if (mode == 2) { /* Cursor key */ if (ch >= 'A' && ch <= 'D') return KEY_UP + (ch - 'A'); else if (ch >= '1' && ch <= '6') mode = 3; else return ch; } else if (mode == 3) { /* Ins Del Home End PgUp PgDn */ if (ch == '~') return KEY_HOME + (last - '1'); else return ch; } last = ch; } }