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

extern int run_argv (char **argv);

const char top_dir[] = "/mit/linux/packages/";
const char *current_dir = "current";
const char spec_dir[] = "/SPECS";
const char specslash_dir[] = "/SPECS/";
const char rpm_dir[] = "/RPMS";
const char mc_file[] = "package-lists";
const char first_line[] = "VERSION ";
const char newpackage_str[] = "%newpackage";
const char athena_prefix[] = "athena-";
const char srvd_prefix[] = "srvd-athena-";
const char spec_suffix[] = ".spec";
const char name_tag[] = "Name: ";
const char version_tag[] = "Version: ";
const char release_tag[] = "Release: ";
const char headers[] =
	"Copyright: MIT\n"
	"Distribution: MIT SIPB Linux-Athena\n"
	"Vendor: The MIT Student Information Processing Board\n"
	"Packager: linux-dev@mit.edu\n"
	"Group: Athena\n";
const char buildroot_tag[] = "Buildroot: ";
const char athena_buildroot[] = "/srvd";
const char srvd_buildroot[] = "/tmp/buildroot";
const char provides_tag[] = "Provides: ";
const char conflicts_tag[] = "Conflicts: ";

char buf[1024];

struct package_info {
    char *name;
    char *filename;
    char *version;
    char **data;
    int release;
};

#define TRUE 1
#define FALSE 0

static int build_filename (char *buf, char *name, int srvd)
{
    const char *prefix;

    prefix = (srvd ? srvd_prefix : athena_prefix);
    strcpy (buf, top_dir);
    strcat (buf, current_dir);
    strcat (buf, specslash_dir);
    strcat (buf, prefix);
    strcat (buf, name);
    strcat (buf, spec_suffix);

    return 0;
}

static char *xgets (char *cp, int len, FILE *fp)
{
    if (!fgets (cp, len, fp))
	return (NULL);
    len = strlen (cp);
    if (cp[len-1] == '\n')
	cp[len-1] = 0;
    return (cp);
}

static FILE *get_srvd_version (char *version)
{
    FILE *fp;
    int x;

    strcpy (buf, top_dir);
    strcat (buf, mc_file);
    fp = fopen (buf, "r");
    if (!fp) {
	perror (buf);
	return (NULL);
    }
    x = strlen (first_line);
    if ((!xgets (buf, sizeof (buf), fp)) ||
	(strncmp (buf, first_line, x) != 0)) {
	fputs ("No version number.\n", stderr);
	return (NULL);
    }
    strcpy (version, buf+x);
    return (fp);
}

static int find_newpackage (FILE *fp)
{
    while (xgets (buf, sizeof (buf), fp) != NULL)
	if (!strcmp (newpackage_str, buf))
	    return (TRUE);
    return (FALSE);
}

static char **get_package_data (FILE *fp)
{
    char **data;
    int alloced, size;

    size = 0;
    alloced = 100;
    data = malloc (sizeof (char *) * alloced);
    while (xgets (buf, sizeof (buf), fp) != NULL) {
	if (!strcmp (newpackage_str, buf))
	    break;
	if (size+1 >= alloced) {
	    alloced += 100;
	    data = realloc (data, sizeof (char *) * alloced);
	}
	data[size] = strdup (buf);
	size++;
    }
    data[size] = NULL;
    return (data);
}

static void get_package_info (struct package_info *pip)
{
    char version[80];
    FILE *fp;
    char *cp;

    pip->version = NULL;
    pip->data = NULL;
    fp = get_srvd_version (version);
    if (!fp)
	return;
    while (find_newpackage (fp)) {
	if (!xgets (buf, sizeof (buf), fp))
	    break;
	cp = strchr (buf, ' ');
	if (cp != NULL) {
	    *cp = 0;
	    cp++;
	}
	if (!strcmp (pip->name, buf)) {
	    pip->version = strdup (cp ? cp : version);
	    pip->data = get_package_data (fp);
	    break;
	}
    }
    fclose (fp);
    if (!pip->version)
	fprintf (stderr, "%s: not found.\n", pip->name);
}

static int get_package_release (char *package)
{
    FILE *fp;
    int release, x;

    release = -1;
    build_filename (buf, package, FALSE);
    fp = fopen (buf, "r");
    if (!fp) {
	perror (buf);
	return (release);
    }
    x = strlen (release_tag);
    while (xgets (buf, sizeof (buf), fp) != NULL)
	if (!strncmp (buf, release_tag, x)) {
	    release = atoi (buf+x);
	    break;
	}
    fclose (fp);
    return (release);
}

static int create_specfile (struct package_info *pip, int srvd)
{
    FILE *fp;
    char **cpp;
    const char *prefix;

    prefix = (srvd ? srvd_prefix : athena_prefix);
    build_filename (buf, pip->filename, srvd);
    fp = fopen (buf, "w");
    if (!fp) {
	perror (buf);
	return (-1);
    }
    fprintf (fp, "%s%s%s\n%s%s\n%s%d\n", name_tag, prefix, pip->name,
	     version_tag, pip->version, release_tag, pip->release);
    if (srvd)
	fprintf (fp, "%s%s%s\n", provides_tag, athena_prefix, pip->name);
    else
	fprintf (fp, "%s%s%s\n", conflicts_tag, srvd_prefix, pip->name);
    fputs (headers, fp);
    fprintf (fp, "%s%s\n", buildroot_tag,
	     (srvd ? srvd_buildroot : athena_buildroot));
    if (pip->data)
	for (cpp = pip->data; *cpp; cpp++) {
	    fputs (*cpp, fp);
	    fputc ('\n', fp);
	}
    fclose (fp);
    return (0);
}

static int build_rpm (char *package, int srvd)
{
    const char *prefix;
    char *argv[8], *rpmrc, *macros;
    FILE *fp;

    /*
     *  Create the rpmrc file on the fly.
     */
    macros = strdup(tmpnam (NULL));
    fp = fopen (macros, "w");
    if (!fp) {
	perror (macros);
	return (-1);
    }
    strcpy (buf, top_dir);
    strcat (buf, current_dir);
    fprintf (fp, "%%_specdir %s%s\n%%_rpmdir %s%s\n%%_target_cpu i386",
	     buf, spec_dir, buf, rpm_dir);
    fclose (fp);
    rpmrc = tmpnam (NULL);
    fp = fopen (rpmrc, "w");
    if (!fp) {
	perror (rpmrc);
	return (-1);
    }
    fprintf (fp, "macrofiles:	/usr/lib/rpm/macros:"
	     "/usr/lib/rpm/i686-linux/macros:/etc/rpm/macros.specspo:"
	     "/etc/rpm/macros:/etc/rpm/i686-linux/macros:~/.rpmmacros:%s",
	     macros);
    fclose(fp);
    /*
     *  Create the rpm command line.
     */
    strcat (buf, specslash_dir);
    prefix = (srvd ? srvd_prefix : athena_prefix);
    strcat (buf, prefix);
    strcat (buf, package);
    strcat (buf, spec_suffix);
    argv[0] = "/bin/rpm";
    argv[1] = "-bb";
    argv[2] = buf;
    argv[3] = "--rcfile";
    argv[4] = rpmrc;
    argv[5] = "--target";
    argv[6] = "i386";
    argv[7] = NULL;
    return (run_argv (argv));
}

static int run_diff (char *file1, char *file2)
{
  char *argv[5];
  argv[0] = "/usr/bin/cmp";
  argv[1] = file1;
  argv[2] = file2;
  argv[3] = "-s";
  argv[4] = NULL;

  return (run_argv (argv));
}

static void usage (void)
{
    fprintf (stderr,
	     "Usage: makepackage [ -s ] [ -v ] [ current ] package\n");
    exit (0);
}

int main (int argc, char *argv[])
{
    int srvd, status, verify;
    char *package;
    struct package_info info;
    int x;

    srvd = verify = FALSE;
    package = NULL;
    for (x = 1; x < argc; x++) {
	if (!strcmp (argv[x], "-s")) {
	    srvd = TRUE;
	    continue;
	}
	if (!strcmp (argv[x], "-c")) {
	    x++;
	    if (x >= argc)
		usage ();
	    current_dir = argv[x];
	    continue;
	}
	if (!strcmp (argv[x], "-v")) {
	    verify = TRUE;
	    continue;
	}
	if (argv[x][0] == '-')
	    usage ();
	if (package != NULL)
	    usage ();
	package = argv[x];
    }
    if (package == NULL)
	usage ();
    info.filename = info.name = package;
    get_package_info (&info);
    if (!info.version)
	return (-1);
    status = get_package_release (package);
    if (verify) {
        char buf2[1024];
        char *name = malloc (strlen(package)+5);

        if (status < 0)
	    status = 1;
	info.release = status;
	sprintf (name, "%s.tst", package);

	info.filename = name;
	status = create_specfile (&info, FALSE);
	if (status != 0)
	    return (status);

	build_filename (buf, package, FALSE);
	build_filename (buf2, name, FALSE);

	status = run_diff (buf, buf2);
	unlink (buf2);
	return (status);
    }
    info.release = (status < 0 ? 1 : status+1);
    status = create_specfile (&info, FALSE);
    if (status != 0)
	return (status);
    status = build_rpm (package, FALSE);
    if (status != 0)
	return (status);
    if (srvd) {
	status = create_specfile (&info, TRUE);
	if (status != 0)
	    return (status);
	status = build_rpm (package, TRUE);
	if (status != 0)
	    return (status);
    }
    return (status);
}
