/*
 * Tru64 package gateway for the ESP Package Manager (EPM)
 *
 * Copyright © 2020 by Jim Jagielski
 * Copyright 2001-2017 by Michael R Sweet
 * Copyright 2001-2010 by Easy Software Products
 * Copyright 2001 by Aneesh Kumar (aneesh.kumar@digital.com) at Digital India.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Include necessary headers...
 */

#include "epm.h"

/*
 * 'make_setld()' - Make a Tru64 setld package.
 */

int                                  /* O - 0 = success, 1 = fail */
make_setld(const char *prodname,     /* I - Product short name */
           const char *directory,    /* I - Directory for distribution files */
           const char *platname,     /* I - Platform name */
           dist_t *dist,             /* I - Distribution information */
           struct utsname *platform) /* I - Platform information */
{
    int i, j;              /* Looping vars */
    FILE *fp;              /* Spec file */
    tarf_t *tarfile;       /* .tardist file */
    char name[1024];       /* Full product name */
    char scpname[1024];    /* XXXVVV control program filename */
    char miname[1024];     /* XXXVVV.mi filename */
    char keyname[1024];    /* XXXVVV.key filename */
    char filename[1024];   /* Destination filename */
    char subset[1024];     /* Subset name */
    file_t *file;          /* Current distribution file */
    command_t *c;          /* Current command */
    char current[1024];    /* Current directory */
    struct passwd *pwd;    /* Pointer to user record */
    struct group *grp;     /* Pointer to group record */
    const char *runlevels; /* Run levels */

    REF(platform);

    /*
     * Check the package information to make sure it complies with the
     * extremely limited Tru64 packager requirements.
     */

    if (dist->vernumber < 100 || dist->vernumber > 999) {
        fprintf(stderr,
                "epm: Need a version number between 100 and 999 inclusive.\n"
                "     The current version number (%d) is out of range.\n",
                dist->vernumber);
        return (1);
    }

    if (strlen(prodname) < 3) {
        fprintf(stderr,
                "epm: Need a product name of at least 3 uppercase characters.\n"
                "     The current product name (%s) is not acceptable.\n",
                prodname);
        return (1);
    }

    for (i = 0; prodname[i]; i++)
        if (!isupper(prodname[i] & 255)) {
            fprintf(stderr,
                    "epm: Need a product name of at least 3 uppercase characters.\n"
                    "     The current product name (%s) is not acceptable.\n",
                    prodname);
            return (1);
        }

    for (i = 0; i < dist->num_subpackages; i++) {
        if ((strlen(dist->subpackages[i]) + strlen(prodname)) > 77) {
            fprintf(stderr,
                    "epm: Product + subpackage names must be less than 77 characters.\n"
                    "     The current subpackage name (%s) is not acceptable.\n",
                    dist->subpackages[i]);
            return (1);
        }

        for (j = 0; dist->subpackages[i][j]; j++)
            if (!isupper(dist->subpackages[i][j] & 255)) {
                fprintf(stderr,
                        "epm: Subpackage names may only contain uppercase letters.\n"
                        "     The current subpackage name (%s) is not acceptable.\n",
                        dist->subpackages[i]);
                return (1);
            }
    }

    /*
     * Prepare for packaging...
     */

    if (dist->release[0]) {
        if (platname[0])
            snprintf(name, sizeof(name), "%s-%s-%s-%s", prodname, dist->version,
                     dist->release, platname);
        else
            snprintf(name, sizeof(name), "%s-%s-%s", prodname, dist->version,
                     dist->release);
    } else if (platname[0])
        snprintf(name, sizeof(name), "%s-%s-%s", prodname, dist->version, platname);
    else
        snprintf(name, sizeof(name), "%s-%s", prodname, dist->version);

    getcwd(current, sizeof(current));

    /*
     * Add symlinks for init scripts...
     */

    for (i = 0; i < dist->num_files; i++)
        if (tolower(dist->files[i].type) == 'i') {
            /*
             * Make symlinks for all of the selected run levels...
             */

            for (runlevels = get_runlevels(dist->files + i, "023");
                 isdigit(*runlevels & 255); runlevels++) {
                file = add_file(dist, dist->files[i].subpackage);
                file->type = 'l';
                file->mode = 0;
                strlcpy(file->user, "root", sizeof(file->user));
                strlcpy(file->group, "sys", sizeof(file->group));
                snprintf(file->src, sizeof(file->src), "../init.d/%s",
                         dist->files[i].dst);

                if (*runlevels == '0')
                    snprintf(file->dst, sizeof(file->dst), "/sbin/rc0.d/K%02d%s",
                             get_stop(dist->files + i, 0), dist->files[i].dst);
                else
                    snprintf(file->dst, sizeof(file->dst), "/sbin/rc%c.d/S%02d%s",
                             *runlevels, get_start(dist->files + i, 99),
                             dist->files[i].dst);
            }

            /*
             * Then send the original file to /sbin/init.d...
             */

            file = dist->files + i;

            snprintf(filename, sizeof(filename), "/sbin/init.d/%s", file->dst);
            strlcpy(file->dst, filename, sizeof(file->dst));
        }

