Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/articles/c-tutorials/)
-   -   Developing Linux Utility like 'ls' in C (http://www.go4expert.com/articles/developing-linux-utility-ls-c-t27426/)

poornaMoksha 24Dec2011 12:08

Developing Linux Utility like 'ls' in C
 
Most of the people working on Linux must have used the basic command 'ls'. I use it many times a day. It is a very useful command when it comes to displaying the contents of a directory and their properties. For those few who have still not used 'ls' its high time now, go to its man page, study it and start using it. Anyways, coming back to the article, this article does not describe the usage of 'ls' command but rather here I try to develop my own 'ls' equivalent 'my_ls'. I'll use C language and will try to develop a full fledged 'ls' command.

The article will be divided in several small articles which would collectively cover the whole effort. This article becomes the part-I of the series.

Properties of 'ls' covered in this part



In this article, we will just cover the basic property where in the contents of the directory are displayed when 'my_ls' is executed.
For example:

Code:

$ my_ls
 article_function_pointer.txt  Desktop    Downloads  Music      Pictures  Public    testdisk.log  test.py            Videos  worm
 chmodOctal.txt                Documents  google    output.log  practice  Templates  testfile      Unsaved Document 1  vlc

The code



The following code is the first draft of the implementation :

Code:

#include<stdio.h>
 #include<stdlib.h>
 #include <sys/types.h>
 #include <dirent.h>
 
 int main(void)
 {
    char *curr_dir = NULL;
    DIR *dp = NULL;
    struct dirent *dptr = NULL;
    unsigned int count = 0;
 
    // Get the value of environment variable PWD
    curr_dir = getenv("PWD");
    if(NULL == curr_dir)
    {
        printf("\n ERROR : Could not get the working directory\n");
        return -1;
    }
 
    // Open the current directory
    dp = opendir((const char*)curr_dir);
    if(NULL == dp)
    {
        printf("\n ERROR : Could not open the working directory\n");
        return -1;
    }
 
    printf("\n");
    // Go through and display all the names (files or folders)
    // Contained in the directory.
    for(count = 0; NULL != (dptr = readdir(dp)); count++)
    {
        printf("%s  ",dptr->d_name);
    }
    printf("\n %u", count);
 
    return 0;
 }

  • In the above code, we first fetch the current working directory through the environment variable PWD
  • Since now we get the working directory, we open it and iterate over its contents by reading the directory.
  • With each file/folder encountered, we display the name of it.

The Output and the Loopholes



The out put of the above code is something like :

Code:

$ ./my_ls 
 
 .config  .gnome2_private  .cache  .gstreamer-0.10  worm  Unsaved Document 1  testfile  testdisk.log  article_function_pointer.txt  .compiz  .macromedia  .xsession-errors.old  .icons  .pulse-cookie  Desktop  .gtk-bookmarks  .bash_logout  vlc  .lesshst  .linuxmint  test.py  .thumbnails  .gnome2  .sudo_as_admin_successful  chmodOctal.txt  output.log  .dmrc  .mozilla-thunderbird  .ICEauthority  .xchm  .recently-used.xbel  Pictures  .gksu.lock  Templates  .ssh  google  .mplayer  .kde  .xchat2  .gvfs  .xsession-errors  .viminfo  .themes  .teamviewer  .profile  .openoffice.org  my_ls  ..  Public  Documents  .kchmviewer  .adobe  Music  .vim  .esd_auth  .fontconfig  .java Downloads  .nautilus  .bash_history  .  .mozilla  .gimp-2.6  .gegl-0.0  .gconf  .thunderbird  .gconfd  .Skype  .pulse  .local  Videos  practice  .dbus

While the output of the standard ls command is :

Code:

$ ls
 article_function_pointer.txt  Desktop    Downloads  Music  output.log  practice  Templates    testfile  Unsaved Document 1  vlc
 chmodOctal.txt                Documents  google    my_ls  Pictures    Public    testdisk.log  test.py  Videos              worm

We see that there is a clear difference between the two outputs. The difference visible are :
  1. Our utility 'my_ls' shows many files which begin with '.' while ls does not.
  2. The output from 'my_ls' is not formatted correctly (For example, executables should be in green, Directories should be in blue etc).
  3. The output from 'my_ls' is not in alphabetical order and the display is not in an order.
So we see that there are above mentioned 3 short comings.

Lets correct them one by one(The first two will be covered in this part while the last one will be covered in part-II).


Omitting the files from output that begin with '.'



Here is the piece of code :

Code:

#include<stdio.h>
 #include<stdlib.h>
 #include <sys/types.h>
 #include <dirent.h>
 
 int main(void)
 {
    char *curr_dir = NULL;
    DIR *dp = NULL;
    struct dirent *dptr = NULL;
    unsigned int count = 0;
 
    curr_dir = getenv("PWD");
    if(NULL == curr_dir)
    {
        printf("\n ERROR : Could not get the working directory\n");
        return -1;
    }
 
    dp = opendir((const char*)curr_dir);
    if(NULL == dp)
    {
        printf("\n ERROR : Could not open the working directory\n");
        return -1;
    }
 
    printf("\n");
    for(count = 0; NULL != (dptr = readdir(dp)); count++)
    {
        // Check if the name of the file/folder begins with '.'
        // If yes, then do not display it.
        if(dptr->d_name[0] != '.')
            printf("%s  ",dptr->d_name);
    }
 
    return 0;
 }

  • In the above code, we first fetch the current working directory through the environment variable PWD.
  • Since now we get the working directory, we open it and iterate over its contents by reading the directory.
  • If the name of the file/folder begins with a '.', then just ignore it as the standard 'ls' also does not display the file beginning with '.' if its executed without any flags.
  • With each file/folder (whose name does not begin with '.') encountered, we display the name of it on stdout.
Now lets compare the output :

Code:

$ ./my_ls 
 
 worm  Unsaved Document 1  testfile  testdisk.log  article_function_pointer.txt  Desktop  vlc  test.py  chmodOctal.txt  output.log  Pictures  Templates  google  my_ls  Public  Documents  Music  Downloads  Videos  practice 
 
 $ ls
 article_function_pointer.txt  Desktop    Downloads  Music  output.log  practice  Templates    testfile  Unsaved Document 1  vlc
 chmodOctal.txt                Documents  google    my_ls  Pictures    Public    testdisk.log  test.py  Videos              worm

We see that the output of 'my_ls' is closer to 'ls' as now there is no file that begins with '.'

Getting the Color formatting correct



Here is the code :

Code:

#include<stdio.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>
 
 
 // Define the color codes as macros
 #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;
 
    // Find the column width of terminal
    // We will make use of this in part-II 
    // Of this article.
    struct winsize w;
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
 
    curr_dir = getenv("PWD");
    if(NULL == curr_dir)
    {
        printf("\n ERROR : Could not get the working directory\n");
        return -1;
    }
   
    dp = opendir((const char*)curr_dir);   
    if(NULL == dp)
    {
        printf("\n ERROR : Could not open the working directory\n");
        return -1;
    }
 
    for(count = 0; NULL != (dptr = readdir(dp)); count++)
    {
        if(dptr->d_name[0] != '.')
        {
            // Check if the file is executable
            if(!access(dptr->d_name,X_OK))
            {
                int fd = -1;
                struct stat st;
 
                fd = open(dptr->d_name, O_RDONLY, 0);
                if(-1 == fd)
                {
                    printf("\n Opening file/Directory failed\n");
                    return -1;
                }
               
                fstat(fd, &st);
                // Check if it actaully was a directory with execute
                // permissions on it.
                if(S_ISDIR(st.st_mode))
                {
                    // If it was a directory, print it in Blue
                    printf(MAKE_BLUE"%s    "RESET_COLOR,dptr->d_name);
                }
                else
                {                                 
                    // If it was a normal executable
                    // Print it in green
                    printf(MAKE_GREEN"%s    "RESET_COLOR,dptr->d_name);
                }
                close(fd);
            }
            else
            {
                // No executable flag ON
                // Print it in black(default)
                printf("%s    ",dptr->d_name);
            }
        }
    }
    printf("\n");
 
    return 0;
 }

  • In the above code, we first fetch the current working directory through the environment variable PWD
  • Since now we get the working directory, we open it and iterate over its contents by reading the directory.
  • If the name of the file/folder begins with a '.', then just ignore it as the standard 'ls' also does not display the file beginning with '.' if its executed without any flags.
  • Then for every valid file/folder name we see that whether it has execute permissions on it.
  • If execute permissions are there, then for folders we print the name in blue while for files we print the name in green.
  • For all other regular files whose execute permissions are not set, the name is printed in black
  • All these names are printed on stdout.
The output of the following code is :

Code:

$ ./my_ls 
 worm    Unsaved Document 1    testfile    testdisk.log    article_function_pointer.txt    Desktop    vlc    test.py    chmodOctal.txt    output.log    Pictures    Templates    google    my_ls    Public    Documents    Music    Downloads    my_ls.c    Videos    practice

If you run the above piece on your machine, you will see that the color formatting is present in the output.

Other formatting



We will take up the following formatting in Part-II.
  • Displaying the output in alphabetical order.
  • Making the display formatting better.

Conclusion



To Conclude, In this article we studied how to develop a simple utility that works like the standard 'ls' utility. We divided the development in different parts and in this part we learn to display simply the contents of the directory and got their color formatting correct. Will be covering the rest in the coming parts.

Stay tuned for more!!!


All times are GMT +5.5. The time now is 23:22.