captoinfo.c revision b55148877d473978f0b46d593fd6213fa526fcc5
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* NAME
* captoinfo - convert a termcap description to a terminfo description
*
* SYNOPSIS
* captoinfo [-1vV] [-w width] [ filename ... ]
*
* AUTHOR
* Tony Hansen, January 22, 1984.
*/
#include "curses.h"
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "otermcap.h"
#include "print.h"
#define trace stderr /* send trace messages to stderr */
/* extra termcap variables no longer in terminfo */
char *oboolcodes[] =
{
"bs", /* Terminal can backspace with "^H" */
"nc", /* No correctly working carriage return (DM2500,H2000) */
"ns", /* Terminal is a CRT but does not scroll. */
"pt", /* Has hardware tabs (may need to be set with "is") */
"MT", /* Has meta key, alternate code. */
"xr", /* Return acts like ce \r \n (Delta Data) */
0
};
int cap_bs = 0, cap_nc = 1, cap_ns = 2, cap_pt = 3, cap_MT = 4, cap_xr = 5;
char *onumcodes[] =
{
"dB", /* Number of millisec of bs delay needed */
"dC", /* Number of millisec of cr delay needed */
"dF", /* Number of millisec of ff delay needed */
"dN", /* Number of millisec of nl delay needed */
"dT", /* Number of millisec of tab delay needed */
"ug", /* Number of blank chars left by us or ue */
/* Ignore the 'kn' number. It was ill-defined and never used. */
"kn", /* Number of "other" keys */
0
};
int cap_dB = 0, cap_dC = 1, cap_dF = 2, cap_dN = 3, cap_dT = 4, cap_ug = 5;
char *ostrcodes[] =
{
"bc", /* Backspace if not "^H" */
"ko", /* Termcap entries for other non-function keys */
"ma", /* Arrow key map, used by vi version 2 only */
"nl", /* Newline character (default "\n") */
"rs", /* undocumented reset string, like is (info is2) */
/* Ignore the 'ml' and 'mu' strings. */
"ml", /* Memory lock on above cursor. */
"mu", /* Memory unlock (turn off memory lock). */
0
};
int cap_bc = 0, cap_ko = 1, cap_ma = 2, cap_nl = 3, cap_rs = 4;
#define numelements(x) (sizeof (x)/sizeof (x[0]))
char oboolval[2][numelements(oboolcodes)];
short onumval[2][numelements(onumcodes)];
char *ostrval[2][numelements(ostrcodes)];
/* externs from libcurses.a */
extern char *boolnames[], *boolcodes[];
extern char *numnames[], *numcodes[];
extern char *strnames[], *strcodes[];
/* globals for this file */
char *progname; /* argv [0], the name of the program */
static char *term_name; /* the name of the terminal being worked on */
static int uselevel; /* whether we're dealing with use= info */
static int boolcount, /* the maximum numbers of each name array */
numcount,
strcount;
/* globals dealing with the environment */
extern char **environ;
static char TERM[100];
#if defined(SYSV) || defined(USG) /* handle both Sys Vr2 and Vr3 curses */
static char dirname[BUFSIZ];
#else
#include <sys/param.h>
static char dirname[MAXPATHLEN];
#endif /* SYSV || USG */
static char TERMCAP[BUFSIZ+15];
static char *newenviron[] = { &TERM[0], &TERMCAP[0], 0 };
/* dynamic arrays */
static char *boolval[2]; /* dynamic array of boolean values */
static short *numval[2]; /* dynamic array of numeric values */
static char **strval[2]; /* dynamic array of string pointers */
/* data buffers */
static char *capbuffer; /* string table, pointed at by strval */
static char *nextstring; /* pointer into string table */
static char *bp; /* termcap raw string table */
static char *buflongname; /* place to copy the long names */
/* flags */
static int verbose = 0; /* debugging printing level */
static int copycomments = 0; /* copy comments from tercap source */
#define ispadchar(c) (isdigit(c) || (c) == '.' || (c) == '*')
static void getlongname(void);
static void handleko(void);
static void handlema(void);
static void print_no_use_entry(void);
static void print_use_entry(char *);
static void captoinfo(void);
static void use_etc_termcap(void);
static void initdirname(void);
static void setfilename(char *);
static void setterm_name(void);
static void use_file(char *);
static void sorttable(char *[], char *[]);
static void inittables(void);
/*
* Verify that the names given in the termcap entry are all valid.
*/
int
capsearch(char *codes[], char *ocodes[], char *cap)
{
for (; *codes; codes++)
if (((*codes)[0] == cap[0]) && ((*codes)[1] == cap[1]))
return (1);
for (; *ocodes; ocodes++)
if (((*ocodes)[0] == cap[0]) && ((*ocodes)[1] == cap[1]))
return (1);
return (0);
}
void
checktermcap()
{
char *tbuf = bp;
enum { tbool, tnum, tstr, tcancel, tunknown } type;
for (;;) {
tbuf = tskip(tbuf);
while (*tbuf == '\t' || *tbuf == ' ' || *tbuf == ':')
tbuf++;
if (*tbuf == 0)
return;
/* commented out entry? */
if (*tbuf == '.') {
if (verbose)
(void) fprintf(trace, "termcap string '%c%c' "
"commented out.\n", tbuf[1], tbuf[2]);
if (!capsearch(boolcodes, oboolcodes, tbuf + 1) &&
!capsearch(numcodes, onumcodes, tbuf + 1) &&
!capsearch(strcodes, ostrcodes, tbuf + 1))
(void) fprintf(stderr,
"%s: TERM=%s: commented out code '%.2s' "
"is unknown.\n", progname, term_name,
tbuf+1);
continue;
}
if (verbose)
(void) fprintf(trace, "looking at termcap string "
"'%.2s'.\n", tbuf);
switch (tbuf[2]) {
case ':': case '\0': type = tbool; break;
case '#': type = tnum; break;
case '=': type = tstr; break;
case '@': type = tcancel; break;
default:
(void) fprintf(stderr,
"%s: TERM=%s: unknown type given for the "
"termcap code '%.2s'.\n", progname,
term_name, tbuf);
type = tunknown;
}
if (verbose > 1)
(void) fprintf(trace, "type of '%.2s' is %s.\n", tbuf,
(type == tbool) ? "boolean" :
(type == tnum) ? "numeric" :
(type = tstr) ? "string" :
(type = tcancel) ? "canceled" : "unknown");
/* look for the name in bools */
if (capsearch(boolcodes, oboolcodes, tbuf)) {
if (type != tbool && type != tcancel)
(void) fprintf(stderr,
"%s: TERM=%s: wrong type given for the "
"boolean termcap code '%.2s'.\n", progname,
term_name, tbuf);
continue;
}
/* look for the name in nums */
if (capsearch(numcodes, onumcodes, tbuf)) {
if (type != tnum && type != tcancel)
(void) fprintf(stderr,
"%s: TERM=%s: wrong type given for the "
"numeric termcap code '%.2s'.\n", progname,
term_name, tbuf);
continue;
}
/* look for the name in strs */
if (capsearch(strcodes, ostrcodes, tbuf)) {
if (type != tstr && type != tcancel)
(void) fprintf(stderr,
"%s: TERM=%s: wrong type given for the "
"string termcap code '%.2s'.\n", progname,
term_name, tbuf);
continue;
}
(void) fprintf(stderr,
"%s: TERM=%s: the %s termcap code '%.2s' is not a valid "
"name.\n", progname, term_name,
(type == tbool) ? "boolean" :
(type == tnum) ? "numeric" :
(type = tstr) ? "string" :
(type = tcancel) ? "canceled" : "(unknown type)", tbuf);
}
}
/*
* Fill up the termcap tables.
*/
int
filltables(void)
{
int i, tret;
/* Retrieve the termcap entry. */
if ((tret = otgetent(bp, term_name)) != 1) {
(void) fprintf(stderr,
"%s: TERM=%s: tgetent failed with return code %d (%s).\n",
progname, term_name, tret,
(tret == 0) ? "non-existent or invalid entry" :
(tret == -1) ? "cannot open $TERMCAP" : "unknown reason");
return (0);
}
if (verbose) {
(void) fprintf(trace, "bp=");
(void) cpr(trace, bp);
(void) fprintf(trace, ".\n");
}
if (uselevel == 0)
checktermcap();
/* Retrieve the values that are in terminfo. */
/* booleans */
for (i = 0; boolcodes[i]; i++) {
boolval[uselevel][i] = otgetflag(boolcodes[i]);
if (verbose > 1) {
(void) fprintf(trace, "boolcodes=%s, ", boolcodes[i]);
(void) fprintf(trace, "boolnames=%s, ", boolnames[i]);
(void) fprintf(trace,
"flag=%d.\n", boolval[uselevel][i]);
}
}
/* numbers */
for (i = 0; numcodes[i]; i++) {
numval[uselevel][i] = otgetnum(numcodes[i]);
if (verbose > 1) {
(void) fprintf(trace, "numcodes=%s, ", numcodes[i]);
(void) fprintf(trace, "numnames=%s, ", numnames[i]);
(void) fprintf(trace, "num=%d.\n", numval[uselevel][i]);
}
}
if (uselevel == 0)
nextstring = capbuffer;
/* strings */
for (i = 0; strcodes[i]; i++) {
strval[uselevel][i] = otgetstr(strcodes[i], &nextstring);
if (verbose > 1) {
(void) fprintf(trace, "strcodes=%s, ", strcodes [i]);
(void) fprintf(trace, "strnames=%s, ", strnames [i]);
if (strval[uselevel][i]) {
(void) fprintf(trace, "str=");
tpr(trace, strval[uselevel][i]);
(void) fprintf(trace, ".\n");
}
else
(void) fprintf(trace, "str=NULL.\n");
}
/* remove zero length strings */
if (strval[uselevel][i] && (strval[uselevel][i][0] == '\0')) {
(void) fprintf(stderr,
"%s: TERM=%s: cap %s (info %s) is NULL: REMOVED\n",
progname, term_name, strcodes[i], strnames[i]);
strval[uselevel][i] = NULL;
}
}
/* Retrieve the values not found in terminfo anymore. */
/* booleans */
for (i = 0; oboolcodes[i]; i++) {
oboolval[uselevel][i] = otgetflag(oboolcodes[i]);
if (verbose > 1) {
(void) fprintf(trace, "oboolcodes=%s, ",
oboolcodes[i]);
(void) fprintf(trace, "flag=%d.\n",
oboolval[uselevel][i]);
}
}
/* numbers */
for (i = 0; onumcodes[i]; i++) {
onumval[uselevel][i] = otgetnum(onumcodes[i]);
if (verbose > 1) {
(void) fprintf(trace, "onumcodes=%s, ", onumcodes[i]);
(void) fprintf(trace, "num=%d.\n",
onumval[uselevel][i]);
}
}
/* strings */
for (i = 0; ostrcodes[i]; i++) {
ostrval[uselevel][i] = otgetstr(ostrcodes[i], &nextstring);
if (verbose > 1) {
(void) fprintf(trace, "ostrcodes=%s, ", ostrcodes[i]);
if (ostrval[uselevel][i]) {
(void) fprintf(trace, "ostr=");
tpr(trace, ostrval[uselevel][i]);
(void) fprintf(trace, ".\n");
}
else
(void) fprintf(trace, "ostr=NULL.\n");
}
/* remove zero length strings */
if (ostrval[uselevel][i] && (ostrval[uselevel][i][0] == '\0')) {
(void) fprintf(stderr,
"%s: TERM=%s: cap %s (no terminfo name) is NULL: "
"REMOVED\n", progname, term_name, ostrcodes[i]);
ostrval[uselevel][i] = NULL;
}
}
return (1);
}
/*
* This routine copies the set of names from the termcap entry into
* a separate buffer, getting rid of the old obsolete two character
* names.
*/
static void
getlongname(void)
{
char *b = &bp[0], *l = buflongname;
/* Skip the two character name */
if (bp[2] == '|')
b = &bp[3];
/* Copy the rest of the names */
while (*b && *b != ':')
*l++ = *b++;
*l = '\0';
if (b != &bp[0]) {
(void) fprintf(stderr, "%s: obsolete 2 character name "
"'%2.2s' removed.\n", progname, bp);
(void) fprintf(stderr, "\tsynonyms are: '%s'\n", buflongname);
}
}
/*
* Return the value of the termcap string 'capname' as stored in our list.
*/
char *
getcapstr(char *capname)
{
int i;
if (verbose > 1)
(void) fprintf(trace, "looking for termcap value of %s.\n",
capname);
/* Check the old termcap list. */
for (i = 0; ostrcodes[i]; i++)
if (strcmp(ostrcodes[i], capname) == 0) {
if (verbose > 1) {
(void) fprintf(trace, "\tvalue is:");
tpr(trace, ostrval[uselevel][i]);
(void) fprintf(trace, ".\n");
}
return (ostrval[uselevel][i]);
}
if (verbose > 1)
(void) fprintf(trace, "termcap name '%s' not found in "
"ostrcodes.\n", capname);
/* Check the terminfo list. */
for (i = 0; strcodes[i]; i++)
if (strcmp(strcodes[i], capname) == 0) {
if (verbose > 1) {
(void) fprintf(trace, "\tvalue is:");
tpr(trace, strval[uselevel][i]);
(void) fprintf(trace, ".\n");
}
return (strval[uselevel][i]);
}
(void) fprintf(stderr, "%s: TERM=%s: termcap name '%s' not found.\n",
progname, term_name, capname);
return ((char *)NULL);
}
/*
* Search for a name in the given table and
* return the index.
* Someday I'll redo this to use bsearch().
*/
/* ARGSUSED */
int
search(char *names[], int max, char *infoname)
{
#ifndef BSEARCH
int i;
for (i = 0; names [i] != NULL; i++)
if (strcmp(names [i], infoname) == 0)
return (i);
return (-1);
#else /* this doesn't work for some reason */
char **bret;
bret = (char **)bsearch(infoname, (char *)names, max,
sizeof (char *), strcmp);
(void) fprintf(trace, "search looking for %s.\n", infoname);
(void) fprintf(trace, "base=%#x, bret=%#x, nel=%d.\n", names,
bret, max);
(void) fprintf(trace, "returning %d.\n", bret == NULL ? -1 :
bret - names);
if (bret == NULL)
return (-1);
else
return (bret - names);
#endif /* OLD */
}
/*
* return the value of the terminfo string 'infoname'
*/
char *
getinfostr(char *infoname)
{
int i;
if (verbose > 1)
(void) fprintf(trace, "looking for terminfo value of %s.\n",
infoname);
i = search(strnames, strcount, infoname);
if (i != -1) {
if (verbose > 1) {
(void) fprintf(trace, "\tvalue is:");
tpr(trace, strval[uselevel][i]);
(void) fprintf(trace, ".\n");
}
return (strval[uselevel][i]);
}
if (verbose > 1)
(void) fprintf(trace, "terminfo name '%s' not found.\n",
infoname);
return ((char *)NULL);
}
/*
* Replace the value stored for the terminfo boolean
* capability 'infoname' with the newvalue.
*/
void
putbool(char *infoname, int newvalue)
{
int i;
if (verbose > 1)
(void) fprintf(trace, "changing value for %s to %d.\n",
infoname, newvalue);
i = search(boolnames, boolcount, infoname);
if (i != -1) {
if (verbose > 1)
(void) fprintf(trace, "value was: %d.\n",
boolval[uselevel][i]);
boolval[uselevel][i] = newvalue;
return;
}
(void) fprintf(stderr, "%s: TERM=%s: the boolean name '%s' was not "
"found!\n", progname, term_name, infoname);
}
/*
* Replace the value stored for the terminfo number
* capability 'infoname' with the newvalue.
*/
void
putnum(char *infoname, int newvalue)
{
int i;
if (verbose > 1)
(void) fprintf(trace, "changing value for %s to %d.\n",
infoname, newvalue);
i = search(numnames, numcount, infoname);
if (i != -1) {
if (verbose > 1)
(void) fprintf(trace, "value was: %d.\n",
numval[uselevel][i]);
numval[uselevel][i] = newvalue;
return;
}
(void) fprintf(stderr, "%s: TERM=%s: the numeric name '%s' was not "
"found!\n",
progname, term_name, infoname);
}
/*
* replace the value stored for the terminfo string capability 'infoname'
* with the newvalue.
*/
void
putstr(char *infoname, char *newvalue)
{
int i;
if (verbose > 1) {
(void) fprintf(trace, "changing value for %s to ", infoname);
tpr(trace, newvalue);
(void) fprintf(trace, ".\n");
}
i = search(strnames, strcount, infoname);
if (i != -1) {
if (verbose > 1) {
(void) fprintf(trace, "value was:");
tpr(trace, strval[uselevel][i]);
(void) fprintf(trace, ".\n");
}
strval[uselevel][i] = nextstring;
while (*newvalue)
*nextstring++ = *newvalue++;
*nextstring++ = '\0';
return;
}
(void) fprintf(stderr, "%s: TERM=%s: the string name '%s' was not "
"found!\n",
progname, term_name, infoname);
}
/*
* Add in extra delays if they are not recorded already.
* This is done before the padding information has been modified by
* changecalculations() below, so the padding information, if there
* already, is still at the beginning of the string in termcap format.
*/
void
addpadding(int cappadding, char *infostr)
{
char *cap;
char tempbuffer [100];
/* Is there padding to add? */
if (cappadding > 0)
/* Is there a string to add it to? */
if (cap = getinfostr(infostr))
/* Is there any padding info already? */
if (ispadchar(*cap)) {
/* EMPTY */;
/* Assume that the padding info that is there is correct. */
} else {
/* Add the padding at the end of the present string. */
(void) snprintf(tempbuffer, sizeof (tempbuffer),
"%s$<%d>", cap, cappadding);
putstr(infostr, tempbuffer);
} else {
/* Create a new string that only has the padding. */
(void) sprintf(tempbuffer, "$<%d>", cappadding);
putstr(infostr, tempbuffer);
}
}
struct
{
char *capname;
char *keyedinfoname;
} ko_map[] = {
"al", "kil1",
"bs", "kbs", /* special addition */
"bt", "kcbt",
"cd", "ked",
"ce", "kel",
"cl", "kclr",
"ct", "ktbc",
"dc", "kdch1",
"dl", "kdl1",
"do", "kcud1",
"ei", "krmir",
"ho", "khome",
"ic", "kich1",
"im", "kich1", /* special addition */
"le", "kcub1",
"ll", "kll",
"nd", "kcuf1",
"sf", "kind",
"sr", "kri",
"st", "khts",
"up", "kcuu1",
/* "", "kctab", */
/* "", "knp", */
/* "", "kpp", */
0, 0
};
/*
* Work with the ko string. It is a comma separated list of keys for which
* the keyboard has a key by the same name that emits the same sequence.
* For example, ko = dc, im, ei means that there are keys called
* delete-character, enter-insert-mode and exit-insert-mode on the keyboard,
* and they emit the same sequences as specified in the dc, im and ei
* capabilities.
*/
static void
handleko(void)
{
char capname[3];
char *capstr;
int i, j, found;
char *infostr;
if (verbose > 1)
(void) fprintf(trace, "working on termcap ko string.\n");
if (ostrval[uselevel][cap_ko] == NULL)
return;
capname[2] = '\0';
for (i = 0; ostrval[uselevel][cap_ko][i] != '\0'; ) {
/* isolate the termcap name */
capname[0] = ostrval[uselevel][cap_ko][i++];
if (ostrval[uselevel][cap_ko][i] == '\0')
break;
capname[1] = ostrval[uselevel][cap_ko][i++];
if (ostrval[uselevel][cap_ko][i] == ',')
i++;
if (verbose > 1) {
(void) fprintf(trace, "key termcap name is '");
tpr(trace, capname);
(void) fprintf(trace, "'.\n");
}
/* match it up into our list */
found = 0;
for (j = 0; !found && ko_map[j].keyedinfoname != NULL; j++) {
if (verbose > 1)
(void) fprintf(trace, "looking at termcap name %s.\n",
ko_map[j].capname);
if (capname[0] == ko_map[j].capname[0] &&
capname[1] == ko_map[j].capname[1]) {
/* add the value to our database */
if ((capstr = getcapstr(capname)) != NULL) {
infostr = getinfostr
(ko_map[j].keyedinfoname);
if (infostr == NULL) {
/* skip any possible padding */
/* information */
while (ispadchar(*capstr))
capstr++;
putstr(ko_map[j].keyedinfoname, capstr);
} else
if (strcmp(capstr, infostr) != 0) {
(void) fprintf(stderr,
"%s: TERM=%s: a function "
"key for '%s' was "
"specified with the "
"value ", progname,
term_name, capname);
tpr(stderr, capstr);
(void) fprintf(stderr,
", but it already has the "
"value '");
tpr(stderr, infostr);
(void) fprintf(stderr, "'.\n");
}
}
found = 1;
}
}
if (!found) {
(void) fprintf(stderr, "%s: TERM=%s: the unknown "
"termcap name '%s' was\n", progname, term_name,
capname);
(void) fprintf(stderr, "specified in the 'ko' "
"termcap capability.\n");
}
}
}
#define CONTROL(x) ((x) & 037)
struct
{
char vichar;
char *keyedinfoname;
} ma_map[] = {
CONTROL('J'), "kcud1", /* down */
CONTROL('N'), "kcud1",
'j', "kcud1",
CONTROL('P'), "kcuu1", /* up */
'k', "kcuu1",
'h', "kcub1", /* left */
CONTROL('H'), "kcub1",
' ', "kcuf1", /* right */
'l', "kcuf1",
'H', "khome", /* home */
CONTROL('L'), "kclr", /* clear */
0, 0
};
/*
* Work with the ma string. This is a list of pairs of characters.
* The first character is the what a function key sends. The second
* character is the equivalent vi function that should be done when
* it receives that character. Note that only function keys that send
* a single character could be defined by this list.
*/
void
prchar(FILE *stream, int c)
{
char xbuf[2];
xbuf[0] = c;
xbuf[1] = '\0';
(void) fprintf(stream, "%s", iexpand(xbuf));
}
static void
handlema(void)
{
char vichar;
char cap[2];
int i, j, found;
char *infostr;
if (verbose > 1)
(void) fprintf(trace, "working on termcap ma string.\n");
if (ostrval[uselevel][cap_ma] == NULL)
return;
cap[1] = '\0';
for (i = 0; ostrval[uselevel][cap_ma][i] != '\0'; ) {
/* isolate the key's value */
cap[0] = ostrval[uselevel][cap_ma][i++];
if (verbose > 1) {
(void) fprintf(trace, "key value is '");
tpr(trace, cap);
(void) fprintf(trace, "'.\n");
}
if (ostrval[uselevel][cap_ma][i] == '\0')
break;
/* isolate the vi key name */
vichar = ostrval[uselevel][cap_ma][i++];
if (verbose > 1) {
(void) fprintf(trace, "the vi key is '");
prchar(trace, vichar);
(void) fprintf(trace, "'.\n");
}
/* match up the vi name in our list */
found = 0;
for (j = 0; !found && ma_map[j].keyedinfoname != NULL; j++) {
if (verbose > 1) {
(void) fprintf(trace, "looking at vi "
"character '");
prchar(trace, ma_map[j].vichar);
(void) fprintf(trace, "'\n");
}
if (vichar == ma_map[j].vichar) {
infostr = getinfostr(ma_map[j].keyedinfoname);
if (infostr == NULL)
putstr(ma_map[j].keyedinfoname, cap);
else if (strcmp(cap, infostr) != 0) {
(void) fprintf(stderr, "%s: TERM=%s: "
"the vi character '", progname,
term_name);
prchar(stderr, vichar);
(void) fprintf(stderr,
"' (info '%s') has the value '",
ma_map[j].keyedinfoname);
tpr(stderr, infostr);
(void) fprintf(stderr, "', but 'ma' "
"gives '");
prchar(stderr, cap[0]);
(void) fprintf(stderr, "'.\n");
}
found = 1;
}
}
if (!found) {
(void) fprintf(stderr, "%s: the unknown vi key '",
progname);
prchar(stderr, vichar);
(void) fprintf(stderr, "' was\n");
(void) fprintf(stderr, "specified in the 'ma' termcap "
"capability.\n");
}
}
}
/*
* Many capabilities were defaulted in termcap which must now be explicitly
* given. We'll assume that the defaults are in effect for this terminal.
*/
void
adddefaults(void)
{
char *cap;
int sg;
if (verbose > 1)
(void) fprintf(trace, "assigning defaults.\n");
/* cr was assumed to be ^M, unless nc was given, */
/* which meant it could not be done. */
/* Also, xr meant that ^M acted strangely. */
if ((getinfostr("cr") == NULL) && !oboolval[uselevel][cap_nc] &&
!oboolval[uselevel][cap_xr])
if ((cap = getcapstr("cr")) == NULL)
putstr("cr", "\r");
else
putstr("cr", cap);
/* cursor down was assumed to be ^J if not specified by nl */
if (getinfostr("cud1") == NULL)
if (ostrval[uselevel][cap_nl] != NULL)
putstr("cud1", ostrval[uselevel][cap_nl]);
else
putstr("cud1", "\n");
/* ind was assumed to be ^J, unless ns was given, */
/* which meant it could not be done. */
if ((getinfostr("ind") == NULL) && !oboolval[uselevel][cap_ns])
if (ostrval[uselevel][cap_nl] == NULL)
putstr("ind", "\n");
else
putstr("ind", ostrval[uselevel][cap_nl]);
/* bel was assumed to be ^G */
if (getinfostr("bel") == NULL)
putstr("bel", "\07");
/* if bs, then could do backspacing, */
/* with value of bc, default of ^H */
if ((getinfostr("cub1") == NULL) && oboolval[uselevel][cap_bs])
if (ostrval[uselevel][cap_bc] != NULL)
putstr("cub1", ostrval[uselevel][cap_bc]);
else
putstr("cub1", "\b");
/* default xon to true */
if (!otgetflag("xo"))
putbool("xon", 1);
/* if pt, then hardware tabs are allowed, */
/* with value of ta, default of ^I */
if ((getinfostr("ht") == NULL) && oboolval[uselevel][cap_pt])
if ((cap = getcapstr("ta")) == NULL)
putstr("ht", "\t");
else
putstr("ht", cap);
/* The dX numbers are now stored as padding */
/* in the appropriate terminfo string. */
addpadding(onumval[uselevel][cap_dB], "cub1");
addpadding(onumval[uselevel][cap_dC], "cr");
addpadding(onumval[uselevel][cap_dF], "ff");
addpadding(onumval[uselevel][cap_dN], "cud1");
addpadding(onumval[uselevel][cap_dT], "ht");
/* The ug and sg caps were essentially identical, */
/* so ug almost never got used. We set sg from ug */
/* if it hasn't already been set. */
if (onumval[uselevel][cap_ug] >= 0 && (sg = otgetnum("sg")) < 0)
putnum("xmc", onumval[uselevel][cap_ug]);
else if ((onumval[uselevel][cap_ug] >= 0) &&
(sg >= 0) && (onumval[uselevel][cap_ug] != sg))
(void) fprintf(stderr,
"%s: TERM=%s: Warning: termcap sg and ug had different "
"values (%d<->%d).\n", progname, term_name, sg,
onumval[uselevel][cap_ug]);
/* The MT boolean was never really part of termcap, */
/* but we can check for it anyways. */
if (oboolval[uselevel][cap_MT] && !otgetflag("km"))
putbool("km", 1);
/* the rs string was renamed r2 (info rs2) */
if ((ostrval[uselevel][cap_rs] != NULL) &&
(ostrval[uselevel][cap_rs][0] != NULL))
putstr("rs2", ostrval[uselevel][cap_rs]);
handleko();
handlema();
}
#define caddch(x) *to++ = (x)
/*
* add the string to the string table
*/
char *
caddstr(char *to, char *str)
{
while (*str)
*to++ = *str++;
return (to);
}
/* If there is no padding info or parmed strings, */
/* then we do not need to copy the string. */
int
needscopying(char *string)
{
/* any string at all? */
if (string == NULL)
return (0);
/* any padding info? */
if (ispadchar(*string))
return (1);
/* any parmed info? */
while (*string)
if (*string++ == '%')
return (1);
return (0);
}
/*
* Certain manipulations of the stack require strange manipulations of the
* values that are on the stack. To handle these, we save the values of the
* parameters in registers at the very beginning and make the changes in
* the registers. We don't want to do this in the general case because of the
* potential performance loss.
*/
int
fancycap(char *string)
{
int parmset = 0;
while (*string)
if (*string++ == '%') {
switch (*string) {
/* These manipulate just the top value on */
/* the stack, so we only have to do */
/* something strange if a %r follows. */
case '>': case 'B': case 'D':
parmset = 1;
break;
/* If the parm has already been been */
/* pushed onto the stack by %>, then we */
/* can not reverse the parms and must get */
/* them from the registers. */
case 'r':
if (parmset)
return (1);
break;
/* This manipulates both parameters, so we */
/* cannot just do one and leave the value */
/* on the stack like we can with %>, */
/* %B or %D. */
case 'n':
return (1);
}
string++;
}
return (0);
}
/*
* Change old style of doing calculations to the new stack style.
* Note that this will not necessarily produce the most efficient string,
* but it will work.
*/
void
changecalculations()
{
int i, currentparm;
char *from, *to = nextstring;
int ch;
int parmset, parmsaved;
char padding[100], *saveto;
for (i = 0; strnames[i]; i++)
if (needscopying(strval[uselevel][i])) {
if (verbose) {
(void) fprintf(trace, "%s needs copying, "
"was:", strnames [i]);
tpr(trace, strval[uselevel][i]);
(void) fprintf(trace, ".\n");
}
from = strval[uselevel][i];
strval[uselevel][i] = to;
currentparm = 1;
parmset = 0;
/* Handle padding information. Save it so that it can be */
/* placed at the end of the string where it should */
/* have been in the first place. */
if (ispadchar(*from)) {
saveto = to;
to = padding;
to = caddstr(to, "$<");
while (isdigit(*from) || *from == '.')
caddch(*from++);
if (*from == '*')
caddch(*from++);
caddch('>');
caddch('\0');
to = saveto;
} else
padding[0] = '\0';
if (fancycap(from)) {
to = caddstr(to, "%p1%Pa%p2%Pb");
parmsaved = 1;
(void) fprintf(stderr,
"%s: TERM=%s: Warning: the string "
"produced for '%s' may be inefficient.\n",
progname, term_name, strnames[i]);
(void) fprintf(stderr, "It should be "
"looked at by hand.\n");
} else
parmsaved = 0;
while ((ch = *from++) != '\0')
if (ch != '%')
caddch(ch);
else
switch (ch = *from++) {
case '.': /* %. -> %p1%c */
case 'd': /* %d -> %p1%d */
case '2': /* %2 -> %p1%2.2d */
case '3': /* %3 -> %p1%3.3d */
case '+':
/* %+x -> %p1%'x'%+%c */
case '>':
/* %>xy -> %p1%Pc%?%'x'%> */
/* %t%gc%'y'%+ */
/* if current value > x, then add y. */
/* No output. */
case 'B':
/* %B: BCD */
/* (16*(x/10))+(x%10) */
/* No output. */
/* (Adds Regent 100) */
case 'D':
/* %D: Reverse coding */
/* (x-2*(x%16)) */
/* No output. */
/* (Delta Data) */
if (!parmset)
if (parmsaved) {
to = caddstr(to, "%g");
if (currentparm == 1)
caddch('a');
else
caddch('b');
} else {
to = caddstr(to, "%p");
if (currentparm == 1)
caddch('1');
else
caddch('2');
}
currentparm = 3 - currentparm;
parmset = 0;
switch (ch) {
case '.':
to = caddstr(to, "%c");
break;
case 'd':
to = caddstr(to, "%d");
break;
case '2': case '3':
#ifdef USG /* Vr2==USG, Vr3==SYSV. Use %02d for Vr2, %2.2d for Vr3 */
caddch('%');
caddch('0');
#else
caddch('%');
caddch(ch);
caddch('.');
#endif /* USG vs. SYSV */
caddch(ch);
caddch('d');
break;
case '+':
to = caddstr(to, "%'");
caddch(*from++);
to = caddstr(to,
"'%+%c");
break;
case '>':
to = caddstr(to,
"%Pc%?%'");
caddch(*from++);
to = caddstr(to,
"'%>%t%gc%'");
caddch(*from++);
to = caddstr(to,
"'%+");
parmset = 1;
break;
case 'B':
to = caddstr(to,
"%Pc%gc%{10}%/%{16}%*%gc%{10}%m%+");
parmset = 1;
break;
case 'D':
to = caddstr(to,
"%Pc%gc%gc%{16}%m%{2}%*%-");
parmset = 1;
break;
}
break;
/* %r reverses current parameter */
case 'r':
currentparm = 3 - currentparm;
break;
/* %n: exclusive-or row AND column */
/* with 0140, 96 decimal, no output */
/* (Datamedia 2500, Exidy Sorceror) */
case 'n':
to = caddstr(to,
"%ga%'`'%^%Pa");
to = caddstr(to,
"%gb%'`'%^%Pb");
break;
/* assume %x means %x */
/* this includes %i and %% */
default:
caddch('%');
caddch(ch);
}
to = caddstr(to, padding);
caddch('\0');
if (verbose) {
(void) fprintf(trace, "and has become:");
tpr(trace, strval[uselevel][i]);
(void) fprintf(trace, ".\n");
}
}
nextstring = to;
}
static void
print_no_use_entry(void)
{
int i;
pr_heading("", buflongname);
pr_bheading();
for (i = 0; boolcodes[i]; i++)
if (boolval[0][i])
pr_boolean(boolnames[i], (char *)0, (char *)0, 1);
pr_bfooting();
pr_sheading();
for (i = 0; numcodes[i]; i++)
if (numval[0][i] > -1)
pr_number(numnames[i], (char *)0, (char *)0,
numval[0][i]);
pr_nfooting();
pr_sheading();
for (i = 0; strcodes[i]; i++)
if (strval[0][i])
pr_string(strnames[i], (char *)0, (char *)0,
strval[0][i]);
pr_sfooting();
}
static void
print_use_entry(char *usename)
{
int i;
pr_heading("", buflongname);
pr_bheading();
for (i = 0; boolcodes[i]; i++)
if (boolval[0][i] && !boolval[1][i])
pr_boolean(boolnames[i], (char *)0, (char *)0, 1);
else if (!boolval[0][i] && boolval[1][i])
pr_boolean(boolnames[i], (char *)0, (char *)0, -1);
pr_bfooting();
pr_nheading();
for (i = 0; numcodes[i]; i++)
if ((numval[0][i] > -1) && (numval[0][i] != numval[1][i]))
pr_number(numnames[i], (char *)0, (char *)0,
numval[0][i]);
else if ((numval [0] [i] == -1) && (numval [1] [i] > -1))
pr_number(numnames[i], (char *)0, (char *)0, -1);
pr_nfooting();
pr_sheading();
for (i = 0; strcodes[i]; i++)
/* print out str[0] if: */
/* str[0] != NULL and str[1] == NULL, or str[0] != str[1] */
if (strval[0][i] && ((strval[1][i] == NULL) ||
(strcmp(strval[0][i], strval[1][i]) != 0)))
pr_string(strnames[i], (char *)0, (char *)0,
strval[0][i]);
/* print out @ if str[0] == NULL and str[1] != NULL */
else if (strval[0][i] == NULL && strval[1][i] != NULL)
pr_string(strnames[i], (char *)0, (char *)0,
(char *)0);
pr_sfooting();
(void) printf("\tuse=%s,\n", usename);
}
static void
captoinfo(void)
{
char usename[512];
char *sterm_name;
if (term_name == NULL) {
(void) fprintf(stderr, "%s: Null term_name given.\n",
progname);
return;
}
if (verbose)
(void) fprintf(trace, "changing cap to info, TERM=%s.\n",
term_name);
uselevel = 0;
if (filltables() == 0)
return;
getlongname();
adddefaults();
changecalculations();
if (TLHtcfound != 0) {
uselevel = 1;
if (verbose)
(void) fprintf(trace, "use= found, %s uses %s.\n",
term_name, TLHtcname);
(void) strcpy(usename, TLHtcname);
sterm_name = term_name;
term_name = usename;
if (filltables() == 0)
return;
adddefaults();
changecalculations();
term_name = sterm_name;
print_use_entry(usename);
} else
print_no_use_entry();
}
#include <signal.h> /* use this file to determine if this is SVR4.0 system */
static void
use_etc_termcap(void)
{
if (verbose)
#ifdef SIGSTOP
(void) fprintf(trace, "reading from /usr/share/lib/termcap\n");
#else /* SIGSTOP */
(void) fprintf(trace, "reading from /etc/termcap\n");
#endif /* SIGSTOP */
term_name = getenv("TERM");
captoinfo();
}
static void
initdirname(void)
{
#if defined(SYSV) || defined(USG) /* handle both Sys Vr2 and Vr3 curses */
(void) getcwd(dirname, BUFSIZ-2);
#else
(void) getwd(dirname);
#endif /* SYSV || USG */
if (verbose)
(void) fprintf(trace, "current directory name=%s.\n", dirname);
environ = newenviron;
}
static void
setfilename(char *capfile)
{
if (capfile [0] == '/')
(void) snprintf(TERMCAP, sizeof (TERMCAP),
"TERMCAP=%s", capfile);
else
(void) snprintf(TERMCAP, sizeof (TERMCAP),
"TERMCAP=%s/%s", dirname, capfile);
if (verbose)
(void) fprintf(trace, "setting the environment for %s.\n",
TERMCAP);
}
static void
setterm_name(void)
{
if (verbose)
(void) fprintf(trace, "setting the environment "
"for TERM=%s.\n", term_name);
(void) snprintf(TERM, sizeof (TERM), "TERM=%s", term_name);
}
/* Look at the current line to see if it is a list of names. */
/* If it is, return the first name in the list, else NULL. */
/* As a side-effect, comment lines and blank lines */
/* are copied to standard output. */
char *
getterm_name(char *line)
{
char *lineptr = line;
if (verbose)
(void) fprintf(trace, "extracting name from '%s'.\n", line);
/* Copy comment lines out. */
if (*line == '#') {
if (copycomments)
(void) printf("%s", line);
}
/* Blank lines get copied too. */
else if (isspace (*line)) {
if (copycomments) {
for (; *lineptr; lineptr++)
if (!isspace(*lineptr))
break;
if (*lineptr == '\0')
(void) printf("\n");
}
}
else
for (; *lineptr; lineptr++)
if (*lineptr == '|' || *lineptr == ':') {
*lineptr = '\0';
if (verbose)
(void) fprintf(trace,
"returning %s.\n", line);
return (line);
}
if (verbose)
(void) fprintf(trace, "returning NULL.\n");
return (NULL);
}
static void
use_file(char *filename)
{
FILE *termfile;
char buffer[BUFSIZ];
if (verbose)
(void) fprintf(trace, "reading from %s.\n", filename);
if ((termfile = fopen(filename, "r")) == NULL) {
(void) fprintf(stderr, "%s: cannot open %s for reading.\n",
progname, filename);
return;
}
copycomments++;
setfilename(filename);
while (fgets(buffer, BUFSIZ, termfile) != NULL) {
if ((term_name = getterm_name(buffer)) != NULL) {
setterm_name();
captoinfo();
}
}
}
/*
* Sort a name and code table pair according to the name table.
* Use a simple bubble sort for now. Too bad I can't call qsort(3).
* At least I only have to do it once for each table.
*/
static void
sorttable(char *nametable[], char *codetable[])
{
int i, j;
char *c;
for (i = 0; nametable[i]; i++)
for (j = 0; j < i; j++)
if (strcmp(nametable[i], nametable[j]) < 0) {
c = nametable[i];
nametable[i] = nametable[j];
nametable[j] = c;
c = codetable[i];
codetable[i] = codetable[j];
codetable[j] = c;
}
}
/*
* Initialize and sort the name and code tables. Allocate space for the
* value tables.
*/
static void
inittables(void)
{
unsigned int i;
for (i = 0; boolnames [i]; i++)
;
boolval[0] = (char *)malloc(i * sizeof (char));
boolval[1] = (char *)malloc(i * sizeof (char));
boolcount = i;
sorttable(boolnames, boolcodes);
for (i = 0; numcodes [i]; i++)
;
numval[0] = (short *)malloc(i * sizeof (short));
numval[1] = (short *)malloc(i * sizeof (short));
numcount = i;
sorttable(numnames, numcodes);
for (i = 0; strcodes [i]; i++)
;
strval[0] = (char **)malloc(i * sizeof (char *));
strval[1] = (char **)malloc(i * sizeof (char *));
strcount = i;
sorttable(strnames, strcodes);
}
int
main(int argc, char **argv)
{
int c;
char _capbuffer [8192];
char _bp [TBUFSIZE];
char _buflongname [128];
capbuffer = &_capbuffer[0];
bp = &_bp[0];
buflongname = &_buflongname[0];
progname = argv[0];
while ((c = getopt(argc, argv, "1vVw:")) != EOF)
switch (c) {
case '1':
pr_onecolumn(1);
break;
case 'w':
pr_width(atoi(optarg));
break;
case 'v':
verbose++;
break;
case 'V':
(void) printf("%s: version %s\n", progname,
"@(#)curses:screen/captoinfo.c 1.12");
(void) fflush(stdout);
exit(0);
/* FALLTHROUGH (not really) */
case '?':
(void) fprintf(stderr,
"usage: %s [-1Vv] [-w width] "
"[filename ...]\n", progname);
(void) fprintf(stderr, "\t-1\tsingle column "
"output\n");
(void) fprintf(stderr,
"\t-v\tverbose debugging output\n");
(void) fprintf(stderr,
"\t-V\tprint program version\n");
exit(-1);
}
/* initialize */
pr_init(pr_terminfo);
inittables();
if (optind >= argc)
use_etc_termcap();
else {
initdirname();
for (; optind < argc; optind++)
use_file(argv [optind]);
}
return (0);
}
/* fake out the modules in print.c so we don't have to load in */
/* cexpand.c and infotocap.c */
/* ARGSUSED */
int
cpr(FILE *stream, char *string)
{
return (0);
}