linux system programming: write a ls command by yourself

ls is used to list the contents of the directory. To achieve this function, there is no doubt that you need to read the directory, involving two APIs:

Opendir: DIR *opendir(const char *name), pass file name, return a pointer, point to directory sequence

Readdir: struct dirent *readdir(DIR *dirp). Pass the return value of opendir to a structure

        struct dirent {
               ino_t          d_ino;       /* inode number */
               off_t          d_off;       /* not an offset; see NOTES */
               unsigned short d_reclen;    /* length of this record */
               unsigned char  d_type;      /* type of file; not supported
                                              by all filesystem types */
               char           d_name[256]; /* filename */
           };

With these two APIs, you can implement a simple ls function

 1 /*================================================================
 2 *   Copyright (C) 2018 . All rights reserved.
 3 *   
 4 *   File name: myls.c
 5 *   Creator: ghostwu (Wu Hua)
 6 *   Date of establishment: January 9, 2018
 7 *   Description: ls command
 8 *
 9 ================================================================*/
10 
11 #include <stdio.h>
12 #include <sys/types.h>
13 #include <dirent.h>
14 #include <stdlib.h>
15 
16 void do_ls( char [] );
17 
18 int main(int argc, char *argv[])
19 {
20     if( argc == 1 ) {
21         do_ls( "." );
22     }else {
23         while( --argc ) {
24             printf( "arg=%s\n", * ++argv );
25             do_ls( *argv );
26         }
27     }
28     return 0;
29 }
30 
31 void do_ls( char dir_entry[] ) {
32     DIR* pDir;
33     struct dirent* pCurDir;
34     if( ( pDir = opendir( dir_entry ) ) == NULL ){
35         perror( "read dir" );
36         exit( -1 );
37     }else {
38         while( ( pCurDir = readdir( pDir ) ) != NULL ) {
39             printf( "%s\n", pCurDir->d_name );
40         }
41     }
42 }

This simple ls function lists all files (including hidden ones), but a lot of information is incomplete, such as permissions, users and groups, modification time, file size, number of links, etc. stat api can obtain these information of files

stat: get file status information

Prototype: int stat(const char *pathname, struct stat *buf), the first parameter: file name, the second parameter: the structure to save the file status information (man 2 stat has the description of the structure)

1. Get file size

 1 /*================================================================
 2 *   Copyright (C) 2018 . All rights reserved.
 3 *   
 4 *   File name: stat.c
 5 *   Creator: ghostwu (Wu Hua)
 6 *   Date of establishment: January 9, 2018
 7 *   Description:
 8 *
 9 ================================================================*/
10 
11 #include <stdio.h>
12 #include <sys/stat.h>
13 
14 #define FILENAME "/etc/passwd"
15 
16 int main(int argc, char *argv[])
17 {
18     struct stat filestat;
19     
20     if( -1 == stat( FILENAME, &filestat ) ) {
21         perror( "file stat" );
22         return -1;
23     }else {
24         printf( "the size of %s is %ld\n",FILENAME,  filestat.st_size );
25     }
26 
27     return 0;
28 }

2. Read file st mode, user id, group id, modification time, number of links

 1 /*================================================================
 2 *   Copyright (C) 2018 . All rights reserved.
 3 *   
 4 *   File name: stat2.c
 5 *   Creator: ghostwu (Wu Hua)
 6 *   Date of establishment: January 9, 2018
 7 *   Description:
 8 *
 9 ================================================================*/
