diff --git a/.github/workflows/full-check.yml b/.github/workflows/full-check.yml index 9e4c294..683c2f5 100644 --- a/.github/workflows/full-check.yml +++ b/.github/workflows/full-check.yml @@ -49,36 +49,3 @@ jobs: clang-tidy --version find src -name '*.c' -print | xargs -P$(nproc) -I{} \ clang-tidy --quiet {} -- $(pkg-config --cflags ./deps.pc) - cygwin: - runs-on: windows-latest - env: - CYGWIN_NOWINPATH: 1 # Removes non-Cygwin dirs from PATH. - CHERE_INVOKING: '' # Makes Cygwin's `bash.exe --login` not cd. - defaults: - run: - shell: C:\cygwin\bin\bash.exe --login -o igncr {0} - steps: - - run: git config --global core.autocrlf input - # This is NOT the Cygwin bash, it's the Git for Windows bash from the - # default Github Actions Windows VM. This step tells git to translate Unix - # newlines to DOS newlines. - shell: bash - - uses: actions/checkout@v4 - - uses: cygwin/cygwin-install-action@v1 - with: - packages: autoconf autoconf-archive automake gcc-core libImlib2-devel \ - libXcomposite-devel libXext-devel libXfixes-devel libXinerama-devel \ - make - - name: distcheck - # NOTE: cygwin's imlib2 version is too old, and it's imlib_apply_filter - # lacks `const` qualifier. so use `-Wno-error=discarded-qualifiers`. - run: | - cd ${GITHUB_WORKSPACE} - ./autogen.sh - ./configure SCROT_PRIVATE_FLAGS="-Werror -Wno-error=cpp -Wno-error=pedantic -Wno-error=discarded-qualifiers" - make distcheck - - name: run_program - run: | - cd ${GITHUB_WORKSPACE} - make - src/scrot -v diff --git a/README.md b/README.md index 23474a1..2bd3dfc 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,9 @@ scrot requires a few projects and libraries: - libXfixes [(can be found in X.Org)](https://gitlab.freedesktop.org/xorg/lib/libxfixes) - libXinerama [(can be found in X.Org)](https://gitlab.freedesktop.org/xorg/lib/libxinerama) +The [deps.pc](./deps.pc) file documents minimum version requirement for some of +the libraries. + ### Generic installation instructions ### If you are building from a git checkout, or if you have applied additional diff --git a/TODO.md b/TODO.md index efff92a..1e34907 100644 --- a/TODO.md +++ b/TODO.md @@ -59,12 +59,12 @@ Work for it happens on the `v2` branch. It will be released when Imlib2 v1.11 is available on Debian stable to ease the transition for users and avoid depending on bleeding edge library versions. -- [ ] Require Imlib2 v1.11.0 -- [ ] Document minimum version of other dependencies [#307](https://github.com/resurrecting-open-source-projects/scrot/issues/307) -- [ ] Use file descriptors when saving images - - [ ] https://github.com/resurrecting-open-source-projects/scrot/issues/244 - - [ ] https://github.com/resurrecting-open-source-projects/scrot/issues/246 +- [x] Require Imlib2 v1.11.0 +- [x] Document minimum version of other dependencies [#307](https://github.com/resurrecting-open-source-projects/scrot/issues/307) +- [x] Use file descriptors when saving images + - [x] https://github.com/resurrecting-open-source-projects/scrot/issues/244 + - [x] https://github.com/resurrecting-open-source-projects/scrot/issues/246 - [ ] https://github.com/resurrecting-open-source-projects/scrot/issues/226 -- [ ] Remove deprecated features - - [ ] --note [#236](https://github.com/resurrecting-open-source-projects/scrot/issues/236) - - [ ] --script +- [x] Remove deprecated features + - [x] --note [#236](https://github.com/resurrecting-open-source-projects/scrot/issues/236) + - [x] --script diff --git a/deps.pc b/deps.pc index bc11760..d4189b0 100644 --- a/deps.pc +++ b/deps.pc @@ -2,4 +2,4 @@ Name: scrot's mandatory dependencies Description: ditto Version: infinite Cflags: -D_XOPEN_SOURCE=700L -Requires: x11 imlib2 xcomposite xext xfixes xinerama +Requires: x11 imlib2 >= 1.11.0 xcomposite >= 0.2.0 xext xfixes >= 5.0.1 xinerama >= 1.1.3 diff --git a/man/scrot.txt b/man/scrot.txt index f78e9ae..aaf01ee 100644 --- a/man/scrot.txt +++ b/man/scrot.txt @@ -175,7 +175,9 @@ SELECTION STYLE Without the -l option, a default style is used: - mode=auto,style=solid,width=1,opacity=100 + mode=auto,style=solid,width=...,opacity=100 + + The default width automatically scales with DPI. Example: diff --git a/src/Makefile.am b/src/Makefile.am index a757311..6cac52b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,7 +35,6 @@ MAINTAINERCLEANFILES = Makefile.in bin_PROGRAMS = scrot scrot_SOURCES = scrot.c scrot.h \ -note.c note.h \ options.c options.h \ scrot_selection.c scrot_selection.h \ selection_classic.c selection_classic.h \ diff --git a/src/note.c b/src/note.c deleted file mode 100644 index e30aae0..0000000 --- a/src/note.c +++ /dev/null @@ -1,281 +0,0 @@ -/* note.c - -Copyright 2019-2022 Daniel T. Borelli -Copyright 2021-2023 Guilherme Janczak -Copyright 2021 IFo Hancroft -Copyright 2021 Peter Wu -Copyright 2023 NRK - -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 of the Software and its documentation and acknowledgment shall be -given in the documentation and software packages that this Software was -used. - -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 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 -#include -#include -#include -#include - -#include - -#include "note.h" -#include "options.h" -#include "util.h" - -enum { // default color - DEF_COLOR_RED = 0, - DEF_COLOR_GREEN = 0, - DEF_COLOR_BLUE = 0, - DEF_COLOR_ALPHA = 255 -}; - -/* - * Format: -f 'NAME/SIZE' -x NUM -y NUM -t 'TEXT' -c NUM,NUM,NUM,NUM - * - * -f fontname/size - absolute path - * -x screen position x - * -y screen position y - * -t text note - * -c color(red,green,blue,alpha) range 0..255 - * - * */ - -struct ScrotNote { - char *font; /* font name */ - char *text; /* text of the note */ - Imlib_Font imFont; /* private */ - int x; /* position screen (optional) */ - int y; /* position screen (optional) */ - double angle; /* angle text (optional) */ - struct Color { /* (optional) */ - int r, /* red */ - g, /* green */ - b, /* blue */ - a; /* alpha */ - } color; -}; - -static struct ScrotNote *note; -static void loadFont(void); -static char *parseText(char **, char const *const); - -static void pfree(char **ptr) -{ - free(*ptr); - *ptr = NULL; -} - -static void nextSpace(char **token) -{ - while (*++*token == ' ' && **token != '\0') { } -} - -static void nextNotSpace(char **token) -{ - while (*++*token != ' ' && **token != '\0') { } -} - -void scrotNoteNew(char const *const format) -{ - char const *const end = format + strlen(format); - - char *token = strpbrk(format, "-"); - - if (!token || (strlen(token) == 1)) - errx(EXIT_FAILURE, "Error --note option : Malformed syntax."); - - scrotNoteFree(); - - note = ecalloc(1, sizeof(*note)); - note->color.r = DEF_COLOR_RED; - note->color.g = DEF_COLOR_GREEN; - note->color.b = DEF_COLOR_BLUE; - note->color.a = DEF_COLOR_ALPHA; - - while (token) { - const char type = *++token; - char *savePtr = NULL; - char *c; - const char *errmsg; - - nextSpace(&token); - - switch (type) { - case 'f': - note->font = parseText(&token, end); - - if (!note->font) - errx(EXIT_FAILURE, "Error --note option : Malformed syntax for -f"); - - char *number = strrchr(note->font, '/'); - - if (!number) - errx(EXIT_FAILURE, "Error --note option : Malformed syntax for -f, required number."); - - const int fontSize = optionsParseNum(++number, 1, INT_MAX, &errmsg); - if (errmsg) { - errx(EXIT_FAILURE, "option --note: font size '%s' is %s", - number, errmsg); - } - - if (fontSize < 6) - warnx("Warning: --note option: font size < 6"); - break; - case 'x': - if ((1 != sscanf(token, "%d", ¬e->x) || (note->x < 0))) - errx(EXIT_FAILURE, "Error --note option : Malformed syntax for -x"); - nextNotSpace(&token); - break; - case 'y': - if ((1 != sscanf(token, "%d", ¬e->y)) || (note->y < 0)) - errx(EXIT_FAILURE, "Error --note option : Malformed syntax for -y"); - nextNotSpace(&token); - break; - case 'a': - if ((1 != sscanf(token, "%lf", ¬e->angle))) - errx(EXIT_FAILURE, "Error --note option : Malformed syntax for -a"); - nextNotSpace(&token); - break; - case 't': - note->text = parseText(&token, end); - if (!note->text) - errx(EXIT_FAILURE, "Error --note option : Malformed syntax for -t"); - break; - case 'c': - c = strtok_r(token, ",", &savePtr); - - int numberColors = 0; - - while (c) { - token = c; - char *const space = strchr(c, ' '); - - if (space) - *space = '\0'; - const int color = optionsParseNum(c, 0, 255, &errmsg); - if (errmsg) { - errx(EXIT_FAILURE, "option --note: color '%s' is %s", - c, errmsg); - } - if (space) - *space = ' '; - - switch (++numberColors) { - case 1: - note->color.r = color; - break; - case 2: - note->color.g = color; - break; - case 3: - note->color.b = color; - break; - case 4: - note->color.a = color; - break; - } - - c = strtok_r(NULL, ",", &savePtr); - } - - if (numberColors > 4) - warnx("Warning --note option : Malformed syntax for -c"); - break; - default: - errx(EXIT_FAILURE, "Error --note option : unknown option: '-%c'", type); - } - - token = strpbrk(token, "-"); - } - - if (!note->font || !note->text) - errx(EXIT_FAILURE, "Error --note option : Malformed syntax."); - - loadFont(); -} - -void scrotNoteFree(void) -{ - if (!note) - return; - - if (note->text) - pfree(¬e->text); - - if (note->font) - pfree(¬e->font); - - if (note->imFont) { - imlib_context_set_font(note->imFont); - imlib_free_font(); - } - - free(note); - note = NULL; -} - -void scrotNoteDraw(Imlib_Image im) -{ - if (!im) - return; - - scrotAssert(note->imFont); - - imlib_context_set_image(im); - imlib_context_set_font(note->imFont); - - imlib_context_set_direction(IMLIB_TEXT_TO_ANGLE); - imlib_context_set_angle(note->angle); - - imlib_context_set_color(note->color.r, - note->color.g, - note->color.b, - note->color.a); - - imlib_text_draw(note->x, note->y, note->text); -} - -static void loadFont(void) -{ - note->imFont = imlib_load_font(note->font); - - if (!note->imFont) - errx(EXIT_FAILURE, "Error --note option : Failed to load fontname: %s", note->font); -} - -static char *parseText(char **token, char const *const end) -{ - if (**token != '\'') - return NULL; - - (*token)++; - - char *begin = *token; - - while ((*token != end) && **token != '\'') - (*token)++; - - ptrdiff_t length = (*token - begin); - - if (length == 0) - return NULL; - - return strndup(begin, length); -} diff --git a/src/note.h b/src/note.h deleted file mode 100644 index 68c4d81..0000000 --- a/src/note.h +++ /dev/null @@ -1,38 +0,0 @@ -/* note.h - -Copyright 2019-2022 Daniel T. Borelli -Copyright 2021 Christopher R. Nelson -Copyright 2021-2023 Guilherme Janczak -Copyright 2021 Peter Wu - -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 of the Software and its documentation and acknowledgment shall be -given in the documentation and software packages that this Software was -used. - -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 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. - -*/ - -#ifndef H_NOTE -#define H_NOTE - -#include - -void scrotNoteNew(char const *const); -void scrotNoteFree(void); -void scrotNoteDraw(Imlib_Image); - -#endif /* !defined(H_NOTE) */ diff --git a/src/options.c b/src/options.c index 24ccf85..1ed5bf8 100644 --- a/src/options.c +++ b/src/options.c @@ -51,7 +51,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include "note.h" #include "options.h" #include "scrot.h" #include "scrot_selection.h" @@ -69,7 +68,6 @@ struct ScrotOptions opt = { .quality = 75, .compression = 7, .lineStyle = LineSolid, - .lineWidth = 1, .lineOpacity = SELECTION_OPACITY_DEFAULT, .stackDirection = HORIZONTAL, .outputFile = defaultOutputFile, @@ -81,7 +79,7 @@ enum { /* long opt only */ OPT_FORMAT = UCHAR_MAX + 1, OPT_LIST_OPTS, }; -static const char stropts[] = "a:bC:cD:d:e:F:fhik::l:M:mn:opq:S:s::t:uvw:Z:z"; +static const char stropts[] = "a:bC:cD:d:e:F:fhik::l:M:mopq:s::t:uvw:Z:z"; // NOTE: make sure lopts and opt_description indexes are kept in sync static const struct option lopts[] = { {"autoselect", required_argument, NULL, 'a'}, @@ -99,11 +97,9 @@ static const struct option lopts[] = { {"line", required_argument, NULL, 'l'}, {"monitor", required_argument, NULL, 'M'}, {"multidisp", no_argument, NULL, 'm'}, - {"note", required_argument, NULL, 'n'}, {"overwrite", no_argument, NULL, 'o'}, {"pointer", no_argument, NULL, 'p'}, {"quality", required_argument, NULL, 'q'}, - {"script", required_argument, NULL, 'S'}, {"select", optional_argument, NULL, 's'}, {"thumb", required_argument, NULL, 't'}, {"focused", no_argument, NULL, 'u'}, @@ -136,11 +132,9 @@ static const struct option_desc { /* l */ { "specify the style of the selection line", "STYLE" }, /* M */ { "capture monitor", "NUM" }, /* m */ { "capture all monitors", "" }, - /* n */ { OPT_DEPRECATED, OPT_DEPRECATED }, /* o */ { "overwrite the output file if needed", "" }, /* p */ { "capture the mouse pointer as well", "" }, /* q */ { "image quality", "NUM" }, - /* S */ { OPT_DEPRECATED, OPT_DEPRECATED }, /* s */ { "interactively select a region to capture", "OPTS" }, /* t */ { "also generate a thumbnail", "% | WxH" }, /* u */ { "capture the currently focused window", "" }, @@ -366,24 +360,11 @@ static void optionsParseLine(char *optarg) } /* while */ } -static const char *getPathOfStdout(void) -{ - const char *paths[] = { "/dev/stdout", "/dev/fd/1", "/proc/self/fd/1" }; - - for (size_t i = 0; i < ARRAY_COUNT(paths); ++i) { - if (access(paths[i], W_OK) == 0) - return paths[i]; - } - err(EXIT_FAILURE, "access to stdout failed"); - return 0; /* silence tcc warning */ -} - void optionsParse(int argc, char *argv[]) { int optch; const char *errmsg; bool FFlagSet = false; - const char *note = NULL; /* Now to pass some optionarinos */ while ((optch = getopt_long(argc, argv, stropts, lopts, NULL)) != -1) { @@ -449,11 +430,6 @@ void optionsParse(int argc, char *argv[]) case 'm': opt.mode = MODE_MULTIDISP; break; - case 'n': - if (optarg[0] == '\0') - errx(EXIT_FAILURE, "Required arguments for --note."); - note = optarg; - break; case 'o': opt.overwrite = true; break; @@ -467,9 +443,6 @@ void optionsParse(int argc, char *argv[]) errmsg); } break; - case 'S': - opt.script = optarg; - break; case 's': opt.mode = MODE_SELECT; optionsParseSelection(optarg); @@ -540,7 +513,6 @@ void optionsParse(int argc, char *argv[]) if (strcmp(opt.outputFile, "-") == 0) { opt.overwrite = true; opt.thumb = THUMB_DISABLED; - opt.outputFile = getPathOfStdout(); } size_t outputFileLen = strlen(opt.outputFile); @@ -551,13 +523,6 @@ void optionsParse(int argc, char *argv[]) if (opt.thumb != THUMB_DISABLED) opt.thumbFile = optionsNameThumbnail(opt.outputFile); - - if (note) { - warnx("--note is deprecated. See: " - "https://github.com/resurrecting-open-source-projects/scrot/discussions/207"); - opt.note = estrdup(note); /* TODO: investigate if dup is needed */ - scrotNoteNew(opt.note); - } } static void showUsage(void) diff --git a/src/options.h b/src/options.h index e69673a..bef21e6 100644 --- a/src/options.h +++ b/src/options.h @@ -82,10 +82,8 @@ struct ScrotOptions { char *thumbFile; const char *exec; const char *display; - char *note; Window windowId; const char *windowClassName; - const char *script; int autoselectX; int autoselectY; int autoselectH; diff --git a/src/scrot.c b/src/scrot.c index c7d6789..80062aa 100644 --- a/src/scrot.c +++ b/src/scrot.c @@ -48,8 +48,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include +#include #include +#include #include #include #include @@ -62,23 +63,21 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include "note.h" #include "options.h" #include "scrot.h" #include "util.h" static void initXAndImlib(const char *, int); static void uninitXAndImlib(void); -static void scrotSaveImage(const char *); +static void scrotSaveImage(int, const char *); static Imlib_Image scrotGrabFocused(void); -static void applyFilterIfRequired(void); static Imlib_Image scrotGrabAutoselect(void); static long miliToNanoSec(int); static Imlib_Image scrotGrabShotMulti(void); static Imlib_Image scrotGrabShotMonitor(void); static Imlib_Image scrotGrabStackWindows(void); static Imlib_Image scrotGrabShot(void); -static void scrotCheckIfOverwriteFile(char **); +static int scrotCheckIfOverwriteFile(char **); static void scrotExecApp(Imlib_Image, struct tm *, char *, char *); static char *imPrintf(const char *, struct tm *, const char *, const char *, Imlib_Image); @@ -104,6 +103,7 @@ int main(int argc, char *argv[]) char *filenameThumb = NULL; struct timespec timeStamp; struct tm *tm; + int fd; /* Get the time ASAP to reduce the timing error in case --delay is used. */ opt.delayStart = clockNow(); @@ -151,20 +151,14 @@ int main(int argc, char *argv[]) } tm = localtime(&timeStamp.tv_sec); - if (opt.note) - scrotNoteDraw(image); - imlib_context_set_image(image); imlib_image_set_format(opt.format); imlib_image_attach_data_value("quality", NULL, opt.quality, NULL); imlib_image_attach_data_value("compression", NULL, opt.compression, NULL); filenameIM = imPrintf(opt.outputFile, tm, NULL, NULL, image); - scrotCheckIfOverwriteFile(&filenameIM); - - applyFilterIfRequired(); - - scrotSaveImage(filenameIM); + fd = scrotCheckIfOverwriteFile(&filenameIM); + scrotSaveImage(fd, filenameIM); if (opt.thumb != THUMB_DISABLED) { int cwidth, cheight; @@ -202,8 +196,8 @@ int main(int argc, char *argv[]) imlib_image_set_format(opt.format); filenameThumb = imPrintf(opt.thumbFile, tm, NULL, NULL, thumbnail); - scrotCheckIfOverwriteFile(&filenameThumb); - scrotSaveImage(filenameThumb); + fd = scrotCheckIfOverwriteFile(&filenameThumb); + scrotSaveImage(fd, filenameThumb); imlib_free_image_and_decache(); } } @@ -255,34 +249,25 @@ static void initXAndImlib(const char *dispStr, int screenNumber) /* atexit register func. */ static void uninitXAndImlib(void) { - if (opt.note) { - scrotNoteFree(); - free(opt.note); - } - if (disp) { XCloseDisplay(disp); disp = NULL; } } -static void scrotSaveImage(const char *filename) +// save image to fd, filename only used for logging +// fd will be closed after calling this function +static void scrotSaveImage(int fd, const char *filename) { - Imlib_Load_Error imErr; - imlib_save_image_with_error_return(filename, &imErr); + imlib_save_image_fd(fd, filename); + int imErr = imlib_get_error(); if (imErr) { - const char *colon = "", *errmsg = ""; // NOLINT(*DeadStores) + const char *errmsg = imlib_strerror(imErr); const char imlibPrefix[] = "Imlib2: "; -#if defined(IMLIB2_VERSION) -#if IMLIB2_VERSION >= IMLIB2_VERSION_(1, 10, 0) - colon = ": "; - errmsg = imlib_strerror(imlib_get_error()); -#endif -#endif if (strncmp(errmsg, imlibPrefix, sizeof(imlibPrefix) - 1) == 0) errmsg += sizeof(imlibPrefix) - 1; - errx(EXIT_FAILURE, "failed to save image: %s%s%s", - filename, colon, errmsg); + errx(EXIT_FAILURE, "failed to save image: %s: %s", + filename, errmsg); } } @@ -555,53 +540,55 @@ static void scrotGrabMousePointer(Imlib_Image image, const int xOffset, XFree(xcim); } -// It assumes that the local variable 'scrot.c:Imlib_Image image' is in context -static void applyFilterIfRequired(void) +static int scrotCheckIfOverwriteFile(char **filename) { - if (opt.script) { - warnx("--script is deprecated. See: " - "https://github.com/resurrecting-open-source-projects/scrot/pull/231"); - imlib_apply_filter(opt.script); + if (strcmp(*filename, "-") == 0) { + // scrotSaveImage will close the fd, so dup it + int fd = fcntl(1, F_DUPFD_CLOEXEC, 3); + if (fd < 0) + err(EXIT_FAILURE, "dup failed"); + return fd; } -} -static void scrotCheckIfOverwriteFile(char **filename) -{ - if (opt.overwrite) - return; - - if (access(*filename, F_OK) == -1) - return; - - const size_t maxCounter = 999; - char fmt[5]; // _000 + NUL byte - const size_t slen = strlen(*filename); - const size_t nalloc = slen + sizeof(fmt); - - char *ext; - size_t extLength = scrotHaveFileExtension(*filename, &ext); - - char *newName = ecalloc(nalloc, sizeof(*newName)); - memcpy(newName, *filename, slen - extLength); - char *ptr = newName + (slen - extLength); - - size_t counter = 0; - do { - snprintf(fmt, sizeof(fmt), "_%03zu", counter++); - memcpy(ptr, fmt, sizeof(fmt)); - memcpy(ptr + sizeof(fmt) - 1, ext, extLength); - } while ((counter < maxCounter) && !access(newName, F_OK)); - - scrotAssert(newName[nalloc - 1] == '\0'); + int flags = O_RDWR | O_CREAT | (opt.overwrite ? O_TRUNC : O_EXCL); + int fd = open(*filename, flags, 0644); + if (!opt.overwrite && fd < 0 && errno == EEXIST) { + const size_t maxCounter = 999; + char fmt[5]; // _000 + NUL byte + const size_t slen = strlen(*filename); + const size_t nalloc = slen + sizeof(fmt); + + char *ext; + size_t extLength = scrotHaveFileExtension(*filename, &ext); + + char *newName = ecalloc(nalloc, sizeof(*newName)); + memcpy(newName, *filename, slen - extLength); + char *ptr = newName + (slen - extLength); + + size_t counter = 0; + do { + snprintf(fmt, sizeof(fmt), "_%03zu", counter++); + memcpy(ptr, fmt, sizeof(fmt)); + memcpy(ptr + sizeof(fmt) - 1, ext, extLength); + fd = open(newName, flags, 0644); + } while ((counter < maxCounter) && fd < 0 && errno == EEXIST); + scrotAssert(newName[nalloc - 1] == '\0'); + + if (counter == maxCounter) { + errx(EXIT_FAILURE, "scrot can no longer generate new file names.\n" + "The last attempt is %s", newName); + } - if (counter == maxCounter) { - errx(EXIT_FAILURE, "scrot can no longer generate new file names.\n" - "The last attempt is %s", newName); + int saved_errno = errno; // avoid errno getting potentially clobbered + warnx("`%s` already exists, attempting `%s` instead", *filename, newName); + free(*filename); + *filename = newName; + errno = saved_errno; } - warnx("`%s` already exists, attempting `%s` instead", *filename, newName); - free(*filename); - *filename = newName; + if (fd < 0) + err(EXIT_FAILURE, "couldn't open file %s", *filename); + return fd; } static int scrotMatchWindowClassName(Window target) diff --git a/src/scrot_selection.c b/src/scrot_selection.c index 1bb55db..fd386a3 100644 --- a/src/scrot_selection.c +++ b/src/scrot_selection.c @@ -423,6 +423,11 @@ Imlib_Image scrotSelectionSelectMode(void) opt.lineMode = LINE_MODE_CLASSIC; } + if (opt.lineWidth == 0) { + int width = (scr->height + scr->width) / (scr->mheight + scr->mwidth); + opt.lineWidth = MIN(MAX(width/4, 1), 8); + } + if (opt.delaySelection) scrotDoDelay(); diff --git a/src/util.h b/src/util.h index 6a350aa..56a1bc5 100644 --- a/src/util.h +++ b/src/util.h @@ -43,6 +43,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ARRAY_COUNT(X) (sizeof(X) / sizeof(0[X])) #define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) typedef struct { char *buf;