Computer Science 5210
Summer 1999
Programming Assignment 1

Write a C program under Unix that works as follows. It should get the name of a file from the command line. It create a pipe and then fork two processes. One process, the producer, should read the contents of the named file and write them into the pipe. The other process, the consumer, should read from the pipe. The consumer should behave somewhat like the Unix more filter. It should print 20 lines, then print a line asking whether to continue. The user should respond with a carriage return. The producer should read the carriage return and print another 20 lines, continuing like this until the entire file has been printed.

If there are fewer than 20 lines left, the consumer should just print the remaining lines and stop.

Hints

Use the following functions. I give very brief descriptions of them here. The Unix man page gives details if you need them. For example, to find out more about the fork function, use command
  man fork

fork()
fork creates a copy of the current process. Each copy runs as a separate process. fork returns 0 to the child process (the new one), and returns the process id of the child to the calling process. The process id of the child is a nonzero integer.

When a process forks, the child process keeps all open files that the parent has.

To use fork, include header file sys/types.h and unistd.h.

Example:

      int pid = fork();
  

wait(status)
Wait for a child process to stop. The current process is suspended until a child stops. (If a child has already stopped, it will return immediately.) The returned value is the process id (an integer) of the child that has stopped. Status must be the address of a variable. That variable is set to the exit status of the child process that stopped.

Include header file sys/wait.h to use wait.

Example:

      int status, pid;
      pid = wait(&status);
  

fopen(filename,mode)
This opens the file whose name is filename for reading or writing, according to the value of string mode. For reading, mode should by "r". For writing it should be "w" (to empty the file before writing) or "a" (to append what is written to what was already there.)

fopen returns a value of type FILE*.

To use fopen, include header file stdio.h.

Example:

     FILE* outfile = fopen("myoutfile.txt", "w");
  

fdopen(fd,mode)
Open files are kept internally in an array in the process table, and are referred to by their indices in that array. For example, the standard input is normally the file at index 0, and the standard output is normally the file at index 1. The index of an open file is called a file descriptor.

There are functions available that read and write files, and that are just given a file descriptor number. But the functions that work on FILE* values are more convenient to use. fdopen(fd,mode) converts a file descriptor into a FILE* value, so that you can use these higher level functions on them. fdopen returns a value similar to what fopen returns.

Example:

     FILE* outfile = fdopen(fd, "w");
  

pipe(fds)
This creates a pipe, with a write end and a read end. fds should be an array of two integers. fd[0] is set to the file descriptor number of the read end of the pipe, and fd[1] is set to the file descriptor number of the write end of the pipe. (On many Unix systems, pipes are actually bidirectional, and you can read or write to each end. On systems where pipes are unidirectional, you read from fd[0] and write to fd[1].)

pipe returns 0 if the pipe is successfully created and -1 if pipe creation failed.

To use pipe, include header file unistd.h.

Example:

     int fds[2];
     FILE *pipein, *pipeout;

     if(pipe(fds) != 0) {
       ...  (no pipe created)
     }
     else {
       pipein  = fdopen(fd[0], "r");
       pipeout = fdopen(fd[1], "w");
     }
  

putc(c,f)
Write character c to file f. Here, f should have type FILE*. You can also use fprintf to print to a file.

getc(f)
Return the next character from file f. Here, f should have type FILE*. If f is at the end of a file, getc returns EOF.

Note: getc returns a value of type int, not of type char.

fclose(f)
Close file f. Here, f should have type FILE*. To close a file descriptor fd, use close(fd).
You should close the files that you have open when you are done with them. It is only when the producer closes the write end of the pipe that the consumer knows there is nothing more to come.

main(argc,argv)
When main is called, it is given two parameters. The first, argc, is the number of command line arguments, including the command itself. The second, argv, is an array of strings (so argv has type char**) holding the parts of the command line. For example, if you type command
      myprog emu ostrich
  
then the main program in myprog will be called with argc = 3, argv[0] = "myprog", argv[1] = "emu" and argv[2] = "ostrich".