Question: This is in C and a complete solution to the problem except for handling the < < and >> operators is provided below. You may

This is in C and a complete solution to the problem except for handling the << and >> operators is provided below. You may use any or all of this code in your solution to this assignment. Thanks in advance. The question is published in http://cs2.ist.unomaha.edu/~stanw/173/csci4500/prog1a-173.pdf if it provides the better readability. Or else, here is the question:

Introduction: The purpose of this programming assignment is to give you some experience with processes and input/output at the system-call level in UNIX/Linux. Make certain you read and understand all of the material in the assignment. A significant part of the code for this assignment is already provided, and you may use it if you wish; details appear later in the assignment.

The program you write will be a simple command line interpreter, or shell. It will not have nearly as much functionality as a normal shell. Instead, it will deal only with running processes and handling here documents and output redirection concatenation, both of which are described below.

Operators for Redirecting the Standard Input and Output

The program for this assignment will handle any of four command line operators for redirecting the standard input and output. Specifically, these operators are <, <<, >, and >>, each followed by a word1 . Many programs read input from the standard input file (which is always associated with file descriptor 0) and write output to the standard output file (which is always associated with file descriptor 1).

Normally the file descriptors refer to the same files used by the shell process that started the program. That is, if you enter the command doit to it after a shell prompt, the program doit will be executed with its input coming from the same file used by the shell for its input, and with its output going to the same file used by the shell for its output. That is, file descriptors 0 and 1 in the process executing doit will identify the same files used by the shell process that started doit.

But its possible to redirect the standard input and/or the standard output for the process to different files. There are four possible operators that can be used in commands processed by a valid solution to this assignment, as mentioned above. One of the operators < and << can be used to redirect the standard input, and one of the operators > and >> can be used to redirect the standard output.

The < operator causes the standard input to be redirected to the file named in the word immediately following the operator. So the command cat < stuff will cause the program named cat to be executed with its standard input (that is, file descriptor 0) associated with the open file named stuff. Obviously this will fail if the file named stuff doesnt exist.

The << operator is not as commonly used as the < operator, but it is very useful. The standard input to the process invoked from a command line using the << operator is associated with a here document, which is a temporary file that contains all the lines in the shells input after the command line through, but not including, a line containing only the word that follows the << operator (and an end of line character, of course). The word that marks the end of the here document is often called the limit string. For our purposes, a word is just a sequence of printable non-blank characters; we assume at least one space or tab character will separate each word from the next.

Consider this input to your shell:

cat << EndOfDoc

This is the first line of a here document.

This is the second line of a here document.

This is the third, and last, line of a here document.

EndOfDoc

The output from this would be the three lines beginning with the word This. The limit string in this example is EndOfDoc and it is not part of the here document. The temporary file created by your program to hold the here document while the process executing the cat program is running will necessarily be deleted when the process terminates, or if the process is not successfully started for any reason. More examples of the use of here documents are provided in the sample input for this assignment.

The > operator causes the standard output from a process (that is, file descriptor 1) to be redirected to the file named in the word immediately following the operator. So the command cat in > out will cause the content of the file named in to be written to the file named out. If the file named out did not exist, it would need to be created. If it did exist, then its content would be overwritten. The command will fail, and an appropriate error message should be displayed, if the file named out could not be opened or created.

The >> operator is very similar to the > operator, but there is one major difference. If the file named in the word immediately following the operator exists, then the standard output from the process being executed should be appended to the existing content in the file. Otherwise (that is, if the file did not already exist), >> should behave the same as if > had been used.

It should be obvious that a command line may not include both the < and << operators. Nor may it contain both the > and >> operators. But the command line may include one of the input redirection operators and one of the output redirection operators, in any order. For example, the following input would be acceptable to the shell, assuming the named files and commands were acceptable:

cat < in > out

cat > out < in

echo one two three >> out

doit one two three << MyEOF > stuff

This is a one-line here document.

MyEOF

doit four five six < moreinput > output

Your Programs Actions

Your program will repeatedly do the following things:

