HelenOS sources

root/uspace/app/bdsh/cmds/modules/mkdir/mkdir.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. help_cmd_mkdir
  2. create_directory
  3. cmd_mkdir

/*
 * Copyright (c) 2008 Tim Post
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <dirent.h>
#include <getopt.h>
#include <stdarg.h>
#include <str.h>
#include <errno.h>
#include <str_error.h>
#include <vfs/vfs.h>

#include "config.h"
#include "errors.h"
#include "util.h"
#include "entry.h"
#include "mkdir.h"
#include "cmds.h"

#define MKDIR_VERSION "0.0.1"

static const char *cmdname = "mkdir";

static struct option const long_options[] = {
        { "parents", no_argument, 0, 'p' },
        { "verbose", no_argument, 0, 'v' },
        { "mode", required_argument, 0, 'm' },
        { "help", no_argument, 0, 'h' },
        { "version", no_argument, 0, 'V' },
        { "follow", no_argument, 0, 'f' },
        { 0, 0, 0, 0 }
};

void help_cmd_mkdir(unsigned int level)
{
        if (level == HELP_SHORT) {
                printf("`%s' creates a new directory\n", cmdname);
        } else {
                help_cmd_mkdir(HELP_SHORT);
                printf(
                    "Usage:  %s [options] <path>\n"
                    "Options:\n"
                    "  -h, --help       A short option summary\n"
                    "  -V, --version    Print version information and exit\n"
                    "  -p, --parents    Create needed parents for <path>\n"
                    "  -m, --mode       Set permissions to [mode] (UNUSED)\n"
                    "  -v, --verbose    Be extremely noisy about what is happening\n"
                    "  -f, --follow     Go to the new directory once created\n"
                    "Currently, %s is under development, some options don't work.\n",
                    cmdname, cmdname);
        }

        return;
}

/* This is kind of clunky, but effective for now */
static unsigned int
create_directory(const char *user_path, bool create_parents)
{
        /* Ensure we would always work with absolute and canonified path. */
        char *path = vfs_absolutize(user_path, NULL);
        if (path == NULL) {
                cli_error(CL_ENOMEM, "%s: path too big?", cmdname);
                return 1;
        }

        int ret = 0;
        errno_t rc;

        if (!create_parents) {
                rc = vfs_link_path(path, KIND_DIRECTORY, NULL);
                if (rc != EOK) {
                        cli_error(CL_EFAIL, "%s: could not create %s (%s)",
                            cmdname, path, str_error(rc));
                        ret = 1;
                }
        } else {
                /* Create the parent directories as well. */
                size_t off = 0;
                while (true) {
                        size_t prev_off = off;
                        char32_t cur_char = str_decode(path, &off, STR_NO_LIMIT);
                        if ((cur_char == 0) || (cur_char == U_SPECIAL)) {
                                break;
                        }
                        if (cur_char != '/') {
                                continue;
                        }
                        if (prev_off == 0) {
                                continue;
                        }
                        /*
                         * If we are here, it means that:
                         * - we found /
                         * - it is not the first / (no need to create root
                         *   directory)
                         *
                         * We would now overwrite the / with 0 to terminate the
                         * string (that shall be okay because we are
                         * overwriting at the beginning of UTF sequence).
                         * That would allow us to create the directories
                         * in correct nesting order.
                         *
                         * Notice that we ignore EEXIST errors as some of
                         * the parent directories may already exist.
                         */
                        char slash_char = path[prev_off];
                        path[prev_off] = 0;

                        rc = vfs_link_path(path, KIND_DIRECTORY, NULL);
                        if (rc != EOK && rc != EEXIST) {
                                cli_error(CL_EFAIL, "%s: could not create %s (%s)",
                                    cmdname, path, str_error(rc));
                                ret = 1;
                                goto leave;
                        }

                        path[prev_off] = slash_char;
                }
                /* Create the final directory. */
                rc = vfs_link_path(path, KIND_DIRECTORY, NULL);
                if (rc != EOK) {
                        cli_error(CL_EFAIL, "%s: could not create %s (%s)",
                            cmdname, path, str_error(rc));
                        ret = 1;
                }
        }

leave:
        free(path);
        return ret;
}

int cmd_mkdir(char **argv)
{
        unsigned int argc, i, ret = 0;
        bool create_parents = false, follow = false, verbose = false;
        int c, opt_ind;

        argc = cli_count_args(argv);

        c = 0;
        optreset = 1;
        optind = 0;
        opt_ind = 0;

        while (c != -1) {
                c = getopt_long(argc, argv, "pvhVfm:", long_options, &opt_ind);
                switch (c) {
                case 'p':
                        create_parents = true;
                        break;
                case 'v':
                        verbose = true;
                        break;
                case 'h':
                        help_cmd_mkdir(HELP_LONG);
                        return CMD_SUCCESS;
                case 'V':
                        printf("%s\n", MKDIR_VERSION);
                        return CMD_SUCCESS;
                case 'f':
                        follow = true;
                        break;
                case 'm':
                        printf("%s: [W] Ignoring mode %s\n", cmdname, optarg);
                        break;
                }
        }

        argc -= optind;

        if (argc < 1) {
                printf("%s - incorrect number of arguments. Try `%s --help'\n",
                    cmdname, cmdname);
                return CMD_FAILURE;
        }

        for (i = optind; argv[i] != NULL; i++) {
                if (verbose)
                        printf("%s: creating %s%s\n",
                            cmdname, argv[i],
                            create_parents ? " (and all parents)" : "");
                ret += create_directory(argv[i], create_parents);
        }

        if (follow && (argv[optind] != NULL)) {
                if (vfs_cwd_set(argv[optind]) != EOK)
                        printf("%s: Error switching to directory.", cmdname);
        }

        if (ret)
                return CMD_FAILURE;
        else
                return CMD_SUCCESS;
}

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub