mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-06-03 11:12:27 +00:00
Add missing ICANON features
This commit is contained in:
parent
dd8544c3bd
commit
03875beadb
22 changed files with 526 additions and 251 deletions
139
examples/ctrlc.c
139
examples/ctrlc.c
|
@ -7,20 +7,43 @@
|
|||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/struct/pollfd.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/limits.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// this program is used by jart for manually testing teletype interrupts
|
||||
// and canonical mode line editing. this file documents the hidden depth
|
||||
// of 1960's era computer usage, that's entrenched in primitive i/o apis
|
||||
//
|
||||
// manual testing checklist:
|
||||
//
|
||||
// - "hello" enter echos "got: hello^J"
|
||||
//
|
||||
// - "hello" ctrl-d echos "got: hello"
|
||||
//
|
||||
// - "hello" ctrl-r echos "^R\nhello"
|
||||
//
|
||||
// - "hello" ctrl-u enter echos "got: ^J"
|
||||
//
|
||||
// - ctrl-d during i/o task prints "got eof" and exits
|
||||
//
|
||||
// - ctrl-d during cpu task gets delayed until read() is called
|
||||
//
|
||||
// - ctrl-c during cpu task echos ^C, then calls SignalHandler()
|
||||
// asynchronously, and program exits
|
||||
//
|
||||
// - ctrl-c during i/o task echos ^C, then calls SignalHandler()
|
||||
// asynchronously, read() raises EINTR, and program exits
|
||||
//
|
||||
// - ctrl-v ctrl-c should echo "^\b" then echo "^C" and insert "\3"
|
||||
//
|
||||
// - ctrl-v ctrl-d should echo "^\b" then echo "^D" and insert "\4"
|
||||
//
|
||||
|
||||
volatile bool gotsig;
|
||||
|
||||
|
@ -34,23 +57,41 @@ void SignalHandler(int sig) {
|
|||
gotsig = true;
|
||||
}
|
||||
|
||||
// this is the easiest way to write a string literal to standard output,
|
||||
// without formatting. printf() has an enormous binary footprint so it's
|
||||
// nice to avoid linking that when it is not needed.
|
||||
#define WRITE(sliteral) write(1, sliteral, sizeof(sliteral) - 1)
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
printf("echoing stdin until ctrl+c is pressed\n");
|
||||
WRITE("echoing stdin until ctrl+c is pressed\n");
|
||||
|
||||
// you need to set your signal handler using sigaction() rather than
|
||||
// signal(), since the latter uses .sa_flags=SA_RESTART, which means
|
||||
// read will restart itself after signals, rather than raising EINTR
|
||||
// when you type ctrl-c, by default it'll kill the process, unless you
|
||||
// define a SIGINT handler. there's multiple ways to do it. the common
|
||||
// way is to say signal(SIGINT, func) which is normally defined to put
|
||||
// the signal handler in Berkeley-style SA_RESTART mode. that means if
|
||||
// a signal handler is called while inside a function like read() then
|
||||
// the read operation will keep going afterwards like nothing happened
|
||||
// which can make it difficult to break your event loop. to avoid this
|
||||
// we can use sigaction() without specifying SA_RESTART in sa_flag and
|
||||
// that'll put the signal in system v mode. this means that whenever a
|
||||
// signal handler function in your program is called during an i/o op,
|
||||
// that i/o op will return an EINTR error, so you can churn your loop.
|
||||
// don't take that error too seriously though since SIGINT can also be
|
||||
// delivered asynchronously, during the times you're crunching numbers
|
||||
// rather than performing i/o which means you get no EINTR to warn you
|
||||
sigaction(SIGINT, &(struct sigaction){.sa_handler = SignalHandler}, 0);
|
||||
|
||||
for (;;) {
|
||||
|
||||
// some programs are blocked on cpu rather than i/o
|
||||
// such programs shall rely on asynchronous signals
|
||||
printf("doing cpu task...\n");
|
||||
// asynchronous signals are needed to interrupt math, which we shall
|
||||
// simulate here. signals can happen any time any place. that's only
|
||||
// not the case when you use sigprocmask() to block signals which is
|
||||
// useful for kicking the can down the road.
|
||||
WRITE("doing cpu task...\n");
|
||||
for (volatile int i = 0; i < INT_MAX / 5; ++i) {
|
||||
if (gotsig) {
|
||||
printf("\rgot ctrl+c asynchronously\n");
|
||||
WRITE("\rgot ctrl+c asynchronously\n");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -71,14 +112,18 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
// read data from standard input
|
||||
//
|
||||
// since this is a blocking operation and we're not performing a
|
||||
// cpu-bound operation it is almost with absolute certainty that
|
||||
// when the ctrl-c signal gets delivered, it'll happen in read()
|
||||
//
|
||||
// it's possible to be more precise if we were building library
|
||||
// code. for example, you can block signals using sigprocmask()
|
||||
// and then use pselect() to do the waiting.
|
||||
printf("doing read i/o task...\n");
|
||||
// assuming you started this program in your terminal standard input
|
||||
// will be plugged into your termios driver, which cosmpolitan codes
|
||||
// in libc/calls/read-nt.c on windows. your read() function includes
|
||||
// a primitive version of readline/linenoise called "canonical mode"
|
||||
// which lets you edit the data that'll be returned by read() before
|
||||
// it's actually returned. for example, if you type hello and enter,
|
||||
// then "hello\n" will be returned. if you type hello and then ^D or
|
||||
// ctrl-d, then "hello" will be returned. the ctrl-d keystroke is in
|
||||
// fact an ascii control code whose special behavior can be bypassed
|
||||
// if you type ctrl-v ctrl-d and then enter, in which case "\3\n" is
|
||||
// returned, also known as ^D^J.
|
||||
WRITE("doing read i/o task...\n");
|
||||
int got = read(0, buf, sizeof(buf));
|
||||
|
||||
// check if the read operation failed
|
||||
|
@ -94,10 +139,10 @@ int main(int argc, char *argv[]) {
|
|||
// the \r character is needed so when the line is printed
|
||||
// it'll overwrite the ^C that got echo'd with the ctrl-c
|
||||
if (gotsig) {
|
||||
printf("\rgot ctrl+c via i/o eintr\n");
|
||||
WRITE("\rgot ctrl+c via i/o eintr\n");
|
||||
exit(0);
|
||||
} else {
|
||||
printf("\rgot spurious eintr\n");
|
||||
WRITE("\rgot spurious eintr\n");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
|
@ -109,16 +154,34 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
// check if the user typed ctrl-d which closes the input handle
|
||||
if (!got) {
|
||||
printf("got eof\n");
|
||||
WRITE("got eof\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// relay read data to standard output
|
||||
// visualize line data returned by canonical mode to standard output
|
||||
//
|
||||
// it's usually safe to ignore the return code of write. the
|
||||
// operating system will send SIGPIPE if there's any problem
|
||||
// which kills the process by default
|
||||
// it's usually safe to ignore the return code of write; your system
|
||||
// will send SIGPIPE if there's any problem, which kills by default.
|
||||
//
|
||||
// it's possible to use keyboard shortcuts to embed control codes in
|
||||
// the line. so we visualize them using the classic tty notation. it
|
||||
// is also possible to type the ascii representation, so we use bold
|
||||
// to visually distinguish ascii codes. see also o//examples/ttyinfo
|
||||
write(1, "got: ", 5);
|
||||
write(1, buf, got);
|
||||
for (int i = 0; i < got; ++i) {
|
||||
if (isascii(buf[i])) {
|
||||
if (iscntrl(buf[i])) {
|
||||
char ctl[2];
|
||||
ctl[0] = '^';
|
||||
ctl[1] = buf[i] ^ 0100;
|
||||
WRITE("\033[1m");
|
||||
write(1, ctl, 2);
|
||||
WRITE("\033[0m");
|
||||
} else {
|
||||
write(1, &buf[i], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
WRITE("\n");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue