#include "config.h"
#include "miscfn.h"

#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#include "header.h"
#include "intl.h"
#include "rpmlib.h"

static char * permsFormat(int_32 type, const void * data, 
		          char * formatPrefix, int padding, int element);
static char * depflagsFormat(int_32 type, const void * data, 
		             char * formatPrefix, int padding, int element);
static char * triggertypeFormat(int_32 type, const void * data, 
		         char * formatPrefix, int padding, int element);
static char * fflagsFormat(int_32 type, const void * data, 
		           char * formatPrefix, int padding, int element);
static int fsnamesTag(Header h, int_32 * type, void ** data, int_32 * count,
		      int * freeData);
static int fssizesTag(Header h, int_32 * type, void ** data, int_32 * count,
		      int * freeData);
static int instprefixTag(Header h, int_32 * type, void ** data, int_32 * count,
		         int * freeData);
static int triggercondsTag(Header h, int_32 * type, void ** data, 
			   int_32 * count, int * freeData);
static int triggertypeTag(Header h, int_32 * type, void ** data, 
			  int_32 * count, int * freeData);
static char * permsString(int mode);

const struct headerSprintfExtension rpmHeaderFormats[] = {
    { HEADER_EXT_TAG, "RPMTAG_FSSIZES", { fssizesTag } },
    { HEADER_EXT_TAG, "RPMTAG_FSNAMES", { fsnamesTag } },
    { HEADER_EXT_TAG, "RPMTAG_INSTALLPREFIX", { instprefixTag } },
    { HEADER_EXT_TAG, "RPMTAG_TRIGGERCONDS", { triggercondsTag } },
    { HEADER_EXT_TAG, "RPMTAG_TRIGGERTYPE", { triggertypeTag } },
    { HEADER_EXT_FORMAT, "depflags", { depflagsFormat } },
    { HEADER_EXT_FORMAT, "fflags", { fflagsFormat } },
    { HEADER_EXT_FORMAT, "perms", { permsFormat } },
    { HEADER_EXT_FORMAT, "permissions", { permsFormat } },
    { HEADER_EXT_FORMAT, "triggertype", { triggertypeFormat } },
    { HEADER_EXT_MORE, NULL, { (void *) headerDefaultFormats } }
} ;

static char * permsString(int mode) {
    char * perms = malloc(11);

    strcpy(perms, "-----------");
   
    if (mode & S_ISVTX) perms[10] = 't';

    if (mode & S_IRUSR) perms[1] = 'r';
    if (mode & S_IWUSR) perms[2] = 'w';
    if (mode & S_IXUSR) perms[3] = 'x';
 
    if (mode & S_IRGRP) perms[4] = 'r';
    if (mode & S_IWGRP) perms[5] = 'w';
    if (mode & S_IXGRP) perms[6] = 'x';

    if (mode & S_IROTH) perms[7] = 'r';
    if (mode & S_IWOTH) perms[8] = 'w';
    if (mode & S_IXOTH) perms[9] = 'x';

    if (mode & S_ISUID) {
	if (mode & S_IXUSR) 
	    perms[3] = 's'; 
	else
	    perms[3] = 'S'; 
    }

    if (mode & S_ISGID) {
	if (mode & S_IXGRP) 
	    perms[6] = 's'; 
	else
	    perms[6] = 'S'; 
    }

    if (S_ISDIR(mode)) 
	perms[0] = 'd';
    else if (S_ISLNK(mode)) {
	perms[0] = 'l';
    }
    else if (S_ISFIFO(mode)) 
	perms[0] = 'p';
    else if (S_ISSOCK(mode)) 
	perms[0] = 'l';
    else if (S_ISCHR(mode)) {
	perms[0] = 'c';
    } else if (S_ISBLK(mode)) {
	perms[0] = 'b';
    }

    return perms;
}

