summaryrefslogtreecommitdiff
path: root/usr.bin/cu
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@cvs.openbsd.org>2012-07-10 08:02:28 +0000
committerNicholas Marriott <nicm@cvs.openbsd.org>2012-07-10 08:02:28 +0000
commit987bf588ac046de0e2a20e1f62985ac0fc03876a (patch)
treecae0b348a408da66e3bb65d95d8fd206fb4deff4 /usr.bin/cu
parentbdb94ced1204fae7a8c01ecb5200a0425ae10526 (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/Makefile11
-rw-r--r--usr.bin/cu/command.c135
-rw-r--r--usr.bin/cu/cu.1137
-rw-r--r--usr.bin/cu/cu.c338
-rw-r--r--usr.bin/cu/cu.h37
-rw-r--r--usr.bin/cu/input.c89
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);
+}