aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattias Andrée <maandree@member.fsf.org>2016-01-02 20:55:21 +0100
committerMattias Andrée <maandree@member.fsf.org>2016-01-02 20:55:57 +0100
commiteec0c8194db183ce24b8307226b53c00bde97a4a (patch)
tree361de5b8fc2e10e66a2e45792b2e3d358da751fa /src
parentknown issues (diff)
downloadradharc-eec0c8194db183ce24b8307226b53c00bde97a4a.tar.gz
radharc-eec0c8194db183ce24b8307226b53c00bde97a4a.tar.bz2
radharc-eec0c8194db183ce24b8307226b53c00bde97a4a.tar.xz
parse command line
Signed-off-by: Mattias Andrée <maandree@member.fsf.org>
Diffstat (limited to 'src')
-rw-r--r--src/arg.h41
-rw-r--r--src/blackbody.c2
-rw-r--r--src/blackbody.h2
-rw-r--r--src/radharc.c6
-rw-r--r--src/settings.c246
-rw-r--r--src/settings.h146
6 files changed, 441 insertions, 2 deletions
diff --git a/src/arg.h b/src/arg.h
new file mode 100644
index 0000000..8d235ba
--- /dev/null
+++ b/src/arg.h
@@ -0,0 +1,41 @@
+/*
+ * Copy me if you can.
+ * by 20h
+ */
+
+#ifndef ARG_H__
+#define ARG_H__
+
+extern char *argv0;
+
+/* use main(int argc, char *argv[]) */
+#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
+ argv[0] && argv[0][1]\
+ && argv[0][0] == '-';\
+ argc--, argv++) {\
+ char argc_;\
+ char **argv_;\
+ int brk_;\
+ if (argv[0][1] == '-' && argv[0][2] == '\0') {\
+ argv++;\
+ argc--;\
+ break;\
+ }\
+ for (brk_ = 0, argv[0]++, argv_ = argv;\
+ argv[0][0] && !brk_;\
+ argv[0]++) {\
+ if (argv_ != argv)\
+ break;\
+ argc_ = argv[0][0];\
+ switch (argc_)
+
+#define ARGEND }\
+ }
+
+#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
+ (char *)0 :\
+ (brk_ = 1, (argv[0][1] != '\0')?\
+ (&argv[0][1]) :\
+ (argc--, argv++, argv[0])))
+
+#endif
diff --git a/src/blackbody.c b/src/blackbody.c
index 1e06fb9..2fb5df4 100644
--- a/src/blackbody.c
+++ b/src/blackbody.c
@@ -114,7 +114,7 @@ interpolate(double x1, double y1, double x2, double y2, double temp, double *r,
* @throws EDOM The selected temperature is below 1000 K.
*/
int
-get_colour(int fd, int temp, double *r, double *g, double *b)
+get_colour(int fd, long int temp, double *r, double *g, double *b)
{
double values[10]; /* low:x,y,r,g,b + high:x,y,r,g,b */
off_t offset;
diff --git a/src/blackbody.h b/src/blackbody.h
index b83f641..4351505 100644
--- a/src/blackbody.h
+++ b/src/blackbody.h
@@ -35,5 +35,5 @@
* @throws 0 The file did not have the expected size.
* @throws EDOM The selected temperature is below 1000 K.
*/
-int get_colour(int fd, int temp, double *r, double *g, double *b);
+int get_colour(int fd, long int temp, double *r, double *g, double *b);
diff --git a/src/radharc.c b/src/radharc.c
index 5d83e1e..7b5f1e5 100644
--- a/src/radharc.c
+++ b/src/radharc.c
@@ -19,11 +19,17 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
+#include "settings.h"
+
int
main(int argc, char *argv[])
{
+ struct settings settings;
+
+ parse_command_line(argc, argv, &settings);
+
return 0;
}
diff --git a/src/settings.c b/src/settings.c
new file mode 100644
index 0000000..1eba1cc
--- /dev/null
+++ b/src/settings.c
@@ -0,0 +1,246 @@
+/**
+ * Copyright © 2016 Mattias Andrée <maandree@member.fsf.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "settings.h"
+#include "arg.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <limits.h>
+
+
+
+/**
+ * The name of the process.
+ */
+char *argv0 = NULL;
+
+
+
+/**
+ * Print usage information and exit if a condition is met.
+ *
+ * @param condition Do no do anything iff this is zero.
+ */
+static void
+usage(int condition)
+{
+ if (condition) {
+ fprintf(stderr,
+ "Usage: %s [OPTIONS]...\n"
+ "See `man 1 radharc` for more information.\n",
+ !argv0 ? "radharc" : argv0);
+ exit(2);
+ }
+}
+
+
+/**
+ * Parse a temperature string.
+ *
+ * @param str The temperature string.
+ * @param temp Output parameter for the temperature.
+ * Overflow is truncated.
+ * @param direction Unless `NULL`, will be set to +1 if `str`
+ * starts with a '+', `-1` if `str` starts
+ * with a '-', 0 otherwise. If `NULL` and
+ * `str` starts with a '-' or a '+', the
+ * function will fail.
+ * @param lower The function shall fail if the evaluated
+ * temperature is below this.
+ * @return 0 on success, -1 on error.
+ */
+static int
+parse_temperature(const char *str, long int *temp, int *direction, int lower)
+{
+ int dir = 0;
+ char *end;
+ switch (*str) {
+ case '-': dir = -1; break;
+ case '+': dir = +1; break;
+ }
+ str += !!dir;
+ if (dir && !direction) return -1;
+ else if (dir) *direction = dir;
+ if (!isdigit(*str)) return -1;
+ *temp = (errno = 0, strtol)(str, &end, 10);
+ if ((errno && !((errno == ERANGE) && (*temp == LONG_MAX))) || *end || (*temp < lower))
+ return -1;
+ return 0;
+}
+
+
+/**
+ * Parse a string as a positively valued `struct timespec`.
+ *
+ * @param str The string.
+ * @param ts Output parameter for the value.
+ * @return 0 on success, -1 on error.
+ */
+static int
+parse_timespec(const char *str, struct timespec *ts)
+{
+ int points = 0;
+ memset(ts, 0, sizeof(*ts));
+
+ /* Parse seconds. */
+ if (!isdigit(*str))
+ return -1;
+ while (isdigit(*str)) {
+ ts->tv_sec *= 10;
+ ts->tv_sec += *str++ & 15;
+ }
+
+ /* End? */
+ if (!*str) return 0;
+ if (*str != '.') return -1;
+
+ /* Parse nanoseconds.*/
+ for (; (points++ < 9) && isdigit(*str); str++) {
+ ts->tv_nsec *= 10;
+ ts->tv_nsec += *str++ & 15;
+ }
+ if (points == 9) {
+ if (!isdigit(*str)) return -1;
+ if (*str++ >= '5') {
+ ts->tv_nsec += 1;
+ if (ts->tv_nsec == 1000000000L)
+ ts->tv_sec += 1, ts->tv_nsec = 0;
+ }
+ }
+ while (isdigit(*str)) str++;
+ if (*str) return -1;
+
+ /* End! */
+ return 0;
+}
+
+
+/**
+ * Parse a latitude or a longitude value.
+ *
+ * @param str The string.
+ * @param loc Output parameter for the value.
+ * @param limit The limit of the absolute value.
+ * @return 0 on success, -1 on error.
+ */
+static int
+parse_location(char *str, double *loc, double limit)
+{
+ char* end;
+ if (strstr(str, "−") == str) /* Support proper minus. */
+ *(str += strlen("−") - 1) = '-';
+ if ((*str != '-') && (*str != '+') && (*str != '.') && !isdigit(*str))
+ return -1;
+ *loc = (errno = 0, strtod)(str, &end);
+ return -(errno || *loc || (fabs(*loc) > limit));
+}
+
+
+/**
+ * Parse the command line.
+ *
+ * @param argc The number of elements in `argv`.
+ * @param argv The commnad line arguments including the zeroth elemenet.
+ * @param settings Output parameter for the settings.
+ */
+void
+parse_command_line(int argc, char *argv[], struct settings *settings)
+{
+ int location_set = 0;
+ char *p;
+ char *arg;
+ int c = 0;
+
+ memset(settings, 0, sizeof(*settings));
+ settings->natural_temp = 6500;
+ settings->day_temp = 5500;
+ settings->night_temp = 3500;
+ settings->trans_speed = 50;
+
+ ARGBEGIN {
+ case 'l':
+ usage(!(p = strchr(arg = ARGF(), ':')));
+ *p++ = '\0', location_set = 1;
+ usage(parse_location(arg, &(settings->latitude), 90.0));
+ usage(parse_location(arg, &(settings->longitude), 180.0));
+ break;
+ case 't':
+ settings->temp = settings->day_temp = settings->night_temp = 0;
+ settings->temp_direction = 0;
+ if ((p = strchr(arg = ARGF(), ':'))) {
+ *p++ = '\0';
+ usage(parse_temperature(arg, &(settings->day_temp), NULL, 1000));
+ usage(parse_temperature(p, &(settings->night_temp), NULL, 1000));
+ } else {
+ usage(parse_temperature(arg, &(settings->temp), &(settings->temp_direction), 1000));
+ }
+ break;
+ case 'T':
+ usage(parse_temperature(ARGF(), &(settings->natural_temp), NULL, 1000));
+ break;
+ case 's':
+ settings->trans_speed = 0;
+ usage(parse_timespec(ARGF(), &(settings->transition)));
+ break;
+ case 'S':
+ usage(parse_temperature(ARGF(), &(settings->trans_speed), NULL, 1));
+ break;
+ case 'h':
+ usage(!(settings->hookpath = ARGF()));
+ break;
+ case 'd': c++; /* Fall though. */
+ case 'e': c++; /* Fall though. */
+ case 'm': c++;
+#define REALLOC(VAR, N) !(VAR = realloc(VAR, (N) * sizeof(*VAR)))
+ settings->monitors_n++;
+ if (REALLOC(settings->monitors_id, settings->monitors_n)) goto fail;
+ if (REALLOC(settings->monitors_arg, settings->monitors_n)) goto fail;
+ settings->monitors_id[settings->monitors_n - 1] = ARGF();
+ settings->monitors_arg[settings->monitors_n - 1] = (c == 3 ? 'm' : c == 2 ? 'e' : 'd'), c = 0;
+ break;
+ case 'p': settings->print_status = 1; break;
+ case 'n': settings->panic_start = 1; break;
+ case 'N': settings->panic_else = 1; break;
+ case 'o': settings->set_and_exit = 1; break;
+ case 'x': settings->ignore_calib = 1; break;
+ case 'i': settings->negative = 1; break;
+ case 'b': settings->use_bus = 1; break;
+ default: usage(1); break;
+ } ARGEND;
+ usage(argc);
+
+ if (!location_set && !(settings->temp)) {
+ fprintf(stderr,
+ "%s: The -l option is mandatory, unless single value -t is used."
+ "See `man 1 radharc` for more information.\n",
+ !argv0 ? "radharc" : argv0);
+ exit(2);
+ }
+
+fail:
+ perror(argv0 ? argv0 : "radharc");
+ exit(1);
+}
+
diff --git a/src/settings.h b/src/settings.h
new file mode 100644
index 0000000..b1909bb
--- /dev/null
+++ b/src/settings.h
@@ -0,0 +1,146 @@
+/**
+ * Copyright © 2016 Mattias Andrée <maandree@member.fsf.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <time.h>
+
+
+
+/**
+ * Settings from the command line.
+ */
+struct settings {
+ /**
+ * Print current status and exit?
+ */
+ int print_status : 1;
+
+ /**
+ * Start without transition?
+ */
+ int panic_start : 1;
+
+ /**
+ * Never transition, apart from at start?
+ */
+ int panic_else : 1;
+
+ /**
+ * Set temperature, possibly with transition, and exit?
+ */
+ int set_and_exit : 1;
+
+ /**
+ * Ignore calibrations?
+ */
+ int ignore_calib : 1;
+
+ /**
+ * Apply negative image filter?
+ */
+ int negative : 1;
+
+ /**
+ * Broadcast event with bus?
+ */
+ int use_bus : 1;
+
+ /**
+ * -1 to decrease temperature,
+ * +1 to increase temperature,
+ * 0 to set temperature.
+ */
+ int temp_direction;
+
+ /**
+ * The temperature, if use, the program will exit when it is done.
+ */
+ long int temp;
+
+ /**
+ * The temperature at full daytime.
+ */
+ long int day_temp;
+
+ /**
+ * The temperature at full night.
+ */
+ long int night_temp;
+
+ /**
+ * The temperature when disabled.
+ */
+ long int natural_temp;
+
+ /**
+ * Pathname to the hook script.
+ */
+ char *hookpath;
+
+ /**
+ * The number of seconds the transition takes.
+ */
+ struct timespec transition;
+
+ /**
+ * The number of kelvins per seconds the
+ * temperature is adjusted during transition.
+ */
+ long int trans_speed;
+
+ /**
+ * The user's latitudinal position.
+ */
+ double latitude;
+
+ /**
+ * The user's longitudinal position.
+ */
+ double longitude;
+
+ /**
+ * The number of elements in `monitors_id` and in `monitors_arg`.
+ */
+ size_t monitors_n;
+
+ /**
+ * Values for -d, -e, and -m, in order.
+ */
+ char **monitors_id;
+
+ /**
+ * The option (the character after the dash)
+ * the option used in correspnding element
+ * in `monitors_id`.
+ */
+ char *monitors_arg;
+};
+
+
+
+/**
+ * Parse the command line.
+ *
+ * @param argc The number of elements in `argv`.
+ * @param argv The commnad line arguments including the zeroth elemenet.
+ * @param settings Output parameter for the settings.
+ */
+void parse_command_line(int argc, char *argv[], struct settings *settings);
+