-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);
}
10
  • 4
    Please do not cast realloc - it is not required and is bad practice stackoverflow.com/questions/605845/… Commented Jan 15, 2017 at 4:18
  • They are a lot of question about this subject you are all in the same school? For example, stackoverflow.com/a/41653484/7076153. Commented Jan 15, 2017 at 4:22
  • Its not a project at least for me , i do it because i like it.We are not in the same school probably. Commented Jan 15, 2017 at 4:24
  • Thats why i am using entries and then i pass them to names. Commented Jan 15, 2017 at 4:25
  • your logic is terrible, are you trying to build an array of the directory entries? Commented Jan 15, 2017 at 4:39

1 Answer 1

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

Sign up to request clarification or add additional context in comments.

5 Comments

woops :) let me fix that
so much for implementing things "correctly" hehe ... should've actually read what I was writing. This is the trouble with write-only code
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.
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.
thank you for your help again , keep doing what you do

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.