Search code examples
clinuxfilesystemssystem-callstraversal

Unexpected Results using fts_children() in C


I have been beating my head on a wall over this fts_children() question. In the man page, http://www.kernel.org/doc/man-pages/online/pages/man3/fts.3.html, it clearly states As a special case, if fts_read() has not yet been called for a hierarchy, fts_children() will return a pointer to the files in the logical directory specified to fts_open(), that is, the arguments specified to fts_open(). Which I take to mean that a linked list of all the files in the current directory are returned. Well, I am finding that not to be the case and I would really appreciate some help in the matter. I expected a linked list to be returned and then I would iterate through it to find the file with the matching file name (the end goal). However, right now, I am just trying to iterate through the linked list (baby steps). Right now, it will return one file and then exit the loop. This does not make sense to me. Any help would very much appreciated!!!

Opening of file system:

char* const path[PATH_MAX] = {directory_name(argv[argc-index]), NULL}; 
            char* name = file_name(argv[argc-index]);

            if ((file_system = fts_open(path, FTS_COMFOLLOW, NULL)) == NULL){
                fprintf(stderr,"%s:%s\n", strerror(errno), getprogname());
                        exit(EXIT_FAILURE);
                 }/*Ends the files system check if statement*/

            /*Displays the information about the specified file.*/
            file_ls(file_system,name, flags);

For clarification, the directory_name parses the inputted path from the user and returns something like /home/tpar44. That directory is then opened.

Searching within the file system:

void
file_ls(FTS* file_system, char* file_name,  int* flags){
    FTSENT* parent = NULL;
    //dint stop = 0;

    parent = fts_children(file_system, 0);

    while( parent != NULL ){
        printf("parent = %s\n", parent->fts_name);
        parent = parent->fts_link;
    }
}

Thanks!


Solution

  • I think this is entirely by design.

    ...that is, the arguments specified to fts_open()...

    What it says is that it will list the root elements in the path_argv parameters for your convenenience. It treats the path_argv array as a logical directory itself.

    In other words this:

    int main(int argc, char* const argv[])
    {
        char* const path[] = { ".", "/home", "more/root/paths", NULL }; 
    
        FTS* file_system = fts_open(path, FTS_COMFOLLOW | FTS_NOCHDIR, &compare);
    
        if (file_system)
        {
            file_ls(file_system, "", 0);
            fts_close(file_system);
        }
        return 0;
    }
    

    Will output

    parent = .
    parent = /home
    parent = more/root/paths
    

    Which, in fact, it does (see http://liveworkspace.org/code/c2d794117eae2d8af1166ccd620d29eb).

    Here is a more complete sample that shows complete directory traversal:

    #include<stdlib.h>
    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fts.h>
    #include<string.h>
    #include<errno.h>
    
    int compare (const FTSENT**, const FTSENT**);
    
    void file_ls(FTS* file_system, const char* file_name, int* flags)
    {
        FTSENT* node = fts_children(file_system, 0);
    
        if (errno != 0)
            perror("fts_children");
    
        while (node != NULL)
        {
            // TODO use file_name and flags
            printf("found: %s%s\n", node->fts_path, node->fts_name);
            node = node->fts_link;
        }
    }
    
    int main(int argc, char* const argv[])
    {
        FTS* file_system = NULL;
        FTSENT* node = NULL;
    
        if (argc<2)
        {
            printf("Usage: %s <path-spec>\n", argv[0]);
            exit(255);
        }
    
        char* const path[] = { argv[1], NULL }; 
        const char* name = "some_name";
    
        file_system = fts_open(path, FTS_COMFOLLOW | FTS_NOCHDIR, &compare);
    
        if (file_system)
        {
            file_ls(file_system, name, 0); // shows roots
    
            while( (node = fts_read(file_system)) != NULL)
                file_ls(file_system, name, 0); // shows child elements
    
            fts_close(file_system);
        }
        return 0;
    }
    
    int compare(const FTSENT** one, const FTSENT** two)
    {
        return (strcmp((*one)->fts_name, (*two)->fts_name));
    }