实现了ls , ls -a , ls -l , ls -al , ls -R.
ls -R中显示结果未排序。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<sys/stat.h>
#include<unistd.h>
#include<sys/types.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 //-a:显示所有文件
#define PARAM_L 2 //-l:一行只显示一个文件的详细信息
#define PARAM_R 5 //-R:显示当前目录下所有子目录及文件
#define MAXROWLEN 80 //一行显示的最多字符数
#define MAX_SIZE 1000 //能存储的最多路径名长度
int g_leave_len = MAXROWLEN; //一行剩余长度,用于输出对齐
int g_maxlen; //存放某目录下最长文件名的长度
int i = 1;
void my_err(const char *err_string, int line)
{
fprintf(stderr, "line: %d", line);
perror( err_string );
exit(1);
}
void display_attribute(struct stat buf, char *name)
{
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");
} 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(" ");
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); //打印文件的时间信息
}
/* 在没有使用-l选项时,打印一个文件名,打印时上下行对齐 */
void display_single( char *name )
{
int i, len;
//如果本行不足以打印一个文件名则换行
if( g_leave_len < g_maxlen ) {
printf("\n");
g_leave_len = MAXROWLEN;
}
len = strlen( name );
len = g_maxlen - len;
printf("%-s", name);
for(i = 0; i < len; i++) {
printf(" ");
}
printf(" ");
g_leave_len -= ( g_maxlen + 2 );
}
//在这里添加ls -R函数
int display_r( char *pathname1 )
{
struct stat buf;
DIR *dir;
struct dirent *ptr;
printf("\n%s:\n",pathname1);
dir = opendir( pathname1 );
while( ( ptr = readdir( dir )) != NULL ) {
if(ptr->d_name[0] != '.') {
display_single( ptr->d_name );
}
}
closedir( dir );
dir = opendir( pathname1 );
while(( ptr = readdir( dir )) != NULL) {
if(ptr->d_name[0] != '.') {
stat( ptr->d_name, &buf );
if( S_ISDIR( buf.st_mode ) ) {
char *temp = pathname1;
strcat( temp, "/" );
strcat( temp, ptr->d_name );
display_r(temp);
}
}
}
closedir( dir );
return 0;
}
//结束
/* 根据命令行参数和完整路径名显示目标文件
* 参数flag: 命令行参数
* 参数pathname: 包含了文件名的路径名 */
void display(int flag, char *pathname)
{
int i, j;
struct stat buf;
char name[NAME_MAX + 1];
char pathname1[MAX_SIZE];
for( i = 0, j = 0; i < strlen(pathname); i++ ) {
if( pathname[i] == '/' ) {
j = 0;
continue;
}
name[j] = pathname[i];
j++;
}
name[j] = '\0';
if( lstat( pathname, &buf ) == -1 ) {
my_err( "stat", __LINE__);
}
switch (flag) {
case PARAM_NONE: //没有-a和-l选项
if( name[0] != '.' ) {
display_single( name );
}
break;
case PARAM_A: //显示包括隐藏文件在内的所有文件
display_single( name );
break;
case PARAM_L: //每个文件单独占一行,显示文件的详细属性信息
if( name[0] != '.' ) {
display_attribute( buf, name );
printf(" %-s\n", name );
}
break;
case PARAM_A + PARAM_L: //同时有-a和-l两种选项的情况
display_attribute( buf, name );
printf(" %-s\n", name );
break;
//在这里添加ls -R的case
case PARAM_R:
getcwd( pathname1, MAX_SIZE );
display_r( pathname1 );
break;
default:
break;
}
}
void display_dir( int flag_param, char *path )
{
DIR *dir;
struct dirent *ptr;
int count = 0;
char filenames[256][PATH_MAX + 1], temp[PATH_MAX + 1];
//获取该目录下文件总数和最长的文件名
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 > 256 ) {
my_err("too many files under this dir", __LINE__);
}
int i, j, len = strlen( path );
//获取该目录下所有的文件名
dir = opendir( path );
for( i = 0; i < count; i++ ) {
ptr = readdir( dir );
if( ptr == NULL ) {
my_err("readdir", __LINE__);
}
strncpy( filenames[i], path, len );
strcat( filenames[i], ptr->d_name );
}
//使用冒泡法对文件名进行排序,排序后文件名按字母顺序存储于filenames
for( i = 0; i < count - 1; i++ ) {
for(j = 0; j < count - 1 - i; j++) {
if(strcmp( filenames[j], filenames[j + 1] ) > 0) {
strcpy( temp, filenames[j + 1] );
strcpy( filenames[j + 1], filenames[j] );
strcpy( filenames[j], temp );
}
}
}
if(flag_param == PARAM_R) {
display( flag_param, filenames[i] );
} else {
for(i = 0; i < count; i++) {
display( flag_param, filenames[i] );
}
}
closedir( dir );
//如果命令行中没有-l选项,打印一个换行符
if( (flag_param & PARAM_L) == 0 ) {
printf("\n");
}
}
int main( int argc, char ** argv )
{
int i, j, k, num;
char path[PATH_MAX + 1];
char param[32]; //保存命令行参数,目标文件名和目录名不在此列
int flag_param = PARAM_NONE; //参数种类,即是否有-l和-a选项
struct stat buf;
/*命令行参数的解析,分析-l -a -al -la选项*/
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++;
}
}
/*只支持参数a,l和R,如果含有其他选项就报错*/
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] == 'R') {
flag_param |= PARAM_R;
continue;
} else {
printf("my_ls: invalid options %-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)) { //argv[i]是一个目录
/* 如果目录的最后一个字符不是'/',就加上'/' */
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 { //argv[i]是一个文件
display(flag_param, path);
i++;
}
}
} while(i < argc);
return 0;
}
动手去写是练习计算机的最好方式,通过实际写代码可以了解到你对知识真正掌握了多少。
对于实现ls -R的部分还存在许多问题,首先就是没有应用到排序,将代码扩充进原有结构也显得有一点别扭,还需要再加强。