root/src/bin/settype.cpp
/*
 * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
 * Distributed under the terms of the MIT License.
 */

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

#include <AppFileInfo.h>
#include <Mime.h>

static const char *kCommandName = "settype";

static int kArgc;
static const char *const *kArgv;

// usage
const char *kUsage =
"Usage: %s <options> <file> ...\n"
"\n"
"Sets the MIME type, signature, and/or preferred application of one or more\n"
"files.\n"
"\n"
"Options:\n"
"  -h, --help\n"
"         - Print this help text and exit.\n"
"  -preferredAppSig <signature>\n"
"         - Set the preferred application of the given files to\n"
"           <signature>.\n"
"  -s <signature>\n"
"         - Set the application signature of the given files to\n"
"           <signature>.\n"
"  -t <type>\n"
"         - Set the MIME type of the given files to <type>.\n"
;

// print_usage
static void
print_usage(bool error)
{
        // get command name
        const char *commandName = NULL;
        if (kArgc > 0) {
                if (const char *lastSlash = strchr(kArgv[0], '/'))
                        commandName = lastSlash + 1;
                else
                        commandName = kArgv[0];
        }

        if (!commandName || strlen(commandName) == 0)
                commandName = kCommandName;

        // print usage
        fprintf((error ? stderr : stdout), kUsage, commandName, commandName,
                commandName);
}

// print_usage_and_exit
static void
print_usage_and_exit(bool error)
{
        print_usage(error);
        exit(error ? 1 : 0);
}

// next_arg
static const char *
next_arg(int &argi, bool optional = false)
{
        if (argi >= kArgc) {
                if (!optional)
                        print_usage_and_exit(true);
                return NULL;
        }

        return kArgv[argi++];
}

// check_mime_type
static void
check_mime_type(const char *type)
{
        // check type
        if (type) {
                if (!BMimeType::IsValid(type)) {
                        fprintf(stderr, "\"%s\" is no valid MIME type.\n", type);

                        exit(1);
                }
        }
}

// main
int
main(int argc, const char *const *argv)
{
        kArgc = argc;
        kArgv = argv;

        // parameters
        const char **files = new const char*[argc];
        int fileCount = 0;
        const char *type = NULL;
        const char *signature = NULL;
        const char *preferredApp = NULL;

        // parse the arguments
        for (int argi = 1; argi < argc; ) {
                const char *arg = argv[argi++];
                if (arg[0] == '-') {
                        if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
                                print_usage_and_exit(false);

                        } else if (strcmp(arg, "-preferredAppSig") == 0) {
                                preferredApp = next_arg(argi);

                        } else if (strcmp(arg, "-s") == 0) {
                                signature = next_arg(argi);

                        } else if (strcmp(arg, "-t") == 0) {
                                type = next_arg(argi);

                        } else {
                                fprintf(stderr, "Error: Invalid option: \"%s\"\n", arg);
                                print_usage_and_exit(true);
                        }
                } else {
                        // file
                        files[fileCount++] = arg;
                }
        }

        // check parameters
        if (!preferredApp && !signature && !type) {
                fprintf(stderr, "Error: At least one option of \"-preferredAppSig\", "
                        "\"-s\", and \"-t\" must be given.\n");
                print_usage_and_exit(true);
        }

        if (fileCount == 0) {
                fprintf(stderr, "Error: No file specified.\n");
                print_usage_and_exit(true);
        }

        // check for valid MIME types
        check_mime_type(preferredApp);
        check_mime_type(type);
        check_mime_type(signature);

        // iterate through the files
        for (int i = 0; i < fileCount; i++) {
                const char *fileName = files[i];

                // check, whether the file exists
                BEntry entry;
                status_t error = entry.SetTo(fileName, false);
                if (error != B_OK) {
                        fprintf(stderr, "Error: Can't access file \"%s\": %s\n",
                                fileName, strerror(error));

                        exit(1);
                }

                if (!entry.Exists()) {
                        fprintf(stderr, "Error: \"%s\": No such file or directory.\n",
                                fileName);

                        exit(1);
                }

                // ... and has the right type
                if (signature && !entry.IsFile()) {
                        fprintf(stderr, "Error: \"%s\" is not a file. Signatures can only "
                                "be set for executable files.\n", fileName);

                        exit(1);
                }

                // open the file
                BFile file;
                BNode _node;
                BNode &node = (signature ? file : _node);
                error = (signature ? file.SetTo(fileName, B_READ_WRITE)
                        : node.SetTo(fileName));
                if (error != B_OK) {
                        fprintf(stderr, "Error: Failed to open file \"%s\": %s\n",
                                fileName, strerror(error));

                        exit(1);
                }

                // prepare an node/app info object
                BAppFileInfo appInfo;
                BNodeInfo _nodeInfo;
                BNodeInfo &nodeInfo = (signature ? appInfo : _nodeInfo);
                error = (signature ? appInfo.SetTo(&file) : nodeInfo.SetTo(&node));
                if (error != B_OK) {
                        fprintf(stderr, "Error: Failed to open file \"%s\": %s\n",
                                fileName, strerror(error));

                        exit(1);
                }

                // set preferred app
                if (preferredApp) {
                        error = nodeInfo.SetPreferredApp(preferredApp);
                        if (error != B_OK) {
                                fprintf(stderr, "Error: Failed to set the preferred "
                                        "application of file \"%s\": %s\n", fileName,
                                        strerror(error));

                                exit(1);
                        }
                }

                // set type
                if (type) {
                        error = nodeInfo.SetType(type);
                        if (error != B_OK) {
                                fprintf(stderr, "Error: Failed to set the MIME type of file "
                                        "\"%s\": %s\n", fileName, strerror(error));

                                exit(1);
                        }
                }

                // set signature
                if (signature) {
                        error = appInfo.SetSignature(signature);
                        if (error != B_OK) {
                                fprintf(stderr, "Error: Failed to set the signature of file "
                                        "\"%s\": %s\n", fileName, strerror(error));

                                exit(1);
                        }
                }
        }

        delete[] files;

        return 0;
}