mirror of
https://github.com/IanDarwin/OpenLookCDROM.git
synced 2026-02-13 11:34:52 +00:00
638 lines
16 KiB
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 */
|
|
}
|