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