Index: parse.c
===================================================================
--- parse.c	(revision 205137)
+++ parse.c	(working copy)
@@ -94,58 +94,112 @@
 	nextfs = &tfs->nextfs;
 	nextfu = &tfs->nextfu;
 
-	/* take the format string and break it up into format units */
-	for (p = fmt;;) {
-		/* skip leading white space */
-		for (; isspace(*p); ++p);
-		if (!*p)
-			break;
+	/* 
+	 * Take the format string and break it up into format units.
+	 *
+	 * The structure of each format unit is as follows:
+	 *
+	 * 	iteration_count/byte_count
+	 *
+	 * iteration_count and byte_count are optional, but either one or the
+	 * other must be present.
+	 *
+	 * Iteration count defaults to 1, and byte count defaults vary
+	 * depending upon the format string specified. See size for more
+	 * details.
+	 */
+	for (p = fmt; *p; ) {
 
-		/* allocate a new format unit and link it in */
-		if ((tfu = calloc(1, sizeof(FU))) == NULL)
-			err(1, NULL);
-		*nextfu = tfu;
-		nextfu = &tfu->nextfu;
-		tfu->reps = 1;
+		/* Skip any and all leading white space. */
+		for (; isspace(*p); p++) ;
 
-		/* if leading digit, repetition count */
-		if (isdigit(*p)) {
-			for (savep = p; isdigit(*p); ++p);
-			if (!isspace(*p) && *p != '/')
+		/* There's more information on the line to scan. */
+		if (*p) {
+
+			/* allocate a new format unit and link it in */
+			if ((tfu = calloc(1, sizeof(FU))) == NULL)
+				err(1, "calloc");
+			*nextfu = tfu;
+			nextfu = &tfu->nextfu;
+			/* Default to one iteration count. */
+			tfu->reps = 1;
+
+			/* 
+			 * Scan the leading digit -- it's the repetition count.
+			 */
+			if (isdigit(*p)) {
+
+				for (savep = p; isdigit(*p); ++p);
+				/* 
+				 * Next character scanned wasn't '/' -- the
+				 * iteration count is invalid.
+				 */
+				if (!isspace(*p) && *p != '/')
+					badfmt(fmt);
+				/* May overwrite either white space or slash */
+				tfu->reps = (int) strtol(savep, NULL, 10);
+				/*
+				 * We only want non-zero numbers. All negative
+				 * numbers would be caught below at the == '"'
+				 * check because the leading character is `-'.
+				 */
+				if (tfu->reps == 0)
+					badfmt(fmt);
+				tfu->flags = F_SETREP;
+				/* skip trailing white space */
+				for (++p; isspace(*p); ++p) ;
+
+			}
+
+			/* Skip the slash and trailing white space */
+			if (*p == '/')
+				while (isspace(*++p));
+
+			/* Scan the trailing digit -- it's the byte count. */
+			if (isdigit(*p)) {
+
+				for (savep = p; isdigit(*p); ++p);
+				if (!isspace(*p))
+					badfmt(fmt);
+				tfu->bcnt = (int) strtol(savep, NULL, 10);
+				/*
+				 * We only want non-zero numbers. All negative
+				 * numbers would be caught below at the == '"'
+				 * check because the leading character is `-'.
+				 */
+				if (tfu->bcnt == 0)
+					badfmt(fmt);
+				/* skip trailing white space */
+				for (++p; isspace(*p); ++p) ;
+
+			}
+
+			/* This wasn't a valid format string */
+			if (*p++ != '"')
 				badfmt(fmt);
-			/* may overwrite either white space or slash */
-			tfu->reps = atoi(savep);
-			tfu->flags = F_SETREP;
-			/* skip trailing white space */
-			for (++p; isspace(*p); ++p);
-		}
+			/* 
+			 * Copy the format string between the '"'
+			 * terminators.
+			 */
+			for (savep = p; *p != '"'; p++) {
 
-		/* skip slash and trailing white space */
-		if (*p == '/')
-			while (isspace(*++p));
+				/* The format string isn't valid (couldn't find
+				 * a '"' terminator) */
+				if (*p == '\0')
+					badfmt(fmt);
 
-		/* byte count */
-		if (isdigit(*p)) {
-			for (savep = p; isdigit(*p); ++p);
-			if (!isspace(*p))
-				badfmt(fmt);
-			tfu->bcnt = atoi(savep);
-			/* skip trailing white space */
-			for (++p; isspace(*p); ++p);
+			}
+			if (!(tfu->fmt = malloc(p - savep + 1)))
+				err(1, "malloc");
+			(void) strlcpy(tfu->fmt, savep, p - savep + 1);
+			/* End format string copy. */
+			escape(tfu->fmt);
+			p++;
+
 		}
 
-		/* format */
-		if (*p != '"')
-			badfmt(fmt);
-		for (savep = ++p; *p != '"';)
-			if (*p++ == 0)
-				badfmt(fmt);
-		if (!(tfu->fmt = malloc(p - savep + 1)))
-			err(1, NULL);
-		(void) strlcpy(tfu->fmt, savep, p - savep + 1);
-		escape(tfu->fmt);
-		p++;
 	}
+
 }
 
 static const char *spec = ".#-+ 0123456789";
@@ -160,47 +214,68 @@
 
 	/* figure out the data block size needed for each format unit */
 	for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
-		if (fu->bcnt) {
-			cursize += fu->bcnt * fu->reps;
-			continue;
-		}
-		for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
-			if (*fmt != '%')
-				continue;
-			/*
-			 * skip any special chars -- save precision in
-			 * case it's a %s format.
-			 */
-			while (index(spec + 1, *++fmt));
-			if (*fmt == '.' && isdigit(*++fmt)) {
-				prec = atoi(fmt);
-				while (isdigit(*++fmt));
-			}
-			switch(*fmt) {
-			case 'c':
-				bcnt += 1;
-				break;
-			case 'd': case 'i': case 'o': case 'u':
-			case 'x': case 'X':
-				bcnt += 4;
-				break;
-			case 'e': case 'E': case 'f': case 'g': case 'G':
-				bcnt += 8;
-				break;
-			case 's':
-				bcnt += prec;
-				break;
-			case '_':
-				switch(*++fmt) {
-				case 'c': case 'p': case 'u':
-					bcnt += 1;
-					break;
+
+		bcnt = fu->bcnt;
+
+		if (bcnt == 0) {
+
+			for (prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+
+				if (*fmt == '%') {
+
+					/*
+					 * Skip any special chars minus `.'.
+					 * Save precision in case it's a %s
+					 * format.
+					 */
+					while (*fmt != '\0' &&
+					        index(spec+1, *++fmt) != NULL) ;
+
+					if (*fmt == '.' && isdigit(*++fmt)) {
+						prec = strtol(fmt, NULL, 10);
+						if (prec == 0) {
+							errx(1, "Bad precision "
+								"value: %s",
+								fmt);
+						}
+						while (isdigit(*++fmt)) ;
+					}
+					switch(*fmt) {
+					case 'c':
+						bcnt += 1;
+						break;
+					case 'd': case 'i': case 'o': case 'u':
+					case 'x': case 'X':
+						bcnt += 4;
+						break;
+					case 'e': case 'E': case 'f': case 'g':
+					case 'G':
+						bcnt += 8;
+						break;
+					case 's':
+						bcnt += prec;
+						break;
+					case '_':
+						switch(*++fmt) {
+						case 'c': case 'p': case 'u':
+							bcnt += 1;
+							break;
+						}
+
+					}
+
 				}
+
 			}
+
 		}
+
 		cursize += bcnt * fu->reps;
+
 	}
+
 	return (cursize);
+
 }
 
 void
@@ -247,10 +322,10 @@
 			if (fu->bcnt) {
 				sokay = USEBCNT;
 				/* Skip to conversion character. */
-				for (++p1; index(spec, *p1); ++p1);
+				for (++p1; *p1 != '\0' && index(spec, *p1); ++p1);
 			} else {
 				/* Skip any special chars, field width. */
-				while (index(spec + 1, *++p1));
+				while (*p1 != '\0' && index(spec + 1, *++p1));
 				if (*p1 == '.' && isdigit(*++p1)) {
 					sokay = USEPREC;
 					prec = atoi(p1);
@@ -396,7 +471,7 @@
 			p1[0] = '\0';
 			len = strlen(fmtp) + strlen(cs) + 1;
 			if ((pr->fmt = calloc(1, len)) == NULL)
-				err(1, NULL);
+				err(1, "calloc");
 			snprintf(pr->fmt, len, "%s%s", fmtp, cs);
 			*p2 = savech;
 			pr->cchar = pr->fmt + (p1 - fmtp);
@@ -425,16 +500,20 @@
 	 */
 	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
 		if (!fu->nextfu && fs->bcnt < blocksize &&
-		    !(fu->flags&F_SETREP) && fu->bcnt)
+		    !(fu->flags & F_SETREP) && fu->bcnt)
 			fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
 		if (fu->reps > 1) {
-			for (pr = fu->nextpr;; pr = pr->nextpr)
-				if (!pr->nextpr)
-					break;
-			for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
-				p2 = isspace(*p1) ? p1 : NULL;
-			if (p2)
-				pr->nospace = p2;
+
+			for (pr = fu->nextpr; pr && pr->nextpr; pr = pr->nextpr) ;
+
+			/* Avoid a NULL pointer. */
+			if (pr != NULL) {
+				for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+					p2 = isspace(*p1) ? p1 : NULL;
+				if (p2)
+					pr->nospace = p2;
+			}
+
 		}
 	}
 #ifdef DEBUG
