options.c (scrot-1.6.tar.bz2) | : | options.c (scrot-1.7.tar.bz2) | ||
---|---|---|---|---|
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 <daltomi@disroot.org> | Copyright 2019-2021 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 IFo Hancroft <contact@ifohancroft.com> | ||||
Copyright 2021 Peter Wu <peterwu@hotmail.com> | ||||
Copyright 2021 Wilson Smith <01wsmith+gh@gmail.com> | ||||
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 38 | skipping to change at line 42 | |||
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 "scrot.h" | ||||
#include "options.h" | #include "options.h" | |||
#include "scrot.h" | ||||
#include <assert.h> | #include <assert.h> | |||
#define MAX_LEN_WINDOW_CLASS_NAME 80 | #define STR_LEN_MAX_FILENAME(msg, fileName) do { \ | |||
if (strlen((fileName)) > MAX_FILENAME) { \ | ||||
errx(EXIT_FAILURE, #msg " filename too long, must be " \ | ||||
"less than %d characters", MAX_FILENAME); \ | ||||
} \ | ||||
} while(0) | ||||
#define checkMaxOutputFileName(fileName) \ | ||||
STR_LEN_MAX_FILENAME(output, (fileName)) | ||||
#define checkMaxInputFileName(fileName) \ | ||||
STR_LEN_MAX_FILENAME(input, (fileName)) | ||||
enum { | ||||
MAX_LEN_WINDOW_CLASS_NAME = 80, //characters | ||||
MAX_FILENAME = 256, // characters | ||||
MAX_DISPLAY_NAME = 256, // characters | ||||
}; | ||||
ScrotOptions opt = { | ||||
.quality = 75, | ||||
.lineStyle = LineSolid, | ||||
.lineWidth = 1, | ||||
.lineOpacity = SELECTION_OPACITY_DEFAULT, | ||||
.lineMode = LINE_MODE_S_CLASSIC, | ||||
.stackDirection = HORIZONTAL, | ||||
}; | ||||
int optionsParseRequiredNumber(char const* str) | ||||
{ | ||||
assert(NULL != str); // fix yout caller function, | ||||
// the user does not impose this behavior | ||||
char* end = NULL; | ||||
long ret = 0L; | ||||
errno = 0; | ||||
ret = strtol(str, &end, 10); | ||||
if (errno) | ||||
goto range_error; | ||||
if (str == end) | ||||
errx(EXIT_FAILURE, "the option is not a number: %s", end); | ||||
if (ret > INT_MAX || ret < INT_MIN) { | ||||
errno = ERANGE; | ||||
goto range_error; | ||||
} | ||||
static void scrot_parse_option_array(int argc, char **argv); | return ret; | |||
scrotoptions opt; | ||||
void | range_error: | |||
init_parse_options(int argc, char **argv) | err(EXIT_FAILURE, "error strtol"); | |||
} | ||||
static int nonNegativeNumber(int number) | ||||
{ | { | |||
/* Set default options */ | return (number < 0) ? 0 : number; | |||
memset(&opt, 0, sizeof(scrotoptions)); | } | |||
opt.quality = 75; | int optionsParseRequireRange(int n, int lo, int hi) | |||
opt.overwrite = 0; | { | |||
opt.line_style = LineSolid; | return (n < lo ? lo : n > hi ? hi : n); | |||
opt.line_width = 1; | } | |||
opt.line_opacity = 100; | ||||
opt.line_mode = LINE_MODE_CLASSIC; | bool optionsParseIsString(char const* const str) | |||
{ | ||||
return (str && (str[0] != '\0')); | ||||
} | ||||
static void optionsParseStack(char const* optarg) | ||||
{ | ||||
// the suboption it's optional | ||||
if (!optarg) { | ||||
opt.stackDirection = HORIZONTAL; | ||||
return; | ||||
} | ||||
char const* value = strchr(optarg, '='); | ||||
/* Parse the cmdline args */ | if (value) | |||
scrot_parse_option_array(argc, argv); | ++value; | |||
else | ||||
value = optarg; | ||||
if (*value == 'v') | ||||
opt.stackDirection = VERTICAL; | ||||
else if (*value == 'h') | ||||
opt.stackDirection = HORIZONTAL; | ||||
else { | ||||
errx(EXIT_FAILURE, "option --stack: Unknown value for suboption '%s'", | ||||
value); | ||||
} | ||||
} | } | |||
int | static void optionsParseSelection(char const* optarg) | |||
options_parse_required_number(char *str) | ||||
{ | { | |||
char *end = NULL; | // the suboption it's optional | |||
int ret = 0; | if (!optarg) { | |||
errno = 0; | opt.selection.mode = SELECTION_MODE_CAPTURE; | |||
return; | ||||
} | ||||
if (str != NULL) { | char const* value = strchr(optarg, '='); | |||
ret = strtol(str, &end, 10); | ||||
} | ||||
if (str == end) { | if (value) | |||
fprintf(stderr, "the option is not a number: %s\n", end); | ++value; | |||
exit(EXIT_FAILURE); | else | |||
} | value = optarg; | |||
if (!strncmp(value, SELECTION_MODE_S_CAPTURE, SELECTION_MODE_L_CAPTURE)) { | ||||
opt.selection.mode = SELECTION_MODE_CAPTURE; | ||||
return; /* it has no parameter */ | ||||
} | ||||
else if (!strncmp(value, SELECTION_MODE_S_HIDE, SELECTION_MODE_L_HIDE)) { | ||||
opt.selection.mode = SELECTION_MODE_HIDE; | ||||
value += SELECTION_MODE_L_HIDE; | ||||
} | ||||
else if (!strncmp(value, SELECTION_MODE_S_HOLE, SELECTION_MODE_L_HOLE)) { | ||||
opt.selection.mode = SELECTION_MODE_HOLE; | ||||
} | ||||
else if (!strncmp(value, SELECTION_MODE_S_BLUR, SELECTION_MODE_L_BLUR)) { | ||||
opt.selection.mode = SELECTION_MODE_BLUR; | ||||
opt.selection.paramNum = SELECTION_MODE_BLUR_DEFAULT; | ||||
value += SELECTION_MODE_L_BLUR; | ||||
} | ||||
else { | ||||
errx(EXIT_FAILURE, "option --select: Unknown value for suboption '%s'", | ||||
value); | ||||
} | ||||
if (errno) { | if (opt.selection.mode & SELECTION_MODE_NOT_NEED_PARAM) | |||
perror("strtol"); | return; | |||
exit(EXIT_FAILURE); | ||||
} | ||||
return ret; | if (*value != SELECTION_MODE_SEPARATOR) | |||
} | return; | |||
static void | if (*(++value) == '\0') | |||
options_parse_line(char *optarg) | errx(EXIT_FAILURE, "option --select: Invalid parameter."); | |||
{ | ||||
enum {STYLE = 0, WIDTH, COLOR, OPACITY, MODE}; | ||||
char *const token[] = { | if (opt.selection.mode == SELECTION_MODE_BLUR) { | |||
[STYLE] = "style", | int const num = nonNegativeNumber(optionsParseRequiredNumber(value)); | |||
[WIDTH] = "width", | ||||
[COLOR] = "color", | ||||
[OPACITY] = "opacity", | ||||
[MODE] = "mode", | ||||
NULL | ||||
}; | ||||
char *subopts = optarg; | opt.selection.paramNum = optionsParseRequireRange(num, | |||
char *value = NULL; | SELECTION_MODE_BLUR_MIN, SELECTION_MODE_BLUR_MAX); | |||
while (*subopts != '\0') { | } else { // SELECTION_MODE_HIDE | |||
switch(getsubopt(&subopts, token, &value)) { | ||||
case STYLE: | checkMaxInputFileName(value); | |||
opt.selection.paramStr = strdup(value); | ||||
} | ||||
} | ||||
if (value == NULL || *value == '\0') { | static void optionsParseLine(char* optarg) | |||
fprintf(stderr, "Missing value for " | { | |||
"suboption '%s'\n", token[STYLE]); | enum { | |||
exit(EXIT_FAILURE); | Style = 0, | |||
Width, | ||||
Color, | ||||
Opacity, | ||||
Mode | ||||
}; | ||||
char* const token[] = { | ||||
[Style] = "style", | ||||
[Width] = "width", | ||||
[Color] = "color", | ||||
[Opacity] = "opacity", | ||||
[Mode] = "mode", | ||||
NULL | ||||
}; | ||||
char* subopts = optarg; | ||||
char* value = NULL; | ||||
while (*subopts != '\0') { | ||||
switch (getsubopt(&subopts, token, &value)) { | ||||
case Style: | ||||
if (!optionsParseIsString(value)) { | ||||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | ||||
token[Style]); | ||||
} | } | |||
if (!strncmp(value, "dash", 4)) | if (!strncmp(value, "dash", 4)) | |||
opt.line_style = LineOnOffDash; | opt.lineStyle = LineOnOffDash; | |||
else if (!strncmp(value, "solid", 5)) | else if (!strncmp(value, "solid", 5)) | |||
opt.line_style = LineSolid; | opt.lineStyle = LineSolid; | |||
else { | else { | |||
fprintf(stderr, "Unknown value for " | errx(EXIT_FAILURE, "Unknown value for suboption '%s': %s", | |||
"suboption '%s': %s\n", token[STYLE], value); | token[Style], value); | |||
exit(EXIT_FAILURE); | ||||
} | } | |||
break; | break; | |||
case Width: | ||||
case WIDTH: | if (!optionsParseIsString(value)) { | |||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | ||||
if (value == NULL) { | token[Width]); | |||
fprintf(stderr, "Missing value for " | ||||
"suboption '%s'\n", token[WIDTH]); | ||||
exit(EXIT_FAILURE); | ||||
} | } | |||
opt.line_width = options_parse_required_number(value); | opt.lineWidth = optionsParseRequiredNumber(value); | |||
if (opt.line_width <= 0 || opt.line_width > 8){ | if (opt.lineWidth <= 0 || opt.lineWidth > 8) { | |||
fprintf(stderr, "Value of the range (1..8) for " | errx(EXIT_FAILURE, "Value of the range (1..8) for " | |||
"suboption '%s': %d\n", token[WIDTH], opt.line_width); | "suboption '%s': %d", token[Width], opt.lineWidth); | |||
exit(EXIT_FAILURE); | ||||
} | } | |||
break; | break; | |||
case Color: | ||||
case COLOR: | if (!optionsParseIsString(value)) { | |||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | ||||
if (value == NULL || *value == '\0') { | token[Color]); | |||
fprintf(stderr, "Missing value for " | ||||
"suboption '%s'\n", token[COLOR]); | ||||
exit(EXIT_FAILURE); | ||||
} | } | |||
opt.line_color = strdup(value); | opt.lineColor = strdup(value); | |||
break; | break; | |||
case Mode: | ||||
case MODE: | if (!optionsParseIsString(value)) { | |||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | ||||
if (value == NULL || *value == '\0') { | token[Mode]); | |||
fprintf(stderr, "Missing value for " | ||||
"suboption '%s'\n", token[MODE]); | ||||
exit(EXIT_FAILURE); | ||||
} | } | |||
bool isValidMode = (bool)(0 == strncmp(value, LINE_MODE_CLASSIC, LIN E_MODE_CLASSIC_LEN) ); | bool isValidMode = !strncmp(value, LINE_MODE_S_CLASSIC, LINE_MODE_L_ CLASSIC); | |||
isValidMode = isValidMode || (bool)(0 == strncmp(value, LINE_MODE_ED GE, LINE_MODE_EDGE_LEN)); | isValidMode = isValidMode || !strncmp(value, LINE_MODE_S_EDGE, LINE_ MODE_L_EDGE); | |||
if(isValidMode == false) { | if (!isValidMode) { | |||
fprintf(stderr, "Unknown value for " | errx(EXIT_FAILURE, "Unknown value for suboption '%s': %s", | |||
"suboption '%s': %s\n", token[MODE], value); | token[Mode], value); | |||
exit(EXIT_FAILURE); | ||||
} | } | |||
opt.line_mode = strdup(value); | opt.lineMode = strdup(value); | |||
break; | ||||
case Opacity: | ||||
if (!optionsParseIsString(value)) { | ||||
errx(EXIT_FAILURE, "Missing value for suboption '%s'", | ||||
token[Opacity]); | ||||
} | ||||
opt.lineOpacity = optionsParseRequiredNumber(value); | ||||
break; | ||||
default: | ||||
errx(EXIT_FAILURE, "No match found for token: '%s'", value); | ||||
break; | break; | |||
} | ||||
} /* while */ | ||||
} | ||||
case OPACITY: | static void optionsParseWindowClassName(const char* windowClassName) | |||
{ | ||||
assert(windowClassName != NULL); | ||||
if (value == NULL) { | if (windowClassName[0] != '\0') | |||
fprintf(stderr, "Missing value for " | opt.windowClassName = strndup(windowClassName, MAX_LEN_WINDOW_CLASS_NAME | |||
"suboption '%s'\n", token[OPACITY]); | ); | |||
exit(EXIT_FAILURE); | } | |||
} | ||||
opt.line_opacity = options_parse_required_number(value); | static bool accessFileOk(const char* const pathName) | |||
{ | ||||
errno = 0; | ||||
return (0 == access(pathName, W_OK)); | ||||
} | ||||
if (opt.line_opacity < 10 || opt.line_opacity > 100){ | static char* getPathOfStdout(void) | |||
fprintf(stderr, "Value of the range (10..100) for " | { | |||
"suboption '%s': %d\n", token[OPACITY], opt.line_opacity); | char path[16] = {"/dev/stdout"}; | |||
exit(EXIT_FAILURE); | size_t const len = sizeof(path); | |||
} | ||||
break; | if (!accessFileOk(path)) { | |||
default: | snprintf(path, len, "/dev/fd/%d", STDOUT_FILENO); | |||
fprintf(stderr, "No match found for token: '%s'\n", value); | ||||
exit(EXIT_FAILURE); | ||||
break; | ||||
} | ||||
} /* while */ | if (!accessFileOk(path)) { | |||
} | ||||
static void options_parse_window_class_name(const char* window_class_name) | snprintf(path, len, "/proc/self/fd/%d", STDOUT_FILENO); | |||
{ | ||||
assert(window_class_name != NULL); | ||||
if (window_class_name[0] != '\0' && | if (!accessFileOk(path)) { | |||
strlen(window_class_name) < MAX_LEN_WINDOW_CLASS_NAME) | // We quit because imlib2 will fail later anyway. | |||
{ | err(EXIT_FAILURE, "access to stdout failed"); | |||
opt.window_class_name = strdup(window_class_name); | } | |||
} | ||||
} | } | |||
return strndup(path, len); | ||||
} | } | |||
static void | void optionsParse(int argc, char** argv) | |||
scrot_parse_option_array(int argc, char **argv) | { | |||
{ | static char stropts[] = "a:ofipbcd:e:hmq:s::t:uvzn:l:D:k::C:S:F:"; | |||
static char stropts[] = "a:ofpbcd:e:hmq:st:uv+:zn:l:D:kC:S:"; | ||||
static struct option lopts[] = { | ||||
static struct option lopts[] = { | /* actions */ | |||
/* actions */ | { "help", no_argument, 0, 'h' }, | |||
{"help", 0, 0, 'h'}, /* okay */ | { "version", no_argument, 0, 'v' }, | |||
{"version", 0, 0, 'v'}, /* okay */ | { "count", no_argument, 0, 'c' }, | |||
{"count", 0, 0, 'c'}, | { "focused", no_argument, 0, 'u' }, | |||
{"select", 0, 0, 's'}, | { "focussed", no_argument, 0, 'u' }, /* macquarie dictionary has both sp | |||
{"focused", 0, 0, 'u'}, | ellings */ | |||
{"focussed", 0, 0, 'u'}, /* macquarie dictionary has both spellings */ | { "border", no_argument, 0, 'b' }, | |||
{"border", 0, 0, 'b'}, | { "multidisp", no_argument, 0, 'm' }, | |||
{"multidisp", 0, 0, 'm'}, | { "silent", no_argument, 0, 'z' }, | |||
{"silent", 0, 0, 'z'}, | { "pointer", no_argument, 0, 'p' }, | |||
{"pointer", 0, 0, 'p'}, | { "ignorekeyboard", no_argument, 0, 'i' }, | |||
{"freeze", 0, 0, 'f'}, | { "freeze", no_argument, 0, 'f' }, | |||
{"overwrite", 0, 0, 'o'}, | { "overwrite", no_argument, 0, 'o' }, | |||
{"stack", 0, 0,'k'}, | /* toggles */ | |||
/* toggles */ | { "stack", optional_argument, 0, 'k' }, | |||
{"thumb", 1, 0, 't'}, | { "select", optional_argument, 0, 's' }, | |||
{"delay", 1, 0, 'd'}, | { "thumb", required_argument, 0, 't' }, | |||
{"quality", 1, 0, 'q'}, | { "delay", required_argument, 0, 'd' }, | |||
{"exec", 1, 0, 'e'}, | { "quality", required_argument, 0, 'q' }, | |||
{"debug-level", 1, 0, '+'}, | { "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' }, | |||
{0, 0, 0, 0} | { "file", required_argument, 0, 'F' }, | |||
}; | { 0, 0, 0, 0 } | |||
int optch = 0, cmdx = 0; | }; | |||
int optch = 0, cmdx = 0; | ||||
/* Now to pass some optionarinos */ | ||||
while ((optch = getopt_long(argc, argv, stropts, lopts, &cmdx)) != | /* Now to pass some optionarinos */ | |||
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': | |||
show_usage(); | showUsage(); | |||
break; | break; | |||
case 'v': | case 'v': | |||
show_version(); | showVersion(); | |||
break; | break; | |||
case 'b': | case 'b': | |||
opt.border = 1; | opt.border = 1; | |||
break; | break; | |||
case 'd': | case 'd': | |||
opt.delay = options_parse_required_number(optarg); | opt.delay = nonNegativeNumber(optionsParseRequiredNumber(optarg)); | |||
break; | break; | |||
case 'e': | case 'e': | |||
opt.exec = strdup(optarg); | opt.exec = strdup(optarg); | |||
break; | break; | |||
case 'm': | case 'm': | |||
opt.multidisp = 1; | opt.multidisp = 1; | |||
break; | break; | |||
case 'q': | case 'q': | |||
opt.quality = options_parse_required_number(optarg); | opt.quality = optionsParseRequiredNumber(optarg); | |||
break; | break; | |||
case 's': | case 's': | |||
opt.select = 1; | optionsParseSelection(optarg); | |||
break; | break; | |||
case 'u': | case 'u': | |||
opt.focused = 1; | opt.focused = 1; | |||
break; | break; | |||
case '+': | ||||
opt.debug_level = options_parse_required_number(optarg); | ||||
break; | ||||
case 'c': | case 'c': | |||
opt.countdown = 1; | opt.countdown = 1; | |||
break; | break; | |||
case 't': | case 't': | |||
options_parse_thumbnail(optarg); | optionsParseThumbnail(optarg); | |||
break; | break; | |||
case 'z': | case 'z': | |||
opt.silent = 1; | opt.silent = 1; | |||
break; | break; | |||
case 'p': | case 'p': | |||
opt.pointer = 1; | opt.pointer = 1; | |||
break; | break; | |||
case 'i': | ||||
opt.ignoreKeyboard = 1; | ||||
break; | ||||
case 'f': | case 'f': | |||
opt.freeze = 1; | opt.freeze = 1; | |||
break; | break; | |||
case 'o': | case 'o': | |||
opt.overwrite = 1; | opt.overwrite = 1; | |||
break; | break; | |||
case 'a': | case 'a': | |||
options_parse_autoselect(optarg); | optionsParseAutoselect(optarg); | |||
break; | break; | |||
case 'D': | case 'D': | |||
options_parse_display(optarg); | optionsParseDisplay(optarg); | |||
break; | break; | |||
case 'n': | case 'n': | |||
options_parse_note(optarg); | optionsParseNote(optarg); | |||
break; | break; | |||
case 'l': | case 'l': | |||
options_parse_line(optarg); | optionsParseLine(optarg); | |||
break; | break; | |||
case 'k': | case 'k': | |||
opt.stack = 1; | opt.stack = 1; | |||
break; | optionsParseStack(optarg); | |||
break; | ||||
case 'C': | case 'C': | |||
options_parse_window_class_name(optarg); | optionsParseWindowClassName(optarg); | |||
break; | break; | |||
case 'S': | case 'S': | |||
opt.script = strdup(optarg); | opt.script = strdup(optarg); | |||
break; | break; | |||
case 'F': | ||||
optionsParseFileName(optarg); | ||||
break; | ||||
case '?': | case '?': | |||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | |||
default: | default: | |||
break; | break; | |||
} | } | |||
} | } | |||
/* Now the leftovers, which must be files */ | ||||
while (optind < argc) | ||||
{ | ||||
/* If recursive is NOT set, but the only argument is a directory | ||||
name, we grab all the files in there, but not subdirs */ | ||||
if (!opt.output_file) | ||||
{ | ||||
opt.output_file = argv[optind++]; | ||||
if ( strlen(opt.output_file) > 256 ) { | /* Now the leftovers, which must be files */ | |||
printf("output filename too long.\n"); | while (optind < argc) { | |||
exit(EXIT_FAILURE); | /* If recursive is NOT set, but the only argument is a directory | |||
} | name, we grab all the files in there, but not subdirs */ | |||
if (!opt.outputFile) { | ||||
optionsParseFileName(argv[optind++]); | ||||
bool const redirectChar = ( opt.outputFile[0] == '-' | ||||
&& opt.outputFile[1] == '\0'); | ||||
if (redirectChar) { | ||||
free(opt.outputFile); | ||||
opt.outputFile = getPathOfStdout(); | ||||
opt.overwrite = 1; | ||||
opt.thumb = 0; | ||||
} | ||||
} else | ||||
warnx("unrecognised option %s", argv[optind++]); | ||||
} | ||||
/* So that we can safely be called again */ | ||||
optind = 1; | ||||
} | ||||
char* optionsNameThumbnail(const char* name) | ||||
{ | ||||
const char* const thumbSuffix = "-thumb"; | ||||
const size_t thumbSuffixLength = 7; | ||||
const size_t newNameLength = strlen(name) + thumbSuffixLength; | ||||
char* newName = calloc(1, newNameLength); | ||||
if (!newName) | ||||
err(EXIT_FAILURE, "Unable to allocate thumbnail"); | ||||
const char* const extension = strrchr(name, '.'); | ||||
if (extension) { | ||||
/* We add one so length includes '\0'*/ | ||||
const ptrdiff_t nameLength = (extension - name) + 1; | ||||
strlcpy(newName, name, nameLength); | ||||
strlcat(newName, thumbSuffix, newNameLength); | ||||
strlcat(newName, extension, newNameLength); | ||||
} else | ||||
snprintf(newName, newNameLength, "%s%s", name, thumbSuffix); | ||||
return newName; | ||||
} | ||||
void optionsParseAutoselect(char* optarg) | ||||
{ | ||||
char* token; | ||||
const char tokenDelimiter[2] = ","; | ||||
int dimensions[4]; | ||||
int i = 0; | ||||
if (strchr(optarg, ',')) { /* geometry dimensions must be in format x,y,w,h | ||||
*/ | ||||
dimensions[i++] = optionsParseRequiredNumber(strtok(optarg, tokenDelimit | ||||
er)); | ||||
while ((token = strtok(NULL, tokenDelimiter))) | ||||
dimensions[i++] = optionsParseRequiredNumber(token); | ||||
opt.autoselect = 1; | ||||
opt.autoselectX = dimensions[0]; | ||||
opt.autoselectY = dimensions[1]; | ||||
opt.autoselectW = dimensions[2]; | ||||
opt.autoselectH = dimensions[3]; | ||||
if (i != 4) | ||||
errx(EXIT_FAILURE, "option 'autoselect' require 4 arguments"); | ||||
} else | ||||
errx(EXIT_FAILURE, "invalid format for option -- 'autoselect'"); | ||||
} | ||||
void optionsParseDisplay(char* optarg) | ||||
{ | ||||
opt.display = strndup(optarg, MAX_DISPLAY_NAME); | ||||
if (!opt.display) | ||||
err(EXIT_FAILURE, "Unable to allocate display"); | ||||
} | ||||
void optionsParseThumbnail(char* optarg) | ||||
{ | ||||
char* token; | ||||
if (strchr(optarg, 'x')) { /* We want to specify the geometry */ | ||||
token = strtok(optarg, "x"); | ||||
opt.thumbWidth = optionsParseRequiredNumber(token); | ||||
token = strtok(NULL, "x"); | ||||
if (token) { | ||||
opt.thumbWidth = optionsParseRequiredNumber(optarg); | ||||
opt.thumbHeight = optionsParseRequiredNumber(token); | ||||
if (opt.thumbWidth < 0) | ||||
opt.thumbWidth = 1; | ||||
if (opt.thumbHeight < 0) | ||||
opt.thumbHeight = 1; | ||||
if (!opt.thumbWidth && !opt.thumbHeight) | ||||
opt.thumb = 0; | ||||
else | ||||
opt.thumb = 1; | ||||
} | ||||
} else { | ||||
opt.thumb = optionsParseRequiredNumber(optarg); | ||||
if (opt.thumb < 1) | ||||
opt.thumb = 1; | ||||
else if (opt.thumb > 100) | ||||
opt.thumb = 100; | ||||
} | ||||
} | ||||
if (opt.thumb) | void optionsParseFileName(const char* optarg) | |||
opt.thumb_file = name_thumbnail(opt.output_file); | { | |||
} | checkMaxOutputFileName(optarg); | |||
else | opt.outputFile = strdup(optarg); | |||
fprintf(stderr, "unrecognised option %s\n", argv[optind++]); | } | |||
} | ||||
void optionsParseNote(char* optarg) | ||||
/* So that we can safely be called again */ | { | |||
optind = 1; | opt.note = strdup(optarg); | |||
} | ||||
char * | ||||
name_thumbnail(char *name) | ||||
{ | ||||
size_t length = 0; | ||||
char *new_title; | ||||
char *dot_pos; | ||||
size_t diff = 0; | ||||
length = strlen(name) + 7; | ||||
new_title = malloc(length); | ||||
if (! new_title) { | ||||
fprintf(stderr, "Unable to allocate thumbnail: %s", strerror(errno)); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
dot_pos = strrchr(name, '.'); | ||||
if (dot_pos) | ||||
{ | ||||
diff = (dot_pos - name) / sizeof(char); | ||||
strncpy(new_title, name, diff); | ||||
strcat(new_title, "-thumb"); | ||||
strcat(new_title, dot_pos); | ||||
} | ||||
else { | ||||
snprintf(new_title, length, "%s-thumb", name); | ||||
} | ||||
return new_title; | ||||
} | ||||
void | ||||
options_parse_autoselect(char *optarg) | ||||
{ | ||||
char *tok; | ||||
const char tokdelim[2] = ","; | ||||
int dimensions[4]; | ||||
int i=0; | ||||
if (strchr(optarg, ',')) /* geometry dimensions must be in format x,y,w,h * | ||||
/ | ||||
{ | ||||
dimensions[i++] = options_parse_required_number(strtok(optarg, tokdelim)); | ||||
while ((tok = strtok(NULL, tokdelim)) ) | ||||
dimensions[i++] = options_parse_required_number(tok); | ||||
opt.autoselect=1; | ||||
opt.autoselect_x=dimensions[0]; | ||||
opt.autoselect_y=dimensions[1]; | ||||
opt.autoselect_w=dimensions[2]; | ||||
opt.autoselect_h=dimensions[3]; | ||||
if (i != 4) | ||||
{ | ||||
fprintf(stderr, "option 'autoselect' require 4 arguments\n"); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
} | ||||
else { | ||||
fprintf(stderr, "invalid format for option -- 'autoselect'\n"); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
} | ||||
void | ||||
options_parse_display(char *optarg) | ||||
{ | ||||
#if SCROT_HAVE_STRNDUP | ||||
opt.display = strndup(optarg, 256); | ||||
#else | ||||
size_t length = 0; | ||||
char *new_display; | ||||
length = strlen(optarg) + 1; | ||||
if (length > 256) { | ||||
length = 256; | ||||
} | ||||
new_display = malloc(length); | ||||
if (! new_display) { | ||||
fprintf(stderr, "Unable to allocate display: %s", strerror(errno)); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
strncpy(new_display, optarg, length); | ||||
opt.display=new_display; | ||||
#endif | ||||
} | ||||
void | ||||
options_parse_thumbnail(char *optarg) | ||||
{ | ||||
char *tok; | ||||
if (strchr(optarg, 'x')) /* We want to specify the geometry */ | ||||
{ | ||||
tok = strtok(optarg, "x"); | ||||
opt.thumb_width = options_parse_required_number(tok); | ||||
tok = strtok(NULL, "x"); | ||||
if (tok) | ||||
{ | ||||
opt.thumb_width = options_parse_required_number(optarg); | ||||
opt.thumb_height = options_parse_required_number(tok); | ||||
if (opt.thumb_width < 0) | ||||
opt.thumb_width = 1; | ||||
if (opt.thumb_height < 0) | ||||
opt.thumb_height = 1; | ||||
if (!opt.thumb_width && !opt.thumb_height) | ||||
opt.thumb = 0; | ||||
else | ||||
opt.thumb = 1; | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
opt.thumb = options_parse_required_number(optarg); | ||||
if (opt.thumb < 1) | ||||
opt.thumb = 1; | ||||
else if (opt.thumb > 100) | ||||
opt.thumb = 100; | ||||
} | ||||
} | ||||
void options_parse_note(char *optarg) | ||||
{ | ||||
opt.note = strdup(optarg); | ||||
if (opt.note == NULL) return; | ||||
if (opt.note[0] == '\0') { | ||||
fprintf(stderr, "Required arguments for --note."); | ||||
exit(EXIT_FAILURE); | ||||
} | ||||
scrot_note_new(opt.note); | if (!opt.note) | |||
return; | ||||
if (opt.note[0] == '\0') | ||||
errx(EXIT_FAILURE, "Required arguments for --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 options_cmp_window_class_name(const char* target_class_name) | int optionsCompareWindowClassName(const char* targetClassName) | |||
{ | { | |||
assert(target_class_name != NULL); | assert(targetClassName != NULL); | |||
assert(opt.window_class_name != NULL); | assert(opt.windowClassName != NULL); | |||
return !!(!strncmp(target_class_name, opt.window_class_name, MAX_LEN_WINDOW_ | return !!(!strncmp(targetClassName, opt.windowClassName, MAX_LEN_WINDOW_CLAS | |||
CLASS_NAME - 1)); | S_NAME - 1)); | |||
} | } | |||
void | void showVersion(void) | |||
show_version(void) | { | |||
{ | printf(SCROT_PACKAGE " version " SCROT_VERSION "\n"); | |||
printf(SCROT_PACKAGE " version " SCROT_VERSION "\n"); | exit(0); | |||
exit(0); | } | |||
} | ||||
void showUsage(void) | ||||
void | { | |||
show_mini_usage(void) | fputs(/* Check that everything lines up after any changes. */ | |||
{ | "usage: " SCROT_PACKAGE " [-bcfhikmopsuvz] [-a X,Y,W,H] [-C NAME] [-D D | |||
printf("Usage : " SCROT_PACKAGE " [OPTIONS]... FILE\nUse " SCROT_PACKAGE | ISPLAY]" | |||
" --help for detailed usage information\n"); | "\n" | |||
exit(0); | " [-F FILE] [-d SEC] [-e CMD] [-l STYLE] [-n OPTS] [-q NUM] | |||
} | [-S CMD] \n" | |||
" [-t NUM | GEOM] [FILE]\n", | ||||
void | stdout); | |||
show_usage(void) | exit(0); | |||
{ | ||||
fprintf(stdout, | ||||
"Usage : " SCROT_PACKAGE " [OPTIONS]... [FILE]\n" | ||||
" Where FILE is the target file for the screenshot.\n" | ||||
" If FILE is not specified, a date-stamped file will be dropped in t | ||||
he\n" | ||||
" current directory.\n" " See man " SCROT_PACKAGE " for more detail | ||||
s\n" | ||||
" -h, --help display this help and exit\n" | ||||
" -v, --version output version information and exit\n" | ||||
" -D, --display Set DISPLAY target other than current\n" | ||||
" -a, --autoselect non-interactively choose a rectangle of | ||||
x,y,w,h\n" | ||||
" -b, --border When selecting a window, grab wm border | ||||
too\n" | ||||
" Use with --select to raise the focus of | ||||
the window.\n" | ||||
" -c, --count show a countdown before taking the shot\ | ||||
n" | ||||
" -d, --delay NUM wait NUM seconds before taking a shot\n" | ||||
" -e, --exec APP run APP on the resulting screenshot\n" | ||||
" -q, --quality NUM Image quality (1-100) high value means\n | ||||
" | ||||
" high size, low compression. Default: 75. | ||||
\n" | ||||
" For lossless compression formats, like p | ||||
ng,\n" | ||||
" low quality means high compression.\n" | ||||
" -m, --multidisp For multiple heads, grab shot from each\ | ||||
n" | ||||
" and join them together.\n" | ||||
" -s, --select interactively choose a window or rectang | ||||
le\n" | ||||
" with the mouse (use the arrow keys to re | ||||
size)\n" | ||||
" -u, --focused use the currently focused window\n" | ||||
" -t, --thumb NUM generate thumbnail too. NUM is the perce | ||||
ntage\n" | ||||
" of the original size for the thumbnail t | ||||
o be,\n" | ||||
" or the geometry in percent, e.g. 50x60 o | ||||
r 80x20.\n" | ||||
" -z, --silent Prevent beeping\n" | ||||
" -p, --pointer Capture the mouse pointer.\n" | ||||
" -f, --freeze Freeze the screen when the selection is | ||||
used: --select\n" | ||||
" -o, --overwrite By default " SCROT_PACKAGE " does not ov | ||||
erwrite the files, use this option to allow it.\n" | ||||
" -l, --line Indicates the style of the line when the | ||||
selection is used: --select\n" | ||||
" See SELECTION STYLE\n" | ||||
" -n, --note Draw a text note.\n" | ||||
" See NOTE FORMAT\n" | ||||
" -k, --stack Capture stack/overlapped windows and joi | ||||
n them together.\n" | ||||
" A running Composite Manager is needed.\n | ||||
" | ||||
" -C, --class NAME Window class name. Associative with opti | ||||
ons: -k\n" | ||||
" -S, --script CMD Imlib2 script commands\n" | ||||
"\n" " SPECIAL STRINGS\n" | ||||
" Both the --exec and filename parameters can take format specifiers | ||||
\n" | ||||
" that are expanded by " SCROT_PACKAGE " when encountered.\n" | ||||
" There are two types of format specifier. Characters preceded by a | ||||
'%%'\n" | ||||
" are interpreted by strftime(2). See man strftime for examples.\n" | ||||
" These options may be used to refer to the current date and time.\n | ||||
" | ||||
" The second kind are internal to " SCROT_PACKAGE | ||||
" and are prefixed by '$'\n" | ||||
" The following specifiers are recognised:\n" | ||||
" $a hostname\n" | ||||
" $f image path/filename (ignored when used in the f | ||||
ilename)\n" | ||||
" $m thumbnail path/filename\n" | ||||
" $n image name (ignored when used in the filename)\ | ||||
n" | ||||
" $s image size (bytes) (ignored when used in the fi | ||||
lename)\n" | ||||
" $p image pixel size\n" | ||||
" $w image width\n" | ||||
" $h image height\n" | ||||
" $t image format (ignored when used in the filename | ||||
)\n" | ||||
" $$ prints a literal '$'\n" | ||||
" \\n prints a newline (ignored when used in the fil | ||||
ename)\n" | ||||
" Example:\n" " " SCROT_PACKAGE | ||||
" '%%Y-%%m-%%d_$wx$h_scrot.png' -e 'mv $f ~/images/shots/'\n" | ||||
" Creates a file called something like 2000-10-30_2560x1024_ | ||||
scrot.png\n" | ||||
" and moves it to your images directory.\n" "\n" | ||||
"\n" " SELECTION STYLE\n" | ||||
" When using --select you can indicate the style of the line with -- | ||||
line.\n" | ||||
" The following specifiers are recognised:\n" | ||||
" style=(solid,dash),width=(range 1 to 8),color=\"va | ||||
lue\",\n" | ||||
" opacity=(range 10 to 100),mode=(edge,classic)\n" | ||||
" The default style is:\n" | ||||
" mode=classic,style=solid,width=1,opacity=100\n" | ||||
" Mode 'edge' ignore : style, --freeze\n" | ||||
" Mode 'classic' ignore : opacity\n\n" | ||||
" The 'opacity' specifier is only effective if a Composite Manager i | ||||
s running.\n\n" | ||||
" For the color you can use a name or a hexadecimal value.\n" | ||||
" color=\"red\" or color=\"#ff0000\"\n" | ||||
" Example:\n" " " SCROT_PACKAGE | ||||
" --line style=dash,width=3,color=\"red\" --select\n\n" | ||||
"\n" " NOTE FORMAT\n" | ||||
" The following specifiers are recognised for the option --note\n" | ||||
" -f 'FontName/size'\n" | ||||
" -t 'text'\n" | ||||
" -x position (optional)\n" | ||||
" -y position (optional)\n" | ||||
" -c color(RGBA) (optional)\n" | ||||
" -a angle (optional)\n" | ||||
" Example:\n" " " SCROT_PACKAGE | ||||
" --note \"-f '/usr/share/fonts/TTF/DroidSans-Bold/40' -x 10 -y 20 -c | ||||
255,0,0,255 -t 'Hi'\"\n\n" | ||||
"This program is free software see the file COPYING for licensing inf | ||||
o.\n" | ||||
"Copyright Tom Gilbert 2000\n" | ||||
"Email bugs to <scrot_sucks@linuxbrit.co.uk>\n"); | ||||
exit(0); | ||||
} | } | |||
End of changes. 76 change blocks. | ||||
527 lines changed or deleted | 481 lines changed or added |