#include "miscfn.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "rpmlib.h"
#include "rpm_malloc.h"
#include "messages.h"
#include "misc.h"
#include "miscfn.h"
#include "oldrpmdb.h"

static int labelstrlistToLabelList(char * str, int length, 
				   struct oldrpmdbLabel ** list);
static char * getScript(char * which, struct oldrpmdb *oldrpmdb, 
		        struct oldrpmdbLabel label);

static char * prefix = "/var/lib/rpm";

char * oldrpmdbLabelToLabelstr(struct oldrpmdbLabel label, int withFileNum) {
    char * c;
    char buffer[50];
 
    if (withFileNum && label.fileNumber > -1) 
	c = malloc(strlen(label.name) + strlen(label.version) + 
		   strlen(label.release) + 10);
    else
	c = malloc(strlen(label.name) + strlen(label.version) + 
		   strlen(label.release) + 3);

    strcpy(c, label.name);
    strcat(c, ":");
    strcat(c, label.version);
    strcat(c, ":");
    strcat(c, label.release);

    if (withFileNum && label.fileNumber > -1)  {
	sprintf(buffer, "%d", label.fileNumber);
	strcat(c, ":");
	strcat(c, buffer);
    }

    return c;
}

int oldrpmdbLabelstrToLabel(char * str, int length, struct oldrpmdbLabel * label) {
    char * chptr;

    label->freeType = RPMDB_FREENAME;
    label->next = NULL;
    label->name = malloc(length + 1);
    if (!label->name) {
	return 1;
    }
    memcpy(label->name, str, length);
    label->name[length] = '\0';

    chptr = label->name;
    while (*chptr != ':') chptr++;
    *chptr = '\0';
    label->version = ++chptr;
    while (*chptr != ':') chptr++;
    *chptr = '\0';
    label->release = chptr + 1;

    label->fileNumber = -1;

    /* there might be a path number tagged on to the end of this */
    while ((chptr - label->name) < length && *chptr != ':') chptr++;
    if ((chptr - label->name) < length) {
	*chptr = '\0';
	label->fileNumber = atoi(chptr + 1);
    }

    return 0;
}

static int labelstrlistToLabelList(char * str, int length, 
				   struct oldrpmdbLabel ** list) {
    char * start, * chptr;
    struct oldrpmdbLabel * head = NULL;
    struct oldrpmdbLabel * tail = NULL;
    struct oldrpmdbLabel * label;
    
    start = str;
    for (chptr = start; (chptr - str) < length; chptr++) {
	/* spaces following a space get ignored */
	if (*chptr == ' ' && start < chptr) {
	    label = malloc(sizeof(struct oldrpmdbLabel));
	    if (!label) {
		oldrpmdbFreeLabelList(head);
		return 1;
	    }
	    if (oldrpmdbLabelstrToLabel(start, chptr - start, label)) {
		free(label);
		oldrpmdbFreeLabelList(head);
		return 1;
	    }

	    if (!head) {
		head = label;
		tail = label;
	    } else {
		tail->next = label;
		tail = tail->next;
	    }

	    start = chptr + 1;
	}
    }

    /* a space on the end would break things horribly w/o this test */
    if (start < chptr) {
	label = malloc(sizeof(struct oldrpmdbLabel));
	if (!label) {
	    oldrpmdbFreeLabelList(head);
	    return 1;
	}
	if (oldrpmdbLabelstrToLabel(start, chptr - start, label)) {
	    free(label);
	    oldrpmdbFreeLabelList(head);
	    return 1;
	}

	if (!head) {
	    head = label;
	    tail = label;
	} else {
	    tail->next = label;
	    tail = tail->next;
	}

	start = chptr + 1;
    }

    *list = head;
    return 0;
}

