1 /*	-*- mode: c; mode: fold -*-	*/
2 /*{{{	includes */
3 # include	"config.h"
4 # include	<stdio.h>
5 # include	<stdlib.h>
6 # include	<stdarg.h>
7 # include	<ctype.h>
8 # include	<unistd.h>
9 # include	<fcntl.h>
10 # include	<string.h>
11 # include	<termios.h>
12 # include	<errno.h>
13 # include	<signal.h>
14 # include	<sys/time.h>
15 # include	<sys/types.h>
16 # include	<sys/stat.h>
17 # if		HAVE_SYS_SELECT_H
18 # include	<sys/select.h>
19 # endif		/* HAVE_SYS_SELECT_H */
20 # if		HAVE_SYS_SYSMACROS_H
21 # include	<sys/sysmacros.h>
22 # elif		HAVE_SYS_MKDEV_H
23 # include	<sys/mkdev.h>
24 # else		/* ! HAVE_SYS_SYSMACROS_H && ! HAVE_SYS_MKDEV_H */
25 # ifndef major
26 # define	major(xx)	(((xx) >> 8) & 0xff)
27 # define	minor(xx)	((xx) & 0xff)
28 # endif		/* major */
29 # endif		/* HAVE_SYS_SYSMACROS_H || HAVE_SYS_MKDEV_H */
30 # include	"pager.h"
31 /*}}}*/
32 /*{{{	statics and typedefs */
33 static struct {
34 	int	speed;
35 	speed_t	tok;
36 }	stab[] = {
37 	{	   300,	B300	},
38 	{	  1200,	B1200	},
39 	{	  2400,	B2400	},
40 	{	  4800,	B4800	},
41 	{	  9600,	B9600	},
42 	{	 19200,	B19200	},
43 	{	 38400,	B38400	},
44 # ifdef		B57600
45 	{	 57600,	B57600	},
46 # endif		/* B57600 */
47 # ifdef		B115200
48 	{	115200, B115200	},
49 # endif		/* B115200 */
50 # ifdef		B230400
51 	{	230400, B230400	},
52 # endif		/* B230400 */
53 # ifdef		B460800
54 	{	460800, B460800	},
55 # endif		/* B460800 */
56 	{	-1,	B9600	}
57 };
58 
59 static char	*lckmth[] = {
60 	"ascii",
61 	"binary",
62 	"lower",
63 	"upper",
64 	"sysv4",
65 	"timeout",
66 	NULL
67 };
68 
69 typedef struct {
70 # ifndef	NDEBUG
71 # define	MAGIC		MKMAGIC ('t', 't', 'y', '\0')
72 	long		magic;
73 # endif		/* NDEBUG */
74 	char		*lck;
75 	char		*device;
76 	struct termios	tty, sav;
77 	int		fd;
78 	string_t	*line;
79 	char		*sep;
80 	void		(*callback) (void *, string_t *, char_t, void *);
81 	void		*data;
82 	Bool		suspend;
83 }	serial;
84 
85 typedef struct _expect {
86 	int	idx;
87 	char	*str;
88 	int	pos;
89 	int	len;
90 	struct _expect
91 		*next;
92 }	expect;
93 /*}}}*/
94 /*{{{	support routines */
95 static char *
mkprint(char * str,int len)96 mkprint (char *str, int len)
97 {
98 	static char	*buf = NULL;
99 	static int	size = 0;
100 	int		n;
101 	char		ch;
102 	char		extra;
103 	char		*ptr;
104 
105 	if (len >= size) {
106 		size = len + 128;
107 		if (! (buf = Realloc (buf, size + 2)))
108 			return NULL;
109 	}
110 	extra = '\0';
111 	for (n = 0; len > 0; ++str, --len) {
112 		if (n + 8 >= size) {
113 			size += 128;
114 			if (! (buf = Realloc (buf, size + 2)))
115 				break;
116 		}
117 		ch = *str;
118 		if (ch & 0x80) {
119 			buf[n++] = '<';
120 			buf[n++] = 'M';
121 			buf[n++] = '-';
122 			ch &= 0x7f;
123 			extra = '>';
124 		}
125 		switch (ch) {
126 		case '\x00':	ptr = "<nul>";	break;
127 		case '\x01':	ptr = "<soh>";	break;
128 		case '\x02':	ptr = "<stx>";	break;
129 		case '\x03':	ptr = "<etx>";	break;
130 		case '\x04':	ptr = "<eot>";	break;
131 		case '\x05':	ptr = "<enq>";	break;
132 		case '\x06':	ptr = "<ack>";	break;
133 		case '\x07':	ptr = "<bel>";	break;
134 		case '\x08':	ptr = "<bs>";	break;
135 		case '\x09':	ptr = "<ht>";	break;
136 		case '\x0a':	ptr = "<lf>";	break;
137 		case '\x0b':	ptr = "<vt>";	break;
138 		case '\x0c':	ptr = "<ff>";	break;
139 		case '\x0d':	ptr = "<cr>";	break;
140 		case '\x0e':	ptr = "<so>";	break;
141 		case '\x0f':	ptr = "<si>";	break;
142 		case '\x10':	ptr = "<dle>";	break;
143 		case '\x11':	ptr = "<dc1>";	break;
144 		case '\x12':	ptr = "<dc2>";	break;
145 		case '\x13':	ptr = "<dc3>";	break;
146 		case '\x14':	ptr = "<dc4>";	break;
147 		case '\x15':	ptr = "<nak>";	break;
148 		case '\x16':	ptr = "<syn>";	break;
149 		case '\x17':	ptr = "<etb>";	break;
150 		case '\x18':	ptr = "<can>";	break;
151 		case '\x19':	ptr = "<em>";	break;
152 		case '\x1a':	ptr = "<sub>";	break;
153 		case '\x1b':	ptr = "<esc>";	break;
154 		case '\x1c':	ptr = "<fs>";	break;
155 		case '\x1d':	ptr = "<gs>";	break;
156 		case '\x1e':	ptr = "<rs>";	break;
157 		case '\x1f':	ptr = "<us>";	break;
158 		case '\x7f':	ptr = "<del>";	break;
159 		default:
160 			ptr = NULL;
161 			buf[n++] = ch;
162 			break;
163 		}
164 		if (ptr)
165 			while (*ptr)
166 				buf[n++] = *ptr++;
167 		if (extra) {
168 			buf[n++] = extra;
169 			extra = '\0';
170 		}
171 	}
172 	if (buf)
173 		buf[n] = '\0';
174 	return buf;
175 }
176 
177 static inline void
msleep(int msec)178 msleep (int msec)
179 {
180 	struct timeval	tv;
181 
182 	if (msec > 0) {
183 		do {
184 			tv.tv_sec = msec / 1000;
185 			tv.tv_usec = (msec % 1000) * 1000;
186 			errno = 0;
187 		}	while ((select (0, NULL, NULL, NULL, & tv) < 0) && (errno == EINTR));
188 	}
189 }
190 
191 static inline int
data_ready(int fd,int * msec)192 data_ready (int fd, int *msec)
193 {
194 	int		ret = 0;
195 	struct timeval	tv;
196 	fd_set		fset;
197 
198 	FD_ZERO (& fset);
199 	FD_SET (fd, & fset);
200 	tv.tv_sec = *msec / 1000;
201 	tv.tv_usec = (*msec % 1000) * 1000;
202 	if (((ret = select (fd + 1, & fset, NULL, NULL, & tv)) > 0) && FD_ISSET (fd, & fset)) {
203 		*msec = tv.tv_sec * 1000 + (tv.tv_usec / 1000);
204 		return 1;
205 	}
206 	return ret;
207 }
208 
209 static Bool
do_lock(serial * s,char * dev,char * prefix,char * method)210 do_lock (serial *s, char *dev, char *prefix, char *method)
211 {
212 	Bool		binary;
213 	Bool		lower, upper;
214 	Bool		sysv4;
215 	int		tout;
216 	struct stat	st;
217 	char		*ptr, *sav, *val;
218 	int		len;
219 	char		*bdev;
220 	int		fd;
221 	char		buf[32];
222 	int		n, m;
223 	pid_t		pid;
224 
225 	s -> lck = NULL;
226 	if (prefix) {
227 		binary = False;
228 		lower = False;
229 		upper = False;
230 		sysv4 = False;
231 		tout = 0;
232 		if (method && (method = strdup (method))) {
233 			for (ptr = method; *ptr; ) {
234 				sav = ptr;
235 				ptr = skipch (ptr, ',');
236 				val = skipch (sav, '=');
237 				len = strlen (sav);
238 				for (n = 0; lckmth[n]; ++n)
239 					if (! strncmp (lckmth[n], sav, len))
240 						break;
241 				switch (n) {
242 				case 0:		/* ascii */
243 					binary = False;
244 					break;
245 				case 1:		/* binary */
246 					binary = True;
247 					break;
248 				case 2:		/* lower */
249 					lower = True;
250 					upper = False;
251 					break;
252 				case 3:		/* upper */
253 					lower = False;
254 					upper = True;
255 					break;
256 				case 4:		/* sysv4 */
257 					sysv4 = True;
258 					break;
259 				case 5:		/* timeout */
260 					tout = atoi (val);
261 					break;
262 				}
263 			}
264 			free (method);
265 		}
266 		if (sysv4) {
267 			bdev = NULL;
268 			if ((stat (dev, & st) != -1) && S_ISCHR (st.st_mode) && (bdev = malloc (96)))
269 				sprintf (bdev, "%03d.%03d.%03d", major (st.st_dev), major (st.st_rdev), minor (st.st_rdev));
270 		} else {
271 			if (bdev = strrchr (dev, '/'))
272 				++bdev;
273 			else
274 				bdev = dev;
275 			bdev = strdup (bdev);
276 		}
277 		len = strlen (prefix);
278 		if (bdev && (s -> lck = malloc (len + strlen (bdev) + 4))) {
279 			sprintf (s -> lck, "%s%s", prefix, bdev);
280 			free (bdev);
281 			if (upper || lower) {
282 				ptr = s -> lck + len;
283 				while (*ptr) {
284 					if (lower)
285 						*ptr = tolower (*ptr);
286 					else if (upper)
287 						*ptr = toupper (*ptr);
288 					++ptr;
289 				}
290 			}
291 			do {
292 				for (n = 0; n < 2; ++n) {
293 					if ((fd = open (s -> lck, O_CREAT | O_EXCL | O_WRONLY, 0600)) != -1)
294 						break;
295 					if ((! n) && ((fd = open (s -> lck, O_RDONLY)) != -1)) {
296 						pid = 0;
297 						if (binary) {
298 							if (read (fd, & pid, sizeof (pid)) != sizeof (pid))
299 								pid = 0;
300 						} else {
301 							if ((m = read (fd, buf, sizeof (buf) - 1)) > 1) {
302 								buf[m - 1] = '\0';
303 								pid = (int) atoi (buf);
304 							}
305 						}
306 						close (fd);
307 						fd = -1;
308 						if ((pid > 0) && (kill (pid, 0) < 0) && (errno == ESRCH))
309 							unlink (s -> lck);
310 						else
311 							break;
312 					}
313 				}
314 				if ((fd < 0) && (tout > 0))
315 					sleep (1);
316 			}	while ((fd < 0) && (tout-- > 0));
317 			if (fd != -1) {
318 				pid = getpid ();
319 				if (binary)
320 					write (fd, & pid, sizeof (pid));
321 				else {
322 					sprintf (buf, "%10d\n", (int) pid);
323 					write (fd, buf, strlen (buf));
324 				}
325 # if		HAVE_FCHMOD
326 				fchmod (fd, 0644);
327 # else		/* HAVE_FCHMOD */
328 				chmod (s -> lck, 0644);
329 # endif		/* HAVE_FCHMOD */
330 # if		HAVE_FCHOWN
331 				fchown (fd, geteuid (), getegid ());
332 # else		/* HAVE_FCHOWN */
333 				chown (s -> lck, geteuid (), getegid ());
334 # endif		/* HAVE_FCHOWN */
335 				close (fd);
336 			} else {
337 				free (s -> lck);
338 				s -> lck = NULL;
339 				return False;
340 			}
341 		} else {
342 			if (bdev)
343 				free (bdev);
344 			return False;
345 		}
346 	}
347 	return True;
348 }
349 
350 static void
do_unlock(serial * s)351 do_unlock (serial *s)
352 {
353 	if (s -> lck) {
354 		unlink (s -> lck);
355 		free (s -> lck);
356 		s -> lck = NULL;
357 	}
358 }
359 /*}}}*/
360 /*{{{	open/close/reopen */
361 void *
tty_open(char * dev,char * lckprefix,char * lckmethod)362 tty_open (char *dev, char *lckprefix, char *lckmethod)
363 {
364 	serial	*s;
365 	int	n;
366 
367 	if (s = (serial *) malloc (sizeof (serial))) {
368 # ifndef	NDEBUG
369 		s -> magic = MAGIC;
370 # endif		/* NDEBUG */
371 		if (do_lock (s, dev, lckprefix, lckmethod)) {
372 			if ((s -> fd = open (dev, O_RDWR)) != -1) {
373 				n = tcgetattr (s -> fd, & s -> sav);
374 				if ((n < 0) || (! (s -> device = strdup (dev)))) {
375 					close (s -> fd);
376 					do_unlock (s);
377 					free (s);
378 					s = NULL;
379 				} else {
380 					s -> tty = s -> sav;
381 					s -> line = NULL;
382 					s -> sep = NULL;
383 					s -> callback = NULL;
384 					s -> data = NULL;
385 					s -> suspend = False;
386 				}
387 			} else {
388 				do_unlock (s);
389 				free (s);
390 				s = NULL;
391 			}
392 		} else {
393 			free (s);
394 			s = NULL;
395 		}
396 	}
397 	return s;
398 }
399 
400 void *
tty_close(void * sp)401 tty_close (void *sp)
402 {
403 	serial	*s = (serial *) sp;
404 
405 	MCHK (s);
406 	if (s) {
407 		if (s -> fd != -1) {
408 			tcsetattr (s -> fd, TCSANOW, & s -> sav);
409 			close (s -> fd);
410 		}
411 		do_unlock (s);
412 		if (s -> device)
413 			free (s -> device);
414 		if (s -> sep)
415 			free (s -> sep);
416 		if (s -> line)
417 			sfree (s -> line);
418 		free (s);
419 	}
420 	return NULL;
421 }
422 
423 Bool
tty_reopen(void * sp,int msec)424 tty_reopen (void *sp, int msec)
425 {
426 	serial	*s = (serial *) sp;
427 
428 	MCHK (s);
429 	if (s -> fd != -1) {
430 		close (s -> fd);
431 		if (msec > 0)
432 			msleep (msec);
433 		s -> fd = -1;
434 	}
435 	if (s -> device && ((s -> fd = open (s -> device, O_RDWR)) != -1))
436 		tcsetattr (s -> fd, TCSANOW, & s -> tty);
437 	return s -> fd != -1 ? True : False;
438 }
439 /*}}}*/
440 /*{{{	hangup, get fd */
441 void
tty_hangup(void * sp,int msec)442 tty_hangup (void *sp, int msec)
443 {
444 	serial		*s = (serial *) sp;
445 	struct termios	tmp;
446 
447 	MCHK (s);
448 	V (2, ("[Hangup] "));
449 	if (s && (s -> fd != -1)) {
450 		tmp = s -> tty;
451 		cfsetispeed (& tmp, B0);
452 		cfsetospeed (& tmp, B0);
453 		if (tcsetattr (s -> fd, TCSANOW, & tmp) != -1) {
454 			msleep (msec);
455 			tcsetattr (s -> fd, TCSANOW, & s -> tty);
456 		}
457 		tty_reopen (s, msec);
458 	}
459 	V (2, ("\n"));
460 }
461 
462 int
tty_fd(void * sp)463 tty_fd (void *sp)
464 {
465 	serial	*s = (serial *) sp;
466 
467 	MCHK (s);
468 	return s ? s -> fd : -1;
469 }
470 /*}}}*/
471 /*{{{	configuration */
472 int
tty_setup(void * sp,Bool raw,Bool modem,int speed,int bpb,int sb,char par)473 tty_setup (void *sp, Bool raw, Bool modem, int speed, int bpb, int sb, char par)
474 {
475 	serial	*s = (serial *) sp;
476 	int	n;
477 
478 	MCHK (s);
479 	if ((! s) || (s -> fd < 0))
480 		return -1;
481 	if ((bpb < 5) || (bpb > 8) ||
482 	    ((sb != 1) && (sb != 2)) ||
483 	    ((par != 'n') && (par != 'e') && (par != 'o')))
484 		return -1;
485 	for (n = 0; stab[n].speed > 0; ++n)
486 		if (stab[n].speed == speed)
487 			break;
488 	if (stab[n].speed < 0)
489 		return -1;
490 	if (raw) {
491 		s -> tty.c_iflag &= ~(IGNCR | ICRNL | INLCR | ISTRIP | IXON | IXOFF);
492 		s -> tty.c_iflag |= IGNBRK;
493 		s -> tty.c_oflag = 0;
494 		s -> tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG);
495 # ifdef		CRTSCTS
496 # ifdef		sun
497 		/* SunOS 4.x needs RTSCTS off if carrier detect is on */
498 		/* but there is no carrier present (fkk) */
499 		s -> tty.c_cflag &= ~CRTSCTS;
500 # else		/* sun */
501 		s -> tty.c_cflag |= CRTSCTS;
502 # endif		/* sun */
503 # endif		/* CRTSCTS */
504 		s -> tty.c_cc[VMIN] = 0;
505 		s -> tty.c_cc[VTIME] = 0;
506 	} else {
507 		s -> tty = s -> sav;
508 		s -> tty.c_lflag |= ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG;
509 	}
510 	if (modem) {
511 # if	0
512 		s -> tty.c_cflag &= ~CLOCAL;
513 # else
514 		s -> tty.c_cflag |= CLOCAL;
515 # endif
516 		s -> tty.c_cflag |= HUPCL;
517 	} else {
518 		s -> tty.c_cflag |= CLOCAL;
519 		s -> tty.c_cflag &= ~HUPCL;
520 	}
521 	cfsetispeed (& s -> tty, stab[n].tok);
522 	cfsetospeed (& s -> tty, stab[n].tok);
523 	s -> tty.c_cflag &= ~(CSIZE);
524 	s -> tty.c_cflag |= CREAD;
525 	switch (sb) {
526 	default:
527 	case 1:	s -> tty.c_cflag &= ~CSTOPB;	break;
528 	case 2:	s -> tty.c_cflag |= CSTOPB;	break;
529 	}
530 	switch (bpb) {
531 	case 5:	s -> tty.c_cflag |= CS5;	break;
532 	case 6:	s -> tty.c_cflag |= CS6;	break;
533 	case 7:	s -> tty.c_cflag |= CS7;	break;
534 	default:
535 	case 8:	s -> tty.c_cflag |= CS8;	break;
536 	}
537 	switch (par) {
538 	default:
539 	case 'n':
540 		s -> tty.c_cflag &= ~PARENB;
541 		break;
542 	case 'e':
543 		s -> tty.c_cflag &= ~PARODD;
544 		s -> tty.c_cflag |= PARENB;
545 		break;
546 	case 'o':
547 		s -> tty.c_cflag |= PARENB | PARODD;
548 		break;
549 	}
550 	return tcsetattr (s -> fd, TCSANOW, & s -> tty) < 0 ? -1 : 0;
551 }
552 /*}}}*/
553 /*{{{	callback */
554 void
tty_set_line_callback(void * sp,void (* func)(void *,string_t *,char_t,void *),char * sep,void * data)555 tty_set_line_callback (void *sp, void (*func) (void *, string_t *, char_t, void *), char *sep, void *data)
556 {
557 	serial	*s = (serial *) sp;
558 
559 	MCHK (s);
560 	if (s) {
561 		if (! (s -> callback = func)) {
562 			if (s -> line)
563 				s -> line = sfree (s -> line);
564 			if (s -> sep) {
565 				free (s -> sep);
566 				s -> sep = NULL;
567 			}
568 			s -> data = NULL;
569 		} else {
570 			if (s -> sep)
571 				free (s -> sep);
572 			s -> sep = sep ? strdup (sep) : NULL;
573 			s -> data = data;
574 		}
575 		s -> suspend = False;
576 	}
577 }
578 
579 void
tty_suspend_callback(void * sp,Bool susp)580 tty_suspend_callback (void *sp, Bool susp)
581 {
582 	serial	*s = (serial *) sp;
583 
584 	MCHK (s);
585 	if (s)
586 		if (s -> suspend = susp)
587 			if (s -> line)
588 				s -> line -> len = 0;
589 }
590 /*}}}*/
591 /*{{{	sending */
592 int
tty_send(void * sp,char * str,int len)593 tty_send (void *sp, char *str, int len)
594 {
595 	serial	*s = (serial *) sp;
596 	int	n, sent;
597 
598 	MCHK (s);
599 	if ((! s) || (s -> fd < 0) || (! str))
600 		return -1;
601 	V (2, ("[Send] %s", mkprint (str, len)));
602 	sent = 0;
603 	while (len > 0)
604 		if ((n = write (s -> fd, str, len)) > 0) {
605 			str += n;
606 			sent += n;
607 			len -= n;
608 		} else if (! n)
609 			break;
610 		else if (errno == EIO) {
611 			if (! tty_reopen (s, 0))
612 				break;
613 		} else if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
614 			msleep (200);
615 		else if (errno != EINTR)
616 			break;
617 	V (2, ("\n"));
618 	return sent;
619 }
620 
621 int
tty_send_string(void * sp,char * str)622 tty_send_string (void *sp, char *str)
623 {
624 	return str ? tty_send (sp, str, strlen (str)) : -1;
625 }
626 /*}}}*/
627 /*{{{	expecting */
628 static void
addline(serial * s,char ch)629 addline (serial *s, char ch)
630 {
631 	if (s -> callback && s -> sep && (! s -> suspend) && ch) {
632 		if (! s -> line)
633 			s -> line = snew (NULL, 32);
634 		else if (s -> line -> len + 1 >= s -> line -> size)
635 			if (! sexpand (s -> line, s -> line -> size * 2))
636 				return;
637 		if (strchr (s -> sep, ch)) {
638 			(*s -> callback) ((void *) s, s -> line, (char_t) ch, s -> data);
639 			s -> line -> len = 0;
640 		} else
641 			s -> line -> str[s -> line -> len++] = (char_t) ch;
642 	}
643 }
644 
645 static int
do_expect(serial * s,int tout,expect * base)646 do_expect (serial *s, int tout, expect *base)
647 {
648 	int	ret;
649 	int	msec;
650 	int	n;
651 	expect	*run;
652 	char	ch;
653 
654 	if ((! s) || (s -> fd < 0))
655 		return -1;
656 	V (2, ("[Expect] "));
657 	for (run = base; run; run = run -> next)
658 		run -> pos = 0;
659 	msec = (tout > 0) ? tout * 1000 : 0;
660 	ret = 0;
661 	while (! ret)
662 		if ((n = data_ready (s -> fd, & msec)) > 0) {
663 			while ((n = read (s -> fd, & ch, 1)) == 1) {
664 				addline (s, ch);
665 				V (3, ("%s", mkprint (& ch, 1)));
666 				for (run = base; run; run = run -> next)
667 					if (run -> str[run -> pos] == ch) {
668 						run -> pos++;
669 						if (run -> pos == run -> len) {
670 							ret = run -> idx;
671 							break;
672 						}
673 					} else
674 						run -> pos = 0;
675 				if (ret)
676 					break;
677 			}
678 			if (n < 0)
679 				if (errno == EIO)
680 					tty_reopen (s, 0);
681 		} else if (! n)
682 			break;
683 	if (verbose > 1)
684 		if (run)
685 			printf (" got %s", mkprint (run -> str, run -> len));
686 		else
687 			printf (" timeout");
688 	V (2, ("\n"));
689 	return ret;
690 }
691 
692 int
tty_expect(void * sp,int tout,...)693 tty_expect (void *sp, int tout, ...)
694 {
695 	va_list	par;
696 	int	ret;
697 	char	*ptr;
698 	expect	*base, *prev, *tmp;
699 	int	idx;
700 
701 	va_start (par, tout);
702 	MCHK ((serial *) sp);
703 	ret = 0;
704 	base = NULL;
705 	prev = NULL;
706 	idx = 1;
707 	while (ptr = va_arg (par, char *))
708 		if (tmp = (expect *) malloc (sizeof (expect))) {
709 			tmp -> idx = idx++;
710 			tmp -> str = ptr;
711 			tmp -> pos = 0;
712 			tmp -> len = va_arg (par, int);
713 			tmp -> next = NULL;
714 			if (prev)
715 				prev -> next = tmp;
716 			else
717 				base = tmp;
718 			prev = tmp;
719 		} else
720 			break;
721 	if (! ptr)
722 		ret = do_expect ((serial *) sp, tout, base);
723 	else
724 		ret = -1;
725 	while (base) {
726 		tmp = base;
727 		base = base -> next;
728 		free (tmp);
729 	}
730 	va_end (par);
731 	return ret;
732 }
733 
734 int
tty_expect_list(void * sp,int tout,char ** strs,int * lens)735 tty_expect_list (void *sp, int tout, char **strs, int *lens)
736 {
737 	int	n;
738 	int	ret;
739 	expect	*base, *prev, *tmp;
740 
741 	MCHK ((serial *) sp);
742 	base = NULL;
743 	prev = NULL;
744 	for (n = 0; strs[n]; ++n)
745 		if (tmp = (expect *) malloc (sizeof (expect))) {
746 			tmp -> idx = n + 1;
747 			tmp -> str = strs[n];
748 			tmp -> pos = 0;
749 			tmp -> len = lens[n];
750 			tmp -> next = NULL;
751 			if (prev)
752 				prev -> next = tmp;
753 			else
754 				base = tmp;
755 			prev = tmp;
756 		} else
757 			break;
758 	if (strs[n])
759 		ret = -1;
760 	else
761 		ret = do_expect ((serial *) sp, tout, base);
762 	while (base) {
763 		tmp = base;
764 		base = base -> next;
765 		free (tmp);
766 	}
767 	return ret;
768 }
769 
770 int
tty_expect_string(void * sp,int tout,char * str)771 tty_expect_string (void *sp, int tout, char *str)
772 {
773 	expect	tmp;
774 
775 	MCHK ((serial *) sp);
776 	if (! str)
777 		return -1;
778 	tmp.idx = 1;
779 	tmp.str = str;
780 	tmp.pos = 0;
781 	tmp.len = strlen (str);
782 	tmp.next = NULL;
783 	return do_expect ((serial *) sp, tout, & tmp);
784 }
785 /*}}}*/
786 /*{{{	send/expect */
787 static int
tonum(char ch)788 tonum (char ch)
789 {
790 	switch (ch) {
791 	default:
792 	case '0':	return 0;
793 	case '1':	return 1;
794 	case '2':	return 2;
795 	case '3':	return 3;
796 	case '4':	return 4;
797 	case '5':	return 5;
798 	case '6':	return 6;
799 	case '7':	return 7;
800 	case '8':	return 8;
801 	case '9':	return 9;
802 	}
803 }
804 
805 static char *
expand(char * str,char ** opts,int ocnt)806 expand (char *str, char **opts, int ocnt)
807 {
808 	char	*ret;
809 	int	len, siz;
810 	int	idx, olen;
811 
812 	ret = NULL;
813 	len = 0;
814 	siz = 0;
815 	while (*str) {
816 		if (len >= siz) {
817 			siz += (siz ? siz : 32);
818 			if (! (ret = Realloc (ret, siz + 2)))
819 				break;
820 		}
821 		if (*str == '\\') {
822 			++str;
823 			if (*str) {
824 				switch (*str) {
825 				case 'a':	ret[len++] = '\a';	break;
826 				case 'b':	ret[len++] = '\b';	break;
827 				case 'f':	ret[len++] = '\f';	break;
828 				case 'l':	ret[len++] = '\012';	break;
829 				case 'n':	ret[len++] = '\n';	break;
830 				case 'r':	ret[len++] = '\r';	break;
831 				case 's':	ret[len++] = ' ';	break;
832 				case 't':	ret[len++] = '\t';	break;
833 				default:	ret[len++] = *str;	break;
834 				}
835 				++str;
836 			}
837 		} else if (*str == '^') {
838 			++str;
839 			if (*str) {
840 				if (*str == '?')
841 					ret[len++] = '\x7f';
842 				else
843 					ret[len++] = *str & 0x1f;
844 				++str;
845 			}
846 		} else if (*str == '%') {
847 			++str;
848 			idx = 0;
849 			while (isdigit (*str)) {
850 				idx *= 10;
851 				idx += tonum (*str++);
852 			}
853 			if ((idx >= 0) && (idx < ocnt)) {
854 				olen = strlen (opts[idx]);
855 				if (len + olen >= siz) {
856 					siz = len + olen + 64;
857 					if (! (ret = Realloc (ret, siz + 2)))
858 						break;
859 				}
860 				strcpy (ret + len, opts[idx]);
861 				len += olen;
862 			}
863 		} else if ((*str == '\'') || (*str == '"'))
864 			++str;
865 		else
866 			ret[len++] = *str++;
867 	}
868 	if (ret)
869 		ret[len] = '\0';
870 	return ret;
871 }
872 
873 static int
handle_command(void * sp,char * str)874 handle_command (void *sp, char *str)
875 {
876 	serial	*s = (serial *) sp;
877 	char	*p1;
878 	int	mult;
879 	int	ret;
880 
881 	ret = 0;
882 	V (2, ("[Cmd"));
883 	for (p1 = str; *p1; ) {
884 		if (isdigit (*p1)) {
885 			mult = 0;
886 			while (isdigit (*p1)) {
887 				mult *= 10;
888 				mult += tonum (*p1++);
889 			}
890 		} else
891 			mult = 1;
892 		if (*p1)
893 			switch (*p1++) {
894 			case 'd':
895 				V (2, (" Dzz %d", mult));
896 				sleep (mult);
897 				break;
898 			case 'D':
899 				V (2, (" Mdzz %d", mult));
900 				msleep (mult);
901 				break;
902 			case 'b':
903 				if (s && (s -> fd != -1)) {
904 					V (2, (" Brk %d", mult));
905 					tcsendbreak (s -> fd, mult);
906 				}
907 				break;
908 			case 'h':
909 				if (s && (s -> fd != -1)) {
910 					V (2, (" Hup"));
911 					tty_hangup (sp, mult * 500);
912 				}
913 				break;
914 			case 'o':
915 				if (s && (s -> fd != -1)) {
916 					V (2, (" Drain"));
917 					tcdrain (s -> fd);
918 				}
919 				break;
920 			case '<':
921 				if (s && (s -> fd != -1)) {
922 					V (2, (" Iflush"));
923 					tcflush (s -> fd, TCIFLUSH);
924 				}
925 				break;
926 			case '>':
927 				if (s && (s -> fd != -1)) {
928 					V (2, (" Oflush"));
929 					tcflush (s -> fd, TCOFLUSH);
930 				}
931 				break;
932 			case 'f':
933 				V (2, (" fail"));
934 				ret = -1;
935 				break;
936 			case 's':
937 				V (2, (" success"));
938 				ret = 1;
939 				break;
940 			}
941 		}
942 	V (2, ("]\n"));
943 	return ret;
944 }
945 
946 static int
expect_expr(void * sp,int deftout,char * line)947 expect_expr (void *sp, int deftout, char *line)
948 {
949 	int	ret;
950 	char	**ex;
951 	int	cnt, siz;
952 	int	n, m, tout, slen;
953 	char	*ptr;
954 	char	*p1, *p2;
955 	char	**list;
956 	int	*len;
957 
958 	MCHK ((serial *) sp);
959 	ex = NULL;
960 	cnt = 0;
961 	siz = 0;
962 	for (ptr = line; *ptr; ) {
963 		if (cnt >= siz) {
964 			siz += 4;
965 			if (! (ex = (char **) Realloc (ex, (siz + 1) * sizeof (char *))))
966 				break;
967 		}
968 		ex[cnt++] = ptr;
969 		while (*ptr && (*ptr != '-'))
970 			++ptr;
971 		if (*ptr)
972 			*ptr++ = '\0';
973 	}
974 	if (! ex)
975 		return -1;
976 	ret = 0;
977 	for (n = 0; n < cnt; ++n) {
978 		ptr = ex[n];
979 		if (isdigit (*ptr)) {
980 			tout = 0;
981 			while (isdigit (*ptr)) {
982 				tout *= 10;
983 				tout += tonum (*ptr++);
984 			}
985 		} else
986 			tout = deftout;
987 		p1 = ptr;
988 		for (siz = 1, p2 = p1; p2; p2 = strchr (p2, '|'))
989 			++siz, ++p2;
990 		if ((list = (char **) malloc ((siz + 1) * sizeof (char *))) &&
991 		    (len = (int *) malloc ((siz + 1) * sizeof (int)))) {
992 			for (n = 0, p1 = ptr; p1; ++n) {
993 				p2 = p1;
994 				if (p1 = strchr (p1, '|'))
995 					*p1++ = '\0';
996 				list[n] = p2;
997 				len[n] = strlen (p2);
998 			}
999 			list[n] = NULL;
1000 			len[n] = 0;
1001 			if (tty_expect_list (sp, tout, list, len) != 1)
1002 				ret = -1;
1003 			free (list);
1004 			free (len);
1005 		} else {
1006 			ret = -1;
1007 			break;
1008 		}
1009 		if ((ret < 0) && (n + 1 < cnt)) {
1010 			++n;
1011 			ptr = ex[n];
1012 			ret = 0;
1013 			if (*ptr == '!') {
1014 				if (m = handle_command (sp, ptr + 1)) {
1015 					if (m < 0)
1016 						ret = -1;
1017 					break;
1018 				}
1019 			} else {
1020 				slen = strlen (ptr);
1021 				if (tty_send (sp, ptr, slen) != slen) {
1022 					ret = -1;
1023 					break;
1024 				}
1025 			}
1026 		} else
1027 			n = cnt;
1028 	}
1029 	if (ex)
1030 		free (ex);
1031 	return ret;
1032 }
1033 
1034 int
tty_send_expect(void * sp,int deftout,char * str,char ** opts)1035 tty_send_expect (void *sp, int deftout, char *str, char **opts)
1036 {
1037 	serial	*s = (serial *) sp;
1038 	int	ret;
1039 	int	ocnt;
1040 	char	*sav, *ptr, *line;
1041 	int	n;
1042 	char	quote;
1043 	Bool	esc;
1044 
1045 	MCHK (s);
1046 	ret = -1;
1047 	if (opts)
1048 		for (ocnt = 0; opts[ocnt]; ++ocnt)
1049 			;
1050 	else
1051 		ocnt = 0;
1052 	if (str = strdup (str)) {
1053 		ret = 0;
1054 		for (ptr = str; *ptr && (! ret); ) {
1055 			sav = ptr;
1056 			esc = False;
1057 			quote = '\0';
1058 			while (*ptr) {
1059 				if (esc)
1060 					esc = False;
1061 				else if (*ptr == '\\')
1062 					esc = True;
1063 				else if (quote) {
1064 					if (*ptr == quote)
1065 						quote = '\0';
1066 				} else if ((*ptr == '\'') || (*ptr == '"'))
1067 					quote = *ptr;
1068 				else if (isspace (*ptr))
1069 					break;
1070 				++ptr;
1071 			}
1072 			if (*ptr) {
1073 				*ptr++ = '\0';
1074 				while (isspace (*ptr))
1075 					++ptr;
1076 			}
1077 			if (line = expand (sav, opts, ocnt)) {
1078 				if (line[0] == '<') {
1079 					if (expect_expr (sp, deftout, line + 1) < 0)
1080 						ret = -1;
1081 				} else if (line[0] == '!') {
1082 					if ((n = handle_command (sp, line + 1)) < 0)
1083 						ret = -1;
1084 					else if (n > 0)
1085 						while (*ptr)
1086 							++ptr;
1087 				} else {
1088 					n = strlen (line);
1089 					if (tty_send (sp, line, n) != n)
1090 						ret = -1;
1091 				}
1092 				free (line);
1093 			} else
1094 				ret = -1;
1095 		}
1096 		free (str);
1097 	}
1098 	return ret;
1099 }
1100 /*}}}*/
1101 /*{{{	draining */
1102 void
tty_mdrain(void * sp,int msecs)1103 tty_mdrain (void *sp, int msecs)
1104 {
1105 	serial	*s = (serial *) sp;
1106 	int	n, m;
1107 	char	ch;
1108 
1109 	MCHK (s);
1110 	if (s && (s -> fd != -1)) {
1111 		V (2, ("[Drain] "));
1112 		if (msecs < 0)
1113 			msecs = 0;
1114 		do {
1115 			if ((n = data_ready (s -> fd, & msecs)) > 0) {
1116 				while ((m = read (s -> fd, & ch, 1)) == 1) {
1117 					addline (s, ch);
1118 					V (2, ("%s", mkprint (& ch, 1)));
1119 				}
1120 				if (m < 0)
1121 					if (errno == EIO)
1122 						tty_reopen (s, 0);
1123 			}
1124 		}	while (n != 0);
1125 		V (2, ("\n"));
1126 	}
1127 }
1128 
1129 void
tty_drain(void * sp,int secs)1130 tty_drain (void *sp, int secs)
1131 {
1132 	tty_mdrain (sp, secs * 1000);
1133 }
1134 /*}}}*/
1135