GSList of structs - c

I read this thread, which helped me figure out dereferencing properly, but then I went and created exactly the situation that post's answer's author said to avoid, haha.
What I'm trying to accomplish is the creation of a basic file browser (per the book I'm reading). The code below is supposed to be reading through the directory contents and filling the details I've chosen into a struct. That struct is then appended as the data member of a GSList. That list is then used to populate row data for a GtkTreeView, and so forth.
typedef struct
{
gchar *name, *size, *date_modified;
}FileProperties;
//...
static void refresh_directory_listing(GtkTreeView *treeview)
{
GtkListStore *store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
GSList *files = NULL;
GtkTreeIter iter;
get_current_directory_contents(&files);
for(GSList *current = files; current != NULL; current = g_slist_next(current))
{
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, FILE_NAME, ((FileProperties *) current->data)->name,
FILE_SIZE, ((FileProperties *) current->data)->size,
DATE_MODIFIED, ((FileProperties *) current->data)->date_modified, -1);
}
gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
g_object_unref(store);
g_slist_free(files);
}
static void get_current_directory_contents(GSList **files)
{
GDir *current_dir = g_dir_open(g_get_current_dir(), 0, NULL);
gchar *file_name = NULL;
FileProperties *file = g_malloc(sizeof(FileProperties));
while((file_name = (gchar *) g_dir_read_name(current_dir)))
{
memset(file, 0, sizeof(FileProperties));
file->name = g_strdup(file_name);
file->size = g_strdup_printf("Nope");
file->date_modified = g_strdup_printf("Nuh uh");
*files = g_slist_append(*files, file);
}
g_free(file);
}
Working to understand why my file names return blank and everybody has the same memory address, it's obvious to me that g_slist_append() is handing the FileProperties structure over directly. So, everything references the same struct that keeps getting overwritten and eventually freed. Whoops.
My question is this: what would be the appropriate way to hand a GSList of struct's back and forth between functions?

You are only allocating one FileProperties structure before the while loop, and then you just change the contents of that single struct, and append it many times.
You need to allocate one FileProperties structure instance for each file whose properties you want to store.
Move the allocation inside the loop, replacing the (spurious) memset():
while((file_name = (gchar *) g_dir_read_name(current_dir)))
{
FileProperties *file = g_malloc(sizeof *file);
/* rest of loop here ... */
}

Related

Saving (and reading) Linked List (that containts dynamic strings) Into Binary File In C

I'm trying to read/write linked list into a binary file, the problem is that this list contains
dynamically allocated strings and when I try to read from the file the linked list, I get in the string field, address instead of the string value.
Someone knows maybe what is the problem?
void saveProject(FrameNode* headFrame, char* filePath)
{
FILE* projectFile = fopen(filePath, WRITING_MODE);
FrameNode* currentFrame = headFrame;
if (projectFile) // making sure the fopen() didn't failed
{
while (currentFrame != NULL) // making sure the list isn't empty
{
fseek(projectFile, 0, SEEK_END); // writing the node to the end of the file
// writing the currentNode into the file and returning the value of ->"next"
fwrite(currentFrame->frame, sizeof(Frame), 1, projectFile);
currentFrame = currentFrame->next; // moving to the next frame
}
fseek(projectFile, 0, SEEK_SET); // returning the seek of the file to the start
fclose(projectFile);
}
}
void openProject(FrameNode** headFrame, char* filePath)
{
FILE* projectFile = fopen(filePath, READING_MODE);
FrameNode* currentFrame = *headFrame;
int numOfFrames = 0;
int i = 0;
if (projectFile) // making sure the fopen() function didn't failed
{
// making sure the headFrame doesn't point to existing list
dealloc_linked_list(*headFrame);
*headFrame = NULL;
// finding the number of nodes (=frames) in the projectFile file
fseek(projectFile, 0, SEEK_END);
numOfFrames = (int)(ftell(projectFile) / sizeof(Frame));
fseek(projectFile, 0, SEEK_SET);
for (i = 0; i < numOfFrames; i++)
{
// reading the next frame in the list
fseek(projectFile, sizeof(Frame) * i, SEEK_SET);
addFrameFromFile(headFrame, projectFile);
}
}
fclose(projectFile);
}
void addFrameFromFile(FrameNode** headFrame, FILE* projectFile)
{
FrameNode* newFrame = NULL;
if (*headFrame == NULL) // in case the list is empty
{
*headFrame = (FrameNode*)malloc(sizeof(FrameNode));
newFrame = *headFrame;
}
else // if the list isn't empty, the function will search for the last node in the list
{
newFrame = findLastFrame(*headFrame);
newFrame->next = (FrameNode*)malloc(sizeof(FrameNode));
newFrame = newFrame->next;
}
// adding the data from the file to the newFrame
newFrame->frame = (Frame*)malloc(sizeof(Frame));
fread(newFrame->frame, sizeof(Frame), 1, projectFile);
newFrame->next = NULL; // making the frame be the last in the list
}
And This is the linked list node structs:
// the content
typedef struct Frame
{
char* name;
unsigned int duration;
char* path;
} Frame;
// Link (node) struct
typedef struct FrameNode
{
Frame* frame;
struct FrameNode* next;
} FrameNode;
When I try to read the file, I get the next output:
click me to see the screenshot of the output
The first printing is the original list that I wrote into the file, and the second is the list I created from the file using the openProject() function.
Thanks in advance
Regarding;
typedef struct Frame
{
char* name;
unsigned int duration;
char* path;
} Frame;
and
fwrite(currentFrame->frame, sizeof(Frame), 1, projectFile);
the call to fwrite() is outputting the contents of an instance of Frame However, that does NOT output the data to be found where the pointers point.
Also, after writing out those pointers, then trying read them back in, the pointers are now meaningless.
Suggest modify the typedef ... Frame to actually contain the data rather than pointers to the data.
Suggest:
typedef struct Frame
{
char name[ MAX_NAME_LEN ];
unsigned int duration;
char path[ MAX_PATH_LEN ];
} Frame;
The will also eliminate the calls to malloc() and family, except for obtaining an instance of Frame
The fwrite() function is used to write the binary values from the active RAM in your program to a file, so when you call fwrite(currentFrame->frame,sizeof(Frame),1,projectFile) you're asking the system to copy sizeof(Frame) (times one) bytes from the memory pointed to by currentFrame->frame into your file.
In your setup, a Frame has two char* in it, but fwrite does not care about this. The fwrite() function will just treat these pointers like any other binary value, and copy the memory addresses, which will soon be incorrect, into the file.
If you want to save the important parts of your linked list into a file so you can reproduce a linked list with the same data in the future you need to actually copy the contents of the strings into the file, like so:
void saveProject(FrameNode* headFrame, char* filePath)
{
FILE* projectFile = fopen(filePath, WRITING_MODE);
FrameNode* currentFrame = headFrame;
if (projectFile == NULL) // making sure the fopen() didn't failed
return;
while (currentFrame != NULL) // making sure the list isn't empty
{
fprintf(projectFile,"%s%d%s",currentFrame->frame->name,currentFrame->frame->duration,currentFrame->frame->path);
currentFrame = currentFrame->next; // moving to the next frame
}
fclose(projectFile);
}
fprintf() works just like printf, except it writes to a file instead of stdout, so it will write the strings into the file not the pointers that point to them.
Note that all the fseeks in your code are unnecessary as the file cursor is automatically moved as you write to the file.
To produce the new linked list again from the file you can use fscanf() in a loop, but make sure you allocate memory for your strings when you copy them from the file.

Trying to copy char array to struct crashing

(to preface this my C is terrible)
I'm trying to send a string from iOS to a BLE device. I encode the string in swift and write it like this:
func sendUserName(userName: String) {
let bytes: [UInt8] = Array(userName.utf8)
print(bytes.count)
let data = NSData(bytes: bytes, length: bytes.count)
capsenseLedBoard!.writeValue(data, forCharacteristic: userIdCharacteristic, type: CBCharacteristicWriteType.WithResponse)
}
I send in this string "THISISATEST123456789" and this line print(bytes.count) prints out 20.
I recieve the data on the BLE device like this and pass it to the below userDidConnect function:
userDidConnect((char *)wrReqParam->handleValPair.value.val);
I have a struct called Event that looks like this:
struct Event {
char time[20]; // The time in ISO 1601 format
char name[3]; // The two character name of the event. See header for declarations.
char userId[20]; // The userId of the connected user if one is present.
struct Event* next;
};
I have a global variable declared like this:
char currentlyConnectedUserID[20];
I then have an enqueue function that looks like this:
/**
Creates a new Event and adds to the linked list.
#param time The time in ISO 8601 format.
#param name The name descriptor of the event ("VS", "VO", etc.)
#param userId The id of the user who is currently connect (if they are connected).
*/
void enqueueEvent(char time[20], char name[3], char userId[20]) {
struct Event* temp = (struct Event*)malloc(sizeof(struct Event));
strncpy( temp->time, time, 20);
strncpy( temp->name, name, 3);
strncpy( temp->userId, userId, 20);
temp->next = NULL;
if(front == NULL && rear == NULL) {
front = rear = temp;
return;
}
rear->next = temp;
rear = temp;
}
I have a function that accepts a new userId and then creates a new Event off of it and adds it to the linked list..well this is what it's suppose to do:
void userDidConnect(char *userId)
{
size_t destination_size = sizeof(userId);
snprintf(currentlyConnectedUserID, destination_size, "%s", userId);
//enqueueEvent("2007-03-01T13:00:20", "UC", currentlyConnectedUserID);
showMessageInUART(currentlyConnectedUserID, sizeof(currentlyConnectedUserID));
}
Currently if I run the userDidConnect method above I'm able to printout the currentlyConnectedUserID properly. However, if I uncomment out this line:
//enqueueEvent("2007-03-01T13:00:20", "UC", currentlyConnectedUserID);
I get a "crash". I'm doing this in a fairly obscure IDE (PSoC Creator from Cypress) so I don't see any error logs or IDE crash logs. The only way I can tell is that the showMessageInUART is never called, so I know it has to be that line.
I'm able to successfully create and enqueue a new Event if I do this:
enqueueEvent("2007-03-01T13:00:20", "UC", "1234567891234567891");
My only thought is that maybe the size of the array is wrong? Maybe? Or perhaps there is some trailing \0 that is screwing things up?
Suggestion updates:
I've tried doing this:
size_t destination_size = strlen(userId) + 1;
Which gives the correct value into currentlyConnectedUserID however enqueueing still causes a crash.
--
I've replaced strcpy with strncpy which is still causing a crash ;(
--
Tried this to ensure I didn't overflow which still didn't work:
sprintf(currentlyConnectedUserID, "%.19s", userId);
UPDATE
I updated my enqueue to look like this since don't have breakpoints:
void enqueueEvent(char time[20], char name[3], char userId[20]) {
UART_UartPutString("start enqueue");
struct Event* temp = (struct Event*)malloc(sizeof(struct Event));
UART_UartPutString("1");
strncpy( temp->time, time, 20);
UART_UartPutString("2");
strncpy( temp->name, name, 3);
UART_UartPutString("3");
strncpy( temp->userId, userId, 20);
UART_UartPutString("4");
temp->next = NULL;
UART_UartPutString("5");
if(front == NULL && rear == NULL) {
front = rear = temp;
return;
}
rear->next = temp;
rear = temp;
}
This line is crashing:
strncpy( temp->time, time, 20);
aka we never make it here: UART_UartPutString("2");
If I call this same function from main it works fine. Any idea why it would be crashing here when called from a different method?
The strcpy funtion Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
Therefore, I propose you to change the enqueueEvent funtion, using strncpy instead of dangerous strcpy as :
void enqueueEvent(char time[20], char name[3], char userId[20]) {
struct Event* temp = (struct Event*)malloc(sizeof(struct Event));
strncpy( temp->time, time,20);
strncpy( temp->name, name,3);
strncpy( temp->userId, userId,20);
temp->next = NULL;
if(front == NULL && rear == NULL) {
front = rear = temp;
return;
}
rear->next = temp;
rear = temp;
}
Change also the allocation of temp pointer from local stack of the enqueueEvent function to global level because the pointer allocation is vanished when going outside of the function.

Memory issue by loading struct-data with fread from binary file and load in a gtk liststore in C

I have a memory issue in my GTK programm and I don't know how to fix it.
The data in the liststore of my programm saved with fwrite in a binary file when I close the programm. The code seems to work:
void on_window_destroy (GtkWidget *object, gpointer user_data)
{
gint i;
GtkTreeIter iter;
GtkTreeModel *model = GTK_TREE_MODEL(gtk_builder_get_object (builder,"liststore"));
gint n_rows = gtk_tree_model_iter_n_children( model, NULL ); //count the rows
FILE *pfile = fopen("Data", "wb" );
gtk_tree_model_get_iter_first (model, &iter);//set iter postion to the first row
data *pdata = malloc(sizeof(data));
for (i = 0 ; i < n_rows; i++)
{
gtk_tree_model_get (model, &iter,
SPALTE_ArtName, &pdata->ArtBez,
SPALTE_ArtNr, &pdata->ArtNr,
SPALTE_LBest, &pdata->LBest,
-1);
//just to check the data
g_printf("Zeile %d: %s | %d | %d\n",i, pdata->ArtBez, pdata->ArtNr, pdata->LBest);
fwrite(pdata,sizeof(data),1,pfile);
gtk_tree_model_iter_next (model, &iter); //iter = next row
}
free(pdata);
fclose(pfile);
g_printf("Saved successfully!\n\n");
gtk_main_quit();
}
After this when I start the program again it should read the binary file with fread and add the data in the empty liststore
I tried this like this:
data *pdata = malloc (sizeof(data));
FILE *pfile = fopen("Data","rb");
if (pfile == NULL)
{
g_printf("Error: Data File not Found! Creating new list....\n");
}
else
{
while (fread (pdata,sizeof(data),1,pfile))
{
g_printf("Test 1 \n\n\n");
gtk_list_store_append(GTK_LIST_STORE(model), &iter); //add new row
g_printf("Test 1 \n\n\n");
gtk_list_store_set (GTK_LIST_STORE(model), &iter,
SPALTE_ArtName,pdata->ArtBez,
SPALTE_ArtNr,pdata->ArtNr,
SPALTE_LBest,pdata->LBest,
-1);
}
free(pdata);
fclose(pfile);
}
Here is the data struct used from pdata:
typedef struct _data
{
gchar *ArtBez;
gint *ArtNr;
gint *LBest;
}data;
The issue must has to do with the pdata->ArtBez because when I tab it out it works fine.
EDIT:
After I red the answer from stark (Thank you so much!) I changed the "data" struct to a struct without Pointers
typedef struct _data
{
gchar ArtBez[128];
gint ArtNr;
gint LBest;
}data;
Then I changed the write function from above to:
void on_window_destroy (GtkWidget *widget, gpointer user_data)
{
gint i, *BufArtNr, *BufLBest;
gchar *BufArtBez;
GtkTreeIter iter;
GtkTreeModel *model = GTK_TREE_MODEL(gtk_builder_get_object (builder, "liststore")); //Hole liststore aus glade-Datei
gint n_rows = gtk_tree_model_iter_n_children( model, NULL ); //count rows
FILE *pfile = fopen("Data", "wb" );
gtk_tree_model_get_iter_first (model, &iter); //Zeiger auf erste Zeile setzen
data *pdata = malloc(sizeof(data)*n_rows);
for (i = 0 ; i < n_rows; i++) //For every row
{
gtk_tree_model_get (model, &iter, //Get data from row
SPALTE_ArtNr, &BufArtNr,
SPALTE_ArtBez, &BufArtBez,
SPALTE_LBest, &BufLBest,
-1);
//Schreibe Daten in Struct
pdata[i].ArtNr = BufArtNr;
strcpy(pdata[i].ArtBez, BufArtBez);
pdata[i].LBest = BufLBest;
fwrite(&pdata[i],sizeof(data),1,pfile); //Write data to file
gtk_tree_model_iter_next (model, &iter); //Next row
}
free(pdata);free(BufArtBez);
fclose(pfile);
g_printf("Saved successfully\n\n");
}
That works, but when I compile the program, the compiler gets me a warning:
warning: assignment makes integer from pointer without a cast [enabled by default]
pdata[i].ArtNr = BufArtNr;
Same warning for pdata[i].LBest = BufLBest;
Where is my mistake?
pdata only contains pointers, which are not useful to save and restore. Instead of
fwrite(pdata,sizeof(data),1,pfile);
you need to do
fwrite(pdata->ArtBez,...
fwrite(pdata->ArtNr,...
fwrite(pdata->LBest,...
and then build a new structure when you read them back.

Problems with drag and drop in Gtk

I'm building an application that uses drag and drop using GTK in C language but I have a problem with this part of code
void view_onDragDataReceived(GtkWidget *wgt, GdkDragContext *context, int x, int y,GtkSelectionData *seldata, guint info, guint time,gpointer userdata)
{
GtkTreeModel *model;
GtkTreeIter iter;
model = GTK_TREE_MODEL(userdata);
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, COL_URI,(gchar*)seldata->data, -1);
pathh=(char*)seldata->data;
}
I call this Function from this line of code
g_signal_connect(view, "drag_data_received",G_CALLBACK(view_onDragDataReceived), liststore);
the problem i'm having is that when i try to use that pathh variable in an other function i find it empty even though it's declared a global variable type char*
Try this way
struct SelectionData
{
GtkListStore *listStore;
gchar *path;
}
and then in the caller function
struct SelectionData *data;
data = malloc(sizeof(*data));
if (data == NULL)
handleMallocFailureAndPleaseDoNotContinue();
data->listStore = liststore;
data->path = NULL;
g_signal_connect(view, "drag_data_received",G_CALLBACK(view_onDragDataReceived), data);
and then
void view_onDragDataReceived(GtkWidget *wgt, GdkDragContext *context, int x, int y,GtkSelectionData *seldata, guint info, guint time, gpointer userdata)
{
GtkTreeModel *model;
GtkTreeIter iter;
SelectionData *selectionData;
size_t length;
selectionData = (SelectionData *)userdata;
model = GTK_TREE_MODEL(selectionData->liststore);
gtk_list_store_append(GTK_LIST_STORE(model), &iter);
gtk_list_store_set(GTK_LIST_STORE(model), &iter, COL_URI,(gchar*)seldata->data, -1);
length = strlen((char*)seldata->data);
if (selectionData->path != NULL) /* for subsequent calls */
free(selectionData->path);
selectionData->path = malloc(1 + length);
if (selectionData->path != NULL)
memcpy(selectionData->path, seldata->data, 1 + length);
}
and then you can access selectionData somewhere else and thus it's path field, which must be freed after you finish using it.
Try to avoid global variables as much as possible.
You can also use the same technique to copy seldata->data into your pathh global variable, but rethink your design and try not to use pathh global variable, since you are going to use malloc to allocate space for the string, and the global variable will be a pointer to it, so it will be very dificult to understand where and how you must free it.

Data structure pointer handling in C

I have a struct like this;
struct abc
{
char bbb[10];
char ccc[4];
char ddd[6];
int i1;
int i2;
struct abc *prior, *next;
};
struct abb *start, *last, *this, *temp;
I have a lot of code using these pointers start,last, etc. so I would like to use them but I would like to add pointers to the struct and pointers within the struct to accomplish the following:
load the struct up with data then depending on the value of i1 for example, display and change data when the value of i1 = 0 or when the value of i1 = 1 or the contents of the struct regardless of the value of i1. And, at the end of the day save the whole struct to file with changes made in any of the three conditions.
I thought to have added pointers such as these:
struct abc *prior1, *next1;
struct abc *prior2, *next2;
};
struct abb *start1, *last1...etc.
struct abb *start2, *last2...etc.
I can have:
start = start1;
last = last1;
But how then do I reference
prior1
next1
Or tell me a better way of doing this.
It sounds like you're asking for advice on a serialization strategy. I'd suggest writing free operator<< and operator>> methods for handling I/O on your struct, where the pointers are referenced in a map that assigns an ascending numeric ID value to each. When writing, upon visiting each struct pointer, you check the map, and if the ID is found, you simply write out the ID and nothing else. Otherwise, you add the next available ID to the map, assign the ID to the pointer, and then write out the entire struct. When reading, you reverse the process, reading in each ID and looking in the map for that pointer. If it's not found, you have the first instance, so you proceed to read in the entire struct and store it in the map. Otherwise, you retrieve the previously read struct from the map and assign the pointer. This general strategy is used by CArchive serialization (Microsoft Foundation Classes). There may be some open source libraries to do the same.
Your question is not very clear.
You might just need to check out C pointers, and such syntax as start->prior which references the value of prior contained within start.
E.g.
// Set the i1 value of start to 42
start->i1 = 42;
printf("The answer is: %d\n", start->i2);
Or you might want to have the equivalent of methods to interact with one of your structures.
For example:
struct abc
{
char bbb[10];
char ccc[4];
char ddd[6];
int i1;
int i2;
struct abc *prior, *next;
};
struct abb *start, *last, *this, *temp;
/**
* Set the BBB of struct target to a given value.
* #param value the value to set
* #return pointer to the target
*/
struct abc *setBBB(struct abc *target, char *value)
{
strncpy(target->bbb, value, sizeof(target->bbb));
return target;
}
char *getBBB(struct abc *target)
{
return target->bbb;
}
struct abc *next(struct abc *target)
{
return target->next;
}
int getI1(struct abc *target)
{
return target->i1;
}
to accomplish the following: load the struct up with data then
depending on the value of i1 for example, display and change data when
the value of i1 = 0 or when the value of i1 = 1 or the contents of the
struct regardless of the value of i1.
This you would do with:
switch(getI1(start))
{
case 0:
setBBB(start, "Hello"); // NOTE: to treat this as a string,
// at most 9 characters are allowed,
// else the terminating zero will be lost.
break;
case 1:
setBBB(start, "World"); // ditto.
break;
...
}
And, at the end of the day save the whole struct to file with changes made in any of the three conditions.
You want to save the structure to a file, you need something like
int saveStruct(char *filename, int position, struct abc *target)
{
int ret;
FILE *fp;
if (NULL == (fp = fopen(filename, "r+")))
{
if (NULL == (fp = fopen(filename, "w")))
{
return -1; // Error.
}
}
fseek(fp, position * sizeof(struct abc), SEEK_SET);
ret = fwrite(target, sizeof(struct abc), 1, fp);
fclose(fp);
return ret;
}
The above has some problems, though. The values of pointers (*prior and *next) will become meaningless once saved to disk, and will remain so when loaded from disk. So the loadStruct() function will require as additional parameters the values of *prior and *next to be restored.
Also, you can't (but it depends on the platform) save a structure at position #1 if you haven't saved one at position #0 before. And each save performs an expensive open-and-close.
int loadStruct(char *filename, int position, struct abc *target)
{
int ret;
FILE *fp;
if (NULL == (fp = fopen(filename, "r")))
{
return -1; // Error.
}
fseek(fp, position * sizeof(struct abc), SEEK_SET);
ret = fread(target, sizeof(struct abc), 1, fp);
target->prior = NULL;
target->next = NULL;
fclose(fp);
return ret;
}
// The above, tested with the test case below, returns 'Hello!' as expected.
int main()
{
struct abc *start, *another;
start = malloc(sizeof(struct abc));
setBBB(start, "Hello!");
saveStruct("file.bin", 0, start);
another = malloc(sizeof(struct abc));
loadStruct("file.bin", 0, another);
printf("Re-read: %s.\n", getBBB(another));
}

Resources