/************************************************************************ * * * Copyright (c) 1982, Fred Fish * * All Rights Reserved * * * * This software and/or documentation is released for public * * distribution for personal, non-commercial use only. * * Limited rights to use, modify, and redistribute are hereby * * granted for non-commercial purposes, provided that all * * copyright notices remain intact and all changes are clearly * * documented. The author makes no warranty of any kind with * * respect to this product and explicitly disclaims any implied * * warranties of merchantability or fitness for any particular * * purpose. * * * ************************************************************************ */ /* * Modified: * 30-Apr-86 Mic Kaczmarczik ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Instead of using VAX C getenv("TERM"), which does not * return the value of logical name "TERM", translate the * logical name by hand. * 11-Oct-86 Mic Kaczmarczik ...!ihnp4!seismo!ut-sally!ut-ngp!mic * Support tc capability to allow the library to use standard * termcaps. Rewrote tgetent to look for tc capability * and add new terminal definition to the caller's buffer. * This makes it rather possible to overflow the caller's * buffer, but the library doesn't make any claim that it * won't overwrite the buffer anyway... * 28-Jul-88 Tom Hageman, AZG, Groningen * reorganize handling of TERMCAP environment variable and * "tc" handling so that entry in TERMCAP can contain "tc" cap. * remove tail recursion from gotcha() * 29-Jul-89 TRH * add HARDWIRED compilation option for personal computers * (currently for GEMDOS and MSDOS w/ ANSI.SYS only) * 12-Dec-89 TRH * some optimizations--termcap file is opened only when needed, * remove "tc" handling for TERMCAP env. var. * 3-Mar-90 TRH * complete revision; compile termcap into a fast binary-search * lookup table. * 24-Jun-92 TRH * allow ``soft'' hardwires; * some optimizations (most notably in-place FASTCAP). * 4-Jan-93 TRH * avoid overwriting tail of long entries by in-place FASTCAP. * (See Log at end) */ #ifdef RCS_ID static const char rcsid[] = "$Id: tgetent.c,v 1.6 1993/05/24 22:41:36 tom Exp tom $"; #endif /* * LIBRARY FUNCTION * * tgetent load buffer with entry for specified terminal * * KEY WORDS * * termcap functions * utility routines * * SYNOPSIS * * int tgetent(bp,name) * char bp[TBUFSIZE]; * const char *name; * * DESCRIPTION * * Extracts the entry for terminal from the termcap file * and places it in the character buffer . It is currently * assumed that bp is at least 2048 characters. If the entry in * the termcap file is larger than 2047 characters the excess * characters will be discarded and appropriate status will * be returned. * * Also note that since bp is used by other termcap * routines, the storage associated with the termcap entry * cannot be freed until all termcap calls are completed. * * Tgetent can be directed to look in a file other than * the default (/etc/termcap) by defining an environment * variable called TERMCAP to be the pathname of the desired * termcap file. This is useful for debugging new entries. * NOTE: the pathname MUST begin with a '/' character. * * Also, if the string assigned to TERMCAP does not begin with * a '/' it is assumed to be a termcap entry. * * RETURNS * * -1 if the termcap file cannot be opened * 0 if no entry in termcap file matches * 1 if extraction is successful with no errors * 2 if extraction is successful but entry truncated * * SEE ALSO * * tgetnum extract numeric type capability * tgetflag test boolean type capability * tgetstr get string value of capability * * AUTHORS * * Fred Fish, Mic Kaczmarczik, Tom Hageman * */ #include #include "_termcap.h" #if (GEMDOS || MSDOS) # define DEFAULT_FILE "c:\\etc\\termcap" # define ISFILE(cp) (*cp == '\\' || cp[1] == ':') #else /* assume unix */ # define DEFAULT_FILE "/etc/termcap" /* default termcap filename */ # define ISFILE(cp) (*cp == '/') /* test for TERMCAP env. var. */ #endif #if (HARDWIRED <= 0) /* "real" termcap database access */ #if FASTCAP t_table _tcptable; /* place to store termcap definition. */ #else char *_tcpbuf; #endif static getent __(( FILE *_(fp), char _(buf)[TBUFSIZE], const char *_(name) )); static gotcha __(( const char *_(bp), const char *_(name) )); #if (HARDWIRED < 0) static hardwired __(( char _(buf)[TBUFSIZE], const char *_(name) )); #endif /* * PSEUDO CODE * * Begin tgetent * Setup status to NO_ENTRY * Setup file pointer to NULL * If TERMCAP environment variable was found Then * If TERMCAP contains absolute pathname Then * Attempt to open this file * Else If TERMCAP entry matches name Then * Set status to SUCCESS and set source pointer to TERMCAP * Endif * Endif * If entry was not in TERMCAP Then * set source pointer to buffer * If no file open yet Then * Open default termcap file * Endif * Find entry associated with name and put it in buffer. * Endif #if FASTCAP * Setup lookup table for compiling. * Copy name to buffer. * While previous entry was found * Skip to next valid capability. * If input exhausted Then * Exit while loop * Endif * If tc capability found Then * Copy tc name to temporary buffer * If invalid tc capability or limit reached Then * Exit while loop with status = TRUNCATED * Endif * Find entry associated with new name in a temporary buffer * and set status accordingly * Setup source pointer to new entry * Else * If there is no more room Then * Exit the loop with status = TRUNCATED * Endif * If capability not yet in lookup-table Then * Enter capability in table * Copy its value into buffer * Endif * Endif * End while * Relocate lookup-table to a safe place. * Turn off compiling flag. #else * While previous entry was found * If no tc capability found Then * Exit while loop with status = SUCCESS * Else If invalid tc capability or limit reached * Exit while loop with status = TRUNCATED * Else * Call getent to get entry indicated by tc= * If entry found then * Concatenate entry into buffer * End If * End if * End while #endif * Close termcap file * Return status code * End tgetent * */ int tgetent(buf, name) char buf[TBUFSIZE]; const char *name; { extern char *getenv __(( const char * )); static char Termcap[] = DEFAULT_FILE, R[] = "r"; char tcbuf[TBUFSIZE]; /* for tc= cap. */ register char *s, *d = buf; register FILE *fp = NULL; /* important! */ register int status = NO_ENTRY, limit = 10; register char *t; /* scratch pointer */ #if (HARDWIRED < 0) if ((status = hardwired(d, name)) > NO_ENTRY && (name == NULL || gotcha(d, name))) return status; #endif *d = '\0'; if (name == NULL) return NO_ENTRY; /* * Check environment variable TERMCAP. If it is defined and is * an absolute path name (UNIX: starts with `/', {MS,GEM}DOS: * starts with `\' or `.:') attempt to open it. Else treat it as * the termcap entry itself and check its name. */ if (s = getenv("TERMCAP")) { if (ISFILE(s)) fp = fopen(s, R); else status = gotcha(s, name); } /* * if we didn't find the right entry from the environment, try * to obtain it from the file. Open the default termcap here if * an alternate termcap hasn't been opened yet. */ if (status <= NO_ENTRY) { s = tcbuf; /* to scratch buffer (important for FASTCAP) */ status = (fp == NULL && (fp = fopen(Termcap, R)) == NULL) ? NO_FILE : getent(fp, s, name); } #if ! FASTCAP s = strcpy(d, s); /* copy to user-supplied buffer. */ _tcpbuf = s; while (status >= 0) { _tcphash(); if ((s = _tcpfind("tc")) == NULL) break; /* no more tc= entries, SUCCESS! */ d = s + 1; if (*s++ == '=') { while (*s) /* zero-terminate name */ if (*s++ == ':') { *--s = '\0'; break; } } DPRINTF(("tc=%s\n", d)); if ((d == s) || /* incalid name */ (--limit == 0) || /* cycle? */ ((fp == NULL && (fp = fopen(Termcap, R)) == NULL) || (status = getent(fp, s, d)) <= NO_ENTRY)) { status = TRUNCATED; break; } /* strip name field from new entry */ while (*s && *s++ != ':') ; /* Append new entry into buffer, right at "tc=" */ /* check against buffer overflow */ for (d -= 3, t = &buf[TBUFSIZE]; *d++ = *s++; ) { if (d == t) { status = TRUNCATED; *--d = '\0'; /* still zero-terminate */ break; } } } #else /* FASTCAP */ /* * Initialize the lookup table (for compiling). The lookup table is * built at the end of the user-supplied buffer (which is assumed to * be properly aligned) and grows downward. * INVARIANT: (&_tcptable.t_body[_tcptable.t_size] == &buf[TBUFSIZE]) */ _tcptable.t_body = (t_entry *) &d[TBUFSIZE]; _tcptable.t_size = 0; _tcptable.t_compiling++; _TCP_SETBASE(d); DPRINTF(("%s\n", s)); /* * Compile the termcap into a fast-lookup table. * Keep an eye open for tc capabilities in the entry. * If found, switch input pointer to the new entry * and proceed. To avoid infinite loops, * allow only a limited number of iterations through this process. */ #ifndef TINY /* first copy name */ while (*d = *s++) if (*d++ == ':') { --d; break; } --s; #endif if (status > 0) for (;;) { register t_entry *ent_p; /* skip to next capability */ while (*s) { if (*s == '\\') { if (*++s == '\0') break; ++s; } else if (*s++ == ':' && *s != ':') break; } if (*s == '\0') break; if (*s++ == 't' && *s == 'c') { if (*++s != '=') continue; ++s; /* copy name to a safe place, since we're in tcbuf. */ for (t = d; *d = *s++; ) { if (*d++ == ':') { *--d = '\0'; break; } } DPRINTF(("tc=%s\n", t)); if ((d == t) || /* incalid tc */ (d = t, /* restore... */ --limit == 0) || /* cycle? */ ((fp == NULL && (fp = fopen(Termcap, R)) == NULL) || (status = getent(fp, s = tcbuf, d)) <= NO_ENTRY)) { status = TRUNCATED; break; } continue; } if (*s++ == '\0') /* end of entry? */ break; /* add new entry, iff we still got the space */ if (d >= (char *) _tcptable.t_body - (sizeof(t_entry) + 1)) { trunc: status = TRUNCATED; break; } if ((ent_p = (t_entry *)_tcpfind(s - 2)) == NULL) continue; /* already there... */ *d++ = '\0'; /* terminate previous entry */ ent_p->t_val = _TCP_OFF(d); /* copy entry to buffer (de-escape on the fly) */ while (*s != ':') { if (d >= (char *) _tcptable.t_body) { --d; goto trunc; } switch (*d = *s++) { case '^': *d++ = *s++ & 037; continue; case '\\': *d = *s++; # define isoctal(c) ('0' <= (c) && (c) < '8') if (isoctal(*d)) { register int i = 3; *d -= '0'; while (--i && isoctal(*s)) { *d = (*d << 3) + *s++; *d -= '0'; } } else { t = s; s = "E\33b\bf\fn\nr\rt\tv\va\7e\33s "; do if (*s++ == *d) { *d = *s; break; } while (*s++); s = t; } default: d++; continue; case '\0': --s; break; } break; } } *d++ = '\0'; _tcptable.t_compiling = 0; /* we're done compiling */ DPRINTF(("tmp: %04x final %04x size %d\n", (int)_tcptable.t_body, (int)d, _tcptable.t_size)); #ifdef DEBUG { register t_entry *ent_p; printf("---------\n"); for (ent_p = &_tcptable.t_body[_tcptable.t_size]; --ent_p >= &_tcptable.t_body[0]; ) { printf("%3d: %c%c -> @%04x ", (int)(ent_p - _tcptable.t_body), ent_p->t_cap>>8, ent_p->t_cap, (int)ent_p->t_val); for (s = _TCP_VAL(ent_p); *s; ) { if ((*s & ~037) == 0) { putchar('^'), putchar(*s++ + '@'); } else if ((unsigned)(*s >= 0177)) { printf("\\%03o", *s++); } else putchar(*s++); } putchar('\n'); } printf("---------\n"); printf("Space occupied: strings %d, table %d, total %d\n", (int)((char *)_tcptable.t_body - buf), (int)(d - (char *)_tcptable.t_body), (int)(d - buf)); printf( status == SUCCESS ? "SUCCESS\n" : status == TRUNCATED ? "TRUNCATED\n" : status == NO_ENTRY ? "NO ENTRY\n" : "NO FILE\n"); } #endif/*DEBUG*/ #endif /* FASTCAP */ /* close termcap file(s) and return the status */ if (fp) fclose(fp); return (status); } /* tgetent */ /* * INTERNAL FUNCTION * * getent find termcap entry in termcap file * * KEY WORDS * * internal functions * getent * * SYNOPSIS * * static int getent(fp, buf, name) * FILE *fp; * char buf[TBUFSIZE]; * const char *name; * * DESCRIPTION * * Getent is called by tgetent each time tgetent attempts to * read a capability from the termcap database file. Places * the entry in the buffer pointed to by bp * * * PSEUDOCODE * * Begin Getent * If file is invalid * Return NO_FILE status. * End if * Seek to beginning of termcap file * Clear buffer * While records left to process * If this is entry is what we want then * If entry was truncated then * Return TRUNCATED status * Else * Return SUCCESS status. * End if * End if * End while * Return NO_ENTRY status. * End * * BUGS * The recognition of escaped newlines is simple-minded. */ static int getent(tfp, buf, name) FILE *tfp; /* pointer to termcap database file. */ char buf[TBUFSIZE]; /* Pointer to buffer (TBUFSIZE min) */ const char *name; /* Pointer to terminal entry to find */ { register FILE *fp = tfp; register int c; register char *cp, *ep; rewind(fp); /* rewind termcap file */ ep = &buf[TBUFSIZE - 1]; do { cp = buf; for (;;) { if ((c = getc(fp)) < 0) { /* i.e. EOF */ if (cp == buf) return NO_ENTRY; break; } if (c == '\n') { if (cp == buf) /* empty line */ continue; if (*--cp != '\\') { /* escaped newline? */ ++cp; break; } } if (cp < ep) /* still room? */ *cp++ = c; } *cp = '\0'; } while (gotcha(buf, name) == NO_ENTRY); return (cp >= ep ? TRUNCATED : SUCCESS); } /* * INTERNAL FUNCTION * * gotcha test to see if entry is for specified terminal * * SYNOPSIS * * static int gotcha(bp,name) * const char *bp; * const char *name; * * DESCRIPTION * * Tests to see if the entry in buffer bp matches the terminal * specified by name. Returns TRUE if match is detected, FALSE * otherwise. * */ /* * PSEUDO CODE * * Begin gotcha * If buffer character is not a comment character then * Initialize name scan pointer. * Loop * If valid name separator/terminator for buffer string * If valid terminator for name string * Return SUCCESS since a match was found. * If not an alternate name separator for buffer string * Exit loop since no more names to check. * Advance buffer pointer * Re-Initialize name pointer * Else * If name and buffer characters don't compare equal * set name pointer to dummy value * Else * Advance name pointer * End if * Advance buffer pointer * End if * End Loop * End if * Return NO_ENTRY * End gotcha * */ static int gotcha(bp, name) register const char *bp; const char *name; { if (*bp != '#') { register char c; register const char *np = name; for (;;) { if ((c = *bp++) == '\0' || c == '|' || c == ':') { if (*np == '\0') return SUCCESS; if (c != '|') break; np = name; } else if (*np++ != c) { np = ":"; /* sentinel */ } } } return NO_ENTRY; } #endif /* HARDWIRED */ #if (HARDWIRED) # if (HARDWIRED < 0) # define tgetent static hardwired /* cheat. */ # define DECL_TCPTABLE(e) # define INIT_TCPTABLE(e) \ (_tcptable.t_body = e, \ _tcptable.t_size = sizeof e / sizeof e[0], \ _tcptable.t_compiled = 0) # else /* HARDWIRED > 0 */ # define DECL_TCPTABLE(e) \ t_table _tcptable = { e, sizeof e / sizeof e[0], 0 }; # define INIT_TCPTABLE(e) # endif static void fill_in __(( const char *_(id), int _(val) )); static void fill_in(id, val) const char *id; register int val; { register char *p = _tcpfind(id) + 1; register int digit; if (digit = val / 100) { /* future small font/big screen? */ val %= 100; *p++ = digit + '0'; } *p++ = val / 10 + '0'; *p++ = val % 10 + '0'; } #define ENTRY(a,b, val) {(a<<8)|b, val} static char /* ANSI says that literal strings are read-only. */ CO[] = "#80\0", LI[] = "#25\0"; #if GEMDOS /* * HARDWIRED tgetent(buf, name) * * provides a termcap entry for atari ST console, * with the actual number of lines and columns filled in. * * The `name' argument is ignored. */ static t_entry entries[] = { /* Note reverse lexical order! */ ENTRY( 'v','s', "=\33e" ), ENTRY( 'v','i', "=\33f" ), ENTRY( 'v','e', "=\33e" ), ENTRY( 'u','p', "=\33A" ), ENTRY( 't','i', "=\33w" ), ENTRY( 't','e', "=\33v" ), ENTRY( 't','a', "=\t" ), ENTRY( 's','r', "=\33I" ), ENTRY( 's','o', "=\33p" ), ENTRY( 's','f', "=\n" ), ENTRY( 's','e', "=\33q" ), ENTRY( 's','c', "=\33j" ), ENTRY( 'r','c', "=\33k" ), ENTRY( 'n','d', "=\33C" ), ENTRY( 'm','r', "=\33p" ), ENTRY( 'm','e', "=\33q" ), ENTRY( 'l','i', LI ), ENTRY( 'l','e', "=\b" ), ENTRY( 'k','m', "" ), ENTRY( 'i','t', "#8" ), ENTRY( 'i','s', "=\33q\33v\33e\33J" ), ENTRY( 'h','o', "=\33H" ), ENTRY( 'd','o', "=\n" ), ENTRY( 'd','l', "=\33M" ), ENTRY( 'c','r', "=\r" ), ENTRY( 'c','o', CO ), ENTRY( 'c','m', "=\33Y%+ %+ " ), ENTRY( 'c','l', "=\33E" ), ENTRY( 'c','e', "=\33K" ), ENTRY( 'c','d', "=\33J" ), ENTRY( 'b','l', "=\7" ), ENTRY( 'a','l', "=\33L" ), }; DECL_TCPTABLE(entries) tgetent(buf, name) char buf[TBUFSIZE]; const char *name; { short lines, columns, dum; INIT_TCPTABLE(entries); vq_chcells(graf_handle(&dum, &dum, &dum, &dum), &lines, &columns); fill_in("li", lines); fill_in("co", columns); strcpy(buf, "st|atari|atari-st console"); return SUCCESS; } #else # if MSDOS /* * HARDWIRED tgetent(buf, name) * * provides a termcap entry for PC console (using ANSI.SYS), * with the actual number of lines and columns filled in. * * The `name' argument is ignored. */ static t_entry entries[] = { ENTRY( 'u','s', "=\33[4m" ), ENTRY( 'u','p', "=\33[A" ), ENTRY( 'u','e', "=\33E[m" ), ENTRY( 't','i', "=\33[?7l" ), ENTRY( 't','e', "=\33[?7h" ), ENTRY( 't','a', "=\t" ), ENTRY( 's','o', "=\33[7m" ), ENTRY( 's','f', "=\n" ), ENTRY( 's','e', "=\33[m" ), ENTRY( 's','c', "=\033[s" ), ENTRY( 'r','c', "=\033[u" ), ENTRY( 'n','d', "=\33[C" ), ENTRY( 'm','r', "=\33[7m" ), ENTRY( 'm','e', "=\33E[m" ), ENTRY( 'm','d', "=\33[1m" ), ENTRY( 'm','b', "=\33[5m" ), ENTRY( 'l','i', LI ), ENTRY( 'l','e', "=\b" ), ENTRY( 'k','m', "" ), ENTRY( 'i','t', "#8" ), ENTRY( 'h','o', "=\33[H" ), ENTRY( 'd','o', "=\33[B" ), ENTRY( 'c','r', "=\r" ), ENTRY( 'c','o', CO ), ENTRY( 'c','m', "=\33[%i%d;%dH" ), ENTRY( 'c','l', "=\33[2J" ), ENTRY( 'c','e', "=\33[K" ), ENTRY( 'b','l', "=\7" ), }; DECL_TCPTABLE(entries) #include #define Crawio(c) ((regs.h.ah = 6),(regs.h.dl = (c)),intdos(®s,®s)) tgetent(buf, name) char buf[TBUFSIZE]; const char *name; { union REGS regs; int lines, columns; INIT_TCPTABLE(entries); /* check if ANSI.SYS is installed... */ regs.x.ax = 0x0c00, intdos(®s, ®s); /* flush input buffer */ write(1, "\33[6n", sizeof "\33[6n" - 1);/* send Device Status request */ /* if installed, Cursor Position Report is available immediately */ if ((char) Crawio(0xff) != '\33') return NO_ENTRY; while ((char) Crawio(0xff) != 'R') /* eat CPR = "ESC[#;#R" */ ; /* now, get size of screen */ regs.h.ah = 0x0f; /* get video mode and active page */ int86(0x10, ®s, ®s); columns = regs.h.ah; regs.h.ah = 0x12; /* check presence of EGA/VGA card */ regs.h.bl = 0x10; int86(0x10, ®s, ®s); if (regs.h.bl != 0x10) { /* EGA/VGA present */ regs.x.ax = 0x1130; /* get textgen. info */ regs.h.bh = 0; int86(0x10, ®s, ®s); lines = regs.h.dl + 1; } else lines = 25; fill_in("li", lines); fill_in("co", columns); strcpy(buf, "pc|msdos|MS-DOS/PC console using ANSI.SYS"); return SUCCESS; } # else #error "No HARDWIRED termcap available for this configuration" # endif /* MSDOS */ #endif /* GEMDOS */ #endif /* HARDWIRED */ /*======================================================================* * $Log: tgetent.c,v $ * Revision 1.6 1993/05/24 22:41:36 tom * tgetstr conversion: add \e, \s; use #error directive; fix typo in RCS Log. * *======================================================================*/