-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
288 lines (268 loc) · 9.35 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
/**
* Cesar encryption and decryption program
* @author Pierre Maurice Schwang
* @copyright 2020 Pierre Maurice Schwang
*/
/**
* Defines the charset of characters which should be encoded.
* The cesar encryption is typically case insensitive, so we only use the alphabet in lowercase.
*/
#define CHARSET "abcdefghijklmnopqrstuvwxyz"
/**
* Encrypts a passed input based on the cesar algorithm.
* If invalid data was passed, the program will exit and notify the user about the problem.
* @param input The string to encrypt.
* @return The encrypted cesar string.
*/
char *cesar_encrypt(char *input, unsigned int shift);
/**
* Decrypts a passed input based on the cesar algorithm.
* If invalid data was passed, the program will exit and notify the user about the problem.
* @param input The string to decrypt.
* @return The decrypted readable string.
*/
char *cesar_decrypt(char *input, int shift);
/**
* Utility method to lowercase a complete char array.
* Required for cesar_encrypt, because the cesar encryption is case insensitive.
* @param input The string to lowercase.
* @return The lowercase string.
*/
char *lowercase(char *input);
/**
* Prompts the user to define the shift amount for the encryption & decryption.
* (shift = how many times the characters should be moved to the right).
* @return The inputted shift.
*/
int parse_shift_input();
/**
* Reads the content of the passed file.
*
* @param fileName The path to the file.
* @return The file content or NULL if an error occourred.
*/
char *read_file(char *fileName);
/**
* Writes data to a file.
*
* @param fileName The path to the file.
*/
void write_file(char *fileName, char *content);
/**
* Program initialization
* @return the exit code
*/
int main()
{
// Parse the provided shift by the user and validate.
int shift = parse_shift_input();
if (shift < 0)
{
printf("Incorrect shift input - Exiting program!");
return 1;
}
// Print all available options the user can select from.
printf("##############################################\n");
printf("# Cesar Encryption /Decryption #\n");
printf("##############################################\n");
printf("# #\n");
printf("# 1. Encrypt input #\n");
printf("# 2. Decrypt input #\n");
printf("# 3. Encrypt file #\n");
printf("# 4. Decrypt file #\n");
printf("# #\n");
printf("# 5. Exit #\n");
printf("# #\n");
printf("##############################################\n\n");
// Parse the selected option by the user
int option;
printf("Select your action: ");
scanf("%d", &option);
// Removes the trailing new line character, which fixes an issue related to fgets()
getchar();
// Defines the buffer (memory size) for the input which can be provided by the user.
char input[2048];
switch (option)
{
case 1: // Menu entry: "Encrypt input"
printf("What should be encrypted? \n > ");
// Read the input provided by the user.
fgets(input, 2048, stdin);
// Print the encrypted input using the cesar algorithm.
printf("%s", cesar_encrypt(input, shift));
break;
case 2: // Menu entry: "Encrypt input"
printf("What should be decrypted? \n > ");
// Read the input provided by the user.
fgets(input, 2048, stdin);
// Print the decrypted input using the cesar algorithm.
printf("%s", cesar_decrypt(input, shift));
break;
case 3: // Menu entry: "Encrypt file"
printf("Enter the file name: \n > ");
// Read the input provided by the user (The file name).
scanf("%s", &input);
// Validate the provided file name (existence, ...) and read its content.
char *content = read_file(input);
if (content == NULL)
return 1;
// Write the encrypted file content to a new (or existing) file with the name of the file + the suffix ".out.txt"
write_file(strcat(input, ".out.txt"), cesar_encrypt(content, shift));
break;
case 4: // Menu entry: "Decrypt file"
printf("Enter the file name: \n > ");
// Read the input provided by the user (The file name).
scanf("%s", &input);
// Validate the provided file name (existence, ...) and read its content.
content = read_file(input);
if (content == NULL)
return 1;
// Write the decrypted file content to a new (or existing) file with the name of the file + the suffix ".out.txt"
write_file(strcat(input, ".out.txt"), cesar_decrypt(content, shift));
break;
case 5: // Menu entry: "Exit"
printf("Exiting program");
break;
default: // Invalid option (no menu entry existing for selected option)
printf("Invalid input - Exiting program");
return 1;
break;
}
return 0;
}
char *read_file(char *fileName)
{
FILE *file;
char *buffer;
long bytes;
// Try to open the provided file in "read" mode.
file = fopen(fileName, "r");
// if fopen() returns NULL, that means that the file could not be found or accessed.
if (file == NULL)
{
printf("File could not be found!");
return NULL;
}
// We read the amount of bytes the file contains.
fseek(file, 0, SEEK_END);
bytes = ftell(file);
fseek(file, 0, SEEK_SET);
// And allocate the amount of bytes in the memory.
buffer = (char *)calloc(bytes, sizeof(char));
// If the buffer is NULL, that means that an error occurred while allocating the memory (e.g. missing memory)
if (buffer == NULL)
{
printf("An error occurred while allocating the required memory!");
return NULL;
}
// Now we read all bytes from the file into the created buffer.
fread(buffer, sizeof(char), bytes, file);
// And close the file after reading it.
fclose(file);
return buffer;
}
void write_file(char *fileName, char *content)
{
// We open the file in "write" mode (which ensures, the file will be created if it does not exist yet)
FILE *file = fopen(fileName, "w");
// We write the provided content into the file.
fprintf(file, "%s", content);
// And close the file.
fclose(file);
// We notify the user where the data was written to.
printf("Data was written to %s", fileName);
}
int parse_shift_input()
{
int input;
printf("Define the shift amount (0-26): ");
// We read the provided input by the user.
scanf("%d", &input);
// And validate it (must be between 0 and 26)
if (input < 0 || input > 26)
{
return -1;
}
return input;
}
char *cesar_encrypt(char *input, unsigned int shift)
{
// create array based on passed shift for replacing the chars
char secret[26] = "";
for (int i = 0; i < 26; i++)
{
// get new index of specific character with the provided shift
unsigned int shiftedIndex = i + shift;
// if the shifted index is out of bounds, we start again at the beginning
if (shiftedIndex >= 26)
{
shiftedIndex = shiftedIndex - 26;
}
secret[i] = CHARSET[shiftedIndex];
}
// create a dynamic string, so we dont end up in a segfault
char *toEncrypt = strdup(input);
// make input lowercase
toEncrypt = lowercase(toEncrypt);
// iterate over every character of the input
for (int i = 0; i < strlen(toEncrypt); i++)
{
// We try to find the element of the current character of the string in the defined CHARSET.
char *element = strchr(CHARSET, toEncrypt[i]);
// If the pointer is NULL, that means that the current character is not part of the defined CHARSET and can be skipped.
if (element == NULL)
continue;
// Get the index of the current character in the defined CHARSET.
unsigned int charsetIndex = (int)(element - CHARSET);
// And change the current character of the string to the shifted version (encrypted).
toEncrypt[i] = secret[charsetIndex];
}
return toEncrypt;
}
char *cesar_decrypt(char *input, int shift)
{
// create array based on passed shift for replacing the chars
char secret[26] = "";
for (int i = 0; i < 26; i++)
{
// get new index of specific character with the provided shift
int shiftedIndex = i - shift;
// if the shifted index is out of bounds, we start again at the end
if (shiftedIndex < 0)
{
shiftedIndex = shiftedIndex + 26;
}
secret[i] = CHARSET[shiftedIndex];
}
// create a dynamic string, so we dont end up in a segfault
char *toDecrypt = strdup(input);
// make input lowercase
toDecrypt = lowercase(toDecrypt);
// iterate over every character of the input
for (int i = 0; i < strlen(toDecrypt); i++)
{
// We try to find the element of the current character of the string in the defined CHARSET.
char *element = strchr(CHARSET, toDecrypt[i]);
// If the pointer is NULL, that means that the current character is not part of the defined CHARSET and can be skipped.
if (element == NULL)
continue;
// Get the index of the current character in the defined CHARSET.
unsigned int charsetIndex = (int)(element - CHARSET);
// And change the current character of the string to the shifted version (encrypted).
toDecrypt[i] = secret[charsetIndex];
}
return toDecrypt;
}
char *lowercase(char *input)
{
// We loop every character of the input and try to lowercase it.
for (int i = 0; input[i]; i++)
{
input[i] = tolower(input[i]);
}
return input;
}