Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/bdsh/cmds/modules/cp/cp.c

    r6348376 re367f5b8  
    3030#include <stdlib.h>
    3131#include <unistd.h>
     32#include <io/console.h>
     33#include <io/keycode.h>
    3234#include <getopt.h>
    3335#include <str.h>
    3436#include <fcntl.h>
     37#include <sys/stat.h>
     38#include <dirent.h>
    3539#include "config.h"
    3640#include "util.h"
     
    4448
    4549static const char *cmdname = "cp";
     50static console_ctrl_t *con;
    4651
    4752static struct option const long_options[] = {
    4853        { "buffer", required_argument, 0, 'b' },
    4954        { "force", no_argument, 0, 'f' },
     55        { "interactive", no_argument, 0, 'i'},
    5056        { "recursive", no_argument, 0, 'r' },
    5157        { "help", no_argument, 0, 'h' },
     
    5561};
    5662
     63typedef enum {
     64        TYPE_NONE,
     65        TYPE_FILE,
     66        TYPE_DIR
     67} dentry_type_t;
     68
     69static int64_t copy_file(const char *src, const char *dest,
     70    size_t blen, int vb);
     71
     72/** Get the type of a directory entry.
     73 *
     74 * @param path  Path of the directory entry.
     75 *
     76 * @return TYPE_DIR if the dentry is a directory.
     77 * @return TYPE_FILE if the dentry is a file.
     78 * @return TYPE_NONE if the dentry does not exists.
     79 */
     80static dentry_type_t get_type(const char *path)
     81{
     82        struct stat s;
     83
     84        int r = stat(path, &s);
     85
     86        if (r)
     87                return TYPE_NONE;
     88        else if (s.is_directory)
     89                return TYPE_DIR;
     90        else if (s.is_file)
     91                return TYPE_FILE;
     92
     93        return TYPE_NONE;
     94}
     95
    5796static int strtoint(const char *s1)
    5897{
     
    66105
    67106        return (int) t1;
     107}
     108
     109/** Get the last component of a path.
     110 *
     111 * e.g. /data/a  ---> a
     112 *
     113 * @param path  Pointer to the path.
     114 *
     115 * @return      Pointer to the last component or to the path itself.
     116 */
     117static char *get_last_path_component(char *path)
     118{
     119        char *ptr;
     120
     121        ptr = str_rchr(path, '/');
     122        if (!ptr)
     123                return path;
     124        else
     125                return ptr + 1;
     126}
     127
     128/** Merge two paths together.
     129 *
     130 * e.g. (path1 = /data/dir, path2 = a/b) --> /data/dir/a/b
     131 *
     132 * @param path1         Path to which path2 will be appended.
     133 * @param path1_size    Size of the path1 buffer.
     134 * @param path2         Path that will be appended to path1.
     135 */
     136static void merge_paths(char *path1, size_t path1_size, char *path2)
     137{
     138        const char *delim = "/";
     139
     140        str_rtrim(path1, '/');
     141        str_append(path1, path1_size, delim);
     142        str_append(path1, path1_size, path2);
     143}
     144
     145static bool get_user_decision(bool bdefault, const char *message, ...)
     146{
     147        va_list args;
     148
     149        va_start(args, message);
     150        vprintf(message, args);
     151        va_end(args);
     152
     153        while (true) {
     154                kbd_event_t ev;
     155                console_flush(con);
     156                console_get_kbd_event(con, &ev);
     157                if ((ev.type != KEY_PRESS)
     158                    || (ev.mods & (KM_CTRL | KM_ALT)) != 0) {
     159                        continue;
     160                }
     161
     162                switch(ev.key) {
     163                case KC_Y:
     164                        printf("y\n");
     165                        return true;
     166                case KC_N:
     167                        printf("n\n");
     168                        return false;
     169                case KC_ENTER:
     170                        printf("%c\n", bdefault ? 'Y' : 'N');
     171                        return bdefault;
     172                default:
     173                        break;
     174                }
     175        }
     176}
     177
     178static int64_t do_copy(const char *src, const char *dest,
     179    size_t blen, int vb, int recursive, int force, int interactive)
     180{
     181        int r = -1;
     182        char dest_path[PATH_MAX];
     183        char src_path[PATH_MAX];
     184        DIR *dir = NULL;
     185        struct dirent *dp;
     186
     187        dentry_type_t src_type = get_type(src);
     188        dentry_type_t dest_type = get_type(dest);
     189
     190        const size_t src_len = str_size(src);
     191
     192        if (src_type == TYPE_FILE) {
     193                char *src_fname;
     194
     195                /* Initialize the src_path with the src argument */
     196                str_cpy(src_path, src_len + 1, src);
     197                str_rtrim(src_path, '/');
     198               
     199                /* Get the last component name from the src path */
     200                src_fname = get_last_path_component(src_path);
     201               
     202                /* Initialize dest_path with the dest argument */
     203                str_cpy(dest_path, PATH_MAX, dest);
     204
     205                if (dest_type == TYPE_DIR) {
     206                        /* e.g. cp file_name /data */
     207                        /* e.g. cp file_name /data/ */
     208                       
     209                        /* dest is a directory,
     210                         * append the src filename to it.
     211                         */
     212                        merge_paths(dest_path, PATH_MAX, src_fname);
     213                        dest_type = get_type(dest_path);
     214                } else if (dest_type == TYPE_NONE) {
     215                        if (dest_path[str_size(dest_path) - 1] == '/') {
     216                                /* e.g. cp /textdemo /data/dirnotexists/ */
     217
     218                                printf("The dest directory %s does not exists",
     219                                    dest_path);
     220                                goto exit;
     221                        }
     222                }
     223
     224                if (dest_type == TYPE_DIR) {
     225                        printf("Cannot overwrite existing directory %s\n",
     226                            dest_path);
     227                        goto exit;
     228                } else if (dest_type == TYPE_FILE) {
     229                        /* e.g. cp file_name existing_file */
     230
     231                        /* dest already exists,
     232                         * if force is set we will try to remove it.
     233                         * if interactive is set user input is required.
     234                         */
     235                        if (force && !interactive) {
     236                                if (unlink(dest_path)) {
     237                                        printf("Unable to remove %s\n",
     238                                            dest_path);
     239                                        goto exit;
     240                                }
     241                        } else if (!force && interactive) {
     242                                bool overwrite = get_user_decision(false,
     243                                    "File already exists: %s. Overwrite? [y/N]: ",
     244                                    dest_path);
     245                                if (overwrite) {
     246                                        printf("Overwriting file: %s\n", dest_path);
     247                                        if (unlink(dest_path)) {
     248                                                printf("Unable to remove %s\n", dest_path);
     249                                                goto exit;
     250                                        }
     251                                } else {
     252                                        printf("Not overwriting file: %s\n", dest_path);
     253                                        r = 0;
     254                                        goto exit;
     255                                }
     256                        } else {
     257                                printf("File already exists: %s\n", dest_path);
     258                                goto exit;
     259                        }
     260                }
     261
     262                /* call copy_file and exit */
     263                r = (copy_file(src, dest_path, blen, vb) < 0);
     264
     265        } else if (src_type == TYPE_DIR) {
     266                /* e.g. cp -r /x/srcdir /y/destdir/ */
     267
     268                if (!recursive) {
     269                        printf("Cannot copy the %s directory without the "
     270                            "-r option\n", src);
     271                        goto exit;
     272                } else if (dest_type == TYPE_FILE) {
     273                        printf("Cannot overwrite a file with a directory\n");
     274                        goto exit;
     275                }
     276
     277                char *src_dirname;
     278
     279                /* Initialize src_path with the content of src */
     280                str_cpy(src_path, src_len + 1, src);
     281                str_rtrim(src_path, '/');
     282
     283                src_dirname = get_last_path_component(src_path);
     284
     285                str_cpy(dest_path, PATH_MAX, dest);
     286
     287                switch (dest_type) {
     288                case TYPE_DIR:
     289                        if (str_cmp(src_dirname, "..") &&
     290                            str_cmp(src_dirname, ".")) {
     291                                /* The last component of src_path is
     292                                 * not '.' or '..'
     293                                 */
     294                                merge_paths(dest_path, PATH_MAX, src_dirname);
     295
     296                                if (mkdir(dest_path, 0) == -1) {
     297                                        printf("Unable to create "
     298                                            "dest directory %s\n", dest_path);
     299                                        goto exit;
     300                                }
     301                        }
     302                        break;
     303                default:
     304                case TYPE_NONE:
     305                        /* dest does not exists, this means the user wants
     306                         * to specify the name of the destination directory
     307                         *
     308                         * e.g. cp -r /src /data/new_dir_src
     309                         */
     310                        if (mkdir(dest_path, 0)) {
     311                                printf("Unable to create "
     312                                    "dest directory %s\n", dest_path);
     313                                goto exit;
     314                        }
     315                        break;
     316                }
     317
     318                dir = opendir(src);
     319                if (!dir) {
     320                        /* Something strange is happening... */
     321                        printf("Unable to open src %s directory\n", src);
     322                        goto exit;
     323                }
     324
     325                /* Copy every single directory entry of src into the
     326                 * destination directory.
     327                 */
     328                while ((dp = readdir(dir))) {
     329                        struct stat src_s;
     330                        struct stat dest_s;
     331
     332                        char src_dent[PATH_MAX];
     333                        char dest_dent[PATH_MAX];
     334
     335                        str_cpy(src_dent, PATH_MAX, src);
     336                        merge_paths(src_dent, PATH_MAX, dp->d_name);
     337
     338                        str_cpy(dest_dent, PATH_MAX, dest_path);
     339                        merge_paths(dest_dent, PATH_MAX, dp->d_name);
     340
     341                        /* Check if we are copying a directory into itself */
     342                        stat(src_dent, &src_s);
     343                        stat(dest_path, &dest_s);
     344
     345                        if (dest_s.index == src_s.index &&
     346                            dest_s.fs_handle == src_s.fs_handle) {
     347                                printf("Cannot copy a directory "
     348                                    "into itself\n");
     349                                goto exit;
     350                        }
     351
     352                        if (vb)
     353                                printf("copy %s %s\n", src_dent, dest_dent);
     354
     355                        /* Recursively call do_copy() */
     356                        r = do_copy(src_dent, dest_dent, blen, vb, recursive,
     357                            force, interactive);
     358                        if (r)
     359                                goto exit;
     360
     361                }
     362        } else
     363                printf("Unable to open source file %s\n", src);
     364
     365exit:
     366        if (dir)
     367                closedir(dir);
     368        return r;
    68369}
    69370
     
    127428        static char helpfmt[] =
    128429            "Usage:  %s [options] <source> <dest>\n"
    129             "Options: (* indicates not yet implemented)\n"
     430            "Options:\n"
    130431            "  -h, --help       A short option summary\n"
    131432            "  -v, --version    Print version information and exit\n"
    132             "* -V, --verbose    Be annoyingly noisy about what's being done\n"
    133             "* -f, --force      Do not complain when <dest> exists\n"
    134             "* -r, --recursive  Copy entire directories\n"
    135             "  -b, --buffer ## Set the read buffer size to ##\n"
    136             "Currently, %s is under development, some options may not work.\n";
     433            "  -V, --verbose    Be annoyingly noisy about what's being done\n"
     434            "  -f, --force      Do not complain when <dest> exists (overrides a previous -i)\n"
     435            "  -i, --interactive Ask what to do when <dest> exists (overrides a previous -f)\n"
     436            "  -r, --recursive  Copy entire directories\n"
     437            "  -b, --buffer ## Set the read buffer size to ##\n";
    137438        if (level == HELP_SHORT) {
    138439                printf("`%s' copies files and directories\n", cmdname);
    139440        } else {
    140441                help_cmd_cp(HELP_SHORT);
    141                 printf(helpfmt, cmdname, cmdname);
     442                printf(helpfmt, cmdname);
    142443        }
    143444
     
    148449{
    149450        unsigned int argc, verbose = 0;
    150         int buffer = 0;
     451        int buffer = 0, recursive = 0;
     452        int force = 0, interactive = 0;
    151453        int c, opt_ind;
    152454        int64_t ret;
    153455
     456        con = console_init(stdin, stdout);
    154457        argc = cli_count_args(argv);
    155458
    156459        for (c = 0, optind = 0, opt_ind = 0; c != -1;) {
    157                 c = getopt_long(argc, argv, "hvVfrb:", long_options, &opt_ind);
     460                c = getopt_long(argc, argv, "hvVfirb:", long_options, &opt_ind);
    158461                switch (c) {
    159462                case 'h':
     
    167470                        break;
    168471                case 'f':
     472                        interactive = 0;
     473                        force = 1;
     474                        break;
     475                case 'i':
     476                        force = 0;
     477                        interactive = 1;
    169478                        break;
    170479                case 'r':
     480                        recursive = 1;
    171481                        break;
    172482                case 'b':
     
    175485                                    "(should be a number greater than zero)\n",
    176486                                    cmdname);
     487                                console_done(con);
    177488                                return CMD_FAILURE;
    178489                        }
     
    191502                printf("%s: invalid number of arguments. Try %s --help\n",
    192503                    cmdname, cmdname);
     504                console_done(con);
    193505                return CMD_FAILURE;
    194506        }
    195507
    196         ret = copy_file(argv[optind], argv[optind + 1], buffer, verbose);
    197 
    198         if (verbose)
    199                 printf("%" PRId64 " bytes copied\n", ret);
    200 
    201         if (ret >= 0)
     508        ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose,
     509            recursive, force, interactive);
     510
     511        console_done(con);
     512
     513        if (ret == 0)
    202514                return CMD_SUCCESS;
    203515        else
Note: See TracChangeset for help on using the changeset viewer.