This lab should be done with a partner of your choosing.
The setup procedure for this lab will be similar to previous labs. First, both you and your partner should run setup31 to grab the starting point code for this assignment. Suppose users molly and tejas which to work together. Molly (mdanner1) can start by running
[~]$ setup31 labs/08 tdanner1Once the script finishes, Tejas (tdanner1) should run
[~]$ setup31 labs/08 mdanner1
For the next step only one partner should copy over the starting code
[~]$ cd ~/cs31/labs/08 [08]$ cp -r ~lammert/public/cs31/labs/08/* ./ [08]$ ls Makefile parsecmd.c parsecmd.h tester.cNow push the changes to your partner
[08]$ git add * [08]$ git commit -m "lab 8 start" [08]$ git pushYour partner can now pull the changes. In this case if Tejas wishes to get files Molly pushed, he would run
[~]$ cd ~/cs31/labs/08 [08]$ git pull
// This is the function you used in the shell lab to convert // the comandline string into an array of strings: one per command // line argument. This version uses a fixed-size max length comdline // and argv list. int parse_cmd(const char *cmdline, char **argv); // this is a slightly different version of a command line parsing // function: it dynamically allocates space for the argv list of // strings that it returns to the caller. The bg value is now // passed-by-reference and this functions sets it to either 0 or 1 // depending on if the command line has a & in it or not char **parse_cmd_dynamic(const char *cmdline, int *bg);More details about each of these are described in the "Details and Requirements" section below.
gcc -g -o tester tester.c parsemd.o
#include "parsecmd.h"parsecmd.h contains two function prototypes for the functions you will implement in parsecmd.c.
$ 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 into which will be each each argv[i] string, and the second function dynamically allocates space for both the argv array and for each string of command line argument.
/* * parse_cmd - Parse the command line and build the argv array. * cmdline: the command line string entered at the shell prompt * (const means that the function will not modify the cmdline string) * argv: an array of size MAXARGS of char * * parse_cmd will initialize its contents from the passed * cmdline string. * returns: non-zero if the command line includes &, to * run in the background, or zero if not */ int parse_cmd(const char *cmdline, char *argv[]);This function will initialize the passed argv array to point into substrings that it creates in a global char buffer (initialized to a copy of the passed command line string). The buffer is already declared as static global char array in parsecmd.c:
static char cmdline_copy[MAXLINE];The parse_cmd function will:
For example, if the command line entered is the following
$ ls -l -a &The command line string associated with this entered line is:
" ls -l -a &\n"the copy of it in the cmdline_copy buffer 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 into
the start of its associated token string in the char buffer
(cmdline_copy array):
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 buffer.
Printing out the argv strings in order will list the
ls -l -aThe function should return 1 if there is an ampersand in the command line or 0 otherwise (so, 1 in the above example)
/* * parse_cmd_dynamic - parse the passed command line into an argv array * * cmdline: the command line string entered at the shell prompt * (const means that this function cannot modify cmdline) * bg: sets the value pointed to by bg 1 if command line is run in * background, 0 otherwise (a pass-by-reference parameter) * * returns: a dynamically allocated array of strings, each element * stores a string corresponding to a command line argument * (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. However, it must also determine how many tokens are in the cmdline string, malloc EXACTLY the right number of argv buckets for the particular cmdline string (remember an extra bucket at the end for NULL), and then fore each token it will malloc up exactly enough space for a a char array to store the string corresponding to a command line argument (remember an extra bucket for the terminating '\0' character).
For example, if the cmdline string is:
" ls -l -a \n"This function will malloc up an argv array of
// local var to store dynamically allocated args array of strings
char **args;
args --------->[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 up space for a local copy of the cmdline string
to tokenize if this helps. If you do this, however, your function
must free this copy before it returns; the returned args list should
not point into this copy like the parse_cmd function does, but each
command line argument should be malloced up separately as a distinct
string of exactly the correct size).
This function is more complicated to implement and will likely
require at least more than a single passes 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 (gdb) display buffer
To submit your code, simply commit your changes locally using git add and git commit. Then run git push while in the labs/08 directory. Only one partner needs to run the final push, but make sure both partners have pulled and merged each others changes. See the section on using a shared repo on the git help page.