/*
 *                     RCS keyword extraction
 */
/*****************************************************************************
 *                       main routine: getoldkeys()
 *                       Testprogram: define KEEPTEST
 *****************************************************************************
 *
 * Copyright (C) 1982 by Walter F. Tichy
 *                       Purdue University
 *                       Computer Science Department
 *                       West Lafayette, IN 47907
 *
 * All rights reserved. No part of this software may be sold or distributed
 * in any form or by any means without the prior written permission of the
 * author.
 * Report problems and direct all inquiries to Tichy@purdue (ARPA net).
 */

/* $Log:	C.rcskeep $
 * Revision 1.1  90/07/15  16:54:31  pmoore
 * Initial revision
 * 
 * Revision 4.4  87/12/18  11:44:21  narten
 * more lint cleanups (Guy Harris)
 *
 * Revision 4.3  87/10/18  10:35:50  narten
 * Updating version numbers. Changes relative to 1.1 actually relative
 * to 4.1
 *
 * Revision 1.3  87/09/24  14:00:00  narten
 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
 * warnings)
 *
 * Revision 1.2  87/03/27  14:22:29  jenkins
 * Port to suns
 *
 * Revision 1.1  84/01/23  14:50:30  kcs
 * Initial revision
 *
 * Revision 4.1  83/05/10  16:26:44  wft
 * Added new markers Id and RCSfile; extraction added.
 * Marker matching with trymatch().
 *
 * Revision 3.2  82/12/24  12:08:26  wft
 * added missing #endif.
 *
 * Revision 3.1  82/12/04  13:22:41  wft
 * Initial revision.
 *
 */


/* Testprogram: define KEEPTEST. Prints out the keyword values found. */

#include  "rcsbase.h"

FILE *fp;
#define IDLENGTH 30
char prevauthor[IDLENGTH];
char prevdate[datelength];
char prevRCS[NCPFN];
char prevrev[revlength];
char prevsource[NCPPN];
char prevstate[IDLENGTH];
char prevlocker[IDLENGTH];
char dummy[IDLENGTH];

static int getval(char *, int, int);

/* Function: Tries to read keyword values for author, date,
 * revision number, RCS file, (both with and without path),
 * state, and workfilename out of the file fname.
 * The results are placed into
 * prevauthor, prevdate, prevRCS, prevrev, prevsource, prevstate.
 * Aborts immediately if it finds an error and returns false.
 * If it returns true, it doesn't mean that any of the
 * values were found; instead, check to see whether the corresponding arrays
 * contain the empty string.
 */
int getoldkeys(char *fname)
{
	register int c;
	char keyword[keylength + 2];
	register char *tp;
	enum markers mresult;

	/* initialize to empty */
	prevauthor[0] = prevsource[0] = prevstate[0] = prevdate[0] = prevrev[0] = '\0';

	if ((fp = fopen(fname, "r")) == NULL)
	{
		error("Can't open %s\n", fname);
		return false;
	}
	while ((c = getc(fp)) != EOF)
	{
		if (c == KDELIM)
		{
			/* try to get keyword */
			tp = keyword;
			while ((c = getc(fp)) != EOF && (tp < keyword + keylength) && (c != '\n')
			       && (c != KDELIM) && (c != VDELIM))
				*tp++ = c;

			if (c == KDELIM)
			{
				VOID ungetc(c, fp);
				continue;
			}
			if (c != VDELIM)
				continue;
			*tp++ = c;
			*tp = '\0';
			while ((c = getc(fp)) == ' ' || c == '\t');	/* skip blanks */
			VOID ungetc(c, fp);	/* needed for getval */

			switch (mresult = trymatch(keyword, true))
			{
			case Author:
				if (getval(prevauthor, IDLENGTH, true))
					if (!checkid(prevauthor, '\0'))
						goto errexit;
				break;
			case Date:
				if (!getprevdate(true))
					goto errexit;
				break;
			case Header:
			case Id:
				if (mresult == Header)
				{
					if (!getval(prevsource, NCPPN, true))
						break;	/* unexpanded */
				}
				else
				{
					if (!getval(prevRCS, NCPFN, true))
						break;	/* unexpanded */
				}
				if (!getval(prevrev, revlength, false))
					goto errexit;
				if (!checknum(prevrev, -1))
				{
					error("Bad revision number");
					goto errexit;
				}
				if (!getprevdate(false))
					goto errexit;
				if (!getval(prevauthor, IDLENGTH, false))
					goto errexit;
				if (!checkid(prevauthor, '\0'))
					goto errexit;
				if (!getval(prevstate, IDLENGTH, false))
					goto errexit;
				if (!checkid(prevstate, '\0'))
					goto errexit;
				VOID getval(dummy, IDLENGTH, true);	/* optional locker */
				VOID getval(prevlocker, IDLENGTH, true);	/* optional locker */
				break;
			case Locker:
				VOID getval(prevlocker, IDLENGTH, true);
				if (!checkid(prevlocker, '\0'))
					goto errexit;
				break;
			case Log:
				VOID getval(prevRCS, NCPFN, true);
				break;
			case RCSfile:
				VOID getval(prevRCS, NCPFN, true);
				break;
			case Revision:
				if (getval(prevrev, revlength, true))
					if (!checknum(prevrev, -1))
					{
						error("Bad revision number");
						goto errexit;
					}
				break;
			case Source:
				VOID getval(prevsource, NCPPN, true);
				break;
			case State:
				if (getval(prevstate, IDLENGTH, true))
					if (!checkid(prevstate, '\0'))
						goto errexit;
				break;
			default:
				continue;
			}
			if (getc(fp) != KDELIM)
				warn("Closing %c missing on keyword", KDELIM);
			if (prevauthor[0] != '\0' && prevrev[0] != '\0' && prevstate[0] != '\0' &&
			    prevdate[0] != '\0' &&
			  ((prevsource[0] != '\0') || (prevRCS[0] != '\0')))
			{
				/* done; prevlocker is irrelevant */
				break;
			}
		}
	}
	VOID fclose(fp);
	return true;

errexit:
	prevauthor[0] = prevsource[0] = prevstate[0] = prevdate[0] = prevrev[0] = '\0';
	VOID fclose(fp);
	return false;
}


