Dynamic reallocation an array of structures with malloc and realloc - c

I am really having a difficult time trying to create an array of structures using malloc and realloc. I have posted mostly the entire code base, or at least the relevant information pertaining to the question below.
struct _section {
char *sectName;
int start_addr;
int end_addr;
char *bytes;
};
struct _section *get_exe_sections(struct _section *exe_sect, Elf *elf, GElf_Ehdr *ehdr, GElf_Shdr *shdr, Elf_Data *data) {
exe_sect->sectName = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name);
exe_sect->start_addr = shdr->sh_addr;
exe_sect->end_addr = shdr->sh_addr + shdr->sh_size;
exe_sect->bytes = (unsigned char *)data->d_buf;
return exe_sect;
}
int main(int argc, char *argv[]) {
Elf *elf;
int fd;
//process input file
int sections_count = count_sections(elf);
GElf_Ehdr ehdr_mem;
GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_mem);
struct _section *exe_sect = (struct _section *)malloc(sizeof(struct _section));
for(int cnt = 0; cnt < sections_count; cnt++) {
Elf_Scn *scn = elf_getscn(elf, (size_t)cnt);
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr(scn, &shdr_mem);
Elf_Data *data = elf_getdata(scn, NULL);
if(ehdr == NULL || shdr == NULL)
exit(1);
if(strcmp(header_name(SECT_TYPE, GELF_ST_TYPE(shdr->sh_type)), "PROGBITS") == 0) {
if(strcmp(flag_name(SECT_FLAGS, shdr->sh_flags), "ALLOC & EXECUTE") == 0 || \
strcmp(flag_name(SECT_FLAGS, shdr->sh_flags), "EXECUTE") == 0) {
exe_sect = get_exe_sections(exe_sect, elf, ehdr, shdr, data);
struct _section *nxt_sect = (struct _section *)realloc(exe_sect, 2*sizeof(*exe_sect));
if(nxt_sect != NULL)
exe_sect = nxt_sect;
}
}
}
return 0;
}
What I am having trouble with is creating an array of structures dynamically and using malloc and realloc to resize the structure to fit more data into it. If I were to place a handful of print statements are the bottom of main the output will give me the last data that was input to the structure. How would I go about accessing individual entries that were made during each call to get_exe_section? From prior posts and other resources I thought this would work, but I cannot create an array in this manner. Any form of help would be great. Thanks.

You could add another element in your struct that points to the next section. This way you could create a linked list
struct _section {
char *sectName;
int start_addr;
int end_addr;
char *bytes;
struct _section *next; // pointer to next section
};
You could use another Struct to point to the head of your list.
and then instead of using realloc. you could just do
exe_sect->next = (struct _section *)malloc(sizeof(struct _section));
Here is how I would change the main function :
int main(int argc, char *argv[]) {
Elf *elf;
int fd;
//process input file
int sections_count = count_sections(elf);
GElf_Ehdr ehdr_mem;
GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_mem);
struct _section *exe_sect = (struct _section *)malloc(sizeof(struct _section));
for(int cnt = 0; cnt < sections_count; cnt++) {
Elf_Scn *scn = elf_getscn(elf, (size_t)cnt);
GElf_Shdr shdr_mem;
GElf_Shdr *shdr = gelf_getshdr(scn, &shdr_mem);
Elf_Data *data = elf_getdata(scn, NULL);
if(ehdr == NULL || shdr == NULL)
exit(1);
if(strcmp(header_name(SECT_TYPE, GELF_ST_TYPE(shdr->sh_type)), "PROGBITS") == 0) {
if(strcmp(flag_name(SECT_FLAGS, shdr->sh_flags), "ALLOC & EXECUTE") == 0 || \
strcmp(flag_name(SECT_FLAGS, shdr->sh_flags), "EXECUTE") == 0) {
exe_sect = get_exe_sections(exe_sect, elf, ehdr, shdr, data);
exe_sect->next = (struct _section *)malloc(sizeof(struct _section));
if(exe_sect->next != NULL)
exe_sect = exe_sect->next;
}
}
}
return 0;
PS : To access all the individual entries, add another struct that consists of a pointer to the head of the list, and a variable that keeps count.

Related

Why does the second malloc fail in this scenario?

I am currently working on a piece of code where we are parsing a file and working with different functions. Through debugging with printf calls, I have found that I am getting a memory error on the second malloc call. What could cause the second malloc to fail in this rough skeleton?
struct example {
char *myChar;
int myInt;
};
struct example *doThing(FILE *file) {
struct example *myToken = (struct example *)malloc(sizeof(struct example));
char buffer[32] = "";
// other stuff
if (strncmp(buffer, "random", 6) == 0) {
strncpy(myToken->myChar, buffer, 6);
myToken->tokenid = 1;
return myToken;
}
return NULL;
}
struct example *doThing2(FILE *file) {
struct example *myOtherToken = (struct example *)malloc(sizeof(struct example));
// other stuff
return myOtherToken;
}
int main() {
FILE *ofp = fopen("thefile.txt", "r");
struct example *token1, *token2;
token1 = doThing(ofp);
token2 = doThing2(ofp);
// other stuff
free(token1);
free(token2);
return 0;
}
You are facing memory leak.
correct your code folowing one of the two samples bellow
and yes, as #Eugene_Sh mentioned, you should allocate memory for myToken->myChar and do not forget to free it before freeing myToken
SAMPLE 1
struct example* doThing(FILE *file) {
char buffer[32] = "";
// other stuff
if (strncmp(buffer, "random", 6) == 0) {
struct example *myToken = (struct example *) malloc(sizeof(struct example));
myToken ->myChar= malloc(7);
strncpy(myToken ->myChar, buffer, 6);
myToken ->myChar[6]=0;
myToken->tokenid = 1;
return myToken;
}
return NULL;
}
SAMPLE 2
struct example* doThing(FILE *file) {
struct example *myToken = (struct example *) malloc(sizeof(struct example));
char buffer[32] = "";
// other stuff
if (strncmp(buffer, "random", 6) == 0) {
myToken ->myChar= malloc(7);
strncpy(myToken ->myChar, buffer, 6);
myToken ->myChar[6]=0;
myToken->tokenid = 1;
return myToken;
}
free(myToken );
return NULL;
}

segmentation fault in a linked list while loop?

I'm trying to setup a graph in C. I tried the graph with user input and it works perfectly. However, i am trying to implement a read from file. The last else statement is where the error is coming from because when i commented it out it compiles without any problems. I have included a comment over the block i think that has the problem. Please let me know if there is anything else needed for this question.
#include <stdio.h>
#include <stdlib.h>
struct node{
int data;
struct node* next;
};
//int counter and mainVertex would be used to determine if graph is connected.
// void graphConnection(){
//
//
//
//
//
//
// }
char* deblank(char* input)
{
int i,j;
char *output=input;
for (i = 0, j = 0; i<strlen(input); i++,j++)
{
if (input[i]!=' ')
output[j]=input[i];
else
j--;
}
output[j]=0;
return output;
}
struct node *G[1000];
int counter = 0;
char *mainVertex;
void readingEachLine(){
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
//Read file and exit if fail
fp = fopen("test.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1) {
line = deblank(line);
int i = 0;
struct node* cursor = malloc(sizeof(struct node));
struct node* secondcursor = malloc(sizeof(struct node));
struct node* tempitem;
while(line[i] != '\n'){
//If its the first of the line look into the array and set struct cursor to the corresponding
//array position
if (i == 0){
mainVertex[counter] = line[0];
int convertor = line[i] - '0';
cursor = G[convertor];
counter++;
}
//if its not the first, then set a struct with that number as data
else{
tempitem = malloc(sizeof(struct node));
int convertor = line[i] - '0';
tempitem->data = convertor;
tempitem->next = NULL;
}
//if there is no element connected to the struct in array, connect the tempitem
if (cursor->next == NULL){
cursor->next = tempitem;
}
//If there are already connected elements, loop until the end of the linked list
//and append the tempitem
//ERROR: I GET SEGMENTATION FAULT FROM HERE. TRIED AFTER COMMENTING IT OUT
else{
secondcursor = cursor;
while(secondcursor->next != NULL){
secondcursor = secondcursor->next;
}
secondcursor->next = tempitem;
}
i++;
}
printf("\n");
}
}
int main(void){
for (int i = 1; i < 1000; i++)
{
G[i]= malloc(sizeof(struct node));
G[i]->data = i;
G[i]->next = NULL;
}
readingEachLine();
}
EDIT: This is how the text file looks like:
1 3 4
2 4
3 1 4
4 2 1 3
Your code has several misconceoptions:
Apparently, you can have a maximum of 1,000 nodes. You have an array G of 1,000 head pointers to linked lists. Don't allocate memory for all 1,000 nodes at the beginning. At the beginning, all lists are empty and an empty linked list is one that has no node and whose head is NULL.
In your example, cursor is used to iterate oer already existing pointers, so don't allocate memory for it. If you have code like this:
struct node *p = malloc(...);
// next use of p:
p = other_node;
you shouldn't allocate. You would overwrite p and lose the handle to the allocated memory. Not all pointers have to be initialised with malloc; allocate only if you create a node.
Your idea to strip all spaces from a line and then parse single digits will fail if you ever have more then 9 nodes. (But you cater for 1,000 node.) Don't try to parse the numbers yourself. There are library functions for that, for example strtol.
It is not clear what mainVertex is supposed to be. You use it only once, when you assign to it. You treat it like an array, but it is a global pointer, initialised to NULL. When you dereference it, you get undefined behaviour, which is where your segmentation fault probably comes from.
Here's a program that does what you want to do. (It always inserts nodes at the head for simplicity and it should have more allocation checks.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum {
maxNodes = 1000
};
struct node{
int data;
struct node* next;
};
struct node *G[maxNodes];
size_t nnode = 0;
int read_graph(const char *fn)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
fp = fopen(fn, "r");
if (fp == NULL) return -1;
while (getline(&line, &len, fp) != -1) {
char *p;
char *end;
int id;
int n;
id = strtol(line, &end, 10);
if (end == line) continue;
if (id < 1 || id > maxNodes) break;
if (id > nnode) nnode = id;
id--;
p = end;
n = strtol(p, &end, 10);
while (p != end) {
struct node *nnew = malloc(sizeof(*nnew));
nnew->data = n - 1;
nnew->next = G[id];
G[id] = nnew;
p = end;
n = strtol(p, &end, 10);
}
}
fclose(fp);
free(line);
return 0;
}
int main(void)
{
if (read_graph("test.txt") < 0) {
fprintf(stderr, "Couldn't gread raph.\n");
exit(1);
}
for (int i = 0; i < nnode; i++) {
struct node *p = G[i];
if (p) {
printf("%d:", i + 1);
for (; p; p = p->next) {
printf(" %d", p->data + 1);
}
puts("");
}
}
for (int i = 0; i < nnode; i++) {
struct node *p = G[i];
while (p) {
struct node *old = p;
p = p->next;
free(old);
}
}
return 0;
}

