[BusyBox] new dd

Matt Kraai kraai at alumni.carnegiemellon.edu
Thu Dec 14 21:54:23 UTC 2000


Howdy,

I rewrote dd.

Advantages of the new version?

 * It checks the return values of all system calls.
 * You don't have to press ^D twice when it reads from stdin.
 * It doesn't print the summary to stdout if the file was printed
   to stdout.
 * It is anal about its arguments.
 * It fixes the suffixes of tail -n.
 * dd.o shrinks by 49 bytes.

Disadvantages?

 * utility.o grows by 49 bytes.
 * tail.o grows by 22 bytes.
 * It is 22 bytes bigger overall.
 * I wrote it.

The patch is attached.  Should I commit it (or send it down the
path of nl, tac, and rev)?

Matt
-------------- next part --------------
Index: dd.c
===================================================================
RCS file: /var/cvs/busybox/dd.c,v
retrieving revision 1.36
diff -u -r1.36 dd.c
--- dd.c	2000/12/07 19:56:48	1.36
+++ dd.c	2000/12/14 20:56:15
@@ -2,16 +2,9 @@
 /*
  * Mini dd implementation for busybox
  *
- * Copyright (C) 1999, 2000 by Lineo, inc.
- * Written by Erik Andersen <andersen at lineo.com>, <andersee at debian.org>
  *
- * Based in part on code taken from sash. 
- *   Copyright (c) 1999 by David I. Bell
- *   Permission is granted to use, distribute, or modify this source,
- *   provided that this copyright notice remains intact.
+ * Copyright (C) 2000 by Matt Kraai <kraai at alumni.carnegiemellon.edu>
  *
- * Permission to distribute this code under the GPL has been granted.
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -28,154 +21,139 @@
  *
  */
 
-
 #include "busybox.h"
-#include <features.h>
-#include <stdio.h>
+
+#include <sys/types.h>
 #include <fcntl.h>
-#include <errno.h>
-#if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1)
-#include <inttypes.h>
-#else
-typedef unsigned long long int uintmax_t;
-#endif
+
+static struct suffix_mult dd_suffixes[] = {
+	{ "c", 1 },
+	{ "w", 2 },
+	{ "b", 512 },
+	{ "kD", 1000 },
+	{ "k", 1024 },
+	{ "MD", 1000000 },
+	{ "M", 1048576 },
+	{ "GD", 1000000000 },
+	{ "G", 1073741824 },
+	{ NULL, 0 }
+};
 
