Read from a file to a linked list C - c

I'm trying to read a txt file, and then save it to a linked list in C. I have tried a lot but I haven't found a solution.
This is what I have:
typedef struct contact{
char name[50];
char number[10];
char bithday[15];
struct contact *next;
}contact;
struct contact *head;
FILE *fp;
This part saves the contacts that I've already have to a txt file.
void backup(){
struct contact *ap;
fp=fopen("save_contacts.txt", "w");
if(fp==NULL){
printf(" Error\n");
system("pause");
return 1;
}
for (ap=head;ap!=NULL;ap=ap->next){
fprintf(fp,"%s ",ap->name);
//fprintf(fp,", ");
fprintf(fp,"%s ",ap->number);
//fprintf(fp,", ");
fprintf(fp,"%s ",ap->birthday);
//fprintf(fp,", ");
fprintf(fp,"; ");
}
fprintf(fp, "End");
}
So my problem is with this part, it doesn't read the txt file.
void read() {
struct contact *ap;
int i;
head=NULL;
char aux[30];
FILE *fp=fopen("save_contacts.txt","r");
do{
head=NULL;
ap=(contact*)malloc(sizeof(contact));
fscanf(fp,"%s ",&ap->name);
fscanf(fp,"%s ",&ap->number);
fscanf(fp,"%s ",&ap->birthday);
fscanf(fp,"%s ",aux);
if(strcmp(aux,";")==0){
ap=ap->next;
}
}while(strcmp(aux,"End")!=0);
}

Doing
ap=(contact*)malloc(sizeof(contact));
fscanf(fp,"%s ",&ap->name);
fscanf(fp,"%s ",&ap->number);
fscanf(fp,"%s ",&ap->birthday);
fscanf(fp,"%s ",aux);
if(strcmp(aux,";")==0){
ap=ap->next;
ap->next is not set so the second turn ap has an undefined value so the behavior is undefined when you dereference it
Note your way to save with :
for (ap=head;ap!=NULL;ap=ap->next){
...
fprintf(fp,"; ");
}
fprintf(fp, "End");
is not compatible with your way to read where ';' is replaced by 'End' for the last element
You can do that (I suppose there is no ';' for the last element) :
int read() {
FILE *fp=fopen("save_contacts.txt","r");
if (fp == NULL) {
fprintf(stderr, "cannot open 'save_contacts.txt'\n");
head = NULL;
return 0;
}
contact ** pp = &head;
char aux[30];
int result;
for (;;) {
*pp = malloc(sizeof(contact));
if (fscanf(fp,"%49s %9s %14s %29s", (*pp)->name, (*pp)->number, (*pp)->birthday, aux) != 4) {
fputs("invalid file, cannot read the 3 fields then ;/End\n", stderr);
result = 0;
free(*pp);
break;
}
pp = &(*pp)->next;
if (strcmp(aux,"End") == 0) {
result = 1;
break;
}
if (strcmp(aux, ";") != 0) {
fputs("invalid file, expected ; or End\n", stderr);
result = 0;
break;
}
}
*pp = NULL;
fclose(fp);
return result;
}
returning 0 in case of a problem, else 1
Note I protect the read of the string to not write out of them and I check if the file is correct
For instance having the full program
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct contact{
char name[50];
char number[10];
char birthday[15]; /* not bithday */
struct contact *next;
}contact;
struct contact *head;
int read() {
FILE *fp=fopen("save_contacts.txt","r");
if (fp == NULL) {
fprintf(stderr, "cannot open 'save_contacts.txt'\n");
head = NULL;
return 0;
}
contact ** pp = &head;
char aux[30];
int result;
for (;;) {
*pp = malloc(sizeof(contact));
if (fscanf(fp,"%49s %9s %14s %29s", (*pp)->name, (*pp)->number, (*pp)->birthday, aux) != 4) {
fputs("invalid file, cannot read the 3 fields then ;/End\n", stderr);
result = 0;
free(*pp);
break;
}
pp = &(*pp)->next;
if (strcmp(aux,"End") == 0) {
result = 1;
break;
}
if (strcmp(aux, ";") != 0) {
fputs("invalid file, expected ; or End\n", stderr);
result = 0;
break;
}
}
*pp = NULL;
fclose(fp);
return result;
}
int main()
{
if (read()) {
/* debug */
for (contact * p = head; p != NULL; p = p->next)
printf("%s %s %s\n", p->name, p->number, p->birthday);
}
/* free resources */
while (head != NULL) {
contact * p = head;
head = head->next;
free(p);
}
return 0;
}
Compilation and execution:
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall l.c
pi#raspberrypi:/tmp $ cat save_contacts.txt
bruno 007 19/02/1960 ;
you 001 01/01/2010 ;
bar 123 31/12/1999 End
pi#raspberrypi:/tmp $ ./a.out
bruno 007 19/02/1960
you 001 01/01/2010
bar 123 31/12/1999
pi#raspberrypi:/tmp $
Execution under valgrind :
pi#raspberrypi:/tmp $ valgrind ./a.out
==2334== Memcheck, a memory error detector
==2334== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2334== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2334== Command: ./a.out
==2334==
bruno 007 19/02/1960
you 001 01/01/2010
bar 123 31/12/1999
==2334==
==2334== HEAP SUMMARY:
==2334== in use at exit: 0 bytes in 0 blocks
==2334== total heap usage: 6 allocs, 6 frees, 5,712 bytes allocated
==2334==
==2334== All heap blocks were freed -- no leaks are possible
==2334==
==2334== For counts of detected and suppressed errors, rerun with: -v
==2334== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)
pi#raspberrypi:/tmp $

Related

Create a tree from a file .txt in C

my problem is the following:
I have to create a tree from a file .txt, this is an example of what is inside the file:
the first element a is the root, after that is always this way:
the first element (x for example is the left tree) the second element (b for example is the right tree).
if an element is preceded by a point, that element for sure dont have a left or right child below.
so in this case the outgoing tree will be the following:
this is the structure in c language of my tree:
typedef char element;
typedef struct tree_element {
element value;
struct tree_element* left, * right;
} node;
typedef node* tree;
Any ideas on how to solve this?
my incorrect solution:
bool IsEmpty(tree t) {
return (t == NULL);
}
tree EmptyTree(void) {
return NULL;
}
tree Left(tree t) {
if (IsEmpty(t)) {
return NULL;
}
else {
return t->left;
}
}
tree Right(tree t) {
if (IsEmpty(t)) {
return NULL;
}
else {
return t->right;
}
}
tree ConsTree(const element* e, tree l, tree r) {
tree t = malloc(sizeof(node));
t->value = *e;
t->left = l;
t->right = r;
return t;
}
tree InsertValue(tree t, element e, int position) {
if (IsEmpty(t)) {
return ConsTree(&e, EmptyTree(), EmptyTree());
}
tree root = t;
while (true) {
if (position == 1) {
if (IsEmpty(Left(t))) {
t->left = ConsTree(&e, EmptyTree(), EmptyTree());
break;
}
else {
t = Left(t);
}
}
else {
if (IsEmpty(Right(t))) {
t->right = ConsTree(&e, EmptyTree(), EmptyTree());
break;
}
else {
t = Right(t);
}
}
}
return root;
}
extern tree LoadTree(const char* filename) {
FILE* f = fopen(filename, "r");
if (f == NULL) {
return EmptyTree();
}
else {
fseek(f, 0, SEEK_END);
if (ftell(f) == 0) {
fclose(f);
return EmptyTree();
}
else {
fseek(f, 0, SEEK_SET);
tree t = EmptyTree();
char c;
c = fgetc(f);
int i = 0;
element e;
e = c;
l++;
t = InsertValue(t,e,0);
while (c != EOF) {
c = fgetc(f);
if (c != ' ' && c != '\t' && c != '\r' && c != '\n' && c != '\v' && c != '\f') {
i++;
}
}
fclose(f);
return t;
}
}
}
thanks in advance
The input file gives for each sub tree the value then if not prefixed by a dot the left tree then the right tree and that recursively, so the way to construct the tree can follow the same recursion :
node * read_tree(FILE * fp)
{
node * r = malloc(sizeof(node));
fscanf(fp, " %c", &r->value);
if (r->value != '.') {
r->left = read_tree(fp);
r->right = read_tree(fp);
}
else {
fscanf(fp, " %c", &r->value);
r->left = r->right = NULL;
}
return r;
}
The space before %c in the format bypass the possible space/newline/tab/... allowing to read a file with the indents or without them like a.xbd.s.u.c as you said in a remark.
The proposal above supposes/hopes the file is valid, else it is possible to do :
node * read_tree(FILE * fp)
{
node * r = malloc(sizeof(node));
const char * err;
if (r == NULL)
err = "not enough memory";
else if (fscanf(fp, " %c", &r->value) != 1)
err = "invalid file, unexpected EOF";
else if (r->value != '.') {
r->left = read_tree(fp);
r->right = read_tree(fp);
return r;
}
else if (fscanf(fp, " %c", &r->value) != 1)
err = "invalid file, unexpected EOF after '.'";
else if (r->value == '.')
err = "invalid file, two consecutive '.'";
else {
r->left = r->right = NULL;
return r;
}
fprintf(stderr, "%s\nposition in the file : %ld\n", err, ftell(fp));
exit(-1);
}
A full program can be :
#include <stdio.h>
#include <stdlib.h>
typedef char element;
typedef struct tree_element {
element value;
struct tree_element* left, * right;
} node;
node * read_tree(FILE * fp)
{
node * r = malloc(sizeof(node));
const char * err;
if (r == NULL)
err = "not enough memory";
else if (fscanf(fp, " %c", &r->value) != 1)
err = "invalid file, unexpected EOF";
else if (r->value != '.') {
r->left = read_tree(fp);
r->right = read_tree(fp);
return r;
}
else if (fscanf(fp, " %c", &r->value) != 1)
err = "invalid file, unexpected EOF after '.'";
else if (r->value == '.')
err = "invalid file, two consecutive '.'";
else {
r->left = r->right = NULL;
return r;
}
fprintf(stderr, "%s\nposition in the file : %ld\n", err, ftell(fp));
exit(-1);
}
void print_free_tree(node * tree, int lvl)
{
if (tree == NULL)
putchar('.');
else {
putchar('(');
print_free_tree(tree->left, lvl+1);
printf("-%c %d-", tree->value, lvl);
print_free_tree(tree->right, lvl+1);
putchar(')');
free(tree);
}
}
int main(int argc, char ** argv)
{
FILE * fp;
if (argc != 2)
fprintf(stderr, "Usage: %s <file>\n", *argv);
else if ((fp = fopen(argv[1], "r")) == NULL)
perror("cannot read file");
else {
node * tree = read_tree(fp);
/* must be at end of file except may be some space/newline */
char c;
if (fscanf(fp, " %c", &c) == 1)
fprintf(stderr, "invalid file, extra element(s) from the position %ld\n", ftell(fp));
fclose(fp);
/* show & free tree */
print_free_tree(tree, 1);
putchar('\n');
}
return 0;
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -Wall c.c
pi#raspberrypi:/tmp $ cat f
a
.x
bd.s.u.c
pi#raspberrypi:/tmp $ ./a.out f
((.-x 2-.)-a 1-(((.-s 4-.)-d 3-(.-u 4-.))-b 2-(.-c 3-.)))
pi#raspberrypi:/tmp $
to help to check the validity of the tree each value is written with the level of its node, the empty branches are indicated by a '.'.
Execution under valgrind :
pi#raspberrypi:/tmp $ valgrind ./a.out f
==13298== Memcheck, a memory error detector
==13298== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13298== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==13298== Command: ./a.out f
==13298==
((.-x 2-.)-a 1-(((.-s 4-.)-d 3-(.-u 4-.))-b 2-(.-c 3-.)))
==13298==
==13298== HEAP SUMMARY:
==13298== in use at exit: 0 bytes in 0 blocks
==13298== total heap usage: 10 allocs, 10 frees, 5,556 bytes allocated
==13298==
==13298== All heap blocks were freed -- no leaks are possible
==13298==
==13298== For lists of detected and suppressed errors, rerun with: -s
==13298== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
pi#raspberrypi:/tmp $

Creating a dynamic char array

I am trying to read in the a file and store the words into a dynamic char array. Right now I am getting a Segmentation fault (core dumped) error.
I have tried using strdup() and strcpy() still i am getting the same error
char ** array;
int main(int argc, char * argv[]){
int size = 0;
int i;
FILE * file;
char * line;
size_t len;
ssize_t read;
file = fopen("wordsEn.txt", "r");
if(file == NULL){
printf("Error coudl not open wordsEn.txt\n");
return -1;
}
while((read = getline(&line, &len, file)) != -1){
size++;
}
array = (char **) malloc((sizeof(char *) * size));
rewind(file);
i = 0;
while((read = getline(&line, &len, file)) != -1){
//strcpy(array[i], line);
array[i] = strdup(line);
i++;
}
for(i = 0; i < size; i++){
printf("%s", array[i]);
}
}
I am expecting for example array[0] to return the string 'alphabet'
I am getting a Segmentation fault (core dumped) error.
warning to get a newly allocated line through getline each time you have to reset line to NULL and len to 0 each time, for instance :
while(line = NULL, len = 0, (read = getline(&line, &len, file)) != -1){
Note you do not have to read two times the file, you can use malloc then realloc to increase the size of the (really) dynamic array
A Proposal :
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char ** array = malloc(0);
size_t size = 0;
FILE * file;
char * line;
size_t len;
ssize_t read;
file = fopen("wordsEn.txt", "r");
if(file == NULL) {
printf("Error coudl not open wordsEn.txt\n");
return -1;
}
while (line = NULL, len = 0, (read = getline(&line, &len, file)) != -1){
array = realloc(array, (size+1) * sizeof(char *));
array[size++] = line;
}
free(line); /* do not forget to free it */
fclose(file);
for(size_t i = 0; i < size; i++){
printf("%s", array[i]);
}
/* free resources */
for(size_t i = 0; i < size; i++){
free(array[i]);
}
free(array);
return 0;
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall ar.c
pi#raspberrypi:/tmp $ cat wordsEn.txt
turlututu foo
bar
loop
pi#raspberrypi:/tmp $ ./a.out
turlututu foo
bar
loop
pi#raspberrypi:/tmp $
Execution under valgrind :
pi#raspberrypi:/tmp $ valgrind ./a.out
==13161== Memcheck, a memory error detector
==13161== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13161== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==13161== Command: ./a.out
==13161==
turlututu foo
bar
loop
==13161==
==13161== HEAP SUMMARY:
==13161== in use at exit: 0 bytes in 0 blocks
==13161== total heap usage: 11 allocs, 11 frees, 5,976 bytes allocated
==13161==
==13161== All heap blocks were freed -- no leaks are possible
==13161==
==13161== For counts of detected and suppressed errors, rerun with: -v
==13161== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)

Segmentation fault in c program,use malloc (Linux os)

this c program works in Windows But Getting " Segmentation fault (core dump)" in Linux. I gues couse of error pointers or malloc function.I can't return struct array without pointers and malloc.
struct team {
char name[12];
int m_count;
int score;
};
struct team *teamName(){
FILE *fp;
fp=fopen("teams.txt", "r");
struct team *items;
items= (struct team *) malloc(sizeof(struct team) * 10);
int i;
for(i=0; i<10; i++)
{
fscanf(fp, "%s\n" , items[i].name);
}
fclose(fp);
return items;
}
int main(void)
{ struct team *items = teamName();
getMatch(items);
}
There several problems in your code :
you do not check fopen success
you do not check fscanf success, and if a read name is greater than 11 you write out of the buffer with an undefined behavior
why the \n in the fscanf format ?
if you read less that 10 names some entries are not set, with the risk later to have an undefined behavior
A proposal taking my remarks into account can be :
#include <stdio.h>
#include <stdlib.h>
struct team {
char name[12];
int m_count;
int score;
};
struct team *teamName(){
FILE *fp = fopen("teams.txt", "r");
if (fp == NULL)
return NULL;
struct team *items = malloc(sizeof(struct team) * 10);
int i;
for (i=0; i<10; i++)
{
if (fscanf(fp, "%11s" , items[i].name) != 1) {
/* empty other names */
do {
items[i].name[0] = 0;
}
while (++i != 10);
break;
}
}
fclose(fp);
return items;
}
int main(void)
{
struct team *items = teamName();
if (items != NULL) {
for (int i = 0; i != 10; ++i) {
if (items[i].name[0] != 0)
puts(items[i].name);
}
}
/* getMatch(items); */
}
Compilation and execution :
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall m.c
pi#raspberrypi:/tmp $ cat teams.txt
aze qsd
loop
bar
pi#raspberrypi:/tmp $ ./a.out
aze
qsd
loop
bar
pi#raspberrypi:/tmp $
Note the fscanf read words, I mean a name must not contain space, else you need to use for instance fgets

Storing data in list c

The idea of my program is to read data from a file (in this case file includes 5 names), and store them in a list, so I can use the data later to for example calculating the min/max characters. So far I have been able to read the data and print it out, but rather than printing, I'd want to save them to a list. I couldn't find a way around how to do this, so I'd appreciate some help.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char file, filename[25];
FILE *f;
printf("Enter the file name: ");
scanf("%s", filename);
f = fopen(filename, "r");
if (f == NULL)
{
perror("No file found.\n");
return 0;
}
printf("The contents of %s file are:\n", filename);
while((file = fgetc(f)) != EOF)
printf("%c", file);
fclose(f);
return 0;
}
Just a simple example using linked names for your list :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct NameList {
char * name;
struct NameList * next;
} NameList;
int append(NameList ** head, NameList ** tail, char * s)
{
NameList * l;
if (((l = malloc(sizeof(NameList))) == NULL) ||
((l->name = strdup(s)) == NULL))
/* not enough memory */
return 0;
l->next = NULL;
if (*head == NULL) {
*head = *tail = l;
}
else {
(*tail)->next = l;
*tail = l;
}
return 1;
}
int main(void) {
char filename[25];
FILE * f;
printf("Enter the file name: ");
if (scanf("%24s", filename) != 1)
return 0;
f = fopen(filename, "r");
if (f == NULL)
{
puts("No file found.");
return 0;
}
NameList * head = NULL;
NameList * tail = NULL;
char s[64];
/* suppose a name has no space even composed and less than 64 characters */
while (fscanf(f, "%63s", s) == 1) {
if (!append(&head, &tail, s))
return 0;
}
fclose(f);
printf("The names in %s file are:\n", filename);
NameList * l;
l = head;
while (l != NULL) {
puts(l->name);
l = l->next;
}
/* search longer name */
size_t maxlen = 0;
char * longer = NULL;
l = head;
while (l != NULL) {
size_t ln = strlen(l->name);
if (ln > maxlen) {
maxlen = ln;
longer = l->name;
}
l = l->next;
}
if (longer != NULL)
printf("longer name is : %s\n", longer);
/* free resources */
while (head != NULL) {
l = head;
head = head->next;
free(l->name);
free(l);
}
return 0;
}
Compilation and execution
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra l.c
pi#raspberrypi:/tmp $ cat aze
firstname secondname
anothername
lastname
pi#raspberrypi:/tmp $ ./a.out
Enter the file name: aze
The names in aze file are:
firstname
secondname
anothername
lastname
longer name is : anothername
Execution under valgrind
pi#raspberrypi:/tmp $ valgrind ./a.out
==10132== Memcheck, a memory error detector
==10132== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10132== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10132== Command: ./a.out
==10132==
Enter the file name: aze
The names in aze file are:
firstname
secondname
anothername
lastname
longer name is : anothername
==10132==
==10132== HEAP SUMMARY:
==10132== in use at exit: 0 bytes in 0 blocks
==10132== total heap usage: 12 allocs, 12 frees, 6,570 bytes allocated
==10132==
==10132== All heap blocks were freed -- no leaks are possible
==10132==
==10132== For counts of detected and suppressed errors, rerun with: -v
==10132== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)
Note the linked list can be replaced by an array of char*, using realloc to increase its size when reading the names etc
Apart for details of the format of the input, reading in the data is not flexible in your current solution. Heres a more generous input reading. Combine this with the other answers and you should be on your way.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
{
char *cwd = getcwd(NULL, 0);
printf("FYI, the current working directory of this program is : `%s'\n", cwd);
free(cwd);
}
printf("Enter the file name: ");
char *filename;
int scanf_return = scanf("%m[a-zA-Z./]", &filename);
if (scanf_return != 1) {
if (errno != 0) {
perror("scanf");
} else {
fprintf(stderr, "%s\n",
"Sorry, unable to read file name. "
"Only 'a'...'z', 'A'...'Z', '.' (period) "
"and '/' (slash) allowed in the name.");
}
return EXIT_FAILURE;
}
FILE *f = fopen(filename, "r");
if (f == NULL) {
perror(filename);
free(filename);
return EXIT_FAILURE;
}
printf("The contents of `%s' file are:\n", filename);
free(filename);
filename = NULL;
size_t line_sz = 0u;
char *line = NULL;
int nread;
errno = 0;
while ((nread = getline(&line, &line_sz, f)) != -1) {
// If we reached the EOF then there might not be a newline character
if (line[nread - 1] != '\n') {
nread++;
}
printf("`%.*s'\n", nread - 1, line);
}
if (errno != 0) {
perror("getline");
free(line);
fclose(f);
return EXIT_FAILURE;
}
free(line);
fclose(f);
return EXIT_SUCCESS;
}

