diff -rc alpine-2.26/alpine/mailview.c alpine-2.26.fillpara/alpine/mailview.c *** alpine-2.26/alpine/mailview.c 2022-06-02 18:14:00.463274817 -0600 --- alpine-2.26.fillpara/alpine/mailview.c 2022-06-02 18:14:56.983136230 -0600 *************** *** 203,209 **** --- 203,217 ---- int pcpine_view_cursor(int, long); #endif + static char *prefix; + #define NO_FLOWED 0x0000 + #define IS_FLOWED 0x0001 + #define DELETEQUO 0x0010 + #define COLORAQUO 0x0100 + #define RAWSTRING 0x1000 + int is_word (char *, int, int); + int is_mailbox(char *, int, int); /*---------------------------------------------------------------------- Format a buffer with the text of the current message for browser *************** *** 294,299 **** --- 302,318 ---- else ps->unseen_in_view = !mc->seen; + prefix = reply_quote_str(env); + /* Make sure the prefix is not only made of spaces, so that we do not + * paint the screen incorrectly + */ + if (prefix && *prefix){ + int i; + for (i = 0; isspace((unsigned char) prefix[i]); i++); + if (i == strlen(prefix)) + fs_give((void **)&prefix); + } + init_handles(&handles); store = so_get(src, NULL, EDIT_ACCESS); *************** *** 478,483 **** --- 497,504 ---- } while(ps->next_screen == SCREEN_FUN_NULL); + if (prefix && *prefix) + fs_give((void **)&prefix); if(we_cancel) cancel_busy_cue(-1); diff -rc alpine-2.26/pico/basic.c alpine-2.26.fillpara/pico/basic.c *** alpine-2.26/pico/basic.c 2022-06-02 18:14:00.487274758 -0600 --- alpine-2.26.fillpara/pico/basic.c 2022-06-02 18:14:56.983136230 -0600 *************** *** 22,30 **** * framing, are hard. */ #include "headers.h" ! #include "osdep/terminal.h" /* * Move the cursor to the --- 22,31 ---- * framing, are hard. */ #include "headers.h" ! #include "../pith/osdep/color.h" #include "osdep/terminal.h" + int indent_match(char **, LINE *, char *, int, int); /* * Move the cursor to the *************** *** 281,287 **** gotobop(int f, int n) { int quoted, qlen; ! UCS qstr[NLINE], qstr2[NLINE]; if (n < 0) /* the other way...*/ return(gotoeop(f, -n)); --- 282,288 ---- gotobop(int f, int n) { int quoted, qlen; ! char qstr[NLINE], qstr2[NLINE], ind_str[NLINE], pqstr[NLINE];; if (n < 0) /* the other way...*/ return(gotoeop(f, -n)); *************** *** 293,298 **** --- 294,307 ---- curwp->w_dotp = lback(curwp->w_dotp); curwp->w_doto = 0; } + + if (indent_match(default_qstr(glo_quote_str, 1), 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 *************** *** 300,317 **** * PLUS: if there's a quote string, a quoted-to-non-quoted * line transition. */ ! quoted = quote_match(glo_quote_str, curwp->w_dotp, qstr, NLINE); ! qlen = quoted ? ucs4_strlen(qstr) : 0; while(lback(curwp->w_dotp) != curbp->b_linep && llength(lback(curwp->w_dotp)) > qlen ! && quoted == quote_match(glo_quote_str, lback(curwp->w_dotp), ! qstr2, NLINE) ! && !ucs4_strcmp(qstr, qstr2) ! && 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) --- 309,367 ---- * PLUS: if there's a quote string, a quoted-to-non-quoted * line transition. */ ! quoted = quote_match(default_qstr(glo_quote_str, 1), 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 ! && (quoted == quote_match(default_qstr(glo_quote_str, 1), lback(curwp->w_dotp), ! qstr2, NLINE, 0)) ! && !strcmp(qstr, qstr2) /* processed string */ ! && (quoted == quote_match(default_qstr(glo_quote_str, 1), ! lback(curwp->w_dotp), qstr2, NLINE, 1)) ! && !strcmp(qstr, qstr2) /* raw string */ ! && !indent_match(default_qstr(glo_quote_str, 1), ! lback(curwp->w_dotp),ind_str, NLINE, 0) ! && !ISspace(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 shall 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(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, 1); + quote_match(default_qstr(glo_quote_str, 1), 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(glo_quote_str, 1), 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(glo_quote_str, 1), + lback(curwp->w_dotp), pqstr, NLINE, 0)) + && !strcmp(qstr, pqstr) + && !ISspace(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) *************** *** 324,330 **** else{ /* leave cursor on first word in para */ curwp->w_doto = 0; ! while(ucs4_isspace(lgetc(curwp->w_dotp, curwp->w_doto).c)) if(++curwp->w_doto >= llength(curwp->w_dotp)){ curwp->w_doto = 0; curwp->w_dotp = lforw(curwp->w_dotp); --- 374,380 ---- else{ /* leave cursor on first word in para */ curwp->w_doto = 0; ! while(ISspace(lgetc(curwp->w_dotp, curwp->w_doto).c)) if(++curwp->w_doto >= llength(curwp->w_dotp)){ curwp->w_doto = 0; curwp->w_dotp = lforw(curwp->w_dotp); *************** *** 347,354 **** int gotoeop(int f, int n) { ! int quoted, qlen; ! UCS qstr[NLINE], qstr2[NLINE]; if (n < 0) /* the other way...*/ return(gotobop(f, -n)); --- 397,405 ---- int gotoeop(int f, int n) { ! 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)); *************** *** 361,384 **** break; } ! /* 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 = quote_match(glo_quote_str, ! curwp->w_dotp, qstr, NLINE); ! qlen = quoted ? ucs4_strlen(qstr) : 0; while(curwp->w_dotp != curbp->b_linep && llength(lforw(curwp->w_dotp)) > qlen ! && (quoted == quote_match(glo_quote_str, ! lforw(curwp->w_dotp), ! qstr2, NLINE) ! && !ucs4_strcmp(qstr, qstr2)) ! && lgetc(lforw(curwp->w_dotp), qlen).c != TAB ! && lgetc(lforw(curwp->w_dotp), qlen).c != ' ') curwp->w_dotp = lforw(curwp->w_dotp); curwp->w_doto = llength(curwp->w_dotp); --- 412,481 ---- 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(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, 1); ! quote_match(default_qstr(glo_quote_str, 1), lforw(curwp->w_dotp), qstr2, NLINE, 1); ! indented = indent_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, ind_str, ! NLINE, 1); ! if (strlenis(qstr) ! + strlenis(ind_str) < strlenis(qstr2)){ ! curwp->w_doto = llength(curwp->w_dotp); ! 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(glo_quote_str, 1), 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. ! */ ! /* 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(glo_quote_str, 1), curwp->w_dotp, qstr, NLINE, fli); ! qlen = quoted ? strlen(qstr) : 0; while(curwp->w_dotp != curbp->b_linep && llength(lforw(curwp->w_dotp)) > qlen ! && (quoted == quote_match(default_qstr(glo_quote_str, 1), ! lforw(curwp->w_dotp), qstr2, NLINE, fli)) ! && !strcmp(qstr, qstr2) ! && (quoted == quote_match(default_qstr(glo_quote_str, 1), ! lforw(curwp->w_dotp), qstr2, NLINE, 1)) ! && !strcmp(qstr, qstr2) ! && !indent_match(default_qstr(glo_quote_str, 1), ! lforw(curwp->w_dotp), ind_str, NLINE, 0) ! && !ISspace(lgetc(lforw(curwp->w_dotp), qlen).c)) curwp->w_dotp = lforw(curwp->w_dotp); curwp->w_doto = llength(curwp->w_dotp); diff -rc alpine-2.26/pico/efunc.h alpine-2.26.fillpara/pico/efunc.h *** alpine-2.26/pico/efunc.h 2022-06-02 18:14:00.487274758 -0600 --- alpine-2.26.fillpara/pico/efunc.h 2022-06-02 18:14:56.983136230 -0600 *************** *** 253,262 **** extern int fillpara(int, int); extern int fillbuf(int, int); extern int inword(void); ! extern int quote_match(UCS *, LINE *, UCS *, size_t); extern int ucs4_isalnum(UCS); extern int ucs4_isalpha(UCS); extern int ucs4_isspace(UCS); extern int ucs4_ispunct(UCS); #endif /* EFUNC_H */ --- 253,268 ---- extern int fillpara(int, int); extern int fillbuf(int, int); extern int inword(void); ! extern int quote_match(char **, LINE *, char *, size_t, int); ! extern void flatten_qstring(QSTRING_S *, char *, int); ! extern void free_qs(QSTRING_S **); ! extern QSTRING_S *do_quote_match (char **, char *, char *, char *, char *, int, int); ! extern QSTRING_S *do_raw_quote_match(char **, char *, char *, char *, QSTRING_S **, QSTRING_S **); ! extern int indent_match(char **, LINE *, char *, int, int); extern int ucs4_isalnum(UCS); extern int ucs4_isalpha(UCS); extern int ucs4_isspace(UCS); extern int ucs4_ispunct(UCS); #endif /* EFUNC_H */ + diff -rc alpine-2.26/pico/line.c alpine-2.26.fillpara/pico/line.c *** alpine-2.26/pico/line.c 2022-06-02 18:14:00.487274758 -0600 --- alpine-2.26.fillpara/pico/line.c 2022-06-02 18:14:57.007136171 -0600 *************** *** 608,623 **** lisblank(LINE *line) { int n = 0; ! UCS qstr[NLINE]; ! n = (glo_quote_str ! && quote_match(glo_quote_str, line, qstr, NLINE)) ! ? ucs4_strlen(qstr) : 0; for(; n < llength(line); n++) ! if(!ucs4_isspace(lgetc(line, n).c) ! || lgetc(line, n).c >= 0xff ! || (unsigned char) lgetc(line,n).c != (unsigned char) NBSPC) return(FALSE); return(TRUE); --- 608,619 ---- lisblank(LINE *line) { int n = 0; ! char qstr[NLINE]; ! n = quote_match(default_qstr(glo_quote_str, 1), line, qstr, NLINE, 1); for(; n < llength(line); n++) ! if(!ISspace(lgetc(line, n).c)) return(FALSE); return(TRUE); diff -rc alpine-2.26/pico/osdep/color.h alpine-2.26.fillpara/pico/osdep/color.h *** alpine-2.26/pico/osdep/color.h 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pico/osdep/color.h 2022-06-02 18:14:57.007136171 -0600 *************** *** 33,38 **** --- 33,39 ---- void pico_toggle_color(int); void pico_set_nfg_color(void); void pico_set_nbg_color(void); + char **default_qstr(void *, int); #endif /* PICO_OSDEP_COLOR_INCLUDED */ diff -rc alpine-2.26/pico/search.c alpine-2.26.fillpara/pico/search.c *** alpine-2.26/pico/search.c 2022-06-02 18:14:00.487274758 -0600 --- alpine-2.26.fillpara/pico/search.c 2022-06-02 18:14:57.015136151 -0600 *************** *** 32,37 **** --- 32,38 ---- int readpattern(char *, int, int); int replace_pat(UCS *, int *, int); int replace_all(UCS *, UCS *, int); + int deletepara(int, int); void reverse_line(LINE *); void reverse_buffer(void); void reverse_ucs4(UCS *); *************** *** 67,73 **** {"^W", N_("Start of Para"), (CTRL|'W')}, {"^O", N_("End of Para"), (CTRL|'O')}, {"^U", N_("FullJustify"), (CTRL|'U')}, ! {NULL, NULL, 0}, {NULL, NULL, 0} }; --- 68,74 ---- {"^W", N_("Start of Para"), (CTRL|'W')}, {"^O", N_("End of Para"), (CTRL|'O')}, {"^U", N_("FullJustify"), (CTRL|'U')}, ! {"^P", N_("Delete Para"), (CTRL|'P')}, {NULL, NULL, 0} }; *************** *** 216,224 **** case (CTRL|'P'): if(flags & SR_ORIGMEN){ ! /* Undefined still */ } ! if(flags & SR_OPTNMEN){ if(flags & SR_FORWARD){ flags &= ~SR_FORWARD; flags |= SR_BACKWRD; --- 217,227 ---- case (CTRL|'P'): if(flags & SR_ORIGMEN){ ! deletepara(0, 1); ! mlerase(); ! FWS_RETURN(TRUE); } ! else if(flags & SR_OPTNMEN){ if(flags & SR_FORWARD){ flags &= ~SR_FORWARD; flags |= SR_BACKWRD; *************** *** 1416,1418 **** --- 1419,1443 ---- if(bsearch) reverse_ucs4(orig); return utf8; } + + int + deletepara(int f, int n) /* Delete the current paragraph */ + { + if(curbp->b_mode&MDVIEW) /* don't allow this command if */ + return(rdonly()); /* we are in read only mode */ + + if(!lisblank(curwp->w_dotp)) + gotobop(FALSE, 1); + + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = 0; + + gotoeop(FALSE, 1); + if (curwp->w_dotp != curbp->b_linep){ /* if we are not at the end of buffer */ + curwp->w_dotp = lforw(curwp->w_dotp); /* get one more line */ + curwp->w_doto = 0; /* but only the beginning */ + } + killregion(f,n); + return(TRUE); + } + diff -rc alpine-2.26/pico/word.c alpine-2.26.fillpara/pico/word.c *** alpine-2.26/pico/word.c 2022-06-02 18:14:00.487274758 -0600 --- alpine-2.26.fillpara/pico/word.c 2022-06-02 18:14:57.019136141 -0600 *************** *** 21,30 **** */ #include "headers.h" ! int fpnewline(UCS *quote); ! int fillregion(UCS *qstr, REGION *addedregion); int setquotelevelinregion(int quotelevel, REGION *addedregion); int is_user_separator(UCS c); --- 21,30 ---- */ #include "headers.h" ! #include "../pith/osdep/color.h" int fpnewline(UCS *quote); ! int fillregion(UCS *qstr, UCS *istr, REGION *addedregion); int setquotelevelinregion(int quotelevel, REGION *addedregion); int is_user_separator(UCS c); *************** *** 427,551 **** return 0; } ! void ! do_quote_match(UCS *q, LINE *l, UCS *buf, size_t buflen) ! { ! register int i, j; ! int qstart, qend, k; ! ! /* ! * The method for determining the quote string is: ! * 1) strip leading and trailing whitespace from q ! * 2) add all leading whitespace to buf ! * 3) check for q ! * 4) if q, append q to buf and any trailing whitespace ! * 5) repeat steps 3 and 4 as necessary ! * ! * q in the future could be made to be an array of (UCS *)'s ! * (">" and whatever the user's quote_string is) ! */ ! ! *buf = '\0'; ! if(l == NULL) ! return; ! /* count leading whitespace as part of the quote */ ! for(j = 0; j <= llength(l) && lgetc(l, j).c == ' ' && j+1 < buflen; j++) ! buf[j] = lgetc(l, j).c; ! buf[j] = '\0'; ! ! if(q == NULL || *q == '\0') ! return; ! ! /* pare down q so it contains no leading or trailing whitespace */ ! for(i = 0; q[i] == ' '; i++); ! qstart = i; ! for(i = ucs4_strlen(q); i > 0 && q[i-1] == ' '; i--); ! qend = i; ! ! /* for quote strings that are blanks, chop buf to the length of q */ ! if(qend <= qstart){ ! if(ucs4_strlen(q) < buflen) ! buf[ucs4_strlen(q)] = '\0'; ! return; ! } ! ! while(j <= llength(l)){ ! for(i = qstart; j <= llength(l) && i < qend; i++, j++) ! if(q[i] != lgetc(l, j).c) ! return; ! ! if(i >= qend){ ! if(ucs4_strlen(buf) + qend - qstart < (buflen - 1)) ! ucs4_strncat(buf, q + qstart, qend - qstart); ! } ! ! /* ! * if we're this far along, we've matched a quote string, ! * and should now add the following white space. ! */ ! for(k = ucs4_strlen(buf); ! j <= llength(l) && lgetc(l,j).c == ' ' && (k + 1 < buflen); ! j++, k++){ ! buf[k] = lgetc(l,j).c; ! } ! buf[k] = '\0'; ! ! if(j > llength(l)) ! return; ! } } /* * Return number of quotes if whatever starts the line matches the quote string */ int ! quote_match(UCS *q, LINE *gl, UCS *bufl, size_t buflen) { ! LINE *nl = gl != curbp->b_linep ? lforw(gl) : NULL; ! LINE *pl = lback(gl) != curbp->b_linep ? lback(gl) : NULL; ! UCS bufp[NSTRING], bufn[NSTRING]; ! int i, j, qstart, qend; ! int quoted_line = 0; ! ! do_quote_match(q, pl, bufp, sizeof(bufp)); /* previous line */ ! do_quote_match(q, gl, bufl, buflen); /* given line */ ! do_quote_match(q, nl, bufn, sizeof(bufn)); /* next line */ ! ! if(!ucs4_strcmp(bufp, bufl) || !ucs4_strcmp(bufl, bufn)) ! return ucs4_strlen(bufl); ! ! /* is this line quoted? */ ! if(q && *q){ ! /* pare down q so it contains no leading or trailing whitespace */ ! for(i = 0; q[i] == ' '; i++); ! qstart = i; ! for(i = ucs4_strlen(q); i > 0 && q[i-1] == ' '; i--); ! qend = i; ! for(i = 0; i < llength(gl) ! && i + qstart < qend ! && lgetc(gl, i).c == q[i+qstart]; i++); ! if(i + qstart == qend) ! quoted_line = 1; ! } ! ! /* compare bufl and bufn */ ! for(i = 0; bufl[i] && bufn[i] && bufl[i] == bufn[i]; i++); ! ! /* go back to last non-space character */ ! for(; i > 0 && bufl[i-1] == ' '; i--); ! ! /* do bufl and bufn differ only in spaces? */ ! for(j = i; bufl[j] && bufl[j] == ' '; j++); ! ! /* if they differ only on trailing spaces, chop bufl to agree with bufn */ ! if (!bufl[j] ) ! bufl[Pmaster && quoted_line ? (j > i ? i+1 : i) : i] = '\0'; ! return ucs4_strlen(bufl); } /* Justify the entire buffer instead of just a paragraph */ int --- 427,591 ---- return 0; } ! /* Support of indentation of paragraphs */ ! #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 indent_match(char **, LINE *, char *, 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')) || ((c) == '?')) ! #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) && ((c) != '/')) ! #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_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)) ! #define qs_allowed(a) (((a)->qstype != qsGdb) && ((a)->qstype != qsProg)) ! ! /* Internal justification functions */ ! QSTRING_S *qs_quote_match(char **, LINE *, char *, int); ! int ucs4_widthis(UCS *); ! int ucs4_strlenis(UCS *); ! void linencpy(char *, LINE *, int); ! void ! linencpy(word, l, buflen) ! char word[NSTRING]; ! LINE *l; ! int buflen; ! { ! int i; ! UCS ucs_word[NSTRING]; ! char *utf_word; ! ! word[0] = '\0'; ! if(l){ ! for (i = 0; i < buflen && i < llength(l) ! && (ucs_word[i] = lgetc(l,i).c); i++); ! ucs_word[i == buflen ? i-1 : i] = '\0'; ! utf_word = ucs4_to_utf8_cpystr(ucs_word); ! strncpy(word, utf_word, (NSTRING < buflen ? NSTRING : buflen)); ! word[NSTRING-1] = '\0'; ! if(utf_word) fs_give((void **)&utf_word); ! } ! } ! ! /* ! * 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(char **q, LINE *l, char *rqstr, int rqstrlen) ! { ! char GLine[NSTRING], NLine[NSTRING], PLine[NSTRING]; ! 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); ! linencpy(NLine, nl, NSTRING); ! ! if (pl){ ! linencpy(PLine, pl, NSTRING); ! if(lback(pl) != curbp->b_linep){ ! char PPLine[NSTRING]; ! 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 + * rqstr is a pointer to raw qstring; buf points to processed qstring */ int ! quote_match(char **q, LINE *l, char *buf, size_t buflen, int raw) { ! QSTRING_S *qs; ! char rqstr[NSTRING]; ! qs = qs_quote_match(q, l, rqstr, NSTRING); ! if(qs) ! record_quote_string(qs); ! 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; } + int ucs4_widthis(UCS *ucstr) + { + int i, rv = 0; + for (i = 0; ucstr && ucstr[i]; i++) + rv += ((ucstr[i] == TAB) ? (~rv & 0x07) + 1 : wcellwidth((UCS) ucstr[i])); + return rv; + } + + int ucs4_strlenis(UCS *ucs_qstr) + { + char *str = ucs4_to_utf8_cpystr(ucs_qstr); + int i = (int) strlenis(str); + + if(str) fs_give((void **)&str); + return i; + } /* Justify the entire buffer instead of just a paragraph */ int *************** *** 800,805 **** --- 840,846 ---- } if(action == 'R' && curwp->w_markp){ + char qstrfl[NSTRING]; /* let yank() know that it may be restoring a paragraph */ thisflag |= CFFILL; *************** *** 811,831 **** swap_mark_and_dot_if_mark_comes_first(); /* determine if we're justifying quoted text or not */ ! qstr = quote_match(glo_quote_str, ! curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp, ! qstr2, NSTRING) ! && *qstr2 ? qstr2 : NULL; ! /* * Fillregion moves dot to the end of the filled region. */ ! if(!fillregion(qstr, &addedregion)) return(FALSE); set_last_region_added(&addedregion); ! } ! else if(action == 'P'){ /* * Justfiy the current paragraph. --- 852,877 ---- swap_mark_and_dot_if_mark_comes_first(); /* determine if we're justifying quoted text or not */ ! qstr = (glo_quote_str ! && quote_match(default_qstr(glo_quote_str, 1), ! (curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp), ! qstrfl, NSTRING, 0) ! && *qstrfl) ? utf8_to_ucs4_cpystr(qstrfl) : NULL; /* * Fillregion moves dot to the end of the filled region. */ ! if(!fillregion(qstr, NULL, &addedregion)) return(FALSE); set_last_region_added(&addedregion); ! ! if(qstr) ! fs_give((void **)&qstr); ! } ! else if(action == 'P'){ ! char ind_str[NSTRING], qstrfl[NSTRING]; ! UCS *istr; /* * Justfiy the current paragraph. *************** *** 837,852 **** if(gotoeop(FALSE, 1) == FALSE) return(FALSE); - /* determine if we're justifying quoted text or not */ - qstr = quote_match(glo_quote_str, - curwp->w_dotp, qstr2, NSTRING) - && *qstr2 ? qstr2 : NULL; - setmark(0,0); /* mark last line of para */ /* jump back to the beginning of the paragraph */ gotobop(FALSE, 1); /* let yank() know that it may be restoring a paragraph */ thisflag |= (CFFILL | CFFLPA); --- 883,898 ---- if(gotoeop(FALSE, 1) == FALSE) return(FALSE); setmark(0,0); /* mark last line of para */ /* jump back to the beginning of the paragraph */ gotobop(FALSE, 1); + istr = indent_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, ind_str, NSTRING, 0) + && *ind_str ? utf8_to_ucs4_cpystr(ind_str) : NULL; + qstr = (quote_match(default_qstr(glo_quote_str, 1), curwp->w_dotp, qstrfl, NSTRING, 0) + && *qstrfl) ? utf8_to_ucs4_cpystr(qstrfl) : NULL; + /* let yank() know that it may be restoring a paragraph */ thisflag |= (CFFILL | CFFLPA); *************** *** 860,868 **** /* * Fillregion moves dot to the end of the filled region. */ ! if(!fillregion(qstr, &addedregion)) return(FALSE); set_last_region_added(&addedregion); /* Leave cursor on first char of first line after justified region */ --- 906,920 ---- /* * Fillregion moves dot to the end of the filled region. */ ! if(!fillregion(qstr, istr, &addedregion)) return(FALSE); + if(qstr) + fs_give((void **)&qstr); + + if(istr) + fs_give((void **)&istr); + set_last_region_added(&addedregion); /* Leave cursor on first char of first line after justified region */ *************** *** 904,919 **** * can delete it and restore the saved part. */ int ! fillregion(UCS *qstr, REGION *addedregion) { long c, sz, last_char = 0; ! int i, j, qlen, same_word, spaces, word_len, word_ind, line_len, ww; int starts_midline = 0; int ends_midline = 0; int offset_into_start; LINE *line_before_start, *lp; ! UCS line_last, word[NSTRING]; REGION region; /* if region starts midline insert a newline */ --- 956,971 ---- * can delete it and restore the saved part. */ int ! fillregion(UCS *qstr, UCS *istr, REGION *addedregion) { long c, sz, last_char = 0; ! int i, j, qlen, same_word, qi, pqi, qlenis, spaces, word_len, word_ind, line_len, ww; int starts_midline = 0; int ends_midline = 0; int offset_into_start; LINE *line_before_start, *lp; ! UCS line_last, word[NSTRING], quoid[NSTRING], qstr2[NSTRING]; REGION region; /* if region starts midline insert a newline */ *************** *** 924,929 **** --- 976,1010 ---- if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp)) ends_midline++; + for (i = 0; (i < NSTRING) && qstr && (quoid[i] = qstr[i]); i++); + for (j = 0; ((i + j) < NSTRING) && istr && (quoid[i] = istr[j]); i++,j++); + quoid[i] = '\0'; + qi = ucs4_strlen(quoid); + if (istr) /* strip trailing spaces */ + for (;ISspace(quoid[qi - 1]); qi--); + quoid[qi] = '\0'; /* we have closed quoid at "X" in the first line */ + + if (ucs4_strlenis(quoid) > fillcol) + return FALSE; /* Too wide, we can't justify this! */ + + if (qstr && istr){ + for (i = ucs4_strlen(qstr) - 1; ISspace(qstr[i]); i--); + qstr[i + 1] = '\0'; /* qstrfl */ + } + qlen = ucs4_strlen(qstr); /* qstrfl*/ + qlenis = ucs4_strlenis(qstr); + + for(i = 0, qstr2[0] = '\0'; qstr && qstr[i] && (qstr2[i] = qstr[i]); i++); + + if (istr && ((j = ucs4_widthis(quoid) - ucs4_widthis(qstr)) > 0)){ + pqi = ucs4_strlen(qstr); + for (i = 0; (i < j) && (qstr2[pqi + i] = ' '); i++); + if (ISspace(istr[ucs4_strlen(istr) - 1])) + qstr2[pqi + i++] = ' '; + qstr2[pqi + i] = '\0'; + qstr = qstr2; + } + /* cut the paragraph into our fill buffer */ fdelete(); if(!getregion(®ion, curwp->w_markp, curwp->w_marko)) *************** *** 940,967 **** /* Now insert it back wrapped */ spaces = word_len = word_ind = line_len = same_word = 0; - qlen = qstr ? ucs4_strlen(qstr) : 0; /* Beginning with leading quoting... */ ! if(qstr){ ! i = 0; ! while(qstr[i]){ ! ww = wcellwidth(qstr[i]); ! line_len += (ww >= 0 ? ww : 1); ! linsert(1, qstr[i++]); ! } line_last = ' '; /* no word-flush space! */ } /* remove first leading quotes if any */ if(starts_midline) i = 0; ! else ! for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){ linsert(1, line_last = (UCS) c); line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); } /* then digest the rest... */ while((c = fremove(i++)) >= 0){ --- 1021,1056 ---- /* Now insert it back wrapped */ spaces = word_len = word_ind = line_len = same_word = 0; /* Beginning with leading quoting... */ ! if(qstr || istr){ ! for(i = 0; quoid[i] != '\0' ; i++) ! linsert(1, quoid[i]); line_last = ' '; /* no word-flush space! */ + line_len = ucs4_strlenis(quoid); /* we demand a recount! */ } /* remove first leading quotes if any */ if(starts_midline) i = 0; ! else{ ! if(qstr || istr){ ! for (i = 0; (c = fremove(i)) != '\0'; i++){ ! word[i] = c; ! word[i+1] = '\0'; ! if(ucs4_strlenis(word) >= ucs4_strlenis(quoid)) ! break; ! } ! i++; ! } ! else ! i = 0; ! for(; ISspace(c = fremove(i)); i++){ linsert(1, line_last = (UCS) c); line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1); } + } /* then digest the rest... */ while((c = fremove(i++)) >= 0){ *************** *** 982,1002 **** case TAB : case ' ' : spaces++; break; default : if(spaces){ /* flush word? */ ! if((line_len - qlen > 0) && line_len + word_len + 1 > fillcol ! && ((ucs4_isspace(line_last)) || (linsert(1, ' '))) && same_word == 0 && (line_len = fpnewline(qstr))) line_last = ' '; /* no word-flush space! */ if(word_len){ /* word to write? */ ! if(line_len && !ucs4_isspace(line_last)){ linsert(1, ' '); /* need padding? */ line_len++; } --- 1071,1092 ---- case TAB : case ' ' : + case NBSP: spaces++; break; default : if(spaces){ /* flush word? */ ! if((line_len - qlenis > 0) && line_len + word_len + 1 > fillcol ! && ((ISspace(line_last)) || (linsert(1, ' '))) && same_word == 0 && (line_len = fpnewline(qstr))) line_last = ' '; /* no word-flush space! */ if(word_len){ /* word to write? */ ! if(line_len && !ISspace(line_last)){ linsert(1, ' '); /* need padding? */ line_len++; } *************** *** 1018,1025 **** if(word_ind + 1 >= NSTRING){ /* Magic! Fake that we output a wrapped word */ ! if((line_len - qlen > 0) && same_word == 0){ ! if(!ucs4_isspace(line_last)) linsert(1, ' '); line_len = fpnewline(qstr); } --- 1108,1115 ---- if(word_ind + 1 >= NSTRING){ /* Magic! Fake that we output a wrapped word */ ! if((line_len - qlenis > 0) && same_word == 0){ ! if(!ISspace(line_last)) linsert(1, ' '); line_len = fpnewline(qstr); } *************** *** 1041,1052 **** } if(word_len){ ! if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol) && same_word == 0){ ! if(!ucs4_isspace(line_last)) linsert(1, ' '); (void) fpnewline(qstr); } ! else if(line_len && !ucs4_isspace(line_last)) linsert(1, ' '); for(j = 0; j < word_ind; j++) --- 1131,1142 ---- } if(word_len){ ! if((line_len - qlenis > 0) && (line_len + word_len + 1 > fillcol) && same_word == 0){ ! if(!ISspace(line_last)) linsert(1, ' '); (void) fpnewline(qstr); } ! else if(line_len && !ISspace(line_last)) linsert(1, ' '); for(j = 0; j < word_ind; j++) *************** *** 1104,1114 **** int len; lnewline(); ! for(len = 0; quote && *quote; quote++){ int ww; ! ww = wcellwidth(*quote); ! len += (ww >= 0 ? ww : 1); linsert(1, *quote); } --- 1194,1204 ---- int len; lnewline(); ! for(len = ucs4_strlenis(quote); quote && *quote; quote++){ int ww; ! /* ww = wcellwidth(*quote); ! len += (ww >= 0 ? ww : 1);*/ linsert(1, *quote); } *************** *** 1252,1256 **** --- 1342,1386 ---- markregion(1); } + /* + * This puts us at the end of the quoted region instead + * of on the following line. This makes it convenient + * for the user to follow a quotelevel adjustment with + * a Justify if desired. + */ + if(backuptoprevline){ + curwp->w_doto = 0; + backchar(0, 1); + } + + if(ends_midline){ /* doesn't need fixing otherwise */ + unmarkbuffer(); + markregion(1); + } + return (TRUE); } + + /* + * If there is an indent string this function returns + * its length + */ + int + indent_match(char **q, LINE *l, char *buf, int buflen, int raw) + { + char GLine[NSTRING]; + int i, k, plb; + + k = quote_match(q,l, buf, buflen, raw); + linencpy(GLine, l, NSTRING); + plb = (lback(l) != curbp->b_linep) ? lisblank(lback(l)) : 1; + if (!plb){ + i = llength(lback(l)) - 1; + for (; i >= 0 && ISspace(lgetc(lback(l), i).c); i--); + if (EOLchar(lgetc(lback(l), i).c)) + plb++; + } + + return get_indent_raw_line(q, GLine, buf, buflen, k, plb); + } + diff -rc alpine-2.26/pith/charconv/utf8.c alpine-2.26.fillpara/pith/charconv/utf8.c *** alpine-2.26/pith/charconv/utf8.c 2022-06-02 18:14:00.495274738 -0600 --- alpine-2.26.fillpara/pith/charconv/utf8.c 2022-06-02 18:14:57.019136141 -0600 *************** *** 1113,1118 **** --- 1113,1168 ---- /* + * Returns the screen cells width of the UTF-8 string argument, treating tabs + * in a special way. + */ + unsigned + utf8_widthis(char *str) + { + unsigned width = 0; + int this_width; + UCS ucs; + unsigned long remaining_octets; + char *readptr; + + if(!(str && *str)) + return(width); + + readptr = str; + remaining_octets = readptr ? strlen(readptr) : 0; + + while(remaining_octets > 0 && *readptr){ + + ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets); + + if(ucs & U8G_ERROR){ + /* + * This should not happen, but do something to handle it anyway. + * Treat each character as a single width character, which is what should + * probably happen when we actually go to write it out. + */ + remaining_octets--; + readptr++; + this_width = 1; + } + else{ + this_width = (ucs == TAB) ? ((~width & 0x07) + 1) : wcellwidth(ucs); + + /* + * If this_width is -1 that means we can't print this character + * with our current locale. Writechar will print a '?'. + */ + if(this_width < 0) + this_width = 1; + } + + width += (unsigned) this_width; + } + + return(width); + } + + /* * Copy UTF-8 characters from src into dst. * This is intended to be used if you want to truncate a string at * the start instead of the end. For example, you have a long string diff -rc alpine-2.26/pith/charconv/utf8.h alpine-2.26.fillpara/pith/charconv/utf8.h *** alpine-2.26/pith/charconv/utf8.h 2022-06-02 18:14:00.495274738 -0600 --- alpine-2.26.fillpara/pith/charconv/utf8.h 2022-06-02 18:14:57.023136132 -0600 *************** *** 82,87 **** --- 82,88 ---- UCS *ucs4_strchr(UCS *s, UCS c); UCS *ucs4_strrchr(UCS *s, UCS c); unsigned utf8_width(char *); + unsigned utf8_widthis(char *); size_t utf8_to_width_rhs(char *, char *, size_t, unsigned); int utf8_snprintf(char *, size_t, char *, ...); size_t utf8_to_width(char *, char *, size_t, unsigned, unsigned *); diff -rc alpine-2.26/pith/color.c alpine-2.26.fillpara/pith/color.c *** alpine-2.26/pith/color.c 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/color.c 2022-06-02 18:14:57.023136132 -0600 *************** *** 17,23 **** #include "../pith/state.h" #include "../pith/conf.h" #include "../pith/filter.h" ! char * color_embed(char *fg, char *bg) --- 17,24 ---- #include "../pith/state.h" #include "../pith/conf.h" #include "../pith/filter.h" ! #include "../pith/mailview.h" ! #include "../pico/estruct.h" char * color_embed(char *fg, char *bg) *************** *** 66,88 **** struct quote_colors *next; }; int color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg) { ! int countem = 0; struct variable *vars = ps_global->vars; ! char *p; struct quote_colors *colors = NULL, *cp, *next; COLOR_PAIR *col = NULL; int is_flowed = is_flowed_msg ? *((int *)is_flowed_msg) : 0; p = line; ! if(!is_flowed) ! while(isspace((unsigned char)*p)) ! p++; ! if(p[0] == '>'){ struct quote_colors *c; /* --- 67,176 ---- struct quote_colors *next; }; + int + is_word (buf, i, j) + char buf[NSTRING]; + int i, j; + { + return i <= j && is_letter(buf[i]) ? + (i < j ? is_word(buf,i+1,j) : 1) : 0; + } + + int + is_mailbox(buf,i,j) + char buf[NSTRING]; + int i, j; + { + return i <= j && (is_letter(buf[i]) || is_digit(buf[i]) || buf[i] == '.') + ? (i < j ? is_mailbox(buf,i+1,j) : 1) : 0; + } + + int + next_level_quote(buf, line, i, is_flowed) + char *buf; + char **line; + int i; + int is_flowed; + { + int j; + + if (!single_level(buf[i])){ + if(is_mailbox(buf,i,i)){ + for (j = i; buf[j] && !isspace(buf[j]); j++); + if (is_word(buf,i,j-1) || is_mailbox(buf,i,j-1)) + j += isspace(buf[j]) ? 2 : 1; + } + else{ + switch(buf[i]){ + case ':' : + if (next(buf,i) != RPAREN) + j = i + 1; + else + j = i + 2; + break; + + case '-' : + if (next(buf,i) != '-') + j = i + 2; + else + j = i + 3; + break; + + case '+' : + case '*' : + if (next(buf,i) != ' ') + j = i + 2; + else + j = i + 3; + break; + + default : + for (j = i; buf[j] && !isspace(buf[j]) + && (!single_level(buf[i]) && !is_letter(buf[j])); j++); + + j += isspace(buf[j]) ? 1 : 0; + break; + } + } + if (line && *line) + (*line) += j - i; + } + else{ + j = i+1; + if (line && *line) + (*line)++; + } + if(!is_flowed){ + if(line && *line) + for(; isspace((unsigned char)*(*line)); (*line)++); + for (i = j; isspace((unsigned char) buf[i]); i++); + } + else i = j; + if (is_flowed && i != j) + buf[i] = '\0'; + return i; + } int color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg) { ! int countem = 0, i, j = 0; struct variable *vars = ps_global->vars; ! char *p, buf[NSTRING] = {'\0'}; struct quote_colors *colors = NULL, *cp, *next; COLOR_PAIR *col = NULL; int is_flowed = is_flowed_msg ? *((int *)is_flowed_msg) : 0; + int code; + + code = (is_flowed ? IS_FLOWED : NO_FLOWED) | COLORAQUO; + select_quote(linenum, line, ins, (void *) &code); + strncpy(buf, tmp_20k_buf, NSTRING < SIZEOF_20KBUF ? NSTRING : SIZEOF_20KBUF); + buf[sizeof(buf)-1] = '\0'; p = line; ! for(i = 0; isspace((unsigned char)buf[i]); i++, p++); ! if(buf[i]){ struct quote_colors *c; /* *************** *** 131,137 **** free_color_pair(&col); cp = NULL; ! while(*p == '>'){ cp = (cp && cp->next) ? cp->next : colors; if(countem > 0) --- 219,225 ---- free_color_pair(&col); cp = NULL; ! while(buf[i]){ cp = (cp && cp->next) ? cp->next : colors; if(countem > 0) *************** *** 141,150 **** countem = (countem == 1) ? 0 : countem; ! p++; ! if(!is_flowed) ! for(; isspace((unsigned char)*p); p++) ! ; } if(colors){ --- 229,237 ---- countem = (countem == 1) ? 0 : countem; ! i = next_level_quote(buf, &p, i, is_flowed); ! for (; isspace((unsigned char)*p); p++); ! for (; isspace((unsigned char)buf[i]); i++); } if(colors){ *************** *** 207,213 **** } } ! return(0); } --- 294,300 ---- } } ! return(1); } diff -rc alpine-2.26/pith/color.h alpine-2.26.fillpara/pith/color.h *** alpine-2.26/pith/color.h 2022-06-02 18:14:00.495274738 -0600 --- alpine-2.26.fillpara/pith/color.h 2022-06-02 18:14:57.027136121 -0600 *************** *** 22,27 **** --- 22,45 ---- #include "../pith/pattern.h" #include "../pith/osdep/color.h" + #define NO_FLOWED 0x0000 + #define IS_FLOWED 0x0001 + #define DELETEQUO 0x0010 + #define COLORAQUO 0x0100 + #define RAWSTRING 0x1000 + + /* This is needed for justification, I will move it to a better place later + * or maybe not + */ + #define is_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0) + + #define is_letter(c) (((c) >= 'a' && (c) <= 'z') || \ + ((c) >= 'A' && (c) <= 'Z')) + + #define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0) + + #define single_level(c) (((c) == '>') || ((c) == '|') || ((c) == '~') || \ + ((c) == ']')) typedef struct spec_color_s { int inherit; /* this isn't a color, it is INHERIT */ *************** *** 81,86 **** --- 99,105 ---- /* exported prototypes */ char *color_embed(char *, char *); int colorcmp(char *, char *); + int next_level_quote(char *, char **, int, int); int color_a_quote(long, char *, LT_INS_S **, void *); void free_spec_colors(SPEC_COLOR_S **); diff -rc alpine-2.26/pith/filter.c alpine-2.26.fillpara/pith/filter.c *** alpine-2.26/pith/filter.c 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/filter.c 2022-06-02 18:14:57.031136112 -0600 *************** *** 42,47 **** --- 42,48 ---- #include "../pith/conf.h" #include "../pith/store.h" #include "../pith/color.h" + #include "../pith/osdep/color.h" #include "../pith/escapes.h" #include "../pith/pipe.h" #include "../pith/status.h" *************** *** 9503,9508 **** --- 9504,9514 ---- margin_r, indent; char special[256]; + long curlinenum; /* current line number */ + int curqstrpos; /* current position in quote string */ + long linenum; /* line number */ + long qstrlen; /* multiples of 100 */ + char **qstrln; /* qstrln[i] = quote string line i - 1 */ } WRAP_S; #define WRAP_MARG_L(F) (((WRAP_S *)(F)->opt)->margin_l) *************** *** 9544,9549 **** --- 9550,9561 ---- #define WRAP_COLOR(F) (((WRAP_S *)(F)->opt)->color) #define WRAP_COLOR_SET(F) ((WRAP_COLOR(F)) && (WRAP_COLOR(F)->fg[0])) #define WRAP_SPACES(F) (((WRAP_S *)(F)->opt)->spaces) + #define WRAP_CURLINE(F) (((WRAP_S *)(F)->opt)->curlinenum) + #define WRAP_CURPOS(F) (((WRAP_S *)(F)->opt)->curqstrpos) + #define WRAP_LINENUM(F) (((WRAP_S *)(F)->opt)->linenum) + #define WRAP_QSTRLEN(F) (((WRAP_S *)(F)->opt)->qstrlen) + #define WRAP_QSTRN(F) (((WRAP_S *)(F)->opt)->qstrln) + #define WRAP_QSTR(F, N) (((WRAP_S *)(F)->opt)->qstrln[(N)]) #define WRAP_PUTC(F,C,W) { \ if((F)->linep == WRAP_LASTC(F)){ \ size_t offset = (F)->linep - (F)->line; \ *************** *** 9621,9626 **** --- 9633,9640 ---- case CCR : /* CRLF or CR in text ? */ state = BOL; /* either way, handle start */ + WRAP_CURLINE(f)++; + WRAP_CURPOS(f) = 0; if(WRAP_FLOW(f)){ /* wrapped line? */ if(f->f2 == 0 && WRAP_SPC_LEN(f) && WRAP_TRL_SPC(f)){ *************** *** 9714,9720 **** case BOL : if(WRAP_FLOW(f)){ ! if(c == '>'){ WRAP_FL_QC(f) = 1; /* init it */ state = FL_QLEV; /* go collect it */ } --- 9728,9738 ---- case BOL : if(WRAP_FLOW(f)){ ! if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) ! && WRAP_QSTR(f, WRAP_CURLINE(f)) ! && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] ! && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){ ! WRAP_CURPOS(f)++; WRAP_FL_QC(f) = 1; /* init it */ state = FL_QLEV; /* go collect it */ } *************** *** 9728,9734 **** } /* quote level change implies new paragraph */ ! if(WRAP_FL_QD(f)){ WRAP_FL_QD(f) = 0; if(WRAP_HARD(f) == 0){ WRAP_HARD(f) = 1; --- 9746,9761 ---- } /* quote level change implies new paragraph */ ! if (WRAP_CURLINE(f) > 0 ! && WRAP_CURLINE(f) < WRAP_QSTRLEN(f) ! && (WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL ! || WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL) ! && ((WRAP_QSTR(f, WRAP_CURLINE(f)) != NULL && ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1) == NULL) ! || (WRAP_QSTR(f, WRAP_CURLINE(f)) == NULL && ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1) != NULL) ! || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)), ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){ WRAP_FL_QD(f) = 0; if(WRAP_HARD(f) == 0){ WRAP_HARD(f) = 1; *************** *** 9780,9787 **** break; case FL_QLEV : ! if(c == '>'){ /* another level */ ! WRAP_FL_QC(f)++; } else { /* if EMBEDed, process it and return here */ --- 9807,9818 ---- break; case FL_QLEV : ! if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) ! && WRAP_QSTR(f, WRAP_CURLINE(f)) ! && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] ! && WRAP_QSTR(f, WRAP_CURLINE(f))[WRAP_CURPOS(f)] == c){ ! WRAP_CURPOS(f)++; ! WRAP_FL_QC(f)++; /* another level */ } else { /* if EMBEDed, process it and return here */ *************** *** 9793,9799 **** } /* quote level change signals new paragraph */ ! if(WRAP_FL_QC(f) != WRAP_FL_QD(f)){ WRAP_FL_QD(f) = WRAP_FL_QC(f); if(WRAP_HARD(f) == 0){ /* add hard newline */ WRAP_HARD(f) = 1; /* hard newline */ --- 9824,9839 ---- } /* quote level change signals new paragraph */ ! if (WRAP_CURLINE(f) > 0 ! && WRAP_CURLINE(f) < WRAP_QSTRLEN(f) ! && (WRAP_QSTR(f, WRAP_CURLINE(f)) ! || WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) ! && ((WRAP_QSTR(f, WRAP_CURLINE(f)) && ! !WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) ! || (!WRAP_QSTR(f, WRAP_CURLINE(f)) && ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1)) ! || strcmp(WRAP_QSTR(f, WRAP_CURLINE(f)), ! WRAP_QSTR(f, WRAP_CURLINE(f) - 1)))){ WRAP_FL_QD(f) = WRAP_FL_QC(f); if(WRAP_HARD(f) == 0){ /* add hard newline */ WRAP_HARD(f) = 1; /* hard newline */ *************** *** 9850,9855 **** --- 9890,9902 ---- state = FL_SIG; break; + case ' ' : /* what? */ + if (WRAP_CURLINE(f) < WRAP_QSTRLEN(f) + && WRAP_QSTR(f, WRAP_CURLINE(f))){ + WRAP_SPC_LEN(f)++; + so_writec(' ', WRAP_SPACES(f)); + } + default : /* something else */ state = DFL; goto case_dfl; /* handle c like DFL */ *************** *** 9866,9872 **** &eob); /* note any embedded*/ wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ ! wrap_bol(f, 1, 1, &ip, &eib, &op, &eob); /* write any prefix */ } --- 9913,9919 ---- &eob); /* note any embedded*/ wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ ! wrap_bol(f, 1, WRAP_FLOW(f), &ip, &eib, &op, &eob); /* write any prefix */ } *************** *** 10363,10369 **** wrap_flush_embed(f, &ip, &eib, &op, &eob); wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ ! wrap_bol(f,1,1, &ip, &eib, &op, &eob); /* write any prefix */ } --- 10410,10416 ---- wrap_flush_embed(f, &ip, &eib, &op, &eob); wrap_eol(f, 1, &ip, &eib, &op, &eob); /* plunk down newline */ ! wrap_bol(f,1,WRAP_FLOW(f), &ip, &eib, &op, &eob); /* write any prefix */ } *************** *** 10436,10441 **** --- 10483,10495 ---- if(WRAP_COLOR(f)) free_color_pair(&WRAP_COLOR(f)); + { long i; + for (i = 0L; i < WRAP_QSTRLEN(f); i++) + if (WRAP_QSTR(f,i)) + fs_give((void **) &(WRAP_QSTR(f,i))); + fs_give((void **)&WRAP_QSTRN(f)); + } + fs_give((void **) &f->line); /* free temp line buffer */ so_give(&WRAP_SPACES(f)); fs_give((void **) &f->opt); /* free wrap widths struct */ *************** *** 10786,10792 **** { int j, i; COLOR_PAIR *col = NULL; ! char *prefix = NULL, *last_prefix = NULL; if(ps_global->VAR_QUOTE_REPLACE_STRING){ get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0); --- 10840,10847 ---- { int j, i; COLOR_PAIR *col = NULL; ! char *prefix = NULL, *last_prefix = NULL, *wrap_qstr = NULL; ! int level = 0, oldj, len; if(ps_global->VAR_QUOTE_REPLACE_STRING){ get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0); *************** *** 10795,10804 **** last_prefix = NULL; } } ! ! for(j = 0; j < WRAP_FL_QD(f); j++){ if(WRAP_USE_CLR(f)){ ! if((j % 3) == 0 && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR, --- 10850,10871 ---- last_prefix = NULL; } } ! ! if(WRAP_CURLINE(f) < WRAP_QSTRLEN(f) && WRAP_QSTR(f, WRAP_CURLINE(f))) ! wrap_qstr = cpystr(WRAP_QSTR(f, WRAP_CURLINE(f))); ! len = wrap_qstr ? strlen(wrap_qstr) : 0; ! ! for (j = wrap_qstr && *wrap_qstr == ' ' ? 1 : 0; ! j < len && isspace((unsigned char)wrap_qstr[j]); j++){ ! GF_PUTC_GLO(f->next, wrap_qstr[j]); ! f->n += ((wrap_qstr[j] == TAB) ? (~f->n & 0x07) + 1 : 1); ! } ! ! for(; j < len && level < len; level++){ ! oldj = j; ! j = next_level_quote(wrap_qstr, (char **)NULL, j, WRAP_FLOW(f)); if(WRAP_USE_CLR(f)){ ! if((level % 3) == 0 && ps_global->VAR_QUOTE1_FORE_COLOR && ps_global->VAR_QUOTE1_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR, *************** *** 10806,10812 **** && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } ! else if((j % 3) == 1 && ps_global->VAR_QUOTE2_FORE_COLOR && ps_global->VAR_QUOTE2_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR, --- 10873,10879 ---- && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } ! else if((level % 3) == 1 && ps_global->VAR_QUOTE2_FORE_COLOR && ps_global->VAR_QUOTE2_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR, *************** *** 10814,10820 **** && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } ! else if((j % 3) == 2 && ps_global->VAR_QUOTE3_FORE_COLOR && ps_global->VAR_QUOTE3_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR, --- 10881,10887 ---- && pico_is_good_colorpair(col)){ GF_COLOR_PUTC(f, col); } ! else if((level % 3) == 2 && ps_global->VAR_QUOTE3_FORE_COLOR && ps_global->VAR_QUOTE3_BACK_COLOR && (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR, *************** *** 10828,10870 **** } } if(!WRAP_LV_FLD(f)){ if(!WRAP_FOR_CMPS(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){ for(i = 0; prefix[i]; i++) GF_PUTC_GLO(f->next, prefix[i]); ! f->n += utf8_width(prefix); ! } ! else if(ps_global->VAR_REPLY_STRING ! && (!strcmp(ps_global->VAR_REPLY_STRING, ">") ! || !strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){ ! GF_PUTC_GLO(f->next, '>'); ! f->n += 1; } else{ ! GF_PUTC_GLO(f->next, '>'); ! GF_PUTC_GLO(f->next, ' '); ! f->n += 2; } } else{ ! GF_PUTC_GLO(f->next, '>'); ! f->n += 1; } } if(j && WRAP_LV_FLD(f)){ GF_PUTC_GLO(f->next, ' '); f->n++; } ! else if(j && last_prefix){ for(i = 0; last_prefix[i]; i++) GF_PUTC_GLO(f->next, last_prefix[i]); ! f->n += utf8_width(last_prefix); } if(prefix) fs_give((void **)&prefix); if(last_prefix) fs_give((void **)&last_prefix); return 0; } --- 10895,10941 ---- } } + if (j > 1 && wrap_qstr[j-1] == ' ') + j -= 1; + if(!WRAP_LV_FLD(f)){ if(!WRAP_FOR_CMPS(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){ for(i = 0; prefix[i]; i++) GF_PUTC_GLO(f->next, prefix[i]); ! f->n += utf8_widthis(prefix); } else{ ! for (i = oldj; i < j; i++) ! GF_PUTC_GLO(f->next, wrap_qstr[i]); ! f->n += j - oldj; } } else{ ! for (i = oldj; i < j; i++) ! GF_PUTC_GLO(f->next, wrap_qstr[i]); ! f->n += j - oldj; } + for (i = j; isspace((unsigned char)wrap_qstr[i]); i++) + GF_PUTC_GLO(f->next, ' '); + f->n += i - j; + for (; isspace((unsigned char)wrap_qstr[j]); j++); } if(j && WRAP_LV_FLD(f)){ GF_PUTC_GLO(f->next, ' '); f->n++; } ! else if(j && !value_is_space(wrap_qstr) && last_prefix){ for(i = 0; last_prefix[i]; i++) GF_PUTC_GLO(f->next, last_prefix[i]); ! f->n += utf8_widthis(last_prefix); } if(prefix) fs_give((void **)&prefix); if(last_prefix) fs_give((void **)&last_prefix); + if (wrap_qstr) + fs_give((void **)&wrap_qstr); return 0; } *************** *** 10896,10901 **** --- 10967,10978 ---- wrap->hdr_color = (GFW_HDRCOLOR & flags) == GFW_HDRCOLOR; wrap->for_compose = (GFW_FORCOMPOSE & flags) == GFW_FORCOMPOSE; wrap->handle_soft_hyphen = (GFW_SOFTHYPHEN & flags) == GFW_SOFTHYPHEN; + wrap->curlinenum = 0L; + wrap->curqstrpos = 0; + wrap->linenum = 0L; + wrap->qstrlen = 100L; + wrap->qstrln = (char **) fs_get(100*sizeof(char *)); + memset(wrap->qstrln, 0, 100*sizeof(char *)); return((void *) wrap); } *************** *** 11339,11345 **** --- 11416,11630 ---- } \ } + #define ADD_QUOTE_STRING(F) { \ + int len = tmp_20k_buf[0] ? strlen(tmp_20k_buf) + 1 : 0; \ + FILTER_S *fltr; \ + \ + for(fltr = (F); fltr && fltr->f != gf_wrap; fltr = fltr->next); \ + if (fltr){ \ + if (WRAP_LINENUM(fltr) >= WRAP_QSTRLEN(fltr)){ \ + fs_resize((void **)&WRAP_QSTRN(fltr), \ + (WRAP_QSTRLEN(fltr) + 100) * sizeof(char *)); \ + memset(WRAP_QSTRN(fltr)+WRAP_QSTRLEN(fltr), 0, \ + 100*sizeof(char*)); \ + WRAP_QSTRLEN(fltr) += 100L; \ + } \ + if (len){ \ + WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = \ + (char *) fs_get(len*sizeof(char)); \ + WRAP_QSTR(fltr, WRAP_LINENUM(fltr)) = cpystr(tmp_20k_buf);\ + } \ + WRAP_LINENUM(fltr)++; \ + } \ + } + + int end_of_line(char *line) + { + int i; + + for(i= 0; line && line[i]; i++){ + if((line[i] == '\015' && line[i+1] == '\012') || line[i] == '\012') + break; + } + return i; + } + + /* This macro is used in gf_quote_test. It receives a return code + from a filter. All filters that will print something must send + return code 0, except color_a_quote which must send return code + 1 + */ + + #define GF_ADD_QUOTED_LINE(F, line) \ + { \ + LT_INS_S *ins = NULL, *insp; \ + int done; \ + char *gline, *cline;\ + unsigned char ch;\ + register char *cp;\ + register int l;\ + \ + for (gline = cline = line; gline && cline; ){\ + if((cline = strchr(gline,'\012')) != NULL)\ + *cline = '\0';\ + done = (*((LINETEST_S *) (F)->opt)->f)((F)->n++, gline, &ins,\ + ((LINETEST_S *) (F)->opt)->local);\ + if (done < 2){ \ + if(done == 1)\ + ADD_QUOTE_STRING((F));\ + for(insp = ins, cp = gline; *cp ; ){\ + if(insp && cp == insp->where){\ + if(insp->len > 0){ \ + for(l = 0; l < insp->len; l++){\ + ch = (unsigned char) insp->text[l];\ + GF_PUTC((F)->next, ch);\ + }\ + insp = insp->next;\ + continue; \ + } else if(insp->len < 0){ \ + cp -= insp->len; \ + insp = insp->next; \ + continue; \ + } \ + }\ + 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);\ + if(cline){ \ + *cline = '\012';\ + gline += cline - gline + 1;\ + }\ + GF_PUTC((F)->next, '\015');\ + GF_PUTC((F)->next, '\012');\ + }\ + }\ + } + /* test second line of old line first */ + #define SECOND_LINE_QUOTE_TEST(line, F) \ + {\ + *p = '\0';\ + i = end_of_line((F)->oldline); \ + if (((F)->oldline)[i]){\ + i += (((F)->oldline)[i] == '\015') ? 2 : 1;\ + line = (F)->oldline + i;\ + i = end_of_line(line); \ + if(line[i])\ + line[i] = '\0'; \ + }\ + for (i = 0; ((F)->line) \ + && (i < LINE_TEST_BLOCK) \ + && (i < SIZEOF_20KBUF)\ + && ((F)->line)[i] \ + && (((F)->line)[i] != '\015')\ + && (((F)->line)[i] != '\012')\ + && (tmp_20k_buf[i] = ((F)->line)[i]); i++);\ + tmp_20k_buf[i] = '\0';\ + GF_ADD_QUOTED_LINE((F), line);\ + } + + #define FIRST_LINE_QUOTE_TEST(line, F)\ + {\ + *p = '\0';\ + line = (F)->line;\ + if ((F)->oldline)\ + fs_give((void **)&(F)->oldline);\ + (F)->oldline = cpystr(line);\ + i = end_of_line(line); \ + if (line[i]){ \ + j = (line[i] == '\015') ? 2 : 1;\ + line[i] = '\0'; \ + i += j; \ + }\ + for (j = 0; ((F)->line) \ + && ((i + j) < LINE_TEST_BLOCK) \ + && (j < SIZEOF_20KBUF) \ + && ((F)->line)[i + j] \ + && (((F)->line)[i + j] != '\015')\ + && (((F)->line)[i + j] != '\012')\ + && (tmp_20k_buf[j] = ((F)->line)[i + j]); j++);\ + tmp_20k_buf[j] = '\0';\ + GF_ADD_QUOTED_LINE((F), line);\ + } + + + void + gf_quote_test(f, flg) + FILTER_S *f; + int flg; + { + register char *p = f->linep; + register char *eobuf = GF_LINE_TEST_EOB(f); + char *line = NULL; + int i, j; + GF_INIT(f, f->next); + + if(flg == GF_DATA){ + register unsigned char c; + register int state = f->f1; + + while(GF_GETC(f, c)){ + + GF_LINE_TEST_ADD(f, c); + if(c == '\012') + state++; + if(state == 2){ /* two full lines read */ + state = 0; + + /* first process the second line of an old line */ + if (f->oldline && f->oldline[0]) + SECOND_LINE_QUOTE_TEST(line, f); + + /* now we process the first line */ + FIRST_LINE_QUOTE_TEST(line, f); + + p = f->line; + } + } + + f->f1 = state; + GF_END(f, f->next); + } + else if(flg == GF_EOD){ + /* first process the second line of an old line */ + if (f->oldline && f->oldline[0]) + SECOND_LINE_QUOTE_TEST(line, f); + + /* now we process the first line */ + FIRST_LINE_QUOTE_TEST(line, f); + + /* We are out of data. In this case we have processed the second + * line of an oldline, then the first line of a line, but we need + * to process the second line of the given line. We do this by + * processing it now!. + */ + if (line[i]){ + tmp_20k_buf[0] = '\0'; /* No next line */ + GF_ADD_QUOTED_LINE(f, line+i); + } + + fs_give((void **) &f->oldline); /* free old line buffer */ + fs_give((void **) &f->line); /* free line buffer */ + fs_give((void **) &f->opt); /* free test struct */ + GF_FLUSH(f->next); + (*f->next->f)(f->next, GF_EOD); + } + else if(flg == GF_RESET){ + 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 diff -rc alpine-2.26/pith/filter.h alpine-2.26.fillpara/pith/filter.h *** alpine-2.26/pith/filter.h 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/filter.h 2022-06-02 18:14:57.031136112 -0600 *************** *** 217,222 **** --- 217,223 ---- void *gf_prepend_editorial_opt(prepedtest_t, char *); void gf_nvtnl_local(FILTER_S *, int); void gf_local_nvtnl(FILTER_S *, int); + void gf_quote_test(FILTER_S *, int); void *gf_url_hilite_opt(URL_HILITE_S *, HANDLE_S **, int); void free_filter_module_globals(void); diff -rc alpine-2.26/pith/filttype.h alpine-2.26.fillpara/pith/filttype.h *** alpine-2.26/pith/filttype.h 2022-06-02 18:14:00.495274738 -0600 --- alpine-2.26.fillpara/pith/filttype.h 2022-06-02 18:14:57.031136112 -0600 *************** *** 36,41 **** --- 36,43 ---- unsigned char t; /* temporary char */ char *line; /* place for temporary storage */ char *linep; /* pointer into storage space */ + char *oldline; /* the previous line to "line" */ + char *oldlinep; /* the previous line to "line" */ void *opt; /* optional per instance data */ void *data; /* misc internal data pointer */ unsigned char queue[1 + GF_MAXBUF]; diff -rc alpine-2.26/pith/mailview.c alpine-2.26.fillpara/pith/mailview.c *** alpine-2.26/pith/mailview.c 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/mailview.c 2022-06-02 18:14:57.035136102 -0600 *************** *** 51,57 **** #include "../pith/escapes.h" #include "../pith/keyword.h" #include "../pith/smime.h" ! #define FBUF_LEN (50) --- 51,61 ---- #include "../pith/escapes.h" #include "../pith/keyword.h" #include "../pith/smime.h" ! #include "../pith/osdep/color.h" ! #include "../pico/osdep/color.h" ! #include "../pico/estruct.h" ! #include "../pico/pico.h" ! #include "../pico/efunc.h" #define FBUF_LEN (50) *************** *** 634,640 **** && pico_usingcolor() && ps_global->VAR_SIGNATURE_FORE_COLOR && ps_global->VAR_SIGNATURE_BACK_COLOR){ ! gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig)); } if((flgs & FM_DISPLAY) --- 638,644 ---- && pico_usingcolor() && ps_global->VAR_SIGNATURE_FORE_COLOR && ps_global->VAR_SIGNATURE_BACK_COLOR){ ! gf_link_filter(gf_quote_test, gf_line_test_opt(color_signature, &is_in_sig)); } if((flgs & FM_DISPLAY) *************** *** 642,649 **** && 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)); } if(!(flgs & FM_NOWRAP)){ wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE; --- 646,655 ---- && 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)); } + else + gf_link_filter(gf_quote_test,gf_line_test_opt(select_quote, NULL)); if(!(flgs & FM_NOWRAP)){ wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE; *************** *** 1459,1485 **** color_signature(long int linenum, char *line, LT_INS_S **ins, 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. */ *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, --- 1465,1552 ---- color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig) { struct variable *vars = ps_global->vars; ! int *in_sig_block, i, j,same_qstr = 0, plb; COLOR_PAIR *col = NULL; + static char GLine[NSTRING] = {'\0'}; + static char PLine[NSTRING] = {'\0'}; + static char PPLine[NSTRING] = {'\0'}; + char NLine[NSTRING] = {'\0'}; + char rqstr[NSTRING] = {'\0'}; + char *p, *q; + static char *buf, buf2[NSTRING] = {'\0'}; + QSTRING_S *qs; + static int qstrlen = 0; if(is_in_sig == NULL) return 0; + if (linenum > 0){ + strncpy(PLine, GLine, sizeof(PLine)); + PLine[sizeof(PLine)-1] = '\0'; + } + + if((p = strchr(tmp_20k_buf, '\015')) != NULL) *p = '\0'; + strncpy(NLine, tmp_20k_buf, sizeof(NLine)); + NLine[sizeof(NLine) - 1] = '\0'; + if (p) *p = '\015'; + + strncpy(GLine, line, sizeof(GLine)); + GLine[sizeof(GLine) - 1] = '\0'; + + ps_global->list_qstr = default_qstr(ps_global->prefix && *ps_global->prefix + ? (void *) ps_global->prefix : (void *) ">", 0); + plb = line_isblank(ps_global->list_qstr, PLine, GLine, PPLine, NSTRING); + qs = do_quote_match(ps_global->list_qstr, GLine, NLine, PLine, rqstr, NSTRING, plb); + if(linenum > 0) + strncpy(PPLine, PLine, NSTRING); + strncpy(buf2, rqstr, NSTRING); + i = buf2 && buf2[0] ? strlen(buf2) : 0; + free_qs(&qs); + + /* determine if buf and buf2 are the same quote string */ + if (!struncmp(buf, buf2, qstrlen)){ + for (j = qstrlen; buf2[j] && isspace((unsigned char)buf2[j]); j++); + if (!buf2[j] || buf2[j] == '|' || (buf2[j] == '*' && buf2[j+1] != '>')) + same_qstr++; + } + in_sig_block = (int *) is_in_sig; ! if (*in_sig_block != OUT_SIG_BLOCK){ ! if (line && *line && (strlen(line) >= qstrlen) && same_qstr) ! line += qstrlen; ! else if (strlen(line) < qstrlen) ! line += i; ! else if (!same_qstr) ! *in_sig_block = OUT_SIG_BLOCK; ! } ! else ! line += i; ! ! if(!strcmp(line, SIGDASHES) || !strcmp(line, "--")){ ! *in_sig_block = START_SIG_BLOCK; ! buf = (char *) fs_get((i + 1)*sizeof(char)); ! buf = cpystr(buf2); ! qstrlen = i; ! } ! else if(*line == '\0'){ /* * Suggested by Eduardo: allow for a blank line right after * the sigdashes. */ *in_sig_block = (*in_sig_block == START_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + } else *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK) ? IN_SIG_BLOCK : OUT_SIG_BLOCK; + if (*in_sig_block == OUT_SIG_BLOCK){ + qstrlen = 0; /* reset back in case there's another paragraph */ + if (buf) + fs_give((void **)&buf); + } + if(*in_sig_block != OUT_SIG_BLOCK && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR, *************** *** 2039,2044 **** --- 2106,2182 ---- } + /* This filter gives a quote string of a line. It sends its reply back to the + calling filter in the tmp_20k_buf variable. This filter replies with + the full quote string including tailing spaces if any. It is the + responsibility of the calling filter to figure out if thos spaces are + useful for that filter or if they should be removed before doing any + useful work. For example, color_a_quote does not require the trailing + spaces, but gf_wrap does. + */ + int + select_quote(long linenum, char *line, LT_INS_S **ins, void *local) + { + int i, plb, *code; + char rqstr[NSTRING] = {'\0'}, buf[NSTRING] = {'\0'}; + char GLine[NSTRING] = {'\0'}, PLine[NSTRING] = {'\0'}; + char PPLine[NSTRING] = {'\0'}, NLine[NSTRING] = {'\0'}; + static char GLine1[NSTRING] = {'\0'}; + static char PLine1[NSTRING] = {'\0'}; + static char PPLine1[NSTRING] = {'\0'}; + static char GLine2[NSTRING] = {'\0'}; + static char PLine2[NSTRING] = {'\0'}; + static char PPLine2[NSTRING] = {'\0'}; + QSTRING_S *qs; + int buflen = NSTRING < SIZEOF_20KBUF ? NSTRING - 1: SIZEOF_20KBUF - 1; + int who, raw; + + code = (int *)local; + who = code ? (*code & COLORAQUO) : 0; /* may I ask who is calling? */ + raw = code ? (*code & RAWSTRING) : 0; /* return raw string */ + strncpy(GLine, (who ? GLine1 : GLine2), buflen); + strncpy(PLine, (who ? PLine1 : PLine2), buflen); + strncpy(PPLine, (who ? PPLine1 : PPLine2), buflen); + + if (linenum > 0) + strncpy(PLine, GLine, buflen); + + strncpy(NLine, tmp_20k_buf, buflen); + + if (line) + strncpy(GLine, line, buflen); + else + GLine[0] = '\0'; + + ps_global->list_qstr = default_qstr(ps_global->prefix && *ps_global->prefix + ? (void *) ps_global->prefix : (void *) ">", 0); + plb = line_isblank(ps_global->list_qstr, PLine, GLine, PPLine, NSTRING); + + qs = do_quote_match(ps_global->list_qstr, GLine, NLine, PLine, rqstr, NSTRING, plb); + if (raw) + strncpy(buf, rqstr, NSTRING); + else + flatten_qstring(qs, buf, NSTRING); + if(qs) + record_quote_string(qs); + free_qs(&qs); + + /* do not paint an extra level for a line with a >From string at the + * begining of it + */ + if (buf[0]){ + i = strlen(buf); + if (strlen(line) >= i + 6 && !strncmp(line+i-1,">From ", 6)) + buf[i - 1] = '\0'; + } + strncpy(tmp_20k_buf, buf, buflen); + if (linenum > 0) + strncpy((who ? PPLine1 : PPLine2), PLine, buflen); + strncpy((who ? GLine1 : GLine2), GLine, buflen); + strncpy((who ? PLine1 : PLine2), PLine, buflen); + return 1; + } + #define UES_LEN 12 #define UES_MAX 32 diff -rc alpine-2.26/pith/mailview.h alpine-2.26.fillpara/pith/mailview.h *** alpine-2.26/pith/mailview.h 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/mailview.h 2022-06-02 18:14:57.035136102 -0600 *************** *** 146,151 **** --- 146,152 ---- char *display_parameters(PARAMETER *); char *pine_fetch_header(MAILSTREAM *, long, char *, char **, long); int color_signature(long, char *, LT_INS_S **, void *); + int select_quote(long, char *, LT_INS_S **, void *); int scroll_handle_start_color(char *, size_t, int *); int scroll_handle_end_color(char *, size_t, int *, int); int width_at_this_position(unsigned char *, unsigned long); diff -rc alpine-2.26/pith/osdep/color.c alpine-2.26.fillpara/pith/osdep/color.c *** alpine-2.26/pith/osdep/color.c 2022-06-02 18:14:00.495274738 -0600 --- alpine-2.26.fillpara/pith/osdep/color.c 2022-06-02 18:14:57.039136093 -0600 *************** *** 28,34 **** #include #include "./color.h" ! /* --- 28,36 ---- #include #include "./color.h" ! #include "./collate.h" ! #include "../charconv/utf8.h" ! #include "../../c-client/c-client.h" /* *************** *** 88,90 **** --- 90,1372 ---- { return(pico_set_colors(col ? col->fg : NULL, col ? col->bg : NULL, flags)); } + + + /* + * Extended Justification support also does not belong here + * but otherwise webpine will not build, so we move everything + * here. Hopefully this will be the permanent place for these + * routines. These routines used to be in pico/word.c + */ + #define NSTRING 256 + #include "../../include/general.h" + + /* Support of indentation of paragraphs */ + #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) + + + /* 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')) || ((c) == '?')) + #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) && ((c) != '/')) + #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_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)) + #define qs_allowed(a) (((a)->qstype != qsGdb) && ((a)->qstype != qsProg)) + + /* Internal justification functions */ + QSTRING_S *is_quote(char **, char *, int); + QSTRING_S *qs_normal_part(QSTRING_S *); + QSTRING_S *qs_remove_trailing_spaces(QSTRING_S *); + QSTRING_S *trim_qs_from_cl(QSTRING_S *, QSTRING_S *, QSTRING_S *); + QSTRING_S *fix_qstring(QSTRING_S *, QSTRING_S *, QSTRING_S *); + QSTRING_S *fix_qstring_allowed(QSTRING_S *, QSTRING_S *, QSTRING_S *); + QSTRING_S *qs_add(char **, char *, QStrType, int, int, int, int); + QSTRING_S *remove_qsword(QSTRING_S *); + QSTRING_S *do_raw_quote_match(char **, char *, char *, char *, QSTRING_S **, QSTRING_S **); + void free_qs(QSTRING_S **); + int word_is_prog(char *); + int qstring_is_normal(QSTRING_S *); + int exists_good_part(QSTRING_S *); + int strcmp_qs(char *, char *); + int count_levels_qstring(QSTRING_S *); + int same_qstring(QSTRING_S *, QSTRING_S *); + int isaword(char *,int ,int); + int isamailbox(char *,int ,int); + int double_check_qstr(char *); + + int + word_is_prog(char *word) + { + static char *list1[] = {"#include", + "#define", + "#ifdef", + "#ifndef", + "#elif", + "#if", + NULL}; + static char *list2[] = {"#else", + "#endif", + NULL}; + int i, j = strlen(word), k, rv = 0; + + for(i = 0; rv == 0 && list1[i] && (k = strlen(list1[i])) && k < j; i++) + if(!strncmp(list1[i], word, k) && ISspace(word[k])) + rv++; + + if(rv) + return rv; + + for(i = 0; rv == 0 && list2[i] && (k = strlen(list2[i])) && k <= j; i++) + if(!strncmp(list2[i], word, k) && (!word[k] || ISspace(word[k]))) + rv++; + + return rv; + } + + /* + * 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(char **qs, char word[NSTRING], QStrType typeqs, int beginw, int endw, + int offset, int neednext) + { + QSTRING_S *qstring, *nextqs; + 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; + } + + nextqs = neednext ? is_quote(qs, word+beginw, 1) : NULL; + + 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(QSTRING_S *cl) + { + for (;cl && (cl->qstype == qsNormal); cl = cl->next); + return cl ? 0 : 1; + } + + /* + * 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(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; + } + + /* + * 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(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; + } + + /* + * 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(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(QSTRING_S *cl) + { + int count; + for (count = 0; cl ; count++, cl = cl->next); + + return count; + } + + int + value_is_space(char *value) + { + for (; value && *value && ISspace(*value); value++); + + return value && *value ? 0 : 1; + } + + void + free_qs(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; + } + + /* + * 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(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(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(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); + + /* by the definition of n and c above we have n <= c */ + + 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 really 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 && cl->qstype == pl->qstype + && !strucmp(cl->value, pl->value)){ + cl = cl->next; + pl = pl->next; + } + if (pl && cl && cl->qstype == pl->qstype + && 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; + } + + /* so n <= c (see above) hence n < c or n = c. If n < c then n+1 <= c, so we + * either have n == c or n + 1 < c or n + 1 == c. We analyze each case in the + * following code + */ + + 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; + } + /* else n == c, Yeah!!! */ + return cqstring; + } + + QSTRING_S * + fix_qstring_allowed(QSTRING_S *cl, QSTRING_S *nl, QSTRING_S *pl) + { + if(!cl) + return (QSTRING_S *) NULL; + + if (qs_allowed(cl)) + cl->next = fix_qstring_allowed(cl->next, (nl ? nl->next : NULL), + (pl ? pl->next : NULL)); + else + if((nl && cl->qstype == nl->qstype) || (pl && cl->qstype == pl->qstype) + || (!nl && !pl)) + free_qs(&cl); + return cl; + } + + /* + * This function flattens the quote string returned to us by is_quote. A + * crash in this function implies a bug elsewhere. + */ + void + flatten_qstring(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'; + } + + extern int list_len; + + + int + double_check_qstr(char *q) + { + if(!q || !*q) + return 0; + + return (*q == '#') ? 1 : 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 (char **qs,char *word, int been_here) + { + int i = 0, j, nxt, prev, finished = 0, offset; + unsigned char c; + QSTRING_S *qstring = (QSTRING_S *) NULL; + + if (word == NULL || word[0] == '\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!. + */ + for(j = 0; j < list_len; j++){ + if(!double_check_qstr(qs[j])){ + i += advance_quote_string(qs[j], word, i); + if (!word[i]) /* went too far? */ + return qs_add(qs, word, qsNormal, 0, i, 0, 0); + } + else + break; + } + + switch (c = (unsigned char) now(word,i)){ + case NBSP: + case TAB : + case ' ' : { QSTRING_S *nextqs, *d; + + for (; ISspace(word[i]); i++); /* FIX ME */ + 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 "^ ^"). + */ + i += nextqs && nextqs->qstype == qsNormal + ? strlen(nextqs->value) : 0; + 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; + if(nextqs && nextqs->qstype == qsNormal){ + d = nextqs->next; + nextqs->next = NULL; + qstring->next = d; + free_qs(&nextqs); + } + else + qstring->next = nextqs; + + 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 ':': /* colon */ + case '~': nxt = next(word,i); + if ((is_tilde(c) && (nxt == '/')) + || (is_colon(c) && !is_cquote(nxt) + && !is_cword(nxt) && nxt != RPAREN)) + finished++; + else if (is_cquote(c) + || is_cquote(nxt) + || (c != '~' && nxt == RPAREN) + || (i != 0 && ISspace(nxt)) + || is_cquote(prev = before(word,i)) + || (ISspace(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 */ + (ISspace(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; + + case '_' : if(ISspace(next(word, i))) + return qs_add(qs, word, qsChar, i, i+1, 0, 1); + else + finished++; + break; + + case '#' : { QStrType qstype = qsChar; + if((nxt = next(word, i)) != c){ + if(isdigit((int) nxt)) + qstype = qsGdb; + else + if(word_is_prog(word)) + qstype = qsProg; + return qs_add(qs, word, qstype, 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)) + && !(ISspace(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 + : ((ISspace(c) + && is_cquote(next(word,j+1))) ? 3 : -1); + + qstype = (is_cquote(c) || (c == RPAREN)) + ? (is_qsword(c) ? qsWord : qsString) + : ((ISspace(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(i > 0) + return qs_add(qs, word, qsNormal, 0, i, 0, 1); + 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; + } + + int + isaword(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(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 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(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 (QSTRING_S *cl) + { + return (cl ? (((cl->qstype != qsWord) && (cl->qstype != qsChar) + && qs_allowed(cl) && !value_is_space(cl->value)) + ? 1 : exists_good_part(cl->next)) + : 0); + } + + int + line_isblank(char **q, char *GLine, char *NLine, char *PLine, 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(char **q, char *GLine, char *NLine, char *PLine, QSTRING_S **nlp, QSTRING_S **plp) + { + QSTRING_S *cl, *nl = NULL, *pl = NULL; + char nbuf[NSTRING], pbuf[NSTRING], buf[NSTRING]; + int emptypl = 0, emptynl = 0; + + if (!(cl = is_quote(q, GLine, 0))) /* if nothing in, nothing out */ + return cl; + + nl = is_quote(q, NLine, 0); /* Next Line */ + if (nlp) *nlp = nl; + 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); + if((cl = fix_qstring_allowed(cl, NULL, NULL)) != NULL) + 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((cl = fix_qstring_allowed(cl, nl, pl)) != NULL){ + nl = trim_qs_from_cl(cl, nl, NULL); + pl = trim_qs_from_cl(cl, NULL, pl); + } + else{ + free_qs(&nl); + free_qs(&pl); + } + if(nlp) + *nlp = nl; + else + free_qs(&nl); + if(plp) + *plp = pl; + else + free_qs(&pl); + return cl; + } + + QSTRING_S * + do_quote_match(char **q, char *GLine, char *NLine, char *PLine, char *rqstr, + int rqstrlen, int 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]; + + if(rqstr) + *rqstr = '\0'; + + /* if nothing in, nothing out */ + cl = do_raw_quote_match(q, GLine, NLine, PLine, &nl, &pl); + if(cl == NULL){ + free_qs(&nl); + free_qs(&pl); + 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 (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]) + if(!get_indent_raw_line(q, PLine, nbuf, NSTRING, p, plb) + || NewP + strlenis(nbuf) != NewC){ + free_qs(&cl); + free_qs(&pl); + return 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(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; ISspace(l[i + k]); k++); + k -= k % es; + return k; + } + for (is = 0; ISspace(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; ISspace(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; ISspace(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(char *qstr) + { + int i, rv = 0; + for (i = 0; qstr && qstr[i]; i++) + rv += ((qstr[i] == TAB) ? (~rv & 0x07) + 1 : 1); + return rv; + } + + int + is_indent (char word[NSTRING], int plb) + { + int i = 0, finished = 0, j, k, digit = 0, bdigits = -1, alpha = 0; + unsigned char c, nxt; + + if (!word || !word[0]) + return i; + + for (i = 0, j = 0; ISspace(word[i]); i++, j++); + while ((i < NSTRING - 2) && !finished){ + switch (c = (unsigned char) now(word,i)){ + case NBSP: + case TAB : + case ' ' : for (; ISspace(word[i]); i++) + ; + if (!is_indent_char(now(word,i))) + finished++; + break; + + case '+' : + case '.' : + case ']' : + case '*' : + case '}' : + case '-' : + case RPAREN: + nxt = (unsigned char) next(word,i); + if ((c == '.' && allowed_after_period(nxt) && alpha) + || (c == '*' && allowed_after_star(nxt)) + || (c == '}' && allowed_after_braces(nxt)) + || (c == '-' && allowed_after_dash(nxt)) + || (c == '+' && allowed_after_dash(nxt)) + || (c == RPAREN && allowed_after_parenth(nxt)) + || (c == ']' && allowed_after_parenth(nxt))) + i++; + else + finished++; + break; + + case 0xE2: /* bullet? */ + nxt = (unsigned char) next(word,i); + if(nxt == 0x80){ + nxt = (unsigned char) next(word,i+1); + if(nxt == 0xA2){ + nxt = (unsigned char) next(word,i+2); + if(allowed_after_star(nxt)) + i += 3; + else + finished++; + } + else finished++; + } + else finished++; + break; + + default : if (is_a_digit(c) && plb){ + if (bdigits < 0) + bdigits = i; /* first digit */ + for (k = i; is_a_digit(now(word,k)); k++); + if (k - bdigits > 2){ /* more than 2 digits? */ + i = bdigits; /* too many! */ + finished++; + } + else{ + if(allowed_after_digit(now(word,k),word,k)){ + alpha++; + i = k; + } + else{ + i = bdigits; + finished++; + } + } + } + else + finished++; + break; + + } + } + if (i == j) + i = 0; /* there must be something more than spaces in an indent string */ + return i; + } + + int + get_indent_raw_line(char **q, char *GLine, char *buf, int buflen, int k, int plb) + { + int i, j; + char testline[1024]; + + if(k > 0){ + for(j = 0; GLine[j] != '\0'; j++){ + testline[j] = GLine[j]; + testline[j+1] = '\0'; + if(strlenis(testline) >= strlenis(buf)) + break; + } + k = ++j; /* reset k */ + } + i = is_indent(GLine+k, plb); + + for (j = 0; j < i && j < buflen && (buf[j] = GLine[j + k]); j++); + buf[j] = '\0'; + + return i; + } + + /* support for remembering quote strings across messages */ + char **allowed_qstr = NULL; + int list_len = 0; + + void + free_allowed_qstr(void) + { + int i; + char **q = allowed_qstr; + + if(q == NULL) + return; + + for(i = 0; i < list_len; i++) + fs_give((void **)&q[i]); + + fs_give((void **)q); + list_len = 0; + } + + void + add_allowed_qstr(void *q, int type) + { + int i; + + if(allowed_qstr == NULL){ + allowed_qstr = malloc(sizeof(char *)); + list_len = 0; + } + + if(type == 0){ + allowed_qstr[list_len] = malloc((1+strlen((char *)q))*sizeof(char)); + strcpy(allowed_qstr[list_len], (char *)q); + } + else + allowed_qstr[list_len] = (char *) ucs4_to_utf8_cpystr((UCS *)q); + + fs_resize((void **)&allowed_qstr, (++list_len + 1)*sizeof(char *)); + allowed_qstr[list_len] = NULL; + } + + void + record_quote_string (QSTRING_S *qs) + { + int i, j, k; + + for(; qs && qs->value; qs = qs->next){ + j = 0; + for (; ;){ + k = j; + for(i = 0; i < list_len; i++){ + j += advance_quote_string(allowed_qstr[i], qs->value, j); + for(; ISspace(qs->value[j]); j++); + } + if(k == j) + break; + } + if(qs->value[j] != '\0') + add_allowed_qstr((void *)(qs->value + j), 0); + } + } + + /* type utf8: code 0; ucs4: code 1. */ + char ** + default_qstr(void *q, int type) + { + if(allowed_qstr == NULL) + add_allowed_qstr(q, type); + + return allowed_qstr; + } + diff -rc alpine-2.26/pith/osdep/color.h alpine-2.26.fillpara/pith/osdep/color.h *** alpine-2.26/pith/osdep/color.h 2022-06-02 18:14:00.495274738 -0600 --- alpine-2.26.fillpara/pith/osdep/color.h 2022-06-02 18:14:57.039136093 -0600 *************** *** 17,22 **** --- 17,40 ---- #ifndef PITH_OSDEP_COLOR_INCLUDED #define PITH_OSDEP_COLOR_INCLUDED + /* + * 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, qsGdb, qsProg, qsText} QStrType; + + typedef struct QSTRING { + char *value; /* possible quote string */ + QStrType qstype; /* type of quote string */ + struct QSTRING *next; /* possible continuation */ + } QSTRING_S; + + #define UCH(c) ((unsigned char) (c)) + #define NBSP UCH('\240') + #define ISspace(c) (UCH(c) == ' ' || UCH(c) == TAB || UCH(c) == NBSP) #define RGBLEN 11 #define MAXCOLORLEN 11 /* longest string a color can be */ *************** *** 93,98 **** char *pico_get_last_bg_color(void); char *color_to_canonical_name(char *); int pico_count_in_color_table(void); ! #endif /* PITH_OSDEP_COLOR_INCLUDED */ --- 111,124 ---- char *pico_get_last_bg_color(void); char *color_to_canonical_name(char *); int pico_count_in_color_table(void); ! int is_indent(char *, int); ! int get_indent_raw_line (char **, char *, char *, int, int, int); ! int line_isblank(char **, char *, char *, char *, int); ! int strlenis(char *); ! int value_is_space(char *); ! int advance_quote_string(char *, char *, int); ! void free_allowed_qstr(void); ! void add_allowed_qstr(void *, int); ! void record_quote_string (QSTRING_S *); #endif /* PITH_OSDEP_COLOR_INCLUDED */ diff -rc alpine-2.26/pith/pine.hlp alpine-2.26.fillpara/pith/pine.hlp *** alpine-2.26/pith/pine.hlp 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/pine.hlp 2022-06-02 18:14:57.059136043 -0600 *************** *** 8510,8515 **** --- 8510,8555 ---- "type the character ^".

