Want to fill Structure for Metadata - c

Here i have one directory which has number of files.
I want to fill this files all information in one structure.
I have two structures which are following.
struct files {
char *file_name;
int file_size;
};
typedef struct file_header {
int file_count;
struct files file[variable as per number of files];
} metadata;
i want to make one header which contains all information regarding these files.
like if i have 3 files than i want to make this structure like this in file_count = 3 and how can i allocate second variable value? and want to store file name and file size as per file.
i want file structure like this
file_count = 3
file[0].file_name = "a.txt"
file[0].file_size = 1024
file[1].file_name = "b.txt"
file[1].file_size = 818
file[2].file_name = "c.txt"
file[2].file_size = 452
I have all logic about file name and file size but how can i fill these things in this structure.?
Code :
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
char path[1024] = "/home/test/main/Integration/testing/package_DIR";
//int count = 5;
struct files {
char *file_name;
int file_size;
};
typedef struct file_header {
int file_count;
struct files file[5];
} metadata;
metadata *create_header();
int main() {
FILE *file = fopen("/home/test/main/Integration/testing/file.txt", "w");
metadata *header;
header = create_header();
if(header != NULL)
{
printf("size of Header is %d\n",sizeof(metadata));
}
if (file != NULL) {
if (fwrite(&header, sizeof(metadata), 1, file) < 1) {
puts("short count on fwrite");
}
fclose(file);
}
file = fopen("/home/test/main/Integration/testing/file.txt", "rb");
if (file != NULL) {
metadata header = { 0 };
if (fread(&header, sizeof(header), 1, file) < 1) {
puts("short count on fread");
}
fclose(file);
printf("File Name = %s\n", header.file[0].file_name);
printf("File count = %d\n", header.file_count);
printf("File Size = %d\n", header.file[0].file_size);
}
return 0;
}
metadata *create_header()
{
int file_count = 0;
DIR * dirp;
struct dirent * entry;
dirp = opendir(path);
metadata *header = (metadata *)malloc(sizeof(metadata));
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_type == DT_REG) { /* If the entry is a regular file */
header->file[file_count].file_name = (char *)malloc(sizeof(char)*strlen(entry->d_name));
strcpy(header->file[file_count].file_name,entry->d_name);
//Put static but i have logic for this i will apply later.
header->file[file_count].file_size = 10;
file_count++;
}
}
header->file_count = file_count;
closedir(dirp);
//printf("File Count : %d\n", file_count);
return header;
}
output :
size of Header is 88
ile Name = �~8
File count = 29205120
File Size = -586425488
Its shows different output. so whats problem here?

Among other things, you are using sizeof on a pointer variable, but seem to think that gives you the size of the object being pointed to. It doesn't. To do that, use the asterisk operator to make the expression have the type that the pointer points at:
printf("size of Header is %d\n", sizeof *metadata);
As a side note, notice that sizeof is not a function, so you don't need parenthesis. When you do see parenthesis, that's when they're part of the expression (a cast).

You are not leaving enough room for the null-terminator:
header->file[file_count].file_name = (char *)malloc(sizeof(char)*strlen(entry->d_name));

Related

How to read all files in the directory and store them in the pointer in the C language program?

Mingw x86_64 v11.2.0
Windows 10 21H2
My current problem is that this function can normally read files and folders in the directory, but if it is saved in the pointer and printed on the screen, a single file will appear repeatedly.
These are the files in the directory.
.
..
main.c
main.exe
README.md
test.txt
The following is the source code I wrote:
#include "../DeleteCompletely.h"
#include <dirent.h>
#define TotalNumber 4096
#define TotalFileNameLen 4096
typedef struct dirent DIRENT;
typedef struct {
DIR *dir_ptr;
DIRENT *dirent_ptr;
char **pathSet;
size_t number;
} snDIR;
static snDIR *getAllFile(const char *path)
{
snDIR *dirSet = (snDIR *)malloc(sizeof(snDIR));
dirSet->dir_ptr = opendir(path);
size_t loopIndex;
dirSet->pathSet = (char **)malloc(sizeof(char **) * TotalNumber);
if(dirSet->dir_ptr) {
dirSet->number = 0;
loopIndex = 0;
while ((dirSet->dirent_ptr = readdir(dirSet->dir_ptr)) != NULL) {
dirSet->pathSet[loopIndex] = (char *)malloc(TotalFileNameLen);
dirSet->pathSet[loopIndex] = dirSet->dirent_ptr->d_name;
dirSet->number++;
loopIndex++;
}
closedir(dirSet->dir_ptr);
}
return dirSet;
}
int main(int argc, char **argv)
{
snDIR *set = getAllFile(".");
for(size_t x = 0; x < set->number; ++x) {
printf("%s\n", set->pathSet[x]);
}
free(set);
return 0;
}
The problem are these assignments:
dirSet->pathSet[loopIndex] = (char *)malloc(TotalFileNameLen);
dirSet->pathSet[loopIndex] = dirSet->dirent_ptr->d_name;
The first make dirSet->pathSet[loopIndex] point to one location. The second make it point somewhere completely different.
Instead of the second assignment you need to copy the string:
strcpy(dirSet->pathSet[loopIndex], dirSet->dirent_ptr->d_name);
You're also wasting quite a lot of memory by always allocating a large amount or memory. The likelihood of the string being that large are rather small. Instead use the string length as the base size:
dirSet->pathSet[loopIndex] = malloc(strlen(dirSet->dirent_ptr->d_name) + 1);
// +1 for the null terminator

