$24
Lab Objective: We commented at the end of last week’s lab that our copyFile program was inflexible in that the “from” and “to” files were hard coded into the source code. We will now remedy this. We will also use the lab to look at the software development process of C or C++ .
Here's a quick summary of the tasks:
1. Create a directory (folder) for this week’s work
2. Copy the C source code copyFile.c from last time into your directory (this week’s)
3. Write a more useful version of the copyFile program so that it can accept “command line arguments”
4. Modify this program now so that it can perform a very weak type of encryption. In doing this you will learn about
a. the way characters (chars) are represented internally in C
b. strcmp, how strings (of characters) are compared in C
c. breaking your program into multiple files which is vital for maintaining large programming projects
5. Once the program is working according to the specifications given below you should submit it for marking using Sulis
In Detail
You can create this week’s lab directory with
mkdir ~/cs4023/labs/week05
This assumes that you already had created the directory ~/cs4023/labs/ from last time. A little shortcut that instructs mkdir to make any necessary “parent” directories along the way is
mkdir -p ~/cs4023/labs/week05
Now change your working directory to this since all of our compiling, etc. will be done in this directory. See last week for how to change working directory.
To start off we need our copyFile.c source file from last time. If you did not succeed in getting last week’s lab completed you can use the readFile.c code instead that is uploaded in the assignment Spec. In the first case, you will issue the following cp command:
cp ../week05/copyFile.c .
If you are going the other way, mention in your blogs what steps did you take to bring the file in Sulis (‘readFile.c’) to a file in your linux machine.
When a C program is entered as a process on the OS’s ready queue the first point of execution is the function called main(). In last week’s lab this function did not take any
arguments. Now we are going to change this so that arguments specified on the command line can be passed into our program. (This is akin to calling a function with different parameters.) To do this you will change
int main()
to
int main(int argc, char* argv[])
The first argument, argc indicates how many arguments our program was called with – including the program’s name itself. This might seem a bit odd but there are good reasons for it. The second argument to main, is a block of memory that allows us to access the command-line arguments of our program. It is called argv where the ’v’ at the end is to denote a vector (or sequential block) of data. We can access a particular argument that our program was called with by means of argv[i]. Remember that C programmers waste nothing so they start counting from 0 instead of 1. In argv[0] we can gain access to the program name. So, if we gave a command from the shell prompt:
copyFile here there
then the character string “copyFile” is associated with argv[0]. Further, if we gave this command from the shell prompt:
../week05/copyFile here there
then the string of characters associated with argv[0] would be “../week05/copyFile”.
argc tells us how many arguments; argv gives a way of accessing each of those arguments. Remember that the name of the program running counts as an argument and can be accessed in argv[0].
In order to open the first file for reading you will need to modify your first fopen() system call so that it uses the first argument given to on the command line. (We call the command itself the zeroth argument.)
The first part of today’s lab is to write this copyFile program. Once you think you have it working you must test it with the diff command. Assuming that you had already created a file here if you gave the command:
copyFile here there
then you would ensure that the source and destination files are identical with:
diff here there
diff operates silently so if the files are identical, you will see no output from diff.
The second task for today is to modify this program so that, if the -f flag is given as the first command-line argument a very simple form of encryption called flipping is performed. If a file is copied in flip format, then all numerical characters and upper and lower case letters should be replaced in the destination by their flip.
So, what is the flip of a character? If we take the lower-case letter ’e’, the fifth letter of the alphabet as an example, then when it is flipped it should be replaced by the fifth letter of the alphabet from the end of the lower-case alphabet. Likewise, for an upper-case letter or a number. For your reference I have given you a “before and after” file. They can be found in Sulis Assignment Spec and were generated as follows:
copyFile -f sample sample.flip
and are repeated here.
The contents of sample:
abcdefghijklmnopqrstuvwxyz;
ABCDEFGHIJKLMNOPQRSTUVWXYZ,
0123456789.
The contents of sample.flip:
zyxwvutsrqponmlkjihgfedcba;
ZYXWVUTSRQPONMLKJIHGFEDCBA,
9876543210.
Notice how the punctuation characters at the end of each line made it through safely
So how – finally! – do we implement in code the flip? What you could do would be to have a giant, mega, 62-way “if” statement (26 lowercase + 26 uppercase + 10 digits.) along the lines of:
if (c == ’a’)
putc(’z’, destfile);
elsif (c == ’b’)
putc(’y’, destfile);
:
// ouch!
The two clinching reasons for not doing things this way is that it is very error-prone and not very maintainable (for making changes later). Most of all though our professional pride should drive us to search for something more elegant. We can do something a lot more elegant and more compact once we realise how C deals with characters. In all computer languages everything must be represented ultimately as a series of 0s and 1s. In C, the letters ’a’ to ’z’ are encoded consecutively. This means that whatever 8-bit pattern is used to represent the letter ’d’, when viewed as an integer, the letter ’e’ can be viewed as an integer one larger. As we will see below it is irrelevant what the value of the integer
corresponding to any of them are: all we need to know is that they occur consecutively.
The previous was put in bold so that you would remember it.
Likewise, the letters ’A’ to ’Z’ occur consecutively but strangely they are not consecutive to their lower-case counterparts. The digits ’0’ to ’9’ are also consecutive but, again, they are isolated form the other two “blocks” by other characters – punctuation, I think.
What this means is that we could write a function to translate any lower-case character to its corresponding upper-case as follows:
char toUpper(char c)
{
if (’a’ <= c && c <= ’z’) // a valid lowercase
return c-’a’ + ’A’; // c-’a’ is offset
return c; // wasn’t LC so leave alone
}
You will need to write a function flipChar() that takes a single character as argument and does something like the toUpper() function above. Because there are three different cases to consider (lower-case, upper-case and digits) you will need to distinguish between the three cases and act accordingly.
In order to keep things simple, in this assignment we will treat anything outside the ranges
0-9, a-z and
A-Z as being non-alphabetic and let them pass through unchanged.
At a very high level the program will operate very similarly to your copyFile program in the first part of the lab. The difference here is that each character that is read is either flipped, or not, depending on the first command-line argument. If it is -f then flipping is in effect – and the origin and destination file command-line arguments are in argv[2] and argv[3]. A nice way to achieve all this is to
• Test firstly at the beginning of your program if flipping is in effect and storing the result in an integer that will be either 0 (false) or 1 (true):
int flipping = (strcmp(argv[1], "-f") == 0);
We compare the string pointed to by argv[1] against the string “-f” using the C function strcmp(). If they are equal, then strcmp() returns 0; in any other case what is returned is the subtraction of the characters where they first differ, so you can tell which string is larger.
Before you can call a function in C you must give the compiler fair warning. To
“declare” the strcmp() function to the compiler all you have to do is to have it read the declarations in the string.h file. This is done by putting at the top of your program:
#include <string.h>
• Now, when you want to write a character to the destination within your main while loop you ask if flipping is in effect and you execute the following
if (flipping) // same as: if (flipping == 1) putc(flipChar(c), destfile);
else
putc(c, destfile);
There is one final task to this week’s lab. While it may appear gratuitous or contrived in this small case it is important enough to introduce this concept early in the series of lab exercises you will do. This is the idea of decomposing programs into multiple source files. All serious software is made up of code coming from multiple files and/or libraries so it is a good habit to get into now.
In our case here you should put your flipChar() function and any other functions that you might write for the assignment in a C file called utils.c. This file will be compiled separately into object code, utils.o, and then linked together with a separately compiled copyFile.o object file as shown below. Whenever you make a change to your flipChar() function you will need to recompile utils.c with:
gcc -c utils.c
and then relink the two object files with:
gcc copyFile.o utils.o -o copyFile
Similarly, changing any of the source code of copyFile.c necessitates a recompilation of this with:
gcc -c copyFile.c
and re-linking of the two object files.
You will call the flipChar() function from copyFile.c so when this is being compiled the compiler had better be pre-warned of its existence. You do this by putting all the utils-related declarations in a file called utils.h and giving the compiler knowledge of its contents by putting
#include "utils.h"
just below all of the other #include directives that you have in your copyFile.c file. I have written this for you, and it is available with all the others.
copyFile
gcc copyFile.o utils.o -o copyFile
copyFile.o utils.o
gcc -c copyFile.c gcc -c utils.c
copyFile.c utils.h utils.c
Submit all files on Sulis under Assignments there be an opening for this.