CS 50 Software Design and Implementation

Lab3

C Programming Lab

Lab3 comprises four C programming problems. They start easy and get progressively more difficult.

This lab will cover many aspects of the C language discussed in class: structures, pointers, bitwise operations, string handling, file operations, reading input from the command line, writing to the display amongst others.

These exercises will help develop your C programming skills. Grading will focus on the correctness of your solutions, but will also consider good coding style - including consistent formatting, selection of identifier names, and use of meaningful comments.

Submitting assignment:

Please make sure that the labs3 directory contains a simple text file, named README, describing anything “unusual” about how your solutions should be located, executed, and considered.

Coding style: Please put comments in your programs to help increase its understanding. Include a header in each C file including the file name; brief description of the program; inputs; and outputs. It is important you write C programs defensively; that is, you need to check the program’s input and provide appropriate warning or error messages back to the user in terms of the program usage. If you call functions from your code make sure you check the return status (if available) and print out meaningful error messages and take suitable action.

Grading: Grading rubric for lab.

Complete the following questions four questions.

____________________________________________________________________________

1) echo.c: Write a program to implement the echo shell command. Include the -n option if the user enters it on the command line. We have used echo in class - type “man echo” for details on the command and its usage, if needed. Code defensively, for example, what if a user enters the wrong switch?

2) network.c: This question relates to the Internet and how your laptop or Linux machine you are logged on to is addressed in the global network. This question introduces bitwise operations for manipulating bits. It also allows you to call a bash script (that you will also write) from your C program. The topic of IP addressing and how it works is important to socket programming (which we will look at in Lab7 and use for the project).

Write a program that takes in a full IP address (e.g., 192.168.5.10) and a network prefix (e.g., 24 bits, written by convention as \24, representing the subnet mask 255.255.255.0) and prints out the network address (e.g., 192.168.5.0). In this case the user enters the full IP address and mask, and the program computes the network address. We call this the interactive mode of the program. Make sure you code defensively taking care of informing the user if incorrect input is provided, etc.

Extend your program so that it also has the option of using the real full IP address and mask from the Linux host machine you are logged onto. We call this the automatic mode of the program. Here, the user does not enter any address or mask details. Rather, the program get the information it needs by interacting with the shell through calling a bash script to get the real information from the Linux system.

Write a script called getadr.sh that uses the shell command “ifconfig eth0” to get the actual full IP address and subnet mask. ifconfig is a shell command to configure network interface parameters but we will use it only to just read the IP address information. Type man ifconfig to learn about this new command. You can type “ifconfig -a” to see all the network data. The “ifconfig eth0” command gives you the information on the Ethernet line card that your host computer uses. So here we assume you are logged on to a Linux machine which uses Ethernet.

Your script getadr.sh should take the filename address.dat as an input parameter. The script should use ifconfig to get the full Internet address and mask and store them in file address.dat. Your C program will read the data from this file to compute the network address.

Your C program can call your script getadr.sh with the file name address.dat using the “system” library call (type man system – pass a command to the shell). Your C program should then open the file address.dat using fopen and read the address and mask. It should then use the address and mask to computer the network address.

A host’s (i.e., computer) full IP address (e.g., 192.168.5.10) is made up of a four byte address each byte of the address separated by a period “.” The full IP address is made up of a network address and host potion. You use the subnet mask or prefix (which in essence are same thing but different format) and the bitwise AND (&) operation with the full network address to compute the network address. See the example below to see how the bits are “anded” together (full IP address & Subnet Mask) to produce the network address

The example below from wikipedia shows the breakdown of the IP address. For more information of the structure of IP addresses and what is called subnetting see IP address.


                      dot-decimal address       binary representation

Full IP Address         192.168.5.10            11000000.10101000.00000101.00001010
Subnet Mask             255.255.255.0           11111111.11111111.11111111.00000000

\\ and these bits to get the Network Address. Note, the prefix could be used
\\ instead of subnet mask - they represent the same thing.

Network Address         192.168.5.0             11000000.10101000.00000101.00000000

\\ Host and prefix

Host Portion            0.0.0.10                00000000.00000000.00000000.00001010
Prefix                  /24                     11111111.11111111.11111111.00000000

Your program will operate in two modes:

Both the interactive and automatic modes print out the same information but get input from different sources - the user for the interactive mode and the system for the automatic mode.

Here is how your program should perform:


[atc@Macintosh-10 getipadress] ./network

Menu
i - to print the IP address details with user input
a - to print the IP address details automatically
e - to exit the program

enter command: i

Enter host address <x.y.z.t>: 129.170.213.202

Enter prefix: 24

