printing line like tree command

2019-09-07 04:30发布

I am trying to learn C programming for a task at work and I have set myself a little project, which consists of reading down a file tree including all the sub directories obtaining information about each file.

The problem I couldn't solve is that how to make line when print all the directories as real tree command does.

and this is my sample code:

enum { doSkip, isFile, isDir } testDir(char *path, char *name)
{ 
     struct stat st_buf;        
     if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
         return doSkip;
     }   
     stat(path, &st_buf);
     if (S_ISDIR(st_buf.st_mode))
         return isDir;
     return isFile;
}

void list(const char *path, int indentlevel)
{
     DIR *dirp = opendir(path);
     struct dirent *dentry;
     char buf[1024];
     if (!dirp) {
      printf("%*sNo access\n",indentlevel,"");
      return;
     }

     while ((dentry = readdir(dirp)) != NULL) {

        sprintf(buf,"%s/%s", path, dentry->d_name);
        switch (testDir(buf,dentry->d_name)) {
        case doSkip:
           /* do nothing */
           break;
        case isDir:
           printf("%*s%s:\n",indentlevel,"",dentry->d_name);
           list(buf,indentlevel+4);
           break;
        case isFile:
           printf("%*s%s\n",indentlevel,"",dentry->d_name);
           break;
        }
     }
     closedir(dirp);
}

int main()
{
     list(".", 0);
     return 0;
}

please give me some idea!

标签: c linux
2条回答
2楼-- · 2019-09-07 04:58

The insight you need is that printf doesn't have to print a full line. Unlike some other print functions in other languages.

for(i=0;i<identlevel;i++)
  printf("-");
 printf("[ %s ]\n", dentry->dname);

Will do the trick.

查看更多
甜甜的少女心
3楼-- · 2019-09-07 05:12
list(buf,indentlevel+4);

You should change this to list(buf,indentlevel+1) because you are only going up one level. This way you can keep track of directory levels. Add another function to add extra spaces. See the example below:

void space(int level, int file)
{
    int i;
    for (i = 0; i < level + 1; i++)
        printf("\x0b3   "); // add the '│' character
    if (file) 
        printf("\x0c3\x0c4  "); // add  '├' and '─' characters
}

void list(const char *path, int indentlevel)
{
    DIR *dirp = opendir(path);
    struct dirent *dentry;
    char buf[1024];
    if (!dirp)
    {
        printf("%*sNo access\n",indentlevel,"");
        return;
    }

    int i;
    while ((dentry = readdir(dirp)) != NULL)
    {
        sprintf(buf,"%s/%s", path, dentry->d_name);
        switch (testDir(buf,dentry->d_name))
        {
        case doSkip:
            break;
        case isDir:
            space(indentlevel, 1);
            putchar('\n');
            space(indentlevel, 0);
            printf("[ %s ]\n",dentry->d_name);
            list(buf,indentlevel+1);
            break;

        case isFile:
            space(indentlevel-1, 1);
            printf("%s\n",dentry->d_name);
            break;
        }
    }

    closedir(dirp);
}

Here is a more complicated version, it stores data in a buffer, so it can find the last child in the folder and draw the correct "square edge" character for the last item. It still prints some extra vertical lines, that needs to be taken care of.

//store directory information
typedef struct TT_dir
{
    char* name;
    int isdir;
    int level;
    int islast;
} T_dir;

//vector for holding lines of data
typedef struct TT_vector
{
    T_dir *data;
    int capacity;
    int size;
} T_vector;

void vector_add(T_vector *pvec, char *buf, int isdir, int level)
{
    if (pvec->size == pvec->capacity)
    {
        pvec->capacity += 16;
        pvec->data = realloc(pvec->data, pvec->capacity * sizeof(T_dir));
    }

    char *duplicate = malloc(strlen(buf) + 1);
    strcpy(duplicate, buf);
    pvec->data[pvec->size].name = duplicate;
    pvec->data[pvec->size].isdir = isdir;
    pvec->data[pvec->size].level = level;
    pvec->data[pvec->size].islast = 0;
    pvec->size++;
}

void list(const char *path, int level, T_vector *pvec)
{
    DIR *dirp = opendir(path);
    struct dirent *dentry;
    char buf[4096];
    if (!dirp)
    {
        printf("%*sNo access\n", level, "");
        return;
    }

    int haschildren = 0;
    while ((dentry = readdir(dirp)) != NULL)
    {
        sprintf(buf, "%s/%s", path, dentry->d_name);
        switch (testDir(buf, dentry->d_name))
        {
        case doSkip:
            break;
        case isDir:
            //show progress:
            printf(".");

            //add directory info to vector array
            vector_add(pvec, dentry->d_name, 1, level);

            //next dir...
            list(buf, level + 1, pvec);
            haschildren = 1;
            break;

        case isFile:
            //add directory info to vector array
            vector_add(pvec, dentry->d_name, 0, level);
            haschildren = 1;
            break;
        }
    }

    //needs this information for drawing the correct character
    if (haschildren)
        pvec->data[pvec->size - 1].islast = 1;

    closedir(dirp);
}

int main()
{
    T_vector vec;
    vec.capacity = 0;
    vec.size = 0;
    vec.data = 0;

    list(".", 0, &vec);
    printf("\n");

    int i, k;
    for (i = 0; i < vec.size; i++)
    {
        if (vec.data[i].isdir)
        {
            for (k = 0; k < vec.data[i].level; k++) printf("\x0b3   ");
            printf("\x0b3\n");

            for (k = 0; k < vec.data[i].level; k++) printf("\x0b3   ");

            printf(vec.data[i].islast ? "\x0c0" : "\x0c3");

            printf("\x0c4  %s:\n", vec.data[i].name);
        }
        else
        {
            for (k = 0; k < vec.data[i].level; k++) printf("\x0b3   ");

            printf(vec.data[i].islast ? "\x0c0" : "\x0c3");

            printf("\x0c4  %s\n", vec.data[i].name);
        }
    }

    return 0;
}
查看更多
登录 后发表回答