How to insert element into this struct

I am newer to C and do some practice,in this example,i want construct a table which can contains alot of element which is Symbol type,but i don't konw how to write that part.I want to use malloc to allocate heap memory to Symbol and insert into the table(SymbolTable).
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>
typedef struct {
char *name;
uint32_t addr;
}Symbol;
typedef struct {
Symbol* tbl;
uint32_t len;
uint32_t cap;
int mode;
} SymbolTable; /*this is the table i want to mantiply*/
SymbolTable* create_table(int mode) {
SymbolTable* st = (SymbolTable*)malloc(sizeof(SymbolTable));
if (st != NULL)
{
st->mode = mode;
st->len = 0;
return st;
}
printf("Memory allocation failed!\n");
return NULL;
}
void free_table(SymbolTable* table) {
int i;
for (i = 0; i < table->len; ++i)
{
free(table->tbl[i].name);
free(&(table->tbl[i]));
}
free(table);
}
int add_to_table(SymbolTable* table, const char* name, uint32_t addr) {
if (addr % 4 != 0)
{
printf("Address alignment erron!\n");
return -1;
}
int table_len = table->len;
if (table->mode == 1)
{
int i;
for (i = 0; i < table_len; ++i)
{
if (*((table->tbl[i]).name) == *name)
{
printf("Name existed!\n");
return -1;
}
`I don't know how to inset element here`
}
}
return 0;
}
int main()
{
SymbolTable * st = create_table(0);
add_to_table(st, "aaa", 4);
add_to_table(st, "bb", 8);
write_table(st, stdout);
free_table(st);
}
There are few problems with your code as it is; also, I had to make a few assumptions since you didn't specify those details in your question:
cap is the capacity of table->tbl, i.e. how much memory we have allocated
When adding a new symbol, we should copy the string containing its name, rather than just assigning that pointer to our new Symbol entry.
You should also pick one coding style and stick to it (braces on same vs new line, T* ptr vs T *ptr etc). Finally, I've removed the cast in create_table; see Do I cast the result of malloc?
Here is a fixed version of your code; in add_to_table, if we don't have enough memory to add a new one, we double the capacity of our Symbol array (calling realloc every single time to add space for one more element would be wasteful). When we extend the capacity of our array, we must take care to set each name pointer to NULL, because if we don't and free_table is called when cap > len, we'd be trying to free an uninitialized pointer (whereas calling free on NULL is perfectly fine and does nothing).
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef struct
{
char* name;
uint32_t addr;
} Symbol;
typedef struct
{
Symbol* tbl;
uint32_t len;
uint32_t cap;
int mode;
} SymbolTable; /*this is the table i want to mantiply*/
SymbolTable* create_table(int mode)
{
SymbolTable* st = malloc(sizeof(SymbolTable));
if (st != NULL)
{
st->tbl = NULL;
st->mode = mode;
st->len = 0;
st->cap = 0;
return st;
}
printf("Memory allocation failed!\n");
return NULL;
}
void free_table(SymbolTable* table)
{
int i;
for (i = 0; i < table->cap; ++i)
{
free(table->tbl[i].name);
}
free(table->tbl);
free(table);
}
int add_to_table(SymbolTable* table, const char* name, uint32_t addr)
{
if (addr % 4 != 0)
{
printf("Address alignment erron!\n");
return -1;
}
int table_len = table->len;
if (table->mode == 1)
{
int i;
for (i = 0; i < table_len; ++i)
{
if (!strcmp(table->tbl[i].name, name))
{
printf("Name existed!\n");
return -1;
}
}
if (table_len + 1 > table->cap)
{
// allocate more memory
uint32_t new_cap = table->cap ? table->cap * 2 : 2;
table->tbl = realloc(table->tbl, new_cap * sizeof(*table->tbl));
if (table->tbl == NULL)
{
// handle the error
}
table->cap = new_cap;
int i;
for (i = table_len; i < new_cap; ++i)
{
table->tbl[i].name = NULL;
}
}
uint32_t name_len = strlen(name);
table->tbl[table_len].name = malloc(name_len + 1);
strncpy(table->tbl[table_len].name, name, name_len);
table->tbl[table_len].name[name_len] = '\0';
table->tbl[table_len].addr = addr;
table->len++;
}
return 0;
}
int main(void)
{
SymbolTable* st = create_table(1);
add_to_table(st, "aaa", 4);
add_to_table(st, "bb", 8);
write_table(st, stdout);
free_table(st);
}
Firstly, I think you need to allocate *tbl and set cap (if he contain the max nb of cell in your table) in create_table().
Next, in add_to_table(), try to malloc(sizeof(struct symbol)) if (len < cap) and allocate memory for *name, and set up to your value (don't forget the \0 at the end of *name). Assign this to tbl[len] and do not forget to increment len.
Try to separate in little function, like int is_in_table(const char *name) who return the index or -1, or symbol new_symbol(const char *name, Uint32 addr) who create and set new symbol.
I hope that I have been useful for you :).
Use the concept of implementing Self-Referential structure.
Giving hints:
typedef struct S{
char *name;
uint32_t addr;
S* next;
}Symbol;
typedef struct {
Symbol* tbl;
uint32_t len;
int mode;
} SymbolTable;
http://www.how2lab.com/programming/c/link-list1.php
self referential struct definition?
Possible Implementation
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>
#include <stdio.h>
typedef struct S{
char *name;
uint32_t addr;
S* next;
}Symbol;
typedef struct {
Symbol* tbl;
uint32_t len;
int mode;
} SymbolTable; /*this is the table I want to maltiply*/
SymbolTable* create_table(int mode) {
SymbolTable* st = (SymbolTable*)malloc(sizeof(SymbolTable));
if (st != NULL)
{
st->tbl = NULL;
st->mode = mode;
st->len = 0;
return st;
}
printf("Memory allocation failed!\n");
return NULL;
}
void free_table(SymbolTable* table) {
free(table);
}
int add_to_table(SymbolTable* table, char* name, uint32_t addr) {
if (addr % 4 != 0)
{
printf("Address alignment erron!\n");
return -1;
}
int table_len = table->len;
if (table->mode == 1)
{
if (table_len == 0)
{
Symbol *t = (Symbol*)malloc(sizeof(Symbol));
t->name = name;
t->next = NULL;
table->len++;
table->tbl = t;
}
else
{
Symbol *t = table->tbl;
while (t->next != NULL)
{
if (t->name == name)
{
printf("Name existed!\n");
return -1;
}
t = t->next;
}
if (t->name == name)
{
printf("Name existed!\n");
return -1;
}
t->next = (Symbol*)malloc(sizeof(Symbol));
table->len++;
t->next->name = name;
t->next->next = NULL;
}
}
return 0;
}
void write_table(SymbolTable *st)
{
Symbol *t = st->tbl;
while (t != NULL)
{
printf("%s\n",t->name);
t = t->next;
}
printf("\n");
}
int main()
{
SymbolTable *st = create_table(0);
st->mode = 1;// Table mode setting to 1 for importing next value to table.
// You may implement it in your own way.
add_to_table(st, "cc", 8);
st->mode = 1;
add_to_table(st, "bb", 8);
st->mode = 1;
write_table(st);
free_table(st);
}

Reading from file to dynamic struct

I would like to read from a file, line by line. Each line has 3 arguments guaranteed. First 2 are first and last name and third is age.
I want to make a linked list, in which, each node represents a person (line) in the file.
I don't know the size of the names so I made it dynamic. I also don't know the number of lines in the file, so I would like that to be dynamic too.
My approach was to use fscanf, but then I wouldn't know how much memory needs to be allocated prior to reading it.
The function convertToList is supposed to receive a file path of the file we wanna read, convert it to a linked list, then return the head node. (Open to improvements)
Check out my code and see where I got stuck:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum
{
FALSE,
TRUE
}bool;
struct Node{
char firstName[50];
char lastName[50];
int age;
struct Node *next;
};
typedef struct {
struct Node *head;
}LinkedList;
struct Node * convertToList(char *inputFilePath);
int main(int argc, char* argv[]) {
if(argc != 4) {
printf("Invalid arguments.\n");
exit(0);
}
if (strlen(argv[3])!=1) {
printf("Invalid sorting type.\n");
exit(0);
}
char *inputFilePath = (char*) malloc(sizeof(char*) * strlen(argv[1]) +1);
memcpy(inputFilePath, argv[1], strlen(argv[1]));
char *outputFilePath = (char*) malloc(sizeof(char*) * strlen(argv[2]) +1);
memcpy(outputFilePath, argv[2], strlen(argv[2]) +1);
char *sortType = argv[3];
//LinkedList* inputList = (LinkedList*)malloc(sizeof(struct Node));
struct Node* head = malloc(sizeof(struct Node));
head = convertToList(inputFilePath);
printf("\n%s %s %d\n", head->firstName, head->lastName, head->age);
// printf("\nsaaap\n");
getchar();
}
struct Node * convertToList(char *inputFilePath) {
FILE* ifp;
ifp = fopen(inputFilePath, "r");
if (!ifp) { perror("fopen"); exit(0); }
struct Node *head = NULL;
struct Node *prev = NULL;
bool isHead = TRUE;
while(!feof(ifp)) {
struct Node *tmp = (struct Node*)malloc(sizeof(struct Node));
if (prev != NULL)
prev->next = tmp;
if (head==NULL)
head = tmp;
fscanf(ifp, "%s %s %d\n", tmp->firstName, tmp->lastName, &tmp->age);
prev = tmp;
//Need to link to next node as well
}
fclose(ifp);
return head;
}
I know that the fscanf is wrong, but I'm not sure how to fix it.
Also, how do I return the root? Is my approach gonna work?
And lastly, how do can I set the next node in the list? I don't see it happening with the current while loop.
Thanks.
If you need to link the nodes this is how you can do it and use dynamic storage, here you go, I didn't think this very much but it is Ok.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct Node
{
char *firstName;
char *lastName;
int age;
struct Node *next;
};
struct Node *convertToList(const char *const inputFilePath);
void freeList(struct Node *);
int main(int argc, char* argv[])
{
struct Node *head;
if (argc != 2)
{
printf("Invalid arguments.\n");
return 1;
}
head = convertToList(argv[1]);
if (head != NULL)
{
struct Node *current;
current = head;
while (current != NULL)
{
fprintf(stderr, "%s %s %d\n", current->firstName, current->lastName, current->age);
current = current->next;
}
/* do manupulations with the list, example above, print the values */
freeList(head);
}
return 0;
}
void freeList(struct Node *node)
{
struct Node *current;
current = node;
while (current != NULL)
{
struct Node *next;
next = current->next;
if (current->firstName != NULL)
free(current->firstName);
if (current->lastName != NULL)
free(current->lastName);
free(current);
current = next;
}
}
size_t appendChar(char **buffer, char character, size_t length)
{
char *temporary;
if (buffer == NULL)
return length;
temporary = realloc(*buffer, 1 + length);
if (temporary == NULL)
return length;
temporary[length] = character;
*buffer = temporary;
return 1 + length;
}
struct Node *parseFileLine(char *line)
{
char *word;
struct Node *node;
char *endptr;
if (line == NULL)
return NULL;
node = malloc(sizeof(struct Node));
if (node == NULL)
return NULL;
node->firstName = NULL;
node->lastName = NULL;
node->age = -1; // an invalid value;
node->next = NULL;
word = strtok(line, " ");
if (word == NULL)
return node;
node->firstName = strdup(word);
word = strtok(NULL, " ");
if (word == NULL)
return node;
node->lastName = strdup(word);
word = strtok(NULL, " ");
if (word == NULL)
return node;
node->age = strtol(word, &endptr, 10);
if (*endptr != '\0')
node->age = -1;
return node;
}
struct Node *getNode(FILE *file)
{
char *line;
int character;
size_t length;
line = NULL;
length = 0;
while ((character = fgetc(file)) != EOF)
{
if (((char)character == '\n') && (line != NULL))
{
struct Node *node;
length = appendChar(&line, '\0', length);
node = parseFileLine(line);
free(line);
return node;
}
length = appendChar(&line, (char)character, length);
}
if (line != NULL)
free(line);
return NULL;
}
struct Node *convertToList(const char *const inputFilePath)
{
FILE *ifp;
struct Node *head;
struct Node *current;
struct Node *last;
ifp = fopen(inputFilePath, "r");
if (ifp == NULL)
{
perror("fopen");
return NULL;
}
head = NULL;
last = NULL;
while ((current = getNode(ifp)) != NULL)
{
if (current == NULL)
return head;
if (head == NULL)
head = current;
if (last != NULL)
last->next = current;
last = current;
}
fclose(ifp);
return head;
}
Here you can also print the nodes to see that the data is correctly there.
I think you don't understand what malloc is for and you don't know much about pointers too, in your fscanf you are storing data in firstName and lastName without allocating memory for it, they are not even initialized so you would get a segmentation fault.
A somewhat different approach.
argv copying
First off, as mentioned, you do not need to copy argv values. Main reason for doing do is if you manipulate the values. There are also cases where one want to erase argv values as they can be read by ps and other tools, read from /proc/ etc. For example some programs take passwords as argument, to prevent password to be readable by anyone having access to the system one typically copy the argument then overwrite the argv value.
It is however usually good practice to use variables for the arguments. It usually makes the code clearer, but also makes it easier to maintain if one do changes. E.g. implement flag arguments like -f <filename>.
exit() and return from main()
You also exit() with zero on error. You would want to exit with zero on success, and other value on error or other. This is the norm. 0 == success. Some applications implement numeric exit codes that can mean different things. E.g. 0 is normal exit, 1 is not an error but some special case, 2 likewise 3 might be an error etc. For example grep:
EXIT STATUS
The exit status is 0 if selected lines are found, and 1 if not found. If an
error occurred the exit status is 2. (Note: POSIX error handling code should
check for '2' or greater.)
scanf
When you use scanf to read strings there are some tricks that can be used to make it better. First off always use the size parameter.
char name[16]
sscanf(buf, "%15s", name);
Do also check items read:
if (sscanf(buf, "%15s %d", name, &age) != 2)
... error ...
Third you can also save number of bytes read by %n:
sscanf(buf, "%n%15s%n %n%d%n", &of1, name, &of2, &age, &of3)
Usage
A very simple, but also quick and user-friendly thing, is to add a usage function.
Typically:
int usage(const char *self, const char *err_str)
{
fprintf(stderr,
"Usage: %s <in-file> <out-file> <sort-type>\n"
" Sort types:\n"
" f Sort by First Name\n"
" l Sort by Last Name\n"
" a Sort by Age\n"
,
self
);
if (err_str) {
fprintf(stderr,
"\nError: %s\n",
err_str
);
}
return ERR_ARG;
}
Then in main() you can quickly and clean add something like:
if (argc < 4)
return usage(argv[0], "Missing arguments.");
A note on you validation of the sort argument. Instead of using strlen() you can check if byte 2 is 0.
if (argv[3][1] != '\0')
... error ...
Finally main could be something like:
int main(int argc, char *argv[])
{
char *in_file, *out_file, sort;
struct Node *head = NULL;
int err = 0;
if (argc < 4)
return usage(argv[0], "Missing arguments.");
if (argc > 4)
return usage(argv[0], "Unknown arguments.");
if (argv[3][1] != '\0')
return usage(argv[0], "Invalid sorting type.");
in_file = argv[1];
out_file = argv[2];
sort = argv[3][0];
if (sort != 'f' && sort != 'l' && sort != 'a')
return usage(argv[0], "Invalid sorting type.");
if ((err = file_to_llist(in_file, &head)) != 0)
return err;
prnt_llist(stdout, head);
free_ll(head);
return err;
}
malloc helpers
When dealing with a lot of mallocing and similar it can be useful to add some helper functions. If you get a memory error you normally would exit right away.
void *alloc(size_t size)
{
void *buf;
if ((buf = malloc(size)) == NULL) {
fprintf(stderr, "Memory error.\n");
exit(ERR_MEM);
}
return buf;
}
void *re_alloc(void *old, size_t size)
{
void *buf;
if ((buf = realloc(old, size)) == NULL) {
fprintf(stderr, "Memory error.\n");
exit(ERR_MEM);
}
return buf;
}
Parsing of the file
As you want to have everything dynamically allocated and no limits (beyond system memory) one solution is to implement some sort of tokenizer. It can be helpful to use a struct to hold it together. Something like:
struct file_toker {
FILE *fh; /* File handle */
char *buf; /* Dynamic Read buffer */
size_t size; /* Size of buffer */
size_t len; /* Length of actual data in buffer. */
};
One point here is to keep length of tokens read. By this one do not need to keep using strlen etc.
If you can afford it it would usually be better to read whole file in one go, then parse the buffer. Optionally one can read file in chunks of say 4096*16 bytes, but then one get some complexity when it comes to overlapping lines between reads etc.
Anyhow in this example one byte is read at a time.
Start code
Finally a starting ground could be something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* memcpy/strncpy */
#include <errno.h> /* errno for fopen() */
#include <ctype.h> /* isspace() */
#define ERR_ARG 1
#define ERR_FILE_FMT 2
#define ERR_MEM 3
struct Node {
char *name_first;
char *name_last;
int age;
struct Node *next;
};
struct file_toker {
FILE *fh;
char *buf;
size_t size;
size_t len;
};
/* ===============----- GEN HELPERS ------=================== */
int usage(const char *self, const char *err_str)
{
fprintf(stderr,
"Usage: %s <in-file> <out-file> <sort-type>\n"
" Sort types:\n"
" f Sort by First Name\n"
" l Sort by Last Name\n"
" a Sort by Age\n"
,
self
);
if (err_str) {
fprintf(stderr,
"\nError: %s\n",
err_str
);
}
return ERR_ARG;
}
void *alloc(size_t size)
{
void *buf;
if ((buf = malloc(size)) == NULL) {
fprintf(stderr, "Memory error.\n");
exit(ERR_MEM);
}
return buf;
}
void *re_alloc(void *old, size_t size)
{
void *buf;
if ((buf = realloc(old, size)) == NULL) {
fprintf(stderr, "Memory error.\n");
exit(ERR_MEM);
}
return buf;
}
/* ===============----- LINKED LIST ------=================== */
void free_node(struct Node *n)
{
if (!n)
return;
if (n->name_first)
free(n->name_first);
if (n->name_last)
free(n->name_last);
free(n);
}
void free_ll(struct Node *n)
{
struct Node *p;
if (!n)
return;
for ( ; n ; ) {
p = n;
n = n->next;
free_node(p);
}
}
void prnt_llist(FILE *fd, struct Node *n)
{
int i = 0;
fprintf(fd, "NODELIST:\n");
for ( ; n != NULL ; n = n->next) {
fprintf(fd,
"Entry %d {\n"
" Name: %s, %s\n"
" Age : %d\n"
"}\n",
++i,
n->name_last,
n->name_first,
n->age
);
}
}
/* ================--------- FILE TOKER ------------==================== */
/* Free / close reader. */
void free_ft(struct file_toker *ft)
{
if (!ft)
return;
if (ft->fh)
fclose(ft->fh);
free(ft->buf);
ft->fh = NULL;
ft->buf = NULL;
}
/* Initiate reader. */
int ft_init(struct file_toker *ft, const char *fn, size_t buf_sz)
{
ft->size = buf_sz;
ft->len = 0;
ft->buf = alloc(ft->size);
ft->fh = fopen(fn, "r");
if (!ft->fh) {
perror("Unable to open file");
return errno;
}
return 0;
}
/* Increase buffer size. */
size_t ft_increase(struct file_toker *ft)
{
if (ft->size < 1)
ft->size = 1;
ft->size *= 2;
ft->buf = re_alloc(ft->buf, ft->size);
return ft->size;
}
/* Read and skip spaces (\n, \r, ' ', \t etc.). Return first non-space. */
char ft_skip_space(struct file_toker *ft)
{
int c;
while ((c = fgetc(ft->fh)) != EOF && isspace(c))
;
return c == EOF ? 0 : (char)c;
}
/* Read next token */
size_t file_tok(struct file_toker *ft)
{
size_t i = 1;
size_t max;
int c;
if (ft->size < 2)
ft_increase(ft);
ft->len = 0;
max = ft->size - 1;
/* Skip any leading spaces. Function return first non-space. */
if ((ft->buf[0] = ft_skip_space(ft)) == 0)
return 0;
while ((c = fgetc(ft->fh)) != EOF) {
/* If space, break. */
if (isspace(c))
break;
/* Save char to buffer. */
ft->buf[i++] = (char)c;
/* If entire buffer used, increase it's size. */
if (i > max)
max = ft_increase(ft) - 1;
}
/* Null terminate. */
ft->buf[i] = 0x00;
/* Length without terminating null */
ft->len = i;
return i;
}
/* Read next space separated token and save it as new allocated string. */
int file_tok_str(struct file_toker *ft, char **out)
{
if (file_tok(ft) == 0)
return 1;
*out = alloc(ft->len + 1);
memcpy(*out, ft->buf, ft->len + 1);
return 0;
}
/* Read next space separated token and scan it as int. */
int file_tok_int(struct file_toker *ft, int *out)
{
if (file_tok(ft) == 0)
return 1;
if ((sscanf(ft->buf, "%d", out)) != 1)
return 1;
return 0;
}
/* ===============----- FILE PARSER ------=================== */
int file_to_llist(const char *fn, struct Node **head)
{
struct Node *node = NULL, *cur = *head;
struct file_toker ft;
/* Initiate new file token reader, initial buffer size 4096 bytes. */
if (ft_init(&ft, fn, 4096))
return 1;
while (1) {
/* Allocate next node */
node = alloc(sizeof(struct Node));
node->name_first = NULL;
node->name_last = NULL;
/* Read and copy first name. */
if (file_tok_str(&ft, &node->name_first))
break;
/* Read and copy last name. */
if (file_tok_str(&ft, &node->name_last))
break;
/* Read and copy age. */
if (file_tok_int(&ft, &node->age))
break;
/* Link and save current for next iteration. */
node->next = NULL;
if (cur) {
cur->next = node;
}
cur = node;
if (*head == NULL)
*head = node;
}
/* Free last unused node. */
free_node(node);
free_ft(&ft);
return 0;
}
/* ===============----- MAIN ROUTINE ------=================== */
int main(int argc, char *argv[])
{
char *in_file, *out_file, sort;
struct Node *head = NULL;
int err = 0;
if (argc < 4)
return usage(argv[0], "Missing arguments.");
if (argc > 4)
return usage(argv[0], "Unknown arguments.");
if (argv[3][1] != '\0')
return usage(argv[0], "Invalid sorting type.");
in_file = argv[1];
out_file = argv[2];
sort = argv[3][0];
if (sort != 'f' && sort != 'l' && sort != 'a')
return usage(argv[0], "Invalid sorting type.");
if ((err = file_to_llist(in_file, &head)) != 0)
return err;
prnt_llist(stdout, head);
free_ll(head);
return err;
}

Linux device driver read write functions issue

I'm writing sample device driver to read and write using cyclic buffer, it means the last node point to the first one. I created the linked list of 10 block, each block buffer size = 5.
now on my write function, when I call write method, it write in the buffer, check if it's filled, then jump to the other one, the next write will write in the current buffer + offset defined in lnod struct . same thing for read.
when I run the echo command twice
echo 123456789 > /dev/driver
echo abcd > /dev/driver
according to the write and read function below the cat command will give123456789abcd as result since the second write will continue on the offset, so the read function will read all the size_to_read, but the cat (called 3 times)command gave me this :
cat /dev/driver
abcd
6789
abcd
some usefull code parts:
static int BlockNumber = 10;
static int BlockSize = 5;
static int size_to_read = 0;
data buffer structure
typedef struct dnode
{
int bufSize;
char *buffer;
struct dnode *next;
} data_node;
liste stucture
typedef struct lnode
{
data_node *head;
data_node *cur_write_node;
data_node *cur_read_node;
int cur_read_offset;
int cur_write_offset;
}liste;
static liste newListe;
the create liste method is called in init function
write function
static ssize_t sample_write_liste(struct file *f, const char *buf, size_t size, loff_t *offset)
{
if (*(offset) == 0)
{
size_to_read += size;
}
int size_to_copy;
size_to_copy = MIN (size, BlockSize - newListe.cur_write_offset);
copy_from_user(newListe.cur_write_node->buffer + newListe.cur_write_offset, buf, size_to_copy);
*(offset) += size_to_copy;
newListe.cur_write_offset += size_to_copy;
if (newListe.cur_write_offset == BlockSize)
{
newListe.cur_write_node = newListe.cur_write_node->next;
newListe.cur_write_offset = 0; // we erase previous things
}
return size_to_copy;
}
the read function
static ssize_t sample_read_liste(struct file *f, char *buf, size_t size, loff_t *offset)
{
int size_to_copy;
size_to_copy = MIN (size_to_read - *(offset), BlockSize - newListe.cur_read_offset);
copy_to_user(buf, newListe.cur_read_node->buffer + newListe.cur_read_offset,size_to_copy);
newListe.cur_read_offset += size_to_copy;
(*offset)+=size_to_copy;
if (newListe.cur_read_offset == BlockSize)
{
newListe.cur_read_node = newListe.cur_read_node->next;
newListe.cur_read_offset = 0;
}
return size_to_copy;
}
create linked list function
static void createlist (void) {
data_node *newNode, *previousNode, *headNode;
int i;
/* new node creation */
newNode = (data_node *)kmalloc(sizeof (data_node), GFP_KERNEL);
newNode->buffer = (char *)kmalloc(BlockSize*sizeof(char), GFP_KERNEL);
newNode->next = NULL;
newListe.head = newNode;
headNode = newNode;
previousNode = newNode;
for (i = 1; i < BlockNumber; i++)
{
newNode = (data_node *)kmalloc(sizeof (data_node), GFP_KERNEL);
newNode->buffer = (char *)kmalloc(BlockSize*sizeof(char), GFP_KERNEL);
newNode->next = NULL;
previousNode->next = newNode;
}
/* cyclic liste : we should tie the last element to the first one (head) */
newNode->next = headNode;
newListe.cur_read_node = headNode;
newListe.cur_write_node = headNode;
newListe.cur_read_offset = 0;
newListe.cur_write_offset = 0;
}
In the createlist() routine , in the for loop, you need to add the follwoing line to make a circular list.
previousNode = newNode;
Your existing createlist would create a circular list with just two nodes.
for (i = 1; i < BlockNumber; i++)
{
newNode = (data_node *)kmalloc(sizeof (data_node), GFP_KERNEL);
newNode->buffer = (char *)kmalloc(BlockSize*sizeof(char), GFP_KERNEL);
newNode->next = NULL;
previousNode->next = newNode;
previousNode = newNode //Please add this line to make the list circular.
}

Resources