options.c (scrot-1.7.tar.bz2) | : | options.c (scrot-1.8) | ||
---|---|---|---|---|
skipping to change at line 12 | skipping to change at line 12 | |||
Copyright 1999-2000 Tom Gilbert <tom@linuxbrit.co.uk, | Copyright 1999-2000 Tom Gilbert <tom@linuxbrit.co.uk, | |||
gilbertt@linuxbrit.co.uk, | gilbertt@linuxbrit.co.uk, | |||
scrot_sucks@linuxbrit.co.uk> | scrot_sucks@linuxbrit.co.uk> | |||
Copyright 2008 William Vera <billy@billy.com.mx> | Copyright 2008 William Vera <billy@billy.com.mx> | |||
Copyright 2009 George Danchev <danchev@spnet.net> | Copyright 2009 George Danchev <danchev@spnet.net> | |||
Copyright 2009 James Cameron <quozl@us.netrek.org> | Copyright 2009 James Cameron <quozl@us.netrek.org> | |||
Copyright 2010 Ibragimov Rinat <ibragimovrinat@mail.ru> | Copyright 2010 Ibragimov Rinat <ibragimovrinat@mail.ru> | |||
Copyright 2017 Stoney Sauce <stoneysauce@gmail.com> | Copyright 2017 Stoney Sauce <stoneysauce@gmail.com> | |||
Copyright 2019 Daniel Lublin <daniel@lublin.se> | Copyright 2019 Daniel Lublin <daniel@lublin.se> | |||
Copyright 2019-2021 Daniel T. Borelli <danieltborelli@gmail.com> | Copyright 2019-2022 Daniel T. Borelli <danieltborelli@gmail.com> | |||
Copyright 2019 Jade Auer <jade@trashwitch.dev> | Copyright 2019 Jade Auer <jade@trashwitch.dev> | |||
Copyright 2020 Sean Brennan <zettix1@gmail.com> | Copyright 2020 Sean Brennan <zettix1@gmail.com> | |||
Copyright 2021 Christopher R. Nelson <christopher.nelson@languidnights.com> | Copyright 2021 Christopher R. Nelson <christopher.nelson@languidnights.com> | |||
Copyright 2021 Guilherme Janczak <guilherme.janczak@yandex.com> | Copyright 2021-2023 Guilherme Janczak <guilherme.janczak@yandex.com> | |||
Copyright 2021 IFo Hancroft <contact@ifohancroft.com> | Copyright 2021 IFo Hancroft <contact@ifohancroft.com> | |||
Copyright 2021 Peter Wu <peterwu@hotmail.com> | Copyright 2021 Peter Wu <peterwu@hotmail.com> | |||
Copyright 2021 Wilson Smith <01wsmith+gh@gmail.com> | Copyright 2021 Wilson Smith <01wsmith+gh@gmail.com> | |||
Copyright 2022 Zev Weiss <zev@bewilderbeest.net> | ||||
Permission is hereby granted, free of charge, to any person obtaining a copy | Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to | of this software and associated documentation files (the "Software"), to | |||
deal in the Software without restriction, including without limitation the | deal in the Software without restriction, including without limitation the | |||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | 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 | sell copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | The above copyright notice and this permission notice shall be included in | |||
all copies of the Software and its documentation and acknowledgment shall be | all copies of the Software and its documentation and acknowledgment shall be | |||
skipping to change at line 42 | skipping to change at line 43 | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |||
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 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. | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
*/ | */ | |||
#include <assert.h> | ||||
#include <err.h> | ||||
#include <errno.h> | ||||
#include <getopt.h> | ||||
#include <limits.h> | ||||
#include <stdbool.h> | ||||
#include <stdio.h> | ||||
#include <stdlib.h> | ||||
#include <string.h> | ||||
#include <unistd.h> | ||||
#include "config.h" | ||||
#include "note.h" | ||||
#include "options.h" | #include "options.h" | |||
#include "scrot.h" | #include "scrot.h" | |||
#include <assert.h> | #include "scrot_selection.h" | |||
#include "util.h" | ||||
#define STR_LEN_MAX_FILENAME(msg, fileName) do { \ | #define STR_LEN_MAX_FILENAME(msg, fileName) do { \ | |||
if (strlen((fileName)) > MAX_FILENAME) { \ | if (strlen((fileName)) > MAX_FILENAME) { \ | |||
errx(EXIT_FAILURE, #msg " filename too long, must be " \ | errx(EXIT_FAILURE, #msg " filename too long, must be " \ | |||
"less than %d characters", MAX_FILENAME); \ | "less than %d characters", MAX_FILENAME); \ | |||
} \ | } \ | |||
} while(0) | } while(0) | |||
#define checkMaxOutputFileName(fileName) \ | #define checkMaxOutputFileName(fileName) \ | |||
STR_LEN_MAX_FILENAME(output, (fileName)) | STR_LEN_MAX_FILENAME(output, (fileName)) | |||
#define checkMaxInputFileName(fileName) \ | #define checkMaxInputFileName(fileName) \ | |||
STR_LEN_MAX_FILENAME(input, (fileName)) | STR_LEN_MAX_FILENAME(input, (fileName)) | |||
enum { | enum { | |||
MAX_LEN_WINDOW_CLASS_NAME = 80, //characters | MAX_LEN_WINDOW_CLASS_NAME = 80, //characters | |||
MAX_FILENAME = 256, // characters | MAX_FILENAME = 256, // characters | |||
MAX_DISPLAY_NAME = 256, // characters | MAX_DISPLAY_NAME = 256, // characters | |||
}; | }; | |||
ScrotOptions opt = { | struct ScrotOptions opt = { | |||
.quality = 75, | .quality = 75, | |||
.lineStyle = LineSolid, | .lineStyle = LineSolid, | |||
.lineWidth = 1, | .lineWidth = 1, | |||
.lineOpacity = SELECTION_OPACITY_DEFAULT, | .lineOpacity = SELECTION_OPACITY_DEFAULT, | |||
.lineMode = LINE_MODE_S_CLASSIC, | .lineMode = LINE_MODE_S_CLASSIC, | |||
.stackDirection = HORIZONTAL, | .stackDirection = HORIZONTAL, | |||
.monitor = -1, | ||||
}; | }; | |||
int optionsParseRequiredNumber(char const* str) | static void showUsage(void); | |||
{ | static void showVersion(void); | |||
assert(NULL != str); // fix yout caller function, | static void optionsParseThumbnail(char *); | |||
// the user does not impose this behavior | ||||
char* end = NULL; | /* optionsParseNum: "string to number" function. | |||
long ret = 0L; | * | |||
errno = 0; | * Parses the string representation of an integer in str, and simultaneously | |||
* ensures that it is >= min and <= max. | ||||
ret = strtol(str, &end, 10); | * | |||
* Returns the integer and sets *errmsg to NULL on success. | ||||
if (errno) | * Returns 0 and sets *errmsg to a pointer to a string containing the | |||
goto range_error; | * reason why the number can't be parsed on error. | |||
* | ||||
if (str == end) | * usage: | |||
errx(EXIT_FAILURE, "the option is not a number: %s", end); | * char *errmsg; | |||
* unsigned int nonnegative; | ||||
if (ret > INT_MAX || ret < INT_MIN) { | * if ((nonnegative = optionsParseNum(optarg, 0, UINT_MAX, &errmsg)) == NULL) | |||
errno = ERANGE; | * errx(EXIT_FAILURE, "-n: '%s' is %s", optarg, errmsg); | |||
goto range_error; | */ | |||
long long optionsParseNum(const char *str, long long min, long long max, | ||||
const char *errmsg[static 1]) | ||||
{ | ||||
char *end = NULL; | ||||
long long rval; | ||||
int saved_errno = errno; | ||||
if (str == NULL) { | ||||
*errmsg = "missing"; | ||||
return 0; | ||||
} | } | |||
*errmsg = NULL; | ||||
return ret; | errno = 0; | |||
rval = strtoll(str, &end, 10); | ||||
range_error: | if (errno == ERANGE) { | |||
err(EXIT_FAILURE, "error strtol"); | *errmsg = "not representable"; | |||
} | } else if (*str == '\0') { | |||
*errmsg = "the null string"; | ||||
static int nonNegativeNumber(int number) | } else if (*end != '\0') { | |||
{ | *errmsg = "not a number"; | |||
return (number < 0) ? 0 : number; | } else if (rval < min) { | |||
} | /* | |||
* rval could be set to 0 due to strtoll() returning error and this | ||||
* could be smaller than min or larger than max. To make sure we don't | ||||
* return the wrong error message, put min/max checks after everything | ||||
* else. | ||||
*/ | ||||
*errmsg = min == 0 ? "negative" : "too small"; | ||||
} else if (rval > max) { | ||||
*errmsg = "too large"; | ||||
} | ||||
errno = saved_errno; | ||||
int optionsParseRequireRange(int n, int lo, int hi) | return (*errmsg ? 0 : rval); | |||
{ | ||||
return (n < lo ? lo : n > hi ? hi : n); | ||||
} | } | |||
bool optionsParseIsString(char const* const str) | static bool optionsParseIsString(const char *const str) | |||
{ | { | |||
return (str && (str[0] != '\0')); | return (str && (str[0] != '\0')); | |||
} | } | |||
static void optionsParseStack(char const* optarg) | static void optionsParseStack(const char *optarg) | |||
{ | { | |||
// the suboption it's optional | // the suboption it's optional | |||
if (!optarg) { | if (!optarg) { | |||
opt.stackDirection = HORIZONTAL; | opt.stackDirection = HORIZONTAL; | |||
return; | return; | |||
} | } | |||
char const* value = strchr(optarg, '='); | const char *value = strchr(optarg, '='); | |||
if (value) | if (value) | |||
++value; | ++value; | |||
else | else | |||
value = optarg; | value = optarg; | |||
if (*value == 'v') | if (*value == 'v') | |||
opt.stackDirection = VERTICAL; | opt.stackDirection = VERTICAL; | |||
else if (*value == 'h') | else if (*value == 'h') | |||
opt.stackDirection = HORIZONTAL; | opt.stackDirection = HORIZONTAL; | |||
else { | else { | |||
errx(EXIT_FAILURE, "option --stack: Unknown value for suboption '%s'", | errx(EXIT_FAILURE, "option --stack: Unknown value for suboption '%s'", | |||
value); | value); | |||
} | } | |||
} | } | |||
static void optionsParseSelection(char const* optarg) | static void optionsParseSelection(const char *optarg) | |||
{ | { | |||
// the suboption it's optional | // the suboption it's optional | |||
if (!optarg) { | if (!optarg) { | |||
opt.selection.mode = SELECTION_MODE_CAPTURE; | opt.selection.mode = SELECTION_MODE_CAPTURE; | |||
return; | return; | |||
} | } | |||
char const* value = strchr(optarg, '='); | const char *value = strchr(optarg, '='); | |||
if (value) | if (value) | |||
++value; | ++value; | |||
else | else | |||
value = optarg; | value = optarg; | |||
if (!strncmp(value, SELECTION_MODE_S_CAPTURE, SELECTION_MODE_L_CAPTURE)) { | if (!strncmp(value, SELECTION_MODE_S_CAPTURE, SELECTION_MODE_L_CAPTURE)) { | |||
opt.selection.mode = SELECTION_MODE_CAPTURE; | opt.selection.mode = SELECTION_MODE_CAPTURE; | |||
return; /* it has no parameter */ | return; /* it has no parameter */ | |||
} | } | |||
skipping to change at line 186 | skipping to change at line 220 | |||
if (opt.selection.mode & SELECTION_MODE_NOT_NEED_PARAM) | if (opt.selection.mode & SELECTION_MODE_NOT_NEED_PARAM) | |||
return; | return; | |||
if (*value != SELECTION_MODE_SEPARATOR) | if (*value != SELECTION_MODE_SEPARATOR) | |||
return; | return; | |||
if (*(++value) == '\0') | if (*(++value) == '\0') | |||
errx(EXIT_FAILURE, "option --select: Invalid parameter."); | errx(EXIT_FAILURE, "option --select: Invalid parameter."); | |||
if (opt.selection.mode == SELECTION_MODE_BLUR) { | if (opt.selection.mode == SELECTION_MODE_BLUR) { | |||
int const num = nonNegativeNumber(optionsParseRequiredNumber(value)); | const char *errmsg; | |||
opt.selection.paramNum = optionsParseNum(value, | ||||
opt.selection.paramNum = optionsParseRequireRange(num, | SELECTION_MODE_BLUR_MIN, SELECTION_MODE_BLUR_MAX, &errmsg); | |||
SELECTION_MODE_BLUR_MIN, SELECTION_MODE_BLUR_MAX); | if (errmsg) | |||
errx(EXIT_FAILURE, "option --select: '%s' is %s", value, errmsg); | ||||
} else { // SELECTION_MODE_HIDE | } else { // SELECTION_MODE_HIDE | |||
checkMaxInputFileName(value); | checkMaxInputFileName(value); | |||
opt.selection.paramStr = strdup(value); | opt.selection.paramStr = estrdup(value); | |||
} | } | |||
} | } | |||
static void optionsParseLine(char* optarg) | static void optionsParseLine(char *optarg) | |||
{ | { | |||
enum { | enum { | |||
Style = 0, | Style = 0, | |||
Width, | Width, | |||
Color, | Color, | |||
Opacity, | Opacity, | |||
Mode | Mode | |||
}; | }; | |||
char* const token[] = { | char *const token[] = { | |||
[Style] = "style", | [Style] = "style", | |||
[Width] = "width", | [Width] = "width", | |||
[Color] = "color", | [Color] = "color", | |||
[Opacity] = "opacity", | [Opacity] = "opacity", | |||
[Mode] = "mode", | [Mode] = "mode", | |||
NULL | NULL | |||
}; | }; | |||
char* subopts = optarg; | char *subopts = optarg; | |||
char* value = NULL; | char *value = NULL; | |||
const char *errmsg; | ||||
while (*subopts != '\0') { | while (*subopts != '\0') { | |||
switch (getsubopt(&subopts, token, &value)) { | switch (getsubopt(&subopts, token, &value)) { | |||
case Style: | case Style: | |||
if (!optionsParseIsString(value)) { | if (!optionsParseIsString(value)) { | |||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | errx(EXIT_FAILURE, "Missing value for suboption '%s'", | |||
token[Style]); | token[Style]); | |||
} | } | |||
if (!strncmp(value, "dash", 4)) | if (!strncmp(value, "dash", 4)) | |||
opt.lineStyle = LineOnOffDash; | opt.lineStyle = LineOnOffDash; | |||
else if (!strncmp(value, "solid", 5)) | else if (!strncmp(value, "solid", 5)) | |||
opt.lineStyle = LineSolid; | opt.lineStyle = LineSolid; | |||
else { | else { | |||
errx(EXIT_FAILURE, "Unknown value for suboption '%s': %s", | errx(EXIT_FAILURE, "Unknown value for suboption '%s': %s", | |||
token[Style], value); | token[Style], value); | |||
} | } | |||
break; | break; | |||
case Width: | case Width: | |||
if (!optionsParseIsString(value)) { | opt.lineWidth = optionsParseNum(value, 1, 8, &errmsg); | |||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | if (errmsg) { | |||
token[Width]); | if (value == NULL) | |||
} | value = "(null)"; | |||
errx(EXIT_FAILURE, "option --line: suboption '%s': '%s' is %s", | ||||
opt.lineWidth = optionsParseRequiredNumber(value); | token[Width], value, errmsg); | |||
if (opt.lineWidth <= 0 || opt.lineWidth > 8) { | ||||
errx(EXIT_FAILURE, "Value of the range (1..8) for " | ||||
"suboption '%s': %d", token[Width], opt.lineWidth); | ||||
} | } | |||
break; | break; | |||
case Color: | case Color: | |||
if (!optionsParseIsString(value)) { | if (!optionsParseIsString(value)) { | |||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | errx(EXIT_FAILURE, "Missing value for suboption '%s'", | |||
token[Color]); | token[Color]); | |||
} | } | |||
opt.lineColor = strdup(value); | opt.lineColor = estrdup(value); | |||
break; | break; | |||
case Mode: | case Mode: | |||
if (!optionsParseIsString(value)) { | if (!optionsParseIsString(value)) { | |||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | errx(EXIT_FAILURE, "Missing value for suboption '%s'", | |||
token[Mode]); | token[Mode]); | |||
} | } | |||
bool isValidMode = !strncmp(value, LINE_MODE_S_CLASSIC, LINE_MODE_L_ CLASSIC); | bool isValidMode = !strncmp(value, LINE_MODE_S_CLASSIC, LINE_MODE_L_ CLASSIC); | |||
isValidMode = isValidMode || !strncmp(value, LINE_MODE_S_EDGE, LINE_ MODE_L_EDGE); | isValidMode = isValidMode || !strncmp(value, LINE_MODE_S_EDGE, LINE_ MODE_L_EDGE); | |||
if (!isValidMode) { | if (!isValidMode) { | |||
errx(EXIT_FAILURE, "Unknown value for suboption '%s': %s", | errx(EXIT_FAILURE, "Unknown value for suboption '%s': %s", | |||
token[Mode], value); | token[Mode], value); | |||
} | } | |||
opt.lineMode = strdup(value); | opt.lineMode = estrdup(value); | |||
break; | break; | |||
case Opacity: | case Opacity: | |||
if (!optionsParseIsString(value)) { | opt.lineOpacity = optionsParseNum(value, | |||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | SELECTION_OPACITY_MIN, SELECTION_OPACITY_MAX, &errmsg); | |||
token[Opacity]); | if (errmsg) { | |||
if (value == NULL) | ||||
value = "(null)"; | ||||
errx(EXIT_FAILURE, "option --line: suboption %s: '%s' is %s", | ||||
token[Opacity], value, errmsg); | ||||
} | } | |||
opt.lineOpacity = optionsParseRequiredNumber(value); | ||||
break; | break; | |||
default: | default: | |||
errx(EXIT_FAILURE, "No match found for token: '%s'", value); | errx(EXIT_FAILURE, "No match found for token: '%s'", value); | |||
break; | break; | |||
} | } | |||
} /* while */ | } /* while */ | |||
} | } | |||
static void optionsParseWindowClassName(const char* windowClassName) | static void optionsParseWindowClassName(const char *windowClassName) | |||
{ | { | |||
assert(windowClassName != NULL); | assert(windowClassName != NULL); | |||
if (windowClassName[0] != '\0') | if (windowClassName[0] != '\0') | |||
opt.windowClassName = strndup(windowClassName, MAX_LEN_WINDOW_CLASS_NAME ); | opt.windowClassName = strndup(windowClassName, MAX_LEN_WINDOW_CLASS_NAME ); | |||
} | } | |||
static bool accessFileOk(const char* const pathName) | static bool accessFileOk(const char *const pathName) | |||
{ | { | |||
errno = 0; | errno = 0; | |||
return (0 == access(pathName, W_OK)); | return (0 == access(pathName, W_OK)); | |||
} | } | |||
static char* getPathOfStdout(void) | static char *getPathOfStdout(void) | |||
{ | { | |||
char path[16] = {"/dev/stdout"}; | char path[16] = {"/dev/stdout"}; | |||
size_t const len = sizeof(path); | const size_t len = sizeof(path); | |||
if (!accessFileOk(path)) { | if (!accessFileOk(path)) { | |||
snprintf(path, len, "/dev/fd/%d", STDOUT_FILENO); | snprintf(path, len, "/dev/fd/%d", STDOUT_FILENO); | |||
if (!accessFileOk(path)) { | if (!accessFileOk(path)) { | |||
snprintf(path, len, "/proc/self/fd/%d", STDOUT_FILENO); | snprintf(path, len, "/proc/self/fd/%d", STDOUT_FILENO); | |||
if (!accessFileOk(path)) { | if (!accessFileOk(path)) { | |||
// We quit because imlib2 will fail later anyway. | // We quit because imlib2 will fail later anyway. | |||
err(EXIT_FAILURE, "access to stdout failed"); | err(EXIT_FAILURE, "access to stdout failed"); | |||
} | } | |||
} | } | |||
} | } | |||
return strndup(path, len); | return strndup(path, len); | |||
} | } | |||
void optionsParse(int argc, char** argv) | void optionsParse(int argc, char *argv[]) | |||
{ | { | |||
static char stropts[] = "a:ofipbcd:e:hmq:s::t:uvzn:l:D:k::C:S:F:"; | static char stropts[] = "a:ofipbcd:e:hmq:s::t:uvzn:l:D:k::C:S:F:M:"; | |||
static struct option lopts[] = { | static struct option lopts[] = { | |||
/* actions */ | /* actions */ | |||
{ "help", no_argument, 0, 'h' }, | { "help", no_argument, 0, 'h' }, | |||
{ "version", no_argument, 0, 'v' }, | { "version", no_argument, 0, 'v' }, | |||
{ "count", no_argument, 0, 'c' }, | { "count", no_argument, 0, 'c' }, | |||
{ "focused", no_argument, 0, 'u' }, | { "focused", no_argument, 0, 'u' }, | |||
{ "focussed", no_argument, 0, 'u' }, /* macquarie dictionary has both sp ellings */ | { "focussed", no_argument, 0, 'u' }, /* macquarie dictionary has both sp ellings */ | |||
{ "border", no_argument, 0, 'b' }, | { "border", no_argument, 0, 'b' }, | |||
{ "multidisp", no_argument, 0, 'm' }, | { "multidisp", no_argument, 0, 'm' }, | |||
skipping to change at line 358 | skipping to change at line 392 | |||
{ "delay", required_argument, 0, 'd' }, | { "delay", required_argument, 0, 'd' }, | |||
{ "quality", required_argument, 0, 'q' }, | { "quality", required_argument, 0, 'q' }, | |||
{ "exec", required_argument, 0, 'e' }, | { "exec", required_argument, 0, 'e' }, | |||
{ "autoselect", required_argument, 0, 'a' }, | { "autoselect", required_argument, 0, 'a' }, | |||
{ "display", required_argument, 0, 'D' }, | { "display", required_argument, 0, 'D' }, | |||
{ "note", required_argument, 0, 'n' }, | { "note", required_argument, 0, 'n' }, | |||
{ "line", required_argument, 0, 'l' }, | { "line", required_argument, 0, 'l' }, | |||
{ "class", required_argument, 0, 'C' }, | { "class", required_argument, 0, 'C' }, | |||
{ "script", required_argument, 0, 'S' }, | { "script", required_argument, 0, 'S' }, | |||
{ "file", required_argument, 0, 'F' }, | { "file", required_argument, 0, 'F' }, | |||
{ "monitor", required_argument, 0, 'M'}, | ||||
{ 0, 0, 0, 0 } | { 0, 0, 0, 0 } | |||
}; | }; | |||
int optch = 0, cmdx = 0; | int optch = 0, cmdx = 0; | |||
const char *errmsg; | ||||
/* Now to pass some optionarinos */ | /* Now to pass some optionarinos */ | |||
while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != EOF) { | while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != EOF) { | |||
switch (optch) { | switch (optch) { | |||
case 0: | case 0: | |||
break; | break; | |||
case 'h': | case 'h': | |||
showUsage(); | showUsage(); | |||
break; | break; | |||
case 'v': | case 'v': | |||
showVersion(); | showVersion(); | |||
break; | break; | |||
case 'b': | case 'b': | |||
opt.border = 1; | opt.border = 1; | |||
break; | break; | |||
case 'd': | case 'd': | |||
opt.delay = nonNegativeNumber(optionsParseRequiredNumber(optarg)); | opt.delay = optionsParseNum(optarg, 0, INT_MAX, &errmsg); | |||
if (errmsg) { | ||||
errx(EXIT_FAILURE, "option --delay: '%s' is %s", optarg, | ||||
errmsg); | ||||
} | ||||
break; | break; | |||
case 'e': | case 'e': | |||
opt.exec = strdup(optarg); | opt.exec = estrdup(optarg); | |||
break; | break; | |||
case 'm': | case 'm': | |||
opt.multidisp = 1; | opt.multidisp = 1; | |||
break; | break; | |||
case 'q': | case 'q': | |||
opt.quality = optionsParseRequiredNumber(optarg); | opt.quality = optionsParseNum(optarg, 1, 100, &errmsg); | |||
if (errmsg) { | ||||
errx(EXIT_FAILURE, "option --quality: '%s' is %s", optarg, | ||||
errmsg); | ||||
} | ||||
break; | break; | |||
case 's': | case 's': | |||
optionsParseSelection(optarg); | optionsParseSelection(optarg); | |||
break; | break; | |||
case 'u': | case 'u': | |||
opt.focused = 1; | opt.focused = 1; | |||
break; | break; | |||
case 'c': | case 'c': | |||
opt.countdown = 1; | opt.countdown = 1; | |||
break; | break; | |||
skipping to change at line 435 | skipping to change at line 479 | |||
optionsParseLine(optarg); | optionsParseLine(optarg); | |||
break; | break; | |||
case 'k': | case 'k': | |||
opt.stack = 1; | opt.stack = 1; | |||
optionsParseStack(optarg); | optionsParseStack(optarg); | |||
break; | break; | |||
case 'C': | case 'C': | |||
optionsParseWindowClassName(optarg); | optionsParseWindowClassName(optarg); | |||
break; | break; | |||
case 'S': | case 'S': | |||
opt.script = strdup(optarg); | opt.script = estrdup(optarg); | |||
break; | break; | |||
case 'F': | case 'F': | |||
optionsParseFileName(optarg); | optionsParseFileName(optarg); | |||
break; | break; | |||
case 'M': | ||||
opt.monitor = optionsParseNum(optarg, 0, INT_MAX, &errmsg); | ||||
if (errmsg) { | ||||
errx(EXIT_FAILURE, "option --monitor: '%s' is %s", optarg, | ||||
errmsg); | ||||
} | ||||
break; | ||||
case '?': | case '?': | |||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | |||
default: | default: | |||
break; | break; | |||
} | } | |||
} | } | |||
/* Now the leftovers, which must be files */ | /* Now the leftovers, which must be files */ | |||
while (optind < argc) { | while (optind < argc) { | |||
/* If recursive is NOT set, but the only argument is a directory | /* If recursive is NOT set, but the only argument is a directory | |||
name, we grab all the files in there, but not subdirs */ | name, we grab all the files in there, but not subdirs */ | |||
if (!opt.outputFile) { | if (!opt.outputFile) { | |||
optionsParseFileName(argv[optind++]); | optionsParseFileName(argv[optind++]); | |||
bool const redirectChar = ( opt.outputFile[0] == '-' | const bool redirectChar = ( opt.outputFile[0] == '-' | |||
&& opt.outputFile[1] == '\0'); | && opt.outputFile[1] == '\0'); | |||
if (redirectChar) { | if (redirectChar) { | |||
free(opt.outputFile); | free(opt.outputFile); | |||
opt.outputFile = getPathOfStdout(); | opt.outputFile = getPathOfStdout(); | |||
opt.overwrite = 1; | opt.overwrite = 1; | |||
opt.thumb = 0; | opt.thumbWorP = 0; | |||
} | } | |||
} else | } else | |||
warnx("unrecognised option %s", argv[optind++]); | warnx("unrecognised option %s", argv[optind++]); | |||
} | } | |||
/* So that we can safely be called again */ | /* So that we can safely be called again */ | |||
optind = 1; | optind = 1; | |||
} | } | |||
char* optionsNameThumbnail(const char* name) | static void showUsage(void) | |||
{ | { | |||
const char* const thumbSuffix = "-thumb"; | fputs(/* Check that everything lines up after any changes. */ | |||
"usage: " PACKAGE " [-bcfhimopuvz] [-a X,Y,W,H] [-C NAME] [-D DISPLAY]\ | ||||
n" | ||||
" [-d SEC] [-e CMD] [-F FILE] [-k OPT] [-l STYLE] [-M NUM]\ | ||||
n" | ||||
" [-n OPTS] [-q NUM] [-S CMD] [-s OPTS] [-t NUM | GEOM] [FI | ||||
LE]\n", | ||||
stdout); | ||||
exit(0); | ||||
} | ||||
static void showVersion(void) | ||||
{ | ||||
printf(PACKAGE " version " VERSION "\n"); | ||||
exit(0); | ||||
} | ||||
char *optionsNameThumbnail(const char *name) | ||||
{ | ||||
const char *const thumbSuffix = "-thumb"; | ||||
const size_t thumbSuffixLength = 7; | const size_t thumbSuffixLength = 7; | |||
const size_t newNameLength = strlen(name) + thumbSuffixLength; | const size_t newNameLength = strlen(name) + thumbSuffixLength; | |||
char* newName = calloc(1, newNameLength); | char *newName = calloc(1, newNameLength); | |||
if (!newName) | if (!newName) | |||
err(EXIT_FAILURE, "Unable to allocate thumbnail"); | err(EXIT_FAILURE, "Unable to allocate thumbnail"); | |||
const char* const extension = strrchr(name, '.'); | const char *const extension = strrchr(name, '.'); | |||
if (extension) { | if (extension) { | |||
/* We add one so length includes '\0'*/ | /* We add one so length includes '\0'*/ | |||
const ptrdiff_t nameLength = (extension - name) + 1; | const ptrdiff_t nameLength = (extension - name) + 1; | |||
strlcpy(newName, name, nameLength); | strlcpy(newName, name, nameLength); | |||
strlcat(newName, thumbSuffix, newNameLength); | strlcat(newName, thumbSuffix, newNameLength); | |||
strlcat(newName, extension, newNameLength); | strlcat(newName, extension, newNameLength); | |||
} else | } else | |||
snprintf(newName, newNameLength, "%s%s", name, thumbSuffix); | snprintf(newName, newNameLength, "%s%s", name, thumbSuffix); | |||
return newName; | return newName; | |||
} | } | |||
void optionsParseAutoselect(char* optarg) | void optionsParseAutoselect(char *optarg) | |||
{ | { | |||
char* token; | char *token; | |||
const char tokenDelimiter[2] = ","; | int *dimensions[] = {&opt.autoselectX, &opt.autoselectY, &opt.autoselectW, | |||
int dimensions[4]; | &opt.autoselectH, NULL /* Sentinel. */}; | |||
int i = 0; | int i = 0; | |||
int min; | ||||
const char *errmsg; | ||||
if (strchr(optarg, ',')) { /* geometry dimensions must be in format x,y,w,h | /* Geometry dimensions must be in format x,y,w,h */ | |||
*/ | token = strtok(optarg, ","); | |||
dimensions[i++] = optionsParseRequiredNumber(strtok(optarg, tokenDelimit | for (; token != NULL; token = strtok(NULL, ",")) { | |||
er)); | if (dimensions[i] == NULL) | |||
while ((token = strtok(NULL, tokenDelimiter))) | errx(EXIT_FAILURE, "option --autoselect: too many dimensions"); | |||
dimensions[i++] = optionsParseRequiredNumber(token); | ||||
opt.autoselect = 1; | min = i >= 2; /* X,Y offsets may be 0. Width and height may not. */ | |||
opt.autoselectX = dimensions[0]; | *dimensions[i] = optionsParseNum(token, min, INT_MAX, &errmsg); | |||
opt.autoselectY = dimensions[1]; | if (errmsg) { | |||
opt.autoselectW = dimensions[2]; | errx(EXIT_FAILURE, "option --autoselect: '%s' is %s", token, | |||
opt.autoselectH = dimensions[3]; | errmsg); | |||
} | ||||
i++; | ||||
if (i != 4) | opt.autoselect = 1; | |||
errx(EXIT_FAILURE, "option 'autoselect' require 4 arguments"); | } | |||
} else | if (i < 4) | |||
errx(EXIT_FAILURE, "invalid format for option -- 'autoselect'"); | errx(EXIT_FAILURE, "option --autoselect: too few dimensions"); | |||
} | } | |||
void optionsParseDisplay(char* optarg) | void optionsParseDisplay(char *optarg) | |||
{ | { | |||
opt.display = strndup(optarg, MAX_DISPLAY_NAME); | opt.display = strndup(optarg, MAX_DISPLAY_NAME); | |||
if (!opt.display) | if (!opt.display) | |||
err(EXIT_FAILURE, "Unable to allocate display"); | err(EXIT_FAILURE, "Unable to allocate display"); | |||
} | } | |||
void optionsParseThumbnail(char* optarg) | static void optionsParseThumbnail(char *optarg) | |||
{ | { | |||
char* token; | char *height; | |||
const char *errmsg; | ||||
if (strchr(optarg, 'x')) { /* We want to specify the geometry */ | if ((height = strchr(optarg, 'x')) != NULL) { /* optarg is a resolution. */ | |||
token = strtok(optarg, "x"); | /* optarg holds the width, height holds the height. */ | |||
opt.thumbWidth = optionsParseRequiredNumber(token); | *height++ = '\0'; | |||
token = strtok(NULL, "x"); | ||||
if (token) { | opt.thumbWorP = optionsParseNum(optarg, 1, INT_MAX, &errmsg); | |||
opt.thumbWidth = optionsParseRequiredNumber(optarg); | if (errmsg) { | |||
opt.thumbHeight = optionsParseRequiredNumber(token); | errx(EXIT_FAILURE, "option --thumb: resolution width '%s' is %s", | |||
optarg, errmsg); | ||||
if (opt.thumbWidth < 0) | } | |||
opt.thumbWidth = 1; | ||||
if (opt.thumbHeight < 0) | opt.thumbH = optionsParseNum(height, 1, INT_MAX, &errmsg); | |||
opt.thumbHeight = 1; | if (errmsg) { | |||
errx(EXIT_FAILURE, "option --thumb: resolution height '%s' is %s", | ||||
if (!opt.thumbWidth && !opt.thumbHeight) | height, errmsg); | |||
opt.thumb = 0; | } | |||
else | } else { /* optarg is a percentage. */ | |||
opt.thumb = 1; | opt.thumbWorP = optionsParseNum(optarg, 1, INT_MAX, &errmsg); | |||
if (errmsg) { | ||||
errx(EXIT_FAILURE, "option --thumb: percentage '%s' is %s", optarg, | ||||
errmsg); | ||||
} | } | |||
} else { | ||||
opt.thumb = optionsParseRequiredNumber(optarg); | ||||
if (opt.thumb < 1) | ||||
opt.thumb = 1; | ||||
else if (opt.thumb > 100) | ||||
opt.thumb = 100; | ||||
} | } | |||
} | } | |||
void optionsParseFileName(const char* optarg) | void optionsParseFileName(const char *optarg) | |||
{ | { | |||
checkMaxOutputFileName(optarg); | checkMaxOutputFileName(optarg); | |||
opt.outputFile = strdup(optarg); | opt.outputFile = estrdup(optarg); | |||
} | } | |||
void optionsParseNote(char* optarg) | void optionsParseNote(char *optarg) | |||
{ | { | |||
opt.note = strdup(optarg); | if (opt.note) | |||
free(opt.note); | ||||
opt.note = estrdup(optarg); | ||||
if (!opt.note) | if (!opt.note) | |||
return; | return; | |||
if (opt.note[0] == '\0') | if (opt.note[0] == '\0') | |||
errx(EXIT_FAILURE, "Required arguments for --note."); | errx(EXIT_FAILURE, "Required arguments for --note."); | |||
scrotNoteNew(opt.note); | scrotNoteNew(opt.note); | |||
} | } | |||
/* | /* | |||
Return: | Return: | |||
0 : It does not match | 0 : It does not match | |||
1 : If it matches | 1 : If it matches | |||
*/ | */ | |||
int optionsCompareWindowClassName(const char* targetClassName) | int optionsCompareWindowClassName(const char *targetClassName) | |||
{ | { | |||
assert(targetClassName != NULL); | assert(targetClassName != NULL); | |||
assert(opt.windowClassName != NULL); | assert(opt.windowClassName != NULL); | |||
return !!(!strncmp(targetClassName, opt.windowClassName, MAX_LEN_WINDOW_CLAS S_NAME - 1)); | return !!(!strncmp(targetClassName, opt.windowClassName, MAX_LEN_WINDOW_CLAS S_NAME - 1)); | |||
} | } | |||
void showVersion(void) | ||||
{ | ||||
printf(SCROT_PACKAGE " version " SCROT_VERSION "\n"); | ||||
exit(0); | ||||
} | ||||
void showUsage(void) | ||||
{ | ||||
fputs(/* Check that everything lines up after any changes. */ | ||||
"usage: " SCROT_PACKAGE " [-bcfhikmopsuvz] [-a X,Y,W,H] [-C NAME] [-D D | ||||
ISPLAY]" | ||||
"\n" | ||||
" [-F FILE] [-d SEC] [-e CMD] [-l STYLE] [-n OPTS] [-q NUM] | ||||
[-S CMD] \n" | ||||
" [-t NUM | GEOM] [FILE]\n", | ||||
stdout); | ||||
exit(0); | ||||
} | ||||
End of changes. 61 change blocks. | ||||
133 lines changed or deleted | 208 lines changed or added |