/* returns 0 on success, -1 on failure */
int oldrpmdbOpen(struct oldrpmdb * oldrpmdb) {
    unsigned int gdbmFlags;
    char path[255];
    int goterr = 0;

    oldrpmdb->rpmdbError = RPMDB_NONE;

    gdbmFlags = GDBM_READER;

    strcpy(path, prefix);
    strcat(path, "/packages");
    oldrpmdb->packages = gdbm_open(path, 1024, gdbmFlags, 0644, NULL);
    if (!oldrpmdb->packages) {
	rpmError(RPMERR_GDBMOPEN, path, gdbm_strerror(gdbm_errno));
	goterr = 1;
    }

    strcpy(path, prefix);
    strcat(path, "/nameidx");
    oldrpmdb->nameIndex = gdbm_open(path, 1024, gdbmFlags, 0644, NULL);
    if (!oldrpmdb->packages) {
	rpmError(RPMERR_GDBMOPEN, path, gdbm_strerror(gdbm_errno));
	goterr = 1;
    }

    strcpy(path, prefix);
    strcat(path, "/pathidx");
    oldrpmdb->pathIndex = gdbm_open(path, 1024, gdbmFlags, 0644, NULL);
    if (!oldrpmdb->packages) {
	rpmError(RPMERR_GDBMOPEN, path, gdbm_strerror(gdbm_errno));
	goterr = 1;
    }

    strcpy(path, prefix);
    strcat(path, "/iconidx");
    oldrpmdb->iconIndex = gdbm_open(path, 1024, gdbmFlags, 0644, NULL);
    if (!oldrpmdb->iconIndex) {
	rpmError(RPMERR_GDBMOPEN, path, gdbm_strerror(gdbm_errno));
	goterr = 1;
    }

    strcpy(path, prefix);
    strcat(path, "/groupindex");
    oldrpmdb->groupIndex = gdbm_open(path, 1024, gdbmFlags, 0644, NULL);
    if (!oldrpmdb->packages) {
	rpmError(RPMERR_GDBMOPEN, path, gdbm_strerror(gdbm_errno));
	goterr = 1;
    }

    strcpy(path, prefix);
    strcat(path, "/postidx");
    oldrpmdb->postIndex = gdbm_open(path, 1024, gdbmFlags, 0644, NULL);
    if (!oldrpmdb->postIndex) {
	rpmError(RPMERR_GDBMOPEN, path, gdbm_strerror(gdbm_errno));
	goterr = 1;
    }

    if (goterr) {
	oldrpmdbClose(oldrpmdb);
	return -1;
    }

    return 0;
}

void oldrpmdbClose(struct oldrpmdb * oldrpmdb) {
    gdbm_close(oldrpmdb->packages);
    gdbm_close(oldrpmdb->nameIndex);
    gdbm_close(oldrpmdb->pathIndex);
    gdbm_close(oldrpmdb->postIndex);
    gdbm_close(oldrpmdb->groupIndex);
    gdbm_close(oldrpmdb->iconIndex);
}

struct oldrpmdbLabel * oldrpmdbGetAllLabels(struct oldrpmdb * oldrpmdb) {
    datum rec;

    struct oldrpmdbLabel * head = NULL;
    struct oldrpmdbLabel * tail = NULL;
    struct oldrpmdbLabel * label;

    oldrpmdb->rpmdbError = RPMDB_NONE;

    rec = gdbm_firstkey(oldrpmdb->packages);
    while (rec.dptr) {
	label = malloc(sizeof(struct oldrpmdbLabel));
	if (!label) {
	    oldrpmdbFreeLabelList(head);
	    oldrpmdb->rpmdbError = RPMDB_NO_MEMORY;
	    return NULL;
	}
	if (oldrpmdbLabelstrToLabel(rec.dptr, rec.dsize, label)) {
	    free(label);
	    oldrpmdbFreeLabelList(head);
	    oldrpmdb->rpmdbError = RPMDB_NO_MEMORY;
	    return NULL;
	}

	if (!head) {
	    head = label;
	    tail = label;
	} else {
	    tail->next = label;
	    tail = tail->next;
	}

	rec = gdbm_nextkey(oldrpmdb->packages, rec);
    }

    return head;
}

struct oldrpmdbLabel * oldrpmdbFindPackagesByFile(struct oldrpmdb * oldrpmdb, char * path) {
    datum rec;
    datum key;
    struct oldrpmdbLabel * list;

    oldrpmdb->rpmdbError = RPMDB_NONE;

    key.dptr = path;
    key.dsize = strlen(path);
    rec = gdbm_fetch(oldrpmdb->pathIndex, key);
    
    if (!rec.dptr) 
	return NULL;
    if (labelstrlistToLabelList(rec.dptr, rec.dsize, &list)) {
	free(rec.dptr);
	oldrpmdb->rpmdbError = RPMDB_NO_MEMORY;
	return NULL;
    }
    free(rec.dptr);

    return list;
}

struct oldrpmdbLabel * oldrpmdbFindPackagesByLabel(struct oldrpmdb * oldrpmdb, 
					     struct oldrpmdbLabel label)

/* the Name has to be here. The version/release fields optionally
   restrict the search. Either will do. */

