Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/articles/c-tutorials/)
-   -   Developing Linux utility - Part III Displaying Detail File Info (http://www.go4expert.com/articles/developing-linux-utility-part-iii-t27451/)

poornaMoksha 27Dec2011 06:54

Developing Linux utility - Part III Displaying Detail File Info
 
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!!!

joschmuck 1Mar2012 13:39

Re: Developing Linux utility - Part III Displaying Detail File Info
 
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.


All times are GMT +5.5. The time now is 07:40.