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