1. Read a command line from the standard input. At the end of file, just quit. Also ignore any line that contain only blanks and tab characters.

2. Identify the words on the command line.

3. Verify that the first word is either a path to an executable command, or that it is the name of an executable file in one of the directories identified by the PATH environment variable; if it is neither of these, display an error message and repeat from step 1.

4. Identify any redirection operators specified on the command line (specifically <, <<, >, and >>, each followed by a word). If the << operator is used, create a temporary file containing the here documents content2 . Verify that the combination of operators is acceptable; if not, display an error message, delete any temporary file created to contain the here document, and repeat from step 1. If the limit string for a here document is not found (that is, if the end of file is found instead of the limit string), display an appropriate error message and terminate the entire shell.

5. Create a child process to execute the command (using the fork system call). Verify that fork did not return -1 (which would indicate an error).

6a. In the child process just created:

a. Redirect the standard input and the standard output, if necessary (as directed by command line options, described below).

b. Prepare the argument structure necessary for the execve system call.

c. Invoke execve to begin execution of the child process; if execve returns, print an appropriate error message and exit (using the exit system call).

6b. In the parent process, execute the wait system call to wait for the child process to terminate.

Details Lets consider more detail about each of these actions.

In step 1 you must use the read system call, not any other input mechanism provided by the C library. To avoid extra complexity, assume you are reading commands from a disk file, and read a single byte at a time. The file descriptor to use is 0, which corresponds to the standard input. When read returns 0 you have encountered the end of file. When the single character read is an end of line character (' '), then you have encountered the end of a line. You should echo the input you read; when youre actually reading from the keyboard youll get an extra copy of the input, but when reading from a disk file, youll get a display of the command youre about to process. You may assume that no input line contains more than 100 characters (including the end of line character).

Step 2 is a simple parsing operation. Assume each word is separated from another word by one or more blanks or tab characters (that is, whitespace). We will not worry about quoting arguments with single- or double-quotes or backslashes (as more complete shells will do). Additionally, you may assume that the characters used to specify redirection of input and output (i.e. '>' and '<') will be separated from other words by whitespace. You may assume there will be no more than 16 words in any input line, including the redirection characters. You may use C library functions like strtok to assist in this step. You may also assume no word will be longer than 64 characters, including a null terminator.

Step 3 processes a sequence of words corresponding to a command name, its arguments, and possibly some input/output redirection information. Here we validate the name of the command to be executed (which is in the first word on the command line). If this word includes a '/', then it is a path to the file containing the executable program for the command. Otherwise it is assumed to be the name of an executable file that appears in one of the directories specified in the PATH environment variable. This environment variable contains a colon-separated list of the names of directories the shell is to search for an executable file with the specified name. You can see what this variables contains by typing the command echo $PATH. A possible result of this might be /bin:/usr/bin:/sbin:/usr/local/bin:/usr/games

So if you should enter a command line doit to it (note that doit is not an explicit path, as it does not include '/'), you expect the shell to search first in /bin for doit, then in /usr/bin, and so forth, finally searching in /usr/games (if doit wasnt found in an earlier directory). If the file doit is not found in any of those directories, an error message should be displayed (something like doit not found), and the shell should repeat from step 1.

To determine if the file exists and is executable you should use the access system call. This call has two arguments. The first is the path to a file, and the second is the access mode to the file that is to be checked. In our case, we specifically want to test for executability, so the second argument would be X_OK (which is defined by including the header file unistd.h). If access returns 0, then the file exists and is executable (at least as far as the files permissions indicate). Otherwise, the file does not exist, or is not executable. (Notice that the first argument to access must be the path to the file, not just the file name. As a result, youll need to concatenate a directory name from the PATH environment variable, a slash, and the file name to yield a suitable string for the first argument to access.)

Step 4 is where the redirection operators <, <<, >, and >> are identified and validated. The operator(s) and the following word(s) will need to be saved for use in step 6a. Additionally, the operator(s) and following word(s) will need to be logically removed from the words on the command line that are passed as command line arguments to the child process created in step 6a.

