/* * loserar.c * Contains main() and general functions * LoseRAR - Peter Graham 2010 */ #include #include #include //#include //#include ////////// size: ________ //////////////////////////////////// #define VER_SIG "LR_0.1.0" // must be 8 bytes, no more no less #define USAGE "usage: %s [-e | -c | -h] filename [new_filename] [expansion_factor]\n" /* Instructions * ------------ * LoseRAR is the opposite of typical compression programs, it will * increase the file size of any given file by padding it out with * random characters. You can specify the expansion factor, which is * how many times the file size is doubled. * For example, an expansion factor of 1 will turn 1KB into 2KB. * Or, an expansion factor of 10 will turn 1KB into 1MB * If an expansion factor of 0 is used, the file size is be unchanged. */ // file functions int expand_file(char * oldfilename, char * newfilename, unsigned int num_of_passes); int compress_file(char * oldfilename, char * newfilename, unsigned int num_of_passes); // file and expansion algorithms char * expand(char * string, unsigned int size, unsigned int num_of_passes); char * compress(char * string, unsigned int size, unsigned int num_of_passes); // misc functions char randomChar(); int errorCodes(int returnvalue); /* main(argc, argv) * Checks command line arguements and calls the correct algorithm on * the given file. * Returns: 0 for success, else failure. */ int main(int argc, char** argv) { int rtn = -255; // check for at least one arguement if(argc <= 1) { printf(USAGE, argv[0]); return(EXIT_FAILURE); } // check for help if(!strcmp(argv[1], "-h")) { char* help = "LoseRAR is the opposite of typical compression programs, it " "increases the file size of any given file by padding it out " "with random characters. You can specify the expansion factor, " "which is how many times the file size is doubled.\n\n" "For example, an expansion factor of 1 will turn 1KB into 2KB.\n" "Or, an expansion factor of 10 will turn 1KB into 1MB\n" "An expansion factor of 0 leaves the filesize unchanged.\n\n"; printf("%s", help); return(EXIT_SUCCESS); } // check for first parameter if(strcmp(argv[1], "-e") && strcmp(argv[1], "-c")) { printf("Could not find -e or -c as first parameter.\n"); printf("%s, %s, %s...", argv[1], argv[2], argv[3]); printf(USAGE, argv[0]); return(EXIT_FAILURE); } // check for old_filename if(!argv[2]) { printf("Filename not specified.\n"); printf(USAGE, argv[0]); return(EXIT_FAILURE); } else { int oldfilename_size = strlen(argv[2]); char old_filename[oldfilename_size]; strcpy(old_filename, argv[2]); } // check for new_filename char * new_filename; if(!argv[3] || argc <= 3) { new_filename = "output"; } else { int newfilename_size = strlen(argv[3]); new_filename = malloc( sizeof(char) * newfilename_size ); strcpy(new_filename, argv[3]); } // check what expansion factor user wants int expansion_factor; if(!argv[4] || argc <= 4) { expansion_factor = 1; } else { // returns 0 if invalid, which is fine as leaves file unchanged expansion_factor = atoi(argv[4]); if(expansion_factor < 0) { expansion_factor = 0; } } // do either an expansion or compression, depending on arguments if (!strcmp(argv[1], "-e")) { if(expansion_factor > 10) printf("Warning: A very large file is going to created.\n"); rtn = expand_file(argv[2], new_filename, expansion_factor); errorCodes(rtn); printf("Expansion complete.\n"); } else if (!strcmp(argv[1], "-c")) { rtn = compress_file(argv[2], new_filename, expansion_factor); errorCodes(rtn); printf("Compression complete.\n"); } else { errorCodes(2); // "unable to parse arguments" } //system("PAUSE"); return(EXIT_SUCCESS); } /* ******************************** * expansion/compression algorithms /* ******************************** /* expand(string, size, num_of_passes) * Takes a string and doubles its length by padding it with random * characters. Requires memory space for both the old string and the * new one, but frees the old string after the new string is created. * Returns: an expanded string */ char * expand(char * string, unsigned int size, unsigned int num_of_passes) { // assumption : size of char is 1 // note: 50 passes on 1 character results in 1 petabyte of data! :D if(num_of_passes > 0) { int i = 0, j = 0; char * newstring; newstring = (char*) malloc( sizeof(char) * size * 2 ); while(i < size) // we use size due to possibility of nulls in file { newstring[j] = string[i]; newstring[j+1] = randomChar(); i++; j+=2; } free(string); // we don't need the old string anymore as we've copied it over string = expand(newstring, size * 2, num_of_passes - 1); } else { string[size] = '\0'; // null terminate string } return string; } /* compress(string, size, num_of_passes) * Takes a string and compresses it by removing every other character, * undoing the effects of the expand() function. Requires memory to * be allocated for both the old and new string. * Returns: a compressed string */ char * compress(char * string, unsigned int size, unsigned int num_of_passes) { if(num_of_passes > 0) { unsigned int i = 0, j = 0; char * newstring; newstring = (char*) malloc( sizeof(char) * size / 2 ); /* we have to use the size rather than treating the file as a string due to possibility of null characters */ while(j < size) { newstring[i] = string[j]; i++; j+=2; } free(string); // free the old string, we don't need it anymore as we've copied it over string = compress(newstring, size / 2, num_of_passes - 1); } string[size] = '\0'; // null terminate string return string; } /* ************** * file functions /* ************** /* expand_file(oldfilename, newfilename, num_of_passes) * Takes an old file and expands it by as many passes as required, * writing the expanded string to the new file. Also adds meta data * at the start of the file. * return values: * 1 general error * 10 unable to open file * 11 unable to create new file * 12 could not write metadata */ int expand_file(char * oldfilename, char * newfilename, unsigned int num_of_passes) { // create a buffer char buffer; FILE *fp_old = NULL; FILE *fp_new = NULL; fp_old = fopen( oldfilename, "r"); if (fp_old) { fp_new = fopen( newfilename, "w+" ); if (fp_new) { // add meta data, 14 bytes // 8 byte version signature, pipe, followed by 4 byte expansion multiplier, then pipe if(fwrite(VER_SIG, sizeof(char), 8, fp_new) != 8) return 12; if(fprintf(fp_new, "|%04d|", num_of_passes) < 1) return 12; // get the size of the old file fseek(fp_old, 0L, SEEK_END); int oldfilesize; oldfilesize = ftell(fp_old); fseek(fp_old, 0L, SEEK_SET); char * string; string = (char*) malloc( sizeof(char) * oldfilesize ); int i = 0; while((buffer = getc(fp_old)) != EOF) { string[i] = buffer; i++; } string = expand(string, oldfilesize, num_of_passes); printf("Bytes written: %i\n", fprintf(fp_new,"%s",string)); /*writes*/ free(string); fclose(fp_new); fclose(fp_old); return 0; } else { // unable to create new file :( fclose(fp_old); return 11; } } else { // null pointer, epic fail // no need to fclose() return 10; } return 0; } /* compress_file(oldfilename, newfilename, num_of_passes) * Takes an LoseRAR file and compresses it by as many passes as required, * writing the compressed string to the new file. Reverses the effects * of expand_file(). The meta data is not currently used. * return values: * 1 general error * 20 unable to open file * 21 unable to create new file */ int compress_file(char * oldfilename, char * newfilename, unsigned int num_of_passes) { // create a buffer char buffer; FILE *fp_old = NULL; FILE *fp_new = NULL; fp_old = fopen( oldfilename, "r" ); if (fp_old) { fp_new = fopen( newfilename, "w+" ); if (fp_new) { // get the size of the old file fseek(fp_old, 0L, SEEK_END); int oldfilesize; oldfilesize = ftell(fp_old) - (14); fseek(fp_old, 14L, SEEK_SET); // start from after the LR sig char * string; string = (char*) malloc( sizeof(char) * oldfilesize ); int i = 0; while((buffer = getc(fp_old)) != EOF) { string[i] = buffer; i++; } printf("Filesize: %i, Passes: %i, %s\n", oldfilesize, num_of_passes, string); string = compress(string, oldfilesize, num_of_passes); printf("New string: %s\n", string); printf("Bytes written: %i\n", fprintf(fp_new,"%s",string)); /*writes*/ free(string); fclose(fp_new); fclose(fp_old); return 0; } else { // unable to create new file fclose(fp_old); return 21; } } else { // null pointer, epic fail // no need to fclose() return 20; } return 0; } /* randomChar() * Returns a random lowercase character from the alphabet */ char randomChar() { return rand() % 26 + 'a'; } /* errorCodes(returnvalue) * General error handling. Probably not the best way to do it. * Catches any return values and prints a relevant message before * exiting the program. If no error found, just returns 1. */ int errorCodes(int returnvalue) { switch(returnvalue) { case 1: printf("General error.\n"); exit(EXIT_FAILURE); break; case 2: printf("Unable to parse arguments.\n"); exit(EXIT_FAILURE); break; case 10: printf("Unable to open file to expand.\n"); exit(EXIT_FAILURE); break; case 11: printf("Unable to create new exapanded file.\n"); exit(EXIT_FAILURE); break; case 12: printf("Could not write metadata to expanded file.\n"); exit(EXIT_FAILURE); break; case 20: printf("Unable to open file to compress.\n"); exit(EXIT_FAILURE); break; case 21: printf("Unable to create compressed file.\n"); exit(EXIT_FAILURE); break; default : return 1; } }