    /*
     * Build package directories...
     *
     * Note: The src/scps directory and the key, inventory, and
     *       subset control programs below should actually go in
     *       a data subdirectory according to the Tru64
     *       documentation, but (at least under 4.0d) the invcutter
     *       program will not be able to find the distribution files
     *       if they are not colocated with these data files.
     */

    if (Verbosity)
        puts("Creating Tru64 (setld) distribution...");

    snprintf(filename, sizeof(filename), "%s/output", directory);
    unlink_directory(filename);

    snprintf(filename, sizeof(filename), "%s/src", directory);
    unlink_directory(filename);

    snprintf(filename, sizeof(filename), "%s/output", directory);
    mkdir(filename, 0777);

    snprintf(filename, sizeof(filename), "%s/src", directory);
    mkdir(filename, 0777);

    snprintf(filename, sizeof(filename), "%s/src/scps", directory);
    mkdir(filename, 0777);

    /*
     * Write the subset control program...
     */

    if (Verbosity)
        puts("Creating subset control program...");

    snprintf(scpname, sizeof(scpname), "%s/src/scps/%sALL%03d.scp", directory, prodname,
             dist->vernumber);

    if ((fp = fopen(scpname, "w")) == NULL) {
        fprintf(stderr, "epm: Unable to create subset control program \"%s\": %s\n",
                scpname, strerror(errno));
        return (1);
    }

    fputs("#!/bin/sh\n", fp);
    fputs("case $ACT in\n", fp);

    fputs("PRE_L)\n", fp);
    for (i = dist->num_files, file = dist->files; i > 0; i--, file++)
        if (tolower(file->type) == 'i') {
            qprintf(fp, "if test -x %s; then\n", file->dst);
            qprintf(fp, "	%s stop\n", file->dst);
            fputs("fi\n", fp);
        }
    for (i = dist->num_commands, c = dist->commands; i > 0; i--, c++)
        if (c->type == COMMAND_PRE_INSTALL)
            fprintf(fp, "%s\n", c->command);
    fputs(";;\n", fp);

    fputs("POST_L)\n", fp);
    for (i = dist->num_commands, c = dist->commands; i > 0; i--, c++)
        if (c->type == COMMAND_POST_INSTALL)
            fprintf(fp, "%s\n", c->command);
    for (i = dist->num_files, file = dist->files; i > 0; i--, file++)
        if (tolower(file->type) == 'c') {
            qprintf(fp, "if test ! -f %s; then\n", file->dst);
            qprintf(fp, "	/bin/cp %s.N %s\n", file->dst, file->dst);
            fputs("fi\n", fp);
        }
    for (i = dist->num_files, file = dist->files; i > 0; i--, file++)
        if (tolower(file->type) == 'i')
            qprintf(fp, "%s start\n", file->dst);
    fputs(";;\n", fp);

    fputs("PRE_D)\n", fp);
    for (i = dist->num_files, file = dist->files; i > 0; i--, file++)
        if (tolower(file->type) == 'i')
            qprintf(fp, "%s stop\n", file->dst);
    for (i = dist->num_commands, c = dist->commands; i > 0; i--, c++)
        if (c->type == COMMAND_PRE_REMOVE)
            fprintf(fp, "%s\n", c->command);
    for (i = dist->num_files, file = dist->files; i > 0; i--, file++)
        if (tolower(file->type) == 'c') {
            qprintf(fp, "if cmp -s %s.N %s; then\n", file->dst, file->dst);
            qprintf(fp, "	/bin/rm -f %s\n", file->dst);
            fputs("fi\n", fp);
        }
    fputs(";;\n", fp);

    fputs("POST_D)\n", fp);
    for (i = dist->num_commands, c = dist->commands; i > 0; i--, c++)
        if (c->type == COMMAND_POST_REMOVE)
            fprintf(fp, "%s\n", c->command);
    fputs(";;\n", fp);

    fputs("esac\n", fp);

    fclose(fp);

    /*
     * Sort the file list by the destination name, since kits need a sorted
     * list...
     */

    sort_dist_files(dist);

    /*
     * Now do the inventory file...
     */

    if (Verbosity)
        puts("Creating master inventory file...");

    snprintf(miname, sizeof(miname), "%s/src/%s%03d.mi", directory, prodname,
             dist->vernumber);

    if ((fp = fopen(miname, "w")) == NULL) {
        fprintf(stderr, "epm: Unable to create master inventory file \"%s\": %s\n",
                miname, strerror(errno));
        return (1);
    }

    for (i = dist->num_files, file = dist->files; i > 0; i--, file++) {
        snprintf(subset, sizeof(subset), "%s%s%03d", prodname,
                 file->subpackage ? file->subpackage : "ALL", dist->vernumber);

        switch (tolower(file->type)) {
        case 'c':
            fprintf(fp, "2\t.%s.N\t%s\n", file->dst, subset);
            break;
        case 'd':
        case 'i':
        case 'f':
        case 'l':
            fprintf(fp, "0\t.%s\t%s\n", file->dst, subset);
            break;
        }
    }

    fclose(fp);

    /*
     * Create the key file...
     */

    if (Verbosity)
        puts("Creating key file...");

    snprintf(keyname, sizeof(keyname), "%s/src/%s%03d.k", directory, prodname,
             dist->vernumber);

    if ((fp = fopen(keyname, "w")) == NULL) {
        fprintf(stderr, "epm: Unable to create key file \"%s\": %s\n", keyname,
                strerror(errno));
        return (1);
    }

    qprintf(fp, "NAME='%s, %s'\n", dist->product, dist->version);
    fprintf(fp, "CODE=%s\n", prodname);
    fprintf(fp, "VER=%03d\n", dist->vernumber);
    fprintf(fp, "MI=%s%03d.mi\n", prodname, dist->vernumber);
    fputs("COMPRESS=0\n", fp);
    fputs("%%\n", fp);
    qprintf(fp, "%sALL%03d\t.\t0\t'%s, %s'\n", prodname, dist->vernumber, dist->product,
            dist->version);
    for (i = 0; i < dist->num_subpackages; i++)
        qprintf(fp, "%s%s%03d\t.\t0\t'%s, %s'\n", prodname, dist->subpackages[i],
                dist->vernumber, dist->product, dist->version);
    fclose(fp);

    /*
     * Copy the files over...
     */

    if (Verbosity)
        puts("Copying temporary distribution files...");

    for (i = dist->num_files, file = dist->files; i > 0; i--, file++) {
        /*
         * Find the username and groupname IDs...
         */

        pwd = getpwnam(file->user);
        grp = getgrnam(file->group);

        endpwent();
        endgrent();

        /*
         * Copy the file or make the directory or make the symlink as needed...
         */

        switch (tolower(file->type)) {
        case 'c':
        case 'f':
        case 'i':
            snprintf(filename, sizeof(filename), "%s/src%s", directory, file->dst);

            if (Verbosity > 1)
                printf("%s -> %s...\n", file->src, filename);

            if (copy_file(filename, file->src, file->mode, pwd ? pwd->pw_uid : 0,
                          grp ? grp->gr_gid : 0))
                return (1);
            break;

        case 'd':
            snprintf(filename, sizeof(filename), "%s/src%s", directory, file->dst);

            if (Verbosity > 1)
                printf("Directory %s...\n", filename);

            make_directory(filename, file->mode, pwd ? pwd->pw_uid : 0,
                           grp ? grp->gr_gid : 0);
            break;

        case 'l':
            snprintf(filename, sizeof(filename), "%s/src%s", directory, file->dst);

            if (Verbosity > 1)
                printf("%s -> %s...\n", file->src, filename);

            make_link(filename, file->src);
            break;
        }
    }

    /*
     * Build the distribution from the inventory and control files...
     */

    if (Verbosity)
        puts("Building Tru64 (setld) distribution...");

    snprintf(filename, sizeof(filename), "%s/src", directory);
    chdir(filename);

    if (run_command(NULL, "kits %s%03d.k . ../output", prodname, dist->vernumber))
        return (1);

    chdir(current);

    /*
     * Tar and compress the distribution...
     */

    if (Verbosity)
        puts("Creating tar.gz file for distribution...");

    snprintf(filename, sizeof(filename), "%s/%s.tar.gz", directory, name);

    if ((tarfile = tar_open(filename, 1)) == NULL)
        return (1);

    snprintf(filename, sizeof(filename), "%s/output", directory);

    if (tar_directory(tarfile, filename, prodname)) {
        tar_close(tarfile);
        return (1);
    }

    tar_close(tarfile);

    /*
     * Remove temporary files...
     */

    if (!KeepFiles) {
        if (Verbosity)
            puts("Removing temporary distribution files...");

        snprintf(filename, sizeof(filename), "%s/output", directory);
        unlink_directory(filename);

        snprintf(filename, sizeof(filename), "%s/src", directory);
        unlink_directory(filename);
    }

    return (0);
}
