Vigenere cipher in C - c

I am having trouble debugging my implementation of Vigenere's cipher in C. The error arises when using file input (-f flag) where the file contains fewer than 6 chars (+ 1 EOF), it spits out some number of random characters as well as the expected input and I cannot figure out why this is, although I suspect it has something to do with the second part of my question which is, when using fread(), I noticed that this
if( fread(fcontents, fsize, sizeof(char), file) != 1 ) {...}
will run with no issues, whereas this
if( fread(fcontents, sizeof(char), fsize, file) != 1 ) {...}
doesn't work (i.e. causes fread() to return 1 and trigger the error handling code beneath it), which I would expect to be the other way around according to answers from here, but I may just be misinterpreting something.
My complete code is as follows:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#define ENC 0 //Encrypt mode
#define DEC 1 //Decrypt mode
#define INP 0 //Commandline input mode
#define FLE 1 //File input mode
typedef struct {
char *password;
char *file_name;
char *input;
int edmode;
int ifmode;
} options;
void string_clean(char *source)
{
char *i = source;
char *j = source;
while(*j != 0) {
*i = *j++;
if( *i != ' ' && (isupper(*i) || islower(*i)) )
i++;
}
*i = 0;
}
char *ftostr(char *file_name) //Takes a file name as input and returns a char* to the files contents, returns NULL pointer on faliure. Allocated string must be freed after use by parent function to prevent memory leaks.
{
FILE *file;
long int fsize;
char *fcontents;
if( !(file = fopen(file_name, "r")) ) {
fprintf(stderr, "Error opening file \"%s\"!\n", file_name);
return NULL;
}
fseek(file, 0L, SEEK_END);
fsize = ftell(file);
rewind(file);
if( !(fcontents = malloc((fsize + 1) * sizeof(char))) ) {
fclose(file);
fprintf(stderr, "Error allocating memory!");
return NULL;
}
if( fread(fcontents, fsize, sizeof(char), file) != 1 ) { //suspected buggy line
fclose(file);
free(fcontents);
fprintf(stderr, "Error copying file to memory!\n");
return NULL;
}
fclose(file);
return fcontents;
}
options parse_opts(int argc, char *argv[])
{
int c;
options args;
args.edmode = ENC; //enable encrypt mode by default
args.ifmode = INP; //enable commandline input mode by default
args.file_name = NULL;
args.password = NULL;
args.input = NULL;
opterr = 0;
while((c = getopt(argc, argv, "dep:i:f:")) != -1) {
switch(c) {
case 'e':
args.edmode = ENC;
break;
case 'd':
args.edmode = DEC;
break;
case 'p':
args.password = optarg;
break;
case 'i':
args.input = optarg;
args.ifmode = INP;
break;
case 'f':
args.file_name = optarg;
args.ifmode = FLE;
break;
case '?':
if(optopt == 'f' || optopt == 'p' || optopt == 'i')
fprintf(stderr, "Option -%c requires an argument.\n", optopt);
else if(isprint(optopt))
fprintf(stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
fprintf(stderr, "Usage: %s (-f file_name || -i input) -p password [options]\n"
"Optional: -e -d\n", argv[0]);
exit(-1);
}
}
return args;
}
char *vigenere_dec(char cipher_text[], char cipher[])
{
char *plain_text;
string_clean(cipher_text);
string_clean(cipher);
int plain_text_len = strlen(cipher_text);
int cipher_len = strlen(cipher);
if( !(plain_text = malloc((plain_text_len + 1) * sizeof(char))) )
return 0;
for(int i = 0; i < cipher_len; i++) {
if(isupper(cipher[i]))
cipher[i] -= 'A';
else if(islower(cipher[i]))
cipher[i] -= 'a';
}
for(int i = 0, j = 0; i < plain_text_len; i++, j++) {
if(j == cipher_len)
j = 0;
if(isupper(cipher_text[i]))
cipher_text[i] -= 'A';
else if(islower(cipher_text[i]))
cipher_text[i] -= 'a';
plain_text[i] = ((cipher_text[i] - cipher[j]) % 26);
if(plain_text[i] < 0)
plain_text[i] += 26;
plain_text[i] += 'A';
}
return plain_text;
}
char *vigenere_enc(char plain[], char cipher[])
{
char *cipher_text;
string_clean(plain);
string_clean(cipher);
int plain_len = strlen(plain);
int cipher_len = strlen(cipher);
if(plain_len == 0 || cipher_len == 0)
return NULL;
if( !(cipher_text = malloc((plain_len + 1) * sizeof(char))) )
return NULL;
for(int i = 0; i < cipher_len; i++) {
if(isupper(cipher[i]))
cipher[i] -= 'A';
else if(islower(cipher[i]))
cipher[i] -= 'a';
}
for(int i = 0, j = 0; i < plain_len; i++, j++) {
if(j == cipher_len)
j = 0;
if(isupper(plain[i]))
plain[i] -= 'A';
else if(islower(plain[i]))
plain[i] -= 'a';
cipher_text[i] = ((plain[i] + cipher[j]) % 26) + 'A';
}
return cipher_text;
}
int main(int argc, char *argv[])
{
options args;
char *output_text = NULL;
args = parse_opts(argc, argv);
if(args.password == NULL) {
fprintf(stderr, "Password uninitialised!\n");
exit(-1);
}
if(args.input == NULL && args.file_name == NULL) {
fprintf(stderr, "Input stream uninitialised!\n");
exit(-1);
}
if(args.ifmode == INP) {
if(args.edmode == ENC)
output_text = vigenere_enc(args.input, args.password);
else if(args.edmode == DEC)
output_text = vigenere_dec(args.input, args.password);
} else if(args.ifmode == FLE) {
if( !(args.input = ftostr(args.file_name)) )
return -1;
if(args.edmode == ENC)
output_text = vigenere_enc(args.input, args.password);
else if(args.edmode == DEC)
output_text = vigenere_dec(args.input, args.password);
free(args.input);
}
puts(output_text);
free(output_text);
return 0;
}

The fault is unterminated strings. You allowed room for the termination char with
if( !(plain_text = malloc(plain_text_len + 1)) ) // (simplified)
but after you have set, for example
plain_text[i] += 'A';
you need to end the string with
plain_text[i+1] = '\0';
or when the string is complete.
For the second part, you quoted another question, but failed to see that fread() returns the number of items read. So if you swap its size and count arguments, expect a different result (unless fsize == 1).
So you can either use
if( fread(fcontents, fsize, 1, file) != 1 ) {...}
or this
if( fread(fcontents, 1, fsize, file) != fsize ) {...}
Note I changed sizeof(char) to 1 since by definition, it is.

Related

C program in CLion runs perfectly in Debug mode but returns exit code -1073741819 (0xC0000005) when executed normally

I am doing the Advent of Code, and I am trying to do it all in C. I am currently on day three, and I kind of solved it, but as the title says it behaves very strangely in my IDE CLion. Here is the objective.
I would very much like to know why it is not running properly, and finding out why appears to be beyond my capability.
This is my code:
//
// Created by gusta on 2022-12-06.
//
#include "aocio.h"
#include <string.h>
int cexists(char* cp, char c, int length);
char getDuplicate(char* line);
int main() {
FILE* fptr = openFile("../Inputs/day3.txt");
char* line;
while (readLine(fptr, &line)) {
char c = getDuplicate(line);
putchar(c);
}
return 0;
}
char getDuplicate(char* line) { // Returnerar STRING_END om ingen fanns
unsigned int length = strlen(line);
char letters[length/2];
char* cp = &letters[0];
for (int i = 0; i < length/2; i++) {
letters[i] = ' ';
}
for (int index = 0; line[index] != STRING_END; index++) {
if (index < length/2) {
int i_char = cexists(letters, line[index], length/2);
if (i_char == -1) {
*cp = line[index];
cp++;
}
}
else {
if (cexists(letters, line[index], length/2) != -1) {
return line[index];
}
}
}
}
int cexists(char* cp, char c, int length) {
for (int i = 0; i < length; i++) {
if (cp[i] == c) {
return i;
}
}
return -1;
}
Here is aocoi.h (advent of code input output.h):
//
// Created by gusta on 2022-12-01.
//
#ifndef ADVENTOFCODE_AOCIO_H
#define ADVENTOFCODE_AOCIO_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define SIGN_ASCII 45
#define TRUE 1
#define FALSE 0
#endif //ADVENTOFCODE_AOCIO_H
#define STRING_END '\0'
#define NUMBERS_ASCII 48
char* prompt(const char* question) {
printf(question);
char* string = malloc(1);
int curChar = 0, index = 0;
while (curChar != '\n') {
curChar = getchar();
if (curChar == '\n'){
string[index] = STRING_END;
}
else{
if (index > 0) {
string = (char*) realloc(string, index+1);
}
string[index] = curChar;
}
index++;
}
return string;
}
FILE* openFile(const char* fileName) {
FILE *fptr;
fptr = fopen(fileName, "r");
if (fptr == NULL) {
printf("Big fat file error!!\n");
fclose(fptr);
getchar();
exit(-1);
}
return fptr;
}
char readLine(FILE* fptr, char** line) {
int index = 0, end = 0;
char* string = (char *) malloc(1);
char curChar;
do {
end = fscanf(fptr, "%c", &curChar);
if (end == EOF) {
string[index] = STRING_END;
fclose(fptr);
*line = string;
return FALSE;
}
if (curChar == '\n') {
string[index] = STRING_END;
*line = string;
return TRUE;
}
else {
if (index > 0) {
string = (char *) realloc(string, index + 1);
}
string[index] = curChar;
index++;
}
} while (end != EOF);
}
int parseToInt(char* string) {
int numberLength = 0, number = 0;
int sign = FALSE;
for (int index = 0; string[index] != STRING_END; index++) {
numberLength++;
}
for (int index = numberLength-1; index >= 0; index--) {
char curChar = string[index];
if (index == 0 && curChar - SIGN_ASCII == 0) {
sign = TRUE;
continue;
}
if (curChar - NUMBERS_ASCII >= 0 && curChar - NUMBERS_ASCII <= 9) {
int num = (int) (curChar - NUMBERS_ASCII);
num *= (int)(pow(10, numberLength-index-1));
number += num;
}
else {
printf("Felaktig inmatning. parseInt kan bara ta in tal"); // Invalid input. parseInt can only take in numbers
getchar();
exit(-1);
}
}
if (sign == TRUE) {
number *= -1;
}
return number;
}
Through searching the web I found that the error code should mean stack overflow (ha!) or something like that, but I cannot for the life of me find any place in my code where that could occur. All pointers and stuff should be automatically freed - is there something I have missed?
In readLine():
if (index > 0) {
string = (char *) realloc(string, index + 1);
}
string[index] = curChar;
After this section, the buffer has size index+1, therefore string[index] is the last element you can write to. Afterwards, you do
index++;
Now, writing to string[index] is out of bounds, resulting in a buffer overflow. This is what happens when an EOF or EOL is detected.

Get_next_line : Abort when BUFFER_SIZE between 8 and 15 on Mac

I am currently completing my "Get_next_line" project. It is a function that reads a file and allows you to read a line ending with a newline character from a file descriptor. When you call the function again on the same file, it grabs the next line. This project deals with memory allocation and when to free and allocate memory to prevent leaks. The value (-1) is returned if an error occurred, (0) is returned if the file is finished reading, and (1) is returned if a line is read.
However, while testing my final code I encounter an abort when BUFFER_SIZE is between 8 and 15. This error only appears on Mac. Does anyone have an idea of what might be the issue?
Compile with :
gcc -Wall -Wextra -Werror -D BUFFER_SIZE=32 get_next_line.c get_next_line_utils.c
The get_next_line() function and some support code;
#include "get_next_line.h"
int ft_backslash(const char *s)
{
int i;
i = -1;
while (s[++i])
if (s[i] == '\n')
return (1);
if (s[i] == '\n')
return (1);
return (0);
}
int ft_read_buffer(int fd, char *buf)
{
int ret;
ret = 0;
ret = read(fd, buf, BUFFER_SIZE);
if (ret < 0)
return (-1);
buf[ret] = '\0';
return (ret);
}
char *ft_treat_save(char *save, char *buf)
{
char *tmp;
if (save == NULL)
save = ft_strdup(buf);
else
{
tmp = ft_strdup(save);
free (save);
save = ft_strjoin(tmp, buf);
free (tmp);
}
return (save);
}
char *ft_treat_tmp(char *save)
{
char *tmp;
tmp = ft_strdup(save);
free (save);
return (tmp);
}
int get_next_line(int fd, char **line)
{
int ret;
char buf[BUFFER_SIZE];
char *tmp;
static char *save;
ret = 1;
if (fd < 0 || fd > 255 || BUFFER_SIZE <= 0 || line == NULL)
return (-1);
*line = NULL;
tmp = NULL;
while (ret > 0)
{
if ((ret = ft_read_buffer(fd, buf)) < 0)
return (-1);
save = ft_treat_save(save, buf);
if (ft_backslash(save) == 1)
{
tmp = ft_treat_tmp(save);
*line = ft_strcut_front(tmp);
save = ft_strcut_back(tmp);
return (1);
}
}
tmp = ft_treat_tmp(save);
save = NULL;
*line = ft_strdup(tmp);
free (tmp);
return (0);
}
Other support code:
size_t ft_strlen(char *str)
{
size_t i;
i = 0;
if (str)
while (str[i])
i++;
return (i);
}
char *ft_strdup(char *str)
{
int i;
char *dst;
dst = malloc(sizeof(char) * ((ft_strlen(str) + 1)));
if (!(dst))
{
free (dst);
return (NULL);
}
i = -1;
while (str[++i])
dst[i] = str[i];
dst[i] = '\0';
return (dst);
}
char *ft_strjoin(char *s1, char *s2)
{
int i;
int j;
char *join;
i = -1;
j = 0;
if (!s1 && !s2)
return (NULL);
join = malloc(sizeof(char) * (ft_strlen(s1) + ft_strlen(s2) + 2));
if (!(join))
return (NULL);
if (BUFFER_SIZE == 1)
while (s1[++i + 1] != '\0')
join[i] = s1[i];
else
while (s1[++i] != '\0')
join[i] = s1[i];
while (s2[j])
join[i++] = s2[j++];
join[i] = '\0';
return (join);
}
char *ft_strcut_front(char *str)
{
int i;
char *front;
i = 0;
while (str[i] != '\n')
i++;
front = malloc(sizeof(char) * (i + 1));
if (!(front))
return (NULL);
i = 0;
while (str[i] != '\n')
{
front[i] = str[i];
i++;
}
front[i] = '\0';
return (front);
}
char *ft_strcut_back(char *str)
{
int i;
int j;
char *back;
i = 0;
while (str[i] != '\n')
i++;
i++;
back = malloc(sizeof(char) * ((ft_strlen(str) - i) + 1));
if (!(back))
return (NULL);
j = 0;
while (str[i] != '\0')
back[j++] = str[i++];
back[j] = '\0';
free (str);
return (back);
}
The main() function:
int main(void)
{
int fd, ret, line_count;
char *line;
line_count = 1;
ret = 0;
line = NULL;
fd = open("baudelaire.txt", O_RDONLY);
while ((ret = get_next_line(fd, &line)) > 0)
{
printf(" \n [ Return: %d ] | A line has been read #%d => |%s|\n", ret, line_count, line);
line_count++;
}
printf(" \n [ Return: %d ] A line has been read #%d: |%s\n", ret, line_count++, line);
printf("\n");
if (ret == -1)
printf("-----------\n An error happened\n");
else if (ret == 0)
{
printf("-----------\n EOF has been reached\n");
}
close(fd);
}
Problem solved. I forgot to add + 1 to my buf size... stupid error ! Thank you

OpenBSD 5.9 header with C99 inline function

I'm building a custom shell and see when I compile that usr/include/ctype.h:92 seem to require c99 inline function but the compiler says that C99 inline functions are not supported. The warning can be disabled with a parameter to the compiler but is it a bug that needs a fix?
This is my main.c
#define _XOPEN_SOURCE 500
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include "openshell.h"
#include "errors.h"
#include <errno.h>
#include <locale.h>
#include <readline/readline.h>
#include <getopt.h>
#include <unistd.h>
#include <assert.h>
#include <readline/history.h>
#ifdef SIGDET
#if SIGDET == 1
int isSignal = 1; /*Termination detected by signals*/
#endif
#endif
static int sourceCount = 0;
static FILE *sourcefiles[MAX_SOURCE];
/*
* The special maximum argument value which means that there is
* no limit to the number of arguments on the command line.
*/
#define INFINITE_ARGS 0x7fffffff
/*
* The table of built-in commands.
* A command is terminated wih an entry containing NULL values.
*/
static const CommandEntry commandEntryTable[] =
{
{
"checkenv", do_checkenv, 1, INFINITE_ARGS,
"Check environment variables",
""
},
{
"add2path", do_add2path, 3, INFINITE_ARGS,
"do_add2path",
"[txp]v arFileName fileName ..."
},
{
"cd", do_cd, 1, 2,
"Change current directory",
"[dirName]"
},
{
"exit", do_exit, 1, 2,
"Exit from shell",
"[exit value]"
},
{
"help", do_help, 1, 2,
"Print help about a command",
"[word]"
},
{
"kill", do_kill, 2, INFINITE_ARGS,
"Send a signal to the specified process",
"[-sig] pid ..."
},
{
NULL, 0, 0, 0,
NULL,
NULL
}
};
struct command {
char *const *argv;
};
char** str_split(char* a_str, const char a_delim)
{
char** result = 0;
size_t count = 0;
char* tmp = a_str;
char* last_comma = 0;
char delim[2];
delim[0] = a_delim;
delim[1] = 0;
/* Count how many elements will be extracted. */
while (*tmp)
{
if (a_delim == *tmp)
{
count++;
last_comma = tmp;
}
tmp++;
}
/* Add space for trailing token. */
count += last_comma < (a_str + strlen(a_str) - 1);
/* Add space for terminating null string so caller
knows where the list of returned strings ends. */
count++;
result = malloc(sizeof(char*) * count);
if (result)
{
size_t idx = 0;
char* token = strtok(a_str, delim);
while (token)
{
assert(idx < count);
*(result + idx++) = strdup(token);
token = strtok(0, delim);
}
assert(idx == count - 1);
*(result + idx) = 0;
}
return result;
}
char ** stripped;
char ** create_pipeline(int argc, char ** argv) {
if(!strstr(argv[argc], "|")) { /* && ! isBetweenquotes*/
stripped = argv;
return stripped;
}
else {
stripped = argv;
return create_pipeline(argc, argv);
}
}
static int runCmd(const char *cmd) {
const char *cp;
pid_t pid;
int status;
struct command shellcommand[4];
char **argv = 0;
int argc = 1;
bool pipe = false;
char *command[40];
char *cmd2[20] = {"cmd2", 0};
int numberofpipelines = 0;
unsigned long i3 = 0;
unsigned long i2 = 0;
unsigned long i1 = 0;
unsigned long n = 0;
char *string;
char *string1;
int max_args = 1; /* there's at least one argument or we wouldn't reach this */
cmd2[0] = NULL;
cmd2[1] = NULL;
cmd2[2] = NULL;
command[0] = NULL;
command[1] = NULL;
command[3] = NULL;
char *string2 = NULL;
char** tokens;
for (cp = cmd; *cp; cp++) {
if ((*cp >= 'a') && (*cp <= 'z')) {
continue;
}
if ((*cp >= 'A') && (*cp <= 'Z')) {
continue;
}
if (isDecimal(*cp)) {
continue;
}
if (isBlank(*cp)){
continue;
}
if ((*cp == '.') || (*cp == '/') || (*cp == '-') ||
(*cp == '+') || (*cp == '=') || (*cp == '_') ||
(*cp == ':') || (*cp == ',') || (*cp == '\'') ||
(*cp == '"')) {
continue;
}
}
makeArgs(cmd, &argc, (const char ***) &argv, pipe);
char a[20] = {0};
if (sscanf(cmd, "%*[^']'%[^']'", a) == 1) {
printf("<undefined>");
}
char cmdtmp[75];
strcpy(cmdtmp, cmd);
tokens = str_split( cmdtmp, '|');
if (tokens)
{
int i;
for (i = 0; *(tokens + i); i++)
{
printf("month=[%s]\n", *(tokens + i));
free(*(tokens + i));
}
printf("\n");
free(tokens);
}
for (int i = 0; i < argc; i++) {
if (argv[i] != NULL && strstr(argv[i], "|")) {
numberofpipelines++;
char subbuff[40];
i1 = 0;
i2 = 0;
i3 = 0;
subbuff[0]='\0';
string = strstr(argv[i], "|");
if (string != NULL) {
i3 = string - argv[i];
printf("**** is null ***");
cmd2[1] = argv[argc - 1];
}
string1 = strstr(&argv[i][i3 + 2], "|");
if (string1 != NULL) {
i2 = string1 - argv[i3 + 1];
printf("i2: %lu", i2);
} else {
char *found3 = strstr(&argv[i][i3 + 1], " ");
if (found3 != NULL) {}
string2 = strstr(argv[1], "|");
if (string2 != NULL) {
i1 = string2 - argv[1];
}
n = strlen(argv[1]) - i1;
if (argc > 2) {
memcpy(subbuff, &argv[i][i3 + 1], n - 1);
subbuff[n - 1] = '\0';
cmd2[0] = subbuff;
cmd2[1] = argv[argc - 1];
} else {
memcpy(subbuff, &argv[i][i3 + 1], n);
subbuff[n] = '\0';
cmd2[0] = subbuff;
cmd2[1] = argv[argc - 1];
}
argc++;
argv[i + 1] = subbuff;
command[i] = "<undefined>";
argv[i + 2] = NULL;
max_args = i;
}
}
if (argv[i] != NULL) {
if (i < max_args) {
command[i] = argv[i];
command[i+1] = 0;
max_args++;
cmd2[1] = argv[argc - 1];
} else {
command[max_args] = argv[max_args];
command[max_args+1] = 0;
cmd2[1] = argv[argc - 1];
}
}
if (argv[i] != NULL) {
char *p = strchr(argv[i], '|');
if (!p) {
/* deal with error: / not present" */;
} else {
*p = 0;
}
}
}
dump_argv((const char *) "d", argc, argv);
/* makeArgs(cmd, &argc, &argv, pipe);*/
/* command[2]= 0;*/
shellcommand[0].argv = command;
shellcommand[1].argv = cmd2;
pid = fork();
if (pid < 0) {
perror("fork failed");
return -1;
}
/* If we are the child process, then go execute the program.*/
if (pid == 0) {
/* spawn(cmd);*/
fork_pipes(numberofpipelines, shellcommand);
}
/*
* We are the parent process.
* Wait for the child to complete.
*/
status = 0;
while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR));
if (pid < 0) {
fprintf(stderr, "Error from waitpid: %s", strerror(errno));
return -1;
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "pid %ld: killed by signal %d\n",
(long) pid, WTERMSIG(status));
return -1;
}
return WEXITSTATUS(status);
}
/* The shell performs wildcard expansion on each token it extracts while parsing the command line.
* Oftentimes, globbing will obviously not do anything (for example, ls just returns ls).
* When you want nullglob behavior you'll have to know whether the glob function actually found any glob characters, though
*/
static void expandVariable(char *shellcommand) {
char mystring[CMD_LEN];
char *cp;
char *ep;
strcpy(mystring, shellcommand);
cp = strstr(mystring, "$(");
if (cp) {
*cp++ = '\0';
strcpy(shellcommand, mystring);
ep = ++cp;
while (*ep && (*ep != ')')) ep++;
if (*ep == ')') *ep++ = '\0';
cp = getenv(cp);
if (cp) strcat(shellcommand, cp);
strcat(shellcommand, ep);
}
return;
}
int do_help(int argc, const char **argv) {
const CommandEntry *entry;
const char *str;
str = NULL;
if (argc == 2)
str = argv[1];
/*
* Check for an exact match, in which case describe the program.
*/
if (str) {
for (entry = commandEntryTable; entry->name; entry++) {
if (strcmp(str, entry->name) == 0) {
printf("%s\n", entry->description);
printf("usage: %s %s\n", entry->name,
entry->usage);
return 0;
}
}
}
/*
* Print short information about commands which contain the
* specified word.
*/
for (entry = commandEntryTable; entry->name; entry++) {
if ((str == NULL) || (strstr(entry->name, str) != NULL) ||
(strstr(entry->usage, str) != NULL)) {
printf("%-10s %s\n", entry->name, entry->usage);
}
}
return 0;
}
/*
* Try to execute a built-in command.
* Returns TRUE if the command is a built in, whether or not the
* command succeeds. Returns FALSE if this is not a built-in command.
*/
bool exec_builtin(const char *cmd) {
const char *endCmd;
const CommandEntry *entry;
int argc;
const char **argv;
char cmdName[CMD_LEN];
/*
* Look for the end of the command name and then copy the
* command name to a buffer so we can null terminate it.
*/
endCmd = cmd;
while (*endCmd && !isBlank(*endCmd))
endCmd++;
memcpy(cmdName, cmd, endCmd - cmd);
cmdName[endCmd - cmd] = '\0';
/*
* Search the command table looking for the command name.
*/
for (entry = commandEntryTable; entry->name != NULL; entry++) {
if (strcmp(entry->name, cmdName) == 0)
break;
}
/*
* If the command is not a built-in, return indicating that.
*/
if (entry->name == NULL) {
return false;
}
bool bo = false;
/*
* The command is a built-in.
* Break the command up into arguments and expand wildcards.
*/
if (!makeArgs(cmd, &argc, &argv, bo)) {
return true;
}
/*
* Give a usage string if the number of arguments is too large
* or too small.
*/
if ((argc < entry->minArgs) || (argc > entry->maxArgs)) {
fprintf(stderr, "usage: %s %s\n", entry->name, entry->usage);
return true;
}
/*
* Call the built-in function with the argument list.
*/
entry->func(argc, argv);
return true;
}
/*
* Parse and execute one null-terminated command line string.
* This breaks the command line up into words, checks to see if the
* command is an alias, and expands wildcards.
*/
int command(const char *cmd) {
const char *endCmd;
char cmdName[CMD_LEN];
freeChunks();
/*
* Skip leading blanks.
*/
while (isBlank(*cmd))
cmd++;
/*
* If the command is empty or is a comment then ignore it.
*/
if ((*cmd == '\0') || (*cmd == '#'))
return 0;
/*
* Look for the end of the command name and then copy the
* command name to a buffer so we can null terminate it.
*/
endCmd = cmd;
while (*endCmd && !isBlank(*endCmd))
endCmd++;
memcpy(cmdName, cmd, endCmd - cmd);
cmdName[endCmd - cmd] = '\0';
/*
* Expand simple environment variables
*/
while (strstr(cmd, "$(")) expandVariable((char *) cmd);
/*
* Now look for the command in the builtin table, and execute
* the command if found.
*/
if (exec_builtin(cmd)) {
return 0;
}
/*
* The command is not a built-in, so run the program along
* the PATH list.
*/
return runCmd(cmd);
}
/*
* Execute the specified file or program
* A null name pointer indicates to read from stdin.
*/
int exec_program(const char *name) {
FILE *fp;
int r = 0;
char *input, shell_prompt[100];
if (sourceCount >= MAX_SOURCE) {
fprintf(stderr, "Too many source files\n");
return 1;
}
fp = stdin;
if (name) {
fp = fopen(name, "r");
if (fp == NULL) {
perror(name);
return 1;
}
}
sourcefiles[sourceCount++] = fp;
setlocale(LC_CTYPE, "");
/*Configure readline to auto-complete paths when the tab key is hit.*/
rl_bind_key('\t', rl_complete);
/*stifle_history(7);*/
for (; ;) {
/* Create prompt string from user name and current working directory.*/
snprintf(shell_prompt, sizeof(shell_prompt), "%s:%s $ ", getenv("USER"), getcwd(NULL, 1024));
// Display prompt and read input (NB: input must be freed after use)...
input = readline(shell_prompt);
// Check for EOF.
if (!input)
break;
add_history(input);
r = command(input);
free(input);
}
return r;
}
static struct option long_options[] = {
{"with_param", 1, 0, 'p'},
{"version", 0, 0, 'v'},
{"help", 0, 0, 'h'},
{0, 0, 0, 0}
};
char s[] = "Interrupt\n";
void int_handler(int signum) {
if (write(fileno(stdin), s, sizeof s - 1)) { } else { }
if (signum) { if (false); } else { }
}
int main(int argc, char *argv[]) {
struct sigaction sh;
sh.sa_handler = int_handler;
sigemptyset(&sh.sa_mask);
sh.sa_flags = 0;
sigaction(SIGINT, &sh, NULL);
sourceCount = 0;
const char *commandFile;
commandFile = NULL;
char *pathValue;
int option;
sh.sa_handler = int_handler;
sigemptyset(&sh.sa_mask);
sh.sa_flags = 0;
sigaction(SIGINT, &sh, NULL);
int option_index = 0;
while (1) {
option_index = 0;
option = getopt_long(argc, argv, "p:vh",
long_options, &option_index);
if (option == -1)
break;
switch (option) {
case 'p': {
/* store_parameter(optarg); */
break;
}
case 'v': {
printf("OpenShell version 0.1(a)\n");
printf("Version: %s\n", VERSION);
exit(EXIT_SUCCESS);
}
case 'h': {
printf("Usage: ./shell\n");
/*print_help();*/
exit(EXIT_SUCCESS);
}
default: {
/* fprintf(stderr, "Error (%s): unrecognized option.\n", __FUNCTION__);*/
/* print_help();*/
return 1;/*RETURN_FAILURE;*/
}
} /* end switch */
}
/* get the PATH environment to find if less is installed */
pathValue = getenv("PATH");
if (!pathValue || getenv("PATH") == NULL) {
printf("'%s' is not set.\n", "PATH");
/* Default our path if it is not set. */
putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc");
}
else {
printf("'%s' is set to %s.\n", "PATH", pathValue);
}
exec_program(commandFile);
return (0);
}
My makefile:
CC = gcc
GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags)
CFLAGS := $(CFLAGS) -L/usr/local/include/ -L/usr/include -pedantic -std=c99 -Wall -O3 -g -DVERSION=\"$(GIT_VERSION)\" -ledit -lncurses
LDIRS = -L/usr/local/lib -L/usr/lib
LIBS =su -lcurses
shell: main.o
$(CC) -o shell main.o errors.c util.c pipeline.c -ledit -lncurses -lcurses
main.o: main.c errors.c util.c
USERNAME := $(shell whoami >> username.txt)
GIT:= $(shell head -n -1 openshell.h > temp.txt ; mv temp.txt openshell.h;git describe --abbrev=4 --dirty --always --tags > VERSION; echo "\#define VERSION \"$(GIT_VERSION)\"" >> openshell.h)
.PHONY: clean
clean:
rm -f *.o
You should compile with c99 or c11, and the flags to gcc:
gcc -std=c99
or
gcc -std=c11

C Heap buffer corruption when using free()

I'm getting the following error when freeing "shifted_text" below. I've checked with print statements and commenting things out, and it's definitely that free(shifted_text). The other free commands are working fine.
Debug Error!
HEAP CORRUPTION DETECTED: After normal block (#77) at 0x007D1F...
CRT detected that the application wrote to memory after end of heap buffer.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void parse(int argc, char *argv[]);
char * shift_text(char *sometext);
char shift_letter(char letter, char shift);
void bring_in_text(void);
void write_to_file(char *sometext);
char *flag;
char *password;
char *text;
int main(int argc, char *argv[])
{
parse(argc, argv);
char *shifted_text;
// at this point flag can only be -e, -d, -df, -ef
if (strcmp(flag, "-e") == 0 || strcmp(flag, "-d") == 0)
{
// location to store the shifted text
shifted_text = (char*)malloc(strlen(text) * sizeof(char));
shift_text(shifted_text);
printf("%s\n", shifted_text);
}
else
{
bring_in_text();
// location to store the shifted text
shifted_text = (char*)malloc(strlen(text) * sizeof(char));
shift_text(shifted_text);
write_to_file(shifted_text);
}
free(shifted_text);
free(text);
free(flag);
free(password);
return 0;
}
void write_to_file(char *sometext)
{
if (strcmp(flag, "-df") == 0)
{
FILE *fp;
fp = fopen("plaintext.txt", "w");
if (fp == NULL)
{
puts("Unable to open file");
exit(1);
}
fprintf(fp, sometext);
fclose(fp);
}
else if (strcmp(flag, "-ef") == 0)
{
FILE *fp;
fp = fopen("ciphertext.txt", "w");
if (fp == NULL)
{
puts("Unable to open file");
exit(1);
}
fprintf(fp, sometext);
fclose(fp);
}
}
void bring_in_text(void)
{
if (strcmp(flag, "-df") == 0)
{
FILE *fp;
fp = fopen("ciphertext.txt", "r");
if (fp == NULL)
{
puts("Unable to open file");
exit(1);
}
while (!feof(fp))
{
text = (char*)malloc(100 * sizeof(char));
fgets(text, 100, fp);
}
fclose(fp);
}
else if (strcmp(flag, "-ef") == 0)
{
FILE *fp;
fp = fopen("plaintext.txt", "r");
if (fp == NULL)
{
puts("Unable to open file");
exit(1);
}
while (!feof(fp))
{
text = (char*)malloc(100 * sizeof(char));
fgets(text, 100, fp);
}
fclose(fp);
}
}
char * shift_text(char *shifted_text)
{
char *temptext;
temptext = text;
char *tempshiftedtext;
tempshiftedtext = shifted_text;
// space for 10 characters plus null
char *temppswd;
temppswd = password;
for (int i = 0; i < strlen(text); i++)
{
char a;
if (*temptext >= 97 && *temptext <= 122)
{
a = shift_letter(*(temptext + i), *(temppswd + (i % strlen(password))));
*(tempshiftedtext + i) = a;
}
else
*(tempshiftedtext + i) = *(temptext + i);
}
*(tempshiftedtext + strlen(text)) = '\0';
}
char shift_letter(char letter, char shift)
{
if (strcmp(flag, "-e") == 0 || strcmp(flag, "-ef") == 0)
{
letter = letter - 97;
shift = shift - 97;
int shifted_letter = letter + shift;
if (shifted_letter > 25)
shifted_letter %= 26;
shifted_letter += 97;
return (char)shifted_letter;
}
else if (strcmp(flag, "-d") == 0 || strcmp(flag, "-df") == 0)
{
int shifted_letter = letter - 97;
shift = shift - 97;
int letter = shifted_letter - shift;
letter %= 26; // mod seems to allow negative results, so if its still negative. add another val equal to modulus
if (letter < 0)
letter += 26;
letter += 97;
return (char)letter;
}
}
void parse(int argc, char *argv[])
{
if (argc == 4)
{
// internally calls malloc on strlen(argv[i])
flag = _strdup(argv[1]);
password = _strdup(argv[2]);
text = _strdup(argv[3]);
if (strlen(password) > 10)
{
puts("Password too long");
exit(1);
}
else if (strcmp(flag, "-e") != 0 && strcmp(flag, "-d") != 0)
{
puts("Incorrect flag");
exit(1);
}
}
else if (argc == 3)
{
// internally calls malloc on strlen(argv[i])
flag = _strdup(argv[1]);
password = _strdup(argv[2]);
if (strlen(password) > 10)
{
puts("Password too long");
exit(1);
}
else if (strcmp(flag, "-ef") != 0 && strcmp(flag, "-df") != 0)
{
puts("Incorrect flag");
exit(1);
}
}
else
{
puts("Incorrect arguements");
exit(1);
}
}
The functions parse simply stores command line arguments in the global's. The shifting functions shift a letter by some number. 'A' shifted by 2 would be 'C' for example. These work fine and without the free(shifted_text) the program works.
I'm new to C so it's probably something simple but I can't see it.
Change this
shifted_text = (char*)malloc(strlen(text) * sizeof(char));
to
shifted_text = malloc((strlen(text) + 1) * sizeof(char)); // don't cast
A C-style string always has a null-terminator, indicating the end of the string. For example, "foo" is stored as 'f', 'o', 'o', '\0' in memory. So you have to
I suspect that the heap buffer corruption isn't caused by your free(shifted_text);. Since insufficient memory is allocated to shifted_text, undefined behaviour is invoked, making everything possible. So your program may either run properly or crash. Perhaps it's only a coincidence that every time free(shifted_text); is commented out, your program runs correctly thanks to the undefined behaviour.
BTW: There are many places in your code to be refined. For example, in void bring_in_text(void):
while (!feof(fp))
{
text = (char*)malloc(100 * sizeof(char));
fgets(text, 100, fp);
}
Covering the previous lines without even processing them? Also, text isn't freed in this function.
strdup allocates strlen+1 chars and you only allocate strlen chars. When you write the null at the end of shifted text you are overflowing the buffer.

C free() invalid next size (normal)

I'm fairly new to C and I can't seem to figure out what seems to be a pretty simple pointer problem. My program adds line numbers to a file. It reads in the file line by line and then adds a line number to the beginning of each line. It works fine on each file individually as you can see below:
soccer#soccer-Dell-DV051:~/code C$ ./a.out test.c
soccer#soccer-Dell-DV051:~/code C$ ./a.out miscellaneousHeader.h
soccer#soccer-Dell-DV051:~/code C$ ./a.out test.c miscellaneousHeader.h
*** Error in `./a.out': free(): invalid next size (normal): 0x08648170 ***
Segmentation fault (core dumped)
soccer#soccer-Dell-DV051:~/code C$
but when I run them together I get the above error. The following code is my program.
Compiler.c:
#include <stdio.h>
#include "lineNumAdderHeader.h"
#include "miscellaneousHeader.h"
int main(int argc, char *argv[]){
if (argc < 2)
fatal("in main(). Invalid number of arguments");
int i = 1;
while (i < argc){
lineNumAdder(argv[i]);
i++;
}
}
I have narrowed the problem to the lineNumPtr. The error occurs when lineNumPtr is freed after the second file. If lineNumPtr is not freed, which I know is bad programming, the program works just fine.
lineNumAdder.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "miscellaneousHeader.h"
#include "lineNumAdderHeader.h"
int lineSizeChecker(FILE*, int);
int lineNumChecker(char*);
int fileSizeChecker(FILE*);
void lineNumAdder(char* filename){
int lineSpace, position, lineNumCheckerBoolean, numOfDigits, fileSpace;
int lineNum = 1;
char *lineNumPtr = NULL;
char *numberedFile = NULL;
char *nonNumberedLine = NULL;
char *originalLine = NULL;
FILE *file = errorCheckedFopen(filename, "r+w");
while(1){
position = ftell(file);
if (position == 0){
fileSpace = fileSizeChecker(file);
numberedFile = errorCheckedMalloc(fileSpace);
}
lineSpace = lineSizeChecker(file, position);
if (position == 0)
originalLine = errorCheckedMalloc(lineSpace);
else
originalLine = realloc(originalLine, lineSpace);
if (fgets(originalLine, lineSpace, file) == NULL)
break;
lineNumCheckerBoolean = lineNumChecker(originalLine);
if (lineNumCheckerBoolean == 0){
if (position == 0)
nonNumberedLine = errorCheckedMalloc(lineSpace - 9);
else
nonNumberedLine = realloc(nonNumberedLine, lineSpace - 9);
strcpy(nonNumberedLine, &originalLine[9]);
}
else{
if (position == 0)
nonNumberedLine = errorCheckedMalloc(lineSpace);
else
nonNumberedLine = realloc(nonNumberedLine, lineSpace);
strcpy(nonNumberedLine, originalLine);
fileSpace += 8;
numberedFile = realloc(numberedFile, fileSpace);
}
numOfDigits = intDigitFinder(lineNum);
if (position == 0)
lineNumPtr = errorCheckedMalloc(numOfDigits);
else
lineNumPtr = realloc(lineNumPtr, numOfDigits);
sprintf(lineNumPtr, "%d", lineNum);
strcat(numberedFile, "/*");
strcat(numberedFile, lineNumPtr);
strcat(numberedFile, "*/");
if (lineNum < 10)
strcat(numberedFile, " ");
else if (lineNum >= 10 && lineNum < 100)
strcat(numberedFile, " ");
else if (lineNum >= 100 && lineNum < 1000)
strcat(numberedFile, " ");
else if (lineNum >= 1000 && lineNum < 10000)
strcat(numberedFile, " ");
strcat(numberedFile, nonNumberedLine);
lineNum++;
}
fclose(file);
free(originalLine);
free(nonNumberedLine);
free(lineNumPtr);
free(numberedFile);
}
int lineNumChecker(char *comment){
if (sizeOf(comment) < 8)
return 1;
if (comment[7] == '/' || comment[6] == '/' || comment[5] == '/' || comment[4] == '/')
return 0;
else
return 1;
}
int lineSizeChecker(FILE *file, int position){
int i = 2;
int ch;
while ((ch = fgetc(file)) != '\n' && ch != EOF)
i++;
fseek(file, position, SEEK_SET);
return i;
}
int fileSizeChecker(FILE *file){
int i = 2;
while (fgetc(file) != EOF)
i++;
fseek(file, 0, SEEK_SET);
return i;
}
miscellaneous.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "miscellaneousHeader.h"
void fatal(char*);
int sizeOf(char *data){
int i = 1;
while(data[i-1] != '\n')
i++;
return i;
}
void *errorCheckedMalloc(size_t size){
void *ptr = malloc(size);
if (ptr == NULL)
fatal("in errorCheckedMalloc(). Memory Allocation Failure\n");
else
return ptr;
}
FILE *errorCheckedFopen(char *filename, char *mode){
FILE *file = fopen(filename, mode);
if (file == NULL)
fatal("in errorCheckedFopen(). File Opening Failure\n");
else
return file;
}
void fatal(char *errorMessage){
char* completedErrorMessage = errorCheckedMalloc(sizeOf(errorMessage)+17);
strcpy(completedErrorMessage, "[!!] Fatal Error ");
strcat(completedErrorMessage, errorMessage);
perror(completedErrorMessage);
free(completedErrorMessage);
exit(-1);
}
int intDigitFinder(int num){
int digits = 0;
do {
num /= 10;
digits++;
} while (num != 0);
return digits;
}
void *reMalloc(void *ptr, size_t size){
char buf[strlen(ptr) + 1];
strcpy(buf, ptr);
free(ptr);
ptr = errorCheckedMalloc(size);
if(size >= strlen(buf))
strcpy(ptr, buf);
return ptr;
}
I apologize for the length. This is my first post and I wanted to make sure that I provided enough information for you guys to give me the best answers possible. Thank you for any and all answers. They are much appriciated.
Ok guys, so I have gotten it to work. I have tweaked the lineNumAdder.c file. The program now increases numberedFile's size by the line each time it is read in. Also the error was occuring on the second file because when I would strcat the line number into the malloced area junk was already stored there, so numberedFile would overflow. I have fixed this by using calloc instead of malloc. Thank you to all who gave answers and commented. They all helped tremendously.
Here is the completed lineNumAdder.c file:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "miscellaneousHeader.h"
#include "lineNumAdderHeader.h"
int lineSizeChecker(FILE*, int);
int lineNumChecker(char*);
int fileSizeChecker(FILE*);
void lineNumAdder(char* filename){
int lineSpace, filePosition, lineNumCheckerBoolean, numOfDigits;
int fileSpace = 0;
int lineNum = 1;
char *lineNumPtr = NULL;
char *numberedFile = NULL;
char *nonNumberedLine = NULL;
char *originalLine = NULL;
FILE *file = errorCheckedFopen(filename, "r+w");
while(1){
filePosition = ftell(file);
lineSpace = lineSizeChecker(file, filePosition);
if (filePosition == 0)
originalLine = calloc(1, lineSpace);
else
originalLine = realloc(originalLine, lineSpace);
if (fgets(originalLine, lineSpace, file) == NULL)
break;
lineNumCheckerBoolean = lineNumChecker(originalLine);
if (lineNumCheckerBoolean == 0){
fileSpace += lineSpace;
if (filePosition == 0){
nonNumberedLine = calloc(1, lineSpace - 8);
numberedFile = calloc(1, fileSpace);
}
else{
nonNumberedLine = realloc(nonNumberedLine, lineSpace - 8);
numberedFile = realloc(numberedFile, fileSpace);
}
strcpy(nonNumberedLine, &originalLine[9]);
}
else{
fileSpace += lineSpace + 9;
if (filePosition == 0){
nonNumberedLine = calloc(1, lineSpace);
numberedFile = calloc(1, fileSpace);
}
else{
nonNumberedLine = realloc(nonNumberedLine, lineSpace);
numberedFile = realloc(numberedFile, fileSpace);
}
strcpy(nonNumberedLine, originalLine);
}
numOfDigits = intDigitFinder(lineNum);
if(filePosition == 0)
lineNumPtr = calloc(1, numOfDigits);
else
lineNumPtr = realloc(lineNumPtr, numOfDigits);
sprintf(lineNumPtr, "%d", lineNum);
strcat(numberedFile, "/*");
strcat(numberedFile, lineNumPtr);
strcat(numberedFile, "*/");
if (lineNum < 10)
strcat(numberedFile, " ");
else if (lineNum >= 10 && lineNum < 100)
strcat(numberedFile, " ");
else if (lineNum >= 100 && lineNum < 1000)
strcat(numberedFile, " ");
else if (lineNum >= 1000 && lineNum < 10000)
strcat(numberedFile, " ");
strcat(numberedFile, nonNumberedLine);
lineNum++;
}
fclose(file);
free(originalLine);
free(nonNumberedLine);
free(lineNumPtr);
free(numberedFile);
}
int lineNumChecker(char *comment){
if (sizeOf(comment) < 8)
return 1;
if (comment[7] == '/' || comment[6] == '/' || comment[5] == '/' || comment[4] == '/')
return 0;
else
return 1;
}
int lineSizeChecker(FILE *file, int position){
int i = 2;
int ch;
while ((ch = fgetc(file)) != '\n' && ch != EOF)
i++;
fseek(file, position, SEEK_SET);
return i;
}
int fileSizeChecker(FILE *file){
int i = 2;
while (fgetc(file) != EOF)
i++;
fseek(file, 0, SEEK_SET);
return i;
}
So, it's difficult to debug this... But at the very least:
int sizeOf(char *data) {
int i = 1;
while(data[i-1] != '\n' && data[i-1] != '\0')
i++;
return i;
}

Resources