diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2005-06-04 12:15:29 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2005-06-04 12:15:29 +0800 |
commit | 4929c8fb23010be17cf1daca876462562d75f93f (patch) | |
tree | 096967859dfd585460718441d656688e89a59d1a /mbbsd | |
parent | 3b067b023cc26430d51198c07bf105ddcfd1cbf0 (diff) | |
download | pttbbs-4929c8fb23010be17cf1daca876462562d75f93f.tar pttbbs-4929c8fb23010be17cf1daca876462562d75f93f.tar.gz pttbbs-4929c8fb23010be17cf1daca876462562d75f93f.tar.bz2 pttbbs-4929c8fb23010be17cf1daca876462562d75f93f.tar.lz pttbbs-4929c8fb23010be17cf1daca876462562d75f93f.tar.xz pttbbs-4929c8fb23010be17cf1daca876462562d75f93f.tar.zst pttbbs-4929c8fb23010be17cf1daca876462562d75f93f.zip |
pmore: new header scanner (clean and robust)
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@2754 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd')
-rw-r--r-- | mbbsd/pmore.c | 402 |
1 files changed, 252 insertions, 150 deletions
diff --git a/mbbsd/pmore.c b/mbbsd/pmore.c index 925ffd85..c4a537d0 100644 --- a/mbbsd/pmore.c +++ b/mbbsd/pmore.c @@ -14,7 +14,7 @@ * - Clean source code, and more readble to mortal * - Correct navigation * - Excellent search ability (for correctness and user behavior) - * - Less memory consumption (mmap is not considered) + * - Less memory consumption (mmap is not considered anyway) * - Better support for large terminals * - Unlimited file length and line numbers * @@ -55,9 +55,9 @@ #endif // --------------------------------------------------------------- <FEATURES> -#define PMORE_USE_PTT_PRINTS -#define PMORE_USE_SCROLL -#define PMORE_PRELOAD_SIZE (512*1024L) // on busy system set smaller or undef +#define PMORE_USE_PTT_PRINTS // PTT or special printing +#define PMORE_USE_OPT_SCROLL // optimized scroll +#define PMORE_PRELOAD_SIZE (128*1024L) // on busy system set smaller or undef // -------------------------------------------------------------- </FEATURES> #define DEBUG @@ -74,6 +74,7 @@ int debug = 0; // Common ANSI commands. #define ANSI_RESET (ESC_STR "[m") #define ANSI_COLOR(x) ESC_STR "[" #x "m" +#define STR_ANSICODE "[0123456789;," // Poor BBS terminal system Workarounds // - Most BBS implements clrtoeol() as fake command @@ -96,7 +97,10 @@ typedef struct long lineno, // lineno of disps oldlineno, // last drawn lineno, < 0 means full update maxlinenoS; // lineno of maxdisps, "S"! + // What does the magic "S" mean? + // Just trying to notify you that it's // NOT REAL MAX LINENO NOR FILELENGTH!!! + // You may consider "S" of "Start" (disps). } MmappedFile; MmappedFile mf = { @@ -118,14 +122,19 @@ enum { // --------------------------- <Aux. Structures> /* pretty format header */ +#define FH_HEADERS (4) // how many headers do we know? +#define FH_HEADER_LEN (4) // strlen of each heads +static const char *_fh_disp_heads[FH_HEADERS] = + {"作者", "標題", "時間", "轉信"}; + typedef struct { int lines; // header lines - int authorlen; - int boardlen; -} ArticleHeader; + unsigned char *headers[FH_HEADERS]; + unsigned char *floats[2]; // right floating, name and val +} MF_PrettyFormattedHeader; -ArticleHeader ah; +MF_PrettyFormattedHeader fh = { 0, {0,0,0,0}, {0, 0}}; /* search records */ typedef struct @@ -133,9 +142,9 @@ typedef struct int len; int (*cmpfunc) (const char *, const char *, size_t); char search_str[81]; // maybe we can change to dynamic allocation -} SearchRecord; +} MF_SearchRecord; -SearchRecord sr = { 0, strncmp, "" }; +MF_SearchRecord sr = { 0, strncmp, "" }; enum { MFSEARCH_FORWARD, @@ -145,12 +154,15 @@ enum { // Reset structures #define RESETMF() { memset(&mf, 0, sizeof(mf)); \ mf.maxlinenoS = mf.oldlineno = -1; } -#define RESETAH() { memset(&ah, 0, sizeof(ah)); \ - ah.lines = ah.authorlen= ah.boardlen = -1; } +#define RESETFH() { memset(&fh, 0, sizeof(fh)); \ + fh.lines = -1; } + // --------------------------- </Aux. Structures> // --------------------------------------------- </Defines and constants> +void mf_parseHeaders(); +void mf_freeHeaders(); int mf_backward(int); // used by mf_attach void mf_sync_lineno(); // used by mf_attach @@ -158,7 +170,8 @@ void mf_sync_lineno(); // used by mf_attach /* * mmap basic operations */ -int mf_attach(unsigned char *fn) +int +mf_attach(unsigned char *fn) { struct stat st; int fd = open(fn, O_RDONLY, 0600); @@ -206,21 +219,29 @@ int mf_attach(unsigned char *fn) mf.disps = mf.dispe = mf.start; mf.lineno = 0; + + /* reset and parse article header */ + mf_parseHeaders(); + return 1; } -void mf_detach() +void +mf_detach() { if(mf.start) { munmap(mf.start, mf.len); RESETMF(); + } + mf_freeHeaders(); } /* * lineno calculation, and moving */ -void mf_sync_lineno() +void +mf_sync_lineno() { unsigned char *p; @@ -238,7 +259,8 @@ void mf_sync_lineno() } } -int mf_backward(int lines) +int +mf_backward(int lines) { int flFirstLine = 1; // first, because we have to trace back to line beginning, @@ -279,7 +301,8 @@ int mf_backward(int lines) return MFNAV_EXCEED; } -int mf_forward(int lines) +int +mf_forward(int lines) { while(mf.disps <= mf.maxdisps && lines > 0) { @@ -302,14 +325,16 @@ int mf_forward(int lines) return MFNAV_EXCEED; } -int mf_goTop() +int +mf_goTop() { mf.disps = mf.start; mf.lineno = 0; return MFNAV_OK; } -int mf_goBottom() +int +mf_goBottom() { mf.disps = mf.maxdisps; /* @@ -321,26 +346,30 @@ int mf_goBottom() return MFNAV_OK; } -int mf_goto(int lineno) +int +mf_goto(int lineno) { mf.disps = mf.start; mf.lineno = 0; return mf_forward(lineno); } -int mf_viewedNone() +int +mf_viewedNone() { return (mf.disps <= mf.start); } -int mf_viewedAll() +int +mf_viewedAll() { return (mf.dispe >= mf.end); } /* * search! */ -int mf_search(int direction) +int +mf_search(int direction) { unsigned char *s = sr.search_str; int l = sr.len; @@ -392,71 +421,184 @@ int mf_search(int direction) return flFound; } +/* String Processing + * + * maybe you already have your string processors (or not). + * whether yes or no, here we provides some. + */ +void +pmore_str_strip_ansi(unsigned char *p) // warning: p is NULL terminated +{ + unsigned char *pb = p; + while (*p != 0) + { + if (*p == ESC_CHR) + { + // ansi code sequence, ignore them. + pb = p++; + while (*p && strchr(STR_ANSICODE, *p++)); + memmove(pb, p, strlen(p)+1); + p = pb; + } + else if (*p < ' ') + { + // control codes, ignore them. + memmove(p, p+1, strlen(p+1)+1); + } + else + p++; + } +} + +/* this chomp is a little different: + * it kills starting and trailing spaces. + */ +void +pmore_str_chomp(unsigned char *p) +{ + unsigned char *pb = p + strlen(p)-1; + + while (pb >= p) + if(isascii(*pb) && isblank(*pb)) + *pb-- = 0; + else + break; + pb = p; + while (*pb && isascii(*pb) && isblank(*pb)) + pb++; + + if(pb != p) + memmove(p, pb, strlen(pb)+1); +} + +int +pmore_str_safe_big5len(unsigned char *p) +{ + return 0; +} + /* * Format Related */ -void mf_parseHeader() + +void +mf_freeHeaders() +{ + if(fh.lines > 0) + { + int i; + + for (i = 0; i < FH_HEADERS; i++) + if(fh.headers[i]) + free(fh.headers[i]); + for (i = 0; i < sizeof(fh.floats) / sizeof(unsigned char*); i++) + free(fh.floats[i]); + RESETFH(); + } +} + +void +mf_parseHeaders() { - /* format: - * AUTHOR: author BOARD: blah - * XXX: xxx - * XXX: xxx - * [blank, fill with seperator] + /* file format: + * AUTHOR: author BOARD: blah <- headers[1], headers[0] + * XXX: xxx <- headers[2] + * XXX: xxx <- headers[n] + * [blank, fill with seperator] <- lines * * #define STR_AUTHOR1 "作者:" * #define STR_AUTHOR2 "發信人:" * #define STR_POST1 "看板:" * #define STR_POST2 "站內:" */ - RESETAH(); - if(mf.len > LEN_AUTHOR2) + unsigned char *pmf = mf.start; + int i = 0; + + RESETFH(); + + if(mf.len < LEN_AUTHOR2) + return; + + if (strncmp(mf.start, STR_AUTHOR1, LEN_AUTHOR1) == 0) { - if (strncmp(mf.start, STR_AUTHOR1, LEN_AUTHOR1) == 0) - { - ah.lines = 3; // local - ah.authorlen = LEN_AUTHOR1; - } - else if (strncmp(mf.start, STR_AUTHOR2, LEN_AUTHOR2) == 0) + fh.lines = 3; // local + } + else if (strncmp(mf.start, STR_AUTHOR2, LEN_AUTHOR2) == 0) + { + fh.lines = 4; + } + else + return; + + for (i = 0; i < fh.lines; i++) + { + unsigned char *p = pmf, *pb = pmf; + int l; + + /* first, go to line-end */ + while(pmf < mf.end && *pmf != '\n') + pmf++; + if(pmf >= mf.end) + break; + p = pmf; + pmf ++; // move to next line. + + // p is pointing at a new line. (\n) + l = (int)(p - pb); + p = (unsigned char*) malloc (l+1); + fh.headers[i] = p; + memcpy(p, pb, l); + p[l] = 0; + + // now, postprocess p. + pmore_str_strip_ansi(p); + + // strip to quotes[+1 space] + if((pb = strchr(p, ':')) != NULL) { - ah.lines = 4; - ah.authorlen = LEN_AUTHOR2; + if(*(pb+1) == ' ') pb++; + memmove(p, pb, strlen(pb)+1); } - /* traverse for author length */ + + // kill staring and trailing spaces + pmore_str_chomp(p); + + // special case, header[0] is in line[0]. + if(i == 0 && (pb = strrchr(p, ':')) != NULL && *(pb+1)) { - unsigned char *p = mf.start; - unsigned char *pb = p; - - /* first, go to line-end */ - while(p < mf.end && *p != '\n') - p++; - pb = p; - /* next, rollback for ':' */ - while(p > mf.start && *p != ':') - p--; - if(p > mf.start && *p == ':') - { - ah.boardlen = pb - p; - while (p > mf.start && *p != ' ') - p--; - if( *p == ' ') - { - ah.authorlen = p - mf.start - ah.authorlen; - } else - ah.boardlen = -1, ah.authorlen = -1; - } else - ah.authorlen = -1; + unsigned char *np = strdup(pb+1); + + fh.floats[1] = np; + pmore_str_chomp(np); + // remove quote and traverse back + *pb-- = 0; + while (pb > p && *pb != ',' && !(isascii(*pb) && isblank(*pb))) + pb--; + + if (pb > p) { + fh.floats[0] = strdup(pb+1); + pmore_str_chomp(fh.floats[0]); + *pb = 0; + pmore_str_chomp(fh.headers[0]); + } else { + fh.floats[0] = strdup(""); + } } } } +static +void MFDISP_SKIPCURLINE() +{ + while (mf.dispe < mf.end && *mf.dispe != '\n') + mf.dispe++; +} + /* * display mf content from disps for MFDISP_PAGE */ -#define STR_ANSICODE "[0123456789;," -#define DISP_HEADS_LEN (4) // strlen of each heads -static const char *disp_heads[] = {"作者", "標題", "時間", "轉信"}; - -void mf_disp() +void +mf_disp() { int lines = 0, col = 0, currline = 0; int startline = 0, endline = MFDISP_PAGE-1; @@ -468,7 +610,7 @@ void mf_disp() * if we scroll a fulfilled buffer, so leave one space. */ -#ifdef PMORE_USE_SCROLL +#ifdef PMORE_USE_OPT_SCROLL /* process scrolling */ if (mf.oldlineno >= 0 && mf.oldlineno != mf.lineno) { @@ -537,7 +679,7 @@ void mf_disp() * (header, seperator, or normal text) * is current line. */ - else if (!mf.rawmode && currline == ah.lines) + else if (!mf.rawmode && currline == fh.lines) { /* case 1, header seperator line */ outs(ANSI_COLOR(36)); @@ -547,85 +689,40 @@ void mf_disp() outs("─"); } outs(ANSI_RESET); - while(mf.dispe < mf.end && *mf.dispe != '\n') - mf.dispe++; + MFDISP_SKIPCURLINE(); col = x_max-1; } - else if (!mf.rawmode && currline < ah.lines) + else if (!mf.rawmode && currline < fh.lines) { /* case 2, we're printing headers */ - int w = t_columns, i_author = 0; - int flDrawBoard = 0, flDrawAuthor = 0; - const char *ph = disp_heads[currline]; - - if (currline == 0 && ah.boardlen > 0) - flDrawAuthor = 1; -draw_header: - if(flDrawAuthor) - w = header_w - ah.boardlen - 6; - else - w = header_w; + const char *val = fh.headers[currline]; + const char *name = _fh_disp_heads[currline]; + int w = header_w - FH_HEADER_LEN - 3; - outs(ANSI_COLOR(47;34) " "); col++; + outs(ANSI_COLOR(47;34) " "); + outs(name); + outs(" " ANSI_COLOR(44;37) " "); - /* special case for STR_AUTHOR2 */ - if(!flDrawBoard) + /* right floating stuff? */ + if (currline == 0 && fh.floats[0]) { - outs(ph); - col += DISP_HEADS_LEN; // strlen(disp_heads[currline]) - } else { - /* display as-is */ - while (*mf.dispe != ':' && *mf.dispe != '\n') - if(col++ <= header_w) - outc(*mf.dispe++); - } - - while (*mf.dispe != ':' && *mf.dispe != '\n') - mf.dispe ++; - - if(*mf.dispe == ':') { - outs(" " ANSI_COLOR(44;37)); - col++; - mf.dispe ++; + w -= strlen(fh.floats[0]) + strlen(fh.floats[1]) + 4; } - while (col < w) { // -2 to match seperator - int flCanDraw = (*mf.dispe != '\n'); - - if(flDrawAuthor) - flCanDraw = i_author < ah.authorlen; - if(flCanDraw) - { - /* strip ansi in headers */ - unsigned char c = *mf.dispe++; - if(inAnsi) - { - if (!strchr(STR_ANSICODE, c)) - inAnsi = 0; - } else { - if(c == ESC_CHR) - inAnsi = 1; - else - outc(c), col++, i_author++; - } - } else { - outc(' '), col++; - } - } + prints("%-*.*s", w, w, + (val ? val : "")); - if (flDrawAuthor) + if (currline == 0 && fh.floats[0]) { - flDrawBoard = 1; - flDrawAuthor = 0; - while(*mf.dispe == ' ') - mf.dispe ++; - goto draw_header; + outs(ANSI_COLOR(47;34) " "); + outs(fh.floats[0]); + outs(" " ANSI_COLOR(44;37) " "); + outs(fh.floats[1]); + outs(" "); } outs(ANSI_RESET); - // skip to end of line - while(mf.dispe < mf.end && *mf.dispe != '\n') - mf.dispe++; + MFDISP_SKIPCURLINE(); col = x_max-1; } else if(mf.dispe < mf.end) @@ -747,7 +844,7 @@ static const char * const pmore_help[] = { "(j)(↑) 上捲一行", "(k)(↓)(Enter) 下捲一行", "(^B)(PgUp)(BackSpace) 上捲一頁", - "(→)(PgDn)(Space) 下捲一頁", + "(^F)(PgDn)(Space)(→) 下捲一頁", "(0)(g)(Home) 檔案開頭", "($)(G)(End) 檔案結尾", "(;/:) 跳至某行/某頁", @@ -765,14 +862,15 @@ static const char * const pmore_help[] = { #ifdef DEBUG "(d) 切換除錯(debug)模式", #endif - "\01本系統使用 piaip 的新式瀏覽程式", + "\01本系統使用 piaip 的新式瀏覽程式: pmore, piaip's more", NULL }; /* * piaip's more, a replacement for old more */ -int pmore(char *fpath, int promptend) +int +pmore(char *fpath, int promptend) { int flExit = 0, retval = 0; int ch = 0; @@ -781,9 +879,6 @@ int pmore(char *fpath, int promptend) if(!mf_attach(fpath)) return -1; - /* reset and parse article header */ - mf_parseHeader(); - clear(); while(!flExit) { @@ -814,7 +909,7 @@ int pmore(char *fpath, int promptend) char buf[256]; // orz int prefixlen = 0; int barlen = 0; - int postfixlen = 0; + int postfix1len = 0, postfix2len = 0; if(mf_viewedAll()) printcolor = ANSI_COLOR(37;44); @@ -841,7 +936,7 @@ int pmore(char *fpath, int promptend) outs(ANSI_COLOR(1;30;47)); sprintf(buf, - " 閱\讀進度%3d%%, 目前顯示: 第 %02d~%02d 行 ", + " 閱\讀進度%3d%%, 目前顯示: 第 %02d~%02d 行", (int)((unsigned long)(mf.dispe-mf.start) * 100 / mf.len), (int)(mf.lineno + 1), (int)(mf.lineno + MFDISP_PAGE) @@ -849,22 +944,29 @@ int pmore(char *fpath, int promptend) outs(buf); prefixlen += strlen(buf); -#define TRAILINGMSGLEN (23) // trailing msg columns - postfixlen = TRAILINGMSGLEN; + postfix1len = 12; // check msg below + postfix2len = 10; - if (prefixlen + postfixlen + 1 > t_columns) - postfixlen = 0; - barlen = t_columns - 1 - postfixlen - prefixlen; + if (prefixlen + postfix1len + postfix2len + 1 > t_columns) + { + postfix1len = 0; + if (prefixlen + postfix1len + postfix2len + 1 > t_columns) + postfix2len = 0; + } + barlen = t_columns - 1 - postfix1len - postfix2len - prefixlen; while(barlen-- > 0) outc(' '); - if(postfixlen > 0) /* enough buffer */ + if(postfix1len > 0) outs( ANSI_COLOR(0;31;47) "(h)" ANSI_COLOR(30) "按鍵說明 " - ANSI_COLOR(31) "←[q]" - ANSI_COLOR(30) "離開 " + ); + if(postfix2len > 0) + outs( + ANSI_COLOR(0;31;47) "←[q]" + ANSI_COLOR(30) "離開 " ); outs(ANSI_RESET); FORCE_CLRTOEOL(); |