主要功能
1.实现-l,-a,-R,-r
2.实现按文件类型以不同颜色显示
主要知识
1.各种有关文件的系统调用函数
函数具体内容以及用法详情参考Linux_c编程实战第六章
2.运用二进制的思想来储存与判断参数
对于一个参数来讲只有两种结果:存在或者不存在。因此我们可以用一个二进制数的一位来表示一个参数,1代表存在,0代表不存在。因此在代码中使用了四位的二进制数来表示四种参数,0001(第一位)0010(第二位)0100(第三位)1000(第四位)分别表示参数a,l,R,r的存在。
3.递归的应用
因为R参数要求打印目录以及其所有子目录的文件信息,对于每一个目录文件来说都要打印其文件信息,也就是在所有目录文件下都要做同一件事情。因此采用递归来实现。(使用栈也可)
4.打印指定颜色字体
格式
printf("\033[字背景颜色;字体颜色m字符串\033[0m" );
部分颜色代码:
字背景颜色: 40–49 字颜色: 30–39
40: 黑 30: 黑
41: 红 31: 红
42: 绿 32: 绿
43: 黄 33: 黄
44: 蓝 34: 蓝
45: 紫 35: 紫
46: 深绿 36: 深绿
47:白色 37:白色
具体代码以及注释
my_ls.h
#ifndef _MY_LS_H
#define _MY_LS_H
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/limits.h>
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <errno.h>
//采用二进制思想解析参数
#define PARAM_NONE 0
#define PARAM_A 1
#define PARAM_L 2
#define PARAM_R 4
#define PARAM_r 8
#define MAXROWLEN 200
#define NORMAL 0
#define GREEN 1
#define BLUE 2
#define S_BLUE 3
#define YELLOW 4
int g_leave_len =MAXROWLEN;
int g_maxlen;
void display_dir(int flag_p,char *path);
void display(int flag,char *pathname,int color);
void print_fname(char *name,int color);
void print_finfo(struct stat buf,char *name);
void my_err(const char *str,int line);
void display_Subdir(int flag_p,int j,char **dirs);
int display_color(struct stat buf);
void print_color(char *name,int color);
#endif
my_ls.c
#include <stdio.h>
#include "my_ls.h"
int main(int argc,char *argv[])
{
int k=0;
int num=0;
char param[32];
int flag_p=PARAM_NONE;
//记录参数
for(int i=1;i<argc;i++){
if(argv[i][0]=='-'){
for(int j=1;j<strlen(argv[i]);j++){
param[k]=argv[i][j];
k++;
}
num++;//记录选项参数个数,方便确认是否指定目录
}
}
//将参数转化为数字标记
for(int i=0;i<k;i++){
if(param[i]=='a'){
flag_p|=PARAM_A;
continue;
}else if(param[i]=='l'){
flag_p|=PARAM_L;
continue;
}else if(param[i]=='R'){
flag_p|=PARAM_R;
continue;
}else if(param[i]=='r'){
flag_p|=PARAM_r;
continue;
}else{
printf("参数错误");
}
}
param[k]=0;
char path[PATH_MAX+1];//储存目录名
//若未指定目录则执行
if(num==argc-1){
strcpy(path,"./");
path[2]=0;
display_dir(flag_p,path);
if((flag_p & PARAM_R)!=0 && (flag_p & PARAM_L)==0){
printf("\n");
}
return 0;
}
//若参数中含有目录项
int i=1;
struct stat buf;
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_p,path);
i++;
}
else{
display(flag_p,path,0);
i++;
}
}while(i<argc);
if((flag_p & PARAM_R)!=0 && (flag_p & PARAM_L)==0){
printf("\n");
}
return 0;
}
void display_dir(int flag_p,char *path)
{
//打开目录
DIR *dir;
if((dir=opendir(path))==NULL){
my_err("opendir",__LINE__);
}
//获取该目录下文件总数和最长文件名的长度
struct dirent *ptr;
int count=0;
while((ptr=readdir(dir))!=NULL){
if(g_maxlen<strlen(ptr->d_name)){
g_maxlen=strlen(ptr->d_name);
}
count++;
}
if(count>20000){
my_err("too many files under this directory",__LINE__);
}
closedir(dir);
//获取该目录下所有文件名并与其路径名储存
char **f_names;
f_names=(char **)malloc(sizeof(char *)*20000);
for(int i=0;i<20000;i++){
f_names[i]=(char *)malloc(PATH_MAX+1);
}
int path_len=strlen(path);
if((dir=opendir(path))==NULL){
my_err("opendir",__LINE__);
}
for(int i=0;i<count;i++){
ptr=readdir(dir);
strncpy(f_names[i],path,path_len);
f_names[i][path_len]=0;
strcat(f_names[i],ptr->d_name);
f_names[i][path_len+strlen(ptr->d_name)]=0;
}
closedir(dir);
//使用冒泡法对文件名排序
char temp[PATH_MAX+1];
for(int i=0;i<count-1;i++){
for(int j=0;j<count-1-i;j++){
if(strcmp(f_names[j],f_names[j+1])>0){
strcpy(temp,f_names[j]);
temp[strlen(f_names[j])]=0;
strcpy(f_names[j],f_names[j+1]);
f_names[j][strlen(f_names[j+1])]=0;
strcpy(f_names[j+1],temp);
f_names[j+1][strlen(temp)]=0;
}
}
}
//遍历并调用函数打印文件
char **dirs;
dirs=(char **)malloc(sizeof(char *)*20000);
for(int i=0;i<20000;i++){
dirs[i]=(char *)malloc(PATH_MAX+1);
}
struct stat buf;
int j=0;
//若有-R选项先打印路径名
if((flag_p & PARAM_R)!=0){
printf("%s:\n",path);
}
//将行剩余空间设置为最大值
g_leave_len=MAXROWLEN;
if((flag_p & PARAM_r)==0){
for(int i=0;i<count;i++){
//如果参数含有-R选项
if((flag_p & PARAM_R)!=0){
//获取文件名
int k=0;
char name[NAME_MAX+1];
for(int a=0;a<strlen(f_names[i]);a++){
if(f_names[i][a]=='/'){
k=0;
continue;
}
name[k]=f_names[i][a];
k++;
}
name[k]=0;
//记录目录文件
memset(&buf,0,sizeof(buf));
if(lstat(f_names[i],&buf)==-1){
my_err("lstat",__LINE__);
}
if(S_ISDIR(buf.st_mode)){
if(name[0]!='.'){
strcpy(dirs[j],f_names[i]);
if(dirs[j][strlen(dirs[j])-1]!='/'){
dirs[j][strlen(dirs[j])]='/';
dirs[j][strlen(dirs[j])+1]=0;
}
j++;
}
}
}
memset(&buf,0,sizeof(buf));
if(lstat(f_names[i],&buf)==-1){
my_err("lstat",__LINE__);
}
int color=display_color(buf);
display(flag_p,f_names[i],color);
}
}
if((flag_p & PARAM_r)!=0){
for(int i=count-1;i>=0;i--){
//如果参数含有-R选项
if((flag_p & PARAM_R)!=0){
//获取文件名
int k=0;
char name[NAME_MAX+1];
for(int a=0;a<strlen(f_names[i]);a++){
if(f_names[i][a]=='/'){
k=0;
continue;
}
name[k]=f_names[i][a];
k++;
}
name[k]=0;
//记录目录文件
memset(&buf,0,sizeof(buf));
if(lstat(f_names[i],&buf)==-1){
my_err("lstat",__LINE__);
}
if(S_ISDIR(buf.st_mode)){
if(name[0]!='.'){
strcpy(dirs[j],f_names[i]);
if(dirs[j][strlen(dirs[j])-1]!='/'){
dirs[j][strlen(dirs[j])]='/';
dirs[j][strlen(dirs[j])+1]=0;
}
j++;
}
}
}
memset(&buf,0,sizeof(buf));
if(lstat(f_names[i],&buf)==-1){
my_err("lstat",__LINE__);
}
int color=display_color(buf);
display(flag_p,f_names[i],color);
}
}
//若参数中有-R选项
if((flag_p & PARAM_R)!=0){
display_Subdir(flag_p,j,dirs);
}
if((flag_p & PARAM_L)==0 && (flag_p & PARAM_R)==0){
printf("\n");
}
free(f_names);
free(dirs);
}
void display(int flag,char *pathname,int color)
{
//从路径名中解析出文件名
int j=0;
char name[NAME_MAX+1];
for(int i=0;i<strlen(pathname);i++){
if(pathname[i]=='/'){
j=0;
continue;
}
name[j++]=pathname[i];
}
name[j]=0;
//获取文件信息并储存
struct stat buf;
if(lstat(pathname,&buf)==-1){
my_err("lstat",__LINE__);
}
//根据命令参数调用不同函数打印文件信息
flag=flag & (~PARAM_r);
switch(flag){
case PARAM_NONE :
if(name[0]!='.'){
print_fname(name,color);
}
break;
case PARAM_A :
print_fname(name,color);
break;
case PARAM_L :
if(name[0]!='.'){
print_finfo(buf,name);
printf(" ");
print_color(name,color);
printf("\n");
}
break;
case PARAM_R :
if(name[0]!='.'){
print_fname(name,color);
}
break;
case PARAM_A+PARAM_L :
print_finfo(buf,name);
printf(" ");
print_color(name,color);
printf("\n");
break;
case PARAM_A+PARAM_R :
print_fname(name,color);
break;
case PARAM_L+PARAM_R :
if(name[0]!='.'){
print_finfo(buf,name);
printf(" ");
print_color(name,color);
printf("\n");
}
break;
case PARAM_A+PARAM_L+PARAM_R :
print_finfo(buf,name);
printf(" ");
print_color(name,color);
printf("\n");
break;
}
}
void print_fname(char *name,int color)
{
//判断本行是否有充足空间打印
if(g_leave_len<g_maxlen){
printf("\n");
g_leave_len=MAXROWLEN;
}
//对齐输出
int len=g_maxlen-strlen(name);
print_color(name,color);
for(int i=0;i<len;i++){
printf(" ");
}
printf(" ");
//实时记录行剩余空间
g_leave_len-=(g_maxlen+2);
}
void print_finfo(struct stat buf,char *name)
{
//打印文件类型和文件访问权限
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");
}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)){
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(" ");
//打印硬链接数
printf("%4ld ",buf.st_nlink);
//打印文件所有者以及所在组
struct passwd *psd=psd=getpwuid(buf.st_uid);//id转为名字
struct group *grp=getgrgid(buf.st_gid);
printf("%-8s",psd->pw_name);
printf("%-8s",grp->gr_name);
//打印文件大小
printf("%6ld",buf.st_size);
//打印文件创建时间
char buf_time[32];
strcpy(buf_time,ctime(&buf.st_mtime));
buf_time[strlen(buf_time)-1]=0;
printf(" %s",buf_time);
}
void display_Subdir(int flag_p,int j,char **dirs)
{
for(int i=0;i<j;i++){
if((flag_p & PARAM_L)==0){
printf("\n");
}
display_dir(flag_p,dirs[i]);
}
}
int display_color(struct stat buf)
{
int color=0;
if(S_ISDIR(buf.st_mode)) {
color = BLUE;
}
if((buf.st_mode & S_IXUSR) && color != BLUE) {
color = GREEN;
}
return color;
}
void print_color(char *name,int color)
{
if(color == GREEN) {
printf("\033[1m\033[32m%-s\033[0m",name);
} else if(color == BLUE){
printf("\033[1m\033[34m%-s\033[0m",name);
} else if(color == NORMAL){
printf("%-s",name);
}
}
void my_err(const char *str,int line)
{
fprintf(stderr,"line:%d",line);
perror(str);
exit(1);
}