For this lab, you will continue working with your shell lab partner: the partner list
Expectations for Working with Partners
Both you and your partner should do:
cd ~/cs31/labs
git clone [your_Lab09_URL]Then cd into your Lab09-you-partner subdirectory.
Makefile parsecmd.c parsecmd.h tester.c QUESTIONNAIRE
If this didn't work, or for more detailed instructions see the the Using Git page.
As you and your partner work on your joint solution, you should be frequently pushing changes to and pulling changes from the master into your local repos.
In this assignment you and your partner will implement a C library and write code to test the functions in your library. You will be implementing the parsecmd library, one function of which you used in your shell lab program. You should be able to use your compiled library (parsecmd.o) in place of the one I gave you in lab 7.
// This is the function you used in the shell lab to convert // the command line string into an array of strings, one per command // line argument. With this version command lines are capped at a // maximum length and a maximum number of arguments. int parse_cmd(const char *cmdline, char *argv[], int *bg); // This is a different version of a command line parsing function: // it dynamically allocates space for the argv array of strings // that it returns to the caller. char **parse_cmd_dynamic(const char *cmdline, int *bg);
More details about each of these are described in the "Details and Requirements" section below.
You will implement a parsecmd library that contains functions to parse a command line string into its individual command line arguments, and construct an argv list of strings from the command line args. Your library functions can then be used by other programs by #including your parsecmd.h file and linking in your parsecmd.o binary on the gcc command line:
gcc -g -o tester tester.c parsecmd.o
#include "parsecmd.h"
$ cat foo.texThese functions will be passed the string:
"cat foo.tex\n"And will parse the command line string into the argv array:
argv [0] ---->"cat" argv [1] ---->"foo.tex" argv [2] ----| (NULL)The main difference between the two functions is that the first uses a single statically declared char array, while the second dynamically allocates space for both the argv array and the strings it points to.
/* * parse_cmd - Parse the command line and build the argv array. * * cmdline: the command line string entered at the shell prompt * argv: an array of size MAXARGS of char * * parse_cmd will initialize its contents from the passed * cmdline string. * The caller should pass in a variable delcared as: * char *argv[MAXARGS]; * (ex) int bg = parse_cmd(commandLine, argv); * * argv will be filled with one string per command line * argument. The first argv[i] value following the last * command line string will be NULL. For example: * ls -l argv[0] will be "ls" argv[1] will be "-l" * argv[2] will be NULL * for an empty command line, argv[0] will be set to NULL * bg: pointer to an int that will be set to 1 if the last * argument is '&', 0 otherwise. So bg will be set to 1 * if the command is meant to be run in the background. * * returns: -2 if cmdline is NULL * -1 if the command line is empty * 0 to indicate success */ int parse_cmd(const char *cmdline, char *argv[], int *bg);This function will initialize the passed-in argv array to point to substrings that it creates in a global char array (initialized to a copy of the passed-in command line string). This global array is already declared in parsecmd.c:
static char cmdline_copy[MAXLINE];The parse_cmd function will:
For example, if the command line entered is the following:
ls -1 -a &The command line string associated with this entered line is:
" ls -l -a &\n"And the copy of it in cmdline_copy looks like:
cmdline_copy 0 | ' ' | 1 | ' ' | 2 | 'l' | 3 | 's' | 4 | ' ' | 5 | ' ' | 6 | '-' | 7 | 'l' | 8 | ' ' | 9 | ' ' | 10 | '-' | 11 | 'a' | 12 | ' ' | 13 | '&' | 14 | '\n'| 15 | '\0'|Your function will TOKENIZE this string and set each argv array bucket to point to the start of its associated token string in cmdline_copy:
0 1 2 3 ------------------------ argv | * | * | * | * | ---|-----|-----|-----|-- cmdline_copy 0 | ' ' | | | | | 1 | ' ' | | | | | 2 | 'l' |<---------- | | ---- 3 | 's' | | | (NULL) 4 | '\0'| | | 5 | ' ' | | | 6 | '-' |<---------------- | 7 | 'l' | | 8 | '\0'| | 9 | ' ' | | 10 | '-' |<----------------------- 11 | 'a' | 12 | '\0'| 13 | '&' | 14 | '\n'| 15 | '\0'|Note the changes to the cmdline_copy string contents and the assignment of argv bucket values into different starting points in the char array.
/* * parse_cmd_dynamic - parse the passed command line into an argv array * * cmdline: the command line string entered at the shell prompt * bg: will set value pointed to 1 if command line is run in * background, 0 otherwise (a pass-by-reference parameter) * * returns: a dynamically allocated array of strings, exactly one * bucket value per argument in the passed command line * the last bucket value is set to NULL to signify the * end of the list of argument values. * or NULL on an error * * The caller is responsible for freeing the returned * argv list. */ char **parse_cmd_dynamic(const char *cmdline, int *bg);This function will find tokens much like the previous version does. However, it must also determine how many tokens are in the cmdline string and malloc EXACTLY the right number of argv buckets for the particular cmdline string (remember an extra bucket at the end for NULL). For each token, it must malloc exactly enough space for a char array to store the token as a string (remember an extra bucket for the terminating '\0' character).
For example, if the cmdline string is:
" ls -l -a \n"This function will malloc an argv array of
// local var to store dynamically allocated args array of strings char **argv; argv --------->[0]-----> "ls" [1]-----> "-l" [2]-----> "-a" [3]-----| (NULL)Your function cannot modify the cmdline string that is passed in to it. But you may malloc space for a local copy of the cmdline string to tokenize if this helps. This may allow you to reuse some of the code you wrote for the parse_cmd function. If you use this approach, your function must free this copy before it returns; the returned args list should not point into this copy like with parse_cmd. Instead each command line argument should be malloced separately as a distinct string of exactly the correct size.
This function is more complicated to implement and will likely require more than a single pass through the chars of the command line string.
cat foo.txt blah.txt & cat foo.txt blah.txt& cat foo.txt blah.txt &TEST that your code works for command lines with any amount of whitespace between command line arguments
strlen, strcpy, strchr, strstr, isspaceHere is an example of using strstr and modifying a string to create a substring:
int i; char *ptr, *str; str = malloc(sizeof(char)*64); if(!str) { exit(1); } ptr = strcpy(str, "hello there, how are you?"); if(!ptr) { exit(1); } ptr = strstr(str, "how"); if(ptr) { printf("%s\n", ptr); // prints: how are you? ptr[3] = '\0'; printf("%s\n", ptr); // prints: how } else { printf("no how in str\n"); }strstr may or may not be useful in this assignment, but you will need to create token strings in a way that has some similarities to this example.
"hello there & how are you?"gets parsed into an argv list as:
argv[0]---->"hello" argv[1]---->"there" argv[2]----| (NULL)
(gdb) display ptr (gdb) display i
Here is Sample Output from a run of my solution. Notice how it handles whitespace chars and parsing commands with & in them. Also note that each argv string is printed between # characters so that you can see if you are incorrectly including any whitespace characters in an argument string result.
With every lab assignment is a file named QUESTIONNAIRE for you to fill out and submit with your lab solution. In this file you will answer some questions about the lab assignment. You should fill this out and submit it with your lab solution.
NOTE: also email your professors after you submit to let them know that you have submitted a solution to this extra credit lab.
From one of your local repos (in your ~you/cs31/labs/Lab9-partner1-partner2 subdirectory):
git push
git add parsecmd.c git add QUESTIONNAIRE git commit git pushAnother likely source of a failed push is that your partner pushed, and you have not pulled their changes. Do a git pull. Compile and test that your code still works. Then you can add, commit, and push.
If that doesn't work, take a look at the "Troubleshooting" section of the Using git page. You may need to pull and merge some changes from master into your local. If so, this indicates that your partner pushed changes that you have not yet merged into your local. Anytime you pull into your local, you need to check that the result is that your code still compiles and runs before submitting.