Confustion with realpath() buffer [duplicate] - c

This question already has an answer here:
Unexpected behavior in printing a char array in C
(1 answer)
Closed 8 years ago.
Here's my function, which is looking for regular files in given directory, and then storing full path to them in a list.
static my_func(const char *path, Files **list) //list - storage for file names
{
DIR *d;
struct dirent *dir;
char buf[PATH_MAX + 1];
d = opendir(path);
if (d) {
while ((dir = readdir(d)) != NULL) {
if ((DT_REG == dir->d_type)) {
realpath(dir->d_name, buf);
List_push(list, buf);
printf("%s\n", dir->d_name);
// memset(buf, 0, PATH_MAX + 1);
}
}
}
closedir(d);
return 0;
}
...
...
int main()
{
// list creation
// my_func call
...
List_print(...)
}
Expected output:
FILE_1.txt
FILE_2.txt
FILE_3.txt
FILE_4.txt
FILE_5.txt
/home/user/c/FILE_1.txt
/home/user/c/FILE_2.txt
/home/user/c/FILE_3.txt
/home/user/c/FILE_4.txt
/home/user/c/FILE_5.txt
The current output:
FILE_1.txt
FILE_2.txt
FILE_3.txt
FILE_4.txt
FILE_5.txt
/home/user/c/FILE_1.txt
/home/user/c/FILE_1.txt
/home/user/c/FILE_1.txt
/home/user/c/FILE_1.txt
/home/user/c/FILE_1.txt
Can it be related with my linked list implementation? It works fine, because I tested it with:
List_push(list, dir->d_name)
and got expected results. This is implementation of List_push (Files is just simply struct with char * and pointer to the next element):
void List_push(Files **head, char *x)
{
Files *new;
new = malloc(sizeof(Files));
if (NULL != new) {
new->next = *head;
new->text = x;
*head = new;
} else {
printf("malloc error");
}
}
Also, as you can see, I was trying to clear buf with memset, but without success - the output is:
FILE_1.txt
FILE_2.txt
FILE_3.txt
FILE_4.txt
FILE_5.txt
[console]$
Yes, blank space seems to be filed with something (or these are just '\n' symbols from List_print), so list is not empty.
What is wrong here?

In List_push(list, buf); you store a pointer to buf in the list. You do this for each file, so you end up with several pointers to the same buf in the list. When printing the list items it then will show the (current) contents of buf.
To avoid this you need to create a copy of buf and store that, so that the stored data won't be overwritten when you reuse buf for the next file.

Related

Why am I getting a "Program failed: Memory fault, core dumped" in my C program?

This program reads text from the standard input and builds a binary search tree containing the individual words from the input. The program reads input until no more data is available, counting the frequency of occurrence of each word in the input. Once the input has been exhausted, the tree will be traversed with an in-order traversal to produce an alphabetical list of words with their frequencies.
Problem: I can compile my code without any errors and when I run ./wordFreq < input.1, it runs perfect and outputs the correct answer with no errors...this is the correct output:
additional 1
agree 3
another 3
don't 3
for 1
I 1
input 4
is 3
line 5
need 1
one 1
road 1
the 1
think 1
This 4
we 1
yet 1
you 3
But whenever I submit it to the try server it tests the code and tells me that I didn't output anything, and that I had a "Program failed: Memory fault, core dumped", this is the output I am getting on page 1:
Try Submission Output
This is my wordFreq.c file:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "wordFreq.h"
#define UNUSED(p) ((void)(p))
// Creates a node for each unique word it is given, and then inserts this
// new node in the proper position in the binary search tree, updating
// whatever pointer is necessary.
//
// #param root a pointer to the variable which contains the pointer to the
// root-level node in the tree
// #param word a pointer to the NUL-terminated arrach of characters
void insert_word(TreeNode** root, const char *word){
if (*root == NULL){
*root = (TreeNode*)malloc(sizeof(TreeNode));
unsigned int len = strlen(word);
(*root)->word = (char *)malloc((len + 1) * sizeof(char));
strncpy((*root)->word, word, len);
(*root)->word[len] = 0;
(*root)->frequency = 1;
(*root)->left = NULL;
(*root)->right = NULL;
}
else{
int compare = strcasecmp(word, (*root)->word);
if (compare < 0){
insert_word(&((*root)->left), word);
} else if(compare> 0){
insert_word(&((*root)->right), word);
} else if(compare == 0){
(*root)->frequency++;
}
}
}
// Traverses the entire tree using an in-order traversal and will
// print th contents of each node as it is visited
//
// #param root a pointer to the root node of the tree
void traverse_tree(const TreeNode* root){
if (root == NULL)
return;
if (root != NULL){
traverse_tree(root->left);
printf("%s %d\n", root->word, root->frequency);
traverse_tree(root->right);
}
return;
}
// Deallocates all the nodes that were created in insert_node()
//
// #param a pointer to the root node of the tree
void cleanup_tree(TreeNode* root){
if (root == NULL)
return;
if (root->left != NULL){
cleanup_tree(root->left);
}
if(root->right != NULL){
cleanup_tree(root->right);
}
free(root->word);
free(root);
return;
}
int main(int argc, char* argv[]){
UNUSED(argv);
if (argc > 1)
printf("Usage: bst");
else{
FILE* pFile = fopen("input.1", "r");
char *buf = NULL;
size_t len = 0;
TreeNode** root = NULL;
if (!pFile){
printf("File not found");
} else{
root = (TreeNode**)malloc(sizeof(TreeNode*));
*root = NULL;
while (getline(&buf,&len,pFile) > 0){
char * pch;
pch = strtok(buf, " !##$%^&*?.,:;\n");
while (pch !=NULL){
insert_word(root, pch);
pch = strtok(NULL, " !##$%^&*,:;?.\n");
}
}
free(buf);
fclose(pFile);
traverse_tree(*root);
}
cleanup_tree(*root);
free(root);
}
return 0;
}
This is my wordFreq.h file:
#ifndef _BST_H_
#define _BST_H_
// The definition of the tree structure
typedef struct TreeNode_st {
char *word; // the word held in this node
unsigned int frequency; // how many times it has been seen
struct TreeNode_st *left; // node's left child
struct TreeNode_st *right; // node's right child
} TreeNode;
// FUNCTIONS ARE REQUIRED TO IMPLEMENT
// insert_word()
// Dynamically build BST by allocating nodes from the heap
//
// args -
// root - a pointer to the pointer to the root of the tree
// to build this tree on to.
// word - string containing the word to be inserted
void insert_word( TreeNode** root, const char *word );
// traverse_tree()
// Recursively traverses the tree and prints the value of each
// node.
//
// args -
// root - a pointer to the root of the tree to traverse
void traverse_tree( const TreeNode* root );
// cleanup_tree()
// Cleanup all memory management associated with the nodes on the heap
//
// args
// root - the current root of the tree
void cleanup_tree( TreeNode* root );
#endif // BST_H
This is the input file (named input.1):
This is one input line.
This is another input line, don't you agree?
Another input line, don't you agree?
This is yet another input line, don't you agree?
I think we need this additional line for the road.
Compile Commands:
root#comp:~/Desktop/Homeworks/5$ gcc -ggdb -std=c99 -Wall -Wextra -pedantic -c wordFreq.c
root#comp:~/Desktop/Homeworks/5$ gcc -ggdb -std=c99 -Wall -Wextra -pedantic -o wordFreq wordFreq.o -lm
Valgrind Test: I also tested the code in valgrind using the command:
I got no errors... here's the link to test on page 2:
Valgrind Test Link
You have some issues in your code:
You include non-standard file <strings.h>, but not <stdlib.h> where malloc() and free() are declared.
You never check malloc() return value. If the test system has very little memory or is configured to make malloc() fail early, which is doable, your program will crash instead of reporting the error.
Do not use strncpy((*root)->word, word, len); to copy the string to allocated memory. Just use strcpy since you allocated enough memory with strlen(word) + 1 bytes, or use strdup(). strncpy does not do what you think! it has very error prone and widely misunderstood semantics. Never use this function.
In the main() function, you should use a TreeNode *root; variable and pass its address to insert_word() instead of allocating a pointer.
The only serious issue is the second point above, although it is unlikely to explain the observed behavior.

Creating a Dictionary in C

