Path: seismo!harvard!talcott!panda!sources-request From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: list-of-numbers generator Message-ID: <1618@panda.UUCP> Date: 7 Apr 86 16:56:24 GMT Sender: jpn@panda.UUCP Lines: 404 Approved: jpn@panda.UUCP Mod.sources: Volume 4, Issue 53 Submitted by: hplabs!hpfcla!ajs (Alan Silverstein) Greetings. Here's an almost-ultimate (?) implementation of a list-of-numbers generator. It's carefully coded, proof-read, tested, and documented. Of course I've only run it on local systems (HP9000s), so I can't guarantee ultimate portability. [ It uses the new system V random number generator functions - srand48 and lrand48. You should be able to replace srand48() with srand, and lrand48 with rand() to make this run on most other systems. - John Nelson, Moderator ] Alan Silverstein, Hewlett-Packard Fort Collins Systems Division, Colorado {ihnp4 | hplabs}!hpfcla!ajs, 303-226-3800 x3053, N 40 31'31" W 105 00'43" #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # nums.1 # nums.c # This archive created: Mon Apr 7 11:47:53 1986 export PATH; PATH=/bin:$PATH echo shar: extracting "'nums.1'" '(2704 characters)' if test -f 'nums.1' then echo shar: will not over-write existing file "'nums.1'" else cat << \SHAR_EOF > 'nums.1' .TH NUMS 1 "HP Experimental" .ad b .SH NAME nums \- print a list of numbers .SH SYNOPSIS .B nums [ .B \-lr ] [ .BI \-s \0size ] [ .BI \-w \0width ] .I value [ .I value2 ] .SH DESCRIPTION This command prints to standard output all integer values from 1 to .IR value , or if you give two numbers, from .I value to .IR value2 , one number per output line. If .I value alone is less than 1, or .I value2 is less than .IR value , it steps backwards rather than forwards. .PP This command may be useful with "for" loops in shell scripts (see example below) or for generating column headers or row numbers before adding text in an editor. .PP Options are: .TP .B \-l Print all output numbers on a single line, i.e. follow each except the last with a blank instead of a newline. .TP .B \-r Print the numbers in random order. A random number generator is seeded to the time plus process number plus real userid plus real groupid. Then successive random values are used to index into an array with one entry for each output value. When the index is of a value already printed, the program searches backward (with wrap-around) for the previous not-yet-printed number, so it terminates in a predictable time. .TP .BI \-s \0size Set the step size (increment) to a non-zero integer (default = 1). Starting with the first value, the program steps either forwards or backwards by the given .IR size , as appropriate, until it passes the second value. The sign of .I size is irrelevant; the direction is determined by the given value(s). .TP .BI \-w \0width Right-justify output numbers in fields of at least the given width (columns) by padding with leading blanks as necessary. If .I width is negative, numbers are left-justified. .PP Separate options from .I value with "\fB--\fR" if .I value is negative. .SH EXAMPLES .TP num 5 Print five lines containing the numbers 1, 2, 3, 4, and 5. .TP num \-l \-w4 \-\- -1 3 Print the numbers -1, 0, 1, 2, and 3 on a single line, each in a field four columns wide (plus leading blank separators), e.g.: .nf .RS \0\0-1 \0\0\00 \0\0\01 \0\0\02 \0\0\03 .RE .fi .TP num \-rs10 42 Print the numbers 1, 11, 21, 31, and 41 in random order. .PP .nf for n in `num 20 5` do whatever done .PD 0 .IP "" Do "whatever" 16 times, with "n" set to 20, 19, ..., 5. .PD .SH SEE ALSO xargs(1), time(2), getpid(2), getuid(2), getgid(2), drand48(3c) .SH DIAGNOSTICS Prints a message to standard error and exits non-zero if invoked wrong or it can't calloc() needed memory for the .B \-r option. .SH LIMITATIONS It doesn't understand real numbers. .PP If the number of numbers printed is not an integer divisor of the range of the random number generator, the output is not "precisely" random. SHAR_EOF if test 2704 -ne "`wc -c < 'nums.1'`" then echo shar: error transmitting "'nums.1'" '(should have been 2704 characters)' fi fi echo shar: extracting "'nums.c'" '(4958 characters)' if test -f 'nums.c' then echo shar: will not over-write existing file "'nums.c'" else cat << \SHAR_EOF > 'nums.c' /* * Print a list of numbers. * See the manual entry for details. */ #include /********************************************************************* * MISCELLANEOUS GLOBAL VALUES: */ #define PROC /* null; easy to find procs */ #define FALSE 0 #define TRUE 1 #define CHNULL ('\0') #define CPNULL ((char *) NULL) #define REG register char *usage[] = { "usage: %s [-lr] [-s size] [-w width] value [value2]", "-l print all one one line (default: one number per line)", "-r print numbers in random order", "-s set step size (increment, default = 1)", "-w print numbers in fields of given width (default: no padding)", "Prints numbers from 1 to value, or value to value2", CPNULL, }; char *myname; /* how program was invoked */ int lflag = FALSE; /* -l (all one line) option */ int rflag = FALSE; /* -r (randomize) option */ int step = 1; /* value from -s option */ int width = 0; /* value from -w option */ /************************************************************************ * M A I N */ PROC main (argc, argv) int argc; char **argv; { extern int optind; /* from getopt() */ extern char *optarg; /* from getopt() */ REG int option; /* option "letter" */ REG int value1 = 1; /* first value */ REG int value2; /* second value */ char format [20]; /* for printing numbers */ REG char sep; /* leading separator */ /* * PARSE ARGUMENTS: */ myname = *argv; while ((option = getopt (argc, argv, "lrs:w:")) != EOF) { switch (option) { case 'l': lflag = TRUE; break; case 'r': rflag = TRUE; break; case 's': step = atoi (optarg); break; case 'w': width = atoi (optarg); break; default: Usage(); } } argc -= optind; /* skip options */ argv += optind; /* * MORE ARGUMENT CHECKS: */ if (argc == 1) { value2 = atoi (argv [0]); } else if (argc == 2) { value1 = atoi (argv [0]); value2 = atoi (argv [1]); } else { Usage(); } if (step == 0) Error ("step size cannot be zero"); /* * FINISH INITIALIZING: */ if (((step < 0) && (value1 < value2)) || ((step > 0) && (value1 > value2))) /* harmless if values equal */ { step = -step; /* invert */ } sprintf (format, (width ? "%%%dd" : "%%d"), width); sep = lflag ? ' ' : '\n'; /* * PRINT NUMBERS: */ if (rflag) PrintRandom (value1, value2, format, sep); else { REG int forward = (step > 0); printf (format, value1); while (forward ? ((value1 += step) <= value2) : ((value1 += step) >= value2)) { putchar (sep); printf (format, value1); } } putchar ('\n'); /* finish last line */ exit (0); } /* main */ /************************************************************************ * P R I N T R A N D O M * * Given value limits, print format, leading separator, and global step size * (positive or negative matching value limits), print numbers in random * order. Don't print the trailing newline for the last line. */ PROC PrintRandom (value1, value2, format, sep) REG int value1; /* base limit */ int value2; /* end limit */ REG char *format; /* how to print */ REG char sep; /* leading char */ { REG int remain = (value2 - value1) / step; REG int count = remain + 1; /* total to print */ REG int *done; /* this number printed? */ REG int which; /* index in done[] */ /* * INITIALIZE: */ if ((done = (int *) calloc (count, sizeof (int))) == (int *) NULL) Error ("calloc %d integers failed", count); srand48 (time ((int *) 0) + getpid() + getuid() + getgid()); done [which = (lrand48() % count)] = TRUE; printf (format, value1 + (which * step)); /* first one */ /* * PRINT RANDOM NUMBERS: */ while (remain-- > 0) /* more to do */ { which = lrand48() % count; /* not *precisely* random */ while (done [which]) /* already taken */ { if (--which < 0) /* wrap around */ which = count - 1; } done [which] = TRUE; putchar (sep); printf (format, value1 + (which * step)); } } /* PrintRandom */ /************************************************************************ * U S A G E * * Print usage messages (char *usage[]) to stderr and exit nonzero. * Each message is followed by a newline. */ PROC Usage() { REG int which = 0; /* current line */ while (usage [which] != CPNULL) { fprintf (stderr, usage [which++], myname); putc ('\n', stderr); } exit (1); } /* Usage */ /************************************************************************ * E R R O R * * Print an error message to stderr and exit nonzero. Message is preceded * by ": " using global char *myname, and followed by a newline. */ /* VARARGS */ PROC Error (message, arg1, arg2, arg3, arg4) char *message; long arg1, arg2, arg3, arg4; { fprintf (stderr, "%s: ", myname); fprintf (stderr, message, arg1, arg2, arg3, arg4); putc ('\n', stderr); exit (1); } /* Error */ SHAR_EOF if test 4958 -ne "`wc -c < 'nums.c'`" then echo shar: error transmitting "'nums.c'" '(should have been 4958 characters)' fi fi exit 0 # End of shell archive