head	1.7;
access;
symbols
	RELEASE_4_2_0:1.6
	RELEASE_4_1_1:1.6
	RELEASE_4_1_0:1.6
	RELEASE_3_5_0:1.6
	RELEASE_4_0_0:1.6
	RELEASE_3_4_0:1.6
	RELEASE_3_3_0:1.5
	RELEASE_3_2_0:1.5
	RELEASE_3_1_0:1.5
	RELEASE_2_2_8:1.5
	RELEASE_3_0_0:1.5
	RELEASE_2_2_7:1.3
	RELEASE_2_2_6:1.2
	zh_pine_3_95:1.1.1.1
	WOJU:1.1.1;
locks; strict;
comment	@# @;


1.7
date	2000.11.17.19.06.52;	author keith;	state dead;
branches;
next	1.6;

1.6
date	99.11.01.00.40.55;	author steve;	state Exp;
branches;
next	1.5;

1.5
date	98.08.09.00.51.25;	author steve;	state Exp;
branches;
next	1.4;

1.4
date	98.07.30.12.30.38;	author vanilla;	state Exp;
branches;
next	1.3;

1.3
date	98.07.21.14.11.36;	author vanilla;	state Exp;
branches;
next	1.2;

1.2
date	98.02.02.14.34.27;	author vanilla;	state Exp;
branches;
next	1.1;

1.1
date	97.12.01.19.47.23;	author vanilla;	state Exp;
branches
	1.1.1.1;
next	;

1.1.1.1
date	97.12.01.19.47.23;	author vanilla;	state Exp;
branches;
next	;


desc
@@


1.7
log
@Upgrade from 4.21 to 4.30.

Especially thanks to the maintainer, Tai-hwa Liang (avatar), for
ending those long lasting evil +400k files/patch-*. :-)

According to kkenn, pine4's security will exist until it's totally
rewritten, so I'll leave pkg-install untouched, which shows the
security warning.
PR:		ports/22436
@
text
@--- pico/estruct.h.orig	Tue Oct  5 04:02:41 1999
+++ pico/estruct.h	Thu Oct 14 10:53:41 1999
@@@@ -130,7 +130,7 @@@@
 #define	LOBIT_CHAR(C)	((C) > 0x1f && (C) < 0x7f)
 #define	HIBIT_CHAR(C)	((C) > 0x7f && (C) <= 0xff)
 #define	HIBIT_OK(C)	(!(gmode & MDHBTIGN))
-#define	VALID_KEY(C)	(LOBIT_CHAR(C) || (HIBIT_OK(C) && HIBIT_CHAR(C)))
+#define	VALID_KEY(C)	(((C) & 0x80) ? 1 : LOBIT_CHAR(C))
 #define ctrl(c)         ((c) & 0x1f)    /* control character mapping */
 
 #define STDIN_FD	0
@


1.6
log
@Update to version 4.20.

PR:		14575
Submitted by:	maintainer
@
text
@@


1.5
log
@Cleanups from maintainer.

PR:		7468
Submitted by:	maintainer
@
text
@d1 3
a3 3
--- pico/estruct.h.orig	Fri May 29 06:56:52 1998
+++ pico/estruct.h	Tue Aug  4 12:42:14 1998
@@@@ -125,7 +125,7 @@@@
@


1.4
log
@Upgrade to 4.02,
Submitted by:	maintainer
@
text
@d1 11
a11 20
--- pico/main.c.orig	Thu Jul 16 16:30:47 1998
+++ pico/main.c	Tue Jul 28 08:35:00 1998
@@@@ -66,7 +66,7 @@@@
     { F9,  (CTRL|'K')},
     { F10, (CTRL|'U')},
     { F11, (CTRL|'C')},
-#ifdef	SPELLER
+#if defined(SPELLER) && !defined(__FreeBSD__)
     { F12, (CTRL|'T')}
 #else
     { F12, (CTRL|'D')}
@@@@ -424,7 +424,7 @@@@
 	    gmode ^= MDSSPD;
 	    break;
 	  case 'q':			/* -q for termcap takes precedence */
-	    gmode ^= MDTCAPWINS;
+	    gmode |= MDTCAPWINS;
 	    break;
 	  case 'w':			/* -w turn off word wrap */
 	    gmode ^= MDWRAP;