I am currently working on creating a dictionary using a binary search tree-like structure we designed in class.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
struct entry
{
char* word;
unsigned int n; /* n is the number of times the word appears in the source. */
struct entry *left;
struct entry *right;
};
/*input_from_args: if no additional argument is given, return stdin. Else, open the text file and read it.*/
FILE*
input_from_args(int argc, const char *argv[]){
if(argc==1){
return stdin;
}else{
return fopen(argv[1],"r");
}
}
Below is the insert function that we also wrote in my class. Given the new word we are looking at, if it is
struct entry*
insert(struct entry *table, char* str)
{
if(table == NULL){
table = (struct entry *)malloc(sizeof(struct entry));
strcpy(table->word,str);
table -> n = 1;
table -> left = NULL;
table -> right = NULL;
}else if(strcmp(str, table->word) == 0){
table -> n = (table ->n)+1;
}else if(strcmp(str, table->word) <0){
table->left = insert(table->left, str);
}else if(strcmp(str, table->word) >0){
table ->right = insert(table->right, str);
}
return table;
}
Below is a print function which I wrote myself which is to print every word in table and N, the number of times it occurs.
void
print_table(struct entry *table){
if(table!=NULL){
print_table(table->left);
printf("%s ", table->word);
printf("%d \n", table->n);
print_table(table->right);
}
}
And finally, below is the main function.
int
main(int argc, const char *argv[])
{
FILE *src = input_from_args(argc, argv);
if(src == NULL){
fprintf(stderr, "%s: unable to open %s\n", argv[0], argv[1]);
exit(EXIT_FAILURE);
}
char str[1024];
struct entry *table;
int c;
while((fscanf(src, "%s", str))!= EOF){
table = insert(table, str);
}
print_table(table);
return 0;
}
I'm having some very odd behavior when I run this function. It seems to only be happening when I run it with longer input.
When I run it with this input(in a .txt file):
This is a test.
This is a test.
This is a test.
I get the following output:
This 3
a 3
is 3
test 3
This is what I should be getting. However, when I give it slightly longer input, such as:
Apple Apple
Blue Blue
Cat Cat
Dog Dog
Elder Elder
Funions Funions
Gosh Gosh
Hairy Hairy
I get the following output:
Appme 2
Blue 2
Cat 2
Dog 2
Elder 2
Funions 2
Gosi 2
Hairy 2
Which is clearly correct as far as the numbers go, but why is it changing some of the letters in my words? I gave it Apple, it returned Appme. I gave it Gosh, it gave me Gosi. What's going on with my code that I am missing?
This line in the insert function is very problematic:
strcpy(table->word,str);
It's problematic because you don't actually allocate memory for the string. That means that table->word is uninitialized and its value will be indeterminate, so the strcpy call will lead to undefined behavior.
The simple solution? Use strdup to duplicate the string:
table->word = strdup(str);
The strdup function is not actually in standard C, but just about all platforms have it.
In your insert function, you do not allocate/malloc() space for the word pointer you are trying to strcpy() to:
if(table == NULL){
table = (struct entry *)malloc(sizeof(struct entry));
strcpy(table->word,str);
table -> n = 1;
table -> left = NULL;
table -> right = NULL;
}
Usually this code would exit with a segmentation fault, because you are copying data to memory you don't own, but this is easy to fix:
table->word = malloc(strlen(str) + 1);
strcpy(table->word, str);
You'll want to allocate one extra byte above the string length, to allow for the null terminator.
You do not need or want to cast the result of malloc(). In other words, this is fine:
table = malloc(sizeof(struct entry));
Get into the habit of using free() on any pointers you have malloc()-ed, when you are done with them. Otherwise, you end up with a memory leak.
Also, compile with -Wall -Weverything flags to enable all warnings.
Note: If one absolutely must use strdup(), it is easy to write a custom function to do so:
char* my_very_own_strdup(const char* src)
{
char* dest = NULL;
if (!src)
return dest;
size_t src_len = strlen(src) + 1;
dest = malloc(src_len);
if (!dest) {
perror("Error: Could not allocate space for string copy\n");
exit(EXIT_FAILURE);
}
memcpy(dest, src, src_len);
return dest;
}
On the line strcpy(table->word,str); where is table->word allocated?
So It copies only 4 bytes to table->word because pointer size is 4-bytes in your machine. So Be careful, you must allocate table->word there,
I would use this one instead of that table->word = strdup(str);

C - Pointer to dynamic array in struct "Segmentation fault (core dumped)"

