Weight: 10% of course grade.
Due date: 10:00 p.m. Thursday October 6, 2005.
In this assignment you will be asked to write two shell scripts (using sh) that manipulate files and directories.
Many shells provide a macro facility, i.e., a way of specifying "shortcuts" to longer command sequences (e.g., see the alias command). For instance, instead of typing:
seawolf$ wc -l /etc/passwd
we could assign this command line to a macro called countusers. Whenever countusers appears as part of a command line, the macro would be "expanded" by replacing it with the command line given above.
Your job is to write a shell script (using sh) called macro that implements a simple macro expansion and update facility. A macro is made up of a name and a macro expansion. In the above example, countusers would be the macro name and wc -l /etc/passwd would be the macro expansion.
The macro script must support the following four functions:
If command matches the name of a defined macro, replace command by the contents of its expansion and execute the command line with the specified arguments (arguments is optional). If command does not match the name of a defined macro, simply execute command with the specified arguments.
Add a new macro called name, which is defined with the given expansion. Only one expansion should exist for a given name at any time; if name is already defined, its expansion should be replaced (updated) with the given expansion.
Delete the macro with the specified name (if name is defined). If the macro name is not currently defined, your script should not do anything.
Print the expansion of the macro name (if name is defined). If the macro name is not currently defined, your script should print an error saying this.
To keep track of the defined macros, we will store the macro name and expansion pairs in a file called .macros. This file will typically be stored in the home directory of the current user (i.e., ~ or $HOME) so the macros will always be accessible.
For example, suppose .macros contained the following:
countusers wc -l /etc/passwd rdir ls -alR
We could run the following commands ($ is the shell prompt):
seawolf$ macro -l rdir ls -alR seawolf$ macro ls . <regular listing of current directory> seawolf$ macro -a ls ls -l seawolf$ macro ls . <long listing of current directory> seawolf$ macro countusers 5610 /etc/passwd seawolf$ macro -d countusers seawolf$ macro -l countusers macro "countusers" is undefined
Most software projects divide code and declarations into several different files. For projects written in C, there tend to be header files for each module and each set of configuration parameters. There are also system header files that declare the interface to system calls and library functions.
All these header files must be included in the C source file at compile time. But if any of these header files are changed, the interface they declare may be out of date, meaning we must recompile the source file.
Your job is to write a shell script (using sh) called printdepends that can identify simple dependencies for a C file. Your script will accomplish a task similar to the makedepend program, but will be far simpler. You will not need to have any knowledge of C to write this script.
Your printdepends script should support the following usage:
Your script will process each specified sourcefile in sequence, looking for #include directives. There are two types of #include directives that may appear in a C file:
Inserts the contents of systemfile.h in the file. If no path is specified, the file should be located in either /usr/include or /usr/include/linux; a relative path would be relative to one of these directories.
Inserts the contents of localfile.h in the file. A relative or absolute path may be specified, otherwise look for the file in the same directory as the file being processed, and if it is not located there, look in /usr/include or /usr/include/linux.
A #include directive must appear entirely on one line, but may have arbitrary whitespace (zero or more whitespace characters) before the #, between the # and include and between include and the filename.
Since a #include directive inserts the contents of another file in the C source file (at the location it appears, but that isn't important here), we must also check the included file for #include directives. This is a naturally recursive process.
Your script should output the following for each specified sourcefile:
Suppose dolittle.c contains the following lines:
#include "a.h" #include "b.h" int main() { return 0; }
Suppose further that neither a.h nor b.h contain any #include directives. We would expect something like the following output:
seawolf$ ./printdepends dolittle.c dolittle.c: a.h b.h
Suppose helloworld.c contains the following lines:
#include <stdio.h> int main() { printf("Hello world!\n"); return 0; }
Then the first few lines of a possible run could look like:
seawolf$ ./printdepends helloworld.c helloworld.c: /usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h /usr/include/linux/stddef.h /usr/include/bits/types.h /usr/include/bits/wordsize.h /usr/include/bits/typesizes.h /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h stdarg.h: file not found /usr/include/bits/wchar.h /usr/include/wctype.h ...
You will commit to the a1
directory of your CSC209
repository the following files:
macro
printdepends
Please remember to make your shell scripts executable by ensuring that
the first line is "#!/bin/sh
".
You are strongly encouraged to take advantage of the version control system and commit your work frequently so that you can keep track of your progress. Please note that perfectly fine (and even recommended) that you keep any additional files related to this assignment (such as files used for testing) under version control. The markers will simply ignore such files.