Step 5 is simple: just use the fork system call to create a process, being sure to save the value it returns. As is always the case, check to see if the returned value is 1. If it is, some error occurred (like you got stuck in a loop calling fork!), and you should abandon further execution (by printing an appropriate error message and calling exit.) Step 6a is executed when the fork system call executed in step 5 returns 0. That is, this step is executed only in the child process. The first task, step 6a.a, is to handle redirection of the standard input and output, if requested. Recall that fork provides a child process with the same set of open files present in its parent process. Without redirection, the child will then access the same standard input and output files used by the parent, specifically through file descriptors 0 and 1, respectively. To redirect the standard input to a file or here document we use four steps: Open the file for reading (use the open system call). You will need to use the O_RDONLY flag with the system call. If the open call fails, then either the file doesnt exist, or were not allowed to read it. In any event, we cannot continue, so display an appropriate error message and exit the child process (deleting the temporary file used to contain a here document, if appropriate). If a here document is being processed, unlink the temporary file containing the here document. This will remove the directory entry for the file, but the file (and its contents) will continue to exist all open file descriptors referencing it are closed. If we assume the file descriptor for the input file is in variable fd, then we must arrange for the child process to have that file descriptor in place of file descriptor 0. Two steps achieve this: close file descriptor 0, and dup file descriptor fd. Since the dup system call makes a copy of the specified file descriptor (fd) on the lowest currently unused file descriptor (which is obviously 0 at this point, since we just closed 0), we now have file descriptor 0 associated with the redirected input file. The last step is to close file descriptor fd, since it is no longer needed (file descriptor 0 will remain associated with the file).

Similar steps can be used to redirect output to a file, using file descriptor 1 instead of file descriptor 0. Of course, the file will need to be created if it does not already exist, and errors could be reported (for example, if the file already exists but you are not allowed to write to it). For the > operator you will use the O_CREAT, O_WRONLY, and O_TRUNC flags. For the >> operator you will need to add the O_APPEND flag, but remove the O_TRUNC flag.

In step 6a.b the child process prepares the arguments for the execve system call. The arguments are passed to execve through an array of pointers to the argument strings, with a null pointer provided as the last entry. It is conventionally the case that the first argument (in the array entry with subscript 0) is a pointer to the string representing the name of the program being executed. The first argument to execve is the path to the file to be executed, which was determined in step 3. The second argument is a pointer to the array of argument pointers. The third and last argument to execve is a pointer to an array of strings representing the environment for the program about to be executed. This should usually be the value of the external variable environ. A very simple program illustrates these concepts. It execves the program /bin/echo to display Hello, world! and is available on loki in the file /home/stanw/csci4500/echo_me.c; this directory also contains other sample programs.):

Step 6a.c is very simple. Remember that the execve system call will not normally return; if it does, it indicates that the system detected an error that prevented its successful execution, so you should print an error message and exit.

Step 6b is executed by the parent process (that is, the process executing the shell) only after receiving a non-negative, non-zero value from fork. Here the parent executes the wait system call to delay further execution until the child process terminates. Like most system calls, wait can return -1 to indicate an error. In our program, the only error that might be expected is failure of the execve system call in step 6a.c. So regardless of the value returned by wait, the process (that is, the original shell process) will continue executing.

An example that shows how a program might use the wait system call can be found in the class directory in the file named waitdemo.c.

Notes and Restrictions

You may use sprintf (as illustrated in the last example) to prepare messages to be displayed, but no standard C library functions that result in input or output must be used in your solution. Instead, all input and output must be accomplished using the read and write system calls (with open used to achieve redirection of standard input and output). You may also use the string functions for things like parsing the command line and computing the length of strings (for the write system call). Likewise, use no other standard C library functions related to creating processes.

Evaluation

Your program will be evaluated by testing with a variety of commands. To receive 100 percent, your program must process the entire set of commands correctly. Failure of your program to compile on Loki will result in a grade of no more than 50 percent. Failure to correctly handle a command line with I/O redirection will result in a grade of no more than 75 percent.

