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: