-4

First I will explain what I want to program. I want to open a directory and get all the files names and their number (in the directory). I want to allocate certain amount of memory for the number of files I have found, and a certain amount of memory for their names. I will give an example.

Lets say we have got a directory named dir and that directory has 4 files in it. The 4 files are named as follows: lalo, camo, mara, sarw.

I want my program to work as follows

Go into dir and while finding files keep allocating memory in order to store them (like a table of strings where I don't know the exact size because I don't know how many files I will find). I am storing the filenames character by character and I keep allocating memory as needed, every time I read one char I am realocating memory and incrementing by 1. Same logic I follow to the files number where I keep incrementing the number of files and reallocating memory as needed. A table like this (pointers) is what I imagine filenames[numberOfFiles][stringCharacter].

I get a segmentation fault in function GetFiles(...) on the second loop here : *(*(entries+*number)+size) = dp->d_name[size];

int main()
{
    int number,i;
    char *mainDirectory = NULL,**names = NULL;

    printf("Give the movies directory: ");
    mainDirectory = ReadMainDirectory();



    if(GetFiles(mainDirectory,&names,&number) != 0)
    {


        system("PAUSE");
        return 1;
    }

    system("PAUSE");
    return 0;
}

char* ReadMainDirectory()
{
    char *dir,c;
    int size = 1;

    dir = (char*)malloc(size+1);
    dir[size-1] = '\0';

    while((c = getchar()) != '\n')
    {
        dir[size-1] = c;
        size++;

        dir = (char*)realloc(dir,size+1);
        dir[size-1] = '\0';
    }

    return dir;
}

int GetFiles(char *dir,char ***names,int *number)
{
    struct dirent *dp;
    DIR *fd;
    int size;
    char **entries = NULL;

    if ((fd = opendir(dir)) == NULL)
    {
        printf("Can't open directory %s!\n",dir);
        return 0;
    }

    *number = 0;
    size = 0;

    entries = (char**)malloc((*number+1) * sizeof(char*));
    *entries = (char*)malloc((size+1) * sizeof(char));

    while ((dp = readdir(fd)) != NULL)
    {
        if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
        {
            continue;
        }

        size = 0;

        while(dp->d_name[size] != '\0')
        {
            *(*(entries+*number)+size) = dp->d_name[size];
            size++;
            *(entries+*number) = (char*)realloc(entries[*number],(size+1) * sizeof(char));
        }

        entries[*number][size] = '\0';

        printf("%s\n",entries[*number]);

        (*number)++;

        entries = (char**)realloc(entries,(*number+1) * sizeof(char*));
    }

    *names = entries;

    closedir(fd);

    return 1;
}

int GetStringSize(char *string)
{
    int size = 0;

    while(string[size] != '\0')
    {
        size++;
    }

    return size;
}

int StringEndsWith(char* string,char* extension)
{
    return !strcmp(string + strlen(string) - strlen(extension), extension);
}
Guillaume Jacquenot
  • 11,217
  • 6
  • 43
  • 49
Jaspar
  • 25
  • 7
  • 4
    Please do not cast `realloc` - it is not required and is bad practice http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc – Ed Heal Jan 15 '17 at 04:18
  • Thanks for the tip. – Jaspar Jan 15 '17 at 04:20
  • They are a lot of question about this subject you are all in the same school? For example, http://stackoverflow.com/a/41653484/7076153. – Stargateur Jan 15 '17 at 04:22
  • Its not a project at least for me , i do it because i like it.We are not in the same school probably. – Jaspar Jan 15 '17 at 04:24
  • Thats why i am using entries and then i pass them to names. – Jaspar Jan 15 '17 at 04:25
  • your logic is terrible, are you trying to build an array of the directory entries? – Ahmed Masud Jan 15 '17 at 04:39
  • It would be good to see the rest of your code. – RoadRunner Jan 15 '17 at 04:41
  • Because i am not very good with english and i tried to explain how i imagined the program to work , you should check my code and i believe you will see how i imagined it to work. – Jaspar Jan 15 '17 at 04:42
  • You need like `entries[*number] = malloc(1);` before next loop. Also `(size+1)` --> `(size+2)` : +1 for NUL. – BLUEPIXY Jan 15 '17 at 04:45
  • BLUEPIXY second time you help me out thank you...it seems to have worked...could you explain to me why it worked in simple words? – Jaspar Jan 15 '17 at 04:49
  • `*(*(entries+*number)+size) = dp->d_name[size];` is assumed to be like `*entries = (char*)malloc((size+1) * sizeof(char));`. So, it is required each time a new name is entered. – BLUEPIXY Jan 15 '17 at 04:57

1 Answers1

0

A proper implementation of getfiles (gets all the files in a directory in a dynamic array) and provides a deallocator:

#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>

/* returns a NULL terminated array of files in directory */

char **getfilelist(const char *dirname) {
        DIR *dp;
        char **entries = NULL;
        struct dirent *entry;
        off_t index = 0;

        dp = opendir(dirname);
        if (!dp) {
                perror(dirname);
                return NULL;
        }

        while((entry = readdir(dp)) != NULL) {
                /* increase entries array size by one pointer-size */
                entries = realloc(entries, (1+index)*sizeof(char *));

                /* strdup returns a newly allocated duplicate of a string that 
                 * you must free yourself
                 */
                entries[index] = strdup(entry->d_name);
                index++;
        }

        /* need one more entry for NULL termination */
        entries = realloc(entries, (1+index)*sizeof(char *));
        entries[index] = NULL;

        return entries;
}

void putfilelist(char **entries) {
        char **p;
        if(!entries)
                return;
        for(p = entries; *p !=NULL ; p++) {
                free(*p); /* free all the strdups */
        }
        free(entries); /* free the array of pointers */
}

An example of how you could use it:

int main(int argc, char **argv) {
        char **p, **entries;
        char *dir = (argc == 1 ? "." : argv[1]);
        entries = getfilelist(dir);
        for (p = entries; p && *p; p++) {
                printf("%s\n", *p);
        }
        putfilelist(entries);
}

Updated solution,

In order to implement your solution without using any of the library code, and keep it to system calls, here is an example that implements everything that the above example does, without relying on higher-level library calls.

Note

The bottom functions are the same as the above example.

/*
 * A partially low-level implementation using a direct system calls
 * and internal memory management 
 * 
 * This is an opinionated (linux only) implementation of OP 
 *
 * linux headers are only included for some of the constants and 
 * where it would be trivial to re-implement system calls (e.g. open, write etc.)
 * which I am too lazy to do in this example.
 *
 */

#define NDEBUG

#include <stdarg.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fcntl.h>
#include <linux/unistd.h>

/* replace this macro with your own error handling messages */
#define handle_error(msg) \
    do { my_fdputs(2, msg); my_exit(-127); } while (0)

#if !defined(NDEBUG)
#define assert(x) do { int __rv = (x); if(!__rv) { my_fdprintf(2, "assertion failed %s\n", #x); my_exit(__rv); } } while(0)
#else 
#define assert(x) do {} while(0)
#endif



/* returns a NULL terminated array of files in directory */
/* low-level list builder using getdents */

void my_exit(int x)
{
    syscall(SYS_exit_group, x);
}

/* a trivial malloc / realloc / free */

/* simple linked list memory accounting */
struct memblock {
    struct memblock *next;
    size_t size;
    int free; /* flag */
    int magic; /* canary value for debugging */
};

#define METASIZE sizeof(struct memblock)

void *global_base = NULL; 

struct memblock *find_free_block(struct memblock **last, size_t size)
{
    struct memblock *current = global_base;
    while(current && !(current->free && current->size >= size)) {
        *last = current;
        current = current->next;
    }
    return current;
}


/*
 * instead of using sbrk, we should really use mmap on newer
 * linux kernels and do better accounting, however this is a
 * simple example to get you started
 */

struct memblock *request_space(struct memblock *last, size_t size)
{
    struct memblock *block;
    void *request;

    block = sbrk(0); /* get current program break */
    request = sbrk(size + METASIZE);
    assert((void *)block == request);
    if(request  == (void *)-1) {
        return NULL;
    }

    if(last) {
        last->next = block;
    }
    block->size = size;
    block->next = NULL;
    block->free = 0;
    block->magic = 0x12345678;

    return block;
}

struct memblock *get_memblock_ptr(void *ptr)
{
    return (struct memblock *)ptr - 1;
}

/* a simple memcpy, can be optimized by taking alignment into account */

void *my_memcpy(void *dst, void *src, size_t len)
{
    size_t i;
    char *d = dst;
    const char *s = src;
    struct memblock *bd, *bs;
    bd = get_memblock_ptr(dst);
    for(i = 0; i < len; i++) {
        d[i] = s[i];
    }
    return dst;
}

/* now to implement malloc */
void *my_malloc(size_t size)
{
    struct memblock *block;

    if(size == 0)
        return NULL;

    if(!global_base) {
        block = request_space(NULL, size);
        if(!block)
            return NULL;
        global_base = block;
    }
    else {
        struct memblock *last = global_base;
        block = find_free_block(&last, size);
        if(!block) {
            block = request_space(last, size);
            if(!block) {
                return NULL;
            }
        }
        else {
            block->free = 0;
            block->magic = 0x77777777;
        }
    }

    return (block+1);
}

void my_free(void *ptr)
{

    struct memblock *block;
    if (!ptr)
        return;

    block = get_memblock_ptr(ptr);
    assert(block->free == 0);
    assert(block->magic == 0x77777777 || block->magic == 0x12345678);
    block->free = 1;
    block->magic = 0x55555555;
}


void *my_realloc(void *ptr, size_t size)
{
    struct memblock *block;
    void *newptr;

    if(!ptr) 
        return my_malloc(size);

    block = get_memblock_ptr(ptr);
    if(block->size >= size)
        return ptr;

    newptr = my_malloc(size);
    if(!newptr)  {
        return NULL;
    }

    my_memcpy(newptr, ptr, block->size);
    my_free(ptr);
    return newptr;
}


/* trivial string functions */

size_t my_strlen(const char *src) {
    size_t len = 0;
    while(src[len])
        len++;
    return len;
}

char *my_strdup(const char *src)
{
    char *dst;
    char *p;
    size_t len = 0, i;
    len = my_strlen(src);


    dst = my_malloc(1+len);
    if(!dst) 
        return NULL;

    for(i = 0; i < len; i++) {
        dst[i] = src[i];
    }
    dst[i] = 0;
    return dst;
}


/* trivial output functions */

my_fdputs(int fd, const char *str)
{
    return write(fd, str, my_strlen(str));
}

int my_fdputc(int fd, char c)
{
    return write(fd, &c, sizeof(char));
}

/* a very limited implementation of printf */
int my_fdvprintf(int fd, const char *fmt, va_list ap)
{
    const char *p;
    int count = 0;
    for(p = fmt; p && *p; p++ ) {
        if(*p == '%')  {
            p++;
            switch(*p) {
            case 's':
                count += my_fdputs(fd, va_arg(ap, char *));
                break;
            case '%':
                count += my_fdputc(fd, '%');
                break;
            default: 
#ifndef NDEBUG
                my_fdputs(2, "warning: unimplemented printf format specifier %");
                my_fdputc(2, *p);
                my_fdputc(2, '\n');
#endif
                break;
            }
        }
        else {
            my_fdputc(fd, *p);
        }
    }
    return count;
}

int my_fdprintf(int fd, const char *fmt, ...)
{
    int rv;
    va_list ap;
    va_start(ap, fmt);
    rv = my_fdvprintf(fd, fmt, ap);
    va_end(ap);
    return rv;
}


/* wrapper to linux getdents directory entry call */    
/* linux dirent structure */
struct linux_dirent {
    long           d_ino;
    off_t          d_off;
    unsigned short d_reclen;
    char           d_name[];
};

/* system call wrapper */
int getdents(int fd, void *buf, size_t bufsize)
{
    return syscall(SYS_getdents, fd, buf, bufsize);
}


/* reimplement getfilelist using our getdents */    
#define BUF_SIZE 1024
char **getfilelist(const char *dirname) {
    int fd, nread;
        char **entries = NULL;
        off_t index = 0;

    char buf[BUF_SIZE];
    struct linux_dirent *d;
    int bpos;


    /* O_DIRECTORY since kernel 2.1 */
        fd = open(dirname, O_DIRECTORY|O_RDONLY);

        if (fd < 0) {
                handle_error(dirname);
        }

    for(;;) {
        nread = getdents(fd, buf, BUF_SIZE);
        if (nread == -1)
            handle_error("getdents");

        if (nread == 0)
            break;

        for (bpos = 0; bpos < nread;) {
            d = (struct linux_dirent *) (buf + bpos);
            entries = my_realloc(entries, (1+index) * sizeof(char *));
            entries[index++] = my_strdup(d->d_name);
            bpos += d->d_reclen;
                }
    }

       /* need one more entry for NULL termination */
        entries = my_realloc(entries, (1+index)*sizeof(char *));
        entries[index] = NULL;

    close(fd);

    return entries;
}

void putfilelist(char **entries) {
        char **p;
        if(!entries)
                return;
        for(p = entries; *p !=NULL ; p++) {
                my_free(*p); /* free all the strdups */
        }
        my_free(entries); /* free the array of pointers */
}


int main(int argc, char **argv) {
        char **p, **entries;
        char *dir = (argc == 1 ? "." : argv[1]);
        entries = getfilelist(dir);
        for (p = entries; p && *p; p++) {
                my_fdprintf(1, "%s\n", *p);
        }
        putfilelist(entries);
}

Hope you enjoy

Ahmed Masud
  • 21,655
  • 3
  • 33
  • 58
  • woops :) let me fix that – Ahmed Masud Jan 15 '17 at 05:16
  • so much for implementing things "correctly" hehe ... should've actually read what I was writing. This is the trouble with write-only code – Ahmed Masud Jan 15 '17 at 05:18
  • Its a good answer and well explained the way it works , but i dont want to use stdrup (i dont like going the easy way like using existing functions).I would prefer a working answer with my existing code.Thanks for your help and time. – Jaspar Jan 15 '17 at 13:25
  • what you're saying doesn't make sense, you are using `printf`, `malloc`, `realloc`, and readdir etc. these are all library functions; however in spirit of what you're trying to do for strings, you should take an approach like the one above, and then implement your own versions of things like `strdup`, once you've done that you can start optimizing. – Ahmed Masud Jan 15 '17 at 15:02
  • thank you for your help again , keep doing what you do – Jaspar Jan 16 '17 at 00:54