Remember, a complete solution to the problem except for handling the << and >> operators is provided below. You may use any or all of this code in your solution to this assignment. Requirements You must write (and test) a C program that functions as a simple shell as just described. Your solution should be a single file of C source code named prog1a.c and a makefile (named makefile or Makefile).

prog1a.c

/*----------------------------------------------------------------------*/

/* A simple shell */

/* */

/* Process command lines each containing one or more words (strings of */

/* non-blank/non-tab characters) separated by blanks or tabs. In each */

/* command, the standard input and standard output may be redirected by */

/* suffixing "< path" and/or "> path" to the end of the command, where */

/* path is the path to a file. The file is created if necessary for */

/* output redirection. The ability to read or write an existing file is */

/* checked; errors are reported as appropriate. */

/*----------------------------------------------------------------------*/

/* Stanley Wileman */

/*----------------------------------------------------------------------*/

/* The shell may be invoked with no arguments by just typing its name */

/* (with a leading "./" in most cases). The standard input and output */

/* of the shell can also beredirected. The following options are */

/* available: */

/* -d display each process termination status */

/* -e echo commands if standard input redirected from a file */

/* -h display help information and exit */

/* -v verbose output (debugging) */

/*----------------------------------------------------------------------*/

#include

#include

#include

#include

#include

#include

#include

#include

#define MAXLINELEN 100 /* max chars in an input line */

#define MAXWORDS 16

#define MAXWORDLEN 64

#define STDINERR "Error reading standard input. "

#define STDINEOF "End of file before end of line on standard input. "

#define NOSUCHFILE "No such file. "

#define CANNOTFORK "Cannot fork. "

#define CANNOTEXEC "Cannot exec. "

#define CANNOTOPEN "Cannot open input file. "

#define CANNOTCREATE "Cannot create output file. "

#define TOOMANYREDIRECTS "Too many < or > redirections. "

#define NOFILENAME "No filename after < or > "

#define THEPROCESSTERMINATED "The process terminated "

#define ABNORMALLY "abnormally. "

#define NORMALLY "normally with exit status "

#define UNKNOWNOPTION "Error: unknown option "

#define UNKNOWNOPERAND "Error: unkown command line operand "

#define LINETOOLONG "Error: input line is too long. "

#define WORDTOOLONG "Error: input word is too long. "

#define TOOMANYWORDS "Error: too many word in command line. "

#define USAGE_1 "Usage: ./shell_r [-d] [-e] [h] [-v] "

#define USAGE_2 " -d ==> display termination status for each process. "

#define USAGE_3 " -e ==> echo command lines read from redirected stdin. "

#define USAGE_4 " -h ==> display this help information. "

#define USAGE_5 " -v ==> enable verbose output (debugging). "

extern char **environ;

char words[MAXWORDS][MAXWORDLEN+1]; /* words on the command line */

int nwds; /* number of words in words array */

int ignore[MAXWORDS]; /* ignore[i] != 0 --> ignore words[i] */

char line[MAXLINELEN+1]; /* current input line */

char *eveargs[MAXWORDS+1]; /* pointers to execve arguments */

char *ifn, *ofn; /* input, output filenames */

char path[PATH_MAX]; /* path to executable file */

int display; /* should we show process termination status? */

int echo; /* echo command lines read from a file? */

int verbose; /* produce verbose output? */

int fromfile; /* does standard input come from a file? */

/*------------------------------------------------------------------*/

/* Get a line from the standard input. Return 1 on success, or 0 at */

/* end of file. If the line contains only whitespace, ignore it and */

/* get another line. If the line is too long, display a message and */

/* get another line. If read fails, diagnose the error and abort. */

/* If standard input is a terminal, prompt for each input line. */

/* If standard input is from a file, do not prompt for input. */

/*------------------------------------------------------------------*/

int GetLine(void)

{

int n; /* result of read system call */

int len; /* length of input line */

int gotnb; /* non-zero when non-whitespace was seen */

char c; /* current input character */

for(;;) {

if (!fromfile) /* prompt for input if appropriate */

write(1,"$ ",2);

gotnb = len = 0;

for(;;) {

n = read(0,&c,1); /* read one character */

if (n == 0) /* end of file? */

return 0;

if (n == -1) { /* read error? */

write(1,STDINERR,strlen(STDINERR));

_exit(1);

}

if (c == ' ') /* end of line? */

break;

if (len >= MAXLINELEN) { /* too many chars? */

len++; /* ignore them, but skip a full line */

continue;

}

if (c != ' ' && c != '\t') /* did we see a non-blank char? */

gotnb = 1;

line[len++] = c; /* save the input */

}

if (len >= MAXLINELEN) {

write(1,LINETOOLONG,strlen(LINETOOLONG));

continue;

}

if (gotnb == 0) /* try again if line is empty */

continue;

line[len] = '\0'; /* end the string with the line. */

return 1;

}

}

/*--------------------------------------------------------------*/

/* Identify "words" on an input line from the array 'line'. */

/* Put a pointer to each null-terminated string representing a */

/* word in the 'words' array. Store the total number of words */

/* the global variable 'nwds'. On success, return 1. */

/* */

/* If there are too many words (more than MAXWORDS), or if a */

/* word is too long (more than MAXWORDLEN characters), display */

/* an appropriate error message to the standard error device */

/* and return 0. */

/* */

/* Except for space and tab characters, none of the characters */

/* in the input line are explicitly tested. It is expected that */

/* the 'line' variable does not include an end of line, but is */

/* instead terminated by a null byte, as are all C strings. */

/*--------------------------------------------------------------*/

int lex(void)

{

char *p; /* current word */

nwds = 0;

p = strtok(line," \t"); /* find first word */

while (p != NULL) {

if (strlen(p) > MAXWORDLEN) { /* validate word length */

write(1,WORDTOOLONG,strlen(WORDTOOLONG));

return 0;

}

if (nwds == MAXWORDS) { /* validate number of words */

write(1,TOOMANYWORDS,strlen(TOOMANYWORDS));

return 0;

}

strcpy(words[nwds],p); /* save pointer to word */

nwds++; /* increment number of words */

p = strtok(NULL," \t"); /* find next word */

}

return 1;

}

/*---------------------------------------------------------------------------*/

/* If the command identified by words[0] is executable, then put the path to */

/* the executable command in the global "path" variable and return 0. */

/* If the command is not executable (for whatever reason), return -1. */

/*---------------------------------------------------------------------------*/

/* The executable command path is determined in the same manner as "normal" */

/* shells. If word[0] contains a slash -- that is, it is an explicit path -- */

/* then it is used as specified. Otherwise, the command named by words[0] is */

/* sought as the name of an executable file in the directories identified by */

/* the PATH environment variable, a colon-separated list of directory names. */

/*---------------------------------------------------------------------------*/

int execok(void)

{

char *p;

char *pathenv; /* copy of PATH env. var. */

if (strchr(words[0],'/') != NULL) { /* does word[0] have a slash? */

strcpy(path,words[0]); /* yes; use it as the path */

return access(path,X_OK); /* return 0 if okay, else -1 */

}

pathenv = strdup(getenv("PATH")); /* make a copy of the PATH env. var */

p = strtok(pathenv,":"); /* find first dir in PATH */

while (p != NULL) {

strcpy(path,p); /* generate possible executable path */

strcat(path,"/");

strcat(path,words[0]);

if (access(path,X_OK) == 0) { /* is it executable? */

free(pathenv); /* yes; free PATH copy */

return 0; /* return success indicator */

}

p = strtok(NULL,":"); /* no, so check next dir in PATH */

}

free(pathenv); /* no more PATH dirs; free PATH copy */

return -1; /* and return failure indicator */

}

/*-------------------------------------------------------*/

/* Write an 8-bit unsigned value 'val' as a 1-3 digit */

/* decimal number to the open file with descriptor 'fd'. */

/*-------------------------------------------------------*/

void write8bu(int fd, unsigned char val)

