This exercise is based on the concepts and examples presented in lectures 1, 2 and 3. This will allow us to revise the some C programming fundamentals and some of the basic system calls (such as fork(...)
and exec(...)
)
First, command line arguments. To create a program that you can run with command line arguments, the main function needs parameters:
int main( int argc, char *argv[] )
where argc is the number of command line arguments given when the program is run, and argv is an array of strings. That is, the command line arguments are all strings.
Write a C program that prints each of its command line arguments on a separate line, e.g.:
week_02 $ cmdargs hello 2 all
cmdargs
hello
2
all
Remember, arrays in C are indexed from 0, so the first command line argument is argv[0] and the last is argv[argc-1].
You should understand how the example programs from lectures 2 and 3 work.
Please copy these examples to your working directory, review, build and run the code.
Review the following example from example04.c:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void){
int i, n = 4;
pid_t childpid;
for (i = 1; i < n; ++i)
if((childpid = fork()) < 0){
/* fork error */
perror("error in fork");
exit(EXIT_FAILURE);
} else if(childpid){
/* parent code */
break;
} else {
/* child code */
}
/* mutual code */
printf("This is process %ld with parent %ld\n",
(long)getpid(), (long)getppid());
return 0;
}
This code Creates 4 processes in chain formation. Make a copy of the program and modify it so that the number of processes is given as a command line argument. E.g.
$ chainargs 3
This is process 19839 with parent 27255
This is process 19841 with parent 19840
This is process 19840 with parent 1
$
You will need to convert argv[1]
from a string to an integer using atoi( argv[1] )
. See man atoi
Next, modify the program again, so that main calls a special function parse_args()
to check that the command lines arguments are correct: that is,
argc == 2
and
atoi( argv[1] ) >= 0
The structure of the program will include:
#define SUCCESS 1
#define FAILURE 0
int parse_args( int argc, char *argv[], int *n ){
if ...
return SUCCESS;
..
return FAILURE;
}
int main( int argc, char *argv[] ){
int i, n;
pid_t childpid;
if( !parse_args( argc, argv, &n ) ){
printf( "Usage: chainargs <number of processes>\n" );
exit(EXIT_FAILURE);
}
...
In general, this is the way programs in this course will deal with command line arguments and you should use this approach in your assignments.
Review the example code from lecture 3:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
pid_t childpid, waitreturn;
int status;
if ((childpid = fork()) == -1) {
perror("The fork failed");
exit(1);
} else if (childpid == 0) {
/* child code */
if (execvp(argv[1], &argv[1]) < 0) {
perror("The exec of command failed");
exit(1);
}
} else
/* parent code */
while(childpid != (waitreturn = wait(&status)))
if ((waitreturn == -1) && (errno != EINTR))
break;
exit(0);
}
Make a copy of the program that will execute the command a number of times, also given by on the command line. E.g:
week_02$ chainexec 2 date +%D
07/25/07
07/25/07
Review the following example from lecture 3:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include "makeargv.h"
int main(int argc, char *argv[]){
char **myargv, delim[] = " \t";
pid_t childpid, waitreturn;
int status;
if (argc != 2) {
fprintf(stderr, "Usage: %s string\n", argv[0]);
exit(EXIT_FAILURE);
}
if ((childpid = fork()) == -1) {
perror("The fork failed");
exit(EXIT_FAILURE);
} else if (childpid == 0) {
/* child code */
if (makeargv(argv[1], delim, &myargv) < 0) {
fprintf(stderr, "Argument array could not be constructed\n");
exit(EXIT_FAILURE);
} else if (execvp(myargv[0], &myargv[0]) < 0) {
perror("The exec of command failed");
exit(EXIT_FAILURE);
}
} else
/* parent code */
while(childpid != (waitreturn = wait(&status)))
if ((waitreturn == -1) && (errno != EINTR))
break;
exit(EXIT_SUCCESS);
}
This example executes a command given as a single string on the command line. It does so by using makeargv
to create an argument vector for exec
.
Build a program called tokenAt
that expects exactly two command line arguments, which if it doesn’t get it prompts the user with a usage string and then exits. The arguments should be a number and a string, in that order. The program will decompose the second argument into an argument array (using space and tabs as delimiters) and then return the token corresponding to the number.
Solutions> tokenAt 0
Usage: tokenAt <number> <string>
Solutions> tokenAt "baz" 0
Usage: tokenAt <number> <string>
Solutions> tokenAt 0 "baz"
Usage: tokenAt <number> <string>
Solutions> tokenAt 1 "baz"
baz
Solutions> tokenAt 6 "this is a long string no?"
no?
Solutions> tokenAt 7 "this is a long string no?"
String contains only 6 tokens
Solutions>
Please review the makeargv function: