NAME mytee -- implement the tee unix utility SYNOPSIS mytee [-v -a] _filename_ DESCRIPTION Copy standard input to the file _filename_, and also to standard output. The following options are available: -a append to _filename_, if existing -v verbose output OUTPUT Usage message if the command is called without the required argument, or unknown options. Otherwise, runs silently. HISTORY Introduced in csc421.181 as a C warm-up and exercise in unix shell concepts. Thanks to Kyle Poore for suggesting this exercise. BUGS
This is a reimplementation of the standard unix command "tee". The code consists of a loop that reads characters one by one until and EOF, and as it reads the character it writes it to both standard out and to the a file. The result should be byte for byte equal to the input stream.
Remember that streams are char's, and all values of the char can be data. If this is a text stream then there are various restrictions. For instance, control versus visible characters. However tee can handle binary streams where any value of the character is data.
Therefore, the stream of char is generally embedded in a stream of int, and int values that are impossible byte values can be used to relay information about the stream, not in the stream. Such is how EOF is handled.
Demonstrated is how unix commands are implemented. The mytee, if appearing in the shell's path,
is invoked equivalently to any of the standard unix commands — ls, cp, tee. What have you.
They are programs like any others. Being shipped with the operating system they are traditionally
found in the directories /bin, /usr/bin, /sbin
or /usr/sbin
,
and these directories are only special in that the default configuration for the shell
PATH variable includes these directories. To see all the directories that are currently
configured for your shell, write echo $PATH
. If you were to extend the list
of colon separated directory names in $PATH
to include the path to your
mytee executable, then myteeM
would look like those standard Unix system utilities.
The getopt call is demonstrated. I require this getopt call for all programs, and it is pretty much done for you except for modifications that should be fairly obvious. See man getopt. In short, it takes a string that encodes the name and kinds of options, and presents the options one by one for processing. The switch statement is traditionally used for the multi-way branch on the option character. A slight bit of trickery at the bottom of the while loop will jump the argv array over the consumed options, so that argv[0] should now be pointing at the first argument, if there are any.
Note that it is not acceptable to write security vulnerable code. Not even in this context. For instance, the check of argc after the getopt, to determine if argv[0] exists is required in this course. You should be as unwilling to access non-existent array items as you are to dive into an empty pool. It has to be that instinctual, in order that we protect the information infrastructure that we rely on. An inexpensive way to achieve this is the assert macro, from assert.h, to deliberately halt the program if a problematic condition exists.
The verbose flag is used for debugging. Typically printf's are guarded by if (is_verbose)
, and
provides an easy way to turn on a code trace for debugging. The sample shows how I use the compiler
macros _FILE_ and _LINE_ to help make the messages more useful.
The makefile demonstrates several aspects of make. A recipe is a target with dependencies followed by a list of actions, all the actions are indented by one tab. It must be a tab. The actions are commands as would be written into the shell. If a command begins with a "-" that means that make should continue on even if an error is encountered. Else make aborts. If a command begins with an "@" it means the command is not to be echo'ed before running. @echo is popular to get expected behavior when using echo's to inform the make user what is going one.
Makefiles customarily contain the target "clean", which removes intermediate build products from the directory. A build after clean might be more correct than a non-clean build, as some intermediate build products might not get rebuilt, if dependencies are incorrect. Before submitting, make sure you pass tests from a clean build.
This makefile contains the target "test", which runs the mytee on a simple example or two. You are to write mytest which continues the tests to other examples. In this case, you must devise a test for mytee when invoked with the -a option. Also, test that bad parameters do not cause a crash — for instance missing or bad arguments.
author: burton rosenberg
created: 02 sep 2018
update: 02 sep 2018