summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Darwin <ian@cvs.openbsd.org>1998-09-28 15:45:48 +0000
committerIan Darwin <ian@cvs.openbsd.org>1998-09-28 15:45:48 +0000
commit0436a5b6c19a613da918536b89a49cbcb38a2e4e (patch)
treea67948a0b07dbb07c82a2f45e028b99f0e42356a
parent85f240539c462832ad5363a277a61928b0d90a2a (diff)
import BTL learn(1)
-rw-r--r--usr.bin/learn/README13
-rw-r--r--usr.bin/learn/lib/Linfo6
-rw-r--r--usr.bin/learn/lib/Xinfo19
-rw-r--r--usr.bin/learn/src/Makefile37
-rw-r--r--usr.bin/learn/src/README33
-rw-r--r--usr.bin/learn/src/lcount.c12
-rw-r--r--usr.bin/learn/src/learn.c989
-rw-r--r--usr.bin/learn/src/learn.h49
-rw-r--r--usr.bin/learn/src/tee.c41
9 files changed, 1199 insertions, 0 deletions
diff --git a/usr.bin/learn/README b/usr.bin/learn/README
new file mode 100644
index 00000000000..c5ff5282d02
--- /dev/null
+++ b/usr.bin/learn/README
@@ -0,0 +1,13 @@
+Learn was originally written by Mike Lesk with some contributions
+from Brian Kernighan. The paper in this directory dates from the
+second version of learn, around 1979.
+
+This version of learn has been resurrected from the original V7
+source and edited enough that the source compiles and the
+lessons can be executed. We have made no attempt to update the
+lessons, nor to fix some of the glaring problems with the code.
+Think of it as a historical document, not a polished system.
+
+cd src
+make
+learn
diff --git a/usr.bin/learn/lib/Linfo b/usr.bin/learn/lib/Linfo
new file mode 100644
index 00000000000..b27813c92b4
--- /dev/null
+++ b/usr.bin/learn/lib/Linfo
@@ -0,0 +1,6 @@
+ files
+ editor
+ morefiles
+ macros
+ eqn
+ C
diff --git a/usr.bin/learn/lib/Xinfo b/usr.bin/learn/lib/Xinfo
new file mode 100644
index 00000000000..8ff69f526ed
--- /dev/null
+++ b/usr.bin/learn/lib/Xinfo
@@ -0,0 +1,19 @@
+files - basic file handling commands
+editor - text editor; must know about files first.
+morefiles - more on file manipulations and other useful stuff
+macros - "-ms" macros for BTL memos & papers; must know editor
+eqn - typing mathematics; must know editor
+C - writing programs in the C language; must know editor
+
+This is probably the proper order, but after you
+have the "files" course and know the basics of "editor",
+try anything you like.
+
+You can always leave learn by typing "bye" (and a RETURN).
+You can stop it from typing by pushing interrupt
+(or break or rubout or delete, depending on your terminal).
+
+If it won't accept your answer, and you know____ you're
+right, answer "no" when it asks whether you
+want to try again, and it will go on to the next lesson.
+Please report troubles to your local guru.
diff --git a/usr.bin/learn/src/Makefile b/usr.bin/learn/src/Makefile
new file mode 100644
index 00000000000..f03ab63fc7c
--- /dev/null
+++ b/usr.bin/learn/src/Makefile
@@ -0,0 +1,37 @@
+LESSONS = files editor morefiles macros eqn C
+
+CFLAGS = -g
+LLIB = ../lib
+
+cp: all
+ cp learn tee lcount $(LLIB)
+ @echo "Do 'make lessons' if you need to extract the lesson archives"
+ @echo "Do 'make play; make log' to make playpen and log directories"
+
+all: learn tee lcount
+
+learn: learn.c learn.h
+ cc -o learn $(CFLAGS) '-DLLIB="$(LLIB)"' learn.c
+
+lcount tee:
+ cc $(CFLAGS) $@.c -o $@
+
+lessons: $(LESSONS)
+
+$(LESSONS):
+ -rm -rf $(LLIB)/$@
+ mkdir $(LLIB)/$@
+ (cd $(LLIB)/$@; ar x ../$@.a)
+
+play log:
+ -rm -rf $(LLIB)/$@; mkdir $(LLIB)/$@; chmod +w $(LLIB)/$@
+
+check:
+ -@test -r $(LLIB)/tee || echo 'tee not present; make tee'
+ -@test -r $(LLIB)/lcount || echo 'lcount not present; make lcount'
+ -@test -r $(LLIB)/play || echo 'play directory not present; make play'
+ -@test -r $(LLIB)/log || echo 'log directory not present; make log'
+ -@for i in $(LESSONS); do test -r $(LLIB)/$$i/L0 || echo $$i not unarchived, make $$i; done
+
+clean:
+ rm -rf lcount learn tee $(LLIB)/play $(LLIB)/log
diff --git a/usr.bin/learn/src/README b/usr.bin/learn/src/README
new file mode 100644
index 00000000000..acf5ccc7a2a
--- /dev/null
+++ b/usr.bin/learn/src/README
@@ -0,0 +1,33 @@
+This is a historical document describing code that once
+worked fine. It sort of limps along now, but there are
+some things that don't work right. Caveat emptor.
+
+
+Make a learn by
+ make
+
+When this seems right, extract the lessons by
+ make lessons
+
+This extracts the lesson archives for each course. You can also
+do this one step at a time with "make files", "make editor",
+etc., if you don't want all courses. The C script is not
+complete; use at your own risk.
+
+To set up the working directories, you must also say
+ make play; make log
+
+Finally, check that it's sensible with
+ make check
+If that doesn't print any messages, you're probably in good shape.
+
+Learn requires general write permission on the user playpen
+directory .../lib/play. Lesson directories may be protected.
+
+Learn collects a log file for each script in the .../lib/log
+directory, with a file for each script (files, editor, etc.)
+containing an entry for each lesson attempted. These files will
+grow without bound, unless they are periodically truncated. The
+log files should have general write permission. If you don't
+want logging (a sensible position to take), set the variable
+"logging" to zero in source/lrndef before making learn.
diff --git a/usr.bin/learn/src/lcount.c b/usr.bin/learn/src/lcount.c
new file mode 100644
index 00000000000..110d827bc9c
--- /dev/null
+++ b/usr.bin/learn/src/lcount.c
@@ -0,0 +1,12 @@
+#include "stdio.h"
+
+main() /* count lines in something */
+{
+ register n, c;
+
+ n = 0;
+ while ((c = getchar()) != EOF)
+ if (c == '\n')
+ n++;
+ printf("%d\n", n);
+}
diff --git a/usr.bin/learn/src/learn.c b/usr.bin/learn/src/learn.c
new file mode 100644
index 00000000000..4c5c521bd1d
--- /dev/null
+++ b/usr.bin/learn/src/learn.c
@@ -0,0 +1,989 @@
+#include "stdio.h"
+#include "learn.h"
+#include "signal.h"
+#include <stdlib.h>
+
+char *direct = LLIB; /* CHANGE THIS ON YOUR SYSTEM */
+int more;
+char *level;
+int speed;
+char *sname;
+char *todo;
+FILE *incopy = NULL;
+int didok;
+int sequence = 1;
+int comfile = -1;
+int status;
+int wrong;
+char *pwline;
+char *dir;
+FILE *scrin;
+int logging = 1; /* set to 0 to turn off logging */
+int ask;
+
+main(int argc, char **argv)
+{
+ extern char * getlogin();
+
+ speed = 0;
+ more = 1;
+ pwline = getlogin();
+ setbuf(stdout, malloc(BUFSIZ));
+ selsub(argc, argv);
+ signal(SIGHUP, hangup);
+ signal(SIGINT, intrpt);
+ while (more) {
+ selunit();
+ dounit();
+ whatnow();
+ }
+ wrapup(0);
+}
+
+void hangup(int x)
+{
+ wrapup(1);
+}
+
+void intrpt(int x)
+{
+ char response[20], *p;
+
+ signal(SIGINT, hangup);
+ write(2, "\nInterrupt.\nWant to go on? ", 28);
+ p = response;
+ *p = 'n';
+ while (read(0, p, 1) == 1 && *p != '\n')
+ p++;
+ if (response[0] != 'y')
+ wrapup(1);
+ ungetc('\n', stdin);
+ signal(SIGINT, intrpt);
+}
+
+
+char last[100];
+char logf[100];
+char subdir[200];
+extern char * ctime();
+
+copy(prompt, fin)
+FILE *fin;
+{
+ FILE *fout, *f;
+ char s[200], t[200], s1[200], *r, *tod;
+ char nm[100];
+ int *p, tv[2];
+ extern int *action();
+ extern char *wordb();
+ int nmatch = 0;
+
+ if (subdir[0]==0)
+ sprintf(subdir, "../../%s", sname);
+ for (;;) {
+ if (pgets(s, prompt, fin) == 0)
+ if (fin == stdin) {
+ /* fprintf(stderr, "Don't type control-D\n"); */
+ /* this didn't work out very well */
+ continue;
+ } else
+ break;
+ trim(s);
+ /* change the sequence %s to lesson directory */
+ /* if needed */
+ for (r = s; *r; r++)
+ if (*r == '%') {
+ sprintf(s1, s, subdir, subdir, subdir);
+ strcpy(s, s1);
+ break;
+ }
+ r = wordb(s, t);
+ p = action(t);
+ if (p && *p == ONCE) { /* some actions done only once per script */
+ if (wrong) { /* we are on 2nd time */
+ scopy(fin, NULL);
+ continue;
+ }
+ strcpy(s, r);
+ r = wordb(s, t);
+ p = action(t);
+ }
+ if (p == 0) {
+ if (comfile >= 0) {
+ write(comfile, s, strlen(s));
+ write(comfile, "\n", 1);
+ }
+ else {
+ signal(SIGINT, SIG_IGN);
+ status = mysys(s);
+ signal(SIGINT, intrpt);
+ }
+ if (incopy) {
+ fprintf(incopy, "%s\n", s);
+ strcpy(last, s);
+ }
+ continue;
+ }
+ switch (*p) {
+ case READY:
+ if (incopy && r) {
+ fprintf(incopy, "%s\n", r);
+ strcpy(last, r);
+ }
+ return;
+ case PRINT:
+ if (wrong)
+ scopy(fin, NULL); /* don't repeat message */
+ else if (r)
+ list(r);
+ else
+ scopy(fin, stdout);
+ break;
+ case NOP:
+ break;
+ case MATCH:
+ if (nmatch > 0) /* we have already passed */
+ scopy(fin, NULL);
+ else if ((status = strcmp(r, last)) == 0) { /* did we pass this time? */
+ nmatch++;
+ scopy(fin, stdout);
+ } else
+ scopy(fin, NULL);
+ break;
+ case BAD:
+ if (strcmp(r, last) == 0) {
+ scopy(fin, stdout);
+ } else
+ scopy(fin, NULL);
+ break;
+ case SUCCEED:
+ scopy(fin, (status == 0) ? stdout : NULL);
+ break;
+ case FAIL:
+ scopy(fin, (status != 0) ? stdout : NULL);
+ break;
+ case CREATE:
+ fout = fopen(r, "w");
+ scopy(fin, fout);
+ fclose(fout);
+ break;
+ case CMP:
+ status = cmp(r); /* contains two file names */
+ break;
+ case MV:
+ sprintf(nm, "%s/L%s.%s", subdir, todo, r);
+ fcopy(r, nm);
+ break;
+ case USER:
+ case NEXT:
+ more = 1;
+ return;
+ case COPYIN:
+ incopy = fopen(".copy", "w");
+ break;
+ case UNCOPIN:
+ fclose(incopy);
+ incopy = NULL;
+ break;
+ case COPYOUT:
+ maktee();
+ break;
+ case UNCOPOUT:
+ untee();
+ break;
+ case PIPE:
+ comfile = makpipe();
+ break;
+ case UNPIPE:
+ close(comfile);
+ wait(0);
+ comfile = -1;
+ break;
+ case YES:
+ case NO:
+ if (incopy) {
+ fprintf(incopy, "%s\n", s);
+ strcpy(last, s);
+ }
+ return;
+ case WHERE:
+ printf("You are in lesson %s\n", todo);
+ fflush(stdout);
+ break;
+ case BYE:
+ more=0;
+ return;
+ case CHDIR:
+ printf("cd not allowed\n");
+ fflush(stdout);
+ break;
+ case LEARN:
+ printf("You are already in learn.\n");
+ fflush(stdout);
+ break;
+ case LOG:
+ if (!logging)
+ break;
+ if (logf[0] == 0)
+ sprintf(logf, "%s/log/%s", direct, sname);
+ f = fopen( (r? r : logf), "a");
+ if (f == NULL)
+ break;
+ time(tv);
+ tod = ctime(tv);
+ tod[24] = 0;
+ fprintf(f, "%s L%-6s %s %2d %s\n", tod,
+ todo, status? "fail" : "pass", speed, pwline);
+ fclose(f);
+ break;
+ }
+ }
+ return;
+}
+
+pgets(char *s, int prompt, FILE *f)
+{
+ if (prompt) {
+ if (comfile < 0)
+ printf("$ ");
+ fflush(stdout);
+ }
+ if (fgets(s, 100,f))
+ return(1);
+ else
+ return(0);
+}
+
+trim(s)
+char *s;
+{
+ while (*s)
+ s++;
+ if (*--s == '\n')
+ *s=0;
+}
+
+scopy(fi, fo) /* copy fi to fo until a line with # */
+FILE *fi, *fo;
+{
+ int c;
+
+ while ((c = getc(fi)) != '#' && c != EOF) {
+ do {
+ if (fo != NULL)
+ putc(c, fo);
+ if (c == '\n')
+ break;
+ } while ((c = getc(fi)) != EOF);
+ }
+ if (c == '#')
+ ungetc(c, fi);
+ if (fo != NULL)
+ fflush(fo);
+}
+
+cmp(r) /* compare two files for status */
+char *r;
+{
+ char *s;
+ FILE *f1, *f2;
+ int c1, c2, stat;
+
+ for (s = r; *s != ' ' && *s != '\0'; s++)
+ ;
+ *s++ = 0; /* r contains file 1 */
+ while (*s == ' ')
+ s++;
+ f1 = fopen(r, "r");
+ f2 = fopen(s, "r");
+ if (f1 == NULL || f2 == NULL)
+ return(1); /* failure */
+ stat = 0;
+ for (;;) {
+ c1 = getc(f1);
+ c2 = getc(f2);
+ if (c1 != c2) {
+ stat = 1;
+ break;
+ }
+ if (c1 == EOF || c2 == EOF)
+ break;
+ }
+ fclose(f1);
+ fclose(f2);
+ return(stat);
+}
+
+char *
+wordb(s, t) /* in s, t is prefix; return tail */
+char *s, *t;
+{
+ int c;
+
+ while (c = *s++) {
+ if (c == ' ' || c == '\t')
+ break;
+ *t++ = c;
+ }
+ *t = 0;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ return(c ? s : NULL);
+}
+
+
+dounit()
+{
+ char tbuff[100];
+
+ if (todo == 0)
+ return;
+ wrong = 0;
+retry:
+ start(todo);
+ sprintf(tbuff, "../../%s/L%s", sname, todo); /* script = lesson */
+ scrin = fopen(tbuff, "r");
+ if (scrin == NULL) {
+ fprintf(stderr, "No script.\n");
+ wrapup(1);
+ }
+
+ copy(0, scrin);
+ if (more == 0)
+ return;
+ copy(1, stdin);
+ if (more == 0)
+ return;
+ copy(0, scrin);
+
+ if (comfile >= 0)
+ close(comfile);
+ wait(&didok);
+ didok = (status == 0);
+ if (!didok) {
+ wrong++;
+ printf("\nSorry, that's %snot right. Do you want to try again? ",
+ wrong > 1 ? "still " : "");
+ fflush(stdout);
+ for(;;) {
+ gets(tbuff);
+ if (tbuff[0] == 'y') {
+ printf("Try the problem again.\n");
+ fflush(stdout);
+ goto retry;
+ } else if (strcmp(tbuff, "bye") == 0) {
+ wrapup(1);
+ } else if (tbuff[0] == 'n') {
+ wrong = 0;
+ printf("\nOK. Lesson %s (%d)\n", todo, speed);
+ printf("Skipping to next lesson.\n\n");
+ fflush(stdout);
+ break;
+ } else {
+ printf("Please type yes, no or bye: ");
+ fflush(stdout);
+ }
+ }
+ }
+ setdid(todo, sequence++);
+}
+
+int istop;
+
+list(r)
+char *r;
+{
+ void stop(int);
+ FILE *ft;
+ char s[200];
+
+ if (r==0)
+ return;
+ istop = 1;
+ signal(SIGINT, stop);
+ ft = fopen(r, "r");
+ if (ft != NULL) {
+ while (fgets(s, sizeof s, ft) && istop)
+ fputs(s, stdout);
+ fclose(ft);
+ }
+ signal(SIGINT, intrpt);
+}
+
+void stop(int x)
+{
+ istop=0;
+}
+
+makpipe()
+{
+ int f[2];
+
+ pipe(f);
+ if (fork()==0) {
+ close(f[1]);
+ close(0);
+ dup(f[0]);
+ close(f[0]);
+ execl ("/bin/sh", "sh", "-i", 0);
+ execl ("/usr/bin/sh", "sh", "-i", 0);
+ write(2,"Exec error\n",11);
+ }
+ close(f[0]);
+ sleep(2); /* so shell won't eat up too much input */
+ return(f[1]);
+}
+
+static int oldout;
+static char tee[100];
+
+maktee()
+{
+ int fpip[2], in, out;
+
+ if (tee[0] == 0)
+ sprintf(tee, "%s/tee", direct);
+ pipe(fpip);
+ in = fpip[0];
+ out= fpip[1];
+ if (fork() == 0) {
+ signal(SIGINT, SIG_IGN);
+ close(0);
+ close(out);
+ dup(in);
+ close(in);
+ execl (tee, "lrntee", 0);
+ fprintf(stderr, "Tee exec failed\n");
+ exit(1);
+ }
+ close(in);
+ fflush(stdout);
+ oldout = dup(1);
+ close(1);
+ if (dup(out) != 1)
+ fprintf(stderr, "Error making tee for copyout\n");
+ close(out);
+ return(1);
+}
+
+untee()
+{
+ int x;
+
+ fflush(stdout);
+ close(1);
+ dup(oldout);
+ close(oldout);
+ wait(&x);
+}
+
+# define SAME 0
+
+struct keys {
+ char *k_wd;
+ int k_val;
+} keybuff[] = {
+ {"ready", READY},
+ {"answer", READY},
+ {"#print", PRINT},
+ {"#copyin", COPYIN},
+ {"#uncopyin", UNCOPIN},
+ {"#copyout", COPYOUT},
+ {"#uncopyout", UNCOPOUT},
+ {"#pipe", PIPE},
+ {"#unpipe", UNPIPE},
+ {"#succeed", SUCCEED},
+ {"#fail", FAIL},
+ {"bye", BYE},
+ {"chdir", CHDIR},
+ {"cd", CHDIR},
+ {"learn", LEARN},
+ {"#log", LOG},
+ {"yes", YES},
+ {"no", NO},
+ {"#mv", MV},
+ {"#user", USER},
+ {"#next", NEXT},
+ {"skip", SKIP},
+ {"#where", WHERE},
+ {"#match", MATCH},
+ {"#bad", BAD},
+ {"#create", CREATE},
+ {"#cmp", CMP},
+ {"#goto", GOTO},
+ {"#once", ONCE},
+ {"#", NOP},
+ {NULL, 0}
+};
+
+int *action(s)
+char *s;
+{
+ struct keys *kp;
+ for (kp=keybuff; kp->k_wd; kp++)
+ if (strcmp(kp->k_wd, s) == SAME)
+ return(&(kp->k_val));
+ return(NULL);
+}
+
+# define NW 100
+# define NWCH 800
+struct whichdid {
+ char *w_less;
+ int w_seq;
+} which[NW];
+int nwh = 0;
+char whbuff[NWCH];
+char *whcp = whbuff;
+
+setdid(lesson, sequence)
+char *lesson;
+{
+ struct whichdid *pw;
+ for(pw=which; pw < which+nwh; pw++)
+ if (strcmp(pw->w_less, lesson) == SAME)
+ {
+ pw->w_seq = sequence;
+ return;
+ }
+ pw=which+nwh++;
+ if (nwh >= NW) {
+ fprintf(stderr, "nwh>=NW\n");
+ wrapup(1);
+ }
+ pw->w_seq = sequence;
+ pw->w_less = whcp;
+ while (*whcp++ = *lesson++);
+ if (whcp >= whbuff + NWCH) {
+ fprintf(stderr, "lesson name too long\n");
+ wrapup(1);
+ }
+}
+
+already(lesson, sequence)
+char *lesson;
+{
+ struct whichdid *pw;
+ for (pw=which; pw < which+nwh; pw++)
+ if (strcmp(pw->w_less, lesson) == SAME)
+ return(1);
+ return(0);
+}
+
+
+#define EASY 1
+#define MEDIUM 2
+#define HARD 3
+
+mysys(s)
+char *s;
+{
+ /* like "system" but rips off "mv", etc.*/
+ /* also tries to guess if can get away with exec cmd */
+ /* instead of sh cmd */
+ char p[300];
+ char *np[40];
+ register char *t;
+ int nv, type, stat;
+
+ type = EASY; /* we hope */
+ for (t = s; *t && type != HARD; t++) {
+ switch (*t) {
+ case '*':
+ case '[':
+ case '?':
+ case '>':
+ case '<':
+ case '$':
+ case '\'':
+ case '"':
+ type = MEDIUM;
+ break;
+ case '|':
+ case ';':
+ case '&':
+ type = HARD;
+ break;
+ }
+ }
+ switch (type) {
+ case HARD:
+ return(system(s));
+ case MEDIUM:
+ strcpy(p, "exec ");
+ strcat(p, s);
+ return(system(p));
+ case EASY:
+ strcpy(p,s);
+ nv = getargs(p, np);
+ t=np[0];
+ if ((strcmp(t, "mv") == 0)||
+ (strcmp(t, "cp") == 0)||
+ (strcmp(t, "rm") == 0)||
+ (strcmp(t, "ls") == 0) ) {
+ if (fork() == 0) {
+ char b[100];
+ signal(SIGINT, SIG_DFL);
+ strcpy(b, "/bin/");
+ strcat(b, t);
+ np[nv] = 0;
+ execv(b, np);
+ fprintf(stderr, "Execv failed\n");
+ exit(1);
+ }
+ wait(&stat);
+ return(stat);
+ }
+ return(system(s));
+ }
+}
+
+/*
+ * system():
+ * same as library version, except that resets
+ * default handling of signals in child, so that
+ * user gets the behavior he expects.
+ */
+
+int system(const char *s)
+{
+ int status, pid, w;
+ void (*istat)(void (*)(int)), (*qstat)(void (*)(int));
+
+ istat = signal(SIGINT, SIG_IGN);
+ qstat = signal(SIGQUIT, SIG_IGN);
+ if ((pid = fork()) == 0) {
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ execl("/bin/sh", "sh", "-c", s, 0);
+ _exit(127);
+ }
+ while ((w = wait(&status)) != pid && w != -1)
+ ;
+ if (w == -1)
+ status = -1;
+ signal(SIGINT, istat);
+ signal(SIGQUIT, qstat);
+ return(status);
+}
+
+getargs(s, v)
+char *s, **v;
+{
+ int i;
+
+ i = 0;
+ for (;;) {
+ v[i++]=s;
+ while (*s != 0 && *s!=' '&& *s != '\t')
+ s++;
+ if (*s == 0)
+ break;
+ *s++ =0;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == 0)
+ break;
+ }
+ return(i);
+}
+
+
+selsub(argc,argv)
+char *argv[];
+{
+ char ans1[100], *cp;
+ static char ans2[30];
+ static char dirname[20];
+ static char subname[20];
+
+ if (argc > 1 && argv[1][0] == '-') {
+ direct = argv[1]+1;
+ argc--;
+ argv++;
+ }
+ chknam(direct);
+ if (chdir(direct) != 0) {
+ fprintf(stderr, "can't cd to %s\n", direct);
+ exit(1);
+ }
+ sname = argc > 1 ? argv[1] : 0;
+ if (argc > 2)
+ strcpy (level=ans2, argv[2]);
+ else
+ level = 0;
+ if (argc > 3 )
+ speed = atoi(argv[3]);
+ if (!sname) {
+ printf("These are the available courses -\n");
+ list("Linfo");
+ printf("If you want more information about the courses,\n");
+ printf("or if you have never used 'learn' before,\n");
+ printf("type 'return'; otherwise type the name of\n");
+ printf("the course you want, followed by 'return'.\n");
+ fflush(stdout);
+ gets(sname=subname);
+ if (sname[0] == '\0') {
+ list("Xinfo");
+ do {
+ printf("\nWhich subject? ");
+ fflush(stdout);
+ gets(sname=subname);
+ } while (sname[0] == '\0');
+ }
+ }
+ chknam(sname);
+ if (!level) {
+ printf("If you were in the middle of this subject\n");
+ printf("and want to start where you left off, type\n");
+ printf("the last lesson number the computer printed.\n");
+ printf("To start at the beginning, just hit return.\n");
+ fflush(stdout);
+ gets(ans2);
+ if (ans2[0]==0)
+ strcpy(ans2,"0");
+ for (cp=ans2; *cp; cp++)
+ if (*cp == '(' || *cp == ' ')
+ *cp= 0;
+ level=ans2;
+ }
+
+ /* make new directory for user to play in */
+ if (chdir("play") != 0) {
+ fprintf(stderr, "can't cd to playpen\n");
+ exit(1);
+ }
+ sprintf(dir=dirname, "pl%da", getpid());
+ sprintf(ans1, "mkdir %s", dir);
+ system(ans1);
+ if (chdir(dir) < 0) {
+ fprintf(stderr, "Couldn't create working directory.\nBye.\n");
+ exit(1);
+ }
+ /* after this point, we have a working directory. */
+ /* have to call wrapup to clean up */
+ if (access(sprintf(ans1, "%s/%s/Init", direct, sname), 04)==0) {
+ sprintf(ans1, "%s/%s/Init %s", direct,sname, level);
+ if (system(ans1) != 0) {
+ printf("Leaving learn.\n");
+ wrapup(1);
+ }
+ }
+ if (level[0] == '-') /* no lesson names start with - */
+ ask = 1;
+ start(level);
+}
+
+chknam(name)
+char *name;
+{
+ if (access(name, 05) < 0) {
+ printf("Sorry, there is no subject or lesson named %s.\nBye.\n", name);
+ exit(1);
+ }
+}
+
+
+int nsave = 0;
+
+selunit()
+{
+ char fnam[20], s[50];
+ static char dobuff[50];
+ char posslev[20][20];
+ int diff[20], i, k, m, n, best, alts;
+ FILE *f;
+ char zb[200];
+ static char saved[20];
+
+ while (ask) {
+ printf("What lesson? ");
+ fflush(stdout);
+ gets(dobuff);
+ if (strcmp(dobuff, "bye") == 0)
+ wrapup(0);
+ level = todo = dobuff;
+ sprintf(s, "../../%s/L%s", sname, dobuff);
+ if (access(s, 04) == 0)
+ return;
+ printf("no such lesson\n");
+ }
+ alts = 0;
+retry:
+ f=scrin;
+ if (f==NULL) {
+ sprintf(fnam, "../../%s/L%s", sname, level);
+ f = fopen(fnam, "r");
+ if (f==NULL) {
+ fprintf(stderr, "No script for lesson %s.\n", level);
+ wrapup(1);
+ }
+ while (fgets(zb, 200, f)) {
+ trim(zb);
+ if (strcmp(zb, "#next")==0)
+ break;
+ }
+ }
+ if (feof(f)) {
+ printf("Congratulations; you have finished this sequence.\n");
+ fflush(stdout);
+ todo = 0;
+ return;
+ }
+ for(i=0; fgets(s, 50, f); i++) {
+ sscanf(s, "%s %d", posslev[i], &diff[i]);
+ }
+ best = -1;
+ /* cycle through lessons from random start */
+ /* first try the current place, failing that back up to
+ last place there are untried alternatives (but only one backup) */
+ n = grand()%i;
+ for(k=0; k<i; k++) {
+ m = (n+k)%i;
+ if (already(posslev[m],0)) continue;
+ if (best<0) best=m;
+ /* real alternatives */
+ alts++;
+ if (abs(diff[m]-speed) < abs(diff[best]-speed))
+ best=m;
+ }
+ if (best < 0 && nsave) {
+ nsave--;
+ strcpy(level, saved);
+ goto retry;
+ }
+ if (best <0) {
+ /* lessons exhausted or missing */
+ printf("Sorry, there are no alternative lessons at this stage.\n");
+ printf("See someone for help.\n");
+ fflush(stdout);
+ todo = 0;
+ return;
+ }
+ strcpy (dobuff, posslev[best]);
+ if (alts>1) {
+ nsave=1;
+ strcpy (saved, level);
+ }
+ todo = dobuff;
+ fclose(f);
+}
+
+abs(x)
+{
+ return(x>=0? x: -x);
+}
+
+grand()
+{
+ static int garbage;
+ int a[2], b;
+
+ time(a);
+ b = a[1]+10*garbage++;
+ return(b&077777);
+}
+
+#define ND 64
+
+start(lesson)
+char *lesson;
+{
+ struct direct {
+ int inode;
+ char name[14];
+ };
+ struct direct dv[ND], *dm, *dp;
+ int f, c, n;
+ char where [100];
+
+ f = open(".", 0);
+ n = read(f, dv, ND*sizeof(*dp));
+ n /= sizeof(*dp);
+ if (n==ND)
+ fprintf(stderr, "lesson too long\n");
+ dm = dv+n;
+ for(dp=dv; dp<dm; dp++)
+ if (dp->inode) {
+ n = strlen(dp->name);
+ if (dp->name[n-2] == '.' && dp->name[n-1] == 'c')
+ continue;
+ c = dp->name[0];
+ if (c>='a' && c<= 'z')
+ unlink(dp->name);
+ }
+ close(f);
+ if (ask)
+ return;
+ sprintf(where, "../../%s/L%s", sname, lesson);
+ if (access(where, 04)==0) /* there is a file */
+ return;
+ fprintf(stderr, "No lesson %s\n",lesson);
+ wrapup(1);
+}
+
+fcopy(new,old)
+char *new, *old;
+{
+ char b[512];
+ int n, fn, fo;
+ fn = creat(new, 0666);
+ fo = open(old,0);
+ if (fo<0) return;
+ if (fn<0) return;
+ while ( (n=read(fo, b, 512)) > 0)
+ write(fn, b, n);
+ close(fn);
+ close(fo);
+}
+
+
+whatnow()
+{
+ if (todo == 0) {
+ more=0;
+ return;
+ }
+ if (didok) {
+ strcpy(level,todo);
+ if (speed<=9) speed++;
+ }
+ else {
+ speed -= 4;
+ /* the 4 above means that 4 right, one wrong leave
+ you with the same speed. */
+ if (speed <0) speed=0;
+ }
+ if (wrong) {
+ speed -= 2;
+ if (speed <0 ) speed = 0;
+ }
+ if (didok && more) {
+ printf("\nGood. Lesson %s (%d)\n\n",level, speed);
+ fflush(stdout);
+ }
+}
+
+
+wrapup(n)
+int n;
+{
+ /* this routine does not use 'system' because it wants
+ interrupts turned off */
+ int retval, pid, pidw;
+
+ signal(SIGINT, SIG_IGN);
+ chdir("..");
+ if ( (pid=fork()) ==0) {
+ signal(SIGHUP, SIG_IGN);
+ execl("/bin/rm", "rm", "-r", dir, 0);
+ execl("/usr/bin/rm", "rm", "-r", dir, 0);
+ fprintf(stderr, "Can't find 'rm' command.\n");
+ exit(0);
+ }
+ printf("Bye.\n"); /* not only does this reassure user but
+ it stalls for time while deleting directory */
+ fflush(stdout);
+ /* printf("Wantd %d got %d val %d\n",pid, pidw, retval); */
+ exit(n);
+}
diff --git a/usr.bin/learn/src/learn.h b/usr.bin/learn/src/learn.h
new file mode 100644
index 00000000000..f597d9e2353
--- /dev/null
+++ b/usr.bin/learn/src/learn.h
@@ -0,0 +1,49 @@
+#define READY 0
+#define PRINT 1
+#define COPYIN 2
+#define COPYOUT 3
+#define UNCOPIN 4
+#define UNCOPOUT 5
+#define PIPE 6
+#define UNPIPE 7
+#define YES 8
+#define NO 9
+#define SUCCEED 10
+#define FAIL 11
+#define BYE 12
+#define LOG 13
+#define CHDIR 14
+#define LEARN 15
+#define MV 16
+#define USER 17
+#define NEXT 18
+#define SKIP 19
+#define WHERE 20
+#define MATCH 21
+#define NOP 22
+#define BAD 23
+#define CREATE 24
+#define CMP 25
+#define GOTO 26
+#define ONCE 27
+
+extern int more;
+extern char *level;
+extern int speed;
+extern char *sname;
+extern char *direct;
+extern char *todo;
+extern int didok;
+extern int sequence;
+extern int comfile;
+extern int status;
+extern int wrong;
+extern char *pwline;
+extern char *dir;
+extern FILE *incopy;
+extern FILE *scrin;
+extern int logging;
+extern int ask;
+
+extern void intrpt(int);
+extern void hangup(int);
diff --git a/usr.bin/learn/src/tee.c b/usr.bin/learn/src/tee.c
new file mode 100644
index 00000000000..6d2a94b44c2
--- /dev/null
+++ b/usr.bin/learn/src/tee.c
@@ -0,0 +1,41 @@
+char *PS1;
+
+main()
+{
+ int f;
+ char c;
+ char *getenv(char *);
+
+ PS1 = getenv("PS1");
+ if (PS1==0)
+ PS1 = "$ ";
+ f = creat(".ocopy", 0666);
+ while (read(0, &c, 1) == 1) {
+ write (1, &c, 1);
+ put(c, f);
+ }
+ fl(f);
+ close(f);
+}
+
+static char ln[5120];
+char *p = ln;
+put(c, f)
+{
+ *p++ = c;
+ if (c == '\n') {
+ fl(f);
+ p=ln;
+ }
+}
+fl(f)
+{
+ register char *s;
+
+ s = ln;
+ while (*s == '$' && *(s+1) == ' ')
+ s += 2;
+ if (strncmp(s, PS1, strlen(PS1)) == 0)
+ s += strlen(PS1);
+ write(f, s, p-s);
+}