Structs for file I/O

I was trying to figure out the best way to keep a record of file pointers as well as individual information for each file such as a file path.
My question is, having a folder struct that holds an array of file pointers, and a file struct that holds information for files, how could merge these two concepts so that can I hold an array of file pointers, and store the file path for each of those files?
Header file for a folder struct below:
#ifndef FOLDER_STRUCT_H
#define FOLDER_STRUCT_H
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
struct folder_
{
FILE **files;
size_t current_size;
size_t capacity;
};
typedef struct folder_ folder_t;
folder_t *folder_create(size_t initial_size); //-----------------------Create a folder struct
void folder_destroy(folder_t *folder); //------------------------------Destroy a folder struct
bool folder_insert_file(folder_t *const folder, FILE *const file); //--Insert a file
FILE *folder_get_file(folder_t *const folder, size_t index); //--------Get a file by index
FILE **folder_get_file_list(folder_t *const folder); //----------------Get a list of files
int folder_get_size(folder_t *folder); //------------------------------Get folder size
int folder_get_total_capacity(folder_t *folder); //--------------------Get folder capacity
#endif
Header file for a file struct used to record file information such as a file path:
#ifndef FILE_H
#define FILE_H
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
struct file_
{
FILE *file;
char *file_path;
};
typedef struct file_ file_t;
#endif
The generic approach is if you need both at the same time use another struct, and if you need either use a union probably with a tag field to tell which of the struct it holds. In your case, it sounds like you have 1 folder to n files, so your folder_file_ struct would have an array of files_. If you have more than one folder, then you need an array of folder_file_.
FILE * usually implies an open file handle. Is that really your use case? Opposed to a bunch of paths, and you only open the file as you have to operate on it. Also, why is FILE ** opposed to a FILE *?
Usually is best to let the system manage the file pointers. The file can be reopened outside your struct for example. Or just closed. The struct can get a copy of a FILE* but there is no way to know if it points to the same file as of the moment of creation.
Anyway, sometimes we may need a way to store info for a collection of files.
An EXAMPLE
typedef struct
{
char* name;
FILE* F;
} File;
typedef struct
{
size_t size;
size_t cap; // capacity
size_t inc; // increment
File** info; // data array
} Folder;
Inside File we can store sizes, dates, maybe permissions.
Folder is an array of pointers to File. cap and size are the usual, inc is the size of increment of Folder in files.
A few functions are implemented:
Folder* create(size_t capacity, size_t step);
Folder* destroy(Folder*);
int insert(File*, Folder*);
int show(Folder*, const char*);
int trim(Folder*);
int get_pos(const char* f_name, Folder*);
insert() accepts a File pointer is easy to customize File.
trim() releases all pointers not in use
show() accepts a title, for convenience
get_pos() returns the position of the provided file in the array
destroy() returns NULL to ease in invalidate the pointer in the same line
main for this test
int main(void)
{
const char tst_size = 10;
const char* scan[] = {"File_7533.tst", "File_7500.tst"};
srand(220520);
Folder* tst = create(4, 4);
show(tst, "just created");
char f_name[20] = {0};
for (int i = 0; i < tst_size; i += 1)
{
sprintf(f_name, "File_%04d.tst", 1+rand()%10000);
insert_this(f_name,NULL,tst);
}
sprintf(f_name, "%d files inserted", tst_size);
show(tst, f_name);
trim(tst);
show(tst, "after trim()");
for (int i = 0; i < sizeof(scan) / sizeof(scan[0]);i+=1)
printf( "search por \"%s\" returned %d\n", scan[i],
get_pos(scan[i], tst));
tst = destroy(tst);
return 0;
}
a Folder is created with size 4 and incrementable in groups of 4
10 files are inserted
the contents are listed
search for 2 files
the thing is destroyed
test output
just created
[0/4 files (step = 4)]
insert() size extended to 8
insert() size extended to 12
10 files inserted
[10/12 files (step = 4)]
0 File_2037.tst
1 File_5785.tst
2 File_6602.tst
3 File_7231.tst
4 File_0854.tst
5 File_7102.tst
6 File_7533.tst
7 File_6460.tst
8 File_1717.tst
9 File_1948.tst
trim() new size: 10
after trim()
[10/10 files (step = 4)]
0 File_2037.tst
1 File_5785.tst
2 File_6602.tst
3 File_7231.tst
4 File_0854.tst
5 File_7102.tst
6 File_7533.tst
7 File_6460.tst
8 File_1717.tst
9 File_1948.tst
search for "File_7533.tst" returned 6
search for "File_7500.tst" returned -1
Folder structure destroyed
stuff.h
#ifndef STUFF_H
#define STUFF_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char* name;
FILE* F;
} File;
typedef struct
{
size_t size;
size_t cap; // capacity
size_t inc; // increment
File** info; // data array
} Folder;
Folder* create(size_t capacity, size_t step);
Folder* destroy(Folder*);
int insert(File*, Folder*);
int show(Folder*, const char*);
int trim(Folder*);
int get_pos(const char* f_name, Folder*);
#endif
stuff.c
#include "stuff.h"
Folder* create(size_t capacity, size_t step)
{
Folder* nw = (Folder*)malloc(sizeof(Folder));
if (nw == NULL) return NULL;
nw->size = 0;
nw->cap = capacity;
nw->inc = step;
nw->info = (File**) malloc(nw->cap*sizeof(File*));
for (int i = 0; i < nw->cap; i += 1)
nw->info[i] = NULL;
return nw;
}
Folder* destroy(Folder* f)
{
if (f == NULL) return NULL;
for (int i = 0; i < f->size; i += 1)
{
free(f->info[i]->name);
free(f->info[i]);
}
free(f->info);
free(f);
printf("Folder structure destroyed\n");
return NULL;
}
int insert(File* item, Folder* f)
{
if (f == NULL) return -1;
if (item == NULL) return -2;
if (f->size >= f->cap)
{ // extends folder struct size
size_t new_size = f->cap + f->inc;
File* p = realloc((void*)f->info, new_size*sizeof(File*));
if (p == NULL) return -3; // error extending
f->info = (File**) p;
f->cap = new_size;
printf("insert() size extended to %zd\n", f->cap);
};
// ok, new File then
File* nw = (File*)malloc(sizeof(File));
nw->name = (char*)malloc(1 + strlen(item->name));
strcpy(nw->name, item->name);
nw->F = item->F;
f->info[f->size] = nw;
f->size += 1;
return (int)f->size;
}
int show(Folder* f, const char* msg)
{
if (msg != NULL) printf("%s\n", msg);
printf(
"[%zd/%zd files (step = %zd)\n", f->size, f->cap,
f->inc);
for (int i = 0; i < f->size; i += 1)
{
printf("%3d %s\n", i, f->info[i]->name);
}
printf("\n");
return 0;
}
int trim(Folder* f)
{
if (f == NULL) return -1;
if (f->cap == f->size) return 0; // nothing to do
File* p = realloc((void*)f->info, f->size * sizeof(File*));
if (p == NULL) return -3; // error extending
f->info = (File**) p;
f->cap = f->size;
printf("trim() new size: %zd\n", f->cap);
return 0;
};
int get_pos(const char* f_name, Folder* f)
{
if (f_name == NULL) return -1;
for (int i = 0; i < f->size; i += 1)
if (strcmp(f_name, f->info[i]->name) == 0) return i;
return -1;
}
Note to SO "code reviewers": I do cast all malloc() pointers, as I do not like implicit things and I like them as additional reminders of things to come.

Memory management in Linked Lists C

I am trying to implement a linked list data structure that represents a folder tree.
The structures below:
typedef struct SRC_ERROR SRC_ERROR;
struct SRC_ERROR {
int error_code;
char *error;
};
typedef struct SRC_FILE SRC_FILE;
struct SRC_FILE {
char *entry;
char md5[MD5_DIGEST_LENGTH];
};
typedef struct SRC SRC; //Source file tree with md5 entry char for source verification.
struct SRC {
SRC_ERROR error;
char *name;
char *full_path;
SRC_FILE **entries;
SRC *next_dir;
};
The idea was that each directory will be stored in SRC the SRC_FILE is to be used as an array to store the filename and MD5 hash for each file.
The scan_source() below populates the structures.
SRC *scan_source(char *source_path) {
SRC *source = malloc(sizeof(SRC));
source->error.error_code = OK;
int count = 0;
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(source_path))) {
source->error.error_code = ERROR;
source->error.error = "Unable to open source directory.\n";
return source;
}
source->entries = (SRC_FILE **)malloc(sizeof(SRC_FILE *) * count);
if (source->entries == NULL) {
source->error.error_code = ERROR;
source->error.error = "Unable to allocate memory to file entry tree\n";
}
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
char path[PATH_MAX];
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
snprintf(path, sizeof(path), "%s/%s", source_path, entry->d_name);
printf("[%s] - %s\n", entry->d_name, path);
//add new node
source = add_dir(source, insert_dir_node(entry->d_name, path));
scan_source(path);
} else
if (entry->d_type == DT_REG) {
printf("[FILE] - %s\n", entry->d_name);
source->entries[count]->entry = entry->d_name; //SEGFAULT HERE
count++;
source->entries = realloc(source->entries, sizeof(SRC_FILE *) * (count));
}
}
closedir(dir);
return source;
}
I am having issues with memory management. I am getting intermittent seg faults when the directory is structured in certain ways.
I have marked the line that the debugger has flagged
source->entries[count]->entry = entry->d_name; //SEGFAULT HERE
I thought that I allocated memory for each structure but maybe I have not done this correctly or there is an underlying problem with the data structure entirely?
For Example:
test> tree
.
└── Text
0 directories, 1 file
This causes a seg fault. Whereas, this does not:
/test> tree
.
├── another sample
│   └── Text
└── sample folder
2 directories, 1 file
Additional functions that are used:
SRC *add_dir(SRC *file_tree, SRC *new_dir) {
new_dir->next_dir = file_tree;
return new_dir;
}
SRC *insert_dir_node(char *name, char *full_path) {
SRC *next_dir;
next_dir = (SRC *)emalloc(sizeof(SRC));
next_dir->name = name;
next_dir->full_path = full_path;
next_dir->next_dir = NULL;
return next_dir;
}
I started looking at the code, and the first issue I see is that you're storing pointers returned by a readdir() call - you should copy the data contained therein instead.
Change
source = add_dir(source, insert_dir_node(entry->d_name, path));
to
source = add_dir(source, insert_dir_node(strdup(entry->d_name), path));
The reason you're seeing segmentation faults is that you always write after the end of the source->entries array.
You initially create a 0-size array:
int count = 0;
/* ... */
source->entries = (SRC_FILE **) malloc(sizeof(SRC_FILE*) * count);
Then set its 1st (indexed by 0) element:
source->entries[count]->entry = entry->d_name; //SEGFAULT HERE
count++;
source->entries = realloc(source->entries, sizeof(SRC_FILE*)*(count));
Then you expand the array to 1 element, then write to the second index, and so on.
You can either fix the logic (allocate space for count+1 elements always, because you want to have room not only for the existing ones but also for the next one), or, which in this case may be more efficient, switch to a linked list structure here as well.
The next problem is that you're only allocating pointers to SRC_FILE, not SRC_FILE structures - you should change the definition to:
struct SRC {
SRC_ERROR error;
char *name;
char *full_path;
SRC_FILE *entries;
SRC *next_dir;
};
And the initialization to
source->entries = (SRC_FILE *) malloc(sizeof(SRC_FILE) * (count + 1));
Then the critical part to
source->entries[count].entry = strdup(entry->d_name);
count++;
source->entries = realloc(source->entries, sizeof(SRC_FILE) * (count + 1));
There's one more thing to attend to: insert_dir_node creates a new SRC struct, which will need to have a freshly initialized entries member:
next_dir->count = 0;
next_dir->entries = (SRC_FILE *)malloc(sizeof(SRC_FILE) * (1));
and, since we have now separate entries we need to have a count for each of them, so move this variable into the struct as well.
Fixing all of these provided me with an error-free program.
The subject is Memory management in linked lists. Indeed this is a major issue in C program because there is no automatic memory management. You must decide and specify how each object pointed to by a pointer in your structures is handled from a memory management standpoint. Is the pointer the reference for the object life time or is the lifetime handled somewhere else and the pointer just an access point.
Let's analyse your object definitions:
typedef struct SRC_ERROR SRC_ERROR;
struct SRC_ERROR {
int error_code;
char *error;
};
SRC_ERROR is just a way to package an error description. If the error member always stores a pointer to a string literal, it should be defined as const char *. Conversely, if in some cases you allocate a string with information specific to the actual error, such as "error allocating 1023 objects\n", then you either need an indicator specifying the error points to allocated memory that should be freed after use or you should always allocate memory for the error message and always free this memory when discarding the SRC_ERROR object.
typedef struct SRC_FILE SRC_FILE;
struct SRC_FILE {
char *entry;
char md5[MD5_DIGEST_LENGTH];
};
entry should point to allocated memory and this memory should be freed when discarding the SRC_FILE object.
typedef struct SRC SRC; //Source file tree with md5 entry char for source verification.
struct SRC {
SRC_ERROR error;
char *name;
char *full_path;
SRC_FILE **entries;
SRC *next_dir;
};
name and full_path should point to allocated memory and should be freed when discarding the SRC object.
next_dir points to another SRC object, which should be allocated and freed consistently.
entries points to an allocated array, each element of which points to an allocated object. You need a way to tell the number of elements in this array. You could maintain a NULL pointer at the end of the array, but it is simpler to add a count member in SRC for this information. It would also be much simpler to make this a pointer to an allocated array of SRC objects.
The function does not construct a tree, but attempts to construct a list of directories. Whenever to recurse into a directory, you should append the new list from the SRC_ERROR object returned by scan_source to the list already constructed in the SRC_ERROR object allocated by the caller and free the object returned by the recursive call.
Here is a modified version in a test program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#define MD5_DIGEST_LENGTH 16
#define TRACE(x) //x
enum { OK = 0, ERROR, OUT_OF_MEMORY };
typedef struct ERROR_STATE ERROR_STATE;
struct ERROR_STATE {
int code;
const char *message; // always a string literal
};
typedef struct SRC_FILE SRC_FILE;
struct SRC_FILE {
char *name; // points to allocated memory
char md5[MD5_DIGEST_LENGTH];
};
typedef struct SRC SRC; //Source file tree with md5 entry char for source verification.
struct SRC {
char *name; // points to allocated memory
char *full_path; // points to allocated memory
size_t count; // number of elements in entries
SRC_FILE *entries; // allocated array of count elements
SRC *next_dir; // the next SRC
};
static char *basename_dup(const char *full_path) {
char *p = strrchr(full_path, '/');
return strdup(p ? p + 1 : full_path);
}
/* construct a SRC describing the directory contents.
* if there is an error, either return a partially constructed SRC or return NULL
*/
SRC *scan_source(const char *source_path, ERROR_STATE *error) {
char *full_path = strdup(source_path);
char *name = basename_dup(source_path);
SRC *source = calloc(1, sizeof(SRC)); // all members initialized to 0
if (source == NULL) {
error->code = ERROR;
error->message = "Unable to allocate memory.\n";
free(full_path);
free(name);
free(source);
return NULL;
}
error->code = OK;
source->full_path = full_path;
source->name = name;
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(source_path))) {
error->code = ERROR;
error->message = "Unable to open source directory.\n";
return source;
}
while ((entry = readdir(dir)) != NULL) {
char path[PATH_MAX];
int len;
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
len = snprintf(path, sizeof(path), "%s/%s", source_path, entry->d_name);
if (len >= (int)sizeof(path)) {
// the path was truncated.
// you can report this or ignore it...
TRACE(printf("[%s] - %s - path too long, ignored\n", entry->d_name, path));
continue;
}
if (entry->d_type == DT_DIR) {
TRACE(printf("[%s] - %s\n", entry->d_name, path));
SRC *source1 = scan_source(path, error);
if (error->code != OK) {
// either ignore the error or abort?
}
if (source1) {
// append the new directory (and its list of sub-directories)
SRC **tailp = &source->next_dir;
while (*tailp) tailp = &(*tailp)->next_dir;
*tailp = source1;
}
} else
if (entry->d_type == DT_REG) {
TRACE(printf("[FILE] - %s\n", entry->d_name));
// add the file to the entries list
SRC_FILE *entries = realloc(source->entries, sizeof(source->entries[0]) * (source->count + 1));
if (entries == NULL) {
// you should return to the caller with a proper error code
error->code = OUT_OF_MEMORY;
error->message = "cannot reallocate entries array";
break;
}
source->entries = entries;
// source->entries[count] must point to an allocated object
name = strdup(entry->d_name);
if (name == NULL) {
error->code = OUT_OF_MEMORY;
error->message = "cannot allocate entry name";
break;
}
source->entries[source->count].name = name;
memset(source->entries[source->count].md5, 0, sizeof(source->entries[source->count].md5));
source->count++;
//if (md5_sum(full_path, source->entries[source->count].md5)) {
// // error computing the MD5 sum...
//}
}
}
closedir(dir);
return source;
}
void free_source(SRC *source) {
if (source) {
free(source->name);
free(source->full_path);
for (size_t i = 0; i < source->count; i++) {
free(source->entries[i].name);
}
free(source);
}
}
int main(int argc, char *argv[1]) {
ERROR_STATE error = { 0, NULL };
if (argc < 2) {
printf("usage: scansource directory [...]\n");
return 1;
}
for (int i = 1; i < argc; i++) {
SRC *source = scan_source(argv[i], &error);
if (error.code) {
printf("Error %d: %s\n", error.code, error.message);
}
while (source) {
SRC *cur = source;
source = source->next_dir;
printf("{\n"
" name: '%s',\n"
" full_path: '%s',\n"
" count: %zu,\n"
" entries: [\n",
cur->name, cur->full_path, cur->count);
for (size_t j = 0; j < cur->count; j++) {
printf(" { md5: '");
for (size_t k = 0; k < MD5_DIGEST_LENGTH; k++)
printf("%02x", cur->entries[j].md5[k]);
printf("', name: '%s' },\n", cur->entries[j].name);
}
printf(" ]\n},\n");
free_source(cur);
}
}
return 0;
}

An element of a struct loses its value after a function is called passing another struct as argument

I have a problem that I can't figure out. I have the following files: file_reader.c, file_reader.h, file_writer.c, file_writer.h, test_file_reader.c
I'm working with 'struct' to read and write files. For better understanding I wrote the following code test_file_reader.c:
#include <stdio.h>
#include "file_reader.h"
#include "file_writer.h"
int main ()
{
char *file_path = "/home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml";
struct FileReader *fr = malloc(sizeof(struct FileReader));
file_reader_new (file_path, fr);
show_file_reader_values(fr);
struct FileWriter *fw = malloc(sizeof(struct FileWriter));
fw->file_path = "/tmp/text1.txt";
fw->content = "aaa";
write (fw);
show_file_reader_values(fr);
return 0;
}
void show_file_reader_values(const struct FileReader *fr)
{
printf("==========FILE READER==========\n");
printf("file path: %s\n", fr->file_path);
printf("----------file content---------\n");
printf("content:\n%s\n", fr->content);
printf("----------file content---------\n");
printf("n lines: %d\n", fr->n_lines);
printf("n characters: %d\n", fr->n_characters);
printf("==========FILE READER==========\n\n");
}
The function 'file_reader_new' reads the file and then signs the content, file path, number of lines and number of characters to the 'struct' 'FileReader'.
When I call the function 'show_file_reader_values' in the first time I do not have problems with the content but when I call the function 'write' and then call the function 'show_file_reader_values' again, the content is not the same anymore. The question is that the function 'write' of the file 'file_writer.c' and its struct does not have any relation to the file 'file_reader' and its struct. So, how can a function using another struct change the values of another struct of another file ?
The output:
[freitas#localhost test]$ ./test_file_reader
==========FILE READER==========
file path: /home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml
----------file content---------
content:
<cleaner> <id>k3b</id> <label>k3b</label> <description>Disc writing software</description> <option> <id>log</id> <label>Log</label> <description>Delete the log file which contains information about the last writing session(s).</description> <command>delete</command> <search>glob</search> <path>~/.kde/share/apps/k3b/*.log</path> </option> <option> <id>log2</id> <label>Log</label> <description>Delete the log file which contains information about the last writing session(s).</description> <command>delete</command> <search>glob</search> <path>~/.kde/share/apps/k3b/*.log</path> </option> </cleaner>
----------file content---------
n lines: 1
n characters: 621
==========FILE READER==========
==========FILE READER==========
file path: /home/freitas/Dropbox/projects/gcleaner/cleaners/custom.xml
----------file content---------
content:
<cleaner> <id>k��U�N
----------file content---------
n lines: 1
n characters: 621
==========FILE READER==========
Did you see ? In the first call I had the entire output:
<cleaner> <id>k3b</id> <label>k3b</label> <description>Disc wri...
but in the second call I had:
<cleaner> <id>k��U�N
file_reader.c
#include <stdio.h>
#include <stdlib.h>
#include "file_reader.h"
int file_reader_new(const char *file_path, struct FileReader *fr)
{
char *content; // holds the file content.
int counter; // holds the file number of lines.
size_t i; // indexing into content.
size_t buffer_size; // size of the content.
char *temp; // for realloc().
char c; // for reading from the input.
FILE *input; // our input stream.
if ((input = fopen(file_path, "r")) == NULL) {
fprintf(stderr, "Error opening input file %s\n", file_path);
exit(EXIT_FAILURE);
}
/* Initial allocation of content */
counter = 0;
i = 0;
buffer_size = BUFSIZ;
if ((content = malloc(buffer_size)) == NULL) {
fprintf(stderr, "Error allocating memory (before reading file).\n");
fclose(input);
}
while ((c = fgetc(input)) != EOF) {
/* Enlarge content if necessary. */
if (i == buffer_size) {
buffer_size += BUFSIZ;
if ((temp = realloc(content, buffer_size)) == NULL) {
fprintf(stderr, "Ran out of core while reading file.\n");
fclose(input);
free(content);
exit(EXIT_FAILURE);
}
content = temp;
}
/* Add input char to the content. */
content[i++] = c;
/* If the character is a break of line
* then the counter will be incremented.
*/
if (c == '\n')
counter++;
}
/* Test if loop terminated from error. */
if (ferror(input)) {
fprintf(stderr, "There was a file input error.\n");
free(content);
fclose(input);
exit(EXIT_FAILURE);
}
/* Make the content a bona-fide string. */
if (i == buffer_size) {
buffer_size += 1;
if ((temp = realloc(content, buffer_size)) == NULL) {
fprintf(stderr, "Ran out of core (and only needed one more byte too ;_;).\n");
fclose(input);
free(content);
exit(EXIT_FAILURE);
}
content = temp;
}
content[i] = '\0';
/* Assigns the variables to the corresponding
* element of the struct.
*/
fr->file_path = file_path;
fr->content = content;
fr->n_lines = counter;
fr->n_characters = i;
/* Clean up. */
free(content);
fclose(input);
return 0;
}
file_reader.h
#ifndef FILE_READER_H_
#define FILE_READER_H_
typedef struct FileReader
{
char *content; // holds the file content.
char *file_path; // holds the file path.
int *n_lines; // holds the number of lines.
int *n_characters; // holds the number of characters.
} FileReader;
// file_reader_new - reads the file
int file_reader_new(const char *file_path, struct FileReader *fr);
#endif
file_writer.c
#include <stdio.h>
#include "file_writer.h"
void write (struct FileWriter *fw)
{
FILE *f = fopen(fw->file_path, "w");
if (f == NULL)
{
printf("Error opening file!\n");
exit(1);
}
fprintf(f, "%s", fw->content);
fclose(f);
}
file_writer.h
#ifndef FILE_WRITER_H_
#define FILE_WRITER_H_
typedef struct FileWriter
{
char *file_path;
char *content;
int *error;
} FileWriter;
#endif
Can you help me ? Thanks!
struct FileReader *fr = malloc(sizeof(struct FileReader));
There is no need to do this. All you need is this:
struct FileReader fr;
Same here:
struct FileWriter fw;
Then just pass the address of these variables to the requisite function(s).
Note this was not given to you as an answer, only as a comment to clean up your code a bit to remove extraneous calls to the heap. It just so happens that the real problem exists elsewhere, and what you're seeing here is undefined behavior in full glory.
I am not sure how are you reading from the file, character by character or block, but anyhow ,
since you update the read data in content buffer, and store the address of content buffer inside file_reader_new() into variable fr->content and immediately releasing the memory will end up loosing the data you read. and lead to condition called Dangling pointer
Dangling pointer
( a pointer variable, which points to a released memory )
that's why its always advised to set the pointer variable after releasing to NULL. Dereferencing a dangling pointer is will lead to Segmentation fault or undefined behavior in some scenarios.
Also, since all you member variables of struct are pointers its better to initialize them to NULL.
you can use calloc to initialize all the variables in a struct, instead of malloc to initialize all the members to NULL, if you are going with dynamic allocation. which goes for string also.
Here is an issue that I see:
fr->content = content;
fr->n_lines = counter;
fr->n_characters = i;
/* Clean up. */
free(content); /* <-- Danger */
You do this in your file_reader_new function. You then call show_file_reader_values and in that function, you're accessing content:
printf("content:\n%s\n", fr->content);
Since you called free() on the content, that pointer no longer points to valid memory, thus undefined behavior occurs.
The fix is to allocate space on fr for the content and copy the characters of content to this space, or simply not call free on content.
So either do this:
fr->content = malloc(i + 1);
strcpy(fr->content, content);
fr->n_lines = counter;
fr->n_characters = i;
/* Clean up. */
free(content);
or this:
fr->content = content;
fr->n_lines = counter;
fr->n_characters = i;
/* No call to free(content) done */

