/* -*- mode: C; mode: fold; -*- */
/*
 This file is part of SLRN.

 Copyright (c) 1994, 1999 John E. Davis <davis@space.mit.edu>
 Copyright (c) 2001, 2002 Thomas Schultz <tststs@gmx.de>

 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the Free
 Software Foundation; either version 2 of the License, or (at your option)
 any later version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along
 with this program; if not, write to the Free Software Foundation, Inc.,
 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "slrnfeat.h"

/*{{{ system include files */

#include <stdio.h>
#include <string.h>
#include <time.h>


#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif

#include <ctype.h>
#include <slang.h>
#include "jdmacros.h"

#include "slrn.h"
#include "group.h"
#include "util.h"
#include "server.h"
#include "art.h"
#include "misc.h"
#include "post.h"
/* #include "clientlib.h" */
#include "startup.h"
#include "hash.h" 
#include "score.h"
#include "menu.h"
#include "xover.h"
#include "chmap.h"
#include "print.h"

/*}}}*/

static int Art_Hide_Quote_Level = 1;
int Slrn_Wrap_Mode = 3;
int Slrn_Wrap_Method = 2;

static char *Super_Cite_Regexp = "^[^A-Za-z0-9]*\"\\([-_a-zA-Z/]+\\)\" == .+";

static void hide_article_lines (Slrn_Article_Type *a, unsigned int mask) /*{{{*/
{
   Slrn_Article_Line_Type *l;

   if (a == NULL)
     return;
   
   a->needs_sync = 1;
   l = a->lines;

   while (l != NULL)
     {
	if (l->flags & mask)
	  l->flags |= HIDDEN_LINE;

	l = l->next;
     }
}

/*}}}*/

static void unhide_article_lines (Slrn_Article_Type *a, unsigned int mask) /*{{{*/
{
   Slrn_Article_Line_Type *l;

   if (a == NULL)
     return;

   a->needs_sync = 1;
   l = a->lines;

   while (l != NULL)
     {
	if (l->flags & mask)
	  l->flags &= ~HIDDEN_LINE;

	l = l->next;
     }
}

/*}}}*/

/* The function that set the needs_sync flag article line number start with _ 
 */

int _slrn_art_unhide_quotes (Slrn_Article_Type *a) /*{{{*/
{
   if (a == NULL)
     return -1;

   unhide_article_lines (a, QUOTE_LINE);
   a->quotes_hidden = 0;
   return 0;
}

/*}}}*/

static unsigned char *is_matching_line (unsigned char *b, SLRegexp_Type **r) /*{{{*/
{
   unsigned int len;
   len = strlen ((char *) b);

   while (*r != NULL)
     {
	SLRegexp_Type *re;

	re = *r++;
	if ((re->min_length > len) 
	    || (b != SLang_regexp_match (b, len, re)))
	  continue;
	
	return b + re->end_matches[0];
     }
   return NULL;
}

/*}}}*/

int _slrn_art_hide_quotes (Slrn_Article_Type *a, int reset) /*{{{*/
{
   Slrn_Article_Line_Type *l, *last;
   
   if (a == NULL)
     return -1;

   _slrn_art_unhide_quotes (a);
   
   a->needs_sync = 1;

   if (!reset)
     Art_Hide_Quote_Level = Slrn_Quotes_Hidden_Mode;
   
   l = a->lines;
   last = NULL;

   while (l != NULL)
     {
	if (l->flags & QUOTE_LINE)
	  {
	     if (Art_Hide_Quote_Level > 1)
	       {
		  if (QUOTE_LEVEL(l->flags) >= (unsigned int) Art_Hide_Quote_Level - 1)
		    l->flags |= HIDDEN_LINE;
	       }
	     /* Show first line of quoted material; try to pick one that
	      * actually has text on it! */
	     else
	       {
		  if (last != NULL)
		    l->flags |= HIDDEN_LINE;
		  else
		    {
		       last = l;
		       if ((l->next != NULL) && (l->next->flags & QUOTE_LINE))
			 {
			    unsigned char *str, *line = (unsigned char*)l->buf;
			    while (NULL != (str = is_matching_line
					    (line, Slrn_Ignore_Quote_Regexp)))
			      line = str;
			    if (0 == *(slrn_skip_whitespace((char*)line)))
			      {
				 l->flags |= HIDDEN_LINE;
				 last = NULL;
			      }
			 }
		    }
	       }
	  }
	else last = NULL;
	l = l->next;
     }
   a->quotes_hidden = Art_Hide_Quote_Level;
   return 0;
}

