回精華區
作者TKyo.bbs@cpu.tfcis.org (暗黑貴公子) 看板: plan
標題[更新 2]PTT's pmore.c 功能 patch for M3 more.c
時間動力核心 (2006/06/14 Wed 22:46:50) Updated: 2006/06/16
[前版前言]

  1.創作本功能前, 已經先行知會 PTT (批踢踢實業坊) BBS 站長 in2
    並獲得他允許 "創意創作" 而後, 自行依照其功能面搭配原本 more.c 程式碼創作
    且未參考 pmore.c 任何程式碼

  2.創作本功能後, 也已經知會 PTT (批踢踢實業坊) BBS pmore.c 原創者 piaip
    並獲得他允許, 將 patch 原始碼交由 itoc 公佈

[本板前言]

  本次更新版本, 完全取代先前發表之版本

[功能面簡介]

  a.按鍵 :
                   Tab - 向右翻閱一次 4 列
         Shift+Tab/Z/z - 向左翻閱一次 4 列
                   ./> - 向右翻閱一次 1 列 (第一次移動 2 列)
                   ,/< - 向左翻閱一次 1 列

  b.第一次按 . 向右翻閱時, 由於有 < 提示符號的關係
    如果只移動 1 列的話, 會讓使用者感覺不出來畫面有在移動, 因此才移動 2 列

  c.PCMan/KKMan(尚未實驗) 不支援 Shift+Tab 按鍵, 會誤判為 Tab 按鍵
    請用 zZ 替代

[本次更新]

  1.當 "一行字數 > 80 行" 時, 畫面再次整理動態移動及特別顯示

    特別顯示 (Header 區域例外)

      '\' - 為自動斷行符號
      '>' - 文章位移後, 表示尚有文章未顯示

  2.修正 :

      擷取 Header 資訊時, 原先使用 f_map() 方式, <== 浪費了太多時間
      故改為使用一般檔案 I/O 方式, 得知是否有 Header, 並適時插入分隔線

      其他額外修正

------------------------------------------------------------------------------
:src/include/global.h

  #define KEY_TAB   9   /* 和 Ctrl('I') 相同 */
+ #define KEY_STAB  90  /* 和 Z 相同 */

  #define QUOTE_CHAR1   '>'
  #define QUOTE_CHAR2   ':'
+ #define QUOTE_CHAR3   '='
+ #define QUOTE_CHAR4   '<'
+ #define QUOTE_CHAR5   '\\'


/* 新板本已將 LINE_HEADER 搬移至 global.h
   給 more.c/bhttpd.c 使用 */

+ #define LINE_HEADER   3   /* 檔頭有三行 */
+ #define STR_HEADER  {"作者", "標題", "時間"}
+ #define STR_HEADER2 {"發信人", "標  題", "發信站"}

------------------------------------------------------------------------------
:scr/include/theme.h

- #define FOOTER_MORE \
- COLOR1 " 瀏覽 P.%d (%d%%) " COLOR2 " (h)說明 [PgUp][PgDn][0][$]移動" \
- " (/n)搜尋 (C)暫存 (←q)結束 "

+ #define FOOTER_MORE \
+ COLOR1 " 瀏覽 P.%d (%d%%) " COLOR2 " (/n)搜尋 (h)說明 (←/q)離開%*s" \
+ "║列:%d - %d│行:%d - %d║ "

+ /* ----------------------------------------------------- */
+ /* 文章閱讀顏色                                          */
+ /* ----------------------------------------------------- */

+ #define COLOR_MORE_MASK   "\033[7m"    /* 比對正確 Color */
+ #define COLOR_MORE_OFFSET "\033[1;37m" /* 特別顯示符號顏色 */
+ #define COLOR_MORE_QUOTE  "\033[1;32m" /* 第一層 QUOTE 顏色 1 */
+ #define COLOR_MORE_QUOTE2 "\033[35m"   /* 第二層 QUOTE 顏色 2 */
+ #define COLOR_MORE_QUOTE3 "\033[32m"   /* 第三層 QUOTE 顏色 3 */

------------------------------------------------------------------------------
:src/maple/visio.c

/*
   新增兩函式 outa() 和 outax()

   outa() - 原先設計給光棒介面中文正確斷行使用
   outax() - 設計給 more.c 專用, 支援中文正確斷行及特殊 Ansi Code
             並回傳該輸出多少 space

   因應 outax(), 更新 outx(), 新增 out_extend_ansi_char() 給兩函式共用函式
*/

#ifdef SHOW_USER_IN_TEXT

