diff options
author | Achim Gottinger <achim@gentoo.org> | 2001-04-30 11:40:12 +0000 |
---|---|---|
committer | Achim Gottinger <achim@gentoo.org> | 2001-04-30 11:40:12 +0000 |
commit | 1e4b581725e1a7a7c13b336f3a400ccddd16f81d (patch) | |
tree | fd15ffd5b93cf4325374f478fab91303c35bf2e4 /net-mail/pine/files | |
parent | Cleanup (diff) | |
download | gentoo-2-1e4b581725e1a7a7c13b336f3a400ccddd16f81d.tar.gz gentoo-2-1e4b581725e1a7a7c13b336f3a400ccddd16f81d.tar.bz2 gentoo-2-1e4b581725e1a7a7c13b336f3a400ccddd16f81d.zip |
Cleanup
Diffstat (limited to 'net-mail/pine/files')
-rw-r--r-- | net-mail/pine/files/digest-pine-4.21-r2 | 1 | ||||
-rw-r--r-- | net-mail/pine/files/filter.c | 6902 | ||||
-rw-r--r-- | net-mail/pine/files/os.c | 5673 |
3 files changed, 0 insertions, 12576 deletions
diff --git a/net-mail/pine/files/digest-pine-4.21-r2 b/net-mail/pine/files/digest-pine-4.21-r2 deleted file mode 100644 index cdba66b6aa7f..000000000000 --- a/net-mail/pine/files/digest-pine-4.21-r2 +++ /dev/null @@ -1 +0,0 @@ -MD5 9252a061387de806f8aa1ced885d41f6 pine4.21.tar.gz diff --git a/net-mail/pine/files/filter.c b/net-mail/pine/files/filter.c deleted file mode 100644 index ed1c2d6aedda..000000000000 --- a/net-mail/pine/files/filter.c +++ /dev/null @@ -1,6902 +0,0 @@ -#if !defined(lint) && !defined(DOS) -static char rcsid[] = "$Id: filter.c,v 1.1 2000/11/15 16:46:05 achim Exp $"; -#endif -/*---------------------------------------------------------------------- - - 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-1999 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 * - *********************************************************************** - - - ----------------------------------------------------------------------*/ - -/*====================================================================== - filter.c - - This code provides a generalized, flexible way to allow - piping of data thru filters. Each filter is passed a structure - that it will use to hold its static data while it operates on - the stream of characters that are passed to it. After processing - it will either return or call the next filter in - the pipe with any character (or characters) it has ready to go. This - means some terminal type of filter has to be the last in the - chain (i.e., one that writes the passed char someplace, but doesn't - call another filter). - - See below for more details. - - The motivation is to handle MIME decoding, richtext conversion, - iso_code stripping and anything else that may come down the - pike (e.g., PEM) in an elegant fashion. mikes (920811) - - TODO: - reasonable error handling - - ====*/ - - -#include "headers.h" - - -/* - * Internal prototypes - */ -int gf_so_readc PROTO((unsigned char *)); -int gf_so_writec PROTO((int)); -int gf_sreadc PROTO((unsigned char *)); -int gf_swritec PROTO((int)); -int gf_freadc PROTO((unsigned char *)); -int gf_fwritec PROTO((int)); -void gf_terminal PROTO((FILTER_S *, int)); -char *gf_filter_puts PROTO((char *)); -void gf_filter_eod PROTO((void)); -void gf_error PROTO((char *)); -void gf_8bit_put PROTO((FILTER_S *, int)); -int so_reaquire PROTO((STORE_S *)); -void *so_file_open PROTO((STORE_S *)); -int so_cs_writec PROTO((int, STORE_S *)); -int so_pico_writec PROTO((int, STORE_S *)); -int so_file_writec PROTO((int, STORE_S *)); -int so_cs_readc PROTO((unsigned char *, STORE_S *)); -int so_pico_readc PROTO((unsigned char *, STORE_S *)); -int so_file_readc PROTO((unsigned char *, STORE_S *)); -int so_cs_puts PROTO((STORE_S *, char *)); -int so_pico_puts PROTO((STORE_S *, char *)); -int so_file_puts PROTO((STORE_S *, char *)); - - -/* - * GENERALIZED STORAGE FUNCTIONS. Idea is to allow creation of - * storage objects that can be written into and read from without - * the caller knowing if the storage is core or in a file - * or whatever. - */ -#define MSIZE_INIT 8192 -#define MSIZE_INC 4096 - - -/* - * System specific options - */ -#ifdef DOS -#define NO_PIPE -#endif -#if defined(DOS) || defined(OS2) -#define CRLF_NEWLINES -#endif - -/* - * Various parms for opening and creating files directly and via stdio. - * NOTE: If "binary" mode file exists, use it. - */ -#ifdef O_BINARY -#define STDIO_READ "rb" -#define STDIO_APPEND "a+b" -#else -#define O_BINARY 0 -#define STDIO_READ "r" -#define STDIO_APPEND "a+" -#endif - -#define OP_FL_RDWR (O_RDWR | O_CREAT | O_APPEND | O_BINARY) -#define OP_FL_RDONLY (O_RDONLY | O_BINARY) - -#ifdef S_IREAD -#define OP_MD_USER (S_IREAD | S_IWRITE) -#else -#define OP_MD_USER 0600 -#endif - -#ifdef S_IRUSR -#define OP_MD_ALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | \ - S_IROTH | S_IWOTH) -#else -#define OP_MD_ALL 0666 -#endif - - -/* - * allocate resources associated with the specified type of - * storage. If requesting a named file object, open it for - * appending, else just open a temp file. - * - * return the filled in storage object - */ -STORE_S * -so_get(source, name, rtype) - SourceType source; /* requested storage type */ - char *name; /* file name */ - int rtype; /* file access type */ -{ - STORE_S *so = (STORE_S *)fs_get(sizeof(STORE_S)); - - memset(so, 0, sizeof(STORE_S)); - so->flags |= rtype; - - if(name) /* stash the name */ - so->name = cpystr(name); -#ifdef DOS - else if(source == TmpFileStar || source == FileStar){ - /* - * Coerce to TmpFileStar. The MSC library's "tmpfile()" - * doesn't observe the "TMP" or "TEMP" environment vars and - * always wants to write "\". This is problematic in shared, - * networked environments. - */ - source = TmpFileStar; - so->name = temp_nam(NULL, "pi"); - } -#else - else if(source == TmpFileStar) /* make one up! */ - so->name = temp_nam(NULL, "pine-tmp"); -#endif - - so->src = source; - if(so->src == FileStar || so->src == TmpFileStar){ - so->writec = so_file_writec; - so->readc = so_file_readc; - so->puts = so_file_puts; - - /* - * The reason for both FileStar and TmpFileStar types is - * that, named or unnamed, TmpFileStar's are unlinked - * when the object is given back to the system. This is - * useful for keeping us from running out of file pointers as - * the pointer associated with the object can be temporarily - * returned to the system without destroying the object. - * - * The programmer is warned to be careful not to assign the - * TmpFileStar type to any files that are expected to remain - * after the dust has settled! - */ - if(so->name){ - if(!(so->txt = so_file_open(so))){ - dprint(1, (debugfile, "so_get error: %s : %s", so->name, - error_description(errno))); - fs_give((void **)&so->name); - fs_give((void **)&so); /* so freed & set to NULL */ - } - } - else{ - if(!(so->txt = (void *) create_tmpfile())){ - dprint(1, (debugfile, "so_get error: tmpfile : %s", - error_description(errno))); - fs_give((void **)&so); /* so freed & set to NULL */ - } - } - } - else if(so->src == PicoText){ - so->writec = so_pico_writec; - so->readc = so_pico_readc; - so->puts = so_pico_puts; - if(!(so->txt = pico_get())){ - dprint(1, (debugfile, "so_get error: alloc of pico text space")); - if(so->name) - fs_give((void **)&so->name); - fs_give((void **)&so); /* so freed & set to NULL */ - } - } - else{ - so->writec = so_cs_writec; - so->readc = so_cs_readc; - so->puts = so_cs_puts; - so->txt = (void *)fs_get((size_t) MSIZE_INIT * sizeof(char)); - so->dp = so->eod = (unsigned char *) so->txt; - so->eot = so->dp + MSIZE_INIT; - memset(so->eod, 0, so->eot - so->eod); - } - - return(so); -} - - -/* - * so_give - free resources associated with a storage object and then - * the object itself. - */ -void -so_give(so) -STORE_S **so; -{ - if(!so) - return; - - if((*so)->src == FileStar || (*so)->src == TmpFileStar){ - if((*so)->txt) - fclose((FILE *)(*so)->txt); /* disassociate from storage */ - - if((*so)->name && (*so)->src == TmpFileStar) - unlink((*so)->name); /* really disassociate! */ - } - else if((*so)->txt && (*so)->src == PicoText) - pico_give((*so)->txt); - else if((*so)->txt) - fs_give((void **)&((*so)->txt)); - - if((*so)->name) - fs_give((void **)&((*so)->name)); /* blast the name */ - - fs_give((void **)so); /* release the object */ -} - - -/* - * so_file_open - */ -void * -so_file_open(so) - STORE_S *so; -{ - char *type = ((so->flags) & WRITE_ACCESS) ? STDIO_APPEND : STDIO_READ; - int flags = ((so->flags) & WRITE_ACCESS) ? OP_FL_RDWR : OP_FL_RDONLY, - mode = (((so->flags) & OWNER_ONLY) || so->src == TmpFileStar) - ? OP_MD_USER : OP_MD_ALL, - fd; - - /* - * Use open instead of fopen so we can make temp files private. - */ - return(((fd = open(so->name, flags, mode)) >= 0) - ? (so->txt = (void *) fdopen(fd, type)) : NULL); -} - - -/* - * put a character into the specified storage object, - * expanding if neccessary - * - * return 1 on success and 0 on failure - */ -int -so_cs_writec(c, so) - int c; - STORE_S *so; -{ - if(so->dp >= so->eot){ - size_t cur_o = so->dp - (unsigned char *) so->txt; - size_t data_o = so->eod - (unsigned char *) so->txt; - size_t size = (so->eot - (unsigned char *) so->txt) + MSIZE_INC; - - fs_resize(&so->txt, size * sizeof(char)); - so->dp = (unsigned char *) so->txt + cur_o; - so->eod = (unsigned char *) so->txt + data_o; - so->eot = (unsigned char *) so->txt + size; - memset(so->eod, 0, so->eot - so->eod); - } - - *so->dp++ = (unsigned char) c; - if(so->dp > so->eod) - so->eod = so->dp; - - return(1); -} - -int -so_pico_writec(c, so) - int c; - STORE_S *so; -{ - unsigned char ch = (unsigned char) c; - - return(pico_writec(so->txt, ch)); -} - -int -so_file_writec(c, so) - int c; - STORE_S *so; -{ - unsigned char ch = (unsigned char) c; - int rv = 0; - - if(so->txt || so_reaquire(so)) - do - rv = fwrite(&ch,sizeof(unsigned char),(size_t)1,(FILE *)so->txt); - while(!rv && ferror((FILE *)so->txt) && errno == EINTR); - - return(rv); -} - - -/* - * get a character from the specified storage object. - * - * return 1 on success and 0 on failure - */ -int -so_cs_readc(c, so) - unsigned char *c; - STORE_S *so; -{ - return((so->dp < so->eod) ? *c = *(so->dp)++, 1 : 0); -} - -int -so_pico_readc(c, so) - unsigned char *c; - STORE_S *so; -{ - return(pico_readc(so->txt, c)); -} - -int -so_file_readc(c, so) - unsigned char *c; - STORE_S *so; -{ - int rv = 0; - - if(so->txt || so_reaquire(so)) - do - rv = fread(c, sizeof(char), (size_t)1, (FILE *)so->txt); - while(!rv && ferror((FILE *)so->txt) && errno == EINTR); - - return(rv); -} - - -/* - * write a string into the specified storage object, - * expanding if necessary (and cheating if the object - * happens to be a file!) - * - * return 1 on success and 0 on failure - */ -int -so_cs_puts(so, s) - STORE_S *so; - char *s; -{ - int slen = strlen(s); - - if(so->dp + slen >= so->eot){ - register size_t cur_o = so->dp - (unsigned char *) so->txt; - register size_t data_o = so->eod - (unsigned char *) so->txt; - register size_t len = so->eot - (unsigned char *) so->txt; - while(len <= cur_o + slen + 1) - len += MSIZE_INC; /* need to resize! */ - - fs_resize(&so->txt, len * sizeof(char)); - so->dp = (unsigned char *)so->txt + cur_o; - so->eod = (unsigned char *)so->txt + data_o; - so->eot = (unsigned char *)so->txt + len; - memset(so->eod, 0, so->eot - so->eod); - } - - memcpy(so->dp, s, slen); - so->dp += slen; - if(so->dp > so->eod) - so->eod = so->dp; - - return(1); -} - -int -so_pico_puts(so, s) - STORE_S *so; - char *s; -{ - return(pico_puts(so->txt, s)); -} - -int -so_file_puts(so, s) - STORE_S *so; - char *s; -{ - int rv = *s ? 0 : 1; - - if(!rv && (so->txt || so_reaquire(so))) - do - rv = fwrite(s, strlen(s)*sizeof(char), (size_t)1, (FILE *)so->txt); - while(!rv && ferror((FILE *)so->txt) && errno == EINTR); - - return(rv); -} - - -/* - * - */ -int -so_nputs(so, s, n) - STORE_S *so; - char *s; - long n; -{ - while(n--) - if(!so_writec((unsigned char) *s++, so)) - return(0); /* ERROR putting char ! */ - - return(1); -} - - - -/* - * Position the storage object's pointer to the given offset - * from the start of the object's data. - */ -int -so_seek(so, pos, orig) - STORE_S *so; - long pos; - int orig; -{ - if(so->src == CharStar){ - switch(orig){ - case 0 : /* SEEK_SET */ - return((pos < so->eod - (unsigned char *) so->txt) - ? so->dp = (unsigned char *)so->txt + pos, 0 : -1); - case 1 : /* SEEK_CUR */ - return((pos > 0) - ? ((pos < so->eod - so->dp) ? so->dp += pos, 0: -1) - : ((pos < 0) - ? ((-pos < so->dp - (unsigned char *)so->txt) - ? so->dp += pos, 0 : -1) - : 0)); - case 2 : /* SEEK_END */ - return((pos < so->eod - (unsigned char *) so->txt) - ? so->dp = so->eod - pos, 0 : -1); - default : - return(-1); - } - } - else if(so->src == PicoText) - return(pico_seek(so->txt, pos, orig)); - else /* FileStar or TmpFileStar */ - return((so->txt || so_reaquire(so)) - ? fseek((FILE *)so->txt,pos,orig) - : -1); -} - - -/* - * Change the given storage object's size to that specified. If size - * is less than the current size, the internal pointer is adjusted and - * all previous data beyond the given size is lost. - * - * Returns 0 on failure. - */ -int -so_truncate(so, size) - STORE_S *so; - long size; -{ - if(so->src == CharStar){ - if(so->eod < (unsigned char *) so->txt + size){ /* alloc! */ - unsigned char *newtxt = (unsigned char *) so->txt; - register size_t len = so->eot - (unsigned char *) so->txt; - - while(len <= size) - len += MSIZE_INC; /* need to resize! */ - - if(len > so->eot - (unsigned char *) newtxt){ - fs_resize((void **) &newtxt, len * sizeof(char)); - so->eot = newtxt + len; - so->eod = newtxt + (so->eod - (unsigned char *) so->txt); - memset(so->eod, 0, so->eot - so->eod); - } - - so->eod = newtxt + size; - so->dp = newtxt + (so->dp - (unsigned char *) so->txt); - so->txt = newtxt; - } - else if(so->eod > (unsigned char *) so->txt + size){ - if(so->dp > (so->eod = (unsigned char *)so->txt + size)) - so->dp = so->eod; - - memset(so->eod, 0, so->eot - so->eod); - } - - return(1); - } - else if(so->src == PicoText){ - fatal("programmer botch: unsupported so_truncate call"); - /*NOTREACHED*/ - } - else /* FileStar or TmpFileStar */ - return(fflush((FILE *) so->txt) != EOF - && fseek((FILE *) so->txt, size, 0) == 0 - && ftruncate(fileno((FILE *)so->txt), size) == 0); -} - - -/* - * so_release - a rather misnamed function. the idea is to release - * what system resources we can (e.g., open files). - * while maintaining a reference to it. - * it's up to the functions that deal with this object - * next to re-aquire those resources. - */ -int -so_release(so) -STORE_S *so; -{ - if(so->txt && so->name && (so->src == FileStar || so->src == TmpFileStar)){ - if(fgetpos((FILE *)so->txt, (fpos_t *)&(so->used)) == 0){ - fclose((FILE *)so->txt); /* free the handle! */ - so->txt = NULL; - } - } - - return(1); -} - - -/* - * so_reaquire - get any previously released system resources we - * may need for the given storage object. - * NOTE: at the moment, only FILE * types of objects are - * effected, so it only needs to be called before - * references to them. - * - */ -so_reaquire(so) -STORE_S *so; -{ - int rv = 1; - - if(!so->txt && (so->src == FileStar || so->src == TmpFileStar)){ - if(!(so->txt = so_file_open(so))){ - q_status_message2(SM_ORDER,3,5, "ERROR reopening %s : %s", so->name, - error_description(errno)); - rv = 0; - } - else if(fsetpos((FILE *)so->txt, (fpos_t *)&(so->used))){ - q_status_message2(SM_ORDER, 3, 5, "ERROR positioning in %s : %s", - so->name, error_description(errno)); - rv = 0; - } - } - - return(rv); -} - - -/* - * so_text - return a pointer to the text the store object passed - */ -void * -so_text(so) -STORE_S *so; -{ - return((so) ? so->txt : NULL); -} - - -/* - * END OF GENERALIZE STORAGE FUNCTIONS - */ - - -/* - * Start of filters, pipes and various support functions - */ - -/* - * pointer to first function in a pipe, and pointer to last filter - */ -FILTER_S *gf_master = NULL; -static gf_io_t last_filter; -static char *gf_error_string; -static long gf_byte_count; -static jmp_buf gf_error_state; - - -/* - * A list of states used by the various filters. Reused in many filters. - */ -#define DFL 0 -#define EQUAL 1 -#define HEX 2 -#define WSPACE 3 -#define CCR 4 -#define CLF 5 -#define TOKEN 6 -#define TAG 7 -#define HANDLE 8 -#define HDATA 9 - - - -/* - * Macros to reduce function call overhead associated with calling - * each filter for each byte filtered, and to minimize filter structure - * dereferences. NOTE: "queuein" has to do with putting chars into the - * filter structs data queue. So, writing at the queuein offset is - * what a filter does to pass processed data out of itself. Ditto for - * queueout. This explains the FI --> queueout init stuff below. - */ -#define GF_QUE_START(F) (&(F)->queue[0]) -#define GF_QUE_END(F) (&(F)->queue[GF_MAXBUF - 1]) - -#define GF_IP_INIT(F) ip = (F) ? &(F)->queue[(F)->queuein] : NULL -#define GF_EIB_INIT(F) eib = (F) ? GF_QUE_END(F) : NULL -#define GF_OP_INIT(F) op = (F) ? &(F)->queue[(F)->queueout] : NULL -#define GF_EOB_INIT(F) eob = (F) ? &(F)->queue[(F)->queuein] : NULL - -#define GF_IP_END(F) (F)->queuein = ip - GF_QUE_START(F) -#define GF_OP_END(F) (F)->queueout = op - GF_QUE_START(F) - -#define GF_INIT(FI, FO) register unsigned char *GF_OP_INIT(FI); \ - register unsigned char *GF_EOB_INIT(FI); \ - register unsigned char *GF_IP_INIT(FO); \ - register unsigned char *GF_EIB_INIT(FO); - -#define GF_CH_RESET(F) (op = eob = GF_QUE_START(F), \ - (F)->queueout = (F)->queuein = 0) - -#define GF_END(FI, FO) (GF_OP_END(FI), GF_IP_END(FO)) - -#define GF_FLUSH(F) ((int)(GF_IP_END(F), (*(F)->f)((F), GF_DATA), \ - GF_IP_INIT(F), GF_EIB_INIT(F))) - -#define GF_PUTC(F, C) ((int)(*ip++ = (C), (ip >= eib) ? GF_FLUSH(F) : 1)) - -#define GF_GETC(F, C) ((op < eob) ? (((C) = *op++), 1) : GF_CH_RESET(F)) - - -/* - * Generalized getc and putc routines. provided here so they don't - * need to be re-done elsewhere to - */ - -/* - * pointers to objects to be used by the generic getc and putc - * functions - */ -static struct gf_io_struct { - FILE *file; - char *txtp; - unsigned long n; -} gf_in, gf_out; - - -#define GF_SO_STACK struct gf_so_stack -static GF_SO_STACK { - STORE_S *so; - GF_SO_STACK *next; -} *gf_so_in, *gf_so_out; - - -/* - * setup to use and return a pointer to the generic - * getc function - */ -void -gf_set_readc(gc, txt, len, src) - gf_io_t *gc; - void *txt; - unsigned long len; - SourceType src; -{ - gf_in.n = len; - if(src == FileStar){ - gf_in.file = (FILE *)txt; - fseek(gf_in.file, 0L, 0); - *gc = gf_freadc; - } - else{ - gf_in.txtp = (char *)txt; - *gc = gf_sreadc; - } -} - - -/* - * setup to use and return a pointer to the generic - * putc function - */ -void -gf_set_writec(pc, txt, len, src) - gf_io_t *pc; - void *txt; - unsigned long len; - SourceType src; -{ - gf_out.n = len; - if(src == FileStar){ - gf_out.file = (FILE *)txt; - *pc = gf_fwritec; - } - else{ - gf_out.txtp = (char *)txt; - *pc = gf_swritec; - } -} - - -/* - * setup to use and return a pointer to the generic - * getc function - */ -void -gf_set_so_readc(gc, so) - gf_io_t *gc; - STORE_S *so; -{ - GF_SO_STACK *sp = (GF_SO_STACK *) fs_get(sizeof(GF_SO_STACK)); - - sp->so = so; - sp->next = gf_so_in; - gf_so_in = sp; - *gc = gf_so_readc; -} - - -void -gf_clear_so_readc(so) - STORE_S *so; -{ - GF_SO_STACK *sp; - - if(sp = gf_so_in){ - if(so == sp->so){ - gf_so_in = gf_so_in->next; - fs_give((void **) &sp); - } - else - panic("Programmer botch: Can't unstack store readc"); - } - else - panic("Programmer botch: NULL store clearing store readc"); -} - - -/* - * setup to use and return a pointer to the generic - * putc function - */ -void -gf_set_so_writec(pc, so) - gf_io_t *pc; - STORE_S *so; -{ - GF_SO_STACK *sp = (GF_SO_STACK *) fs_get(sizeof(GF_SO_STACK)); - - sp->so = so; - sp->next = gf_so_out; - gf_so_out = sp; - *pc = gf_so_writec; -} - - -void -gf_clear_so_writec(so) - STORE_S *so; -{ - GF_SO_STACK *sp; - - if(sp = gf_so_out){ - if(so == sp->so){ - gf_so_out = gf_so_out->next; - fs_give((void **) &sp); - } - else - panic("Programmer botch: Can't unstack store writec"); - } - else - panic("Programmer botch: NULL store clearing store writec"); -} - - -/* - * put the character to the object previously defined - */ -int -gf_so_writec(c) -int c; -{ - return(so_writec(c, gf_so_out->so)); -} - - -/* - * get a character from an object previously defined - */ -int -gf_so_readc(c) -unsigned char *c; -{ - return(so_readc(c, gf_so_in->so)); -} - - -/* get a character from a file */ -/* assumes gf_out struct is filled in */ -int -gf_freadc(c) -unsigned char *c; -{ - int rv = 0; - - do { - errno = 0; - clearerr(gf_in.file); - rv = fread(c, sizeof(unsigned char), (size_t)1, gf_in.file); - } while(!rv && ferror(gf_in.file) && errno == EINTR); - - return(rv); -} - - -/* put a character to a file */ -/* assumes gf_out struct is filled in */ -int -gf_fwritec(c) - int c; -{ - unsigned char ch = (unsigned char)c; - int rv = 0; - - do - rv = fwrite(&ch, sizeof(unsigned char), (size_t)1, gf_out.file); - while(!rv && ferror(gf_out.file) && errno == EINTR); - - return(rv); -} - - -/* get a character from a string, return nonzero if things OK */ -/* assumes gf_out struct is filled in */ -int -gf_sreadc(c) -unsigned char *c; -{ - return((gf_in.n) ? *c = *(gf_in.txtp)++, gf_in.n-- : 0); -} - - -/* put a character into a string, return nonzero if things OK */ -/* assumes gf_out struct is filled in */ -int -gf_swritec(c) - int c; -{ - return((gf_out.n) ? *(gf_out.txtp)++ = c, gf_out.n-- : 0); -} - - -/* - * output the given string with the given function - */ -int -gf_puts(s, pc) - register char *s; - gf_io_t pc; -{ - while(*s != '\0') - if(!(*pc)((unsigned char)*s++)) - return(0); /* ERROR putting char ! */ - - return(1); -} - - -/* - * output the given string with the given function - */ -int -gf_nputs(s, n, pc) - register char *s; - long n; - gf_io_t pc; -{ - while(n--) - if(!(*pc)((unsigned char)*s++)) - return(0); /* ERROR putting char ! */ - - return(1); -} - - -/* - * Start of generalized filter routines - */ - -/* - * initializing function to make sure list of filters is empty. - */ -void -gf_filter_init() -{ - FILTER_S *flt, *fltn = gf_master; - - while((flt = fltn) != NULL){ /* free list of old filters */ - fltn = flt->next; - fs_give((void **)&flt); - } - - gf_master = NULL; - gf_error_string = NULL; /* clear previous errors */ - gf_byte_count = 0L; /* reset counter */ -} - - - -/* - * link the given filter into the filter chain - */ -void -gf_link_filter(f, data) - filter_t f; - void *data; -{ - FILTER_S *new, *tail; - -#ifdef CRLF_NEWLINES - /* - * If the system's native EOL convention is CRLF, then there's no - * point in passing data thru a filter that's not doing anything - */ - if(f == gf_nvtnl_local || f == gf_local_nvtnl) - return; -#endif - - new = (FILTER_S *)fs_get(sizeof(FILTER_S)); - memset(new, 0, sizeof(FILTER_S)); - - new->f = f; /* set the function pointer */ - new->opt = data; /* set any optional parameter data */ - (*f)(new, GF_RESET); /* have it setup initial state */ - - if(tail = gf_master){ /* or add it to end of existing */ - while(tail->next) /* list */ - tail = tail->next; - - tail->next = new; - } - else /* attach new struct to list */ - gf_master = new; /* start a new list */ -} - - -/* - * terminal filter, doesn't call any other filters, typically just does - * something with the output - */ -void -gf_terminal(f, flg) - FILTER_S *f; - int flg; -{ - if(flg == GF_DATA){ - GF_INIT(f, f); - - while(op < eob) - if((*last_filter)(*op++) <= 0) /* generic terminal filter */ - gf_error(errno ? error_description(errno) : "Error writing pipe"); - - GF_CH_RESET(f); - } - else if(flg == GF_RESET) - errno = 0; /* prepare for problems */ -} - - -/* - * set some outside gf_io_t function to the terminal function - * for example: a function to write a char to a file or into a buffer - */ -void -gf_set_terminal(f) /* function to set generic filter */ - gf_io_t f; -{ - last_filter = f; -} - - -/* - * common function for filter's to make it known that an error - * has occurred. Jumps back to gf_pipe with error message. - */ -void -gf_error(s) - char *s; -{ - /* let the user know the error passed in s */ - gf_error_string = s; - longjmp(gf_error_state, 1); -} - - -/* - * The routine that shoves each byte through the chain of - * filters. It sets up error handling, and the terminal function. - * Then loops getting bytes with the given function, and passing - * it on to the first filter in the chain. - */ -char * -gf_pipe(gc, pc) - gf_io_t gc, pc; /* how to get a character */ -{ - unsigned char c; - -#if defined(DOS) && !defined(_WINDOWS) - MoveCursor(0, 1); - StartInverse(); -#endif - - dprint(4, (debugfile, "-- gf_pipe: ")); - - /* - * set up for any errors a filter may encounter - */ - if(setjmp(gf_error_state)){ -#if defined(DOS) && !defined(_WINDOWS) - ibmputc(' '); - EndInverse(); -#endif - dprint(4, (debugfile, "ERROR: %s\n", - gf_error_string ? gf_error_string : "NULL")); - return(gf_error_string); /* */ - } - - /* - * set and link in the terminal filter - */ - gf_set_terminal(pc); - gf_link_filter(gf_terminal, NULL); - - /* - * while there are chars to process, send them thru the pipe. - * NOTE: it's necessary to enclose the loop below in a block - * as the GF_INIT macro calls some automatic var's into - * existence. It can't be placed at the start of gf_pipe - * because its useful for us to be called without filters loaded - * when we're just being used to copy bytes between storage - * objects. - */ - { - GF_INIT(gf_master, gf_master); - - while((*gc)(&c)){ - gf_byte_count++; -#ifdef DOS - if(!(gf_byte_count & 0x3ff)) -#ifdef _WINDOWS - /* Under windows we yeild to allow event processing. - * Progress display is handled throught the alarm() - * mechinism. - */ - mswin_yeild (); -#else - /* Poor PC still needs spinning bar */ - ibmputc("/-\\|"[((int) gf_byte_count >> 10) % 4]); - MoveCursor(0, 1); -#endif -#endif - - GF_PUTC(gf_master, c & 0xff); - } - - /* - * toss an end-of-data marker down the pipe to give filters - * that have any buffered data the opportunity to dump it - */ - GF_FLUSH(gf_master); - (*gf_master->f)(gf_master, GF_EOD); - } - -#if defined(DOS) && !defined(_WINDOWS) - ibmputc(' '); - EndInverse(); -#endif - - dprint(1, (debugfile, "done.\n")); - return(NULL); /* everything went OK */ -} - - -/* - * return the number of bytes piped so far - */ -long -gf_bytes_piped() -{ - return(gf_byte_count); -} - - -/* - * filter the given input with the given command - * - * Args: cmd -- command string to execute - * prepend -- string to prepend to filtered input - * source_so -- storage object containing data to be filtered - * pc -- function to write filtered output with - * aux_filters -- additional filters to pass data thru after "cmd" - * - * Returns: NULL on sucess, reason for failure (not alloc'd!) on error - */ -char * -gf_filter(cmd, prepend, source_so, pc, aux_filters) - char *cmd, *prepend; - STORE_S *source_so; - gf_io_t pc; - FILTLIST_S *aux_filters; -{ - unsigned char c; - int flags; - char *errstr = NULL, buf[MAILTMPLEN], *rfile = NULL; - PIPE_S *fpipe; - - dprint(4, (debugfile, "so_filter: \"%s\"\n", cmd)); - - gf_filter_init(); - for( ; aux_filters && aux_filters->filter; aux_filters++) - gf_link_filter(aux_filters->filter, aux_filters->data); - - gf_set_terminal(pc); - gf_link_filter(gf_terminal, NULL); - - /* - * Spawn filter feeding it data, and reading what it writes. - */ - so_seek(source_so, 0L, 0); -#ifdef NO_PIPE - /* - * When there're no pipes for IPC, use an output file to collect - * the result... - */ - flags = PIPE_WRITE | PIPE_NOSHELL | PIPE_RESET; - rfile = temp_nam(NULL, "pf"); -#else - flags = PIPE_WRITE | PIPE_READ | PIPE_NOSHELL | PIPE_RESET; -#endif - - if(fpipe = open_system_pipe(cmd, rfile ? &rfile : NULL, NULL, flags, 0)){ -#ifdef NO_PIPE - if(prepend && (fputs(prepend, fpipe->out.f) == EOF - || fputc('\n', fpipe->out.f) == EOF)) - errstr = error_description(errno); - - /* - * Write the output, and deal with the result later... - */ - while(!errstr && so_readc(&c, source_so)) - if(fputc(c, fpipe->out.f) == EOF) - errstr = error_description(errno); -#else -#ifdef NON_BLOCKING_IO - int n; - - if(fcntl(fileno(fpipe->in.f), F_SETFL, NON_BLOCKING_IO) == -1) - errstr = "Can't set up non-blocking IO"; - - if(prepend && (fputs(prepend, fpipe->out.f) == EOF - || fputc('\n', fpipe->out.f) == EOF)) - errstr = error_description(errno); - - while(!errstr){ - /* if the pipe can't hold a K we're sunk (too bad PIPE_MAX - * isn't ubiquitous ;). - */ - for(n = 0; !errstr && fpipe->out.f && n < 1024; n++) - if(!so_readc(&c, source_so)){ - fclose(fpipe->out.f); - fpipe->out.f = NULL; - } - else if(fputc(c, fpipe->out.f) == EOF) - errstr = error_description(errno); - - /* - * Note: We clear errno here and test below, before ferror, - * because *some* stdio implementations consider - * EAGAIN and EWOULDBLOCK equivalent to EOF... - */ - errno = 0; - clearerr(fpipe->in.f); /* fix from <cananian@cananian.mit.edu> */ - - while(!errstr && fgets(buf, MAILTMPLEN, fpipe->in.f)) - errstr = gf_filter_puts(buf); - - /* then fgets failed! */ - if(!errstr && !(errno == EAGAIN || errno == EWOULDBLOCK)){ - if(feof(fpipe->in.f)) /* nothing else interesting! */ - break; - else if(ferror(fpipe->in.f)) /* bummer. */ - errstr = error_description(errno); - } - else if(errno == EAGAIN || errno == EWOULDBLOCK) - clearerr(fpipe->in.f); - } -#else - if(prepend && (fputs(prepend, fpipe->out.f) == EOF - || fputc('\n', fpipe->out.f) == EOF)) - errstr = error_description(errno); - - /* - * Well, do the best we can, and hope the pipe we're writing - * doesn't fill up before we start reading... - */ - while(!errstr && so_readc(&c, source_so)) - if(fputc(c, fpipe->out.f) == EOF) - errstr = error_description(errno); - - fclose(fpipe->out.f); - fpipe->out.f = NULL; - while(!errstr && fgets(buf, MAILTMPLEN, fpipe->in.f)) - errstr = gf_filter_puts(buf); -#endif /* NON_BLOCKING */ -#endif /* NO_PIPE */ - - gf_filter_eod(); - - if(close_system_pipe(&fpipe) && !errstr) - errstr = "Pipe command returned error."; - -#ifdef NO_PIPE - /* - * retrieve filters result... - */ - { - FILE *fp; - - if(fp = fopen(rfile, STDIO_READ)){ - while(!errstr && fgets(buf, MAILTMPLEN, fp)) - errstr = gf_filter_puts(buf); - - fclose(fp); - } - - fs_give((void **)&rfile); - } -#endif - } - - return(errstr); -} - - -/* - * gf_filter_puts - write the given string down the filter's pipe - */ -char * -gf_filter_puts(s) - register char *s; -{ - GF_INIT(gf_master, gf_master); - - /* - * set up for any errors a filter may encounter - */ - if(setjmp(gf_error_state)){ - dprint(4, (debugfile, "ERROR: gf_filter_puts: %s\n", - gf_error_string ? gf_error_string : "NULL")); - return(gf_error_string); - } - - while(*s) - GF_PUTC(gf_master, (*s++) & 0xff); - - GF_END(gf_master, gf_master); - return(NULL); -} - - -/* - * gf_filter_eod - flush pending data filter's input queue and deliver - * the GF_EOD marker. - */ -void -gf_filter_eod() -{ - GF_INIT(gf_master, gf_master); - GF_FLUSH(gf_master); - (*gf_master->f)(gf_master, GF_EOD); -} - - - - -/* - * END OF PIPE SUPPORT ROUTINES, BEGINNING OF FILTERS - * - * Filters MUST use the specified interface (pointer to filter - * structure, the unsigned character buffer in that struct, and a - * cmd flag), and pass each resulting octet to the next filter in the - * chain. Only the terminal filter need not call another filter. - * As a result, filters share a pretty general structure. - * Typically three main conditionals separate initialization from - * data from end-of-data command processing. - * - * Lastly, being character-at-a-time, they're a little more complex - * to write than filters operating on buffers because some state - * must typically be kept between characters. However, for a - * little bit of complexity here, much convenience is gained later - * as they can be arbitrarily chained together at run time and - * consume few resources (especially memory or disk) as they work. - * (NOTE 951005: even less cpu now that data between filters is passed - * via a vector.) - * - * A few notes about implementing filters: - * - * - A generic filter template looks like: - * - * void - * gf_xxx_filter(f, flg) - * FILTER_S *f; - * int flg; - * { - * GF_INIT(f, f->next); // def's var's to speed queue drain - * - * if(flg == GF_DATA){ - * register unsigned char c; - * - * while(GF_GETC(f, c)){ // macro taking data off input queue - * // operate on c and pass it on here - * GF_PUTC(f->next, c); // macro writing output queue - * } - * - * GF_END(f, f->next); // macro to sync pointers/offsets - * //WARNING: DO NOT RETURN BEFORE ALL INCOMING DATA'S PROCESSED - * } - * else if(flg == GF_EOD){ - * // process any buffered data here and pass it on - * GF_FLUSH(f->next); // flush pending data to next filter - * (*f->next->f)(f->next, GF_EOD); - * } - * else if(flg == GF_RESET){ - * // initialize any data in the struct here - * } - * } - * - * - Any free storage allocated during initialization (typically tied - * to the "line" pointer in FILTER_S) is the filter's responsibility - * to clean up when the GF_EOD command comes through. - * - * - Filter's must pass GF_EOD they receive on to the next - * filter in the chain so it has the opportunity to flush - * any buffered data. - * - * - All filters expect NVT end-of-lines. The idea is to prepend - * or append either the gf_local_nvtnl or gf_nvtnl_local - * os-dependant filters to the data on the appropriate end of the - * pipe for the task at hand. - * - * - NOTE: As of 951004, filters no longer take their input as a single - * char argument, but rather get data to operate on via a vector - * representing the input queue in the FILTER_S structure. - * - */ - - - -/* - * BASE64 TO BINARY encoding and decoding routines below - */ - - -/* - * BINARY to BASE64 filter (encoding described in rfc1341) - */ -void -gf_binary_b64(f, flg) - FILTER_S *f; - int flg; -{ - static char *v = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register unsigned char t = f->t; - register long n = f->n; - - while(GF_GETC(f, c)){ - - switch(n++){ - case 0 : case 3 : case 6 : case 9 : case 12: case 15: case 18: - case 21: case 24: case 27: case 30: case 33: case 36: case 39: - case 42: case 45: - GF_PUTC(f->next, v[c >> 2]); - /* byte 1: high 6 bits (1) */ - t = c << 4; /* remember high 2 bits for next */ - break; - - case 1 : case 4 : case 7 : case 10: case 13: case 16: case 19: - case 22: case 25: case 28: case 31: case 34: case 37: case 40: - case 43: - GF_PUTC(f->next, v[(t|(c>>4)) & 0x3f]); - t = c << 2; - break; - - case 2 : case 5 : case 8 : case 11: case 14: case 17: case 20: - case 23: case 26: case 29: case 32: case 35: case 38: case 41: - case 44: - GF_PUTC(f->next, v[(t|(c >> 6)) & 0x3f]); - GF_PUTC(f->next, v[c & 0x3f]); - break; - } - - if(n == 45){ /* start a new line? */ - GF_PUTC(f->next, '\015'); - GF_PUTC(f->next, '\012'); - n = 0L; - } - } - - f->n = n; - f->t = t; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ /* no more data */ - switch (f->n % 3) { /* handle trailing bytes */ - case 0: /* no trailing bytes */ - break; - - case 1: - GF_PUTC(f->next, v[(f->t) & 0x3f]); - GF_PUTC(f->next, '='); /* byte 3 */ - GF_PUTC(f->next, '='); /* byte 4 */ - break; - - case 2: - GF_PUTC(f->next, v[(f->t) & 0x3f]); - GF_PUTC(f->next, '='); /* byte 4 */ - break; - } - - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset binary_b64\n")); - f->n = 0L; - } -} - - - -/* - * BASE64 to BINARY filter (encoding described in rfc1341) - */ -void -gf_b64_binary(f, flg) - FILTER_S *f; - int flg; -{ - static char v[] = {65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, - 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65, - 65,65,65,65,65,65,65,65,65,65,65,62,65,65,65,63, - 52,53,54,55,56,57,58,59,60,61,62,65,65,64,65,65, - 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, - 15,16,17,18,19,20,21,22,23,24,25,65,65,65,65,65, - 65,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, - 41,42,43,44,45,46,47,48,49,50,51,65,65,65,65,65}; - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register unsigned char t = f->t; - register int n = (int) f->n; - register int state = f->f1; - - while(GF_GETC(f, c)){ - - if(state){ - state = 0; - if (c != '=') { - gf_error("Illegal '=' in base64 text"); - /* NO RETURN */ - } - } - - /* in range, and a valid value? */ - if((c & ~0x7f) || (c = v[c]) > 63){ - if(c == 64){ - switch (n++) { /* check quantum position */ - case 2: - state++; /* expect an equal as next char */ - break; - - case 3: - n = 0L; /* restart quantum */ - break; - - default: /* impossible quantum position */ - gf_error("Internal base64 decoder error"); - /* NO RETURN */ - } - } - } - else{ - switch (n++) { /* install based on quantum position */ - case 0: /* byte 1: high 6 bits */ - t = c << 2; - break; - - case 1: /* byte 1: low 2 bits */ - GF_PUTC(f->next, (t|(c >> 4))); - t = c << 4; /* byte 2: high 4 bits */ - break; - - case 2: /* byte 2: low 4 bits */ - GF_PUTC(f->next, (t|(c >> 2))); - t = c << 6; /* byte 3: high 2 bits */ - break; - - case 3: - GF_PUTC(f->next, t | c); - n = 0L; /* reinitialize mechanism */ - break; - } - } - } - - f->f1 = state; - f->t = t; - f->n = n; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset b64_binary\n")); - f->n = 0L; /* quantum position */ - f->f1 = 0; /* state holder: equal seen? */ - } -} - - - - -/* - * QUOTED-PRINTABLE ENCODING AND DECODING filters below. - * encoding described in rfc1341 - */ - -#define GF_MAXLINE 80 /* good buffer size */ - -/* - * default action for QUOTED-PRINTABLE to 8BIT decoder - */ -#define GF_QP_DEFAULT(f, c) { \ - if((c) == ' '){ \ - state = WSPACE; \ - /* reset white space! */ \ - (f)->linep = (f)->line; \ - *((f)->linep)++ = ' '; \ - } \ - else if((c) == '='){ \ - state = EQUAL; \ - } \ - else \ - GF_PUTC((f)->next, (c)); \ - } - - -/* - * QUOTED-PRINTABLE to 8BIT filter - */ -void -gf_qp_8bit(f, flg) - FILTER_S *f; - int flg; -{ - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - - while(GF_GETC(f, c)){ - - switch(state){ - case DFL : /* default case */ - default: - GF_QP_DEFAULT(f, c); - break; - - case CCR : /* non-significant space */ - state = DFL; - if(c == '\012') - continue; /* go on to next char */ - - GF_QP_DEFAULT(f, c); - break; - - case EQUAL : - if(c == '\015'){ /* "=\015" is a soft EOL */ - state = CCR; - break; - } - - if(c == '='){ /* compatibility clause for old guys */ - GF_PUTC(f->next, '='); - state = DFL; - break; - } - - if(!isxdigit((unsigned char)c)){ /* must be hex! */ - fs_give((void **)&(f->line)); - gf_error("Non-hexadecimal character in QP encoding"); - /* NO RETURN */ - } - - if (isdigit ((unsigned char)c)) - f->t = c - '0'; - else - f->t = c - (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10); - - state = HEX; - break; - - case HEX : - state = DFL; - if(!isxdigit((unsigned char)c)){ /* must be hex! */ - fs_give((void **)&(f->line)); - gf_error("Non-hexadecimal character in QP encoding"); - /* NO RETURN */ - } - - if (isdigit((unsigned char)c)) - c -= '0'; - else - c -= (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10); - - GF_PUTC(f->next, c + (f->t << 4)); - break; - - case WSPACE : - if(c == ' '){ /* toss it in with other spaces */ - if(f->linep - f->line < GF_MAXLINE) - *(f->linep)++ = ' '; - break; - } - - state = DFL; - if(c == '\015'){ /* not our white space! */ - f->linep = f->line; /* reset buffer */ - GF_PUTC(f->next, '\015'); - break; - } - - /* the spaces are ours, write 'em */ - f->n = f->linep - f->line; - while((f->n)--) - GF_PUTC(f->next, ' '); - - GF_QP_DEFAULT(f, c); /* take care of 'c' in default way */ - break; - } - } - - f->f1 = state; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - fs_give((void **)&(f->line)); - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset qp_8bit\n")); - f->f1 = DFL; - f->linep = f->line = (char *)fs_get(GF_MAXLINE * sizeof(char)); - } -} - - - -/* - * USEFUL MACROS TO HELP WITH QP ENCODING - */ - -#define QP_MAXL 75 /* 76th place only for continuation */ - -/* - * Macro to test and wrap long quoted printable lines - */ -#define GF_8BIT_WRAP(f) { \ - GF_PUTC((f)->next, '='); \ - GF_PUTC((f)->next, '\015'); \ - GF_PUTC((f)->next, '\012'); \ - } - -/* - * write a quoted octet in QUOTED-PRINTABLE encoding, adding soft - * line break if needed. - */ -#define GF_8BIT_PUT_QUOTE(f, c) { \ - if(((f)->n += 3) > QP_MAXL){ \ - GF_8BIT_WRAP(f); \ - (f)->n = 3; /* set line count */ \ - } \ - GF_PUTC((f)->next, '='); \ - GF_PUTC((f)->next, HEX_CHAR1(c)); \ - GF_PUTC((f)->next, HEX_CHAR2(c)); \ - } - -/* - * just write an ordinary octet in QUOTED-PRINTABLE, wrapping line - * if needed. - */ -#define GF_8BIT_PUT(f, c) { \ - if((++(f->n)) > QP_MAXL){ \ - GF_8BIT_WRAP(f); \ - f->n = 1L; \ - } \ - if(f->n == 1L && c == '.'){ \ - GF_8BIT_PUT_QUOTE(f, c); \ - f->n = 3; \ - } \ - else \ - GF_PUTC(f->next, c); \ - } - - -/* - * default action for 8bit to quoted printable encoder - */ -#define GF_8BIT_DEFAULT(f, c) if((c) == ' '){ \ - state = WSPACE; \ - } \ - else if(c == '\015'){ \ - state = CCR; \ - } \ - else if(iscntrl(c & 0x7f) || (c == 0x7f) \ - || (c & 0x80) || (c == '=')){ \ - GF_8BIT_PUT_QUOTE(f, c); \ - } \ - else{ \ - GF_8BIT_PUT(f, c); \ - } - - -/* - * 8BIT to QUOTED-PRINTABLE filter - */ -void -gf_8bit_qp(f, flg) - FILTER_S *f; - int flg; -{ - short dummy_dots = 0, dummy_dmap = 1; - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - - while(GF_GETC(f, c)){ - - /* keep track of "^JFrom " */ - Find_Froms(f->t, dummy_dots, f->f2, dummy_dmap, c); - - switch(state){ - case DFL : /* handle ordinary case */ - GF_8BIT_DEFAULT(f, c); - break; - - case CCR : /* true line break? */ - state = DFL; - if(c == '\012'){ - GF_PUTC(f->next, '\015'); - GF_PUTC(f->next, '\012'); - f->n = 0L; - } - else{ /* nope, quote the CR */ - GF_8BIT_PUT_QUOTE(f, '\015'); - GF_8BIT_DEFAULT(f, c); /* and don't forget about c! */ - } - break; - - case WSPACE: - state = DFL; - if(c == '\015' || f->t){ /* handle the space */ - GF_8BIT_PUT_QUOTE(f, ' '); - f->t = 0; /* reset From flag */ - } - else - GF_8BIT_PUT(f, ' '); - - GF_8BIT_DEFAULT(f, c); /* handle 'c' in the default way */ - break; - } - } - - f->f1 = state; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - switch(f->f1){ - case CCR : - GF_8BIT_PUT_QUOTE(f, '\015'); /* write the last cr */ - break; - - case WSPACE : - GF_8BIT_PUT_QUOTE(f, ' '); /* write the last space */ - break; - } - - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset 8bit_qp\n")); - f->f1 = DFL; /* state from last character */ - f->f2 = 1; /* state of "^NFrom " bitmap */ - f->t = 0; - f->n = 0L; /* number of chars in current line */ - } -} - - - -/* - * RICHTEXT-TO-PLAINTEXT filter - */ - -/* - * option to be used by rich2plain (NOTE: if this filter is ever - * used more than once in a pipe, all instances will have the same - * option value) - */ - - -/*---------------------------------------------------------------------- - richtext to plaintext filter - - Args: f -- - flg -- - - This basically removes all richtext formatting. A cute hack is used - to get bold and underlining to work. - Further work could be done to handle things like centering and right - and left flush, but then it could no longer be done in place. This - operates on text *with* CRLF's. - - WARNING: does not wrap lines! - ----*/ -void -gf_rich2plain(f, flg) - FILTER_S *f; - int flg; -{ -/* BUG: qoute incoming \255 values */ - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - - while(GF_GETC(f, c)){ - - switch(state){ - case TOKEN : /* collect a richtext token */ - if(c == '>'){ /* what should we do with it? */ - state = DFL; /* return to default next time */ - *(f->linep) = '\0'; /* cap off token */ - if(f->line[0] == 'l' && f->line[1] == 't'){ - GF_PUTC(f->next, '<'); /* literal '<' */ - } - else if(f->line[0] == 'n' && f->line[1] == 'l'){ - GF_PUTC(f->next, '\015');/* newline! */ - GF_PUTC(f->next, '\012'); - } - else if(!strcmp("comment", f->line)){ - (f->f2)++; - } - else if(!strcmp("/comment", f->line)){ - f->f2 = 0; - } - else if(!strcmp("/paragraph", f->line)) { - GF_PUTC(f->next, '\r'); - GF_PUTC(f->next, '\n'); - GF_PUTC(f->next, '\r'); - GF_PUTC(f->next, '\n'); - } - else if(!f->opt /* gf_rich_plain */){ - if(!strcmp(f->line, "bold")) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, TAG_BOLDON); - } else if(!strcmp(f->line, "/bold")) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, TAG_BOLDOFF); - } else if(!strcmp(f->line, "italic")) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, TAG_ULINEON); - } else if(!strcmp(f->line, "/italic")) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, TAG_ULINEOFF); - } else if(!strcmp(f->line, "underline")) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, TAG_ULINEON); - } else if(!strcmp(f->line, "/underline")) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, TAG_ULINEOFF); - } - } - /* else we just ignore the token! */ - - f->linep = f->line; /* reset token buffer */ - } - else{ /* add char to token */ - if(f->linep - f->line > 40){ - /* What? rfc1341 says 40 char tokens MAX! */ - fs_give((void **)&(f->line)); - gf_error("Richtext token over 40 characters"); - /* NO RETURN */ - } - - *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c; - } - break; - - case CCR : - state = DFL; /* back to default next time */ - if(c == '\012'){ /* treat as single space? */ - GF_PUTC(f->next, ' '); - break; - } - /* fall thru to process c */ - - case DFL : - default: - if(c == '<') - state = TOKEN; - else if(c == '\015') - state = CCR; - else if(!f->f2) /* not in comment! */ - GF_PUTC(f->next, c); - - break; - } - } - - f->f1 = state; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - if(f->f1 = (f->linep != f->line)){ - /* incomplete token!! */ - gf_error("Incomplete token in richtext"); - /* NO RETURN */ - } - - fs_give((void **)&(f->line)); - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset rich2plain\n")); - f->f1 = DFL; /* state */ - f->f2 = 0; /* set means we're in a comment */ - f->linep = f->line = (char *)fs_get(45 * sizeof(char)); - } -} - - -/* - * function called from the outside to set - * richtext filter's options - */ -void * -gf_rich2plain_opt(plain) - int plain; -{ - return((void *) plain); -} - - - -/* - * ENRICHED-TO-PLAIN text filter - */ - -#define TEF_QUELL 0x01 -#define TEF_NOFILL 0x02 - - - -/*---------------------------------------------------------------------- - enriched text to plain text filter (ala rfc1523) - - Args: f -- state and input data - flg -- - - This basically removes all enriched formatting. A cute hack is used - to get bold and underlining to work. - - Further work could be done to handle things like centering and right - and left flush, but then it could no longer be done in place. This - operates on text *with* CRLF's. - - WARNING: does not wrap lines! - ----*/ -void -gf_enriched2plain(f, flg) - FILTER_S *f; - int flg; -{ -/* BUG: qoute incoming \255 values */ - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - - while(GF_GETC(f, c)){ - - switch(state){ - case TOKEN : /* collect a richtext token */ - if(c == '>'){ /* what should we do with it? */ - int off = *f->line == '/'; - char *token = f->line + (off ? 1 : 0); - state = DFL; - *f->linep = '\0'; - if(!strcmp("param", token)){ - if(off) - f->f2 &= ~TEF_QUELL; - else - f->f2 |= TEF_QUELL; - } - else if(!strcmp("nofill", token)){ - if(off) - f->f2 &= ~TEF_NOFILL; - else - f->f2 |= TEF_NOFILL; - } - else if(!f->opt /* gf_enriched_plain */){ - /* Following is a cute hack or two to get - bold and underline on the screen. - See Putline0n() where these codes are - interpreted */ - if(!strcmp("bold", token)) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, off ? TAG_BOLDOFF : TAG_BOLDON); - } else if(!strcmp("italic", token)) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, off ? TAG_ULINEOFF : TAG_ULINEON); - } else if(!strcmp("underline", token)) { - GF_PUTC(f->next, TAG_EMBED); - GF_PUTC(f->next, off ? TAG_ULINEOFF : TAG_ULINEON); - } - } - /* else we just ignore the token! */ - - f->linep = f->line; /* reset token buffer */ - } - else if(c == '<'){ /* literal '<'? */ - if(f->linep == f->line){ - GF_PUTC(f->next, '<'); - state = DFL; - } - else{ - fs_give((void **)&(f->line)); - gf_error("Malformed Enriched text: unexpected '<'"); - /* NO RETURN */ - } - } - else{ /* add char to token */ - if(f->linep - f->line > 60){ /* rfc1523 says 60 MAX! */ - fs_give((void **)&(f->line)); - gf_error("Malformed Enriched text: token too long"); - /* NO RETURN */ - } - - *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c; - } - break; - - case CCR : - if(c != '\012'){ /* treat as single space? */ - state = DFL; /* lone cr? */ - f->f2 &= ~TEF_QUELL; - GF_PUTC(f->next, '\015'); - goto df; - } - - state = CLF; - break; - - case CLF : - if(c == '\015'){ /* treat as single space? */ - state = CCR; /* repeat crlf's mean real newlines */ - f->f2 |= TEF_QUELL; - GF_PUTC(f->next, '\r'); - GF_PUTC(f->next, '\n'); - break; - } - else{ - state = DFL; - if(!((f->f2) & TEF_QUELL)) - GF_PUTC(f->next, ' '); - - f->f2 &= ~TEF_QUELL; - } - - /* fall thru to take care of 'c' */ - - case DFL : - default : - df : - if(c == '<') - state = TOKEN; - else if(c == '\015' && (!((f->f2) & TEF_NOFILL))) - state = CCR; - else if(!((f->f2) & TEF_QUELL)) - GF_PUTC(f->next, c); - - break; - } - } - - f->f1 = state; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - if(f->f1 = (f->linep != f->line)){ - /* incomplete token!! */ - gf_error("Incomplete token in richtext"); - /* NO RETURN */ - } - - /* Make sure we end with a newline so everything gets flushed */ - GF_PUTC(f->next, '\015'); - GF_PUTC(f->next, '\012'); - - fs_give((void **)&(f->line)); - - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset enriched2plain\n")); - f->f1 = DFL; /* state */ - f->f2 = 0; /* set means we're in a comment */ - f->linep = f->line = (char *)fs_get(65 * sizeof(char)); - } -} - - -/* - * function called from the outside to set - * richtext filter's options - */ -void * -gf_enriched2plain_opt(plain) - int plain; -{ - return((void *) plain); -} - - - -/* - * HTML-TO-PLAIN text filter - */ - - -/* OK, here's the plan: - - * a universal output function handles writing chars and worries - * about wrapping. - - * a unversal element collector reads chars and collects params - * and dispatches the appropriate element handler. - - * element handlers are stacked. The most recently dispatched gets - * first crack at the incoming character stream. It passes bytes it's - * done with or not interested in to the next - - * installs that handler as the current one collecting data... - - * stacked handlers take their params from the element collector and - * accept chars or do whatever they need to do. Sort of a vertical - * piping? recursion-like? hmmm. - - * at least I think this is how it'll work. tres simple, non? - - */ - - -/* - * Some important constants - */ -#define HTML_BUF_LEN 1024 /* max scratch buffer length */ -#define MAX_ENTITY 20 /* maximum length of an entity */ -#define MAX_ELEMENT 72 /* maximum length of an element */ -#define HTML_BADVALUE 0x0100 /* good data, but bad entity value */ -#define HTML_BADDATA 0x0200 /* bad data found looking for entity */ -#define HTML_LITERAL 0x0400 /* Literal character value */ -#define HTML_NEWLINE 0x010A /* hard newline */ -#define HTML_DOBOLD 0x0400 /* Start Bold display */ -#define HTML_ID_GET 0 /* indent func: return current val */ -#define HTML_ID_SET 1 /* indent func: set to absolute val */ -#define HTML_ID_INC 2 /* indent func: increment by val */ -#define HTML_HX_CENTER 0x0001 -#define HTML_HX_ULINE 0x0002 - - -/* - * Types used to manage HTML parsing - */ -typedef int (*html_f) PROTO(()); - -/* - * Handler data, state information including function that uses it - */ -typedef struct handler_s { - FILTER_S *html_data; - struct handler_s *below; - html_f f; - long x, y, z; - unsigned char *s; -} HANDLER_S; - - -/* - * to help manage line wrapping. - */ -typedef struct _wrap_line { - char *buf; /* buf to collect wrapped text */ - int used, /* number of chars in buf */ - width, /* text's width as displayed */ - len; /* length of allocated buf */ -} WRAPLINE_S; - - -/* - * to help manage centered text - */ -typedef struct _center_s { - WRAPLINE_S line; /* buf to assembled centered text */ - WRAPLINE_S word; /* word being to append to Line */ - int anchor; - short embedded; - short space; -} CENTER_S; - - -/* - * Collector data and state information - */ -typedef struct collector_s { - char buf[HTML_BUF_LEN]; /* buffer to collect data */ - int len; /* length of that buffer */ - unsigned end_tag:1; /* collecting a closing tag */ - unsigned hit_equal:1; /* collecting right half of attrib */ - unsigned mkup_decl:1; /* markup declaration */ - unsigned start_comment:1; /* markup declaration comment */ - unsigned end_comment:1; /* legit comment format */ - unsigned hyphen:1; /* markup hyphen read */ - unsigned badform:1; /* malformed markup element */ - unsigned overrun:1; /* Overran buf above */ - char quoted; /* quoted element param value */ - char *element; /* element's collected name */ - PARAMETER *attribs; /* element's collected attributes */ - PARAMETER *cur_attrib; /* attribute now being collected */ -} CLCTR_S; - - -/* - * State information for all element handlers - */ -typedef struct html_data { - HANDLER_S *h_stack; /* handler list */ - CLCTR_S *el_data; /* element collector data */ - CENTER_S *centered; /* struct to manage centered text */ - int (*token) PROTO((FILTER_S *, int)); - char quoted; /* quoted, by either ' or ", text */ - short indent_level; /* levels of indention */ - int in_anchor; /* text now being written to anchor */ - int blanks; /* Consecutive blank line count */ - int wrapcol; /* column to wrap lines on */ - int *prefix; /* buffer containing Anchor prefix */ - int prefix_used; - COLOR_PAIR *color; - unsigned wrapstate:1; /* whether or not to wrap output */ - unsigned li_pending:1; /* <LI> next token expected */ - unsigned de_pending:1; /* <DT> or <DD> next token expected */ - unsigned bold_on:1; /* currently bolding text */ - unsigned uline_on:1; /* currently underlining text */ - unsigned center:1; /* center output text */ - unsigned bitbucket:1; /* Ignore input */ - unsigned head:1; /* In doc's HEAD */ - unsigned alt_entity:1; /* use alternative entity values */ -} HTML_DATA_S; - - -/* - * HTML filter options - */ -typedef struct _html_opts { - char *base; /* Base URL for this html file */ - int columns; /* Display columns */ - unsigned strip:1; /* Hilite TAGs allowed */ - unsigned handles:1; /* Anchors as handles requested? */ - unsigned handles_loc:1; /* Local handles requested? */ -} HTML_OPT_S; - - -/* - * Some macros to make life a little easier - */ -#define WRAP_COLS(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->columns : 80) -#define HTML_BASE(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->base : NULL) -#define STRIP(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->strip) -#define HANDLES(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->handles) -#define HANDLES_LOC(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->handles_loc) -#define MAKE_LITERAL(C) (HTML_LITERAL | ((C) & 0xff)) -#define IS_LITERAL(C) (HTML_LITERAL & (C)) -#define HD(X) ((HTML_DATA_S *)(X)->data) -#define ED(X) (HD(X)->el_data) -#define HTML_ISSPACE(C) (IS_LITERAL(C) == 0 && isspace((unsigned char) (C))) -#define NEW_CLCTR(X) { \ - ED(X) = (CLCTR_S *)fs_get(sizeof(CLCTR_S)); \ - memset(ED(X), 0, sizeof(CLCTR_S)); \ - HD(X)->token = html_element_collector; \ - } - -#define FREE_CLCTR(X) { \ - if(ED(X)->attribs){ \ - PARAMETER *p; \ - while(p = ED(X)->attribs){ \ - ED(X)->attribs = ED(X)->attribs->next; \ - if(p->attribute) \ - fs_give((void **)&p->attribute); \ - if(p->value) \ - fs_give((void **)&p->value); \ - fs_give((void **)&p); \ - } \ - } \ - if(ED(X)->element) \ - fs_give((void **) &ED(X)->element); \ - fs_give((void **) &ED(X)); \ - HD(X)->token = NULL; \ - } -#define HANDLERS(X) (HD(X)->h_stack) -#define BOLD_BIT(X) (HD(X)->bold_on) -#define ULINE_BIT(X) (HD(X)->uline_on) -#define CENTER_BIT(X) (HD(X)->center) -#define HTML_FLUSH(X) { \ - html_write(X, (X)->line, (X)->linep - (X)->line); \ - (X)->linep = (X)->line; \ - (X)->f2 = 0L; \ - } -#define HTML_BOLD(X, S) if(! STRIP(X)){ \ - if(S){ \ - html_output((X), TAG_EMBED); \ - html_output((X), TAG_BOLDON); \ - } \ - else if(!(S)){ \ - html_output((X), TAG_EMBED); \ - html_output((X), TAG_BOLDOFF); \ - } \ - } -#define HTML_ULINE(X, S) \ - if(! STRIP(X)){ \ - if(S){ \ - html_output((X), TAG_EMBED); \ - html_output((X), TAG_ULINEON); \ - } \ - else if(!(S)){ \ - html_output((X), TAG_EMBED); \ - html_output((X), TAG_ULINEOFF); \ - } \ - } -#define WRAPPED_LEN(X) ((HD(f)->centered) \ - ? (HD(f)->centered->line.width \ - + HD(f)->centered->word.width \ - + ((HD(f)->centered->line.width \ - && HD(f)->centered->word.width) \ - ? 1 : 0)) \ - : 0) -#define HTML_DUMP_LIT(F, S, L) { \ - int i, c; \ - for(i = 0; i < (L); i++){ \ - c = isspace((S)[i]) \ - ? (S)[i] \ - : MAKE_LITERAL((S)[i]); \ - HTML_TEXT(F, c); \ - } \ - } -#define HTML_PROC(F, C) { \ - if(HD(F)->token){ \ - int i; \ - if(i = (*(HD(F)->token))(F, C)){ \ - if(i < 0){ \ - HTML_DUMP_LIT(F, "<", 1); \ - if(HD(F)->el_data->element){ \ - HTML_DUMP_LIT(F, \ - HD(F)->el_data->element, \ - strlen(HD(F)->el_data->element));\ - } \ - if(HD(F)->el_data->len){ \ - HTML_DUMP_LIT(F, \ - HD(F)->el_data->buf, \ - HD(F)->el_data->len); \ - } \ - HTML_TEXT(F, C); \ - } \ - FREE_CLCTR(F); \ - } \ - } \ - else if((C) == '<'){ \ - NEW_CLCTR(F); \ - } \ - else \ - HTML_TEXT(F, C); \ - } -#define HTML_TEXT(F, C) switch((F)->f1){ \ - case WSPACE : \ - if(HTML_ISSPACE(C)) /* ignore repeated WS */ \ - break; \ - HTML_TEXT_OUT(F, ' '); \ - (F)->f1 = DFL;/* stop sending chars here */ \ - /* fall thru to process 'c' */ \ - case DFL: \ - if(HD(F)->bitbucket) \ - (F)->f1 = DFL; /* no op */ \ - else if(HTML_ISSPACE(C) && HD(F)->wrapstate) \ - (F)->f1 = WSPACE;/* coalesce white space */ \ - else HTML_TEXT_OUT(F, C); \ - break; \ - } -#define HTML_TEXT_OUT(F, C) if(HANDLERS(F)) /* let handlers see C */ \ - (*HANDLERS(F)->f)(HANDLERS(F),(C),GF_DATA); \ - else \ - html_output(F, C); -#ifdef DEBUG -#define HTML_DEBUG_EL(S, D) { \ - dprint(2, (debugfile, "-- html %s: %s\n", \ - S, (D)->element \ - ? (D)->element : "NULL")); \ - if(debug > 5){ \ - PARAMETER *p; \ - for(p = (D)->attribs; \ - p && p->attribute; \ - p = p->next) \ - dprint(6, (debugfile, \ - " PARM: %s%s%s\n", \ - p->attribute \ - ? p->attribute : "NULL",\ - p->value ? "=" : "", \ - p->value ? p->value : ""));\ - } \ - } -#else -#define HTML_DEBUG_EL(S, D) -#endif - - -/* - * Protos for Tag handlers - */ -int html_head PROTO((HANDLER_S *, int, int)); -int html_base PROTO((HANDLER_S *, int, int)); -int html_title PROTO((HANDLER_S *, int, int)); -int html_a PROTO((HANDLER_S *, int, int)); -int html_br PROTO((HANDLER_S *, int, int)); -int html_hr PROTO((HANDLER_S *, int, int)); -int html_p PROTO((HANDLER_S *, int, int)); -int html_tr PROTO((HANDLER_S *, int, int)); -int html_td PROTO((HANDLER_S *, int, int)); -int html_b PROTO((HANDLER_S *, int, int)); -int html_i PROTO((HANDLER_S *, int, int)); -int html_img PROTO((HANDLER_S *, int, int)); -int html_form PROTO((HANDLER_S *, int, int)); -int html_ul PROTO((HANDLER_S *, int, int)); -int html_ol PROTO((HANDLER_S *, int, int)); -int html_menu PROTO((HANDLER_S *, int, int)); -int html_dir PROTO((HANDLER_S *, int, int)); -int html_li PROTO((HANDLER_S *, int, int)); -int html_h1 PROTO((HANDLER_S *, int, int)); -int html_h2 PROTO((HANDLER_S *, int, int)); -int html_h3 PROTO((HANDLER_S *, int, int)); -int html_h4 PROTO((HANDLER_S *, int, int)); -int html_h5 PROTO((HANDLER_S *, int, int)); -int html_h6 PROTO((HANDLER_S *, int, int)); -int html_blockquote PROTO((HANDLER_S *, int, int)); -int html_address PROTO((HANDLER_S *, int, int)); -int html_pre PROTO((HANDLER_S *, int, int)); -int html_center PROTO((HANDLER_S *, int, int)); -int html_div PROTO((HANDLER_S *, int, int)); -int html_dl PROTO((HANDLER_S *, int, int)); -int html_dt PROTO((HANDLER_S *, int, int)); -int html_dd PROTO((HANDLER_S *, int, int)); - -/* - * Proto's for support routines - */ -void html_pop PROTO((FILTER_S *, html_f)); -void html_push PROTO((FILTER_S *, html_f)); -int html_element_collector PROTO((FILTER_S *, int)); -int html_element_flush PROTO((CLCTR_S *)); -void html_element_comment PROTO((FILTER_S *, char *)); -void html_element_output PROTO((FILTER_S *, int)); -int html_entity_collector PROTO((FILTER_S *, int, char **)); -void html_a_prefix PROTO((FILTER_S *)); -void html_a_finish PROTO((HANDLER_S *)); -void html_a_output_prefix PROTO((FILTER_S *, int)); -void html_a_relative PROTO((char *, char *, HANDLE_S *)); -int html_indent PROTO((FILTER_S *, int, int)); -void html_blank PROTO((FILTER_S *, int)); -void html_newline PROTO((FILTER_S *)); -void html_output PROTO((FILTER_S *, int)); -void html_output_flush PROTO((FILTER_S *)); -void html_output_centered PROTO((FILTER_S *, int)); -void html_centered_handle PROTO((int *, char *, int)); -void html_centered_putc PROTO((WRAPLINE_S *, int)); -void html_centered_flush PROTO((FILTER_S *)); -void html_centered_flush_line PROTO((FILTER_S *)); -void html_write_anchor PROTO((FILTER_S *, int)); -void html_write_newline PROTO((FILTER_S *)); -void html_write_indent PROTO((FILTER_S *, int)); -void html_write PROTO((FILTER_S *, char *, int)); -void html_putc PROTO((FILTER_S *, int)); - - -/* - * Named entity table -- most from HTML 2.0 (rfc1866) plus some from - * W3C doc "Additional named entities for HTML" - */ -static struct html_entities { - char *name; /* entity name */ - unsigned char value; /* entity value */ - char *plain; /* plain text representation */ -} entity_tab[] = { - {"quot", 042}, /* Double quote sign */ - {"amp", 046}, /* Ampersand */ - {"bull", 052}, /* Bullet */ - {"ndash", 055}, /* Dash */ - {"mdash", 055}, /* Dash */ - {"lt", 074}, /* Less than sign */ - {"gt", 076}, /* Greater than sign */ - {"nbsp", 0240, " "}, /* no-break space */ - {"iexcl", 0241}, /* inverted exclamation mark */ - {"cent", 0242}, /* cent sign */ - {"pound", 0243}, /* pound sterling sign */ - {"curren", 0244, "CUR"}, /* general currency sign */ - {"yen", 0245}, /* yen sign */ - {"brvbar", 0246, "|"}, /* broken (vertical) bar */ - {"sect", 0247}, /* section sign */ - {"uml", 0250, "\""}, /* umlaut (dieresis) */ - {"copy", 0251, "(C)"}, /* copyright sign */ - {"ordf", 0252, "a"}, /* ordinal indicator, feminine */ - {"laquo", 0253, "<<"}, /* angle quotation mark, left */ - {"not", 0254, "NOT"}, /* not sign */ - {"shy", 0255, "-"}, /* soft hyphen */ - {"reg", 0256, "(R)"}, /* registered sign */ - {"macr", 0257}, /* macron */ - {"deg", 0260, "DEG"}, /* degree sign */ - {"plusmn", 0261, "+/-"}, /* plus-or-minus sign */ - {"sup2", 0262}, /* superscript two */ - {"sup3", 0263}, /* superscript three */ - {"acute", 0264, "'"}, /* acute accent */ - {"micro", 0265}, /* micro sign */ - {"para", 0266}, /* pilcrow (paragraph sign) */ - {"middot", 0267}, /* middle dot */ - {"cedil", 0270}, /* cedilla */ - {"sup1", 0271}, /* superscript one */ - {"ordm", 0272, "o"}, /* ordinal indicator, masculine */ - {"raquo", 0273, ">>"}, /* angle quotation mark, right */ - {"frac14", 0274, " 1/4"}, /* fraction one-quarter */ - {"frac12", 0275, " 1/2"}, /* fraction one-half */ - {"frac34", 0276, " 3/4"}, /* fraction three-quarters */ - {"iquest", 0277}, /* inverted question mark */ - {"Agrave", 0300, "A"}, /* capital A, grave accent */ - {"Aacute", 0301, "A"}, /* capital A, acute accent */ - {"Acirc", 0302, "A"}, /* capital A, circumflex accent */ - {"Atilde", 0303, "A"}, /* capital A, tilde */ - {"Auml", 0304, "AE"}, /* capital A, dieresis or umlaut mark */ - {"Aring", 0305, "A"}, /* capital A, ring */ - {"AElig", 0306, "AE"}, /* capital AE diphthong (ligature) */ - {"Ccedil", 0307, "C"}, /* capital C, cedilla */ - {"Egrave", 0310, "E"}, /* capital E, grave accent */ - {"Eacute", 0311, "E"}, /* capital E, acute accent */ - {"Ecirc", 0312, "E"}, /* capital E, circumflex accent */ - {"Euml", 0313, "E"}, /* capital E, dieresis or umlaut mark */ - {"Igrave", 0314, "I"}, /* capital I, grave accent */ - {"Iacute", 0315, "I"}, /* capital I, acute accent */ - {"Icirc", 0316, "I"}, /* capital I, circumflex accent */ - {"Iuml", 0317, "I"}, /* capital I, dieresis or umlaut mark */ - {"ETH", 0320, "DH"}, /* capital Eth, Icelandic */ - {"Ntilde", 0321, "N"}, /* capital N, tilde */ - {"Ograve", 0322, "O"}, /* capital O, grave accent */ - {"Oacute", 0323, "O"}, /* capital O, acute accent */ - {"Ocirc", 0324, "O"}, /* capital O, circumflex accent */ - {"Otilde", 0325, "O"}, /* capital O, tilde */ - {"Ouml", 0326, "OE"}, /* capital O, dieresis or umlaut mark */ - {"times", 0327, "x"}, /* multiply sign */ - {"Oslash", 0330, "O"}, /* capital O, slash */ - {"Ugrave", 0331, "U"}, /* capital U, grave accent */ - {"Uacute", 0332, "U"}, /* capital U, acute accent */ - {"Ucirc", 0333, "U"}, /* capital U, circumflex accent */ - {"Uuml", 0334, "UE"}, /* capital U, dieresis or umlaut mark */ - {"Yacute", 0335, "Y"}, /* capital Y, acute accent */ - {"THORN", 0336, "P"}, /* capital THORN, Icelandic */ - {"szlig", 0337, "ss"}, /* small sharp s, German (sz ligature) */ - {"agrave", 0340, "a"}, /* small a, grave accent */ - {"aacute", 0341, "a"}, /* small a, acute accent */ - {"acirc", 0342, "a"}, /* small a, circumflex accent */ - {"atilde", 0343, "a"}, /* small a, tilde */ - {"auml", 0344, "ae"}, /* small a, dieresis or umlaut mark */ - {"aring", 0345, "a"}, /* small a, ring */ - {"aelig", 0346, "ae"}, /* small ae diphthong (ligature) */ - {"ccedil", 0347, "c"}, /* small c, cedilla */ - {"egrave", 0350, "e"}, /* small e, grave accent */ - {"eacute", 0351, "e"}, /* small e, acute accent */ - {"ecirc", 0352, "e"}, /* small e, circumflex accent */ - {"euml", 0353, "e"}, /* small e, dieresis or umlaut mark */ - {"igrave", 0354, "i"}, /* small i, grave accent */ - {"iacute", 0355, "i"}, /* small i, acute accent */ - {"icirc", 0356, "i"}, /* small i, circumflex accent */ - {"iuml", 0357, "i"}, /* small i, dieresis or umlaut mark */ - {"eth", 0360, "dh"}, /* small eth, Icelandic */ - {"ntilde", 0361, "n"}, /* small n, tilde */ - {"ograve", 0362, "o"}, /* small o, grave accent */ - {"oacute", 0363, "o"}, /* small o, acute accent */ - {"ocirc", 0364, "o"}, /* small o, circumflex accent */ - {"otilde", 0365, "o"}, /* small o, tilde */ - {"ouml", 0366, "oe"}, /* small o, dieresis or umlaut mark */ - {"divide", 0367, "/"}, /* divide sign */ - {"oslash", 0370, "o"}, /* small o, slash */ - {"ugrave", 0371, "u"}, /* small u, grave accent */ - {"uacute", 0372, "u"}, /* small u, acute accent */ - {"ucirc", 0373, "u"}, /* small u, circumflex accent */ - {"uuml", 0374, "ue"}, /* small u, dieresis or umlaut mark */ - {"yacute", 0375, "y"}, /* small y, acute accent */ - {"thorn", 0376, "p"}, /* small thorn, Icelandic */ - {"yuml", 0377, "y"}, /* small y, dieresis or umlaut mark */ - {NULL, 0} -}; - - -/* - * Table of supported elements and corresponding handlers - */ -static struct element_table { - char *element; - int (*handler) PROTO(()); -} element_table[] = { - {"HTML", NULL}, /* HTML ignore if seen? */ - {"HEAD", html_head}, /* slurp until <BODY> ? */ - {"TITLE", html_title}, /* Document Title */ - {"BASE", html_base}, /* HREF base */ - {"BODY", NULL}, /* (NO OP) */ - {"A", html_a}, /* Anchor */ - {"IMG", html_img}, /* Image */ - {"HR", html_hr}, /* Horizontal Rule */ - {"BR", html_br}, /* Line Break */ - {"P", html_p}, /* Paragraph */ - {"OL", html_ol}, /* Ordered List */ - {"UL", html_ul}, /* Unordered List */ - {"MENU", html_menu}, /* Menu List */ - {"DIR", html_dir}, /* Directory List */ - {"LI", html_li}, /* ... List Item */ - {"DL", html_dl}, /* Definition List */ - {"DT", html_dt}, /* ... Def. Term */ - {"DD", html_dd}, /* ... Def. Definition */ - {"I", html_i}, /* Italic Text */ - {"EM", html_i}, /* Typographic Emphasis */ - {"STRONG", html_i}, /* STRONG Typo Emphasis */ - {"VAR", html_i}, /* Variable Name */ - {"B", html_b}, /* Bold Text */ - {"BLOCKQUOTE", html_blockquote}, /* Blockquote */ - {"ADDRESS", html_address}, /* Address */ - {"CENTER", html_center}, /* Centered Text v3.2 */ - {"DIV", html_div}, /* Document Division 3.2 */ - {"H1", html_h1}, /* Headings... */ - {"H2", html_h2}, - {"H3", html_h3}, - {"H4", html_h4}, - {"H5", html_h5}, - {"H6", html_h6}, - {"PRE", html_pre}, /* Preformatted Text */ - {"KBD", NULL}, /* Keyboard Input (NO OP) */ - {"TT", NULL}, /* Typetype (NO OP) */ - {"SAMP", NULL}, /* Sample Text (NO OP) */ - -/*----- Handlers below are NOT DONE OR CHECKED OUT YET -----*/ - - {"CITE", NULL}, /* Citation */ - {"CODE", NULL}, /* Code Text */ - -/*----- Handlers below UNIMPLEMENTED (and won't until later) -----*/ - - {"FORM", html_form}, /* form within a document */ - {"INPUT", NULL}, /* One input field, options */ - {"OPTION", NULL}, /* One option within Select */ - {"SELECT", NULL}, /* Selection from a set */ - {"TEXTAREA", NULL}, /* A multi-line input field */ - -/*----- Handlers below provide limited support for RFC 1942 Tables -----*/ - - {"CAPTION", html_center}, /* Table Caption */ - {"TR", html_tr}, /* Table Table Row */ - {"TD", html_td}, /* Table Table Data */ - - {NULL, NULL} -}; - - - -/* - * Initialize the given handler, and add it to the stack if it - * requests it. - */ -void -html_push(fd, hf) - FILTER_S *fd; - html_f hf; -{ - HANDLER_S *new; - - new = (HANDLER_S *)fs_get(sizeof(HANDLER_S)); - memset(new, 0, sizeof(HANDLER_S)); - new->html_data = fd; - new->f = hf; - if((*hf)(new, 0, GF_RESET)){ /* stack the handler? */ - new->below = HANDLERS(fd); - HANDLERS(fd) = new; /* push */ - } - else - fs_give((void **) &new); -} - - -/* - * Remove the most recently installed the given handler - * after letting it accept its demise. - */ -void -html_pop(fd, hf) - FILTER_S *fd; - html_f hf; -{ - HANDLER_S *tp; - - for(tp = HANDLERS(fd); tp && hf != tp->f; tp = tp->below) - ; - - if(tp){ - (*tp->f)(tp, 0, GF_EOD); /* may adjust handler list */ - if(tp != HANDLERS(fd)){ - HANDLER_S *p; - - for(p = HANDLERS(fd); p->below != tp; p = p->below) - ; - - if(p) - p->below = tp->below; /* remove from middle of stack */ - /* BUG: else programming botch and we should die */ - } - else - HANDLERS(fd) = tp->below; /* pop */ - - fs_give((void **)&tp); - } - else if(hf == html_p || hf == html_li || hf == html_dt || hf == html_dd){ - /* - * Possible "special case" tag handling here. - * It's for such tags as Paragraph (`</P>'), List Item - * (`</LI>'), Definition Term (`</DT>'), and Definition Description - * (`</DD>') elements, which may be omitted... - */ - HANDLER_S hd; - - memset(&hd, 0, sizeof(HANDLER_S)); - hd.html_data = fd; - hd.f = hf; - - (*hf)(&hd, 0, GF_EOD); - } - /* BUG: else, we should bitch */ -} - - -/* - * Deal with data passed a hander in its GF_DATA state - */ -html_handoff(hd, ch) - HANDLER_S *hd; - int ch; -{ - if(hd->below) - (*hd->below->f)(hd->below, ch, GF_DATA); - else - html_output(hd->html_data, ch); -} - - -/* - * HTML <BR> element handler - */ -int -html_br(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET) - html_output(hd->html_data, HTML_NEWLINE); - - return(0); /* don't get linked */ -} - - -/* - * HTML <HR> (Horizontal Rule) element handler - */ -int -html_hr(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - int i, old_wrap, width, align; - PARAMETER *p; - - width = WRAP_COLS(hd->html_data); - align = 0; - for(p = HD(hd->html_data)->el_data->attribs; - p && p->attribute; - p = p->next) - if(p->value){ - if(!strucmp(p->attribute, "ALIGN")){ - if(!strucmp(p->value, "LEFT")) - align = 1; - else if(!strucmp(p->value, "RIGHT")) - align = 2; - } - else if(!strucmp(p->attribute, "WIDTH")){ - char *cp; - - width = 0; - for(cp = p->value; *cp; cp++) - if(*cp == '%'){ - width = (WRAP_COLS(hd->html_data)*min(100,width))/100; - break; - } - else if(isdigit((unsigned char) *cp)) - width = (width * 10) + (*cp - '0'); - - width = min(width, WRAP_COLS(hd->html_data)); - } - } - - html_blank(hd->html_data, 1); /* at least one blank line */ - - old_wrap = HD(hd->html_data)->wrapstate; - HD(hd->html_data)->wrapstate = 0; - if((i = max(0, WRAP_COLS(hd->html_data) - width)) - && ((align == 0) ? i /= 2 : (align == 2))) - for(; i > 0; i--) - html_output(hd->html_data, ' '); - - for(i = 0; i < width; i++) - html_output(hd->html_data, '_'); - - html_blank(hd->html_data, 1); - HD(hd->html_data)->wrapstate = old_wrap; - } - - return(0); /* don't get linked */ -} - - -/* - * HTML <P> (paragraph) element handler - */ -int -html_p(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - /* Make sure there's at least 1 blank line */ - html_blank(hd->html_data, 1); - - /* adjust indent level if needed */ - if(HD(hd->html_data)->li_pending){ - html_indent(hd->html_data, 4, HTML_ID_INC); - HD(hd->html_data)->li_pending = 0; - } - } - else if(cmd == GF_EOD) - /* Make sure there's at least 1 blank line */ - html_blank(hd->html_data, 1); - - return(0); /* don't get linked */ -} - - -/* - * HTML Table <TR> (paragraph) table row - */ -int -html_tr(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET || cmd == GF_EOD) - /* Make sure there's at least 1 blank line */ - html_blank(hd->html_data, 0); - - return(0); /* don't get linked */ -} - - -/* - * HTML Table <TD> (paragraph) table data - */ -int -html_td(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - PARAMETER *p; - - for(p = HD(hd->html_data)->el_data->attribs; - p && p->attribute; - p = p->next) - if(!strucmp(p->attribute, "nowrap") - && (hd->html_data->f2 || hd->html_data->n)){ - HTML_DUMP_LIT(hd->html_data, " | ", 3); - break; - } - } - - return(0); /* don't get linked */ -} - - -/* - * HTML <I> (italic text) element handler - */ -int -html_i(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - /* include LITERAL in spaceness test! */ - if(hd->x && !isspace((unsigned char) (ch & 0xff))){ - HTML_ULINE(hd->html_data, 1); - hd->x = 0; - } - - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - hd->x = 1; - } - else if(cmd == GF_EOD){ - if(!hd->x) - HTML_ULINE(hd->html_data, 0); - } - - return(1); /* get linked */ -} - - -/* - * HTML <b> (Bold text) element handler - */ -int -html_b(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - /* include LITERAL in spaceness test! */ - if(hd->x && !isspace((unsigned char) (ch & 0xff))){ - HTML_ULINE(hd->html_data, 1); - hd->x = 0; - } - - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - hd->x = 1; - } - else if(cmd == GF_EOD){ - if(!hd->x) - HTML_ULINE(hd->html_data, 0); - } - - return(1); /* get linked */ -} - - -/* - * HTML <IMG> element handler - */ -int -html_img(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - PARAMETER *p; - char *s = NULL; - - for(p = HD(hd->html_data)->el_data->attribs; - p && p->attribute; - p = p->next) - if(!strucmp(p->attribute, "alt")){ - if(p->value && p->value[0]){ - HTML_DUMP_LIT(hd->html_data, p->value, strlen(p->value)); - HTML_TEXT(hd->html_data, ' '); - } - - return(0); - } - - for(p = HD(hd->html_data)->el_data->attribs; - p && p->attribute; - p = p->next) - if(!strucmp(p->attribute, "src") && p->value) - if((s = strrindex(p->value, '/')) && *++s != '\0'){ - HTML_TEXT(hd->html_data, '['); - HTML_DUMP_LIT(hd->html_data, s, strlen(s)); - HTML_TEXT(hd->html_data, ']'); - HTML_TEXT(hd->html_data, ' '); - return(0); - } - - HTML_DUMP_LIT(hd->html_data, "[IMAGE] ", 7); - } - - return(0); /* don't get linked */ -} - - -/* - * HTML <FORM> (Form) element handler - */ -int -html_form(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - char *p; - - html_blank(hd->html_data, 0); - - HTML_DUMP_LIT(hd->html_data, "[FORM]", 6); - - html_blank(hd->html_data, 0); - } - - return(0); /* don't get linked */ -} - - -/* - * HTML <HEAD> element handler - */ -int -html_head(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - HD(hd->html_data)->head = 1; - } - else if(cmd == GF_EOD){ - HD(hd->html_data)->head = 0; - } - - return(1); /* get linked */ -} - - -/* - * HTML <BASE> element handler - */ -int -html_base(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - if(HD(hd->html_data)->head && !HTML_BASE(hd->html_data)){ - PARAMETER *p; - - for(p = HD(hd->html_data)->el_data->attribs; - p && p->attribute && strucmp(p->attribute, "HREF"); - p = p->next) - ; - - if(p && p->value && !((HTML_OPT_S *)(hd->html_data)->opt)->base) - ((HTML_OPT_S *)(hd->html_data)->opt)->base = cpystr(p->value); - } - } - - return(0); /* DON'T get linked */ -} - - -/* - * HTML <TITLE> element handler - */ -int -html_title(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - if(hd->x + 1 >= hd->y){ - hd->y += 80; - fs_resize((void **)&hd->s, (size_t)hd->y * sizeof(unsigned char)); - } - - hd->s[hd->x++] = (unsigned char) ch; - } - else if(cmd == GF_RESET){ - hd->x = 0L; - hd->y = 80L; - hd->s = (unsigned char *)fs_get((size_t)hd->y * sizeof(unsigned char)); - } - else if(cmd == GF_EOD){ - /* Down the road we probably want to give these bytes to - * someone... - */ - hd->s[hd->x] = '\0'; - fs_give((void **)&hd->s); - } - - return(1); /* get linked */ -} - - -/* - * HTML <A> (Anchor) element handler - */ -int -html_a(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - int i, n, x; - char buf[256]; - HANDLE_S *h; - PARAMETER *p, *href = NULL, *name = NULL; - - /* - * Pending Anchor!?!? - * space insertion/line breaking that's yet to get done... - */ - if(HD(hd->html_data)->prefix){ - dprint(1, (debugfile, "-- html_a: NESTED/UNTERMINATED ANCHOR!\n")); - html_a_finish(hd); - } - - /* - * Look for valid Anchor data vis the filter installer's parms - * (e.g., Only allow references to our internal URLs if asked) - */ - for(p = HD(hd->html_data)->el_data->attribs; - p && p->attribute; - p = p->next) - if(!strucmp(p->attribute, "HREF") - && p->value - && (HANDLES_LOC(hd->html_data) - || struncmp(p->value, "x-pine-", 7))) - href = p; - else if(!strucmp(p->attribute, "NAME")) - name = p; - - if(HANDLES(hd->html_data) && (href || name)){ - h = new_handle(); - - /* - * Enhancement: we might want to get fancier and parse the - * href a bit further such that we can launch images using - * our image viewer, or browse local files or directories - * with our internal tools. Of course, having the jump-off - * point into text/html always be the defined "web-browser", - * just might be the least confusing UI-wise... - */ - h->type = URL; - - if(name && name->value) - h->h.url.name = cpystr(name->value); - - /* - * Prepare to build embedded prefix... - */ - HD(hd->html_data)->prefix = (int *) fs_get(64 * sizeof(int)); - x = 0; - - /* - * Is this something that looks like a URL? If not and - * we were giving some "base" string, proceed ala RFC1808... - */ - if(href){ - if(HTML_BASE(hd->html_data) && !rfc1738_scan(href->value, &n)) - html_a_relative(HTML_BASE(hd->html_data), href->value, h); - else - h->h.url.path = cpystr(href->value); - - if(pico_usingcolor()){ - char *fg = NULL, *bg = NULL, *q; - - if(ps_global->VAR_SLCTBL_FORE_COLOR - && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, - ps_global->VAR_NORM_FORE_COLOR)) - fg = ps_global->VAR_SLCTBL_FORE_COLOR; - - if(ps_global->VAR_SLCTBL_BACK_COLOR - && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, - ps_global->VAR_NORM_BACK_COLOR)) - bg = ps_global->VAR_SLCTBL_BACK_COLOR; - - if(fg || bg){ - COLOR_PAIR *tmp; - - /* - * The blacks are just known good colors for testing - * whether the other color is good. - */ - tmp = new_color_pair(fg ? fg : colorx(COL_BLACK), - bg ? bg : colorx(COL_BLACK)); - if(pico_is_good_colorpair(tmp)){ - q = color_embed(fg, bg); - - for(i = 0; q[i]; i++) - HD(hd->html_data)->prefix[x++] = q[i]; - } - - if(tmp) - free_color_pair(&tmp); - } - - if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)) - HD(hd->html_data)->prefix[x++] = HTML_DOBOLD; - } - else - HD(hd->html_data)->prefix[x++] = HTML_DOBOLD; - } - - HD(hd->html_data)->prefix[x++] = TAG_EMBED; - HD(hd->html_data)->prefix[x++] = TAG_HANDLE; - - sprintf(buf, "%d", h->key); - HD(hd->html_data)->prefix[x++] = n = strlen(buf); - for(i = 0; i < n; i++) - HD(hd->html_data)->prefix[x++] = buf[i]; - - HD(hd->html_data)->prefix_used = x; - } - } - else if(cmd == GF_EOD){ - html_a_finish(hd); - } - - return(1); /* get linked */ -} - - -void -html_a_prefix(f) - FILTER_S *f; -{ - int *prefix, n; - - /* Do this so we don't visit from html_output... */ - prefix = HD(f)->prefix; - HD(f)->prefix = NULL; - - for(n = 0; n < HD(f)->prefix_used; n++) - html_a_output_prefix(f, prefix[n]); - - fs_give((void **) &prefix); -} - - -/* - * html_a_finish - house keeping associated with end of link tag - */ -void -html_a_finish(hd) - HANDLER_S *hd; -{ - if(HANDLES(hd->html_data)){ - if(HD(hd->html_data)->prefix) - html_a_prefix(hd->html_data); - - if(pico_usingcolor()){ - char *fg = NULL, *bg = NULL, *p; - int i; - - if(ps_global->VAR_SLCTBL_FORE_COLOR - && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, - ps_global->VAR_NORM_FORE_COLOR)) - fg = ps_global->VAR_NORM_FORE_COLOR; - - if(ps_global->VAR_SLCTBL_BACK_COLOR - && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, - ps_global->VAR_NORM_BACK_COLOR)) - bg = ps_global->VAR_NORM_BACK_COLOR; - - if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)) - HTML_BOLD(hd->html_data, 0); /* turn OFF bold */ - - if(fg || bg){ - COLOR_PAIR *tmp; - - /* - * The blacks are just known good colors for testing - * whether the other color is good. - */ - tmp = new_color_pair(fg ? fg : colorx(COL_BLACK), - bg ? bg : colorx(COL_BLACK)); - if(pico_is_good_colorpair(tmp)){ - p = color_embed(fg, bg); - - for(i = 0; p[i]; i++) - html_output(hd->html_data, p[i]); - } - - if(tmp) - free_color_pair(&tmp); - } - } - else - HTML_BOLD(hd->html_data, 0); /* turn OFF bold */ - - html_output(hd->html_data, TAG_EMBED); - html_output(hd->html_data, TAG_HANDLEOFF); - } -} - - -/* - * html_output_a_prefix - dump Anchor prefix data - */ -void -html_a_output_prefix(f, c) - FILTER_S *f; - int c; -{ - switch(c){ - case HTML_DOBOLD : - HTML_BOLD(f, 1); - break; - - default : - html_output(f, c); - break; - } -} - - - -/* - * relative_url - put full url path in h based on base and relative url - */ -void -html_a_relative(base_url, rel_url, h) - char *base_url, *rel_url; - HANDLE_S *h; -{ - size_t len; - char tmp[MAILTMPLEN], *p, *q; - char *scheme = NULL, *net = NULL, *path = NULL, - *parms = NULL, *query = NULL, *frag = NULL, - *base_scheme = NULL, *base_net_loc = NULL, - *base_path = NULL, *base_parms = NULL, - *base_query = NULL, *base_frag = NULL, - *rel_scheme = NULL, *rel_net_loc = NULL, - *rel_path = NULL, *rel_parms = NULL, - *rel_query = NULL, *rel_frag = NULL; - - /* Rough parse of base URL */ - rfc1808_tokens(base_url, &base_scheme, &base_net_loc, &base_path, - &base_parms, &base_query, &base_frag); - - /* Rough parse of this URL */ - rfc1808_tokens(rel_url, &rel_scheme, &rel_net_loc, &rel_path, - &rel_parms, &rel_query, &rel_frag); - - scheme = rel_scheme; /* defaults */ - net = rel_net_loc; - path = rel_path; - parms = rel_parms; - query = rel_query; - frag = rel_frag; - if(!scheme && base_scheme){ - scheme = base_scheme; - if(!net){ - net = base_net_loc; - if(path){ - if(*path != '/'){ - if(base_path){ - for(p = q = base_path; /* Drop base path's tail */ - p = strchr(p, '/'); - q = ++p) - ; - - len = q - base_path; - } - else - len = 0; - - if(len + strlen(rel_path) < MAILTMPLEN - 1){ - if(len) - sprintf(path = tmp, "%.*s", len, base_path); - - strcpy(tmp + len, rel_path); - - /* Follow RFC 1808 "Step 6" */ - for(p = tmp; p = strchr(p, '.'); ) - switch(*(p+1)){ - /* - * a) All occurrences of "./", where "." is a - * complete path segment, are removed. - */ - case '/' : - if(p > tmp) - for(q = p; *q = *(q+2); q++) - ; - else - p++; - - break; - - /* - * b) If the path ends with "." as a - * complete path segment, that "." is - * removed. - */ - case '\0' : - if(p == tmp || *(p-1) == '/') - *p = '\0'; - else - p++; - - break; - - /* - * c) All occurrences of "<segment>/../", - * where <segment> is a complete path - * segment not equal to "..", are removed. - * Removal of these path segments is - * performed iteratively, removing the - * leftmost matching pattern on each - * iteration, until no matching pattern - * remains. - * - * d) If the path ends with "<segment>/..", - * where <segment> is a complete path - * segment not equal to "..", that - * "<segment>/.." is removed. - */ - case '.' : - if(p > tmp + 1){ - for(q = p - 2; q > tmp && *q != '/'; q--) - ; - - if(*q == '/') - q++; - - if(q + 1 == p /* no "//.." */ - || (*q == '.' /* and "../.." */ - && *(q+1) == '.' - && *(q+2) == '/')){ - p += 2; - break; - } - - switch(*(p+2)){ - case '/' : - len = (p - q) + 3; - p = q; - for(; *q = *(q+len); q++) - ; - - break; - - case '\0': - *(p = q) = '\0'; - break; - - default: - p += 2; - break; - } - } - else - p += 2; - - break; - - default : - p++; - break; - } - } - else - path = ""; /* lame. */ - } - } - else{ - path = base_path; - if(!parms){ - parms = base_parms; - if(!query) - query = base_query; - } - } - } - } - - len = (scheme ? strlen(scheme) : 0) + (net ? strlen(net) : 0) - + (path ? strlen(path) : 0) + (parms ? strlen(parms) : 0) - + (query ? strlen(query) : 0) + (frag ? strlen(frag ) : 0) + 8; - - h->h.url.path = (char *) fs_get(len * sizeof(char)); - sprintf(h->h.url.path, "%s%s%s%s%s%s%s%s%s%s%s%s", - scheme ? scheme : "", scheme ? ":" : "", - net ? "//" : "", net ? net : "", - (path && *path == '/') ? "" : ((path && net) ? "/" : ""), - path ? path : "", - parms ? ";" : "", parms ? parms : "", - query ? "?" : "", query ? query : "", - frag ? "#" : "", frag ? frag : ""); - - if(base_scheme) - fs_give((void **) &base_scheme); - - if(base_net_loc) - fs_give((void **) &base_net_loc); - - if(base_path) - fs_give((void **) &base_path); - - if(base_parms) - fs_give((void **) &base_parms); - - if(base_query) - fs_give((void **) &base_query); - - if(base_frag) - fs_give((void **) &base_frag); - - if(rel_scheme) - fs_give((void **) &rel_scheme); - - if(rel_net_loc) - fs_give((void **) &rel_net_loc); - - if(rel_parms) - fs_give((void **) &rel_parms); - - if(rel_query) - fs_give((void **) &rel_query); - - if(rel_frag) - fs_give((void **) &rel_frag); - - if(rel_path) - fs_give((void **) &rel_path); -} - - -/* - * HTML <UL> (Unordered List) element handler - */ -int -html_ul(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - HD(hd->html_data)->li_pending = 1; - html_blank(hd->html_data, 0); - } - else if(cmd == GF_EOD){ - html_blank(hd->html_data, 0); - - if(!HD(hd->html_data)->li_pending) - html_indent(hd->html_data, -4, HTML_ID_INC); - else - HD(hd->html_data)->li_pending = 0; - } - - return(1); /* get linked */ -} - - -/* - * HTML <OL> (Ordered List) element handler - */ -int -html_ol(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* - * Signal that we're expecting to see <LI> as our next elemnt - * and set the the initial ordered count. - */ - HD(hd->html_data)->li_pending = 1; - hd->x = 1L; - html_blank(hd->html_data, 0); - } - else if(cmd == GF_EOD){ - html_blank(hd->html_data, 0); - - if(!HD(hd->html_data)->li_pending) - html_indent(hd->html_data, -4, HTML_ID_INC); - else - HD(hd->html_data)->li_pending = 0; - } - - return(1); /* get linked */ -} - - -/* - * HTML <MENU> (Menu List) element handler - */ -int -html_menu(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - HD(hd->html_data)->li_pending = 1; - } - else if(cmd == GF_EOD){ - html_blank(hd->html_data, 0); - - if(!HD(hd->html_data)->li_pending) - html_indent(hd->html_data, -4, HTML_ID_INC); - else - HD(hd->html_data)->li_pending = 0; - } - - return(1); /* get linked */ -} - - -/* - * HTML <DIR> (Directory List) element handler - */ -int -html_dir(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - HD(hd->html_data)->li_pending = 1; - } - else if(cmd == GF_EOD){ - html_blank(hd->html_data, 0); - - if(!HD(hd->html_data)->li_pending) - html_indent(hd->html_data, -4, HTML_ID_INC); - else - HD(hd->html_data)->li_pending = 0; - } - - return(1); /* get linked */ -} - - -/* - * HTML <LI> (List Item) element handler - */ -int -html_li(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - HANDLER_S *p, *found = NULL; - - /* - * There better be a an unordered list, ordered list, - * Menu or Directory handler installed - * or else we crap out... - */ - for(p = HANDLERS(hd->html_data); p; p = p->below) - if(p->f == html_ul || p->f == html_ol - || p->f == html_menu || p->f == html_dir){ - found = p; - break; - } - - if(found){ - char buf[8], *p; - int wrapstate; - - /* Start a new line */ - html_blank(hd->html_data, 0); - - /* adjust indent level if needed */ - if(HD(hd->html_data)->li_pending){ - html_indent(hd->html_data, 4, HTML_ID_INC); - HD(hd->html_data)->li_pending = 0; - } - - if(found->f == html_ul){ - int l = html_indent(hd->html_data, 0, HTML_ID_GET); - - strcpy(buf, " "); - buf[1] = (l < 5) ? '*' : (l < 9) ? '+' : (l < 17) ? 'o' : '#'; - } - else if(found->f == html_ol) - sprintf(buf, "%2ld.", found->x++); - else if(found->f == html_menu) - strcpy(buf, " ->"); - - html_indent(hd->html_data, -4, HTML_ID_INC); - - /* So we don't munge whitespace */ - wrapstate = HD(hd->html_data)->wrapstate; - HD(hd->html_data)->wrapstate = 0; - - html_write_indent(hd->html_data, HD(hd->html_data)->indent_level); - for(p = buf; *p; p++) - html_output(hd->html_data, (int) *p); - - HD(hd->html_data)->wrapstate = wrapstate; - html_indent(hd->html_data, 4, HTML_ID_INC); - } - /* BUG: should really bitch about this */ - } - - return(0); /* DON'T get linked */ -} - - - -/* - * HTML <DL> (Definition List) element handler - */ -int -html_dl(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* - * Set indention level for definition terms and definitions... - */ - hd->x = html_indent(hd->html_data, 0, HTML_ID_GET); - hd->y = hd->x + 2; - hd->z = hd->y + 4; - } - else if(cmd == GF_EOD){ - html_indent(hd->html_data, (int) hd->x, HTML_ID_SET); - html_blank(hd->html_data, 1); - } - - return(1); /* get linked */ -} - - -/* - * HTML <DT> (Definition Term) element handler - */ -int -html_dt(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - HANDLER_S *p; - - /* - * There better be a Definition Handler installed - * or else we crap out... - */ - for(p = HANDLERS(hd->html_data); p && p->f != html_dl; p = p->below) - ; - - if(p){ /* adjust indent level if needed */ - html_indent(hd->html_data, (int) p->y, HTML_ID_SET); - html_blank(hd->html_data, 1); - } - /* BUG: else should really bitch about this */ - } - - return(0); /* DON'T get linked */ -} - - -/* - * HTML <DD> (Definition Definition) element handler - */ -int -html_dd(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_RESET){ - HANDLER_S *p; - - /* - * There better be a Definition Handler installed - * or else we crap out... - */ - for(p = HANDLERS(hd->html_data); p && p->f != html_dl; p = p->below) - ; - - if(p){ /* adjust indent level if needed */ - html_indent(hd->html_data, (int) p->z, HTML_ID_SET); - html_blank(hd->html_data, 0); - } - /* BUG: should really bitch about this */ - } - - return(0); /* DON'T get linked */ -} - - -/* - * HTML <H1> (Headings 1) element handler. - * - * Bold, very-large font, CENTERED. One or two blank lines - * above and below. For our silly character cell's that - * means centered and ALL CAPS... - */ -int -html_h1(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* turn ON the centered bit */ - CENTER_BIT(hd->html_data) = 1; - } - else if(cmd == GF_EOD){ - /* turn OFF the centered bit, add blank line */ - CENTER_BIT(hd->html_data) = 0; - html_blank(hd->html_data, 1); - } - - return(1); /* get linked */ -} - - -/* - * HTML <H2> (Headings 2) element handler - */ -int -html_h2(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - if((hd->x & HTML_HX_ULINE) && !isspace((unsigned char) (ch & 0xff))){ - HTML_ULINE(hd->html_data, 1); - hd->x ^= HTML_HX_ULINE; /* only once! */ - } - - html_handoff(hd, (ch < 128 && islower((unsigned char) ch)) - ? toupper((unsigned char) ch) : ch); - } - else if(cmd == GF_RESET){ - /* - * Bold, large font, flush-left. One or two blank lines - * above and below. - */ - if(CENTER_BIT(hd->html_data)) /* stop centering for now */ - hd->x = HTML_HX_CENTER; - else - hd->x = 0; - - hd->x |= HTML_HX_ULINE; - - CENTER_BIT(hd->html_data) = 0; - hd->y = html_indent(hd->html_data, 0, HTML_ID_SET); - hd->z = HD(hd->html_data)->wrapcol; - HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8; - html_blank(hd->html_data, 1); - } - else if(cmd == GF_EOD){ - /* - * restore previous centering, and indent level - */ - if(!(hd->x & HTML_HX_ULINE)) - HTML_ULINE(hd->html_data, 0); - - html_indent(hd->html_data, hd->y, HTML_ID_SET); - html_blank(hd->html_data, 1); - CENTER_BIT(hd->html_data) = (hd->x & HTML_HX_CENTER) != 0; - HD(hd->html_data)->wrapcol = hd->z; - } - - return(1); /* get linked */ -} - - -/* - * HTML <H3> (Headings 3) element handler - */ -int -html_h3(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - if((hd->x & HTML_HX_ULINE) && !isspace((unsigned char) (ch & 0xff))){ - HTML_ULINE(hd->html_data, 1); - hd->x ^= HTML_HX_ULINE; /* only once! */ - } - - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* - * Italic, large font, slightly indented from the left - * margin. One or two blank lines above and below. - */ - if(CENTER_BIT(hd->html_data)) /* stop centering for now */ - hd->x = HTML_HX_CENTER; - else - hd->x = 0; - - hd->x |= HTML_HX_ULINE; - CENTER_BIT(hd->html_data) = 0; - hd->y = html_indent(hd->html_data, 2, HTML_ID_SET); - hd->z = HD(hd->html_data)->wrapcol; - HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8; - html_blank(hd->html_data, 1); - } - else if(cmd == GF_EOD){ - /* - * restore previous centering, and indent level - */ - if(!(hd->x & HTML_HX_ULINE)) - HTML_ULINE(hd->html_data, 0); - - html_indent(hd->html_data, hd->y, HTML_ID_SET); - html_blank(hd->html_data, 1); - CENTER_BIT(hd->html_data) = (hd->x & HTML_HX_CENTER) != 0; - HD(hd->html_data)->wrapcol = hd->z; - } - - return(1); /* get linked */ -} - - -/* - * HTML <H4> (Headings 4) element handler - */ -int -html_h4(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* - * Bold, normal font, indented more than H3. One blank line - * above and below. - */ - hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */ - CENTER_BIT(hd->html_data) = 0; - hd->y = html_indent(hd->html_data, 4, HTML_ID_SET); - hd->z = HD(hd->html_data)->wrapcol; - HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8; - html_blank(hd->html_data, 1); - } - else if(cmd == GF_EOD){ - /* - * restore previous centering, and indent level - */ - html_indent(hd->html_data, (int) hd->y, HTML_ID_SET); - html_blank(hd->html_data, 1); - CENTER_BIT(hd->html_data) = hd->x; - HD(hd->html_data)->wrapcol = hd->z; - } - - return(1); /* get linked */ -} - - -/* - * HTML <H5> (Headings 5) element handler - */ -int -html_h5(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* - * Italic, normal font, indented as H4. One blank line - * above. - */ - hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */ - CENTER_BIT(hd->html_data) = 0; - hd->y = html_indent(hd->html_data, 6, HTML_ID_SET); - hd->z = HD(hd->html_data)->wrapcol; - HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8; - html_blank(hd->html_data, 1); - } - else if(cmd == GF_EOD){ - /* - * restore previous centering, and indent level - */ - html_indent(hd->html_data, (int) hd->y, HTML_ID_SET); - html_blank(hd->html_data, 1); - CENTER_BIT(hd->html_data) = hd->x; - HD(hd->html_data)->wrapcol = hd->z; - } - - return(1); /* get linked */ -} - - -/* - * HTML <H6> (Headings 6) element handler - */ -int -html_h6(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* - * Bold, indented same as normal text, more than H5. One - * blank line above. - */ - hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */ - CENTER_BIT(hd->html_data) = 0; - hd->y = html_indent(hd->html_data, 8, HTML_ID_SET); - hd->z = HD(hd->html_data)->wrapcol; - HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8; - html_blank(hd->html_data, 1); - } - else if(cmd == GF_EOD){ - /* - * restore previous centering, and indent level - */ - html_indent(hd->html_data, (int) hd->y, HTML_ID_SET); - html_blank(hd->html_data, 1); - CENTER_BIT(hd->html_data) = hd->x; - HD(hd->html_data)->wrapcol = hd->z; - } - - return(1); /* get linked */ -} - - -/* - * HTML <BlockQuote> element handler - */ -int -html_blockquote(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - int j; -#define HTML_BQ_INDENT 6 - - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* - * A typical rendering might be a slight extra left and - * right indent, and/or italic font. The Blockquote element - * causes a paragraph break, and typically provides space - * above and below the quote. - */ - html_indent(hd->html_data, HTML_BQ_INDENT, HTML_ID_INC); - j = HD(hd->html_data)->wrapstate; - HD(hd->html_data)->wrapstate = 0; - html_blank(hd->html_data, 1); - HD(hd->html_data)->wrapstate = j; - HD(hd->html_data)->wrapcol -= HTML_BQ_INDENT; - } - else if(cmd == GF_EOD){ - html_blank(hd->html_data, 1); - - j = HD(hd->html_data)->wrapstate; - HD(hd->html_data)->wrapstate = 0; - html_indent(hd->html_data, -(HTML_BQ_INDENT), HTML_ID_INC); - HD(hd->html_data)->wrapstate = j; - HD(hd->html_data)->wrapcol += HTML_BQ_INDENT; - } - - return(1); /* get linked */ -} - - -/* - * HTML <Address> element handler - */ -int -html_address(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - int j; -#define HTML_ADD_INDENT 2 - - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* - * A typical rendering might be a slight extra left and - * right indent, and/or italic font. The Blockquote element - * causes a paragraph break, and typically provides space - * above and below the quote. - */ - html_indent(hd->html_data, HTML_ADD_INDENT, HTML_ID_INC); - j = HD(hd->html_data)->wrapstate; - HD(hd->html_data)->wrapstate = 0; - html_blank(hd->html_data, 1); - HD(hd->html_data)->wrapstate = j; - } - else if(cmd == GF_EOD){ - html_blank(hd->html_data, 1); - - j = HD(hd->html_data)->wrapstate; - HD(hd->html_data)->wrapstate = 0; - html_indent(hd->html_data, -(HTML_ADD_INDENT), HTML_ID_INC); - HD(hd->html_data)->wrapstate = j; - } - - return(1); /* get linked */ -} - - -/* - * HTML <PRE> (Preformatted Text) element handler - */ -int -html_pre(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - /* - * remove CRLF after '>' in element. - * We see CRLF because wrapstate is off. - */ - switch(hd->y){ - case 2 : - if(ch == '\012'){ - hd->y = 3; - return(1); - } - else - html_handoff(hd, '\015'); - - break; - - case 1 : - if(ch == '\015'){ - hd->y = 2; - return(1); - } - - default : - hd->y = 0; - break; - } - - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - html_blank(hd->html_data, 1); - hd->x = HD(hd->html_data)->wrapstate; - HD(hd->html_data)->wrapstate = 0; - hd->y = 1; - } - else if(cmd == GF_EOD){ - HD(hd->html_data)->wrapstate = (hd->x != 0); - html_blank(hd->html_data, 0); - } - - return(1); -} - - - - -/* - * HTML <CENTER> (Centerd Text) element handler - */ -int -html_center(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - /* turn ON the centered bit */ - CENTER_BIT(hd->html_data) = 1; - } - else if(cmd == GF_EOD){ - /* turn OFF the centered bit */ - CENTER_BIT(hd->html_data) = 0; - } - - return(1); -} - - - -/* - * HTML <DIV> (Document Divisions) element handler - */ -int -html_div(hd, ch, cmd) - HANDLER_S *hd; - int ch, cmd; -{ - if(cmd == GF_DATA){ - html_handoff(hd, ch); - } - else if(cmd == GF_RESET){ - PARAMETER *p; - - for(p = HD(hd->html_data)->el_data->attribs; - p && p->attribute; - p = p->next) - if(!strucmp(p->attribute, "ALIGN")){ - if(p->value){ - /* remember previous values */ - hd->x = CENTER_BIT(hd->html_data); - hd->y = html_indent(hd->html_data, 0, HTML_ID_GET); - - html_blank(hd->html_data, 0); - CENTER_BIT(hd->html_data) = !strucmp(p->value, "CENTER"); - html_indent(hd->html_data, 0, HTML_ID_SET); - /* NOTE: "RIGHT" not supported yet */ - } - } - } - else if(cmd == GF_EOD){ - /* restore centered bit and indentiousness */ - CENTER_BIT(hd->html_data) = hd->y; - html_indent(hd->html_data, hd->y, HTML_ID_SET); - html_blank(hd->html_data, 0); - } - - return(1); -} - - - -/* - * return the function associated with the given element name - */ -html_f -html_element_func(el_name) - char *el_name; -{ - register int i; - - for(i = 0; element_table[i].element; i++) - if(!strucmp(el_name, element_table[i].element)) - return(element_table[i].handler); - - return(NULL); -} - - -/* - * collect element's name and any attribute/value pairs then - * dispatch to the appropriate handler. - * - * Returns 1 : got what we wanted - * 0 : we need more data - * -1 : bogus input - */ -int -html_element_collector(fd, ch) - FILTER_S *fd; - int ch; -{ - if(ch == '>'){ - if(ED(fd)->overrun){ - /* - * If problem processing, don't bother doing anything - * internally, just return such that none of what we've - * digested is displayed. - */ - HTML_DEBUG_EL("too long", ED(fd)); - return(1); /* Let it go, Jim */ - } - else if(ED(fd)->mkup_decl){ - if(ED(fd)->badform){ - dprint(2, (debugfile, "-- html <!-- BAD: %.*s\n", - ED(fd)->len, ED(fd)->buf)); - /* - * Invalid comment -- make some guesses as - * to whether we should stop with this greater-than... - */ - if(ED(fd)->buf[0] != '-' - || ED(fd)->len < 4 - || (ED(fd)->buf[1] == '-' - && ED(fd)->buf[ED(fd)->len - 1] == '-' - && ED(fd)->buf[ED(fd)->len - 2] == '-')) - return(1); - } - else{ - dprint(2, (debugfile, "-- html <!-- OK: %.*s\n", - ED(fd)->len, ED(fd)->buf)); - if(ED(fd)->start_comment == ED(fd)->end_comment){ - if(ED(fd)->len > 10){ - ED(fd)->buf[ED(fd)->len - 2] = '\0'; - html_element_comment(fd, ED(fd)->buf + 2); - } - - return(1); - } - /* else keep collecting comment below */ - } - } - else if(!ED(fd)->quoted || ED(fd)->badform){ - html_f f; - - /* - * We either have the whole thing or all that we could - * salvage from it. Try our best... - */ - - if(HD(fd)->bitbucket) - return(1); /* element inside chtml clause! */ - - if(!ED(fd)->badform && html_element_flush(ED(fd))) - return(1); /* return without display... */ - - /* - * If we ran into an empty tag or we don't know how to deal - * with it, just go on, ignoring it... - */ - if(ED(fd)->element && (f = html_element_func(ED(fd)->element))){ - /* dispatch the element's handler */ - if(ED(fd)->end_tag) - html_pop(fd, f); /* remove it's handler */ - else - html_push(fd, f); /* add it's handler */ - - HTML_DEBUG_EL(ED(fd)->end_tag ? "POP" : "PUSH", ED(fd)); - } - else{ /* else, empty or unrecognized */ - HTML_DEBUG_EL("?", ED(fd)); - } - - return(1); /* all done! see, that didn't hurt */ - } - } - - if(ED(fd)->mkup_decl){ - if((ch &= 0xff) == '-'){ - if(ED(fd)->hyphen){ - ED(fd)->hyphen = 0; - if(ED(fd)->start_comment) - ED(fd)->end_comment = 1; - else - ED(fd)->start_comment = 1; - } - else - ED(fd)->hyphen = 1; - } - else{ - if(ED(fd)->end_comment) - ED(fd)->start_comment = ED(fd)->end_comment = 0; - - /* - * no "--" after ! or non-whitespace between comments - bad - */ - if(ED(fd)->len < 2 || (!ED(fd)->start_comment - && !isspace((unsigned char) ch))) - ED(fd)->badform = 1; /* non-comment! */ - - ED(fd)->hyphen = 0; - } - - /* - * Remember the comment for possible later processing, if - * it get's too long, remember first and last few chars - * so we know when to terminate (and throw some garbage - * in between when we toss out what's between. - */ - if(ED(fd)->len == HTML_BUF_LEN){ - ED(fd)->buf[2] = ED(fd)->buf[3] = 'X'; - ED(fd)->buf[4] = ED(fd)->buf[ED(fd)->len - 2]; - ED(fd)->buf[5] = ED(fd)->buf[ED(fd)->len - 1]; - ED(fd)->len = 6; - } - - ED(fd)->buf[(ED(fd)->len)++] = ch; - return(0); /* comments go in the bit bucket */ - } - else if(ED(fd)->overrun || ED(fd)->badform){ - return(0); /* swallow char's until next '>' */ - } - else if(!ED(fd)->element && !ED(fd)->len){ - if(ch == '/'){ /* validate leading chars */ - ED(fd)->end_tag = 1; - return(0); - } - else if(ch == '!'){ - ED(fd)->mkup_decl = 1; - return(0); - } - else if(!isalpha((unsigned char) ch)) - return(-1); /* can't be a tag! */ - } - else if(ch == '\"' || ch == '\''){ - if(!ED(fd)->hit_equal){ - ED(fd)->badform = 1; /* quote in element name?!? */ - return(0); - } - - if(ED(fd)->quoted){ - if(ED(fd)->quoted == (char) ch){ - ED(fd)->quoted = 0; - return(0); /* continue collecting chars */ - } - /* else fall thru writing other quoting char */ - } - else{ - ED(fd)->quoted = (char) ch; - return(0); /* need more data */ - } - } - - ch &= 0xff; /* strip any "literal" high bits */ - if(ED(fd)->quoted - || isalnum(ch) - || strchr("-.!", ch) - || (ED(fd)->hit_equal && !isspace((unsigned char) ch))){ - if(ED(fd)->len < ((ED(fd)->element || !ED(fd)->hit_equal) - ? HTML_BUF_LEN:MAX_ELEMENT)){ - ED(fd)->buf[(ED(fd)->len)++] = ch; - } - else - ED(fd)->overrun = 1; /* flag it broken */ - } - else if(isspace((unsigned char) ch) || ch == '='){ - if(html_element_flush(ED(fd))){ - ED(fd)->badform = 1; - return(0); /* else, we ain't done yet */ - } - - if(!ED(fd)->hit_equal) - ED(fd)->hit_equal = (ch == '='); - } - else - ED(fd)->badform = 1; /* unrecognized data?? */ - - return(0); /* keep collecting */ -} - - -/* - * Element collector found complete string, integrate it and reset - * internal collection buffer. - * - * Returns zero if element collection buffer flushed, error flag otherwise - */ -int -html_element_flush(el_data) - CLCTR_S *el_data; -{ - int rv = 0; - - if(el_data->hit_equal){ /* adding a value */ - el_data->hit_equal = 0; - if(el_data->cur_attrib){ - if(!el_data->cur_attrib->value){ - el_data->cur_attrib->value = cpystr(el_data->len - ? el_data->buf : ""); - } - else{ - dprint(2, (debugfile, - "** element: unexpected value: %.10s...\n", - el_data->len ? el_data->buf : "\"\"")); - rv = 1; - } - } - else{ - dprint(2, (debugfile, - "** element: missing attribute name: %.10s...\n", - el_data->len ? el_data->buf : "\"\"")); - rv = 2; - } - - el_data->len = 0; - memset(el_data->buf, 0, HTML_BUF_LEN); - } - else if(el_data->len){ - if(!el_data->element){ - el_data->element = cpystr(el_data->buf); - } - else{ - PARAMETER *p = (PARAMETER *)fs_get(sizeof(PARAMETER)); - memset(p, 0, sizeof(PARAMETER)); - if(el_data->attribs){ - el_data->cur_attrib->next = p; - el_data->cur_attrib = p; - } - else - el_data->attribs = el_data->cur_attrib = p; - - p->attribute = cpystr(el_data->buf); - } - - el_data->len = 0; - memset(el_data->buf, 0, HTML_BUF_LEN); - } - - return(rv); /* report whatever happened above */ -} - - -/* - * html_element_comment - "Special" comment handling here - */ -void -html_element_comment(f, s) - FILTER_S *f; - char *s; -{ - char *p; - - while(*s && isspace((unsigned char) *s)) - s++; - - /* - * WARNING: "!--chtml" denotes "Conditional HTML", a UW-ism. - */ - if(!struncmp(s, "chtml ", 6)){ - s += 6; - if(!struncmp(s, "if ", 3)){ - HD(f)->bitbucket = 1; /* default is failure! */ - switch(*(s += 3)){ - case 'P' : - case 'p' : - if(!struncmp(s + 1, "inemode=", 8)){ - if(!strucmp(s = removing_quotes(s + 9), "function_key") - && F_ON(F_USE_FK, ps_global)) - HD(f)->bitbucket = 0; - else if(!strucmp(s, "running")) - HD(f)->bitbucket = 0; - else if(!strucmp(s, "phone_home") && ps_global->phone_home) - HD(f)->bitbucket = 0; -#ifdef _WINDOWS - else if(!strucmp(s, "os_windows")) - HD(f)->bitbucket = 0; -#endif - } - - break; - - case '[' : /* test */ - if(p = strindex(++s, ']')){ - *p = '\0'; /* tie off test string */ - removing_leading_white_space(s); - removing_trailing_white_space(s); - if(*s == '-' && *(s+1) == 'r'){ /* readable file? */ - for(s += 2; *s && isspace((unsigned char) *s); s++) - ; - - - HD(f)->bitbucket = (can_access(removing_quotes(s), - READ_ACCESS) != 0); - } - } - - break; - - default : - break; - } - } - else if(!strucmp(s, "else")){ - HD(f)->bitbucket = !HD(f)->bitbucket; - } - else if(!strucmp(s, "endif")){ - /* Clean up after chtml here */ - HD(f)->bitbucket = 0; - } - } - else if(!HD(f)->bitbucket){ - if(!struncmp(s, "#include ", 9)){ - char buf[MAILTMPLEN], *bufp; - int len, end_of_line; - FILE *fp; - - /* Include the named file */ - if(!struncmp(s += 9, "file=", 5) - && (fp = fopen(removing_quotes(s+5), "r"))){ - html_element_output(f, HTML_NEWLINE); - - while(fgets(buf, MAILTMPLEN, fp)){ - if((len = strlen(buf)) && buf[len-1] == '\n'){ - end_of_line = 1; - buf[--len] = '\0'; - } - else - end_of_line = 0; - - for(bufp = buf; len; bufp++, len--) - html_element_output(f, (int) *bufp); - - if(end_of_line) - html_element_output(f, HTML_NEWLINE); - } - - fclose(fp); - html_element_output(f, HTML_NEWLINE); - HD(f)->blanks = 0; - if(f->f1 == WSPACE) - f->f1 = DFL; - } - } - else if(!struncmp(s, "#echo ", 6)){ - if(!struncmp(s += 6, "var=", 4)){ - char *p, buf[MAILTMPLEN]; - ADDRESS *adr; - extern char datestamp[]; - - if(!strcmp(s = removing_quotes(s + 4), "PINE_VERSION")){ - p = pine_version; - } - else if(!strcmp(s, "PINE_COMPILE_DATE")){ - p = datestamp; - } - else if(!strcmp(s, "PINE_TODAYS_DATE")){ - rfc822_date(p = buf); - } - else if(!strcmp(s, "_LOCAL_FULLNAME_")){ - p = (ps_global->VAR_LOCAL_FULLNAME - && ps_global->VAR_LOCAL_FULLNAME[0]) - ? ps_global->VAR_LOCAL_FULLNAME - : "Local Support"; - } - else if(!strcmp(s, "_LOCAL_ADDRESS_")){ - p = (ps_global->VAR_LOCAL_ADDRESS - && ps_global->VAR_LOCAL_ADDRESS[0]) - ? ps_global->VAR_LOCAL_ADDRESS - : "postmaster"; - adr = rfc822_parse_mailbox(&p, ps_global->maildomain); - sprintf(p = buf, "%s@%s", adr->mailbox, adr->host); - mail_free_address(&adr); - } - else if(!strcmp(s, "_BUGS_FULLNAME_")){ - p = (ps_global->VAR_BUGS_FULLNAME - && ps_global->VAR_BUGS_FULLNAME[0]) - ? ps_global->VAR_BUGS_FULLNAME - : "Place to report Pine Bugs"; - } - else if(!strcmp(s, "_BUGS_ADDRESS_")){ - p = (ps_global->VAR_BUGS_ADDRESS - && ps_global->VAR_BUGS_ADDRESS[0]) - ? ps_global->VAR_BUGS_ADDRESS : "postmaster"; - adr = rfc822_parse_mailbox(&p, ps_global->maildomain); - sprintf(p = buf, "%s@%s", adr->mailbox, adr->host); - mail_free_address(&adr); - } - else if(!strcmp(s, "CURRENT_DIR")){ - getcwd(p = buf, MAILTMPLEN); - } - else if(!strcmp(s, "HOME_DIR")){ - p = ps_global->home_dir; - } - else - p = NULL; - - if(p){ - if(f->f1 == WSPACE){ - html_element_output(f, ' '); - f->f1 = DFL; /* clear it */ - } - - while(*p) - html_element_output(f, (int) *p++); - } - } - } - } -} - - -void -html_element_output(f, ch) - FILTER_S *f; - int ch; -{ - if(HANDLERS(f)) - (*HANDLERS(f)->f)(HANDLERS(f), ch, GF_DATA); - else - html_output(f, ch); -} - - -/* - * collect html entities and return its value when done. - * - * Returns 0 : we need more data - * 1-255 : char value of entity collected - * HTML_BADVALUE : good data, but no named match or out of range - * HTML_BADDATA : invalid input - * - * NOTES: - * - entity format is "'&' tag ';'" and represents a literal char - * - named entities are CASE SENSITIVE. - * - numeric char references (where the tag is prefixed with a '#') - * are a char with that numbers value - * - numeric vals are 0-255 except for the ranges: 0-8, 11-31, 127-159. - */ -int -html_entity_collector(f, ch, alternate) - FILTER_S *f; - int ch; - char **alternate; -{ - static char len = 0; - static char buf[MAX_ENTITY]; - int rv = 0, i; - - if((len == 0) - ? (isalpha((unsigned char) ch) || ch == '#') - : ((isdigit((unsigned char) ch) - || (isalpha((unsigned char) ch) && buf[0] != '#')) - && len < MAX_ENTITY - 1)){ - buf[len++] = ch; - } - else if((isspace((unsigned char) ch) || ch == ';') && len){ - buf[len] = '\0'; /* got something! */ - switch(buf[0]){ - case '#' : - rv = atoi(&buf[1]); - if(F_ON(F_PASS_CONTROL_CHARS, ps_global) - || (rv == '\t' || rv == '\n' || rv == '\r' - || (rv > 31 && rv < 127) || (rv > 159 && rv < 256))){ - if(alternate) - for(i = 0, *alternate = NULL; entity_tab[i].name; i++) - if(entity_tab[i].value == rv){ - *alternate = entity_tab[i].plain; - break; - } - } - else - rv = HTML_BADVALUE; - - break; - - default : - rv = HTML_BADVALUE; /* in case we fail below */ - for(i = 0; entity_tab[i].name; i++) - if(strcmp(entity_tab[i].name, buf) == 0){ - rv = entity_tab[i].value; - if(alternate) - *alternate = entity_tab[i].plain; - - break; - } - - break; - } - } - else - rv = HTML_BADDATA; /* bogus input! */ - - if(rv){ /* nonzero return, clean up */ - if(rv > 0xff && alternate){ /* provide bogus data to caller */ - buf[len] = '\0'; - *alternate = buf; - } - - len = 0; - } - - return(rv); -} - - -/*---------------------------------------------------------------------- - HTML text to plain text filter - - This basically tries to do the best it can with HTML 2.0 (RFC1866) - with bits of RFC 1942 (plus some HTML 3.2 thrown in as well) text - formatting. - - ----*/ -void -gf_html2plain(f, flg) - FILTER_S *f; - int flg; -{ -/* BUG: qoute incoming \255 values (see "yuml" above!) */ - if(flg == GF_DATA){ - register int c; - GF_INIT(f, f->next); - - while(GF_GETC(f, c)){ - /* - * First we have to collect any literal entities... - * that is, IF we're not already collecting one - * AND we're not in element's text or, if we are, we're - * not in quoted text. Whew. - */ - if(f->t){ - int i; - char *alt = NULL; - - switch(i = html_entity_collector(f, c, &alt)){ - case 0: /* more data required? */ - continue; /* go get another char */ - - case HTML_BADVALUE : - case HTML_BADDATA : - /* if supplied, process bogus data */ - HTML_PROC(f, '&'); - for(; *alt; alt++) - HTML_PROC(f, *alt); - - if(c == '&' && !HD(f)->quoted){ - f->t = '&'; - continue; - } - else - f->t = 0; /* don't come back next time */ - - break; - - default : /* thing to process */ - f->t = 0; /* don't come back */ - - /* - * Map some of the undisplayable entities? - */ - if(HD(f)->alt_entity && i > 127 && alt && alt[0]){ - for(; *alt; alt++){ - c = MAKE_LITERAL(*alt); - HTML_PROC(f, c); - } - - continue; - } - - c = MAKE_LITERAL(i); - break; - } - } - else if(c == '&' && !HD(f)->quoted){ - f->t = '&'; - continue; - } - - /* - * then we process whatever we got... - */ - - HTML_PROC(f, c); - } - - GF_OP_END(f); /* clean up our input pointers */ - } - else if(flg == GF_EOD){ - while(HANDLERS(f)) - /* BUG: should complain about "Unexpected end of HTML text." */ - html_pop(f, HANDLERS(f)->f); - - html_output(f, HTML_NEWLINE); - HTML_FLUSH(f); - fs_give((void **)&f->line); - if(HD(f)->color) - free_color_pair(&HD(f)->color); - - fs_give(&f->data); - if(f->opt){ - if(((HTML_OPT_S *)f->opt)->base) - fs_give((void **) &((HTML_OPT_S *)f->opt)->base); - - fs_give(&f->opt); - } - - (*f->next->f)(f->next, GF_DATA); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset html2plain\n")); - f->data = (HTML_DATA_S *) fs_get(sizeof(HTML_DATA_S)); - memset(f->data, 0, sizeof(HTML_DATA_S)); - HD(f)->wrapstate = 1; /* start with flowing text */ - HD(f)->wrapcol = WRAP_COLS(f) - 8; - f->f1 = DFL; /* state */ - f->f2 = 0; /* chars in wrap buffer */ - f->n = 0L; /* chars on line so far */ - f->linep = f->line = (char *)fs_get(HTML_BUF_LEN * sizeof(char)); - HD(f)->alt_entity = (!ps_global->VAR_CHAR_SET - || strucmp(ps_global->VAR_CHAR_SET, - "iso-8859-1")); - } -} - - - -/* - * html_indent - do the requested indent level function with appropriate - * flushing and such. - * - * Returns: indent level prior to set/increment - */ -int -html_indent(f, val, func) - FILTER_S *f; - int val, func; -{ - int old = HD(f)->indent_level; - - /* flush pending data at old indent level */ - switch(func){ - case HTML_ID_INC : - html_output_flush(f); - if((HD(f)->indent_level += val) < 0) - HD(f)->indent_level = 0; - - break; - - case HTML_ID_SET : - html_output_flush(f); - HD(f)->indent_level = val; - break; - - default : - break; - } - - return(old); -} - - - -/* - * html_blanks - Insert n blank lines into output - */ -void -html_blank(f, n) - FILTER_S *f; - int n; -{ - /* Cap off any flowing text, and then write blank lines */ - if(f->f2 || f->n || CENTER_BIT(f) || HD(f)->centered || WRAPPED_LEN(f)) - html_output(f, HTML_NEWLINE); - - if(HD(f)->wrapstate) - while(HD(f)->blanks < n) /* blanks inc'd by HTML_NEWLINE */ - html_output(f, HTML_NEWLINE); -} - - - -/* - * html_newline -- insert a newline mindful of embedded tags - */ -void -html_newline(f) - FILTER_S *f; -{ - html_write_newline(f); /* commit an actual newline */ - - if(f->n){ /* and keep track of blank lines */ - HD(f)->blanks = 0; - f->n = 0L; - } - else - HD(f)->blanks++; -} - - -/* - * output the given char, handling any requested wrapping. - * It's understood that all whitespace handed us is written. In other - * words, junk whitespace is weeded out before it's given to us here. - * - */ -void -html_output(f, ch) - FILTER_S *f; - int ch; -{ - if(CENTER_BIT(f)){ /* center incoming text */ - html_output_centered(f, ch); - } - else{ - static short embedded = 0; /* BUG: reset on entering filter */ - static char *color_ptr = NULL; - - if(HD(f)->centered){ - html_centered_flush(f); - fs_give((void **) &HD(f)->centered->line.buf); - fs_give((void **) &HD(f)->centered->word.buf); - fs_give((void **) &HD(f)->centered); - } - - if(HD(f)->wrapstate){ - if(ch == HTML_NEWLINE){ /* hard newline */ - html_output_flush(f); - html_newline(f); - } - else - HD(f)->blanks = 0; /* reset blank line counter */ - - if(ch == TAG_EMBED){ /* takes up no space */ - embedded = 1; - *(f->linep)++ = TAG_EMBED; - } - else if(embedded){ /* ditto */ - if(ch == TAG_HANDLE) - embedded = -1; /* next ch is length */ - else if(ch == TAG_FGCOLOR || ch == TAG_BGCOLOR){ - if(!HD(f)->color) - HD(f)->color = new_color_pair(NULL, NULL); - - if(ch == TAG_FGCOLOR) - color_ptr = HD(f)->color->fg; - else - color_ptr = HD(f)->color->bg; - - embedded = 11; - } - else if(embedded < 0){ - embedded = ch; /* number of embedded chars */ - } - else{ - embedded--; - if(color_ptr) - *color_ptr++ = ch; - - if(embedded == 0 && color_ptr){ - *color_ptr = '\0'; - color_ptr = NULL; - } - } - - *(f->linep)++ = ch; - } - else if(HTML_ISSPACE(ch)){ - html_output_flush(f); - } - else{ - if(HD(f)->prefix) - html_a_prefix(f); - - if(++f->f2 >= WRAP_COLS(f)){ - HTML_FLUSH(f); - html_newline(f); - if(HD(f)->in_anchor) - html_write_anchor(f, HD(f)->in_anchor); - } - else - *(f->linep)++ = ch & 0xff; - } - } - else{ - if(HD(f)->prefix) - html_a_prefix(f); - - html_output_flush(f); - - switch(embedded){ - case 0 : - switch(ch){ - default : - f->n++; /* inc displayed char count */ - HD(f)->blanks = 0; /* reset blank line counter */ - html_putc(f, ch & 0xff); - break; - - case TAG_EMBED : /* takes up no space */ - html_putc(f, TAG_EMBED); - embedded = -2; - break; - - case HTML_NEWLINE : /* newline handling */ - if(!f->n) - break; - - case '\n' : - html_newline(f); - - case '\r' : - break; - } - - break; - - case -2 : - embedded = 0; - switch(ch){ - case TAG_HANDLE : - embedded = -1; /* next ch is length */ - break; - - case TAG_BOLDON : - BOLD_BIT(f) = 1; - break; - - case TAG_BOLDOFF : - BOLD_BIT(f) = 0; - break; - - case TAG_ULINEON : - ULINE_BIT(f) = 1; - break; - - case TAG_ULINEOFF : - ULINE_BIT(f) = 0; - break; - - case TAG_FGCOLOR : - if(!HD(f)->color) - HD(f)->color = new_color_pair(NULL, NULL); - - color_ptr = HD(f)->color->fg; - embedded = 11; - break; - - case TAG_BGCOLOR : - if(!HD(f)->color) - HD(f)->color = new_color_pair(NULL, NULL); - - color_ptr = HD(f)->color->bg; - embedded = 11; - break; - - case TAG_HANDLEOFF : - ch = TAG_INVOFF; - HD(f)->in_anchor = 0; - break; - - default : - break; - } - - html_putc(f, ch); - break; - - case -1 : - embedded = ch; /* number of embedded chars */ - html_putc(f, ch); - break; - - default : - embedded--; - if(color_ptr) - *color_ptr++ = ch; - - if(embedded == 0 && color_ptr){ - *color_ptr = '\0'; - color_ptr = NULL; - } - - html_putc(f, ch); - break; - } - } - } -} - - -/* - * flush any buffered chars waiting for wrapping. - */ -void -html_output_flush(f) - FILTER_S *f; -{ - if(f->f2){ - if(f->n && ((int) f->n) + f->f2 > HD(f)->wrapcol) - html_newline(f); /* wrap? */ - - if(f->n){ /* text already on the line? */ - html_putc(f, ' '); - f->n++; /* increment count */ - } - else{ - /* write at start of new line */ - html_write_indent(f, HD(f)->indent_level); - - if(HD(f)->in_anchor) - html_write_anchor(f, HD(f)->in_anchor); - } - - f->n += f->f2; - HTML_FLUSH(f); - } -} - - - -/* - * html_output_centered - managed writing centered text - */ -void -html_output_centered(f, ch) - FILTER_S *f; - int ch; -{ - if(!HD(f)->centered){ /* new text? */ - html_output_flush(f); - if(f->n) /* start on blank line */ - html_newline(f); - - HD(f)->centered = (CENTER_S *) fs_get(sizeof(CENTER_S)); - memset(HD(f)->centered, 0, sizeof(CENTER_S)); - /* and grab a buf to start collecting centered text */ - HD(f)->centered->line.len = WRAP_COLS(f); - HD(f)->centered->line.buf = (char *) fs_get(HD(f)->centered->line.len - * sizeof(char)); - HD(f)->centered->line.used = HD(f)->centered->line.width = 0; - HD(f)->centered->word.len = 32; - HD(f)->centered->word.buf = (char *) fs_get(HD(f)->centered->word.len - * sizeof(char)); - HD(f)->centered->word.used = HD(f)->centered->word.width = 0; - } - - if(ch == HTML_NEWLINE){ /* hard newline */ - html_centered_flush(f); - } - else if(ch == TAG_EMBED){ /* takes up no space */ - HD(f)->centered->embedded = 1; - html_centered_putc(&HD(f)->centered->word, TAG_EMBED); - } - else if(HD(f)->centered->embedded){ - static char *color_ptr = NULL; - - if(ch == TAG_HANDLE){ - HD(f)->centered->embedded = -1; /* next ch is length */ - } - else if(ch == TAG_FGCOLOR || ch == TAG_BGCOLOR){ - if(!HD(f)->color) - HD(f)->color = new_color_pair(NULL, NULL); - - if(ch == TAG_FGCOLOR) - color_ptr = HD(f)->color->fg; - else - color_ptr = HD(f)->color->bg; - - HD(f)->centered->embedded = 11; - } - else if(HD(f)->centered->embedded < 0){ - HD(f)->centered->embedded = ch; /* number of embedded chars */ - } - else{ - HD(f)->centered->embedded--; - if(color_ptr) - *color_ptr++ = ch; - - if(HD(f)->centered->embedded == 0 && color_ptr){ - *color_ptr = '\0'; - color_ptr = NULL; - } - } - - html_centered_putc(&HD(f)->centered->word, ch); - } - else if(isspace((unsigned char) ch)){ - if(!HD(f)->centered->space++){ /* end of a word? flush! */ - int i; - - if(WRAPPED_LEN(f) > HD(f)->wrapcol){ - html_centered_flush_line(f); - /* fall thru to put current "word" on blank "line" */ - } - else if(HD(f)->centered->line.width){ - /* put space char between line and appended word */ - html_centered_putc(&HD(f)->centered->line, ' '); - HD(f)->centered->line.width++; - } - - for(i = 0; i < HD(f)->centered->word.used; i++) - html_centered_putc(&HD(f)->centered->line, - HD(f)->centered->word.buf[i]); - - HD(f)->centered->line.width += HD(f)->centered->word.width; - HD(f)->centered->word.used = 0; - HD(f)->centered->word.width = 0; - } - } - else{ - if(HD(f)->prefix) - html_a_prefix(f); - - /* ch is start of next word */ - HD(f)->centered->space = 0; - if(HD(f)->centered->word.width >= WRAP_COLS(f)) - html_centered_flush(f); - - html_centered_putc(&HD(f)->centered->word, ch); - HD(f)->centered->word.width++; - } -} - - -/* - * html_centered_putc -- add given char to given WRAPLINE_S - */ -void -html_centered_putc(wp, ch) - WRAPLINE_S *wp; - int ch; -{ - if(wp->used + 1 >= wp->len){ - wp->len += 64; - fs_resize((void **) &wp->buf, wp->len * sizeof(char)); - } - - wp->buf[wp->used++] = ch; -} - - - -/* - * html_centered_flush - finish writing any pending centered output - */ -void -html_centered_flush(f) - FILTER_S *f; -{ - int i, h; - - /* - * If word present (what about line?) we need to deal with - * appending it... - */ - if(HD(f)->centered->word.width && WRAPPED_LEN(f) > HD(f)->wrapcol) - html_centered_flush_line(f); - - if(WRAPPED_LEN(f)){ - /* figure out how much to indent */ - if((i = (WRAP_COLS(f) - WRAPPED_LEN(f))/2) > 0) - html_write_indent(f, i); - - if(HD(f)->centered->anchor) - html_write_anchor(f, HD(f)->centered->anchor); - - html_centered_handle(&HD(f)->centered->anchor, - HD(f)->centered->line.buf, - HD(f)->centered->line.used); - html_write(f, HD(f)->centered->line.buf, HD(f)->centered->line.used); - - if(HD(f)->centered->word.used){ - if(HD(f)->centered->line.width) - html_putc(f, ' '); - - html_centered_handle(&HD(f)->centered->anchor, - HD(f)->centered->word.buf, - HD(f)->centered->word.used); - html_write(f, HD(f)->centered->word.buf, - HD(f)->centered->word.used); - } - - HD(f)->centered->line.used = HD(f)->centered->word.used = 0; - HD(f)->centered->line.width = HD(f)->centered->word.width = 0; - } - else - HD(f)->blanks++; /* advance the blank line counter */ - - html_newline(f); /* finish the line */ -} - - -/* - * html_centered_handle - scan the line for embedded handles - */ -void -html_centered_handle(h, line, len) - int *h; - char *line; - int len; -{ - int n; - - while(len-- > 0) - if(*line++ == TAG_EMBED && len-- > 0) - switch(*line++){ - case TAG_HANDLE : - if((n = *line++) >= --len){ - *h = 0; - len -= n; - while(n--) - *h = (*h * 10) + (*line++ - '0'); - } - break; - - case TAG_HANDLEOFF : - case TAG_INVOFF : - *h = 0; /* assumption 23,342: inverse off ends tags */ - break; - - default : - break; - } -} - - - -/* - * html_centered_flush_line - flush the centered "line" only - */ -void -html_centered_flush_line(f) - FILTER_S *f; -{ - if(HD(f)->centered->line.used){ - int i, j; - - /* hide "word" from flush */ - i = HD(f)->centered->word.used; - j = HD(f)->centered->word.width; - HD(f)->centered->word.used = 0; - HD(f)->centered->word.width = 0; - html_centered_flush(f); - - HD(f)->centered->word.used = i; - HD(f)->centered->word.width = j; - } -} - - -/* - * html_write_indent - write indention mindful of display attributes - */ -void -html_write_indent(f, indent) - FILTER_S *f; - int indent; -{ - if(! STRIP(f)){ - if(BOLD_BIT(f)){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_BOLDOFF); - } - - if(ULINE_BIT(f)){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_ULINEOFF); - } - } - - f->n = indent; - while(indent-- > 0) - html_putc(f, ' '); /* indent as needed */ - - /* - * Resume any previous embedded state - */ - if(! STRIP(f)){ - if(BOLD_BIT(f)){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_BOLDON); - } - - if(ULINE_BIT(f)){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_ULINEON); - } - } -} - - -/* - * - */ -void -html_write_anchor(f, anchor) - FILTER_S *f; - int anchor; -{ - char buf[256]; - int i; - - html_putc(f, TAG_EMBED); - html_putc(f, TAG_HANDLE); - sprintf(buf, "%d", anchor); - html_putc(f, (int) strlen(buf)); - - for(i = 0; buf[i]; i++) - html_putc(f, buf[i]); -} - - -/* - * html_write_newline - write a newline mindful of display attributes - */ -void -html_write_newline(f) - FILTER_S *f; -{ - if(! STRIP(f)){ /* First tie, off any embedded state */ - if(HD(f)->in_anchor){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_INVOFF); - } - - if(BOLD_BIT(f)){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_BOLDOFF); - } - - if(ULINE_BIT(f)){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_ULINEOFF); - } - - if(HD(f)->color && HD(f)->color->fg[0] && HD(f)->color->bg[0]){ - char *p; - int i; - - p = color_embed(ps_global->VAR_NORM_FORE_COLOR, - ps_global->VAR_NORM_BACK_COLOR); - for(i = 0; i < 2 * (RGBLEN + 2); i++) - html_putc(f, p[i]); - } - } - - html_write(f, "\015\012", 2); - - if(! STRIP(f)){ /* First tie, off any embedded state */ - if(BOLD_BIT(f)){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_BOLDON); - } - - if(ULINE_BIT(f)){ - html_putc(f, TAG_EMBED); - html_putc(f, TAG_ULINEON); - } - - if(HD(f)->color && HD(f)->color->fg[0] && HD(f)->color->bg[0]){ - char *p; - int i; - COLOR_PAIR *tmp; - - tmp = new_color_pair(HD(f)->color->fg, HD(f)->color->bg); - if(pico_is_good_colorpair(tmp)){ - p = color_embed(HD(f)->color->fg, HD(f)->color->bg); - for(i = 0; i < 2 * (RGBLEN + 2); i++) - html_putc(f, p[i]); - } - - HD(f)->color->fg[0] = '\0'; - HD(f)->color->bg[0] = '\0'; - - if(tmp) - free_color_pair(&tmp); - } - } -} - - -/* - * html_write - write given int array to the next filter. - */ -void -html_write(f, s, n) - FILTER_S *f; - char *s; - int n; -{ - GF_INIT(f, f->next); - - while(n-- > 0){ - /* keep track of attribute state? Not if last char! */ - if(*s == TAG_EMBED && n-- > 0){ - GF_PUTC(f->next, TAG_EMBED); - switch(*++s){ - case TAG_BOLDON : - BOLD_BIT(f) = 1; - break; - case TAG_BOLDOFF : - BOLD_BIT(f) = 0; - break; - case TAG_ULINEON : - ULINE_BIT(f) = 1; - break; - case TAG_ULINEOFF : - ULINE_BIT(f) = 0; - break; - case TAG_HANDLEOFF : - HD(f)->in_anchor = 0; - GF_PUTC(f->next, TAG_INVOFF); - s++; - continue; - case TAG_HANDLE : - if(n-- > 0){ - int i = *++s; - - GF_PUTC(f->next, TAG_HANDLE); - if(i <= n){ - n -= i; - GF_PUTC(f->next, i); - HD(f)->in_anchor = 0; - while(1){ - HD(f)->in_anchor = (HD(f)->in_anchor * 10) - + (*++s - '0'); - if(--i) - GF_PUTC(f->next, *s); - else - break; - } - } - } - - break; - default: - break; - } - } - - GF_PUTC(f->next, (*s++) & 0xff); - } - - GF_IP_END(f->next); /* clean up next's input pointers */ -} - - -/* - * html_putc -- actual work of writing to next filter. - * NOTE: Small opt not using full GF_END since our input - * pointers don't need adjusting. - */ -void -html_putc(f, ch) - FILTER_S *f; - int ch; -{ - GF_INIT(f, f->next); - GF_PUTC(f->next, ch & 0xff); - GF_IP_END(f->next); /* clean up next's input pointers */ -} - - - -/* - * Only current option is to turn on embedded data stripping for text - * bound to a printer or composer. - */ -void * -gf_html2plain_opt(base, columns, flags) - char *base; - int columns, flags; -{ - HTML_OPT_S *op; - - op = (HTML_OPT_S *) fs_get(sizeof(HTML_OPT_S)); - - op->base = cpystr(base); - op->columns = columns; - op->strip = ((flags & GFHP_STRIPPED) == GFHP_STRIPPED); - op->handles = ((flags & GFHP_HANDLES) == GFHP_HANDLES); - op->handles_loc = ((flags & GFHP_LOCAL_HANDLES) == GFHP_LOCAL_HANDLES); - return((void *) op); -} - - -/* END OF HTML-TO-PLAIN text filter */ - -/* - * ESCAPE CODE FILTER - remove unknown and possibly dangerous escape codes - * from the text stream. - */ - -#define MAX_ESC_LEN 5 - -/* - * the simple filter, removes unknown escape codes from the stream - */ -void -gf_escape_filter(f, flg) - FILTER_S *f; - int flg; -{ - register char *p; - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - - while(GF_GETC(f, c)){ - - if(state){ - if(c == '\033' || f->n == MAX_ESC_LEN){ - f->line[f->n] = '\0'; - f->n = 0L; - if(!match_escapes(f->line)){ - GF_PUTC(f->next, '^'); - GF_PUTC(f->next, '['); - } - else - GF_PUTC(f->next, '\033'); - - p = f->line; - while(*p) - GF_PUTC(f->next, *p++); - - if(c == '\033') - continue; - else - state = 0; /* fall thru */ - } - else{ - f->line[f->n++] = c; /* collect */ - continue; - } - } - - if(c == '\033') - state = 1; - else - GF_PUTC(f->next, c); - } - - f->f1 = state; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - if(f->f1){ - if(!match_escapes(f->line)){ - GF_PUTC(f->next, '^'); - GF_PUTC(f->next, '['); - } - else - GF_PUTC(f->next, '\033'); - } - - for(p = f->line; f->n; f->n--, p++) - GF_PUTC(f->next, *p); - - fs_give((void **)&(f->line)); /* free temp line buffer */ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset escape\n")); - f->f1 = 0; - f->n = 0L; - f->linep = f->line = (char *)fs_get((MAX_ESC_LEN + 1) * sizeof(char)); - } -} - - - -/* - * CONTROL CHARACTER FILTER - transmogrify control characters into their - * corresponding string representations (you know, ^blah and such)... - */ - -/* - * the simple filter transforms unknown control characters in the stream - * into harmless strings. - */ -void -gf_control_filter(f, flg) - FILTER_S *f; - int flg; -{ - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - - while(GF_GETC(f, c)){ - - if(iscntrl(c & 0x7f) - && !(isspace((unsigned char) c) - || c == '\016' || c == '\017' || c == '\033')){ - GF_PUTC(f->next, '^'); - GF_PUTC(f->next, c + '@'); - } - else - GF_PUTC(f->next, c); - } - - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } -} - - - -/* - * LINEWRAP FILTER - insert CRLF's at end of nearest whitespace before - * specified line width - */ - - -typedef struct wrap_col_s { - unsigned bold:1; - unsigned uline:1; - unsigned inverse:1; - unsigned tags:1; - unsigned do_indent:1; - unsigned on_comma:1; - unsigned quoted:1; - COLOR_PAIR *color; - short embedded, - space_len; - char *lineendp, - space; - int anchor; - int wrap_col, - wrap_max, - indent; - char special[256]; -} WRAP_S; - - -#define WRAP_COL(F) (((WRAP_S *)(F)->opt)->wrap_col) -#define WRAP_MAX_COL(F) (((WRAP_S *)(F)->opt)->wrap_max) -#define WRAP_INDENT(F) (((WRAP_S *)(F)->opt)->indent) -#define WRAP_DO_IND(F) (((WRAP_S *)(F)->opt)->do_indent) -#define WRAP_COMMA(F) (((WRAP_S *)(F)->opt)->on_comma) -#define WRAP_QUOTED(F) (((WRAP_S *)(F)->opt)->quoted) -#define WRAP_TAGS(F) (((WRAP_S *)(F)->opt)->tags) -#define WRAP_BOLD(F) (((WRAP_S *)(F)->opt)->bold) -#define WRAP_ULINE(F) (((WRAP_S *)(F)->opt)->uline) -#define WRAP_INVERSE(F) (((WRAP_S *)(F)->opt)->inverse) -#define WRAP_COLOR(F) (((WRAP_S *)(F)->opt)->color) -#define WRAP_LASTC(F) (((WRAP_S *)(F)->opt)->lineendp) -#define WRAP_EMBED(F) (((WRAP_S *)(F)->opt)->embedded) -#define WRAP_ANCHOR(F) (((WRAP_S *)(F)->opt)->anchor) -#define WRAP_SPACE(F) (((WRAP_S *)(F)->opt)->space) -#define WRAP_SPACES(F) (WRAP_SPACE(F) ? (((WRAP_S *)(F)->opt)->space_len): 0) -#define WRAP_SPC_LEN(F) (((WRAP_S *)(F)->opt)->space_len) -#define WRAP_SPEC(S, C) (S)[C] - -#define WRAP_EOL(F) { \ - if(WRAP_BOLD(F)){ \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_BOLDOFF); \ - } \ - if(WRAP_ULINE(F)){ \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_ULINEOFF); \ - } \ - if(WRAP_INVERSE(F) || WRAP_ANCHOR(F)){ \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_INVOFF); \ - } \ - if(WRAP_COLOR(F)){ \ - char *p; \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_FGCOLOR); \ - p = color_to_asciirgb(ps_global->VAR_NORM_FORE_COLOR);\ - for(; *p; p++) \ - GF_PUTC((F)->next, *p); \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_BGCOLOR); \ - p = color_to_asciirgb(ps_global->VAR_NORM_BACK_COLOR);\ - for(; *p; p++) \ - GF_PUTC((F)->next, *p); \ - } \ - GF_PUTC((F)->next, '\015'); \ - GF_PUTC((F)->next, '\012'); \ - f->n = 0L; \ - WRAP_SPACE(F) = 0; \ - } - -#define WRAP_BOL(F) { \ - if(WRAP_BOLD(F)){ \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_BOLDON); \ - } \ - if(WRAP_ULINE(F)){ \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_ULINEON); \ - } \ - if(WRAP_INVERSE(F)){ \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_INVON); \ - } \ - if(WRAP_COLOR(F)){ \ - char *p; \ - if(WRAP_COLOR(F)->fg[0]){ \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_FGCOLOR); \ - p = color_to_asciirgb(WRAP_COLOR(F)->fg);\ - for(; *p; p++) \ - GF_PUTC((F)->next, *p); \ - } \ - if(WRAP_COLOR(F)->bg[0]){ \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_BGCOLOR); \ - p = color_to_asciirgb(WRAP_COLOR(F)->bg);\ - for(; *p; p++) \ - GF_PUTC((F)->next, *p); \ - } \ - } \ - if(WRAP_ANCHOR(F)){ \ - char buf[64]; int i; \ - GF_PUTC((F)->next, TAG_EMBED); \ - GF_PUTC((F)->next, TAG_HANDLE); \ - sprintf(buf, "%d", WRAP_ANCHOR(F)); \ - GF_PUTC((F)->next, (int) strlen(buf)); \ - for(i = 0; buf[i]; i++) \ - GF_PUTC((F)->next, buf[i]); \ - } \ - } - -#define WRAP_PUTC(F,C) { \ - if((F)->linep == WRAP_LASTC(F)){ \ - size_t offset = (F)->linep - (F)->line; \ - fs_resize((void **) &(F)->line, \ - (2 * offset) * sizeof(char)); \ - (F)->linep = &(F)->line[offset]; \ - WRAP_LASTC(F) = &(F)->line[2*offset-1]; \ - } \ - *(F)->linep++ = (C); \ - } - -#define WRAP_FLUSH(F) { \ - register char *s = f->line; \ - register int n; \ - if(!f->n){ \ - if(WRAP_DO_IND(F)){ \ - if(f->n = (long)(n = WRAP_INDENT(F))) \ - while(n-- > 0) \ - GF_PUTC((F)->next, ' '); \ - WRAP_DO_IND(F) = 0; \ - } \ - WRAP_BOL(F); \ - } \ - if(WRAP_SPACE(F)){ \ - GF_PUTC(f->next, WRAP_SPACE(F)); \ - WRAP_SPACE(F) = 0; \ - f->n += WRAP_SPC_LEN(F); \ - } \ - for(n = f->linep - f->line; n > 0; n--){ \ - if(*s == TAG_EMBED){ \ - if(n-- > 0){ \ - GF_PUTC(f->next, TAG_EMBED); \ - switch(*++s){ \ - case TAG_BOLDON : \ - WRAP_BOLD(f) = 1; \ - break; \ - case TAG_BOLDOFF : \ - WRAP_BOLD(f) = 0; \ - break; \ - case TAG_ULINEON : \ - WRAP_ULINE(f) = 1; \ - break; \ - case TAG_ULINEOFF : \ - WRAP_ULINE(f) = 0; \ - break; \ - case TAG_INVOFF : \ - WRAP_ANCHOR(f) = 0; \ - break; \ - case TAG_HANDLE : \ - if(n-- > 0){ \ - int i = *++s; \ - GF_PUTC(f->next, TAG_HANDLE); \ - if(i <= n){ \ - n -= i; \ - GF_PUTC(f->next, i); \ - WRAP_ANCHOR(f) = 0; \ - while(1){ \ - WRAP_ANCHOR(f) \ - = (WRAP_ANCHOR(f) \ - * 10) \ - + (*++s-'0');\ - if(--i) \ - GF_PUTC(f->next,*s);\ - else \ - break; \ - } \ - } \ - } \ - break; \ - case TAG_FGCOLOR : \ - if(pico_usingcolor() \ - && n >= RGBLEN){ \ - if(!WRAP_COLOR(f)) \ - WRAP_COLOR(f)=new_color_pair(NULL,NULL); \ - strncpy(WRAP_COLOR(f)->fg, \ - s+1, RGBLEN); \ - WRAP_COLOR(f)->fg[RGBLEN]='\0'; \ - i = RGBLEN; \ - n -= i; \ - while(i-- > 0) \ - GF_PUTC(f->next, \ - (*s++) & 0xff); \ - } \ - break; \ - case TAG_BGCOLOR : \ - if(pico_usingcolor() \ - && n >= RGBLEN){ \ - if(!WRAP_COLOR(f)) \ - WRAP_COLOR(f)=new_color_pair(NULL,NULL); \ - strncpy(WRAP_COLOR(f)->bg,\ - s+1, RGBLEN); \ - WRAP_COLOR(f)->bg[RGBLEN]='\0'; \ - i = RGBLEN; \ - n -= i; \ - while(i-- > 0) \ - GF_PUTC(f->next, \ - (*s++) & 0xff); \ - } \ - break; \ - default : \ - break; \ - } \ - } \ - } \ - else \ - f->n++; \ - GF_PUTC(f->next, (*s++) & 0xff); \ - } \ - f->f2 = 0; \ - f->linep = f->line; \ - } - - - - -/* - * the simple filter, breaks lines at end of white space nearest - * to global "gf_wrap_width" in length - */ -void -gf_wrap(f, flg) - FILTER_S *f; - int flg; -{ - register long i; - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - int wrap_threshold = WRAP_MAX_COL(f) - WRAP_INDENT(f); - char *special = ((WRAP_S *) f->opt)->special; - - while(GF_GETC(f, c)){ - - switch(state){ - case CCR : - state = DFL; /* CRLF or CR in text ? */ - WRAP_FLUSH(f); /* it's a newline to us */ - WRAP_EOL(f); - WRAP_BOL(f); - if(c == '\012') /* process anything but LF */ - break; - - case DFL : - if(WRAP_SPEC(special, c)) - switch(c){ - default : - if(WRAP_QUOTED(f)) - break; - - WRAP_FLUSH(f); /* flush buf */ - switch(WRAP_SPACE(f) = c){ /* remember separator */ - case ' ' : - WRAP_SPC_LEN(f) = 1; - break; - - case TAB : - { - int i = (int) f->n; - while(++i & 0x07) - ; - - WRAP_SPC_LEN(f) = i - (int) f->n; - } - - break; - - default : /* some control char? */ - WRAP_SPC_LEN(f) = 2; - break; - } - - continue; - - case '\"' : - WRAP_QUOTED(f) = !WRAP_QUOTED(f); - break; - - case '\015' : /* already has newline? */ - state = CCR; - continue; - - case '\012' : /* bare LF in text? */ - WRAP_FLUSH(f); /* they must've meant */ - WRAP_EOL(f); /* newline... */ - WRAP_BOL(f); - continue; - - case (unsigned char) TAG_EMBED : - WRAP_PUTC(f, TAG_EMBED); - state = TAG; - continue; - - case ',' : - if(!WRAP_QUOTED(f)){ - WRAP_PUTC(f, ','); - WRAP_FLUSH(f); - WRAP_SPACE(f) = 0; - continue; - } - - break; - } - - if(!f->n && (f->f2 >= wrap_threshold)){ - char *ep, *space = NULL; - int cntr, new_f2; - - /* Flush the buf'd line up to last WS */ - if(WRAP_COMMA(f)){ - char *p; - - new_f2 = cntr = f->f2; - for(p = f->line; p < f->linep; p++){ - /* - * Don't split at WS which is in the middle - * of a tag. - */ - switch((unsigned char)*p){ - case (unsigned char)TAG_EMBED: - if(++p >= f->linep) - break; - - switch(*p){ - case TAG_FGCOLOR: - case TAG_BGCOLOR: - p += RGBLEN; - break; - - case TAG_HANDLE: - if(++p >= f->linep) - break; - - p += (*p); - break; - - default: - break; - } - - continue; - break; - - default: - break; - } - - cntr--; - - if(p < f->linep && isspace((unsigned char)*p)){ - space = p; - new_f2 = cntr; - } - } - - if(space){ - ep = f->linep; - f->linep = space; - } - } - - WRAP_FLUSH(f); /* write buffered */ - WRAP_EOL(f); /* write end of line */ - WRAP_DO_IND(f) = 1; /* indent next line */ - - if(space){ - f->linep = f->line; - while(++space < ep) - *f->linep++ = *space; - - f->f2 = new_f2; - } - } - - WRAP_PUTC(f, c); - - if(c == '\t' && WRAP_COMMA(f)){ - int i = (int) f->n; - while(++i & 0x07) - ; - - f->f2 += i - (int) f->n; - } - else - f->f2++; - - if(f->n && (f->n + f->f2 + WRAP_SPACES(f) >= WRAP_COL(f))){ - WRAP_EOL(f); /* write end of line */ - WRAP_DO_IND(f) = 1; /* indent next line */ - } - - break; - - case TAG : - WRAP_PUTC(f, c); - switch(c){ - case TAG_HANDLE : - WRAP_EMBED(f) = -1; - state = HANDLE; - break; - - case TAG_FGCOLOR : - case TAG_BGCOLOR : - WRAP_EMBED(f) = RGBLEN; - state = HDATA; - break; - - default : - state = DFL; - break; - } - - break; - - case HANDLE : - WRAP_PUTC(f, c); - WRAP_EMBED(f) = c; - state = HDATA; - break; - - case HDATA : - WRAP_PUTC(f, c); - if(!(WRAP_EMBED(f) -= 1)) - state = DFL; - - break; - } - } - - f->f1 = state; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - WRAP_FLUSH(f); - if(WRAP_COLOR(f)) - free_color_pair(&WRAP_COLOR(f)); - - fs_give((void **) &f->line); /* free temp line buffer */ - fs_give((void **) &f->opt); /* free wrap widths struct */ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset wrap\n")); - f->f1 = DFL; - f->n = 0L; /* displayed length of line so far */ - f->f2 = 0; /* displayed length of buffered chars */ - if(! (WRAP_S *) f->opt) - f->opt = gf_wrap_filter_opt(75, 80, 0, 0); - - while(WRAP_INDENT(f) >= WRAP_MAX_COL(f)) - WRAP_INDENT(f) /= 2; - - f->line = (char *) fs_get(WRAP_MAX_COL(f) * sizeof(char)); - f->linep = f->line; - WRAP_LASTC(f) = &f->line[WRAP_MAX_COL(f) - 1]; - - for(i = 0; i < 256; i++) - ((WRAP_S *) f->opt)->special[i] = ((i == '\"' && WRAP_COMMA(f)) - || i == '\015' - || i == '\012' - || (i == (unsigned char) TAG_EMBED - && WRAP_TAGS(f)) - || (i == ',' && WRAP_COMMA(f) - && !WRAP_QUOTED(f)) - || (!WRAP_COMMA(f) - && isspace((unsigned char) i))); - } -} - - -/* - * function called from the outside to set - * wrap filter's width option - */ -void * -gf_wrap_filter_opt(width, width_max, indent, flags) - int width, width_max, indent, flags; -{ - WRAP_S *wrap; - - wrap = (WRAP_S *) fs_get(sizeof(WRAP_S)); - memset(wrap, 0, sizeof(WRAP_S)); - wrap->wrap_col = width; - wrap->wrap_max = width_max; - wrap->indent = indent; - wrap->tags = (GFW_HANDLES & flags) == GFW_HANDLES; - wrap->on_comma = (GFW_ONCOMMA & flags) == GFW_ONCOMMA; - return((void *) wrap); -} - - - - -/* - * LINE PREFIX FILTER - insert given text at beginning of each - * line - */ - - -#define GF_PREFIX_WRITE(s) { \ - register char *p; \ - if(p = (s)) \ - while(*p) \ - GF_PUTC(f->next, *p++); \ - } - - -/* - * the simple filter, prepends each line with the requested prefix. - * if prefix is null, does nothing, and as with all filters, assumes - * NVT end of lines. - */ -void -gf_prefix(f, flg) - FILTER_S *f; - int flg; -{ - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - register int first = f->f2; - - while(GF_GETC(f, c)){ - - if(first){ /* write initial prefix!! */ - first = 0; /* but just once */ - GF_PREFIX_WRITE((char *) f->opt); - } - else if(state){ - state = 0; - GF_PUTC(f->next, '\015'); - if(c == '\012'){ - GF_PUTC(f->next, '\012'); - GF_PREFIX_WRITE((char *) f->opt); - continue; - } - /* else fall thru to handle 'c' */ - } - - if(c == '\015') /* already has newline? */ - state = 1; - else - GF_PUTC(f->next, c); - } - - f->f1 = state; - f->f2 = first; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset prefix\n")); - f->f1 = 0; - f->f2 = 1; /* nothing written yet */ - } -} - - -/* - * function called from the outside to set - * prefix filter's prefix string - */ -void * -gf_prefix_opt(prefix) - char *prefix; -{ - return((void *) prefix); -} - - -/* - * LINE TEST FILTER - accumulate lines and offer each to the provided - * test function. - */ - -typedef struct _linetest_s { - linetest_t f; - void *local; -} LINETEST_S; - - -/* accumulator growth increment */ -#define LINE_TEST_BLOCK 1024 - -#define GF_LINE_TEST_EOB(f) \ - ((f)->line + ((f)->f2 - 1)) - -#define GF_LINE_TEST_ADD(f, c) \ - { \ - if(p >= eobuf){ \ - f->f2 += LINE_TEST_BLOCK; \ - fs_resize((void **)&f->line, \ - (size_t) f->f2 * sizeof(char)); \ - eobuf = GF_LINE_TEST_EOB(f); \ - p = eobuf - LINE_TEST_BLOCK; \ - } \ - *p++ = c; \ - } - -#define GF_LINE_TEST_TEST(F, D) \ - { \ - unsigned char c; \ - register char *cp; \ - register int l; \ - LT_INS_S *ins = NULL, *insp; \ - *p = '\0'; \ - (D) = (*((LINETEST_S *) (F)->opt)->f)((F)->n++, \ - (F)->line, &ins, \ - ((LINETEST_S *) (F)->opt)->local); \ - if((D) < 2){ \ - for(insp = ins, cp = (F)->line; cp < p; ){ \ - while(insp && cp == insp->where){ \ - for(l = 0; l < insp->len; l++){ \ - c = (unsigned char) insp->text[l];\ - GF_PUTC((F)->next, c); \ - } \ - insp = insp->next; \ - } \ - GF_PUTC((F)->next, *cp); \ - cp++; \ - } \ - while(insp){ \ - for(l = 0; l < insp->len; l++){ \ - c = (unsigned char) insp->text[l]; \ - GF_PUTC((F)->next, c); \ - } \ - insp = insp->next; \ - } \ - gf_line_test_free_ins(&ins); \ - } \ - } - - - -/* - * this simple filter accumulates characters until a newline, offers it - * to the provided test function, and then passes it on. It assumes - * NVT EOLs. - */ -void -gf_line_test(f, flg) - FILTER_S *f; - int flg; -{ - register char *p = f->linep; - register char *eobuf = GF_LINE_TEST_EOB(f); - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - - while(GF_GETC(f, c)){ - - if(state){ - state = 0; - if(c == '\012'){ - int done; - - GF_LINE_TEST_TEST(f, done); - - p = (f)->line; - - if(done == 2) /* skip this line! */ - continue; - - GF_PUTC(f->next, '\015'); - GF_PUTC(f->next, '\012'); - /* - * if the line tester returns TRUE, it's - * telling us its seen enough and doesn't - * want to see any more. Remove ourself - * from the pipeline... - */ - if(done){ - if(gf_master == f){ - gf_master = f->next; - } - else{ - FILTER_S *fprev; - - for(fprev = gf_master; - fprev && fprev->next != f; - fprev = fprev->next) - ; - - if(fprev) /* wha??? */ - fprev->next = f->next; - else - continue; - } - - while(GF_GETC(f, c)) /* pass input */ - GF_PUTC(f->next, c); - - GF_FLUSH(f->next); /* and drain queue */ - fs_give((void **)&f->line); - fs_give((void **)&f); /* wax our data */ - return; - } - else - continue; - } - else /* add CR to buffer */ - GF_LINE_TEST_ADD(f, '\015'); - } /* fall thru to handle 'c' */ - - if(c == '\015') /* newline? */ - state = 1; - else - GF_LINE_TEST_ADD(f, c); - } - - f->f1 = state; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - int i; - - GF_LINE_TEST_TEST(f, i); /* examine remaining data */ - fs_give((void **) &f->line); /* free line buffer */ - fs_give((void **) &f->opt); /* free test struct */ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset line_test\n")); - f->f1 = 0; /* state */ - f->n = 0L; /* line number */ - f->f2 = LINE_TEST_BLOCK; /* size of alloc'd line */ - f->line = p = (char *) fs_get(f->f2 * sizeof(char)); - } - - f->linep = p; -} - - -/* - * function called from the outside to operate on accumulated line. - */ -void * -gf_line_test_opt(test_f, local) - linetest_t test_f; - void *local; -{ - LINETEST_S *ltp; - - ltp = (LINETEST_S *) fs_get(sizeof(LINETEST_S)); - memset(ltp, 0, sizeof(LINETEST_S)); - ltp->f = test_f; - ltp->local = local; - return((void *) ltp); -} - - - -LT_INS_S ** -gf_line_test_new_ins(ins, p, s, n) - LT_INS_S **ins; - char *p, *s; - int n; -{ - *ins = (LT_INS_S *) fs_get(sizeof(LT_INS_S)); - if((*ins)->len = n) - strncpy((*ins)->text = (char *) fs_get(n * sizeof(char)), s, n); - - (*ins)->where = p; - (*ins)->next = NULL; - return(&(*ins)->next); -} - - -void -gf_line_test_free_ins(ins) - LT_INS_S **ins; -{ - if(ins && *ins){ - if((*ins)->next) - gf_line_test_free_ins(&(*ins)->next); - - if((*ins)->text) - fs_give((void **) &(*ins)->text); - - fs_give((void **) ins); - } -} - - -/* - * Network virtual terminal to local newline convention filter - */ -void -gf_nvtnl_local(f, flg) - FILTER_S *f; - int flg; -{ - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - register int state = f->f1; - - while(GF_GETC(f, c)){ - if(state){ - state = 0; - if(c == '\012'){ - GF_PUTC(f->next, '\012'); - continue; - } - else - GF_PUTC(f->next, '\015'); - /* fall thru to deal with 'c' */ - } - - if(c == '\015') - state = 1; - else - GF_PUTC(f->next, c); - } - - f->f1 = state; - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset nvtnl_local\n")); - f->f1 = 0; - } -} - - -/* - * local to network newline convention filter - */ -void -gf_local_nvtnl(f, flg) - FILTER_S *f; - int flg; -{ - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - - while(GF_GETC(f, c)){ - if(c == '\012'){ - GF_PUTC(f->next, '\015'); - GF_PUTC(f->next, '\012'); - } - else - GF_PUTC(f->next, c); - } - - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(GF_RESET){ - dprint(9, (debugfile, "-- gf_reset local_nvtnl\n")); - /* no op */ - } - -} - -#if defined(DOS) || defined(OS2) -/* - * DOS CodePage to Character Set Translation (and back) filters - */ - -/* - * Charset and CodePage mapping table pointer and length - */ -static unsigned char *gf_xlate_tab; -static unsigned gf_xlate_tab_len; - -/* - * the simple filter takes DOS Code Page values and maps them into - * the indicated external CharSet mapping or vice-versa. - */ -void -gf_translate(f, flg) - FILTER_S *f; - int flg; -{ - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - - while(GF_GETC(f, c)) - if((unsigned long) c < ((SIZEDTEXT *) (f->opt))->size) - GF_PUTC(f->next, (int) ((SIZEDTEXT *) (f->opt))->data[c]); - - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - fs_give((void **) &f->opt); /* free up table description */ - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(GF_RESET){ - dprint(9, (debugfile, "-- gf_reset translate\n")); - } -} - - -/* - * function called from the outside to set - * prefix filter's prefix string - */ -void * -gf_translate_opt(xlatetab, xlatetablen) - unsigned char *xlatetab; - unsigned xlatetablen; -{ - SIZEDTEXT *xlate_tab = (SIZEDTEXT *) fs_get(sizeof(SIZEDTEXT)); - - xlate_tab->data = xlatetab; - xlate_tab->size = (unsigned long) xlatetablen; - - return((void *) xlate_tab); -} -#endif - -/* - * display something indicating we're chewing on something - * - * NOTE : IF ANY OTHER FILTERS WRITE THE DISPLAY, THIS WILL NEED FIXING - */ -void -gf_busy(f, flg) - FILTER_S *f; - int flg; -{ - static short x = 0; - GF_INIT(f, f->next); - - if(flg == GF_DATA){ - register unsigned char c; - - while(GF_GETC(f, c)){ - - if(!((++(f->f1))&0x7ff)){ /* ding the bell every 2K chars */ - MoveCursor(0, 1); - f->f1 = 0; - if((++x)&0x04) x = 0; - Writechar((x == 0) ? '/' : /* CHEATING! */ - (x == 1) ? '-' : - (x == 2) ? '\\' : '|', 0); - } - - GF_PUTC(f->next, c); - } - - GF_END(f, f->next); - } - else if(flg == GF_EOD){ - MoveCursor(0, 1); - Writechar(' ', 0); - EndInverse(); - GF_FLUSH(f->next); - (*f->next->f)(f->next, GF_EOD); - } - else if(flg == GF_RESET){ - dprint(9, (debugfile, "-- gf_reset busy\n")); - f->f1 = 0; - x = 0; - StartInverse(); - } - - fflush(stdout); -} diff --git a/net-mail/pine/files/os.c b/net-mail/pine/files/os.c deleted file mode 100644 index 2fd967904207..000000000000 --- a/net-mail/pine/files/os.c +++ /dev/null @@ -1,5673 +0,0 @@ -/*---------------------------------------------------------------------- - - 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, "Can only open local folders"); - 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, - "\"..\" not allowed in folder name"); - 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, - "Error expanding folder name: \"%s\" unknown user", - 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, fatal) - char *file; - int *fatal; -{ -#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++; - - *fatal = TRUE; - if(*ptr != '\0') { - if(*ptr == '\n') { - ill_char = "<newline>"; - } else if(*ptr == '\r') { - ill_char = "<carriage return>"; - } else if(*ptr == '\t') { - ill_char = "<tab>"; - *fatal = FALSE; /* just whitespace */ - } 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; - *fatal = FALSE; /* less offensive? */ - } - if(!*fatal){ - strcpy(error, ill_char); - } - else 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){ - char *p; - - for(p = name; *p; p++) - if(isupper((unsigned char)*p)) - break; - - /* try changing it to all lower case */ - if(p && *p){ - char *lcase; - - lcase = cpystr(name); - for(p = lcase; *p; p++) - if(isupper((unsigned char)*p)) - *p = tolower((unsigned char)*p); - - pw = getpwnam(lcase); - - if(pw) - strcpy(name, lcase); - - fs_give((void **)&lcase); - } - } - - 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)); -} - - - -/*====================================================================== - 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; -int child_pid; - - -/* - * Internal Protos - */ -void pipe_error_cleanup PROTO((PIPE_S **, char *, char *, char *)); -void zot_pipe PROTO((PIPE_S **)); -static SigType pipe_alarm SIG_PROTO((int)); - - - - -/*---------------------------------------------------------------------- - 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, timeout) - char *command; - char **outfile, **errfile; - int mode, timeout; -{ - 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((child_pid = 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((syspipe->timeout = timeout) != 0){ - syspipe->alrm = signal(SIGALRM, pipe_alarm); - syspipe->old_timeo = alarm(timeout); - } - - 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); - 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)->timeout){ - (void)signal(SIGALRM, (*syspipe)->alrm); - alarm((*syspipe)->old_timeo); - child_pid = 0; - } - - 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); -} - - - -static SigType -pipe_alarm SIG_PROTO((int sig)) -{ - if(child_pid) - kill(child_pid, SIGINT); -} - -/*====================================================================== - 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, 0)){ - 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, 0)){ - close_system_pipe(&syspipe); - if(needsterminal == 1) - q_status_message(SM_ORDER, 0, 4, "VIEWER command completed"); - 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, "Cannot spawn command : %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, 0)) - ? 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", "Yes"}, - {'n', 'n', "N", "No"}, - {ctrl('P'), 10, "^P", "Prev Printer"}, - {ctrl('N'), 11, "^N", "Next Printer"}, - {-2, 0, NULL, NULL}, - {'c', 'c', "C", "CustomPrint"}, - {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, - "No printer has been chosen. Use SETUP on main menu to make choice."); - return(-1); - } - - /* Is there just one print command available? */ - just_one = (ps_global->printer_category!=3&&ps_global->printer_category!=2) - || (ps_global->printer_category == 2 - && !(ps_global->VAR_STANDARD_PRINTER - && ps_global->VAR_STANDARD_PRINTER[0] - && ps_global->VAR_STANDARD_PRINTER[1])) - || (ps_global->printer_category == 3 - && !(ps_global->VAR_PERSONAL_PRINT_COMMAND - && ps_global->VAR_PERSONAL_PRINT_COMMAND[0] - && ps_global->VAR_PERSONAL_PRINT_COMMAND[1])); - - if(F_ON(F_CUSTOM_PRINT, ps_global)) - ekey[CUSTOM_KEY].ch = 'c'; /* turn this key on */ - else - ekey[CUSTOM_KEY].ch = -2; /* turn this key off */ - - if(just_one){ - ekey[PREV_KEY].ch = -2; /* turn these keys off */ - ekey[NEXT_KEY].ch = -2; - ekey[UP_KEY].ch = -2; - ekey[DOWN_KEY].ch = -2; - } - else{ - ekey[PREV_KEY].ch = ctrl('P'); /* turn these keys on */ - ekey[NEXT_KEY].ch = ctrl('N'); - ekey[UP_KEY].ch = KEY_UP; - ekey[DOWN_KEY].ch = KEY_DOWN; - /* - * count how many printers in list and find the default in the list - */ - if(ps_global->printer_category == 2) - list = ps_global->VAR_STANDARD_PRINTER; - else - list = ps_global->VAR_PERSONAL_PRINT_COMMAND; - - for(i = 0; list[i]; i++) - if(strcmp(ps_global->VAR_PRINTER, list[i]) == 0) - cur_printer = i; - - lastprinter = i - 1; - } - - help = NO_HELP; - ps_global->mangled_footer = 1; - - while(!done){ - if(init) - fs_give((void **)&init); - - if(trailer) - fs_give((void **)&trailer); - - if(just_one) - printer = ps_global->VAR_PRINTER; - else - printer = list[cur_printer]; - - parse_printer(printer, &nick, &p, &init, &trailer, NULL, NULL); - strncpy(command, p, 200); - fs_give((void **)&p); - sprintf(prompt, "Print %.50s%susing \"%.50s\" ? ", - desc ? desc : "", - (desc && *desc && desc[strlen(desc) - 1] != ' ') ? " " : "", - *nick ? nick : command); - - fs_give((void **)&nick); - - cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), - ekey, 'y', 'x', help, RB_NORM); - - switch(cmd){ - case 'y': - q_status_message1(SM_ORDER, 0, 9, - "Printing with command \"%s\"", command); - done++; - break; - - case 10: - cur_printer = (cur_printer>0) - ? (cur_printer-1) - : lastprinter; - break; - - case 11: - cur_printer = (cur_printer<lastprinter) - ? (cur_printer+1) - : 0; - break; - - case 'n': - case 'x': - done++; - break; - - case 'c': - done++; - break; - - default: - break; - } - } - - if(cmd == 'c'){ - if(init) - fs_give((void **)&init); - - if(trailer) - fs_give((void **)&trailer); - - sprintf(prompt, "Enter custom command : "); - 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, - "Printing with command \"%s\"", command); - } - } - } - - if(cmd == 'x' || cmd == 'n'){ - q_status_message(SM_ORDER, 0, 2, "Print cancelled"); - 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, - "Printing to attached desktop printer..."); - 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, 0)){ - 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, - "Error opening printer: %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, "Print command completed"); - 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 - */ -int pine_simple_ttgetc PROTO((int (*)(), void (*)())); -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){ - if(setjmp(winch_state) != 0){ - winch_occured = 1; - ready_for_winch = 0; - - /* - * Need to unblock signal after longjmp from handler, because - * signal is normally unblocked upon routine exit from the handler. - */ - our_sigunblock(SIGWINCH); - } - else - ready_for_winch = 1; - } - - if(winch_occured){ - winch_occured = ready_for_winch = 0; - fix_windsize(ps_global); - return(KEY_RESIZE); - } -#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(pine_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); -} - - -int -pine_simple_ttgetc(fi, fv) - int (*fi)(); - void (*fv)(); -{ - int ch; - -#ifdef RESIZING - if(!winch_occured){ - if(setjmp(winch_state) != 0){ - winch_occured = 1; - ready_for_winch = 0; - - /* - * Need to unblock signal after longjmp from handler, because - * signal is normally unblocked upon routine exit from the handler. - */ - our_sigunblock(SIGWINCH); - } - else - ready_for_winch = 1; - } - - if(winch_occured){ - winch_occured = ready_for_winch = 0; - fix_windsize(ps_global); - return(KEY_RESIZE); - } -#endif /* RESIZING */ - - ch = simple_ttgetc(fi, fv); - -#ifdef RESIZING - ready_for_winch = 0; -#endif - - return(ch); -} - - - -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" - -static int g_mc_row, g_mc_col; - -int pcpine_oe_cursor PROTO((int, long)); -#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_message3(SM_ORDER, 3, 3, - "%s message%s expunged from folder \"%s\"", - long2string(ps_global->expunge_count), - plural(ps_global->expunge_count), - pretty_fn(ps_global->cur_folder)); - ps_global->expunge_count = 0L; - display_message('x'); - } - - if(ps_global->inbox_expunge_count){ - q_status_message3(SM_ORDER, 3, 3, - "%s message%s expunged from folder \"%s\"", - long2string(ps_global->inbox_expunge_count), - plural(ps_global->inbox_expunge_count), - pretty_fn(ps_global->inbox_name)); - 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, "Abysmal failure!"); - } - else{ - (void) pine_send_status(ps_global->post->status, - ps_global->post->fcc, tmp_20k_buf, - &winner); - q_status_message(SM_ORDER | (winner ? 0 : SM_DING), 3, 3, - tmp_20k_buf); - - } - - if(!winner) - q_status_message(SM_ORDER, 0, 3, - "Re-send via \"Compose\" then \"Yes\" to \"Continue INTERRUPTED?\""); - - if(ps_global->post->fcc) - fs_give((void **) &ps_global->post->fcc); - - fs_give((void **) &ps_global->post); - } -#endif - - return(ch); -} - - - - -/* - * - */ -static struct display_line { - int row, col; /* where display starts */ - int dlen; /* length of display line */ - char *dl; /* line on display */ - char *vl; /* virtual line */ - int vlen; /* length of virtual line */ - int vused; /* length of virtual line in use */ - int vbase; /* first virtual char on display */ -} dline; - - - -static struct key oe_keys[] = - {{"^G","Help",KS_SCREENHELP}, {"^C","Cancel",KS_NONE}, - {"^T","xxx",KS_NONE}, {"Ret","Accept",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; - COLOR_PAIR *lastc = NULL, *promptc = NULL; - struct variable *vars = ps_global->vars; -#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); - } - - if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR && - VAR_PROMPT_BACK_COLOR && - pico_is_good_color(VAR_PROMPT_FORE_COLOR) && - pico_is_good_color(VAR_PROMPT_BACK_COLOR)){ - lastc = pico_get_cur_color(); - if(lastc){ - promptc = new_color_pair(VAR_PROMPT_FORE_COLOR, - VAR_PROMPT_BACK_COLOR); - (void)pico_set_colorp(promptc, PSC_NONE); - } - } - else - StartInverse(); - - /* - * 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_showcaret(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; - - while(return_v == -10) { - -#ifdef MOUSE - mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0); - register_mfunc(mouse_in_content, - real_y_base, x_base + prompt_len, - real_y_base, ps_global->ttyo->screen_cols); -#endif -#ifdef _WINDOWS - mswin_allowpaste(MSWIN_PASTE_LINE); - g_mc_row = real_y_base; - g_mc_col = x_base + prompt_len; - mswin_mousetrackcallback(pcpine_oe_cursor); -#endif - - /* Timeout 10 min to keep imap mail stream alive */ - ch = read_char(600); - -#ifdef MOUSE - clear_mfunc(mouse_in_content); -#endif -#ifdef _WINDOWS - mswin_allowpaste(MSWIN_PASTE_DISABLE); - mswin_mousetrackcallback(NULL); -#endif - - /* - * 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); - if(lastc) - (void)pico_set_colorp(lastc, PSC_NONE); - else - EndInverse(); - - draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global), - 0, FirstMenu); - - if(promptc) - (void)pico_set_colorp(promptc, PSC_NONE); - else - 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; - -#ifdef MOUSE - case KEY_MOUSE : - { - MOUSEPRESS mp; - - mouse_get_last (NULL, &mp); - - /* The clicked line have anything special on it? */ - switch(mp.button){ - case M_BUTTON_LEFT : /* position cursor */ - mp.col -= x_base + prompt_len; /* normalize column */ - if(dline.vbase + mp.col <= dline.vused) - line_paint(field_pos = dline.vbase + mp.col, &passwd); - - break; - - case M_BUTTON_RIGHT : -#ifdef _WINDOWS - mp.col -= x_base + prompt_len; /* normalize column */ - if(dline.vbase + mp.col <= dline.vused) - line_paint(field_pos = dline.vbase + mp.col, &passwd); - - mswin_allowpaste(MSWIN_PASTE_LINE); - mswin_paste_popup(); - mswin_allowpaste(MSWIN_PASTE_DISABLE); - break; -#endif - - case M_BUTTON_MIDDLE : /* NO-OP for now */ - default: /* just ignore */ - break; - } - } - - break; -#endif - - 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){ - line_paint(field_pos, &passwd); - 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; - if(lastc) - (void)pico_set_colorp(lastc, PSC_NONE); - else - EndInverse(); - - ClearScreen(); - redraw_titlebar(); - if(ps_global->redrawer != (void (*)())NULL) - (*ps_global->redrawer)(); - - redraw_keymenu(); - if(promptc) - (void)pico_set_colorp(promptc, PSC_NONE); - else - 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_showcaret(0); -#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); - - if(lastc){ - (void)pico_set_colorp(lastc, PSC_NONE); - free_color_pair(&lastc); - if(promptc) - free_color_pair(&promptc); - } - else - 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); -} - - -#ifdef _WINDOWS -int -pcpine_oe_cursor(col, row) - int col; - long row; -{ - return((row == g_mc_row - && col >= g_mc_col - && col < ps_global->ttyo->screen_cols) - ? MSWIN_CURSOR_IBEAM - : MSWIN_CURSOR_ARROW); -} -#endif - -/*====================================================================== - 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, - *_cleartoeoln, *_cleartoeos, - *_startinsert, *_endinsert, *_insertchar, *_deletechar, - *_deleteline, *_insertline, - *_scrollregion, *_scrollup, *_scrolldown, - *_termcap_init, *_termcap_end; -extern char term_name[]; -extern int _tlines, _tcolumns, _bce; - -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); - - pico_toggle_color(0); - switch(ps_global->color_style){ - case COL_NONE: - case COL_TERMDEF: - pico_set_color_options(0); - break; - case COL_ANSI8: - pico_set_color_options(COLOR_ANSI8_OPT); - break; - case COL_ANSI16: - pico_set_color_options(COLOR_ANSI16_OPT); - break; - } - - if(ps_global->color_style != COL_NONE) - pico_toggle_color(1); - - /* set colors */ - if(pico_usingcolor()){ - if(ps_global->VAR_NORM_FORE_COLOR) - pico_nfcolor(ps_global->VAR_NORM_FORE_COLOR); - - if(ps_global->VAR_NORM_BACK_COLOR) - pico_nbcolor(ps_global->VAR_NORM_BACK_COLOR); - - if(ps_global->VAR_REV_FORE_COLOR) - pico_rfcolor(ps_global->VAR_REV_FORE_COLOR); - - if(ps_global->VAR_REV_BACK_COLOR) - pico_rbcolor(ps_global->VAR_REV_BACK_COLOR); - - pico_set_normal_color(); - } - - /* 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); - - /* unset colors */ - if(pico_hascolor()) - pico_endcolor(); - - 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 the terminal doesn't have back color erase, then we have to - * erase manually to preserve the background color. - */ - if(pico_usingcolor() && (!_bce || !_clearscreen)){ - ClearLines(0, _lines-1); - MoveCursor(0, 0); - } - else if(_clearscreen){ - 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); - -} - - - -/*---------------------------------------------------------------------- - 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 -- string to be output - - Result: String written to the screen - ----*/ -void -Write_to_screen(string) /* UNIX */ - register char *string; -{ - while(*string) - Writechar((unsigned char) *string++, 0); -} - - -/*---------------------------------------------------------------------- - Write no more than n chars of string to screen at current cursor position - - Args: string -- string to be output - n -- number of chars to output - - Result: String written to the screen - ----*/ -void -Write_to_screen_n(string, n) /* UNIX */ - register char *string; - int n; -{ - while(n-- && *string) - Writechar((unsigned char) *string++, 0); -} - - - -/*---------------------------------------------------------------------- - Clear screen to end of line on current line - - Result: Line is cleared - ----*/ -void -CleartoEOLN() -{ - int c, starting_col, starting_line; - char *last_bg_color; - - /* - * If the terminal doesn't have back color erase, then we have to - * erase manually to preserve the background color. - */ - if(pico_usingcolor() && (!_bce || !_cleartoeoln)){ - starting_col = _col; - starting_line = _line; - last_bg_color = pico_get_last_bg_color(); - pico_set_nbg_color(); - for(c = _col; c < _columns; c++) - Writechar(' ', 0); - - MoveCursor(starting_line, starting_col); - if(last_bg_color){ - (void)pico_set_bg_color(last_bg_color); - fs_give((void **)&last_bg_color); - } - } - else if(_cleartoeoln) - tputs(_cleartoeoln, 1, outchar); -} - - - -/*---------------------------------------------------------------------- - Clear screen to end of screen from current point - - Result: screen is cleared - ----*/ -CleartoEOS() -{ - int starting_col, starting_line; - - /* - * If the terminal doesn't have back color erase, then we have to - * erase manually to preserve the background color. - */ - if(pico_usingcolor() && (!_bce || !_cleartoeos)){ - starting_col = _col; - starting_line = _line; - CleartoEOLN(); - ClearLines(_line+1, _lines-1); - MoveCursor(starting_line, starting_col); - } - else if(_cleartoeos) - 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; -#ifdef _WINDOWS - int hkey = 0; -#endif - - 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 _WINDOWS - hkey = key; -#endif - - if(key == handles->key){ - if(pico_usingcolor() && - ps_global->VAR_SLCTBL_FORE_COLOR && - ps_global->VAR_SLCTBL_BACK_COLOR){ - pico_set_normal_color(); - } - else - EndBold(); - - StartInverse(); - } - } - else{ - /* BUG: complain? */ - line += *line + 1; - } - - break; - case TAG_FGCOLOR : - if(length < RGBLEN){ - Writechar(TAG_EMBED, 0); - Writechar(*(line-1), 0); - break; - } - - (void)pico_set_fg_color(line); - length -= RGBLEN; - line += RGBLEN; - break; - case TAG_BGCOLOR : - if(length < RGBLEN){ - Writechar(TAG_EMBED, 0); - Writechar(*(line-1), 0); - break; - } - - (void)pico_set_bg_color(line); - length -= RGBLEN; - line += RGBLEN; - 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); - } - - -#if _WINDOWS_X - if(hkey) { - char *tmp_file = NULL, ext[32], mtype[128]; - HANDLE_S *h; - extern HANDLE_S *get_handle (HANDLE_S *, int); - - if((h = get_handle(handles, hkey)) && h->type == Attach){ - ext[0] = '\0'; - strcpy(mtype, body_type_names(h->h.attach->body->type)); - if (h->h.attach->body->subtype) { - strcat (mtype, "/"); - strcat (mtype, h->h.attach->body->subtype); - } - - if(!set_mime_extension_by_type(ext, mtype)){ - char *namep, *dotp, *p; - - if(namep = rfc2231_get_param(h->h.attach->body->parameter, - "name", NULL, NULL)){ - for(dotp = NULL, p = namep; *p; p++) - if(*p == '.') - dotp = p + 1; - - if(dotp && strlen(dotp) < sizeof(ext) - 1) - strcpy(ext, dotp); - - fs_give((void **) &namep); - } - } - - if(ext[0] && (tmp_file = temp_nam_ext(NULL, "im", ext))){ - FILE *f = fopen(tmp_file, "w"); - - mswin_registericon(x, h->key, tmp_file); - - fclose(f); - unlink(tmp_file); - fs_give((void **)&tmp_file); - } - } - } -#endif -} - - -/*---------------------------------------------------------------------- - 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; -} - - |