Homework #4¶
Due: Friday, October 27th at 11:59pm
This homework is intended to server as an introduction to C programming language. This assignment is not meant to cover everything in the C language. It solely serves as a way for you to get familiar with working with C, continue learning about shell scripting.
CS Linux Machine¶
You will need access to an Linux based machine when working on your homework assignments. You should not test your programs on macOS or Windows Linux because these operating systems do not provide all utility commands necessary for completing this and possibly future assignments. Additionally, if they do provide a command then it may not contain all options that a Unix-like system provides. We will use and grade all assignments on the CS Linux machines and all programming assignments must work correctly on these machines. However, you can work locally on a Unix or Unix-like machine but ensure that you test your final solutions on a CS Linux machine.
Please follow the instructions provided here
Creating Your Private Repository¶
For each assignment, a Git repository will be created for you on GitHub. However, before that repository can be created for you, you need to have a GitHub account. If you do not yet have one, you can get an account here: https://github.com/join.
To actually get your private repository, you will need this invitation URL:
HW4 invitation (Please check the Post “Homework #4 is ready” Ed)
When you click on an invitation URL, you will have to complete the following steps:
You will need to select your CNetID from a list. This will allow us to know what student is associated with each GitHub account. This step is only done for the very first invitation you accept.
Note
If you are on the waiting list for this course you will not have a repository made for you until you are admitted into the course. I will post the starter code on Ed so you can work on the assignment until you are admitted into the course.
You must click “Accept this assignment” or your repository will not actually be created.
After accepting the assignment, Github will take a few minutes to create your repository. You should receive an email from Github when your repository is ready. Normally, it’s ready within seconds and you can just refresh the page.
- You now need to clone your repository (i.e., download it to your machine).
Make sure you’ve set up SSH access on your GitHub account.
For each repository, you will need to get the SSH URL of the repository. To get this URL, log into GitHub and navigate to your project repository (take into account that you will have a different repository per project). Then, click on the green “Code” button, and make sure the “SSH” tab is selected. Your repository URL should look something like this: git@github.com:mpcs51082-aut23/hw4-GITHUB-USERNAME.git.
If you do not know how to use
git clone
to clone your repository then follow this guide that Github provides: Cloning a Repository
If you run into any issues, or need us to make any manual adjustments to your registration, please let us know via Ed Discussion.
Empty Repository?¶
One of the main goals for this course is to understand the Unix filesystem and creating directories and files within it. Moving forward in the course, it will be your responsibility to create the directories and files for the programming problems. You must also correctly commit them to your repository.
Make sure you create the directory structure exactly as stated in each problem. For this assignment, we won’t have sub-folders as previous assignments (with the exception of a testing directory that will be provided later). The root directory of your repository must contain the following items
parse.h
parse.c
Programming Problem¶
This assignment is intended to help you get prepared for starting your shell application in the next assignment. Specifically, you will implement functions that parse command line and retrieve information about them (e.g., the total number of arguments). For example,
$ ls -la ~/mpcs51082-aut23
The command line is ls -la ~/mpcs51082-aut23
, where the command is ls
and has two arguments: -la
, and ~/mpcs51082-aut23
.
To get started, copy the following code into the parse.h
file:
#ifndef PARSE
#define PARSE
//Holds information about the number of foreground and background jobs.
typedef struct shellinfo {
int fg; // Total number of foreground jobs
int bg; // Total number of background jobs
}shellinfo_t;
// Function prototypes
#endif
In the next few sections, we discuss the functions you will implement that will be part of the parse library (i.e., parse.h
and parse.c
). Please note that we are not building an executable for this assignment. You are only implementing functions in
the parse library. However, you should test the parse functions by creating a main.c
file and calling these functions with different arguments. We provide sample test cases in each section.
compute_num_args
function¶
Inside your parse.h
, copy the following function prototype:
/*
* compute_num_args - computes the number of arguments in the provided command line string
*
* line: a string that represents text from a shell's command line
*
* Assumptions: You can assume the line does not contain more than a single command (i.e., we do not have "&" or ";" in the line string).
*
* Returns: the total number of arguments for the command including the command name
*/
int compute_num_args(const char *line);
This function counts the total number of arguments including the program name. For example, if line ="ls -la ~/mpcs51082-aut23"
then compute_num_args
will return 3
. For this function, you
can assume there will not be multiple commands executed on a single line (e..g, ls ; echo -e hw & cd ..
will never be given). Here are few test-cases to try
printf("%d\n",compute_num_args("ls -la ~/mpcs51082-aut23")); // prints 3
printf("%d\n",compute_num_args("cd ..")); // prints 2
printf("%d\n",compute_num_args("ls")); // prints 1
Implement the actual function definition inside parse.c
and only include the above function comment and header inside parse.h
.
find_arg
function¶
Inside your parse.h
, copy the following function prototype:
/*
* find_arg - finds the argument at a specific index from within the command line string
*
* line: a string that represents text from a shell's command line
*
* index: the index of the argument that is being requested.
*
* argument: the string to copy the requested argument into.
*
* Assumptions: O-indexing is assume. the parameter ("argument") is big enough to hold its value.
* You can assume the line does not contain more than a single command (i.e., we do not have "&" or ";" in the line string).
* Returns: the following error codes based on execution:
* 0 - success. Was able to find the argument as the specified index
* 1 - failure. Index was not valid (i.e., not within the range of 0 to maximum number of arguments in the line)
*/
int find_arg(const char *line, const int index, char *argument);
This function finds a specific argument on the command line and copies the argument into the argument
parameter. For example, if line ="ls -la ~/mpcs51082-aut23"
then find_arg
at index=0
will copy ls
and will return 0
to indicate
it was able to find the argument. Otherwise, if find_arg
is given an invalid index (i.e., outside the bounds of the line
arguments) then the function returns 1
. For this function, you
can assume there will not be multiple commands executed on a single line (e..g, ls ; echo -e hw & cd ..
will never be given). Also you can assume argument
will always be big enough to hold the argument being copied. Here are few test-cases to try
char argument[10];
printf("%d\n",find_arg("ls -la ~/mpcs51082-aut23",0,argument)); // prints 0
printf("%s\n", argument); // prints ls
printf("%d\n",find_arg("ls -la ~/mpcs51082-aut23",1,argument)); // prints 0
printf("%s\n", argument); // prints -la
printf("%d\n",find_arg("ls -la ~/mpcs51082-aut23",10,argument)); // prints 1
Implement the actual function definition inside parse.c
and only include the above function comment and header inside parse.h
.
parse_lines
function¶
Inside your parse.h
, copy the following function prototype:
/*
* parse_lines - Counts the total number of background and foreground jobs in the array of lines provided.
*
* lines: an array of strings that contain strings representing command line strings
*
* lines_len: the length of the lines array.
*
* Assumption: You can assume that the command line strings will only contain "&" and ";" characters to run multiple commands on a single line.
*
* Returns: a shellinfo_t with the total counts of the the number of background and foreground jobs.
*/
shellinfo_t parse_lines(const char*lines[], const int lines_len);
This function determines the total number foreground and background jobs across all command lines within the array of strings. This function uses the shellinfo_t
type to return the total number of foreground and background jobs. For example, if given the following
const char *lines2[2] = {"ls -la ~/mpcs51082-aut23 & cd ..; ls",
"echo -e \"Hello\""};
results = parse_lines(lines2,2);
printf("fg=%d, bg=%d\n",results.fg, results.bg);
This will return fg=3, bg=1
because:
ls -la ~/mpcs51082-aut23
is background jobs since&
follows the command.cd ..
is a foreground job since it ends in a;
.ls
is the last command on the line and it is a foreground job since it does not end in a&
echo -e \"Hello\"
is a foreground job.
You can assume for this problem the lines will only contain ;
and &
to represent running multiple commands on a single line. Here are few test-cases to try
shellinfo_t results;
const char *lines1[1] = {"ls -la ~/mpcs51082-aut23 & cd .. "};
results = parse_lines(lines1,1);
printf("fg=%d, bg=%d\n",results.fg, results.bg); //prints fg=1,bg=1
const char *lines2[2] = {"ls -la ~/mpcs51082-aut23 & cd .. ",
"echo -e \"Hello\";ls;ls;cat file.txt > out.txt &"};
results = parse_lines(lines2,2);
printf("fg=%d, bg=%d\n",results.fg, results.bg); // prints fg=4,bg=2
const char *lines3[3] = {"ls -la ~/mpcs51082-aut23 & cd .. ; ./crawler &",
"echo -e \"Hello\";ls;ls;cat file.txt > out.txt &",
"cd .."};
results = parse_lines(lines3,3);
printf("fg=%d, bg=%d\n",results.fg, results.bg); // prints fg=5,bg=3
Implement the actual function definition inside parse.c
and only include the above function comment and header inside parse.h
.
Testing¶
You can test your code by implementing a main.c
file with a main
function. You can include the parse.h
file to gain access to the above functions for testing. Do not submit the main.c file to your repository!. This is only for testing purposes.
Professor Samuels will provide additional test cases later in the week; however, we want you to first do your own testing to learn how to become better testers before providing you with more test cases.
Grading¶
Programming assignments will be graded according to a general rubric. Specifically, we will assign points for completeness, correctness, design, and style. (For more details on the categories, see our Assignment Rubric page.)
The exact weights for each category will vary from one assignment to another. For this assignment, the weights will be:
Completeness: 80%
Correctness: 20%
Design/Style: 10%
Submission¶
Before submitting, make sure you’ve added, committed, and pushed all your code to GitHub. You must submit your final work through Gradescope (linked from our Canvas site) in the “Homework #4” assignment page via two ways,
Uploading from Github directly (recommended way): You can link your Github account to your Gradescope account and upload the correct repository based on the homework assignment. When you submit your homework, a pop window will appear. Click on “Github” and then “Connect to Github” to connect your Github account to Gradescope. Once you connect (you will only need to do this once), then you can select the repository you wish to upload and the branch (which should always be “main” or “master”) for this course.
Uploading via a Zip file: You can also upload a zip file of the homework directory. Please make sure you upload the entire directory and keep the initial structure the same as the starter code; otherwise, you run the risk of not passing the automated tests.
Note
For either option, you must upload the entire directory structure; otherwise, your automated test grade will not run correctly and you will be penalized if we have to manually run the tests. Going with the first option will do this automatically for you. You can always add additional directories and files (and even files/directories inside the stater directories) but the default directory/file structure must not change.
Depending on the assignment, once you submit your work, an “autograder” will run. This autograder should produce the same test results as when you run the code yourself; if it doesn’t, please let us know so we can look into it. A few other notes:
You are allowed to make as many submissions as you want before the deadline.
Please make sure you have read and understood our Late Submission Policy.
Your completeness score is determined solely based on the automated tests, but we may adjust your score if you attempt to pass tests by rote (e.g., by writing code that hard-codes the expected output for each possible test input).
Gradescope will report the test score it obtains when running your code. If there is a discrepancy between the score you get when running our grader script, and the score reported by Gradescope, please let us know so we can take a look at it.