myfind Project
Overview
In this project, you will implement a variation of the
find and wc commands called
myfind. This tool will recursively list out all files in
the source tree (in any order).
Input
USAGE: myfind [-l] path
EXAMPLES:
myfind file.c
myfind dirfile
myfind dirfile/file.c
myfind dirfile/subdir/file
myfind -l dirfile/subdir/file.cThe program should be called myfind. It takes one
argument and one optional flag. path is any absolute or
relative path. If a non-directory file is given, it will only list that
one file. A directory file will have all files in its directory listed
and recursively follow any subdirectories
Add a flag “-l” to list out the number of lines (number of ‘’ characters) in a regular file
Error conditions
In the following cases, exit immediately with
EXIT_FAILURE:
- If there are no arguments to the program
- If there are two arguments but the first is a not the “-l” flag.
In the following cases, skip printing the file
- The file or directory doesn’t exist, lacks necessary permissions, or cannot be opened, read, and closed for any other reason
Output
The output should be a list of the paths to each file to standard
out, starting with the input path given as the command-line argument. If
the -l flag is set, then the number of lines in the file as
a integer should be printed after the filename, separated by a tab
\t (see format strings for
the exact strings to use for printing output).
Example
If I construct a new directory tree like this:
mkdir contents
mkdir contents/subdir
touch contents/file1
echo -ne "1\n2\n3" > contents/file2
echo -ne "1\n2\n3\n4\n" > contents/subdir/file3and then run myfind on it like this:
./myfind -l contentsI should see the following output:
contents
contents/subdir
contents/subdir/file3 4
contents/file2 2
contents/file1 0Where the space between the file name and the number of lines is a
single tab character (\t).
See Grading for some automated test cases.
Implementation
For each file in the file tree starting from path:
- If it’s a directory file, list its name, then recurse over each file
in the directory, except for
.and... Note that the tool should accept . or .. as arguments to the tool. Just be sure not to recursively traverse these directories.) - If it’s a regular file, list its name, then also list the number of
lines in the file, i.e., the number of newline characters
(
'\n') - If it’s any other kind of file, list its name only
- If the file doesn’t exist, or cannot be open, etc., skip it
Error messages are optional, but should only be written to stderr,
not to stdout, which will be used for program output, i.e., use
fprintf(stderr, ...) instead of printf.
Format strings
| File Type | Format String |
|---|---|
| Regular (no -l) | “%s” |
| Regular (with -l) | “%s Directory |
| Other | “%s” |
Be sure to print the entire path for each file, i.e., the given path argument joined with the path separator character (“/”).
System call and library references
Do not use helper libraries or other simplified calls to achieve similar results as functions below. Just use the syscalls or library functions below for these aspects of the project.
| Symbol | Reference | Reading |
|---|---|---|
| stat() | man 2 stat |
LPI 15.1 |
| struct stat | man 3 stat |
|
| st_mode | man 7 inode |
|
| ——————– | —————— | ———- |
| opendir() | man 3 opendir |
LPI 18.8 |
| readdir() | man 3 readdir |
|
| closedir() | man 3 closedir |
|
| struct dirent * | man 3 readdir |
|
| ——————– | —————— | ———- |
| open() | man 2 open |
LPI 4.1 |
| read() | man 2 read |
|
| write() | man 2 write |
|
| close() | man 2 close |
|
| ——————– | —————— | ———- |
| perror() | man 3 perror |
LPI 3.4 |
| exit(EXIT_FAILURE) | man 3 exit |
In addition to the above functions, you may also use functions
defined in the C standard header string.h as necessary.
manis the command-line manual.- LPI is The Linux Program Interface book.
- Knowledge of memory management and string processing is assumed.
Example header files
These are a set of header files that is sufficient for completing the project:
#include <assert.h> // assert
#include <dirent.h> // opendir, readdir, DIR, struct dirent
#include <errno.h> // errno
#include <fcntl.h> // O_RDONLY, O_WRONLY, O_CREAT, open
#include <stdbool.h> // bool
#include <stdio.h> // perror, fprintf
#include <stdlib.h> // exit
#include <string.h> // string.h
#include <sys/stat.h> // stat
#include <unistd.h> // read, write, closeJoining paths
Be sure when concatenating strings for the paths, that you
malloc() enough space for the entire new string
plus the additional path separator and plus the null
terminator for the string, e.g.,
// len + separator + len + terminator
int concatlen = strlen(curpath) + 1 + strlen(curdir->d_name) + 1;
char *newpath = malloc(concatlen * sizeof(char));
if (NULL == newpath) {
perror("malloc");
exit(EXIT_FAILURE);
}
snprintf(newpath, concatlen, "%s/%s", curpath, curdir->d_name);The grading tests will not put a path separator character at the end
of the path, even if its a directory. In testing, if you do have a path
separator, then joining by string concatenation may result in a double
path separator, e.g., subdir//file, which should be
harmless.
Building and running the tool
Create a Makefile that will build your project and give the resulting
program the name myfind. See the hello project for an example
Makefile.
Your project /must/ be buildable with make and runnable
with ./myfind, both from the root of your repo, i.e.,
cd ~/cop3402fal2026/myfind
make
./myfindAutomated grading will build and run your tool this way and only this way.
Submitting your project
git setup
Be sure to complete the vc exercise before attempting this project.
Create a new local repository, following the directions in the vc
exercise (including the last step,
git --set-upstream) and set the local and remote
repository URLs to be the following locations:
| Local repository | ~/cop3402fal2026/myfind |
| Remote repository | gitolite3@eustis3.eecs.ucf.edu:cop3402/$USER/myfind |
Commands
These steps only setup the repo, do not submit your code, and assume you have already completed the vc exercise. Consult that exercise for specifics on validating each step and submitting your code.
mkdir -p ~/cop3402fal2026/myfind
cd ~/cop3402fal2026/myfind
git init
echo "myfind project" > README.md
git add README.md
git commit README.md
# Enter a commit message in the editor that pops up
git remote add submission gitolite3@eustis3.eecs.ucf.edu:cop3402/$USER/myfind
git push --set-upstream submission masterSelf-check
See the hello project for instructions on cloning a project from the grading server.
Bonus features
Implement a file type filter
Implement a file type filter option. This option will either limit
the results to either only regular files or only directory files. The
option will either be --type f to limit to only regular
files or --type d to limit to only directory files. Without
this option, there should be no filtering as in the non-bonus project
requirements. Here is an example:
Take the following directory structure, where dir1 and
dir2 are directory files while file1,
file2, and file3 are regular files:
.
├── dir1
│ ├── file1
│ └── file2
├── dir2
└── file3Running ./myfind --type f . should yield the following
(in any order):
./file3
./dir1/file2
./dir1/file1Running ./myfind --type d . should yield the following
(in any order):
.
./dir2
./dir1Grading
The test cases used to grade the project are listed below. Scripts to run the odd-numbered test cases are provided publicly, while the even-numbered test cases are private.
To use the automated tests, first download them, e.g.,
cd ~/cop3402fal2026/myfind
wget https://www.cs.ucf.edu/~pappas/teaching/cop3402/fal2026/files/myfind_public_tests.tar
tar -xvf myfind_public_tests.tarTo run a test case, e.g., tests/03.sh, use the following
bash command from the root of your local repository directory:
bash myfind-tests/03.sh $(realpath myfind)
echo $?This runs the bash script, passing in the absolute path to your
program, which is what $(realpath myfind) does. The result
of echo $? should be 0 which indicates the
test was successful.
Test cases
| # | Description | Expected Output |
|---|---|---|
| 1 | Call myfind with no arguments | It should fail with the appropriate exit code. |
| 2 | Call myfind with an invalid option (not -l) | It should fail with the appropriate exit code. |
| 3 | Call myfind with a single empty file | It should print the file name |
| 4 | Call myfind with a path to an empty directory | It should only print the directory path |
| 5 | Call myfind with a path to a directory with no subdirectories | It should print the directory path and paths to all files in the directory |
| 6 | Call myfind with a path to a directory containing a subdirectory | It should recursively print the directories’ paths and their contents |
| 7 | Call myfind with . | It should print . and all its contents |
| 8 | Call myfind with .. | It should print .. and all its contents |
| 9 | Call myfind with -l on a single file | It should print the file path and number of lines in the file |
| 10 | Call myfind with -l on a directory containing nested files and directories | It should recursively print the directory and contents with lines for regular files |
Grading schema
| Criterion | Points |
|---|---|
| The git repo exists | 1 |
| The program is written, builds, and runs as described here | 1 |
| Tests pass, prorated by the number of test cases passing | 6 |
| ———————————————————— | ——– |
| TOTAL | 8 |