Cant find memory leak in C - c
I've been sitting for hours checking this code after I found out there's a memory leak/error somewhere
Where is that leak? How can it be fixed?
here is Dr.Memory report:
Dr. Memory version 2.3.0
Running "C:\Users\Beni\source\repos\Magshimim_EX8\Debug\Magshimim_EX8.exe"
Using system call file C:\Users\Beni\AppData\Roaming\Dr. Memory\symcache\syscalls_wow64.txt
Error #1: UNADDRESSABLE ACCESS: reading 1 byte(s)
replace_strlen
d:\drmemory_package\drmemory\replace.c(412):
Magshimim_EX8.exe!?
??:0
Magshimim_EX8.exe!?
??:0
Magshimim_EX8.exe!?
??:0
Magshimim_EX8.exe!?
??:0
Magshimim_EX8.exe!?
??:0
KERNEL32.dll!BaseThreadInitThunk
??:0
ERRORS FOUND:
1 unique, 1 total unaddressable access(es)
0 unique, 0 total uninitialized access(es)
0 unique, 0 total invalid heap argument(s)
0 unique, 0 total GDI usage error(s)
0 unique, 0 total handle leak(s)
0 unique, 0 total warning(s)
0 unique, 0 total, 0 byte(s) of leak(s)
0 unique, 0 total, 0 byte(s) of possible leak(s)
Details: C:\Users\Beni\AppData\Roaming\Dr. Memory\DrMemory-Magshimim_EX8.exe.5208.000\results.txt
WARNING: application exited with abnormal code 0xc0000005
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#define FALSE 0
#define TRUE !FALSE
#define FIRST_TWO_FILES 2
#define FIRST_TWENTY_PRECENTS 1
#define MIDDLE_SIXTY_PRECENTS 2
#define LAST_TWENTY_PRECENTS 3
long findLenOfFile(FILE * file);
char* readFile(FILE* f, char* dest, long len);
char menu(char* scanFolder, char* virusSignature);
char** writeFilesFromFolder(char* scanFolder, char ** filesList, int* len);
char* writePart(char* src, char* dest, int length, int* newLen, int part);
int findSignature(char* virusSignature, char* buffer, int sigLen, int bufferLen);
void scanFiles(char* scanFolder, char** filesList, int amountOfFiles, char* virusSignature, long virusLength, char option);
int main(int argc, char* argv[])
{
char* log = malloc(sizeof(char)*strlen(argv[1]) + sizeof(char)*strlen("\\Log.txt") + 4);
FILE* virusSignatureFile = fopen(argv[2], "rb");
long virusLength = 0;
char** filesList = (char**)malloc(sizeof(char) * 0);
char* virusSignature = 0;
int amountOfFiles = 0;
char option = 0;
int i = 0;
virusLength = findLenOfFile(virusSignatureFile);
// get the virusSignature as a string and write the files to check into the filesList
virusSignature = readFile(virusSignatureFile, virusSignature, virusLength);
filesList = writeFilesFromFolder(argv[1], filesList, &amountOfFiles);
// create log file
strcpy(log, "");
strcat(log, argv[1]);
strcat(log, "\\Log.txt");
FILE * logFile = fopen(log, "w");
fprintf(logFile, "Anti-virus began! Welcome!\n\nFolder to scan:\n%s\nVirus signature:\n%s\n\nScanning option:\n", argv[1], argv[2]);
// get scanning option (normal or quick) and continue accordingly
option = menu(argv[1], argv[2]);
if (option == '0') {
fprintf(logFile, "Normal Scan\n\n");
}
else {
fprintf(logFile, "Quick Scan\n\n");
}
fprintf(logFile, "Results:\n");
fclose(logFile);
// initiate scan
scanFiles(argv[1], filesList, amountOfFiles, virusSignature, virusLength, option);
fclose(virusSignatureFile);
free(log);
free(filesList);
free(virusSignature);
getchar();
return 0;
}
/*
This function will print the scanning folder path and signature path, also will print the option menu to the user of quick or normal scan,
after that function will return user option(0, or other key)
input: scanFolder path (string), virus signature path (also string)
output: user option (char: '0', or other key)
*/
char menu(char * scanFolder, char * virusSignature)
{
char userOption = '\0';
printf("Welcome to my Virus Scan!\n\nFolder to scan: %s\nVirus signature: %s\n\nPress 0 for a norman scan or any other key for a quick scan: ", scanFolder, virusSignature);
userOption = getchar();
printf("Scanning began...\nThis process may take several minutes...\n\n");
return userOption;
}
/*
This function writes all files name from folder to the filesList
input: the scanning folder path
output: amount of files
*/
char** writeFilesFromFolder(char * scanFolder, char ** filesList, int* len)
{
DIR *d = 0;
struct dirent *dir;
d = opendir(scanFolder);
int i = 0;
if (d)
{
while ((dir = readdir(d)) != NULL)
{
if (i > 1)
{
filesList = (char**)realloc(filesList, sizeof(filesList) + sizeof(char*) + 4);
*(filesList + (i - FIRST_TWO_FILES)) = (char*)malloc(sizeof(char) * strlen(dir->d_name) + 1);
strcpy(*(filesList + (i - FIRST_TWO_FILES)), (dir->d_name));
}
i++;
}
closedir(d);
}
*len = i - FIRST_TWO_FILES; //first two names is "." and ".."
return filesList;
}
/*
This function will read the contents of a file into a string
input: a file (FILE *) to read from
output: char* with the contents of the file
*/
char* readFile(FILE* f, char * dest, long len)
{
dest = (char*)malloc(sizeof(char) * len);
fread(dest, 1, len, f);
return dest;
}
void scanFiles(char * scanFolder, char ** filesList, int amountOfFiles, char * virusSignature, long virusLength, char option)
{
char* log = malloc(sizeof(char)*strlen(scanFolder) + sizeof(char)*strlen("\\Log.txt") + 1);
char * buffer = (char*)malloc(sizeof(char) * 0);
char* subBuffer = 0;
char* slash = "\\";
long length = 0;
char* name = 0;
int subLen = 0;
int i = 0;
FILE * f;
// reopen log file and append to it
strcpy(log, "");
strcat(log, scanFolder);
strcat(log, "\\Log.txt");
FILE * logFile = fopen(log, "a");
// iterate over each file
for (i = 0; i < amountOfFiles; i++)
{
name = (char*)malloc(sizeof(char) * strlen(scanFolder) + 1 + sizeof(char) * strlen(slash) + sizeof(char) * strlen(*(filesList + i)) + 20);
// open current file
strcpy(name, "");
strcat(name, scanFolder);
strcat(name, slash);
strcat(name, *(filesList + i));
f = fopen(name, "rb");
length = findLenOfFile(f);
if (f != NULL) // if file can be accessed
{
buffer = readFile(f, buffer, length);
if (option == '0') { // Normal Mode
if (findSignature(virusSignature, buffer, virusLength, length))
{
printf("%s - Infected!\n", name);
fprintf(logFile, "%s - Infected!\n", name);
}
else
{
printf("%s - Clean\n", name);
fprintf(logFile, "%s - Clean\n", name);
}
}
else { // Quick Mode
subBuffer = writePart(buffer, subBuffer, length, &subLen, FIRST_TWENTY_PRECENTS); // get first 20%
if (findSignature(virusSignature, subBuffer, virusLength, subLen))
{
printf("%s - infected! (first 20%%)\n", name);
fprintf(logFile, "%s - infected! (first 20%%)\n", name);
}
else {
free(subBuffer);
subBuffer = writePart(buffer, subBuffer, length, &subLen, LAST_TWENTY_PRECENTS); // get last 20%
if (findSignature(virusSignature, subBuffer, virusLength, subLen))
{
printf("%s - Infected! (last 20%%)\n", name);
fprintf(logFile, "%s - Infected! (last 20%%)\n", name);
}
else {
subBuffer = writePart(buffer, subBuffer, length, &subLen, MIDDLE_SIXTY_PRECENTS); // get the 60% left in the middle
if (findSignature(virusSignature, subBuffer, virusLength, subLen))
{
printf("%s - Infected!\n", name);
fprintf(logFile, "%s - Infected!\n", name);
}
else {
printf("%s - clean\n", name);
fprintf(logFile, "%s - Clean\n", name);
}
}
}
free(subBuffer);
}
fclose(f);
}
else
{
printf("No file found\n");
}
free(*(filesList + i));
free(name);
}
fclose(logFile);
free(log);
free(buffer);
getchar();
}
/*
This function will write part of the file (beginning, middle or end) to a string
input: source (string) to take the information from, destination (string) to write a part of the source to it,
length (int) of the source string, a pointer (int*) to store the new length of the destination string and
part of the file to write from (int) 1,2 or 3: first 20%, 60% in the middle and last 20% accordingly
output: string containing the desired part of the source string
*/
char* writePart(char *src, char *dest, int length, int *newLen, int part) {
int i = 0;
int percentedLength = 0;
int count = 0;
percentedLength = (int)(length / 5); // this len is 20% of the entire file's length
if (part == FIRST_TWENTY_PRECENTS) // return beginning
{
dest = (char*)malloc(sizeof(char) * percentedLength);
*newLen = percentedLength;
for (i = 0; i < percentedLength; i++)
{
*(dest + i) = *(src + i);
}
}
else if (part == MIDDLE_SIXTY_PRECENTS) // return middle
{
// allocate space for the middle: The entire file size minus 20% from the start and 20% from the end
dest = (char*)malloc(sizeof(char) * (length - 2 * percentedLength));
*newLen = length - 2 * percentedLength;
for (i = percentedLength; i < length - percentedLength; i++) {
*(dest + count) = *(src + i);
count++;
}
}
else if (part == LAST_TWENTY_PRECENTS) // return end
{
dest = (char*)malloc(sizeof(char) * percentedLength);
*newLen = percentedLength;
for (i = length - percentedLength; i < length; i++)
{
*(dest + count) = *(src + i);
count++;
}
}
return dest;
}
/*
function that finds the length of a file
input: file (FILE *)
output: the file's length (long)
*/
long findLenOfFile(FILE * file)
{
long length = 0;
fseek(file, 0, SEEK_END);
length = ftell(file);
fseek(file, 0, SEEK_SET);
return length;
}
/*
function checks whether a file contains the virusSignature. It iterates over each letter of the file and checks
if it is the same as the first letter in the virusSignature. If it is, it checks the rest of the characters and
returns True if a match is found. if not it continues the same process until the end of the file is reached.
input: The virusSignature (string), a buffer with the content of a file (string), the signature's length (int)
and the buffer's length (int)
output: True if signature is in file, False otherwise
*/
int findSignature(char* virusSignature, char* buffer, int sigLen, int bufferLen)
{
int found = 0;
int i = 0;
int j = 0;
for (i = 0; i < bufferLen - (sigLen - 1); i++) {
if (*(buffer + i) == *virusSignature) // check if a letter is the same as first letter in virusSignature
{
found = TRUE;
// check if the rest of the letters match the signature and stop if one doesn't
for (j = 1; (j < sigLen) && found; j++) {
if (*(buffer + (i + j)) != *(virusSignature + j)) {
found = FALSE;
}
}
if (found) {
return TRUE; // if we got a match, return true!
}
}
}
return FALSE;
}
The realloc call in writeFilesFromFolder has a bug.
It is:
filesList = (char **) realloc(filesList, sizeof(filesList) + sizeof(char *) + 4);
Notice that the space allocated for filesList is constant. It does not grow as new elements are added, so you have undefined behavior.
This is not a memory leak as the tool detected. A memory leak means that you fail to free a pointer that goes out of scope.
Rather, you're storing data beyond the end of the area you've allocated, trashing whatever is there, which is probably the [hidden] chain pointer area that malloc et. al. use to keep track of allocations.
I'm not sure how either of the sizeof factor in, but, filesList is a pointer, so sizeof(filesList) is constant [either 4 on a 32 bit machine or 8 for 64 bit].
The allocated space has to increase in proportion to i.
Here is a refactored version of that function that fixes the bug along with some simplification and cleanup:
BTW, don't cast malloc: Do I cast the result of malloc?
Also, note that sizeof(char) is [by definition] always 1 regardless of how many bits a char actually has for a given architecture. So, remove any sizeof(char) *
Instead of (e.g.):
*(filesList + i)
It's usually simpler/cleaner to do:
filesList[i]
Anyway, here's the code:
/*
This function writes all files name from folder to the filesList
input: the scanning folder path
output: amount of files
*/
char **
writeFilesFromFolder(char *scanFolder, char **filesList, int *len)
{
DIR *d = 0;
struct dirent *dir;
d = opendir(scanFolder);
int i = -FIRST_TWO_FILES;
if (d) {
while ((dir = readdir(d)) != NULL) {
if (i >= 0) {
filesList = realloc(filesList,sizeof(*filesList) * (i + 1));
filesList[i] = strdup(dir->d_name);
}
i++;
}
closedir(d);
}
*len = i; // first two names is "." and ".."
return filesList;
}
UPDATE:
Okay, you do have memory leaks. And, I've coded up detection and a fix.
Primarily, what you are doing is passing down a buffer pointer to a function (e.g. readFile or writePart) as an argument (e.g. dest).
Then, you are doing:
dest = malloc(percentedLength);
This leaks the previous value of dest.
Normally, functions that allocate a buffer and return it do not take it as an argument. But, after analyzing your code, replacing the malloc with a realloc prevents the leak.
I had to do a complete code review to find this. And, I did several simplifications and cleanups along the way to try to understand your code and isolate possible further issues.
I replaced your allocation/concatenation of filenames with a new function filejoin.
I replaced the other malloc calls with a macro: ALLOCME that detects the memory leaks before they happen. This works in conjunction with the [new] FREEME macro that replaces the free calls.
The default mode is to detect the leak and abort. If you give the program a -f option, it will fix the problem. After you analyze and understand what happened, you can change the default to the "fix" mode.
Where possible, I left your original code under #if 0
A few more style tips:
Keep lines to <= 80 chars.
Don't use "sidebar" comments, particularly on if clauses (e.g.):
if (...) { // process the file
Replace with:
// process the file
if (...) {
Don't replicate code. When you are replicating similar code [as in where I replaced the code with filejoin], this indicates a good place to write a modular function
Anyway, here's the refactored and fixed code:
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <assert.h>
#define FALSE 0
#define TRUE (! FALSE)
#define FIRST_TWO_FILES 2
#define FIRST_TWENTY_PRECENTS 1
#define MIDDLE_SIXTY_PRECENTS 2
#define LAST_TWENTY_PRECENTS 3
#define ALLOCME(_ptr,_len) \
do { \
_ptr = allocme(_ptr,_len,__FUNCTION__,__LINE__); \
} while (0)
#define FREEME(_ptr) \
do { \
if (_ptr != NULL) \
free(_ptr); \
_ptr = NULL; \
} while (0)
void
sysfault(const char *fmt,...)
{
va_list ap;
va_start(ap,fmt);
vfprintf(stderr,fmt,ap);
va_end(ap);
exit(1);
}
long findLenOfFile(FILE * file);
char *readFile(FILE * f, char *dest, long len);
char menu(const char *scanFolder, const char *virusSignature);
char **writeFilesFromFolder(const char *scanFolder, char **filesList, int *len);
char *writePart(char *src, char *dest, int length, int *newLen, int part);
int findSignature(char *virusSignature, char *buffer, int sigLen,
int bufferLen);
void scanFiles(const char *scanFolder, char **filesList, int amountOfFiles,
char *virusSignature, long virusLength, char option);
int opt_fixme = 0;
// allocme -- guarded allocation
void *
allocme(void *ptr,size_t len,const char *fnc,int lno)
{
if (! opt_fixme) {
if (ptr != NULL)
sysfault("allocme: leaking ptr=%p len=%zu (from %s at line %d)\n",
ptr,len,fnc,lno);
}
ptr = realloc(ptr,len);
if (ptr == NULL)
sysfault("allocme: realloc failure\n");
return ptr;
}
#ifdef __linux__
const char *slash = "/";
#else
const char *slash = "\\";
#endif
// filejoin -- create filename from directory and file tail
char *
filejoin(const char *dir,const char *tail)
{
size_t len;
char *file;
len = 0;
len += strlen(dir);
len += strlen(slash);
len += strlen(tail);
len += 1;
file = malloc(len);
if (file == NULL)
sysfault("filejoin: unable to alloc -- %s\n",strerror(errno));
*file = 0;
strcat(file,dir);
strcat(file,slash);
strcat(file,tail);
return file;
}
int
main(int argc, char **argv)
{
#if 0
char *log = malloc(strlen(argv[1]) + strlen("\\Log.txt") + 4);
#else
char *log;
#endif
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'f':
opt_fixme = ! opt_fixme;
break;
}
}
if (argc != 2)
sysfault("usage: <folder_to_scan> <virus_signature_file>\n");
const char *topdir = argv[0];
const char *sigfile = argv[1];
FILE *virusSignatureFile = fopen(sigfile, "rb");
if (virusSignatureFile == NULL)
sysfault("main: unable to open '%s' -- %s\n",sigfile,strerror(errno));
long virusLength = 0;
#if 0
char **filesList = malloc(0);
#else
char **filesList = NULL;
#endif
char *virusSignature = NULL;
int amountOfFiles = 0;
char option = 0;
virusLength = findLenOfFile(virusSignatureFile);
// get the virusSignature as a string and write the files to check into the
// filesList
virusSignature = readFile(virusSignatureFile, virusSignature, virusLength);
#if 1
fclose(virusSignatureFile);
#endif
filesList = writeFilesFromFolder(topdir, filesList, &amountOfFiles);
// create log file
#if 0
strcpy(log, "");
strcat(log, argv[1]);
strcat(log, "\\Log.txt");
#else
log = filejoin(topdir,"Log.txt");
#endif
FILE *logFile = fopen(log, "w");
fprintf(logFile, "Anti-virus began! Welcome!\n\n"
"Folder to scan:\n%s\n"
"Virus signature:\n%s\n\n"
"Scanning option:\n", topdir, sigfile);
// get scanning option (normal or quick) and continue accordingly
option = menu(topdir, sigfile);
if (option == '0') {
fprintf(logFile, "Normal Scan\n\n");
}
else {
fprintf(logFile, "Quick Scan\n\n");
}
fprintf(logFile, "Results:\n");
fclose(logFile);
// initiate scan
scanFiles(topdir, filesList, amountOfFiles, virusSignature, virusLength,
option);
#if 0
fclose(virusSignatureFile);
#endif
FREEME(log);
FREEME(filesList);
FREEME(virusSignature);
#ifndef __linux__
getchar();
#endif
return 0;
}
/*
This function will print the scanning folder path and signature path, also
will print the option menu to the user of quick or normal scan,
after that function will return user option(0, or other key)
input: scanFolder path (string), virus signature path (also string)
output: user option (char: '0', or other key)
*/
char
menu(const char *scanFolder, const char *virusSignature)
{
char userOption = '\0';
printf("Welcome to my Virus Scan!\n\n"
"Folder to scan: %s\n"
"Virus signature: %s\n\n"
"Press 0 for a norman scan or any other key for a quick scan: ",
scanFolder, virusSignature);
userOption = getchar();
printf("Scanning began...\nThis process may take several minutes...\n\n");
return userOption;
}
/*
This function writes all files name from folder to the filesList
input: the scanning folder path
output: amount of files
*/
char **
writeFilesFromFolder(const char *scanFolder, char **filesList, int *len)
{
DIR *d = 0;
struct dirent *dir;
d = opendir(scanFolder);
int i = -FIRST_TWO_FILES;
if (d) {
while ((dir = readdir(d)) != NULL) {
if (i >= 0) {
filesList = realloc(filesList,sizeof(*filesList) * (i + 1));
filesList[i] = strdup(dir->d_name);
}
i++;
}
closedir(d);
}
// first two names is "." and ".."
*len = i;
return filesList;
}
/*
This function will read the contents of a file into a string
input: a file (FILE *) to read from
output: char* with the contents of the file
*/
char *
readFile(FILE * f, char *dest, long len)
{
// NOTE/BUG: this does _not_ free the prior value -- memory leak!
ALLOCME(dest,len);
fread(dest, 1, len, f);
return dest;
}
void
scanFiles(const char *scanFolder, char **filesList, int amountOfFiles,
char *virusSignature, long virusLength, char option)
{
#if 0
char *log = malloc(strlen(scanFolder) + strlen("\\Log.txt") + 1);
#else
char *log;
#endif
#if 0
char *buffer = malloc(0);
#else
char *buffer = NULL;
#endif
char *subBuffer = NULL;
long length = 0;
char *name = NULL;
int subLen = 0;
int i = 0;
FILE *f;
// reopen log file and append to it
#if 0
strcpy(log, "");
strcat(log, scanFolder);
strcat(log, "\\Log.txt");
#else
log = filejoin(scanFolder,"Log.txt");
#endif
FILE *logFile = fopen(log, "a");
// iterate over each file
for (i = 0; i < amountOfFiles; i++) {
#if 0
name = malloc(strlen(scanFolder) + 1 + strlen(slash) + strlen(*(filesList + i)) + 20);
#endif
// open current file
#if 0
strcpy(name, "");
strcat(name, scanFolder);
strcat(name, slash);
strcat(name, *(filesList + i));
#else
name = filejoin(scanFolder,filesList[i]);
#endif
f = fopen(name, "rb");
length = findLenOfFile(f);
// if file can be accessed
if (f != NULL) {
buffer = readFile(f, buffer, length);
// Normal Mode
if (option == '0') {
if (findSignature(virusSignature, buffer, virusLength, length)) {
printf("%s - Infected!\n", name);
fprintf(logFile, "%s - Infected!\n", name);
}
else {
printf("%s - Clean\n", name);
fprintf(logFile, "%s - Clean\n", name);
}
}
// Quick Mode
else {
// get first 20%
subBuffer = writePart(buffer, subBuffer, length, &subLen,
FIRST_TWENTY_PRECENTS);
if (findSignature(virusSignature, subBuffer, virusLength,
subLen)) {
printf("%s - infected! (first 20%%)\n", name);
fprintf(logFile, "%s - infected! (first 20%%)\n", name);
}
else {
FREEME(subBuffer);
// get last 20%
subBuffer = writePart(buffer, subBuffer, length, &subLen,
LAST_TWENTY_PRECENTS);
if (findSignature(virusSignature, subBuffer, virusLength,
subLen)) {
printf("%s - Infected! (last 20%%)\n", name);
fprintf(logFile, "%s - Infected! (last 20%%)\n", name);
}
else {
// get the 60% left in the middle
subBuffer = writePart(buffer, subBuffer, length,
&subLen, MIDDLE_SIXTY_PRECENTS);
if (findSignature(virusSignature, subBuffer,
virusLength, subLen)) {
printf("%s - Infected!\n", name);
fprintf(logFile, "%s - Infected!\n", name);
}
else {
printf("%s - clean\n", name);
fprintf(logFile, "%s - Clean\n", name);
}
}
}
FREEME(subBuffer);
}
fclose(f);
}
else {
printf("No file found\n");
}
FREEME(filesList[i]);
FREEME(name);
}
fclose(logFile);
FREEME(log);
FREEME(buffer);
getchar();
}
/*
This function will write part of the file (beginning, middle or end) to a string
input: source (string) to take the information from, destination (string) to
write a part of the source to it, length (int) of the source string, a pointer
(int*) to store the new length of the destination string and part of the file
to write from (int) 1,2 or 3: first 20%, 60% in the middle and last 20%
accordingly
output: string containing the desired part of the source string
*/
char *
writePart(char *src, char *dest, int length, int *newLen, int part)
{
int i = 0;
int percentedLength = 0;
int count = 0;
// this len is 20% of the entire file's length
percentedLength = (int) (length / 5);
// NOTE/BUG: this does _not_ free the prior value -- memory leak!
switch (part) {
case FIRST_TWENTY_PRECENTS: // return beginning
ALLOCME(dest,percentedLength);
*newLen = percentedLength;
for (i = 0; i < percentedLength; i++) {
*(dest + i) = *(src + i);
}
break;
case MIDDLE_SIXTY_PRECENTS: // return middle
// allocate space for the middle: The entire file size minus 20% from
// the start and 20% from the end
ALLOCME(dest,length - 2 * percentedLength);
*newLen = length - 2 * percentedLength;
for (i = percentedLength; i < length - percentedLength; i++) {
*(dest + count) = *(src + i);
count++;
}
break;
case LAST_TWENTY_PRECENTS: // return end
ALLOCME(dest,percentedLength);
*newLen = percentedLength;
for (i = length - percentedLength; i < length; i++) {
*(dest + count) = *(src + i);
count++;
}
break;
}
return dest;
}
/*
function that finds the length of a file
input: file (FILE *)
output: the file's length (long)
*/
long
findLenOfFile(FILE * file)
{
long length = 0;
fseek(file, 0, SEEK_END);
length = ftell(file);
fseek(file, 0, SEEK_SET);
return length;
}
/*
function checks whether a file contains the virusSignature.
It iterates over each letter of the file and checks if it is the same as the
first letter in the virusSignature.
If it is, it checks the rest of the characters and returns True if a match is
found.
if not it continues the same process until the end of the file is reached.
input: The virusSignature (string), a buffer with the content of a file
(string), the signature's length (int)
and the buffer's length (int)
output: True if signature is in file, False otherwise
*/
int
findSignature(char *virusSignature, char *buffer, int sigLen, int bufferLen)
{
int found = 0;
int i = 0;
int j = 0;
for (i = 0; i < bufferLen - (sigLen - 1); i++) {
// check if a letter is the same as first letter in virusSignature
if (*(buffer + i) == *virusSignature)
{
found = TRUE;
// check if the rest of the letters match the signature and stop
// if one doesn't
for (j = 1; (j < sigLen) && found; j++) {
if (*(buffer + (i + j)) != *(virusSignature + j)) {
found = FALSE;
}
}
// if we got a match, return true!
if (found) {
return TRUE;
}
}
}
return FALSE;
}
Related
why am i getting extra text on last line of file when writing to a file
i am implementing setenv && unsetenv My logic is to edit the .bashrc file, seeing as using IPC didn't work for me My problem is with the _unsetenv function, when I try to remove an environment variable, it removes it but adds extra text to the last line for example, I have these variable set(from line 122 - 126) and i want to delete 'FOO': export R='make re' export FOO=BAR export PATH=/home/marlon/learning_programming/C/shell:$PATH export CRAB=VALUE export SOME='CHANGED' now after executing _unsetenv my .bashrc file looks like this: export R='make re' export PATH=/home/marlon/learning_programming/C/shell:$PATH export CRAB=VALUE export SOME='CHANGED' SOME='CHANGED' why is it adding the line: SOME='CHANGED'? here's some helper functions: #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <sys/mman.h> /** * string_count - counts the words in a string separated by a delimiter * and the letters befor a delimiter. * #str: string to be counted. * #token: string of delimiters. * * Return: an array of: * a pointer to the number of words * an array of the letters before each delimiter. */ int **string_count(char *str, char *token) { int words, *before_delim, letters, i, j, k; int **how_many; char flag; how_many = malloc(sizeof(int *) * 2); if (!how_many) return (NULL); words = 0; for (i = 0; str[i]; i++) { for (j = 0; token[j]; j++) { if (str[i] == token[j]) words++; } } words++; /* count the letters before delimiters */ before_delim = malloc(sizeof(int) * words); if (!before_delim) return (NULL); i = 0; for (k = 0; k < words; k++) { flag = 0; letters = 0; for (; str[i]; i++) { for (j = 0; token[j]; j++) { if (str[i] == token[j]) { flag = 1; i++; } } if (flag) { break; } else letters++; } before_delim[k] = letters; } how_many[0] = &words; how_many[1] = before_delim; return (how_many); } /** * file_count - counts a file. * Description: COUNT[0]: number of lines in the file * COUNT[1]: line number where sub occurs, 0 if not found or empty. * COUNT[2]: number of letters on each line. * A string can be searched for in a file using sub. * Set sub to "" if no string should be searched for. * #stream: an opened file descriptor. * #sub: an optional string to be searched for in a file. * Can be set to "" for no search. * * Return: an array of pointers on the heap to: * the number of lines in the file * the line number where sub occurs * the number of letters on each line */ int **file_count(char *filename, char *sub) { char *buffer = 0, flag = 0; size_t n = 0, l1; ssize_t nread; FILE *stream; int **count, i, j, k; int *letters; /* opening file */ stream = fopen(filename, "r"); if (!stream) return (NULL); count = malloc(sizeof(int *) * 3); if (!count) return (NULL); i = j = 0; count[0] = 0; l1 = strlen(sub); /* reading file line by line */ while ((nread = getline(&buffer, &n, stream)) != -1) { /* if found string to be searched, stop incrementing j */ if (!(strncmp(sub, buffer, l1))) { j++; flag = 1; } /* counting lines */ i++; /* counting till string to be searched for is found */ if (flag == 0) j++; } if (!l1 || !flag) j = 0; count[0] = &i; count[1] = &j; /* closing file for letter count */ free(buffer); fclose(stream); /* counting letters on each line */ /* opening file */ stream = fopen(filename, "r"); if (!stream) return (NULL); letters = malloc(sizeof(int) * i); if (!letters) return (NULL); buffer = 0; n = 0; i = 0; while ((nread = getline(&buffer, &n, stream)) != -1) { for (k = 0; buffer[k]; k++) ; letters[i] = k; i++; } count[2] = letters; free(buffer); fclose(stream); return (count); } /** * strsplt - splits a string by delimiter into an array of each word of the string. * #str: string to split. * #token: string of delimiters. * #new_delim: string to split new array by. * * Return: a new array of strings. */ char **strsplt(char *str, char *token, char new_delim) { char **split; int *array; int index, watch, assign, letters; int **str_count, wc; str_count = string_count(str, token); wc = **str_count; split = malloc(sizeof(char *) * (wc + 1)); if (!split) return (NULL); array = str_count[1]; assign = 0; for (index = 0; index < wc; index++) { letters = array[index]; split[index] = malloc(sizeof(char) * letters + 2); if (!split[index]) return (NULL); for (watch = 0; watch < letters; watch++) { split[index][watch] = str[assign++]; } assign++; split[index][watch] = new_delim; split[index][watch + 1] = 0; } split[index] = NULL; free(str_count); free(array); return (split); } here is _unsetenv with main: /** * _unsetenv - removes an environment variable. * #name: variable to be removed. * * Return: zero on success, or -1 on error. */ int _unsetenv(const char *name) { char *map, *to_search, **split, **temp_split; char *shell, *home, *filename; size_t ln; int s_size, **f_count, occurrence, i = 0; int ls, lh, f_size, f_d, lines, j; struct stat st; FILE *stream; map = NULL; split = NULL; ln = strlen(name); /* checking for errors */ if (!name || !ln || strchr(name, '=')) { errno = EINVAL; return (-1); } /* creating variable to search for */ s_size = 7 + ln + 2; to_search = malloc(sizeof(char) * s_size); if (!to_search) return (-1); strcpy(to_search, "export "); strcat(to_search, name); strcat(to_search, "="); /* creating filename */ home = getenv("HOME"); lh = strlen(home); /* getting shell name */ shell = getenv("SHELL"); shell = &shell[5]; ls = strlen(shell); /* creating file to open */ f_size = lh + 2 + ls + 3; filename = malloc(sizeof(char) * f_size); if (!filename) return (-1); strcpy(filename, home); strcat(filename, "/."); strcat(filename, shell); strcat(filename, "rc"); /* searching for variable */ f_count = file_count(filename, to_search); occurrence = f_count[1][0]; lines = **f_count; temp_split = malloc(sizeof(char) * lines - 1); if (!temp_split) return (0); /* if variable doesn't exist, return success */ if (!occurrence) return (0); /* else remove it */ else { /* open filename */ stream = fopen(filename, "r+"); if (!stream) return (-1); f_d = stream->_fileno; if (fstat(f_d, &st) == -1) return (-1); map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f_d, 0); /* splitting string to edit file */ split = strsplt(map, "\n", '\n'); free(split[occurrence - 1]); split[occurrence - 1] = NULL; for (j = 0; j < lines - 1; j++, i++) { if (j == occurrence - 1) i++; temp_split[j] = split[i]; if (fputs(temp_split[j], stream) == EOF) return (-1); printf("temp_split[%d]: %s\n", j, temp_split[j]); } return (0); } return (-1); } /** * main - tests _unsetenv(). * #argc: argument count. * argv: argument vector. * * Return: 0. */ int main(void) { int i; char *name = "FOO"; if ((i = _unsetenv(name)) == -1) printf("%s not removed\n", name); else printf("%s removed\n", name); return (0); }
c code to compare two binary files runs on windows but won't run on Linux [closed]
Closed. This question needs debugging details. It is not currently accepting answers. Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question. Closed 1 year ago. Improve this question I wrote a c code on visual studio to compare binary file to search a know virus in other binary file. the code is running on my windows PC perfectly however it won't compile on the Linux test of my collage. the code receive a folder containing the files and the file of the virus this is the code adjusted for Linux that i sent to the test #include <stdlib.h> #include <stdio.h> #include <string.h> #include <dirent.h> #define SLASH "/" #define FIFTH 0.2 #define NORMAL '0' #define PARAMS 3 #define MAX 200 #define WELCOME_MES "Welcome to my Virus Scan!\n\nFolder to scan: " #define FLOG_WELCOME "Anti-virus began! Welcome!\n\nFolder to scan:\n" #define COMP_MES "Scan Completed.\nSee log path for results: antiVirus_log.txt" #define INF_LAST "INFECTED (LAST 20%%)" #define INF_FIRST "INFECTED (FIRST 20%%)" #define INF "INFECTED" int search_virus(char word[], char virus[], char name[], int finish_point, int sigLen, int starting_point); char welcome_op(char directory_name[], char virus_name[], FILE* log); int is_directory(const char* path); int fast_or_regular(FILE* file, FILE* virus, char name[], char option, FILE* log); int main(int argc, char** argv) { char full_path[MAX] = ""; char option = ' '; FILE* fLog = fopen("antiVirus_log.txt", "w"); if (!fLog) { return 0; } FILE* virusSig = fopen(argv[2], "rb");// **** 2 if (!virusSig) { return 0; } if (argc != PARAMS) { return 0; } struct dirent* struct_lab; DIR* ptr_lab = opendir(argv[1]); if (!ptr_lab) { printf("Couldn't open the directory!"); return 0; } else { option = welcome_op(argv[1], argv[2], fLog); while ((struct_lab = readdir(ptr_lab)) != NULL)//enter dir { if (!is_directory(struct_lab->d_name)) { strcpy(full_path, argv[1]);//add name of the direcotry strcat(full_path, SLASH);// add slash strcat(full_path, struct_lab->d_name);// add the file name FILE* check_file = fopen(full_path, "rb");// open if (check_file) { fast_or_regular(check_file, virusSig, full_path, option, fLog); fclose(check_file); } } } closedir(ptr_lab); } fclose(fLog); printf("%s", COMP_MES); fclose(virusSig); getchar(); return(0); } /* * prints the welcome message and ask the user to choose quick or normal scan * input: directory_name: the name of the directory, virus name: virus file name, logL: writes the action there * output: 0 if normal, anything else quick */ char welcome_op(char* directory_name, char* virus_name, FILE* log) { char op = ' '; printf("%s%s\nVirus signature: %s\nPress 0 for normal scan or any other key for a quick scan: ", WELCOME_MES, directory_name, virus_name); fprintf(log, "%s%s\nVirus signature:\n%s", FLOG_WELCOME, directory_name, virus_name); fflush(stdin); op = getchar(); printf("\nscanning began...\nThis process may take several minutes...\n"); (op == 1) ? fprintf(log, "\n\nScanning option:\nQuick scan\n\nResults:\n") : fprintf(log, "\n\nScanning option:\nRegular scan\n\nResults:\n"); return(op); } /* * check if its directory * input: path to the file * output: 0 if not directory, 1 if is directory */ int is_directory(const char* path) { struct stat statbuf; if (stat(path, &statbuf) != 0) return 0; return S_ISDIR(statbuf.st_mode); } /* *send it 1 time full scan if option = 0 (regular) or split the file to 3 parts, 0-20%, 80-100%, 20-80%. * copy the binary file into a string and send it to a func to scan it. * if found it prints INFECTED and write it in log file. * else print CLEAN and writes it in log file. * input: file - the file we scanning, virus: virus's signiture, name: name of the file, option: quick or normal, log: file where we write the actions * output: 0, just to finish the operation. */ int fast_or_regular(FILE* file, FILE* virus, char name[], char option, FILE* log) { int start = 0, fifth_of_len = 0, fourth_len = 0; fseek(file, 0, SEEK_END); long len = ftell(file); rewind(file); char* word = (char*)malloc(len); fread(word, 1, len, file);//binary file into string fseek(virus, 0, SEEK_END); long lenSig = ftell(virus); rewind(virus); char* sig = (char*)malloc(lenSig); fread(sig, 1, lenSig, virus);//binary file into string if (option == '0')//normal { if (search_virus(word, sig, name, len, lenSig, start)) { fprintf(log, "%s - INFECTED!\n", name); } else { fprintf(log, "%s - clean\n", name); } free(sig); free(word); return(0); } else//quick { fifth_of_len = len * FIFTH; fourth_len = fifth_of_len * 4; if (search_virus(word, sig, name, fifth_of_len, lenSig, start))//0-20 { fprintf(log, "%s - %s\n", name, INF_FIRST); free(sig); free(word); return(0); } else if (search_virus(word, sig, name, len, lenSig, fourth_len))//80-100 { fprintf(log, "%s - %s\n", name, INF_LAST); free(sig); free(word); return(0); } else if (search_virus(word, sig, name, fifth_of_len, lenSig, fourth_len))//20-80 { fprintf(log, "%s - INFECTED\n", name); free(sig); free(word); return(0); } } fprintf(log, "%s - clean\n", name);//clean free(sig); free(word); return 0; } /* * compare between the file and the virus signiture * runs on the file untill it equal to the first symbol of the signiture, then runs on both add 1 to the counter * if counter = len of sig returns same. else its different * input: word: the word, virus: virus signiture to camper with, name: name of the file, finish_point: where to stop, starting_point: where to start * output: 1 if same, 0 if not. */ int search_virus(char word[], char virus[], char name[], int finish_point, int sigLen, int starting_point) { long i = 0, j = 0, same = 0; for (i = starting_point; i < finish_point; i++)//run on the word { if (word[i] == virus[0]) { same = 1; for (j = 1; j < sigLen; j++)//run on the virus sig { if (word[i + j] == virus[j]) { same++; } else { break; } } } if (same == sigLen) { return(1); } } return(0); } this is the code I run on windows, I add the dirent.h library to open the folder directory #include <stdlib.h> #include <stdio.h> #include <string.h> #include "dirent.h" #define SLASH "\\" #define FIFTH 0.2 #define NORMAL '0' #define PARAMS 3 #define MAX 200 #define WELCOME_MES "Welcome to my Virus Scan!\n\nFolder to scan: " #define FLOG_WELCOME "Anti-virus began! Welcome!\n\nFolder to scan:\n" #define COMP_MES "Scan Completed.\nSee log path for results: antiVirus_log.txt" #define INF_LAST "INFECTED (LAST 20%%)" #define INF_FIRST "INFECTED (FIRST 20%%)" #define INF "INFECTED" int search_virus(char word[], char virus[], char name[], int finish_point, int sigLen, int starting_point); char welcome_op(char directory_name[], char virus_name[], FILE* log); int is_directory(const char* path); int fast_or_regular(FILE* file, FILE* virus, char name[], char option, FILE* log); int main(int argc, char** argv) { char full_path[MAX] = ""; char option = ' '; //עבור בחירת מצב מהיר או רגיל FILE* fLog = fopen("antiVirus_log.txt", "w"); if (!fLog) { return 0; } FILE* virusSig = fopen(argv[2], "rb");// **** 2 if (!virusSig) { return 0; } if (argc != PARAMS) { return; } struct dirent* struct_lab; DIR* ptr_lab = opendir(argv[1]); if (!ptr_lab) { printf("Couldn't open the directory!"); return 0; } else { option = welcome_op(argv[1], argv[2], fLog); while ((struct_lab = readdir(ptr_lab)) != NULL)//enter dir { if (!is_directory(struct_lab->d_name)) { strcpy(full_path, argv[1]);//add name of the direcotry strcat(full_path, SLASH);// add slash strcat(full_path, struct_lab->d_name);// add the file name FILE* check_file = fopen(full_path, "rb");// open if (check_file) { fast_or_regular(check_file, virusSig, full_path, option, fLog); fclose(check_file); } } } closedir(ptr_lab); } fclose(fLog); FILE* fLog = fopen("antiVirus_log.txt", "r"); printf("%s", COMP_MES); fclose(virusSig); getchar(); return(0); } /* * prints the welcome message and ask the user to choose quick or normal scan * input: directory_name: the name of the directory, virus name: virus file name, logL: writes the action there * output: 0 if normal, anything else quick */ char welcome_op(char* directory_name, char* virus_name, FILE* log) { char op = ' '; printf("%s%s\nVirus signature: %s\nPress 0 for normal scan or any other key for a quick scan: ", WELCOME_MES, directory_name, virus_name); fprintf(log, "%s%s\nVirus signature:\n%s", FLOG_WELCOME, directory_name, virus_name); op = getch(); printf("\nscanning began...\nThis process may take several minutes...\n"); (op == 1) ? fprintf(log, "\n\nScanning option:\nQuick scan\n\nResults:\n") : fprintf(log, "\n\nScanning option:\nRegular scan\n\nResults:\n"); return(op); } /* * check if its directory * input: path to the file * output: 0 if not directory, 1 if is directory */ int is_directory(const char* path) { struct stat statbuf; if (stat(path, &statbuf) != 0) return 0; return S_ISDIR(statbuf.st_mode); } /* *send it 1 time full scan if option = 0 (regular) or split the file to 3 parts, 0-20%, 80-100%, 20-80%. * copy the binary file into a string and send it to a func to scan it. * if found it prints INFECTED and write it in log file. * else print CLEAN and writes it in log file. * input: file - the file we scanning, virus: virus's signiture, name: name of the file, option: quick or normal, log: file where we write the actions * output: 0, just to finish the operation. */ int fast_or_regular(FILE* file, FILE* virus, char name[], char option, FILE* log) { int start = 0, fifth_of_len = 0, fourth_len = 0; fseek(file, 0, SEEK_END); long len = ftell(file); rewind(file); char* word = (char*)malloc(len); fread(word, 1, len, file);//binary file into string fseek(virus, 0, SEEK_END); long lenSig = ftell(virus); rewind(virus); char* sig = (char*)malloc(virus); fread(sig, 1, lenSig, virus);//binary file into string if (option == '0')//normal { if (search_virus(word, sig, name, len, lenSig, start)) { fprintf(log, "%s - INFECTED!\n", name); } else { fprintf(log, "%s - clean\n", name); } free(sig); free(word); return(0); } else//quick { fifth_of_len = len * FIFTH; fourth_len = fifth_of_len * 4; if (search_virus(word, sig, name, fifth_of_len, lenSig, start))//0-20 { fprintf(log, "%s - %s\n", name, INF_FIRST); free(sig); free(word); return(0); } else if (search_virus(word, sig, name, len, lenSig, fourth_len))//80-100 { fprintf(log, "%s - %s\n", name, INF_LAST); free(sig); free(word); return(0); } else if (search_virus(word, sig, name, fifth_of_len, lenSig, fourth_len))//20-80 { fprintf(log, "%s - INFECTED\n", name); free(sig); free(word); return(0); } } fprintf(log, "%s - clean\n", name);//clean free(sig); free(word); return 0; } /* * compare between the file and the virus signiture * runs on the file untill it equal to the first symbol of the signiture, then runs on both add 1 to the counter * if counter = len of sig returns same. else its different * input: word: the word, virus: virus signiture to camper with, name: name of the file, finish_point: where to stop, starting_point: where to start * output: 1 if same, 0 if not. */ int search_virus(char word[], char virus[], char name[], int finish_point, int sigLen, int starting_point) { long i = 0, j = 0, same = 0; for (i = starting_point; i < finish_point; i++)//run on the word { if (word[i] == virus[0]) { same = 1; for (j = 1; j < sigLen; j++)//run on the virus sig { if (word[i + j] == virus[j]) { same++; } else { break; } } } if (same == sigLen) { return(1); } } return(0); }
Pasting your code into godbolt quickly reveals the problem. struct stat isn't defined. For linux, you need to #include <sys/types.h> and #include <sys/stat.h> for struct stat. Pay attention to the remaining warning(s).
How to loop a nested array in C
I've been developing a guessing game in which the goal is to guess the character selected by the user among specific characters, anyway, my first and only idea is to create an array with the questions to be asked, and each question has its options like in the code below I'm a newbie in C language so that I there are several things which I'm not sure how to handle. In short, I'd like to know how can I loop over the array showing to the user the questions with its questions to be answered? Here's the code. #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #define ROW 500 #define LINE 200 //Read file and append to an array buffer char *characters(){ char *source = NULL; FILE *fp = fopen("file.txt", "r"); if (fp != NULL) { /* Go to the end of the file. */ if (fseek(fp, 0L, SEEK_END) == 0) { /* Get the size of the file. */ long bufsize = ftell(fp); if (bufsize == -1) { /* Error */ } /* Allocate our buffer to that size. */ source = malloc(sizeof(char) * (bufsize + 1)); /* Go back to the start of the file. */ if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ } /* Read the entire file into memory. */ size_t newLen = fread(source, sizeof(char), bufsize, fp); if ( ferror( fp ) != 0 ) { fputs("Error reading file", stderr); } else { source[newLen++] = '\0'; /* Just to be safe. */ } } fclose(fp); } return source; } char *strndup(const char *s, size_t n) { char *p; size_t n1; for (n1 = 0; n1 < n && s[n1] != '\0'; n1++) continue; p = malloc(n + 1); if (p != NULL) { memcpy(p, s, n1); p[n1] = '\0'; } return p; } // User input char *input(){ char *value; char buffer[10]; int j = 0; while( j < 1 && fgets(buffer, 10, stdin) != NULL){ value = strndup(buffer, 10); j++; } return value; } // Main function int main (void) { char *questions[] = { "Genre",{"male","female"}, "Hair", {"black","red","blond"}, "Cloths",{"dress","shirt","pants"}, "pet", {"dog","cat","pig"} }; int asked[4] = {0}; char *answers[5]; char buffer[6]; srand(time(NULL)); for (int i = 0; i < 4; i++) { int q = rand() % 4; while (asked[q]) q = rand() % 4; asked[q]++; printf ("%s\n", questions[q]); answers[i] = input(); } for(int i = 0; i < 4; i++) { printf(" %s ",answers[i]); } return 0; } That's the file's structure I'll compare as long as I have all the answers from the user. female,blond,vestido,pig,character b male,black,shirt,pants,dog,character c male,black,shirt,pants,cat,character d female,blond,dress,cat,character A male,red,shirt,pants,pig,character e
struct pointers to same memory address producing different data?
I have this simple code to read the lines of a file and store them in a struct: #include <stdio.h> #include <stdlib.h> #include <string.h> struct filedata { char **items; int lines; }; struct filedata *read_file(char *filename) { FILE* file = fopen(filename, "r"); if (file == NULL) { printf("Can't read %s \n", filename); exit(1); } char rbuff; int nlines = 0; // amount of lines int chr = 0; // character count int maxlen = 0; // max line length (to create optimal buffer) int minlen = 2; // min line length (ignores empty lines with just \n, etc) while ((rbuff = fgetc(file) - 0) != EOF) { if (rbuff == '\n') { if (chr > maxlen) { maxlen = chr + 1; } if (chr > minlen) { nlines++; } chr = 0; } else { chr++; } } struct filedata *rdata = malloc(sizeof(struct filedata)); rdata->lines = nlines; printf("lines: %d\nmax string len: %d\n\n", nlines, maxlen); rewind(file); char *list[nlines]; int buffsize = maxlen * sizeof(char); char buff[buffsize]; int i = 0; while (fgets(buff, buffsize, file)) { if (strlen(buff) > minlen) { list[i] = malloc(strlen(buff) * sizeof(char) + 1); strcpy(list[i], buff); i++; } } rdata->items = (char **)list; fclose(file); int c = 0; for (c; c < rdata->lines; c++) { printf("line %d: %s\n", c + 1, rdata->items[c]); } printf("\n"); return rdata; } int main(void) { char fname[] = "test.txt"; struct filedata *ptr = read_file(fname); int c = 0; for (c; c < ptr->lines; c++) { printf("line %d: %s\n", c + 1, ptr->items[c]); } return 0; } This is the output when I run it: lines: 2 max string len: 6 line 1: hello line 2: world line 1: hello line 2: H�� For some reason when it reaches the second index in ptr->items, it prints gibberish output. But yet, if I throw some printf()'s in there to show the pointer addresses, they're exactly the same. Valgrind also prints this when iterating over the char array the second time: ==3777== Invalid read of size 8 ==3777== at 0x400AB3: main (test.c:81) ==3777== Address 0xfff000540 is on thread 1's stack ==3777== 240 bytes below stack pointer But that really doesn't give me any clues in this case. I'm using gcc 4.9.4 with glibc-2.24 if that matters.
list is an non-static local variable and using it after exiting its scope (returning from read_file in this case) will invoke undefined behavior because it will vanish on exiting its scope. Allocate it dynamically (typically on the heap) like char **list = malloc(sizeof(char*) * nlines); Adding code to check if malloc()s are successful will make your code better.
The variable list is local to read_file, but you store a pointer to list in rdata->items. When read_file returns, rdata->items is a dangling pointer, and accessing it is undefined behavior.
reading a text file and sort it in a new file
I have a school assignment in which we have to read a text file, sort the words by alphabetical order and write the result into a new text file. I've already got a program that can read the file and print it on the screen and a different program to sort words which you have to type in. Now I'm trying to merge these two programs, so that the data which been read out of the file will be put into the sorting program. The program that we use to make the code is called CodeBlocks. Below are the two programs. I hope that you can give me advice and an example how to fix this because I tried everything I know but couldn't get it working. #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <limits.h> #define MAX_NUMBER_WORDS 100 char* ReadFile(char *filename) { char *buffer = NULL; int string_size, read_size; FILE *handler = fopen(filename, "r"); if (handler) { //seek the last byte of the file fseek(handler, 0, SEEK_END); //offset from the first to the last byte, or in other words, filesize string_size = ftell(handler); //go back to the start of the file rewind(handler); //allocate a string that can hold it all buffer = (char*)malloc(sizeof(char) * (string_size + 1)); //read it all in one operation read_size = fread(buffer, sizeof(char), string_size, handler); //fread doesnt set it so put a \0 in the last position //and buffer is now officialy a string buffer[string_size] = '\0'; if (string_size != read_size) { //something went wrong, throw away the memory and set //the buffer to NULL free(buffer); buffer = NULL; } } return buffer; } int numberOfWordsInDict(char **dict) { int i; for (i = 0; i < MAX_NUMBER_WORDS; i++) { if (dict[i] == NULL) return i; } return MAX_NUMBER_WORDS; } void printDict(char **dict) { int i; printf("Dictionary:\n"); for (i = 0; i < numberOfWordsInDict(dict); i++) printf("- %s\n", dict[i]); if (numberOfWordsInDict(dict) == 0) printf("The dictionary is empty.\n"); } void swapWords(char **dict, char *word, char *word2) { int i, p1 = -1, p2 = -1; char *tmp; for (i = 0; i < numberOfWordsInDict(dict); i++) { if (strcmp(dict[i], word) == 0) p1 = i; if (strcmp(dict[i], word2) == 0) p2 = i; } if (p1 != -1 && p2 != -1) { tmp = dict[p1]; dict[p1] = dict[p2]; dict[p2] = tmp; } } void sortDict(char **dict) { int swap; int i = 0; do { swap = 0; for (i = 0; i < numberOfWordsInDict(dict) - 1; i++) { if (strcmp(dict[i], dict[i + 1]) > 0) { swapWords(dict, dict[i], dict[i + 1]); swap = 1; } } } while (swap == 1); } void splitSentenceToWords(char **words, char *sentence) { int p1 = 0, p2 = 0; int nrwords = 0; char *word; while (sentence[p2] != '\0') { if (isspace(sentence[p2]) && p1 != p2) { word = (char*)malloc(sizeof(char)*(p2 - p1 + 1)); words[nrwords] = word; strncpy(words[nrwords], &sentence[p1], p2 - p1); words[nrwords][p2 - p1] = '\0'; nrwords++; p1 = p2 + 1; p2 = p1; } else { p2++; } } if (p1 != p2) { word = (char*)malloc(sizeof(char)*(p2 - p1 + 1)); words[nrwords] = word; strncpy(words[nrwords], &sentence[p1], p2 - p1); words[nrwords][p2 - p1] = '\0'; nrwords++; p1 = p2 + 1; p2 = p1; } } int main(void) { char sentence[1024]; char *dict[MAX_NUMBER_WORDS] = {}; char *words[MAX_NUMBER_WORDS] = {}; char *string = ReadFile("test.txt"); if (string) { puts(string); free(string); } //printf("Type een zin in: "); scanf("%[^\n]s", &sentence); splitSentenceToWords(words, &sentence); printDict(words); printf("Words has been sorted\n"); sortDict(words); printDict(words); return 0; }
You are on the right track. The problem is that after your read in your file, you are not using the input to build your word list. Instead of; splitSentenceToWords(words, &sentence); try: splitSentenceToWords(words, &string); Delete free(string) This will get you started. You will have to clean this up when you understand it a bit better.