/*}}}*/


#if SLRN_HAS_SPOILERS
void slrn_art_mark_spoilers (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   
   if ((Slrn_Spoiler_Char == 0) || (a == NULL))
     return;

   l = a->lines;
   
   while ((l != NULL) && (l->flags & HEADER_LINE))
     l = l->next; /* skip header */
   
   while ((l != NULL) && (NULL == strchr (l->buf, 12)))
     l = l->next; /* skip to first formfeed */
   
   while (l != NULL)
     {
	l->flags |= SPOILER_LINE;
	l = l->next;
     }
}

/*}}}*/
#endif

static int try_supercite (Slrn_Article_Line_Type *l) /*{{{*/
{
   Slrn_Article_Line_Type *last, *lsave;
   static unsigned char compiled_pattern_buf[256];
   static SLRegexp_Type re;
   unsigned char *b;
   int count;
   char name[32];
   unsigned int len;
   int ret;

   re.pat = (unsigned char *) Super_Cite_Regexp;
   re.buf = compiled_pattern_buf;
   re.case_sensitive = 1;
   re.buf_len = sizeof (compiled_pattern_buf);
   
   /* skip header --- I should look for Xnewsreader: gnus */
   while ((l != NULL) && (*l->buf != 0)) l = l->next;
   
   if ((*compiled_pattern_buf == 0) && SLang_regexp_compile (&re))
     return -1;
   
   /* look at the first 15 lines on first attempt.
    * After that, scan the whole buffer looking for more citations */
   count = 15;
   lsave = l;
   ret = -1;
   while (1)
     {
	while (count && (l != NULL))
	  {
	     if ((l->flags & QUOTE_LINE) == 0)
	       {
		  if (NULL != slrn_regexp_match (&re, l->buf))
		    {
		       l->flags |= QUOTE_LINE;
		       break;
		    }
	       }
	     l = l->next;
	     count--;
	  }
	
	if ((l == NULL) || (count == 0)) return ret;
	
	/* Now find out what is used for citing. */
	b = (unsigned char *) l->buf + re.beg_matches[1];
	len = re.end_matches[1];
	if (len > sizeof (name) - 2) return ret;
	
	ret = 0;
	strncpy (name, (char *) b, len); name[len] = 0;
	
	while (l != NULL)
	  {
	     unsigned char ch;
	     
	     b = (unsigned char *) l->buf;
	     last = l;
	     l = l->next;
	     if (last->flags & QUOTE_LINE) continue;
	     
	     b = (unsigned char *) slrn_skip_whitespace ((char *) b);

	     if (!strncmp ((char *) b, name, len)
		 && (((ch = b[len] | 0x20) < 'a')
		     || (ch > 'z')))
	       {
		  last->flags |= QUOTE_LINE;
		  
		  while (l != NULL)
		    {
		       b = (unsigned char *) slrn_skip_whitespace (l->buf);
		       if (strncmp ((char *) b, name, len)
			   || (((ch = b[len] | 0x20) >= 'a')
			       && (ch <= 'z')))
			 break;
		       l->flags |= QUOTE_LINE;
		       l = l->next;
		    }
	       }
	  }
	count = -1;
	l = lsave;
     }
}

/*}}}*/

void slrn_art_mark_quotes (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   unsigned char *b;
   
   if (a == NULL)
     return;

   if (0 == try_supercite (a->lines))
     {
	/* return; */
     }
   
   if (Slrn_Ignore_Quote_Regexp[0] == NULL) return;
   
   /* skip header */
   l = a->lines;
   while ((l != NULL) && (l->flags == HEADER_LINE))
     l = l->next;

   while (l != NULL)
     {
	int level;
	b = (unsigned char *) l->buf;

	if (NULL == (b = is_matching_line (b, Slrn_Ignore_Quote_Regexp)))
	  {
	     l = l->next;
	     continue;
	  }
	l->flags |= QUOTE_LINE;
	level = 1;
	while ((level < MAX_QUOTE_LEVELS) &&
	       (NULL != (b = is_matching_line (b, Slrn_Ignore_Quote_Regexp))))
	  level++;

	l->flags |= ((level-1) << QUOTE_LEVEL_SHIFT);
	l = l->next;
     }
   a->is_modified = 1;
}

/*}}}*/

void slrn_art_mark_signature (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   
   if (a == NULL)
     return;
   
   l = a->lines;
   if (l == NULL) return;
   /* go to end of article */
   while (l->next != NULL) l = l->next;

   /* skip back until a line matches the signature RegExp */
   
   while ((l != NULL) && (0 == (l->flags & HEADER_LINE))
	  && (NULL == is_matching_line ((unsigned char *) l->buf,
					Slrn_Strip_Sig_Regexp)))
     l = l->prev;
   
   if ((l == NULL) || (l->flags & HEADER_LINE)) return;
   
   while (l != NULL)
     {
        l->flags |= SIGNATURE_LINE;
        l->flags &= ~(
		      QUOTE_LINE  /* if in a signature, not a quote */
#if SLRN_HAS_SPOILERS
		      | SPOILER_LINE     /* not a spoiler */
#endif
		      );
	l = l->next;
     }
}

/*}}}*/

/* The input line is assumed to be the first wrapped portion of a line.  For
 * example, if a series of lines denoted as A/B is wrapped: A0/A1/A2/B0/B1,
 * then to unwrap A, A1 is passed and B0 is returned.
 */
static Slrn_Article_Line_Type *unwrap_line (Slrn_Article_Type *a, /*{{{*/
					    Slrn_Article_Line_Type *l) /*{{{*/
{
   char *b;
   Slrn_Article_Line_Type *next, *ll;

   a->needs_sync = 1;
   a->is_modified = 1;

   ll = l->prev;
   b = ll->buf;
   do 
     {
	b += strlen (b);
	/* skip the space at beginning of the wrapped line: */
	strcpy (b, l->buf + 1); /* safe */
	next = l->next;
	slrn_free ((char *)l->buf);
	slrn_free ((char *)l);
	if (l == a->cline) a->cline = ll;
	l = next;
     }
   while ((l != NULL) && (l->flags & WRAPPED_LINE));
	     
   ll->next = l;
   if (l != NULL) l->prev = ll;
   return l;
}

/*}}}*/

/*}}}*/

int _slrn_art_unwrap_article (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   
   if (a == NULL)
     return -1;

   a->needs_sync = 1;
   a->is_modified = 1;
   l = a->lines;

   while (l != NULL)
     {
	if (l->flags & WRAPPED_LINE)
	  l = unwrap_line (a, l);      /* cannot fail */
	else l = l->next;
     }
   a->is_wrapped = 0;
   return 0;
}

/*}}}*/

int _slrn_art_wrap_article (Slrn_Article_Type *a) /*{{{*/
{
   unsigned int len;
   unsigned char *buf, ch;
   Slrn_Article_Line_Type *l;
   unsigned int wrap_mode = Slrn_Wrap_Mode;

   if (a == NULL)
     return -1;

   a->is_modified = 1;
   a->needs_sync = 1;

   if (-1 == _slrn_art_unwrap_article (a))
     return -1;

   l = a->lines;

   while (l != NULL)
     {
	unsigned char header_char_delimiter = 0;

	if (l->flags & HEADER_LINE) 
	  {
	     if ((wrap_mode & 1) == 0)
	       {
		  l = l->next;
		  continue;
	       }

	     if (0 == slrn_case_strncmp ((unsigned char *)"Path: ",
					 (unsigned char *)l->buf, 6))
	       header_char_delimiter = '!';
	     else if (0 == slrn_case_strncmp ((unsigned char *) "Newsgroups: ",
					      (unsigned char *)l->buf, 12))
	       header_char_delimiter = ',';
	  }
	else if (l->flags & QUOTE_LINE)
	  {
	     if ((wrap_mode & 2) == 0)
	       {
		  l = l->next;
		  continue;
	       }
	  }

	len = 0;
	buf = (unsigned char *) l->buf;
	ch = *buf;
	while (ch != 0)
	  {
	     if ((ch == '\t') && (SLsmg_Tab_Width > 0))
	       {
		  len += SLsmg_Tab_Width;
		  len -= len % SLsmg_Tab_Width;
	       }
	     else if (((ch >= ' ') && (ch < 127))
		      || (ch >= (unsigned char) SLsmg_Display_Eight_Bit))
	       len++;
	     else
	       {
		  len += 2;
		  if (ch & 0x80) len++;
	       }

	     if (len > (unsigned int) SLtt_Screen_Cols)
	       {
		  Slrn_Article_Line_Type *new_l;
		  unsigned char *buf0, *lbuf;

		  /* Try to break the line on a word boundary.  
		   * For now, I will only break on space characters.
		   */
		  buf0 = buf;
		  lbuf = (unsigned char *) l->buf;

		  lbuf += 1;	       /* avoid space at beg of line */
		  
		  if (Slrn_Wrap_Method == 0 || Slrn_Wrap_Method == 2)
		    {
		       while (buf0 > lbuf)
			 {
			    if ((*buf0 == ' ') || (*buf0 == '\t')
				|| (header_char_delimiter 
				    && (*buf0 == header_char_delimiter)))
			      {
				 buf = buf0;
				 break;
			      }
			    buf0--;
			 }
		       
		       if (buf0 == lbuf)
			 {
			    if (Slrn_Wrap_Method == 0)
			      {
		       /* Could not find a place to break the line.  Ok, so
			* we will not break this.  Perhaps it is a URL.  
			* If not, it is a long word and who cares about it.
			*/
				 while (((ch = *buf) != 0)
					&& (ch != ' ') && (ch != '\t'))
				   buf++;
				 
				 if (ch == 0)
				   continue;
			      }
			    else {
			       lbuf = (unsigned char *) l->buf;
			       lbuf += 1; /* avoid space at beg of line */
			    }
			 }
		    }
		  
		  /* Start wrapped lines with a space.  To do this, I will
		   * _temporally_ modify the previous character for the purpose
		   * of creating the new space.
		   */
		  buf--;
		  ch = *buf;
		  *buf = ' ';
		  
		  new_l = (Slrn_Article_Line_Type *) slrn_malloc (sizeof (Slrn_Article_Line_Type), 1, 1);
		  if (new_l == NULL)
		    return -1;
		  
		  if (NULL == (new_l->buf = slrn_strmalloc ((char *)buf, 1)))
		    {
		       slrn_free ((char *) new_l);
		       return -1;
		    }

		  *buf++ = ch;
		  *buf = 0;
		  
		  new_l->next = l->next;
		  new_l->prev = l;
		  l->next = new_l;
		  if (new_l->next != NULL) new_l->next->prev = new_l;

		  new_l->flags = l->flags | WRAPPED_LINE;
		  l = new_l;
		  buf = (unsigned char *) new_l->buf;
		  len = 0;
		  a->is_wrapped = 1;
	       }
	     else buf++;
	     
	     ch = *buf;
	  }
	l = l->next;
     }
   return 0;
}

/*}}}*/


static int is_blank_line (unsigned char *b) /*{{{*/
{
   b = (unsigned char *) slrn_skip_whitespace ((char *) b);
   return (*b == 0);
}

/*}}}*/

void _slrn_art_skip_quoted_text (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;

   if (a == NULL)
     return;

   l = a->cline;
   a->needs_sync = 1;

   /* look for a quoted line */
   while (l != NULL)
     {
	if ((l->flags & HIDDEN_LINE) == 0)
	  {
	     a->cline = l;
	     if (l->flags & QUOTE_LINE) break;
	  }
	l = l->next;
     }
   
   /* Now we are either at the end of the buffer or on a quote line. Skip
    * past other quote lines.
    */
   
   if (l == NULL)
     return;
   
   l = l->next;
   
   while (l != NULL)
     {
	if (l->flags & HIDDEN_LINE)
	  {
	     l = l->next;
	     continue;
	  }
	a->cline = l;
	if ((l->flags & QUOTE_LINE) == 0)
	  {
	     /* Check to see if it is blank */
	     if (is_blank_line ((unsigned char *) l->buf) == 0) break;
	  }
	l = l->next;
     }
}

