From fc83c4c3535cb5bce344821d382122262a8450f5 Mon Sep 17 00:00:00 2001
From: Eric Andersen <andersen@codepoet.org>
Date: Wed, 14 Feb 2001 07:15:30 +0000
Subject: Several cleanups from Manuel Novoa III.

get_kernel_revision --  size reduction
        NOTE: may want to combine with get_kernel_version in insmod???

parse_mode -- size reduction, multiple settings with "," now work correctly,
        sticky-bit setting now implemented

process_escape_sequence -- size reduction, octal code to big for char bug fixed

format -- size reduction, val > LONG_MAX and hr = 1 printing bug fixed (was %ld),
---
 busybox.h         |   2 +-
 include/busybox.h |   2 +-
 utility.c         | 285 +++++++++++++++++++++++++++++-------------------------
 3 files changed, 155 insertions(+), 134 deletions(-)

diff --git a/busybox.h b/busybox.h
index 2fc0cc7e0..101e65989 100644
--- a/busybox.h
+++ b/busybox.h
@@ -232,7 +232,7 @@ extern int sysinfo (struct sysinfo* info);
 #endif
 
 #ifdef BB_FEATURE_HUMAN_READABLE
-char *format(unsigned long val, unsigned long hr);
+const char *format(unsigned long val, unsigned long hr);
 #define KILOBYTE 1024
 #define MEGABYTE (KILOBYTE*1024)
 #define GIGABYTE (MEGABYTE*1024)
diff --git a/include/busybox.h b/include/busybox.h
index 2fc0cc7e0..101e65989 100644
--- a/include/busybox.h
+++ b/include/busybox.h
@@ -232,7 +232,7 @@ extern int sysinfo (struct sysinfo* info);
 #endif
 
 #ifdef BB_FEATURE_HUMAN_READABLE
-char *format(unsigned long val, unsigned long hr);
+const char *format(unsigned long val, unsigned long hr);
 #define KILOBYTE 1024
 #define MEGABYTE (KILOBYTE*1024)
 #define GIGABYTE (MEGABYTE*1024)
diff --git a/utility.c b/utility.c
index 9f0bca971..f3c184e73 100644
--- a/utility.c
+++ b/utility.c
@@ -51,6 +51,7 @@
 #include <unistd.h>
 #include <ctype.h>
 #include <stdlib.h>
+#include <limits.h>
 #include <sys/ioctl.h>
 #include <sys/utsname.h>		/* for uname(2) */
 
@@ -77,7 +78,7 @@ const char mtab_file[] = "/proc/mounts";
 
 extern void usage(const char *usage)
 {
-	fprintf(stderr, "%s\n\nUsage: %s\n", full_version, usage);
+	fprintf(stderr, "%s\n\nUsage: %s\n\n", full_version, usage);
 	exit(EXIT_FAILURE);
 }
 
@@ -145,16 +146,21 @@ extern void perror_msg_and_die(const char *s, ...)
 extern int get_kernel_revision(void)
 {
 	struct utsname name;
-	int major = 0, minor = 0, patch = 0;
+	char *s;
+	int i, r;
 
 	if (uname(&name) == -1) {
 		perror_msg("cannot get system information");
 		return (0);
 	}
-	major = atoi(strtok(name.release, "."));
-	minor = atoi(strtok(NULL, "."));
-	patch = atoi(strtok(NULL, "."));
-	return major * 65536 + minor * 256 + patch;
+
+	s = name.release;
+	r = 0;
+	for (i=0 ; i<3 ; i++) {
+		r = r * 256 + atoi(strtok(s, "."));
+		s = NULL;
+	}
+	return r;
 }
 #endif                                                 /* BB_INIT */
 
@@ -749,102 +755,100 @@ extern int create_path(const char *name, int mode)
  || defined (BB_MKFIFO) || defined (BB_MKNOD) || defined (BB_AR)
 /* [ugoa]{+|-|=}[rwxst] */
 
