Modified TTYPE source
Paul Hubbard
paulh at copper.UUCP
Sat Dec 14 02:33:34 AEST 1985
---------
Several people have requested my modified ttype source, so here it is.
--------8<-----------8<------------8<-------------8<------------8<--------
/*
* ttype By Chris Bertin, 1983
* Modified by Paul Hubbard, 1985
*
* Description: TTYPE allows you to practice terminal or typewriter
* typing. TTYPE is self-explanatory if you request
* instructions.
*
* Compilation: cc -o ttype ttype.c -lcurses -ltermlib
*
* Invocation: ttype
*/
static char cpright[] = "(C) Chris Bertin, 1983";
static char cpleft[] = "Paul Hubbard, 1985";
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/timeb.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <curses.h>
#define WORDLIST "/usr/dict/words"
#define LFCR "\n\r"
#define CHAR 0 /* answer types */
#define NUM 1 /* ... */
#define PRACTICE 0 /* run types */
#define DICT 1 /* ... */
#define FILEIN 2 /* ... */
#define MAX 128 /* std array size as well as max ASCII */
#define SPEED 0 /* screen locations */
#define SCORE 2 /* ... */
#define TYPE LINES -12 /* ... */
#define ASK LINES - 3 /* ... */
#define BOTTOM LINES - 2 /* ... */
#define ALLCHARS "&*()_+|\\{}:\"~<>?-=[];'`,./!#$%^ ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 abcdefghijklmnopqrstuvwxyz"
#define moveto(x,y,str) if(CA)move(x,y),clrtoeol();else pr(0,str);
#define dorefresh() if(CA){clearok(stdscr,TRUE);refresh();clearok(stdscr,FALSE);}
#define sethelp() wclear(helpscr);wrefresh(helpscr);moveto(TYPE,0,LFCR);
char what_chars[MAX*2]; /* the characters to practice on */
char try_str[MAX*2]; /* the string to be retyped */
char typed_str[MAX*2]; /* the chars being typed by the user */
char work_str[MAX*2]; /* work space (temp string) */
char badchar[MAX]; /* errors; the mistyped chars are pointers into them */
char gbadchar[MAX]; /* same but session total */
int xcoor,ycoor; /* temp coordinates */
int isafastscreen; /* is a fast screen? */
int trials; /* number of trials */
int npractice; /* number of characters to practice on (practice opt.)*/
char lasterr[500]; /* last error, the next string will start with it */
int err_num; /* last error pointer */
int linerrors, toterrors, gtoterrors; /* errors per line / run / session */
float totlngth, gtotlngth; /* chars typed per run / session */
float tottime, gtottime; /* time spent per run / session */
struct timeb tb, ta;
struct stat sbuf;
FILE *fp, *dictfp, *fopen();
WINDOW *helpscr, *subwin();
main()
{
register option, screenloc, lngth, more = 1;
int out_size, nwords, instruct;
char fname[MAX];
float chkstr();
setbuf(stdin, (char *)NULL);
ftime(&ta);
srandom(getpid() + ta.millitm - getuid()); /* should be unique... */
initscr();
scrollok(stdscr, FALSE);
(void) signal(SIGFPE, SIG_IGN);
if(CA) /* Do we have cursor control?? */
isafastscreen = highspeed();
else
pr(1, "[No cursor addressing, using hard copy mode]\n");
raw(), noecho();
moveto(TYPE - 6, 0, "");
pr(2, "\t\t\tP R A C T I C E T Y P I N G");
pr(2, "\t\t\t~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
if(instruct = answer(work_str, "Instructions? (y or n)", CHAR) == 1)
helpscr = subwin(stdscr, 9, COLS, TYPE, 0);
gtoterrors = gtotlngth = gtottime = 0.;
zero(gbadchar, sizeof gbadchar);
while(more) {
screenloc = TYPE;
if(instruct)
help1();
(void) answer(work_str, "What text type do you want?", CHAR);
if(! strcmp(work_str, "words")) {
option = DICT;
if(dictfp == NULL) /* don't redo it */
if(((stat(WORDLIST, &sbuf)) < 0) ||
((dictfp = fopen(WORDLIST,"r")) == NULL)){
note("Can't open %s", WORDLIST);
continue;
}
if(instruct)
help3();
nwords = answer(work_str,
"How many words in each line?", NUM);
if(nwords > COLS/11)
note("Set to maximum allowed (%d)",
(nwords = COLS/11));
}
else if(! strcmp(work_str, "file")) {
option = FILEIN;
if(instruct)
help2();
(void) answer(fname, "Enter file name:", CHAR);
if(! *fname)
continue;
if((fp = fopen(fname, "r")) == NULL) {
note("Can't open %s", fname);
continue;
}
}
else {
option = PRACTICE;
strcpy(what_chars, work_str[0] ? work_str : ALLCHARS);
npractice = strlen(what_chars);
if(instruct)
help4();
out_size = answer(work_str,
"How many characters in each string?", NUM);
}
if(option != FILEIN) {
if(instruct)
help5();
trials = answer(work_str, "How many trials?", NUM);
}
if(instruct)
help7();
zero(badchar, sizeof badchar);
err_num = toterrors = 0;
totlngth = tottime = 0.;
if(CA)
clear();
moveto(SCORE, 0, LFCR);
pr(2, "\tNext line: ^C, Quit: ^D, Redraw: ^L");
while(makestr(option, out_size, nwords) >= 0) {
ioctl(0, TIOCFLUSH, 0);
if(CA)
note("", "");
if((lngth = chkstr(screenloc)) == 0)
continue;
totlngth += lngth;
toterrors += linerrors;
if(isafastscreen)
score(badchar, toterrors, totlngth, tottime);
if((screenloc += 3) > (ASK - 2))
screenloc = TYPE;
}
gtotlngth += totlngth;
gtoterrors += toterrors;
if( ! CA || ! isafastscreen)
score(badchar, toterrors, totlngth, tottime);
more = answer(work_str, "Try again?", CHAR);
}
leave();
}
answer(str, msg, type)
register char *str;
char *msg;
{
register c, i, ret = 0;
for(;;) {
moveto(ASK, 10, "\n\r> ");
pr(1, "%s: ", msg);
for(i=0; ((c = getch()) != '\n' && c != '\r') ;)
switch(c) {
case '\f':
dorefresh();
break;
case '\10':
if(i>0)
--i, pr(1, "\010 \010");
break;
case '\04':
case '\03':
case 0177:
leave();
default:
pr(1, "%c", (str[i++] = noctrl(c)));
}
if(CA)
note("", "");
while(str[--i] == ' ') ;
str[++i] = 0;
if(type == CHAR)
if(*str == 'y')
return(1);
else
return(0);
if((ret = atoi(str)) <= 0) {
note("Numeric value (> 0) required", "");
continue;
}
return(ret);
}
}
makestr(opt, lng, n_of_wrds) /* builds the string to be typed */
{
register ind;
if(opt != FILEIN && trials <= 0)
return(-1);
zero(try_str, sizeof try_str);
switch(opt) {
case FILEIN:
if(fgets(try_str, sizeof try_str, fp) == NULL)
return(fclose(fp));
fixstr(try_str);
return(1);
case DICT:
for(ind = 0; ind < n_of_wrds ; ++ind, strcat(try_str, " ")) {
if(fseek(dictfp, random()%(long)sbuf.st_size, 0) == -1)
strcpy(work_str, "fseek failed!!!");
fgets(work_str, sizeof work_str, dictfp);
if(fgets(work_str, sizeof work_str, dictfp) == NULL)
strcpy(work_str, "End of file!!!");
work_str[strlen(work_str) - 1] = 0;
strcat(try_str, work_str);
}
return(--trials);
case PRACTICE:
for(ind = 0 ; ind < lng ; ++ind)
try_str[ind] = what_chars[(int)random() % npractice];
try_str[0] = (lasterr[err_num] ? lasterr[err_num] : try_str[0]);
err_num = 0;
return(--trials);
}
/* NOTREACHED */
}
fixstr(str)
char *str;
{
register char *in, *out;
short space = 0;
for(in = str ; isspace(*in) ; ++in) ;
for(out = str ; *in ; ++in) {
if(*in == '\010') {
if(out > str)
out--;
continue;
}
if(isspace(*in) && space)
continue;
space = isspace(*in);
*out++ = noctrl(*in);
}
*out = 0;
}
float
chkstr(location) /* checks the typing */
{
register ind, c = 0;
register char *typed_pnt;
register float n_of_chars;
float f, elapsed, starttime, gettime();
zero(typed_str, sizeof typed_str);
typed_pnt = typed_str;
if(strlen(try_str) >= COLS-1) {
note("Line too long, truncated", "");
try_str[COLS-1] = 0;
}
if((n_of_chars = (float) putline(try_str, location)) == 0.)
return(0.);
moveto(location+1, 0, LFCR);
refresh();
for(linerrors = ind = 0; ind < n_of_chars ; ++ind) {
switch(c = getch()) {
case '\n':
case '\r':
--ind;
break;
case 0177:
case '\b':
getyx(stdscr,ycoor,xcoor);
move(ycoor,xcoor-1);
delch();
if (lasterr[err_num-1] == try_str[ind-1])
{
--linerrors;
--badchar[lasterr[err_num-1]];
--gbadchar[lasterr[err_num-1]];
--err_num;
}
clrtoeol();
refresh();
ind -= 2;
break;
case '\f':
dorefresh();
--ind;
break;
case '\03':
if((n_of_chars = ind) == 0) /* force out */
return(0.);
break;
case '\04':
gtotlngth += totlngth + ind;
gtoterrors += toterrors + linerrors;
leave();
default: /* a character... */
if(ind == 0)
starttime = gettime(0, "");
if(c != try_str[ind]) { /* error */
pr(0, "%c", ch_case(c, try_str[ind]));
warnerror(location+1, ind+1);
lasterr[err_num] = try_str[ind];
++err_num;
++linerrors;
++badchar[lasterr[err_num-1]];
++gbadchar[lasterr[err_num-1]];
}
else {
pr(0, "%c", noctrl(c));
clrtoeol();
}
refresh();
break;
}
}
*typed_pnt = 0;
standend();
clrtoeol();
elapsed = gettime(12, "/ ") - starttime;
tottime += elapsed;
gtottime += elapsed;
moveto(SPEED, COLS-40, LFCR);
standout();
if(! CA)
printf("\t\t\t\t");
pr(2, "%.1f secs, %d error(s), %.2f w/min", elapsed, linerrors,
(f=((n_of_chars / 5.) - linerrors) / (elapsed / 60.))>0.?f:0.0);
standend();
refresh();
return(n_of_chars);
}
float
gettime(where, str)
register char *str;
{
struct tm *localtime();
register struct tm *tm;
register x, y;
ftime(&tb);
if(isafastscreen) {
tm = localtime(&tb.time);
getyx(stdscr, y, x);
move(SPEED, where);
pr(1, "%s%02d:%02d:%02d.%02d", str, tm->tm_hour, tm->tm_min,
tm->tm_sec, (tb.millitm / 10));
clrtoeol();
move(y, x);
refresh();
}
return((float) (tb.time - ta.time) + ((float)tb.millitm / 1000.));
}
putline(outstr, where)
register char *outstr;
{
register n;
char *rindex();
register char *back = rindex(outstr, '\0') - 1;
while(isspace(*back))
*back-- = 0;
moveto(where, 0, "\r");
for(n = 0 ; *outstr ; ++n)
pr(0, "%c", noctrl(*outstr++));
if(n)
refresh();
return(n);
}
warnerror(x, y)
{
if(! isafastscreen || y > COLS-15) {
putchar('\07');
return;
}
clrtoeol();
move(x, y+3);
standout();
pr(0, " Error");
standend();
move(x, y);
refresh();
}
/* NOSTRICT */
/* VARARGS */
note(str1, str2)
char *str1;
long str2;
{
moveto(BOTTOM+1, COLS-40, LFCR);
clrtoeol();
standout();
pr(2, str1, str2);
standend();
}
ch_case(bad, good) /* converts to upper or lower case */
{
register ret;
if(isupper(bad))
return((good - (ret=tolower(bad))) ? ret : bad);
if(islower(bad))
return((good - (ret=toupper(bad))) ? ret : bad);
return(noctrl(bad));
}
noctrl(ch)
register ch;
{
if(ch == '\t')
return(' ');
return(iscntrl(ch) ? (ch - '\01' + 'A') : ch);
}
score(errstr, err, lng, ttime) /* prints the score */
char *errstr;
register float lng, ttime;
{
register s, i, n=0;
float f;
if(lng == 0.)
return;
moveto(SCORE, 0, "\n\n\r");
s = (lng - (float) err) * 100. / lng;
pr(0,"%d errors on %.0f characters (%d %% error), ", err, lng, (100-s));
pr(1, "average speed: %.2f words/minute",
((f = ((lng / 5.) - err) / (ttime / 60.)) >0. ? f : 0.), err);
if(err) {
moveto(SCORE + 2, 0, LFCR);
for(n = i = 0 ; i < MAX ; ++i)
if(errstr[i] != 0) {
pr(0, "%4d errors on '%c'", errstr[i], i);
if(++n > 3) {
n = 0;
pr(1, LFCR);
}
}
pr(1, LFCR);
}
return;
}
/* NOSTRICT */
/* VARARGS */
pr(mode, fmt, str1, str2, str3, str4, str5) /* mode 2: add LFCR, 1: refresh() */
char *fmt;
long str1, str2, str3, str4, str5;
{
if( CA) {
printw(fmt, str1, str2, str3, str4, str5);
if(mode == 2)
printw(LFCR);
if(mode > 0)
refresh();
}
else {
printf(fmt, str1, str2, str3, str4, str5);
if(mode == 2)
printf(LFCR);
fflush(stdout);
}
}
highspeed() /*return 1 if 1200 baud or more */
{
struct sgttyb mod;
gtty(0, &mod);
if(mod.sg_ispeed >= B1200)
return(1);
move(ASK, 10);
pr(1, "[Using slow terminal mode]");
return(0);
}
help1()
{
sethelp();
pr(2,"\tIf you hit <RETURN>, the system will give you a random string");
pr(2,"\tof characters to practice on. If you want to limit the range");
pr(2,"\tof characters, enter a character list. To practice on random");
pr(2,"\twords from the dictionary type 'words'. To practice on the");
pr(2,"\ttext in a file, type 'file'.");
}
help2()
{
sethelp();
pr(2, "\tThe system will print one by one all the lines from the file");
pr(2, "\tand wait for you to retype them. If the file is not in the");
pr(2, "\tcurrent directory, enter the full path name (Ex:/home/test).");
pr(2, "\tNote: trailing spaces and tabs are truncated, tabs in the");
pr(2, "\tlines are replaced with spaces and empty lines are skipped.");
}
help3()
{
sethelp();
pr(2, "\tThe program will pick random words from the dictionary.");
pr(2, "\tEnter the number of words you want the system to give you");
pr(2, "\ton each line. (More words produce a more accurate speed");
pr(2, "\trating.)");
}
help4()
{
sethelp();
pr(2, "\tThe program will make strings of characters for you to");
pr(2, "\tenter. Enter the length of string. (More letters produce");
pr(2, "\ta more accurate speed rating.)");
}
help5()
{
sethelp();
pr(2, "\tHow many lines do you want the system to give you in this");
pr(2, "\tsession?");
}
help6()
{
sethelp();
pr(2,"\t Do you want to see the characters you are typing as you type");
pr(2,"\tthem? If you type 'no', only the errors will be printed.");
}
help7()
{
sethelp();
pr(2, "\tMiscellaneous Notes: <RETURN> and <LINE-FEED> are ignored.");
pr(2, "\tEach error subtracts one word from the speed. You can use ");
pr(2, "\tbackspaces to recover from errors.");
if(CA)
(void) answer(work_str, "Hit <RETURN> to continue", CHAR);
}
leave()
{
if(gtotlngth) {
moveto(SPEED, 0, LFCR);
pr(2, "\t\t\tS E S S I O N T O T A L");
score(gbadchar, gtoterrors, gtotlngth, gtottime);
}
clrtobot();
moveto(BOTTOM, 0, LFCR);
refresh();
endwin();
exit(0);
}
zero(str, n)
register char *str;
register n;
{
while(n--)
*str++ = 0;
}
More information about the Comp.sources.unix
mailing list