Copying file names into an array with C

I'm attempting to find all the files in a directory of a certain type (hardcoded here to tif) and copy them into an array. Everything compiles cleanly (gcc -Wall gives no errors or warnings) but there are some memory issues. Though the program I've written seems to run cleanly (no segfaults), some of the file names are weird characters you get when you've got something other than ascii values in your string. This led me to run with valgrind, which shows errors (output below) but I can't track down what the actual problem is. In some directories, valgrind it self segfaults (the program runs clean in the same dir).
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <search.h>
#include <string.h>
#include <error.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
int exitStatus = 0;
/*------------------------------------------------------------------------------
* array_find
*
* ARGS - Takes a pointer to a string, a pointer to an array of strings, and an
* int representing the length of the array.
*
* RETURN - returns an int indicating the first index of the key in the array,
* or -1 if the key was not found
*-----------------------------------------------------------------------------*/
int array_find(char *key, char *argv[], int argc){
int i;
for (i = 0; i < argc; i++)
{
#ifdef DEBUG_array_find
printf("strncmp(%s, %s, %d) = %d\n", key, argv[i], min(strlen(key), strlen(argv[i])), strncmp(key, argv[i], min(strlen(key), strlen(argv[i]))));
#endif
if (strncmp(key, argv[i], min(strlen(key), strlen(argv[i]))) == 0)
{
return i;
}
}
return -1;
}
/*------------------------------------------------------------------------------
* ends_with
*
* ARGS - str = string to be checked
* sub = string to look for
*
* RETURN - Returns true if str ends with sub or both strings are NULL.
False otherwise.
*-----------------------------------------------------------------------------*/
bool ends_with(char *str, char *sub){
if (str == NULL && sub == NULL)
{
return true;
}
if (str == NULL || sub == NULL)
{
return false;
}
char *last_instance_of_sub = rindex(str, *sub); //Finds the last index of the first char of sub
int sub_len = strlen(sub);
if (last_instance_of_sub == NULL || strlen(last_instance_of_sub) != sub_len)
{
return false;
}
return strncmp(last_instance_of_sub, sub, sub_len) == 0;
}
int main(int argc, char *argv[])
{
/*Parse args*/
DIR *dir;
int index = array_find("-d", argv, argc);
char *dirname;
if (index >= 0)
{
dirname = argv[index + 1];
dir = opendir(dirname);
}
else
{
dirname = getcwd(NULL, 0);
if (dirname == NULL)
{
perror("Error getting current directory name.");
exit(1);
}
dir = opendir(dirname);
}
if (dir == NULL)
{
perror(dirname);
exit(1);
}
#ifdef DEBUG_MAIN
printf("dirname = %s\n", dirname);
#endif
int threads = 1;
index = array_find("-t", argv, argc);
if (index >= 0)
{
threads = atoi(argv[index + 1]);
}
#ifdef DEBUG_MAIN
printf("threads = %d\n", threads);
#endif
struct dirent *entry = readdir(dir);
int num_files = 0;
while (entry != NULL)
{
if (ends_with(entry->d_name, ".tif")){
#ifdef DEBUG_MAIN
printf("%s\n", entry->d_name);
#endif
num_files++;
}
entry = readdir(dir);
}
if (closedir(dir) != 0)
{
perror("Failed to close directory.");
}
#ifdef DEBUG_MAIN
printf("Num files = %d\n", num_files);
#endif
dir = opendir(dirname);
if (dir == NULL)
{
perror(dirname);
exit(1);
}
entry = readdir(dir);
char *file_names[num_files];
int i = 0;
for(; entry != NULL; i++)
{
if (ends_with(entry->d_name, ".tif")){
file_names[i] = strdup(entry->d_name);
if (file_names[i] == NULL)
{
perror("Could not create the filename array.\n");
exit(1);
}
}
entry = readdir(dir);
}
/* #ifdef DEBUG_MAIN*/
for (i = 0; i < num_files; i++)
{
printf("%s\n", file_names[i]);
/* free(file_names[i]);*/
}
/* #endif*/
free(dir);
return exitStatus;
}
Valgrind output:
==24488== Memcheck, a memory error detector
==24488== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==24488== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==24488== Command: ./myprogram -d /home/chris/Pictures/Catalinas\ with\ Christie/Processed/
==24488==
dirname = /home/chris/Pictures/Catalinas with Christie/Processed/
threads = 1
cacti2_lzn.tif
DSC_2139_lzn.tif
DSC_1512_lzn.tif
DSC_1296_lzn.tif
DSC_1577_lzn.tif
DSC_1658_lzn.tif
DSC_1293_lzn.tif
DSC_1631_lzn.tif
DSC_1418_lzn.tif
DSC_1315_2crop_lzn.tif
DSC_1377_lzn2crop.tif
DSC_2167_lzn.tif
1981-1985-HDR3_lzn2.tif
DSC_2129_lzn.tif
DSC_1448_lzn.tif
DSC_1607_lzn.tif
DSC_1564_lzn.tif
DSC_2052-DSC_2072_lzn.tif
DSC_1487_lzn.tif
DSC_1591_2_lzn.tif
DSC_2124_lzn.tif
DSC_1622_lzn.tif
DSC_2157_lzn.tif
DSC_1685_lzn.tif
Num files = 24
cacti2_lzn.tif
DSC_2139_lzn.tif
DSC_1512_lzn.tif
DSC_1296_lzn.tif
DSC_1577_lzn.tif
DSC_1658_lzn.tif
==24488== Use of uninitialised value of size 8
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24488== by 0x4EA4ECB: puts (ioputs.c:36)
==24488== by 0x400D52: main (batch-convert.c:161)
==24488==
==24488== Invalid read of size 1
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24488== by 0x4EA4ECB: puts (ioputs.c:36)
==24488== by 0x400D52: main (batch-convert.c:161)
==24488== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==24488==
==24488==
==24488== Process terminating with default action of signal 11 (SIGSEGV)
==24488== Access not within mapped region at address 0x0
==24488== at 0x4C2D7C2: __GI_strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==24488== by 0x4EA4ECB: puts (ioputs.c:36)
==24488== by 0x400D52: main (batch-convert.c:161)
==24488== If you believe this happened as a result of a stack
==24488== overflow in your program's main thread (unlikely but
==24488== possible), you can try to increase the size of the
==24488== main thread stack using the --main-stacksize= flag.
==24488== The main thread stack size used in this run was 8388608.
==24488==
==24488== HEAP SUMMARY:
==24488== in use at exit: 33,243 bytes in 25 blocks
==24488== total heap usage: 26 allocs, 1 frees, 66,051 bytes allocated
==24488==
==24488== LEAK SUMMARY:
==24488== definitely lost: 0 bytes in 0 blocks
==24488== indirectly lost: 0 bytes in 0 blocks
==24488== possibly lost: 0 bytes in 0 blocks
==24488== still reachable: 33,243 bytes in 25 blocks
==24488== suppressed: 0 bytes in 0 blocks
==24488== Rerun with --leak-check=full to see details of leaked memory
==24488==
==24488== For counts of detected and suppressed errors, rerun with: -v
==24488== Use --track-origins=yes to see where uninitialised values come from
==24488== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
Segmentation fault (core dumped)
It's been a while since I've used C at all, but as I understand it (from the man pages), strdup should use malloc to allocate memory on the heap for the copy of the string. Before I remembered the strdup function, I had tried to do exactly that manually, and had the same errors. I thought maybe my code was flawed, and thought the strdup function would take care of it, but apparently there is some other issue.
Can anyone tell me what I'm doing wrong?
EDIT 1:
As per requests, I've added the full source of the program. Also, for those saying to check i against num_files, as you'll see, I count the number of tif files ahead of time, so I know the exact number of files that will be copied into the array, thus checking the index isn't necessary.
Also, as a note, the program was compiled with DEBUG_MAIN defined, so anything in an #ifdef DEBUG_MAIN block does run. No other debug flags were defined.
in your code this part for(; entry != NULL; i++) is way too dangerous , for example lets say that the value of num_files is 1000 , what if a given directory contains 1002 entries , then you'll have a problem.
replace it with for(; entry != NULL && i < num_files ; i++)
The problem is that if you have any entries that don't match your pattern (such as the . and .. entries), you skip the corresponding entry in the array. It also means you go writing outside your file_names array. You should only increment i when the file name matches.
Using getcwd() instead of just using . for the current directory works, but is hardly necessary.
Using free(dir) instead of closedir(dir) is an unmitigated disaster.
The command line argument handling is unusual. As originally written, it would accept -delete as equivalent to -d. That's not good style.
#include <assert.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
bool ends_with(char *str, char *sub);
int array_find(char *key, char *argv[], int argc);
int array_find(char *key, char *argv[], int argc)
{
for (int i = 0; i < argc; i++)
{
if (strcmp(key, argv[i]) == 0)
return i;
}
return -1;
}
bool ends_with(char *str, char *sub)
{
if (str == NULL && sub == NULL)
return true;
if (str == NULL || sub == NULL)
return false;
char *last_instance_of_sub = rindex(str, *sub);
size_t sub_len = strlen(sub);
if (last_instance_of_sub == NULL || strlen(last_instance_of_sub) != sub_len)
return false;
return strcmp(last_instance_of_sub, sub) == 0;
}
int main(int argc, char *argv[])
{
int index = array_find("-d", argv, argc);
char *dirname;
if (index >= 0)
{
dirname = argv[index + 1];
}
else
{
dirname = getcwd(NULL, 0);
if (dirname == NULL)
{
perror("Error getting current directory name.");
exit(1);
}
}
DIR *dir = opendir(dirname);
if (dir == NULL)
{
perror(dirname);
exit(1);
}
char suffix[] = ".c";
printf("dirname = %s\n", dirname);
struct dirent *entry;
int num_files = 0;
while ((entry = readdir(dir)) != NULL)
{
if (ends_with(entry->d_name, suffix))
num_files++;
}
if (closedir(dir) != 0)
{
perror("Failed to close directory.");
}
printf("Num files = %d\n", num_files);
dir = opendir(dirname);
if (dir == NULL)
{
perror(dirname);
exit(1);
}
char *file_names[num_files];
int i = 0;
while ((entry = readdir(dir)) != NULL)
{
if (ends_with(entry->d_name, suffix))
{
file_names[i] = strdup(entry->d_name);
if (file_names[i++] == NULL)
{
perror("Could not create the filename array.\n");
exit(1);
}
}
}
assert(i <= num_files);
if (i < num_files)
num_files = i;
for (i = 0; i < num_files; i++)
{
printf("%s\n", file_names[i]);
free(file_names[i]);
}
closedir(dir);
return 0;
}
The index of the array should be checked:
i<num_files

Resources