二、实现tree系统命令
1.问题分析
linux中的tree命令可以实现以树形结构列出某个指定目录下的所有问价以及目录名,如下所示的效果:
我们就要实现这样一个tree命令。基本的流程如下:
(1)系统初始化。构建一个空队列,该队列将实时缓存整个目录(含有子目录)中未输出的文件呢信息。
(2)读取目录下所有的文件信息以及目录信息,按照ASCII码值的大小构建一个新的子排序队列。
(3)将子队列与整个目录的队列合并,采用的方法类似于栈的操作,即将整个子队列加到原来的队列的前面,更新全局队列的信息。
(4)出队列。读取队列的第一个成员的属性。如果是一个文件,直接按信息输出,如果是一个目录,重复上面的(2)(3)(4)步骤。递归循环操作。
2.程序流程图
3.代码
关于递归部分,类似与之前写的递归复制目录文件的应用小程序。
#include
#include
#include
#include
#include
#include
#include
#define REGU_FILE 1
#define DIR_FILE 2
// 构建栈式结构中每个节点代表一个文件,节点数据结构定义如下:
typedef struct node
{
struct node *next; // 下一个节点,构建单链结构
unsigned int level; // 当前子目录的深度,用于区分输出时的层次
char *name; // 所在的目录下的文件名,不含有上级目录,用于输出名字
char *fullname; // 输出整个路径名,用于读取属性
}filenode;
// 队列数据结构
typedef struct head
{
struct node *head; // 队首指针
struct node *rear; // 队尾指针
}headnode;
void dir_tree(char *dirname,headnode *link_stack, int level);
int insert_sort(headnode *link_stack, filenode *NEW)
{
filenode *next = link_stack->head;
filenode *prev = NULL;
while(next)
{
if (strcmp( NEW->name, next->name ) > 0 )
{
prev = next;
next = next->next;
}
else
break;
}
if(link_stack->head == NULL && link_stack->rear == NULL) //the first one
{
link_stack->head = NEW;
link_stack->rear = NEW;
}
else if(prev == NULL && next != NULL) //insert in front of the queue
{
NEW->next = link_stack->head;
link_stack->head = NEW;
}
else if( prev != NULL && next == NULL) //insert in end of the queue.
{
prev->next = NEW;
link_stack->rear = NEW;
}
else
{
prev->next = NEW;
NEW->next = next;
}
}
headnode *read_dir_to_link_stack(char *dir,int level)
{
DIR *dirp = NULL;
if(NULL== (dirp=opendir(dir)))
{
perror("opendir");exit(EXIT_FAILURE);
}
headnode *ptr = (headnode *)malloc (sizeof (headnode));
if(ptr==NULL)
{
perror("malloc");exit(EXIT_FAILURE);
}
ptr->head = NULL;
ptr->rear = NULL;
struct dirent *entp = NULL;
while ( NULL != (entp =readdir(dirp)))
{
if (strcmp(entp->d_name, "..")==0 || strcmp(entp->d_name, ".")==0) //ignore ./ ../
{
continue;
}
else
{
filenode *temp = (filenode *)malloc(sizeof(filenode));
if(temp==NULL)
{
perror("malloc");exit(EXIT_FAILURE);
}
temp->next = NULL;
temp->level = level;
temp->name = (char *)malloc(strlen( entp->d_name) + 1);
sprintf(temp->name ,"%s\0", entp->d_name);
temp->fullname = (char *)malloc(strlen(dir)+1+strlen( entp->d_name) + 1);
sprintf(temp->fullname,"%s/%s\0",dir,entp->d_name);
insert_sort(ptr,temp);
}
}
closedir(dirp);
return ptr;
}
/*
type:1, regufile. 2 dir.
*/
// 输出某个文件的信息格式
void out_file_info(filenode *ptr, int type)
{
int i;
printf("|");
for(i = 0;i < ptr->level; i++)
{
printf(" ");
}
printf("|-- ");
printf("%s\n",ptr->name);
}
void pop_file_tree(headnode *link_stack)
{
while(link_stack->head != NULL ) // 遍历所有节点
{
struct stat stat_src;
if (lstat(link_stack->head->fullname, &stat_src) != 0)
{
fprintf(stderr, "%s(%d): stat error(%s)!\n", __FILE__, __LINE__, strerror(errno));
}
if(S_ISDIR(stat_src.st_mode)) // 目录,调用dir_tree函数
{
filenode *temp = link_stack->head;
link_stack->head = link_stack->head->next;
if(link_stack->head == NULL)
link_stack->rear ==NULL;
out_file_info(temp,DIR_FILE);
dir_tree(temp->fullname,link_stack,temp->level);
free(temp->name);
free(temp->fullname);
free(temp);
}
else // 否则输出文件
{
filenode *temp = link_stack->head;
link_stack->head = link_stack->head->next;
if(link_stack->head == NULL)
link_stack->rear ==NULL;
out_file_info(temp,REGU_FILE);
free(temp->name);
free(temp->fullname);
free(temp);
}
}
}
// 输出目录和子目录信息递归函数
void dir_tree(char *dirname,headnode *link_stack, int level)
{
headnode *ret = NULL;
// 读取目录信息,返回一个排序队列,队列中每个节点包括子文件的文件名和整个路径
ret = read_dir_to_link_stack(dirname, level+1);
// 将原队列与新的返回队列合并,将新的返回队列放到原队列前,类似于栈操作
if(link_stack->head != NULL && ret->head != NULL)
{
ret->rear->next = link_stack->head;
link_stack->head = ret->head;
}
if(link_stack->head == NULL && ret->head != NULL )
link_stack->head = ret->head;
if(link_stack->rear == NULL && ret->rear != NULL)
link_stack->rear = ret->rear;
free(ret);
pop_file_tree(link_stack);
}
int main(int argc,char *argv[])
{
if (argc != 2 ) // 如果没有给定目录
{
fprintf(stderr,"pls useage %s dir_name\n",argv[0]);exit(EXIT_FAILURE);
}
struct stat stat_src;
if (lstat(argv[1], &stat_src) != 0) // 读取文件属性
{
fprintf(stderr, "%s(%d): stat error(%s)!\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (S_ISREG(stat_src.st_mode)) //regular file,报错
{
fprintf(stderr, "%s [error opening dir]\n",argv[1]);exit(EXIT_FAILURE);
}
else if(S_ISDIR(stat_src.st_mode)) // 目录文件
{
headnode *link_stack = (headnode *)malloc (sizeof (headnode));
if(link_stack == NULL)
{
perror("malloc");exit(EXIT_FAILURE);
}
link_stack->head = NULL; // 初始化全局队列
link_stack->rear = NULL;
printf("%s\n",argv[1]);
dir_tree(argv[1], link_stack,-1); // 执行函数
free(link_stack); // 完毕,回收内存
}
else
return 1;
}
运行结果: