diff -rc pine4.56/pico/basic.c pine4.56.fillpara/pico/basic.c *** pine4.56/pico/basic.c Mon Mar 10 19:27:40 2003 --- pine4.56.fillpara/pico/basic.c Mon Aug 11 13:10:05 2003 *************** *** 267,273 **** int f, n; /* default Flag & Numeric argument */ { int quoted, qlen; ! char qstr[NLINE], qstr2[NLINE]; if (n < 0) /* the other way...*/ return(gotoeop(f, -n)); --- 267,273 ---- int f, n; /* default Flag & Numeric argument */ { int quoted, qlen; ! char qstr[NLINE], qstr2[NLINE], ind_str[NLINE]; if (n < 0) /* the other way...*/ return(gotoeop(f, -n)); *************** *** 279,284 **** --- 279,295 ---- curwp->w_dotp = lback(curwp->w_dotp); curwp->w_doto = 0; } + + if (indent_match((glo_quote_str || (Pmaster && Pmaster->quote_str)) + ? (glo_quote_str ? glo_quote_str : Pmaster->quote_str) + : "", + 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 *************** *** 288,294 **** */ 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; qlen = quoted ? strlen(qstr) : 0; while(lback(curwp->w_dotp) != curbp->b_linep && llength(lback(curwp->w_dotp)) > qlen --- 299,305 ---- */ 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) : 0; qlen = quoted ? strlen(qstr) : 0; while(lback(curwp->w_dotp) != curbp->b_linep && llength(lback(curwp->w_dotp)) > qlen *************** *** 296,308 **** ? (quoted == quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, lback(curwp->w_dotp), ! qstr2, NLINE) && !strcmp(qstr, qstr2)) : 1) && lgetc(curwp->w_dotp, qlen).c != TAB && lgetc(curwp->w_dotp, qlen).c != ' ') curwp->w_dotp = lback(curwp->w_dotp); if(n){ /* keep looking */ if(lback(curwp->w_dotp) == curbp->b_linep) --- 307,361 ---- ? (quoted == quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, lback(curwp->w_dotp), ! qstr2, NLINE, 0) && !strcmp(qstr, qstr2)) : 1) + && !indent_match((glo_quote_str + || (Pmaster && Pmaster->quote_str)) + ? (glo_quote_str ? glo_quote_str : Pmaster->quote_str) + : "", + 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 indented, i, j; + + /* + * for the following test we need to have the raw values, + * not the processed values + */ + if (glo_quote_str || (Pmaster && Pmaster->quote_str)){ + quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, + curwp->w_dotp, qstr, NLINE, 1); + quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, + lback(curwp->w_dotp), qstr2, NLINE, 1); + } + else + qstr[0] = qstr2[0] = '\0'; + indented = indent_match((glo_quote_str || (Pmaster && Pmaster->quote_str)) + ? (glo_quote_str ? glo_quote_str : Pmaster->quote_str) + : "", + lback(curwp->w_dotp), + ind_str, NLINE, 1); + if (strlenis(qstr2) + strlenis(ind_str) < strlenis(qstr)) + indented = 0; /* Hack, so that it won't return one more line */ + 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 (!qstr2[i] && !qstr[j] && indented) + curwp->w_dotp = lback(curwp->w_dotp); + } + if(n){ /* keep looking */ if(lback(curwp->w_dotp) == curbp->b_linep) *************** *** 339,346 **** int f, n; /* default Flag & Numeric argument */ { ! int quoted, qlen; ! char qstr[NLINE], qstr2[NLINE]; if (n < 0) /* the other way...*/ return(gotobop(f, -n)); --- 392,399 ---- int f, n; /* default Flag & Numeric argument */ { ! int quoted, qlen, indented; ! char qstr[NLINE], qstr2[NLINE], ind_str[NLINE]; if (n < 0) /* the other way...*/ return(gotobop(f, -n)); *************** *** 353,358 **** --- 406,463 ---- 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){ + int i,j; + + if (glo_quote_str || (Pmaster && Pmaster->quote_str)){ + quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, + curwp->w_dotp, qstr, NLINE, 1); + quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, + lforw(curwp->w_dotp), qstr2, NLINE, 1); + } + else + qstr[0] = qstr2[0] = '\0'; + indented = indent_match((glo_quote_str || (Pmaster && Pmaster->quote_str)) + ? (glo_quote_str ? glo_quote_str + : Pmaster->quote_str) : "", 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){ + if (indent_match((glo_quote_str + || (Pmaster && Pmaster->quote_str)) + ? (glo_quote_str ? glo_quote_str + : Pmaster->quote_str) : "", + 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 * *************** *** 361,367 **** */ 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); qlen = quoted ? strlen(qstr) : 0; while(curwp->w_dotp != curbp->b_linep --- 466,472 ---- */ 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) : 0); qlen = quoted ? strlen(qstr) : 0; while(curwp->w_dotp != curbp->b_linep *************** *** 370,378 **** ? (quoted == quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, lforw(curwp->w_dotp), ! qstr2, NLINE) && !strcmp(qstr, qstr2)) : 1) && lgetc(lforw(curwp->w_dotp), qlen).c != TAB && lgetc(lforw(curwp->w_dotp), qlen).c != ' ') curwp->w_dotp = lforw(curwp->w_dotp); --- 475,489 ---- ? (quoted == quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, lforw(curwp->w_dotp), ! qstr2, NLINE, 0) && !strcmp(qstr, qstr2)) : 1) + && !indent_match((glo_quote_str + || (Pmaster && Pmaster->quote_str)) + ? (glo_quote_str ? glo_quote_str + : Pmaster->quote_str) : "", + 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); diff -rc pine4.56/pico/efunc.h pine4.56.fillpara/pico/efunc.h *** pine4.56/pico/efunc.h Wed Apr 9 11:35:04 2003 --- pine4.56.fillpara/pico/efunc.h Mon Aug 11 12:19:01 2003 *************** *** 336,341 **** extern int forwword PROTO((int, int)); extern int fillpara PROTO((int, int)); extern int inword PROTO((void)); ! extern int quote_match PROTO((char *, LINE *, char *, int)); #endif /* EFUNC_H */ --- 336,345 ---- extern int forwword PROTO((int, int)); extern int fillpara PROTO((int, int)); extern int inword PROTO((void)); ! extern int quote_match PROTO((char *, LINE *, char *, int, int)); ! extern void flatten_qstring PROTO((QSTRING_S *, char *)); ! extern void free_qs PROTO((QSTRING_S **)); ! extern QSTRING_S *do_quote_match PROTO((char *, int[], int[], int[], int)); ! extern QSTRING_S *copy_qs PROTO((QSTRING_S *)); #endif /* EFUNC_H */ diff -rc pine4.56/pico/line.c pine4.56.fillpara/pico/line.c *** pine4.56/pico/line.c Mon Mar 10 19:27:42 2003 --- pine4.56.fillpara/pico/line.c Mon Aug 11 12:19:01 2003 *************** *** 635,641 **** 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; for(; n < llength(line); n++) --- 635,641 ---- n = ((glo_quote_str || (Pmaster && Pmaster->quote_str)) && quote_match(glo_quote_str ? glo_quote_str : Pmaster->quote_str, ! line, qstr, NLINE, 1)) ? strlen(qstr) : 0; for(; n < llength(line); n++) diff -rc pine4.56/pico/pico.h pine4.56.fillpara/pico/pico.h *** pine4.56/pico/pico.h Thu May 29 12:36:04 2003 --- pine4.56.fillpara/pico/pico.h Mon Aug 11 12:19:01 2003 *************** *** 338,343 **** --- 338,359 ---- 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) diff -rc pine4.56/pico/random.c pine4.56.fillpara/pico/random.c *** pine4.56/pico/random.c Thu Dec 12 14:24:48 2002 --- pine4.56.fillpara/pico/random.c Mon Aug 11 12:19:01 2003 *************** *** 340,346 **** backchar(FALSE, 1); dotp = curwp->w_dotp; ! gotobop(FALSE, 1); /* then go to the top of the para */ curwp->w_doto = 0; getregion(®ion, dotp, llength(dotp)); --- 340,347 ---- backchar(FALSE, 1); dotp = curwp->w_dotp; ! 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; getregion(®ion, dotp, llength(dotp)); diff -rc pine4.56/pico/word.c pine4.56.fillpara/pico/word.c *** pine4.56/pico/word.c Fri Apr 4 14:22:56 2003 --- pine4.56.fillpara/pico/word.c Tue Aug 12 18:09:59 2003 *************** *** 360,404 **** && isalnum((unsigned char)lgetc(curwp->w_dotp, curwp->w_doto).c)); } /* ! * Return number of quotes if whatever starts the line matches the quote string */ ! quote_match(q, l, buf, buflen) char *q; LINE *l; char *buf; int buflen; { ! register int i, n, j, qb; ! *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'; ! } } ! if(j > llength(l)) ! return(n); ! else if(qb && lgetc(l, j).c == ' ') ! j++; } ! return(n); /* never reached */ } fillpara(f, n) /* Fill the current paragraph according to the current fill column */ --- 360,1327 ---- && isalnum((unsigned char)lgetc(curwp->w_dotp, curwp->w_doto).c)); } + /* Support of indentation of paragraphs */ + #define is_indent_char(c) (((c) == '.' || (c) == '}' || (c) == RPAREN || \ + (c) == '*' || (c) == ' ' || is_a_digit(c) || \ + (c) == TAB || (c) == '-' || (c) == ']') ? 1 : 0) + #define allowed_after_digit(c,word,k) ((((c) == '.' && \ + allowed_after_period(next((word),(k)))) ||\ + (c) == RPAREN || (c) == '}' || (c) == ']' ||\ + (c) == ' ' || (c) == TAB || \ + is_a_digit(c) || \ + ((c) == '-' ) && \ + allowed_after_dash(next((word),(k)))) \ + ? 1 : 0) + #define allowed_after_period(c) (((c) == RPAREN || (c) == '}' || (c) == ']' ||\ + (c) == ' ' || (c) == TAB || (c) == '-' || \ + is_a_digit(c)) ? 1 : 0) + #define allowed_after_parenth(c) (((c) == ' ' || (c) == TAB) ? 1 : 0) + #define allowed_after_space(c) (((c) == ' ' || (c) == TAB) ? 1 : 0) + #define allowed_after_braces(c) (((c) == ' ' || (c) == TAB) ? 1 : 0) + #define allowed_after_star(c) (((c) == ' ' || (c) == TAB || (c) == RPAREN\ + || (c) == ']' || (c) == '}') ? 1 : 0) + #define allowed_after_dash(c) (((c) == ' ' || (c) == TAB || \ + is_a_digit(c)) ? 1 : 0) + + int is_indent PROTO((int[], int)); + int indent_match PROTO(( char *, LINE *, char *, int, int)); + + /* Extended justification support */ + + #define is_cquote(c) ((c) == '>' || (c) == RPAREN || \ + (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 is_cletter(c) (((c) >= 'a') && ((c) <= 'z')) ||\ + (((c) >= 'A') && ((c) <= 'Z')) + #define is_cnumber(c) ((c) >= '0' && (c) <= '9') + #define allwd_after_word(c) (((c) == ' ') || ((c) == '>') || is_cletter(c)) + #define before(word,i) (((i) > 0) ? (word)[(i) - 1] : 0) + #define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) + #define now(word,i) (word[(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 *, 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 *, int[], QStrType, int, int, int, int)); + QSTRING_S *remove_qsword PROTO((QSTRING_S *)); + 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 *, int [], int)); + int strlenis PROTO((char *)); + void flatten_qstring PROTO((QSTRING_S *, char *)); + void linencpy PROTO((int [], LINE *, int)); + + /* + * 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; + int 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); + + qstring->value = (char *) malloc((beginw+1)*sizeof(char)); + for (i = 0; (i < beginw) && (qstring->value[i] = word[i]); i++); + qstring->value[i] = '\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((unsigned char) cl->value[i])) + i--; + i += (i < l) ? 2 : 1; + cl->value[i] = '\0'; + } + } + + return cl; + } + /* ! * 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. ! */ ! 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 ! tl = qs_normal_part(cl); ! } ! 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) ! QSTRING_S *qs; ! char *buff; ! { ! int i = 0, j; ! ! for (; qs; qs = qs->next) ! for (j = 0; (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) ! char *qs; ! int word[NSTRING]; ! { ! 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 TAB: ! case ' ': { QSTRING_S *nextqs, *testqs = NULL; ! int j; ! ! for (; isspace(word[i]); i++); ! nextqs = is_quote(qs,word+i); ! /* ! * 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)); ! for (j = 0; (j < i) && (qstring->value[j] = word[j]); j++); ! qstring->value[j] = '\0'; ! qstring->qstype = qsNormal; ! ! qstring->next = testqs; ! ! return qstring; ! } ! break; ! ! case ';': ! case ':': /* colon */ ! case '~': if (is_cquote(nxt = next(word,i)) ! || ((i != 0) && is_space(nxt)) ! || is_cquote(prev = before(word,i)) ! || (is_space(prev))) ! i++; ! else ! if (i == 0) ! return qs_add(qs, word, qsChar, i, i, 1, 1); ! else ! finished++; ! break; ! ! 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 '+' : /* 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 '#' : 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_cword(nxt = next(word,j)) && !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)){ ! int offset; ! QStrType qstype; ! ! offset = is_cquote(next(word,j)) ? 2 ! : ((is_space(next(word,j)) ! && is_cquote(next(word,j+1))) ? 3 : -1); ! ! qstype = is_cquote(next(word,j)) ! ? (is_qsword(next(word,j)) ? qsWord : qsString) ! : ((is_space(next(word,j)) ! && is_cquote(next(word,j+1))) ! ? (is_qsword(next(word,j+1)) ! ? qsWord : qsString) : qsString); ! ! if (offset > 0) ! return qs_add(qs, word, qstype, i, j, offset, 1); ! } ! finished++; ! } ! 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) ! int word[NSTRING]; ! LINE *l; ! int buflen; ! { ! int i; ! for (i = 0;(i < buflen) && (i < llength(l)) && (word[i] = lgetc(l,i).c); i++); ! word[buflen - 1] = 0; ! } ! ! ! ! int ! isaword(word,i,j) ! int word[NSTRING]; ! int i; ! int j; ! { ! return i <= j && is_cletter(word[i]) ? ! (i < j ? isaword(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, buf, buflen, raw) char *q; LINE *l; char *buf; int buflen; + int raw; { ! int 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; ! if (nl) ! linencpy(NLine, nl, NSTRING); ! if (pl) ! linencpy(PLine, pl, NSTRING); ! linencpy(GLine, l, NSTRING); ! ! return do_quote_match(q,GLine, NLine, PLine, raw); ! } ! ! /* ! * Return number of quotes if whatever starts the line matches the quote ! * string ! */ ! quote_match(q, l, buf, buflen, raw) ! char *q; ! LINE *l; ! char *buf; ! int buflen; ! int raw; ! { ! QSTRING_S *qs; ! ! qs = qs_quote_match(q, l, buf, buflen, raw); ! flatten_qstring(qs, buf); ! if (qs) ! free_qs(&qs); ! ! 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); ! } ! ! ! QSTRING_S * ! do_quote_match(q,GLine, NLine, PLine, raw) ! char *q; ! int GLine[NSTRING]; ! int NLine[NSTRING]; ! int PLine[NSTRING]; ! int raw; ! { ! QSTRING_S *cl, *nl = NULL, *pl = NULL; ! int c, n, p,i, j, NewC, NewN, clength, same = 0; ! char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING]; ! int emptynl = 0, emptypl = 0; ! ! cl = is_quote(q,GLine); /* Current or Given line */ ! ! if (!cl) /* if nothing in, nothing out */ ! return cl; ! ! if (NLine && NLine[0]) ! nl = is_quote(q,NLine); /* Next Line */ ! if (PLine && PLine[0]) ! pl = is_quote(q,PLine); /* Previous Line */ ! ! /* ! * Experimental: 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); ! emptypl = (!PLine || !PLine[0] || ! (pl && value_is_space(pbuf)) && !PLine[strlen(pbuf)]) ? 1 : 0; ! if (emptypl){ ! flatten_qstring(nl, nbuf); ! emptynl = (!NLine || !NLine[0] || ! (nl && value_is_space(nbuf) && !NLine[strlen(nbuf)])) ? 1 : 0; ! if (emptynl){ ! cl = remove_qsword(cl); ! free_qs(&nl); ! free_qs(&pl); ! ! 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 = (emptynl && emptypl) ? cl : fix_qstring(cl, nl, pl); ! nl = trim_qs_from_cl(cl, nl, NULL); ! pl = trim_qs_from_cl(cl, NULL, pl); ! flatten_qstring(cl, buf); ! flatten_qstring(nl, nbuf); ! flatten_qstring(pl, pbuf); ! ! if (raw){ /* if we are asked for the raw string */ ! free_qs(&nl); ! free_qs(&pl); ! ! return cl; /* return now! */ ! } ! /* ! * 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) ! */ ! ! 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){ ! for (j = 0; (j < n) && (GLine[j] == NLine[j]); j++); ! clength = j; ! if (j < n){ /* see if buf and nbuf are padded with spaces and tabs */ ! for (i = clength; i < n && isspace(NLine[i]); i++); ! if (i == n){ ! for (i = clength; i < c && isspace(GLine[i]); i++); ! if (i == c) ! j = n; ! } } ! if (j == n){ ! 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. ! */ ! ! for (j = 0; (j < p) && (GLine[j] == PLine[j]); j++); ! if (((p == c) && ((j != p) && NLine[n])) ! || ((p != c) && NLine[n])){ ! 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; ! int 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; } ! 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; + + if (qstr && *qstr){ + for (i = 0; qstr[i]; i++) + rv += ((qstr[i] == TAB) ? (~rv & 0x07) + 1 : 1); + } + return rv; + } + fillpara(f, n) /* Fill the current paragraph according to the current fill column */ *************** *** 405,415 **** 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]; LINE *eopline; REGION region; if(curbp->b_mode&MDVIEW){ /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ --- 1328,1340 ---- int f, n; /* deFault flag and Numeric argument */ { ! 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 */ *************** *** 429,442 **** /* and back to the beginning of the paragraph */ gotobop(FALSE, 1); ! /* 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; /* let yank() know that it may be restoring a paragraph */ thisflag |= CFFILL; --- 1354,1427 ---- /* and back to the beginning of the paragraph */ gotobop(FALSE, 1); + setimark(FALSE, 1); /* Remember this spot in case we unjustify */ ! /* ! * 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((glo_quote_str || (Pmaster && Pmaster->quote_str)) ! ? (glo_quote_str ? glo_quote_str : Pmaster->quote_str) ! : "", curwp->w_dotp, ind_str, NSTRING, 0); ! qstrfl = (quote_match((glo_quote_str || (Pmaster && Pmaster->quote_str)) ! ? (glo_quote_str ? glo_quote_str : Pmaster->quote_str) ! : ">", curwp->w_dotp, qstrfl2, NSTRING,0) ! && *qstrfl2) ? qstrfl2 : NULL; ! if (qstrfl){ ! if (glo_quote_str || (Pmaster && Pmaster->quote_str)) ! for (; (i < NSTRING) && (quoid[i] = qstrfl[i]); i++); ! else{ ! for (; (i < NSTRING) && qstrfl[i] && (quoid[i] = ' '); i++); ! qstrfl[0] = '\0'; ! } ! } ! 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) ! for (;isspace(quoid[qi - 1]); qi--); /* strip trailing spaces */ ! quoid[qi] = '\0'; /* we have closed quoid at "X" in the first line */ ! ! if (strlenis(quoid) > fillcol) ! return FALSE; /* Too wide, we can't justify this! */ ! ! /* determine if we're justifying quoted text or not */ qstr = ((glo_quote_str || (Pmaster && Pmaster->quote_str)) ! && quote_match(glo_quote_str ? glo_quote_str : ! Pmaster->quote_str, ! curwp->w_dotp, qstr2, NSTRING, 0) ! && *qstr2) ? qstr2 : NULL; ! qlen = qstr ? strlen(qstr) : 0; ! qlenis = qstr ? strlenis(qstr) : 0; ! ! /* ! * Compare effective lengths of quoid and qstr to decide how much space ! * we need to use to pad with. ! */ ! if (indlen && ((j = strlenis(quoid) - strlenis(qstr)) > 0)){ ! pqi = qstr ? strlen(qstr) : 0; ! for (i = 0; (i < j) && (qstr2[pqi + i] = ' '); i++); ! if (isspace(ind_str[indlen - 1])) ! qstr2[pqi + i++] = ' '; ! qstr2[pqi + i] = '\0'; ! if (!qstr) ! qstr = qstr2; ! } /* let yank() know that it may be restoring a paragraph */ thisflag |= CFFILL; *************** *** 454,471 **** return(FALSE); /* Now insert it back wrapped */ ! spaces = word_len = line_len = same_word = 0; /* Beginning with leading quoting... */ ! if(qstr){ ! while(qstr[line_len]) ! linsert(1, qstr[line_len++]); line_last = ' '; /* no word-flush space! */ } /* ...and leading white space */ ! for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){ linsert(1, line_last = c); line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); } --- 1439,1476 ---- return(FALSE); /* Now insert it back wrapped */ ! spaces = word_len = line_len = same_word = i = 0; /* Beginning with leading quoting... */ ! if(qstrfl){ ! while((tbuf[line_len] = qstrfl[line_len]) == fremove(line_len)) ! linsert(1, qstrfl[line_len++]); ! /* ! * The only way that at the end of the above loop we don't have ! * line_len == qlen is that there are trailing spaces or TABS ! * which could not be accounted in the qstr in is_quote or other ! * functions before we got here. Now we enter the common part of ! * the quote string in the first line and the rest is only spaces ! * (or TABS) that need to be entered, which are left to the loop ! * following this "if" statement ! */ ! i = line_len; /* start next loop from here */ ! tbuf[line_len] = '\0'; /* closing tbuf... */ ! line_len = strlenis(tbuf); /* we demand a recount! */ ! line_last = ' '; /* no word-flush space! */ ! } + /* ...followed by the indent string, if any */ + if (indlen){ + for (i, j = 0; (c = fremove(i)) && ind_str[j]; i++, j++){ + linsert(1, line_last = c); + line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); + } line_last = ' '; /* no word-flush space! */ } /* ...and leading white space */ ! for(i; (c = fremove(i)) == ' ' || c == TAB; i++){ linsert(1, line_last = c); line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); } *************** *** 484,490 **** default : if(spaces){ /* flush word? */ ! if((line_len - qlen > 0) && line_len + word_len + 1 > fillcol && (line_len = fpnewline(qstr))) line_last = ' '; /* no word-flush space! */ --- 1489,1495 ---- default : if(spaces){ /* flush word? */ ! if((line_len - qlenis > 0) && line_len + word_len + 1 > fillcol && (line_len = fpnewline(qstr))) line_last = ' '; /* no word-flush space! */ *************** *** 512,518 **** if(word_len + 1 >= NSTRING){ /* Magic! Fake that we output a wrapped word */ ! if((line_len - qlen > 0) && !same_word++) line_len = fpnewline(qstr); line_len += word_len; --- 1517,1523 ---- if(word_len + 1 >= NSTRING){ /* Magic! Fake that we output a wrapped word */ ! if((line_len - qlenis > 0) && !same_word++) line_len = fpnewline(qstr); line_len += word_len; *************** *** 529,536 **** } if(word_len){ ! if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol)) ! (void) fpnewline(qstr); else if(line_len && !isspace((unsigned char) line_last)) linsert(1, ' '); --- 1534,1543 ---- } if(word_len){ ! if((line_len - qlenis > 0) && (line_len + word_len + 1 > fillcol)){ ! if (line_len && (line_len != qlenis)) ! (void) fpnewline(qstr); ! } else if(line_len && !isspace((unsigned char) line_last)) linsert(1, ' '); *************** *** 558,562 **** --- 1565,1672 ---- for(len = 0; quote && *quote; quote++, len++) linsert(1, *quote); + quote -= len; /* go back */ + len = strlenis(quote); /* and recount */ return(len); + } + + int + is_indent (word, plb) + int word[NSTRING]; + int plb; + { + int i = 0, finished = 0, c, nxt, j, k, digit = 0, bdigits = -1; + + if (!word || !word[0]) + return i; + + for (i = 0, j = 0; isspace(word[i]); i++, j++); + while ((i < NSTRING - 2) && !finished){ + switch (c = now(word,i)){ + case TAB: + case ' ': for (; isspace(word[i]); i++); + if (!is_indent_char(now(word,i))) + finished++; + break; + + case '.' : + case ']' : + case '*' : + case '}' : + case '-' : + case RPAREN: + nxt = next(word,i); + if (((c == '.') && allowed_after_period(nxt)) + || ((c == '*') && allowed_after_star(nxt)) + || ((c == '}') && allowed_after_braces(nxt)) + || ((c == '-') && allowed_after_dash(nxt)) + || ((c == RPAREN) && allowed_after_parenth(nxt)) + || ((c == ']') && allowed_after_parenth(nxt))) + i++; + else + finished++; + break; + + default : if (is_a_digit(c)){ + if (bdigits < 0) + bdigits = i; /* first digit */ + if (plb){ + for (k = i; is_a_digit(now(word,k)); k++); + if (allowed_after_digit(now(word,k),word,k)){ + i = k; + } + else{ + i = bdigits; + finished++; + } + } + else{ + i -= (i > 0) ? 1 : 0; + finished++; + } + } + else + finished++; + break; + } + } + if (i == j) + i = 0; /* there must be something more than spaces in an indent string */ + return i; + } + + + /* + * If there is an indent string this function returns + * its length + */ + int + indent_match(q, l, buf, buflen, raw) + char *q; + LINE *l; + char *buf; + int buflen; + int raw; + { + int GLine[NSTRING] = {0}; + int i, j, k, plb; + + k = quote_match(q,l, buf, buflen, raw); + + linencpy(GLine, l, NSTRING); + + plb = (lback(l) != curbp->b_linep) ? lisblank(lback(l)) : 1; + if (!plb){ + i = llength(lback(l)) - 1; + for (; i >= 0 && isspace(lgetc(lback(l), i).c); i--); + if (lgetc(lback(l), i).c == '.' || lgetc(lback(l), i).c == ':') + plb++; + } + + i = is_indent(GLine+k, plb); + + for (j = 0; (j < i) && (buf[j] = GLine[j + k]); j++); + buf[j] = '\0'; + + return i; } diff -rc pine4.56/pine/filter.c pine4.56.fillpara/pine/filter.c *** pine4.56/pine/filter.c Mon Apr 7 12:36:32 2003 --- pine4.56.fillpara/pine/filter.c Mon Aug 11 20:46:32 2003 *************** *** 7205,7210 **** --- 7205,7369 ---- + #define GF_ADD_QUOTED_LINE(f, line, ins) \ + { \ + LT_INS_S *insp;\ + unsigned char ch;\ + register char *cp;\ + register int l;\ + \ + if (line){\ + color_a_quote((f)->n++, line, &ins,\ + ((LINETEST_S *) (f)->opt)->local);\ + for(insp = ins, cp = line; *cp ; ){\ + while(insp && cp == insp->where){\ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((f)->next, ch);\ + }\ + insp = insp->next;\ + }\ + GF_PUTC((f)->next, *cp);\ + cp++;\ + }\ + while(insp){\ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((f)->next, ch);\ + }\ + insp = insp->next;\ + }\ + gf_line_test_free_ins(&ins);\ + }\ + GF_PUTC(f->next, '\015');\ + GF_PUTC(f->next, '\012');\ + } + + + void + gf_quote_test(f, flg) + FILTER_S *f; + int flg; + { + register char *p = f->linep; + register char *eobuf = GF_LINE_TEST_EOB(f); + static char *oldline = NULL; + char *line = NULL; + int i, j; + LT_INS_S *ins = NULL; + GF_INIT(f, f->next); + + if(flg == GF_DATA){ + register unsigned char c; + register int state = f->f1; + + while(GF_GETC(f, c)){ + + if(state == 2){ /* two full lines read */ + state = 0; + if(c == '\012'){ + + *p = '\0'; /* tie f->line off */ + /* first process the second line of an old line */ + if (oldline && oldline[0]){ + for (i = 0; oldline[i] && oldline[i] != '\015'; i++); + if (oldline[i]) + line = oldline + i + 2; + for (i = 0; ((f)->line) && ((f)->line)[i] + && (i < LINE_TEST_BLOCK) + && (i < SIZEOF_20KBUF) + && (((f)->line)[i] != '\015') + && (tmp_20k_buf[i] = ((f)->line)[i]); i++); + tmp_20k_buf[i] = '\0'; + GF_ADD_QUOTED_LINE(f, line, ins); + } + + /* now we process the first line */ + line = (f)->line; + oldline = cpystr(line); + for (i = 0; line[i] && line[i] != '\015'; i++); + if (line[i]){ + line[i] = '\0'; + i += 2; + } + for (j = 0; ((f)->line) && ((f)->line)[i + j] + && (((f)->line)[i + j] != '\015') + && (tmp_20k_buf[j] = ((f)->line)[i + j]) + ; j++); + tmp_20k_buf[j] = '\0'; + + GF_ADD_QUOTED_LINE(f, line, ins); + + p = (f)->line; + continue; + } + else + GF_LINE_TEST_ADD(f, '\015'); + } + + if(c == '\015'){ + state++; + if (state == 1) + GF_LINE_TEST_ADD(f, c); + } + else + GF_LINE_TEST_ADD(f, c); + } + + f->f1 = state; + GF_END(f, f->next); + } + else if(flg == GF_EOD){ + int i; + + *p = '\0'; /* tie f->line off */ + /* first process the second line of an old line */ + if (oldline && oldline[0]){ + for (i = 0; oldline[i] && oldline[i] != '\015'; i++); + if (oldline[i]) + line = oldline + i + 2; + for (i = 0; ((f)->line) && ((f)->line)[i] + && (i < LINE_TEST_BLOCK) + && (i < SIZEOF_20KBUF) + && (((f)->line)[i] != '\015') + && (tmp_20k_buf[i] = ((f)->line)[i]); i++); + tmp_20k_buf[i] = '\0'; + GF_ADD_QUOTED_LINE(f, line, ins); + } + + /* now we process the first line */ + line = (f)->line; + oldline = cpystr(line); + for (i = 0; line[i] && line[i] != '\015'; i++); + if (line[i]){ + line[i] = '\0'; + i += 2; + } + for (j = 0; ((f)->line) && ((f)->line)[i + j] + && (((f)->line)[i + j] != '\015') + && (tmp_20k_buf[j] = ((f)->line)[i + j]) ; j++); + tmp_20k_buf[j] = '\0'; + + GF_ADD_QUOTED_LINE(f, line, ins); + if (oldline) + fs_give((void **) &oldline); + fs_give((void **) &f->line); /* free line buffer */ + fs_give((void **) &f->opt); /* free test struct */ + GF_FLUSH(f->next); + (*f->next->f)(f->next, GF_EOD); + } + else if(flg == GF_RESET){ + dprint(9, (debugfile, "-- gf_reset line_test\n")); + f->f1 = 0; /* state */ + f->n = 0L; /* line number */ + f->f2 = LINE_TEST_BLOCK; /* size of alloc'd line */ + f->line = p = (char *) fs_get(f->f2 * sizeof(char)); + } + + f->linep = p; + } + + /* * this simple filter accumulates characters until a newline, offers it * to the provided test function, and then passes it on. It assumes *************** *** 7228,7234 **** if(state){ state = 0; if(c == '\012'){ ! int done; GF_LINE_TEST_TEST(f, done); --- 7387,7398 ---- if(state){ state = 0; if(c == '\012'){ ! int done, i, j = 0; ! ! for (i = 0; op && op[i] && (i < LINE_TEST_BLOCK) && ! (i < SIZEOF_20KBUF) && (op[i] != '\015') && ! (tmp_20k_buf[i] = op[i]); i++); ! tmp_20k_buf[i] = '\0'; GF_LINE_TEST_TEST(f, done); diff -rc pine4.56/pine/mailview.c pine4.56.fillpara/pine/mailview.c *** pine4.56/pine/mailview.c Thu May 29 10:27:58 2003 --- pine4.56.fillpara/pine/mailview.c Tue Aug 12 18:24:07 2003 *************** *** 97,102 **** --- 97,103 ---- static char *g_editorial_prefix, *g_editorial_postfix; + static char *prefix; /* * Def's to help in sorting out multipart/alternative *************** *** 355,360 **** --- 356,362 ---- int pcpine_resize_scroll PROTO((void)); int pcpine_view_cursor PROTO((int, long)); #endif + int is_word PROTO((char [], int, int)); *************** *** 433,438 **** --- 435,451 ---- else ps->unseen_in_view = !mc->seen; + prefix = reply_quote_str(env); + /* Make sure the prefix is not only made of spaces, so that we do not + * paint the screen incorrectly + */ + if (prefix && *prefix){ + int i; + for (i = 0; prefix[i] == ' '; i++); + if (i == strlen(prefix)) + fs_give((void **)&prefix); + } + #if defined(DOS) && !defined(WIN32) /* * Handle big text for DOS here. *************** *** 585,590 **** --- 598,605 ---- } while(ps->next_screen == SCREEN_FUN_NULL); + if (prefix && *prefix) + fs_give((void **)&prefix); if(we_cancel) cancel_busy_alarm(-1); } *************** *** 1542,1548 **** && pico_usingcolor() && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR){ ! gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL)); } gf_link_filter(gf_wrap, gf_wrap_filter_opt(ps_global->ttyo->screen_cols, --- 1557,1563 ---- && pico_usingcolor() && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR){ ! gf_link_filter(gf_quote_test, gf_line_test_opt(color_a_quote, NULL)); } gf_link_filter(gf_wrap, gf_wrap_filter_opt(ps_global->ttyo->screen_cols, *************** *** 3269,3275 **** --- 3284,3308 ---- struct quote_colors *next; }; + #define is_letter(c) (((c) >= 'a' && (c) <= 'z') || \ + ((c) >= 'A' && (c) <= 'Z')) + + #define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) + + #define single_level(c) (((c) == '>') || ((c) == '|') || ((c) == '~') || \ + ((c) == ']')) + int + is_word (buf, i, j) + char buf[NSTRING]; + int i; + int j; + { + return i <= j && is_letter(buf[i]) ? + (i < j ? is_word(buf,i+1,j) : 1) : 0; + } + + int color_a_quote(linenum, line, ins, local) long linenum; char *line; *************** *** 3276,3292 **** LT_INS_S **ins; void *local; { ! int countem = 0; struct variable *vars = ps_global->vars; char *p; struct quote_colors *colors = NULL, *cp, *next; COLOR_PAIR *col = NULL; p = line; ! while(isspace((unsigned char)*p)) ! p++; ! if(p[0] == '>'){ struct quote_colors *c; /* --- 3309,3364 ---- LT_INS_S **ins; void *local; { ! int countem = 0, i, j = 0; struct variable *vars = ps_global->vars; char *p; + char buf[NSTRING] = {'\0'}; struct quote_colors *colors = NULL, *cp, *next; COLOR_PAIR *col = NULL; + static int GLine[NSTRING] = {0}; + static int PLine[NSTRING] = {0}; + int NLine[NSTRING] = {0}; + QSTRING_S *qs; + if (linenum != 0){ + for (i = 0; GLine[i] && (PLine[i] = GLine[i]); i++); + PLine[i] = 0; + } + + for (i = 0; tmp_20k_buf[i] && (tmp_20k_buf[i] != '\015') && (i < NSTRING) + && (i < SIZEOF_20KBUF) + && (NLine[i] = (int) tmp_20k_buf[i]); i++); + NLine[i] = 0; + p = line; ! if (p){ ! for (i = 0; (i < NSTRING) && ((GLine[i] = (int)p[i]) != 0); i++); ! GLine[NSTRING - 1] = 0; ! } ! else ! GLine[0] = 0; ! qs = do_quote_match(prefix && *prefix ? prefix : ">", ! GLine, NLine, PLine, 0); ! flatten_qstring(qs, buf); ! i = (buf && buf[0] ? strlen(buf) : 0) - 1; ! free_qs(&qs); ! for (; (i > 0) && isspace(buf[i]); i--); ! buf[++i] = '\0'; ! /* do not paint an extra level for a line with a >From string at the ! * begining of it ! */ ! if (buf[0] && (strlen(buf) + strlen("From ") + 1 < NSTRING)){ ! i = strlen(buf); ! if (strstr(p,(char *)strcat(buf,"From ")) == p) ! buf[i - 1] = '\0'; ! else ! buf[i] = '\0'; ! } ! ! for (i = 0; isspace((unsigned char)buf[i]); i++) ! p++; ! if(buf[i]){ struct quote_colors *c; /* *************** *** 3335,3341 **** free_color_pair(&col); cp = NULL; ! while(*p == '>'){ cp = (cp && cp->next) ? cp->next : colors; if(countem > 0) --- 3407,3413 ---- free_color_pair(&col); cp = NULL; ! while(buf[i]){ cp = (cp && cp->next) ? cp->next : colors; if(countem > 0) *************** *** 3345,3352 **** countem = (countem == 1) ? 0 : countem; ! for(p++; isspace((unsigned char)*p); p++) ; } if(colors){ --- 3417,3468 ---- countem = (countem == 1) ? 0 : countem; ! if (!single_level(buf[i])){ ! if (is_letter(buf[i])){ /* if there's a word */ ! for (j = i; buf[j] && is_letter(buf[j]); j++); ! j += isspace(buf[j]) ? 2 : 1; ! } ! else{ ! switch(buf[i]){ ! case ':' : ! if (next(buf,i) != RPAREN) ! j = i + 1; ! else ! j = i + 2; ! break; ! ! case '-' : ! if (next(buf,i) != '-') ! j = i + 2; ! else ! j = i + 3; ! break; ! ! case '+' : ! case '*' : ! if (next(buf,i) != ' ') ! j = i + 2; ! else ! j = i + 3; ! break; ! ! default : ! for (j = i; buf[j] && !isspace(buf[j]) ! && (!single_level(buf[i]) && !is_letter(buf[j])); j++); ! j += isspace(buf[j]) ? 1 : 0; ! break; ! } ! } ! p += j - i; ! } ! else{ /* count every '>' for one level */ ! j = ++i; ! p++; ! } ! ! for(; isspace((unsigned char)*p); p++) ; + for(i = j; isspace(buf[i]); i++); } if(colors){ *************** *** 3422,3438 **** void *is_in_sig; { struct variable *vars = ps_global->vars; ! int *in_sig_block; COLOR_PAIR *col = NULL; if(is_in_sig == NULL) return 0; in_sig_block = (int *) is_in_sig; ! ! if(!strcmp(line, SIGDASHES)) ! *in_sig_block = START_SIG_BLOCK; ! else if(*line == '\0') /* * Suggested by Eduardo: allow for a blank line right after * the sigdashes. --- 3538,3596 ---- void *is_in_sig; { struct variable *vars = ps_global->vars; ! int *in_sig_block, i, j; COLOR_PAIR *col = NULL; + static int GLine[NSTRING] = {0}; + static int PLine[NSTRING] = {0}; + int NLine[NSTRING] = {0}; + static char *buf, buf2[NSTRING] = {'\0'}; + char *p; + QSTRING_S *qs; + static qstrlen = 0; if(is_in_sig == NULL) return 0; + if (linenum != 0){ + for (i = 0; GLine[i] && (PLine[i] = GLine[i]); i++); + PLine[i] = 0; + } + + for (i = 0; tmp_20k_buf[i] && (tmp_20k_buf[i] != '\015') && (i < NSTRING) + && (i < SIZEOF_20KBUF) + && (NLine[i] = (int) tmp_20k_buf[i]); i++); + NLine[i] = 0; + + p = line; + + for (i = 0; (i < NSTRING) && ((GLine[i] = (int)p[i]) != 0); i++); + GLine[NSTRING - 1] = 0; + qs = do_quote_match(prefix && *prefix ? prefix : ">", + GLine, NLine, PLine, 1); + flatten_qstring(qs, buf2); + i = buf2 && buf2[0] ? strlen(buf2) : 0; + free_qs(&qs); + in_sig_block = (int *) is_in_sig; ! ! if (*in_sig_block != OUT_SIG_BLOCK){ ! if (line && *line && (strlen(line) >= qstrlen) ! && !strncmp(line, buf, qstrlen)) ! line += qstrlen; ! else if (strlen(line) < qstrlen) ! line += i; ! else if (strncmp(line, buf, qstrlen)) ! *in_sig_block = OUT_SIG_BLOCK; ! } ! else ! line += i; ! ! if(!strcmp(line, SIGDASHES) || !strcmp(line, "--")){ ! *in_sig_block = START_SIG_BLOCK; ! buf = buf2; ! qstrlen = i; ! } ! else if(*line == '\0'){ /* * Suggested by Eduardo: allow for a blank line right after * the sigdashes. *************** *** 3439,3448 **** */ *in_sig_block = (*in_sig_block == START_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; else ! *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; if(*in_sig_block != OUT_SIG_BLOCK && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR, --- 3597,3610 ---- */ *in_sig_block = (*in_sig_block == START_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + } else ! *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + if (*in_sig_block == OUT_SIG_BLOCK) + qstrlen = 0; /* reset back in case there's another paragraph */ + if(*in_sig_block != OUT_SIG_BLOCK && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR, *************** *** 5094,5100 **** && pico_usingcolor() && VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(color_a_quote, NULL); } } --- 5256,5262 ---- && pico_usingcolor() && VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(color_a_quote, NULL); } } diff -rc pine4.56/pine/pine.h pine4.56.fillpara/pine/pine.h *** pine4.56/pine/pine.h Thu May 29 10:28:00 2003 --- pine4.56.fillpara/pine/pine.h Mon Aug 11 12:19:03 2003 *************** *** 3909,3914 **** --- 3909,3915 ---- void gf_busy PROTO((FILTER_S *, int)); void gf_nvtnl_local PROTO((FILTER_S *, int)); void gf_local_nvtnl PROTO((FILTER_S *, int)); + void gf_quote_test PROTO((FILTER_S *, int)); void gf_line_test PROTO((FILTER_S *, int)); void *gf_line_test_opt PROTO((linetest_t, void *)); LT_INS_S **gf_line_test_new_ins PROTO((LT_INS_S **, char *, char *, int)); diff -rc pine4.56/pine/pine.hlp pine4.56.fillpara/pine/pine.hlp *** pine4.56/pine/pine.hlp Thu May 29 11:10:13 2003 --- pine4.56.fillpara/pine/pine.hlp Mon Aug 11 12:19:05 2003 *************** *** 5367,5372 **** --- 5367,5412 ---- not preserved.

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

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

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

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

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

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

<End of help on this topic>