    001: #include <iostream>
    002: #include <fstream>
    003: #include <string>
    004: #include <sstream>
    005:
    006: using namespace std;
    007:
    008: /**
    009:    Prints usage instructions.
    010:    @param program_name the name of this program
    011: */
    012: void usage(string program_name)
    013: {
    014:    cout << "Usage: " << program_name
    015:       << " [-d] [-kn] infile outfile\n";
    016:    exit(1);
    017: }
    018:
    019: /**
    020:    Prints file opening error message
    021:    @param filename the name of the file that could not be opened
    022: */
    023: void open_file_error(string filename)
    024: {
    025:    cout << "Error opening file " << filename << "\n";
    026:    exit(1);
    027: }
    028:
    029: /**
    030:    Computes correct remainder for negative dividend.
    031:    @param a an integer
    032:    @param n an integer > 0
    033:    @return the mathematically correct remainder r such that
    034:       a - r is divisible by n and 0 <= r and r < n
    035: */
    036: int remainder(int a, int n)
    037: {
    038:    if (a >= 0)
    039:       return a % n;
    040:    else
    041:       return n - 1 - (-a - 1) % n;
    042: }
    043:
    044: /**
    045:    Encrypts a character using the Caesar cipher.
    046:    @param ch the character to encrypt
    047:    @param k the encryption key
    048:    @return the encrypted character
    049: */
    050: char encrypt(char ch, int k)
    051: {
    052:    const int NLETTER = 'Z' - 'A' + 1;
    053:    if ('A' <= ch && ch <= 'Z')
    054:       return static_cast<char>(
    055:          'A' + remainder(ch - 'A' + k, NLETTER));
    056:    if ('a' <= ch && ch <= 'z')
    057:       return static_cast<char>(
    058:          'a' + remainder(ch - 'a' + k, NLETTER));
    059:    return ch;
    060: }
    061:
    062: /**
    063:    Encrypts a stream using the Caesar cipher.
    064:    @param in the stream to read from
    065:    @param out the stream to write to
    066:    @param k the encryption key
    067: */
    068: void encrypt_file(istream& in, ostream& out, int k)
    069: {
    070:    char ch;
    071:    while (in.get(ch))
    072:       out.put(encrypt(ch, k));
    073: }
    074:
    075: /**
    076:    Converts a string to an integer, e.g. "3" -> 3.
    077:    @param s a string representing an integer
    078:    @return the equivalent integer
    079: */
    080: int string_to_int(string s)
    081: {
    082:    istringstream instr(s);
    083:    int n;
    084:    instr >> n;
    085:    return n;
    086: }
    087:
    088: int main(int argc, char* argv[])
    089: {
    090:    bool decrypt = false;
    091:    int key = 3;
    092:    int nfile = 0; /* the number of files specified */
    093:    ifstream infile;
    094:    ofstream outfile;
    095:
    096:    if (argc < 3 or argc > 5) usage(string(argv[0]));
    097:
    098:    int i;
    099:    for (i = 1; i < argc; i++)
    100:    {  string arg = string(argv[i]);
    101:       if (arg.length() >= 2 and arg[0] == '-')
    102:       /* it is a command line option */
    103:       {
    104:          char option = arg[1];
    105:          if (option == 'd')
    106:          decrypt = true;
    107:          else if (option == 'k')
    108:          key = string_to_int(arg.substr(2, arg.length() - 2));
    109:       }
    110:       else
    111:       {
    112:          nfile++;
    113:          if (nfile == 1)
    114:          {
    115:             infile.open(arg.c_str());
    116:             if (infile.fail()) open_file_error(arg);
    117:          }
    118:          else if (nfile == 2)
    119:          {
    120:             outfile.open(arg.c_str());
    121:             if (outfile.fail()) open_file_error(arg);
    122:          }
    123:       }
    124:    }
    125:
    126:    if(nfile != 2) usage(string(argv[0]));
    127:
    128:    if (decrypt) key = -key;
    129:
    130:    encrypt_file(infile, outfile, key);
    131:    infile.close();
    132:    outfile.close();
    133:    return 0;
    134: }
    135: