[alt.sources] uniform - universal form filer

istvan@hhb.UUCP (Istvan Mohos) (03/01/90)

However much you may despise scribbling redundant and purposeless
responses into little boxes on pre-printed official forms...

on the rare occasion when this is unavoidable you will grit your
teeth, grab a ball point pen, and put the gestalt behind you.
Still, should you have to face the same form time and time again
(colored perhaps in a nice, mottled pink --- it can happen!)
call UNIFORM to your "emotional rescue".

Complete the form one last time with your computer and printer, and
let UNIFORM remember every move you made: what went into which box,
where the boxes are, linefeeds, and the spacing between fields.
UNIFORM will *learn* your form and will then be able to reprint it
on demand.  Naturally, you can specify arbitrary boxes to contain
string variables, and supply current strings at reprint time.  But
don't throw away your ball point just yet!  You must still sign the
form by hand.

Although UNIFORM is a fullblooded, "curses"-guzzling, Unix native,
it can jolt a Unix mindset, being darn right contrary to ideas of
       device-independent output,
       device indistinguishable from file,
       the printer as a shared resource.

UNIFORM forces the printer back the evolutionary ladder, demoting
it to typewriter status (having clearly and separately controllable
paper and carriage movements).  The printer MUST be in the user's
immediate vicinity for positioning the blank form and for sustained
visual feedback.  The printer MUST be able to print characters on a
line one at a time, and MUST be able to return the carriage to the
left without a corresponding line feed.

The installer of the program is requested to define a handful of
printer escape codes in "myprinter.h" specific to the intended slave
printer.  The present source defines such escape sequences for the
   C ITOH 8510A     dot matrix printer (ImageWriter compatible),
   ROYAL BETA 8000  typewriter (Diablo 630 compatible),
   DECWRITER II     serial printer.
Posted or E-mailed additions to this printcap are accepted with
gratitude, and will be included in an update of UNIFORM.

Installation procedures are given in Install.  System- and printer-
specific compile-time defines (notably REALUNIX for SysV) should be
enabled in Makefile prior to compiling the source.  Use
"troff -man uniform.man" to print the operating instructions.

Part 1 (this posting) is the complete source in a shell archive.
Part 2 is the man pages.

===========================CUT HERE===============================
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# Install Makefile README globals.h myprinter.h adjust.c counters.c etc.c getvar.c highlight.c makeform.c printform.c uniform.c

echo x - Install
cat > "Install" << '//E*O*F Install//'
This file contains instructions for compiling the source of
the program "uniform" under SysV or BSD Unix, and instructions for
defining printer "escape sequences" to control a fully operational
printer existing on the system, from "uniform".

No instructions are given for installing or "setting up" a printer
for normal operation in a Unix system.  In particular, it is assumed
that a "character device" exists under /dev as a device driver for
the port that the printer is connected to, and that this /dev file is
active and writable by the "uniform" process.

/******************************************************************
 * In order to compile the source for your printer and on your system,
 * edit "myprinter.h" and "Makefile".
 * 
 * In "myprinter.h" add a #ifdef section of the form:
 ******************************************************************/
#ifdef FOOBAR
    /* more stuff will be added here, momentarily */
#endif

/******************************************************************
 * where FOOBAR stands for the name of your printer, NOT YET DEFINED
 * in "myprinter.h".
 * Within the "#ifdef FOOBAR" block, define the following strings
 * and character arrays (example initial values are given):
 ******************************************************************/
    char *Prtype = "Reconditioned 1932 FOO operating at near 300 baud";
                   /* strlen must be less than 70 chars */

    /* three lines of instructions on setting up the printer manually;
       len must be less than 77 chars per string;
       leave string NULL if there is nothing to say
    */
    char *Setmesg0 = "Turn printer on with on-off switch hidden inside";
    char *Setmesg1 = "Slap sharply from left if head starts to vibrate";
    char *Setmesg2;

    /* Provide escape code sequence that will initialize the printer
       to operate most closely to UNIFORM's assumptions:
       1) set printer to incremental mode (printing "by byte").
          If the printer does this anyway, skip this.
       2) set width of a single line feed to 1/48 inch
          (eight feeds/singlespaced line).  If the printer can't do
          this, set the smallest linefeed the printer is capable of.
          If the width of the linefeed cannot be set, skip this..
       3) Set the printer to print 12 characters per inch.  (For
          some printers, this will be the same as using the "elite"
          character set).  If the printer prints 12 characters anyway,
          or if 12 characters per inch cannot be set, skip this.
       4) Add trailing carriage return.
       5) Terminate the sequence with a null byte.
       For example:
    */
    char prinit[] = {       27, 84, 48, 51, 27, 69, 13, 0};
/*                    |     |               |       |   |
prints by byte anyway /     |               |       |   |
ESC T03 for 1/48" line feed /               |       |   |
ESC E to select elite character set (12cpi) /       |   |
carriage return (without line feed) at end of init  /   |
null byte Unix string terminator - printer won't see it /
*/

    /* Provide escape code sequence that will reset printer to initial
       conditions:
       1) reset printer to buffered mode.  If not applicable, skip this.
       2) reset printer to line feed width of 1/6" (standard
          single spaced print), or whatever line feed the the printer
          powers up with.
          If the width of the linefeed cannot be set, skip this..
       3) Reset the printer to print 10 characters per inch.  (To
          some printers this means "pica" font.)  Alternately, reset
          the printer to the character spacing evident at powerup.
          If character spacing cannot be set, skip this.
       4) Add trailing CRLF (carriage return and line feed).
       5) Terminate the sequence with a null byte.
       For example:
    */
    char prreset[] = {        27, 84, 50, 52, 27, 78, 13, 10, 0};
/*                       |    |               |       |       |
always prints unbuffered /    |               |       |       |
ESC T24 for 1/6" line feed    /               |       |       |
ESC N to select pica  character set (10cpi)   /       |       |
carriage return followed by line feed at end of reset /       |
null byte Unix string terminator - printer won't see it       /
*/

    /* Provide eight normal 1/48" line feeds in succession, equivalent
       to a 1/6" paper feed (1 single spaced line).  Alternate
       escape sequences may exist for this.  One way is to just give
       eight CRLF commands, each feeding 1/48", adding up to 1/6".
       Interim carriage returns may not be necessary for your printer,
       but the head must return to the left by the end of the sequence.
       For example:
    */
    char tabfeed[] = {13,10,13,10,13,10,13,10,13,10,13,10,13,10,13,10,0};
/*                    CR LF CR LF CR LF CR LF CR LF CR LF CR LF CR LF |
              null byte Unix string terminator - printer won't see it /
*/

    /* Provide the three simple but essential control codes for
       carriage return and line feed.  Note the null byte necessary
       at the end of each array.
    */
    char cr[] = {13, 0};       /* carriage return without line feed */
    char lf[] = {10, 0};       /* line feed without carriage return */
    char crlf[] = {13, 10, 0}; /* carriage return plus linefeed */

    /* Provide optional font switching and other definitions.  That is,
       the arrays must be declared here, but supplying escape sequences
       is optional.  If an escape sequence is not given, the array
       should be defined as a single null byte.  Example:
    */
    char bs[] = {0};         /* backspace; GENERATED IN SOFTWARE */
    char pi[] = {27, 78, 0}; /* pica pitch/font 10 cpi */
    char el[] = {27, 69, 0}; /* elite pitch/font 12 cpi */
    char co[] = {0};         /* compressed pitch; NOT AVAILABLE */
    char bo[] = {0};         /* overstrike (bold); NOT AVAILABLE */
    char kb[] = {0};         /* kill overstrike; NOT AVAILABLE */

    /* In the above set if a capability is 0, the user's control
       keys will expand to zero-length  escape codes, and will not be
       written to the printer, except for bs.  If bs is 0,
       backspace capability will be manufactured in software,
       by returning the carriage to the left (margin),
       and then advancing the carriage to the right, to one less
       column position than the head occupied before the bs command.
    */

/******************************************************************
 * Edit "Makefile" next:
 * 
 * define REALUNIX under CFLAGS if running on SysV
 * define printer type you just wrote a printcap for (FOOBAR)
 * define baud rate if printer is a serial printer
 * do not define baud rate if printer is a parallel printer
 * define name of the /dev character device (tty) of the printer port
 *     unless the name is /dev/lp
 * set "LIBS=-lcurses" for SysV systems, or
 * set "LIBS=-lcurses -ltermcap" for BSD systems
 ******************************************************************/

# SysV example if PRINTERPORT is "/dev/lp"
CFLAGS=-O -DREALUNIX -DFOOBAR -D'BAUD=300'
LIBS=-lcurses

# BSD example
CFLAGS=-O -DFOOBAR -D'BAUD=300' -D'PRINTERPORT="/dev/ttya"'
LIBS=-lcurses -ltermcap

/*** MAKE ***/
//E*O*F Install//

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
#
CC=/bin/cc

# PC6300+ SysV, Royal 8000 typewriter (parallel, Diablo 630 compatible)
#CFLAGS=-O -DREALUNIX -DPLUS6300 -DROYAL
#LIBS=-lcurses

# generic SysV, CItoh 8310A (parallel, ImageWriter compatible)
#CFLAGS=-O -DREALUNIX -DCITOH
#LIBS=-lcurses

# generic BSD, 300 baud Decwriter II (serial)
CFLAGS=-O -DDECWRITER -D'BAUD=300' -D'PRINTERPORT="/dev/ttya"'
LIBS=-lcurses -ltermcap

#.SILENT:

FILES= adjust.o makeform.o printform.o counters.o \
	getvar.o highlight.o etc.o

uniform: uniform.o ${FILES}
	$(CC) -o $@ uniform.o ${FILES} ${LIBS}

uniform.o: globals.h myprinter.h
${FILES}:  globals.h
//E*O*F Makefile//

echo x - README
cat > "README" << '//E*O*F README//'
uniform - universal form filer
robot control sequence prototyping

However much you may despise scribbling redundant and purposeless
responses into little boxes on pre-printed official forms...

on the rare occasion when this is unavoidable you will grit your
teeth, grab a ball point pen, and put the gestalt behind you.
Still, should you have to face the same form time and time again
(colored perhaps in a nice, mottled pink --- it can happen!)
call UNIFORM to your "emotional rescue".

Complete the form one last time with your computer and printer, and
let UNIFORM remember every move you made: what went into which box,
where the boxes are, linefeeds, and the spacing between fields.
UNIFORM will *learn* your form and will then be able to reprint it
on demand.  Naturally, you can specify arbitrary boxes to contain
string variables, and supply current strings at reprint time.  But
don't throw away your ball point just yet!  You must still sign the
form by hand.

Although UNIFORM is a fullblooded, "curses"-guzzling, Unix native,
it can jolt a Unix mindset, being darn right contrary to ideas of
       device-independent output,
       device indistinguishable from file,
       the printer as a shared resource.

UNIFORM forces the printer back the evolutionary ladder, demoting
it to typewriter status (having clearly and separately controllable
paper and carriage movements).  The printer MUST be in the user's
immediate vicinity for positioning the blank form and for sustained
visual feedback.  The printer MUST be able to print characters on a
line one at a time, and MUST be able to return the carriage to the
left without a corresponding line feed.

The installer of the program is requested to define a handful of
printer escape codes in "myprinter.h" specific to the intended slave
printer.  The present source defines such escape sequences for the
   C ITOH 8510A     dot matrix printer (ImageWriter compatible),
   ROYAL BETA 8000  typewriter (Diablo 630 compatible),
   DECWRITER II     serial printer.
Posted or E-mailed additions to this printcap are accepted with
gratitude, and will be included in an update of UNIFORM.

Installation procedures are given in Install.  System- and printer-
specific compile-time defines (notably REALUNIX for SysV) should be
enabled in Makefile prior to compiling the source.  Use
"troff -man uniform.man" to print the operating instructions.
//E*O*F README//

echo x - globals.h
cat > "globals.h" << '//E*O*F globals.h//'
/* globals.h */

#include <curses.h>
#include <signal.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifndef X_OK
#    ifdef REALUNIX
#        define F_OK 0
#        define X_OK 1
#        define W_OK 2
#        define R_OK 4
#        include <fcntl.h>
#    else
#        include <sys/file.h>
#    endif
#endif

#define BAILOUT   sprintf (Mesg, "bad write at %d of %s",\
					 __LINE__, __FILE__), goaway()

#define BADCHARP(p)  ((p) == (char *)NULL || *(p) == '\0')
#define NULCHARP(p)  ((p) == (char *)NULL)

#define ABORT     1         /* ASCII value of ^A character */
#define COMMENT   1         /* ASCII value of ^A character */
#define LITTLINE  12        /* ASCII value of ^L character */
#define FEED      6         /* ASCII ^F as alternate RETURN in getvar */
#define DELETE    127       /* ASCII value of DEL character */
#define SPACE     32        /* ASCII value of the space character */
#define TOKBUFSIZ 256       /* length limit of input token string */
#define COUNTERS  0         /* y screen position of counter displays */
#define TOPLINE   5         /* y of first line of echoed user text */
#define BOTLINE   (LINES-5) /* y of last line of echoed user text */

int  goaway();
int  fstat();
int  stat();
char *malloc();
char *gets();

#ifdef MAIN

char Record[BUFSIZ*2]; /* buffer for new data, prior to saving to file */
char Mesg[256];      /* scratch buffer for interface to user */
char File[256];      /* recording the session */
char Onefeed[128];   /* CRLF escape sequence plus margin */
char Eightfeed[128]; /* eightfeed escape sequence plus margin */
char *Recp;          /* to base of malloc buffer for data from file */
char *Ep;            /* to end of &Recp (cutting off sentinel newline) */
int  Partial;        /* 1 == continue work on incomplete file */
int  Margin;         /* blanks to align print head with left edge of box */
int  Descr;          /* file descriptor to open /dev/$PRINTER */
int  Curx, Cury;     /* screen cursor position */
int  Lcnt;           /* microfeed increments; singlespaced line=6mf */
int  Ccnt;           /* print character position */
int  Back;           /* how many characters can be back spaced */
int  Onefeedsiz;     /* length of onefeed string */
int  Eightfeedsiz;   /* length of eightfeed string */

#else

extern char *Prtype;
extern char *Setmesg0;
extern char *Setmesg1;
extern char *Setmesg2;
extern char prinit[];
extern char prreset[];
extern char cr[];
extern char lf[];
extern char crlf[];
extern char tabfeed[];
extern char bs[];
extern char pi[];
extern char el[];
extern char co[];
extern char bo[];
extern char kb[];

extern char Record[];
extern char Mesg[];
extern char File[];
extern char Onefeed[];
extern char Eightfeed[];
extern char *Recp;
extern char *Ep;
extern int  Partial;
extern int  Margin;
extern int  Descr;
extern int  Curx, Cury;
extern int  Lcnt;
extern int  Ccnt;
extern int  Back;
extern int  Onefeedsiz;
extern int  Eightfeedsiz;

#endif
//E*O*F globals.h//

echo x - myprinter.h
cat > "myprinter.h" << '//E*O*F myprinter.h//'
/* myprinter.h */

#ifndef PRINTERPORT
#define PRINTERPORT "/dev/lp"
#endif


/****************************************************************/
#ifdef CITOH
	char *Prtype = "C Itoh Model 8510A";
	char *Setmesg0 =
"Printer head seeks to left or right boundaries of an imaginary window;";
	char *Setmesg1 =
"do not use head as an indication for horizontal print position in line.";
	char *Setmesg2;

	/* PRINIT: set printer to incremental mode ("by byte" print),
	   set single line feed width to 1/48" (eight feeds/singlespaced line),
	   set elite font, add trailing carriage return, no line feed */
	char prinit[] = {27, 91, 27, 84, 48, 51, 27, 69, 13, 0};

	/* PRRESET: reset printer to initial conditions: buffered mode,
	   line feed width 1/6" (== singlespaced),
	   reset to pica font, add trailing crlf */
	char prreset[] = {27, 93, 27, 84, 50, 52, 27, 78, 13, 10, 0};

	/* TABFEED: eight normal line feeds in succession == 1 singlespaced line */
	char tabfeed[] = {13,10,13,10,13,10,13,10,13,10,13,10,13,10,13,10,0};

	char cr[] = {13, 0};        /* carriage return without line feed */
	char lf[] = {10, 0};        /* line feed without cr */
	char crlf[] = {13, 10, 0};  /* carriage_return+linefeed: normal feed */

	/* optional capabilities */
	char bs[] = {8, 0};         /* backspace */
	char pi[] = {27, 78, 0};    /* pica pitch/font */
	char el[] = {27, 69, 0};    /* elite pitch/font */
	char co[] = {27, 81, 0};    /* compressed pitch/font */
	char bo[] = {27, 33, 0};    /* bold print: overstrike */
	char kb[] = {27, 34, 0};    /* kill overstrike */
#endif


/****************************************************************/
#ifdef ROYAL
	char *Prtype = "Royal Beta 8000 Electric Typewriter";
	char *Setmesg0 =
"Advance to right margin, hit MAR REL; advance to right edge, hit MAR RIGHT.";
	char *Setmesg1 =
"After setting margin, get printer on-line by simultaneously pressing CODE o";
	char *Setmesg2;

	/* PRINIT:
	   set single line feed width to 1/48" (eight feeds/singlespaced line),
	   set elite pitch, add trailing carriage return, no line feed */
	char prinit[] = {27, 30, 2, 27, 31, 11, 13, 0};

	/* PRRESET: reset to singlespaced, pica pitch, add trailing crlf */
	char prreset[] = {27, 30, 9, 27, 31, 13, 13, 10, 0};

	/* TABFEED: eight normal line feeds in succession == 1 singlespaced line */
	char tabfeed[] = {13,10,13,10,13,10,13,10,13,10,13,10,13,10,13,10,0};

	char cr[] = {13, 0};         /* carriage return without line feed */
	char lf[] = {10, 0};         /* line feed without cr */
	char crlf[] = {13, 10, 0};   /* carriage_return+linefeed: normal feed */

	/* optional capabilities */
	char bs[] = {8, 0};          /* backspace */
	char pi[] = {27, 31, 13, 0}; /* pica pitch */
	char el[] = {27, 31, 11, 0}; /* elite pitch */
	char co[] = {27, 31, 9, 0};  /* compressed pitch */
	char bo[] = {27, 79, 0};     /* bold print: automatically cleared on CR */
	char kb[] = {27, 38, 0};     /* kill overstrike */
#endif


/****************************************************************/
#ifdef DECWRITER
/* a practically useless printer, serving as the lowest common denominator */
	char *Prtype = "Digital DECWRITER II at 300 baud";
	char *Setmesg0 =
"Minimum linefeed width for printer is 1/6\" --- use ^L instead of RETURN";
	char *Setmesg1;
	char *Setmesg2;

	char prinit[] = {0};
	char prreset[] = {13, 10, 0};

	/* TABFEED: eight normal line feeds in succession */
	char tabfeed[] = {13,10,13,10,13,10,13,10,13,10,13,10,13,10,13,10,0};

	char cr[] = {13, 0};        /* carriage return without line feed */
	char lf[] = {10, 0};        /* line feed without cr */
	char crlf[] = {13, 10, 0};  /* carriage_return+linefeed: normal feed */

	/* optional capabilities */
	char bs[] = {8, 0}; /* backspace */
	char pi[] = {0};    /* pica pitch/font */
	char el[] = {0};    /* elite pitch/font */
	char co[] = {0};    /* compressed pitch/font */
	char bo[] = {0};    /* bold print: overstrike */
	char kb[] = {0};    /* kill overstrike */
#endif
//E*O*F myprinter.h//

echo x - adjust.c
cat > "adjust.c" << '//E*O*F adjust.c//'
/* adjust.c */
#include "globals.h"

char *
adjust (new)
int new;
{
	char *getvar(), *readrem();
	char tokbuf[TOKBUFSIZ];
	char *rp;
	char *bp;
	int scrflag = new;
	int cnt;
	int first;
	int ry;
	int cy;
	int remy = 4;            /* comments, if applicable */
	char c;

mvaddstr(0, 0, "Using: "), addstr (Prtype);
	if (Setmesg0 != NULL)
		mvaddstr (1, 0, Setmesg0);
	if (Setmesg1 != NULL)
		mvaddstr (2, 0, Setmesg1);
	if (Setmesg2 != NULL)
		mvaddstr (3, 0, Setmesg2);

	if (new) {
		showhow();
		rp = Record;
	}
	else { /* file was read at Recp in main(); may begin with comments */
		rp = Recp;
		if (*rp != COMMENT)
			showhow();
		else {
			for (cnt = 4; cnt < 18; cnt++) {
				rp = readrem (rp, tokbuf, COLS);
				if (*tokbuf)
					mvaddstr (cnt, 0, tokbuf);
			}
		}
	}

ry = 18;
mvaddstr(ry++, 0, "Type ");
standout(), addstr ("spaces"), standend();
addstr (" from the computer, until the next print position coincides with");

mvaddstr(ry++, 0, "the spot where the bracket should go; then hit ");
standout(), addstr ("RETURN"), standend();
addstr (" to print the bracket.");

mvaddstr(ry++, 0,
"Repeat the procedure if the bracket is in the wrong place.  After alignment");

mvaddstr(ry++, 0, "hit ");
standout(), addstr ("ESC"), standend();
addstr (" to continue.  Hit ");
standout(), addstr ("^A"), standend();
addstr (" to abort: process may hang if printer is \"OFF\"");

	refresh();
	move (Cury = ry, Curx = 0);
	refresh();

	sprintf (tokbuf, "]%s", cr);
	for (first = cnt = 1; c = getchar();) {
		if (first && c != ABORT) {
			first = 0;
			if (write (Descr, prinit, strlen (prinit)) < 0)
				BAILOUT;
		}
		switch (c) {
			case SPACE:
				addch ('.');
				if (! (++Curx % COLS))
					++Cury;
				refresh();
				if (write (Descr, " ", 1) != 1)
					BAILOUT;
				++cnt;
				break;

			case 27:
				move (0, 0);
				clrtobot();
				refresh();

				strcpy (Onefeed, crlf);
				bp = Onefeed + strlen (Onefeed);
				for (cnt = Margin; --cnt >= 0; *bp++ = SPACE);
				*bp = 0;
				Onefeedsiz = strlen (Onefeed);

				strcpy (Eightfeed, tabfeed);
				bp = Eightfeed + strlen (Eightfeed);
				for (cnt = Margin; --cnt >= 0; *bp++ = SPACE);
				*bp = 0;
				Eightfeedsiz = strlen (Eightfeed);

				return (rp);

			case ABORT: /* abort, do not return to main */
				if (!first && write (Descr, prreset, strlen (prreset)) < 0)
					BAILOUT;
				goaway();

			case '\n':
			case '\r':
				addch (']');
				move (Cury=ry, Curx=0);
				refresh();
				if (write (Descr, tokbuf,  strlen (tokbuf)) < 0)
					BAILOUT;
				Margin = cnt;
				cnt = 1;
				break;

			/* Instead of turning platen knob for vertical adjust,
			   ^L advances paper by microfeed, leaves head in place.
			*/
			case LITTLINE:
				if (write (Descr, lf,  strlen (lf)) < 0)
					BAILOUT;
				break;

			case DELETE: /* just an inconvenient value above 31 */
				break;

			/* For any printing characters, start automatic comment
			   routine if new file is written, otherwise do nothing
			*/
			default:
				if (c > 32) {
					if (new && remy < 18) {
						getyx (stdscr, Cury, Curx);
						if (scrflag) {
	                        scrflag = 0;
							for (cy = remy+14; --cy >= remy;)
								move (cy, 0), clrtoeol();
	                    }
						move (Cury, Curx);
						refresh();
						sprintf (rp, "%c%s\n", COMMENT,
							getvar (remy++, 0, COLS-1, c));
						rp += strlen (rp);
					}
				}
				break;
		}
	}
}

showhow ()
{
	int ry = 4;

mvaddstr(ry++, 0,
"Insert the form into the printer,  positioning the top part of the paper on");
mvaddstr(ry++, 0,
"the printer platen (under the print head, ready for printing).  The goal of");
mvaddstr(ry++, 0,
"the following alignment procedure is to print a square bracket (shown below");
mvaddstr(ry++, 0,
"in inverse) just to the left of the top corner of the top box on the paper.");

mvaddstr(ry++,20, " _____________________________________");
mvaddstr(ry++,20, "| upper edge of paper");
mvaddstr(ry++,20, "|      _______________________________");
mvaddstr(ry,  20, "|    *| top left box            |");
standout(), mvaddstr(ry++,25,"]"), standend();
mvaddstr(ry++,20, "|     |_________________________|_____");
mvaddstr(ry++,20, "|     |                   |");
mvaddstr(ry++,20, "|     |___________________|___________");
mvaddstr(ry++,20, "|     |");

mvaddstr(ry++, 0,
"Turn the printer platen knob to get the print head in line with the topmost");

mvaddstr(ry++, 0, "box of the form, or by typing ");
standout(), addstr ("^L"), standend();
addstr (" (CONTROL-L) characters  from the computer.");
}

char *
readrem (ptr, target, maxc)
char *ptr, *target;
int maxc;
{
	char *rp = target;

	if (ptr == Ep || *ptr != COMMENT) {
		*rp = 0;
		return (ptr);
	}

	while (++ptr !=Ep && *ptr != '\n' && maxc--)
		*rp++ = *ptr;
	*rp = 0;

	if (!maxc) /* comment line too long */
		while (++ptr !=Ep && *ptr != '\n');

	if (ptr != Ep)
		++ptr; /* pass the newline */

	return (ptr);
}
//E*O*F adjust.c//

echo x - counters.c
cat > "counters.c" << '//E*O*F counters.c//'
/* counters.c */
#include "globals.h"

/* horizontal beginning column of counter displays */
#define  LINEX      14
#define  MICROX     27
#define  COLUMNX    36
#define  SCREENXX   45
#define  SCREENYX   54
#define  BACKX      63

/* Display running count of Lcnt, Ccnt, Curx, Cury, Back in top line,
   move cursor back to current screen coordinates Cury, Curx
*/
counters()
{
	char microbuf[8];
	char colbuf[8];
	char ybuf[8];
	char xbuf[8];
	char bbuf[8];
	char *fourspace = "    ";

	sprintf (microbuf, "%d", Lcnt/8);
	mvaddstr (COUNTERS, LINEX, fourspace);
	mvaddstr (COUNTERS, LINEX, microbuf);

	sprintf (microbuf, "%d", Lcnt);
	mvaddstr (COUNTERS, MICROX, fourspace);
	mvaddstr (COUNTERS, MICROX, microbuf);

	sprintf (colbuf, "%d", Ccnt);
	mvaddstr (COUNTERS, COLUMNX, fourspace);
	mvaddstr (COUNTERS, COLUMNX, colbuf);

	sprintf (xbuf, "%d", Curx);
	mvaddstr (COUNTERS, SCREENXX, fourspace);
	mvaddstr (COUNTERS, SCREENXX, xbuf);

	sprintf (ybuf, "%d", Cury);
	mvaddstr (COUNTERS, SCREENYX, fourspace);
	mvaddstr (COUNTERS, SCREENYX, ybuf);

	sprintf (bbuf, "%d", Back);
	mvaddstr (COUNTERS, BACKX, fourspace);
	mvaddstr (COUNTERS, BACKX, bbuf);

	move (Cury, Curx);
}
//E*O*F counters.c//

echo x - etc.c
cat > "etc.c" << '//E*O*F etc.c//'
/* etc.c */
#include "globals.h"

iread (fname, mallocp)
char *fname;
char **mallocp;
{
	struct stat sbuf;
	int checkval, fd;
	int count;

	if (BADCHARP (fname) || (fd = open (fname, 0)) == -1)
		return (-1);
	if ((checkval = fstat (fd, &sbuf)) == -1)
		return (-1);
	if ((count = (int)sbuf.st_size) == 0)
		return (-1);
	if (NULCHARP (*mallocp = malloc ((unsigned int) count+1)))
		return (-1);
	if ((checkval = read (fd, *mallocp, count)) != count)
		return (-1);
	close (fd);
	*(*mallocp + count) = 0;
	return (checkval);
}

input (ptr, message)
char *ptr, *message;
{
	char *pp;

	fprintf (stderr, message);
	gets (ptr);
	for (pp = ptr; *pp++;);
	return (--pp - ptr);
}

iwrite (fname, start, end, append)
char *fname;
char *start;
char *end;
int  append;
{
	struct stat sbuf;
	int checkval, descr;
	int perm = 0644;
	long offset;
	long lseek();

	if (BADCHARP (fname))
		return (-1);
	if ((checkval = stat(fname, &sbuf)) != -1)
		perm = (int)sbuf.st_mode;

	if (NULCHARP (start)) {
		if ((descr = open(fname, O_RDWR | O_CREAT, perm)) == -1)
			return(-1); /* no access */
		if (checkval != -1) {
			close(descr);
			return(1); /* it was there, it's OK, didn't touch it */
		}
		unlink(fname);
		return(0); /* wasn't there, not there now, looks OK */
	}

	if (append) {
		if ((descr = open (fname, O_WRONLY, perm)) == -1)
			return (-1);

		/* File has a sentinel newline at end so it can be edited regardless
		   of where the learning process ended it; must loose this newline
		   to continue with new data.
		*/
		offset = (long) sbuf.st_size;
		if (lseek (descr, offset-1, 0) == -1)
			return (-1);
	}
	else if ((descr = open (fname, O_WRONLY | O_CREAT | O_TRUNC, perm)) == -1)
		return (-1);

	if ((checkval = write(descr, start, end-start)) != end-start)
		return (-1);
	close (descr);
	return (checkval);
}

goaway()    /* restore terminal in case of interrupt */
{
	mvcur (0, COLS-1, LINES-1, 0);
	endwin();
	printf ("%s\n", Mesg);
	exit (0);
}
//E*O*F etc.c//

echo x - getvar.c
cat > "getvar.c" << '//E*O*F getvar.c//'
/* getvar.c */
#include "globals.h"

char *
getvar (vary, varx, varlen, comment)
int vary, varx, varlen, comment;
{
	static char varbuf[TOKBUFSIZ];
	char varcnt[8];
	char c;
	char *p;
	int  ri;
	int  ly, lx;

	getyx (stdscr, Cury, Curx);
	move (vary,   0), clrtoeol();
	if (!comment) {
		move (vary+1, 0), clrtoeol();
		move (vary+2, 0), clrtoeol();
	}
	move (ly = vary, lx = 0);

	p = varbuf;
	if (comment) {
		addch (comment);
		++lx, --varlen;
		*p++ = comment;
	}
	refresh();

	for (ri = varlen; c = getchar(); ri--) {
		if (c == DELETE)
			c = '\b';

		if (!comment && c == LITTLINE) {
			++ri; /* no strlen penalty */
			if (write (Descr, Onefeed, Onefeedsiz) != Onefeedsiz)
					BAILOUT;
		}
		else if (!comment && c == FEED) {
			++ri;
			if (write (Descr, Eightfeed, Eightfeedsiz) != Eightfeedsiz)
						BAILOUT;
		}
		/* quit printing, do not go back to main */
		else if (!comment && c == ABORT) {
			if (write (Descr, prreset, strlen (prreset)) < 0)
				BAILOUT;
			goaway();
		}
		else if (c == '\n' || c == '\r' || (!ri && c != '\b')) {
			if (!comment)
				while (ri--)
					*p++ = SPACE;
			*p = 0;
			move (Cury, Curx);
			refresh();
			return (varbuf);
		}
		else if (c > 31) {
			*p++ = c;
			addch (c);
			if (! (++lx % COLS))
				lx = 0, ++ly;
			if (!comment) {
				sprintf (varcnt, "%d", ri-1);
				mvaddstr (vary-3, varx, "   ");
				mvaddstr (vary-3, varx, varcnt);
			}
			move (ly, lx);
			refresh();
		}
		else if (c == '\b' && p > varbuf) {
			--p, ri += 2; /* 1 byte for original character, 1 byte for \b */
			if (lx)
				--lx;
			else
				lx = COLS-1, --ly;
			if (!comment) {
				sprintf (varcnt, "%d", ri-1);
				mvaddstr (vary-3, varx, "   ");
				mvaddstr (vary-3, varx, varcnt);
			}
			move (ly, lx);
			clrtoeol();
			refresh();
		}
		else
			++ri;
	}
}
//E*O*F getvar.c//

echo x - highlight.c
cat > "highlight.c" << '//E*O*F highlight.c//'
/* highlight.c */
#include "globals.h"

/* horizontal beginning column and Y locations of display of active keys */
#define  LFX        0
#define  LFY        (BOTLINE+2)
#define  RETX       12
#define  RETY       (BOTLINE+2)
#define  UNX        29
#define  UNY        (BOTLINE+2)
#define  TABX       41
#define  TABY       (BOTLINE+2)
#define  BSX        52
#define  BSY        (BOTLINE+2)
#define  SPX        65
#define  SPY        (BOTLINE+2)

#define  COX        0
#define  COY        (BOTLINE+3)
#define  PIX        12
#define  PIY        (BOTLINE+3)
#define  ELX        20
#define  ELY        (BOTLINE+3)
#define  KBX        29
#define  KBY        (BOTLINE+3)
#define  BOX        41
#define  BOY        (BOTLINE+3)
#define  REX        52
#define  REY        (BOTLINE+3)
#define  VARX       65
#define  VARY       (BOTLINE+3)

/* Any call first turns off the highlighting of the previous display,
   then highlights the display of an active control key.
   Cursor is moved back to the current screen coordinates Cury, Curx.
*/
highlight (spec)
int spec;
{
	static int prev;

	switch (prev) {
		default:
			break;
		case LITTLINE: /* ^L */
			mvaddstr (LFY, LFX, "^L");
			break;
		case '\n':
		case '\r':
			mvaddstr (RETY, RETX, "RETURN");
			break;
		case 21: /* ^U */
			mvaddstr (UNY, UNX, "^U");
			break;
		case '\t':
			mvaddstr (TABY, TABX, "TAB");
			break;
		case '\b':
			mvaddstr (BSY, BSX, "^H");
			break;
		case SPACE:
			mvaddstr (SPY, SPX, "SPACE");
			break;
		case 3: /* ^C */
			mvaddstr (COY, COX, "^C");
			break;
		case 16: /* ^P */
			mvaddstr (PIY, PIX, "^P");
			break;
		case 5: /* ^E */
			mvaddstr (ELY, ELX, "^E");
			break;
		case 11: /* ^K */
			mvaddstr (KBY, KBX, "^K");
			break;
		case 2: /* ^B */
			mvaddstr (BOY, BOX, "^B");
			break;
		case 18: /* ^R */
			mvaddstr (REY, REX, "^R");
			break;
		case 22: /* ^V */
			mvaddstr (VARY, VARX, "^V");
			break;
	}

	switch (spec) {
		default:
			break;
		case LITTLINE: /* ^L */
			standout();
			mvaddstr (LFY, LFX, "^L");
			standend();
			break;
		case '\n':
		case '\r':
			standout();
			mvaddstr (RETY, RETX, "RETURN");
			standend();
			break;
		case 21: /* ^U */
			standout();
			mvaddstr (UNY, UNX, "^U");
			standend();
			break;
		case '\t':
			standout();
			mvaddstr (TABY, TABX, "TAB");
			standend();
			break;
		case '\b':
			standout();
			mvaddstr (BSY, BSX, "^H");
			standend();
			break;
		case SPACE:
			standout();
			mvaddstr (SPY, SPX, "SPACE");
			standend();
			break;
		case 3: /* ^C */
			standout();
			mvaddstr (COY, COX, "^C");
			standend();
			break;
		case 16: /* ^P */
			standout();
			mvaddstr (PIY, PIX, "^P");
			standend();
			break;
		case 5: /* ^E */
			standout();
			mvaddstr (ELY, ELX, "^E");
			standend();
			break;
		case 11: /* ^K */
			standout();
			mvaddstr (KBY, KBX, "^K");
			standend();
			break;
		case 2: /* ^B */
			standout();
			mvaddstr (BOY, BOX, "^B");
			standend();
			break;
		case 18: /* ^R */
			standout();
			mvaddstr (REY, REX, "^R");
			standend();
			break;
		case 22: /* ^V */
			standout();
			mvaddstr (VARY, VARX, "^V");
			standend();
			break;
	}
	prev = spec;
	move (Cury, Curx);
}
//E*O*F highlight.c//

echo x - makeform.c
cat > "makeform.c" << '//E*O*F makeform.c//'
/* makeform.c */
#include "globals.h"

makeform (inside, abrupt)
char *inside;
int abrupt;
{
	char tokbuf[TOKBUFSIZ]; /* for getting constant or variable */
	char bsbuf[TOKBUFSIZ];  /* simulated backspace if no bs capability */
	char cret[128];      /* carriage return without linefeed, plus margin */
	char *bp;            /* buffer pointer into tokbuf */
	char *rp;            /* buffer pointer into Record */
	char *undop;         /* to leftmost history byte affected by undo */
	char *leasttok;      /* leftmost tokbuf byte to which bs applies */
	char *limp;          /* limit at rightmost available tokbuf position */
	char *bsp;           /* mark the print start byte in bsbuf */
	char c;              /* for holding getchar() */
	int  ri;
	int  toksiz;         /* length of finished constant or variable */
	int  cretsiz;        /* length of cret string */
	int  bssiz;          /* length of backspace string */
	int  leastimm;       /* least immediate line col to which bs applies */
	int  emptyline;      /* flag that no printing character got into line */
	int  variable;       /* flag that variable definition is under way */

	/* set up carriage return without line feed, + bytes of left margin */
	strcpy (cret, cr);
	bp = cret + strlen (cret);
	for (ri = Margin; --ri >= 0; *bp++ = SPACE);
	*bp = 0;
	cretsiz = strlen (cret);

	/* initialize bsbuf with spaces */
	if ((bssiz = strlen (bs)) == 0) {
		for (bsp = bsbuf, ri = TOKBUFSIZ; --ri; *bsp++ = SPACE);
		*bsp = 0;
	}

	if (Margin) {
		bp = tokbuf;
		for (ri = Margin; --ri >= 0; *bp++ = SPACE);
		*bp = 0;
		if (!abrupt)
			if (write (Descr, tokbuf, strlen (tokbuf)) < 0)
				BAILOUT;
	}
	Lcnt = Ccnt = Back = leastimm = 0;
	clear();

	mvaddstr (COUNTERS, 0,
"quit:ESC line:    littline:    Pcol:    scrX:    scrY:    back:    abort: ^A");

	mvaddstr (BOTLINE+2, 0,
"^L:littline RETURN:normaline ^U:undoline TAB:8space ^H:backspace SPACE:space");

	mvaddstr (BOTLINE+3, 0, "^C:");
	if (strlen (co))
		addstr ("compress ^P:");
	else
		addstr ("         ^P:");
	if (strlen (pi))
		addstr ("pica ^E:");
	else
		addstr ("     ^E:");
	if (strlen (el))
		addstr ("elite ^K:");
	else
		addstr ("      ^K:");
	if (strlen (kb))
		addstr ("killbold ^B:");
	else
		addstr ("         ^B:");
	if (strlen (bo))
		addstr ("boldfnt ^R:redisplay ^V:variable");
	else
		addstr ("        ^R:redisplay ^V:variable");

	Cury=TOPLINE, Curx=0;
	counters();
	refresh();
	rp = undop = inside;
	limp = tokbuf + TOKBUFSIZ -10; /* allow tab expansion past limp */


/*************************
   MAIN INTERACTIVE LOOP 
 *************************/
	for (emptyline = 1, variable = 0; c = getchar();) {


		/******************************
		   COLLECT TOKEN/VARIABLE LOOP 
		 ******************************/
		if (c == DELETE)
			c = '\b';
		/* collect string if any printing character was typed */
		if (c > 32) {
			highlight (0);
varentry:
			emptyline = 0;
			bp = leasttok = tokbuf;
			*bp++ = c; /* printing char or ^V */
			if (!variable)
				addch (c), Ccnt++, Back++;

			/* advance leasttok to first byte of current value of variable */
			if (variable) {
				++leasttok; /* past the ^V */
				varname (&leasttok);
				bp = leasttok;
			}
			else if (! (++Curx % COLS))
				++Cury, Curx = 0; /* Cury will not exceed BOTLINE */
			counters();
			refresh();

			for (; c = getchar(); counters(), refresh()) {
				if (c == DELETE)
					c = '\b';
				if (Ccnt > 247 && c != '\b' && c != ABORT)
					c = 21; /* automatic ^Undo on line overflow */

				/* RETURN (newline) or tokbuf limit terminates string */
				if (c == '\n' || c == '\r' || bp >= limp) {
					if (variable)
						 *bp++ = 22; /* terminating ^V */
					*bp = 0; /* do not put newline in buffer */
					Back = 0;
					break;
				}

				/* convert each tab unilaterally to eight spaces */
				else if (c == '\t') {
					Ccnt += 8;
					Back += 8;
					highlight (c);
					for (ri = 8; --ri >= 0; addch (SPACE)) {
						*bp++ = SPACE;
						if (! (++Curx % COLS))
							++Cury, Curx = 0; /* won't exceed BOTLINE */
					}
				}

				/* ^U (undo) clears history to beginning of print line */
				else if (c == 21) {
					highlight (c);
					*tokbuf = 0;
					variable = 0;
					move (COUNTERS+2, 0), clrtoeol();
					move (COUNTERS+3, 0), clrtoeol();
					emptyline = 1;
					rp = undop;
					/* length of entire string also includes present toksiz */
						Cury -= Ccnt/COLS;   /* 0, 1, 2, or 3 */
					move (Cury, Curx=0); /* no clrtoeol */
					Ccnt = Back = leastimm = 0;
					counters();
					refresh();
					if (write (Descr, cret, cretsiz) != cretsiz)
						BAILOUT;
					break;
				}

				/* abort, do not write file, do not go back to main */
				else if (c == ABORT) {
					if (write (Descr, prreset, strlen (prreset)) < 0)
						BAILOUT;
					goaway();
				}

				/* clear bytes maximally to beginning of string */
				else if (c == '\b') {
					highlight (c);
					*bp = 0;
					if (bp > leasttok) {
						--bp, --Ccnt, --Back;
						if (Curx)
							--Curx;
						else {
							move (Cury, Curx);
							clrtoeol();
							/* will not underrun TOPLINE */
							Curx = COLS-1, --Cury;
						}
						move (Cury, Curx);
						clrtoeol();
					}
				}

				/* all printing characters */
				else if (c > 31) {
					++Ccnt, ++Back;
					highlight (c);
					*bp++ = c;
					addch (c);
					if (! (++Curx % COLS))
						++Cury, Curx = 0; /* won't overrun BOTLINE */
				}
			}

			/* write out finished non-null string */
			if (variable) {
				variable = 0;
				move (COUNTERS+2, 0), clrtoeol();
				move (COUNTERS+3, 0), clrtoeol();
				move (Cury, Curx);
				toksiz = strlen (leasttok) -1; /* no trailing ^V to printer */

				/* variable stays in stream even if value is empty */
				leastimm = 0;
				strcpy (rp, tokbuf);
				rp += strlen (tokbuf);
				counters();
				refresh();
			}
			else {
				toksiz = strlen (tokbuf);
				if (toksiz) {
					leastimm = 0;
					strcpy (rp, tokbuf);
					rp += strlen (tokbuf);
					counters();
					refresh();
				}
			}
			if (toksiz)
				if (write (Descr, leasttok, toksiz) != toksiz)
					BAILOUT;
		}

		/***************************
		   IMMEDIATE RESPONSE LOOP 
		 ***************************/
		else {
			if (Ccnt > 247 && c != '\b' && c != ABORT)
				c = 21; /* automatic ^Undo for line overflow */

			highlight (c);
			refresh();
			switch (c) {
				/* garbage; do nothing */
				default:
					break;

				case SPACE:
					++Ccnt, ++Back, ++leastimm;
					if (++Curx % 5)
						addch ('.');
					else
						addch ('|');
					if (! (Curx % COLS))
						++Cury, Curx = 0; /* won't overrun BOTLINE */
					*rp++ = SPACE;
					counters();
					refresh();
					if (write (Descr, " ", 1) != 1)
						BAILOUT;
					break;

				case '\t':
					Ccnt += 8;
					Back += 8;
					leastimm += 8;
					for (ri = 8; --ri >= 0; *rp++ = SPACE) {
						if (++Curx % 5)
							addch ('.');
						else
							addch ('|');
						if (! (Curx % COLS))
							++Cury, Curx = 0; /* won't overrun BOTLINE */
					}
					counters();
					refresh();
					if (write (Descr, "        ", 8) != 8)
						BAILOUT;
					break;

				/* ctrl-L small line feed */
				case LITTLINE:
					Lcnt++, Curx = 0; /* even if there were no tokens */

					/* proceed to the next line on the screen only if
					   present line contains tokens
					*/
					if (!emptyline) {
						emptyline = 1;
						if (++Cury > (BOTLINE-3))
							newscreen();
					}
					else
						rp = undop;
					*rp++ = '\n';
					undop = rp;
					Ccnt = Back = leastimm = 0;

					/* ....|.. or possible screen garbage from previous undo */
					move (Cury, Curx), clrtoeol();
					counters();
					refresh();
					if (write (Descr, Onefeed, Onefeedsiz) != Onefeedsiz)
						BAILOUT;
					break;

				case '\n':
				case '\r':
					Lcnt += 8, Curx = 0;

					/* proceed to the next line on the screen only if
					   present line contains tokens
					*/
					if (!emptyline) {
						emptyline = 1;
						if (++Cury > (BOTLINE-3))
							newscreen();
					}
					else
						rp = undop; /* don't print spaces, just linefeed */
					for (ri = 8; --ri >= 0; *rp++ = '\n');
					undop = rp;
					Ccnt = Back = leastimm = 0;

					/* ....|.. or possible screen garbage from previous undo */
					move (Cury, Curx), clrtoeol();
					counters();
					refresh();
					if (write (Descr, Eightfeed, Eightfeedsiz) != Eightfeedsiz)
						BAILOUT;
					break;

				/* ^Undo clears history to beginning of print line, returns head
				   to left margin, leaves already printed characters on paper
				*/
				case 21:
					emptyline = 1;
					rp = undop;
					Cury -= Ccnt/COLS;   /* 0, 1, 2, or 3 */
					move (Cury, Curx=0); /* learn from mistake; no clrtoeol */
					Ccnt = Back = leastimm = 0;
					counters();
					refresh();
					if (write (Descr, cret, cretsiz) != cretsiz)
						BAILOUT;
					break;

				/* ^H or BACKSPACE or DELETE clears bytes, maximally to
				   beginning of line or to end of previous variable or
				   constant, whichever is nearest.
				*/
				case '\b':
					highlight (c);
					if (leastimm) {
						--leastimm, --Ccnt, --Back;
						if (Curx)
							--Curx;
						else {
							move (Cury, Curx);
							clrtoeol();
							Curx = COLS-1, --Cury;
						}
						move (Cury, Curx);
						clrtoeol();
						while (*--rp < 32);
						counters();
						if (bssiz) {
							if (write (Descr, bs, bssiz) != bssiz)
								BAILOUT;
						}
						else {
							if (write (Descr, cret, cretsiz) != cretsiz)
								BAILOUT;
							bsp = bsbuf + TOKBUFSIZ -1 -Ccnt;
							ri = strlen (bsp);
							if (write (Descr, bsp, ri) != ri)
								BAILOUT;
						}
					}
					refresh();
					break;

				/*************************************************
				   change printer attributes, no update of screen
				 *************************************************/

				/* ctrl-P to select pica font */
				case 16:
					emptyline = 0;
					*rp++ = 16;
					if (write (Descr, pi, strlen (pi)) < 0)
						BAILOUT;
					break;

				/* ctrl-E to select elite font */
				case 5:
					emptyline = 0;
					*rp++ = 5;
					if (write (Descr, el, strlen (el)) < 0)
						BAILOUT;
					break;

				/* ctrl-C to select compressed font */
				case 3:
					emptyline = 0;
					*rp++ = 3;
					if (write (Descr, co, strlen (co)) < 0)
						BAILOUT;
					break;

				/* ctrl-B to select overstrike (bold) */
				case 2:
					emptyline = 0;
					*rp++ = 2;
					if (write (Descr, bo, strlen (bo)) < 0)
						BAILOUT;
					break;

				/* ctrl-K to kill overstrike (kill bold) */
				case 11:
					emptyline = 0;
					*rp++ = 11;
					if (write (Descr, kb, strlen (kb)) < 0)
						BAILOUT;
					break;

				/* ctrl-R to redisplay screen */
				case 18:
					clearok (curscr, 1);
					refresh();
					break;

				/* ctrl-V to begin variable definition */
				case 22:
					variable = 1;
					goto varentry;

				case ABORT: /* do not write file, do not go back to main */
					if (write (Descr, prreset, strlen (prreset)) < 0)
						BAILOUT;
					goaway();

				case 27: /* quit, return to main */
					*rp++ = '\n'; /* so that saved file can be edited */
					if (iwrite (File, Record, rp, Partial) < 0)
						sprintf (Mesg, "can't write Record to %s", File);
					return;
			}
		}
	}
}