+ This version of Alpine 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, Alpine 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, Alpine 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. Aline + 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> diff -rc alpine-2.26/pith/reply.c alpine-2.26.fillpara/pith/reply.c *** alpine-2.26/pith/reply.c 2022-06-02 18:14:00.495274738 -0600 --- alpine-2.26.fillpara/pith/reply.c 2022-06-02 18:14:57.059136043 -0600 *************** *** 2834,2839 **** --- 2834,2842 ---- if(flow_res && ps_global->reply.use_flowed) wrapflags |= GFW_FLOW_RESULT; + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(select_quote, NULL); + filters[filtcnt].filter = gf_wrap; /* * The 80 will cause longer lines than what is likely *************** *** 2927,2933 **** dq.do_color = 0; dq.delete_all = 1; ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } --- 2930,2936 ---- dq.do_color = 0; dq.delete_all = 1; ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } diff -rc alpine-2.26/pith/state.c alpine-2.26.fillpara/pith/state.c *** alpine-2.26/pith/state.c 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/state.c 2022-06-02 18:14:57.059136043 -0600 *************** *** 231,236 **** --- 231,238 ---- if((*pps)->kw_colors) free_spec_colors(&(*pps)->kw_colors); + free_allowed_qstr(); + if((*pps)->atmts){ int i; diff -rc alpine-2.26/pith/state.h alpine-2.26.fillpara/pith/state.h *** alpine-2.26/pith/state.h 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/state.h 2022-06-02 18:14:57.087135974 -0600 *************** *** 264,269 **** --- 264,271 ---- SPEC_COLOR_S *hdr_colors; /* list of configured colors for view */ SPEC_COLOR_S *index_token_colors; /* list of configured colors for index */ + char *prefix; /* prefix for fillpara */ + char **list_qstr; /* list of known quote strings */ short init_context; struct { diff -rc alpine-2.26/pith/text.c alpine-2.26.fillpara/pith/text.c *** alpine-2.26/pith/text.c 2022-06-02 18:14:00.491274749 -0600 --- alpine-2.26.fillpara/pith/text.c 2022-06-02 18:14:57.091135965 -0600 *************** *** 88,94 **** char *err, *charset; int filtcnt = 0, error_found = 0, column, wrapit; int is_in_sig = OUT_SIG_BLOCK; ! int is_flowed_msg = 0; int is_delsp_yes = 0; int filt_only_c0 = 0; char *parmval; --- 88,94 ---- char *err, *charset; int filtcnt = 0, error_found = 0, column, wrapit; int is_in_sig = OUT_SIG_BLOCK; ! int is_flowed_msg = 0, add_me = 1, doraw = RAWSTRING; int is_delsp_yes = 0; int filt_only_c0 = 0; char *parmval; *************** *** 176,182 **** && pico_usingcolor() && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR){ ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(color_signature, &is_in_sig); } --- 176,182 ---- && pico_usingcolor() && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR){ ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(color_signature, &is_in_sig); } *************** *** 194,202 **** && pico_usingcolor() && VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ ! filters[filtcnt].filter = gf_line_test; ! filters[filtcnt++].data = gf_line_test_opt(color_a_quote, ! &is_flowed_msg); } } else if(!strucmp(att->body->subtype, "richtext")){ --- 194,202 ---- && pico_usingcolor() && VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ ! add_me = 0; ! filters[filtcnt].filter = gf_quote_test; ! filters[filtcnt++].data = gf_line_test_opt(color_a_quote, &is_flowed_msg); } } else if(!strucmp(att->body->subtype, "richtext")){ *************** *** 277,282 **** --- 277,287 ---- } } + if (add_me){ + filters[filtcnt].filter = gf_quote_test; + filters[filtcnt++].data = gf_line_test_opt(select_quote, &doraw); + } + /* * If the message is not flowed, we do the quote suppression before * the wrapping, because the wrapping does not preserve the quote *************** *** 301,307 **** dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } if(ps_global->VAR_QUOTE_REPLACE_STRING --- 306,312 ---- dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } if(ps_global->VAR_QUOTE_REPLACE_STRING *************** *** 360,366 **** dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); ! filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } --- 365,371 ---- dq.handlesp = handlesp; dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); ! filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); } *************** *** 565,571 **** { DELQ_S *dq; char *lp; ! int i, lines, not_a_quote = 0; size_t len = 0; dq = (DELQ_S *) local; --- 570,576 ---- { DELQ_S *dq; char *lp; ! int i, lines, not_a_quote = 0, code; size_t len = 0; dq = (DELQ_S *) local; *************** *** 585,590 **** --- 590,597 ---- for(i = dq->indent_length; i > 0 && !not_a_quote && *lp; i--) if(*lp++ != SPACE) not_a_quote++; + while(isspace((unsigned char) *lp)) + lp++; /* skip over leading tags */ while(!not_a_quote *************** *** 624,636 **** } } ! /* skip over whitespace */ ! if(!dq->is_flowed) ! while(isspace((unsigned char) *lp)) ! lp++; ! ! /* check first character to see if it is a quote */ ! if(!not_a_quote && *lp != '>') not_a_quote++; if(not_a_quote){ --- 631,642 ---- } } ! len = lp - line; ! if(strlen(tmp_20k_buf) > len) ! strcpy(tmp_20k_buf, tmp_20k_buf+len); ! code = (dq->is_flowed ? IS_FLOWED : NO_FLOWED) | DELETEQUO; ! select_quote(linenum, lp, ins, &code); ! if (!not_a_quote && !tmp_20k_buf[0]) not_a_quote++; if(not_a_quote){