CS 61C (Fall 2007) |
Project 1Submit as "proj1". Due 2:45pm before lecture 9/19/2007. |
This is an individual assignment; you must work alone. Submit your solution, a file named directory.c, as proj1.
This project is intended to give you substantial practice with C pointers and strings, linked structures, and use of malloc and free.
Most of you have had experience working with the UNIX operating system. UNIX provides a tree-structured file system and operations for navigating around it. In this project, you will work with a command interpreter, implementing commands that create and delete nodes in a tree that loosely simulates a UNIX directory. (An actual UNIX file system is organized somewhat differently; see The UNIX Programming Environment, by Brian Kernighan and Rob Pike (Prentice-Hall, 1984), for further information.)
The nodes in the tree represent text files and directory files. A directory file contains zero or more text files and zero or more directory files. A text file contains characters, and corresponds to a leaf in the tree. (An empty directory also corresponds to a leaf.) Each file has a name, a sequence of alphanumeric espace characters. The root of the tree represents the root directory in the file system. The working directory is also a directory in the file system; the cd command (for "change directory") lets the user move the working directory up and down in the tree.
The commands to be supported by the interpreter are described in the table below. They are simpler than their counterparts in the UNIX shell: as noted above, there are only two kinds of files; no command options (normally specified using "–") are allowed; all arguments refer to files in the working directory except as specified in the table; and there is no special handling of "/" within file names.
command |
number of arguments |
comments |
pwd |
0 |
"Print Working Directory" — Prints the full path name of the working directory. |
cd |
1 |
"Change Directory" — Changes the working directory to that specified by the argument, which must be one of the following:
|
ls |
0 or 1 |
"LiSt files" — If given without arguments, prints the names of files in the working directory in alphabetical order; if given with the name of a text file in the working directory, prints that name; if given with the name of a directory in the working directory, prints the names of files in that directory in alphabetical order by name; otherwise prints nothing. |
cat |
≥ 1 |
"conCATenate" — Prints the contents of the text files named by the arguments. |
mkdir |
1 |
"MaKe DIRectory" — Creates a directory with the given name. The working directory must not already contain a file with that name. |
rmdir |
1 |
"ReMove DIRectory" — Removes the directory with the given name. The working directory must contain the named directory, and the named directory must be empty. |
create |
1 |
Creates a "text file" with the given name, then reads the contents of the file from standard input. The working directory must not already contain a file with the given name. |
rm |
1 |
"ReMove" — Removes the named text file from the working directory. The working directory must contain the named file. |
cp |
2 |
"CoPy" — Creates a copy of the first argument in the working directory; the second argument specifies the name of the copied file. If the first argument names a directory, the copy will contain copies of the directory's files and subdirectories. (I.e. it is a deep copy.) The working directory must contain the file named by the first argument, and must not already contain a text file whose name is the second argument. |
mv |
2 |
"MoVe" — If the second argument names a directory, moves the first argument into the directory; the directory must not already contain a file whose name is the first argument. If the working directory does not contain a file whose name is the second argument, changes the name of the file named by the first argument to be the second argument. The working directory must not contain a file whose name is the second argument. |
Table of commands to be supported by the project 1 command interpreter |
---|
The directory ~cs61c/files/proj/1/ contains four files: dirmain.c, directory.h, directory.c, and oracle. The main program in dirmain.c repeatedly reads a command from standard input, checks that it is syntactically correct, and if so calls one of the functions in directory.c to execute the command. The file directory.h contains declarations for functions that access and modify the directory tree. The file directory.c contains the actual definitions of these functions, as well as a struct entryNode declaration that represents a file (directory or text file). It also provides complete implementations for commands that don't change the directory structure, namely cd, pwd, ls, and cat. Don't change these functions or the struct entryNode declaration. The file oracle is a compiled version of our solution, which you can use when this spec isn't clear, and to test and automated tests you develop. You are to submit only your directory.c file. Changes to other files will not be accepted.
Each entryNode stores the name of the corresponding file (that is, a pointer to the file name's first character), a pointer to the file in the same directory whose name is next in alphabetical order, an indicator of whether the file is a text file or a directory file, a pointer to the parent node, and a pointer to the contents of the file (text for a text file, a list of files, ordered alphabetically by file name, for a directory file). Here is the entryNode declaration.
struct entryNode { char * name; struct entryNode * next; /* sibling */ int isDirectory; struct entryNode * parent; union { char * contents; /* for a text file */ struct entryNode * entryList; /* for a directory */ } entry; };Figure 1 contains a sample directory and its representation in entryNodes.
A sample directory:
Its implementation:
Implementation of the pwd, cd, ls, and cat commands have been provided in the directory.c file for you. You are to complete the program, providing code to initialize the file system and implement the mkdir, create, rmdir, cp, mv, and rm commands. You may provide auxiliary functions. Detailed information about the behavior of these commands follows.
file system initialization | returns a pointer to a directory entryNode with name = "/", no siblings, parent = itself, and no children. |
createDir | creates an entryNode for the new directory, copying the argument string into dynamically allocated space for the directory name, then adds the new file to the working directory's entries list. |
createFile |
creates an entryNode for the new text file, copying the argument string into dynamically allocated space for the file name. It then reads the contents of the simulated text file from standard input, and stores it into suitably allocated dynamic storage. (You should not assume any bound on the amount of text.) Two consecutive carriage returns (that is, '\n' followed immediately by another '\n') indicates the end of this text. The first is stored in the contents string, followed by a null character; the second carriage return is not stored. The new file is then added to the working directory's entries list. |
removeFile, removeDir |
removes the named file from the working directory's entries list, and frees all dynamically allocated storage associated with the file. |
moveFile | removes the named file from the working directory's entries list, renames it, and inserts it either back into the working directory's entries list or into the specified subdirectory's entries list. Checks to ensure that the move does not replace an existing file. |
copyFile | makes a deep copy of the named file, supplies a new name, and inserts the copy into the working directory's entries list. Checks to ensure that the copy does not replace an existing file. |
Functions in directory.c to be completed |
---|
cd
to a nonexistant directory.cd
to a file.ls
of a directory, and of a subdirectory.create
.mkdir
a directory whose name already exists.rmdir
a non-empty directory.mv
a basic file, and a directory.cp
file, and directory.tests.in
into your project, which
we assume is named directory
for this example (gcc ... -o directory
), and directs
the output to tests.out.dirty
. The second command will show you any differences
between the tests.out
and tests.out.dirty
files. Note that you might want
to have more than one set of tests for your project, in which case you might call
them tests.1.in
, tests.2.in
, etc, and you may wish to
write a script or program to run them all. nova> cat tests.in | directory > tests.out.dirty nova> diff -bi tests.out tests.out.dirtySimilar commands can be used to test programs which take command line arguments:
cat tests.in | xargs yourprogram
will run your program once for each line of tests.in
, making
that line of tests.in
the command line arguments for your program. Type man
xargs
on nova.cs.berkeley.edu for more information about xargs
, a very handy program.None of the commands to be implemented in this interpreter take any options. Input/output redirection is not allowed, and arguments to all commands except cd must be simple file names, each consisting of one or more alphanumeric characters. Other differences are described in the table below.
command |
differences |
pwd |
No differences. |
cd |
The working directory can be changed only to the root directory, the working directory's parent, or one of the child directories of the working directory. |
ls |
Takes only 0 or 1 argument. Directories are not sorted before files. |
cat |
No differences. |
mkdir |
Takes only one argument. |
rmdir |
Takes only one argument. |
create |
No "real UNIX" counterpart. |
rm |
Takes only one argument. |
cp |
Takes only two arguments; does not allow overwriting an existing file; does recursive copying of directories (enabled by the –r option of the real cp. |
mv |
Takes only two arguments; does not allow overwriting an existing file. |
Differences between commands in this interpreter and "real" UNIX commands |
---|
d
, of the above example diagram.
> ls e f > cd e > ls g h i > cd .. > create file1 text in file1 > ls e f file1 > mv file1 e > ls e f > cd e > ls file1 g h i > cat file1 text in file1 >