static int
out_extend_ansi_char(ch)
  uschar ch;
{
  char buf[20];

  switch (ch)
  {
    case 's':           /* **s 顯示 ID */
      sprintf(buf, "%s", cuser.userid);
      break;

    case 'n':           /* **n 顯示暱稱 */
      sprintf(buf, "%s", cuser.username);
      break;

    case 'l':           /* **l 顯示上站次數 */
      sprintf(buf, "%d", cuser.numlogins);
      break;

    case 'p':           /* **p 顯示發文次數 */
      sprintf(buf, "%d", cuser.numposts);
      break;

    case 'g':           /* **g 顯示金幣 */
      sprintf(buf, "%d", cuser.gold);
      break;

    case 'm':           /* **m 顯示銀幣 */
      sprintf(buf, "%u", cuser.money);
      break;

    case 't':           /* **t 顯示日期 */
      sprintf(buf, "%s", Now());
      break;

    case 'u':           /* **u 顯示線上人數 */
      sprintf(buf, "%d", total_user);
      break;

    case 'b':           /* **b 顯示站名 */
      sprintf(buf, "%s", BBSNAME);
      break;

    default:
      *buf = '\0';
  }

  if (*buf)
  {
    outs(buf);

    return strlen(buf);
  }
  else
    return 0;
}

void
outx(str)
  uschar *str;
{
  int ch;

  while (ch = *str)
  {
    /* itoc.020301: ESC + * + s 等控制碼 */
    if (ch == KEY_ESC && str[1] == '*')
    {
      if (out_extend_ansi_char(str[2]))
      {
        str += 3;
        continue;
      }
    }
    outc(ch);
    str++;
  }
}

char
outax(str, max)
  uschar *str;
  int max;
{
  uschar old_ch, ch;
  char *ptr, ret;
  char ansi_buf[80];                          /* 雙色字緩衝區 */
  int len, in_ansi, in_chi, i;

  in_chi = in_ansi = len = ret = i = 0;
  *ansi_buf = '\0';

  ptr = str;

  while ((ch = *ptr++))
  {
    if (in_ansi)
    {
      if (!(strchr(STR_ANSICODE, ch)))
      {
        in_ansi = 0;

        if (i)
          ansi_buf[i++] = ch;
      }
    }
    else
    {
      if (ch == KEY_ESC)
      {
        in_ansi = 1;

        if (*ptr == '*')
        {
          in_ansi = out_extend_ansi_char(ptr[1]);

          if (in_ansi)
          {
            len += in_ansi;
            ptr += 2;
            in_ansi = 0;
            continue;
          }
        }
      }
      else
      {
        if (in_chi || IS_ZHC_HI(ch))
        {
          in_chi ^= 1;
          if (in_chi)
            old_ch = ch;
          else
          {
            outc(old_ch);

            if (i)
            {
              ansi_buf[i] = '\0';
              outs(ansi_buf);
              i = 0;
              *ansi_buf = '\0';
            }
          }
        }

        len++;
      }
    }

    if (in_chi && in_ansi)
      ansi_buf[i++] = ch;
    else if (!(in_chi))
      outc(ch);

    if (len >= max)
    {
      if (in_ansi || (*ptr == KEY_ESC))
        continue;

      /* 解決 '>' 誤判問題, 及 ANSI Code */
      if (*(ptr + 1) == '\0' || *(ptr + 1) == KEY_ESC)
        continue;

      ret++;

      if (in_chi)
        ret++;

      break;
    }
  }

  return ret;
}

char
outa(str, max, end)
  uschar *str;
  int max;
  int end;
{
  uschar old_ch, ch;
  char *ptr, ret;
  int len, in_ansi, in_chi;

  in_chi = in_ansi = len = ret = 0;
  ptr = str;

  while ((ch = *ptr++))
  {
    if (in_chi || IS_ZHC_HI(ch))
      in_chi ^= 1;

    if (in_ansi)
    {
      if (!(strchr(STR_ANSICODE, ch)))
        in_ansi = 0;
    }
    else
    {
      if (ch == KEY_ESC)
        in_ansi = 1;
      else if (++len >= max)
        break;
    }
  }

  if (ch)
  {
    old_ch = *ptr;
    *ptr = '\0';
  }

  max = (in_chi) ? max - 1 : max;

  prints("%-*.*s", max, max, str);

  if (ch)
    *ptr = old_ch;

  if (in_chi)
  {
    outc(' ');
    ret++;
  }

  if (end)
    outs(str_ransi);

  return ret;
}

------------------------------------------------------------------------------
:src/maple/more.c

+ typedef struct MORELINE
+ {
+   char data[ANSILINELEN];
+   char auto_break_flag;     /* 0: 無換行, 1: 有換行 */
+   int more_offset;          /* 畫面偏移值 */
+   int b_cols;               /* bottom columns length */
+ #ifdef SLIDE_SHOW
+   int slideshow;            /* !=0: 撥放 movie 的速度 */
+ #endif
+   off_t header_offset;      /* Header 區域 */
+ }        MORELINE;


static uschar *fimage;      /* file image begin */
static uschar *fend;        /* file image end */
static uschar *foff;        /* 目前讀到哪裡 */
+ static uschar *flast;     /* last page start offset address*/

------------------------------------------------------------------------------
:src/maple/more.c - more_slideshow()

/* 因應本次更新修正 */

static int
more_slideshow(moreline)
  MORELINE *moreline;
{
  int ch;

  if (moreline->slideshow == 0)
  {
    ch = vkey();

    if (ch == '@')
    {
      moreline->slideshow =
        vans("請選擇放映的速度 1(最慢)~9(最快)?撥放中按任意鍵可停止撥放:")
        - '0';
      if (moreline->slideshow < 1 || moreline->slideshow > 9)
        moreline->slideshow = 5;

      ch = KEY_PGDN;
    }
  }
  else
  {
    struct timeval tv[9] =
    {
      {4, 0}, {3, 0}, {2, 0}, {1, 500000}, {1, 0},
      {0, 800000}, {0, 600000}, {0, 400000}, {0, 200000}
    };

    refresh();
    ch = 1;
    if (select(1, (fd_set *) &ch, NULL, NULL,
      tv + moreline->slideshow - 1) > 0)
    {
      /* 若撥放中按任意鍵,則停止撥放 */
      moreline->slideshow = 0;
      ch = vkey();
    }
    else
    {
      ch = KEY_PGDN;
    }
  }

  return ch;
}

------------------------------------------------------------------------------
:src/maple/more.c - more_line()

/*
   mode = 0 : 一般模式
   mode = 1 : Get Offset
*/

static int
more_line(moreline, fsrc, mode)
  MORELINE *moreline;
  uschar *fsrc;
  char mode;
{
  int len, bytes, in_ansi, in_chi;
  uschar ch, *offset;
  char *buf, no_auto_break;

  len = bytes = in_ansi = in_chi = moreline->auto_break_flag = 0;
  offset = fsrc;

  if (offset <= fimage + moreline->header_offset)
    no_auto_break = 1;
  else
    no_auto_break = moreline->more_offset;

  if (mode == 0)
    buf = moreline->data;
  for (;;)
  {
    if (offset >= fend)
      break;

    ch = *offset;

    offset++;
    bytes++;

    if (ch == '\n')
      break;

    if (in_ansi)
    {
      if (!(strchr(STR_ANSICODE, ch)))
        in_ansi = 0;
    }
    else
    {
      if (ch == KEY_ESC)
        in_ansi = 1;
      else
      {
        if (isprint2(ch))
        {
          if (in_chi || IS_ZHC_HI(ch))
            in_chi ^= 1;
        }
        else
          ch = ' ';     /* 印出不來的都換成空白 */

        len++;
      }
    }

    if (mode == 0)
      *buf++ = ch;

    /* 若不含控制碼的長度已達 b_cols 字,或含控制碼的長度已達 ANSILINELEN-1
       ,那麼離開迴圈 */

    if ((bytes >= ANSILINELEN - 1) ||
      (no_auto_break == 0 && (len >= moreline->b_cols)))
    {
      /* itoc.031123: 如果是控制碼,即使不含控制碼的長度已達 b_cols 了
         ,還可以繼續吃 */
      if ((in_ansi || (offset < fend && *offset == KEY_ESC)) &&
        bytes < ANSILINELEN - 1)
        continue;

      /* itoc.031123: 再檢查下一個字是不是 '\n',避免恰好是 b_cols 或
         ANSILINELEN-1 時,會多跳一行空白行 */
      if (offset < fend && *offset == '\n')
      {
        offset++;
        bytes++;
      }
      /* 因應 PTT 80 行顯示 (moreline->b_cols 為 79, 可刪除此判斷) */
      else if (offset < fend && (*(offset + 1) == '\n' || *(offset + 1) == KEY_ESC))
        continue;
      else
      {
        if (in_chi)
        {
          offset--;
          bytes--;
          buf--;
        }

        if (no_auto_break == 0)
        {
          moreline->auto_break_flag++;

          if (in_chi)
            moreline->auto_break_flag++;
        }
      }

      break;
    }
  }

  if (mode == 0)
  {
    *buf = '\0';
    foff = offset;
  }

  return bytes;
}

------------------------------------------------------------------------------
:src/maple/more.c - outs_line()

/* 修改幅度大, 請整個函式換掉 */

