From sources-request@genrad.UUCP Sun Jun 9 11:05:46 1985 Relay-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site seismo.UUCP Posting-Version: version B 2.10.2 9/3/84; site genrad.UUCP Path: seismo!harvard!talcott!panda!grkermi!genrad!sources-request From: sources-request@genrad.UUCP Newsgroups: mod.sources Subject: Bourne shell history + tilde + job control + more (Part 1 of 9) Message-ID: <879@genrad.UUCP> Date: 9 Jun 85 15:05:46 GMT Date-Received: 10 Jun 85 00:41:40 GMT Sender: john@genrad.UUCP Lines: 2661 Approved: john@genrad.UUCP From: Arnold Robbins This and the following eight postings consist of files and context diffs to add many long-desired features into the Bourne shell. The details of the new features are listed below in README.gt.sh. This is a list of what each article contains Part 1 -- README.gt.sh, new .c files needed for the shell, miscellany Part 2 -- Context diffs of C code for 4.2 BSD /bin/sh Part 3 -- Context diffs of C code for 4.2 BSD /bin/sh Part 4 -- Context diffs of sh.1 for 4.2 BSD /bin/sh Part 5 -- Context diffs of C code for System V Release 2 /bin/sh Part 6 -- Context diffs of C code for System V Release 2 /bin/sh Part 7 -- Context diffs of sh.1 for System V Release 2 /bin/sh Part 8 -- Context diffs of C code for BRL Unix /usr/5bin/sh Part 9 -- Context diffs of sh.1 for BRL Unix /usr/5bin/sh I am sorry that there are so many articles; I had to do it this way to insure that each one would be less that 64K in size. Arnold Robbins arnold@gatech.{CSNET, UUCP} ------------------ tear along perforations ------------ #!/bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #!/bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # README.gt.sh # Bugs # signal.c # ulimit.c # jobs.c # homedir.c # history.c # sample.shrc # aliases.sh # This archive created: Fri Jun 7 13:49:23 1985 # By: Arnold Robbins (Pr1mebusters!) export PATH; PATH=/bin:$PATH echo shar: extracting "'README.gt.sh'" '(7022 characters)' if test -f 'README.gt.sh' then echo shar: over-writing existing file "'README.gt.sh'" fi cat << \SHAR_EOF > 'README.gt.sh' README.gt.sh --- README file for Georgia Tech mods to the shell This and the following eight postings should be all you need to add the following features to the Bourne shell: 1. BSD style job control on Berkeley Systems. Code to outwit symbolic links for shells which have 'pwd' built in. Also code to print a resource usage summary after every command on BSD systems. These are courtesy of the folks at BRL, who gave me permission to post their code. 2. The ability to catch Control-D's, and force you to use "exit", also courtesy of BRL. This will work whether or not you are on a BSD system. 3. The <> I/O redirecter is now documented, and also works (courtesy of ihnp4!trwrb!lcc!brian's recent posting in net.unix-wizards). 4. The shell parameter $+ gives you the parent process id of the shell. It will track the value of the getppid() system call. 5. The shell will read in $HOME/.shrc on startup, if that file exists. This file is read *after* /etc/profile and $HOME/.profile (unlike the csh, which readc ~/.cshrc before ~/.login). 6. The ~ and ~person notation is understood, both for command line arguments, and in the PATH and CDPATH shell variables. 7. Special sequences in PS1 (the shell's prompt string) will print useful info at the prompt. Currently, you can get or all or some or none of: the time of day your current directory (if the shell has pwd is built in) your machine's hostname your login name the current 'event' number for the ... 8. History mechanism. The history mechanism is powerful, yet easy to use. Although different from the csh's, it is somewhat more general and orthogonal. The shell will save history across login sessions, automatically restoring on login, and saving on exit or on the exec builtin. On a Pyramid, the shell has some additional capabilities: 9. The 'att', 'ucb' and 'universe' commands are built in. 10. The $UNIVERSE shell variable tracks the current universe. 11. An additional sequence for the prompt to print the current universe. ***************** I am posting diffs for the versions of the Bourne shell listed below. Here are the instructions for setting up the makefile for each shell, depending on your target machine and OS. For all versions, you will need the files history.c and homedir.c; these have been added to the makefile, but will only be posted once. It will help if you have the 'patch' program. It was just reposted around the middle of May, 1985 (Version 1.3). If you don't have it, someone at your site or someone you know probably does. First, unshar this article. You wil have the following files: README.gt.sh # this file Bugs # some known bugs in what I've added signal.c # courtesy of BRL ulimit.c # courtesy of BRL jobs.c # courtesy of BRL homedir.c history.c sample.shrc aliases.sh Next, make a new directory, and copy the source for the version of the shell that you are going to modify, into it. Copy the *unformatted* man page for that shell into the directory also. Move history.c and homedir.c into the directory. Find which version of the shell you have, and follow the supplementary instructions below. 1. The Berkeley /bin/sh as distributed with 4.2, for 4.2BSD. Add the files signal.c and jobs.c to the directory. Get parts 2, 3 and 4, and run patch on them. Run make. On a Pyramid, follow the previous paragraph but don't run make yet. First, make sure you are in the 'ucb' universe. Remove all references to signal.c and signal.o from the makefile. Now run make. 2. The System V Release 2 shell as distributed from ATT (for the vax). On System V systems, the job stuff is conditionally compiled in. Get parts 5, 6, and 7, and run patch on them. Remove the -DJOBS and all references to signal.c, ulimit.c, jobs.c, and their .o files, from the makefile (sh.mk). Run make (make -f sh.mk). The sh.1 file will need editing to remove any reference to the 'J' flag, the 'I' flag, and to all the job control features. On BSD systems, copy signal.c, ulimit.c, and jobs.c into the directory. Get parts 5, 6, and 7, and run patch on them. Run make. (make -f sh.mk) On a Pyramid, copy ulimit.c, and jobs.c into the directory. Run patch. Remove signal.o and signal.c from the makefile, and the testing for u370 for xec.c. Be sure to be in the ucb universe, and then run make. (make -f sh.mk) 3. The System V Release 2 shell as modified at BRL, for BRL Unix. Get parts 8 and 9, and run patch on them. There are two ways to compile this version under BRL Unix. A. Use the standard BSD make and cc. In this case, all you will need is history.c and homedir.c. Run make. B. Use the System V emulation, /usr/5bin/make and /usr/5bin/cc. In this case, make sure your PATH is set properly. Remove the -DBSD from the makefile, and also any references to signal.c, and ulimit.c, and their .o files. Now run /usr/5bin/make. On a Pyramid, run patch. Edit the makefile to exclude signal.c and signal.o. Be sure to be in the ucb universe, and then run make. ********** Aliases.sh is a bunch of useful shell functions --- only of value for one or the other of the System V Release 2 Versions of the shell. Sample.shrc is a sample .shrc file. In particular, it gives you some compatibility with the Korn shell. It sets PPID=$+, and if the ENV environment variable is set to a file name, it will source that file (that is how the ksh does ~/.shrc). If you want job control to be turned on automatically (on a BSD system), add the line set -J to /etc/profile (or /usr/5lib/profile, depending). This will turn job control on for login shells. *************************************** I have tested the shells on the following systems: Vax (BRL Unix) | Pyramid | 3B2 (S5R2) | 3B20A (S5R2) BSD shell x x S5R2 shell x x x x BRL/S5R2 shell x x x As should be clear from the above table, I have only had access to four different kinds of machines. If you are running on some other kind of hardware, and/or another flavor of Unix (V7, Xenix, Perkin Elmer, whatever), and you successfully add these mods to the shell, please let me know. Also send me any diff listings you may have had to generate. I am particularly interested to know if it will still fit on a PDP-11. *************************************** Please don't send me any flames to the effect "You should use the C-shell". Here is a case where I can have my cake and eat it too, and this should be a big win for people who only have System V and don't have the ksh. I hope that these modifications to the shell help to meet a need out there in the real world. If you find any bugs, please let me know. Arnold Robbins CSNET: arnold@gatech ARPA: arnold%gatech.csnet@csnet-relay.arpa UUCP: { akgua, allegra, hplabs, ihnp4, seismo, ut-sally }!gatech!arnold School of Information and Computer Science Georgia Institute of Technology 225 North Avenue, N.W. Atlanta, Georgia 30332 (404) 894-3658 SHAR_EOF echo shar: extracting "'Bugs'" '(2013 characters)' if test -f 'Bugs' then echo shar: over-writing existing file "'Bugs'" fi cat << \SHAR_EOF > 'Bugs' Bugs -- known problems in the shell. The "suspend" built-in command is *very* naive. E.g. cranking up a subshell from vi, and then suspending it, will leave you sort of in limbo. A control-Z will then suspend the vi. Then a 'fg' command foregrounds the stopped sub-shell. Control-D'ing the subshell puts you back in vi. If the shell is being run from a terminal, and you interrupt it in the middle of doing a here document (cat << FOO ..., interrupt before the FOO), then if history was turned on, you will be left with history turned off. Use set +H to turn it back on. I do not have access to a PDP-11, so there will probably be problems trying to move this stuff to a small address space machine. Let me know what you encounter, and I will encorporate any diffs that people send back to me. A recent posting in net.micro.att indicated that the Unix PC's window manager uses $HOME/.history to save things. This is also the default for where this history mechanism keeps things -- change the value of HISTFILE in your .profile to be something different, and export it, if you're on a Unix PC. **************** There are probably bugs in the code I have added to the shell. I think I have caught everything, but I can't guarantee. If you discover any problems, please let me know, so that I can track them down and fix them. I regard this as a "first iteration." In other words, I will not be suprised if there are bugs. I am counting on the net to be friendly enough to let me know about any that may be discovered. I am also open to suggestions for other fixes or additions to the shell. As things come in to me, I will incorporate what I can, and hopefully post a new set of revisions. Meanwhile, enjoy! Arnold Robbins CSNET: arnold@gatech ARPA: arnold%gatech.csnet@csnet-relay.arpa UUCP: { akgua, allegra, hplabs, ihnp4, seismo, ut-sally }!gatech!arnold School of Information and Computer Science Georgia Institute of Technology 225 North Avenue, N.W. Atlanta, Georgia 30332 (404) 894-3658 SHAR_EOF echo shar: extracting "'signal.c'" '(3896 characters)' if test -f 'signal.c' then echo shar: over-writing existing file "'signal.c'" fi cat << \SHAR_EOF > 'signal.c' /* signal -- old system call emulation for 4.2BSD (VAX version) (adapted from BRL UNIX System V emulation for 4.2BSD) last edit: 25-Aug-1984 D A Gwyn NOTE: Although this module is VAX-specific, it should be possible to adapt it to other fairly clean implementations of 4.2BSD. The difficulty lies in avoiding the automatic restart of certain system calls when the signal handler returns. I use here a trick first described by Donn Seeley of UCSD Chem. Dept. */ #include #include #include extern int sigvec(); extern int sigsetmask(); extern etext; extern int errno; static int (*handler[NSIG])() = /* "current handler" memory */ { BADSIG /* initially, unknown state */ }; static int inited = 0; /* for initializing above */ static int catchsig(); static int ret_eintr(); int (* signal( sig, func ) /* returns previous handler */ )() register int sig; /* signal affected */ register int (*func)(); /* new handler */ { register int (*retval)(); /* previous handler value */ struct sigvec oldsv; /* previous state */ struct sigvec newsv; /* state being set */ if ( func >= (int (*)())&etext ) /* "lint" hates this */ { errno = EFAULT; return BADSIG; /* error */ } /* cancel pending signals */ newsv.sv_handler = SIG_IGN; newsv.sv_mask = newsv.sv_onstack = 0; if ( sigvec( sig, &newsv, &oldsv ) != 0 ) return BADSIG; /* error */ /* C language provides no good way to initialize handler[] */ if ( !inited ) /* once only */ { register int i; for ( i = 1; i < NSIG; ++i ) handler[i] = BADSIG; /* initialize */ ++inited; } /* the first time for this sig, get state from the system */ if ( (retval = handler[sig-1]) == BADSIG ) retval = oldsv.sv_handler; handler[sig-1] = func; /* keep track of state */ if ( func == SIG_DFL ) newsv.sv_handler = SIG_DFL; else if ( func != SIG_IGN ) newsv.sv_handler = catchsig; /* actual sig catcher */ if ( func != SIG_IGN /* sig already being ignored */ && sigvec( sig, &newsv, (struct sigvec *)0 ) != 0 ) return BADSIG; /* error */ return retval; /* previous handler */ } /* # bytes to skip at the beginning of C ret_eintr() function code: */ #define OFFSET 2 /* for VAX .word reg_mask */ /* PC will be pointing at a syscall if it is to be restarted: */ typedef unsigned char opcode; /* one byte long */ #define SYSCALL ((opcode)0xBC) /* VAX CHMK instruction */ #define IMMEDIATE ((opcode)0x8F) /* VAX immediate addressing */ /*ARGSUSED*/ static int catchsig( sig, code, scp ) /* signal interceptor */ register int sig; /* signal number */ int code; /* code for SIGILL, SIGFPE */ register struct sigcontext *scp; /* -> interrupted context */ { register int (*uhandler)(); /* user handler */ register opcode *pc; /* for snooping instructions */ struct sigvec newsv; /* state being set */ /* at this point, sig is blocked */ uhandler = handler[sig - 1]; /* most UNIXes usually want the state reset to SIG_DFL */ if ( sig != SIGILL && sig != SIGTRAP ) { handler[sig-1] = newsv.sv_handler = SIG_DFL; newsv.sv_mask = newsv.sv_onstack = 0; (void)sigvec( sig, &newsv, (struct sigvec *)0 ); } (void)sigsetmask( scp->sc_mask ); /* restore old mask */ /* at this point, sig is not blocked, usually have SIG_DFL; a longjmp may safely be taken by the user signal handler */ (void)(*uhandler)( sig ); /* user signal handler */ /* must now avoid restarting certain system calls */ pc = (opcode *)scp->sc_pc; if ( *pc++ == SYSCALL && (*pc == SYS_read || *pc == SYS_write || *pc == SYS_ioctl || *pc++ == IMMEDIATE && (*pc == SYS_wait || *pc == SYS_readv || *pc == SYS_writev) ) ) scp->sc_pc = (int)ret_eintr + OFFSET; /* return here restores interrupted context */ } static int ret_eintr() /* substitute for system call */ { errno = EINTR; return -1; } SHAR_EOF echo shar: extracting "'ulimit.c'" '(855 characters)' if test -f 'ulimit.c' then echo shar: over-writing existing file "'ulimit.c'" fi cat << \SHAR_EOF > 'ulimit.c' /* ulimit -- system call emulation for Bourne shell on 4.2BSD last edit: 22-Aug-1983 D A Gwyn */ #include extern int getrlimit(), setrlimit(); extern int errno; long ulimit( cmd, newlimit ) int cmd; /* subcommand */ long newlimit; /* desired new limit */ { struct { long rlim_cur; long rlim_max; } limit; /* data being gotten/set */ switch ( cmd ) { case 1: /* get file size limit */ if ( getrlimit( 1, &limit ) != 0 ) return -1L; /* errno is already set */ return limit.rlim_max / 512L; case 2: /* set file size limit */ limit.rlim_cur = limit.rlim_max = newlimit * 512L; return setrlimit( 1, &limit ); case 3: /* get maximum break value */ if ( getrlimit( 2, &limit ) != 0 ) return -1L; /* errno is already set */ return limit.rlim_max; default: errno = EINVAL; return -1L; } } SHAR_EOF echo shar: extracting "'jobs.c'" '(11717 characters)' if test -f 'jobs.c' then echo shar: over-writing existing file "'jobs.c'" fi cat << \SHAR_EOF > 'jobs.c' /* * JOBS.C -- job control for Bourne shell * * created by Ron Natalie, BRL * slight changes by Doug Gwyn * some more slight changes by Arnold Robbins (mainly for the BSD /bin/sh) */ #include "defs.h" #include "sym.h" #ifndef TAB /* very original, early /bin/sh */ #include #define comptr(x) ((COMPTR) x) #define lstptr(x) ((LSTPTR) x) #define forkptr(x) ((FORKPTR) x) #define parptr(x) ((PARPTR) x) #define forptr(x) ((FORPTR) x) #define whptr(x) ((WHPTR) x) #define ifptr(x) ((IFPTR) x) #define swptr(x) ((SWPTR) x) #endif #if BSD || (JOBS && ! BRL) /* native /bin/sh */ #include #else /* /usr/5bin/sh */ #include #define ioctl _ioctl #define killpg _killpg #define setpgrp _setpgrp #define TIOCSETD _IOW( 't', 1, int ) #define TIOCSPGRP _IOW( 't', 118, int ) #define NTTYDISC 2 #endif #define NJCH 30 #define JCOMSIZE 50 static struct j_child { int j_pgid; int j_status; int j_info; char j_com[JCOMSIZE]; int j_jobnum; } j_children[NJCH]; static int j_number = 1; static int j_current = 0; #define JEMPTY 0 #define JALIVE 1 #define JSTOP 2 #define JBG 3 BOOL j_top_level = TRUE; int j_default_pg = 0; int j_original_pg = 0; static int j_last_pgrp = 0; static int j_do(), j_getnumber(), j_stuff(); static void j_backoff(), j_print_ent(), j_really_reset_pg(), j_setcommand(); j_child_post(p, bg, pin, t) int p; int bg; register int pin; struct trenod ***t; { register struct j_child *j = j_children; if((flags & jobflg) == 0) return; if(!j_top_level) return; if(!pin) { setpgrp(p, p); j_last_pgrp = p; if(!bg) { ioctl(1, TIOCSPGRP, &p); setpgrp(0, p); } } else setpgrp(p, j_last_pgrp); for(j=j_children; j < &j_children[NJCH]; j++) { if(pin && j->j_pgid == j_last_pgrp) { j_setcommand(j, t); return; } if(!pin &&j->j_status == JEMPTY) { j->j_com[0] = 0; j->j_status = bg ? JBG : JALIVE; j->j_pgid = p; j_setcommand(j, t); if(bg) { post(p); j->j_jobnum = j_getnumber(); j_print_ent(j); } else j->j_jobnum = 0; return; } } prn(p); prs(cjpostr); /* DAG -- made strings sharable */ } j_child_clear(p) register int p; { register struct j_child *j = j_children; if(p == 0 || p == -1) return; for(; j < &j_children[NJCH]; j++) if(j->j_status == JALIVE && j->j_pgid == p) { j->j_status = JEMPTY; if(j->j_jobnum && j->j_jobnum == j_current) j_backoff(); break; } } j_child_stop(p, sig) register int p; int sig; { register struct j_child *j = j_children; for(; j < &j_children[NJCH]; j++) if((j->j_status == JALIVE || j->j_status == JBG) && j->j_pgid == p) { j->j_status = JSTOP; j->j_info = sig; if(j->j_jobnum == 0) j->j_jobnum = j_getnumber(); j_current = j->j_jobnum; prc(NL); j_print_ent(j); fault(SIGSTOP); break; } } j_child_die(p) register int p; { register struct j_child *j = j_children; if(p == 0 || p == -1) return; for(; j < &j_children[NJCH]; j++) if( j->j_status != JEMPTY && j->j_pgid == p) { j->j_status = JEMPTY; if(j->j_jobnum && j->j_jobnum == j_current) j_backoff(); break; } } j_print() { register struct j_child *j = j_children; if((flags & jobflg) == 0) { prs(jcoffstr); /* DAG */ return; } await(-2, 1); for(; j < &j_children[NJCH]; j++) j_print_ent(j); } static void j_print_ent(j) register struct j_child *j; { if(j->j_status == JEMPTY) return; if(j->j_jobnum == 0) { prs(jpanstr); /* DAG */ prn(j->j_pgid); prc(NL); } prc('['); prn(j->j_jobnum); prs(rsqbrk); /* DAG */ if(j_current == j->j_jobnum) prs(execpmsg); /* DAG */ else prs(spspstr); /* DAG */ prn(j->j_pgid); prc(' '); switch(j->j_status) { case JALIVE: prs(fgdstr); /* DAG */ break; case JSTOP: prs(stpdstr); /* DAG */ switch(j->j_info) { case SIGTSTP: prs(lotspstr); /* DAG */ break; case SIGSTOP: prs(psgpstr); /* DAG */ break; case SIGTTIN: prs(ptinstr); /* DAG */ break; case SIGTTOU: prs(ptoustr); /* DAG */ break; } break; case JBG: prs(bgdstr); /* DAG */ break; } prc(' '); prs(j->j_com); prc(NL); } j_resume(cp, bg) char *cp; BOOL bg; { register struct j_child *j = j_children; int p; if((flags & jobflg) == 0) { prs(jcoffstr); /* DAG */ return; } if(cp) { p = atoi(cp); if(p == 0) { prs(jinvstr); /* DAG */ return; } } else p = 0; await(-2, 1); if(p == 0 && j_current == 0) { prs(ncjstr); /* DAG */ return; } for(; j < &j_children[NJCH]; j++) if(j->j_status != JEMPTY) if( (p != 0 && j->j_pgid == p) || (p == 0 && j->j_jobnum == j_current) ) { p = j->j_pgid; if(!bg) { ioctl(1, TIOCSPGRP, &p); setpgrp(0, p); } j->j_status = bg ? JBG : JALIVE; j_print_ent(j); if(killpg(p, SIGCONT) == -1) { j->j_status = JEMPTY; break; } if(bg) post(p); else await(p, 0); j_reset_pg(); return; } prn(p); prs(nstpstr); /* DAG */ } char * j_macro() { static char digbuf[40]; register char *c; register int i; register struct j_child *j = j_children; c = digbuf; *c++ = '%'; for(;;) { *c = readc(); if( c==(digbuf+1) && *c == '%') { i = j_current; break; } if(!digchar(*c)) { peekc = *c | MARK; *c = 0; i = stoi(digbuf+1); break; } c++; } if(i != 0) for(; j < &j_children[NJCH]; j++) if(j->j_status != JEMPTY && j->j_jobnum == i) { itos(j->j_pgid); movstr(numbuf, digbuf); /* DAG */ break; } return digbuf; } j_reset_pg() { if((flags & jobflg) == 0) return; if(j_top_level) { ioctl(0, TIOCSPGRP, &j_default_pg); setpgrp(0, j_default_pg); } } static void j_really_reset_pg() { ioctl(0, TIOCSPGRP, &j_original_pg); setpgrp(0, j_original_pg); } #include "ctype.h" extern BOOL trapflg[]; j_init() { static int ldisc = NTTYDISC; /* BSD ioctl brain damage */ if(flags & jobflg) return; j_reset_pg(); trapflg[SIGTTIN] = SIGMOD | 1; trapflg[SIGTTOU] = SIGMOD | 1; trapflg[SIGTSTP] = SIGMOD | 1; trapflg[SIGSTOP] = SIGMOD | 1; ignsig(SIGTSTP); ignsig(SIGSTOP); /* Just to make sure */ (void)ioctl( 0, TIOCSETD, &ldisc ); /* DAG -- require "new tty" handler */ /* flags |= jobflg; */ } BOOL j_finish(force) BOOL force; { register struct j_child *j = j_children; if((flags & jobflg) == 0) return FALSE; await(-2, 1); for(; j < &j_children[NJCH]; j++) if(j->j_status == JSTOP ) if(force) { killpg(j->j_pgid, SIGHUP); killpg(j->j_pgid, SIGCONT); } else { prs(tasjstr); /* DAG */ return TRUE; } if(force) { await(-2, 1); return FALSE; } trapflg[SIGTTIN] = SIGMOD; trapflg[SIGTTOU] = SIGMOD; trapflg[SIGTSTP] = SIGMOD; trapflg[SIGSTOP] = SIGMOD; flags &= ~jobflg; j_really_reset_pg(); return FALSE; } static int j_numbers = 0; static int j_getnumber() { register struct j_child *j = j_children; for(; j < &j_children[NJCH]; j++) if(j->j_status != JEMPTY && j->j_jobnum) return j_numbers++; j_numbers = 2; return 1; } static void j_backoff() { register struct j_child *j = j_children; j_current = 0; for(; j < &j_children[NJCH]; j++) if(j->j_status != JEMPTY && j->j_jobnum) if(j->j_jobnum > j_current) j_current = j->j_jobnum; } static int jcleft; static char *jcp; static void j_setcommand(j, t) register struct j_child *j; struct trenod *t; { jcleft = strlen(j->j_com); jcp = j->j_com + jcleft; jcleft = JCOMSIZE - 1 - jcleft; if(j->j_com[0] == '\0' || !j_stuff(pipestr)) /* DAG */ j_do(t); } static int j_do_chain(a) register struct argnod *a; { while(a) { if(j_stuff(a->argval)) return 1; a = a->argnxt; if(a) j_stuff(spcstr); /* DAG */ } return 0; } #define IOGET 0 static int j_do_redir(t) register struct ionod *t; { register int iof; /* DAG -- added for speed */ register int i; while(t) { if(t->ioname) { if(j_stuff(spcstr)) /* DAG */ return 1; iof = t->iofile; i = iof & IOUFD; if( ((iof&IOPUT) && (i != 1)) || (((iof&IOPUT)==0) && (i!= 0)) ) { itos(i); if(j_stuff(numbuf)) return 1; } switch(iof & (IODOC|IOPUT|IOMOV|IOAPP|IORDW)) { case IOGET: if(j_stuff(rdinstr)) /* DAG */ return 1; break; case IOPUT: if(j_stuff(readmsg)) /* DAG */ return 1; break; case IOAPP|IOPUT: if(j_stuff(appdstr)) /* DAG */ return 1; break; case IODOC: if(j_stuff(inlnstr)) /* DAG */ return 1; break; case IOMOV|IOPUT: if(j_stuff(toastr)) /* DAG */ return 1; break; case IOMOV|IOGET: if(j_stuff(fromastr)) /* DAG */ return 1; break; case IORDW: if(j_stuff(rdwstr)) /* ADR */ return 1; break; } if(j_stuff(t->ioname)) return 1; } t = t->ionxt; } return 0; } static int j_do(t) register struct trenod *t; { int type; if (t == (struct trenod *)0) /* DAG -- added safety check */ return 0; type = t->tretyp & COMMSK; switch(type) { #ifdef TFND /* ADR --- don't put this stuff in the plain BSD /bin/sh */ case TFND: /* added by DAG for System V Release 2 shell */ return j_stuff(fndptr(t)->fndnam) || j_stuff(sfnstr) /* DAG */ || j_do(fndptr(t)->fndval) || j_stuff(efnstr); /* DAG */ #endif case TCOM: if(comptr(t)->comset) { if(j_do_chain(comptr(t)->comset) || j_stuff(spcstr)) return 1; } return j_do_chain(comptr(t)->comarg) || j_do_redir(comptr(t)->comio); case TLST: case TAND: case TORF: case TFIL: /* DAG -- merged */ if(j_do(lstptr(t)->lstlef)) return 1; switch(type) { case TLST: if(j_stuff(semspstr)) /* DAG */ return 1; break; case TAND: if(j_stuff(andstr)) /* DAG */ return 1; break; case TORF: if(j_stuff(orstr)) /* DAG */ return 1; break; case TFIL: if(j_stuff(pipestr)) /* DAG */ return 1; break; } return j_do(lstptr(t)->lstrit); case TFORK: return j_do(forkptr(t)->forktre) || j_do_redir(forkptr(t)->forkio) || (forkptr(t)->forktyp & FAMP) && j_stuff(amperstr); /* DAG */ case TPAR: return j_stuff(lpnstr) /* DAG */ || j_do(parptr(t)->partre) || j_stuff(rpnstr); /* DAG */ case TFOR: case TWH: case TUN: { struct trenod *c; switch(type) { case TFOR: if(j_stuff(forstr) /* DAG */ || j_stuff(forptr(t)->fornam)) return 1; if(forptr(t)->forlst) { if(j_stuff(insstr) /* DAG */ || j_do_chain(forptr(t)->forlst->comarg)) return 1; } c = forptr(t)->fortre; break; case TWH: if(j_stuff(whilestr) /* DAG */ || j_do(whptr(t)->whtre)) return 1; c = whptr(t)->dotre; break; case TUN: if(j_stuff(untilstr) /* DAG */ || j_do(whptr(t)->whtre)) return 1; c = whptr(t)->dotre; break; } return j_stuff(sdostr) /* DAG */ || j_do(c) || j_stuff(sdonstr); /* DAG */ } case TIF: if(j_stuff(ifstr) /* DAG */ || j_do(ifptr(t)->iftre) || j_stuff(sthnstr) /* DAG */ || j_do(ifptr(t)->thtre)) return 1; if(ifptr(t)->eltre) { if(j_stuff(selsstr) /* DAG */ || j_do(ifptr(t)->eltre)) return 1; } return j_stuff(sfistr); /* DAG -- bug fix (was "; done") */ case TSW: return j_stuff(casestr) /* DAG */ || j_stuff(swptr(t)->swarg) || j_stuff(iesacstr); /* DAG */ default: /* printf("sh bug: j_do--unknown type %d\n", type); */ return 0; } } static int j_stuff(f) char *f; { register int i; register int runover; i = strlen(f); runover = i > jcleft; if(runover) i = jcleft; strncpy(jcp, f, i); jcleft -= i; jcp += i; *jcp = 0; if(runover) { jcp[-1] = '.'; jcp[-2] = '.'; jcp[-3] = '.'; } return runover; } SHAR_EOF echo shar: extracting "'homedir.c'" '(3036 characters)' if test -f 'homedir.c' then echo shar: over-writing existing file "'homedir.c'" fi cat << \SHAR_EOF > 'homedir.c' /* * homedir.c * * find a person's login directory, for use by the shell * also find the current user's login name. * * Arnold Robbins */ #include "defs.h" /* validtilde --- indicate whether or not a ~ is valid */ int validtilde (start, argp) register char *start, *argp; { return ( start == argp - 1 || /* ~ at beginning of argument */ argp[-2] == '=' || /* ~ after an assignment */ (*start == '-' && argp - 3 == start) /* in middle of an option */ /* CSH does not do that one */ ); } /* homedir --- return the person's login directory */ char *homedir (person) register char *person; { register int count, i, j, fd; static char dir[150]; char buf[300], name[100], rest[100]; if (person[0] == '\0') /* just a plain ~ */ return (homenod.namval); else if (person[0] == '/') /* e.g. ~/bin */ { /* sprintf (dir, "%s%s", homenod.namval, person); */ movstr (movstr (homenod.namval, dir), person); return (dir); } if ((fd = open ("/etc/passwd", 0)) < 0) return (nullstr); /* * this stuff is to handle the ~person/bin sort of thing * for catpath() */ movstr (person, name); *rest = '\0'; for (i = 0; person[i]; i++) if (person[i] == '/') { movstr (& person[i], rest); name[i] = '\0'; break; } while ((count = read (fd, buf, sizeof(buf))) > 0) { for (i = 0; i < count; i++) if (buf[i] == '\n') { i++; lseek (fd, (long) (- (count - i)), 1); break; } buf[i] = '\0'; for (j = 0; name[j] && buf[j] == name[j]; j++) ; if (buf[j] == ':' && name[j] == '\0') break; /* found it */ } if (count == 0) { close (fd); return (nullstr); } j--; for (i = 1; i <= 5; i++) { for (; buf[j] != ':'; j++) ; j++; } for (i = 0; buf[j] != ':'; i++, j++) dir[i] = buf[j]; if (rest[0]) for (j = 0; rest[j]; j++) dir[i++] = rest[j]; dir[i] = '\0'; close (fd); return (dir); } /* username --- return the user's login name */ /* * this routine returns the first user name in /etc/passwd that matches the * real uid. This could be a problem on some systems, but we don't want to * call getlogin(), since it uses stdio, and the shell does not. */ char *username () { register int count, i, j, fd; static char logname[50]; static int foundname = FALSE; char buf[300]; if (foundname) return (logname); if ((fd = open ("/etc/passwd", 0)) < 0) return (nullstr); itos (getuid()); while ((count = read (fd, buf, sizeof(buf))) > 0) { for (i = 0; i < count; i++) if (buf[i] == '\n') { i++; lseek (fd, (long) (- (count - i)), 1); break; } buf[i] = '\0'; for (j = 0, i = 1; i <= 2; i++) { for (; buf[j] != ':'; j++) ; /* skip name && passwd */ j++; } for (i = 0; numbuf[i] && buf[j] == numbuf[i]; i++, j++) ; if (buf[j] == ':' && numbuf[i] == '\0') break; /* found it */ } if (count == 0) { close (fd); return (nullstr); } for (i = 0; buf[i] != ':'; i++) logname[i] = buf[i]; logname[i] = '\0'; foundname = TRUE; close (fd); return (logname); } SHAR_EOF echo shar: extracting "'history.c'" '(23486 characters)' if test -f 'history.c' then echo shar: over-writing existing file "'history.c'" fi cat << \SHAR_EOF > 'history.c' /* history.c --- interacterive history mechanism for the Bourne shell */ /* * Original design by Jeff Lee for the Software Tools Subsystem, * This implementation by Arnold Robbins, based on Jeff's, but * a little bit more capable. */ #include "defs.h" /* defines HISTSIZE */ #include "sym.h" #define MAXHIST 256 /* max no. saved commands */ #define MAXLINE 257 #define BIGBUF (MAXLINE * 2) #define HISTCHAR '!' /* history flag character */ #define HISTLOOK '?' /* history global search command */ #define HISTARG '`' /* history argument character */ #define HISTSUB '^' /* history substitution character */ #define YES (1) #define NO (0) #ifndef TAB /* earlier version of the shell */ #define TAB '\t' #endif static char Histbuf[HISTSIZE]; /* queue holding actual history */ static int Histptr[MAXHIST]; /* queue of pointers into buffer */ static int Hbuffirst = 0; /* First pointer into Histbuf */ static int Hbuflast = 0; /* Last pointer into Histbuf */ static int Hptrfirst = 0; /* First pointer into Histptr */ static int Hptrlast = 0; /* Last pointer into Histptr */ static int Histline = 0; /* no. of cmd pointed to by Histptr[Hptrlast] */ static char h_badopt[] = " unrecognized history option"; static char badarg[] = " illegal argument history"; static char nohist[] = " no history exists, yet"; static char h_illegal[] = " illegal history construct"; static char bufover[] = " history buffer overflow"; static char bigtok[] = " history token too large"; static char internal[] = " history internal error"; static char badtoken[] = " illegal history token"; static char h_notfound[] = " history item not found"; static char bigexp[] = " history expansion too big"; extern int histsub (); /* do a history substitution */ static void histinit (); /* reinitialize history mechanism */ static int histexp (); /* do a history expansion */ static int histque (); /* save a command in the buffers */ static void histfree (); /* free up some buffer storage */ static int histfind (); /* find a history command */ static int histlook (); /* get a previous command */ static int histget (); /* get a string from the buffers */ static void histarg (); /* get individual arguments */ extern int histrest (); /* restore saved history */ extern int histsave (); /* save current history */ static int Bquote = 0; /* count grave accents */ static int Dquote = 0; /* count single quotes */ static int Squote = 0; /* count double quotes */ #define errmsg(x, s) { prs(x); prc(COLON); prs(s); newline(); return (FALSE); } #define repeat do /* repeat ... until is easier to read */ #define until(cond) while (!(cond)) /* histsub --- perform a history substitution */ int histsub (in, out, outsize) char *in, *out; int outsize; { if ((flags&prompt) == 0 || in == 0 || *in == '\0') return (TRUE); /* no history, pretend all ok */ return (histexp (in, out, outsize) && histque (out)); } /* histexp --- perform history expansion on a command line */ static int histexp (in, out, outsize) char *in, *out; int outsize; { int i; int istart, ilen, ostart; char buf[MAXLINE], result[BIGBUF]; auto int bangseen = NO; if (in[0] == NL || in[0] == '\0') return (FALSE); istart = ostart = ilen = 0; while (in[istart] && in[istart] != HISTCHAR) { if (ostart >= outsize) errmsg (in, bigexp); switch (in[istart]) { case ESCAPE: out[ostart++] = in[istart++]; if (in[istart] == HISTCHAR) { bangseen = YES; if (Squote) out[ostart++] = in[istart++]; else out[ostart - 1] = in[istart++]; /* no quotes, nuke \ */ continue; } break; case '`': if (Dquote == 0 && Squote == 0) Bquote = 1 - Bquote; break; case '\'': if (Bquote == 0 && Dquote == 0) Squote = 1 - Squote; break; case '"': if (Bquote == 0 && Squote == 0) Dquote = 1 - Dquote; break; } if (ostart >= outsize) errmsg (in, bigexp); out[ostart++] = in[istart++]; if (Squote && in[istart] == HISTCHAR) if (ostart >= outsize) { errmsg (in, bigexp); } else out[ostart++] = in[istart++]; } if (in[istart] == '\0') { out[ostart] = '\0'; if (bangseen) expanded = YES; /* see comment below */ return (TRUE); /* no history to do */ } expanded = NO; /* this is a global flag */ while (histfind (in, &istart, &ilen)) /* we found something to do */ { if (ilen >= MAXLINE) errmsg (&in[istart], bigtok); /* save the history part */ strncpy (buf, & in[istart], ilen); buf[ilen] = '\0'; istart += ilen; if (buf[ilen-1] == HISTCHAR) buf[--ilen] = '\0'; /* actually make the substitution */ if (! histlook (buf, result)) return (FALSE); /* put it into generated line */ i = length (result) - 2; if (result[i] == NL) result[i] = '\0'; if (ostart + i + 1 >= outsize) errmsg (&in[istart], bigexp); movstr (result, & out[ostart]); ostart += length (result) - 1; expanded = YES; while (in[istart] && in[istart] != HISTCHAR) { if (ostart >= outsize) errmsg (&in[istart], bigexp); switch (in[istart]) { case ESCAPE: out[ostart++] = in[istart++]; if (in[istart] == HISTCHAR) { bangseen = YES; if (Squote) out[ostart++] = in[istart++]; else out[ostart - 1] = in[istart++]; /* no quotes, nuke \ */ continue; } break; case '`': if (Dquote == 0 && Squote == 0) Bquote = 1 - Bquote; break; case '\'': if (Bquote == 0 && Dquote == 0) Squote = 1 - Squote; break; case '"': if (Bquote == 0 && Squote == 0) Dquote = 1 - Dquote; break; } if (ostart >= outsize) errmsg (&in[istart], bigexp); out[ostart++] = in[istart++]; if (Squote && in[istart] == HISTCHAR) if (ostart >= outsize) { errmsg (&in[istart], bigexp); } else out[ostart++] = in[istart++]; } } out[ostart] = '\0'; if (expanded) prs (out); /* should contain newline */ else if (bangseen) expanded = YES; /* * This is a KLUDGE, so that escaped !s work; * it depends on knowledge of how readb() in word.c * works, i.e., if expanded, use the generated buffer. * This way, only expanded is needed as a global variable. */ return (TRUE); } /* histque --- place the given command in the history queue */ static int histque (command) char *command; { int c; char *p; static int Inaquote = FALSE; /* in a quote across commands */ for (; *command && (*command == SP || *command == TAB); command++) ; /* skip leading white space */ if (*command == NL && *(command+1) == '\0') return (TRUE); /* don't queue empty commands */ /* or increment event_count */ if (Inaquote) { /* clobber trailing \0 */ if (Hbuffirst == 0) Hbuffirst = HISTSIZE - 1; else Hbuffirst--; event_count--; } Histptr[Hptrfirst] = Hbuffirst; if (! Inaquote) Hptrfirst = (Hptrfirst + 1) % MAXHIST; if (Hptrfirst == Hptrlast) histfree (); p = command; c = *p++; while (c != '\0' && Hptrfirst != Hptrlast) { repeat { Histbuf[Hbuffirst] = c; c = *p++; Hbuffirst = (Hbuffirst + 1) % HISTSIZE; } until (c == '\0' || Hbuffirst == Hbuflast); if (Hbuffirst == Hbuflast) histfree (); } if (Hptrfirst != Hptrlast) { Histbuf[Hbuffirst] = '\0'; Hbuffirst = (Hbuffirst + 1) % HISTSIZE; if (Hbuffirst == Hbuflast) histfree (); } Inaquote = (Bquote || Dquote || Squote); if (Hptrfirst == Hptrlast) { histinit (); errmsg (nullstr, bufover); /* errmsg returns FALSE */ } event_count++; return (TRUE); } /* histfree --- free the next queue pointer */ static void histfree () { Hptrlast = (Hptrlast + 1) % MAXHIST; Hbuflast = Histptr[Hptrlast]; Histline++; } /* histfind --- find the start and length of a history pattern */ static int histfind (command, start, len) char *command; int *start, *len; { char *p, c; int subseen; p = command + *start; c = *p++; *len = 0; if (c == NL || c == '\0') return (FALSE); /* skip leading non-history */ while (c && c != HISTCHAR) { if (c == ESCAPE) { c = *p++; *start += 1; } if (c != '\0') { c = *p++; *start += 1; } } if (c == NL || c == '\0') return (FALSE); *len = 1; c = *p++; if (c == HISTLOOK) /* !?...? */ { *len += 1; c = *p++; while (c && c != HISTLOOK && c != NL) { if (c == ESCAPE) { c = *p++; *len += 1; } if (c != '\0') { c = *p++; *len += 1; } } if (c == HISTLOOK) { c = *p++; *len += 1; } } else if (digit (c) || c == '-') /* ! */ { if (c == '-') { c = *p++; *len += 1; if (! digit(c)) errmsg (command + *start, h_illegal); } while (digit (c)) { c = *p++; *len += 1; } } else /* ! */ while (c && c != HISTARG && c != HISTSUB && c != SP && c != TAB && c != NL && c != HISTCHAR) { if (c == ESCAPE) { c = *p++; *len += 1; } if (c != '\0') { c = *p++; *len += 1; } } if (c == HISTARG) { *len += 1; c = *p++; while (c && digit (c)) { *len += 1; c = *p++; } if (c == '-') { *len += 1; c = *p++; } if (c == '$') { *len += 1; c = *p++; } else { while (c && digit (c)) { *len += 1; c = *p++; } } } while (c == HISTSUB) { *len += 1; subseen = 0; c = *p++; while (subseen < 2 && c != NL && c != '\0') { if (c == ESCAPE) { c = *p++; *len += 1; } if (c != '\0') { c = *p++; *len += 1; } if (c == HISTSUB) subseen++; } if (c == HISTSUB) { *len += 1; c = *p++; if (c == 'g' || c == 'G') { *len += 1; c = *p++; } } } if (c == HISTCHAR) *len += 1; return (TRUE); } /* histlook --- lookup the value of a history string */ static int histlook (str, sub) char *str, *sub; { char c; char *save, *p, *sp; char buf[BIGBUF], rep[BIGBUF]; char new[BIGBUF]; int i, j, val, si, flag, len, last; static int ctoi(); save = sub; /* * first attempt to find which command on which we are to operate * * the entire hstory format is as follows * * ! [ | | ??] [` [- []]] {^^^ [g]} */ si = 0; if (str[si] == HISTCHAR) si++; switch (str[si]) { case '\0': /* ! */ case NL: case HISTARG: /* on these, retrive previous line, then break */ case HISTSUB: if (Hptrfirst == Hptrlast) errmsg (nullstr, nohist); val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1; if (! histget (val, sub)) errmsg (nullstr, internal); break; case '-': case '0': /* ! */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': val = ctoi (str, &si) - 1; /* for 0-based indexing */ if (! histget(val, sub)) errmsg (str, h_notfound); break; case HISTLOOK: /* ?? */ i = 0; si++; while (str[si] && str[si] != HISTLOOK) { if (str[si] == ESCAPE) si++; if (str[si]) buf[i++] = str[si++]; } buf [i] = '\0'; if (str[si] == HISTLOOK) si++; if (buf[i-1] == NL) buf[--i] = '\0'; flag = FALSE; val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1; *sub = '\0'; while (histget (val, sub)) { p = sub; c = *p++; while (c) { i = 0; while (c != '\0' && buf[i] != '\0' && c != buf[i]) { /* skip non matching */ c = *p++; if (*p == '\0') break; } sp = p; while (c && buf[i] && c == buf[i]) { /* possibly matching */ c = *p++; i++; } if (buf[i] == '\0') { /* did match */ flag = TRUE; goto out; } p = sp; c = *p++; } val--; /* search further back, next time around */ *sub = '\0'; } out: if (flag == FALSE) errmsg (str, h_notfound); break; default: /* ! */ i = 0; while (str[si] && str[si] != HISTARG && str[si] != HISTSUB) { if (str[si] == ESCAPE) si++; if (str[si]) buf[i++] = str[si++]; } buf[i] = '\0'; flag = FALSE; val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1; while (histget (val, sub)) { p = sub; c = *p++; while (c == SP || c == TAB) c = *p++; i = 0; while (buf[i] && buf[i] == c) { c = *p++; i++; } if (buf[i] == '\0') { flag = TRUE; /* found it */ break; /* while */ } val--; } if (flag == FALSE) errmsg (str, h_notfound); break; } /* end switch */ j = length (sub) - 2; if (sub[j] == NL) sub[j] = '\0'; /* * ! [ | | ? ?] has now been parsed and the command * line has been placed in "sub". Now see if the next character is a * legal following character */ if (str[si] && str[si] != HISTARG && str[si] != HISTSUB && str[si] != NL) errmsg (str, badtoken); /* if there is no more to the history string, we are done */ if (str[si] == NL || str[si] == '\0') return (TRUE); /* * now check for possible argument substitution. This section parses * [` ] and turns "sub" into the appropriate argument */ if (str[si] == HISTARG) /* `- */ { si++; if (! digit(str[si]) && str[si] != '-' && str[si] != '$') errmsg (str, badarg); /* determine the last argument */ p = sub; last = -1; /* count arguments, last will be val of $ */ histarg (p, &len); while (len > 0) { last++; p += len; histarg (p, &len); } if (str[si] == '-') /* default to arg 1 */ val = 1; else if (digit(str[si])) val = min (ctoi(str, &si), last + 1); else { /* $ */ val = last; if (str[si] != '$') { errmsg (str, internal); } else si++; } p = sub; for (i = val; i > 0; i--) /* delete preceding arguments */ { histarg (p, & len); p += len; } /* p points to beginning of first wanted arg */ /* remove leading blanks */ c = *p++; while (c == SP || c == TAB) c = *p++; sub = p - 1; if (str[si] == '-') { si++; if (digit(str[si])) val = min (ctoi (str, &si), last) - val + 1; else { val = last - val + 1; if (str[si] != '\0') si++; } p = sub; histarg (p, & len); while (val > 0 && len > 0) { val--; p += len; histarg (p, &len); } *p = '\0'; } else { histarg (sub, & len); sub [len] = '\0'; } } /* move everything to beginning of buffer */ if (save != sub) { movstr (sub, save); sub = save; } /* * check that the remaining characters represent * legal following characters */ if (str[si] && str[si] != HISTSUB && str[si] != NL) errmsg (str, badtoken); /* check for no substitutions and return if we are done */ if (str[si] && str[si] != HISTSUB) return (TRUE); /* keep performing substitutions until there are no more */ while (str[si] == HISTSUB) { i = 0; si++; flag = FALSE; /* buf is what to look for */ while (str[si] && str[si] != HISTSUB) { if (str[si] == ESCAPE) si++; if (str[si]) buf[i++] = str[si++]; } buf[i] = '\0'; i = 0; if (str[si]) si++; /* rep is replacement */ while (str[si] && str[si] != HISTSUB) { if (str[si] == ESCAPE) si++; if (str[si]) rep[i++] = str[si++]; } rep[i] = '\0'; if (str[si] == HISTSUB) si++; if (str[si] == 'g' || str[si] == 'G') { flag = TRUE; si++; } j = 0; /* j indexes new */ p = sub; c = *p++; sp = p; /* save position for backing up */ while (c != '\0') { i = 0; while (c && c != buf[i]) { /* copy what doesn't match */ new[j++] = c; c = *p++; sp = p; } while (c && buf[i] && c == buf[i]) { /* partial matching */ c = *p++; i++; } if (buf[i] == '\0') { /* successful match */ char *cp = rep; while (*cp) new[j++] = *cp++; /* put in replacement text */ if (flag == FALSE) /* just 1 replacement */ { new[j++] = c; while (*p) new[j++] = *p++; /* copy the rest */ break; } } else if (c != '\0') { /* back up and try again */ new[j++] = *(sp - 1); p = sp; c = *p++; sp = p; } } new[j] = '\0'; movstr (new, sub); /* now look for next substitution */ } if (save != sub) { movstr (sub, save); sub = save; } j = length (sub) - 2; if (sub[j] == NL) sub[j] = '\0'; return (TRUE); } /* histget --- get a specified string from the history buffers */ static int histget (hp, sub) int hp; char *sub; { char buf[BIGBUF]; int i, j, maxinx, hval; *sub = '\0'; maxinx = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1; if (hp < Histline || hp > maxinx) /* out of range */ return (FALSE); hval = ((hp - Histline + Hptrlast - 1) % MAXHIST) + 1; for (i = Histptr[hval]; Histbuf[i] != '\0'; ) { int k; j = 0; while (Histbuf[i] != '\0' && j < sizeof(buf) - 1) { buf[j] = Histbuf[i]; i = (i + 1) % HISTSIZE; j++; } buf[j] = '\0'; /* strcat (sub, buf); */ movstr (buf, sub + (((k = length (sub) - 1) <= 0 ? 0 : k))); } return (TRUE); } /* histarg --- return the last position of the next argument */ static void histarg (ptr, len) char *ptr; int *len; { char *p; char c; int bracket, paren, brace, squote, dquote, bquote, skip; p = ptr; *len = 0; skip = FALSE; bracket = paren = brace = squote = dquote = bquote = 0; repeat { *len += 1; c = *p++; while (skip == FALSE && (c == SP || c == TAB)) { c = *p++; *len += 1; } skip = TRUE; switch (c) { case ESCAPE: c = *p++; *len += 1; break; case '[': if (squote == 0 && dquote == 0 && bquote == 0) bracket++; break; case ']': if (squote == 0 && dquote == 0 && bquote == 0) bracket--; break; case '(': if (squote == 0 && dquote == 0 && bquote == 0) paren++; break; case ')': if (squote == 0 && dquote == 0 && bquote == 0) paren--; break; case '{': if (squote == 0 && dquote == 0 && bquote == 0) brace++; break; case '}': if (squote == 0 && dquote == 0 && bquote == 0) brace--; break; case '\'': if (dquote == 0 && bquote == 0) squote = 1 - squote; break; case '"': if (squote == 0 && bquote == 0) dquote = 1 - dquote; break; case '`': if (dquote == 0 && squote == 0) bquote = 1 - bquote; break; } } until (c == '\0' || ((c == SP || c == TAB) && paren == 0 && brace == 0 && bracket == 0 && squote == 0 && dquote == 0 && bquote == 0)); *len -= 1; return; } /* ctoi --- character to integer conversion, updates indices ala Fortrash */ static int ctoi (str, inx) register char *str; register int *inx; { register int ret = 0; int neg = 0; if (str[*inx] == '-') { neg = 1; *inx += 1; } while (digit (str[*inx])) { ret = 10 * ret + str[*inx] - '0'; *inx += 1; } return (neg ? -ret : ret); } /* min --- real function to return min of two numbers */ static int min (a, b) register int a, b; { return (a < b ? a : b); } /* histinit --- reinitialize history buffers */ static void histinit () { Hbuffirst = Hbuflast = Hptrfirst = Hptrlast = Histline = 0; event_count = 1; } /* histsave --- save history command lines */ histsave (file) char *file; { int fd, status, junk; if ((flags&nohistflg) != 0) return (FALSE); if ((flags&prompt) == 0) return (FALSE); if ((fd = creat (file, 0600)) < 0) /* delete previous contents */ return (FALSE); status = TRUE; junk = MAXHIST; if (write (fd, & junk, sizeof (junk)) != sizeof (junk)) status = FALSE; junk = HISTSIZE; if (status == TRUE && write (fd, & junk, sizeof (junk)) != sizeof (junk)) status = FALSE; if (status == TRUE && write (fd, &Hbuffirst, sizeof(Hbuffirst)) != sizeof (Hbuffirst)) status = FALSE; if (status == TRUE && write (fd, & Hbuflast, sizeof (Hbuflast)) != sizeof (Hbuflast)) status = FALSE; if (status == TRUE && write (fd, &Hptrfirst, sizeof(Hptrfirst)) != sizeof (Hptrfirst)) status = FALSE; if (status == TRUE && write (fd, & Hptrlast, sizeof (Hptrlast)) != sizeof (Hptrlast)) status = FALSE; if (status == TRUE && write (fd, Histptr, sizeof(Histptr)) != sizeof (Histptr)) status = FALSE; if (status == TRUE && write (fd, Histbuf, sizeof(Histbuf)) != sizeof (Histbuf)) status = FALSE; close (fd); if (status == FALSE) unlink (file); /* remove entirely */ return (status); } /* histrest --- restore a history save file */ int histrest (file) char *file; { int fd, status, junk; if (flags&nohistflg) return (FALSE); if ((flags&prompt) == 0) return (FALSE); if ((fd = open (file, 0)) < 0) /* open for reading */ return (FALSE); status = TRUE; if (read (fd, & junk, sizeof (junk)) != sizeof (junk) || junk != MAXHIST) status = FALSE; if (status == TRUE && read (fd, & junk, sizeof (junk)) != sizeof (junk) || junk != HISTSIZE) status = FALSE; if (status == TRUE && read (fd, &Hbuffirst, sizeof (Hbuffirst)) != sizeof (Hbuffirst)) status = FALSE; if (status == TRUE && read (fd, & Hbuflast, sizeof (Hbuflast)) != sizeof (Hbuflast)) status = FALSE; if (status == TRUE && read (fd, &Hptrfirst, sizeof (Hptrfirst)) != sizeof (Hptrfirst)) status = FALSE; if (status == TRUE && read (fd, & Hptrlast, sizeof (Hptrlast)) != sizeof (Hptrlast)) status = FALSE; if (status == TRUE && read (fd, Histptr, sizeof(Histptr)) != sizeof (Histptr)) status = FALSE; if (status == TRUE && read (fd, Histbuf, sizeof(Histbuf)) != sizeof (Histbuf)) status = FALSE; Histline = - (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST; event_count = 1; close (fd); return (status); } /* history --- print history buffer, or save or restore buffer to file */ int history (argc, argv) int argc; char **argv; { int i; char *hf; int (*hfp)(); if ((flags&nohistflg) != 0) { if (flags&prompt) prs ("history processing not enabled\n"); return 1; /* failure */ } if ((flags & prompt) == 0) /* shell file */ return 1; if (argc == 1) { int j = Histline + 1; int k, l, m; int neg; char *cp; #define outstr(s) for (cp = s; *cp; cp++) \ if (*cp == NL && *(cp+1)) \ prs_buff ("\\n"); \ else \ prc_buff (*cp) for (i = Hptrlast; i != Hptrfirst; i = (i + 1) % MAXHIST) { neg = FALSE; k = j++; if (k < 0) { neg = TRUE; k = -k; } itos (k); l = length (numbuf) - 1; for (m = 3 - l; m > 0; m--) prc_buff (SP); prc_buff (neg ? '-' : SP); prs_buff (numbuf); prc_buff (COLON); prc_buff (SP); /* * make sure that what we're printing * doesn't wrap around the history buffer. */ k = (i % MAXHIST) + 1; if ((k != Hptrfirst && Histptr[i] < Histptr[k]) || (k == Hptrfirst && Histptr[i] < Hbuffirst)) outstr (& Histbuf[Histptr[i]]); else { /* saved text wraps around */ for (l = Histptr[i]; l <= HISTSIZE - 1 && Histbuf[l] != '\0'; l++) if (Histbuf[l] == NL && Histbuf[l + 1 <= HISTSIZE - 1 ? l + 1 : 0] != '\0') prs_buff ("\\n"); else prc_buff (Histbuf[l]); if (Histbuf[HISTSIZE - 1] != '\0') outstr (Histbuf); } } return 0; } else if (eq (argv[1], dashi)) { histinit (); return 0; } else if (eq (argv[1], dashr)) hfp = histrest; else if (eq (argv[1], dashs)) hfp = histsave; else { prs(argv[1]); prc(COLON); prs(h_badopt); newline(); return 1; } if (argc >= 3) hf = argv[2]; else hf = histfnod.namval; return ((*hfp)(hf) != 0); /* do a save or restore */ } SHAR_EOF echo shar: extracting "'sample.shrc'" '(443 characters)' if test -f 'sample.shrc' then echo shar: over-writing existing file "'sample.shrc'" fi cat << \SHAR_EOF > 'sample.shrc' # .shrc file --- this file will be read every time the shell cranks up # if it is in the $HOME directory # this is a sample, currently set up to do some Korn shell emulation PPID=$+ # set the Parent Process Id # source file name given by ENV environment variable # and only if $ENV is not this file. if [ "$ENV" != "" -a "$ENV" != "$HOME/.shrc" ] then . $ENV fi # put any useful shell functions here, or source a file with them in it. SHAR_EOF echo shar: extracting "'aliases.sh'" '(1027 characters)' if test -f 'aliases.sh' then echo shar: over-writing existing file "'aliases.sh'" fi cat << \SHAR_EOF > 'aliases.sh' # aliases.sh --- sample shell functions which do some of what the csh does # pushd, popd, and dirs --- written by Chris Bertin # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris # as modified by Patrick Elam of GTRI pushd () { SAVE=`pwd` DSTACK="$SAVE $DSTACK" if [ "$1" = "" ] then if [ "$DSTACK" = "$SAVE " ] then echo "pushd: directory stack empty." DSTACK="" return 1 fi set $DSTACK cd $2 shift 2 DSTACK="$SAVE $*" else if (cd $1) then cd $1 >&- else popd > /dev/null return 1 fi fi dirs return 0 } popd () { if [ "$DSTACK" = "" ] then echo "popd: Directory statck empty" return 1 fi set $DSTACK cd $1 shift DSTACK=$* dirs return 0 } dirs () { echo "`pwd` $DSTACK" return 0 } xchng () { # exchanged top two entries on the stack if [ "$DSTACK" = "" ] then echo exchange directory stack empty return 1 else pushd return 0 fi } source () { # have the shell read a file in the current shell . $* } bye () { logout ; } logout () { exit 0 ; } SHAR_EOF # End of shell archive exit 0