1
0
mirror of https://git.FreeBSD.org/ports.git synced 2024-11-01 22:05:08 +00:00
freebsd-ports/chinese/pine4/files/patch-an
Vanilla I. Shu e144099054 Upgrade to 4.00
Submitted by:	Liang Tai-hwa <avatar@www.mmlab.cse.yzu.edu.tw>
1998-07-21 14:11:48 +00:00

5417 lines
143 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--- 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, "<22>ȯ<EFBFBD><C8AF>}<7D>ҥ<EFBFBD><D2A5>a<EFBFBD><61><EFBFBD>ɮק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,
+ "<22>ɮקX<D7A7>W<EFBFBD>٤<EFBFBD><D9A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>\\<5C><> \"..\"");
+ 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,
+ "<22>ɮקX<D7A7>i<EFBFBD>}<7D><><EFBFBD>~<7E>G\"%s\" <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϥΪ<CFA5>",
+ 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 <20>R<EFBFBD>O<EFBFBD><4F><EFBFBD><EFBFBD>");
+ 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, "<22>L<EFBFBD>k<EFBFBD>_<EFBFBD>l<EFBFBD>R<EFBFBD>O<EFBFBD>G%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", "<22>O"},
+ {'n', 'n', "N", "<22>_"},
+ {ctrl('P'), 10, "^P", "<22>e<EFBFBD>@<40>L<EFBFBD><4C><EFBFBD><EFBFBD>"},
+ {ctrl('N'), 11, "^N", "<22>U<EFBFBD>@<40>L<EFBFBD><4C><EFBFBD><EFBFBD>"},
+ {-2, 0, NULL, NULL},
+ {'c', 'c', "C", "<22>۩w<DBA9>L<EFBFBD><4C><EFBFBD><EFBFBD>"},
+ {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,
+ "<22>|<7C><><EFBFBD><EFBFBD><EFBFBD>ܦL<DCA6><4C><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD>ХΥD<CEA5><44><EFBFBD><EFBFBD><E6A4A4><EFBFBD>]<5D>w<EFBFBD>ӿ<EFBFBD><D3BF>ܡ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,
+ "<22><><EFBFBD>H \"%s\" <20>R<EFBFBD>O<EFBFBD>C<EFBFBD>L<EFBFBD><4C>", 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, "<22><><EFBFBD>J<EFBFBD>R<EFBFBD>O<EFBFBD>G");
+ 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,
+ "<22><><EFBFBD>H \"%s\" <20>R<EFBFBD>O<EFBFBD>C<EFBFBD>L<EFBFBD><4C>", command);
+ }
+ }
+ }
+
+ if(cmd == 'x' || cmd == 'n'){
+ q_status_message(SM_ORDER, 0, 2, "<22><><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD>L");
+ 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,
+ "<22><><EFBFBD>C<EFBFBD>L<EFBFBD>ܮ<EFBFBD><DCAE>W<EFBFBD>L<EFBFBD><4C><EFBFBD><EFBFBD>...");
+ 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,
+ "<22>L<EFBFBD><4C><EFBFBD><EFBFBD><EFBFBD>}<7D>ҿ<EFBFBD><D2BF>~<7E>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, "<22>C<EFBFBD>L<EFBFBD><4C><EFBFBD>O<EFBFBD><4F><EFBFBD><EFBFBD>");
+ 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,
+ "<22>۸<EFBFBD><DBB8>ƧX \"%s\" <20><><EFBFBD>R<EFBFBD><52> %s <20>ʫH<CAAB><48>",
+ 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,
+ "<22>۸<EFBFBD><DBB8>ƧX \"%s\" <20><><EFBFBD>R<EFBFBD><52> %s <20>ʫH<CAAB><48>",
+ 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, "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>!");
+ }
+ 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,
+ "<22><> \"<22>s<EFBFBD><73>\" <20>A<EFBFBD>^<5E><> \"<22>O\" <20><><EFBFBD>~<7E>򭫰e \"<22>W<EFBFBD><57><EFBFBD><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD>H<EFBFBD><48><EFBFBD>H\"");
+/*
+ "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","<22><><EFBFBD>U<EFBFBD><55><EFBFBD><EFBFBD>",KS_SCREENHELP}, {"^C","<22><><EFBFBD><EFBFBD>",KS_NONE},
+ {"^T","xxx",KS_NONE}, {"Ret","<22>P<EFBFBD>N",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);
+}
+
+