#include "args.h"

#include "config.h"
/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

#ifndef lint
static const char *rcsid = "$Header: /usr/local/src/mm/mm-0.94/mm/RCS/headers.c,v 1.2 2005/05/28 22:27:14 beebe Exp $";
#endif

/*
 * headers.c - header manipulation stuff
 *
 */

#include "mm.h"
#include "parse.h"

static char *findheader ARGS((const char *h, const char *s, int mallocp));

/*
 * compare two strings, ignoring case.  returns true if the strings match
 * or if s1 is a leading substring of s2 and that substring in s2 ends with
 * a ':'.  Note, s1 may or may not be colon terminated, so check both cases.
 */

int
#if HAVE_STDC
hdrcmp (register const char *s1, register const char *s2)
#else /* K&R style */
hdrcmp (s1, s2)
register const char *s1, *s2;
#endif /* HAVE_STDC */
{
    register char c1, c2;

    while (1) {
	c1 = *s1++;
	c2 = *s2++;
	if (c2 == '\0') return false;	/* header line shorter than name */
	if (c1 == '\0' && c2 == ':') return true; /* a match w/o ':' */
	if (c1 == ':' && c2 == ':') return true; /* a match w/ ':'  */
	if (((isascii (c1) && isupper (c1)) ? tolower (c1) : c1) !=
	    ((isascii (c2) && isupper (c2)) ? tolower (c2) : c2))
	    return false;
    }
}


/*
 * return a pointer to the first char following a (possibly folded) line.
 */
char *
#if HAVE_STDC
skipheader (register const char *s)
#else /* K&R style */
skipheader (s)
register const char *s;
#endif /* HAVE_STDC */
{
    for (;;) {
	while (*s && (*s != '\n'))
	    ++s;
	if (*s == '\0')
	    return (char*)s;
	/* s now points to a linefeed */
	++s;
	if (isblank (*s))
	    continue;
	return (char*)s;
    }
}

char *
#if HAVE_STDC
next_header(register char **s, int *next)
#else /* K&R style */
next_header(s,next)
register char **s;
int *next;
#endif /* HAVE_STDC */
{
    char *s1, *s2;
    int len;

    s1 = skipheader(*s);
    if (s1) {
	len = s1 - *s;
	s2 = (char*)malloc(len+1);
	bcopy(*s,s2,len);
	s2[len] = '\0';
	*s = s1;
    }
    else
	return(NULL);

    if (*s1 == '\n') {
	*next = false;
    }
    else
	*next = true;
    return(s2);
}

/*
 * findheader:
 * do the real work of htext and hfind
 * Don't call this routine, okay?  Just call one of those up there ^^^.
 */
static char *
#if HAVE_STDC
findheader(const char *h, const char *s, int mallocp)
            				/* should we malloc up the space? */
#else /* K&R style */
findheader(h, s, mallocp)
const char *h, *s;
int mallocp;				/* should we malloc up the space? */
#endif /* HAVE_STDC */
{
    int size;
    char *head;
    int len;

    while (1) {
	if (s == nil || *s == '\0' || *s == '\n')
	    return nil;
	if (hdrcmp (h, s)) {
	    if (!mallocp)
		return ((char*)s);	/* here it is! */
	    len = strlen(h);
	    if (h[len-1] == ':')
		s += len;
	    else
		s += len+1;
	    for (;;) {
		switch (*s) {		/* skip past whitespace, ':' etc */
		  case 0:
		    return nil;
		  case ' ':
		  case '\t':
		    ++s;
		    break;
		  case '\r':
		  case '\n':
		    ++s;
		    if (!isblank(*s))
		      return(nil);
		    break;
		  default:
		    /* s now points to the beginning of the header text,
		       so find the end and copy it */
		    size = (int) (skipheader (s) - s - 1); /* -1 for lf */
		    head = (char*)malloc (size + 1);
		    if (head == nil)
			return nil;
		    strncpy(head, s, size);
		    head[size] = '\0'; 	/* make sure it's null-terminated */
		    return head;
		}
	    }
	}
	else
	    s = skipheader (s);
    }
}

/*
 * htext:
 * Return the body of the first header "h" in string at "s".  The return
 * value is allocated with malloc and should be freed when no longer needed.
 */
char *
#if HAVE_STDC
htext(const char *h, const char *s)
#else /* K&R style */
htext(h, s)
const char *h, *s;
#endif /* HAVE_STDC */
{
    return (findheader (h, s, true));	/* malloc the text string */
}

/*
 * hfind:
 * Return a pointer to the first header "h" in message text "s".  The
 * return value is a pointer right into the message text.
 */
char *
#if HAVE_STDC
hfind(const char *h, const char *s)
#else /* K&R style */
hfind(h, s)
const char *h, *s;
#endif /* HAVE_STDC */
{
    return (findheader (h, s, false));	/* don't malloc it */
}

/*
 * strip out multiple spaces and folded lines in place.  any substring that
 * can be interpreted as a run of whitespace (tabs, spaces, or line-folding
 * sequences) is collapsed to a single space.
 */
