root/src/bin/package/command_add.cpp
/*
 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de>
 * Distributed under the terms of the MIT License.
 */


#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <Entry.h>

#include <package/PackageInfo.h>
#include <package/hpkg/HPKGDefs.h>
#include <package/hpkg/PackageWriter.h>

#include "package.h"
#include "PackageWriterListener.h"
#include "PackageWritingUtils.h"


using namespace BPackageKit::BHPKG;


int
command_add(int argc, const char* const* argv)
{
        const char* changeToDirectory = NULL;
        const char* packageInfoFileName = NULL;
        bool quiet = false;
        bool verbose = false;
        bool force = false;
        int32 compressionLevel = BPackageKit::BHPKG::B_HPKG_COMPRESSION_LEVEL_BEST;

        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, "+0123456789C:fhi:qv",
                        sLongOptions, NULL);
                if (c == -1)
                        break;

                switch (c) {
                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9':
                                compressionLevel = c - '0';
                                break;

                        case 'C':
                                changeToDirectory = optarg;
                                break;

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

                        case 'f':
                                force = true;
                                break;

                        case 'i':
                                packageInfoFileName = optarg;
                                break;

                        case 'q':
                                quiet = true;
                                break;

                        case 'v':
                                verbose = true;
                                break;

                        default:
                                print_usage_and_exit(true);
                                break;
                }
        }

        // The remaining arguments are the package file and the entries to add.
        if (optind >= argc)
                print_usage_and_exit(true);

        const char* packageFileName = argv[optind++];

        // entries must be specified, if a .PackageInfo hasn't been specified via
        // an option
        if (optind >= argc && packageInfoFileName == NULL)
                print_usage_and_exit(true);

        const char* const* entriesToAdd = argv + optind;
        int entriesToAddCount = argc - optind;

        // create package
        BPackageWriterParameters writerParameters;
        writerParameters.SetFlags(
                B_HPKG_WRITER_UPDATE_PACKAGE | (force ? B_HPKG_WRITER_FORCE_ADD : 0));
        writerParameters.SetCompressionLevel(compressionLevel);
        if (compressionLevel == 0) {
                writerParameters.SetCompression(
                        BPackageKit::BHPKG::B_HPKG_COMPRESSION_NONE);
        }

        PackageWriterListener listener(verbose, quiet);
        BPackageWriter packageWriter(&listener);
        status_t result = packageWriter.Init(packageFileName, &writerParameters);
        if (result != B_OK)
                return 1;

        // If a package info file has been specified explicitly, open it.
        int packageInfoFD = -1;
        if (packageInfoFileName != NULL) {
                packageInfoFD = open(packageInfoFileName, O_RDONLY);
                if (packageInfoFD < 0) {
                        fprintf(stderr, "Error: Failed to open package info file \"%s\": "
                                "%s\n", packageInfoFileName, strerror(errno));
                }
        }

        // 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));
                }
        }

        // add the entries
        for (int i = 0; i < entriesToAddCount; i++) {
                const char* entry = entriesToAdd[i];

                if (strcmp(entry, ".") == 0) {
                        // add all entries in the current directory; skip .PackageInfo,
                        // if a different file was specified
                        if (add_current_directory_entries(packageWriter,
                                        listener, packageInfoFileName != NULL) != B_OK)
                                return 1;
                } else {
                        // skip .PackageInfo, if a different file was specified
                        if (packageInfoFileName != NULL
                                && strcmp(entry, B_HPKG_PACKAGE_INFO_FILE_NAME) == 0) {
                                continue;
                        }

                        if (packageWriter.AddEntry(entry) != B_OK)
                                return 1;
                }
        }

        // add the .PackageInfo, if explicitly specified
        if (packageInfoFileName != NULL) {
                result = packageWriter.AddEntry(B_HPKG_PACKAGE_INFO_FILE_NAME,
                        packageInfoFD);
                if (result != B_OK)
                        return 1;
        }

        // write the package
        result = packageWriter.Finish();
        if (result != B_OK)
                return 1;

        if (verbose)
                printf("\nsuccessfully created package '%s'\n", packageFileName);

        return 0;
}