Developing Linux utility - Part III Displaying Detail File Info

Discussion in 'C' started by poornaMoksha, Dec 27, 2011.

  1. poornaMoksha

    poornaMoksha New Member

    Joined:
    Jan 29, 2011
    Messages:
    150
    Likes Received:
    33
    Trophy Points:
    0
    Occupation:
    Software developer
    Location:
    India
    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!!!
     
  2. joschmuck

    joschmuck New Member

    Joined:
    Feb 29, 2012
    Messages:
    2
    Likes Received:
    0
    Trophy Points:
    0
    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.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice