public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Sam James" <sam@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/gentoo-functions:master commit in: /
Date: Fri,  9 Jun 2023 11:02:26 +0000 (UTC)	[thread overview]
Message-ID: <1686136464.e08b541e2c875d7718d4452bbd8bb1228bd1a53b.sam@gentoo> (raw)

commit:     e08b541e2c875d7718d4452bbd8bb1228bd1a53b
Author:     Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Tue Jun  6 03:54:50 2023 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Wed Jun  7 11:14:24 2023 +0000
URL:        https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=e08b541e

Introduce a utility to obtain the cursor coordinates via ECMA-48 CPR

The ECMA-48 specification defines the CPR (Cursor Position Report)
sequence which, as its name implies, instructs the terminal to divulge
the present coordinates of the cursor. Though it is theoretically
possible to make use of this sequence by using the shell and standard
utilities, it would be tremendously costly and somewhat unreliable to
do so. This commit introduces a utility - written in C - that does the
job.

Its behaviour is quite simple. If STDIN is found to be a terminal, an
attempt will be made to initiate - and read - a Cursor Position Report.
Upon success, the present row and column of the cursor shall be printed
as two space-separated decimal integers. Otherwise, a diagnostic
message shall be printed to STDERR, and the utility shall exit with
a non-zero status.

This utility will be used by the impending overhaul of the _eprint()
and _eend() functions.

Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
Signed-off-by: Sam James <sam <AT> gentoo.org>

 ecma48-cpr.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 230 insertions(+)

diff --git a/ecma48-cpr.c b/ecma48-cpr.c
new file mode 100644
index 0000000..9aef182
--- /dev/null
+++ b/ecma48-cpr.c
@@ -0,0 +1,230 @@
+/*
+ * ecma48-cpr.c
+ * Treat STDIN as a tty and report the cursor position using the CPR sequence.
+ *
+ * Originally distributed as wsize.c by Stephen J. Friedl <steve@unixwiz.net>.
+ * Repurposed for gentoo-functions by Kerin F. Millar <kfm@plushkava.net>.
+ * This software is in the public domain.
+ */
+
+#define _POSIX_C_SOURCE 200809L
+
+#define PROGRAM "ecma48-cpr"
+#define READ_TIMEOUT_NS 250000000
+#define BUFSIZE 100
+#define MAX_LOOPS 20
+
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+static struct termios save_tty;
+static bool is_timed_out = false;
+static bool is_tty_saved = false;
+
+static void cleanup(void);
+static void die(char const * const errmsg);
+static void on_signal(int const signo);
+
+#ifndef __APPLE__
+static timer_t init_timer(void);
+#endif
+
+int
+main(void) {
+	/*
+	 * Establish that STDIN is a terminal.
+	 */
+	if (! isatty(STDIN_FILENO)) {
+		die("cannot determine the cursor position because stdin is not a tty");
+	}
+
+	/*
+	 * Duplicate STDIN to a new file descriptor before reopening it as a
+	 * writeable stream.
+	 */
+	int fd = dup(STDIN_FILENO);
+	FILE *tty;
+	if (fd < 0) {
+		die("failed to dup stdin");
+	} else {
+		tty = fdopen(fd, "w");
+		if (tty == NULL) {
+			die("failed to re-open the tty for writing");
+		}
+	}
+
+	/*
+	 * Save the current terminal settings.
+	 */
+	if (tcgetattr(STDIN_FILENO, &save_tty) != 0) {
+		die("failed to obtain the current terminal settings");
+	} else {
+		is_tty_saved = true;
+	}
+
+	/*
+	 * Duplicate the current terminal settings for modification.
+	 */
+	struct termios new_tty = save_tty;
+	new_tty = save_tty;
+
+	/*
+	 * Turn off ECHO, so that the response from the terminal isn't printed.
+	 * Also, the terminal must be operating in its noncanonical mode,
+	 * thereby ensuring that its input is always immediately available,
+	 * with no processing having been performed.
+	 */
+	new_tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
+	new_tty.c_lflag &= ~(ICANON);
+
+	/*
+	 * Set an interbyte timeout of 1 decisecond. The timer is started only
+	 * after the first byte is read, so read(2) will block until then.
+	 */
+	new_tty.c_cc[VMIN]  = 1;
+	new_tty.c_cc[VTIME] = 1;
+
+	/*
+	 * Try to apply the new terminal settings.
+	 */
+	if (tcsetattr(STDIN_FILENO, TCSANOW, &new_tty) != 0) {
+		die("failed to modify the terminal settings");
+	} else if (fprintf(tty, "\033[6n") != 4) {
+		die("failed to write the CPR sequence to the terminal");
+	} else if (fclose(tty) != 0) {
+		die("failed to flush the stream after writing the CPR sequence");
+	}
+
+	/*
+	 * Prepare to catch our signals. We treat both an interrupt and a
+	 * depleted timer as essentially the same thing: fatal errors.
+	 */
+	struct sigaction act;
+	act.sa_handler = on_signal;
+	sigemptyset(&act.sa_mask);
+	act.sa_flags = 0;
+	sigaction(SIGALRM, &act, NULL);
+
+	/*
+	 * A timeout is required, just in case read(2) proves unable to read an
+	 * initial byte, otherwise causing the program to hang.
+	 */
+#ifdef __APPLE__
+	alarm(1);
+#else
+	timer_t timerid = init_timer();
+#endif
+
+	/*
+	 * Read up to (sizeof ibuf - 1) bytes of input in total. Upon each
+	 * successful read, scan the input buffer for a valid ECMA4-8 CPR
+	 * response. Abort if no such response is found within MAX_LOOPS
+	 * iterations.
+	 */
+	char ibuf[BUFSIZE];
+	char const * const imax = ibuf + sizeof ibuf - 1;
+	char *iptr = ibuf;
+	int maxloops = MAX_LOOPS;
+	int row = -1;
+	int col = -1;
+	ssize_t nr;
+	while (--maxloops > 0 && (nr = read(STDIN_FILENO, iptr, imax - iptr)) > 0) {
+		iptr += nr;
+		*iptr = '\0'; /* NUL-terminate for strchr(3) and sscanf(3) */
+		char const *p;
+		if ((p = strchr(ibuf, '\033')) != 0) {
+			if (sscanf(p, "\033[%d;%dR", &row, &col) == 2) {
+				break;
+			} else {
+				col = -1;
+				row = -1;
+			}
+		}
+	}
+
+	/*
+	 * Deactivate the timer.
+	 */
+#ifdef __APPLE__
+	alarm(0);
+#else
+	timer_delete(timerid);
+#endif
+
+	/*
+	 * Die in the case that the timer fired.
+	 */
+	if (is_timed_out) {
+		die("timed out waiting for the terminal to respond to CPR");
+	}
+
+	/*
+	 * Restore the original terminal settings.
+	 */
+	cleanup();
+
+	/*
+	 * Print the cursor position, provided both col and row are above zero.
+	 */
+	if (col < 1 || row < 1) {
+		die("failed to read the cursor position");
+	} else if (printf("%d %d\n", row, col) == -1 || fflush(stdout) == EOF) {
+		return EXIT_FAILURE;
+	} else {
+		return EXIT_SUCCESS;
+	}
+}
+
+#ifndef __APPLE__
+static timer_t
+init_timer(void) {
+	struct itimerspec timer;
+	struct sigevent event;
+	timer_t timerid;
+	event.sigev_notify = SIGEV_SIGNAL;
+	event.sigev_signo = SIGALRM;
+	event.sigev_value.sival_ptr = &timerid;
+	if (timer_create(CLOCK_REALTIME, &event, &timerid) == -1) {
+		die("failed to create a per-process timer");
+	} else {
+		timer.it_value.tv_sec = 0;
+		timer.it_value.tv_nsec = READ_TIMEOUT_NS;
+		timer.it_interval.tv_sec = 0;
+		timer.it_interval.tv_nsec = 0;
+		if (timer_settime(timerid, 0, &timer, NULL) == -1) {
+			die("failed to configure the per-process timer");
+		}
+	}
+	return timerid;
+}
+#endif
+
+/*
+ * Tries to restore the terminal settings. Only one attempt will ever be made.
+ */
+static void
+cleanup(void) {
+	bool const is_saved = is_tty_saved;
+	if (is_saved) {
+		tcsetattr(STDIN_FILENO, TCSANOW, &save_tty);
+		is_tty_saved = false;
+	}
+}
+
+static void
+die(char const * const errmsg) {
+	cleanup();
+	fprintf(stderr, "%s: %s\n", PROGRAM, errmsg);
+	exit(EXIT_FAILURE);
+}
+
+static void
+on_signal(int const signo) {
+	is_timed_out = true;
+}


             reply	other threads:[~2023-06-09 11:02 UTC|newest]