varname (ptr)
char **ptr;
{
	char *p;
	int  ri;
	int  lx, ly;
	char namebuf[3];
	char c;

	p = *ptr;
	mvaddstr (COUNTERS+2, 0, "VARIABLE NAME (MAX. 45 CHARS): ");
	getyx (stdscr, ly, lx);
	refresh();
	for (ri = 45; c = getchar(); ri--) {
		if (c == DELETE)
			c = '\b';
		if (c == '\n' || c == '\r' || (!ri && c != '\b')) {
			*p++ = 22; /* the second ^V */
			*p = 0;
			*ptr += strlen (*ptr);
			mvaddstr (COUNTERS+3, 0,
"Give current value, padding at the end with SPACE/TAB to the maximum length:");
			move (Cury, Curx);
			refresh();
			return;
		}
		else if (c > 31) {
			*p++ = c;
			addch (c);
			++lx;
			sprintf (namebuf, "%d", ri-1);
			mvaddstr (COUNTERS+2, 20, "  ");
			mvaddstr (COUNTERS+2, 20, namebuf);
			move (ly, lx);
			refresh();
		}
		else if (c == '\b' && p > *ptr) {
			--p, ri += 2; /* 1 byte for original character, 1 byte for \b */
			move (ly, --lx);
			clrtoeol();
			sprintf (namebuf, "%d", ri-1);
			mvaddstr (COUNTERS+2, 20, "  ");
			mvaddstr (COUNTERS+2, 20, namebuf);
			move (ly, lx);
			refresh();
		}
		else
			++ri;
	}
}

newscreen()
{
	int ri;

	for (ri = TOPLINE; ri <= BOTLINE; ri++)
		move (ri, 0), clrtoeol();
	Cury = TOPLINE, Curx = 0;
}
//E*O*F makeform.c//

echo x - printform.c
cat > "printform.c" << '//E*O*F printform.c//'
/* printform.c */
#include "globals.h"

int
printform (inside)
char *inside;
{
	char *getvar();
	char tokbuf[TOKBUFSIZ];
	char margbuf[256];
	char varcnt[8];
	int  ri;
	int  crlfsiz;      /* string length of crlf */
	int  varlen;       /* max length of current string variable */
	char *bp;          /* buffer pointer into tokbuf */
	char *rp;          /* buffer pointer into &Recp */
	char *vrp1;        /* place holder pointer in &Recp */
	char *vrp2;        /* place holder pointer in &Recp */

	/* set up linefeed and margin strings */
	crlfsiz = strlen (crlf);
	for (bp = margbuf, ri = Margin; --ri >= 0; *bp++ = SPACE);
	*bp = 0;

	clear();
	for (bp = tokbuf, rp = inside; rp < Ep; rp++) {

		if (*rp > 31) { /* make a string of all normal characters in tokbuf */
			*bp++ = *rp;
			continue;
		}
		switch (*rp) {
			/* weed out garbage */
			default:
				break;

			/* Begin variable; accummulate current value in tokbuf.
			   Variable format in saved file:
...other...^Vvarname^Vpast value padded to max field width^V...other...
			   To process:
			   rp is moved past the first ^V,
			   [variable name] is printed in prompt for user input
			   rp is moved past the second ^V,
			   max width of field is computed as the third ^V - rp,
			   rp is moved to third ^V for post-increment in for-loop,
			   user input of current value is padded to field width,
			   processed variable is added to tokbuf, bp moved past it.
			*/
			case 22:
				vrp1 = ++rp;
				while (*rp++ != 22);
				*(rp-1) = 0;         /* the second ^V */
				vrp2 = rp;           /* beginning of past value */
				while (*rp++ != 22);
				--rp;                /* back to the last ^V */
				varlen = rp - vrp2;
				move (TOPLINE-3, 0), clrtoeol();
				mvaddstr (TOPLINE-3, 0, "CURRENT VALUE (MAX.     CHARS) ");
				addstr (vrp1);
				sprintf (varcnt, "%d", varlen);
				mvaddstr (TOPLINE-3, 20, "   ");
				mvaddstr (TOPLINE-3, 20, varcnt);
				strcpy (bp, getvar (TOPLINE, 20, varlen, 0));
				bp += varlen;
				break;

			/* at line feed, print out accummulated token */
			case '\n':
			case '\r':
				if (bp == tokbuf) { /* multiple line feeds */
					if (write (Descr, crlf, crlfsiz) != crlfsiz)
						BAILOUT;
				}
				else {
					/* print margin if non-null */
					if (Margin && (write (Descr, margbuf, Margin) != Margin))
						BAILOUT;
					strcpy (bp, crlf);
					bp += crlfsiz;
					if (write (Descr, tokbuf, bp - tokbuf) < 0)
						BAILOUT;
					bp = tokbuf;
				}
				break;

			/* ctrl-A begins comment; skip rest of line */
			case COMMENT:
				while (++rp !=Ep && *rp != '\n');
				break;

			/* ctrl-P to select pica font */
			case 16:
				strcpy (bp, pi);
				bp += strlen (pi);;
				break;

			/* ctrl-E to select elite font */
			case 5:
				strcpy (bp, el);
				bp += strlen (el);;
				break;

			/* ctrl-C to select compressed font */
			case 3:
				strcpy (bp, co);
				bp += strlen (co);;
				break;

			/* ctrl-B to select overstrike (bold) */
			case 2:
				strcpy (bp, bo);
				bp += strlen (bo);;
				break;

			/* ctrl-K to kill overstrike (bold) */
			case 11:
				strcpy (bp, kb);
				bp += strlen (kb);;
				break;
		}
	}

	/* print accumulated token if last printer-bound byte was not a newline */
	if (bp != tokbuf) {
		if (Margin && (write (Descr, margbuf, Margin) != Margin))
			BAILOUT;
		if (write (Descr, tokbuf, bp - tokbuf) < 0)
			BAILOUT;
	}
	return (*(Ep-1) != '\n');
}
//E*O*F printform.c//

echo x - uniform.c
cat > "uniform.c" << '//E*O*F uniform.c//'
/* uniform.c --- universal form filer */
/**********************************************************************
* Overlay printed blank forms with computerized information
* uniform         --- fill out sample form, record ("learn") commands
* uniform file    --- fill out blank form from data stored in file
* uniform \&file  --- fill from incomplete file, "learn" rest of form
* Author: Istvan Mohos
* Version 1, March 1990
**********************************************************************/

#include "myprinter.h"
#define MAIN
#include "globals.h"

main (argc, argv)
int argc;
char *argv[];
{
	int new = argc == 1; /* create new form if no filename */
	int no_crlf_end;     /* TRUE if session ended without crlf (in line) */
	int cnt;
	char *file;
	char *remark;        /* point in buffer where parsing should resume */
	char *adjust();

	if ((Descr = open (PRINTERPORT, O_WRONLY)) == -1)
		fprintf (stderr, "can't write to printer\n"), exit (1);
#ifdef BAUD
	sprintf (Mesg, "stty %d > %s", BAUD, PRINTERPORT);
	system (Mesg);
#endif

	if (new) {
		input (File, "Record session under: ");
		if (iwrite (File, (char *)NULL, (char *)NULL, 0) == -1)
			fprintf (stderr, "can't write to %s\n", File), exit (1);
	}
	else {
		file = argv[1];
		if (*file == '&') {
			++file;
			Partial = 1;
			if (access (file, W_OK) == -1)
				fprintf (stderr, "can't append to %s\n", file), exit (1);
		}
		sprintf (File, file);
		if (access (File, R_OK) == -1)
			fprintf (stderr, "can't read %s\n", File), exit (1);
	}

	strcpy (Mesg, "User interrupt... exiting");
	initscr(), raw(), noecho();
	if (COLS < 76 || LINES < 23)
		strcpy (Mesg, "Insufficient screen size... sorry"), goaway();

	if (!new) {
		if ((cnt = iread (File, &Recp)) < 0)
			sprintf (Mesg, "can't read %s", File), goaway();
		Ep = Recp + cnt -1; /* loose sentinel newline at end */
	}

	/* If brand new project, saved data may begin with comments specified
	   by user during adjust().  In this case, "remark" will be moved to
	   just past the comments accummulated in "Record".
	   Else the file is read at "Recp", leading comments are displayed
	   during adjust(), and "remark" will be moved past the displayed
	   comments in "Recp", for printform() to continue from.
	*/
	remark = adjust (new);
	if (new)
		makeform (remark, 0);
	else {
		no_crlf_end = printform (remark);
		if (Partial)
			makeform (Record, no_crlf_end);
	}

	/* Reset printer; abnormal exits made own arrangements */
	if (write (Descr, prreset, strlen (prreset)) < 0)
		BAILOUT;
	Mesg[0] = 0;
	goaway();
}
//E*O*F uniform.c//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     151    1085    7276 Install
      25      67     601 Makefile
      48     374    2403 README
      99     453    2972 globals.h
     104     625    3986 myprinter.h
     209     735    4910 adjust.c
      49     147    1180 counters.c
      93     311    1958 etc.c
      91     294    1793 getvar.c
     159     422    3154 highlight.c
     508    1859   12358 makeform.c
     131     535    3400 printform.c
      83     384    2541 uniform.c
    1750    7291   48532 total
!!!
wc  Install Makefile README globals.h myprinter.h adjust.c counters.c etc.c getvar.c highlight.c makeform.c printform.c uniform.c | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
-- 
        Istvan Mohos
        ...uunet!pyrdc!pyrnj!hhb!istvan
        RACAL-REDAC/HHB 1000 Wyckoff Ave. Mahwah NJ 07430 201-848-8000
======================================================================

istvan@hhb.UUCP (Istvan Mohos) (03/01/90)