static char * triggertypeFormat(int_32 type, const void * data, 
		         char * formatPrefix, int padding, int element) {
    const int_32 * item = data;
    char * val;

    if (type != RPM_INT32_TYPE) {
	val = malloc(20);
	strcpy(val, _("(not a number)"));
    } else if (*item & RPMSENSE_TRIGGERIN) {
	val = strdup("in");
    } else {
	val = strdup("un");
    }

    return val;
}

static char * permsFormat(int_32 type, const void * data, 
		         char * formatPrefix, int padding, int element) {
    char * val;
    char * buf;

    if (type != RPM_INT32_TYPE) {
	val = malloc(20);
	strcpy(val, _("(not a number)"));
    } else {
	val = malloc(15 + padding);
	strcat(formatPrefix, "s");
	buf = permsString(*((int_32 *) data));
	sprintf(val, formatPrefix, buf);
	free(buf);
    }

    return val;
}

static char * fflagsFormat(int_32 type, const void * data, 
		         char * formatPrefix, int padding, int element) {
    char * val;
    char buf[15];
    int anint = *((int_32 *) data);

    if (type != RPM_INT32_TYPE) {
	val = malloc(20);
	strcpy(val, _("(not a number)"));
    } else {
	buf[0] = '\0';
	if (anint & RPMFILE_DOC)
	    strcat(buf, "d");
	if (anint & RPMFILE_CONFIG)
	    strcat(buf, "c");
	if (anint & RPMFILE_SPECFILE)
	    strcat(buf, "s");
	if (anint & RPMFILE_MISSINGOK)
	    strcat(buf, "m");
	if (anint & RPMFILE_NOREPLACE)
	    strcat(buf, "n");
	if (anint & RPMFILE_GHOST)
	    strcat(buf, "g");

	val = malloc(5 + padding);
	strcat(formatPrefix, "s");
	sprintf(val, formatPrefix, buf);
    }

    return val;
}

static char * depflagsFormat(int_32 type, const void * data, 
		         char * formatPrefix, int padding, int element) {
    char * val;
    char buf[10];
    int anint = *(((int_32 *) data) + element);

    if (type != RPM_INT32_TYPE) {
	val = malloc(20);
	strcpy(val, _("(not a number)"));
    } else {
	*buf = '\0';

	if (anint & RPMSENSE_LESS) 
	    strcat(buf, "<");
	if (anint & RPMSENSE_GREATER)
	    strcat(buf, ">");
	if (anint & RPMSENSE_EQUAL)
	    strcat(buf, "=");
	if (anint & RPMSENSE_SERIAL)
	    strcat(buf, "S");

	val = malloc(5 + padding);
	strcat(formatPrefix, "s");
	sprintf(val, formatPrefix, buf);
    }

    return val;
}

static int fsnamesTag(Header h, int_32 * type, void ** data, int_32 * count,
		      int * freeData) {
    char ** list;

    if (rpmGetFilesystemList(&list, count)) {
	return 1;
    }

    *type = RPM_STRING_ARRAY_TYPE;
    *((char ***) data) = list;

    *freeData = 0;

    return 0; 
}

static int instprefixTag(Header h, int_32 * type, void ** data, int_32 * count,
		      int * freeData) {
    char ** array;

    if (headerGetEntry(h, RPMTAG_INSTALLPREFIX, type, data, count)) {
	*freeData = 0;
	return 0;
    } else if (headerGetEntry(h, RPMTAG_INSTPREFIXES, NULL, (void **) &array, 
			      count)) {
	*((char **) data) = strdup(array[0]);
	*freeData = 1;
	*type = RPM_STRING_TYPE;
	free(array);
	return 0;
    } 

    return 1;
}

