Re: [PATCH] elkscmd/file_utils/cp.c: add options -b, -n, -v, -u and updated usage

Nils Stec <[email protected]> Wed, 01 Apr 2015 13:41:04 +0200
Newsgroups org.kernel.vger.linux-8086
Message-ID <[email protected]>
On 04/01/2015 01:32 PM, Jody Bruchon wrote:
> On 4/1/2015 7:22 AM, Nils Stec wrote:
> + * Returns -1 if OPT_NOCLOBBER is set and a file is skipped if existent.
> ...
> +        } else if (ISFLAG(OPT_NOCLOBBER)) {
> +            return -1;
> ...
> +        retval = copyfile(srcname, destname, 0);
> +        if (!retval) goto error_copy;
>
> This causes an error to be returned if someone asks for no clobbering.
> Doing what is asked for by the user should never be an error condition
> and should return success unless a true error occurred (i.e. write
> errors). Here's example output when using the -n switch with GNU 
> coreutils 'cp' and testing for any error conditions:
>
> $ ls -l foo bar
> bar:
> total 0
> -rw-r--r-- 1 user user 0 Apr  1 07:31 1
> -rw-r--r-- 1 user user 0 Apr  1 07:31 2
>
> foo:
> total 12
> -rw-r--r-- 1 user user 4 Apr  1 07:31 1
> -rw-r--r-- 1 user user 4 Apr  1 07:31 2
> -rw-r--r-- 1 user user 4 Apr  1 07:31 3
> $ cp -n foo/* bar/ || echo error
> $ cp -n foo/* bar/ && echo success
> success
> $ ls -l foo bar
> bar:
> total 4
> -rw-r--r-- 1 user user 0 Apr  1 07:31 1
> -rw-r--r-- 1 user user 0 Apr  1 07:31 2
> -rw-r--r-- 1 user user 4 Apr  1 07:32 3
>
> foo:
> total 12
> -rw-r--r-- 1 user user 4 Apr  1 07:31 1
> -rw-r--r-- 1 user user 4 Apr  1 07:31 2
> -rw-r--r-- 1 user user 4 Apr  1 07:31 3
> $
>
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-8086" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
patch-cp-add_options_004.diff (text/x-patch, 4.7 KB)
diff --git a/elkscmd/file_utils/cp.c b/elkscmd/file_utils/cp.c
index 6e23864..2e413e9 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.
@@ -78,15 +92,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 +180,14 @@ int copyfile(char *srcname, char *destname, int setmodes)
 		(void) utime(destname, &times);
 	}
 
+	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 +199,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);
 }