1 /* -*- mode: C; mode: fold; -*- */
2 /*
3 This file is part of SLRN.
4
5 Copyright (c) 1994, 1999, 2007-2016 John E. Davis <jed@jedsoft.org>
6 Copyright (c) 2001-2006 Thomas Schultz <tststs@gmx.de>
7
8 This program is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2 of the License, or (at your option)
11 any later version.
12
13 This program is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22 #include "config.h"
23 #include "slrnfeat.h"
24
25 /*{{{ system include files */
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <time.h>
30
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34
35 #ifdef HAVE_STDLIB_H
36 # include <stdlib.h>
37 #endif
38
39 #include <ctype.h>
40 #include <slang.h>
41 #include "jdmacros.h"
42
43 #include "slrn.h"
44 #include "group.h"
45 #include "util.h"
46 #include "strutil.h"
47 #include "server.h"
48 #include "art.h"
49 #include "misc.h"
50 #include "post.h"
51 /* #include "clientlib.h" */
52 #include "startup.h"
53 #include "hash.h"
54 #include "score.h"
55 #include "menu.h"
56 #include "xover.h"
57 #include "print.h"
58
59 /*}}}*/
60
61 static int Art_Hide_Quote_Level = 1;
62 int Slrn_Wrap_Mode = 3;
63 int Slrn_Wrap_Method = 2;
64 int Slrn_Wrap_Width = -1;
65
66 static char *Super_Cite_Regexp = "^[^A-Za-z0-9]*\"\\([-_a-zA-Z/]+\\)\" == .+";
67
hide_article_lines(Slrn_Article_Type * a,unsigned int mask)68 static void hide_article_lines (Slrn_Article_Type *a, unsigned int mask) /*{{{*/
69 {
70 Slrn_Article_Line_Type *l;
71
72 if (a == NULL)
73 return;
74
75 a->needs_sync = 1;
76 l = a->lines;
77
78 while (l != NULL)
79 {
80 if (l->flags & mask)
81 l->flags |= HIDDEN_LINE;
82
83 l = l->next;
84 }
85 }
86
87 /*}}}*/
88
unhide_article_lines(Slrn_Article_Type * a,unsigned int mask)89 static void unhide_article_lines (Slrn_Article_Type *a, unsigned int mask) /*{{{*/
90 {
91 Slrn_Article_Line_Type *l;
92
93 if (a == NULL)
94 return;
95
96 a->needs_sync = 1;
97 l = a->lines;
98
99 while (l != NULL)
100 {
101 if (l->flags & mask)
102 l->flags &= ~HIDDEN_LINE;
103
104 l = l->next;
105 }
106 }
107
108 /*}}}*/
109
110 /* The function that set the needs_sync flag article line number start with _
111 */
112
_slrn_art_unhide_quotes(Slrn_Article_Type * a)113 int _slrn_art_unhide_quotes (Slrn_Article_Type *a) /*{{{*/
114 {
115 if (a == NULL)
116 return -1;
117
118 unhide_article_lines (a, QUOTE_LINE);
119 a->quotes_hidden = 0;
120 return 0;
121 }
122
123 /*}}}*/
124
is_matching_line(unsigned char * b,SLRegexp_Type ** r)125 static unsigned char *is_matching_line (unsigned char *b, SLRegexp_Type **r) /*{{{*/
126 {
127 unsigned int len;
128 len = strlen ((char *) b);
129
130 while (*r != NULL)
131 {
132 SLRegexp_Type *re;
133 unsigned int match_len;
134
135 re = *r++;
136 #if SLANG_VERSION < 20000
137 if ((re->min_length > len)
138 || (b != SLang_regexp_match (b, len, re)))
139 continue;
140 match_len = re->end_matches[0];
141 #else
142 if (b != (unsigned char *) SLregexp_match (re, (char *) b, len))
143 continue;
144 (void) SLregexp_nth_match (re, 0, NULL, &match_len);
145 #endif
146 return b + match_len;
147 }
148 return NULL;
149 }
150
151 /*}}}*/
152
_slrn_art_hide_quotes(Slrn_Article_Type * a,int reset)153 int _slrn_art_hide_quotes (Slrn_Article_Type *a, int reset) /*{{{*/
154 {
155 Slrn_Article_Line_Type *l, *last;
156
157 if (a == NULL)
158 return -1;
159
160 _slrn_art_unhide_quotes (a);
161
162 a->needs_sync = 1;
163
164 if (!reset)
165 Art_Hide_Quote_Level = Slrn_Quotes_Hidden_Mode;
166
167 l = a->lines;
168 last = NULL;
169
170 while (l != NULL)
171 {
172 if (l->flags & QUOTE_LINE)
173 {
174 if (Art_Hide_Quote_Level > 1)
175 {
176 if (l->v.quote_level + 1 >= (unsigned int) Art_Hide_Quote_Level)
177 l->flags |= HIDDEN_LINE;
178 }
179 /* Show first line of quoted material; try to pick one that
180 * actually has text on it! */
181 else
182 {
183 if (last != NULL)
184 l->flags |= HIDDEN_LINE;
185 else
186 {
187 last = l;
188 if ((l->next != NULL) && (l->next->flags & QUOTE_LINE))
189 {
190 unsigned char *str, *line = (unsigned char*)l->buf;
191 while (NULL != (str = is_matching_line
192 (line, Slrn_Ignore_Quote_Regexp)))
193 line = str;
194 if (0 == *(slrn_skip_whitespace((char*)line)))
195 {
196 l->flags |= HIDDEN_LINE;
197 last = NULL;
198 }
199 }
200 }
201 }
202 }
203 else last = NULL;
204 l = l->next;
205 }
206 a->quotes_hidden = Art_Hide_Quote_Level;
207 return 0;
208 }
209
210 /*}}}*/
211
212 #if SLRN_HAS_SPOILERS
slrn_art_mark_spoilers(Slrn_Article_Type * a)213 void slrn_art_mark_spoilers (Slrn_Article_Type *a) /*{{{*/
214 {
215 Slrn_Article_Line_Type *l;
216
217 if ((Slrn_Spoiler_Char == 0) || (a == NULL))
218 return;
219
220 l = a->lines;
221
222 while ((l != NULL) && (l->flags & HEADER_LINE))
223 l = l->next; /* skip header */
224
225 while ((l != NULL) && (NULL == slrn_strbyte (l->buf, 12)))
226 l = l->next; /* skip to first formfeed */
227
228 while (l != NULL)
229 {
230 l->flags |= SPOILER_LINE;
231 l = l->next;
232 }
233 }
234
235 /*}}}*/
236 #endif
237
try_supercite(Slrn_Article_Line_Type * l)238 static int try_supercite (Slrn_Article_Line_Type *l) /*{{{*/
239 {
240 Slrn_Article_Line_Type *last, *lsave;
241 #if SLANG_VERSION < 20000
242 static unsigned char compiled_pattern_buf[256];
243 static SLRegexp_Type re_buf;
244 SLRegexp_Type *re;
245 #else
246 static SLRegexp_Type *re = NULL;
247 #endif
248 unsigned char *b;
249 int count;
250 char name[32];
251 unsigned int len;
252 unsigned int ofs;
253 int ret;
254
255 #if SLANG_VERSION < 20000
256 re = &re_buf;
257 re->pat = (unsigned char *) Super_Cite_Regexp;
258 re->buf = compiled_pattern_buf;
259 re->case_sensitive = 1;
260 re->buf_len = sizeof (compiled_pattern_buf);
261 if ((*compiled_pattern_buf == 0) && SLang_regexp_compile (re))
262 return -1;
263 #else
264 if ((re == NULL)
265 && (NULL == (re = SLregexp_compile (Super_Cite_Regexp, 0))))
266 return -1;
267 #endif
268
269 /* skip header --- I should look for Xnewsreader: gnus */
270 while ((l != NULL) && (*l->buf != 0)) l = l->next;
271
272 /* look at the first 15 lines on first attempt.
273 * After that, scan the whole buffer looking for more citations */
274 count = 15;
275 lsave = l;
276 ret = -1;
277 while (1)
278 {
279 while (count && (l != NULL))
280 {
281 if ((l->flags & QUOTE_LINE) == 0)
282 {
283 if (NULL != slrn_regexp_match (re, l->buf))
284 {
285 l->flags |= QUOTE_LINE;
286 break;
287 }
288 }
289 l = l->next;
290 count--;
291 }
292
293 if ((l == NULL) || (count == 0)) return ret;
294
295 /* Now find out what is used for citing. */
296 if (-1 == SLregexp_nth_match (re, 1, &ofs, &len))
297 return ret;
298
299 b = (unsigned char *) l->buf + ofs;
300 if (len > sizeof (name) - 2) return ret;
301
302 ret = 0;
303 strncpy (name, (char *) b, len); name[len] = 0;
304
305 while (l != NULL)
306 {
307 unsigned char ch;
308
309 b = (unsigned char *) l->buf;
310 last = l;
311 l = l->next;
312 if (last->flags & QUOTE_LINE) continue;
313
314 b = (unsigned char *) slrn_skip_whitespace ((char *) b);
315
316 if (!strncmp ((char *) b, name, len)
317 && (((ch = b[len] | 0x20) < 'a')
318 || (ch > 'z')))
319 {
320 last->flags |= QUOTE_LINE;
321
322 while (l != NULL)
323 {
324 b = (unsigned char *) slrn_skip_whitespace (l->buf);
325 if (strncmp ((char *) b, name, len)
326 || (((ch = b[len] | 0x20) >= 'a')
327 && (ch <= 'z')))
328 break;
329 l->flags |= QUOTE_LINE;
330 l = l->next;
331 }
332 }
333 }
334 count = -1;
335 l = lsave;
336 }
337 }
338
339 /*}}}*/
340
slrn_art_mark_quotes(Slrn_Article_Type * a)341 void slrn_art_mark_quotes (Slrn_Article_Type *a) /*{{{*/
342 {
343 Slrn_Article_Line_Type *l;
344 unsigned char *b;
345
346 if (a == NULL)
347 return;
348
349 if (0 == try_supercite (a->lines))
350 {
351 /* return; */
352 }
353
354 if (Slrn_Ignore_Quote_Regexp[0] == NULL) return;
355
356 /* skip header */
357 l = a->lines;
358 while ((l != NULL) && (l->flags == HEADER_LINE))
359 l = l->next;
360
361 while (l != NULL)
362 {
363 int level;
364 b = (unsigned char *) l->buf;
365
366 if (NULL == (b = is_matching_line (b, Slrn_Ignore_Quote_Regexp)))
367 {
368 l = l->next;
369 continue;
370 }
371 l->flags |= QUOTE_LINE;
372 level = 1;
373 while (NULL != (b = is_matching_line (b, Slrn_Ignore_Quote_Regexp)))
374 level++;
375
376 l->v.quote_level = level;
377 l = l->next;
378 }
379 a->is_modified = 1;
380 }
381
382 /*}}}*/
383
slrn_art_mark_signature(Slrn_Article_Type * a)384 void slrn_art_mark_signature (Slrn_Article_Type *a) /*{{{*/
385 {
386 Slrn_Article_Line_Type *l;
387
388 if (a == NULL)
389 return;
390
391 l = a->lines;
392 if (l == NULL) return;
393 /* go to end of article */
394 while (l->next != NULL) l = l->next;
395
396 /* skip back until a line matches the signature RegExp */
397
398 while ((l != NULL) && (0 == (l->flags & HEADER_LINE))
399 && ((l->flags & VERBATIM_LINE) ||
400 (NULL == is_matching_line ((unsigned char *) l->buf,
401 Slrn_Strip_Sig_Regexp))))
402 l = l->prev;
403
404 if ((l == NULL) || (l->flags & HEADER_LINE)) return;
405
406 while (l != NULL)
407 {
408 l->flags |= SIGNATURE_LINE;
409 l->flags &= ~(
410 QUOTE_LINE /* if in a signature, not a quote */
411 #if SLRN_HAS_SPOILERS
412 | SPOILER_LINE /* not a spoiler */
413 #endif
414 );
415 l = l->next;
416 }
417 }
418
419 /*}}}*/
420
421 /* The input line is assumed to be the first wrapped portion of a line. For
422 * example, if a series of lines denoted as A/B is wrapped: A0/A1/A2/B0/B1,
423 * then to unwrap A, A1 is passed and B0 is returned.
424 */
unwrap_line(Slrn_Article_Type * a,Slrn_Article_Line_Type * l)425 static Slrn_Article_Line_Type *unwrap_line (Slrn_Article_Type *a, /*{{{*/
426 Slrn_Article_Line_Type *l) /*{{{*/
427 {
428 char *b;
429 Slrn_Article_Line_Type *next, *ll;
430
431 a->needs_sync = 1;
432 a->is_modified = 1;
433
434 ll = l->prev;
435 b = ll->buf;
436 do
437 {
438 b += strlen (b);
439 /* skip the space at beginning of the wrapped line: */
440 strcpy (b, l->buf + 1); /* safe */
441 next = l->next;
442 slrn_free ((char *)l->buf);
443 slrn_free ((char *)l);
444 if (l == a->cline) a->cline = ll;
445 l = next;
446 }
447 while ((l != NULL) && (l->flags & WRAPPED_LINE));
448
449 ll->next = l;
450 if (l != NULL) l->prev = ll;
451 return l;
452 }
453
454 /*}}}*/
455
456 /*}}}*/
457
_slrn_art_unwrap_article(Slrn_Article_Type * a)458 int _slrn_art_unwrap_article (Slrn_Article_Type *a) /*{{{*/
459 {
460 Slrn_Article_Line_Type *l;
461
462 if (a == NULL)
463 return -1;
464
465 a->needs_sync = 1;
466 a->is_modified = 1;
467 l = a->lines;
468
469 while (l != NULL)
470 {
471 if (l->flags & WRAPPED_LINE)
472 l = unwrap_line (a, l); /* cannot fail */
473 else l = l->next;
474 }
475 a->is_wrapped = 0;
476 return 0;
477 }
478
479 /*}}}*/
480
_slrn_art_wrap_article(Slrn_Article_Type * a)481 int _slrn_art_wrap_article (Slrn_Article_Type *a) /*{{{*/
482 {
483 unsigned char *buf, ch;
484 Slrn_Article_Line_Type *l;
485 unsigned int wrap_mode = Slrn_Wrap_Mode;
486 int wrap_width = Slrn_Wrap_Width;
487
488 if ((wrap_width < 5) || (wrap_width > SLtt_Screen_Cols))
489 wrap_width = SLtt_Screen_Cols;
490
491 if (a == NULL)
492 return -1;
493
494 a->is_modified = 1;
495 a->needs_sync = 1;
496
497 if (-1 == _slrn_art_unwrap_article (a))
498 return -1;
499
500 l = a->lines;
501
502 #if SLANG_VERSION >= 20000
503 SLsmg_gotorc(0,0); /* used by Slsmg_strbytes later on */
504 #endif
505
506 while (l != NULL)
507 {
508 unsigned char header_char_delimiter = 0;
509
510 if (l->flags & HEADER_LINE)
511 {
512 if ((wrap_mode & 1) == 0)
513 {
514 l = l->next;
515 continue;
516 }
517
518 if (0 == slrn_case_strncmp ("Path: ", l->buf, 6))
519 header_char_delimiter = '!';
520 else if (0 == slrn_case_strncmp ( "Newsgroups: ", l->buf, 12))
521 header_char_delimiter = ',';
522 }
523 else if (l->flags & QUOTE_LINE)
524 {
525 if ((wrap_mode & 2) == 0)
526 {
527 l = l->next;
528 continue;
529 }
530 }
531
532 buf = (unsigned char *) l->buf;
533 ch = *buf;
534 while (ch != 0)
535 {
536 unsigned int bytes = SLsmg_strbytes (buf, buf+strlen((char *)buf),
537 (unsigned int) wrap_width);
538 buf += bytes;
539 while ((*buf == ' ') || (*buf == '\t'))
540 buf++;
541 if (*buf) /* we need to wrap */
542 {
543 Slrn_Article_Line_Type *new_l;
544 unsigned char *buf0, *lbuf;
545
546 /* Try to break the line on a word boundary.
547 * For now, I will only break on space characters.
548 */
549 buf0 = buf;
550 lbuf = (unsigned char *) l->buf;
551
552 lbuf += 1; /* avoid space at beg of line */
553
554 if (Slrn_Wrap_Method == 0 || Slrn_Wrap_Method == 2)
555 {
556 while (buf0 > lbuf)
557 {
558 if ((*buf0 == ' ') || (*buf0 == '\t')
559 || (header_char_delimiter
560 && (*buf0 == header_char_delimiter)))
561 {
562 buf = buf0;
563 break;
564 }
565 buf0--;
566 }
567
568 if (buf0 == lbuf)
569 {
570 if (Slrn_Wrap_Method == 0)
571 {
572 /* Could not find a place to break the line. Ok, so
573 * we will not break this. Perhaps it is a URL.
574 * If not, it is a long word and who cares about it.
575 */
576 while (((ch = *buf) != 0)
577 && (ch != ' ') && (ch != '\t'))
578 buf++;
579
580 if (ch == 0)
581 continue;
582 }
583 else {
584 lbuf = (unsigned char *) l->buf;
585 lbuf += 1; /* avoid space at beg of line */
586 }
587 }
588 }
589
590 /* Start wrapped lines with a space. To do this, I will
591 * _temporally_ modify the previous character for the purpose
592 * of creating the new space.
593 */
594 buf--;
595 ch = *buf;
596 *buf = ' ';
597
598 new_l = (Slrn_Article_Line_Type *) slrn_malloc (sizeof (Slrn_Article_Line_Type), 1, 1);
599 if (new_l == NULL)
600 return -1;
601
602 if (NULL == (new_l->buf = slrn_strmalloc ((char *)buf, 1)))
603 {
604 slrn_free ((char *) new_l);
605 return -1;
606 }
607
608 *buf++ = ch;
609 *buf = 0;
610
611 new_l->next = l->next;
612 new_l->prev = l;
613 l->next = new_l;
614 if (new_l->next != NULL) new_l->next->prev = new_l;
615
616 new_l->flags = l->flags | WRAPPED_LINE;
617
618 if (new_l->flags & QUOTE_LINE)
619 new_l->v.quote_level = l->v.quote_level;
620
621 l = new_l;
622 buf = (unsigned char *) new_l->buf;
623 a->is_wrapped = 1;
624 }
625 #if SLANG_VERSION < 20000
626 else buf++;
627 #endif
628
629 ch = *buf;
630 }
631 l = l->next;
632 }
633 return 0;
634 }
635
636 /*}}}*/
637
is_blank_line(unsigned char * b)638 static int is_blank_line (unsigned char *b) /*{{{*/
639 {
640 b = (unsigned char *) slrn_skip_whitespace ((char *) b);
641 return (*b == 0);
642 }
643
644 /*}}}*/
645
_slrn_art_skip_quoted_text(Slrn_Article_Type * a)646 void _slrn_art_skip_quoted_text (Slrn_Article_Type *a) /*{{{*/
647 {
648 Slrn_Article_Line_Type *l;
649
650 if (a == NULL)
651 return;
652
653 l = a->cline;
654 a->needs_sync = 1;
655
656 /* look for a quoted line */
657 while (l != NULL)
658 {
659 if ((l->flags & HIDDEN_LINE) == 0)
660 {
661 a->cline = l;
662 if (l->flags & QUOTE_LINE) break;
663 }
664 l = l->next;
665 }
666
667 /* Now we are either at the end of the buffer or on a quote line. Skip
668 * past other quote lines.
669 */
670
671 if (l == NULL)
672 return;
673
674 l = l->next;
675
676 while (l != NULL)
677 {
678 if (l->flags & HIDDEN_LINE)
679 {
680 l = l->next;
681 continue;
682 }
683 a->cline = l;
684 if ((l->flags & QUOTE_LINE) == 0)
685 {
686 /* Check to see if it is blank */
687 if (is_blank_line ((unsigned char *) l->buf) == 0) break;
688 }
689 l = l->next;
690 }
691 }
692
693 /*}}}*/
694
_slrn_art_skip_digest_forward(Slrn_Article_Type * a)695 int _slrn_art_skip_digest_forward (Slrn_Article_Type *a) /*{{{*/
696 {
697 Slrn_Article_Line_Type *l;
698 int num_passes;
699
700 if (a == NULL)
701 return -1;
702
703 /* We are looking for:
704 * <blank line> (actually, most digests do not have this-- even the FAQ that suggests it!!)
705 * ------------------------------
706 * <blank line>
707 * Subject: something
708 *
709 * In fact, most digests do not conform to this. So, I will look for:
710 * <blank line>
711 * Subject: something
712 *
713 * Actually, most faqs, etc... do not support this. So, look for any line
714 * beginning with a number on second pass. Sigh.
715 */
716 num_passes = 0;
717 while (num_passes < 2)
718 {
719 l = a->cline->next;
720
721 while (l != NULL)
722 {
723 char ch;
724 char *buf;
725
726 if ((l->flags & HIDDEN_LINE) || (l->flags & HEADER_LINE))
727 {
728 l = l->next;
729 continue;
730 }
731
732 buf = l->buf;
733 if (num_passes == 0)
734 {
735 if ((strncmp ("Subject:", buf, 8))
736 || (((ch = buf[8]) != ' ') && (ch != '\t')))
737 {
738 l = l->next;
739 continue;
740 }
741 }
742 else
743 {
744 ch = *buf;
745 if ((ch > '9') || (ch < '0'))
746 {
747 l = l->next;
748 continue;
749 }
750 }
751
752 a->cline = l;
753 a->needs_sync = 1;
754 return 0;
755 }
756 num_passes++;
757 }
758 return -1;
759 }
760
761 /*}}}*/
762
slrn_art_extract_header(char * hdr,unsigned int len)763 char *slrn_art_extract_header (char *hdr, unsigned int len) /*{{{*/
764 {
765 Slrn_Article_Line_Type *l;
766 Slrn_Article_Type *a = Slrn_Current_Article;
767
768 if (a == NULL)
769 return NULL;
770
771 l = a->lines;
772
773 while ((l != NULL)
774 && (*l->buf != 0))
775 {
776 if (0 == slrn_case_strncmp ( hdr,
777 l->buf, len))
778 {
779 char *result;
780
781 if ((l->next != NULL) && (l->next->flags & WRAPPED_LINE))
782 {
783 (void) unwrap_line (a, l->next);
784 slrn_art_sync_article (a);
785 }
786
787 /* Return the data after the colon */
788 result = slrn_strbyte (l->buf, ':');
789 if (result == NULL)
790 result = l->buf + len;
791 else result += 1;
792
793 return slrn_skip_whitespace (result);
794 }
795 l = l->next;
796 }
797 return NULL;
798 }
799
800 /*}}}*/
801
802 typedef struct _Visible_Header_Type /*{{{*/
803 {
804 char *header;
805 unsigned int len;
806 struct _Visible_Header_Type *next;
807 }
808
809 /*}}}*/
810 Visible_Header_Type;
811
812 static Visible_Header_Type *Visible_Headers;
813
814 char *Slrn_Visible_Headers_String; /* for interpreter */
815
free_visible_header_list(void)816 static void free_visible_header_list (void) /*{{{*/
817 {
818 while (Visible_Headers != NULL)
819 {
820 Visible_Header_Type *next;
821 next = Visible_Headers->next;
822 SLang_free_slstring (Visible_Headers->header); /* NULL ok */
823 SLfree ((char *) Visible_Headers);
824 Visible_Headers = next;
825 }
826 SLang_free_slstring (Slrn_Visible_Headers_String);
827 Slrn_Visible_Headers_String = NULL;
828 }
829
830 /*}}}*/
831
slrn_set_visible_headers(char * headers)832 int slrn_set_visible_headers (char *headers) /*{{{*/
833 {
834 char buf[256];
835 unsigned int nth;
836
837 free_visible_header_list ();
838
839 Slrn_Visible_Headers_String = SLang_create_slstring (headers);
840 if (Slrn_Visible_Headers_String == NULL)
841 return -1;
842
843 nth = 0;
844 while (-1 != SLextract_list_element (headers, nth, ',', buf, sizeof(buf)))
845 {
846 Visible_Header_Type *next;
847
848 next = (Visible_Header_Type *) SLmalloc (sizeof (Visible_Header_Type));
849 if (next == NULL)
850 return -1;
851 memset ((char *) next, 0, sizeof(Visible_Header_Type));
852 if (NULL == (next->header = SLang_create_slstring (buf)))
853 {
854 SLfree ((char *) next);
855 return -1;
856 }
857 next->len = strlen (buf);
858 next->next = Visible_Headers;
859 Visible_Headers = next;
860
861 nth++;
862 }
863 if ((Slrn_Current_Article != NULL) &&
864 (Slrn_Current_Article->headers_hidden))
865 _slrn_art_hide_headers (Slrn_Current_Article); /* commit the changes */
866 return 0;
867 }
868
869 /*}}}*/
870
_slrn_art_hide_headers(Slrn_Article_Type * a)871 void _slrn_art_hide_headers (Slrn_Article_Type *a) /*{{{*/
872 {
873 Slrn_Article_Line_Type *l;
874 char ch;
875
876 if (a == NULL)
877 return;
878
879 _slrn_art_unhide_headers (a);
880
881 a->needs_sync = 1;
882
883 l = a->lines;
884
885 while ((l != NULL) && (l->flags & HEADER_LINE))
886 {
887 int hide_header;
888 Visible_Header_Type *v;
889
890 ch = l->buf[0];
891 ch |= 0x20;
892
893 hide_header = 1;
894
895 v = Visible_Headers;
896 while (v != NULL)
897 {
898 int hide = (v->header[0] == '!') ? 1 : 0;
899 char chv = (0x20 | v->header[hide]);
900
901 if ((chv == ch)
902 && (0 == slrn_case_strncmp (l->buf,
903 v->header + hide,
904 (v->len) - hide)))
905 {
906 hide_header = hide;
907 break;
908 }
909
910 v = v->next;
911 }
912
913 do
914 {
915 if (hide_header)
916 l->flags |= HIDDEN_LINE;
917
918 l = l->next;
919 }
920 while ((l != NULL) && ((*l->buf == ' ') || (*l->buf == '\t')));
921 }
922 a->headers_hidden = 1;
923 }
924
925 /*}}}*/
926
_slrn_art_unhide_headers(Slrn_Article_Type * a)927 void _slrn_art_unhide_headers (Slrn_Article_Type *a) /*{{{*/
928 {
929 if (a == NULL)
930 return;
931
932 unhide_article_lines (a, HEADER_LINE);
933 a->headers_hidden = 0;
934 }
935
936 /*}}}*/
937
_slrn_art_unfold_header_lines(Slrn_Article_Type * a)938 int _slrn_art_unfold_header_lines (Slrn_Article_Type *a) /*{{{*/
939 {
940 Slrn_Article_Line_Type *l;
941 char ch;
942
943 if (a == NULL)
944 return -1;
945
946 l = a->lines->next;
947 a->is_modified = 1;
948 a->needs_sync = 1;
949
950 while ((l != NULL) && (l->flags & HEADER_LINE))
951 {
952 ch = l->buf[0];
953 if ((ch == ' ') || (ch == '\t'))
954 {
955 unsigned int len0, len1;
956 Slrn_Article_Line_Type *prev;
957 char *new_buf;
958
959 l->buf[0] = ' ';
960
961 prev = l->prev;
962
963 len0 = strlen (prev->buf);
964 len1 = len0 + strlen (l->buf) + 1;
965
966 new_buf = slrn_realloc (prev->buf, len1, 1);
967 if (new_buf == NULL)
968 return -1;
969
970 prev->buf = new_buf;
971
972 strcpy (new_buf + len0, l->buf); /* safe */
973 prev->next = l->next;
974 if (l->next != NULL) l->next->prev = prev;
975 slrn_free ((char *)l->buf);
976 slrn_free ((char *) l);
977 l = prev;
978 }
979
980 l = l->next;
981 }
982 return 0;
983 }
984
985 /*}}}*/
986
slrn_mark_header_lines(Slrn_Article_Type * a)987 void slrn_mark_header_lines (Slrn_Article_Type *a) /*{{{*/
988 {
989 Slrn_Article_Line_Type *l;
990
991 if (a == NULL)
992 return;
993 l = a->lines;
994
995 while ((l != NULL) && (l->buf[0] != 0))
996 {
997 l->flags = HEADER_LINE;
998 l = l->next;
999 }
1000 }
1001
1002 /*}}}*/
1003
_slrn_art_hide_signature(Slrn_Article_Type * a)1004 void _slrn_art_hide_signature (Slrn_Article_Type *a) /*{{{*/
1005 {
1006 if (a == NULL)
1007 return;
1008
1009 hide_article_lines (a, SIGNATURE_LINE);
1010 a->signature_hidden = 1;
1011 }
1012
1013 /*}}}*/
1014
_slrn_art_unhide_signature(Slrn_Article_Type * a)1015 void _slrn_art_unhide_signature (Slrn_Article_Type *a) /*{{{*/
1016 {
1017 if (a == NULL)
1018 return;
1019
1020 unhide_article_lines (a, SIGNATURE_LINE);
1021 a->signature_hidden = 0;
1022 }
1023
1024 /*}}}*/
1025
slrn_art_mark_pgp_signature(Slrn_Article_Type * a)1026 void slrn_art_mark_pgp_signature (Slrn_Article_Type *a) /*{{{*/
1027 {
1028 Slrn_Article_Line_Type *l;
1029
1030 if (a == NULL)
1031 return;
1032
1033 l = a->lines;
1034 while (l != NULL)
1035 {
1036 Slrn_Article_Line_Type *l0;
1037 int count;
1038
1039 if ((l->buf[0] == '-')
1040 && !strcmp (l->buf, "-----BEGIN PGP SIGNED MESSAGE-----"))
1041 {
1042 l->flags |= PGP_SIGNATURE_LINE;
1043 l->flags &= ~QUOTE_LINE;
1044
1045 if ((NULL != (l = l->next)) &&
1046 !strncmp (l->buf, "Hash: ", 6))
1047 {
1048 /* catch the `Hash: ... ' line */
1049 l->flags |= PGP_SIGNATURE_LINE;
1050 l->flags &= ~QUOTE_LINE;
1051
1052 l = l->next;
1053 }
1054 if ((NULL != l) &&
1055 !strncmp (l->buf, "NotDashEscaped: ", 16))
1056 {
1057 /* the optional "NotDashEscaped: ..." line */
1058 l->flags |= PGP_SIGNATURE_LINE;
1059 l->flags &= ~QUOTE_LINE;
1060
1061 l = l->next;
1062 }
1063
1064 continue;
1065 }
1066
1067 if ((l->buf[0] != '-')
1068 || strcmp (l->buf, "-----BEGIN PGP SIGNATURE-----"))
1069 {
1070 l = l->next;
1071 continue;
1072 }
1073 l0 = l;
1074 l = l->next;
1075
1076 count = 256; /* arbitrary */
1077 while ((l != NULL) && count)
1078 {
1079 if ((l->buf[0] != '-')
1080 || strcmp (l->buf, "-----END PGP SIGNATURE-----"))
1081 {
1082 count--;
1083 l = l->next;
1084 continue;
1085 }
1086
1087 l0->flags |= PGP_SIGNATURE_LINE;
1088 l0->flags &= ~(QUOTE_LINE|SIGNATURE_LINE);
1089 do
1090 {
1091 l0 = l0->next;
1092 l0->flags |= PGP_SIGNATURE_LINE;
1093 l0->flags &= ~(QUOTE_LINE|SIGNATURE_LINE);
1094 }
1095 while (l0 != l);
1096 return;
1097 }
1098 l = l0->next;
1099 }
1100 }
1101
1102 /*}}}*/
1103
_slrn_art_hide_pgp_signature(Slrn_Article_Type * a)1104 void _slrn_art_hide_pgp_signature (Slrn_Article_Type *a)
1105 {
1106 if (a == NULL)
1107 return;
1108
1109 hide_article_lines (a, PGP_SIGNATURE_LINE);
1110 a->pgp_signature_hidden = 1;
1111 }
1112
1113 /*}}}*/
1114
_slrn_art_unhide_pgp_signature(Slrn_Article_Type * a)1115 void _slrn_art_unhide_pgp_signature (Slrn_Article_Type *a) /*{{{*/
1116 {
1117 if (a == NULL)
1118 return;
1119
1120 unhide_article_lines (a, PGP_SIGNATURE_LINE);
1121 a->pgp_signature_hidden = 0;
1122 }
1123
1124 /*}}}*/
1125
slrn_art_mark_verbatim(Slrn_Article_Type * a)1126 void slrn_art_mark_verbatim (Slrn_Article_Type *a)
1127 {
1128 Slrn_Article_Line_Type *l;
1129 unsigned char chon, choff;
1130 unsigned int mask, next_mask;
1131 char *von, *voff;
1132
1133 if (a == NULL)
1134 return;
1135
1136 von = "#v+";
1137 voff = "#v-";
1138
1139 chon = von[0];
1140 choff = voff[0];
1141
1142 l = a->lines;
1143 next_mask = mask = 0;
1144
1145 while (l != NULL)
1146 {
1147 if (mask == 0)
1148 {
1149 if ((l->buf[0] == chon)
1150 && (0 == strcmp ((char *) l->buf, von)))
1151 {
1152 mask = VERBATIM_LINE|VERBATIM_MARK_LINE;
1153 next_mask = VERBATIM_LINE;
1154 }
1155 }
1156 else if ((l->buf[0] == choff)
1157 && (0 == strcmp ((char *) l->buf, voff)))
1158 {
1159 mask = VERBATIM_LINE|VERBATIM_MARK_LINE;
1160 next_mask = 0;
1161 }
1162
1163 if (mask)
1164 {
1165 l->flags &= ~(QUOTE_LINE|SIGNATURE_LINE|PGP_SIGNATURE_LINE);
1166 l->flags |= mask;
1167 mask = next_mask;
1168 }
1169
1170 l = l->next;
1171 }
1172 }
1173
_slrn_art_unhide_verbatim(Slrn_Article_Type * a)1174 void _slrn_art_unhide_verbatim (Slrn_Article_Type *a) /*{{{*/
1175 {
1176 if (a == NULL)
1177 return;
1178
1179 unhide_article_lines (a, VERBATIM_LINE);
1180 a->verbatim_hidden = 0;
1181 }
1182
1183 /*}}}*/
1184
_slrn_art_hide_verbatim(Slrn_Article_Type * a)1185 void _slrn_art_hide_verbatim (Slrn_Article_Type *a) /*{{{*/
1186 {
1187 if (a == NULL)
1188 return;
1189
1190 hide_article_lines (a, VERBATIM_LINE);
1191 a->verbatim_hidden = 1;
1192 }
1193
1194 /*}}}*/
1195