Hello everyone.
I made this "text file de/encryption" program as my first big C project.
It's working pretty decent, and takes a lot of special characters and tabs without any problems.

I have tested the code by encrypting and then decrypting some source code, and compared the output with the original file, using this tool: http://www.textdiff.com and the result where total identical.

Still have some bugs, and things to come (details in source).

As of now I can only give "one" option like this: "./crypter -e file.txt" or like this "./crypter -er file.txt" (-r would be the replace function, not jet implemented). Because the code relies on argv[2] being the input file, so I can't give options like this (./crypter -e -r file.txt).

So I'm looking for some kind of options structure that would be more efficient, and at the same time fix the last bug, where the input file is checked and opened and output file is opened before the actual options are verified.
I know from BASH that option menu’s can be done very sophisticated and clean. I doubt that there are implementations that will make C this clean and simple, but hoping for something better than my solution.

I realize that I may need to restructure the entire code, because as of now, the encrypt and decrypt function is almost identical, and the main function is really messy.

#include <stdio.h>
#include <stdlib.h>

/* ENCRYPTING FILES
   15.Dec.2012 
   By Morten Lund

Thing to add:
# Progress bar.
# Create options menu. (Rethink the hole "options given" concept.
# Option to delete and replace source file. (-r)

Bugs:
# FIXED 12.Jan.2013 - Not posible to en/decrypt numbers.
# FIXED 15.Jan.2013 - Not posible to en/decrypt "new line" & "tab".
# FIXED 15.Jan.2013 - Not posible to en/decrypt "special chars".
#
# If valid "input file" is given but nonvalid option, output file will still be opened.

*/

#define ALPHA "\\~!?=#)%(&[{]}|@,.;:-_*^+<>1234567890abcdefghijklmnopqrstuvwxyzABCDEFG HIJKLMNOPQRSTUVWXYZ/"
#define CRYPT_ALPHA "/#UwVM{>12E+j,4amN&stin]Ao_6}Q|7q[!f(FJKcLBz^)XYdhZkl9e?Wv@u;35yHrg< P*pSGTx=Ib~%R0O.8:-DC\\"
#define ALPHA_SIZE sizeof(ALPHA)

void encrypt(FILE *fpIN, FILE *fpOUT); // decrypting file
void decrypt(FILE *fpIN, FILE *fpOUT); // encrypting file
void wrongUse(); // Error msg

int main(int argc, char *argv[]) {

    if (sizeof(ALPHA) != sizeof(CRYPT_ALPHA)) {
        printf("Normal and cryptic alphabet is not the same size!!");
        return 1;
    }

    FILE *fpIN;  // Filepointer to input file
    FILE *fpOUT; // Filepointer to output file

    if (argv[1] != NULL && argv[2] != NULL) {           // If argument 1 and 2 is given

        if ((fpIN = fopen(argv[2], "r")) != NULL) {     // If argument 2 is a readable file & open it

            // If no "output file" is given, it's set to default: out.txt
            if (argv[3] == NULL) {                      

                if (strcmp(argv[2], "out.txt") == 0)
                    fpOUT = fopen("out2.txt", "w");
                else
                    fpOUT = fopen("out.txt", "w");
            }
            else
                fpOUT = fopen(argv[3], "w");

            // Running encrypt or decrypt function, depending on what argument 1 (option)
            if (strcmp(argv[1], "-e") == 0)             
                encrypt(fpIN, fpOUT);
            else if (strcmp(argv[1], "-d") == 0)
                decrypt(fpIN, fpOUT);
            else
                wrongUse();

            // Closing input and output pointer after en/decrypt functions
            fclose(fpIN);                               
            fclose(fpOUT);

        } else
            wrongUse();

    } else
        wrongUse();

    return EXIT_SUCCESS;
}

void encrypt(FILE *fpIN, FILE *fpOUT) {

    char curChar; int i, encryptFound;
    while ((curChar = fgetc(fpIN)) != EOF) {

        for (i = 0; i < ALPHA_SIZE; i++) {

            if (curChar == ALPHA[i])
                fputc(CRYPT_ALPHA[i], fpOUT);
        }

        if (curChar == '\n')
            fputc('\n', fpOUT);
        if (curChar == '\t')
            fputc('\t', fpOUT);
    }
}

void decrypt(FILE *fpIN, FILE *fpOUT) {

    char curChar; int i;
    while ((curChar = fgetc(fpIN)) != EOF) {

        for (i = 0; i < ALPHA_SIZE; i++) {

            if (curChar == CRYPT_ALPHA[i])
                fputc(ALPHA[i], fpOUT);
        }

        if (curChar == '\n')
            fputc('\n', fpOUT);
        if (curChar == '\t')
            fputc('\t', fpOUT);
    }
}

void wrongUse() {
    printf("\nUse: option fileIN fileOUT\n\nfileOUT is optional\nif no options is given fileOUT will be named out.txt\nif fileIN is named out.txt, fileOUT will be named out2.txt\n\nOptions:\n-e = encrypt\n-d = decrypt\n");
}

I guess you might be interested in "getopt" API provided by Linux. It allows to get reterive various options from command line. See the man page for more info.

getopt is a good idea.

If it doesn't suit you, what you're trying to do isn't difficult; it just requires you to think carefully about how to actually parse the input.

If the structure is to be something like:

executableFile -a -b -c someFile -d etc. etc.

the solution is relatively simple.

Something like this (in ugly pseduocode!):

for (x = 1 /*assuming zero is the executableName*/; x < numberOfInputs; x++)
{
  if (input[x] == '-e')
  { // set encrpyt mode}

  if (input[x] == '-f')
  { // this is the name of the file to work on
    x++; // move on to the actual filename
    fileToWorkOn = input[x];
  }

  if (input[x] == '-d')
  { // set decrypt mode}

  if (input[x] == '-g')
  { // this is the name of some other file I care about
    x++; // move on to the actual filename
    someOtherFileICareAbout = input[x];
  }
}

More options are easy to add. The order of the options doesn't matter. The stucture of the code is easy to read and understand.

Edited 3 Years Ago by Moschops

Comments
Very helpful, easy to understand, lost of comments

Thanks, very helpful. I will go read up on getops, and decide what to do next. But I actually like Moschops's solution, gives a lot more flexibility to the code.

Okay I redesigned it a bit. But It's failing and I have no idea why, looks like it's in the crypt function or main, but not really sure, can't seem to find out.

OUTPUT:

crypter2.exe -e -r -f test.txt

Input file: test.txt
Output file: out.txt
mode (E)ncryption (D)ecrypting: E
Working 1
Working 2
EXITING crypt successful

Then the program crash. (Compiling with CodeBlocks on windows).

NEW SOURCE:

#include <stdio.h>
#include <stdlib.h>

#define ALPHA "\\~!?=#)%(&[{]}|@,.;:-_*^+<>1234567890abcdefghijklmnopqrstuvwxyzABCDEFG HIJKLMNOPQRSTUVWXYZ/"
#define CRYPT_ALPHA "/#UwVM{>12E+j,4amN&stin]Ao_6}Q|7q[!f(FJKcLBz^)XYdhZkl9e?Wv@u;35yHrg< P*pSGTx=Ib~%R0O.8:-DC\\"
#define ALPHA_SIZE sizeof(ALPHA)

void crypt(FILE *fpIN, FILE *fpOUT, char mode);
void replaceOriginal(char *inFileName, char *outFileName, int replaceMode);

int main(int argc, char* argv[]) {

    if (sizeof(ALPHA) != sizeof(CRYPT_ALPHA)) {
        printf("ALPHA and CRYPT_ALPHA is not the same size!!");
        return EXIT_FAILURE;
    }

/*-----------------OPTIONS MENU:-----------------*/

    char *inFileName;               // Input file name/dir
    char *outFileName = "out.txt";  // Output file name/dir

    char mode = 'N';                // E=encrypt, D=decrypt mode. N=Not set
    int replaceMode = 0;            // Replace original file :default

    int x;
    for (x = 1; x < argc; x++) {

        if (strcmp(argv[x], "-e") == 0) {    // Encryption mode
            mode = 'E';
        }
        if (strcmp(argv[x], "-d") == 0) {    // Decryption mode
            mode = 'D';
        }
        if (strcmp(argv[x], "-r") == 0) {    // Replace original file
            replaceMode = 1;
        }
        if (strcmp(argv[x], "-f") == 0) {    // Input file option
            x++;
            inFileName = argv[x];
        }
        if (strcmp(argv[x], "-o") == 0) {    // Output file option
            x++;
            outFileName = argv[x];
        }
    }
/*-----------------OPTIONS MENU:-----------------*/

    FILE *fpIN, *fpOUT; // File pointer to in and output file

    // If mode is set & inFileName is readable/set & outFileName is writable
    if (mode != 'N' && (fpIN = fopen(inFileName, "r")) != NULL && (fpIN = fopen(outFileName, "w")) != NULL) {

        printf("\nInput file: %s\n", inFileName);
        printf("Output file: %s\n", outFileName);
        printf("mode (E)ncryption (D)ecrypting: %c\n", mode);
        crypt(fpIN, fpOUT, mode);

        printf("EXITING crypt successful\n");

        fclose(fpIN);  // Closing input pointer
        fclose(fpOUT); // and output pointer
    }

    replaceOriginal(inFileName, outFileName, replaceMode); // Replacing if mode is set

    return EXIT_SUCCESS;
}

void crypt(FILE *fpIN, FILE *fpOUT, char mode) {

    printf("Working 1\n");

    char *alphaFrom, *alphaTo;      // Alphabet to copy from, to

    if (mode == 'E') {               // If mode=Encrypt, copy from ALPHA to CRYPT_ALPHA
        alphaFrom = ALPHA;
        alphaTo = CRYPT_ALPHA;
    }
    else if (mode == 'D') {          // If mode=Decrypt, copy from CRYPT_ALPHA to ALPHA
        alphaFrom = CRYPT_ALPHA;
        alphaTo = ALPHA;
    }

    printf("Working 2\n");

    char curChar; int i;                        // Current character, and counter (i)
    while ((curChar = fgetc(fpIN)) != EOF) {    // Assign curChar from fpIN as long it's not EOF

        for (i = 0; i < ALPHA_SIZE; i++) {      // For every character in alphabet size

            if (curChar == alphaFrom[i])        // If character = alphaFrom, replace with alphaTO
                fputc(alphaTo[i], fpOUT);
        }

        if (curChar == '\n')                    // Finding \n and inserting with \n
            fputc('\n', fpOUT);
        if (curChar == '\t')                    // Finding \t and inserting with \t
            fputc('\t', fpOUT);
    }

}

void replaceOriginal(char *inFileName, char *outFileName, int replaceMode) {
    if (replaceMode) {
        printf("\n replacing %s with %s", inFileName, outFileName);
    }
} 

Edited 3 Years Ago by hust921

In the future, if you have a new question you should create a new thread. Otherwise, people will skip over your thread thinking it's solved.

The crash is located on the line where you call fclose(fpOUT). Upon further inspection, it appears that you never actually open fpOUT so why it's not crashing earlier is actually a mystery to me. Your mistake appears to be on this line:

if (mode != 'N' && (fpIN = fopen(inFileName, "r")) != NULL && (fpIN = fopen(outFileName, "w")) != NULL) {    

Not how you're setting fpIN twice. One of those should be fpOUT. If you're wondering how you can find this information yourself in the future, here's what to do if you notice your program crashing, since you're using Code::Blocks.

You can access your debugger by first switching your build target from 'Release' to 'Debug', and then at the menu at the top you go to 'Debug' and click 'Start'. When your program crashes, it will point a yellow arrow right at the line where it crashed.

Tumlee Thanks a lot for your reply, even thought it's in the same thread. I totally forgot I marked it solved. In this case I thought it would be overkill to start another thread, but I will remember that to the next time.

Obvious mistake, even more with the "printf exiting crypt()", but I never really thought about that, since the crypt() function was actually writing to fpOUT, but I guess it dosn't have to exist. Still pretty new to this.

Last thing, about debugging: Normally that would be the first thing I do, but. I can't seem to get it running with arguments, (since the program will not "fully" run without "-e -f somefile"). And when googleling for it, I would find how to set arguments for the compiler, and not the program.

Haven't tried out what you wrote, since I'm on another computer.

To set the command line parameters in Code::Blocks, you go to Project -> Set programs' Arguments..., and if your version is not bugged like mine is, you should see a text box where you can enter command line parameters to be used when running your various build targets. Normally, I'd tell you to RTFM, but I've experienced the nightmare of trying to navigate the CodeBlocks wiki myself so I really don't blame you.

A forewarning, though. By default, your program will actually execute from your main project folder, and will therefore have trouble finding certain files in the same directory as the executable. You might have to circumvent this by doing "-e -f bin/somefile" instead of "-e -f somefile".

Now that I compile a single C file without creating a project, I don't think I need the hole directory, but then, always better to be sure. I actually used full path when testing, just to make sure that it wasn't a problem.

Appreciated the answer, and time. I didn't actually knew that codeblocks had a wiki, nice to have a future reference, instead of disturbing people with such simple questions.

If anyone is interested in the final code:

#include <stdio.h>
#include <stdlib.h>

/* ENCRYPTING FILES
   15.Dec.2012
   By Morten Lund
*/

#define ALPHA "\\~!?=#)%(&[{]}|@,.;:-_*^+<>1234567890abcdefghijklmnopqrstuvwxyzABCDEFG HIJKLMNOPQRSTUVWXYZ/$"
#define CRYPT_ALPHA "/#UwVM{>12E+j,4amN&stin]Ao_6}Q|7q[!f(FJKcLBz^)XYdhZkl9e?Wv@u;35yHrg< P*pSGTx=Ib~%R0$O.8:-DC\\"
#define ALPHA_SIZE sizeof(ALPHA)

void man();
void crypt(FILE *fpIN, FILE *fpOUT, char mode);
void replaceOriginal(char *inFileName, char *outFileName, int replaceMode);

int main(int argc, char* argv[]) {

    // Check is ALPHA and CRYPT_ALPHA is same size
    if (sizeof(ALPHA) != sizeof(CRYPT_ALPHA)) {
        printf("ALPHA and CRYPT_ALPHA is not the same size!!");
        return EXIT_FAILURE;
    }

/*-----------------OPTIONS MENU:-----------------*/

    char *inFileName;               // Input file name/dir
    char *outFileName = "out.txt";  // Output file name/dir

    char mode = 'N';                // E=encrypt, D=decrypt mode. N=Not set
    int replaceMode = 0;            // Replace original file :default

    int x;
    for (x = 1; x < argc; x++) {

        if (strcmp(argv[x], "-e") == 0) {    // Encryption mode
            mode = 'E';
        }
        if (strcmp(argv[x], "-d") == 0) {    // Decryption mode
            mode = 'D';
        }
        if (strcmp(argv[x], "-r") == 0) {    // Replace original file
            replaceMode = 1;
        }
        if (strcmp(argv[x], "-f") == 0) {    // Input file option
            x++;
            inFileName = argv[x];
        }
        if (strcmp(argv[x], "-o") == 0) {    // Output file option
            x++;
            outFileName = argv[x];
        }
    }
/*-----------------OPTIONS MENU:-----------------*/

    FILE *fpIN, *fpOUT; // File pointer to in and output file

    // If mode is set & inFileName is readable/set & outFileName is writable
    if (mode != 'N' && (fpIN = fopen(inFileName, "r")) != NULL && (fpOUT = fopen(outFileName, "w")) != NULL) {

        printf("\nInput file: %s\n", inFileName);
        printf("Output file: %s\n", outFileName);
        printf("mode (E)ncryption (D)ecrypting: %c\n", mode);
        crypt(fpIN, fpOUT, mode);

        fclose(fpIN);  // Closing input pointer
        fclose(fpOUT); // and output pointer
    } else
        man();

    replaceOriginal(inFileName, outFileName, replaceMode); // Replacing if mode is set

    return EXIT_SUCCESS;
}

void crypt(FILE *fpIN, FILE *fpOUT, char mode) {

    char *alphaFrom, *alphaTo;      // Alphabet to copy from, to

    if (mode == 'E') {               // If mode=Encrypt, copy from ALPHA to CRYPT_ALPHA
        alphaFrom = ALPHA;
        alphaTo = CRYPT_ALPHA;
    }
    else if (mode == 'D') {          // If mode=Decrypt, copy from CRYPT_ALPHA to ALPHA
        alphaFrom = CRYPT_ALPHA;
        alphaTo = ALPHA;
    }

    char curChar; int i;                        // Current character, and counter (i)
    while ((curChar = fgetc(fpIN)) != EOF) {    // Assign curChar from fpIN as long it's not EOF

        for (i = 0; i < ALPHA_SIZE; i++) {      // For every character in alphabet size

            if (curChar == alphaFrom[i])        // If character = alphaFrom, replace with alphaTO
                fputc(alphaTo[i], fpOUT);
        }

        if (curChar == '\n')                    // Finding \n and inserting with \n
            fputc('\n', fpOUT);
        if (curChar == '\t')                    // Finding \t and inserting with \t
            fputc('\t', fpOUT);
    }

}

void replaceOriginal(char *inFileName, char *outFileName, int replaceMode) {

    if (replaceMode) {                                  // If replaceMode is set
        if (remove(inFileName) == 0) {                  // Remove inFileName
            if (rename(outFileName, inFileName) == 0)   // Rename outFileName
                printf("File %s successfully replaced\n", inFileName);
        } else
            printf("Couldn't delete %s.. Replace failed", inFileName);
    }
}

void man() {

    printf("\n.....Simple Crypter.....\n\nUsage: sCrypter [Arguments] -f [input file]\n\tOr: sCrypter -e -f file.txt\n\tOr: sCrypter -d -r -f file.txt\n\tOr: sCrypter -e -f file.txt -o file2.txt\n\n");
    printf("  [-h]  This page\n");
    printf("  [-f]  Set input file\n");
    printf("  [-o]  Set output file [OPTIONAL] default: out.txt\n");
    printf("  [-e]  Encrypting input file\n");
    printf("  [-d]  Decrypting input file\n");
    printf("  [-r]  Replace the input file with the new output file\n\n");


}
This question has already been answered. Start a new discussion instead.