CMSC 216 Project #9 

$30.00

Category: You will Instantly receive a download link for .zip solution file upon Payment || To Order Original Work Click Custom Order?

Description

5/5 - (5 votes)

1 Introduction and purpose
In this project you will write a small simulation of a UNIX utility xargs, described below. The purpose of the project is
to get some experience with process control and basic systems programming concepts. The difficulty in this project is in
understanding what to do, not in the amount of code (which is actually relatively small) or complexity of data structures.
Note that your program in this project does not have to create any pipes. And note that you may not use the C library
function system() anywhere in your code, or you will not receive credit for the project.
For reasons described below, this project will only have public tests.
Due to the size of the course it is not feasible for us to provide project information or help via email/ELMS messages,
so such questions will not be answered. You are welcome to ask any questions verbally during the TAs’ office hours.
First we explain the basics of the actual UNIX xargs utility. Then we describe what your program has to do, including
some examples. Although the first time you read the project this may be somewhat confusing (and even the second time),
Section 4 gives a step by step outline of the things your program has to do.
1.1 Extra credit and number of submissions
You can again get extra credit for this project. If you make only one submission that passes all the public tests you will
get 3 extra–credit bonus points. And if your single submission is made at least 48 hours before the on–time deadline
you will get 3 additional extra credit bonus points, for 6 extra credit points total. (Obviously you can’t get the second
3 extra credit points if you don’t get the first 3, but you can get the first 3 without getting these second 3.)
However, as before, if you make too many submissions you may again lose credit. To avoid making submissions that
don’t compile or don’t pass the public tests you should compile your code, run the tests, check their output, and fix any
problems, all before submitting.
(If for some reason your code passes all the public tests on Grace, but doesn’t work when you submit it, so you have
to submit more than once– and this is not due to an error on your part or a bug in your code– you can talk with me
verbally in office hours about receiving the extra credit despite having more than one submission.)
2 About the UNIX xargs utility
UNIX utilities are generally small programs that do one thing, but are designed to easily work with other utilities or
programs, so they can be used together to do more complex things when needed. Shell pipes are one way to make
programs work together, where the standard output of one program or command is used as the standard input of another
program or command, whose standard output in turn can be piped to another one, etc.
But shell pipes are not helpful with programs or utilities that operate upon command–line arguments rather than
standard input, for example if we want one program’s output to be used as another program’s input, but the second
program operates upon command–line arguments rather than standard input. In fact, many UNIX commands/utilities do
not read any standard input and operate only upon command–line arguments, for example ls, cp, mv, rm, mkdir, etc.
One solution that can be used in cases like these is to write a shell script to run the second program using the output
of the first program as command–line arguments, but this is more work than needed in many cases.
Another solution that is sometimes appropriate is “backquote command substitution”, where one program is backquoted in the arguments of a second program, for example, ls `program1.x`. This causes the output of the backquoted
program (program1.x here) to be used as command–line arguments for the first program (ls here). Although this is very
useful at times we do not explain it further here.
A third solution is the xargs utility, which is the subject of this project. It converts its standard input to command
line arguments for another program. xargs has many capabilities, the majority of which we don’t need to worry about.
xargs for this project differs from the real xargs UNIX utility in a number of ways, and in fact it is much simpler than
the real xargs utility.
To differentiate the UNIX xargs utility from the program that you will write to simulate it we will use xargs to refer
to the UNIX utility, and your program will be named yargs.c. We will just use yargs to refer to your program, yargs.c
to refer to its source file, and yargs.x to refer to its compiled executable.
© 2023 L. Herman; all rights reserved 1
Your code will be in a file yargs.c, which will be a standalone program as in Project #2. In order to run the other
program that it executes, xargs has to create a child process that will actually run the other program; this is what your
yargs simulation of it will also have to do.
3 Usage of yargs.x
yargs.x will be invoked as follows:
yargs.x -n target–program target–args
where all of the command–line arguments are optional, and they are explained next.
-n (a minus sign followed immediately by a lowercase ’n’) is an argument to yargs.x itself. If it is present it has the
same effect as the -n option to make, meaning that it causes yargs.x to print the commands that it would execute
if -n had not been given, without actually executing them. Like the -n option to make, this allows a user to make
sure that they are running yargs.x with the arguments and input that will cause it to perform the commands that
they want. (Once they have verified that, they will presumably run yargs.x again with the same arguments and
input, but without the -n option.)
target–program is an argument to yargs.x. If present it is name of the program that yargs.x should execute, using the
standard input to yargs.x itself as command–line arguments for target–program. Note that a target program could
be a UNIX utility, or a user–created program.
target–args is a list of things that will be used by yargs.x as command–line argument(s) for target–program. When
running the target program these arguments will precede any command–line arguments that yargs.x takes from
its standard input and supplies as additional arguments to the target program.
The purpose of the target arguments is for arguments that will be supplied to the target program every time it is
run by yargs.x, and the standard input of yargs.x will become different arguments that are given to the target
program in addition to (and following) the target arguments.
The standard input to yargs.x consists of zero or more lines, i.e., each ending in a newline. For explanation, let
input1, input2, ···, inputn denote the successive lines in the standard input of yargs.x (i.e., n is zero if and only if the
input to yargs.x is empty). What yargs.x must do is to run target–program target–args input1, then target–program
target–args input2, and so on until it runs target–program target–args inputn.
In other words, yargs.x will execute the target program once for each line of its own standard input. The first time
the target program is run it will have the target arguments as command–line arguments and the first line of the standard
input of yargs.x itself as additional command–line arguments. (To do this the input lines will have to be broken up
into separate words, discussed further in Section 4.2 below.) The second time the target program is run its command line
arguments will be the target arguments followed by the words of the second line of the standard input of yargs.x, and so
on for each line of its own standard input.
However, if the -n option was given then yargs.x will just print target–program target–args input1, then target–
program target–args input2, up to target–program target–args inputn, each on a separate output line, but without actually
executing any of them.
3.1 Examples of yargs usage
The public tests have examples of running yargs.x in different ways. Here are some examples with explanation.
Some of these examples use the UNIX utility /bin/echo as the target program. echo was briefly demonstrated in
class when make and makefiles were covered. It just echoes or prints what is on its command line. For example, try a
UNIX command something like /bin/echo I want to win chocolate! to see its operation.
Suppose the current directory has a file datafile whose contents are the three lines shown on the left below, and a
file datafile2 whose contents are the two lines shown on the right. (In each file, each line ends in a newline.)
datafile
aa bb
cc dd
ee ff
datafile2
program1.c -o program1.x
list-test.c list.c -o list-test.x
© 2023 L. Herman; all rights reserved 2
yargs.x -n program.x xx yy < datafile
In this invocation program.x is the target program name and xx and yy are the target arguments. Because the -n
option was given, yargs.x will not attempt to execute program.x (which is not a UNIX command, but it could be
a user–written program). yargs.x will just print program.x and the target arguments once for (and followed by)
each line of the file datafile (which, due to input redirection, will be the standard input of yargs.x). So yargs.x
will simply print these three lines that it would try to execute if -n had not been given:
program.x xx yy aa bb
program.x xx yy cc dd
program.x xx yy ee ff
yargs.x /bin/echo < datafile
In this invocation the -n option is not used, /bin/echo is the target program name, and there are no target arguments. This will cause yargs.x to actually run the /bin/echo utility for each line of the file datafile. So echo
first runs with the two command–line arguments aa and bb (the first line of datafile), then echo runs with its
arguments being the second line of datafile, then with its arguments being the final line of datafile. So three
lines of output would be produced:
aa bb
cc dd
ee ff
The command cat datafile | yargs.x /bin/echo would have the same effect, because the cat command and
the pipe also cause the contents of the file datafile to be the standard input of yargs.x.
yargs.x mv < datafile
In this invocation mv is the target program name and there are no target arguments. Here yargs.x should attempt to
run mv three times, once for each line of datafile. The first run of mv will have the two command–line arguments
aa and bb, the second run of mv will have arguments cc and dd, and the last run of mv will have arguments ee and
ff.
The effects of these commands depend on whether bb, dd, and ff are directories, or whether they’re files (or
whether they don’t currently exist). The first invocation will run mv aa bb and if bb is a directory it will move aa
to the directory bb. But if bb is a file or if bb doesn’t exist, aa will be renamed to bb. Similarly with the commands
mv cc dd and mv ee ff which yargs.x will run for the second and third lines of its input. Of course if any of
files aa, cc, and ee don’t exist then there will be an error when yargs.x tries to run the command(s) that use the
names of nonexistent things. (How yargs.x should handle performing commands that have errors is discussed in
the next section.)
yargs.x gcc -Wall -ansi < datafile2
In this invocation gcc is the target program name and -Wall and -ansi are the target arguments that will be passed
by yargs.x as the first two argument to gcc. The remainder of the arguments each time that gcc will be run will
be the words on the lines of yargs.x’s standard input, meaning the contents of each line of datafile2, and will
be different during the two runs of gcc. So in this invocation yargs.x should execute gcc twice, once for each line
of the file datafile2. As a result, the following commands will be run:
gcc -Wall -ansi program1.c -o program1.x
gcc -Wall -ansi list-test.c list.c -o list-test.x
Note that the real xargs utility does not work the same as these examples, for reasons not fully explained here.
Certain options can cause it to work as these examples do, but since you should be concentrating on what your yargs
program has to do, we don’t describe the options that would cause xargs to mimic the behavior of your yargs. (If you
are interested in learning more about the real xargs utility though you can just run man xargs on Grace. However, to
avoid confusing yourself with what xargs would do compared to your yargs, even if you want to look at this information
you might want to wait until after you have finished writing the project to do so.)
© 2023 L. Herman; all rights reserved 3
4 What your program must do
Extracting the files from the project tarfile will create a directory project09. In that directory you must write your
program in a file named yargs.c (spelled and capitalized exactly as shown). Here is an outline of what it has to do:
1. It must include two header files safe-fork.h and split.h containing prototypes for functions safe_fork() and
split(), which are described respectively later in this section, and in Section 4.2.
2. yargs.x must initially figure out what the arguments were. The first step is to determine whether the -n argument
was used on its command line. Note that if the -n option is given, to have an effect it must be the first argument to
yargs.x (otherwise it will appear to be the target program name or part of the target arguments).
3. After seeing if the -n argument appeared on the command line, yargs.x must determine the name of the target
program, and see if there were any target arguments. For example, if it is run using the invocation yargs.x ls -t,
yargs.x will run ls (the target program name here) with target argument -t (followed by arguments taken from
lines of its standard input, as described below).
• If there were arguments on its command line, and the first one was not -n, the first one is assumed to be
the target program name. If there were no arguments after that then there are no target arguments for this
execution, otherwise any arguments after the first one are the target arguments for this execution.
• If there were arguments on its command line, and the first one was -n, then the second argument must be
the target program name. If there were no arguments after that then there are no target arguments for this
execution, otherwise any arguments after the -n and the target program name are the target arguments for this
execution.
• If yargs.x is run without any command–line arguments at all it won’t have anything to do because there is
no target program to run, so it will end up just quitting without doing anything. (Its exit status or return value
is discussed below.)
• If there is only one argument, but it was -n, then again it means that there was no target program, so yargs.x
will also quit without doing anything.
4. Once it has examined the command–line arguments, if the -n argument was not used, yargs.x will use its standard
input as command–line argument(s) to the target program, running the target program (in child processes) once for
every line of its standard input, until the end of the input is seen, with the (whitespace–separated) words of each
input line being used arguments as the command–line arguments for the target program. (As above, each line of
the input will end in a newline.) Recall that you read until the end of the input in Project #2.
Use the function split() described in Section 4.2 to break each input line up into its whitespace–separated words.
5. However, once it has examined the command–line arguments, if -n was given as the first command–line argument
to yargs.x (following the name yargs.x itself, of course), then yargs.x must print the commands without executing them. If there are n lines in its standard input then yargs.x will print n lines of output, where the first one
consists of the target program name, followed by the target arguments, followed by the words on the first line of
the standard input. The second line of output begins with the target program name and target arguments, followed
by the words on the second line of the standard input, etc. Regardless of how much whitespace separated the target
arguments or the words of the lines of the standard input, yargs.x must print each command with one single space
between (separating) the target arguments and the words of the line of standard input.
It is strongly suggested that you get the -n option to work right before trying to get your yargs.x program to
handle executing commands. If your program is not accessing arguments correctly, or is not reading the lines
of standard input correctly, then executing programs based on the target arguments and lines of standard input is
certainly never going to work.
6. As mentioned, yargs.x must create a child process each time it runs the target program. This is usually done using
the fork() system call, however, do not call fork() directly. We have provided an object file safe-fork.o,
which contains a function safe_fork(), which you should call instead. It prevents the possibility of having a
serious fork bomb. Your yargs.c should include safe-fork.h so the prototype of safe_fork() there will be
visible. safe_fork() has the same return value as fork() and has no parameter, as you can see by looking at
safe-fork.h. Use safe_fork(), instead of directly calling fork().
© 2023 L. Herman; all rights reserved 4
We set things up so your program will not even compile on the submit server if it calls fork() directly– but you
could still cause a fork bomb on the Grace machines if you call fork() and do it incorrectly, which could cause
dozens of other students to lose whatever work they were working on. Do not call fork() directly.
7. yargs.x can run the target program multiple times (once for each line of the standard input), with different arguments each time. The target program could produce different results each time it is run, because it’s being run with
different arguments each time. One invocation of the target program might even use results that were produced
by an earlier invocation of the target program. Consequently your yargs.x must ensure that each time the target
program is executed it finishes running before the next time that yargs.x runs the target program for the
next line of its standard input.
8. After each time it runs the target program in a child process, yargs.x will have to determine the exit status of the
child process, to know what status it should itself exit with. It will determine its exit status as follows:
• If the target program exits successfully every time that it is run (for every line of the standard input of
yargs.x), then yargs.x itself must also quit (after running the target program once for each line of its own
standard input) with exit status 0.
• If any invocation of the target program exits with any nonzero exit code, your yargs.x must stop running
the target program (meaning not run it for any further lines of its standard input), and quit immediately with
whatever nonzero exit status the target program had.
• If any error ever occurs trying to run the target program, meaning it could not even be run, your program must
quit (without trying to run the target program for further lines of its standard input) with exit status 255.
For example, suppose your program is run as yargs.x bananas are fun. Although perhaps there should be,
there is not actually a UNIX command bananas, so, assuming that there is no program in the current directory
or on the UNIX search path (see Section 4.1) that happens to be named bananas, when your program tries to
create a child process running bananas it will fail, and yargs.x must exit with status 255.
9. Another case where yargs.x should quit without doing anything is if its standard input is empty. Regardless of
what command–line were given to it, yargs.x is supposed to do something for every line of its standard input. If
it has no standard input then it will not have anything to do, so it will end up just quitting, with an exit status of 0.
4.1 Notes
• yargs.x will not use pipes. Any output produced by the child process created to run the target program will just
be printed to your program’s standard output.
• The output produced when the -n option is used should go to the program’s standard output.
Your program has to read its standard input, and produce output when the -n option is used. Do not use low–level
UNIX I/O system calls (that were recently covered) to read input or produce output– this is more difficult, and
unnecessary in this project. Just use the C standard I/O library functions covered in Chapter 15 of the Reek text.
• Before starting to code, look at the way that yargs.x is run in all of the public test scripts, and look at the files
that will be used as standard input for each test. Then look at the expected output files. If you aren’t sure why the
arguments and input result in the contents of the expected output files then ask about it in the TAs’ office hours
before starting to code, so you are sure you understand what yargs has to do.
• Several special cases were mentioned above in which yargs.x should not do anything and just quit (with an exit
status of 0). These may not have to be handled as special cases; much of this behavior could very well occur
naturally in these situation when you write the program to handle normal cases, so do not create special cases for
these situations unless they are necessary.
• Recall that to see the exit status (or exit code) of a program run in the tcsh shell on Grace, just use the command
echo $status. (It has to be the very next command, because it prints the exit status of the most recent command.)
• The -n option is not passed from yargs.x to the target program. (It is not a target argument.) It is an argument to
yargs.x, which affects the operation of yargs.x itself.
• Your program may assume that no input line will ever have more than 10000 characters before its terminating
newline. Other than what’s imposed by the maximum length of a line there is no specific limit to the number of
© 2023 L. Herman; all rights reserved 5
arguments that may be on a line, or to the number of input lines that your program may have to read and use as
command–line arguments to the target program that it is running. Situations where it’s not known in advance how
much data has to be read are cases where dynamic memory allocation is needed.
• Notice that when your yargs.x runs the target program, this will need to be done using some form of exec (one of
the exec system calls). Your program should use the UNIX search path to find the target program to run. Look at
the forms of exec mentioned in class and figure out which one or ones can be used in these situations.
Recall that the name of a program to run (the target program here) is given twice in a call to exec! It has to appear
as the first and second arguments to an exec–family system call.
• When a program is run using input redirection, such as program.x arg1 arg2 arg3 < inputfile, the program
does not see the “< inputfile” in its arguments. In this example the program would just see three command–line
arguments arg1, arg2, and arg3. This is because when input redirection is used the shell creates a process to run
the program in, that process’s input is redirected to come from the input file, and the program is run in that process.
The program has no way to know or tell where its standard input is coming from, whether from the keyboard or
in this case from the file inputfile– it just reads input. So yargs.c does not need to look for input redirection
symbols or input files on its command line, it just needs to read its input, no matter where it is coming from.
Similarly, when input is redirected to come from a pipe (as in program1.x | program2.x arg1 arg2 arg3), the
pipe symbol and whatever is before it don’t appear in the program’s arguments, so “program1.x |” will not be in
the arguments for program2.x here. program2.x will just see the output of program1.x as its standard input when
it reads input.
• yargs.c will have to free any dynamically–allocated memory when it is no longer needed or it will fail some tests.
4.2 Our function char **split(const char line[])
To facilitate the process of reading input lines and breaking them up into words we are supplying you with a function
split(), with prototype above, in the object file split.o (with associated header file split.h). It will take a string and
break it up into its components, where each component (except as indicated below) is a word, separated from other words
by either whitespace, or by the beginning or end of the string.
split() returns a dynamically–allocated array of dynamically–allocated strings, each of which is one word of the
line. The array will end with a NULL pointer, so its last element can be detected. split() will ignore (discard) blank
spaces, tabs, and newlines before and between words. Its argument string optionally can end with a newline. (Since you
will be reading lines from the input and calling split() on them, by default they would end in newlines.)
For example, if called on an input line parrots eat carrots (which ends with a null character), split() will return
a dynamically–allocated array with four elements, which will be (in order) the strings parrots, eat, carrots, and the
fourth element of which is just NULL (the three non–NULL strings, and the array itself, are all dynamically allocated). Note
the result would be the same if more than one blank space or tab separated the words.
The UNIX shell has many characters that have special meaning, for example backslashes can be used to escape certain
special characters, and single quotes have somewhat different effects than double quotes. For simplicity, our split()
function does not attempt to emulate these behaviors, other than one, which is that a double–quoted string is treated as
a single argument, and whitespace is preserved inside double–quoted strings. For example, if your program is run as
yargs.x⊔test.x⊔one⊔”two⊔⊔three⊔ ⊔ ⊔four”⊔five the target program test.x must be run with three command–
line arguments, the second of which will be “two⊔⊔three⊔⊔⊔four” (containing spaces as shown), and split() would
treat it as a single argument.
The array returned by split() is dynamically allocated, and all the strings that the array points to are also dynamically allocated. To avoid memory leaks you will have to free this memory when it is no longer needed. Note that the
array argv of command–line arguments to your program is of the same form as the array returned by split(), but the
argv array should not be explicitly freed– it is created automatically before a program starts, and automatically freed if
need be when the program ends. If a program tries to explicitly free its command–line arguments it will most likely result
in a fatal error.
Do not try to write your own split() function. We are giving you a correct split() function. If you write your
own it might not always work correctly, or might not be consistent with ours in all cases. Just call our split() function
to break up lines of the standard input into their constituent words.
© 2023 L. Herman; all rights reserved 6
A Development procedure review
A.1 Obtaining the project files and compiling
Log into the Grace machines and use commands similar to those from before:
cd ~/216
tar -zxvf ~/216public/project09/project09.tgz
This will create a directory project09 that contains the necessary files for the project, including the public tests and
associated data files, safe-fork.h, safe-fork.o, split.h, and split.o. After extracting the files from the tarfile, cd
to the project09 directory, create a file named yargs.c (spelled exactly that way) that will #include the header files
safe-fork.h and split.h, and write the program as described above.
Since your program will be a self–contained standalone program, you don’t need to write a makefile for this project
(although you are welcome to if you like; if so it will just be ignored on the submit server). You can compile your program
just using the (exact) command:
gcc yargs.c safe-fork.o split.o -o yargs.x
(Of course if you do write a makefile you need to ensure that the required compilation options are being used.)
A.2 Checking your results and submitting
The tests of this project will all be UNIX shell scripts, named public01, public02, etc. These public test scripts have
no standard input. They will run your program with different arguments, and although the scripts do not read any input,
they may run your yargs.x program with input redirected from a file or piped from a command. The public test scripts
produce output that can be compared with the expected output, for example:
public09 | diff – public09.output
The test script (public09 in this example) is running your yargs.x with a target program, a target argument, and
some input; you can see the exact commands that each test is using to run yargs.x by looking at the scripts using less.
These scripts are expecting the name of your executable program to be yargs.x, so you must use that exact name when
compiling.
In a recent discussion section your TA showed you the run-tests shell script, which runs a program on all of its
tests at once. However, the run-tests script will not work correctly for this project, because run-tests is expecting
the project tests to be compiled C programs (executable files) rather than shell scripts. A modified version of run-tests
named run-tests2 has been put on Grace that you can run instead for this project– after you compile yargs.x, just
running run-tests2 will execute all of the shell scripts (tests) and indicate whether they all passed or any failed.
We can’t use our memory checking functions to test your program for memory leaks or problems in the heap, because
you’re writing a self–contained program with a main() function. However, some of the tests will use valgrind with the
right arguments to check your program for problems of this type. Since our memory checking functions are incompatible
with valgrind, do not use our memory checking functions in your program, or it will fail these tests.
A.3 Grading criteria
Your grade for this project will be based on:
public tests 85 points
programming style 15 points
As you can see, and as mentioned above, there are only public tests for this project. For you to come up with your
own tests of your program you might have to think about what UNIX commands to run with what arguments, and you
would also need to write your own shell scripts. But while understanding basic UNIX shell script concepts is useful, the
course doesn’t really expect you to have to learn how to write shell scripts. To avoid this, all of the tests for this project
will just be public tests, and those and style will make up your entire project grade.
© 2023 L. Herman; all rights reserved 7
B Project–specific requirements, suggestions, and other notes
• Before starting to code, make sure you understand the lecture examples of process control. If you have questions
about them, ask about them in the TAs’ office hours in advance.
• Of course the program could be written without explicitly using process control (e.g., by using the C library
function system()). But since the entire point of the project is to write a program using process control, you will
not get any credit for the project if you use system() at all anywhere. You must write the project as described.
Also do not use a loop to try to make a process wait for another one to do something. (This is called busy–waiting
and it is very inefficient, compared to correct ways to accomplish this.)
• If any system call fails your program should print some sort of descriptive message saying what didn’t work right
and where. It doesn’t matter what happens after that. The exact wording of the message doesn’t matter and it
doesn’t matter what your code does if this occurs, as long as an explanatory message is printed.
• Any function that allocates memory should test whether the memory allocation was successful, and handle the
situation appropriately if it was not successful. (The appropriate action might just be gracefully exiting the program
after printing an error message, but it at least should not be just crashing).
• As mentioned above, do not use our memory checking functions for this project. Besides being incompatible with
some of the tests that use valgrind, when your code is compiled on the submit server it will not be linked with
memory-functions.o, so your program will not even compile on the submit server if you try to use our memory
functions.
• If you make changes to yargs.c, don’t forget to recompile it to create a new yargs.x before rerunning the public
test shell scripts!
• You cannot run a shell script using gdb (or gede), or valgrind. If you want to debug, run yargs.x under gdb
(compiling of course with -g), then look at the shell script for the test that you want to run the program for, to see
what command–line arguments the public test script is running yargs.x with, and what it is using for its input.
Then run your yargs.x under gdb with the same arguments and input. And similarly, if you want to run valgrind,
look at the shell script that is the test you want to run it on, and run it on the command given in the script.
You can run a program under gdb with command–line arguments and input redirection. For example (suppose this
is after running gdb yargs.x and setting some breakpoints), say we want to run yargs.x with the argument -gcc
-Wall, reading input from the file datafile2. The following command to your running gdb will do this:
run gcc -Wall < datafile2
You can also run valgrind on a program with command–line arguments and using input redirection:
valgrind yargs.x gcc -Wall < datafile2
Some tests will run yargs.x with its standard input coming from a pipe, for instance, an invocation like the
example ls *.c | yargs.x touch. If you want to debug your program in a case like this we recommend
redirecting the output of the commands or commands before yargs.x into a file, then running gdb on yargs.x
with input redirected from that file. Just be sure to use a backslash to disable aliases for any commands that are
being used to generate input for yargs.x (the way the public tests are written takes care of this). In this example,
run \ls *.c > tmpfile, then run gdb yargs.x, and (as mentioned above) at the gdb prompt run < tmpfile.
Comments in the public tests will explain what commands to use if you want to run the debugger on your yargs.x
for that test.
• If you are debugging a program that uses process control to create child processes, note that gdb will by default
continue to trace the execution of the parent process after a fork. However, if you use the gdb command set
follow-fork-mode child before the program creates a child, gdb will trace the child instead. (If needed, set
follow-fork-mode parent will switch back to tracing the parent when subsequent child processes are created.)
If your yargs.x program ends up running child processes with the wrong command–line arguments nothing is
going to work right, and it might be difficult to know why. So if things aren’t working right and you’re not sure
why, set a breakpoint in gdb right before a child process is created, use follow-fork-mode child, set a breakpoint
in the child process right before it runs another program, and use gdb to view the arguments at that point that the
other program is about to be run with. If they’re not right, that is the problem.
© 2023 L. Herman; all rights reserved 8
• Do not write code using loops (or recursion) that has the same effect as any string library functions. If you need
to perform an operation on strings and there is a string library function already written that accomplishes that task,
you are expected to use it, otherwise you will lose credit.
• You will lose credit if you cast the return value of the memory allocation functions. Besides being completely
unnecessary, in some cases this can mask certain errors in code.
• You cannot modify anything in the header files safe-fork.h or split.h or add anything to them, because your
code will be compiled on the submit server using our version of these files.
Your code may not comprise any source (.c) files other than yargs.c, so all your code must be in that file. You
cannot write any header files of your own either.
• For this project you will lose one point from your final project score for every submission that you make in excess
of five submissions, for any reason.
• Make sure none of your program lines have length more than 80 by running your Project #2 line length check
programs on yargs.c!
• Be sure to make frequent backups of your yargs.c source file in a different directory in your course disk
space.
• Recall that all your projects must work on at least half of the public tests (by the end of the semester) in order for
you to be eligible to pass the course. See the project policies handout for full details.
• Recent projects said that if you have a problem with your code and have to come to the TAs’ office hours for
debugging help you must come with tests you have written yourself that illustrate the problem, not the public tests.
However, since you aren’t expected to have to write shell scripts in this project, the TAs will help find bugs with
the public tests in this project, even if you have not written your own tests.
C Academic integrity
Please carefully read the academic honesty section of the syllabus. Any evidence of impermissible cooperation on
projects, use of disallowed materials or resources, publicly providing others access to your project code online, or unauthorized use of computer accounts, will be submitted to the Office of Student Conduct, which could result in an XF for
the course, or suspension or expulsion from the University. Be sure you understand what you are and what you are not
permitted to do in regards to academic integrity when it comes to projects. These policies apply to all students, and the
Student Honor Council does not consider lack of knowledge of the policies to be a defense for violating them. More
information is in the course syllabus– please review it now.
The academic integrity requirements also apply to any test data for projects, which must be your own original work.
Exchanging test data or working together to write test cases is also prohibited.
© 2023 L. Herman; all rights reserved 9