/* $Header: header.c,v 2.5 89/11/10 12:23:54 network Exp $
 *
 * A program to parse RFC 822 mail/news headers.
 *
 * usage: header [-c] [-n] [-v] [-f field] ... files
 *
 * Default action is to print entire header.  If one or more -f options
 * are given, only the specified fields are printed.  The field names are
 * not printed unless -n is specified.  Field name comparisons are case
 * insensitive unless -c is specified.  If -v is specified, all headers 
 * except those specified with -f are printed.  NOTE: -v implies -n.
 *
 * Output lines are preceeded by the filename if more than one file is
 * specified.
 *
 * This program is intended for use in delivery files, to extract multi-
 * line header fields.
 *
 * $Log:	header.c,v $
 * Revision 2.5  89/11/10  12:23:54  network
 * Delintify.
 * 
 * Revision 2.4  89/10/30  16:08:48  network
 * Don't automatically print field names with "-v".  (Sorry, Tom.)
 * 
 * Revision 2.3  89/10/30  16:03:29  network
 * Add "-v" (everything except) option.
 * Submitted by Tom Neff <tneff%bfmny0@uunet.uu.net>.
 * 
 * Revision 2.2  89/06/09  13:08:07  network
 * Adapt to BSD quirks.
 * 
 * Revision 2.1  89/06/09  12:25:29  network
 * Update RCS revisions.
 * 
 * Revision 1.5  89/06/09  12:23:51  network
 * Baseline for 2.0 release.
 * 
 */

#include <stdio.h>
#include <ctype.h>

/*
 * Manifest constants
 */

#define TRUE 1
#define FALSE 0

/*
 * Other useful macros.
 */

#define GETSIZE(buf)    (sizeof(buf) - 1)

#define ISFROM(p) ((p)[0] == 'F' && (p)[1] == 'r' && (p)[2] == 'o' \
		&& (p)[3] == 'm' && (p)[4] == ' ')

/*
 * External data.
 */

/* Variables set by getopt() [blech] */

extern  int     optind, opterr;
extern  char    *optarg;

/*
 * Library functions.
 */

extern  char    *malloc();
extern  char    *realloc();
extern  void    free();

/*
 * Global data
 */

int     field_count     = 0;
int     field_alloc     = 0;
char    **field_names   = NULL;

int     nocasematch     = TRUE;         /* ignore case in header matches */
int     printnames      = FALSE;        /* print field names with data */
int     except          = FALSE;        /* reverse sense of -f */

/*----------------------------------------------------------------------
 * The Program.
 */

main(argc, argv)
int     argc;
char    **argv;
{
	int     c, errors;

	field_alloc = 8;
	field_names = (char **) malloc(field_alloc * sizeof(char **));
	if (field_names == NULL)
		nomem();

	errors = FALSE;
	while ((c = getopt(argc, argv, "cnvf:")) != EOF)
	{
		switch (c)
		{
		case 'c':
			nocasematch = FALSE;
			break;
		case 'n':
			printnames = TRUE;
			break;
		case 'v':
			except = TRUE;
			break;
		case 'f':
			if (field_count >= field_alloc)
			{
				field_alloc *= 2;
				field_names =
				    (char **) realloc((char *)field_names,
					      field_alloc * sizeof(char **));
				if (field_names == NULL)
					nomem();
			}
			field_names[field_count++] = optarg;
			break;
		default:
			errors = TRUE;
			break;
		}
	}

	if (errors)
		usage();

	if (optind == argc)
		header(stdin, (char *)NULL);
	else
	{
		FILE    *fp;
		int     a, filenames;

		filenames = ((argc - optind) > 1);
		for (a = optind; a < argc; ++a)
		{
			if ((fp = fopen(argv[a], "r")) == NULL)
			{
				errors = TRUE;
				perror(argv[a]);
				continue;
			}

			header(fp, (filenames ? argv[a] : (char *)NULL));
			(void) fclose(fp);
		}
	}

	exit(errors ? 1 : 0);
	/* NOTREACHED */
}

usage()
{
	(void) fprintf(stderr,
		"usage: header [-c] [-n] [-v] [-f fieldname] ... files\n");
	exit(1);
}

nomem()
{
	(void) fprintf(stderr, "header: out of memory\n");
	exit(1);
}

header(fp, filename)
FILE    *fp;
char    *filename;
{
	char    buf[1024];

	if (fgets(buf, GETSIZE(buf), fp) == NULL)
		return;

	/* Ignore From_ line(s). */

	while (ISFROM(buf) || buf[0] == '>')
	{
		if (fgets(buf, GETSIZE(buf), fp) == NULL)
			return;
	}

	while (buf[0] != '\n')
	{
		char    *p;
		int     print_this;

		p = buf;
		while (isupper(*p) || islower(*p) || isdigit(*p) || *p == '-')
			++p;
		if (p == buf || *p != ':')
			break;
		print_this = field(buf, p - buf);
		if (except)
			print_this = !print_this;
		if (print_this)
		{
			if (filename)
			{
				(void) fputs(filename, stdout);
				(void) fputc(':', stdout);
			}
			++p;
			if (*p == ' ' || *p == '\t')
				++p;
			if (field_count == 0 || printnames)
				(void) fputs(buf, stdout);
			else
				(void) fputs(p, stdout);
		}

		/* get the next input line */
		if (fgets(buf, GETSIZE(buf), fp) == NULL)
			break;

		/* deal with continuation lines */
		while (buf[0] == ' ' || buf[0] == '\t')
		{
			if (print_this)
			{
				if (filename)
				{
					(void) fputs(filename, stdout);
					(void) fputc(':', stdout);
				}
				(void) fputs(buf, stdout);
			}

			if (fgets(buf, GETSIZE(buf), fp) == NULL)
			{
				buf[0] = '\n';
				break;
			}
		}
	}
}

int
field(s, n)
char    *s;
int     n;
{
	int     i;

	if (field_count == 0)
		return TRUE;

	for (i = 0; i < field_count; ++i)
	{
		char    *f = field_names[i];

		if (strlen(f) == n)
		{
			if (nocasematch)
			{
				if (ci_strncmp(f, s, n) == 0)
					return TRUE;
			}
			else
			{
				if (strncmp(f, s, n) == 0)
					return TRUE;
			}
		}
	}

	return FALSE;
}

int
ci_strncmp(s, t, n)
char    *s, *t;
int     n;
{
	char    c, d;

	while (n-- > 0)
	{
		c = *s++;
		d = *t++;
		if ((c == 0) && (d == 0))
			break;
		if (isupper(c))
			c = tolower(c);
		if (isupper(d))
			d = tolower(d);
		if (c > d)
			return 1;
		if (c < d)
			return -1;
	}

	return 0;
}