{

int sig = 0;

char c;

if (val >= 100) { /* val >= 100 ? */

c = '0' + val / 100; /* yes, get hundreds digit */

val %= 100; /* remove it from val */

sig = 1; /* indicate significance start */

write(fd,&c,1); /* display the digit */

}

if (val >= 10 || sig != 0) { /* val >= 10 or significance started? */

c = '0' + val / 10; /* yes, get tens digit */

val %= 10; /* remove it from val */

write(fd,&c,1); /* display it */

}

c = '0' + val; /* always display the units digit */

write(fd,&c,1);

}

/*--------------------------------------------------------------------------*/

/* The main program first checks for the various allowed options. THis is */

/* a simple implementation, and doesn't include many of the extended option */

/* specification syntax features of many applications. */

/* */

/* The program then gets an input line, does lexical processing of the line */

/* to identify the words, and then determines if the first word represents */

/* the name of an executable file. */

/* */

/* The list of words is then checked for occurrences of "> filename" and/or */

/* "< filename", detecting if they are used more than once. If found, the */

/* words array entries that contain these words are marked to be ignored, */

/* by setting the appropriate entries in the ignore array. */

/* */

/* Pointers to the words that are not ignored are copied to the eveargs */

/* array for use with the execve system call. */

/* */

/* A child process is created to execute the command. In the child process, */

/* any input/output redirection requested is set up, and the execve system */

/* call is used to execute the requested program with the given arguments. */

/* */

/* The parent process waits for the child process to terminate. If the -d */

/* option was specified, the child's termination status is also displayed. */

/* */

/* Agressive checking for errors is performed in each of the above steps, */

/* with display of an appropriate message and appropriate subsequent action */

/* taking place. */

/*--------------------------------------------------------------------------*/

int main(int argc, char *argv[])

