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