ttsl_drv.c

来自「OTP是开放电信平台的简称」· C语言 代码 · 共 743 行 · 第 1/2 页

C
743
字号
/* ``The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved via the world wide web at http://www.erlang.org/. *  * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. *  * The Initial Developer of the Original Code is Ericsson Utvecklings AB. * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings * AB. All Rights Reserved.'' *  *     $Id$ *//* * Tty driver that reads one character at the time and provides a * smart line for output. */#ifdef HAVE_CONFIG_H#  include "config.h"#endif#include "sys.h"#include <ctype.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <signal.h>#include <fcntl.h>#include <locale.h>#include <unistd.h>#include <termios.h>#include "erl_driver.h"#define TRUE 1#define FALSE 0/* Termcap functions. */int tgetent(char* bp, char *name);int tgetnum(char* cap);int tgetflag(char* cap);char *tgetstr(char* cap, char** buf);char *tgoto(char* cm, int col, int line);int tputs(char* cp, int affcnt, int (*outc)(int c));/* Terminal capabilites in which we are interested. */static char *capbuf;static char *up, *down, *left, *right;static int cols, xn;/* The various opcodes. */#define OP_PUTC 0#define OP_MOVE 1#define OP_INSC 2#define OP_DELC 3#define OP_BEEP 4static int lbuf_size = BUFSIZ;#define MAXSIZE (1 << 16)static byte *lbuf;		/* The current line buffer */static int llen;		/* The current line length */static byte *lc;		/* The current character pointer */static int lpos;#define COL(_l) ((_l) % cols)#define LINE(_l) ((_l) / cols)#define PAD '\n'#define NL '\n'/* Main interface functions. */static int ttysl_init(void);static ErlDrvData ttysl_start(ErlDrvPort, char*);static void ttysl_stop(ErlDrvData);static void ttysl_from_erlang(ErlDrvData, char*, int);static void ttysl_from_tty(ErlDrvData, ErlDrvEvent);static Sint16 get_sint16(char*);static ErlDrvPort ttysl_port;static int ttysl_fd;static FILE *ttysl_out;/* Functions that work on the line buffer. */static int start_lbuf(void);static int stop_lbuf(void);static int put_chars(byte*,int);static int move_rel(int);static int ins_chars(byte*,int);static int del_chars(int);static byte *step_over_chars(int);static int insert_buf(byte*,int);static int write_buf(byte*,int);static int outc(int c);static int move_cursor(int,int);/* Termcap functions. */static int start_termcap(void);static int stop_termcap(void);static int move_left(int);static int move_right(int);static int move_up(int);static int move_down(int);/* Terminal setting functions. */static int tty_init(int,int,int,int);static int tty_set(int);static int tty_reset(int);static RETSIGTYPE suspend(int);static RETSIGTYPE cont(int);/* Define the driver table entry. */struct erl_drv_entry ttsl_driver_entry = {    ttysl_init,    ttysl_start,    ttysl_stop,    ttysl_from_erlang,    ttysl_from_tty,    NULL,    "tty_sl"  };static int ttysl_init(void){    ttysl_port = (ErlDrvPort)-1;    ttysl_fd = -1;    lbuf = NULL;		/* For line buffer handling */    capbuf = NULL;		/* For termcap handling */    return TRUE;}static ErlDrvData ttysl_start(ErlDrvPort port, char* buf){    char *s, *t, c;    int canon, echo, sig;	/* Terminal characteristics */    int flag;    extern int using_oldshell; /* set this to let the rest of erts know */    if (ttysl_port != (ErlDrvPort)-1)      return ERL_DRV_ERROR_GENERAL;    if (!isatty(0) || !isatty(1))	return ERL_DRV_ERROR_GENERAL;    /* Set the terminal modes to default leave as is. */    canon = echo = sig = 0;    /* Parse the input parameters. */    for (s = strchr(buf, ' '); s; s = t) {	s++;	/* Find end of this argument (start of next) and insert NUL. */	if ((t = strchr(s, ' '))) {	    c = *t;	    *t = '\0';	}	if ((flag = ((*s == '+') ? 1 : ((*s == '-') ? -1 : 0)))) {	    if (s[1] == 'c') canon = flag;	    if (s[1] == 'e') echo = flag;	    if (s[1] == 's') sig = flag;	}	else if ((ttysl_fd = open(s, O_RDWR, 0)) < 0)	      return ERL_DRV_ERROR_GENERAL;    }    if (ttysl_fd < 0)      ttysl_fd = 0;    if (tty_init(ttysl_fd, canon, echo, sig) < 0 ||	tty_set(ttysl_fd) < 0) {	ttysl_port = (ErlDrvPort)-1;	tty_reset(ttysl_fd);	return ERL_DRV_ERROR_GENERAL;    }    /* Set up smart line and termcap stuff. */    if (!start_lbuf() || !start_termcap()) {	stop_lbuf();		/* Must free this */	tty_reset(ttysl_fd);	return ERL_DRV_ERROR_GENERAL;    }    /* Open the terminal and set the terminal */    ttysl_out = fdopen(ttysl_fd, "w");    setlocale(LC_CTYPE, "");	/* Set international environment */    sys_sigset(SIGCONT, cont);    driver_select(port, (ErlDrvEvent)(Uint)ttysl_fd, DO_READ, 1);    ttysl_port = port;    /* we need to know this when we enter the break handler */    using_oldshell = 0;    return (ErlDrvData)ttysl_port;	/* Nothing important to return */}static void ttysl_stop(ErlDrvData ttysl_data){    if (ttysl_port != (ErlDrvPort)-1) {	stop_lbuf();	stop_termcap();	tty_reset(ttysl_fd);	if (ttysl_fd != 0)	  close(ttysl_fd);	driver_select(ttysl_port, (ErlDrvEvent)(Uint)ttysl_fd, DO_READ, 0);	sys_sigset(SIGCONT, SIG_DFL);    }    ttysl_port = (ErlDrvPort)-1;    ttysl_fd = -1;    /* return TRUE; */}/* * Check that there is enough room in all buffers to copy all pad chars * and stiff we need If not, realloc lbuf. */static int check_buf_size(byte *s, int n){    int size;    byte *tmp, *old_lbuf;    size = 10;    for (tmp = s; n > 0; --n, tmp++) {	if (isprint(*tmp)) 	    size++;	else if (*tmp == '\t') 	    size += 8;	else if (*tmp >= 128) 	    size += 4;	else size += 2;    }    if (size + lpos >= lbuf_size) {	lbuf_size = size + lpos + BUFSIZ;	old_lbuf = lbuf;	if ((lbuf = driver_realloc(lbuf, lbuf_size)) == NULL) {	    driver_failure(ttysl_port, -1);	    return(0);	}	lc = lbuf + (lc - old_lbuf);    }    return(1);}static void ttysl_from_erlang(ErlDrvData ttysl_data, char* buf, int count){    if (lpos > MAXSIZE) 	put_chars((byte*)"\n", 1);    if (check_buf_size((byte*)buf+1, count-1) == 0)	return; /*(-1); */    switch (buf[0]) {    case OP_PUTC:	put_chars((byte*)buf+1, count-1);	break;    case OP_MOVE:	move_rel(get_sint16(buf+1));	break;    case OP_INSC:	ins_chars((byte*)buf+1, count-1);	break;    case OP_DELC:	del_chars(get_sint16(buf+1));	break;    case OP_BEEP:	outc('\007');	break;    default:	/* Unknown op, just ignore. */	break;    }    fflush(ttysl_out);    return; /* TRUE; */}static void ttysl_from_tty(ErlDrvData ttysl_data, ErlDrvEvent fd){    char b[1024];    ssize_t i;    if ((i = read((int)(Sint)fd, b, 1024)) >= 0)      driver_output(ttysl_port, b, i);    else      driver_failure(ttysl_port, -1);    /* return TRUE;*/}/* Procedures for putting and getting integers to/from strings. */static Sint16 get_sint16(char *s){    return ((*s << 8) | ((byte*)s)[1]);}static int start_lbuf(void){    if (!lbuf && !(lbuf = (byte*) driver_alloc(lbuf_size)))      return FALSE;    llen = 0;    lc = lbuf;    lpos = 0;    return TRUE;}static int stop_lbuf(void){    if (lbuf) driver_free(lbuf);    lbuf = NULL;    return TRUE;}/* Put l characters from s into the buffer and output them. */static int put_chars(byte *s, int l){    int n;    n = insert_buf(s, l);    if (n > 0)      write_buf(lc - n, n);    if (lpos > llen)      llen = lpos;    return TRUE;}/* * Move the current postition forwards or backwards within the current * line. We know about padding. */static int move_rel(int n){    int npos;			/* The new position */    byte *c;    /* Step forwards or backwards over the buffer. */    c = step_over_chars(n);    /* Calculate move, updates pointers and move the cursor. */    npos = c > lc ? lpos + (c - lc) : lpos - (lc - c);    move_cursor(lpos, npos);    lc = c;    lpos = npos;    return TRUE;}/* Insert characters into the buffer at the current position. */static int ins_chars(byte *s, int l){    int n, tl;    byte *tbuf = NULL;		/* Suppress warning about use-before-set */    /* Move tail of buffer to make space. */    if ((tl = llen - lpos) > 0) {	if ((tbuf = driver_alloc(tl)) == NULL)	    return FALSE;	memcpy(tbuf, lc, tl);    }    n = insert_buf(s, l);    if (tl > 0) {	memcpy(lc, tbuf, tl);	driver_free(tbuf);    }    llen += n;    write_buf(lc - n, llen - lpos + n);    move_cursor(llen, lpos);    return TRUE;}/* * Delete characters in the buffer. Can delete characters before (n < 0)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?