[mod.sources] untic - decompile terminfo description file.

sources-request@panda.UUCP (05/23/86)

Mod.sources:  Volume 5, Issue 15
Submitted by: Dave Regan <talcott!cmcl2!hp-pcd!orstcs!regan>

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	untic.1
#	Makefile
#	untic.c
# This archive created: Wed May 21 23:00:29 1986
export PATH; PATH=/bin:$PATH
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
untic -- Decompile a "terminfo" terminal description file for System V.

I wrote the following program, "untic", because I needed to make a terminal
description file which was just slightly different from one of the existing
terminal descriptions.

This program looks up the description directly from the compiled description
file.  Thus it produces a file, which when compiled, generated exactly the
same information as was in the original file.

This "shar" file contains the following files:
	README		This file
	untic.1		A (very simple) manual page
	Makefile	A (very simple) make file
	untic.c		The program

I hope this program is of some use to people.

			Dave Regan
			21 May 1986
			PO Box 601
			Corvallis OR 97339
			hplabs!hp-pcd!orstcs!regan	(I think)
SHAR_EOF
fi # end of overwriting check
if test -f 'untic.1'
then
	echo shar: will not over-write existing file "'untic.1'"
else
cat << \SHAR_EOF > 'untic.1'
.TH UNTIC 1 Public Domain
.SH NAME
untic \- Uncompile terminfo terminal description files
.SH SYNOPSIS
.B untic
.B terminal_name
[
.B . . .
]
.SH DESCRIPTION
.B untic
convert the terminfo file which corresponds to the specified terminal name
into a file that can be processed by
.B tic.
.PP
If multiple terminal names are given, a terminal description is generated
for each of the named terminals.
The output is written on standard output.
.PP
It is wise to backup the original terminal description
(from /usr/lib/terminfo)
before compiling the generated file.
.SH EXAMPLES
To uncompile the terminal description for a "vt100", type:
.RS
untic vt100 >vt100
.RE
To compile this generated file, you probably need root permissions and type:
.RS
tic vt100
.RE
.SH NOTES
If new capabilities are added to the terminfo database,
the
.B untic
program will need to be modified.
With luck, the new entries will go at the end of the appropriate tables.
.PP
Most of the capabilities are in the tables in the order that the capabilites
are documented.
There are a few exceptions in the string variables.
.PP
This program is placed into the public domain.
That means that you can do anything you want to with it.
.SH BUGS
None known, but . . .
.SH FILES
The terminal descriptions are stored in
.B /usr/lib/terminfo/?/*
SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
CFLAGS = -O
DESTDIR = /usr/local/bin
DOCDIR = /usr/local/man/man1

untic:	untic.c
	$(CC) $(CFLAGS) untic.c -o untic

lint:
	lint untic.c

install:	untic
	cp untic $(DESTDIR)/untic
	strip $(DESTDIR)/untic
#	cp untic.1 $(DOCDIR)/untic.1

clean:
	rm untic
SHAR_EOF
fi # end of overwriting check
if test -f 'untic.c'
then
	echo shar: will not over-write existing file "'untic.c'"
else
cat << \SHAR_EOF > 'untic.c'
/*
 * Untic.c -- Uncompile a terminfo file
 *
 * Usage:
 *	untic terminal_name . . .
 *
 * This program finds the terminal description in
 *	/usr/lib/terminfo/?/terminal_name
 * It then converts the information into an ASCII file suitable for
 * running into "tic".  The resulting file is written to standard output.
 *
 * Compile by:
 *	cc -O -o untic untic.c
 *
 * It is probably a good idea to ensure that the file produced will compile
 * to the original file before trusting "untic".
 *
 * Structure of terminfo file:
 *	short	magic number
 *	short	length of terminal names + NUL
 *	short	length of booleans
 *	short	length of numerics
 *	short	length of strings
 *	short	length of string table
 *	chars	NUL terminated terminal name
 *	chars	boolean flags for each of the possible booleans
 *	shorts	values for each of the possible numerics
 *	shorts	offsets (from start of strings) for each of the
 *		possible strings
 *	chars	NUL terminated strings for each of the defined strings
 *
 * Most of the variables are in the order that the documentation lists
 * them.  This is important, as the information is stored in the file
 * based upon the ordinal position of the variable.  Some of the string
 * variables are not in order.  Presumably, if they add more variables,
 * it will be to the end of the list, and not in the middle.
 *
 * This has been tested on
 *	Plexus P20 (M68010), System 5 Release 2 (I think)
 *
 * Bugs:
 *	The longest string capability is limited to 4096 bytes.  If a longer
 *	string is encountered, the program will do unpredicatable things.
 *	(Who uses strings that long anyway?)  The longest that the terminfo
 *	file can be is 4096 bytes anyway, so this isn't too big a problem.
 *
 * Credits:
 *	Written by Dave Regan
 *	orstcs!regan
 *	16 May 86
 *
 *	I disclaim that this program does anything useful.  It might also
 *	do accidental damage.  Backup your original terminfo files.
 *
 *	This program is public domain.  That means you can do anything
 *	you want with it.
 */