10 
11 #include <stdio.h>
12 #include <sys/stat.h>
13 #include <string.h>
14 #include <time.h>
15 
16 void show_info( char *file, struct stat* statinfo );
17 void show_time( time_t filetime );
18 char* format_time( char* dsttime, const char* srctime );
19 
20 int main(int argc, char *argv[])
21 {
22     struct stat fileinfo;
23     if( argc > 1 ) {
24         /*debug information
25         printf( "%s\n", argv[1] );
26         int res = stat( argv[1], &fileinfo );
27         printf( "%d\n", res );
28         */
29         if( stat( argv[1], &fileinfo ) != -1 ) {
30             show_info( argv[1], &fileinfo );
31         }
32     }else {
33         perror( "get args from terminal" );
34     }
35     
36     return 0;
37 }
38 
39 void show_info( char* file, struct stat* statinfo ){
40     printf( "%s The file information is as follows:\n", file );
41     printf( "st_mode = %d\n", statinfo->st_mode );
42     printf( "links = %ld\n", statinfo->st_nlink );
43     printf( "uid = %d\n", statinfo->st_uid );
44     printf( "gid = %d\n", statinfo->st_gid );
45     printf( "file size = %ld\n", statinfo->st_size );
46     show_time( statinfo->st_mtime );
47 }
48 
49 void show_time( time_t filetime ) {
50     struct tm* ptm;
51     ptm = localtime( &filetime );
52 
53     int month = ptm->tm_mon + 1;
54     int day = ptm->tm_mday;
55     int hour = ptm->tm_hour;
56     int min = ptm->tm_min;
57 
58     char srchour[3] = "0";
59     char srcmin[3] = "0";
60     char dsthour[3] = "0";
61     char dstmin[3] = "0";
62     sprintf( srchour, "%d", hour );
63     sprintf( srcmin, "%d", min );
64     format_time( dsthour, srchour );
65     format_time( dstmin, srcmin );
66 
67     printf( "File last modified: %d month\t%d\t%s:%s\n", month, day, dsthour, dstmin );
68 }
69 
70 char* format_time( char* dsttime, const char* srctime ) {
71     if( strlen( srctime ) < 2 ) {
72         return strcat( dsttime, srctime );
73     }
74     return strcpy( dsttime, srctime );
75 }

