/***********
 * $Id: cmd_cp.c,v 1.8 2001/05/30 15:40:00 harbourn Exp $
 * cp command for fatback
 ***********/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include "getopt.h"
#include "interface_data.h"
#include "interface.h"
#include "dirtree.h"
#include "recovery.h"
#include "output.h"

static struct option long_opts[] =
{
     {"deleted", 0, 0, 'd'},
     {"recursive", 0, 0, 'R'},
     {NULL, 0, 0, 0}
};

static char *usage = "Usage: cp -[dR] [files] [dir]\n   or: cp -[dR] [from file] [to file]\n";

static int contains_deleted(dirent_t *);

void cmd_cp(int argc, char *argv[])
{
     char *output_fname = NULL, *output;
     int bpc, to_dir;
     int opt, opt_index;
     int only_deleted = 0, recursive = 0;
     entlist_t *list, *ent, *ptr;

     if (argc < 3) {
          display(NORMAL, usage);
          return;
     }
     /* clear out getopt's globals, their might be stuff left over 
      * from the main() invocation of getopt_long. 
      */
     optarg = NULL;
     optind = 0;
     opterr = 0;
     optopt = 0;

     /* parse the command line options */
     while ((opt = getopt_long(argc, argv, "dR", long_opts, &opt_index)) > 0) {
          switch (opt) {
          case 'd':
               only_deleted = 1;
               break;
          case 'R':
               recursive = 1;
               break;
          default:
               break;
          }
     }
     if ((argc - optind) < 2) {
          display(NORMAL, usage);
          return;
     }
     output = argv[argc - 1];

     if (output[0] == '~')
          output = replace_tilde(output);

     to_dir = (stat_is_dir(output) > 0) ? 1 : 0;
     
     /* if their are more than one file being copied, and the 
      * givin output specifier is a file, warn the user. */
     if (!to_dir && !recursive) {
          display(NORMAL, "Error: last argument must be a directory\n");
          return;
     } else if (!to_dir && recursive) {
          char *new_output = unused_fname(output);
          if (!make_dir(new_output)) {
               free(new_output);
               return;
          }
          output = new_output;
          to_dir = 1;
     }

     if (!(list = find_files(argc - optind - 1, &argv[optind])))
          return;
     bpc = vbr->bytes_per_sect * vbr->sects_per_clust;
     /* now loop over all the files and write them out */
     for (ent = list; ent; ent = ent->next) {
          char *fname;
          if (ent->ent->lfn && (strlen(ent->ent->lfn) < NAME_MAX))
               fname = ent->ent->lfn;
          else
               fname = ent->ent->filename;
          if ((ent->ent->attrs & ATTR_DIR) && 
              (!only_deleted || contains_deleted(ent->ent))) {
               if (recursive) { 
                    /* recurse down the tree */
                    dirent_t *tmp = cwd;
                    char *myargv, *new_dirname = fn_cat(output, fname);
                    cwd = ent->ent;
                    myargv = argv[argc - 1];
                    argv[argc - 1] = new_dirname;
                    cmd_cp(argc, argv);
                    free(argv[argc - 1]);
                    argv[argc - 1] = myargv;
                    cwd = tmp;
               }
          }
          if (to_dir)
               output_fname = fn_cat(output, fname);
          else
               output_fname = output;
          if (!(only_deleted) || (ent->ent->flags & ENT_DELETED))
               extract_file(ent->ent,
                            clusts, 
                            bpc,
                            ent->ent->size,
                            ent->ent->cluster,
                            vbr->fat_entries,
                            output_fname);
          if (to_dir)
               free(output_fname);
     }
     /* now free all the elements of the list of entries */
     ptr = list;
     while (ptr) {
          entlist_t *tmp;
          tmp = ptr->next;
          free(ptr);
          ptr = tmp;
     }
}

/*
 * Recursively determine if a given directory 
 * contains any deleted files or is deleted itself.
 */
static int contains_deleted(dirent_t *dir)
{
     dirent_t *ent;

     if (dir->flags & ENT_DELETED)
          return 1;
     for (ent = dir->child; ent; ent = ent->next)
          if (ent->flags & ENT_DELETED)
               return 1;
     return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1