{

int i, j, n, status;

int cpid;

int fail;

display = 0; /* set default option values */

verbose = 0;

echo = 0;

fromfile = !isatty(0); /* fromfile = 1 if input from a file */

while (argc > 1 && argv[1][0] == '-') { /* check for options */

if (!strcmp(argv[1],"-d")) { /* show proc term status? */

display = 1;

argc--;

argv++;

continue;

}

if (!strcmp(argv[1],"-e")) { /* echo lines from a file? */

echo = 1;

argc--;

argv++;

continue;

}

if (!strcmp(argv[1],"-h")) { /* display help and quit? */

write(1,USAGE_1,strlen(USAGE_1));

write(1,USAGE_2,strlen(USAGE_2));

write(1,USAGE_3,strlen(USAGE_3));

write(1,USAGE_4,strlen(USAGE_4));

write(1,USAGE_5,strlen(USAGE_5));

_exit(0);

}

if (!strcmp(argv[1],"-v")) { /* turn on verbosity? */

verbose = 1;

argc--;

argv++;

continue;

}

write(1,UNKNOWNOPTION,strlen(UNKNOWNOPTION));

write(1,argv[1],strlen(argv[1]));

write(1," ",1);

_exit(1);

}

/*--------------------------------------------------*/

/* Verify there's nothing else on the command line. */

/*--------------------------------------------------*/

if (argc > 1) {

write(1,UNKNOWNOPERAND,strlen(UNKNOWNOPERAND));

_exit(1);

}

/*-----------------------*/

/* Process the commands. */

/*-----------------------*/

while (GetLine()) { /* get a command line */

if (!lex()) /* separate into words */

continue; /* ignore line if it had an error */

if (execok() != 0) { /* if the command is not executable */

write(1,CANNOTEXEC,strlen(CANNOTEXEC)); /* diagnose */

continue; /* try again */

}

ifn = ofn = NULL; /* assume no I/O redirection */

for (i=0;i

ignore[i] = 0;

/*------------------------------*/

/* Check for input redirection. */

/*------------------------------*/

fail = 0;

for (i=1;i

if (strcmp(words[i],"<")) /* skip if not a less-than symbol */

continue;

if (i == nwds-1) { /* must not be last word */

write(1,NOFILENAME,strlen(NOFILENAME));

fail = 1;

break;

}

if (ifn != NULL) { /* already have input redir? */

write(1,TOOMANYREDIRECTS,strlen(TOOMANYREDIRECTS));

fail = 1;

break;

}

ignore[i] = 1;

ignore[i+1] = 1;

ifn = words[i+1];

i++;

}

if (fail)

continue;

/*-------------------------------*/

/* Check for output redirection. */

/*-------------------------------*/

fail = 0;

for (i=1;i

if (ignore[i]) /* skip those words in input redir */

continue;

if (strcmp(words[i],">")) /* skip if not greater-than symbol */

continue;

if (i == nwds-1 || ignore[i+1] != 0) { /* verify filename */

write(1,NOFILENAME,strlen(NOFILENAME));

fail = 1;

break;

}

if (ofn != NULL) { /* already have output redir? */

write(1,TOOMANYREDIRECTS,strlen(TOOMANYREDIRECTS));

fail = 1;

break;

}

ignore[i] = 1;

ignore[i+1] = 1;

ofn = words[i+1];

i++;

}

if (fail)

continue;

/*----------------------------------*/

/* Build a list of args for execve. */

/*----------------------------------*/

eveargs[0] = words[0]; /* always start with words[0] */

for (i=1,j=1;i

if (ignore[i]) /* word already used? */

continue;

eveargs[j++] = words[i];

}

eveargs[j] = NULL; /* mark end of argument list */

if (verbose) {

write(1,"execve args: ",13);

for(i=0;;i++) {

if (eveargs[i] == NULL)

break;

else

write(1,eveargs[i],strlen(eveargs[i]));

write(1," ",1);

}

}

/*----------------------------------------------*/

/* Create a new process to execute the program. */

/*----------------------------------------------*/

cpid = fork(); /* create child process */

if (cpid == -1) { /* and check for failure */

write(2,CANNOTFORK,strlen(CANNOTFORK));

continue;

}

if (cpid == 0) { /* In the child process... */

/*----------------------------------------------*/

/* Setup input file descriptor per redirection. */

/*----------------------------------------------*/

if (ifn != NULL) {

int fd = open(ifn,O_RDONLY); /* open input file */

if (fd < 0) { /* if not successful... */

write(2,CANNOTOPEN,strlen(CANNOTOPEN));

_exit(1); /* child process terminates with failure */

}

close(0); /* map stdin to new file descriptor */

dup(fd);

close(fd);

}

/*-----------------------------------------------*/

/* Setup output file descriptor per redirection. */

/*-----------------------------------------------*/

if (ofn != NULL) {

int fd = open(ofn,O_CREAT|O_WRONLY|O_TRUNC,0644);

if (fd < 0) {

write(2,CANNOTCREATE,strlen(CANNOTCREATE));

exit(1);

}

close(1);

dup(fd);

close(fd);

}

execve(path,eveargs,environ);

write(2,CANNOTEXEC,strlen(CANNOTEXEC));

exit(1);

}

/*--------------------------------------------------*/

/* Parent (shell): wait for the child to terminate. */

/*--------------------------------------------------*/

wait(&status);

/*-------------------------------------------------*/

/* Display exit status and reason for termination. */

/*-------------------------------------------------*/

if (display) {

write(1,THEPROCESSTERMINATED,strlen(THEPROCESSTERMINATED));

if (status & 0xff) /* lo-order byte != 0 */

write(1,ABNORMALLY,strlen(ABNORMALLY)); /* error if non-zero */

else {

write(1,NORMALLY,strlen(NORMALLY));

write8bu(1,(unsigned char)((status >> 8) & 0xff));

write(1,". ",2);

}

}

}

}

Step by Step Solution

There are 3 Steps involved in it

1 Expert Approved Answer
Step: 1 Unlock blur-text-image
Question Has Been Solved by an Expert!

Get step-by-step solutions from verified subject matter experts

Step: 2 Unlock
Step: 3 Unlock

Students Have Also Explored These Related Databases Questions!