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) + retur