1/* $XFree86: xc/programs/xtrap/chparse.c,v 1.3tsi Exp $ */
2/*****************************************************************************
3Copyright 1987, 1988, 1989, 1990, 1991 by Digital Equipment Corp., Maynard, MA
4
5Permission to use, copy, modify, and distribute this software and its
6documentation for any purpose and without fee is hereby granted,
7provided that the above copyright notice appear in all copies and that
8both that copyright notice and this permission notice appear in
9supporting documentation, and that the name of Digital not be
10used in advertising or publicity pertaining to distribution of the
11software without specific, written prior permission.
12
13DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
14ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
15DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
16ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
18ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
19SOFTWARE.
20
21*****************************************************************************/
22/*
23**++
24**  FACILITY:  chparse - ASCII ESC & CSI parser
25**
26**  MODULE DESCRIPTION:
27**
28**      This module accepts single character I/O from
29**      stdin and returns a parsed character or sequence
30**      of characters.  This is to be used in conjunction
31**      with passing data from dragon-speak to XTrap.
32**
33**  AUTHORS:
34**
35**      Roy Lomicka (revised by Martin Minow & later Kenneth B. Miller)
36**
37**  CREATION DATE:  March 26, 1991
38**
39**  DESIGN ISSUES:
40**
41**      The algorithm is rather obscure.  However, it was decided
42**      that it should be left in its original condition since it
43**      closely depicts the state transition diagram for ASCII
44**      sequences (about the only good thing goto's are for;) .
45**      The old-fashioned bracketing and tabbing schemed were
46**      also preserved for posterity.
47**
48**--
49*/
50
51#include	<stdio.h>
52#include	<ctype.h>
53#ifdef vms
54#include	<ssdef.h>
55#include	<stsdef.h>
56#include	<iodef.h>
57#include	<descrip.h>
58#else
59#include	<sys/types.h>
60#include	<sys/time.h>
61#endif
62#include	<string.h>
63#include	<unistd.h>
64#include	"chparse.h"
65
66#ifdef __QNX__
67#include <sys/select.h>
68#endif
69
70#ifndef	VERBOSE
71#define	VERBOSE	0
72#endif
73
74#ifndef FALSE
75#define	FALSE	0
76#define	TRUE	1
77#endif
78#define	EOS	'\0'
79#define	ERROR	(-1)
80#define	TIMEOUT	(-2)
81#define	NPARAM	8
82#define	NINTER	8
83#define	BUFFLEN	10			/* Size of typeahead buffer	*/
84#ifndef	EXIT_SUCCESS
85#ifdef vms
86#define	EXIT_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
87#define	EXIT_FAILURE	(SS$_ABORT)
88#else
89#define EXIT_SUCCESS	0
90#define EXIT_FAILURE	1
91#endif
92#endif
93
94#define	NUL	0x00
95#define	CAN	0x18
96#define	SUB	0x1A
97#define	ESC	0x1B
98#define DEL	0x7F
99#define SS3     0x8f
100#define	DCS	0x90
101#define	CSI	0x9B
102#define	ST	0x9C
103#define	OSC	0x9D
104#define	PM	0x9E
105#define	APC	0x9F
106
107static int kbinr(int max_delay);
108
109/*
110 * Escape sequence parser, obscure but useful.
111 */
112int chparse(
113int		max_delay,
114int		rest_delay,
115int		*state,		/* Parser state (n.z. if incomplete)	*/
116int		*private,	/* Sequence private char, 'X' if error	*/
117int		param[],	/* numeric param, starting at param[1]	*/
118int		*nparam,	/* Number of parameters			*/
119int		inter[],	/* intermediate char, starting at [1]	*/
120int		*ninter,	/* Number of intermediates		*/
121int		*final)		/* Sequence terminator			*/
122{
123	register int		c;
124	register int		i;
125
126	if (*state == 0)
127	    *private = 0;
128label1:	c = kbinr(max_delay);
129#if 0
130	printf("c %02x, *state %02x, *nparam %d\n", c, *state, *nparam);
131#endif
132	max_delay = rest_delay;
133label2:	switch (c) {
134	case NUL:
135	case DEL:
136	    goto label5;
137	case ESC:
138	case CSI:
139        case SS3:
140	case DCS:
141	case OSC:
142	case PM:
143	case APC:
144	    *state = c;
145	    *private = 0;
146	    for (i = 0; i < NPARAM; i++)
147		param[i] = 0;
148	    for (i = 0; i < NINTER; i++)
149		inter[i] = EOS;
150	    *nparam = *ninter = 0;
151	    goto label1;
152	}
153	if (*state == 0)
154	    goto label5;
155	if ((c >= 0x80 && c < 0xA0)
156	 || c == TIMEOUT
157	 || c == ERROR
158	 || c == CAN
159	 || c == SUB) {
160	    *state = 0;
161	    goto label5;
162	}
163	if (c < 0x20)				/* Doesn't stop seq.	*/
164	    goto label5;
165	if (c <= 0x2F) {
166	    if (*ninter < 7)
167		inter[++*ninter] = c;
168	    goto label1;
169	}
170	if (*state == ESC) {
171	    if (*ninter == 0
172	     && (c & 0x3F) < 0x20) {
173		c = (c & 0x3F) + 0x80;
174		goto label2;
175	    }
176	    goto label4;
177	}
178	if (c >= 0x40)
179	    goto label3;
180	else if (c >= 0x3C) {			/* Private introducer	*/
181	    if (*nparam != 0)
182		 *private = 'X';
183	    else {
184		*private = c;
185		*nparam = 1;
186	    }
187	    goto label1;
188	}
189	if (*nparam == 0)
190	    *nparam = 1;
191	if (*ninter != 0) {
192	    *ninter = 0;
193	    *private = 'X';
194	}
195	if (c == ';') {
196	    if (*nparam >= (NPARAM - 1))
197		*private = 'X';
198	    else {
199		++*nparam;
200	    }
201	    goto label1;
202	}
203	if (c > '9') {
204	    *private = 'X';
205	    goto label1;
206	}
207	param[*nparam] = (param[*nparam] * 10) + (c - '0');
208	goto label1;
209label3:	if (*nparam == 0)
210	    *nparam = 1;
211label4:	*final = c;
212	c = *state;
213	*state = 0;
214label5:	return (c);
215}
216
217void dumpsequence(
218int            state,
219int		c,
220int		private,	/* Sequence private char, 'X' if error	*/
221int		param[],	/* numeric param, starting at param[1]	*/
222int		nparam,		/* Number of parameters			*/
223int		inter[],	/* intermediate char, starting at [1]	*/
224int		ninter,		/* Number of intermediates		*/
225int		final,		/* Sequence terminator			*/
226short           *column)        /* column display count                 */
227{
228    register int	i;
229
230    if (isascii(c) && isprint(c)) {
231        *column +=2;
232        if (*column >= 79) {
233            printf("\n");
234            *column = 2;
235        }
236        printf("%c ", c);
237    }
238    else if (private == 'X') {
239        *column += strlen("bad sequence ");
240        if (*column >= 79) {
241            printf("\n");
242            *column = strlen("bad sequence ");
243        }
244        printf("bad sequence ");
245    }
246    else if (state != NUL) {
247        *column += 32;
248        if (*column >= 79) {
249            printf("\n");
250            *column = 32;
251        }
252        printf("incomplete sequence (type <%02x>) ", state);
253    }
254    else {
255        *column += 5;    /* update total chars printed */
256        if (*column >= 79) {
257            printf("\n");
258            *column = 6;
259        }
260        switch (c) {
261        case ESC:	printf("<ESC>");	break;
262        case DCS:	printf("<DCS>");	break;
263        case CSI:	printf("<CSI>");	break;
264        case SS3:	printf("<SS3>");	break;
265        default:	printf("<%02x>", c & 0xFF);
266        }
267        if (c == ESC || c == DCS || c == CSI || c == SS3) {
268            *column += 1 + nparam*2 + ninter + 1;    /* total chars printed */
269            if (*column >= 79) {
270                printf("\n");
271                *column = 1 + nparam*2 + ninter + 1;
272            }
273    	    if (private != NUL && private != 'X')
274    	        printf("%c", private);
275            for (i = 1; i <= nparam; i++)
276    	        printf("%s%d", (i > 1) ? "," : " ", param[i]);
277	    for (i = 1; i <= ninter; i++)
278	        printf("%c", inter[i]);
279	    printf("%c", final);
280        }
281        (*column)++;
282        if (*column >= 79) {
283            printf("\n");
284            *column = 1;
285        }
286        printf(" ");
287    }
288}
289
290#ifdef vms
291/*
292 * Read a character from the terminal (with selectable timeout)
293 */
294
295int
296kbinr(timeout)
297int		timeout;		/* In seconds, 0 == immediately	*/
298/*
299 * Get one byte without echoing, if available.  Returns
300 *	ERROR		Something is dwrong
301 *	TIMEOUT		Nothing read within limit.
302 * Note:
303 *	timeout = 0	return immediately if nothing is present
304 *	timeout = 1	Indeterminate, do not use
305 *	timeout = 2	Wait at least one second.
306 */
307{
308	register int	incount;
309	static char	buffer[BUFFLEN];
310	static char	*bufptr = buffer;
311	static char	*bufend = buffer;
312
313	if (bufptr >= bufend) {
314	    bufptr = bufend = buffer;
315	    incount = vmsread(buffer, BUFFLEN, 0);
316	    if (incount == TIMEOUT)
317		incount = vmsread(buffer, 1, timeout);
318	    if (incount <= 0)
319		return (incount);
320	    bufend = &buffer[incount];
321	}
322	return (*bufptr++ & 0xFF);
323}
324
325static int		tt_channel;	/* Gets channel number		*/
326typedef	struct {
327    short int	status;
328    short int	term_offset;
329    short int	terminator;
330    short int	term_size;
331} IOSTAB;
332
333int
334vmsread(buffer, size, timeout)
335char		*buffer;
336int		size;
337int		timeout;
338{
339	register int		status;
340	IOSTAB			iostab;
341	static $DESCRIPTOR(tt_device, "SYS$COMMAND");
342	static long	termset[2] = { 0, 0 };	/* No terminator	*/
343	static short	opened = FALSE;		/* TRUE when opened	*/
344
345	if (!opened) {
346	    status = sys$assign(&tt_device, &tt_channel, 0, 0);
347	    if (status != SS$_NORMAL)
348		lib$stop(status);
349            opened = TRUE;
350	}
351	status = sys$qiow(
352		0,			/* Event flag 			*/
353		tt_channel,		/* Input channel		*/
354		IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED,
355					/* Read, no echo, no translate	*/
356		&iostab,		/* I/O status block		*/
357		NULL,			/* AST block (none)		*/
358		0,			/* AST parameter		*/
359		buffer,			/* P1 - input buffer		*/
360		size,			/* P2 - buffer length		*/
361		timeout,		/* P3 - timeout			*/
362		&termset,		/* P4 - terminator set		*/
363		NULL,			/* P5 - ignored (prompt buffer)	*/
364		0			/* P6 - ignored (prompt size)	*/
365	);
366	if (status == SS$_TIMEOUT)
367	    return (TIMEOUT);
368	else if (status != SS$_NORMAL)
369	    return (ERROR);
370	else {
371	    if ((status = iostab.term_offset + iostab.term_size) > 0)
372		return (status);
373	    return (TIMEOUT);
374	}
375}
376#else
377static int
378kbinr(int max_delay)
379{
380	auto int		fdmask;
381	struct timeval		timeout;
382	int			count;
383	static unsigned char	buffer[80];
384	static unsigned char	*bend;
385	static unsigned char	*bptr;
386
387	if (bptr >= bend) {
388	    fdmask = 1 << fileno(stdin);
389	    timeout.tv_usec = 0;
390	    timeout.tv_sec = max_delay;
391	    count = select(fileno(stdin) + 1, (fd_set *)&fdmask, NULL, NULL, &timeout);
392	    if (count < 0)
393		return (ERROR);
394	    else if (count == 0)
395		return (TIMEOUT);
396	    if (count >= sizeof buffer)
397		count = sizeof buffer;
398	    count = read(fileno(stdin), buffer, count);
399	    if (count <= 0)
400		return (ERROR);
401	    bptr = buffer;
402	    bend = buffer + count;
403	}
404	return (*bptr++);
405}
406#endif
407