summaryrefslogtreecommitdiffstats
path: root/mbbsd/announce.c
diff options
context:
space:
mode:
authorin2 <in2@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2002-03-07 23:13:44 +0800
committerin2 <in2@63ad8ddf-47c3-0310-b6dd-a9e9d9715204>2002-03-07 23:13:44 +0800
commitae31e19f92e717919ac8e3db9039eb38d2b89aae (patch)
treec70164d6a1852344f44b04a653ae2815043512af /mbbsd/announce.c
downloadpttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.gz
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.bz2
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.lz
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.xz
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.tar.zst
pttbbs-ae31e19f92e717919ac8e3db9039eb38d2b89aae.zip
Initial revision
git-svn-id: http://opensvn.csie.org/pttbbs/pttbbs/trunk/pttbbs@1 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd/announce.c')
-rw-r--r--mbbsd/announce.c1590
1 files changed, 1590 insertions, 0 deletions
diff --git a/mbbsd/announce.c b/mbbsd/announce.c
new file mode 100644
index 00000000..960a45aa
--- /dev/null
+++ b/mbbsd/announce.c
@@ -0,0 +1,1590 @@
+/* $Id: announce.c,v 1.1 2002/03/07 15:13:48 in2 Exp $ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "config.h"
+#include "pttstruct.h"
+#include "perm.h"
+#include "common.h"
+#include "modes.h"
+#include "proto.h"
+
+extern int b_lines;
+extern int p_lines;
+extern int TagNum;
+extern int currbid;
+static void g_showmenu(gmenu_t *pm) {
+ static char *mytype = "編 選 絲路之旅";
+ char *title, ch;
+ int n, max;
+ item_t *item;
+
+ showtitle("精華文章", pm->mtitle);
+ prints(" \033[1;36m編號 標 題%56s\033[m", mytype);
+
+ if(pm->num) {
+ n = pm->page;
+ max = n + p_lines;
+ if(max > pm->num)
+ max = pm->num;
+ while(n < max) {
+ item = pm->item[n++];
+ title = item->title;
+ ch = title[1];
+ prints("\n%5d. %-72.71s", n, title);
+ }
+ } else
+ outs("\n 《精華區》尚在吸取天地間的日精月華 :)");
+
+ move(b_lines, 1);
+ outs(pm->level ?
+ "\033[34;46m 【板 主】 \033[31;47m (h)\033[30m說明 "
+ "\033[31m(q/←)\033[30m離開 \033[31m(n)\033[30m新增文章 "
+ "\033[31m(g)\033[30m新增目錄 \033[31m(e)\033[30m編輯檔案 \033[m" :
+ "\033[34;46m 【功\能鍵】 \033[31;47m (h)\033[30m說明 "
+ "\033[31m(q/←)\033[30m離開 \033[31m(k↑j↓)\033[30m移動游標 "
+ "\033[31m(enter/→)\033[30m讀取資料 \033[m");
+}
+
+static FILE *go_cmd(item_t *node, int *sock) {
+ struct sockaddr_in sin;
+ struct hostent *host;
+ char *site;
+ FILE *fp;
+
+ *sock = socket(AF_INET, SOCK_STREAM, 0);
+
+ if(*sock < 0) {
+ syslog(LOG_ERR, "socket(): %m");
+ return NULL;
+ }
+ memset((char *)&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(node->X.G.port);
+
+ host = gethostbyname(site = node->X.G.server);
+ if(host == NULL)
+ sin.sin_addr.s_addr = inet_addr(site);
+ else
+ memcpy(&sin.sin_addr.s_addr, host->h_addr, host->h_length);
+
+ if(connect(*sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ syslog(LOG_ERR, "connect(): %m");
+ return NULL;
+ }
+ fp = fdopen(*sock, "r+");
+ if(fp != NULL) {
+ setbuf(fp, (char *) 0);
+ fprintf(fp, "%s\r\n", node->X.G.path);
+ fflush(fp);
+ } else
+ close(*sock);
+ return fp;
+}
+
+static char *nextfield(char *data, char *field) {
+ register int ch;
+
+ while((ch = *data)) {
+ data++;
+ if((ch == '\t') || (ch == '\r' && *data == '\n'))
+ break;
+ *field++ = ch;
+ }
+ *field = '\0';
+ return data;
+}
+
+extern char *str_author1;
+
+static FILE* my_open(char* path) {
+ FILE* ans = 0;
+ char buf[80];
+ struct stat st;
+ time_t now = time(0);
+
+ if(stat(path, &st) == 0 && st.st_mtime < now - 3600 * 24 * 7) {
+ return fopen(path, "w");
+ }
+
+ if((ans = fopen(path, "r+"))) {
+ fclose(ans);
+ return 0;
+ /*
+ return directly due to currutmp->pager > 1 mode (real copy)
+ */
+ fgets(buf, 80, ans);
+ if(!strncmp(buf, str_author1, strlen(str_author1)) ||
+ *buf == '0' || *buf == '1') {
+ fclose(ans);
+ return 0;
+ }
+
+ rewind(ans);
+ } else
+ ans = fopen(path, "w");
+ return ans;
+}
+
+static jmp_buf jbuf;
+
+static void isig(int sig) {
+ longjmp(jbuf, 1);
+}
+
+#define PROXY_HOME "proxy/"
+extern userec_t cuser;
+extern userinfo_t *currutmp;
+
+static void go_proxy(char* fpath, item_t *node, int update) {
+ char *ptr, *str, *server;
+ int ch;
+ static FILE *fo;
+
+ strcpy(fpath, PROXY_HOME);
+ ptr = fpath + sizeof(PROXY_HOME) - 1;
+ str = server = node->X.G.server;
+ while((ch = *str)) {
+ str++;
+ if(ch == '.') {
+ if(!strcmp(str, "edu.tw"))
+ break;
+ } else if(ch >= 'A' && ch <= 'Z') {
+ ch |= 0x20;
+ }
+ *ptr++ = ch;
+ }
+ *ptr = '\0';
+ mkdir(fpath, 0755);
+
+ *ptr++ = '/';
+ str = node->X.G.path;
+ while((ch = *str)) {
+ str++;
+ if(ch == '/') {
+ ch = '.';
+ }
+ *ptr++ = ch;
+ }
+ *ptr = '\0';
+
+ /* expire proxy data */
+
+ if((fo = update ? fopen(fpath, "w") : my_open(fpath))) {
+ FILE *fp;
+ char buf[512];
+ int sock;
+
+ if(fo == NULL)
+ return;
+
+ outmsg("★ 建立 proxy 資料連線中 ... ");
+ refresh();
+
+ sock = -1;
+ if(setjmp(jbuf)) {
+ if(sock != -1)
+ close(sock);
+ fseek(fo, 0, SEEK_SET);
+ fwrite("", 0, 0, fo);
+ fclose(fo);
+ alarm(0);
+ return;
+ }
+
+ signal(SIGALRM, isig);
+ alarm(5);
+ fp = go_cmd(node, &sock);
+ alarm(0);
+
+ str = node->title;
+ ch = str[1];
+ if(ch == (char) 0xbc &&
+ !(HAS_PERM(PERM_SYSOP) && currutmp->pager > 1)) {
+ time_t now;
+
+ time(&now);
+ fprintf(fo, "作者: %s (連線精華區)\n標題: %s\n時間: %s\n",
+ server, str + 3, ctime(&now)
+ );
+ }
+
+ while(fgets(buf, 511, fp)) {
+ if(!strcmp(buf, ".\r\n"))
+ break;
+ if((ptr = strstr(buf, "\r\n")))
+ strcpy(ptr, "\n");
+ fputs(buf, fo);
+ }
+ fclose(fo);
+ fclose(fp);
+ }
+}
+
+static void g_additem(gmenu_t *pm, item_t *myitem) {
+ if(pm->num < MAX_ITEMS) {
+ item_t *newitem = (item_t *)malloc(sizeof(item_t));
+
+ memcpy(newitem, myitem, sizeof(item_t));
+ pm->item[(pm->num)++] = newitem;
+ }
+}
+
+static void go_menu(gmenu_t *pm, item_t *node, int update) {
+ FILE *fp;
+ char buf[512], *ptr, *title;
+ item_t item;
+ int ch;
+
+ go_proxy(buf, node, update);
+ pm->num = 0;
+ if((fp = fopen(buf, "r"))) {
+ title = item.title;
+ while(fgets(buf, 511, fp)) {
+ ptr = buf;
+ ch = *ptr++;
+ if(ch != '0' && ch != '1')
+ continue;
+
+ strcpy(title, "□ ");
+ if(ch == '1')
+ title[1] = (char) 0xbd;
+
+ ptr = nextfield(ptr, title + 3);
+ if(!*ptr)
+ continue;
+ title[sizeof(item.title) - 1] = '\0';
+
+ ptr = nextfield(ptr, item.X.G.path);
+ if(!*ptr)
+ continue;
+
+ ptr = nextfield(ptr, item.X.G.server);
+ if(!*ptr)
+ continue;
+
+ nextfield(ptr, buf);
+ item.X.G.port = atoi(buf);
+
+ g_additem(pm, &item);
+ }
+ fclose(fp);
+ }
+}
+
+static int g_searchtitle(gmenu_t* pm, int rev) {
+ static char search_str[30] = "";
+ int pos;
+
+ if(getdata(b_lines - 1, 1,"[搜尋]關鍵字:", search_str, 40, DOECHO))
+ if(!*search_str)
+ return pm->now;
+
+ str_lower(search_str, search_str);
+
+ rev = rev ? -1 : 1;
+ pos = pm->now;
+ do {
+ pos += rev;
+ if(pos == pm->num)
+ pos = 0;
+ else if(pos < 0)
+ pos = pm->num - 1;
+ if(strstr_lower(pm->item[pos]->title, search_str))
+ return pos;
+ } while(pos != pm->now);
+ return pm->now;
+}
+
+static void g_showhelp() {
+ clear();
+ outs("\033[36m【 " BBSNAME "連線精華區使用說明 】\033[m\n\n"
+ "[←][q] 離開到上一層目錄\n"
+ "[↑][k] 上一個選項\n"
+ "[↓][j] 下一個選項\n"
+ "[→][r][enter] 進入目錄/讀取文章\n"
+ "[b][PgUp] 上頁選單\n"
+ "[^F][PgDn][Spc] 下頁選單\n"
+ "[##] 移到該選項\n"
+ "[^S] 將文章存到信箱\n"
+ "[R] 更新資料\n"
+ "[N] 查詢檔名\n"
+ "[c][C][^C] 拷貝文章/並跳至上次貼文章的地方/"
+ "直接貼到上次貼的地方\n");
+ pressanykey();
+}
+
+#define PATHLEN 256
+
+static char paste_fname[200];
+extern time_t paste_time;
+extern char paste_path[];
+extern char paste_title[];
+extern int paste_level;
+
+static void load_paste() {
+ struct stat st;
+ FILE *fp;
+
+ if(!*paste_fname)
+ setuserfile(paste_fname, "paste_path");
+ if(stat(paste_fname, &st) == 0 && st.st_mtime > paste_time &&
+ (fp = fopen(paste_fname, "r"))) {
+ int i;
+ fgets(paste_path, PATHLEN, fp);
+ i = strlen(paste_path) - 1;
+ if(paste_path[i] == '\n')
+ paste_path[i] = 0;
+ fgets(paste_title, STRLEN, fp);
+ i = strlen(paste_title) - 1;
+ if(paste_title[i] == '\n')
+ paste_title[i] = 0;
+ fscanf(fp, "%d", &paste_level);
+ paste_time = st.st_mtime;
+ fclose(fp);
+ }
+}
+
+static char copyfile[PATHLEN];
+static char copytitle[TTLEN+1];
+static char copyowner[IDLEN + 2];
+
+void a_copyitem(char* fpath, char* title, char* owner, int mode) {
+ strcpy(copyfile, fpath);
+ strcpy(copytitle, title);
+ if(owner)
+ strcpy(copyowner, owner);
+ else
+ *copyowner = 0;
+ if(mode) {
+ outmsg("檔案標記完成。[注意] 拷貝後才能刪除原文!");
+ igetch();
+ }
+}
+
+#define FHSZ sizeof(fileheader_t)
+
+static void a_loadname(menu_t *pm) {
+ char buf[PATHLEN];
+ int len;
+
+ setadir(buf, pm->path);
+ len = get_records(buf, pm->header, FHSZ, pm->page+1, p_lines);
+ if(len < p_lines)
+ bzero(&pm->header[len], FHSZ*(p_lines-len));
+}
+
+static void a_timestamp(char *buf, time_t *time) {
+ struct tm *pt = localtime(time);
+
+ sprintf(buf, "%02d/%02d/%02d", pt->tm_mon + 1, pt->tm_mday, (pt->tm_year + 1900) % 100);
+}
+
+static void a_showmenu(menu_t *pm) {
+ char *title, *editor;
+ int n;
+ fileheader_t *item;
+ char buf[PATHLEN];
+ time_t dtime;
+
+ showtitle("精華文章", pm->mtitle);
+ prints(" \033[1;36m編號 標 題%56s\033[0m",
+ "編 選 日 期");
+
+ if(pm->num) {
+ setadir(buf, pm->path);
+ a_loadname(pm);
+ for(n = 0; n < p_lines && pm->page + n < pm->num; n++) {
+ item = &pm->header[n];
+ title = item->title;
+ editor = item->owner;
+ /* Ptt 把時間改為取檔案時間
+ dtime = atoi(&item->filename[2]);
+ */
+ sprintf(buf,"%s/%s",pm->path,item->filename);
+ dtime = dasht(buf);
+ a_timestamp(buf, &dtime);
+ prints("\n%6d%c %-47.46s%-13s[%s]", pm->page+n+1,
+ (item->filemode & FILE_BM) ?'X':
+ (item->filemode & FILE_HIDE) ?')':'.',
+ title, editor,
+ buf);
+ }
+ } else
+ outs("\n 《精華區》尚在吸取天地間的日月精華中... :)");
+
+ move(b_lines, 1);
+ outs(pm->level ?
+ "\033[34;46m 【板 主】 \033[31;47m (h)\033[30m說明 "
+ "\033[31m(q/←)\033[30m離開 \033[31m(n)\033[30m新增文章 "
+ "\033[31m(g)\033[30m新增目錄 \033[31m(e)\033[30m編輯檔案 \033[m" :
+ "\033[34;46m 【功\能鍵】 \033[31;47m (h)\033[30m說明 "
+ "\033[31m(q/←)\033[30m離開 \033[31m(k↑j↓)\033[30m移動游標 "
+ "\033[31m(enter/→)\033[30m讀取資料 \033[m");
+}
+
+static int a_searchtitle(menu_t *pm, int rev) {
+ static char search_str[40] = "";
+ int pos;
+
+ getdata(b_lines - 1, 1, "[搜尋]關鍵字:", search_str, 40, DOECHO);
+
+ if(!*search_str)
+ return pm->now;
+
+ str_lower(search_str, search_str);
+
+ rev = rev ? -1 : 1;
+ pos = pm->now;
+ do {
+ pos += rev;
+ if(pos == pm->num)
+ pos = 0;
+ else if(pos < 0)
+ pos = pm->num - 1;
+ if(pos < pm->page || pos >= pm->page + p_lines) {
+ pm->page = pos - pos % p_lines;
+ a_loadname(pm);
+ }
+ if(strstr_lower(pm->header[pos - pm->page].title, search_str))
+ return pos;
+ } while(pos != pm->now);
+ return pm->now;
+}
+
+enum {NOBODY, MANAGER, SYSOP};
+
+static void a_showhelp(int level) {
+ clear();
+ outs("\033[36m【 " BBSNAME "公佈欄使用說明 】\033[m\n\n"
+ "[←][q] 離開到上一層目錄\n"
+ "[↑][k] 上一個選項\n"
+ "[↓][j] 下一個選項\n"
+ "[→][r][enter] 進入目錄/讀取文章\n"
+ "[^B][PgUp] 上頁選單\n"
+ "[^F][PgDn][Spc] 下頁選單\n"
+ "[##] 移到該選項\n"
+ "[F][U] 將文章寄回 Internet 郵箱/"
+ "將文章 uuencode 後寄回郵箱\n");
+ if(level >= MANAGER) {
+ outs("\n\033[36m【 板主專用鍵 】\033[m\n"
+ "[H] 切換為 公開/會員/版主 才能閱\讀\n"
+ "[n/g/G] 收錄精華文章/開闢目錄/建立連線\n"
+ "[m/d/D] 移動/刪除文章/刪除一個範圍的文章\n"
+ "[f/T/e] 編輯標題符號/修改文章標題/內容\n"
+ "[c/p/a] 拷貝/粘貼/附加文章\n"
+ "[^P/^A] 粘貼/附加已用't'標記文章\n");
+ }
+
+ if(level >= SYSOP) {
+ outs("\n\033[36m【 站長專用鍵 】\033[m\n"
+ "[l] 建 symbolic link\n"
+ "[N] 查詢檔名\n");
+ }
+ pressanykey();
+}
+
+static int AnnounceSelect() {
+ static char xboard[20];
+ char buf[20];
+ char fpath[256];
+ boardheader_t *bp;
+
+ move(2, 0);
+ clrtoeol();
+ move(3, 0);
+ clrtoeol();
+ move(1, 0);
+ make_blist();
+ namecomplete("選擇精華區看板:", buf);
+ if(*buf)
+ strcpy(xboard, buf);
+ if(*xboard && (bp = getbcache(getbnum(xboard)))) {
+ setapath(fpath, xboard);
+ setutmpmode(ANNOUNCE);
+ a_menu(xboard, fpath,
+ (HAS_PERM(PERM_ALLBOARD) ||
+ (HAS_PERM(PERM_BM) && is_BM(bp->BM))) ? 1 : 0);
+ }
+ return FULLUPDATE;
+}
+
+extern char vetitle[];
+
+void gem(char* maintitle, item_t* path, int update) {
+ gmenu_t me;
+ int ch;
+ char fname[PATHLEN];
+
+ strncpy(me.mtitle, maintitle, 40);
+ me.mtitle[40] = 0;
+ go_menu(&me, path, update);
+
+ /* 精華區-tree 中部份結構屬於 cuser ==> BM */
+
+ me.level = 0;
+ me.page = 9999;
+ me.now = 0;
+ for(;;) {
+ if(me.now >= me.num && me.num > 0)
+ me.now = me.num - 1;
+ else if(me.now < 0)
+ me.now = 0;
+
+ if(me.now < me.page || me.now >= me.page + p_lines) {
+ me.page = me.now - (me.now % p_lines);
+ g_showmenu(&me);
+ }
+ ch = cursor_key(2 + me.now - me.page, 0);
+ if(ch == 'q' || ch == 'Q' || ch == KEY_LEFT)
+ break;
+
+ if(ch >= '0' && ch <= '9') {
+ if((ch = search_num(ch, me.num)) != -1)
+ me.now = ch;
+ me.page = 9999;
+ continue;
+ }
+
+ switch(ch) {
+ case KEY_UP:
+ case 'k':
+ if(--me.now < 0)
+ me.now = me.num - 1;
+ break;
+ case KEY_DOWN:
+ case 'j':
+ if(++me.now >= me.num)
+ me.now = 0;
+ break;
+ case KEY_PGUP:
+ case 'b':
+ if(me.now >= p_lines)
+ me.now -= p_lines;
+ else if(me.now > 0)
+ me.now = 0;
+ else
+ me.now = me.num - 1;
+ break;
+ case ' ':
+ case KEY_PGDN:
+ case Ctrl('F'):
+ if(me.now < me.num - p_lines)
+ me.now += p_lines;
+ else if(me.now < me.num - 1)
+ me.now = me.num - 1;
+ else
+ me.now = 0;
+ break;
+ case 'h':
+ g_showhelp();
+ me.page = 9999;
+ break;
+ case '?':
+ case '/':
+ me.now = g_searchtitle(&me, ch == '?');
+ me.page = 9999;
+ break;
+ case 'N':
+ if(HAS_PERM(PERM_SYSOP)) {
+ go_proxy(fname, me.item[me.now], 0);
+ move(b_lines - 1, 0);
+ outs(fname);
+ pressanykey();
+ me.page = 9999;
+ }
+ break;
+ case 'c':
+ case 'C':
+ case Ctrl('C'):
+ if(me.now < me.num) {
+ item_t *node = me.item[me.now];
+ char *title = node->title;
+ int mode = title[1];
+
+ load_paste();
+ if(mode == (char) 0xbc || ch == Ctrl('C')) {
+ if(mode == (char) 0xbc)
+ go_proxy(fname, node, 0);
+ if(ch == Ctrl('C') && *paste_path && paste_level) {
+ char newpath[PATHLEN];
+ fileheader_t item;
+
+ strcpy(newpath, paste_path);
+ if(mode == (char) 0xbc) {
+ stampfile(newpath, &item);
+ unlink(newpath);
+ Link(fname, newpath);
+ } else
+ stampdir(newpath, &item);
+ strcpy(item.owner, cuser.userid);
+ sprintf(item.title, "%s%.72s",
+ (currutmp->pager > 1) ? "" :
+ (mode == (char) 0xbc) ? "◇ " : "◆ ",
+ title + 3);
+ strcpy(strrchr(newpath, '/') + 1, ".DIR");
+ append_record(newpath, &item, FHSZ);
+ if(++me.now >= me.num)
+ me.now = 0;
+ break;
+ }
+ if(mode == (char) 0xbc) {
+ a_copyitem(fname,
+ title + ((currutmp->pager > 1) ? 3 : 0),
+ 0, 1);
+ if(ch == 'C' && *paste_path) {
+ setutmpmode(ANNOUNCE);
+ a_menu(paste_title, paste_path, paste_level);
+ }
+ me.page = 9999;
+ } else
+ bell();
+ }
+ }
+ break;
+ case Ctrl('B'):
+ m_read();
+ me.page = 9999;
+ break;
+ case Ctrl('I'):
+ t_idle();
+ me.page = 9999;
+ break;
+ case 's':
+ AnnounceSelect();
+ me.page = 9999;
+ break;
+ case '\n':
+ case '\r':
+ case KEY_RIGHT:
+ case 'r':
+ case 'R':
+ if(me.now < me.num) {
+ item_t *node = me.item[me.now];
+ char *title = node->title;
+ int mode = title[1];
+ int update = (ch == 'R') ? 1 : 0;
+
+ title += 3;
+
+ if(mode == (char) 0xbc) {
+ int more_result;
+
+ go_proxy(fname, node, update);
+ strcpy(vetitle, title);
+ while((more_result = more(fname, YEA))) {
+ if(more_result == 1) {
+ if(--me.now < 0) {
+ me.now = 0;
+ break;
+ }
+ } else if(more_result == 3) {
+ if(++me.now >= me.num) {
+ me.now = me.num - 1;
+ break;
+ }
+ } else
+ break;
+ node = me.item[me.now];
+ if(node->title[1] != (char) 0xbc)
+ break;
+ go_proxy(fname, node, update);
+ strcpy(vetitle, title);
+ }
+ } else if(mode == (char) 0xbd) {
+ gem(title, node, update);
+ }
+ me.page = 9999;
+ }
+ break;
+ }
+ }
+ for(ch = 0; ch < me.num; ch++)
+ free(me.item[ch]);
+}
+
+extern char *msg_fwd_ok;
+extern char *msg_fwd_err1;
+extern char *msg_fwd_err2;
+
+static void a_forward(char *path, fileheader_t *pitem, int mode) {
+ fileheader_t fhdr;
+
+ strcpy(fhdr.filename, pitem->filename);
+ strcpy(fhdr.title, pitem->title);
+ switch(doforward(path, &fhdr, mode)) {
+ case 0:
+ outmsg(msg_fwd_ok);
+ break;
+ case -1:
+ outmsg(msg_fwd_err1);
+ break;
+ case -2:
+ outmsg(msg_fwd_err2);
+ break;
+ }
+}
+
+static void a_additem(menu_t *pm, fileheader_t *myheader) {
+ char buf[PATHLEN];
+
+ setadir(buf, pm->path);
+ if(append_record(buf, myheader, FHSZ) == -1)
+ return;
+ pm->now = pm->num++;
+
+ if(pm->now >= pm->page + p_lines) {
+ pm->page = pm->now - ((pm->page == 10000 && pm->now > p_lines / 2) ?
+ (p_lines / 2) : (pm->now % p_lines));
+ }
+
+ /*Ptt*/
+ strcpy(pm->header[pm->now - pm->page].filename, myheader->filename);
+}
+
+#define ADDITEM 0
+#define ADDGROUP 1
+#define ADDGOPHER 2
+#define ADDLINK 3
+extern char currboard[];
+
+static void a_newitem(menu_t *pm, int mode) {
+ static char *mesg[4] = {
+ "[新增文章] 請輸入標題:", /* ADDITEM */
+ "[新增目錄] 請輸入標題:", /* ADDGROUP */
+ "[新增連線] 請輸入標題:", /* ADDGOPHER */
+ "請輸入標題:" /* ADDLINK */
+ };
+
+ char fpath[PATHLEN], buf[PATHLEN], lpath[PATHLEN];
+ fileheader_t item;
+ int d;
+
+ strcpy(fpath, pm->path);
+
+ switch(mode) {
+ case ADDITEM:
+ stampfile(fpath, &item);
+ strcpy(item.title, "◇ "); /* A1BA */
+ break;
+
+ case ADDGROUP:
+ stampdir(fpath, &item);
+ strcpy(item.title, "◆ "); /* A1BB */
+ break;
+ case ADDGOPHER:
+ bzero(&item, sizeof(item));
+ strcpy(item.title, "☉ "); /* A1BB */
+ if(!getdata(b_lines - 2, 1, "輸入URL位址:",
+ item.filename+2,61, DOECHO))
+ return;
+ break;
+ case ADDLINK:
+ stamplink(fpath, &item);
+ if (!getdata(b_lines - 2, 1, "新增連線:", buf, 61, DOECHO))
+ return;
+ if(invalid_pname(buf)) {
+ unlink(fpath);
+ outs("目的地路徑不合法!");
+ igetch();
+ return;
+ }
+
+ item.title[0] = 0;
+ for(d = 0; d <= 3; d++) {
+ switch(d) {
+ case 0:
+ sprintf(lpath, "%s%s%s/%s",
+ BBSHOME, "/man/boards/",currboard , buf);
+ break;
+ case 1:
+ sprintf(lpath, "%s%s%s",
+ BBSHOME, "/man/boards/" , buf);
+ break;
+ case 2:
+ sprintf(lpath, "%s%s%s",
+ BBSHOME, "/" , buf);
+ break;
+ case 3:
+ sprintf(lpath, "%s%s%s",
+ BBSHOME, "/etc/" , buf);
+ break;
+ }
+ if(dashf(lpath)) {
+ strcpy(item.title, "☆ "); /* A1B3 */
+ break;
+ } else if (dashd(lpath)) {
+ strcpy(item.title, "★ "); /* A1B4 */
+ break;
+ }
+ if(!HAS_PERM(PERM_BBSADM) && d==1)
+ break;
+ }
+
+ if(!item.title[0]) {
+ unlink(fpath);
+ outs("目的地路徑不合法!");
+ igetch();
+ return;
+ }
+ }
+
+ if(!getdata(b_lines - 1, 1, mesg[mode], &item.title[3], 55, DOECHO)) {
+ if(mode == ADDGROUP)
+ rmdir(fpath);
+ else if(mode != ADDGOPHER)
+ unlink(fpath);
+ return;
+ }
+
+ switch(mode) {
+ case ADDITEM:
+ if(vedit(fpath, 0, NULL) == -1) {
+ unlink(fpath);
+ pressanykey();
+ return;
+ }
+ break;
+ case ADDLINK:
+ unlink(fpath);
+ if(symlink(lpath, fpath) == -1) {
+ outs("無法建立 symbolic link");
+ igetch();
+ return;
+ }
+ break;
+ case ADDGOPHER:
+ strcpy(item.date, "70");
+ strncpy(item.filename, "H.",2);
+ break;
+ }
+
+ strcpy(item.owner, cuser.userid);
+ a_additem(pm, &item);
+}
+
+static void a_pasteitem(menu_t *pm, int mode) {
+ char newpath[PATHLEN];
+ char buf[PATHLEN];
+ char ans[2];
+ int i;
+ fileheader_t item;
+
+ move(b_lines - 1, 1);
+ if(copyfile[0]) {
+ if(dashd(copyfile)) {
+ for(i = 0; copyfile[i] && copyfile[i] == pm->path[i]; i++);
+ if(!copyfile[i]) {
+ outs("將目錄拷進自己的子目錄中,會造成無窮迴圈!");
+ igetch();
+ return;
+ }
+ }
+ if(mode) {
+ sprintf(buf, "確定要拷貝[%s]嗎(Y/N)?[N] ", copytitle);
+ getdata(b_lines - 1, 1, buf, ans, 3, LCECHO);
+ } else
+ ans[0]='y';
+ if(ans[0] == 'y') {
+ strcpy(newpath, pm->path);
+
+ if(*copyowner) {
+ char* fname = strrchr(copyfile, '/');
+
+ if(fname)
+ strcat(newpath, fname);
+ else
+ return;
+ if(access(pm->path, X_OK | R_OK | W_OK))
+ mkdir(pm->path, 0755);
+ memset(&item, 0, sizeof(fileheader_t));
+ strcpy(item.filename, fname + 1);
+ memcpy(copytitle, "◎", 2);
+ if(HAS_PERM(PERM_BBSADM))
+ Link(copyfile, newpath);
+ else {
+ sprintf(buf, "/bin/cp %s %s", copyfile, newpath);
+ system(buf);
+ }
+ } else if(dashf(copyfile)) {
+ stampfile(newpath, &item);
+ memcpy(copytitle, "◇", 2);
+ sprintf(buf, "/bin/cp %s %s", copyfile, newpath);
+ } else if(dashd(copyfile)) {
+ stampdir(newpath, &item);
+ memcpy(copytitle, "◆", 2);
+ sprintf(buf, "/bin/cp -r %s/* %s/.D* %s", copyfile, copyfile,
+ newpath);
+ } else {
+ outs("無法拷貝!");
+ igetch();
+ return;
+ }
+ strcpy(item.owner, *copyowner ? copyowner : cuser.userid);
+ strcpy(item.title, copytitle);
+ if(!*copyowner)
+ system(buf);
+ a_additem(pm, &item);
+ copyfile[0] = '\0';
+ }
+ } else {
+ outs("請先執行 copy 命令後再 paste");
+ igetch();
+ }
+}
+
+static void a_appenditem(menu_t *pm, int isask) {
+ char fname[PATHLEN];
+ char buf[ANSILINELEN];
+ char ans[2] = "y";
+ FILE *fp, *fin;
+
+ move(b_lines - 1, 1);
+ if(copyfile[0]) {
+ if(dashf(copyfile)) {
+ sprintf(fname, "%s/%s", pm->path,
+ pm->header[pm->now-pm->page].filename);
+ if(dashf(fname)) {
+ if(isask) {
+ sprintf(buf, "確定要將[%s]附加於此嗎(Y/N)?[N] ",
+ copytitle);
+ getdata(b_lines - 2, 1, buf, ans, 3, LCECHO);
+ }
+ if(ans[0] == 'y') {
+ if((fp = fopen(fname, "a+"))) {
+ if((fin = fopen(copyfile, "r"))) {
+ memset(buf, '-', 74);
+ buf[74] = '\0';
+ fprintf(fp, "\n> %s <\n\n", buf);
+ if(isask)
+ getdata(b_lines - 1, 1,
+ "是否收錄簽名檔部份(Y/N)?[Y] ",
+ ans, 3, LCECHO);
+ while(fgets(buf, sizeof(buf), fin)) {
+ if((ans[0] == 'n' ) &&
+ !strcmp(buf, "--\n"))
+ break;
+ fputs(buf, fp);
+ }
+ fclose(fin);
+ copyfile[0] = '\0';
+ }
+ fclose(fp);
+ }
+ }
+ } else {
+ outs("檔案不得附加於此!");
+ igetch();
+ }
+ } else {
+ outs("不得附加整個目錄於檔案後!");
+ igetch();
+ }
+ } else {
+ outs("請先執行 copy 命令後再 append");
+ igetch();
+ }
+}
+
+static int a_pastetagpost(menu_t *pm, int mode) {
+ extern int TagNum;
+ extern void EnumTagFhdr();
+ extern void UnTagger(int locus);
+ fileheader_t fhdr;
+ int ans = 0, ent=0, tagnum;
+ char title[TTLEN + 1]= "◇ ";
+ char dirname[200],buf[200];
+
+ setbdir(dirname, currboard);
+ tagnum = TagNum;
+
+ if (!tagnum) return ans;
+
+ while (tagnum--)
+ {
+ EnumTagFhdr (&fhdr, dirname, ent++);
+ setbfile (buf, currboard, fhdr.filename);
+
+ if (dashf (buf))
+ {
+ strncpy(title+3, fhdr.title, TTLEN-3);
+ title[TTLEN] = '\0';
+ a_copyitem(buf, title, 0, 0);
+ if(mode)
+ {
+ mode--;
+ a_pasteitem(pm,0);
+ }
+ else a_appenditem(pm, 0);
+ ++ans;
+ UnTagger (tagnum);
+ }
+
+ };
+
+ return ans;
+}
+
+static void a_moveitem(menu_t *pm) {
+ fileheader_t *tmp;
+ char newnum[4];
+ int num, max, min;
+ char buf[PATHLEN];
+ int fail;
+
+ sprintf(buf, "請輸入第 %d 選項的新次序:", pm->now + 1);
+ if(!getdata(b_lines - 1, 1, buf, newnum, 6, DOECHO))
+ return;
+ num = (newnum[0] == '$') ? 9999 : atoi(newnum) - 1;
+ if(num >= pm->num)
+ num = pm->num - 1;
+ else if(num < 0)
+ num = 0;
+ setadir(buf, pm->path);
+ min = num < pm->now ? num : pm->now;
+ max = num > pm->now ? num : pm->now;
+ tmp = (fileheader_t *) calloc(max + 1, FHSZ);
+
+ fail = 0;
+ if(get_records(buf, tmp, FHSZ, 1, min) != min)
+ fail = 1;
+ if(num > pm->now) {
+ if(get_records(buf, &tmp[min], FHSZ, pm->now+2, max-min) != max-min)
+ fail = 1;
+ if(get_records(buf, &tmp[max], FHSZ, pm->now+1, 1) != 1)
+ fail = 1;
+ } else {
+ if(get_records(buf, &tmp[min], FHSZ, pm->now+1, 1) != 1)
+ fail = 1;
+ if(get_records(buf, &tmp[min+1], FHSZ, num+1, max-min) != max-min)
+ fail = 1;
+ }
+ if(!fail)
+ substitute_record(buf, tmp, FHSZ * (max + 1), 1);
+ pm->now = num;
+ free(tmp);
+}
+
+static void a_delrange(menu_t *pm) {
+ char fname[256];
+
+ sprintf(fname,"%s/.DIR",pm->path);
+ del_range(0, NULL, fname);
+ pm->num = get_num_records(fname, FHSZ);
+}
+
+static void a_delete(menu_t *pm) {
+ char fpath[PATHLEN], buf[PATHLEN], cmd[PATHLEN];
+ char ans[4];
+ fileheader_t backup;
+
+ sprintf(fpath, "%s/%s", pm->path, pm->header[pm->now - pm->page].filename);
+ setadir(buf, pm->path);
+
+ if(pm->header[pm->now - pm->page].filename[0] == 'H' &&
+ pm->header[pm->now - pm->page].filename[1] == '.') {
+ getdata(b_lines - 1, 1, "您確定要刪除此精華區連線嗎(Y/N)?[N] ",
+ ans, 3, LCECHO);
+ if(ans[0] != 'y')
+ return;
+ if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+ return;
+ } else if (dashl(fpath)) {
+ getdata(b_lines - 1, 1, "您確定要刪除此 symbolic link 嗎(Y/N)?[N] ",
+ ans, 3, LCECHO);
+ if(ans[0] != 'y')
+ return;
+ if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+ return;
+ unlink(fpath);
+ } else if(dashf(fpath)) {
+ getdata(b_lines - 1, 1, "您確定要刪除此檔案嗎(Y/N)?[N] ", ans, 3,
+ LCECHO);
+ if(ans[0] != 'y')
+ return;
+ if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+ return;
+
+ setbpath(buf, "deleted");
+ stampfile(buf, &backup);
+ strcpy(backup.owner, cuser.userid);
+ strcpy(backup.title,pm->header[pm->now - pm->page].title + 2);
+ backup.savemode = 'D';
+
+ sprintf(cmd, "mv -f %s %s", fpath,buf);
+ system(cmd);
+ setbdir(buf, "deleted");
+ append_record(buf, &backup, sizeof(backup));
+ } else if (dashd(fpath)) {
+ getdata(b_lines - 1, 1, "您確定要刪除整個目錄嗎(Y/N)?[N] ", ans, 3,
+ LCECHO);
+ if(ans[0] != 'y')
+ return;
+ if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+ return;
+
+ setapath(buf, "deleted");
+ stampdir(buf, &backup);
+
+ sprintf(cmd, "rm -rf %s;/bin/mv -f %s %s",buf,fpath,buf);
+ system(cmd);
+
+ strcpy(backup.owner, cuser.userid);
+ strcpy(backup.title,pm->header[pm->now - pm->page].title +2);
+ setapath(buf, "deleted");
+ setadir(buf,buf);
+ append_record(buf, &backup, sizeof(backup));
+ } else { /* Ptt 損毀的項目 */
+ getdata(b_lines - 1, 1, "您確定要刪除此損毀的項目嗎(Y/N)?[N] ",
+ ans, 3, LCECHO);
+ if(ans[0] != 'y')
+ return;
+ if(delete_record(buf, FHSZ, pm->now + 1) == -1)
+ return;
+ }
+ pm->num--;
+}
+
+static void a_newtitle(menu_t *pm) {
+ char buf[PATHLEN];
+ fileheader_t item;
+
+ memcpy(&item, &pm->header[pm->now - pm->page], FHSZ);
+ strcpy(buf,item.title + 3);
+ if(getdata_buf(b_lines - 1, 1, "新標題:", buf, 60, DOECHO)) {
+ strcpy(item.title + 3, buf);
+ setadir(buf, pm->path);
+ substitute_record(buf, &item, FHSZ, pm->now + 1);
+ }
+}
+static void a_hideitem(menu_t *pm) {
+ fileheader_t *item=&pm->header[pm->now - pm->page];
+ char buf[PATHLEN];
+ if(item->filemode&FILE_BM)
+ {
+ item->filemode &= ~FILE_BM;
+ item->filemode &= ~FILE_HIDE;
+ }
+ else if(item->filemode&FILE_HIDE)
+ item->filemode |= FILE_BM;
+ else item->filemode |= FILE_HIDE;
+ setadir(buf, pm->path);
+ substitute_record(buf, item, FHSZ, pm->now + 1);
+}
+static void a_editsign(menu_t *pm) {
+ char buf[PATHLEN];
+ fileheader_t item;
+
+ memcpy(&item, &pm->header[pm->now - pm->page], FHSZ);
+ sprintf(buf, "%c%c", item.title[0], item.title[1]);
+ if(getdata_buf(b_lines - 1, 1, "符號", buf, 5, DOECHO)) {
+ item.title[0] = buf[0] ? buf[0] : ' ';
+ item.title[1] = buf[1] ? buf[1] : ' ';
+ item.title[2] = buf[2] ? buf[2] : ' ';
+ setadir(buf, pm->path);
+ substitute_record(buf, &item, FHSZ, pm->now + 1);
+ }
+}
+
+static void a_showname(menu_t *pm) {
+ char buf[PATHLEN];
+ int len;
+ int i;
+ int sym;
+
+ move(b_lines - 1, 1);
+ sprintf(buf, "%s/%s", pm->path, pm->header[pm->now - pm->page].filename);
+ if(dashl(buf)) {
+ prints("此 symbolic link 名稱為 %s\n",
+ pm->header[pm->now - pm->page].filename);
+ if((len = readlink(buf, buf, PATHLEN-1)) >= 0) {
+ buf[len] = '\0';
+ for(i = 0; BBSHOME[i] && buf[i] == BBSHOME[i]; i++);
+ if(!BBSHOME[i] && buf[i] == '/') {
+ if(HAS_PERM(PERM_BBSADM))
+ sym = 1;
+ else {
+ sym = 0;
+ for(i++; BBSHOME "/man"[i] && buf[i] == BBSHOME "/man"[i];
+ i++);
+ if(!BBSHOME "/man"[i] && buf[i] == '/')
+ sym = 1;
+ }
+ if(sym) {
+ pressanykey();
+ move(b_lines - 1, 1);
+ prints("此 symbolic link 指向 %s\n", &buf[i+1]);
+ }
+ }
+ }
+ } else if(dashf(buf))
+ prints("此文章名稱為 %s", pm->header[pm->now - pm->page].filename);
+ else if(dashd(buf))
+ prints("此目錄名稱為 %s", pm->header[pm->now - pm->page].filename);
+ else
+ outs("此項目已損毀, 建議將其刪除!");
+ pressanykey();
+}
+
+static char *a_title;
+
+static void atitle() {
+ showtitle("精華文章", a_title);
+ outs("[←]離開 [→]閱\讀 [^P]發表文章 [b]備忘錄 [d]刪除 [q]精華區 "
+ "[TAB]文摘 [h]elp\n\033[7m 編號 日 期 作 者 "
+ "文 章 標 題\033[m");
+}
+
+extern char currtitle[];
+
+char trans_buffer[256];
+extern char quote_file[];
+extern unsigned int currstat;
+
+static int isvisible_man(menu_t *me)
+{
+ fileheader_t *fhdr = &me->header[me->now-me->page];
+ if( me->level<MANAGER && ((fhdr->filemode & FILE_BM) ||
+ ((fhdr->filemode & FILE_HIDE) &&
+ hbflcheck(currbid, currutmp->uid))))
+ return 0;
+ return 1;
+}
+int a_menu(char *maintitle, char *path, int lastlevel) {
+ static char Fexit;
+ menu_t me;
+ char fname[PATHLEN];
+ int ch, returnvalue = FULLUPDATE;
+
+ trans_buffer[0] = 0;
+
+ Fexit = 0;
+ me.header = (fileheader_t *)calloc(p_lines, FHSZ);
+ me.path = path;
+ strcpy(me.mtitle, maintitle);
+ setadir(fname, me.path);
+ me.num = get_num_records(fname, FHSZ);
+
+ /* 精華區-tree 中部份結構屬於 cuser ==> BM */
+
+ if(!(me.level = lastlevel)) {
+ char *ptr;
+
+ if((ptr = strrchr(me.mtitle, '[')))
+ me.level = is_BM(ptr + 1);
+ }
+
+ me.page = 9999;
+ me.now = 0;
+ for(;;) {
+ if(me.now >= me.num)
+ me.now = me.num - 1;
+ if(me.now < 0)
+ me.now = 0;
+
+ if(me.now < me.page || me.now >= me.page + p_lines) {
+ me.page = me.now - ((me.page == 10000 && me.now > p_lines / 2) ?
+ (p_lines / 2) : (me.now % p_lines));
+ a_showmenu(&me);
+ }
+
+ ch = cursor_key(2 + me.now - me.page, 0);
+
+ if(ch == 'q' || ch == 'Q' || ch == KEY_LEFT)
+ break;
+
+ if(ch >= '1' && ch <= '9') {
+ if((ch = search_num(ch, me.num)) != -1)
+ me.now = ch;
+ me.page = 10000;
+ continue;
+ }
+
+ switch(ch) {
+ case KEY_UP:
+ case 'k':
+ if(--me.now < 0)
+ me.now = me.num - 1;
+ break;
+
+ case KEY_DOWN:
+ case 'j':
+ if(++me.now >= me.num)
+ me.now = 0;
+ break;
+
+ case KEY_PGUP:
+ case Ctrl('B'):
+ if(me.now >= p_lines)
+ me.now -= p_lines;
+ else if (me.now > 0)
+ me.now = 0;
+ else
+ me.now = me.num - 1;
+ break;
+
+ case ' ':
+ case KEY_PGDN:
+ case Ctrl('F'):
+ if(me.now < me.num - p_lines)
+ me.now += p_lines;
+ else if(me.now < me.num - 1)
+ me.now = me.num - 1;
+ else
+ me.now = 0;
+ break;
+
+ case '0':
+ me.now = 0;
+ break;
+ case '?':
+ case '/':
+ me.now = a_searchtitle(&me, ch == '?');
+ me.page = 9999;
+ break;
+ case '$':
+ me.now = me.num -1;
+ break;
+ case 'h':
+ a_showhelp(me.level);
+ me.page = 9999;
+ break;
+ case Ctrl('C'):
+ cal();
+ me.page = 9999;
+ break;
+
+ case Ctrl('I'):
+ t_idle();
+ me.page = 9999;
+ break;
+
+ case 's':
+ AnnounceSelect();
+ me.page = 9999;
+ break;
+
+ case 'e':
+ case 'E':
+ sprintf(fname, "%s/%s", path, me.header[me.now-me.page].filename);
+ if(dashf(fname) && me.level >= MANAGER) {
+ *quote_file = 0;
+ if(vedit(fname, NA, NULL) != -1) {
+ char fpath[200];
+ fileheader_t fhdr;
+
+ strcpy(fpath, path);
+ stampfile(fpath, &fhdr);
+ unlink(fpath);
+ Rename(fname, fpath);
+ strcpy(me.header[me.now-me.page].filename, fhdr.filename);
+ strcpy(me.header[me.now-me.page].owner, cuser.userid);
+ setadir(fpath, path);
+ substitute_record(fpath, me.header+me.now-me.page,
+ sizeof(fhdr), me.now + 1);
+ }
+ me.page = 9999;
+ }
+ break;
+
+ case 'c':
+ if(me.now < me.num) {
+ if(!isvisible_man(&me)) break;
+ sprintf(fname, "%s/%s", path,
+ me.header[me.now-me.page].filename);
+ a_copyitem(fname, me.header[me.now-me.page].title, 0, 1);
+ me.page = 9999;
+ break;
+ }
+
+ case '\n':
+ case '\r':
+ case KEY_RIGHT:
+ case 'r':
+ if(me.now < me.num) {
+ fileheader_t *fhdr = &me.header[me.now-me.page];
+ if(!isvisible_man(&me)) break;
+ sprintf(fname, "%s/%s", path, fhdr->filename);
+ if(*fhdr->filename == 'H' && fhdr->filename[1] == '.') {
+ item_t item;
+ strcpy(item.X.G.server, fhdr->filename + 2);
+ strcpy(item.X.G.path, "1/");
+ item.X.G.port = 70;
+ gem(fhdr->title, &item, (ch == 'R') ? 1 : 0);
+ } else if (dashf(fname)) {
+ int more_result;
+
+ while((more_result = more(fname, YEA))) {
+ /* Ptt 範本精靈 plugin */
+ if(currstat == EDITEXP || currstat == OSONG) {
+ char ans[4];
+
+ move(22, 0);
+ clrtoeol();
+ getdata(22, 1,
+ currstat == EDITEXP ?
+ "要把範例 Plugin 到文章嗎?[y/N]":
+ "確定要點這首歌嗎?[y/N]",
+ ans, 3, LCECHO);
+ if(ans[0]=='y') {
+ strcpy(trans_buffer,fname);
+ Fexit = 1;
+ free(me.header);
+ if(currstat == OSONG){
+ log_file(FN_USSONG,fhdr->title);
+ }
+ return FULLUPDATE;
+ }
+ }
+ if(more_result == 1) {
+ if(--me.now < 0) {
+ me.now = 0;
+ break;
+ }
+ } else if(more_result == 3) {
+ if(++me.now >= me.num) {
+ me.now = me.num - 1;
+ break;
+ }
+ } else
+ break;
+ if(!isvisible_man(&me)) break;
+ sprintf(fname, "%s/%s", path,
+ me.header[me.now-me.page].filename);
+ if(!dashf(fname))
+ break;
+ }
+ } else if(dashd(fname)) {
+ a_menu(me.header[me.now-me.page].title, fname, me.level);
+ /* Ptt 強力跳出recursive */
+ if(Fexit) {
+ free(me.header);
+ return FULLUPDATE;
+ }
+ }
+ me.page = 9999;
+ }
+ break;
+
+ case 'F':
+ case 'U':
+ sprintf(fname, "%s/%s", path, me.header[me.now-me.page].filename);
+ if(me.now < me.num && HAS_PERM(PERM_BASIC) && dashf(fname)) {
+ a_forward(path, &me.header[me.now-me.page], ch /*== 'U'*/);
+ /*By CharlieL*/
+ } else
+ outmsg("無法轉寄此項目");
+
+ me.page = 9999;
+ refresh();
+ sleep(1);
+ break;
+ }
+
+ if(me.level >= MANAGER) {
+ int page0 = me.page;
+
+ switch(ch) {
+ case 'n':
+ a_newitem(&me, ADDITEM);
+ me.page = 9999;
+ break;
+ case 'g':
+ a_newitem(&me, ADDGROUP);
+ me.page = 9999;
+ break;
+ case 'G':
+ a_newitem(&me, ADDGOPHER);
+ me.page = 9999;
+ break;
+ case 'p':
+ a_pasteitem(&me,1);
+ me.page = 9999;
+ break;
+ case 'f':
+ a_editsign(&me);
+ me.page = 9999;
+ break;
+ case Ctrl('P'):
+ a_pastetagpost(&me, -1);
+ returnvalue = DIRCHANGED;
+ me.page = 9999;
+ break;
+ case Ctrl('A'):
+ a_pastetagpost(&me, 1);
+ returnvalue = DIRCHANGED;
+ me.page = 9999;
+ break;
+ case 'a':
+ a_appenditem(&me, 1);
+ me.page = 9999;
+ break;
+ default:
+ me.page = page0;
+ break;
+ }
+
+ if(me.num)
+ switch(ch) {
+ case 'm':
+ a_moveitem(&me);
+ me.page = 9999;
+ break;
+
+ case 'D':
+ /* Ptt me.page = -1;*/
+ a_delrange(&me);
+ me.page = 9999;
+ break;
+ case 'd':
+ a_delete(&me);
+ me.page = 9999;
+ break;
+ case 'H':
+ a_hideitem(&me);
+ me.page = 9999;
+ break;
+ case 'T':
+ a_newtitle(&me);
+ me.page = 9999;
+ break;
+ }
+ }
+
+ if(me.level == SYSOP) {
+ switch(ch) {
+ case 'l':
+ a_newitem(&me, ADDLINK);
+ me.page = 9999;
+ break;
+ case 'N':
+ a_showname(&me);
+ me.page = 9999;
+ break;
+ }
+ }
+ }
+ free(me.header);
+ return returnvalue;
+}
+
+static char *mytitle = BBSNAME "佈告欄";
+
+int Announce() {
+ setutmpmode(ANNOUNCE);
+ a_menu(mytitle, "man",
+ ((HAS_PERM(PERM_SYSOP) || HAS_PERM(PERM_ANNOUNCE)) ? SYSOP :
+ NOBODY));
+ return 0;
+}