Fix unused parameter warnings
[cu.git] / cu.c
blobdb4729ea6f871e3af27c385c392f31a6e2a24215
1 /* $OpenBSD: cu.c,v 1.14 2013/04/24 16:01:10 tedu Exp $ */
3 /*
4 * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/param.h>
20 #include <sys/ioctl.h>
22 #include <ctype.h>
23 #include <err.h>
24 #include <event.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <paths.h>
28 #include <pwd.h>
29 #include <signal.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <termios.h>
34 #include <unistd.h>
36 #include "cu.h"
38 extern char *__progname;
40 FILE *record_file;
41 struct termios saved_tio;
42 struct bufferevent *input_ev;
43 struct bufferevent *output_ev;
44 int line_fd;
45 struct termios line_tio;
46 struct bufferevent *line_ev;
47 struct event sigterm_ev;
48 struct event sighup_ev;
49 enum {
50 STATE_NONE,
51 STATE_NEWLINE,
52 STATE_TILDE
53 } last_state = STATE_NEWLINE;
55 #if !defined(__dead)
56 #define __dead __attribute__((__noreturn__))
57 #endif
59 __dead void usage(void);
60 void signal_event(int, short, void *);
61 void stream_read(struct bufferevent *, void *);
62 void stream_error(struct bufferevent *, short, void *);
63 void line_read(struct bufferevent *, void *);
64 void line_error(struct bufferevent *, short, void *);
66 __dead void
67 usage(void)
69 fprintf(stderr, "usage: %s [-l line] [-s speed | -speed]\n",
70 __progname);
71 exit(1);
74 int
75 main(int argc, char **argv)
77 const char *line, *errstr;
78 char *tmp;
79 int opt, speed, i;
81 line = "/dev/cua00";
82 speed = 9600;
85 * Convert obsolescent -### speed to modern -s### syntax which getopt()
86 * can handle.
88 for (i = 1; i < argc; i++) {
89 if (strcmp("--", argv[i]) == 0)
90 break;
91 if (argv[i][0] != '-' || !isdigit(argv[i][1]))
92 continue;
94 if (asprintf(&argv[i], "-s%s", &argv[i][1]) == -1)
95 errx(1, "speed asprintf");
98 while ((opt = getopt(argc, argv, "l:s:")) != -1) {
99 switch (opt) {
100 case 'l':
101 line = optarg;
102 break;
103 case 's':
104 speed = strtonum(optarg, 0, UINT_MAX, &errstr);
105 if (errstr != NULL)
106 errx(1, "speed is %s: %s", errstr, optarg);
107 break;
108 default:
109 usage();
112 argc -= optind;
113 argv += optind;
114 if (argc != 0)
115 usage();
117 if (strchr(line, '/') == NULL) {
118 if (asprintf(&tmp, "%s%s", _PATH_DEV, line) == -1)
119 err(1, "asprintf");
120 line = tmp;
123 line_fd = open(line, O_RDWR);
124 if (line_fd < 0)
125 err(1, "open(\"%s\")", line);
126 if (ioctl(line_fd, TIOCEXCL) != 0)
127 err(1, "ioctl(TIOCEXCL)");
128 if (tcgetattr(line_fd, &line_tio) != 0)
129 err(1, "tcgetattr");
130 if (set_line(speed) != 0)
131 err(1, "tcsetattr");
133 if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &saved_tio) != 0)
134 err(1, "tcgetattr");
136 event_init();
138 signal_set(&sigterm_ev, SIGTERM, signal_event, NULL);
139 signal_add(&sigterm_ev, NULL);
140 signal_set(&sighup_ev, SIGHUP, signal_event, NULL);
141 signal_add(&sighup_ev, NULL);
142 if (signal(SIGINT, SIG_IGN) == SIG_ERR)
143 err(1, "signal");
144 if (signal(SIGQUIT, SIG_IGN) == SIG_ERR)
145 err(1, "signal");
147 set_termios(); /* after this use cu_err and friends */
149 /* stdin and stdout get separate events */
150 input_ev = bufferevent_new(STDIN_FILENO, stream_read, NULL,
151 stream_error, NULL);
152 bufferevent_enable(input_ev, EV_READ);
153 output_ev = bufferevent_new(STDOUT_FILENO, NULL, NULL, stream_error,
154 NULL);
155 bufferevent_enable(output_ev, EV_WRITE);
157 line_ev = bufferevent_new(line_fd, line_read, NULL, line_error,
158 NULL);
159 bufferevent_enable(line_ev, EV_READ|EV_WRITE);
161 printf("Connected (speed %d)\r\n", speed);
162 event_dispatch();
164 restore_termios();
165 printf("\r\n[EOT]\n");
167 exit(0);
170 void
171 signal_event(
172 int fd,
173 short events __attribute__((unused)),
174 void *data __attribute__((unused)))
176 restore_termios();
177 printf("\r\n[SIG%d]\n", fd);
179 exit(0);
182 void
183 set_termios(void)
185 struct termios tio;
187 if (!isatty(STDIN_FILENO))
188 return;
190 memcpy(&tio, &saved_tio, sizeof(tio));
191 tio.c_lflag &= ~(ICANON|IEXTEN|ECHO);
192 tio.c_iflag &= ~(INPCK|ICRNL);
193 tio.c_iflag &= ~(IXON); /* disable XON/XOFF flow control on output. */
194 tio.c_oflag &= ~OPOST;
195 tio.c_cc[VMIN] = 1;
196 tio.c_cc[VTIME] = 0;
197 tio.c_cc[VDISCARD] = _POSIX_VDISABLE;
198 #if defined(VDSUSP)
199 tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
200 #endif
201 tio.c_cc[VINTR] = _POSIX_VDISABLE;
202 tio.c_cc[VLNEXT] = _POSIX_VDISABLE;
203 tio.c_cc[VQUIT] = _POSIX_VDISABLE;
204 tio.c_cc[VSUSP] = _POSIX_VDISABLE;
205 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0)
206 cu_err(1, "tcsetattr");
209 void
210 restore_termios(void)
212 if (isatty(STDIN_FILENO))
213 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio);
217 set_line(int speed)
219 struct termios tio;
221 memcpy(&tio, &line_tio, sizeof(tio));
222 tio.c_iflag &= ~(ISTRIP|ICRNL);
223 tio.c_oflag &= ~OPOST;
224 tio.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
225 tio.c_cflag &= ~(CSIZE|PARENB);
226 tio.c_cflag |= CREAD|CS8|CLOCAL;
227 tio.c_cc[VMIN] = 1;
228 tio.c_cc[VTIME] = 0;
229 cfsetspeed(&tio, speed);
230 if (tcsetattr(line_fd, TCSAFLUSH, &tio) != 0)
231 return (-1);
232 return (0);
235 void
236 stream_read(
237 struct bufferevent *bufev __attribute__((unused)),
238 void *data __attribute__((unused)))
240 unsigned char *new_data, *ptr;
241 size_t new_size;
242 int state_change;
244 new_data = EVBUFFER_DATA(input_ev->input);
245 new_size = EVBUFFER_LENGTH(input_ev->input);
246 if (new_size == 0)
247 return;
249 state_change = isatty(STDIN_FILENO);
250 for (ptr = new_data; ptr < new_data + new_size; ptr++) {
251 switch (last_state) {
252 case STATE_NONE:
253 if (state_change && *ptr == '\r')
254 last_state = STATE_NEWLINE;
255 break;
256 case STATE_NEWLINE:
257 if (state_change && *ptr == '~') {
258 last_state = STATE_TILDE;
259 continue;
261 if (*ptr != '\r')
262 last_state = STATE_NONE;
263 break;
264 case STATE_TILDE:
265 do_command(*ptr);
266 last_state = STATE_NEWLINE;
267 continue;
270 bufferevent_write(line_ev, ptr, 1);
273 evbuffer_drain(input_ev->input, new_size);
276 void
277 stream_error(
278 struct bufferevent *bufev __attribute__((unused)),
279 short what __attribute__((unused)),
280 void *data __attribute__((unused)))
282 event_loopexit(NULL);
285 void
286 line_read(
287 struct bufferevent *bufev __attribute__((unused)),
288 void *data __attribute__((unused)))
290 unsigned char *new_data;
291 size_t new_size;
293 new_data = EVBUFFER_DATA(line_ev->input);
294 new_size = EVBUFFER_LENGTH(line_ev->input);
295 if (new_size == 0)
296 return;
298 if (record_file != NULL)
299 fwrite(new_data, 1, new_size, record_file);
300 bufferevent_write(output_ev, new_data, new_size);
302 evbuffer_drain(line_ev->input, new_size);
305 void
306 line_error(
307 struct bufferevent *bufev __attribute__((unused)),
308 short what __attribute__((unused)),
309 void *data __attribute__((unused)))
311 event_loopexit(NULL);
314 /* Expands tildes in the file name. Based on code from ssh/misc.c. */
315 char *
316 tilde_expand(const char *filename1)
318 const char *filename, *path, *sep;
319 char user[128], *out;
320 struct passwd *pw;
321 u_int len, slash;
322 int rv;
324 if (*filename1 != '~')
325 goto no_change;
326 filename = filename1 + 1;
328 path = strchr(filename, '/');
329 if (path != NULL && path > filename) { /* ~user/path */
330 slash = path - filename;
331 if (slash > sizeof(user) - 1)
332 goto no_change;
333 memcpy(user, filename, slash);
334 user[slash] = '\0';
335 if ((pw = getpwnam(user)) == NULL)
336 goto no_change;
337 } else if ((pw = getpwuid(getuid())) == NULL) /* ~/path */
338 goto no_change;
340 /* Make sure directory has a trailing '/' */
341 len = strlen(pw->pw_dir);
342 if (len == 0 || pw->pw_dir[len - 1] != '/')
343 sep = "/";
344 else
345 sep = "";
347 /* Skip leading '/' from specified path */
348 if (path != NULL)
349 filename = path + 1;
351 if ((rv = asprintf(&out, "%s%s%s", pw->pw_dir, sep, filename)) == -1)
352 cu_err(1, "asprintf");
353 if (rv >= MAXPATHLEN) {
354 free(out);
355 goto no_change;
358 return (out);
360 no_change:
361 out = strdup(filename1);
362 if (out == NULL)
363 cu_err(1, "strdup");
364 return (out);