diff --git a/redshift.1 b/redshift.1
index c9667aa8..965d0fd7 100644
--- a/redshift.1
+++ b/redshift.1
@@ -3,7 +3,7 @@
redshift \- Set color temperature of display according to time of day.
.SH SYNOPSIS
.B redshift
-\fI[\-l LAT:LON | \-l PROVIDER:OPTIONS] [\-t DAY:NIGHT] \fR[\fIOPTIONS\fR...]
+\fI[\-l LAT:LON | \-l PROVIDER OPTIONS] [\-t DAY:NIGHT] \fR[\fIOPTIONS\fR...]
.SH DESCRIPTION
.B redshift
adjusts the color temperature of your screen according to your
@@ -46,11 +46,11 @@ Your current location, in degrees, given as floating point numbers,
towards north and east, with negative numbers representing south and
west, respectively.
.TP
-\fB\-l\fR PROVIDER[:OPTIONS]
+\fB\-l\fR PROVIDER [OPTIONS]
Select provider for automatic location updates
(Use `-l list' to see available providers)
.TP
-\fB\-m\fR METHOD[:OPTIONS]
+\fB\-m\fR METHOD [OPTIONS]
Method to use to set color temperature
(Use `-m list' to see available methods)
.TP
diff --git a/src/Makefile.am b/src/Makefile.am
index c7a5444e..b5d22710 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,6 +15,7 @@ redshift_SOURCES = \
location-manual.c location-manual.h \
solar.c solar.h \
systemtime.c systemtime.h \
+ opt-parser.c opt-parser.h \
hooks.c hooks.h \
gamma-dummy.c gamma-dummy.h
diff --git a/src/opt-parser.c b/src/opt-parser.c
new file mode 100644
index 00000000..7277bd3a
--- /dev/null
+++ b/src/opt-parser.c
@@ -0,0 +1,79 @@
+/* opt-parser.c -- getopt wrapper source
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+#include "opt-parser.h"
+
+#include
+#include
+#include
+
+
+int
+parseopt(int argc, char *const *argv, const char *shortopts, const char **args, int *args_count)
+{
+ int opt;
+ char *p;
+
+ *args_count = 0;
+ opt = getopt(argc, argv, shortopts);
+ if (opt < 0)
+ return opt;
+
+ p = strchr(shortopts, opt);
+ if ((p == NULL) || (p[1] != ':'))
+ return opt;
+
+ args[(*args_count)++] = optarg;
+ while (optind < argc && argv[optind][0] != '-') {
+ args[(*args_count)++] = argv[optind++];
+ }
+
+ return opt;
+}
+
+
+char *
+coalesce_args(const char *const *args, int args_count, char delimiter, char final)
+{
+ size_t n = args_count ? (args_count + 1) : 2;
+ size_t i;
+ char *rc;
+ char *rc_;
+
+ for (i = 0; i < args_count; i++)
+ n += strlen(args[i]);
+
+ rc = rc_ = malloc(n * sizeof(char));
+ if (rc == NULL)
+ return NULL;
+
+ for (i = 0; i < args_count; i++) {
+ n = strlen(args[i]);
+ memcpy(rc_, args[i], n * sizeof(char));
+ rc_ += n;
+ *rc_++ = delimiter;
+ }
+ if (args_count > 0)
+ rc_--;
+
+ *rc_++ = final;
+ *rc_++ = '\0';
+
+ return rc;
+}
+
diff --git a/src/opt-parser.h b/src/opt-parser.h
new file mode 100644
index 00000000..ec681584
--- /dev/null
+++ b/src/opt-parser.h
@@ -0,0 +1,28 @@
+/* opt-parser.h -- getopt wrapper header
+ This file is part of Redshift.
+
+ Redshift is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Redshift is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Redshift. If not, see .
+
+ Copyright (c) 2014 Mattias Andrée
+*/
+#ifndef REDSHIFT_OPT_PARSER_H
+#define REDSHIFT_OPT_PARSER_H
+
+
+int parseopt(int argc, char *const *argv, const char *shortopts, const char **args, int *args_count);
+
+char *coalesce_args(const char *const *args, int args_count, char delimiter, char final);
+
+
+#endif /* ! REDSHIFT_OPT_PARSER_H */
diff --git a/src/redshift.c b/src/redshift.c
index 0fcb0bad..2a909576 100644
--- a/src/redshift.c
+++ b/src/redshift.c
@@ -21,6 +21,7 @@
# include "config.h"
#endif
+#include
#include
#include
#include
@@ -47,6 +48,7 @@
#include "config-ini.h"
#include "solar.h"
#include "systemtime.h"
+#include "opt-parser.h"
#include "hooks.h"
@@ -519,10 +521,10 @@ print_method_list()
}
fputs("\n", stdout);
- fputs(_("Specify colon-separated options with"
- " `-m METHOD:OPTIONS'.\n"), stdout);
+ fputs(_("Specify space-separated options with"
+ " `-m METHOD OPTIONS'.\n"), stdout);
/* TRANSLATORS: `help' must not be translated. */
- fputs(_("Try `-m METHOD:help' for help.\n"), stdout);
+ fputs(_("Try `-m METHOD help' for help.\n"), stdout);
}
static void
@@ -534,10 +536,10 @@ print_provider_list()
}
fputs("\n", stdout);
- fputs(_("Specify colon-separated options with"
- "`-l PROVIDER:OPTIONS'.\n"), stdout);
+ fputs(_("Specify space-separated options with"
+ "`-l PROVIDER OPTIONS'.\n"), stdout);
/* TRANSLATORS: `help' must not be translated. */
- fputs(_("Try `-l PROVIDER:help' for help.\n"), stdout);
+ fputs(_("Try `-l PROVIDER help' for help.\n"), stdout);
}
@@ -570,7 +572,7 @@ provider_try_start(const location_provider_t *provider,
provider->name);
/* TRANSLATORS: `help' must not be
translated. */
- fprintf(stderr, _("Try `-l %s:help' for more"
+ fprintf(stderr, _("Try `-l %s help' for more"
" information.\n"),
provider->name);
return -1;
@@ -582,43 +584,42 @@ provider_try_start(const location_provider_t *provider,
/* Set provider options from command line. */
const char *manual_keys[] = { "lat", "lon" };
int i = 0;
- while (args != NULL) {
- char *next_arg = strchr(args, ':');
- if (next_arg != NULL) *(next_arg++) = '\0';
-
- const char *key = args;
- char *value = strchr(args, '=');
- if (value == NULL) {
- /* The options for the "manual" method can be set
- without keys on the command line for convencience
- and for backwards compatability. We add the proper
- keys here before calling set_option(). */
- if (strcmp(provider->name, "manual") == 0 &&
- i < sizeof(manual_keys)/sizeof(manual_keys[0])) {
- key = manual_keys[i];
- value = args;
+ if (args != NULL) {
+ while (*args != '\0') {
+ const char *key = args;
+ char *value = strchr(args, '=');
+ if (value == NULL) {
+ /* The options for the "manual" method can be set
+ without keys on the command line for convencience
+ and for backwards compatability. We add the proper
+ keys here before calling set_option(). */
+ if (strcmp(provider->name, "manual") == 0 &&
+ i < sizeof(manual_keys)/sizeof(manual_keys[0])) {
+ key = manual_keys[i];
+ value = args;
+ } else {
+ fprintf(stderr, _("Failed to parse option `%s'.\n"),
+ args);
+ return -1;
+ }
} else {
- fprintf(stderr, _("Failed to parse option `%s'.\n"),
- args);
+ *(value++) = '\0';
+ }
+
+ r = provider->set_option(state, key, value);
+ if (r < 0) {
+ provider->free(state);
+ fprintf(stderr, _("Failed to set %s option.\n"),
+ provider->name);
+ /* TRANSLATORS: `help' must not be translated. */
+ fprintf(stderr, _("Try `-l %s help' for more"
+ " information.\n"), provider->name);
return -1;
}
- } else {
- *(value++) = '\0';
- }
- r = provider->set_option(state, key, value);
- if (r < 0) {
- provider->free(state);
- fprintf(stderr, _("Failed to set %s option.\n"),
- provider->name);
- /* TRANSLATORS: `help' must not be translated. */
- fprintf(stderr, _("Try `-l %s:help' for more"
- " information.\n"), provider->name);
- return -1;
+ args = value + strlen(value) + 1;
+ i += 1;
}
-
- args = next_arg;
- i += 1;
}
/* Start provider. */
@@ -662,7 +663,7 @@ method_try_start(const gamma_method_t *method,
method->name);
/* TRANSLATORS: `help' must not be
translated. */
- fprintf(stderr, _("Try `-m %s:help' for more"
+ fprintf(stderr, _("Try `-m %s help' for more"
" information.\n"),
method->name);
return -1;
@@ -672,32 +673,31 @@ method_try_start(const gamma_method_t *method,
}
/* Set method options from command line. */
- while (args != NULL) {
- char *next_arg = strchr(args, ':');
- if (next_arg != NULL) *(next_arg++) = '\0';
-
- const char *key = args;
- char *value = strchr(args, '=');
- if (value == NULL) {
- fprintf(stderr, _("Failed to parse option `%s'.\n"),
- args);
- return -1;
- } else {
- *(value++) = '\0';
- }
+ if (args != NULL) {
+ while (*args != '\0') {
+ const char *key = args;
+ char *value = strchr(args, '=');
+ if (value == NULL) {
+ fprintf(stderr, _("Failed to parse option `%s'.\n"),
+ args);
+ return -1;
+ } else {
+ *(value++) = '\0';
+ }
- r = method->set_option(state, key, value);
- if (r < 0) {
- method->free(state);
- fprintf(stderr, _("Failed to set %s option.\n"),
- method->name);
- /* TRANSLATORS: `help' must not be translated. */
- fprintf(stderr, _("Try -m %s:help' for more"
- " information.\n"), method->name);
- return -1;
- }
+ r = method->set_option(state, key, value);
+ if (r < 0) {
+ method->free(state);
+ fprintf(stderr, _("Failed to set %s option.\n"),
+ method->name);
+ /* TRANSLATORS: `help' must not be translated. */
+ fprintf(stderr, _("Try -m %s help' for more"
+ " information.\n"), method->name);
+ return -1;
+ }
- args = next_arg;
+ args = value + strlen(value) + 1;
+ }
}
/* Start method. */
@@ -1088,7 +1088,9 @@ main(int argc, char *argv[])
/* Parse command line arguments. */
int opt;
- while ((opt = getopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx")) != -1) {
+ const char **args = alloca(argc * sizeof(char*));
+ int args_count;
+ while ((opt = parseopt(argc, argv, "b:c:g:hl:m:oO:prt:vVx", args, &args_count)) != -1) {
switch (opt) {
case 'b':
parse_brightness_string(optarg,
@@ -1121,38 +1123,47 @@ main(int argc, char *argv[])
break;
case 'l':
/* Print list of providers if argument is `list' */
- if (strcasecmp(optarg, "list") == 0) {
+ if (strcasecmp(*args, "list") == 0) {
print_provider_list();
exit(EXIT_SUCCESS);
}
- char *provider_name = NULL;
+ provider_args = coalesce_args(args, args_count, '\0', '\0');
+ if (provider_args == NULL) {
+ perror("coalesce_args");
+ exit(EXIT_FAILURE);
+ }
/* Don't save the result of strtof(); we simply want
to know if optarg can be parsed as a float. */
errno = 0;
char *end;
- strtof(optarg, &end);
- if (errno == 0 && *end == ':') {
- /* Use instead as arguments to `manual'. */
- provider_name = "manual";
- provider_args = optarg;
- } else {
- /* Split off provider arguments. */
- s = strchr(optarg, ':');
- if (s != NULL) {
- *(s++) = '\0';
- provider_args = s;
+ strtof(provider_args, &end);
+ if (errno == 0 && (end[0] == ':' || end[0] == '\0') && end[1] != '\0') {
+ /* Set delimiter to `\0'. */
+ end[0] = '\0';
+ /* Set provider method to `manual'. */
+ char *old_buf = provider_args;
+ size_t n = 0;
+ while (provider_args[n] != '\0' || provider_args[n + 1] != '\0')
+ n++;
+ n += 2;
+ provider_args = realloc(old_buf, (strlen("manual") + 1 + n) * sizeof(char));
+ if (provider_args == NULL) {
+ perror("realloc");
+ free(old_buf);
+ exit(EXIT_FAILURE);
}
-
- provider_name = optarg;
+ memmove(provider_args + strlen("manual") + 1, provider_args, n * sizeof(char));
+ strcpy(provider_args, "manual");
}
/* Lookup provider from name. */
- provider = find_location_provider(provider_name);
+ provider = find_location_provider(provider_args);
if (provider == NULL) {
fprintf(stderr, _("Unknown location provider"
- " `%s'.\n"), provider_name);
+ " `%s'.\n"), provider_args);
+ free(provider_args);
exit(EXIT_FAILURE);
}
@@ -1160,6 +1171,7 @@ main(int argc, char *argv[])
if (provider_args != NULL &&
strcasecmp(provider_args, "help") == 0) {
provider->print_help(stdout);
+ free(provider_args);
exit(EXIT_SUCCESS);
}
break;
@@ -1170,20 +1182,20 @@ main(int argc, char *argv[])
exit(EXIT_SUCCESS);
}
- /* Split off method arguments. */
- s = strchr(optarg, ':');
- if (s != NULL) {
- *(s++) = '\0';
- method_args = s;
+ method_args = coalesce_args(args, args_count, '\0', '\0');
+ if (method_args == NULL) {
+ perror("coalesce_args");
+ exit(EXIT_FAILURE);
}
/* Find adjustment method by name. */
- method = find_gamma_method(optarg);
+ method = find_gamma_method(method_args);
if (method == NULL) {
/* TRANSLATORS: This refers to the method
used to adjust colors e.g VidMode */
fprintf(stderr, _("Unknown adjustment method"
" `%s'.\n"), optarg);
+ free(method_args);
exit(EXIT_FAILURE);
}
@@ -1191,6 +1203,7 @@ main(int argc, char *argv[])
if (method_args != NULL &&
strcasecmp(method_args, "help") == 0) {
method->print_help(stdout);
+ free(method_args);
exit(EXIT_SUCCESS);
}
break;
@@ -1409,8 +1422,9 @@ main(int argc, char *argv[])
mode != PROGRAM_MODE_MANUAL) {
if (provider != NULL) {
/* Use provider specified on command line. */
- r = provider_try_start(provider, &location_state,
- &config_state, provider_args);
+ r = provider_try_start(provider, &location_state, &config_state,
+ provider_args == NULL ? NULL :
+ provider_args + strlen(provider_args) + 1);
if (r < 0) exit(EXIT_FAILURE);
} else {
/* Try all providers, use the first that works. */
@@ -1559,7 +1573,8 @@ main(int argc, char *argv[])
if (method != NULL) {
/* Use method specified on command line. */
r = method_try_start(method, &state, &config_state,
- method_args);
+ method_args == NULL ? NULL :
+ method_args + strlen(method_args) + 1);
if (r < 0) exit(EXIT_FAILURE);
} else {
/* Try all methods, use the first that works. */
@@ -1704,5 +1719,11 @@ main(int argc, char *argv[])
/* Clean up gamma adjustment state */
method->free(&state);
+ /* Free memory */
+ if (method_args != NULL)
+ free(method_args);
+ if (provider_args != NULL)
+ free(provider_args);
+
return EXIT_SUCCESS;
}