root/src/bin/package_repo/command_create.cpp
/*
 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
 * Distributed under the terms of the MIT License.
 */


#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <Entry.h>
#include <Path.h>

#include <package/hpkg/HPKGDefs.h>
#include <package/hpkg/RepositoryWriter.h>
#include <package/PackageInfo.h>
#include <package/RepositoryInfo.h>

#include "package_repo.h"


using BPackageKit::BHPKG::BRepositoryWriterListener;
using BPackageKit::BHPKG::BRepositoryWriter;
using namespace BPackageKit;


class RepositoryWriterListener  : public BRepositoryWriterListener {
public:
        RepositoryWriterListener(bool verbose, bool quiet)
                : fVerbose(verbose), fQuiet(quiet)
        {
        }

        virtual void PrintErrorVarArgs(const char* format, va_list args)
        {
                vfprintf(stderr, format, args);
        }

        virtual void OnPackageAdded(const BPackageInfo& packageInfo)
        {
                if (fQuiet)
                        return;

                printf("%s (%s)\n", packageInfo.Name().String(),
                        packageInfo.Version().ToString().String());
                if (fVerbose) {
                        printf("\tsummary:  %s\n", packageInfo.Summary().String());
                        printf("\tvendor:   %s\n", packageInfo.Vendor().String());
                        printf("\tpackager: %s\n", packageInfo.Packager().String());
                        printf("\tchecksum: %s\n", packageInfo.Checksum().String());
                        if (uint32 flags = packageInfo.Flags()) {
                                printf("\tflags:\n");
                                if ((flags & B_PACKAGE_FLAG_APPROVE_LICENSE) != 0)
                                        printf("\t\tapprove_license\n");
                                if ((flags & B_PACKAGE_FLAG_SYSTEM_PACKAGE) != 0)
                                        printf("\t\tsystem_package\n");
                        }
                } else
                        printf("\tchecksum: %s\n", packageInfo.Checksum().String());
        }

        virtual void OnRepositoryInfoSectionDone(uint32 uncompressedSize)
        {
                if (fQuiet || !fVerbose)
                        return;

                printf("----- Repository Info Section --------------------\n");
                printf("repository info size:    %10" B_PRIu32 " (uncompressed)\n",
                        uncompressedSize);
        }

        virtual void OnPackageAttributesSectionDone(uint32 stringCount,
                uint32 uncompressedSize)
        {
                if (fQuiet || !fVerbose)
                        return;

                printf("----- Package Attribute Section -------------------\n");
                printf("string count:            %10" B_PRIu32 "\n", stringCount);
                printf("package attributes size: %10" B_PRIu32 " (uncompressed)\n",
                        uncompressedSize);
        }

        virtual void OnRepositoryDone(uint32 headerSize, uint32 repositoryInfoSize,
                uint32 licenseCount, uint32 packageCount, uint32 packageAttributesSize,
                uint64 totalSize)
        {
                if (fQuiet)
                        return;

                printf("----- Package Repository Info -----\n");
                if (fVerbose)
                        printf("embedded license count   %10" B_PRIu32 "\n", licenseCount);
                printf("package count            %10" B_PRIu32 "\n", packageCount);
                printf("-----------------------------------\n");
                printf("header size:             %10" B_PRIu32 "\n", headerSize);
                printf("repository header size:  %10" B_PRIu32 "\n",
                        repositoryInfoSize);
                printf("package attributes size: %10" B_PRIu32 "\n",
                        packageAttributesSize);
                printf("total size:              %10" B_PRIu64 "\n", totalSize);
                printf("-----------------------------------\n");
        }

private:
        bool fVerbose;
        bool fQuiet;
};


int
command_create(int argc, const char* const* argv)
{
        const char* changeToDirectory = NULL;
        bool quiet = false;
        bool verbose = false;

        while (true) {
                static struct option sLongOptions[] = {
                        { "help", no_argument, 0, 'h' },
                        { "quiet", no_argument, 0, 'q' },
                        { "verbose", no_argument, 0, 'v' },
                        { 0, 0, 0, 0 }
                };

                opterr = 0; // don't print errors
                int c = getopt_long(argc, (char**)argv, "+C:hqv", sLongOptions, NULL);
                if (c == -1)
                        break;

                switch (c) {
                        case 'C':
                                changeToDirectory = optarg;
                                break;

                        case 'h':
                                print_usage_and_exit(false);
                                break;

                        case 'q':
                                quiet = true;
                                break;

                        case 'v':
                                verbose = true;
                                break;

                        default:
                                print_usage_and_exit(true);
                                break;
                }
        }

        // The remaining arguments are the repository info file plus one or more
        // package files, i.e. at least two more arguments.
        if (optind + 2 > argc)
                print_usage_and_exit(true);

        const char* repositoryInfoFileName = argv[optind++];
        const char* const* packageFileNames = argv + optind;

        RepositoryWriterListener listener(verbose, quiet);

        BEntry repositoryInfoEntry(repositoryInfoFileName);
        if (!repositoryInfoEntry.Exists()) {
                listener.PrintError(
                        "Error: given repository-info file '%s' doesn't exist!\n",
                        repositoryInfoFileName);
                return 1;
        }

        // determine path for 'repo' file from given info file
        BEntry repositoryParentEntry;
        repositoryInfoEntry.GetParent(&repositoryParentEntry);
        BPath repositoryPath;
        if (repositoryParentEntry.GetPath(&repositoryPath) != B_OK) {
                listener.PrintError(
                        "Error: can't determine path of given repository-info file!\n");
                return 1;
        }
        repositoryPath.Append("repo");

        // create repository
        BRepositoryInfo repositoryInfo(repositoryInfoEntry);
        status_t result = repositoryInfo.InitCheck();
        if (result != B_OK) {
                listener.PrintError(
                        "Error: can't parse given repository-info file : %s\n",
                        strerror(result));
                return 1;
        }
        BRepositoryWriter repositoryWriter(&listener, &repositoryInfo);
        if ((result = repositoryWriter.Init(repositoryPath.Path())) != B_OK) {
                listener.PrintError("Error: can't initialize repository-writer : %s\n",
                        strerror(result));
                return 1;
        }

        // change directory, if requested
        if (changeToDirectory != NULL) {
                if (chdir(changeToDirectory) != 0) {
                        listener.PrintError(
                                "Error: Failed to change the current working directory to "
                                "\"%s\": %s\n", changeToDirectory, strerror(errno));
                        return 1;
                }
        }

        // add all given package files
        for (int i = 0; i < argc - optind; ++i) {
                if (verbose)
                        printf("reading package '%s' ...\n", packageFileNames[i]);
                BEntry entry(packageFileNames[i]);
                if (!entry.Exists()) {
                        printf("package '%s' does not exist\n", packageFileNames[i]);
                        return 1;
                }
                result = repositoryWriter.AddPackage(entry);
                if (result != B_OK)
                        return 1;
        }

        // write the repository
        result = repositoryWriter.Finish();
        if (result != B_OK)
                return 1;

        if (verbose) {
                printf("\nsuccessfully created repository '%s'\n",
                        repositoryPath.Path());
        }

        return 0;
}