/*
 * extract/output headers
 */

#include "defs.h"
#include <sys/timeb.h>

extern char systemid[];
extern long now;

char hform[]		 = "%s: %s\n";

/* Mandatory Headers */
char t_relayversion[]	 = "Relay-Version";
char t_postversion[]	 = "Posting-Version";
char t_from[]		 = "From";
char t_date[]		 = "Date";
char t_newsgroups[]	 = "Newsgroups";
char t_subject[]	 = "Subject";
char t_messageid[]	 = "Message-ID";
char t_path[]		 = "Path";

/* Optional Headers */
char t_replyto[]	 = "Reply-To";
char t_sender[]		 = "Sender";
char t_followupto[]	 = "Followup-To";
char t_datereceived[]	 = "Date-Received";
char t_expires[]	 = "Expires";
char t_references[]	 = "References";
char t_control[]	 = "Control";
char t_distribution[]	 = "Distribution";
char t_organization[]	 = "Organization";
char t_lines[]		 = "Lines";

typedef enum ft
{
	f_control, f_date, f_datereceived, f_distribution,
	f_expires, f_followupto, f_from, f_lines, f_messageid,
	f_newsgroups, f_organization, f_path, f_postversion,
	f_references, f_relayversion, f_replyto, f_sender,
	f_subject
}


ftype;

typedef struct field {
	char *f_name;
	ftype	f_type;
} field;

static field fields[] = 
{
	{ t_control, 	f_control	 },
	{ t_date, 		f_date		 },
	{ t_datereceived, 	f_datereceived	 },
	{ t_distribution, 	f_distribution	 },
	{ t_expires, 	f_expires	 },
	{ t_followupto, 	f_followupto	 },
	{ t_from, 		f_from		 },
	{ t_lines, 	f_lines		 },
	{ t_messageid, 	f_messageid	 },
	{ t_newsgroups, 	f_newsgroups	 },
	{ t_organization, 	f_organization	 },
	{ t_path, 		f_path		 },
	{ t_postversion, 	f_postversion	 },
	{ t_references, 	f_references	 },
	{ t_relayversion, 	f_relayversion	 },
	{ t_replyto, 	f_replyto	 },
	{ t_sender, 	f_sender	 },
	{ t_subject, 	f_subject	 }
};


char *weekdays[7] = 
{
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};


char *months[12] = 
{
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};


static
fieldcmp(a, b)
field *a, *b;
{
	return CMP(a->f_name, b->f_name);
}


/*
 * Binary search algorithm, generalized from Knuth (6.2.1) Algorithm B.
 *
 * Written by J. S. Rugaber; rewritten by L. Rosler, Dept. 45175, August, 1981.
 */
char *
search(key, base, nel, width, compar)
char *key;			/* Key to be located */
char *base;			/* Beginning of table */
unsigned nel;			/* Number of elements in the table */
unsigned width;			/* Width of an element (bytes) */
int	(*compar)();		/* Comparison function */
{
	int two_width = width + width;
	char *last = base + width * (nel - 1); /* Last element in table */

	while (last >= base) {

		register char *p = base + width * ((last - base)/two_width);
		register int res = (*compar)(key, p);

		if (res == 0)
			return (p);	/* Key found */
		if (res < 0)
			last = p - width;
		else
			base = p + width;
	}
	return ((char *) 0);		/* Key not found */
}


/*
 * extract headers from file,
 * position file to start of body
 */
gethead(f, hp)
FILE *f;
header *hp;
{
	register char *colon, *space, *s;
	register field	*fp;
	field		af;
	char buf[BUFLEN*2];
	char *hfgets();

	memset((char *) hp, 0, sizeof(header));
	while (hfgets(buf, sizeof(buf), f)) {
		if (buf[0] == '\n')
			return;
		if (isupper(buf[0]) && (colon = strchr(buf, ':')) &&
		    (space = strchr(buf, ' ')) && (colon + 1 == space)) {
			*colon = '\0';
			af.f_name = buf;
			fp = (field * )search((char *)&af, (char *)fields,
				sizeof fields/sizeof fields[0], sizeof fields[0],
				fieldcmp);
			*colon = ':';
		} else
			fp = NULL;
		if (!fp)
			if (hp->h_others)
				hp->h_others = catstr(hp->h_others, buf);
			else
				hp->h_others = newstr(buf);
		else {
			if (colon = strchr(space + 1, '\n'))
				*colon = '\0';
			s = newstr(space + 1);
			switch (fp->f_type) {
			case f_control:		
				hp->h_control = s;	
				break;
			case f_date:		
				hp->h_date = s;		
				break;
			case f_datereceived:	
				hp->h_datereceived = s;	
				break;
			case f_distribution:	
				hp->h_distribution = s;	
				break;
			case f_expires:		
				hp->h_expires = s;	
				break;
			case f_followupto:	
				hp->h_followupto = s;	
				break;
			case f_from:		
				hp->h_from = s;		
				break;
			case f_lines:		
				hp->h_lines = s;	
				break;
			case f_messageid:	
				hp->h_messageid = s;	
				break;
			case f_newsgroups:	
				hp->h_newsgroups = s;	
				break;
			case f_organization:	
				hp->h_organisation = s;	
				break;
			case f_path:		
				hp->h_path = s;		
				break;
			case f_postversion:	
				hp->h_postversion = s;	
				break;
			case f_references:	
				hp->h_references = s;	
				break;
			case f_relayversion:	
				hp->h_relayversion = s;	
				break;
			case f_replyto:		
				hp->h_replyto = s;	
				break;
			case f_sender:		
				hp->h_sender = s;	
				break;
			case f_subject:		
				hp->h_subject = s;	
				break;
			}
		}
	}
}