char *
#if HAVE_STDC
stripspaces(register char *s)
#else /* K&R style */
stripspaces(s)
register char *s;
#endif /* HAVE_STDC */
{
    register char *cp = s, *p = s;

    while (*cp) {
	if (isascii(*cp) && isspace(*cp)) {
	    if (cp != s)
		*p++ = ' ';
	    while (isascii(*cp) && isspace(*++cp))
		;
	}
	else
	    *p++ = *cp++;
    }
    *p++ = '\0';
    return s;
}

/*
 * header_summary:
 * print out a little summary of this message, with the author and subject
 * assume we have 79 columns to print:
NFADK123) 21-Aug Melissa Metz    12345678901234567890123456789 (10000000 chars)
 */
#define BIGNUM 10000000			/* 8 characters worth */
#define BIGNAME "10M"			/* a short string to name that */

void
#if HAVE_STDC
header_summary (int n, FILE *fp, int longfmt)
#else /* K&R style */
header_summary (n, fp, longfmt)
int n, longfmt;
FILE *fp;
#endif /* HAVE_STDC */
{
    buffer line;
    message *m;
    char *cp = line;
    char *p;
    char *freeme;			/* somebody forgot to free this! */
    char *angle;			/* location of angle bracket */

    if (fp == NULL)			/* print to stdout if output */
	fp = stdout;			/* descriptor is NULL */

    m = &cf->msgs[n];

    if (!(m->flags & M_SEEN))
	if (m->flags & M_RECENT)
	    *cp++ = 'N';
	else
	    *cp++ = 'U';
    else
	if (m->flags & M_RECENT)
	    *cp++ = 'R';
	else
	    *cp++ = ' ';
    *cp++ = (m->flags & M_FLAGGED) ? 'F' : ' ';
    *cp++ = (m->flags & M_ANSWERED) ? 'A' : ' ';
    *cp++ = (m->flags & M_DELETED) ? 'D' : ' ';
    *cp++ = (m->keywords) ? 'K' : ' ';
    (void) sprintf (cp, "%3d) %s ", n, hdate (m->date));
    cp += strlen (cp);
    freeme = htext ("from", m->text);
    if (freeme) {
	char *b, *z;
	if ((angle = index(freeme, '<'), angle)) {
	    if (angle != freeme) /* some text before <foo>? */
		*angle = '\0';		/* print "foo" not "foo <foo>" */
	    sprintf (cp, "%-15.15s ", freeme); /* XXX should be smarter */
	}
	else if ((b = index(freeme, '(')) && (z = index(b + 1, ')'))) {
	    ++b;
	    *z = 0;
	    sprintf (cp, "%-15.15s ", b);
	}
	else
	    sprintf (cp, "%-15.15s ", freeme);

	safe_free (freeme);
    }
    else
	sprintf (cp, "%-15.15s ", "???");

    cp += strlen (cp);

    freeme = htext ("subject", m->text);
    if (longfmt) {
	/* use the rest of the line for the Subject: header */
	if (freeme)
	    sprintf (cp, "%.*s", (int)(cmcsb._cmcmx - (cp - line)), freeme);
	else
	    sprintf (cp, "(No subject)");
    }
    else {
	if (freeme) {
	    sprintf (cp, "%.*s", cmcsb._cmcmx - 1 - 50, freeme); /* XXX */
	    cp += strlen (cp);
	}
	if (m->size > BIGNUM)
	    sprintf (cp, " (>%s chars)", BIGNAME);
	else
	    sprintf (cp, " (%ld chars)", (long)(m->size));
	cp += strlen (cp);
    }
    safe_free (freeme);
    for (p = line; p && *p != '\0'; p++) /* convert newlines to spaces */
	if (*p == '\n')			/* to make sure things stay on one */
	    *p = ' ';			/* line.  could be done more */
    fprintf (fp, "%s\n", line);		/* elegantly... */
}

/*
 * search:
 * find string "s" in string "t", return pointer to where it starts, or nil
 */
char *
#if HAVE_STDC
search (const char *s, const char *t)
#else /* K&R style */
search (s, t)
const char *s, *t;
#endif /* HAVE_STDC */
{
    register char c = s[0];

    if (isupper (c))
	c = tolower (c);

    do {
	while (*t && (c != (isupper (*t) ? tolower (*t) : *t))) /* XXX ugh */
	    ++t;
	if (*t && !ustrncmp(s, t, (int)strlen(s)))
		return (char*)t;
    } while (*t++);
    return nil;
}

/*
 * is the string "s" in the "h" header of this message (m)?
 * returns a boolean
 */
int
#if HAVE_STDC
search_header (const char *h, const char *s, message *m)
#else /* K&R style */
search_header (h, s, m)
const char *h, *s;
message *m;
#endif /* HAVE_STDC */
{
    char *q;
    char *p = htext (h, m->text);
    if (p == nil)
	return ((int)nil);
    q = search (s, p);
    safe_free (p);
    return (q != (char*)NULL);
}

/*
 * is the string "s" anywhere (including the headers) in message m?
 * this too is a boolean
 */
int
#if HAVE_STDC
search_text (const char *s, message *m)
#else /* K&R style */
search_text (s, m)
const char *s;
message *m;
#endif /* HAVE_STDC */
{
    return (search (s, m->text) != (char*)NULL);
}
