Roger L. Kraft

CS 30200/ECE 46810 - Programming Assignment 2

In this assignment you will implement a small part of a shell program. You will write a program that implements the "pipe" and "I/O redirection" features of a shell. This assignment makes use of files contained in this zip file. The ideas and code used in this assignment are covered in Inter-Process_Communication.zip This assignment is due Monday, March 28.

A "pipe" is an operating system feature that lets one process communicate with another process. Pipes are created by calling the Windows operating system function CreatePipe(), but users of Windows can have the shell program cmd.exe create a pipe for them between two processes. In this assignment you will write a "baby shell" program that calls CreatePipe() and CreateProcess() to build a pipeline between two processes chosen by the user of your shell program.

In the zip file there is a program called bsh_demo.exe that is a demonstration version of the baby shell that you need to write. This program takes a "command-line" as its command-line arguments. That is, you run the bsh_demo.exe program with a command-line like this one.

   hw2> bsh_demo.exe find \"pipe\" ^< hw2.html ^| sort /r ^> result.txt

This command-line has nine command-line arguments for the bsh_demo.exe program. These command-line argument simulate a shell command-line. These arguments tell bsh_demo.exe to create a pipeline between the find.exe program and the sort.exe program. The find.exe program has a parameter, the string "pipe", and the program should have its stdin redirected to the file hw2.html. The sort.exe program should have its stdout redirected to the file result.txt. The sort.exe program also has a singe command-line argument of its own, /r. So the shell command that is being simulated above would be.

   hw2> find "pipe" < hw2.html | sort /r > result.txt

You can type this command directly in to the cmd.exe shell. The result should be the same as the above execution of bsh_demo.exe.

The "command-line" passed to bsh_demo.exe looks kind of strange because it has several special characters in it. We are using command-line arguments to simulate a command-line. The problem is that we want command-line special characters, like < or >, to be command-line arguments to bsh_demo.exe. In order to have the cmd.exe shell ignore what would otherwise be special characters for it, we put an "escape character", the ^, in front of the special characters like < and >. Similarly, we put the escape character in front of the pipe character, |, so that cmd.exe will not treat it as the pipe symbol. We also need to escape the quotes around the string "pipe" but, for whatever weird reason, we use a different escape character here. We escape the quotes using \.

In general, here is the syntax for the command-line arguments to bsh_demo.exe.

   bsh_demo program1 [arg_list] [^< in_file] ^| program2 [arg_list] [^> out_file]

The program1 and program2 arguments are both required. The argument lists for program1 and program2 are optional and they can be arbitrarily long. The in_file and out_file arguments are also optional (but must be accompanied by the escaped redirection symbols). The escaped pipe symbol, ^|, is required. If you need any quotes in either of the two arg_lists, then the quotes must be escaped.

The semantics (meaning) of the above syntax is as follows. The bsh_demo process shall:

  1. create a process from the program1.exe program and give it whatever command-line arguments are present in the first optional arg_list,
  2. create a process from the program2.exe program and give it whatever command-line arguments are present in the second optional arg_list,
  3. create a pipe between the standard output of the program1 process and the standard input of the program2 process,
  4. if the in_file option is present, redirect the standard input of the program1 process to the given input file, otherwise, let the program1 process inherit the standard input of the bsh_demo process,
  5. if the out_file option is present, redirect the standard output of the program2 process to the given output file, otherwise, let the program2 process inherit the standard output of the bsh_demo process,
  6. wait for the program2 process to terminate.

Here are some examples to think about (and try).

This example

   hw2> bsh_demo doubleN ^| remove_vowels

means that bsh_demo.exe should create a pipe between the doubleN.exe and remove_vowels.exe processes. The doubleN.exe process should inherit standard input from bsh_demo.exe. The remove_vowels.exe process should inherit standard output from bsh_demo.exe.

This example

   hw2> bsh_demo doubleN ^< hw2.html ^| remove_vowels

means that bsh_demo.exe should create a pipe between the doubleN.exe and remove_vowels.exe processes. The doubleN.exe process should have its standard input redirected to the file hw2.html. The remove_vowels.exe process should inherit standard output from bsh_demo.exe.

This example

   hw2> bsh_demo doubleN 5 ^< hw2.html ^| remove_vowels

is similar to the previous one but here the doubleN.exe process should be passed a single command-line argument, 5, when bsh_demo.exe calls CreateProcess().

In this example

   hw2> bsh_demo doubleN 5 ^< hw2.html ^| remove_vowels ^> result.txt

the only change is that now the remove_vowels.exe process should have its standard output redirected to the file result.txt (which should be created automatically if it doesn't already exist).

Here is a tricky example. The two commands

   hw2> bsh_demo doubleN 5 ^< hw2.html ^| remove_vowels ^> result.txt
   hw2> bsh_demo doubleN 5 ^| remove_vowels < hw2.html > result.txt

should do essentially the same thing. Why? In each case, who is doing what I/O redirection? (Draw pictures! Your pictures should include the four processes cmd.exe, bsh_demo.exe, doubleN.exe, and remove_vowels.exe, a pipe, and the two files hw2.html and result.txt.)

Write a program bsh.c that acts like bsh_demo.exe. Your program will need to:

  1. "parse" its command-line arguments,
  2. open the input and output files (if needed),
  3. create the pipe,
  4. create the first child process with
    • stdin redirected to the input file or inherited from the parent,
    • stdout redirected to the pipe's input,
    • stderr inherited from the parent,
  5. create the second child process with
    • stdin redirected to the pipe's output,
    • stdout redirected to the output file or inherited from the parent,
    • stderr inherited from the parent,
  6. wait for the second child to terminate.

You're main source of code for this program is the file Pipeline.c from the "9. Child to Child Pipeline" sub-folder of Inter-Process_Communication.zip. You should also look at the file CreateChildRedirectStdinStdout.c from the "7. Redirect Child's Standard Streams" sub-folder.

In the zip file there are a number of filter programs that you can use when experimenting with the demo program or testing your program. There is also a file example_command_lines.cmd that shows you a number of good examples for testing your program. I will use a file similar to this when grading your assignment.

In the zip file there is one example program, list_arguments.c, that can be used both to test your program and also to experiment with command-line arguments and see exactly how the cmd.exe shell treats special characters and their escape characters. See the notes inside list_arguments.c for some good examples.

A few things to watch out for.

You must make the handles to the input and output files inheritable, otherwise the child processes will not see the files. But make sure that only program1 inherits the handle to the input file and only program2 inherits the handle to the output file. Similarly for the two pipe handles. On the other hand, the handles to the parent's stdin, stdout, and stderr are always inheritable.

Be sure to error check all your Windows operating system function calls. If one of those function calls fails silently, you will have a very hard time figuring out what went wrong. Error check all of them, even the CloseHandle() calls.

You do not need to check the syntax of the command-line arguments in your version of bsh.c. Your program can assume that every simulated "command-line" is correct and your program is allowed to fail spectacularly if there is a syntax error. The bsh_demo.exe program does do some syntax checking, to make it easier to experiment with the program and learn how it should work. You do not need to emulate the demo program's error messages (though you are welcome to try; it's not really all that hard).

Turn in a zip file called CS302Hw2Surname.zip (where Surname is your last name) containing your C program bsh.c.

This assignment is due Monday, March 28.

Here are references to relevant functions in the C Library and the Windows API.