I'm writing a c program to read the files and directories from a directory and then point the number of elements found in a data of a struct and point the name of the elements in a dynamic array in a data of the same struct. I did it and its output is right. The problem is that when I run the program a "Segmentation fault (core dumped)" shows up.
The code:
#include <stdio.h>
#include <dirent.h>
#define EXIT_FAILURE 1
typedef struct FileDir
{
int *n_files;
char *file_name[];
} FileDir;
int get_files_within_dir(struct FileDir *fd)
{
DIR *dir;
int n_files;
int index;
n_files = 0;
if ((dir = opendir ("/tmp")) != NULL) {
/* counts all the files and directories within directory */
while (readdir (dir) != NULL) {
n_files++;
}
closedir (dir);
} else {
/* could not open directory */
perror ("");
return EXIT_FAILURE;
}
char *file_name[n_files];
struct dirent *ent;
if ((dir = opendir ("/tmp")) != NULL) {
/* gets all the files and directories within directory */
index = 0;
while ((ent = readdir (dir)) != NULL) {
file_name[index++] = ent->d_name;
}
closedir (dir);
} else {
/* could not open directory */
perror ("");
return EXIT_FAILURE;
}
fd->n_files = n_files;
fd->file_name[0] = file_name[0];
fd->file_name[1] = file_name[1];
fd->file_name[2] = file_name[2];
fd->file_name[3] = file_name[3];
return 0;
}
int main()
{
struct FileDir fd;
get_files_within_dir(&fd);
printf("%d\n", fd.n_files);
printf("%s\n", fd.file_name[1]);
printf("%s\n", fd.file_name[2]);
printf("%s\n", fd.file_name[3]);
return 0;
}
The output:
[freitas#localhost src]$ ./file_dir
21
..
geany_socket.fcda02b3
tmpiSdUX3
Segmentation fault (core dumped)
The interesting thing is that if I just point less than or equal to 2 values to the dynamic array of the data of the struct the error message does not show up. Do you have any idea ?
Thank you!
You have 2 problems, that can be causing the SEGMENTATION FAULT
The n_files field is a pointer, and you assigned an integer to it, it should be declared as
int n_files;
You don't ever allocate space for the file_name field, you should at least provide a fixed size, like this
char *file_name[1000];
you could allocate the memory dynamically using malloc() but that's another thing, and it requires explanation.
note: enabling compiler warnigns would help you prevent silly mistakes like int *n_files and then doing fd->n_files = n_files;.
n_files should not be a pointer
typedef struct FileDir
{
int n_files;
char *file_name[];
} FileDir;
Then your line
printf("%d\n", fd.n_files);
will not crash. Try looking at the struct with a debugger

Hashed array linked list collision resolution error

This block of code reads a dictionary file and stores it in a hashed array. This hashing array uses linked list collision resolution. But, for some incomprehensible reason, the reading stops in the middle. (i'm assuming some problem occurs when linked list is made.) Everything works fine when data is being stored in a empty hashed array element.
#define SIZE_OF_ARRAY 350
typedef struct {
char* key;
int status; // (+1) filled, (-1) deleted, 0 empty
LIST* list;
}HASHED_ARRAY;
void insertDictionary (HASHED_ARRAY hashed_array[])
{
//Local Declaration
FILE* data;
char word[30];
char* pWord;
int index;
int length;
int countWord = 0;
//Statement
if (!(data = fopen("dictionaryWords.txt", "r")))
{
printf("Error Opening File");
exit(1);
}
SetStatusToNew (hashed_array); //initialize all status to 'empty'
while(fscanf(data, "%s\n", word) != EOF)
{
length = strlen(word) + 1;
index = hashing_function(word);
if (hashed_array[index].status == 0)//empty
{
hashed_array[index].key = (char*) malloc(length * sizeof(char));//allocate word.
if(!hashed_array[index].key)//check error
{
printf("\nMemory Leak\n");
exit(1);
}
strcpy(hashed_array[index].key, word); //insert the data into hashed array.
hashed_array[index].status = 1;//change hashed array node to filled.
}
else
{
//collision resolution (linked list)
pWord = (char*) malloc(length * sizeof(char));
strcpy (pWord, word);
if (hashed_array[index].list == NULL) // <====== program doesn't enter
//this if statement although the list is NULL.
//So I'm assuming this is where the program stops reading.
{
hashed_array[index].list = createList(compare);
}
addNode(hashed_array[index].list, pWord);
}
countWord++;
//memory allocation for key
}
printStatLinkedList(hashed_array, countWord);
fclose(data);
return;
}
createList and addNode are both ADT function. Former takes a function pointer (compare is a function that I build inside the main function) as a parameter, and latter takes list name, and void type data as parameters. compare sorts linked list. Please spot me the problem .
Depending on where you declare the hashed_array you pass to this function, the contents of it may not be initialized. This means that all contents of all entries is random. This includes the list pointer.
You need to initialize this array properly first. The easiest way is to simple use memset:
memset(hashed_array, 0, sizeof(HASHED_ARRAY) * whatever_size_it_is);
This will set all members to zero, i.e. NULL for pointers.

memset throwing segmentation fault

I am having some problem with writing a function to extract strings from a file as part of a bigger program. Everything seems to be working fine, except when I use memset or bzero to erase the character arrays I have been using. I have been sitting with this problem for more than an hour and I keep getting seg faults whatever I do. I am getting this error for both bzero and memset. Please help me out.
I am attaching my code below. The statement "Come out of addfront" is printed but none of the "Done with all bzero" statements are printing. I get a segmentation fault at that point. Thank you
void extractFileData(FILE *fp , char clientName[])
{
char tempFileName[50], tempFilePath[100], tempFileSize[50];
struct stat fileDetails;
while(fgets(tempFileName, sizeof(tempFileName), fp)!= NULL)
{
if((newLinePos = strchr(tempFileName, '\n')) != NULL)
{
*newLinePos = '\0';
}
strcat(tempFilePath, "SharedFiles/");
strcat(tempFilePath, tempFileName);
if(stat(tempFilePath, &fileDetails) < 0)
{
perror("Stat error");
exit(1);
}
//Copy it into a string
sprintf(tempFileSize, "%zu", fileDetails.st_size);
printf("temp file size: %s\n", tempFileSize);
//Add all these details to the file list by creating a new node
addFront(tempFileName, tempFileSize, clientName);
printf("Come out of addfront\n");
memset(&tempFileName, 0, 45);
printf("Done with all bzero\n");
memset(&tempFileSize, 0, sizeof(tempFileSize));
memset(&tempFilePath, 0, sizeof(tempFilePath));
printf("Done with all bzero\n");
}
}
EDIT:
void addFront(char fileName[], char fileSize[], char clientName[])
{
FILENODE* n;
printf("Inside add front function\n");
strcpy(n->fileName, fileName);
printf("n->filename: %s\n", n->fileName);
strcpy(n->fileSize, fileSize);
printf("n->filesize: %s\n", n->fileSize);
strcpy(n->ownerName, clientName);
printf("n->ownername: %s\n", n->ownerName);
myFileList.head = n;
printf("Did it go past myfilelist head = n\n");
myFileList.numOfNodes++;
printf("num of nodes: %d\n", myFileList.numOfNodes);
}
I have added my code for the addFront function. It basically adds
the details to a struct myFileList which is basically an implementation
of a linked list. The FILENODE represents each entry in the list.
EDIT:
Adding the structs I am using
struct fileNode
{
char fileName[50];
char fileSize[50];
char ownerName[25];
struct fileNode* next;
};
struct fileList
{
struct fileNode* head;
struct fileNode* tail;
int numOfNodes;
};
typedef struct fileList FILELIST;
typedef struct fileNode FILENODE;
I don't know why your program would crash there. But I can another error in the program. Fix the other error first, see if you still have problems.
This is wrong:
strcat(tempFilePath, "SharedFiles/");
strcat(tempFilePath, tempFileName);
The tempFilePath variable is uninitialized. This may coincidentally not crash, but you cannot rely on it not to crash. It may scribble on your stack.
Do this instead:
snprintf(tempFilePath, sizeof(tempFilePath), "SharedFiles/%s", tempFileName);
Finally, there is no need to zero the arrays. The contents of the arrays are not used in the next loop iteration, so you might as well ignore them.
void extractFileData(FILE *fp , char clientName[])
{
char tempFileName[50], tempFilePath[100], *newLinePos;
struct stat fileDetails;
while (fgets(tempFileName, sizeof(tempFileName), fp)) {
if ((newLinePos = strchr(tempFileName, '\n')))
*newLinePos = '\0';
snprintf(tempFilePath, sizeof(tempFilePath),
"SharedFiles/%s", tempFileName);
if (stat(tempFilePath, &fileDetails) < 0) {
perror("Stat error");
exit(1);
}
printf("temp file size: %zu\n", tempFileSize);
addFront(tempFileName, tempFileSize, clientName);
}
}
The snprintf() function is really the number one choice for doing work like this in C. It's easy to write code with snprintf() that "obviously won't crash", as opposed to code that "won't obviously crash".
If your code still crashes, there is an error somewhere else.
addFront() needs a n = malloc( sizeof *n) before you do anything with it.

Resources