{
    datum rec;
    datum key;
    struct oldrpmdbLabel * list;
    struct oldrpmdbLabel * prospect;
    struct oldrpmdbLabel * parent;
    int bad;

    oldrpmdb->rpmdbError = RPMDB_NONE;

    key.dptr = label.name;
    key.dsize = strlen(label.name);
    rec = gdbm_fetch(oldrpmdb->nameIndex, key);
    
    if (!rec.dptr) 
	return NULL;
    if (labelstrlistToLabelList(rec.dptr, rec.dsize, &list)) {
	free(rec.dptr);
	oldrpmdb->rpmdbError = RPMDB_NO_MEMORY;
	return NULL;
    }
    free(rec.dptr);

    prospect = list;
    parent = NULL;
    while (prospect) {
	bad = 0;
	if (label.version && strcmp(label.version, prospect->version))
	    bad = 1;
	else if (label.release && strcmp(label.release, prospect->release))
	    bad = 1;

	if (bad) {
	    oldrpmdbFreeLabel(*prospect);
	    if (!parent) {
		list = prospect->next;
		free(prospect);
		prospect = list;
	    } else {
		parent->next = prospect->next;
		free(prospect);
		prospect = parent->next;
	    }
	} else {
	    prospect = prospect->next;
	}
    }

    return list;
}

struct oldrpmdbLabel oldrpmdbMakeLabel(char * name, char * version, char * release,
				 int fileNumber, enum oldrpmdbFreeType freeType) {
    struct oldrpmdbLabel label;

    label.next = NULL;
    label.freeType = freeType;
    label.name = name;
    label.version = version;
    label.release = release;
    label.fileNumber = fileNumber;

    return label;
}

void oldrpmdbFreeLabelList(struct oldrpmdbLabel * list) {
    struct oldrpmdbLabel * saved;

    while (list) {
	oldrpmdbFreeLabel(*list);
	saved = list->next;
	free(list);
	list = saved;
    }
}

void oldrpmdbFreeLabel(struct oldrpmdbLabel label) {
    if (label.freeType == RPMDB_NOFREE) return;

    free(label.name);
    if (label.freeType == RPMDB_FREEALL) {
	free(label.version);
	free(label.release);
    }
}

/* Returns NULL on error */
char * oldrpmdbGetPackageGroup(struct oldrpmdb * oldrpmdb, struct oldrpmdbLabel label) {
    datum key, rec;
    char * g;
    
    key.dptr = label.name;
    key.dsize = strlen(label.name);
    
    rec = gdbm_fetch(oldrpmdb->groupIndex, key);
    if (!rec.dptr) {
	return strdup("Unknown");
    }
    
    g = malloc(rec.dsize + 1);
    strncpy(g, rec.dptr, rec.dsize);
    g[rec.dsize] = '\0';
    free(rec.dptr);

    return g;
}

static char * getScript(char * which, struct oldrpmdb *oldrpmdb, 
		        struct oldrpmdbLabel label) {
    datum key, rec;
    char * labelstr, * l;

    labelstr = oldrpmdbLabelToLabelstr(label, 0);
    labelstr = realloc(labelstr, strlen(labelstr) + 10);
    strcat(labelstr, ":");
    strcat(labelstr, which);

    key.dptr = labelstr;
    key.dsize = strlen(labelstr);
    
    rec = gdbm_fetch(oldrpmdb->postIndex, key);
    free(labelstr);

    if (!rec.dptr) return NULL;

    l = malloc(rec.dsize + 1);
    strncpy(l, rec.dptr, rec.dsize);
    l[rec.dsize] = '\0';

    free(rec.dptr);

    return l;
}

char *oldrpmdbGetPackagePostun (struct oldrpmdb *oldrpmdb, 
				struct oldrpmdbLabel label) {
    return getScript("post", oldrpmdb, label);
}

char *oldrpmdbGetPackagePreun (struct oldrpmdb *oldrpmdb, 
				struct oldrpmdbLabel label) {
    return getScript("pre", oldrpmdb, label);
}

/* Returns NULL on error or if no icon exists */
char * oldrpmdbGetPackageGif(struct oldrpmdb * oldrpmdb, 
			     struct oldrpmdbLabel label, int * size) {
    datum key, rec;
    char * labelstr;

    labelstr = oldrpmdbLabelToLabelstr(label, 0);
    
    key.dptr = labelstr;
    key.dsize = strlen(labelstr);
    
    rec = gdbm_fetch(oldrpmdb->iconIndex, key);
    free(labelstr);
    if (!rec.dptr) {
	return NULL;
    }

    *size = rec.dsize;

    return rec.dptr;
}

