worldstone benchmark script
. also imports seq(1) to help it . add -C option to time(1) to print tsc difference . increase col width for ministat for tsc numbers
This commit is contained in:
		
							parent
							
								
									49532259e5
								
							
						
					
					
						commit
						bc0a39238e
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -32,3 +32,5 @@ CVS
 | 
				
			|||||||
.gitignore
 | 
					.gitignore
 | 
				
			||||||
.svn
 | 
					.svn
 | 
				
			||||||
minix-port.patch
 | 
					minix-port.patch
 | 
				
			||||||
 | 
					*.worldstone.log
 | 
				
			||||||
 | 
					.worldstone*
 | 
				
			||||||
 | 
				
			|||||||
@ -33,7 +33,7 @@ SUBDIR=	aal add_route adduser arp ash at autil awk \
 | 
				
			|||||||
	unstack update uud uue version vol wc \
 | 
						unstack update uud uue version vol wc \
 | 
				
			||||||
	whereis which who write writeisofs fetch \
 | 
						whereis which who write writeisofs fetch \
 | 
				
			||||||
	xargs yacc yes zdump zic zmodem pkgin_cd \
 | 
						xargs yacc yes zdump zic zmodem pkgin_cd \
 | 
				
			||||||
	mktemp
 | 
						mktemp worldstone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.if ${COMPILER_TYPE} == "gnu"
 | 
					.if ${COMPILER_TYPE} == "gnu"
 | 
				
			||||||
SUBDIR += elf2aout
 | 
					SUBDIR += elf2aout
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
PROG=	time
 | 
					PROG=	time
 | 
				
			||||||
MAN=
 | 
					MAN=
 | 
				
			||||||
 | 
					NEED_NBSDLIBC= yes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.include <bsd.prog.mk>
 | 
					.include <bsd.prog.mk>
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@
 | 
				
			|||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <sys/wait.h>
 | 
					#include <sys/wait.h>
 | 
				
			||||||
#include <minix/minlib.h>
 | 
					#include <minix/minlib.h>
 | 
				
			||||||
 | 
					#include <minix/u64.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* -DNEW prints time to 0.01 sec. */
 | 
					/* -DNEW prints time to 0.01 sec. */
 | 
				
			||||||
@ -33,7 +34,7 @@ int main(argc, argv)
 | 
				
			|||||||
int argc;
 | 
					int argc;
 | 
				
			||||||
char *argv[];
 | 
					char *argv[];
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  int cycles = 0;
 | 
				
			||||||
  struct tms pre_buf, post_buf;
 | 
					  struct tms pre_buf, post_buf;
 | 
				
			||||||
  int status, pid;
 | 
					  int status, pid;
 | 
				
			||||||
#if _VMD_EXT
 | 
					#if _VMD_EXT
 | 
				
			||||||
@ -42,12 +43,28 @@ char *argv[];
 | 
				
			|||||||
  struct tms dummy;
 | 
					  struct tms dummy;
 | 
				
			||||||
  int start_time, end_time;
 | 
					  int start_time, end_time;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					  u64_t start_tsc, end_tsc, spent_tsc;
 | 
				
			||||||
  clock_t real_time;
 | 
					  clock_t real_time;
 | 
				
			||||||
 | 
					  int c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (argc == 1) exit(0);
 | 
					  if (argc == 1) exit(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  args = &argv[1];
 | 
					  while((c=getopt(argc, argv, "C")) != EOF) {
 | 
				
			||||||
  name = argv[1];
 | 
					    switch(c) {
 | 
				
			||||||
 | 
					  	case 'C':
 | 
				
			||||||
 | 
							cycles = 1;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							fprintf(stderr, "usage: time [-C] <command>\n");
 | 
				
			||||||
 | 
							exit(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  argv += optind;
 | 
				
			||||||
 | 
					  argc -= optind;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  args = &argv[0];
 | 
				
			||||||
 | 
					  name = argv[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Get real time at start of run. */
 | 
					  /* Get real time at start of run. */
 | 
				
			||||||
#if _VMD_EXT
 | 
					#if _VMD_EXT
 | 
				
			||||||
@ -55,6 +72,7 @@ char *argv[];
 | 
				
			|||||||
#else
 | 
					#else
 | 
				
			||||||
  start_time = times(&dummy);
 | 
					  start_time = times(&dummy);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					  read_tsc_64(&start_tsc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Fork off child. */
 | 
					  /* Fork off child. */
 | 
				
			||||||
  if ((pid = fork()) < 0) {
 | 
					  if ((pid = fork()) < 0) {
 | 
				
			||||||
@ -70,6 +88,8 @@ char *argv[];
 | 
				
			|||||||
  do {
 | 
					  do {
 | 
				
			||||||
	times(&pre_buf);
 | 
						times(&pre_buf);
 | 
				
			||||||
  } while (wait(&status) != pid);
 | 
					  } while (wait(&status) != pid);
 | 
				
			||||||
 | 
					  read_tsc_64(&end_tsc);
 | 
				
			||||||
 | 
					  spent_tsc = sub64(end_tsc, start_tsc);
 | 
				
			||||||
#if _VMD_EXT
 | 
					#if _VMD_EXT
 | 
				
			||||||
  (void) sysutime(UTIME_TIMEOFDAY, &end_time);
 | 
					  (void) sysutime(UTIME_TIMEOFDAY, &end_time);
 | 
				
			||||||
  real_time = (end_time.tv_sec - start_time.tv_sec) * CLOCKS_PER_SEC
 | 
					  real_time = (end_time.tv_sec - start_time.tv_sec) * CLOCKS_PER_SEC
 | 
				
			||||||
@ -82,6 +102,9 @@ char *argv[];
 | 
				
			|||||||
  if ((status & 0377) != 0) std_err("Command terminated abnormally.\n");
 | 
					  if ((status & 0377) != 0) std_err("Command terminated abnormally.\n");
 | 
				
			||||||
  times(&post_buf);
 | 
					  times(&post_buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if(cycles) {
 | 
				
			||||||
 | 
					  	fprintf(stderr, "%qd tsc ", spent_tsc);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  /* Print results. -DNEW enables time on one line to 0.01 sec */
 | 
					  /* Print results. -DNEW enables time on one line to 0.01 sec */
 | 
				
			||||||
#ifndef NEW
 | 
					#ifndef NEW
 | 
				
			||||||
  std_err("real ");
 | 
					  std_err("real ");
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								commands/worldstone/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								commands/worldstone/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					SCRIPTS= worldstone.sh
 | 
				
			||||||
 | 
					MAN= worldstone.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.include <bsd.prog.mk>
 | 
				
			||||||
							
								
								
									
										85
									
								
								commands/worldstone/worldstone.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								commands/worldstone/worldstone.1
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					.Dd $Mdocdate: September 22 2011 $
 | 
				
			||||||
 | 
					.Dt WORLDSTONE 1
 | 
				
			||||||
 | 
					.Os
 | 
				
			||||||
 | 
					.Sh NAME
 | 
				
			||||||
 | 
					.Nm worldstone
 | 
				
			||||||
 | 
					.Nd shell script to consistently execute benchmarks
 | 
				
			||||||
 | 
					.Sh SYNOPSIS
 | 
				
			||||||
 | 
					.Nm worldstone
 | 
				
			||||||
 | 
					.Op Fl n Ar iterations
 | 
				
			||||||
 | 
					.Op Fl c Ar command
 | 
				
			||||||
 | 
					.Op Fl p Ar command
 | 
				
			||||||
 | 
					.Op Fl t Ar tag
 | 
				
			||||||
 | 
					.Sh DESCRIPTION
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					utility is a shell script and takes care of some of the
 | 
				
			||||||
 | 
					grunt work around benchmarking, in order to make it easier
 | 
				
			||||||
 | 
					to get consistent and comparable benchmark results. Its basic
 | 
				
			||||||
 | 
					operation is: execute the precommand, then execute and time
 | 
				
			||||||
 | 
					the command, and do this a set number of iterations, and record
 | 
				
			||||||
 | 
					the times in a logfile.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Its features are:
 | 
				
			||||||
 | 
					.Bl -tag -width Ds
 | 
				
			||||||
 | 
					.It -
 | 
				
			||||||
 | 
					It executes the precommand and command once without timing it
 | 
				
			||||||
 | 
					in order to mitigate cold cache effects.
 | 
				
			||||||
 | 
					.It -
 | 
				
			||||||
 | 
					It allows a precommand to run before the command, so that the initial
 | 
				
			||||||
 | 
					state can be set up by the precommand without it being part of the timing
 | 
				
			||||||
 | 
					(e.g. make clean).
 | 
				
			||||||
 | 
					.It -
 | 
				
			||||||
 | 
					It redirects the stdout and stderr to /dev/null so that lots of output
 | 
				
			||||||
 | 
					going over a network connection doesn't influence timing.
 | 
				
			||||||
 | 
					.It -
 | 
				
			||||||
 | 
					It does a sync before running the timed command, and makes sure a final
 | 
				
			||||||
 | 
					sync is part of the timed command, to make the i/o more consistent.
 | 
				
			||||||
 | 
					.It -
 | 
				
			||||||
 | 
					It logs the times of each iteration in an easy-to-parse logfile.
 | 
				
			||||||
 | 
					.It -
 | 
				
			||||||
 | 
					It tries to guess a sensible log file name based on the current git
 | 
				
			||||||
 | 
					branch in /usr/src.
 | 
				
			||||||
 | 
					.El
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The options are as follows:
 | 
				
			||||||
 | 
					.Bl -tag -width Ds
 | 
				
			||||||
 | 
					.It Fl n Ar iterations
 | 
				
			||||||
 | 
					Set the number of iterations to perform, after the initial run.
 | 
				
			||||||
 | 
					The default is 5.
 | 
				
			||||||
 | 
					.It Fl c Ar command
 | 
				
			||||||
 | 
					Set the command to run to be timed. This is passed to sh -c, so shell constructs
 | 
				
			||||||
 | 
					like loops etc. are okay to do. Default: make all.
 | 
				
			||||||
 | 
					.It Fl p Ar command
 | 
				
			||||||
 | 
					Set the pre-command to run. This command gets run before the timed command in order
 | 
				
			||||||
 | 
					to make the timed command get a consistent state before it starts.
 | 
				
			||||||
 | 
					Default: make clean.
 | 
				
			||||||
 | 
					.It Fl t Ar tag
 | 
				
			||||||
 | 
					Use the given tag name to modify the logfile that the utility uses
 | 
				
			||||||
 | 
					to write its results in. The default is just 'time' plus the git branch you
 | 
				
			||||||
 | 
					are currently on in /usr/src. In order for this to be useful you have to make sure the
 | 
				
			||||||
 | 
					git branch you are on reflects the system you wish to benchmark of course.
 | 
				
			||||||
 | 
					The script checks /usr/src/.git even if you are outside the /usr/src hierarchy
 | 
				
			||||||
 | 
					(such as in pkgsrc).
 | 
				
			||||||
 | 
					.El
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The script executes the commands the set number of iterations, redirecting stdout
 | 
				
			||||||
 | 
					and stderr to /dev/null, and records the timed results in the logfile tagged with
 | 
				
			||||||
 | 
					the given tag.
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					executes
 | 
				
			||||||
 | 
					.Xr time 1
 | 
				
			||||||
 | 
					with the -C option, resulting in printing the 64-bit cpu cycle counter
 | 
				
			||||||
 | 
					for both HZ-independent high resolution and an easy way not to have to convert minutes
 | 
				
			||||||
 | 
					and seconds to seconds when parsing the results.
 | 
				
			||||||
 | 
					.Pp
 | 
				
			||||||
 | 
					You can feed the two separate logfiles directly to
 | 
				
			||||||
 | 
					.Xr ministat 1
 | 
				
			||||||
 | 
					to have it tell you the statistical properties of the two datasets, and judge whether
 | 
				
			||||||
 | 
					there is a statistically significant difference.
 | 
				
			||||||
 | 
					.Sh ENVIRONMENT
 | 
				
			||||||
 | 
					The default commands, i.e. make all and make clean, can be modified by supplying a MAKE
 | 
				
			||||||
 | 
					environment variable, so e.g. MAKE=bmake worldstone still does something sensible
 | 
				
			||||||
 | 
					by default in /usr/pkgsrc directories.
 | 
				
			||||||
 | 
					.Sh SEE ALSO
 | 
				
			||||||
 | 
					.Xr ministat 1
 | 
				
			||||||
							
								
								
									
										56
									
								
								commands/worldstone/worldstone.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								commands/worldstone/worldstone.sh
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					if [ ! "$MAKE" ]; then	MAKE=make; fi
 | 
				
			||||||
 | 
					ITERATIONS=5
 | 
				
			||||||
 | 
					PRECMD="$MAKE clean"
 | 
				
			||||||
 | 
					COMMAND="$MAKE all"
 | 
				
			||||||
 | 
					TAG=time.$(basename $(git --git-dir=/usr/src/.git describe --all --dirty))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while getopts "n:d:p:c:r:" c
 | 
				
			||||||
 | 
					do
 | 
				
			||||||
 | 
					        case "$c" in
 | 
				
			||||||
 | 
							n)	ITERATIONS=$OPTARG ;;
 | 
				
			||||||
 | 
							p)	PRECMD="$OPTARG" ;;
 | 
				
			||||||
 | 
							c)	COMMAND="$OPTARG" ;;
 | 
				
			||||||
 | 
							t)	TAG=$OPTARG	;;
 | 
				
			||||||
 | 
							r)	echo "Reading settings from $OPTARG"; cat $OPTARG; . $OPTARG ; echo "Reading done.";;
 | 
				
			||||||
 | 
							*)	exit 1	;;
 | 
				
			||||||
 | 
						esac
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CONFIGPREFIX=".worldstone"
 | 
				
			||||||
 | 
					CONFIGVARS="ITERATIONS PRECMD COMMAND MAKE"
 | 
				
			||||||
 | 
					TMPF=.worldstone.tmpconfig.$$
 | 
				
			||||||
 | 
					rm -f $TMPF
 | 
				
			||||||
 | 
					for d in $CONFIGVARS
 | 
				
			||||||
 | 
					do	eval "echo $d=\\\"\$$d\\\"" >>$TMPF
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					CONFIGTAG=`crc <$TMPF | awk '{ print $1 }'`
 | 
				
			||||||
 | 
					CONFIGFILE=$CONFIGPREFIX.$CONFIGTAG
 | 
				
			||||||
 | 
					mv -f $TMPF $CONFIGFILE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGFILE=$TAG.worldstone.log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while [ -f $LOGFILE ]
 | 
				
			||||||
 | 
					do	echo "$0: WARNING: $LOGFILE already exists, appending."
 | 
				
			||||||
 | 
						LOGFILE=$LOGFILE.next
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "Logging to $LOGFILE."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "First run."
 | 
				
			||||||
 | 
					sh -c "$PRECMD"
 | 
				
			||||||
 | 
					sh -c "$COMMAND"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for n in `seq 1 $ITERATIONS`
 | 
				
			||||||
 | 
					do	echo -n "$n"
 | 
				
			||||||
 | 
						sh -c "$PRECMD >/dev/null 2>&1"
 | 
				
			||||||
 | 
						echo -n "."
 | 
				
			||||||
 | 
						sync
 | 
				
			||||||
 | 
						time -C sh -c "$COMMAND >/dev/null 2>&1; sync" 2>>$LOGFILE
 | 
				
			||||||
 | 
						echo -n " "
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					echo "Done."
 | 
				
			||||||
 | 
					echo "Time measurements logfile is $LOGFILE."
 | 
				
			||||||
 | 
					echo "Config file is $CONFIGFILE."
 | 
				
			||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
.SH NAME
 | 
					.SH NAME
 | 
				
			||||||
time \- report how long a command takes
 | 
					time \- report how long a command takes
 | 
				
			||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
\fBtime \fIcommand\fR
 | 
					\fBtime [-C] \fIcommand\fR
 | 
				
			||||||
.br
 | 
					.br
 | 
				
			||||||
.de FL
 | 
					.de FL
 | 
				
			||||||
.TP
 | 
					.TP
 | 
				
			||||||
@ -14,6 +14,8 @@ time \- report how long a command takes
 | 
				
			|||||||
\\fB\\$1\\fR
 | 
					\\fB\\$1\\fR
 | 
				
			||||||
# \\$2
 | 
					# \\$2
 | 
				
			||||||
..
 | 
					..
 | 
				
			||||||
 | 
					The -C option tells time to report the cpu cycle counter
 | 
				
			||||||
 | 
					difference.
 | 
				
			||||||
.SH EXAMPLES
 | 
					.SH EXAMPLES
 | 
				
			||||||
.EX "time a.out" "Report how long \fIa.out\fR takes"
 | 
					.EX "time a.out" "Report how long \fIa.out\fR takes"
 | 
				
			||||||
.EX "time ls \-l *.c" "Report how long \fIls\fR takes"
 | 
					.EX "time ls \-l *.c" "Report how long \fIls\fR takes"
 | 
				
			||||||
 | 
				
			|||||||
@ -13,3 +13,4 @@ usr.bin/stat		src/usr.bin/stat
 | 
				
			|||||||
usr.bin/tic		src/usr.bin/tic
 | 
					usr.bin/tic		src/usr.bin/tic
 | 
				
			||||||
usr.bin/mkdep		src/usr.bin/mkdep
 | 
					usr.bin/mkdep		src/usr.bin/mkdep
 | 
				
			||||||
usr.bin/uniq		src/usr.bin/uniq
 | 
					usr.bin/uniq		src/usr.bin/uniq
 | 
				
			||||||
 | 
					usr.bin/seq		src/usr.bin/seq
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
.include <bsd.own.mk>
 | 
					.include <bsd.own.mk>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# NetBSD imports
 | 
					# NetBSD imports
 | 
				
			||||||
SUBDIR= indent m4 stat tic sed mkdep uniq
 | 
					SUBDIR= indent m4 stat tic sed mkdep uniq seq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Non-NetBSD imports
 | 
					# Non-NetBSD imports
 | 
				
			||||||
SUBDIR+= ministat
 | 
					SUBDIR+= ministat
 | 
				
			||||||
 | 
				
			|||||||
@ -215,14 +215,14 @@ static void
 | 
				
			|||||||
VitalsHead(void)
 | 
					VitalsHead(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("    N           Min           Max        Median           Avg        Stddev\n");
 | 
						printf("    N               Min               Max            Median               Avg            Stddev\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
Vitals(struct dataset *ds, int flag)
 | 
					Vitals(struct dataset *ds, int flag)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printf("%c %3d %13.8g %13.8g %13.8g %13.8g %13.8g", symbol[flag],
 | 
						printf("%c %3d %17.12g %17.12g %17.12g %17.12g %17.12g", symbol[flag],
 | 
				
			||||||
	    ds->n, Min(ds), Max(ds), Median(ds), Avg(ds), Stddev(ds));
 | 
						    ds->n, Min(ds), Max(ds), Median(ds), Avg(ds), Stddev(ds));
 | 
				
			||||||
	printf("\n");
 | 
						printf("\n");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										7
									
								
								usr.bin/seq/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usr.bin/seq/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					#       $NetBSD: Makefile,v 1.3 2009/04/14 22:15:26 lukem Exp $
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PROG=	seq
 | 
				
			||||||
 | 
					DPADD=	${LIBMATH}
 | 
				
			||||||
 | 
					LDADD=	-lm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.include <bsd.prog.mk>
 | 
				
			||||||
							
								
								
									
										187
									
								
								usr.bin/seq/seq.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								usr.bin/seq/seq.1
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,187 @@
 | 
				
			|||||||
 | 
					.\"	$NetBSD: seq.1,v 1.7 2010/05/27 08:30:35 dholland Exp $
 | 
				
			||||||
 | 
					.\"
 | 
				
			||||||
 | 
					.\" Copyright (c) 2005 The NetBSD Foundation, Inc.
 | 
				
			||||||
 | 
					.\" All rights reserved.
 | 
				
			||||||
 | 
					.\"
 | 
				
			||||||
 | 
					.\" This code is derived from software contributed to The NetBSD Foundation
 | 
				
			||||||
 | 
					.\" by Brian Ginsbach.
 | 
				
			||||||
 | 
					.\"
 | 
				
			||||||
 | 
					.\" Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					.\" modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					.\" are met:
 | 
				
			||||||
 | 
					.\" 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					.\"    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					.\" 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					.\"    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					.\"    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					.\"
 | 
				
			||||||
 | 
					.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 | 
				
			||||||
 | 
					.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | 
				
			||||||
 | 
					.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
				
			||||||
 | 
					.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 | 
				
			||||||
 | 
					.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
				
			||||||
 | 
					.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
				
			||||||
 | 
					.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
				
			||||||
 | 
					.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
				
			||||||
 | 
					.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
				
			||||||
 | 
					.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
				
			||||||
 | 
					.\" POSSIBILITY OF SUCH DAMAGE.
 | 
				
			||||||
 | 
					.\"
 | 
				
			||||||
 | 
					.\"
 | 
				
			||||||
 | 
					.Dd May 27, 2010
 | 
				
			||||||
 | 
					.Dt SEQ 1
 | 
				
			||||||
 | 
					.Os
 | 
				
			||||||
 | 
					.Sh NAME
 | 
				
			||||||
 | 
					.Nm seq
 | 
				
			||||||
 | 
					.Nd print sequences of numbers
 | 
				
			||||||
 | 
					.Sh SYNOPSIS
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					.Op Fl w
 | 
				
			||||||
 | 
					.Op Fl f Ar format
 | 
				
			||||||
 | 
					.Op Fl s Ar string
 | 
				
			||||||
 | 
					.Op Fl t Ar string
 | 
				
			||||||
 | 
					.Op Ar first Op Ar incr
 | 
				
			||||||
 | 
					.Ar last
 | 
				
			||||||
 | 
					.Sh DESCRIPTION
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					utility prints a sequence of numbers, one per line
 | 
				
			||||||
 | 
					.Pq default ,
 | 
				
			||||||
 | 
					from
 | 
				
			||||||
 | 
					.Ar first
 | 
				
			||||||
 | 
					.Pq default 1 ,
 | 
				
			||||||
 | 
					to near
 | 
				
			||||||
 | 
					.Ar last
 | 
				
			||||||
 | 
					as possible, in increments of
 | 
				
			||||||
 | 
					.Ar incr
 | 
				
			||||||
 | 
					.Pq default 1 .
 | 
				
			||||||
 | 
					When
 | 
				
			||||||
 | 
					.Ar first
 | 
				
			||||||
 | 
					is larger than
 | 
				
			||||||
 | 
					.Ar last
 | 
				
			||||||
 | 
					the default
 | 
				
			||||||
 | 
					.Ar incr
 | 
				
			||||||
 | 
					is -1.
 | 
				
			||||||
 | 
					.Pp
 | 
				
			||||||
 | 
					All numbers are interpreted as floating point.
 | 
				
			||||||
 | 
					.Pp
 | 
				
			||||||
 | 
					Normally integer values are printed as decimal integers.
 | 
				
			||||||
 | 
					.Pp
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					utility accepts the following options:
 | 
				
			||||||
 | 
					.Bl -tag -width Ar
 | 
				
			||||||
 | 
					.It Fl f Ar format
 | 
				
			||||||
 | 
					Use a
 | 
				
			||||||
 | 
					.Xr printf 3
 | 
				
			||||||
 | 
					style
 | 
				
			||||||
 | 
					.Ar format
 | 
				
			||||||
 | 
					to print each number.
 | 
				
			||||||
 | 
					Only the
 | 
				
			||||||
 | 
					.Cm A ,
 | 
				
			||||||
 | 
					.Cm a ,
 | 
				
			||||||
 | 
					.Cm E ,
 | 
				
			||||||
 | 
					.Cm e ,
 | 
				
			||||||
 | 
					.Cm F ,
 | 
				
			||||||
 | 
					.Cm f ,
 | 
				
			||||||
 | 
					.Cm G ,
 | 
				
			||||||
 | 
					.Cm g ,
 | 
				
			||||||
 | 
					and
 | 
				
			||||||
 | 
					.Cm %
 | 
				
			||||||
 | 
					conversion characters are valid, along with any optional
 | 
				
			||||||
 | 
					flags and an optional numeric mimimum field width or precision.
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Ar format
 | 
				
			||||||
 | 
					can contain character escape sequences in backslash notation as
 | 
				
			||||||
 | 
					defined in
 | 
				
			||||||
 | 
					.St -ansiC .
 | 
				
			||||||
 | 
					The default is
 | 
				
			||||||
 | 
					.Cm %g .
 | 
				
			||||||
 | 
					.It Fl s Ar string
 | 
				
			||||||
 | 
					Use
 | 
				
			||||||
 | 
					.Ar string
 | 
				
			||||||
 | 
					to separate numbers.
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Ar string
 | 
				
			||||||
 | 
					can contain character escape sequences in backslash notation as
 | 
				
			||||||
 | 
					defined in
 | 
				
			||||||
 | 
					.St -ansiC .
 | 
				
			||||||
 | 
					The default is
 | 
				
			||||||
 | 
					.Cm \en .
 | 
				
			||||||
 | 
					.It Fl t Ar string
 | 
				
			||||||
 | 
					Use
 | 
				
			||||||
 | 
					.Ar string
 | 
				
			||||||
 | 
					to terminate sequence of numbers.
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Ar string
 | 
				
			||||||
 | 
					can contain character escape sequences in backslash notation as
 | 
				
			||||||
 | 
					defined in
 | 
				
			||||||
 | 
					.St -ansiC .
 | 
				
			||||||
 | 
					This option is useful when the default separator
 | 
				
			||||||
 | 
					does not contain a
 | 
				
			||||||
 | 
					.Cm \en .
 | 
				
			||||||
 | 
					.It Fl w
 | 
				
			||||||
 | 
					Equalize the widths of all numbers by padding with zeros as necessary.
 | 
				
			||||||
 | 
					This option has no effect with the
 | 
				
			||||||
 | 
					.Fl f
 | 
				
			||||||
 | 
					option.
 | 
				
			||||||
 | 
					If any sequence numbers will be printed in exponential notation,
 | 
				
			||||||
 | 
					the default conversion is changed to
 | 
				
			||||||
 | 
					.Cm %e .
 | 
				
			||||||
 | 
					.El
 | 
				
			||||||
 | 
					.Pp
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					utility exits 0 on success and non-zero if an error occurs.
 | 
				
			||||||
 | 
					.Sh EXAMPLES
 | 
				
			||||||
 | 
					.Bd -literal -offset indent
 | 
				
			||||||
 | 
					# seq 1 3
 | 
				
			||||||
 | 
					1
 | 
				
			||||||
 | 
					2
 | 
				
			||||||
 | 
					3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# seq 3 1
 | 
				
			||||||
 | 
					3
 | 
				
			||||||
 | 
					2
 | 
				
			||||||
 | 
					1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# seq -w 0 .05 .1
 | 
				
			||||||
 | 
					0.00
 | 
				
			||||||
 | 
					0.05
 | 
				
			||||||
 | 
					0.10
 | 
				
			||||||
 | 
					.Ed
 | 
				
			||||||
 | 
					.Sh SEE ALSO
 | 
				
			||||||
 | 
					.Xr jot 1 ,
 | 
				
			||||||
 | 
					.Xr printf 1 ,
 | 
				
			||||||
 | 
					.Xr printf 3
 | 
				
			||||||
 | 
					.Sh HISTORY
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					command first appeared in
 | 
				
			||||||
 | 
					.Tn "Plan 9 from Bell Labs" .
 | 
				
			||||||
 | 
					A
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					command appeared in
 | 
				
			||||||
 | 
					.Nx 3.0 .
 | 
				
			||||||
 | 
					This command was based on the command of the same name in
 | 
				
			||||||
 | 
					.Tn "Plan 9 from Bell Labs"
 | 
				
			||||||
 | 
					and the
 | 
				
			||||||
 | 
					.Tn GNU
 | 
				
			||||||
 | 
					core utilities.
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Tn GNU
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					command first appeared in the 1.13 shell utilities release.
 | 
				
			||||||
 | 
					.Sh BUGS
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Fl w
 | 
				
			||||||
 | 
					option does not handle the transition from pure floating point
 | 
				
			||||||
 | 
					to exponent representation very well.
 | 
				
			||||||
 | 
					The
 | 
				
			||||||
 | 
					.Nm
 | 
				
			||||||
 | 
					command is not bug for bug compatible with the
 | 
				
			||||||
 | 
					.Tn "Plan 9 from Bell Labs"
 | 
				
			||||||
 | 
					or
 | 
				
			||||||
 | 
					.Tn GNU
 | 
				
			||||||
 | 
					versions of
 | 
				
			||||||
 | 
					.Nm .
 | 
				
			||||||
							
								
								
									
										474
									
								
								usr.bin/seq/seq.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								usr.bin/seq/seq.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,474 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2005 The NetBSD Foundation, Inc.
 | 
				
			||||||
 | 
					 * All rights reserved.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This code is derived from software contributed to The NetBSD Foundation
 | 
				
			||||||
 | 
					 * by Brian Ginsbach.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 | 
				
			||||||
 | 
					 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 | 
				
			||||||
 | 
					 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | 
				
			||||||
 | 
					 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 | 
				
			||||||
 | 
					 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
				
			||||||
 | 
					 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
				
			||||||
 | 
					 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
				
			||||||
 | 
					 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
				
			||||||
 | 
					 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
				
			||||||
 | 
					 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
				
			||||||
 | 
					 * POSSIBILITY OF SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sys/cdefs.h>
 | 
				
			||||||
 | 
					#ifndef lint
 | 
				
			||||||
 | 
					__COPYRIGHT("@(#) Copyright (c) 2005\
 | 
				
			||||||
 | 
					 The NetBSD Foundation, Inc.  All rights reserved.");
 | 
				
			||||||
 | 
					__RCSID("$NetBSD: seq.c,v 1.7 2010/05/27 08:40:19 dholland Exp $");
 | 
				
			||||||
 | 
					#endif /* not lint */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <ctype.h>
 | 
				
			||||||
 | 
					#include <err.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					#include <locale.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ZERO	'0'
 | 
				
			||||||
 | 
					#define SPACE	' '
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX(a, b)	(((a) < (b))? (b) : (a))
 | 
				
			||||||
 | 
					#define ISSIGN(c)	((int)(c) == '-' || (int)(c) == '+')
 | 
				
			||||||
 | 
					#define ISEXP(c)	((int)(c) == 'e' || (int)(c) == 'E')
 | 
				
			||||||
 | 
					#define ISODIGIT(c)	((int)(c) >= '0' && (int)(c) <= '7')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Globals */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *decimal_point = ".";	/* default */
 | 
				
			||||||
 | 
					char default_format[] = { "%g" };	/* default */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Prototypes */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double e_atof(const char *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int decimal_places(const char *);
 | 
				
			||||||
 | 
					int main(int, char *[]);
 | 
				
			||||||
 | 
					int numeric(const char *);
 | 
				
			||||||
 | 
					int valid_format(const char *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					char *generate_format(double, double, double, int, char);
 | 
				
			||||||
 | 
					char *unescape(char *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * The seq command will print out a numeric sequence from 1, the default,
 | 
				
			||||||
 | 
					 * to a user specified upper limit by 1.  The lower bound and increment
 | 
				
			||||||
 | 
					 * maybe indicated by the user on the command line.  The sequence can
 | 
				
			||||||
 | 
					 * be either whole, the default, or decimal numbers.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					main(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int c = 0, errflg = 0;
 | 
				
			||||||
 | 
						int equalize = 0;
 | 
				
			||||||
 | 
						double first = 1.0;
 | 
				
			||||||
 | 
						double last = 0.0;
 | 
				
			||||||
 | 
						double incr = 0.0;
 | 
				
			||||||
 | 
						struct lconv *locale;
 | 
				
			||||||
 | 
						char *fmt = NULL;
 | 
				
			||||||
 | 
						const char *sep = "\n";
 | 
				
			||||||
 | 
						const char *term = NULL;
 | 
				
			||||||
 | 
						char pad = ZERO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Determine the locale's decimal point. */
 | 
				
			||||||
 | 
						locale = localeconv();
 | 
				
			||||||
 | 
						if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
 | 
				
			||||||
 | 
							decimal_point = locale->decimal_point;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
					         * Process options, but handle negative numbers separately
 | 
				
			||||||
 | 
					         * least they trip up getopt(3).
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
						while ((optind < argc) && !numeric(argv[optind]) &&
 | 
				
			||||||
 | 
						    (c = getopt(argc, argv, "f:hs:t:w")) != -1) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (c) {
 | 
				
			||||||
 | 
							case 'f':	/* format (plan9) */
 | 
				
			||||||
 | 
								fmt = optarg;
 | 
				
			||||||
 | 
								equalize = 0;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case 's':	/* separator (GNU) */
 | 
				
			||||||
 | 
								sep = unescape(optarg);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case 't':	/* terminator (new) */
 | 
				
			||||||
 | 
								term = unescape(optarg);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case 'w':	/* equal width (plan9) */
 | 
				
			||||||
 | 
								if (!fmt)
 | 
				
			||||||
 | 
									if (equalize++)
 | 
				
			||||||
 | 
										pad = SPACE;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case 'h':	/* help (GNU) */
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								errflg++;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						argc -= optind;
 | 
				
			||||||
 | 
						argv += optind;
 | 
				
			||||||
 | 
						if (argc < 1 || argc > 3)
 | 
				
			||||||
 | 
							errflg++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (errflg) {
 | 
				
			||||||
 | 
							fprintf(stderr,
 | 
				
			||||||
 | 
							    "usage: %s [-w] [-f format] [-s string] [-t string] [first [incr]] last\n",
 | 
				
			||||||
 | 
							    getprogname());
 | 
				
			||||||
 | 
							exit(1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						last = e_atof(argv[argc - 1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (argc > 1)
 | 
				
			||||||
 | 
							first = e_atof(argv[0]);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if (argc > 2) {
 | 
				
			||||||
 | 
							incr = e_atof(argv[1]);
 | 
				
			||||||
 | 
							/* Plan 9/GNU don't do zero */
 | 
				
			||||||
 | 
							if (incr == 0.0)
 | 
				
			||||||
 | 
								errx(1, "zero %screment", (first < last)? "in" : "de");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* default is one for Plan 9/GNU work alike */
 | 
				
			||||||
 | 
						if (incr == 0.0)
 | 
				
			||||||
 | 
							incr = (first < last) ? 1.0 : -1.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (incr <= 0.0 && first < last)
 | 
				
			||||||
 | 
							errx(1, "needs positive increment");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (incr >= 0.0 && first > last)
 | 
				
			||||||
 | 
							errx(1, "needs negative decrement");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fmt != NULL) {
 | 
				
			||||||
 | 
							if (!valid_format(fmt))
 | 
				
			||||||
 | 
								errx(1, "invalid format string: `%s'", fmt);
 | 
				
			||||||
 | 
							fmt = unescape(fmt);
 | 
				
			||||||
 | 
							if (!valid_format(fmt))
 | 
				
			||||||
 | 
								errx(1, "invalid format string");
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
						         * XXX to be bug for bug compatible with Plan 9 add a
 | 
				
			||||||
 | 
							 * newline if none found at the end of the format string.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							fmt = generate_format(first, incr, last, equalize, pad);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (incr > 0) {
 | 
				
			||||||
 | 
							for (; first <= last; first += incr) {
 | 
				
			||||||
 | 
								printf(fmt, first);
 | 
				
			||||||
 | 
								fputs(sep, stdout);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							for (; first >= last; first += incr) {
 | 
				
			||||||
 | 
								printf(fmt, first);
 | 
				
			||||||
 | 
								fputs(sep, stdout);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (term != NULL)
 | 
				
			||||||
 | 
							fputs(term, stdout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * numeric - verify that string is numeric
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					numeric(const char *s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int seen_decimal_pt, decimal_pt_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* skip any sign */
 | 
				
			||||||
 | 
						if (ISSIGN((unsigned char)*s))
 | 
				
			||||||
 | 
							s++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						seen_decimal_pt = 0;
 | 
				
			||||||
 | 
						decimal_pt_len = strlen(decimal_point);
 | 
				
			||||||
 | 
						while (*s) {
 | 
				
			||||||
 | 
							if (!isdigit((unsigned char)*s)) {
 | 
				
			||||||
 | 
								if (!seen_decimal_pt &&
 | 
				
			||||||
 | 
								    strncmp(s, decimal_point, decimal_pt_len) == 0) {
 | 
				
			||||||
 | 
									s += decimal_pt_len;
 | 
				
			||||||
 | 
									seen_decimal_pt = 1;
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (ISEXP((unsigned char)*s)) {
 | 
				
			||||||
 | 
									s++;
 | 
				
			||||||
 | 
									if (ISSIGN((unsigned char)*s)) {
 | 
				
			||||||
 | 
										s++;
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							s++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return (*s == '\0');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * valid_format - validate user specified format string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					valid_format(const char *fmt)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned conversions = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (*fmt != '\0') {
 | 
				
			||||||
 | 
							/* scan for conversions */
 | 
				
			||||||
 | 
							if (*fmt != '%') {
 | 
				
			||||||
 | 
								fmt++;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fmt++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* allow %% but not things like %10% */
 | 
				
			||||||
 | 
							if (*fmt == '%') {
 | 
				
			||||||
 | 
								fmt++;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* flags */
 | 
				
			||||||
 | 
							while (*fmt != '\0' && strchr("#0- +'", *fmt)) {
 | 
				
			||||||
 | 
								fmt++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* field width */
 | 
				
			||||||
 | 
							while (*fmt != '\0' && strchr("0123456789", *fmt)) {
 | 
				
			||||||
 | 
								fmt++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* precision */
 | 
				
			||||||
 | 
							if (*fmt == '.') {
 | 
				
			||||||
 | 
								fmt++;
 | 
				
			||||||
 | 
								while (*fmt != '\0' && strchr("0123456789", *fmt)) {
 | 
				
			||||||
 | 
									fmt++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* conversion */
 | 
				
			||||||
 | 
							switch (*fmt) {
 | 
				
			||||||
 | 
							    case 'A':
 | 
				
			||||||
 | 
							    case 'a':
 | 
				
			||||||
 | 
							    case 'E':
 | 
				
			||||||
 | 
							    case 'e':
 | 
				
			||||||
 | 
							    case 'F':
 | 
				
			||||||
 | 
							    case 'f':
 | 
				
			||||||
 | 
							    case 'G':
 | 
				
			||||||
 | 
							    case 'g':
 | 
				
			||||||
 | 
								/* floating point formats are accepted */
 | 
				
			||||||
 | 
								conversions++;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							    default:
 | 
				
			||||||
 | 
								/* anything else is not */
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (conversions <= 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * unescape - handle C escapes in a string
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					char *
 | 
				
			||||||
 | 
					unescape(char *orig)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char c, *cp, *new = orig;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (cp = orig; (*orig = *cp); cp++, orig++) {
 | 
				
			||||||
 | 
							if (*cp != '\\')
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (*++cp) {
 | 
				
			||||||
 | 
							case 'a':	/* alert (bell) */
 | 
				
			||||||
 | 
								*orig = '\a';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case 'b':	/* backspace */
 | 
				
			||||||
 | 
								*orig = '\b';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case 'e':	/* escape */
 | 
				
			||||||
 | 
								*orig = '\e';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case 'f':	/* formfeed */
 | 
				
			||||||
 | 
								*orig = '\f';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case 'n':	/* newline */
 | 
				
			||||||
 | 
								*orig = '\n';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case 'r':	/* carriage return */
 | 
				
			||||||
 | 
								*orig = '\r';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case 't':	/* horizontal tab */
 | 
				
			||||||
 | 
								*orig = '\t';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case 'v':	/* vertical tab */
 | 
				
			||||||
 | 
								*orig = '\v';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case '\\':	/* backslash */
 | 
				
			||||||
 | 
								*orig = '\\';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case '\'':	/* single quote */
 | 
				
			||||||
 | 
								*orig = '\'';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case '\"':	/* double quote */
 | 
				
			||||||
 | 
								*orig = '"';
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case '0':
 | 
				
			||||||
 | 
							case '1':
 | 
				
			||||||
 | 
							case '2':
 | 
				
			||||||
 | 
							case '3':	/* octal */
 | 
				
			||||||
 | 
							case '4':
 | 
				
			||||||
 | 
							case '5':
 | 
				
			||||||
 | 
							case '6':
 | 
				
			||||||
 | 
							case '7':	/* number */
 | 
				
			||||||
 | 
								for (i = 0, c = 0;
 | 
				
			||||||
 | 
								     ISODIGIT((unsigned char)*cp) && i < 3;
 | 
				
			||||||
 | 
								     i++, cp++) {
 | 
				
			||||||
 | 
									c <<= 3;
 | 
				
			||||||
 | 
									c |= (*cp - '0');
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								*orig = c;
 | 
				
			||||||
 | 
								--cp;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							case 'x':	/* hexidecimal number */
 | 
				
			||||||
 | 
								cp++;	/* skip 'x' */
 | 
				
			||||||
 | 
								for (i = 0, c = 0;
 | 
				
			||||||
 | 
								     isxdigit((unsigned char)*cp) && i < 2;
 | 
				
			||||||
 | 
								     i++, cp++) {
 | 
				
			||||||
 | 
									c <<= 4;
 | 
				
			||||||
 | 
									if (isdigit((unsigned char)*cp))
 | 
				
			||||||
 | 
										c |= (*cp - '0');
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
										c |= ((toupper((unsigned char)*cp) -
 | 
				
			||||||
 | 
										    'A') + 10);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								*orig = c;
 | 
				
			||||||
 | 
								--cp;
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								--cp;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (new);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * e_atof - convert an ASCII string to a double
 | 
				
			||||||
 | 
					 *	exit if string is not a valid double, or if converted value would
 | 
				
			||||||
 | 
					 *	cause overflow or underflow
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					double
 | 
				
			||||||
 | 
					e_atof(const char *num)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *endp;
 | 
				
			||||||
 | 
						double dbl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errno = 0;
 | 
				
			||||||
 | 
						dbl = strtod(num, &endp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (errno == ERANGE)
 | 
				
			||||||
 | 
							/* under or overflow */
 | 
				
			||||||
 | 
							err(2, "%s", num);
 | 
				
			||||||
 | 
						else if (*endp != '\0')
 | 
				
			||||||
 | 
							/* "junk" left in number */
 | 
				
			||||||
 | 
							errx(2, "invalid floating point argument: %s", num);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* zero shall have no sign */
 | 
				
			||||||
 | 
						if (dbl == -0.0)
 | 
				
			||||||
 | 
							dbl = 0;
 | 
				
			||||||
 | 
						return (dbl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * decimal_places - count decimal places in a number (string)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					decimal_places(const char *number)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int places = 0;
 | 
				
			||||||
 | 
						char *dp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* look for a decimal point */
 | 
				
			||||||
 | 
						if ((dp = strstr(number, decimal_point))) {
 | 
				
			||||||
 | 
							dp += strlen(decimal_point);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (isdigit((unsigned char)*dp++))
 | 
				
			||||||
 | 
								places++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return (places);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * generate_format - create a format string
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * XXX to be bug for bug compatable with Plan9 and GNU return "%g"
 | 
				
			||||||
 | 
					 * when "%g" prints as "%e" (this way no width adjustments are made)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					char *
 | 
				
			||||||
 | 
					generate_format(double first, double incr, double last, int equalize, char pad)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static char buf[256];
 | 
				
			||||||
 | 
						char cc = '\0';
 | 
				
			||||||
 | 
						int precision, width1, width2, places;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (equalize == 0)
 | 
				
			||||||
 | 
							return (default_format);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* figure out "last" value printed */
 | 
				
			||||||
 | 
						if (first > last)
 | 
				
			||||||
 | 
							last = first - incr * floor((first - last) / incr);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							last = first + incr * floor((last - first) / incr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sprintf(buf, "%g", incr);
 | 
				
			||||||
 | 
						if (strchr(buf, 'e'))
 | 
				
			||||||
 | 
							cc = 'e';
 | 
				
			||||||
 | 
						precision = decimal_places(buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						width1 = sprintf(buf, "%g", first);
 | 
				
			||||||
 | 
						if (strchr(buf, 'e'))
 | 
				
			||||||
 | 
							cc = 'e';
 | 
				
			||||||
 | 
						if ((places = decimal_places(buf)))
 | 
				
			||||||
 | 
							width1 -= (places + strlen(decimal_point));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						precision = MAX(places, precision);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						width2 = sprintf(buf, "%g", last);
 | 
				
			||||||
 | 
						if (strchr(buf, 'e'))
 | 
				
			||||||
 | 
							cc = 'e';
 | 
				
			||||||
 | 
						if ((places = decimal_places(buf)))
 | 
				
			||||||
 | 
							width2 -= (places + strlen(decimal_point));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (precision) {
 | 
				
			||||||
 | 
							sprintf(buf, "%%%c%d.%d%c", pad,
 | 
				
			||||||
 | 
							    MAX(width1, width2) + (int) strlen(decimal_point) +
 | 
				
			||||||
 | 
							    precision, precision, (cc) ? cc : 'f');
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							sprintf(buf, "%%%c%d%c", pad, MAX(width1, width2),
 | 
				
			||||||
 | 
							    (cc) ? cc : 'g');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (buf);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user