Submitted By: BLFS Book Date: 2006-02-23 Initial Package Version: 4.64 Origin: http://www.suse.de/~bk/pine/4.64/2006-02-23/bigpatch.diff Upstream Status: Mostly applied. Description: Allows PINE UTF-8 and charset conversion $LastChangedBy: bdubbs $ $Date: 2006-05-16 13:25:45 -0600 (Tue, 16 May 2006) $ --- pine4.64/imap/src/c-client/imap4r1.c 2005-05-26 08:15:11.000000000 +0200 +++ pine4.64.SuSE/imap/src/c-client/imap4r1.c 2006-02-14 14:45:22.000000000 +0100 @@ -4395,6 +4395,7 @@ if (*env) { /* need to merge this header into envelope? */ if (!(*env)->newsgroups) { /* need Newsgroups? */ (*env)->newsgroups = nenv->newsgroups; + (*env)->ngpathexists = nenv->ngpathexists; nenv->newsgroups = NIL; } if (!(*env)->followup_to) { /* need Followup-To? */ @@ -4449,6 +4450,7 @@ if (oenv) { /* need to merge old envelope? */ (*env)->newsgroups = oenv->newsgroups; oenv->newsgroups = NIL; + (*env)->ngpathexists = oenv->ngpathexists; (*env)->followup_to = oenv->followup_to; oenv->followup_to = NIL; (*env)->references = oenv->references; diff -ru pine4.64/imap/src/c-client/mail.c pine4.64.SuSE/imap/src/c-client/mail.c --- pine4.64/imap/src/c-client/mail.c 2005-09-15 18:57:31.000000000 +0200 +++ pine4.64.SuSE/imap/src/c-client/mail.c 2006-02-14 14:45:22.000000000 +0100 @@ -979,6 +979,7 @@ (((*mailbox == '{') || (*mailbox == '#')) && (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT)))) d = stream->dtb; + else if (maildir_valid_name(mailbox)) return maildir_create(stream, mailbox); else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb; else { /* failed utterly */ sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox); diff -ru pine4.64/imap/src/c-client/mail.h pine4.64.SuSE/imap/src/c-client/mail.h --- pine4.64/imap/src/c-client/mail.h 2005-02-09 00:44:54.000000000 +0100 +++ pine4.64.SuSE/imap/src/c-client/mail.h 2006-02-14 14:45:22.000000000 +0100 @@ -149,6 +149,8 @@ #define SET_LOGOUTHOOK (long) 226 #define GET_LOGOUTDATA (long) 227 #define SET_LOGOUTDATA (long) 228 +#define SET_PASSWORDFILE 229 +#define GET_PASSWORDFILE 230 /* 3xx: TCP/IP */ #define GET_OPENTIMEOUT (long) 300 @@ -311,6 +313,8 @@ #define SET_SNARFPRESERVE (long) 567 #define GET_INBOXPATH (long) 568 #define SET_INBOXPATH (long) 569 +#define GET_COURIERSTYLE (long) 570 +#define SET_COURIERSTYLE (long) 571 /* Driver flags */ @@ -622,6 +626,7 @@ /* Message envelope */ typedef struct mail_envelope { + unsigned int ngpathexists : 1; /* newsgroups may be bogus */ unsigned int incomplete : 1; /* envelope may be incomplete */ unsigned int imapenvonly : 1; /* envelope only has IMAP envelope */ char *remail; /* remail header if any */ @@ -790,6 +795,7 @@ unsigned int spare7 : 1; /* seventh spare bit */ unsigned int spare8 : 1; /* eighth spare bit */ void *sparep; /* spare pointer */ + void *maildirp; /* for the Maildir driver, can't use sparep */ unsigned long user_flags; /* user-assignable flags */ } MESSAGECACHE; diff -ru pine4.64/imap/src/c-client/rfc822.c pine4.64.SuSE/imap/src/c-client/rfc822.c --- pine4.64/imap/src/c-client/rfc822.c 2005-01-18 21:41:09.000000000 +0100 +++ pine4.64.SuSE/imap/src/c-client/rfc822.c 2006-02-14 14:45:22.000000000 +0100 @@ -354,6 +354,7 @@ ENVELOPE *env = (*en = mail_newenvelope ()); BODY *body = bdy ? (*bdy = mail_newbody ()) : NIL; long MIMEp = -1; /* flag that MIME semantics are in effect */ + long PathP = NIL; /* flag that a Path: was seen */ parseline_t pl = (parseline_t) mail_parameters (NIL,GET_PARSELINE,NIL); if (!host) host = BADHOST; /* make sure that host is non-null */ while (i && *s != '\n') { /* until end of header */ @@ -443,6 +444,9 @@ *t++ = '\0'; } break; + case 'P': /* possible Path: */ + if (!strcmp (tmp+1,"ATH")) env->ngpathexists = T; + break; case 'R': /* possible Reply-To: */ if (!strcmp (tmp+1,"EPLY-TO")) rfc822_parse_adrlist (&env->reply_to,d,host); diff -ru pine4.64/imap/src/dmail/Makefile pine4.64.SuSE/imap/src/dmail/Makefile --- pine4.64/imap/src/dmail/Makefile 2002-11-19 01:43:31.000000000 +0100 +++ pine4.64.SuSE/imap/src/dmail/Makefile 2006-02-14 14:45:25.000000000 +0100 @@ -24,7 +24,7 @@ # Get local definitions from c-client directory CC = `cat $C/CCTYPE` -CFLAGS = -I$C `cat $C/CFLAGS` +CFLAGS = $(EXTRACFLAGS) -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` dmail: $(CCLIENTLIB) dmail.o diff -ru pine4.64/imap/src/imapd/Makefile pine4.64.SuSE/imap/src/imapd/Makefile --- pine4.64/imap/src/imapd/Makefile 2004-06-29 23:26:28.000000000 +0200 +++ pine4.64.SuSE/imap/src/imapd/Makefile 2006-02-14 14:45:25.000000000 +0100 @@ -47,7 +47,7 @@ C = ../c-client CCLIENTLIB = $C/c-client.a CC = `cat $C/CCTYPE` -CFLAGS = -I$C `cat $C/CFLAGS` $(NSBD) $(ENBD) -DANOFILE=\"$(ANO)\" \ +CFLAGS = $(EXTRACFLAGS) -I$C `cat $C/CFLAGS` $(NSBD) $(ENBD) -DANOFILE=\"$(ANO)\" \ -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" \ -DUSERALERTFILE=\"$(USERALERT)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\" LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` diff -ru pine4.64/imap/src/ipopd/Makefile pine4.64.SuSE/imap/src/ipopd/Makefile --- pine4.64/imap/src/ipopd/Makefile 2000-10-25 01:55:07.000000000 +0200 +++ pine4.64.SuSE/imap/src/ipopd/Makefile 2006-02-14 14:45:25.000000000 +0100 @@ -25,7 +25,7 @@ # Get local definitions from c-client directory CC = `cat $C/CCTYPE` -CFLAGS = -I$C `cat $C/CFLAGS` +CFLAGS = $(EXTRACFLAGS) -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` ipopd: ipop2d ipop3d diff -ru pine4.64/imap/src/mailutil/Makefile pine4.64.SuSE/imap/src/mailutil/Makefile --- pine4.64/imap/src/mailutil/Makefile 2002-11-19 01:41:46.000000000 +0100 +++ pine4.64.SuSE/imap/src/mailutil/Makefile 2006-02-14 14:45:25.000000000 +0100 @@ -25,7 +25,7 @@ # Get local definitions from c-client directory CC = `cat $C/CCTYPE` -CFLAGS = -I$C `cat $C/CFLAGS` +CFLAGS = $(EXTRACFLAGS) -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` mailutil: $(CCLIENTLIB) mailutil.o diff -ru pine4.64/imap/src/mailutil/mailutil.c pine4.64.SuSE/imap/src/mailutil/mailutil.c --- pine4.64/imap/src/mailutil/mailutil.c 2005-02-09 00:50:49.000000000 +0100 +++ pine4.64.SuSE/imap/src/mailutil/mailutil.c 2006-02-14 14:45:22.000000000 +0100 @@ -29,6 +29,7 @@ /* Globals */ +int passfile = NIL; /* password file supplied ? */ int debugp = NIL; /* flag saying debug */ int verbosep = NIL; /* flag saying verbose */ int rwcopyp = NIL; /* flag saying readwrite copy (for POP) */ @@ -159,6 +160,7 @@ for (nargs = argc ? argc - 1 : 0,args = argv + 1; nargs; args++,nargs--) { if (*(s = *args) == '-') { /* parse switches */ if (!strcmp (s,"-debug") || !strcmp (s,"-d")) debugp = T; + else if (!strcmp (s,"-passfile")) passfile = T; else if (!strcmp (s,"-verbose") || !strcmp (s,"-v")) verbosep = T; else if (!strcmp (s,"-rwcopy") || !strcmp (s,"-rw")) rwcopyp = T; else if ((nargs > 1) && (!strcmp (s,"-merge") || !strcmp (s,"-m"))) { @@ -179,6 +181,10 @@ exit (ret); } } + else if (passfile) { + env_parameters(SET_PASSWORDFILE, (void *)s); + passfile = NIL; + } else if (!cmd) cmd = s; /* first non-switch is command */ else if (!src) src = s; /* second non-switch is source */ else if (!dst) dst = s; /* third non-switch is destination */ @@ -665,7 +671,9 @@ username[NETMAXUSER-1] = '\0'; if (s = strchr (username,'\n')) *s = '\0'; } - strcpy (password,getpass ("password: ")); + mm_userpwd(mb, &username, &password); + if (!password || !*password) + strcpy (password,getpass ("password: ")); } diff -ru pine4.64/imap/src/mlock/Makefile pine4.64.SuSE/imap/src/mlock/Makefile --- pine4.64/imap/src/mlock/Makefile 2002-11-19 01:42:42.000000000 +0100 +++ pine4.64.SuSE/imap/src/mlock/Makefile 2006-02-14 14:45:25.000000000 +0100 @@ -23,7 +23,7 @@ # Get local definitions from c-client directory CC = `cat $C/CCTYPE` -CFLAGS = `cat $C/CFLAGS` +CFLAGS = $(EXTRACFLAGS) `cat $C/CFLAGS` all: mlock diff -ru pine4.64/imap/src/mlock/mlock.c pine4.64.SuSE/imap/src/mlock/mlock.c --- pine4.64/imap/src/mlock/mlock.c 2004-06-22 03:05:42.000000000 +0200 +++ pine4.64.SuSE/imap/src/mlock/mlock.c 2006-02-14 14:45:25.000000000 +0100 @@ -32,6 +32,7 @@ #include #include #include +#include #define LOCKTIMEOUT 5 /* lock timeout in minutes */ #define LOCKPROTECTION 0775 diff -ru pine4.64/imap/src/mtest/Makefile pine4.64.SuSE/imap/src/mtest/Makefile --- pine4.64/imap/src/mtest/Makefile 2000-10-25 01:55:39.000000000 +0200 +++ pine4.64.SuSE/imap/src/mtest/Makefile 2006-02-14 14:45:25.000000000 +0100 @@ -25,7 +25,7 @@ # Get local definitions from c-client directory CC = `cat $C/CCTYPE` -CFLAGS = -I$C `cat $C/CFLAGS` +CFLAGS = $(EXTRACFLAGS) -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` all: mtest diff -ru pine4.64/imap/src/osdep/unix/Makefile pine4.64.SuSE/imap/src/osdep/unix/Makefile --- pine4.64/imap/src/osdep/unix/Makefile 2005-04-30 22:51:13.000000000 +0200 +++ pine4.64.SuSE/imap/src/osdep/unix/Makefile 2006-02-14 14:45:24.000000000 +0100 @@ -119,7 +119,7 @@ # Standard distribution build parameters DEFAULTAUTHENTICATORS=md5 pla log -DEFAULTDRIVERS=imap nntp pop3 mh mx mbx tenex mtx mmdf unix news phile +DEFAULTDRIVERS=maildir courier imap nntp pop3 mh mx mbx tenex mtx mmdf unix news phile # Normally no need to change any of these @@ -128,7 +128,7 @@ BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o siglocal.o \ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \ - unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o + unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o maildir.o CFLAGS=-g CAT=cat @@ -257,7 +257,7 @@ cyg: # Cygwin - note that most local file drivers don't work!! $(BUILD) `$(CAT) SPECIALS` OS=$@ \ - DEFAULTDRIVERS="imap nntp pop3 mbx unix phile" \ + DEFAULTDRIVERS="imap nntp pop3 mbx unix maildir phile" \ SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \ SPOOLDIR=/var \ ACTIVEFILE=/usr/local/news/lib/active \ @@ -846,7 +846,7 @@ tenex.o: mail.h misc.h osdep.h dummy.h unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h utf8.o: mail.h misc.h osdep.h utf8.h - +maildir.o: mail.h misc.h osdep.h maildir.h dummy.h # OS-dependent diff -ru pine4.64/imap/src/osdep/unix/dummy.c pine4.64.SuSE/imap/src/osdep/unix/dummy.c --- pine4.64/imap/src/osdep/unix/dummy.c 2004-11-11 01:16:23.000000000 +0100 +++ pine4.64.SuSE/imap/src/osdep/unix/dummy.c 2006-02-14 14:45:22.000000000 +0100 @@ -104,6 +104,7 @@ { char *s,tmp[MAILTMPLEN]; struct stat sbuf; + maildir_remove_root(&name); /* must be valid local mailbox */ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) { /* indeterminate clearbox INBOX */ @@ -364,7 +365,10 @@ char *s,tmp[MAILTMPLEN]; /* don't \NoSelect dir if it has a driver */ if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) && - (d != &dummydriver)) attributes &= ~LATT_NOSELECT; + (d != &dummydriver)){ + attributes &= ~LATT_NOSELECT; + attributes |= LATT_NOINFERIORS; + } if (!contents || /* notify main program */ (!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) && (s = mailboxfile (tmp,name)) && @@ -385,6 +389,8 @@ { char *s,tmp[MAILTMPLEN]; long ret = NIL; + if(!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4)) + return maildir_create(stream, mailbox); /* validate name */ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) { sprintf (tmp,"Can't create %.80s: invalid name",mailbox); @@ -450,6 +456,14 @@ { struct stat sbuf; char *s,tmp[MAILTMPLEN]; + if (!strncmp(mailbox,"#md/",4) || !strncmp(mailbox,"#mc/", 4) + || is_valid_maildir(&mailbox)){ + char tmp[MAILTMPLEN] = {'\0'}; + strcpy(tmp, mailbox); + if(tmp[strlen(tmp) - 1] != '/') + tmp[strlen(tmp)] = '/'; + return maildir_delete(stream, tmp); + } if (!(s = dummy_file (tmp,mailbox))) { sprintf (tmp,"Can't delete - invalid name: %.80s",s); MM_LOG (tmp,ERROR); @@ -476,6 +490,9 @@ { struct stat sbuf; char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; + + maildir_remove_root(&old); + maildir_remove_root(&newname); /* no trailing / allowed */ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || ((s = strrchr (s,'/')) && !s[1])) { diff -ru pine4.64/imap/src/osdep/unix/env_unix.c pine4.64.SuSE/imap/src/osdep/unix/env_unix.c --- pine4.64/imap/src/osdep/unix/env_unix.c 2004-09-13 23:31:19.000000000 +0200 +++ pine4.64.SuSE/imap/src/osdep/unix/env_unix.c 2006-02-14 14:45:22.000000000 +0100 @@ -24,6 +24,7 @@ /* c-client environment parameters */ +static char *pwdfile = NIL; /* password file */ static char *myUserName = NIL; /* user name */ static char *myHomeDir = NIL; /* home directory name */ static char *myMailboxDir = NIL;/* mailbox directory name */ @@ -41,6 +42,7 @@ static char *blackBoxDir = NIL; /* black box directory name */ /* black box default home directory */ static char *blackBoxDefaultHome = NIL; +static int xlate_key; /* for password file support */ static short anonymous = NIL; /* is anonymous */ static short blackBox = NIL; /* is a black box */ static short closedBox = NIL; /* is a closed box */ @@ -215,6 +217,13 @@ case GET_SHAREDHOME: ret = (void *) sharedHome; break; + case SET_PASSWORDFILE: + if (pwdfile) fs_give ((void **) &pwdfile); + pwdfile = cpystr ((char *) value); + break; + case GET_PASSWORDFILE: + ret = (void *) pwdfile; + break; case SET_SYSINBOX: if (sysInbox) fs_give ((void **) &sysInbox); sysInbox = cpystr ((char *) value); @@ -1638,3 +1647,77 @@ } return ret; } + +/* + * + * Module to add support for password file to a c-client application + * + * Written by Eduardo Chappa, based on password file support for Pine + * + */ +#ifndef PWDFILE +#define PWDFILE 1 +#endif + +#define FIRSTCH 0x20 +#define LASTCH 0x7e +#define TABSZ (LASTCH - FIRSTCH + 1) + +char mm_xlate_out (char c); +void mm_userpwd (NETMBX *mb, char **username, char **password); + +/* function that decodes passwords */ + +char mm_xlate_out (char c) +{ + register int dti; + register int xch; + + if((c >= FIRSTCH) && (c <= LASTCH)){ + xch = c - (dti = xlate_key); + xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0; + dti = (xch - FIRSTCH) + dti; + dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0; + xlate_key = dti; + return(xch); + } + else + return(c); +} + +void mm_userpwd (NETMBX *mb, char **username, char **password) +{ + char *s; + char tmp[MAILTMPLEN], *ui[5]; + FILE *fp; + int i, j, n; + + if (!(pwdfile = env_parameters(GET_PASSWORDFILE, NULL))) + return; + + if (fp = fopen(pwdfile, "r")){ + for(n = 0; fgets(tmp, sizeof(tmp), fp); n++){ + xlate_key = n; + for(i = 0; tmp[i]; i++) + tmp[i] = mm_xlate_out(tmp[i]); + + if(i && tmp[i-1] == '\n') + tmp[i-1] = '\0'; + + ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL; + for(i = 0, j = 0; tmp[i] && j < 5; j++){ + for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++); + + if(tmp[i]) + tmp[i++] = '\0'; + } + if (*username && ui[1] && !strcmp(*username, ui[1]) && mb->host + && ((ui[2] && !strcmp(mb->host, ui[2])) + || (ui[4] && !strcmp(mb->host,ui[4])) + || (ui[2] && !strcmp(mb->orighost, ui[2])) + || (ui[4] && !strcmp(mb->orighost,ui[4])))) + strcpy (*password,ui[0]); + } + fclose(fp); + } +} Only in pine4.64.SuSE/imap/src/osdep/unix: maildir.c Only in pine4.64.SuSE/imap/src/osdep/unix: maildir.h diff -ru pine4.64/imap/src/osdep/unix/mh.c pine4.64.SuSE/imap/src/osdep/unix/mh.c --- pine4.64/imap/src/osdep/unix/mh.c 2004-11-05 02:46:54.000000000 +0100 +++ pine4.64.SuSE/imap/src/osdep/unix/mh.c 2006-02-14 14:45:24.000000000 +0100 @@ -28,6 +28,7 @@ #include #include #include +#include #include "mh.h" #include "misc.h" #include "dummy.h" diff -ru pine4.64/imap/src/osdep/unix/mx.c pine4.64.SuSE/imap/src/osdep/unix/mx.c --- pine4.64/imap/src/osdep/unix/mx.c 2004-11-05 02:49:37.000000000 +0100 +++ pine4.64.SuSE/imap/src/osdep/unix/mx.c 2006-02-14 14:45:24.000000000 +0100 @@ -28,6 +28,7 @@ #include #include #include +#include #include "mx.h" #include "misc.h" #include "dummy.h" diff -ru pine4.64/imap/src/osdep/unix/news.c pine4.64.SuSE/imap/src/osdep/unix/news.c --- pine4.64/imap/src/osdep/unix/news.c 2004-07-08 23:14:20.000000000 +0200 +++ pine4.64.SuSE/imap/src/osdep/unix/news.c 2006-02-14 14:45:25.000000000 +0100 @@ -27,6 +27,7 @@ #include "osdep.h" #include #include +#include #include "misc.h" #include "newsrc.h" diff -ru pine4.64/imap/src/osdep/unix/os_cyg.h pine4.64.SuSE/imap/src/osdep/unix/os_cyg.h --- pine4.64/imap/src/osdep/unix/os_cyg.h 2004-04-19 17:22:07.000000000 +0200 +++ pine4.64.SuSE/imap/src/osdep/unix/os_cyg.h 2006-02-14 14:45:22.000000000 +0100 @@ -39,6 +39,7 @@ #define setpgrp setpgid #define SYSTEMUID 18 /* Cygwin returns this for SYSTEM */ +#define FLAGSEP ';' #define geteuid Geteuid uid_t Geteuid (void); diff -ru pine4.64/imap/src/osdep/unix/os_slx.c pine4.64.SuSE/imap/src/osdep/unix/os_slx.c --- pine4.64/imap/src/osdep/unix/os_slx.c 2005-04-21 02:49:42.000000000 +0200 +++ pine4.64.SuSE/imap/src/osdep/unix/os_slx.c 2006-02-14 14:45:25.000000000 +0100 @@ -33,6 +33,7 @@ extern int errno; /* just in case */ #include #include +#include #include "misc.h" diff -ru pine4.64/imap/src/osdep/unix/phile.c pine4.64.SuSE/imap/src/osdep/unix/phile.c --- pine4.64/imap/src/osdep/unix/phile.c 2004-04-27 22:00:47.000000000 +0200 +++ pine4.64.SuSE/imap/src/osdep/unix/phile.c 2006-02-14 14:45:25.000000000 +0100 @@ -29,6 +29,7 @@ #include #include #include +#include #include "rfc822.h" #include "misc.h" #include "dummy.h" diff -ru pine4.64/imap/src/tmail/Makefile pine4.64.SuSE/imap/src/tmail/Makefile --- pine4.64/imap/src/tmail/Makefile 2002-11-19 01:45:14.000000000 +0100 +++ pine4.64.SuSE/imap/src/tmail/Makefile 2006-02-14 14:45:25.000000000 +0100 @@ -24,7 +24,7 @@ # Get local definitions from c-client directory CC = `cat $C/CCTYPE` -CFLAGS = -I$C `cat $C/CFLAGS` +CFLAGS = $(EXTRACFLAGS) -I$C `cat $C/CFLAGS` LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS` tmail: $(CCLIENTLIB) tmail.o Only in pine4.64.SuSE/: ldap diff -ru pine4.64/pico/basic.c pine4.64.SuSE/pico/basic.c --- pine4.64/pico/basic.c 2004-05-07 23:43:40.000000000 +0200 +++ pine4.64.SuSE/pico/basic.c 2006-02-14 14:45:23.000000000 +0100 @@ -266,8 +266,8 @@ gotobop(f, n) int f, n; /* default Flag & Numeric argument */ { - int quoted, qlen; - char qstr[NLINE], qstr2[NLINE]; + int quoted, qlen, j; + char qstr[NLINE], qstr2[NLINE], ind_str[NLINE], pqstr[NLINE]; if (n < 0) /* the other way...*/ return(gotoeop(f, -n)); @@ -279,6 +279,14 @@ curwp->w_dotp = lback(curwp->w_dotp); curwp->w_doto = 0; } + + if (indent_match(default_qstr(), curwp->w_dotp,ind_str, NLINE, 0)){ + if (n){ /* look for another paragraph ? */ + curwp->w_dotp = lback(curwp->w_dotp); + continue; + } + break; + } /* scan line by line until we come to a line ending with * a or or @@ -286,23 +294,60 @@ * PLUS: if there's a quote string, a quoted-to-non-quoted * line transition. */ - quoted = (glo_quote_str || (Pmaster && Pmaster->quote_str)) - ? quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, - curwp->w_dotp, qstr, NLINE) : 0; + quoted = quote_match(default_qstr(), curwp->w_dotp, qstr, NLINE, 0); qlen = quoted ? strlen(qstr) : 0; + while(lback(curwp->w_dotp) != curbp->b_linep && llength(lback(curwp->w_dotp)) > qlen - && ((glo_quote_str || (Pmaster && Pmaster->quote_str)) - ? (quoted == quote_match(glo_quote_str ? glo_quote_str - : Pmaster->quote_str, - lback(curwp->w_dotp), - qstr2, NLINE) - && !strcmp(qstr, qstr2)) - : 1) + && (quoted == quote_match(default_qstr(), + lback(curwp->w_dotp), qstr2, NLINE, 0)) + && !strcmp(qstr, qstr2) /* processed string */ + && (quoted == quote_match(default_qstr(), + lback(curwp->w_dotp), qstr2, NLINE, 1)) + && !strcmp(qstr, qstr2) /* raw string */ + && !indent_match(default_qstr(), + lback(curwp->w_dotp),ind_str, NLINE, 0) && lgetc(curwp->w_dotp, qlen).c != TAB && lgetc(curwp->w_dotp, qlen).c != ' ') curwp->w_dotp = lback(curwp->w_dotp); + /* + * Ok, we made it here and we assume that we are at the begining + * of the paragraph. Let's double check this now. In order to do + * so we shell check if the first line was indented in a special + * way. + */ + if(lback(curwp->w_dotp) == curbp->b_linep) + break; + else{ + int i, j; + + /* + * First we test if the preceding line is indented. + * for the following test we need to have the raw values, + * not the processed values + */ + quote_match(default_qstr(), curwp->w_dotp, qstr, NLINE, 1); + quote_match(default_qstr(), lback(curwp->w_dotp), qstr2, NLINE, 1); + for (i = 0, j = 0; + qstr[i] && qstr2[i] && (qstr[i] == qstr2[i]); i++, j++); + for (; isspace(qstr2[i]); i++); + for (; isspace(qstr[j]); j++); + if ((indent_match(default_qstr(), lback(curwp->w_dotp), + ind_str, NLINE, 1) + && (strlenis(qstr2) + strlenis(ind_str) >= strlenis(qstr))) + || (lback(curwp->w_dotp) != curbp->b_linep + && llength(lback(curwp->w_dotp)) > qlen + && (quoted == quote_match(default_qstr(), + lback(curwp->w_dotp), pqstr, NLINE, 0)) + && !strcmp(qstr, pqstr) + && lgetc(curwp->w_dotp, qlen).c != TAB + && lgetc(curwp->w_dotp, qlen).c != ' ' + && (strlenis(qstr2) > strlenis(qstr))) + && !qstr2[i] && !qstr[j]) + curwp->w_dotp = lback(curwp->w_dotp); + } + if(n){ /* keep looking */ if(lback(curwp->w_dotp) == curbp->b_linep) @@ -329,18 +374,210 @@ return(TRUE); } - +GetAccent() +{ + char c,d; + unsigned char ch, saved; + c = (char) GetKey(); + if ((c == '?') || (c == '!')) { + d = c; + c = '\\'; + } + else + if ((c == 's') || (c == 'S')){ + c = d = 's'; + } + else + if ((c == 'l') || (c == 'L')){ + c = d = 'l'; + } + else + d = (char) GetKey(); + ch = (unsigned char) accent(c,d); + if (gmode & P_UNICODE){ + saved = 0x80 | (ch & 0x3f); + ch = 0xc0 | ((ch >> 6) & 0x3f); + execute(ch,0, 1); + execute(saved,0, 1); + ch = 0; + } + return (int) ch; +} + +pineaccent(f,n) +int f,n; +{ int e; + + if (e = GetAccent()) + execute(e,0,1); + return 1; +} + +unsigned char accent(f,n) +int f,n; +{ char c,d; + + c = (char) f; + d = (char) n; + switch(c){ + case '~' : + switch(d){ + case 'a' : return '\343'; + case 'n' : return '\361'; + case 'o' : return '\365'; + case 'A' : return '\303'; + case 'N' : return '\321'; + case 'O' : return '\325'; + } + break; + case '\047' : + switch(d){ + case 'a' : return '\341'; + case 'e' : return '\351'; + case 'i' : return '\355'; + case 'o' : return '\363'; + case 'u' : return '\372'; + case 'y' : return '\375'; + case 'A' : return '\301'; + case 'E' : return '\311'; + case 'I' : return '\315'; + case 'O' : return '\323'; + case 'U' : return '\332'; + case 'Y' : return '\335'; + } + break; + case '"' : + switch(d){ + case 'a' : return '\344'; + case 'e' : return '\353'; + case 'i' : return '\357'; + case 'o' : return '\366'; + case 'u' : return '\374'; + case 'y' : return '\377'; + case 'A' : return '\304'; + case 'E' : return '\313'; + case 'I' : return '\317'; + case 'O' : return '\326'; + case 'U' : return '\334'; + } + break; + case '^' : + switch(d){ + case 'a' : return '\342'; + case 'e' : return '\352'; + case 'i' : return '\356'; + case 'o' : return '\364'; + case 'u' : return '\373'; + case 'A' : return '\302'; + case 'E' : return '\312'; + case 'I' : return '\316'; + case 'O' : return '\324'; + case 'U' : return '\333'; + case '0' : return '\260'; + case '1' : return '\271'; + case '2' : return '\262'; + case '3' : return '\263'; + } + break; + case '`' : + switch(d){ + case 'a' : return '\340'; + case 'e' : return '\350'; + case 'i' : return '\354'; + case 'o' : return '\362'; + case 'u' : return '\371'; + case 'A' : return '\300'; + case 'E' : return '\310'; + case 'I' : return '\314'; + case 'O' : return '\322'; + case 'U' : return '\331'; + } + break; + case 'o' : + switch(d){ + case 'a' : return '\345'; + case 'A' : return '\305'; + case '/' : return '\370'; + case 'r' : return '\256'; + case 'R' : return '\256'; + case 'c' : return '\251'; + case 'C' : return '\251'; + } + break; + case '-' : + switch(d){ + case 'o' : return '\272'; + case 'O' : return '\272'; + case '0' : return '\272'; + case 'a' : return '\252'; + case 'A' : return '\252'; + case 'l' : return '\243'; + case 'L' : return '\243'; + } + break; + case 'O' : + switch(d){ + case '/' : return '\330'; + case 'r' : return '\256'; + case 'R' : return '\256'; + case 'c' : return '\251'; + case 'C' : return '\251'; + } + case '/' : + switch(d){ + case 'o' : return '\370'; + case 'O' : return '\330'; + } + break; + case 'a' : + switch(d){ + case 'e' : return '\346'; + case 'E' : return '\346'; + } + break; + case 'A' : + switch(d){ + case 'E' : return '\306'; + case 'e' : return '\306'; + } + break; + case ',' : + switch(d){ + case 'c' : return '\347'; + case 'C' : return '\307'; + } + break; + case '\\' : + switch(d){ + case '?' : return '\277'; + case '!' : return '\241'; + } + break; + case 's' : + switch(d){ + case 's' : return '\337'; + } + break; + case 'l' : + switch(d){ + case 'l' : return '\243'; + } + break; + } + return '\0'; + } + /* - * go forword to the end of the current paragraph + * go forward to the end of the current paragraph * here we look for a or or * combination to delimit the begining of a paragraph */ gotoeop(f, n) int f, n; /* default Flag & Numeric argument */ - { - int quoted, qlen; - char qstr[NLINE], qstr2[NLINE]; + int quoted, qlen, indented, changeqstr = 0; + int i,j, fli = 0; /* fli = first line indented a boolean variable */ + char qstr[NLINE], qstr2[NLINE], ind_str[NLINE]; if (n < 0) /* the other way...*/ return(gotobop(f, -n)); @@ -353,26 +590,68 @@ break; } + /* + * We need to figure out if this line is the first line of + * a paragraph that has been indented in a special way. If this + * is the case, we advance one more line before we use the + * algorithm below + */ + + if(curwp->w_dotp != curbp->b_linep){ + quote_match(default_qstr(), curwp->w_dotp, qstr, NLINE, 1); + quote_match(default_qstr(), lforw(curwp->w_dotp), qstr2, NLINE, 1); + indented = indent_match(default_qstr(), curwp->w_dotp, ind_str, + NLINE, 1); + if (strlenis(qstr) + strlenis(ind_str) < strlenis(qstr2)){ + curwp->w_doto = 0; + if(n){ /* this line is a paragraph by itself */ + curwp->w_dotp = lforw(curwp->w_dotp); + continue; + } + break; + } + for (i=0,j=0; qstr[i] && qstr2[i] && (qstr[i] == qstr2[i]);i++,j++); + for (; isspace(qstr[i]); i++); + for (; isspace(qstr2[j]); j++); + if (!qstr[i] && !qstr2[j] && indented){ + fli++; + if (indent_match(default_qstr(), lforw(curwp->w_dotp), + ind_str, NLINE, 0)){ + if (n){ /* look for another paragraph ? */ + curwp->w_dotp = lforw(curwp->w_dotp); + continue; + } + } + else{ + if (!lisblank(lforw(curwp->w_dotp))) + curwp->w_dotp = lforw(curwp->w_dotp); + } + } + } + /* scan line by line until we come to a line ending with * a or or * * PLUS: if there's a quote string, a quoted-to-non-quoted * line transition. */ - quoted = ((glo_quote_str || (Pmaster && Pmaster->quote_str)) - ? quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, - curwp->w_dotp, qstr, NLINE) : 0); + /* if the first line is indented (fli == 1), then the test below + is on the second line, and in that case we will need the raw + string, not the processed string + */ + quoted = quote_match(default_qstr(), curwp->w_dotp, qstr, NLINE, fli); qlen = quoted ? strlen(qstr) : 0; - + while(curwp->w_dotp != curbp->b_linep && llength(lforw(curwp->w_dotp)) > qlen - && ((glo_quote_str || (Pmaster && Pmaster->quote_str)) - ? (quoted == quote_match(glo_quote_str ? glo_quote_str - : Pmaster->quote_str, - lforw(curwp->w_dotp), - qstr2, NLINE) - && !strcmp(qstr, qstr2)) - : 1) + && (quoted == quote_match(default_qstr(), + lforw(curwp->w_dotp), qstr2, NLINE, fli)) + && !strcmp(qstr, qstr2) + && (quoted == quote_match(default_qstr(), + lforw(curwp->w_dotp), qstr2, NLINE, 1)) + && !strcmp(qstr, qstr2) + && !indent_match(default_qstr(), + lforw(curwp->w_dotp), ind_str, NLINE, 0) && lgetc(lforw(curwp->w_dotp), qlen).c != TAB && lgetc(lforw(curwp->w_dotp), qlen).c != ' ') curwp->w_dotp = lforw(curwp->w_dotp); @@ -653,7 +932,47 @@ return (scrollback (n, TRUE)); } +/* deltext deletes from the specified position until the end of the file + * or until the signature (when called from Pine), whichever comes first. + */ +int +deltext (f,n) +int f,n; +{ + LINE *currline = curwp->w_dotp; + + if ((lastflag&CFKILL) == 0) + kdelete(); + + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = curwp->w_doto; + + while (curwp->w_dotp != curbp->b_linep){ + if ((Pmaster) + && (llength(curwp->w_dotp) == 3) + && (lgetc(curwp->w_dotp, 0).c == '-') + && (lgetc(curwp->w_dotp, 1).c == '-') + && (lgetc(curwp->w_dotp, 2).c == ' ')){ + if (curwp->w_dotp == currline){ + if (curwp->w_doto) + curwp->w_dotp = lforw(curwp->w_dotp); + else + break; + } + else{ + curwp->w_dotp = lback(curwp->w_dotp); + curwp->w_doto = llength(curwp->w_dotp); + break; + } + } + else + curwp->w_dotp = lforw(curwp->w_dotp); + } + killregion(FALSE,1); + lastflag |= CFKILL; + return TRUE; +} scrollupline (f, n) int f, n; diff -ru pine4.64/pico/composer.c pine4.64.SuSE/pico/composer.c --- pine4.64/pico/composer.c 2005-03-17 20:07:47.000000000 +0100 +++ pine4.64.SuSE/pico/composer.c 2006-02-14 14:45:23.000000000 +0100 @@ -344,7 +344,189 @@ return(TRUE); } +/* + * check_utf8 - check for UTF-8 bytes + * Takes two arguments: + * char *c - a byte of the stream + * char *utf_seq - a status array holding the function's state + * utf_seq must be provided by the caller this way: + * (static) char utf_seq[7] = ""; (content must be retained over calls) + * and must be initialized at start using: utf_seq[0] = 0; + * + * Returns NULL if an UTF-8 sequence has been started and is not completed. + * If an UTF-8 sequence is complete, it returns a pointer to a static string + * which is valid until the next use of the function. + * If the character is a double width character, a space(' ') is prepended + * to the returned string. + * If a character < 128 is passed, the UTF-8 state in utf_seq[] is cleared, + * because a valid UTF-8 sequence only consists of bytes >= 0x80. The pointer + * returned points to the address of the passed character to indicate this. + * Features: Supports UTF-8 seqencies up to 4 bytes. + * Todo: Instead of passing a pointer to the char and comparing the returned + * pointer to this address afterwards, the Interface could be changed + * to just pass the character as simple char(thus not requesting the + * address of a variable which might be declared as register) and replace + * the check of the return value with a check of (c & 0x80) and if this + * is not the case, assuming that (utf_seq[0] == 0) means that this last + * non-ASCII byte completed the UTF-8 sequence, while having + * utf_seq[0] != 0 means having an incomplete UTF-8 sequence. + */ +char * +check_utf8(c, utf_seq, sizeof_utf_seq) + char *c; + char *utf_seq; + size_t sizeof_utf_seq; +{ + static char char_string[8]; /* (six UTF-8 sequence bytes + ' ' + '\0') */ + int ix; + unsigned char dbl_wide[7][2][4] = {0xe1,0x84,0x80,0x00, 0xe1,0x85,0x9F,0x00, + 0xe2,0x8c,0xa9,0x00, 0xe2,0x8c,0xaa,0x00, + 0xe2,0xba,0x80,0x00, 0xed,0x9e,0xa3,0x00, + 0xef,0xa4,0x80,0x00, 0xef,0xa9,0xaa,0x00, + 0xef,0xb8,0xb0,0x00, 0xef,0xb9,0xa8,0x00, + 0xef,0xbc,0x81,0x00, 0xef,0xbd,0xad,0x00, + 0xef,0xbf,0xa0,0x00, 0xef,0xbf,0xa6,0x00}; + if (*c & 0x80) { + char_string[0] = *c; + char_string[1] = 0; + if (strlen(utf_seq) == sizeof_utf_seq - 1) + utf_seq[0] = 0; /* don't allow a overlong UTF-8 sequence */ + if ((*c & 0xF0) >= 0xC0) { + strncpy(utf_seq, char_string, sizeof_utf_seq); + return NULL; /* possible UTF-8 sequence, need next byte */ + } else if (utf_seq[0]) { + strncat(utf_seq, char_string, sizeof_utf_seq); /* append to string */ + switch (utf_seq[0] & 0xF0) { + case 0xC0 : + case 0xD0 : + strncpy(char_string, utf_seq, sizeof(char_string)); + utf_seq[0] = 0; /* sequence complete, clear for next */ + return char_string; /* pass the new UTF-8 sequence on */ + case 0xE0 : + if (strlen(utf_seq) < 3) + return NULL; // 3-byte UTF-8, need next byte + char_string[0] = '\0'; // init + for (ix = 0; ix < 7; ix++) + if (strcmp(utf_seq, &dbl_wide[ix][0][0]) >= 0 + && strcmp(utf_seq, &dbl_wide[ix][1][0]) <= 0) { + char_string[0] = ' '; /* flag as double-width char */ + break; + } + strncat(char_string, utf_seq, sizeof(char_string)); + utf_seq[0] = 0; // this sequence is over, clear for restart + return char_string; // process this UTF-8 char... + case 0xF0 : + if (strlen(utf_seq) < 4) + return NULL; /* 4-byte UTF-8 sequence, need next byte */ + char_string[0] = '\0'; /* init the sequence space */ + if ((utf_seq[1] & 0xF0) == 0xA0) + char_string[0] = ' '; /* flag as double-width UTF-8 char */ + strncat(char_string, utf_seq, sizeof(char_string)); + utf_seq[0] = 0; /* sequence complete, clear for next */ + return char_string; /* pass the new UTF-8 sequence on */ + } + } + } + utf_seq[0] = 0; /* clear sequence buffer in case of an invalid sequence */ + return c; /* single-byte, NON-UTF-8 chars are process it as usual */ +} + +/* + * wrapper to check_utf8 for pico, if not in UTF-8 mode, do not check UTF-8 + */ +char * +pico_check_utf8(c, utf_seq, sizeof_utf_seq) + char *c; + char *utf_seq; + size_t sizeof_utf_seq; +{ + if(!(Pmaster->pine_flags & P_UNICODE)) + return c; + return check_utf8(c, utf_seq, sizeof_utf_seq); +} + +/* + * Get the number of columns which are filled by the text in the current + * line of LineEdit(from the start of the line to the current position) + */ +static int +count_screencols(void) +{ + char utf_seq[7] = "", *cp, *r; + int seq = 0, w = 0; + + for(cp = ods.cur_l->text; *cp && cp < ods.cur_l->text + ods.p_off; + cp++) { + if (!(r = pico_check_utf8(cp, utf_seq, sizeof(utf_seq)))) { + seq = 1; + continue; + } + if (seq) + w++; + seq = 0; + if (r == cp) + w++; + else if (*r == ' ') + w++; + } + return w; +} +/* + * Get the offset in screen positions which must be subsctracted from the + * byte count in the LineEdit line in order to reach the line position on + * screen(because of double wide characters and multible UTF-8 bytes) + */ +static int +offset_on_screen(void) +{ + return ods.p_off - count_screencols(); +} + +/* + * Move current position in LineEdit one character left, return the number + * of byte positons which were neccesary to jump left in order to + * arrive at the start of the previous multibyte character(UTF-8). + */ +static int +LineEditCharLeft() +{ + int col_right = ods.p_off, cols = count_screencols(); + + do + if (--ods.p_off < 0) + break; + while (count_screencols() - cols == -1); + + ods.p_off++; + + if (col_right - ods.p_off > 0) + return col_right - ods.p_off; + + do + if (--ods.p_off < 0) + break; + while (count_screencols() - cols == -2); + + ods.p_off++; + + return col_right - ods.p_off; +} + +/* + * Move current position in LineEdit one character right, if UTF-8 + * mode is active, the ods.p_off is assumed to be at the start of + * a UTF-8 sequence or at a normal ASCII character. It is moved to + * the next character, jumping past the end of the current UTF-8 + * sequence, if UTF8 mode is active. + */ +static void +LineEditCharRight() +{ + char utf_seq[7] = ""; + while(ods.p_off < ods.p_len && ods.cur_l->text[ods.p_off] && + !pico_check_utf8(ods.cur_l->text + ods.p_off++, utf_seq, sizeof(utf_seq))); +} /* * ResizeHeader - Handle resizing display when SIGWINCH received. @@ -397,7 +579,7 @@ PaintBody(0); if(ComposerEditing) - movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen); + HeaderPaintCursor(); (*term.t_flush)(); return(TRUE); @@ -1560,6 +1742,8 @@ } UpdateHeader(0); + if(sendnow) + return(status !=0); PaintHeader(COMPOSER_TOP_LINE, status != 0); PaintBody(1); return(status != 0); @@ -1594,6 +1778,7 @@ int skipmove = 0; char *strng; int last_key; /* last keystroke */ + unsigned char utf_seq[7] = ""; strng = ods.cur_l->text; /* initialize offsets */ ods.p_len = strlen(strng); @@ -1676,7 +1861,7 @@ } clearcursor(); - movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen); + HeaderPaintCursor(); if(ch == NODATA) /* GetKey timed out */ continue; @@ -1686,12 +1871,12 @@ if(mpresf){ /* blast old messages */ if(mpresf++ > NMMESSDELAY){ /* every few keystrokes */ mlerase(); - movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen); + HeaderPaintCursor(); } } if(VALID_KEY(ch)){ /* char input */ - /* +insert_char:/* * if we are allowing editing, insert the new char * end up leaving tbufp pointing to newly * inserted character in string, and offset to the @@ -1732,12 +1917,40 @@ /* * then find out where things fit... + * + * For UTF-8, the < LINELEN check should need to do it's + * calculation based on count_screencols() plus the width + * of the new char as provided by pico_check_utf8. + * The buffer size may need to be increased for this. */ if(ods.p_len < LINELEN()){ CELL c; + char tmp, *chp; - c.c = ch; c.a = 0; + if(Pmaster->pine_flags & P_UNICODE) { + tmp = ch; + chp = pico_check_utf8(&tmp, utf_seq, sizeof(utf_seq)); + if (chp == NULL) + continue; /* on to the next! */ + if (chp != &tmp && *chp == ' ') + chp++; + if (*chp & 0x80) { + while (*chp && ods.p_len < LINELEN()) { + c.c = *chp++; + pinsert(c); /* add char to str */ + } + /* update the display: */ + PaintHeader(COMPOSER_TOP_LINE, TRUE); + /* If end char was inserted, set physical .. */ + if (ods.p_off == ods.p_len) + /* cursor pos on next movecursor_offset: */ + movecursor_offset(-1, 0, 0); + continue; /* on to the next! */ + } + } + + c.c = ch; if(pinsert(c)){ /* add char to str */ skipmove++; /* must'a been optimal */ continue; /* on to the next! */ @@ -1774,7 +1987,15 @@ } } else { /* interpret ch as a command */ + utf_seq[0] = '\0'; switch (ch = normalize_cmd(ch, ckm, 2)) { + case (CTRL|'\\') : + if (ch = GetAccent()) + goto insert_char; + else + clearcursor(); + break; + case (CTRL|'@') : /* word skip */ while(strng[ods.p_off] && isalnum((unsigned char)strng[ods.p_off])) @@ -1859,9 +2080,7 @@ case (CTRL|'F') : case KEY_RIGHT: /* move character right */ if(ods.p_off < ods.p_len){ - pputc(pscr(ods.p_line, - (ods.p_off++)+headents[ods.cur_e].prlen)->c,0); - skipmove++; + LineEditCharRight(); continue; } else if(gmode & MDHDRONLY) @@ -1873,7 +2092,7 @@ case (CTRL|'B') : case KEY_LEFT : /* move character left */ if(ods.p_off > 0){ - ods.p_off--; + LineEditCharLeft(); continue; } if(ods.p_line != COMPOSER_TOP_LINE) @@ -1908,7 +2127,8 @@ continue; } - pputc(strng[ods.p_off++], 0); /* drop through and rubout */ + LineEditCharRight(); /* jump to next char */ + /* and fall thru */ case DEL : /* blast previous char */ case (CTRL|'H') : @@ -1922,20 +2142,27 @@ continue; } - if(ods.p_off > 0){ /* just shift left one char */ - ods.p_len--; + if(ods.p_off > 0){ /* shift left one char */ + int todelete = LineEditCharLeft(); + + ods.p_len -= todelete; + headents[ods.cur_e].dirty = 1; if(ods.p_len == 0) headents[ods.cur_e].sticky = 0; else headents[ods.cur_e].sticky = 1; - tbufp = &strng[--ods.p_off]; - while(*tbufp++ != '\0') - tbufp[-1] = *tbufp; tbufp = &strng[ods.p_off]; + + while(*tbufp++ != '\0') + tbufp[-1] = tbufp[todelete-1]; + if(pdel()) /* physical screen delete */ skipmove++; /* must'a been optimal */ + + /* needed if pine bgcolor != terminal background color */ + PaintHeader(ods.p_line, TRUE); } else{ /* may have work to do */ if(ods.cur_l->prev == NULL){ @@ -1946,18 +2173,16 @@ ods.p_line--; ods.cur_l = ods.cur_l->prev; strng = ods.cur_l->text; - if((i=strlen(strng)) > 0){ - strng[i-1] = '\0'; /* erase the character */ - ods.p_off = i-1; + if((ods.p_off=strlen(strng)) > 0){ + ods.p_off -= LineEditCharLeft() - 1; + strng[ods.p_off] = '\0'; /* erase the character */ } - else{ + else headents[ods.cur_e].sticky = 0; - ods.p_off = 0; - } - - tbufp = &strng[ods.p_off]; } + tbufp = &strng[ods.p_off]; + if((status = FormatLines(ods.cur_l, "", LINELEN(), headents[ods.cur_e].break_on_comma,0))==-1){ (*term.t_beep)(); @@ -1982,7 +2207,7 @@ PaintBody(1); } - movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen); + HeaderPaintCursor(); if(skipmove) continue; @@ -2007,7 +2232,8 @@ void HeaderPaintCursor() { - movecursor(ods.p_line, ods.p_off+headents[ods.cur_e].prlen); + movecursor_offset(ods.p_line, ods.p_off + headents[ods.cur_e].prlen, + offset_on_screen()); } @@ -2955,6 +3181,9 @@ { register char *bufp; + if (sendnow) + return; + if(ComposerTopLine - 1 >= BOTTOM()) /* silently forget it */ return; @@ -3004,6 +3233,9 @@ register char *bufp; register int i; + if (sendnow) + return(TRUE); + bufp = headents[entry].prompt; /* fresh prompt paint */ if((i = entry_line(entry, FALSE)) == -1) return(-1); /* silently forget it */ @@ -3884,6 +4116,9 @@ void ShowPrompt() { + if (sendnow) + return; + if(headents[ods.cur_e].key_label){ menu_header[TO_KEY].name = "^T"; menu_header[TO_KEY].label = headents[ods.cur_e].key_label; diff -ru pine4.64/pico/display.c pine4.64.SuSE/pico/display.c --- pine4.64/pico/display.c 2004-05-07 23:43:40.000000000 +0200 +++ pine4.64.SuSE/pico/display.c 2006-02-14 14:45:23.000000000 +0100 @@ -133,6 +133,201 @@ #define ISCONTROL(C) ((C) < 0x20 || (C) == 0x7F \ || ((gmode & P_HICTRL) && ((C) > 0x7F && (C) < 0xA0))) +/* + * This is an implementation of wcwidth() (defined in IEEE Std 1002.1-2001) + * for Unicode: + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2003-05-20 (Unicode 4.0) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted. The author + * disclaims all warranties with regard to this software. + * + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + * + * Adapted for pine by Bernhard Kaindl + */ + +struct interval { + int first; + int last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(int ucs, const struct interval *table, int max) { + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that ucs characters are encoded + * in ISO 10646. + */ + +int mk_wcwidth(int ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x0357 }, { 0x035D, 0x036F }, { 0x0483, 0x0486 }, + { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 }, + { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C4 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 }, + { 0x064B, 0x0658 }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 }, + { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, + { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, + { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, + { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, + { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, + { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, + { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, + { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, + { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, + { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, + { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, + { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, + { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, + { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, + { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, + { 0x0CCC, 0x0CCD }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x1712, 0x1714 }, + { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, + { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, + { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D }, + { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 }, + { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x200B, 0x200F }, + { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F }, + { 0x20D0, 0x20EA }, { 0x302A, 0x302F }, { 0x3099, 0x309A }, + { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 }, + { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} /* * Initialize the data structures used by the display code. The edge vectors @@ -282,6 +477,10 @@ } else vp->v_text[vtcol++] = c; + if (gmode & P_UNICODE && mk_wcwidth(c.c) == 2) { + ac.c = 0xfedc0000; // dirty trick, ttputc uses char (signed..) and checks >127, to be done properly... + vtputc(ac); + } } @@ -322,6 +521,10 @@ vp->v_text[vtcol] = c; ++vtcol; } + if (gmode & P_UNICODE && mk_wcwidth(c.c) == 2) { + ac.c = 0xfedc0000; // dirty trick, ttputc uses char (signed..) and checks >127, to be done properly... + vtputc(ac); + } } @@ -362,6 +565,9 @@ register int scroll = 0; CELL c; + if (sendnow) + return; + #if TYPEAH if (typahead()) return; @@ -608,6 +814,9 @@ curcol |= 0x07; else if (ISCONTROL(c.c)) ++curcol; + else if (gmode & P_UNICODE) { + curcol += mk_wcwidth(c.c) - 1; + } ++curcol; } @@ -1086,7 +1295,22 @@ } } +void +movecursor_offset(row, col, offs) +int row, col, offs; +{ + static int force_next = 0; + if(row == -1) { + force_next = row; + return; + } + if(row!=ttrow || col!=ttcol || force_next) { + (*term.t_move)(row, col - offs); + ttrow = row; + ttcol = col; + } +} /* * Send a command to the terminal to move the hardware cursor to row "row" @@ -1400,6 +1624,7 @@ maxl = (nbuf-1 < term.t_ncol - plen - 1) ? nbuf-1 : term.t_ncol - plen - 1; + if (!sendnow) pputs(buf, 1); b = &buf[(flg & QBOBUF) ? 0 : strlen(buf)]; @@ -1460,6 +1685,19 @@ b++; continue; + case (CTRL|'N'): /* Insert pattern */ + if (pat[0] != '\0'){ + strcat(buf,pat); + pputs(pat,1); + b += strlen(pat); + changed = TRUE; + } + else + (*term.t_beep)(); + continue; + + + case (CTRL|'G') : /* CTRL-G help */ if(term.t_mrow == 0 && km_popped == 0){ movecursor(term.t_nrow-2, 0); @@ -1515,6 +1753,11 @@ b--; continue; + case (CTRL|'\\'): + if (c = GetAccent()) + goto text; + continue; + case KEY_RIGHT: if(*b != '\0') b++; @@ -1566,6 +1809,7 @@ #endif default : +text: if(strlen(buf) >= maxl){ /* contain the text */ (*term.t_beep)(); continue; @@ -1604,22 +1848,26 @@ b[i+1] = b[i]; while(i-- > 0); - pputc(*b++ = c, 0); + if(sendnow) + *b++ = c; + else + pputc(*b++ = c, 0); } } - pputs(b, 1); /* show default */ + if(!sendnow) + pputs(b, 1); /* show default */ i = term.t_ncol-1; while(pscreen[ttrow]->v_text[i].c == ' ' && pscreen[ttrow]->v_text[i].a == 0) i--; - while(ttcol <= i) + while(!sendnow && ttcol <= i) pputc(' ', 0); } ret: - if(km_popped){ + if(!sendnow && km_popped){ term.t_mrow = 0; movecursor(term.t_nrow, 0); peeol(); @@ -1745,6 +1993,8 @@ register int c; register char *ap; + if(sendnow) + return 0; /* * the idea is to only highlight if there is something to show */ @@ -2472,6 +2722,9 @@ char nbuf[NLINE]; #endif + if(sendnow) + return; + #ifdef _WINDOWS pico_config_menu_items (keymenu); #endif diff -ru pine4.64/pico/ebind.h pine4.64.SuSE/pico/ebind.h --- pine4.64/pico/ebind.h 2004-05-07 23:43:40.000000000 +0200 +++ pine4.64.SuSE/pico/ebind.h 2006-02-14 14:45:22.000000000 +0100 @@ -105,7 +105,9 @@ {CTRL|'^', setmark}, {CTRL|'_', alt_editor}, {0x7F, backdel}, - {0, NULL} + {CTRL|'\\', pineaccent}, + {0, +NULL} }; diff -ru pine4.64/pico/edef.h pine4.64.SuSE/pico/edef.h --- pine4.64/pico/edef.h 2004-10-14 03:27:01.000000000 +0200 +++ pine4.64.SuSE/pico/edef.h 2006-02-14 14:45:22.000000000 +0100 @@ -43,6 +43,7 @@ /* initialized global definitions */ +int sendnow = 0; /* should we send now */ int fillcol = 72; /* Current fill column */ int userfillcol = -1; /* Fillcol set from cmd line */ char pat[NPAT]; /* Search pattern */ @@ -96,6 +97,7 @@ /* initialized global external declarations */ +extern int sendnow; /* should we send now */ extern int fillcol; /* Fill column */ extern int userfillcol; /* Fillcol set from cmd line */ extern char pat[]; /* Search pattern */ diff -ru pine4.64/pico/efunc.h pine4.64.SuSE/pico/efunc.h --- pine4.64/pico/efunc.h 2004-06-16 00:22:35.000000000 +0200 +++ pine4.64.SuSE/pico/efunc.h 2006-02-14 14:45:23.000000000 +0100 @@ -65,6 +65,7 @@ extern int gotoeop PROTO((int, int)); extern int forwpage PROTO((int, int)); extern int backpage PROTO((int, int)); +extern int deltext PROTO((int, int)); extern int scrollupline PROTO((int, int)); extern int scrolldownline PROTO((int, int)); extern int scrollto PROTO((int, int)); @@ -73,6 +74,9 @@ extern int setimark PROTO((int, int)); extern int swapimark PROTO((int, int)); extern int mousepress PROTO((int, int)); +extern unsigned char accent PROTO((int, int)); +extern int pineaccent PROTO((int, int)); +extern int GetAccent PROTO((void)); /* bind.c */ extern int whelp PROTO((int, int)); @@ -114,6 +118,7 @@ extern VARS_TO_SAVE *save_pico_state PROTO((void)); extern void restore_pico_state PROTO((VARS_TO_SAVE *)); extern void free_pico_state PROTO((VARS_TO_SAVE *)); +extern char *check_utf8 PROTO((char *, char *, size_t)); extern void HeaderPaintCursor PROTO((void)); extern void PaintBody PROTO((int)); @@ -337,6 +342,13 @@ extern int fillpara PROTO((int, int)); extern int fillbuf PROTO((int, int)); extern int inword PROTO((void)); -extern int quote_match PROTO((char *, LINE *, char *, int)); +extern int quote_match PROTO((char *, LINE *, char *, int, int)); +extern char *default_qstr PROTO((void)); +extern void flatten_qstring PROTO((QSTRING_S *, char *, int)); +extern void free_qs PROTO((QSTRING_S **)); +extern QSTRING_S *do_quote_match PROTO((char *,char *, char *, char *, char *, int, int)); +extern QSTRING_S *copy_qs PROTO((QSTRING_S *)); +extern QSTRING_S *do_raw_quote_match PROTO((char *, char *, char *, char *, QSTRING_S **, QSTRING_S **)); + #endif /* EFUNC_H */ diff -ru pine4.64/pico/estruct.h pine4.64.SuSE/pico/estruct.h --- pine4.64/pico/estruct.h 2004-12-01 01:37:37.000000000 +0100 +++ pine4.64.SuSE/pico/estruct.h 2006-02-14 14:45:23.000000000 +0100 @@ -290,7 +290,7 @@ * and short if there are problems... */ typedef struct CELL { - unsigned int c : 8; /* Character value in cell */ + unsigned int c : 32; /* Character value in cell */ unsigned int a : 8; /* Its attributes */ } CELL; @@ -354,6 +354,7 @@ #define lgetc(lp, n) ((lp)->l_text[(n)]) #define lputc(lp, n, c) ((lp)->l_text[(n)]=(c)) #define llength(lp) ((lp)->l_used) +#define cell_isspace(lp,n) Pisspace(lgetc(lp, n).c) /* * The editor communicates with the display using a high level interface. A diff -ru pine4.64/pico/file.c pine4.64.SuSE/pico/file.c --- pine4.64/pico/file.c 2004-06-11 23:49:40.000000000 +0200 +++ pine4.64.SuSE/pico/file.c 2006-02-14 14:45:23.000000000 +0100 @@ -425,11 +425,11 @@ for(p = (glo_quote_str ? glo_quote_str : (Pmaster ? Pmaster->quote_str : NULL)); p && *p; p++) - if(!linsert(1, *p)) + if(!linsert_byte(1, *p)) return(0); } else if(c != '\r') /* ignore CR (likely CR of CRLF) */ - return(linsert(1, c)); + return(linsert_byte(1, c)); return(1); } @@ -493,7 +493,7 @@ case FIOLNG : for(linep = line; charsread-- > 0; linep++) - linsert(1, (unsigned char) *linep); + linsert_byte(1, *linep); break; @@ -910,7 +910,7 @@ case FIOLNG : for(linep = line; charsread-- > 0; linep++) - linsert(1, (unsigned char) *linep); + linsert_byte(1, *linep); break; diff -ru pine4.64/pico/fileio.c pine4.64.SuSE/pico/fileio.c --- pine4.64/pico/fileio.c 2002-02-12 23:53:53.000000000 +0100 +++ pine4.64.SuSE/pico/fileio.c 2006-02-14 14:45:23.000000000 +0100 @@ -71,9 +71,20 @@ { register int i; - for (i = 0; i < nbuf; ++i) + for (i = 0; i < nbuf; ++i) { + if (gmode & P_UNICODE && buf[i].c & 0xffffff80) { + if (buf[i].c & 0xf800) { + fputc(0xe0 | (buf[i].c >> 12), g_pico_fio.fp); + fputc(0x80 | ((buf[i].c >> 6) & 0x3f), g_pico_fio.fp); + } + else + fputc(0xc0 | ((buf[i].c >> 6) & 0x3f), g_pico_fio.fp); + if (fputc(0x80 | (buf[i].c & 0x3f), g_pico_fio.fp) == EOF) + break; + } else if(fputc(buf[i].c&0xFF, g_pico_fio.fp) == EOF) break; + } if(i == nbuf) fputc('\n', g_pico_fio.fp); diff -ru pine4.64/pico/line.c pine4.64.SuSE/pico/line.c --- pine4.64/pico/line.c 2004-05-07 23:43:40.000000000 +0200 +++ pine4.64.SuSE/pico/line.c 2006-02-14 14:45:23.000000000 +0100 @@ -51,7 +51,7 @@ */ struct pkchunk { short used; /* # of bytes used in this buffer*/ - char bufp[KBLOCK]; /* buffer containing text */ + int bufp[KBLOCK]; /* buffer containing text */ struct pkchunk *next; /* pointer to next chunk */ }; @@ -210,6 +210,83 @@ backchar(f, n); } +/* Return UCS-4 character from UTF-8 string + * (Based on code from pine-4.61/imap/src/c-client/utf8.c) + * Accepts: pointer to string, remaining octets in string + * Returns: UCS-4 character or negative if error + */ +unsigned int utf8_get_ucs_string(unsigned char **s, unsigned int i) +{ + unsigned char c; + unsigned int ret = 0; + int more = 0; + while (i--) { + if (((c = *(*s)++) > 0x7f) && (c < 0xc0)) { /* UTF-8 continuation? */ + if (!more) /* continuation outside of UTF-8 sequence? */ + return '?'; /* bad sequence, put replacement character */ + ret <<= 6; + ret |= c & 0x3f; + if (!--more) /* last octet reached? */ + return ret; /* return UTC-4 code */ + } + else if (more) /* in sequence, but not a continuation byte */ + return '?'; /* bad sequence, put replacement character */ + else if (c < 0x80) /* U+0000 - U+007f */ + return c; + else if (c < 0xe0) { /* U+0080 - U+07ff */ + ret = c & 0x1f; /* first 5 bits of 12 */ + more = 1; + } + else if (c < 0xf0) { /* U+1000 - U+ffff */ + ret = c & 0x0f; /* first 4 bits of 16 */ + more = 2; + } /* non-BMP Unicode */ + else if (c < 0xf8) { /* U+10000 - U+10ffff (U+1fffff) */ + ret = c & 0x07; /* first 3 bits of 20.5 (21) */ + more = 3; + } + else if (c < 0xfc) { /* ISO 10646 200000 - 3fffffff */ + ret = c & 0x03; /* first 2 bits of 26 */ + more = 4; + } + else if (c < 0xfe) { /* ISO 10646 4000000-7fffffff */ + ret = c & 0x01; /* first bit of 31 */ + more = 5; + } else + return '?'; /* not in ISO 10646 -> replacement character */ + } /* end of input, but sequnece not complete */ + return 0; +} +unsigned int utf8_get_ucs(unsigned char *s, unsigned int i) +{ + unsigned char *l = s; + return utf8_get_ucs_string(&l, i); +} + +/* + * Insert "n" copies of the character "c" at the current location of dot. + * The real work is done by linsert(). This is a wrapper does: + * In UTF-8 mode, decode byte sequencies and if a sequence is complete, + * insert the resulting Unicode(UCS4) value as cell value into the buffer. + */ +int linsert_byte(n, c) +unsigned int n, c; +{ + static char linsert_buf[6], linsert_buf_count = 0; + if (n == 1 && gmode & P_UNICODE && c & 0x80) { + if (linsert_buf_count >= sizeof(linsert_buf)) + linsert_buf_count = 0; + linsert_buf[linsert_buf_count++] = c; + c = 0; + if (linsert_buf_count > 1) + c = utf8_get_ucs(linsert_buf, linsert_buf_count); + if (!c) + return 1; + } + linsert_buf_count = 0; + return linsert(n, c); +} + /* * Insert "n" copies of the character "c" at the current location of dot. In * the easy case all that happens is the text is stored in the line. In the @@ -294,7 +371,7 @@ lp1->l_bp = lp2; *doto = n; *dotp = lp1; - ac.c = ((char)c & 0xff); + ac.c = c; cp1 = &(*dotp)->l_text[0]; while(n--) *cp1++ = ac; @@ -342,7 +419,7 @@ *--cp2 = *--cp1; } - ac.c = ((char)c & 0xff); + ac.c = c; while(n--) /* add the chars */ (*dotp)->l_text[(*doto)++] = ac; @@ -633,10 +710,7 @@ int n = 0; char qstr[NLINE]; - n = ((glo_quote_str || (Pmaster && Pmaster->quote_str)) - && quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, - line, qstr, NLINE)) - ? strlen(qstr) : 0; + n = quote_match(default_qstr(), line, qstr, NLINE, 1); for(; n < llength(line); n++) if(!isspace((unsigned char) lgetc(line, n).c)) @@ -767,7 +841,7 @@ if(!(p = p->next)) return(-1); - return(p->bufp[n % KBLOCK] & 0xff); + return(p->bufp[n % KBLOCK]); } else return(-1); diff -ru pine4.64/pico/main.c pine4.64.SuSE/pico/main.c --- pine4.64/pico/main.c 2004-03-26 23:36:45.000000000 +0100 +++ pine4.64.SuSE/pico/main.c 2006-02-14 14:45:25.000000000 +0100 @@ -119,7 +119,6 @@ #endif "\t +[line#] \tLine - start on line# line, default=1", "\t -v \t\tView - view file", -"\t -setlocale_ctype\tdo setlocale(LC_CTYPE) if available", "\t -no_setlocale_collate\tdo not do setlocale(LC_COLLATE)", "\t -version\tPico version number", "", @@ -146,9 +145,10 @@ int viewflag = FALSE; /* are we starting in view mode?*/ int starton = 0; /* where's dot to begin with? */ int setlocale_collate = 1; - int setlocale_ctype = 0; + int setlocale_ctype = 1; /* if a problem shows up, should be fixed */ char bname[NBUFN]; /* buffer name of file to read */ char *file_to_edit = NULL; + int line_information_on = FALSE; timeo = 600; Pmaster = NULL; /* turn OFF composer functionality */ @@ -247,7 +247,7 @@ curwp->w_flag |= WFMODE; /* and force an update */ if(timeoutset) - emlwrite("Checking for new mail every %D seconds", (void *)timeo); + emlwrite("Checking for new mail every %d seconds", (void *)timeo); forwline(0, starton - 1); /* move dot to specified line */ @@ -306,6 +306,12 @@ emlwrite("You may possibly have new mail.", NULL); } + if (c == (CTRL|'\\')){ + c = GetAccent(); + if (!c) + c = NODATA; + } + if(km_popped) switch(c){ case NODATA: @@ -327,14 +333,30 @@ mlerase(); } - f = FALSE; + f = (c == (CTRL|'J')); n = 1; + if (!line_information_on) + line_information_on = (c == (CTRL|'C')); + else + line_information_on = ((c == KEY_DOWN) || (c == KEY_UP) || + (c == KEY_RIGHT) || (c == KEY_LEFT) || + (c == (CTRL|'V')) || (c == (CTRL|'Y')) || + (c == (CTRL|'K')) || (c == (CTRL|'D')) || + (c == (CTRL|'F')) || (c == (CTRL|'B')) || + (c == (CTRL|'N')) || (c == (CTRL|'P')) || + (c == (CTRL|'A')) || (c == (CTRL|'E')) || + (c == (CTRL|'U')) || (c == (CTRL|'^'))) + && (c != (CTRL|'C')); #ifdef MOUSE clear_mfunc(mouse_in_content); #endif /* Do it. */ execute(normalize_cmd(c, fkm, 1), f, n); + if (line_information_on){ + c = (CTRL|'C'); + execute(normalize_cmd(c, fkm, 1), f, n); + } } } diff -ru pine4.64/pico/makefile.lnx pine4.64.SuSE/pico/makefile.lnx --- pine4.64/pico/makefile.lnx 2001-10-24 00:18:36.000000000 +0200 +++ pine4.64.SuSE/pico/makefile.lnx 2006-02-14 14:45:25.000000000 +0100 @@ -36,7 +36,7 @@ RM= rm -f LN= ln -s MAKE= make -OPTIMIZE= # -O +OPTIMIZE= -pipe PROFILE= # -pg DEBUG= -g -DDEBUG diff -ru pine4.64/pico/osdep/makefile pine4.64.SuSE/pico/osdep/makefile --- pine4.64/pico/osdep/makefile 2001-02-23 23:45:24.000000000 +0100 +++ pine4.64.SuSE/pico/osdep/makefile 2006-02-14 14:45:25.000000000 +0100 @@ -21,7 +21,7 @@ all: includer $(ALL) includer: includer.c - $(CC) -o includer includer.c + $(CC) $(EXTRACFLAGS) -o includer includer.c clean: $(RM) $(ALL) includer diff -ru pine4.64/pico/osdep/makefile.bas pine4.64.SuSE/pico/osdep/makefile.bas --- pine4.64/pico/osdep/makefile.bas 2000-12-30 00:01:57.000000000 +0100 +++ pine4.64.SuSE/pico/osdep/makefile.bas 2006-02-14 14:45:25.000000000 +0100 @@ -21,7 +21,7 @@ all: includer $(ALL) includer: includer.c - $(CC) -o includer includer.c + $(CC) $(EXTRACFLAGS) -o includer includer.c clean: $(RM) $(ALL) includer diff -ru pine4.64/pico/osdep/os-lnx.h pine4.64.SuSE/pico/osdep/os-lnx.h --- pine4.64/pico/osdep/os-lnx.h 2004-01-23 23:48:50.000000000 +0100 +++ pine4.64.SuSE/pico/osdep/os-lnx.h 2006-02-14 14:45:25.000000000 +0100 @@ -25,6 +25,9 @@ #include #include +/* Added for GLIBC port */ +#include + #define USE_DIRENT #include #include @@ -187,8 +190,10 @@ /* * Make sys_errlist visible */ -/* extern char *sys_errlist[]; */ -/* extern int sys_nerr; */ +#ifndef __GLIBC__ +extern char *sys_errlist[]; +extern int sys_nerr; +#endif #endif /* _PICO_OS_INCLUDED */ diff -ru pine4.64/pico/osdep/term.cap pine4.64.SuSE/pico/osdep/term.cap --- pine4.64/pico/osdep/term.cap 2004-07-22 00:06:24.000000000 +0200 +++ pine4.64.SuSE/pico/osdep/term.cap 2006-02-14 14:45:22.000000000 +0100 @@ -438,6 +438,12 @@ { int row, col; + if (sendnow){ + term.t_nrow = 23; + term.t_ncol = 80; + return 0; + } + /* * determine the terminal's communication speed and decide * if we need to do optimization ... diff -ru pine4.64/pico/osdep/unix pine4.64.SuSE/pico/osdep/unix --- pine4.64/pico/osdep/unix 2005-04-19 23:29:12.000000000 +0200 +++ pine4.64.SuSE/pico/osdep/unix 2006-02-14 14:45:24.000000000 +0100 @@ -1,3 +1,7 @@ +#ifdef LC_CTYPE +#include +#endif + int timeo = 0; time_t time_of_last_input; int (*pcollator)(); @@ -221,6 +225,15 @@ */ ttputc(c) { + if (gmode & P_UNICODE && c > 127) { + if (c & 0xf800) { + putc(0xe0 | (c >> 12), stdout); + putc(0x80 | ((c >> 6) & 0x3f), stdout); + } + else + putc(0xc0 | ((c >> 6) & 0x3f), stdout); + return putc(0x80 | (c & 0x3f), stdout); + } return(putc(c, stdout)); } @@ -362,6 +375,26 @@ GetKey() { int ch, status, cc; + static int saved; + + if(sendnow){ + ch = Pmaster && Pmaster->auto_cmds && *Pmaster->auto_cmds + ? *Pmaster->auto_cmds++ : NODATA; + + if(ch & 0x80 && Pmaster && Pmaster->hibit_entered) + *Pmaster->hibit_entered = 1; + + if (ch >= 0x00 && ch <= 0x1F) + ch = CTRL | (ch+'@'); + + return(ch); + } + + if (saved) { + ch = saved; + saved = 0; + return ch; + } if(!ReadyForKey(FUDGE-5)) return(NODATA); @@ -399,6 +432,10 @@ } ch = i; + if (gmode & P_UNICODE) { + saved = 0x80 | (ch & 0x3f); + ch = 0xc0 | ((ch >> 6) & 0x3f); + } } else{ if(islower((unsigned char)ch)) /* canonicalize if alpha */ @@ -2199,7 +2236,7 @@ #ifdef SIGCHLD (void) signal(SIGCHLD, SIG_DFL); #endif - if(execl("/bin/sh", "sh", "-c", eb, 0) < 0) + if(execl("/bin/sh", "sh", "-c", eb,(void*)0) < 0) exit(-1); } else { /* error! */ @@ -3871,6 +3908,9 @@ #ifdef LC_CTYPE if(ctype){ (void)setlocale(LC_CTYPE, ""); + /* For reference, see: "The Single UNIX(R) Specification, Version 2" */ + if (!strcmp(nl_langinfo(CODESET), "UTF-8")) + gmode ^= P_UNICODE; } #endif } diff -ru pine4.64/pico/pico.c pine4.64.SuSE/pico/pico.c --- pine4.64/pico/pico.c 2005-03-31 19:08:28.000000000 +0200 +++ pine4.64.SuSE/pico/pico.c 2006-02-14 14:45:24.000000000 +0100 @@ -138,6 +138,15 @@ pico_all_done = 0; km_popped = 0; + if (pm->auto_cmds){ + int i; +#define CTRL_X 24 + for (i = 0; pm->auto_cmds[i]; i++); + if ((i > 1) && (pm->auto_cmds[i - 2] == CTRL_X) && + ((pm->auto_cmds[i - 1] == 'y') || (pm->auto_cmds[i-1] == 'Y'))) + sendnow++; + } + if(!vtinit()) /* Init Displays. */ return(COMP_CANCEL); @@ -540,7 +549,7 @@ /* do the appropriate insertion */ /* pico never does C mode, this is simple */ - status = linsert(n, c); + status = linsert_byte(n, c); /* * Check to make sure we didn't go off of the screen @@ -552,7 +561,7 @@ register int k; for(j = k = 0; j < llength(curwp->w_dotp); j++, k++) - if(isspace((unsigned char)lgetc(curwp->w_dotp, j).c)){ + if(cell_isspace(curwp->w_dotp, j)){ if(lgetc(curwp->w_dotp, j).c == TAB) while(k+1 & 0x07) k++; @@ -686,6 +695,17 @@ return(FALSE); } + /* When we send a message using the command line we are going to + ignore if the user wants to spell check, we assume she already + did */ + if (sendnow){ + result = (*Pmaster->exittest)(Pmaster->headents, + redraw_pico_for_callback, Pmaster->allow_flowed_text); + if (!result) + pico_all_done = COMP_EXIT; + return(result ? FALSE : TRUE); + } + #ifdef SPELLER if(Pmaster->always_spell_check) if(spell(0, 0) == -1) @@ -810,7 +830,7 @@ is_cursor_line = (cursor_dotp == (*lp)); /* trim trailing whitespace, to be added back if flowing */ for(i = llength(*lp); i; i--) - if(!isspace(lgetc(*lp, i - 1).c)) + if(!cell_isspace(*lp, i - 1)) break; if(i != llength(*lp)){ int flow_line = 0; @@ -818,7 +838,7 @@ if(Pmaster && !Pmaster->strip_ws_before_send && lforw(*lp) != curbp->b_linep && llength(lforw(*lp)) - && !(isspace(lgetc(lforw(*lp), 0).c) + && !(cell_isspace(lforw(*lp), 0) || isquotedspace(lforw(*lp))) && !(llength(lforw(*lp)) == 3 && lgetc(lforw(*lp), 0).c == '-' @@ -833,7 +853,7 @@ ldelete(llength(*lp) - i, NULL); } } - else if(flow_line && i && isspace(lgetc(*lp, i).c)){ + else if(flow_line && i && cell_isspace(*lp, i)){ /* flowed line ending with whitespace other than space*/ curwp->w_doto = i; ldelete(llength(*lp) - i, NULL); @@ -845,7 +865,7 @@ } } if(Pmaster && Pmaster->allow_flowed_text - && llength(*lp) && isspace(lgetc(*lp, 0).c)){ + && llength(*lp) && cell_isspace(*lp, 0)){ /* space-stuff only if flowed */ curwp->w_doto = 0; if(is_cursor_line && cursor_doto) @@ -867,30 +887,6 @@ } /* - * Remove all trailing white space from the text - */ -int -stripwhitespace() -{ - int i; - LINE *cur_line = lforw(curbp->b_linep); - - do{ - /* we gotta test for the sigdash case here */ - if(!(cur_line->l_used == 3 && - lgetc(cur_line, 0).c == '-' && - lgetc(cur_line, 1).c == '-' && - lgetc(cur_line, 2).c == ' ')) - for(i = cur_line->l_used - 1; i >= 0; i--) - if(isspace(lgetc(cur_line, i).c)) - cur_line->l_used--; - else - break; - }while((cur_line = lforw(cur_line)) != curbp->b_linep); - return 0; -} - -/* * Abort. * Beep the beeper. Kill off any keyboard macro, etc., that is in progress. * Sometimes called as a routine, to do general aborting of stuff. @@ -1451,6 +1447,7 @@ LINE *dotp; int doto; short crinread; + char readch[6]; } PICOTEXT; #define PT(X) ((PICOTEXT *)(X)) @@ -1504,6 +1501,8 @@ * pico_readc - return char at current point. Up to calling routines * to keep cumulative count of chars. */ +#define PUTC(w, c) PT(w)->readch[PT(w)->crinread++] = c; +#define GETC(w) PT(w)->readch[--PT(w)->crinread]; int pico_readc(w, c) void *w; @@ -1512,12 +1511,20 @@ int rv = 0; if(PT(w)->crinread){ - *c = '\012'; /* return LF */ - PT(w)->crinread = 0; + *c = GETC(w); rv++; } else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */ - *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c; + int ch = lgetc(PT(w)->dotp, (PT(w)->doto)++).c; + if (gmode & P_UNICODE && ch & 0xff80) { + PUTC(w, 0x80 | (ch & 0x3f)) + if (ch & 0xf800) { /* three byte code */ + *c = 0xe0 | (ch >> 12); + PUTC(w, 0x80 | ((ch >> 6) & 0x3f)) + } else + *c = 0xc0 | ((ch >> 6) & 0x3f); + } else + *c = ch; rv++; } else if(PT(w)->dotp != PT(w)->linep){ /* return line break */ @@ -1525,7 +1532,7 @@ PT(w)->doto = 0; #if defined(DOS) || defined(OS2) *c = '\015'; - PT(w)->crinread++; + PUTC(w, '\012') #else *c = '\012'; /* return local eol! */ #endif @@ -1584,8 +1591,20 @@ rv++; } - else + else { + if (gmode & P_UNICODE && c & 0xffffff80) { + if (PT(w)->crinread >= 6) + PT(w)->crinread = 0; + PUTC(w, c); + c = 0; + if (PT(w)->crinread > 1) + c = utf8_get_ucs(PT(w)->readch, PT(w)->crinread); + if (!c) + return 1; + } + PT(w)->crinread = 0; rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL); + } return((rv) ? 1 : 0); /* return number written */ } diff -ru pine4.64/pico/pico.h pine4.64.SuSE/pico/pico.h --- pine4.64/pico/pico.h 2005-03-31 00:44:40.000000000 +0200 +++ pine4.64.SuSE/pico/pico.h 2006-02-14 14:45:23.000000000 +0100 @@ -219,6 +219,7 @@ void (*resize)(); /* callback handling screen resize */ void (*winch_cleanup)(); /* callback handling screen resize */ int arm_winch_cleanup; /* do the winch_cleanup if resized */ + int *auto_cmds; /* Initial keystroke commands */ HELP_T search_help; HELP_T ins_help; HELP_T ins_m_help; @@ -342,6 +343,22 @@ struct KBSTREE *left; } KBESC_T; +/* + * struct that will help us determine what the quote string of a line + * is. The "next" field indicates the presence of a possible continuation. + * The idea is that if a continuation fails, we free it and check for the + * remaining structure left + */ + +typedef enum {qsNormal, qsString, qsWord, qsChar} QStrType; + +typedef struct QSTRING { + char *value; /* possible quote string */ + QStrType qstype; /* type of quote string */ + struct QSTRING *next; /* possible continuation */ +} QSTRING_S; + + /* * Protos for functions used to manage keyboard escape sequences * NOTE: these may ot actually get defined under some OS's (ie, DOS, WIN) @@ -356,7 +373,7 @@ #define P_HICTRL 0x80000000 /* overwrite mode */ #define P_CHKPTNOW 0x40000000 /* do the checkpoint on entry */ #define P_DELRUBS 0x20000000 /* map ^H to forwdel */ -#define P_LOCALLF 0x10000000 /* use local vs. NVT EOL */ +#define P_UNICODE 0x10000000 /* run in Unicode mode */ #define P_BODY 0x08000000 /* start composer in body */ #define P_HEADEND 0x04000000 /* start composer at end of header */ #define P_VIEW MDVIEW /* read-only */ diff -ru pine4.64/pico/pilot.c pine4.64.SuSE/pico/pilot.c --- pine4.64/pico/pilot.c 2004-06-11 23:49:40.000000000 +0200 +++ pine4.64.SuSE/pico/pilot.c 2006-02-14 14:45:25.000000000 +0100 @@ -131,7 +131,7 @@ curbp->b_mode |= gmode; /* and set default modes*/ if(timeo) - emlwrite("Checking for new mail every %D seconds", (void *) timeo); + emlwrite("Checking for new mail every %d seconds", (void *) timeo); set_browser_title(PILOT_VERSION); FileBrowse(filedir, NSTRING, filename, NSTRING, NULL, 0, NULL); diff -ru pine4.64/pico/random.c pine4.64.SuSE/pico/random.c --- pine4.64/pico/random.c 2004-05-07 23:43:40.000000000 +0200 +++ pine4.64.SuSE/pico/random.c 2006-02-14 14:45:23.000000000 +0100 @@ -141,6 +141,11 @@ return(linsert(tabsize - (getccol(FALSE) % tabsize), ' ')); } +int Pisspace(int c) { + if (gmode & P_UNICODE && c > 127) /* to be extended when time permits */ + return 0; + return isspace((unsigned char)c); +} /* * Insert a newline. Bound to "C-M". @@ -170,7 +175,7 @@ /* pico's never in C mode */ if(Pmaster && Pmaster->allow_flowed_text && curwp->w_doto - && isspace(lgetc(curwp->w_dotp, curwp->w_doto - 1).c) + && cell_isspace(curwp->w_dotp, curwp->w_doto - 1) && !(curwp->w_doto == 3 && lgetc(curwp->w_dotp, 0).c == '-' && lgetc(curwp->w_dotp, 1).c == '-' @@ -181,7 +186,7 @@ */ int i, dellen; for(i = curwp->w_doto - 1; - i && isspace(lgetc(curwp->w_dotp, i - 1).c); + i && cell_isspace(curwp->w_dotp, i - 1); i--); dellen = curwp->w_doto - i; curwp->w_doto = i; @@ -364,7 +369,8 @@ else{ backchar(FALSE, 1); dotp = curwp->w_dotp; - gotobop(FALSE, 1); /* then go to the top of the para */ + swapimark(FALSE, 1); /* go back to the spot we marked before justify */ + /* We assume that no imarks have been set between fillpara and now */ } curwp->w_doto = 0; diff -ru pine4.64/pico/search.c pine4.64.SuSE/pico/search.c --- pine4.64/pico/search.c 2004-07-01 23:33:30.000000000 +0200 +++ pine4.64.SuSE/pico/search.c 2006-02-14 14:45:23.000000000 +0100 @@ -78,6 +78,10 @@ "\tbrackets. This string is the default search prompt.", "~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the", "\tsearch to be made with the default value.", +" ", +"~ Hitting ~^~N will reinsert the last string you searched for", +"\tso that you can edit it (in case you made a mistake entering the", +"\tsearch pattern the first time).", " ", "\tThe text search is not case sensitive, and will examine the", "\tentire message.", @@ -113,6 +117,8 @@ forwsearch(f, n) int f, n; { + LINE *lastline; /* line position before scan */ + int lastoff; /* last position within line */ register int status; int wrapt = FALSE, wrapt2 = FALSE; int repl_mode = FALSE; @@ -235,10 +241,20 @@ mlerase(); FWS_RETURN(TRUE); + case (CTRL|'P'): + deletepara(0, 1); + mlerase(); + FWS_RETURN(TRUE); + case (CTRL|'R'): /* toggle replacement option */ repl_mode = !repl_mode; break; + case (CTRL|'X'): + deltext(f,n); + mlerase(); + FWS_RETURN(TRUE); + default: if(status == ABORT) emlwrite("Search Cancelled", NULL); @@ -259,26 +275,15 @@ } } + lastline = curwp->w_dotp; /* line position before scan */ + lastoff = curwp->w_doto; /* last position within line */ + /* - * This code is kind of dumb. What I want is successive C-W 's to - * move dot to successive occurences of the pattern. So, if dot is - * already sitting at the beginning of the pattern, then we'll move - * forward a char before beginning the search. We'll let the - * automatic wrapping handle putting the dot back in the right - * place... + * Successive C-W 's should move the dot to successive occurences + * of the pattern. So move the dot forward one char before the search + * and if the seach fails, put it back were it was. */ - status = 0; /* using "status" as int temporarily! */ - while(1){ - if(defpat[status] == '\0'){ - forwchar(0, 1); - break; /* find next occurence! */ - } - - if(status + curwp->w_doto >= llength(curwp->w_dotp) || - !eq(defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c)) - break; /* do nothing! */ - status++; - } + forwchar(0, 1); /* search for the pattern */ @@ -290,6 +295,8 @@ /* and complain if not there */ if (status == FALSE){ emlwrite("\"%s\" not found", defpat); + curwp->w_dotp = lastline; /* line position before scan */ + curwp->w_doto = lastoff; /* last position within line */ } else if((gmode & MDREPLACE) && repl_mode == TRUE){ status = replace_pat(defpat, &wrapt2); /* replace pattern */ @@ -499,7 +506,7 @@ register int s; int i = 0; char tpat[NPAT+20]; - EXTRAKEYS menu_pat[8]; + EXTRAKEYS menu_pat[10]; menu_pat[i = 0].name = "^Y"; menu_pat[i].label = "FirstLine"; @@ -517,6 +524,11 @@ KS_OSDATASET(&menu_pat[i], KS_NONE); if(!repl_mode){ + menu_pat[++i].name = "^X"; + menu_pat[i].label = "EndText"; + menu_pat[i].key = (CTRL|'X'); + KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^T"; menu_pat[i].label = "LineNumber"; menu_pat[i].key = (CTRL|'T'); @@ -532,6 +544,11 @@ menu_pat[i].key = (CTRL|'O'); KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^P"; + menu_pat[i].label = "Delete Para"; + menu_pat[i].key = (CTRL|'P'); + KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^U"; menu_pat[i].label = "FullJustify"; menu_pat[i].key = (CTRL|'U'); @@ -630,7 +647,7 @@ register int s; int i; char tpat[NPAT+20]; - EXTRAKEYS menu_pat[7]; + EXTRAKEYS menu_pat[9]; menu_pat[i = 0].name = "^Y"; menu_pat[i].label = "FirstLine"; @@ -643,6 +660,11 @@ KS_OSDATASET(&menu_pat[i], KS_NONE); if(text_mode){ + menu_pat[++i].name = "^X"; + menu_pat[i].label = "EndText"; + menu_pat[i].key = (CTRL|'X'); + KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^T"; menu_pat[i].label = "LineNumber"; menu_pat[i].key = (CTRL|'T'); @@ -658,6 +680,11 @@ menu_pat[i].key = (CTRL|'O'); KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^P"; + menu_pat[i].label = "Delete Para"; + menu_pat[i].key = (CTRL|'P'); + KS_OSDATASET(&menu_pat[i], KS_NONE); + menu_pat[++i].name = "^U"; menu_pat[i].label = "FullJustify"; menu_pat[i].key = (CTRL|'U'); @@ -705,9 +732,19 @@ register int c; /* character at current position */ register LINE *matchline; /* current line during matching */ register int matchoff; /* position in matching line */ - register char *patptr; /* pointer into pattern */ + register char *patptr = patrn; /* pointer into pattern */ + unsigned char *tmp; register int stopoff; /* offset to stop search */ register LINE *stopline; /* line to stop search */ + unsigned int ucspat[NPAT], ucspos = 0, match; + + /* In Unicode mode, we've to create an UCS-4 vector from the pattern: */ + while (gmode & P_UNICODE && *patptr != 0 && ucspos < NPAT) { + tmp = patptr; + ucspat[ucspos++] = utf8_get_ucs_string(&tmp, strlen(patptr)); + patptr = tmp; + } + ucspat[ucspos] = 0; /* terminate the int vector with a zero int */ *wrapt = FALSE; @@ -759,15 +796,23 @@ else c = lgetc(curline, curoff++).c; /* get the char */ + if (gmode & P_UNICODE) { + match = ucspat[ucspos=0]; + } else + match = patrn[0]; + /* test it against first char in pattern */ - if (eq(c, patrn[0]) != FALSE) { /* if we find it..*/ + if (eq(c, match) != FALSE) { /* if we find it..*/ /* setup match pointers */ matchline = curline; matchoff = curoff; patptr = &patrn[0]; /* scan through patrn for a match */ - while (*++patptr != 0) { + while (1) { + if (!(match = *++patptr) || (gmode & P_UNICODE && + !(match = ucspat[++ucspos]))) + break; /* advance all the pointers */ if (matchoff == llength(matchline)) { /* advance past EOL */ @@ -781,7 +826,7 @@ return(FALSE); /* and test it against the pattern */ - if (eq(*patptr, c) == FALSE) + if (eq(match, c) == FALSE) goto fail; } @@ -820,10 +865,10 @@ int maxlength; /* maximum chars in destination */ { - char c; /* current char to translate */ + unsigned char c; /* current char to translate */ /* scan through the string */ - while ((c = *srcstr++) != 0) { + while ((c = (unsigned char) *srcstr++) != 0) { if (c == '\n') { /* its an EOL */ *deststr++ = '<'; *deststr++ = 'N'; diff -ru pine4.64/pico/word.c pine4.64.SuSE/pico/word.c --- pine4.64/pico/word.c 2004-05-07 23:45:04.000000000 +0200 +++ pine4.64.SuSE/pico/word.c 2006-02-14 14:45:23.000000000 +0100 @@ -54,7 +54,7 @@ return(FALSE); for(bp = cnt = i = 0; cnt < llength(curwp->w_dotp) && !bp; cnt++, i++){ - if(isspace((unsigned char) lgetc(curwp->w_dotp, cnt).c)){ + if(cell_isspace(curwp->w_dotp, cnt)){ first = 0; if(lgetc(curwp->w_dotp, cnt).c == TAB) while(i+1 & 0x07) @@ -84,7 +84,7 @@ if(!(curbp->b_flag & BFWRAPOPEN) && lforw(curwp->w_dotp) != curbp->b_linep && llength(lforw(curwp->w_dotp)) - && !isspace((unsigned char) lgetc(lforw(curwp->w_dotp), 0).c) + && !cell_isspace(lforw(curwp->w_dotp), 0) && (llength(curwp->w_dotp) + llength(lforw(curwp->w_dotp)) < fillcol)){ gotoeol(0, 1); /* then pull text up from below */ if(lgetc(curwp->w_dotp, curwp->w_doto - 1).c != ' ') @@ -360,44 +360,1172 @@ && isalnum((unsigned char)lgetc(curwp->w_dotp, curwp->w_doto).c)); } +/* Support of indentation of paragraphs */ +#define UCH(c) ((unsigned char) (c)) +#define NBSP UCH('\240') +#define ISspace(c) (UCH(c) == ' ' || UCH(c) == TAB || UCH(c) == NBSP) +#define is_indent_char(c) (((c) == '.' || (c) == '}' || (c) == RPAREN || \ + (c) == '*' || (c) == '+' || is_a_digit(c) || \ + ISspace(c) || (c) == '-' || \ + (c) == ']') ? 1 : 0) +#define allowed_after_digit(c,word,k) ((((c) == '.' && \ + allowed_after_period(next((word),(k)))) ||\ + (c) == RPAREN || (c) == '}' || (c) == ']' ||\ + ISspace(c) || is_a_digit(c) || \ + ((c) == '-' ) && \ + allowed_after_dash(next((word),(k)))) \ + ? 1 : 0) +#define allowed_after_period(c) (((c) == RPAREN || (c) == '}' || (c) == ']' ||\ + ISspace(c) || (c) == '-' || \ + is_a_digit(c)) ? 1 : 0) +#define allowed_after_parenth(c) (ISspace(c) ? 1 : 0) +#define allowed_after_space(c) (ISspace(c) ? 1 : 0) +#define allowed_after_braces(c) (ISspace(c) ? 1 : 0) +#define allowed_after_star(c) ((ISspace(c) || (c) == RPAREN ||\ + (c) == ']' || (c) == '}') ? 1 : 0) +#define allowed_after_dash(c) ((ISspace(c) || is_a_digit(c)) ? 1 : 0) +#define EOLchar(c) (((c) == '.' || (c) == ':' || (c) == '?' ||\ + (c) == '!') ? 1 : 0) + +int is_indent PROTO((char *, int)); +int indent_match PROTO(( char *, LINE *, char *, int, int)); +int get_indent_raw_line PROTO((char *, char *, char *, int, int, int)); + +/* Extended justification support */ + +#define is_cquote(c) ((c) == '>' || (c) == '|' || (c) == ']' || (c) == ':') +#define is_cword(c) ((((c) >= 'a') && ((c) <= 'z')) || \ + (((c) >= 'A') && ((c) <= 'Z')) || \ + (((c) >= '0') && ((c) <= '9')) || \ + ((c) == ' ') || ((c) == '?') || \ + ((c) == '@') || ((c) == '.') || \ + ((c) == '!') || ((c) == '\'') || \ + ((c) == ',') || ((c) == '\"') ? 1 : 0) +#define isaquote(c) ((c) == '\"' || (c) == '\'') +#define is8bit(c) ((((int) (c)) & 0x80) ? 1 : 0) +#define iscontrol(c) (iscntrl(((int) (c)) & 0x7f) ? 1 : 0) +#define forbidden(c) (((c) == '\"') || ((c) == '\'') || ((c) == '$') ||\ + ((c) == ',') || ((c) == '.') || ((c) == '-') ||\ + ((c) == LPAREN) || ((c) == '/')|| ((c) == '`') ||\ + ((c) == '{') || ((c) == '\\') || (iscontrol((c))) ||\ + (((c) >= '0') && ((c) <= '9'))) +#define is_cletter(c) ((((c) >= 'a') && ((c) <= 'z'))) ||\ + ((((c) >= 'A') && ((c) <= 'Z'))||\ + is8bit(c)) +#define is_cnumber(c) ((c) >= '0' && (c) <= '9') +#define allwd_after_word(c) (((c) == ' ') || ((c) == '>') || is_cletter(c)) +#define allwd_after_qsword(c) (((c) != '\\') && ((c) != RPAREN)) +#define before(word,i) (((i) > 0) ? (word)[(i) - 1] : 0) +#define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) +#define now(w,i) ((w)[(i)]) +#define is_qsword(c) (((c) == ':') || ((c) == RPAREN) ? 1 : 0) +#define is_colon(c) (((c) == ':') ? 1 : 0) +#define is_rarrow(c) (((c) == '>') ? 1 : 0) +#define is_tilde(c) (((c) == '~') ? 1 : 0) +#define is_dash(c) (((c) == '-') ? 1 : 0) +#define is_pound(c) (((c) == '#') ? 1 : 0) +#define is_space(c) (((c) == ' ') ? 1 : 0) +#define is_a_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0) +#define is_allowed(c) (is_cquote(c) || is_cword(c) || is_dash(c) || \ + is_pound(c)) + +/* Internal justification functions */ + +QSTRING_S *is_quote PROTO((char *, char *, int)); +QSTRING_S *copy_qs PROTO((QSTRING_S *)); +QSTRING_S *qs_normal_part PROTO((QSTRING_S *)); +QSTRING_S *qs_remove_trailing_spaces PROTO((QSTRING_S *)); +QSTRING_S *trim_qs_from_cl PROTO((QSTRING_S *, QSTRING_S *, QSTRING_S *)); +QSTRING_S *fix_qstring PROTO((QSTRING_S *, QSTRING_S *, QSTRING_S *)); +QSTRING_S *qs_add PROTO((char *, char *, QStrType, int, int, int, int)); +QSTRING_S *remove_qsword PROTO((QSTRING_S *)); +QSTRING_S *qs_quote_match PROTO((char *, LINE *, char *, int)); +int qstring_is_normal PROTO((QSTRING_S *)); +int exists_good_part PROTO((QSTRING_S *)); +int value_is_space PROTO((char *)); +int strcmp_qs PROTO((char *, char *)); +int count_levels_qstring PROTO((QSTRING_S *)); +int same_qstring PROTO((QSTRING_S *, QSTRING_S *)); +int advance_quote_string PROTO((char *, char *, int)); +int strlenis PROTO((char *)); +void linencpy PROTO((char *, LINE *, int)); + +char * +default_qstr() +{ + return (glo_quote_str ? glo_quote_str + : (Pmaster && Pmaster->quote_str) ? Pmaster->quote_str : ""); +} + +/* + * This function creates a qstring pointer with the information that + * is_quote handles to it. + * Parameters: + * qs - User supplied quote string + * word - The line of text that the user is trying to read/justify + * beginw - Where we need to start copying from + * endw - Where we end copying + * offset - Any offset in endw that we need to account for + * typeqs - type of the string to be created + * neednext - boolean, indicating if we need to compute the next field + * of leave it NULL + * + * It is a mistake to call this function if beginw >= endw + offset. + * Please note the equality sign in the above inequality (this is because + * we always assume that qstring->value != ""). + */ + +QSTRING_S * +qs_add(qs, word, typeqs, beginw, endw, offset, neednext) + char *qs; + char word[NSTRING]; + QStrType typeqs; + int beginw; + int endw; + int offset; + int neednext; +{ + QSTRING_S *qstring, *nextqs = (QSTRING_S *) NULL; + int i; + + qstring = (QSTRING_S *) malloc (sizeof(QSTRING_S)); + memset (qstring, 0, sizeof(QSTRING_S)); + qstring->qstype = qsNormal; + + if (beginw == 0){ + beginw = endw + offset; + qstring->qstype = typeqs; + } + + if (neednext) + nextqs = is_quote(qs, word+beginw, 1); + + qstring->value = (char *) malloc((beginw+1)*sizeof(char)); + strncpy(qstring->value, word, beginw); + qstring->value[beginw] = '\0'; + + qstring->next = nextqs; + + return qstring; +} + + +int +qstring_is_normal(cl) + QSTRING_S *cl; +{ + for (;cl && (cl->qstype == qsNormal); cl = cl->next); + + return cl ? 0 : 1; +} + +void +free_qs(cl) + QSTRING_S **cl; +{ + if (!(*cl)) + return; + + if ((*cl)->next) + free_qs(&((*cl)->next)); + + (*cl)->next = (QSTRING_S *) NULL; + + if ((*cl)->value) + free((void *)(*cl)->value); + + (*cl)->value = (char *) NULL; + + free((void *)(*cl)); + *cl = (QSTRING_S *) NULL; +} + +QSTRING_S * +copy_qs(cl) + QSTRING_S *cl; +{ + QSTRING_S *qs; + + if (!cl) + return (QSTRING_S *)NULL; + + qs = (QSTRING_S *) malloc (sizeof(QSTRING_S)); + memset (qs, 0, sizeof(QSTRING_S)); + + qs->value = (char *) malloc ((strlen(cl->value)+1)*sizeof(char)); + strcpy(qs->value, cl->value); + qs->qstype = cl->qstype; + qs->next = copy_qs(cl->next); + return qs; +} + +/* + * Given a quote string, this function returns the part that is the leading + * normal part of it. (the normal part is the part that is tagged qsNormal, + * that is to say, the one that is not controversial at all (like qsString + * for example). + */ +QSTRING_S * +qs_normal_part(cl) + QSTRING_S *cl; +{ + + if (!cl) /* nothing in, nothing out */ + return cl; + + if (cl->qstype != qsNormal) + free_qs(&cl); + + if (cl) + cl->next = qs_normal_part(cl->next); + + return cl; +} + +int +value_is_space(value) + char *value; +{ + for (; value && *value && ISspace(*value); value++); + + return value && *value ? 0 : 1; +} + +/* + * this function removes trailing spaces from a quote string, but leaves the + * last one if there are trailing spaces + */ +QSTRING_S * +qs_remove_trailing_spaces(cl) + QSTRING_S *cl; +{ + QSTRING_S *rl = cl; + + if (!cl) /* nothing in, nothing out */ + return cl; + + if (cl->next) + cl->next = qs_remove_trailing_spaces(cl->next); + else{ + if (value_is_space(cl->value)) + free_qs(&cl); + else{ + int i, l; + i = l = strlen(cl->value) - 1; + while (cl->value && cl->value[i] + && ISspace(cl->value[i])) + i--; + i += (i < l) ? 2 : 1; + cl->value[i] = '\0'; + } + } + + return cl; +} /* - * Return number of quotes if whatever starts the line matches the quote string + * This function returns if two strings are the same quote string. + * The call is not symmetric. cl must preceed the line nl. This function + * should be called for comparing the last part of cl and nl. + */ +int +strcmp_qs(valuecl, valuenl) + char *valuecl; + char *valuenl; +{ + int j; + + for (j = 0; valuecl[j] && (valuecl[j] == valuenl[j]); j++); + return !strcmp(valuecl, valuenl) + || (valuenl[j] && value_is_space(valuenl+j) + && value_is_space(valuecl+j) + && strlenis(valuecl+j) >= strlenis(valuenl+j)) + || (!valuenl[j] && value_is_space(valuecl+j)); +} + +int +count_levels_qstring(cl) + QSTRING_S *cl; +{ + int count; + for (count = 0; cl ; count++, cl = cl->next); + + return count; +} + +/* + * This function returns the number of agreements between + * cl and nl. The call is not symmetric. cl must be the line + * preceding nl. */ -quote_match(q, l, buf, buflen) +int +same_qstring(cl,nl) + QSTRING_S *cl; + QSTRING_S *nl; +{ + int same = 0, done = 0; + + for (;cl && nl && !done; cl = cl->next, nl = nl->next) + if ((cl->qstype == nl->qstype) && (!strcmp(cl->value, nl->value) + || ((!cl->next) && strcmp_qs(cl->value, nl->value)))) + same++; + else + done++; + + return same; +} + +QSTRING_S * +trim_qs_from_cl(cl, nl, pl) + QSTRING_S *cl; + QSTRING_S *nl; + QSTRING_S *pl; +{ + QSTRING_S *cqstring = pl ? pl : nl; + QSTRING_S *tl = pl ? pl : nl; + int p, c; + + if (qstring_is_normal(tl)) + return tl; + + p = same_qstring(pl ? pl : cl, pl ? cl : nl); + + for (c = 1; c < p; c++, cl = cl->next, tl = tl->next); + + /* + * cl->next and tl->next differ, it may be because cl->next does not + * exist or tl->next does not exist or simply both exist but are + * different. In this last case, it may be that cl->next->value is made + * of spaces. If this is the case, tl advances once more. + */ + + if (tl->next){ + if (cl && cl->next && value_is_space(cl->next->value)) + tl = tl->next; + if (tl->next) + free_qs(&(tl->next)); + } + + if (!p) + free_qs(&cqstring); + + return cqstring; +} + +/* This function trims cl so that it returns a real quote string based + * on information gathered from the previous and next lines. pl and cl are + * also trimmed, but that is done in another function, not here. + */ +QSTRING_S * +fix_qstring(cl, nl, pl) + QSTRING_S *cl; + QSTRING_S *nl; + QSTRING_S *pl; +{ + QSTRING_S *cqstring = cl, *nqstring = nl, *pqstring = pl; + int c, n; + + if (qstring_is_normal(cl)) + return cl; + + c = count_levels_qstring(cl); + n = same_qstring(cl,nl); + + if (!n){ /* no next line or no agreement with next line */ + int p = same_qstring(pl, cl); /* number of agreements between pl and cl */ + QSTRING_S *tl; /* test line */ + + /* + * Here p <= c, so either p < c or p == c. If p == c, we are done, + * and return cl. If not, there are two cases, either p == 0 or + * 0 < p < c. In the first case, we do not have enough evidence + * to return anything other than the normal part of cl, in the second + * case we can only return p levels of cl. + */ + + if (p == c) + tl = cqstring; + else{ + if (p){ + for (c = 1; c < p; c++) + cl = cl->next; + free_qs(&(cl->next)); + tl = cqstring; + } + else{ + int done = 0; + QSTRING_S *al = cl; /* another line */ + /* + * Ok, we reaelly don't have enough evidence to return anything, + * different from the normal part of cl, but it could be possible + * that we may want to accept the not-normal part, so we better + * make an extra test to determine what needs to be freed + */ + while (pl && cl && !strucmp(cl->value, pl->value)){ + cl = cl->next; + pl = pl->next; + } + if (pl && cl && strcmp_qs(pl->value, cl->value)) + cl = cl->next; /* next level differs only in spaces */ + while (!done){ + while (cl && cl->qstype == qsNormal) + cl = cl->next; + if (cl){ + if ((cl->qstype == qsString) + && (cl->value[strlen(cl->value) - 1] == '>')) + cl = cl->next; + else done++; + } + else done++; + } + if (al == cl){ + free_qs(&(cl)); + tl = cl; + } + else { + while (al && (al->next != cl)) + al = al->next; + cl = al; + if (cl && cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + } + } + return tl; + } + if (n + 1 < c){ /* if there are not enough agreements */ + int p = same_qstring(pl, cl); /* number of agreement between pl and cl */ + QSTRING_S *tl; /* test line */ + + /* + * There's no way we can use cl in this case, but we can use + * part of cl, this is if pl does not have more agreements + * with cl. + */ + + if (p == c) + tl = cqstring; + else{ + int m = p < n ? n : p; + for (c = 1; c < m; c++){ + pl = pl ? pl->next : (QSTRING_S *) NULL; + nl = nl ? nl->next : (QSTRING_S *) NULL; + cl = cl->next; + } + if ((p == n) && pl && pl->next && nl && nl->next + && ((cl->next->qstype == pl->next->qstype) + || (cl->next->qstype == nl->next->qstype)) + && (strcmp_qs(cl->next->value, pl->next->value) + || strcmp_qs(pl->next->value, cl->next->value) + || strcmp_qs(cl->next->value, nl->next->value) + || strcmp_qs(nl->next->value, cl->next->value))) + cl = cl->next; /* next level differs only in spaces */ + if (cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + return tl; + } + if (n + 1 == c){ + int p = same_qstring(pl, cl); + QSTRING_S *tl; /* test line */ + + /* + * p <= c, so p <= n+1, which means p < n + 1 or p == n + 1. + * If p < n + 1, then p <= n. + * so we have three possibilities: + * p == n + 1 or p == n or p < n. + * In the first case we copy p == n + 1 == c levels, in the second + * and third case we copy n levels, and check if we can copy the + * n + 1 == c level. + */ + + if (p == n + 1) /* p == c, in the above sense of c */ + tl = cl; /* use cl, this is enough evidence */ + else{ + for (c = 1; c < n; c++) + cl = cl->next; + /* + * Here c == n, we only have one more level of cl, and at least one + * more level of nl + */ + if (cl->next->qstype == qsNormal) + cl = cl->next; + if (cl->next) + free_qs(&(cl->next)); + tl = cqstring; + } + return tl; + } + if (n == c) /* Yeah!!! */ + return cqstring; +} + +/* + * This function flattens the quote string returned to us by is_quote. A + * crash in this function implies a bug elsewhere. + */ +void +flatten_qstring(qs, buff, bufflen) + QSTRING_S *qs; + char *buff; + int bufflen; +{ + int i, j; + + if(!buff || bufflen <= 0) + return; + + for (i = 0; qs; qs = qs->next) + for (j = 0; i < bufflen - 1 + && (qs->value[j]) && (buff[i++] = qs->value[j]); j++); + buff[i] = '\0'; +} + +/* + * Given a string, we return the position where the function thinks that + * the quote string is over, if you are ever thinking of fixing something, + * you got to the right place. Memory freed by caller. Experience shows + * that it only makes sense to initialize memory when we need it, not at + * the start of this function. + */ +QSTRING_S * +is_quote (qs,word, been_here) + char *qs; + char word[NSTRING]; + int been_here; +{ + int i = 0, j, c, nxt, prev, finished = 0, offset; + QSTRING_S *qstring = (QSTRING_S *) NULL; + + if (!word || !word[0]) + return (QSTRING_S *) NULL; + + while (!finished){ + /* + * Before we apply our rules, let's advance past the quote string + * given by the user, this will avoid not recognition of the + * user's indent string and application of the arbitrary rules + * below. Notice that this step may bring bugs into this + * procedure, but these bugs will only appear if the indent string + * is really really strange and the text to be justified + * cooperates a lot too, so in general this will not be a problem. + * If you are concerned about this bug, simply remove the + * following lines after this comment and before the "switch" + * command below and use a more normal quote string!. + */ + i += advance_quote_string(qs, word, i); + if (!word[i]) /* went too far? */ + return qs_add(qs, word, qsNormal, 0, i, 0, 0); + + switch (c = now(word,i)){ + case NBSP: + case TAB : + case ' ' : { QSTRING_S *nextqs, *testqs = NULL; + int j; + + for (; ISspace(word[i]); i++); + nextqs = is_quote(qs,word+i, 1); + /* + * Merge qstring and nextqs, since this is an artificial + * separation, unless nextqs is of different type. + * What this means in practice is that if + * qs->qstype == qsNormal and qs->next != NULL, then + * qs->next->qstype != qsNormal. + * + * Can't use qs_add to merge because it could lead + * to an infinite loop (e.g a line "^ ^"). + */ + if (nextqs){ + if(nextqs->qstype == qsNormal){ + i += strlen(nextqs->value); + testqs = copy_qs(nextqs->next); + } + else + testqs = copy_qs(nextqs); + free_qs(&nextqs); + } + + qstring = (QSTRING_S *) malloc (sizeof(QSTRING_S)); + memset (qstring, 0, sizeof(QSTRING_S)); + + qstring->value = (char *) malloc((i+1)*sizeof(char)); + strncpy(qstring->value, word, i); + qstring->value[i] = '\0'; + qstring->qstype = qsNormal; + qstring->next = testqs; + + return qstring; + } + break; + + case RPAREN: /* parenthesis ')' */ + if ((i != 0) || ((i == 0) && been_here)) + i++; + else + if (i == 0) + return qs_add(qs, word, qsChar, i, i, 1, 1); + else + finished++; + break; + + case ';': + case ':': /* colon */ + case '~': nxt = next(word,i); + if (is_tilde(c) && (nxt == '/')) + finished++; + else if (is_cquote(c) + || is_cquote(nxt) + || ((c != '~') && (nxt == RPAREN)) + || ((i != 0) && is_space(nxt)) + || is_cquote(prev = before(word,i)) + || (is_space(prev) && !is_tilde(c)) + || (is_tilde(c) && nxt != '/')) + i++; + else if (i == 0 && been_here) + return qs_add(qs, word, qsChar, i, i, 1, 1); + else + finished++; + break; + + case '<' : + case '=' : + case '-' : offset = is_cquote(nxt = next(word,i)) ? 2 + : ((nxt == c) + && is_cquote(next(word,i+1))) ? 3 : -1; + + if (offset > 0) + return qs_add(qs, word, qsString, i, i, offset, 1); + else + finished++; + break; + + case '[' : + case '+' : /* accept +>, *> */ + case '*' : if (is_rarrow(nxt = next(word, i)) || /* stars */ + (is_space(nxt) && is_rarrow(next(word,i+1)))) + i++; + else + finished++; + break; + + case '^' : + case '!' : + case '%' : + case '#' : if (next(word,i) != c) + return qs_add(qs, word, qsChar, i, i+1, 0, 1); + else + finished++; + break; + + default: + if (is_cquote(c)) + i++; + else if (is_cletter(c)){ + for (j = i; (is_cletter(nxt = next(word,j)) || is_cnumber(nxt)) + && !(is_space(nxt));j++); + /* + * The whole reason why we are splitting the quote + * string is so that we will be able to accept quote + * strings that are strange in some way. Here we got to + * a point in which a quote string might exist, but it + * could be strange, so we need to create a "next" field + * for the quote string to warn us that something + * strange is coming. We need to confirm if this is a + * good choice later. For now we will let it pass. + */ + if (isaword(word,i,j) || isamailbox(word,i,j)){ + int offset; + QStrType qstype; + + offset = (is_cquote(c = next(word,j)) + || (c == RPAREN)) ? 2 + : ((is_space(c) + && is_cquote(next(word,j+1))) ? 3 : -1); + + qstype = (is_cquote(c) || (c == RPAREN)) + ? (is_qsword(c) ? qsWord : qsString) + : ((is_space(c) && is_cquote(next(word,j+1))) + ? (is_qsword(next(word,j+1)) + ? qsWord : qsString) + : qsString); + + /* + * qsWords are valid quote strings only when + * they are followed by text. + */ + if ((offset > 0) && (qstype == qsWord) && + !(allwd_after_qsword(now(word,j + offset)))) + offset = -1; + + if (offset > 0) + return qs_add(qs, word, qstype, i, j, offset, 1); + } + finished++; + } + else{ + if(!forbidden(c)) + return qs_add(qs, word, qsChar, 0, 1, 0, 1); + else /* chao pescao */ + finished++; + } + break; + } /* End Switch */ + } /* End while */ + + if (i > 0) + qstring = qs_add(qs, word, qsNormal, 0, i, 0, 0); + + return qstring; +} + +void +linencpy(word, l, buflen) +char word[NSTRING]; +LINE *l; +int buflen; +{ + int i = 0; + for (;(i < buflen) && (i < llength(l)) && (word[i] = (char)lgetc(l,i).c); i++); + word[buflen - 1] = '\0'; +} + +int +isaword(word,i,j) +char word[NSTRING]; +int i; +int j; +{ + return i <= j && is_cletter(word[i]) ? + (i < j ? isaword(word,i+1,j) : 1) : 0; +} + +int +isamailbox(word,i,j) +char word[NSTRING]; +int i; +int j; +{ + return i <= j && (is_cletter(word[i]) || is_a_digit(word[i]) + || word[i] == '.') + ? (i < j ? isamailbox(word,i+1,j) : 1) : 0; +} + +/* + * This function returns the quote string as a structure. In this way we + have two ways to get the quote string: as a char * or as a QSTRING_S * + directly. + */ +QSTRING_S * +qs_quote_match(q, l, rqstr, rqstrlen) + char *q; + LINE *l; + char *rqstr; + int rqstrlen; +{ + char GLine[NSTRING] = {'\0'}, NLine[NSTRING] = {'\0'}, + PLine[NSTRING] = {'\0'}; + LINE *nl = l != curbp->b_linep ? lforw(l) : NULL; + LINE *pl = lback(l) != curbp->b_linep ? lback(l) : NULL; + int plb = 1; + + linencpy(GLine, l, NSTRING); + + if (nl) + linencpy(NLine, nl, NSTRING); + + if (pl){ + linencpy(PLine, pl, NSTRING); + if(lback(pl) != curbp->b_linep){ + char PPLine[NSTRING] = {'\0'}; + + linencpy(PPLine, lback(pl), NSTRING); + plb = line_isblank(q, PLine, GLine, PPLine, NSTRING); + } + } + + return do_quote_match(q, GLine, NLine, PLine, rqstr, rqstrlen, plb); +} + +/* + * Return number of quotes if whatever starts the line matches the quote + * string. + * rqstring is pointer to raw qstring, buf points to processed qstring + */ +quote_match(q, l, buf, buflen, raw) char *q; LINE *l; char *buf; int buflen; + int raw; { - register int i, n, j, qb; + QSTRING_S *qs; + char rqstr[NSTRING] = {'\0'}; - *buf = '\0'; - if(*q == '\0') - return(1); - - qb = (strlen(q) > 1 && q[strlen(q)-1] == ' ') ? 1 : 0; - for(n = 0, j = 0; ;){ - for(i = 0; j <= llength(l) && qb ? q[i+1] : q[i]; i++, j++) - if(q[i] != lgetc(l, j).c) - return(n); - - n++; - if((!qb && q[i] == '\0') || (qb && q[i+1] == '\0')){ - if(strlen(buf) + strlen(q) + 1 < buflen){ - strcat(buf,q); - if(qb && (j > llength(l) || lgetc(l, j).c != ' ')) - buf[strlen(buf)-1] = '\0'; - } + qs = qs_quote_match(q, l, rqstr, NSTRING); + flatten_qstring(qs, buf, buflen); + if (qs) + free_qs(&qs); + + if(raw){ + strncpy(buf, rqstr, buflen < NSTRING ? buflen : NSTRING); + buf[buflen-1] = '\0'; + } + + return buf && buf[0] ? strlen(buf) : 0; +} + +/* + This routine removes the last part that is qsword or qschar that is not + followed by a normal part. This means that if a qsword or qschar is + followed by a qsnormal (or qsstring), we accept the qsword (or qschar) + as part of a quote string. + */ + +QSTRING_S * +remove_qsword(cl) + QSTRING_S *cl; +{ + QSTRING_S *np = cl; + QSTRING_S *cp = np; /* this variable trails cl */ + + while(1){ + while (cl && cl->qstype == qsNormal) + cl = cl->next; + + if (cl){ + if (((cl->qstype == qsWord) || (cl->qstype == qsChar)) + && !exists_good_part(cl)){ + if (np == cl) /* qsword or qschar at the beginning */ + free_qs(&cp); + else{ + while (np->next != cl) + np = np->next; + free_qs(&(np->next)); + } + break; + } + else + cl = cl->next; + } + else + break; + } + return cp; +} + +int +exists_good_part (cl) + QSTRING_S *cl; +{ + return (cl ? (((cl->qstype != qsWord) && (cl->qstype != qsChar) + && !value_is_space(cl->value)) + ? 1 + : exists_good_part(cl->next)) + : 0); +} + +line_isblank(q, GLine, NLine, PLine, buflen) + char *q, *GLine, *PLine, *NLine; + int buflen; +{ + int n = 0; + QSTRING_S *cl; + char qstr[NSTRING]; + + cl = do_raw_quote_match(q, GLine, NLine, PLine, NULL, NULL); + + flatten_qstring(cl, qstr, NSTRING); + + free_qs(&cl); + + for(n = strlen(qstr); n < buflen && GLine[n]; n++) + if(!isspace((unsigned char) GLine[n])) + return(FALSE); + + return(TRUE); +} + + +QSTRING_S * +do_raw_quote_match(q, GLine, NLine, PLine, nlp, plp) + char *q, *GLine, *NLine, *PLine; + QSTRING_S **nlp, **plp; +{ + QSTRING_S *cl, *nl = NULL, *pl = NULL; + char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING]; + int emptypl = 0, emptynl = 0; + + cl = is_quote(q, GLine, 0); /* Current or Given line */ + + if (!cl) /* if nothing in, nothing out */ + return cl; + + if (NLine && NLine[0]){ + nl = is_quote(q, NLine, 0); /* Next Line */ + if(nlp) + *nlp = nl; + } + if (PLine && PLine[0]){ + pl = is_quote(q, PLine, 0); /* Previous Line */ + if(plp) + *plp = pl; + } + + /* + * If there's nothing in the preceeding or following line + * there is not enough information to accept it or discard it. In this + * case it's likely to be an isolated line, so we better accept it + * if it does not look like a word. */ + + flatten_qstring(pl, pbuf, NSTRING); + emptypl = (!PLine || !PLine[0] || + (pl && value_is_space(pbuf)) && !PLine[strlen(pbuf)]) ? 1 : 0; + if (emptypl){ + flatten_qstring(nl, nbuf, NSTRING); + emptynl = (!NLine || !NLine[0] || + (nl && value_is_space(nbuf) && !NLine[strlen(nbuf)])) ? 1 : 0; + if (emptynl){ + cl = remove_qsword(cl); + cl = qs_remove_trailing_spaces(cl); + free_qs(&nl); + free_qs(&pl); + if(nlp) *nlp = NULL; + if(plp) *plp = NULL; + + return cl; + } + } + + /* + * If either cl, nl or pl contain suspicious characters that may make + * them (or not) be quote strings, we need to fix them, so that the + * next pass will be done correctly. + */ + + cl = fix_qstring(cl, nl, pl); + nl = trim_qs_from_cl(cl, nl, NULL); + pl = trim_qs_from_cl(cl, NULL, pl); + + if(nlp) *nlp = nl; + if(plp) *plp = pl; + + return cl; +} + +QSTRING_S * +do_quote_match(q, GLine, NLine, PLine, rqstr, rqstrlen, plb) + char *q, *GLine, *NLine, *PLine, *rqstr; + int rqstrlen, plb; +{ + QSTRING_S *cl, *nl = NULL, *pl = NULL; + int c, n, p,i, j, NewP, NewC, NewN, clength, same = 0; + char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING]; + + cl = do_raw_quote_match(q, GLine, NLine, PLine, &nl, &pl); + + if (!cl) /* if nothing in, nothing out */ + return cl; + + flatten_qstring(cl, rqstr, rqstrlen); + flatten_qstring(cl, buf, NSTRING); + flatten_qstring(nl, nbuf, NSTRING); + flatten_qstring(pl, pbuf, NSTRING); + + /* + * Once upon a time, is_quote used to return the length of the quote + * string that it had found. One day, not long ago, black hand came + * and changed all that, and made is_quote return a quote string + * divided in several fields, making the algorithm much more + * complicated. Fortunately black hand left a few comments in the + * source code to make it more understandable. Because of this change + * we need to compute the lengths of the quote strings separately + */ + c = buf && buf[0] ? strlen(buf) : 0; + n = nbuf && nbuf[0] ? strlen(nbuf) : 0; + p = pbuf && pbuf[0] ? strlen(pbuf) : 0; + + /* + * When quote strings contain only blank spaces (ascii code 32) the + * above count is equal to the length of the quote string, but if + * there are TABS, the length of the quote string as seen by the user + * is different than the number that was just computed. Because of + * this we demand a recount (hmm.. unless you are in Florida, where + * recounts are forbidden) + */ + + NewP = strlenis(pbuf); + NewC = strlenis(buf); + NewN = strlenis(nbuf); + + /* + * For paragraphs with spaces in the first line, but no space in the + * quote string of the second line, we make sure we choose the quote + * string without a space at the end of it. + */ + if ((NLine && !NLine[0]) + && ((PLine && !PLine[0]) + || (((same = same_qstring(pl, cl)) != 0) + && (same != count_levels_qstring(cl))))) + cl = qs_remove_trailing_spaces(cl); + else + if (NewC > NewN){ + int agree = 0; + for (j = 0; (j < n) && (GLine[j] == NLine[j]); j++); + clength = j; + /* clength is the common length in which Gline and Nline agree */ + /* j < n means that they do not agree fully */ + /* GLine = " \tText" + NLine = " Text" */ + if(j == n) + agree++; + if (clength < n){ /* see if buf and nbuf are padded with spaces and tabs */ + for (i = clength; i < n && ISspace(NLine[i]); i++); + if (i == n){/* padded NLine until the end of spaces? */ + for (i = clength; i < c && ISspace(GLine[i]); i++); + if (i == c) /* Padded CLine until the end of spaces? */ + agree++; + } } - if(j > llength(l)) - return(n); - else if(qb && lgetc(l, j).c == ' ') - j++; + if (agree){ + for (j = clength; j < c && ISspace(GLine[j]); j++); + if (j == c){ + + /* + * If we get here, it means that the current line has the same + * quote string (visually) than the next line, but both of them + * are padded with different amount of TABS or spaces at the end. + * The current line (GLine) has more spaces/TABs than the next + * line. This is the typical situation that is found at the + * begining of a paragraph. We need to check this, however, by + * checking the previous line. This avoids that we confuse + * ourselves with being in the last line of a paragraph. + * Example when it should not free_qs(cl) + * " Text in Paragraph 1" (PLine) + * " Text in Paragraph 1" (GLine) + * " Other Paragraph Number 2" (NLine) + * + * Example when it should free_qs(cl): + * ":) " (PLine) p = 3, j = 3 + * ":) Text" (GLine) c = 5 + * ":) More text" (NLine) n = 3 + * + * Example when it should free_qs(cl): + * ":) " (PLine) p = 3, j = 3 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * Example when it should free_qs(cl): + * ":) :) " (PLine) p = 6, j = 3 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * Example when it should free_qs(cl): + * ":) > > > " (PLine) p = 13, j = 11 + * ":) > > > Text" (GLine) c = 11 + * ":) > > > More text" (NLine) n = 9 + * + * The following example is very interesting. The "Other Text" + * line below should free the quote string an make it equal to the + * quote string of the line below it, but any algorithm trying + * to advance past that line should make it stop there, so + * we need one more check, to check the raw quote string and the + * processed quote string at the same time. + * FREE qs in this example. + * " Some Text" (PLine) p = 3, j = 0 + * "\tOther Text" (GLine) c = 1 + * " More Text" (NLine) n = 3 + * + */ + + for (j = 0; (j < p) && (GLine[j] == PLine[j]); j++); + if (((p == c) && ((j != p) && NLine[n])) + || ((p != c) && NLine[n])){ + if(!get_indent_raw_line(q, PLine, nbuf, NSTRING, p, plb) + || NewP + strlenis(nbuf) != NewC){ + free_qs(&cl); + cl = copy_qs(nl); + } + } + } + } + } + + free_qs(&nl); + free_qs(&pl); + + return cl; +} + +/* + * Given a line, an initial position, and a quote string, we advance the + * current line past the quote string, including arbitraty spaces + * contained in the line, except that it removes trailing spaces. We do + * not handle TABs, if any, contained in the quote string. At least not + * yet. + * + * Arguments: q - quote string + * l - a line to process + * i - position in the line to start processing. i = 0 is the + * begining of that line. + */ +int +advance_quote_string(q, l, i) + char *q; + char l[NSTRING]; + int i; +{ + int n = 0, j = 0, is = 0, es = 0; + int k, m, p, adv; + char qs[NSTRING] = {'\0'}; + + if(!q || !*q) + return(0); + + for (p = strlen(q); (p > 0) && (q[p - 1] == ' '); p--, es++); + if (!p){ /* string contains only spaces */ + for (k = 0; l[i + k] == ' '; k++); + k -= k % es; + return k; } - return(n); /* never reached */ + for (is = 0; q[is] == ' '; is++); /* count initial spaces */ + for (m = 0 ; is + m < p ; m++) + qs[m] = q[is + m]; /* qs = quote string without any space at the end */ + /* advance as many spaces as there are at the begining */ + for (k = 0; l[i + j] == ' '; k++, j++); + /* now find the visible string in the line */ + for (m = 0; qs[m] && l[i + j] == qs[m]; m++, j++); + if (!qs[m]){ /* no match */ + /* + * So far we have advanced at least "is" spaces, plus the visible + * string "qs". Now we need to advance the trailing number of + * spaces "es". If we can do that, we have found the quote string. + */ + for (p = 0; l[i + j + p] == ' '; p++); + adv = advance_quote_string(q, l, i + j + ((p < es) ? p : es)); + n = ((p < es) ? 0 : es) + k + m + adv; + } + return n; } +/* + * This function returns the effective length in screen of the quote + * string. If the string contains a TAB character, it is added here, if + * not, the length returned is the length of the string + */ + +int +strlenis(qstr) +char *qstr; +{ + int i, rv = 0; + + for (i = 0; qstr && qstr[i]; i++) + rv += ((qstr[i] == TAB) ? (~rv & 0x07) + 1 : 1); + + return rv; +} /* Justify the entire buffer instead of just a paragraph */ fillbuf(f, n) @@ -475,11 +1602,13 @@ int f, n; /* deFault flag and Numeric argument */ { - int i, j, c, qlen, word[NSTRING], same_word, - spaces, word_len, line_len, line_last, qn; - char *qstr, qstr2[NSTRING]; + int i = 0, j, c, qlen, word[NSTRING], same_word, qlenis, + spaces, word_len, line_len, line_last, qn, indlen, qi, pqi; + char *qstr, qstr2[NSTRING], tbuf[NSTRING], ind_str[NSTRING], + *qstrfl, qstrfl2[NSTRING], quoid[NSTRING]; LINE *eopline; REGION region; + QSTRING_S *tl; if(curbp->b_mode&MDVIEW){ /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ @@ -496,17 +1625,75 @@ return(FALSE); eopline = curwp->w_dotp; /* first line of para */ - /* and back to the beginning of the paragraph */ gotobop(FALSE, 1); + setimark(FALSE, 1); /* Remember this spot in case we unjustify */ - /* determine if we're justifying quoted text or not */ - qstr = ((glo_quote_str || (Pmaster && Pmaster->quote_str)) - && quote_match(glo_quote_str ? glo_quote_str - : Pmaster->quote_str, - curwp->w_dotp, qstr2, NSTRING) - && *qstr2) ? qstr2 : NULL; - qlen = qstr ? strlen(qstr) : 0; + /* + * When a paragraph has special indentation, we will get two quote + * strings. One from the first line of the paragraph, and one from + * the last line of the paragraph. We will need to use both when + * we justify. + * + * Here's a model of what we will code: + * + * +-------+-------+-+-----+ + * | qstrfl|ind_str|X| text| + * +-----+-+-------+-+-----+ + * | qstr| *(space)|X| text| + * +-----+---------+-+-----+ + * + * Here X represents 1 space if it exists after ind_str and + * "*(space)" represent a variable amount of space that is put there + * to pad text so that it will align correctly when justified. + */ + indlen = indent_match(default_qstr(), curwp->w_dotp, ind_str, NSTRING, 0); + qstrfl = (quote_match(default_qstr(), curwp->w_dotp, qstrfl2, NSTRING, 0) + && *qstrfl2) ? qstrfl2 : NULL; + if (qstrfl) + for (; (i < NSTRING) && (quoid[i] = qstrfl[i]); i++); + if (indlen) + for (j = 0; ((i + j) < NSTRING) && (quoid[i] = ind_str[j]); i++,j++); + quoid[i] = '\0'; + qi = quoid && quoid[0] ? strlen(quoid) : 0; + if (indlen) /* strip trailing spaces */ + for (;ISspace(quoid[qi - 1]); qi--); + quoid[qi] = '\0'; /* we have closed quoid at "X" in the first line */ + + if (strlenis(quoid) > fillcol) + return FALSE; /* Too wide, we can't justify this! */ + + /* determine if we're justifying quoted text or not */ + qstr = quote_match(default_qstr(), curwp->w_dotp, qstr2, NSTRING, 0) + && *qstr2 ? qstr2 : NULL; + /* In some situations, like in the following paragraph, qstr can be non + * empty as returned above, when indeed it is empty. Fix it!. + + * Item #1 + * Item #2 + continuation of item #2 + */ + if (qstr && indlen){ + for (i = strlen(qstr) - 1; ISspace(qstr[i]); i--); + qstr[i + 1] = '\0'; + } + + qlen = qstr ? strlen(qstr) : 0; + qlenis = qstr ? strlenis(qstr) : 0; + + /* + * Compare effective lengths of quoid and qstr to decide how much space + * we need to use to pad with. + */ + if (indlen && ((j = strlenis(quoid) - strlenis(qstr)) > 0)){ + pqi = qstr ? strlen(qstr) : 0; + for (i = 0; (i < j) && (qstr2[pqi + i] = ' '); i++); + if (ISspace(ind_str[indlen - 1])) + qstr2[pqi + i++] = ' '; + qstr2[pqi + i] = '\0'; + if (!qstr) + qstr = qstr2; + } /* let yank() know that it may be restoring a paragraph */ thisflag |= CFFILL; @@ -524,18 +1711,38 @@ return(FALSE); /* Now insert it back wrapped */ - spaces = word_len = line_len = same_word = 0; + spaces = word_len = line_len = same_word = i = 0; /* Beginning with leading quoting... */ - if(qstr){ - while(qstr[line_len]) - linsert(1, qstr[line_len++]); + if(qstrfl){ + while((tbuf[line_len] = qstrfl[line_len]) == fremove(line_len)) + linsert(1, qstrfl[line_len++]); + /* + * The only way that at the end of the above loop we don't have + * line_len == qlen is that there are trailing spaces or TABS + * which could not be accounted in the qstr in is_quote or other + * functions before we got here. Now we enter the common part of + * the quote string in the first line and the rest is only spaces + * (or TABS) that need to be entered, which are left to the loop + * following this "if" statement + */ + i = line_len; /* start next loop from here */ + tbuf[line_len] = '\0'; /* closing tbuf... */ + line_len = strlenis(tbuf); /* we demand a recount! */ + line_last = ' '; /* no word-flush space! */ + } + /* ...followed by the indent string, if any */ + if (indlen){ + for (i, j = 0; (c = fremove(i)) && ind_str[j]; i++, j++){ + linsert(1, line_last = c); + line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); + } line_last = ' '; /* no word-flush space! */ } /* ...and leading white space */ - for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){ + for(i; (c = fremove(i)) == ' ' || c == TAB; i++){ linsert(1, line_last = c); line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); } @@ -558,15 +1765,15 @@ default : if(spaces){ /* flush word? */ - if((line_len - qlen > 0) + if((line_len - qlenis > 0) && line_len + word_len + 1 > fillcol - && ((isspace((unsigned char)line_last)) + && ((Pisspace(line_last)) || (linsert(1, ' '))) && (line_len = fpnewline(qstr))) line_last = ' '; /* no word-flush space! */ if(word_len){ /* word to write? */ - if(line_len && !isspace((unsigned char) line_last)){ + if(line_len && !Pisspace(line_last)){ linsert(1, ' '); /* need padding? */ line_len++; } @@ -588,8 +1795,8 @@ if(word_len + 1 >= NSTRING){ /* Magic! Fake that we output a wrapped word */ - if((line_len - qlen > 0) && !same_word++){ - if(!isspace((unsigned char) line_last)) + if((line_len - qlenis > 0) && !same_word++){ + if(!Pisspace(line_last)) linsert(1, ' '); line_len = fpnewline(qstr); } @@ -608,12 +1815,13 @@ } if(word_len){ - if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol)){ - if(!isspace((unsigned char) line_last)) + if((line_len - qlenis > 0) && (line_len + word_len + 1 > fillcol)){ + if(!Pisspace(line_last)) linsert(1, ' '); - (void) fpnewline(qstr); + if (line_len && (line_len != qlenis)) + (void) fpnewline(qstr); } - else if(line_len && !isspace((unsigned char) line_last)) + else if(line_len && !Pisspace(line_last)) linsert(1, ' '); for(j = 0; j < word_len; j++) @@ -634,11 +1842,155 @@ fpnewline(quote) char *quote; { - int len; + int i; lnewline(); - for(len = 0; quote && *quote; quote++, len++) - linsert(1, *quote); + for(i = 0; quote && quote[i]; i++) + linsert(1, quote[i]); - return(len); + return strlenis(quote); } + +int +is_indent (word, plb) + char word[NSTRING]; + int plb; +{ + int i = 0, finished = 0, c, nxt, j, k, digit = 0, bdigits = -1, alpha = 0; + + if (!word || !word[0]) + return i; + + for (i = 0, j = 0; ISspace(word[i]); i++, j++); + while ((i < NSTRING - 2) && !finished){ + switch (c = now(word,i)){ + case NBSP: + case TAB : + case ' ' : for (; ISspace(word[i]); i++); + if (!is_indent_char(now(word,i))) + finished++; + break; + + case '+' : + case '.' : + case ']' : + case '*' : + case '}' : + case '-' : + case RPAREN: + nxt = next(word,i); + if (((c == '.') && allowed_after_period(nxt) && alpha) + || ((c == '*') && allowed_after_star(nxt)) + || ((c == '}') && allowed_after_braces(nxt)) + || ((c == '-') && allowed_after_dash(nxt)) + || ((c == '+') && allowed_after_dash(nxt)) + || ((c == RPAREN) && allowed_after_parenth(nxt)) + || ((c == ']') && allowed_after_parenth(nxt))) + i++; + else + finished++; + break; + + default : if (is_a_digit(c) && plb){ + if (bdigits < 0) + bdigits = i; /* first digit */ + for (k = i; is_a_digit(now(word,k)); k++); + if (k - bdigits > 2){ /* more than 2 digits? */ + i = bdigits; /* too many! */ + finished++; + } + else{ + if(allowed_after_digit(now(word,k),word,k)){ + alpha++; + i = k; + } + else{ + i = bdigits; + finished++; + } + } + } + else + finished++; + break; + } + } + if (i == j) + i = 0; /* there must be something more than spaces in an indent string */ + return i; +} + +/* + * If there is an indent string this function returns + * its length + */ +int +indent_match(q, l, buf, buflen, raw) + char *q; + LINE *l; + char *buf; + int buflen; + int raw; +{ + char GLine[NSTRING] = {'\0'}; + int i, k, plb; + + k = quote_match(q,l, buf, buflen, raw); + + linencpy(GLine, l, NSTRING); + + plb = (lback(l) != curbp->b_linep) ? lisblank(lback(l)) : 1; + if (!plb){ + i = llength(lback(l)) - 1; + for (; i >= 0 && ISspace(lgetc(lback(l), i).c); i--); + if (EOLchar(lgetc(lback(l), i).c)) + plb++; + } + + return get_indent_raw_line(q, GLine, buf, buflen, k, plb); +} + +int +get_indent_raw_line(q, GLine, buf, buflen, k, plb) + char *q; + char *GLine; + char *buf; + int buflen; + int k; + int plb; +{ + int i, j; + + i = is_indent(GLine+k, plb); + + for (j = 0; (j < i) && (j < buflen) && (buf[j] = GLine[j + k]); j++); + buf[j] = '\0'; + + return i; +} + +deletepara(f, n) /* Delete the current paragraph */ + +int f, n; /* deFault flag and Numeric argument */ + +{ + if(curbp->b_mode&MDVIEW){ /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + } + + if(!lisblank(curwp->w_dotp)) + gotobop(FALSE, 1); + + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = curwp->w_doto; + + gotoeop(FALSE, 1); + if (curwp->w_dotp != curbp->b_linep){ /* if we are not at the end of buffer */ + curwp->w_dotp = lforw(curwp->w_dotp); /* get one more line */ + curwp->w_doto = 0; /* but only the beginning */ + } + killregion(f,n); + + return(TRUE); +} + diff -ru pine4.64/pine/addrbook.c pine4.64.SuSE/pine/addrbook.c --- pine4.64/pine/addrbook.c 2005-09-13 00:04:25.000000000 +0200 +++ pine4.64.SuSE/pine/addrbook.c 2006-02-14 14:45:22.000000000 +0100 @@ -6659,10 +6659,11 @@ *warped; { int find_result, rc, flags; + static char last_search_string[MAX_SEARCH + 1] = { '\0' }; static char search_string[MAX_SEARCH + 1] = { '\0' }; char prompt[MAX_SEARCH + 50], nsearch_string[MAX_SEARCH+1]; HelpType help; - ESCKEY_S ekey[4]; + ESCKEY_S ekey[5]; PerAddrBook *pab; long nl; @@ -6677,17 +6678,22 @@ ekey[0].name = ""; ekey[0].label = ""; - ekey[1].ch = ctrl('Y'); - ekey[1].rval = 10; - ekey[1].name = "^Y"; - ekey[1].label = "First Adr"; - - ekey[2].ch = ctrl('V'); - ekey[2].rval = 11; - ekey[2].name = "^V"; - ekey[2].label = "Last Adr"; + ekey[1].ch = ctrl('N'); + ekey[1].rval = 9; + ekey[1].name = "^N"; + ekey[1].label = "Ins Pat"; + + ekey[2].ch = ctrl('Y'); + ekey[2].rval = 10; + ekey[2].name = "^Y"; + ekey[2].label = "First Adr"; + + ekey[3].ch = ctrl('V'); + ekey[3].rval = 11; + ekey[3].name = "^V"; + ekey[3].label = "Last Adr"; - ekey[3].ch = -1; + ekey[4].ch = -1; flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE; while(1){ @@ -6698,6 +6704,9 @@ help = help == NO_HELP ? h_oe_searchab : NO_HELP; continue; } + else if(rc == 9) + insert_pattern_in_string(nsearch_string, last_search_string + , MAX_SEARCH); else if(rc == 10){ *warped = 1; warp_to_beginning(); /* go to top of addrbooks */ @@ -6725,7 +6734,7 @@ } } - if(rc != 4) + if(rc != 4 && rc != 9) break; } @@ -6738,6 +6747,9 @@ search_string[sizeof(search_string)-1] = '\0'; } + strncpy(last_search_string, nsearch_string, sizeof(last_search_string)); + last_search_string[sizeof(last_search_string)-1] = '\0'; + find_result = find_in_book(cur_line, search_string, new_line, wrapped); if(*wrapped == 1) diff -ru pine4.64/pine/adrbkcmd.c pine4.64.SuSE/pine/adrbkcmd.c --- pine4.64/pine/adrbkcmd.c 2005-09-27 23:27:55.000000000 +0200 +++ pine4.64.SuSE/pine/adrbkcmd.c 2006-02-14 14:45:22.000000000 +0100 @@ -3866,6 +3866,8 @@ * won't do anything, but will cause compose_mail to think there's * already a role so that it won't try to confirm the default. */ + if (ps_global->role) + fs_give((void **)&ps_global->role); if(role) role = copy_action(role); else{ @@ -3873,6 +3875,7 @@ memset((void *)role, 0, sizeof(*role)); role->nick = cpystr("Default Role"); } + ps_global->role = cpystr(role->nick); } compose_mail(addr, fcc, role, NULL, NULL); diff -ru pine4.64/pine/args.c pine4.64.SuSE/pine/args.c --- pine4.64/pine/args.c 2005-03-10 02:10:08.000000000 +0100 +++ pine4.64.SuSE/pine/args.c 2006-02-14 14:45:22.000000000 +0100 @@ -74,6 +74,7 @@ char args_err_non_abs_passfile[] = "argument to \"-passfile\" should be fully-qualified"; char args_err_missing_lu[] = "missing argument for option \"-create_lu\"\nUsage: pine -create_lu "; char args_err_missing_sort[] = "missing argument for option \"-sort\""; +char args_err_missing_thread_sort[] = "missing argument for option \"-threadsort\""; char args_err_missing_flag_arg[] = "missing argument for flag \"%c\""; char args_err_missing_flag_num[] = "Non numeric argument for flag \"%c\""; char args_err_missing_debug_num[] = "Non numeric argument for \"%s\""; @@ -117,6 +118,7 @@ " -z \t\tSuspend - allow use of ^Z suspension", " -r \t\tRestricted - can only send mail to oneself", " -sort \tSort - Specify sort order of folder:", +" -threadsort \tSort - Specify sort order of thread index screen:", "\t\t subject, arrival, date, from, size, /reverse", " -i\t\tIndex - Go directly to index, bypassing main menu", " -I Initial keystrokes to be executed", @@ -202,6 +204,7 @@ char *cmd_list = NULL; char *debug_str = NULL; char *sort = NULL; + char *threadsort = NULL; char *pinerc_file = NULL; char *addrbook_file = NULL; char *ab_sort_descrip = NULL; @@ -389,6 +392,18 @@ goto Loop; } + else if(strcmp(*av, "threadsort") == 0){ + if(--ac){ + threadsort = *++av; + COM_THREAD_SORT_KEY = cpystr(threadsort); + } + else{ + display_args_err(args_err_missing_thread_sort, NULL, 1); + ++usage; + } + + goto Loop; + } else if(strcmp(*av, "url") == 0){ if(args->action == aaFolder && !args->data.folder){ args->action = aaURL; @@ -496,6 +511,12 @@ do_version = 1; goto Loop; } + else if(strcmp(*av, "subject") == 0){ + if(--ac){ + pine_state->subject = cpystr(*++av); + } + goto Loop; + } #ifdef _WINDOWS else if(strcmp(*av, "install") == 0){ ps_global->install_flag = 1; diff -ru pine4.64/pine/bldaddr.c pine4.64.SuSE/pine/bldaddr.c --- pine4.64/pine/bldaddr.c 2005-09-27 23:27:55.000000000 +0200 +++ pine4.64.SuSE/pine/bldaddr.c 2006-02-14 14:45:22.000000000 +0100 @@ -1,5 +1,5 @@ #if !defined(lint) && !defined(DOS) -static char rcsid[] = "$Id: bldaddr.c 14092 2005-09-27 21:27:55Z hubert@u.washington.edu $"; +static char rcsid[] = "$Id: bldaddr.c 14099 2005-10-04 22:47:31Z hubert@u.washington.edu $"; #endif /*---------------------------------------------------------------------- @@ -2306,8 +2306,14 @@ if(as.cur >= as.how_many_personals) pab->type |= GLOBAL; - pab->access = adrbk_access(pab); - + if(ps_global->mail_stream && + ps_global->mail_stream->lock && (pab->type & REMOTE_VIA_IMAP)){ + as.initialized = 0; + pab->access = NoAccess; + } + else{ + pab->access = adrbk_access(pab); + } /* global address books are forced readonly */ if(pab->type & GLOBAL && pab->access != NoAccess) pab->access = ReadOnly; @@ -7424,14 +7430,17 @@ int abook_num; { ADDRESS *addr; - char *a_string; + char *a_string, *ret = NULL; int return_nick = 1; + AdrBk_Entry *abe_copy = NULL; if(!dl || !abe) return(cpystr("")); + abe_copy = copy_ae(abe); + if(!(dl->type == Simple || dl->type == ListHead) - || !(abe->nickname && abe->nickname[0])) + || !(abe_copy->nickname && abe_copy->nickname[0])) return_nick = 0; if(return_nick){ @@ -7455,7 +7464,8 @@ * Also, if the other entry with the same nickname is in this * same addressbook and comes before this entry, same deal. */ - fname = addr_lookup(abe->nickname, &which_addrbook, -1, &found_abe); + fname = addr_lookup(abe_copy->nickname, &which_addrbook, -1, + &found_abe); found_it = (fname != NULL); if(fname) fs_give((void **) &fname); @@ -7466,31 +7476,31 @@ /* if the returned abe is not the same as abe don't use the nickname */ if(return_nick && found_it && which_addrbook == abook_num){ if(found_abe - && ((found_abe->tag != abe->tag) - || (found_abe->fullname && !abe->fullname) - || (!found_abe->fullname && abe->fullname) - || strcmp(found_abe->fullname, abe->fullname) - || (found_abe->fcc && !abe->fcc) - || (!found_abe->fcc && abe->fcc) - || strcmp(found_abe->fcc, abe->fcc) - || (found_abe->extra && !abe->extra) - || (!found_abe->extra && abe->extra) - || strcmp(found_abe->extra, abe->extra) - || (abe->tag == Single && strcmp(found_abe->addr.addr ? found_abe->addr.addr : "", abe->addr.addr ? abe->addr.addr : "")))) + && ((found_abe->tag != abe_copy->tag) + || (found_abe->fullname && !abe_copy->fullname) + || (!found_abe->fullname && abe_copy->fullname) + || strcmp(found_abe->fullname, abe_copy->fullname) + || (found_abe->fcc && !abe_copy->fcc) + || (!found_abe->fcc && abe_copy->fcc) + || strcmp(found_abe->fcc, abe_copy->fcc) + || (found_abe->extra && !abe_copy->extra) + || (!found_abe->extra && abe_copy->extra) + || strcmp(found_abe->extra, abe_copy->extra) + || (abe_copy->tag == Single && strcmp(found_abe->addr.addr ? found_abe->addr.addr : "", abe_copy->addr.addr ? abe_copy->addr.addr : "")))) return_nick = 0; /* I suppose we ought to check for the lists being the same */ - if(return_nick && abe->tag == List && found_abe){ + if(return_nick && abe_copy->tag == List && found_abe){ char **p, **q; int i, n1, n2; - for(p = abe->addr.list; p && *p; p++) + for(p = abe_copy->addr.list; p && *p; p++) ; if(p == NULL) n1 = 0; else - n1 = p - abe->addr.list; + n1 = p - abe_copy->addr.list; for(p = found_abe->addr.list; p && *p; p++) ; @@ -7502,7 +7512,7 @@ if(n1 == n2){ for(i = 0; i < n1 && return_nick; i++) - if(strcmp(abe->addr.list[i], found_abe->addr.list[i])) + if(strcmp(abe_copy->addr.list[i],found_abe->addr.list[i])) return_nick = 0; } else @@ -7515,9 +7525,10 @@ } if(return_nick) - return(cpystr(abe->nickname)); + ret = cpystr(abe_copy->nickname); else{ - addr = abe_to_address(abe, dl, as.adrbks[abook_num].address_book, NULL); + addr = abe_to_address(abe_copy, dl, as.adrbks[abook_num].address_book, + NULL); /* always returns a string */ a_string = addr_list_string(addr, NULL, 0, 0); @@ -7525,8 +7536,13 @@ if(addr) mail_free_address(&addr); - return(a_string); + ret = a_string; } + + if(abe_copy) + free_ae(&abe_copy); + + return(ret); } Only in pine4.64.SuSE/pine: date.c diff -ru pine4.64/pine/filter.c pine4.64.SuSE/pine/filter.c --- pine4.64/pine/filter.c 2005-08-30 02:08:19.000000000 +0200 +++ pine4.64.SuSE/pine/filter.c 2006-02-14 14:45:24.000000000 +0100 @@ -65,6 +65,9 @@ #include "headers.h" +#ifdef HAVE_ICONV +#include +#endif /* @@ -204,6 +207,10 @@ error_description(errno))); if(source == TmpFileStar) (void)unlink(so->name); + if (ps_global->send_immediately){ + printf("%s : %s\n", so->name, error_description(errno)); + exit(1); + } fs_give((void **)&so->name); fs_give((void **)&so); /* so freed & set to NULL */ @@ -705,6 +712,7 @@ #define FL_SIG 17 #define STOP_DECODING 18 #define SPACECR 19 +#define UTF8 20 @@ -2266,6 +2274,110 @@ } } +#ifdef HAVE_ICONV +/* + * This filter converts the input buffer in the MIME charset of + * a message, for example) to another (the user's display charset) + * using iconv(3), POSIX/Single Unix Standard API. + */ +void +gf_convert_utf8_charset(f, flg) + FILTER_S *f; + int flg; +{ + static iconv_t iconv_desc; + static int einval_inbytesleft; + GF_INIT(f, f->next); + + switch (flg) { + case GF_DATA: { + size_t conv, inbytesleft = eob - op, outbytesleft = eib - ip; + /* + * If einval_inbytesleft is set, iconv() encountered an incomplete + * multibyte sequence and we asked for more input. In case the number + * of chars left to convert did not change, we should be at the end + * of input and we have an incomplete multibyte sequence at the end + * end of input. We only mark this and ignore the incomplete data. + */ + if (inbytesleft == einval_inbytesleft) { + char *einval_error = "[invalid multibyte seq at end of input]"; + dprint(8,(debugfile, "inval multibyte seq at end of input\n")); + for (;*einval_error;einval_error++) + GF_PUTC(f->next, *einval_error); + GF_FLUSH(f->next); + op = eob; /* throw the remaing unusable bytes away */ + GF_CH_RESET(f); + break; + } + while (1) { + if (!outbytesleft || !inbytesleft) { + GF_FLUSH(f->next); + outbytesleft = eib - ip; + } + if (!inbytesleft) { + GF_CH_RESET(f); + break; + } + einval_inbytesleft = -1; + conv = iconv(iconv_desc, (char **)&op, &inbytesleft, + (char **)&ip, &outbytesleft); + if (conv != (size_t) (-1)) { /* iconv succeeded */ + dprint(9,(debugfile, "irres. conv. count: %d, il: %d, ol: %d\n", + conv, inbytesleft, outbytesleft)); + /* iconv failed. check errno */ + } else if (errno == E2BIG){ + dprint(9,(debugfile, "e2big: outbytesleft=%d\n", outbytesleft)); + outbytesleft = 0; + } else if (errno == EILSEQ){ + char hexout[3]; + dprint(9,(debugfile, "eilseq: ill.octet=0x%02x, il=%d, ol=%d\n", + *op, inbytesleft, outbytesleft)); + sprintf(hexout, "%2x", *op++); + inbytesleft--; + GF_PUTC(f->next, '['); + GF_PUTC(f->next, hexout[0]); + GF_PUTC(f->next, hexout[1]); + GF_PUTC(f->next, ']'); + outbytesleft = eib - ip; + iconv(iconv_desc, NULL, NULL, NULL, NULL); + } else if (errno == EINVAL){ + /* + * We have to return from this function now because our input + * buffer contains an incomplete multibyte character which we + * can't complete without the next bytes of input. + */ + dprint(9,(debugfile, + "einval: %d, ol: %d, incomplete input: 0x%02x\n", + inbytesleft, outbytesleft, (unsigned char) *op)); + /* + * Before we abort here, we need to flush already converted + * output to the filter chain, otherwise we may loose this + * already converted content. + */ + GF_FLUSH(f->next); + /* + * In case we are at the end of all input, and we have + * an incomplete multibyte sequence left, we must find + * a way to not fall into a loop, remember the bytes left: + */ + einval_inbytesleft = inbytesleft; + break; /* Take the straigt way out now */ + } /* errno check */ + } /* while (1) */ + GF_END(f, f->next); + break; + } /* GF_DATA */ + case GF_RESET: + iconv_desc = (iconv_t)(f->opt); + iconv(iconv_desc, NULL, NULL, NULL, NULL); + einval_inbytesleft = -1; + break; + case GF_EOD: + GF_FLUSH(f->next); + (*f->next->f)(f->next, GF_EOD); + } /* switch (flg) */ +} +#else /* * This filter converts characters in UTF-8 to an 8-bit or 16-bit charset. @@ -2369,6 +2481,7 @@ f->n = 0L; } } +#endif /* @@ -6767,8 +6880,15 @@ wrap_max, margin_l, margin_r, + offset, indent; + char utf_seq[8]; char special[256]; + long curlinenum; /* current line number */ + int curqstrpos; /* current position in quote string */ + long linenum; /* line number */ + long qstrlen; /* multiples of 100 */ + char **qstrln; /* qstrln[i] = quote string line i - 1 */ } WRAP_S; #define WRAP_MARG_L(F) (((WRAP_S *)(F)->opt)->margin_l) @@ -6788,6 +6908,7 @@ #define WRAP_USE_CLR(F) (((WRAP_S *)(F)->opt)->use_color) #define WRAP_STATE(F) (((WRAP_S *)(F)->opt)->state) #define WRAP_QUOTED(F) (((WRAP_S *)(F)->opt)->quoted) +#define WRAP_UTF_SEQ(F) (((WRAP_S *)(F)->opt)->utf_seq) #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) @@ -6803,6 +6924,12 @@ #define WRAP_COLOR(F) (((WRAP_S *)(F)->opt)->color) #define WRAP_COLOR_SET(F) ((WRAP_COLOR(F)) && (WRAP_COLOR(F)->fg[0])) #define WRAP_SPACES(F) (((WRAP_S *)(F)->opt)->spaces) +#define WRAP_CURLINE(F) (((WRAP_S *)(F)->opt)->curlinenum) +#define WRAP_CURPOS(F) (((WRAP_S *)(F)->opt)->curqstrpos) +#define WRAP_LINENUM(F) (((WRAP_S *)(F)->opt)->linenum) +#define WRAP_QSTRLEN(F) (((WRAP_S *)(F)->opt)->qstrlen) +#define WRAP_QSTRN(F) (((WRAP_S *)(F)->opt)->qstrln) +#define WRAP_QSTR(F, N) (((WRAP_S *)(F)->opt)->qstrln[(N)]) #define WRAP_PUTC(F,C,V) { \ if((F)->linep == WRAP_LASTC(F)){ \ size_t offset = (F)->linep - (F)->line; \ @@ -6867,7 +6994,7 @@ GF_INIT(f, f->next); if(flg == GF_DATA){ - register unsigned char c; + unsigned char c, *ch, *cm; register int state = f->f1; register int x; @@ -6877,6 +7004,8 @@ case CCR : /* CRLF or CR in text ? */ state = BOL; /* either way, handle start */ + WRAP_CURLINE(f)++; + WRAP_CURPOS(f) = 0; if(WRAP_FLOW(f)){ if(f->f2 == 0 && WRAP_SPC_LEN(f)){ /* wrapped line */ /* @@ -6968,7 +7097,10 @@ case BOL : if(WRAP_FLOW(f)){ - if(c == '>'){ + if(WRAP_QSTR(f, WRAP_CURLINE(f)) + && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] + && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){ + WRAP_CURPOS(f)++; WRAP_FL_QC(f) = 1; /* init it */ state = FL_QLEV; /* go collect it */ } @@ -6982,7 +7114,16 @@ } /* quote level change implies new paragraph */ - if(WRAP_FL_QD(f)){ + if (WRAP_CURLINE(f) > 0 + && WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && (WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL + || WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL) + && ((WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL && + WRAP_QSTR(f, WRAP_CURLINE(f) - 1) == NULL) + || (WRAP_QSTR(f, WRAP_CURLINE(f)) == NULL && + WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL) + || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)), + WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){ WRAP_FL_QD(f) = 0; if(WRAP_HARD(f) == 0){ WRAP_HARD(f) = 1; @@ -7034,8 +7175,11 @@ break; case FL_QLEV : - if(c == '>'){ /* another level */ - WRAP_FL_QC(f)++; + if(WRAP_QSTR(f, WRAP_CURLINE(f)) + && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] + && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){ + WRAP_CURPOS(f)++; + WRAP_FL_QC(f)++; /* another level */ } else { /* if EMBEDed, process it and return here */ @@ -7047,7 +7191,16 @@ } /* quote level change signals new paragraph */ - if(WRAP_FL_QC(f) != WRAP_FL_QD(f)){ + if (WRAP_CURLINE(f) > 0 + && WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && (WRAP_QSTR(f, WRAP_CURLINE(f)) + || WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) + && ((WRAP_QSTR(f, WRAP_CURLINE(f)) && + !WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) + || (!WRAP_QSTR(f, WRAP_CURLINE(f)) && + WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) + || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)), + WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){ WRAP_FL_QD(f) = WRAP_FL_QC(f); if(WRAP_HARD(f) == 0){ /* add hard newline */ WRAP_HARD(f) = 1; /* hard newline */ @@ -7104,6 +7257,12 @@ state = FL_SIG; break; + case ' ' : /* what? */ + if (WRAP_QSTR(f, WRAP_CURLINE(f))){ + WRAP_SPC_LEN(f)++; + so_writec(' ', WRAP_SPACES(f)); + } + default : /* something else */ state = DFL; goto case_dfl; /* handle c like DFL */ @@ -7120,7 +7279,7 @@ &eob); /* note any embedded*/ wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ - wrap_bol(f, 1, 1, &ip, &eib, + wrap_bol(f, 1, WRAP_FLOW(f), &ip, &eib, &op, &eob); /* write any prefix */ } @@ -7276,8 +7435,42 @@ break; + case UTF8 : + if(!(ch = cm = pine_check_utf8(&c, WRAP_UTF_SEQ(f), sizeof(WRAP_UTF_SEQ(f))))) + break; /* sequence not complete, need next byte */ + state = DFL; /* end of sequence, leave the UTF-8 mode */ + if(ch != &c) { /* seq. complete, wrap and write it */ + if(f->n + f->f2 + WRAP_SPC_LEN(f) + > WRAP_COL(f) - (*ch == ' '?2:1)) { + dprint(8, (debugfile, "UTF8: newline\n")); + wrap_flush(f, &ip, &eib, &op, &eob); /* write everything */ + wrap_eol(f, 0, &ip, &eib, &op, &eob); /* no fit, the end */ + wrap_bol(f,1,1, &ip, &eib, &op, &eob); /* start w/prefix */ + } + f->n++; + if(*ch == ' ') { /* double-wide UTF-8 char, check space */ + ch++; /* ' ' was just a flag, skip over it */ + f->n++; + } + dprint(9, (debugfile, "UTF8: free room: %02d char: '%s'\n", + (WRAP_COL(f) - f->n - f->f2 - WRAP_SPC_LEN(f)), ch)); + f->n -= strlen(ch); + WRAP_PUTC(f, *ch++, 1); + while(*ch) + WRAP_PUTC(f, *ch++, 1); + if(*cm == ' ') + wrap_flush(f, &ip, &eib, &op, &eob); /* write everything */ + break; + } + WRAP_PUTC(f, '?', 1); /* in place of invalid sequence */ + /* fall thru to process new char */ + wrap_flush(f, &ip, &eib, &op, &eob); /* write everything */ case_dfl : case DFL : + if (!pine_check_utf8(&c, WRAP_UTF_SEQ(f), sizeof(WRAP_UTF_SEQ(f)))) { + state = UTF8; /* Change to UTF-8 mode */ + break; /* Process next char in UTF-8 mode */ + } if(WRAP_SPEC(f, c)){ switch(c){ default : @@ -7425,7 +7618,7 @@ wrap_flush_embed(f, &ip, &eib, &op, &eob); wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ - wrap_bol(f,1,1, &ip, &eib, &op, + wrap_bol(f,1,WRAP_FLOW(f), &ip, &eib, &op, &eob); /* write any prefix */ } @@ -7483,6 +7676,13 @@ if(WRAP_COLOR(f)) free_color_pair(&WRAP_COLOR(f)); + { long i; + for (i = 0L; i < WRAP_QSTRLEN(f); i++) + if (WRAP_QSTR(f,i)) + fs_give((void **) &(WRAP_QSTR(f,i))); + fs_give((void **)&WRAP_QSTRN(f)); + } + fs_give((void **) &f->line); /* free temp line buffer */ so_give(&WRAP_SPACES(f)); fs_give((void **) &f->opt); /* free wrap widths struct */ @@ -7804,7 +8004,8 @@ { int j, i; COLOR_PAIR *col = NULL; - char *prefix = NULL, *last_prefix = NULL; + char *prefix = NULL, *last_prefix = NULL, *wrap_qstr = NULL; + int level = 0, oldj, len; if(ps_global->VAR_QUOTE_REPLACE_STRING){ get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0); @@ -7813,10 +8014,22 @@ last_prefix = NULL; } } - - for(j = 0; j < WRAP_FL_QD(f); j++){ + + if(WRAP_QSTR(f, WRAP_CURLINE(f))) + wrap_qstr = cpystr(WRAP_QSTR(f, WRAP_CURLINE(f))); + len = wrap_qstr ? strlen(wrap_qstr) : 0; + + for (j = wrap_qstr && *wrap_qstr == ' ' ? 1 : 0; + j < len && isspace((unsigned char)wrap_qstr[j]); j++){ + GF_PUTC_GLO(f->next, wrap_qstr[j]); + f->n += ((wrap_qstr[j] == TAB) ? (~f->n & 0x07) + 1 : 1); + } + + for(; j < len && level < len; level++){ + oldj = j; + j = next_level_quote(wrap_qstr, (char **)NULL, j, WRAP_FLOW(f)); if(WRAP_USE_CLR(f)){ - if((j % 3) == 0 + if((level % 3) == 0 && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR, @@ -7824,7 +8037,7 @@ && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } - else if((j % 3) == 1 + else if((level % 3) == 1 && ps_global->VAR_QUOTE2_FORE_COLOR && ps_global->VAR_QUOTE2_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR, @@ -7832,7 +8045,7 @@ && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } - else if((j % 3) == 2 + else if((level % 3) == 2 && ps_global->VAR_QUOTE3_FORE_COLOR && ps_global->VAR_QUOTE3_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR, @@ -7845,45 +8058,54 @@ col = NULL; } } + if (j > 1 && wrap_qstr[j-1] == ' ') + j -= 1; - if(!WRAP_LV_FLD(f)){ - if(ps_global->VAR_QUOTE_REPLACE_STRING && prefix){ - for(i = 0; prefix[i]; i++) - GF_PUTC_GLO(f->next, prefix[i]); - f->n += strlen(prefix); - } - else if(ps_global->VAR_REPLY_STRING - && (!strcmp(ps_global->VAR_REPLY_STRING, ">") - || !strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){ - GF_PUTC_GLO(f->next, '>'); - f->n += 1; - } - else{ - GF_PUTC_GLO(f->next, '>'); - GF_PUTC_GLO(f->next, ' '); - f->n += 2; - } + if(!WRAP_LV_FLD(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){ + for(i = 0; prefix[i]; i++) + GF_PUTC_GLO(f->next, prefix[i]); + f->n += strlenis(prefix); } else{ - GF_PUTC_GLO(f->next, '>'); - f->n += 1; + for (i = oldj; i < j; i++) + GF_PUTC_GLO(f->next, wrap_qstr[i]); + f->n += j - oldj; + } + for (i = j; isspace((unsigned char)wrap_qstr[i]); i++); + if(!wrap_qstr[i]){ + f->n += i - j; + for (; j < i; j++) + GF_PUTC_GLO(f->next, ' '); } + else{ + if((WRAP_LV_FLD(f) + || !ps_global->VAR_QUOTE_REPLACE_STRING || !prefix) + || !ps_global->VAR_REPLY_STRING + || (strcmp(ps_global->VAR_REPLY_STRING, ">") + && strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){ + GF_PUTC_GLO(f->next, ' '); + f->n += 1; + } + } + for (; isspace((unsigned char)wrap_qstr[j]); j++); } if(j && WRAP_LV_FLD(f)){ GF_PUTC_GLO(f->next, ' '); f->n++; } - else if(j && last_prefix){ + else if(j && !value_is_space(wrap_qstr) && last_prefix){ for(i = 0; last_prefix[i]; i++) GF_PUTC_GLO(f->next, last_prefix[i]); - f->n += strlen(last_prefix); + f->n += strlenis(last_prefix); } if(prefix) fs_give((void **)&prefix); if(last_prefix) fs_give((void **)&last_prefix); - + if (wrap_qstr) + fs_give((void **)&wrap_qstr); + return 0; } @@ -7914,6 +8136,12 @@ wrap->leave_flowed = (GFW_FLOW_RESULT & flags) == GFW_FLOW_RESULT; wrap->delsp = (GFW_DELSP & flags) == GFW_DELSP; wrap->use_color = (GFW_USECOLOR & flags) == GFW_USECOLOR; + wrap->curlinenum = 0L; + wrap->curqstrpos = 0; + wrap->linenum = 0L; + wrap->qstrlen = 100L; + wrap->qstrln = (char **) fs_get(100*sizeof(char *)); + memset(wrap->qstrln, 0, 100*sizeof(char *)); return((void *) wrap); } @@ -8335,7 +8563,184 @@ } \ } +#define ADD_QUOTE_STRING(F) { \ + int len = tmp_20k_buf[0] ? strlen(tmp_20k_buf) + 1 : 0; \ + FILTER_S *fltr; \ + \ + for(fltr = (F); fltr && fltr->f != gf_wrap; fltr = fltr->next);\ + if (fltr){ \ + if (WRAP_LINENUM(fltr) >= WRAP_QSTRLEN(fltr)){ \ + fs_resize((void **)&WRAP_QSTRN(fltr), \ + (WRAP_QSTRLEN(fltr) + 100) * sizeof(char *)); \ + memset(WRAP_QSTRN(fltr)+WRAP_QSTRLEN(fltr), 0, \ + 100*sizeof(char*)); \ + WRAP_QSTRLEN(fltr) += 100L; \ + } \ + if (len){ \ + WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = \ + (char *) fs_get(len*sizeof(char)); \ + WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = cpystr(tmp_20k_buf);\ + } \ + WRAP_LINENUM(fltr)++; \ + } \ +} + +#define GF_ADD_QUOTED_LINE(F, line) \ +{ \ + LT_INS_S *ins = NULL, *insp; \ + int done;\ + unsigned char ch;\ + register char *cp;\ + register int l;\ + \ + if (line){\ + done = (*((LINETEST_S *) (F)->opt)->f)((F)->n++,\ + line, &ins,\ + ((LINETEST_S *) (F)->opt)->local);\ + if (done < 2){ \ + ADD_QUOTE_STRING((F));\ + for(insp = ins, cp = line; *cp ; ){\ + while(insp && cp == insp->where){\ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((F)->next, ch);\ + }\ + insp = insp->next;\ + }\ + GF_PUTC((F)->next, *cp);\ + cp++;\ + }\ + while(insp){\ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((F)->next, ch);\ + }\ + insp = insp->next;\ + }\ + gf_line_test_free_ins(&ins);\ + GF_PUTC((F)->next, '\015');\ + GF_PUTC((F)->next, '\012');\ + }\ + }\ +} +/* test second line of old line first */ +#define SECOND_LINE_QUOTE_TEST(line, F) \ +{\ + *p = '\0';\ + for (i = 0; ((F)->oldline)[i] && ((F)->oldline)[i] != '\015'; i++);\ + if (((F)->oldline)[i]){\ + i += (((F)->oldline)[i+1] == '\012') ? 2 : 1;\ + line = (F)->oldline + i;\ + }\ + for (i = 0; ((F)->line) \ + && (i < LINE_TEST_BLOCK) \ + && (i < SIZEOF_20KBUF)\ + && ((F)->line)[i] \ + && (((F)->line)[i] != '\015')\ + && (((F)->line)[i] != '\012')\ + && (tmp_20k_buf[i] = ((F)->line)[i]); i++);\ + tmp_20k_buf[i] = '\0';\ + GF_ADD_QUOTED_LINE((F), line);\ +} + +#define FIRST_LINE_QUOTE_TEST(line, F)\ +{\ + *p = '\0';\ + line = (F)->line;\ + (F)->oldline = cpystr(line);\ + for (i = 0; line[i] && line[i] != '\015' && line[i] != '\012'; i++); \ + if (line[i]){\ + (line[i]) = '\0'; \ + i+= (line[i+1] == '\012') ? 2 : 1;\ + }\ + for (j = 0; ((F)->line) \ + && ((i + j) < LINE_TEST_BLOCK) \ + && (j < SIZEOF_20KBUF) \ + && ((F)->line)[i + j] \ + && (((F)->line)[i + j] != '\015')\ + && (((F)->line)[i + j] != '\012')\ + && (tmp_20k_buf[j] = ((F)->line)[i + j]); j++);\ + tmp_20k_buf[j] = '\0';\ + GF_ADD_QUOTED_LINE((F), line);\ +} + + +void +gf_quote_test(f, flg) + FILTER_S *f; + int flg; +{ + register char *p = f->linep; + register char *eobuf = GF_LINE_TEST_EOB(f); + char *line = NULL; + int i, j; + 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 == 2){ /* two full lines read */ + state = 0; + /* first process the second line of an old line */ + if (f->oldline && f->oldline[0]) + SECOND_LINE_QUOTE_TEST(line, f); + + /* now we process the first line */ + FIRST_LINE_QUOTE_TEST(line, f); + + p = f->line; + continue; + } + if(c == '\015'){ + state++; + if (state == 1) + GF_LINE_TEST_ADD(f, c); + } + else + GF_LINE_TEST_ADD(f, c); + } + + f->f1 = state; + GF_END(f, f->next); + } + else if(flg == GF_EOD){ + /* first process the second line of an old line */ + if (f->oldline && f->oldline[0]) + SECOND_LINE_QUOTE_TEST(line, f); + + /* now we process the first line */ + FIRST_LINE_QUOTE_TEST(line, f); + + /* We are out of data. In this case we have processed the second + * line of an oldline, then the first line of a line, but we need + * to process the second line of the given line. We do this by + * processing it now!. + */ + if (line[i]){ + tmp_20k_buf[0] = '\0'; /* No next line */ + GF_ADD_QUOTED_LINE(f, line+i); + } + + fs_give((void **) &f->oldline); /* free old line buffer */ + 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; +} /* * this simple filter accumulates characters until a newline, offers it @@ -8360,7 +8765,12 @@ if(state){ state = 0; if(c == '\012'){ - int done; + int done, i, j = 0; + + for (i = 0; op && op[i] && (i < LINE_TEST_BLOCK) && + (i < SIZEOF_20KBUF) && (op[i] != '\015') && + (tmp_20k_buf[i] = op[i]); i++); + tmp_20k_buf[i] = '\0'; GF_LINE_TEST_TEST(f, done); @@ -8422,6 +8832,7 @@ else if(flg == GF_EOD){ int i; + tmp_20k_buf[0] = '\0'; 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 */ diff -ru pine4.64/pine/folder.c pine4.64.SuSE/pine/folder.c --- pine4.64/pine/folder.c 2005-09-13 00:04:25.000000000 +0200 +++ pine4.64.SuSE/pine/folder.c 2006-02-14 14:45:22.000000000 +0100 @@ -94,7 +94,7 @@ #define FLW_SLCT 0x02 #define FLW_LIST 0x04 - +static int max_slot_size = 0; /*---------------------------------------------------------------------- @@ -224,6 +224,8 @@ gf_io_t, HANDLE_S **, int)); int folder_list_write_folder PROTO((gf_io_t, CONTEXT_S *, int, char *, int)); +int folder_list_write_count PROTO((FOLDER_S *, CONTEXT_S *, gf_io_t, + int, int)); int folder_list_write_prefix PROTO((FOLDER_S *, int, gf_io_t)); int folder_list_ith PROTO((int, CONTEXT_S *)); char *folder_list_center_space PROTO((char *, int)); @@ -469,7 +471,7 @@ HELP_MENU, OTHER_MENU, - NULL_MENU, + {"^H","ChkIncFld",{MC_FORCECHECK,1,ctrl('H')}, KS_NONE}, NULL_MENU, NULL_MENU, NULL_MENU, @@ -1708,6 +1710,7 @@ gf_io_t pc; dprint(1, (debugfile, "\n\n ---- FOLDER LISTER ----\n")); + ps->in_fld_list = 1; memset(&folder_proc_data, 0, sizeof(FPROC_S)); folder_proc_data.fs = fs; @@ -1842,6 +1845,7 @@ *fs->cache_streamp = NULL; } + ps->in_fld_list = 0; return(folder_proc_data.rv); } @@ -1944,6 +1948,9 @@ gf_puts("\n", pc); } + if(!selected_folders(c_list) && (c_list->use & CNTXT_ZOOM)) + c_list->use &= ~CNTXT_ZOOM; + if(c_list->use & CNTXT_ZOOM){ sprintf(tmp_20k_buf, "[ ZOOMED on %d (of %d) %ss ]", selected_folders(c_list), @@ -1955,6 +1962,18 @@ gf_puts("\n", pc); } + if (c_list->use & CNTXT_INCMNG && + F_ON(F_ENABLE_INCOMING_CHECK,ps_global) && + F_ON(F_ENABLE_INCOMING,ps_global) && + F_OFF(F_ENABLE_FAST_RECENT, ps_global)){ + strcpy(tmp_20k_buf, + "Format: Folder-name [Total New Messages/Total Messages]"); + gf_puts(folder_list_center_space(tmp_20k_buf, cols), pc); + gf_puts(tmp_20k_buf, pc); + gf_puts("\n", pc); + } + + gf_puts(repeat_char(cols, '-'), pc); gf_puts("\n\n", pc); } @@ -1992,8 +2011,22 @@ else if(c_list == fp->fs->list_cntxt) len += 4; /* "[X] " */ + if (c_list->use & CNTXT_INCMNG && + F_ON(F_ENABLE_INCOMING_CHECK,ps_global) && + F_ON(F_ENABLE_INCOMING,ps_global)){ + if(F_OFF(F_ENABLE_FAST_RECENT, ps_global)){ + len += strlen(comatose(f->countrecent)); + len += strlen(comatose(f->messages)); + len += 4; /* "[/]" + "."/"_"/"~"/" " */ + } + else + len += 1; /* " " */ + } + len += 1; + if(slot_size < len) slot_size = len; + max_slot_size = slot_size; } if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)){ @@ -2098,7 +2131,7 @@ int flags; { char buf[256]; - int l = 0; + int l = 0, s = 0; FOLDER_S *fp; HANDLE_S *h; @@ -2121,6 +2154,11 @@ && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1) && (fp ? ((l = folder_list_write_prefix(fp, flags, pc)) >= 0 && gf_puts(FLDR_NAME(fp), pc) + && (s = folder_list_write_count(fp, ctxt, pc, l, + (ps_global->last_folder_checked == fnum) + ? 1 + : ((ps_global->first_folder_checked == fnum) + ? -1 : 0))) >= 0 && ((fp->isdir && fp->isfolder) ? (*pc)('[') : 1) && ((fp->isdir) ? (*pc)(ctxt->dir->delim) : 1) && ((fp->isdir && fp->isfolder) ? (*pc)(']') : 1)) @@ -2128,7 +2166,7 @@ && (h ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)){ if(fp){ - l += strlen(FLDR_NAME(fp)); + l += strlen(FLDR_NAME(fp)) + s; if(fp->isdir) l += (fp->isfolder) ? 3 : 1; } @@ -2139,6 +2177,49 @@ return(l); } +int +folder_list_write_count(f, ctxt, pc, l, is_last) + FOLDER_S *f; + CONTEXT_S *ctxt; + gf_io_t pc; + int l, is_last; +{ + int rv = 0, i; + + if (ctxt->use & CNTXT_INCMNG && + F_ON(F_ENABLE_INCOMING_CHECK,ps_global) && + F_ON(F_ENABLE_INCOMING,ps_global)){ + rv = max_slot_size - strlen(FLDR_NAME(f)) - l; + if(F_OFF(F_ENABLE_FAST_RECENT, ps_global)){ + rv -= strlen(comatose(f->countrecent)); + rv -= strlen(comatose(f->messages)); + rv -= 3; + } + if(f->skipped) + gf_puts(".", pc); + else if(is_last > 0) + gf_puts("_", pc); + else if (is_last < 0) + gf_puts("~", pc); + if(f->skipped || is_last) + rv--; + + rv -= 1; /* len += 1 was added as space between folders */ + if(F_OFF(F_ENABLE_FAST_RECENT, ps_global)){ + for (i = 0; i < rv; i++) gf_puts(" ", pc); + gf_puts("[", pc); + gf_puts(comatose(f->countrecent), pc); + gf_puts("/", pc); + gf_puts(comatose(f->messages), pc); + gf_puts("]", pc); + rv = max_slot_size - strlen(FLDR_NAME(f)) - l - 1; + } + else + rv = max_slot_size - strlen(FLDR_NAME(f)) - l - 1 - rv; + } + return rv; +} + int folder_list_write_prefix(f, flags, pc) @@ -2224,6 +2305,19 @@ p = strchr(p, fs->context->dir->delim)) name = ++p; + if(fs->context->use & CNTXT_INCMNG){ + FOLDER_S *f; + int total = folder_total(FOLDERS(fs->context)), index; + for(index = folder_index(ps_global->inbox_name, fs->context, FI_FOLDER); + index >= 0 && index < total + && (f = folder_entry(index, FOLDERS(fs->context))) + && !f->isdir; index++) + if(!strcmp(fs->first_folder, f->name)){ + name = FLDR_NAME(f); + break; + } + } + for(h = handles; h; h = h->next) if(h->h.f.context == fs->context){ if(!h_found) /* match at least given context */ @@ -2462,7 +2556,16 @@ "Empty folder collection. No folder to rename!"); break; - + + + /*------- Check incoming forlders -------*/ + case MC_FORCECHECK: + ps_global->force_check_now = 1; + rv = (new_mail_incfolder(ps_global,MC_FORCECHECK) && + (ps_global->refresh_list & IF_REFRESH_STRONG)) ? 1 : 0; + ps_global->refresh_list &= IF_REFRESH_NONE; + break; + /*-------------- Delete --------------------*/ case MC_DELETE : @@ -2737,10 +2840,14 @@ tot = strm->nmsgs; rec = strm->recent; } + if(folder->origrecent > rec) + folder->origrecent = rec; + folder->countrecent = rec > folder->origrecent + ? rec - folder->origrecent : 0L; } else{ tot = strm->nmsgs; - rec = sp_recent_since_visited(strm); + rec = folder->countrecent = sp_recent_since_visited(strm); } } /* @@ -2759,6 +2866,10 @@ && mm_status_result.flags & SA_RECENT){ tot = mm_status_result.messages; rec = mm_status_result.recent; + if(folder->origrecent > rec) + folder->origrecent = rec; + folder->countrecent = rec > folder->origrecent + ? rec - folder->origrecent : 0L; gotit++; } } @@ -2771,6 +2882,10 @@ if(strm){ tot = strm->nmsgs; rec = strm->recent; + if(folder->origrecent > rec) + folder->origrecent = rec; + folder->countrecent = rec > folder->origrecent + ? rec - folder->origrecent : 0L; gotit++; pine_mail_close(strm); } @@ -2783,10 +2898,16 @@ if(we_cancel) cancel_busy_alarm(-1); - if(gotit) + if(gotit){ sprintf(tmp_output, "%lu total message%.2s, %lu of them recent", tot, plural(tot), rec); + ps_global->refresh_list |= IF_REFRESH_STRONG; + if (rec > 0L) + folder->notified = 1; + folder->selected = folder->countrecent > 0L + ? 1 : folder->user_selected; + } } }else strncpy(tmp_output, "No folder to check! Can't get recent info", @@ -3323,8 +3444,10 @@ case 'f' : /* flip selection */ n = folder_total(FOLDERS(context)); for(total = i = 0; i < n; i++) - if(f = folder_entry(i, FOLDERS(context))) + if(f = folder_entry(i, FOLDERS(context))){ f->selected = !f->selected; + f->user_selected = f->selected; + } return(1); /* repaint */ @@ -3467,15 +3590,64 @@ CONTEXT_S *context; { int i, n, total; + FOLDER_S *f; n = folder_total(FOLDERS(context)); - for(total = i = 0; i < n; i++) - if(folder_entry(i, FOLDERS(context))->selected) + for(total = i = 0; i < n; i++){ + f = folder_entry(i, FOLDERS(context)); + if(f->selected) total++; + } return(total); } +void +update_incoming_folder_data(stream, context) + MAILSTREAM *stream; + CONTEXT_S *context; +{ + FOLDER_S *f = incoming_folder_data(stream, context); + + if(f){ + f->origrecent = f->recent = stream->recent; + f->messages = stream->nmsgs; + } +} + + +FOLDER_S * +incoming_folder_data(stream, cntxt) + MAILSTREAM *stream; + CONTEXT_S *cntxt; +{ + long index, total, done = 0; + FOLDER_S *f = NULL; + + if (cntxt && cntxt->use & CNTXT_INCMNG){ + total = folder_total(FOLDERS(cntxt)); + for (index = 0L; index < total ; index++){ + f = folder_entry(index, FOLDERS(cntxt)); + if (!strcmp(STREAMNAME(stream), FLDR_NAME(f))){ + done++; + break; + } + } + } + if (!done) + f = NULL; + return f; +} + +int +need_folder_report(folder) + char *folder; +{ + return (ps_global->VAR_INCOMING_FOLDERS_CHECK && + ((ps_global->VAR_INCOMING_FOLDERS_CHECK[0] == '*') || + strstr(ps_global->VAR_INCOMING_FOLDERS_CHECK, folder))); +} + SELECTED_S * new_selected() @@ -4204,6 +4376,7 @@ if(f = folder_entry(index, FOLDERS(context))){ f->selected = !f->selected; + f->user_selected = f->selected; return((*func)(context, index)); } return 1; @@ -7223,6 +7396,9 @@ FOLDERS(context) = init_folder_entries(); init_incoming_folder_list(ps_global, context); init_inbox_mapping(ps_global->VAR_INBOX_PATH, context); + ps_global->force_check_now = 1; /* sorry about this */ + new_mail_incfolder(ps_global,MC_FORCECHECK); + ps_global->refresh_list |= IF_REFRESH_STRONG; } @@ -8406,11 +8582,17 @@ long *find_recent; int *did_cancel; { - int index, recent = 0, failed_status = 0, try_fast; + int index, recent = 0, failed_status = 0, try_fast, done = 0; char prompt[128]; FOLDER_S *f = NULL; char tmp[MAILTMPLEN]; - + char *test_current = cpystr(current); + int cur_indx = folder_index(ps_global->cur_folder, cntxt, FI_FOLDER); + int loop = !strcmp(next, ps_global->cur_folder) ? 0 : + (folder_index(test_current, cntxt, FI_FOLDER) <= cur_indx + ? 1 : 0); + int last = !strcmp(ps_global->cur_folder, ps_global->inbox_name) + ? 1 : cur_indx; /* note: find_folders may assign "stream" */ build_folder_list(streamp, cntxt, NULL, NULL, @@ -8421,7 +8603,9 @@ if(find_recent) *find_recent = 0L; - for(index = folder_index(current, cntxt, FI_FOLDER) + 1; + +find_new_message: + for(index = folder_index(test_current, cntxt, FI_FOLDER) + 1; index > 0 && index < folder_total(FOLDERS(cntxt)) && (f = folder_entry(index, FOLDERS(cntxt))) @@ -8432,6 +8616,11 @@ int rv, we_cancel = 0, mlen, match; char msg_buf[MAX_BM+1], mbuf[MAX_BM+1]; + if (loop && (index == last)){ + done++; + break; + } + /* must be a folder and it can't be the current one */ if(ps_global->context_current == ps_global->context_list && !strcmp(ps_global->cur_folder, FLDR_NAME(f))) @@ -8583,19 +8772,196 @@ } } - if(f && (!find_recent || recent)) + if(f && (!find_recent || recent)){ strcpy(next, FLDR_NAME(f)); + done++; + } else *next = '\0'; + if (!done && F_ON(F_AUTO_CIRCULAR_TAB,ps_global) + && strcmp(test_current,ps_global->inbox_name)){ + done++; loop++; + if (test_current) + fs_give((void **)&test_current); + test_current = cpystr(ps_global->inbox_name); + goto find_new_message; + } + /* BUG: how can this be made smarter so we cache the list? */ free_folder_list(cntxt); + if (test_current) + fs_give((void **)&test_current); return((*next) ? next : NULL); } /* + * next_folder - given a current folder in a context, return the next in + * the list, or NULL if no more or there's a problem. + */ +int +next_folder_check(streamp, cntxt, find_recent, find_messages, f, opstrm) + MAILSTREAM **streamp; + CONTEXT_S *cntxt; + long *find_recent, *find_messages; + FOLDER_S *f; + int *opstrm; +{ + char *next; + int index, failed_status = 0; + + /* note: find_folders may assign "stream" */ + build_folder_list(streamp, cntxt, NULL, NULL, BFL_NONE); + + if(find_recent && find_messages && opstrm){ + MAILSTREAM *stream = NULL; + int rv, we_cancel = 0; + char msg_buf[MAX_SCREEN_COLS+1] = {'\0'}; + char tmp[MAILTMPLEN]; + + *opstrm = 0; /* default value */ + *find_recent = f->recent; /* default value. Return this if */ + *find_messages = f->messages; /* not requested */ + + if(((stream = sp_stream_get(context_apply(tmp, cntxt, f->name,sizeof(tmp)), + SP_MATCH | SP_RO_OK)) != NULL) + || ((stream = already_open_stream(tmp, AOS_NONE)) != NULL)){ + *opstrm = 1; + (void) pine_mail_ping(stream); + if(stream == ps_global->mail_stream){ + next = new_mail_in_open_stream(stream, find_recent, find_messages); + if(f->origrecent > *find_recent) + f->origrecent = *find_recent; + if(ps_global->in_fld_list) + f->notified = f->countrecent == 0 + ? (*find_recent == f->recent) + && (*find_messages == f->messages) + : 1; + f->countrecent = *find_recent > f->origrecent + ? *find_recent - f->origrecent : 0L; + } + else{ + *find_recent = f->countrecent = sp_recent_since_visited(stream); + *find_messages = stream->nmsgs; + next = *find_recent ? STREAMNAME(stream) : NULL; + } + free_folder_list(cntxt); + return next ? 1 : 0; + } + else if ((F_ON(F_ENABLE_FAST_RECENT, ps_global) && + F_OFF(F_ENABLE_INCOMING_RECHECK,ps_global) && + (f->notified || f->selected)) + || (f->selected && f->user_selected)){ + + next = f->notified ? FLDR_NAME(f) : NULL; + free_folder_list(cntxt); + return next ? 1 : 0; + } + else if (need_folder_report(FLDR_NAME(f)) + && (strcmp(ps_global->cur_folder, FLDR_NAME(f)) || !stream)){ + + we_cancel = busy_alarm(1, msg_buf, NULL, 1); + + /* First, get a stream for the test */ + if(streamp && *streamp){ + if(context_same_stream(cntxt, f->name, *streamp)) + stream = *streamp; + else{ + mail_close(*streamp); + *streamp = NULL; + } + } + + if(!stream) + stream = sp_stream_get(context_apply(tmp, cntxt, f->name, + sizeof(tmp)), SP_SAME); + + if(!stream){ + if(!(stream = sp_stream_status_get( + context_apply(tmp, cntxt, f->name, + sizeof(tmp))))){ + stream = (*f->name == '{') + ? mail_open (NIL,f->name,OP_HALFOPEN) : NIL; + sp_add_status(stream); + } + } + + if(F_OFF(F_ENABLE_FAST_RECENT, ps_global) + || !((rv = folder_exists(cntxt,f->name)) + & (FEX_ISMARKED | FEX_UNMARKED))){ + extern MAILSTATUS mm_status_result; + + if((F_ON(F_ENABLE_FAST_RECENT, ps_global) && + (rv == 0 || rv & FEX_ERROR))){ + failed_status = 1; + mm_status_result.flags = 0L; + } + else{ + if(stream){ + if(!context_status_full(cntxt, stream, + f->name, SA_RECENT | SA_MESSAGES, + &f->uidvalidity, + &f->uidnext)){ + failed_status = 1; + mm_status_result.flags = 0L; + } + } + else{ + if(!context_status_streamp_full(cntxt, streamp, f->name, + SA_RECENT | SA_MESSAGES, + &f->uidvalidity, + &f->uidnext)){ + failed_status = 1; + mm_status_result.flags = 0L; + } + } + } + + if (!failed_status){ + *find_messages = mm_status_result.messages; + *find_recent = mm_status_result.recent; + } + else{ + /* We failed, so return old data, even if this is wrong. + We can not zero out the values because the failure may + be temporary and in this case when the folder is back + we would find new mail that simply does not exist in + the folder. + */ + mm_status_result.messages = f->messages; + mm_status_result.recent = f->recent; + } + rv = (((mm_status_result.flags & SA_RECENT) || + (F_OFF(F_ENABLE_FAST_RECENT,ps_global) + && (mm_status_result.recent != f->recent))) + && (*find_recent = mm_status_result.recent)) + ? FEX_ISMARKED : 0; + } + + if(we_cancel) + cancel_busy_alarm(0); + + failed_status = 0; + + if(f->origrecent > *find_recent) + f->origrecent = *find_recent; + if(rv & FEX_ISMARKED){ + next = f ? FLDR_NAME(f) : NULL; + free_folder_list(cntxt); + f->countrecent = *find_recent > f->origrecent + ? *find_recent - f->origrecent : 0L; + return next ? 1 : 0; + } + } + return 0; + } +} + + + +/* * folder_is_nick - check to see if the given name is a nickname * for some folder in the given context... * diff -ru pine4.64/pine/help.c pine4.64.SuSE/pine/help.c --- pine4.64/pine/help.c 2004-11-23 19:29:47.000000000 +0100 +++ pine4.64.SuSE/pine/help.c 2006-02-14 14:45:22.000000000 +0100 @@ -168,7 +168,7 @@ int flags; { char **shown_text; - int cmd = MC_NONE; + int cmd = MC_NONE, in_fld_list; long offset = 0L; char *error = NULL, tmp_title[MAX_SCREEN_COLS + 1]; STORE_S *store; @@ -181,6 +181,8 @@ dprint(1, (debugfile, "\n\n ---- HELPER ----\n")); + in_fld_list = ps_global->in_fld_list; + ps_global->in_fld_list = 0; #ifdef HELPFILE if(otext) shown_text = otext; @@ -396,6 +398,7 @@ free_list_array(&dynamic_text); #endif + ps_global->in_fld_list = in_fld_list; return(cmd); } diff -ru pine4.64/pine/imap.c pine4.64.SuSE/pine/imap.c --- pine4.64/pine/imap.c 2005-09-13 00:04:25.000000000 +0200 +++ pine4.64.SuSE/pine/imap.c 2006-02-14 14:45:22.000000000 +0100 @@ -694,6 +694,7 @@ int len, rc, q_line, flags; int oespace, avail, need, save_dont_use; struct servent *sv; + time_t now; #define NETMAXPASSWD 100 dprint(9, (debugfile, "mm_login trial=%ld user=%s service=%s%s%s\n", @@ -702,9 +703,10 @@ mb->port ? " port=" : "", mb->port ? comatose(mb->port) : "")); q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3); + now = time(0); /* make sure errors are seen */ - if(ps_global->ttyo) + if(ps_global->ttyo && !ps_global->checking_incfld) flush_status_messages(0); /* @@ -1026,12 +1028,13 @@ /* default */ if(rc == 0 && !*user) strncpy(user, defuser, NETMAXUSER); - + if(rc != 4) break; } if(rc == 1 || !user[0]) { + ps_global->cancelproc = (rc == 1); user[0] = '\0'; pwd[0] = '\0'; } @@ -1213,10 +1216,12 @@ } if(rc == 1 || !pwd[0]) { + ps_global->cancelproc = (rc == 1); user[0] = pwd[0] = '\0'; return; } + ps_global->login_time = time(0) - now; nopwpmt: /* remember the password for next time */ if(F_OFF(F_DISABLE_PASSWORD_CACHING,ps_global)) @@ -1570,7 +1575,7 @@ } - if(mail_status(stream, source, flags)){ + if(!ps_global->cancelproc && mail_status(stream, source, flags)){ DRIVER *d; int is_news = 0; @@ -1770,13 +1775,14 @@ || !strucmp(d->name, "nntp"))) flags |= SA_MULNEWSRC; } - - ret = mail_status(stream, mailbox, flags); /* non #move case */ + if(!ps_global->cancelproc) + ret = mail_status(stream, mailbox, flags); /* non #move case */ } if(ourstream) pine_mail_close(ourstream); + ps_global->cancelproc = 0; return ret; } @@ -1950,11 +1956,17 @@ #endif if(elapsed >= (long)ps_global->tcp_query_timeout){ + if(!ps_global->checking_incfld){ sprintf(pmt, "Waited %s seconds for server reply. Break connection to server", long2string(elapsed)); - if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y') + if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){ + ps_global->cancelproc = 1; return(0L); + } + } + else + rv = 0L; } return(rv); @@ -1996,6 +2008,7 @@ if(elapsed >= (long)ps_global->tcp_query_timeout){ int clear_inverse; + if(!ps_global->checking_incfld){ ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)); if(clear_inverse = !InverseState()) StartInverse(); @@ -2009,13 +2022,18 @@ fflush(stdout); flush_input(); ch = read_char(7); - if(ch == 'y' || ch == 'Y') + if(ch == 'y' || ch == 'Y'){ + ps_global->cancelproc = 1; rv = 0L; + } if(clear_inverse) EndInverse(); ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)); + } + else + rv = 0L; } if(rv == 1L){ /* just warn 'em something's up */ @@ -2032,6 +2050,36 @@ return(rv); } +QUOTALIST *pine_quotalist_copy (pquota) + QUOTALIST *pquota; +{ + QUOTALIST *cquota = NULL; + + if(pquota){ + cquota = mail_newquotalist(); + if (pquota->name && *pquota->name){ + cquota->name = (char *) fs_get((strlen(pquota->name) + 1)*sizeof(char)); + cquota->name = cpystr(pquota->name); + } + cquota->usage = pquota->usage; + cquota->limit = pquota->limit; + if (pquota->next) + cquota->next = pine_quotalist_copy(pquota->next); + } + return cquota; +} + + +/* C-client callback to handle quota */ + +void +pine_parse_quota (stream, msg, pquota) + MAILSTREAM *stream; + unsigned char *msg; + QUOTALIST *pquota; +{ + ps_global->quota = pine_quotalist_copy (pquota); +} /* * C-client callback to handle SSL/TLS certificate validation failures diff -ru pine4.64/pine/init.c pine4.64.SuSE/pine/init.c --- pine4.64/pine/init.c 2005-09-12 20:53:17.000000000 +0200 +++ pine4.64.SuSE/pine/init.c 2006-02-14 14:45:25.000000000 +0100 @@ -66,11 +66,15 @@ #include "headers.h" #include "../c-client/imap4r1.h" /* for LEVELSTATUS() */ +#ifdef LC_CTYPE +# include +#endif typedef enum {Sapling, Seedling, Seasoned} FeatureLevel; #define TO_BAIL_THRESHOLD 60 +#define INCFLD_THRESHOLD 5 #define METASTR "\nremote-abook-metafile=" static char meta_prefix[] = ".ab"; @@ -158,6 +162,8 @@ CONF_TXT_T cf_text_incoming_folders[] = "List of incoming msg folders besides INBOX, e.g. ={host2}inbox, {host3}inbox\n# Syntax: optnl-label {optnl-imap-host-name}folder-path"; +CONF_TXT_T cf_incoming_folders_check[] = "List of incoming folders to be checked for new mail"; + CONF_TXT_T cf_text_folder_collections[] = "List of directories where saved-message folders may be. First one is\n# the default for Saves. Example: Main {host1}mail/[], Desktop mail\\[]\n# Syntax: optnl-label {optnl-imap-hostname}optnl-directory-path[]"; CONF_TXT_T cf_text_news_collections[] = "List, only needed if nntp-server not set, or news is on a different host\n# than used for NNTP posting. Examples: News *[] or News *{host3/nntp}[]\n# Syntax: optnl-label *{news-host/protocol}[]"; @@ -214,12 +220,52 @@ CONF_TXT_T cf_text_sort_key[] = "Sets presentation order of messages in Index. Choices:\n# Subject, From, Arrival, Date, Size, To, Cc, OrderedSubj, Score, and Thread.\n# Order may be reversed by appending /Reverse. Default: \"Arrival\"."; +CONF_TXT_T cf_text_thread_sort_key[] = "#Sets presentation order of threads in thread index. Choices:\n#arrival, and thread."; + CONF_TXT_T cf_text_addrbook_sort_rule[] = "Sets presentation order of address book entries. Choices: dont-sort,\n# fullname-with-lists-last, fullname, nickname-with-lists-last, nickname\n# Default: \"fullname-with-lists-last\"."; CONF_TXT_T cf_text_folder_sort_rule[] = "Sets presentation order of folder list entries. Choices: alphabetical,\n# alpha-with-dirs-last, alpha-with-dirs-first.\n# Default: \"alpha-with-directories-last\"."; +CONF_TXT_T cf_text_compose_rules[] = "Allows a user to set rules when composing messages."; + +CONF_TXT_T cf_text_forward_rules[] = "Allows a user to set rules when forwarding messages."; + +CONF_TXT_T cf_text_reply_rules[] = "Allows a user to set rules when replying messages."; + +CONF_TXT_T cf_text_index_rules[] = "Allows a user to supercede global index format variable in designated folders."; + +CONF_TXT_T cf_text_replace_rules[] = "Allows a user to change the form a specify field in the index-format is \n# displayed."; + +CONF_TXT_T cf_text_reply_indent_rules[] = "Allows a user to change the form a specify a reply-indent-string\n# based of rules."; + +CONF_TXT_T cf_text_reply_leadin_rules[] = "Allows a user to replace the reply-leadin message based on different parameters."; + +CONF_TXT_T cf_text_reply_subject_rules[] = "Allows a user to replace the subject of a message in a customs based way"; + +CONF_TXT_T cf_text_thread_displaystyle_rule[] = "Allows a user to specify the threading style of specific folders"; + +CONF_TXT_T cf_text_thread_indexstyle_rule[] = "Allows a user to specify the threading index style of specific folders"; + +CONF_TXT_T cf_text_save_rules[] = "Allows a user to specify a save folder message for specific senders or folders."; + +CONF_TXT_T cf_text_smtp_rules[] = "Allows a user to specify a smtp server to be used when sending e-mail,\n# according to the rules specified here."; + +CONF_TXT_T cf_text_sort_rules[] = "Allows a user to specify the sort default order of a specific folder."; + +CONF_TXT_T cf_text_startup_rules[] = "Allows a user to specify the position of a highlighted message when opening a \n# folder."; + CONF_TXT_T cf_text_character_set[] = "Reflects capabilities of the display you have. Default: US-ASCII.\n# Typical alternatives include ISO-8859-x, (x is a number between 1 and 9)."; +CONF_TXT_T cf_text_assumed_charset[] = "When MIME charset information is missing in Content-Type header field.\n# Message is assumed to be in this charset. Default: US-ASCII. Typical values\n# include ISO-8859-x, ISO-2022-JP, EUC-KR, GB2312, and Big5. The value of\n# header fields which are not encoded per RFC 2047\n# is also assumed to be\n# in this charset."; + +CONF_TXT_T cf_text_charset_aliases[] = "List of charset aliases. Each alias is a pair of charsets delimetered by a\n# single colon, the first one being an alias to the second one. The latter is\n# usually standard/prefered MIME name while the former is non-standard name used\n# by some email clients. For instance, you may have 'x-big5:big5,euc-cn:gb2312'"; + +#ifdef HAVE_ICONV +CONF_TXT_T cf_text_iconv_aliases[] = "List of charset aliases to use with iconv(). Each alias is a pair of\n# charsets delimetered by a single colon, the first one being an alias to the\n# second one. The former is usually standard/prefered MIME name while the\n# latter is non-standard name used by iconv(3) on your system.\n#For example,\n# your iconv may use non-standard 'UTF8' for the standard 'UTF-8'. In that\n# case, you can put 'UTF-8:UTF8' here."; + +CONF_TXT_T cf_text_send_charset[] = "Specifies the MIME charset that a message will be sent in. If not set,\n# the value of character set will be used."; +#endif + CONF_TXT_T cf_text_editor[] = "Specifies the program invoked by ^_ in the Composer,\n# or the \"enable-alternate-editor-implicitly\" feature."; CONF_TXT_T cf_text_speller[] = "Specifies the program invoked by ^T in the Composer."; @@ -228,6 +274,8 @@ CONF_TXT_T cf_text_fillcol[] = "Specifies the column of the screen where the composer should wrap."; +CONF_TXT_T cf_special_text_color[] = "Specifies a comma separated list of text and regular expresions that Pine\n# will highlight"; + CONF_TXT_T cf_text_replystr[] = "Specifies the string to insert when replying to a message."; CONF_TXT_T cf_text_quotereplstr[] = "Specifies the string to replace quotes with when viewing a message."; @@ -242,6 +290,8 @@ CONF_TXT_T cf_text_inc_startup[] = "Sets message which cursor begins on. Choices: first-unseen, first-recent,\n# first-important, first-important-or-unseen, first-important-or-recent,\n# first, last. Default: \"first-unseen\"."; +CONF_TXT_T cf_text_inc_check[] = "Sets how and when checks for new mail should happen. Choices: automatic,\n# automatic-after-first-manual-check, manual-only, Default: automatic"; + CONF_TXT_T cf_pruning_rule[] = "Allows a default answer for the prune folder questions. Choices: yes-ask,\n# yes-no, no-ask, no-no, ask-ask, ask-no. Default: \"ask-ask\"."; CONF_TXT_T cf_reopen_rule[] = "Controls behavior when reopening an already open folder."; @@ -378,6 +428,8 @@ CONF_TXT_T cf_text_tcp_query_timeo[] = "If this much time has elapsed at the time of a tcp read or write\n# timeout, pine will ask if you want to break the connection.\n# Default is 60 seconds, minimum is 5, maximum is 1000."; +CONF_TXT_T cf_text_inc_fld_timeout[] = "If this much time has elapsed at the time of a tcp read or write\n# timeout while checking for new mail in an incoming folder, pine will break the connection.\n# Default is 5 seconds, minimum is 2, maximum is 60."; + CONF_TXT_T cf_text_rsh_open_timeo[] = "Sets the time in seconds that Pine will attempt to open a UNIX remote\n# shell connection. The default is 15, min is 5, and max is unlimited.\n# Zero disables rsh altogether."; CONF_TXT_T cf_text_rsh_path[] = "Sets the name of the command used to open a UNIX remote shell connection.\n# The default is typically /usr/ucb/rsh."; @@ -418,6 +470,9 @@ CONF_TXT_T cf_text_newsrc_path[] = "Full path and name of NEWSRC file"; +CONF_TXT_T cf_text_maildir_location[] = "Location relative to your HOME directory of the directory where your INBOX\n# for the maildir format is located. Default value is \"Maildir\". If your\n# inbox is located at \"~/Maildir\" you do not need to change this value.\n# A common value is also \".maildir\""; + + /* these are used to report folder directory creation problems */ CONF_TXT_T init_md_exists[] = "The \"%s\" subdirectory already exists, but it is not writable by Pine so Pine cannot run. Please correct the permissions and restart Pine."; @@ -471,6 +526,8 @@ cf_text_nntp_server}, {"inbox-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_inbox_path}, +{"incoming-folders-to-check", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, + cf_incoming_folders_check}, {"incoming-archive-folders", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, cf_text_archived_folders}, {"pruned-folders", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, @@ -511,6 +568,8 @@ cf_text_fcc_name_rule}, {"sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_sort_key}, +{"thread-sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, + cf_text_thread_sort_key}, {"addrbook-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_addrbook_sort_rule}, {"folder-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, @@ -519,6 +578,8 @@ cf_text_goto_default}, {"incoming-startup-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_inc_startup}, +{"incoming-check-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, + cf_text_inc_check}, {"pruning-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_pruning_rule}, {"folder-reopen-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, @@ -533,14 +594,54 @@ cf_text_thread_exp_char}, {"threading-lastreply-character", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_thread_lastreply_char}, +{"threading-display-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_thread_displaystyle_rule}, +{"threading-index-style-rule", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_thread_indexstyle_rule}, +{"compose-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_compose_rules}, +{"forward-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_forward_rules}, +{"index-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_index_rules}, +{"replace-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_replace_rules}, +{"reply-indent-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_reply_indent_rules}, +{"reply-leadin-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_reply_leadin_rules}, +{"reply-subject-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_reply_subject_rules}, +{"save-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_save_rules}, +{"smtp-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_smtp_rules}, +{"sort-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_sort_rules}, +{"startup-rules", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_text_startup_rules}, {"character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_character_set}, +#ifdef ENABLE_SEND_CHARSET +{"send-charset", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, + cf_text_send_charset}, +#endif +{"assumed-charset", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, + cf_text_assumed_charset}, +{"charset-aliases", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, + cf_text_charset_aliases}, +#ifdef HAVE_ICONV +{"iconv-aliases", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, + cf_text_iconv_aliases}, +#endif {"editor", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, cf_text_editor}, {"speller", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_speller}, {"composer-wrap-column", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_fillcol}, +{"special-text-color", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, + cf_special_text_color}, {"reply-indent-string", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, cf_text_replystr}, {"reply-leadin", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, @@ -605,6 +706,8 @@ cf_text_news_active}, {"news-spool-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_news_spooldir}, +{"maildir-location", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, + cf_text_maildir_location}, {"upload-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_upload_cmd}, {"upload-command-prefix", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, @@ -673,6 +776,8 @@ cf_text_tcp_write_timeo}, {"tcp-query-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_tcp_query_timeo}, +{"inc-fld-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, + cf_text_inc_fld_timeout}, {"rsh-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, cf_text_rsh_command}, {"rsh-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, @@ -772,6 +877,8 @@ {"quote3-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, NULL}, {"signature-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, NULL}, {"signature-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, NULL}, +{"special-text-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, NULL}, +{"special-text-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, NULL}, {"prompt-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, NULL}, {"prompt-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, NULL}, {"index-to-me-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, NULL}, @@ -1475,7 +1582,7 @@ register struct variable *vars = ps->vars; int obs_header_in_reply, /* the obs_ variables are to */ obs_old_style_reply, /* support backwards compatibility */ - obs_save_by_sender, i, def_sort_rev; + obs_save_by_sender, i, def_sort_rev, thread_def_sort_rev; long rvl; PINERC_S *fixedprc = NULL; FeatureLevel obs_feature_level; @@ -1484,6 +1591,15 @@ /*--- The defaults here are defined in os-xxx.h so they can vary per machine ---*/ +#ifdef LC_CTYPE + setlocale(LC_CTYPE, ""); /* needed for using nl_langinfo */ + GLO_CHAR_SET = cpystr(nl_langinfo(CODESET)); + /* if codeset indicates that we are in an US-ASCII locale, */ + if (!strcmp(GLO_CHAR_SET, "ANSI_X3.4-1968")) { + fs_give((void **) &(GLO_CHAR_SET)); + cpystr("US-ASCII"); /* default to US-ASCII */ + } +#endif GLO_PRINTER = cpystr(DF_DEFAULT_PRINTER); GLO_ELM_STYLE_SAVE = cpystr(DF_ELM_STYLE_SAVE); @@ -1497,6 +1613,7 @@ GLO_FEATURE_LEVEL = cpystr(DF_FEATURE_LEVEL); GLO_OLD_STYLE_REPLY = cpystr(DF_OLD_STYLE_REPLY); GLO_SORT_KEY = cpystr(DF_SORT_KEY); + GLO_THREAD_SORT_KEY = cpystr(DF_THREAD_SORT_KEY); GLO_SAVED_MSG_NAME_RULE = cpystr(DF_SAVED_MSG_NAME_RULE); GLO_FCC_RULE = cpystr(DF_FCC_RULE); GLO_AB_SORT_RULE = cpystr(DF_AB_SORT_RULE); @@ -1507,6 +1624,7 @@ GLO_REMOTE_ABOOK_VALIDITY = cpystr(DF_REMOTE_ABOOK_VALIDITY); GLO_GOTO_DEFAULT_RULE = cpystr(DF_GOTO_DEFAULT_RULE); GLO_INCOMING_STARTUP = cpystr(DF_INCOMING_STARTUP); + GLO_INCOMING_RULE = cpystr(DF_INCOMING_RULE); GLO_PRUNING_RULE = cpystr(DF_PRUNING_RULE); GLO_REOPEN_RULE = cpystr(DF_REOPEN_RULE); GLO_THREAD_DISP_STYLE = cpystr(DF_THREAD_DISP_STYLE); @@ -1862,8 +1980,20 @@ set_current_val(&vars[V_POSTPONED_FOLDER], TRUE, TRUE); set_current_val(&vars[V_READ_MESSAGE_FOLDER], TRUE, TRUE); set_current_val(&vars[V_FORM_FOLDER], TRUE, TRUE); + set_current_val(&vars[V_COMPOSE_RULES], TRUE, TRUE); + set_current_val(&vars[V_FORWARD_RULES], TRUE, TRUE); + set_current_val(&vars[V_INDEX_RULES], TRUE, TRUE); + set_current_val(&vars[V_REPLACE_RULES], TRUE, TRUE); + set_current_val(&vars[V_REPLY_INDENT_RULES], TRUE, TRUE); + set_current_val(&vars[V_REPLY_LEADIN_RULES], TRUE, TRUE); + set_current_val(&vars[V_RESUB_RULES], TRUE, TRUE); + set_current_val(&vars[V_SAVE_RULES], TRUE, TRUE); + set_current_val(&vars[V_SMTP_RULES], TRUE, TRUE); + set_current_val(&vars[V_SORT_RULES], TRUE, TRUE); + set_current_val(&vars[V_STARTUP_RULES], TRUE, TRUE); set_current_val(&vars[V_EDITOR], TRUE, TRUE); set_current_val(&vars[V_SPELLER], TRUE, TRUE); + set_current_val(&vars[V_SPECIAL_TEXT], TRUE, TRUE); set_current_val(&vars[V_IMAGE_VIEWER], TRUE, TRUE); set_current_val(&vars[V_BROWSER], TRUE, TRUE); set_current_val(&vars[V_SMTP_SERVER], TRUE, TRUE); @@ -2082,6 +2212,13 @@ else ps->tcp_query_timeout = i; + set_current_val(&vars[V_INCFLDTIMEO], TRUE, TRUE); + ps->incfld_timeout = i = INCFLD_THRESHOLD; + if(VAR_INCFLDTIMEO && SVAR_INCFLDQUERY(ps, i, tmp_20k_buf)) + init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); + else + ps->incfld_timeout = i; + set_current_val(&vars[V_NEWSRC_PATH], TRUE, TRUE); if(VAR_NEWSRC_PATH && VAR_NEWSRC_PATH[0]) mail_parameters(NULL, SET_NEWSRC, (void *)VAR_NEWSRC_PATH); @@ -2096,6 +2233,10 @@ mail_parameters(NULL, SET_NEWSSPOOL, (void *)VAR_NEWS_SPOOL_DIR); + set_current_val(&vars[V_MAILDIR_LOCATION], TRUE, TRUE); + if(VAR_MAILDIR_LOCATION && VAR_MAILDIR_LOCATION[0]) + maildir_parameters(SET_INBOXPATH, (void *)VAR_MAILDIR_LOCATION); + /* guarantee a save default */ set_current_val(&vars[V_DEFAULT_SAVE_FOLDER], TRUE, TRUE); if(!VAR_DEFAULT_SAVE_FOLDER || !VAR_DEFAULT_SAVE_FOLDER[0]) @@ -2117,6 +2258,9 @@ set_current_val(&vars[V_OLD_STYLE_REPLY], TRUE, TRUE); obs_old_style_reply = !strucmp(VAR_OLD_STYLE_REPLY, "yes"); + /* needed in process_feature_list */ + set_current_val(&vars[V_CHAR_SET], TRUE, TRUE); + set_feature_list_current_val(&vars[V_FEATURE_LIST]); process_feature_list(ps, VAR_FEATURE_LIST, (obs_feature_level == Seasoned) ? 1 : 0, @@ -2125,6 +2269,14 @@ set_current_val(&vars[V_SIGNATURE_FILE], TRUE, TRUE); set_current_val(&vars[V_LITERAL_SIG], TRUE, TRUE); set_current_val(&vars[V_CHAR_SET], TRUE, TRUE); +#ifdef ENABLE_SEND_CHARSET + set_current_val(&vars[V_SEND_CHARSET], TRUE, TRUE); +#endif + set_current_val(&vars[V_ASSUMED_CHAR_SET], TRUE, TRUE); + set_current_val(&vars[V_CHAR_SET_ALIASES], TRUE, TRUE); +#ifdef HAVE_ICONV + set_current_val(&vars[V_ICONV_ALIASES], TRUE, TRUE); +#endif set_current_val(&vars[V_GLOB_ADDRBOOK], TRUE, TRUE); set_current_val(&vars[V_ADDRESSBOOK], TRUE, TRUE); set_current_val(&vars[V_FORCED_ABOOK_ENTRY], TRUE, TRUE); @@ -2329,8 +2481,9 @@ set_current_val(&vars[V_PRUNED_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE); set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE); + set_current_val(&vars[V_INCOMING_FOLDERS_CHECK], TRUE, TRUE); set_current_val(&vars[V_SORT_KEY], TRUE, TRUE); - if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev) == -1){ + if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev,0) == -1){ sprintf(tmp_20k_buf, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY); init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); ps->def_sort = SortArrival; @@ -2339,6 +2492,17 @@ else ps->def_sort_rev = def_sort_rev; + set_current_val(&vars[V_THREAD_SORT_KEY], TRUE, TRUE); + if(decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, + &thread_def_sort_rev, 1) == -1){ + sprintf(tmp_20k_buf, "Sort type \"%s\" is invalid", VAR_THREAD_SORT_KEY); + init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf); + ps->thread_def_sort = SortThread; + ps->thread_def_sort_rev = 0; + } + else + ps->thread_def_sort_rev = thread_def_sort_rev; + cur_rule_value(&vars[V_SAVED_MSG_NAME_RULE], TRUE, TRUE); {NAMEVAL_S *v; int i; for(i = 0; v = save_msg_rules(i); i++) @@ -2366,11 +2530,14 @@ cur_rule_value(&vars[V_TITLEBAR_COLOR_STYLE], TRUE, TRUE); cur_rule_value(&vars[V_FLD_SORT_RULE], TRUE, TRUE); cur_rule_value(&vars[V_INCOMING_STARTUP], TRUE, TRUE); + cur_rule_value(&vars[V_INCOMING_RULE], TRUE, TRUE); cur_rule_value(&vars[V_PRUNING_RULE], TRUE, TRUE); cur_rule_value(&vars[V_REOPEN_RULE], TRUE, TRUE); cur_rule_value(&vars[V_GOTO_DEFAULT_RULE], TRUE, TRUE); cur_rule_value(&vars[V_THREAD_DISP_STYLE], TRUE, TRUE); cur_rule_value(&vars[V_THREAD_INDEX_STYLE], TRUE, TRUE); + cur_rule_value(&vars[V_THREAD_DISP_STYLE_RULES], TRUE, TRUE); + cur_rule_value(&vars[V_THREAD_INDEX_STYLE_RULES], TRUE, TRUE); set_current_val(&vars[V_THREAD_MORE_CHAR], TRUE, TRUE); if(VAR_THREAD_MORE_CHAR[0] && VAR_THREAD_MORE_CHAR[1]){ @@ -2424,6 +2591,7 @@ if(VAR_INIT_CMD_LIST && VAR_INIT_CMD_LIST[0] && VAR_INIT_CMD_LIST[0][0]) process_init_cmds(ps, VAR_INIT_CMD_LIST); + create_rule_list(); #ifdef _WINDOWS mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global)); if(ps_global->update_registry != UREG_NEVER_SET){ @@ -2613,6 +2781,8 @@ F_STRIP_WS_BEFORE_SEND, h_config_strip_ws_before_send, PREF_COMP}, /* Reply Prefs */ + {"alternate-reply-menu", + F_ALT_REPLY_MENU, h_config_alt_reply_menu, PREF_RPLY}, {"enable-reply-indent-string-editing", F_ENABLE_EDIT_REPLY_INDENT, h_config_prefix_editing, PREF_RPLY}, {"include-attachments-in-reply", @@ -2647,6 +2817,8 @@ F_AUTO_FCC_ONLY, h_config_auto_fcc_only, PREF_SEND}, {"fcc-without-attachments", F_NO_FCC_ATTACH, h_config_no_fcc_attach, PREF_SEND}, + {"return-path-uses-domain-name", + F_USE_DOMAIN_NAME, h_config_use_domain, PREF_SEND}, {"mark-fcc-seen", F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND}, {"send-without-confirm", @@ -2667,6 +2839,10 @@ F_ENABLE_DOT_FOLDERS, h_config_enable_dot_folders, PREF_FLDR}, {"enable-incoming-folders", F_ENABLE_INCOMING, h_config_enable_incoming, PREF_FLDR}, + {"enable-check-incoming-folders", + F_ENABLE_INCOMING_CHECK, h_config_enable_check_incoming, PREF_FLDR}, + {"recheck-all-incoming-folders", + F_ENABLE_INCOMING_RECHECK, h_config_enable_recheck_incoming,PREF_FLDR}, {"enable-lame-list-mode", F_FIX_BROKEN_LIST, h_config_lame_list_mode, PREF_FLDR}, {"expanded-view-of-folders", @@ -2683,6 +2859,8 @@ F_SORT_DEFAULT_SAVE_ALPHA, h_config_sort_save_alpha, PREF_FLDR}, {"vertical-folder-list", F_VERTICAL_FOLDER_LIST, h_config_vertical_list, PREF_FLDR}, + {"use-courier-folder-list", + F_COURIER_FOLDER_LIST, h_config_courier_list, PREF_FLDR}, /* Addr book */ {"combined-addrbook-display", @@ -2699,6 +2877,8 @@ /* Index prefs */ {"auto-open-next-unread", F_AUTO_OPEN_NEXT_UNREAD, h_config_auto_open_unread, PREF_INDX}, + {"enable-circular-tab", + F_AUTO_CIRCULAR_TAB, h_config_circular_tab, PREF_INDX}, {"continue-tab-without-confirm", F_TAB_NO_CONFIRM, h_config_tab_no_prompt, PREF_INDX}, {"delete-skips-deleted", @@ -2721,6 +2901,8 @@ F_TAB_TO_NEW, h_config_tab_new_only, PREF_INDX}, {"thread-index-shows-important-color", F_COLOR_LINE_IMPORTANT, h_config_color_thrd_import, PREF_INDX}, + {"enhanced-fancy-thread-support", + F_ENHANCED_THREAD, h_config_enhanced_thread, PREF_INDX}, /* Viewer prefs */ {"enable-msg-view-addresses", @@ -2744,6 +2926,9 @@ #endif {"quell-charset-warning", F_QUELL_CHARSET_WARNING, h_config_quell_charset_warning, PREF_VIEW}, + {"quell-displaying-flowed-text", + F_QUELL_DISPLAYING_FLOWED_TEXT, h_config_quell_displaying_flowed_text, + PREF_VIEW}, /* News */ {"compose-sets-newsgroup-without-confirm", @@ -2818,14 +3003,12 @@ F_QUELL_FULL_HDR_RESET, h_config_quell_full_hdr_reset, PREF_ACMD}, /* Adv user prefs */ -#if !defined(DOS) && !defined(OS2) - {"allow-talk", - F_ALLOW_TALK, h_config_allow_talk, PREF_MISC}, -#endif {"assume-slow-link", F_FORCE_LOW_SPEED, h_config_force_low_speed, PREF_OS_LWSD}, {"auto-move-read-msgs", F_AUTO_READ_MSGS, h_config_auto_read_msgs, PREF_MISC}, + {"auto-move-read-msgs-using-rules", + F_AUTO_READ_MSGS_RULES, h_config_auto_read_msgs_rules, PREF_MISC}, {"auto-unzoom-after-apply", F_AUTO_UNZOOM, h_config_auto_unzoom, PREF_MISC}, {"auto-zoom-after-select", @@ -3097,6 +3280,9 @@ F_SET(feat->id, ps, 0); + /* flowed text has some problems, disable it by default (bug #45364) */ + F_TURN_ON(F_QUELL_FLOWED_TEXT, ps_global); + /* backwards compatibility */ if(hir) F_TURN_ON(F_INCLUDE_HEADER, ps); @@ -3109,6 +3295,10 @@ if(old_growth) set_old_growth_bits(ps, 0); + if(ps_global->VAR_CHAR_SET + && !strucmp(ps_global->VAR_CHAR_SET, "UTF-8")) + F_TURN_ON(F_QUELL_CHARSET_WARNING, ps_global); /* if not user-off */ + /* now run through the list (global, user, and cmd_line lists are here) */ if(list){ for(p = list; (q = *p) != NULL; p++){ @@ -3160,6 +3350,11 @@ #ifdef _WINDOWS ps->pass_ctrl_chars = 1; #else + if(ps_global->VAR_CHAR_SET + && !strucmp(ps_global->VAR_CHAR_SET, "UTF-8")) { + F_TURN_ON(F_PASS_C1_CONTROL_CHARS, ps_global); /* global see below */ + F_TURN_ON(F_ENABLE_SETLOCALE_CTYPE, ps); /* for setting gmode */ + } ps->pass_ctrl_chars = F_ON(F_PASS_CONTROL_CHARS,ps_global) ? 1 : 0; ps->pass_c1_ctrl_chars = F_ON(F_PASS_C1_CONTROL_CHARS,ps_global) ? 1 : 0; @@ -3773,6 +3968,23 @@ ? &is_rules[index] : NULL); } +/* + * Standard way to get incoming check rules... + */ +NAMEVAL_S * +incoming_check_rules(index) + int index; +{ + static NAMEVAL_S is_rules[] = { + {"automatic", NULL, IC_AUTO}, + {"automatic-after-first-manual-check", NULL, IC_MAN_AUTO}, + {"manual-only", NULL, IC_MAN} + }; + + return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0]))) + ? &is_rules[index] : NULL); +} + NAMEVAL_S * startup_rules(index) @@ -4207,10 +4419,15 @@ if(i > 0){ ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int)); ps->free_initial_cmds = ps->initial_cmds; + ps->initial_cmds_backup = (int *)fs_get((i+1) * sizeof(int)); + ps->free_initial_cmds_backup = ps->initial_cmds_backup; for(j = 0; j < i; j++) - ps->initial_cmds[j] = i_cmds[j]; - - ps->initial_cmds[i] = 0; + ps->initial_cmds[j] = ps->initial_cmds_backup[j] = i_cmds[j]; +#define ctrl_x 24 + if (i > 1) + ps->send_immediately = i_cmds[i - 2] == ctrl_x + && ((i_cmds[i - 1] == 'y') || (i_cmds[i-1] == 'Y')); + ps->initial_cmds[i] = ps->initial_cmds_backup[i] = 0; ps->in_init_seq = ps->save_in_init_seq = 1; } } @@ -6448,23 +6665,24 @@ * argument also means arrival/reverse. */ int -decode_sort(sort_spec, def_sort, def_sort_rev) +decode_sort(sort_spec, def_sort, def_sort_rev, thread) char *sort_spec; SortOrder *def_sort; int *def_sort_rev; + int thread; { char *sep; char *fix_this = NULL; - int x, reverse; + int x = 0, reverse; if(!sort_spec || !*sort_spec){ - *def_sort = SortArrival; + *def_sort = thread ? SortThread : SortArrival; *def_sort_rev = 0; return(0); } if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){ - *def_sort = SortArrival; + *def_sort = thread ? SortThread : SortArrival; *def_sort_rev = 1; return(0); } @@ -6487,13 +6705,19 @@ sort_spec, strlen(sort_spec)) == 0) break; + if (thread && ps_global->sort_types[x] != SortArrival + && ps_global->sort_types[x] != SortDate + && ps_global->sort_types[x] != SortThread) + for(x = 0; ps_global->sort_types[x] != EndofList; x++); + if(fix_this) *fix_this = '/'; if(ps_global->sort_types[x] == EndofList) return(-1); - *def_sort = ps_global->sort_types[x]; + *def_sort = (thread && ps_global->sort_types[x] == SortDate) + ? SortThread : ps_global->sort_types[x]; *def_sort_rev = reverse; return(0); } @@ -9062,7 +9286,7 @@ */ if(!err && !add_only_first_msg){ char *tempfile = NULL; - int fd; + int fd = 0; if(rd->flags & NO_FILE){ if(so_truncate(rd->sonofile, 0L) == 0) @@ -11053,6 +11277,14 @@ break; } } + else if(var == &ps_global->vars[V_INCOMING_RULE]){ + if(ps_global->VAR_INCOMING_RULE) + for(i = 0; v = incoming_check_rules(i); i++) + if(!strucmp(ps_global->VAR_INCOMING_RULE, S_OR_L(v))){ + ps_global->inc_check_rule = v->value; + break; + } + } else if(var == &ps_global->vars[V_PRUNING_RULE]){ if(ps_global->VAR_PRUNING_RULE) for(i = 0; v = pruning_rules(i); i++) diff -ru pine4.64/pine/mailcap.c pine4.64.SuSE/pine/mailcap.c --- pine4.64/pine/mailcap.c 2004-11-03 21:11:00.000000000 +0100 +++ pine4.64.SuSE/pine/mailcap.c 2006-02-14 14:45:25.000000000 +0100 @@ -1,3 +1,4 @@ + #if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: mailcap.c 13858 2004-11-03 20:11:00Z hubert $"; #endif @@ -993,14 +994,18 @@ * have to put those outside of the single quotes. * (The parm+1000 nonsense is to protect against * malicious mail trying to overlow our buffer.) + * + * TCH - Change 2/8/1999 + * Also quote the ` slash to prevent execution of arbirtrary code */ for(p = parm; *p && p < parm+1000; p++){ - if(*p == '\''){ + if((*p == '\'')||(*p=='`')){ *to++ = '\''; /* closing quote */ *to++ = '\\'; - *to++ = '\''; /* below will be opening quote */ - } - *to++ = *p; + *to++ = *p; /* quoted character */ + *to++ = '\''; /* opening quote */ + } else + *to++ = *p; } fs_give((void **) &parm); @@ -1042,7 +1047,7 @@ */ if(!used_tmp_file && tmp_file) sprintf(to, MC_ADD_TMP, tmp_file); - + return(cpystr(tmp_20k_buf)); } diff -ru pine4.64/pine/mailcmd.c pine4.64.SuSE/pine/mailcmd.c --- pine4.64/pine/mailcmd.c 2005-09-13 00:04:25.000000000 +0200 +++ pine4.64.SuSE/pine/mailcmd.c 2006-02-14 14:45:25.000000000 +0100 @@ -58,6 +58,7 @@ /* * Internal Prototypes */ +void cmd_quota PROTO((struct pine *)); void cmd_delete PROTO((struct pine *, MSGNO_S *, int, CmdWhere)); void cmd_undelete PROTO((struct pine *, MSGNO_S *, int)); void cmd_reply PROTO((struct pine *, MSGNO_S *, int)); @@ -89,11 +90,14 @@ int save_ex_explain_body PROTO((BODY *, unsigned long *, gf_io_t)); int save_ex_explain_parts PROTO((BODY *, int, unsigned long *, gf_io_t)); int save_ex_output_line PROTO((char *, unsigned long *, gf_io_t)); +void total_messages_status PROTO ((MAILSTREAM *, int, unsigned long *, + unsigned long *)); +char *any_report_message PROTO ((MAILSTREAM *)); int create_for_save PROTO((MAILSTREAM *, CONTEXT_S *, char *)); void set_keywords_in_msgid_msg PROTO((MAILSTREAM *, MESSAGECACHE *, MAILSTREAM *, char *, long)); long get_msgno_by_msg_id PROTO((MAILSTREAM *, char *, MSGNO_S *)); -int select_sort PROTO((struct pine *, int, SortOrder *, int *)); +int select_sort PROTO((struct pine *, int, SortOrder *, int *, int)); void aggregate_select PROTO((struct pine *, MSGNO_S *, int, CmdWhere,int)); int select_number PROTO((MAILSTREAM *, MSGNO_S *, SEARCHSET **)); int select_thrd_number PROTO((MAILSTREAM *, MSGNO_S *, SEARCHSET **)); @@ -109,6 +113,27 @@ char *currentf_sequence PROTO((MAILSTREAM *, MSGNO_S *, long, long *, int, char **, char **)); char *invalid_elt_sequence PROTO((MAILSTREAM *, MSGNO_S *)); +long top_thread PROTO((MAILSTREAM *, long)); +void move_top_thread PROTO((MAILSTREAM *, MSGNO_S *, long)); +long top_this_thread PROTO((MAILSTREAM *, long)); +void move_top_this_thread PROTO((MAILSTREAM *, MSGNO_S *, long)); +void cmd_delete_this_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *)); +void cmd_delete_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *)); +void cmd_undelete_this_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *)); +void cmd_undelete_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *)); +void cmd_select_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *)); +void kolapse_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, char, int)); +int count_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, long)); +int count_this_thread PROTO((MAILSTREAM *, unsigned long)); +int this_thread_is_kolapsed PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, long)); +int thread_is_kolapsed PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, long)); +int collapse_this_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int, int)); +void collapse_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int)); +int expand_this_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int, int)); +void expand_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int)); +int move_next_this_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int)); +int move_next_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int)); +int move_prev_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int)); char *selected_sequence PROTO((MAILSTREAM *, MSGNO_S *, long *, int)); int any_messages PROTO((MSGNO_S *, char *, char *)); int can_set_flag PROTO((struct pine *, char *, int)); @@ -119,6 +144,12 @@ int read_msg_prompt PROTO((long, char *)); char *move_read_incoming PROTO((MAILSTREAM *, CONTEXT_S *, char *, char **, char *)); +char *move_read_msgs_using_rules PROTO((MAILSTREAM *, char *, char *)); +unsigned get_perfolder_startup_rule PROTO((MAILSTREAM *, int, char *)); +void setup_threading_index_style PROTO(()); +int find_startup_position PROTO((int, MAILSTREAM *, long)); +char *get_rule_result PROTO((int, char *, int)); +char *get_folder_to_save PROTO((MAILSTREAM *, long, char *)); void cross_delete_crossposts PROTO((MAILSTREAM *)); void menu_clear_cmd_binding PROTO((struct key_menu *, int)); int update_folder_spec PROTO((char *, char *)); @@ -141,6 +172,9 @@ #define SV_FOR_FILT 0x2 #define SV_FIX_DELS 0x4 +static MAILSTREAM *saved_stream; +static unsigned long rule_curpos = 0L; + typedef struct append_package { MAILSTREAM *stream; char *flags; @@ -254,9 +288,9 @@ static char *sel_text = - "Select based on To, From, Cc, Recip, Partic, Subject fields or All msg text ? "; + "Select based on To, From, Cc, Recip, Partic, Subject, Body or All msg text ? "; static char *sel_not_text = - "Select based on NOT To, From, Cc, Recip, Partic, Subject or All msg text ? "; + "Select based on NOT To, From, Cc, Recip, Partic, Subject, Body or All msg text ? "; static ESCKEY_S sel_text_opt[] = { {'f', 'f', "F", "From"}, {'s', 's', "S", "Subject"}, @@ -267,6 +301,7 @@ {'r', 'r', "R", "Recipient"}, {'p', 'p', "P", "Participant"}, {'b', 'b', "B", "Body"}, + {'h', 'h', "H", "Header"}, {-1, 0, NULL, NULL} }; @@ -936,7 +971,7 @@ state->context_current, &recent_cnt, F_ON(F_TAB_NO_CONFIRM,state) ? NULL : &did_cancel))){ - if(!in_inbox){ + if(!in_inbox && F_OFF(F_AUTO_CIRCULAR_TAB,state)){ static ESCKEY_S inbox_opt[] = { {'y', 'y', "Y", "Yes"}, {'n', 'n', "N", "No"}, @@ -1186,6 +1221,10 @@ if(SORT_IS_THREADED(msgmap)) refresh_sort(stream, msgmap, SRT_NON); + if (msgmap->nmsgs + && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS()) + kolapse_thread(state, stream, msgmap, '[', 0); + state->mangled_body = 1; state->mangled_header = 1; q_status_message2(SM_ORDER, 0, 4, @@ -1256,6 +1295,7 @@ ps_global->expunge_in_progress = 0; if(we_cancel) cancel_busy_alarm((sp_expunge_count(stream) > 0) ? 0 : -1); + update_incoming_folder_data(stream, state->context_current); dprint(2,(debugfile,"expunge complete cur:%ld max:%ld\n", mn_get_cur(msgmap), mn_get_total(msgmap))); @@ -1298,6 +1338,9 @@ */ if(SORT_IS_THREADED(msgmap)) refresh_sort(stream, msgmap, SRT_NON); + if (msgmap->nmsgs + && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS()) + kolapse_thread(state, stream, msgmap, '[', 0); } else{ if(del_count) @@ -1434,7 +1477,7 @@ if(any_messages(msgmap, NULL, NULL)){ if(any_lflagged(msgmap, MN_SLCT) > 0L){ if(apply_command(state, stream, msgmap, 0, - AC_NONE, question_line) + AC_NONE, question_line, 1) && F_ON(F_AUTO_UNZOOM, state)) unzoom_index(state, stream, msgmap); } @@ -1447,18 +1490,25 @@ /*-------- Sort command -------*/ case MC_SORT : + case MC_SORTHREAD : { int were_threading = THREADING(); SortOrder sort = mn_get_sort(msgmap); int rev = mn_get_revsort(msgmap); + int thread = (command == MC_SORT) ? 0 : 1; + if (sort == SortThread) + sort = ps_global->thread_cur_sort; dprint(1, (debugfile,"MAIL_CMD: sort\n")); - if(select_sort(state, question_line, &sort, &rev)){ + if(select_sort(state, question_line, &sort, &rev, thread)){ /* $ command reinitializes threading collapsed/expanded info */ if(SORT_IS_THREADED(msgmap) && !SEP_THRDINDX()) erase_threading_info(stream, msgmap); - sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN); + if (command == MC_SORTHREAD) + ps_global->thread_cur_sort = sort; + sort = (command == MC_SORT) ? sort : SortThread; + sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN, 1); } state->mangled_footer = 1; @@ -1533,6 +1583,12 @@ break; + /*--------Incoming Folders Auto Check --------*/ + case MC_FORCECHECK: + state->force_check_now = 1; + new_mail_incfolder(state,command); + break; + /*--------- Default, unknown command ----------*/ default: panic("Unexpected command case"); @@ -1940,6 +1996,168 @@ } +static struct key quota_keys[] = + {HELP_MENU, + NULL_MENU, + {"E","Exit",{MC_EXIT,3,{'e','i',ctrl('C')}},KS_EXITMODE}, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU, + NULL_MENU}; +INST_KEY_MENU(pine_quota_keymenu, quota_keys); + +int process_quota_cmd(cmd, msgmap, sparms) + int cmd; + MSGNO_S *msgmap; + SCROLL_S *sparms; +{ +return cmd; +} + + +void cmd_quota (state) + struct pine *state; +{ + QUOTALIST *imapquota; + NETMBX mb; + unsigned long len, storageuse, storagelim, messageuse, messagelim; + STORE_S *store; + SCROLL_S sargs; + char *linedata; + char *storageq = NULL, *messageq = NULL; + int storage=0, message=0, other=0, storagelen = 0, messagelen = 0; + + if(!state->mail_stream || !is_imap_stream(state->mail_stream)){ + q_status_message(SM_ORDER, 1, 5, "Quota only available for IMAP folders"); + return; + } + + if (state->mail_stream + && !sp_dead_stream(state->mail_stream) + && state->mail_stream->mailbox + && *state->mail_stream->mailbox + && mail_valid_net_parse(state->mail_stream->mailbox, &mb)) + imap_getquotaroot(state->mail_stream, mb.mailbox); + + if(!state->quota) /* failed ? */ + return; /* go back... */ + + if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){ + q_status_message(SM_ORDER | SM_DING, 3, 3, "Error allocating space."); + return; + } + + if (state->mail_stream && state->mail_stream->original_mailbox){ + len = strlen(state->mail_stream->original_mailbox) + 19; + linedata = (char *) fs_get(len*sizeof(char)); + sprintf(linedata,"Quota Report for %s\n", + state->mail_stream->original_mailbox); + so_puts(store,linedata); + if (linedata) + fs_give((void **)&linedata); + } + else + so_puts(store,"Quota Report:\n"); + so_puts(store,"\n"); + + for (imapquota = state->quota; imapquota; imapquota = imapquota->next){ + if(!strucmp(imapquota->name,"STORAGE")){ + storage++; /* 1 = '\0', 3 = " KB" */ + storagelen = strlen(long2string(imapquota->limit)) + 1 + 3; + storageuse = imapquota->usage; + storagelim = imapquota->limit; + } + if(!strucmp(imapquota->name,"MESSAGE")){ + message++; /* " messages" = 9 */ + messagelen = strlen(long2string(imapquota->limit)) + 1 + 9; + messageuse = imapquota->usage; + messagelim = imapquota->limit; + } + other += strucmp(imapquota->name,"STORAGE") && + strucmp(imapquota->name,"MESSAGE") ? 0 : 1; + } + + storageq = (char *) fs_get(storagelen*sizeof(char)); + if (storage) + sprintf(storageq, "%lu KB", storageuse); + messageq = (char *) fs_get(messagelen*sizeof(char)); + if (message) + sprintf(messageq, "%lu message%s", messageuse, plural(messageuse)); + len = strlen("Usage: ") + storagelen + messagelen + 8; + linedata = (char *) fs_get(len*sizeof(char)); + sprintf(linedata,"Usage: %s%s%s%s\n", (storage ? storageq : ""), + (message ? (storage ? " (" : "") : ""), + (message ? messageq : ""), + (message ? (storage ? ")" : "") : "")); + so_puts(store, linedata); + if (storageq) + fs_give((void **)&storageq); + if (messageq) + fs_give((void **)&messageq); + if (linedata) + fs_give((void **)&linedata); + + storageq = (char *) fs_get(storagelen*sizeof(char)); + if (storage) + sprintf(storageq, "%lu KB", storagelim); + messageq = (char *) fs_get(messagelen*sizeof(char)); + if (message) + sprintf(messageq, "%lu message%s", messagelim, plural(messagelim)); + len = strlen("Limit: ") + storagelen + messagelen + 9; + linedata = (char *) fs_get(len*sizeof(char)); + sprintf(linedata,"Limit: %s%s%s%s", (storage ? storageq : ""), + (message ? (storage ? " (" : "") : ""), + (message ? messageq : ""), + (message ? (storage ? ")" : "") : "")); + so_puts(store, linedata); + if (storageq) + fs_give((void **)&storageq); + if (messageq) + fs_give((void **)&messageq); + if (linedata) + fs_give((void **)&linedata); + + for (imapquota = state->quota; other && imapquota; + imapquota = imapquota->next){ + if (strucmp(imapquota->name,"STORAGE") && + strucmp(imapquota->name,"MESSAGE")){ + len = (imapquota->name ? strlen(imapquota->name) : strlen("No Name")) + + 2*strlen(long2string(imapquota->limit)) + 46; + linedata = (char *) fs_get(len*sizeof(char)); + sprintf(linedata, + "Resource : %s\nUsage : %lu (%lu%%)\nLimit : %lu\n\n", + (imapquota->name ? imapquota->name : "No Name"), + imapquota->usage, (100*imapquota->usage/imapquota->limit), + imapquota->limit); + so_puts(store,linedata); + if (linedata) + fs_give((void **)&linedata); + } + } + + memset(&sargs, 0, sizeof(SCROLL_S)); + sargs.text.text = so_text(store); + sargs.text.src = CharStar; + sargs.text.desc = "Quota Resources Summary"; + sargs.bar.title = cpystr("QUOTA SUMMARY"); + sargs.proc.tool = process_quota_cmd; + sargs.help.text = NO_HELP; + sargs.help.title = NULL; + sargs.keys.menu = &pine_quota_keymenu; + setbitmap(sargs.keys.bitmap); + + scrolltool(&sargs); + so_give(&store); + + if (state->quota) + mail_free_quotalist(&(state->quota)); +} + /*---------------------------------------------------------------------- Execute DELETE message command @@ -2931,6 +3149,7 @@ if(agg && !pseudo_selected(msgmap)) return; + saved_stream = stream; /* ugly hack! */ state->ugly_consider_advancing_bit = 0; if(F_OFF(F_SAVE_PARTIAL_WO_CONFIRM, state) && msgno_any_deletedparts(stream, msgmap) @@ -3178,7 +3397,7 @@ { static char folder[MAILTMPLEN+1] = {'\0'}; static CONTEXT_S *last_context = NULL; - int rc, n, flags, last_rc = 0, saveable_count = 0, done = 0; + int rc, n=0, flags, last_rc = 0, saveable_count = 0, done = 0; int context_was_set, delindex; char prompt[MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN]; char *buf = tmp_20k_buf; @@ -3187,6 +3406,9 @@ char *deltext = NULL; CONTEXT_S *tc; ESCKEY_S ekey[9]; + RULE_RESULT *rule; + + saved_stream = state->mail_stream; if(!cntxt) panic("no context ptr in save_prompt"); @@ -3217,6 +3439,13 @@ return(0); } + if (rule = get_result_rule(V_SAVE_RULES, FOR_RULE | FOR_SAVE, env)){ + strncpy(folder,rule->result,sizeof(folder)-1); + folder[sizeof(folder)-1] = '\0'; + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } /* how many context's can be saved to... */ for(tc = state->context_list; tc; tc = tc->next) @@ -4137,7 +4366,7 @@ *date = '\0'; rv = save_fetch_append(stream, mn_m2raw(msgmap, i), - NULL, save_stream, save_folder, context, + NULL, save_stream, folder, context, mc ? mc->rfc822_size : 0L, flags, date, so); if(sp_expunge_count(stream)) @@ -7268,6 +7497,65 @@ return(newfolder); } +char * +any_report_message (stream) + MAILSTREAM *stream; +{ + char message[40] = {'\0'}, *report; + unsigned long new, unseen; + int i, imapstatus = 0; + + for (i = 0; ps_global->index_disp_format[i].ctype != iNothing + && ps_global->index_disp_format[i].ctype != iIStatus; i++); + imapstatus = ps_global->index_disp_format[i].ctype == iIStatus; + + report = (char *) fs_get(41*sizeof(char)); + total_messages_status(stream, imapstatus, &new, &unseen); + + if (new > 0L || (unseen > 0L && imapstatus)){ + sprintf(message," - %s%s%s%s%s", new > 0L ? comatose(new) : "", + new > 0L ? " new" : "", + new > 0L && unseen > 0L && imapstatus ? ", " : "", + unseen > 0L && imapstatus ? comatose(unseen) : "", + unseen > 0L && imapstatus ? " unseen" : ""); + } + report = *message ? cpystr(message) : cpystr(""); + + return report; +} + +void +total_messages_status (stream, imapstatus, new, unseen) + MAILSTREAM *stream; + int imapstatus; + unsigned long *new, *unseen; +{ + MESSAGECACHE *mc; + unsigned long i; + int *searched; + + if (new) *new = imapstatus ? count_flagged(stream, F_RECENT | F_UNDEL) + : count_flagged(stream, F_UNSEEN | F_UNDEL); + if (unseen) *unseen = 0L; + + if (!imapstatus || !unseen) + return; + + searched = (int *) fs_get(stream->nmsgs*sizeof(int)); + memset(searched, 0, stream->nmsgs*sizeof(searched)); + for (i = 1L; stream && i <= stream->nmsgs && (mc = mail_elt(stream, i)); i++) + if (mc->searched + || (mc->valid && !get_lflag(stream, NULL, i, MN_EXLD) + && mc->recent && !mc->deleted)) + searched[i-1] = 1; + count_flagged(stream, F_UNSEEN | F_UNDEL); + for (i = 1L; stream && i <= stream->nmsgs && (mc = mail_elt(stream, i)); i++) + if (!searched[i-1] && (mc->searched + || (mc->valid && !get_lflag(stream, NULL, i, MN_EXLD) + && !mc->seen && !mc->recent && !mc->deleted))) + (*unseen)++; + fs_give((void **)&searched); +} /*---------------------------------------------------------------------- Check to see if user input is in form of old c-client mailbox speck @@ -7325,6 +7613,212 @@ return(FALSE); } +void +setup_threading_index_style() +{ + RULE_RESULT *rule; + NAMEVAL_S *v; + int i; + + rule = get_result_rule(V_THREAD_INDEX_STYLE_RULES, + FOR_RULE | FOR_THREAD, (ENVELOPE *) NULL); + if (rule || ps_global->VAR_THREAD_INDEX_STYLE){ + for(i = 0; v = thread_index_styles(i); i++) + if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_INDEX_STYLE, + rule ? (v ? v->name : "" ) : S_OR_L(v))){ + ps_global->thread_index_style = v->value; + break; + } + if (rule){ + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } +} + + +char *get_rule_result(rule_context, newfolder, code) +int rule_context; +char *newfolder; +int code; +{ char *rule_result = NULL; + ENVELOPE *news_envelope; + RULE_RESULT *rule; + + if (IS_NEWS(ps_global->mail_stream)){ + news_envelope = mail_newenvelope(); + news_envelope->newsgroups = cpystr(newfolder); + } + else + news_envelope = (ENVELOPE *) NULL; + + rule = get_result_rule(code, rule_context, news_envelope); + + if (news_envelope) + mail_free_envelope (&news_envelope); + + if (rule){ + rule_result = cpystr(rule->result); + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + + return rule_result; +} + +find_startup_position(rule, m, pc) +int rule; +MAILSTREAM *m; +long pc; +{ + long n; + switch(rule){ + /* + * For news in incoming collection we're doing the same thing + * for first-unseen and first-recent. In both those cases you + * get first-unseen if FAKE_NEW is off and first-recent if + * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the + * same as first recent because all recent msgs are unseen + * and all unrecent msgs are seen (see pine_mail_open). + */ + case IS_FIRST_UNSEEN: +first_unseen: + mn_set_cur(ps_global->msgmap, + (sp_first_unseen(m) + && mn_get_sort(ps_global->msgmap) == SortArrival + && !mn_get_revsort(ps_global->msgmap) + && !get_lflag(ps_global->mail_stream, NULL, + sp_first_unseen(m), MN_EXLD) + && (n = mn_raw2m(ps_global->msgmap, + sp_first_unseen(m)))) + ? n + : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_FIRST_RECENT: +first_recent: + /* + * We could really use recent for news but this is the way + * it has always worked, so we'll leave it. That is, if + * the FAKE_NEW feature is on, recent and unseen are + * equivalent, so it doesn't matter. If the feature isn't + * on, all the undeleted messages are unseen and we start + * at the first one. User controls with the FAKE_NEW feature. + */ + if(IS_NEWS(ps_global->mail_stream)){ + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + } + else{ + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_RECENT | F_UNSEEN + | F_UNDEL, + m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + } + break; + + case IS_FIRST_IMPORTANT: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_FIRST_IMPORTANT_OR_UNSEEN: + + if(IS_NEWS(ps_global->mail_stream)) + goto first_unseen; + + { + MsgNo flagged, first_unseen; + + flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + first_unseen = (sp_first_unseen(m) + && mn_get_sort(ps_global->msgmap) == SortArrival + && !mn_get_revsort(ps_global->msgmap) + && !get_lflag(ps_global->mail_stream, NULL, + sp_first_unseen(m), MN_EXLD) + && (n = mn_raw2m(ps_global->msgmap, + sp_first_unseen(m)))) + ? n + : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + mn_set_cur(ps_global->msgmap, + (MsgNo) min((int) flagged, (int) first_unseen)); + + } + + break; + + case IS_FIRST_IMPORTANT_OR_RECENT: + + if(IS_NEWS(ps_global->mail_stream)) + goto first_recent; + + { + MsgNo flagged, first_recent; + + flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN + | F_UNDEL, + m, pc, + THREADING() ? 0 : FSF_SKIP_CHID); + mn_set_cur(ps_global->msgmap, + (MsgNo) min((int) flagged, (int) first_recent)); + } + + break; + + case IS_FIRST: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNDEL, m, pc, + THREADING() ? 0 : FSF_SKIP_CHID)); + break; + + case IS_LAST: + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNDEL, m, pc, + FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID))); + break; + + default: + panic("Unexpected incoming startup case"); + break; + + } +} + +unsigned +get_perfolder_startup_rule(stream, rule_type, folder) + MAILSTREAM *stream; + int rule_type; + char *folder; +{ + unsigned startup_rule; + char *rule_result; + + startup_rule = reset_startup_rule(stream); + rule_result = get_rule_result(FOR_RULE | FOR_STARTUP, folder, rule_type); + if (rule_result && *rule_result){ + int i; + NAMEVAL_S *v; + + for(i = 0; v = incoming_startup_rules(i); i++) + if(!strucmp(rule_result, v->name)){ + startup_rule = v->value; + break; + } + fs_give((void **)&rule_result); + } + return startup_rule; +} + /*---------------------------------------------------------------------- Actually attempt to open given folder @@ -7359,7 +7853,7 @@ int open_inbox, rv, old_tros, we_cancel = 0, do_reopen = 0, n, was_dead = 0, cur_already_set = 0; char expanded_file[max(MAXPATH,MAILTMPLEN)+1], - *old_folder, *old_path, *p; + *old_folder, *old_path, *p, *report; long openmode, rflags = 0L, pc = 0L, cur, raw; ENVELOPE *env = NULL; char status_msg[81]; @@ -7693,15 +8187,20 @@ sizeof(ps_global->cur_folder)-1); ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0'; ps_global->context_current = ps_global->context_list; + setup_threading_index_style(); reset_index_format(); clear_index_cache(); /* MUST sort before restoring msgno! */ refresh_sort(ps_global->mail_stream, ps_global->msgmap, SRT_NON); - q_status_message3(SM_ORDER, 0, 3, - "Opened folder \"%.200s\" with %.200s message%.200s", - ps_global->inbox_name, - long2string(mn_get_total(ps_global->msgmap)), - plural(mn_get_total(ps_global->msgmap))); + report = any_report_message(ps_global->mail_stream); + q_status_message4(SM_ORDER, 0, 3, + "Opened folder \"%.200s\" with %.200s message%.200s%.200s", + ps_global->inbox_name, + long2string(mn_get_total(ps_global->msgmap)), + plural(mn_get_total(ps_global->msgmap)), + report); + if (report) + fs_give((void **)&report); #ifdef _WINDOWS mswin_settitle(ps_global->inbox_name); #endif @@ -8002,6 +8501,7 @@ clear_index_cache(); reset_index_format(); + setup_threading_index_style(); /* * Start news reading with messages the user's marked deleted @@ -8021,8 +8521,16 @@ if(!sp_flagged(ps_global->mail_stream, SP_FILTERED)) process_filter_patterns(ps_global->mail_stream, ps_global->msgmap, 0L); - q_status_message6(SM_ORDER, 0, 4, - "%.20s \"%.200s\" opened with %.20s message%.20s%.20s%.20s", + if(!(rflags & SP_MATCH) || !(rflags & SP_LOCKED)) + reset_sort_order(SRT_VRB); + else if(sp_new_mail_count(ps_global->mail_stream) > 0L + || sp_unsorted_newmail(ps_global->mail_stream) + || sp_need_to_rethread(ps_global->mail_stream)) + refresh_sort(ps_global->mail_stream, ps_global->msgmap, SRT_NON); + + report = any_report_message(ps_global->mail_stream); + q_status_message7(SM_ORDER, 0, 4, + "%.20s \"%.200s\" opened with %.20s message%.20s%.20s%.20s%.20s", IS_NEWS(ps_global->mail_stream) ? "News group" : "Folder", pretty_fn(newfolder), @@ -8032,20 +8540,15 @@ && sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)) ? " (StayOpen)" : "", READONLY_FOLDER(ps_global->mail_stream) - ? " READONLY" : ""); + ? " READONLY" : "", + report); + if (report) + fs_give((void **)&report); #ifdef _WINDOWS mswin_settitle(pretty_fn(newfolder)); #endif - if(!(rflags & SP_MATCH) || !(rflags & SP_LOCKED)) - reset_sort_order(SRT_VRB); - else if(sp_new_mail_count(ps_global->mail_stream) > 0L - || sp_unsorted_newmail(ps_global->mail_stream) - || sp_need_to_rethread(ps_global->mail_stream)) - refresh_sort(ps_global->mail_stream, ps_global->msgmap, SRT_NON); - - /* * Set current message number when re-opening Stay-Open or * cached folders. @@ -8109,7 +8612,10 @@ if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){ - perfolder_startup_rule = reset_startup_rule(ps_global->mail_stream); + perfolder_startup_rule = get_perfolder_startup_rule(ps_global->mail_stream, + V_STARTUP_RULES, newfolder); + + reset_startup_rule(ps_global->mail_stream); if(ps_global->start_entry > 0){ mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap) @@ -8131,140 +8637,24 @@ else use_this_startup_rule = ps_global->inc_startup_rule; - switch(use_this_startup_rule){ - /* - * For news in incoming collection we're doing the same thing - * for first-unseen and first-recent. In both those cases you - * get first-unseen if FAKE_NEW is off and first-recent if - * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the - * same as first recent because all recent msgs are unseen - * and all unrecent msgs are seen (see pine_mail_open). - */ - case IS_FIRST_UNSEEN: -first_unseen: - mn_set_cur(ps_global->msgmap, - (sp_first_unseen(m) - && mn_get_sort(ps_global->msgmap) == SortArrival - && !mn_get_revsort(ps_global->msgmap) - && !get_lflag(ps_global->mail_stream, NULL, - sp_first_unseen(m), MN_EXLD) - && (n = mn_raw2m(ps_global->msgmap, - sp_first_unseen(m)))) - ? n - : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc, - THREADING() ? 0 : FSF_SKIP_CHID)); - break; + find_startup_position(use_this_startup_rule, m, pc); - case IS_FIRST_RECENT: -first_recent: - /* - * We could really use recent for news but this is the way - * it has always worked, so we'll leave it. That is, if - * the FAKE_NEW feature is on, recent and unseen are - * equivalent, so it doesn't matter. If the feature isn't - * on, all the undeleted messages are unseen and we start - * at the first one. User controls with the FAKE_NEW feature. - */ - if(IS_NEWS(ps_global->mail_stream)){ - mn_set_cur(ps_global->msgmap, - first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, - THREADING() ? 0 : FSF_SKIP_CHID)); - } - else{ - mn_set_cur(ps_global->msgmap, - first_sorted_flagged(F_RECENT | F_UNSEEN - | F_UNDEL, - m, pc, + } + else if(IS_NEWS(ps_global->mail_stream)){ + /* + * This will go to two different places depending on the FAKE_NEW + * feature (see pine_mail_open). + */ + mn_set_cur(ps_global->msgmap, + first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, THREADING() ? 0 : FSF_SKIP_CHID)); - } - break; - - case IS_FIRST_IMPORTANT: - mn_set_cur(ps_global->msgmap, - first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, - THREADING() ? 0 : FSF_SKIP_CHID)); - break; - - case IS_FIRST_IMPORTANT_OR_UNSEEN: - - if(IS_NEWS(ps_global->mail_stream)) - goto first_unseen; - - { - MsgNo flagged, first_unseen; - - flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, - THREADING() ? 0 : FSF_SKIP_CHID); - first_unseen = (sp_first_unseen(m) - && mn_get_sort(ps_global->msgmap) == SortArrival - && !mn_get_revsort(ps_global->msgmap) - && !get_lflag(ps_global->mail_stream, NULL, - sp_first_unseen(m), MN_EXLD) - && (n = mn_raw2m(ps_global->msgmap, - sp_first_unseen(m)))) - ? n - : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, - THREADING() ? 0 : FSF_SKIP_CHID); - mn_set_cur(ps_global->msgmap, - (MsgNo) min((int) flagged, (int) first_unseen)); - - } - - break; - - case IS_FIRST_IMPORTANT_OR_RECENT: - - if(IS_NEWS(ps_global->mail_stream)) - goto first_recent; - - { - MsgNo flagged, first_recent; - - flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc, - THREADING() ? 0 : FSF_SKIP_CHID); - first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN - | F_UNDEL, - m, pc, - THREADING() ? 0 : FSF_SKIP_CHID); - mn_set_cur(ps_global->msgmap, - (MsgNo) min((int) flagged, (int) first_recent)); - } - - break; - - case IS_FIRST: - mn_set_cur(ps_global->msgmap, - first_sorted_flagged(F_UNDEL, m, pc, - THREADING() ? 0 : FSF_SKIP_CHID)); - break; - - case IS_LAST: - mn_set_cur(ps_global->msgmap, - first_sorted_flagged(F_UNDEL, m, pc, - FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID))); - break; - - default: - panic("Unexpected incoming startup case"); - break; - - } - } - else if(IS_NEWS(ps_global->mail_stream)){ - /* - * This will go to two different places depending on the FAKE_NEW - * feature (see pine_mail_open). - */ - mn_set_cur(ps_global->msgmap, - first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc, - THREADING() ? 0 : FSF_SKIP_CHID)); - } - else{ - mn_set_cur(ps_global->msgmap, - mn_get_revsort(ps_global->msgmap) - ? 1L - : mn_get_total(ps_global->msgmap)); - } + } + else{ + mn_set_cur(ps_global->msgmap, + mn_get_revsort(ps_global->msgmap) + ? 1L + : mn_get_total(ps_global->msgmap)); + } adjust_cur_to_visible(ps_global->mail_stream, ps_global->msgmap); } @@ -8287,7 +8677,10 @@ PAT_S *pat; int we_set_it = 0; - if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ + if(find_index_rule()) + return; + + if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ if(match_pattern(pat->patgrp, ps_global->mail_stream, NULL, NULL, NULL, SO_NOSERVER|SE_NOPREFETCH)) @@ -8317,10 +8710,27 @@ PAT_S *pat; SortOrder the_sort_order; int sort_is_rev; - + char *rule_result; + SortOrder new_sort = EndofList; + int is_rev; + + rule_result = get_rule_result(FOR_RULE | FOR_SORT, ps_global->cur_folder, + V_SORT_RULES); + if (rule_result && *rule_result){ + new_sort = (SortOrder) translate(rule_result, 1); + is_rev = (SortOrder) translate(rule_result, 0) == EndofList ? 0 : 1; + fs_give((void **)&rule_result); + } + if (new_sort != EndofList){ + the_sort_order = new_sort; + sort_is_rev = is_rev; + } + else{ /* set default order */ the_sort_order = ps_global->def_sort; - sort_is_rev = ps_global->def_sort_rev; + sort_is_rev = the_sort_order == SortThread + ? (ps_global->thread_def_sort_rev + ps_global->def_sort_rev) % 2 + : ps_global->def_sort_rev; if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){ @@ -8332,12 +8742,18 @@ if(pat && pat->action && !pat->action->bogus && pat->action->sort_is_set){ the_sort_order = pat->action->sortorder; - sort_is_rev = pat->action->revsort; + sort_is_rev = the_sort_order == SortThread + ? (ps_global->thread_def_sort_rev + pat->action->revsort) % 2 + : pat->action->revsort; } } + } + if (ps_global->thread_cur_sort != SortArrival + && ps_global->thread_cur_sort != SortThread) + ps_global->thread_cur_sort = ps_global->thread_def_sort; sort_folder(ps_global->mail_stream, ps_global->msgmap, - the_sort_order, sort_is_rev, flags); + the_sort_order, sort_is_rev, flags, 1); } @@ -8423,6 +8839,7 @@ temp[MAILTMPLEN+1], buff1[MAX_SCREEN_COLS+1], *moved_msg = NULL, buff2[MAX_SCREEN_COLS+1], *folder; CONTEXT_S *context; + FOLDER_S *f; struct variable *vars = ps_global->vars; int ret, expunge = FALSE, no_close = 0; char ing[4]; @@ -8439,7 +8856,7 @@ } if(stream != NULL){ - context = sp_context(stream); + context = ps_global->context_current; folder = STREAMNAME(stream); dprint(2, (debugfile, "expunge_and_close: \"%s\"%s\n", @@ -8452,6 +8869,14 @@ buff1[0] = '\0'; buff2[0] = '\0'; + if(F_OFF(F_ENABLE_FAST_RECENT,ps_global) && + (f = incoming_folder_data(stream, context))){ + new_mail_in_open_stream(stream, &(f->origrecent), &(f->messages)); + f->notified = 0; + f->countrecent = f->recent = 0L; + f->selected = f->user_selected; + } + if(!stream->rdonly){ if(!no_close){ @@ -8479,9 +8904,11 @@ /* Save read messages? */ if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0] && sp_flagged(stream, SP_INBOX) - && (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL))){ + && (F_ON(F_AUTO_READ_MSGS_RULES, ps_global) || + (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL)))){ if(F_ON(F_AUTO_READ_MSGS,ps_global) + || F_ON(F_AUTO_READ_MSGS_RULES, ps_global) || read_msg_prompt(seen_not_del, VAR_READ_MESSAGE_FOLDER)) /* move inbox's read messages */ moved_msg = move_read_msgs(stream, VAR_READ_MESSAGE_FOLDER, @@ -10136,6 +10563,9 @@ char *bufp = NULL; MESSAGECACHE *mc; + if (F_ON(F_AUTO_READ_MSGS_RULES, ps_global)) + return move_read_msgs_using_rules(stream, dstfldr, buf); + if(!is_absolute_path(dstfldr) && !(save_context = default_save_context(ps_global->context_list))) save_context = ps_global->context_list; @@ -10177,8 +10607,9 @@ sprintf(buf, "Moving %s read message%s to \"%.45s\"", comatose(searched), plural(searched), dstfldr); we_cancel = busy_alarm(1, buf, NULL, 1); - if(save(ps_global, stream, save_context, dstfldr, msgmap, - SV_DELETE | SV_FIX_DELS) == searched) + ps_global->exiting = 1; + if((save(ps_global, stream, save_context, dstfldr, msgmap, + SV_DELETE | SV_FIX_DELS) == searched)) strncpy(bufp = buf + 1, "Moved", 5); /* change Moving to Moved */ mn_give(&msgmap); @@ -10189,6 +10620,143 @@ return(bufp); } +char * +move_read_msgs_using_rules(stream, dstfldr,buf) + MAILSTREAM *stream; + char *dstfldr; + char *buf; +{ + CONTEXT_S *save_context = NULL; + char **folder_to_save = NULL; + int num, we_cancel; + long i, j, success, nmsgs = 0L; + MSGNO_S *msgmap = NULL; + + saved_stream = stream; /* horrible hack! */ + if(!is_absolute_path(dstfldr) + && !(save_context = default_save_context(ps_global->context_list))) + save_context = ps_global->context_list; + + folder_to_save = (char **)fs_get((stream->nmsgs + 1)*sizeof(char *)); + folder_to_save[0] = NULL; + mn_init(&msgmap, stream->nmsgs); + for (i = 1L; i <= stream->nmsgs ; i++){ + set_lflag(stream, msgmap, i, MN_SLCT, 0); + folder_to_save[i] = get_lflag(stream, NULL, i, MN_EXLD) + ? NULL : get_folder_to_save(stream, i, dstfldr); + } + for (i = 1L; i <= stream->nmsgs; i++){ + num = 0; + if (folder_to_save[i]){ + mn_init(&msgmap, stream->nmsgs); + for (j = i; j <= stream->nmsgs ; j++){ + if (folder_to_save[j]){ + if (!strcmp(folder_to_save[i], folder_to_save[j])){ + set_lflag(stream, msgmap, j, MN_SLCT, 1); + num++; + if (j != i) + fs_give((void **)&folder_to_save[j]); + } + } + } + pseudo_selected(msgmap); + sprintf(buf, "Moving %s read message%s to \"%.45s\"", + comatose(num), plural(num), folder_to_save[i]); + we_cancel = busy_alarm(1, buf, NULL, 1); + ps_global->exiting = 1; + if(success = save(ps_global, stream,save_context, folder_to_save[i], + msgmap, SV_DELETE | SV_FIX_DELS)) + nmsgs += success; + if(we_cancel) + cancel_busy_alarm(success ? 0 : -1); + for (j = i; j <= stream->nmsgs ; j++) + set_lflag(stream, msgmap, j, MN_SLCT, 0); + fs_give((void **)&folder_to_save[i]); + mn_give(&msgmap); + } + } + ps_global->exiting = 0; /* useful if we call from aggregate operations */ + sprintf(buf, "Moved automatically %s message%s", + comatose(nmsgs), plural(nmsgs)); + if (folder_to_save) + fs_give((void **)folder_to_save); + rule_curpos = 0L; + return buf; +} + +unsigned long +rules_cursor_pos(stream) + MAILSTREAM *stream; +{ + MSGNO_S *msgmap = sp_msgmap(stream); + return rule_curpos != 0L ? rule_curpos : mn_m2raw(msgmap,mn_get_cur(msgmap)); +} + + +MAILSTREAM * +find_open_stream() +{ + return saved_stream; +} + +char * +get_folder_to_save(stream, i, dstfldr) + MAILSTREAM *stream; + long i; + char *dstfldr; +{ + MESSAGECACHE *mc = NULL; + RULE_RESULT *rule; + MSGNO_S *msgmap = NULL; + char *folder_to_save = NULL, *save_folder = NULL; + int n; + long msgno; + + /* The plan is as follows: Select each message of the folder. We + * need to set the cursor correctly so that iFlag gets the value + * correctly too, otherwise iFlag will get the value of the position + * of the cursor. After that we need to look for a rule that applies + * to the message and get the saving folder. If we get a saving folder, + * and we used the _FLAG_ token, use that folder, if no + * _FLAG_ token was used, move only if seen and not deleted, to the + * folder specified in the saving rule. If we did not get a saving + * folder from the rule, just save in the default folder. + */ + + mn_init(&msgmap, stream->nmsgs); + rule_curpos = i; + msgno = mn_m2raw(msgmap, i); + if (msgno > 0L){ + mc = mail_elt(stream, msgno); + rule = (RULE_RESULT *) + get_result_rule(V_SAVE_RULES, FOR_RULE | FOR_SAVE, + (ENVELOPE *) mc->private.msg.env); + if (rule){ + folder_to_save = cpystr(rule->result); + n = rule->number; + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } + + if (folder_to_save && *folder_to_save){ + RULELIST *list = get_rulelist_from_code(V_SAVE_RULES, + ps_global->rule_list); + RULE_S *prule = get_rule(list, n); + if (condition_contains_token(prule->condition, "_FLAG_") + || (mc->valid && mc->seen && !mc->deleted) + || (!mc->valid && mc->searched)) + save_folder = cpystr(folder_to_save); + else + save_folder = NULL; + } + else + if (!mc || (mc->seen && !mc->deleted)) + save_folder = cpystr(dstfldr); + mn_give(&msgmap); + rule_curpos = 0L; + return save_folder; +} /*---------------------------------------------------------------------- @@ -10235,7 +10803,9 @@ && ((context_isambig(folder) && folder_is_nick(folder, FOLDERS(context), 0)) || folder_index(folder, context, FI_FOLDER) > 0) - && (seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))){ + && ((seen_undel = count_flagged(stream, F_SEEN | F_UNDEL)) + || (F_ON(F_AUTO_READ_MSGS,ps_global) && + F_ON(F_AUTO_READ_MSGS_RULES, ps_global)))){ for(; f && *archive; archive++){ char *p; @@ -11517,13 +12087,14 @@ ----*/ int -apply_command(state, stream, msgmap, preloadkeystroke, flags, q_line) +apply_command(state, stream, msgmap, preloadkeystroke, flags, q_line, display) struct pine *state; MAILSTREAM *stream; MSGNO_S *msgmap; int preloadkeystroke; int flags; int q_line; + int display; { int i = 8, /* number of static entries in sel_opts3 */ rv = 1, @@ -11650,9 +12221,19 @@ collapse_or_expand(state, stream, msgmap, F_ON(F_SLASH_COLL_ENTIRE, ps_global) ? 0L - : mn_get_cur(msgmap)); + : mn_get_cur(msgmap), + display); break; + case '[' : + collapse_this_thread(state, stream, msgmap, display, 0); + break; + + case ']' : + expand_this_thread(state, stream, msgmap, display, 0); + break; + + case ':' : select_thread_stmp(state, stream, msgmap); break; @@ -12170,6 +12751,7 @@ SEARCHSET **msgset; { PINETHRD_S *nthrd, *bthrd; + unsigned long next, branch; if(!(stream && thrd)) return; @@ -12178,14 +12760,14 @@ && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno))) mm_searched(stream, thrd->rawno); - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next= get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) set_search_bit_for_thread(stream, nthrd, msgset); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) set_search_bit_for_thread(stream, bthrd, msgset); } @@ -12393,7 +12975,7 @@ { int r, type, we_cancel = 0, not = 0, flags, old_imap; char sstring[80], savedsstring[80], origcharset[16], tmp[128]; - char *sval = NULL, *cset = NULL, *charset = NULL; + char namehdr[80], *sval = NULL, *cset = NULL, *charset = NULL; char buftmp[MAILTMPLEN]; ESCKEY_S ekey[4]; ENVELOPE *env = NULL; @@ -12467,6 +13049,40 @@ sval = "BODYTEXT"; break; + case 'h' : + sprintf(tmp, "Name of HEADER to match : "); + flags = OE_APPEND_CURRENT; + namehdr[0] = '\0'; + r = 'x'; + while (r == 'x'){ + int done = 0; + + r = optionally_enter(namehdr, -FOOTER_ROWS(ps_global), 0, + sizeof(namehdr), tmp, ekey, NO_HELP, &flags); + if (r == 1){ + cmd_cancelled("Selection by text"); + return(1); + } + removing_leading_white_space(namehdr); + while(!done){ + while ((namehdr[0] != '\0') && /* remove trailing ":" */ + (namehdr[strlen(namehdr) - 1] == ':')) + namehdr[strlen(namehdr) - 1] = '\0'; + if ((namehdr[0] != '\0') + && isspace((unsigned char) namehdr[strlen(namehdr) - 1])) + removing_trailing_white_space(namehdr); + else + done++; + } + if (strchr(namehdr,' ') || strchr(namehdr,'\t') || + strchr(namehdr,':')) + namehdr[0] = '\0'; + if (namehdr[0] == '\0') + r = 'x'; + } + sval = namehdr; + break; + case 'x': break; @@ -12590,6 +13206,9 @@ } switch(type){ + case 'h' : /* Any header */ + pgm->header = mail_newsearchheader (namehdr,sstring); + break; case 'r' : /* TO or CC */ if(old_imap){ /* No OR on old servers */ @@ -13767,14 +14386,15 @@ Returns 0 if it was cancelled, 1 otherwise. ----*/ int -select_sort(state, ql, sort, rev) +select_sort(state, ql, sort, rev, thread) struct pine *state; int ql; SortOrder *sort; int *rev; + int thread; { char prompt[200], tmp[3], *p; - int s, i; + int s, i, j; int deefault = 'a', retval = 1; HelpType help; ESCKEY_S sorts[14]; @@ -13808,17 +14428,27 @@ strncpy(prompt, "Choose type of sort, or 'R' to reverse current sort : ", sizeof(prompt)); - for(i = 0; state->sort_types[i] != EndofList; i++) { - sorts[i].rval = i; - p = sorts[i].label = sort_name(state->sort_types[i]); - while(*(p+1) && islower((unsigned char)*p)) - p++; - - sorts[i].ch = tolower((unsigned char)(tmp[0] = *p)); - sorts[i].name = cpystr(tmp); - - if(mn_get_sort(state->msgmap) == state->sort_types[i]) - deefault = sorts[i].rval; + for(i = 0, j = 0; state->sort_types[i] != EndofList; i++) { + sorts[i].rval = i; + sorts[i].name = cpystr(""); + sorts[i].label = ""; + sorts[i].ch = -2; + if (!thread || state->sort_types[i] == SortArrival + || state->sort_types[i] == SortThread){ + p = sorts[j].label = sort_name(state->sort_types[i]); + while(*(p+1) && islower((unsigned char)*p)) + p++; + sorts[j].ch = tolower((unsigned char)(tmp[0] = *p)); + sorts[j++].name = cpystr(tmp); + } + + if (thread){ + if (state->thread_def_sort == state->sort_types[i]) + deefault = sorts[j-1].rval; + } + else + if(mn_get_sort(state->msgmap) == state->sort_types[i]) + deefault = sorts[i].rval; } sorts[i].ch = 'r'; @@ -13843,7 +14473,7 @@ if(s == 'r') *rev = !mn_get_revsort(state->msgmap); else - *sort = state->sort_types[s]; + *sort = state->sort_types[thread ? (s == 0 ? 1 : 9) : s]; if(F_ON(F_SHOW_SORT, ps_global)) ps_global->mangled_header = 1; @@ -14531,3 +15161,699 @@ return(flag_submenu); } #endif /* _WINDOWS */ + +/* Extra Fancy Thread support */ + +long +top_thread(stream, rawmsgno) + MAILSTREAM *stream; + long rawmsgno; +{ + PINETHRD_S *thrd = NULL; + unsigned long rawno; + + if(!stream) + return -1L; + + if(rawmsgno) + thrd = fetch_thread(stream, rawmsgno); + + if(!thrd) + return -1L; + + return F_ON(F_ENHANCED_THREAD, ps_global) + ? (thrd->toploose ? thrd->toploose : thrd->top) + : thrd->top; +} + +void +move_top_thread(stream, msgmap, rawmsgno) + MAILSTREAM *stream; + MSGNO_S *msgmap; + long rawmsgno; +{ + mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream, rawmsgno))); +} + +long +top_this_thread(stream, rawmsgno) + MAILSTREAM *stream; + long rawmsgno; +{ + PINETHRD_S *thrd = NULL; + unsigned long rawno; + + if(!stream) + return -1L; + + if(rawmsgno) + thrd = fetch_thread(stream, rawmsgno); + + if(!thrd) + return -1L; + + return thrd->top; +} + +void +move_top_this_thread(stream, msgmap, rawmsgno) + MAILSTREAM *stream; + MSGNO_S *msgmap; + long rawmsgno; +{ + mn_set_cur(msgmap,mn_raw2m(msgmap, top_this_thread(stream, rawmsgno))); +} + + +void +cmd_delete_this_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; +{ + unsigned long rawno, top, save_kolapsed; + PINETHRD_S *thrd = NULL, *nxthrd; + + if(!stream) + return; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_this_thread(stream, msgmap, rawno); + top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, top); + collapse_this_thread(state, stream, msgmap, 0, 0); + thread_command(state, stream, msgmap, 'd', -FOOTER_ROWS(state), 1); + if (!save_kolapsed) + expand_this_thread(state, stream, msgmap, 0, 0); +} + +void +cmd_delete_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; +{ + unsigned long rawno, top, orig_top, topnxt, save_kolapsed; + PINETHRD_S *thrd = NULL, *nxthrd; + int done = 0, count; + + if(!stream) + return; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap, rawno); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + while (!done){ + cmd_delete_this_thread(state, stream, msgmap); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, rawno)); + cmd_delete(state, msgmap, 0, MsgIndx); + count = count_thread(state, stream, msgmap, rawno); + q_status_message2(SM_ORDER, 0, 1, "%s message%s marked deleted", + int2string(count), plural(count)); +} + + + +int +thread_is_kolapsed(state, stream, msgmap, rawmsgno) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + long rawmsgno; +{ + int collapsed; + PINETHRD_S *thrd = NULL; + unsigned long rawno, orig, orig_rawno; + + if(!stream) + return -1; + + orig = mn_get_cur(msgmap); + move_top_thread(stream, msgmap, rawmsgno); + rawno = orig_rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + while(collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno)) + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_rawno != top_thread(stream, rawno))) + break; + + mn_set_cur(msgmap,orig); /* return home */ + + return collapsed; +} + +/* this function tells us if the thread (or branch in the case of loose threads) + * is collapsed + */ + +int +this_thread_is_kolapsed(state, stream, msgmap, rawmsgno) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + long rawmsgno; +{ + int collapsed; + PINETHRD_S *thrd = NULL; + unsigned long rawno, orig; + + if(!stream) + return -1; + + rawno = rawmsgno; + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + collapsed = get_lflag(stream, NULL, rawno, MN_COLL | MN_CHID); + + if (!thrd->next){ + if (thrd->rawno != top_thread(stream, thrd->rawno)) + collapsed = get_lflag(stream, NULL, rawno, MN_CHID); + else + collapsed = get_lflag(stream, NULL, rawno, MN_COLL); + } + + return collapsed; +} + +int +collapse_this_thread(state, stream, msgmap, display, special) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; + int special; +{ + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL, *nthrd; + unsigned long rawno, orig, msgno; + + if(!stream) + return 0; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return rv; + + collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); + + if (special && collapsed){ + expand_this_thread(state, stream, msgmap, 0, 0); + collapsed = 0; + } + + clear_index_cache_ent(mn_raw2m(msgmap,rawno)); + + if (!collapsed && thrd->next){ + if (thrd->rawno == top_thread(stream, thrd->rawno)) + collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display); + else{ + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 1); + set_thread_subtree(stream, thrd, msgmap, 1, MN_CHID); + } + } + else{ + if (!collapsed && special + && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next) + || F_ON(F_ENHANCED_THREAD, state))){ + if (thrd->toploose){ + if (thrd->rawno != thrd->toploose) + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, + 1); + else + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, + 1); + } + } + else{ + rv = 0; + if (display) + q_status_message(SM_ORDER, 0, 1, "Thread already collapsed"); + } + } + return rv; +} + +void +collapse_thread(state, stream, msgmap, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; +{ + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL; + unsigned long orig, orig_top, top; + + if(!stream) + return; + + expand_this_thread(state, stream, msgmap, display, 1); + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap,orig); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + while (!done){ + collapse_this_thread(state, stream, msgmap, display, 1); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top)); +} + +int +expand_this_thread(state, stream, msgmap, display, special) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; + int special; +{ + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL, *nthrd; + unsigned long rawno, orig, msgno; + + if(!stream) + return 0; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_this_thread(stream, msgmap,orig); + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return rv; + + collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); + + if (special && !collapsed){ + collapse_this_thread(state, stream, msgmap, 0, 0); + collapsed = 1; + } + + clear_index_cache_ent(mn_raw2m(msgmap,rawno)); + + if (collapsed && thrd->next){ + if (thrd->rawno == top_thread(stream, thrd->rawno)) + collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display); + else{ + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 0); + set_thread_subtree(stream, thrd, msgmap, 0, MN_CHID); + } + } + else{ + if (collapsed && special + && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next) + || F_ON(F_ENHANCED_THREAD, state))){ + if (thrd->toploose) + if (thrd->rawno != thrd->toploose) + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, 0); + else + set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, 0); + } + else{ + rv = 0; + if (display) + q_status_message(SM_ORDER, 0, 1, "Thread already expanded"); + } + } + return rv; +} + +void +expand_thread(state, stream, msgmap, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; +{ + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL; + unsigned long orig, orig_top, top; + + if(!stream) + return; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + while (!done){ + expand_this_thread(state, stream, msgmap, display, 1); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top)); +} + + +void +cmd_undelete_this_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; +{ + unsigned long rawno; + int save_kolapsed; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno); + collapse_this_thread(state, stream, msgmap, 0, 0); + thread_command(state, stream, msgmap, 'u', -FOOTER_ROWS(state), 1); + if (!save_kolapsed) + expand_this_thread(state, stream, msgmap, 0, 0); +} + +void +cmd_undelete_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; +{ + PINETHRD_S *thrd = NULL; + unsigned long rawno, top, orig_top; + int done = 0, count; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap, rawno); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return; + + while (!done){ + cmd_undelete_this_thread(state, stream, msgmap); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, rawno)); + count = count_thread(state, stream, msgmap, rawno); + q_status_message2(SM_ORDER, 0, 1, "Deletion mark removed from %s message%s", + int2string(count), plural(count)); +} + +void +kolapse_thread(state, stream, msgmap, ch, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + char ch; + int display; +{ + PINETHRD_S *thrd = NULL; + unsigned long rawno; + int rv = 1, done = 0; + + if(!stream) + return; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return; + + clear_index_cache(); + mn_set_cur(msgmap,1); /* go to the first message */ + while (!done){ + if (ch == '[') + collapse_thread(state, stream, msgmap, display); + else + expand_thread(state, stream, msgmap, display); + if ((rv = move_next_thread(state, stream, msgmap, 0)) <= 0) + done++; + } + + if (rv < 0){ + if (display) + q_status_message(SM_ORDER, 0, 1, (ch == '[') + ? "Error while collapsing thread" + : "Error while expanding thread"); + } + else + if(display) + q_status_message(SM_ORDER, 0, 1, (ch == '[') + ? "All threads collapsed. Use \"}\" to expand them" + : "All threads expanded. Use \"{\" to collapse them"); + + mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,rawno))); +} + +int +move_next_this_thread(state, stream, msgmap, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; +{ + PINETHRD_S *thrd = NULL, *thrdnxt; + unsigned long rawno, top; + int rv = 1; + + if(!stream) + return -1; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + top = top_thread(stream, rawno); + + thrdnxt = (top == rawno) ? fetch_thread(stream, top) : thrd; + if (thrdnxt->nextthd) + mn_set_cur(msgmap,mn_raw2m(msgmap, thrdnxt->nextthd)); + else{ + rv = 0; + if (display) + q_status_message(SM_ORDER, 0, 1, "No more Threads to advance"); + } + return rv; +} + +int +move_next_thread(state, stream, msgmap, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; +{ + int collapsed, rv = 1, done = 0; + PINETHRD_S *thrd = NULL; + unsigned long orig, orig_top, top; + + if(!stream) + return 0; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap,orig); + top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return 0; + + while (rv > 0 && !done){ + rv = move_next_this_thread(state, stream, msgmap, display); + if (F_OFF(F_ENHANCED_THREAD, state) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + if (display){ + if (rv > 0 && SEP_THRDINDX()) + q_status_message(SM_ORDER, 0, 2, "Viewing next thread"); + if (!rv) + q_status_message(SM_ORDER, 0, 2, "No more threads to advance"); + } + if(rv <= 0){ + rv = 0; + mn_set_cur(msgmap, mn_raw2m(msgmap, orig)); + } + + return rv; +} + +int +move_prev_thread(state, stream, msgmap, display) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int display; +{ + PINETHRD_S *thrd = NULL; + unsigned long rawno, top; + int rv = 1; + + if(!stream) + return -1; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return -1; + + top = top_thread(stream, rawno); + + if (top != rawno) + mn_set_cur(msgmap,mn_raw2m(msgmap, top)); + else if (thrd->prevthd) + mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,thrd->prevthd))); + else + rv = 0; + if (display){ + if (rv && SEP_THRDINDX()) + q_status_message(SM_ORDER, 0, 2, "Viewing previous thread"); + if (!rv) + q_status_message(SM_ORDER, 0, 2, "No more threads to go back"); + } + + return rv; +} + +void +cmd_select_thread(state, stream, msgmap) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; +{ + unsigned long rawno; + int save_kolapsed; + + rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)); + save_kolapsed = thread_is_kolapsed(state, stream, msgmap, rawno); + collapse_thread(state, stream, msgmap, 0); + thread_command(state, stream, msgmap, ':', -FOOTER_ROWS(state), 1); + if (!save_kolapsed) + expand_thread(state, stream, msgmap, 0); +} + +/* + * This function assumes that it is called at a top of a thread in its + * first call + */ + +int +count_this_thread(stream, rawno) + MAILSTREAM *stream; + unsigned long rawno; +{ + unsigned long top, orig_top, topnxt; + PINETHRD_S *thrd = NULL; + int count = 1; + + if(!stream) + return 0; + + if(rawno) + thrd = fetch_thread(stream, rawno); + + if(!thrd) + return 0; + + if (thrd->next) + count += count_this_thread(stream, thrd->next); + + if (thrd->branch) + count += count_this_thread(stream, thrd->branch); + + return count; +} + +int +count_thread(state, stream, msgmap, rawno) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + long rawno; +{ + unsigned long top, orig, orig_top; + PINETHRD_S *thrd = NULL; + int done = 0, count = 0; + + if(!stream) + return 0; + + orig = mn_m2raw(msgmap, mn_get_cur(msgmap)); + move_top_thread(stream, msgmap,rawno); + top = orig_top = top_thread(stream, rawno); + if(top) + thrd = fetch_thread(stream, top); + + if(!thrd) + return 0; + + while (!done){ + count += count_this_thread(stream, top); + if (F_OFF(F_ENHANCED_THREAD, state) + || (move_next_this_thread(state, stream, msgmap, 0) <= 0) + || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap))) + || (orig_top != top_thread(stream, top))) + done++; + } + mn_set_cur(msgmap,mn_raw2m(msgmap, orig)); + return count; +} diff -ru pine4.64/pine/mailindx.c pine4.64.SuSE/pine/mailindx.c --- pine4.64/pine/mailindx.c 2005-05-04 00:00:53.000000000 +0200 +++ pine4.64.SuSE/pine/mailindx.c 2006-02-14 14:45:24.000000000 +0100 @@ -112,9 +112,22 @@ RCOMPOSE_MENU, HOMEKEY_MENU, ENDKEY_MENU, - NULL_MENU, + {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE}, {"/","Collapse/Expand",{MC_COLLAPSE,1,{'/'}},KS_NONE}, - NULL_MENU, + {"{","Collapse All",{MC_KOLAPSE,1,{'{'}},KS_NONE}, + {"}","Expand All", {MC_EXPTHREAD,1,{'}'}},KS_NONE}, + + HELP_MENU, + OTHER_MENU, + {")","Next Threa",{MC_NEXTHREAD,1,{')'}},KS_NONE}, + {"(","Prev Threa",{MC_PRETHREAD,1,{'('}},KS_NONE}, + {"^D","Delete Thr",{MC_DELTHREAD,1,{ctrl('D')}},KS_NONE}, + {"^U","Undel Thre",{MC_UNDTHREAD,1,{ctrl('U')}},KS_NONE}, + {"^T","Select Thr",{MC_SELTHREAD,1,{ctrl('T')}},KS_NONE}, + {"^H","ChkIncFl",{MC_FORCECHECK,1,{ctrl('H')}},KS_NONE}, + {"[","Close Thre",{MC_CTHREAD,1,{'['}},KS_NONE}, + {"]","Open Threa",{MC_OTHREAD,1,{']'}},KS_NONE}, + QUOTA_MENU, NULL_MENU}; INST_KEY_MENU(index_keymenu, index_keys); #define BACK_KEY 2 @@ -197,9 +210,22 @@ RCOMPOSE_MENU, HOMEKEY_MENU, ENDKEY_MENU, - NULL_MENU, + {"]","Open Threa",{MC_OTHREAD,1,{']'}},KS_NONE}, {"/","Collapse/Expand",{MC_COLLAPSE,1,{'/'}},KS_NONE}, + {")","Next Threa",{MC_NEXTHREAD,1,{')'}},KS_NONE}, + {"(","Prev Threa",{MC_PRETHREAD,1,{'('}},KS_NONE}, + + HELP_MENU, + OTHER_MENU, + NULL_MENU, + NULL_MENU, + {"^D","Delete Thr",{MC_DELTHREAD,1,{ctrl('D')}},KS_NONE}, + {"^U","Undel Thre",{MC_UNDTHREAD,1,{ctrl('U')}},KS_NONE}, + {"^T","Select Thr",{MC_SELTHREAD,1,{ctrl('T')}},KS_NONE}, NULL_MENU, + {"^H","ChkIncFl",{MC_FORCECHECK,1,{ctrl('H')}},KS_NONE}, + QUOTA_MENU, + {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE}, NULL_MENU}; INST_KEY_MENU(thread_keymenu, thread_keys); @@ -315,12 +341,17 @@ HLINE_S *(*format_index_line) PROTO((INDEXDATA_S *)); void (*setup_header_widths) PROTO((void)); - +static int erase_thread_info = 1; /* * Internal prototypes */ +SortOrder translate PROTO ((char *, int)); +ENVELOPE *make_envelope PROTO ((INDEXDATA_S *, int)); +char *find_value PROTO ((char *,char *, int, char *, int, char *, int, INDEXDATA_S *, int)); +int find_index_rule PROTO((void)); +void setup_threading_display_style PROTO((void)); void index_index_screen PROTO((struct pine *)); void thread_index_screen PROTO((struct pine *)); void setup_for_index_index_screen PROTO((void)); @@ -346,6 +377,14 @@ void index_data_env PROTO((INDEXDATA_S *, ENVELOPE *)); int set_index_addr PROTO((INDEXDATA_S *, char *, ADDRESS *, char *, int, char *)); +unsigned long get_next PROTO((MAILSTREAM *,PINETHRD_S *)); +unsigned long get_branch PROTO((MAILSTREAM *,PINETHRD_S *)); +long get_length_branch PROTO((MAILSTREAM *, long)); +THREADNODE *copy_tree PROTO((THREADNODE *)); +void find_msgmap PROTO((MAILSTREAM *, MSGNO_S *, int, SortOrder, + unsigned)); +void move_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int)); +void relink_threads PROTO((MAILSTREAM *, MSGNO_S *, long *)); int i_cache_size PROTO((long)); int i_cache_width PROTO(()); int ctype_is_fixed_length PROTO((IndexColType)); @@ -390,17 +429,19 @@ struct pass_along *sort_thread_flatten PROTO((THREADNODE *, MAILSTREAM *, struct pass_along *, - PINETHRD_S *, unsigned)); + PINETHRD_S *, unsigned, int, + long,long)); void make_thrdflags_consistent PROTO((MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int)); THREADNODE *collapse_threadnode_tree PROTO((THREADNODE *)); +THREADNODE *copy_tree PROTO((THREADNODE *)); PINETHRD_S *msgno_thread_info PROTO((MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned)); long calculate_visible_threads PROTO((MAILSTREAM *)); void set_thread_subtree PROTO((MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int, int)); void thread_command PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, - int, int)); + int, int, int)); void set_flags_for_thread PROTO((MAILSTREAM *, MSGNO_S *, int, PINETHRD_S *, int)); unsigned long count_flags_in_thread PROTO((MAILSTREAM *, PINETHRD_S *, long)); @@ -592,9 +633,12 @@ return; } + state->redrawer = redraw_index_body; state->prev_screen = mail_index_screen; state->next_screen = SCREEN_FUN_NULL; + setup_threading_display_style(); + if(THRD_AUTO_VIEW() && sp_viewing_a_thread(state->mail_stream) && state->view_skipped_index) unview_thread(state, state->mail_stream, state->msgmap); @@ -702,19 +746,29 @@ MAILSTREAM *stream; MSGNO_S *msgmap; { - int ch, cmd, which_keys, force, + int ch, cmd, which_keys, force, skip = 0, cur_row, cur_col, km_popped, paint_status; int old_day = -1; - long i, j, k, old_max_msgno; + long i, j, k, old_max_msgno, nm; IndexType style, old_style = MsgIndex; struct index_state id; struct key_menu *km = NULL; + FOLDER_S *f; #if defined(DOS) || defined(OS2) extern void (*while_waiting)(); #endif dprint(1, (debugfile, "\n\n ---- INDEX MANAGER ----\n")); - + if (f = incoming_folder_data(stream, cntxt)){ + f->selected = f->user_selected; /* unselect this folder now */ + f->origrecent = stream->recent; /* more accurate than f->recent */ + f->notified = 1; /* no updates in this screen */ + f->countrecent = 0; /* Yes, we want new mail only */ + f->skipped = 0; /* Erase the ".", if any */ + f->last_check_time = 0; /* and assume no delay for now */ + if(!selected_folders(cntxt) && (cntxt->use & CNTXT_ZOOM)) + cntxt->use &= ~CNTXT_ZOOM; /* exit zoom mode if necessary */ + } ch = 'x'; /* For displaying msg 1st time thru */ force = 0; km_popped = 0; @@ -749,9 +803,24 @@ } /*------- Check for new mail -------*/ - new_mail(force, NM_TIMING(ch), NM_STATUS_MSG); - force = 0; /* may not need to next time around */ - + nm = new_mail(force, NM_TIMING(ch), NM_STATUS_MSG); + if (!skip || nm > 0L){ + if(nm > 0L) + state->force_check_now = 1; + if(f) + f->notified = 1; + new_mail_incfolder(state, MC_IFAUTOCHECK); + } + if (f){ + long rec, tot; + new_mail_in_open_stream(stream, &rec, &tot); + f->countrecent = 0; + f->selected = f->user_selected; + f->recent = rec; + f->messages = tot; + } + ps_global->refresh_list &= IF_REFRESH_NONE; + force = skip = 0; /* may not need to next time around */ /* * If the width of the message number field in the display changes * we need to flush the cache and redraw. When the cache is cleared @@ -943,6 +1012,9 @@ break; } + if ((cmd != MC_NONE) && (cmd != MC_FORCECHECK)) + state->force_check_now = 0; + /*----------- Execute the command ------------------*/ switch(cmd){ @@ -958,6 +1030,7 @@ /*---------- Scroll line up ----------*/ case MC_CHARUP : +previtem: (void) process_cmd(state, stream, msgmap, MC_PREVITEM, (style == MsgIndex || style == MultiMsgIndex @@ -975,6 +1048,7 @@ /*---------- Scroll line down ----------*/ case MC_CHARDOWN : +nextitem: /* * Special Page framing handling here. If we * did something that should scroll-by-a-line, frame @@ -1192,6 +1266,7 @@ case MC_THRDINDX : +mc_thrdindx: msgmap->top = msgmap->top_after_thrd; if(unview_thread(state, stream, msgmap)){ ps_global->redrawer = NULL; @@ -1239,7 +1314,7 @@ && mp.col == id.plus_col && style != ThreadIndex){ collapse_or_expand(state, stream, msgmap, - mn_get_cur(msgmap)); + mn_get_cur(msgmap), 1); } else if (mp.doubleclick){ if(mp.button == M_BUTTON_LEFT){ @@ -1309,6 +1384,8 @@ reset_index_border(); break; + case MC_QUOTA: + cmd_quota(state); /*---------- Redraw ----------*/ case MC_REPAINT : @@ -1333,9 +1410,106 @@ case MC_COLLAPSE : - thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state)); + thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state), 1); break; + case MC_CTHREAD : + if (SEP_THRDINDX()) + goto mc_thrdindx; + else + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, + "to collapse a thread")) + collapse_thread(state, stream,msgmap, 1); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_OTHREAD : + if (SEP_THRDINDX()) + goto view_a_thread; + else + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to expand a thread")) + expand_thread(state, stream,msgmap, 1); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_NEXTHREAD: + case MC_PRETHREAD: + if (THRD_INDX()){ + if (cmd == MC_NEXTHREAD) + goto nextitem; + else + goto previtem; + } + else + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, + "to move to other thread")) + move_thread(state, stream, msgmap, + cmd == MC_NEXTHREAD ? 1 : -1); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_KOLAPSE: + case MC_EXPTHREAD: + if (SEP_THRDINDX()){ + q_status_message(SM_ORDER, 0, 1, + "Command not available in this screen"); + } + else{ + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, + cmd == MC_KOLAPSE ? "to collapse" : "to expand")) + kolapse_thread(state, stream, msgmap, + (cmd == MC_KOLAPSE) ? '[' : ']', 1); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + } + break; + + case MC_DELTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to delete")) + cmd_delete_thread(state, stream, msgmap); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_UNDTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to undelete")) + cmd_undelete_thread(state, stream, msgmap); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + + case MC_SELTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to undelete")) + cmd_select_thread(state, stream, msgmap); + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + case MC_DELETE : case MC_UNDELETE : case MC_REPLY : @@ -1356,13 +1530,12 @@ if(rawno) thrd = fetch_thread(stream, rawno); - collapsed = thrd && thrd->next - && get_lflag(stream, NULL, rawno, MN_COLL); + collapsed = thread_is_kolapsed(ps_global, stream, msgmap, rawno); } if(collapsed){ thread_command(state, stream, msgmap, - ch, -FOOTER_ROWS(state)); + ch, -FOOTER_ROWS(state),1); /* increment current */ if(cmd == MC_DELETE){ advance_cur_after_delete(state, stream, msgmap, @@ -1401,7 +1574,10 @@ } /* else fall thru to normal default */ - + case MC_TAB: + skip++; + /* do not check for new mail in inc fldrs and fall through */ + /*---------- Default -- all other command ----------*/ default: do_the_default: @@ -2051,6 +2227,9 @@ int uc, ac, do_arrow; int i, drew_X = 0, cols = ps_global->ttyo->screen_cols; int inverse_hack = 0, need_inverse_hack = 0, lim; + int seq = 0, w = 0; + char utf_seq[7], *cp, *r; + if(uc=pico_usingcolor()) lastc = pico_get_cur_color(); @@ -2130,9 +2309,26 @@ draw[acol+i] = '>'; } + cp = NULL; if(pcol >= 0 && pcol < cols){ - save_pchar = draw[pcol]; - draw[pcol] = h->plus; + memset(utf_seq, 0, sizeof(utf_seq)); + for(cp = draw; *cp; cp++) { + if (!(r = pine_check_utf8(cp, utf_seq, sizeof(utf_seq)))) { + seq = 1; + continue; + } + if (seq) + w++; + seq = 0; + if (r == cp) + w++; + else if (*r == ' ') + w++; + if (w > pcol) + break; + } + save_pchar = *cp; + *cp = h->plus; } if(h->offs[0].offset < 0 || h->offs[0].offset >= cols){ @@ -2295,8 +2491,8 @@ if(!ac && cur) EndInverse(); - if(pcol >= 0 && pcol < cols) - draw[pcol] = save_pchar; + if(cp) + *cp = save_pchar; if(do_arrow && cur){ int i; @@ -2772,6 +2968,7 @@ n = mn_raw2m(msgs, thrd->rawno); while(thrd){ + unsigned long branch; if(!msgline_hidden(stream, msgs, n, 0) && (++m % lines_per_page) == 1L) t = n; @@ -2840,11 +3037,12 @@ /* n is the end of this thread */ while(thrd){ + unsigned long next = 0L, branch = 0L; n = mn_raw2m(msgs, thrd->rawno); - if(thrd->branch) - thrd = fetch_thread(stream, thrd->branch); - else if(thrd->next) - thrd = fetch_thread(stream, thrd->next); + if(branch = get_branch(stream,thrd)) + thrd = fetch_thread(stream, branch); + else if(next = get_next(stream,thrd)) + thrd = fetch_thread(stream, next); else thrd = NULL; } @@ -2998,6 +3196,7 @@ case iSTime: case iKSize: case iSize: + case iSizeThread: (*answer)[column].req_width = 7; break; case iS1Date: @@ -3034,14 +3233,15 @@ static INDEX_PARSE_T itokens[] = { {"STATUS", iStatus, FOR_INDEX}, {"MSGNO", iMessNo, FOR_INDEX}, - {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, + {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, {"FROMORTO", iFromTo, FOR_INDEX}, {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX}, {"SIZE", iSize, FOR_INDEX}, {"SIZECOMMA", iSizeComma, FOR_INDEX}, + {"SIZETHREAD", iSizeThread, FOR_INDEX}, {"SIZENARROW", iSizeNarrow, FOR_INDEX}, {"KSIZE", iKSize, FOR_INDEX}, - {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, + {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_TRIM}, {"FULLSTATUS", iFStatus, FOR_INDEX}, {"IMAPSTATUS", iIStatus, FOR_INDEX}, {"SUBJKEY", iSubjKey, FOR_INDEX}, @@ -3051,26 +3251,29 @@ {"DESCRIPSIZE", iDescripSize, FOR_INDEX}, {"ATT", iAtt, FOR_INDEX}, {"SCORE", iScore, FOR_INDEX}, - {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, + {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_RULE|FOR_COMPOSE}, + {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_RULE|FOR_COMPOSE}, + {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_RULE}, + {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_RULE}, + {"ADDRESSTO", iAddressTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_RULE}, + {"ADDRESSCC", iAddressCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_RULE}, + {"ADDRESSRECIPS", iAddressRecip, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_SAVE|FOR_RULE}, {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, @@ -3079,46 +3282,55 @@ {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, + {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE}, + {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE}, {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE}, {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE}, - {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, + {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb, - FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, + FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit, - FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, - {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT}, + FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, + {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_RULE|FOR_SAVE|FOR_FILT}, {"ARROW", iArrow, FOR_INDEX}, {"CURSORPOS", iCursorPos, FOR_TEMPLATE}, + {"NICK", iNick, FOR_RULE|FOR_SAVE}, + {"FOLDER", iFolder, FOR_RULE|FOR_SAVE|FOR_FOLDER}, + {"ROLE", iRole, FOR_RULE|FOR_RESUB|FOR_TRIM|FOR_TEMPLATE}, + {"FLAG", iFlag, FOR_RULE|FOR_SAVE|FOR_FLAG}, + {"COLLECTION", iCollection, FOR_RULE|FOR_SAVE|FOR_COMPOSE|FOR_FOLDER}, + {"BCC", iBcc, FOR_COMPOSE|FOR_RULE}, + {"LCC", iLcc, FOR_COMPOSE|FOR_RULE}, + {"FORWARDFROM", iFfrom, FOR_COMPOSE|FOR_RULE}, + {"FORWARDADDRESS", iFadd, FOR_COMPOSE|FOR_RULE}, {NULL, iNothing, FOR_NOTHING} }; @@ -3286,7 +3498,7 @@ */ static IndexColType fixed_ctypes[] = { iArrow, iMessNo, iStatus, iFStatus, iIStatus, iDate, iSDate, iSDateTime, - iSTime, iLDate, + iSTime, iLDate, iSizeThread, iS1Date, iS2Date, iS3Date, iS4Date, iDateIso, iDateIsoS, iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit, @@ -3457,6 +3669,7 @@ case iTime12: case iSize: case iKSize: + case iSizeThread: cdesc->actual_length = 7; cdesc->adjustment = Right; break; @@ -3521,7 +3734,7 @@ cdesc->ctype != iNothing; cdesc++) if(cdesc->ctype == iSize || cdesc->ctype == iKSize || - cdesc->ctype == iSizeNarrow || + cdesc->ctype == iSizeNarrow || cdesc->ctype == iSizeThread || cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){ if(cdesc->actual_length == 0){ if((fix=cdesc->width) > 0){ /* had this reserved */ @@ -3813,6 +4026,80 @@ } } +ENVELOPE *make_envelope(idata, index) + INDEXDATA_S *idata; + int index; +{ + ENVELOPE *result; + + result = mail_newenvelope(); + + result->from = rfc822_cpy_adr(idata->from); + result->to = rfc822_cpy_adr(idata->to); + result->cc = rfc822_cpy_adr(idata->cc); + result->sender = rfc822_cpy_adr(idata->sender); + result->subject = cpystr(idata->subject); + result->newsgroups = index ? cpystr(idata->newsgroups) : + IS_NEWS(idata->stream) ? cpystr(ps_global->cur_folder) + : NULL; + return result; +} + +/*--------------------------------- + +-----------*/ + +char *find_value(token,function1, context1, function2, context2, function3, context3, idata, code) +char *token; +char *function1; +int context1; +char *function2; +int context2; +char *function3; +int context3; +INDEXDATA_S *idata; +int code; +{ int n = 0, done = 0, next_step = 0; + char *rule_result; + int rule_context; + RULELIST *rule = get_rulelist_from_code(code, ps_global->rule_list); + RULE_S *prule; + + if (rule){ + rule_context = FOR_RULE; + while (!done && (prule = get_rule(rule,n++))){ + if (context1 && prule->action->token && + !strcmp(prule->action->token, token) + && !strcmp(prule->action->function, function1)){ + rule_context |= context1; + next_step++; + } + if (context2 && prule->action->token && + !strcmp(prule->action->token, token) + && !strcmp(prule->action->function, function2)){ + rule_context |= context2; + next_step++; + } + if (context3 && prule->action->token && + !strcmp(prule->action->token, token) + && !strcmp(prule->action->function, function3)){ + rule_context |= context3; + next_step++; + } + if (next_step){ + ENVELOPE *local_env = make_envelope(idata,1); + next_step = FALSE; + rule_result = process_rule(prule, rule_context, local_env); + if (local_env) + mail_free_envelope(&local_env); + if (rule_result) + done++; + } + } + } + return done ? rule_result : NULL; +} + /*---------------------------------------------------------------------- Create a string summarizing the message header for index on screen @@ -3945,10 +4232,11 @@ /* find next thread which is visible */ do{ + unsigned long branch; if(mn_get_revsort(msgmap) && thrd->prevthd) thrd = fetch_thread(stream, thrd->prevthd); - else if(!mn_get_revsort(msgmap) && thrd->nextthd) - thrd = fetch_thread(stream, thrd->nextthd); + else if(!mn_get_revsort(msgmap) && thrd->branch) + thrd = fetch_thread(stream, thrd->branch); else thrd = NULL; } while(thrd @@ -4363,6 +4651,58 @@ return(doy); } +static char * +rfc_1522_check_charset(chp) + char *chp; +{ + static char *subj_cs = NULL; + char *cs, *enc; + + while(chp && (chp = strstr(chp, "=?"))) + if(rfc1522_valid(chp++, 1, &cs, &enc, NULL, NULL)){ + int cs_len = enc - cs - 1; + + if(subj_cs) + fs_give((void **)&subj_cs); + + strncpy(subj_cs = fs_get(cs_len + 1), cs, cs_len); + subj_cs[cs_len] = 0; + + return subj_cs; + } + return NULL; +} + +static void +rfc1522_decode_width(dest, source, width, idata) + char *dest; + char *source; + int width; + INDEXDATA_S *idata; +{ + char *subj_cs, *assumed_save = NULL, *dummy = NULL, *tmp; + + if(idata && (subj_cs = rfc_1522_check_charset(fetch_subject(idata)))){ + assumed_save = ps_global->VAR_ASSUMED_CHAR_SET; + ps_global->VAR_ASSUMED_CHAR_SET = subj_cs; + } + + tmp = (char *) rfc1522_decode((unsigned char *) tmp_20k_buf, + SIZEOF_20KBUF, source, &dummy); + if(idata){ + if(tmp == source) + strncpy(tmp = tmp_20k_buf, source, SIZEOF_20KBUF); + + removing_leading_and_trailing_white_space(tmp); + + if(subj_cs) + ps_global->VAR_ASSUMED_CHAR_SET = assumed_save; + } + charset_istrncpy(dest, tmp, width, 0); + + if(dummy) + fs_give((void **)&dummy); +} /*---------------------------------------------------------------------- @@ -4379,7 +4719,8 @@ format_index_index_line(idata) INDEXDATA_S *idata; { - char str_buf[MAXIFLDS][MAX_SCREEN_COLS+1], to_us, status, *field, +#define STRLEN MAX_SCREEN_COLS*6 + char str_buf[MAXIFLDS][STRLEN+1], to_us, status, *field, *buffer, *s_tmp, *p, *str, *newsgroups; int width, i, j, smallest, which_array = 0, collapsed = 0, offsets_set = 0, cur_offset = 0, noff = 0, noff_was; @@ -4404,11 +4745,9 @@ } /* is this a collapsed thread index line? */ + thrd = fetch_thread(idata->stream, idata->rawno); if(!idata->bogus && THREADING()){ - thrd = fetch_thread(idata->stream, idata->rawno); - collapsed = thrd && thrd->next - && get_lflag(idata->stream, NULL, - idata->rawno, MN_COLL); + collapsed = thrd && thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno); } /* calculate contents of the required fields */ @@ -4866,7 +5205,7 @@ case iFrom: case iAddress: case iMailbox: - from_str(cdesc->ctype, idata, width, str); + from_str(cdesc->ctype, idata, min(width*6,STRLEN), str); break; case iTo: @@ -4940,7 +5279,31 @@ break; + case iSizeThread: + if (!THREADING()){ + goto getsize; + } else if (collapsed){ + l = count_flags_in_thread(idata->stream, thrd, F_NONE); + sprintf(str, "(%lu)", l); + } + else{ + thrd = fetch_thread(idata->stream, idata->rawno); + if(!thrd) + sprintf(str,"Error"); + else{ + long lengthb; + lengthb = get_length_branch(idata->stream, idata->rawno); + + if (lengthb > 0L) + sprintf(str,"(%lu)", lengthb); + else + sprintf(str," "); + } + } + break; + case iSize: +getsize: /* 0 ... 9999 */ if((l = fetch_size(idata)) < 10*1000L) sprintf(str, "(%lu)", l); @@ -5159,12 +5522,12 @@ break; case iSubject: - subj_str(idata, width, str, NoKW, NULL, NULL); + subj_str(idata, min(width*6,STRLEN), str, NoKW, NULL, NULL); break; case iSubjKey: noff_was = noff; - subj_str(idata, width, str, KW, hline->offs, &noff); + subj_str(idata, min(width*6,STRLEN), str, KW, hline->offs, &noff); /* fix offsets which are now relative to str */ for(i = noff_was; i < noff; i++) if(hline->offs[i].offset >= 0) @@ -5345,34 +5708,11 @@ } if(cdesc->adjustment == Left) - sprintf(p, "%-*.*s", width, width, str); + charset_istrncpy(p, str, width, 1); else sprintf(p, "%*.*s", width, width, str); - /* - * Make sure there are no nulls in the part we were supposed to - * have just written. This may happen if sprintf returns an - * error, but we don't want to check for that because some - * sprintfs don't return anything. If there are nulls, rewrite it. - */ - for(q = p; q < p+width; q++) - if(*q == '\0') - break; - - if(q < p+width){ - strncpy(p, repeat_char(width, ' '), width); - p[width] = '\0'; - /* throw a ? in there too */ - if(width > 4){ - p[(width-1)/2 - 1] = '?'; - p[(width-1)/2 ] = '?'; - p[(width-1)/2 + 1] = '?'; - } - else if(width > 2) - p[(width-1)/2] = '?'; - } - - p += width; + p += strlen(p); } @@ -5401,7 +5741,7 @@ } /* Truncate it to be sure not too wide */ - buffer[min(ps_global->ttyo->screen_cols, i_cache_width())] = '\0'; + buffer[i_cache_width()] = '\0'; hline->id = line_hash(buffer); dprint(9, (debugfile, "INDEX(%p) -->%s<-- (%d), 0x%lx>\n", hline, @@ -6251,21 +6591,12 @@ if(addr && !addr->next /* only one address */ && addr->host /* not group syntax */ && addr->personal && addr->personal[0]){ /* there is a personal name */ - char *dummy = NULL; - char buftmp[MAILTMPLEN]; int l; if(l = prefix ? strlen(prefix) : 0) strcpy(s, prefix); - sprintf(buftmp, "%.75s", addr->personal); - p = (char *) rfc1522_decode((unsigned char *) tmp_20k_buf, - SIZEOF_20KBUF, buftmp, &dummy); - removing_leading_and_trailing_white_space(p); - istrncpy(s + l, p, width - l); - s[width] = '\0'; - if(dummy) - fs_give((void **)&dummy); + rfc1522_decode_width(s + l, addr->personal, width - l, idata); if(*(s+l)) return(TRUE); @@ -6286,8 +6617,13 @@ if(l = prefix ? strlen(prefix) : 0) strcpy(s, prefix); - istrncpy(s + l, a_string, width - l); - s[width] = '\0'; + if (p = rfc_1522_check_charset(fetch_subject(idata))) { + char *dest = s + l; + conv_sstrncpy(p, NULL, &dest, a_string, width); + } else { + istrncpy(s + l, a_string, width - l); + s[width] = '\0'; + } fs_give((void **)&a_string); return(TRUE); @@ -7031,11 +7367,19 @@ unsigned long rawno; unsigned char buf[MAILTMPLEN]; OFFCOLOR_S myoffs[OFFS]; - int mynoff = 0; + int mynoff = 0, we_clear = 0; + char *rule_result; memset(str, 0, (width+1) * sizeof(*str)); origstr = str; - origsubj = fetch_subject(idata); + rule_result = find_value("_SUBJECT_", "_REPLACE_", FOR_REPLACE, "_TRIM_", FOR_TRIM, "_REXTRIM_", FOR_TRIM, idata, V_REPLACE_RULES); + if (rule_result){ + we_clear++; + origsubj = cpystr(rule_result); + fs_give((void **)&rule_result); + } + else + origsubj = fetch_subject(idata); if(!origsubj) origsubj = ""; @@ -7060,8 +7404,8 @@ thdorig = thd = fetch_thread(idata->stream, idata->rawno); border = str + width; if(current_index_state->plus_col >= 0 && !THRD_INDX()){ - collapsed = thd && thd->next && - get_lflag(idata->stream, NULL, idata->rawno, MN_COLL); + collapsed = this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno); + collapsed = collapsed && (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1); hline = get_index_cache(idata->msgno); hline->plus = collapsed ? ps_global->VAR_THREAD_MORE_CHAR[0] : (thd && thd->next) @@ -7316,6 +7660,8 @@ if(free_subj) fs_give((void **) &free_subj); + if (we_clear && origsubj) + fs_give((void **)&origsubj); /* adjust offsets for indents and subject truncation */ if(offs && noff && *noff < OFFS && mynoff && pico_usingcolor()){ @@ -7543,8 +7889,8 @@ thdorig = thd = fetch_thread(idata->stream, idata->rawno); border = str + width; if(current_index_state->plus_col >= 0 && !THRD_INDX()){ - collapsed = thd && thd->next && - get_lflag(idata->stream, NULL, idata->rawno, MN_COLL); + collapsed = this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno); + collapsed = collapsed && (count_thread (ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1); hline = get_index_cache(idata->msgno); hline->plus = collapsed ? ps_global->VAR_THREAD_MORE_CHAR[0] : (thd && thd->next) @@ -7635,16 +7981,30 @@ ? "To" : (addr = fetch_cc(idata)) ? "Cc" - : NULL)) - && set_index_addr(idata, field, addr, "To: ", - width, fptr)) - break; - + : NULL))){ + char *rule_result; + rule_result = find_value("_FROM_","_REPLACE_",FOR_REPLACE, NULL,0,NULL,0, idata,V_REPLACE_RULES); + if (!rule_result) + set_index_addr(idata, field, addr, "To: ", + width, str); + else{ + sprintf(str, "%-*.*s", width, width, rule_result); + fs_give((void **)&rule_result); + } + break; + } if(ctype == iFromTo && (newsgroups = fetch_newsgroups(idata)) && *newsgroups){ - sprintf(fptr, "To: %-*.*s", width-4, width-4, - newsgroups); + char *rule_result; + rule_result = find_value("_FROM_","_REPLACE_",FOR_REPLACE, NULL,0,NULL, 0, idata, V_REPLACE_RULES); + if (!rule_result) + sprintf(str, "To: %-*.*s", width-4, width-4, + newsgroups); + else{ + sprintf(str, "%-*.*s", width, width, rule_result); + fs_give((void **)&rule_result); + } break; } @@ -7657,8 +8017,17 @@ break; case iFrom: - set_index_addr(idata, "From", fetch_from(idata), - NULL, width, fptr); + { char *rule_result; + rule_result = find_value("_FROM_","_REPLACE_", FOR_REPLACE, "_TRIM_", FOR_TRIM, "_REXTRIM_", FOR_TRIM, idata, V_REPLACE_RULES); + if (!rule_result) + set_index_addr(idata, "From", fetch_from(idata), + NULL, width, str); + else{ + sprintf(str, "%-*.*s", width, width, rule_result); + fs_give((void **)&rule_result); + } + } + break; case iAddress: @@ -7741,6 +8110,24 @@ return(1); } +void +insert_pattern_in_string(buf, last, maxbuf) + char *buf; + char *last; + int maxbuf; +{ + if (last[0] != '\0'){ + int lenlast, lenbuf; + lenlast = strlen(last); + lenbuf = buf[0] == '\0' ? 0 : strlen(buf); + if (lenlast + lenbuf <= maxbuf){ + if (buf[0] == '\0') + strcpy(buf, last); + else + strcat(buf, last); + } + } +} /*---------------------------------------------------------------------- @@ -7765,9 +8152,11 @@ long i, sorted_msg, selected = 0L; char prompt[MAX_SEARCH+50], new_string[MAX_SEARCH+1]; HelpType help; + static char last_search_pat[MAX_SEARCH+1] = {'\0'}; static char search_string[MAX_SEARCH+1] = { '\0' }; HLINE_S *hl; static ESCKEY_S header_search_key[] = { {0, 0, NULL, NULL }, + {ctrl('N'), 9, "^N","Ins Pat"}, {ctrl('Y'), 10, "^Y", "First Msg"}, {ctrl('V'), 11, "^V", "Last Msg"}, {-1, 0, NULL, NULL} }; @@ -7814,6 +8203,8 @@ : h_os_index_whereis; continue; } + else if(rc == 9) + insert_pattern_in_string(new_string, last_search_pat, MAX_SEARCH); else if(rc == 10){ q_status_message(SM_ORDER, 0, 3, "Searched to First Message."); if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){ @@ -7851,7 +8242,7 @@ break; } - if(rc != 4) /* redraw */ + if(rc != 4 && rc != 9) /* redraw */ break; /* redraw */ } @@ -7868,6 +8259,9 @@ strncpy(search_string, new_string, sizeof(search_string)); search_string[sizeof(search_string)-1] = '\0'; + strncpy(last_search_pat, new_string, sizeof(last_search_pat)); + last_search_pat[sizeof(last_search_pat)-1] = '\0'; + #ifndef DOS intr_handling_on(); #endif @@ -8054,6 +8448,43 @@ : ((mdiff = *mess_a - *mess_b) ? ((mdiff > 0) ? 1 : -1) : 0)); } +SortOrder translate(order, is_rev) +char *order; +int is_rev; +{ + int rev = 0; + if (!strncmp(order,"tHread", 6) + || (rev = !strncmp(order,"Reverse tHread", 14))) + return is_rev || rev ? SortThread : EndofList; + if (!strncmp(order,"OrderedSubj", 11) + || (rev = !strncmp(order,"Reverse OrderedSubj", 19))) + return is_rev || rev ? SortSubject2 : EndofList; + if (!strncmp(order,"Subject", 7) + || (rev = !strncmp(order,"Reverse SortSubject", 15))) + return is_rev || rev ? SortSubject : EndofList; + if (!strncmp(order,"Arrival", 7) + || (rev = !strncmp(order,"Reverse Arrival", 15))) + return is_rev || rev ? SortArrival : EndofList; + if (!strncmp(order,"From", 4) + || (rev = !strncmp(order,"Reverse From", 12))) + return is_rev || rev ? SortFrom : EndofList; + if (!strncmp(order,"To", 2) + || (rev = !strncmp(order,"Reverse To", 10))) + return is_rev || rev ? SortTo : EndofList; + if (!strncmp(order,"Cc", 2) + || (rev = !strncmp(order,"Reverse Cc", 10))) + return is_rev || rev ? SortCc : EndofList; + if (!strncmp(order,"Date", 4) + || (rev = !strncmp(order,"Reverse Date", 12))) + return is_rev || rev ? SortDate : EndofList; + if (!strncmp(order,"siZe", 4) + || (rev = !strncmp(order,"Reverse siZe", 12))) + return is_rev || rev ? SortSize : EndofList; + if (!strncmp(order,"scorE", 5) + || (rev = !strncmp(order,"Reverse scorE", 13))) + return is_rev || rev ? SortScore : EndofList; + return EndofList; +} /*---------------------------------------------------------------------- Sort the current folder into the order set in the msgmap @@ -8069,12 +8500,13 @@ causes the sort to happen if it is still needed. ----*/ void -sort_folder(stream, msgmap, new_sort, new_rev, flags) +sort_folder(stream, msgmap, new_sort, new_rev, flags, first) MAILSTREAM *stream; MSGNO_S *msgmap; SortOrder new_sort; int new_rev; unsigned flags; + int first; { long raw_current, i, j; unsigned long *sort = NULL; @@ -8084,6 +8516,15 @@ int current_rev; MESSAGECACHE *mc; + if (first){ + if (new_sort == SortThread) + find_msgmap(stream, msgmap, flags, + ps_global->thread_cur_sort, new_rev); + else + sort_folder(stream, msgmap, new_sort, new_rev, flags, 0); + return; + } + dprint(2, (debugfile, "Sorting by %s%s\n", sort_name(new_sort), new_rev ? "/reverse" : "")); @@ -8446,6 +8887,7 @@ thrd = fetch_head_thread(stream); for(j = msgmap->max_thrdno; thrd && j >= 1L; j--){ + unsigned long branch; thrd->thrdno = j; if(thrd->nextthd) @@ -8512,7 +8954,7 @@ MESSAGECACHE *mc; PINELT_S *peltp; - if(!(stream && stream->spare)) + if(!(stream && stream->spare) || !erase_thread_info) return; ps_global->view_skipped_index = 0; @@ -8574,7 +9016,7 @@ unsigned long msgno, rawno, set_in_thread, in_thread; int bail, this_is_vis; int un_view_thread = 0; - long raw_current; + long raw_current, branch; dprint(2, (debugfile, "sort_thread_callback\n")); @@ -8593,9 +9035,11 @@ * way. If the dummy node is at the top-level, then its children are * promoted to the top-level as separate threads. */ - collapsed_tree = collapse_threadnode_tree(tree); - (void) sort_thread_flatten(collapsed_tree, stream, thrd_flatten_array, - NULL, THD_TOP); + collapsed_tree = F_ON(F_ENHANCED_THREAD, ps_global) ? + copy_tree(tree) : collapse_threadnode_tree(tree); + (void) sort_thread_flatten(collapsed_tree, + stream, thrd_flatten_array, + NULL, THD_TOP, 0, 1L, 0L); mail_free_threadnode(&collapsed_tree); if(any_lflagged(g_sort.msgmap, MN_HIDE)) @@ -8760,12 +9204,14 @@ else{ thrd = fetch_head_thread(stream); while(thrd){ + unsigned long raw = thrd->rawno; + unsigned long top = top_thread(stream, raw); /* * The top-level threads aren't hidden by collapse. */ msgno = mn_raw2m(g_sort.msgmap, thrd->rawno); - if(msgno) - set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); + if(msgno && !get_lflag(stream, NULL,thrd->rawno, MN_COLL)) + set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0); if(thrd->next){ PINETHRD_S *nthrd; @@ -8779,9 +9225,10 @@ MN_COLL)); } - if(thrd->nextthd) + while (thrd && top_thread(stream, thrd->rawno) == top + && thrd->nextthd) thrd = fetch_thread(stream, thrd->nextthd); - else + if (!(thrd && thrd->nextthd)) thrd = NULL; } } @@ -8802,10 +9249,17 @@ PINETHRD_S *not_this_thread; { PINETHRD_S *thrd = NULL, *nthrd; - unsigned long msgno; + unsigned long msgno, branch; dprint(9, (debugfile, "collapse_threads\n")); +/* if(F_ON(F_ENHANCED_THREAD, ps_global)){*/ + kolapse_thread(ps_global, stream, msgmap, '[', 0); + if (not_this_thread) + expand_thread(ps_global, stream, msgmap, 0); + return; +/* }*/ + thrd = fetch_head_thread(stream); while(thrd){ if(thrd != not_this_thread){ @@ -8840,7 +9294,7 @@ int a_parent_is_collapsed; { PINETHRD_S *nthrd, *bthrd; - unsigned long msgno; + unsigned long msgno, next, branch; if(!thrd) return; @@ -8858,8 +9312,8 @@ set_lflag(stream, msgmap, msgno, MN_CHID, 0); } - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) make_thrdflags_consistent(stream, msgmap, nthrd, a_parent_is_collapsed @@ -8868,8 +9322,8 @@ MN_COLL)); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) make_thrdflags_consistent(stream, msgmap, bthrd, a_parent_is_collapsed); @@ -8882,7 +9336,7 @@ MAILSTREAM *stream; { PINETHRD_S *thrd = NULL; - long vis = 0L; + long vis = 0L, branch; thrd = fetch_head_thread(stream); while(thrd){ @@ -8899,47 +9353,86 @@ struct pass_along * -sort_thread_flatten(node, stream, entry, thrd, flags) +sort_thread_flatten(node, stream, entry, thrd, flags, adopted, top, threadno) THREADNODE *node; MAILSTREAM *stream; struct pass_along *entry; PINETHRD_S *thrd; unsigned flags; + int adopted; + long top; + long threadno; { long n = 0L; - PINETHRD_S *newthrd = NULL; + PINETHRD_S *newthrd = NULL, *save_thread = NULL; if(node){ - if(node->num){ /* holes happen */ + if(node->num){ n = (long) (entry - thrd_flatten_array); + if (adopted == 2) + top = node->num; + for(; n > 0; n--) if(thrd_flatten_array[n].rawno == node->num) break; /* duplicate */ - if(!n) - entry->rawno = node->num; - } - - /* - * Build a richer threading structure that will help us paint - * and operate on threads and subthreads. - */ - if(!n && node->num){ - newthrd = msgno_thread_info(stream, node->num, thrd, flags); - if(newthrd){ - entry->thrd = newthrd; - entry++; - - if(node->next) + if(!n){ + entry->rawno = node->num; + newthrd = msgno_thread_info(stream, node->num, thrd, flags); + if(newthrd){ + if (adopted == 2) + threadno = newthrd->thrdno; + if (adopted){ + newthrd->toploose = top; + newthrd->thrdno = threadno; + } + entry->thrd = newthrd; + entry++; + } + adopted = adopted ? 1 : 0; + if (node->next) entry = sort_thread_flatten(node->next, stream, entry, - newthrd, THD_NEXT); - + newthrd, THD_NEXT, adopted, top, + threadno); if(node->branch) entry = sort_thread_flatten(node->branch, stream, entry, - newthrd, - (flags == THD_TOP) ? THD_TOP - : THD_BRANCH); + newthrd, + (flags == THD_TOP) ? THD_TOP: THD_BRANCH, + adopted, top, threadno); + } + } + else{ + adopted = 2; + if(node->next) + entry = sort_thread_flatten(node->next, stream, entry, + thrd, THD_TOP, adopted, top, threadno); + adopted = 0; + if(node->branch){ + if(entry){ + struct pass_along *last_entry = entry; + int i = 0; + + /* + * Next moved up to replace "tree" in the tree. + * If next has no branches, then we want to branch off + * of next. If next has branches, we want to branch off + * of the last of those branches instead. + */ + last_entry--; + while(last_entry->thrd->parent) + last_entry--; + save_thread = last_entry->thrd; + last_entry += i+1; + + last_entry = sort_thread_flatten(node->branch, stream, entry, + save_thread, + (flags == THD_TOP) ? THD_TOP: THD_BRANCH, + adopted, top, threadno); + } + else + entry = sort_thread_flatten(node->branch, stream, entry, + NULL, THD_TOP, adopted, top, threadno); } } } @@ -8947,6 +9440,26 @@ return(entry); } +/* + * Make a copy of c-client's THREAD tree + */ +THREADNODE * +copy_tree(tree) + THREADNODE *tree; +{ + THREADNODE *newtree = NULL; + + if(tree){ + newtree = mail_newthreadnode(NULL); + newtree->num = tree->num; + if(tree->next) + newtree->next = copy_tree(tree->next); + + if(tree->branch) + newtree->branch = copy_tree(tree->branch); + } + return(newtree); +} /* * Make a copy of c-client's THREAD tree while eliminating dummy nodes. @@ -10101,7 +10614,7 @@ { long j; size_t newsize = sizeof(HLINE_S) - + ((max(ps_global->ttyo->screen_cols, 80)+1) * sizeof(char)); + + ((max(ps_global->ttyo->screen_cols, 80)+1)*6*sizeof(char)); if(j = (newsize % sizeof(long))) /* alignment hack */ newsize += (sizeof(long) - (size_t)j); @@ -10179,7 +10692,7 @@ dprint(2, (debugfile, "Called get_index_cache with msgno=%ld\n", msgno)); - big_enough = sizeof(HLINE_S) + (MAX_SCREEN_COLS * sizeof(char)) + big_enough = sizeof(HLINE_S) + (MAX_SCREEN_COLS * sizeof(char) * 6) + sizeof(long); if(!dummy_to_protect_ourselves) dummy_to_protect_ourselves = (HLINE_S *) fs_get(big_enough); @@ -10708,12 +11221,13 @@ void -thread_command(state, stream, msgmap, preloadkeystroke, q_line) +thread_command(state, stream, msgmap, preloadkeystroke, q_line, display) struct pine *state; MAILSTREAM *stream; MSGNO_S *msgmap; int preloadkeystroke; int q_line; + int display; { PINETHRD_S *thrd = NULL; unsigned long rawno, save_branch; @@ -10762,7 +11276,7 @@ cancel_busy_alarm(0); (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags, - q_line); + q_line, display); /* restore the original flags */ copy_lflags(stream, msgmap, MN_STMP, MN_SLCT); @@ -10800,20 +11314,21 @@ int v; { PINETHRD_S *nthrd, *bthrd; + unsigned long next = 0L, branch = 0L; if(!(stream && thrd && msgmap)) return; set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v); - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream,thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) set_flags_for_thread(stream, msgmap, f, nthrd, v); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) set_flags_for_thread(stream, msgmap, f, bthrd, v); } @@ -10868,11 +11383,12 @@ * index. */ void -collapse_or_expand(state, stream, msgmap, msgno) +collapse_or_expand(state, stream, msgmap, msgno, display) struct pine *state; MAILSTREAM *stream; MSGNO_S *msgmap; unsigned long msgno; + int display; { int collapsed, adjust_current = 0; PINETHRD_S *thrd = NULL, *nthrd; @@ -10926,7 +11442,7 @@ if(!thrd) return; - collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next; + collapsed = this_thread_is_kolapsed(ps_global, stream, msgmap, thrd->rawno); if(collapsed){ msgno = mn_raw2m(msgmap, thrd->rawno); @@ -10944,13 +11460,13 @@ msgno = mn_raw2m(msgmap, thrd->rawno); if(msgno > 0L && msgno <= mn_get_total(msgmap)){ set_lflag(stream, msgmap, msgno, MN_COLL, 1); - if(nthrd = fetch_thread(stream, thrd->next)) + if((thrd->next) && (nthrd = fetch_thread(stream, thrd->next))) set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID); clear_index_cache_ent(msgno); } } - else + else if (display) q_status_message(SM_ORDER, 0, 1, "No thread to collapse or expand on this line"); @@ -11032,18 +11548,19 @@ unsigned long rawno, count = 0; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; + unsigned long next = 0L, branch = 0L; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) count += count_flags_in_thread(stream, nthrd, flags); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) count += count_flags_in_thread(stream, bthrd, flags); } @@ -11074,20 +11591,20 @@ MSGNO_S *msgmap; int flags; /* flag to count */ { - unsigned long rawno, count = 0; + unsigned long rawno, count = 0, next, branch; PINETHRD_S *nthrd, *bthrd; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) count += count_lflags_in_thread(stream, nthrd, msgmap, flags); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) count += count_lflags_in_thread(stream, bthrd, msgmap,flags); } @@ -11109,7 +11626,7 @@ MAILSTREAM *stream; PINETHRD_S *thrd; { - unsigned long rawno, count = 0; + unsigned long rawno, count = 0, next, branch; PINETHRD_S *nthrd, *bthrd; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) @@ -11118,14 +11635,14 @@ if(get_lflag(stream, NULL, thrd->rawno, MN_HIDE) == 0) return 1; - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd && thread_has_some_visible(stream, nthrd)) return 1; } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd && thread_has_some_visible(stream, bthrd)) return 1; } @@ -11200,20 +11717,21 @@ MSGNO_S *msgmap; { int count = 0; + long next, branch; PINETHRD_S *nthrd, *bthrd; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return count; - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) count += mark_msgs_in_thread(stream, nthrd, msgmap); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream, thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) count += mark_msgs_in_thread(stream, bthrd, msgmap); } @@ -11247,7 +11765,7 @@ int flags; /* flags to set or clear */ int v; /* set or clear? */ { - unsigned long rawno, msgno; + unsigned long rawno, msgno, next, branch; PINETHRD_S *nthrd, *bthrd; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) @@ -11273,14 +11791,14 @@ && v == 1 && get_index_cache(msgno)->line[0]) clear_index_cache_ent(msgno); - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream, thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) set_thread_lflags(stream, nthrd, msgmap, flags, v); } - if(thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(branch = get_branch(stream,thrd)){ + bthrd = fetch_thread(stream, branch); if(bthrd) set_thread_lflags(stream, bthrd, msgmap, flags, v); } @@ -11367,19 +11885,20 @@ { char to_us = ' '; PINETHRD_S *nthrd, *bthrd; + unsigned long next = 0L, branch = 0L; MESSAGECACHE *mc; if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs) return to_us; - if(thrd->next){ - nthrd = fetch_thread(stream, thrd->next); + if(next = get_next(stream,thrd)){ + nthrd = fetch_thread(stream, next); if(nthrd) to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged); } - if(to_us == ' ' && thrd->branch){ - bthrd = fetch_thread(stream, thrd->branch); + if(to_us == ' ' && (branch = get_branch(stream, thrd))){ + bthrd = fetch_thread(stream, branch); if(bthrd) to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged); } @@ -11414,7 +11933,7 @@ break; } - if(to_us != '+' && resent_to_us(&idata)) + if(to_us != '+' && !idata.bogus && resent_to_us(&idata)) to_us = '+'; if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global)) @@ -11450,7 +11969,7 @@ int flags; /* flags to set or clear */ { int hiding; - unsigned long rawno, msgno; + unsigned long rawno, msgno, next, branch; PINETHRD_S *nthrd, *bthrd; hiding = (flags == MN_CHID) && v; @@ -11462,7 +11981,8 @@ set_lflag(stream, msgmap, msgno, flags, v); - if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ + if(thrd->next + && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){ nthrd = fetch_thread(stream, thrd->next); if(nthrd) set_thread_subtree(stream, nthrd, msgmap, v, flags); @@ -11506,8 +12026,8 @@ if(rawno) thrd = fetch_thread(stream, rawno); - if(thrd && thrd->top && thrd->top != thrd->rawno) - thrd = fetch_thread(stream, thrd->top); + if(thrd && thrd->top && top_thread(stream,thrd->top) != thrd->rawno) + thrd = fetch_thread(stream, top_thread(stream,thrd->top)); if(!thrd) return 0; @@ -11523,8 +12043,9 @@ set_thread_lflags(stream, thrd, msgmap, MN_CHID2, 1); } - if(current_index_state) - msgmap->top_after_thrd = current_index_state->msg_at_top; +/* if(current_index_state) + msgmap->top_after_thrd = current_index_state->msg_at_top;*/ + msgmap->top_after_thrd = mn_raw2m(msgmap, thrd->top); /* * If this is one of those wacky users who like to sort backwards @@ -11577,7 +12098,7 @@ thrd = fetch_thread(stream, rawno); if(thrd && thrd->top) - topthrd = fetch_thread(stream, thrd->top); + topthrd = fetch_thread(stream, top_thread(stream,thrd->top)); if(!topthrd) return 0; @@ -11714,3 +12235,338 @@ } } } + +unsigned long +get_branch(stream,thrd) + MAILSTREAM *stream; + PINETHRD_S *thrd; +{ + PINETHRD_S *nthrd = NULL; + unsigned long top; + + if (thrd->toploose && thrd->nextthd) + nthrd = fetch_thread(stream, thrd->nextthd); + if (!nthrd) + return thrd->branch; + top = top_thread(stream, thrd->rawno); + return thrd->branch + ? thrd->branch + : (F_ON(F_ENHANCED_THREAD, ps_global) + ? (top == top_thread(stream, nthrd->rawno) ? thrd->nextthd : 0L) + : 0L); +} + +unsigned long +get_next(stream,thrd) + MAILSTREAM *stream; + PINETHRD_S *thrd; +{ + return thrd->next; +} + +long +get_length_branch(stream, rawno) +MAILSTREAM *stream; +long rawno; +{ + int branchp = 0, done = 0; + long top, count = 1L, raw; + PINETHRD_S *thrd, *pthrd = NULL, *nthrd; + + thrd = fetch_thread(stream, rawno); + + if (!thrd) + return -1L; + + top = thrd->top; + + if (thrd->parent) + pthrd = fetch_thread(stream, thrd->parent); + + if (thrd->rawno == top) + branchp++; + + if (!branchp && !pthrd){ /* what!!?? */ + raw = top; + while (!done){ + pthrd = fetch_thread(stream, raw); + if ((pthrd->next == rawno) || (pthrd->branch == rawno)) + done++; + else{ + if (pthrd->next) + raw = pthrd->next; + else if (pthrd->branch) + raw = pthrd->branch; + } + } + } + + if (pthrd && pthrd->next == thrd->rawno && thrd->branch) + branchp++; + + if (pthrd && pthrd->next && pthrd->next != thrd->rawno){ + nthrd = fetch_thread(stream, pthrd->next); + while (nthrd && nthrd->branch && nthrd->branch != thrd->rawno) + nthrd = fetch_thread(stream, nthrd->branch); + if(nthrd && nthrd->branch && nthrd->branch == thrd->rawno) + branchp++; + } + + if(branchp){ + int entry = 0; + while(thrd && thrd->next){ + entry = 1; + count++; + thrd = fetch_thread(stream, thrd->next); + if (thrd->branch) + break; + } + if (entry && thrd->branch) + count--; + } + return branchp ? (count ? count : 1L) : 0L; +} + +void +find_msgmap(stream, msgmap, flags, ordersort, is_rev) +MAILSTREAM *stream; +MSGNO_S *msgmap; +int flags; +SortOrder ordersort; +unsigned is_rev; +{ + int we_cancel; + long *old_arrival,*new_arrival; + long init_thread, end_thread, current; + long k = 1L, j, last_thread = 0L; + long i, tmsg, ntmsg, nthreads; + int nflags = (SRT_VRB | SRT_MAN); + char sort_msg[MAX_SCREEN_COLS+1] = {'\0'}; + PINETHRD_S *thrd, *tthrd, *nthrd; + + erase_thread_info = 0; + current = mn_m2raw(msgmap, mn_get_cur(msgmap)); + + /* now sort by arrival */ + sort_folder(stream, msgmap, ordersort, 0, nflags, 0); + + tmsg = mn_get_total(msgmap) + 1; + + if (tmsg <= 1) + return; + + old_arrival = (long *) fs_get(tmsg * sizeof(long)); + memset(old_arrival, 0, tmsg*sizeof(long)); + for (i= 1L;(i <= mn_get_total(msgmap)) && (old_arrival[i] = msgmap->sort[i]); i++); + + /* now sort by thread */ + sort_folder(stream, msgmap, SortThread, 0, nflags, 0); + ntmsg = mn_get_total(msgmap) + 1; + + if (tmsg != ntmsg){ /* oh oh, something happened,we better try again */ + fs_give((void **)&old_arrival); + find_msgmap(stream, msgmap, flags, ordersort, is_rev); + return; + } + + /* reconstruct the msgmap */ + + new_arrival = (long *) fs_get(tmsg * sizeof(long)); + memset(new_arrival, 0, tmsg*sizeof(long)); + i = mn_get_total(msgmap); + while (new_arrival[1] == 0){ /* think of this as (tmsg > 0) */ + int done = 0; + long n = mn_get_total(msgmap); + + init_thread = top_thread(stream, old_arrival[i]); + thrd = fetch_thread(stream, init_thread); + while ((new_arrival[n] != 0) && !done){ /* compare raw numbers */ + done = (new_arrival[n] == init_thread); + n--; + } + if (!done){ + k = 1L; + mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); + if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; + else + j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); + end_thread = mn_raw2m(msgmap, init_thread) + j; + while (k <= j){ + new_arrival[tmsg - k] = msgmap->sort[end_thread - k]; + k++; + } + tmsg -= j; + } + i--; + } + relink_threads(stream, msgmap, new_arrival); + for (i = 1; (i <= mn_get_total(msgmap)) + && (msgmap->sort[i] = new_arrival[i]); i++); + msgno_reset_isort(msgmap); + + fs_give((void **)&new_arrival); + fs_give((void **)&old_arrival); + + + if(is_rev && (mn_get_total(msgmap) > 1L)){ + long *rev_sort; + long i = 1L, l = mn_get_total(msgmap); + + rev_sort = (long *) fs_get((mn_get_total(msgmap)+1L) * sizeof(long)); + memset(rev_sort, 0, (mn_get_total(msgmap)+1L)*sizeof(long)); + while (l > 0L){ + if (top_thread(stream, msgmap->sort[l]) == msgmap->sort[l]){ + long init_thread = msgmap->sort[l]; + long j, k; + + mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread)); + if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1; + else + j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread); + for (k = 0L; (k < j) && (rev_sort[i+k] = msgmap->sort[l+k]); k++); + i += j; + } + l--; + } + relink_threads(stream, msgmap, rev_sort); + for (i = 1L; i <= mn_get_total(msgmap); i++) + msgmap->sort[i] = rev_sort[i]; + msgno_reset_isort(msgmap); + fs_give((void **)&rev_sort); + } + mn_reset_cur(msgmap, first_sorted_flagged(is_rev ? F_NONE : F_SRCHBACK, + stream, mn_raw2m(msgmap, current), FSF_SKIP_CHID)); + msgmap->top = -1L; + + sp_set_unsorted_newmail(ps_global->mail_stream, 0); + + for(i = 1L; i <= ps_global->mail_stream->nmsgs; i++) + mail_elt(ps_global->mail_stream, i)->spare7 = 0; + + mn_set_sort(msgmap, SortThread); + mn_set_revsort(msgmap, is_rev); + erase_thread_info = 1; + clear_index_cache(); +} + +void +move_thread(state, stream, msgmap, direction) + struct pine *state; + MAILSTREAM *stream; + MSGNO_S *msgmap; + int direction; +{ + long new_cursor, old_cursor = mn_get_cur(msgmap); + int rv; + PINETHRD_S *thrd; + + rv = direction > 0 ? move_next_thread(state, stream, msgmap, 1): + move_prev_thread(state, stream, msgmap, 1); + if (rv > 0 && THRD_INDX_ENABLED()){ + new_cursor = mn_get_cur(msgmap); + mn_set_cur(msgmap, old_cursor); + unview_thread(state, stream, msgmap); + thrd = fetch_thread(stream,mn_m2raw(msgmap, new_cursor)); + mn_set_cur(msgmap, new_cursor); + view_thread(state, stream, msgmap, 1); + state->next_screen = SCREEN_FUN_NULL; + } +} + +void +relink_threads(stream, msgmap, new_arrival) + MAILSTREAM *stream; + MSGNO_S *msgmap; + long *new_arrival; +{ + long last_thread = 0L; + long i = 0L, j = 1L, k; + PINETHRD_S *thrd, *nthrd; + + while (j <= mn_get_total(msgmap)){ + i++; + thrd = fetch_thread(stream, new_arrival[j]); + if (!thrd) /* sort failed!, better leave from here now!!! */ + break; + thrd->prevthd = last_thread; + thrd->thrdno = i; + thrd->head = new_arrival[1]; + last_thread = thrd->rawno; + mn_set_cur(msgmap, mn_raw2m(msgmap,thrd->top)); + k = mn_get_cur(msgmap); + if (move_next_thread(ps_global, stream, msgmap, 0) <= 0) + j += mn_get_total(msgmap) + 1 - k; + else + j += mn_get_cur(msgmap) - k; + if (!thrd->toploose) + thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; + else{ + int done = 0; + while(thrd->nextthd && !done){ + thrd->thrdno = i; + thrd->head = new_arrival[1]; + if (thrd->nextthd) + nthrd = fetch_thread(stream, thrd->nextthd); + else + done++; + if(top_thread(stream, thrd->rawno) == top_thread(stream, nthrd->rawno)) + thrd = nthrd; + else + done++; + } + thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L; + last_thread = thrd->rawno; + } + } +} + + +int +find_index_rule() +{ + int found = 0; + RULE_RESULT *rule; + INDEXDATA_S idata; + ENVELOPE *local_env; + + memset(&idata, 0, sizeof(INDEXDATA_S)); + idata.stream = ps_global->mail_stream; + local_env = (ENVELOPE *)make_envelope(&idata, 0); + rule = get_result_rule(V_INDEX_RULES, FOR_RULE | FOR_INDEX, local_env); + if (local_env) + mail_free_envelope(&local_env); + if (rule){ + init_index_format(rule->result, &ps_global->index_disp_format); + found = 1; + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + return found; +} + +void +setup_threading_display_style() +{ + RULE_RESULT *rule; + NAMEVAL_S *v; + int i; + + rule = get_result_rule(V_THREAD_DISP_STYLE_RULES, + FOR_RULE | FOR_THREAD, (ENVELOPE *) NULL); + if (rule || ps_global->VAR_THREAD_DISP_STYLE){ + for(i = 0; v = thread_disp_styles(i); i++) + if(!strucmp(rule ? rule->result : ps_global->VAR_THREAD_DISP_STYLE, + rule ? (v ? v->name : "" ) : S_OR_L(v))){ + ps_global->thread_disp_style = v->value; + break; + } + if (rule){ + if (rule->result) + fs_give((void **)&rule->result); + fs_give((void **)&rule); + } + } +} diff -ru pine4.64/pine/mailpart.c pine4.64.SuSE/pine/mailpart.c --- pine4.64/pine/mailpart.c 2005-04-27 20:53:45.000000000 +0200 +++ pine4.64.SuSE/pine/mailpart.c 2006-02-14 14:45:24.000000000 +0100 @@ -815,8 +815,14 @@ /*--- get string ---*/ {int rc, found = 0; char *result = NULL, buf[64]; + static char last_pat[64] = {'\0'}; static char last[64], tmp[64]; HelpType help; + static ESCKEY_S ekey[] = { + {0, 0, "", ""}, + {ctrl('N'), 9, "^N", "Ins Pat"}, + {-1, 0, NULL, NULL}}; + ps->mangled_footer = 1; buf[0] = '\0'; @@ -829,18 +835,23 @@ int flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE; rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf), - tmp,NULL,help,&flags); + tmp,ekey,help,&flags); if(rc == 3) help = help == NO_HELP ? h_attach_index_whereis : NO_HELP; - else if(rc == 0 || rc == 1 || !buf[0]){ + else if(rc == 0 || rc == 1 || rc == 9 || !buf[0]){ if(rc == 0 && !buf[0] && last[0]) strcpy(buf, last); - - break; + if (rc == 9) + insert_pattern_in_string(buf, last_pat, 63); + else + break; } } if(rc == 0 && buf[0]){ + strncpy(last_pat, buf, sizeof(last_pat)); + last_pat[sizeof(last_pat)-1] = '\0'; + ctmp = current; while(ctmp = next_attline(ctmp)) if(srchstr(ctmp->dstring, buf)){ @@ -3463,7 +3474,7 @@ /* * For consistency, the first question is always "include text?" */ - if((include_text = reply_text_query(ps_global, 1, &prefix)) >= 0 + if((include_text = reply_text_query(ps_global, 1, NULL, &prefix)) >= 0 && reply_news_test(a->body->nested.msg->env, outgoing) > 0 && reply_harvest(ps_global, msgno, a->number, a->body->nested.msg->env, &saved_from, @@ -4113,7 +4124,8 @@ fs_give((void **) &p); } else - passed = !strucmp(test + 9, "us-ascii"); + passed = !strucmp(test + 9, + ps_global->VAR_ASSUMED_CHAR_SET ? ps_global->VAR_ASSUMED_CHAR_SET : "us-ascii"); } else dprint(1, (debugfile, diff -ru pine4.64/pine/mailview.c pine4.64.SuSE/pine/mailview.c --- pine4.64/pine/mailview.c 2005-09-20 20:26:20.000000000 +0200 +++ pine4.64.SuSE/pine/mailview.c 2006-02-14 14:45:25.000000000 +0100 @@ -75,6 +75,15 @@ int len; } SCRLFILE_S; +#include + +#define REGERROR 128 + +typedef struct IVAL { + int start; + int end; + struct IVAL *next; +} IVAL_S; /* * Struct to help write lines do display as they're decoded @@ -152,6 +161,7 @@ #define SS_CUR 2 #define SS_FREE 3 +static ACTION_S *role_chosen = NULL; /* * Def's to help page reframing based on handles @@ -182,6 +192,8 @@ #define CHARSET_DISCLAIMER_2 "display is set" #define CHARSET_DISCLAIMER_3 \ " for the \"%.40s\" character set. \015\012Some %.40scharacters may be displayed incorrectly." +#define CHARSET_DISCLAIMER_4 \ + " for the \"%.40s\" character set. \015\012Error: character set unkown or conversion not supported." #define ENCODING_DISCLAIMER \ "The following text contains the unknown encoding type \"%.20s\". \015\012Some or all of the text may be displayed incorrectly." @@ -235,12 +247,12 @@ HOMEKEY_MENU, ENDKEY_MENU, RCOMPOSE_MENU, - NULL_MENU, - NULL_MENU, - NULL_MENU, - NULL_MENU, - NULL_MENU, - NULL_MENU, + {"(","Prev Threa",{MC_PRETHREAD,1,{'('}},KS_NONE}, + {")","Next Threa",{MC_NEXTHREAD,1,{')'}},KS_NONE}, + {"^D","Delete Thr",{MC_DELTHREAD,1,{ctrl('D')}},KS_NONE}, + {"^U","Undel Thre",{MC_UNDTHREAD,1,{ctrl('U')}},KS_NONE}, + {"^T","selcT Thre",{MC_SELTHREAD,1,{ctrl('T')}},KS_NONE}, + {"^H","ChkIncFl",{MC_FORCECHECK,1,{ctrl('H')}},KS_NONE}, NULL_MENU}; INST_KEY_MENU(view_keymenu, view_keys); #define VIEW_ATT_KEY 3 @@ -268,7 +280,12 @@ {"S", "Save", {MC_SAVETEXT,1,{'s'}}, KS_SAVE}}; INST_KEY_MENU(simple_text_keymenu, simple_text_keys); - +static char *prefix; +#define NO_FLOWED 0x0000 +#define IS_FLOWED 0x0001 +#define DELETEQUO 0x0010 +#define COLORAQUO 0x0100 +#define RAWSTRING 0x1000 /* @@ -362,6 +379,16 @@ void embed_color PROTO((COLOR_PAIR *, gf_io_t)); int color_a_quote PROTO((long, char *, LT_INS_S **, void *)); int color_signature PROTO((long, char *, LT_INS_S **, void *)); +IVAL_S *copy_ival PROTO((IVAL_S *)); +IVAL_S * compute_interval PROTO((char *, char *, int)); +void remove_spaces_ival PROTO((IVAL_S **, char *)); +void interval_free PROTO((IVAL_S **)); +char * regex_pattern PROTO((char **)); +LT_INS_S ** insert_color_special_text PROTO((LT_INS_S **, char **, IVAL_S *, + int, COLOR_PAIR *)); +int any_color_in_string PROTO((char *)); +int length_color PROTO((char *, int)); +int color_this_text PROTO((long, char *, LT_INS_S **, void *)); int color_headers PROTO((long, char *, LT_INS_S **, void *)); int url_hilite_hdr PROTO((long, char *, LT_INS_S **, void *)); int url_launch PROTO((HANDLE_S *)); @@ -386,6 +413,8 @@ int pcpine_resize_scroll PROTO((void)); int pcpine_view_cursor PROTO((int, long)); #endif +int is_word PROTO((char [], int, int)); +int is_mailbox PROTO((char [], int, int)); @@ -420,6 +449,7 @@ HANDLE_S *handles = NULL; SCROLL_S scrollargs; SourceType src = CharStar; + FOLDER_S *f = NULL; dprint(1, (debugfile, "\n\n ----- MAIL VIEW -----\n")); @@ -473,6 +503,17 @@ else ps->unseen_in_view = !mc->seen; + prefix = reply_quote_str(env); + /* Make sure the prefix is not only made of spaces, so that we do not + * paint the screen incorrectly + */ + if (prefix && *prefix){ + int i; + for (i = 0; isspace((unsigned char) prefix[i]); i++); + if (i == strlen(prefix)) + fs_give((void **)&prefix); + } + #if defined(DOS) && !defined(WIN32) /* * Handle big text for DOS here. @@ -634,6 +675,8 @@ } while(ps->next_screen == SCREEN_FUN_NULL); + if (prefix && *prefix) + fs_give((void **)&prefix); if(we_cancel) cancel_busy_alarm(-1); @@ -1590,6 +1633,14 @@ if((flgs & FM_DISPLAY) && !(flgs & FM_NOCOLOR) && pico_usingcolor() + && ps_global->VAR_SPECIAL_TEXT_FORE_COLOR + && ps_global->VAR_SPECIAL_TEXT_BACK_COLOR){ + gf_link_filter(gf_line_test, gf_line_test_opt(color_this_text, NULL)); + } + + if((flgs & FM_DISPLAY) + && !(flgs & FM_NOCOLOR) + && pico_usingcolor() && ps_global->VAR_SIGNATURE_FORE_COLOR && ps_global->VAR_SIGNATURE_BACK_COLOR){ gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig)); @@ -1600,8 +1651,10 @@ && pico_usingcolor() && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR){ - gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL)); + gf_link_filter(gf_quote_test,gf_line_test_opt(color_a_quote, NULL)); } + else + gf_link_filter(gf_quote_test,gf_line_test_opt(select_quote, NULL)); wrapflags = (flgs & FM_DISPLAY) ? GFW_HANDLES : 0; if(flgs & FM_DISPLAY @@ -2109,9 +2162,14 @@ *p = '\0'; } - sprintf(p, CHARSET_DISCLAIMER_3, + if (quality == CV_NO_TRANSLATE_POSSIBLE) + sprintf(p, CHARSET_DISCLAIMER_4, + ps_global->VAR_CHAR_SET ? ps_global->VAR_CHAR_SET : "US-ASCII"); + else { + sprintf(p, CHARSET_DISCLAIMER_3, ps_global->VAR_CHAR_SET ? ps_global->VAR_CHAR_SET : "US-ASCII", (quality == CV_LOSES_SPECIAL_CHARS) ? "special " : ""); + } return(format_editorial(buf, width, pc) == NULL && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)); @@ -2691,6 +2749,8 @@ {0, 'a', "A", "editApp"}, {-1, 0, NULL, NULL}}; + if (role_chosen) + free_action(&role_chosen); if(handle->type == URL){ launch_opts[4].ch = 'u'; @@ -2801,11 +2861,41 @@ * sense if you just say View selected URL ... */ if(handle->type == URL && - !struncmp(handle->h.url.path, "mailto:", 7)) - sprintf(prompt, "Compose mail to \"%.*s%s\" ? ", - min(max(0,sc - 25), sizeof(prompt)-50), handle->h.url.path+7, - (strlen(handle->h.url.path+7) > max(0,sc-25)) ? "..." : ""); - else + !struncmp(handle->h.url.path, "mailto:", 7)){ + int rolenick = role_chosen ? strlen(role_chosen->nick) : 0; + int offset = 25 + (role_chosen ? 20 : 0); + int offset2 = max(0, sc - offset) - strlen(handle->h.url.path+7); + int offset3 = sc - strlen(handle->h.url.path+7) - rolenick - offset; + int laddress = min(max(0,sc - offset), sizeof(prompt)-50); + int lrole = rolenick; + + if (offset3 < 0){ + lrole = rolenick; + laddress = sc - offset - lrole; + offset3 = laddress - 20; /* redefine offset3 */ + if (offset3 < 0){ + laddress = 20; + lrole = sc - offset - laddress; + } + } + launch_opts[2].ch = 'r'; + launch_opts[2].rval = 'r'; + launch_opts[2].name = "R"; + launch_opts[2].label = "Set Role"; + sprintf(prompt, "Compose mail to \"%.*s%s\" %s%.*s%s%s? ", + laddress, handle->h.url.path+7, + offset2 < 0 ? "..." : "", + role_chosen ? "using role \"" : "", + role_chosen ? lrole : 0, + role_chosen ? role_chosen->nick : "", + role_chosen ? (rolenick > lrole ? "..." : "") : "", + role_chosen ? "\" " : ""); + } + else{ + launch_opts[2].ch = -2; + launch_opts[2].rval = 0; + launch_opts[2].name = NULL; + launch_opts[2].label = NULL; sprintf(prompt, "View selected %s %s%.*s%s ? ", (handle->type == URL) ? "URL" : "Attachment", (handle->type == URL) ? "<" : "", @@ -2814,6 +2904,7 @@ (handle->type == URL) ? ((strlen(handle->h.url.path) > max(0,sc-27)) ? "...>" : ">") : ""); + } switch(radio_buttons(prompt, -FOOTER_ROWS(ps_global), launch_opts, 'y', 'n', NO_HELP, RB_SEQ_SENSITIVE, @@ -2821,6 +2912,29 @@ case 'y' : return(1); + case 'r': + { + void (*prev_screen)() = ps_global->prev_screen, + (*redraw)() = ps_global->redrawer; + ps_global->redrawer = NULL; + ps_global->next_screen = SCREEN_FUN_NULL; + if(role_select_screen(ps_global, &role_chosen, 1) < 0){ + cmd_cancelled("Compose"); + ps_global->next_screen = prev_screen; + ps_global->redrawer = redraw; + if(ps_global->redrawer) + (*ps_global->redrawer)(); + return 0; + } + ps_global->next_screen = prev_screen; + ps_global->redrawer = redraw; + if(role_chosen) + role_chosen = combine_inherited_role(role_chosen); + if(ps_global->redrawer) + (*ps_global->redrawer)(); + break; + } + case 'u' : strncpy(tmp, handle->h.url.path, sizeof(tmp)-1); tmp[sizeof(tmp)-1] = '\0'; @@ -3482,6 +3596,35 @@ return(buf); } +#define is_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0) + +#define is_letter(c) (((c) >= 'a' && (c) <= 'z') || \ + ((c) >= 'A' && (c) <= 'Z')) + +#define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) + +#define single_level(c) (((c) == '>') || ((c) == '|') || ((c) == '~') || \ + ((c) == ']')) + +int +is_word (buf, i, j) +char buf[NSTRING]; +int i; +int j; +{ + return i <= j && is_letter(buf[i]) ? + (i < j ? is_word(buf,i+1,j) : 1) : 0; +} + +int +is_mailbox(buf,i,j) +char buf[NSTRING]; +int i; +int j; +{ + return i <= j && (is_letter(buf[i]) || is_digit(buf[i]) || buf[i] == '.') + ? (i < j ? is_mailbox(buf,i+1,j) : 1) : 0; +} int colorcmp(color1, color2) @@ -3504,6 +3647,73 @@ struct quote_colors *next; }; + +int +next_level_quote(buf, line, i, is_flowed) + char *buf; + char **line; + int i; + int is_flowed; +{ + int j; + + if (!single_level(buf[i])){ + if(is_mailbox(buf,i,i)){ + for (j = i; buf[j] && !isspace(buf[j]); j++); + if (is_word(buf,i,j-1) || is_mailbox(buf,i,j-1)) + j += isspace(buf[j]) ? 2 : 1; + } + else{ + switch(buf[i]){ + case ':' : + if (next(buf,i) != RPAREN) + j = i + 1; + else + j = i + 2; + break; + + case '-' : + if (next(buf,i) != '-') + j = i + 2; + else + j = i + 3; + break; + + case '+' : + case '*' : + if (next(buf,i) != ' ') + j = i + 2; + else + j = i + 3; + break; + + default : + for (j = i; buf[j] && !isspace(buf[j]) + && (!single_level(buf[i]) && !is_letter(buf[j])); j++); + j += isspace(buf[j]) ? 1 : 0; + break; + } + } + if (line && *line) + (*line) += j - i; + } + else{ + j = i+1; + if (line && *line) + (*line)++; + } + if(!is_flowed){ + if(line && *line) + for(; isspace((unsigned char)*(*line)); (*line)++); + for (i = j; isspace((unsigned char) buf[i]); i++); + } + else i = j; + if (is_flowed && i != j) + buf[i] = '\0'; + return i; +} + + int color_a_quote(linenum, line, ins, is_flowed_msg) long linenum; @@ -3511,19 +3721,25 @@ LT_INS_S **ins; void *is_flowed_msg; { - int countem = 0; + int countem = 0, i, j = 0; struct variable *vars = ps_global->vars; char *p; + char buf[NSTRING] = {'\0'}; struct quote_colors *colors = NULL, *cp, *next; COLOR_PAIR *col = NULL; + int code; int is_flowed = is_flowed_msg ? *((int *)is_flowed_msg) : 0; + code = (is_flowed ? IS_FLOWED : NO_FLOWED) | COLORAQUO; + select_quote(linenum, line, ins, (void *)code); + for (i = 0; tmp_20k_buf[i] && (buf[i] = tmp_20k_buf[i]); i++); + buf[i] = '\0'; + p = line; - if(!is_flowed) - while(isspace((unsigned char)*p)) - p++; + for(i = 0; isspace((unsigned char)buf[i]); i++) + p++; /*(i = is_flowed ? (buf[j] ? j : 0): j);*/ - if(p[0] == '>'){ + if(buf[i]){ struct quote_colors *c; /* @@ -3572,7 +3788,7 @@ free_color_pair(&col); cp = NULL; - while(*p == '>'){ + while(buf[i]){ cp = (cp && cp->next) ? cp->next : colors; if(countem > 0) @@ -3582,10 +3798,9 @@ countem = (countem == 1) ? 0 : countem; - p++; - if(!is_flowed) - for(; isspace((unsigned char)*p); p++) - ; + i = next_level_quote(buf, &p, i, is_flowed); + for (; isspace((unsigned char)*p); p++); + for (; isspace((unsigned char)buf[i]); i++); } if(colors){ @@ -3649,6 +3864,78 @@ return(0); } +/* This filter gives a quote string of a line. It sends its reply back to the + calling filter in the tmp_20k_buf variable. This filter replies with + the full quote string including tailing spaces if any. It is the + responsibility of the calling filter to figure out if thos spaces are + useful for that filter or if they should be removed before doing any + useful work. For example, color_a_quote does not require the trailing + spaces, but gf_wrap does. + */ +int +select_quote(linenum, line, ins, code) + long linenum; + char *line; + LT_INS_S **ins; + void *code; +{ + int i, plb; + char rqstr[NSTRING] = {'\0'}; + char buf[NSTRING] = {'\0'}; + char GLine[NSTRING] = {'\0'}; + char PLine[NSTRING] = {'\0'}; + char PPLine[NSTRING] = {'\0'}; + char NLine[NSTRING] = {'\0'}; + static char GLine1[NSTRING] = {'\0'}; + static char PLine1[NSTRING] = {'\0'}; + static char PPLine1[NSTRING] = {'\0'}; + static char GLine2[NSTRING] = {'\0'}; + static char PLine2[NSTRING] = {'\0'}; + static char PPLine2[NSTRING] = {'\0'}; + QSTRING_S *qs; + int buflen = NSTRING < SIZEOF_20KBUF ? NSTRING - 1: SIZEOF_20KBUF - 1; + int who = ((int) code) & COLORAQUO; /* may I ask who is calling? */ + int raw = ((int) code) & RAWSTRING; /* return raw string */ + + strncpy(GLine, (who ? GLine1 : GLine2), buflen); + strncpy(PLine, (who ? PLine1 : PLine2), buflen); + strncpy(PPLine, (who ? PPLine1 : PPLine2), buflen); + + if (linenum > 0) + strncpy(PLine, GLine, buflen); + + strncpy(NLine, tmp_20k_buf, buflen); + + if (line) + strncpy(GLine, line, buflen); + else + GLine[0] = '\0'; + + plb = line_isblank((prefix && *prefix ? prefix : ">"), + PLine, GLine, PPLine, NSTRING); + + qs = do_quote_match((prefix && *prefix ? prefix : ">"), + GLine, NLine, PLine, rqstr, NSTRING, plb); + if (raw) + strncpy(buf, rqstr, NSTRING); + else + flatten_qstring(qs, buf, NSTRING); + free_qs(&qs); + /* do not paint an extra level for a line with a >From string at the + * begining of it + */ + if (buf[0]){ + i = strlen(buf); + if (strlen(line) >= i + 6 && !strncmp(line+i-1,">From ", 6)) + buf[i - 1] = '\0'; + } + strncpy(tmp_20k_buf, buf, buflen); + if (linenum > 0) + strncpy((who ? PPLine1 : PPLine2), PLine, buflen); + strncpy((who ? GLine1 : GLine2), GLine, buflen); + strncpy((who ? PLine1 : PLine2), PLine, buflen); + return -1; +} /* * Paint the signature. @@ -3661,27 +3948,86 @@ void *is_in_sig; { struct variable *vars = ps_global->vars; - int *in_sig_block; + int *in_sig_block, i, j,same_qstr = 0, plb; COLOR_PAIR *col = NULL; + static char GLine[NSTRING] = {'\0'}; + static char PLine[NSTRING] = {'\0'}; + static char PPLine[NSTRING] = {'\0'}; + char NLine[NSTRING] = {'\0'}; + char rqstr[NSTRING] = {'\0'}; + static char *buf, buf2[NSTRING] = {'\0'}; + QSTRING_S *qs; + static qstrlen = 0; if(is_in_sig == NULL) return 0; + if (linenum > 0){ + for (i = 0; GLine[i] && (PLine[i] = GLine[i]); i++); + PLine[i] = '\0'; + } + + for (i = 0; tmp_20k_buf[i] && (tmp_20k_buf[i] != '\015') && (i < NSTRING) + && (i < SIZEOF_20KBUF) + && (NLine[i] = tmp_20k_buf[i]); i++); + NLine[i] = '\0'; + + for (i = 0; (i < NSTRING) && ((GLine[i] = line[i]) != 0); i++); + GLine[NSTRING - 1] = '\0'; + plb = line_isblank((prefix && *prefix ? prefix : ">"), + PLine, GLine, PPLine); + qs = do_quote_match((prefix && *prefix ? prefix : ">"), + GLine, NLine, PLine, rqstr, NSTRING, plb); + if(linenum > 0) + strncpy(PPLine, PLine, NSTRING); + strncpy(buf2, rqstr, NSTRING); + i = buf2 && buf2[0] ? strlen(buf2) : 0; + free_qs(&qs); + + /* determine if buf and buf2 are the same quote string */ + if (!struncmp(buf, buf2, qstrlen)){ + for (j = qstrlen; buf2[j] && isspace((unsigned char)buf2[j]); j++); + if (!buf2[j] || buf2[j] == '|' || (buf2[j] == '*' && buf2[j+1] != '>')) + same_qstr++; + } + in_sig_block = (int *) is_in_sig; - - if(!strcmp(line, SIGDASHES)) - *in_sig_block = START_SIG_BLOCK; - else if(*line == '\0') + + if (*in_sig_block != OUT_SIG_BLOCK){ + if (line && *line && (strlen(line) >= qstrlen) && same_qstr) + line += qstrlen; + else if (strlen(line) < qstrlen) + line += i; + else if (!same_qstr) + *in_sig_block = OUT_SIG_BLOCK; + } + else + line += i; + + if(!strcmp(line, SIGDASHES) || !strcmp(line, "--")){ + *in_sig_block = START_SIG_BLOCK; + buf = (char *) fs_get((i + 1)*sizeof(char)); + buf = cpystr(buf2); + qstrlen = i; + } + else if(*line == '\0'){ /* * Suggested by Eduardo: allow for a blank line right after * the sigdashes. */ *in_sig_block = (*in_sig_block == START_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + } else - *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK) + *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + if (*in_sig_block == OUT_SIG_BLOCK){ + qstrlen = 0; /* reset back in case there's another paragraph */ + if (buf) + fs_give((void **)&buf); + } + if(*in_sig_block != OUT_SIG_BLOCK && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR, @@ -3752,6 +4098,247 @@ return 0; } +IVAL_S * +copy_ival(ival) + IVAL_S *ival; +{ + IVAL_S *cp; + + if (!ival) + return (IVAL_S *)NULL; + + cp = (IVAL_S *) malloc (sizeof(IVAL_S)); + memset (cp, 0, sizeof(IVAL_S)); + + cp->start = ival->start; + cp->end = ival->end; + cp->next = copy_ival(ival->next); + return cp; +} + +void +interval_free(ival) + IVAL_S **ival; +{ + if (!(*ival)) + return; + + if ((*ival)->next) + interval_free(&((*ival)->next)); + + (*ival)->next = (IVAL_S *) NULL; + + free((void *)(*ival)); + *ival = (IVAL_S *) NULL; +} + +IVAL_S * +compute_interval (string, pattern, endm) + char *string; + char *pattern; + int endm; +{ + IVAL_S *ival = NULL, *nextival= NULL; + int error, sizerror; + regex_t preg; + regmatch_t pmatch; + + if (error = regcomp(&preg, pattern, REG_EXTENDED)){ +/* char message[REGERROR]; + sizerror = regerror(error, &preg, message, REGERROR); + printf("%s\n", message);*/ + regfree(&preg); + return (IVAL_S *) NULL; + } + else{ + if (((error = regexec(&preg, string, 1, &pmatch, 0)) != REG_NOMATCH) + && !error){ + ival = (IVAL_S *) malloc(sizeof(IVAL_S)); + memset (ival, 0, sizeof(IVAL_S)); + ival->start = endm + pmatch.rm_so; + ival->end = endm + pmatch.rm_eo; + nextival = compute_interval(string+pmatch.rm_so+1, pattern, ival->start+1); + if (nextival){ + if (nextival->start <= ival->end){ + ival->end = nextival->end; + ival->next = copy_ival(nextival->next); + interval_free(&nextival); + } + else + ival->next = nextival; + } + } + } + regfree(&preg); + return ival; +} + +char * +regex_pattern(plist) + char **plist; +{ + int i = 0, j = 0, k = 0, n = 0, len = 0; + char *pattern = NULL; + + if(plist && plist[0] && plist[0][0]){ + for (i = 0; plist[i] && plist[i][0]; i++) + len += strlen(plist[i]) + 1; + pattern = (char *) fs_get(len * sizeof(char)); + for (j = 0; j < i; j++){ + for(k = 0; plist[j][k]; k++) + pattern[n++] = plist[j][k]; + pattern[n++] = (j == i - 1) ? '\0' : '|'; + } + } + return pattern; +} + +LT_INS_S ** +insert_color_special_text(ins, p, ival, last_end, col) + LT_INS_S **ins; + char **p; + IVAL_S *ival; + int last_end; + COLOR_PAIR *col; +{ + struct variable *vars = ps_global->vars; + + if (ival){ + *p += ival->start - last_end; + ins = gf_line_test_new_ins(ins, *p, color_embed(col->fg, col->bg), + (2 * RGBLEN) + 4); + *p += ival->end - ival->start; + ins = gf_line_test_new_ins(ins, *p, color_embed(VAR_NORM_FORE_COLOR, + VAR_NORM_BACK_COLOR), (2 * RGBLEN) + 4); + ins = insert_color_special_text(ins, p, ival->next, ival->end, col); + } + return ins; +} + +int +length_color(p, begin_color) + char *p; + int begin_color; +{ + int len = 0, done = begin_color ? 0 : -1; + char *orig = p; + + while (*p && done <= 0){ + switch(*p++){ + case TAG_HANDLE : + p += *p + 1; + done++; + break; + + case TAG_FGCOLOR : + case TAG_BGCOLOR : + p += RGBLEN; + if (!begin_color) + done++; + break; + + default : + break; + } + } + len = p - orig; + return len; +} + +int +any_color_in_string(p) + char *p; +{ + int rv = 0; + char *orig = p; + while (*p && !rv) + if (*p++ == TAG_EMBED) + rv = p - orig; + return rv; +} + +void +remove_spaces_ival(ivalp, p) + IVAL_S **ivalp; + char *p; +{ + IVAL_S *ival; + int i; + if (!ivalp || !*ivalp) + return; + ival = *ivalp; + for (i = 0; isspace((unsigned char) p[ival->start + i]); i++); + if (ival->start + i < ival->end) /* do not do this if match only spaces */ + ival->start += i; + else + return; + for (i = 0; isspace((unsigned char) p[ival->end - i - 1]); i++); + ival->end -= i; + if (ival->next) + remove_spaces_ival(&(ival->next), p); +} + +int +color_this_text(linenum, line, ins, local) + long linenum; + char *line; + LT_INS_S **ins; + void *local; +{ + struct variable *vars = ps_global->vars; + COLOR_PAIR *col = NULL; + char *p; + int i; + static char *pattern = NULL; + + select_quote(linenum, line, ins, NULL); + p = line + strlen(tmp_20k_buf); + + if(VAR_SPECIAL_TEXT_FORE_COLOR && VAR_SPECIAL_TEXT_BACK_COLOR + && (col = new_color_pair(VAR_SPECIAL_TEXT_FORE_COLOR, + VAR_SPECIAL_TEXT_BACK_COLOR)) + && !pico_is_good_colorpair(col)) + free_color_pair(&col); + + if (linenum == 0){ + if (pattern) + fs_give((void **)&pattern); + pattern = regex_pattern(ps_global->VAR_SPECIAL_TEXT); + } + + if(pattern && col){ + IVAL_S *ival; + int done = 0, begin_color = 0; + + while (!done){ + if (i = any_color_in_string(p)){ + begin_color = (begin_color + 1) % 2; + if (begin_color){ + p[i - 1] = '\0'; + ival = compute_interval(p, pattern, 0); + remove_spaces_ival(&ival, p); + p[i - 1] = TAG_EMBED; + ins = insert_color_special_text(ins, &p, ival, 0, col); + } + for (;*p++ != TAG_EMBED; ); + p += length_color(p, begin_color); + } + else{ + ival = compute_interval(p, pattern, 0); + remove_spaces_ival(&ival, p); + ins = insert_color_special_text(ins, &p, ival, 0, col); + done++; + } + interval_free(&ival); + if (!*p) + done++; + } + free_color_pair(&col); + } + + return 0; +} + /* * Replace quotes of nonflowed messages. This needs to happen @@ -3844,7 +4431,7 @@ { DELQ_S *dq; char *lp; - int i, lines, not_a_quote = 0; + int i, lines, not_a_quote = 0, code, feedback; size_t len; dq = (DELQ_S *) local; @@ -3854,6 +4441,7 @@ if(dq->lines > 0 || dq->lines == Q_DEL_ALL){ lines = (dq->lines == Q_DEL_ALL) ? 0 : dq->lines; + feedback = (dq->lines == Q_DEL_ALL) ? 0 : 1; /* * First determine if this line is part of a quote. @@ -3864,6 +4452,9 @@ for(i = dq->indent_length; i > 0 && !not_a_quote && *lp; i--) if(*lp++ != SPACE) not_a_quote++; + + while(isspace((unsigned char) *lp)) + lp++; /* skip over leading tags */ while(!not_a_quote @@ -3903,17 +4494,16 @@ } } - /* skip over whitespace */ - if(!dq->is_flowed) - while(isspace((unsigned char) *lp)) - lp++; - - /* check first character to see if it is a quote */ - if(!not_a_quote && *lp != '>') - not_a_quote++; + code = (dq->is_flowed ? IS_FLOWED : NO_FLOWED) | DELETEQUO; + len = lp - line; + if(strlen(tmp_20k_buf) > len) + strcpy(tmp_20k_buf, tmp_20k_buf+len); + select_quote(linenum, lp, ins, (void *) code); + if (!not_a_quote && !tmp_20k_buf[0]) + not_a_quote++; if(not_a_quote){ - if(dq->in_quote > lines+1){ + if(dq->in_quote > lines+1 && feedback){ char tmp[500]; /* @@ -5018,7 +5514,7 @@ fs_give((void **) &urlp); rflags = ROLE_COMPOSE; - if(nonempty_patterns(rflags, &dummy)){ + if(!(role = copy_action(role_chosen)) && nonempty_patterns(rflags, &dummy)){ role = set_role_from_msg(ps_global, rflags, -1L, NULL); if(confirm_role(rflags, &role)) role = combine_inherited_role(role); @@ -5094,6 +5590,7 @@ free_redraft_pos(&redraft_pos); free_action(&role); + free_action(&role_chosen); return(rv); } @@ -5666,7 +6163,7 @@ char *err, *charset; int filtcnt = 0, error_found = 0, column, wrapit; int is_in_sig = OUT_SIG_BLOCK; - int is_flowed_msg = 0; + int is_flowed_msg = 0, add_me = 1; int is_delsp_yes = 0; int filt_only_c0 = 0; char *parmval; @@ -5687,7 +6184,8 @@ && !strucmp(att->body->subtype, "plain") && (parmval = rfc2231_get_param(att->body->parameter, "format", NULL, NULL))){ - if(!strucmp(parmval, "flowed")) + if(!strucmp(parmval, "flowed") && + F_OFF(F_QUELL_DISPLAYING_FLOWED_TEXT, ps_global)) is_flowed_msg = 1; fs_give((void **) &parmval); @@ -5774,6 +6272,15 @@ filters[filtcnt++].data = gf_line_test_opt(url_hilite, handlesp); } + if((flags & FM_DISPLAY) + && !(flags & FM_NOCOLOR) + && pico_usingcolor() + && VAR_SPECIAL_TEXT_FORE_COLOR + && VAR_SPECIAL_TEXT_BACK_COLOR){ + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(color_this_text, NULL); + } + /* * First, paint the signature. * Disclaimers noted below for coloring quotes apply here as well. @@ -5801,9 +6308,9 @@ && pico_usingcolor() && VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ - filters[filtcnt].filter = gf_line_test; - filters[filtcnt++].data = gf_line_test_opt(color_a_quote, - &is_flowed_msg); + add_me = 0; + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(color_a_quote, &is_flowed_msg); } } else if(!strucmp(att->body->subtype, "richtext")){ @@ -5826,11 +6333,7 @@ /*BUG: sniff the params for "version=2.0" ala draft-ietf-html-spec-01 */ int opts = 0; - if(flags & FM_DISPLAY){ - if(handlesp) /* pass on handles awareness */ - opts |= GFHP_HANDLES; - } - else + if(!(flags & FM_DISPLAY)) opts |= GFHP_STRIPPED; /* don't embed anything! */ wrapit = 0; /* wrap already handled! */ @@ -5873,6 +6376,12 @@ } } + if (add_me){ + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(select_quote, + (void *) RAWSTRING); + } + if(wrapit && (!(flags & FM_NOWRAP) || ((flags & FM_WRAPFLOWED) && is_flowed_msg))){ int margin = 0, wrapflags = (flags & FM_DISPLAY) ? GFW_HANDLES : 0; @@ -5920,7 +6429,7 @@ dq.saved_line = &free_this; dq.handlesp = handlesp; - filters[filtcnt].filter = gf_line_test; + filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } @@ -6446,8 +6955,20 @@ format_addr_string(s, n, sect, "Return-Path: ", e->return_path, flags, pc); - if((which & FE_NEWSGROUPS) && e->newsgroups) + if((which & FE_NEWSGROUPS) && e->newsgroups){ + int bogus = NIL; format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc); + if (!e->ngpathexists && e->message_id && + strncmp (e->message_id,"message_id,"message_id,"message_id,"message_id,"followup_to) format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc); @@ -7215,7 +7736,7 @@ register long cur_top_line, num_display_lines; int result, done, ch, cmd, found_on, found_on_col, first_view, force, scroll_lines, km_size, - cursor_row, cursor_col, km_popped; + cursor_row, cursor_col, km_popped, nm; long jn; struct key_menu *km; HANDLE_S *next_handle; @@ -7422,7 +7943,8 @@ /*============ Check for New Mail and CheckPoint ============*/ if(!sparms->quell_newmail && - new_mail(force, NM_TIMING(ch), NM_STATUS_MSG) >= 0){ + (nm = new_mail(force, NM_TIMING(ch), NM_STATUS_MSG)) >= 0){ + ps_global->force_check_now = nm > 0 ? 1 : 0; update_scroll_titlebar(cur_top_line, 1); if(ps_global->mangled_footer) draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols, @@ -7430,6 +7952,21 @@ ps_global->mangled_footer = 0; } + ps_global->in_pico = 0; + + if (!sparms->quell_newmail && !ps_global->skip_ifcheck) + new_mail_incfolder(ps_global, MC_IFAUTOCHECK); + ps_global->skip_ifcheck = 0; + if (ps_global->refresh_list){ + if(ps_global->in_fld_list && + ((ps_global->refresh_list & IF_REFRESH_STRONG) + || (ps_global->refresh_list & IF_REFRESH_WEAK + && (sparms->text.handles->h.f.context->use & CNTXT_INCMNG)))) + cmd = MC_RESIZE; + ps_global->refresh_list &= IF_REFRESH_NONE; + if(cmd == MC_RESIZE) + goto end; + } /* * If an expunge of the current message happened during the @@ -7564,6 +8101,7 @@ break; } + ps_global->force_check_now = (((cmd == MC_NONE) || (cmd == MC_FORCECHECK)) ? 1 : 0); /*============= Execute command =======================*/ switch(cmd){ @@ -8276,6 +8814,52 @@ print_to_printer(sparms); break; + case MC_NEXTHREAD: + case MC_PRETHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, + "to move to other thread")) + move_thread(ps_global, ps_global->mail_stream, ps_global->msgmap, + cmd == MC_NEXTHREAD ? 1 : -1); + done = 1; + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_DELTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to delete")) + cmd_delete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); + done = 1; + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_UNDTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to undelete")) + cmd_undelete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); + done = 1; + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; + + case MC_SELTHREAD: + if (THREADING()){ + if (any_messages(ps_global->msgmap, NULL, "to undelete")) + cmd_select_thread(ps_global, ps_global->mail_stream, ps_global->msgmap); + done = 1; + } + else + q_status_message(SM_ORDER, 0, 1, + "Command available in threaded mode only"); + break; /* ------- First handle on Line ------ */ case MC_GOTOBOL : @@ -8308,7 +8892,27 @@ break; + /*------- Check incoming folders -------*/ + case MC_FORCECHECK: + ps_global->force_check_now = 1; + if (new_mail_incfolder(ps_global,MC_FORCECHECK) && + ps_global->refresh_list){ + if(ps_global->in_fld_list && + ((ps_global->refresh_list & IF_REFRESH_STRONG) + || (ps_global->refresh_list & IF_REFRESH_WEAK + && (sparms->text.handles->h.f.context->use + & CNTXT_INCMNG)))) + cmd = MC_RESIZE; + ps_global->refresh_list &= IF_REFRESH_NONE; + if(cmd == MC_RESIZE) + goto end; + } + break; + case MC_TAB: + ps_global->skip_ifcheck++; + /* do not check for new mail in inc fldrs and fall through */ + /*------- Standard commands ------*/ default: whereis_pos.row = 0; @@ -8380,6 +8984,7 @@ } /* End of while() -- loop executing commands */ +end: ps_global->redrawer = NULL; /* next statement makes this invalid! */ zero_scroll_text(); /* very important to zero out on return!!! */ scroll_state(SS_FREE); @@ -8488,8 +9093,10 @@ char prompt[MAX_SEARCH+50], nsearch_string[MAX_SEARCH+1]; HelpType help; int rc, flags; + static char last_search_string[MAX_SEARCH+1] = { '\0' }; static char search_string[MAX_SEARCH+1] = { '\0' }; static ESCKEY_S word_search_key[] = { { 0, 0, "", "" }, + {ctrl('N'), 9, "^N", "Ins Pat"}, {ctrl('Y'), 10, "^Y", "First Line"}, {ctrl('V'), 11, "^V", "Last Line"}, {-1, 0, NULL, NULL} @@ -8511,6 +9118,9 @@ help = help == NO_HELP ? h_oe_searchview : NO_HELP; continue; } + else if(rc == 9) + insert_pattern_in_string(nsearch_string, last_search_string, + MAX_SEARCH); else if(rc == 10){ strcpy(report, "Searched to First Line."); return(-4); @@ -8520,7 +9130,7 @@ return(-5); } - if(rc != 4) + if(rc != 4 && rc != 9) break; } @@ -8532,6 +9142,9 @@ search_string[sizeof(search_string)-1] = '\0'; } + strncpy(last_search_string, nsearch_string, sizeof(last_search_string)); + last_search_string[sizeof(last_search_string)-1] = '\0'; + rc = search_scroll_text(start_line, start_col, search_string, cursor_pos, offset_in_line); return(rc); diff -ru pine4.64/pine/makefile.lnx pine4.64.SuSE/pine/makefile.lnx --- pine4.64/pine/makefile.lnx 2003-11-25 07:46:01.000000000 +0100 +++ pine4.64.SuSE/pine/makefile.lnx 2006-02-14 14:45:25.000000000 +0100 @@ -45,9 +45,9 @@ RM= rm -f LN= ln -s MAKE= make -OPTIMIZE= # -O2 +OPTIMIZE= -O2 -pipe PROFILE= # -pg -DEBUG= -g -DDEBUG -DDEBUGJOURNAL +DEBUG= -g # -DDEBUG -DDEBUGJOURNAL CCLIENTDIR= ../c-client PICODIR= ../pico diff -ru pine4.64/pine/newmail.c pine4.64.SuSE/pine/newmail.c --- pine4.64/pine/newmail.c 2005-01-14 01:43:13.000000000 +0100 +++ pine4.64.SuSE/pine/newmail.c 2006-02-14 14:45:23.000000000 +0100 @@ -48,6 +48,8 @@ #include "headers.h" +static long incoming_folders_new_mail = 0L; + /* * Internal prototypes @@ -840,6 +842,8 @@ if(subject) fs_give((void **) &subject); + + ps_global->skip_ifcheck = 0; } @@ -1095,6 +1099,11 @@ if(m && sp_flagged(m, SP_LOCKED)) sp_set_mail_since_cmd(m, 0L); } + + if (incoming_folders_new_mail > 0L){ + icon_text(NULL, IT_NEWMAIL); + incoming_folders_new_mail = 0L; + } } @@ -1299,3 +1308,296 @@ } } #endif + +#define ADD_FLD_MSG(m, F, j) \ + {\ + strcat((m),"\"");\ + strcat((m),FLDR_NAME((F)));\ + strcat((m),"\"");\ + (F)->notified = 1;\ + if (j)\ + strcat((m),", ");\ + } +#define MSG(n) (((n) + 30 > SIZEOF_20KBUF) ? message : tmp_20k_buf) +#define CODE() ((command == MC_FORCECHECK) ? 1 : ((newflds > 0) ? -1 : 1)) +#define NMVAR() ((command == MC_FORCECHECK) ? nflds : \ + newflds > 0 ? newflds : nflds) + +/* Check for new mail in incoming folders */ +int +new_mail_incfolder(state,command) + struct pine *state; + int command; +{ + char *message = NULL; + int i, j; + int checks, indx, f_indx, first_check; + int nflds = 0, tlflds = 0, newflds = 0, tflds; + int save_state, last_fld_chkd = state->last_folder_checked; + int tcp_query_timeout = state->tcp_query_timeout; + int tcp_open_timeout = 30; + int offset = F_ON(F_ENABLE_FAST_RECENT, state) ? 1 : 0; + static int index = -1; + static int check_started = 0; + time_t start_check, this_check, total_check; + static time_t now, old = 0; + FOLDER_S *f; + + if (F_OFF(F_ENABLE_INCOMING,ps_global) + || F_OFF(F_ENABLE_INCOMING_CHECK,ps_global) + || (state->inc_check_rule == IC_MAN + && command != MC_FORCECHECK) + || (state->inc_check_rule == IC_MAN_AUTO + && check_started == 0 && command != MC_FORCECHECK)) + return -1; + + if ((!state->force_check_now) || (state->checking_incfld)){ + state->force_check_now = 1; /* I'll be back, but wait a moment */ + return -1; + } + + now = time(0); + if ((old != 0) && (command != MC_FORCECHECK) && + (now - old < timeo)) + return -1; + + state->checking_incfld = 1; /* point of no return */ + check_started = 1; /* checks have already started */ + ps_global->mm_log_error = 0; /* turn off display of errors */ + ps_global->noshow_error = 1; + + if(state->VAR_TCPOPENTIMEO) + (void)SVAR_TCP_OPEN(state, tcp_open_timeout, tmp_20k_buf); + mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)state->incfld_timeout); + + save_state = ps_global->in_init_seq; + state->in_init_seq = 0; /* force output of cue during check */ + check_cue_display("+"); /* Show something to indicate delay */ + MoveCursor(state->ttyo->screen_rows -FOOTER_ROWS(state),0); + fflush(stdout); + state->tcp_query_timeout = state->incfld_timeout; + + if(state->context_current){ + MAILSTREAM *nxtstrm = NULL; + long rec, tot, fslctd; + int opstrm, updated; + CONTEXT_S *ctxt = sp_context(sp_inbox_stream()); + + /* Look for the Incoming folder collections, Normally the incoming folders + * collection is the first collection, but just to be sure, we back up to + * the beginning and go forward from there to try to find it. + */ + + if (!ctxt){ + ctxt = state->context_current; + while (1){ + if (ctxt->prev) + ctxt = ctxt->prev; + else + break; + } + while (1){ + if (ctxt->use & CNTXT_INCMNG) + break; + else + ctxt = ctxt->next; + } + } + + tflds = folder_total(FOLDERS(ctxt)); + + /* Someone removed a folder between checks among other things */ + if(index >= tflds) + index = -1; + + if(index < folder_index(state->inbox_name, ctxt, FI_FOLDER) + + offset) + index = folder_index(state->inbox_name, ctxt, FI_FOLDER) + offset; + + f_indx = index; + if(state->first_folder_checked != f_indx) + state->refresh_list |= IF_REFRESH_WEAK; + state->first_folder_checked = f_indx; + this_check = total_check = 0; + for(checks = 0; checks < tflds - offset + && (f = folder_entry(index, FOLDERS(ctxt))) + && !f->isdir; index++, checks++){ + + if(checks == 0) + first_check = 0; + + if(F_OFF(F_ENABLE_INCOMING_RECHECK,state) + && (command != MC_FORCECHECK) + && (f->last_check_time != 0) /* do a full first check */ + && total_check > state->incfld_timeout) + break; + + state->login_time = 0; /* modified in mm_login */ + start_check = time(0); + if(F_ON(F_ENABLE_INCOMING_RECHECK, state) + || (command == MC_FORCECHECK) + || (f->last_check_time == 0) /* first call? */ + || (start_check - f->last_check_time > f->last_check_length*timeo)){ + if(first_check == 0){ + first_check++; + state->first_folder_checked = index; + } + state->last_folder_checked = index; + fslctd = next_folder_check(&nxtstrm, ctxt, &rec, &tot, f, &opstrm); + this_check = time(0) - start_check - state->login_time; + f->last_check_time = start_check + this_check + state->login_time; + f->interesting = fslctd; + f->opstrm = opstrm; + f->last_check_length = this_check + 1; + state->refresh_list |= f->skipped ? IF_REFRESH_WEAK : IF_REFRESH_NONE; + f->skipped = 0; + } + else{ + fslctd = f->interesting; + opstrm = f->opstrm; + rec = f->recent; /* use the old data */ + tot = f->messages; + this_check = 0; + state->refresh_list |= f->skipped ? IF_REFRESH_NONE : IF_REFRESH_WEAK; + f->skipped = 1; + } + total_check += this_check; + + if(fslctd && !strcmp(FLDR_NAME(f), state->cur_folder) + && !state->in_fld_list) + fslctd = 0L; + + updated = (rec != f->recent || tot != f->messages); + if ((f->recent + f->messages == 0L && updated) + || (((!opstrm && updated) || (!f->notified && opstrm && updated)) + && fslctd)) + state->refresh_list |= IF_REFRESH_STRONG; + + f->messages = tot; + f->recent = rec; + + if (!offset && f->countrecent == 0L && fslctd) + fslctd = 0L; + + if (fslctd){ /* this folder contains new mail */ + state->refresh_list |= f->selected ? IF_REFRESH_NONE : IF_REFRESH_STRONG; + f->selected = 1; + tlflds += strlen(FLDR_NAME(f)) + 4; + f->new_mail = 1; + if(!f->notified){ + newflds++; + f->new_mail = (command == MC_FORCECHECK) ? 1 : -1; + } + nflds++; + } + else{ + if (f->selected) + state->refresh_list |= f->user_selected ? IF_REFRESH_NONE : IF_REFRESH_STRONG; + if (f->notified) + f->selected = f->user_selected ? 1 : 0; + f->notified = f->new_mail = 0; /* reset */ + } + if(index == tflds - 1) + index = folder_index(state->inbox_name, ctxt, FI_FOLDER) + + offset - 1; + } + + if(nxtstrm) + pine_mail_close(nxtstrm); + + if(ps_global->last_folder_checked != last_fld_chkd) + state->refresh_list |= IF_REFRESH_WEAK; + + state->mm_log_error = 1; /* turn display of errors back on */ + state->noshow_error = 0; + + if(nflds == 0){ + if (command == MC_FORCECHECK && state->VAR_INCOMING_FOLDERS_CHECK) + q_status_message(SM_ORDER, 0, 2, + "There are NO new messages in your Incoming Folders"); + } + else{ /* nflds > 0 */ + if (tlflds + 30 > SIZEOF_20KBUF) + message = (char *) fs_get((tlflds + 30)*sizeof(char)); + if(newflds > 0) + state->refresh_list |= IF_REFRESH_STRONG; + strcpy(MSG(tlflds),"New mail in folder"); + strcat(MSG(tlflds),(NMVAR() > 1) ? "s " : " "); + for(i = 0, j = 0, indx = f_indx; + j < checks && (f = folder_entry(indx, FOLDERS(ctxt))); j++, indx++){ + if(f->new_mail == CODE()){ + if(NMVAR() > 1){ + ADD_FLD_MSG(MSG(tlflds), f, (i < (NMVAR() - 2)) ? 1 : 0); + if(i == NMVAR() - 2) + strcat(MSG(tlflds)," and "); + } + else + ADD_FLD_MSG(MSG(tlflds), f, 0); + f->new_mail = 1; + if(++i == NMVAR()) + break; + } + if(indx == tflds - 1) + indx = folder_index(state->inbox_name, ctxt,FI_FOLDER)+ offset - 1; + } + if (newflds > 0 || command == MC_FORCECHECK){ + if(strlen(MSG(tlflds)) < state->ttyo->screen_cols - 2){ + if (command != MC_FORCECHECK){ + q_status_message(SM_ASYNC | SM_DING, 0, 60, MSG(tlflds)); + icon_text(MSG(tlflds), IT_NEWMAIL); + } + else + q_status_message(SM_ORDER, 0, 2, MSG(tlflds)); + } + else{ + strcpy(tmp_20k_buf, + "You have NEW mail in your Incoming Folders"); + if (command != MC_FORCECHECK){ + q_status_message(SM_ASYNC | SM_DING, 0, 60, tmp_20k_buf); + icon_text(tmp_20k_buf, IT_NEWMAIL); + } + else + q_status_message(SM_ORDER, 0, 2, tmp_20k_buf); + } + } + if (message) + fs_give((void **)&message); + } /* end of nflds > 0 */ + } + state->checking_incfld = 0; + check_cue_display(" "); /* Erase the "+" added before */ + state->in_init_seq = save_state; /* restore original value */ + MoveCursor(state->ttyo->screen_rows -FOOTER_ROWS(state),0); + incoming_folders_new_mail = nflds; + + old = time(0); + state->delay = total_check; + state->tcp_query_timeout = tcp_query_timeout; + mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)tcp_open_timeout); + + return nflds; +} + + +char * +new_mail_in_open_stream(stream, rec, tot) + MAILSTREAM *stream; + long *rec; + long *tot; +{ + long excluded; + + if((excluded = any_lflagged(sp_msgmap(stream), MN_EXLD))){ + *tot = stream->nmsgs - excluded; + if(tot) + *rec = count_flagged(stream, F_RECENT); + else + *rec = 0L; + } + else{ + *tot = stream->nmsgs; + *rec = stream->recent; + } + + return *rec ? STREAMNAME(stream) : NULL; +} diff -ru pine4.64/pine/osdep/makefile pine4.64.SuSE/pine/osdep/makefile --- pine4.64/pine/osdep/makefile 2004-04-03 01:14:27.000000000 +0200 +++ pine4.64.SuSE/pine/osdep/makefile 2006-02-14 14:45:25.000000000 +0100 @@ -20,7 +20,7 @@ all: includer $(ALL) includer: includer.c - $(CC) -o includer includer.c + $(CC) $(EXTRACFLAGS) -o includer includer.c clean: $(RM) $(ALL) includer --- pine4.64/pine/osdep/os-lnx.h 2003-05-23 18:05:21.000000000 +0200 +++ pine4.64.SuSE/pine/osdep/os-lnx.h 2006-02-14 14:45:25.000000000 +0100 @@ -214,6 +214,9 @@ ----*/ #define DF_DEFAULT_PRINTER ANSI_PRINTER +/* all recent Linux distributions come with glibc 2.x. with an excellent + * iconv implementation */ +#define HAVE_ICONV /*----- The usual sendmail configuration for sending mail on Unix ------*/ @@ -250,6 +253,11 @@ #define MAX_SCREEN_COLS (170) #define MAX_SCREEN_ROWS (200) +/*---------------------------------------------------------------------- + File name used to store the user's server/id/password triple between + session. It is rooted in the same directory as the PINERC. + ----*/ +#define PASSFILE ".pinepw" /*---------------------------------------------------------------------- Where to put the output of pine in debug mode. Files are created diff -ru pine4.64/pine/osdep/pipe pine4.64.SuSE/pine/osdep/pipe --- pine4.64/pine/osdep/pipe 2004-09-22 21:31:52.000000000 +0200 +++ pine4.64.SuSE/pine/osdep/pipe 2006-02-14 14:45:25.000000000 +0100 @@ -266,7 +266,7 @@ shellpath[sizeof(shellpath)-1] = '\0'; } - execl(shellpath, shell, command ? "-c" : 0, command, 0); + execl(shellpath, shell, command ? "-c" : NULL, command, NULL); } fprintf(stderr, "Can't exec %s\nReason: %s", diff -ru pine4.64/pine/osdep/termin.gen pine4.64.SuSE/pine/osdep/termin.gen --- pine4.64/pine/osdep/termin.gen 2004-12-01 19:56:45.000000000 +0100 +++ pine4.64.SuSE/pine/osdep/termin.gen 2006-02-14 14:45:24.000000000 +0100 @@ -6,6 +6,23 @@ int pcpine_oe_cursor PROTO((int, long)); #endif +void +fake_config_screen(tt) + struct ttyo **tt; +{ + struct ttyo *ttyo; + + ttyo = (struct ttyo *)fs_get(sizeof (struct ttyo)); + + ttyo->header_rows = 2; + ttyo->footer_rows = 3; + ttyo->screen_rows = 24; + ttyo->screen_cols = 80; + + *tt = ttyo; + +} + /* * Generic tty input routines @@ -122,14 +139,93 @@ 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 *dl; /* line on display */ + int *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; +/* + * In UTF-8 mode, decode byte sequencies and if a sequence is complete, + * insert the resulting Unicode(UCS4) value as cell value into the buffer. + */ +static int insert_byte(offset, c) +unsigned int offset, c; +{ + int *s2; + static char linsert_buf[6], linsert_buf_count = 0; + if (gmode & P_UNICODE && c & 0x80) { + if (linsert_buf_count >= sizeof(linsert_buf)) + linsert_buf_count = 0; + linsert_buf[linsert_buf_count++] = c; + c = 0; + if (linsert_buf_count > 1) + c = utf8_get_ucs(linsert_buf, linsert_buf_count); + if (!c) + return 0; + } + linsert_buf_count = 0; + for(s2 = &dline.vl[++dline.vused]; s2 - dline.vl > offset; s2--) + *s2 = *(s2-1); + dline.vl[offset] = c; + return 1; +} + +/*---------------------------------------------------------------------- + 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_UCS4(c) + register unsigned int c; +{ + if (gmode & P_UNICODE && c > 127) { + if (c & 0xf800) { + Writechar(0xe0 | (c >> 12), 0); + Writechar(0x80 | ((c >> 6) & 0x3f), 0); + } + else + Writechar(0xc0 | ((c >> 6) & 0x3f), 0); + Writechar(0x80 | (c & 0x3f), 0); + return; + } + Writechar(c, 0); +} + +/*---------------------------------------------------------------------- + ----*/ +void +UCS4vektor_to_UTF8string(c, inchars, utf8, ospace) +int *c; +size_t inchars; +unsigned char *utf8; +int ospace; +{ + for(;inchars > 0 && ospace > 3 && *c; c++) { + if (gmode & P_UNICODE && *c > 127) { + if (*c & 0xf800) { + *utf8++ = 0xe0 | (*c >> 12); + *utf8++ = 0x80 | ((*c >> 6) & 0x3f); + ospace -= 3; + } + else { + ospace -= 2; + *utf8++ = 0xc0 | ((*c >> 6) & 0x3f); + } + *utf8++ = 0x80 | (*c & 0x3f); + } + else { + *utf8++ = *c; + ospace--; + } + } + *utf8 = '\0'; +} static struct key oe_keys[] = {{"^G","Help",KS_SCREENHELP}, {"^C","Cancel",KS_NONE}, @@ -200,12 +296,13 @@ int x_base, y_base, field_len; int *flags; { - register char *s2; + register int *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; + int *kill_buffer = NULL; + size_t kb_len; char **help_text; int fkey_table[12]; struct key_menu *km; @@ -227,7 +324,7 @@ (escape_list && escape_list[0].ch != -1) ? escape_list[0].label: "")); - if(!ps_global->ttyo) + if((!ps_global->ttyo) || (ps_global->send_immediately)) return(pre_screen_config_opt_enter(string, field_len, prompt, escape_list, help, flags)); @@ -366,11 +463,14 @@ 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.dl = fs_get((size_t)dline.dlen*4 + 4); + memset((void *)dline.dl, 0, (size_t)(dline.dlen*4 + 4) * sizeof(char)); dline.row = real_y_base; dline.col = x_base + prompt_len; - dline.vl = string; + + dline.vl = fs_get((size_t)field_len*4 + 4); + memset((void *)dline.vl, 0, (size_t)(field_len*4 + 4) * sizeof(char)); + dline.vlen = --field_len; /* -1 for terminating NULL */ dline.vbase = field_pos = 0; @@ -382,12 +482,12 @@ /* 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++; + if(flags && *flags & OE_APPEND_CURRENT) + for(kb = string; (i=strlen(kb)) > 0 && field_pos < field_len;) + dline.vl[field_pos++] = utf8_get_ucs_string(&kb, i); string[field_pos] = '\0'; - dline.vused = (int)(&string[field_pos] - string); + dline.vused = field_pos; passwd = (flags && *flags & OE_PASSWD) ? 1 : 0; line_paint(field_pos, &passwd); @@ -458,7 +558,7 @@ /*--------------- KEY RIGHT ---------------*/ case ctrl('F'): case KEY_RIGHT: - if(field_pos >= field_len || string[field_pos] == '\0') + if(field_pos >= field_len || dline.vl[field_pos] == 0) goto bleep; line_paint(++field_pos, &passwd); @@ -482,13 +582,13 @@ */ /* skip thru current word */ - while(string[field_pos] - && isalnum((unsigned char) string[field_pos])) + while(dline.vl[field_pos] + && isalnum((unsigned char) dline.vl[field_pos])) field_pos++; /* skip thru current white space to next word */ - while(string[field_pos] - && !isalnum((unsigned char) string[field_pos])) + while(dline.vl[field_pos] + && !isalnum((unsigned char) dline.vl[field_pos])) field_pos++; line_paint(field_pos, &passwd); @@ -518,11 +618,11 @@ /*-------------------- Delete char --------------------*/ case ctrl('D'): case KEY_DEL: - if(field_pos >= field_len || !string[field_pos]) + if(field_pos >= field_len || !dline.vl[field_pos]) goto bleep; dline.vused--; - for(s2 = &string[field_pos]; *s2 != '\0'; s2++) + for(s2 = &dline.vl[field_pos]; *s2 != 0; s2++) *s2 = s2[1]; *s2 = '\0'; /* Copy last NULL */ @@ -538,14 +638,15 @@ 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; + if(field_pos != 0 || dline.vl[0]){ + if(passwd || !F_ON(F_DEL_FROM_DOT, ps_global)) + field_pos = 0; + kb_len = (dline.vused - field_pos)*4+4; + kill_buffer = fs_get(kb_len); + dline.vused = field_pos; - kill_buffer = cpystr(&string[field_pos = i]); - string[field_pos] = '\0'; + memcpy(kill_buffer, &dline.vl[field_pos], kb_len); + dline.vl[field_pos] = 0; line_paint(field_pos, &passwd); if(flags) /* record change if requested */ *flags |= OE_USER_MODIFIED; @@ -559,7 +660,7 @@ if(kill_buffer == NULL) goto bleep; - /* Make string so it will fit */ + /* Make string so it will fit kb = cpystr(kill_buffer); dprint(2, (debugfile, "Undelete: %d %d\n", strlen(string), field_len)); @@ -567,24 +668,24 @@ kb[field_len - strlen(string)] = '\0'; dprint(2, (debugfile, "Undelete: %d %d\n", field_len - strlen(string), - strlen(kb))); + strlen(kb))); */ - if(string[field_pos] == '\0') { + if(dline.vl[field_pos] == 0) { /*--- adding to the end of the string ----*/ - for(k = kb; *k; k++) - string[field_pos++] = *k; - - string[field_pos] = '\0'; + if ((field_len-field_pos)*4 < kb_len) + goto bleep; + memcpy(&dline.vl[field_pos], kill_buffer, kb_len); + field_pos = kb_len/4-1; + dline.vl[field_pos] = 0; } else { goto bleep; /* To lazy to do insert in middle of string now */ } - if(*kb && flags) /* record change if requested */ + if(flags) /* record change if requested */ *flags |= OE_USER_MODIFIED; - dline.vused = strlen(string); - fs_give((void **)&kb); + dline.vused = field_pos; line_paint(field_pos, &passwd); break; @@ -650,8 +751,8 @@ 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)); + fs_resize((void **)&dline.dl, (size_t)dline.dlen*4 + 4); + memset((void *)dline.dl, 0, (size_t)(dline.dlen*4 + 4)); line_paint(field_pos, &passwd); break; } @@ -748,8 +849,8 @@ } 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)); + fs_resize((void **)&dline.dl, (size_t)dline.dlen*4 + 4); + memset((void *)dline.dl, 0, (size_t)(dline.dlen*4 + 4)); line_paint(field_pos, &passwd); } fflush(stdout); @@ -785,7 +886,7 @@ break; } - if(iscntrl(ch & 0x7f)){ + if(iscntrl(ch)){ bleep: putc(BELL, stdout); continue; @@ -796,14 +897,11 @@ 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; + if (insert_byte(field_pos, ch)) { + line_paint(++field_pos, &passwd); + if(flags) /* record change if requested */ + *flags |= OE_USER_MODIFIED; + } } /*---- End of switch on char ----*/ } @@ -813,6 +911,10 @@ mswin_showcaret(0); #endif + UCS4vektor_to_UTF8string(dline.vl, dline.vused, string, field_len); + dprint(10, (debugfile, "converted: '%s'\n", string)); + + fs_give((void **)&dline.vl); fs_give((void **)&dline.dl); if(saved_original) fs_give((void **)&saved_original); @@ -857,8 +959,8 @@ int offset; /* current dot offset into line */ int *passwd; /* flag to hide display of chars */ { - register char *pfp, *pbp; - register char *vfp, *vbp; + register int *pfp, *pbp; + register int *vfp, *vbp; int extra = 0; #define DLEN (dline.vbase + dline.dlen) @@ -891,15 +993,15 @@ if(dline.vbase){ /* off screen cue left */ vfp = &dline.vl[dline.vbase+1]; pfp = &dline.dl[1]; - if(dline.dl[0] != '<'){ + if(dline.dl[0] != 60 /* '<' */ ){ MoveCursor(dline.row, dline.col); - Writechar(dline.dl[0] = '<', 0); + Writechar(dline.dl[0] = 60 /* '<' */ , 0); } } else{ vfp = dline.vl; pfp = dline.dl; - if(dline.dl[0] == '<'){ + if(dline.dl[0] == 60 /* '<' */ ){ MoveCursor(dline.row, dline.col); Writechar(dline.dl[0] = ' ', 0); } @@ -908,16 +1010,16 @@ 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] != '>'){ + if(pbp[1] != 62 /* '>' */ ){ MoveCursor(dline.row, dline.col+dline.dlen); - Writechar(pbp[1] = '>', 0); + Writechar(pbp[1] = 62 /* '>' */ , 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] == '>'){ + if(pbp[0] == 62 /* '>' */ ){ MoveCursor(dline.row, dline.col+dline.dlen); Writechar(pbp[0] = ' ', 0); } @@ -949,9 +1051,9 @@ MoveCursor(dline.row, dline.col + (int)(pfp - dline.dl)); do - Writechar((unsigned char)((vfp <= vbp && *vfp) + Writechar_UCS4(((vfp <= vbp && *vfp) ? ((*pfp = *vfp++) == TAB) ? ' ' : *pfp - : (*pfp = ' ')), 0); + : (*pfp = ' '))); while(++pfp <= pbp); } @@ -1030,6 +1132,7 @@ return(0); *ch = *ps_global->initial_cmds++; + ps_global->initial_cmds_offset++; if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){ fs_give((void **)&(ps_global->free_initial_cmds)); ps_global->initial_cmds = 0; @@ -1039,7 +1142,7 @@ } if(firsttime) { - firsttime = 0; + firsttime = ps_global->checking_incfld ? (char) 1 : 0; if(ps_global->in_init_seq) { ps_global->in_init_seq = 0; ps_global->save_in_init_seq = 0; diff -ru pine4.64/pine/osdep/termin.unx pine4.64.SuSE/pine/osdep/termin.unx --- pine4.64/pine/osdep/termin.unx 2004-08-03 23:46:57.000000000 +0200 +++ pine4.64.SuSE/pine/osdep/termin.unx 2006-02-14 14:45:25.000000000 +0100 @@ -46,66 +46,17 @@ init_tty_driver(ps) struct pine *ps; { + if(ps->send_immediately) + return 0; #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) @@ -125,7 +76,6 @@ fflush(stdout); dprint(2, (debugfile, "about to end_tty_driver\n")); - tty_chmod(ps, 0, TMD_RESET); PineRaw(0); } @@ -264,11 +214,17 @@ int time_out; { int ch, status, cc; + static int saved; /* Get input from initial-keystrokes */ if(process_config_input(&ch)) return(ch); + if (saved) { + ch = saved; + saved = 0; + return ch; + } /* * We only check for timeouts at the start of read_char, not in the * middle of escape sequences. @@ -310,6 +266,10 @@ } ch = i; + if (gmode & P_UNICODE) { + saved = 0x80 | (ch & 0x3f); + ch = 0xc0 | ((ch >> 6) & 0x3f); + } } else{ if(islower((unsigned char)ch)) /* canonicalize if alpha */ @@ -573,6 +533,9 @@ init_keyboard(use_fkeys) int use_fkeys; { + if (ps_global->send_immediately) + return; + if(use_fkeys && (!strucmp(term_name,"vt102") || !strucmp(term_name,"vt100"))) printf("\033\133\071\071\150"); @@ -591,6 +554,9 @@ end_keyboard(use_fkeys) int use_fkeys; { + if(ps_global->send_immediately) + return; + if(use_fkeys && (!strcmp(term_name, "vt102") || !strcmp(term_name, "vt100"))){ printf("\033\133\071\071\154"); diff -ru pine4.64/pine/osdep/termout.unx pine4.64.SuSE/pine/osdep/termout.unx --- pine4.64/pine/osdep/termout.unx 2004-11-30 18:54:05.000000000 +0100 +++ pine4.64.SuSE/pine/osdep/termout.unx 2006-02-14 14:45:23.000000000 +0100 @@ -160,6 +160,9 @@ void init_screen() { + if(ps_global->send_immediately) + return; + if(_termcap_init) /* init using termcap's rule */ tputs(_termcap_init, 1, outchar); @@ -267,6 +270,9 @@ { int footer_rows_was_one = 0; + if(ps_global->send_immediately) + return; + if(!panicking){ dprint(9, (debugfile, "end_screen called\n")); @@ -321,7 +327,7 @@ _line = 0; /* clear leaves us at top... */ _col = 0; - if(ps_global->in_init_seq) + if(ps_global->in_init_seq || ps_global->send_immediately) return; mark_status_unknown(); @@ -744,7 +750,8 @@ register unsigned int ch; int new_esc_len; { - static int esc_len = 0; + static int esc_len = 0, seq = 0; + static unsigned char utf_seq[7] = ""; if(ps_global->in_init_seq /* silent */ || (F_ON(F_BLANK_KEYMENU, ps_global) /* or bottom, */ @@ -753,6 +760,35 @@ && _col + 1 == ps_global->ttyo->screen_cols)) return; + /* Treat UTF-8 sequences if we are not in a special escape sequence */ + if(esc_len <= 0) { + unsigned char *chp; + char tmp; + tmp = (char)ch; + if ((chp = pine_check_utf8(&tmp, utf_seq, sizeof(utf_seq))) == NULL) { + seq = 1; /* flag that we are in a open UTF-8 sequence */ + return; /* UTF-8 sequence not complete, need next char */ + } + if (chp != (unsigned char*)&tmp) { + seq = 0; /* flag that we are not in a open UTF-8 sequence */ + _col++; + if (*chp == ' ') { + if(++_col > ps_global->ttyo->screen_cols) { + printf("\342\200\246"); /* UTF-8 points... */ + goto wrap; + } + chp++; + } + while(*chp) + putchar(*chp++); + return; + } + if (seq) { /* incomplete UTF-8 sequence */ + seq = 0; /* flag that we are not in a open UTF-8 sequence */ + putchar('?'); /* print question mark at place of sequence */ + } + } + if(ch == LINE_FEED || ch == RETURN || ch == BACKSPACE || ch == BELL || ch == TAB || ch == ESCAPE){ switch(ch){ @@ -830,7 +866,9 @@ 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) { + if(_col >= ps_global->ttyo->screen_cols) { +wrap: + dprint(9, (debugfile, "%d,%02d, wrap(%x)\n",_line,_col,ch)); _col = 0; if(_line + 1 < ps_global->ttyo->screen_rows) _line++; diff -ru pine4.64/pine/other.c pine4.64.SuSE/pine/other.c --- pine4.64/pine/other.c 2005-09-13 00:04:25.000000000 +0200 +++ pine4.64.SuSE/pine/other.c 2006-02-14 14:45:25.000000000 +0100 @@ -362,8 +362,8 @@ char *checkbox_pretty_value PROTO((struct pine *, CONF_S *)); char *color_pretty_value PROTO((struct pine *, CONF_S *)); char *radio_pretty_value PROTO((struct pine *, CONF_S *)); -char *sort_pretty_value PROTO((struct pine *, CONF_S *)); -char *generalized_sort_pretty_value PROTO((struct pine *, CONF_S *, int)); +char *sort_pretty_value PROTO((struct pine *, CONF_S *, int)); +char *generalized_sort_pretty_value PROTO((struct pine *, CONF_S *, int, int)); char *yesno_pretty_value PROTO((struct pine *, CONF_S *)); char *sigfile_pretty_value PROTO((struct pine *, CONF_S *)); void set_radio_pretty_vals PROTO((struct pine *, CONF_S **)); @@ -1608,7 +1608,7 @@ if(lv < (j = strlen(sort_name(ps->sort_types[i])))) lv = j; - decode_sort(pval, &def_sort, &def_sort_rev); + decode_sort(pval, &def_sort, &def_sort_rev, 0); for(j = 0; j < 2; j++){ for(i = 0; ps->sort_types[i] != EndofList; i++){ @@ -1623,6 +1623,56 @@ } } } + else if(vtmp == &ps->vars[V_THREAD_SORT_KEY]){ /* radio case */ + SortOrder thread_def_sort; + int thread_def_sort_rev; + + ctmpa->flags |= CF_NOSELECT; + ctmpa->keymenu = &config_radiobutton_keymenu; + ctmpa->tool = NULL; + + /* put a nice delimiter before list */ + new_confline(&ctmpa)->var = NULL; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_radiobutton_keymenu; + ctmpa->help = NO_HELP; + ctmpa->tool = radiobutton_tool; + ctmpa->valoffset = 12; + ctmpa->flags |= CF_NOSELECT; + ctmpa->value = cpystr("Set Thread Sort Options"); + + new_confline(&ctmpa)->var = NULL; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_radiobutton_keymenu; + ctmpa->help = NO_HELP; + ctmpa->tool = radiobutton_tool; + ctmpa->valoffset = 12; + ctmpa->flags |= CF_NOSELECT; + ctmpa->value = cpystr("--- ----------------------"); + + /* find longest value's name */ + for(lv = 0, i = 0; ps->sort_types[i] != EndofList; i++) + if(lv < (j = strlen(sort_name(ps->sort_types[i])))) + lv = j; + + decode_sort(pval, &thread_def_sort, &thread_def_sort_rev, 1); + + for(j = 0; j < 2; j++){ + for(i = 0; ps->sort_types[i] != EndofList; i++){ + if (ps->sort_types[i] == SortArrival + || ps->sort_types[i] == SortThread){ + new_confline(&ctmpa)->var = vtmp; + ctmpa->varnamep = ctmpb; + ctmpa->keymenu = &config_radiobutton_keymenu; + ctmpa->help = config_help(vtmp - ps->vars, 0); + ctmpa->tool = radiobutton_tool; + ctmpa->valoffset = 12; + ctmpa->varmem = i + (j * EndofList); + ctmpa->value = pretty_value(ps, ctmpa); + } + } + } + } else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */ ctmpa->keymenu = &config_yesno_keymenu; ctmpa->tool = yesno_tool; @@ -1674,6 +1724,7 @@ || vtmp == &ps->vars[V_TCPREADWARNTIMEO] || vtmp == &ps->vars[V_TCPWRITEWARNTIMEO] || vtmp == &ps->vars[V_TCPQUERYTIMEO] + || vtmp == &ps->vars[V_INCFLDTIMEO] || vtmp == &ps->vars[V_RSHOPENTIMEO] || vtmp == &ps->vars[V_SSHOPENTIMEO] || vtmp == &ps->vars[V_USERINPUTTIMEO] @@ -1779,6 +1830,15 @@ } } + pval = PVAL(&ps->vars[V_THREAD_SORT_KEY], ew); + if(vsave[V_THREAD_SORT_KEY].saved_user_val.p && pval + && strcmp(vsave[V_THREAD_SORT_KEY].saved_user_val.p, pval)){ + if(!mn_get_mansort(ps_global->msgmap)){ + clear_index_cache(); + reset_sort_order(SRT_VRB); + } + } + treat_color_vars_as_text = 0; free_saved_config(ps, &vsave, expose_hidden_config); #ifdef _WINDOWS @@ -1799,6 +1859,7 @@ v == &ps->vars[V_FCC_RULE] || v == &ps->vars[V_GOTO_DEFAULT_RULE] || v == &ps->vars[V_INCOMING_STARTUP] || + v == &ps->vars[V_INCOMING_RULE] || v == &ps->vars[V_PRUNING_RULE] || v == &ps->vars[V_REOPEN_RULE] || v == &ps->vars[V_THREAD_DISP_STYLE] || @@ -1828,6 +1889,8 @@ rulefunc = goto_rules; else if(v == &ps->vars[V_INCOMING_STARTUP]) rulefunc = incoming_startup_rules; + else if(v == &ps->vars[V_INCOMING_RULE]) + rulefunc = incoming_check_rules; else if(v == startup_ptr) rulefunc = startup_rules; else if(v == &ps->vars[V_PRUNING_RULE]) @@ -1933,7 +1996,8 @@ CONF_S *ctmp; if(!(cl && *cl && - ((*cl)->var == &ps->vars[V_SORT_KEY] || + (((*cl)->var == &ps->vars[V_SORT_KEY]) || + ((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]) || standard_radio_var(ps, (*cl)->var) || (*cl)->var == startup_ptr))) return; @@ -1999,6 +2063,7 @@ case V_TCPREADWARNTIMEO : case V_TCPWRITEWARNTIMEO : case V_TCPQUERYTIMEO : + case V_INCFLDTIMEO : case V_RSHCMD : case V_RSHPATH : case V_RSHOPENTIMEO : @@ -6471,7 +6536,7 @@ int multicol; { char tmp[MAXPATH+1]; - int cmd, i, j, ch = 'x', done = 0, changes = 0; + int cmd, i, j, k = 1, ch = 'x', done = 0, changes = 0; int retval = 0; int km_popped = 0, stay_in_col = 0; struct key_menu *km = NULL; @@ -6518,6 +6583,7 @@ } /*----------- Check for new mail -----------*/ + if (!ps->send_immediately){ if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0) ps->mangled_header = 1; @@ -6547,6 +6613,7 @@ mark_status_unknown(); } + } /* send_immediately */ if(ps->mangled_footer || km != screen->current->keymenu){ bitmap_t bitmap; @@ -6618,6 +6685,7 @@ } } + if(!ps_global->send_immediately){ MoveCursor(cursor_pos.row, cursor_pos.col); #ifdef MOUSE mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */ @@ -6636,6 +6704,14 @@ #ifdef _WINDOWS mswin_setscrollcallback(NULL); #endif + } /* send_immediately */ + + if (ps->send_immediately){ + ps_global->dont_use_init_cmds = 0; + process_config_input(&ch); + if (ch == '\030') /* ^X, bye */ + goto end; + } cmd = menu_command(ch, km); @@ -7057,10 +7133,12 @@ #define FOUND_NOSELECT 0x08 #define FOUND_ABOVE 0x10 char *result = NULL, buf[64]; + static char last_pat[64] = {'\0'}; static char last[64]; HelpType help; static ESCKEY_S ekey[] = { {0, 0, "", ""}, + {ctrl('N'), 9, "^N", "Ins Pat"}, {ctrl('Y'), 10, "^Y", "Top"}, {ctrl('V'), 11, "^V", "Bottom"}, {-1, 0, NULL, NULL}}; @@ -7079,13 +7157,22 @@ tmp,ekey,help,&flags); if(rc == 3) help = help == NO_HELP ? h_config_whereis : NO_HELP; - else if(rc == 0 || rc == 1 || rc == 10 || rc == 11 || !buf[0]){ + else if(rc == 0 || rc == 1 || rc == 9 || rc == 10 + || rc == 11 || !buf[0]){ if(rc == 0 && !buf[0] && last[0]) strncpy(buf, last, 64); - break; + if(rc == 9) + insert_pattern_in_string(buf, last_pat, 63); + else + break; } } + + if (buf[0] != '\0'){ + strncpy(last_pat, buf, sizeof(last_pat)); + last_pat[sizeof(buf) - 1] = '\0'; + } screen->current->flags &= ~CF_VAR2; if(rc == 0 && buf[0]){ @@ -7297,7 +7384,7 @@ break; } } - +end: screen->current = first_confline(screen->current); free_conflines(&screen->current); return(retval); @@ -7440,6 +7527,8 @@ return(h_config_nntp_server); case V_INBOX_PATH : return(h_config_inbox_path); + case V_INCOMING_FOLDERS_CHECK : + return(h_config_check_inc_fld); case V_PRUNED_FOLDERS : return(h_config_pruned_folders); case V_DEFAULT_FCC : @@ -7478,12 +7567,52 @@ return(h_config_fcc_rule); case V_SORT_KEY : return(h_config_sort_key); + case V_THREAD_SORT_KEY : + return(h_config_thread_sort_key); case V_AB_SORT_RULE : return(h_config_ab_sort_rule); case V_FLD_SORT_RULE : return(h_config_fld_sort_rule); + case V_THREAD_DISP_STYLE_RULES: + return(h_config_thread_display_style_rule); + case V_THREAD_INDEX_STYLE_RULES: + return(h_config_thread_index_style_rule); + case V_COMPOSE_RULES: + return(h_config_compose_rules); + case V_FORWARD_RULES: + return(h_config_forward_rules); + case V_INDEX_RULES: + return(h_config_index_rules); + case V_REPLACE_RULES: + return(h_config_replace_rules); + case V_REPLY_INDENT_RULES: + return(h_config_reply_indent_rules); + case V_REPLY_LEADIN_RULES: + return(h_config_reply_leadin_rules); + case V_RESUB_RULES: + return(h_config_resub_rules); + case V_SAVE_RULES: + return(h_config_save_rules); + case V_SMTP_RULES: + return(h_config_smtp_rules); + case V_SORT_RULES: + return(h_config_sort_rules); + case V_STARTUP_RULES: + return(h_config_startup_rules); case V_CHAR_SET : return(h_config_char_set); +#ifdef ENABLE_SEND_CHARSET + case V_SEND_CHARSET : + return(h_config_send_char_set); +#endif + case V_ASSUMED_CHAR_SET : + return(h_config_assumed_charset); + case V_CHAR_SET_ALIASES : + return(h_config_charset_aliases); +#ifdef HAVE_ICONV + case V_ICONV_ALIASES : + return(h_config_iconv_aliases); +#endif case V_EDITOR : return(h_config_editor); case V_SPELLER : @@ -7514,6 +7643,8 @@ return(h_config_scroll_margin); case V_DEADLETS : return(h_config_deadlets); + case V_SPECIAL_TEXT : + return(h_config_special_text_to_color); case V_FILLCOL : return(h_config_composer_wrap_column); case V_TCPOPENTIMEO : @@ -7524,6 +7655,8 @@ return(h_config_tcp_writewarn_timeo); case V_TCPQUERYTIMEO : return(h_config_tcp_query_timeo); + case V_INCFLDTIMEO : + return(h_config_inc_fld_timeo); case V_RSHOPENTIMEO : return(h_config_rsh_open_timeo); case V_SSHOPENTIMEO : @@ -7606,6 +7739,8 @@ return(h_config_goto_default); case V_INCOMING_STARTUP: return(h_config_inc_startup); + case V_INCOMING_RULE: + return(h_config_inc_rule); case V_PRUNING_RULE: return(h_config_pruning_rule); case V_REOPEN_RULE: @@ -7632,6 +7767,8 @@ return(h_config_newmailwidth); case V_NEWSRC_PATH : return(h_config_newsrc_path); + case V_MAILDIR_LOCATION : + return(h_config_maildir_location); case V_BROWSER : return(h_config_browser); #if defined(DOS) || defined(OS2) @@ -7663,6 +7800,9 @@ case V_SIGNATURE_FORE_COLOR : case V_SIGNATURE_BACK_COLOR : return(h_config_signature_color); + case V_SPECIAL_TEXT_FORE_COLOR : + case V_SPECIAL_TEXT_BACK_COLOR : + return(h_config_special_text_color); case V_PROMPT_FORE_COLOR : case V_PROMPT_BACK_COLOR : return(h_config_prompt_color); @@ -8056,6 +8196,10 @@ lowrange = 5; hirange = 1000; } + else if((*cl)->var == &ps->vars[V_INCFLDTIMEO]){ + lowrange = 2; + hirange = 60; + } else if((*cl)->var == &ps->vars[V_TCPWRITEWARNTIMEO] || (*cl)->var == &ps->vars[V_RSHOPENTIMEO] || (*cl)->var == &ps->vars[V_SSHOPENTIMEO] || @@ -9382,7 +9526,7 @@ } set_current_val((*cl)->var, TRUE, TRUE); - if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev) != -1){ + if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev,0) != -1){ ps->def_sort = def_sort; ps->def_sort_rev = def_sort_rev; } @@ -9391,6 +9535,37 @@ ps->mangled_body = 1; /* BUG: redraw it all for now? */ rv = 1; } + else if((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]){ + SortOrder thread_def_sort; + int thread_def_sort_rev; + + thread_def_sort_rev = (*cl)->varmem >= (short) EndofList; + thread_def_sort = (SortOrder) ((*cl)->varmem - (thread_def_sort_rev + * EndofList)); + sprintf(tmp_20k_buf, "%s%s", sort_name(thread_def_sort), + (thread_def_sort_rev) ? "/Reverse" : ""); + + if((*cl)->var->cmdline_val.p) + fs_give((void **)&(*cl)->var->cmdline_val.p); + + if(apval){ + if(*apval) + fs_give((void **)apval); + + *apval = cpystr(tmp_20k_buf); + } + + set_current_val((*cl)->var, TRUE, TRUE); + if(decode_sort(ps->VAR_THREAD_SORT_KEY, &thread_def_sort, + &thread_def_sort_rev, 1) != -1){ + ps->thread_def_sort = thread_def_sort; + ps->thread_def_sort_rev = thread_def_sort_rev; + } + + set_radio_pretty_vals(ps, cl); + ps->mangled_body = 1; /* BUG: redraw it all for now? */ + rv = 1; + } else q_status_message(SM_ORDER | SM_DING, 3, 6, "Programmer botch! Unknown radiobutton type."); @@ -10915,7 +11090,9 @@ else if(standard_radio_var(ps, v) || v == startup_ptr) return(radio_pretty_value(ps, cl)); else if(v == &ps->vars[V_SORT_KEY]) - return(sort_pretty_value(ps, cl)); + return(sort_pretty_value(ps, cl, 0)); + else if(v == &ps->vars[V_THREAD_SORT_KEY]) + return(sort_pretty_value(ps, cl, 1)); else if(v == &ps->vars[V_SIGNATURE_FILE]) return(sigfile_pretty_value(ps, cl)); else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME]) @@ -11584,19 +11761,21 @@ char * -sort_pretty_value(ps, cl) +sort_pretty_value(ps, cl, thread) struct pine *ps; CONF_S *cl; + int thread; { - return(generalized_sort_pretty_value(ps, cl, 1)); + return(generalized_sort_pretty_value(ps, cl, 1, thread)); } char * -generalized_sort_pretty_value(ps, cl, default_ok) +generalized_sort_pretty_value(ps, cl, default_ok, thread) struct pine *ps; CONF_S *cl; int default_ok; + int thread; { char tmp[MAXPATH]; char *pvalnorm, *pvalexc, *pval; @@ -11646,7 +11825,7 @@ } else if(fixed){ pval = v->fixed_val.p; - decode_sort(pval, &var_sort, &var_sort_rev); + decode_sort(pval, &var_sort, &var_sort_rev, thread); is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); sprintf(tmp, "(%c) %s%-*s%*s%s", @@ -11657,9 +11836,9 @@ is_the_one ? " (value is fixed)" : ""); } else if(is_set_for_this_level){ - decode_sort(pval, &var_sort, &var_sort_rev); + decode_sort(pval, &var_sort, &var_sort_rev, thread); is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort); - decode_sort(pvalexc, &exc_sort, &exc_sort_rev); + decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread); the_exc_one = (editing_normal_which_isnt_except && pvalexc && exc_sort_rev == line_sort_rev && exc_sort == line_sort); sprintf(tmp, "(%c) %s%-*s%*s%s", @@ -11677,7 +11856,7 @@ } else{ if(pvalexc){ - decode_sort(pvalexc, &exc_sort, &exc_sort_rev); + decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread); is_the_one = (exc_sort_rev == line_sort_rev && exc_sort == line_sort); sprintf(tmp, "( ) %s%-*s%*s%s", @@ -11688,7 +11867,7 @@ } else{ pval = v->current_val.p; - decode_sort(pval, &var_sort, &var_sort_rev); + decode_sort(pval, &var_sort, &var_sort_rev, thread); is_the_one = ((pval || default_ok) && var_sort_rev == line_sort_rev && var_sort == line_sort); @@ -11886,6 +12065,19 @@ cl->value = pretty_value(ps, cl); } + if (f->id == F_ENHANCED_THREAD && ps->mail_stream + && SORT_IS_THREADED(ps->msgmap)){ + refresh_sort(ps->mail_stream, ps->msgmap, SRT_NON); + if (COLL_THRDS()) /* sortring by thread destroys collapsed info */ + kolapse_thread(ps, ps->mail_stream, ps->msgmap,'[', 0); + if(SEP_THRDINDX() && SORT_IS_THREADED(ps->msgmap) + && sp_viewing_a_thread(ps->mail_stream)){ + unview_thread(ps, ps->mail_stream, ps->msgmap); + view_thread(ps, ps->mail_stream, ps->msgmap, 0); + ps_global->next_screen = SCREEN_FUN_NULL; + } + } + /* * Handle any features that need special attention here... @@ -11896,6 +12088,10 @@ mail_parameters(NULL,SET_FROMWIDGET,(void *)(F_ON(f->id ,ps) ? 1 : 0)); break; + case F_COURIER_FOLDER_LIST: + mail_parameters(NULL,SET_COURIERSTYLE,(void *)(F_ON(f->id ,ps)? 1 : 0)); + break; /* COURIER == 1, CCLIENT == 0, see maildir.h */ + case F_CMBND_ABOOK_DISP : addrbook_reset(); break; @@ -12000,14 +12196,6 @@ #endif #if !defined(DOS) && !defined(OS2) - case F_ALLOW_TALK : - if(F_ON(f->id, ps)) - allow_talk(ps); - else - disallow_talk(ps); - - break; - case F_PASS_CONTROL_CHARS : ps->pass_ctrl_chars = F_ON(F_PASS_CONTROL_CHARS,ps_global) ? 1 : 0; break; @@ -12015,6 +12203,9 @@ case F_PASS_C1_CONTROL_CHARS : ps->pass_c1_ctrl_chars = F_ON(F_PASS_C1_CONTROL_CHARS,ps_global) ? 1 : 0; + if(ps_global->VAR_CHAR_SET + && !strucmp(ps_global->VAR_CHAR_SET, "UTF-8")) + ps->pass_c1_ctrl_chars = 1; break; #endif #ifdef MOUSE @@ -12695,6 +12886,27 @@ var == &ps->vars[V_ABOOK_FORMATS]){ addrbook_reset(); } + else if(var == &ps->vars[V_COMPOSE_RULES] || + var == &ps->vars[V_FORWARD_RULES] || + var == &ps->vars[V_INDEX_RULES] || + var == &ps->vars[V_REPLACE_RULES] || + var == &ps->vars[V_REPLY_INDENT_RULES] || + var == &ps->vars[V_REPLY_LEADIN_RULES] || + var == &ps->vars[V_RESUB_RULES] || + var == &ps->vars[V_SAVE_RULES] || + var == &ps->vars[V_SMTP_RULES] || + var == &ps->vars[V_SORT_RULES] || + var == &ps->vars[V_STARTUP_RULES] || + var == &ps->vars[V_THREAD_DISP_STYLE_RULES] || + var == &ps->vars[V_THREAD_INDEX_STYLE_RULES]){ + if(ps_global->rule_list) + free_parsed_rule_list(&ps_global->rule_list); + create_rule_list(); + if(var == &ps->vars[V_INDEX_RULES]){ + reset_index_format(); + clear_iindex_cache(); + } + } else if(var == &ps->vars[V_INDEX_FORMAT]){ reset_index_format(); clear_iindex_cache(); @@ -12861,6 +13073,12 @@ if(ps->VAR_TCPQUERYTIMEO && SVAR_TCP_QUERY(ps, val, tmp_20k_buf)) q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); } + else if(var == &ps->vars[V_INCFLDTIMEO]){ + val = 5; + if(!revert) + if(ps->VAR_INCFLDTIMEO && SVAR_TCP_QUERY(ps, val, tmp_20k_buf)) + q_status_message(SM_ORDER, 3, 5, tmp_20k_buf); + } else if(var == &ps->vars[V_RSHOPENTIMEO]){ val = 15; if(!revert) @@ -12902,6 +13120,11 @@ fs_give((void **)&ps->vars[V_OPER_DIR].main_user_val.p); } } + else if(var == &ps->vars[V_INCOMING_FOLDERS_CHECK] && + F_OFF(F_ENABLE_FAST_RECENT, ps)){ + ps->force_check_now = 1; + new_mail_incfolder(ps, MC_FORCECHECK); /* yes, update it now */ + } else if(var == &ps->vars[V_MAILCHECK]){ timeo = 15; if(SVAR_MAILCHK(ps, timeo, tmp_20k_buf)){ @@ -12967,6 +13190,10 @@ (void *)var->current_val.p); } #endif + else if(var == &ps->vars[V_MAILDIR_LOCATION]){ + if(var->current_val.p && var->current_val.p[0]) + maildir_parameters(SET_INBOXPATH, (void *)var->current_val.p); + } else if(revert && standard_radio_var(ps, var)){ cur_rule_value(var, TRUE, FALSE); @@ -13013,9 +13240,15 @@ else if(revert && var == &ps->vars[V_SORT_KEY]){ int def_sort_rev; - decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev); + decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev, 0); ps->def_sort_rev = def_sort_rev; } + else if(revert && var == &ps->vars[V_THREAD_SORT_KEY]){ + int thread_def_sort_rev; + + decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, &thread_def_sort_rev, 1); + ps->thread_def_sort_rev = thread_def_sort_rev; + } else if(var == &ps->vars[V_THREAD_MORE_CHAR] || var == &ps->vars[V_THREAD_EXP_CHAR] || var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){ @@ -13772,12 +14005,16 @@ if(!(nonempty_patterns(rflags, &pstate) && first_pattern(&pstate))){ - q_status_message(SM_ORDER, 0, 3, + if (!ps->send_immediately) + q_status_message(SM_ORDER, 0, 3, "No roles available. Use Setup/Rules to add roles."); + else{ + printf("No roles available. Use Setup/Rules to add roles."); + exit(-1); + } return(ret); } - if(alt_compose){ menu_init_binding(&role_select_km, alt_compose == MC_FORWARD ? 'F' : alt_compose == MC_REPLY ? 'R' : 'C', @@ -17995,7 +18232,7 @@ pval = PVAL(&sort_act_var, ew); if(pval) - decode_sort(pval, &def_sort, &def_sort_rev); + decode_sort(pval, &def_sort, &def_sort_rev, 0); /* allow user to set their default sort order */ new_confline(&ctmp)->var = &sort_act_var; @@ -18005,7 +18242,7 @@ ctmp->tool = role_sort_tool; ctmp->valoffset = 12; ctmp->varmem = -1; - ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0); + ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0, 0); for(j = 0; j < 2; j++){ for(i = 0; ps->sort_types[i] != EndofList; i++){ @@ -18017,7 +18254,7 @@ ctmp->valoffset = 12; ctmp->varmem = i + (j * EndofList); ctmp->value = generalized_sort_pretty_value(ps, ctmp, - 0); + 0, 0); } } @@ -18922,7 +19159,7 @@ (*result)->patgrp->stat_boy = PAT_STAT_EITHER; if(sort_act){ - decode_sort(sort_act, &def_sort, &def_sort_rev); + decode_sort(sort_act, &def_sort, &def_sort_rev, 0); (*result)->action->sort_is_set = 1; (*result)->action->sortorder = def_sort; (*result)->action->revsort = (def_sort_rev ? 1 : 0); @@ -21350,6 +21587,11 @@ if(apval) *apval = (role && role->nick) ? cpystr(role->nick) : NULL; + if (ps_global->role) + fs_give((void **)&ps_global->role); + if (role && role->nick) + ps_global->role = cpystr(role->nick); + if((*cl)->value) fs_give((void **)&((*cl)->value)); @@ -24152,6 +24394,7 @@ set_color_val(&vars[V_IND_UNS_FORE_COLOR], 0); set_color_val(&vars[V_IND_ARR_FORE_COLOR], 0); set_color_val(&vars[V_SIGNATURE_FORE_COLOR], 0); + set_color_val(&vars[V_SPECIAL_TEXT_FORE_COLOR], 0); set_current_val(&ps->vars[V_VIEW_HDR_COLORS], TRUE, TRUE); set_current_val(&ps->vars[V_KW_COLORS], TRUE, TRUE); @@ -24702,3 +24945,5 @@ return(TRUE); } #endif /* _WINDOWS */ + +#include "rules.c" diff -ru pine4.64/pine/pine-use.c pine4.64.SuSE/pine/pine-use.c --- pine4.64/pine/pine-use.c 1996-03-15 08:17:22.000000000 +0100 +++ pine4.64.SuSE/pine/pine-use.c 2006-02-14 14:45:25.000000000 +0100 @@ -45,7 +45,7 @@ #include #ifndef MAILSPOOLPCTS -#define MAILSPOOLPCTS "/usr/spool/mail/%s" +#define MAILSPOOLPCTS "/var/mail/%s" /* #define MAILSPOOLPCTS "/usr/mail/%s" */ #endif diff -ru pine4.64/pine/pine.c pine4.64.SuSE/pine/pine.c --- pine4.64/pine/pine.c 2005-09-13 00:04:25.000000000 +0200 +++ pine4.64.SuSE/pine/pine.c 2006-02-14 14:45:23.000000000 +0100 @@ -87,6 +87,7 @@ /* * Internal prototypes */ +int sp_add_status PROTO((MAILSTREAM *)); int sp_add PROTO((MAILSTREAM *, int)); int sp_nusepool_notperm PROTO((void)); void sp_delete PROTO((MAILSTREAM *)); @@ -252,6 +253,7 @@ pine_state = (struct pine *)fs_get(sizeof (struct pine)); memset((void *)pine_state, 0, sizeof(struct pine)); ps_global = pine_state; + ps_global->thread_def_sort = SortDate; ps_global->def_sort = SortArrival; ps_global->sort_types[0] = SortSubject; ps_global->sort_types[1] = SortArrival; @@ -264,6 +266,8 @@ ps_global->sort_types[8] = SortScore; ps_global->sort_types[9] = SortThread; ps_global->sort_types[10] = EndofList; + ps_global->force_check_now = 1; + ps_global->delay = 1; ps_global->atmts = (ATTACH_S *) fs_get(sizeof(ATTACH_S)); ps_global->atmts_allocated = 1; ps_global->atmts->description = NULL; @@ -342,7 +346,7 @@ pine_args(pine_state, argc, argv, &args); #ifndef _WINDOWS - if(!isatty(0)){ + if((!pine_state->send_immediately) && !isatty(0)){ /* * monkey with descriptors so our normal tty i/o routines don't * choke... @@ -366,12 +370,15 @@ exit(-1); } + mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota); + /* set some default timeouts in case pinerc is remote */ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30); mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15); mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout); /* could be TO_BAIL_THRESHOLD, 15 seems more appropriate for now */ pine_state->tcp_query_timeout = 15; + pine_state->incfld_timeout = 5; mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened); mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback); @@ -523,12 +530,20 @@ ps_global->s_pool.max_remstream)); init_vars(pine_state); + if (args.action == aaFolder && !args.data.folder && + ps_global->send_immediately){ + printf("No value for To: field specified\n"); + exit(-1); + } if(args.action == aaFolder){ pine_state->beginning_of_month = first_run_of_month(); pine_state->beginning_of_year = first_run_of_year(); } + mail_parameters(NULL,SET_COURIERSTYLE, + (void *)(F_ON(F_COURIER_FOLDER_LIST, ps_global) ? 1 : 0)); + set_collation(F_OFF(F_DISABLE_SETLOCALE_COLLATE, ps_global), F_ON(F_ENABLE_SETLOCALE_CTYPE, ps_global)); @@ -734,6 +749,7 @@ /*--- output side ---*/ + if (!ps_global->send_immediately){ rv = config_screen(&(pine_state->ttyo)); #if !defined(DOS) && !defined(OS2) /* always succeeds under DOS! */ if(rv){ @@ -758,6 +774,9 @@ exit(-1); } #endif + } + else + fake_config_screen(&(pine_state->ttyo)); if(F_ON(F_BLANK_KEYMENU,ps_global)) FOOTER_ROWS(ps_global) = 1; @@ -804,7 +823,7 @@ goodnight_gracey(pine_state, exit_val); } - if(args.action == aaFolder + if(!pine_state->send_immediately && args.action == aaFolder && (pine_state->first_time_user || pine_state->show_new_version)){ pine_state->mangled_header = 1; show_main_screen(pine_state, 0, FirstMenu, &main_keymenu, 0, @@ -958,6 +977,12 @@ int len, good_addr = 1; int exit_val = 0; BUILDER_ARG fcc; + ACTION_S *role = NULL; + + if (pine_state->in_init_seq && pine_state->send_immediately + && (char) *pine_state->initial_cmds++ == '#' + && ++pine_state->initial_cmds_offset) + role_select_screen(pine_state, &role, 1); if(pine_state->in_init_seq){ pine_state->in_init_seq = pine_state->save_in_init_seq = 0; @@ -993,7 +1018,7 @@ memset(&fcc, 0, sizeof(fcc)); if(good_addr){ - compose_mail(addr, fcc.tptr, NULL, + compose_mail(addr, fcc.tptr, role, args.data.mail.attachlist, stdin_getc); } else{ @@ -1026,6 +1051,7 @@ pine_state->mail_stream = NULL; pine_state->mangled_screen = 1; + pine_state->subject = NULL; if(args.action == aaURL){ url_tool_t f; @@ -1115,6 +1141,7 @@ "mail folder"); } + if (!pine_state->send_immediately) fflush(stdout); #if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO) @@ -3181,7 +3208,8 @@ { int quit = 0; - dprint(1, (debugfile, "\n\n ---- QUIT SCREEN ----\n")); + dprint(1, (debugfile, "\n\n ---- QUIT SCREEN ----\n")); + ps_global->in_pico = 1; /* we are leaving anyway */ if(F_ON(F_CHECK_MAIL_ONQUIT,ps_global) && new_mail(1, VeryBadTime, NM_STATUS_MSG | NM_DEFER_SORT) > 0 @@ -3226,6 +3254,7 @@ extern KBESC_T *kbesc; dprint(2, (debugfile, "goodnight_gracey:\n")); + sprintf(pine_state->cur_folder, pine_state->inbox_name); /* We want to do this here before we close up the streams */ trim_remote_adrbks(); @@ -3328,6 +3357,7 @@ dprint(7, (debugfile, "goodnight_gracey: sp_end\n")); ps_global->noshow_error = 1; sp_end(); + sp_status_end(); /* after sp_end, which might call a filter */ completely_done_with_adrbks(); @@ -3365,6 +3395,8 @@ free_saved_query_parameters(); #endif + if(pine_state->subject != NULL) + fs_give((void **)&pine_state->subject); if(pine_state->hostname != NULL) fs_give((void **)&pine_state->hostname); if(pine_state->localdomain != NULL) @@ -3426,6 +3458,9 @@ fs_give((void **)&ps_global->atmts); } + + if(ps_global->rule_list) + free_parsed_rule_list(&ps_global->rule_list); dprint(7, (debugfile, "goodnight_gracey: free_vars\n")); free_vars(pine_state); @@ -3973,14 +4008,15 @@ was_invisible = (mc->spare || mc->spare4) ? 1 : 0; + thrd = fetch_thread(stream, rawno); + if(chk_thrd_cnt = ((msgs->visible_threads >= 0L) && THRD_INDX_ENABLED() && (f & MN_HIDE) && (mc->spare != v))){ - thrd = fetch_thread(stream, rawno); if(thrd && thrd->top){ - if(thrd->top == thrd->rawno) + if(top_thread(stream,thrd->top) == thrd->rawno) topthrd = thrd; else - topthrd = fetch_thread(stream, thrd->top); + topthrd = fetch_thread(stream, top_thread(stream,thrd->top)); } if(topthrd){ @@ -4429,6 +4465,11 @@ char **lock_these; static unsigned long streamcounter = 0; + if(ps_global->cancelproc){ + dprint(7, (debugfile, "pine_mail_open: cancelled by user\n")); + return retstream; + } + dprint(7, (debugfile, "pine_mail_open: opening \"%s\"%s openflags=0x%x %s%s%s%s%s%s%s%s%s (%s)\n", mailbox ? mailbox : "(NULL)", @@ -6337,6 +6378,91 @@ return(NULL); } +MAILSTREAM * +sp_stream_status_get(mailbox) + char *mailbox; +{ + int i; + MAILSTREAM *m = NULL; + + for(i = 0; i < ps_global->s_pool_status.nstream; i++){ + m = ps_global->s_pool_status.streams[i]; + if(m && same_stream(mailbox, m) && pine_mail_ping(m)) + return m; + } + return NULL; +} + +void +sp_status_end() +{ + int i; + MAILSTREAM *m; + + for(i = 0; i < ps_global->s_pool_status.nstream; i++){ + m = ps_global->s_pool_status.streams[i]; + if(m) + pine_mail_actually_close(m); + } + + if(ps_global->s_pool_status.streams) + fs_give((void **) &ps_global->s_pool_status.streams); + + ps_global->s_pool_status.nstream = 0; +} + +int +sp_add_status(stream) + MAILSTREAM *stream; +{ + int i, slot = -1; + MAILSTREAM *m; + + if(!stream) + return -1; + + for(i = 0; i < ps_global->s_pool_status.nstream; i++){ + m = ps_global->s_pool_status.streams[i]; + if(m == stream){ + slot = i; + return 0; + } + } + + for(i = 0; i < ps_global->s_pool_status.nstream; i++){ + m = ps_global->s_pool_status.streams[i]; + if(!m){ + slot = i; + break; + } + } + + if(slot < 0){ + slot = ps_global->s_pool_status.nstream++; + if(ps_global->s_pool_status.streams){ + fs_resize((void **) &ps_global->s_pool_status.streams, + ps_global->s_pool_status.nstream * + sizeof(*ps_global->s_pool_status.streams)); + ps_global->s_pool_status.streams[slot] = NULL; + } + else{ + ps_global->s_pool_status.streams = + (MAILSTREAM **) fs_get(ps_global->s_pool_status.nstream * + sizeof(*ps_global->s_pool_status.streams)); + memset(ps_global->s_pool_status.streams, 0, + ps_global->s_pool_status.nstream * + sizeof(*ps_global->s_pool_status.streams)); + } + } + + if(slot >= 0 && slot < ps_global->s_pool_status.nstream){ + ps_global->s_pool_status.streams[slot] = stream; + return 0; + } + else + return -1; +} + void sp_end() diff -ru pine4.64/pine/pine.h pine4.64.SuSE/pine/pine.h --- pine4.64/pine/pine.h 2005-09-16 02:39:42.000000000 +0200 +++ pine4.64.SuSE/pine/pine.h 2006-02-14 14:45:25.000000000 +0100 @@ -68,6 +68,7 @@ #define PHONE_HOME_HOST "docserver.cac.washington.edu" #define UNKNOWN_CHARSET "X-UNKNOWN" +#define US_ASCII_CHARSET "US-ASCII" #define OUR_HDRS_LIST "X-Our-Headers" @@ -178,7 +179,6 @@ #define GER_ALLPARTS 0x04 /* AllParts toggle is on */ #define GFHP_STRIPPED 0x01 -#define GFHP_HANDLES 0x02 #define GFHP_LOCAL_HANDLES 0x04 #define GFW_HANDLES 0x01 @@ -232,6 +232,9 @@ #ifndef DF_INCOMING_STARTUP #define DF_INCOMING_STARTUP "first-unseen" #endif +#ifndef DF_INCOMING_RULE +#define DF_INCOMING_RULE "automatic" +#endif #ifndef DF_PRUNING_RULE #define DF_PRUNING_RULE "ask-ask" #endif @@ -620,6 +623,7 @@ , V_SMTP_SERVER , V_NNTP_SERVER , V_INBOX_PATH + , V_INCOMING_FOLDERS_CHECK , V_ARCHIVED_FOLDERS , V_PRUNED_FOLDERS , V_DEFAULT_FCC @@ -640,10 +644,12 @@ , V_SAVED_MSG_NAME_RULE , V_FCC_RULE , V_SORT_KEY + , V_THREAD_SORT_KEY , V_AB_SORT_RULE , V_FLD_SORT_RULE , V_GOTO_DEFAULT_RULE , V_INCOMING_STARTUP + , V_INCOMING_RULE , V_PRUNING_RULE , V_REOPEN_RULE , V_THREAD_DISP_STYLE @@ -651,10 +657,32 @@ , V_THREAD_MORE_CHAR , V_THREAD_EXP_CHAR , V_THREAD_LASTREPLY_CHAR + , V_THREAD_DISP_STYLE_RULES + , V_THREAD_INDEX_STYLE_RULES + , V_COMPOSE_RULES + , V_FORWARD_RULES + , V_INDEX_RULES + , V_REPLACE_RULES + , V_REPLY_INDENT_RULES + , V_REPLY_LEADIN_RULES + , V_RESUB_RULES + , V_SAVE_RULES + , V_SMTP_RULES + , V_SORT_RULES + , V_STARTUP_RULES , V_CHAR_SET +#ifdef ENABLE_SEND_CHARSET + , V_SEND_CHARSET +#endif + , V_ASSUMED_CHAR_SET + , V_CHAR_SET_ALIASES +#ifdef HAVE_ICONV + , V_ICONV_ALIASES +#endif , V_EDITOR , V_SPELLER , V_FILLCOL + , V_SPECIAL_TEXT , V_REPLY_STRING , V_REPLY_INTRO , V_QUOTE_REPLACE_STRING @@ -687,6 +715,7 @@ , V_NEWSRC_PATH , V_NEWS_ACTIVE_PATH , V_NEWS_SPOOL_DIR + , V_MAILDIR_LOCATION , V_UPLOAD_CMD , V_UPLOAD_CMD_PREFIX , V_DOWNLOAD_CMD @@ -724,6 +753,7 @@ , V_TCPREADWARNTIMEO , V_TCPWRITEWARNTIMEO , V_TCPQUERYTIMEO + , V_INCFLDTIMEO , V_RSHCMD , V_RSHPATH , V_RSHOPENTIMEO @@ -785,6 +815,8 @@ , V_QUOTE3_BACK_COLOR , V_SIGNATURE_FORE_COLOR , V_SIGNATURE_BACK_COLOR + , V_SPECIAL_TEXT_FORE_COLOR + , V_SPECIAL_TEXT_BACK_COLOR , V_PROMPT_FORE_COLOR , V_PROMPT_BACK_COLOR , V_IND_PLUS_FORE_COLOR @@ -841,6 +873,8 @@ #define VAR_INBOX_PATH vars[V_INBOX_PATH].current_val.p #define GLO_INBOX_PATH vars[V_INBOX_PATH].global_val.p #define VAR_INCOMING_FOLDERS vars[V_INCOMING_FOLDERS].current_val.l +#define VAR_INCOMING_FOLDERS_CHECK vars[V_INCOMING_FOLDERS_CHECK].current_val.p +#define GLO_INCOMING_FOLDERS_CHECK vars[V_INCOMING_FOLDERS_CHECK].global_val.p #define VAR_FOLDER_SPEC vars[V_FOLDER_SPEC].current_val.l #define GLO_FOLDER_SPEC vars[V_FOLDER_SPEC].global_val.l #define VAR_NEWS_SPEC vars[V_NEWS_SPEC].current_val.l @@ -895,16 +929,66 @@ #define VAR_SORT_KEY vars[V_SORT_KEY].current_val.p #define GLO_SORT_KEY vars[V_SORT_KEY].global_val.p #define COM_SORT_KEY vars[V_SORT_KEY].cmdline_val.p +#define VAR_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].current_val.p +#define GLO_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].global_val.p +#define COM_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].cmdline_val.p #define VAR_AB_SORT_RULE vars[V_AB_SORT_RULE].current_val.p #define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p #define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p #define GLO_FLD_SORT_RULE vars[V_FLD_SORT_RULE].global_val.p +#define VAR_COMPOSE_RULES vars[V_COMPOSE_RULES].current_val.l +#define GLO_COMPOSE_RULES vars[V_COMPOSE_RULES].global_val.l +#define USR_COMPOSE_RULES vars[V_COMPOSE_RULES].user_val.l +#define VAR_FORWARD_RULES vars[V_FORWARD_RULES].current_val.l +#define GLO_FORWARD_RULES vars[V_FORWARD_RULES].global_val.l +#define USR_FORWARD_RULES vars[V_FORWARD_RULES].user_val.l +#define VAR_INDEX_RULES vars[V_INDEX_RULES].current_val.l +#define GLO_INDEX_RULES vars[V_INDEX_RULES].global_val.l +#define USR_INDEX_RULES vars[V_INDEX_RULES].user_val.l +#define VAR_REPLACE_RULES vars[V_REPLACE_RULES].current_val.l +#define GLO_REPLACE_RULES vars[V_REPLACE_RULES].global_val.l +#define USR_REPLACE_RULES vars[V_REPLACE_RULES].user_val.l +#define VAR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].current_val.l +#define GLO_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].global_val.l +#define USR_REPLY_INDENT_RULES vars[V_REPLY_INDENT_RULES].user_val.l +#define VAR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].current_val.l +#define GLO_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].global_val.l +#define USR_REPLY_LEADIN_RULES vars[V_REPLY_LEADIN_RULES].user_val.l +#define VAR_RESUB_RULES vars[V_RESUB_RULES].current_val.l +#define GLO_RESUB_RULES vars[V_RESUB_RULES].global_val.l +#define USR_RESUB_RULES vars[V_RESUB_RULES].user_val.l +#define VAR_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].current_val.l +#define GLO_THREAD_DISP_STYLE_RULES vars[V_THREAD_DISP_STYLE_RULES].global_val.l +#define VAR_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].current_val.l +#define GLO_THREAD_INDEX_STYLE_RULES vars[V_THREAD_INDEX_STYLE_RULES].global_val.l +#define VAR_SAVE_RULES vars[V_SAVE_RULES].current_val.l +#define GLO_SAVE_RULES vars[V_SAVE_RULES].global_val.l +#define USR_SAVE_RULES vars[V_SAVE_RULES].user_val.l +#define VAR_SMTP_RULES vars[V_SMTP_RULES].current_val.l +#define GLO_SMTP_RULES vars[V_SMTP_RULES].global_val.l +#define USR_SMTP_RULES vars[V_SMTP_RULES].user_val.l +#define VAR_SORT_RULES vars[V_SORT_RULES].current_val.l +#define GLO_SORT_RULES vars[V_SORT_RULES].global_val.l +#define USR_SORT_RULES vars[V_SORT_RULES].user_val.l +#define VAR_STARTUP_RULES vars[V_STARTUP_RULES].current_val.l +#define GLO_STARTUP_RULES vars[V_STARTUP_RULES].global_val.l +#define USR_STARTUP_RULES vars[V_STARTUP_RULES].user_val.l #define VAR_CHAR_SET vars[V_CHAR_SET].current_val.p #define GLO_CHAR_SET vars[V_CHAR_SET].global_val.p +#ifdef ENABLE_SEND_CHARSET +#define VAR_SEND_CHARSET vars[V_SEND_CHARSET].current_val.p +#endif +#define VAR_ASSUMED_CHAR_SET vars[V_ASSUMED_CHAR_SET].current_val.p +#define VAR_CHAR_SET_ALIASES vars[V_CHAR_SET_ALIASES].current_val.l +#ifdef HAVE_ICONV +#define VAR_ICONV_ALIASES vars[V_ICONV_ALIASES].current_val.l +#endif #define VAR_EDITOR vars[V_EDITOR].current_val.l #define GLO_EDITOR vars[V_EDITOR].global_val.l #define VAR_SPELLER vars[V_SPELLER].current_val.p #define GLO_SPELLER vars[V_SPELLER].global_val.p +#define VAR_SPECIAL_TEXT vars[V_SPECIAL_TEXT].current_val.l +#define GLO_SPECIAL_TEXT vars[V_SPECIAL_TEXT].global_val.l #define VAR_FILLCOL vars[V_FILLCOL].current_val.p #define GLO_FILLCOL vars[V_FILLCOL].global_val.p #define VAR_DEADLETS vars[V_DEADLETS].current_val.p @@ -989,6 +1073,8 @@ #define GLO_NEWS_ACTIVE_PATH vars[V_NEWS_ACTIVE_PATH].global_val.p #define VAR_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].current_val.p #define GLO_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].global_val.p +#define VAR_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].current_val.p +#define GLO_MAILDIR_LOCATION vars[V_MAILDIR_LOCATION].global_val.p #define VAR_DISABLE_DRIVERS vars[V_DISABLE_DRIVERS].current_val.l #define VAR_DISABLE_AUTHS vars[V_DISABLE_AUTHS].current_val.l #define VAR_REMOTE_ABOOK_METADATA vars[V_REMOTE_ABOOK_METADATA].current_val.p @@ -1033,6 +1119,7 @@ #define VAR_TCPREADWARNTIMEO vars[V_TCPREADWARNTIMEO].current_val.p #define VAR_TCPWRITEWARNTIMEO vars[V_TCPWRITEWARNTIMEO].current_val.p #define VAR_TCPQUERYTIMEO vars[V_TCPQUERYTIMEO].current_val.p +#define VAR_INCFLDTIMEO vars[V_INCFLDTIMEO].current_val.p #define VAR_RSHOPENTIMEO vars[V_RSHOPENTIMEO].current_val.p #define VAR_RSHPATH vars[V_RSHPATH].current_val.p #define VAR_RSHCMD vars[V_RSHCMD].current_val.p @@ -1045,6 +1132,8 @@ #define VAR_BROWSER vars[V_BROWSER].current_val.l #define VAR_INCOMING_STARTUP vars[V_INCOMING_STARTUP].current_val.p #define GLO_INCOMING_STARTUP vars[V_INCOMING_STARTUP].global_val.p +#define VAR_INCOMING_RULE vars[V_INCOMING_RULE].current_val.p +#define GLO_INCOMING_RULE vars[V_INCOMING_RULE].global_val.p #define VAR_PRUNING_RULE vars[V_PRUNING_RULE].current_val.p #define GLO_PRUNING_RULE vars[V_PRUNING_RULE].global_val.p #define VAR_REOPEN_RULE vars[V_REOPEN_RULE].current_val.p @@ -1122,6 +1211,8 @@ #define VAR_QUOTE3_BACK_COLOR vars[V_QUOTE3_BACK_COLOR].current_val.p #define VAR_SIGNATURE_FORE_COLOR vars[V_SIGNATURE_FORE_COLOR].current_val.p #define VAR_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].current_val.p +#define VAR_SPECIAL_TEXT_FORE_COLOR vars[V_SPECIAL_TEXT_FORE_COLOR].current_val.p +#define VAR_SPECIAL_TEXT_BACK_COLOR vars[V_SPECIAL_TEXT_BACK_COLOR].current_val.p #define VAR_PROMPT_FORE_COLOR vars[V_PROMPT_FORE_COLOR].current_val.p #define VAR_PROMPT_BACK_COLOR vars[V_PROMPT_BACK_COLOR].current_val.p #define VAR_VIEW_HDR_COLORS vars[V_VIEW_HDR_COLORS].current_val.l @@ -1151,6 +1242,7 @@ F_FULL_AUTO_EXPUNGE, F_EXPUNGE_MANUALLY, F_AUTO_READ_MSGS, + F_AUTO_READ_MSGS_RULES, F_AUTO_FCC_ONLY, F_READ_IN_NEWSRC_ORDER, F_SELECT_WO_CONFIRM, @@ -1168,6 +1260,7 @@ F_SHOW_DELAY_CUE, F_CANCEL_CONFIRM, F_AUTO_OPEN_NEXT_UNREAD, + F_AUTO_CIRCULAR_TAB, F_SELECTED_SHOWN_BOLD, F_QUOTE_ALL_FROMS, F_AUTO_INCLUDE_IN_REPLY, @@ -1184,6 +1277,8 @@ F_DISABLE_PIPES_IN_TEMPLATES, F_ATTACHMENTS_IN_REPLY, F_ENABLE_INCOMING, + F_ENABLE_INCOMING_CHECK, + F_ENABLE_INCOMING_RECHECK, F_NO_NEWS_VALIDATION, F_QUELL_EXTRA_POST_PROMPT, F_DISABLE_TAKE_LASTFIRST, @@ -1206,10 +1301,12 @@ F_PASS_C1_CONTROL_CHARS, F_SINGLE_FOLDER_LIST, F_VERTICAL_FOLDER_LIST, + F_COURIER_FOLDER_LIST, F_TAB_CHK_RECENT, F_AUTO_REPLY_TO, F_VERBOSE_POST, F_FCC_ON_BOUNCE, + F_USE_DOMAIN_NAME, F_SEND_WO_CONFIRM, F_USE_SENDER_NOT_X, F_BLANK_KEYMENU, @@ -1314,10 +1411,12 @@ F_MAILDROPS_PRESERVE_STATE, F_EXPOSE_HIDDEN_CONFIG, F_ALT_COMPOSE_MENU, + F_ALT_REPLY_MENU, F_ALT_ROLE_MENU, F_ALWAYS_SPELL_CHECK, F_QUELL_TIMEZONE, F_COLOR_LINE_IMPORTANT, + F_ENHANCED_THREAD, F_SLASH_COLL_ENTIRE, F_ENABLE_FULL_HDR_AND_TEXT, F_QUELL_FULL_HDR_RESET, @@ -1331,6 +1430,7 @@ F_SORT_DEFAULT_FCC_ALPHA, F_SORT_DEFAULT_SAVE_ALPHA, F_QUOTE_REPLACE_NOFLOW, + F_QUELL_DISPLAYING_FLOWED_TEXT, #ifdef _WINDOWS F_ENABLE_TRAYICON, F_QUELL_SSL_LARGEBLOCKS, @@ -1547,6 +1647,13 @@ #define IS_NOTSET 7 /* for reset version */ /* + * Incoming check rules + */ +#define IC_AUTO 0 +#define IC_MAN_AUTO 1 +#define IC_MAN 2 + +/* * Pruning rules. If these grow, widen pruning_rule. */ #define PRUNE_ASK_AND_ASK 0 @@ -1761,20 +1868,6 @@ /* - * Macros to aid hack to turn off talk permission. - * Bit 020 is usually used to turn off talk permission, we turn off - * 002 also for good measure, since some mesg commands seem to do that. - */ -#define TALK_BIT 020 /* mode bits */ -#define GM_BIT 002 -#define TMD_CLEAR 0 /* functions */ -#define TMD_SET 1 -#define TMD_RESET 2 -#define allow_talk(p) tty_chmod((p), TALK_BIT, TMD_SET) -#define disallow_talk(p) tty_chmod((p), TALK_BIT|GM_BIT, TMD_CLEAR) - - -/* * Macros to help set numeric pinerc variables. Defined here are the * allowed min and max values as well as the name of the var as it * should be displayed in error messages. @@ -1830,6 +1923,10 @@ #define SVAR_TCP_QUERY(ps, n, e) strtoval((ps)->VAR_TCPQUERYTIMEO, \ &(n), 5, 30000, 0, (e), \ "Tcp-Query-Timeout") +#define SVAR_INCFLDQUERY(ps, n, e) strtoval((ps)->VAR_INCFLDTIMEO, \ + &(n), 2, 60, 0, (e), \ + "Inc-fld-timeout") + #define SVAR_RSH_OPEN(ps, n, e) strtoval((ps)->VAR_RSHOPENTIMEO, \ &(n), 5, 30000, 0, (e), \ "Rsh-Open-Timeout") @@ -1930,7 +2027,7 @@ SortSubject2, SortScore, SortThread, EndofList} SortOrder; #define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \ - mn_get_revsort(M), (F)) + mn_get_revsort(M), (F), 1) /* * The two structs below hold all knowledge regarding @@ -1974,13 +2071,13 @@ typedef struct pine_thrd { unsigned long rawno; /* raw msgno of this message */ unsigned long thrdno; /* thread number */ - unsigned long flags; unsigned long next; /* msgno of first reply to us */ unsigned long branch; /* like THREADNODE branch, next replier */ unsigned long parent; /* message that this is a reply to */ unsigned long nextthd; /* next thread, only tops have this */ unsigned long prevthd; /* previous thread, only tops have this */ unsigned long top; /* top of this thread */ + unsigned long toploose; /* top of this thread, if is loose */ unsigned long head; /* head of the whole thread list */ } PINETHRD_S; @@ -2571,10 +2668,22 @@ unsigned hasnochildren:1; /* known not to have children */ unsigned scanned:1; /* scanned by c-client */ unsigned selected:1; /* selected by user */ + unsigned user_selected:1; /* selected by user (not Pine)*/ unsigned subscribed:1; /* selected by user */ unsigned long varhash; /* hash of var for incoming */ unsigned long uidvalidity; /* only for #move folder */ unsigned long uidnext; /* only for #move folder */ + time_t last_check_time; /* elapsed time in last check */ + int notified; /* notified the change? */ + int new_mail; /* folder has new mail */ + int last_check_length; /* elapsed time in last check */ + int skipped; /* skipped test? */ + int opstrm; /* open stream? */ + long interesting; /* result of last test */ + long origrecent; /* # recent messages in stream*/ + long countrecent; /* # recent messages displayed*/ + long recent; /* # recent messages adjusted */ + long messages; /* # messages */ char *nickname; /* folder's short name */ char name[1]; /* folder's name */ } FOLDER_S; @@ -2600,12 +2709,15 @@ iLstYear, iLstYear2Digit, iMessNo, iAtt, iMsgID, iSubject, iSubjKey, iSubjKeyInit, iKey, iKeyInit, - iSize, iSizeComma, iSizeNarrow, iDescripSize, + iSize, iSizeComma, iSizeNarrow, iDescripSize, iSizeThread, iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews, iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips, iCurNews, iArrow, iMailbox, iAddress, iInit, iCursorPos, iDay2Digit, iMon2Digit, iYear2Digit, + iFolder, iFlag, iCollection, iRole, + iNick, iAddressTo, iAddressCc, iAddressRecip, iBcc, iLcc, + iFfrom, iFadd, iSTime, iKSize, iRoleNick, iScore, iDayOfWeekAbb, iDayOfWeek, @@ -2622,13 +2734,24 @@ } INDEX_PARSE_T; /* these are flags for the what_for field in INDEX_PARSE_T */ -#define FOR_NOTHING 0x00 -#define FOR_INDEX 0x01 -#define FOR_REPLY_INTRO 0x02 -#define FOR_TEMPLATE 0x04 /* or for signature */ -#define FOR_FILT 0x08 -#define DELIM_USCORE 0x10 -#define DELIM_PAREN 0x20 +#define FOR_NOTHING 0x00000 +#define FOR_INDEX 0x00001 +#define FOR_REPLY_INTRO 0x00002 +#define FOR_TEMPLATE 0x00004 /* or for signature */ +#define FOR_FILT 0x00008 +#define DELIM_USCORE 0x00010 +#define DELIM_PAREN 0x00020 +#define FOR_SAVE 0x00040 /* for rules */ +#define FOR_FOLDER 0x00080 /* for rules */ +#define FOR_RULE 0x00100 /* for rules */ +#define FOR_TRIM 0x00200 /* for rules */ +#define FOR_RESUB 0x00400 /* for rules */ +#define FOR_REPLACE 0x00800 /* for rules */ +#define FOR_SORT 0x01000 /* for rules */ +#define FOR_FLAG 0x02000 /* for rules */ +#define FOR_COMPOSE 0x04000 /* for rules */ +#define FOR_THREAD 0x08000 /* for rules */ +#define FOR_STARTUP 0x10000 /* for rules */ #define DEFAULT_REPLY_INTRO "default" @@ -2992,11 +3115,26 @@ #define MC_NOT 799 #define MC_COLLAPSE 800 #define MC_CHK_RECENT 801 - +#define MC_PRETHREAD 802 +#define MC_CTHREAD 803 +#define MC_OTHREAD 804 +#define MC_DELTHREAD 805 +#define MC_UNDTHREAD 806 +#define MC_SELTHREAD 807 +#define MC_SSUTHREAD 808 +#define MC_DSUTHREAD 809 +#define MC_USUTHREAD 810 +#define MC_SORTHREAD 811 +#define MC_NEXTHREAD 812 +#define MC_KOLAPSE 813 +#define MC_EXPTHREAD 814 +#define MC_QUOTA 815 /* * Some standard Key/Command Bindings */ +#define MC_IFAUTOCHECK 820 +#define MC_FORCECHECK 821 #define NULL_MENU {NULL, NULL, {MC_NONE}, KS_NONE} #define HELP_MENU {"?", "Help", \ {MC_HELP, 2, {'?',ctrl('G')}}, \ @@ -3094,7 +3232,9 @@ #define TAB_MENU {"Tab", "NextNew", \ {MC_TAB,1,{TAB}}, \ KS_NONE} - +#define QUOTA_MENU {"@", "Quota", \ + {MC_QUOTA,1,{'@'}}, \ + KS_NONE} #define USER_INPUT_TIMEOUT(ps) ((ps->hours_to_timeout > 0) && \ ((time(0) - time_of_last_input) > 60*60*(ps->hours_to_timeout))) @@ -3538,6 +3678,7 @@ } data; } REPLY_S; +#define pico(F) call_pico(F) #define REPLY_PSEUDO 1 #define REPLY_FORW 2 /* very similar to REPLY_PSEUDO */ #define REPLY_MSGNO 3 @@ -3683,8 +3824,60 @@ #define HEX_CHAR1(C) HEX_ARRAY[((C) & 0xf0) >> 4] #define HEX_CHAR2(C) HEX_ARRAY[(C) & 0xf] +typedef struct rule { + char *result; /* The result of the rule */ + int number; /* The number of the rule that succeded, -1 if not */ +} RULE_RESULT; + +#define TOKEN_VALUE struct tokenvalue_s +#define CONDITION_S struct condition_s +#define RULEACTION_S struct ruleaction_s +#define RULE_S struct rule_s +#define RULELIST struct rulelist_s +#define PRULELIST_S struct parsedrulelist_s + +TOKEN_VALUE { + char *testxt; + TOKEN_VALUE *next; +}; + +typedef enum {Equal, Subset, Includes, + NotEqual, NotSubset, NotIncludes, + EndTypes} TestType; + +CONDITION_S { + char *tname; /* tname ttype {value} */ + TestType ttype; /* tname ttype {value} */ + TOKEN_VALUE *value; /* value to check against */ + CONDITION_S *next; /* next condition to test */ +}; + +RULEACTION_S { + char *token; /* token := function{value} or token = null */ + char *function; /* token := function{value} or simply function{value}*/ + TOKEN_VALUE *value; /* token := function{value} or simply function{value}*/ + int context; /* context in which this rule can be used */ + char* (*exec)(); + unsigned int is_trim:1; + unsigned int is_rextrim:1; + unsigned int is_replace:1; +}; +RULE_S { + CONDITION_S *condition; + RULEACTION_S *action; +}; +RULELIST { + RULE_S *prule; + RULELIST *next; +}; + +PRULELIST_S { + int varnum; /* number associated to the variable */ + RULELIST *rlist; + PRULELIST_S *next; +}; /*------------------------------ Structure to pass optionally_enter to tell it what keystrokes @@ -3832,6 +4025,7 @@ PrivateAffector *affector; } PrivateTop; +#define DF_THREAD_SORT_KEY "thread" typedef enum {OpenFolder, SaveMessage, FolderMaint, GetFcc, Subscribe, PostNews} FolderFun; @@ -3978,6 +4172,8 @@ unsigned char t; /* temporary char */ char *line; /* place for temporary storage */ char *linep; /* pointer into storage space */ + char *oldline; /* the previous line to "line" */ + char *oldlinep; /* the previous line to "line" */ void *opt; /* optional per instance data */ void *data; /* misc internal data pointer */ unsigned char queue[1 + GF_MAXBUF]; @@ -4039,7 +4235,6 @@ } ATABLE_S; -#define TAG_EMBED '\377' /* Announces embedded data in text string */ #define TAG_INVON '\001' /* Supported character attributes */ #define TAG_INVOFF '\002' #define TAG_BOLDON '\003' @@ -4049,6 +4244,7 @@ #define TAG_FGCOLOR '\010' /* Change to this foreground color */ #define TAG_BGCOLOR '\011' /* Change to this background color */ #define TAG_HANDLE '\020' /* indicate's a handle to an action */ +#define TAG_EMBED '\021' /* Announces embedded data in text string */ #define TAG_HANDLEOFF '\030' /* indicate's end of handle text */ @@ -4217,7 +4413,9 @@ RFC2369DATA_S data[MLCMD_MAXDATA]; } RFC2369_S; - +#define IF_REFRESH_NONE 0x00 +#define IF_REFRESH_WEAK 0x01 +#define IF_REFRESH_STRONG 0x10 /*---------------------------------------------------------------------- This structure sort of takes the place of global variables or perhaps @@ -4243,6 +4441,7 @@ CONTEXT_S *context_last; /* most recently open context */ SP_S s_pool; /* stream pool */ + SP_S s_pool_status; /* stream pool */ char inbox_name[MAXFOLDER+1]; char pine_pre_vers[10]; /* highest version previously run */ @@ -4250,10 +4449,25 @@ MAILSTREAM *mail_stream; /* ptr to current folder stream */ MSGNO_S *msgmap; /* ptr to current message map */ + char *role; /* role used when composing */ + int exiting; + unsigned read_predicted:1; char cur_folder[MAXPATH+1]; + QUOTALIST *quota; char last_unambig_folder[MAXPATH+1]; + int refresh_list; + int in_pico; + int in_fld_list; + int force_check_now; + int checking_incfld; + int incfld_timeout; + int last_folder_checked; + int first_folder_checked; + int skip_ifcheck; + time_t login_time; + int delay; ATTACH_S *atmts; int atmts_allocated; int remote_abook_validity; /* minutes, -1=never, 0=only on opens */ @@ -4277,6 +4491,8 @@ unsigned unseen_in_view:1; unsigned start_in_context:1; /* start fldr_scrn in current cntxt */ unsigned def_sort_rev:1; /* true if reverse sort is default */ + unsigned thread_def_sort_rev:1; /* true if reverse sort is default in thread screen */ + unsigned msgmap_thread_def_sort_rev:1; /* true if reverse sort is being used in thread screen */ unsigned restricted:1; unsigned save_msg_rule:5; @@ -4287,6 +4503,7 @@ unsigned titlebar_color_style:3; unsigned fld_sort_rule:3; unsigned inc_startup_rule:3; + unsigned inc_check_rule:2; unsigned pruning_rule:3; unsigned reopen_rule:4; unsigned goto_default_rule:3; @@ -4378,6 +4595,9 @@ int *initial_cmds; /* cmds to execute on startup */ int *free_initial_cmds; /* used to free when done */ + int *initial_cmds_backup; /* keep a copy in case they are freed */ + int *free_initial_cmds_backup; /* free the copy */ + int initial_cmds_offset; /* how many commands we have executed */ char c_client_error[300]; /* when nowhow_error is set and PARSE */ @@ -4414,6 +4634,9 @@ EditWhich ew_for_other_take; SortOrder def_sort, /* Default sort type */ + thread_def_sort, /* Default Sort Type in Thread Screen */ + thread_cur_sort, /* current sort style for threads */ + msgmap_thread_sort, sort_types[22]; int last_expire_year, last_expire_month; @@ -4426,8 +4649,13 @@ int nmw_width; + char *subject; + int send_immediately; + int hours_to_timeout; + int cancelproc; /* cancel this process now */ + int tcp_query_timeout; time_t check_interval_for_noncurr; @@ -4447,6 +4675,7 @@ INIT_ERR_S *init_errs; PRINT_S *print; + PRULELIST_S *rule_list; struct variable *vars; }; @@ -4574,6 +4803,7 @@ void gf_busy PROTO((FILTER_S *, int)); void gf_nvtnl_local PROTO((FILTER_S *, int)); void gf_local_nvtnl PROTO((FILTER_S *, int)); +void gf_quote_test PROTO((FILTER_S *, int)); void gf_line_test PROTO((FILTER_S *, int)); void *gf_line_test_opt PROTO((linetest_t, void *)); LT_INS_S **gf_line_test_new_ins PROTO((LT_INS_S **, char *, char *, int)); @@ -4610,12 +4840,17 @@ char *folder_is_nick PROTO((char *, void *, int)); char *next_folder PROTO((MAILSTREAM **, char *, char *,CONTEXT_S *, long *, int *)); +int next_folder_check PROTO((MAILSTREAM **, CONTEXT_S *, long *, long *, + FOLDER_S *, int *)); void init_inbox_mapping PROTO((char *, CONTEXT_S *)); int news_build PROTO((char *, char **, char **, BUILDER_ARG *, int *)); char *news_group_selector PROTO((char **)); void free_newsgrp_cache PROTO(()); char *context_edit_screen PROTO((struct pine *, char *, char *, char *, char *, char *)); +void update_incoming_folder_data PROTO((MAILSTREAM *, CONTEXT_S *)); +FOLDER_S *incoming_folder_data PROTO((MAILSTREAM *, CONTEXT_S *)); +int need_folder_report PROTO ((char *)); SELECTED_S *new_selected PROTO((void)); void free_selected PROTO((SELECTED_S **)); int add_new_folder PROTO((CONTEXT_S *, EditWhich, int, char *, size_t, @@ -4637,6 +4872,7 @@ #endif /*-- imap.c --*/ +void pine_parse_quota PROTO((MAILSTREAM *, unsigned char *msg,QUOTALIST *)); char *cached_user_name PROTO((char *)); void imap_flush_passwd_cache PROTO(()); long pine_tcptimeout PROTO((long, long)); @@ -4669,7 +4905,7 @@ int write_pinerc PROTO((struct pine *, EditWhich, int)); int var_in_pinerc PROTO((char *)); void free_pinerc_lines PROTO((PINERC_LINE **)); -int decode_sort PROTO((char *, SortOrder *, int *)); +int decode_sort PROTO((char *, SortOrder *, int *, int)); void dump_global_conf PROTO((void)); void dump_new_pinerc PROTO((char *)); int set_variable PROTO((int, char *, int, int, EditWhich)); @@ -4700,6 +4936,7 @@ NAMEVAL_S *titlebar_col_style PROTO((int)); NAMEVAL_S *fld_sort_rules PROTO((int)); NAMEVAL_S *incoming_startup_rules PROTO((int)); +NAMEVAL_S *incoming_check_rules PROTO((int)); NAMEVAL_S *startup_rules PROTO((int)); NAMEVAL_S *pruning_rules PROTO((int)); NAMEVAL_S *reopen_rules PROTO((int)); @@ -4742,10 +4979,11 @@ int set_mime_extension_by_type PROTO((char *, char *)); /*---- mailcmd.c ----*/ +MAILSTREAM *find_open_stream PROTO((void)); int process_cmd PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int, CmdWhere, int *)); int apply_command PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int, - int, int)); + int, int, int)); int menu_command PROTO((int, struct key_menu *)); void menu_init_binding PROTO((struct key_menu *, int, int, char *, char *, int)); @@ -4788,6 +5026,7 @@ char *get_uname PROTO((char *, char *, int)); char *build_updown_cmd PROTO((char *, char *, char *, char*)); int file_lister PROTO((char *, char *, int, char *, int, int, int)); +unsigned long rules_cursor_pos PROTO((MAILSTREAM *)); int display_folder_list PROTO((CONTEXT_S **, char *, int, int (*) PROTO((struct pine *, CONTEXT_S **, @@ -4812,6 +5051,7 @@ #endif /*--- mailindx.c ---*/ +void insert_pattern_in_string PROTO((char *, char *, int)); void mail_index_screen PROTO((struct pine *)); int index_lister PROTO((struct pine *, CONTEXT_S *, char *, MAILSTREAM *, MSGNO_S *)); @@ -4842,7 +5082,7 @@ MSGNO_S *, IndexType, int *, int)); char *sort_name PROTO((SortOrder)); void sort_folder PROTO((MAILSTREAM *, MSGNO_S *, - SortOrder, int, unsigned)); + SortOrder, int, unsigned, int)); int percent_sorted PROTO((void)); void msgno_init PROTO((MSGNO_S **, long)); void msgno_give PROTO((MSGNO_S **)); @@ -4866,7 +5106,7 @@ void free_pine_elt PROTO((void **)); SEARCHSET *build_searchset PROTO((MAILSTREAM *)); void collapse_or_expand PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, - unsigned long)); + unsigned long, int)); PINETHRD_S *fetch_thread PROTO((MAILSTREAM *, unsigned long)); PINETHRD_S *fetch_head_thread PROTO((MAILSTREAM *)); int view_thread PROTO((struct pine *, MAILSTREAM *, MSGNO_S *, int)); @@ -4914,6 +5154,8 @@ int, char *)); /*--- mailview.c ---*/ +int select_quote PROTO((long, char *, LT_INS_S **, void *)); +int next_level_quote PROTO((char *, char **, int, int)); void mail_view_screen PROTO((struct pine *)); int scrolltool PROTO((SCROLL_S *)); char *body_type_names PROTO((int)); @@ -4960,6 +5202,8 @@ void check_point_change PROTO((MAILSTREAM *)); void reset_check_point PROTO((MAILSTREAM *)); void zero_new_mail_count PROTO((void)); +int new_mail_incfolder PROTO((struct pine *, int)); +char *new_mail_in_open_stream PROTO((MAILSTREAM *, long *, long *)); int changes_to_checkpoint PROTO((MAILSTREAM *)); void close_newmailfifo PROTO((void)); void init_newmailfifo PROTO((char *)); @@ -5145,12 +5389,14 @@ int sp_flagged PROTO((MAILSTREAM *, unsigned long)); void sp_mark_stream_dead PROTO((MAILSTREAM *)); MAILSTREAM *sp_stream_get PROTO((char *, unsigned long)); +MAILSTREAM *sp_stream_status_get PROTO((char *)); int sp_a_locked_stream_is_dead PROTO((void)); int sp_a_locked_stream_changed PROTO((void)); MAILSTREAM *sp_inbox_stream PROTO((void)); void sp_cleanup_dead_streams PROTO((void)); int sp_nremote_permlocked PROTO((void)); void sp_end PROTO((void)); +void sp_status_end PROTO((void)); /*-- reply.c --*/ void reply PROTO((struct pine *, ACTION_S *)); @@ -5161,7 +5407,7 @@ ENVELOPE *, ADDRESS **, ADDRESS **, ADDRESS **, ADDRESS **,int *)); int reply_news_test PROTO((ENVELOPE *, ENVELOPE *)); -int reply_text_query PROTO((struct pine *, long, char **)); +int reply_text_query PROTO((struct pine *, long, ENVELOPE *, char **)); BODY *reply_body PROTO((MAILSTREAM *, ENVELOPE *, BODY *, long, char *, void *, char *, int, ACTION_S *, int, REDRAFT_POS_S **)); @@ -5208,6 +5454,46 @@ void standard_picobuf_setup PROTO((PICO *)); void standard_picobuf_teardown PROTO((PICO *)); +/* -- rules.c -- */ +RULE_RESULT *get_result_rule PROTO ((int, int, ENVELOPE *)); +char *test_rule PROTO ((RULELIST *, int, ENVELOPE *, int *)); +char *process_rule PROTO ((RULE_S *, int, ENVELOPE *)); +int test_condition PROTO ((CONDITION_S *, int, ENVELOPE *)); +int test_in PROTO ((CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int)); +int test_ni PROTO ((CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int)); +int test_not_in PROTO ((CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int)); +int test_not_ni PROTO ((CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int)); +int test_eq PROTO ((CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int)); +int test_not_eq PROTO ((CONDITION_S *, TOKEN_VALUE *, ENVELOPE *, int)); +int isolate_condition PROTO ((char *, char **, int *)); +int condition_contains_token PROTO((CONDITION_S *, char *)); +char *trim PROTO ((RULEACTION_S *, int, ENVELOPE *)); +char *rextrim PROTO ((RULEACTION_S *, int, ENVELOPE *)); +char *raw_value PROTO ((RULEACTION_S *, int, ENVELOPE *)); +char *extended_value PROTO ((RULEACTION_S *, int, ENVELOPE *)); +char *expand PROTO ((char *, char *)); +char *get_name_token PROTO ((char *)); +char *advance_to_char PROTO ((char *, char, int, int *)); +void free_token_value PROTO ((TOKEN_VALUE **)); +void free_condition PROTO ((CONDITION_S **)); +void free_ruleaction PROTO ((RULEACTION_S **)); +void free_rule PROTO ((RULE_S **)); +void free_rule_list PROTO ((RULELIST **)); +void free_parsed_rule_list PROTO((PRULELIST_S **)); +void *alloc_mem PROTO ((size_t)); +void add_rule PROTO ((int, int)); +void create_rule_list PROTO ((void)); +RULE_S *parse_rule PROTO ((char *, int)); +RULE_S *get_rule PROTO ((RULELIST *, int)); +RULELIST *get_rule_list PROTO ((char **, int, int)); +RULELIST *get_rulelist_from_code PROTO((int, PRULELIST_S *)); +TOKEN_VALUE *parse_group_data PROTO ((char *,int *)); +TOKEN_VALUE *copy_parsed_value PROTO((TOKEN_VALUE *, int, ENVELOPE *)); +CONDITION_S *fill_condition PROTO ((char *)); +CONDITION_S *parse_condition PROTO ((char *, int *)); +PRULELIST_S *add_prule PROTO ((PRULELIST_S *, PRULELIST_S *)); +RULEACTION_S *parse_action PROTO ((char *, int)); + /*-- screen.c --*/ void draw_keymenu PROTO((struct key_menu *, bitmap_t, int, int, int, OtherMenu)); @@ -5333,6 +5619,7 @@ void removing_leading_white_space PROTO((char *)); void removing_leading_and_trailing_white_space PROTO((char *)); int removing_double_quotes PROTO((char *)); +void removing_extra_stuff PROTO((char *)); char *skip_white_space PROTO((char *)); char *skip_to_white_space PROTO((char *)); char *removing_quotes PROTO((char *)); @@ -5452,6 +5739,9 @@ int rfc2369_parse_fields PROTO((char *, RFC2369_S *)); unsigned char *trans_euc_to_2022_jp PROTO((unsigned char *)); unsigned char *trans_2022_jp_to_euc PROTO((unsigned char *, unsigned int *)); +unsigned char* resolve_charset_alias PROTO((char *, char **)); +char *pine_check_utf8 PROTO((char *, char *, size_t)); +unsigned char *trans_with_iconv PROTO((unsigned char *, char *, char *)); char *keyword_to_nick PROTO((char *)); void find_8bitsubj_in_messages PROTO((MAILSTREAM *, SEARCHSET *, int, int)); @@ -5514,6 +5804,7 @@ void MoveCursor PROTO((int, int)); void NewLine PROTO((void)); int config_screen PROTO((struct ttyo **)); +void fake_config_screen PROTO((struct ttyo **)); void init_screen PROTO((void)); void end_screen PROTO((char *, int)); void outchar PROTO((int)); Only in pine4.64.SuSE/pine: pine.h.orig diff -ru pine4.64/pine/pine.hlp pine4.64.SuSE/pine/pine.hlp --- pine4.64/pine/pine.hlp 2005-09-28 19:56:29.000000000 +0200 +++ pine4.64.SuSE/pine/pine.hlp 2006-02-14 14:45:25.000000000 +0100 @@ -492,6 +492,7 @@

Some topics of current interest include:

    +

  • Information on patches for this release

  • Mail Drops

  • Information on Folder Locking

  • Information on Missing mail and the mbox driver @@ -1116,6 +1117,66 @@ <End of Configuration Notes> +====== h_patches ====== + + +Information on patches added to this release + + +

    Information on patches added to this release

    +

    +This version of Pine has been modified by including patches from + +http://www.math.washington.edu/~chappa/pine/. These patches include +new features and bug fixes. More complete information on each patch +included in this version can be found in the web. + +

    If you have any problems with this release of Pine, please contact +Eduardo Chappa <chappa@math.washington.edu>. Include the word +"Pine" in the subject of the message to bypass spam filters. + +

    The list of patches included in this release are: + +

    New Features: + +

      +
    • Maildir Patch. (more...) +
    • Enhanced fancy thread interface. +(more...) +
    • Pine justifies paragraphs with more than one level of indentation. +(more...) +
    • Rules patch, to make Pine flexible. +(more...) +
    • Send mail from the command line. +
    • Automatic check of new mail in incoming folders. +(more...) +
    • Write accents like áé or other foreing characters: ñ etc. +
    • Tab check folders on cycles. +(more...) +
    • Get the number of new messages when opening a folder. +
    • Reinsert the pattern you searched for last. +
    • New Reply command menu. +(more...) +
    • Change your From header without any effort! +
    • Choose a role when composing a message from a mailto: link. +
    • Paint special text in the body of the message in any custom color. +(more...) +
    • Select messages by the content of an arbitrary header. +
    • Delete until the the end of a file, or message (press ^W^X). +
    • Get the QUOTA information from an IMAP server (if such server supports + the QUOTA command). +
    • Cancel opening a connection when pressing ^C. +
    + +

    Bug Fixes: +

      +
    • Fixed a bug that makes Pine crash if certain characters appear in +the entries of a nickname in the addressbook. By Steve Hubert of the +Pine team. +
    + + + ====== h_news_legal ====== @@ -3128,7 +3189,9 @@
  • FEATURE: Alternate-Role-Menu
  • FEATURE: Assume-Slow-Link
  • FEATURE: Auto-Move-Read-Msgs +
  • FEATURE: auto-move-read-msgs-using-rules
  • FEATURE: Auto-Open-Next-Unread +
  • FEATURE: enable-circular-tab
  • FEATURE: Auto-Unzoom-After-Apply
  • FEATURE: Auto-Zoom-After-Select
  • FEATURE: Check-Newmail-When-Quitting @@ -3260,6 +3323,7 @@
  • FEATURE: Quell-Charset-Warning
  • FEATURE: Quell-Content-ID
  • FEATURE: Quell-Dead-Letter-On-Cancel +
  • FEATURE: Quell-Displaying-Flowed-Text
  • FEATURE: Quell-Empty-Directories
  • FEATURE: Quell-Extra-Post-Prompt
  • FEATURE: Quell-Filtering-Done-Message @@ -3387,6 +3451,7 @@
  • OPTION: Addressbook-Formats
  • OPTION: Alt-Addresses
  • OPTION: Character-Set +
  • OPTION: Special Text to Color
  • OPTION: Color-Style
  • OPTION: Composer-Wrap-Column
  • OPTION: Current-Indexline-Style @@ -3487,9 +3552,11 @@
  • OPTION: Sending-Filters
  • OPTION: Sendmail-Path
  • OPTION: Signature Color +
  • OPTION: Special Text Color
  • OPTION: Signature-File
  • OPTION: SMTP-Server
  • OPTION: Sort-Key +
  • OPTION: Thread-Sort-Key
  • OPTION: Speller
  • OPTION: Ssh-Command
  • OPTION: Ssh-Open-Timeout @@ -5337,6 +5404,159 @@ <End of help on this topic> +======= h_thread_index_sort_arrival ======= + + +SORT OPTION: Arrival + + +

    SORT OPTION: Arrival

    + +The Arrival sort option arranges threads according to the last +time that a message was added to it. In this order the last thread +contains the most recent message in the folder. + +

    +<End of help on this topic> + + +======= h_thread_index_sort_date ======= + + +SORT OPTION: Date + + +

    SORT OPTION: Date

    + +The Date sort option in the THREAD INDEX screen is the same +as sorting by thread, the most likely thing is that you won't see Pine +sorting the folder, because it's already sorted. + +

    +On a folder like INBOX, sorting by "Date" should be almost +identical to sorting by "Arrival". + +

    +<End of help on this topic> + + +======= h_thread_index_sort_subj ======= + + +SORT OPTION: Subject + + +

    SORT OPTION: Subject

    + +The Subject sort option has not been defined yet. + +

    +<End of help on this topic> + + +======= h_thread_index_sort_ordsubj ======= + + +SORT OPTION: OrderedSubject + + +

    SORT OPTION: OrderedSubject

    + +The OrderedSubject sort option in the THREAD INDEX screen is +the same as sorting by Subject. + +

    +<End of help on this topic> + + +======= h_thread_index_sort_thread ======= + + +SORT OPTION: Thread + + +

    SORT OPTION: Thread

    + +The Thread sort option in the THREAD INDEX screen sorts all +messages by the proposed algorithm by Crispin and Murchison. In this +method of sorting once threads have been isolated they are sorted by the +date of their parents, or if that is missing, the first message in that +thread. + +

    +<End of help on this topic> + + +======= h_thread_index_sort_from ======= + + +SORT OPTION: From + + +

    SORT OPTION: From

    + +The From sort option has not been defined yet. + +

    +<End of help on this topic> + + +======= h_thread_index_sort_size ======= + + +SORT OPTION: Size + + +

    SORT OPTION: Size

    + +The Size sort option has not been defined yet. + +

    +<End of help on this topic> + + +======= h_thread_index_sort_score ======= + + +SORT OPTION: Score + + +

    SORT OPTION: Score

    + +The Score sort option has not been defined yet. + +

    +<End of help on this topic> + + +======= h_thread_index_sort_to ======= + + +SORT OPTION: To + + +

    SORT OPTION: To

    + +The To sort option has not been defined yet. + +

    +<End of help on this topic> + + +======= h_thread_index_sort_cc ======= + + +SORT OPTION: Cc + + +

    SORT OPTION: Cc

    + +The Cc sort option has not been defined yet. + +

    +<End of help on this topic> + + ======= h_index_cmd_whereis ======= @@ -6555,6 +6775,46 @@ not preserved.

    +This version of Pine contains an enhanced algorithm for justification, +which allows you to justify text that contains more complicated quote +strings. This algorithm is based on pragmatics, rather than on a theory, +and seems to work well with most messages. Below you will find technical +information on how this algorithm works. + +

    +When justifying, Pine goes through each line of the text and tries to +determine for each line what the quote string of that line is. The quote +string you provided is always recognized. Among other characters +recognized is ">". + +

    +Some other constructions of quote strings are recognized only if they +appear enough in the text. For example "Peter :" is only +recognized if it appears in two consecutive lines. + +

    +Additionaly, Pine recognizes indent-strings and justifies text in a +paragraph to the right of indent-string, padding with spaces if necessary. +An indent string is one which you use to delimit elements of a list. For +example, if you were to write a list of groceries, one may write: + +

      +
    • Fruit +
    • Bread +
    • Eggs +
    + +

    +In this case the character "*" is the indent-string. Pine +recognizes numbers (0, 1, 2.5, etc) also as indent-strings, and certain +combinations of spaces, periods, and parenthesis. In any case, numbers are +recognized ONLY if the line preceeding the given line is empty or +ends in one of the characters "." or ":". +In addition to the explanation of what constitutes a paragraph above, a +new paragraph is recognized when an indent-string is found in it (and +validated according to the above stated rules). + +

    <End of help on this topic> @@ -17396,6 +17656,7 @@ "Index-Format" option, in the "Reply-Leadin" option, in signature files, +in the "new-rules" option, in template files used in "roles", and in the folder name that is the target of a Filter Rule. @@ -17408,7 +17669,7 @@

    -

    Tokens Available for all Cases (except Filter Rules)

    +

    Tokens Available for all Cases (except Filter Rules or in some cases for new-rules)

    SUBJECT
    @@ -17430,6 +17691,15 @@ For example, "mailbox@domain". +
    ADDRESSTO
    +
    +This is similar to the "TO" token, only it is always the +email address of all people listed in the TO: field of the messages. Addresses +are separated by a blank space. Example, "mailbox@domain" when +the e-mail message contains only one person in the To: field, or +"peter@flintstones.com president@world.com". +
    +
    MAILBOX
    This is the same as the "ADDRESS" except that the @@ -17477,6 +17747,15 @@ message's "Cc:" header field.
    +
    ADDRESSCC
    +
    +This is similar to the "CC" token, only it is always the +email address of all people listed in the Cc: field of the messages. Addresses +are separated by a blank space. Example: "mailbox@domain" when +the e-mail message contains only one person in the Cc: field, or +"peter@flintstones.com president@world.com". +
    +
    RECIPS
    This token represents the personal names (or email addresses if the names @@ -17485,6 +17764,14 @@ the message's "Cc:" header field.
    +
    ADDRESSRECIPS
    +
    +This token represent the e-mail addresses of the people in the To: and +Cc: fields, exactly in that order separated by a space. It is almost obtained +by concatenating the ADDRESSTO and ADDRESSCC tokens. +
    + +
    NEWSANDRECIPS
    This token represents the newsgroups from the @@ -17857,6 +18144,14 @@

    +
    SIZETHREAD
    +
    +This token represents the total size of the thread for a collapsed thread +or the size of the branch for an expanded thread. The field is omitted for +messages that are not top of threads nor branches and it defaults to +the SIZE token when your folders is not sorted by thread. +
    +
    SIZENARROW
    This token represents the total size, in bytes, of the message. @@ -18212,6 +18507,78 @@

    +

    Tokens Available Only for New-Rules

    + +
    +
    FOLDER
    +
    +Name of the folder where the rule will be applied +
    +
    + +
    +
    COLLECTION
    +
    +Name of the collection list where the rule will be applied. +
    +
    + +
    +
    ROLE
    +
    +Name of the Role used to reply a message. +
    +
    + +
    +
    BCC
    +
    +Not implemented yet, but it will be implemented in future versions. It will +be used for compose +reply +forward +rules. +
    +
    + +
    +
    LCC
    +
    +This is the value of the Lcc: field at the moment that you start the composition. +
    +
    + +
    +
    FORWARDFROM
    +
    +This corresponds to the personal name (or address if there's no personal +name) of the person who sent the message that you are forwarding. +
    +
    + +
    +
    FORWARDADDRESS
    +
    +This is the address of the person that sent the message that you +are forwarding. +
    +
    + + + + +
    +
    FLAG
    +
    +A string containing the value of all the flags associated to a specific +message. The possible values of allowed flags are "*" for Important, "N" +for recent or new, "U" for unseen or unread, "R" for seen or read, "A" for +answered and "D" for deleted. See an example of its use in the +new rules explanation and example help. +
    +
    + +

    Token Available Only for Templates and Signatures

    @@ -18964,6 +19331,23 @@ be combined with the other fields if you'd like. +====== h_config_check_inc_fld ====== + + +OPTION: incoming-folders-to-check + + +

    OPTION: incoming-folders-to-check

    +

    +if you set this option and +enable-check-incoming-folders then you can use this option to write a space +separate list of incoming folders where you want new mail to be +checked. If you want all your incoming folders to be checked just write a +"*" as the value for this option. +

    +<End of help on this topic> + + ======= h_address_format ======= @@ -20086,61 +20470,157 @@ <End of help on this topic> -====== h_config_literal_sig ===== +====== h_config_maildir_location ====== -OPTION: Literal-Signature +OPTION: maildir-location -

    OPTION: Literal-Signature

    +

    OPTION: maildir-location

    -With this option your actual signature, as opposed to -the name of a file containing your signature, -is stored in the Pine configuration file. -If this is defined it takes precedence over the Signature-File option.

    +This option should be used only if you have a Maildir folder which you +want to use as your INBOX. If this is not your case (or don't know what +this is), you can safely ignore this option. -This is simply a different way to store the signature. -The signature is stored inside your Pine configuration file instead of in -a separate file. -Tokens work the same way they do with the -Signature-File so look there for -help.

    +This option overrides the default directory Pine uses to find the location of +your INBOX, in case this is in Maildir format. The default value of this +option is "Maildir", but in some systems, this directory could have been +renamed (e.g. to ".maildir"). If this is your case use this option to change +the default. -The Setup/Signature command on Pine's Main Menu will edit -the "Literal-Signature" by default. However, if no -"Literal-Signature" is defined and the file named in the -"Signature-File" option exists, then the latter will be used -instead.

    - -The two character sequence \n (backslash followed by -the character n) will be used to signify a line-break in your signature. -You don't have to enter the \n, but it will be visible in the -SETUP CONFIGURATION window after you are done editing the signature. +The value of this option is prefixed with the "~/" string to determine the +full path to your INBOX.

    -

    +

    <End of help on this topic> -====== h_config_signature_file ===== +====== h_config_maildir ===== -OPTION: Signature-File +Maildir Support -

    OPTION: Signature-File

    +

    Maildir Support

    -If a Literal-Signature option is defined, -then this "Signature-File" option will be ignored. -You can tell that that is the case because the value of the -"Signature-File" will show up as -

    -

    <Ignored: using Literal-Signature instead>
    +This version of Pine has been enhanced with Maildir support. This text is +intended to be a reference on its support. +

    + +A Maildir folder is a directory that contains three directories called +cur, tmp and new. A program that delivers mail (e.g. postfix) will put new +mail in the new directory. A program that reads mail will look for for old +messages in the cur directory, while it will look for new mail in the new +directory. +

    + +In order to use maildir support it is better to set your inbox-path to the +value "#md/inbox" (without quotes). This assumes that your mail +delivery agent is delivering new mail to ~/Maildir/new. If the directory +where new mail is being delivered is not called "Maildir", you can set the +name of the subdirectory of home where it is being delivered in the maildir-location configuration +variable. Most of the time you will not have to worry about the +maildir-location variable, because it will probably be set by your +administrator in the pine.conf configuration file. +

    + +One of the advantages of the Maildir support of this version of Pine is +that you do not have to stop using folders in another styles (mbox, mbx, +etc.). This is desirable since the usage of a specific mail storage system +is a personal decision. Folders in the maildir format that are part of the +Mail collection will be recognized without any extra configuration of your +part. If your mail/ collection is located under the mail/ directory, then +creating a new maildir folder in this collection is done by pressing "A" +and entering the string "#driver.md/mail/newfolder". Observe that adding a +new folder as "newfolder" may not create such folder in maildir format. + +

    +If you would like to have all folders created in the maildir format by +default, you do so by adding a Maildir Collection. In order to convert +your current mail/ collection into a maildir collection, edit the +collection and change the path variable from "mail/" to +"#md/mail". In a maildir collection folders of any other format +are ignored. + +

    Finally, This version also has +support for the Courier style file system +when a maildir collection is accessed locally. + +

    +

    +<End of help on this topic> + + +====== h_config_literal_sig ===== + + +OPTION: Literal-Signature + + +

    OPTION: Literal-Signature

    + +With this option your actual signature, as opposed to +the name of a file containing your signature, +is stored in the Pine configuration file. +If this is defined it takes precedence over the Signature-File option. +

    + +This is simply a different way to store the signature. +The signature is stored inside your Pine configuration file instead of in +a separate file. +Tokens work the same way they do with the +Signature-File so look there for +help. +

    + +The Setup/Signature command on Pine's Main Menu will edit +the "Literal-Signature" by default. However, if no +"Literal-Signature" is defined and the file named in the +"Signature-File" option exists, then the latter will be used +instead. +

    + +The two character sequence \n (backslash followed by +the character n) will be used to signify a line-break in your signature. +You don't have to enter the \n, but it will be visible in the +SETUP CONFIGURATION window after you are done editing the signature. + +

    +

    +<End of help on this topic> + + +====== h_config_signature_file ===== + + +OPTION: Signature-File + + +

    OPTION: Signature-File

    + +If a Literal-Signature option is defined, +then this "Signature-File" option will be ignored. +You can tell that that is the case because the value of the +"Signature-File" will show up as +

    +

    <Ignored: using Literal-Signature instead>

    You may either use all Literal Signatures (signatures stored in your configuration file) throughout Pine, or all signature files. @@ -20853,6 +21333,41 @@ <End of help on this topic> +====== h_config_thread_sort_key ===== + + +OPTION: Thread-Sort-Key + + +

    OPTION: Thread-Sort-Key

    + +This option determines the order in which threads will be displayed. You +can choose from the following options. + +

    +

    + +

    Each type of sort may also be reversed. Normal default is by +"Thread". + +

    +

    +<End of help on this topic> + + ====== h_config_other_startup ===== @@ -21001,6 +21516,650 @@ <End of help on this topic> +====== h_config_compose_rules ===== + + +OPTION: Compose-Rule + + +

    OPTION: Compose-Rule

    + +

    At this time, this option is used to generate values for signature +files that is not possible to do with the use of +roles. + +

    For example, you can have a rule like:
    +_TO_ >> {Peter Flinstones} => _SIGNATURE_{~/.petersignature} + +

    This configuration option is just one of many that allow you to +override the value of some global configurations within Pine. There is a +help text explaining how to define all of them, which you can read by +following this link. + +

    <End of help on this topic> + + +====== h_config_forward_rules ===== + + +OPTION: Forward-Rule + + +

    OPTION: Forward-Rule

    + +

    At this time this option can be used to trim values of some fields, +for example it can be used in the following way: + +

    +_ROLE_ == {work} => _LCC_ := _TRIM_{_FORWARDFROM_ <_FORWARDADDRESS_>} + +

    This configuration option is just one of many that allow you to +override the value of some global configurations within Pine. There is a +help text explaining how to define all of them, which you can read by +following this link. + +

    <End of help on this topic> + + +====== h_config_index_rules ===== + + +OPTION: Index-Rule + + +

    OPTION: Index-Rule

    + +

    This option is used to supercede the value of the option index-format for specific folders. In +this form you can have different index-formats for different folders. For +example an entry here may be: + +

    +_FOLDER_ == {INBOX} => _INDEX_{IMAPSTATUS DATE FROM(33%) SIZE SUBJECT(67%)} + +

    This configuration option is just one of many that allow you to +override the value of some global configurations within Pine. There is a +help text explaining how to define all of them, which you can read by +following this link. + +

    <End of help on this topic> + + +====== h_config_replace_rules ===== + + +OPTION: Replace-Rule + + +

    OPTION: Replace-Rule

    + +

    This option is used to have Pine print different values for specific +tokens in the index-format. For example you +can replace strings like "To: newsgroup" by your name. + +

    Here are examples of possible rules:
    +_FOLDER_ != {sent-mail} && _NICK_ != {} => _FROM_ := _REPLACE_{_FROM_ (_NICK_)} + +

    or if you receive messages with tags that contain arbitrary numbers, and +you want them removed from the index (but not from the subject), use a rule +like the following
    +_FOLDER_ == {INBOX} => _SUBJECT_ := _REXTRIM_{\[some-tag-here #[0-9].*\]} + +

    You can also use this configuration option to remove specific strings of +the index display screen, so that you can trim unnecessary information in +your index. + +

    This configuration option is just one of many that allow you to +override the value of some global configurations within Pine. There is a +help text explaining how to define all of them, which you can read by +following this link. + +

    <End of help on this topic> + + +====== h_config_reply_leadin_rules ===== + + +OPTION: Reply-Leadin-Rule + + +

    OPTION: Reply-Leadin-Rule

    + +

    This option is used to have Pine generate a different +reply-leadin string dependent either on +the person you are replying to, or the folder where the message is being +replied is in, or both. + +

    Here there are examples of how this can be used. One can use the definition +below to post to newsgroups and the pine-info mailing list, say: +

    +_FOLDER_ << {pine-info;_NEWS_} => _REPLY_{*** _FROM_ _ADDRESS_("_FROM_" "" "(_ADDRESS_) ")wrote in_NEWS_("" " the" "") _FOLDER_ _NEWS_("" "list " "")_SMARTDATE_("Today" "today" "on _LONGDATE_"):} + +

    Here there is an example that one can use to change the reply indent string +to reply people that speak spanish. +

    +_FROM_{Condorito;Quico} => _REPLY_{*** _FROM_ (_ADDRESS_) escribió _SMARTDATE_("Today" "hoy" "en _LONGDATE_"):} + +

    This configuration option is just one of many that allow you to +override the value of some global configurations within Pine. There is a +help text explaining how to define all of them, which you can read by +following this link. + +

    <End of help on this topic> + + +====== h_config_resub_rules ===== + + +OPTION: Replace-Subject-Rule + + +

    OPTION: Replace-Subject-Rule

    + +

    This option is used to have Pine generate a different subject when +replying rather than the one Pine would generate automatically. + +

    Here there are a couple of examples about how to use this +configuration option: + +

    In order to have messages with empty subject to be replied with the message +"your message" use the rule
    +

    _SUBJECT_ == {} => _RESUB_{Re: your message}
    + +

    If you want to trim some parts of the subject when you reply use the +rule
    +

    _SUBJECT_ >> {[one];two} => _SUBJECT_ := _TRIM_{[;];two}
    + +

    this rule removes the brackets "[" and "]" whenever the string "[one]" +appears in it, it also removes the word "two" from it. + +

    Another example where you may want to use this rule is when you +correspond with people that change the reply string from "Re:" +to "AW:" or "Sv:". In this case a rule like
    +

    _SUBJECT_ >> {Sv: ;AW: } => _SUBJECT_ := _TRIM_{Sv: ;AW: }
    +

    +would eliminate undesired strings in replies. + +

    You can also use this configuration option to customize reply subjects +according to the sender of the message. + +

    This configuration option is just one of many that allow you to +override the value of some global configurations within Pine. There is a +help text explaining how to define all of them, which you can read by +following this link. + +

    <End of help on this topic> + + +====== h_config_sort_rules ===== + + +OPTION: Sort-Rule + + +

    OPTION: Sort-Rule

    + +

    This option is used to have Pine sort different folders in different orders +and thus override the value already set in the +sort-key configuration option. + +

    Here's an example of the way it can be used. In this case all incoming +folders are mailing lists, except for INBOX, so we sort INBOX by arrival +(which is the default type of sort), but we want all the rest of mailing +lists and newsgroups to be sorted by thread. + +

    +_COLLECTION_ >> {Incoming-Folders;News} && _FOLDER_ != {INBOX} => _SORT_{tHread} + +

    Another example could be
    +_FOLDER_ == {Mailing List} => _SORT_{Reverse tHread} + +

    This configuration option is just one of many that allow you to +override the value of some global configurations within Pine. There is a +help text explaining how to define all of them, which you can read by +following this link. + +

    <End of help on this topic> + + + +====== h_config_save_rules ===== + + +OPTION: Save-Rules + + +

    OPTION: Save-Rules

    + +

    This option is used to specify which folder should be used to save a +message depending either on the folder the message is in, who the message +is from, or text that the message contains in specific headers (Cc:, +Subject:, etc). + +

    If this option is set and the +auto-move-read-msgs configuration +option is also set then these definitions will be used to move messages +from your INBOX when exiting Pine. + +

    Here there are some examples
    +_FLAG_ >> {D} -> Trash
    +_FROM_ == {U2} -> Bono
    +_FOLDER_ == {comp.mail.pine} -> pine-stuff
    +_NICK_ != {} -> _NICK_/_NICK_
    +_DATEISO_ >> {02-10;02-11} -> archive-oct-nov-2002 + +

    This configuration option is just one of many that allow you to +override the value of some glob