-
-
 extern int parse_mode(const char *s, mode_t * theMode)
 {
-	mode_t andMode =
+	static const mode_t group_set[] = { 
+		S_ISUID | S_IRWXU,		/* u */
+		S_ISGID | S_IRWXG,		/* g */
+		S_IRWXO,				/* o */
+		S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO /* a */
+	};
+
+	static const mode_t mode_set[] = {
+		S_IRUSR | S_IRGRP | S_IROTH, /* r */
+		S_IWUSR | S_IWGRP | S_IWOTH, /* w */
+		S_IXUSR | S_IXGRP | S_IXOTH, /* x */
+		S_ISUID | S_ISGID,		/* s */
+		S_ISVTX					/* t */
+	};
+
+	static const char group_string[] = "ugoa";
+	static const char mode_string[] = "rwxst";
+
+	const char *p;
 
+	mode_t andMode =
 		S_ISVTX | S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
 	mode_t orMode = 0;
-	mode_t mode = 0;
-	mode_t groups = 0;
+	mode_t mode;
+	mode_t groups;
 	char type;
 	char c;
 
-	if (s==NULL)
+	if (s==NULL) {
 		return (FALSE);
+	}
 
 	do {
-		for (;;) {
-			switch (c = *s++) {
-			case '\0':
-				return -1;
-			case 'u':
-				groups |= S_ISUID | S_IRWXU;
-				continue;
-			case 'g':
-				groups |= S_ISGID | S_IRWXG;
-				continue;
-			case 'o':
-				groups |= S_IRWXO;
-				continue;
-			case 'a':
-				groups |= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
-				continue;
-			case '+':
+		mode = 0;
+		groups = 0;
+	NEXT_GROUP:
+		if ((c = *s++) == '\0') {
+			return -1;
+		}
+		for (p=group_string ; *p ; p++) {
+			if (*p == c) {
+				groups |= group_set[(int)(p-group_string)];
+				goto NEXT_GROUP;
+			}
+		}
+		switch (c) {
 			case '=':
+			case '+':
 			case '-':
 				type = c;
-				if (groups == 0)	/* The default is "all" */
-					groups |=
-						S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
+				if (groups == 0) { /* The default is "all" */
+					groups |= S_ISUID | S_ISGID | S_ISVTX
+							| S_IRWXU | S_IRWXG | S_IRWXO;
+				}
 				break;
 			default:
-				if (isdigit(c) && c >= '0' && c <= '7' &&
-					mode == 0 && groups == 0) {
+				if ((c < '0') || (c > '7') || (mode | groups)) {
+					return (FALSE);
+				} else {
 					*theMode = strtol(--s, NULL, 8);
 					return (TRUE);
-				} else
-					return (FALSE);
-			}
-			break;
+				}
 		}
 
-		while ((c = *s++) != '\0') {
-			switch (c) {
-			case ',':
-				break;
-			case 'r':
-				mode |= S_IRUSR | S_IRGRP | S_IROTH;
-				continue;
-			case 'w':
-				mode |= S_IWUSR | S_IWGRP | S_IWOTH;
-				continue;
-			case 'x':
-				mode |= S_IXUSR | S_IXGRP | S_IXOTH;
-				continue;
-			case 's':
-				mode |= S_IXGRP | S_ISUID | S_ISGID;
-				continue;
-			case 't':
-				mode |= 0;
-				continue;
-			default:
-				*theMode &= andMode;
-				*theMode |= orMode;
-				return (TRUE);
+	NEXT_MODE:
+		if (((c = *s++) != '\0') && (c != ',')) {
+			for (p=mode_string ; *p ; p++) {
+				if (*p == c) {
+					mode |= mode_set[(int)(p-mode_string)];
+					goto NEXT_MODE;
+				}
 			}
-			break;
+			break;				/* We're done so break out of loop.*/
 		}
 		switch (type) {
-		case '=':
-			andMode &= ~(groups);
-			/* fall through */
-		case '+':
-			orMode |= mode & groups;
-			break;
-		case '-':
-			andMode &= ~(mode & groups);
-			orMode &= andMode;
-			break;
+			case '=':
+				andMode &= ~(groups); /* Now fall through. */
+			case '+':
+				orMode |= mode & groups;
+				break;
+			case '-':
+				andMode &= ~(mode & groups);
+				orMode &= ~(mode & groups);
+				break;
 		}
 	} while (c == ',');
+
 	*theMode &= andMode;
 	*theMode |= orMode;
-	return (TRUE);
-}
 
+	return TRUE;
+}
 
 #endif
 /* BB_CHMOD_CHOWN_CHGRP || BB_MKDIR || BB_MKFIFO || BB_MKNOD */
@@ -1575,48 +1579,43 @@ extern int print_file_by_name(char *filename)
 #if defined BB_ECHO || defined BB_SH || defined BB_TR
 char process_escape_sequence(char **ptr)
 {
-	char c;
-
-	switch (c = *(*ptr)++) {
-	case 'a':
-		c = '\a';
-		break;
-	case 'b':
-		c = '\b';
-		break;
-	case 'f':
-		c = '\f';
-		break;
-	case 'n':
-		c = '\n';
-		break;
-	case 'r':
-		c = '\r';
-		break;
-	case 't':
-		c = '\t';
-		break;
-	case 'v':
-		c = '\v';
-		break;
-	case '\\':
-		c = '\\';
-		break;
-	case '0': case '1': case '2': case '3':
-	case '4': case '5': case '6': case '7':
-		c -= '0';
-		if ('0' <= **ptr && **ptr <= '7') {
-			c = c * 8 + (*(*ptr)++ - '0');
-			if ('0' <= **ptr && **ptr <= '7')
-				c = c * 8 + (*(*ptr)++ - '0');
-		}
-		break;
-	default:
-		(*ptr)--;
-		c = '\\';
-		break;
-	}
-	return c;
+       static const char charmap[] = {
+		   'a',  'b',  'f',  'n',  'r',  't',  'v',  '\\', 0,
+	       '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\\' };
+
+       const char *p;
+	   char *q;
+       int num_digits;
+       unsigned int n;
+
+       n = 0;
+	   q = *ptr;
+
+       for ( num_digits = 0 ; num_digits < 3 ; ++num_digits) {
+	       if ((*q < '0') || (*q > '7')) { /* not a digit? */
+		       break;
+	       }
+	       n = n * 8 + (*q++ - '0');
+       }
+
+       if (num_digits == 0) {	/* mnemonic escape sequence? */
+		   for (p=charmap ; *p ; p++) {
+			   if (*p == *q) {
+				   q++;
+				   break;
+			   }
+		   }
+		   n = *(p+(sizeof(charmap)/2));
+	   }
+
+	   /* doesn't hurt to fall through to here from mnemonic case */
+	   if (n > UCHAR_MAX) {	/* is octal code too big for a char? */
+		   n /= 8;			/* adjust value and */
+		   --q;				/* back up one char */
+	   }
+
+	   *ptr = q;
+	   return (char) n;
 }
 #endif
 
@@ -1737,23 +1736,45 @@ ssize_t safe_read(int fd, void *buf, size_t count)
 #endif
 
 #ifdef BB_FEATURE_HUMAN_READABLE
-char *format(unsigned long val, unsigned long hr)
+const char *format(unsigned long val, unsigned long hr)
 {
-	static char str[10] = "\0";
-
-	if(val == 0)
-		return("0");
-	if(hr)
-		snprintf(str, 9, "%ld", val/hr);
-	else if(val >= GIGABYTE)
-		snprintf(str, 9, "%.1LfG", ((long double)(val)/GIGABYTE));
-	else if(val >= MEGABYTE)
-		snprintf(str, 9, "%.1LfM", ((long double)(val)/MEGABYTE));
-	else if(val >= KILOBYTE)
-		snprintf(str, 9, "%.1Lfk", ((long double)(val)/KILOBYTE));
-	else
-		snprintf(str, 9, "%ld", (val));
-	return(str);
+	static const char strings[] = { '0', 0, 'k', 0, 'M', 0, 'G', 0 };
+	static const char fmt[] = "%lu";
+	static const char fmt_u[] = "%lu.%lu%s";
+
+	static char str[10];
+
+	unsigned long frac __attribute__ ((unused));	/* 'may be uninitialized' warning is ok */
+	const char *u;
+	const char *f;
+
+#if 1
+	if(val == 0) {				/* This may be omitted to reduce size */
+		return strings;			/* at the cost of speed. */
+	}
+#endif
+
+	u = strings;
+	f = fmt;
+	if (hr) {
+		val /= hr;
+	} else {
+		while ((val >= KILOBYTE) && (*u != 'G')) {
+			f = fmt_u;
+			u += 2;
+			frac = (((val % KILOBYTE) * 10) + (KILOBYTE/2)) / KILOBYTE;
+			val /= KILOBYTE;
+			if (frac >= 10) {	/* We need to round up here. */
+				++val;
+				frac = 0;
+			}
+		}
+	}
+
+	/* If f==fmt then 'frac' and 'u' are ignored and need not be set. */
+	snprintf(str, sizeof(str), f, val, frac, u);
+
+	return str;
 }
 #endif
 
-- 
cgit v1.2.3