static int
outs_line(moreline)     /* 印出一般內容 */
  MORELINE *moreline;
{
  int ch1, ch2, ch3, ansi, curr_offset, in_ansi, in_chi;
  char *str, *ptr, last_ansi_code[40], num_ch_space;
  char buf[ANSILINELEN];
  off_t ret;

  /* ※處理引用者 & 引言 */

  ptr = str = moreline->data;

  ch1 = str[0];
  ch2 = str[1];
  ch3 = str[2];
  last_ansi_code[0] = '\0';

  if ((ch1 == '\241' && ch2 == '\260') ||   /* ※ ==> 引言者 */
    (ch1 == QUOTE_CHAR3 && ch2 == QUOTE_CHAR3 && ch3 == QUOTE_CHAR1))
  {
    ansi = 1;
    strcpy(last_ansi_code, COLOR_MORE_QUOTE);
    outs(last_ansi_code);
  }
  /* 引言 */
  else if ((ch2 == ' ') && (ch1 == QUOTE_CHAR1 || ch1 == QUOTE_CHAR2))
  {
    ansi = 1;
    strcpy(last_ansi_code, (ch3 == QUOTE_CHAR1 || ch3 == QUOTE_CHAR2) ?
      COLOR_MORE_QUOTE2 : COLOR_MORE_QUOTE3);
    outs(last_ansi_code);   /* 引用一層/二層不同顏色 */
  }
  else
    ansi = 0;

  /* 計算偏移及取得最後 ANSI Code*/

  if (moreline->more_offset)
  {
    curr_offset = in_ansi = in_chi = ch2 = 0;

    prints(COLOR_MORE_OFFSET "%c%s%s", QUOTE_CHAR4, str_ransi,
     (ansi) ? last_ansi_code : "");

    while (ch1 = *ptr)
    {
      if (ch1 == KEY_ESC)
      {
        last_ansi_code[ch2++] = ch1;
        ansi = in_ansi = 1;
      }
      else if (in_ansi)
      {
        if (!strchr(STR_ANSICODE, ch1))
          in_ansi = 0;

        last_ansi_code[ch2++] = ch1;
        last_ansi_code[ch2] = '\0';

        if (in_ansi == 0)
        {
          outs(last_ansi_code);
          ch2 = 0;
        }
      }
      else
      {
        if (moreline->more_offset == curr_offset)
          break;

        if (in_chi || IS_ZHC_HI(ch1))
          in_chi ^= 1;

        curr_offset++;
      }
      ptr++;
    }

    if (in_chi)
      ptr++;
  }

  ret = ptr - str;

  /* 印出內容 */

  if (hunt[0])
  {
    int len;
    char *ptr1, *ptr2;

    len = strlen(hunt);
    ptr2 = buf;

    while (1)
    {
      if (!(ptr1 = (char *) str_sub(ptr, hunt)))
      {
        strcpy(ptr2, ptr);
         break;
      }

      /* buf 空間不夠 */
      if (buf + ANSILINELEN - 1 <= ptr2 + (ptr1 - ptr) + (len + 7))
        break;

      str_ncpy(ptr2, ptr, ptr1 - ptr + 1);
      ptr2 += ptr1 - ptr;
      sprintf(ptr2, COLOR_MORE_MASK "%.*s%s", len, ptr1, str_ransi);
      ptr2 += len + 7;
      ptr = ptr1 + len;
    }

    ptr = buf;
  }

  ch1 = moreline->b_cols;

  if (moreline->more_offset)
  {
    if (moreline->more_offset > 1);
      ch1--;

    if (in_chi)
    {
      outc(' ');
      ch1--;
    }
  }

#ifdef SHOW_USER_IN_TEXT
  num_ch_space = outax(ptr, ch1);
#else
  num_ch_space = outa(ptr, ch1, 1);
#endif

  if (moreline->auto_break_flag--)
  {
    if (moreline->auto_break_flag)
      outc(' ');

    prints(COLOR_MORE_OFFSET "%c", QUOTE_CHAR5);
    ansi = 1;
  }
  else if (moreline->more_offset && num_ch_space--)
  {
    if (num_ch_space)
      outc(' ');

    prints(COLOR_MORE_OFFSET "%c", QUOTE_CHAR1);
    ansi = 1;
  }

  if (ansi)
    outs(str_ransi);

  return ret;
}

------------------------------------------------------------------------------
:src/maple/more.c - outs_header()

/* 修改幅度大, 請整個函式換掉 */

static char header1[LINE_HEADER][LEN_AUTHOR1] = STR_HEADER;
static char header2[LINE_HEADER][LEN_AUTHOR2] = STR_HEADER2;

static void
outs_header(moreline, header_len)       /* 印出檔頭 */
  MORELINE *moreline;
  int header_len;
{
  int i;
  char *buf, *ptr, *word;

  buf = moreline->data;

  /* 處理檔頭 */

  for (i = 0; i < LINE_HEADER; i++)
  {
    if ((header_len == LEN_AUTHOR1 &&
      !memcmp(buf, header1[i], LEN_AUTHOR1 - 1)) ||
      (header_len == LEN_AUTHOR2 &&
      !memcmp(buf, header2[i], LEN_AUTHOR2 - 1)))
    {
      word = buf + header_len;

      /* 作者/看板 檔頭有二欄,特別處理 */
      if (i == 0 &&
        ((ptr = strstr(word, str_post1)) || (ptr = strstr(word, str_post2))))
      {
        if (ptr > word)
          ptr[-1] = '\0';

        ptr[4] = '\0';

        prints(COLOR5 " %s " COLOR6 "%-*.*s" COLOR5 " %s " COLOR6
          "%-13s\033[m", header1[0], d_cols + 53, d_cols + 53, word
          , ptr, ptr + 5, str_ransi);
      }
      else
      {
        /* 其他檔頭都只有一欄 */
        prints(COLOR5 " %s " COLOR6 "%-*.*s\033[m",
          header1[i], d_cols + 72, d_cols + 72, word, str_ransi);
      }

      return;
    }
  }

  /* 如果不是檔頭,就當一般文字印出 */
  outs_line(buf);
}

