Introduction to Unix I have an account. What now? Well, what can you do with a Unix account? Just about anything you want. You can read and send mail and news from it, program, do online research, and a whole bunch of other things. You can use it for "serious" work or for fun. Serious work might include writing code in C or C++, researching the history of art, or writing up a technical paper; fun might include writing code in C or C++, playing games, reading news, or trying to figure out how to write up that technical paper. The Beginning: logging in. You can login by a number of means, the main means being telnet and rlogin. There are other means that are variations on these, but these are the important ones. To use telnet, you can use a command like this: % telnet cory.eecs Trying 128.32.138.81... Connected to cory.eecs.Berkeley.EDU. Escape character is '^]'. ULTRIX V4.4 (Rev. 69) (cory.EECS.Berkeley.EDU) login: password: This is an example of how to use telnet from a Unix system. There are also telnet programs available for MacOS, OS/2, and Windows 95 (where it is included with the system software). The rlogin command was once more or less a Unix specific command, though there are now versions available for non-Unix machines. Here is how one might use the rlogin command on a Unix system: % rlogin cory.eecs Password: The rlogin command will assume that your account name is the same as that on the account that you wish to login to. This, and its relatives, rcp, rsh, and its more security conscious friends, ssh, slogin and scp, can also be set up to log you in without a password. With rsh/rlogin/rcp, this can be done with a file called .rhosts in your home directory. It has the following format: [taken from the account of helper@soda.CSUA] % cat .rhosts soda.CSUA.Berkeley.EDU jon ocf.Berkeley.EDU helper scam.XCF.Berkeley.EDU What this means is that for this account, the user jon is permitted to login in from soda.CSUA.Berkeley.EDU without a passwd, and similarly for the next two lines. If you omit the second part of each line, then one may login to the helper@soda account only if one has the same login, that is helper@scam.xcf, in the case of the last line. After you login, the system will print out the contents of the file /etc/motd, also known as the "message-of-the-day". You should take the time to read this file as it will often contain important information. Then, the system will give you a prompt to tell you it is ready for your commands. % <Now, it is your turn to say something> Well, now what? You have logged in. That means that somehow, you have shown to some degree of certainty that you are the person associated with a certain account name. After the system is convinced of this, it will put you, figuratively, in a directory known as your home directory. This is where most of your personal files are stored and it usually has a name like /home/d/users/jonboy or something like that. Your home directory is like your own personal filing cabinet. You can place files in it, have folders - (called directories in Unix parlance) - with more files and even directories in those as well. A few basic commands: Commands in Unix tend to have a number of options or "flags" that tell the command how to act when run. Some of these flags can be used together, others cannot be used if others are present. Let's use the ls command as an example. The ls command lists files. One important flag to ls, the "-l" flag, tells ls to return a long listing of files, which will contain a lot of information about the files listed. The "-a" flag will list files beginning with a period ("."), files that ls would otherwise not show. Here is an example: % ls / altroot dev kern proc stand usr bin etc lost+found root sys var bsd home mnt sbin tmp A simple listing of files. Not a lot of info, but if you were familiar with the general layout, you would probably know what was what. % ls -l / total 2659 drwxr-xr-x 2 root wheel 512 May 25 17:05 altroot drwxr-xr-x 2 root wheel 1024 Jul 20 06:47 bin -rw-r--r-- 1 root wheel 1325378 Aug 23 09:25 bsd drwxr-xr-x 3 root wheel 2560 Sep 2 21:42 dev drwxrwxr-x 10 1000 wheel 2048 Aug 31 12:56 etc drwxr-xr-x 3 root wheel 512 Aug 4 20:55 home ... --------------------------------------------------------- Filetype/Mode Links Owner Group Size Modif. date Filename... As you can see, ls -l gives a long listing, with much more info. The first part of each line tells us what kind of file each file is; the one that have a "d" at the beginning are directories, and the ones with a "-" there are "regular" files. The next nine characters refer to the file mode or permissions. These permissions detail what people may do with the file in question. The next column is the number of "links" made to that file. That information isn't all that useful right now. The next column is the owner. Some files may be owned by root (the system administrator), others by users like yourself. The next column is the group owner. Groups are groups of users listed under a common group name in the file /etc/group. The numbers that follow give the size of the file in kilobytes. Then comes the last date that the file was modified. Finally is the name of the file itself. % ls -a / . .profile bsd home mnt sbin tmp .. altroot dev kern proc stand usr .cshrc bin etc lost+found root sys var Here, we see some files that begin with "."; the first two, . and .. are present in every directory. The first one, ".", refers to the current directory, similar to DOS, and just as in DOS, "..", refers to the "parent" directory, the one just above this one in the directory tree. There are, depending on the particular flavor of Unix and the version of "ls", between twenty and thirty flags to the ls command. What do they all do? Never fear, man is here. The man command, short for manual, gives you access to the large amount of online documentation that is, or should be, available on any Unix system. There are "man pages" available for user commands, system calls, library routines, devices, file formats, games, miscellaneous things, and system administration -- these are the actual categories and are numbered from (1) through (8). % man man will give you a one to two page summary of what the man command can do and some cross-references to other related man pages. You may from time to time see references like this: see sync(2). This means to see the man page on sync in section 2. Here is how that works: % man 2 sync (note, man on Solaris requires "man -s2 sync". Why? It's Solaris.) Often, there will be a user command and related, though sometimes completely unrelated, system or library call. The above would be one way to get the specific man page for the item you want. Looking at intro(x) will give you a short introduction to what section x of the manual pages is about. The man command is very useful; it is your first line of defense against the unknown! % man -k this will give you all the entries on "something" Fortunately, Unix is more than the ls command and online documentation; you can also move files too. Actually, there is a lot you can do with files, but let's take it slow first. The mv command moves files around, and can be used to rename files or to replace one file with another. It has, thankfully, only two flags, "-i" and "-f". The "-f" flag tells mv to "just do it and forget it". The "-i" tells mv to ask before possibly "clobbering" another file out of existence. You can move files into directories, one file onto another, directories into directories, but you can not move directories across file systems. What does that mean? A little later on, there will be a little diversion for a talk about file systems and how they affect you as a user. For now, don't worry about it. If you keep most of your files in your home directory, you will not have to worry about it too much. % mv jon.01Jan98 elaine.03Feb98 letters (Moves files "jon.01Jan98" and "elaine.03Feb98" into the directory "letters", provided it exists.) % mv foo.c project1.c (Renames the file "foo.c" as "project1.c", DELETING any pre-existing file named "project1.c".) % mv partA partB partC summary.txt report (Moves directories "partA", "partB", and "partC", and the file "summary.txt", into the directory "report".) The cp command, used to copy files, has about five or six flags, which are documented in its man page. You can use cp two different ways; you can copy a file or several files into a directory or copy one file to another file. The cp command does a simple thing and so, it is, usually, a simple command to use; the syntax is quite similar to that of mv, detailed above. The mkdir command makes a directory like so: % mkdir personal You can make "personal" your current directory with the "cd" command ("change directory") like so: % cd personal How do you know what your current directory is? The pwd (print working directory) command will tell you. Try it out. To get back to where you were before, try "cd ..". Dot-dot, or "..", is the Unix symbol for the "parent directory", or the directory which contains the current directory. You can refer to the current directory by dot, or ".". Now is a good a time as any for a diversion into how the Unix file system is laid out. (Note, the term "Unix file system" is used in two different contexts. One refers, as here, to how files are laid out in a hierarchical structure. The other refers to the way bits and bytes are laid out on a very low level on the physical surface of the disk and made to represent files that you and I use on a everyday basis.) The Unix file system is similar to the DOS and MacOS file systems in that it is hierarchical. That is about it. At a very low level, the Unix FS is very different from either of those two. The three only bear a very general, high-level similarity to each other. The Unix file system has a beginning, called the "root". It is represented by /. If you cd to /, you will be in the top most directory in a machine's file system. From a physical standpoint, this will often be on a small (32 - 64 meg) piece of disk, called a partition. Other disks or partitions, will be "mounted" onto directories, called mount points in this context. The resulting structure is like a tree, where the root file system is like a trunk, and the other file systems are like branches. Some of the other filesystems include /usr, /home, and /mnt. To see what file systems are mounted, use the "df" or "mount" command: % df Filesystem 1K-blocks Used Avail Capacity Mounted on /dev/sd0a 31501 11603 16747 41% / /dev/sd0g 141940 125854 1892 99% /usr kernfs 16384 2796 0 100% /kern procfs 4319944 8528 0 100% /proc bloodrose:/home 1037855 835999 118828 88% /home bloodrose:/usr 695311 397914 241773 62% /mnt/usr % mount /dev/sd0a on / type ffs (local) /dev/sd0g on /usr type ffs (local) kernfs on /kern type kernfs (local) procfs on /proc type procfs (local) bloodrose:/home on /home type nfs bloodrose:/usr on /mnt/usr type nfs In this case, /home and /mnt/usr are actually located on the machine bloodrose, and are made available to the local machine by the wonders (and horrors) of the Network File System, NFS. The filesystems kernfs and procfs are "special" and I will leave it at that for now. In any case, each filesystem can be thought of a more or less separate physical entity (even though partitions can be on the same disk, they are separated from each other). This is why directories can not be moved across filesystems, say from / to /usr in this case. The system call that is used to move files does not work across file systems, and so other means are used. On a user level, you can use the tar command. Some OS's provide a mvdir command that acts like mv and moves directories across filesystems, but this is quite uncommon. The Unix file system, starting from /, will then branch out more and more, rather like a tree. In fact, sometimes files are referred to as leaves on the branch (directory). / (root)-/dev/sd0a | | ------------------------------------------------------------------- | | | | | | | | | | | | | | /usr-/dev/sd0e /bin /sbin | /home - /dev/sd1a /etc /dev | /tmp | | | --------------------- --------------------------------- | | | | | | | | | | | | | | | | | | /local /bin /man /share /jon /hde /lila /tom /jwm | | ---------------- | | | | | | /Mail /personal /pub ... Often, people will talk about relative and full pathnames. Relative pathnames are those that are taken relative to the current directory, like ../../../lila/humor/. Full pathnames are those that are in some way taken with respect to the root of file system, /, like say /home/jon/personal/addresses/heather. Okay, now lets nuke some files with rm. To remove a file, just say "rm file". You use the rmdir command to remove a directory nicely, but it will complain if the directory has files in it. To be a little more forceful -- and less cautious -- use "rm -r". To be really forceful, "rm -rf" will get rid of a directory and ALL ITS CONTENTS to the extent of your permissions. The rm command also takes the "-i" flag like cp and mv. Now lets say you have a file and you want to see whats in it. One way is with cat. Used like this: % cat .cshrc It will give you the contents of the file .cshrc. If you don't give it a filename(s), then it will expect you to type at the keyboard until you type Ctrl-D to tell "cat" that you are done. Another way is with more and its companion less. These will let you look at files one page at a time. You should be careful what kind of files you give to cat, more, and head. Sometimes, you might accidentally give them a binary file instead of a ASCII file. You can check what kind of file you have with the file command if you don't know. Some other useful ways to look at files are head and tail. These give you the head and tail of a file, that is, the first few or last few lines of a file. Now, say you wanted to cat a bunch of files. The cat command will take a list of files as arguments, but you may not want to type all that out. You can use what is called "globbing", the shorthand way one can represent a number of files. Here is an example: % cat personal/mail/* That would run the cat command on every file in the directory personal/mail. The "*" character can be used to represent any number of characters, including no character at all. The "?" represents one character, for example: % ls draft.? draft.1 draft.2 draft.3 The glob ".??*" is often used to get all the files that begin with "." but not get "." and "..". For more on globbing and other wildcard characters, read the man page for sh or csh, two common Unix shells. Okay, say you let "ls -l" loose on a large directory, say /usr/bin, and want to save what ls outputs. How can you do that? Well, most Unix shells will let you redirect command output to a file. You usually do that like this: % ls -l /usr/bin > ls.output Then you can run more or cat or whatever on that file, ls.output. If you want to add to the file, say add some system info to that file, you can append output to a file like this: % uname -a >> ls.output You can also do what is called "piping" with the pipe character "|". The pipe will let you use the output of one command as input for another, so you can do things like this: % ls -l /usr/bin | more Of course, this won't save the output of ls -l for you, but if you just want to page through once, this is alright. The pipe is used often to build up filters where you have one command feeding into another which will feed into another and so on, each command doing its little trick in turn. Here is one such pipeline: % repquota /var/mail | sort -nr +2 | head -20 This is one that I use to find out who is using the most disk space on /var/mail, the directory where users' mail gets delivered by default. Grep -- Searching for text If you want to search for a string of text in a file, the grep command is your friend. The name stands for "global regular expression printer," which comes from the ed text editor's command for finding all the lines in a file which match a given pattern. This handout is too short to discuss the full flavor of regular expressions, but suffice it to say that you can search for incredibly complicated patterns that involve much more than static, contiguous strings of text. The syntax of arguments to grep works something like % grep "string" file For example, let's say you have a file full of people's phone numbers, named "phones." You want to look up your friend Geordan's phone number, but you don't want to have to read the entire list to find it. % cat phones Dave 510-665-0000 Jon 510-601-0000 [...etc...] % grep Geordan phones Geordan 510-845-0000 Important stuff that doesn't really fit anywhere else, or keeping yourself on good terms with the system administrators There are certain commands you should know that don't necessarily fit anywhere. These are commands that in some cases will keep you on good terms with the administrators of your particular system, who can often be contacted by mailing root. Passwords The first of these is the passwd command. It is used primarily to change your password and it is suggested that you change your password often. Here is what a typical interaction with passwd will look like: % passwd Old password: New password: Verify: % The idea behind passwd having you enter your new password twice is that there is little chance of you typing your intended password incorrectly the same way twice in a row - helpful since you don't get to see what you type. Some systems, like those run by EECS Instructional, get their password information from a central server. In that case, you would see something like this: % passwd ... * chfn, chsh, passwd, and yppasswd * * have been disabled on this machine. * * * * You should now login to po.eecs to run * * any of the above commands by typing: * * * * % rlogin po.eecs * ... In this case, the password server (It's all done with NIS) is po.EECS. You would login into the machine po.EECS with rlogin as suggested - telnet would work too - and run the passwd command there. It will take some time for the change to take affect everywhere so be sure to remember your old password until you are sure it has updated everywhere; this normally takes no more than a day. Disk Usage Something to be conscious of is how much disk space you are using. Disk space is always a scarce commodity on any computer, not just Unix systems. System administrators constantly work hard to try to find more disk space somehow. In fact, there is a limit on how much space you can use, called your "disk quota". Usually, this is somewhere from five to 20 megabytes, depending on the type of account and the classwork involved. To see how much your quota is, use the quota command, like so: % quota -v Disk quotas for jon (uid 3558): Filesystem usage quota limit timeleft files quota limit timeleft /usr/mail 0 1000 4000 0 0 0 /home/awk 0 1 1 0 1 1 /home/biff 0 1 1 0 1 1 /home/cat 0 1 1 0 1 1 /home/diff 0 1 1 0 1 1 /home/grep 0 1 1 0 1 1 /home/perl 6988 10000 12500 946 0 0 The column usage is pretty self explanatory. The next one, quota, tells you what your soft limit is. "Soft" means that you may exceed this limit for a short period of time, usually only as long as you are still logged in. After that period of time, you may not use anymore disk, whether it be by creating more files or causing existing files to grow. The next column, labeled "limit", denotes your hard limit. It is the absolute maximum amount of space you may consume. It a good idea to check how much disk you are using because not only will it keep your system administrator happy, but if you go over quota, you may lose files as you save them, whether it be from an editor or other such program. As this happens, you will likely receive a message stating that your quota has been exceeded. Also, some windowing systems, like CDE or HP-VUE, may try to write temporary files to your home directory. If they can't do that, say because of disk quota problems, then you can not login in front of the workstation. Well, how can you fix this? There are a few parts to the problem. The first is logging back on again so you can fix this. Some workstations allow to you login "on console" so that you can avoid using the windowing system. Otherwise, you can login from another machine and work from there. Once you can access your account and home directory again, you need to find out where you are using disk space. You can do that with the "du" command. Actually, if you use netscape a lot and haven't fiddled with it a lot, you might want to consider removing files from the directory called .netscape in your home directory. Netscape will save all sorts of things there, some useful, some not. Usually, the most fruitful gains are derived from removing the directory .netscape/cache which is where netscape will save all images that are downloaded. After nuking any netscape stuff, or before, you can use the du command to see what is taking up space in your account. Here is one way to see what is up: % du -s * .??* 206 Ch3.zip 34 Constitution.TEXT 22 FAQ.html 20 FAQ.tex 30 HelpSessionTemplate 2 News 498 UCSEE 32 Week2..backup 32 Week2.fm 15090 bin ... The command "du -s" will show how large a file is, or, if it is a directory, the total sum of the space taken up by all the files in that directory. The "*" and ".??*" as mentioned before are shorthand ways to represent a lot of files. You can pipe this through the sort command so that you can see more quickly what is taking up space: % du -s * .??* | sort -nr | head -10 15090 bin 4916 sbin 4228 .netscape 834 vp 678 csua-lib 612 const ... Here, I can see that most of my disk usage is in the directory bin in my home directory. But, I am under quota at the moment, so I don't have to worry too much yet. But, I'll pretend for the sake of illustration. So now that I have identified where my disk usage is, I can start to clean up a bit. I can remove files if I think I don't need them anymore. I can also compress them too. Most Unixes have a compress command. You can also use the gzip command. The basic usage is like so: % gzip really.big.file % ls really.big.file.* really.big.file.gz The gzip command will by default put a ".gz" suffix on the file after it compresses the file. The compress command puts a ".Z" suffix instead. The gzip and compress both have several options. Now say you want to gzip or compress a whole directory the way pkzip or StuffIt do for PC's or Macs, or just put it all into one big file so that you can move stuff from one account to another. You can use the tar command to do this. Tar is short for Tape Archive. It can be used to move files to tape, move directories around, package up source code for later distribution, and a whole lot of other stuff. Here's how one might use tar: % tar cvf /tmp/myaccount.tar /home/jon This will place everything in /home/jon into one big file called /tmp/myaccount.tar. You can then gzip or compress this tar file later if you like. To get files out of the tar file, you can use tar too, with different flags: % tar xvf /tmp/myaccount.tar The "c" says to extract and the "x" says to eXtract. The "v" means to be verbose while the "f" says which tar file to act on. GNU tar can be built to compress while "tarring" up a directory with the "z" flag and to uncompress when untarring. You can use this to back up files to tape too: % tar cvf /dev/nrst0 /home/jon This will tar up the directory /home/jon to the tape associated with /dev/nrst0. You can also use tar to move directories around. % tar cvf - . | (cd /home/kuroda; tar xvf -) This will tar up the current directory and instead of writing it to disk or tape, tar will send it all to output (actually "standard output") which is then piped to that command in parentheses which is actually a subshell. It seems a little complicated, but it works. This is useful if you have to move a directory across filesystems as mentioned. Managing Processes Everytime you start up a command, you start a process. Well, there are details, but that is the idea. Sometimes, the processes you start on a machine get out of hand and you can not get them to stop with a ctrl-c or interrupt. Well then, you have to go out and kill them with the kill command. The kill takes what is called a pid (process ID) as an argument. Think of the pid as the target to hit so the process will stop. It's a violent analogy but it works. Well, how do you get the process ID? You can find this out with the ps (or process status) command. It will tell you what processes are running. % ps -eaf | grep jon (on BSD systems like cory.eecs, use -aux instead) jon 7019 6972 0 17:37:14 pts/1 0:00 tcsh jon 6985 6970 0 16:49:20 pts/2 0:05 ctwm -W jon 6972 6964 1 16:49:14 pts/1 0:02 tcsh jon 6948 6914 0 16:49:06 ?? 0:01 xterm -n Everywhere -C -geometry 80x6+0-0 jon 6963 6948 0 16:49:11 ? 0:01 xterm -rv -sb -n Here -geometry 80x24+0+0 jon 6968 6963 0 16:49:13 pts/0 0:01 tcsh jon 6964 6948 1 16:49:11 ? 0:01 xterm -rv -sb -n There -geometry 80x24-0+0 jon 6949 6948 0 16:49:07 ? 0:00 /usr/local/bin/ssh-agent /accounts/jon/.xinitrc jon 6970 6948 0 16:49:14 pts/2 0:01 tcsh I have used grep to strip only those processes with my name on them. In this case, I want to kill the process called ctwm, the on the second line. It has process ID 6985. So, to kill it, I would do this: % kill 6985 % ps -eaf | grep (to see if it really died) If it did not really die, you could try killing it again, or be a little more forceful. You can do this by saying "kill -INT" or "kill -KILL" in that order. The last one is a really nasty mean way to kill something and is not very clean in terms of what it leaves behind. The kill command basically sends what are called signals to processes. Some signals get interpreted as meaning "die, now" as in the case of SIGKILL, or 9. To get a list of signals kill can send, try "kill -l". Your .cshrc and .login files The two files which most control the behavior of your shell are .cshrc and .login. (This is true for the Berkeley C Shell and its derivatives such as tcsh, which most people tend to use.) These two files are full of commands which are run by your shell as it starts up, for example, when you log in. The difference between your .cshrc and your .login files is that .cshrc is run whenever a "csh" shell starts -- for example, when you open up a new "xterm", and .login is run only once, when you first log in, after your .cshrc is run. Some of the things you might find in your .cshrc file are: * The path setting, which determines where your shell will look for the programs you ask it to run * Environment variable settings, such as $DISPLAY (where your windows appear) or $TERM (what kind of terminal you are using) or $HOME (your home directory) * Aliases, which let you type complicated or difficult-to-remember commands quickly. For example, a good alias I sometimes use to check my email on UCLink is alias pop 'popclient -akc uclink4.berkeley.edu > ~/berkmail' so when I type 'pop' to the shell, it acts as if I typed the full string in the quotes. What you might find in the .login file is generally more arbitrary; not everyone likes to have the same things run as soon as they log in. A good example would be the class reminder and birthday files that class instructional accounts view upon every login, but not every time you open an xterm window or start a shell from within Emacs. Changing Shells Most people start out with either with Berkeley C Shell, csh, or the Bourne shell, sh when they get their first Unix account. But there are other shells available. Most are extensions or variations on these two shells. There are tcsh, bash, ksh, zsh, scsh and a few others. If you want to change your shell, you can do this with the chsh command. Just run the chsh command and you will shown your current shell and be prompted for a new one. You should read the man page for your new shell before changing it. You can even test it out by running the shell as a user command for a short while then exiting. The chsh command has similar behavior to the passwd command, so if you need to run passwd on a special machine, you will likely have to do the same for chsh. Reading Mail and News This really deserves a whole other help session handout which it will get. But suffice it to say, Internet News and Mail are two very important services that people expect from a Unix account. Mail There are several ways to read mail. One is with the good old Unix mail program or the BSD Unix Mail (or mailx or /usr/ucb/mail ...) program. It works well and is simple but is not too pretty. The man page for it is quite informative and well written. Another mail reader is elm. It is very much like mail but has a nicer interface and also has support for mail filters which let you filter mail based on who sent it, who it is sent to, what the subject is and so on. A very easy mail reader to pick up is pine. Pine also has the neat feature that it doubles as a newsreader. It is self documenting, comes with a menu that collects the most used options in one place and looks pretty. It has a built in editor that is also self documenting. The downside of pine is that it is not quite as powerful as other mail reading systems and has a somewhat undeserved reputation of being associated with cluelessness. The MH mail system is actually a collection of little programs that each do one specific mail function, like one for composing mail, one for sorting mail, one for replying to mail, and so on. It is also a very configurable mail system and has a filtering agent called slocal that works reasonably well but is not as powerful as another filtering agent, procmail. The GNU emacs editor has a mail reading which can be invoked with "M-x rmail". The emacs editor has built-in documentation on this. Emacs also has an MH interface called mh-e, which is also documented by emacs as well as the MH book by O'Reilly and Associates. You can forward mail to another account by having a file called .forward located in your home directory. This file can contain the other address -- or addresses -- that you want your mail sent to. This mail forwarding works much better than the mail forwarding the US Postal Service provides. News As noted, pine has a easy to learn news reader too. Emacs, of course, has a news reading system called Gnus. You can start this up with "M-x gnus". Some other stand-alone news readers are tin and trn. A lot of people use trn and it is a very fine newsreader. It is relatively easy to pick up and fairly well featured. You can subscribe to a number of newsgroups. There are subject specific groups that have postings that others write and you can post your own articles as well. It is a very free form medium that works well for general discussions. Some good ones for the Berkeley Campus include ucb.news which has FAQs on how to find more newsgroups to subscribe to, ucb.cs.undergrads, which has info for CS undergrads, and ucb.general, which carries news for the entire Berkeley Campus. Things to look at later grep, sed, awk, perl, find, sort, uniq, emacs, vi, jove, write, finger, talk written by Jon Kuroda copyright 1997. revised slightly by Brian Gaeke Jan. 1998.