summaryrefslogtreecommitdiff
path: root/chinese/pine4/files/patch-ar
diff options
context:
space:
mode:
Diffstat (limited to 'chinese/pine4/files/patch-ar')
-rw-r--r--chinese/pine4/files/patch-ar6672
1 files changed, 5414 insertions, 1258 deletions
diff --git a/chinese/pine4/files/patch-ar b/chinese/pine4/files/patch-ar
index d70d02915af1..60458238b332 100644
--- a/chinese/pine4/files/patch-ar
+++ b/chinese/pine4/files/patch-ar
@@ -1,1260 +1,5416 @@
---- pine/folder.c.orig Tue Jul 7 07:13:25 1998
-+++ pine/folder.c Wed Jul 15 17:02:32 1998
-@@ -63,13 +63,13 @@
- ((X)->dir->status&CNTXT_PARTFIND) == 0)
- #define FLDR_NAME(X) ((X) ? ((X)->nickname ? (X)->nickname : (X)->name) :"")
- #define SUBSCRIBE_PMT \
-- "Enter newsgroup name (or partial name to get a list): "
--#define LISTMODE_GRIPE "Use \"X\" to mark selections in list mode"
--#define SEL_ALTER_PMT "ALTER folder selection : "
--#define SEL_TEXT_PMT "Select by folder Name or Contents ? "
--#define SEL_PROP_PMT "Select by which folder property ? "
-+ "輸入新聞組群名稱(或部份名稱以取得列表):"
-+#define LISTMODE_GRIPE "以 \"X\" 來在列表模式中標示選擇"
-+#define SEL_ALTER_PMT "更改資料匣的選擇:"
-+#define SEL_TEXT_PMT "根據資料匣名稱或內容選擇?"
-+#define SEL_PROP_PMT "根據哪一個資料匣性質?"
- #define DIR_FOLD_PMT \
-- "Folder by the same name *MAY* get deleted as well. Continue"
-+ "同名稱的資料匣 *可能* 被刪除。繼續"
-
- #define mail_list(S, R, N) mail_list_internal(S, R, N)
-
-@@ -270,18 +270,18 @@
- /*
- * Various screen keymenu/command binding s.
- */
--#define PREVC_MENU {"P", "PrevCltn", {MC_PREVITEM, 1, {'p'}}, KS_NONE}
--#define NEXTC_MENU {"N", "NextCltn", {MC_NEXTITEM, 2, {'n',TAB}}, KS_NONE}
--#define DELC_MENU {"D", "Del Cltn", {MC_DELETE,2,{'d',KEY_DEL}}, KS_NONE}
--#define PREVF_MENU {"P", "PrevFldr", {MC_PREVITEM, 1, {'p'}}, KS_NONE}
--#define NEXTF_MENU {"N", "NextFldr", {MC_NEXTITEM, 2, {'n',TAB}}, KS_NONE}
--#define CIND_MENU {"I", "CurIndex", {MC_INDEX,1,{'i'}}, KS_FLDRINDEX}
-+#define PREVC_MENU {"P", "前一總集", {MC_PREVITEM, 1, {'p'}}, KS_NONE}
-+#define NEXTC_MENU {"N", "次一總集", {MC_NEXTITEM, 2, {'n',TAB}}, KS_NONE}
-+#define DELC_MENU {"D", "刪除總集", {MC_DELETE,2,{'d',KEY_DEL}}, KS_NONE}
-+#define PREVF_MENU {"P", "前一資料匣", {MC_PREVITEM, 1, {'p'}}, KS_NONE}
-+#define NEXTF_MENU {"N", "次一資料匣", {MC_NEXTITEM, 2, {'n',TAB}}, KS_NONE}
-+#define CIND_MENU {"I", "索引", {MC_INDEX,1,{'i'}}, KS_FLDRINDEX}
-
- static struct key context_mgr_keys[] =
- {HELP_MENU,
- OTHER_MENU,
-- {"<", "Main Menu", {MC_MAIN,3,{'m','<',','}}, KS_EXITMODE},
-- {">", "[View Cltn]",
-+ {"<", "主選單", {MC_MAIN,3,{'m','<',','}}, KS_EXITMODE},
-+ {">", "[檢視總集]",
- {MC_CHOICE,5,{'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
- PREVC_MENU,
- NEXTC_MENU,
-@@ -313,15 +313,15 @@
- static struct key context_cfg_keys[] =
- {HELP_MENU,
- OTHER_MENU,
-- {"E", "Exit Setup", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-- {"C", "[Change]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
-+ {"E", "離開設定", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-+ {"C", "[修改]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
- PREVC_MENU,
- NEXTC_MENU,
- PREVPAGE_MENU,
- NEXTPAGE_MENU,
-- {"A", "Add Cltn", {MC_ADD,1,{'a'}}, KS_NONE},
-+ {"A", "新增總集", {MC_ADD,1,{'a'}}, KS_NONE},
- DELC_MENU,
-- {"$", "Shuffle", {MC_SHUFFLE,1,{'$'}},KS_NONE},
-+ {"$", "重整", {MC_SHUFFLE,1,{'$'}},KS_NONE},
- WHEREIS_MENU,
-
- HELP_MENU,
-@@ -340,9 +340,9 @@
-
- static struct key context_select_keys[] =
- {HELP_MENU,
-- {"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-+ {"E", "離開", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
- NULL_MENU,
-- {">", "[View Cltn]",
-+ {">", "[檢視總集]",
- {MC_CHOICE, 5, {'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
- PREVC_MENU,
- NEXTC_MENU,
-@@ -356,9 +356,9 @@
-
- static struct key context_fcc_keys[] =
- {HELP_MENU,
-- {"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-+ {"E", "離開", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
- NULL_MENU,
-- {">", "[View Cltn]",
-+ {">", "[檢視總集]",
- {MC_CHOICE, 5, {'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
- PREVC_MENU,
- NEXTC_MENU,
-@@ -373,16 +373,16 @@
- static struct key folder_keys[] =
- {HELP_MENU,
- OTHER_MENU,
-- {"<", NULL, {MC_EXIT,3,{' ','<',','}}, KS_NONE},
-- {">", "[View Fldr]",
-+ {"M", NULL, {MC_EXIT,3,{' ','<',','}}, KS_NONE},
-+ {">", "[檢視檔案匣]",
- {MC_CHOICE,5,{'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
- PREVF_MENU,
- NEXTF_MENU,
- PREVPAGE_MENU,
- NEXTPAGE_MENU,
-- {"A","Add",{MC_ADDFLDR,1,{'a'}},KS_NONE},
-+ {"A","新增",{MC_ADDFLDR,1,{'a'}},KS_NONE},
- DELETE_MENU,
-- {"R","Rename",{MC_RENAMEFLDR,1,{'r'}}, KS_NONE},
-+ {"R","更名",{MC_RENAMEFLDR,1,{'r'}}, KS_NONE},
- WHEREIS_MENU,
-
- HELP_MENU,
-@@ -394,9 +394,9 @@
- CIND_MENU,
- COMPOSE_MENU,
- PRYNTTXT_MENU,
-- {"Z", "ZoomMode", {MC_ZOOM,1,{'z'}}, KS_NONE},
-- {";","Select",{MC_SELECT,1,{';'}},KS_SELECT},
-- {":","SelectCur",{MC_SELCUR,1,{':'}},KS_SELECT}};
-+ {"Z", "縮放模式", {MC_ZOOM,1,{'z'}}, KS_NONE},
-+ {";","選擇",{MC_SELECT,1,{';'}},KS_SELECT},
-+ {":","選擇目前的",{MC_SELCUR,1,{':'}},KS_SELECT}};
- INST_KEY_MENU(folder_km, folder_keys);
- #define KM_COL_KEY 2
- #define KM_SEL_KEY 3
-@@ -408,9 +408,9 @@
-
- static struct key folder_sel_keys[] =
- {HELP_MENU,
-- {"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-- {"<", "Collections", {MC_COLLECTIONS,2,{'<',','}}, KS_NONE},
-- {"S", "[Select]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}},KS_NONE},
-+ {"E", "離開", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-+ {"<", "總集", {MC_COLLECTIONS,2,{'<',','}}, KS_NONE},
-+ {"S", "[選擇]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}},KS_NONE},
- PREVF_MENU,
- NEXTF_MENU,
- PREVPAGE_MENU,
-@@ -424,9 +424,9 @@
-
- static struct key folder_sub_sel_keys[] =
- {HELP_MENU,
-- {"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-+ {"E", "離開", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
- NULL_MENU,
-- {"S", "[Select]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}},KS_NONE},
-+ {"S", "[選擇]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}},KS_NONE},
- PREVF_MENU,
- NEXTF_MENU,
- PREVPAGE_MENU,
-@@ -440,9 +440,9 @@
-
- static struct key folder_fcc_keys[] =
- {HELP_MENU,
-- {"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-- {"<", "Collections", {MC_COLLECTIONS,2,{'<',','}}, KS_NONE},
-- {"S", "[Select]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}},
-+ {"E", "離開", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-+ {"<", "總集", {MC_COLLECTIONS,2,{'<',','}}, KS_NONE},
-+ {"S", "[選擇]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}},
- KS_NONE},
- PREVF_MENU,
- NEXTF_MENU,
-@@ -458,9 +458,9 @@
-
- static struct key folder_sub_keys[] =
- {HELP_MENU,
-- {"S", "Subscribe", {MC_CHOICE,1,{'s'}}, KS_NONE},
-- {"E", "ExitSubscb", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-- {NULL, "[Select]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
-+ {"S", "訂閱\", {MC_CHOICE,1,{'s'}}, KS_NONE},
-+ {"E", "離開", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-+ {NULL, "[選擇]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
- PREVF_MENU,
- NEXTF_MENU,
- PREVPAGE_MENU,
-@@ -477,8 +477,8 @@
- static struct key folder_post_keys[] =
- {HELP_MENU,
- NULL_MENU,
-- {"E", "ExitSelect", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-- {"S", "[Select]", {MC_CHOICE, 3, {'s',ctrl('M'),ctrl('J')}}, KS_NONE},
-+ {"E", "離開", {MC_EXIT,1,{'e'}}, KS_EXITMODE},
-+ {"S", "[選擇]", {MC_CHOICE, 3, {'s',ctrl('M'),ctrl('J')}}, KS_NONE},
- PREVF_MENU,
- NEXTF_MENU,
- PREVPAGE_MENU,
-@@ -573,12 +573,12 @@
- mailcap_free(); /* free resources we won't be using for a while */
-
- memset(&css, 0, sizeof(CONT_SCR_S));
-- css.title = "SETUP COLLECTION LIST";
-+ css.title = "設定總集列表";
- css.print_string = "contexts ";
- css.start = ps->context_current;
- css.contexts = &ps_global->context_list;
- css.help.text = h_collection_maint;
-- css.help.title = "HELP FOR SETUP COLLECTION";
-+ css.help.title = "設定總集的輔助說明";
- css.keymenu = &c_cfg_km;
- css.edit = 1;
-
-@@ -867,7 +867,7 @@
- /* leave (*new_dir)->ref == NULL */
- }
-
-- sprintf(tmp_20k_buf, "List of folders matching \"%s*\"", folder);
-+ sprintf(tmp_20k_buf, "符合 \"%s*\" 的資料匣列表", folder);
- (*new_dir)->desc = cpystr(tmp_20k_buf);
- }
-
-@@ -966,12 +966,12 @@
- CONT_SCR_S css;
-
- memset(&css, 0, sizeof(CONT_SCR_S));
-- css.title = "COLLECTION LIST";
-+ css.title = "總集列表";
- css.print_string = "contexts ";
- css.start = start;
- css.contexts = &ps_global->context_list;
- css.help.text = h_collection_screen;
-- css.help.title = "HELP FOR COLLECTION LIST";
-+ css.help.title = "總集列表的輔助說明";
- css.keymenu = km;
- css.edit = edit_config;
-
-@@ -1042,7 +1042,7 @@
- pbuf.browse_help = h_composer_browse;
- pbuf.attach_help = h_composer_ctrl_j;
- pbuf.composer_help = h_composer;
-- sprintf(tmp, "FOLDER COLLECTION %s", func);
-+ sprintf(tmp, "資料匣總集 %s", func);
- pbuf.pine_anchor = set_titlebar(tmp, ps_global->mail_stream,
- ps_global->context_current,
- ps_global->cur_folder,ps_global->msgmap,
-@@ -1278,10 +1278,10 @@
- else
- exists = (i & FEX_ISDIR);
-
-- sprintf(prompt, "Exit%s" ,
-+ sprintf(prompt, "離開%s" ,
- exists
-- ? " and save changes"
-- : ", saving changes and creating Path");
-+ ? " 並存檔"
-+ : ",存檔並建立路徑");
- if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'y'){
- if(!exists && !mail_create(NULL, tmp)){
- flush_status_messages(1); /* mail_create gripes */
-@@ -1313,7 +1313,7 @@
- char *rstr = NULL;
- void (*redraw)() = ps_global->redrawer;
- #define CCA_PROMPT \
-- "Cancel Add (answering \"Yes\" will abandon any changes made) "
-+ "取消新增 (回答 \"Yes\" 將放棄先前做過的任何改變) "
-
- ps_global->redrawer = redraw_pico;
- fix_windsize(ps_global);
-@@ -1430,7 +1430,7 @@
- /*BUG: test writing with NNTP to misc.test via mark's code. reasonable err msg?*/
- if(NEWS_TEST(fs->context)) {
- q_status_message(SM_ORDER | SM_DING, 3, 4,
-- "Can't save messages to bulletin boards or news groups!");
-+ "無法將訊息存至電子佈告欄或新聞組群上!");
- return(0);
- }
- #endif
-@@ -1445,7 +1445,7 @@
- FSTATE_S *fs;
- {
- if(!strncmp(f->prefix, "SUB", 3)){
-- q_status_message1(SM_ORDER, 0, 4, "Already subscribed to \"%s\"",
-+ q_status_message1(SM_ORDER, 0, 4, "已訂閱\至 \"%s\"",
- FLDR_NAME(f));
- return(0);
- }
-@@ -1458,7 +1458,7 @@
- fl_hdr_gen(ps)
- struct pine *ps;
- {
-- set_titlebar("FOLDER LIST", ps->mail_stream, ps->context_current,
-+ set_titlebar("信件匣列表", ps->mail_stream, ps->context_current,
- ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0);
- }
-
-@@ -1573,7 +1573,7 @@
- {
- int ch, cmd, mangled_footer, mangled_header,
- n, rc, cur_row, cur_col, km_size, was_dir = -1,
-- km_popped = 0, listmode = 0, done = 0;
-+ km_popped = 0, listmode = 0, done = 0, exit_to_main = 0;
- unsigned short new_col;
- FOLDER_S *cur_f = NULL;
- STRINGLIST *sl = NULL;
-@@ -1676,7 +1676,7 @@
- }
- else{
- clrbitn(KM_MAIN_KEY, bitmap);
-- km.keys[KM_COL_KEY].label = "Main Menu";
-+ km.keys[KM_COL_KEY].label = "主選單";
- km.keys[KM_COL_KEY].bind.cmd = MC_MAIN;
- km.keys[KM_COL_KEY].bind.ch[0] = 'm';
- }
-@@ -1696,14 +1696,14 @@
- if(listmode){
- clrbitn(SB_LIST_KEY, bitmap);
- km.keys[SB_SEL_KEY].name = "X";
-- km.keys[SB_SEL_KEY].label = "[Set/Unset]";
-+ km.keys[SB_SEL_KEY].label = "[設定/取消設定]";
- km.keys[SB_SEL_KEY].bind.cmd = MC_SELCUR;
- km.keys[SB_SEL_KEY].bind.ch[0] = 'x';
- }
- else{
- clrbitn(SB_SUB_KEY, bitmap);
- km.keys[SB_SEL_KEY].name = "S";
-- km.keys[SB_SEL_KEY].label = "[Subscribe]";
-+ km.keys[SB_SEL_KEY].label = "[訂閱\]";
- km.keys[SB_SEL_KEY].bind.cmd = MC_CHOICE;
- km.keys[SB_SEL_KEY].bind.ch[0] = 's';
- }
-@@ -1717,7 +1717,7 @@
- }
-
- if(cur_f && cur_f->isdir){
-- static struct key sel_key = {">", "[View Dir]",
-+ static struct key sel_key = {">", "[檢視目錄]",
- {MC_CHOICE,5,
- {'s','>','.',
- ctrl('M'),ctrl('J')}},
-@@ -1843,8 +1843,15 @@
- /*---------------------- Key left --------------*/
- case MC_CHARLEFT :
- case MC_PREVITEM :
-+ if (exit_to_main)
-+ {
-+ ps_global->next_screen = main_menu_screen;
-+ done++;
+--- pine/osdep/os-bsf.c.orig Tue Jul 28 08:35:04 1998
++++ pine/osdep/os-bsf.c Tue Jul 28 08:35:05 1998
+@@ -0,0 +1,5413 @@
++/*----------------------------------------------------------------------
++
++ T H E P I N E M A I L S Y S T E M
++
++ Laurence Lundblade and Mike Seibel
++ Networks and Distributed Computing
++ Computing and Communications
++ University of Washington
++ Administration Builiding, AG-44
++ Seattle, Washington, 98195, USA
++ Internet: lgl@CAC.Washington.EDU
++ mikes@CAC.Washington.EDU
++
++ Please address all bugs and comments to "pine-bugs@cac.washington.edu"
++
++
++ Pine and Pico are registered trademarks of the University of Washington.
++ No commercial use of these trademarks may be made without prior written
++ permission of the University of Washington.
++
++ Pine, Pico, and Pilot software and its included text are Copyright
++ 1989-1998 by the University of Washington.
++
++ The full text of our legal notices is contained in the file called
++ CPYRIGHT, included with this distribution.
++
++
++ Pine is in part based on The Elm Mail System:
++ ***********************************************************************
++ * The Elm Mail System - Revision: 2.13 *
++ * *
++ * Copyright (c) 1986, 1987 Dave Taylor *
++ * Copyright (c) 1988, 1989 USENET Community Trust *
++ ***********************************************************************
++
++
++ ----------------------------------------------------------------------*/
++
++/*======================================================================
++
++ This contains most of Pine's interface to the local operating system
++and hardware. Hopefully this file, os-xxx.h and makefile.xxx are the
++only ones that have to be modified for most ports. Signals.c, ttyin.c,
++and ttyout.c also have some dependencies. See the doc/tech-notes for
++notes on porting Pine to other platforms. Here is a list of the functions
++required for an implementation:
++
++
++ File System Access
++ can_access -- See if a file can be accessed
++ name_file_size -- Return the number of bytes in the file (by name)
++ fp_file_size -- Return the number of bytes in the file (by FILE *)
++ name_file_mtime -- Return the mtime of a file (by name)
++ fp_file_mtime -- Return the mtime of a file (by FILE *)
++ file_attrib_copy -- Copy attributes of one file to another.
++ is_writable_dir -- Check to see if directory exists and is writable
++ create_mail_dir -- Make a directory
++ rename_file -- change name of a file
++ build_path -- Put together a file system path
++ last_cmpnt -- Returns pointer to last component of path
++ expand_foldername -- Expand a folder name to full path
++ fnexpand -- Do filename exansion for csh style "~"
++ filter_filename -- Make sure file name hasn't got weird chars
++ cntxt_allowed -- Check whether a pathname is allowed for read/write
++ disk_quota -- Check the user's disk quota
++ read_file -- Read whole file into memory (for small files)
++ create_tmpfile -- Just like ANSI C tmpfile function
++ temp_nam -- Almost like common tempnam function
++ fget_pos,fset_pos -- Just like ANSI C fgetpos, fsetpos functions
++
++ Abort
++ coredump -- Abort running Pine dumping core if possible
++
++ System Name and Domain
++ hostname -- Figure out the system's host name, only
++ used internally in this file.
++ getdomainnames -- Figure out the system's domain name
++ canonical_name -- Returns canonical form of host name
++
++ Job Control
++ have_job_control -- Returns 1 if job control exists
++ stop_process -- What to do to stop process when it's time to stop
++ (only used if have_job_control returns 1)
++
++ System Error Messages (in case given one is a problem)
++ error_description -- Returns string describing error
++
++ System Password and Accounts
++ gcos_name -- Parses full name from system, only used
++ locally in this file so if you don't use it you
++ don't need it
++ get_user_info -- Finds in login name, full name, and homedir
++ local_name_lookup -- Get full name of user on system
++ change_passwd -- Calls system password changer
++
++ MIME utilities
++ mime_can_display -- Can we display this type/subtype?
++ exec_mailcap_cmd -- Run the mailcap command to view a type/subtype.
++ exec_mailcap_test_cmd -- Run mailcap test= test command.
++
++ Other stuff
++ srandom -- Dummy srandom if you don't have this function
++ init_debug
++ do_debug
++ save_debug_on_crash
++
++ ====*/
++
++
++#include "headers.h"
++
++
++
++/*----------------------------------------------------------------------
++ Check if we can access a file in a given way
++
++ Args: file -- The file to check
++ mode -- The mode ala the access() system call, see ACCESS_EXISTS
++ and friends in pine.h.
++
++ Result: returns 0 if the user can access the file according to the mode,
++ -1 if he can't (and errno is set).
++ ----*/
++int
++can_access(file, mode)
++ char *file;
++ int mode;
++{
++ return(access(file, mode));
++}
++
++
++/*----------------------------------------------------------------------
++ Check if we can access a file in a given way in the given path
++
++ Args: path -- The path to look for "file" in
++ file -- The file to check
++ mode -- The mode ala the access() system call, see ACCESS_EXISTS
++ and friends in pine.h.
++
++ Result: returns 0 if the user can access the file according to the mode,
++ -1 if he can't (and errno is set).
++ ----*/
++can_access_in_path(path, file, mode)
++ char *path, *file;
++ int mode;
++{
++ char tmp[MAXPATH], *path_copy, *p, *t;
++ int rv = -1;
++
++ if(!path || !*path || *file == '/'){
++ rv = access(file, mode);
++ }
++ else if(*file == '~'){
++ strcpy(tmp, file);
++ rv = fnexpand(tmp, sizeof(tmp)) ? access(tmp, mode) : -1;
++ }
++ else{
++ for(p = path_copy = cpystr(path); p && *p; p = t){
++ if(t = strindex(p, ':'))
++ *t++ = '\0';
++
++ sprintf(tmp, "%s/%s", p, file);
++ if((rv = access(tmp, mode)) == 0)
++ break;
++ }
++
++ fs_give((void **)&path_copy);
++ }
++
++ return(rv);
++}
++
++/*----------------------------------------------------------------------
++ Return the number of bytes in given file
++
++ Args: file -- file name
++
++ Result: the number of bytes in the file is returned or
++ -1 on error, in which case errno is valid
++ ----*/
++long
++name_file_size(file)
++ char *file;
++{
++ struct stat buffer;
++
++ if(stat(file, &buffer) != 0)
++ return(-1L);
++
++ return((long)buffer.st_size);
++}
++
++
++/*----------------------------------------------------------------------
++ Return the number of bytes in given file
++
++ Args: fp -- FILE * for open file
++
++ Result: the number of bytes in the file is returned or
++ -1 on error, in which case errno is valid
++ ----*/
++long
++fp_file_size(fp)
++ FILE *fp;
++{
++ struct stat buffer;
++
++ if(fstat(fileno(fp), &buffer) != 0)
++ return(-1L);
++
++ return((long)buffer.st_size);
++}
++
++
++/*----------------------------------------------------------------------
++ Return the modification time of given file
++
++ Args: file -- file name
++
++ Result: the time of last modification (mtime) of the file is returned or
++ -1 on error, in which case errno is valid
++ ----*/
++time_t
++name_file_mtime(file)
++ char *file;
++{
++ struct stat buffer;
++
++ if(stat(file, &buffer) != 0)
++ return((time_t)(-1));
++
++ return(buffer.st_mtime);
++}
++
++
++/*----------------------------------------------------------------------
++ Return the modification time of given file
++
++ Args: fp -- FILE * for open file
++
++ Result: the time of last modification (mtime) of the file is returned or
++ -1 on error, in which case errno is valid
++ ----*/
++time_t
++fp_file_mtime(fp)
++ FILE *fp;
++{
++ struct stat buffer;
++
++ if(fstat(fileno(fp), &buffer) != 0)
++ return((time_t)(-1));
++
++ return(buffer.st_mtime);
++}
++
++
++/*----------------------------------------------------------------------
++ Copy the mode, owner, and group of sourcefile to targetfile.
++
++ Args: targetfile --
++ sourcefile --
++
++ We don't bother keeping track of success or failure because we don't care.
++ ----*/
++void
++file_attrib_copy(targetfile, sourcefile)
++ char *targetfile;
++ char *sourcefile;
++{
++ struct stat buffer;
++
++ if(stat(sourcefile, &buffer) == 0){
++ chmod(targetfile, buffer.st_mode);
++#if !defined(DOS) && !defined(OS2)
++ chown(targetfile, buffer.st_uid, buffer.st_gid);
++#endif
++ }
++}
++
++
++
++/*----------------------------------------------------------------------
++ Check to see if a directory exists and is writable by us
++
++ Args: dir -- directory name
++
++ Result: returns 0 if it exists and is writable
++ 1 if it is a directory, but is not writable
++ 2 if it is not a directory
++ 3 it doesn't exist.
++ ----*/
++is_writable_dir(dir)
++ char *dir;
++{
++ struct stat sb;
++
++ if(stat(dir, &sb) < 0)
++ /*--- It doesn't exist ---*/
++ return(3);
++
++ if(!(sb.st_mode & S_IFDIR))
++ /*---- it's not a directory ---*/
++ return(2);
++
++ if(can_access(dir, 07))
++ return(1);
++ else
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Create the mail subdirectory.
++
++ Args: dir -- Name of the directory to create
++
++ Result: Directory is created. Returns 0 on success, else -1 on error
++ and errno is valid.
++ ----*/
++create_mail_dir(dir)
++ char *dir;
++{
++ if(mkdir(dir, 0700) < 0)
++ return(-1);
++
++ (void)chmod(dir, 0700);
++ /* Some systems need this, on others we don't care if it fails */
++ (void)chown(dir, getuid(), getgid());
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Rename a file
++
++ Args: tmpfname -- Old name of file
++ fname -- New name of file
++
++ Result: File is renamed. Returns 0 on success, else -1 on error
++ and errno is valid.
++ ----*/
++rename_file(tmpfname, fname)
++ char *tmpfname, *fname;
++{
++ return(rename(tmpfname, fname));
++}
++
++
++
++/*----------------------------------------------------------------------
++ Paste together two pieces of a file name path
++
++ Args: pathbuf -- Put the result here
++ first_part -- of path name
++ second_part -- of path name
++
++ Result: New path is in pathbuf. No check is made for overflow. Note that
++ we don't have to check for /'s at end of first_part and beginning
++ of second_part since multiple slashes are ok.
++
++BUGS: This is a first stab at dealing with fs naming dependencies, and others
++still exist.
++ ----*/
++void
++build_path(pathbuf, first_part, second_part)
++ char *pathbuf, *first_part, *second_part;
++{
++ if(!first_part)
++ strcpy(pathbuf, second_part);
++ else
++ sprintf(pathbuf, "%s%s%s", first_part,
++ (*first_part && first_part[strlen(first_part)-1] != '/')
++ ? "/" : "",
++ second_part);
++}
++
++
++/*----------------------------------------------------------------------
++ Test to see if the given file path is absolute
++
++ Args: file -- file path to test
++
++ Result: TRUE if absolute, FALSE otw
++
++ ----*/
++int
++is_absolute_path(path)
++ char *path;
++{
++ return(path && (*path == '/' || *path == '~'));
++}
++
++
++
++/*----------------------------------------------------------------------
++ Return pointer to last component of pathname.
++
++ Args: filename -- The pathname.
++
++ Result: Returned pointer points to last component in the input argument.
++ ----*/
++char *
++last_cmpnt(filename)
++ char *filename;
++{
++ register char *p = NULL, *q = filename;
++
++ while(q = strchr(q, '/'))
++ if(*++q)
++ p = q;
++
++ return(p);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Expand a folder name, taking account of the folders_dir and `~'.
++
++ Args: filename -- The name of the file that is the folder
++
++ Result: The folder name is expanded in place.
++ Returns 0 and queues status message if unsuccessful.
++ Input string is overwritten with expanded name.
++ Returns 1 if successful.
++
++BUG should limit length to MAXPATH
++ ----*/
++int
++expand_foldername(filename)
++ char *filename;
++{
++ char temp_filename[MAXPATH+1];
++
++ dprint(5, (debugfile, "=== expand_foldername called (%s) ===\n",filename));
++
++ /*
++ * We used to check for valid filename chars here if "filename"
++ * didn't refer to a remote mailbox. This has been rethought
++ */
++
++ strcpy(temp_filename, filename);
++ if(strucmp(temp_filename, "inbox") == 0) {
++ strcpy(filename, ps_global->VAR_INBOX_PATH == NULL ? "inbox" :
++ ps_global->VAR_INBOX_PATH);
++ } else if(temp_filename[0] == '{') {
++ strcpy(filename, temp_filename);
++ } else if(ps_global->restricted
++ && (strindex("./~", temp_filename[0]) != NULL
++ || srchstr(temp_filename,"/../"))){
++ q_status_message(SM_ORDER, 0, 3, "僅能開啟本地的檔案匣");
++ return(0);
++ } else if(temp_filename[0] == '*') {
++ strcpy(filename, temp_filename);
++ } else if(ps_global->VAR_OPER_DIR && srchstr(temp_filename,"..")){
++ q_status_message(SM_ORDER, 0, 3,
++ "檔案匣名稱中不允許\有 \"..\"");
++ return(0);
++ } else if (temp_filename[0] == '~'){
++ if(fnexpand(temp_filename, sizeof(temp_filename)) == NULL) {
++ char *p = strindex(temp_filename, '/');
++ if(p != NULL)
++ *p = '\0';
++ q_status_message1(SM_ORDER, 3, 3,
++ "檔案匣展開錯誤:\"%s\" 未知的使用者",
++ temp_filename);
++ return(0);
++ }
++ strcpy(filename, temp_filename);
++ } else if(temp_filename[0] == '/') {
++ strcpy(filename, temp_filename);
++ } else if(F_ON(F_USE_CURRENT_DIR, ps_global)){
++ strcpy(filename, temp_filename);
++ } else if(ps_global->VAR_OPER_DIR){
++ build_path(filename, ps_global->VAR_OPER_DIR, temp_filename);
++ } else {
++ build_path(filename, ps_global->home_dir, temp_filename);
++ }
++ dprint(5, (debugfile, "returning \"%s\"\n", filename));
++ return(1);
++}
++
++
++
++struct passwd *getpwnam();
++
++/*----------------------------------------------------------------------
++ Expand the ~ in a file ala the csh (as home directory)
++
++ Args: buf -- The filename to expand (nothing happens unless begins with ~)
++ len -- The length of the buffer passed in (expansion is in place)
++
++ Result: Expanded string is returned using same storage as passed in.
++ If expansion fails, NULL is returned
++ ----*/
++char *
++fnexpand(buf, len)
++ char *buf;
++ int len;
++{
++ struct passwd *pw;
++ register char *x,*y;
++ char name[20];
++
++ if(*buf == '~') {
++ for(x = buf+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
++ *y = '\0';
++ if(x == buf + 1)
++ pw = getpwuid(getuid());
++ else
++ pw = getpwnam(name);
++ if(pw == NULL)
++ return((char *)NULL);
++ if(strlen(pw->pw_dir) + strlen(buf) > len) {
++ return((char *)NULL);
++ }
++ rplstr(buf, x - buf, pw->pw_dir);
++ }
++ return(len ? buf : (char *)NULL);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Filter file names for strange characters
++
++ Args: file -- the file name to check
++
++ Result: Returns NULL if file name is OK
++ Returns formatted error message if it is not
++ ----*/
++char *
++filter_filename(file)
++ char *file;
++{
++#ifdef ALLOW_WEIRD
++ static char illegal[] = {'\177', '\0'};
++#else
++ static char illegal[] = {'"', '#', '$', '%', '&', '\'','(', ')','*',
++ ',', ':', ';', '<', '=', '>', '?', '[', ']',
++ '\\', '^', '|', '\177', '\0'};
++#endif
++ static char error[100];
++ char ill_file[MAXPATH+1], *ill_char, *ptr, e2[10];
++ int i;
++
++ for(ptr = file; *ptr == ' '; ptr++) ; /* leading spaces gone */
++
++ while(*ptr && (unsigned char)(*ptr) > ' ' && strindex(illegal, *ptr) == 0)
++ ptr++;
++
++ if(*ptr != '\0') {
++ if(*ptr == ' ') {
++ ill_char = "<space>";
++ } else if(*ptr == '\n') {
++ ill_char = "<newline>";
++ } else if(*ptr == '\r') {
++ ill_char = "<carriage return>";
++ } else if(*ptr == '\t') {
++ ill_char = "<tab>";
++ } else if(*ptr < ' ') {
++ sprintf(e2, "control-%c", *ptr + '@');
++ ill_char = e2;
++ } else if (*ptr == '\177') {
++ ill_char = "<del>";
++ } else {
++ e2[0] = *ptr;
++ e2[1] = '\0';
++ ill_char = e2;
++ }
++ if(ptr != file) {
++ strncpy(ill_file, file, ptr - file);
++ ill_file[ptr - file] = '\0';
++ sprintf(error,
++ "Character \"%s\" after \"%s\" not allowed in file name",
++ ill_char, ill_file);
++ } else {
++ sprintf(error,
++ "First character, \"%s\", not allowed in file name",
++ ill_char);
++ }
++
++ return(error);
++ }
++
++ if((i=is_writable_dir(file)) == 0 || i == 1){
++ sprintf(error, "\"%s\" is a directory", file);
++ return(error);
++ }
++
++ if(ps_global->restricted || ps_global->VAR_OPER_DIR){
++ for(ptr = file; *ptr == ' '; ptr++) ; /* leading spaces gone */
++
++ if((ptr[0] == '.' && ptr[1] == '.') || srchstr(ptr, "/../")){
++ sprintf(error, "\"..\" not allowed in filename");
++ return(error);
++ }
++ }
++
++ return((char *)NULL);
++}
++
++
++/*----------------------------------------------------------------------
++ Check to see if user is allowed to read or write this folder.
++
++ Args: s -- the name to check
++
++ Result: Returns 1 if OK
++ Returns 0 and posts an error message if access is denied
++ ----*/
++int
++cntxt_allowed(s)
++ char *s;
++{
++ struct variable *vars = ps_global->vars;
++ int retval = 1;
++ MAILSTREAM stream; /* fake stream for error message in mm_notify */
++
++ if(ps_global->restricted
++ && (strindex("./~", s[0]) || srchstr(s, "/../"))){
++ stream.mailbox = s;
++ mm_notify(&stream, "Restricted mode doesn't allow operation", WARN);
++ retval = 0;
++ }
++ else if(VAR_OPER_DIR
++ && s[0] != '{' && !(s[0] == '*' && s[1] == '{')
++ && strucmp(s,ps_global->inbox_name) != 0
++ && strcmp(s, ps_global->VAR_INBOX_PATH) != 0){
++ char *p, *free_this = NULL;
++
++ p = s;
++ if(strindex(s, '~')){
++ p = strindex(s, '~');
++ free_this = (char *)fs_get(strlen(p) + 200);
++ strcpy(free_this, p);
++ fnexpand(free_this, strlen(p)+200);
++ p = free_this;
++ }
++ else if(p[0] != '/'){ /* add home dir to relative paths */
++ free_this = p = (char *)fs_get(strlen(s)
++ + strlen(ps_global->home_dir) + 2);
++ build_path(p, ps_global->home_dir, s);
++ }
++
++ if(!in_dir(VAR_OPER_DIR, p)){
++ char err[200];
++
++ sprintf(err, "Not allowed outside of %s", VAR_OPER_DIR);
++ stream.mailbox = p;
++ mm_notify(&stream, err, WARN);
++ retval = 0;
++ }
++ else if(srchstr(p, "/../")){ /* check for .. in path */
++ stream.mailbox = p;
++ mm_notify(&stream, "\"..\" not allowed in name", WARN);
++ retval = 0;
++ }
++
++ if(free_this)
++ fs_give((void **)&free_this);
++ }
++
++ return retval;
++}
++
++
++
++#if defined(USE_QUOTAS)
++
++/*----------------------------------------------------------------------
++ This system doesn't have disk quotas.
++ Return space left in disk quota on file system which given path is in.
++
++ Args: path - Path name of file or directory on file system of concern
++ over - pointer to flag that is set if the user is over quota
++
++ Returns: If *over = 0, the number of bytes free in disk quota as per
++ the soft limit.
++ If *over = 1, the number of bytes *over* quota.
++ -1 is returned on an error looking up quota
++ 0 is returned if there is no quota
++
++BUG: If there's more than 2.1Gb free this function will break
++ ----*/
++long
++disk_quota(path, over)
++ char *path;
++ int *over;
++{
++ return(0L);
++}
++#endif /* USE_QUOTAS */
++
++
++
++/*----------------------------------------------------------------------
++ Read whole file into memory
++
++ Args: filename -- path name of file to read
++
++ Result: Returns pointer to malloced memory with the contents of the file
++ or NULL
++
++This won't work very well if the file has NULLs in it and is mostly
++intended for fairly small text files.
++ ----*/
++char *
++read_file(filename)
++ char *filename;
++{
++ int fd;
++ struct stat statbuf;
++ char *buf;
++ int nb;
++
++ fd = open(filename, O_RDONLY);
++ if(fd < 0)
++ return((char *)NULL);
++
++ fstat(fd, &statbuf);
++
++ buf = fs_get((size_t)statbuf.st_size + 1);
++
++ /*
++ * On some systems might have to loop here, if one read isn't guaranteed
++ * to get the whole thing.
++ */
++ if((nb = read(fd, buf, (int)statbuf.st_size)) < 0)
++ fs_give((void **)&buf); /* NULL's buf */
++ else
++ buf[nb] = '\0';
++
++ close(fd);
++ return(buf);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Create a temporary file, the name of which we don't care about
++and that goes away when it is closed. Just like ANSI C tmpfile.
++ ----*/
++FILE *
++create_tmpfile()
++{
++ return(tmpfile());
++}
++
++
++
++/*----------------------------------------------------------------------
++ Abort with a core dump
++ ----*/
++void
++coredump()
++{
++ abort();
++}
++
++
++
++/*----------------------------------------------------------------------
++ Call system gethostname
++
++ Args: hostname -- buffer to return host name in
++ size -- Size of buffer hostname is to be returned in
++
++ Result: returns 0 if the hostname is correctly set,
++ -1 if not (and errno is set).
++ ----*/
++hostname(hostname,size)
++ char *hostname;
++ int size;
++{
++ return(gethostname(hostname, size));
++}
++
++
++
++/*----------------------------------------------------------------------
++ Get the current host and domain names
++
++ Args: hostname -- buffer to return the hostname in
++ hsize -- size of buffer above
++ domainname -- buffer to return domain name in
++ dsize -- size of buffer above
++
++ Result: The system host and domain names are returned. If the full host
++ name is akbar.cac.washington.edu then the domainname is
++ cac.washington.edu.
++
++On Internet connected hosts this look up uses /etc/hosts and DNS to
++figure all this out. On other less well connected machines some other
++file may be read. If there is no notion of a domain name the domain
++name may be left blank. On a PC where there really isn't a host name
++this should return blank strings. The .pinerc will take care of
++configuring the domain names. That is, this should only return the
++native system's idea of what the names are if the system has such
++a concept.
++ ----*/
++void
++getdomainnames(hostname, hsize, domainname, dsize)
++ char *hostname, *domainname;
++ int hsize, dsize;
++{
++ char *dn, hname[MAX_ADDRESS+1];
++ struct hostent *he;
++ char **alias;
++ char *maybe = NULL;
++
++ gethostname(hname, MAX_ADDRESS);
++ he = gethostbyname(hname);
++ hostname[0] = '\0';
++
++ if(he == NULL)
++ strncpy(hostname, hname, hsize-1);
++ else{
++ /*
++ * If no dot in hostname it may be the case that there
++ * is an alias which is really the fully-qualified
++ * hostname. This could happen if the administrator has
++ * (incorrectly) put the unqualified name first in the
++ * hosts file, for example. The problem with looking for
++ * an alias with a dot is that now we're guessing, since
++ * the aliases aren't supposed to be the official hostname.
++ * We'll compromise and only use an alias if the primary
++ * name has no dot and exactly one of the aliases has a
++ * dot.
++ */
++ strncpy(hostname, he->h_name, hsize-1);
++ if(strindex(hostname, '.') == NULL){ /* no dot in hostname */
++ for(alias = he->h_aliases; *alias; alias++){
++ if(strindex(*alias, '.') != NULL){ /* found one */
++ if(maybe){ /* oops, this is the second one */
++ maybe = NULL;
++ break;
++ }
++ else
++ maybe = *alias;
++ }
+ }
- if((n = folder_lister_prev(fs)) >= 0)
- fs->folder_index = n;
++
++ if(maybe)
++ strncpy(hostname, maybe, hsize-1);
++ }
++ }
++
++ hostname[hsize-1] = '\0';
++
++
++ if((dn = strindex(hostname, '.')) != NULL)
++ strncpy(domainname, dn+1, dsize-1);
++ else
++ strncpy(domainname, hostname, dsize-1);
++
++ domainname[dsize-1] = '\0';
++}
++
++
++
++/*----------------------------------------------------------------------
++ Return canonical form of host name ala c-client (UNIX version).
++
++ Args: host -- The host name
++
++ Result: Canonical form, or input argument (worst case)
++ ----*/
++char *
++canonical_name(host)
++ char *host;
++{
++ struct hostent *hent;
++ char hostname[MAILTMPLEN];
++ char tmp[MAILTMPLEN];
++ extern char *lcase();
++ /* domain literal is easy */
++ if (host[0] == '[' && host[(strlen (host))-1] == ']')
++ return host;
++
++ strcpy (hostname,host); /* UNIX requires lowercase */
++ /* lookup name, return canonical form */
++ return (hent = gethostbyname (lcase (strcpy (tmp,host)))) ?
++ hent->h_name : host;
++}
++
++
++
++/*----------------------------------------------------------------------
++ This routine returns 1 if job control is available. Note, thiis
++ could be some type of fake job control. It doesn't have to be
++ real BSD-style job control.
++ ----*/
++have_job_control()
++{
++ return 1;
++}
++
++
++/*----------------------------------------------------------------------
++ If we don't have job control, this routine is never called.
++ ----*/
++stop_process()
++{
++ SigType (*save_usr2) SIG_PROTO((int));
++
++ /*
++ * Since we can't respond to KOD while stopped, the process that sent
++ * the KOD is going to go read-only. Therefore, we can safely ignore
++ * any KODs that come in before we are ready to respond...
++ */
++ save_usr2 = signal(SIGUSR2, SIG_IGN);
++ kill(0, SIGSTOP);
++ (void)signal(SIGUSR2, save_usr2);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Return string describing the error
++
++ Args: errnumber -- The system error number (errno)
++
++ Result: long string describing the error is returned
++ ----*/
++char *
++error_description(errnumber)
++ int errnumber;
++{
++ static char buffer[50+1];
++
++ if(errnumber >= 0 && errnumber < sys_nerr)
++ sprintf(buffer, "%.*s", 50, sys_errlist[errnumber]);
++ else
++ sprintf(buffer, "Unknown error #%d", errnumber);
++
++ return ( (char *) buffer);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Pull the name out of the gcos field if we have that sort of /etc/passwd
++
++ Args: gcos_field -- The long name or GCOS field to be parsed
++ logname -- Replaces occurances of & with logname string
++
++ Result: returns pointer to buffer with name
++ ----*/
++static char *
++gcos_name(gcos_field, logname)
++ char *logname, *gcos_field;
++{
++ static char fullname[MAX_FULLNAME+1];
++ register char *fncp, *gcoscp, *lncp, *end;
++
++ /* full name is all chars up to first ',' (or whole gcos, if no ',') */
++ /* replace any & with logname in upper case */
++
++ for(fncp = fullname, gcoscp= gcos_field, end = fullname + MAX_FULLNAME - 1;
++ (*gcoscp != ',' && *gcoscp != '\0' && fncp != end);
++ gcoscp++) {
++
++ if(*gcoscp == '&') {
++ for(lncp = logname; *lncp; fncp++, lncp++)
++ *fncp = toupper((unsigned char)(*lncp));
++ } else {
++ *fncp++ = *gcoscp;
++ }
++ }
++
++ *fncp = '\0';
++ return(fullname);
++}
++
++
++/*----------------------------------------------------------------------
++ Fill in homedir, login, and fullname for the logged in user.
++ These are all pointers to static storage so need to be copied
++ in the caller.
++
++ Args: ui -- struct pointer to pass back answers
++
++ Result: fills in the fields
++ ----*/
++void
++get_user_info(ui)
++ struct user_info *ui;
++{
++ struct passwd *unix_pwd;
++
++ unix_pwd = getpwuid(getuid());
++ if(unix_pwd == NULL) {
++ ui->homedir = cpystr("");
++ ui->login = cpystr("");
++ ui->fullname = cpystr("");
++ }else {
++ ui->homedir = cpystr(unix_pwd->pw_dir);
++ ui->login = cpystr(unix_pwd->pw_name);
++ ui->fullname = cpystr(gcos_name(unix_pwd->pw_gecos, unix_pwd->pw_name));
++ }
++}
++
++
++/*----------------------------------------------------------------------
++ Look up a userid on the local system and return rfc822 address
++
++ Args: name -- possible login name on local system
++
++ Result: returns NULL or pointer to alloc'd string rfc822 address.
++ ----*/
++char *
++local_name_lookup(name)
++ char *name;
++{
++ struct passwd *pw = getpwnam(name);
++
++ if(pw == NULL)
++ return((char *)NULL);
++
++ return(cpystr(gcos_name(pw->pw_gecos, name)));
++}
++
++
++
++/*----------------------------------------------------------------------
++ Call the system to change the passwd
++
++It would be nice to talk to the passwd program via a pipe or ptty so the
++user interface could be consistent, but we can't count on the the prompts
++and responses from the passwd program to be regular so we just let the user
++type at the passwd program with some screen space, hope he doesn't scroll
++off the top and repaint when he's done.
++ ----*/
++change_passwd()
++{
++ char cmd_buf[100];
++
++ ClearLines(1, ps_global->ttyo->screen_rows - 1);
++
++ MoveCursor(5, 0);
++ fflush(stdout);
++
++ PineRaw(0);
++ strcpy(cmd_buf, PASSWD_PROG);
++ system(cmd_buf);
++ sleep(3);
++ PineRaw(1);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Can we display this type/subtype?
++
++ Args: type -- the MIME type to check
++ subtype -- the MIME subtype
++ params -- parameters
++ use_viewer -- tell caller he should run external viewer cmd to view
++
++ Result: Returns:
++
++ MCD_NONE if we can't display this type at all
++ MCD_INTERNAL if we can display it internally
++ MCD_EXTERNAL if it can be displayed via an external viewer
++
++ ----*/
++mime_can_display(type, subtype, params)
++ int type;
++ char *subtype;
++ PARAMETER *params;
++{
++ return((mailcap_can_display(type, subtype, params)
++ ? MCD_EXTERNAL : MCD_NONE)
++ | ((type == TYPETEXT || type == TYPEMESSAGE
++ || MIME_VCARD(type,subtype))
++ ? MCD_INTERNAL : MCD_NONE));
++}
++
++
++
++/*----------------------------------------------------------------------
++ This is just a call to the ANSI C fgetpos function.
++ ----*/
++fget_pos(stream, ptr)
++FILE *stream;
++fpos_t *ptr;
++{
++ return(fgetpos(stream, ptr));
++}
++
++
++/*----------------------------------------------------------------------
++ This is just a call to the ANSI C fsetpos function.
++ ----*/
++fset_pos(stream, ptr)
++FILE *stream;
++fpos_t *ptr;
++{
++ return(fsetpos(stream, ptr));
++}
++
++
++
++/*======================================================================
++ pipe
++
++ Initiate I/O to and from a process. These functions are similar to
++ popen and pclose, but both an incoming stream and an output file are
++ provided.
++
++ ====*/
++
++#ifndef STDIN_FILENO
++#define STDIN_FILENO 0
++#endif
++#ifndef STDOUT_FILENO
++#define STDOUT_FILENO 1
++#endif
++#ifndef STDERR_FILENO
++#define STDERR_FILENO 2
++#endif
++
++
++/*
++ * Defs to help fish child's exit status out of wait(2)
++ */
++#ifdef HAVE_WAIT_UNION
++#define WaitType union wait
++#ifndef WIFEXITED
++#define WIFEXITED(X) (!(X).w_termsig) /* child exit by choice */
++#endif
++#ifndef WEXITSTATUS
++#define WEXITSTATUS(X) (X).w_retcode /* childs chosen exit value */
++#endif
++#else
++#define WaitType int
++#ifndef WIFEXITED
++#define WIFEXITED(X) (!((X) & 0xff)) /* low bits tell how it died */
++#endif
++#ifndef WEXITSTATUS
++#define WEXITSTATUS(X) (((X) >> 8) & 0xff) /* high bits tell exit value */
++#endif
++#endif
++
++
++/*
++ * Global's to helpsignal handler tell us child's status has changed...
++ */
++short child_signalled;
++short child_jump = 0;
++jmp_buf child_state;
++
++
++/*
++ * Internal Protos
++ */
++void pipe_error_cleanup PROTO((PIPE_S **, char *, char *, char *));
++void zot_pipe PROTO((PIPE_S **));
++
++
++
++
++/*----------------------------------------------------------------------
++ Spawn a child process and optionally connect read/write pipes to it
++
++ Args: command -- string to hand the shell
++ outfile -- address of pointer containing file to receive output
++ errfile -- address of pointer containing file to receive error output
++ mode -- mode for type of shell, signal protection etc...
++ Returns: pointer to alloc'd PIPE_S on success, NULL otherwise
++
++ The outfile is either NULL, a pointer to a NULL value, or a pointer
++ to the requested name for the output file. In the pointer-to-NULL case
++ the caller doesn't care about the name, but wants to see the pipe's
++ results so we make one up. It's up to the caller to make sure the
++ free storage containing the name is cleaned up.
++
++ Mode bits serve several purposes.
++ PIPE_WRITE tells us we need to open a pipe to write the child's
++ stdin.
++ PIPE_READ tells us we need to open a pipe to read from the child's
++ stdout/stderr. *NOTE* Having neither of the above set means
++ we're not setting up any pipes, just forking the child and exec'ing
++ the command. Also, this takes precedence over any named outfile.
++ PIPE_STDERR means we're to tie the childs stderr to the same place
++ stdout is going. *NOTE* This only makes sense then if PIPE_READ
++ or an outfile is provided. Also, this takes precedence over any
++ named errfile.
++ PIPE_PROT means to protect the child from the usual nasty signals
++ that might cause premature death. Otherwise, the default signals are
++ set so the child can deal with the nasty signals in its own way.
++ PIPE_NOSHELL means we're to exec the command without the aid of
++ a system shell. *NOTE* This negates the affect of PIPE_USER.
++ PIPE_USER means we're to try executing the command in the user's
++ shell. Right now we only look in the environment, but that may get
++ more sophisticated later.
++ PIPE_RESET means we reset the terminal mode to what it was before
++ we started pine and then exec the command.
++ ----*/
++PIPE_S *
++open_system_pipe(command, outfile, errfile, mode)
++ char *command;
++ char **outfile, **errfile;
++ int mode;
++{
++ PIPE_S *syspipe = NULL;
++ char shellpath[32], *shell;
++ int p[2], oparentd = -1, ochildd = -1, iparentd = -1, ichildd = -1;
++
++ dprint(5, (debugfile, "Opening pipe: \"%s\" (%s%s%s%s%s%s)\n", command,
++ (mode & PIPE_WRITE) ? "W":"", (mode & PIPE_READ) ? "R":"",
++ (mode & PIPE_NOSHELL) ? "N":"", (mode & PIPE_PROT) ? "P":"",
++ (mode & PIPE_USER) ? "U":"", (mode & PIPE_RESET) ? "T":""));
++
++ syspipe = (PIPE_S *)fs_get(sizeof(PIPE_S));
++ memset(syspipe, 0, sizeof(PIPE_S));
++
++ /*
++ * If we're not using the shell's command parsing smarts, build
++ * argv by hand...
++ */
++ if(mode & PIPE_NOSHELL){
++ char **ap, *p;
++ size_t n;
++
++ /* parse the arguments into argv */
++ for(p = command; *p && isspace((unsigned char)(*p)); p++)
++ ; /* swallow leading ws */
++
++ if(*p){
++ syspipe->args = cpystr(p);
++ }
++ else{
++ pipe_error_cleanup(&syspipe, "<null>", "execute",
++ "No command name found");
++ return(NULL);
++ }
++
++ for(p = syspipe->args, n = 2; *p; p++) /* count the args */
++ if(isspace((unsigned char)(*p))
++ && *(p+1) && !isspace((unsigned char)(*(p+1))))
++ n++;
++
++ syspipe->argv = ap = (char **)fs_get(n * sizeof(char *));
++ memset(syspipe->argv, 0, n * sizeof(char *));
++
++ for(p = syspipe->args; *p; ){ /* collect args */
++ while(*p && isspace((unsigned char)(*p)))
++ *p++ = '\0';
++
++ *ap++ = (*p) ? p : NULL;
++ while(*p && !isspace((unsigned char)(*p)))
++ p++;
++ }
++
++ /* make sure argv[0] exists in $PATH */
++ if(can_access_in_path(getenv("PATH"), syspipe->argv[0],
++ EXECUTE_ACCESS) < 0){
++ pipe_error_cleanup(&syspipe, syspipe->argv[0], "access",
++ error_description(errno));
++ return(NULL);
++ }
++ }
++
++ /* fill in any output filenames */
++ if(!(mode & PIPE_READ)){
++ if(outfile && !*outfile)
++ *outfile = temp_nam(NULL, "pine_p"); /* asked for, but not named? */
++
++ if(errfile && !*errfile)
++ *errfile = temp_nam(NULL, "pine_p"); /* ditto */
++ }
++
++ /* create pipes */
++ if(mode & (PIPE_WRITE | PIPE_READ)){
++ if(mode & PIPE_WRITE){
++ pipe(p); /* alloc pipe to write child */
++ oparentd = p[STDOUT_FILENO];
++ ichildd = p[STDIN_FILENO];
++ }
++
++ if(mode & PIPE_READ){
++ pipe(p); /* alloc pipe to read child */
++ iparentd = p[STDIN_FILENO];
++ ochildd = p[STDOUT_FILENO];
++ }
++ }
++ else if(!(mode & PIPE_SILENT)){
++ flush_status_messages(0); /* just clean up display */
++ ClearScreen();
++ fflush(stdout);
++ }
++
++ if((syspipe->mode = mode) & PIPE_RESET)
++ PineRaw(0);
++
++#ifdef SIGCHLD
++ /*
++ * Prepare for demise of child. Use SIGCHLD if it's available so
++ * we can do useful things, like keep the IMAP stream alive, while
++ * we're waiting on the child.
++ */
++ child_signalled = child_jump = 0;
++#endif
++
++ if((syspipe->pid = vfork()) == 0){
++ /* reset child's handlers in requested fashion... */
++ (void)signal(SIGINT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
++ (void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
++ (void)signal(SIGHUP, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
++#ifdef SIGCHLD
++ (void) signal(SIGCHLD, SIG_DFL);
++#endif
++
++ /* if parent isn't reading, and we have a filename to write */
++ if(!(mode & PIPE_READ) && outfile){ /* connect output to file */
++ int output = creat(*outfile, 0600);
++ dup2(output, STDOUT_FILENO);
++ if(mode & PIPE_STDERR)
++ dup2(output, STDERR_FILENO);
++ else if(errfile)
++ dup2(creat(*errfile, 0600), STDERR_FILENO);
++ }
++
++ if(mode & PIPE_WRITE){ /* connect process input */
++ close(oparentd);
++ dup2(ichildd, STDIN_FILENO); /* tie stdin to pipe */
++ close(ichildd);
++ }
++
++ if(mode & PIPE_READ){ /* connect process output */
++ close(iparentd);
++ dup2(ochildd, STDOUT_FILENO); /* tie std{out,err} to pipe */
++ if(mode & PIPE_STDERR)
++ dup2(ochildd, STDERR_FILENO);
++ else if(errfile)
++ dup2(creat(*errfile, 0600), STDERR_FILENO);
++
++ close(ochildd);
++ }
++
++ if(mode & PIPE_NOSHELL){
++ execvp(syspipe->argv[0], syspipe->argv);
++ }
++ else{
++ if(mode & PIPE_USER){
++ char *env, *sh;
++ if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){
++ shell = sh + 1;
++ strcpy(shellpath, env);
++ }
++ else{
++ shell = "csh";
++ strcpy(shellpath, "/bin/csh");
++ }
++ }
++ else{
++ shell = "sh";
++ strcpy(shellpath, "/bin/sh");
++ }
++
++ execl(shellpath, shell, command ? "-c" : 0, command, 0);
++ }
++
++ fprintf(stderr, "Can't exec %s\nReason: %s",
++ command, error_description(errno));
++ _exit(-1);
++ }
++
++ if(syspipe->pid > 0){
++ syspipe->isig = signal(SIGINT, SIG_IGN); /* Reset handlers to make */
++ syspipe->qsig = signal(SIGQUIT, SIG_IGN); /* sure we don't come to */
++ syspipe->hsig = signal(SIGHUP, SIG_IGN); /* a premature end... */
++
++ if(mode & PIPE_WRITE){
++ close(ichildd);
++ if(mode & PIPE_DESC)
++ syspipe->out.d = oparentd;
++ else
++ syspipe->out.f = fdopen(oparentd, "w");
++ }
++
++ if(mode & PIPE_READ){
++ close(ochildd);
++ if(mode & PIPE_DESC)
++ syspipe->in.d = iparentd;
++ else
++ syspipe->in.f = fdopen(iparentd, "r");
++ }
++
++ dprint(5, (debugfile, "PID: %d, COMMAND: %s\n",syspipe->pid,command));
++ }
++ else{
++ if(mode & (PIPE_WRITE | PIPE_READ)){
++ if(mode & PIPE_WRITE){
++ close(oparentd);
++ close(ichildd);
++ }
++
++ if(mode & PIPE_READ){
++ close(iparentd);
++ close(ochildd);
++ }
++ }
++ else if(!(mode & PIPE_SILENT)){
++ ClearScreen();
++ ps_global->mangled_screen = 1;
++ }
++
++ if(mode & PIPE_RESET)
++ PineRaw(1);
++
++#ifdef SIGCHLD
++ (void) signal(SIGCHLD, SIG_DFL);
++#endif
++ if(outfile)
++ fs_give((void **) outfile);
++
++ pipe_error_cleanup(&syspipe, command, "fork",error_description(errno));
++ }
++
++ return(syspipe);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Write appropriate error messages and cleanup after pipe error
++
++ Args: syspipe -- address of pointer to struct to clean up
++ cmd -- command we were trying to exec
++ op -- operation leading up to the exec
++ res -- result of that operation
++
++ ----*/
++void
++pipe_error_cleanup(syspipe, cmd, op, res)
++ PIPE_S **syspipe;
++ char *cmd, *op, *res;
++{
++ q_status_message3(SM_ORDER, 3, 3, "Pipe can't %s \"%.20s\": %s",
++ op, cmd, res);
++ dprint(1, (debugfile, "* * PIPE CAN'T %s(%s): %s\n", op, cmd, res));
++ zot_pipe(syspipe);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Free resources associated with the given pipe struct
++
++ Args: syspipe -- address of pointer to struct to clean up
++
++ ----*/
++void
++zot_pipe(syspipe)
++ PIPE_S **syspipe;
++{
++ if((*syspipe)->args)
++ fs_give((void **) &(*syspipe)->args);
++
++ if((*syspipe)->argv)
++ fs_give((void **) &(*syspipe)->argv);
++
++ if((*syspipe)->tmp)
++ fs_give((void **) &(*syspipe)->tmp);
++
++ fs_give((void **)syspipe);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Close pipe previously allocated and wait for child's death
++
++ Args: syspipe -- address of pointer to struct returned by open_system_pipe
++ Returns: returns exit status of child or -1 if invalid syspipe
++ ----*/
++int
++close_system_pipe(syspipe)
++ PIPE_S **syspipe;
++{
++ WaitType stat;
++ int status;
++
++ if(!(syspipe && *syspipe))
++ return(-1);
++
++ if(((*syspipe)->mode) & PIPE_WRITE){
++ if(((*syspipe)->mode) & PIPE_DESC){
++ if((*syspipe)->out.d >= 0)
++ close((*syspipe)->out.d);
++ }
++ else if((*syspipe)->out.f)
++ fclose((*syspipe)->out.f);
++ }
++
++ if(((*syspipe)->mode) & PIPE_READ){
++ if(((*syspipe)->mode) & PIPE_DESC){
++ if((*syspipe)->in.d >= 0)
++ close((*syspipe)->in.d);
++ }
++ else if((*syspipe)->in.f)
++ fclose((*syspipe)->in.f);
++ }
++
++#ifdef SIGCHLD
++ {
++ SigType (*alarm_sig)();
++ int old_cue = F_ON(F_SHOW_DELAY_CUE, ps_global);
++
++ /*
++ * remember the current SIGALRM handler, and make sure it's
++ * installed when we're finished just in case the longjmp
++ * out of the SIGCHLD handler caused sleep() to lose it.
++ * Don't pay any attention to that man behind the curtain.
++ */
++ alarm_sig = signal(SIGALRM, SIG_IGN);
++ (void) signal(SIGALRM, alarm_sig);
++ F_SET(F_SHOW_DELAY_CUE, ps_global, 0);
++ ps_global->noshow_timeout = 1;
++ while(!child_signalled){
++ /* wake up and prod server */
++ new_mail(0, 2, ((*syspipe)->mode & PIPE_RESET)
++ ? NM_NONE : NM_DEFER_SORT);
++
++ if(!child_signalled){
++ if(setjmp(child_state) == 0){
++ child_jump = 1; /* prepare to wake up */
++ sleep(600); /* give it 5mins to happend */
++ }
++ else
++ our_sigunblock(SIGCHLD);
++ }
++
++ child_jump = 0;
++ }
++
++ ps_global->noshow_timeout = 0;
++ F_SET(F_SHOW_DELAY_CUE, ps_global, old_cue);
++ (void) signal(SIGALRM, alarm_sig);
++ }
++#endif
++
++ /*
++ * Call c-client's pid reaper to wait() on the demise of our child,
++ * then fish out its exit status...
++ */
++ grim_pid_reap_status((*syspipe)->pid, 0, &stat);
++ status = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
++
++ /*
++ * restore original handlers...
++ */
++ (void)signal(SIGINT, (*syspipe)->isig);
++ (void)signal(SIGHUP, (*syspipe)->hsig);
++ (void)signal(SIGQUIT, (*syspipe)->qsig);
++
++ if((*syspipe)->mode & PIPE_RESET) /* restore our tty modes */
++ PineRaw(1);
++
++ if(!((*syspipe)->mode & (PIPE_WRITE | PIPE_READ | PIPE_SILENT))){
++ ClearScreen(); /* No I/O to forked child */
++ ps_global->mangled_screen = 1;
++ }
++
++ zot_pipe(syspipe);
++
++ return(status);
++}
++
++/*======================================================================
++ post_reap
++
++ Manage exit status collection of a child spawned to handle posting
++ ====*/
++
++
++
++#if defined(BACKGROUND_POST) && defined(SIGCHLD)
++/*----------------------------------------------------------------------
++ Check to see if we have any posting processes to clean up after
++
++ Args: none
++ Returns: any finished posting process reaped
++ ----*/
++post_reap()
++{
++ WaitType stat;
++ int r;
++
++ if(ps_global->post && ps_global->post->pid){
++ r = waitpid(ps_global->post->pid, &stat, WNOHANG);
++ if(r == ps_global->post->pid){
++ ps_global->post->status = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
++ ps_global->post->pid = 0;
++ return(1);
++ }
++ else if(r < 0 && errno != EINTR){ /* pid's become bogus?? */
++ fs_give((void **) &ps_global->post);
++ }
++ }
++
++ return(0);
++}
++#endif
++
++/*----------------------------------------------------------------------
++ Routines used to hand off messages to local agents for sending/posting
++
++ The two exported routines are:
++
++ 1) smtp_command() -- used to get local transport agent to invoke
++ 2) post_handoff() -- used to pass messages to local posting agent
++
++ ----*/
++
++
++
++/*
++ * Protos for "sendmail" internal functions
++ */
++static char *mta_parse_post PROTO((METAENV *, BODY *, char *, char *));
++static long pine_pipe_soutr_nl PROTO((void *, char *));
++
++
++
++/* ----------------------------------------------------------------------
++ Figure out command to start local SMTP agent
++
++ Args: errbuf -- buffer for reporting errors (assumed non-NULL)
++
++ Returns an alloc'd copy of the local SMTP agent invocation or NULL
++
++ ----*/
++char *
++smtp_command(errbuf)
++ char *errbuf;
++{
++#if defined(SENDMAIL) && defined(SENDMAILFLAGS)
++ char tmp[256];
++
++ sprintf(tmp, "%s %s", SENDMAIL, SENDMAILFLAGS);
++ return(cpystr(tmp));
++#else
++ strcpy(errbuf, "No default posting command.");
++ return(NULL);
++#endif
++}
++
++
++
++/*----------------------------------------------------------------------
++ Hand off given message to local posting agent
++
++ Args: envelope -- The envelope for the BCC and debugging
++ header -- The text of the message header
++ errbuf -- buffer for reporting errors (assumed non-NULL)
++
++ ----*/
++int
++mta_handoff(header, body, errbuf)
++ METAENV *header;
++ BODY *body;
++ char *errbuf;
++{
++ char cmd_buf[256], *cmd = NULL;
++
++ /*
++ * A bit of complicated policy implemented here.
++ * There are two posting variables sendmail-path and smtp-server.
++ * Precedence is in that order.
++ * They can be set one of 4 ways: fixed, command-line, user, or globally.
++ * Precedence is in that order.
++ * Said differently, the order goes something like what's below.
++ *
++ * NOTE: the fixed/command-line/user precendence handling is also
++ * indicated by what's pointed to by ps_global->VAR_*, but since
++ * that also includes the global defaults, it's not sufficient.
++ */
++
++ if(ps_global->FIX_SENDMAIL_PATH
++ && ps_global->FIX_SENDMAIL_PATH[0]){
++ cmd = ps_global->FIX_SENDMAIL_PATH;
++ }
++ else if(!(ps_global->FIX_SMTP_SERVER
++ && ps_global->FIX_SMTP_SERVER[0])){
++ if(ps_global->COM_SENDMAIL_PATH
++ && ps_global->COM_SENDMAIL_PATH[0]){
++ cmd = ps_global->COM_SENDMAIL_PATH;
++ }
++ else if(!(ps_global->COM_SMTP_SERVER
++ && ps_global->COM_SMTP_SERVER[0])){
++ if(ps_global->USR_SENDMAIL_PATH
++ && ps_global->USR_SENDMAIL_PATH[0]){
++ cmd = ps_global->USR_SENDMAIL_PATH;
++ }
++ else if(!(ps_global->USR_SMTP_SERVER
++ && ps_global->USR_SMTP_SERVER[0])){
++ if(ps_global->GLO_SENDMAIL_PATH
++ && ps_global->GLO_SENDMAIL_PATH[0]){
++ cmd = ps_global->GLO_SENDMAIL_PATH;
++ }
++#ifdef DF_SENDMAIL_PATH
++ /*
++ * This defines the default method of posting. So,
++ * unless we're told otherwise use it...
++ */
++ else if(!(ps_global->GLO_SMTP_SERVER
++ && ps_global->GLO_SMTP_SERVER[0])){
++ strcpy(cmd = cmd_buf, DF_SENDMAIL_PATH);
++ }
++#endif
++ }
++ }
++ }
++
++ *errbuf = '\0';
++ if(cmd){
++ dprint(4, (debugfile, "call_mailer via cmd: %s\n", cmd));
++
++ (void) mta_parse_post(header, body, cmd, errbuf);
++ return(1);
++ }
++ else
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Hand off given message to local posting agent
++
++ Args: envelope -- The envelope for the BCC and debugging
++ header -- The text of the message header
++ errbuf -- buffer for reporting errors (assumed non-NULL)
++
++ Fork off mailer process and pipe the message into it
++ Called to post news via Inews when NNTP is unavailable
++
++ ----*/
++char *
++post_handoff(header, body, errbuf)
++ METAENV *header;
++ BODY *body;
++ char *errbuf;
++{
++ char *err = NULL;
++#ifdef SENDNEWS
++ char *s;
++
++ if(s = strstr(header->env->date," (")) /* fix the date format for news */
++ *s = '\0';
++
++ if(err = mta_parse_post(header, body, SENDNEWS, errbuf))
++ sprintf(err = errbuf, "News not posted: \"%s\": %s", SENDNEWS, err);
++
++ if(s)
++ *s = ' '; /* restore the date */
++
++#else /* !SENDNEWS */ /* this is the default case */
++ sprintf(err = errbuf, "Can't post, NNTP-server must be defined!");
++#endif /* !SENDNEWS */
++ return(err);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Hand off message to local MTA; it parses recipients from 822 header
++
++ Args: header -- struct containing header data
++ body -- struct containing message body data
++ cmd -- command to use for handoff (%s says where file should go)
++ errs -- pointer to buf to hold errors
++
++ ----*/
++static char *
++mta_parse_post(header, body, cmd, errs)
++ METAENV *header;
++ BODY *body;
++ char *cmd;
++ char *errs;
++{
++ char *result = NULL;
++ PIPE_S *pipe;
++
++ dprint(1, (debugfile, "=== mta_parse_post(%s) ===\n", cmd));
++
++ if(pipe = open_system_pipe(cmd, &result, NULL,
++ PIPE_STDERR|PIPE_WRITE|PIPE_PROT|PIPE_NOSHELL|PIPE_DESC)){
++ if(!pine_rfc822_output(header, body, pine_pipe_soutr_nl,
++ (TCPSTREAM *) pipe))
++ strcpy(errs, "Error posting.");
++
++ if(close_system_pipe(&pipe) && !*errs){
++ sprintf(errs, "Posting program %s returned error", cmd);
++ if(result)
++ display_output_file(result, "POSTING ERRORS", errs, 1);
++ }
++ }
++ else
++ sprintf(errs, "Error running \"%s\"", cmd);
++
++ if(result){
++ unlink(result);
++ fs_give((void **)&result);
++ }
++
++ return(*errs ? errs : NULL);
++}
++
++
++/*
++ * pine_pipe_soutr - Replacement for tcp_soutr that writes one of our
++ * pipes rather than a tcp stream
++ */
++static long
++pine_pipe_soutr_nl (stream,s)
++ void *stream;
++ char *s;
++{
++ long rv = T;
++ char *p;
++ size_t n;
++
++ while(*s && rv){
++ if(n = (p = strstr(s, "\015\012")) ? p - s : strlen(s))
++ while((rv = write(((PIPE_S *)stream)->out.d, s, n)) != n)
++ if(rv < 0){
++ if(errno != EINTR){
++ rv = 0;
++ break;
++ }
++ }
++ else{
++ s += rv;
++ n -= rv;
++ }
++
++ if(p && rv){
++ s = p + 2; /* write UNIX EOL */
++ while((rv = write(((PIPE_S *)stream)->out.d,"\n",1)) != 1)
++ if(rv < 0 && errno != EINTR){
++ rv = 0;
++ break;
++ }
++ }
++ else
++ break;
++ }
++
++ return(rv);
++}
++
++/* ----------------------------------------------------------------------
++ Execute the given mailcap command
++
++ Args: cmd -- the command to execute
++ image_file -- the file the data is in
++ needsterminal -- does this command want to take over the terminal?
++ ----*/
++void
++exec_mailcap_cmd(cmd, image_file, needsterminal)
++char *cmd;
++char *image_file;
++int needsterminal;
++{
++ char *command = NULL,
++ *result_file = NULL,
++ *p;
++ char **r_file_h;
++ PIPE_S *syspipe;
++ int mode;
++
++ p = command = (char *)fs_get((32 + strlen(cmd) + (2*strlen(image_file)))
++ * sizeof(char));
++ if(!needsterminal) /* put in background if it doesn't need terminal */
++ *p++ = '(';
++ sprintf(p, "%s ; rm -f %s", cmd, image_file);
++ p += strlen(p);
++ if(!needsterminal){
++ *p++ = ')';
++ *p++ = ' ';
++ *p++ = '&';
++ }
++ *p++ = '\n';
++ *p = '\0';
++ dprint(9, (debugfile, "exec_mailcap_cmd: command=%s\n", command));
++
++ mode = PIPE_RESET;
++ if(needsterminal == 1)
++ r_file_h = NULL;
++ else{
++ mode |= PIPE_WRITE | PIPE_STDERR;
++ result_file = temp_nam(NULL, "pine_cmd");
++ r_file_h = &result_file;
++ }
++
++ if(syspipe = open_system_pipe(command, r_file_h, NULL, mode)){
++ close_system_pipe(&syspipe);
++ if(needsterminal == 1)
++ q_status_message(SM_ORDER, 0, 4, "VIEWER 命令完成");
++ else if(needsterminal == 2)
++ display_output_file(result_file, "VIEWER", " command result", 1);
++ else
++ display_output_file(result_file, "VIEWER", " command launched", 1);
++ }
++ else
++ q_status_message1(SM_ORDER, 3, 4, "無法起始命令:%s", cmd);
++
++ fs_give((void **)&command);
++ if(result_file)
++ fs_give((void **)&result_file);
++}
++
++
++/* ----------------------------------------------------------------------
++ Execute the given mailcap test= cmd
++
++ Args: cmd -- command to execute
++ Returns exit status
++
++ ----*/
++int
++exec_mailcap_test_cmd(cmd)
++ char *cmd;
++{
++ PIPE_S *syspipe;
++
++ return((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_SILENT))
++ ? close_system_pipe(&syspipe) : -1);
++}
++
++
++
++/*======================================================================
++ print routines
++
++ Functions having to do with printing on paper and forking of spoolers
++
++ In general one calls open_printer() to start printing. One of
++ the little print functions to send a line or string, and then
++ call print_end() when complete. This takes care of forking off a spooler
++ and piping the stuff down it. No handles or anything here because there's
++ only one printer open at a time.
++
++ ====*/
++
++
++
++static char *trailer; /* so both open and close_printer can see it */
++
++/*----------------------------------------------------------------------
++ Open the printer
++
++ Args: desc -- Description of item to print. Should have one trailing blank.
++
++ Return value: < 0 is a failure.
++ 0 a success.
++
++This does most of the work of popen so we can save the standard output of the
++command we execute and send it back to the user.
++ ----*/
++int
++open_printer(desc)
++ char *desc;
++{
++ char command[201], prompt[200];
++ int cmd, rc, just_one;
++ char *p, *init, *nick;
++ char aname[100];
++ char *printer;
++ int done = 0, i, lastprinter, cur_printer = 0;
++ HelpType help;
++ char **list;
++ static ESCKEY_S ekey[] = {
++ {'y', 'y', "Y", "是"},
++ {'n', 'n', "N", "否"},
++ {ctrl('P'), 10, "^P", "前一印表機"},
++ {ctrl('N'), 11, "^N", "下一印表機"},
++ {-2, 0, NULL, NULL},
++ {'c', 'c', "C", "自定印表機"},
++ {KEY_UP, 10, "", ""},
++ {KEY_DOWN, 11, "", ""},
++ {-1, 0, NULL, NULL}};
++#define PREV_KEY 2
++#define NEXT_KEY 3
++#define CUSTOM_KEY 5
++#define UP_KEY 6
++#define DOWN_KEY 7
++
++ trailer = NULL;
++ init = NULL;
++ nick = NULL;
++ command[200] = '\0';
++
++ if(ps_global->VAR_PRINTER == NULL){
++ q_status_message(SM_ORDER | SM_DING, 3, 5,
++ "尚未選擇印表機。請用主選單中的設定來選擇。");
++ return(-1);
++ }
++
++ /* Is there just one print command available? */
++ just_one = (ps_global->printer_category!=3&&ps_global->printer_category!=2)
++ || (ps_global->printer_category == 2
++ && !(ps_global->VAR_STANDARD_PRINTER
++ && ps_global->VAR_STANDARD_PRINTER[0]
++ && ps_global->VAR_STANDARD_PRINTER[1]))
++ || (ps_global->printer_category == 3
++ && !(ps_global->VAR_PERSONAL_PRINT_COMMAND
++ && ps_global->VAR_PERSONAL_PRINT_COMMAND[0]
++ && ps_global->VAR_PERSONAL_PRINT_COMMAND[1]));
++
++ if(F_ON(F_CUSTOM_PRINT, ps_global))
++ ekey[CUSTOM_KEY].ch = 'c'; /* turn this key on */
++ else
++ ekey[CUSTOM_KEY].ch = -2; /* turn this key off */
++
++ if(just_one){
++ ekey[PREV_KEY].ch = -2; /* turn these keys off */
++ ekey[NEXT_KEY].ch = -2;
++ ekey[UP_KEY].ch = -2;
++ ekey[DOWN_KEY].ch = -2;
++ }
++ else{
++ ekey[PREV_KEY].ch = ctrl('P'); /* turn these keys on */
++ ekey[NEXT_KEY].ch = ctrl('N');
++ ekey[UP_KEY].ch = KEY_UP;
++ ekey[DOWN_KEY].ch = KEY_DOWN;
++ /*
++ * count how many printers in list and find the default in the list
++ */
++ if(ps_global->printer_category == 2)
++ list = ps_global->VAR_STANDARD_PRINTER;
++ else
++ list = ps_global->VAR_PERSONAL_PRINT_COMMAND;
++
++ for(i = 0; list[i]; i++)
++ if(strcmp(ps_global->VAR_PRINTER, list[i]) == 0)
++ cur_printer = i;
++
++ lastprinter = i - 1;
++ }
++
++ help = NO_HELP;
++ ps_global->mangled_footer = 1;
++
++ while(!done){
++ if(init)
++ fs_give((void **)&init);
++
++ if(trailer)
++ fs_give((void **)&trailer);
++
++ if(just_one)
++ printer = ps_global->VAR_PRINTER;
++ else
++ printer = list[cur_printer];
++
++ parse_printer(printer, &nick, &p, &init, &trailer, NULL, NULL);
++ strncpy(command, p, 200);
++ fs_give((void **)&p);
++ sprintf(prompt, "Print %.50s%susing \"%.50s\" ? ",
++ desc ? desc : "",
++ (desc && *desc && desc[strlen(desc) - 1] != ' ') ? " " : "",
++ *nick ? nick : command);
++
++ fs_give((void **)&nick);
++
++ cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
++ ekey, 'y', 'x', help, RB_NORM);
++
++ switch(cmd){
++ case 'y':
++ q_status_message1(SM_ORDER, 0, 9,
++ "正以 \"%s\" 命令列印中", command);
++ done++;
++ break;
++
++ case 10:
++ cur_printer = (cur_printer>0)
++ ? (cur_printer-1)
++ : lastprinter;
++ break;
++
++ case 11:
++ cur_printer = (cur_printer<lastprinter)
++ ? (cur_printer+1)
++ : 0;
++ break;
++
++ case 'n':
++ case 'x':
++ done++;
++ break;
++
++ case 'c':
++ done++;
++ break;
++
++ default:
++ break;
++ }
++ }
++
++ if(cmd == 'c'){
++ if(init)
++ fs_give((void **)&init);
++
++ if(trailer)
++ fs_give((void **)&trailer);
++
++ sprintf(prompt, "輸入命令:");
++ command[0] = '\0';
++ rc = 1;
++ help = NO_HELP;
++ while(rc){
++ int flags = OE_APPEND_CURRENT;
++
++ rc = optionally_enter(command, -FOOTER_ROWS(ps_global), 0,
++ 200, prompt, NULL, help, &flags);
++
++ if(rc == 1){
++ cmd = 'x';
++ rc = 0;
++ }
++ else if(rc == 3)
++ help = (help == NO_HELP) ? h_custom_print : NO_HELP;
++ else if(rc == 0){
++ removing_trailing_white_space(command);
++ removing_leading_white_space(command);
++ q_status_message1(SM_ORDER, 0, 9,
++ "正以 \"%s\" 命令列印中", command);
++ }
++ }
++ }
++
++ if(cmd == 'x' || cmd == 'n'){
++ q_status_message(SM_ORDER, 0, 2, "取消列印");
++ if(init)
++ fs_give((void **)&init);
++
++ if(trailer)
++ fs_give((void **)&trailer);
++
++ return(-1);
++ }
++
++ display_message('x');
++
++ ps_global->print = (PRINT_S *)fs_get(sizeof(PRINT_S));
++ memset(ps_global->print, 0, sizeof(PRINT_S));
++
++ strcat(strcpy(aname, ANSI_PRINTER), "-no-formfeed");
++ if(strucmp(command, ANSI_PRINTER) == 0
++ || strucmp(command, aname) == 0){
++ /*----------- Printer attached to ansi device ---------*/
++ q_status_message(SM_ORDER, 0, 9,
++ "正列印至桌上印表機...");
++ display_message('x');
++ xonxoff_proc(1); /* make sure XON/XOFF used */
++ crlf_proc(1); /* AND LF->CR xlation */
++ fputs("\033[5i", stdout);
++ ps_global->print->fp = stdout;
++ if(strucmp(command, ANSI_PRINTER) == 0){
++ /* put formfeed at the end of the trailer string */
++ if(trailer){
++ int len = strlen(trailer);
++
++ fs_resize((void **)&trailer, len+2);
++ trailer[len] = '\f';
++ trailer[len+1] = '\0';
++ }
++ else
++ trailer = cpystr("\f");
++ }
++ }
++ else{
++ /*----------- Print by forking off a UNIX command ------------*/
++ dprint(4, (debugfile, "Printing using command \"%s\"\n", command));
++ ps_global->print->result = temp_nam(NULL, "pine_prt");
++ if(ps_global->print->pipe = open_system_pipe(command,
++ &ps_global->print->result, NULL,
++ PIPE_WRITE | PIPE_STDERR)){
++ ps_global->print->fp = ps_global->print->pipe->out.f;
++ }
++ else{
++ fs_give((void **)&ps_global->print->result);
++ q_status_message1(SM_ORDER | SM_DING, 3, 4,
++ "印表機開啟錯誤:%s",
++ error_description(errno));
++ dprint(2, (debugfile, "Error popening printer \"%s\"\n",
++ error_description(errno)));
++ if(init)
++ fs_give((void **)&init);
++
++ if(trailer)
++ fs_give((void **)&trailer);
++
++ return(-1);
++ }
++ }
++
++ ps_global->print->err = 0;
++ if(init){
++ if(*init)
++ fputs(init, ps_global->print->fp);
++
++ fs_give((void **)&init);
++ }
++
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Close printer
++
++ If we're piping to a spooler close down the pipe and wait for the process
++to finish. If we're sending to an attached printer send the escape sequence.
++Also let the user know the result of the print
++ ----*/
++void
++close_printer()
++{
++ if(trailer){
++ if(*trailer)
++ fputs(trailer, ps_global->print->fp);
++
++ fs_give((void **)&trailer);
++ }
++
++ if(ps_global->print->fp == stdout) {
++ fputs("\033[4i", stdout);
++ fflush(stdout);
++ if(F_OFF(F_PRESERVE_START_STOP, ps_global))
++ xonxoff_proc(0); /* turn off XON/XOFF */
++
++ crlf_proc(0); /* turn off CF->LF xlantion */
++ } else {
++ (void) close_system_pipe(&ps_global->print->pipe);
++ display_output_file(ps_global->print->result, "PRINT", NULL, 1);
++ fs_give((void **)&ps_global->print->result);
++ }
++
++ fs_give((void **)&ps_global->print);
++
++ q_status_message(SM_ASYNC, 0, 3, "列印指令完成");
++ display_message('x');
++}
++
++
++
++/*----------------------------------------------------------------------
++ Print a single character
++
++ Args: c -- char to print
++ Returns: 1 on success, 0 on ps_global->print->err
++ ----*/
++int
++print_char(c)
++ int c;
++{
++ if(!ps_global->print->err && putc(c, ps_global->print->fp) == EOF)
++ ps_global->print->err = 1;
++
++ return(!ps_global->print->err);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Send a line of text to the printer
++
++ Args: line -- Text to print
++
++ ----*/
++
++void
++print_text(line)
++ char *line;
++{
++ if(!ps_global->print->err && fputs(line, ps_global->print->fp) == EOF)
++ ps_global->print->err = 1;
++}
++
++
++
++/*----------------------------------------------------------------------
++ printf style formatting with one arg for printer
++
++ Args: line -- The printf control string
++ a1 -- The 1st argument for printf
++ ----*/
++void
++print_text1(line, a1)
++ char *line, *a1;
++{
++ if(!ps_global->print->err
++ && fprintf(ps_global->print->fp, line, a1) < 0)
++ ps_global->print->err = 1;
++}
++
++
++
++/*----------------------------------------------------------------------
++ printf style formatting with one arg for printer
++
++ Args: line -- The printf control string
++ a1 -- The 1st argument for printf
++ a2 -- The 2nd argument for printf
++ ----*/
++void
++print_text2(line, a1, a2)
++ char *line, *a1, *a2;
++{
++ if(!ps_global->print->err
++ && fprintf(ps_global->print->fp, line, a1, a2) < 0)
++ ps_global->print->err = 1;
++}
++
++
++
++/*----------------------------------------------------------------------
++ printf style formatting with one arg for printer
++
++ Args: line -- The printf control string
++ a1 -- The 1st argument for printf
++ a2 -- The 2nd argument for printf
++ a3 -- The 3rd argument for printf
++ ----*/
++void
++print_text3(line, a1, a2, a3)
++ char *line, *a1, *a2, *a3;
++{
++ if(!ps_global->print->err
++ && fprintf(ps_global->print->fp, line, a1, a2, a3) < 0)
++ ps_global->print->err = 1;
++}
++
++#ifdef DEBUG
++/*----------------------------------------------------------------------
++ Initialize debugging - open the debug log file
++
++ Args: none
++
++ Result: opens the debug logfile for dprints
++
++ Opens the file "~/.pine-debug1. Also maintains .pine-debug[2-4]
++ by renaming them each time so the last 4 sessions are saved.
++ ----*/
++void
++init_debug()
++{
++ char nbuf[5];
++ char newfname[MAXPATH+1], filename[MAXPATH+1];
++ int i, fd;
++
++ if(!(debug || ps_global->debug_imap))
++ return;
++
++ for(i = ps_global->debug_nfiles - 1; i > 0; i--){
++ build_path(filename, ps_global->home_dir, DEBUGFILE);
++ strcpy(newfname, filename);
++ sprintf(nbuf, "%d", i);
++ strcat(filename, nbuf);
++ sprintf(nbuf, "%d", i+1);
++ strcat(newfname, nbuf);
++ (void)rename_file(filename, newfname);
++ }
++
++ build_path(filename, ps_global->home_dir, DEBUGFILE);
++ strcat(filename, "1");
++
++ debugfile = NULL;
++ if((fd = open(filename, O_TRUNC|O_RDWR|O_CREAT, 0600)) >= 0)
++ debugfile = fdopen(fd, "w+");
++
++ if(debugfile != NULL){
++ time_t now = time((time_t *)0);
++ if(ps_global->debug_flush)
++ setbuf(debugfile, NULL);
++
++ if(ps_global->debug_nfiles == 0){
++ /*
++ * If no debug files are asked for, make filename a tempfile
++ * to be used for a record should pine later crash...
++ */
++ if(debug < 9 && !ps_global->debug_flush && ps_global->debug_imap<4)
++ unlink(filename);
++ }
++
++ dprint(0, (debugfile,
++ "Debug output of the Pine program (debug=%d debug_imap=%d). Version %s\n%s\n",
++ debug, ps_global->debug_imap, pine_version, ctime(&now)));
++ }
++}
++
++
++/*----------------------------------------------------------------------
++ Try to save the debug file if we crash in a controlled way
++
++ Args: dfile: pointer to open debug file
++
++ Result: tries to move the appropriate .pine-debugx file to .pine-crash
++
++ Looks through the four .pine-debug files hunting for the one that is
++ associated with this pine, and then renames it.
++ ----*/
++void
++save_debug_on_crash(dfile)
++FILE *dfile;
++{
++ char nbuf[5], crashfile[MAXPATH+1], filename[MAXPATH+1];
++ int i;
++ struct stat dbuf, tbuf;
++ time_t now = time((time_t *)0);
++
++ if(!(dfile && fstat(fileno(dfile), &dbuf) != 0))
++ return;
++
++ fprintf(dfile, "\nsave_debug_on_crash: Version %s: debug level %d\n",
++ pine_version, debug);
++ fprintf(dfile, "\n : %s\n", ctime(&now));
++
++ build_path(crashfile, ps_global->home_dir, ".pine-crash");
++
++ fprintf(dfile, "\nAttempting to save debug file to %s\n", crashfile);
++ fprintf(stderr,
++ "\n\n Attempting to save debug file to %s\n\n", crashfile);
++
++ /* Blat out last n keystrokes */
++ fputs("========== Latest keystrokes ==========\n", dfile);
++ while((i = key_playback(0)) != -1)
++ fprintf(dfile, "\t%s\t(0x%04.4x)\n", pretty_command(i), i);
++
++ /* look for existing debug file */
++ for(i = 1; i <= ps_global->debug_nfiles; i++){
++ build_path(filename, ps_global->home_dir, DEBUGFILE);
++ sprintf(nbuf, "%d", i);
++ strcat(filename, nbuf);
++ if(stat(filename, &tbuf) != 0)
++ continue;
++
++ /* This must be the current debug file */
++ if(tbuf.st_dev == dbuf.st_dev && tbuf.st_ino == dbuf.st_ino){
++ rename_file(filename, crashfile);
++ break;
++ }
++ }
++
++ /* if current debug file name not found, write it by hand */
++ if(i > ps_global->debug_nfiles){
++ FILE *cfp;
++ char buf[1025];
++
++ /*
++ * Copy the debug temp file into the
++ */
++ if(cfp = fopen(crashfile, "w")){
++ buf[1024] = '\0';
++ fseek(dfile, 0L, 0);
++ while(fgets(buf, 1025, dfile) && fputs(buf, cfp) != EOF)
++ ;
++
++ fclose(cfp);
++ }
++ }
++
++ fclose(dfile);
++}
++
++
++#define CHECK_EVERY_N_TIMES 100
++#define MAX_DEBUG_FILE_SIZE 200000L
++/*
++ * This is just to catch runaway Pines that are looping spewing out
++ * debugging (and filling up a file system). The stop doesn't have to be
++ * at all precise, just soon enough to hopefully prevent filling the
++ * file system. If the debugging level is high (9 for now), then we're
++ * presumably looking for some problem, so don't truncate.
++ */
++int
++do_debug(debug_fp)
++FILE *debug_fp;
++{
++ static int counter = CHECK_EVERY_N_TIMES;
++ static int ok = 1;
++ long filesize;
++
++ if(debug == DEFAULT_DEBUG
++ && !ps_global->debug_flush
++ && !ps_global->debug_timestamp
++ && ps_global->debug_imap < 2
++ && ok
++ && --counter <= 0){
++ if((filesize = fp_file_size(debug_fp)) != -1L)
++ ok = (unsigned long)filesize < (unsigned long)MAX_DEBUG_FILE_SIZE;
++
++ counter = CHECK_EVERY_N_TIMES;
++ if(!ok){
++ fprintf(debug_fp, "\n\n --- No more debugging ---\n");
++ fprintf(debug_fp,
++ " (debug file growing too large - over %ld bytes)\n\n",
++ MAX_DEBUG_FILE_SIZE);
++ fflush(debug_fp);
++ }
++ }
++
++ if(ok && ps_global->debug_timestamp)
++ fprintf(debug_fp, "\n%s\n", debug_time(0));
++
++ return(ok);
++}
++
++
++/*
++ * Returns a pointer to static string for a timestamp.
++ *
++ * If timestamp is set .subseconds are added if available.
++ * If include_date is set the date is appended.
++ */
++char *
++debug_time(include_date)
++ int include_date;
++{
++ time_t t;
++ struct tm *tm_now;
++ struct timeval tp;
++ struct timezone tzp;
++ static char timestring[23];
++ char subsecond[8];
++ char datestr[7];
++
++ if(gettimeofday(&tp, &tzp) == 0){
++ t = (time_t)tp.tv_sec;
++ if(include_date){
++ tm_now = localtime(&t);
++ sprintf(datestr, " %d/%d", tm_now->tm_mon+1, tm_now->tm_mday);
++ }
++ else
++ datestr[0] = '\0';
++
++ if(ps_global->debug_timestamp)
++ sprintf(subsecond, ".%06ld", tp.tv_usec);
++ else
++ subsecond[0] = '\0';
++
++ sprintf(timestring, "%.8s%s%s", ctime(&t)+11, subsecond, datestr);
++ }
++ else
++ timestring[0] = '\0';
++
++ return(timestring);
++}
++#endif /* DEBUG */
++
++
++/*
++ * Fills in the passed in structure with the current time.
++ *
++ * Returns 0 if ok
++ * -1 if can't do it
++ */
++int
++get_time(our_time_val)
++ TIMEVAL_S *our_time_val;
++{
++ struct timeval tp;
++ struct timezone tzp;
++
++ if(gettimeofday(&tp, &tzp) == 0){
++ our_time_val->sec = tp.tv_sec;
++ our_time_val->usec = tp.tv_usec;
++ return 0;
++ }
++ else
++ return -1;
++}
++
++
++/*
++ * Returns the difference between the two values, in microseconds.
++ * Value returned is first - second.
++ */
++long
++time_diff(first, second)
++ TIMEVAL_S *first,
++ *second;
++{
++ return(1000000L*(first->sec - second->sec) + (first->usec - second->usec));
++}
++
++
++
++/*======================================================================
++ Things having to do with reading from the tty driver and keyboard
++ - initialize tty driver and reset tty driver
++ - read a character from terminal with keyboard escape seqence mapping
++ - initialize keyboard (keypad or such) and reset keyboard
++ - prompt user for a line of input
++ - read a command from keyboard with timeouts.
++
++ ====*/
++
++
++/*
++ * Helpful definitions
++ */
++#define RETURN_CH(X) return(key_recorder((X)))
++/*
++ * Should really be using pico's TERM's t_getchar to read a character but
++ * we're just calling ttgetc directly for now. Ttgetc is the same as
++ * t_getchar whenever we use it so we're avoiding the trouble of initializing
++ * the TERM struct and calling ttgetc directly.
++ */
++#define READ_A_CHAR() ttgetc(NO_OP_COMMAND, key_recorder, read_bail)
++
++
++/*
++ * Internal prototypes
++ */
++void line_paint PROTO((int, int *));
++int process_config_input PROTO((int *));
++int check_for_timeout PROTO((int));
++void read_bail PROTO((void));
++
++
++/*----------------------------------------------------------------------
++ Initialize the tty driver to do single char I/O and whatever else (UNIX)
++
++ Args: struct pine
++
++ Result: tty driver is put in raw mode so characters can be read one
++ at a time. Returns -1 if unsuccessful, 0 if successful.
++
++Some file descriptor voodoo to allow for pipes across vforks. See
++open_mailer for details.
++ ----------------------------------------------------------------------*/
++init_tty_driver(ps)
++ struct pine *ps;
++{
++#ifdef MOUSE
++ if(F_ON(F_ENABLE_MOUSE, ps_global))
++ init_mouse();
++#endif /* MOUSE */
++
++ /* turn off talk permission by default */
++
++ if(F_ON(F_ALLOW_TALK, ps))
++ allow_talk(ps);
++ else
++ disallow_talk(ps);
++
++ return(PineRaw(1));
++}
++
++
++
++/*----------------------------------------------------------------------
++ Set or clear the specified tty mode
++
++ Args: ps -- struct pine
++ mode -- mode bits to modify
++ clear -- whether or not to clear or set
++
++ Result: tty driver mode change.
++ ----------------------------------------------------------------------*/
++void
++tty_chmod(ps, mode, func)
++ struct pine *ps;
++ int mode;
++ int func;
++{
++ char *tty_name;
++ int new_mode;
++ struct stat sbuf;
++ static int saved_mode = -1;
++
++ /* if no problem figuring out tty's name & mode? */
++ if((((tty_name = (char *) ttyname(STDIN_FD)) != NULL
++ && fstat(STDIN_FD, &sbuf) == 0)
++ || ((tty_name = (char *) ttyname(STDOUT_FD)) != NULL
++ && fstat(STDOUT_FD, &sbuf) == 0))
++ && !(func == TMD_RESET && saved_mode < 0)){
++ new_mode = (func == TMD_RESET)
++ ? saved_mode
++ : (func == TMD_CLEAR)
++ ? (sbuf.st_mode & ~mode)
++ : (sbuf.st_mode | mode);
++ /* assign tty new mode */
++ if(chmod(tty_name, new_mode) == 0){
++ if(func == TMD_RESET) /* forget we knew */
++ saved_mode = -1;
++ else if(saved_mode < 0)
++ saved_mode = sbuf.st_mode; /* remember original */
++ }
++ }
++}
++
++
++
++/*----------------------------------------------------------------------
++ End use of the tty, put it back into it's normal mode (UNIX)
++
++ Args: ps -- struct pine
++
++ Result: tty driver mode change.
++ ----------------------------------------------------------------------*/
++void
++end_tty_driver(ps)
++ struct pine *ps;
++{
++ ps = ps; /* get rid of unused parameter warning */
++
++#ifdef MOUSE
++ end_mouse();
++#endif /* MOUSE */
++ fflush(stdout);
++ dprint(2, (debugfile, "about to end_tty_driver\n"));
++
++ tty_chmod(ps, 0, TMD_RESET);
++ PineRaw(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Actually set up the tty driver (UNIX)
++
++ Args: state -- which state to put it in. 1 means go into raw, 0 out of
++
++ Result: returns 0 if successful and < 0 if not.
++ ----*/
++
++PineRaw(state)
++int state;
++{
++ int result;
++
++ result = Raw(state);
++
++ if(result == 0 && state == 1){
++ /*
++ * Only go into 8 bit mode if we are doing something other
++ * than plain ASCII. This will save the folks that have
++ * their parity on their serial lines wrong thr trouble of
++ * getting it right
++ */
++ if(ps_global->VAR_CHAR_SET && ps_global->VAR_CHAR_SET[0] &&
++ strucmp(ps_global->VAR_CHAR_SET, "us-ascii"))
++ bit_strip_off();
++
++#ifdef DEBUG
++ if(debug < 9) /* only on if full debugging set */
++#endif
++ quit_char_off();
++ ps_global->low_speed = ttisslow();
++ crlf_proc(0);
++ xonxoff_proc(F_ON(F_PRESERVE_START_STOP, ps_global));
++ }
++
++ return(result);
++}
++
++
++#ifdef RESIZING
++jmp_buf winch_state;
++int winch_occured = 0;
++int ready_for_winch = 0;
++#endif
++
++/*----------------------------------------------------------------------
++ This checks whether or not a character (UNIX)
++ is ready to be read, or it times out.
++
++ Args: time_out -- number of seconds before it will timeout
++
++ Result: Returns a NO_OP_IDLE or a NO_OP_COMMAND if the timeout expires
++ before input is available, or a KEY_RESIZE if a resize event
++ occurs, or READY_TO_READ if input is available before the timeout.
++ ----*/
++int
++check_for_timeout(time_out)
++ int time_out;
++{
++ int res;
++
++ fflush(stdout);
++
++#ifdef RESIZING
++ if(winch_occured || setjmp(winch_state) != 0){
++ ready_for_winch = 0;
++ fix_windsize(ps_global);
++
++ /*
++ * May need to unblock signal after longjmp from handler, because
++ * signal is normally unblocked upon routine exit from the handler.
++ */
++ if(!winch_occured)
++ our_sigunblock(SIGWINCH);
++
++ winch_occured = 0;
++ return(KEY_RESIZE);
++ }
++ else
++ ready_for_winch = 1;
++#endif /* RESIZING */
++
++ switch(res=input_ready(time_out)){
++ case BAIL_OUT:
++ read_bail(); /* non-tragic exit */
++ /* NO RETURN */
++
++ case PANIC_NOW:
++ panic1("Select error: %s\n", error_description(errno));
++ /* NO RETURN */
++
++ case READ_INTR:
++ res = NO_OP_COMMAND;
++ /* fall through */
++
++ case NO_OP_IDLE:
++ case NO_OP_COMMAND:
++ case READY_TO_READ:
++#ifdef RESIZING
++ ready_for_winch = 0;
++#endif
++ return(res);
++ }
++}
++
++
++
++/*----------------------------------------------------------------------
++ Read input characters with lots of processing for arrow keys and such (UNIX)
++
++ Args: time_out -- The timeout to for the reads
++
++ Result: returns the character read. Possible special chars.
++
++ This deals with function and arrow keys as well.
++
++ The idea is that this routine handles all escape codes so it done in
++ only one place. Especially so the back arrow key can work when entering
++ things on a line. Also so all function keys can be disabled and not
++ cause weird things to happen.
++ ---*/
++int
++read_char(time_out)
++ int time_out;
++{
++ int ch, status, cc;
++
++ /* Get input from initial-keystrokes */
++ if(process_config_input(&ch))
++ return(ch);
++
++ /*
++ * We only check for timeouts at the start of read_char, not in the
++ * middle of escape sequences.
++ */
++ if((ch = check_for_timeout(time_out)) != READY_TO_READ)
++ goto done;
++
++ ps_global->time_of_last_input = time((time_t *)0);
++ switch(status = kbseq(simple_ttgetc, key_recorder, read_bail, &ch)){
++ case KEY_DOUBLE_ESC:
++ /*
++ * Special hack to get around comm devices eating control characters.
++ */
++ if(check_for_timeout(5) != READY_TO_READ){
++ ch = KEY_JUNK; /* user typed ESC ESC, then stopped */
++ goto done;
++ }
++ else
++ ch = READ_A_CHAR();
++
++ ch &= 0x7f;
++ if(isdigit((unsigned char)ch)){
++ int n = 0, i = ch - '0';
++
++ if(i < 0 || i > 2){
++ ch = KEY_JUNK;
++ goto done; /* bogus literal char value */
++ }
++
++ while(n++ < 2){
++ if(check_for_timeout(5) != READY_TO_READ
++ || (!isdigit((unsigned char) (ch = READ_A_CHAR()))
++ || (n == 1 && i == 2 && ch > '5')
++ || (n == 2 && i == 25 && ch > '5'))){
++ ch = KEY_JUNK; /* user typed ESC ESC #, stopped */
++ goto done;
++ }
++
++ i = (i * 10) + (ch - '0');
++ }
++
++ ch = i;
++ }
++ else{
++ if(islower((unsigned char)ch)) /* canonicalize if alpha */
++ ch = toupper((unsigned char)ch);
++
++ ch = (isalpha((unsigned char)ch) || ch == '@'
++ || (ch >= '[' && ch <= '_'))
++ ? ctrl(ch) : ((ch == SPACE) ? ctrl('@'): ch);
++ }
++
++ goto done;
++
++#ifdef MOUSE
++ case KEY_XTERM_MOUSE:
++ if(mouseexist()){
++ /*
++ * Special hack to get mouse events from an xterm.
++ * Get the details, then pass it past the keymenu event
++ * handler, and then to the installed handler if there
++ * is one...
++ */
++ static int down = 0;
++ int x, y, button;
++ unsigned cmd;
++
++ clear_cursor_pos();
++ button = READ_A_CHAR() & 0x03;
++
++ x = READ_A_CHAR() - '!';
++ y = READ_A_CHAR() - '!';
++
++ ch = NO_OP_COMMAND;
++ if(button == 0){ /* xterm button 1 down */
++ down = 1;
++ if(checkmouse(&cmd, 1, x, y))
++ ch = (int)cmd;
++ }
++ else if (down && button == 3){
++ down = 0;
++ if(checkmouse(&cmd, 0, x, y))
++ ch = (int)cmd;
++ }
++
++ goto done;
++ }
++
++ break;
++#endif /* MOUSE */
++
++ case KEY_UP :
++ case KEY_DOWN :
++ case KEY_RIGHT :
++ case KEY_LEFT :
++ case KEY_PGUP :
++ case KEY_PGDN :
++ case KEY_HOME :
++ case KEY_END :
++ case KEY_DEL :
++ case PF1 :
++ case PF2 :
++ case PF3 :
++ case PF4 :
++ case PF5 :
++ case PF6 :
++ case PF7 :
++ case PF8 :
++ case PF9 :
++ case PF10 :
++ case PF11 :
++ case PF12 :
++ dprint(9, (debugfile, "Read char returning: %d %s\n",
++ status, pretty_command(status)));
++ return(status);
++
++ case KEY_SWALLOW_Z:
++ status = KEY_JUNK;
++ case KEY_SWAL_UP:
++ case KEY_SWAL_DOWN:
++ case KEY_SWAL_LEFT:
++ case KEY_SWAL_RIGHT:
++ do
++ if(check_for_timeout(2) != READY_TO_READ){
++ status = KEY_JUNK;
++ break;
++ }
++ while(!strchr("~qz", READ_A_CHAR()));
++ ch = (status == KEY_JUNK) ? status : status - (KEY_SWAL_UP - KEY_UP);
++ goto done;
++
++ case KEY_KERMIT:
++ do{
++ cc = ch;
++ if(check_for_timeout(2) != READY_TO_READ){
++ status = KEY_JUNK;
++ break;
++ }
++ else
++ ch = READ_A_CHAR();
++ }while(cc != '\033' && ch != '\\');
++
++ ch = KEY_JUNK;
++ goto done;
++
++ case BADESC:
++ ch = KEY_JUNK;
++ goto done;
++
++ case 0: /* regular character */
++ default:
++ /*
++ * we used to strip (ch &= 0x7f;), but this seems much cleaner
++ * in the face of line noise and has the benefit of making it
++ * tougher to emit mistakenly labeled MIME...
++ */
++ if((ch & 0x80) && (!ps_global->VAR_CHAR_SET
++ || !strucmp(ps_global->VAR_CHAR_SET, "US-ASCII"))){
++ dprint(9, (debugfile, "Read char returning: %d %s\n",
++ status, pretty_command(status)));
++ return(KEY_JUNK);
++ }
++ else if(ch == ctrl('Z')){
++ dprint(9, (debugfile, "Read char calling do_suspend\n"));
++ return(do_suspend());
++ }
++
++
++ done:
++ dprint(9, (debugfile, "Read char returning: %d %s\n",
++ ch, pretty_command(ch)));
++ return(ch);
++ }
++}
++
++
++/*----------------------------------------------------------------------
++ Reading input somehow failed and we need to shutdown now
++
++ Args: none
++
++ Result: pine exits
++
++ ---*/
++void
++read_bail()
++{
++ end_signals(1);
++ if(ps_global->inbox_stream){
++ if(ps_global->inbox_stream == ps_global->mail_stream)
++ ps_global->mail_stream = NULL;
++
++ if(!ps_global->inbox_stream->lock) /* shouldn't be... */
++ pine_close_stream(ps_global->inbox_stream);
++ }
++
++ if(ps_global->mail_stream && !ps_global->mail_stream->lock)
++ pine_close_stream(ps_global->mail_stream);
++
++ end_keyboard(F_ON(F_USE_FK,ps_global));
++ end_tty_driver(ps_global);
++ if(filter_data_file(0))
++ unlink(filter_data_file(0));
++
++ exit(0);
++}
++
++
++extern char term_name[];
++/* -------------------------------------------------------------------
++ Set up the keyboard -- usually enable some function keys (UNIX)
++
++ Args: struct pine
++
++So far all we do here is turn on keypad mode for certain terminals
++
++Hack for NCSA telnet on an IBM PC to put the keypad in the right mode.
++This is the same for a vtXXX terminal or [zh][12]9's which we have
++a lot of at UW
++ ----*/
++void
++init_keyboard(use_fkeys)
++ int use_fkeys;
++{
++ if(use_fkeys && (!strucmp(term_name,"vt102")
++ || !strucmp(term_name,"vt100")))
++ printf("\033\133\071\071\150");
++}
++
++
++
++/*----------------------------------------------------------------------
++ Clear keyboard, usually disable some function keys (UNIX)
++
++ Args: pine state (terminal type)
++
++ Result: keyboard state reset
++ ----*/
++void
++end_keyboard(use_fkeys)
++ int use_fkeys;
++{
++ if(use_fkeys && (!strcmp(term_name, "vt102")
++ || !strcmp(term_name, "vt100"))){
++ printf("\033\133\071\071\154");
++ fflush(stdout);
++ }
++}
++
++
++#ifdef _WINDOWS
++#line 3 "osdep/termin.gen"
++#endif
++/*
++ * Generic tty input routines
++ */
++
++
++/*----------------------------------------------------------------------
++ Read a character from keyboard with timeout
++ Input: none
++
++ Result: Returns command read via read_char
++ Times out and returns a null command every so often
++
++ Calculates the timeout for the read, and does a few other house keeping
++things. The duration of the timeout is set in pine.c.
++ ----------------------------------------------------------------------*/
++int
++read_command()
++{
++ int ch, tm = 0;
++ long dtime;
++
++ cancel_busy_alarm(-1);
++ tm = (messages_queued(&dtime) > 1) ? (int)dtime : timeo;
++
++ /*
++ * Before we sniff at the input queue, make sure no external event's
++ * changed our picture of the message sequence mapping. If so,
++ * recalculate the dang thing and run thru whatever processing loop
++ * we're in again...
++ */
++ if(ps_global->expunge_count){
++ q_status_message2(SM_ORDER, 3, 3,
++ "自資料匣 \"%s\" 中刪除 %s 封信件",
++ pretty_fn(ps_global->cur_folder),
++ long2string(ps_global->expunge_count));
++ ps_global->expunge_count = 0L;
++ display_message('x');
++ }
++
++ if(ps_global->inbox_expunge_count){
++ q_status_message2(SM_ORDER, 3, 3,
++ "自資料匣 \"%s\" 中刪除 %s 封信件",
++ pretty_fn(ps_global->inbox_name),
++ long2string(ps_global->inbox_expunge_count));
++ ps_global->inbox_expunge_count = 0L;
++ display_message('x');
++ }
++
++ if(ps_global->mail_box_changed && ps_global->new_mail_count){
++ dprint(2, (debugfile, "Noticed %ld new msgs! \n",
++ ps_global->new_mail_count));
++ return(NO_OP_COMMAND); /* cycle thru so caller can update */
++ }
++
++ ch = read_char(tm);
++ dprint(9, (debugfile, "Read command returning: %d %s\n", ch,
++ pretty_command(ch)));
++ if(ch != NO_OP_COMMAND && ch != NO_OP_IDLE && ch != KEY_RESIZE)
++ zero_new_mail_count();
++
++#ifdef BACKGROUND_POST
++ /*
++ * Any expired children to report on?
++ */
++ if(ps_global->post && ps_global->post->pid == 0){
++ int winner = 0;
++
++ if(ps_global->post->status < 0){
++ q_status_message(SM_ORDER | SM_DING, 3, 3, "徹底失敗!");
++ }
++ else{
++ (void) pine_send_status(ps_global->post->status,
++ ps_global->post->fcc, tmp_20k_buf,
++ &winner);
++ q_status_message(SM_ORDER | (winner ? 0 : SM_DING), 3, 3,
++ tmp_20k_buf);
++
++ }
++
++ if(!winner)
++ q_status_message(SM_ORDER, 0, 3,
++ "由 \"編修\" 再回答 \"是\" 來繼續重送 \"上次中斷的信件?\"");
++/*
++ "Re-send via \"Compose\" then \"Yes\" to \"Continue INTERRUPTED?\"");
++*/
++ if(ps_global->post->fcc)
++ fs_give((void **) &ps_global->post->fcc);
++
++ fs_give((void **) &ps_global->post);
++ }
++#endif
++
++ return(ch);
++}
++
++
++
++
++/*
++ *
++ */
++static struct display_line {
++ int row, col; /* where display starts */
++ int dlen; /* length of display line */
++ char *dl; /* line on display */
++ char *vl; /* virtual line */
++ int vlen; /* length of virtual line */
++ int vused; /* length of virtual line in use */
++ int vbase; /* first virtual char on display */
++} dline;
++
++
++
++static struct key oe_keys[] =
++ {{"^G","輔助說明",KS_SCREENHELP}, {"^C","取消",KS_NONE},
++ {"^T","xxx",KS_NONE}, {"Ret","同意",KS_NONE},
++ {NULL,NULL,KS_NONE}, {NULL,NULL,KS_NONE},
++ {NULL,NULL,KS_NONE}, {NULL,NULL,KS_NONE},
++ {NULL,NULL,KS_NONE}, {NULL,NULL,KS_NONE},
++ {NULL,NULL,KS_NONE}, {NULL,NULL,KS_NONE}};
++INST_KEY_MENU(oe_keymenu, oe_keys);
++#define OE_HELP_KEY 0
++#define OE_CANCEL_KEY 1
++#define OE_CTRL_T_KEY 2
++#define OE_ENTER_KEY 3
++
++
++/*----------------------------------------------------------------------
++ Prompt user for a string in status line with various options
++
++ Args: string -- the buffer result is returned in, and original string (if
++ any) is passed in.
++ y_base -- y position on screen to start on. 0,0 is upper left
++ negative numbers start from bottom
++ x_base -- column position on screen to start on. 0,0 is upper left
++ field_len -- Maximum length of string to accept
++ prompt -- The string to prompt with
++ escape_list -- pointer to array of ESCKEY_S's. input chars matching
++ those in list return value from list.
++ help -- Arrary of strings for help text in bottom screen lines
++ flags -- pointer (because some are return values) to flags
++ OE_USER_MODIFIED - Set on return if user modified buffer
++ OE_DISALLOW_CANCEL - No cancel in menu.
++ OE_DISALLOW_HELP - No help in menu.
++ OE_KEEP_TRAILING_SPACE - Allow trailing space.
++ OE_SEQ_SENSITIVE - Caller is sensitive to sequence
++ number changes.
++ OE_APPEND_CURRENT - String should not be truncated
++ before accepting user input.
++ OE_PASSWD - Don't echo on screen.
++
++ Result: editing input string
++ returns -1 unexpected errors
++ returns 0 normal entry typed (editing and return or PF2)
++ returns 1 typed ^C or PF2 (cancel)
++ returns 3 typed ^G or PF1 (help)
++ returns 4 typed ^L for a screen redraw
++
++ WARNING: Care is required with regard to the escape_list processing.
++ The passed array is terminated with an entry that has ch = -1.
++ Function key labels and key strokes need to be setup externally!
++ Traditionally, a return value of 2 is used for ^T escapes.
++
++ Unless in escape_list, tabs are trapped by isprint().
++This allows near full weemacs style editing in the line
++ ^A beginning of line
++ ^E End of line
++ ^R Redraw line
++ ^G Help
++ ^F forward
++ ^B backward
++ ^D delete
++----------------------------------------------------------------------*/
++
++optionally_enter(string, y_base, x_base, field_len,
++ prompt, escape_list, help, flags)
++ char *string, *prompt;
++ ESCKEY_S *escape_list;
++ HelpType help;
++ int x_base, y_base, field_len;
++ int *flags;
++{
++ register char *s2;
++ register int field_pos;
++ int i, j, return_v, cols, ch, prompt_len, too_thin,
++ real_y_base, km_popped, passwd;
++ char *saved_original = NULL, *k, *kb;
++ char *kill_buffer = NULL;
++ char **help_text;
++ int fkey_table[12];
++ struct key_menu *km;
++ bitmap_t bitmap;
++#ifdef _WINDOWS
++ int cursor_shown;
++#endif
++
++ dprint(5, (debugfile, "=== optionally_enter called ===\n"));
++ dprint(9, (debugfile, "string:\"%s\" y:%d x:%d length: %d append: %d\n",
++ string, x_base, y_base, field_len,
++ (flags && *flags & OE_APPEND_CURRENT)));
++ dprint(9, (debugfile, "passwd:%d prompt:\"%s\" label:\"%s\"\n",
++ (flags && *flags & OE_PASSWD),
++ prompt, (escape_list && escape_list[0].ch != -1)
++ ? escape_list[0].label: ""));
++
++#ifdef _WINDOWS
++ if (mswin_usedialog ()) {
++ MDlgButton button_list[12];
++ int b;
++ int i;
++
++ memset (&button_list, 0, sizeof (MDlgButton) * 12);
++ b = 0;
++ for (i = 0; escape_list && escape_list[i].ch != -1 && i < 11; ++i) {
++ if (escape_list[i].name != NULL
++ && escape_list[i].ch > 0 && escape_list[i].ch < 256) {
++ button_list[b].ch = escape_list[i].ch;
++ button_list[b].rval = escape_list[i].rval;
++ button_list[b].name = escape_list[i].name;
++ button_list[b].label = escape_list[i].label;
++ ++b;
++ }
++ }
++ button_list[b].ch = -1;
++
++
++ help_text = get_help_text (help);
++ return_v = mswin_dialog (prompt, string, field_len,
++ (flags && *flags & OE_APPEND_CURRENT),
++ (flags && *flags & OE_PASSWD),
++ button_list,
++ help_text, flags ? *flags : OE_NONE);
++ free_list_array (&help_text);
++ return (return_v);
++ }
++#endif
++
++ suspend_busy_alarm();
++ cols = ps_global->ttyo->screen_cols;
++ prompt_len = strlen(prompt);
++ too_thin = 0;
++ km_popped = 0;
++ if(y_base > 0) {
++ real_y_base = y_base;
++ } else {
++ real_y_base= y_base + ps_global->ttyo->screen_rows;
++ if(real_y_base < 2)
++ real_y_base = ps_global->ttyo->screen_rows;
++ }
++
++ flush_ordered_messages();
++ mark_status_dirty();
++ if(flags && *flags & OE_APPEND_CURRENT) /* save a copy in case of cancel */
++ saved_original = cpystr(string);
++
++ /*
++ * build the function key mapping table, skipping predefined keys...
++ */
++ memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
++ for(i = 0, j = 0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
++ if(i+j == OE_HELP_KEY)
++ j++;
++
++ if(i+j == OE_CANCEL_KEY)
++ j++;
++
++ if(i+j == OE_ENTER_KEY)
++ j++;
++
++ fkey_table[i+j] = escape_list[i].ch;
++ }
++
++#if defined(HELPFILE)
++ help_text = (help != NO_HELP) ? get_help_text(help) : (char **)NULL;
++#else
++ help_text = help;
++#endif
++ if(help_text){ /*---- Show help text -----*/
++ int width = ps_global->ttyo->screen_cols - x_base;
++
++ if(FOOTER_ROWS(ps_global) == 1){
++ km_popped++;
++ FOOTER_ROWS(ps_global) = 3;
++ clearfooter(ps_global);
++
++ y_base = -3;
++ real_y_base = y_base + ps_global->ttyo->screen_rows;
++ }
++
++ for(j = 0; j < 2 && help_text[j]; j++){
++ MoveCursor(real_y_base + 1 + j, x_base);
++ CleartoEOLN();
++
++ if(width < strlen(help_text[j])){
++ char *tmp = fs_get((width + 1) * sizeof(char));
++ strncpy(tmp, help_text[j], width);
++ tmp[width] = '\0';
++ PutLine0(real_y_base + 1 + j, x_base, tmp);
++ fs_give((void **)&tmp);
++ }
++ else
++ PutLine0(real_y_base + 1 + j, x_base, help_text[j]);
++ }
++
++#if defined(HELPFILE)
++ free_list_array(&help_text);
++#endif
++
++ } else {
++ clrbitmap(bitmap);
++ clrbitmap((km = &oe_keymenu)->bitmap); /* force formatting */
++ if(!(flags && (*flags) & OE_DISALLOW_HELP))
++ setbitn(OE_HELP_KEY, bitmap);
++
++ setbitn(OE_ENTER_KEY, bitmap);
++ if(!(flags && (*flags) & OE_DISALLOW_CANCEL))
++ setbitn(OE_CANCEL_KEY, bitmap);
++
++ setbitn(OE_CTRL_T_KEY, bitmap);
++
++ /*---- Show the usual possible keys ----*/
++ for(i=0,j=0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
++ if(i+j == OE_HELP_KEY)
++ j++;
++
++ if(i+j == OE_CANCEL_KEY)
++ j++;
++
++ if(i+j == OE_ENTER_KEY)
++ j++;
++
++ oe_keymenu.keys[i+j].label = escape_list[i].label;
++ oe_keymenu.keys[i+j].name = escape_list[i].name;
++ setbitn(i+j, bitmap);
++ }
++
++ for(i = i+j; i < 12; i++)
++ if(!(i == OE_HELP_KEY || i == OE_ENTER_KEY || i == OE_CANCEL_KEY))
++ oe_keymenu.keys[i].name = NULL;
++
++ draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global), 0, FirstMenu);
++ }
++
++ StartInverse(); /* Always in inverse */
++
++ /*
++ * if display length isn't wide enough to support input,
++ * shorten up the prompt...
++ */
++ if((dline.dlen = cols - (x_base + prompt_len + 1)) < 5){
++ prompt_len += (dline.dlen - 5); /* adding negative numbers */
++ prompt -= (dline.dlen - 5); /* subtracting negative numbers */
++ dline.dlen = 5;
++ }
++
++ dline.dl = fs_get((size_t)dline.dlen + 1);
++ memset((void *)dline.dl, 0, (size_t)(dline.dlen + 1) * sizeof(char));
++ dline.row = real_y_base;
++ dline.col = x_base + prompt_len;
++ dline.vl = string;
++ dline.vlen = --field_len; /* -1 for terminating NULL */
++ dline.vbase = field_pos = 0;
++
++#ifdef _WINDOWS
++ cursor_shown = mswin_showcursor(1);
++#endif
++
++ PutLine0(real_y_base, x_base, prompt);
++ /* make sure passed in string is shorter than field_len */
++ /* and adjust field_pos.. */
++
++ while((flags && *flags & OE_APPEND_CURRENT) &&
++ field_pos < field_len && string[field_pos] != '\0')
++ field_pos++;
++
++ string[field_pos] = '\0';
++ dline.vused = (int)(&string[field_pos] - string);
++ passwd = (flags && *flags & OE_PASSWD) ? 1 : 0;
++ line_paint(field_pos, &passwd);
++
++ /*----------------------------------------------------------------------
++ The main loop
++
++ here field_pos is the position in the string.
++ s always points to where we are in the string.
++ loops until someone sets the return_v.
++ ----------------------------------------------------------------------*/
++ return_v = -10;
++
++#ifdef _WINDOWS
++ mswin_allowpaste(MSWIN_PASTE_LINE);
++#endif
++
++ while(return_v == -10) {
++ /* Timeout 10 min to keep imap mail stream alive */
++ ch = read_char(600);
++
++ /*
++ * Don't want to intercept all characters if typing in passwd.
++ * We select an ad hoc set that we will catch and let the rest
++ * through. We would have caught the set below in the big switch
++ * but we skip the switch instead. Still catch things like ^K,
++ * DELETE, ^C, RETURN.
++ */
++ if(passwd)
++ switch(ch) {
++ case ctrl('F'):
++ case KEY_RIGHT:
++ case ctrl('B'):
++ case KEY_LEFT:
++ case ctrl('U'):
++ case ctrl('A'):
++ case KEY_HOME:
++ case ctrl('E'):
++ case KEY_END:
++ case TAB:
++ goto ok_for_passwd;
++ }
++
++ if(too_thin && ch != KEY_RESIZE && ch != ctrl('Z') && ch != ctrl('C'))
++ goto bleep;
++
++ switch(ch) {
++
++ /*--------------- KEY RIGHT ---------------*/
++ case ctrl('F'):
++ case KEY_RIGHT:
++ if(field_pos >= field_len || string[field_pos] == '\0')
++ goto bleep;
++
++ line_paint(++field_pos, &passwd);
++ break;
++
++ /*--------------- KEY LEFT ---------------*/
++ case ctrl('B'):
++ case KEY_LEFT:
++ if(field_pos <= 0)
++ goto bleep;
++
++ line_paint(--field_pos, &passwd);
++ break;
++
++ /*-------------------- WORD SKIP --------------------*/
++ case ctrl('@'):
++ /*
++ * Note: read_char *can* return NO_OP_COMMAND which is
++ * the def'd with the same value as ^@ (NULL), BUT since
++ * read_char has a big timeout (>25 secs) it won't.
++ */
++
++ /* skip thru current word */
++ while(string[field_pos]
++ && isalnum((unsigned char) string[field_pos]))
++ field_pos++;
++
++ /* skip thru current white space to next word */
++ while(string[field_pos]
++ && !isalnum((unsigned char) string[field_pos]))
++ field_pos++;
++
++ line_paint(field_pos, &passwd);
++ break;
++
++ /*-------------------- RETURN --------------------*/
++ case PF4:
++ if(F_OFF(F_USE_FK,ps_global)) goto bleep;
++ case ctrl('J'):
++ case ctrl('M'):
++ return_v = 0;
++ break;
++
++ /*-------------------- Destructive backspace --------------------*/
++ case '\177': /* DEL */
++ case ctrl('H'):
++ /* Try and do this with by telling the terminal to delete a
++ a character. If that fails, then repaint the rest of the
++ line, acheiving the same much less efficiently
++ */
++ if(field_pos <= 0)
++ goto bleep;
++
++ field_pos--;
++ /* drop thru to pull line back ... */
++
++ /*-------------------- Delete char --------------------*/
++ case ctrl('D'):
++ case KEY_DEL:
++ if(field_pos >= field_len || !string[field_pos])
++ goto bleep;
++
++ dline.vused--;
++ for(s2 = &string[field_pos]; *s2 != '\0'; s2++)
++ *s2 = s2[1];
++
++ *s2 = '\0'; /* Copy last NULL */
++ line_paint(field_pos, &passwd);
++ if(flags) /* record change if requested */
++ *flags |= OE_USER_MODIFIED;
++
++ break;
++
++
++ /*--------------- Kill line -----------------*/
++ case ctrl('K'):
++ if(kill_buffer != NULL)
++ fs_give((void **)&kill_buffer);
++
++ if(field_pos != 0 || string[0]){
++ if(!passwd && F_ON(F_DEL_FROM_DOT, ps_global))
++ dline.vused -= strlen(&string[i = field_pos]);
++ else
++ dline.vused = i = 0;
++
++ kill_buffer = cpystr(&string[field_pos = i]);
++ string[field_pos] = '\0';
++ line_paint(field_pos, &passwd);
++ if(flags) /* record change if requested */
++ *flags |= OE_USER_MODIFIED;
++
++ }
++
++ break;
++
++ /*------------------- Undelete line --------------------*/
++ case ctrl('U'):
++ if(kill_buffer == NULL)
++ goto bleep;
++
++ /* Make string so it will fit */
++ kb = cpystr(kill_buffer);
++ dprint(2, (debugfile,
++ "Undelete: %d %d\n", strlen(string), field_len));
++ if(strlen(kb) + strlen(string) > field_len)
++ kb[field_len - strlen(string)] = '\0';
++ dprint(2, (debugfile,
++ "Undelete: %d %d\n", field_len - strlen(string),
++ strlen(kb)));
++
++ if(string[field_pos] == '\0') {
++ /*--- adding to the end of the string ----*/
++ for(k = kb; *k; k++)
++ string[field_pos++] = *k;
++
++ string[field_pos] = '\0';
++ } else {
++ goto bleep;
++ /* To lazy to do insert in middle of string now */
++ }
++
++ if(*kb && flags) /* record change if requested */
++ *flags |= OE_USER_MODIFIED;
++
++ dline.vused = strlen(string);
++ fs_give((void **)&kb);
++ line_paint(field_pos, &passwd);
++ break;
++
++
++ /*-------------------- Interrupt --------------------*/
++ case ctrl('C'): /* ^C */
++ if(F_ON(F_USE_FK,ps_global)
++ || (flags && ((*flags) & OE_DISALLOW_CANCEL)))
++ goto bleep;
++
++ goto cancel;
++
++ case PF2:
++ if(F_OFF(F_USE_FK,ps_global)
++ || (flags && ((*flags) & OE_DISALLOW_CANCEL)))
++ goto bleep;
++
++ cancel:
++ return_v = 1;
++ if(saved_original)
++ strcpy(string, saved_original);
++
++ break;
++
++
++ case ctrl('A'):
++ case KEY_HOME:
++ /*-------------------- Start of line -------------*/
++ line_paint(field_pos = 0, &passwd);
++ break;
++
++
++ case ctrl('E'):
++ case KEY_END:
++ /*-------------------- End of line ---------------*/
++ line_paint(field_pos = dline.vused, &passwd);
++ break;
++
++
++ /*-------------------- Help --------------------*/
++ case ctrl('G') :
++ case PF1:
++ if(flags && ((*flags) & OE_DISALLOW_HELP))
++ goto bleep;
++ else if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
++ km_popped++;
++ FOOTER_ROWS(ps_global) = 3;
++ clearfooter(ps_global);
++ EndInverse();
++ draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global),
++ 0, FirstMenu);
++ StartInverse();
++ mark_keymenu_dirty();
++ y_base = -3;
++ dline.row = real_y_base = y_base + ps_global->ttyo->screen_rows;
++ PutLine0(real_y_base, x_base, prompt);
++ fs_resize((void **)&dline.dl, (size_t)dline.dlen + 1);
++ memset((void *)dline.dl, 0, (size_t)(dline.dlen + 1));
++ line_paint(field_pos, &passwd);
++ break;
++ }
++
++ if(FOOTER_ROWS(ps_global) > 1){
++ mark_keymenu_dirty();
++ return_v = 3;
++ }
++ else
++ goto bleep;
++
++ break;
++
++ case NO_OP_IDLE:
++ /* Keep mail stream alive */
++ i = new_mail(0, 2, NM_DEFER_SORT);
++ if(ps_global->expunge_count &&
++ flags && ((*flags) & OE_SEQ_SENSITIVE))
++ goto cancel;
++
++ if(i < 0)
++ break; /* no changes, get on with life */
++ /* Else fall into redraw */
++
++ /*-------------------- Redraw --------------------*/
++ case ctrl('L'):
++ /*---------------- re size ----------------*/
++ case KEY_RESIZE:
++
++ dline.row = real_y_base = y_base > 0 ? y_base :
++ y_base + ps_global->ttyo->screen_rows;
++ EndInverse();
++ ClearScreen();
++ redraw_titlebar();
++ if(ps_global->redrawer != (void (*)())NULL)
++ (*ps_global->redrawer)();
++
++ redraw_keymenu();
++ StartInverse();
++
++ PutLine0(real_y_base, x_base, prompt);
++ cols = ps_global->ttyo->screen_cols;
++ too_thin = 0;
++ if(cols < x_base + prompt_len + 4) {
++ Writechar(BELL, 0);
++ PutLine0(real_y_base, 0, "Screen's too thin. Ouch!");
++ too_thin = 1;
++ } else {
++ dline.col = x_base + prompt_len;
++ dline.dlen = cols - (x_base + prompt_len + 1);
++ fs_resize((void **)&dline.dl, (size_t)dline.dlen + 1);
++ memset((void *)dline.dl, 0, (size_t)(dline.dlen + 1));
++ line_paint(field_pos, &passwd);
++ }
++ fflush(stdout);
++
++ dprint(9, (debugfile,
++ "optionally_enter RESIZE new_cols:%d too_thin: %d\n",
++ cols, too_thin));
++ break;
++
++ case PF3 : /* input to potentially remap */
++ case PF5 :
++ case PF6 :
++ case PF7 :
++ case PF8 :
++ case PF9 :
++ case PF10 :
++ case PF11 :
++ case PF12 :
++ if(F_ON(F_USE_FK,ps_global)
++ && fkey_table[ch - PF1] != NO_OP_COMMAND)
++ ch = fkey_table[ch - PF1]; /* remap function key input */
++
++ default:
++ if(escape_list){ /* in the escape key list? */
++ for(j=0; escape_list[j].ch != -1; j++){
++ if(escape_list[j].ch == ch){
++ return_v = escape_list[j].rval;
++ break;
++ }
++ }
++
++ if(return_v != -10)
++ break;
++ }
++
++ if(iscntrl(ch & 0x7f)){
++ bleep:
++ putc(BELL, stdout);
++ continue;
++ }
++
++ ok_for_passwd:
++ /*--- Insert a character -----*/
++ if(dline.vused >= field_len)
++ goto bleep;
++
++ /*---- extending the length of the string ---*/
++ for(s2 = &string[++dline.vused]; s2 - string > field_pos; s2--)
++ *s2 = *(s2-1);
++ string[field_pos++] = ch;
++ line_paint(field_pos, &passwd);
++ if(flags) /* record change if requested */
++ *flags |= OE_USER_MODIFIED;
++
++ } /*---- End of switch on char ----*/
++ }
++
++#ifdef _WINDOWS
++ if(!cursor_shown)
++ mswin_showcursor(0);
++
++ mswin_allowpaste(MSWIN_PASTE_DISABLE);
++#endif
++ fs_give((void **)&dline.dl);
++ if(saved_original)
++ fs_give((void **)&saved_original);
++
++ if(kill_buffer)
++ fs_give((void **)&kill_buffer);
++
++ if (!(flags && (*flags) & OE_KEEP_TRAILING_SPACE))
++ removing_trailing_white_space(string);
++ EndInverse();
++ MoveCursor(real_y_base, x_base); /* Move the cursor to show we're done */
++ fflush(stdout);
++ resume_busy_alarm(0);
++ if(km_popped){
++ FOOTER_ROWS(ps_global) = 1;
++ clearfooter(ps_global);
++ ps_global->mangled_body = 1;
++ }
++
++ return(return_v);
++}
++
++
++/*
++ * line_paint - where the real work of managing what is displayed gets done.
++ * The passwd variable is overloaded: if non-zero, don't
++ * output anything, else only blat blank chars across line
++ * once and use this var to tell us we've already written the
++ * line.
++ */
++void
++line_paint(offset, passwd)
++ int offset; /* current dot offset into line */
++ int *passwd; /* flag to hide display of chars */
++{
++ register char *pfp, *pbp;
++ register char *vfp, *vbp;
++ int extra = 0;
++#define DLEN (dline.vbase + dline.dlen)
++
++ /*
++ * for now just leave line blank, but maybe do '*' for each char later
++ */
++ if(*passwd){
++ if(*passwd > 1)
++ return;
++ else
++ *passwd == 2; /* only blat once */
++
++ extra = 0;
++ MoveCursor(dline.row, dline.col);
++ while(extra++ < dline.dlen)
++ Writechar(' ', 0);
++
++ MoveCursor(dline.row, dline.col);
++ return;
++ }
++
++ /* adjust right margin */
++ while(offset >= DLEN + ((dline.vused > DLEN) ? -1 : 1))
++ dline.vbase += dline.dlen/2;
++
++ /* adjust left margin */
++ while(offset < dline.vbase + ((dline.vbase) ? 2 : 0))
++ dline.vbase = max(dline.vbase - (dline.dlen/2), 0);
++
++ if(dline.vbase){ /* off screen cue left */
++ vfp = &dline.vl[dline.vbase+1];
++ pfp = &dline.dl[1];
++ if(dline.dl[0] != '<'){
++ MoveCursor(dline.row, dline.col);
++ Writechar(dline.dl[0] = '<', 0);
++ }
++ }
++ else{
++ vfp = dline.vl;
++ pfp = dline.dl;
++ if(dline.dl[0] == '<'){
++ MoveCursor(dline.row, dline.col);
++ Writechar(dline.dl[0] = ' ', 0);
++ }
++ }
++
++ if(dline.vused > DLEN){ /* off screen right... */
++ vbp = vfp + (long)(dline.dlen-(dline.vbase ? 2 : 1));
++ pbp = pfp + (long)(dline.dlen-(dline.vbase ? 2 : 1));
++ if(pbp[1] != '>'){
++ MoveCursor(dline.row, dline.col+dline.dlen);
++ Writechar(pbp[1] = '>', 0);
++ }
++ }
++ else{
++ extra = dline.dlen - (dline.vused - dline.vbase);
++ vbp = &dline.vl[max(0, dline.vused-1)];
++ pbp = &dline.dl[dline.dlen];
++ if(pbp[0] == '>'){
++ MoveCursor(dline.row, dline.col+dline.dlen);
++ Writechar(pbp[0] = ' ', 0);
++ }
++ }
++
++ while(*pfp == *vfp && vfp < vbp) /* skip like chars */
++ pfp++, vfp++;
++
++ if(pfp == pbp && *pfp == *vfp){ /* nothing to paint! */
++ MoveCursor(dline.row, dline.col + (offset - dline.vbase));
++ return;
++ }
++
++ /* move backward thru like characters */
++ if(extra){
++ while(extra >= 0 && *pbp == ' ') /* back over spaces */
++ extra--, pbp--;
++
++ while(extra >= 0) /* paint new ones */
++ pbp[-(extra--)] = ' ';
++ }
++
++ if((vbp - vfp) == (pbp - pfp)){ /* space there? */
++ while((*pbp == *vbp) && pbp != pfp) /* skip like chars */
++ pbp--, vbp--;
++ }
++
++ if(pfp != pbp || *pfp != *vfp){ /* anything to paint?*/
++ MoveCursor(dline.row, dline.col + (int)(pfp - dline.dl));
++
++ do
++ Writechar((unsigned char)((vfp <= vbp && *vfp)
++ ? ((*pfp = *vfp++) == TAB) ? ' ' : *pfp
++ : (*pfp = ' ')), 0);
++ while(++pfp <= pbp);
++ }
++
++ MoveCursor(dline.row, dline.col + (offset - dline.vbase));
++}
++
++
++
++/*----------------------------------------------------------------------
++ Check to see if the given command is reasonably valid
++
++ Args: ch -- the character to check
++
++ Result: A valid command is returned, or a well know bad command is returned.
++
++ ---*/
++validatekeys(ch)
++ int ch;
++{
++#ifndef _WINDOWS
++ if(F_ON(F_USE_FK,ps_global)) {
++ if(ch >= 'a' && ch <= 'z')
++ return(KEY_JUNK);
++ } else {
++ if(ch >= PF1 && ch <= PF12)
++ return(KEY_JUNK);
++ }
++#else
++ /*
++ * In windows menu items are bound to a single key command which
++ * gets inserted into the input stream as if the user had typed
++ * that key. But all the menues are bonund to alphakey commands,
++ * not PFkeys. to distinguish between a keyboard command and a
++ * menu command we insert a flag (KEY_MENU_FLAG) into the
++ * command value when setting up the bindings in
++ * configure_menu_items(). Here we strip that flag.
++ */
++ if(F_ON(F_USE_FK,ps_global)) {
++ if(ch >= 'a' && ch <= 'z' && !(ch & KEY_MENU_FLAG))
++ return(KEY_JUNK);
++ ch &= ~ KEY_MENU_FLAG;
++ } else {
++ ch &= ~ KEY_MENU_FLAG;
++ if(ch >= PF1 && ch <= PF12)
++ return(KEY_JUNK);
++ }
++#endif
++
++ return(ch);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Prepend config'd commands to keyboard input
++
++ Args: ch -- pointer to storage for returned command
++
++ Returns: TRUE if we're passing back a useful command, FALSE otherwise
++
++ ---*/
++int
++process_config_input(ch)
++ int *ch;
++{
++ static char firsttime = (char) 1;
++
++ /* commands in config file */
++ if(ps_global->initial_cmds && *ps_global->initial_cmds) {
++ /*
++ * There are a few commands that may require keyboard input before
++ * we enter the main command loop. That input should be interactive,
++ * not from our list of initial keystrokes.
++ */
++ if(ps_global->dont_use_init_cmds)
++ return(0);
++
++ *ch = *ps_global->initial_cmds++;
++ if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){
++ fs_give((void **)&(ps_global->free_initial_cmds));
++ ps_global->initial_cmds = 0;
++ }
++
++ return(1);
++ }
++
++ if(firsttime) {
++ firsttime = 0;
++ if(ps_global->in_init_seq) {
++ ps_global->in_init_seq = 0;
++ ps_global->save_in_init_seq = 0;
++ clear_cursor_pos();
++ F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
++ /* draw screen */
++ *ch = ctrl('L');
++ return(1);
++ }
++ }
++
++ return(0);
++}
++
++
++#define TAPELEN 256
++static int tape[TAPELEN];
++static long recorded = 0L;
++static short length = 0;
++
++
++/*
++ * record user keystrokes
++ *
++ * Args: ch -- the character to record
++ *
++ * Returns: character recorded
++ */
++int
++key_recorder(ch)
++ int ch;
++{
++ tape[recorded++ % TAPELEN] = ch;
++ if(length < TAPELEN)
++ length++;
++
++ return(ch);
++}
++
++
++/*
++ * playback user keystrokes
++ *
++ * Args: ch -- ignored
++ *
++ * Returns: character played back or -1 to indicate end of tape
++ */
++int
++key_playback(ch)
++ int ch;
++{
++ ch = length ? tape[(recorded + TAPELEN - length--) % TAPELEN] : -1;
++ return(ch);
++}
++
++
++
++/*======================================================================
++ Routines for painting the screen
++ - figure out what the terminal type is
++ - deal with screen size changes
++ - save special output sequences
++ - the usual screen clearing, cursor addressing and scrolling
++
++
++ This library gives programs the ability to easily access the
++ termcap information and write screen oriented and raw input
++ programs. The routines can be called as needed, except that
++ to use the cursor / screen routines there must be a call to
++ InitScreen() first. The 'Raw' input routine can be used
++ independently, however. (Elm comment)
++
++ Not sure what the original source of this code was. It got to be
++ here as part of ELM. It has been changed significantly from the
++ ELM version to be more robust in the face of inconsistent terminal
++ autowrap behaviour. Also, the unused functions were removed, it was
++ made to pay attention to the window size, and some code was made nicer
++ (in my opinion anyways). It also outputs the terminal initialization
++ strings and provides for minimal scrolling and detects terminals
++ with out enough capabilities. (Pine comment, 1990)
++
++
++This code used to pay attention to the "am" auto margin and "xn"
++new line glitch fields, but they were so often incorrect because many
++terminals can be configured to do either that we've taken it out. It
++now assumes it dosn't know where the cursor is after outputing in the
++80th column.
++*/
++
++#define PUTLINE_BUFLEN 256
++
++static int _lines, _columns;
++static int _line = FARAWAY;
++static int _col = FARAWAY;
++static int _in_inverse;
++
++
++/*
++ * Internal prototypes
++ */
++static void moveabsolute PROTO((int, int));
++static void CursorUp PROTO((int));
++static void CursorDown PROTO((int));
++static void CursorLeft PROTO((int));
++static void CursorRight PROTO((int));
++
++
++extern char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
++ *_setinverse, *_clearinverse,
++ *_setunderline, *_clearunderline,
++ *_setbold, *_clearbold,
++ *_cleartoeoln, *_cleartoeos,
++ *_startinsert, *_endinsert, *_insertchar, *_deletechar,
++ *_deleteline, *_insertline,
++ *_scrollregion, *_scrollup, *_scrolldown,
++ *_termcap_init, *_termcap_end;
++extern char term_name[];
++extern int _tlines, _tcolumns;
++
++static enum {NoScroll,UseScrollRegion,InsertDelete} _scrollmode;
++
++char *tgoto(); /* and the goto stuff */
++
++
++
++/*----------------------------------------------------------------------
++ Initialize the screen for output, set terminal type, etc
++
++ Args: tt -- Pointer to variable to store the tty output structure.
++
++ Result: terminal size is discovered and set in pine state
++ termcap entry is fetched and stored
++ make sure terminal has adequate capabilites
++ evaluate scrolling situation
++ returns status of indicating the state of the screen/termcap entry
++
++ Returns:
++ -1 indicating no terminal name associated with this shell,
++ -2..-n No termcap for this terminal type known
++ -3 Can't open termcap file
++ -4 Terminal not powerful enough - missing clear to eoln or screen
++ or cursor motion
++ ----*/
++int
++config_screen(tt)
++ struct ttyo **tt;
++{
++ struct ttyo *ttyo;
++ int err;
++
++ ttyo = (struct ttyo *)fs_get(sizeof (struct ttyo));
++
++ _line = 0; /* where are we right now?? */
++ _col = 0; /* assume zero, zero... */
++
++ /*
++ * This is an ugly hack to let vtterminalinfo know it's being called
++ * from pine.
++ */
++ Pmaster = (PICO *)1;
++ if(err = vtterminalinfo(F_ON(F_TCAP_WINS, ps_global)))
++ return(err);
++
++ Pmaster = NULL;
++
++ if(_tlines <= 0)
++ _lines = DEFAULT_LINES_ON_TERMINAL;
++ else
++ _lines = _tlines;
++
++ if(_tcolumns <= 0)
++ _columns = DEFAULT_COLUMNS_ON_TERMINAL;
++ else
++ _columns = _tcolumns;
++
++ get_windsize(ttyo);
++
++ ttyo->header_rows = 2;
++ ttyo->footer_rows = 3;
++
++ /*---- Make sure this terminal has the capability.
++ All we need is cursor address, clear line, and
++ reverse video.
++ ---*/
++ if(_moveto == NULL || _cleartoeoln == NULL ||
++ _setinverse == NULL || _clearinverse == NULL) {
++ return(-4);
++ }
++
++ dprint(1, (debugfile, "Terminal type: %s\n", term_name));
++
++ /*------ Figure out scrolling mode -----*/
++ if(_scrollregion != NULL && _scrollregion[0] != '\0' &&
++ _scrollup != NULL && _scrollup[0] != '\0'){
++ _scrollmode = UseScrollRegion;
++ } else if(_insertline != NULL && _insertline[0] != '\0' &&
++ _deleteline != NULL && _deleteline[0] != '\0') {
++ _scrollmode = InsertDelete;
++ } else {
++ _scrollmode = NoScroll;
++ }
++ dprint(7, (debugfile, "Scroll mode: %s\n",
++ _scrollmode==NoScroll ? "No Scroll" :
++ _scrollmode==InsertDelete ? "InsertDelete" : "Scroll Regions"));
++
++ if (!_left) {
++ _left = "\b";
++ }
++
++ *tt = ttyo;
++
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Initialize the screen with the termcap string
++ ----*/
++void
++init_screen()
++{
++ if(_termcap_init) /* init using termcap's rule */
++ tputs(_termcap_init, 1, outchar);
++
++ /* and make sure there are no scrolling surprises! */
++ BeginScroll(0, ps_global->ttyo->screen_rows - 1);
++ /* and make sure icon text starts out consistent */
++ icon_text(NULL);
++ fflush(stdout);
++}
++
++
++
++
++/*----------------------------------------------------------------------
++ Get the current window size
++
++ Args: ttyo -- pointer to structure to store window size in
++
++ NOTE: we don't override the given values unless we know better
++ ----*/
++int
++get_windsize(ttyo)
++struct ttyo *ttyo;
++{
++#ifdef RESIZING
++ struct winsize win;
++
++ /*
++ * Get the window size from the tty driver. If we can't fish it from
++ * stdout (pine's output is directed someplace else), try stdin (which
++ * *must* be associated with the terminal; see init_tty_driver)...
++ */
++ if(ioctl(1, TIOCGWINSZ, &win) >= 0 /* 1 is stdout */
++ || ioctl(0, TIOCGWINSZ, &win) >= 0){ /* 0 is stdin */
++ if(win.ws_row)
++ _lines = min(win.ws_row, MAX_SCREEN_ROWS);
++
++ if(win.ws_col)
++ _columns = min(win.ws_col, MAX_SCREEN_COLS);
++
++ dprint(2, (debugfile, "new win size -----<%d %d>------\n",
++ _lines, _columns));
++ }
++ else
++ /* Depending on the OS, the ioctl() may have failed because
++ of a 0 rows, 0 columns setting. That happens on DYNIX/ptx 1.3
++ (with a kernel patch that happens to involve the negotiation
++ of window size in the telnet streams module.) In this case
++ the error is EINVARG. Leave the default settings. */
++ dprint(1, (debugfile, "ioctl(TIOCWINSZ) failed :%s\n",
++ error_description(errno)));
++#endif
++
++ ttyo->screen_cols = min(_columns, MAX_SCREEN_COLS);
++ ttyo->screen_rows = min(_lines, MAX_SCREEN_ROWS);
++ return(0);
++}
++
++
++/*----------------------------------------------------------------------
++ End use of the screen.
++ Print status message, if any.
++ Flush status messages.
++ ----*/
++void
++end_screen(message)
++ char *message;
++{
++ int footer_rows_was_one = 0;
++
++ dprint(9, (debugfile, "end_screen called\n"));
++
++ if(FOOTER_ROWS(ps_global) == 1){
++ footer_rows_was_one++;
++ FOOTER_ROWS(ps_global) = 3;
++ mark_status_unknown();
++ }
++
++ flush_status_messages(1);
++ blank_keymenu(_lines - 2, 0);
++ MoveCursor(_lines - 2, 0);
++ if(_termcap_end != NULL)
++ tputs(_termcap_end, 1, outchar);
++
++ if(message){
++ printf("%s\r\n", message);
++ }
++
++ if(F_ON(F_ENABLE_XTERM_NEWMAIL, ps_global) && getenv("DISPLAY"))
++ icon_text("xterm");
++
++ fflush(stdout);
++
++ if(footer_rows_was_one){
++ FOOTER_ROWS(ps_global) = 1;
++ mark_status_unknown();
++ }
++}
++
++
++
++/*----------------------------------------------------------------------
++ Clear the terminal screen
++
++ Result: The screen is cleared
++ internal cursor position set to 0,0
++ ----*/
++void
++ClearScreen()
++{
++ _line = 0; /* clear leaves us at top... */
++ _col = 0;
++
++ if(ps_global->in_init_seq)
++ return;
++
++ mark_status_unknown();
++ mark_keymenu_dirty();
++ mark_titlebar_dirty();
++
++ if(!_clearscreen){
++ ClearLines(0, _lines-1);
++ MoveCursor(0, 0);
++ }
++ else{
++ tputs(_clearscreen, 1, outchar);
++ moveabsolute(0, 0); /* some clearscreens don't move correctly */
++ }
++}
++
++
++/*----------------------------------------------------------------------
++ Internal move cursor to absolute position
++
++ Args: col -- column to move cursor to
++ row -- row to move cursor to
++
++ Result: cursor is moved (variables, not updates)
++ ----*/
++
++static void
++moveabsolute(col, row)
++{
++
++ char *stuff, *tgoto();
++
++ stuff = tgoto(_moveto, col, row);
++ tputs(stuff, 1, outchar);
++}
++
++
++/*----------------------------------------------------------------------
++ Move the cursor to the row and column number
++ Args: row number
++ column number
++
++ Result: Cursor moves
++ internal position updated
++ ----*/
++void
++MoveCursor(row, col)
++ int row, col;
++{
++ /** move cursor to the specified row column on the screen.
++ 0,0 is the top left! **/
++
++ int scrollafter = 0;
++
++ /* we don't want to change "rows" or we'll mangle scrolling... */
++
++ if(ps_global->in_init_seq)
++ return;
++
++ if (col < 0)
++ col = 0;
++ if (col >= ps_global->ttyo->screen_cols)
++ col = ps_global->ttyo->screen_cols - 1;
++ if (row < 0)
++ row = 0;
++ if (row > ps_global->ttyo->screen_rows) {
++ if (col == 0)
++ scrollafter = row - ps_global->ttyo->screen_rows;
++ row = ps_global->ttyo->screen_rows;
++ }
++
++ if (!_moveto)
++ return;
++
++ if (row == _line) {
++ if (col == _col)
++ return; /* already there! */
++
++ else if (abs(col - _col) < 5) { /* within 5 spaces... */
++ if (col > _col && _right)
++ CursorRight(col - _col);
++ else if (col < _col && _left)
++ CursorLeft(_col - col);
++ else
++ moveabsolute(col, row);
++ }
++ else /* move along to the new x,y loc */
++ moveabsolute(col, row);
++ }
++ else if (col == _col && abs(row - _line) < 5) {
++ if (row < _line && _up)
++ CursorUp(_line - row);
++ else if (_line > row && _down)
++ CursorDown(row - _line);
++ else
++ moveabsolute(col, row);
++ }
++ else if (_line == row-1 && col == 0) {
++ putchar('\n'); /* that's */
++ putchar('\r'); /* easy! */
++ }
++ else
++ moveabsolute(col, row);
++
++ _line = row; /* to ensure we're really there... */
++ _col = col;
++
++ if (scrollafter) {
++ while (scrollafter--) {
++ putchar('\n');
++ putchar('\r');
++
++ }
++ }
++
++ return;
++}
++
++
++
++/*----------------------------------------------------------------------
++ Newline, move the cursor to the start of next line
++
++ Result: Cursor moves
++ ----*/
++void
++NewLine()
++{
++ /** move the cursor to the beginning of the next line **/
++
++ Writechar('\n', 0);
++ Writechar('\r', 0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Move cursor up n lines with terminal escape sequence
++
++ Args: n -- number of lines to go up
++
++ Result: cursor moves,
++ internal position updated
++
++ Only for ttyout use; not outside callers
++ ----*/
++static void
++CursorUp(n)
++int n;
++{
++ /** move the cursor up 'n' lines **/
++ /** Calling function must check that _up is not null before calling **/
++
++ _line = (_line-n > 0? _line - n: 0); /* up 'n' lines... */
++
++ while (n-- > 0)
++ tputs(_up, 1, outchar);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Move cursor down n lines with terminal escape sequence
++
++ Arg: n -- number of lines to go down
++
++ Result: cursor moves,
++ internal position updated
++
++ Only for ttyout use; not outside callers
++ ----*/
++static void
++CursorDown(n)
++ int n;
++{
++ /** move the cursor down 'n' lines **/
++ /** Caller must check that _down is not null before calling **/
++
++ _line = (_line+n < ps_global->ttyo->screen_rows ? _line + n
++ : ps_global->ttyo->screen_rows);
++ /* down 'n' lines... */
++
++ while (n-- > 0)
++ tputs(_down, 1, outchar);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Move cursor left n lines with terminal escape sequence
++
++ Args: n -- number of lines to go left
++
++ Result: cursor moves,
++ internal position updated
++
++ Only for ttyout use; not outside callers
++ ----*/
++static void
++CursorLeft(n)
++int n;
++{
++ /** move the cursor 'n' characters to the left **/
++ /** Caller must check that _left is not null before calling **/
++
++ _col = (_col - n> 0? _col - n: 0); /* left 'n' chars... */
++
++ while (n-- > 0)
++ tputs(_left, 1, outchar);
++}
++
++
++/*----------------------------------------------------------------------
++ Move cursor right n lines with terminal escape sequence
++
++ Args: number of lines to go right
++
++ Result: cursor moves,
++ internal position updated
++
++ Only for ttyout use; not outside callers
++ ----*/
++static void
++CursorRight(n)
++int n;
++{
++ /** move the cursor 'n' characters to the right (nondestructive) **/
++ /** Caller must check that _right is not null before calling **/
++
++ _col = (_col+n < ps_global->ttyo->screen_cols? _col + n :
++ ps_global->ttyo->screen_cols); /* right 'n' chars... */
++
++ while (n-- > 0)
++ tputs(_right, 1, outchar);
++
++}
++
++
++
++/*----------------------------------------------------------------------
++ Start painting inverse on the screen
++
++ Result: escape sequence to go into inverse is output
++ returns 1 if it was done, 0 if not.
++ ----*/
++int
++StartInverse()
++{
++ /** set inverse video mode **/
++
++ if (!_setinverse)
++ return(0);
++
++ if(_in_inverse)
++ return(1);
++
++ _in_inverse = 1;
++ tputs(_setinverse, 1, outchar);
++ return(1);
++}
++
++
++
++/*----------------------------------------------------------------------
++ End painting inverse on the screen
++
++ Result: escape sequence to go out of inverse is output
++ returns 1 if it was done, 0 if not.
++ ----------------------------------------------------------------------*/
++void
++EndInverse()
++{
++ /** compliment of startinverse **/
++
++ if (!_clearinverse)
++ return;
++
++ if(_in_inverse){
++ _in_inverse = 0;
++ tputs(_clearinverse, 1, outchar);
++ }
++}
++
++
++int
++StartUnderline()
++{
++ if (!_setunderline)
++ return(0);
++
++ tputs(_setunderline, 1, outchar);
++ return(1);
++}
++
++
++void
++EndUnderline()
++{
++ if (!_clearunderline)
++ return;
++
++ tputs(_clearunderline, 1, outchar);
++}
++
++int
++StartBold()
++{
++ if (!_setbold)
++ return(0);
++
++ tputs(_setbold, 1, outchar);
++ return(1);
++}
++
++void
++EndBold()
++{
++ if (!_clearbold)
++ return;
++
++ tputs(_clearbold, 1, outchar);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Insert character on screen pushing others right
++
++ Args: c -- character to insert
++
++ Result: charcter is inserted if possible
++ return -1 if it can't be done
++ ----------------------------------------------------------------------*/
++InsertChar(c)
++ int c;
++{
++ if(_insertchar != NULL && *_insertchar != '\0') {
++ tputs(_insertchar, 1, outchar);
++ Writechar(c, 0);
++ } else if(_startinsert != NULL && *_startinsert != '\0') {
++ tputs(_startinsert, 1, outchar);
++ Writechar(c, 0);
++ tputs(_endinsert, 1, outchar);
++ } else {
++ return(-1);
++ }
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Delete n characters from line, sliding rest of line left
++
++ Args: n -- number of characters to delete
++
++
++ Result: characters deleted on screen
++ returns -1 if it wasn't done
++ ----------------------------------------------------------------------*/
++DeleteChar(n)
++ int n;
++{
++ if(_deletechar == NULL || *_deletechar == '\0')
++ return(-1);
++
++ while(n) {
++ tputs(_deletechar, 1, outchar);
++ n--;
++ }
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Go into scrolling mode, that is set scrolling region if applicable
++
++ Args: top -- top line of region to scroll
++ bottom -- bottom line of region to scroll
++ (These are zero-origin numbers)
++
++ Result: either set scrolling region or
++ save values for later scrolling
++ returns -1 if we can't scroll
++
++ Unfortunately this seems to leave the cursor in an unpredictable place
++ at least the manuals don't say where, so we force it here.
++-----*/
++static int __t, __b;
++
++BeginScroll(top, bottom)
++ int top, bottom;
++{
++ char *stuff;
++
++ if(_scrollmode == NoScroll)
++ return(-1);
++
++ __t = top;
++ __b = bottom;
++ if(_scrollmode == UseScrollRegion){
++ stuff = tgoto(_scrollregion, bottom, top);
++ tputs(stuff, 1, outchar);
++ /*-- a location very far away to force a cursor address --*/
++ _line = FARAWAY;
++ _col = FARAWAY;
++ }
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ End scrolling -- clear scrolling regions if necessary
++
++ Result: Clear scrolling region on terminal
++ -----*/
++void
++EndScroll()
++{
++ if(_scrollmode == UseScrollRegion && _scrollregion != NULL){
++ /* Use tgoto even though we're not cursor addressing because
++ the format of the capability is the same.
++ */
++ char *stuff = tgoto(_scrollregion, ps_global->ttyo->screen_rows -1, 0);
++ tputs(stuff, 1, outchar);
++ /*-- a location very far away to force a cursor address --*/
++ _line = FARAWAY;
++ _col = FARAWAY;
++ }
++}
++
++
++/* ----------------------------------------------------------------------
++ Scroll the screen using insert/delete or scrolling regions
++
++ Args: lines -- number of lines to scroll, positive forward
++
++ Result: Screen scrolls
++ returns 0 if scroll succesful, -1 if not
++
++ positive lines goes foward (new lines come in at bottom
++ Leaves cursor at the place to insert put new text
++
++ 0,0 is the upper left
++ -----*/
++ScrollRegion(lines)
++ int lines;
++{
++ int l;
++
++ if(lines == 0)
++ return(0);
++
++ if(_scrollmode == UseScrollRegion) {
++ if(lines > 0) {
++ MoveCursor(__b, 0);
++ for(l = lines ; l > 0 ; l--)
++ tputs((_scrolldown == NULL || _scrolldown[0] =='\0') ? "\n" :
++ _scrolldown, 1, outchar);
++ } else {
++ MoveCursor(__t, 0);
++ for(l = -lines; l > 0; l--)
++ tputs(_scrollup, 1, outchar);
++ }
++ } else if(_scrollmode == InsertDelete) {
++ if(lines > 0) {
++ MoveCursor(__t, 0);
++ for(l = lines; l > 0; l--)
++ tputs(_deleteline, 1, outchar);
++ MoveCursor(__b, 0);
++ for(l = lines; l > 0; l--)
++ tputs(_insertline, 1, outchar);
++ } else {
++ for(l = -lines; l > 0; l--) {
++ MoveCursor(__b, 0);
++ tputs(_deleteline, 1, outchar);
++ MoveCursor(__t, 0);
++ tputs(_insertline, 1, outchar);
++ }
++ }
++ } else {
++ return(-1);
++ }
++ fflush(stdout);
++ return(0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Write a character to the screen, keeping track of cursor position
++
++ Args: ch -- character to output
++
++ Result: character output
++ cursor position variables updated
++ ----*/
++void
++Writechar(ch, new_esc_len)
++ register unsigned int ch;
++ int new_esc_len;
++{
++ static int esc_len = 0;
++
++ if(ps_global->in_init_seq /* silent */
++ || (F_ON(F_BLANK_KEYMENU, ps_global) /* or bottom, */
++ && !esc_len /* right cell */
++ && _line + 1 == ps_global->ttyo->screen_rows
++ && _col + 1 == ps_global->ttyo->screen_cols))
++ return;
++
++ if(!iscntrl(ch & 0x7f)){
++ putchar(ch);
++ if(esc_len > 0)
++ esc_len--;
++ else
++ _col++;
++ }
++ else{
++ switch(ch){
++ case LINE_FEED:
++ /*-- Don't have to watch out for auto wrap or newline glitch
++ because we never let it happen. See below
++ ---*/
++ putchar('\n');
++ _line = min(_line+1,ps_global->ttyo->screen_rows);
++ esc_len = 0;
++ break;
++
++ case RETURN : /* move to column 0 */
++ putchar('\r');
++ _col = 0;
++ esc_len = 0;
++ break;
++
++ case BACKSPACE : /* move back a space if not in column 0 */
++ if(_col != 0) {
++ putchar('\b');
++ _col--;
++ } /* else BACKSPACE does nothing */
++
++ break;
++
++ case BELL : /* ring the bell but don't advance _col */
++ putchar(ch);
++ break;
++
++ case TAB : /* if a tab, output it */
++ do /* BUG? ignores tty driver's spacing */
++ putchar(' ');
++ while(_col < ps_global->ttyo->screen_cols - 1
++ && ((++_col)&0x07) != 0);
++ break;
++
++ case ESCAPE :
++ /* If we're outputting an escape here, it may be part of an iso2022
++ escape sequence in which case take up no space on the screen.
++ Unfortunately such sequences are variable in length.
++ */
++ esc_len = new_esc_len - 1;
++ putchar(ch);
++ break;
++
++ default : /* Change remaining control characters to ? */
++ if(F_ON(F_PASS_CONTROL_CHARS, ps_global))
++ putchar(ch);
++ else
++ putchar('?');
++
++ if(esc_len > 0)
++ esc_len--;
+ else
-+ exit_to_main = 1;
-
- break;
-
-@@ -1852,6 +1859,7 @@
- /*--------------------- Key right -------------------*/
- case MC_CHARRIGHT :
- case MC_NEXTITEM :
-+ exit_to_main = 0;
- if(n = folder_lister_next(fs))
- fs->folder_index = n;
-
-@@ -1873,7 +1881,7 @@
- rc--;
-
- if(rc < 0){
-- q_status_message(SM_ORDER, 0, 1, "Already on first line.");
-+ q_status_message(SM_ORDER, 0, 1, "已經到第一行了。");
- if(fs->top_row != 0){ /* make sure! */
- fs->top_row = 0;
- fs->prev_index = -1;
-@@ -1919,7 +1927,7 @@
- FOLDERS(fs->context))->d_line + 1;
-
- if(rc > fs->last_row){
-- q_status_message(SM_ORDER, 0, 1, "Already on last line.");
-+ q_status_message(SM_ORDER, 0, 1, "已經到最後一行了。");
- break;
- }
-
-@@ -2038,7 +2046,7 @@
- rc++;
- }
- else
-- q_status_message(SM_ORDER,0,1,"Already on first page.");
-+ q_status_message(SM_ORDER,0,1,"已經到第一頁了。");
-
- break;
-
-@@ -2051,7 +2059,7 @@
- if((rc = fs->top_row + fs->display_rows) > fs->last_row){
- if((int)folder_entry(fs->folder_index,
- FOLDERS(fs->context))->d_line >= fs->last_row){
-- q_status_message(SM_ORDER,0,1,"Already on last page.");
-+ q_status_message(SM_ORDER,0,1,"已經到最後一頁了。");
- break;
- }
- else
-@@ -2092,7 +2100,7 @@
- }
- else
- q_status_message(SM_ORDER | SM_DING, 3, 3,
-- "Sorry, no help text available");
-+ "很抱歉,文字說明無法取得");
-
- break;
-
-@@ -2101,7 +2109,7 @@
- case MC_CHOICE :
- if(!folder_total(FOLDERS(fs->context))){
- q_status_message(SM_ORDER | SM_DING, 3, 3,
-- "Empty folder collection. Nothing to select!");
-+ "空的資料匣總集。沒有東西可供選擇!");
- }
- else if(folder_lister_select(cur_f, fs, listmode)){
- mangled_footer++;
-@@ -2163,7 +2171,7 @@
- mangled_footer++;
- }
- else
-- q_status_message(SM_ORDER, 0, 4, "Already in List Mode");
-+ q_status_message(SM_ORDER, 0, 4, "已經在列表模式了。");
-
- break;
-
-@@ -2296,8 +2304,8 @@
- case MC_DELETE :
- if(!ALL_FOUND(fs->context) || (fs->context->use & CNTXT_PSEUDO)){
- q_status_message1(SM_ORDER | SM_DING, 0, 3,
-- "No folder selected to delete. %s list.",
-- ALL_FOUND(fs->context) ? "Empty" : "Expand");
-+ "尚未選擇供刪除的資料匣。%s列表。",
-+ ALL_FOUND(fs->context) ? "空" : "展開");
- break;
- }
-
-@@ -2375,13 +2383,13 @@
- if(fs->zoomed = !fs->zoomed){ /* clear all the prefixes */
- (void) folder_lister_nearest_selected(fs);
- q_status_message1(SM_ORDER, 0, 3,
-- "In Zoomed list of %s folders. Use \"Z\" to restore regular list",
-+ "在 %s 個資料匣縮放的列表中。使用 \"Z\" 來回復正常列表",
- int2string(n));
-
- }
- else{
- q_status_message(SM_ORDER, 0, 3,
-- "Folder List Zoom mode is now off");
-+ "資料匣列表縮放模式目前為關閉狀態");
- }
-
- create_folder_display(fs, ps->ttyo->screen_cols);
-@@ -2389,7 +2397,7 @@
- }
- else
- q_status_message(SM_ORDER, 0, 3,
-- "No selected folders to Zoom on");
-+ "尚未選擇供放大的資料匣。");
-
-
- break;
-@@ -2403,12 +2411,12 @@
- break;
-
- case 0 :
-- q_status_message(SM_ORDER | SM_DING, 0, 2, "Word not found");
-+ q_status_message(SM_ORDER | SM_DING, 0, 2, "找不到該字");
- break;
-
- case 2 :
- q_status_message(SM_ORDER, 0, 2,
-- "Search wrapped to beginning");
-+ "從頭搜尋");
- break;
- }
-
-@@ -2469,8 +2477,8 @@
- && ALL_FOUND(fs->context))
- return(fs->folder_index + 1);
-
-- q_status_message1(SM_ORDER, 0, 1, "Already on last %sfolder",
-- fs->zoomed ? "Zoomed " : "");
-+ q_status_message1(SM_ORDER, 0, 1, "已經在最後一個%s資料匣了",
-+ fs->zoomed ? "縮放後的" : "");
- return(0);
- }
-
-@@ -2491,8 +2499,8 @@
- else if(fs->folder_index > 0 && ALL_FOUND(fs->context))
- return(fs->folder_index - 1);
-
-- q_status_message1(SM_ORDER, 0, 1, "Already on first %sfolder",
-- fs->zoomed ? "Zoomed " : "");
-+ q_status_message1(SM_ORDER, 0, 1, "已經在第一個%s資料匣了",
-+ fs->zoomed ? "縮放後的" : "");
- return(-1);
- }
-
-@@ -2507,7 +2515,7 @@
- if(listmode){
- if(NEWS_TEST(fs->context) && !strncmp(f->prefix, "SUB", 3)){
- q_status_message1(SM_ORDER, 0, 3,
-- "Already subscribed to \"%s\"",
-+ "已訂閱\至 \"%s\"",
- FLDR_NAME(f));
- }
- else{
-@@ -2517,7 +2525,7 @@
- }
- else{
- if(f->isdir){
-- q_status_message(SM_ORDER, 0, 3, "Can't select directories");
-+ q_status_message(SM_ORDER, 0, 3, "無法選擇目錄");
- }
- else
- folder_select_toggle(fs, f);
-@@ -2574,13 +2582,13 @@
- extern char *sel_pmt2;
-
- if((f = folder_entry(fs->folder_index, FOLDERS(fs->context)))->isdir){
-- q_status_message(SM_ORDER | SM_DING, 0, 3, "Can't Select directories");
-+ q_status_message(SM_ORDER | SM_DING, 0, 3, "無法選擇目錄");
- return(0);
- }
-
- sel_opts = self_opts2;
- if(old_tot = selected_folders(fs)){
-- sel_opts1[1].label = "unselect Cur" + (f->selected ? 0 : 2);
-+ sel_opts1[1].label = "取消目前選擇" + (f->selected ? 0 : 2);
- sel_opts += 2; /* disable extra options */
- switch(q = radio_buttons(SEL_ALTER_PMT, -FOOTER_ROWS(ps_global),
- sel_opts1, 'c', 'x', help, RB_NORM)){
-@@ -2605,7 +2613,7 @@
-
- default :
- q_status_message(SM_ORDER | SM_DING, 3, 3,
-- "Unsupported Select option");
-+ "未支援的選項");
- return(0);
- }
- }
-@@ -2641,10 +2649,10 @@
- create_folder_display(fs, ps_global->ttyo->screen_cols);
- }
-
-- q_status_message4(SM_ORDER, 0, 2, "%s%s folder%s %sselected",
-- old_tot ? "" : "All ",
-+ q_status_message3(SM_ORDER, 0, 2, "%s%s 個資料匣已被%s選擇",
-+ old_tot ? "" : "全部 ",
- comatose(old_tot ? old_tot : n),
-- plural(old_tot ? old_tot : n), old_tot ? "UN" : "");
-+ old_tot ? "取消" : "");
- return(1);
-
- case 't' : /* Text */
-@@ -2661,7 +2669,7 @@
-
- default :
- q_status_message(SM_ORDER | SM_DING, 3, 3,
-- "Unsupported Select option");
-+ "未支援的選項");
- return(0);
- }
-
-@@ -2690,35 +2698,30 @@
-
- if(!(diff = (total = selected_folders(fs)) - old_tot)){
- if(narrow)
-- q_status_message4(SM_ORDER, 0, 2,
-- "%s. %s folder%s remain%s selected.",
-- j ? "No change resulted"
-- : "No messages in intersection",
-- comatose(old_tot), plural(old_tot),
-- (old_tot == 1L) ? "s" : "");
-+ q_status_message2(SM_ORDER, 0, 2,
-+ "%s。 仍有 %s 個資料匣被選擇。",
-+ j ? "沒有導致改變"
-+ : "交點中沒有信件", comatose(old_tot));
- else if(old_tot && j)
- q_status_message(SM_ORDER, 0, 2,
-- "No change resulted. Matching folders already selected.");
-+ "沒有導致改變。符合的資料匣已經被選擇了。");
- else
- q_status_message1(SM_ORDER | SM_DING, 0, 2,
-- "Select failed! No %sfolders selected.",
-- old_tot ? "additional " : "");
-+ "選擇失敗!沒有%s資料匣被選擇。",
-+ old_tot ? "額外的 " : "");
- }
- else{
- if(old_tot){
- sprintf(tmp_20k_buf,
-- "Select matched %ld folder%s! %s %sfolder%s %sselected.",
-+ "選擇符合的 %ld 個資料匣!共計 %s 個資料匣被%s選擇.",
- (diff > 0) ? diff : old_tot + diff,
-- plural((diff > 0) ? diff : old_tot + diff),
- comatose((diff > 0) ? total : -diff),
-- (diff > 0) ? "total " : "",
-- plural((diff > 0) ? total : -diff),
-- (diff > 0) ? "" : "UN");
-+ (diff > 0) ? "" : "取消");
- q_status_message(SM_ORDER, 0, 2, tmp_20k_buf);
- }
- else{
-- q_status_message2(SM_ORDER, 0, 2, "Select matched %s folder%s!",
-- comatose(diff), plural(diff));
-+ q_status_message1(SM_ORDER, 0, 2, "選擇符合的 %s 個資料匣!",
-+ comatose(diff));
-
- if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global)){
- folder_prefixes(fs, " ");
-@@ -2833,7 +2836,7 @@
- fp = next_folder_dir(fs->context, tmpf->name);
-
- /* Provide context in new collection header */
-- sprintf(tmp_20k_buf, "Dir: %s",
-+ sprintf(tmp_20k_buf, "目錄:%s",
- ((p = strstr(fs->context->context, "%s")) && !*(p+2)
- && !strncmp(fp->ref, fs->context->context,
- p - fs->context->context))
-@@ -2854,9 +2857,9 @@
-
- fs->prev_index = -1; /* redraw display */
-
-- q_status_message2(SM_ORDER, 0, 3, "Now in %sdirectory: %s",
-+ q_status_message2(SM_ORDER, 0, 3, "目前在 %s目錄中:%s",
- folder_total(FOLDERS(fs->context))
-- ? "" : "EMPTY ", fp->ref);
-+ ? "" : "空的 ", fp->ref);
- rv++;
- }
- else
-@@ -2902,12 +2905,12 @@
- fs->prev_index = -1; /* redraw display */
-
- if(fp->status & CNTXT_SUBDIR)
-- q_status_message1(SM_ORDER, 0, 3, "Now in directory: %s",
-+ q_status_message1(SM_ORDER, 0, 3, "目前所在目錄:%s",
- strsquish(tmp_20k_buf + 500, fp->ref,
- fs->display_cols - 22));
- else
- q_status_message(SM_ORDER, 0, 3,
-- "Returned to collection's top directory");
-+ "回到總集的最上層目錄");
-
- rv++;
- }
-@@ -3473,12 +3476,12 @@
- if(fs->context->use & CNTXT_INCMNG){
- char inbox_host[MAXPATH], *beg, *end = NULL;
- ESCKEY_S *special_key;
-- static ESCKEY_S host_key[] = {{ctrl('X'),12,"^X","Use Inbox Host"},
-+ static ESCKEY_S host_key[] = {{ctrl('X'),12,"^X","使用 Inbox 的主機"},
- {-1, 0, NULL, NULL}};
-
- if(ps_global->readonly_pinerc){
- q_status_message(SM_ORDER,3,5,
-- "Addition cancelled: config file not editable");
-+ "取消新增:無法編輯設定檔");
- return(FALSE);
- }
-
-@@ -3500,7 +3503,7 @@
- else
- special_key = NULL;
-
-- sprintf(tmp, "Name of server to contain added folder : ");
-+ sprintf(tmp, "包含新增資料匣的主機名:");
- help = NO_HELP;
- while(1){
- int flags = OE_APPEND_CURRENT;
-@@ -3518,7 +3521,7 @@
- }
- else if(rc == 1){
- q_status_message(SM_ORDER,0,2,
-- "Addition of new folder cancelled");
-+ "取消新增資料匣");
- return(FALSE);
- }
- else if(rc == 0)
-@@ -3528,7 +3531,7 @@
-
- if(offset = strlen(add_folder)){ /* must be host for incoming */
- int i;
-- sprintf(tmp, "Folder on \"%s\" to add : ", add_folder);
-+ sprintf(tmp, "加入在 \"%s\" 上的資料匣:", add_folder);
- for(i = offset;i >= 0; i--)
- add_folder[i+1] = add_folder[i];
-
-@@ -3537,7 +3540,7 @@
- add_folder[++offset] = '\0'; /* +2, total */
- }
- else
-- sprintf(tmp, "Folder name to add : ");
-+ sprintf(tmp, "新增資料匣名稱:");
-
- help = NO_HELP;
- while(1){
-@@ -3545,14 +3548,14 @@
-
- p = NULL;
- if(isdir){
-- add_key[0].label = "Create Folder";
-+ add_key[0].label = "建立資料匣";
- if(tmp[0] == 'F')
-- rplstr(tmp, 6, "Directory");
-+ rplstr(tmp, 6, "目錄");
- }
- else{
-- add_key[0].label = "Create Directory";
-+ add_key[0].label = "建立目錄";
- if(tmp[0] == 'D')
-- rplstr(tmp, 9, "Folder");
-+ rplstr(tmp, 9, "資料匣");
- }
-
- flags = OE_APPEND_CURRENT;
-@@ -3566,7 +3569,7 @@
- if(!ps_global->show_dot_names && add_folder[offset] == '.'){
- if(cnt++ <= 0)
- q_status_message(SM_ORDER,3,3,
-- "Folder name can't begin with dot");
-+ "資料匣不能以點 \".\" 為名稱開頭");
- else{
- NAMEVAL_S *feat;
- int i;
-@@ -3576,7 +3579,7 @@
- ;/* do nothing */
-
- q_status_message1(SM_ORDER,3,3,
-- "Config feature \"%s\" enables names beginning with dot",
-+ "設定檔中 \"%s\" 的功\能可使資料匣以點 \".\" 為名稱開頭",
- feat && feat->name ? feat->name : "");
- }
-
-@@ -3596,7 +3599,7 @@
- }
- else if(*p == fs->context->dir->delim){
- q_status_message(SM_ORDER|SM_DING, 3, 3,
-- "Can't have trailing directory delimiters!");
-+ "不能有目錄尾端的分隔號 \"/\"!");
- display_message('X');
- continue;
- }
-@@ -3618,7 +3621,7 @@
- : NO_HELP;
- }
- else if(rc == 1 || add_folder[0] == '\0') {
-- q_status_message(SM_ORDER,0,2, "Addition of new folder cancelled");
-+ q_status_message(SM_ORDER,0,2, "取消新增資料匣");
- return(FALSE);
- }
- }
-@@ -3637,7 +3640,7 @@
-
- help = NO_HELP;
- if(fs->context->use & CNTXT_INCMNG){
-- sprintf(tmp, "Nickname for folder \"%s\" : ", &add_folder[offset]);
-+ sprintf(tmp, "資料匣\"%s\" 的暱稱:", &add_folder[offset]);
- while(1){
- int flags = OE_APPEND_CURRENT;
-
-@@ -3658,7 +3661,7 @@
- }
- else if(rc == 1 || (rc != 3 && !*nickname)){
- q_status_message(SM_ORDER,0,2,
-- "Addition of new folder cancelled");
-+ "取消新增資料匣");
- return(FALSE);
- }
- }
-@@ -3673,7 +3676,7 @@
- f = folder_entry(offset, FOLDERS(fs->context));
- if(!strucmp(FLDR_NAME(f), nickname[0] ? nickname : add_folder)){
- q_status_message1(SM_ORDER | SM_DING, 0, 3,
-- "Incoming folder \"%s\" already exists",
-+ "新進資料匣(Incoming folder) \"%s\" 已存在",
- nickname[0] ? nickname : add_folder);
- return(FALSE);
- }
-@@ -3726,7 +3729,7 @@
- if(nickname[0])
- strcpy(add_folder, nickname); /* known by new name */
-
-- q_status_message1(SM_ORDER, 0, 3, "Folder \"%s\" created",add_folder);
-+ q_status_message1(SM_ORDER, 0, 3, "資料匣 \"%s\" 已建立",add_folder);
- return_val = add_folder;
- }
- else if(context_isambig(add_folder)){
-@@ -3742,15 +3745,15 @@
- */
- refresh_folder_list(fs, TRUE);
-
-- q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" created",
-- isdir ? "Directory" : "Folder", add_folder);
-+ q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" 已建立",
-+ isdir ? "目錄" : "資料匣", add_folder);
- }
-
- return_val = add_folder;
- }
- else
- q_status_message1(SM_ORDER, 0, 3,
-- "Folder \"%s\" created outside current collection",
-+ "資料匣 \"%s\" 建立於目前的總集之外",
- add_folder);
-
- return(return_val != NULL);
-@@ -3789,13 +3792,13 @@
- subscribe_keys[i = 0].ch = ctrl('T');
- subscribe_keys[i].rval = 12;
- subscribe_keys[i].name = "^T";
-- subscribe_keys[i++].label = "To All Grps";
-+ subscribe_keys[i++].label = "給所有的組群";
-
- if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
- subscribe_keys[i].ch = ctrl('I');
- subscribe_keys[i].rval = 11;
- subscribe_keys[i].name = "TAB";
-- subscribe_keys[i++].label = "Complete";
-+ subscribe_keys[i++].label = "完成";
- }
-
- subscribe_keys[i].ch = -1;
-@@ -3855,7 +3858,7 @@
- }
- else{
- q_status_message(SM_ORDER, 0, 2,
-- "No group substring to match! Use ^T to list all news groups.");
-+ "沒有符合的組群字串!請用 ^T 列出所有新聞組群。");
- continue;
- }
-
-@@ -3908,10 +3911,10 @@
- else{
- if(rc == 12)
- q_status_message(SM_ORDER | SM_DING, 3, 3,
-- "No groups to select from!");
-+ "沒有可供選擇的組群!");
- else
- q_status_message1(SM_ORDER, 3, 3,
-- "News group \"%s\" didn't match any existing groups",
-+ "新聞組群 \"%s\" 不符合任何現存的組群",
- folder);
-
- continue;
-@@ -3959,7 +3962,7 @@
-
- if(rc < 0){
- if(rc == -1)
-- q_status_message(SM_ORDER, 0, 3, "Subscribe cancelled");
-+ q_status_message(SM_ORDER, 0, 3, "取消訂閱\");
- }
- else{
- if(folders){ /*------ Actually do the subscription -----*/
-@@ -3981,7 +3984,7 @@
- */
- q_status_message1(errors ?SM_INFO : SM_ORDER,
- errors ? 0 : 3, 3,
-- "Error subscribing to \"%s\"",
-+ "訂閱\至 \"%s\" 時發生錯誤",
- (char *) flp->text.data);
- errors++;
- }
-@@ -4005,13 +4008,13 @@
-
- if(n == 0)
- q_status_message(SM_ORDER | SM_DING, 3, 5,
-- "Subscriptions failed, subscribed to no new groups");
-+ "訂閱\失敗,沒有訂閱\任何新組群");
- else
- q_status_message3(SM_ORDER | (errors ? SM_DING : 0),
- errors ? 3 : 0,3,
-- "Subscribed to %s new groups%s%s",
-+ "訂閱\至 %s 個新組群%s%s",
- comatose((long)n),
-- errors ? ", failed on " : "",
-+ errors ? ",發生錯誤於 " : "",
- errors ? comatose((long)errors) : "");
-
- mail_free_stringlist(&folders);
-@@ -4020,7 +4023,7 @@
- (void) context_apply(tmp_20k_buf, &subscribe_cntxt, folder);
- if(mail_subscribe(NULL, tmp_20k_buf) == 0L){
- q_status_message1(SM_ORDER | SM_DING, 3, 3,
-- "Error subscribing to \"%s\"", folder);
-+ "訂閱\至 \"%s\" 時發生錯誤", folder);
- }
- else if(ALL_FOUND(cntxt)){
- /*---- Update the screen display data structures -----*/
-@@ -4034,7 +4037,7 @@
- }
-
- if(folder[0])
-- q_status_message1(SM_ORDER, 0, 3, "Subscribed to \"%s\"", folder);
-+ q_status_message1(SM_ORDER, 0, 3, "訂閱\至 \"%s\"", folder);
- }
-
- free_fdir(&subscribe_cntxt.dir, 1);
-@@ -4074,19 +4077,19 @@
-
- if(NEWS_TEST(fs->context)){
- q_status_message(SM_ORDER | SM_DING, 3, 3,
-- "Can't rename bulletin boards or news groups!");
-+ "無法更改電子佈告欄或新聞組群的名稱!");
- return(0);
- }
- else if(!folder_total(FOLDERS(fs->context))){
- q_status_message(SM_ORDER | SM_DING, 0, 4,
-- "Empty folder collection. No folder to rename!");
-+ "空的資料匣總集。沒有可供更名的資料匣!");
- return(0);
- }
- else if((new_f = folder_entry(fs->folder_index, FOLDERS(fs->context)))
- && (!strucmp(FLDR_NAME(new_f), ps_global->inbox_name)
- || new_f->parent)) {
- q_status_message1(SM_ORDER | SM_DING, 3, 4,
-- "Can't change special folder name \"%s\"",
-+ "無法更改特殊資料匣 \"%s\" 的名稱",
- new_f->parent
- ? new_f->nickname
- : ps_global->inbox_name);
-@@ -4102,11 +4105,11 @@
-
- ren_cur = strcmp(folder, ps_global->cur_folder) == 0;
-
-- sprintf(prompt, "Rename %s to : ",
-+ sprintf(prompt, "將 %s 更名為:",
- (fs->context->use & CNTXT_INCMNG)
-- ? "nickname"
-+ ? "暱稱"
- : (isdir = new_f->isdir)
-- ? "directory" : "folder");
-+ ? "目錄" : "資料匣");
- help = NO_HELP;
- strcpy(new_name, folder);
- while(1) {
-@@ -4127,7 +4130,7 @@
- if(!ps_global->show_dot_names && *new_name == '.'){
- if(cnt++ <= 0)
- q_status_message(SM_ORDER,3,3,
-- "Folder name can't begin with dot");
-+ "資料匣不能以點 \".\" 為名稱開頭");
- else{
- NAMEVAL_S *feat;
- int i;
-@@ -4137,7 +4140,7 @@
- ;/* do nothing */
-
- q_status_message1(SM_ORDER,3,3,
-- "Config feature \"%s\" enables names beginning with dot",
-+ "設定檔中 \"%s\" 的功\能可使資料匣以點 \".\" 為名稱開頭",
- feat && feat->name ? feat->name : "");
- }
-
-@@ -4147,13 +4150,13 @@
-
- if(folder_index(new_name, fs->context, FI_ANY) >= 0){
- q_status_message1(SM_ORDER, 3, 3,
-- "Folder \"%s\" already exists",
-+ "資料匣 \"%s\" 已存在",
- pretty_fn(new_name));
- display_message(NO_OP_COMMAND);
- continue;
- }
- else if(!strucmp(new_name, ps_global->inbox_name)){
-- q_status_message1(SM_ORDER, 3, 3, "Can't rename folder to %s",
-+ q_status_message1(SM_ORDER, 3, 3, "無法將資料匣更名至 %s",
- ps_global->inbox_name);
- display_message(NO_OP_COMMAND);
- continue;
-@@ -4176,7 +4179,7 @@
- if(rc == 1
- || !(*new_name || (fs->context->use & CNTXT_INCMNG))
- || !strcmp(new_name, folder)){
-- q_status_message(SM_ORDER, 0, 2, "Folder rename cancelled");
-+ q_status_message(SM_ORDER, 0, 2, "取消資料匣更名");
- return(0);
- }
-
-@@ -4258,7 +4261,7 @@
- /* renaming sent-mail or saved-messages */
- if(context_create(fs->context, NULL, folder)){
- q_status_message3(SM_ORDER,0,3,
-- "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
-+ "資料匣 \"%s\" 名稱改為 \"%s\"。建立新的 \"%s\"",
- folder, new_name,
- pretty_fn(
- (strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER,
-@@ -4269,7 +4272,7 @@
- }
- else{
- q_status_message1(SM_ORDER | SM_DING, 3, 4,
-- "Error creating new \"%s\"", folder);
-+ "建立新的資料匣 \"%s\" 時發生錯誤", folder);
-
- dprint(2, (debugfile, "Error creating \"%s\" in %s context\n",
- folder, fs->context->context));
-@@ -4277,7 +4280,7 @@
- }
- else
- q_status_message2(SM_ORDER, 0, 3,
-- "Folder \"%s\" renamed to \"%s\"",
-+ "資料匣 \"%s\" 名稱改為 \"%s\"",
- pretty_fn(folder), pretty_fn(new_name));
-
- /* Rebuild folder list */
-@@ -4326,7 +4329,7 @@
- int ret, close_opened = 0, blast_folder = 1;
-
- if(NEWS_TEST(fs->context)){
-- static char fmt[] = "Really unsubscribe from \"%.*s\"";
-+ static char fmt[] = "確定自 \"%.*s\" 中解除訂閱\嗎";
-
- folder = folder_entry(fs->folder_index, FOLDERS(fs->context))->name;
- /* 4 is strlen("%.*s") */
-@@ -4348,7 +4351,7 @@
- (void) context_apply(tmp_20k_buf, fs->context, folder);
- if(!mail_unsubscribe(NULL, tmp_20k_buf)){
- q_status_message1(SM_ORDER | SM_DING, 3, 3,
-- "Error unsubscribing from \"%s\"", folder);
-+ "自 \"%s\" 取消訂閱\時發生錯誤", folder);
- return(0);
- }
-
-@@ -4366,7 +4369,7 @@
-
- if(!folder_total(FOLDERS(fs->context))){
- q_status_message(SM_ORDER | SM_DING, 0, 4,
-- "Empty folder collection. No folder to delete!");
-+ "空的資料匣總集。沒有東西可供刪除!");
- return(0);
- }
-
-@@ -4376,12 +4379,12 @@
-
- if(ps_global->readonly_pinerc && (fs->context->use & CNTXT_INCMNG)){
- q_status_message(SM_ORDER,3,5,
-- "Deletion cancelled: config file not editable");
-+ "取消刪除:無法編輯設定檔");
- return(0);
- }
- else if(strucmp(folder, ps_global->inbox_name) == 0 || fp->parent) {
- q_status_message1(SM_ORDER | SM_DING, 3, 4,
-- "Can't delete special folder \"%s\".", ps_global->inbox_name);
-+ "無法刪除特殊資料匣 \"%s\"。", ps_global->inbox_name);
- return(0);
- }
- else if(fs->context == ps_global->context_current
-@@ -4396,7 +4399,7 @@
-
- if(ret){
- q_status_message1(SM_ORDER | SM_DING, 3, 4,
-- "Can't delete non-empty directory \"%s\".",
-+ "無法刪除非空的目錄 \"%s\"。",
- folder);
- return(0);
- }
-@@ -4407,19 +4410,19 @@
- */
- if(folder_index(folder, fs->context, FI_FOLDER) >= 0
- && (ret = want_to(DIR_FOLD_PMT,'n','x',NO_HELP,WT_NORM)) != 'y'){
-- q_status_message(SM_ORDER,0,3, (ret == 'x') ? "Delete cancelled"
-- : "No folder deleted");
-+ q_status_message(SM_ORDER,0,3, (ret == 'x') ? "取消刪除"
-+ : "沒有任何資料匣被刪除");
- return(0);
- }
- }
-
- if(fs->context->use & CNTXT_INCMNG){
- static ESCKEY_S delf_opts[] = {
-- {'n', 'n', "N", "Nickname only"},
-- {'b', 'b', "B", "Both Folder and Nickname"},
-+ {'n', 'n', "N", "僅有暱稱"},
-+ {'b', 'b', "B", "資料匣與暱稱"},
- {-1, 0, NULL, NULL}
- };
--#define DELF_PROMPT "DELETE only Nickname or Both nickname and folder? "
-+#define DELF_PROMPT "刪除「僅有暱稱」或「資料匣與暱稱」?"
-
- switch(radio_buttons(DELF_PROMPT, -FOOTER_ROWS(ps_global),
- delf_opts,'n','x',NO_HELP,RB_NORM)){
-@@ -4436,13 +4439,13 @@
- }
- }
- else{
-- sprintf(ques_buf, "DELETE \"%s\"%s", folder,
-- close_opened ? " (the currently open folder)"
-- : fp->isdir ? " (a directory)" : "");
-+ sprintf(ques_buf, "刪除 \"%s\"%s", folder,
-+ close_opened ? " (目前開啟的資料匣)"
-+ : fp->isdir ? " (目錄)" : "");
-
- if((ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM)) != 'y'){
-- q_status_message(SM_ORDER,0,3, (ret == 'x') ? "Delete cancelled"
-- : "No folder deleted");
-+ q_status_message(SM_ORDER,0,3, (ret == 'x') ? "取消刪除"
-+ : "沒有任何資料匣被刪除");
- return(0);
- }
- }
-@@ -4485,13 +4488,13 @@
- /*
- * BUG: what if sent-mail or saved-messages????
- */
-- q_status_message1(SM_ORDER,3,3,"Delete of \"%s\" Failed!", folder);
-+ q_status_message1(SM_ORDER,3,3,"刪除 \"%s\" 失敗!", folder);
- return(0);
- }
- }
-
-- q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" deleted!",
-- blast_folder ? "Folder" : "Nickname", folder);
-+ q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" 已被刪除!",
-+ blast_folder ? "資料匣" : "暱稱", folder);
-
-
- if(fs->context->use & CNTXT_INCMNG){
-@@ -4636,7 +4639,7 @@
- int flags;
-
- pat[0] = '\0';
-- sprintf(prompt, "String in folder %s to match : ", kind);
-+ sprintf(prompt, "資料匣 %s 中欲符合的字串:", kind);
-
- while(1){
- flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
-@@ -4761,7 +4764,7 @@
- if(!strucmp(folder = f->name, ps_global->inbox_name))
- return(FEX_ISFILE);
-
-- sprintf(tmp, "Scanning \"%.*s\"", 40, FLDR_NAME(f));
-+ sprintf(tmp, "正在掃描 \"%.*s\"", 40, FLDR_NAME(f));
- we_cancel = busy_alarm(1, tmp, NULL, 0);
-
- mm_list_info = &ldata; /* tie down global reference */
-@@ -4926,7 +4929,7 @@
- while(1){
- flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
- sprintf(number, "%ld", *count);
-- sprintf(prompt, "Select folders with messages %s : ", tense[*cmp]);
-+ sprintf(prompt, "選擇有信件 %s 的資料匣:", tense[*cmp]);
- r = optionally_enter(number, -FOOTER_ROWS(ps_global), 0, 31,
- prompt, sel_num_opt, NO_HELP, &flags);
- switch (r){
-@@ -4935,7 +4938,7 @@
- break;
- else if((*count = atol(number)) < 0L)
- q_status_message(SM_ORDER, 3, 3,
-- "Can't have NEGATIVE message count!");
-+ "不可有「負的」信件數量!");
- else
- return(1); /* success */
-
-@@ -5141,14 +5144,14 @@
- int rc, t_index, done = 0;
- static char search_string[MAX_SEARCH+1];
- static ESCKEY_S search_keys[] = {{0, 0, NULL, NULL},
-- {ctrl('Y'), 10, "^Y","First Fldr"},
-- {ctrl('V'), 11, "^V","Last Fldr"},
-+ {ctrl('Y'), 10, "^Y","第一個資料匣"},
-+ {ctrl('V'), 11, "^V","最後一個資料匣"},
- {-1, 0, NULL, NULL} };
-
- nsearch_string[0] = '\0';
- if(!folder_total(FOLDERS(fd->context))){
- q_status_message(SM_ORDER | SM_DING, 0, 4,
-- "Empty folder collection. No folders to search!");
-+ "空的資料匣總集。沒有東西可供搜尋!");
- return(0);
- }
- else{
-@@ -5156,7 +5159,7 @@
- search_keys[0].ch = ctrl('X');
- search_keys[0].rval = 9;
- search_keys[0].name = "^X";
-- search_keys[0].label = "List Matches";
-+ search_keys[0].label = "列出符合者";
- }
- else{
- search_keys[0].ch = 0;
-@@ -5167,7 +5170,7 @@
- }
-
- t_index = fd->folder_index;
-- sprintf(prompt, "Folder name to search for %s%s%s: ",
-+ sprintf(prompt, "欲搜尋的資料匣名稱 %s%s%s:",
- (*search_string == '\0') ? "" : "[",
- search_string,
- (*search_string == '\0') ? "" : "] ");
-@@ -5178,7 +5181,7 @@
- switch(optionally_enter(nsearch_string, ask_line, 0, MAX_SEARCH,
- prompt, search_keys, help, &flags)){
- case -1 :
-- q_status_message(SM_ORDER | SM_DING, 3, 3, "Error reading word");
-+ q_status_message(SM_ORDER | SM_DING, 3, 3, "讀取字元時發生錯誤");
- return(0);
-
- case 0 : /*----- Search away ------*/
-@@ -5235,9 +5238,9 @@
- }
-
- if(rc){
-- q_status_message2(SM_ORDER, 0, 3,
-- "Searched matched %s folder%s",
-- int2string(count), plural(count));
-+ q_status_message1(SM_ORDER, 0, 3,
-+ "共有 %s 個資料匣符合搜尋條件",
-+ int2string(count));
- fd->prev_index = -1; /* repaint! */
- }
-
-@@ -5251,14 +5254,14 @@
- while((t_index = folder_lister_prev(fd)) >= 0)
- fd->folder_index = t_index;
-
-- q_status_message(SM_ORDER, 0, 3, "Searched to First Folder.");
-+ q_status_message(SM_ORDER, 0, 3, "搜尋至第一個資料匣。");
- return(3);
-
- case 11 :
- while((t_index = folder_lister_next(fd)))
- fd->folder_index = t_index;
-
-- q_status_message(SM_ORDER, 0, 3, "Searched to Last Folder.");
-+ q_status_message(SM_ORDER, 0, 3, "搜尋至最後一個資料匣。");
- return(3);
-
- default :
-@@ -6251,7 +6254,7 @@
-
- if(p = context_digest(c_string, dcontext, host, rcontext, view)){
- q_status_message2(SM_ORDER | SM_DING, 3, 4,
-- "Bad context, %s : %s", p, c_string);
-+ "錯誤的內容,%s:%s", p, c_string);
- fs_give((void **) &c_string);
- if(nickname)
- fs_give((void **)&nickname);
-@@ -6292,14 +6295,14 @@
-
- /* fix up label */
- if(NEWS_TEST(c)){
-- sprintf(tmp_20k_buf, "%sews groups%s%s",
-- (*host) ? "N" : "Local n", (*host) ? " on " : "",
-+ sprintf(tmp_20k_buf, "%s文組群%s%s",
-+ (*host) ? "新" : "本地的新", (*host) ? " 於 " : "",
- (*host) ? host : "");
- }
- else{
- p = srchstr(rcontext, "[]");
-- sprintf(tmp_20k_buf, "%solders%s%s in %.*s%s",
-- (*host) ? "F" : "Local f", (*host) ? " on " : "",
-+ sprintf(tmp_20k_buf, "%s料匣%s%s在 %.*s%s",
-+ (*host) ? "資" : "本地的資", (*host) ? " 於 " : "",
- (*host) ? host : "", p ? p - rcontext : 0,
- rcontext, (p && (p - rcontext) > 0) ? "" : "home directory");
- }
-@@ -7482,7 +7485,7 @@
- if(error && num_in_error){
- cnt_errs = num_in_error;
- memset((void *)ng_error, 0, (size_t)90);
-- sprintf(ng_error, "Unknown news group%s: ", plural(num_in_error));
-+ sprintf(ng_error, "未知的新聞組群:");
- ep = ng_error + strlen(ng_error);
- }
- for(ntmp = nglist; ntmp; ntmp = ntmp->next){
++ _col++;
++
++ break;
++ }
++ }
++
++
++ /* Here we are at the end of the line. We've decided to make no
++ assumptions about how the terminal behaves at this point.
++ What can happen now are the following
++ 1. Cursor is at start of next line, and next character will
++ apear there. (autowrap, !newline glitch)
++ 2. Cursor is at start of next line, and if a newline is output
++ it'll be ignored. (autowrap, newline glitch)
++ 3. Cursor is still at end of line and next char will apear
++ there over the top of what is there now (no autowrap).
++ We ignore all this and force the cursor to the next line, just
++ like case 1. A little expensive but worth it to avoid problems
++ with terminals configured so they don't match termcap
++ */
++ if(_col == ps_global->ttyo->screen_cols) {
++ _col = 0;
++ if(_line + 1 < ps_global->ttyo->screen_rows)
++ _line++;
++
++ moveabsolute(_col, _line);
++ }
++}
++
++
++
++/*----------------------------------------------------------------------
++ Write string to screen at current cursor position
++
++ Args: string -- strings to be output
++
++ Result: Line written to the screen
++ ----*/
++void
++Write_to_screen(string) /* UNIX */
++ register char *string;
++{
++ while(*string)
++ Writechar((unsigned char) *string++, 0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Clear screen to end of line on current line
++
++ Result: Line is cleared
++ ----*/
++void
++CleartoEOLN()
++{
++ if(!_cleartoeoln)
++ return;
++
++ tputs(_cleartoeoln, 1, outchar);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Clear screen to end of screen from current point
++
++ Result: screen is cleared
++ ----*/
++CleartoEOS()
++{
++ if(!_cleartoeos){
++ CleartoEOLN();
++ ClearLines(_line, _lines-1);
++ }
++ else
++ tputs(_cleartoeos, 1, outchar);
++}
++
++
++
++/*----------------------------------------------------------------------
++ function to output character used by termcap
++
++ Args: c -- character to output
++
++ Result: character output to screen via stdio
++ ----*/
++void
++outchar(c)
++int c;
++{
++ /** output the given character. From tputs... **/
++ /** Note: this CANNOT be a macro! **/
++
++ putc((unsigned char)c, stdout);
++}
++
++
++
++/*----------------------------------------------------------------------
++ function to output string such that it becomes icon text
++
++ Args: s -- string to write
++
++ Result: string indicated become our "icon" text
++ ----*/
++void
++icon_text(s)
++ char *s;
++{
++ static char *old_s;
++ static enum {ukn, yes, no} xterm;
++
++ if(xterm == ukn)
++ xterm = (getenv("DISPLAY") != NULL) ? yes : no;
++
++ if(F_ON(F_ENABLE_XTERM_NEWMAIL,ps_global) && xterm == yes && (s || old_s)){
++ fputs("\033]1;", stdout);
++ fputs((old_s = s) ? s : ps_global->pine_name, stdout);
++ fputs("\007", stdout);
++ fflush(stdout);
++ }
++}
++
++
++#ifdef _WINDOWS
++#line 3 "osdep/termout.gen"
++#endif
++
++/*
++ * Generic tty output routines...
++ */
++
++/*----------------------------------------------------------------------
++ Printf style output line to the screen at given position, 0 args
++
++ Args: x -- column position on the screen
++ y -- row position on the screen
++ line -- line of text to output
++
++ Result: text is output
++ cursor position is update
++ ----*/
++void
++PutLine0(x, y, line)
++ int x,y;
++ register char *line;
++{
++ MoveCursor(x,y);
++ Write_to_screen(line);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Output line of length len to the display observing embedded attributes
++
++ Args: x -- column position on the screen
++ y -- column position on the screen
++ line -- text to be output
++ length -- length of text to be output
++
++ Result: text is output
++ cursor position is updated
++ ----------------------------------------------------------------------*/
++void
++PutLine0n8b(x, y, line, length, handles)
++ int x, y, length;
++ register char *line;
++ HANDLE_S *handles;
++{
++ unsigned char c;
++
++ MoveCursor(x,y);
++ while(length-- && (c = (unsigned char)*line++)){
++ if(c == (unsigned char)TAG_EMBED && length){
++ length--;
++ switch(*line++){
++ case TAG_INVON :
++ StartInverse();
++ break;
++ case TAG_INVOFF :
++ EndInverse();
++ break;
++ case TAG_BOLDON :
++ StartBold();
++ break;
++ case TAG_BOLDOFF :
++ EndBold();
++ break;
++ case TAG_ULINEON :
++ StartUnderline();
++ break;
++ case TAG_ULINEOFF :
++ EndUnderline();
++ break;
++ case TAG_HANDLE :
++ length -= *line + 1; /* key length plus length tag */
++ if(handles){
++ int key, n;
++
++ for(key = 0, n = *line++; n; n--) /* forget Horner? */
++ key = (key * 10) + (*line++ - '0');
++
++ if(key == handles->key){
++ EndBold();
++ StartInverse();
++ }
++ }
++ else{
++ /* BUG: complain? */
++ line += *line + 1;
++ }
++
++ break;
++ default : /* literal "embed" char? */
++ Writechar(TAG_EMBED, 0);
++ Writechar(*(line-1), 0);
++ break;
++ } /* tag with handle, skip it */
++ }
++ else if(c == '\033') /* check for iso-2022 escape */
++ Writechar(c, match_escapes(line));
++ else
++ Writechar(c, 0);
++ }
++}
++
++
++/*----------------------------------------------------------------------
++ Printf style output line to the screen at given position, 1 arg
++
++ Input: position on the screen
++ line of text to output
++
++ Result: text is output
++ cursor position is update
++ ----------------------------------------------------------------------*/
++void
++/*VARARGS2*/
++PutLine1(x, y, line, arg1)
++ int x, y;
++ char *line;
++ void *arg1;
++{
++ char buffer[PUTLINE_BUFLEN];
++
++ sprintf(buffer, line, arg1);
++ PutLine0(x, y, buffer);
++}
++
++
++/*----------------------------------------------------------------------
++ Printf style output line to the screen at given position, 2 args
++
++ Input: position on the screen
++ line of text to output
++
++ Result: text is output
++ cursor position is update
++ ----------------------------------------------------------------------*/
++void
++/*VARARGS3*/
++PutLine2(x, y, line, arg1, arg2)
++ int x, y;
++ char *line;
++ void *arg1, *arg2;
++{
++ char buffer[PUTLINE_BUFLEN];
++
++ sprintf(buffer, line, arg1, arg2);
++ PutLine0(x, y, buffer);
++}
++
++
++/*----------------------------------------------------------------------
++ Printf style output line to the screen at given position, 3 args
++
++ Input: position on the screen
++ line of text to output
++
++ Result: text is output
++ cursor position is update
++ ----------------------------------------------------------------------*/
++void
++/*VARARGS4*/
++PutLine3(x, y, line, arg1, arg2, arg3)
++ int x, y;
++ char *line;
++ void *arg1, *arg2, *arg3;
++{
++ char buffer[PUTLINE_BUFLEN];
++
++ sprintf(buffer, line, arg1, arg2, arg3);
++ PutLine0(x, y, buffer);
++}
++
++
++/*----------------------------------------------------------------------
++ Printf style output line to the screen at given position, 4 args
++
++ Args: x -- column position on the screen
++ y -- column position on the screen
++ line -- printf style line of text to output
++
++ Result: text is output
++ cursor position is update
++ ----------------------------------------------------------------------*/
++void
++/*VARARGS5*/
++PutLine4(x, y, line, arg1, arg2, arg3, arg4)
++ int x, y;
++ char *line;
++ void *arg1, *arg2, *arg3, *arg4;
++{
++ char buffer[PUTLINE_BUFLEN];
++
++ sprintf(buffer, line, arg1, arg2, arg3, arg4);
++ PutLine0(x, y, buffer);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Printf style output line to the screen at given position, 5 args
++
++ Args: x -- column position on the screen
++ y -- column position on the screen
++ line -- printf style line of text to output
++
++ Result: text is output
++ cursor position is update
++ ----------------------------------------------------------------------*/
++void
++/*VARARGS6*/
++PutLine5(x, y, line, arg1, arg2, arg3, arg4, arg5)
++ int x, y;
++ char *line;
++ void *arg1, *arg2, *arg3, *arg4, *arg5;
++{
++ char buffer[PUTLINE_BUFLEN];
++
++ sprintf(buffer, line, arg1, arg2, arg3, arg4, arg5);
++ PutLine0(x, y, buffer);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Output a line to the screen, centered
++
++ Input: Line number to print on, string to output
++
++ Result: String is output to screen
++ Returns column number line is output on
++ ----------------------------------------------------------------------*/
++int
++Centerline(line, string)
++ int line;
++ char *string;
++{
++ register int length, col;
++
++ length = strlen(string);
++
++ if (length > ps_global->ttyo->screen_cols)
++ col = 0;
++ else
++ col = (ps_global->ttyo->screen_cols - length) / 2;
++
++ PutLine0(line, col, string);
++ return(col);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Clear specified line on the screen
++
++ Result: The line is blanked and the cursor is left at column 0.
++
++ ----*/
++void
++ClearLine(n)
++ int n;
++{
++ if(ps_global->in_init_seq)
++ return;
++
++ MoveCursor(n, 0);
++ CleartoEOLN();
++}
++
++
++
++/*----------------------------------------------------------------------
++ Clear specified lines on the screen
++
++ Result: The lines starting at 'x' and ending at 'y' are blanked
++ and the cursor is left at row 'x', column 0
++
++ ----*/
++void
++ClearLines(x, y)
++ int x, y;
++{
++ int i;
++
++ for(i = x; i <= y; i++)
++ ClearLine(i);
++
++ MoveCursor(x, 0);
++}
++
++
++
++/*----------------------------------------------------------------------
++ Indicate to the screen painting here that the position of the cursor
++ has been disturbed and isn't where these functions might think.
++ ----*/
++void
++clear_cursor_pos()
++{
++ _line = FARAWAY;
++ _col = FARAWAY;
++}
++
++
++/*----------------------------------------------------------------------
++ Return current inverse state
++
++ Result: returns 1 if in inverse state, 0 if not.
++ ----------------------------------------------------------------------*/
++int
++InverseState()
++{
++ return(_in_inverse);
++}
++
++