summaryrefslogtreecommitdiffstats
path: root/mbbsd/pmore.c
diff options
context:
space:
mode:
authorpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2005-06-04 16:21:08 +0800
committerpiaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2005-06-04 16:21:08 +0800
commita15f96c8170586be0eab917a40f322d63bddebcc (patch)
tree7a33aaa9fc2a154b1b08f6a4953f6a68db79becd /mbbsd/pmore.c
parent4929c8fb23010be17cf1daca876462562d75f93f (diff)
downloadpttbbs-a15f96c8170586be0eab917a40f322d63bddebcc.tar
pttbbs-a15f96c8170586be0eab917a40f322d63bddebcc.tar.gz
pttbbs-a15f96c8170586be0eab917a40f322d63bddebcc.tar.bz2
pttbbs-a15f96c8170586be0eab917a40f322d63bddebcc.tar.lz
pttbbs-a15f96c8170586be0eab917a40f322d63bddebcc.tar.xz
pttbbs-a15f96c8170586be0eab917a40f322d63bddebcc.tar.zst
pttbbs-a15f96c8170586be0eab917a40f322d63bddebcc.zip
pmore: wrap support, finally
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@2755 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd/pmore.c')
-rw-r--r--mbbsd/pmore.c316
1 files changed, 234 insertions, 82 deletions
diff --git a/mbbsd/pmore.c b/mbbsd/pmore.c
index c4a537d0..8191f8d8 100644
--- a/mbbsd/pmore.c
+++ b/mbbsd/pmore.c
@@ -21,8 +21,10 @@
* TODO:
* - Speed up with supporting Scroll [done]
* - Support PTT_PRINTS [done]
- * - Wrap long lines or left-right wide navigation
+ * - Wrap long lines [done]
+ * - left-right wide navigation
* - Big5 truncation
+ * - BBSMovie Support
*
* WONTDO:
* - The message seperator line is different from old more.
@@ -72,7 +74,7 @@ int debug = 0;
#define ESC_CHR '\x1b'
// Common ANSI commands.
-#define ANSI_RESET (ESC_STR "[m")
+#define ANSI_RESET ESC_STR "[m"
#define ANSI_COLOR(x) ESC_STR "[" #x "m"
#define STR_ANSICODE "[0123456789;,"
@@ -81,14 +83,13 @@ int debug = 0;
// and usually messed up when output ANSI quoted string.
// - A workaround is suggested by kcwu:
// https://opensvn.csie.org/traccgi/pttbbs/trac.cgi/changeset/519
-#define FORCE_CLRTOEOL() outs(ESC_STR "[K");
+#define FORCE_CLRTOEOL() outs(ESC_STR "[K")
// --------------------------- </Display>
// --------------------------- <Main Navigation>
typedef struct
{
unsigned char
- rawmode, // show file as-is.
*start, *end, // file buffer
*disps, *dispe, // disply start/end
*maxdisps; // a very special pointer,
@@ -96,31 +97,67 @@ typedef struct
off_t len; // file total length
long lineno, // lineno of disps
oldlineno, // last drawn lineno, < 0 means full update
+ //
+ wraplines, // wrapped lines in last display
+ dispedlines, // how many different lines displayed
+ // usually dispedlines = PAGE-wraplines,
+ // but if last line is incomplete(wrapped),
+ // dispedlines = PAGE-wraplines + 1
+ lastpagelines,// lines of last page to show
+ // this indicates how many lines can
+ // maxdisps(maxlinenoS) display.
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).
+ // 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 = {
- 0, 0, 0, 0, 0, 0,
- 0, 0, -1L, -1L
+ 0, 0, 0, 0, 0, 0L,
+ 0, 0, 0, -1L, -1L, -1L
}; // current file
-/* mf_* navigation commands and return value meanings */
+/* mf_* navigation commands return value meanings */
enum {
MFNAV_OK, // navigation ok
MFNAV_EXCEED, // request exceeds buffer
} MF_NAV_COMMANDS;
-// Navigation units (dynamic, so not in enum const)
+/* Navigation units (dynamic, so not in enum const) */
#define MFNAV_PAGE (t_lines-2) // when navigation, how many lines in a page to move
-#define MFDISP_PAGE (t_lines-1) // for display, the real number of lines to be shown.
+
+/* Display system */
+enum {
+ /* newline method (because of poor BBS implementation) */
+ MFDISP_NEWLINE_CLEAR = 0, // \n and cleartoeol
+ MFDISP_NEWLINE_SKIP,
+ MFDISP_NEWLINE_MOVE, // use move to simulate newline.
+
+ MFDISP_WRAP_TRUNCATE = 0,
+ MFDISP_WRAP_WRAP,
+
+} MF_DISP_CONST;
+
+#define MFDISP_PAGE (t_lines-1) // the real number of lines to be shown.
#define MFDISP_DIRTY() { mf.oldlineno = -1; }
+
+/* Indicators */
+#define MFDISP_TRUNC_INDICATOR ANSI_COLOR(0;1;37) ">" ANSI_RESET
+#define MFDISP_WRAP_INDICATOR ANSI_COLOR(0;1;37) "\\" ANSI_RESET
// --------------------------- </Main Navigation>
// --------------------------- <Aux. Structures>
+/* browsing preference */
+typedef struct
+{
+ int rawmode, // show file as-is.
+ wrapmode; // wrap?
+} MF_BrowsingPrefrence;
+
+MF_BrowsingPrefrence bpref =
+{ 0, MFDISP_WRAP_WRAP, };
+
/* pretty format header */
#define FH_HEADERS (4) // how many headers do we know?
#define FH_HEADER_LEN (4) // strlen of each heads
@@ -153,7 +190,7 @@ enum {
// Reset structures
#define RESETMF() { memset(&mf, 0, sizeof(mf)); \
- mf.maxlinenoS = mf.oldlineno = -1; }
+ mf.lastpagelines = mf.maxlinenoS = mf.oldlineno = -1; }
#define RESETFH() { memset(&fh, 0, sizeof(fh)); \
fh.lines = -1; }
@@ -161,11 +198,10 @@ enum {
// --------------------------------------------- </Defines and constants>
+// used by mf_attach
void mf_parseHeaders();
void mf_freeHeaders();
-
-int mf_backward(int); // used by mf_attach
-void mf_sync_lineno(); // used by mf_attach
+void mf_determinemaxdisps(int);
/*
* mmap basic operations
@@ -205,17 +241,7 @@ mf_attach(unsigned char *fn)
mf.disps = mf.dispe = mf.start;
mf.lineno = 0;
- // build maxdisps
- mf.disps = mf.end - 1;
- mf_backward(MFNAV_PAGE);
- mf.maxdisps = mf.disps;
-
-#ifdef PMORE_PRELOAD_SIZE
- if(mf.len <= PMORE_PRELOAD_SIZE)
- mf_sync_lineno(); // maxlinenoS will be automatically updated
- else
-#endif
- mf.maxlinenoS = -1;
+ mf_determinemaxdisps(MFNAV_PAGE);
mf.disps = mf.dispe = mf.start;
mf.lineno = 0;
@@ -259,6 +285,41 @@ mf_sync_lineno()
}
}
+int mf_backward(int); // used by mf_buildmaxdisps
+
+void
+mf_determinemaxdisps(int backlines)
+{
+ unsigned char *pbak = mf.disps, *mbak = mf.maxdisps;
+ long lbak = mf.lineno;
+
+ if( mf.lastpagelines >= 0 &&
+ mf.lastpagelines <= backlines)
+ return;
+
+ mf.disps = mf.end - 1;
+ mf_backward(backlines);
+ if(mf.disps != mbak)
+ {
+ mf.maxdisps = mf.disps;
+ mf.lastpagelines = backlines;
+
+ mf.maxlinenoS = -1;
+#ifdef PMORE_PRELOAD_SIZE
+ if(mf.len <= PMORE_PRELOAD_SIZE)
+ mf_sync_lineno(); // maxlinenoS will be automatically updated
+#endif
+ }
+ mf.disps = pbak;
+ mf.lineno = lbak;
+}
+
+/*
+ * mf_backwards is also used for maxno determination,
+ * so we cannot change anything in mf except these:
+ * mf.disps
+ * mf.lineno
+ */
int
mf_backward(int lines)
{
@@ -337,10 +398,6 @@ int
mf_goBottom()
{
mf.disps = mf.maxdisps;
- /*
- mf.disps = mf.end-1;
- mf_backward(MFNAV_PAGE);
- */
mf_sync_lineno();
return MFNAV_OK;
@@ -459,12 +516,12 @@ pmore_str_chomp(unsigned char *p)
unsigned char *pb = p + strlen(p)-1;
while (pb >= p)
- if(isascii(*pb) && isblank(*pb))
+ if(isascii(*pb) && isspace(*pb))
*pb-- = 0;
else
break;
pb = p;
- while (*pb && isascii(*pb) && isblank(*pb))
+ while (*pb && isascii(*pb) && isspace(*pb))
pb++;
if(pb != p)
@@ -501,15 +558,13 @@ void
mf_parseHeaders()
{
/* file format:
- * AUTHOR: author BOARD: blah <- headers[1], headers[0]
- * XXX: xxx <- headers[2]
+ * AUTHOR: author BOARD: blah <- headers[0], flaots[0], floats[1]
+ * XXX: xxx <- headers[1]
* XXX: xxx <- headers[n]
* [blank, fill with seperator] <- lines
*
* #define STR_AUTHOR1 "作者:"
* #define STR_AUTHOR2 "發信人:"
- * #define STR_POST1 "看板:"
- * #define STR_POST2 "站內:"
*/
unsigned char *pmf = mf.start;
int i = 0;
@@ -563,7 +618,7 @@ mf_parseHeaders()
// kill staring and trailing spaces
pmore_str_chomp(p);
- // special case, header[0] is in line[0].
+ // special case, floats are in line[0].
if(i == 0 && (pb = strrchr(p, ':')) != NULL && *(pb+1))
{
unsigned char *np = strdup(pb+1);
@@ -572,7 +627,7 @@ mf_parseHeaders()
pmore_str_chomp(np);
// remove quote and traverse back
*pb-- = 0;
- while (pb > p && *pb != ',' && !(isascii(*pb) && isblank(*pb)))
+ while (pb > p && *pb != ',' && !(isascii(*pb) && isspace(*pb)))
pb--;
if (pb > p) {
@@ -587,28 +642,46 @@ mf_parseHeaders()
}
}
-static
-void MFDISP_SKIPCURLINE()
+/*
+ * mf_disp utility macros
+ */
+inline static void
+MFDISP_SKIPCURLINE()
{
while (mf.dispe < mf.end && *mf.dispe != '\n')
mf.dispe++;
}
+inline static int
+MFDISP_DBCS_HEADERWIDTH(int originalw)
+{
+ return originalw - (originalw %2);
+// return (originalw >> 1) << 1;
+}
+
/*
* display mf content from disps for MFDISP_PAGE
*/
void
mf_disp()
{
- int lines = 0, col = 0, currline = 0;
+ int lines = 0, col = 0, currline = 0, wrapping = 0;
int startline = 0, endline = MFDISP_PAGE-1;
- // int x_min = 0;
- int x_max = t_columns - 1;
- int header_w = (t_columns - 1 - (t_columns-1)%2);
- /*
- * it seems like that BBS scroll has some bug
- * if we scroll a fulfilled buffer, so leave one space.
+
+ /* why t_columns-1 here?
+ * because BBS systems usually have a poor terminal system
+ * and many stupid clients behave differently.
+ * So we try to avoid using the last column, leave it for
+ * BBS to place '\n' and CLRTOEOL.
*/
+ const int headerw = MFDISP_DBCS_HEADERWIDTH(t_columns-1);
+ const int dispw = headerw - (t_columns - headerw < 2);
+ const int maxcol = dispw - 1;
+
+ if(mf.wraplines)
+ MFDISP_DIRTY(); // we can't scroll with wrapped lines.
+ mf.wraplines = 0;
+ mf.dispedlines = 0;
#ifdef PMORE_USE_OPT_SCROLL
/* process scrolling */
@@ -663,41 +736,43 @@ mf_disp()
while (lines < MFDISP_PAGE)
{
int inAnsi = 0;
- int skip = 0;
+ int newline = MFDISP_NEWLINE_CLEAR;
currline = mf.lineno + lines;
col = 0;
+
+ if(!wrapping)
+ mf.dispedlines++;
/* Is currentline visible? */
if (lines < startline || lines > endline)
{
while(mf.dispe < mf.end && *mf.dispe != '\n')
mf.dispe++;
- skip = 1; /* prevent printing trailing '\n' */
+ newline = MFDISP_NEWLINE_SKIP;
}
/* Now, consider what kind of line
* (header, seperator, or normal text)
* is current line.
*/
- else if (!mf.rawmode && currline == fh.lines)
+ else if (!bpref.rawmode && currline == fh.lines)
{
/* case 1, header seperator line */
outs(ANSI_COLOR(36));
- for(col = 0; col < header_w; col+=2)
+ for(col = 0; col < headerw; col+=2)
{
// prints("%02d", col);
outs("─");
}
outs(ANSI_RESET);
MFDISP_SKIPCURLINE();
- col = x_max-1;
}
- else if (!mf.rawmode && currline < fh.lines)
+ else if (!bpref.rawmode && currline < fh.lines)
{
/* case 2, we're printing headers */
const char *val = fh.headers[currline];
const char *name = _fh_disp_heads[currline];
- int w = header_w - FH_HEADER_LEN - 3;
+ int w = headerw - FH_HEADER_LEN - 3;
outs(ANSI_COLOR(47;34) " ");
outs(name);
@@ -723,7 +798,6 @@ mf_disp()
outs(ANSI_RESET);
MFDISP_SKIPCURLINE();
- col = x_max-1;
}
else if(mf.dispe < mf.end)
{
@@ -731,9 +805,10 @@ mf_disp()
long dist = mf.end - mf.dispe;
long flResetColor = 0;
int srlen = -1;
+ int breaknow = 0;
// first check quote
- if(!mf.rawmode)
+ if(!bpref.rawmode)
{
if(dist > 1 &&
(*mf.dispe == ':' || *mf.dispe == '>') &&
@@ -750,13 +825,13 @@ mf_disp()
}
}
- while(mf.dispe < mf.end && *mf.dispe != '\n')
+ while(!breaknow && mf.dispe < mf.end && *mf.dispe != '\n')
{
if(inAnsi)
{
if (!strchr(STR_ANSICODE, *mf.dispe))
inAnsi = 0;
- if(col <= x_max)
+ if(col <= maxcol)
outc(*mf.dispe);
} else {
if(*mf.dispe == ESC_CHR)
@@ -784,11 +859,14 @@ mf_disp()
strncpy(buf, mf.dispe, 3); // ^[[*s
mf.dispe += 2;
- Ptt_prints(buf, NO_RELOAD); // result in buf
+ if(bpref.rawmode)
+ buf[0] = '*';
+ else
+ Ptt_prints(buf, NO_RELOAD); // result in buf
i = strlen(buf);
- if (col + i > x_max)
- i = x_max - col;
+ if (col + i > maxcol)
+ i = maxcol - col;
if(i > 0)
{
buf[i] = 0;
@@ -799,8 +877,22 @@ mf_disp()
} else
#endif
{
- if(col <= x_max)
+ if(col <= maxcol)
outc(*mf.dispe);
+ else switch (bpref.wrapmode)
+ {
+ case MFDISP_WRAP_WRAP:
+ breaknow = 1;
+ wrapping = 1;
+ mf.wraplines ++;
+ break;
+ case MFDISP_WRAP_TRUNCATE:
+ breaknow = 1;
+ MFDISP_SKIPCURLINE();
+ wrapping = 0;
+ break;
+ }
+
if(!inAnsi)
{
col++;
@@ -811,28 +903,57 @@ mf_disp()
}
}
}
- mf.dispe ++;
+ if(!breaknow)
+ mf.dispe ++;
}
if(flResetColor)
outs(ANSI_RESET);
+
+ /* "wrapping" should be only in normal text section.
+ * We don't support wrap within scrolling,
+ * so if we have to wrap, invalidate all lines.
+ */
+ if(breaknow)
+ {
+ if(wrapping)
+ endline = MFDISP_PAGE-1;
+
+ if(wrapping)
+ outs(MFDISP_WRAP_INDICATOR);
+ else
+ outs(MFDISP_TRUNC_INDICATOR);
+ }
+ else
+ wrapping = 0;
}
- if(mf.dispe < mf.end)
+ if(mf.dispe < mf.end && *mf.dispe == '\n')
mf.dispe ++;
+ // else, we're in wrap mode.
- if(!skip)
+ switch(newline)
{
- if(col < x_max) /* can we do so? */
- {
+ case MFDISP_NEWLINE_SKIP:
+ break;
+ case MFDISP_NEWLINE_CLEAR:
FORCE_CLRTOEOL();
outc('\n');
- }
- else
- // outc('>'),
+ break;
+ case MFDISP_NEWLINE_MOVE:
move(lines+1, 0);
+ break;
}
lines ++;
}
+ /*
+ * we've displayed the file.
+ * but if we got wrapped lines in last page,
+ * mf.maxdisps may be required to be larger.
+ */
+ if(mf.disps == mf.maxdisps && mf.dispe < mf.end)
+ {
+ mf_determinemaxdisps(0);
+ }
mf.oldlineno = mf.lineno;
}
@@ -856,7 +977,7 @@ static const char * const pmore_help[] = {
"(f/b) 跳至下/上篇",
"(a/A) 跳至同一作者下/上篇",
"(t/[-/]+) 主題式閱\讀:循序/前/後篇",
- "(\\) 切換顯示原始內容", // this IS already aligned!
+ "(w/\\) 切換顯示原始內容/自動折行", // this IS already aligned!
"(q)(←) 結束",
"(h)(H)(?) 本說明畫面",
#ifdef DEBUG
@@ -865,6 +986,24 @@ static const char * const pmore_help[] = {
"\01本系統使用 piaip 的新式瀏覽程式: pmore, piaip's more",
NULL
};
+/*
+ * pmore utility macros
+ */
+inline static void
+PMORE_UINAV_FORWARDPAGE()
+{
+ /* Usually, a forward is just mf_forward(MFNAV_PAGE);
+ * but because of wrapped lines...
+ * This function is used when user tries to nagivate
+ * with page request.
+ * If you want to a real page forward, don't use this.
+ * That's why we have this special function.
+ */
+ int i = mf.dispedlines - 1;
+ if(i < 1)
+ i = 1;
+ mf_forward(i);
+}
/*
* piaip's more, a replacement for old more
@@ -891,10 +1030,10 @@ pmore(char *fpath, int promptend)
// clrtoeol(); // this shall be done in mf_disp to speed up.
/* PRINT BOTTOM STATUS BAR */
+#ifdef DEBUG
if(debug)
- prints("L#%d pmt=%d Dsp:%08X/%08X/%08X, F:%08X/%08X(%d) tScr(%dx%d)",
- (int)mf.lineno,
- promptend,
+ prints("L#%ld(w%ld) pmt=%d Dsp:%08X/%08X/%08X, F:%08X/%08X(%d) tScr(%dx%d)",
+ mf.lineno, mf.wraplines, promptend,
(unsigned int)mf.disps,
(unsigned int)mf.maxdisps,
(unsigned int)mf.dispe,
@@ -904,6 +1043,7 @@ pmore(char *fpath, int promptend)
t_lines
);
else
+#endif
{
char *printcolor;
char buf[256]; // orz
@@ -939,7 +1079,7 @@ pmore(char *fpath, int promptend)
" 閱\讀進度%3d%%, 目前顯示: 第 %02d~%02d 行",
(int)((unsigned long)(mf.dispe-mf.start) * 100 / mf.len),
(int)(mf.lineno + 1),
- (int)(mf.lineno + MFDISP_PAGE)
+ (int)(mf.lineno + mf.dispedlines)
);
outs(buf); prefixlen += strlen(buf);
@@ -1029,7 +1169,7 @@ pmore(char *fpath, int promptend)
case Ctrl('F'):
case KEY_PGDN:
- mf_forward(MFNAV_PAGE);
+ PMORE_UINAV_FORWARDPAGE();
break;
case Ctrl('B'):
case KEY_PGUP:
@@ -1062,13 +1202,13 @@ pmore(char *fpath, int promptend)
if (mf_viewedAll())
flExit = 1, retval = READ_NEXT;
else
- mf_forward(MFNAV_PAGE);
+ PMORE_UINAV_FORWARDPAGE();
break;
case KEY_RIGHT:
if(mf_viewedAll())
promptend = 0, flExit = 1, retval = 0;
else
- mf_forward(MFNAV_PAGE);
+ PMORE_UINAV_FORWARDPAGE();
break;
case KEY_UP:
@@ -1088,7 +1228,7 @@ pmore(char *fpath, int promptend)
if (mf_viewedAll())
flExit = 1, retval = RELATE_NEXT;
else
- mf_forward(MFNAV_PAGE);
+ PMORE_UINAV_FORWARDPAGE();
break;
/* ------------------ SEARCH KEYS ------------------ */
case '/':
@@ -1170,8 +1310,20 @@ pmore(char *fpath, int promptend)
return 0;
}
break;
+ case 'w':
+ switch(bpref.wrapmode)
+ {
+ case MFDISP_WRAP_WRAP:
+ bpref.wrapmode = MFDISP_WRAP_TRUNCATE;
+ break;
+ case MFDISP_WRAP_TRUNCATE:
+ bpref.wrapmode = MFDISP_WRAP_WRAP;
+ break;
+ }
+ MFDISP_DIRTY();
+ break;
case '\\':
- mf.rawmode = !mf.rawmode;
+ bpref.rawmode = !bpref.rawmode;
MFDISP_DIRTY();
break;
#ifdef DEBUG