summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2014-08-06 12:40:21 -0400
committerTavian Barnes <tavianator@tavianator.com>2014-08-06 12:40:21 -0400
commita30419c2781cbfbb87dd3c8b3c832be471afeeb0 (patch)
tree781cdc921de3cf3094bd3132e81688001085eedb
parentb72e0da99ef6c379bb95c637a83496ebd1aa3284 (diff)
downloadkd-forest-a30419c2781cbfbb87dd3c8b3c832be471afeeb0.tar.xz
Split out option handling into its own file.
-rw-r--r--Makefile6
-rw-r--r--main.c338
-rw-r--r--options.c322
-rw-r--r--options.h45
4 files changed, 373 insertions, 338 deletions
diff --git a/Makefile b/Makefile
index 5a9f5dd..ae18799 100644
--- a/Makefile
+++ b/Makefile
@@ -10,14 +10,14 @@
#####################################################################
CC ?= gcc
-CFLAGS ?= -std=c99 -pipe -g -O3 -flto -Werror -Wall -Wpedantic -Wextra -Wno-sign-compare -Wno-unused-parameter -Wunreachable-code -Wshadow -Wpointer-arith -Wwrite-strings -Wcast-align -Wstrict-prototypes
+CFLAGS ?= -std=c99 -D_POSIX_C_SOURCE=200809L -pipe -g -O3 -flto -Werror -Wall -Wpedantic -Wextra -Wno-sign-compare -Wno-unused-parameter -Wunreachable-code -Wshadow -Wpointer-arith -Wwrite-strings -Wcast-align -Wstrict-prototypes
LDFLAGS ?= -Wl,-O1,--sort-common,--as-needed,-z,relro
LIBS ?= -lm -lpng
RM ?= rm -f
-HEADERS = color.h kd-forest.h util.h
+HEADERS = color.h kd-forest.h options.h util.h
-kd-forest: color.o kd-forest.o main.o util.o
+kd-forest: color.o kd-forest.o main.o options.o util.o
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LIBS) -o kd-forest
%.o: %.c $(HEADERS)
diff --git a/main.c b/main.c
index a385e3d..eaa7f01 100644
--- a/main.c
+++ b/main.c
@@ -9,52 +9,22 @@
* the COPYING file or http://www.wtfpl.net/ for more details. *
*********************************************************************/
-#define _POSIX_C_SOURCE 200809L
-
+#include "color.h"
#include "kd-forest.h"
+#include "options.h"
#include "util.h"
-#include "color.h"
-#include <errno.h>
#include <math.h>
#include <png.h>
#include <setjmp.h>
#include <stdarg.h>
-#include <stdio.h>
#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
#if __unix__
# include <unistd.h>
#endif
-// Possible generation modes
-typedef enum {
- MODE_HUE_SORT,
- MODE_RANDOM,
-} mode_t;
-
-// Possible color spaces
-typedef enum {
- COLOR_SPACE_RGB,
- COLOR_SPACE_LAB,
- COLOR_SPACE_LUV,
-} color_space_t;
-
-// Command-line options
-typedef struct {
- unsigned int bit_depth;
- mode_t mode;
- color_space_t color_space;
- bool animate;
- const char *filename;
- unsigned int seed;
- bool help;
-} options_t;
-
-static bool parse_options(options_t *options, int argc, char *argv[]);
-static void print_usage(FILE *file, const char *command);
-
// All-encompasing state struct
typedef struct {
const options_t *options;
@@ -92,308 +62,6 @@ main(int argc, char *argv[])
return EXIT_SUCCESS;
}
-static bool
-parse_arg(int argc, char *argv[],
- const char *short_form, const char *long_form,
- const char **value, int *i, bool *error)
-{
- size_t short_len = strlen(short_form);
- size_t long_len = strlen(long_form);
-
- const char *actual_form;
- const char *arg = argv[*i];
- const char *candidate = NULL;
-
- if (strncmp(arg, short_form, short_len) == 0) {
- actual_form = short_form;
- if (strlen(arg) > short_len) {
- candidate = arg + short_len;
- }
- } else if (strncmp(arg, long_form, long_len) == 0) {
- actual_form = long_form;
- if (strlen(arg) > long_len) {
- if (arg[long_len] == '=') {
- candidate = arg + long_len + 1;
- } else {
- return false;
- }
- }
- } else {
- return false;
- }
-
- if (value) {
- if (candidate) {
- *value = candidate;
- } else if (*i < argc - 1) {
- ++*i;
- *value = argv[*i];
- } else {
- fprintf(stderr, "Expected a value for %s\n", arg);
- *error = true;
- return false;
- }
- } else if (candidate) {
- fprintf(stderr, "Unexpected value for %s: `%s'\n",
- actual_form, candidate);
- *error = true;
- return false;
- }
-
- return true;
-}
-
-static bool
-str_to_uint(const char *str, unsigned int *value)
-{
- char *endptr;
- long result = strtol(str, &endptr, 10);
- if (*str == '\0' || *endptr != '\0') {
- return false;
- }
- if (result < 0 || result > UINT_MAX) {
- return false;
- }
-
- *value = result;
- return true;
-}
-
-static void
-strcatinc(char **destp, const char *src)
-{
- strcpy(*destp, src);
- *destp += strlen(src);
-}
-
-typedef enum {
- COLORIZE_NORMAL,
- COLORIZE_AT,
- COLORIZE_BANG,
- COLORIZE_STAR,
- COLORIZE_SHORT_OPTION,
- COLORIZE_LONG_OPTION,
-} colorize_state_t;
-
-static void
-print_colorized(FILE *file, bool tty, const char *format, ...)
-{
- const char *bold = tty ? "\033[1m" : "";
- const char *red = tty ? "\033[1;31m" : "";
- const char *green = tty ? "\033[1;32m" : "";
- const char *normal = tty ? "\033[0m" : "";
-
- size_t size = strlen(format) + 1;
- char colorized[16*size];
- char *builder = colorized;
-
- colorize_state_t state = COLORIZE_NORMAL;
- for (size_t i = 0; i < size; ++i) {
- char c = format[i];
-
- if (c == '\\') {
- *builder++ = format[++i];
- continue;
- }
-
- switch (state) {
- case COLORIZE_AT:
- if (c == '@') {
- strcatinc(&builder, normal);
- state = COLORIZE_NORMAL;
- } else {
- *builder++ = c;
- }
- break;
-
- case COLORIZE_BANG:
- if (c == '!') {
- strcatinc(&builder, normal);
- state = COLORIZE_NORMAL;
- } else {
- *builder++ = c;
- }
- break;
-
- case COLORIZE_STAR:
- if (c == '*') {
- strcatinc(&builder, normal);
- state = COLORIZE_NORMAL;
- } else {
- *builder++ = c;
- }
- break;
-
- case COLORIZE_SHORT_OPTION:
- *builder++ = c;
- strcatinc(&builder, normal);
- state = COLORIZE_NORMAL;
- break;
-
- case COLORIZE_LONG_OPTION:
- if (!isalpha(c) && c != '-') {
- strcatinc(&builder, normal);
- state = COLORIZE_NORMAL;
- }
- *builder++ = c;
- break;
-
- default:
- switch (c) {
- case '@':
- state = COLORIZE_AT;
- strcatinc(&builder, green);
- break;
-
- case '!':
- state = COLORIZE_BANG;
- strcatinc(&builder, bold);
- break;
-
- case '*':
- state = COLORIZE_STAR;
- strcatinc(&builder, red);
- break;
-
- case '-':
- if (c == '-') {
- if (format[i + 1] == '-') {
- state = COLORIZE_LONG_OPTION;
- } else {
- state = COLORIZE_SHORT_OPTION;
- }
- strcatinc(&builder, red);
- }
- *builder++ = c;
- break;
-
- default:
- *builder++ = c;
- break;
- }
- break;
- }
- }
-
- va_list args;
- va_start(args, format);
- vprintf(colorized, args);
- va_end(args);
-}
-
-static void
-print_usage(FILE *file, const char *command)
-{
-#if __unix__
- bool tty = isatty(fileno(file));
-#else
- bool tty = false;
-#endif
-
- size_t length = strlen(command);
- char whitespace[length + 1];
- memset(whitespace, ' ', length);
- whitespace[length] = '\0';
-
-#define usage(...) print_colorized(file, tty, __VA_ARGS__)
- usage("Usage:\n");
- usage(" !$! *%s* [-b|--bit-depth @DEPTH@]\n", command);
- usage(" %s [-s|--hue-sort] [-r|--random]\n", whitespace);
- usage(" %s [-c|--color-space @RGB@|@Lab@|@Luv@]\n", whitespace);
- usage(" %s [-a|--animate]\n", whitespace);
- usage(" %s [-o|--output @PATH@]\n", whitespace);
- usage(" %s [-e|--seed @SEED@]\n", whitespace);
- usage(" %s [-h|--help]\n", whitespace);
- usage("\n");
- usage(" -b, --bit-depth @DEPTH@:\n");
- usage(" Use all @DEPTH@\\-bit colors (!default!: @24@)\n\n");
- usage(" -s, --hue-sort:\n");
- usage(" Sort colors by hue first (!default!)\n");
- usage(" -r, --random:\n");
- usage(" Randomize colors first\n\n");
- usage(" -c, --color-space @RGB@|@Lab@|@Luv@:\n");
- usage(" Use the given color space (!default!: @Lab@)\n\n");
- usage(" -a, --animate:\n");
- usage(" Generate frames of an animation\n\n");
- usage(" -o, --output @PATH@:\n");
- usage(" Output a PNG file at @PATH@ (!default!: @kd\\-forest.png@)\n\n");
- usage(" If -a/--animate is specified, this is treated as a directory which\n");
- usage(" will hold many frames\n\n");
- usage(" -e, --seed @SEED@:\n");
- usage(" Seed the random number generator (!default!: @0@)\n\n");
- usage(" -h, --help:\n");
- usage(" Show this message\n");
-#undef usage
-}
-
-static bool
-parse_options(options_t *options, int argc, char *argv[])
-{
- // Set defaults
- options->bit_depth = 24;
- options->mode = MODE_HUE_SORT;
- options->seed = 0;
- options->color_space = COLOR_SPACE_LAB;
- options->animate = false;
- options->filename = NULL;
- options->help = false;
-
- bool result = true;
-
- for (int i = 1; i < argc; ++i) {
- const char *value;
- bool error = false;
-
- if (parse_arg(argc, argv, "-b", "--bit-depth", &value, &i, &error)) {
- if (!str_to_uint(value, &options->bit_depth)
- || options->bit_depth <= 1
- || options->bit_depth > 24) {
- fprintf(stderr, "Invalid bit depth: `%s'\n", value);
- error = true;
- }
- } else if (parse_arg(argc, argv, "-s", "--hue-sort", NULL, &i, &error)) {
- options->mode = MODE_HUE_SORT;
- } else if (parse_arg(argc, argv, "-r", "--random", NULL, &i, &error)) {
- options->mode = MODE_RANDOM;
- } else if (parse_arg(argc, argv, "-e", "--seed", &value, &i, &error)) {
- if (!str_to_uint(value, &options->seed)) {
- fprintf(stderr, "Invalid random seed: `%s'\n", value);
- error = true;
- }
- } else if (parse_arg(argc, argv, "-a", "--animate", NULL, &i, &error)) {
- options->animate = true;
- } else if (parse_arg(argc, argv, "-o", "--output", &value, &i, &error)) {
- options->filename = value;
- } else if (parse_arg(argc, argv, "-c", "--color-space", &value, &i, &error)) {
- if (strcmp(value, "RGB") == 0) {
- options->color_space = COLOR_SPACE_RGB;
- } else if (strcmp(value, "Lab") == 0) {
- options->color_space = COLOR_SPACE_LAB;
- } else if (strcmp(value, "Luv") == 0) {
- options->color_space = COLOR_SPACE_LUV;
- } else {
- fprintf(stderr, "Invalid color space: `%s'\n", value);
- }
- } else if (parse_arg(argc, argv, "-h", "--help", NULL, &i, &error)) {
- options->help = true;
- } else if (!error) {
- fprintf(stderr, "Unexpected argument `%s'\n", argv[i]);
- error = true;
- }
-
- if (error) {
- result = false;
- }
- }
-
- // Default filename depends on -a flag
- if (!options->filename) {
- options->filename = options->animate ? "frames" : "kd-forest.png";
- }
-
- return result;
-}
-
static uint32_t *
create_colors(const state_t *state)
{
diff --git a/options.c b/options.c
new file mode 100644
index 0000000..04f1f82
--- /dev/null
+++ b/options.c
@@ -0,0 +1,322 @@
+/*********************************************************************
+ * kd-forest *
+ * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This program is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+#include "options.h"
+#include <ctype.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#if __unix__
+# include <unistd.h>
+#endif
+
+static bool
+parse_arg(int argc, char *argv[],
+ const char *short_form, const char *long_form,
+ const char **value, int *i, bool *error)
+{
+ size_t short_len = strlen(short_form);
+ size_t long_len = strlen(long_form);
+
+ const char *actual_form;
+ const char *arg = argv[*i];
+ const char *candidate = NULL;
+
+ if (strncmp(arg, short_form, short_len) == 0) {
+ actual_form = short_form;
+ if (strlen(arg) > short_len) {
+ candidate = arg + short_len;
+ }
+ } else if (strncmp(arg, long_form, long_len) == 0) {
+ actual_form = long_form;
+ if (strlen(arg) > long_len) {
+ if (arg[long_len] == '=') {
+ candidate = arg + long_len + 1;
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+
+ if (value) {
+ if (candidate) {
+ *value = candidate;
+ } else if (*i < argc - 1) {
+ ++*i;
+ *value = argv[*i];
+ } else {
+ fprintf(stderr, "Expected a value for %s\n", arg);
+ *error = true;
+ return false;
+ }
+ } else if (candidate) {
+ fprintf(stderr, "Unexpected value for %s: `%s'\n",
+ actual_form, candidate);
+ *error = true;
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+str_to_uint(const char *str, unsigned int *value)
+{
+ char *endptr;
+ long result = strtol(str, &endptr, 10);
+ if (*str == '\0' || *endptr != '\0') {
+ return false;
+ }
+ if (result < 0 || result > UINT_MAX) {
+ return false;
+ }
+
+ *value = result;
+ return true;
+}
+
+static void
+strcatinc(char **destp, const char *src)
+{
+ strcpy(*destp, src);
+ *destp += strlen(src);
+}
+
+typedef enum {
+ COLORIZE_NORMAL,
+ COLORIZE_AT,
+ COLORIZE_BANG,
+ COLORIZE_STAR,
+ COLORIZE_SHORT_OPTION,
+ COLORIZE_LONG_OPTION,
+} colorize_state_t;
+
+static void
+print_colorized(FILE *file, bool tty, const char *format, ...)
+{
+ const char *bold = tty ? "\033[1m" : "";
+ const char *red = tty ? "\033[1;31m" : "";
+ const char *green = tty ? "\033[1;32m" : "";
+ const char *normal = tty ? "\033[0m" : "";
+
+ size_t size = strlen(format) + 1;
+ char colorized[16*size];
+ char *builder = colorized;
+
+ colorize_state_t state = COLORIZE_NORMAL;
+ for (size_t i = 0; i < size; ++i) {
+ char c = format[i];
+
+ if (c == '\\') {
+ *builder++ = format[++i];
+ continue;
+ }
+
+ switch (state) {
+ case COLORIZE_AT:
+ if (c == '@') {
+ strcatinc(&builder, normal);
+ state = COLORIZE_NORMAL;
+ } else {
+ *builder++ = c;
+ }
+ break;
+
+ case COLORIZE_BANG:
+ if (c == '!') {
+ strcatinc(&builder, normal);
+ state = COLORIZE_NORMAL;
+ } else {
+ *builder++ = c;
+ }
+ break;
+
+ case COLORIZE_STAR:
+ if (c == '*') {
+ strcatinc(&builder, normal);
+ state = COLORIZE_NORMAL;
+ } else {
+ *builder++ = c;
+ }
+ break;
+
+ case COLORIZE_SHORT_OPTION:
+ *builder++ = c;
+ strcatinc(&builder, normal);
+ state = COLORIZE_NORMAL;
+ break;
+
+ case COLORIZE_LONG_OPTION:
+ if (!isalpha(c) && c != '-') {
+ strcatinc(&builder, normal);
+ state = COLORIZE_NORMAL;
+ }
+ *builder++ = c;
+ break;
+
+ default:
+ switch (c) {
+ case '@':
+ state = COLORIZE_AT;
+ strcatinc(&builder, green);
+ break;
+
+ case '!':
+ state = COLORIZE_BANG;
+ strcatinc(&builder, bold);
+ break;
+
+ case '*':
+ state = COLORIZE_STAR;
+ strcatinc(&builder, red);
+ break;
+
+ case '-':
+ if (c == '-') {
+ if (format[i + 1] == '-') {
+ state = COLORIZE_LONG_OPTION;
+ } else {
+ state = COLORIZE_SHORT_OPTION;
+ }
+ strcatinc(&builder, red);
+ }
+ *builder++ = c;
+ break;
+
+ default:
+ *builder++ = c;
+ break;
+ }
+ break;
+ }
+ }
+
+ va_list args;
+ va_start(args, format);
+ vprintf(colorized, args);
+ va_end(args);
+}
+
+void
+print_usage(FILE *file, const char *command)
+{
+#if __unix__
+ bool tty = isatty(fileno(file));
+#else
+ bool tty = false;
+#endif
+
+ size_t length = strlen(command);
+ char whitespace[length + 1];
+ memset(whitespace, ' ', length);
+ whitespace[length] = '\0';
+
+#define usage(...) print_colorized(file, tty, __VA_ARGS__)
+ usage("Usage:\n");
+ usage(" !$! *%s* [-b|--bit-depth @DEPTH@]\n", command);
+ usage(" %s [-s|--hue-sort] [-r|--random]\n", whitespace);
+ usage(" %s [-c|--color-space @RGB@|@Lab@|@Luv@]\n", whitespace);
+ usage(" %s [-a|--animate]\n", whitespace);
+ usage(" %s [-o|--output @PATH@]\n", whitespace);
+ usage(" %s [-e|--seed @SEED@]\n", whitespace);
+ usage(" %s [-h|--help]\n", whitespace);
+ usage("\n");
+ usage(" -b, --bit-depth @DEPTH@:\n");
+ usage(" Use all @DEPTH@\\-bit colors (!default!: @24@)\n\n");
+ usage(" -s, --hue-sort:\n");
+ usage(" Sort colors by hue first (!default!)\n");
+ usage(" -r, --random:\n");
+ usage(" Randomize colors first\n\n");
+ usage(" -c, --color-space @RGB@|@Lab@|@Luv@:\n");
+ usage(" Use the given color space (!default!: @Lab@)\n\n");
+ usage(" -a, --animate:\n");
+ usage(" Generate frames of an animation\n\n");
+ usage(" -o, --output @PATH@:\n");
+ usage(" Output a PNG file at @PATH@ (!default!: @kd\\-forest.png@)\n\n");
+ usage(" If -a/--animate is specified, this is treated as a directory which\n");
+ usage(" will hold many frames\n\n");
+ usage(" -e, --seed @SEED@:\n");
+ usage(" Seed the random number generator (!default!: @0@)\n\n");
+ usage(" -h, --help:\n");
+ usage(" Show this message\n");
+#undef usage
+}
+
+bool
+parse_options(options_t *options, int argc, char *argv[])
+{
+ // Set defaults
+ options->bit_depth = 24;
+ options->mode = MODE_HUE_SORT;
+ options->seed = 0;
+ options->color_space = COLOR_SPACE_LAB;
+ options->animate = false;
+ options->filename = NULL;
+ options->help = false;
+
+ bool result = true;
+
+ for (int i = 1; i < argc; ++i) {
+ const char *value;
+ bool error = false;
+
+ if (parse_arg(argc, argv, "-b", "--bit-depth", &value, &i, &error)) {
+ if (!str_to_uint(value, &options->bit_depth)
+ || options->bit_depth <= 1
+ || options->bit_depth > 24) {
+ fprintf(stderr, "Invalid bit depth: `%s'\n", value);
+ error = true;
+ }
+ } else if (parse_arg(argc, argv, "-s", "--hue-sort", NULL, &i, &error)) {
+ options->mode = MODE_HUE_SORT;
+ } else if (parse_arg(argc, argv, "-r", "--random", NULL, &i, &error)) {
+ options->mode = MODE_RANDOM;
+ } else if (parse_arg(argc, argv, "-e", "--seed", &value, &i, &error)) {
+ if (!str_to_uint(value, &options->seed)) {
+ fprintf(stderr, "Invalid random seed: `%s'\n", value);
+ error = true;
+ }
+ } else if (parse_arg(argc, argv, "-a", "--animate", NULL, &i, &error)) {
+ options->animate = true;
+ } else if (parse_arg(argc, argv, "-o", "--output", &value, &i, &error)) {
+ options->filename = value;
+ } else if (parse_arg(argc, argv, "-c", "--color-space", &value, &i, &error)) {
+ if (strcmp(value, "RGB") == 0) {
+ options->color_space = COLOR_SPACE_RGB;
+ } else if (strcmp(value, "Lab") == 0) {
+ options->color_space = COLOR_SPACE_LAB;
+ } else if (strcmp(value, "Luv") == 0) {
+ options->color_space = COLOR_SPACE_LUV;
+ } else {
+ fprintf(stderr, "Invalid color space: `%s'\n", value);
+ }
+ } else if (parse_arg(argc, argv, "-h", "--help", NULL, &i, &error)) {
+ options->help = true;
+ } else if (!error) {
+ fprintf(stderr, "Unexpected argument `%s'\n", argv[i]);
+ error = true;
+ }
+
+ if (error) {
+ result = false;
+ }
+ }
+
+ // Default filename depends on -a flag
+ if (!options->filename) {
+ options->filename = options->animate ? "frames" : "kd-forest.png";
+ }
+
+ return result;
+}
diff --git a/options.h b/options.h
new file mode 100644
index 0000000..6f1b2f8
--- /dev/null
+++ b/options.h
@@ -0,0 +1,45 @@
+/*********************************************************************
+ * kd-forest *
+ * Copyright (C) 2014 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This program is free software. It comes without any warranty, to *
+ * the extent permitted by applicable law. You can redistribute it *
+ * and/or modify it under the terms of the Do What The Fuck You Want *
+ * To Public License, Version 2, as published by Sam Hocevar. See *
+ * the COPYING file or http://www.wtfpl.net/ for more details. *
+ *********************************************************************/
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <stdbool.h>
+#include <stdio.h>
+
+// Possible generation modes
+typedef enum {
+ MODE_HUE_SORT,
+ MODE_RANDOM,
+} mode_t;
+
+// Possible color spaces
+typedef enum {
+ COLOR_SPACE_RGB,
+ COLOR_SPACE_LAB,
+ COLOR_SPACE_LUV,
+} color_space_t;
+
+// Command-line options
+typedef struct {
+ unsigned int bit_depth;
+ mode_t mode;
+ color_space_t color_space;
+ bool animate;
+ const char *filename;
+ unsigned int seed;
+ bool help;
+} options_t;
+
+bool parse_options(options_t *options, int argc, char *argv[]);
+void print_usage(FILE *file, const char *command);
+
+#endif // OPTIONS_H