diff options
-rw-r--r-- | Makefile.in | 12 | ||||
-rw-r--r-- | TODO | 7 | ||||
-rw-r--r-- | mk/lang-c.mk | 9 | ||||
-rw-r--r-- | po/sv.po | 74 | ||||
-rw-r--r-- | src/common.h | 76 | ||||
-rw-r--r-- | src/info.c | 115 | ||||
-rw-r--r-- | src/info.h | 48 | ||||
-rw-r--r-- | src/kern-linux.c | 151 | ||||
-rw-r--r-- | src/kern.h | 68 | ||||
-rw-r--r-- | src/pattern.c | 151 | ||||
-rw-r--r-- | src/pattern.h | 40 | ||||
-rw-r--r-- | src/pnm.h | 49 | ||||
-rw-r--r-- | src/scrotty.c | 451 |
13 files changed, 802 insertions, 449 deletions
diff --git a/Makefile.in b/Makefile.in index 71385d0..fd8a4ff 100644 --- a/Makefile.in +++ b/Makefile.in @@ -56,12 +56,12 @@ _VERSION = 1.1 _C_STD = c99 _PEDANTIC = yes _BIN = scrotty -_OBJ_scrotty = scrotty -_HEADER_DIRLEVELS = 0 +_OBJ_scrotty = scrotty kern-linux info pattern +_HEADER_DIRLEVELS = 1 _CPPFLAGS = -D'PACKAGE="$(PKGNAME)"' -D'PROGRAM_VERSION="$(_VERSION)"' # Used by mk/i18n.mk -_SRC = $(foreach B,$(_BIN),$(_OBJ_$(B)).c) +_SRC = $(foreach B,$(_BIN),$(foreach F,$(_OBJ_$(B)),$(F).c)) _PROJECT_FULL = scrotty _COPYRIGHT_HOLDER = Mattias Andrée (maandree@member.fsf.org) @@ -81,11 +81,13 @@ _MAN_sv_1 = scrotty _COPYING = COPYING # Used by mk/dist.mk -_EVERYTHING_INFO = scrotty titlepage-data content hardcopy-copying \ +___EVERYTHING_INFO = scrotty titlepage-data content hardcopy-copying \ appx/fdl appx/free-software-needs-free-documentation appx/gpl \ chap/invoking chap/overview chap/strftime \ reusable/macros reusable/paper reusable/titlepage -_EVERYTHING = $(foreach F,$(_EVERYTHING_INFO),doc/info/$(F).texinfo) \ +___EVERYTHING_H = common kern info pattern pnm +_EVERYTHING = $(foreach F,$(___EVERYTHING_INFO),doc/info/$(F).texinfo) \ + $(foreach F,$(___EVERYTHING_H),src/$(F).h) \ $(__EVERYTHING_ALL_COMMON) DEPENDENCIES INSTALL NEWS $(__todo) doc/concept # }} @@ -2,10 +2,9 @@ Use auto-auto-complete. (maybe) include date and time by default in filenames. -Take the screenshot the the framebuffers -more simultaneously. +Take the screenshot of the framebuffers more simultaneously. (async) -/dev/fb/[0-9]* +Support framebuffers with other depths. -framebuffers with other depths. +Investigate Hurd support. diff --git a/mk/lang-c.mk b/mk/lang-c.mk index a8f9896..5d952b6 100644 --- a/mk/lang-c.mk +++ b/mk/lang-c.mk @@ -153,7 +153,7 @@ aux/%.o: $(v)src/%.c $(foreach H,$(__H),$(v)$(H)) @$(ECHO_EMPTY) # Link object files into a command. -# Dependencies are declared below.. +# Dependencies are declared below. bin/%: @$(PRINTF_INFO) '\e[00;01;31mLD\e[34m %s\e[00;32m$A\n' "$@" @$(MKDIR) -p bin @@ -161,8 +161,11 @@ bin/%: @$(ECHO_EMPTY) # Dependencies for the rule above. -$(foreach B,$(_BIN),$(foreach O,$(_OBJ_$(B)),bin/$(B): aux/$(O).o\ -)) +include aux/lang-c.mk +aux/lang-c.mk: Makefile + @$(MKDIR) -p aux + @$(ECHO) > aux/lang-c.mk + @$(foreach B,$(_BIN),$(ECHO) bin/$(B): $(foreach O,$(_OBJ_$(B)),aux/$(O).o) >> aux/lang-c.mk) # INSTALL RULES: @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: scrotty 1.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-12-10 02:23+0100\n" +"POT-Creation-Date: 2015-12-10 16:17+0100\n" "PO-Revision-Date: 2015-12-07 18:50+0100\n" "Last-Translator: Mattias Andrée <maandree@member.fsf.org>\n" "Language-Team: none\n" @@ -17,12 +17,40 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/scrotty.c:606 +#: src/scrotty.c:377 #, c-format -msgid "Saved framebuffer %i to %s,\n" +msgid "Saved framebuffer %i to %s.\n" msgstr "Bildrutebuffert %i sparad till %s.\n" -#: src/scrotty.c:686 +#: src/scrotty.c:467 src/scrotty.c:474 +#, c-format +msgid "%s: %s. Type '%s --help' for help.\n" +msgstr "%s: %s. Kör '%s --help' för hjälp.\n" + +#: src/scrotty.c:467 +msgid "Unrecognised option." +msgstr "Val känns inte igen." + +#: src/scrotty.c:474 +msgid "--exec has no argument." +msgstr "--exec saknar argument." + +#: src/scrotty.c:524 +#, c-format +msgid "%s: It looks like you are inside a display server. If this is correct, what you see is probably not what you get.\n" +msgstr "%s: Det ser ut som att du är innan för en grafikserver. Om detta är korrect, vad de ser är troligtvis inte vad du får.\n" + +#: src/scrotty.c:534 +#, c-format +msgid "%s: %s: %s\n" +msgstr "%s: %s: %s\n" + +#: src/kern-linux.c:41 +#, c-format +msgid "%s: Unable to find a framebuffer. If you have a file named %s/MAKEDEV, run it with '-d /dev/fb' as root, or try running '%s'.\n" +msgstr "%s: Kunde inte hitta någon bildrutebuffert. Om filen %s/MAKEDEV finns ditt system, kör den med '-d /dev/fb' som root, eller testa att köra '%s'.\n" + +#: src/info.c:32 #, c-format msgid "" "SYNOPSIS\n" @@ -93,8 +121,8 @@ msgstr "" "\tslutet av strängen, och om ett sådant även förekom direkt framför.\n" "\n" -#: src/scrotty.c:731 -#, fuzzy, c-format +#: src/info.c:77 +#, c-format msgid "" "%s\n" "Copyright (C) %s.\n" @@ -112,7 +140,7 @@ msgstr "" "\n" "Skriven av Mattias Andrée.\n" -#: src/scrotty.c:751 +#: src/info.c:97 #, c-format msgid "" "scrotty -- Screenshot program for Linux's TTY\n" @@ -146,35 +174,3 @@ msgstr "" "\n" "Du skall ha fått en kopia av GNU General Public License tillsammans\n" "med det här programmet. Om inte, se <http://www.gnu.org/licenses/>.\n" - -#: src/scrotty.c:813 src/scrotty.c:820 -#, c-format -msgid "%s: %s. Type '%s --help' for help.\n" -msgstr "%s: %s. Kör '%s --help' för hjälp.\n" - -#: src/scrotty.c:813 -msgid "Unrecognised option." -msgstr "Val känns inte igen." - -#: src/scrotty.c:820 -msgid "--exec has no argument." -msgstr "--exec saknar argument." - -#: src/scrotty.c:860 -#, fuzzy, c-format -msgid "%s: Unable to find a framebuffer.\n" -msgstr "%s: Kunde inte hitta någon bildrutebuffert.\n" - -#: src/scrotty.c:867 -#, c-format -msgid "%s: It looks like you are inside a display server. If this is correct, what you see is probably not what you get.\n" -msgstr "%s: Det ser ut som att du är innan för en grafikserver. Om detta är korrect, vad de ser är troligtvis inte vad du får.\n" - -#: src/scrotty.c:879 -#, c-format -msgid "%s: %s: %s\n" -msgstr "%s: %s: %s\n" - -#, fuzzy -#~ msgid "%s: %i %i.\n" -#~ msgstr "%s: %s: %s\n" diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..93753b6 --- /dev/null +++ b/src/common.h @@ -0,0 +1,76 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#define _POSIX_SOURCE +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <alloca.h> +#include <time.h> + + +#ifdef USE_GETTEXT +# include <libintl.h> +# define _(STR) gettext (STR) +#else +# define _(STR) STR +#endif + + +#ifndef DEVDIR +# define DEVDIR "/dev" +#endif +#ifndef SYSDIR +# define SYSDIR "/sys" +#endif +#ifndef PATH_MAX /* TODO can we avoid this and still have clean code */ +# define PATH_MAX 4096 +#endif + + +/** + * Stores a filename to `failure_file` and goes to the label `fail`. + * + * @param PATH:char * The file that could not be used. + * Must be accessible by `main`. + */ +#define FILE_FAILURE(PATH) \ + do \ + { \ + failure_file = PATH; \ + goto fail; \ + } \ + while (0) + + +/** + * `argv[0]` from `main`. + */ +extern const char *execname; + +/** + * If a function fails when it tries to + * open a file, it will set this variable + * point to the pathname of that file. + */ +extern const char *failure_file; + diff --git a/src/info.c b/src/info.c new file mode 100644 index 0000000..9eff7cb --- /dev/null +++ b/src/info.c @@ -0,0 +1,115 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "common.h" +#include "info.h" + + + +/** + * Print usage information. + * + * @return Zero on success, -1 on error. + */ +int +print_help (void) +{ + return printf (_("SYNOPSIS\n" + "\t%s [options...] [filename-pattern] [-- options-for-convert...]\n" + "\n" + "OPTIONS\n" + "\t--help Print usage information.\n" + "\t--version Print program name and version.\n" + "\t--copyright Print copyright information.\n" + "\t--raw Save in PNM rather than in PNG.\n" + "\t--exec CMD Command to run for each saved image.\n" + "\n" + "SPECIAL STRINGS\n" + "\tBoth the --exec and filename-pattern parameters can take format specifiers\n" + "\tthat are expanded by scrotty when encountered. There are two types of format\n" + "\tspecifier. Characters preceded by a '%%' are interpreted by strftime(3).\n" + "\tSee `man strftime` for examples. These options may be used to refer to the\n" + "\tcurrent date and time. The second kind are internal to scrotty and are prefixed\n" + "\tby '$' or '\\'. The following specifiers are recognised:\n" + "\n" + "\t$i framebuffer index\n" + "\t$f image filename/pathname (ignored when used in filename-pattern)\n" + "\t$n image filename (ignored when used in filename-pattern)\n" + "\t$p image width multiplied by image height\n" + "\t$w image width\n" + "\t$h image height\n" + "\t$$ expands to a literal '$'\n" + "\t\\n expands to a new line\n" + "\t\\\\ expands to a literal '\\'\n" + "\t\\ expands to a literal ' ' (the string is a backslash followed by a space)\n" + "\n" + "\tA space that is not prefixed by a backslash in --exec is interpreted as an\n" + "\targument delimiter. This is the case even at the beginning and end of the\n" + "\tstring and if a space was the previous character in the string.\n" + "\n"), + execname) < 0 ? -1 : 0; +} + + +/** + * Print program name and version. + * + * @return Zero on success, -1 on error. + */ +int +print_version (void) +{ + return printf (_("%s\n" + "Copyright (C) %s.\n" + "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" + "This is free software: you are free to change and redistribute it.\n" + "There is NO WARRANTY, to the extent permitted by law.\n" + "\n" + "Written by Mattias Andrée.\n"), + PROGRAM_NAME " " PROGRAM_VERSION, + "2014, 2015 Mattias Andrée") < 0 ? -1 : 0; +} + + +/** + * Print copyright information. + * + * @return Zero on success, -1 on error. + */ +int +print_copyright (void) +{ + return printf (_("scrotty -- Screenshot program for Linux's TTY\n" + "Copyright (C) %s\n" + "\n" + "This program is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, either version 3 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"), + "2014, 2015 Mattias Andrée (maandree@member.fsf.org)" + ) < 0 ? -1 : 0; +} + diff --git a/src/info.h b/src/info.h new file mode 100644 index 0000000..1d2c652 --- /dev/null +++ b/src/info.h @@ -0,0 +1,48 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef PROGRAM_NAME +# define PROGRAM_NAME "scrotty" +#endif +#ifndef PROGRAM_VERSION +# define PROGRAM_VERSION "1.1" +#endif + + + +/** + * Print usage information. + * + * @return Zero on success, -1 on error. + */ +int print_help (void); + +/** + * Print program name and version. + * + * @return Zero on success, -1 on error. + */ +int print_version (void); + +/** + * Print copyright information. + * + * @return Zero on success, -1 on error. + */ +int print_copyright (void); + diff --git a/src/kern-linux.c b/src/kern-linux.c new file mode 100644 index 0000000..af17822 --- /dev/null +++ b/src/kern-linux.c @@ -0,0 +1,151 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "common.h" +#include "kern.h" +#include "pnm.h" + + + +/** + * Stop when `try_alt_fbpath` (in scrotty.c) reaches this value. + */ +const int alt_fbpath_limit = 2; + + + +/** + * This is called if no framebuffer is found. + * + * It prints a message describing this condition + * and suggests how to resolve it. + */ +void +print_not_found_help (void) +{ + fprintf (stderr, _("%s: Unable to find a framebuffer. " + "If you have a file named %s/MAKEDEV, " + "run it with '-d /dev/fb' as root, " + "or try running '%s'.\n"), + execname, DEVDIR, "mknod /dev/fb0 c 29 0 && chgrp video /dev/fb0"); +} + + +/** + * Construct the path to a framebuffer device. + * + * @param pathbuf Ouput buffer for the path. + * @param altpath The index of the alternative path-pattern to use. + * @param fbno The index of the framebuffer. + */ +void +get_fbpath (char *pathbuf, int altpath, int fbno) +{ + sprintf (pathbuf, "%s/fb%s%i", DEVDIR, (altpath ? "/" : ""), fbno); +} + + +/** + * Get the dimensions of a framebuffer. + * + * @param fbno The number of the framebuffer. + * @param fbpath The path to the framebuffer device.. + * @param width Output parameter for the width of the image. + * @param height Output parameter for the height of the image. + * @return Zero on success, -1 on error. + */ +int +measure (int fbno, char *fbpath, long *width, long *height) +{ + static char buf[PATH_MAX]; + char *delim; + int sizefd = -1; + ssize_t got; + int saved_errno; + + /* Open the file with the framebuffer's dimensions. */ + sprintf (buf, "%s/class/graphics/fb%i/virtual_size", SYSDIR, fbno); + sizefd = open (buf, O_RDONLY); + if (sizefd == -1) + FILE_FAILURE (buf); + + /* Get the dimensions of the framebuffer. */ + got = read (sizefd, buf, sizeof (buf) / sizeof (char) - 1); + if (got < 0) + goto fail; + close (sizefd); + + /* The read content is formated as `%{width},%{height}\n`, + convert it to `%{width}\0%{height}\0` and parse it. */ + buf[got] = '\0'; + delim = strchr (buf, ','); + *delim++ = '\0'; + *width = atol (buf); + *height = atol (delim); + + return 0; + + fail: + saved_errno = errno; + if (sizefd >= 0) + close (sizefd); + errno = saved_errno; + return -1; + + (void) fbpath; +} + + +/** + * Convert read data from a framebuffer to PNM pixel data. + * + * @param file The output image file. + * @param buf Buffer with read data. + * @param n The number of read characters. + * @param adjustment Set to zero if all bytes were converted + * (a whole number of pixels where available,) + * otherwise, set to the number of bytes a + * pixel is encoded. + * @return Zero on success, -1 on error. + */ +int +convert_fb (FILE *file, char *buf, size_t n, size_t *adjustment) +{ + uint32_t *pixel; + int r, g, b; + size_t off; + + for (off = 0; off < n; off += 4) + { + /* A pixel in the framebuffer is formatted as `%{blue}%{green}%{red}%{x}` + in big-endian binary, or `%{x}%{red}%{green}%{blue}` in little-endian binary. */ + pixel = (uint32_t *)(buf + off); + r = (*pixel >> 16) & 255; + g = (*pixel >> 8) & 255; + b = (*pixel >> 0) & 255; + + if (SAVE_PIXEL(file, r, g, b) < 0) + goto fail; + } + + *adjustment = (off != n ? 4 : 0); + return 0; + fail: + return -1; +} + diff --git a/src/kern.h b/src/kern.h new file mode 100644 index 0000000..69eaf0b --- /dev/null +++ b/src/kern.h @@ -0,0 +1,68 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +/** + * Stop when `try_alt_fbpath` (in scrotty.c) reaches this value. + */ +extern const int alt_fbpath_limit; + + +/** + * This is called if no framebuffer is found. + * + * It prints a message describing this condition + * and suggests how to resolve it. + */ +void print_not_found_help (void); + +/** + * Construct the path to a framebuffer device. + * + * @param pathbuf Ouput buffer for the path. + * @param altpath The index of the alternative path-pattern to use. + * @param fbno The index of the framebuffer. + */ +void get_fbpath (char *pathbuf, int altpath, int fbno); + +/** + * Get the dimensions of a framebuffer. + * + * @param fbno The number of the framebuffer. + * @param fbpath The path to the framebuffer device.. + * @param width Output parameter for the width of the image. + * @param height Output parameter for the height of the image. + * @return Zero on success, -1 on error. + */ +int measure (int fbno, char *fbpath, long *width, long *height); + +/** + * Convert read data from a framebuffer to PNM pixel data. + * + * @param file The output image file. + * @param buf Buffer with read data. + * @param n The number of read characters. + * @param adjustment Set to zero if all bytes were converted + * (a whole number of pixels where available,) + * otherwise, set to the number of bytes a + * pixel is encoded. + * @return Zero on success, -1 on error. + */ +int convert_fb (FILE *file, char *buf, size_t n, size_t *adjustment); + diff --git a/src/pattern.c b/src/pattern.c new file mode 100644 index 0000000..6912731 --- /dev/null +++ b/src/pattern.c @@ -0,0 +1,151 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "common.h" +#include "pattern.h" + + + +/** + * Duplicate all '%':s in a buffer until the first occurrence of a zero byte. + * + * @param buf The buffer. + * @param n The size of the buffer. + * @return -1 if the buffer is too small, otherwise. + * the new position of the first zero byte. + */ +static ssize_t +duplicate_percents (char *restrict buf, size_t n) +{ + size_t p = 0, pi, pc = 0, i; + + /* Count number of '%':s. */ + for (i = 0; buf[i]; i++) + if (buf[i] == '%') + pc++; + + /* Check whether the string will overflow. */ + if (i + pc > n) + return -1; + + /* Duplicate all '%':s. */ + for (pi = 0; pi < pc; pi++) + { + p = (size_t)(strchr (buf + p, '%') - buf); + memmove (buf + p + 1, buf + p, (i - (p - pi)) * sizeof (char)); + p += 2; + } + + return (ssize_t)(i + pi); +} + + +/** + * Parse and evaluate a --exec argument or filename pattern. + * + * If `path != NULL` than all non-escaped spaces in + * `pattern` will be stored as 255-bytes in `buf`. + * + * @param buf The output buffer. + * @param n The size of `buf`. + * @param pattern The pattern to evaluate. + * @param fbno The index of the framebuffer. + * @param width The width of the image/framebuffer. + * @param height The height of the image/framebuffer. + * @param path The filename of the saved image, `NULL` + * during the evaluation of the filename pattern. + * @return Zero on success, -1 on error. + */ +int +evaluate (char *restrict buf, size_t n, const char *restrict pattern, + int fbno, long width, long height, const char *restrict path) +{ +#define P(format, value) r = snprintf (buf + i, n - i, format "%zn", value, &j) + + size_t i = 0; + ssize_t j = 0; + int percent = 0, backslash = 0, dollar = 0, r; + char c; + char *fmt; + time_t t; + struct tm *tm; + + /* Expand '$' and '\'. */ + while ((i < n) && ((c = *pattern++))) + if (dollar) + { + dollar = 0; + if (path == NULL) + if ((c == 'f') || (c == 'n')) + continue; + if (c == 'i') P ("%i", fbno); + else if (c == 'f') P ("%s", path); + else if (c == 'n') P ("%s", strrchr (path, '/') ? (strrchr (path, '/') + 1) : path); + else if (c == 'p') P ("%ju", (uintmax_t)width * (uintmax_t)height); + else if (c == 'w') P ("%li", width); + else if (c == 'h') P ("%li", height); + else if (c == '$') r = 0, j = 1, buf[i] = '$'; + else if ((r < 0) || (j <= 0)) + return -1; + else if ((c == 'f') || (c == 'n')) + if (j = duplicate_percents (buf + i, n - i), j < 0) + goto enametoolong; + i += (size_t)j; + } + else if (backslash) buf[i++] = (c == 'n' ? '\n' : c), backslash = 0; + else if (percent) buf[i++] = c, percent = 0; + else if (c == '%') buf[i++] = c, percent = 1; + else if (c == '\\') backslash = 1; + else if (c == '$') dollar = 1; + else if (c == ' ') buf[i++] = path == NULL ? ' ' : (char)255; /* 255 is not valid in UTF-8. */ + else buf[i++] = c; + if (i >= n) + goto enametoolong; + buf[i] = '\0'; + + /* Check whether there are any '%' to expand. */ + if (strchr (buf, '%') == NULL) + return 0; + + /* Copy the buffer so we can reuse the buffer and use its old content for the format. */ + fmt = alloca ((strlen (buf) + 1) * sizeof (char)); + memcpy (fmt, buf, (strlen (buf) + 1) * sizeof (char)); + + /* Expand '%'. */ + t = time (NULL); + tm = localtime (&t); + if (tm == NULL) + goto fail; + +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + if (strftime (buf, n, fmt, tm) == 0) + goto enametoolong; /* No errors are defined for `strftime`. What else can we do? */ + + return 0; + + enametoolong: + return errno = ENAMETOOLONG, -1; + + fail: + return -1; + +#undef P +} + diff --git a/src/pattern.h b/src/pattern.h new file mode 100644 index 0000000..98bc7a3 --- /dev/null +++ b/src/pattern.h @@ -0,0 +1,40 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <stddef.h> + + +/** + * Parse and evaluate a --exec argument or filename pattern. + * + * If `path != NULL` than all non-escaped spaces in + * `pattern` will be stored as 255-bytes in `buf`. + * + * @param buf The output buffer. + * @param n The size of `buf`. + * @param pattern The pattern to evaluate. + * @param fbno The index of the framebuffer. + * @param width The width of the image/framebuffer. + * @param height The height of the image/framebuffer. + * @param path The filename of the saved image, `NULL` + * during the evaluation of the filename pattern. + * @return Zero on success, -1 on error. + */ +int evaluate (char *restrict buf, size_t n, const char *restrict pattern, + int fbno, long width, long height, const char *restrict path); + diff --git a/src/pnm.h b/src/pnm.h new file mode 100644 index 0000000..3c1b2ee --- /dev/null +++ b/src/pnm.h @@ -0,0 +1,49 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <stdio.h> + + +/** + * Store a pixel to an PNM image. + * + * A pixel in the PNM image is formatted as `%{red} %{green} %{blue} ` in text. + * + * @parma F:FILE * The file whither the pixel shall be save. + * @param R:int The [0, 255]-value on the red subpixel. + * @param G:int The [0, 255]-value on the green subpixel. + * @param B:int The [0, 255]-value on the blue subpixel. + * @return Positive on success (not zero!), -1 on error. + */ +#define SAVE_PIXEL(F, R, G, B) \ + fprintf (F, "%s%s%s", inttable[R], inttable[G], inttable[B]) + + +/** + * [0, 255]-integer-to-text convertion lookup table for faster conversion from + * raw framebuffer data to the PNM format. The values have a whitespace at the + * end for even faster conversion. + * Lines should not be longer than 70 (although most programs will probably + * work even if there are longer lines), therefore the selected whitespace + * is LF (new line). + * + * ASCII is wider supported than binary, and is create for version control, + * especifially with one datum per line. + */ +extern const char* inttable[]; + diff --git a/src/scrotty.c b/src/scrotty.c index 54e5b7d..c6cb3fc 100644 --- a/src/scrotty.c +++ b/src/scrotty.c @@ -16,72 +16,30 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef PROGRAM_NAME -# define PROGRAM_NAME "scrotty" -#endif -#ifndef PROGRAM_VERSION -# define PROGRAM_VERSION "1.1" -#endif - +#include "common.h" +#include "kern.h" +#include "info.h" +#include "pnm.h" +#include "pattern.h" -#define _POSIX_SOURCE -#include <stdio.h> -#include <unistd.h> -#include <errno.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdio.h> -#include <alloca.h> -#include <time.h> #ifdef USE_GETTEXT -# include <libintl.h> # include <locale.h> #endif -#ifndef PATH_MAX -# define PATH_MAX 4096 -#endif -#ifndef DEVDIR -# define DEVDIR "/dev" -#endif -#ifndef SYSDIR -# define SYSDIR "/sys" -#endif - - - -#ifdef USE_GETTEXT -# define _(STR) gettext (STR) -#else -# define _(STR) STR -#endif - - /** - * Stores a filename to `failure_file` and goes to the label `fail`. - * - * @param PATH:char * The file that could not be used. - * Must be accessible by `main`. + * X-macro that lists all environment variables + * that indicate that the program is running + * inside a display server. */ -#define FILE_FAILURE(PATH) \ - do \ - { \ - failure_file = PATH; \ - goto fail; \ - } \ - while (0) - +#define LIST_DISPLAY_VARS X(DISPLAY) X(MDS_DISPLAY) X(MIR_DISPLAY) X(WAYLAND_DISPLAY) X(PREFERRED_DISPLAY) #define LIST_0_9(P) P"0\n", P"1\n", P"2\n", P"3\n", P"4\n", P"5\n", P"6\n", P"7\n", P"8\n", P"9\n" - /** * [0, 255]-integer-to-text convertion lookup table for faster conversion from * raw framebuffer data to the PNM format. The values have a whitespace at the @@ -89,8 +47,11 @@ * Lines should not be longer than 70 (although most programs will probably * work even if there are longer lines), therefore the selected whitespace * is LF (new line). + * + * ASCII is wider supported than binary, and is create for version control, + * especifially with one datum per line. */ -static const char* inttable[] = +const char* inttable[] = { LIST_0_9(""), LIST_0_9("1"), LIST_0_9("2"), LIST_0_9("3"), LIST_0_9("4"), LIST_0_9("5"), LIST_0_9("6"), LIST_0_9("7"), LIST_0_9("8"), LIST_0_9("9"), @@ -106,7 +67,14 @@ static const char* inttable[] = /** * `argv[0]` from `main`. */ -static const char *execname; +const char *execname; + +/** + * If a function fails when it tries to + * open a file, it will set this variable + * point to the pathname of that file. + */ +const char *failure_file = NULL; /** * Arguments for `convert`, @@ -116,11 +84,11 @@ static const char *execname; static char **convert_args = NULL; /** - * If a function fails when it tries to - * open a file, it will set this variable - * point to the pathname of that file. + * The index of the alternative path-pattern, + * for the framebuffers, to try. */ -static const char *failure_file = NULL; +static int try_alt_fbpath = 0; + /** @@ -130,7 +98,7 @@ static const char *failure_file = NULL; * @param fbname The framebuffer device. * @param width The width of the image. * @param height The height of the image. - * @param fd The file descriptor connected to `convert`'s stdin + * @param fd The file descriptor connected to `convert`'s stdin. * @return Zero on success, -1 on error. */ static int @@ -139,10 +107,9 @@ save_pnm (const char *fbpath, long width, long height, int fd) char buf[PATH_MAX]; FILE *file = NULL; int fbfd = 1; - int r, g, b; ssize_t got, off; - uint32_t *pixel; int saved_errno; + size_t adjustment; /* Open the framebuffer device for reading. */ fbfd = open (fbpath, O_RDONLY); @@ -171,24 +138,14 @@ save_pnm (const char *fbpath, long width, long height, int fd) got += off; /* Convert read pixels. */ - for (off = 0; off < got; off += 4) - { - /* A pixel in the framebuffer is formatted as `%{blue}%{green}%{red}%{x}` - in big-endian binary, or `%{x}%{red}%{green}%{blue}` in little-endian binary. */ - pixel = (uint32_t *)(buf + off); - r = (*pixel >> 16) & 255; - g = (*pixel >> 8) & 255; - b = (*pixel >> 0) & 255; - /* A pixel in the PNM image is formatted as `%{red} %{green} %{blue} ` in text. */ - if (fprintf (file, "%s%s%s", inttable[r], inttable[g], inttable[b]) < 0) - goto fail; - } + if (convert_fb (file, buf, (size_t)got, &adjustment) < 0) + goto fail; /* If we read a whole number of pixels, reset the buffer, otherwise, move the unconverted bytes to the beginning of the buffer. */ - if (off != got) + if (adjustment) { - off -= 4; + off -= (ssize_t)adjustment; memcpy (buf, buf + off, (size_t)(got - off) * sizeof (char)); off = got - off; } @@ -243,6 +200,7 @@ save (const char *fbpath, const char *imgpath, long width, long height) if (pid == -1) goto fail; + /* Child process: */ if (pid == 0) { @@ -265,6 +223,7 @@ save (const char *fbpath, const char *imgpath, long width, long height) _exit(1); } + /* Parent process: */ /* Close the read-end of the pipe. */ @@ -314,189 +273,6 @@ save (const char *fbpath, const char *imgpath, long width, long height) /** - * Get the dimensions of the framebuffer. - * - * @param fbno The number of the framebuffer. - * @param width Output parameter for the width of the image. - * @param height Output parameter for the height of the image. - * @return Zero on success, -1 on error. - */ -static int -measure (int fbno, long *width, long *height) -{ - static char buf[PATH_MAX]; - char *delim; - int sizefd = -1; - ssize_t got; - int saved_errno; - - /* Open the file with the framebuffer's dimensions. */ - sprintf (buf, "%s/class/graphics/fb%i/virtual_size", SYSDIR, fbno); - sizefd = open (buf, O_RDONLY); - if (sizefd == -1) - FILE_FAILURE (buf); - - /* Get the dimensions of the framebuffer. */ - got = read (sizefd, buf, sizeof (buf) / sizeof (char) - 1); - if (got < 0) - goto fail; - close (sizefd); - - /* The read content is formated as `%{width},%{height}\n`, - convert it to `%{width}\0%{height}\0` and parse it. */ - buf[got] = '\0'; - delim = strchr (buf, ','); - *delim++ = '\0'; - *width = atol (buf); - *height = atol (delim); - - return 0; - - fail: - saved_errno = errno; - if (sizefd >= 0) - close (sizefd); - errno = saved_errno; - return -1; -} - - -/** - * Duplicate all '%':s in a buffer until the first occurrence of a zero byte. - * - * @param buf The buffer. - * @param n The size of the buffer. - * @return -1 if the buffer is too small, otherwise. - * the new position of the first zero byte. - */ -static ssize_t -duplicate_percents (char *restrict buf, size_t n) -{ - size_t p = 0, pi, pc = 0, i; - - /* Count number of '%':s. */ - for (i = 0; buf[i]; i++) - if (buf[i] == '%') - pc++; - - /* Check whether the string will overflow. */ - if (i + pc > n) - return -1; - - /* Duplicate all '%':s. */ - for (pi = 0; pi < pc; pi++) - { - p = (size_t)(strchr (buf + p, '%') - buf); - memmove (buf + p + 1, buf + p, (i - (p - pi)) * sizeof (char)); - p += 2; - } - - return (ssize_t)(i + pi); -} - - -/** - * Parse and evaluate a --exec argument or filename pattern. - * - * If `path != NULL` than all non-escaped spaces in - * `pattern` will be stored as 255-bytes in `buf`. - * - * @param buf The output buffer. - * @param n The size of `buf`. - * @param pattern The pattern to evaluate. - * @param fbno The index of the framebuffer. - * @param width The width of the image/framebuffer. - * @param height The height of the image/framebuffer. - * @param path The filename of the saved image, `NULL` during the evaluation of the filename pattern. - * @return Zero on success, -1 on error. - */ -static int evaluate (char *restrict buf, size_t n, const char *restrict pattern, - int fbno, long width, long height, const char *restrict path) -{ -#define P(format, value) r = snprintf (buf + i, n - i, format "%zn", value, &j) - - size_t i = 0; - ssize_t j = 0; - int percent = 0, backslash = 0, dollar = 0, r; - char c; - char *fmt; - time_t t; - struct tm *tm; - - /* Expand '$' and '\'. */ - while ((c = *pattern++)) - { - if (dollar) - { - dollar = 0; - if (path == NULL) - if ((c == 'f') || (c == 'n')) - continue; - if (c == 'i') P ("%i", fbno); - else if (c == 'f') P ("%s", path); - else if (c == 'n') P ("%s", strrchr (path, '/') ? (strrchr (path, '/') + 1) : path); - else if (c == 'p') P ("%ju", (uintmax_t)width * (uintmax_t)height); - else if (c == 'w') P ("%li", width); - else if (c == 'h') P ("%li", height); - else if (c == '$') r = 0, j = 1, buf[i] = '$'; - else - continue; - if ((r < 0) || (j <= 0)) - return -1; - if ((c == 'f') || (c == 'n')) - if (j = duplicate_percents (buf + i, n - i), j < 0) - goto enametoolong; - i += (size_t)j; - } - else if (backslash) buf[i++] = (c == 'n' ? '\n' : c), backslash = 0; - else if (percent) buf[i++] = c, percent = 0; - else if (c == '%') buf[i++] = c, percent = 1; - else if (c == '\\') backslash = 1; - else if (c == '$') dollar = 1; - else if (c == ' ') buf[i++] = path == NULL ? ' ' : (char)255; /* 255 is not valid in UTF-8. */ - else buf[i++] = c; - - if (i >= n) - goto enametoolong; - } - buf[i] = '\0'; - - /* Check whether there are any '%' to expand. */ - if (strchr (buf, '%') == NULL) - return 0; - - /* Copy the buffer so we can reuse the buffer and use its old content for the format. */ - fmt = alloca ((strlen (buf) + 1) * sizeof (char)); - memcpy (fmt, buf, (strlen (buf) + 1) * sizeof (char)); - - /* Expand '%'. */ - t = time (NULL); - tm = localtime (&t); - if (tm == NULL) - goto fail; -#ifdef __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wformat-nonliteral" -#endif - if (strftime (buf, n, fmt, tm) == 0) - goto enametoolong; /* No errors are defined for `strftime`. */ -#ifdef __GNUC__ -# pragma GCC diagnostic pop -#endif - - return 0; - - enametoolong: - return errno = ENAMETOOLONG, -1; - - fail: - return -1; - -#undef P -} - - -/** * Run a command for an image * * @param flatten_args The arguments to run, 255 delimits the arguments @@ -576,25 +352,20 @@ save_fb (int fbno, int raw, const char *filepattern, const char *execpattern) int i, saved_errno; /* Get pathname for framebuffer, and stop if we have read all existing ones. */ - sprintf (fbpath, "%s/fb%i", DEVDIR, fbno); + get_fbpath (fbpath, try_alt_fbpath, fbno); if (access (fbpath, F_OK)) return 1; /* Get the size of the framebuffer. */ - if (measure (fbno, &width, &height) < 0) + if (measure (fbno, fbpath, &width, &height) < 0) goto fail; - + /* Get output pathname. */ if (filepattern == NULL) { sprintf (imgpath, "fb%i.%s", fbno, (raw ? "pnm" : "png")); - if (access (imgpath, F_OK) == 0) - for (i = 2;; i++) - { - sprintf (imgpath, "fb%i.%s.%i", fbno, (raw ? "pnm" : "png"), i); - if (access (imgpath, F_OK)) - break; - } + for (i = 2; access (imgpath, F_OK) == 0; i++) + sprintf (imgpath, "fb%i.%s.%i", fbno, (raw ? "pnm" : "png"), i); } else if (evaluate (imgpath, (size_t)PATH_MAX, filepattern, fbno, width, height, NULL) < 0) @@ -603,7 +374,7 @@ save_fb (int fbno, int raw, const char *filepattern, const char *execpattern) /* Take a screenshot of the current framebuffer. */ if (save (fbpath, imgpath, width, height) < 0) goto fail; - fprintf (stderr, _("Saved framebuffer %i to %s,\n"), fbno, imgpath); + fprintf (stderr, _("Saved framebuffer %i to %s.\n"), fbno, imgpath); /* Should we run a command over the image? */ if (execpattern == NULL) @@ -645,135 +416,18 @@ static int have_display (void) { char *env; - - /* X (should also contain a ':'.) */ - env = getenv("DISPLAY"); - if (env && *env) - return 1; - - /* mds (should also contain a ':'.) */ - env = getenv("MDS_DISPLAY"); - if (env && *env) - return 1; - - /* Mir (not verified, have not been able to find documentation.) */ - env = getenv("MIR_DISPLAY"); - if (env && *env) - return 1; - - /* Wayland. */ - env = getenv("WAYLAND_DISPLAY"); - if (env && *env) - return 1; - - /* Proposed metavariable. */ - env = getenv("PREFERRED_DISPLAY"); - if (env && *env) - return 1; - +#define X(VAR) env = getenv(#VAR); if (env && *env) return 1; + LIST_DISPLAY_VARS return 0; } /** - * Print usage information. - * - * @return Zero on success, -1 on error. - */ -static int -print_help(void) -{ - return printf (_("SYNOPSIS\n" - "\t%s [options...] [filename-pattern] [-- options-for-convert...]\n" - "\n" - "OPTIONS\n" - "\t--help Print usage information.\n" - "\t--version Print program name and version.\n" - "\t--copyright Print copyright information.\n" - "\t--raw Save in PNM rather than in PNG.\n" - "\t--exec CMD Command to run for each saved image.\n" - "\n" - "SPECIAL STRINGS\n" - "\tBoth the --exec and filename-pattern parameters can take format specifiers\n" - "\tthat are expanded by scrotty when encountered. There are two types of format\n" - "\tspecifier. Characters preceded by a '%%' are interpreted by strftime(3).\n" - "\tSee `man strftime` for examples. These options may be used to refer to the\n" - "\tcurrent date and time. The second kind are internal to scrotty and are prefixed\n" - "\tby '$' or '\\'. The following specifiers are recognised:\n" - "\n" - "\t$i framebuffer index\n" - "\t$f image filename/pathname (ignored when used in filename-pattern)\n" - "\t$n image filename (ignored when used in filename-pattern)\n" - "\t$p image width multiplied by image height\n" - "\t$w image width\n" - "\t$h image height\n" - "\t$$ expands to a literal '$'\n" - "\t\\n expands to a new line\n" - "\t\\\\ expands to a literal '\\'\n" - "\t\\ expands to a literal ' ' (the string is a backslash followed by a space)\n" - "\n" - "\tA space that is not prefixed by a backslash in --exec is interpreted as an\n" - "\targument delimiter. This is the case even at the beginning and end of the\n" - "\tstring and if a space was the previous character in the string.\n" - "\n"), - execname) < 0 ? -1 : 0; -} - - -/** - * Print program name and version. - * - * @return Zero on success, -1 on error. - */ -static int -print_version (void) -{ - return printf (_("%s\n" - "Copyright (C) %s.\n" - "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n" - "This is free software: you are free to change and redistribute it.\n" - "There is NO WARRANTY, to the extent permitted by law.\n" - "\n" - "Written by Mattias Andrée.\n"), - PROGRAM_NAME " " PROGRAM_VERSION, - "2014, 2015 Mattias Andrée") < 0 ? -1 : 0; -} - - -/** - * Print copyright information. + * Take a screenshot of all framebuffers. * - * @return Zero on success, -1 on error. - */ -static int -print_copyright (void) -{ - return printf (_("scrotty -- Screenshot program for Linux's TTY\n" - "Copyright (C) %s\n" - "\n" - "This program is free software: you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation, either version 3 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"), - "2014, 2015 Mattias Andrée (maandree@member.fsf.org)" - ) < 0 ? -1 : 0; -} - - -/** - * Take a screenshot of all framebuffers - * - * @param argc The number of elements in `argv` - * @param argv Command line arguments - * @return Zero on and only on success + * @param argc The number of elements in `argv`. + * @param argv Command line arguments, run with `--help` for more information. + * @return Zero on and only on success. */ int main (int argc, char *argv[]) @@ -836,6 +490,7 @@ main (int argc, char *argv[]) convert_args[3 + (argc - dash)] = NULL; } + retry: /* Take a screenshot of each framebuffer. */ for (fbno = 0;; fbno++) { @@ -857,18 +512,18 @@ main (int argc, char *argv[]) /* Did not find any framebuffer? */ if (found == 0) { - fprintf (stderr, _("%s: Unable to find a framebuffer.\n"), execname); + if (try_alt_fbpath++ < alt_fbpath_limit) + goto retry; + + print_not_found_help (); return 1; } /* Warn about being inside a display server. */ if (have_display ()) - { - fprintf (stderr, _("%s: It looks like you are inside a " - "display server. If this is correct, " - "what you see is probably not what " - "you get.\n"), execname); - } + fprintf (stderr, _("%s: It looks like you are inside a display server. " + "If this is correct, what you see is probably not " + "what you get.\n"), execname); return 0; |