/*}}}*/

int _slrn_art_skip_digest_forward (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   int num_passes;

   if (a == NULL)
     return -1;

   /* We are looking for:
    * <blank line>  (actually, most digests do not have this-- even the FAQ that suggests it!!)
    * ------------------------------
    * <blank line>
    * Subject: something
    *
    * In fact, most digests do not conform to this.  So, I will look for:
    * <blank line>
    * Subject: something
    *
    * Actually, most faqs, etc... do not support this.  So, look for any line
    * beginning with a number on second pass.  Sigh.
    */
   num_passes = 0;
   while (num_passes < 2)
     {
	l = a->cline->next;

	while (l != NULL)
	  {
	     char ch;
	     char *buf;
	     
	     if ((l->flags & HIDDEN_LINE) || (l->flags & HEADER_LINE))
	       {
		  l = l->next;
		  continue;
	       }
	     
	     buf = l->buf;
	     if (num_passes == 0)
	       {
		  if ((strncmp ("Subject:", buf, 8))
		      || (((ch = buf[8]) != ' ') && (ch != '\t')))
		    {
		       l = l->next;
		       continue;
		    }
	       }
	     else
	       {
		  ch = *buf;
		  if ((ch > '9') || (ch < '0'))
		    {
		       l = l->next;
		       continue;
		    }
	       }
	     
	     a->cline = l;
	     a->needs_sync = 1;
	     return 0;
	  }
	num_passes++;
     }
   return -1;
}

/*}}}*/

char *slrn_art_extract_header (char *hdr, unsigned int len) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   Slrn_Article_Type *a = Slrn_Current_Article;

   if (a == NULL)
     return NULL;
   
   l = a->lines;

   while ((l != NULL)
	  && (*l->buf != 0))
     {
	if (0 == slrn_case_strncmp ((unsigned char *) hdr,
				    (unsigned char *) l->buf, len))
	  {
	     char *result;

	     if ((l->next != NULL) && (l->next->flags & WRAPPED_LINE))
	       {
		  (void) unwrap_line (a, l->next);
		  slrn_art_sync_article (a);
	       }

	     /* Return the data after the colon */
	     result = slrn_strchr (l->buf, ':');
	     if (result == NULL) 
	       result = l->buf + len;
	     else result += 1;

	     return slrn_skip_whitespace (result);
	  }
	l = l->next;
     }
   return NULL;
}

/*}}}*/

typedef struct _Visible_Header_Type /*{{{*/
{
   char *header;
   unsigned int len;
   struct _Visible_Header_Type *next;
}

/*}}}*/
Visible_Header_Type;

static Visible_Header_Type *Visible_Headers;

#if SLRN_HAS_SLANG
char *Slrn_Visible_Headers_String;     /* for interpreter */
#endif

static void free_visible_header_list (void) /*{{{*/
{
   while (Visible_Headers != NULL)
     {
	Visible_Header_Type *next;
	next = Visible_Headers->next;
	SLang_free_slstring (Visible_Headers->header);   /* NULL ok */
	SLfree ((char *) Visible_Headers);
	Visible_Headers = next;
     }
#if SLRN_HAS_SLANG
   SLang_free_slstring (Slrn_Visible_Headers_String);
#endif
}

/*}}}*/


