MY_LS(-a -l -R)
一、主要函数
1.opendir
头文件:
#include<sys/types.h>
#include<dirent.h>
函数原型:
DIR* opendir (const char * path );
(获取path子目录下的所由文件和目录的列表,如果path是个文件则返回值为NULL)
功能:
打开一个目录,在失败的时候返回一个空的指针。
返回值(DIR):
DIR 结构体的原型为:struct_dirstream
struct __dirstream
{
void *__fd;
char *__data;
int __entry_data;
char *__ptr;
int __entry_ptr;
size_t __allocation;
size_t __size;
__libc_lock_define (, __lock)
};
typedef struct __dirstream DIR;
2.readdir
头文件:
#include<dirent.h>
函数原型:
struct dirent* readdir(DIR* dir_handle);
功能:
读取opendir 返回值的那个列表
返回值:
返回dirent结构体指针,dirent结构体成员如下,(文件和目录都行)
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
3.stat / lstat
头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
函数原型:
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
第一个形参:指出文件(文件路径); 第二个形参:出参数(函数对该参数操作,然后传出)。
区别:
lstat函数的形参跟stat函数的形参一样。其功能也跟stat函数功能一样,仅有一点不同:stat函数是穿透(追踪)函数,即对软链接文件进行操作时,操作的是链接到的那一个文件,不是软链接文件本身;而lstat函数是不穿透(不追踪)函数,对软链接文件进行操作时,操作的是软链接文件本身。
返回值:
成功返回0,失败返回-1,并且将详细错误信息赋值给errno全局变量。
struct stat {
dev_t st_dev; /* 文件的设备编号 */
ino_t st_ino; /* 索引结点编号 */
mode_t st_mode; /* 文件类型和权限*/
nlink_t st_nlink; /*硬链接数 */
uid_t st_uid; /*用户ID*/
gid_t st_gid; /* 组ID*/
dev_t st_rdev; /* 设备类型(若此文件为设备文件,则为设备编号*/
off_t st_size; /* 文件大小*/
blksize_t st_blksize; /*文件系统的I/O缓冲区大小*/
blkcnt_t st_blocks; /* 块数 */
time_t st_atime; /* 访问时间 */
time_t st_mtime; /* 修改时间 */
time_t st_ctime; /* 更改时间 */
};
4.getcwd
头文件:
#include <unistd.h>
函数原型:
char * getcwd(char * buf, size_t size);
功能:
getcwd()会将当前的工作目录绝对路径复制到参数buf 所指的内存空间,参数size 为buf 的空间大小。
返回值:
执行成功则将结果复制到参数buf 所指的内存空间, 或是返回自动配置的字符串指针. 失败返回NULL,错误代码存于errno.
注:
1、在调用此函数时,buf 所指的内存空间要足够大。若工作目录绝对路径的字符串长度超过参数size 大小,则返回NULL,errno 的值则为ERANGE。
2、倘若参数buf 为NULL,getcwd()会依参数size 的大小自动配置内存(使用malloc()),如果参数size 也为0,则getcwd()会依工作目录绝对路径的字符串程度来决定所配置的内存大小,进程可以在使用完次字符串后利用free()来释放此空间。
5.chdir
头文件:
#include <unistd.h>
函数原型:
int chdir(const char * path);
功能:
chdir()用来将当前的工作目录改变成以参数path 所指的目录.
返回值:
执行成功则返回0, 失败返回-1, errno 为错误代码.
二、主体思路
通过《LinuxC编程实战》的学习,先对ls命令有初步整体的认识,在研究每一个参数各自的实现过程。
1.先解析命令行参数,判断是否有-a -l -R以及是否有目录或文件。
2.-a可以通过文件名首是否为".“判断,不用单独函数实现。
3.-l用lstat函数解析,打印即可。
4.-R的实现需要递归调用,我遇到的问题是一定要在切换到当前目录再进行递归,否则找不到目录或文件。一定要排除” . " " … "的可能性。一定要在适当的位置返回上一层目录。
三、实现代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<errno.h>
#include<dirent.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include <signal.h>
#define PARAM_NONE 0
#define PARAM_A 1
#define PARAM_L 2
#define PARAM_R 4
#define MAXLEN 80 //一行显示的最多字符数
#define PATH_MAX 2000
#define NAME_MAX 256
int g_leave_len = MAXLEN; //一行剩余长度,用于输出对齐
int g_maxlen; //存放某目录下最长文件名的长度
int flag = 0;
char *filename[50000];
//屏蔽ctrl+c
static void mask_ctrl_c() //???
{
sigset_t intmask;
sigemptyset(&intmask);//将信号集合设置为空
sigaddset(&intmask,SIGINT);//加入中断 Ctrl+C 信号
//阻塞信号
sigprocmask(SIG_BLOCK,&intmask,NULL);
}
//错误处理
void error(const char *err_string , int line)
{
fprintf(stderr,"line:%d",line);
perror(err_string);
exit(1);
}
//???
char * get_exe_path( char *path ,char * buf, int count)
{
int i;
int rslt = readlink(path, buf, count - 1);
if (rslt < 0 || (rslt >= count - 1))
{
return NULL;
}
buf[rslt] = '\0';
for (i = rslt; i >= 0; i--)
{
if (buf[i] == '/')
{
buf[i + 1] = '\0';
break;
}
}
return buf;
}
void display(char name[][NAME_MAX],int num)
{
int i,j=0;
for(i=0;i<num;i++)
{
j++;
printf("%-20s",name[i]);
if(j==5)
{
printf("\n");
j = 0;
}
}
printf("\n");
}
int display_attribute(struct stat buf ,char *name)
{
char time[64];
char hen[1024];
struct passwd *pwd; //从该结构体获取文件所有者的名字
struct group *grp; //从该结构体获取文件组名
//判断文件类型
if(S_ISLNK(buf.st_mode)) //判断是否为链接
printf("l");
if(S_ISREG(buf.st_mode)) //判断是否为一般文件
printf("-");
if(S_ISDIR(buf.st_mode)) //判断是否为目录
printf("d");
if(S_ISCHR(buf.st_mode)) //字符设备文件
printf("c");
if(S_ISBLK(buf.st_mode)) //块设备文件
printf("b");
if(S_ISFIFO(buf.st_mode)) //先进先出FIFO
printf("f");
if(S_ISSOCK(buf.st_mode)) //socket
printf("s");
//判断文件所有者权限
if((buf.st_mode & S_IRUSR))
printf("r");
else
printf("-");
if((buf.st_mode & S_IWUSR))
printf("w");
else
printf("-");
if((buf.st_mode & S_IXUSR))
printf("x");
else
printf("-");
//判断文件所有者同组的用户权限
if((buf.st_mode & S_IRGRP))
printf("r");
else
printf("-");
if((buf.st_mode & S_IWGRP))
printf("w");
else
printf("-");
if((buf.st_mode & S_IXGRP))
printf("x");
else
printf("-");
//判断其他用户权限
if((buf.st_mode & S_IROTH))
printf("r");
else
printf("-");
if((buf.st_mode & S_IWOTH))
printf("w");
else
printf("-");
if((buf.st_mode & S_IXOTH))
printf("x");
else
printf("-");
//打印链接数
printf(" %4d ",buf.st_nlink);
//打印文件用户和用户组
pwd = getpwuid(buf.st_uid);
grp = getgrgid(buf.st_gid);
printf("%-10s",pwd->pw_name);
printf("%-8s",grp->gr_name);
//打印文件大小
printf("%6lld",buf.st_size);
//打印文件最后一次修改的时间
strcpy(time,ctime(&buf.st_ctime));
time[strlen(time)-1] = '\0';
printf(" %s",time);
//打印文件名
if(S_ISLNK(buf.st_mode))
printf("%s\n",get_exe_path(name,(char*)hen,1024)); //???
else
printf(" %s\n",name);
return 0;
}
//ls -a ls
void display_dir(char * path)
{
DIR *dir;
int i = 0;
int number = 0;
char a[10000][256];
struct dirent *ptr;
dir = opendir(path);
if(dir == NULL)
error("opendir",__LINE__);
while((ptr = readdir(dir))!=NULL)
{
if(ptr->d_name[1] == '.' && flag == 0){
continue;
}
strcpy(a[i],ptr->d_name);
a[i][strlen(a[i])+1] = '\0';
i++;
number++;
}
display(a,number);
}
//ls -l -al
void display2(char* pathname) {
int i, j;
struct stat buf;
char name[NAME_MAX + 1];
//从路径解析文件名
for (i = 0, j = 0; i < strlen(pathname); i++) {
if (pathname[i] == '/') {
j = 0;
continue;
}
name[j++] = pathname[i]; //???
}
name[j] = '\0';
//用lstat解析链接文件
if (lstat(pathname, &buf) == -1) {
error("stat", __LINE__);
}
if (name[0] != '.' ) {
display_attribute(buf, name);
}
}
//ls -l -al
void display_dir2(char *path)
{
//DIR *dir;
int i = 0;
int number = 0;
DIR *dir2;
struct stat buf;
char a[10000][256];
char temp[PATH_MAX + 1];
struct dirent *ptr;
//dir = opendir(path);
DIR* dir = opendir(path);
if(dir == NULL)
{
perror("open dir error");
return ;
}
if(dir == NULL){
error("opendir",__LINE__);
}
while((ptr = readdir(dir)) != NULL){
/*if(ptr->d_name[1] == '.' && flag == 0)
continue;
if(lstat(ptr->d_name,&buf)<0)
continue;*/
//strcpy(a[i],ptr->d_name);
//a[i][strlen(ptr->d_name)+1] = '\0';
//i++;
number++;
}
closedir(dir);
int len = strlen(path);
dir = opendir(path);
for(i = 0;i < number;i++){
ptr = readdir(dir);
if(ptr == NULL){
error("opendir",__LINE__);
}
strcpy(a[i],path);
a[i][len] = '\0';
strcat(a[i],ptr->d_name);
a[i][len+strlen(ptr->d_name)] = '\0';
}
/*for(i=0;i<number;i++)
{
printf("%s",a[i]);
printf("\n");
if(lstat(a[i],&buf)==-1)
continue;
//printf("%s",a[i]);
//printf("\n");
if(S_ISDIR(buf.st_mode))
{
if((dir2 = opendir(a[i])) < 0)
continue;
}
display_attribute(buf,a[i]);
}*/
int j;
for(i = 0;i < number-1;j++) {
for (j = 0; j < number - 1 - i; j++) {
if (strcmp(a[j], a[j + 1]) > 0) {
strcpy(temp, a[j + 1]);
temp[strlen(a[j + 1])] = '\0';
strcpy(a[j + 1], a[j]);
a[j + 1][strlen(a[j])] = '\0';
strcpy(a[j], temp);
a[j][strlen(temp)] = '\0';
}
}
for (i = 0; i < number; i++) {
display2(a[i]);
}
closedir(dir);
}
}
void display_R(char *path)
{
DIR * dir;
struct dirent *ptr;
int i,count = 0;
struct stat buf;
char name[80];
int sign = 0;
printf("\n");
getcwd(name,sizeof(name));
printf("\n%s:\n",name);
chdir(path);
dir = opendir(path);
if(dir==NULL){
error("opendir",__LINE__);
}
while((ptr = readdir(dir))!=NULL){
count++;
}
char ** filenames=(char**)malloc(sizeof(char*)*20000);
for(int i=0;i<count;i++){
filenames[i]=(char*)malloc(sizeof(char)*PATH_MAX+1);
}
closedir(dir);
dir = opendir(path);
for(i=0;i<count;i++)
{
ptr = readdir(dir);
if(ptr == NULL){
printf("readdir error! line:%d",__LINE__);
}
if(strcmp(ptr->d_name,"..") != 0 && strcmp(ptr->d_name,".") != 0){
printf("%-15s ",ptr->d_name);
sign+=1;
}
if(sign == 4){
printf("\n");
sign = 0;
}
strcat(filenames[i],ptr->d_name);
}
for(i=0;i<count;i++){
/*if(lstat(filenames[i],&buf)==-1){
error("lstat",__LINE__);
printf("%s",filenames[i]);
return ;
}*/
if(strcmp(filenames[i],"..")==0)
continue;
if(strcmp(filenames[i],".")==0)
continue;
lstat(filenames[i],&buf);
if(S_ISDIR(buf.st_mode))
{
chdir(filenames[i]);
display_R("./");
chdir("../");
}
else if(!S_ISDIR(buf.st_mode))
{
continue;
}
}
for(i=0;i<count;i++){
free(filenames[i]);
}
free(filenames);
closedir(dir);
}
void display_LR(char *path)
{
DIR * dir;
struct dirent *ptr;
int i,count = 0;
struct stat buf;
int sign = 0;
char name[80];
printf("\n");
getcwd(name,sizeof(name));
printf("\n%s:\n",name);
chdir(path);
dir = opendir(path);
if(dir==NULL){
error("opendir",__LINE__);
}
while((ptr = readdir(dir))!=NULL){
count++;
}
char ** filenames=(char**)malloc(sizeof(char*)*20000);
for(int i=0;i<count;i++){
filenames[i]=(char*)malloc(sizeof(char)*PATH_MAX+1);
}
closedir(dir);
int len = strlen(path);
dir = opendir(path);
for(i=0;i<count;i++)
{
ptr = readdir(dir);
if(ptr == NULL){
error("opendir",__LINE__);
}
strcpy(filenames[i],path);
filenames[i][len] = '\0';
strcat(filenames[i],ptr->d_name);
filenames[i][len+strlen(ptr->d_name)] = '\0';
//lstat(filenames[i],&buf);
//display_attribute(buf ,filenames[i]);
}
for (i = 0; i < count; i++) {
display2(filenames[i]);
}
closedir(dir);
for(i=0;i<count;i++){
if(lstat(filenames[i],&buf)==-1){
error("lstat",__LINE__);
printf("%s",filenames[i]);
return ;
}
ptr = readdir(dir);
if(strcmp(ptr->d_name,".")==0)
continue;
if(strcmp(ptr->d_name,"..") == 0)
continue;
if(strcmp(ptr->d_name,"proc")==0)
continue;
if(ptr->d_name[0] == '.')
continue;
if(strcmp(ptr->d_name,"OS")==0)
continue;
lstat(filenames[i],&buf);
if(S_ISDIR(buf.st_mode))
{
chdir(filenames[i]);
display_LR("./");
chdir("../");
}
else if(!S_ISDIR(buf.st_mode))
{
continue;
}
//chdir("../");
}
for(i=0;i<count;i++){
free(filenames[i]);
}
free(filenames);
closedir(dir);
}
int main(int argc, char *argv[])
{
char path[PATH_MAX+1];
//char param[32];//保存命令行参数,目标文件和目录名不在此列
int flag_param = PARAM_NONE;//参数种类
struct stat buf;
int i,j,k,num=0;
mask_ctrl_c(); //ctrl+c屏蔽
//命令行参数
if(argc==1)
{
strcpy(path,"./");
display_dir(path);
return 0;
}
for(i=1;i<argc;i++){
if(argv[i][0] == '-'){
k = strlen(argv[i]);
for(j=0;j<k;j++)
{
if(argv[i][j] == 'a'){
flag_param |= PARAM_A;
continue;
}
if(argv[i][j] == 'l'){
flag_param |= PARAM_L;
continue;
}
if(argv[i][j] == 'R'){
flag_param |= PARAM_R;
continue;
}
}
}
else{
strcpy(path,argv[i]);
num++;
}
}
i--;
if(num !=0){
if(stat(path,&buf) == -1){
error("path",__LINE__);
}
if(S_ISDIR(buf.st_mode))
{
if(path[strlen(argv[i])-1] != '/')
{
path[strlen(argv[i])] = '/';
path[strlen(argv[i])+1] = '\0';
}
switch(flag_param)
{
case 1:
{
flag = 1;
display_dir(path);
break;
}
case 2:
{
flag = 0;
display_dir2(path);
break;
}
case 3:
{
flag = 1;
display_dir2(path);
break;
}
case 4:
{
display_R(path);
break;
}
case 5:
{
display_R(path);
break;
}
case 6:
{
display_LR(path);
break;
}
case 7:
{
display_LR(path);
break;
}
default:break;
}
}
else
{
lstat(path,&buf);
switch(flag_param)
{
case 1:
{
printf("%s\n",path);
break;
}
case 2:
{
display_attribute(buf,path);
break;
}
case 3:
{
display_attribute(buf,path);
break;
}
case 4:
{
printf("%s\n",path);
break;
}
case 5:
{
printf("%s\n",path);
break;
}
case 6:
{
display_attribute(buf,path);
break;
}
case 7:
{
display_attribute(buf,path);
break;
}
default:break;
}
}
}
else{
strcpy(path,"./");
switch(flag_param)
{
case 1:
{
flag = 1;
display_dir(path);
break;
}
case 2:
{
flag = 0;
display_dir2(path);
break;
}
case 3:
{
flag = 1;
display_dir2(path);
break;
}
case 4:
{
display_R(path);
break;
}
case 5:
{
flag = 1;
display_R(path);
break;
}
case 6:
{
display_LR(path);
break;
}
case 7:
{
flag = 1;
display_R(path);
break;
}
default:break;
}
}
return 0;
}
四、总结
从看书的毫无头绪到慢慢理解,是一个不断总结的过程。
目前代码不够简洁和规范,在malloc和free等处还有一些问题,会继续进行优化和调整。
未完待续,会更好!!