3. Permission st mode to character permission bit (for example: - rwxrwxrwx), user id and group id to user name and group name, determine file type

  1 /*================================================================
  2 *   Copyright (C) 2018 . All rights reserved.
  3 *   
  4 *   File name: stat3.c
  5 *   Creator: ghostwu (Wu Hua)
  6 *   Date of establishment: January 9, 2018
  7 *   Description: file type and permission bit
  8 *
  9 ================================================================*/
 10 
 11 #include <stdio.h>
 12 #include <stdlib.h>
 13 #include <sys/stat.h>
 14 #include <string.h>
 15 #include <sys/types.h>
 16 #include <pwd.h>
 17 #include <grp.h>
 18 
 19 void do_ls( char* filename );
 20 void show_filetype( char* filename, int filemode );
 21 void show_filetype2( char* filename, int filemode );
 22 void mode_to_letters( int filemode, char str[] );
 23 //user id Transfer name
 24 char* uid_to_name( uid_t uid );
 25 //group id Transfer name
 26 char* gid_to_name( gid_t gid );
 27 
 28 int main(int argc, char *argv[])
 29 {
 30     if( argc < 2 ) {
 31         printf( "usage:%s file\n", argv[0] );
 32         return -1;
 33     }else {
 34         do_ls( argv[1] );
 35     }
 36     return 0;
 37 }
 38 
 39 char* uid_to_name( uid_t uid ){
 40     return getpwuid( uid )->pw_name;
 41 }
 42 
 43 char* gid_to_name( gid_t gid ){
 44     return getgrgid( gid )->gr_name;
 45 }
 46 
 47 void do_ls( char* filename ) {
 48     struct stat fileinfo;
 49     if( stat( filename, &fileinfo ) == -1 ) {
 50         printf( "%s open failure\n", filename );
 51         exit( -1 );
 52     }
 53     //printf( "st_mode = %d\n", fileinfo.st_mode );    
 54     show_filetype( filename, fileinfo.st_mode );
 55     show_filetype2( filename, fileinfo.st_mode );
 56     char file_permission[10];
 57     mode_to_letters( fileinfo.st_mode, file_permission );
 58     printf( "%s\n", file_permission  );
 59     printf( "user:%s\n", uid_to_name( fileinfo.st_uid ) );
 60     printf( "group:%s\n", gid_to_name( fileinfo.st_gid ) );
 61 }
 62 
 63 //Mask to determine file type
 64 void show_filetype( char* filename, int filemode ){
 65     //use st_mode The result of the mask phase sum of Judge document type
 66     if ( ( filemode & 0170000 ) == 0100000 ){
 67         printf( "%s It's a regular file\n", filename );
 68     }else if( ( filemode & 0170000 ) == 0040000 ){ 
 69         printf( "%s Is the catalog\n", filename );
 70     }else if ( ( filemode & S_IFMT ) == S_IFLNK ){
 71         printf( "%s Is a symbolic link\n", filename );
 72     }
 73 }
 74 
 75 //Using macro to judge file type
 76 void show_filetype2( char* filename, int filemode ){
 77     if( S_ISREG( filemode ) ) {
 78         printf( "%s It's a regular file\n", filename );
 79     }else if( S_ISDIR( filemode ) ) {
 80         printf( "%s Is the catalog\n", filename );
 81     }else if( S_ISLNK( filemode ) ){
 82         printf( "%s Is a symbolic link\n", filename );
 83     }
 84 }
 85 
 86 //Decode numbers into alphanumeric permission bits
 87 void mode_to_letters( int filemode, char str[] ) {
 88     strcpy( str, "----------" );
 89     if( S_ISREG( filemode ) ) str[0] = '-';
 90     if( S_ISDIR( filemode ) ) str[0] = 'd';
 91     if( S_ISLNK( filemode ) ) str[0] = 'l';
 92 
 93     //User permission bit
 94     if( filemode & S_IRUSR ) str[1] = 'r';
 95     if( filemode & S_IWUSR ) str[2] = 'w';
 96     if( filemode & S_IXUSR ) str[3] = 'x';
 97 
 98     //Group permission bit
 99     if( filemode & S_IRGRP ) str[4] = 'r';
100     if( filemode & S_IWGRP ) str[5] = 'w';
101     if( filemode & S_IXGRP ) str[6] = 'x';
102 
103     //Other group permission bits
104     if( filemode & S_IROTH ) str[7] = 'r';
105     if( filemode & S_IWOTH ) str[8] = 'w';
106     if( filemode & S_IXOTH ) str[9] = 'x';
107 }

Based on the above three small examples, a well formatted version of ls command can be obtained:

  1 /*================================================================
  2 *   Copyright (C) 2018 . All rights reserved.
  3 *   
  4 *   File name: myls2.c
  5 *   Creator: ghostwu (Wu Hua)
  6 *   Date of establishment: January 9, 2018
  7 *   Description: ls command (version 1.2)
  8 *
  9 ================================================================*/
 10 
 11 #include <stdio.h>
 12 #include <sys/types.h>
 13 #include <dirent.h>
 14 #include <stdlib.h>
 15 #include <sys/types.h>
 16 #include <sys/stat.h>
 17 #include <unistd.h>
 18 #include <string.h>
 19 #include <sys/types.h>
 20 #include <pwd.h>
 21 #include <grp.h>
 22 #include <time.h>
 23 
 24 void do_ls( char [] );
 25 void do_stat( char* filename );
 26 void show_list( char* filename, struct stat* statinfo );
 27 void mode_to_letters( mode_t filemode, char str[] );
 28 void show_time( time_t filetime );
 29 char* format_time( char* dsttime, const char* srctime );
 30 
 31 //user id Transfer name
 32 char* uid_to_name( uid_t uid );
 33 //group id Transfer name
 34 char* gid_to_name( gid_t gid );
 35 
 36 int main(int argc, char *argv[])
 37 {
 38     if( argc == 1 ) {
 39         do_ls( "." );
 40     }else {
 41         while( --argc ) {
 42             printf( "arg=%s\n", * ++argv );
 43             do_ls( *argv );
 44         }
 45     }
 46     return 0;
 47 }
 48 
 49 void do_ls( char dir_entry[] ) {
 50     DIR* pDir;
 51     struct dirent* pCurDir;
 52     if( ( pDir = opendir( dir_entry ) ) == NULL ){
 53         perror( "read dir" );
 54         exit( -1 );
 55     }else {
 56         while( ( pCurDir = readdir( pDir ) ) != NULL ) {
 57             do_stat( pCurDir->d_name );
 58         }
 59         closedir( pDir );
 60     }
 61 }
 62 
 63 //Get file information
 64 void do_stat( char* filename ){
 65     struct stat statinfo;
 66     if ( stat( filename, &statinfo ) == -1 ) {
 67         printf( "open%s fail\n", filename );
 68         exit( -1 );
 69     }else {
 70         show_list( filename, &statinfo );
 71     }
 72 }
 73 
 74 //Show file list
 75 void show_list( char* filename, struct stat* statinfo ) {
 76     mode_t st_mode = statinfo->st_mode;
 77 
 78     char str[10];
 79     mode_to_letters( st_mode, str );
 80     printf( "%s\t", str );
 81 
 82     printf( "%ld\t", statinfo->st_nlink ); //Symbolic link
 83     printf( "%s\t\t", uid_to_name( statinfo->st_uid ) ); //user name
 84     printf( "%s\t", gid_to_name( statinfo->st_gid ) ); //Group name
 85     printf( "%10ld", statinfo->st_size ); //file size
 86     show_time( statinfo->st_mtime ); //Last modification time
 87     printf( "\t%s", filename );
 88 
 89     printf( "\n" );
 90 }
 91 
 92 char* uid_to_name( uid_t uid ){
 93     return getpwuid( uid )->pw_name;
 94 }
 95 
 96 char* gid_to_name( gid_t gid ){
 97     return getgrgid( gid )->gr_name;
 98 }
 99 
100 void mode_to_letters( mode_t filemode, char str[] ) {
101 
102     strcpy( str, "----------" );
103     if( S_ISREG( filemode ) ) str[0] = '-';
104     if( S_ISDIR( filemode ) ) str[0] = 'd';
105     if( S_ISLNK( filemode ) ) str[0] = 'l';
106 
107     //User permission bit
108     if( filemode & S_IRUSR ) str[1] = 'r';
109     if( filemode & S_IWUSR ) str[2] = 'w';
110     if( filemode & S_IXUSR ) str[3] = 'x';
111 
112     //Group permission bit
113     if( filemode & S_IRGRP ) str[4] = 'r';
114     if( filemode & S_IWGRP ) str[5] = 'w';
115     if( filemode & S_IXGRP ) str[6] = 'x';
116 
117     //Other group permission bits
118     if( filemode & S_IROTH ) str[7] = 'r';
119     if( filemode & S_IWOTH ) str[8] = 'w';
120     if( filemode & S_IXOTH ) str[9] = 'x';
121 }
122 
123 void show_time( time_t filetime ) {
124     struct tm* ptm;
125     ptm = localtime( &filetime );
126 
127     int month = ptm->tm_mon + 1;
128     int day = ptm->tm_mday;
129     int hour = ptm->tm_hour;
130     int min = ptm->tm_min;
131 
132     char srchour[3] = "0";
133     char srcmin[3] = "0";
134     char dsthour[3] = "0";
135     char dstmin[3] = "0";
136     sprintf( srchour, "%d", hour );
137     sprintf( srcmin, "%d", min );
138     format_time( dsthour, srchour );
139     format_time( dstmin, srcmin );
140 
141     printf( "%4d month%4d%4s:%2s", month, day, dsthour, dstmin );
142 }
143 
144 char* format_time( char* dsttime, const char* srctime ) {
145     if( strlen( srctime ) < 2 ) {
146         return strcat( dsttime, srctime );
147     }
148     return strcpy( dsttime, srctime );
149 }

 

Summary:

1) The usage of opendir and readdir

2) Application of struct direct

3) Usage of stat

Keywords: Linux

Added by tapupartforpres on Fri, 01 May 2020 20:21:39 +0300