diff options
| author | Mattias Andrée <maandree@member.fsf.org> | 2016-01-02 20:55:21 +0100 | 
|---|---|---|
| committer | Mattias Andrée <maandree@member.fsf.org> | 2016-01-02 20:55:57 +0100 | 
| commit | eec0c8194db183ce24b8307226b53c00bde97a4a (patch) | |
| tree | 361de5b8fc2e10e66a2e45792b2e3d358da751fa | |
| parent | known issues (diff) | |
| download | radharc-eec0c8194db183ce24b8307226b53c00bde97a4a.tar.gz radharc-eec0c8194db183ce24b8307226b53c00bde97a4a.tar.bz2 radharc-eec0c8194db183ce24b8307226b53c00bde97a4a.tar.xz  | |
parse command line
Signed-off-by: Mattias Andrée <maandree@member.fsf.org>
| -rw-r--r-- | README | 47 | ||||
| -rw-r--r-- | src/arg.h | 41 | ||||
| -rw-r--r-- | src/blackbody.c | 2 | ||||
| -rw-r--r-- | src/blackbody.h | 2 | ||||
| -rw-r--r-- | src/radharc.c | 6 | ||||
| -rw-r--r-- | src/settings.c | 246 | ||||
| -rw-r--r-- | src/settings.h | 146 | 
7 files changed, 478 insertions, 12 deletions
@@ -7,7 +7,7 @@ PRONUNCIATION  SYNOPSIS  	radharc [OPTIONS]... -	The -l option is mandatory. +	The -l option is mandatory, unless single value -t is used.  OPTIONS  	-l LATITUDE:LONGITUDE @@ -19,45 +19,55 @@ OPTIONS  		Reminder for Americans (particularly US Americans),  		you are an the western hemisphere, not the eastern, -		thus your latitude is negative. If you experience +		thus your longtiude is negative. If you experience  		weird colour temperatures, 100 % of the times it is  		because you forgot the minus sign. But no need to  		feel stupid, it is a really common mistake. +		No complicated stuff please, only latitudes within +		±90° and longitudes within ±180°. No unit thought. +  	-t DAY:NIGHT  		Select colour temperature to use during full daytime  		and full night. This should be a integer. Do not -		include the unit (the 'K'). +		include the unit (the 'K'). The temperatures must +		be at least 1000 K.  	-t TEMPERATURE  		Select temperature to use. The program will exit  		when it is done setting the temperature. The  		natural colour temperature is 6500 K ('-t 6500'). +		The temperature must be at least 1000 K.  	-t +DELTA -		Increase the colour temperature by DELTA Kelvin. +		Increase the colour temperature by DELTA kelvin.  	-t -DELTA -		Decrease the colour temperature by DELTA Kelvin. +		Decrease the colour temperature by DELTA kelvin.  	-T TEMPERATURE  		Temperature that shall be used when the program  		is disabled (via SIGUSR1). -	-p	Print the current colour temperature. +	-p	Print the current status.  	-n	Set the temperature immediately, do not transition. -	-N	Do not transision when exiting, reset and exit -		immediately on exit. +	-N	Do not transition when exiting, disabling, or +		reenabling. -	-o	Set the colour immediately, and exit. +	-o	Set the colour, and exit.  	-x	Ignore the current calibrations on the monitors.  	-s SECONDS  		The start and exit transitions shall take SECONDS -		seconds. This may be a floating point number. +		seconds. This may be a floating point number, with +		an explicit unit. + +	-S KELVINS +		The transitions speed, in kelvin per second. +		This most be a positive integer.  	-i  		Apply negative image filter. Radharc will detect @@ -69,6 +79,16 @@ OPTIONS  	-b  		Broadcast events with bus. +	-d SERVER=DISPLAY +		Use the display server whose identifier is +		DISPLAY and whose identifier is stored in the +		environment variable SERVER, for example +		-d DISPLAY=:0 for the X display :0. + +	-d SYSTEM +		Use a subsystem which does not have identifiers. +		For example drm for the Direct Rendering Manager. +  	-e EDID  		Select monitor to use by its EDID. @@ -96,6 +116,13 @@ SIGNALS  		have been selected manually, it will be in the  		order they where selected. +ENVIRONMENT +	RADHARC_STATE +		The pathname to the state file, will be +		determined automatically if not set. +		If not set, you may only have one instance +		running per display server instance. +  NOTES  	I suggest using a local script named radharc that  	sets all options for you. 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); +  | 