.TH UNIFORM 1 ""
.UC 4
.SH NAME
.nh
uniform - universal form filer
.SH SYNOPSIS
.nf
.B uniform
.B uniform \ file
.B uniform \ '&file'
.fi
.SH OVERVIEW
\fBuniform\fR is a program for storing ``box contents'' and for
automatically reprinting such contents
into the appropriate ``boxes'' on pre-printed forms.
.LP
\fBuniform\fR performs two distinct
printer-related tasks.  The first task is reading the input typed on
the computer keyboard, and translating the input to unbuffered
printer output.
The printer serving the \fBuniform\fR process is assumed
not to be a resource
shared among multiple users (at least for the life of the process),
and is
assumed to be in the user's physical vicinity
for sustained visual feedback.
In essence, the printer is made to behave like a typewriter.
.LP
The second task is to record not only the character data that the user
directs to the printer/typewriter, but also the recording
of printer control events that caused
head seeks to the left margin, and of paper feed.
The two tasks combined allow filling boxes on standard
pre-printed forms or questionnaires
from the computer keyboard; and having saved in a file
both text input and the topology of the boxes, allow
future reprinting of the form with a minimum of effort.
.LP
Three distinct variants of the command line syntax accomplish
different goals.  The command given without a file name instructs
\fBuniform\fR that a new form is to be \fIlearned\fR.  A name
of a file in which to store the extracted data,
is interactively requested.  Following this, the user is shown how
to align the blank form in the printer; the user then ``fills out''
the form from the computer keyboard.  The computer screen provides
important visual feedback, prompting when necessary; making the
mechanics of the operation intuitively obvious.
.LP
Having \fIlearned\fR a form in this manner, a
new \fBuniform\fR command of the second form can name
the saved \fIfile\fR on the command line.
This command instructs \fBuniform\fR to fill out a blank form.
The user is shown how to align the blank form as before.
Following this the \fIfile\fR is read, and printer control
codes saved in \fIfile\fR are
expanded to the actual sequences used by the slave
printer.  The user is prompted for supplying new values of
previously named variables.  The form is printed.
.LP
The third variant of the command line
is for continuing an originally interrupted
learning session.  The user aligns the blank form
as before.  Incomplete data already saved in \fIfile\fR is
reprinted into the form.  A new \fIlearn\fR
process is started at this point, aiming to complete the yet
unfilled parts.  Data recorded during the
session is seamlessly appended to \fIfile\fR.

.SH CONTROL CHARACTER SET
Data input to \fBuniform\fR from the keyboard falls into two main
categories: text strings and control bytes.  Text strings are
arbitrary aggregates of all ``printing characters'' (characters
that mark the paper when printed), and the SPACE character
(input by pressing the space bar).  Pressing the TAB key
contributes eight consecutive spaces to the text.  The completion
of a text string is signaled by pressing the RETURN key; the
value of RETURN does not become part of the text string.
.LP
Control bytes to \fBuniform\fR
are ASCII values in the range of 1 through 31.
Control bytes are input to the program by simultaneously pressing
the CONTROL key and an alphabetic character.
Some of the control values
are ``hard wired'' or automatically mapped to keys with familiar
names: CONTROL-H is equivalent to BACKSPACE,
CONTROL-I is equivalent to the TAB key, RETURN is mapped either to
CONTROL-J or CONTROL-M.  ASCII 27 is the ``escape key'' ESC.
Additionally, the SPACE (ASCII 32) character can assume a second role
and become a control byte depending on the operating context.
.LP
\fBuniform\fR uses the BACKSPACE key to negate the rightward
advance (measured in units of ``character pitch'')
of the printer head, or to
move back in a text string to erase errors.  The
DELETE (or DEL) key is remapped to work exactly as BACKSPACE.
Each ``hit'' of the keyboard TAB key or CONTROL-I is identically
converted to eight SPACE characters, and the program immediately
``forgets'' that the eight spaces on record originated as
a single TAB control byte.
.LP
The ESC key propels the program along the chief operating states:
pressing ESC within the form-aligning mode exits that mode and
begins either the form-learning or the form-printing process.
Pressing ESC within the form-learning process signals the completion
of user input, and terminates the program.
.LP
Other control inputs are mapped as easily remembered mnemonics.
Some of these control characters are restricted in scope,
operational only during certain phases of the \fBuniform\fR process.
.LP
.nf
\fB ^A: \fR	ABORT the process, reset printer and terminal.
\fB ^B: \fR	Command printer to print text in BOLD font.
\fB ^C: \fR	Command printer to use COMPRESSED font or character pitch.
\fB ^E: \fR	Command printer to use ELITE font or character pitch.
\fB ^F: \fR	Issue 1/6 inch line FEED (during variable restore only).
\fB ^K: \fR	Command printer to deactivate bold font: KILLBOLD.
\fB ^L: \fR	Issue 1/48 inch LITTL'LINE feed.
\fB ^P: \fR	Command printer to use PICA font or character pitch.
\fB ^R: \fR	REDRAW terminal screen distorted by another process.
\fB ^U: \fR	UNDO entire current line (when creating new form only).
\fB ^V: \fR	Define next text string as VARIABLE.
.fi

.SH "DETAILED DESCRIPTION OF `ALIGN' AND `LEARN'"
At the beginning,
the screen is overlaid with instructions for paper
alignment, and the process is poised for keyboard input.
The first line of the CRT shows the name of the printer
the program expects to control.  It should be a cause for
concern if the printer readied next to the computer is
of a different type from the one assumed by the program.
.LP
The next three lines of the screen contain printer-specific
initialization steps: how to turn on the printer, how to
set up manual margins, how to bring the printer ``on-line''.
The more intelligent the printer, the less text appears in these
lines; in extreme cases the three lines may be blank.
.LP
The next fourteen lines contain a description of the alignment
process, and depict with character-graphics a
conveniently remembered spot on the blank form that should be
aimed for and hit by a character typed at the computer.
The alignment procedure consists of repeated attempts
to print a ``right square bracket'' character \fB\]\fR
at this conveniently remembered spot
just outside the main borders of the form.
(By this time the user should thread a blank
form under the printer head and get it ready for printing.)
.LP
In between tries, the vertical position of the
form should be adjusted manually, or advanced in 1/48 inch
increments by typing CONTROL-L characters from the keyboard.
The horizontal position of the
printer head can be advanced by typing spaces on the keyboard.
The program down-loads the bracket to the printer every time a
RETURN is typed on the keyboard, then immediately returns the
printer head to the leftmost print position, ready for a retry.
The width of the final
``spaces . . . bracket'' string
is the width of an automatic margin for the duration of the
rest of the session.
.LP
Because the designer's and the user's idea of a
``conveniently remembered spot'' may differ, when \fIlearning\fR a
form the user may substitute these fourteen screen lines with
arbitrary text that will hopefully pinpoint a better target for
future recall.  The insertion of the new text is akin to a comment
facility, and begins when a ``black'' or ``printing'' character
is typed during the alignment procedure, instead of the
SPACE/RETURN/CONTROL-L aggregates that direct printer head and paper
movement.  The new text is echoed in the next available one of the
fourteen lines not yet containing user comment, and ends at the
right margin of the screen or by a RETURN from the keyboard.  If the
next key then is a SPACE, RETURN, or CONTROL-L, the adjustment procedure
continues where it was left off.  If the next key is a printing
character instead, a new line of comment is accrued.
The comment text is saved at the beginning of the \fIfile\fR
record of the session, and will automatically appear on the screen
(instead of the original fourteen lines) when adjusting the form
for a reprint.  If the adjustment session is in preparation to
a \fIreprint\fR, the
user's original comments (or lack of) displayed on the screen
(as saved in preparation to a \fIlearn\fR session) can no longer be
changed.
.LP
The alignment procedure ends when the user judges the
most recently printed bracket sufficiently near the
target point.
The final RETURN (printing the bracket)
is then followed by pressing the ESC key, and the process is
propelled into either the \fIlearn\fR mode or the
\fIreprint\fR mode.
It should be noted that the ESC key can terminate alignment at
any point along the printer control sequence: if the last key before
ESC was a SPACE the final bracket is not printed and the width of
the margin is the number of spaces
typed since the previous output of the bracket.
If the document is not too sensitive to layout (as when
addressing an envelope), the alignment procedure can simply be
skipped by hitting the ESC key as the first control action.
.LP
The program puts the terminal in the \fIraw\fR mode, and in this mode
terminal interrupts are disabled.  Typing CONTROL-A during the
alignment process (except while writing a line of comment)
aborts the program immediately, restores the printer to its
normal operating mode, and resets the computer terminal.
The process may not be able to die however, if output was directed
to an inoperative printer.  While waiting to complete the I/O
task, the process can hang, and may need to be \fIkilled\fR from
another terminal.  Following this, the \fIreset\fR command should be
issued on the original terminal, to restore it to \fIcooked\fR mode.
.LP
Passing the adjustment procedure, a new form is
\fIlearned\fR and filled out under
keyboard control.  The basic unit of terminal input is a line,
with maximum width a function of the horizontal travel capacity of
the print head, not to exceed 248 characters.
Keyboard activity is immediately echoed to the screen.
Items or \fItokens\fR on a line are one of four types:
\fIspaces, controls, constants,\fR and
\fIvariables\fR.  Constants and variables are text strings.
Spaces and controls are input as single bytes
(each TAB is automatically converted to eight spaces).
Spaces (and expanded TABs)
are echoed to the screen as ``dot'' or ``bar'' markers on an imagined
ruler:
.LP
 . . . . | . . . . | . . . . | . . .
