paul@osu-dbs.UUCP (Paul Placeway) (04/13/84)
. The following code is my changes to Ken Greer's tcsh. I have added a visual mini-editor to the shell, and cleaned up the expansion routines some what. note that this is the 4.1 version. When we get 4.2 up I'll repost the new changes. Please send any changes back to me so that I can update our version. Note: this is part 1 of 5, you need all of the parts to make tcsh. Paul W. Placeway The Ohio State University IRCC (UUCP: cbosgd!osu-dbs!paul) (CSNet: paul@ohio-state) ================ cut here ================ : This is a shar archive. Extract with sh, not csh. echo x - INSTALL cat > INSTALL << '!Funky!Stuff!' Installing the new T-C shell... 1. Make a new directory and copy the original csh sources to it. 2. Copy these sources to that directory. 3. The file "DIFFS" is a list of differences between the orignal csh and the same files in tcsh. Go through the file, making the changes to the files. 4. make 5. Move ./tcsh wherever you like. 6. Enjoy. Note: The files dir.h and dir14.c support the old 14 character UNIX directories. Change the makefile if you have the Berkeley arbitrary length file names. Any references to /u0/osu should be changed to whatever your local software directory is. See tcsh.1 (man file) for changes made. If you make any fixes or changes, please mail them back to Paul W. Placeway The Ohio State University IRCC (UUCP: cbosgd!osu-dbs!paul) (CSNet: paul@ohio-state) !Funky!Stuff! echo x - DIFFS cat > DIFFS << '!Funky!Stuff!' ======== diff -bc csh/sh.c tcsh/sh.c ======== *** csh/sh.c Fri Apr 13 10:43:58 1984 --- tcsh/sh.c Fri Apr 13 10:45:00 1984 *************** *** 12,18 * April 1980 */ ! char *pathlist[] = { ".", "/usr/ucb", "/bin", "/usr/bin", 0 }; char HIST = '!'; char HISTSUB = '^'; bool nofile; --- 12,20 ----- * April 1980 */ ! #define AUTOLOGOUT "60" /* 1 Hour alarm */ ! char *pathlist[] = { ".", "/u0/osu/bin", "/usr/ucb", "/bin", ! "/usr/bin", "/u0/local/bin", 0 }; char HIST = '!'; char HISTSUB = '^'; bool nofile; *************** *** 23,28 bool fast; bool prompt = 1; main(c, av) int c; char **av; --- 25,38 ----- bool fast; bool prompt = 1; + auto_logout () + { + printf("auto-logout\n"); + close(SHIN); + child++; + goodbye(); + } + main(c, av) int c; char **av; *************** *** 54,59 */ set("status", "0"); dinit(cp = getenv("HOME")); /* dinit thinks that HOME == cwd in a * login shell */ if (cp == NOSTR) --- 64,72 ----- */ set("status", "0"); + set("tcsh", "1"); + set("autologout", AUTOLOGOUT); + sigset (SIGALRM, auto_logout); dinit(cp = getenv("HOME")); /* dinit thinks that HOME == cwd in a * login shell */ if (cp == NOSTR) *************** *** 69,74 if ((cp = getenv("TERM")) != NOSTR) set("term", savestr(cp)); /* * Re-initialize path if set in environment */ if ((cp = getenv("PATH")) == NOSTR) --- 82,99 ----- if ((cp = getenv("TERM")) != NOSTR) set("term", savestr(cp)); /* + * set the shell-level var to 1 or increment it. + */ + if ((cp = getenv("SHLVL")) != NOSTR) { + char buff[128]; + itoa (1 + atoi (cp), buff); + set("shlvl", savestr (buff)); + setenv ("SHLVL", buff); + } else { + set("shlvl", savestr("1")); + setenv ("SHLVL", "1"); + } + /* * Re-initialize path if set in environment */ if ((cp = getenv("PATH")) == NOSTR) *************** *** 95,100 pv[i] = 0; set1("path", pv, &shvhed); } set("shell", SHELLPATH); doldol = putn(getpid()); /* For $$ */ --- 120,126 ----- pv[i] = 0; set1("path", pv, &shvhed); } + set("shell", SHELLPATH); doldol = putn(getpid()); /* For $$ */ *************** *** 236,243 /* * Set up the prompt. */ ! if (prompt) ! set("prompt", uid == 0 ? "# " : "% "); /* * If we are an interactive shell, then start fiddling --- 262,270 ----- /* * Set up the prompt. */ ! if (prompt) { ! set("prompt", uid == 0 ? "} " : "> "); ! } /* * If we are an interactive shell, then start fiddling *************** *** 312,317 haderr = 0; /* In case second time through */ if (!fast && reenter == 0) { reenter++; /* Will have value("home") here because set fast if don't */ srccat(value("home"), "/.cshrc"); if (!fast && !arginp && !onelflg) --- 339,349 ----- haderr = 0; /* In case second time through */ if (!fast && reenter == 0) { reenter++; + if (!fast && !arginp && !onelflg) { + tw_init_bindings(); + ilsetup(SHIN, SHOUT); + /* setup the editor BEFORE doing .tcshrc */ + } /* Will have value("home") here because set fast if don't */ srccat(value("home"), "/.tcshrc"); if (!fast && !arginp && !onelflg) { *************** *** 313,320 if (!fast && reenter == 0) { reenter++; /* Will have value("home") here because set fast if don't */ ! srccat(value("home"), "/.cshrc"); ! if (!fast && !arginp && !onelflg) dohash(); if (loginsh) { int ldisc; --- 345,352 ----- /* setup the editor BEFORE doing .tcshrc */ } /* Will have value("home") here because set fast if don't */ ! srccat(value("home"), "/.tcshrc"); ! if (!fast && !arginp && !onelflg) { dohash(); } /* setup the command compleation path */ if (loginsh) { *************** *** 316,321 srccat(value("home"), "/.cshrc"); if (!fast && !arginp && !onelflg) dohash(); if (loginsh) { int ldisc; srccat(value("home"), "/.login"); --- 348,354 ----- srccat(value("home"), "/.tcshrc"); if (!fast && !arginp && !onelflg) { dohash(); + } /* setup the command compleation path */ if (loginsh) { int ldisc; srccat(value("home"), "/.login"); *************** *** 414,419 #ifdef INGRES srcunit(unit, 0); #else srcunit(unit, 1); #endif } --- 447,455 ----- #ifdef INGRES srcunit(unit, 0); #else + #ifdef OSU + srcunit(unit, 0); + #else srcunit(unit, 1); #endif #endif *************** *** 416,421 #else srcunit(unit, 1); #endif } /* --- 452,458 ----- #else srcunit(unit, 1); #endif + #endif } /* *************** *** 420,426 /* * Source to a unit. If onlyown it must be our file or our group or ! * we don't chance it. This occurs on ".cshrc"s and the like. */ srcunit(unit, onlyown) register int unit; --- 457,463 ----- /* * Source to a unit. If onlyown it must be our file or our group or ! * we don't chance it. This occurs on ".tcshrc"s and the like. */ srcunit(unit, onlyown) register int unit; *************** *** 623,629 getexit(osetexit); for (;;) { pendjob(); ! paraml.next = paraml.prev = ¶ml; paraml.word = ""; t = 0; setexit(); --- 660,666 ----- getexit(osetexit); for (;;) { pendjob(); ! paraml.next = paraml .prev = ¶ml; paraml.word = ""; t = 0; setexit(); *************** *** 677,697 * need or want to prompt. */ if (fseekp == feobp) ! if (!whyles) ! for (cp = value("prompt"); *cp; cp++) ! if (*cp == HIST) ! printf("%d", eventno + 1); ! else { ! if (*cp == '\\' && cp[1] == HIST) ! cp++; ! putchar(*cp | QUOTE); ! } ! else ! /* ! * Prompt for forward reading loop ! * body content. ! */ ! printf("? "); flush(); } err = 0; --- 714,720 ----- * need or want to prompt. */ if (fseekp == feobp) ! printprompt (); flush(); if (cp = value("autologout")) alarm (atoi(cp) * 60); *************** *** 693,698 */ printf("? "); flush(); } err = 0; --- 716,723 ----- if (fseekp == feobp) printprompt (); flush(); + if (cp = value("autologout")) + alarm (atoi(cp) * 60); } err = 0; *************** *** 705,710 prlex(¶ml); haderr = 0; } /* * The parser may lose space if interrupted. --- 730,736 ----- prlex(¶ml); haderr = 0; } + alarm (0); /* Turn auto-logout off */ /* * The parser may lose space if interrupted. *************** *** 859,862 #else _exit(i); #endif } --- 885,917 ----- #else _exit(i); #endif + } + + printprompt () + { + register char *cp; + extern char *value (); + char buff[128]; + PromptBuf[0] = '\0'; + if (!whyles) { + for (cp = value("prompt"); *cp; cp++) { + if (*cp == HIST) { + itoa(eventno + 1, buff); + strcat(PromptBuf, buff); + } + else { + if (*cp == '\\' && cp[1] == HIST) { + cp++; + } + strncat(PromptBuf, cp, 1); + } + } + } + else { + /* + * Prompt for forward reading loop + * body content. + */ + strncat(PromptBuf, "? ", 2); + } } ======== diff -bc csh/sh.exec.c tcsh/sh.exec.c ======== *** csh/sh.exec.c Fri Apr 13 10:44:01 1984 --- tcsh/sh.exec.c Fri Apr 13 10:45:04 1984 *************** *** 252,257 struct stat stb; struct direct dirbuf[BUFSIZ / sizeof (struct direct)]; char d_name[DIRSIZ + 1]; register int dirf, cnt; int i = 0; struct varent *v = adrof("path"); --- 252,258 ----- struct stat stb; struct direct dirbuf[BUFSIZ / sizeof (struct direct)]; char d_name[DIRSIZ + 1]; + char fnamebuf[BUFSIZ]; register int dirf, cnt; int i = 0; struct varent *v = adrof("path"); *************** *** 257,262 struct varent *v = adrof("path"); char **pv; havhash = 1; for (cnt = 0; cnt < HSHSIZ; cnt++) xhash[cnt] = 0; --- 258,264 ----- struct varent *v = adrof("path"); char **pv; + tw_clear_comm_list(); havhash = 1; for (cnt = 0; cnt < HSHSIZ; cnt++) xhash[cnt] = 0; *************** *** 272,278 close(dirf); continue; } ! while ((cnt = read(dirf, (char *) dirbuf, sizeof dirbuf)) >= sizeof dirbuf[0]) { register struct direct *ep = dirbuf; for (cnt /= sizeof(struct direct); cnt > 0; cnt--, ep++) { --- 274,282 ----- close(dirf); continue; } ! /* put check for non-exes here */ ! while ((cnt = read(dirf, (char *) dirbuf, ! sizeof dirbuf)) >= sizeof dirbuf[0]) { register struct direct *ep = dirbuf; for (cnt /= sizeof(struct direct); *************** *** 275,281 while ((cnt = read(dirf, (char *) dirbuf, sizeof dirbuf)) >= sizeof dirbuf[0]) { register struct direct *ep = dirbuf; ! for (cnt /= sizeof(struct direct); cnt > 0; cnt--, ep++) { if (ep->d_ino == 0) continue; copdent(d_name, ep->d_name); --- 279,286 ----- sizeof dirbuf)) >= sizeof dirbuf[0]) { register struct direct *ep = dirbuf; ! for (cnt /= sizeof(struct direct); ! cnt > 0; cnt--, ep++) { if (ep->d_ino == 0) continue; copdent(d_name, ep->d_name); *************** *** 279,284 if (ep->d_ino == 0) continue; copdent(d_name, ep->d_name); xhash[hash(d_name)] |= (1 << i); } } --- 284,299 ----- if (ep->d_ino == 0) continue; copdent(d_name, ep->d_name); + if (*d_name == '.') + continue; + + strcpy (fnamebuf, *pv); + strcat (fnamebuf, "/"); + strcat (fnamebuf, d_name); + + if (access(fnamebuf, 1)) /* if not exe file */ + continue; + xhash[hash(d_name)] |= (1 << i); tw_add_comm_name (d_name); } *************** *** 280,285 continue; copdent(d_name, ep->d_name); xhash[hash(d_name)] |= (1 << i); } } close(dirf); --- 295,301 ----- continue; xhash[hash(d_name)] |= (1 << i); + tw_add_comm_name (d_name); } } close(dirf); *************** *** 284,289 } close(dirf); } } dounhash() --- 300,308 ----- } close(dirf); } + tw_add_builtins(); + tw_add_aliases(); + tw_sort_comms (); /* re-build the command path for twenex.c */ } dounhash() ======== diff -bc csh/sh.func.c tcsh/sh.func.c ======== *** csh/sh.func.c Fri Apr 13 10:44:03 1984 --- tcsh/sh.func.c Fri Apr 13 10:45:07 1984 *************** *** 498,504 bseek(0l); do { if (intty && fseekp == feobp) ! printf("? "), flush(); aword[0] = 0, getword(aword); switch (srchx(aword)) { --- 498,504 ----- bseek(0l); do { if (intty && fseekp == feobp) ! printprompt(); /* printf("? "), flush(); */ aword[0] = 0, getword(aword); switch (srchx(aword)) { *************** *** 727,732 blkfree(gargv), gargv = 0; } char **environ; dosetenv(v) --- 727,733 ----- blkfree(gargv), gargv = 0; } + char **environ; dosetenv(v) *************** *** 1005,1012 int old, ldisc; short ctpgrp; ! if (loginsh) ! error("Can't suspend a login shell (yet)"); untty(); old = sigsys(SIGTSTP, SIG_DFL); kill(0, SIGTSTP); --- 1006,1063 ----- int old, ldisc; short ctpgrp; ! if (loginsh) { ! error ("Use \'detach\' to suspend the login shell."); ! } ! untty(); ! old = sigsys(SIGTSTP, SIG_DFL); ! kill(0, SIGTSTP); ! /* the shell stops here */ ! sigsys(SIGTSTP, old); ! if (tpgrp != -1) { ! retry: ! ioctl(FSHTTY, TIOCGPGRP, &ctpgrp); ! if (ctpgrp != opgrp) { ! old = sigsys(SIGTTIN, SIG_DFL); ! kill(0, SIGTTIN); ! sigsys(SIGTTIN, old); ! goto retry; ! } ! ioctl(FSHTTY, TIOCSPGRP, &shpgrp); ! setpgrp(0, shpgrp); ! } ! ioctl(FSHTTY, TIOCGETD, &oldisc); ! if (oldisc != NTTYDISC) { ! printf("Switching to new tty driver...\n"); ! ldisc = NTTYDISC; ! ioctl(FSHTTY, TIOCSETD, &ldisc); ! } ! } ! ! /* dodetach - try to stop the shell and insulate it from the SIGKILL that ! init will send it by forking and having the PARENT exit(0) */ ! ! dodetach() ! { ! int old, ldisc; ! short ctpgrp; ! int fork_val; ! ! if (!loginsh) { ! dosuspend(); ! return; ! } ! printf ("Trying to detach, hang on..."); ! /* try to clone yourself */ ! fork_val = fork(); ! ! /* if you did, then die, else spit an error */ ! if (fork_val == -1) { ! error ("Could not make the fork for detaching."); ! } else if (fork_val != 0) { ! exit (0); /* parent fork dies at this point, leaving ! the orphan which we now are */ ! } untty(); old = sigsys(SIGTSTP, SIG_DFL); kill(0, SIGTSTP); ======== diff -bc csh/sh.h tcsh/sh.h ======== *** csh/sh.h Fri Apr 13 10:44:07 1984 --- tcsh/sh.h Fri Apr 13 10:45:09 1984 *************** *** 5,10 #include <sys/vtimes.h> #endif /* * C shell * --- 5,12 ----- #include <sys/vtimes.h> #endif + #define OSU + /* do the ohio state changes */ /* * C shell * *************** *** 73,79 short opgrp; /* Initial pgrp and tty pgrp */ int oldisc; /* Initial line discipline or -1 */ struct tms shtimes; /* shell and child times for process timing */ ! /* * These are declared here because they want to be * initialized in sh.init.c (to allow them to be made readonly) --- 75,83 ----- short opgrp; /* Initial pgrp and tty pgrp */ int oldisc; /* Initial line discipline or -1 */ struct tms shtimes; /* shell and child times for process timing */ ! char PromptBuf[256]; /* buffer for the actual printed prompt. ! this is used in tenex.c and sh.c for ! pegets.c */ /* * These are declared here because they want to be * initialized in sh.init.c (to allow them to be made readonly) ======== diff -bc csh/sh.init.c tcsh/sh.init.c ======== *** csh/sh.init.c Fri Apr 13 10:44:07 1984 --- tcsh/sh.init.c Fri Apr 13 10:45:11 1984 *************** *** 8,13 extern int doalias(); extern int dobg(); extern int dobreak(); extern int dochngd(); extern int docontin(); --- 8,14 ----- extern int doalias(); extern int dobg(); + extern int dobind(); extern int dobreak(); extern int dochngd(); extern int docontin(); *************** *** 11,16 extern int dobreak(); extern int dochngd(); extern int docontin(); extern int dodirs(); extern int doecho(); extern int doelse(); --- 12,18 ----- extern int dobreak(); extern int dochngd(); extern int docontin(); + extern int dodetach(); extern int dodirs(); extern int doecho(); extern int doelse(); *************** *** 78,83 "alloc", showall, 0, 1, #endif "bg", dobg, 0, INF, "break", dobreak, 0, 0, "breaksw", doswbrk, 0, 0, #ifdef IIASA --- 80,86 ----- "alloc", showall, 0, 1, #endif "bg", dobg, 0, INF, + "bind", dobind, 0, 2, "break", dobreak, 0, 0, "breaksw", doswbrk, 0, 0, #ifdef IIASA *************** *** 88,93 "chdir", dochngd, 0, 1, "continue", docontin, 0, 0, "default", dozip, 0, 0, "dirs", dodirs, 0, 1, "echo", doecho, 0, INF, "else", doelse, 0, INF, --- 91,97 ----- "chdir", dochngd, 0, 1, "continue", docontin, 0, 0, "default", dozip, 0, 0, + /* "detach", dodetach, 0, 0, */ "dirs", dodirs, 0, 1, "echo", doecho, 0, INF, "else", doelse, 0, INF, ======== diff -bc csh/sh.lex.c tcsh/sh.lex.c ======== *** csh/sh.lex.c Fri Apr 13 10:44:09 1984 --- tcsh/sh.lex.c Fri Apr 13 10:45:14 1984 *************** *** 1,4 static char *sccsid = "@(#)sh.lex.c 4.1 10/9/80"; #include "sh.h" --- 1,5 ----- static char *sccsid = "@(#)sh.lex.c 4.1 10/9/80"; + static char *SCCSid = "@(#)sh.lex.c 1.3 - 1/1/82 13:57:46 - HP/CRC"; #include "sh.h" *************** *** 1169,1174 bgetc() { register int buf, off, c; #ifdef TELL if (cantell) { --- 1170,1177 ----- bgetc() { register int buf, off, c; + char ttyline[BUFSIZ]; + register int numleft = 0, roomleft; #ifdef TELL if (cantell) { *************** *** 1207,1212 if (fseekp >= feobp) { buf = (int) feobp / BUFSIZ; off = (int) feobp % BUFSIZ; do c = read(SHIN, fbuf[buf] + off, BUFSIZ - off); while (c < 0 && errno == EINTR); --- 1210,1216 ----- if (fseekp >= feobp) { buf = (int) feobp / BUFSIZ; off = (int) feobp % BUFSIZ; + roomleft = BUFSIZ - off; do if (intty) /* then use tenex routine */ { *************** *** 1208,1214 buf = (int) feobp / BUFSIZ; off = (int) feobp % BUFSIZ; do ! c = read(SHIN, fbuf[buf] + off, BUFSIZ - off); while (c < 0 && errno == EINTR); if (c <= 0) return (-1); --- 1212,1235 ----- off = (int) feobp % BUFSIZ; roomleft = BUFSIZ - off; do ! if (intty) /* then use tenex routine */ ! { ! c = numleft ? numleft : ! (isatty (SHIN) ? twenex(ttyline, BUFSIZ) : ! read(SHIN, ttyline, BUFSIZ) ); ! if (c > roomleft) /* No room in this buffer? */ ! { ! /* start with fresh buffer */ ! feobp = fseekp = fblocks * BUFSIZ; ! numleft = c; ! goto again; ! } ! if (c > 0) ! copy (fbuf[buf] + off, ttyline, c); ! numleft = 0; ! } ! else ! c = read(SHIN, fbuf[buf] + off, roomleft); while (c < 0 && errno == EINTR); if (c <= 0) return (-1); *************** *** 1213,1218 if (c <= 0) return (-1); feobp += c; goto again; } c = fbuf[buf][(int) fseekp % BUFSIZ]; --- 1234,1240 ----- if (c <= 0) return (-1); feobp += c; + if (!intty) goto again; } c = fbuf[buf][(int) fseekp % BUFSIZ]; ======== diff -bc csh/sh.misc.c tcsh/sh.misc.c ======== *** csh/sh.misc.c Fri Apr 13 10:44:10 1984 --- tcsh/sh.misc.c Fri Apr 13 10:45:16 1984 *************** *** 370,372 return (0); } } --- 370,413 ----- return (0); } } + + itoa(n, s) /* convert n to characters in s */ + int n; + char *s; + { + int i, sign; + + if ((sign = n) < 0) /* record sign */ + n = -n; + i = 0; + do { + s[i++] = n % 10 + '0'; + } while ((n /= 10) > 0); + if (sign < 0) + s[i++] = '-'; + s[i] = '\0'; + Reverse(s); + } + + Reverse(s) + char *s; + { + int c, i, j; + + for (i=0, j = strlen(s)-1; i < j; i++, j--) { + c = s[i]; + s[i] = s[j]; + s[j] = c; + } + } + + #include <sgtty.h> + + isatty(f) + { + struct sgttyb ttyb; + + if (gtty(f, &ttyb) < 0) + return(0); + return(1); + } ======== diff -bc csh/sh.proc.c tcsh/sh.proc.c ======== *** csh/sh.proc.c Fri Apr 13 10:44:16 1984 --- tcsh/sh.proc.c Fri Apr 13 10:45:22 1984 *************** *** 299,305 register int index; if (pp->p_pid == 0) { ! printf("BUG: process flushed twice"); return; } while (pp->p_pid != pp->p_jobid) --- 299,305 ----- register int index; if (pp->p_pid == 0) { ! printf("BUG: process flushed twice\n"); return; } while (pp->p_pid != pp->p_jobid) *************** *** 605,611 break; default: ! printf("BUG: status=%-9o", status); } } } --- 605,611 ----- break; default: ! printf("BUG: status=%-9o\n", status); } } } ======== diff -bc csh/sh.set.c tcsh/sh.set.c ======== *** csh/sh.set.c Fri Apr 13 10:44:20 1984 --- tcsh/sh.set.c Fri Apr 13 10:45:24 1984 *************** *** 73,79 HIST = *p++; HISTSUB = *p; ! } else if (eq(vp, "user")) setenv("USER", value(vp)); else if (eq(vp, "term")) setenv("TERM", value(vp)); --- 73,79 ----- HIST = *p++; HISTSUB = *p; ! } else if (eq(vp, "user")) { setenv("USER", value(vp)); } else if (eq(vp, "shlvl")) { setenv("SHLVL", value(vp)); *************** *** 75,83 HISTSUB = *p; } else if (eq(vp, "user")) setenv("USER", value(vp)); ! else if (eq(vp, "term")) ! setenv("TERM", value(vp)); ! else if (eq(vp, "home")) setenv("HOME", value(vp)); } while (p = *v++); } --- 75,83 ----- HISTSUB = *p; } else if (eq(vp, "user")) { setenv("USER", value(vp)); ! } else if (eq(vp, "shlvl")) { ! setenv("SHLVL", value(vp)); ! } else if (eq(vp, "home")) { setenv("HOME", value(vp)); } else if (eq(vp, "term")) { setenv("TERM", value(vp)); *************** *** 79,84 setenv("TERM", value(vp)); else if (eq(vp, "home")) setenv("HOME", value(vp)); } while (p = *v++); } --- 79,88 ----- setenv("SHLVL", value(vp)); } else if (eq(vp, "home")) { setenv("HOME", value(vp)); + } else if (eq(vp, "term")) { + setenv("TERM", value(vp)); + ilsetup(SHIN, SHOUT); + } } while (p = *v++); } !Funky!Stuff!
paul@osu-dbs.UUCP (Paul Placeway) (04/13/84)
. The following code is my changes to Ken Greer's tcsh. I have added a visual mini-editor to the shell, and cleaned up the expansion routines some what. note that this is the 4.1 version. When we get 4.2 up I'll repost the new changes. Please send any changes back to me so that I can update our version. Note: this is part 2 of 5, you need all of the parts to make tcsh. Paul W. Placeway The Ohio State University IRCC (UUCP: cbosgd!osu-dbs!paul) (CSNet: paul@ohio-state) ================ cut here ================ : This is a shar archive. Extract with sh, not csh. echo x - inputl.c cat > inputl.c << '!Funky!Stuff!' /* @(#)inputl.c 1.1 (Ohio State) 6/24/83 Paul Placeway */ /* * $Compile: cc -DTEST -o foobar -O %f -ltermcap */ /***************************************************************** * inputl() -- get a promped line from the keyboard with input editing * * SYNOPSIS: * char * * inputl(prompt, string, stringlen) * char *prompt; * char *string; * int stringlen; length of the above string * * ilsetup (instr, outstr) * short instr, outstr; input and output streams - normaly 0 & 1 * * int * ilpushback(string) * char *string; * * DESCRIPTION: * Inputl reads a string from the terminal and allows editing by the * user. The editing is done by control keys and special use keys such * as DELETE or BACKSPACE. A list of the controls follows: * * ^@ Nothing (becuase some parts won't cope with it) * * ^A Moves the cursor to the begining of the line (after * the prompt) * * ^B Moves the cursor back one space. Beeps if it is * allready in column one. * * ^C Sends a SIGINTR to the current process (from the tty * driver) * * ^D Deletes the character that is over the cursor. * * ^E Moves the cursor to the end of the line. * * ^F Moves the cursor forward one space. Beeps if at the * end of the line. * * ^G Erase entire line and start over. * * ^H BS Same as ^B * * ^I TAB Inserts a tab character * * ^J LF Moves the cursor to the end of the line, inserts a * newline character, and sends the line. * * ^K Delete all characters from the current cursor * possition to the end inclusive and save them in a * special buffer to be called back later (see ^Y). * * ^L Put a ^L at the end of the line and send it (without * echoing a newline). * * ^M RETURN Same as ^J * * ^N Move to the next following word (i.e. skip to the * end of the current whitespace, then skip to the end * of the non-whitespace) * * ^O Flush tty output until the next tty input request * (tty driver). * * ^P Same as ^N but go backwards. * * ^Q Restart output. (see ^S) * * ^R Redisplay the current line and move the cursor to * it's original place. * * ^S Stop the tty's output. * * ^T Transpose previous two characters. * * ^U Same as ^K but kill to the beginning of the line * exclusive. * * ^V Clear the screen and redisplay the line * * ^W Delete the previous word. * * ^X Delete the next word (see ^W). * * ^Y Yank the characters saved (by ^K or ^U) as if they * were typed on the keyboard. * * ^Z Send a SIGTSTP to the current process (tty driver). * * ^[ ESC put a ESC at the end of the line and send it * (without echoing a newline) * * ^\ send a SIGQUIT to the current process (tty driver) * * ^] Send a SIGTSTP when the program attempts to read the * NULL. (tty driver) This is usually immeaditly * because of CBREAK mode, but the assignment is left * so that cooked mode uses it. * * ^^ Insert the next character regardless of it's * meaning. * * ^_ Put an end of file mark at the end and send the * line. (tty driver) * * ^? DELETE Delete the character immeaditely to the left of the * cursor. Beeps if the cursor is at the beginning of * the line. * * ALL OTHERS All other characters insert themselfs into the line. * * Ilpushback pushes the string onto the input line exactly as if it * had been typed on the console. The next call to inputl will print * the string and put the cursor at the end of the line. If control * characters are passed to ilpushback, they will act as if they had * been quoted. * * NOTE * This program is in the public domain. It may be copied and * modified, but not sold or included in another product which * is sold. * * If you change anything, PLEASE send me mail describing your changes * so that I can include them in later versions. A diff listing would * be very helpful, also. * * Also, please send any bug reports to me. Thanks, * * Paul W. Placeway * The Ohio State University IRCC * (UUCP: cbosgd!osu-dbs!paul) * (CSNet: paul@ohio-state) * ***************************************************************** */ #define CSH #define MAKE_INPUTL /* so that some defines get done */ #include "inputl.h" /* Interface to termcap library. */ static char *ClearEOL; static char *ClearScr; static char *DeleteCh; static char *StartIns; static char *EndIns; static char *FwdCh; static char *BkdCh; static char input_line[1024]; /* what i'm getting */ static char kill_buf[1024]; /* for ^K, ^U, ^Y */ static char last_inp[1024]; /* for matching input compleation and ^@ */ static int endptr; /* the last char in the line */ static int cursor; /* The current cursor posisition (0 = beg) */ static int killbuf_len = 0; /* Length of the kill buffer string */ static int lastin_len = 0; /* for last input saving */ static int matching_length = 0; /* for command compleation */ static int pushed_input = FALSE; /* weather some input has been pushed on */ static char *prompt; /* a global pointer to the prompt */ static int prompt_length; /* how long the prompt given is */ static int edit_flags; /* flags that effect the cursor posisition and other things */ static int do_redisplay = FALSE; /* redraw what you have now */ static int has_been_setup = FALSE; /* for autosetup of the terminal */ static int tty_driver_not_setup = TRUE; /* for autosetup of the tty driver */ static int keybindings[256]; /* the currently assigned keys */ static int bindings_been_inited = FALSE; /* for setup of bindings */ /* the first time */ #ifdef CSH extern short SHIN, SHOUT; #else static short SHIN = 0; /* for input and output munging */ static short SHOUT = 1; #endif /* vars for the tty driver */ struct sgttyb old_term; struct sgttyb new_term; struct sgttyb none_term; struct sgttyb test_term; /* for testing wether the modes have been */ /* changed on us so we can compensate */ struct tchars old_tchars; /* INT, QUIT, XON, XOFF, EOF, BRK */ struct tchars new_tchars; struct tchars none_tchars; struct ltchars old_ltchars; /* SUSP, DSTOP, RPRNT, FLUSH, WERASE, LNEXT */ struct ltchars new_ltchars; struct ltchars none_ltchars; long old_modwd; /* both for testing if things have changed */ long test_modwd; /* on us during proccessing */ /* * inputl.c -- get an input line from the terminal using full line editing * and take the line on an escape, control-V , return, * or line feed. */ char inputl(prompt_str, string_to_get, string_to_get_length) char *prompt_str; /* prompt string */ char *string_to_get; int string_to_get_length; /* max length of the input line */ { register int i; char c; int doing_input; char temp_c1; char temp_c2; int old_cursor; int quoted; int outputchar(); char retval; /* return value - char typed to send line */ if (isatty (SHIN) == 0) { if (read (SHIN, string_to_get, 512) <= 0) { string_to_get[0] = -1; string_to_get[1] = '\0'; } return ('\n'); } if (has_been_setup == FALSE) { ilsetup(); } prompt = prompt_str; prompt_length = strlen (prompt); if (prompt_length > 70) prompt_length = 70; set_term(); /* turn on CBREAK and NOECHO */ if (pushed_input == FALSE) { if (ClearEOL != 0) { write (SHOUT, "\r", 1); /* start at the beg of line */ write (SHOUT, prompt, prompt_length); /* write the prompt */ tputs (ClearEOL, 0, outputchar); } else { write (SHOUT, prompt, prompt_length); /* write the prompt */ } cursor = 0; /* start cursor off at beginning */ endptr = 0; } else { if (do_redisplay == FALSE) { for (i = matching_length; i < endptr; i++) { PrintChar (input_line[i], i+1); } } pushed_input = FALSE; } doing_input = TRUE; if (do_redisplay == TRUE) { Redisplay(); do_redisplay = FALSE; } quoted = FALSE; while (doing_input) { if (read (SHIN, &c, 1) != 1) { /* get an input character */ input_line[0] = -1; /* fool it into thinking that the */ endptr = 1; /* end-of-file char was typed */ break; } if (quoted == TRUE) { quoted = FALSE; Insert(c); set_term(); /* reset the special characters */ } else switch (keybindings[c]) { /* and parse it for kbrd commands */ case YANK_LAST_INPUT: /* ^@ -- YANK_LAST_INPUT */ for (i = 0; i < lastin_len; i++) { Insert(last_inp[i]); } break; case BEGINNING_OF_LINE: /* ^A -- BEGINNING_OF_LINE */ MoveCursor(cursor, 0); cursor = 0; break; case BACKWARD_CHAR: /* ^B -- BACKWARD_CHAR */ if (cursor != 0) { MoveCursor(cursor, cursor - 1); cursor--; } else beep(); break; case TTY_SIGINTR: /* ^C -- SIGINTR (tty driver) */ break; case DELETE_CHAR_FORWARD: /* ^D -- DELETE_CHAR_FORWARD */ Delete(); break; case END_OF_LINE: /* ^E -- END_OF_LINE */ MoveCursor(cursor, endptr); cursor = endptr; break; case FORWARD_CHAR: /* ^F -- FORWARD_CHAR */ if (cursor != endptr) { MoveCursor(cursor, cursor +1); /* move the cursor fwd */ cursor++; /* by writing its current */ } /* character */ else beep(); /* at end-of-line */ break; case START_OVER: /* ^G -- START_OVER */ ClearLine(); (void) write (SHOUT, prompt, prompt_length); endptr = 0; cursor = 0; break; case KILL_TO_END: /* ^K -- KILL_TO_END */ if (cursor == endptr) { beep(); break; } else { killbuf_len = endptr - cursor; for (i = 0; i < killbuf_len; i++) { kill_buf[i] = input_line[cursor + i]; } endptr = cursor; if (ClearEOL != 0) { tputs (ClearEOL, 0, outputchar); } else { Redisplay(); } break; } case SEND_EOF: /* ^_ -- TTY_EOF */ c = -1; /* sends an EOF */ /* fall through to */ case SEND_LINE: /* ^V -- SEND_LINE */ input_line[endptr] = '\0'; /* just in case */ doing_input = FALSE; /* send it */ MoveCursor(cursor, endptr); /* make sure your at end */ retval = c; /* set the return value */ break; case NL_SEND_LINE: /* ^J LF -- NL_SEND_LINE */ if (endptr > 0) { /* save the input for ^H */ lastin_len = endptr; for (i = 0; i <=lastin_len; i++) { last_inp[i] = input_line[i]; } } input_line[endptr] = '\n'; retval = '\n'; /* like the name says: NL... */ doing_input = FALSE; /* send it */ if ((c == '\n') || (c == '\r')) { outputchar('\n'); } break; case FORWARD_WORD: /* ^N -- FORWARD_WORD */ old_cursor = cursor; while ((IsInWord (input_line[cursor]) == 0) && (cursor != endptr)) { ++cursor; } while ((IsInWord (input_line[cursor])) && (cursor != endptr)) { ++cursor; } MoveCursor(old_cursor, cursor); break; case ERR: /* beep on this key */ beep(); /* fall through to... */ case NOP: /* no operation */ break; case TTY_FLUSH_OUTPUT: /* ^O -- TTY_FLUSH_OUTPUT */ break; case BACKWARD_WORD: /* ^P -- BACKWARD_WORD */ old_cursor = cursor; while ((IsInWord (input_line[cursor-1]) == 0) && (cursor != 0)) { --cursor; } while ((IsInWord (input_line[cursor-1])) && (cursor != 0)) { --cursor; } MoveCursor(old_cursor, cursor); break; case TTY_START_OUTPUT: /* ^Q -- START_OUTPUT (tty driver) */ break; case REDISPLAY: /* ^R -- REDISPLAY */ Redisplay(); break; case TTY_STOP_OUTPUT: /* ^S -- STOP_OUTPUT */ break; case TRANSPOSE_CHARS: /* ^T -- TRANSPOSE_PREVIOUS_TWO */ if (cursor >= 2) { temp_c2 = input_line[cursor - 2]; temp_c1 = input_line[cursor - 1]; DelPrev(); DelPrev(); Insert(temp_c1); Insert(temp_c2); } else beep(); break; case KILL_TO_BEGINNING: /* ^U -- KILL_TO_BEGINNING */ if (cursor == 0) { beep(); break; } else { killbuf_len = cursor; for (i = 0; i < cursor; i++) { kill_buf[i] = input_line[i]; } while (cursor != 0) { DelPrev(); } break; } case CLEAR_SCREEN: /* ^L -- CLEAR_SCREEN */ ClearScreen(); Redisplay(); break; case DELETE_WORD_BACKWARD: /* ^W -- DELETE_WORD_BACKWARD */ while ((IsInWord (input_line[cursor-1]) == 0) && (cursor != 0)) { DelPrev(); } while ((IsInWord (input_line[cursor-1])) && (cursor != 0)) { DelPrev(); } break; case DELETE_WORD_FORWARD: /* ^X -- DELETE_WORD_FORWARD */ while ((IsInWord (input_line[cursor]) == 0) && (cursor != endptr)) { Delete(); } while ((IsInWord (input_line[cursor])) && (cursor != endptr)) { Delete(); } break; case YANK_KILLBUFFER: /* ^Y -- YANK_KILLBUFFER */ for (i = 0; i < killbuf_len; i++) { Insert(kill_buf[i]); } break; case TTY_SIGTSUSP: /* ^Z -- TTY_SIGTSUSP */ break; case TTY_SIGQUIT: /* ^\ -- TTY_SIGQUIT */ break; case TTY_DSUSP: /* ^] -- TTY_DSUSP */ break; case QUOTE_NEXT: /* ^^ -- QUOTE_NEXT */ quoted = TRUE; nospecial(); /* this is gross -- turn off all the chars */ break; case DELETE_CHAR_BACKWARD: /* ^? DEL -- DELETE_CHAR_BACKWARD */ DelPrev(); break; case AUTO_INSERT: /* it's a normal character, insert it */ Insert(c); break; default: /* it's garbage, ignore it */ break; } /* end of switch (c) */ } /* end of while (doing_input) */ input_line[endptr+1] = NULL; reset_term(); strncpy(string_to_get, input_line, string_to_get_length- 1); string_to_get[string_to_get_length] = '\0'; /* string_to_get = input_line */ return (retval); } /************************************************************/ #ifdef CSH ilsetup () { #else ilsetup(Shin, Shout) short Shin, Shout; /* SHIN = input stream, SHOUT = output str. */ { #endif static char bp[1024]; static char buffer[1024]; char *area = buffer; char *getenv(); char *tgetstr(); char *name; int retcode; #ifndef CSH SHIN = Shin; SHOUT = Shout; #endif name = getenv("TERM"); retcode = tgetent(bp, name); switch(retcode) { case -1: printf("can't open termcap file.\n"); exit(1); break; case 0: printf("No termcap entry for %s, using \"dumb\"\n", name); if ((tgetent(bp, "dumb")) == -1) { printf("can't open termcap file.\n"); exit (1); } break; } StartIns = tgetstr("im", &area); EndIns = tgetstr("ei", &area); DeleteCh = tgetstr("dc", &area); ClearEOL = tgetstr("ce", &area); ClearScr = tgetstr("cl", &area); FwdCh = tgetstr("nd", &area); BkdCh = tgetstr("bc", &area); InitBindings (); has_been_setup = TRUE; } static InitBindings () { register int i; if (bindings_been_inited) return; get_tty_special (); /* get the old chars */ for (i=0; i<128; i++) { /* assign all the keys */ DoBindToKey (AUTO_INSERT, i); } DoBindToKey(BEGINNING_OF_LINE, 001); DoBindToKey(BACKWARD_CHAR, 002); DoBindToKey(TTY_SIGINTR, 003); DoBindToKey(DELETE_CHAR_FORWARD, 004); DoBindToKey(END_OF_LINE, 005); DoBindToKey(FORWARD_CHAR, 006); DoBindToKey(START_OVER, 007); DoBindToKey(YANK_LAST_INPUT, 010); DoBindToKey(KILL_TO_END, 013); DoBindToKey(SEND_EOF, 037); DoBindToKey(SEND_LINE, 000); DoBindToKey(SEND_LINE, 077); DoBindToKey(SEND_LINE, 033); DoBindToKey(NL_SEND_LINE, 012); DoBindToKey(NL_SEND_LINE, 015); DoBindToKey(FORWARD_WORD, 016); DoBindToKey(TTY_FLUSH_OUTPUT, 017); DoBindToKey(BACKWARD_WORD, 020); DoBindToKey(TTY_START_OUTPUT, 021); DoBindToKey(REDISPLAY, 022); DoBindToKey(TTY_STOP_OUTPUT, 023); DoBindToKey(TRANSPOSE_CHARS, 024); DoBindToKey(KILL_TO_BEGINNING, 025); DoBindToKey(CLEAR_SCREEN, 014); DoBindToKey(DELETE_WORD_BACKWARD, 027); DoBindToKey(DELETE_WORD_FORWARD, 030); DoBindToKey(YANK_KILLBUFFER, 031); DoBindToKey(TTY_SIGTSUSP, 032); DoBindToKey(TTY_SIGQUIT, 034); DoBindToKey(TTY_DSUSP, 035); DoBindToKey(QUOTE_NEXT, 026); /* ^^ -> ^V (back to normal) */ DoBindToKey(DELETE_CHAR_BACKWARD, 0177); reset_term (); /* export bindings */ bindings_been_inited = TRUE; } static ClearLine() { int outputchar(); if (ClearEOL != 0) { outputchar ('\r'); tputs (ClearEOL, 0, outputchar); } else { outputchar ('\n'); } } static ClearScreen() { int outputchar(); if (ClearScr != 0) { tputs (ClearScr, 0, outputchar); } } static DelPrevChar() { register int distance; int Count(); MoveCursor(cursor, cursor-1); distance = Count(cursor) - Count(cursor - 1); DelScrChar(distance); } static DelChar() { register int distance; int Count(); distance = Count(cursor + 1) - Count(cursor); DelScrChar(distance); } static DelScrChar(distance) int distance; { register int i; int outputchar(); if (DeleteCh == 0) { /* do a huge pain */ for (i = cursor; i < endptr; i++) { PrintChar(input_line[i], i+1); /* i+1??? brain-dammage */ } for (i = 0; i < distance; i++) { outputchar(' '); } /* MoveCursor(endptr + distance, cursor); INCONSISTANT USAGE... */ for (i = distance; i > 0; i--) { if (BkdCh != 0) { tputs (BkdCh, 0, outputchar); } else { outputchar ('\b'); } } /* now I'm at the end... */ MoveCursor(endptr, cursor); } else { for (i = 0; i < distance; i++) { tputs (DeleteCh, 0, outputchar); } } } static InsChar(c) char c; { register int i; int outputchar(); if (StartIns == 0) { for (i = cursor-1; i < endptr; i++) { PrintChar(input_line[i], i+1); } MoveCursor(endptr, cursor); } else { if (cursor != endptr) { tputs (StartIns, 0, outputchar); } PrintChar(c, cursor); if (cursor != endptr) { tputs (EndIns, 0, outputchar); } } } static MoveCursor(where_from, where_to) int where_from; int where_to; { int diff; int i; int Count(); int outputchar(); diff = Count(where_to) - Count(where_from); if (diff > 0) { if (FwdCh != 0) { for (i = 0; i < diff; i++) { tputs (FwdCh, 0, outputchar); } } else { for (i = where_from; i < where_to; i++) { PrintChar(input_line[i], i); } } } else if (diff < 0) { diff = -diff; for (i = 0; i < diff; i++) { if (BkdCh != 0) { tputs (BkdCh, 0, outputchar); } else { outputchar ('\b'); } } } } /* Set terminal to CBREAK and NOECHO. */ static set_term() { if(tty_driver_not_setup == 0) { /* tty driver is set up */ ioctl (SHIN, TIOCGETP, &test_term); /* get the setup */ ioctl (SHIN, TIOCLGET, &test_modwd); /* get local mode word */ if ((old_term.sg_ispeed != test_term.sg_ispeed) || (old_term.sg_ospeed != test_term.sg_ospeed) || (old_term.sg_flags != test_term.sg_flags) || (old_modwd != test_modwd)) { /* printf ("tty has changed.\n"); */ tty_driver_not_setup = 1; /* things have changed, fake it */ } } if (tty_driver_not_setup) { /* hasn't been setup yet or reseting do to a change */ get_tty_modes (); /* get ONLY the mode word and term strucs */ tty_driver_not_setup = 0; } ioctl (SHIN, TIOCSETN, &new_term); /* set the terminal in CBREAK */ ioctl (SHIN, TIOCSETC, &new_tchars); /* with these special chars */ ioctl (SHIN, TIOCSLTC, &new_ltchars); /* and these local specials */ /* printf("in: %d out: %d\n", new_term.sg_ispeed, new_term.sg_ospeed); */ } /* get the special characters from the tty driver. */ static get_tty_special () { ioctl (SHIN, TIOCGETP, &old_term); ioctl (SHIN, TIOCGETC, &old_tchars); ioctl (SHIN, TIOCGLTC, &old_ltchars); ioctl (SHIN, TIOCGETP, &new_term); ioctl (SHIN, TIOCGETP, &none_term); ioctl (SHIN, TIOCGETC, &new_tchars); ioctl (SHIN, TIOCGETC, &none_tchars); ioctl (SHIN, TIOCGLTC, &new_ltchars); ioctl (SHIN, TIOCGLTC, &none_ltchars); ioctl (SHIN, TIOCLGET, &old_modwd); /* get local mode word */ /* so that we know old setup */ /* test prints */ /* printf("in: %d out: %d\n", old_term.sg_ispeed, old_term.sg_ospeed); */ /* the old setup for when inputl exits */ old_term.sg_flags &= ~(RAW | CBREAK); /* unset */ old_term.sg_flags |= (ECHO | CRMOD); /* set */ DoBindToKey (DELETE_CHAR_BACKWARD, old_term.sg_erase); DoBindToKey (KILL_TO_BEGINNING, old_term.sg_kill); DoBindToKey (TTY_SIGINTR, old_tchars.t_intrc); DoBindToKey (TTY_SIGQUIT, old_tchars.t_quitc); DoBindToKey (TTY_START_OUTPUT, old_tchars.t_startc); DoBindToKey (TTY_STOP_OUTPUT, old_tchars.t_stopc); DoBindToKey (SEND_EOF, old_tchars.t_eofc); old_tchars.t_brkc = -1; /* none */ DoBindToKey (TTY_SIGTSUSP, old_ltchars.t_suspc); DoBindToKey (TTY_DSUSP, old_ltchars.t_dsuspc); DoBindToKey (REDISPLAY, old_ltchars.t_rprntc); DoBindToKey (TTY_FLUSH_OUTPUT, old_ltchars.t_flushc); DoBindToKey (DELETE_WORD_BACKWARD, old_ltchars.t_werasc); DoBindToKey (QUOTE_NEXT, old_ltchars.t_lnextc); /* now for the current setup (CBREAK with some de-assigned special chars */ new_term.sg_flags &= ~(ECHO | XTABS); /* unset */ new_term.sg_flags |= (CBREAK | CRMOD); /* set */ /* the new chars should get worked out in the old chars bind-to-keys */ /* now the none structures for literal nexting of characters */ none_term.sg_flags &= ~(ECHO | CRMOD); /* unset */ none_term.sg_flags |= (CBREAK); /* set */ none_tchars.t_intrc = -1; none_tchars.t_quitc = -1; none_tchars.t_startc = -1; none_tchars.t_stopc = -1; none_tchars.t_eofc = -1; none_tchars.t_brkc = -1; none_ltchars.t_suspc = -1; none_ltchars.t_dsuspc = -1; none_ltchars.t_rprntc = -1; none_ltchars.t_flushc = -1; none_ltchars.t_werasc = -1; none_ltchars.t_lnextc = -1; reset_term (); /* export the new bound values */ } /* get only the modes from the tty driver. */ static get_tty_modes () { ioctl (SHIN, TIOCGETP, &new_term); /* get new now, old later */ ioctl (SHIN, TIOCGETP, &none_term); ioctl (SHIN, TIOCLGET, &old_modwd); /* get local mode word */ /* so that we know old setup */ /* now for the current setup (CBREAK with some de-assigned special chars */ new_term.sg_flags &= ~(ECHO | XTABS); /* unset */ new_term.sg_flags |= (CBREAK | CRMOD); /* set */ new_term.sg_erase = old_term.sg_erase; /* do this so that the */ new_term.sg_kill = old_term.sg_kill; /* erase and kill chars */ /* don't get changed to */ ioctl (SHIN, TIOCGETP, &old_term); /* something else. */ old_term.sg_erase = new_term.sg_erase; old_term.sg_kill = new_term.sg_kill; /* the old setup for when inputl exits */ old_term.sg_flags &= ~(RAW | CBREAK); /* unset */ old_term.sg_flags |= (ECHO | CRMOD); /* set */ /* now the none structures for literal nexting of characters */ none_term.sg_flags &= ~(ECHO | CRMOD); /* unset */ none_term.sg_flags |= (CBREAK); /* set */ reset_term (); /* export the new bound values */ } /* Reset the terminal to normal mode. */ static reset_term() { ioctl (SHIN, TIOCSETN, &old_term); /* set the terminal in CBREAK */ ioctl (SHIN, TIOCSETC, &old_tchars); /* with these special chars */ ioctl (SHIN, TIOCSLTC, &old_ltchars); /* and these local specials */ } /* unset all special chars */ static nospecial() { ioctl (SHIN, TIOCSETN, &none_term); /* set the terminal in CBREAK */ ioctl (SHIN, TIOCSETC, &none_tchars); /* with these special chars */ ioctl (SHIN, TIOCSLTC, &none_ltchars); /* and these local specials */ } /* ******************************************************* */ #ifdef TEST main() { char buffer[1024]; char retnval; while (1) { if ((retnval = inputl("inputl:", buffer, 72)) == '\033') { strcat(buffer, "foo"); ilpushback(buffer); continue; } else printf ("Return value = %o,\nI saw :%s\n", retnval, buffer); if (buffer[0] == 'Q') exit(0); } } #endif /* ********************************************************* */ static int outputchar(c) char c; { (void) write (SHOUT, &c, 1); } static beep() { outputchar(BELL); } static Delete() { register int i; if (cursor < endptr) { for (i = cursor; i < endptr; i++) { input_line[i] = input_line[i+1]; } endptr--; DelChar(); } else beep(); } static DelPrev() { register int i; if (cursor != 0) { DelPrevChar(); --cursor; if (cursor <0 ) cursor = 0; for (i = cursor; i < endptr; i++) { input_line[i] = input_line[i+1]; } endptr--; if (endptr <0 ) endptr = 0; } else beep(); } static Redisplay() { register int i; ClearLine(); (void) write (SHOUT, prompt, prompt_length); for (i = 1; i <= endptr; i++) { PrintChar(input_line[i -1], i); } MoveCursor(endptr, cursor); } static Insert(c) char c; { register int i; if (cursor >= endptr){ /* put it at the end of the line */ endptr++; if (endptr > 1023) endptr = 1023; input_line[cursor++] = c; } else { /* its in the middle of the line, shift line first, then put in char */ endptr++; if (endptr > 1023) endptr = 1023; for (i = endptr; i >= cursor; i--) { input_line[i+1] = input_line[i]; } input_line[cursor++] = c; } InsChar(c); } static int IsInWord (c) char c; { register char *cp; register int is_word_char = 0; for (cp = WORD_CHARS; *cp; cp++) if (*cp == c) is_word_char = 1; return (is_word_char); } static int Count(to_where) /* count the number of spaces that many */ int to_where; /* characters take up on the screen */ { register int i; register int cntr = 0; for (i = 0; i < to_where; i++) { if (input_line[i] == '\t') { cntr += prompt_length; cntr = (((cntr/8)+1)*8); cntr -= prompt_length; } else if ((input_line[i] < ' ') || (input_line[i] == '\0177')) { cntr += 2; } else cntr++; } return (cntr); } static PrintChar(c, pos) char c; int pos; /* where the character is */ { register int count; register int i; c &= 0177; if (c == '\177') { outputchar('^'); c = '?'; } else if (c == '\t') { count = Count(pos) - Count(pos -1); for (i = 0; i < count; i++) { outputchar(' '); } return; } else if (c < ' ') { outputchar('^'); c |= 0100; } outputchar(c); } /* **************************************************************** */ ilpushback(string) char string[]; { register int i; int string_length; int matched; string_length = strlen(string); if (string_length > 1023) string_length = 1023; matching_length = 0; matched = 1; for (i = 0; i <= string_length; i++) { if ((input_line[i] == string[i]) && (matched)) { matching_length++; } else { matched = FALSE; input_line[i] = string[i]; } } endptr = string_length; if (endptr > 1023) endptr = 1023; cursor = endptr; pushed_input = TRUE; /* do_redisplay = TRUE; */ } ilredisplay() { do_redisplay = TRUE; } /* this next pair is done this way so that I don't get an infinate recursion bug with ilsetup. it is important that ilsetup is called first, so that the keys actually get bound. */ ilbindtokey (func, key) int func; char key; { if (has_been_setup == FALSE) { ilsetup(); } DoBindToKey (func, key); } /* force a reset_term */ ilreset_tty() { reset_term(); } static DoBindToKey (func, key) int func; char key; { if (key == -1) { /* trap unsetings */ return (-1); } if ((func < 0) || (func > 200)) { return(-1); } keybindings[key] = func; switch (func) { case TTY_DSUSP: old_ltchars.t_dsuspc = key; new_ltchars.t_dsuspc = key; break; case TTY_FLUSH_OUTPUT: old_ltchars.t_flushc = key; /* ^O */ new_ltchars.t_flushc = key; /* ^O */ break; case TTY_SIGINTR: old_tchars.t_intrc = key; /* ^C */ new_tchars.t_intrc = key; /* ^C */ break; case TTY_SIGQUIT: old_tchars.t_quitc = key; /* ^\ */ new_tchars.t_quitc = key; /* ^\ */ break; case TTY_SIGTSUSP: old_ltchars.t_suspc = key; /* ^Z */ new_ltchars.t_suspc = key; /* ^Z */ break; case TTY_START_OUTPUT: old_tchars.t_startc = key; /* ^Q */ new_tchars.t_startc = key; /* ^Q */ break; case TTY_STOP_OUTPUT: old_tchars.t_stopc = key; /* ^S */ new_tchars.t_stopc = key; /* ^S */ break; case REDISPLAY: old_ltchars.t_rprntc = key; /* ^R */ new_ltchars.t_rprntc = -1; /* ^R */ break; case SEND_EOF: old_tchars.t_eofc = key; /* ^_ */ new_tchars.t_eofc = -1; /* ^_ */ break; case QUOTE_NEXT: old_ltchars.t_lnextc = key; /* ^^ */ new_ltchars.t_lnextc = -1; /* ^^ */ break; case DELETE_WORD_BACKWARD: old_ltchars.t_werasc = key; /* ^W */ new_ltchars.t_werasc = -1; /* ^W */ break; case DELETE_CHAR_BACKWARD: old_term.sg_erase = key; new_term.sg_erase = -1; break; case KILL_TO_BEGINNING: old_term.sg_kill = key; new_term.sg_kill = -1; break; default: return; } } int ilgetbinding(key) char key; { return (keybindings[key]); } static int saved_ldisc; /* line discipline */ static struct sgttyb saved_term; /* old terminal modes + ERASE, KILL */ static struct tchars saved_tchars; /* INT, QUIT, XON, XOFF, EOF, BRK */ static struct ltchars saved_ltchars; /* SUSP, DSTOP, RPRNT, FLUSH, WERASE, LNEXT */ static long saved_modwd; static int tty_has_been_saved = 0; /* * Save and restore all of the terminal states */ ilsavetty () { ioctl (SHIN, TIOCGETD, &saved_ldisc); ioctl (SHIN, TIOCGETP, &saved_term); ioctl (SHIN, TIOCGETC, &saved_tchars); ioctl (SHIN, TIOCGLTC, &saved_ltchars); ioctl (SHIN, TIOCLGET, &saved_modwd); tty_has_been_saved = 1; } ilrestoretty () { if (tty_has_been_saved == 0) return; ioctl (SHIN, TIOCSETD, &saved_ldisc); ioctl (SHIN, TIOCSETN, &saved_term); ioctl (SHIN, TIOCSETC, &saved_tchars); ioctl (SHIN, TIOCSLTC, &saved_ltchars); ioctl (SHIN, TIOCLSET, &saved_modwd); } !Funky!Stuff! echo x - inputl.h cat > inputl.h << '!Funky!Stuff!' #ifdef MAKE_INPUTL #define TRUE 1 #define FALSE 0 #define NULL 0 #define BELL '\007' /* this next is for all characters that should be considered a word to the shell */ #define WORD_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*?_-." #include <sgtty.h> #endif #define AUTO_INSERT 0 #define BACKWARD_CHAR 1 #define BACKWARD_WORD 2 #define BEGINNING_OF_LINE 3 #define CLEAR_SCREEN 4 #define DELETE_CHAR_BACKWARD 5 #define DELETE_CHAR_FORWARD 6 #define DELETE_WORD_BACKWARD 7 #define DELETE_WORD_FORWARD 8 #define END_OF_LINE 9 #define SEND_EOF 10 #define FORWARD_CHAR 11 #define FORWARD_WORD 12 #define KILL_TO_BEGINNING 13 #define KILL_TO_END 14 #define NL_SEND_LINE 15 #define QUOTE_NEXT 16 #define REDISPLAY 17 #define SEND_LINE 18 #define START_OVER 19 #define TRANSPOSE_CHARS 20 #define YANK_KILLBUFFER 21 #define YANK_LAST_INPUT 22 #define NOP 23 #define ERR 24 #define TTY_DSUSP 101 #define TTY_FLUSH_OUTPUT 102 #define TTY_SIGINTR 103 #define TTY_SIGQUIT 104 #define TTY_SIGTSUSP 105 #define TTY_START_OUTPUT 106 #define TTY_STOP_OUTPUT 107 !Funky!Stuff!
paul@osu-dbs.UUCP (Paul Placeway) (04/13/84)
. The following code is my changes to Ken Greer's tcsh. I have added a visual mini-editor to the shell, and cleaned up the expansion routines some what. note that this is the 4.1 version. When we get 4.2 up I'll repost the new changes. Please send any changes back to me so that I can update our version. Note: this is part 3 of 5, you need all of the parts to make tcsh. Paul W. Placeway The Ohio State University IRCC (UUCP: cbosgd!osu-dbs!paul) (CSNet: paul@ohio-state) ================ cut here ================ : This is a shar archive. Extract with sh, not csh. echo x - TCSH.README cat > TCSH.README << '!Funky!Stuff!' ------------------------------------------------------------------ C-shell with tenex-style file name recognition (and more). I've included the full source of the changed files here. If you've made local changes to your C shell, use diff to see what I've done. If you fix any bugs or make improvements, please send them back to me! (Please note bug mentioned in manual tcsh.1!! - NB: that has been fixed --fjl) Ken Greer Hewlett-Packard Computer Research Center Palo Alto, Ca. 94301 ucbvax!hplabs!kg (on uucp) kg@hplabs (on CSNET) kg.hplabs@udel-relay (on ARPANET) (415) 857-8801 (on MA-BELL NET) The following files have been added or changed: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sh.c (changed): 1) The code to print out the shell prompt was moved into a procedure "printprompt" from the previous in-line code. The routine "tenex" (in tenex.c) calls it. 2) Added an "autologout" facility. No action at prompt level for one hour logs you out. Time controlled by shell variable "autologout" set to minutes. An unset or setting to 0 turns autologout off. -------------- sh.lex.c (changed): Added code to call "tenex" routine when reading from terminal. -------------- tenex.c (new): The file name recognition code. -------------- tcsh.1 (new): Man entry for changes made. -------------- makefile (changed): Changed to load with tenex.o. Output executable called "tcsh". ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NOTES: 1) To make autologout useful on dialups, add to getty at line 190: ioctl(0, TIOCHPCL, 0); /* Hang up on close */ (This should be there anyway to hangup up the modem on logout.) 2) If you run "tcsh" as a login shell, login won't know to set you up with the new tty driver. Here's my login.c fix with < = oldlogin, > = newlogin... 140c140 < if (!strcmp(pwd->pw_shell, "/bin/csh")) { --- > if (cshell (pwd->pw_shell)) { 364a365,379 > } > > /* > * return TRUE if the shell is the C shell. > * Algorithm: TRUE if "csh" appears anywhere in string. > */ > cshell (shell) > char *shell; > { > register char *p; > extern char *index (); > for (p = shell; p = index (p, 'c'); p++) > if (strncmp (p, "csh", 3) == 0) > return (TRUE); > return (FALSE); ------------------------------------------------------------------ !Funky!Stuff! echo x - READ_TOO cat > READ_TOO << '!Funky!Stuff!' Lepreau's notes: 1. Here's a minor change I made to kg's tenex.c. You may want to change the value to simething else, perhaps depending if you have auto-wrap on your terminals. Use the full 80 columns for alternatives display. Should probably do a getwidth ioctl and use that instead... 176c176 < columns = 78 / maxwidth; --- > columns = 80 / maxwidth; 2. makefile.orig is kg's original. makefile is the above plus some local Utah flags, minus a real SCCS, and set up to install tcsh as linked to csh. 3. The man page should mention the "autologout" shell variable. !Funky!Stuff! echo x - READ_THREE cat > READ_THREE << '!Funky!Stuff!' Placeway's notes: 1. An editor has been added to tcsh which is called by twenex(). The routines for packing things back into the tty driver have been removed since the editor does that stuff itself. 2. The makefile has been changed to make tcsh (not linked to csh) and put it in /usr/bin. /u0/osu is the local changes directory. Our man(1) command searches multiple paths, that's why tcsh.1 gets put there. The make copy command is used to save a working set of sources in our local directory. 3. I *think* that I have documented all the changes to tcsh in the manual pages. 4. The editor's handling of up/down the history list isn't perfect, because the orignal shells handling wasn't. *sigh* ---------------------------------------------------------------- If you make any changes or additions, please send them to me so that I can update my own version. Paul W. Placeway The Ohio State University IRCC (UUCP: cbosgd!osu-dbs!paul) (CSNet: paul@ohio-state) (Phone: (614) 422-0915) !Funky!Stuff! echo x - tcsh.1 cat > tcsh.1 << '!Funky!Stuff!' .TH TCSH 1 Ohio-State .SH NAME tcsh \- C shell with file name completion and command line editing .SH SYNOPSIS chsh username /bin/tcsh -- to get it .SH DESCRIPTION .I Tcsh is an enhanced version of the Berkeley UNIX C shell .I csh (1). It behaves exactly like the C shell, except for the added utilities of: .PP .in +6 .ti -3 1) Editing of the command line using control characters. .sp .ti -3 2) Interactive command, file name and user name completion. .sp .ti -3 3) File/Directory/User list in the middle of a typed command. .sp .ti -3 4) Lookup of command documentation in the middle of a typed command. .sp .ti -3 5) Visual step up/down through the history list. .sp .ti -3 6) Automatic logout after long amounts of idle time. .in -6 .PP .SH "FILE NAME COMPLETION" In typing file names as arguments to commands, it is no longer necessary to type a complete name, only a unique abbreviation is necessary. When you type an ESCAPE to .I tcsh it will complete the file name for you, echoing the full name on the terminal. If the file name prefix you typed matches no file name, the terminal bell is enunciated. The file name may be partially completed if the prefix matches several longer file names. If this is the case, the name is extended up to the ambiguous deviation, and the bell is enunciated. .PP .I Example .PP In the following example, assume the plus character ``+'' is where the user typed the ESCAPE key. Assume the current directory contained the files: .sp .nf DSC bin cmd lib memos DSC.NEW chaosnet cmtest mail netnews bench class dev mbox new .fi .sp The command: .sp > vi ch+ .sp would cause .I tcsh to complete the command with the name chaosnet. If instead, the user had typed: .sp > vi D+ .sp .I tcsh would have extended the name to DSC and enunciated the terminal bell, indicating partial completion. .PP File name completion works equally well when other directories are addressed. Additionally, .I tcsh understands the C shell tilde (~) convention for home directories. Thus, .sp > cd ~speech/data/fr+ .sp does what one might expect. This may also be used to expand login names only. Thus, .sp > cd ~sy+ .sp does a .I cd to the .I synthesis directory. .SH "COMMAND NAME RECOGNITION" Command name recognition and completion works in the same manner as file name recognition and completion above. The current value of the environment variable \fBPATH\fR is used in searching for the command. For example .sp > newa+ .sp might expand to .sp > newaliases .sp Also, .sp > new? .sp would list all commands (along PATH) that begin with "new". .SH "FILE/DIRECTORY LIST" At any point in typing a command, you may request "what files are available". Thus, when you have typed, perhaps: .sp > cd ~speech/data/fritz/ .sp you may wish to know what files or subdirectories exist (in ~speech/data/fritz), without, of course, aborting the command you are typing. Typing the character Question mark (?), will list the files available. The files are listed in multicolumn format, sorted column-wise . Directories and executable files are indicated with a trailing `/' and `*', respectively. Once printed, the command is re-echoed for you to complete. .PP Additionally, one may want to know which files match a prefix. If the user had typed: .sp > cd ~speech/data/fr? .sp all files and subdirectories whose prefix was ``fr'' would be printed. Notice that the example before was simply a degenerate case of this with a null trailing file name. (The null string is a prefix of all strings.) Notice also, that a trailing slash is required to pass to a new directory for both file name completion and listing. .PP The degenerate .sp > ~? .sp will print a full list of login names on the current system. .PP The behavior of the completion can be changed by setting the shell variable .I recexact. This makes an exact command be expanded rather than just ringing the bell. For example, assume the current directory has two subdirectorys called foo and food, then with .I recexact set the following could be done: .sp > cd fo+ .br to ... .br > cd foo+ .br to ... .br > cd foo/ .sp rather than beeping on the second escape. .SH EDITING The shell edits the command line using a (slightly changed) emacs format. It does so by setting the terminal to `CBREAK' mode and getting a single character at a time. The following is a list of which control characters do what. .PP .in +6 .ti -4 .sp ^@ (NUL) Prints a help file on the current command (usually a man page). This help file is found by searching the path list HPATH for files of the form foo.help, foo.1, foo.8, or foo.6 in that order (assuming that the current command is foo). The file is just printed, not paged in any way. This is because ^@ is meant to be used to look up short help files, not manual pages. .ti -4 .sp ^A Moves the cursor to the beginning of the line (after the prompt) .ti -4 .sp ^B Moves the cursor back one space. Beeps if it is already in column one. .ti -4 .sp ^C Sends a SIGINTR to the current process (from the tty driver) .ti -4 .sp ^D Deletes the character that is over the cursor. .ti -4 .sp ^E Moves the cursor to the end of the line. .ti -4 .sp ^F Moves the cursor forward one space. Beeps if at the end of the line. .ti -4 .sp ^G Erase entire line and start over. .ti -4 .sp ^H (BS) Yanks back the last command typed (ignoring empty lines) the same way that ^Y does for saved text. This is useful if you muff a command and want to correct it. Its also nice for re-doing the last command. .ti -4 .sp ^I (TAB) Inserts a tab character .ti -4 .sp ^J (LF) Moves the cursor to the end of the line, inserts a newline character, and sends the line. .ti -4 .sp ^K Delete all characters from the current cursor position to the end inclusive and save them in a special buffer to be called back later (see ^Y). .ti -4 .sp ^L Clear the screen and redisplay the line .ti -4 .sp ^M (RETURN) Same as ^J .ti -4 .sp ^N Move to the next following word. A word is a contigous string of one of the characters A-Z, a-z, 0-9, *, ?, \-, ., or _. .ti -4 .sp ^O Flush tty output until the next tty input request (tty driver). .ti -4 .sp ^P Same as ^N but go backwards. I.e. previous word. .ti -4 .sp ^Q Restart output. (see ^S) .ti -4 .sp ^R Redisplay the current line and move the cursor to it's original place. .ti -4 .sp ^S Stop the tty's output. .ti -4 .sp ^T Transpose previous two characters. .ti -4 .sp ^U Same as ^K but kill to the beginning of the line exclusive. .ti -4 .sp ^V Insert the next character regardless of it's meaning (quote it). .ti -4 .sp ^W Delete the previous word. .ti -4 .sp ^X Delete the next word (see ^W). .ti -4 .sp ^Y Yank the characters saved (by ^K or ^U) as if they were typed on the keyboard. .ti -4 .sp ^Z Send a SIGTSTP (stop) to the current process (tty driver). .ti -4 .sp ^[ (ESC) Try to complete the typed in command or file name (see ?). .ti -4 .sp ^\\ send a SIGQUIT (quit and dump core) to the current process (tty driver) .ti -4 .sp ^] Send a SIGTSTP when the program attempts to read the ^]. (tty driver) This is usually immediately because of cbreak mode, but the assignment is left so that cooked mode uses it. .ti -4 .sp ^_ Put an end of file mark at the end and send the line. (tty driver) This either exits a pushed shell or gives you a warning about how to log out. .ti -4 .sp ? (question mark) Show the possible choices for completing commands. It does this by putting a ? at the end of the line and sending it without echoing a newline. The completer then does it's stuff and sends the line back. .ti -4 .sp ^? (DELETE) Delete the character immediately to the left of the cursor. Beeps if the cursor is at the beginning of the line. .ti -4 .sp ALL OTHERS All other characters insert themselves into the line. .in -6 .PP There is a new shell command, .B bind, that allows the user to redefine what any key does. If given two arguments .B bind binds the function (first argument) to the given key (second argument). the key may be either the direct character or a carret-<letter> combination, which is converted to control-<letter>. If given one argument .B bind tells you what that key does. If you give bind the lone argument of 'defaults', it resets each key to its default value (see the above list). The folowing are legal functions: .PP .in +6 .nf function default key -------- ----------- auto_insert all printables backward_char ^B backward_word ^P beginning_of_line ^A clear_screen ^L delete_char_backward ^? delete_char_forward ^D delete_word_backward ^W delete_word_forward ^X end_of_line ^E send_eof ^_ forward_char ^F forward_word ^N helpme ^@ kill_to_beginning ^U kill_to_end ^K nl_send_line ^J, ^M quote_next ^V redisplay ^R complete ^[ list_options ? start_over ^G transpose_chars ^T yank_killbuffer ^Y yank_last_input ^H tty_dsusp ^] tty_flush_output ^O tty_sigintr ^C tty_sigquit ^\\ tty_sigtsusp ^Z tty_start_output ^Q tty_stop_output ^S .in -6 .fi .PP The folowing functions are also provided, but not bound by default. The history functions step up and down through the history list by placing the next or previous line in the editor line. The contents of the current line are saved, but editing changes do not effect any commands that are up the list. These functions are normaly bound to ^P and ^N (previous and next). .PP .in +6 .nf function -------- down_history up_history .in -6 .fi .PP If the bind command is given no arguments, all the current bindings are listed. .SH "OTHER THINGS" There is also a new shell variable called .I shlvl Which is equal to the number of shells which the user has pushed (where one is the login shell). This is done by maintaining the environment variable .I SHLVL and incrementing it whenever a new shell is started. .PP The automatic logout time is controled by the variable .I autologout who's value is the number of minutes of inactivity will be allowed before automaticly loging the user out. When that many minutes have been reached, the shell prints "autologout" and dies (without executing ~/.logout). .SH FYI This shell uses cbreak mode but takes typed-ahead characters anyway. You can still use .I stty(1) to set some of the modes of your terminal (but not bindings). .PP This shell will restore your tty to a sane mode if it appears to return from some command in raw, cbreak, or noecho mode. .SH FILES ~/.tcshrc -- TC-shell startup file .SH ENVORONMENT HPATH -- path to look for command documentation .br SHLVL -- the number of shell levels pushed (handled be the shell) .br TERM -- used to tell how to handle the terminal .SH "SHELL VARIABLES" shlvl, term -- see above .br recexact -- recognize exact matches even if they are ambigous .SH SEE ALSO csh (1), chsh (1) .SH BUGS Currently it has many bugs. .PP Startup of the shell can be very slow. .PP The editor will occasionally get confused as to what it thinks is on the screen if a tab character is deleted in the middle of a line. .PP Any other bugs should be sent to Paul Placeway (osu-dbs!paul or paul@ohio-state). .SH AUTHOR Paul Placeway, OSU IRCC wrote the editor and is supporting tcsh at OSU. .sp Ken Greer, HP Labs wrote the command completion. .sp Mike Ellis, Fairchild, added command name recognition/completion. !Funky!Stuff! echo x - makefile cat > makefile << '!Funky!Stuff!' # # makefile 4.1 10/9/80 # # C Shell with process control; VM/UNIX VAX Makefile # Bill Joy UC Berkeley; Jim Kulp IIASA, Austria # CFLAGS= -O -DTELL -DVMUNIX -Ddebug -DVFORK -I XSTR= /usr/ucb/xstr ED= -ed AS= -as RM= -rm CXREF= /usr/ucb/cxref CTAGS= /usr/ucb/ctags LIBES= -ljobs -ltermcap DESTDIR= /u1/paul SRCDEST= /u1/paul/src/tcsh/COPY DESTMAN= /u1/paul/man/man1 # strings.o must be last in OBJS since it can change when # previous files compile OBJS= sh.o sh.dol.o sh.err.o sh.exec.o sh.exp.o sh.func.o sh.glob.o \ sh.hist.o sh.lex.o sh.misc.o sh.parse.o sh.print.o sh.sem.o sh.set.o \ sh.proc.o sh.dir.o sh.time.o alloc.o sh.init.o printf.o \ tw.help.o tw.main.o tw.init.o tw.parse.o sh.nfunc.o dir14.o inputl.o \ tw.sort.o strings.o doprnt.o # for use in copying a good version to a safe place SRCS= sh.c sh.dol.c sh.err.c sh.exec.c sh.exp.c sh.func.c sh.glob.c \ sh.hist.c sh.lex.c sh.misc.c sh.parse.c sh.print.c sh.sem.c sh.set.c \ sh.proc.c sh.dir.c sh.time.c alloc.c sh.init.c printf.c sh.nfunc.c \ dir14.c doprnt.c dir.h sh.dir.h sh.h sh.local.h inputl.c \ sh.proc.h makefile READ_THREE READ_TOO TODO TCSH.README inputl.h \ tcsh.1 tw.help.c tw.main.c tw.init.c tw.parse.c tw.h tw.sort.c # Special massaging of C files for sharing of strings .c.o: ${CC} -E ${CFLAGS} $*.c | ${XSTR} -c - ${CC} -c ${CFLAGS} x.c mv x.o $*.o tcsh: ${OBJS} sh.local.h rm -f tcsh cc ${OBJS} -o tcsh ${LIBES} csh.prof: ${OBJS} sh.prof.o sh.local.h mcrt0.o rm -f csh.prof ld -X mcrt0.o ${OBJS} -o csh.prof ${LIBES} -lc sh.o.prof: cp sh.c sh.prof.c cc -c ${CFLAGS} -DPROF sh.prof.c .DEFAULT: ${SCCS} get $< # need an old doprnt, whose output we can trap doprnt.o: doprnt.c cc -E doprnt.c > doprnt.s as -o doprnt.o doprnt.s rm -f doprnt.s # strings.o and sh.init.o are specially processed to be shared strings.o: strings ${XSTR} ${CC} -c -R xs.c mv xs.o strings.o sh.init.o: ${CC} -E ${CFLAGS} sh.init.c | ${XSTR} -c - ${CC} ${CFLAGS} -c -R x.c mv x.o sh.init.o lint: lint ${CFLAGS} sh*.c inputl.c tw*.c print: @pr READ_ME @pr makefile makefile.* @(size -l a.out; size *.o) | pr -h SIZES @${CXREF} sh*.c | pr -h XREF @ls -l | pr @pr sh*.h [a-rt-z]*.h sh*.c alloc.c vprint: @pr -l84 READ_ME TODO @pr -l84 makefile makefile.* @(size -l a.out; size *.o) | pr -l84 -h SIZES @${CXREF} sh*.c | pr -l84 -h XREF @ls -l | pr -l84 @${CXREF} sh*.c | pr -l84 -h XREF @pr -l84 sh*.h [a-rt-z]*.h sh*.c alloc.c vgrind: @cp /dev/null index @for i in *.h; do vgrind -t -h "C Shell" $$i >/crp/bill/csh/$$i.t; done @for i in *.c; do vgrind -t -h "C Shell" $$i >/crp/bill/csh/$$i.t; done @vgrind -t -x -h Index index >/crp/bill/csh/index.t install: tcsh sh.local.h rm -f ${DESTDIR}/bin/tcsh cp tcsh ${DESTDIR}/bin/tcsh # cp tcsh.1 ${DESTMAN} clean: ${RM} -f a.out strings x.c xs.c tcsh ${RM} -f *.o sh.prof.c copy: ${SRCS} cp ${SRCS} ${SRCDEST} tags: /tmp ${CTAGS} sh*.c !Funky!Stuff!
paul@osu-dbs.UUCP (Paul Placeway) (04/13/84)
. The following code is my changes to Ken Greer's tcsh. I have added a visual mini-editor to the shell, and cleaned up the expansion routines some what. note that this is the 4.1 version. When we get 4.2 up I'll repost the new changes. Please send any changes back to me so that I can update our version. Note: this is part 4 of 5, you need all of the parts to make tcsh. Paul W. Placeway The Ohio State University IRCC (UUCP: cbosgd!osu-dbs!paul) (CSNet: paul@ohio-state) ================ cut here ================ : This is a shar archive. Extract with sh, not csh. echo x - tw.h cat > tw.h << '!Funky!Stuff!' #ifdef MAKE_TWENEX #include <sys/types.h> #include <sys/stat.h> #include <sgtty.h> #include "dir.h" #include <signal.h> #include <pwd.h> /* * Input lines are parsed into doubly linked circular * lists of words of the following form. */ struct wordent { char *word; struct wordent *prev; struct wordent *next; }; /* * History list * * Each history list entry contains an embedded wordlist * from the scanner, a number for the event, and a reference count * to aid in discarding old entries. * * Essentially "invisible" entries are put on the history list * when history substitution includes modifiers, and thrown away * at the next discarding since their event numbers are very negative. */ struct Hist { struct wordent Hlex; int Hnum; int Href; struct Hist *Hnext; } Histlist; /* Don't include stdio.h! Csh doesn't like it!! */ extern short SHIN, SHOUT; #define FREE_ITEMS(items)\ {\ sighold (SIGINT);\ free_items (items);\ items = NULL;\ sigrelse (SIGINT);\ } #define FREE_DIR(fd)\ {\ sighold (SIGINT);\ closedir (fd);\ fd = NULL;\ sigrelse (SIGINT);\ } #define TRUE 1 #define FALSE 0 #define ON 1 #define OFF 0 #define FILSIZ 512 /* Max reasonable file name length */ #define ESC '\033' #define equal(a, b) (strcmp(a, b) == 0) #define is_set(var) adrof(var) #define BUILTINS "/usr/local/lib/builtins/" /* fake builtin bin */ #define MAXITEMS 2048 #define SEARCHLIST "HPATH" /* Env. param for helpfile searchlist */ #define DEFAULTLIST ":/u0/osu/man/cat1:/u0/osu/man/cat8:/u0/osu/man/cat6:/usr/man/cat1:/usr/man/cat8:/usr/man/cat6:/u0/local/man/cat1:/u0/local/man/cat8:/u0/local/man/cat6" /* if no HPATH */ extern char PromptBuf[]; extern char *getenv (); typedef enum {LIST, RECOGNIZE, PRINT_HELP} COMMAND; #define beep() write (SHOUT, BELL, 1) char *getentry(); char GetAChar(); char *tilde(); char filetype(); #ifndef DONT_EXTERN extern char *BELL; extern char *command_list[]; /* the pre-digested list of commands for speed and general usefullness */ extern int numcommands; extern int commands_file; /* for a global file discriptor */ extern int key_bindings[]; /* the currently assigned keys */ extern int bindings_are_inited; /* for setup of bindings */ /* the first time */ extern int dirctr; /* -1 0 1 2 ... */ extern char dirflag[5]; /* ' nn\0' - dir #s - . 1 2 ... */ #endif #endif #define ERROR -1 #define NO_OP 0 #define UP_HIST 1 #define DOWN_HIST 2 #define COMPLETE 3 #define HELPME 4 #define LIST_CHOICES 5 !Funky!Stuff! echo x - tw.help.c cat > tw.help.c << '!Funky!Stuff!' #define MAKE_TWENEX /* flag to include definitions */ #include "tw.h" /* actually look up and print documentation on a file. Look down the path for an approiate file, then print it. Note that the printing is NOT PAGED. This is because the function is NOT ment to look at manual pages, it only does so if there is no .help file to look in. */ do_help (command) char *command; { char name[FILSIZ + 1]; char *cmd_p; int f; char curdir[128]; /* Current directory being looked at */ char *getenv(); register char *hpath; /* The environment parameter */ char *skipslist(); char full[128], buf[512]; /* full path name and buffer for read */ int len; /* length of read buffer */ /* copy the string to a safe place */ copyn (name, command, sizeof (name)); /* trim off the garbage that may be at the end */ for (cmd_p = name; *cmd_p != '\0'; cmd_p++) { if (*cmd_p == ' ' || *cmd_p == '\t') *cmd_p = '\0'; } /* if nothing left, return */ if (*name == '\0') return; /* got is, now "cat" the file based on the path $HPATH */ hpath = getenv(SEARCHLIST); if(hpath == (char *)0) hpath = DEFAULTLIST; while (1) { if(!*hpath) { printf("No help file for %s\n", name); break; } nextslist(hpath, curdir); hpath = skipslist(hpath); /* now make the full path name - try first /bar/foo.help, then /bar/foo.1, /bar/foo.8, then finally /bar/foo.6 . This is so that you don't spit a binary at the tty when $HPATH == $PATH. I know, I know, gotos are BOGUS */ copyn (full, curdir, sizeof (full)); catn (full, "/", sizeof (full)); catn (full, name, sizeof (full)); catn (full, ".help", sizeof (full)); f = open (full, 0); /* try to open for reading */ if (f != -1) goto cat_it; /* no file there */ copyn (full, curdir, sizeof (full)); catn (full, "/", sizeof (full)); catn (full, name, sizeof (full)); catn (full, ".1", sizeof (full)); f = open (full, 0); /* try to open for reading */ if (f != -1) goto cat_it; /* no file there */ copyn (full, curdir, sizeof (full)); catn (full, "/", sizeof (full)); catn (full, name, sizeof (full)); catn (full, ".8", sizeof (full)); f = open (full, 0); /* try to open for reading */ if (f != -1) goto cat_it; /* no file there */ copyn (full, curdir, sizeof (full)); catn (full, "/", sizeof (full)); catn (full, name, sizeof (full)); catn (full, ".6", sizeof (full)); f = open (full, 0); /* try to open for reading */ if (f == -1) continue; /* no file there */ cat_it: while ((len = read (f, buf, 512)) != 0) { /* the file is here */ write (SHOUT, buf, len); /* so cat it to the */ } /* terminal */ break; } } /* these next two are stolen from CMU's man(1) command for looking down * paths. they are prety straight forward. */ /* * nextslist takes a search list and copies the next path in it * to np. A null search list entry is expanded to ".". * If there are no entries in the search list, then np will point * to a null string. */ nextslist(sl, np) register char *sl; register char *np; { if(!*sl) *np = '\000'; else if(*sl == ':') { *np++ = '.'; *np = '\000'; } else { while(*sl && *sl != ':') *np++ = *sl++; *np = '\000'; } } /* * skipslist returns the pointer to the next entry in the search list. */ char * skipslist(sl) register char *sl; { while(*sl && *sl++ != ':'); return(sl); } !Funky!Stuff! echo x - tw.init.c cat > tw.init.c << '!Funky!Stuff!' #define MAKE_TWENEX /* flag to include definitions */ #include "tw.h" /* * Build the command name list (and print the results). This is a HACK until * I can get the rehash command to include its results in the command list. */ char *extract_dir_from_path(); int fcompare(); static int tw_been_inited = 0; /* to avoid double reading of commands file */ extern struct biltins { char *bname; int (*bfunct)(); short minargs, maxargs; } bfunc[]; extern struct varent { char **vec; /* Array of words which is the value */ char *name; /* Name of variable/alias */ struct varent *link; } aliases; tw_clear_comm_list() { register int i; /* printf ("Building the completion path..."); flush(); */ if (numcommands != 0) { /* printf("Freeing..."); */ for (i = 0; command_list[i]; i++) { if (command_list[i] == 0) { printf ("tried to free a null, i = %d\n", i); } else { free (command_list[i]); } command_list[i] = NULL; } numcommands = 0; } } tw_sort_comms () { register int i,j; /* printf ("sorting..."); flush(); */ /* sort the list. */ qsort (command_list, numcommands, sizeof (command_list[0]), fcompare); /* get rid of multiple entries */ for (i = 0; i < numcommands - 1; i++) { if (strcmp (command_list[i], command_list[i+1]) == 0) { if (command_list[i] == 0) { printf ("tried to free a null, i = %d, previous = %s\n", i, command_list [i-1]); } else { free (command_list[i]); } for (j = i; j < numcommands; j++) { command_list[j] = command_list[j+1]; } numcommands--; /* keep count right */ i--; /* to catch many versions */ } } /* print_by_column (looking_for_lognames ? NULL:tilded_dir, command_list, numcommands, 1); */ /* printf ("Done.\n"); */ flush(); } tw_add_comm_name (name) char *name; { register int length; if (numcommands >= MAXITEMS) { printf ("\nYikes!! Too many commands in PATH!!\n"); return; } length = strlen(name) + 1; if ((command_list[numcommands] = (char *)malloc (length)) == NULL) { printf ("\nYikes!! I ran out of memory!!!\n"); return; } copyn (command_list[numcommands], name, MAXNAMLEN); numcommands++; } tw_add_builtins() { register char *cp; register struct biltins *bptr; for (bptr = bfunc; cp = bptr->bname; bptr++) { tw_add_comm_name (cp); } } tw_add_aliases () { register struct varent *vp; vp = &aliases; for (vp = vp->link; vp != 0; vp = vp->link) { tw_add_comm_name(vp->name); } } !Funky!Stuff! echo x - tw.main.c cat > tw.main.c << '!Funky!Stuff!' /* tw.c -- input parser main routines. 84/04/02 Paul Placeway */ /* * This is a re-write of the routines by Ken Greer, Mike Ellis, and myself * for doing twenex (Tops-20 (tm)) style command and file recognition, * expansion, and listing, as well as other nice additions to my (inputl) * input line parser for C-shell and other things. This is styled after the * Tops-20 COMND jsys, but somewhat changed to what I think it should be in * Unix. */ #define MAKE_TWENEX /* flag to include definitions */ #define DONT_EXTERN /* don't do the extern ... stuff */ #include "tw.h" static char *BELL = "\07"; char *command_list[2048] = NULL; /* the pre-digested list of commands for speed and general usefullness */ int numcommands = 0; int commands_file = -1; /* for a global file discriptor */ int key_bindings[256]; /* the currently assigned keys */ int bindings_are_inited = FALSE; /* for setup of bindings */ int dirctr; /* -1 0 1 2 ... */ char dirflag[5]; /* ' nn\0' - dir #s - . 1 2 ... */ /* the main routine -- the inputline and size are the location to place the * expanded line into. */ twenex (inputline, inputline_size) char *inputline; /* pointer to a buffer */ int inputline_size; /* size of the buffer */ { register int numitems, num_read; int hist_num = 0; int hnumcntr; char linebuf[512]; struct Hist *hp; if (bindings_are_inited = FALSE) tw_init_bindings(); while (1) { register char *str_end, last_char, should_retype; char inputl(); COMMAND command; last_char = inputl (PromptBuf, inputline, inputline_size); /* PWP: use the editor */ num_read = strlen(inputline); if (last_char == -1) /* PWP: for end-of-file */ break; /* did i hear you screem HACK? */ last_char &= 0177; /* keep it to 7 bits */ if (last_char == '\n' || num_read == inputline_size) break; /* send the line */ switch (key_bindings[last_char]) { case UP_HIST: /* ^P -- previous hist line */ if (hist_num == 0) copyn(linebuf, inputline, 511); hp = Histlist.Hnext; if (hp == 0) { beep(); ilpushback (inputline); break; } hist_num++; for (hnumcntr = 1; hnumcntr < hist_num; hnumcntr++) { if ((hp->Hnext) == 0) { beep(); hist_num--; break; } hp = hp -> Hnext; } sprlex (inputline, &hp->Hlex); if (inputline[strlen(inputline)-1] == '\n') inputline[strlen(inputline)-1] = '\0'; printprompt (); ilpushback (inputline); ilredisplay (); break; case DOWN_HIST: /* ^N -- next hist line */ if (hist_num == 0) { ilpushback (inputline); beep(); break; } if (hist_num == 1) { copyn(inputline, linebuf, inputline_size); hist_num--; printprompt (); ilpushback (inputline); ilredisplay(); break; } hp = Histlist.Hnext; hist_num--; for (hnumcntr = 1; hnumcntr < hist_num; hnumcntr++) { if ((hp->Hnext) == 0) break; hp = hp -> Hnext; } sprlex (inputline, &hp->Hlex); if (inputline[strlen(inputline)-1] == '\n') inputline[strlen(inputline)-1] = '\0'; printprompt (); ilpushback (inputline); ilredisplay (); break; case COMPLETE: /* RECOGNIZE */ do_recognize (inputline, inputline_size, num_read); break; case HELPME: /* help */ print_help (inputline, inputline_size, num_read); break; case LIST_CHOICES: /* LIST */ list_choices (inputline, inputline_size, num_read); break; case NO_OP: /* just push it back */ ilpushback (inputline); break; case ERROR: default: beep(); /* complain */ ilpushback (inputline); break; } /* end case */ } /* end while (1) */ return (num_read); } /* handle the keybindings stuff like initing them, binding them, and seeing what is bound to a given key. */ /* bind function func to key key */ int tw_bind_to_key (func, key) int func; char key; { if (key == -1) { /* trap unsetings */ return (-1); } if ((func < 0) || (func > 10)) { return(-1); } if (bindings_are_inited = FALSE) tw_init_bindings(); key_bindings[key] = func; } /* return what function is bound to key key */ int tw_get_binding(key) char key; { return (key_bindings[key]); } /* initilize the bindings */ tw_init_bindings() { register int i; if (bindings_are_inited) return; for (i=0; i<128; i++) { /* assign all the keys */ key_bindings[i] = NO_OP; } key_bindings[020] = NO_OP; /* ^P */ key_bindings[016] = NO_OP; /* ^N */ key_bindings[033] = COMPLETE; /* ^[ (ESC) */ key_bindings[000] = HELPME; /* ^@ */ key_bindings[077] = LIST_CHOICES; /* ? */ bindings_are_inited = TRUE; } /* the top level of the recognize and list procedures. These are just to save a little work on the part of twenex(). */ /* do the recognize: take the inputline, expand the command name, if there is a white space character, hand the stuff to the parse expander */ do_recognize (inputline, inputline_size, num_read) char *inputline; int inputline_size; int num_read; { /* CharPtr = &inputline[num_read]; */ /* do it!! */ if (tenematch (inputline, inputline_size, num_read, RECOGNIZE) != 1) /* Beep = No match/ambiguous */ beep (); flush (); ilpushback (inputline); /* pushback for editor */ } /* run the list choices option: Same as above, but use a parse list options routine rather than an expander. Nothing additional is pushed back onto the line (just what was typed in). */ list_choices (inputline, inputline_size, num_read) char *inputline; int inputline_size; int num_read; { putchar ('\n'); /* CharPtr = &inputline[num_read]; */ /* do this early so that you can ^C it */ printprompt (); ilpushback (inputline); /* pushback for editor */ ilredisplay (); tenematch (inputline, inputline_size, num_read, LIST); /* do it!! */ flush (); } /* print help: Figure out what command is being entered, expand the name, then do a lookup on documentation of this command */ print_help (inputline, inputline_size, num_read) char *inputline; int inputline_size; int num_read; { putchar ('\n'); /* CharPtr = &inputline[num_read]; */ /* do this early so that you can ^C it */ printprompt (); ilpushback (inputline); /* pushback for editor */ ilredisplay (); tenematch (inputline, inputline_size, num_read, PRINT_HELP); /* do it!! */ flush (); } /* * Concatonate src onto tail of des. * Des is a string whose maximum length is count. * Always null terminate. */ catn (des, src, count) register char *des, *src; register count; { while (--count >= 0 && *des) des++; while (--count >= 0) if ((*des++ = *src++) == 0) return; *des = '\0'; } max (a, b) { if (a > b) return (a); return (b); } /* * like strncpy but always leave room for trailing \0 * and always null terminate. */ copyn (des, src, count) register char *des, *src; register count; { while (--count >= 0) if ((*des++ = *src++) == 0) return; *des = '\0'; } !Funky!Stuff! echo x - tw.parse.c cat > tw.parse.c << '!Funky!Stuff!' #define MAKE_TWENEX /* flag to include definitions */ #include "tw.h" /* do the expand or list on the command line -- SHOULD BE REPLACED */ int fcompare(); tenematch (inputline, inputline_size, num_read, command) char *inputline; /* match string prefix */ int inputline_size; /* max size of string */ int num_read; /* # actually in inputline */ COMMAND command; /* LIST or RECOGNIZE or PRINT_HELP */ { static char *delims = " '\"\t;&<>()|^%"; static char *cmd_delims = ";&(|`"; char word [FILSIZ + 1]; register char *str_end, *word_start, *cmd_start, *wp; char *cmd_st; int space_left; int is_a_cmd; /* UNIX command rather than filename */ int search_ret; /* what search returned for debugging */ str_end = &inputline[num_read]; /* * Find LAST occurence of a delimiter in the inputline. * The word start is one character past it. */ for (word_start = str_end; word_start > inputline; --word_start) if (index (delims, word_start[-1])) break; /* space backward looking for the beginning of this command */ for (cmd_st = str_end; cmd_st > inputline; --cmd_st) if (index (cmd_delims, cmd_st[-1])) break; /* step forward over leading spaces */ while (*cmd_st != '\0' && (*cmd_st == ' ' || *cmd_st == '\t')) cmd_st++; space_left = inputline_size - (word_start - inputline) - 1; is_a_cmd = starting_a_command (word_start, inputline); for (cmd_start = word_start, wp = word; cmd_start < str_end; *wp++ = *cmd_start++); *wp = 0; /* printf ("\ncmd_st:%s:\nword_start:%s:\n", cmd_st, word_start); */ /* for debugging */ if (command != PRINT_HELP) { search_ret = search (word, wp, command, space_left, is_a_cmd); copyn (word_start, word, space_left); /* put the stuf INTO the line */ return (search_ret); } else { do_help (cmd_st); return (1); } } /* * return true if check items initial chars in template * This differs from PWB imatch in that if check is null * it items anything */ static is_prefix (check, template) char *check, *template; { register char *check_char, *template_char; check_char = check; template_char = template; do if (*check_char == 0) return (TRUE); while (*check_char++ == *template_char++); return (FALSE); } /* return true if the command starting at wordstart is a command */ starting_a_command (wordstart, inputline) register char *wordstart, *inputline; { static char *cmdstart = ";&(|`", *cmdalive = " \t'\""; while (--wordstart >= inputline) { if (index (cmdstart, *wordstart)) break; if (!index (cmdalive, *wordstart)) return (FALSE); } if (wordstart > inputline && *wordstart == '&') /* Look for >& */ { while (wordstart > inputline && (*--wordstart == ' ' || *wordstart == '\t')); if (*wordstart == '>') return (FALSE); } return (TRUE); } /* * Object: extend what user typed up to an ambiguity. * Algorithm: * On first match, copy full entry (assume it'll be the only match) * On subsequent matches, shorten extended_name to the first * character mismatch between extended_name and entry. * If we shorten it back to the prefix length, stop searching. */ recognize (extended_name, entry, name_length, numitems) char *extended_name, *entry; { if (numitems == 1) /* 1st match */ copyn (extended_name, entry, MAXNAMLEN); else /* 2nd and subsequent matches */ { register char *x, *ent; register int len = 0; for (x = extended_name, ent = entry; *x && *x == *ent++; x++, len++); *x = '\0'; /* Shorten at 1st char diff */ if (len == name_length) /* Ambiguous to prefix? */ return (-1); /* So stop now and save time */ } return (0); } /* * Perform a RECOGNIZE or LIST command on string "word". */ static search (word, wp, command, max_word_length, looking_for_command) char *word, *wp; /* original end-of-word */ COMMAND command; { register numitems, name_length, /* Length of prefix (file name) */ looking_for_lognames; /* True if looking for login names */ int showpathn; /* True if we want path number */ struct stat dot_statb, /* Stat buffer for "." */ curdir_statb; /* Stat buffer for current directory */ int dot_scan, /* True if scanning "." */ dot_got; /* True if have scanned dot already */ char tilded_dir[FILSIZ + 1], /* dir after ~ expansion */ dir[FILSIZ + 1], /* /x/y/z/ part in /x/y/z/f */ name[MAXNAMLEN + 1], /* f part in /d/d/d/f */ extended_name[MAXNAMLEN+1], /* the recognized (extended) name */ *entry, /* single directory entry or logname */ *path; /* hacked PATH environment variable */ int next_command = 0; /* the next command to take out of */ /* the list of commands */ static DIR *dir_fd = NULL; static char **items = NULL; /* file names when doing a LIST */ if (items != NULL) FREE_ITEMS (items); numitems = 0; if (dir_fd != NULL) FREE_DIR (dir_fd); looking_for_lognames = (*word == '~') && (index (word, '/') == NULL); looking_for_command &= (*word != '~') && (index (word, '/') == NULL); dot_got = FALSE; stat (".", &dot_statb); if (looking_for_lognames) /* Looking for login names? */ { setpwent (); /* Open passwd file */ copyn (name, &word[1], MAXNAMLEN); /* name sans ~ */ } else if (!looking_for_command) { /* Open directory */ extract_dir_and_name (word, dir, name); if ((tilde (tilded_dir, dir) == 0) || /* expand ~user/... stuff */ ((dir_fd = opendir (*tilded_dir ? tilded_dir : ".")) == NULL)) { /* LOBOTOMIZE printf ("No directory file discriptor.\n"); */ return (0); } dot_scan = FALSE; } name_length = strlen (name); showpathn = looking_for_command && is_set("listpathnum"); while (1) { if (!looking_for_command) { if ((entry = getentry (dir_fd, looking_for_lognames)) == NULL) { break; } /* * Don't match . files on null prefix match */ if (name_length == 0 && entry[0] == '.' && !looking_for_lognames) continue; } else { if (numcommands == 0) { dohash (); } if ((entry = command_list[next_command++]) == NULL) break; copyn (name, word, MAXNAMLEN); /* so it can match things */ } if (!is_prefix (name, entry)) continue; if (command == LIST) /* LIST command */ { extern char *malloc (); register int length; if (numitems >= MAXITEMS) { printf ("\nYikes!! Too many %s!!\n", looking_for_lognames ? "names in password file":"files"); break; } if (items == NULL) { items = (char **) calloc (sizeof (items[1]), MAXITEMS + 1); if (items == NULL) break; } length = strlen(entry) + 1; if (showpathn) length += strlen(dirflag); if ((items[numitems] = malloc (length)) == NULL) { printf ("\nYikes!! I ran out of memory!!!\n"); break; } copyn (items[numitems], entry, MAXNAMLEN); if (showpathn) catn (items[numitems], dirflag, MAXNAMLEN); numitems++; } else { /* RECOGNIZE command */ if (adrof ("recexact")) { if (strcmp (name, entry) == 0) { /* EXACT match */ copyn (extended_name, entry, MAXNAMLEN); numitems = 1; /* fake into expanding */ break; } } if (recognize (extended_name, entry, name_length, ++numitems)) break; } } if (!looking_for_command) { if (looking_for_lognames) endpwent (); else FREE_DIR (dir_fd); } if (command == RECOGNIZE && numitems > 0) { if (looking_for_lognames) copyn (word, "~", 1); else if (looking_for_command) word[0] = 0; else copyn (word, dir, max_word_length); /* put back dir part */ catn (word, extended_name, max_word_length); /* add extended name */ if (numitems == 1) { if (looking_for_lognames) { /* add / */ catn (word, "/", max_word_length); } else { if (looking_for_command) { /* add space */ catn (word, " ", max_word_length); } else { if (filetype (tilded_dir, extended_name) == '/') { catn (word, "/", max_word_length); } else { catn (word, " ", max_word_length); } } } } return (numitems); /* at the end */ } if (command == LIST) { qsort (items, numitems, sizeof (items[1]), fcompare); print_by_column (looking_for_lognames ? NULL:tilded_dir, items, numitems, looking_for_command); if (items != NULL) FREE_ITEMS (items); } return (0); } /* stuff for general command line hacking */ /* * Strip next directory from path; return ptr to next unstripped directory. */ char *extract_dir_from_path (path, dir) char *path, dir[]; { register char *d = dir; while (*path && (*path == ' ' || *path == ':')) path++; while (*path && (*path != ' ' && *path != ':')) *(d++) = *(path++); while (*path && (*path == ' ' || *path == ':')) path++; ++dirctr; if (*dir == '.') strcpy (dirflag, " ."); else { dirflag[0] = ' '; if (dirctr <= 9) { dirflag[1] = '0' + dirctr; dirflag[2] = '\0'; } else { dirflag[1] = '0' + dirctr / 10; dirflag[2] = '0' + dirctr % 10; dirflag[3] = '\0'; } } *(d++) = '/'; *d = 0; return path; } static free_items (items) register char **items; { register int i; for (i = 0; items[i]; i++) free (items[i]); free (items); } /* * parse full path in file into 2 parts: directory and file names * Should leave final slash (/) at end of dir. */ static extract_dir_and_name (path, dir, name) char *path, *dir, *name; { extern char *rindex (); register char *p; p = rindex (path, '/'); if (p == NULL) { copyn (name, path, MAXNAMLEN); dir[0] = '\0'; } else { p++; copyn (name, p, MAXNAMLEN); copyn (dir, path, p - path); } } char * getentry (dir_fd, looking_for_lognames) DIR *dir_fd; { if (looking_for_lognames) /* Is it login names we want? */ { extern struct passwd *getpwent (); register struct passwd *pw; if ((pw = getpwent ()) == NULL) return (NULL); return (pw -> pw_name); } else /* It's a dir entry we want */ { register struct direct *dirp; if (dirp = readdir (dir_fd)) return (dirp -> d_name); return (NULL); } } /* * expand "old" file name with possible tilde usage * ~person/mumble * expands to * home_directory_of_person/mumble * into string "new". */ char * tilde (new, old) char *new, *old; { extern struct passwd *getpwuid (), *getpwnam (); register char *o, *p; register struct passwd *pw; static char person[40] = {0}; if (old[0] != '~') { strcpy (new, old); return (new); } for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++); *p = '\0'; if (person[0] == '\0') /* then use current uid */ pw = getpwuid (getuid ()); else pw = getpwnam (person); if (pw == NULL) return (NULL); strcpy (new, pw -> pw_dir); (void) strcat (new, o); return (new); } char filetype (dir, file) char *dir, *file; { if (dir) { char path[512]; struct stat statb; strcpy (path, dir); catn (path, file, sizeof path); if (stat (path, &statb) >= 0) { if (statb.st_mode & S_IFDIR) return ('/'); if (statb.st_mode & 0111) return ('*'); } } return (' '); } /* * Print sorted down columns */ print_by_column (dir, items, count, looking_for_command) register char *dir, *items[]; { register int i, rows, r, c, maxwidth = 0, columns; for (i = 0; i < count; i++) maxwidth = max (maxwidth, strlen (items[i])); maxwidth += looking_for_command ? 1:2; /* for the file tag and space */ columns = 80 / maxwidth; rows = (count + (columns - 1)) / columns; for (r = 0; r < rows; r++) { for (c = 0; c < columns; c++) { i = c * rows + r; if (i < count) { register int w; printf("%s", items[i]); w = strlen (items[i]); /* Print filename followed by '/' or '*' or ' ' */ if (!looking_for_command) putchar (filetype (dir, items[i])), w++; if (c < (columns - 1)) /* Not last column? */ for (; w < maxwidth; w++) putchar (' '); } } printf ("\n"); } } /* * For qsort() */ fcompare (file1, file2) char **file1, **file2; { return (strcmp (*file1, *file2)); } !Funky!Stuff! echo x - tw.sort.c cat > tw.sort.c << '!Funky!Stuff!' /* @(#)qsort.c 4.1 (Berkeley) 12/21/80 */ static int (*Qscmp)(); static int Qses; Qsort(a, n, es, fc) char *a; unsigned n; int es; int (*fc)(); { Qscmp = fc; Qses = es; Qs1(a, a+n*es); } static Qs1(a, l) char *a, *l; { register char *i, *j; register es; char **k; char *lp, *hp; int c; unsigned n; es = Qses; start: if((n=l-a) <= es) return; n = es * (n / (2*es)); hp = lp = a+n; i = a; j = l-es; for(;;) { if(i < lp) { if((c = (*Qscmp)(i, lp)) == 0) { Qsexc(i, lp -= es); continue; } if(c < 0) { i += es; continue; } } loop: if(j > hp) { if((c = (*Qscmp)(hp, j)) == 0) { Qsexc(hp += es, j); goto loop; } if(c > 0) { if(i == lp) { Qstexc(i, hp += es, j); i = lp += es; goto loop; } Qsexc(i, j); j -= es; i += es; continue; } j -= es; goto loop; } if(i == lp) { /* the two are the same, dump the copy */ /* **(lp+1) = '*'; */ /* for(k=lp+2; k<=hp;) **k++ = '\0'; */ /* for(k=lp+1; k<=hp;) **k++ = '\0'; */ if(lp-a >= l-hp) { Qs1(hp+es, l); l = lp; } else { Qs1(a, lp); a = hp+es; } goto start; } Qstexc(j, lp -= es, i); j = hp -= es; } } static Qsexc(i, j) char *i, *j; { register char *ri, *rj, c; int n; n = Qses; ri = i; rj = j; do { c = *ri; *ri++ = *rj; *rj++ = c; } while(--n); } static Qstexc(i, j, k) char *i, *j, *k; { register char *ri, *rj, *rk; int c; int n; n = Qses; ri = i; rj = j; rk = k; do { c = *ri; *ri++ = *rk; *rk++ = *rj; *rj++ = c; } while(--n); } !Funky!Stuff!
paul@osu-dbs.UUCP (Paul Placeway) (04/13/84)
. The following code is my changes to Ken Greer's tcsh. I have added a visual mini-editor to the shell, and cleaned up the expansion routines some what. note that this is the 4.1 version. When we get 4.2 up I'll repost the new changes. Please send any changes back to me so that I can update our version. Note: this is part 5 of 5, you need all of the parts to make tcsh. Paul W. Placeway The Ohio State University IRCC (UUCP: cbosgd!osu-dbs!paul) (CSNet: paul@ohio-state) ================ cut here ================ : This is a shar archive. Extract with sh, not csh. echo x - dir.h cat > dir.h << '!Funky!Stuff!' /* Copyright (c) 1982 Regents of the University of California */ /* @(#)ndir.h 4.4 3/30/82 */ /* * This sets the "page size" for directories. * Requirements are DEV_BSIZE <= DIRBLKSIZ <= MINBSIZE with * DIRBLKSIZ a power of two. * Dennis Ritchie feels that directory pages should be atomic * operations to the disk, so we use DEV_BSIZE. */ #define DIRBLKSIZ 512 /* * This limits the directory name length. Its main constraint * is that it appears twice in the user structure. (u. area) */ #define MAXNAMLEN 255 struct direct { u_long d_ino; short d_reclen; short d_namlen; char d_name[MAXNAMLEN + 1]; /* typically shorter */ }; struct _dirdesc { int dd_fd; long dd_loc; long dd_size; char dd_buf[DIRBLKSIZ]; }; /* * useful macros. */ #undef DIRSIZ #define DIRSIZ(dp) \ ((sizeof(struct direct) - MAXNAMLEN + (dp)->d_namlen + sizeof(ino_t) - 1) &\ ~(sizeof(ino_t) - 1)) typedef struct _dirdesc DIR; #ifndef NULL #define NULL 0 #endif /* * functions defined on directories */ extern DIR *opendir(); extern struct direct *readdir(); extern long telldir(); extern void seekdir(); #define rewinddir(dirp) seekdir((dirp), 0) extern void closedir(); !Funky!Stuff! echo x - dir14.c cat > dir14.c << '!Funky!Stuff!' /* Copyright (c) 1982 Regents of the University of California */ /* @(#)opendir.c 4.2.1.1 7/1/82 - HPL */ #include <sys/types.h> #include <sys/stat.h> #include <dir.h> /* * open a directory. */ DIR * opendir(name) char *name; { register DIR *dirp; register int fd; struct stat sbuf; if ((fd = open(name, 0)) == -1) return NULL; fstat(fd, &sbuf); if (((sbuf.st_mode & S_IFDIR) == 0) || ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL)) { close (fd); return NULL; } dirp->dd_fd = fd; dirp->dd_loc = 0; return dirp; } /* Copyright (c) 1982 Regents of the University of California */ /* @(#)closedir.c 4.2 3/10/82 */ /* * close a directory. */ void closedir(dirp) register DIR *dirp; { close(dirp->dd_fd); dirp->dd_fd = -1; dirp->dd_loc = 0; free(dirp); } /* Copyright (c) 1982 Regents of the University of California */ /* @(#)readdir.c 4.2 3/12/82 */ /* * read an old stlye directory entry and present it as a new one */ #define ODIRSIZ 14 struct olddirect { ino_t d_ino; char d_name[ODIRSIZ]; }; /* * get next entry in a directory. */ struct direct * readdir(dirp) register DIR *dirp; { register struct olddirect *dp; static struct direct dir; for (;;) { if (dirp->dd_loc == 0) { dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ); if (dirp->dd_size <= 0) return NULL; } if (dirp->dd_loc >= dirp->dd_size) { dirp->dd_loc = 0; continue; } dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc); dirp->dd_loc += sizeof(struct olddirect); if (dp->d_ino == 0) continue; dir.d_ino = dp->d_ino; strncpy(dir.d_name, dp->d_name, ODIRSIZ); dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */ dir.d_namlen = strlen(dir.d_name); dir.d_reclen = DIRSIZ(&dir); return (&dir); } } !Funky!Stuff! echo x - sh.nfunc.c cat > sh.nfunc.c << '!Funky!Stuff!' /* static char *sccsid = "@(#)sh.nfunc.c 1.0 84/01/31"; */ #include "sh.h" #include "inputl.h" /* for the function names */ #include "tw.h" #include <sys/ioctl.h> static char *seech (); /* * Tops-C shell */ /* do a bind command -- 'bind FUNC key' */ /* list structure... */ struct kfuncs { char *name; int ifunc; /* inputl function name */ int tfunc; /* twenex function name */ } fnames[] = { "auto_insert", AUTO_INSERT, NO_OP, "AUTO_INSERT", AUTO_INSERT, NO_OP, "backward_char", BACKWARD_CHAR, NO_OP, "BACKWARD_CHAR", BACKWARD_CHAR, NO_OP, "backward_word", BACKWARD_WORD, NO_OP, "BACKWARD_WORD", BACKWARD_WORD, NO_OP, "beginning_of_line", BEGINNING_OF_LINE, NO_OP, "BEGINNING_OF_LINE", BEGINNING_OF_LINE, NO_OP, "clear_screen", CLEAR_SCREEN, NO_OP, "CLEAR_SCREEN", CLEAR_SCREEN, NO_OP, "complete", SEND_LINE, COMPLETE, "COMPLETE", SEND_LINE, COMPLETE, "delete_char_backward", DELETE_CHAR_BACKWARD, NO_OP, "DELETE_CHAR_BACKWARD", DELETE_CHAR_BACKWARD, NO_OP, "delete_char_forward", DELETE_CHAR_FORWARD, NO_OP, "DELETE_CHAR_FORWARD", DELETE_CHAR_FORWARD, NO_OP, "delete_word_backward", DELETE_WORD_BACKWARD, NO_OP, "DELETE_WORD_BACKWARD", DELETE_WORD_BACKWARD, NO_OP, "delete_word_forward", DELETE_WORD_FORWARD, NO_OP, "DELETE_WORD_FORWARD", DELETE_WORD_FORWARD, NO_OP, "down_history", SEND_LINE, DOWN_HIST, "DOWN_HISTORY", SEND_LINE, DOWN_HIST, "end_of_line", END_OF_LINE, NO_OP, "END_OF_LINE", END_OF_LINE, NO_OP, "send_eof", SEND_EOF, NO_OP, "SEND_EOF", SEND_EOF, NO_OP, "forward_char", FORWARD_CHAR, NO_OP, "FORWARD_CHAR", FORWARD_CHAR, NO_OP, "forward_word", FORWARD_WORD, NO_OP, "FORWARD_WORD", FORWARD_WORD, NO_OP, "kill_to_beginning", KILL_TO_BEGINNING, NO_OP, "KILL_TO_BEGINNING", KILL_TO_BEGINNING, NO_OP, "kill_to_end", KILL_TO_END, NO_OP, "KILL_TO_END", KILL_TO_END, NO_OP, "nl_send_line", NL_SEND_LINE, NO_OP, "NL_SEND_LINE", NL_SEND_LINE, NO_OP, "quote_next", QUOTE_NEXT, NO_OP, "QUOTE_NEXT", QUOTE_NEXT, NO_OP, "redisplay", REDISPLAY, NO_OP, "REDISPLAY", REDISPLAY, NO_OP, "list_options", SEND_LINE, LIST_CHOICES, "LIST_OPTIONS", SEND_LINE, LIST_CHOICES, "send_line", SEND_LINE, ERROR, "SEND_LINE", SEND_LINE, ERROR, "start_over", START_OVER, NO_OP, "START_OVER", START_OVER, NO_OP, "transpose_chars", TRANSPOSE_CHARS, NO_OP, "TRANSPOSE_CHARS", TRANSPOSE_CHARS, NO_OP, "up_history", SEND_LINE, UP_HIST, "UP_HISTORY", SEND_LINE, UP_HIST, "yank_killbuffer", YANK_KILLBUFFER, NO_OP, "YANK_KILLBUFFER", YANK_KILLBUFFER, NO_OP, "yank_last_input", YANK_LAST_INPUT, NO_OP, "YANK_LAST_INPUT", YANK_LAST_INPUT, NO_OP, "tty_dsusp", TTY_DSUSP, NO_OP, "TTY_DSUSP", TTY_DSUSP, NO_OP, "tty_flush_output", TTY_FLUSH_OUTPUT, NO_OP, "TTY_FLUSH_OUTPUT", TTY_FLUSH_OUTPUT, NO_OP, "tty_sigintr", TTY_SIGINTR, NO_OP, "TTY_SIGINTR", TTY_SIGINTR, NO_OP, "tty_sigquit", TTY_SIGQUIT, NO_OP, "TTY_SIGQUIT", TTY_SIGQUIT, NO_OP, "tty_sigtsusp", TTY_SIGTSUSP, NO_OP, "TTY_SIGTSUSP", TTY_SIGTSUSP, NO_OP, "tty_start_output", TTY_START_OUTPUT, NO_OP, "TTY_START_OUTPUT", TTY_START_OUTPUT, NO_OP, "tty_stop_output", TTY_STOP_OUTPUT, NO_OP, "TTY_STOP_OUTPUT", TTY_STOP_OUTPUT, NO_OP, "helpme", SEND_LINE, HELPME, "HELPME", SEND_LINE, HELPME, 0, 0, 0 }; int dobind(v) register char **v; { register char c; register struct kfuncs *fp; register int i; register int isfound; /* assume at this point that i'm given 2 or 3 args - 'bind', the f-name, and the key; or 'bind' key to print the func for that key. */ if (v[1] && v[2]) { for (fp = fnames; fp->name; fp++) { if (strcmp(v[1], fp->name) == 0) { if ((v[2][0] == '^') && v[2][1]) { c = (v[2][1] == '?') ? 0177 : v[2][1] & 037; } else { c = v[2][0]; } tw_bind_to_key (fp->tfunc, c); /* bind for twenex()... */ ilbindtokey (fp->ifunc, c); /* actually bind the thing */ ilreset_tty (); return; } } bferr ("I don't know that function."); } else if (v[1]) { if (strcmp (v[1], "defaults") == 0) { /* reset keys to default */ for (i=0; i<128; i++) { /* assign all the keys */ ilbindtokey (AUTO_INSERT, i); tw_bind_to_key (NO_OP, i); } ilbindtokey (SEND_LINE, 000); tw_bind_to_key (HELPME, 000); ilbindtokey (BEGINNING_OF_LINE, 001); ilbindtokey (BACKWARD_CHAR, 002); ilbindtokey (TTY_SIGINTR, 003); ilbindtokey (DELETE_CHAR_FORWARD, 004); ilbindtokey (END_OF_LINE, 005); ilbindtokey (FORWARD_CHAR, 006); ilbindtokey (START_OVER, 007); ilbindtokey (KILL_TO_END, 013); ilbindtokey (SEND_EOF, 037); ilbindtokey (YANK_LAST_INPUT, 010); ilbindtokey (SEND_LINE, 077); tw_bind_to_key (LIST_CHOICES, 077); ilbindtokey (SEND_LINE, 033); tw_bind_to_key (COMPLETE, 033); ilbindtokey (NL_SEND_LINE, 012); ilbindtokey (NL_SEND_LINE, 015); ilbindtokey (FORWARD_WORD, 016); tw_bind_to_key (NO_OP, 016); ilbindtokey (TTY_FLUSH_OUTPUT, 017); ilbindtokey (BACKWARD_WORD, 020); tw_bind_to_key (NO_OP, 020); ilbindtokey (TTY_START_OUTPUT, 021); ilbindtokey (REDISPLAY, 022); ilbindtokey (TTY_STOP_OUTPUT, 023); ilbindtokey (TRANSPOSE_CHARS, 024); ilbindtokey (KILL_TO_BEGINNING, 025); ilbindtokey (QUOTE_NEXT, 026); ilbindtokey (CLEAR_SCREEN, 014); ilbindtokey (DELETE_WORD_BACKWARD, 027); ilbindtokey (DELETE_WORD_FORWARD, 030); ilbindtokey (YANK_KILLBUFFER, 031); ilbindtokey (TTY_SIGTSUSP, 032); ilbindtokey (TTY_SIGQUIT, 034); ilbindtokey (TTY_DSUSP, 035); ilbindtokey (DELETE_CHAR_BACKWARD, 0177); ilreset_tty(); } else { /* want to know what this key does */ if ((v[1][0] == '^') && v[1][1]) { c = (v[1][1] == '?') ? 0177 : v[1][1] & 037; } else { c = v[1][0]; } for (fp = fnames; fp->name; fp++) { if ((fp->ifunc == ilgetbinding(c)) && (fp->tfunc == tw_get_binding (c))) { printf ("%s is bound to %s.\n", seech(c), fp->name); return; } } printf ("%s isn't bound to anything.\n", seech(c)); printf ("inputl: %d, twenex: %d\n", ilgetbinding (c), tw_get_binding (c)); } } else { /* list all the bindings */ for (i = 0; i < 128; i++) { isfound = 0; for (fp = fnames; fp->name; fp++) { if ((fp->ifunc == ilgetbinding(i)) && (fp->tfunc == tw_get_binding (i))) { printf ("%s is bound to %s.\n", seech(i), fp->name); isfound++; break; } } if (isfound) continue; printf ("%s isn't bound to anything.\n", seech(i)); } } } char * sprlex(buf, sp0) struct wordent *sp0; char *buf; { register struct wordent *sp = sp0->next; buf[0] = '\0'; for (;;) { strcat(buf, sp->word); sp = sp->next; if (sp == sp0) break; strcat(buf, " "); } return(buf); } donhist(av) char **av; { char buf[512]; struct Hist *hp; for (hp = Histlist.Hnext; hp != 0; hp = hp->Hnext) { sprlex (buf, &hp->Hlex); printf ("hist: %s\n", buf); } } static char * seech (c) /* 'c' -> "c", '^C' -> "^" + "C" */ register char c; { static char tmp[3]; if (c >= ' ') { tmp[0] = c; tmp[1] = '\0'; return (tmp); } else { tmp[0] = '^'; if (c == '\177') { tmp[1] = '?'; } else { tmp[1] = '@' + (c & 037); } tmp[2] = '\0'; return (tmp); } } !Funky!Stuff!