static int fssizesTag(Header h, int_32 * type, void ** data, int_32 * count,
		      int * freeData) {
    char ** filenames;
    int_32 * filesizes;
    uint_32 * usages;
    int numFiles;

    headerGetEntry(h, RPMTAG_FILENAMES, NULL, (void **) &filenames, NULL);
    headerGetEntry(h, RPMTAG_FILESIZES, NULL, (void **) &filesizes, &numFiles);

    if (rpmGetFilesystemList(NULL, count)) {
	return 1;
    }

    if (rpmGetFilesystemUsage(filenames, filesizes, numFiles, &usages, 0))	
	return 1;

    *type = RPM_INT32_TYPE;
    *freeData = 1;
    *data = usages;

    return 0;
}

static int triggercondsTag(Header h, int_32 * type, void ** data, 
			   int_32 * count, int * freeData) {
    int_32 * indices, * flags;
    char ** names, ** versions;
    int numNames, numScripts;
    char ** conds, ** s;
    char * item, * flagsStr;
    char * chptr;
    int i, j;
    char buf[5];

    if (!headerGetEntry(h, RPMTAG_TRIGGERNAME, NULL, (void **) &names, 
			&numNames)) {
	*freeData = 0;
	return 0;
    }

    headerGetEntry(h, RPMTAG_TRIGGERINDEX, NULL, (void **) &indices, NULL);
    headerGetEntry(h, RPMTAG_TRIGGERFLAGS, NULL, (void **) &flags, NULL);
    headerGetEntry(h, RPMTAG_TRIGGERVERSION, NULL, (void **) &versions, NULL);
    headerGetEntry(h, RPMTAG_TRIGGERSCRIPTS, NULL, (void **) &s, &numScripts);
    free(s);

    *freeData = 1;
    *data = conds = malloc(sizeof(char * ) * numScripts);
    *count = numScripts;
    *type = RPM_STRING_ARRAY_TYPE;
    for (i = 0; i < numScripts; i++) {
	chptr = malloc(1);
	*chptr = '\0';

	for (j = 0; j < numNames; j++) {
	    if (indices[j] != i) continue;

	    item = malloc(strlen(names[j]) + strlen(versions[j]) + 20);
	    if (flags[j] & RPMSENSE_SENSEMASK) {
		buf[0] = '%', buf[1] = '\0';
		flagsStr = depflagsFormat(RPM_INT32_TYPE, flags, buf,
					  0, j);
		sprintf(item, "%s %s %s", names[j], flagsStr, versions[j]);
		free(flagsStr);
	    } else {
		strcpy(item, names[j]);
	    }

	    chptr = realloc(chptr, strlen(chptr) + strlen(item) + 5);
	    if (*chptr) strcat(chptr, ", ");
	    strcat(chptr, item);
	    free(item);
	}

	conds[i] = chptr;
    }

    free(names);
    free(versions);

    return 0;
}

static int triggertypeTag(Header h, int_32 * type, void ** data, 
			   int_32 * count, int * freeData) {
    int_32 * indices, * flags;
    char ** conds, ** s;
    int i, j;
    char buf[5];
    int numScripts, numNames;

    if (!headerGetEntry(h, RPMTAG_TRIGGERINDEX, NULL, (void **) &indices, 
			&numNames)) {
	*freeData = 0;
	return 1;
    }

    headerGetEntry(h, RPMTAG_TRIGGERFLAGS, NULL, (void **) &flags, NULL);

    headerGetEntry(h, RPMTAG_TRIGGERSCRIPTS, NULL, (void **) &s, &numScripts);
    free(s);

    *freeData = 1;
    *data = conds = malloc(sizeof(char * ) * numScripts);
    *count = numScripts;
    *type = RPM_STRING_ARRAY_TYPE;
    for (i = 0; i < numScripts; i++) {
	for (j = 0; j < numNames; j++) {
	    if (indices[j] != i) continue;

	    if (flags[j] & RPMSENSE_TRIGGERIN)
		conds[i] = strdup("in");
	    else if (flags[j] & RPMSENSE_TRIGGERUN)
		conds[i] = strdup("un");
	    else
		conds[i] = strdup("postun");
	    break;
	}
    }

    return 0;
}