.LP
At the same time, an equal number of
true space characters (ASCII 32) are output to
the printer to force carriage movement and to
provide a visual
indication of the current horizontal position of the print head.
.LP
Control bytes that affect the printer
are converted ``on-the-fly'' to printer
escape sequences predefined for the printer in use.
Escape sequences
are immediately down-loaded to the printer, and effect switching
between character-sets or enable/disable other printer capabilities.
Printer control bytes
are not echoed to the line accrued on the screen.  That the program
has recognized these is made evident by highlighting the
appropriate control byte on a narrow ``control code menu'' at the
bottom of the screen.
.LP
CONTROL-H, DEL or BACKSPACE cause the print head to move to the previous
print column, while simultaneously erase the
``dot'' or ``bar'' to the left of the cursor on the screen
and move the cursor to the erased column.
Backing up in this manner
is possible all the way to the last byte of a
previous constant or variable, or to the beginning of an otherwise
empty line.  Note that while
backing up does not ``erase'' or deactivate
control commands already sent to the printer, control bytes
in the session record are automatically skipped backwards and
are in effect removed from the record.
.LP
The first ``printing'' character typed at the beginning of
the line or after
any sequence of \fIspaces, controls, constants\fR or
\fIvariables\fR, begins a constant and switches the input parser to
a ``collect text string'' mode.
Individual bytes of the constant are echoed to the screen
as they are typed, but the
constant is not output to the printer until a RETURN from the keyboard
signals that the constant is complete.  This allows the correction of
typos with the
BACKSPACE, DEL or CONTROL-H keys in the conventional manner.
The character-set of a constant
is the set of all ``printing'' characters and the SPACE character
(again, TABs automatically convert to eight spaces).
The RETURN at the end of the constant
signals only the end of the constant, not of
the line: the string may be followed by more \fIspaces,
controls, constants,\fR
or \fIvariables\fR.  The completed string is equivalent to an unchanging
horizontal field of data to be reprinted in an identical
position on future copies of the form.
.LP
Variable syntax is identical to that of constants.  But while constants
represent unchanging data, the text of a variable is expected to
be different on future copies of the form.
Variable definition begins when a CONTROL-V is typed at the
beginning of a line or following
any sequence of \fIspaces, controls, constants\fR or
\fIvariables\fR.
The user is immediately
prompted for a name for the variable, so that
the field can be referenced in the future.
Length of variable names is limited to 45 characters.
The character-set of the
name is once more all ``printing'' characters and SPACE, and the name
definition ends when the user types RETURN.
The name query is in a different area of the screen than the line
in construction;
neither the CONTROL-V or the name query disturbs the screen image of
the collected line.
After naming the variable, the cursor returns to its
former point in the line under construction, and input continues with
the current text of the variable.
Once again, text is echoed to the screen,
terminated with a RETURN, and printed identically to a
string constant.  A message advises the user to pad the
current value with spaces or TABs to the maximum
width of the target box: the print width
of future values is not allowed to exceed the present width.
.LP
Concurrently with the completion of each token,
the \fIspace, control byte, constant\fR or
\fIvariable\fR is entered into the session record.
Spaces and most control bytes (except BACKSPACE, process and screen
control bytes, CONTROL-L and RETURN)
are written as themselves; in the record
a printer control byte is not expanded to the printer's escape
sequence.
Constants are written to the record as ordinary text
(but without the \fInewline\fR that signified the end of the constant).
Variables are written
beginning with a CONTROL-V, followed by the user-given variable name,
a second CONTROL-V, the current string
of the variable possibly padded with spaces, and a trailing CONTROL-V.
.LP
The interactively composed line is terminated when a CONTROL-L or
RETURN is typed from outside the constant/variable collection
process, or when a CONTROL-U is typed anywhere on the line.
CONTROL-U is a mnemonic for \fIundo\fR; it returns
the process to the point where the assembly of the
line began.  The printer head is returned to the left margin without
line feed.  Characters already printed on the current line of
the form remain, but will be absent from the data
saved in the session record.
.LP
If the line is terminated with a CONTROL-L, a single
\fI``carriage return + line feed''\fR (CRLF)
sequence is sent to the printer.  If the line is terminated with
RETURN, eight consecutive CRLF sequences are sent to the printer.
.LP
Most dot matrix printers and electronic typewriters are capable of
computer controlled paper feed in increments of 1/48 inch.  It is
assumed that the control sequence for feeding paper in 1/48 inch
increments has been predefined for the printer in use
at the installation of
\fBuniform\fR, so that a CONTROL-L will cause only a very slight
advance of the form on the printer platen.  As RETURN 
gets expanded to eight successive line feeds, it produces a
paper advance of 1/6 inch, the width of a standard ``single spaced''
line.  During the \fIlearning\fR of the form then, RETURN will
cause a normal 1/6 inch
line feed; one or more CONTROL-L can be
typed for precise alignment of the text between some upper and
lower box boundaries.  If the printer can not feed paper at this
granularity, fitting text between vertical boundaries can be
considerably more difficult (however the algorithm and the
CONTROL-L/RETURN relation stay the same).
.LP
The line-terminating
CONTROL-L is saved in the session record as the normal \fInewline\fR
or \fIend-of-line\fR
character, and as such, will not be visible when using standard editors
to view the session record.
Eight \fInewlines\fR are recorded for a line-terminating RETURN.
Subsequent reprint sessions under \fBuniform\fR will reset the printer
to feed paper in 1/48 inch increments, and re-translate eight
\fInewlines\fR of the record to single spaced, 1/6 inch line feeds.
.LP
Thus, the saved data file is a perfectly ordinary
if somewhat sparse, ASCII text file with a few embedded
``control characters'' to bracket variables or to serve as printer
control flags.  CONTROL-A characters in the file begin comments;
the comment ends with the
\fInewline\fR terminator at the end of the line.
The file can be edited with conventional editors, in order to review
the saved data, to alter
recorded text, or to adjust layout.

.SH HINTS FROM A PRO
.in +.3i
.ti -.3i
\(bu \ \ \ Most printers use ``pin feed'' paper,
whereas most blank forms
do not have pin feed stripes.  If the printer can not
be set to ``friction feed'', or if the friction feed mechanism
is unreliable,
tape the blank form on top of a sheet of pin feed paper with matte
cellophane tape.  Lightweight tape should be used,
covering the entire left edge of the form.  This is
to prevent the print head from colliding with a protruding
paper edge, and tearing the form or damaging itself.  Do not tape up
the pin feed holes.  Afterwards, peel off the tape or cut
along the edges of the form.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ If the top edge of the form does not reach
the paper guide when the
printer head is lined up with the topmost box on the form, stick two
pieces of paper tape on the top of the form and fold down each one over
itself to form a ``tab'' or ``ear'' that will reach under
the rollers on the paper guide.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ Some machines will insist on moving the printer head to the left or
right boundary of an invisible ``print window'' after each
print activity.  This makes it impossible to visually
infer the horizontal location where the next byte would go.
A strip of plastic, a toothpick, or a piece of wire can usually be
affixed to the moving case of the print head (for example, taped to
the moving ribbon cartridge) in a way to point to the next print
position even though the head already returned to its left or right
window boundary.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ A couple of ``dry runs'' with just normal computer paper in
the printer provide good practice, and will instill the confidence
necessary for producing and printing arbitrary forms when
squeezed for time.
Mark a spot somewhere on the paper with a pen
to serve as corner of the top left box on a form.
Call \fBuniform\fR without a file name, and
follow the instructions on the screen for homing the printer head
to the spot you marked.  ``Fill out'' the imaginary form with
various information, don't hesitate to define some variables.
Then call \fBuniform\fR again, this time with the name of the record
of the previous session; rewind the paper back to the previous spot,
and attempt to print the copy directly over the original.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ \fILearning\fR a form represents a fair investment of effort.  If
you are filling out your only copy of the form as you \fIlearn\fR it,
you will have to be extra careful with the layout, because the
\fBundo\fR command will return the printer head to the beginning of
the line, but will not erase mistakes already printed in that line.
If you have access to a photocopy machine, copy the blank form, and
\fIlearn\fR it using the copy.  Similarly, you can use a copy to
verify that the file recorded during the learning session is correct,
before printing the data on the original form.
Minor mis-alignment problems in the recorded file are easy to fix
by editing the file and adding/deleting blank lines to change
vertical layout, or adding/deleting spaces to change the horizontal
positions of constants or variables.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ There is no penalty for recording text broken into
several small tokens instead of as one long token.  For example, a
Social Security number
can be split into three separate tokens to achieve a more precise
horizontal alignment on either sides of the pre-printed \fIdashes\fR.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ If you mistakenly began a string and
then erase all characters of the string
(by typing \fIbackspaces\fR), the process will still
remain in
the ``collect string'' mode until it receives the terminating
RETURN from the keyboard.  If the constant is empty at this point, it
is not recorded in the file of the session; input continues
normally, and BACKSPACE characters are not blocked by the erased
constant.  If the null string was the value specified for a variable,
the empty variable is output to the saved file, and BACKSPACE
characters will be blocked at the invisible variable boundary.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ Even if you remember the exact spot used to ``home'' the
printer head when creating the original, the horizontal
alignment of the copy will be off on occasion
by the better part of a character
width.  This usually is of no consequence, unless the printed data
is a single \fBX\fR checkmark typed into a character-sized
square.  To combat the problem, make it a practice to use
\fBXX\fR for a checkmark instead of \fBX\fR.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ Be careful with ``undo''-ing
lines in which you have transmitted an
escape sequence to the printer, or with \fIbackspacing\fR over printer
control codes.  The printer will still be
affected by the escape sequence (and may continue filling out the
original form in a compressed character-set for example);
but the control command will not get into the record, and
future copies may be misaligned (printed using the wider,
original character-set in the example).
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ The task of the variable mechanism of
\fBuniform\fR is to print changing data into unchanging
coordinates on the form.  On rare occasions the vertical layout
of the form may not be constant.  As an example, a line-oriented
medical questionnaire may begin with an informative list of visits
and corresponding dates, charges, etc.
to the medical facility; and depending on the number of visits,
the space for the user-supplied answers to questions
would get shifted lower and lower on the form.
An obscure feature in the \fInew variable value\fR collector
allows printing such
vertically variable forms.  When collecting a new variable value,
the recognized character-set is expanded by the CONTROL-L and
CONTROL-F characters, such that a CONTROL-L causes an immediate
1/48 inch line feed on the printer, and CONTROL-F causes an
immediate 1/6 inch line feed (RETURN could not be used for this here,
since it would signal the end of the variable).
A minimal-height form should be
chosen for creating the prototype, with a ``dummy'' variable
installed within the vertical sequence of line feeds at the
point where the form can be expected to ``stretch''.
At reprint time the printing process will be interrupted so
that the program can receive a new value for the variable.
Instead of responding with a string of text, the user can
issue paper feed commands, until the extra line feeds catch up
with the vertical expansion of the current version of the form.
.LP
.in +.3i
.ti -.3i
\(bu \ \ \ \fBuniform\fR may also see use in producing
``form letters'', electronic check writing,
computerized ``typewriter art'',
and in other printing endeavors requiring
fine control over and repeatability of print topology.
Less obvious applications may substitute
a different type of slave for the printer: \fBuniform\fR
could precisely and interactively tune a servo-mechanism 
in programming a robot, ``canning'' the instruction sequence
at the same time for automatic replay.

-- 
        Istvan Mohos
        ...uunet!pyrdc!pyrnj!hhb!istvan
        RACAL-REDAC/HHB 1000 Wyckoff Ave. Mahwah NJ 07430 201-848-8000
======================================================================