1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: color.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006 University of Washington
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * ========================================================================
20 * These routines themselves aren't necessarily OS-specific, they
21 * are all called from within pico, pine and webpine.
23 * They used to be in pico source (osdep/unix, mswin.c), but considering
24 * webpine uses color as well and it should *not* have to be linked
25 * against libpico and considering pico uses these routines but should
26 * not have to link against libpith (and in turn c-client) we put them
27 * in pith/osdep which should only have to link against system libraries
28 * and thus be include freely in all of pine, pico and webpine.
34 #include "./collate.h"
38 * new_color_pair - allocate a new color pair structure assigning
39 * given foreground and background color strings
42 new_color_pair(char *fg
, char *bg
)
46 if((ret
= (COLOR_PAIR
*) malloc(sizeof(*ret
))) != NULL
){
47 memset(ret
, 0, sizeof(*ret
));
49 strncpy(ret
->fg
, fg
, MAXCOLORLEN
);
50 ret
->fg
[MAXCOLORLEN
] = '\0';
54 strncpy(ret
->bg
, bg
, MAXCOLORLEN
);
55 ret
->bg
[MAXCOLORLEN
] = '\0';
64 * free_color_pair - release resources associated with given
65 * color pair structure
68 free_color_pair(COLOR_PAIR
**cp
)
78 * Just like pico_set_color except it doesn't set the color, it just
79 * returns the value. Assumes def of PSC_NONE, since otherwise we always
80 * succeed and don't need to call this.
83 pico_is_good_colorpair(COLOR_PAIR
*cp
)
85 return(cp
&& pico_is_good_color(cp
->fg
) && pico_is_good_color(cp
->bg
));
90 pico_set_colorp(COLOR_PAIR
*col
, int flags
)
92 return(pico_set_colors(col
? col
->fg
: NULL
, col
? col
->bg
: NULL
, flags
));
97 * Extended Justification support also does not belong here
98 * but otherwise webpine will not build, so we move everything
99 * here. Hopefully this will be the permanent place for these
100 * routines. These routines used to be in pico/word.c
103 #include "../../include/general.h"
105 /* Support of indentation of paragraphs */
106 #define is_indent_char(c) (((c) == '.' || (c) == '}' || (c) == RPAREN || \
107 (c) == '*' || (c) == '+' || is_a_digit(c) || \
108 ISspace(c) || (c) == '-' || \
110 #define allowed_after_digit(c,word,k) ((((c) == '.' && \
111 allowed_after_period(next((word),(k)))) ||\
112 (c) == RPAREN || (c) == '}' || (c) == ']' ||\
113 ISspace(c) || is_a_digit(c) || \
115 allowed_after_dash(next((word),(k)))) \
117 #define allowed_after_period(c) (((c) == RPAREN || (c) == '}' || (c) == ']' ||\
118 ISspace(c) || (c) == '-' || \
119 is_a_digit(c)) ? 1 : 0)
120 #define allowed_after_parenth(c) (ISspace(c) ? 1 : 0)
121 #define allowed_after_space(c) (ISspace(c) ? 1 : 0)
122 #define allowed_after_braces(c) (ISspace(c) ? 1 : 0)
123 #define allowed_after_star(c) ((ISspace(c) || (c) == RPAREN ||\
124 (c) == ']' || (c) == '}') ? 1 : 0)
125 #define allowed_after_dash(c) ((ISspace(c) || is_a_digit(c)) ? 1 : 0)
126 #define EOLchar(c) (((c) == '.' || (c) == ':' || (c) == '?' ||\
130 /* Extended justification support */
131 #define is_cquote(c) ((c) == '>' || (c) == '|' || (c) == ']' || (c) == ':')
132 #define is_cword(c) ((((c) >= 'a') && ((c) <= 'z')) || \
133 (((c) >= 'A') && ((c) <= 'Z')) || \
134 (((c) >= '0') && ((c) <= '9')) || \
135 ((c) == ' ') || ((c) == '?') || \
136 ((c) == '@') || ((c) == '.') || \
137 ((c) == '!') || ((c) == '\'') || \
138 ((c) == ',') || ((c) == '\"') ? 1 : 0)
139 #define isaquote(c) ((c) == '\"' || (c) == '\'')
140 #define is8bit(c) ((((int) (c)) & 0x80) ? 1 : 0)
141 #define iscontrol(c) (iscntrl(((int) (c)) & 0x7f) ? 1 : 0)
142 #define forbidden(c) (((c) == '\"') || ((c) == '\'') || ((c) == '$') ||\
143 ((c) == ',') || ((c) == '.') || ((c) == '-') ||\
144 ((c) == LPAREN) || ((c) == '/')|| ((c) == '`') ||\
145 ((c) == '{') || ((c) == '\\') || (iscontrol((c))) ||\
146 (((c) >= '0') && ((c) <= '9')) || ((c) == '?'))
147 #define is_cletter(c) ((((c) >= 'a') && ((c) <= 'z'))) ||\
148 ((((c) >= 'A') && ((c) <= 'Z'))||\
150 #define is_cnumber(c) ((c) >= '0' && (c) <= '9')
151 #define allwd_after_word(c) (((c) == ' ') || ((c) == '>') || is_cletter(c))
152 #define allwd_after_qsword(c) (((c) != '\\') && ((c) != RPAREN))
153 #define before(word,i) (((i) > 0) ? (word)[(i) - 1] : 0)
154 #define next(w,i) ((((w)[(i)]) != 0) ? ((w)[(i) + 1]) : 0)
155 #define now(w,i) ((w)[(i)])
156 #define is_qsword(c) (((c) == ':') || ((c) == RPAREN) ? 1 : 0)
157 #define is_colon(c) (((c) == ':') ? 1 : 0)
158 #define is_rarrow(c) (((c) == '>') ? 1 : 0)
159 #define is_tilde(c) (((c) == '~') ? 1 : 0)
160 #define is_dash(c) (((c) == '-') ? 1 : 0)
161 #define is_pound(c) (((c) == '#') ? 1 : 0)
162 #define is_a_digit(c) ((((c) >= '0') && ((c) <= '9')) ? 1 : 0)
163 #define is_allowed(c) (is_cquote(c) || is_cword(c) || is_dash(c) || \
165 #define qs_allowed(a) (((a)->qstype != qsGdb) && ((a)->qstype != qsProg))
167 /* Internal justification functions */
168 QSTRING_S
*is_quote(char **, char *, int);
169 QSTRING_S
*qs_normal_part(QSTRING_S
*);
170 QSTRING_S
*qs_remove_trailing_spaces(QSTRING_S
*);
171 QSTRING_S
*trim_qs_from_cl(QSTRING_S
*, QSTRING_S
*, QSTRING_S
*);
172 QSTRING_S
*fix_qstring(QSTRING_S
*, QSTRING_S
*, QSTRING_S
*);
173 QSTRING_S
*fix_qstring_allowed(QSTRING_S
*, QSTRING_S
*, QSTRING_S
*);
174 QSTRING_S
*qs_add(char **, char *, QStrType
, int, int, int, int);
175 QSTRING_S
*remove_qsword(QSTRING_S
*);
176 QSTRING_S
*do_raw_quote_match(char **, char *, char *, char *, QSTRING_S
**, QSTRING_S
**);
177 void free_qs(QSTRING_S
**);
178 int word_is_prog(char *);
179 int qstring_is_normal(QSTRING_S
*);
180 int exists_good_part(QSTRING_S
*);
181 int strcmp_qs(char *, char *);
182 int count_levels_qstring(QSTRING_S
*);
183 int same_qstring(QSTRING_S
*, QSTRING_S
*);
184 int isaword(char *,int ,int);
185 int isamailbox(char *,int ,int);
186 int double_check_qstr(char *);
189 word_is_prog(char *word
)
191 static char *list1
[] = {"#include",
198 static char *list2
[] = {"#else",
201 int i
, j
= strlen(word
), k
, rv
= 0;
203 for(i
= 0; rv
== 0 && list1
[i
] && (k
= strlen(list1
[i
])) && k
< j
; i
++)
204 if(!strncmp(list1
[i
], word
, k
) && ISspace(word
[k
]))
210 for(i
= 0; rv
== 0 && list2
[i
] && (k
= strlen(list2
[i
])) && k
<= j
; i
++)
211 if(!strncmp(list2
[i
], word
, k
) && (!word
[k
] || ISspace(word
[k
])))
218 * This function creates a qstring pointer with the information that
219 * is_quote handles to it.
221 * qs - User supplied quote string
222 * word - The line of text that the user is trying to read/justify
223 * beginw - Where we need to start copying from
224 * endw - Where we end copying
225 * offset - Any offset in endw that we need to account for
226 * typeqs - type of the string to be created
227 * neednext - boolean, indicating if we need to compute the next field
230 * It is a mistake to call this function if beginw >= endw + offset.
231 * Please note the equality sign in the above inequality (this is because
232 * we always assume that qstring->value != "").
235 qs_add(char **qs
, char word
[NSTRING
], QStrType typeqs
, int beginw
, int endw
,
236 int offset
, int neednext
)
238 QSTRING_S
*qstring
, *nextqs
;
241 qstring
= (QSTRING_S
*) malloc (sizeof(QSTRING_S
));
242 memset (qstring
, 0, sizeof(QSTRING_S
));
243 qstring
->qstype
= qsNormal
;
246 beginw
= endw
+ offset
;
247 qstring
->qstype
= typeqs
;
250 nextqs
= neednext
? is_quote(qs
, word
+beginw
, 1) : NULL
;
252 qstring
->value
= (char *) malloc((beginw
+1)*sizeof(char));
253 strncpy(qstring
->value
, word
, beginw
);
254 qstring
->value
[beginw
] = '\0';
256 qstring
->next
= nextqs
;
262 qstring_is_normal(QSTRING_S
*cl
)
264 for (;cl
&& (cl
->qstype
== qsNormal
); cl
= cl
->next
);
269 * Given a quote string, this function returns the part that is the leading
270 * normal part of it. (the normal part is the part that is tagged qsNormal,
271 * that is to say, the one that is not controversial at all (like qsString
275 qs_normal_part(QSTRING_S
*cl
)
278 if (!cl
) /* nothing in, nothing out */
281 if (cl
->qstype
!= qsNormal
)
285 cl
->next
= qs_normal_part(cl
->next
);
291 * this function removes trailing spaces from a quote string, but leaves the
292 * last one if there are trailing spaces
295 qs_remove_trailing_spaces(QSTRING_S
*cl
)
298 if (!cl
) /* nothing in, nothing out */
302 cl
->next
= qs_remove_trailing_spaces(cl
->next
);
304 if (value_is_space(cl
->value
))
308 i
= l
= strlen(cl
->value
) - 1;
309 while (cl
->value
&& cl
->value
[i
]
310 && ISspace(cl
->value
[i
]))
312 i
+= (i
< l
) ? 2 : 1;
320 * This function returns if two strings are the same quote string.
321 * The call is not symmetric. cl must preceed the line nl. This function
322 * should be called for comparing the last part of cl and nl.
325 strcmp_qs(char *valuecl
, char *valuenl
)
329 for (j
= 0; valuecl
[j
] && (valuecl
[j
] == valuenl
[j
]); j
++);
330 return !strcmp(valuecl
, valuenl
)
331 || (valuenl
[j
] && value_is_space(valuenl
+j
)
332 && value_is_space(valuecl
+j
)
333 && strlenis(valuecl
+j
) >= strlenis(valuenl
+j
))
334 || (!valuenl
[j
] && value_is_space(valuecl
+j
));
338 count_levels_qstring(QSTRING_S
*cl
)
341 for (count
= 0; cl
; count
++, cl
= cl
->next
);
347 value_is_space(char *value
)
349 for (; value
&& *value
&& ISspace(*value
); value
++);
351 return value
&& *value
? 0 : 1;
355 free_qs(QSTRING_S
**cl
)
361 free_qs(&((*cl
)->next
));
363 (*cl
)->next
= (QSTRING_S
*) NULL
;
366 free((void *)(*cl
)->value
);
367 (*cl
)->value
= (char *) NULL
;
369 *cl
= (QSTRING_S
*) NULL
;
373 * This function returns the number of agreements between
374 * cl and nl. The call is not symmetric. cl must be the line
378 same_qstring(QSTRING_S
*cl
, QSTRING_S
*nl
)
380 int same
= 0, done
= 0;
382 for (;cl
&& nl
&& !done
; cl
= cl
->next
, nl
= nl
->next
)
383 if (cl
->qstype
== nl
->qstype
384 && (!strcmp(cl
->value
, nl
->value
)
385 || (!cl
->next
&& strcmp_qs(cl
->value
, nl
->value
))))
393 trim_qs_from_cl(QSTRING_S
*cl
, QSTRING_S
*nl
, QSTRING_S
*pl
)
395 QSTRING_S
*cqstring
= pl
? pl
: nl
;
396 QSTRING_S
*tl
= pl
? pl
: nl
;
399 if (qstring_is_normal(tl
))
402 p
= same_qstring(pl
? pl
: cl
, pl
? cl
: nl
);
404 for (c
= 1; c
< p
; c
++, cl
= cl
->next
, tl
= tl
->next
);
407 * cl->next and tl->next differ, it may be because cl->next does not
408 * exist or tl->next does not exist or simply both exist but are
409 * different. In this last case, it may be that cl->next->value is made
410 * of spaces. If this is the case, tl advances once more.
414 if (cl
&& cl
->next
&& value_is_space(cl
->next
->value
))
417 free_qs(&(tl
->next
));
426 /* This function trims cl so that it returns a real quote string based
427 * on information gathered from the previous and next lines. pl and cl are
428 * also trimmed, but that is done in another function, not here.
431 fix_qstring(QSTRING_S
*cl
, QSTRING_S
*nl
, QSTRING_S
*pl
)
433 QSTRING_S
*cqstring
= cl
, *nqstring
= nl
, *pqstring
= pl
;
436 if (qstring_is_normal(cl
))
439 c
= count_levels_qstring(cl
);
440 n
= same_qstring(cl
,nl
);
442 if (!n
){ /* no next line or no agreement with next line */
443 int p
= same_qstring(pl
, cl
); /* number of agreements between pl and cl */
444 QSTRING_S
*tl
; /* test line */
447 * Here p <= c, so either p < c or p == c. If p == c, we are done,
448 * and return cl. If not, there are two cases, either p == 0 or
449 * 0 < p < c. In the first case, we do not have enough evidence
450 * to return anything other than the normal part of cl, in the second
451 * case we can only return p levels of cl.
458 for (c
= 1; c
< p
; c
++)
460 free_qs(&(cl
->next
));
465 QSTRING_S
*al
= cl
; /* another line */
467 * Ok, we really don't have enough evidence to return anything,
468 * different from the normal part of cl, but it could be possible
469 * that we may want to accept the not-normal part, so we better
470 * make an extra test to determine what needs to be freed
472 while (pl
&& cl
&& cl
->qstype
== pl
->qstype
473 && !strucmp(cl
->value
, pl
->value
)){
477 if (pl
&& cl
&& cl
->qstype
== pl
->qstype
478 && strcmp_qs(pl
->value
, cl
->value
))
479 cl
= cl
->next
; /* next level differs only in spaces */
481 while (cl
&& cl
->qstype
== qsNormal
)
484 if ((cl
->qstype
== qsString
)
485 && (cl
->value
[strlen(cl
->value
) - 1] == '>'))
496 while (al
&& (al
->next
!= cl
))
500 free_qs(&(cl
->next
));
507 if (n
+ 1 < c
){ /* if there are not enough agreements */
508 int p
= same_qstring(pl
, cl
); /* number of agreement between pl and cl */
509 QSTRING_S
*tl
; /* test line */
511 * There's no way we can use cl in this case, but we can use
512 * part of cl, this is if pl does not have more agreements
518 int m
= p
< n
? n
: p
;
519 for (c
= 1; c
< m
; c
++){
520 pl
= pl
? pl
->next
: (QSTRING_S
*) NULL
;
521 nl
= nl
? nl
->next
: (QSTRING_S
*) NULL
;
524 if (p
== n
&& pl
&& pl
->next
&& nl
&& nl
->next
525 && ((cl
->next
->qstype
== pl
->next
->qstype
)
526 || (cl
->next
->qstype
== nl
->next
->qstype
))
527 && (strcmp_qs(cl
->next
->value
, pl
->next
->value
)
528 || strcmp_qs(pl
->next
->value
, cl
->next
->value
)
529 || strcmp_qs(cl
->next
->value
, nl
->next
->value
)
530 || strcmp_qs(nl
->next
->value
, cl
->next
->value
)))
531 cl
= cl
->next
; /* next level differs only in spaces */
533 free_qs(&(cl
->next
));
539 int p
= same_qstring(pl
, cl
);
540 QSTRING_S
*tl
; /* test line */
543 * p <= c, so p <= n+1, which means p < n + 1 or p == n + 1.
544 * If p < n + 1, then p <= n.
545 * so we have three possibilities:
546 * p == n + 1 or p == n or p < n.
547 * In the first case we copy p == n + 1 == c levels, in the second
548 * and third case we copy n levels, and check if we can copy the
551 if (p
== n
+ 1) /* p == c, in the above sense of c */
552 tl
= cl
; /* use cl, this is enough evidence */
554 for (c
= 1; c
< n
; c
++)
557 * Here c == n, we only have one more level of cl, and at least one
560 if (cl
->next
->qstype
== qsNormal
)
563 free_qs(&(cl
->next
));
568 if (n
== c
) /* Yeah!!! */
573 fix_qstring_allowed(QSTRING_S
*cl
, QSTRING_S
*nl
, QSTRING_S
*pl
)
576 return (QSTRING_S
*) NULL
;
579 cl
->next
= fix_qstring_allowed(cl
->next
, (nl
? nl
->next
: NULL
),
580 (pl
? pl
->next
: NULL
));
582 if((nl
&& cl
->qstype
== nl
->qstype
) || (pl
&& cl
->qstype
== pl
->qstype
)
589 * This function flattens the quote string returned to us by is_quote. A
590 * crash in this function implies a bug elsewhere.
593 flatten_qstring(QSTRING_S
*qs
, char *buff
, int bufflen
)
596 if(!buff
|| bufflen
<= 0)
599 for (i
= 0; qs
; qs
= qs
->next
)
600 for (j
= 0; i
< bufflen
- 1
601 && (qs
->value
[j
]) && (buff
[i
++] = qs
->value
[j
]); j
++);
609 double_check_qstr(char *q
)
614 return (*q
== '#') ? 1 : 0;
618 * Given a string, we return the position where the function thinks that
619 * the quote string is over, if you are ever thinking of fixing something,
620 * you got to the right place. Memory freed by caller. Experience shows
621 * that it only makes sense to initialize memory when we need it, not at
622 * the start of this function.
625 is_quote (char **qs
,char *word
, int been_here
)
627 int i
= 0, j
, nxt
, prev
, finished
= 0, offset
;
629 QSTRING_S
*qstring
= (QSTRING_S
*) NULL
;
631 if (word
== NULL
|| word
[0] == '\0')
632 return (QSTRING_S
*) NULL
;
636 * Before we apply our rules, let's advance past the quote string
637 * given by the user, this will avoid not recognition of the
638 * user's indent string and application of the arbitrary rules
639 * below. Notice that this step may bring bugs into this
640 * procedure, but these bugs will only appear if the indent string
641 * is really really strange and the text to be justified
642 * cooperates a lot too, so in general this will not be a problem.
643 * If you are concerned about this bug, simply remove the
644 * following lines after this comment and before the "switch"
645 * command below and use a more normal quote string!.
647 for(j
= 0; j
< list_len
; j
++){
648 if(!double_check_qstr(qs
[j
])){
649 i
+= advance_quote_string(qs
[j
], word
, i
);
650 if (!word
[i
]) /* went too far? */
651 return qs_add(qs
, word
, qsNormal
, 0, i
, 0, 0);
657 switch (c
= (unsigned char) now(word
,i
)){
660 case ' ' : { QSTRING_S
*nextqs
, *d
;
662 for (; ISspace(word
[i
]); i
++); /* FIX ME */
663 nextqs
= is_quote(qs
,word
+i
, 1);
665 * Merge qstring and nextqs, since this is an artificial
666 * separation, unless nextqs is of different type.
667 * What this means in practice is that if
668 * qs->qstype == qsNormal and qs->next != NULL, then
669 * qs->next->qstype != qsNormal.
671 * Can't use qs_add to merge because it could lead
672 * to an infinite loop (e.g a line "^ ^").
674 i
+= nextqs
&& nextqs
->qstype
== qsNormal
675 ? strlen(nextqs
->value
) : 0;
676 qstring
= (QSTRING_S
*) malloc (sizeof(QSTRING_S
));
677 memset (qstring
, 0, sizeof(QSTRING_S
));
678 qstring
->value
= (char *) malloc((i
+1)*sizeof(char));
679 strncpy(qstring
->value
, word
, i
);
680 qstring
->value
[i
] = '\0';
681 qstring
->qstype
= qsNormal
;
682 if(nextqs
&& nextqs
->qstype
== qsNormal
){
689 qstring
->next
= nextqs
;
694 case RPAREN
: /* parenthesis ')' */
695 if ((i
!= 0) || ((i
== 0) && been_here
))
699 return qs_add(qs
, word
, qsChar
, i
, i
, 1, 1);
704 case ':': /* colon */
705 case '~': nxt
= next(word
,i
);
706 if ((is_tilde(c
) && (nxt
== '/'))
707 || (is_colon(c
) && !is_cquote(nxt
)
708 && !is_cword(nxt
) && nxt
!= RPAREN
))
710 else if (is_cquote(c
)
712 || (c
!= '~' && nxt
== RPAREN
)
713 || (i
!= 0 && ISspace(nxt
))
714 || is_cquote(prev
= before(word
,i
))
715 || (ISspace(prev
) && !is_tilde(c
))
716 || (is_tilde(c
) && nxt
!= '/'))
718 else if (i
== 0 && been_here
)
719 return qs_add(qs
, word
, qsChar
, i
, i
, 1, 1);
726 case '-' : offset
= is_cquote(nxt
= next(word
,i
)) ? 2
727 : (nxt
== c
&& is_cquote(next(word
,i
+1))) ? 3 : -1;
730 return qs_add(qs
, word
, qsString
, i
, i
, offset
, 1);
736 case '+' : /* accept +>, *> */
737 case '*' : if (is_rarrow(nxt
= next(word
, i
)) || /* stars */
738 (ISspace(nxt
) && is_rarrow(next(word
,i
+1))))
746 case '%' : if (next(word
,i
) != c
)
747 return qs_add(qs
, word
, qsChar
, i
, i
+1, 0, 1);
752 case '_' : if(ISspace(next(word
, i
)))
753 return qs_add(qs
, word
, qsChar
, i
, i
+1, 0, 1);
758 case '#' : { QStrType qstype
= qsChar
;
759 if((nxt
= next(word
, i
)) != c
){
760 if(isdigit((int) nxt
))
763 if(word_is_prog(word
))
765 return qs_add(qs
, word
, qstype
, i
, i
+1, 0, 1);
775 else if (is_cletter(c
)){
776 for (j
= i
; (is_cletter(nxt
= next(word
,j
)) || is_cnumber(nxt
))
777 && !(ISspace(nxt
));j
++);
779 * The whole reason why we are splitting the quote
780 * string is so that we will be able to accept quote
781 * strings that are strange in some way. Here we got to
782 * a point in which a quote string might exist, but it
783 * could be strange, so we need to create a "next" field
784 * for the quote string to warn us that something
785 * strange is coming. We need to confirm if this is a
786 * good choice later. For now we will let it pass.
788 if (isaword(word
,i
,j
) || isamailbox(word
,i
,j
)){
792 offset
= (is_cquote(c
= next(word
,j
))
793 || (c
== RPAREN
)) ? 2
795 && is_cquote(next(word
,j
+1))) ? 3 : -1);
797 qstype
= (is_cquote(c
) || (c
== RPAREN
))
798 ? (is_qsword(c
) ? qsWord
: qsString
)
799 : ((ISspace(c
) && is_cquote(next(word
,j
+1)))
800 ? (is_qsword(next(word
,j
+1))
805 * qsWords are valid quote strings only when
806 * they are followed by text.
808 if (offset
> 0 && qstype
== qsWord
&&
809 !allwd_after_qsword(now(word
,j
+ offset
)))
813 return qs_add(qs
, word
, qstype
, i
, j
, offset
, 1);
819 return qs_add(qs
, word
, qsNormal
, 0, i
, 0, 1);
820 else if(!forbidden(c
))
821 return qs_add(qs
, word
, qsChar
, 0, 1, 0, 1);
822 else /* chao pescao */
829 qstring
= qs_add(qs
, word
, qsNormal
, 0, i
, 0, 0);
834 isaword(char word
[NSTRING
], int i
, int j
)
836 return i
<= j
&& is_cletter(word
[i
]) ?
837 (i
< j
? isaword(word
,i
+1,j
) : 1) : 0;
841 isamailbox(char word
[NSTRING
], int i
, int j
)
843 return i
<= j
&& (is_cletter(word
[i
]) || is_a_digit(word
[i
])
845 ? (i
< j
? isamailbox(word
,i
+1,j
) : 1) : 0;
849 This routine removes the last part that is qsword or qschar that is not
850 followed by a normal part. This means that if a qsword or qschar is
851 followed by a qsnormal (or qsstring), we accept the qsword (or qschar)
852 as part of a quote string.
855 remove_qsword(QSTRING_S
*cl
)
858 QSTRING_S
*cp
= np
; /* this variable trails cl */
861 while (cl
&& cl
->qstype
== qsNormal
)
865 if (((cl
->qstype
== qsWord
) || (cl
->qstype
== qsChar
))
866 && !exists_good_part(cl
)){
867 if (np
== cl
) /* qsword or qschar at the beginning */
870 while (np
->next
!= cl
)
872 free_qs(&(np
->next
));
886 exists_good_part (QSTRING_S
*cl
)
888 return (cl
? (((cl
->qstype
!= qsWord
) && (cl
->qstype
!= qsChar
)
889 && qs_allowed(cl
) && !value_is_space(cl
->value
))
890 ? 1 : exists_good_part(cl
->next
))
895 line_isblank(char **q
, char *GLine
, char *NLine
, char *PLine
, int buflen
)
901 cl
= do_raw_quote_match(q
, GLine
, NLine
, PLine
, NULL
, NULL
);
903 flatten_qstring(cl
, qstr
, NSTRING
);
907 for(n
= strlen(qstr
); n
< buflen
&& GLine
[n
]; n
++)
908 if(!ISspace((unsigned char) GLine
[n
]))
915 do_raw_quote_match(char **q
, char *GLine
, char *NLine
, char *PLine
, QSTRING_S
**nlp
, QSTRING_S
**plp
)
917 QSTRING_S
*cl
, *nl
= NULL
, *pl
= NULL
;
918 char nbuf
[NSTRING
], pbuf
[NSTRING
], buf
[NSTRING
];
919 int emptypl
= 0, emptynl
= 0;
921 if (!(cl
= is_quote(q
, GLine
, 0))) /* if nothing in, nothing out */
924 nl
= is_quote(q
, NLine
, 0); /* Next Line */
926 pl
= is_quote(q
, PLine
, 0); /* Previous Line */
929 * If there's nothing in the preceeding or following line
930 * there is not enough information to accept it or discard it. In this
931 * case it's likely to be an isolated line, so we better accept it
932 * if it does not look like a word.
934 flatten_qstring(pl
, pbuf
, NSTRING
);
935 emptypl
= (!PLine
|| !PLine
[0] ||
936 (pl
&& value_is_space(pbuf
)) && !PLine
[strlen(pbuf
)]) ? 1 : 0;
938 flatten_qstring(nl
, nbuf
, NSTRING
);
939 emptynl
= (!NLine
|| !NLine
[0] ||
940 (nl
&& value_is_space(nbuf
) && !NLine
[strlen(nbuf
)])) ? 1 : 0;
942 cl
= remove_qsword(cl
);
943 if((cl
= fix_qstring_allowed(cl
, NULL
, NULL
)) != NULL
)
944 cl
= qs_remove_trailing_spaces(cl
);
955 * If either cl, nl or pl contain suspicious characters that may make
956 * them (or not) be quote strings, we need to fix them, so that the
957 * next pass will be done correctly.
960 cl
= fix_qstring(cl
, nl
, pl
);
961 nl
= trim_qs_from_cl(cl
, nl
, NULL
);
962 pl
= trim_qs_from_cl(cl
, NULL
, pl
);
963 if((cl
= fix_qstring_allowed(cl
, nl
, pl
)) != NULL
){
964 nl
= trim_qs_from_cl(cl
, nl
, NULL
);
965 pl
= trim_qs_from_cl(cl
, NULL
, pl
);
983 do_quote_match(char **q
, char *GLine
, char *NLine
, char *PLine
, char *rqstr
,
984 int rqstrlen
, int plb
)
986 QSTRING_S
*cl
, *nl
= NULL
, *pl
= NULL
;
987 int c
, n
, p
,i
, j
, NewP
, NewC
, NewN
, clength
, same
= 0;
988 char nbuf
[NSTRING
], pbuf
[NSTRING
], buf
[NSTRING
];
993 /* if nothing in, nothing out */
994 cl
= do_raw_quote_match(q
, GLine
, NLine
, PLine
, &nl
, &pl
);
1001 flatten_qstring(cl
, rqstr
, rqstrlen
);
1002 flatten_qstring(cl
, buf
, NSTRING
);
1003 flatten_qstring(nl
, nbuf
, NSTRING
);
1004 flatten_qstring(pl
, pbuf
, NSTRING
);
1007 * Once upon a time, is_quote used to return the length of the quote
1008 * string that it had found. One day, not long ago, black hand came
1009 * and changed all that, and made is_quote return a quote string
1010 * divided in several fields, making the algorithm much more
1011 * complicated. Fortunately black hand left a few comments in the
1012 * source code to make it more understandable. Because of this change
1013 * we need to compute the lengths of the quote strings separately
1015 c
= buf
&& buf
[0] ? strlen(buf
) : 0;
1016 n
= nbuf
&& nbuf
[0] ? strlen(nbuf
) : 0;
1017 p
= pbuf
&& pbuf
[0] ? strlen(pbuf
) : 0;
1019 * When quote strings contain only blank spaces (ascii code 32) the
1020 * above count is equal to the length of the quote string, but if
1021 * there are TABS, the length of the quote string as seen by the user
1022 * is different than the number that was just computed. Because of
1023 * this we demand a recount (hmm.. unless you are in Florida, where
1024 * recounts are forbidden)
1026 NewP
= strlenis(pbuf
);
1027 NewC
= strlenis(buf
);
1028 NewN
= strlenis(nbuf
);
1031 * For paragraphs with spaces in the first line, but no space in the
1032 * quote string of the second line, we make sure we choose the quote
1033 * string without a space at the end of it.
1035 if ((NLine
&& !NLine
[0])
1036 && ((PLine
&& !PLine
[0])
1037 || (((same
= same_qstring(pl
, cl
)) != 0)
1038 && (same
!= count_levels_qstring(cl
)))))
1039 cl
= qs_remove_trailing_spaces(cl
);
1043 for (j
= 0; (j
< n
) && (GLine
[j
] == NLine
[j
]); j
++);
1045 /* clength is the common length in which Gline and Nline agree */
1046 /* j < n means that they do not agree fully */
1047 /* GLine = " \tText"
1051 if (clength
< n
){ /* see if buf and nbuf are padded with spaces and tabs */
1052 for (i
= clength
; i
< n
&& ISspace(NLine
[i
]); i
++);
1053 if (i
== n
){/* padded NLine until the end of spaces? */
1054 for (i
= clength
; i
< c
&& ISspace(GLine
[i
]); i
++);
1055 if (i
== c
) /* Padded CLine until the end of spaces? */
1060 for (j
= clength
; j
< c
&& ISspace(GLine
[j
]); j
++);
1063 * If we get here, it means that the current line has the same
1064 * quote string (visually) than the next line, but both of them
1065 * are padded with different amount of TABS or spaces at the end.
1066 * The current line (GLine) has more spaces/TABs than the next
1067 * line. This is the typical situation that is found at the
1068 * begining of a paragraph. We need to check this, however, by
1069 * checking the previous line. This avoids that we confuse
1070 * ourselves with being in the last line of a paragraph.
1071 * Example when it should not free_qs(cl)
1072 * " Text in Paragraph 1" (PLine)
1073 * " Text in Paragraph 1" (GLine)
1074 * " Other Paragraph Number 2" (NLine)
1076 * Example when it should free_qs(cl):
1077 * ":) " (PLine) p = 3, j = 3
1078 * ":) Text" (GLine) c = 5
1079 * ":) More text" (NLine) n = 3
1081 * Example when it should free_qs(cl):
1082 * ":) " (PLine) p = 3, j = 3
1083 * ":) > > > Text" (GLine) c = 11
1084 * ":) > > > More text" (NLine) n = 9
1086 * Example when it should free_qs(cl):
1087 * ":) :) " (PLine) p = 6, j = 3
1088 * ":) > > > Text" (GLine) c = 11
1089 * ":) > > > More text" (NLine) n = 9
1091 * Example when it should free_qs(cl):
1092 * ":) > > > " (PLine) p = 13, j = 11
1093 * ":) > > > Text" (GLine) c = 11
1094 * ":) > > > More text" (NLine) n = 9
1096 * The following example is very interesting. The "Other Text"
1097 * line below should free the quote string an make it equal to the
1098 * quote string of the line below it, but any algorithm trying
1099 * to advance past that line should make it stop there, so
1100 * we need one more check, to check the raw quote string and the
1101 * processed quote string at the same time.
1102 * FREE qs in this example.
1103 * " Some Text" (PLine) p = 3, j = 0
1104 * "\tOther Text" (GLine) c = 1
1105 * " More Text" (NLine) n = 3
1108 for (j
= 0; (j
< p
) && (GLine
[j
] == PLine
[j
]); j
++);
1109 if ((p
!= c
|| j
!= p
) && NLine
[n
])
1110 if(!get_indent_raw_line(q
, PLine
, nbuf
, NSTRING
, p
, plb
)
1111 || NewP
+ strlenis(nbuf
) != NewC
){
1127 * Given a line, an initial position, and a quote string, we advance the
1128 * current line past the quote string, including arbitraty spaces
1129 * contained in the line, except that it removes trailing spaces. We do
1130 * not handle TABs, if any, contained in the quote string. At least not
1133 * Arguments: q - quote string
1134 * l - a line to process
1135 * i - position in the line to start processing. i = 0 is the
1136 * begining of that line.
1139 advance_quote_string(char *q
, char l
[NSTRING
], int i
)
1141 int n
= 0, j
= 0, is
= 0, es
= 0;
1143 char qs
[NSTRING
] = {'\0'};
1146 for (p
= strlen(q
); (p
> 0) && (q
[p
- 1] == ' '); p
--, es
++);
1147 if (!p
){ /* string contains only spaces */
1148 for (k
= 0; ISspace(l
[i
+ k
]); k
++);
1152 for (is
= 0; ISspace(q
[is
]); is
++); /* count initial spaces */
1153 for (m
= 0 ; is
+ m
< p
; m
++)
1154 qs
[m
] = q
[is
+ m
]; /* qs = quote string without any space at the end */
1155 /* advance as many spaces as there are at the begining */
1156 for (k
= 0; ISspace(l
[i
+ j
]); k
++, j
++);
1157 /* now find the visible string in the line */
1158 for (m
= 0; qs
[m
] && l
[i
+ j
] == qs
[m
]; m
++, j
++);
1159 if (!qs
[m
]){ /* no match */
1161 * So far we have advanced at least "is" spaces, plus the visible
1162 * string "qs". Now we need to advance the trailing number of
1163 * spaces "es". If we can do that, we have found the quote string.
1165 for (p
= 0; ISspace(l
[i
+ j
+ p
]); p
++);
1166 adv
= advance_quote_string(q
, l
, i
+ j
+ ((p
< es
) ? p
: es
));
1167 n
= ((p
< es
) ? 0 : es
) + k
+ m
+ adv
;
1173 * This function returns the effective length in screen of the quote
1174 * string. If the string contains a TAB character, it is added here, if
1175 * not, the length returned is the length of the string
1177 int strlenis(char *qstr
)
1180 for (i
= 0; qstr
&& qstr
[i
]; i
++)
1181 rv
+= ((qstr
[i
] == TAB
) ? (~rv
& 0x07) + 1 : 1);
1186 is_indent (char word
[NSTRING
], int plb
)
1188 int i
= 0, finished
= 0, c
, nxt
, j
, k
, digit
= 0, bdigits
= -1, alpha
= 0;
1190 if (!word
|| !word
[0])
1193 for (i
= 0, j
= 0; ISspace(word
[i
]); i
++, j
++);
1194 while ((i
< NSTRING
- 2) && !finished
){
1195 switch (c
= now(word
,i
)){
1198 case ' ' : for (; ISspace(word
[i
]); i
++);
1199 if (!is_indent_char(now(word
,i
)))
1211 if ((c
== '.' && allowed_after_period(nxt
) && alpha
)
1212 || (c
== '*' && allowed_after_star(nxt
))
1213 || (c
== '}' && allowed_after_braces(nxt
))
1214 || (c
== '-' && allowed_after_dash(nxt
))
1215 || (c
== '+' && allowed_after_dash(nxt
))
1216 || (c
== RPAREN
&& allowed_after_parenth(nxt
))
1217 || (c
== ']' && allowed_after_parenth(nxt
)))
1223 default : if (is_a_digit(c
) && plb
){
1225 bdigits
= i
; /* first digit */
1226 for (k
= i
; is_a_digit(now(word
,k
)); k
++);
1227 if (k
- bdigits
> 2){ /* more than 2 digits? */
1228 i
= bdigits
; /* too many! */
1232 if(allowed_after_digit(now(word
,k
),word
,k
)){
1249 i
= 0; /* there must be something more than spaces in an indent string */
1254 get_indent_raw_line(char **q
, char *GLine
, char *buf
, int buflen
, int k
, int plb
)
1257 char testline
[1024];
1260 for(j
= 0; GLine
[j
] != '\0'; j
++){
1261 testline
[j
] = GLine
[j
];
1262 testline
[j
+1] = '\0';
1263 if(strlenis(testline
) >= strlenis(buf
))
1266 k
= ++j
; /* reset k */
1268 i
= is_indent(GLine
+k
, plb
);
1270 for (j
= 0; j
< i
&& j
< buflen
&& (buf
[j
] = GLine
[j
+ k
]); j
++);
1276 /* support for remembering quote strings across messages */
1277 char **allowed_qstr
= NULL
;
1281 free_allowed_qstr(void)
1284 char **q
= allowed_qstr
;
1289 for(i
= 0; i
< list_len
; i
++)
1290 fs_give((void **)&q
[i
]);
1292 fs_give((void **)q
);
1297 add_allowed_qstr(void *q
, int type
)
1301 if(allowed_qstr
== NULL
){
1302 allowed_qstr
= malloc(sizeof(char *));
1307 allowed_qstr
[list_len
] = malloc((1+strlen((char *)q
))*sizeof(char));
1308 strcpy(allowed_qstr
[list_len
], (char *)q
);
1311 allowed_qstr
[list_len
] = (char *) ucs4_to_utf8_cpystr((UCS
*)q
);
1313 fs_resize((void **)&allowed_qstr
, (++list_len
+ 1)*sizeof(char *));
1314 allowed_qstr
[list_len
] = NULL
;
1318 record_quote_string (QSTRING_S
*qs
)
1322 for(; qs
&& qs
->value
; qs
= qs
->next
){
1326 for(i
= 0; i
< list_len
; i
++){
1327 j
+= advance_quote_string(allowed_qstr
[i
], qs
->value
, j
);
1328 for(; ISspace(qs
->value
[j
]); j
++);
1333 if(qs
->value
[j
] != '\0')
1334 add_allowed_qstr((void *)(qs
->value
+ j
), 0);
1338 /* type utf8: code 0; ucs4: code 1. */
1340 default_qstr(void *q
, int type
)
1342 if(allowed_qstr
== NULL
)
1343 add_allowed_qstr(q
, type
);
1345 return allowed_qstr
;