[comp.os.vms] Unix termcap to VMS termtable converter 2/3

forrest@CSA4.LBL.GOV (07/14/88)

$Part2:
$ File_is="TC2TT.C"
$ Check_Sum_is=16860395
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X/*
X
X	TC2TT Version 0.1 - Converts Unix (*tm) termcap files into VMS (*tm)
X	TERMTABLE.TXT files. See accompanying README.TC2TT for more
X	information.
X
X	This program may not be sold. Otherwise it is in the public
X	domain.
X
X	Compile and link this program like any other Vax C program.
X	It requires no special linking parameters.
X
X	Jon Forrest
X	Lawrence Berkeley Lab
X	FORREST@LBL.GOV
X	ucbvax!lbl-csam!ux1!forrest
X
X*/
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include "table.h"
X
X#ifdef VMS
X#define TERMCAP		"termcap."
X#define ERRLOG		"errlog."
X#else
X#define TERMCAP		"termcap"
X#define ERRLOG		"errlog"
X#endif
X
X#define LINE_LEN	256
X#define TERMNAME_LEN	64		/* max len of terminal name */
X#define	LONGNAME	2		/* long terminal name */
X#define EOL		((char *)1)
X#define NO_TC		0
X#define	TC		1
X#define	FOUND		1
X
X/*
X** The following two lines are global only because of a bug in the
X** VMS Debugger in VMS 4.7 which doesn't allow local character arrays to be
X** examined.
X*/
X
Xchar input_line1[LINE_LEN];
Xchar input_line2[LINE_LEN];
Xchar term_name[TERMNAME_LEN];		/* official name of current terminal */
XFILE *ifp, *ofp, *efp;
XFILE *tcfp = NULL;
Xint do_end = 0;				/* don't output END 1st time */
X
Xmain()
X{
X
X	if ((ifp = fopen(TERMCAP,"r")) == NULL)
X	{
X		perror(TERMCAP);
X		exit(1);
X	}
X	if ((ofp = fopen("termtable.txt","w")) == NULL)
X	{
X		perror("termtable.txt");
X		exit(1);
X	}
X	if ((efp = fopen(ERRLOG,"w")) == NULL)
X	{
X		perror(ERRLOG);
X		exit(1);
X	}
X
X	while (do_entry(ifp, input_line1, NO_TC) != EOF);
X	if (do_end)
X		fprintf(ofp,"END\n");
X	fclose(ifp);
X	fclose(ofp);
X	fclose(efp);
X	if (tcfp != NULL)
X		fclose(tcfp);
X}
X
X/*
X** Process an entry for a terminal in termcap. This might be because
X** we're processing a new entry or because we're looking for a terminal
X** specified in a tc capability. The actual purpose is shown in the
X** third argument.
X*/
X
Xdo_entry(ifp, input_line, purpose)
X	FILE *ifp;
X	char *input_line;
X	int purpose;
X{
X	register char *beg_cap;
X	register struct table_item *t_ptr;
X	char curr_term_name[TERMNAME_LEN];
X	char *get_next_cap();
X	struct table_item *binary();
X
X	for (;;)
X	{
X		if (fgets(input_line, LINE_LEN, ifp) == NULL)
X			return(EOF);
X
X		/* ignore comment lines */
X		if (*input_line == '#')
X			continue;
X
X		beg_cap = input_line;
X
X		/*
X		** If we're not finding a terminal as a result of a
X		** tc command and we're at the first line in an entry
X		*/
X
X		if ((purpose != TC) && (!isspace(*beg_cap)))
X		{
X			char *curr_ptr, *term_ptr;
X			int did_dash, did_plus;
X
X			if (do_end)
X				fprintf(ofp,"END\n");
X
X			/* get the terminal name */
X			get_term_name(beg_cap,curr_term_name,LONGNAME);
X			curr_ptr = curr_term_name;
X			term_ptr = term_name;
X			did_dash = did_plus = 0;
X			while (*curr_ptr)
X				switch (*curr_ptr)
X				{
X				case '-':
X					curr_ptr++;
X					*term_ptr++ = '_';
X					if (did_dash == 0)
X					{
X						fprintf(efp,"%s : Changed dash(s) to '_'\n",curr_term_name);
X						did_dash = 1;
X					}
X					break;
X				case '+':
X					curr_ptr++;
X					*term_ptr++ = 'p';
X					*term_ptr++ = 'l';
X					*term_ptr++ = 'u';
X					*term_ptr++ = 's';
X					if (did_plus == 0)
X					{
X						fprintf(efp,"%s : Changed + to plus\n",curr_term_name);
X						did_plus = 1;
X					}
X					break;
X				default:
X					*term_ptr++ = *curr_ptr++;
X					break;
X				}
X			*term_ptr = '\0';
X
X			fprintf(ofp,"NAME = \"%s\"\n",term_name);
X			do_end = 1;
X		}
X
X		/* get next cap on the line, if any left */
X		while ((beg_cap = get_next_cap(beg_cap)) != EOL)
X		{
X			/*
X			** If the cap is a "tc" then we have to
X			** recursively call do_entry to put together
X			** all the information for the original term
X			*/
X
X			if (strncmp(beg_cap,"tc",2) == 0)
X			{
X				if (find_term(beg_cap) == FOUND)
X					do_entry(tcfp, input_line2, TC);
X				else
X					fprintf(efp,"%s : No entry found for tc\n",term_name);
X			}
X			else
X			{
X				/* look up the cap in the list */
X				t_ptr = binary(beg_cap);
X				if (t_ptr != ((struct table_item *)0))
X					do_cap(beg_cap, t_ptr);
X			}
X		}
X		if (purpose == TC)
X			return((int)EOL);
X	}
X}
X
X/*
X** Reads the first line in a terminal entry and fills in term_name
X** with the terminal name found. since there can be many different
X** terminal names, the one to use is in which_name. For example,
X** if which_name is 2 this means to use the second terminal name found.
X** If successful, returns which_name, otherwise returns NULL.
X*/
X
Xget_term_name(c_ptr,term_name,which_name)
X	register char *c_ptr;
X	char term_name[];
X	int which_name;
X{
X	register int i;
X
X	for (i = 1; i < which_name; i++)
X	{
X		c_ptr = strchr(c_ptr,'|');
X		if (c_ptr == NULL)
X			return (NULL);
X		c_ptr++;
X	}
X
X	/* now c_ptr points to the which_name'th terminal name */
X	for (i = 0;(i < TERMNAME_LEN) && (*c_ptr != '|') && (*c_ptr != ':');)
X		term_name[i++] = *c_ptr++;
X	if (i == TERMNAME_LEN)
X	{
X		term_name[i - 1] = '\0';
X		fprintf(efp,"Term name starting with \"%s\" too long\n",
X			term_name);
X		exit(0);
X	}
X	term_name[i] = '\0';
X	return(which_name);
X}
X
Xchar *
Xget_next_cap(beg_cap)
X	register char *beg_cap;
X{
X	beg_cap = strchr(beg_cap,':');
X
X	if (beg_cap == NULL)
X		return(EOL);
X	else
X	{
X		beg_cap++;
X		if ((*beg_cap == '\\') || (*beg_cap == '\n'))
X			return(EOL);
X		else
X			return(beg_cap);
X	}
X}
X
Xdo_cap(beg_cap, t_ptr)
X	char *beg_cap;
X	struct table_item *t_ptr;
X{
X	int arg_num = 1;
X	int inc_amount = 1;
X	int reverse = 0;
X	char *cap_begin;
X
X	if (*t_ptr->vms_name == '\0')
X	{
X		fprintf(efp,"%s : No SMG translation for %s\n",term_name,t_ptr->unix_name);
X		return;
X	}
X	cap_begin = beg_cap;
X
X	/* move past the capability name */
X	beg_cap++;
X	beg_cap++;
X	if (*beg_cap == '@')
X		return;
X
X	switch(t_ptr->type)
X	{
X	case BOOL:
X		fprintf(ofp,"BOOLEAN %s = 1\n",t_ptr->vms_name);
X		break;
X	case STR:
X		if (*beg_cap != '=')
X		{
V			fprintf(efp,"%s : missing equal char in capability %.2s\n",term_name,cap_beg
Xin);
X			break;
X		}
X		beg_cap++;
X
X		fprintf(ofp,"STRING %s = \"",t_ptr->vms_name);
X
X		/* skip padding values and asteric */
X		while (isdigit(*beg_cap) || (*beg_cap == '*') || (*beg_cap == '.'))
X			beg_cap++;
X
X		/* go through all characters in string */
X		while (*beg_cap != ':')
X		{
X			switch (*beg_cap)
X			{
X			case '\n':
X				
V				fprintf(efp,"%s : missing ending colon in capability %.2s\n",term_name,cap_
Xbegin);
X				return;
X			case '\\':
X				beg_cap++;
X				switch (*beg_cap)
X				{
X				case 'E':		/* escape */
X					fputc('$',ofp);
X					break;
X				case 'n':		/* newline */
X					fprintf(ofp,"^j");
X					break;
X				case 'r':		/* carriage return */
X					fprintf(ofp,"^m");
X					break;
X				case 't':		/* horizontal tab */
X					fprintf(ofp,"^i");
X					break;
X				case 'b':		/* backspace */
X					fprintf(ofp,"^h");
X					break;
X				case 'f':		/* form feed */
X					fprintf(ofp,"^l");
X					break;
X				case '^':		/* caret */
X					fprintf(ofp,"_^");
X					break;
X				case '\\':		/* backslash */
X					fputc('\\',ofp);
X					break;
X				case '0':
X					if (strncmp(beg_cap,"072",3) == 0)
X					{
X						fputc(':',ofp);
X						beg_cap++;
X						beg_cap++;
X					}
X					else
X					{
X						/* better be just 3 digits */
X						fputc(*beg_cap++,ofp);
X						fputc(*beg_cap++,ofp);
X						fputc(*beg_cap,ofp);
X					}
X					break;
X				};
X				beg_cap++;
X				break;
X			case '^':			/* control */
X				fputc(*beg_cap++,ofp);
X				fputc(*beg_cap++,ofp);
X				break;
X			case '%':
X				/* missing %> */
X				beg_cap++;
X				switch (*beg_cap)
X				{
X				case 'n':
X				case 'B':
X				case 'D':
X				case '>':
V					fprintf(efp,"%s : cursor addressing mode %%%c not supported\n",term_name,*
Xbeg_cap);
X					break;
X				case '%':
X					fputc('%',ofp);
X					break;
X				case '+':
X				case 'c':
X				case '.':
X					fprintf(ofp,"(%%%d",arg_num);
X					if (*beg_cap++ == '+')
X					{
X						fprintf(ofp,"+%d)",*beg_cap - 1);
X						beg_cap++;
X					}
X					else
X						fputc(')',ofp);
X					if (reverse)
X						arg_num--;
X					else
X						arg_num++;
X					break;
X				case 'i':
X					beg_cap++;
X					inc_amount = 0;
X					break;
X				case 'r':
X					reverse = 1;
X					arg_num = 2;
X					beg_cap++;
X					break;
X				/* convert to ASCII */
X				case 'd':
X				case '2':
X				case '3':
X					if (inc_amount)
X					{
X						fprintf(ofp,"(%%%d-%d)",arg_num,inc_amount);
X						if (reverse)
X							arg_num--;
X						else
X							arg_num++;
X					}
X					else
X					{
X						/* the 2 or 3 isn't valid
X						if (*beg_cap == 'd')
X							fprintf(ofp,"!UL");
X						else
X							fprintf(ofp,"!%cUL",*beg_cap);
X						*/
X						if (*beg_cap != 'd')
X							fprintf(efp,"%s : %cdchanged to !UL\n",term_name,*beg_cap);
X						fprintf(ofp,"!UL");
X					}
X					beg_cap++;
X					break;
X				}
X				break;
X
X			/* characters that must be escaped */
X			case '&':
X			case '\'':
X			case '@':
X			case '"':
X			case '$':
X			case '!':
X			case '(':
X			case '_':
X			case '+':
X			case '-':
X			case '*':
X			case '/':
X				fputc('_',ofp);
X				fputc(*beg_cap++,ofp);
X				break;
X				
X			default:
X				fputc(*beg_cap++,ofp);
X				break;
X			}
X		}
X		fprintf(ofp,"\"\n");
X		break;
X	case NUM:
X		fprintf(ofp,"NUMERIC %s = ",t_ptr->vms_name);
X		while (*beg_cap++ != '#');
X		while(isdigit(*beg_cap))
X			fputc(*beg_cap++,ofp);
X		fputc('\n',ofp);
X		break;
X	};
X	return;
X}
X
Xstruct table_item *
Xbinary (cap)
X	char *cap;
X{
X	int cond;
X	register struct table_item *low = &table[0];
V	register struct table_item *high = &table[(sizeof table / sizeof (struct table
X_item)) - 1];
X	register struct table_item *mid;
X
X	while (low <= high)
X	{
X		mid = low + (high - low) / 2;
X		if ((cond = strncmp(cap, mid->unix_name, 2)) < 0)
X			high = mid - 1;
X		else
X			if (cond > 0)
X				low = mid + 1;
X			else
X				return(mid);
X	}
X	return(NULL);
X}
X
X/*
X** given the address of a tc entry, find the full entry for
X** the terminal given in the tc entry. When this routine returns
X** tcfp will be positioned to the first line of the entry.
X*/
X
Xfind_term(char_ptr)
X	register char *char_ptr;
X{
X	char *temp_ptr;
X	long start_pos;
X	int term_num;
X	char t_name[TERMNAME_LEN];
X
X	/* move over to the terminal name */
X	char_ptr++;
X	char_ptr++;
X	char_ptr++;
X
X	/* look for the end of the tc cap terminal name and terminate with null */
X	temp_ptr = char_ptr;
X	while((*temp_ptr != ':') && (*temp_ptr != '\n'))
X		temp_ptr++;
X	*temp_ptr = '\0';
X
X	/* char_ptr now points to the terminal name to find */
X	if (tcfp == NULL)
X		tcfp = fopen(TERMCAP,"r");
X	else
X		rewind(tcfp);
X
X	for(;;)
X	{
X		if (fgets(input_line2, LINE_LEN, tcfp) == NULL)
X			return(NULL);
X
X		/* point to the beginning of the line */
X		temp_ptr = input_line2;
X
X		if ((*temp_ptr == '#') || (isspace(*temp_ptr)))
X			continue;
X
X		start_pos = ftell(tcfp);
X
X		term_num = 1;
X		while (get_term_name(temp_ptr, t_name, term_num))
X		{
X			if (strcmp(char_ptr, t_name) == 0)
X			{
X				fseek(tcfp, start_pos, 0);
X				move_over(tcfp);
X				return(FOUND);
X			}
X			else
X			{
X				term_num++;
X				continue;
X			}
X		}
X		continue;
X	}
X}
X
Xmove_over(tcfp)
X	FILE *tcfp;
X{
X	int in_char;
X
X	/* read over all terminal names */
X	for (;;)
X	{
X		in_char = fgetc(tcfp);
X		if ((in_char != ':') && (in_char != '\\'))
X			continue;
X
X		/* Now we're either at ':' or the '\'.
X		** If at ':' then the next char is either '\',
X		** meaning more lines are coming, or another cap.
X		** It had better not be '\n' because this means that
X		** a line consisting of only a terminal name was given.
X		** If at '\' the line is missing the ':' and the next char
X		** is '\n' followed by at least one continuation line.
X		*/
X
X		if (in_char == ':')
X		{
X			in_char = fgetc(tcfp);
X			if (in_char == '\\')
X				while(isspace(in_char = fgetc(tcfp)));
X		}
X		else
X		{
X			while(isspace(fgetc(tcfp)));
X		}
X
X		/* in either case, restore the character just read */
X		ungetc(in_char,tcfp);
X		break;
X	}
X}
$ GoSub Convert_File
$ Goto Part3