123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- /*++
- Copyright (c) 2016 Minoca Corp. All Rights Reserved
- Module Name:
- seq.c
- Abstract:
- This module implements the seq (sequence) utility, which simply prints out
- a sequence of numbers.
- Author:
- Evan Green 9-May-2016
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <getopt.h>
- #include <stdlib.h>
- #include <string.h>
- #include "swlib.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define SEQ_VERSION_MAJOR 1
- #define SEQ_VERSION_MINOR 0
- #define SEQ_USAGE \
- "usage: seq [options] [[first] increment] last\n" \
- "The seq utility prints a sequence of numbers between the given range.\n" \
- "Options are:\n" \
- " -f, --format=format -- Specify the printf-style format to print " \
- "values in.\n" \
- " -s, --separator=string -- Specify the separator (default newline).\n" \
- " -w, --equal-width -- Pad with leading zeros so all values have the \n" \
- " same width.\n" \
- " --help -- Show this help text and exit.\n" \
- " --version -- Print the application version information and exit.\n"
- //
- // No permutations of arguments are allowed because something like -1 might be
- // specified as an argument.
- //
- #define SEQ_OPTIONS_STRING "+f:s:whV"
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- BOOL
- SeqCheckFormat (
- PSTR Format
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option SeqLongOptions[] = {
- {"format", no_argument, 0, 'f'},
- {"separator", no_argument, 0, 's'},
- {"equal-width", no_argument, 0, 'w'},
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- SeqMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the seq utility.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- PSTR AfterScan;
- PSTR Argument;
- INT ArgumentIndex;
- double Current;
- INT CurrentFractionDigits;
- PSTR CurrentSeparator;
- INT CurrentWidth;
- PSTR Decimal;
- double End;
- BOOL EqualWidth;
- PSTR Format;
- INT FractionDigits;
- double Increment;
- INT Index;
- INT LastCheck;
- INT Option;
- PSTR Separator;
- double Start;
- int Status;
- INT Width;
- EqualWidth = FALSE;
- Format = NULL;
- Separator = "\n";
- Start = 1;
- Increment = 1;
- //
- // Process the control arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- SEQ_OPTIONS_STRING,
- SeqLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- //
- // Watch out for the possibility of a negative number as the first
- // argument.
- //
- if ((optind > 1) && (Arguments[optind - 1][0] == '-') &&
- (isdigit(Arguments[optind - 1][1]))) {
- optind -= 1;
- break;
- }
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- case 'f':
- Format = optarg;
- if (SeqCheckFormat(Format) == FALSE) {
- SwPrintError(0, Format, "Invalid format string");
- Status = 1;
- goto MainEnd;
- }
- break;
- case 's':
- Separator = optarg;
- break;
- case 'w':
- EqualWidth = TRUE;
- break;
- case 'V':
- SwPrintVersion(SEQ_VERSION_MAJOR, SEQ_VERSION_MINOR);
- return 1;
- case 'h':
- printf(SEQ_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- Status = 1;
- if ((Format != NULL) && (EqualWidth != FALSE)) {
- SwPrintError(0, NULL, "Cannot have -f and -w together");
- goto MainEnd;
- }
- ArgumentIndex = optind;
- if (ArgumentIndex >= ArgumentCount) {
- SwPrintError(0, NULL, "Argument expected");
- goto MainEnd;
- }
- LastCheck = ArgumentCount - 1;
- //
- // Get the end.
- //
- Argument = Arguments[ArgumentCount - 1];
- End = strtod(Argument, &AfterScan);
- if (*AfterScan != '\0') {
- SwPrintError(0, Argument, "Invalid value");
- goto MainEnd;
- }
- //
- // If there are more arguments, then there's a start. If there's a third
- // argument, there's an increment. Any more is an error.
- //
- if (ArgumentIndex + 1 < ArgumentCount) {
- //
- // For some reason the ending value isn't checked for width if there's
- // a start and/or increment.
- //
- LastCheck = ArgumentCount - 2;
- Argument = Arguments[ArgumentIndex];
- Start = strtod(Argument, &AfterScan);
- if (*AfterScan != '\0') {
- SwPrintError(0, Argument, "Invalid value");
- goto MainEnd;
- }
- if (ArgumentIndex + 2 < ArgumentCount) {
- if (ArgumentIndex + 3 < ArgumentCount) {
- SwPrintError(0, NULL, "Too many arguments");
- goto MainEnd;
- }
- Argument = Arguments[ArgumentIndex + 1];
- Increment = strtod(Argument, &AfterScan);
- if (*AfterScan != '\0') {
- SwPrintError(0, Argument, "Invalid value");
- goto MainEnd;
- }
- }
- }
- //
- // Figure out the proper width to print.
- //
- Width = 0;
- FractionDigits = 0;
- while (ArgumentIndex <= LastCheck) {
- Argument = Arguments[ArgumentIndex];
- Decimal = strchr(Argument, '.');
- if (Decimal == NULL) {
- CurrentWidth = strlen(Argument);
- CurrentFractionDigits = 0;
- } else {
- CurrentWidth = Decimal - Argument;
- CurrentFractionDigits = strlen(Decimal);
- }
- if (Width < CurrentWidth) {
- Width = CurrentWidth;
- }
- if (FractionDigits < CurrentFractionDigits) {
- FractionDigits = CurrentFractionDigits;
- }
- ArgumentIndex += 1;
- }
- if (FractionDigits != 0) {
- FractionDigits -= 1;
- if (FractionDigits != 0) {
- Width += FractionDigits + 1;
- }
- }
- if (EqualWidth == FALSE) {
- Width = 0;
- }
- Status = 0;
- Index = 0;
- Current = Start;
- CurrentSeparator = "";
- while (TRUE) {
- if (Increment >= 0) {
- if (Current > End) {
- break;
- }
- } else {
- if (Current < End) {
- break;
- }
- }
- //
- // Print with the given format if supplied, or the default format
- // otherwise.
- //
- if (Format != NULL) {
- if (printf("%s", CurrentSeparator) < 0) {
- Status = 1;
- break;
- }
- if (printf(Format, Current) < 0) {
- Status = 1;
- break;
- }
- } else {
- Status = printf("%s%0*.*f",
- CurrentSeparator,
- Width,
- FractionDigits,
- Current);
- if (Status < 0) {
- Status = 1;
- break;
- }
- }
- CurrentSeparator = Separator;
- Index += 1;
- Current = Start + (Index * Increment);
- }
- if (Index != 0) {
- printf("\n");
- }
- MainEnd:
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- BOOL
- SeqCheckFormat (
- PSTR Format
- )
- /*++
- Routine Description:
- This routine ensures the given format string is correct for a double
- argument.
- Arguments:
- Format - Supplies a pointer to the potential format string.
- Return Value:
- TRUE if the format is okay.
- FALSE if the format string is invalid.
- --*/
- {
- INT Specifiers;
- Specifiers = 0;
- while (*Format != '\0') {
- //
- // Skip anything that's not a format specifier.
- //
- if (*Format != '%') {
- Format += 1;
- continue;
- }
- Format += 1;
- //
- // Skip a literal percent.
- //
- if (*Format == '%') {
- Format += 1;
- continue;
- }
- //
- // Only one specifier is allowed.
- //
- Specifiers += 1;
- if (Specifiers > 1) {
- return FALSE;
- }
- //
- // Swallow the flags.
- //
- while ((*Format == '\'') || (*Format == '-') || (*Format == '+') ||
- (*Format == ' ') || (*Format == '#') || (*Format == '0')) {
- Format += 1;
- }
- //
- // Swallow the field width.
- //
- while (isdigit(*Format)) {
- Format += 1;
- }
- //
- // Swallow the precision.
- //
- if (*Format == '.') {
- Format += 1;
- while (isdigit(*Format)) {
- Format += 1;
- }
- }
- //
- // Now it had better be a specifier.
- //
- if ((*Format != 'f') && (*Format != 'F') && (*Format != 'e') &&
- (*Format != 'E') && (*Format != 'g') && (*Format != 'G') &&
- (*Format != 'A') && (*Format != 'a')) {
- return FALSE;
- }
- Format += 1;
- }
- return TRUE;
- }
|