int slrn_set_visible_headers (char *headers) /*{{{*/
{
   char buf[256];
   unsigned int nth;

   free_visible_header_list ();
   
#if SLRN_HAS_SLANG
   Slrn_Visible_Headers_String = SLang_create_slstring (headers);
   if (Slrn_Visible_Headers_String == NULL)
     return -1;
#endif

   nth = 0;
   while (-1 != SLextract_list_element (headers, nth, ',', buf, sizeof(buf)))
     {
	Visible_Header_Type *next;
	
	next = (Visible_Header_Type *) SLmalloc (sizeof (Visible_Header_Type));
	if (next == NULL)
	  return -1;
	memset ((char *) next, 0, sizeof(Visible_Header_Type));
	if (NULL == (next->header = SLang_create_slstring (buf)))
	  {
	     SLfree ((char *) next);
	     return -1;
	  }
	next->len = strlen (buf);
	next->next = Visible_Headers;
	Visible_Headers = next;
	
	nth++;
     }
   if ((Slrn_Current_Article != NULL) &&
       (Slrn_Current_Article->headers_hidden))
     _slrn_art_hide_headers (Slrn_Current_Article); /* commit the changes */
   return 0;
}

/*}}}*/

void _slrn_art_hide_headers (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   char ch;
   
   if (a == NULL)
     return;

   _slrn_art_unhide_headers (a);

   a->needs_sync = 1;
   
   l = a->lines;

   while ((l != NULL) && (l->flags & HEADER_LINE))
     {
	int hide_header;
	Visible_Header_Type *v;
	
	ch = l->buf[0];
	ch |= 0x20;

	hide_header = 1;

	v = Visible_Headers;
	while (v != NULL)
	  {
	     int hide = (v->header[0] == '!') ? 1 : 0;
	     char chv = (0x20 | v->header[hide]);
	     
	     if ((chv == ch)
		 && (0 == slrn_case_strncmp ((unsigned char *)l->buf,
					     (unsigned char *)v->header + hide,
					     (v->len) - hide)))
	       {
		  hide_header = hide;
		  break;
	       }
	     
	     v = v->next;
	  }

	do
	  {
	     if (hide_header)
	       l->flags |= HIDDEN_LINE;
	     
	     l = l->next;
	  }
	while ((l != NULL) && ((*l->buf == ' ') || (*l->buf == '\t')));
     }
   a->headers_hidden = 1;
}

/*}}}*/

void _slrn_art_unhide_headers (Slrn_Article_Type *a) /*{{{*/
{
   if (a == NULL)
     return;

   unhide_article_lines (a, HEADER_LINE);
   a->headers_hidden = 0;
}

/*}}}*/

int _slrn_art_unfold_header_lines (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   char ch;
   
   if (a == NULL)
     return -1;
   
   l = a->lines->next;
   a->is_modified = 1;
   a->needs_sync = 1;

   while ((l != NULL) && (l->flags & HEADER_LINE))
     {
	ch = l->buf[0];
	if ((ch == ' ') || (ch == '\t'))
	  {
	     unsigned int len0, len1;
	     Slrn_Article_Line_Type *prev;
	     char *new_buf;
	     
	     l->buf[0] = ' ';
	     
	     prev = l->prev;
	     
	     len0 = strlen (prev->buf);
	     len1 = len0 + strlen (l->buf) + 1;
	     
	     new_buf = slrn_realloc (prev->buf, len1, 1);
	     if (new_buf == NULL)
	       return -1;

	     prev->buf = new_buf;
	     
	     strcpy (new_buf + len0, l->buf); /* safe */
	     prev->next = l->next;
	     if (l->next != NULL) l->next->prev = prev;
	     slrn_free ((char *)l->buf);
	     slrn_free ((char *) l);
	     l = prev;
	  }
	
	l = l->next;
     }
   return 0;
}

/*}}}*/

void slrn_mark_header_lines (Slrn_Article_Type *a) /*{{{*/
{
   Slrn_Article_Line_Type *l;
   
   if (a == NULL)
     return;
   l = a->lines;

   while ((l != NULL) && (l->buf[0] != 0))
     {
	l->flags = HEADER_LINE;
	l = l->next;
     }
}

/*}}}*/

void _slrn_art_hide_signature (Slrn_Article_Type *a) /*{{{*/
{
   if (a == NULL)
     return;

   hide_article_lines (a, SIGNATURE_LINE);
   a->signature_hidden = 1;
}

/*}}}*/

void _slrn_art_unhide_signature (Slrn_Article_Type *a) /*{{{*/
{
   if (a == NULL)
     return;

   unhide_article_lines (a, SIGNATURE_LINE);
   a->signature_hidden = 0;
}

/*}}}*/

void slrn_art_mark_pgp_signature (Slrn_Article_Type *a) /*{{{*/
{	
   Slrn_Article_Line_Type *l;

   if (a == NULL)
     return;
   
   l = a->lines;
   while (l != NULL)
     {
	Slrn_Article_Line_Type *l0;
	int count;

	if ((l->buf[0] == '-')
	    && !strcmp (l->buf, "-----BEGIN PGP SIGNED MESSAGE-----"))
	  {
	     l->flags |= PGP_SIGNATURE_LINE;
	     l->flags &= ~QUOTE_LINE;
	     
	     if ((NULL != (l = l->next)) &&
		 (*l->buf != 0))
	       {
		  /* catch the `Hash: ... ' line */
		  l->flags |= PGP_SIGNATURE_LINE;
		  l->flags &= ~QUOTE_LINE;
		  
		  l = l->next;
	       }
	     continue;
	  }

	if ((l->buf[0] != '-')
	    || strcmp (l->buf, "-----BEGIN PGP SIGNATURE-----"))
	  {
	     l = l->next;
	     continue;
	  }
	l0 = l;
	l = l->next;
	
	count = 20;
	while ((l != NULL) && count)
	  {
	     if ((l->buf[0] != '-')
		 || strcmp (l->buf, "-----END PGP SIGNATURE-----"))
	       {
		  count--;
		  l = l->next;
		  continue;
	       }
	     
	     l0->flags |= PGP_SIGNATURE_LINE;
	     l0->flags &= ~QUOTE_LINE;
	     do
	       {
		  l0 = l0->next;
		  l0->flags |= PGP_SIGNATURE_LINE;
		  l0->flags &= ~QUOTE_LINE;
	       }
	     while (l0 != l);
	     return;
	  }
	l = l0->next;
     }
}

/*}}}*/

void _slrn_art_hide_pgp_signature (Slrn_Article_Type *a)
{
   if (a == NULL)
     return;

   hide_article_lines (a, PGP_SIGNATURE_LINE);
   a->pgp_signature_hidden = 1;
}

/*}}}*/

void _slrn_art_unhide_pgp_signature (Slrn_Article_Type *a) /*{{{*/
{
   if (a == NULL)
     return;

   unhide_article_lines (a, PGP_SIGNATURE_LINE);
   a->pgp_signature_hidden = 0;
}

/*}}}*/

void slrn_art_mark_verbatim (Slrn_Article_Type *a)
{
   Slrn_Article_Line_Type *l;
   unsigned char chon, choff;
   unsigned int mask, next_mask;
   char *von, *voff;

   if (a == NULL)
     return;
   
   von = "#v+";
   voff = "#v-";

   chon = von[0];
   choff = voff[0];

   l = a->lines;
   next_mask = mask = 0;

   while (l != NULL)
     {
	if (mask == 0)
	  {
	     if ((l->buf[0] == chon)
		 && (0 == strcmp ((char *) l->buf, von)))
	       {
		  mask = VERBATIM_LINE|VERBATIM_MARK_LINE;
		  next_mask = VERBATIM_LINE;
	       }
	  }
	else if ((l->buf[0] == choff)
		 && (0 == strcmp ((char *) l->buf, voff)))
	  {
	     mask = VERBATIM_LINE|VERBATIM_MARK_LINE;
	     next_mask = 0;
	  }

	if (mask)
	  {
	     l->flags &= ~(QUOTE_LINE|SIGNATURE_LINE|PGP_SIGNATURE_LINE);
	     l->flags |= mask;
	     mask = next_mask;
	  }

	l = l->next;
     }
}


void _slrn_art_unhide_verbatim (Slrn_Article_Type *a) /*{{{*/
{
   if (a == NULL)
     return;

   unhide_article_lines (a, VERBATIM_LINE);
   a->verbatim_hidden = 0;
}

/*}}}*/

void _slrn_art_hide_verbatim (Slrn_Article_Type *a) /*{{{*/
{
   if (a == NULL)
     return;

   hide_article_lines (a, VERBATIM_LINE);
   a->verbatim_hidden = 1;
}

/*}}}*/
