ls实现的一些思路(很多来自于书上和其他人的建议)
- 首先就是解析参数,对于参数的解析,可以采用或运算来定义,比如将没有参数定义为0,将-a定义为1,将-l定义为2,将-R定义为4等,这样的优点实质就是采取了二进制位的或运算的优势,对与每一位刚好代表不同的参数,这样就可以将所有的参数保存在一个int型的数中,当后面要查看是否有某个参数时,只需要判断一下这一位是否是1,即就是在使用一次与运算来得到结果。
- 对于目录的解析与参数是类似的,但是首先就是要将目录的参数与ls的参数(-a, -l, -R)能够区分开来,那么一个明显的标志就是ls命令本身的参数在之前是带有字符 ‘-’ 的,因而我们可以很方便的将两种参数区分开来,从而获得路径的参数
- 对于显示,应当是由目录的解析开始,对于目录,我们可以使用opendir()函数来进行读取从而得到这一个文件夹中文件的名称,并将其使用 strcat()函数连接在path之后,组合成为带有文件名的pathname,其实到这里就等于将一个文件的完整路径包括文件名在内的完整信息的到了,然后反复利用opendir函数得到本目录下所有的完整文件路径将其存入一个二维数组当中。
- 对于不同的参数不同的处理。刚开始两个参数时switch还比较好写,后来不断增加,case语句按阶乘增加,导致写不下去。后来听取了别人的意见,分开处理。比如说i,a,l是与文件的信息打印有关系,但是和整体的打印没有太大的关系,所以只在打印单个文件的函数里处理。而t,r是关于时间排序和倒序输出,与单个文件打印无关,只是整体文件的顺序,所以在打印文件函数里处理。
- 关于-R,我是采取的递归处理,判断是不是文件,如果是文件,则递归,不然就打印文件。(ps:.和..文件不能递归,无论是自递归还是向上递归,都很容易让系统资源耗尽。)
- 对于错误处理,-R时有时要错过一些错误处理,有些不必要的错误处理会导致函数结束,即使用sudo也会有文件无法打开,它只允许指定的用户打开,当errno==13直接跳过。在递归根目录时,用malloc去开辟空间,对空间比较大,而且用完之后可以释放。
7.递归时需要对文件名进行处理,最后加上’/’,否则会读到错误的文件名,使递归异常终止 - 染色,用的一个颜色打印函数来表示,下面是Linux下的一些特殊颜色集
格式: echo -e "\033[字背景颜色;字体颜色m字符串\033[0m"
例如:
echo -e "\033[41;36m something here \033[0m"
其中41的位置代表底色, 36的位置是代表字的颜色
那些ascii code 是对颜色调用的始末.
\033[ ; m …… \033[0m
字背景颜色范围:40----49
40:黑
41:深红
42:绿
43:黄色
44:蓝色
45:紫色
46:深绿
47:白色
字颜色:30-----------39
30:黑
31:红
32:绿
33:黄
34:蓝色
35:紫色
36:深绿
37:白色
===============================================ANSI控制码的说明
\33[0m 关闭所有属性
\33[1m 设置高亮度
\33[4m 下划线
\33[5m 闪烁
\33[7m 反显
\33[8m 消隐
\33[30m -- \33[37m 设置前景色
\33[40m -- \33[47m 设置背景色
\33[nA 光标上移n行
\33[nB 光标下移n行
\33[nC 光标右移n行
\33[nD 光标左移n行
\33[y;xH设置光标位置
\33[2J 清屏
\33[K 清除从光标到行尾的内容
\33[s 保存光标位置
\33[u 恢复光标位置
\33[?25l 隐藏光标
\33[?25h 显示光标
代码很烂,很长,架构来自于书上,也借鉴很多其他人的意见和思路
不过还是写完了^_^
/*************************************************************************
> File Name: my_ls.c
> Author: YinJianxiang
> Mail: YinJianxiang123@gmail.com
> Created Time: 2017年07月20日 星期四 11时32分26秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<errno.h>
#include<pwd.h>
#include<linux/limits.h>
#include<time.h>
#include<grp.h>
#include<dirent.h>
#define PARAM_NONE 0 //r
#define PARAM_A 1 //a
#define PARAM_L 2 //l
#define PARAM_I 4 //i
#define PARAM_T 8 //t
#define PARAM_R_ 16 //r
#define PARAM_R 32 //R
#define MAXROWLEN 80
int g_leavelen = MAXROWLEN;
int g_maxlen;
#define NORMAL 0
#define GREEN 1
#define BLUE 2
#define S_BLUE 3
#define YELLOW 4
void display_dir(int flag_param,char *path);
/*简单错误处理*/
void my_err(const char *err_string,int line) {
fprintf(stderr,"line:%d ",line);
perror(err_string);
if(errno != 13) {
exit(0);
} else {
printf("无权限\n");
}
}
/*颜色打印包裹函数,之区分了可执行文件,目录和普通文件*/
void printfColor(char *name,int color) {
if(color == GREEN) {
printf("\033[;32m %-s\033[0m" "",name);
} else if(color == BLUE){
printf("\033[;36m %-s\033[0m" "",name);
} else if(color == NORMAL){
printf(" %-s",name);
}
}
/*
* 输出文件信息 -l
* 文件信息结构体
* 文件名
* 颜色
* */
void display_attribute(struct stat buf,char *name,int color) {
char buf_time[32];
struct passwd *psd;
struct group *grp;
//文件类型
if(S_ISLNK(buf.st_mode)){ //符号链接
printf("l");
} else if(S_ISREG(buf.st_mode)){ //一般文件
printf("-");
} else if(S_ISDIR(buf.st_mode)){ //目录文件
printf("d");
color = BLUE;
} else if(S_ISCHR(buf.st_mode)){ //字符设备文件
printf("c");
} else if(S_ISBLK(buf.st_mode)){ //块设备文件
printf("b");
} else if(S_ISFIFO(buf.st_mode)){ //先进先出文件
printf("f");
} else 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("\t");
//通过用户和组id得到用户的信息和其所在组的信息
psd = getpwuid(buf.st_uid);
grp = getgrgid(buf.st_gid);
printf("%4ld ",buf.st_nlink); //打印文件的硬链接数
printf("%-8s ",psd->pw_name); //打印用户的名字
printf("%-8s", grp->gr_name); //打印用户组的名字
printf("%6ld", buf.st_size); //打印文件大小
strcpy(buf_time,ctime(&buf.st_mtime)); //把时间转换成普通表示格式
buf_time[strlen(buf_time)-1] = '\0'; //去掉换行符
printf(" %s", buf_time); //输出时间
printfColor(name,color); //颜色打印
printf("\n");
}
/*
* 没有-l时的输出
* 参数
* 名字
* 颜色
*
*/
void display_single(char *name,int color) {
int i;
int len;
if(g_leavelen < g_maxlen) { //剩余空间不够时换行
printf("\n");
g_leavelen = MAXROWLEN;
}
//算出空格的个数,用最大长度对齐
len = strlen(name);
len = g_maxlen - len;
printfColor(name,color);
//打印空格
for(i = 0;i < len;i++) {
printf(" ");
}
printf(" ");
//多减去的2是空格的个数
g_leavelen -= (g_maxlen + 2);
}
/*
* 含参数i时的打印
* 参数
* 文件名
* 颜色
* */
void display_inode(char *name,int color) {
int i;
int len;
struct stat buf;
//剩余空间不够时换行
if(g_leavelen < g_maxlen) {
printf("\n");
g_leavelen = MAXROWLEN;
}
//获取文件信息
if(lstat(name,&buf) == -1) {
my_err("lstat",__LINE__);
}
//打印inode编号
printf("%ld ",buf.st_ino);
//算出空格的个数
len = strlen(name);
len = g_maxlen - len;
printfColor(name,color);
//打印空格
for(i = 0;i < len;i++) {
printf(" ");
}
printf(" ");
//因为inode号和空格多减了8
g_leavelen -= (g_maxlen + 2 + 8);
}
/*
* 显示文件函数
* 参数
* 权限
* 文件名
*
* */
void display(int flag,char *pathname) {
int i;
int j;
struct stat buf;
char name[PATH_MAX + 1];
int color = NORMAL;
//获取文件名称
for(i = 0,j = 0;i < strlen(pathname);i++) {
if(pathname[i] == '/') {
j = 0;
continue;
}
name[j++] = pathname[i];
}
name[j] = 0;
//获取文件信息
lstat(pathname,&buf);
//判断颜色
if(S_ISDIR(buf.st_mode)) {
color = BLUE;
}
if((buf.st_mode & S_IXUSR) && color != BLUE) {
color = GREEN;
}
if(flag & PARAM_T) {
flag -= PARAM_T;
}
if(flag & PARAM_R) {
flag -= PARAM_R;
}
if(flag & PARAM_R_) {
flag -= PARAM_R_;
}
/*-a显示本目录和上一级目录,-i显示node,-l显示详细信息*/
switch(flag) {
case PARAM_NONE: //没有参数
if(name[0] != '.') {
display_single(name,color);
}
break;
case PARAM_L: //-l
if(name[0] != '.') {
display_attribute(buf,name,color);
}
break;
case PARAM_A: //-a
display_single(name,color);
break;
case PARAM_I: //-i
if(name[0] != '.') {
display_inode(name,color);
}
break;
case PARAM_A + PARAM_L: //-la
display_attribute(buf,name,color);
break;
case PARAM_A + PARAM_I: //-ia
display_inode(name,color);
break;
case PARAM_L + PARAM_I: //-il
if(name[0] != '.') {
printf(" %ld",buf.st_ino);
display_dir(flag,name);
}
break;
case PARAM_A + PARAM_L + PARAM_I: //-ial
printf("%ld ",buf.st_ino);
display_dir(flag,name);
break;
default:
break;
}
}
/*
* 显示目录中的文件
* 参数
* 权限
* 路径
*
* */
void display_dir(int flag_param,char *path) {
DIR *dir;
struct dirent *ptr;
int count = 0;
struct stat buf;
//char filename[2000][PATH_MAX + 1];
char **filename;
filename = (char **)malloc(sizeof(char *) * 20000);
int t;
for(t = 0;t < 20000;t++) {
filename[t] = (char *)malloc(PATH_MAX+1);
}
long *filetime;
filetime = (long *)malloc(sizeof(long) * 20000);
char temp[PATH_MAX];
//long filetime[2000];
long timeTemp;
/*用malloc来分配堆空间,堆空间可以开比较大*/
dir = opendir(path); //打开目录
if(dir == NULL){
my_err("opendir",__LINE__);
}
/*获取目录下文件的个数,以及最大长度*/
while((ptr = readdir(dir))!= NULL){
if(g_maxlen < strlen(ptr->d_name))
g_maxlen = strlen(ptr->d_name);
count++;
}
closedir(dir);
if(count > 20000) {
my_err("too many files under this directory",__LINE__);
}
int i;
int j;
int len = strlen(path);
dir = opendir(path);
/*获取目录下文件名和时间参数*/
for(i = 0;i < count;i++) {
ptr = readdir(dir);
if(ptr == NULL) {
my_err("readdir",__LINE__);
}
strncpy(filename[i],path,len);
filename[i][len] = 0;
strcat(filename[i],ptr->d_name);
filename[i][len + strlen(ptr->d_name)] = 0;
lstat(filename[i],&buf);
filetime[i] = buf.st_mtime;
}
closedir(dir);
//排序
if(flag_param & PARAM_T){ //含t按时间排序
for(i = 0;i < count - 1;i++) {
for(j = 0;j < count - 1 -i;j++) {
if(filetime[j] < filetime[j+1]) {
timeTemp = filetime[j];
filetime[j] = filetime[j+1];
filetime[j+1] = timeTemp;
strcpy(temp,filename[j]);
strcpy(filename[j],filename[j+1]);
strcpy(filename[j+1],temp);
}
}
}
} else { //按文件名排序
for(i = 0;i < count - 1;i++) {
for(j = 0;j < count - 1 -i;j++) {
if(strcmp(filename[j],filename[j+1]) > 0) {
strcpy(temp,filename[j]);
strcpy(filename[j],filename[j+1]);
strcpy(filename[j+1],temp);
}
}
}
}
if(flag_param & PARAM_R) { //如果含r倒序输出
if(flag_param & PARAM_R_) {
for(i = count - 1;i >= 0;i--) {
display(flag_param,filename[i]);
}
for(i = count - 1;i >= 0;i--) {
if((stat(filename[i],&buf)) == -1) {
my_err("stat",__LINE__);
}
//判断文件是否是目录
if(S_ISDIR(buf.st_mode)) {
len = strlen(filename[i]);
//.和..不能自递归
if(filename[i][len-1] == '.' && filename[i][len-2] == '/' ||
filename[i][len-1] == '.' && filename[i][len-2] == '.' && filename[i][len-3] == '/') {
continue;
}
printf("\n\n%s:",filename[i]);
len = strlen(filename[i]);
strcat(filename[i],"/"); //一定要加上
display_dir(flag_param,filename[i]);
} else {
display(flag_param,filename[i]);
}
}
} else {
for(i = 0;i < count;i++) {
stat(filename[i],&buf);
//没有进行错误处理,选择直接打印,一旦错误处理之后,会导致后面无法进行
if(S_ISDIR(buf.st_mode)) {
len = strlen(filename[i]);
if(filename[i][len-1] == '.' && filename[i][len-2] == '/' ||
filename[i][len-1] == '.' && filename[i][len-2] == '.' && filename[i][len-3] == '/') {
continue;
}
printf("\n\n%s:\n",filename[i]);
len = strlen(filename[i]);
strcat(filename[i],"/");
display_dir(flag_param,filename[i]);
} else {
display(flag_param,filename[i]);
}
}
}
}else {
if(flag_param & PARAM_R_) {
for(i = count - 1;i >= 0;i--) {
display(flag_param,filename[i]);
}
} else {
for(i = 0;i < count;i++) {
display(flag_param,filename[i]);
}
}
}
if((flag_param & PARAM_L) == 0) {
printf("\n");
}
for(i = 0;i < 20000;i++) {
free(filename[i]);
}
free(filename);
free(filetime);
}
int main(int argc, char *argv[]) {
int i;
int j;
int k;
int num;
char path[PATH_MAX+1];
char param[32];
int flag_param = PARAM_NONE;
struct stat buf;
j = 0;
num = 0;
//解析其参数,以-为标志
for(i = 1; i < argc;i++) {
if(argv[i][0] == '-') {
for(k = 1;k < strlen(argv[i]);k++,j++) {
param[j] = argv[i][k];
}
num++;
}
}
//利用位运算确定具体参数
for(i = 0;i < j;i++) {
if(param[i] == 'a'){
flag_param |= PARAM_A;//利用位运算
continue;
}
else if(param[i] == 'l'){
flag_param |= PARAM_L;
continue;
}
else if(param[i] == 'i'){
flag_param |= PARAM_I;
continue;
}
else if(param[i] == 'r'){
flag_param |= PARAM_R_;
continue;
}
else if(param[i] == 't'){
flag_param |= PARAM_T;
continue;
}else if(param[i] == 'R'){
flag_param |= PARAM_R;
continue;
}
else{
printf("my_ls: incalid option -%c\n", param[i]);
exit(1);
}
}
param[j] = 0;
//默认为当前路径
if((num+1) == argc) {
strcpy(path,"./");
path[2] = 0;
display_dir(flag_param,path);
return 0;
}
i = 1;
do{
if(argv[i][0] == '-') {
i++;
continue;
} else {
//得到具体路径
strcpy(path, argv[i]);
if(stat(path,&buf) == -1)
my_err("stat",__LINE__);
//判断是否为目录文件
if(S_ISDIR(buf.st_mode)) {
//如果目录最后忘记了/则加上
if(path[strlen(argv[i]) - 1] != '/') {
path[strlen(argv[i])] = '/';
path[strlen(argv[i])+1] = 0;
}
else
path[strlen(argv[i])] = 0;
display_dir(flag_param,path);//按照目录输出
i++;
} else {
//按照文件输出
display(flag_param,path);
i++;
}
}
} while(i < argc);
return 0;
}