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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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, &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 +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);
 }

[Index of Archives]     [Kernel]     [Linux ia64]     [DCCP]     [Linux for ARM]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux