Here's my updated patch. I think this attempt is better than the first,
if you still find something annoying i will fix/change it.
This patch add some command line options to cp:
-b make backup if file exists
-n no clobber
-v be verbose
-u overwrite if source is newer than destintation file
I also tried to add "-i" (interactive), but i still need to figure out
how to ask for just one keypress (y/n) without pressing return...
Everything for that is prepared, in usage-text -i is commented out.
Hope you like this,
Nils
diff --git a/elkscmd/file_utils/cp.c b/elkscmd/file_utils/cp.c
index 6e23864..b881af0 100644
--- a/elkscmd/file_utils/cp.c
+++ b/elkscmd/file_utils/cp.c
@@ -20,11 +20,25 @@
#include <grp.h>
#include <utime.h>
#include <errno.h>
+#include <stdint.h>
#define BUF_SIZE 4096
static char *buf;
+/* macros for flag setting/clearing/testing */
+#define ISFLAG(a) ((flags & a) == a)
+#define SETFLAG(a) (flags |= a)
+#define CLRFLAG(a) (flags &= ~(a))
+
+/* some flags for command-line-options */
+static uint16_t flags;
+#define OPT_NOCLOBBER 0x0001
+#define OPT_VERBOSE 0x0002
+#define OPT_INTERACTIVE 0x0004
+#define OPT_BACKUP 0x0008
+#define OPT_BACKUPMADE 0x0010
+#define OPT_UPDATE 0x0020
/*
* Build a path name from the specified directory name and file name.
@@ -67,6 +81,9 @@ int isadir(char *name)
* and modes. Returns 1 if successful, or 0 on a failure with an
* error message output. (Failure is not indicated if the attributes cannot
* be set.)
+ *
+ * Returns -1 if OPT_NOCLOBBER is set and a file is skipped if existent.
+ * This "skipped-file-indicator" is not used atm.
*/
int copyfile(char *srcname, char *destname, int setmodes)
{
@@ -78,15 +95,38 @@ int copyfile(char *srcname, char *destname, int setmodes)
struct stat statbuf1;
struct stat statbuf2;
struct utimbuf times;
+ static char backupname[PATHLEN];
+ /* source nonexistent */
if (stat(srcname, &statbuf1) < 0) {
perror(srcname);
return 0;
}
+ /* destination nonexistent */
if (stat(destname, &statbuf2) < 0) {
statbuf2.st_ino = -1;
statbuf2.st_dev = -1;
+ } else { /* destination existent */
+ if (ISFLAG(OPT_BACKUP)) {
+ strcpy(backupname, destname);
+ strcat(backupname, "~");
+ rename(destname, backupname);
+ SETFLAG(OPT_BACKUPMADE);
+ } else if (ISFLAG(OPT_UPDATE)) {
+ /* if dest is newer than source, don't overwrite it */
+ if (statbuf1.st_mtime < statbuf2.st_mtime) return -1;
+ } else if (ISFLAG(OPT_NOCLOBBER)) {
+ return -1;
+ } else if (ISFLAG(OPT_INTERACTIVE)) {
+ /* TODO fix this, getchar wants return */
+ /*
+ * fprintf(stderr, "really overwrite '%s'? (y/n) \n", destname);
+ if(getchar() != 'y') {
+ return -1;
+ }
+ */
+ }
}
if ((statbuf1.st_dev == statbuf2.st_dev) &&
@@ -143,9 +183,14 @@ int copyfile(char *srcname, char *destname, int setmodes)
(void) utime(destname, ×);
}
+ if (ISFLAG(OPT_BACKUPMADE) && ISFLAG(OPT_VERBOSE)) {
+ fprintf(stderr, "`%s' -> `%s' (backup: `%s')\n", srcname, destname, backupname);
+ CLRFLAG(OPT_BACKUPMADE);
+ }
+ else if (ISFLAG(OPT_VERBOSE))
+ fprintf(stderr, "`%s' -> `%s'\n", srcname, destname);
return 1;
-
error_exit:
close(rfd);
close(wfd);
@@ -157,37 +202,76 @@ error_exit:
int main(int argc, char **argv)
{
int dirflag;
+ int arg_cnt;
+ int opt_cnt;
+ int retval;
+ unsigned int file_count;
char *srcname;
char *destname;
char *lastarg;
if (argc < 3) goto usage;
+ /* lastarg is copy destination */
lastarg = argv[argc - 1];
dirflag = isadir(lastarg);
- if ((argc > 3) && !dirflag) {
- fprintf(stderr, "%s: not a directory\n", lastarg);
+ flags = 0;
+ opt_cnt = 0;
+
+ arg_cnt = 1; /* start at argv[1] */
+ while(arg_cnt < argc) {
+ if (!strcmp(argv[arg_cnt], "-n")) {
+ SETFLAG(OPT_NOCLOBBER);
+ } else if (!strcmp(argv[arg_cnt], "-v")) {
+ SETFLAG(OPT_VERBOSE);
+ } else if (!strcmp(argv[arg_cnt], "-i")) {
+ SETFLAG(OPT_INTERACTIVE);
+ } else if (!strcmp(argv[arg_cnt], "-u")) {
+ SETFLAG(OPT_UPDATE);
+ } else if (!strcmp(argv[arg_cnt], "-b")) {
+ SETFLAG(OPT_BACKUP);
+ } else if (!strcmp(argv[arg_cnt], "-h")) goto usage;
+ else break;
+ opt_cnt++;
+ arg_cnt++;
+ }
+ opt_cnt++;
+
+ /* if multiple files are specified, last one must be a directory */
+ if ((argc > (opt_cnt + 2)) && !dirflag) {
+ fprintf(stderr, "%s is not a directory\n", lastarg);
exit(1);
}
buf = malloc(BUF_SIZE);
- while (argc-- > 2) {
- srcname = argv[1];
+ file_count = 0;
+ while (argc-- > (opt_cnt + 1)) {
+ srcname = argv[opt_cnt+file_count];
destname = lastarg;
if (dirflag) destname = buildname(destname, srcname);
- if (!copyfile(*++argv, destname, 0)) goto error_copy;
+ retval = copyfile(srcname, destname, 0);
+ if (!retval) goto error_copy;
+ file_count++;
}
free(buf);
+
exit(0);
error_copy:
fprintf(stderr, "Failed to copy %s -> %s\n", srcname, destname);
exit(1);
usage:
- fprintf(stderr, "usage: %s source_file dest_file\n", argv[0]);
- fprintf(stderr, " %s file1 file2 ... dest_directory\n", argv[0]);
+ fprintf(stderr, "usage: %s [options] source_file dest_file\n", argv[0]);
+ fprintf(stderr, " %s [options] file1 file2 ... dest_directory\n", argv[0]);
+ fprintf(stderr, "options: -b make a backup of each exisiting destination file\n");
+ fprintf(stderr, " -b overrides -u (update)\n");
+ fprintf(stderr, " -n no overwriting, no clobber\n");
+ fprintf(stderr, " -v be verbose\n");
+/* fprintf(stderr, " -i interactive, prompt before overwrite\n");
+ fprintf(stderr, " -b, -u and -n override interactive mode\n");*/
+ fprintf(stderr, " -u copy only if source file is newer than destination file\n");
exit(1);
}