#include <stdio.h>
#ifdef QC
#define	OPEN_MODE	"rb"	/* Mode used to open terminfo files	*/
#define	void	int		/* Sigh . . .				*/
#else		/* QC */
#include <ctype.h>
#define	OPEN_MODE	"r"	/* Mode used to open terminfo files	*/
#endif		/* QC */
#ifndef TRUE
#define	TRUE	1
#define	FALSE	0
#endif		/* TRUE */

#define	DEBUG	FALSE		/* TRUE/FALSE to enable debugging output */

#ifdef BSD
#define	TERMINFO_DIR	"/usr/static/sys5r2v2/usr/lib/terminfo"
#endif		/* BSD */
#ifndef TERMINFO_DIR
#define	TERMINFO_DIR	"/usr/lib/terminfo"
#endif		/* TERMINFO_DIR */


#define	MAGIC	0x011A		/* Terminfo magic number		*/
#define	MAXLINE	65		/* Longest emited line			*/
#define	MAX_CAP	4096		/* Longest single capability		*/

extern	char *strcpy();

int	line_len;		/* Current length of line		*/

main(argc, argv)
  int	argc;			/* Number of paramters			*/
  char	*argv[];		/* The parameters themselves		*/
    {
    char subdir[2];		/* Subdirectory name			*/
    FILE *file;
    extern FILE *fopen();

#ifdef CPM
    wildexp(&argc, &argv);
#endif		/* CPM */

    /* Change directory to the working directory			*/
    (void)chdir(TERMINFO_DIR);

    /* Go through the arguments						*/
    subdir[1] = '\0';
    if (argc == 1)
	convert(stdin, "stdin");
    else
	{
	while (--argc)
	    {
	    ++argv;
	    subdir[0] = argv[0][0];
	    (void)chdir(subdir);
	    if ((file = fopen(*argv, OPEN_MODE)) == NULL)
		{
		perror(*argv);
		}
	    else
		{
		convert(file, *argv);
		(void)fclose(file);
		}
	    (void)chdir("..");
	    }
	}
    }


/*
 * Addchar -- Add a character
 */
char *
addchar(cptr, ch)
  char	*cptr;
  int	ch;
    {
    char *addstr(), *addoctal();

    if (ch == 0x1B)
	return (addstr(cptr, "\\E"));
    if (ch == '\n')
	return (addstr(cptr, "\\n"));
    if (ch == '\r')
	return (addstr(cptr, "\\r"));
    if (ch == '\t')
	return (addstr(cptr, "\\t"));
    if (ch == '\b')
	return (addstr(cptr, "\\b"));
    if (ch == '\f')
	return (addstr(cptr, "\\f"));
    if (ch == ' ')
	return (addstr(cptr, "\\s"));
    if (ch == '^')
	return (addstr(cptr, "\\^"));
    if (ch == '\\')
	return (addstr(cptr, "\\\\"));
    if (ch == ',')
	return (addstr(cptr, "\\,"));
    if (ch == ':')
	return (addstr(cptr, "\\:"));
    if (ch >= ('A' - '@') && ch <= ('Z' - '@'))
	{
	*cptr++ = '^';
	*cptr++ = ch + '@';
	return (cptr);
	}
#ifdef notdef
    /*
     * Did you know that \r \n \f \t are NOT control characters
     * as defined by "iscntrl" under BSD 4.2?  I find that
     * rather odd.
     */
#endif		/* notdef */
    if (iscntrl(ch) || isspace(ch) || ch > 0x7F)
    	return (addoctal(cptr, ch));
    *cptr++ = ch;
    return (cptr);
    }


/*
 * Addoctal -- Add an octal character
 *
 * Use sprintf just in case "0" through "7" are not contiguous.  Some
 * machines are weird.
 */
char *
addoctal(cptr, ch)
  char	*cptr;
  int	ch;
    {
    char *addstr();

    ch &= 0xFF;

    if (ch == 0x80)
	return (addstr(cptr, "\\0"));
    (void)sprintf(cptr, "\\%03o", ch);
    while (*cptr != '\0')
	cptr++;
    return (cptr);
    }


/*
 * Addstr -- Add a string to the capability
 */
char *
addstr(cptr, str)
  char	*cptr, *str;
    {
    while (*str)
	*cptr++ = *str++;
    return (cptr);
    }


/*
 * Convert -- Do the actual conversion
 */
convert(file, name)
  FILE	*file;		/* The file with the compiled information	*/
  char	*name;		/* Printable version of the filename		*/
    {
    int	ch, val, i, j, fail;
    int name_length, bool_length, num_length, str_length, s_length;
    char capability[MAX_CAP+1], *cptr, *addchar();

    static char *booleans[] =	/* Names of boolean variables, in order	*/
	{ "bw", "am", "xsb", "xhp", "xenl", "eo", "gn", "hc", "km",
	  "hs", "in", "da", "db", "mir", "msgr", "os", "eslok", "xt",
	  "hz", "ul", "xon" };
    static char *numerics[] =	/* Names of numeric variables, in order	*/
	{ "cols", "it", "lines", "lm", "xmc", "pb", "vt", "wsl" };
    static char *strings[] =	/* Names of string variables, not in strict
				   order.  Makes things a little harder	*/
	{ "cbt", "bel", "cr", "csr", "tbc", "clear", "el", "ed", "hpa",
	  "cmdch", "cup", "cud1", "home", "civis", "cub1", "mrcup",
	  "cnorm", "cuf1", "ll", "cuu1", "cvvis", "dch1", "dl1", "dsl",
	  "hd", "smacs", "blink", "bold", "smcup", "smdc", "dim", "smir",
	  "invis", "prot", "rev", "smso", "smul", "ech", "rmacs", "sgr0", 
	  "rmcup", "rmdc", "rmir", "rmso", "rmul", "flash", "ff", "fsl",
	  "is1", "is2", "is3", "if", "ich1", "il1", "ip", "kbs", "ktbc",
	  "kclr", "kctab", "kdch1", "kdl1", "kcud1", "krmir", "kel", "ked",
	  "kf0", "kf1", "kf10", "kf2", "kf3", "kf4", "kf5", "kf6", "kf7",
	  "kf8", "kf9", "khome", "kich1", "kil1", "kcub1", "kll", "knp",
	  "kpp", "kcuf1", "kind", "kri", "khts", "kcuu1", "rmkx", "smkx", 
	  "lf0", "lf1", "lf10", "lf2", "lf3", "lf4", "lf5", "lf6", "lf7",
	  "lf8", "lf9", "rmm", "smm", "nel", "pad", "dch", "dl", "cud",
	  "ich", "indn", "il", "cub", "cuf", "rin", "cuu", "pfkey", "pfloc",
	  "pfx", "mc0", "mc4", "mc5", "rep", "rs1", "rs2", "rs3", "rf",
	  "rc", "vpa", "sc", "ind", "ri", "sgr", "hts", "wind", "ht", "tsl",
	  "uc", "hu", "iprog", "ka1", "ka3", "kb2", "kc1", "kc3", "mc5p" };
    int str_cap[sizeof(strings) / sizeof(char *)];

    /* Check the magic number out					*/
    if (get2(file) != MAGIC)
	{
	fprintf(stderr, "\"%s\" is not a terminfo file\n", name);
	return;
	}

    /* Get the rest of the header information				*/
    name_length = get2(file);	/* Get the length of the terminal names	*/
    bool_length = get2(file);	/* Get the length of the booleans	*/
    num_length = get2(file);	/* Get the length of the numerics	*/
    str_length = get2(file);	/* Get the length of the strings	*/
    s_length = get2(file);	/* Get the length of the string tables	*/

    /* Check for too many data items					*/
    fail = FALSE;
    if (bool_length > (sizeof(booleans) / sizeof(char *)))
	{
	fprintf(stderr, "Boolean variables have been added to terminfo.\n");
	fail = TRUE;
	}
    if (num_length > (sizeof(numerics) / sizeof(char *)))
	{
	fprintf(stderr, "Numeric variables have been added to terminfo.\n");
	fail = TRUE;
	}
    if (str_length > (sizeof(strings) / sizeof(char *)))
	{
	fprintf(stderr, "String variables have been added to terminfo.\n");
	fail = TRUE;
	}
    if (fail)
	{
	fprintf(stderr,
"Update the \"untic\" program.  Use \"xxx\" if needed.  Good luck.\n");
	return;
	}

    /* Time to get real information					*/
    while ((ch = getc(file)) != '\0' && ch != EOF)
	putchar(ch);
    printf(",\n\t");

    /* Send out the non-null boolean variables				*/
    line_len = 0;
    for (i = 0; i < bool_length; i++)
	{
	if ((ch = getc(file)) != 0)
	    emit(booleans[i]);
	}

    /* The rest of the file is on a 16 bit boundary, so adjust the file	*/
    if ((name_length + bool_length) & 0x01)
	(void)getc(file);

    /* Get the numeric variables					*/
    for (i = 0; i < num_length; i++)
	{
	if ((val = get2(file)) != 0xFFFF)
	    {
	    (void)sprintf(capability, "%s#%d", numerics[i], val);
	    emit(capability);
	    }
	}

    /* Get the string variables offsets					*/
    for (i = 0; i < str_length; i++)
	str_cap[i] = get2(file);

    /* Get the string variables themselves				*/
    for (i = 0; i < s_length; i++)
	{
	for (j = 0; j < str_length; j++)	/* Find the name	*/
	    if (str_cap[j] == i)
		break;
#if DEBUG
	if (j >= str_length)
	    fprintf(stderr, "Cannot find address %d\n", i);
#endif		/* DEBUG */
	(void)strcpy(capability, strings[j]);
	cptr = &capability[strlen(capability)];
	*cptr++ = '=';
	for (; (ch = getc(file)) != '\0' && ch != EOF; i++)
	    cptr = addchar(cptr, ch);
	*cptr = '\0';
	emit(capability);
	}

    printf("\n\n");
    }


/*
 * Emit -- Emit the string
 *
 * Emit the given string, and append a comma.  If the line gets too long,
 * send out a newline and a tab.
 */
emit(str)
  char	*str;		/* String to emit				*/
    {
    if ((line_len += strlen(str) + 2) > MAXLINE)
	{
	line_len = strlen(str) + 2;
	printf("\n\t");
	}
    printf("%s, ", str);
    }


/*
 * Get2 -- Get a two byte number
 */
get2(file)
  FILE *file;		/* The file with the compiled information	*/
    {
    short temp;

    temp = getc(file) & 0xFF;
    return (temp + (getc(file) << 8));
    }


#ifdef CPM
chdir(str)
  char	*str;
    {
    }

perror(str)
  char	*str;
    {
    fprintf(stderr, "Cannot open \"%s\"\n", str);
    }
#endif		/* CPM */
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0