/*
 * put headers to file
 */
puthead(hp, f, com)
header *hp;
FILE *f;
pheadcom com;			/* actually ignored now */
{
	register char *s;
	char *getunique();
	extern char *getenv();

	if (hp->h_relayversion)
		(void) fprintf(f, hform, t_relayversion, hp->h_relayversion);

	if (hp->h_postversion)
		(void) fprintf(f, hform, t_postversion, hp->h_postversion);

	if (hp->h_from)
		(void) fprintf(f, hform, t_from, hp->h_from);

	if (hp->h_date)
		(void) fprintf(f, hform, t_date, hp->h_date);

	if (hp->h_newsgroups)
		(void) fprintf(f, hform, t_newsgroups, hp->h_newsgroups);

	if (hp->h_subject)
		(void) fprintf(f, hform, t_subject, hp->h_subject);

	if (hp->h_messageid)
		(void) fprintf(f, hform, t_messageid, hp->h_messageid);

	if (hp->h_path)
		(void) fprintf(f, hform, t_path, hp->h_path);

	/* optional */

	if (hp->h_replyto)
		(void) fprintf(f, hform, t_replyto, hp->h_replyto);

	if (hp->h_sender)
		(void) fprintf(f, hform, t_sender, hp->h_sender);

	if (hp->h_followupto)
		(void) fprintf(f, hform, t_followupto, hp->h_followupto);

	if (hp->h_datereceived)
		(void) fprintf(f, hform, t_datereceived, hp->h_datereceived);

	if (hp->h_expires)
		(void) fprintf(f, hform, t_expires, hp->h_expires);

	if (hp->h_references)
		(void) fprintf(f, hform, t_references, hp->h_references);

	if (hp->h_control)
		(void) fprintf(f, hform, t_control, hp->h_control);

	if (hp->h_distribution)
		(void) fprintf(f, hform, t_distribution, hp->h_distribution);

	if (hp->h_organisation)
		(void) fprintf(f, hform, t_organization, hp->h_organisation);

	if (hp->h_lines)
		(void) fprintf(f, hform, t_lines, hp->h_lines);

	if (hp->h_others)
		fputs(hp->h_others, f);
}


/*
 * free all strings allocated to header
 */
freehead(hp)
register header *hp;
{
	if (hp->h_relayversion)	
		free(hp->h_relayversion);
	if (hp->h_postversion)	
		free(hp->h_postversion);
	if (hp->h_from)		
		free(hp->h_from);
	if (hp->h_date)		
		free(hp->h_date);
	if (hp->h_newsgroups)	
		free(hp->h_newsgroups);
	if (hp->h_subject)	
		free(hp->h_subject);
	if (hp->h_messageid)	
		free(hp->h_messageid);
	if (hp->h_path)		
		free(hp->h_path);
	if (hp->h_replyto)	
		free(hp->h_replyto);
	if (hp->h_sender)	
		free(hp->h_sender);
	if (hp->h_followupto)	
		free(hp->h_followupto);
	if (hp->h_datereceived)	
		free(hp->h_datereceived);
	if (hp->h_expires)	
		free(hp->h_expires);
	if (hp->h_references)	
		free(hp->h_references);
	if (hp->h_control)	
		free(hp->h_control);
	if (hp->h_distribution)	
		free(hp->h_distribution);
	if (hp->h_organisation)	
		free(hp->h_organisation);
	if (hp->h_lines)		
		free(hp->h_lines);
	if (hp->h_others)	
		free(hp->h_others);
}


/*
 * hfgets is like fgets, but deals with continuation lines.
 * It also ensures that even if a line that is too long is
 * received, the remainder of the line is thrown away
 * instead of treated like a second line.
 */
char *
hfgets(buf, len, fp)
char *buf;
int len;
FILE *fp;
{
	register int c;
	register char *cp, *tp;

	if ((cp = fgets(buf, len, fp)) == NULL)
		return NULL;

	if (*cp == '\n')
		return cp;

	tp = cp + strlen(cp);
	if (tp[-1] != '\n') {
		/* Line too long - part read didn't fit into a newline */
		while ((c = getc(fp)) != '\n' && c != EOF)
			;
	} else
		*--tp = '\0';	/* clobber newline */

	while ((c = getc(fp)) == ' ' || c == '\t') {
		/* Continuation line. */
		while ((c = getc(fp)) == ' ' || c == '\t')
			;
		if (tp - cp < len) {
			*tp++ = ' ';
			*tp++ = c;
		}
		while ((c = getc(fp)) != '\n' && c != EOF)
			if (tp - cp < len)
				*tp++ = c;
	}
	*tp++ = '\n';
	*tp++ = '\0';
	if (c != EOF)
		ungetc(c, fp);	/* push back first char of next header */
	return cp;
}



/*
 * ascii to time
 * return 0L on error
 */
long
atot(s)
char *s;
{
	extern long getindate();

	return getindate(s, (struct timeb *)NULL);
}


syntax highlighted by Code2HTML, v. 0.9.1