UQ Students should read the Disclaimer & Warning

Note: This page dates from 2005, and is kept for historical purposes.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>COMP2301 - Assignment 2 - File I/O in C</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
<!--
.wrong {
    background: #FF9999;
}
body {
    background: url(_img/DSC04989.jpg) fixed center;
    font-family: "Arial Unicode MS", Arial, Helvetica, sans-serif;
}
th, td, textarea {
    border: 1px solid #000000;
    padding: 0 1ex;
    background: transparent;
    overflow: hidden;
}
table {
    border: none;
}
-->
</style>
</head>
<body> 
<h1>COMP2301 &ndash; Assignment Two &ndash; File <acronym title="Input/Output">I/O</acronym> in
    C</h1> 
<p> This assignment is a pass/fail assignment. I achieved a pass.</p>
<p>The goal of this assignment is to write C code to perform a simple file encryption
        and decryption. </p>
<p>The solution must have the following features / functions:</p> 
<h2>Encrypt </h2> 
<ul> 
    <li>The application must encrypt any sample files for use as test input. Valid
        files include JPEG, ZIP, EXE, TXT, WORD... </li> 
    <li>The first 30 bytes of the encrypted files must contain the source student
        name as clear text. (Max 30 char) </li> 
    <li>The last 35 bytes of the encrypted files must contain an encrypted text
        version of source student name. </li> 
    <li>The last five bytes of the encrypted file must contain a clear ASCII representation
        of original file size in bytes. </li> 
    <li>Data must be encrypted by taking a binary representation of the key and
        performing a bit wise XOR with the original file binary then repeating the
        process with the next key length file segment until the file is encrypted. </li> 
    <li>A unique five digit hexadecimal key will be supplied with each encrypted
        sample file. </li> 
</ul> 
<h2>Decrypt </h2> 
<ul> 
    <li>The application must decrypt at least 10 unique sample files obtained from
        10 different individuals. (+10 unique keys) </li> 
    <li>The application must decrypt any sample file provided by a tutor during
        marking. </li> 
    <li>The application must generate a text menu showing all input files (max 20)
        and allow the user to select one file </li> 
    <li>This menu must include file selection number, source name, original file
        size and encrypted file size (Max 20 files) </li> 
    <li>After file selection the user must be prompted for a five digit hexadecimal
        decryption key. </li> 
    <li>The key is to be applied to the encrypted file to restore its original content. </li> 
    <li>In addition to decrypting the file content the original file structure and
        size must be restored. The application must display start and end messages
        plus include the following strings &lt;source name&gt; = &lt;decoded source
        name&gt; is [True| False] Original file size &lt;nnnnn&gt; = decrypted file
        size &lt;nnnnn&gt; is [True| False] </li> 
    <li>Encryption Key is [Valid | Invalid] </li> 
</ul> 
<h2>General </h2> 
<ul> 
    <li>The application must provide a simple text menu system to navigate its functions. </li> 
    <li>Code may use C library functions. </li> 
    <li>Code must be modular and make good use of C functions. </li> 
    <li>Code comments must correctly reference the work of others. </li> 
    <li>Code must be readable, well structured and conform to the pre-defined style
        guide </li> 
</ul> 
<p>The most complex part of this assignment, for me, was the creation of the
    key. I ended up using the technique below: </p>
<p><a href="#c" title="Skip to the C code">Skip to the code</a>. </p>
<p>Key Creation<br /> 
    <textarea name="k" cols="82" rows="38" readonly="readonly" title="Logic behind the key creation">5 byte ASCII String Input:
    DE345

Binary representation of the key. It is only 2 ½ bytes long:
    DE        34        5
    11011110    00110100    0101

Binary representation of key doubled. It is now 5 bytes long:
    DE        34        5D        E3        45
    11011110    00110100    01011101    11100011    01000101

Binary key converted back to decimal:
    11011110    00110100    01011101    11100011    01000101
    222        52        93        227        69

5 byte key (as ASCII):
    Þ4]ãE

Using sprtinf, the input key is divided into two-byte chunks:
sprintf ( keyChunk, "%c%c", keyChars[0], keyChars[1] );
keyChunk then holds 2 byte ASCII string "DE"

Using the string to long function, the two byte chunk is converted into a single
base 16 long, which is a one byte long hexadecimal representation of the two
byte input:
oKey[0] = (char)strtol (keyChunk, NULL, 16);
oKey[0] then holds 1 byte ASCII character "Þ"

This can then be placed in a while loop for any length N key:
for( byte = 0; byte &lt; N; byte++ )
{
    // double keyChars and divide into two-byte keyChunks
    sprintf ( keyChunk, "%c%c", keyChars[ii % N], keyChars[(ii + 1) % N] );
    ii+=2;

    // convert two-byte keyChunks into one-byte hex equivalent
    oKey[byte] = (char)strtol (keyChunk, NULL, 16);
}</textarea>
</p>
<p>&nbsp;</p>
<p id="c"> Assignment-2.c<br /> 
    <textarea name="c" cols="82" rows="1124" readonly="readonly" title="C Code - Copyright 2004 Ned Martin">/*
 * Author:        Ned Martin #40529927
 *
 * Creation Date:    17-APR-2004
 *
 * Version:        1.2 18-APR-2004
 *
 * Copyright:        2004 Ned Martin, Student s40529927, for COMP2301,
 *            University of Queensland
 *
 * Description:        Assignment 2 for COMP2301
 *            Encrypts a given file with a given key, or decrypts a
 *            given file with a key either given or auto-determined.
 *            Writes the encrypted or decrypted output file to another
 *            user specified file. A simple XOR function is used to
 *            perform the encryption. The key is a five digit
 *            hexadecimal key.
 *
 * Restrictions:    Valid files for decryption, per the specification, are
 *            files which have an unencrypted header of SNAME_LENGTH
 *            length in bytes as specified below, where the original
 *            filesize is less than FSIZE_MAX (see its define below
 *            for more information) and this size is stored in the
 *            last FSIZE_LENGTH bytes of the encrypted file as plain
 *            text ASCII, and where an encrypted form of the
 *            SNAME_LENGTH header is stored as a footer directly
 *            preceding the last FSIZE_LENGTH bytes at the end of the
 *            file. Encryption must be performed by a simple bitwise
 *            XOR using a hexadecimal key of length KEY_LENGTH. The
 *            file is encrypted seperately to the footer, and the key
 *            is not continued between the file and its footer
 *            although the same key is used. Will not accept empty
 *            files.
 *
 * Usage:        filename [filename ...]
 *
 * Tab Stops:        Eight spaces
 */



// System includes
#include &lt;stdio.h>
#include &lt;stdlib.h>        // defines EXIT_FAILURE, EXIT_SUCCESS
#include &lt;string.h>        // strcmp(), strcmp()
#include &lt;ctype.h>        // isxdigit()
#include &lt;io.h>            // filelength()


/*
 *    Constant string used as a clear-text header and encrypted as a footer 
 *    in the decrypted file.
 *
 *    Note: This string must be equal in length to SNAME_LENGTH
 */
const char* STUDENT_NAME = "N Martin www.nedmartin.org/uni";

/*
 *    Length in bytes to store the student name.
 *
 *    Note: This value must be the same as the length in bytes of the string
 *    STUDENT_NAME
 *
 *    Default: 30
 */
#define SNAME_LENGTH    30

/*
 *    Length in bytes of the key
 *
 *    This value specifies how int in bytes the key is when doubled, and also
 *    how many hexadecimal characters int it is.
 *
 *    Default: 5
 */
#define KEY_LENGTH    5

/*
 *    Maximum number of files to display at a time.
 *
 *    This value determines how many files will be displayed. If more than 
 *    NUM_FILES files are specified as input, only NUM_FILES of them will be
 *    acknowledged.
 *
 *    Default: 20
 */
#define NUM_FILES    20

/*
 *    Length in bytes to store file size at end of encrypted file.
 * 
 *    This value defines the maximum size that of an input file as the maximum
 *    size that can be stored in FSIZE_LENGTH ASCII characters.
 *
 *    NOTE: there is a sprintf() in encrypt() that requires manual changing if
 *    the FSIZE_LENGTH value is changed.
 *
 *    Default: 5
 */
#define FSIZE_LENGTH    5

/*
 *    Maximum allowable input file size in bytes for encryption.
 *
 *    This value is defined as the maximum size in bytes that can be held in
 *    an ASCII integer value (where 1 is stored as "1" and so on) of
 *    FSIZE_LENGTH characters and must not exceed that value.
 *
 *    Default: 99999
 */
#define FSIZE_MAX    99999

/*
 *    Memory to allocate for file names.
 *
 *    This defines the amount of memory allocated to hold user specified file
 *    names and paths.
 *
 *    NOTE: File names (including path) exceeding this value may result in
 *    unpredictable behaviour.
 *
 *    Default: Maximum value required to store a file and path name as string
 */
#define FNAME_LENGTH    30




/*
 * Function:    xorFile
 *
 * Purpose:    XOR a file with a key and output to another file.
 *
 * Method:    Accepts input and output file pointers, a hexadecimal key, and
 *        a start and end position. Seeks to start position in input file,
 *        and XORs each byte of the input file with a byte of the key,
 *        looping through the key, and outputting the result to the output
 *        file.
 *
 * Returns:    int 0 on success
 *        int 1 otherwise
 */
int xorFile
(
    char*    iKey,        // key to encrypt with
    FILE*    iFileIn,    // input file pointer
    FILE*    iFileOut,    // output file pointer
    int    iStart,        // start byte
    int    iEnd        // end byte
)
{
    int    byte = 0,    // counter for looping through iKey
        c = 0;        // stores current input file character

    // seek to required iStart position
    if( (fseek (iFileIn, iStart, SEEK_SET)) )
    {
        // seeking error occured
        fprintf (stderr, "XOR reports \"Error seeking to %d in file "
            "%s\"\n", iStart, iFileIn);
        return 1;
    }

    // while we haven't reached iEnd position
    while ( (ftell (iFileIn)) &lt; iEnd )
    {
        // get an input byte from input file
        c = getc (iFileIn);

        // xor input byte with byte of iKey
        c ^= iKey[byte++];

        // loop through bytes of iKey
        byte%=KEY_LENGTH;

        // output xored byte to output file
        putc (c, iFileOut);
    }
    return 0;
} // end xorFile


/*
 * Function:    selectFiles
 *
 * Purpose:    Prints a list of verified input filenames and allows a user to
 *        select one.
 *
 * Method:    Accepts an array of potential filenames and a decrypting flag
 *        and verifies that the potential filenames refer to valid files
 *        for decryption or encryption depending on the value of the
 *        decryption flag, then prints a listing of valid files and allows
 *        the user to select a specific file and returns the name of the
 *        selected valid file.
 *
 * Returns:    char* name of valid user selected file or
 *        NULL if an error or no file was selected.
 */
char* selectFiles
(
    int    iNumFiles,        // number of iFileNames
    char*    iFileNames[],        // array of filenames
    int    iDecrypting        // flag if mode is decrypting
)
{
    int    count = 0,        // counter for number of input files
        bytes = 0,        // byte counter for get student name
        invalid = 0,        // number of invalid files
        newCount = 0,        // number of valid files
        printMax = 0,        // holds maximum files to select
        fileNum = 0,        // holds input
        originalSize = 0,    // size of original file
        fileSize = 0;        // size of file

    char    c = 0,                // holds input character
        studentName[SNAME_LENGTH];    // holds student name
    char*    validFileNames[NUM_FILES];    // holds a list of valid files

    FILE*    f = NULL;            // file pointer

    // loop through iNumFiles items in iFileNames array upto max NUM_FILES
    for( count = 0; (count &lt; iNumFiles) &amp;&amp; (count &lt; NUM_FILES); count++ )
    {
        // attempt to open file in ascii mode
        f = fopen (iFileNames[count], "rt");

        // file was opened and hence exists
        if( f )
        {
            // get fileSize
            fileSize = filelength(fileno (f));

            // we are in iDecrypting mode
            if( iDecrypting )
            {
                // seek to last 5 bytes and read ASCII size
                fseek (f, -FSIZE_LENGTH, SEEK_END);

                // store ASCII size as int in originalSize
                fscanf (f, "%d", &amp;originalSize);
            }
            // we are in decrypting mode
            // and fileSize is greater than zero
            // and fileSize is less than the allowed size
            // and fileSize - originalsize = 65 bytes
            // then file is assumed valid for decryption
            if( iDecrypting
                &amp;&amp; (fileSize > 0)
                &amp;&amp; (fileSize &lt;= (FSIZE_MAX + SNAME_LENGTH * 2
                    + FSIZE_LENGTH))
                &amp;&amp; ((fileSize - originalSize) ==
                    (SNAME_LENGTH * 2 + FSIZE_LENGTH)) )
            {

                // put only valid files into array iFileNames
                validFileNames[newCount++] =
                    iFileNames[count];

                // get first SNAME_LENGTH bytes student name
                rewind (f);
                for( bytes = 0; bytes &lt; SNAME_LENGTH; bytes++ )
                {
                    studentName[bytes] = getc (f);
                }
                // print valid files, the source student name,
                // their fileSize, and original unencrypted size
                printf ("\t (%d) %s - \"%s\" (%d/%d)\n",
                    newCount, iFileNames[count],
                    studentName, originalSize, fileSize);
            }
            // we are in encrypting mode
            // and fileSize is greater than zero
            // and fileSize is less than the allowed size
            else if( !iDecrypting
                &amp;&amp; (fileSize > 0)
                &amp;&amp; (fileSize &lt; FSIZE_MAX) )
            {
                // put only valid files into array iFileNames
                validFileNames[newCount++] =
                    iFileNames[count];

                // print valid files and their fileSize
                printf ("\t (%d) \"%s\" (%d bytes)\n",
                    newCount, iFileNames[count],
                    fileSize);
            }
            // file is not within bounds for decryption
            else
            {
                // increment the invalid files counter
                invalid++;
            }
            // close file
            fclose (f);
        }
        // file could not be opened so assumed to not exist
        else
        {
            // increment the invalid files counter
            invalid++;
        }
    }
    // print a count of invalid files
    if( invalid == iNumFiles ) // then no files were valid
    {
        printf ("\nNo valid files were specified\n");
        return NULL;
    }
    else if( invalid ) // some files were invalid
    {
        printf ("\n%d invalid files were specified\n", invalid);
    }

    // loop until 'Q' or a valid file is selected then return that file
    for( ; ;  )
    {
        // get the input
        scanf (" %s", &amp;c);

        // quit if Q is pressed
        if( c == 'q' || c == 'Q' )
        {
            return NULL;
        }

        // convert input into a number
        fileNum = atoi (&amp;c);

        // if number is a valid file number
        if( fileNum &lt;= NUM_FILES &amp;&amp; fileNum &lt;= newCount &amp;&amp; fileNum > 0 )
        {
            // return that file name
            return validFileNames[fileNum - 1];
        }
        // number is not a valid file number
        else
        {
            // there are more than NUM_FILES files
            if( newCount > NUM_FILES )
            {
                // request user enter number below NUM_FILES
                printMax = NUM_FILES;
            }
            // there are NUM_FILES or less files
            else
            {
                // request user enter number >0 and &lt; newCount
                printMax = newCount; 
            }
            // file is wrong, print message saying so
            printf ("(%d) %s %d\n", fileNum,
                "is not a valid file. Please select a file "
                "between 1 and", printMax);
        }
    }
    // we should never get here
    return NULL;
} // end selectFiles


/*
 * Function:    requestKey
 *
 * Purpose:    Requests a HEX key of length KEY_LENGTH from the user and
 *        converts it into a hexadecimal representation.
 *
 * Method:    Requests a string of characters from the user, verifies that
 *        this string matches the required length and can be converted to
 *        hexadecimal, doubles this string and converts it into two-byte
 *        chunks, then converts the two-byte chunks into single-byte
 *        hexadecimal representation using a string-to-base-16-int
 *        function.
 *
 * Returns:    int 0 on success
 *        int 1 otherwse
 *        
 */
int requestKey
(
    char*    oKey    // holds resulting hex key
)
{
    int    notHex = 1,        // flag for hex verification
        ii = 0,            // keyChunk counter
        byte = 0;        // byte counter
    char    c[KEY_LENGTH],        // holds key input
        keyChars[KEY_LENGTH],    // holds verified key input
        keyChunk[2];        // holds intermediate oKey chunks

    // verify that input is KEY_LENGTH character hexadecimal
    while( notHex )
    {
        // get input string
        scanf (" %5s", c);

        // input assumed to be valid hex
        notHex = 0;

        // exit if Q is input
        if( (c[0] == 'Q') || (c[0] == 'q') )
        {
            // exit this routine and return to caller
            return 1;
        }
        // loop through checking all KEY_LENGTH chars are hex
        for( byte = 0; byte &lt; KEY_LENGTH; byte++ )
        {
            // if char is not valid hex we don't exit while loop
            if( !isxdigit (c[byte]) )
            {
                notHex = 1;
            }
        }
        // if char is not hex then print message
        if( notHex )
        {
            printf ("Invalid Input: \"%s\" is not a %d digit "
                "hexadecimal key\n"
                "Please enter a valid key or Q to return to "
                "the main menu\n", c, KEY_LENGTH);
        }
    }
    // set  keyChars to KEY_LENGTH digits of c
    for( byte = 0; byte &lt; KEY_LENGTH; byte++ )
    {
        keyChars[byte] = c[byte];
    }

    // convert keyChars to hex and store in oKey
    for( byte = 0; byte &lt; KEY_LENGTH; byte++ )
    {
        // double keyChars and divide into two-byte keyChunks
        sprintf ( keyChunk, "%c%c", keyChars[(ii % KEY_LENGTH)],
            keyChars[((ii + 1) % KEY_LENGTH)] );
        ii+=2;

        // convert two-byte keyChunks into one-byte hex equivalent
        oKey[byte] = (char)strtol (keyChunk, NULL, 16);
    }
    return 0;
} // end requestKey


/*
 * Function:    xorString
 *
 * Purpose:    XOR A string with another string using a key.
 *
 * Method:    Accepts a key and string, XOR's the string with the key and
 *        modifies the string accordingly.
 *
 * Returns:    void but ioString is modified by this function.
 */
void xorString
(
    char*    iKey,        // key to use in XOR
    char*    ioString    // input and output string to XOR
)
{
    int    c,        // holds input byte for XORing
        byte,        // input byte counter
        keyByte = 0;    // key byte counter

    // for the length of ioString
    for( byte = 0; byte &lt; SNAME_LENGTH; byte++ )
    {
        // get an input byte from ioString
        c = ioString[byte];

        // xor input byte with byte of key
        c ^= iKey[keyByte++];

        // loop through bytes of key
        keyByte%=KEY_LENGTH;

        // put XORed byte to ioString
        ioString[byte] = c;
    }

} // end xorString


/*
 * Function:    verify
 *
 * Purpose:    Verifies decryption was successful.
 *
 * Method:    Accepts file pointers to the encrypted and decrypted files and
 *        the key used to decrypt, checks if the original file size stored
 *        at the end of the encrypted file is the same as the resulting
 *        size of the decrypted file and checks if the clear source name
 *        at the start of the encrypted file is the same as the encrypted
 *        source name (once decrypted using the key) at the end of the
 *        encrypted file, and prints a message containing this
 *        information.
 *
 * Restriction:    The decrypted file must have its file pointer set to the last
 *        position in the file as  this is used to determine the size of
 *        that file.
 *
 * Returns:    int 1 on successfully verifying decryption or
 *        int 0 on failure to verify
 */
int verify
(
    char*    iKey,        // iKey used to decrypt
    FILE*    iEncFile,    // file pointer to encrypted file
    FILE*    iDecFile    // file pointer to decrypted file
)
{
    int    byte = 0,            // byte counter
        sizesMatch = 0,            // if sizes match
        namesMatch = 0,            // if names match
        success = 0,            // if verification was success
        originalFileSize = 0,        // holds original file size
        decFileSize = 0;        // holds decrypted file size
    char    sourceName[SNAME_LENGTH],    // holds original source name
        decSourceName[SNAME_LENGTH + 1];// holds decrypted source name

    // rewind input file to start and loop through first SNAME_LENGTH bytes
    // to get the source name
    rewind (iEncFile);
    for( byte = 0; byte &lt; SNAME_LENGTH; byte++ )
    {
        sourceName[byte] = getc (iEncFile);
    }

    // seek to the end SNAME_LENGTH + FSIZE_LENGTH bytes of input file
    if( (fseek (iEncFile, -(SNAME_LENGTH + FSIZE_LENGTH), SEEK_END)) )
    {
        // seeking error occured
        fprintf (stderr, "Verify reports \"Error seeking to position "
            "in input file\"\n");
        return 1;
    }

    // get encrypted source name from input file
    for( byte = 0; byte &lt; SNAME_LENGTH; byte++ )
    {
        decSourceName[byte] = getc (iEncFile);
    }
    // terminate so can print as a string
    decSourceName[SNAME_LENGTH] = '\0';

    // XOR the encrypted source name to its clear text original format
    xorString(iKey, decSourceName);

    // set decrypted file size to current output file pointer position
    decFileSize = ftell (iDecFile);

    // store the ASCII size as an int in originalSize
    fscanf (iEncFile, "%d", &amp;originalFileSize);

    // if the original and decrypted sizes match set sizesMatch to 1
    if( originalFileSize == decFileSize )
    {
        sizesMatch = 1;
    }

    // check if sourceName equals decSourceName by looping through both
    // and comparing each byte
    byte = 0;
    while( sourceName[byte] == decSourceName[byte] &amp;&amp; byte &lt; SNAME_LENGTH)
    {
        byte++;
    }
    // if we successfully looped through entire sourceName and decSourceName
    // then they are equal so set namesMatch to 1
    if( byte == SNAME_LENGTH )
    {
        namesMatch = 1;
    }

    // if sizes matched and names matched, then verification was a success
    if( sizesMatch &amp;&amp; namesMatch )
    {
        success = 1;
    }

    // print verification message
    printf ("\"%s\" = \"%s\" is %s\n", sourceName, decSourceName,
        (namesMatch)?"True":"False");
    printf ("Original file size %d bytes = "
        "decrypted file size %d bytes is %s\n",
        originalFileSize, decFileSize, (sizesMatch) ? "True" : "False");
    printf ("Encryption Key is %s\n", (success) ? "Valid" : "Invalid");

    // return 1 if success, else return 0 for failure to verify
    return success;
} // end verify


/*
 * Function:    decrypt
 *
 * Purpose:    Decrypts a user selected file from an array of possible
 *        filenames and outputs to another user selected file.
 *
 * Method:    Accepts an array of possible filenames, calls a function to
 *        verify and return a user selected file for decryption, calls a
 *        function to request a user input hexadecimal key, calls a
 *        function to decrypt the given file using the given key and
 *        writes the decrypted output to a user specified file then calls
 *        a function to verify that decryption was a success.
 *
 * Returns:    int 0 on success or
 *        int 1 on failure
 */
int decrypt
(
    int    iNumFiles,    // number of elements in iFileNames array
    char*    iFileNames[]    // array of possible file names
)
{
    int    decrypting = 1,        // decrypting mode
        start = 0,        // bytes to start xor
        end = 0;        // bytes to end xor
    char*    file = NULL;        // input file
    char    key[KEY_LENGTH],    // key to xor with
        c[FNAME_LENGTH];    // holds output filename
    FILE*    fi = NULL;        // input file pointer
    FILE*    fo = NULL;        // output file pointer

    // ask for input filenames
    printf ("(2)\tSelect a file to decrypt or Q to return to the main "
        "menu\n\n");
    if( !(file = selectFiles(iNumFiles, iFileNames, decrypting)) )
    {
        // no file was selected so return to menu
        return 1;
    }

    printf ("Decrypting \"%s\"\n", file);

    // request a key to decrypt the file
    printf ("%s \"%s\"\n%s\n", "Enter a 5 digit HEX key to decrypt", file,
        "(example: \"A3B5d\") or Q to return to main menu");
    if( requestKey(key) )
    {
        // invalid key or Q entered so return to main menu
        return 1;
    }

    // request output filename
    printf ("Enter a filename for the decrypted version of \"%s\"\n", file);
    scanf (" %s", c);
    while( !(strcmp (c, file)) )
    {
        printf ("Output filename cannot be the same as input filename\n"
            "Enter another filename\n");
        scanf (" %s", c);
    }
    printf ("Decrypted filename is \"%s\"\n", &amp;c);

    // open input and output files
    fi = fopen (file, "rb");
    fo = fopen (c, "wb");

    // unable to open output file
    if( !fo )
    {
        // print error and return to main menu
        fprintf (stderr, "Decrypt reports \"Unable to open output "
        "file %s\"\n", c);
        return 1;
    }

    // start XOR at SNAME_LENGTH + 1 after the SNAME_LENGTH student name
    start = SNAME_LENGTH;

    // length of input file minus SNAME_LENGTH + FSIZE_LENGTH
    // of encrypted student name and ASCII filesize
    end = ( filelength(fileno (fi)) - (SNAME_LENGTH + FSIZE_LENGTH) );

    // decrypt file from start value from above to end value from above
    xorFile(key, fi, fo, start, end);

    // verify that decryption was successful
    printf ("Verifying output...\n\n");
    if( verify(key, fi, fo) )
    {
        printf ("\nDecryption Successful\n\n");
    }
    else
    {
        printf ("\nDecryption Failed\n\n");
        return 1;
    }
    
    // close input and output files
    fclose (fi);
    fclose (fo);

    return 0;
} // end decrypt


/*
 * Function:    encrypt
 *
 * Purpose:    Encrypts a user selected file from an array of possible
 *        filenames and outputs to another user selected file.
 *
 * Method:    Accepts an array of possible filenames, calls a function that
 *        returns a user selected and verified file from this array, calls
 *        a function that requests and returns a hexadecimal key, calls a
 *        function that encrypts the given file with the given key and
 *        outputs to another user specified file. Encryption is not
 *        verified to have successfully worked.
 *
 * Returns:    int 0 on success or
 *        int 1 on failure
 */
int encrypt
(
    int    iNumFiles,    // number of elements in iFileNames array
    char*    iFileNames[]    // array of possible file names
)
{
    int    byte = 0,        // byte counter
        decrypting = 0,        // not decrypting flag
        start = 0,        // bytes to start xor
        end = 0;        // bytes to end xor

    char*    file = NULL;            // holds input file name
    char    ioString[SNAME_LENGTH + 1],    // string used in xorString
        fileSize[FSIZE_LENGTH + 1],    // string holds input filesize
        key[KEY_LENGTH],        // key to xor with
        c[FNAME_LENGTH];        // output filename

    FILE*    fi = NULL;        // input file pointer
    FILE*    fo = NULL;        // output file pointer

    // copy the constant STUDENT_NAME to ioString
    strcpy (ioString, STUDENT_NAME);

    // request input filename
    printf ("(1)\tSelect a file to encrypt or Q to return to the main "
        "menu\n\n");
    if( !(file = selectFiles(iNumFiles, iFileNames, decrypting)) )
    {
        // Q entered or no file was selected so return to menu
        return 1;
    }

    printf ("Encrypting \"%s\"\n", file);

    // request a key
    printf ("%s \"%s\"\n%s\n", "Enter a 5 digit HEX key to encrypt", file,
        "(example: \"A3B5d\") or Q to return to main menu");
    if( requestKey(key) )
    {
        // invalid key or Q entered so return to menu
        return 1;
    }

    // request output filename
    printf ("Enter a filename for the encrypted version of \"%s\"\n", file);
    scanf (" %s", c);
    while( !(strcmp (c, file)) )
    {
        printf ("Output filename cannot be the same as input filename\n"
            "Enter another filename\n");
        scanf (" %s", c);
    }
    printf ("Encrypted filename is \"%s\"\n", &amp;c);

    // open input (original) and output (encrypted) files
    fi = fopen (file, "rb");
    fo = fopen (c, "wb");

    // unable to open output file
    if( !fo )
    {
        // print error and return to main menu
        fprintf (stderr, "Encrypt reports \"Unable to open output "
        "file %s\"\n", c);
        return 1;
    }

    // write 30 bytes cleartext STUDENT_NAME
    fputs (STUDENT_NAME, fo);

    // start XOR at byte 0 which becomes byte 31 of output file
    start = 0;

    // XOR to the end of the input file
    end = filelength(fileno (fi));

    // perform the XOR to encrypt the file
    xorFile(key, fi, fo, start, end);

    // xor the STUDENT_NAME
    xorString(key, ioString);

    // put XORed STUDENT_NAME to output file
    for( byte = 0; byte &lt; SNAME_LENGTH; byte++ )
    {
        putc (ioString[byte], fo);
    }
    
    // put the 5 byte padded ASCII filesize to the end of the output file
    // NOTE: 5 here should be equal to FSIZE_LENGTH but cannot use
    // defined constant in string modifier.
    sprintf (fileSize, "%5u", end);
    fputs (fileSize, fo);

    // close files
    fclose (fi);
    fclose (fo);

    return 0;
} // end encrypt


/*
 * Function:    getKey
 *
 * Purpose:    Accepts a pointer to an encrypted file and finds the key that
 *        was used to encrypt that file.
 *
 * Method:    Finds the cleartext and encrypted student names in an encrypted
 *        file and XOR's them together to determine the key used to
 *        encrypt the file. iKey is then set to this key.
 *
 * Returns:    int 0 on success or
 *        int 1 on failure
 *        iKey is modified by this function
 */
int getKey
(
    FILE*    iFile,        // pointer to encrypted file
    char*    oKey        // key that is found
)
{
    int    byte = 0;            // byte counter
    char    studentName[SNAME_LENGTH],    // holds student name
        ioString[SNAME_LENGTH];        // holds encrypted student name

    // gets the student name from the start of the encrypted file
    for( byte = 0; byte &lt; SNAME_LENGTH; byte++ )
    {
        ioString[byte] = getc (iFile);
    }

    // seeks to position of encrypted student name in encrypted file
    if( (fseek (iFile, -(SNAME_LENGTH + FSIZE_LENGTH), SEEK_END)) )
    {
        // seeking error occured
        fprintf (stderr, "GetKey reports \"Error seeking to "
            "position in encrypted file\"\n");
        return 1;
    }
    // gets encrypted student name from encrypted file
    for( byte = 0; byte &lt; SNAME_LENGTH; byte++ )
    {
        studentName[byte] = getc (iFile);
    }

    // XOR's the encrypted and unencrypted student names. ioString returns
    // the result of this encryption which is the key used to encrypt
    xorString(studentName, ioString);

    // sets the bytes of the key to equal the bytes of the returned ioString
    for( byte = 0; byte &lt; KEY_LENGTH; byte++ )
    {
        oKey[byte] = ioString[byte];
    }

    return 0;
} // end getKey


/*
 * Function:    autoDecrypt
 *
 * Purpose:    Decrypts a user selected file from an array of possible
 *        filenames and outputs to another user selected file.
 *
 * Method:    Accepts an array of possible filenames, calls a function to
 *        verify and return a user selected file for decryption, calls a
 *        function to determine the hexadecimal key used, calls a
 *        function to decrypt the given file using the given key and
 *        writes the decrypted output to a user specified file then calls
 *        a function to verify that decryption was a success.
 *
 * Returns:    int 0 on success or
 *        int 1 on failure
 */
int autoDecrypt
(
    int    iNumFiles,    // number of elements in iFileNames array
    char*    iFileNames[]    // array of possible file names
)
{
    int    decrypting = 1,        // decrypting mode
        start = 0,        // bytes to start xor
        end = 0;        // bytes to end xor

    char*    file = NULL;            // holds input file name
    char    key[KEY_LENGTH],        // key to xor with
        c[FNAME_LENGTH];        // holds output file name

    FILE*    fi = NULL;    // input file pointer
    FILE*    fo = NULL;    // output file pointer

    // ask for input filenames
    printf ("(3)\tSelect a file to auto-decrypt or Q to return to the main "
        "menu\n\n");
    if( !(file = selectFiles(iNumFiles, iFileNames, decrypting)) )
    {
        // no file was selected so return to menu
        return 1;
    }

    printf ("Attempting to auto decrypt \"%s\"\n", file);

    // request output filename
    printf ("Enter a filename for the decrypted version of \"%s\"\n", file);
    scanf (" %s", c);
    while( !(strcmp (c, file)) )
    {
        printf ("Output filename cannot be the same as input filename\n"
            "Enter another filename\n");
        scanf (" %s", c);
    }
    printf ("Decrypted filename is \"%s\"\n", &amp;c);

    // open input and output files
    fi = fopen (file, "rb");
    fo = fopen (c, "wb");

    // unable to open output file
    if( !fo )
    {
        // print error and return to main menu
        fprintf (stderr, "Auto-Decrypt reports \"Unable to open output "
        "file %s\"\n", c);
        return 1;
    }

    // get the key used to encrypt the file
    getKey(fi, key);

    // start XOR at SNAME_LENGTH + 1 after the SNAME_LENGTH student name
    start = SNAME_LENGTH;

    // length of input file minus SNAME_LENGTH + FSIZE_LENGTH
    // of encrypted student name and ASCII filesize
    end = ( filelength(fileno (fi)) - (SNAME_LENGTH + FSIZE_LENGTH) );

    // decrypt file from start value from above to end value from above
    xorFile(key, fi, fo, start, end);

    // verify that decryption was successful
    printf ("Verifying output...\n\n");
    if( verify(key, fi, fo) )
    {
        printf ("\nDecryption Successful\n\n");
    }
    else
    {
        printf ("\nDecryption Failed\n\n");
        return 1;
    }
    
    // close input and output files
    fclose (fi);
    fclose (fo);

    return 0;
} // end autoDecrypt


/*
 * Function:    showHelp
 *
 * Purpose:    Prints a screen with information about the application.
 *
 * Method:    Prints a screen with information about the application.
 *
 * Returns:    none
 */
void showHelp()
{
    printf ("(3)\tHelp &amp; About\n");
    printf ("\tCOMP2301 Assignment 2\tNed Martin www.nedmartin.org/uni\n");
    printf ("\tVersion 1.2 18-APR-2004\t(c) Copyright 2004 Ned Martin\n");
    printf ("DESCRIPTION:\n");
    printf ("\tThis program will encrypt and decrypt files given on the\n");
    printf ("\tcommandline provided they have an original filesize less\n");
    printf ("\tthan 10,000 bytes. Some simple checking is performed on\n");
    printf ("\tfiles to be decrypted. Invalid files are not shown, but\n");
    printf ("\ta count of the number of invalid files is shown.\n");
    printf ("USAGE:\n");
    printf ("\tSelect from the various options in the main menu below.\n");
    printf ("\tEntering \"Q\" or \"q\" will return you from a sub-menu\n");
    printf ("\tback to the main menu, or exit the program if you are\n");
    printf ("\talready at the main menu.\n");
    printf ("\tFiles are encrypted using a 5 digit HEX key. This is\n");
    printf ("\tentered in the format \"XXXXX\", where X is a single \n");
    printf ("\tdigit or character from A to F. You are asked to specify\n");
    printf ("\tthe output filename for encrypting and decrypting. You\n");
    printf ("\tcan specify any name but you cannot use the same name as\n");
    printf ("\tthe current input. The instability is intentional ;-)\n");
} // end showHelp


/*
 * Function:    splash
 *
 * Purpose:    Shows a screen with options and allows a user to select from 
 *        those options and then calls the appropriate function.
 *
 * Method:    Accepts an array of possible file names, prints a screen with
 *        options, waits on user input, calls an appropriate function 
 *        based on user input.
 *
 * Returns:    int 0 on success or
 *        int 1 on failure
 */
int splash
(
    int    iNumFiles,    // number of elements in iFileNames array
    char*    iFileNames[]    // array of possible file names
)
{
    char c;        // holds user input for menu

    // print menu
    printf ("Select\t1  to\tEncrypt a file\n");
    printf ("\t2\tDecrypt a file\n");
    printf ("\t3\tAttempt Auto-Decrypt\n");
    printf ("\tH\tHelp &amp; About\n");
    printf ("\tQ\tExit");

    // capture and parse input
    for( ; ;  )
    {
        // capture input
        scanf (" %1s", &amp;c);

        // clear screen
        system ("cls");

        // parse input
        switch( c )
        {
            // encrypt a file
            case '1':
                encrypt(iNumFiles, iFileNames);
                return 0;

            // decrypt a file
            case '2': 
                decrypt(iNumFiles, iFileNames);
                return 0;

            // attempt auto-decrypt
            case '3':
                autoDecrypt(iNumFiles, iFileNames);
                return 0;

            // show help and about screen
            case 'h':
            case 'H':
                showHelp();
                return 0;

            // quit
            case 'q': 
            case 'Q': 
                // this will return and exit the calling loop
                return 1;

            // invalid user input
            default : 
                printf ("Invalid Selection \"%s\"\n\n", &amp;c);
                return 0;
        }
    }
} // end splash


/*
 * Function:    main
 *
 * Purpose:    Checks arguments, calls splash function.
 *
 * Method:    Checks for valid number of arguments, prints an introduction
 *        screen and calls the splash function.
 *
 * Returns:    int EXIT_SUCCESS on success
 *        int EXIT_FAILURE on invalid number of arguments
 */
int main
(
    int    argc,
    char*    argv[]
)
{
    // insufficient arguments
    if( argc &lt; 2 )
    {
        fprintf (stderr, "Usage: %s filename [filename ...]\n", 
            argv[0]);
        exit (EXIT_FAILURE);
    }

    // introduction
    system ("cls");
    printf ("\n");
    printf ("COMP2301 Assignment 2\t\t"
        "(c) Copyright 2004 Ned Martin (40529927)\n");
    printf ("\n");

    // make splash screen
    while( !splash(argc - 1, argv + 1) )
    {
        // run
    }
    return EXIT_SUCCESS;
} // end main
</textarea> 
    <br /> 
    Code &copy; Copyright 2004 Ned Martin</p> 
<p>20-Apr-2004</p> 
</body>
</html>