@


1.3
log
@Upgrade to 4.00
Submitted by:	Liang Tai-hwa <avatar@@www.mmlab.cse.yzu.edu.tw>
@
text
@d1 20
a20 5416
--- pine/osdep/os-bsf.c.orig	Wed Jul 15 17:02:35 1998
+++ pine/osdep/os-bsf.c	Wed Jul 15 17:02:35 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, "ȯ}ҥaɮקX");
+	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,
+			 "ɮקXW٤\\ \"..\"");
+	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,
+                    "ɮקXi}~G\"%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(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 RO");
+	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, "Lk_lROG%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", "O"},
+	{'n', 'n', "N", "_"},
+	{ctrl('P'), 10, "^P", "e@@L"},
+	{ctrl('N'), 11, "^N", "U@@L"},
+	{-2,   0,   NULL, NULL},
+	{'c', 'c', "C", "۩wL"},
+	{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,
+	"|ܦLCХΥD椤]wӿܡC");
+	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 %susing \"%s\" ? ", desc ? desc : "",
+	    *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,
+		"H \"%s\" ROCL", 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, "JROG");
+	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,
+		    "H \"%s\" ROCL", command);
+	    }
+	}
+    }
+
+    if(cmd == 'x' || cmd == 'n'){
+	q_status_message(SM_ORDER, 0, 2, "CL");
+	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,
+	    "CLܮWL...");
+        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,
+			      "L}ҿ~G%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, "CLO");
+    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,
+			  "۸ƧX \"%s\" R %s ʫH",
+			  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,
+			  "۸ƧX \"%s\" R %s ʫH",
+			  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,
+	  " \"s\" A^ \"O\" ~򭫰e \"W_HH\"");
+/*
+	  "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","U",KS_SCREENHELP},	{"^C","",KS_NONE},
+	{"^T","xxx",KS_NONE},		{"Ret","PN",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
+	      _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);
+}
+
+
@


1.2
log
@Add a lot of patches of chinese menu.
Submitted by:	maintainer
@
text
@d1 5416
a5416 32
*** pico/os_unix.h.orig	Thu Jun 13 00:47:23 1996
--- pico/os_unix.h	Thu Jan 29 21:29:51 1998
***************
*** 122,133 ****
  /*
   * Place where mail gets delivered (for pico's new mail checking)
   */
  #if	defined(sv3) || defined(ct) || defined(isc) || defined(AUX) || defined(sgi)
  #define	MAILDIR		"/usr/mail"
  #else
  #define	MAILDIR		"/usr/spool/mail"
  #endif
! 
  
  /*
   * What and where the tool that checks spelling is located.  If this is
--- 122,136 ----
  /*
   * Place where mail gets delivered (for pico's new mail checking)
   */
+ #ifdef __FreeBSD__
+ #define MAILDIR         "/var/mail"
+ #else
  #if	defined(sv3) || defined(ct) || defined(isc) || defined(AUX) || defined(sgi)
  #define	MAILDIR		"/usr/mail"
  #else
  #define	MAILDIR		"/usr/spool/mail"
  #endif
! #endif
  
  /*
   * What and where the tool that checks spelling is located.  If this is
@


1.1
log
@Initial revision
@
text
@d1 32
a32 8
--- pico/estruct.h.orig	Sat Dec 28 02:40:35 1996
+++ pico/estruct.h	Sat Dec 28 02:41:45 1996
@@@@ -114,6 +114,6 @@@@
 #define	LOBIT_CHAR(C)	((C) > 0x1f && (C) < 0x7f)
 #define	HIBIT_CHAR(C)	((C) > 0x7f && (C) <= 0xff)
 #define	HIBIT_OK(C)	(!(gmode & MDHBTIGN))
-#define	VALID_KEY(C)	(LOBIT_CHAR(C) || (HIBIT_OK(C) && HIBIT_CHAR(C)))
+#define	VALID_KEY(ch)  (((ch) & 0x80) ? 1 : LOBIT_CHAR(ch))
@


1.1.1.1
log
@Fix chinese display/input problem in pine.
PR:		ports/5083
Submitted by:	Yen-Shuo Su <yssu@@CCCA.NCTU.edu.tw>
@
text
@@