/* Function: Places a keyword value into target, but not more
 * than maxchars characters. Prints an error if optional==false
 * and there is no keyword. Returns true if one is found, false otherwise.
 */
static int getval(char *target, int maxchars, int optional)
{
	register char *tp;
	register int c;

	tp = target;
	c = getc(fp);
	if (c == KDELIM)
	{
		if (!optional)
			error("Missing keyword value");
		VOID ungetc(c, fp);
		return false;
	}
	else
	{
		while (!(c == ' ' || c == '\n' || c == '\t' || c == KDELIM || c == EOF))
		{
			if (tp - target >= maxchars - 1)
			{
				error("keyword value too long");
				return false;
			}
			else
			{
				*tp++ = c;
				c = getc(fp);
			}
		}
		*tp = '\0';
#       ifdef KEEPTEST
		VOID printf("getval: %s\n", target);
#       endif
		while (c == ' ' || c == '\t')
			c = getc(fp);	/* skip trailing blanks */
	}
	VOID ungetc(c, fp);
	return true;
}


/* Function: reads a date prevdate; checks format
 * If there is not date and optional==false, an error is printed.
 * Returns false on error, true otherwise.
 */
int getprevdate(int optional)
{
	char prevday[10];
	char prevtime[10];

	prevday[0] = prevtime[0] = '\0';
	if (!getval(prevday, 9, optional))
		return optional;
	if (!getval(prevtime, 9, false))
		return false;
	/* process date */
	prevday[2] = prevday[5] = prevday[8] = prevtime[2] = prevtime[5] = '.';
	prevday[9] = '\0';
	VOID strcpy(prevdate, prevday);
	VOID strcat(prevdate, prevtime);
	if (!checknum(prevdate, 5))
	{
		error("Bad date: %s", prevdate);
		prevdate[0] = '\0';
		return false;
	}
	return true;
}


int checknum(char *sp, int fields)
{
	register int dotcount;
	if (sp == nil || *sp == '\0')
		return true;
	dotcount = 0;
	while (*sp)
	{
		if (*sp == '.')
			dotcount++;
		else if (ctab[*sp] != DIGIT)
			return false;
		sp++;
	}
	if (fields >= 0 && dotcount != fields)
		return false;
	return true;
}


#ifdef KEEPTEST
char *RCSfilename;
char *workfilename;
char *basename;

int main(int argc, char *argv[])
{
	cmdid = "keeptest";
	while (*(++argv))
	{
		if (getoldkeys(*argv))
			VOID printf("%s:  revision: %s, date: %s, author: %s, state: %s\n",
			   *argv, prevrev, prevdate, prevauthor, prevstate);
		VOID printf("Source: %s, RCSfile: %s\n", prevsource, prevRCS);
	}

	return 0;
}
#endif
