Files
IanDarwin.OpenLookCDROM/NeWS/gterm/tcap_parse.c

638 lines
16 KiB
C

/*
* This file is a product of Sun Microsystems, Inc. and is provided for
* unrestricted use provided that this legend is included on all tape
* media and as a part of the software program in whole or part.
* Users may copy, modify or distribute this file at will.
*
* THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
* WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
* PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
*
* This file is provided with no support and without any obligation on the
* part of Sun Microsystems, Inc. to assist in its use, correction,
* modification or enhancement.
*
* SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
* INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS FILE
* OR ANY PART THEREOF.
*
* In no event will Sun Microsystems, Inc. be liable for any lost revenue
* or profits or other special, indirect and consequential damages, even
* if Sun has been advised of the possibility of such damages.
*
* Sun Microsystems, Inc.
* 2550 Garcia Avenue
* Mountain View, California 94043
*
* Modifications to the original Sun Microsystems, Inc. source code
* made by the Grasshopper Group are in the Public Domain.
*
* Extensions to this file by Eric Messick of the Grasshopper Group.
*
* Grasshopper Group
* 212 Clayton St
* San Francisco, CA 94117
*
*/
#ifndef lint
static char sccsid[] =
"@(#)tcap_parse.c 9.5 88/01/19 Copyright 1985 Sun Micro";
static char RCSid[] =
"@(#)$Header: /it/grass/gterm/RCS/tcap_parse.c,v 2.7 1991/04/23 06:52:46 hugh Grass2 $";
#endif
#ifdef GPROF
#define MARK
#include <prof.h>
#else
#define MARK(x)
#endif /* GPROF */
/*#define DEBUG*/
#ifdef DEBUG
#define err0(A) fprintf(stderr,A)
#define err1(A,B) fprintf(stderr,A,B)
#define err2(A,B,C) fprintf(stderr,A,B,C)
#define err3(A,B,C,D) fprintf(stderr,A,B,C,D)
#else
#define err0(A)
#define err1(A,B)
#define err2(A,B,C)
#define err3(A,B,C,D)
#endif
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
/*-
tcap_parse.c: Parse termcap output based on termcap entry.
tcap_parse.c, Mon Mar 24 11:25:44 1986
David Rosenthal,
Sun Microsystems
*/
/* XXX - remember longest outstanding partial match? */
/* XXX - overlapping partial matches? */
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#ifdef REF
#include <ref/config.h>
#endif
#include "termcap.h"
extern char *malloc();
#ifndef bcopy
extern void bcopy();
#endif
static int TerminalIsBraindamaged = 0;
/* Import these from tcap_ops.c */
extern struct tcap T[];
extern int Ts;
extern struct tcap *CheckCR;
extern struct tcap *CheckNL;
extern struct tcap *CheckTAB;
extern struct tcap *CheckBS;
extern int PageFull;
extern int PageMode;
static char *unpad();
static int tc_init_stacks();
/*
* Initialize the display system from the TERMCAP entry.
* We parse the entry and build the tcap structures
* describing the operations supported by this type of
* terminal. These descriptions are then used by tc_display()
* in interpreting the data stream generated by the
* application
*/
int
tc_initialize(term)
char *term;
{
#ifdef SUNTGETENT
static void tc_fix_tcap_ent();
#endif
static char tcapbuf[1024], tcaparea[1024];
char *areap = tcaparea, *s, *ss;
extern int tgetent(), tgetnum(), tgetflag();
extern char *tgetstr(), *getenv();
register struct tcap *tp;
register int retval;
retval = tgetent(tcapbuf, term);
if (retval != 1) { /* not in the /etc/termcap file ? */
/* the Makefile installs a termcap file so we go looking for it... */
if( s=getenv("OPENWINHOME") ) {
ss = (char *)malloc(strlen(s) + sizeof("/etc/termcap.NeWS"));
sprintf(ss, "%s/etc/termcap.NeWS");
set_environment_var("TERMCAP", ss);
free(ss);
retval = tgetent(tcapbuf, term);
}
if( retval != 1 && (s=getenv("NEWSHOME"))) {
ss = (char *)malloc(strlen(s) + sizeof("/lib/termcap.NeWS"));
sprintf(ss, "%s/lib/termcap.NeWS");
set_environment_var("TERMCAP", ss);
free(ss);
retval = tgetent(tcapbuf, term);
}
if( retval != 1) {
set_environment_var("TERMCAP",
"/usr/openwin/etc/termcap.NeWS");
retval = tgetent(tcapbuf, term);
}
if( retval != 1) {
set_environment_var("TERMCAP",
"/usr/NeWS/lib/termcap.NeWS");
retval = tgetent(tcapbuf, term);
}
if( retval != 1) {
set_environment_var("TERMCAP",
"/usr/openwin/etc/termcap.psterm");
retval = tgetent(tcapbuf, term);
}
if( retval != 1) {
set_environment_var("TERMCAP", "/usr/local/etc/termcap");
retval = tgetent(tcapbuf, term);
}
if( retval != 1) { /* Then we could not find the right termcap anywyere! */
fprintf(stderr,
"gterm: tgetent failed, looking for the %s termcap entry.\n", term);
return (1);
}
}
#ifdef SUNTGETENT
tc_fix_tcap_ent(tcapbuf);
#endif
set_environment_var("TERMCAP", tcapbuf);
for (tp = T; tp < T+Ts; tp++) {
switch (tp->t_type) {
case string:
tp->t_text = unpad(tgetstr(tp->t_key, &areap));
if (tp->t_text == NULL)
tp->t_text = tp->t_deftext;
if (tp->t_text) {
tp->t_size = strlen(tp->t_text);
if (isprint(tp->t_text[0]))
TerminalIsBraindamaged = 1;
} else
tp->t_size = 0;
break;
case num:
tp->t_x = tgetnum(tp->t_key);
break;
case bool:
tp->t_x = tgetflag(tp->t_key);
break;
}
/* invoke any initialize routine */
if (tp->t_in && (*tp->t_in)(tp)) {
fprintf(stderr, "termcap init failed for %s\n", tp->t_key);
return (1);
}
}
tc_init_ops();
return tc_init_stacks();
}
#ifdef HAVE_TERMCAP
static char *
unpad(s)
register char *s;
{
if (s) {
register pad = 0;
while (isdigit(*s))
pad++, s++;
if (pad && *s == '*')
s++;
}
return (s);
}
#else /* !HAVE_TERMCAP, ie next code is for TERMINFO */
/*
* Remove the substring of the form "$<x^>" where x = number, and ^ = characters
* in the set [* /]. This is the terminfo way of specifying delays or padding.
*/
static char *
unpad(s)
register char *s;
{
if (s) {
register char *spt = s;
register char *spt1, *spt2;
char *strchr();
while (spt1 = strchr(spt, '$')) {
if (*(spt1 + 1) == '<') { /* found the '$<' pair */
if (spt2 = strchr(spt1, '>')) {
strcpy(spt1, ++spt2); /* found end '>' */
spt = spt1; /* copy tail of */
continue; /* string over */
/* '$<..', look */
/* for more */
} else
break; /* no match for '$<' so quit */
} else {
spt = spt1 + 1; /* found '$' but no '<', */
continue; /* look for more */
}
}
}
return (s);
}
#endif /* !HAVE_TERMCAP */
#ifdef SUNTGETENT
#define TCAPBUFSIZE 1024
#define SPECIALSIZE 2
static struct {
char *last;
char str[3];
} tcapSpecials[SPECIALSIZE] = {
NULL, "co",
NULL, "li"
};
/*
* Stomp on the first "co" and "li" entries in the termcap entry
* to avoid braindamage in the Sun version of the termcap library.
* Apparently the Sun version of tgetent() looks at the terminal
* state and uses this to prepend extra line+column spec's that
* reflect the terminal's current state. This is not what we want,
* so our only recourse is to undo the this braindamage here.
*/
static void
tc_fix_tcap_ent(buf)
char *buf;
{
char *bp = buf;
#ifndef SYSVREF
char *index();
#else
#define index(s, c) (char *)strchr(s, c)
#endif
int i;
/* for each item in buf ... */
for (bp = index(bp, ':'); bp && *(bp+1); bp = index(bp, ':')) {
++bp;
/* for each special tcap code ... */
for (i = 0; i < SPECIALSIZE; i++) {
if (strncmp(tcapSpecials[i].str, bp, 2) == 0) {
if (tcapSpecials[i].last)
strncpy(tcapSpecials[i].last, "xx", 2);
tcapSpecials[i].last = bp;
break;
}
}
}
}
#endif /* SUNTGETENT */
/*
* Matching is performed with a push-down automata implemented
* with dual stacks. An initial stack is loaded with all the
* potential matches from the termcap structure. Matching then
* takes place by popping each potential match off the ``current
* stack'' and, if a successful match for the current character
* occurs, pushing the match on the ``other stack''. When the
* ``current stack'' is empty (all elements have been examined),
* the stacks are swapped and the process restarted. This continues
* until a completed match or the stack of potential matches has
* been exhausted.
*/
static struct tcap **curstack, **cursp; /* ``potential match'' stack */
static struct tcap **otherstack, **othersp; /* ``match this pass'' stack */
static struct tcap **resetstack; /* prototype curstack */
static int stacksize; /* # of potential matches */
static int MatchInProgress; /* for fast check */
#define PushMatch(tp) (*--othersp = tp)
#define PopMatch() (*cursp++)
#define PopMatched() (*othersp++)
#define SwapStacks() { \
struct tcap **t; \
t = curstack, curstack = otherstack, otherstack = t; \
cursp = othersp, othersp = otherstack + stacksize; \
MatchInProgress = 1; \
}
/* The bcopy should NEVER over lap, so lets write a better macro! */
/* curstack == dest resetstack == source */
#define TRYSOMETHINGCUTE
#ifdef TRYSOMETHINGCUTE
#define ResetMatchStack() { \
register int loop = stacksize; \
if((resetstack + loop) > curstack) { \
while(loop--) \
*(curstack + loop) = *(resetstack + loop); \
} else { \
register int offset=0; \
while(loop--) \
*(curstack + offset) = *(resetstack + offset++); \
} \
cursp = curstack; \
MatchInProgress = 0; \
}
#else
#define ResetMatchStack() { \
bcopy((char *)resetstack, (char *)curstack, \
stacksize*sizeof (struct tcap *)); \
cursp = curstack; \
MatchInProgress = 0; \
}
#endif /* TRYSOMETHINGCUTE */
#define FlushStack(sp, stack) { \
while (sp < stack+stacksize) { \
tp = *sp++; \
tp->t_index = 0; \
tp->t_param = 0 ; \
tp->t_matched = 0; \
tp->t_2nd = 0; \
} \
}
#define FlushMatchStack() FlushStack(cursp, curstack)
#define FlushMatchedStack() FlushStack(othersp, otherstack);
#define MatchStackEmpty() (cursp >= curstack+stacksize)
#define MatchedStackEmpty() (othersp >= otherstack+stacksize)
/*
* Reset the pattern matching stack and load
* it with all the potential matching entries.
*/
static int
tc_init_stacks()
{
register struct tcap *tp;
for (tp = T; tp < T+Ts; tp++)
if (tp->t_text != NULL)
stacksize++;
curstack = (struct tcap **)malloc((unsigned)
(3*sizeof(struct tcap *) * stacksize));
if (!curstack)
return 1;
otherstack = curstack+stacksize;
resetstack = otherstack+stacksize;
othersp = resetstack+stacksize;
for (tp = T; tp < T+Ts; tp++)
if (tp->t_text != NULL)
PushMatch(tp);
othersp = otherstack+stacksize;
ResetMatchStack();
return 0;
}
extern struct tcap interruptedOp;
/*
* Interpret data from the application. We match data against
* the ``escape sequences'' expected for this termcap description
* and, if successful, invoke the routines used to emulate the
* capabilities on the window.
*/
int
tc_display(cp, n)
u_char *cp;
register int n;
{
register int c, j;
register struct tcap *tp;
static char dbuf[256], *dp = dbuf;
int restart, lim;
/*int c1=0, c2=0, c3=0, c4=0;*/
/*fprintf(stderr, "tc_display(%d, %d)\n", strlen(cp), n);*/
/*
* If we're blocked with a page full, indicate
* nothing was sent to the screen. We should
* never be called when already blocked, but
* just in case, turn scrolling on again so we
* don't lost any data.
*/
if (PageFull) {
if (interruptedOp.t_key == 0)
return (n);
scrollreset(0); /* XXX */
}
/*
* If we have previous output, process it first.
* Check on completion to see if we filled the screen.
*/
if (interruptedOp.t_key) {
(*interruptedOp.t_op)(&interruptedOp);
if (PageFull)
return (n);
interruptedOp.t_key = 0;
}
/*
* For each input character, look for potential
* matches in the tcap structure. For each possible
* match, construct the resultant output buffer.
* On first match process the operation (e.g. invoke
* internal routine) and flush extraneous matches.
* If input doesn't match any capability, send it to
* the window.
*/
MARK (While1);
while (n > 0 && !PageFull) {
/*
* If we're not in the middle of a match, then
* try and bypass the pattern matcher by performing
* special checks on the most common input.
*/
if (!MatchInProgress) {
MARK (While2);
while (n > 0 && !PageFull) {
/*
* If terminal has only non-printing escape sequences,
* then process printable characters w/o matching against
* the termcap strings.
*/
if (!TerminalIsBraindamaged) {
for (dp = (char *)cp; n > 0 && isprint((int)*cp); n--)
cp++;
if ((char *)cp > dp) {
tp = T+Ts;
tp->t_text = dp; /* use original storage */
tp->t_size = (char *)cp - dp;
(*tp->t_op)(tp);
/*++c1;*/
if (PageFull)
return (n);
continue;
}
}
/*
* Make quick checks for standard NL, CR, BS, and TAB
* characters. This speeds up scrolling for most
* terminal types.
*/
c = *cp;
if (CheckNL && c == '\n')
tp = CheckNL;
else if (CheckCR && c == '\r')
tp = CheckCR;
else if (CheckTAB && c == '\t')
tp = CheckTAB;
else if (CheckBS && c == '\b')
tp = CheckBS;
else
break;
cp++, n--;
(*tp->t_op)(tp);
/*++c2;*/
if (PageFull)
return (n);
}
dp = dbuf;
if (n == 0)
break;
}
c = *dp++ = *cp++, n--;
MARK (2Begin);
while (!MatchStackEmpty()) {
MARK (1Other);
tp = PopMatch();
MARK (Again1);
again:
j = tp->t_index;
restart = 0;
/*
* Check match against numeric %[d23] specification.
*/
if (tp->t_text[j] == '%') {
MARK (AFoo);
switch (tp->t_text[j+1]) {
case 'd': /* series of decimal digits */
lim = 127;
goto digit;
case '2': /* two decimal digits */
lim = 2;
goto digit;
case '3': /* three decimal digits */
lim = 3;
/* fall thru.. */
MARK (Digit);
digit:
if (isdigit(c) && tp->t_matched < lim) {
tp->t_matched++;
tp->t_param = tp->t_param*10 + (c-'0');
goto plainmatch;
} else {
if (tp->t_matched == 0)
tp->t_param = 1 ;
tp->t_matched = 0;
restart = !isdigit(c);
goto gotvalue;
}
/*NOTREACHED*/
break;
case '.': /* binary character */
tp->t_param = c;
gotvalue:
MARK (GotoVl);
switch (tp->t_2nd + tp->t_pc_r) {
case 0:
case 2:
if ((tp->t_y = tp->t_param) >= tp->t_yilim)
tp->t_y -= tp->t_yi;
break;
case 1:
if ((tp->t_x = tp->t_param) >= tp->t_xilim)
tp->t_x -= tp->t_xi;
break;
}
tp->t_2nd = !tp->t_2nd;
tp->t_index += 2;
tp->t_param = 0 ;
goto plainmatch;
case '%':
if ((c & 0177) == '%') {
tp->t_index += 2;
goto plainmatch;
} else
goto nomatch;
default:
abort(); /* XXX */
/* NOTREACHED */
}
} else if ((c & 0177) == (tp->t_text[j] & 0177)) {
tp->t_index++;
MARK (PlainM);
plainmatch: /* plain match */
if (tp->t_index >= tp->t_size) {/* match completed */
if (tp->t_op) {
(*tp->t_op)(tp);
/*++c3;*/
}
dp = dbuf;
tp->t_index = 0;
tp->t_matched = 0;
tp->t_param = 0 ;
tp->t_2nd = 0;
goto done;
}
/*
* The end of a %d match is the only case where a
* character must be pushed-back and re-parsed.
*/
if (restart)
goto again;
PushMatch(tp); /* push partial match */
} else {
nomatch: /* failed match */
tp->t_index = 0;
tp->t_param = 0 ;
tp->t_matched = 0;
tp->t_2nd = 0;
}
}
MARK (2After);
if (!MatchedStackEmpty()) {
SwapStacks();
continue;
}
MARK (DoneL);
done:
/*
* Come here either because no partial matches were
* found in the table, or because a match completed.
* In the first case we send the input data off
* immediately. In the second case we reset the
* state machines and go on to the next character.
* Note: testing shows that most text gets displayed from here.
*/
if (dp - dbuf) { /* flush output */
tp = T+Ts;
tp->t_text = dbuf;
tp->t_size = dp - dbuf;
(*tp->t_op)(tp);
/*++c4;*/
dp = dbuf;
}
MARK (3After);
FlushMatchedStack(); /* reset partial matches */
FlushMatchStack(); /* reset unchecked partials */
ResetMatchStack(); /* re-init match stack */
}
/*fprintf(stderr, "c1=%d, c2=%d, c3=%d, c4=%d, n=%d\n", c1,c2,c3,c4,n);*/
return (n); /* return number of chars processed */
}