diff options
author | Nicholas Marriott <nicm@cvs.openbsd.org> | 2012-07-10 08:02:28 +0000 |
---|---|---|
committer | Nicholas Marriott <nicm@cvs.openbsd.org> | 2012-07-10 08:02:28 +0000 |
commit | 987bf588ac046de0e2a20e1f62985ac0fc03876a (patch) | |
tree | cae0b348a408da66e3bb65d95d8fd206fb4deff4 /usr.bin/cu | |
parent | bdb94ced1204fae7a8c01ecb5200a0425ae10526 (diff) |
Add first cut of replacement for tip/cu. Not linked to the build.
Currently supports only -l and -s (no parity), no variables and ~., ~>,
~$, ~#, ~^Z, ~?. More to come.
Tested by naddy, otto. ok miod deraadt
Diffstat (limited to 'usr.bin/cu')
-rw-r--r-- | usr.bin/cu/Makefile | 11 | ||||
-rw-r--r-- | usr.bin/cu/command.c | 135 | ||||
-rw-r--r-- | usr.bin/cu/cu.1 | 137 | ||||
-rw-r--r-- | usr.bin/cu/cu.c | 338 | ||||
-rw-r--r-- | usr.bin/cu/cu.h | 37 | ||||
-rw-r--r-- | usr.bin/cu/input.c | 89 |
6 files changed, 747 insertions, 0 deletions
diff --git a/usr.bin/cu/Makefile b/usr.bin/cu/Makefile new file mode 100644 index 00000000000..fe37a6d0dee --- /dev/null +++ b/usr.bin/cu/Makefile @@ -0,0 +1,11 @@ +# $OpenBSD: Makefile,v 1.1 2012/07/10 08:02:27 nicm Exp $ + +PROG= cu +SRCS= cu.c command.c input.c + +CDIAGFLAGS+= -Wall -W -Wno-unused-parameter + +LDADD= -levent +DPADD= ${LIBEVENT} + +.include <bsd.prog.mk> diff --git a/usr.bin/cu/command.c b/usr.bin/cu/command.c new file mode 100644 index 00000000000..b63ccd08a46 --- /dev/null +++ b/usr.bin/cu/command.c @@ -0,0 +1,135 @@ +/* $OpenBSD: command.c,v 1.1 2012/07/10 08:02:27 nicm Exp $ */ + +/* + * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/wait.h> + +#include <event.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include "cu.h" + +void pipe_command(void); +void send_file(void); + +void +pipe_command(void) +{ + const char *cmd; + pid_t pid; + int fd; + + cmd = get_input("Local command?"); + if (cmd == NULL || *cmd == '\0') + return; + + switch (pid = fork()) { + case -1: + err(1, "fork"); + case 0: + fd = open(_PATH_DEVNULL, O_RDWR); + if (fd < 0 || dup2(fd, STDIN_FILENO) == -1) + _exit(1); + close(fd); + + /* attach stdout to line */ + if (dup2(line_fd, STDOUT_FILENO) == -1) + _exit(1); + + if (closefrom(STDOUT_FILENO + 1) != 0) + _exit(1); + + execl(_PATH_BSHELL, "sh", "-c", cmd, (void*)NULL); + _exit(1); + default: + while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) + /* nothing */; + break; + } +} + +void +send_file(void) +{ + const char *file; + FILE *f; + char buf[BUFSIZ], *expanded; + size_t len; + + file = get_input("Local file?"); + if (file == NULL || *file == '\0') + return; + + expanded = tilde_expand(file); + f = fopen(expanded, "r"); + if (f == NULL) { + warn("%s", file); + return; + } + + while (!feof(f) && !ferror(f)) { + len = fread(buf, 1, sizeof(buf), f); + if (len != 0) + bufferevent_write(line_ev, buf, len); + } + + fclose(f); + free(expanded); +} + +void +do_command(char c) +{ + switch (c) { + case '.': + case '\004': /* ^D */ + event_loopexit(NULL); + break; + case '\032': /* ^Z */ + restore_termios(); + kill(getpid(), SIGTSTP); + set_termios(); + break; + case '$': + pipe_command(); + break; + case '>': + send_file(); + break; + case '#': + ioctl(line_fd, TIOCSBRK, NULL); + sleep(1); + ioctl(line_fd, TIOCCBRK, NULL); + break; + case '?': + printf("\r\n" + "~> send file to remote host\r\n" + "~$ pipe local command to remote host\r\n" + "~? get this summary\r\n" + "~# send break\r\n"); + break; + } +} diff --git a/usr.bin/cu/cu.1 b/usr.bin/cu/cu.1 new file mode 100644 index 00000000000..b5568259e63 --- /dev/null +++ b/usr.bin/cu/cu.1 @@ -0,0 +1,137 @@ +.\" $OpenBSD: cu.1,v 1.1 2012/07/10 08:02:27 nicm Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tip.1 8.4 (Berkeley) 4/18/94 +.\" +.Dd $Mdocdate: July 10 2012 $ +.Dt CU 1 +.Os +.Sh NAME +.Nm cu +.Nd serial terminal emulator +.Sh SYNOPSIS +.Nm +.Op Fl l Ar line +.Op Fl s Ar speed \*(Ba Fl Ar speed +.Sh DESCRIPTION +.Nm +is used to connect to another system over a serial link. +In the era before modern networks, it was typically used to +connect to a modem in order to dial in to a remote host. +It is now frequently used for tasks such as attaching to the +serial console of another machine for administrative or +debugging purposes. +.Pp +The options are as follows: +.Bl -tag -width 4n +.It Fl l Ar line +Specify the line to use. +Either of the forms like +.Pa tty00 +or +.Pa /dev/tty00 +are permitted. +The default is +.Pa /dev/cua00 . +.Pp +For reasons outlined in +.Xr tty 4 , +.Xr cua 4 +devices should be used on architectures which have them. +For those which do not, +.Xr tty 4 +devices can be used. +Users in group +.Dq dialer +are permitted to use +.Xr cua 4 +devices by default; +permissions on +.Pa /dev/tty00 +or +.Pa /dev/ttya +can be changed, +but they will revert to their defaults +after an upgrade or (re)install. +.It Fl s Ar speed \*(Ba Fl Ar speed +Set the speed of the connection. +The default is 9600. +.El +.Pp +Typed characters are normally transmitted directly to the remote +machine (which does the echoing as well). +A tilde +.Pq Ql ~ +appearing as the first character of a line is an escape signal; the +following are recognized: +.Bl -tag -offset indent -width Fl +.It Ic ~^D No or Ic ~. +Drop the connection and exit. +Only the connection is dropped \(en the login session is not terminated. +.It Ic ~\*(Gt +Copy file from local to remote. +.Nm +prompts for the name of a local file to transmit. +.It Ic ~$ +Pipe the output from a local +.Ux +process to the remote host. +The command string sent to the local +.Ux +system is processed by the shell. +.It Ic ~# +Send a +.Dv BREAK +to the remote system. +.It Ic ~^Z +Stop +.Nm +(only available with job control). +.It Ic ~? +Get a summary of the tilde escapes. +.El +.Pp +When +.Nm +prompts for an argument, for example during setup of a file transfer, +the line typed may be edited with the standard erase and kill characters. +A null line in response to a prompt, or an interrupt, will abort the +dialogue and return the user to the remote machine. +.Pp +.Nm +guards against multiple users connecting to a remote system by opening +modems and terminal lines with exclusive access. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +This version was written for +.Ox 5.2 +by Nicholas Marriott. diff --git a/usr.bin/cu/cu.c b/usr.bin/cu/cu.c new file mode 100644 index 00000000000..ad60e9a1b26 --- /dev/null +++ b/usr.bin/cu/cu.c @@ -0,0 +1,338 @@ +/* $OpenBSD: cu.c,v 1.1 2012/07/10 08:02:27 nicm Exp $ */ + +/* + * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> + +#include <err.h> +#include <event.h> +#include <fcntl.h> +#include <getopt.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "cu.h" + +extern char *__progname; + +struct termios saved_tio; +struct bufferevent *input_ev; +struct bufferevent *output_ev; +int line_fd; +struct bufferevent *line_ev; +struct event sigterm_ev; +enum { + STATE_NONE, + STATE_NEWLINE, + STATE_TILDE +} last_state = STATE_NEWLINE; + +__dead void usage(void); +void signal_event(int, short, void *); +void stream_read(struct bufferevent *, void *); +void stream_error(struct bufferevent *, short, void *); +void line_read(struct bufferevent *, void *); +void line_error(struct bufferevent *, short, void *); + +__dead void +usage(void) +{ + fprintf(stderr, "usage: %s [-l line] [-s speed]\n", __progname); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *line, *errstr; + char *tmp; + int opt, speed, i, ch; + struct termios tio; + static char sbuf[12]; + + line = "/dev/cua00"; + speed = 9600; + + /* + * Convert obsolescent -### speed to modern -s### syntax which getopt() + * can handle. + */ + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + ch = snprintf(sbuf, sizeof(sbuf), "-s%s", + &argv[i][1]); + if (ch <= 0 || ch >= (int)sizeof(sbuf)) { + errx(1, "invalid speed: %s", + &argv[i][1]); + } + argv[i] = sbuf; + break; + case '-': + /* if we get "--" stop processing args */ + if (argv[i][2] == '\0') + goto getopt; + break; + } + } + } + +getopt: + while ((opt = getopt(argc, argv, "l:s:")) != -1) { + switch (opt) { + case 'l': + line = optarg; + break; + case 's': + speed = strtonum(optarg, 0, UINT_MAX, &errstr); + if (errstr != NULL) + errx(1, "speed is %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc != 0) + usage(); + + if (strchr(line, '/') == NULL) { + if (asprintf(&tmp, "%s%s", _PATH_DEV, line) == -1) + err(1, "asprintf"); + line = tmp; + } + + line_fd = open(line, O_RDWR); + if (line_fd < 0) + err(1, "open(\"%s\")", line); + if (ioctl(line_fd, TIOCEXCL) != 0) + err(1, "ioctl(TIOCEXCL)"); + + cfmakeraw(&tio); + tio.c_iflag = 0; + tio.c_oflag = 0; + tio.c_lflag = 0; + tio.c_cflag = CREAD|CS8; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + cfsetspeed(&tio, speed); + if (tcsetattr(line_fd, TCSAFLUSH, &tio) != 0) + err(1, "tcsetattr"); + + if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &saved_tio) != 0) + err(1, "tcgetattr"); + + event_init(); + signal_set(&sigterm_ev, SIGTERM, signal_event, NULL); + signal_add(&sigterm_ev, NULL); + + set_termios(); + + /* stdin and stdout get separate events */ + input_ev = bufferevent_new(STDIN_FILENO, stream_read, NULL, + stream_error, NULL); + bufferevent_enable(input_ev, EV_READ); + output_ev = bufferevent_new(STDOUT_FILENO, NULL, NULL, stream_error, + NULL); + bufferevent_enable(output_ev, EV_WRITE); + + line_ev = bufferevent_new(line_fd, line_read, NULL, line_error, + NULL); + bufferevent_enable(line_ev, EV_READ|EV_WRITE); + + printf("Connected (speed %u)\r\n", speed); + event_dispatch(); + + if (isatty(STDIN_FILENO)) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio); + printf("\r\n[EOT]\n"); + + exit(0); +} + +void +signal_event(int fd, short events, void *data) +{ + if (isatty(STDIN_FILENO)) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio); + printf("\r\n[SIG%s]\n", sys_signame[fd]); + + exit(0); +} + +void +set_termios(void) +{ + struct termios tio; + + if (!isatty(STDIN_FILENO)) + return; + + memcpy(&tio, &saved_tio, sizeof(tio)); + tio.c_lflag &= ~(ICANON|IEXTEN|ECHO); + tio.c_iflag &= ~(INPCK|ICRNL); + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + tio.c_cc[VDISCARD] = _POSIX_VDISABLE; + tio.c_cc[VDSUSP] = _POSIX_VDISABLE; + tio.c_cc[VINTR] = _POSIX_VDISABLE; + tio.c_cc[VLNEXT] = _POSIX_VDISABLE; + tio.c_cc[VQUIT] = _POSIX_VDISABLE; + tio.c_cc[VSUSP] = _POSIX_VDISABLE; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0) + err(1, "tcsetattr"); +} + +void +restore_termios(void) +{ + if (!isatty(STDIN_FILENO)) + return; + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio) != 0) + err(1, "tcsetattr"); +} + +void +stream_read(struct bufferevent *bufev, void *data) +{ + char *new_data, *ptr; + size_t new_size; + int state_change; + + new_data = EVBUFFER_DATA(input_ev->input); + new_size = EVBUFFER_LENGTH(input_ev->input); + if (new_size == 0) + return; + + state_change = isatty(STDIN_FILENO); + for (ptr = new_data; ptr < new_data + new_size; ptr++) { + switch (last_state) { + case STATE_NONE: + if (state_change && *ptr == '\r') + last_state = STATE_NEWLINE; + break; + case STATE_NEWLINE: + if (state_change && *ptr == '~') { + last_state = STATE_TILDE; + continue; + } + if (*ptr != '\r') + last_state = STATE_NONE; + break; + case STATE_TILDE: + do_command(*ptr); + last_state = STATE_NEWLINE; + continue; + } + + bufferevent_write(line_ev, ptr, 1); + } + + evbuffer_drain(input_ev->input, new_size); +} + +void +stream_error(struct bufferevent *bufev, short what, void *data) +{ + event_loopexit(NULL); +} + +void +line_read(struct bufferevent *bufev, void *data) +{ + char *new_data; + size_t new_size; + + new_data = EVBUFFER_DATA(line_ev->input); + new_size = EVBUFFER_LENGTH(line_ev->input); + if (new_size == 0) + return; + + bufferevent_write(output_ev, new_data, new_size); + + evbuffer_drain(line_ev->input, new_size); +} + +void +line_error(struct bufferevent *bufev, short what, void *data) +{ + event_loopexit(NULL); +} + +/* Expands tildes in the file name. Based on code from ssh/misc.c. */ +char * +tilde_expand(const char *filename1) +{ + const char *filename, *path; + char user[128], ret[MAXPATHLEN], *out; + struct passwd *pw; + u_int len, slash; + + if (*filename1 != '~') + goto no_change; + filename = filename1 + 1; + + path = strchr(filename, '/'); + if (path != NULL && path > filename) { /* ~user/path */ + slash = path - filename; + if (slash > sizeof(user) - 1) + goto no_change; + memcpy(user, filename, slash); + user[slash] = '\0'; + if ((pw = getpwnam(user)) == NULL) + goto no_change; + } else if ((pw = getpwuid(getuid())) == NULL) /* ~/path */ + goto no_change; + + if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret)) + goto no_change; + + /* Make sure directory has a trailing '/' */ + len = strlen(pw->pw_dir); + if ((len == 0 || pw->pw_dir[len - 1] != '/') && + strlcat(ret, "/", sizeof(ret)) >= sizeof(ret)) + goto no_change; + + /* Skip leading '/' from specified path */ + if (path != NULL) + filename = path + 1; + if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret)) + goto no_change; + + out = strdup(ret); + if (out == NULL) + err(1, "strdup"); + return (out); + +no_change: + out = strdup(filename1); + if (out == NULL) + err(1, "strdup"); + return (out); +} diff --git a/usr.bin/cu/cu.h b/usr.bin/cu/cu.h new file mode 100644 index 00000000000..db669becf31 --- /dev/null +++ b/usr.bin/cu/cu.h @@ -0,0 +1,37 @@ +/* $OpenBSD: cu.h,v 1.1 2012/07/10 08:02:27 nicm Exp $ */ + +/* + * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CU_H +#define CU_H + +/* command.c */ +void do_command(char); + +/* cu.c */ +extern struct bufferevent *input_ev; +extern struct bufferevent *output_ev; +extern int line_fd; +extern struct bufferevent *line_ev; +void set_termios(void); +void restore_termios(void); +char *tilde_expand(const char *); + +/* input.c */ +const char *get_input(const char *); + +#endif diff --git a/usr.bin/cu/input.c b/usr.bin/cu/input.c new file mode 100644 index 00000000000..03f8599662d --- /dev/null +++ b/usr.bin/cu/input.c @@ -0,0 +1,89 @@ +/* $OpenBSD: input.c,v 1.1 2012/07/10 08:02:27 nicm Exp $ */ + +/* + * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#include "cu.h" + +/* + * Prompt and read a line of user input from stdin. We want to use the termios + * we were started with so restore and stick in a signal handler for ^C. + */ + +volatile sig_atomic_t input_stop; + +void input_signal(int); + +void +input_signal(int sig) +{ + input_stop = 1; +} + +const char * +get_input(const char *prompt) +{ + static char s[BUFSIZ]; + struct sigaction act, oact; + char c, *cp, *out = NULL; + ssize_t n; + + memset(&act, 0, sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = input_signal; + if (sigaction(SIGINT, &act, &oact) != 0) + err(1, "sigaction"); + input_stop = 0; + + restore_termios(); + + printf("%s ", prompt); + fflush(stdout); + + cp = s; + while (cp != s + sizeof(s) - 1) { + n = read(STDIN_FILENO, &c, 1); + if (n == -1 && errno != EINTR) + err(1, "read"); + if (n != 1 || input_stop) + break; + if (c == '\n') { + out = s; + break; + } + if (!iscntrl((u_char)c)) + *cp++ = c; + } + *cp = '\0'; + + set_termios(); + + sigaction(SIGINT, &oact, NULL); + + return (out); +} |