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
 | 
			
		||||
.svn
 | 
			
		||||
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 \
 | 
			
		||||
	whereis which who write writeisofs fetch \
 | 
			
		||||
	xargs yacc yes zdump zic zmodem pkgin_cd \
 | 
			
		||||
	mktemp
 | 
			
		||||
	mktemp worldstone
 | 
			
		||||
 | 
			
		||||
.if ${COMPILER_TYPE} == "gnu"
 | 
			
		||||
SUBDIR += elf2aout
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
PROG=	time
 | 
			
		||||
MAN=
 | 
			
		||||
NEED_NBSDLIBC= yes
 | 
			
		||||
 | 
			
		||||
.include <bsd.prog.mk>
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <minix/minlib.h>
 | 
			
		||||
#include <minix/u64.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
/* -DNEW prints time to 0.01 sec. */
 | 
			
		||||
@ -33,7 +34,7 @@ int main(argc, argv)
 | 
			
		||||
int argc;
 | 
			
		||||
char *argv[];
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
  int cycles = 0;
 | 
			
		||||
  struct tms pre_buf, post_buf;
 | 
			
		||||
  int status, pid;
 | 
			
		||||
#if _VMD_EXT
 | 
			
		||||
@ -42,12 +43,28 @@ char *argv[];
 | 
			
		||||
  struct tms dummy;
 | 
			
		||||
  int start_time, end_time;
 | 
			
		||||
#endif
 | 
			
		||||
  u64_t start_tsc, end_tsc, spent_tsc;
 | 
			
		||||
  clock_t real_time;
 | 
			
		||||
  int c;
 | 
			
		||||
 | 
			
		||||
  if (argc == 1) exit(0);
 | 
			
		||||
 | 
			
		||||
  args = &argv[1];
 | 
			
		||||
  name = argv[1];
 | 
			
		||||
  while((c=getopt(argc, argv, "C")) != EOF) {
 | 
			
		||||
    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. */
 | 
			
		||||
#if _VMD_EXT
 | 
			
		||||
@ -55,6 +72,7 @@ char *argv[];
 | 
			
		||||
#else
 | 
			
		||||
  start_time = times(&dummy);
 | 
			
		||||
#endif
 | 
			
		||||
  read_tsc_64(&start_tsc);
 | 
			
		||||
 | 
			
		||||
  /* Fork off child. */
 | 
			
		||||
  if ((pid = fork()) < 0) {
 | 
			
		||||
@ -70,6 +88,8 @@ char *argv[];
 | 
			
		||||
  do {
 | 
			
		||||
	times(&pre_buf);
 | 
			
		||||
  } while (wait(&status) != pid);
 | 
			
		||||
  read_tsc_64(&end_tsc);
 | 
			
		||||
  spent_tsc = sub64(end_tsc, start_tsc);
 | 
			
		||||
#if _VMD_EXT
 | 
			
		||||
  (void) sysutime(UTIME_TIMEOFDAY, &end_time);
 | 
			
		||||
  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");
 | 
			
		||||
  times(&post_buf);
 | 
			
		||||
 | 
			
		||||
  if(cycles) {
 | 
			
		||||
  	fprintf(stderr, "%qd tsc ", spent_tsc);
 | 
			
		||||
  }
 | 
			
		||||
  /* Print results. -DNEW enables time on one line to 0.01 sec */
 | 
			
		||||
#ifndef NEW
 | 
			
		||||
  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
 | 
			
		||||
time \- report how long a command takes
 | 
			
		||||
.SH SYNOPSIS
 | 
			
		||||
\fBtime \fIcommand\fR
 | 
			
		||||
\fBtime [-C] \fIcommand\fR
 | 
			
		||||
.br
 | 
			
		||||
.de FL
 | 
			
		||||
.TP
 | 
			
		||||
@ -14,6 +14,8 @@ time \- report how long a command takes
 | 
			
		||||
\\fB\\$1\\fR
 | 
			
		||||
# \\$2
 | 
			
		||||
..
 | 
			
		||||
The -C option tells time to report the cpu cycle counter
 | 
			
		||||
difference.
 | 
			
		||||
.SH EXAMPLES
 | 
			
		||||
.EX "time a.out" "Report how long \fIa.out\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/mkdep		src/usr.bin/mkdep
 | 
			
		||||
usr.bin/uniq		src/usr.bin/uniq
 | 
			
		||||
usr.bin/seq		src/usr.bin/seq
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
.include <bsd.own.mk>
 | 
			
		||||
 | 
			
		||||
# NetBSD imports
 | 
			
		||||
SUBDIR= indent m4 stat tic sed mkdep uniq
 | 
			
		||||
SUBDIR= indent m4 stat tic sed mkdep uniq seq
 | 
			
		||||
 | 
			
		||||
# Non-NetBSD imports
 | 
			
		||||
SUBDIR+= ministat
 | 
			
		||||
 | 
			
		||||
@ -222,7 +222,7 @@ static void
 | 
			
		||||
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));
 | 
			
		||||
	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