Hyo-Jong  Kim
Hyo-Jong Kim

Reputation: 15

Making command in c by recursion

kimhyojong@kimhyojong-VirtualBox:~/newpro$ tree
.
├── main
├── main.c
├── main.c~
├── mkdirTest
│   ├── recursion
│   ├── test1
│   │   └── recursion1
│   └── test2
│       └── recursion2
├── re
├── recurs
│   └── what
├── recursion
├── recursion.c
└── test3

It looks so beatiful.

I can get this tree by ' apt-get install tree'.

So I want to print like that tree.

And this is my code

void recursion(const char *pwd){
char next[255];
struct dirent *filelist;
struct stat info;
int i;
int dirNum;
DIR* dp;

if((dp = opendir(pwd)) == NULL)
{
    exit(1);
}

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

        memset(next, 0, 255);

        lstat(filelist->d_name, &info);

        if(S_ISDIR(info.st_mode))
        {
            strcat(next, pwd);
            strcat(next, "/");
            strcat(next, filelist->d_name);

            printf("%-15s", filelist->d_name);
            printf("%s\n", next);

            recursion(next);
        }
        else
            printf("%-15s\n", filelist->d_name);
    }
}

}

I think it works by recursion.

but, it is printing like this.

test3          
recursion.c    
main           
mkdirTest      recursion      
test2          
test1          
recursion      
main.c~        
main.c         
recurs         what           
re        

Not recursing.... Just root lvl and next lvl. No more proceed.

What's the problem?

Upvotes: 0

Views: 76

Answers (1)

Tony
Tony

Reputation: 1655

There are a number of problems in your code.

  1. Your call to lstat is silently failing if you are not in the current directory (the one you started the program from). This is because filelist->d_name does not include the path.

  2. When you find a directory (when lstat works) you are printing its name twice. This is because filelist->d_name and next are the same except for a prefixed path on the latter.

  3. You have no code to put multiple levels of spaces or tabs in to get the tree structure.

  4. Even if you were calling lstat correctly, it would break on directories you don't have access to (e.g. those owned by root).

My suggestions are:

  1. Right near the start of your recursion function, change to the directory that is passed as an argument, using chdir, then call opendir("."). Hopefully lstat will work at that point. You can save the current working directory (getcwd) and restore it at the end of the function, so as not to mess things up for further calls.

  2. Only print the name of the file once.

  3. Pass in a level argument to your recursion argument which counts the number of levels you are down in the tree (0 when you first call it). Add 1 to this for future recursions. You can then use level to print spaces or tabs to get the tree effect.

  4. I don't have a solution for the permissions issue. Maybe check for the error code on lstat and print a question mark for that entry?

Here is some code which implements some of the above suggestions and might work (not properly tested):

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#define TABSIZE 4


void recursion(const char *pwd, int level) {   // note number of levels added

    char next[255];
    struct dirent *filelist;
    struct stat info;
    int i;
    int dirNum;
    DIR* dp;
    char * lastpwd;

    lastpwd = getcwd(NULL, 0);                 // remember current directory

    if (chdir(pwd) != 0) {                     // change directory to pwd
        free(lastpwd);
        exit(1);
    };

    if((dp = opendir(".")) == NULL)            // opendir is called on cwd
    {
        chdir(lastpwd);
        free(lastpwd);
        exit(1);
    }

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

            memset(next, 0, 255);

            lstat(filelist->d_name, &info);

            for (i=0; i<level*TABSIZE; i++) {   // spaces for tree effect
                putchar(' ');
            }
            printf("%s\n", filelist->d_name);   // print just once

            if(S_ISDIR(info.st_mode))           // note info will be broken
                                                // if lstat silently failed
            {
                strcat(next, filelist->d_name);
                recursion(next, level+1);       // recurse, next level down
            }
        }
    }

    chdir(lastpwd);                             // restore current directory
    free(lastpwd);
}

int main(int argc, char ** argv)
{
    recursion(argv[1], 0);
    return 0;
}

Upvotes: 1

Related Questions