回文章列表
作者mail6543210.bbs@sony.tfcis.org ( ) 看板: itoc
標題請問一下新版的MapleBBS
時間索尼小站 (2008/04/20 Sun 12:38:44)
我近日灌上MapleBBS-3.10-20080205-PACK.tgz後

用install.sh編譯、安裝

可是編譯到後來卻出現:

post.c: In function `post_delete':
post.c:2248: error: syntax error at end of input
post.c:2119: warning: unused variable `post_cb'
post.c:2191: warning: unused variable `xpost_cb'
post.c:2238: warning: unused variable `news_cb'
*** Error code 1

Stop in /home/bbs/src/maple.
*** Error code 1

Stop in /home/bbs/src/maple.
*** Error code 1

Stop in /home/bbs/src.
/home/bbs/bin/install.sh: bin/bbsd: not found
/home/bbs/bin/install.sh: bin/camera: not found
/home/bbs/bin/install.sh: bin/account: not found
Trying 0.0.0.0...
telnet: connect to address 0.0.0.0: Connection refused
telnet: Unable to connect to remote host

請問要如何解決?

以下附上post.c:
/*-------------------------------------------------------*/
/* post.c       (
NTHU CS MapleBBS Ver 2.39 )
*/
/*-------------------------------------------------------*/
/* target :
bulletin boards' routines            */
/* create : 95/03/29                     */
/* update :
96/04/05
*/
/*-------------------------------------------------------*/


#include
"bbs.h"


extern BCACHE *bshm;
extern XZ xz[];


extern int wordsnum;        /*
itoc.010408: 計算文章字數 */
extern int TagNum;
extern char xo_pool[];
extern
char brd_bits[];


#ifdef HAVE_ANONYMOUS
extern char anonymousid[];  /*
itoc.010717: 自定匿名 ID */
#endif


int
cmpchrono(hdr)
  HDR *hdr;
{

 return hdr->chrono == currchrono;
}


/*
----------------------------------------------------- */
/* 改良 innbbsd 轉出
信件、連線砍信之處理程序         */
/*
-----------------------------------------------------
*/


void
btime_update(bno)
  int bno;
{
  if (bno >= 0)
    (bshm->bcache +
bno)->btime = -1;   /* 讓 class_item() 更新用 */
}


#ifndef
HAVE_NETTOOL
static          /* 給 enews.c 用 */
#endif
void
outgo_post(hdr,
board)
  HDR *hdr;
  char *board;
{
  bntp_t bntp;

  memset(&bntp, 0,
sizeof(bntp_t));

  if (board)        /* 新信 */
  {
    bntp.chrono =
hdr->chrono;
  }
  else          /* cancel */
  {
    bntp.chrono = -1;
    board =
currboard;
  }
  strcpy(bntp.board, board);
  strcpy(bntp.xname,
hdr->xname);
  strcpy(bntp.owner, hdr->owner);
  strcpy(bntp.nick,
hdr->nick);
  strcpy(bntp.title, hdr->title);
  rec_add("innd/out.bntp",
&bntp, sizeof(bntp_t));
}


void
cancel_post(hdr)
  HDR *hdr;
{
  if
((hdr->xmode & POST_OUTGO) &&       /* 外轉信件 */
    (hdr->chrono > ap_start - 7
* 86400))   /* 7 天之內有效 */
  {
    outgo_post(hdr, NULL);
  }
}


static
inline int      /* 回傳文章 size 去扣錢 */
move_post(hdr, folder, by_bm)   /* 將
 hdr 從 folder 搬到別的板 */
  HDR *hdr;
  char *folder;
  int by_bm;
{
  HDR
post;
  int xmode;
  char fpath[64], fnew[64], *board;
  struct stat st;


 xmode = hdr->xmode;
  hdr_fpath(fpath, folder, hdr);

  if (!(xmode &
POST_BOTTOM))   /* 置底文被砍不用 move_post */
  {
#ifdef HAVE_REFUSEMARK

 board = by_bm && !(xmode & POST_RESTRICT) ? BN_DELETED : BN_JUNK;  /* 加密文章
丟去 junk */
#else
    board = by_bm ? BN_DELETED : BN_JUNK;
#endif


 brd_fpath(fnew, board, fn_dir);
    hdr_stamp(fnew, HDR_LINK | 'A', &post,
fpath);

    /* 直接複製 trailing data:owner(含)以下所有欄位 */


 memcpy(post.owner, hdr->owner, sizeof(HDR) -
      (sizeof(post.chrono) +
sizeof(post.xmode) + sizeof(post.xid) + sizeof(post.xname)));

    if
(by_bm)
      sprintf(post.title, "%-13s%.59s", cuser.userid, hdr->title);


   rec_bot(fnew, &post, sizeof(HDR));
    btime_update(brd_bno(board));
  }


 by_bm = stat(fpath, &st) ? 0 : st.st_size;

  unlink(fpath);

 btime_update(currbno);
  cancel_post(hdr);

  return by_bm;
}


#ifdef
HAVE_DETECT_CROSSPOST
/*
----------------------------------------------------- */
/* 改良 cross post 停
權                   */
/* -----------------------------------------------------
*/


#define MAX_CHECKSUM_POST   20  /* 記錄最近 20 篇文章的 checksum */
#define
MAX_CHECKSUM_LINE   6   /* 只取文章前 6 行來算 checksum */


typedef struct
{

 int sum;           /* 文章的 checksum */
  int total;            /* 此文章已發表幾篇 */
}
 CHECKSUM;


static CHECKSUM checksum[MAX_CHECKSUM_POST];
static int checknum
= 0;


static inline int
checksum_add(str)       /* 回傳本列文字的 checksum */

 char *str;
{
  int i, len, sum;

  len = strlen(str);

  sum = len;    /* 當字數
太少時,前四分之一很可能完全相同,所以將字數也加入 sum 值 */
  for (i = len
>> 2; i > 0; i--)   /* 只算前四分之一字元的 sum 值 */
    sum += *str++;


 return sum;
}


static inline int       /* 1:是cross-post 0:不是cross-post
*/
checksum_put(sum)
  int sum;
{
  int i;

  if (sum)
  {
    for (i = 0; i
< MAX_CHECKSUM_POST; i++)
    {
      if (checksum[i].sum == sum)

 {
    checksum[i].total++;

    if (checksum[i].total > MAX_CROSS_POST)
      return
1;
    return 0;   /* total <= MAX_CROSS_POST */
      }
    }

    if (++checknum
>= MAX_CHECKSUM_POST)
      checknum = 0;
    checksum[checknum].sum = sum;

   checksum[checknum].total = 1;
  }
  return 0;
}


static int          /* 1:是
cross-post 0:不是cross-post */
checksum_find(fpath)
  char *fpath;
{
  int i,
sum;
  char buf[ANSILINELEN];
  FILE *fp;

  sum = 0;
  if (fp = fopen(fpath,
"r"))
  {
    for (i = -(LINE_HEADER + 1);;)  /* 前幾列是檔頭 */
    {

 if (!fgets(buf, ANSILINELEN, fp))
    break;

      if (i < 0)    /* 跳過檔頭 */

     {
    i++;
    continue;
      }

      if (*buf == QUOTE_CHAR1 || *buf ==
'\n' || !strncmp(buf, "※", 2))  /* 跳過引言 */
    continue;

      sum +=
checksum_add(buf);
      if (++i >= MAX_CHECKSUM_LINE)
    break;
    }


 fclose(fp);
  }

  return checksum_put(sum);
}


static
int
check_crosspost(fpath, bno)
  char *fpath;
  int bno;          /* 要轉去的看板
 */
{
  char *blist, folder[64];
  ACCT acct;
  HDR hdr;

  if
(HAS_PERM(PERM_ALLADMIN))
    return 0;

  /* 板主在自己管理的看板不列入跨貼檢
查 */
  blist = (bshm->bcache + bno)->BM;
  if (HAS_PERM(PERM_BM) && blist[0]
> ' ' && is_bm(blist, cuser.userid))
    return 0;

  if
(checksum_find(fpath))
  {
    /* 如果是 cross-post,那麼轉去 BN_SECURITY 並直
接停權 */
    brd_fpath(folder, BN_SECURITY, fn_dir);
    hdr_stamp(folder,
HDR_COPY | 'A', &hdr, fpath);
    strcpy(hdr.owner, cuser.userid);

 strcpy(hdr.nick, cuser.username);
    sprintf(hdr.title, "%s %s Cross-Post",
cuser.userid, Now());
    rec_bot(folder, &hdr, sizeof(HDR));

 btime_update(brd_bno(BN_SECURITY));

    bbstate &= ~STAT_POST;

 cuser.userlevel &= ~PERM_POST;
    cuser.userlevel |= PERM_DENYPOST;
    if
(acct_load(&acct, cuser.userid) >= 0)
    {
      acct.tvalid = time(NULL) +
CROSSPOST_DENY_DAY * 86400;
      acct_setperm(&acct, PERM_DENYPOST,
PERM_POST);
    }
    board_main();
    mail_self(FN_ETC_CROSSPOST,
str_sysop, "Cross-Post 停權", 0);
    vmsg("您因為過度 Cross-Post 已被停權
");
    return 1;
  }
  return 0;
}
#endif  /* HAVE_DETECT_CROSSPOST */


/*
----------------------------------------------------- */
/* 發表、回應、編輯、
轉錄文章                 */
/* -----------------------------------------------------
*/


int
is_author(hdr)
  HDR *hdr;
{
  /* 這裡沒有檢查是不是 guest,注意使用
此函式時要特別考慮 guest 情況 */

  /* itoc.070426: 當帳號被清除後,新註冊相同
 ID 的帳號並不擁有過去該 ID 發表的文章之所有權 */
  return
!strcmp(hdr->owner, cuser.userid) && (hdr->chrono >
cuser.firstlogin);
}


#ifdef HAVE_REFUSEMARK
int
chkrestrict(hdr)
  HDR
*hdr;
{
  return !(hdr->xmode & POST_RESTRICT) || is_author(hdr) || (bbstate
& STAT_BM);
}
#endif


#ifdef HAVE_ANONYMOUS
static
void
log_anonymous(fname)
  char *fname;
{
  char buf[512];

  sprintf(buf,
"%s %-13s(%s)\n%-13s %s %s\n",
    Now(), cuser.userid, fromhost, currboard,
fname, ve_title);
  f_cat(FN_RUN_ANONYMOUS, buf);
}
#endif


#ifdef
HAVE_UNANONYMOUS_BOARD
static void
do_unanonymous(fpath)
  char *fpath;
{

 HDR hdr;
  char folder[64];

  brd_fpath(folder, BN_UNANONYMOUS, fn_dir);

 hdr_stamp(folder, HDR_LINK | 'A', &hdr, fpath);

  strcpy(hdr.owner,
cuser.userid);
  strcpy(hdr.title, ve_title);

  rec_bot(folder, &hdr,
sizeof(HDR));
  btime_update(brd_bno(BN_UNANONYMOUS));
}
#endif


static
int
do_post(xo, title)
  XO *xo;
  char *title;
{
  /* Thor.981105: 進入前需設
好 curredit 及 quote_file */
  HDR hdr, buf;
  char fpath[64], *folder,
*nick, *rcpt;
  int mode;
  time_t spendtime, prev, chrono;

  if (!(bbstate
& STAT_POST))
  {
#ifdef NEWUSER_LIMIT
    if (cuser.lastlogin -
cuser.firstlogin < 3 * 86400)
      vmsg("新手上路,三日後始可張貼文章");

 else
#endif
      vmsg("對不起,您沒有在此發表文章的權限");
    return
XO_FOOT;
  }

  film_out(FILM_POST, 0);

  prints("發表文章於【 %s 】看板",
currboard);

#ifdef POST_PREFIX
  /* 借用 mode、rcpt、fpath */

  if (title)

 {
    rcpt = NULL;
  }
  else      /* itoc.020113: 新文章選擇標題分類 */

 {
#define NUM_PREFIX 6
    char *prefix[NUM_PREFIX] = {"[公告] ", "[新聞] ",
"[閒聊] ", "[文件] ", "[問題] ", "[測試] "};

    move(21, 0);
    outs("類別
:");
    for (mode = 0; mode < NUM_PREFIX; mode++)
      prints("%d.%s",
mode + 1, prefix[mode]);

    mode = vget(20, 0, "請選擇文章類別(按 Enter 跳
過):", fpath, 3, DOECHO) - '1';
    if (mode >= 0 && mode < NUM_PREFIX)     /*
輸入數字選項 */
      rcpt = prefix[mode];
    else                    /* 空白跳過 */

 rcpt = NULL;
  }

  if (!ve_subject(21, title, rcpt))
#else
  if
(!ve_subject(21, title, NULL))
#endif
      return XO_HEAD;

  /* 未具備
 Internet 權限者,只能在站內發表文章 */
  /* Thor.990111: 沒轉信出去的看板, 也
只能在站內發表文章 */

  if (!HAS_PERM(PERM_INTERNET) || (currbattr &
BRD_NOTRAN))
    curredit &= ~EDIT_OUTGO;

  utmp_mode(M_POST);
  fpath[0] =
'\0';
  time(&spendtime);
  if (vedit(fpath, 1) < 0)
  {
    unlink(fpath);

   vmsg(msg_cancel);
    return XO_HEAD;
  }
  spendtime = time(0) -
spendtime;  /* itoc.010712: 總共花的時間(秒數) */

  /* build filename */


 folder = xo->dir;
  hdr_stamp(folder, HDR_LINK | 'A', &hdr, fpath);

  /*
set owner to anonymous for anonymous board */

#ifdef HAVE_ANONYMOUS
  /*
Thor.980727: lkchu新增之[簡單的選擇性匿名功能] */
  if (curredit &
EDIT_ANONYMOUS)
  {
    rcpt = anonymousid; /* itoc.010717: 自定匿名 ID */

 nick = STR_ANONYMOUS;

    /* Thor.980727: lkchu patch: log anonymous post
*/
    /* Thor.980909: gc patch: log anonymous post filename */

 log_anonymous(hdr.xname);

#ifdef HAVE_UNANONYMOUS_BOARD

 do_unanonymous(fpath);
#endif
  }
  else
#endif
  {
    rcpt =
cuser.userid;
    nick = cuser.username;
  }
  title = ve_title;
  mode =
(curredit & EDIT_OUTGO) ? POST_OUTGO : 0;
#ifdef HAVE_REFUSEMARK
  if
(curredit & EDIT_RESTRICT)
    mode |= POST_RESTRICT;
#endif

  hdr.xmode =
mode;
  strcpy(hdr.owner, rcpt);
  strcpy(hdr.nick, nick);

 strcpy(hdr.title, title);

  rec_bot(folder, &hdr, sizeof(HDR));

 btime_update(currbno);

  if (mode & POST_OUTGO)
    outgo_post(&hdr,
currboard);

#if 1   /* itoc.010205: post 完文章就記錄,使不出現未閱讀的+號
 */
  chrono = hdr.chrono;
  prev = ((mode = rec_num(folder, sizeof(HDR)) -
2) >= 0 && !rec_get(folder, &buf, sizeof(HDR), mode)) ? buf.chrono : chrono;

 brh_add(prev, chrono, chrono);
#endif

  clear();
  outs("順利貼出文章,
");

  if (currbattr & BRD_NOCOUNT || wordsnum < 30)
  {             /* itoc.010408: 以
此減少灌水現象 */
    outs("文章不列入紀錄,敬請包涵。");
  }
  else
  {

 /* itoc.010408: 依文章長度/所費時間來決定要給多少錢;幣制才會有意義 */

 mode = BMIN(wordsnum, spendtime) / 10; /* 每十字/秒 一元 */
    prints("這是
您的第 %d 篇文章,得 %d 銀。", ++cuser.numposts, mode);
    addmoney(mode);

 }

  /* 回應到原作者信箱 */

  if (curredit & EDIT_BOTH)
  {
    rcpt =
quote_user;

    if (strchr(rcpt, '@'))  /* 站外 */
      mode = bsmtp(fpath,
title, rcpt, 0);
    else            /* 站內使用者 */
      mode = mail_him(fpath,
rcpt, title, 0);

    outs(mode >= 0 ? "\n\n成功\回應至作者信箱" : "\n\n作者無
法收信");
  }

  unlink(fpath);

  vmsg(NULL);

  return
XO_INIT;
}


int
do_reply(xo, hdr)
  XO *xo;
  HDR *hdr;
{
  curredit = 0;


 switch (vans("▲ 回應至 (F)看板 (M)作者信箱 (B)二者皆是 (Q)取消?[F] "))

 {
  case 'm':
    hdr_fpath(quote_file, xo->dir, hdr);
    return
do_mreply(hdr, 0);

  case 'q':
    return XO_FOOT;

  case 'b':
    /* 若無寄
信的權限,則只回看板 */
    if (HAS_PERM(strchr(hdr->owner, '@') ?
PERM_INTERNET : PERM_LOCAL))
      curredit = EDIT_BOTH;
    break;
  }

  /*
Thor.981105: 不論是轉進的, 或是要轉出的, 都是別站可看到的, 所以回信也都應該轉
出 */
  if (hdr->xmode & (POST_INCOME | POST_OUTGO))
    curredit |=
EDIT_OUTGO;

  hdr_fpath(quote_file, xo->dir, hdr);
  strcpy(quote_user,
hdr->owner);
  strcpy(quote_nick, hdr->nick);
  return do_post(xo,
hdr->title);
}


static int
post_reply(xo)
  XO *xo;
{
  if (bbstate &
STAT_POST)
  {
    HDR *hdr;

    hdr = (HDR *) xo_pool + (xo->pos -
xo->top);

#ifdef HAVE_REFUSEMARK
    if (!chkrestrict(hdr))
      return
XO_NONE;
#endif

    return do_reply(xo, hdr);
  }
  return
XO_NONE;
}


static int
post_add(xo)
  XO *xo;
{
  curredit = EDIT_OUTGO;

 *quote_file = '\0';
  return do_post(xo, NULL);
}


/*
----------------------------------------------------- */
/* 印出 hdr 標題
                     */
/* -----------------------------------------------------
*/


int
tag_char(chrono)
  int chrono;
{
  return TagNum && !Tagger(chrono,
0, TAG_NIN) ? '*' : ' ';
}


#ifdef HAVE_DECLARE
static inline
int
cal_day(date)       /* itoc.010217: 計算星期幾 */
  char *date;
{
#if 0
   蔡勒
公式是一個推算哪一天是星期幾的公式.
   這公式是:
         c                y
      26(m+1)
    W= [---] - 2c + y + [---] + [---------] + d - 1
         4
               4         10
    W → 為所求日期的星期數. (星期日: 0  星期一:
1  ...  星期六: 6)
    c → 為已知公元年份的前兩位數字.
    y → 為已知公元年
份的後兩位數字.
    m → 為月數
    d → 為日數
   [] → 表示只取該數的整數部
分 (地板函數)
    ps.所求的月份如果是1月或2月,則應視為上一年的13月或14月.

    所以公式中m的取值範圍不是1到12,而是3到14
#endif

  /* 適用 2000/03/01 至
 2099/12/31 */

  int y, m, d;

  y = 10 * ((int) (date[0] - '0')) + ((int)
(date[1] - '0'));
  d = 10 * ((int) (date[6] - '0')) + ((int) (date[7] -
'0'));
  if (date[3] == '0' && (date[4] == '1' || date[4] == '2'))
  {
    y
-= 1;
    m = 12 + (int) (date[4] - '0');
  }
  else
  {
    m = 10 * ((int)
(date[3] - '0')) + ((int) (date[4] - '0'));
  }
  return (-1 + y + y / 4 + 26
* (m + 1) / 10 + d) % 7;
}
#endif


void
hdr_outs(hdr, cc)       /* print HDR's
subject */
  HDR *hdr;
  int cc;           /* 印出最多 cc - 1 字的標題 */
{
  /* 回覆/
轉錄/原創/閱讀中的同主題回覆/閱讀中的同主題轉錄/閱讀中的同主題原創 */

 static char *type[6] = {"Re", "Fw", "◇", "\033[1;33m=>", "\033[1;33m=>",
"\033[1;32m◆"};
  uschar *title, *mark;
  int ch, len;
  int in_chi;       /* 1:
在中文字中 */
#ifdef HAVE_DECLARE
  int square;       /* 1: 要處理方括
 */
#endif
#ifdef CHECK_ONLINE
  UTMP *online;
#endif

  /*
--------------------------------------------------- */
  /* 印出日期
*/
  /* --------------------------------------------------- */

#ifdef
HAVE_DECLARE
  /* itoc.010217: 改用星期幾來上色 */

 prints("\033[1;3%dm%s\033[m ", cal_day(hdr->date) + 1, hdr->date +
3);
#else
  outs(hdr->date + 3);
  outc(' ');
#endif

  /*
--------------------------------------------------- */
  /* 印出作者
*/
  /* --------------------------------------------------- */

#ifdef
CHECK_ONLINE
  if (online = utmp_seek(hdr))
    outs(COLOR7);
#endif

  mark
= hdr->owner;
  len = IDLEN + 1;
  in_chi = 0;

  while (ch = *mark)
  {

 if (--len <= 0)
    {
      /* 把超過 len 長度的部分直接切掉 */
      /*
itoc.060604.註解: 如果剛好切在中文字的一半就會出現亂碼,不過這情況很少發生,所
以就不管了 */
      ch = '.';
    }
    else
    {
      /* 站外的作者把 '@'
換成 '.' */
      if (in_chi || IS_ZHC_HI(ch))  /* 中文字尾碼是 '@' 的不算
 */
    in_chi ^= 1;
      else if (ch == '@')
    ch = '.';
    }


 outc(ch);

    if (ch == '.')
      break;

    mark++;
  }

  while
(len--)
    outc(' ');

#ifdef CHECK_ONLINE
  if (online)

 outs(str_ransi);
#endif

  /*
--------------------------------------------------- */
  /* 印出標題的種類
                     */
  /* --------------------------------------------------- */

  /*
len: 標題是 type[] 裡面的那一種 */
  title = str_ttl(mark = hdr->title);

 len = (title == mark) ? 2 : (*mark == 'R') ? 0 : 1;
  if (!strcmp(currtitle,
title))
    len += 3;
  outs(type[len]);
  outc(' ');

  /*
--------------------------------------------------- */
  /* 印出標題
*/
  /* --------------------------------------------------- */

  mark =
title + cc;

#ifdef HAVE_DECLARE /* Thor.980508: Declaration, 嘗試使某些title
更明顯 */
  square = in_chi = 0;
  if (len < 3)
  {
    if (*title == '[')

 {
      outs("\033[1m");
      square = 1;
    }
  }
#endif

  /* 把超過 cc
長度的部分直接切掉 */
  /* itoc.060604.註解: 如果剛好切在中文字的一半就會出現
亂碼,不過這情況很少發生,所以就不管了 */
  while ((ch = *title++) && (title
< mark))
  {
#ifdef HAVE_DECLARE
    if (square)
    {
      if (in_chi ||
IS_ZHC_HI(ch))  /* 中文字的第二碼若是 ']' 不算是方括 */
      {
    in_chi ^= 1;

     }
      else if (ch == ']')
      {
    outs("]\033[m");
    square = 0;         /*
只處理一組方括,方括已經處理完了 */
    continue;
      }
    }
#endif


 outc(ch);
  }

#ifdef HAVE_DECLARE
  if (square || len >= 3)   /* Thor.980508:
變色還原用 */
#else
  if (len >= 3)
#endif
    outs("\033[m");


 outc('\n');
}


/* -----------------------------------------------------
*/
/* 看板功能表                        */
/*
----------------------------------------------------- */


static int
post_body();
static int post_head();


static int
post_init(xo)
  XO *xo;
{

 xo_load(xo, sizeof(HDR));
  return post_head(xo);
}


static
int
post_load(xo)
  XO *xo;
{
  xo_load(xo, sizeof(HDR));
  return
post_body(xo);
}


static int
post_attr(hdr)
  HDR *hdr;
{
  int mode,
attr;

  mode = hdr->xmode;

  /* 已閱讀為小寫,未閱讀為大寫 */
  /* 由於置底
文沒有閱讀記錄,所以視為已讀 */
  /* 加密文章視為已讀 */
#ifdef
HAVE_REFUSEMARK
  attr = ((mode & POST_BOTTOM) || !brh_unread(hdr->chrono) ||
!chkrestrict(hdr)) ? 0x20 : 0;
#else
  attr = ((mode & POST_BOTTOM) ||
!brh_unread(hdr->chrono)) ? 0x20 : 0;
#endif

#ifdef HAVE_REFUSEMARK
  if
(mode & POST_RESTRICT)
    attr |= 'X';
  else
#endif
#ifdef
HAVE_LABELMARK
  if (mode & POST_DELETE)
    attr |= 'T';
  else
#endif
  if
(mode & POST_MARKED)
    attr |= 'M';
  else if (!attr)
    attr = '+';


 return attr;
}


static void
post_item(num, hdr)
  int num;
  HDR
*hdr;
{
#ifdef HAVE_SCORE
  static char scorelist[36] =
  {
    '0', '1',
'2', '3', '4', '5', '6', '7', '8', '9',
    'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J',
    'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',

 'U', 'V', 'W', 'X', 'Y', 'Z'
  };

  prints("%6d%c%c", (hdr->xmode &
POST_BOTTOM) ? -1 : num, tag_char(hdr->chrono), post_attr(hdr));
  if
(hdr->xmode & POST_SCORE)
  {
    num = hdr->score;

 prints("\033[1;3%cm%c\033[m ", num >= 0 ? '1' : '2', scorelist[abs(num)]);

 }
  else
  {
    outs("  ");
  }
  hdr_outs(hdr, d_cols + 46);   /* 少一格來放
分數 */
#else
  prints("%6d%c%c ", (hdr->xmode & POST_BOTTOM) ? -1 : num,
tag_char(hdr->chrono), post_attr(hdr));
  hdr_outs(hdr, d_cols +
47);
#endif
}


static int
post_body(xo)
  XO *xo;
{
  HDR *hdr;
  int num,
max, tail;

  max = xo->max;
  if (max <= 0)
  {
    if (bbstate &
STAT_POST)
    {
      if (vans("要新增資料嗎(Y/N)?[N] ") == 'y')
    return
post_add(xo);
    }
    else
    {
      vmsg("本看板尚無文章");
    }

 return XO_QUIT;
  }

  hdr = (HDR *) xo_pool;
  num = xo->top;
  tail = num
+ XO_TALL;
  if (max > tail)
    max = tail;

  move(3, 0);
  do
  {

 post_item(++num, hdr++);
  } while (num < max);
  clrtobot();

  /* return
XO_NONE; */
  return XO_FOOT;   /* itoc.010403: 把 b_lines 填上 feeter
*/
}


static int
post_head(xo)
  XO *xo;
{
  vs_head(currBM, xo->xyz);

 prints(NECKER_POST, d_cols, "", currbattr & BRD_NOSCORE ? "╳" : "○",
bshm->mantime[currbno]);
  return post_body(xo);
}


/*
----------------------------------------------------- */
/* 資料之瀏覽:
browse / history                 */
/*
----------------------------------------------------- */


static
int
post_visit(xo)
  XO *xo;
{
  int ans, row, max;
  HDR *hdr;

  ans =
vans("設定所有文章 (U)未讀 (V)已讀 (W)前已讀後未讀 (Q)取消?[Q] ");
  if (ans
== 'v' || ans == 'u' || ans == 'w')
  {
    row = xo->top;
    max = xo->max
- row + 3;
    if (max > b_lines)
      max = b_lines;

    hdr = (HDR *)
xo_pool + (xo->pos - row);
    /* brh_visit(ans == 'w' ? hdr->chrono : ans ==
'u'); */
    /* weiyu.041010: 在置底文上選 w 視為全部已讀 */

 brh_visit((ans == 'u') ? 1 : (ans == 'w' && !(hdr->xmode & POST_BOTTOM)) ?
hdr->chrono : 0);

    hdr = (HDR *) xo_pool;
    row = 3;
    do
    {

 move(row, 7);
      outc(post_attr(hdr++));
    } while (++row < max);
  }

 return XO_FOOT;
}


static void
post_history(xo, hdr)       /* 將 hdr 這篇加入
 brh */
  XO *xo;
  HDR *hdr;
{
  time_t prev, chrono, next;
  int pos, top;

 char *dir;
  HDR buf;

  if (hdr->xmode & POST_BOTTOM) /* 置底文不加入閱讀記
錄 */
    return;

  chrono = hdr->chrono;
  if (!brh_unread(chrono))  /* 如果
已在 brh 中,就無需動作 */
    return;

  dir = xo->dir;
  pos = xo->pos;

 top = xo->top;

  pos--;
  if (pos >= top)
  {
    prev = hdr[-1].chrono;

 }
  else
  {
    /* amaki.040302.註解: 在畫面以上,只好讀硬碟 */
    if
(!rec_get(dir, &buf, sizeof(HDR), pos))
      prev = buf.chrono;
    else

   prev = chrono;
  }

  pos += 2;
  if (pos < top + XO_TALL && pos <
xo->max)
  {
    next = hdr[1].chrono;
  }
  else
  {
    /* amaki.040302.註解
: 在畫面以下,只好讀硬碟 */
    if (!rec_get(dir, &buf, sizeof(HDR), pos))

   next = buf.chrono;
    else
      next = chrono;
  }

  brh_add(prev,
chrono, next);
}


static int
post_browse(xo)
  XO *xo;
{
  HDR *hdr;
  int
xmode, pos, key;
  char *dir, fpath[64];

  dir = xo->dir;

  for (;;)
  {

 pos = xo->pos;
    hdr = (HDR *) xo_pool + (pos - xo->top);
    xmode =
hdr->xmode;

#ifdef HAVE_REFUSEMARK
    if (!chkrestrict(hdr))

 break;
#endif

    hdr_fpath(fpath, dir, hdr);

    /* Thor.990204: 為考慮
more 傳回值 */
    if ((key = more(fpath, FOOTER_POST)) < 0)

 break;

    post_history(xo, hdr);
    strcpy(currtitle,
str_ttl(hdr->title));

re_key:
    switch (xo_getch(xo, key))
    {
    case
XO_BODY:
      continue;

    case 'y':
    case 'r':
      if (bbstate &
STAT_POST)
      {
    if (do_reply(xo, hdr) == XO_INIT)   /* 有成功地 post 出去了
 */
      return post_init(xo);
      }
      break;

    case 'm':
      if
((bbstate & STAT_BOARD) && !(xmode & POST_MARKED))
      {
    /* hdr->xmode =
xmode ^ POST_MARKED; */
    /* 在 post_browse 時看不到 m 記號,所以限制只能 mark
*/
    hdr->xmode = xmode | POST_MARKED;
    currchrono =
hdr->chrono;
    rec_put(dir, hdr, sizeof(HDR), pos, cmpchrono);
      }

 break;

#ifdef HAVE_SCORE
    case '%':
      post_score(xo);
      return
post_init(xo);
#endif

    case '/':
      if (vget(b_lines, 0, "搜尋:",
hunt, sizeof(hunt), DOECHO))
      {
    more(fpath, FOOTER_POST);
    goto
re_key;
      }
      continue;

    case 'E':
      return post_edit(xo);


   case 'C':    /* itoc.000515: post_browse 時可存入暫存檔 */
      {
    FILE
*fp;
    if (fp = tbf_open())
    {
      f_suck(fp, fpath);
      fclose(fp);
    }

 }
      break;

    case 'h':
      xo_help("post");
      break;
    }

 break;
  }

  return post_head(xo);
}


/*
----------------------------------------------------- */
/* 精華區
*/
/* ----------------------------------------------------- */


static
int
post_gem(xo)
  XO *xo;
{
  int level;
  char fpath[64];

  strcpy(fpath,
"gem/");
  strcpy(fpath + 4, xo->dir);

  level = 0;
  if (bbstate &
STAT_BOARD)
    level ^= GEM_W_BIT;
  if (HAS_PERM(PERM_SYSOP))
    level ^=
GEM_X_BIT;
  if (bbstate & STAT_BM)
    level ^= GEM_M_BIT;

  XoGem(fpath, "
精華區", level);
  return post_init(xo);
}


/*
----------------------------------------------------- */
/* 進板畫面
*/
/* ----------------------------------------------------- */


static
int
post_memo(xo)
  XO *xo;
{
  char fpath[64];

  brd_fpath(fpath,
currboard, fn_note);
  /* Thor.990204: 為考慮more 傳回值 */
  if
(more(fpath, NULL) < 0)
  {
    vmsg("本看板尚無「進板畫面」");
    return
XO_FOOT;
  }

  return post_head(xo);
}


/*
----------------------------------------------------- */
/* 功能:tag /
switch / cross / forward             */
/*
----------------------------------------------------- */


static
int
post_tag(xo)
  XO *xo;
{
  HDR *hdr;
  int tag, pos, cur;

  pos =
xo->pos;
  cur = pos - xo->top;
  hdr = (HDR *) xo_pool + cur;

  if (xo->key
== XZ_XPOST)
    pos = hdr->xid;

  if (tag = Tagger(hdr->chrono, pos,
TAG_TOGGLE))
  {
    move(3 + cur, 6);
    outc(tag > 0 ? '*' : ' ');
  }


 /* return XO_NONE; */
  return xo->pos + 1 + XO_MOVE; /* lkchu.981201: 跳至下
一項 */
}


static int
post_switch(xo)
  XO *xo;
{
  int bno;
  BRD *brd;

 char bname[BNLEN + 1];

  if (brd = ask_board(bname, BRD_R_BIT, NULL))
  {

   if ((bno = brd - bshm->bcache) >= 0 && currbno != bno)
    {

 XoPost(bno);
      return XZ_POST;
    }
  }
  else
  {
    vmsg(err_bid);

 }
  return post_head(xo);
}


int
post_cross(xo)
  XO *xo;
{
  /* 來源看板
 */
  char *dir, *ptr;
  HDR *hdr, xhdr;

  /* 欲轉去的看板 */
  int xbno;

 usint xbattr;
  char xboard[BNLEN + 1], xfolder[64];
  HDR xpost;

  int
tag, rc, locus, finish;
  int method;       /* 0:原文轉載 1:從公開看板/精華區/信箱
轉錄文章 2:從秘密看板轉錄文章 */
  usint tmpbattr;
  char tmpboard[BNLEN +
1];
  char fpath[64], buf[ANSILINELEN];
  FILE *fpr, *fpw;

  if
(!cuser.userlevel)  /* itoc.000213: 避免 guest 轉錄去 sysop 板 */
    return
XO_NONE;

  tag = AskTag("轉錄");
  if (tag < 0)
    return XO_FOOT;

  dir =
xo->dir;

  if (!ask_board(xboard, BRD_W_BIT, "\n\n\033[1;33m請挑選適當的看板
,切勿轉錄超過三板。\033[m\n\n") ||
    (*dir == 'b' && !strcmp(xboard,
currboard)))    /* 信箱、精華區中可以轉錄至currboard */
    return XO_HEAD;


 hdr = tag ? &xhdr : (HDR *) xo_pool + (xo->pos - xo->top); /* lkchu.981201:
整批轉錄 */

  /* 原作者轉錄自己文章時,可以選擇「原文轉載」 */
  method =
(HAS_PERM(PERM_ALLBOARD) || (!tag && is_author(hdr))) &&
    (vget(2, 0, "(1)
原文轉載 (2)轉錄文章?[1] ", buf, 3, DOECHO) != '2') ? 0 : 1;

  if (!tag) /*
lkchu.981201: 整批轉錄就不要一一詢問 */
  {
    if (method)

 sprintf(ve_title, "Fw: %.68s", str_ttl(hdr->title));   /* 已有 Re:/Fw: 字樣就只
要一個 Fw: */
    else
      strcpy(ve_title, hdr->title);

    if (!vget(2,
0, "標題:", ve_title, TTLEN + 1, GCARRY))
      return XO_HEAD;
  }

#ifdef
HAVE_REFUSEMARK
  rc = vget(2, 0, "(S)存檔 (L)站內 (X)密封 (Q)取消?[Q]
", buf, 3, LCECHO);
  if (rc != 'l' && rc != 's' && rc != 'x')
#else
  rc =
vget(2, 0, "(S)存檔 (L)站內 (Q)取消?[Q] ", buf, 3, LCECHO);
  if (rc != 'l'
&& rc != 's')
#endif
    return XO_HEAD;

  if (method && *dir == 'b')    /* 從看
板轉出,先檢查此看板是否為秘密板 */
  {
    /* 借用 tmpbattr */
    tmpbattr
= (bshm->bcache + currbno)->readlevel;
    if (tmpbattr == PERM_SYSOP ||
tmpbattr == PERM_BOARD)
      method = 2;
  }

  xbno = brd_bno(xboard);

 xbattr = (bshm->bcache + xbno)->battr;

  /* Thor.990111: 在可以轉出前,要檢
查有沒有轉出的權力? */
  if ((rc == 's') && (!HAS_PERM(PERM_INTERNET) ||
(xbattr & BRD_NOTRAN)))
    rc = 'l';

  /* 備份 currboard */
  if (method)

 {
    /* itoc.030325: 一般轉錄呼叫 ve_header,會使用到 currboard、currbattr,
先備份起來 */
    strcpy(tmpboard, currboard);
    strcpy(currboard,
xboard);
    tmpbattr = currbattr;
    currbattr = xbattr;
  }

  locus = 0;

 do /* lkchu.981201: 整批轉錄 */
  {
    if (tag)
    {
      EnumTag(hdr,
dir, locus, sizeof(HDR));

      if (method)
    sprintf(ve_title, "Fw: %.68s",
str_ttl(hdr->title));   /* 已有 Re:/Fw: 字樣就只要一個 Fw: */

 else
    strcpy(ve_title, hdr->title);
    }

    if (hdr->xmode &
GEM_FOLDER) /* 非 plain text 不能轉 */
      continue;

#ifdef
HAVE_REFUSEMARK
    if (hdr->xmode & POST_RESTRICT)
      continue;
#endif


   hdr_fpath(fpath, dir, hdr);

#ifdef HAVE_DETECT_CROSSPOST
    if
(check_crosspost(fpath, xbno))
      break;
#endif

    brd_fpath(xfolder,
xboard, fn_dir);

    if (method)     /* 一般轉錄 */
    {
      /* itoc.030325:
一般轉錄要重新加上 header */
      fpw = fdopen(hdr_stamp(xfolder, 'A',
&xpost, buf), "w");
      ve_header(fpw);

      /* itoc.040228: 如果是從精華
區轉錄出來的話,會顯示轉錄自 [currboard] 看板,
     然而 currboard 未必是該精華
區的看板。不過不是很重要的問題,所以就不管了 :p */
      fprintf(fpw, "※ 本文
轉錄自 [%s] %s\n\n",
    *dir == 'u' ? cuser.userid : method == 2 ? "秘密" :
tmpboard,
    *dir == 'u' ? "信箱" : "看板");

      /* Kyo.051117: 若是從秘密看
板轉出的文章,刪除文章第一行所記錄的看板名稱 */
      finish = 0;
      if
((method == 2) && (fpr = fopen(fpath, "r")))
      {
    if (fgets(buf,
sizeof(buf), fpr) &&
      ((ptr = strstr(buf, str_post1)) || (ptr =
strstr(buf, str_post2))) && (ptr > buf))
    {
      ptr[-1] = '\n';
      *ptr =
'\0';

      do
      {
        fputs(buf, fpw);
      } while (fgets(buf, sizeof(buf),
fpr));
      finish = 1;
    }
    fclose(fpr);
      }
      if
(!finish)
    f_suck(fpw, fpath);

      fclose(fpw);

      strcpy(xpost.owner,
cuser.userid);
      strcpy(xpost.nick, cuser.username);
    }
    else        /* 原
文轉錄 */
    {
      /* itoc.030325: 原文轉錄直接 copy 即可 */

 hdr_stamp(xfolder, HDR_COPY | 'A', &xpost, fpath);


 strcpy(xpost.owner, hdr->owner);
      strcpy(xpost.nick, hdr->nick);

 strcpy(xpost.date, hdr->date); /* 原文轉載保留原日期 */
    }


 strcpy(xpost.title, ve_title);

    if (rc == 's')
      xpost.xmode =
POST_OUTGO;
#ifdef HAVE_REFUSEMARK
    else if (rc == 'x')
      xpost.xmode
= POST_RESTRICT;
#endif

    rec_bot(xfolder, &xpost, sizeof(HDR));

    if
(rc == 's')
      outgo_post(&xpost, xboard);
  } while (++locus < tag);


 btime_update(xbno);

  /* Thor.981205: check 被轉的板有沒有列入紀錄? */
  if
(!(xbattr & BRD_NOCOUNT))
    cuser.numposts += tag ? tag : 1;    /*
lkchu.981201: 要算 tag */

  /* 復原 currboard、currbattr */
  if (method)

 {
    strcpy(currboard, tmpboard);
    currbattr = tmpbattr;
  }

  vmsg("轉
錄完成");
  return XO_HEAD;
}


int
post_forward(xo)
  XO *xo;
{
  ACCT
muser;
  HDR *hdr;

  if (!HAS_PERM(PERM_LOCAL))
    return XO_NONE;

  hdr =
(HDR *) xo_pool + (xo->pos - xo->top);

  if (hdr->xmode & GEM_FOLDER)  /* 非
 plain text 不能轉 */
    return XO_NONE;

#ifdef HAVE_REFUSEMARK
  if
(!chkrestrict(hdr))
    return XO_NONE;
#endif

  if (acct_get("轉達信件給:
", &muser) > 0)
  {
    strcpy(quote_user, hdr->owner);

 strcpy(quote_nick, hdr->nick);
    hdr_fpath(quote_file, xo->dir, hdr);

 sprintf(ve_title, "%.64s (fwd)", hdr->title);
    move(1, 0);

 clrtobot();
    prints("轉達給: %s (%s)\n標  題: %s\n", muser.userid,
muser.username, ve_title);

    mail_send(muser.userid);
    *quote_file =
'\0';
  }
  return XO_HEAD;
}


/*
----------------------------------------------------- */
/* 板主功能:mark /
delete / label           */
/* -----------------------------------------------------
*/


static int
post_mark(xo)
  XO *xo;
{
  if (bbstate & STAT_BOARD)
  {

 HDR *hdr;
    int pos, cur, xmode;

    pos = xo->pos;
    cur = pos -
xo->top;
    hdr = (HDR *) xo_pool + cur;
    xmode = hdr->xmode;

#ifdef
HAVE_LABELMARK
    if (xmode & POST_DELETE)    /* 待砍的文章不能 mark */

 return XO_NONE;
#endif

    hdr->xmode = xmode ^ POST_MARKED;
    currchrono
= hdr->chrono;
    rec_put(xo->dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ?
hdr->xid : pos, cmpchrono);

    move(3 + cur, 7);
    outc(post_attr(hdr));

 }
  return XO_NONE;
}


static int
post_bottom(xo)
  XO *xo;
{
  if (bbstate
& STAT_BOARD)
  {
    HDR *hdr, post;
    char fpath[64];

    hdr = (HDR *)
xo_pool + (xo->pos - xo->top);

    hdr_fpath(fpath, xo->dir, hdr);

 hdr_stamp(xo->dir, HDR_LINK | 'A', &post, fpath);
#ifdef HAVE_REFUSEMARK

 post.xmode = POST_BOTTOM | (hdr->xmode & POST_RESTRICT);
#else

 post.xmode = POST_BOTTOM;
#endif
    strcpy(post.owner, hdr->owner);

 strcpy(post.nick, hdr->nick);
    strcpy(post.title, hdr->title);


 rec_add(xo->dir, &post, sizeof(HDR));
    /* btime_update(currbno); */    /* 不
需要,因為置底文章不列入未讀 */

    return post_load(xo);   /* 立刻顯示置底文章
 */
  }
  return XO_NONE;
}


#ifdef HAVE_REFUSEMARK
static
int
post_refuse(xo)     /* itoc.010602: 文章加密 */
  XO *xo;
{
  HDR *hdr;

 int pos, cur;

  if (!cuser.userlevel) /* itoc.020114: guest 不能對其他
 guest 的文章加密 */
    return XO_NONE;

  pos = xo->pos;
  cur = pos -
xo->top;
  hdr = (HDR *) xo_pool + cur;

  if (is_author(hdr) || (bbstate &
STAT_BM))
  {
    hdr->xmode ^= POST_RESTRICT;
    currchrono = hdr->chrono;

   rec_put(xo->dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos,
cmpchrono);

    move(3 + cur, 7);
    outc(post_attr(hdr));
  }

  return
XO_NONE;
}
#endif


#ifdef HAVE_LABELMARK
static int
post_label(xo)
  XO
*xo;
{
  if (bbstate & STAT_BOARD)
  {
    HDR *hdr;
    int pos, cur,
xmode;

    pos = xo->pos;
    cur = pos - xo->top;
    hdr = (HDR *) xo_pool
+ cur;
    xmode = hdr->xmode;

    if (xmode & (POST_MARKED |
POST_RESTRICT)) /* mark 或 加密的文章不能待砍 */
      return XO_NONE;


 hdr->xmode = xmode ^ POST_DELETE;
    currchrono = hdr->chrono;

 rec_put(xo->dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos,
cmpchrono);

    move(3 + cur, 7);
    outc(post_attr(hdr));

    return pos
+ 1 + XO_MOVE;  /* 跳至下一項 */
  }

  return XO_NONE;
}


static
int
post_delabel(xo)
  XO *xo;
{
  int fdr, fsize, xmode;
  char fnew[64],
fold[64], *folder;
  HDR *hdr;
  FILE *fpw;

  if (!(bbstate & STAT_BOARD))

   return XO_NONE;

  if (vans("確定要刪除待砍文章嗎(Y/N)?[N] ") != 'y')

 return XO_FOOT;

  folder = xo->dir;
  if ((fdr = open(folder, O_RDONLY)) <
0)
    return XO_FOOT;

  if (!(fpw = f_new(folder, fnew)))
  {

 close(fdr);
    return XO_FOOT;
  }

  fsize = 0;
  mgets(-1);
  while (hdr
= mread(fdr, sizeof(HDR)))
  {
    xmode = hdr->xmode;

    if (!(xmode &
POST_DELETE))
    {
      if ((fwrite(hdr, sizeof(HDR), 1, fpw) != 1))

 {
    close(fdr);
    fclose(fpw);
    unlink(fnew);
    return XO_FOOT;
      }

 fsize++;
    }
    else
    {
      /* 連線砍信 */
      cancel_post(hdr);


     hdr_fpath(fold, folder, hdr);
      unlink(fold);
    }
  }

 close(fdr);
  fclose(fpw);

  sprintf(fold, "%s.o", folder);

 rename(folder, fold);
  if (fsize)
    rename(fnew, folder);
  else

 unlink(fnew);

  btime_update(currbno);

  return
post_load(xo);
}
#endif


static int
post_delete(xo)
  XO *xo;
{
  int pos,
cur, by_BM;
  HDR *hdr;
  char buf[80];

  if (!cuser.userlevel ||

 !strcmp(currboard, BN_DELETED) ||
    !strcmp(currboard, BN_JUNK))

 return XO_NONE;

  pos = xo->pos;
  cur = pos - xo->top;
  hdr = (HDR *)
xo_pool + cur;

  if ((hdr->xmode & POST_MARKED) || (!(bbstate & STAT_BOARD)
&& !is_author(hdr)))
    return XO_NONE;

  by_BM = bbstate & STAT_BOARD;


 if (vans(msg_del_ny) == 'y')
  {
    currchrono = hdr->chrono;

    if
(!rec_del(xo->dir, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos,
cmpchrono))
    {
      pos = move_post(hdr, xo->dir, by_BM);

      if
(!by_BM && !(currbattr & BRD_NOCOUNT) && !(hdr->xmode & POST_BOTTOM))

 {
    /* itoc.010711: 砍文章要扣錢,算檔案大小 */
    pos = pos >> 3; /* 相對於
 post 時 wordsnum / 10 */

    /* itoc.010830.註解: 漏洞: 若 multi-login 砍不到另
一隻的錢 */
    if (cuser.money > pos)
      cuser.money -= pos;
    else

 cuser.money = 0;

    if (cuser.numposts > 0)

 cuser.numposts--;
    sprintf(buf, "%s,您的文章減為 %d 篇", MSG_DEL_OK,
cuser.numposts);
    vmsg(buf);
      }

      if (xo->key == XZ_XPOST)

 {
    vmsg("原列表經刪除後混亂,請重進串接模式!");
    return XO_QUIT;
      }

   return XO_LOAD;
    }
  }
  return XO_FOOT;
}


static int
chkpost(hdr)

 HDR *hdr;
{
  return (hdr->xmode & POST_MARKED);
}


static int
vfypost(hdr,
pos)
  HDR *hdr;
  int pos;
{
  return (Tagger(hdr->chrono, pos, TAG_NIN) ||
chkpost(hdr));
}


static void
delpost(xo, hdr)
  XO *xo;
  HDR *hdr;
{

 char fpath[64];

  cancel_post(hdr);
  hdr_fpath(fpath, xo->dir, hdr);

 unlink(fpath);
}


static int
post_rangedel(xo)
  XO *xo;
{
  if (!(bbstate
& STAT_BOARD))
    return XO_NONE;

  btime_update(currbno);

  return
xo_rangedel(xo, sizeof(HDR), chkpost, delpost);
}


static
int
post_prune(xo)
  XO *xo;
{
  int ret;

  if (!(bbstate & STAT_BOARD))

 return XO_NONE;

  ret = xo_prune(xo, sizeof(HDR), vfypost, delpost);


 btime_update(currbno);

  if (xo->key == XZ_XPOST && ret == XO_LOAD)
  {

 vmsg("原列表經批次刪除後混亂,請重進串接模式!");
    return XO_QUIT;
  }


 return ret;
}


static int
post_copy(xo)      /* itoc.010924: 取代 gem_gather
*/
  XO *xo;
{
  int tag;

  tag = AskTag("看板文章拷貝");

  if (tag < 0)

 return XO_FOOT;

#ifdef HAVE_REFUSEMARK
  gem_buffer(xo->dir, tag ? NULL :
(HDR *) xo_pool + (xo->pos - xo->top), chkrestrict);
#else

 gem_buffer(xo->dir, tag ? NULL : (HDR *) xo_pool + (xo->pos - xo->top),
NULL);
#endif

  if (bbstate & STAT_BOARD)
  {
#ifdef XZ_XPOST
    if
(xo->key == XZ_XPOST)
    {
      zmsg("檔案標記完成。[注意] 您必須先離開串接
模式才能進入精華區。");
      return XO_FOOT;
    }
    else
#endif
    {

   zmsg("拷貝完成。[注意] 貼上後才能刪除原文!");
      return
post_gem(xo);   /* 拷貝完直接進精華區 */
    }
  }

  zmsg("檔案標記完成。[注意
] 您只能在擔任(小)板主所在或個人精華區貼上。");
  return XO_FOOT;
}


/*
----------------------------------------------------- */
/* 站長功能:edit /
title                */
/* -----------------------------------------------------
*/


int
post_edit(xo)
  XO *xo;
{
  char fpath[64];
  HDR *hdr;
  FILE
*fp;

  hdr = (HDR *) xo_pool + (xo->pos - xo->top);

  hdr_fpath(fpath,
xo->dir, hdr);

  if (HAS_PERM(PERM_ALLBOARD))          /* 站長修改 */
  {
#ifdef
HAVE_REFUSEMARK
    if (!chkrestrict(hdr))
      return XO_NONE;
#endif

 vedit(fpath, 0);
  }
  else if (cuser.userlevel && is_author(hdr))   /* 原作者
修改 */
  {
    if (!vedit(fpath, 0))   /* 若非取消則加上修改資訊 */
    {

 if (fp = fopen(fpath, "a"))
      {
    ve_banner(fp, 1);
    fclose(fp);
      }

   }
  }
  else      /* itoc.010301: 提供使用者修改(但不能儲存)其他人發表的文章
 */
  {
#ifdef HAVE_REFUSEMARK
    if (hdr->xmode & POST_RESTRICT)

 return XO_NONE;
#endif
    vedit(fpath, -1);
  }

  /* return post_head(xo);
*/
  return XO_HEAD;   /* itoc.021226: XZ_POST 和 XZ_XPOST 共用 post_edit()
*/
}


void
header_replace(xo, hdr)     /* itoc.010709: 修改文章標題順便修改內文
的標題 */
  XO *xo;
  HDR *hdr;
{
  FILE *fpr, *fpw;
  char srcfile[64],
tmpfile[64], buf[ANSILINELEN];

  hdr_fpath(srcfile, xo->dir, hdr);

 strcpy(tmpfile, "tmp/");
  strcat(tmpfile, hdr->xname);
  f_cp(srcfile,
tmpfile, O_TRUNC);

  if (!(fpr = fopen(tmpfile, "r")))
    return;

  if
(!(fpw = fopen(srcfile, "w")))
  {
    fclose(fpr);
    return;
  }


 fgets(buf, sizeof(buf), fpr);      /* 加入作者 */
  fputs(buf, fpw);


 fgets(buf, sizeof(buf), fpr);      /* 加入標題 */
  if (!str_ncmp(buf, "標",
2))     /* 如果有 header 才改 */
  {
    strcpy(buf, buf[2] == ' ' ? "標  題: "
: "標題: ");
    strcat(buf, hdr->title);
    strcat(buf, "\n");
  }

 fputs(buf, fpw);

  while(fgets(buf, sizeof(buf), fpr))   /* 加入其他 */

 fputs(buf, fpw);

  fclose(fpr);
  fclose(fpw);
  f_rm(tmpfile);
}


static
int
post_title(xo)
  XO *xo;
{
  HDR *fhdr, mhdr;
  int pos, cur;

  if
(!cuser.userlevel)  /* itoc.000213: 避免 guest 在 sysop 板改標題 */
    return
XO_NONE;

  pos = xo->pos;
  cur = pos - xo->top;
  fhdr = (HDR *) xo_pool +
cur;
  memcpy(&mhdr, fhdr, sizeof(HDR));

  if (!is_author(&mhdr) &&
!HAS_PERM(PERM_ALLBOARD))
    return XO_NONE;

  vget(b_lines, 0, "標題:",
mhdr.title, TTLEN + 1, GCARRY);

  if (HAS_PERM(PERM_ALLBOARD))  /*
itoc.000213: 原作者只能改標題 */
  {
    vget(b_lines, 0, "作者:",
mhdr.owner, 73 /* sizeof(mhdr.owner) */, GCARRY);
        /* Thor.980727:
sizeof(mhdr.owner) = 80 會超過一行 */
    vget(b_lines, 0, "暱稱:",
mhdr.nick, sizeof(mhdr.nick), GCARRY);
    vget(b_lines, 0, "日期:",
mhdr.date, sizeof(mhdr.date), GCARRY);
  }

  if (memcmp(fhdr, &mhdr,
sizeof(HDR)) && vans(msg_sure_ny) == 'y')
  {
    memcpy(fhdr, &mhdr,
sizeof(HDR));
    currchrono = fhdr->chrono;
    rec_put(xo->dir, fhdr,
sizeof(HDR), xo->key == XZ_XPOST ? fhdr->xid : pos, cmpchrono);

    move(3 +
cur, 0);
    post_item(++pos, fhdr);

    /* itoc.010709: 修改文章標題順便修改
內文的標題 */
    header_replace(xo, fhdr);
  }
  return XO_FOOT;
}


/*
----------------------------------------------------- */
/* 額外功能:write /
score                */
/* -----------------------------------------------------
*/


int
post_write(xo)          /* itoc.010328: 丟線上作者水球 */
  XO *xo;
{
  if
(HAS_PERM(PERM_PAGE))
  {
    HDR *hdr;
    UTMP *up;

    hdr = (HDR *)
xo_pool + (xo->pos - xo->top);

    if (!(hdr->xmode & POST_INCOME) && (up =
utmp_seek(hdr)))
      do_write(up);
  }
  return XO_NONE;
}


#ifdef
HAVE_SCORE

static int curraddscore;


static void
addscore(hdd, ram)
  HDR
*hdd, *ram;
{
  hdd->xmode |= POST_SCORE;
  if (curraddscore > 0)
  {
    if
(hdd->score < 35)
      hdd->score++;
  }
  else
  {
    if (hdd->score >
-35)
      hdd->score--;
  }
}


int
post_score(xo)
  XO *xo;
{
  HDR *hdr;

 int pos, cur, ans, vtlen, maxlen;
  char *dir, *userid, *verb, fpath[64],
reason[80], vtbuf[12];
  FILE *fp;
#ifdef HAVE_ANONYMOUS
  char uid[IDLEN +
1];
#endif

  if ((currbattr & BRD_NOSCORE) || !cuser.userlevel || !(bbstate
& STAT_POST))   /* 評分視同發表文章 */
    return XO_NONE;

  pos = xo->pos;

 cur = pos - xo->top;
  hdr = (HDR *) xo_pool + cur;

#ifdef HAVE_REFUSEMARK

 if (!chkrestrict(hdr))
    return XO_NONE;
#endif

  switch (ans = vans("◎
評分 1)推文 2)唾棄 3)自定推 4)自定呸?[Q] "))
  {
  case '1':
    verb = "1m推
";
    vtlen = 2;
    break;

  case '2':
    verb = "2m呸";
    vtlen = 2;

   break;

  case '3':
  case '4':
    if (!vget(b_lines, 0, "請輸入動詞:",
fpath, 5, DOECHO))
      return XO_FOOT;
    vtlen = strlen(fpath);

 sprintf(verb = vtbuf, "%cm%s", ans - 2, fpath);
    break;

  default:

 return XO_FOOT;
  }

#ifdef HAVE_ANONYMOUS
  if (currbattr & BRD_ANONYMOUS)

   maxlen = 64 - IDLEN - vtlen;
  else
#endif
    maxlen = 64 -
strlen(cuser.userid) - vtlen;

  if (!vget(b_lines, 0, "請輸入理由:",
reason, maxlen, DOECHO))
    return XO_FOOT;

#ifdef HAVE_ANONYMOUS
  if
(currbattr & BRD_ANONYMOUS)
  {
    userid = uid;
    if (!vget(b_lines, 0, "
請輸入您想用的ID,也可直接按[Enter],或是按[r]用真名:", userid, IDLEN,
DOECHO))
      userid = STR_ANONYMOUS;
    else if (userid[0] == 'r' &&
userid[1] == '\0')
      userid = cuser.userid;
    else
      strcat(userid,
".");       /* 自定的話,最後加 '.' */
    maxlen = 64 - strlen(userid) - vtlen;

 }
  else
#endif
    userid = cuser.userid;

  dir = xo->dir;

 hdr_fpath(fpath, dir, hdr);

  if (fp = fopen(fpath, "a"))
  {
    time_t
now;
    struct tm *ptime;

    time(&now);
    ptime = localtime(&now);


 fprintf(fp, "→ \033[36m%s \033[3%s\033[m:%-*s%02d/%02d/%02d\n",

 userid, verb, maxlen, reason,
      ptime->tm_year % 100, ptime->tm_mon +
1, ptime->tm_mday);
    fclose(fp);
  }

  curraddscore = 0;
  if ((ans -
'0') & 0x01)    /* 加分 */
  {
    if (hdr->score < 35)
      curraddscore = 1;

 }
  else              /* 扣分 */
  {
    if (hdr->score > -35)
      curraddscore =
-1;
  }

  if (curraddscore)
  {
    currchrono = hdr->chrono;

 rec_ref(dir, hdr, sizeof(HDR), xo->key == XZ_XPOST ? hdr->xid : pos,
cmpchrono, addscore);
    return XO_LOAD;
  }

  return XO_FOOT;
}
#endif  /*
HAVE_SCORE */


static int
post_help(xo)
  XO *xo;
{
  xo_help("post");
  /*
return post_head(xo); */
  return XO_HEAD;       /* itoc.001029: 與 xpost_help 共用
 */
}


KeyFunc post_cb[] =
{
  XO_INIT, post_init,
  XO_LOAD, post_load,

 XO_HEAD, post_head,
  XO_BODY, post_body,

  'r', post_browse,
  's',
post_switch,
  KEY_TAB, post_gem,
  'z', post_gem,

  'y', post_reply,
  'd',
post_delete,
  'v', post_visit,
  'x', post_cross,      /* 在 post/mbox 中都是小寫
 x 轉看板,大寫 X 轉使用者 */
  'X', post_forward,
  't', post_tag,
  'E',
post_edit,
  'T', post_title,
  'm', post_mark,
  '_', post_bottom,
  'D',
post_rangedel,

#ifdef HAVE_SCORE
  '%', post_score,
#endif

  'w',
post_write,

  'b', post_memo,
  'c', post_copy,
  'g', gem_gather,


 Ctrl('P'), post_add,
  Ctrl('D'), post_prune,
  Ctrl('Q'), xo_uquery,

 Ctrl('O'), xo_usetup,

#ifdef HAVE_REFUSEMARK
  Ctrl('Y'),
post_refuse,
#endif

#ifdef HAVE_LABELMARK
  'n', post_label,
  Ctrl('N'),
post_delabel,
#endif

  'B' | XO_DL, (void *) "bin/manage.so:post_manage",

 'R' | XO_DL, (void *) "bin/vote.so:vote_result",
  'V' | XO_DL, (void *)
"bin/vote.so:XoVote",

#ifdef HAVE_TERMINATOR
  Ctrl('X') | XO_DL, (void *)
"bin/manage.so:post_terminator",
#endif

  '~', XoXselect,       /* itoc.001220: 搜
尋作者/標題 */
  'S', XoXsearch,       /* itoc.001220: 搜尋相同標題文章 */
  'a',
XoXauthor,      /* itoc.001220: 搜尋作者 */
  '/', XoXtitle,        /* itoc.001220: 搜尋
標題 */
  'f', XoXfull,         /* itoc.030608: 全文搜尋 */
  'G', XoXmark,         /*
itoc.010325: 搜尋 mark 文章 */
  'L', XoXlocal,        /* itoc.010822: 搜尋本地文章
 */

#ifdef HAVE_XYNEWS
  'u', XoNews,          /* itoc.010822: 新聞閱讀模式
 */
#endif

  'h', post_help
};


KeyFunc xpost_cb[] =
{
  XO_INIT,
xpost_init,
  XO_LOAD, xpost_load,
  XO_HEAD, xpost_head,
  XO_BODY,
post_body,      /* Thor.980911: 共用即可 */

  'r', xpost_browse,
  'y',
post_reply,
  't', post_tag,
  'x', post_cross,
  'X', post_forward,
  'c',
post_copy,
  'g', gem_gather,
  'm', post_mark,
  'd', post_delete,     /*
Thor.980911: 方便板主 */
  'E', post_edit,       /* itoc.010716: 提供 XPOST 中可以
編輯標題、文章,加密 */
  'T', post_title,
#ifdef HAVE_SCORE
  '%',
post_score,
#endif
  'w', post_write,
#ifdef HAVE_REFUSEMARK
  Ctrl('Y'),
post_refuse,
#endif
#ifdef HAVE_LABELMARK
  'n', post_label,
#endif

  '~',
XoXselect,
  'S', XoXsearch,
  'a', XoXauthor,
  '/', XoXtitle,
  'f',
XoXfull,
  'G', XoXmark,
  'L', XoXlocal,

  Ctrl('P'), post_add,

 Ctrl('D'), post_prune,
  Ctrl('Q'), xo_uquery,
  Ctrl('O'), xo_usetup,


 'h', post_help     /* itoc.030511: 共用即可 */
};


#ifdef HAVE_XYNEWS
KeyFunc
news_cb[] =
{
  XO_INIT, news_init,
  XO_LOAD, news_load,
  XO_HEAD,
news_head,
  XO_BODY, post_body,

  'r', XoXsearch,

  'h', post_help        /*
itoc.030511: 共用即可 */
};
#endif  /* HAVE_XYNEWS */


--
               ─╮                  ╭───────╮
               │  │x�n一�丑D�薔坐p�sony.TFcis.org│  │
                   ╰─────────╯              ╰─
               by mail6543210 from  59.104.2.118 (種子電信)
文章選讀←離開[主題上]主題下(k)上篇(j)下篇S/a搜尋 G串列 TAB精華 ↑↓捲 Pg/Space翻