/* Routine to "verify" a parsed spec file */

/***************

Here's what we do

. check for duplicate fields
. make sure a certain set of fields are present
. in subpackages, make sure a certain set of fields are present
. in subpackages, make sure certain fields are *not* present

. check for duplicate sources/patch numbers
. do some checking on the field values for legit characters

****************/

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

#include "header.h"
#include "spec.h"
#include "specP.h"
#include "messages.h"
#include "rpmlib.h"
#include "stringbuf.h"
#include "misc.h"

#define EMPTY(s) ((!(s)) || (!(*(s))))
#define OOPS(s) {fprintf(stderr, "verifySpec: %s\n", s); res = 1;}
#define MUST 1

struct packageFieldsRec {
    int tag;
    int present;
    int shouldBePresent;
};

struct fieldNamesRec {
    int tag;
    char *name;
};

static int checkHeaderTags(Header inh, struct packageFieldsRec *pfr);
static char *tagName(int tag);

/* This is a list of tags related to the "main" package. */
/* Only list those that must or must not be present.     */

static struct packageFieldsRec packageFields[] = {
    { RPMTAG_NAME,            0, MUST },
    { RPMTAG_VERSION,         0, MUST },
    { RPMTAG_RELEASE,         0, MUST },
/*    { RPMTAG_SUMMARY,         0, MUST }, */
    { RPMTAG_DESCRIPTION,     0, MUST },
    { RPMTAG_COPYRIGHT,       0, MUST },
/*    { RPMTAG_PACKAGER,        0, MUST }, */
    { RPMTAG_GROUP,           0, MUST },
    { 0, 0, 0 },
};

/* This is a list of tags related to "sub" packages. */
/* Only list those that must or must not be present. */

static struct packageFieldsRec subpackageFields[] = {
    { RPMTAG_NAME,            0, MUST },  /* This is inserted automaticaly */
    { RPMTAG_SUMMARY,         0, MUST },
    { RPMTAG_DESCRIPTION,     0, MUST },
    { RPMTAG_GROUP,           0, MUST },
    { RPMTAG_NAME,            0, 0 },
    { RPMTAG_COPYRIGHT,       0, 0 },
    { RPMTAG_PACKAGER,        0, 0 },
    { RPMTAG_BUILDROOT,       0, 0 },
    { 0, 0, 0 },
};

static struct packageFieldsRec *findTag(int tag, struct packageFieldsRec *pfr)
{
    struct packageFieldsRec *p = pfr;

    while (p->tag) {
	if (p->tag == tag)
	    return p;
	p++;
    }

    return NULL;
}

static char *tagName(int tag)
{
    int i = 0;
    static char nameBuf[1024];
    char *s;

    strcpy(nameBuf, "(unknown)");
    while (i < rpmTagTableSize) {
	if (tag == rpmTagTable[i].val) {
	    strcpy(nameBuf, rpmTagTable[i].name + 7);
	    s = nameBuf+1;
	    while (*s) {
		*s = tolower(*s);
		s++;
	    }
	}
	i++;
    }

    return nameBuf;
}

static int checkHeaderTags(Header inh, struct packageFieldsRec *pfr)
{
    Header h;
    HeaderIterator headerIter;
    int res = 0;
    int_32 tag, lastTag, type, c;
    void *ptr;
    struct packageFieldsRec *p;

    /* Sort the index, so it'll be easy to catch dups */
    h = headerCopy(inh);
    headerSort(h);

    headerIter = headerInitIterator(h);
    lastTag = 0;
    while (headerNextIterator(headerIter, &tag, &type, &ptr, &c)) {
	if (tag == lastTag) {
	    rpmError(RPMERR_BADSPEC, "Duplicate fields for     : %s",
		  tagName(tag));
	    res = 1;
	}
	p = findTag(tag, pfr);
	if (p) 
	    p->present = 1;
	lastTag = tag;
    }
    headerFreeIterator(headerIter);
    headerFree(h);

    p = pfr;
    while (p->tag) {
	if (p->shouldBePresent != p->present) {
	    rpmError(RPMERR_BADSPEC, "Field must%s be present%s: %s",
		  p->present ? " NOT" : "", p->present ? "" : "    ",
		  tagName(p->tag));
	    res = 1;
	}
	p->present = 0;  /* hack to clear it for next time :-) */
	p++;
    }
    
    return res;
}

int verifySpec(Spec s)
{
    int res = 0;
    struct PackageRec *pr;
    struct packageFieldsRec *fields;
    char name[1024];
    char *val;
    
    if (EMPTY(s->name)) {
	OOPS("No Name field");
    }

    /* Check each package/subpackage */
    pr = s->packages;
    fields = packageFields;
    while (pr) {
	if (! pr->subname) {
	    if (pr->newname) {
		strcpy(name, pr->newname);
	    } else {
		strcpy(name, s->name);
	    }
	} else {
	    sprintf(name, "%s-%s", s->name, pr->subname);
	}
	printf("* Package: %s\n", name);

	if (checkHeaderTags(pr->header, fields)) {
	    res = 1;
	}

	val = NULL;
	headerGetEntry(pr->header, RPMTAG_VERSION, NULL, (void *) &val, NULL);
	if (val && strchr(val, '-')) {
	    rpmError(RPMERR_BADSPEC, "Illegal '-' char in version: %s\n", val);
	    res = 1;
	}
	val = NULL;
	headerGetEntry(pr->header, RPMTAG_RELEASE, NULL, (void *) &val, NULL);
	if (val && strchr(val, '-')) {
	    rpmError(RPMERR_BADSPEC, "Illegal '-' char in release: %s\n", val);
	    res = 1;
	}
	
	pr = pr->next;
	fields = subpackageFields;
    }
    
    return res;
}