Addresses:
Full IP Address:      129.170.213.202
Subnet Mask           255.255.255.0
Network Address:      129.170.213.0
Prefix:               /24
Host Portion            0.0.0.202

Menu
i - to print the IP address details with user input
a - to print the IP address details automatically
e - to exit the program

enter command: a

Addresses:
Full IP Address:      129.170.213.202
Subnet Mask           255.255.255.0
Network Address:      129.170.213.0
Prefix:               /24
Host Portion            0.0.0.202

Menu
i - to print the IP address details with user input
a - to print the IP address details automatically
e - to exit the program

enter command: e

.. exiting

3) sort.c: Write a program to implement the sort shell command but limited to sorting numbers stored in a file given as an input argument - just like sort. The sort command should support the following switches: -r to reverse the sort; -u to only display unique numbers; -n sort numerically (i.e., 9 comes before 10) instead of alphabetically (10 comes before 9 because it begins with a “1”). We have used sort in class. Type “man sort” for details on the command and its usage.

Switches can be entered together or individually e.g., -unr or -r -u -n. Your program needs to be able to handle both types of switch input.

Use the quicksort algorithm as the basis of your command implementation. There is an implementation of quicksort in the course book (Bronson), pg. 430. Please do not copy the code. Rather, study it and write your our version which uses recursion. But you need to understand its details.

Your program should perform as follows:


[atc@Macintosh-10 a3]$ ./sort numbers
1
1
10001
110
2
3
4
4
5
53
56
6
7000
72
73
764
9
9
9

[atc@Macintosh-10 a3]$ ./sort -n numbers
1
1
2
3
4
4
5
6
9
9
9
53
56
72
73
110
764
7000
10001

[atc@Macintosh-10 a3]$ ./sort -nu numbers
1
2
3
4
5
6
9
53
56
72
73
110
764
7000
10001

[atc@Macintosh-10 a3]$ ./sort -nur numbers
10001
7000
764
110
73
72
56
53
9
6
5
4
3
2
1

Please test your program for correctness against a variety of input data.

4) rps.c: Implement the classic rock paper scissors game. This question challenges you in what is sometimes considered the black art of software decomposition. Given the requirements of this simple, well-known game (see how the program performs below), how do we decompose the game into main() and its associated functions. How does the code break down into functions?

Hints on decomposition of software into functions:

Consider the goals of the game and the requirements implicit in the performance of the game. Rather than list the game’s requirements we simply show the operation of the game below. The computer should randomly make its choice when playing the user.

Clearly, one part of the program deals with user input. Input needs to parsed to the correct commands and the appropriate command function invoked. Another part of the program should compute the machines choice (i.e.., rock, paper, or scissors). From below you can see that other parts of the program should include printing the status of the game and responding to the user if help is required. Another function should display who won the particular round. What are the function names and their interface definition (inputs and outputs). Clearly, “main()” pulls all the functions together to drive and control the execution of the game.

Hints on modularity, clarify, and correctness:

Small is beautiful: Initially develop all your code in a single .c file and debug using printf statements. Once you have a working prototype put each function into its own .c file. and create a game.h file with any structures, #defines, and prototypes for the functions. You can compile all the .c files, for example. Re-run your program and make sure it functions correctly. Make sure you code defensively.

mygcc -o rps main.c f1.c f2.c f3.c f4.c

f1, f2, f3 etc. will be replaced by the names of your files which will reflect the function breakdown of the game to functions/code.

Hints on being defensive:

What will your program do if there is incorrect input from the user? Also, because the example game below does not show all possible outcomes feel free to define any behavior that is unstated, for example, a draw, a loss, etc.

Your game.h file should hold all your prototypes and data structures. Write your game.h header file first. You can pass it by me if you wish.

PIC


[atc@Macintosh-10 ]./rps

So you want a chance your luck again the computer
for a game of rock (r), paper (p), scissors (s)

Rules:
paper covers the rock and wins
rock breaks the scissors and wins
scissors cut the paper and wins

You and the computer will now play.
We will keep playing until you (e)xit.

Menu:
g - game status
h - help
e - exit

Enter p, r, or s:
You: s
Computer: p
You win!

Enter p, r, or s:
You: s
Computer: p
You win!

Enter p, r, or s:  r

Enter p, r, or s:
You: r
Computer: s
You win!

Enter p, r, or s:
You: h

The following characters can be used for input:
     p   for paper
     r   for rock
     s   for scissors

Enter p, r, or s: g

Game status:
   Win:      3
   Lose:     0
   Tie:      0
   Total:    3

Darn it you won!

Enter p, r, or s: e

OK. You are done.

Tip: Make sure you always logout when you are done and see the prompt to login again before you leave the terminal.