/* -*- mode: c; mode: fold -*- */ /*{{{ includes */ #include # include "config.h" # include # include # include # include # include # include # include # include // include # include # include // include # include # include // include # include # include # define major(xx) (((xx) >> 8) & 0xff) # define minor(xx) ((xx) & 0xff) # include "pager.h" /*}}}*/ /*{{{ statics and typedefs */ static char *lckmth[] = { "ascii", "binary", "lower", "upper", "sysv4", "timeout", NULL }; typedef struct { # ifndef NDEBUG # define MAGIC MKMAGIC ('t', 't', 'y', '\0') long magic; # endif /* NDEBUG */ char *lck; char *device; //struct termios tty, sav; HANDLE fd; string_t *line; char *sep; void (*callback) (void *, string_t *, char_t, void *); void *data; Bool suspend; } serial; typedef struct _expect { int idx; char *str; int pos; int len; struct _expect *next; } expect; /*}}}*/ /*{{{ support routines */ static char * mkprint (char *str, int len) { static char *buf = NULL; static int size = 0; int n; char ch; char extra; char *ptr; if (len >= size) { size = len + 128; if (! (buf = Realloc (buf, size + 2))) return NULL; } extra = '\0'; for (n = 0; len > 0; ++str, --len) { if (n + 8 >= size) { size += 128; if (! (buf = Realloc (buf, size + 2))) break; } ch = *str; if (ch & 0x80) { buf[n++] = '<'; buf[n++] = 'M'; buf[n++] = '-'; ch &= 0x7f; extra = '>'; } switch (ch) { case '\x00': ptr = ""; break; case '\x01': ptr = ""; break; case '\x02': ptr = ""; break; case '\x03': ptr = ""; break; case '\x04': ptr = ""; break; case '\x05': ptr = ""; break; case '\x06': ptr = ""; break; case '\x07': ptr = ""; break; case '\x08': ptr = ""; break; case '\x09': ptr = ""; break; case '\x0a': ptr = ""; break; case '\x0b': ptr = ""; break; case '\x0c': ptr = ""; break; case '\x0d': ptr = ""; break; case '\x0e': ptr = ""; break; case '\x0f': ptr = ""; break; case '\x10': ptr = ""; break; case '\x11': ptr = ""; break; case '\x12': ptr = ""; break; case '\x13': ptr = ""; break; case '\x14': ptr = ""; break; case '\x15': ptr = ""; break; case '\x16': ptr = ""; break; case '\x17': ptr = ""; break; case '\x18': ptr = ""; break; case '\x19': ptr = ""; break; case '\x1a': ptr = ""; break; case '\x1b': ptr = ""; break; case '\x1c': ptr = ""; break; case '\x1d': ptr = ""; break; case '\x1e': ptr = ""; break; case '\x1f': ptr = ""; break; case '\x7f': ptr = ""; break; default: ptr = NULL; buf[n++] = ch; break; } if (ptr) while (*ptr) buf[n++] = *ptr++; if (extra) { buf[n++] = extra; extra = '\0'; } } if (buf) buf[n] = '\0'; return buf; } static inline void msleep (int msec) { struct timeval tv; if (msec > 0) { do { tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; errno = 0; } while ((select (0, NULL, NULL, NULL, & tv) < 0) && (errno == EINTR)); } } static inline int data_ready (HANDLE fd, int msec) { COMMTIMEOUTS m_CommTimeouts; GetCommTimeouts (fd, &m_CommTimeouts); m_CommTimeouts.ReadIntervalTimeout = msec; m_CommTimeouts.ReadTotalTimeoutConstant =msec; m_CommTimeouts.ReadTotalTimeoutMultiplier =1; m_CommTimeouts.WriteTotalTimeoutConstant = msec; m_CommTimeouts.WriteTotalTimeoutMultiplier = 1; SetCommTimeouts (fd, &m_CommTimeouts); return 1; /* int ret = 0; struct timeval tv; fd_set fset; FD_ZERO (& fset); FD_SET (fd, & fset); tv.tv_sec = *msec / 1000; tv.tv_usec = (*msec % 1000) * 1000; if (((ret = select (fd + 1, & fset, NULL, NULL, & tv)) > 0) && FD_ISSET (fd, & fset)) { *msec = tv.tv_sec * 1000 + (tv.tv_usec / 1000); return 1; } return ret;*/ } static Bool do_lock (serial *s, char *dev, char *prefix, char *method) { return True; } static void do_unlock (serial *s) { } /*}}}*/ /*{{{ open/close/reopen */ void * tty_open (char *dev, char *lckprefix, char *lckmethod) { serial *s; if (s = (serial *) malloc (sizeof (serial))) { # ifndef NDEBUG s -> magic = MAGIC; # endif /* NDEBUG */ if (do_lock (s, dev, lckprefix, lckmethod)) { s -> fd = CreateFile (dev, GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0); if (1) { if ((s->fd == INVALID_HANDLE_VALUE) || (! (s -> device = strdup (dev)))) { CloseHandle(s -> fd); do_unlock (s); free (s); s = NULL; } else { s -> line = NULL; s -> sep = NULL; s -> callback = NULL; s -> data = NULL; s -> suspend = False; { COMMTIMEOUTS m_CommTimeouts; GetCommTimeouts (s->fd, &m_CommTimeouts); m_CommTimeouts.ReadIntervalTimeout = 1000; m_CommTimeouts.ReadTotalTimeoutConstant =1000; m_CommTimeouts.ReadTotalTimeoutMultiplier =1; m_CommTimeouts.WriteTotalTimeoutConstant = 1000; m_CommTimeouts.WriteTotalTimeoutMultiplier = 1; SetCommTimeouts (s->fd, &m_CommTimeouts); } } } else { do_unlock (s); free (s); s = NULL; } } else { free (s); s = NULL; } } return s; } void * tty_close (void *sp) { serial *s = (serial *) sp; MCHK (s); if (s) { if (s -> fd != INVALID_HANDLE_VALUE) { CloseHandle(s -> fd); } do_unlock (s); if (s -> device) free (s -> device); if (s -> sep) free (s -> sep); if (s -> line) sfree (s -> line); free (s); } return NULL; } Bool tty_reopen (void *sp, int msec) { serial *s = (serial *) sp; MCHK (s); if (s -> fd != INVALID_HANDLE_VALUE) { CloseHandle(s -> fd); if (msec > 0) msleep (msec); s -> fd = INVALID_HANDLE_VALUE; } return s -> fd != INVALID_HANDLE_VALUE ? True : False; } /*}}}*/ /*{{{ hangup, get fd */ void tty_hangup (void *sp, int msec) { serial *s = (serial *) sp; MCHK (s); V (2, ("[Hangup] ")); if (s && (s -> fd != INVALID_HANDLE_VALUE)) { // tty_reopen (s, msec); } V (2, ("\n")); } int tty_fd (void *sp) { serial *s = (serial *) sp; MCHK (s); return s ? (int)(s->fd) : -1; } /*}}}*/ /*{{{ configuration */ int tty_setup (void *sp, Bool raw, Bool modem, int speed, int bpb, int sb, char par) { serial *s = (serial *) sp; MCHK (s); if ((! s) || (s -> fd < 0)) return -1; return 0; } /*}}}*/ /*{{{ callback */ void tty_set_line_callback (void *sp, void (*func) (void *, string_t *, char_t, void *), char *sep, void *data) { serial *s = (serial *) sp; MCHK (s); if (s) { if (! (s -> callback = func)) { if (s -> line) s -> line = sfree (s -> line); if (s -> sep) { free (s -> sep); s -> sep = NULL; } s -> data = NULL; } else { if (s -> sep) free (s -> sep); s -> sep = sep ? strdup (sep) : NULL; s -> data = data; } s -> suspend = False; } } void tty_suspend_callback (void *sp, Bool susp) { serial *s = (serial *) sp; MCHK (s); if (s) if (s -> suspend = susp) if (s -> line) s -> line -> len = 0; } /*}}}*/ /*{{{ sending */ int tty_send (void *sp, char *str, int len) { serial *s = (serial *) sp; int n, sent; MCHK (s); if ((! s) || (s -> fd < 0) || (! str)) return -1; V (2, ("[Send] %s", mkprint (str, len))); sent = 0; while (len > 0) { if (WriteFile(s -> fd, str, len,&n,0) && n > 0) { str += n; sent += n; len -= n; } else if (! n) break; else if (errno == EIO) { if (! tty_reopen (s, 0)) break; } else if ((errno == EAGAIN) ) msleep (200); else if (errno != EINTR) break; } V (2, ("\n")); return sent; } int tty_send_string (void *sp, char *str) { return str ? tty_send (sp, str, strlen (str)) : -1; } /*}}}*/ /*{{{ expecting */ static void addline (serial *s, char ch) { if (s -> callback && s -> sep && (! s -> suspend) && ch) { if (! s -> line) s -> line = snew (NULL, 32); else if (s -> line -> len + 1 >= s -> line -> size) if (! sexpand (s -> line, s -> line -> size * 2)) return; if (strchr (s -> sep, ch)) { (*s -> callback) ((void *) s, s -> line, (char_t) ch, s -> data); s -> line -> len = 0; } else s -> line -> str[s -> line -> len++] = (char_t) ch; } } static int do_expect (serial *s, int tout, expect *base) { int ret; int msec; int n; expect *run; char ch; long w=1; if ((! s) || (s -> fd < 0)) return -1; V (2, ("[Expect] ")); for (run = base; run; run = run -> next) run -> pos = 0; msec = (tout > 0) ? tout * 1000 : 0; ret = 0; data_ready (s -> fd, msec); while (! ret && w==1) while (ReadFile(s -> fd, & ch, 1,&w,0) && w == 1) { addline (s, ch); V (3, ("%s", mkprint (& ch, 1))); for (run = base; run; run = run -> next) if (run -> str[run -> pos] == ch) { run -> pos++; if (run -> pos == run -> len) { ret = run -> idx; break; } } else run -> pos = 0; if (ret) break; } if (run) printf (" got %s", mkprint (run -> str, run -> len)); else printf (" timeout %d",msec); V (2, ("\n")); return ret; } int tty_expect (void *sp, int tout, ...) { va_list par; int ret; char *ptr; expect *base, *prev, *tmp; int idx; va_start (par, tout); MCHK ((serial *) sp); ret = 0; base = NULL; prev = NULL; idx = 1; while (ptr = va_arg (par, char *)) if (tmp = (expect *) malloc (sizeof (expect))) { tmp -> idx = idx++; tmp -> str = ptr; tmp -> pos = 0; tmp -> len = va_arg (par, int); tmp -> next = NULL; if (prev) prev -> next = tmp; else base = tmp; prev = tmp; } else break; if (! ptr) ret = do_expect ((serial *) sp, tout, base); else ret = -1; while (base) { tmp = base; base = base -> next; free (tmp); } va_end (par); return ret; } int tty_expect_list (void *sp, int tout, char **strs, int *lens) { int n; int ret; expect *base, *prev, *tmp; MCHK ((serial *) sp); base = NULL; prev = NULL; for (n = 0; strs[n]; ++n) if (tmp = (expect *) malloc (sizeof (expect))) { tmp -> idx = n + 1; tmp -> str = strs[n]; tmp -> pos = 0; tmp -> len = lens[n]; tmp -> next = NULL; if (prev) prev -> next = tmp; else base = tmp; prev = tmp; } else break; if (strs[n]) ret = -1; else ret = do_expect ((serial *) sp, tout, base); while (base) { tmp = base; base = base -> next; free (tmp); } return ret; } int tty_expect_string (void *sp, int tout, char *str) { expect tmp; MCHK ((serial *) sp); if (! str) return -1; tmp.idx = 1; tmp.str = str; tmp.pos = 0; tmp.len = strlen (str); tmp.next = NULL; return do_expect ((serial *) sp, tout, & tmp); } /*}}}*/ /*{{{ send/expect */ static int tonum (char ch) { switch (ch) { default: case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; } } static char * expand (char *str, char **opts, int ocnt) { char *ret; int len, siz; int idx, olen; ret = NULL; len = 0; siz = 0; while (*str) { if (len >= siz) { siz += (siz ? siz : 32); if (! (ret = Realloc (ret, siz + 2))) break; } if (*str == '\\') { ++str; if (*str) { switch (*str) { case 'a': ret[len++] = '\a'; break; case 'b': ret[len++] = '\b'; break; case 'f': ret[len++] = '\f'; break; case 'l': ret[len++] = '\012'; break; case 'n': ret[len++] = '\n'; break; case 'r': ret[len++] = '\r'; break; case 's': ret[len++] = ' '; break; case 't': ret[len++] = '\t'; break; default: ret[len++] = *str; break; } ++str; } } else if (*str == '^') { ++str; if (*str) { if (*str == '?') ret[len++] = '\x7f'; else ret[len++] = *str & 0x1f; ++str; } } else if (*str == '%') { ++str; idx = 0; while (isdigit (*str)) { idx *= 10; idx += tonum (*str++); } if ((idx >= 0) && (idx < ocnt)) { olen = strlen (opts[idx]); if (len + olen >= siz) { siz = len + olen + 64; if (! (ret = Realloc (ret, siz + 2))) break; } strcpy (ret + len, opts[idx]); len += olen; } } else if ((*str == '\'') || (*str == '"')) ++str; else ret[len++] = *str++; } if (ret) ret[len] = '\0'; return ret; } static int handle_command (void *sp, char *str) { serial *s = (serial *) sp; char *p1; int mult; int ret; ret = 0; V (2, ("[Cmd")); for (p1 = str; *p1; ) { if (isdigit (*p1)) { mult = 0; while (isdigit (*p1)) { mult *= 10; mult += tonum (*p1++); } } else mult = 1; if (*p1) switch (*p1++) { case 'd': V (2, (" Dzz %d", mult)); Sleep (mult); break; case 'D': V (2, (" Mdzz %d", mult)); msleep (mult); break; case 'b': if (s && (s -> fd != INVALID_HANDLE_VALUE)) { V (2, (" Brk %d", mult)); // tcsendbreak (s -> fd, mult); } break; case 'h': if (s && (s -> fd != INVALID_HANDLE_VALUE)) { V (2, (" Hup")); tty_hangup (sp, mult * 500); } break; case 'o': if (s && (s -> fd != INVALID_HANDLE_VALUE)) { V (2, (" Drain")); // tcdrain (s -> fd); } break; case '<': if (s && (s -> fd != INVALID_HANDLE_VALUE)) { V (2, (" Iflush")); // tcflush (s -> fd, TCIFLUSH); } break; case '>': if (s && (s -> fd != INVALID_HANDLE_VALUE)) { V (2, (" Oflush")); // tcflush (s -> fd, TCOFLUSH); } break; case 'f': V (2, (" fail")); ret = -1; break; case 's': V (2, (" success")); ret = 1; break; } } V (2, ("]\n")); return ret; } static int expect_expr (void *sp, int deftout, char *line) { int ret; char **ex; int cnt, siz; int n, m, tout, slen; char *ptr; char *p1, *p2; char **list; int *len; MCHK ((serial *) sp); ex = NULL; cnt = 0; siz = 0; for (ptr = line; *ptr; ) { if (cnt >= siz) { siz += 4; if (! (ex = (char **) Realloc (ex, (siz + 1) * sizeof (char *)))) break; } ex[cnt++] = ptr; while (*ptr && (*ptr != '-')) ++ptr; if (*ptr) *ptr++ = '\0'; } if (! ex) return -1; ret = 0; for (n = 0; n < cnt; ++n) { ptr = ex[n]; if (isdigit (*ptr)) { tout = 0; while (isdigit (*ptr)) { tout *= 10; tout += tonum (*ptr++); } } else tout = deftout; p1 = ptr; for (siz = 1, p2 = p1; p2; p2 = strchr (p2, '|')) ++siz, ++p2; if ((list = (char **) malloc ((siz + 1) * sizeof (char *))) && (len = (int *) malloc ((siz + 1) * sizeof (int)))) { for (n = 0, p1 = ptr; p1; ++n) { p2 = p1; if (p1 = strchr (p1, '|')) *p1++ = '\0'; list[n] = p2; len[n] = strlen (p2); } list[n] = NULL; len[n] = 0; if (tty_expect_list (sp, tout, list, len) != 1) ret = -1; free (list); free (len); } else { ret = -1; break; } if ((ret < 0) && (n + 1 < cnt)) { ++n; ptr = ex[n]; ret = 0; if (*ptr == '!') { if (m = handle_command (sp, ptr + 1)) { if (m < 0) ret = -1; break; } } else { slen = strlen (ptr); if (tty_send (sp, ptr, slen) != slen) { ret = -1; break; } } } else n = cnt; } if (ex) free (ex); return ret; } int tty_send_expect (void *sp, int deftout, char *str, char **opts) { serial *s = (serial *) sp; int ret; int ocnt; char *sav, *ptr, *line; int n; char quote; Bool esc; MCHK (s); ret = -1; if (opts) for (ocnt = 0; opts[ocnt]; ++ocnt) ; else ocnt = 0; if (str = strdup (str)) { ret = 0; for (ptr = str; *ptr && (! ret); ) { sav = ptr; esc = False; quote = '\0'; while (*ptr) { if (esc) esc = False; else if (*ptr == '\\') esc = True; else if (quote) { if (*ptr == quote) quote = '\0'; } else if ((*ptr == '\'') || (*ptr == '"')) quote = *ptr; else if (isspace (*ptr)) break; ++ptr; } if (*ptr) { *ptr++ = '\0'; while (isspace (*ptr)) ++ptr; } if (line = expand (sav, opts, ocnt)) { if (line[0] == '<') { if (expect_expr (sp, deftout, line + 1) < 0) ret = -1; } else if (line[0] == '!') { if ((n = handle_command (sp, line + 1)) < 0) ret = -1; else if (n > 0) while (*ptr) ++ptr; } else { n = strlen (line); if (tty_send (sp, line, n) != n) ret = -1; } free (line); } else ret = -1; } free (str); } return ret; } /*}}}*/ /*{{{ draining */ void tty_mdrain (void *sp, int msecs) { serial *s = (serial *) sp; int n; char ch; MCHK (s); if (s && (s -> fd != INVALID_HANDLE_VALUE)) { long w; V (2, ("[Drain] ")); if (msecs < 0) msecs = 0; data_ready (s -> fd, msecs); do { while (ReadFile(s -> fd, & ch, 1,&w,0) && w==1) { addline (s, ch); V (2, ("%s", mkprint (& ch, 1))); } } while (w != 0); V (2, ("\n")); } } void tty_drain (void *sp, int secs) { tty_mdrain (sp, secs * 1000); } /*}}}*/