/* return 0 on success, 1 on failure */
int oldrpmdbGetPackageInfo(struct oldrpmdb * oldrpmdb, struct oldrpmdbLabel label,
			struct oldrpmdbPackageInfo * pinfo) {
    char * labelstr;
    char ** list, ** prelist;
    char ** strptr;
    datum key, rec;
    int i, j;

    labelstr = oldrpmdbLabelToLabelstr(label, 0);

    rpmMessage(RPMMESS_DEBUG, "pulling %s from database\n", labelstr);

    key.dptr = labelstr;
    key.dsize = strlen(labelstr);
    
    rec = gdbm_fetch(oldrpmdb->packages, key);
    if (!rec.dptr) {
	rpmError(RPMERR_OLDDBCORRUPT, "package not found in database");
	return 1;
    }

    free(labelstr);

    list = splitString(rec.dptr, rec.dsize, '\1');
    free(rec.dptr);

    pinfo->version = strdup(list[1]); 
    pinfo->release = strdup(list[2]); 
    /* list[3] == "1" always */
    pinfo->name = malloc(strlen(list[0]) + strlen(list[4]) + 2);
    strcpy(pinfo->name, list[0]);
    if (strlen(list[4])) {
	strcat(pinfo->name, "-");
	strcat(pinfo->name, list[4]);
    }
    pinfo->labelstr = malloc(strlen(pinfo->name) + strlen(pinfo->version) +
			     strlen(pinfo->release) + 3);
    strcpy(pinfo->labelstr, pinfo->name);
    strcat(pinfo->labelstr, ":");
    strcat(pinfo->labelstr, pinfo->version);
    strcat(pinfo->labelstr, ":");
    strcat(pinfo->labelstr, pinfo->release);

    pinfo->preamble = strdup(list[5]);
    pinfo->installTime = atoi(list[6]);
    pinfo->fileCount = atoi(list[7]);
    
    prelist = splitString(pinfo->preamble, strlen(pinfo->preamble), '\n');

    /* these are optional */
    pinfo->distribution = NULL;
    pinfo->vendor = NULL;
    pinfo->description = NULL;
    pinfo->copyright = NULL;

    for (strptr = prelist; *strptr; strptr++) {
	if (!strncasecmp("Description: ", *strptr, 13))
	    pinfo->description = strdup((*strptr) + 13);
	else if (!strncasecmp("Copyright: ", *strptr, 11))
	    pinfo->copyright = strdup((*strptr) + 11);
	else if (!strncasecmp("Distribution: ", *strptr, 14))
	    pinfo->distribution = strdup((*strptr) + 14);
	else if (!strncasecmp("Vendor: ", *strptr, 8))
	    pinfo->vendor = strdup((*strptr) + 8);
	else if (!strncasecmp("size: ", *strptr, 6))
	    pinfo->size = atoi((*strptr) + 6);
	else if (!strncasecmp("BuildDate: ", *strptr, 11))
	    pinfo->buildTime =atoi((*strptr) + 11);
	else if (!strncasecmp("BuildHost: ", *strptr, 11))
	    pinfo->buildHost = strdup((*strptr) + 11);
    }
    freeSplitString(prelist);

    if (!pinfo->vendor) pinfo->vendor = strdup("");
    if (!pinfo->description) pinfo->description = strdup("");
    if (!pinfo->distribution) pinfo->distribution = strdup("");
    if (!pinfo->copyright) {
	pinfo->copyright = strdup("");
	printf("no copyright!\n");
    }

    pinfo->files = malloc(sizeof(struct oldrpmFileInfo) * pinfo->fileCount);

    j = 8;
    for (i = 0; i < pinfo->fileCount; i++) {
	oldrpmfileFromInfoLine(list[j], list[j + 1], list[j + 2],
			    &pinfo->files[i]);
	j += 3;
    }

    freeSplitString(list);
		
    return 0;
}

void oldrpmdbFreePackageInfo(struct oldrpmdbPackageInfo package) {
    int i;

    free(package.version);
    free(package.release);
    free(package.name);
    free(package.labelstr);
    free(package.buildHost);
    free(package.vendor);
    free(package.description);
    free(package.copyright);
    free(package.distribution);
    free(package.preamble);

    for (i = 0; i < package.fileCount; i++) {
	oldrpmfileFree(&package.files[i]);
    }

    free(package.files);
}

int oldrpmdbLabelCmp(struct oldrpmdbLabel * one, struct oldrpmdbLabel * two) {
    int i;

    if ((i = strcmp(one->name, two->name)))
	return i;
    else if ((i = strcmp(one->version, two->version)))
	return i;
    else
	return strcmp(one->release, two->release);
}

void oldrpmdbSetPrefix(char * new) {
    prefix = new;
}