Thread overview: 286+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-09 11:02 Sam James [this message]
  -- strict thread matches above, loose matches on Subject: below --
2025-05-13  0:30 [gentoo-commits] proj/gentoo-functions:master commit in: / Sam James
2025-05-13  0:30 Sam James
2025-05-13  0:30 Sam James
2025-05-13  0:30 Sam James
2025-05-13  0:30 Sam James
2024-10-05  7:25 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-10-05  4:15 Sam James
2024-08-11 10:23 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-11 10:11 Sam James
2024-08-05 20:39 Sam James
2024-08-05 20:39 Sam James
2024-08-05  2:03 Sam James
2024-08-05  2:02 Sam James
2024-08-05  2:02 Sam James
2024-08-05  2:02 Sam James
2024-08-05  2:02 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-08-02 23:14 Sam James
2024-07-08  3:00 Sam James
2024-07-08  2:31 Sam James
2024-07-08  2:31 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-07-07  5:55 Sam James
2024-06-25  4:06 Sam James
2024-06-25  4:06 Sam James
2024-06-25  4:06 Sam James
2024-06-25  4:06 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-06-21 13:14 Sam James
2024-05-24  6:05 Sam James
2024-05-24  1:18 Sam James
2024-05-24  1:18 Sam James
2024-05-22  1:12 Sam James
2024-05-22  1:12 Sam James
2024-05-22  1:12 Sam James
2024-05-22  1:12 Sam James
2024-05-22  1:12 Sam James
2024-05-22  1:12 Sam James
2024-05-22  1:12 Sam James
2024-05-22  1:12 Sam James
2024-05-22  1:12 Sam James
2024-05-19 15:27 Sam James
2024-05-19 15:27 Sam James
2024-05-19 15:27 Sam James
2024-05-19 15:27 Sam James
2024-05-18 16:07 Sam James
2024-05-18 16:06 Sam James
2024-05-18 16:06 Sam James
2024-05-18 15:34 Sam James
2024-05-18 15:32 Sam James
2024-05-18 15:32 Sam James
2024-05-18 14:04 Sam James
2024-05-18 14:04 Sam James
2024-05-18 14:04 Sam James
2024-05-18 14:04 Sam James
2024-05-18 14:04 Sam James
2024-05-18 14:04 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-17  4:03 Sam James
2024-05-15 10:28 Sam James
2024-05-15 10:28 Sam James
2024-05-14  0:18 Sam James
2024-05-14  0:15 Sam James
2024-05-14  0:12 Sam James
2024-05-14  0:12 Sam James
2024-05-14  0:08 Sam James
2024-05-14  0:08 Sam James
2024-05-14  0:05 Sam James
2024-05-14  0:05 Sam James
2024-05-14  0:05 Sam James
2024-05-14  0:05 Sam James
2024-05-14  0:05 Sam James
2024-02-16 21:35 Sam James
2023-06-11 16:47 Sam James
2023-06-11 16:47 Sam James
2023-06-11 16:47 Sam James
2023-06-11 16:47 Sam James
2023-06-11 16:47 Sam James
2023-06-11 16:47 Sam James
2023-06-11 16:47 Sam James
2023-06-11 16:47 Sam James
2023-06-10  7:23 Sam James
2023-06-10  7:23 Sam James
2023-06-10  6:04 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-10  4:22 Sam James
2023-06-09 11:17 Sam James
2023-06-09 11:11 Sam James
2023-06-09 11:02 Sam James
2023-06-09 11:02 Sam James
2023-06-09 11:02 Sam James
2023-06-07 11:13 Sam James
2023-06-07 11:13 Sam James
2023-06-07 11:13 Sam James
2023-06-07 11:13 Sam James
2023-06-07 11:13 Sam James
2023-06-07 11:13 Sam James
2023-06-07 11:13 Sam James
2023-06-07 11:13 Sam James
2023-06-07 11:13 Sam James
2023-02-19 16:14 Sam James
2023-02-19 16:14 Sam James
2023-02-19 16:14 Sam James
2023-02-19 16:14 Sam James
2023-02-19 16:14 Sam James
2023-02-19 16:14 Sam James
2023-02-19 16:14 Sam James
2023-02-19 16:14 Sam James
2023-02-19 16:14 Sam James
2023-02-17  7:44 Sam James
2023-02-17  7:44 Sam James
2023-02-17  7:44 Sam James
2023-02-17  1:33 Sam James
2023-02-17  1:33 Sam James
2023-02-17  1:33 Sam James
2023-02-15  8:18 Sam James
2023-02-15  7:48 Sam James
2023-02-15  7:46 Sam James
2023-02-15  7:46 Sam James
2023-02-15  7:46 Sam James
2023-02-15  7:46 Sam James
2023-02-15  7:46 Sam James
2023-02-15  7:46 Sam James
2023-02-15  2:24 Sam James
2023-02-15  2:24 Sam James
2023-02-15  2:24 Sam James
2023-02-14  3:40 Sam James
2023-02-14  3:40 Sam James
2023-02-14  3:40 Sam James
2023-02-14  3:40 Sam James
2023-02-14  0:09 Sam James
2023-02-14  0:09 Sam James
2023-02-13 21:37 Sam James
2023-02-13 21:37 Sam James
2023-02-13 21:37 Sam James
2023-02-13 21:37 Sam James
2023-02-13 21:37 Sam James
2023-02-13 21:37 Sam James
2023-02-13 21:37 Sam James
2023-02-13 21:37 Sam James
2023-02-12 18:53 Sam James
2023-02-12 18:53 Sam James
2023-02-12  6:53 Sam James
2023-02-12  6:53 Sam James
2023-02-12  6:53 Sam James
2023-02-11  1:43 Sam James
2023-02-11  1:43 Sam James
2023-02-10  6:09 Sam James
2023-02-10  6:09 Sam James
2023-02-10  6:09 Sam James
2023-02-09  3:54 Sam James
2023-02-09  3:54 Sam James
2023-02-08  3:37 Sam James
2023-02-08  1:06 Sam James
2023-02-08  0:03 Sam James
2023-02-08  0:03 Sam James
2023-02-07 23:47 Sam James
2023-02-07 23:42 Sam James
2023-02-07 23:42 Sam James
2023-02-07 23:42 Sam James
2023-02-07 23:42 Sam James
2023-02-07  1:08 Sam James
2023-02-07  1:08 Sam James
2023-02-06 13:47 Sam James
2023-02-06  4:32 Sam James
2023-02-06  4:23 Sam James
2023-02-06  4:19 Sam James
2023-02-06  4:10 Sam James
2023-02-06  4:10 Sam James
2023-02-06  3:59 Sam James
2023-02-06  3:59 Sam James
2023-02-06  3:59 Sam James
2022-07-30  5:48 Sam James
2022-07-29  2:03 Sam James
2022-07-29  2:03 Sam James
2022-07-29  2:03 Sam James
2021-08-30 21:14 Mike Gilbert
2021-08-30 21:14 Mike Gilbert
2020-11-19 18:20 Mike Gilbert
2020-11-19 18:20 Mike Gilbert
2020-11-19 18:20 Mike Gilbert
2020-01-26 23:19 Mike Gilbert

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1686136464.e08b541e2c875d7718d4452bbd8bb1228bd1a53b.sam@gentoo \
    --to=sam@gentoo.org \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox