[comp.sources.unix] v10i008: Crypt Breaker's Workbench, Part08/11

rs@uunet.UU.NET (Rich Salz) (06/19/87)

Submitted by: Robert W. Baldwin <BALDWIN@XX.LCS.MIT.EDU>
Mod.sources: Volume 10, Issue 8
Archive-name: cbw/Part08


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 8 (of 11)."
# Contents:  stats.c terminal.c
# Wrapped by rs@uunet on Wed Jun 17 18:17:26 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f stats.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"stats.c\"
else
echo shar: Extracting \"stats.c\" \(18796 characters\)
sed "s/^X//" >stats.c <<'END_OF_stats.c'
X/*
X * Statistics routines.
X *
X * Robert W. Baldwin, January 1985.
X * Scoring based on letter pairs added by Bob Baldwin May 1985.
X */
X
X
X#include	<stdio.h>
X#include	<math.h>
X#include	"window.h"
X#include	"specs.h"
X#include	"cipher.h"
X
X
X#define STANDALONE	FALSE
X
X
X
X/* Globals */
Xint		stats1loaded = FALSE;	/* True if letter stats loaded. */
Xchar	*letterstats;			/* Filename to find single letter counts. */
Xint		stats2loaded = FALSE;	/* True if letter pair stats loaded. */
Xchar	*bigramstats;			/* Filename to find letter pair counts. */
X
X
X/* This array contains the single letter frequencies.
X * That is, prob[i] is the probability that a randomly selected
X * plaintext character has ascii value i.
X */
Xfloat	prob[MAXCHAR+1];
Xfloat	pmean, pvar;			/* Mean and variance of above. */
X
X
X/* This array contains the base ten logarithms of the single
X * letter frequencies (probabilities) of ASCII characters.
X * The frequencies are between 0 and 1, so the enteries in the
X * table are between minus infinity and 0.
X * The log of a vanishingly small frequency is represented as
X * zero rather than some large negative number.
X * The expected value of logprob[c] for a randomly selected
X * character, c, is given by logmean.  The variance of logprob[c]
X * is given by logvar.  The standard deviation is in logsd.
X */
Xfloat	logprob[MAXCHAR+1];
Xfloat	logmean, logvar, logsd;
X
X
X/* This array contains the bigram (letter pair) frequencies.
X * That is, biprob[i][j] is the probability that a randomly selected
X * pair of adjacent plaintext characters is Ai, Aj.  Where Ai
X * is the ith letter of the alphabet (0 = 'A' or 'a', and
X * 26 = space or other non-alphabetic).
X * Eventually this will be generalized to include an arbitrary
X * character translation table to handle punctuation and to allow
X * groups of characters such as (, <, {, and [ to be treated the same.
X * 
X * The array slbiprob is the single letter frequencies taken from the
X * same sources as biprob[][].
X */
Xfloat	biprob[MXBIINDEX][MXBIINDEX];
Xfloat	slbiprob[MXBIINDEX];
X
X
X/* The array is used to map from 7-bit ascii to indices in the biprob
X * and related arrays.  The variable nbichars is set to the next index
X * to use in the biprob array.
X */
Xint		char_bimap[MAXCHAR+1];
Xint		nbichars;
X
X
X/* This array contains the base ten logarithms of the letter pair
X * frequencies (biprob[][]).
X * The frequencies are between 0 and 1, so the enteries in the
X * table are between minus infinity and 0.
X * The log of a vanishingly small frequency is represented as
X * zero rather than some large negative number.
X * The expected value of bilogprob[c] for a randomly selected
X * character, c, is given by bilogmean.  The variance of bilogprob[c]
X * is given by bilogvar.  The standard deviation is in bilogsd.
X */
Xfloat	bilogprob[MXBIINDEX][MXBIINDEX];
X
X
X/* This vector contains the base ten logarithms of the single letter
X * frequencies which are derived from biprob[][].
X * They are used to compute the log of the conditional probabilities
X * of letter pairs, given a known value for either the first or
X * second character.
X * Specifically: log( prob(XY given X=Ai) ) equals
X *    log( prob(XY) / prob(Ai) ) which equals 
X *    bilogprob[X][Y] - sllogprob[Ai].
X */
Xfloat	sllogprob[MXBIINDEX];
X
X
X/* The scoring function that uses letter pair frequencies is based
X * on a statistic that has a computable mean and variance (and
X * standard deviation).  The are stored in the following variables.
X */
Xfloat	score2_mean, score2_var, score2_sd, score2_scale;
Xfloat	score1_mean, score1_var, score1_sd, score1_scale;
X
X
X#if STANDALONE
X#define	filename		"/usr/baldwin/Ecrypt/mss-bigram.stats"
Xmain()
X{
X	FILE	*inp;
X
X	load_2stats_from(filename);
X	print_2stats(stdout);
X}
X#endif
X
X
X
X/* Score the given plaintext block.  Returns a floating point number.
X * For now a stud.
X */
Xfloat	score(pblock)
Xint		pblock[];
X{
X	int		pchar;
X	int		i;
X	float	score;
X
X	score = 0.0;
X	for (i = 0 ; i < BLOCKSIZE ; i++)  {
X		pchar = pblock[i];
X		if (pchar == -1)  continue;
X		if (pchar == ' ')			{score += 0.1;}
X		else if (lletter(pchar))	{score += 0.1;}
X		else if (uletter(pchar))	{score += 0.05;}
X		else if (printable(pchar))	{score += 0.02;}
X		else if (pchar == '\t' || pchar == '\n' || pchar == '\f')
X									{score += 0.05;}
X		else if ('0' <= pchar && pchar <= '9')
X									{score += 0.05;}
X		else 						{score -= 0.4;}
X		}
X	return(score);
X}
X
X
X/* Score a vector of integers that represent characters.
X * The vector is terminated by a value of NONE.
X * The returned score is the number of standard deviations
X * that the observed statistics differs from its expected value.
X * Scores are positive with low scores being better.
X * A negative score indicates an impossible plaintext value.
X */
Xfloat	pvec_1score(pvec)
Xint	*pvec;
X{
X	int		i;
X	int		c;
X	float	tmp, sum, count, score;
X
X	if (!stats1loaded)  {
X		load_1stats_from(letterstats);
X		}
X
X	count = 0.0;
X	sum = 0.0;
X	while (*pvec != NONE)  {
X		count += 1.0;
X		c = *pvec++;
X		if (c != c & CHARMASK)  return(-1.0);
X		tmp = logprob[c & CHARMASK];
X		if (tmp == 0.0)  return(-1.0);
X		sum += tmp;
X		}
X
X	if (count == 0.0)  return(-1.0);
X	tmp = (sum / count) - logmean;
X	tmp = tmp > 0 ? tmp : 0.0 - tmp;
X	score = tmp / (logsd / sqrt(count));
X/*	printf("  dividing by logsd yields %g", score);
X	tmp = tmp * tmp;
X	tmp = (tmp * count) / logvar;
X	tmp = exp(-0.5 * tmp);
X	printf("\nThe exponential yields %g", tmp);
X	printf("\n");
X	score = sqrt(count) * exp((0 - tmp)/2.0);
X*/
X	return(score);
X}
X
X
X/* Score a vector of integers that represent characters.
X * The vector is terminated by a value of NONE.
X * Scoring is based on ratio of observed and expected variance.
X */
Xfloat	var_1score(pvec)
Xint	*pvec;
X{
X	int		i;
X	int		c;
X	float	tmp, sum, count, score;
X
X	if (!stats1loaded)  {
X		load_1stats_from(letterstats);
X		}
X
X	count = 0.0;
X	sum = 0.0;
X	while (*pvec != NONE)  {
X		count += 1.0;
X		c = *pvec++;
X		if (c != c & CHARMASK)  return(0.0);
X		tmp = logprob[c & CHARMASK];
X		if (tmp == 0.0)  return(0.0);
X		tmp = tmp - logmean;
X		tmp = tmp * tmp;
X		sum += tmp;
X		}
X
X	if (count == 0.0)  return(0.0);
X	score = sum / (count * logvar);
X	return(score);
X}
X
X
X/* Score a vector of integers that represent characters.
X * The vector is terminated by a value of NONE.
X * Score is the probability that the given characters
X * were drawn from english.
X * NOTE: doesn't correctly handle repeated letters.
X */
Xfloat	prob_1score(pvec)
Xint	*pvec;
X{
X	int		i;
X	int		c;
X	float	tmp, product, count, score;
X
X	if (!stats1loaded)  {
X		load_1stats_from(letterstats);
X		}
X
X	count = 0.0;
X	product = 1.0;
X	while (*pvec != NONE)  {
X		count += 1.0;
X		c = *pvec++;
X		if (c != c & CHARMASK)  return(0.0);
X		product *= prob[c] * count;
X		}
X
X	if (count == 0.0)  return(0.0);
X	score = product;
X	return(score);
X}
X
X
X/* Score a guess based on letter pair frequencies.
X * The returned score is the number of standard deviations
X * that the observed statistics differs from its expected value.
X * Scores are positive with low scores being better.
X * A negative score indicates an impossible plaintext value.
X */
Xfloat	gsi_2score(gsi)
Xreg		gsinfo	*gsi;
X{
X	float	score;
X	float	total;
X	float	tmp;
X	int		nchars;
X	int		i;
Xreg	int		pos;	
Xreg	int		c;
Xreg	int		center_letter;
X	int		left_letter, right_letter;
X	float	pair_score;
X
X	if (!stats2loaded)  {
X		load_2stats_from(bigramstats);
X		}
X
X	nchars = 0;
X	total = 0.0;
X	for (i = 0 ; (pos = gsi->cpos[i]) != NONE ; i++)  {
X		nchars++;
X		c = (gsi->cguessed)[pos];
X		center_letter = char_bimap[c & CHARMASK];
X		if (sllogprob[center_letter] == 0.0)
X			return(-1.0);
X
X		if (pos == 0) {
X			total += sllogprob[center_letter];
X			}
X		else {
X			c = (gsi->cknown)[pos - 1];
X			if (c == NONE)  {
X				c = (gsi->cguessed)[pos - 1];
X				}
X			if (c == NONE)  {
X				total += sllogprob[center_letter];
X				}
X			else {
X				left_letter = char_bimap[c & CHARMASK];
X				pair_score = bilogprob[left_letter][center_letter];
X				if (pair_score == 0.0)
X					return(-1.0);
X				total += pair_score - sllogprob[center_letter];
X				}
X			}
X
X		if (pos == (BLOCKSIZE - 1)) {
X			total += sllogprob[center_letter];
X			}
X		else {
X			c = (gsi->cknown)[pos + 1];
X			if (c == NONE)  {
X				c = (gsi->cguessed)[pos + 1];
X				}
X			if (c == NONE)  {
X				total += sllogprob[center_letter];
X				}
X			else {
X				right_letter = char_bimap[c & CHARMASK];
X				pair_score = bilogprob[center_letter][right_letter];
X				if (pair_score == 0.0)
X					return(-1.0);
X				total += pair_score - sllogprob[center_letter];
X				}
X			}
X		}
X
X	if (nchars == 0)
X		return(-1.0);
X	tmp = (total / nchars) - score2_mean;
X	tmp = tmp > 0.0 ? tmp : 0.0 - tmp;
X	score = tmp / (score2_sd / isqrt[nchars]);
X	return(score);
X}
X
X
X/* Score a guess based on single letter frequencies.
X * The returned score is the number of standard deviations
X * that the observed statistics differs from its expected value.
X * Scores are positive with low scores being better.
X * A negative score indicates an impossible plaintext value.
X */
Xfloat	gsi_1score(gsi)
Xreg		gsinfo	*gsi;
X{
Xreg	int		pos;
X	int		i;
X	int		c;
X	int		nchars;
X	float	sum, score;
Xreg	float	tmp;
X
X	if (!stats1loaded)  {
X		load_1stats_from(letterstats);
X		}
X
X	nchars = 0;
X	sum = 0.0;
X	for (i = 0 ; (pos = gsi->cpos[i]) != NONE ; i++)  {
X		nchars++;
X		c = (gsi->cguessed)[pos];
X		tmp = logprob[c & CHARMASK];
X		if (tmp == 0.0)
X			return(-1.0);
X		sum += tmp;
X		}
X
X	if (nchars == 0)
X		return(-1.0);
X	tmp = (sum / nchars) - logmean;
X	tmp = tmp > 0 ? tmp : 0.0 - tmp;
X	score = tmp / (logsd / isqrt[nchars]);
X
X	return(score);
X}
X
X
X/* Compute expected value of a scoring function given
X * a vector of probabilities for values and a vector of
X * scores for each value.
X */
Xfloat	vec_mean(probvec, scorevec, maxindex)
Xfloat	*probvec;
Xfloat	*scorevec;
Xint		maxindex;
X{
X	int		i;
X	float	mean;
X
X	mean = 0.0;
X	for (i = 0 ; i <= maxindex ; i++) {
X		mean += (*probvec++) * (*scorevec++);
X		}
X	return(mean);
X}
X
X
X/* Compute variance of a scoring function given
X * a vector of probabilities for values and a vector of
X * scores for each value.
X */
Xfloat	vec_variance(probvec, scorevec, maxindex)
Xfloat	*probvec;
Xfloat	*scorevec;
Xint		maxindex;
X{
X	int		i;
X	float	var, mean;
X	float	delta;
X
X	mean = vec_mean(probvec, scorevec, maxindex);
X	var = 0.0;
X	for (i = 0 ; i <= maxindex ; i++) {
X		delta = (*scorevec++) - mean;
X		var += (*probvec++) * (delta * delta);
X		}
X	return(var);
X}
X
X
X/* Read from given stream to set up logprob table and constants
X * logmean and logvar.
X *
X * The table format is:
X * <Total count>
X * <Blankline>
X * <Count><space><One or more slashified characters to share that count>
X *  ...
X * <Count><space><One or more slashified characters to share that count>
X * <Blankline>
X * <EOF>
X */
Xload_1stats(inp)
XFILE	*inp;
X{
X	int		i,n;
X	int		tmp;
X	int		c;
X	float	v, lv, fv;
X	float	etotal, ctotal;
X
X	stats1loaded = TRUE;
X
X	for (i = 0 ; i <= MAXCHAR ; i++)  logprob[i] = 0.0;
X
X	if (fscanf(inp, "%d", &tmp) != 1)  {
X		printf("Error while getting total");
X		return;
X		}
X	etotal = tmp;
X	ctotal = 0.0;
X
X	if (fscanf(inp, "\n") != 0)  {
X		printf("Error while skipping blank line");
X		return;
X		}
X
X	while (TRUE) {
X		if ((n = fscanf(inp, "%d", &tmp)) != 1)  {
X			if (n == 0) break;
X			if (n == EOF) break;
X			printf("Error while getting character count");
X			return;
X			}
X		v = tmp;
X		ctotal += v;
X		fv = v/etotal;
X		if (fv != 0.0)  {lv = log10(fv);}
X		else {lv = 0.0;}
X
X		c = read_char(inp);		/* Skip the space. */
X		while (TRUE) {
X			c = read_char(inp);
X			if (c == EOL)  break;
X			prob[c&CHARMASK] = fv;
X			logprob[c&CHARMASK] = lv;
X			}
X		}
X
X	if (etotal != ctotal) {
X		printf("Expected total is %f.  Actual total is %f.\n",etotal,ctotal);
X		}
X
X	logmean = vec_mean(prob, logprob, MAXCHAR);
X	logvar  = vec_variance(prob, logprob, MAXCHAR);
X	logsd = sqrt(logvar);
X	score1_mean = logmean;
X	score1_var = logvar;
X	score1_sd = logsd;
X	score1_scale = sqrt(2 * PI * score1_var);
X	pmean = vec_mean(prob, prob, MAXCHAR);
X	pvar = vec_variance(prob, prob, MAXCHAR);
X}
X
X
X/* Load the letter pair statistics from the given file name.
X */
Xload_2stats_from(statfname)
Xchar	*statfname;		/* Full path name of file with statistics. */
X{
X	FILE	*inp;
X
X	if ((inp = fopen(statfname, "r")) == NULL) {
X		printf("\nCan't open %s to read letter statistics\n", statfname);
X		exit(0);
X		}
X	load_2stats(inp);
X	fclose(inp);
X}
X
X
X/* Read from given stream to set up bilogprob table and constants
X * bilogmean, bilogsd, and bilogvar.
X *
X * The format of the statistics file is: [This should be more general.]
X * <Total count>
X * <Blankline>
X * <single letter counts>
X * <line with the chars '***'>
X * <double letter counts>
X * <line with the chars '***'>
X * <mean of matrix>
X * <variance of matrix>
X * <standard deviation of matrix>
X * <Blankline>
X * <EOF>
X *
X * Where single letter counts also define the mapping from ascii chars to
X * distinguished letters (i.e., all open brackets are treated the same).
X * The single letter format is:
X * <Count><space><One or more slashified characters to share that count>
X *  ...
X * <Count><space><One or more slashified characters to share that count>
X * NOTE: the first entry should be for a low probability letter because the
X * default mapping for unknown chars is zero.  See code for details.
X *
X * The double letter format is:
X * <Count><space><Representative of first letter group><Rep of second letter>
X *  ...
X * <Count><space><Representative of first letter group><Rep of second letter>
X *
X * For example if 'T' and 't' are treated the same, a double letter entry
X * might look like: "1247 TT" and count for Tt, tT, tt, and TT.
X */
Xload_2stats(inp)
XFILE	*inp;
X{
Xregister	int		i,j;
X    int		n;
X	int		tmp;
X	int		c;
X	int		left_index, right_index;
X	float	v, lv, fv;
X	float	etotal, ctotal;
X	char	linebuf[300];
X
X	stats2loaded = TRUE;
X	nbichars = 0;
X
X	for (i = 0 ; i < MXBIINDEX ; i++)  {
X		sllogprob[i] = 0.0;
X		slbiprob[i] = 0.0;
X		for (j = 0 ; j < MXBIINDEX ; j++)  {
X	 		bilogprob[i][j] = 0.0;
X			biprob[i][j] = 0.0;
X			}
X		}
X
X	for (i = 0 ; i < MAXCHAR+1 ; i++)
X		char_bimap[i] = 0;		/* Default index if char unknown. */
X
X	if (fscanf(inp, "%d", &tmp) != 1)  {
X		printf("Error while getting total");
X		exit(0);
X		}
X	etotal = tmp;
X
X	if (fscanf(inp, "\n") != 0)  {
X		printf("Error while skipping blank line before single letters");
X		exit(0);
X		}
X
X	ctotal = 0.0;
X	while (TRUE) {
X		if ((n = fscanf(inp, "%d", &tmp)) != 1)  {
X			if (n == 0) break;
X			if (n == EOF) break;
X			printf("Error while getting character count (singles)");
X			exit(0);
X			}
X		v = tmp;
X		ctotal += v;
X		fv = v/etotal;
X		if (fv == 0.0)
X			lv = 0.0;
X		else
X			lv = log10(fv);
X
X		c = read_char(inp);		/* Skip the space. */
X		while (TRUE) {
X			c = read_char(inp);
X			if (c == EOL)  break;
X			char_bimap[c & CHARMASK] = nbichars;
X			slbiprob[nbichars] = fv;
X			sllogprob[nbichars] = lv;
X			}
X		nbichars++;
X		}
X
X	if (etotal != ctotal) {
X		printf("Expected total is %f.  Actual total is %f for singles.\n",
X				etotal, ctotal);
X		exit(0);
X		}
X
X
X	if (fscanf(inp, "***\n") != 0)  {
X		printf("Error on delimiter before letter pairs");
X		exit(0);
X		}
X
X	ctotal = 0.0;
X	while (TRUE) {
X		if ((n = fscanf(inp, "%d", &tmp)) != 1)  {
X			if (n == 0) break;
X			if (n == EOF) break;
X			printf("Error while getting character count (pairs)");
X			exit(0);
X			}
X		v = tmp;
X		ctotal += v;
X		fv = v/etotal;
X		if (fv == 0.0)
X			lv = 0.0;
X		else
X			lv = log10(fv);
X
X		c = read_char(inp);		/* Skip the space. */
X		c = read_char(inp);		/* First letter. */
X		if (c == EOL)  {
X			printf("Line ends before letter pair");
X			exit(0);
X			}
X		left_index = char_bimap[c & CHARMASK];
X		c = read_char(inp);		/* Second letter. */
X		if (c == EOL)  {
X			printf("Line ends in middle of letter pair");
X			exit(0);
X			}
X		right_index = char_bimap[c & CHARMASK];
X
X		biprob[left_index][right_index] = fv;
X		bilogprob[left_index][right_index] = lv;
X		}
X
X	if (etotal != ctotal) {
X		printf("Expected total is %f.  Actual total is %f for pairs.\n",
X				etotal, ctotal);
X		exit(0);
X		}
X
X	if (fscanf(inp, "***\n") == 0)  {
X		if (fscanf(inp, "%f", &score2_mean) != 1)  {
X			printf("Error reading mean.");
X			exit(0);
X			}
X		if (fscanf(inp, "%f", &score2_var) != 1)  {
X			printf("Error reading variance.");
X			exit(0);
X			}
X		if (fscanf(inp, "%f", &score2_sd) != 1)  {
X			printf("Error reading standard deviations.");
X			exit(0);
X			}
X		score2_scale = sqrt(2 * PI * score2_var);
X		approx_init();
X		return;
X		}
X
X	stats2();
X	printf("Mean: %f, Var: %f, SD: %f\n", score2_mean, score2_var, score2_sd);
X}
X
X
X/* Compute scoring statistics for the letter pair frequencies.
X * Uses the globals: biprob[][], sllogbiprob[], and bilogprob[][].
X * Sets gobals: score2_mean, score2_var, score2_sd.
X */
Xstats2()
X{
Xregister	int	i,j,k;
X	float	mean, var;
X	float	weight, score;
X
X	mean = 0.0;
X	var = 0.0;
X	for (i = 0 ; i < nbichars ; i++)
X		for (j = 0 ; j < nbichars ; j++)  {
X			if (slbiprob[j] == 0.0)
X				continue;
X			for (k = 0 ; k < nbichars ; k++) {
X				weight = biprob[i][j] * biprob[j][k] / slbiprob[j];
X				score = bilogprob[i][j] + bilogprob[j][k] - 2 * sllogprob[j];
X				mean += weight * score;
X				var += weight * score * score;
X				}
X			}
X	var -= mean * mean;
X
X	score2_mean = mean;
X	score2_var = var;
X	score2_sd = sqrt(score2_var);
X	score2_scale = sqrt(2 * PI * score2_var);
X
X	approx_init();
X}
X
X
X/* Print the bigram statistics.
X */
Xprint_2stats(out)
XFILE	*out;
X{
X	float	sllog_mean;
X	float	sllog_var;
X	float	lev_mean, lev_var;
X	float	rev_mean, rev_var;
X
X	fprintf(out, "\t\tBigram Statistics\n");
X	fprintf(out, "Score2_mean is %f", score2_mean);
X	fprintf(out, ", score2_var is %f", score2_var);
X	fprintf(out, ", score2_sd is %f", score2_sd);
X	fprintf(out, "\nnbichars is %d", nbichars);
X	fprintf(out, "\n");
X
X	sllog_mean = vec_mean(slbiprob, sllogprob, nbichars);
X	sllog_var = vec_variance(slbiprob, sllogprob, nbichars);
X	fprintf(out, "sllog_mean is %f", sllog_mean);
X	fprintf(out, ", sllog_var is %f", sllog_var);
X	fprintf(out, "\n");
X}
X
X
X
X/* Print the first order log statistics on a stream.
X */
Xprint_1stats(out)
XFILE	*out;
X{
X
X	fprintf(out, "Single letter frequencies\n");
X	fprintf(out, "\nExpected value of prob is %f.  Variance is %f.\n",
X	       pmean, pvar);
X	print_stat_tab(out, prob, MAXCHAR);
X
X	fprintf(out, "\nExpected value of logprob is %f.  Variance is %f.\n",
X	       logmean, logvar);
X	fprintf(out, "Log of single letter frequencies\n");
X	print_stat_tab(out, logprob, MAXCHAR);
X}
X
X
X/* Dumpa statistics table on to a stream.
X */
Xprint_stat_tab(out, table, maxindex)
XFILE	*out;
Xfloat	table[];
Xint		maxindex;
X{
X	int		i;
X
X	for (i = 0 ; i <= maxindex ; i++) {
X		if (i % 8 == 0)  fprintf(out, "\n");
X		fprintf(out, "%7.4f ", table[i]);
X		}
X	fprintf(out, "\n");
X}
X
X
X/* Load the first order statistics from the given file name.
X */
Xload_1stats_from(statfname)
Xchar	*statfname;
X{
X	FILE	*inp;
X
X	if ((inp = fopen(statfname, "r")) == NULL) {
X		printf("\nCan't open %s to read letter statistics\n", statfname);
X		exit(0);
X		}
X	load_1stats(inp);
X	fclose(inp);
X}
X
END_OF_stats.c
if test 18796 -ne `wc -c <stats.c`; then
    echo shar: \"stats.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f terminal.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"terminal.c\"
else
echo shar: Extracting \"terminal.c\" \(19285 characters\)
sed "s/^X//" >terminal.c <<'END_OF_terminal.c'
X/* Terminal dependent routines (well, most of them ...)
X *
X * Author: Bob Baldwin, October 1986.
X */
X
X/* 
X * Routines in terminal abstraction:
X *
X *	set_term()
X *		Initialize terminal, clear the screen.
X *
X *	unset_term()
X *		Return terminal to state before set_term().
X *
X *	char2sym(char)
X *		Return the symbol used to display the given char in the
X *		decryption window.
X *
X *	putsym(symbol)
X *		Displays the given symbol on the terminal.  Handles
X * 		entering and exiting graphics mode.
X *
X *	getcmd()
X *		Reads stdin for a keystroke and returns
X *		a command integer.
X *
X *	beep()
X *		Cause the terminal to beep or flash.
X */
X
X/* The trick to device independence is to clearly separate
X * internal and external representations.  On the inbound
X * side, we separate KEYSTOKES which generate a sequence
X * of ascii characters (one per keystroke in the simple case),
X * from COMMAND-KEYS such as move-cursor-up.  On the outbound
X * side we separate SYMBOLS from the sequence of ascii characters,
X * called GRAPHICS, used to display the symbol.  Each symbol
X * has a single use by the code, though two symbols might
X * appear the same on the user's terminal.
X */
X
X/* Symbols are represented by integers.  If the integer is
X * greater than 256, then it denotes one of the symbols defined
X * in terminal.h.
X *
X * Commands are represented by two byte integers.  The high byte
X * describes the command (see terminal.h), the low byte is the
X * argument to the command.  For example, the insert char command
X * sets the low byte to the character to be inserted.
X */
X
X/* INTERNALS: symbols and graphics
X *
X * The terminal is assumed to be in one of three modes: normal, graphics,
X * or standout (inverse video).  This abstraction takes care of all the
X * switching between modes and tries to avoid sending redundant escape
X * sequences to the terminal.
X * 
X * Part of the terminal initialization is to build a table that describes
X * how to display each symbol.  Ascii characters (including control chars)
X * pass through unchanged, but symbols are looked up in graphtab.
X * Each entry in the graphics table specifies a terminal mode and a
X * string to print that will display that symbol in the given mode.
X * If necessary a mode switch sequence will be sent before printing the
X * string.
X *
X * The graphics corresponding to symbols can be set by the shell variable
X * 'GRAHPICS' using a format similar to the termcap format.  The two
X * character names of the symbols are defined in the table symnames[],
X * which can be indexed by the symbol codes (see terminal.h).
X * The mapping from symbols to graphics is first set to a default value,
X * and then updated by the values found in the shell variable (if any).
X *
X * The GRAPHICS string consists of a number of entries separated by colon
X * characters.  Each entry have the form xx=\Mccc, where xx is the name
X * of a symbol (see symnames[] below), \M indicates the mode for displaying
X * this symbol (it must be one of \N (for normal), \G (for graphics), or \S
X * (for standout or inverse video)), and ccc is a sequence of characters
X * to send.  To include a colon character in the ccc portion, preceed it
X * by a backslash (i.e., '\').  If the \M is ommitted, it defaults to normal.
X */
X
X/* INTERNALS: keystrokes and commands
X *
X * The table keycmdtab is used to convert a sequence of ascii characters
X * into a command integer.  The table specifies the escape sequences that
X * might be generated by the different command keys.  The getcmd routine
X * will read characters from stdin until it has uniquely identified a
X * command, or decided that there is no match.  If no match is found,
X * the terminal is beeped and matching is restarted with the next character.
X * By default keystrokes turn into self-insert commands.
X *
X * In general the last character of the sequence is returned as the arg.
X * This helps windows assign different meanings to keystokes.  For example
X * The return key can be either an insert-return command or an execute-
X * command-line command.
X */
X
X
X#include	<curses.h>
X#include	<sgtty.h>
X#include	<strings.h>
X#include	"window.h"
X#include	"terminal.h"
X#include	"specs.h"
X
X
X/* Routines from termcap library.
X */
Xextern	char *getenv(), *tgetstr(), *tgoto();
X
X
X/* Screen control strings from termcap entry.
X */
Xchar	*term_is;		/* Terminal initializaion string. */
Xchar	*erase_eol;		/* Erase to end of line. */
Xchar	*erase_eos;		/* Erase to end of screen. */
Xchar	*erase_scr;		/* Erase whole screen. */
Xchar	*cm;			/* Cursor motion. */
Xchar	*start_kp;		/* Start graphics mode. */
Xchar	*end_kp;		/* End graphics mode. */
Xchar	*start_alt;		/* Start graphics mode. */
Xchar	*end_alt;		/* End graphics mode. */
Xchar	*start_so;		/* Start standout mode. */
Xchar	*end_so;		/* End standout mode. */
X
X/* Keymap strings from termcap file.
X */
Xchar	*term_f1;		/* The f1 key. */
Xchar	*term_f2;		/* The f2 key. */
Xchar	*term_f3;		/* The f3 key. */
Xchar	*term_f4;		/* The f4 key. */
Xchar	*term_up;		/* Up arrow. */
Xchar	*term_down;		/* Down arrow. */
Xchar	*term_left;		/* Left arrow. */
Xchar	*term_right;		/* Right arrow. */
X
X
X/* Symbol names for the shell variable 'GRAPHICS'
X * The values must be and'ed with SYMBOLM to make
X * suitable indices for graphtab[].
X */
Xlabelv	symnames[NSYMC + 1] = {
X	{"tb", STAB},		/* Tab */
X	{"na", SNOTASCII},	/* Not ascii */
X	{"lf", SLINEFEED},	/* Linefeed */
X	{"cr", SCARETURN},	/* Carriage return */
X	{"ff", SFORMFEED},	/* Formfeed */
X	{"cc", SCONTCODE},	/* Other control characters */
X	{"uk", SUNKNOWN},	/* Plaintext unknown */
X	{"ul", SUNDERLINE},	/* Pseudo underline char */
X	{"hb", SHORZBAR},	/* Horizontal bar */
X	{"vb", SVERTBAR},	/* Vertical bar */
X	{"ll", SLLCORNER},	/* Lower left corner */
X	{NULL, 0},		/* End flag. */
X};
X
X/* Table of graphics characters initialized for ordinary CRT.
X */
Xsymgraph graphtab[NSYMC];
X
X
X/* Symbol names for the shell variable KEYMAPVAR
X * A command's index in this table should be one less
X * than the command code.
X */
Xlabelv	cmdnames[] = {
X	{"up", CGO_UP},
X	{"do", CGO_DOWN},
X	{"le", CGO_LEFT},
X	{"ri", CGO_RIGHT},
X	{"re", CREFRESH},
X	{"un", CUNDO},
X	{"cl", CCLRLINE},
X	{"ws", CWRDSRCH},
X	{"df", CDELF},
X	{"db", CDELB},
X	{"pr", CPREVBLOCK},
X	{"ne", CNEXTBLOCK},
X	{"ac", CACCEPT},
X	{"ex", CEXECUTE},
X	{"--", CINSERT},		/* Should not be in keymap var. */
X	{"ta", CTRYALL},
X	{"jc", CJUMPCMD},
X	{NULL, 0},
X};
X
X/* Table of keystroke commands.
X * Self-insert commands are the default.
X * There can be several keystrokes that generate the same command.
X * To insert control chars, they must be quoted, see QUOTEC.
X * The table is terminated by an entry with c_seq == NULL.
X */
X#define	QUOTEC	(CNTRL & 'Q')
Xkeycmd	keycmdtab[100];
Xkeycmd	*keycmdp;		/* Pointer to next free entry. */
X
X
X
X/* Saved process control characters.
X */
Xstruct	tchars	saved_tchars;
X
X
X/* Buffer for termcap entry. */
X#define TBUFSIZ 1024
Xchar	buf[TBUFSIZ];
Xchar	free_buf[1000], *fr;		/* Must be global, see tgetstr() */
X
X
X/* Current terminal mode. */
Xint	termmode = -1;
X
X
X/* Set up the terminal. This package now makes calls to both curses
X * and termcap subroutine packages, although the old code is used
X * for screen refresh.
X */
Xset_term()
X{
X	printf("\n\nInitializing terminal ...");
X	fflush(stdout);
X
X	get_termstrs();
X	get_genv();
X	get_kenv();
X	savetty();
X	crmode();
X	noecho();
X	noflow();
X	Puts(term_is);
X	Puts(start_kp);
X	enter_mode(SMNORMAL);
X
X	printf(" done.\n");
X
X	clrscreen();
X}
X
X
X/* Get keystroke characters, build keymap.
X * The earlier entries have priority, so fill them in from
X * the shell var, then add defaults from a string then termcap.
X */
Xget_kenv()
X{
X	char	*kenv;
X	char	tcapstr[1000];
X
X	keycmdp = keycmdtab;
X	kenv = getenv(KEYMAPVAR);
X	if (kenv != NULL)
X	  	read_keymap(kenv);
X	kenv_termcap(tcapstr);
X	read_keymap(tcapstr);
X	read_keymap(DKEYMAP);
X}
X
X
X/* Build a keymap string in the given buffer from the info
X * in the termcap file.
X * The string format is like: "up=\Eu:do=\033d".
X */
Xkenv_termcap(str)
Xchar	*str;
X{
X	*str = NULL;
X
X	if (term_up != NULL)  {
X		strcat(str, cmdnames[CGO_UP - 1].label);
X		strcat(str, "=");
X		strcat(str, term_up);
X		strcat(str, ":");
X	}
X	if (term_down != NULL)  {
X		strcat(str, cmdnames[CGO_DOWN - 1].label);
X		strcat(str, "=");
X		strcat(str, term_down);
X		strcat(str, ":");
X	}
X	if (term_left != NULL)  {
X		strcat(str, cmdnames[CGO_LEFT - 1].label);
X		strcat(str, "=");
X		strcat(str, term_left);
X		strcat(str, ":");
X	}
X	if (term_right != NULL)  {
X		strcat(str, cmdnames[CGO_RIGHT - 1].label);
X		strcat(str, "=");
X		strcat(str, term_right);
X		strcat(str, ":");
X	}
X	if (term_f1 != NULL)  {
X		strcat(str, cmdnames[CPREVBLOCK - 1].label);
X		strcat(str, "=");
X		strcat(str, term_f1);
X		strcat(str, ":");
X	}
X	if (term_f2 != NULL)  {
X		strcat(str, cmdnames[CNEXTBLOCK - 1].label);
X		strcat(str, "=");
X		strcat(str, term_f2);
X		strcat(str, ":");
X	}
X	if (term_f3 != NULL)  {
X		strcat(str, cmdnames[CACCEPT - 1].label);
X		strcat(str, "=");
X		strcat(str, term_f3);
X		strcat(str, ":");
X	}
X	if (term_f4 != NULL)  {
X		strcat(str, cmdnames[CJUMPCMD - 1].label);
X		strcat(str, "=");
X		strcat(str, term_f4);
X		strcat(str, ":");
X	}
X}
X
X
X
X/* Add key bindings from the given string to the keycmdtab.
X */
Xread_keymap(var)
Xchar	*var;
X{
X	int	cmd_code;
X
X	while (*var != NULL)  {
X		if (! read_varlabel(&var, cmdnames, &cmd_code))  {
X			if (*var == NULL)
X			  	break;
X			disperr("Can't parse label in read_keymap.");
X			exit(1);
X		}
X
X		keycmdp->c_code = cmd_code;
X		if (! read_varval(&var, &(keycmdp->c_seq)))  {
X			disperr("keymap value has bad format.");
X			exit(1);
X		}
X		keycmdp++;
X	}
X}
X
X
X/* Get graphics characters.
X * Set to defaults, then read changes from shell var (if any).
X */
Xget_genv()
X{
X	char	*genv;
X
X	read_graphics(DGRAPHICS);
X	genv = getenv(GRAPHICSVAR);
X	if (genv == NULL)
X	  	return;
X	read_graphics(genv);
X}
X
X
X/* Read graphics map from the given string.
X */
Xread_graphics(var)
Xchar	*var;
X{
X	int	sym_idx;
X
X	while (*var != NULL)  {
X		if (! read_varlabel(&var, symnames, &sym_idx))  {
X			if (*var == NULL)
X			  	break;
X			disperr("Can't parse label in GRAPHICSMAP.");
X			exit(1);
X		}
X
X		if ((var[0] != '\\') || (index(GVARMODES, var[1]) == 0))  {
X			disperr("GRAPHICSMAP value has bad mode.");
X			exit(1);
X 		}
X		sym_idx &= SYMBOLM;
X		graphtab[sym_idx].s_mode = var[1] & CHARM;
X		var++, var++;
X
X		if (! read_varval(&var, &(graphtab[sym_idx].s_seq)))  {
X			disperr("GRAPHICSMAP val has bad format.");
X			exit(1);
X		}
X	}
X}
X
X
X/* Advance to the next label in strp, look the label up in the
X * labeltab, and set *valp to the value in the labeltab.
X * Return with *strp pointing after '=' that follows the label.
X * Return TRUE is parses ok, else return FALSE.
X * If string empty, return false and set *strp to point to a NULL.
X */
Xint read_varlabel(strp, labeltab, valp)
Xchar	**strp;
Xlabelv	*labeltab;
Xint	*valp;
X{
X	char	*str;
X	labelv	*lp;
X
X	for (str = *strp ; *str && index(VARSEP, *str) != 0 ; str++ );
X
X	for (lp = labeltab ; lp->label != NULL ; lp++)  {
X		if (substrp(str, lp->label))  {
X			*valp = lp->value;
X			str = index(str, '=');
X			if (str == NULL)
X			  	return(FALSE);
X			str++;
X			*strp = str;
X			return(TRUE);
X		}
X	}
X	*strp = str;
X	return(FALSE);
X}
X
X
X/* Read a string value from a shell var string.
X * Return with *strp pointing after the string,
X * fill in valp with a pointer to a copy of the value string
X * on the heap.
X * Return TRUE if things go ok.
X */
Xint read_varval(strp, valp)
Xchar	**strp;
Xchar	**valp;
X{
X	char	buf[100], *bp;
X	char	*var;
X
X	var = *strp;
X	bp = buf;
X	while ((*var != NULL) && (index(VARTERM, *var) == NULL))  {
X		*bp = read_slashed(&var);
X	       	bp++;
X        }
X	*bp = 0;
X	*valp = savestr(buf);
X	*strp = var;
X	return (TRUE);
X}
X
X
X/* Read the given (slashified) string and return a character.
X * Advance *strp over chars read.
X * Handle \\, \001, \n, \t, \r.
X * The symbol \E maps to escape (\033).
X * An unexpected char after the slash is just returned.
X */
Xint read_slashed(strp)
Xchar	**strp;
X{
X	char	*str;
X	char	c;
X
X	str = *strp;
X	if (*str != '\\')  {
X		*strp = (*strp) + 1;
X	  	return (*str);
X	}
X	str++;
X	c = *str;
X	switch (c)  {
X	  default:
X		if (index(DIGITS, c) == NULL)
X		  	break;
X		c -= '0';
X		if (index(DIGITS, str[1]) == NULL)
X		  	break;
X		str++;
X		c = (c * 8) + (*str - '0');
X		if (index(DIGITS, str[1]) == NULL)
X		  	break;
X		str++;
X		c = (c * 8) + (*str - '0');
X		break;
X
X	  case 'n':
X		c = '\n';
X		break;
X
X	  case 't':
X		c = '\t';
X		break;
X
X	  case 'E':
X		c = '\033';
X		break;
X
X	  case 'f':
X		c = '\f';
X		break;
X
X	  case 'r':
X		c = '\r';
X		break;
X	}
X	*strp = str + 1;
X  	return (c);
X}
X
X
X/* Turn off flow control characters.
X */
Xnoflow()
X{
X	struct	tchars	new_tchars;
X	
X	/* Turn off C-Q, C-S flow control. */
X	if (ioctl(0, TIOCGETC, &saved_tchars) < 0)  {
X		perror("noflow iocl get");
X		exit(1);
X	}
X	new_tchars = saved_tchars;
X	new_tchars.t_stopc = -1;
X	new_tchars.t_startc = -1;
X	if (ioctl(0, TIOCSETC, &new_tchars) < 0)  {
X		perror("noflow iocl set");
X		exit(1);
X	}
X}
X
X
X/* Restore the flow control characters.
X */
Xrestore_flow()
X{
X	if (ioctl(0, TIOCSETC, &saved_tchars) < 0)  {
X		perror("restore_flow iocl set");
X		exit(1);
X	}
X}
X
X
X/* Read in the termcap strings.
X */
Xget_termstrs()
X{
X	char	*term;
X	int	res;
X
X	fr=free_buf;
X	term = getenv("TERM");
X	if (term == NULL)  {
X		disperr("The shell variable TERM is not defined.");
X		exit(1);
X	}
X	res = tgetent(buf,term);
X	switch (res)  {
X	  case -1:
X		disperr("Can't open termcap file.");
X		exit(1);
X
X	  case 0:
X		disperr("No termcap entry for your terminal.");
X		exit(1);
X
X	  default:
X		break;
X	}	       
X
X	term_is = tgetstr("is", &fr);
X	if (term_is == NULL)
X	  	term_is = "";
X	erase_eol = tgetstr("ce", &fr);
X	erase_eos = tgetstr("cd", &fr);
X	erase_scr = tgetstr("cl", &fr);
X	start_so = tgetstr("so", &fr);
X	end_so = tgetstr("se", &fr);
X	start_alt = tgetstr("as", &fr);
X	end_alt = tgetstr("ae", &fr);
X	if (start_alt == 0  ||  end_alt == 0) {
X		start_alt = "\033F";	       	/* VT100 default. */
X		end_alt = "\033G";
X	}
X	cm = tgetstr("cm", &fr);		/* for cursor positioning */
X	start_kp = tgetstr("ks", &fr);
X	end_kp = tgetstr("ke", &fr);
X
X	if ((term_is == NULL) || (erase_eol == NULL) ||
X	    (erase_eos == NULL) || (erase_scr == NULL) ||
X	    (cm == NULL) ||
X	    (end_kp == NULL) || (start_kp == NULL) ||
X	    (end_alt == NULL) || (start_alt == NULL) ||
X	    (end_so == NULL) || (start_so == NULL) )  {
X		disperr("A required termcap capability is missing.");
X		disperr("\t one of: ce, cd, cl, so, se, cm, ks, ke.");
X		exit(1);
X	}
X
X	/* Now get entries for keymap, NULL means no such entry.
X	 */
X	term_f1 = tgetstr("k1", &fr);
X	term_f2 = tgetstr("k2", &fr);
X	term_f3 = tgetstr("k3", &fr);
X	term_f4 = tgetstr("k4", &fr);
X	term_up = tgetstr("ku", &fr);
X	term_down = tgetstr("kd", &fr);
X	term_left = tgetstr("kl", &fr);
X	term_right = tgetstr("kr", &fr);
X}
X
X
X/* Restore the terminal to its original mode.
X */
Xunset_term()
X{
X	enter_mode(SMNORMAL);
X	Puts(end_kp);		/* Can't tell if this is original. */
X	fflush(stdout);
X	nocrmode();
X	echo();
X	restore_flow();
X	resetty();
X}
X
X
X/* Return the symbol used to display the given char in the
X * decryption window.
X */
Xint  char2sym(pchar)
Xint	pchar;
X{
X 	int	gchar;
X
X	if (printable(pchar))		{gchar = pchar;}
X	else if (pchar == -1) 		{gchar = SUNKNOWN;}
X	else if (notascii(pchar))	{gchar = SNOTASCII;}
X	else if (pchar == '\n')		{gchar = SLINEFEED;}
X	else if (pchar == '\r')		{gchar = SCARETURN;}
X	else if (pchar == '\f')		{gchar = SFORMFEED;}
X	else if (pchar == '\t')		{gchar = STAB;}
X	else				{gchar = SCONTCODE;}
X	return (gchar);
X}
X
X
X/* Displays the given symbol on the terminal.  Handles
X * entering and exiting graphics or standout mode.
X */
Xputsym(symbol)
Xint	symbol;
X{
X	int		symcode;
X	symgraph	*gp;
X
X	if (! graphic(symbol))  {
X		enter_mode(SMNORMAL);
X		putchar(symbol & CHARM);
X		return;
X	}
X	symcode = symbol & SYMBOLM;
X	if (symcode >= NSYMC)  {
X		disperr("Bad symbol code in putsym.");
X		return;
X	}
X	gp = &graphtab[symcode];
X	enter_mode(gp->s_mode);
X	Puts(gp->s_seq);
X}
X
X
X/* Enter a particular mode.  If necessary send escape sequence
X * to the terminal.  Handle terminating the previous mode.
X */
Xenter_mode(mode)
Xint	mode;
X{
X	if (termmode == mode)
X	  	return;
X
X	switch (termmode)  {
X	  case SMNORMAL:
X		break;
X
X	  case SMGRAPHIC:
X		Puts(end_alt);
X		break;
X
X	  case SMSTANDOUT:
X		Puts(end_so);
X		break;
X
X	  default:
X		Puts(end_so);
X		Puts(end_alt);
X		break;
X	}
X
X	termmode = mode;
X
X	switch (termmode)  {
X	  case SMNORMAL:
X		break;
X
X	  case SMGRAPHIC:
X		Puts(start_alt);
X		break;
X
X	  case SMSTANDOUT:
X		Puts(start_so);
X		break;
X
X	  default:
X		disperr("Bad terminal mode.");
X		break;
X	}
X}
X
X
X/* Return values from srch_ktab().
X */
X#define SK_SUBSTR	-1
X#define SK_NOMATCH	-2
X
X
X/* Search the key command table for the given keystroke.
X * If no match, return SK_NOMATCH (which is < 0).
X * If the keystroke is the prefix for one or more commands,
X * return SK_SUBSTR (which is < 0).
X * If an exact match is found, return the index ( >= 0) of
X * the entry that matched.
X */
Xint srch_ktab(ktab, stroke)
Xkeycmd	ktab[];
Xchar	*stroke;
X{
X	int	i;
X	int	nsubstr = 0;		/* Number of close entries */
X
X	for (i = 0 ; ktab[i].c_seq != NULL ; i++)  {
X		if (strcmp(ktab[i].c_seq, stroke) == 0)
X		  	return(i);
X		if (substrp(ktab[i].c_seq, stroke))
X		  	nsubstr++;
X	}
X	if (nsubstr > 0)
X	  	return (SK_SUBSTR);
X	return (SK_NOMATCH);
X}
X
X
X/* Return TRUE if the model string starts with the given string.
X * Return false if strlen(given) > strlen(model).
X */
Xint substrp(model, given)
Xchar	*model;
Xchar	*given;
X{
X	for ( ; (*model != 0) && (*given != 0) ; model++, given++)  {
X		if (*model != *given)
X		  	return (FALSE);
X	}
X	if (*given == 0)
X	  	return (TRUE);
X	return (FALSE);
X}
X
X
X/* Read a keystroke from stdin and return the command for it.
X *
X * Single character keystrokes not found in the table generate
X * self-insert commands.
X * Control characters other than \n, and \t must be quoted in order
X * to generate self-insert commands.  To quote a char, preceed it
X * by the QUOTEC character.
X * Multi character keystrokes should end in an exact match.  If not,
X * throw away the char that caused no matches in the keycmdtab,
X * beep the terminal, and start over.
X */
Xint getcmd()
X{
X	char	keystroke[10];		/* Chars in keystroke. */
X	int	nchars;			/* Length of keystroke[]. */
X	int	c;
X	int	index;			/* Cmd index in keycmdtab. */
X	int	code;			/* Cmd code. */
X
X  start_over:
X	nchars = 0;
X	keystroke[0] = 0;
X
X	while (TRUE)  {
X		c = getchar();
X		keystroke[nchars++] = c;
X		keystroke[nchars] = 0;
X		index = srch_ktab(keycmdtab, keystroke);
X		switch (index)  {
X		  case SK_SUBSTR:
X			continue;
X
X		  case SK_NOMATCH:
X			if (nchars != 1)  {
X				beep();
X				goto start_over;
X			}
X			code = CINSERT;
X			if (c == QUOTEC)  {
X				c = getchar();
X				break;
X			}
X			else if (printable(c))  {
X				break;
X			}
X			else if ((c != LINEFEED) && (c != TAB))  {
X				beep();
X				goto start_over;
X			}
X			break;
X			
X		  default:
X			if (index < 0)  {
X				disperr("Bad keycmdtab index.");
X			}
X			code = keycmdtab[index].c_code;
X			break;
X		}
X	return ((code << CMDSHIFT) | (c & CHARM));
X	}
X}
X
X
X/* Cause the terminal to beep.
X */
Xbeep()
X{
X	Puts("\007");			/* C-G */
X}
X
X
X/* Save a copy of the given string on the heap.
X * Return pointer to copy.
X */
Xchar *savestr(s)
Xregister char	*s;
X{
X	char	 *p;
X	register char	*t;
X
X	if (s == NULL)
X		return( NULL );
X	t = p = (char*) calloc(strlen(s) + 1, 1);
X	if (t == NULL)
X		return(NULL);
X	while (*t++ = *s++);
X	return(p); 
X}
END_OF_terminal.c
if test 19285 -ne `wc -c <terminal.c`; then
    echo shar: \"terminal.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 8 \(of 11\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 11 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0