-extern int dd_main(int argc, char **argv)
+ssize_t safe_read(int fd, void *buf, size_t count)
 {
-	char *inFile = NULL;
-	char *outFile = NULL;
-	int inFd;
-	int outFd;
-	int inCc = 0;
-	int outCc;
-	int trunc=TRUE;
-	int sync=FALSE;
-	long blockSize = 512,ibs;
-	uintmax_t skipBlocks = 0;
-	uintmax_t seekBlocks = 0;
-	uintmax_t count = (uintmax_t) - 1;
-	uintmax_t inTotal = 0;
-	uintmax_t outTotal = 0;
-	uintmax_t totalSize;
-
-	unsigned char buf[BUFSIZ];
-	char *keyword = NULL;
-
-	argc--;
-	argv++;
-
-	/* Parse any options */
-	while (argc) {
-		if (inFile == NULL && (strncmp(*argv, "if", 2) == 0))
-			inFile = ((strchr(*argv, '=')) + 1);
-		else if (outFile == NULL && (strncmp(*argv, "of", 2) == 0))
-			outFile = ((strchr(*argv, '=')) + 1);
-		else if (strncmp("count", *argv, 5) == 0) {
-			count = atoi_w_units((strchr(*argv, '=')) + 1);
-			if (count < 0) {
-				error_msg("Bad count value %s\n", *argv);
-				goto usage;
-			}
-		} else if (strncmp(*argv, "bs", 2) == 0) {
-			blockSize = atoi_w_units((strchr(*argv, '=')) + 1);
-			if (blockSize <= 0) {
-				error_msg("Bad block size value %s\n", *argv);
-				goto usage;
-			}
-		} else if (strncmp(*argv, "skip", 4) == 0) {
-			skipBlocks = atoi_w_units((strchr(*argv, '=')) + 1);
-			if (skipBlocks <= 0) {
-				error_msg("Bad skip value %s\n", *argv);
-				goto usage;
-			}
+	ssize_t n;
 
-		} else if (strncmp(*argv, "seek", 4) == 0) {
-			seekBlocks = atoi_w_units((strchr(*argv, '=')) + 1);
-			if (seekBlocks <= 0) {
-				error_msg("Bad seek value %s\n", *argv);
-				goto usage;
+	do {
+		n = read(fd, buf, count);
+	} while (n < 0 && errno == EINTR);
+
+	return n;
+}
+
+int dd_main(int argc, char **argv)
+{
+	int i, ifd, ofd, sync = FALSE, trunc = TRUE;
+	size_t in_full = 0, in_part = 0, out_full = 0, out_part = 0;
+	size_t bs = 512, count = -1;
+	ssize_t n;
+	off_t seek = 0, skip = 0;
+	FILE *statusfp;
+	char *infile = NULL, *outfile = NULL, *buf;
+
+	for (i = 1; i < argc; i++) {
+		if (strncmp("bs=", argv[i], 3) == 0)
+			bs = parse_number(argv[i]+3, dd_suffixes);
+		else if (strncmp("count=", argv[i], 6) == 0)
+			count = parse_number(argv[i]+6, dd_suffixes);
+		else if (strncmp("seek=", argv[i], 5) == 0)
+			seek = parse_number(argv[i]+5, dd_suffixes);
+		else if (strncmp("skip=", argv[i], 5) == 0)
+			skip = parse_number(argv[i]+5, dd_suffixes);
+		else if (strncmp("if=", argv[i], 3) == 0)
+			infile = argv[i]+3;
+		else if (strncmp("of=", argv[i], 3) == 0)
+			outfile = argv[i]+3;
+		else if (strncmp("conv=", argv[i], 5) == 0) {
+			buf = argv[i]+5;
+			while (1) {
+				if (strncmp("notrunc", buf, 7) == 0) {
+					trunc = FALSE;
+					buf += 7;
+				} else if (strncmp("sync", buf, 4) == 0) {
+					sync = TRUE;
+					buf += 4;
+				} else {
+					error_msg_and_die("invalid conversion `%s'", argv[i]+5);
+				}
+				if (buf[0] == '\0')
+					break;
+				if (buf[0] == ',')
+					buf++;
 			}
-		} else if (strncmp(*argv, "conv", 4) == 0) {
-			keyword = (strchr(*argv, '=') + 1);
-                	if (strcmp(keyword, "notrunc") == 0) 
-				trunc=FALSE;
-			if (strcmp(keyword, "sync") == 0) 
-				sync=TRUE;
-		} else {
-			goto usage;
-		}
-		argc--;
-		argv++;
+		} else
+			usage(dd_usage);
 	}
 
-	if (inFile == NULL)
-		inFd = fileno(stdin);
-	else
-		inFd = open(inFile, 0);
-
-	if (inFd < 0) {
-		/* Note that we are not freeing buf or closing
-		 * files here to save a few bytes. This exits
-		 * here anyways... */
-
-		/* free(buf); */
-		perror_msg_and_die("%s", inFile);
-	}
-
-	if (outFile == NULL)
-		outFd = fileno(stdout);
-	else
-		outFd = open(outFile, O_WRONLY | O_CREAT, 0666);
-
-	if (outFd < 0) {
-		/* Note that we are not freeing buf or closing
-		 * files here to save a few bytes. This exits
-		 * here anyways... */
-
-		/* close(inFd);
-		   free(buf); */
-		perror_msg_and_die("%s", outFile);
-	}
-
-	lseek(inFd, (off_t) (skipBlocks * blockSize), SEEK_SET);
-	lseek(outFd, (off_t) (seekBlocks * blockSize), SEEK_SET);
-	totalSize=count*blockSize;
-
-	ibs=blockSize;
-	if (ibs > BUFSIZ)
-		ibs=BUFSIZ;
-			
-	while (totalSize > outTotal) {
-		inCc = full_read(inFd, buf, ibs);
-		inTotal += inCc;
-		if ( (sync==TRUE) && (inCc>0) )
-			while (inCc<ibs)
-				buf[inCc++]='\0';
-
-		if ((outCc = full_write(outFd, buf, inCc)) < 1){
-			if (outCc < 0 ){
-				perror("Error during write");
-			}
+	buf = xmalloc(bs);
+
+	if (infile != NULL) {
+		if ((ifd = open(infile, O_RDONLY)) < 0)
+			perror_msg_and_die("%s", infile);
+	} else {
+		ifd = STDIN_FILENO;
+		infile = "standard input";
+	}
+
+	if (outfile != NULL) {
+		if ((ofd = open(outfile, O_WRONLY | O_CREAT, 0666)) < 0)
+			perror_msg_and_die("%s", outfile);
+		statusfp = stdout;
+	} else {
+		ofd = STDOUT_FILENO;
+		outfile = "standard output";
+		statusfp = stderr;
+	}
+
+	if (skip) {
+		if (lseek(ifd, skip * bs, SEEK_CUR) < 0)
+			perror_msg_and_die("%s", infile);
+	}
+
+	if (seek) {
+		if (lseek(ofd, seek * bs, SEEK_CUR) < 0)
+			perror_msg_and_die("%s", outfile);
+	}
+
+	if (trunc) {
+		if (ftruncate(ofd, seek * bs) < 0)
+			perror_msg_and_die("%s", outfile);
+	}
+
+	while (in_full + in_part != count) {
+		n = safe_read(ifd, buf, bs);
+		if (n < 0)
+			perror_msg_and_die("%s", infile);
+		if (n == 0)
 			break;
+		if (n == bs)
+			in_full++;
+		else
+			in_part++;
+		if (sync) {
+			memset(buf + n, '\0', bs - n);
+			n = bs;
 		}
-		outTotal += outCc;
-        }
-	if (trunc == TRUE) {
-		ftruncate(outFd, lseek(outFd, 0, SEEK_CUR));
-	}
-	/* Note that we are not freeing memory or closing
-	 * files here, to save a few bytes. */
-#ifdef BB_FEATURE_CLEAN_UP
-	close(inFd);
-	close(outFd);
-#endif
-
-	printf("%ld+%d records in\n", (long) (inTotal / blockSize),
-		   (inTotal % blockSize) != 0);
-	printf("%ld+%d records out\n", (long) (outTotal / blockSize),
-		   (outTotal % blockSize) != 0);
-	return EXIT_SUCCESS;
-  usage:
+		n = full_write(ofd, buf, n);
+		if (n < 0)
+			perror_msg_and_die("%s", outfile);
+		if (n == bs)
+			out_full++;
+		else
+			out_part++;
+	}
+
+	fprintf(statusfp, "%d+%d records in\n", in_full, in_part);
+	fprintf(statusfp, "%d+%d records out\n", out_full, out_part);
 
-	usage(dd_usage);
+	return EXIT_SUCCESS;
 }
Index: tail.c
===================================================================
RCS file: /var/cvs/busybox/tail.c,v
retrieving revision 1.27
diff -u -r1.27 tail.c
--- tail.c	2000/12/07 19:56:48	1.27
+++ tail.c	2000/12/14 21:08:57
@@ -61,6 +61,13 @@
 
 static off_t units=0;
 
+static struct suffix_mult tail_suffixes[] = {
+	{ "b", 512 },
+	{ "k", 1024 },
+	{ "m", 1048576 },
+	{ NULL, 0 }
+};
+
 static int tail_stream(int fd)
 {
 	ssize_t startpoint;
@@ -170,32 +177,6 @@
 
 		switch (opt) {
 #ifndef BB_FEATURE_SIMPLE_TAIL
-		case 'c':
-			unit_type = BYTES;
-			test = atoi(optarg);
-			if(test==0)
-				usage(tail_usage);
-			if(optarg[strlen(optarg)-1]>'9') {
-				switch (optarg[strlen(optarg)-1]) {
-				case 'b':
-					test *= 512;
-					break;
-				case 'k':
-					test *= 1024;
-					break;
-				case 'm':
-					test *= (1024 * 1024);
-					break;
-				default:
-					fprintf(stderr,"Size must be b,k, or m.");
-					usage(tail_usage);
-				}
-			}
-			if(optarg[0]=='+')
-				units=test+1;
-			else
-				units=-(test+1);
-			break;
 		case 'q':
 			show_headers = 0;
 			break;
@@ -207,15 +188,12 @@
 		case 'v':
 			verbose = 1;
 			break;
+		case 'c':
+			unit_type = BYTES;
+			/* FALLS THROUGH */
 #endif
-		case 'f':
-			follow = 1;
-			break;
-		case 'h':
-			usage(tail_usage);
-			break;
 		case 'n':
-			test = atoi(optarg);
+			test = parse_number(optarg, tail_suffixes);
 			if (test) {
 				if (optarg[0] == '+')
 					units = test;
@@ -223,6 +201,12 @@
 					units = -(test+1);
 			} else
 				usage(tail_usage);
+			break;
+		case 'f':
+			follow = 1;
+			break;
+		case 'h':
+			usage(tail_usage);
 			break;
 		default:
 			error_msg("\nUnknown arg: %c.\n\n",optopt);
Index: utility.c
===================================================================
RCS file: /var/cvs/busybox/utility.c,v
retrieving revision 1.173
diff -u -r1.173 utility.c
--- utility.c	2000/12/13 01:52:39	1.173
+++ utility.c	2000/12/14 20:56:15
@@ -1215,58 +1215,6 @@
 }
 #endif							/* BB_DF || BB_MTAB */
 
-
-
-#if defined BB_DD || defined BB_TAIL
-/*
- * Read a number with a possible multiplier.
- * Returns -1 if the number format is illegal.
- */
-extern long atoi_w_units(const char *cp)
-{
-	long value;
-
-	if (!is_decimal(*cp))
-		return -1;
-
-	value = 0;
-
-	while (is_decimal(*cp))
-		value = value * 10 + *cp++ - '0';
-
-	switch (*cp++) {
-	case 'M':
-	case 'm':					/* `tail' uses it traditionally */
-		value *= 1048576;
-		break;
-
-	case 'k':
-		value *= 1024;
-		break;
-
-	case 'b':
-		value *= 512;
-		break;
-
-	case 'w':
-		value *= 2;
-		break;
-
-	case '\0':
-		return value;
-
-	default:
-		return -1;
-	}
-
-	if (*cp)
-		return -1;
-
-	return value;
-}
-#endif							/* BB_DD || BB_TAIL */
-
-
 #if defined BB_INIT || defined BB_SYSLOGD 
 /* try to open up the specified device */
 extern int device_open(char *device, int mode)
@@ -1790,6 +1738,33 @@
 
 	return strcmp(applet1->name, applet2->name);
 }
+
+#if defined BB_DD || defined BB_TAIL
+unsigned long int parse_number(const char *numstr, struct suffix_mult *suffixes)
+{
+	struct suffix_mult *sm;
+	unsigned long int ret;
+	int len;
+	char *end;
+	
+	ret = strtoul(numstr, &end, 10);
+	if (numstr == end)
+		error_msg_and_die("invalid number `%s'", numstr);
+	while (end[0] != '\0') {
+		for (sm = suffixes; sm->suffix != NULL; sm++) {
+			len = strlen(sm->suffix);
+			if (strncmp(sm->suffix, end, len) == 0) {
+				ret *= sm->mult;
+				end += len;
+				break;
+			}
+		}
+		if (sm->suffix == NULL)
+			error_msg_and_die("invalid number `%s'", numstr);
+	}
+	return ret;
+}
+#endif
 
 /* END CODE */
 /*


More information about the busybox mailing list