about summary refs log tree commit diff
path: root/src/usr.bin/jot
diff options
context:
space:
mode:
Diffstat (limited to 'src/usr.bin/jot')
-rw-r--r--src/usr.bin/jot/jot.147
-rw-r--r--src/usr.bin/jot/jot.c278
2 files changed, 153 insertions, 172 deletions
diff --git a/src/usr.bin/jot/jot.1 b/src/usr.bin/jot/jot.1
index 628b07e..c82553e 100644
--- a/src/usr.bin/jot/jot.1
+++ b/src/usr.bin/jot/jot.1
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: jot.1,v 1.21 2016/07/17 04:15:25 tb Exp $
+.\"	$OpenBSD: jot.1,v 1.23 2016/08/12 21:49:31 tb Exp $
 .\"	$NetBSD: jot.1,v 1.2 1994/11/14 20:27:36 jtc Exp $
 .\"
 .\" Copyright (c) 1993
@@ -30,7 +30,7 @@
 .\"
 .\"	@(#)jot.1	8.1 (Berkeley) 6/6/93
 .\"
-.Dd $Mdocdate: July 17 2016 $
+.Dd $Mdocdate: August 12 2016 $
 .Dt JOT 1
 .Os
 .Sh NAME
@@ -101,28 +101,39 @@ conversion specification inside
 in which case the data is inserted rather than appended.
 .El
 .Pp
-The last four arguments indicate, respectively,
-the maximum number of data, the lower bound, the upper bound,
-and the step size.
-While at least one of them must appear,
-any of the other three may be omitted, and
-will be considered as such if given as
-.Ql - .
-Any three of these arguments determines the fourth.
-If four are specified and the given and computed values of
+The last four arguments specify the length of the output sequence,
+its start and end points, and the step size.
+Any three of these arguments determine the fourth.
+If the given and computed values for
 .Ar reps
 conflict, the lower value is used.
-If fewer than three are specified, defaults are assigned
-left to right, except for
-.Ar s ,
-which assumes its default unless both
+.Pp
+Arguments can be omitted by specifying a
+.Ql - .
+The default values for
+.Ar reps ,
+.Ar begin ,
+.Ar end ,
+and
+.Ar s
+are 100, 1, 100, and 1, respectively.
+Omitted values are computed if possible or assume the default.
+A special case arises if only
 .Ar begin
 and
 .Ar end
-are given.
+are specified:
+if
+.Ar begin
+is greater than
+.Ar end
+then
+.Ar s
+is set to \(mi1, otherwise it is set to 1;
+afterwards
+.Ar reps
+is computed.
 .Pp
-Defaults for the four arguments are, respectively,
-100, 1, 100, and 1.
 .Ar reps
 is expected to be an unsigned integer,
 and if given as zero is taken to be infinite.
diff --git a/src/usr.bin/jot/jot.c b/src/usr.bin/jot/jot.c
index 08da69f..0de3a51 100644
--- a/src/usr.bin/jot/jot.c
+++ b/src/usr.bin/jot/jot.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: jot.c,v 1.28 2016/07/17 04:04:46 tb Exp $	*/
+/*	$OpenBSD: jot.c,v 1.36 2016/09/02 14:23:09 tb Exp $	*/
 /*	$NetBSD: jot.c,v 1.3 1994/12/02 20:29:43 pk Exp $	*/
 
 /*-
@@ -36,44 +36,45 @@
  * Author:  John Kunze, Office of Comp. Affairs, UCB
  */
 
-#include <err.h>
-#include <stdbool.h>
 #include <ctype.h>
+#include <err.h>
 #include <limits.h>
 #include <math.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#define	REPS_DEF	100
-#define	BEGIN_DEF	1
-#define	ENDER_DEF	100
-#define	STEP_DEF	1
+#define	REPS	1
+#define	BEGIN	2
+#define	ENDER	4
+#define	STEP	8
 
 #define	is_default(s)	(strcmp((s), "-") == 0)
 
-static double	begin;
-static double	ender;
-static double	s;
-static long	reps;
-static bool	randomize;
-static bool	infinity;
-static bool	boring;
+static long	reps	= 100;
+static double	begin	= 1;
+static double	ender	= 100;
+static double	step	= 1;
+
+static char	format[BUFSIZ];
+static char	sepstring[BUFSIZ] = "\n";
 static int	prec = -1;
+static bool	boring;
+static bool	chardata;
+static bool	finalnl = true;
+static bool	infinity;
 static bool	intdata;
 static bool	longdata;
-static bool	chardata;
 static bool	nosign;
-static bool	finalnl = true;
-static char	sepstring[BUFSIZ] = "\n";
-static char	format[BUFSIZ];
+static bool	randomize;
 
 static void	getformat(void);
 static int	getprec(char *);
 static int	putdata(double, bool);
-static void	usage(void);
+static void __dead	usage(void);
 
 int
 main(int argc, char *argv[])
@@ -89,10 +90,13 @@ main(int argc, char *argv[])
 	if (pledge("stdio", NULL) == -1)
 		err(1, "pledge");
 
-	while ((ch = getopt(argc, argv, "rb:w:cs:np:")) != -1)
+	while ((ch = getopt(argc, argv, "b:cnp:rs:w:")) != -1) {
 		switch (ch) {
-		case 'r':
-			randomize = true;
+		case 'b':
+			boring = true;
+			if (strlcpy(format, optarg, sizeof(format)) >=
+			    sizeof(format))
+				errx(1, "-b word too long");
 			break;
 		case 'c':
 			chardata = true;
@@ -100,40 +104,38 @@ main(int argc, char *argv[])
 		case 'n':
 			finalnl = false;
 			break;
-		case 'b':
-			boring = true;
-			if (strlcpy(format, optarg, sizeof(format)) >=
-			    sizeof(format))
-				errx(1, "-b word too long");
+		case 'p':
+			prec = strtonum(optarg, 0, INT_MAX, &errstr);
+			if (errstr != NULL)
+				errx(1, "bad precision value, %s: %s", errstr,
+					optarg);
 			break;
-		case 'w':
-			if (strlcpy(format, optarg, sizeof(format)) >=
-			    sizeof(format))
-				errx(1, "-w word too long");
+		case 'r':
+			randomize = true;
 			break;
 		case 's':
 			if (strlcpy(sepstring, optarg, sizeof(sepstring)) >=
 			    sizeof(sepstring))
 				errx(1, "-s string too long");
 			break;
-		case 'p':
-			prec = strtonum(optarg, 0, INT_MAX, &errstr);
-			if (errstr != NULL)
-				errx(1, "bad precision value, %s: %s", errstr,
-					optarg);
+		case 'w':
+			if (strlcpy(format, optarg, sizeof(format)) >=
+			    sizeof(format))
+				errx(1, "-w word too long");
 			break;
 		default:
 			usage();
 		}
+	}
 	argc -= optind;
 	argv += optind;
 
 	switch (argc) {	/* examine args right to left, falling thru cases */
 	case 4:
 		if (!is_default(argv[3])) {
-			if (!sscanf(argv[3], "%lf", &s))
+			if (!sscanf(argv[3], "%lf", &step))
 				errx(1, "Bad s value:  %s", argv[3]);
-			mask |= 01;
+			mask |= STEP;
 			if (randomize)
 				warnx("random seeding not supported");
 		}
@@ -141,7 +143,7 @@ main(int argc, char *argv[])
 		if (!is_default(argv[2])) {
 			if (!sscanf(argv[2], "%lf", &ender))
 				ender = argv[2][strlen(argv[2])-1];
-			mask |= 02;
+			mask |= ENDER;
 			if (prec == -1)
 				n = getprec(argv[2]);
 		}
@@ -149,7 +151,7 @@ main(int argc, char *argv[])
 		if (!is_default(argv[1])) {
 			if (!sscanf(argv[1], "%lf", &begin))
 				begin = argv[1][strlen(argv[1])-1];
-			mask |= 04;
+			mask |= BEGIN;
 			if (prec == -1)
 				prec = getprec(argv[1]);
 			if (n > prec)		/* maximum precision */
@@ -159,7 +161,9 @@ main(int argc, char *argv[])
 		if (!is_default(argv[0])) {
 			if (!sscanf(argv[0], "%ld", &reps))
 				errx(1, "Bad reps value:  %s", argv[0]);
-			mask |= 010;
+			mask |= REPS;
+			if (reps == 0)
+				infinity = true;
 			if (prec == -1)
 				prec = 0;
 		}
@@ -171,105 +175,80 @@ main(int argc, char *argv[])
 		errx(1, "Too many arguments.  What do you mean by %s?",
 		    argv[4]);
 	}
+
 	getformat();
-	while (mask)	/* 4 bit mask has 1's where last 4 args were given */
-		switch (mask) {	/* fill in the 0's by default or computation */
-		case 001:
-			reps = REPS_DEF;
-			mask = 011;
-			break;
-		case 002:
-			reps = REPS_DEF;
-			mask = 012;
-			break;
-		case 003:
-			reps = REPS_DEF;
-			mask = 013;
-			break;
-		case 004:
-			reps = REPS_DEF;
-			mask = 014;
-			break;
-		case 005:
-			reps = REPS_DEF;
-			mask = 015;
+
+	if (!randomize) {
+		/*
+		 * Consolidate the values of reps, begin, ender, step:
+		 * The formula ender - begin == (reps - 1) * step shows that any
+		 * three determine the fourth (unless reps == 1 or step == 0).
+		 * The manual states the following rules:
+		 * 1. If four are specified, compare the given and the computed
+		 *    value of reps and take the smaller of the two.
+		 * 2. If steps was omitted, it takes the default, unless both
+		 *    begin and ender were specified.
+		 * 3. Assign defaults to omitted values for reps, begin, ender,
+		 *    from left to right.
+		 */
+		switch (mask) { /* Four cases involve both begin and ender. */
+		case REPS | BEGIN | ENDER | STEP:
+			if (infinity)
+				errx(1,
+				    "Can't specify end of infinite sequence");
+			if (step != 0.0) {
+				long t = (ender - begin + step) / step;
+				if (t <= 0)
+					errx(1, "Impossible stepsize");
+				if (t < reps)
+					reps = t;
+			}
 			break;
-		case 006:
-			reps = REPS_DEF;
-			mask = 016;
+		case REPS | BEGIN | ENDER:
+			if (infinity)
+				errx(1,
+				    "Can't specify end of infinite sequence");
+			if (reps == 1)
+				step = 0.0;
+			else
+				step = (ender - begin) / (reps - 1);
 			break;
-		case 007:
-			if (randomize) {
-				reps = REPS_DEF;
-				mask = 0;
-				break;
-			}
-			if (s == 0.0) {
+		case BEGIN | ENDER:
+			step = ender > begin ? 1 : -1; /* FreeBSD's behavior. */
+			/* FALLTHROUGH */
+		case BEGIN | ENDER | STEP:
+			if (step == 0.0) {
 				reps = 0;
-				mask = 0;
+				infinity = true;
 				break;
 			}
-			reps = (ender - begin + s) / s;
+			reps = (ender - begin + step) / step;
 			if (reps <= 0)
 				errx(1, "Impossible stepsize");
-			mask = 0;
-			break;
-		case 010:
-			begin = BEGIN_DEF;
-			mask = 014;
-			break;
-		case 011:
-			begin = BEGIN_DEF;
-			mask = 015;
 			break;
-		case 012:
-			s = STEP_DEF;
-			mask = 013;
-			break;
-		case 013:
-			if (randomize)
-				begin = BEGIN_DEF;
-			else if (reps == 0)
-				errx(1, "Must specify begin if reps == 0");
-			begin = ender - reps * s + s;
-			mask = 0;
-			break;
-		case 014:
-			s = STEP_DEF;
-			mask = 015;
-			break;
-		case 015:
-			if (randomize)
-				ender = ENDER_DEF;
-			else
-				ender = begin + reps * s - s;
-			mask = 0;
-			break;
-		case 016:
-			if (reps == 0)
-				errx(1, "Infinite sequences cannot be bounded");
-			else if (reps == 1)
-				s = 0.0;
-			else
-				s = (ender - begin) / (reps - 1);
-			mask = 0;
-			break;
-		case 017:		/* if reps given and implied, */
-			if (!randomize && s != 0.0) {
-				long t = (ender - begin + s) / s;
-				if (t <= 0)
-					errx(1, "Impossible stepsize");
-				if (t < reps)		/* take lesser */
-					reps = t;
-			}
-			mask = 0;
+		case ENDER:		/* Four cases involve only ender. */
+		case ENDER | STEP:
+		case REPS | ENDER:
+		case REPS | ENDER | STEP:
+			if (infinity)
+				errx(1,
+				    "Must specify start of infinite sequence");
+			begin = ender - reps * step + step;
 			break;
 		default:
-			errx(1, "bad mask");
+			/*
+			 * The remaining eight cases omit ender.  We don't need
+			 * to compute anything because only reps, begin, step
+			 * are used for producing output below.  Rules 2. and 3.
+			 * together imply that ender will be set last.
+			 */
+			break;
 		}
-	if (reps == 0)
-		infinity = true;
-	if (randomize) {
+
+		for (i = 1, x = begin; i <= reps || infinity; i++, x += step)
+			if (putdata(x, reps == i && !infinity))
+				errx(1, "range error in conversion: %f", x);
+	} else { /* Random output: use defaults for omitted values. */
 		bool		use_unif;
 		uint32_t	pow10 = 1;
 		uint32_t	uintx = 0; /* Initialized to make gcc happy. */
@@ -316,13 +295,11 @@ main(int argc, char *argv[])
 				errx(1, "range error in conversion: %f", v);
 		}
 	}
-	else
-		for (i = 1, x = begin; i <= reps || infinity; i++, x += s)
-			if (putdata(x, reps == i && !infinity))
-				errx(1, "range error in conversion: %f", x);
+
 	if (finalnl)
 		putchar('\n');
-	exit(0);
+
+	return 0;
 }
 
 static int
@@ -334,31 +311,31 @@ putdata(double x, bool last)
 		if (x <= (double)ULONG_MAX && x >= 0.0)
 			printf(format, (unsigned long)x);
 		else
-			return (1);
+			return 1;
 	} else if (longdata) {
 		if (x <= (double)LONG_MAX && x >= (double)LONG_MIN)
 			printf(format, (long)x);
 		else
-			return (1);
+			return 1;
 	} else if (chardata || (intdata && !nosign)) {
 		if (x <= (double)INT_MAX && x >= (double)INT_MIN)
 			printf(format, (int)x);
 		else
-			return (1);
+			return 1;
 	} else if (intdata) {
 		if (x <= (double)UINT_MAX && x >= 0.0)
 			printf(format, (unsigned int)x);
 		else
-			return (1);
+			return 1;
 	} else
 		printf(format, x);
 	if (!last)
 		fputs(sepstring, stdout);
 
-	return (0);
+	return 0;
 }
 
-static void
+static void __dead
 usage(void)
 {
 	(void)fprintf(stderr, "usage: jot [-cnr] [-b word] [-p precision] "
@@ -370,18 +347,9 @@ usage(void)
 static int
 getprec(char *s)
 {
-	char	*p;
-	char	*q;
-
-	for (p = s; *p != '\0'; p++)
-		if (*p == '.')
-			break;
-	if (*p == '\0')
-		return (0);
-	for (q = ++p; *p != '\0'; p++)
-		if (!isdigit((unsigned char)*p))
-			break;
-	return (p - q);
+	if ((s = strchr(s, '.')) == NULL)
+		return 0;
+	return strspn(s + 1, "0123456789");
 }
 
 static void
@@ -394,8 +362,11 @@ getformat(void)
 	if (boring)				/* no need to bother */
 		return;
 	for (p = format; *p != '\0'; p++)	/* look for '%' */
-		if (*p == '%' && *(p+1) != '%')	/* leave %% alone */
-			break;
+		if (*p == '%') {
+			if (*(p+1) != '%')
+				break;
+			p++;			/* leave %% alone */
+		}
 	sz = sizeof(format) - strlen(format) - 1;
 	if (*p == '\0' && !chardata) {
 		int n;
@@ -474,7 +445,6 @@ getformat(void)
 fmt_broken:
 			*++p = '\0';
 			errx(1, "illegal or unsupported format '%s'", p2);
-			/* NOTREACHED */
 		}
 		while (*++p != '\0')
 			if (*p == '%' && *(p+1) != '\0' && *(p+1) != '%')