------------------------------------------------------------------------------
:src/maple/more.c - outs_footer()

/* 修改幅度大, 請整個函式換掉 */

static void
outs_footer(moreline, lino, fsize)
  MORELINE *moreline;
  int lino;
  int fsize;
{
  int i, j;

  j = moreline->b_cols + moreline->more_offset;

  sprintf(moreline->data, FOOTER_MORE, (lino - 2) / PAGE_SCROLL + 1,
    ((foff - fimage) * 100) / fsize, 0, "", (lino > PAGE_SCROLL) ?
    lino - PAGE_SCROLL : 1, lino, moreline->more_offset + 1, j);

  i = b_cols + strlen(COLOR1) + (strlen(COLOR2) * 3) + (strlen(FCOLOR) * 3)
      - strlen(moreline->data) - 1;

  sprintf(moreline->data, FOOTER_MORE, (lino - 2) / PAGE_SCROLL + 1,
    ((foff - fimage) * 100) / fsize, i, "", (lino > PAGE_SCROLL) ?
    lino - PAGE_SCROLL : 1, lino, moreline->more_offset + 1, j);

  outz(moreline->data);
  outs(str_ransi);
}

------------------------------------------------------------------------------
/* 整個函式換掉, 更動幅度大 */
:src/maple/more.c - more()

int
more(fpath, footer)
  char *fpath;
  char *footer;
{
  MORELINE moreline;
  FILE *fp;
  struct stat st;
  char *str;
  int i;
  int shift;            /* 還需要往下移動幾列 */
  int lino;             /* 目前 line number */
  int header_len;       /* 檔頭的長度,同時也是站內/站外信的區別 */
  int key;              /* 按鍵 */
  int cmd;              /* 中斷時所按的鍵 */
  int fsize;            /* 檔案大小 */
  int hunt_index;       /* 搜尋索引 */
  int curr_refresh;
  /* 每 32 列為一個 block,記錄此 block 的 offset */
  static off_t block[MAXBLOCK];
  off_t header_end;     /* 檔頭結束 */
  off_t header_end2;    /* 檔頭分隔線 */

  if (!(fp = fopen(fpath, "r")))
    return -1;

  if (fstat(fileno(fp), &st) || !(S_ISREG(st.st_mode)) ||
   (fsize = st.st_size) <= 0)
  {
    fclose(fp);
    return -1;
  }

  lino = strlen(BCOLOR MSG_SEPERATOR "\033[m\n");

  if (!(fimage = malloc(fsize + lino)))
    return -1;

  foff = fimage;
  fend = foff + fsize;

  memset(&moreline, 0, sizeof(MORELINE));
  moreline.b_cols = b_cols - ((currbattr & BRD_NOTRAN) ? 1 : 0);
  header_end = header_end2 = hunt_index = curr_refresh = 0;

  for (i = 0; ; i++)
  {
    if (feof(fp) || !(str = fgets(foff, MORE_BUFSIZE, fp)))
      break;

    shift = strlen(str);
    foff += shift;

    /* 讀出檔案第一列,來判斷站內信還是站外信 */
    if (i == 0)
    {
      header_len =
        !memcmp(str, str_author1, LEN_AUTHOR1) ? LEN_AUTHOR1 :
        /* 「作者:」表站內文章 */
        !memcmp(str, str_author2, LEN_AUTHOR2) ? LEN_AUTHOR2 :
        /* 「發信人:」表轉信文章 */
        0;                              /* 沒有檔頭 */
    }

    if (i >= (header_len - 2))
      break;

    if ((header_len == LEN_AUTHOR1 &&
          !memcmp(str, header1[i], LEN_AUTHOR1 - 1)) ||
        (header_len == LEN_AUTHOR2 &&
           !memcmp(str, header2[i], LEN_AUTHOR2 - 1)))
      header_end = moreline.header_offset = foff - fimage;
    else
    {
      if (i < (header_len - 2))
        break;
    }
  }

  if (header_len)
  {
    foff -= shift;
    memcpy(foff + lino, foff, shift);
    memcpy(foff, BCOLOR MSG_SEPERATOR "\033[m\n", lino);
    foff += lino + shift;
    fsize += lino;
    fend = fimage + fsize;
    header_end2 = moreline.header_offset = header_end + lino;
  }

  while ((foff < fend) && (feof(fp) == 0))
    foff += fread(foff, MORE_BUFSIZE, MORE_BUFSIZE, fp);

  fclose(fp);

  /* 歸零 */
  flast = foff = fimage;

  lino = key = cmd = 0;
  memset(&block, 0, sizeof(off_t) * MAXBLOCK);

  if (hunt[0])      /* 在 xxxx_browse() 請求搜尋字串 */
  {
    str_lowest(hunt, hunt);
    shift = HUNT_MASK | HUNT_START;
  }
  else
    shift = b_lines;

  clear();

  for(;;)
  {
    if (more_line(&moreline, foff, 0))
    {
      /* ------------------------------------------------- */
      /* 印出一列的文字                                    */
      /* ------------------------------------------------- */

      /* 首頁前幾列才需要處理檔頭 */
      if (foff <= fimage + header_end)
        outs_header(&moreline, header_len);
      else
      {
        if (foff <= fimage + header_end2)
#ifdef SHOW_USER_IN_TEXT
          outax(moreline.data, moreline.b_cols);
#else
          outa(ptr, moreline.b_cols, 1);
#endif
        else
          hunt_index = outs_line(&moreline);
      }

      outc('\n');

      /* ------------------------------------------------- */
      /* 依 shift 來決定動作                               */
      /* ------------------------------------------------- */

      /* itoc.030303.註解: shift 在此的意義
         >0: 還需要往下移幾行
         <0: 還需要往上移幾行
         =0: 結束這頁,等待使用者按鍵 */

      if (curr_refresh)
        shift--;
      else if (shift > 0)       /* 還要下移 shift 列 */
      {
        /* 只有在剛進 more,第一次印第一頁時才可能 lino <= b_lines */
        if (lino >= b_lines)
          scroll();

        lino++;

        if ((lino % 32 == 0) && ((i = lino >> 5) < MAXBLOCK))
          block[i] = foff - fimage;

        if (shift & END_MASK)       /* 按 End 鍵 */
        {
          if (!(shift & END_MORE_MASK))
            flast += more_line(&moreline, flast, 1);
        }
        else if (shift & HUNT_MASK)     /* 字串搜尋 */
        {
          if (shift & HUNT_NEXT)    /* 按 n 搜尋下一筆 */
          {
            flast += more_line(&moreline, flast, 1);
            /* 一找到就停於該列 */
            if (str_sub(&moreline.data[hunt_index], hunt))
              shift = 0;
          }
          else          /* 按 / 開始搜尋 */
          {
            /* 若在第二頁以後找到,一找到就停於該列;
               若在第一頁找到,必須等到讀完第一頁才能停止 */
            if (shift & HUNT_START)
            {
              if (lino > b_lines)
                flast += more_line(&moreline, flast, 1);

              /* 拿掉 HUNT_START 並加上 HUNT_FOUND */
              if (str_sub(&moreline.data[hunt_index], hunt))
                shift ^= HUNT_START | HUNT_FOUND;
            }

            if (shift & HUNT_FOUND && lino >= b_lines)
              shift = 0;
          }
          hunt_index = 0;
        }
        else
        {
          if (lino > b_lines)
            flast += more_line(&moreline, flast, 1);
          shift--;
        }
      }
      else if (shift < 0)       /* 還要上移 -shift 列 */
      {
        shift++;

        if (!(shift))
        {
          move(b_lines, 0);
          clrtoeol();

          /* 剩下 b_lines+shift 列是 rscroll,offsect 去正確位置;
             這裡的 i 是總共要 shift 的列數 */

          for (i += b_lines; i > 0; i--)
            more_line(&moreline, foff, 0);
        }
      }
    }

    if (foff >= fend)   /* 已經讀完全部的檔案 */
    {
      /* 倘若是按 End 移到最後一頁,那麼停留在 100% 而不結束;否則一律結束 */
      //if (!(shift & END_MASK))
      //  break;
      if (!(footer))
        break;

      shift = 0;
    }

    if (shift)          /* 還需要繼續讀資料 */
      continue;

    if (curr_refresh)
      curr_refresh = 0;

    /* ------------------------------------------------- */
    /* 到此印完所需的 shift 列,接下來印出 footer 並等待 */
    /* 使用者按鍵                                        */
    /* ------------------------------------------------- */

re_key:

    outs_footer(&moreline, lino, fsize);

#ifdef SLIDE_SHOW
    key = more_slideshow(&moreline);
#else
    key = vkey();
#endif

    if (key == ' ' || key == KEY_PGDN || key == KEY_RIGHT || key == Ctrl('F'))
    {
      if (foff >= fend)
      {
        cmd = 'j';
        break;
      }
      shift = PAGE_SCROLL;
    }

    else if (key == KEY_DOWN || key == '\n')
    {
      if (foff >= fend)
      {
        cmd = 'j';
        break;
      }
      shift = 1;
    }

    else if (key == KEY_PGUP || key == Ctrl('B') || key == KEY_DEL)
    {
      /* itoc.010324: 到了最開始再上捲表示離開,
         並回傳 'k' (keymap[] 定義上一篇) */

      if (lino <= b_lines)
      {
    cmd = 'k';
    break;
      }
      /* 最多只能上捲到一開始 */
      i = PAGE_SCROLL + 1 - lino;
      shift = BMAX(-PAGE_SCROLL, i);
    }

    else if (key == KEY_UP)
    {
      /* itoc.010324: 到了最開始再上捲表示離開,
         並回傳 'k' (keymap[] 定義上一篇) */
      if (lino <= b_lines)
      {
        cmd = 'k';
        break;
      }
      shift = -1;
    }

    else if (key == KEY_END || key == '$')
    {
      shift = END_MASK;
    }

    else if (key == KEY_HOME || key == '0')
    {
      if (lino <= b_lines)  /* 已經在最開始了 */
    shift = 0;
      else
    shift = -b_lines;
    }

    else if (key == '/' || key == 'n')
    {
      /* 如果按 n 卻未輸入過搜尋字串,那麼視同按 / */
      if (key == 'n' && hunt[0])
      {
        shift = HUNT_MASK | HUNT_NEXT;
      }
      else
      {
        if (vget(b_lines, 0, "搜尋(關鍵字):", hunt, sizeof(hunt), DOECHO))
        {
          str_lowest(hunt, hunt);
          shift = HUNT_MASK | HUNT_START;
        }
        else                /* 取消搜尋 */
        {
          hunt[0] = '\0';
          foff = flast;
          curr_refresh = 1;
          shift = b_lines;
          clear();
          continue;
        }
      }
    }

    else if (key == 'C')    /* Thor.980405: more 時可存入暫存檔 */
    {
      FILE *fp;
      if (fp = tbf_open())
      {
        f_suck(fp, fpath);
        fclose(fp);
      }
      shift = 0;      /* 重繪 footer */
    }
    else if (key == KEY_TAB)
    {
      moreline.more_offset += TAB_STOP;

      if (moreline.more_offset >= ANSILINELEN)
        moreline.more_offset = ANSILINELEN;;

      foff = flast;
      curr_refresh = 1;
      shift = b_lines;
      clear();
      continue;
    }

    else if (key == KEY_STAB || key == 'z')
    {
      moreline.more_offset -= TAB_STOP;

      if (moreline.more_offset < 0)
        moreline.more_offset = 0;

      foff = flast;
      curr_refresh = 1;
      shift = b_lines;
      clear();
      continue;
    }

    else if (key == '>' || key == '.')
    {
      if (moreline.more_offset)
        moreline.more_offset++;
      else
        moreline.more_offset += 2;

      if (moreline.more_offset >= MORE_BUFSIZE)
        moreline.more_offset = MORE_BUFSIZE;

      foff = flast;
      curr_refresh = 1;
      shift = b_lines;
      clear();
      continue;
    }

    else if (key == '<' || key == ',')
    {
      if (moreline.more_offset)
      {
        moreline.more_offset--;
        foff = flast;
        curr_refresh = 1;
        shift = b_lines;
        clear();
        continue;
      }
    }

    else if (key == 'h')
    {
      screenline slt[T_LINES];
      uschar *tmp_fimage;
      uschar *tmp_foff,  *tmp_flast;
      uschar *tmp_fend;
      off_t tmp_block[MAXBLOCK];
      int tmp_more_offset;

      tmp_fimage = fimage;
      tmp_foff = foff;
      tmp_flast = flast;
      tmp_fend = fend;
      memcpy(tmp_block, block, sizeof(off_t) * MAXBLOCK);
      tmp_more_offset = moreline.more_offset;

      vs_save(slt);
      xo_help("post");
      vs_restore(slt);

      moreline.more_offset = tmp_more_offset;
      memcpy(block, tmp_block, sizeof(off_t) * MAXBLOCK);
      fend = tmp_fend;
      flast = tmp_flast;
      foff = tmp_foff;
      fimage = tmp_fimage;

      shift = 0;
    }

    else        /* 其他鍵都是使用者中斷 */
    {
      /* itoc.041006: 使用者中斷的按鍵要 > 0 (而 KEY_LEFT 是 < 0) */
      cmd = (key > 0) ? key : 'q';
      break;
    }

    /* ------------------------------------------------- */
    /* 使用者已按鍵,若 break 則離開迴圈;否則依照 shift */
    /* 的種類 (亦即按鍵的種類) 而做不同的動作            */
    /* ------------------------------------------------- */

    if (shift > 0)          /* 準備下移 shift 列 */
    {
      if (shift < (HUNT_MASK | HUNT_START)) /* 一般下移 */
      {
        /* itoc.041114.註解:
           目標是秀出 lino-b_lines+1+shift ~ lino+shift 列的內容:
           就只要清 footer 即可,其他的就交給前面循序印 shift 列的程式 */

        move(b_lines, 0);
        clrtoeol();

        /* itoc.041116:
            End 的作法其實和一般下移可以是完全一樣的,但是如果遇到超長文章時,
            會造成前面循序印 shift 列的程式就得一直翻,直到找到最後一頁,這樣
            會做太多 outs_line() 白工,
            所以在此特別檢查超長文章時,就先去找最後一頁所在 */

        /* 還有一堆沒讀過,才特別處理 */
        if ((shift & END_MASK) && (fsize - (foff - fimage) >= MORE_BUFSIZE))
        {
          int totallino = lino;

          /* 先讀到最後一列看看全部有幾列 */
          while (more_line(&moreline, foff, 0))
          {
            totallino++;
            if ((totallino % 32 == 0) && ((i = totallino >> 5) < MAXBLOCK))
              block[i] = foff - fimage;
          }

          /* 先位移到上一個 block 的尾端 */
          i = (totallino - b_lines) >> 5;
          if (i >= MAXBLOCK)
            i = MAXBLOCK - 1;

          flast = foff = fimage + block[i];
          i = i << 5;

          /* 再從上一個 block 的尾端位移到 totallino-b_lines+1 列 */
          for (i = totallino - b_lines - i; i > 0; i--)
            more_line(&moreline, foff, 0);

          flast = foff;
          lino = totallino - b_lines;
          shift |= END_MORE_MASK;
        }
      }
      else
      {
        /* '/' 從頭開始搜尋 */
        lino = 0;
        flast = foff = fimage;
        clear();
      }
    }
    else if (shift < 0)         /* 準備上移 -shift 列 */
    {
      if (shift > -b_lines) /* 上捲數列 */
      {
        lino += shift;

        /* itoc.041114.註解: 目標是秀出 lino-b_lines+1 ~ lino 列的內容:
          1. 先從頭位移到 lino-b_lines+1 列
          2. 其中有 b_lines+shift 列是不變的內容,用 rscroll 達成
          3. 在前面的 outs_line() 的地方印出 -shift 列
          4. 最後再位移剛才 rscroll 的列數
        */

        /* 先位移到上一個 block 的尾端 */
        i = (lino - b_lines) >> 5;
        if (i >= MAXBLOCK)
          i = MAXBLOCK - 1;
        foff = fimage + block[i];
        i = i << 5;

        /* 再從上一個 block 的尾端位移到 lino-b_lines+1 列 */
        for (i = lino - b_lines - i; i > 0; i--)
          more_line(&moreline, foff, 0);

        for (i = shift; i < 0; i++)
        {
          rscroll();
          move(0, 0);
          clrtoeol();
        }

        i = shift;
        flast = foff;
      }
      else          /* Home */
      {
        /* itoc.041226.註解: 目標是秀出 1 ~ b_lines 列的內容:
           作法就是全部都歸零,從頭再印 b_lines 列即可 */

        clear();

        flast = foff = fimage;
        lino = 0;
        shift = b_lines;
      }
    }
    else                /* 重繪 footer 並 re-key */
    {
      move(b_lines, 0);
      clrtoeol();
      goto re_key;
    }
  } /* 迴圈的結束 */

  /* --------------------------------------------------- */
  /* 檔案已經秀完 (cmd = 0) 或 使用者中斷 (cmd != 0)     */
  /* --------------------------------------------------- */

  free(fimage);

  if (!cmd) /* 檔案正常秀完,要處理 footer */
  {
    if (footer)     /* 有 footer */
    {
      if (footer != (char *) -1)
        outf(footer);
      else
        outs(str_ransi);
    }
    else        /* 沒有 footer 要 vmsg() */
    {
      /* lkchu.981201: 先清一次以免重疊顯示 */
      move(b_lines, 0);
      clrtoeol();

      if (vmsg(NULL) == 'C') /* Thor.990204: 特別注意若回傳 'C' 表示暫存檔 */
      {
        FILE *fp;

        if (fp = tbf_open())
        {
          f_suck(fp, fpath);
          fclose(fp);
        }
      }
    }
  }
  else      /* 使用者中斷,直接離開 */
    outs(str_ransi);

  hunt[0] = '\0';

  /* Thor.990204: 讓key可回傳至more外 */
  return cmd;
}
------------------------------------------------------------------------------

--
=[:摃�◣�:Origin ]|[  Ocpu.tfcis.org  ]|[�搟說:]=
=[:摃�╱�:Author ]|[     cszone.twbbs.org     ]|[�:]=
精華選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