pointer problems when trying to build a directory tree in memory

Problem 1: what's the best data structure to save the directory structure?
Problem 2: I have tried to use a general tree to solve it, but there are a lot of problems:
The number of files under a directory is not certain. So the number of child nodes under a tree node is also not certain. and I try to add a keyword nchild to each node, showing nchild child nodes. so there are nchild pointers (saved with **child) to the child nodes. And once that, **child and *child should be dynamically allocated space with no certain child nodes. So you know, this is really difficult to release these spaces(and the program below is not called free()). Is there a better way to solve it?
And sometimes the program below would get the garbage characters when I output the directory tree, which make me really confused. while debugging it, found that is the function ent=readdir(pDir); has read garbage characters. But when I write another simple program to read the same directory, that goes well. I think the problem is the recursive function, but I didn't get any idea. I will be appreciated if some one can give me a idea. Thanks!
```
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
typedef struct tree_file_s
{
char path[512];
time_t date;
char type;
long size;
int nchild;
struct tree_file_s **child;
} tree_file_t;
int dir_child_len(const char *dir)
{
int nchild = 0;
DIR *pDir;
struct dirent *ent;
pDir = opendir(dir);
while((ent=readdir(pDir)) != NULL)
{
if (strcmp(ent->d_name, ".")==0 || strcmp(ent->d_name, "..")==0)
{
continue;
}
nchild++;
}
return nchild;
}
void tree_create(tree_file_t *tft, const char *dir)
{
int nchild; // the tft has n child
DIR *pDir;
struct dirent *ent; // the directory dir dirent info
struct stat file_stat; // the new file's stat info
stat(dir, &file_stat);
nchild = dir_child_len(dir);
pDir = opendir(dir);
// Initialize the parent
//tft->path = calloc(1, strlen(dir)+1);
strcpy(tft->path, dir);
tft->date = file_stat.st_mtime;
tft->type = 'D';
tft->size = file_stat.st_size;
tft->nchild = nchild;
tft->child = calloc(1, nchild);
nchild = 0;
while ((ent=readdir(pDir)) != NULL)
{
if (ent->d_type & DT_DIR)
{
if (strcmp(ent->d_name, ".")==0 || strcmp(ent->d_name, "..")==0)
{
continue;
}
tree_file_t *new_dir = calloc(1, sizeof(tree_file_t));
tft->child[nchild] = new_dir;
char *new_path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
sprintf(new_path, "%s/%s", dir, ent->d_name);
tree_create(new_dir, new_path);
free(new_path);
} else {
tree_file_t *new_file = calloc(1, sizeof(tree_file_t));
char *new_path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
// new_file->path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
sprintf(new_path, "%s/%s", dir, ent->d_name);
stat(new_path, &file_stat);
strcpy(new_file->path, new_path);
free(new_path);
new_file->date = file_stat.st_mtime;
new_file->type = 'F';
new_file->size = file_stat.st_size;
new_file->nchild = 0;
new_file->child = 0;
tft->child[nchild] = new_file;
}
//free(new_path);
//new_path = 0;
nchild++;
}
}
void display_tree(tree_file_t *tft)
{
int nchild, i;
nchild = tft->nchild;
printf("%c: %s\n", tft->type, tft->path);
for(i = 0; i < nchild; i++)
{
if(tft->child[i]->type == 'F')
{
printf("%c: %s\n", tft->child[i]->type, tft->child[i]->path);
} else {
display_tree(tft->child[i]);
}
}
}
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: a.out dir\n");
exit(0);
}
char dir[512];
strcpy(dir, argv[1]);
tree_file_t *tft = calloc(1, sizeof(tree_file_t));
tree_create(tft, dir);
display_tree(tft);
return 0;
}
```
When you allocate space for new_path you need to add 2 (one for the slash, one for the null terminator). And you never close the directories you open (use closedir()).
An even more serious error is this line:
tft->child = calloc(1, nchild);
which only allocates nchild bytes, not enough to hold nchild pointers! Try:
tft->child = calloc(nchild, sizeof(*tft->child));

Resources