/* * 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: tcap_parse.c,v 2.3 88/10/04 06:00:03 gnu Release $"; #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 #include #include #ifdef REF #include #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; extern int tgetent(), tgetnum(), tgetflag(); extern char *tgetstr(); register struct tcap *tp; if (tgetent(tcapbuf, term) != 1) { /* unknown terminal type? */ fprintf(stderr, "tgetent failed\n"); 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 "$" 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; \ } #define ResetMatchStack() { \ bcopy((char *)resetstack, (char *)curstack, \ stacksize*sizeof (struct tcap *)); \ cursp = curstack; \ MatchInProgress = 0; \ } #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. */ 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; /* * 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. */ 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) { 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); 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); if (PageFull) return (n); } dp = dbuf; if (n == 0) break; } c = *dp++ = *cp++, n--; while (!MatchStackEmpty()) { tp = PopMatch(); again: j = tp->t_index; restart = 0; /* * Check match against numeric %[d23] specification. */ if (tp->t_text[j] == '%') { 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.. */ 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: 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++; plainmatch: /* plain match */ if (tp->t_index >= tp->t_size) {/* match completed */ if (tp->t_op) (*tp->t_op)(tp); 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; } } if (!MatchedStackEmpty()) { SwapStacks(); continue; } 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. */ if (dp - dbuf) { /* flush output */ tp = T+Ts; tp->t_text = dbuf; tp->t_size = dp - dbuf; (*tp->t_op)(tp); dp = dbuf; } FlushMatchedStack(); /* reset partial matches */ FlushMatchStack(); /* reset unchecked partials */ ResetMatchStack(); /* re-init match stack */ } return (n); /* return number of chars processed */ }