$29
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 back-quoted 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 argu-ments. 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 exe-cuting 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 argu-ments 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 dynam-ically 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
• 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
• 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.
• 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 unau-thorized 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