aboutsummaryrefslogtreecommitdiffstats
path: root/src/rq.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2021-02-21 01:23:38 +0100
committerMattias Andrée <maandree@kth.se>2021-02-21 11:21:17 +0100
commitb266bd971ea01d4361176dcce9273711844df73d (patch)
treea53cab2126d77a4ca217cfd2724edefa5e8a5913 /src/rq.c
parentm todo (diff)
downloadread-quickly-b266bd971ea01d4361176dcce9273711844df73d.tar.gz
read-quickly-b266bd971ea01d4361176dcce9273711844df73d.tar.bz2
read-quickly-b266bd971ea01d4361176dcce9273711844df73d.tar.xz
change license, improve makefile, remove info manual, misc
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'src/rq.c')
-rw-r--r--src/rq.c484
1 files changed, 0 insertions, 484 deletions
diff --git a/src/rq.c b/src/rq.c
deleted file mode 100644
index 349e6d5..0000000
--- a/src/rq.c
+++ /dev/null
@@ -1,484 +0,0 @@
-/**
- * MIT/X Consortium License
- *
- * Copyright © 2015 Mattias Andrée <maandree@member.fsf.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include <strings.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <termios.h>
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#define t(...) do { if (__VA_ARGS__) goto fail; } while (0)
-
-
-
-/**
- * The default word rate.
- */
-#ifndef DEFAULT_RATE
-# define DEFAULT_RATE 120 /* 2 hz */
-#endif
-
-/**
- * Delta-value of rate increment and rate decrement.
- */
-#ifndef RATE_DELTA
-# define RATE_DELTA 10 /* 10/min */
-#endif
-
-
-
-/**
- * The a word.
- */
-struct word {
- /**
- * The word.
- */
- const char *word;
-
- /**
- * Should reverse video be applied?
- */
- int reverse_video;
-};
-
-/**
- * The name of the process.
- */
-static const char *argv0;
-
-/**
- * Have the terminal been resized?
- */
-static volatile sig_atomic_t caught_sigwinch = 1;
-
-/**
- * Has the timer expired?
- */
-static volatile sig_atomic_t caught_sigalrm = 0;
-
-/**
- * The width of the terminal.
- */
-static size_t width = 80;
-
-/**
- * The height of the terminal.
- */
-static size_t height = 30;
-
-/**
- * The number of words.
- */
-static size_t word_count = 0;
-
-/**
- * All loaded words. Refer to `word_count`
- * for the number of contained words.
- */
-static struct word *words;
-
-
-
-/**
- * Signal handler for SIGWINCH.
- * Invoked when the terminal resizes.
- */
-static void sigwinch(int signo)
-{
- signal(signo, sigwinch);
- caught_sigwinch = 1;
-}
-
-
-/**
- * Signal handler for SIGALRM.
- * Invoked when the timer expires.
- */
-static void sigalrm(int signo)
-{
- signal(signo, sigalrm);
- caught_sigalrm = 1;
-}
-
-
-/**
- * Get the size of the terminal.
- */
-static void get_terminal_size(void)
-{
- struct winsize winsize;
-
- if (!caught_sigwinch)
- return;
-
- caught_sigwinch = 0;
-
- while (ioctl(STDOUT_FILENO, (unsigned long)TIOCGWINSZ, &winsize) < 0)
- if (errno != EINTR)
- return;
-
- height = winsize.ws_row;
- width = winsize.ws_col;
-}
-
-
-/**
- * Get the selected word rate by reading
- * the environment variable RQ_RATE.
- *
- * @return The rate in words per minute.
- */
-static long get_word_rate(void)
-{
- char *s;
- char *e;
- long r;
-
- errno = 0;
- s = getenv("RQ_RATE");
- if (!s || !*s || !isdigit(*s))
- return DEFAULT_RATE;
-
- r = strtol(s, &e, 10);
- if (r <= 0)
- return DEFAULT_RATE;
- while (*e == ' ')
- e++;
-
- if (!*e) r *= 1;
- else if (!strcasecmp(e, "wpm")) r *= 1;
- else if (!strcasecmp(e, "w/m")) r *= 1;
- else if (!strcasecmp(e, "/m")) r *= 1;
- else if (!strcasecmp(e, "wpmin")) r *= 1;
- else if (!strcasecmp(e, "w/min")) r *= 1;
- else if (!strcasecmp(e, "/min")) r *= 1;
- else if (!strcasecmp(e, "wps")) r *= 60;
- else if (!strcasecmp(e, "w/s")) r *= 60;
- else if (!strcasecmp(e, "/s")) r *= 60;
- else if (!strcasecmp(e, "wpsec")) r *= 60;
- else if (!strcasecmp(e, "w/sec")) r *= 60;
- else if (!strcasecmp(e, "/sec")) r *= 60;
- else if (!strcasecmp(e, "hz")) r *= 60;
- else
- return DEFAULT_RATE;
-
- return r;
-}
-
-
-/**
- * Count the number of character in a string.
- *
- * Possible improvement:
- * Figure out how many columns the terminal is
- * likely to used to display the each character,
- * and sum it.
- *
- * @param s The string.
- * @return The number of characters in `s`.
- */
-static size_t display_len(const char *s)
-{
- size_t r = 0;
- for (; *s; s++)
- r += (((int)*s & 0xC0) != 0x80);
- return r;
-}
-
-
-/**
- * Load the file and do some preparsing.
- *
- * @param fd The file descriptor to the file, -1 to clean up instead.
- * @return 0 on success, -1 on error.
- */
-static int load_file(int fd)
-{
- static char *buffer = NULL;
- size_t ptr = 0;
- size_t size = 0;
- void *new;
- int saved_errno;
- char *s;
- char *end;
- size_t i;
- ssize_t n;
-
- if (fd == -1)
- return free(buffer), 0;
-
- /* Load file. */
- for (;;) {
- if (ptr == size) {
- size = size ? (size << 1) : (8 << 10);
- new = realloc(buffer, size);
- t (new == NULL);
- buffer = new;
- }
- n = read(fd, buffer + ptr, size - ptr);
- if (n < 0) {
- t (errno != EINTR);
- continue;
- } else if (n == 0) {
- break;
- }
- ptr += (size_t)n;
- }
- if (buffer == NULL)
- return 0;
- if (ptr == size) {
- new = realloc(buffer, size += 2);
- t (new == NULL);
- buffer = new;
- }
- buffer[ptr++] = '\0';
- buffer[ptr++] = '\0';
-
- /* Split words. */
- size = 0;
- for (s = buffer; *s; s = end + 1) {
- if (word_count == size) {
- size = size ? (size << 1) : 512;
- new = realloc(words, size * sizeof(*words));
- t (new == NULL);
- words = new;
- }
- while (isspace(*s))
- s++;
- end = strpbrk(s, " \f\n\r\t\v");
- if (end == NULL)
- end = strchr(s, '\0');
- *end = '\0';
- words[word_count].word = s;
- words[word_count].reverse_video = 0;
- word_count++;
- }
-
- /* Figure out which words should have reverse video. */
- for (i = 1; i < word_count; i++)
- if (!strcmp(words[i].word, words[i - 1].word))
- words[i].reverse_video = words[i - 1].reverse_video ^ 1;
-
- return 0;
-fail:
- saved_errno = errno;
- free(buffer), buffer = NULL;
- errno = saved_errno;
- return -1;
-}
-
-
-/**
- * Display a file word by word.
- *
- * @param ttyfd File descriptor for reading from the terminal.
- * @param rate The number of words per minute to display.
- * @return 0 on success, -1 on error.
- */
-static int display_file(int ttyfd, long rate)
-{
-#define SET_RATE \
- (interval.it_value.tv_usec = 60000000L / rate, \
- interval.it_value.tv_sec = interval.it_value.tv_usec / 1000000L, \
- interval.it_value.tv_usec %= 1000000L)
-
- ssize_t n;
- int timer_set = 1;
- char c;
- size_t i;
- struct itimerval interval;
- memset(&interval, 0, sizeof(interval));
-
- SET_RATE;
- for (i = 0; i < word_count; i++) {
- t (setitimer(ITIMER_REAL, &interval, NULL));
- rewait:
- n = read(ttyfd, &c, sizeof(c));
- if (n < 0) {
- t (errno != EINTR);
- c = 0;
- } else if (n == 0) {
- break;
- }
- switch (c) {
- case '+': /* plus */
- case '-': /* hyphen */
- rate += (c == '+' ? RATE_DELTA : -RATE_DELTA);
- rate = (rate <= 0 ? 1 : rate);
- SET_RATE;
- goto rewait;
- case 'p': /* P */
- if (timer_set)
- memset(&interval, 0, sizeof(interval));
- else
- SET_RATE;
- t (setitimer(ITIMER_REAL, &interval, NULL));
- timer_set ^= 1;
- goto rewait;
- case 'q': /* Q */
- goto done;
- case 'B': /* down */
- case 'C': /* right */
- break;
- case 'A': /* up */
- case 'D': /* left */
- i = (i < 2 ? 0 : (i - 2));
- break;
- case 0:
- if (!caught_sigalrm)
- goto rewait;
- caught_sigalrm = 0;
- break;
- default:
- goto rewait;
- }
-
- get_terminal_size();
- t (fprintf(stdout, "\033[H\033[2J\033[%zu;%zuH%s%s%s",
- (height + 1) / 2,
- (width - display_len(words[i].word)) / 2 + 1,
- words[i].reverse_video ? "\033[7m" : "",
- words[i].word,
- words[i].reverse_video ? "\033[27m" : "") < 0);
- t (fflush(stdout));
- }
-
- t (setitimer(ITIMER_REAL, &interval, NULL));
- (void) read(ttyfd, &c, sizeof(c));
-
-done:
- return 0;
-
-fail:
- return -1;
-}
-
-
-int main(int argc, char *argv[])
-{
- int dashed = 0;
- long rate = get_word_rate();
- char *file = NULL;
- char *arg;
- int fd = -1, ttyfd = -1, tty_configured = 0;
- struct termios stty;
- struct termios saved_stty;
- struct stat _attr;
-
- /* Check that we have a stdout. */
- if (fstat(STDOUT_FILENO, &_attr))
- t (errno == EBADF);
-
- /* Parse arguments. */
- argv0 = argv ? (argc--, *argv++) : "rq";
- while (argc) {
- if (!dashed && !strcmp(*argv, "--")) {
- dashed = 1;
- argv++;
- argc--;
- } else if (!dashed && **argv == '-') {
- arg = *argv++;
- argc--;
- for (arg++; *arg; arg++) {
- goto usage;
- }
- } else {
- if (file)
- goto usage;
- file = *argv++;
- argc--;
- }
- }
-
- /* Open file. */
- if (!file || !strcmp(file, "-")) {
- fd = STDIN_FILENO;
- } else {
- fd = open(file, O_RDONLY);
- t (fd == -1);
- }
-
- /* Load file. */
- t (load_file(fd));
-
- /* We do not need the file anymore. */
- close(fd), fd = -1;
-
- /* Get a readable file descriptor for the controlling terminal. */
- ttyfd = open("/dev/tty", O_RDONLY);
- t (ttyfd == -1);
-
- /* Configure terminal. */
- t (fprintf(stdout, "\033[?1049h\033[?25l") < 0);
- t (fflush(stdout));
- t (tcgetattr(ttyfd, &stty));
- saved_stty = stty;
- stty.c_lflag &= (tcflag_t)~(ICANON | ECHO | ISIG);
- t (tcsetattr(ttyfd, TCSAFLUSH, &stty));
- tty_configured = 1;
-
- /* Display file. */
- signal(SIGALRM, sigalrm);
- signal(SIGWINCH, sigwinch);
- t (display_file(ttyfd, rate));
-
- /* Restore terminal configurations. */
- tcsetattr(ttyfd, TCSAFLUSH, &saved_stty);
- fprintf(stdout, "\033[?25h\033[?1049l");
- fflush(stdout);
- tty_configured = 0;
-
- free(words);
- load_file(-1);
- close(ttyfd);
- return 0;
-
-fail:
- perror(argv0);
- free(words);
- load_file(-1);
- if (tty_configured) {
- tcsetattr(ttyfd, TCSAFLUSH, &saved_stty);
- fprintf(stdout, "\033[?25h\033[?1049l");
- fflush(stdout);
- }
- if (fd >= 0)
- close(fd);
- if (ttyfd >= 0)
- close(ttyfd);
- return 1;
-
-usage:
- fprintf(stderr, "%s: Invalid arguments, see `man 1 rq'.\n", argv0);
- return 2;
-}
-