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 ======================================================================