Developing Linux utility - Part III Displaying Detail File Info

poornaMoksha's Avatar author of Developing Linux utility - Part III Displaying Detail File Info
This is an article on Developing Linux utility - Part III Displaying Detail File Info in C.
In this article we will extend our code to print more comprehensive information about a file/directory in a directory. Please Refer
  1. Developing Linux Utility like 'ls' in C
  2. Developing Linux Utility - Part II Arranging Output in Alphabetical Order

Example



Code:
$ ls -l 
total 148 
-rwxr-xr-x 1 himanshu family   13630 2011-12-27 06:16 alpha 
-rw-r--r-- 1 himanshu family    3828 2011-09-11 16:04 article_function_pointer.txt 
-rw-r--r-- 1 himanshu himanshu   155 2011-04-24 10:58 chmodOctal.txt 
drwxr-xr-x 9 himanshu himanshu  4096 2011-12-24 09:02 Desktop 
drwxr-xr-x 2 himanshu himanshu  4096 2011-12-01 08:15 Documents 
drwxr-xr-x 9 himanshu himanshu  4096 2011-12-27 05:55 Downloads 
drwxr-xr-x 2 root     root      4096 2011-12-03 22:07 google 
drwxr-xr-x 2 himanshu himanshu  4096 2011-03-12 06:31 Music 
-rwxr-xr-x 1 himanshu family   13352 2011-12-24 15:09 my_ls 
-rw-r--r-- 1 himanshu family    8452 2011-12-27 06:24 my_ls_alpha.c 
-rw-r--r-- 1 himanshu family    4433 2011-12-24 14:57 my_ls.c 
-rw-r--r-- 1 himanshu himanshu 16904 2011-04-16 17:58 output.log 
drwxr-xr-x 3 himanshu himanshu  4096 2011-04-02 13:12 Pictures 
drwxr-xr-x 7 himanshu family   12288 2011-12-26 22:39 practice 
drwxr-xr-x 2 himanshu himanshu  4096 2011-03-12 06:31 Public 
drwxr-xr-x 2 himanshu himanshu  4096 2011-03-12 06:31 Templates 
-rw-r--r-- 1 root     root      1014 2011-08-16 23:27 testdisk.log 
-rw-r--r-- 1 himanshu himanshu     0 2011-04-24 12:16 testfile 
-rw-r--r-- 1 himanshu himanshu   436 2011-04-17 08:54 test.py 
-rw-r--r-- 1 himanshu family    3739 2011-09-11 18:42 Unsaved Document 1 
drwxr-xr-x 2 himanshu himanshu  4096 2011-03-12 06:31 Videos 
drwxr-xr-x 2 root     root      4096 2011-12-03 22:43 vlc 
drwxr-xr-x 6 himanshu family    4096 2011-07-03 17:07 worm
  • As you can see in the above output, we have used the standard Linux command 'ls -l' and it displays a very detailed information about each file.
  • The first column gives the permission for owner, group and others.
  • Then number of hard links to the file are displayed.
  • Then we have the user and the group name.
  • Then the size of the file is displayed.
  • Then we have the date and time of file creation.
  • Finally we have file name.

Lets write a code that generated information close to the example shown above.

The code



Here is the code :

Code:
#include<stdio.h> 
#include<string.h> 
#include<stdlib.h> 
#include <sys/types.h> 
#include <dirent.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <fcntl.h> 
#include <sys/ioctl.h> 
#include <pwd.h> 
#include <grp.h> 
#include <time.h> 
 
 
 
#define RESET_COLOR "\e[m" 
#define MAKE_GREEN "\e[32m" 
#define MAKE_BLUE "\e[36m" 
 
 
int main(void) 
{ 
   char *curr_dir = NULL; 
   DIR *dp = NULL; 
   struct dirent *dptr = NULL; 
   unsigned int count = 0; 
   long *ptr = NULL; 
   struct winsize w; 
 
   //to get the number of rows and column visible on terminal 
   ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); 
 
   // Fetch the environment variable PWD so as to get the  
   // Current working directory 
   curr_dir = getenv("PWD"); 
   if(NULL == curr_dir) 
   { 
       printf("\n ERROR : Could not get the working directory\n"); 
       return -1; 
   } 
 
   // Variable to hold number of files inside the directory 
   int num_files = 0; 
   // opne the directory 
   dp = opendir((const char*)curr_dir);   
   // Start reading the directory contents 
   while(NULL != (dptr = readdir(dp)))  
   { 
       // Do not count the files beginning with '.' 
       if(dptr->d_name[0] != '.') 
       num_files++; 
   } 
   // Our aim was to count the number of files/folders  
   // inside the current working directory. Since its  
   // done so close the directory. 
   closedir(dp); 
 
   // Restore the values back as we will be using them 
   // later again 
   dp = NULL; 
   dptr = NULL; 
 
   // Check that we should have at least one file/folder 
   // inside the current working directory 
   if(!num_files) 
   { 
       return 0; 
   } 
   else 
   { 
       // Allocate memory to hold the addresses of the  
       // names of contents in current working directory 
       ptr = malloc(num_files*8); 
       if(NULL == ptr) 
       { 
           printf("\n Memory allocation failed\n"); 
           return -1; 
       } 
       else 
       { 
           // Initialize the memory by zeros 
           memset(ptr,0,num_files*8); 
       } 
   }  
 
   // Open the directory again 
   dp = opendir((const char*)curr_dir);    
   if(NULL == dp) 
   { 
       printf("\n ERROR : Could not open the working directory\n"); 
       free(ptr); 
       return -1; 
   } 
  
   // Start iterating the directory and read all its contents 
   // inside an array allocated above. 
   unsigned int j = 0; 
   for(count = 0; NULL != (dptr = readdir(dp)); count++) 
   { 
       if(dptr->d_name[0] != '.') 
       { 
          ptr[j] = (long)dptr->d_name; 
          j++;  
       } 
   } 
 
   // Start sorting the names alphabetically 
   // Using bubble sorting here 
   for(count = 0; count< num_files-1;count++) 
   { 
       for(j=count+1; j< (num_files);j++) 
       { 
           char *c = (char*)ptr[count]; 
           char *d = (char*)ptr[j]; 
            
           // Check that the two characters should be from same set 
           if( ((*c >= 'a') && (*d >= 'a')) || ((*c <='Z') && (*d <='Z')) ) 
           { 
               int i = 0; 
               // If initial characters are same, continue comparing 
               // the characters until a difference is found 
               if(*c == *d) 
               { 
                   while(*(c+i)==*(d+i)) 
                   { 
                       i++; 
                   } 
               } 
               // Check if the earlier stored value is alphabetically 
               // higher than the next value 
               if(*(c+i) > *(d+i)) 
               { 
                   // If yes, then swap the values 
                   long temp = 0; 
                   temp = ptr[count]; 
                   ptr[count] = ptr[j]; 
                   ptr[j] = temp; 
               } 
 
           } 
           else 
           { 
               // if the two beginning characters are not from 
               // the same ascii set then make them same and then 
               // compare. 
               int off_1=0, off_2=0; 
               if(*c <= 'Z') 
               { 
                   off_1 = 32; 
               } 
               if(*d <= 'Z') 
               { 
                   off_2 = 32; 
               } 
 
               int i = 0; 
               // After the character set are made same, check if the 
               // beginning characters are same. If yes, then continue  
               // searching until we find some difference. 
               if(*c+ off_1 == *d + off_2) 
               { 
                   while(*(c+off_1+i)==*(d+off_2+i)) 
                   { 
                       i++; 
                   } 
               } 
               // After difference is found, check if a swap is required. 
               if((*c + off_1+i) > (*d + off_2+i)) 
               { 
                   // If yes, go ahead and do the swap 
                   long temp = 0; 
                   temp = ptr[count]; 
                   ptr[count] = ptr[j]; 
                   ptr[j] = temp; 
               } 
           } 
       } 
    } 
 
   // Now the names are sorted alphabetically 
   // Start displaying on console. 
   for(count = 0; count< num_files; count++) 
   { 
       int fd = -1; 
       struct stat st; 
 
       fd = open((char*)ptr[count], O_RDONLY, 0); 
       if(-1 == fd) 
       { 
           printf("\n Opening file/Directory failed\n"); 
           free(ptr); 
           return -1; 
       } 
 
      // Call fstat to get the stat info about the file 
      if(fstat(fd, &st)) 
      { 
          // If fstat() fails 
          printf("\n Fstat() failed\n"); 
          close(fd); 
          free(ptr); 
          return -1; 
      } 
 
      // Check if a directory 
      if(S_ISDIR(st.st_mode)) 
      { 
      printf("d"); 
      } 
      else 
      {     
          printf("-"); 
      } 
 
      // Check the owner permission 
      mode_t permission = st.st_mode & S_IRWXU; 
 
      if(permission & S_IRUSR) 
      { 
          printf("r"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
      if(permission & S_IWUSR) 
      { 
          printf("w"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
      if(permission & S_IXUSR) 
      { 
          printf("x"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
 
      // CHeck the group permission 
      permission = st.st_mode & S_IRWXG; 
 
      if(permission & S_IRGRP) 
      { 
          printf("r"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
      if(permission & S_IWGRP) 
      { 
          printf("w"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
      if(permission & S_IXGRP) 
      { 
          printf("x"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
 
      // CHeck other's permission 
      permission = st.st_mode & S_IRWXO; 
 
      if(permission & S_IROTH) 
      { 
          printf("r"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
      if(permission & S_IWOTH) 
      { 
          printf("w"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
      if(permission & S_IXOTH) 
      { 
          printf("x"); 
      } 
      else 
      { 
          printf("-"); 
      } 
 
      // Print the number of hard links 
      printf(" %d ", (int)st.st_nlink); 
 
      // Get the user name 
      struct passwd *pt = getpwuid(st.st_uid); 
      printf("%s ",pt->pw_name); 
 
      // Get the group name 
      struct group *p = getgrgid(st.st_gid); 
      printf("%s ",p->gr_name); 
 
      // Get the file size 
      printf("%lld ",(long long) st.st_size); 
 
      // Get the date and time 
      // Note that some logic is applied here 
      // so as to remove the trailing newline. 
      char date_time[100]; 
      memset(date_time,0,sizeof(date_time)); 
      strncpy(date_time, ctime(&st.st_ctime), sizeof(date_time)); 
      int c = 0; 
      while(date_time[c] != '\0') 
      { 
          if(date_time[c] == '\n') 
              date_time[c] = '\0'; 
          c++; 
      } 
      printf("%s ", date_time); 
 
       // Check if the file/folder is executable. 
      if(!access((const char*)ptr[count],X_OK)) 
      { 
          if(S_ISDIR(st.st_mode)) 
          { 
              // If folder, print in blue 
              printf(MAKE_BLUE"%s\n"RESET_COLOR,(char*)ptr[count]); 
          } 
          else 
          {        
              // If executable file, print in green                            
              printf(MAKE_GREEN"%s\n"RESET_COLOR,(char*)ptr[count]); 
          } 
      } 
      else 
      { 
          // If normal file, print by the default way(black color) 
          printf("%s\n",(char*)ptr[count]); 
      } 
      close(fd); 
   } 
 
   //Free the allocated memory 
   free(ptr); 
   return 0; 
}
In the code above :
  • We have used the object of structure stat filled by the function fstat()
  • We have used various bit mask operations to get the permissions for owner, group and others.
  • We have used the ctime() API to convert the time into a displayable format.
Now lets see the output :

Code:
$ ./my_ls
-rwxr-xr-x 1 himanshu family 13630 Tue Dec 27 06:16:31 2011 alpha 
-rw-r--r-- 1 himanshu family 3828 Sun Sep 11 16:04:53 2011 article_function_pointer.txt 
-rw-r--r-- 1 himanshu himanshu 155 Sun Apr 24 10:58:15 2011 chmodOctal.txt 
drwxr-xr-x 9 himanshu himanshu 4096 Sat Dec 24 09:02:37 2011 Desktop 
drwxr-xr-x 2 himanshu himanshu 4096 Thu Dec  1 08:15:50 2011 Documents 
drwxr-xr-x 9 himanshu himanshu 4096 Tue Dec 27 05:55:36 2011 Downloads 
drwxr-xr-x 2 root root 4096 Sat Dec  3 22:07:11 2011 google 
-rwxr-xr-x 1 himanshu family 13352 Sat Dec 24 15:09:36 2011 my_ls 
drwxr-xr-x 2 himanshu himanshu 4096 Sat Mar 12 06:31:00 2011 Music 
-rw-r--r-- 1 himanshu family 4433 Sat Dec 24 14:57:25 2011 my_ls.c 
-rw-r--r-- 1 himanshu family 8452 Tue Dec 27 06:24:18 2011 my_ls_alpha.c 
-rw-r--r-- 1 himanshu himanshu 16904 Sat Apr 16 17:58:18 2011 output.log 
drwxr-xr-x 3 himanshu himanshu 4096 Sat Apr  2 13:12:39 2011 Pictures 
drwxr-xr-x 2 himanshu himanshu 4096 Sat Mar 12 06:31:00 2011 Public 
drwxr-xr-x 7 himanshu family 12288 Mon Dec 26 22:39:38 2011 practice 
-rw-r--r-- 1 himanshu himanshu 436 Sun Apr 17 08:54:27 2011 test.py 
-rw-r--r-- 1 root root 1014 Tue Aug 16 23:27:19 2011 testdisk.log 
drwxr-xr-x 2 himanshu himanshu 4096 Sat Mar 12 06:31:00 2011 Templates 
-rw-r--r-- 1 himanshu himanshu 0 Sun Apr 24 12:16:35 2011 testfile 
-rw-r--r-- 1 himanshu family 3739 Sun Sep 11 18:42:55 2011 Unsaved Document 1 
drwxr-xr-x 2 himanshu himanshu 4096 Sat Mar 12 06:31:00 2011 Videos 
drwxr-xr-x 2 root root 4096 Sat Dec  3 22:43:08 2011 vlc 
drwxr-xr-x 6 himanshu family 4096 Sun Jul  3 17:07:07 2011 worm
We see that the above output is quite close to the output of standard 'ls -l' utility described in the beginning of this article. Only some indentation in display is required.

Conclusion



To conclude, In this article we extended our code so that it can print the detailed information about file just like we get with the command 'ls -l'

Stay tuned for more!!!
Newbie Member
1Mar2012,13:39   #2
joschmuck's Avatar
Helpful article.

Is it possible to run this utility with the ability to set which directory to list the file info?
And to also to arrange the columns with appropriate header?

Thanks.