aboutsummaryrefslogtreecommitdiffstats
path: root/rq.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2021-02-21 12:01:39 +0100
committerMattias Andrée <maandree@kth.se>2021-02-21 12:01:39 +0100
commit250376f60aeebbc215359e51ce003f0e104e7986 (patch)
treebce660d205538b8cba461d851421d35badd28514 /rq.c
parentRemove dist (diff)
downloadread-quickly-250376f60aeebbc215359e51ce003f0e104e7986.tar.gz
read-quickly-250376f60aeebbc215359e51ce003f0e104e7986.tar.bz2
read-quickly-250376f60aeebbc215359e51ce003f0e104e7986.tar.xz
Rename to read-quickly2.0
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to 'rq.c')
-rw-r--r--rq.c482
1 files changed, 0 insertions, 482 deletions
diff --git a/rq.c b/rq.c
deleted file mode 100644
index fbcec13..0000000
--- a/rq.c
+++ /dev/null
@@ -1,482 +0,0 @@
-/* See LICENSE file for copyright and license details. */
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <termios.h>
-#include <unistd.h>
-#include <wchar.h>
-
-
-
-/**
- * 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)
-{
- caught_sigwinch = 1;
- (void) signo;
-}
-
-
-/**
- * Signal handler for SIGALRM.
- * Invoked when the timer expires.
- */
-static void
-sigalrm(int signo)
-{
- caught_sigalrm = 1;
- (void) signo;
-}
-
-
-/**
- * Get the size of the terminal.
- */
-static void
-get_terminal_size(void)
-{
- struct winsize winsize;
- if (caught_sigwinch) {
- 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;
- long r;
-
- errno = 0;
- s = getenv("RQ_RATE");
- if (!s || !*s || !isdigit(*s))
- return DEFAULT_RATE;
-
- r = strtol(s, &s, 10);
- if (r <= 0)
- return DEFAULT_RATE;
- while (*s == ' ')
- s++;
-
- if (!*s) r *= 1;
- else if (!strcasecmp(s, "wpm")) r *= 1;
- else if (!strcasecmp(s, "w/m")) r *= 1;
- else if (!strcasecmp(s, "/m")) r *= 1;
- else if (!strcasecmp(s, "wpmin")) r *= 1;
- else if (!strcasecmp(s, "w/min")) r *= 1;
- else if (!strcasecmp(s, "/min")) r *= 1;
- else if (!strcasecmp(s, "wps")) r *= 60;
- else if (!strcasecmp(s, "w/s")) r *= 60;
- else if (!strcasecmp(s, "/s")) r *= 60;
- else if (!strcasecmp(s, "wpsec")) r *= 60;
- else if (!strcasecmp(s, "w/sec")) r *= 60;
- else if (!strcasecmp(s, "/sec")) r *= 60;
- else if (!strcasecmp(s, "hz")) r *= 60;
- else
- return DEFAULT_RATE;
-
- return r;
-}
-
-
-/**
- * Count the number of character in a string.
- *
- * @param s The string.
- * @return The number of characters in `s`.
- */
-static size_t
-display_len(const char *s)
-{
- size_t r = 0;
- wchar_t wc;
- int len, w;
- for (; *s; s += len) {
- len = mbtowc(&wc, s, SIZE_MAX);
- if (len <= 0)
- break;
- w = wcwidth(wc);
- if (w < 0)
- break;
- r += (size_t)w;
- }
- 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);
- if (!new)
- goto fail;
- buffer = new;
- }
- n = read(fd, buffer + ptr, size - ptr);
- if (n <= 0) {
- if (!n)
- break;
- if (errno == EINTR)
- continue;
- goto fail;
- }
- ptr += (size_t)n;
- }
- if (!buffer)
- return 0;
- new = realloc(buffer, ptr + 2);
- if (!new)
- goto fail;
- 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));
- if (!new)
- goto fail;
- words = new;
- }
- while (isspace(*s))
- s++;
- end = strpbrk(s, " \f\n\r\t\v");
- if (!end)
- 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++) {
- if (setitimer(ITIMER_REAL, &interval, NULL))
- goto fail;
- rewait:
- n = read(ttyfd, &c, sizeof(c));
- if (n < 0) {
- if (errno != EINTR)
- goto fail;
- 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;
- if (setitimer(ITIMER_REAL, &interval, NULL))
- goto fail;
- 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();
- if (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)
- goto fail;
- if (fflush(stdout))
- goto fail;
- }
-
- if (setitimer(ITIMER_REAL, &interval, NULL))
- goto fail;
- (void) read(ttyfd, &c, sizeof(c));
-
-done:
- return 0;
-
-fail:
- return -1;
-}
-
-
-int
-main(int argc, char *argv[])
-{
- long rate = get_word_rate();
- int fd = -1, ttyfd = -1, tty_configured = 0;
- struct termios stty, saved_stty;
- struct stat _attr;
- struct sigaction sa;
-
- /* Parse arguments. */
- argv0 = argv ? (argc--, *argv++) : "rq";
- if (argc && argv[0][0] == '-') {
- if (argv[0][1] == '-' && !argv[0][2]) {
- argc--;
- argv++;
- } else if (argv[0][1]) {
- goto usage;
- }
- }
- if (argc > 1)
- goto usage;
-
- /* Check that we have a stdout. */
- if (fstat(STDOUT_FILENO, &_attr))
- if (errno == EBADF)
- goto fail;
-
- /* Open file. */
- if (argc && strcmp(*argv, "-")) {
- fd = open(*argv, O_RDONLY);
- if (fd < 0)
- goto fail;
- } else {
- fd = STDIN_FILENO;
- }
-
- /* Load file. */
- if (load_file(fd))
- goto fail;
-
- /* 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);
- if (ttyfd < 0)
- goto fail;
-
- /* Configure terminal. */
- if (fprintf(stdout, "\033[?1049h\033[?25l") < 0)
- goto fail;
- if (fflush(stdout))
- goto fail;
- if (tcgetattr(ttyfd, &stty))
- goto fail;
- saved_stty = stty;
- stty.c_lflag &= (tcflag_t)~(ICANON | ECHO | ISIG);
- if (tcsetattr(ttyfd, TCSAFLUSH, &stty))
- goto fail;
- tty_configured = 1;
-
- /* Display file. */
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = sigalrm;
- sigaction(SIGALRM, &sa, NULL);
- sa.sa_handler = sigwinch;
- sigaction(SIGWINCH, &sa, NULL);
- if (display_file(ttyfd, rate))
- goto fail;
-
- /* 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, "usage: %s [file].\n", argv0);
- return 1;
-}