Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C (http://www.go4expert.com/articles/c-tutorials/)
-   -   Developing Linux Utility - Part II Arranging Output in Alphabetical Order (http://www.go4expert.com/articles/developing-linux-utility-part-ii-t27431/)

poornaMoksha 24Dec2011 18:15

Developing Linux Utility - Part II Arranging Output in Alphabetical Order
 
Continuation of Developing Linux Utility like 'ls' in C series. In the first part we studied a code that was developed to behave like a basic ls utility. Here in this part I have extended the code to give output in alphabetical order.

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>
 
 #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 begining 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++)
    {
        // Check if the file/folder is executable.
        if(!access((const char*)ptr[count],X_OK))
            {
                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;
                }
               
                fstat(fd, &st);
                if(S_ISDIR(st.st_mode))
                {
                    // If folder, print in blue
                    printf(MAKE_BLUE"%s    "RESET_COLOR,(char*)ptr[count]);
                }
                else
                {       
                    // If executable file, print in green                           
                    printf(MAKE_GREEN"%s    "RESET_COLOR,(char*)ptr[count]);
                }
                close(fd);
            }
            else
            {
                // If normal file, print by the default way(black color)
                printf("%s    ",(char*)ptr[count]);
            }
    }
    printf("\n");
 
    //Free the allocated memory
    free(ptr);
    return 0;
 }

In the code above :
  • Rather than displaying the names of the files and folders inside the current working directory we have stored them onto heap
  • The size of the memory allocated on heap was calculated in a small loop prior to iterating the working directory.
  • After all the names are stored on heap, a logic that uses bubble sort technique is applied to arrange the names alphabetically.
  • Please note that special characters and numeric values are not being taken care of as of now.
  • Once the sorting is done, the names are displayed on terminal with appropriate color coding.
  • Note that I have used Ubuntu systems for developing and testing this logic.
  • This is a quick piece of code that I have written. Any improvements and suggestions are most welcome.

The Output



As the above piece is executed, the output was :

Code:

$ ./my_ls 
 1    1.c    a.out    abc    acii.c    alpha    aneesh    aneesh.c    array_pointer    array_pointer.c    ascii    ascii.c    basicBO    basicBO.c    bufferoverflow    bufferoverflow.c    bus    bus.c    byteorder    byteorder.c    charptrarr    charptrarr.c    cmdline    cmdline.c    code    constant_pointer    constant_pointer.c    copy    copy_getc_putc    copy_getc_putc.c    copy_unbuffered    copy_unbuffered.c    crack    crack.c    curl.c    data.txt    direntry    direntry.c    elfvirus    environ    environ.c    exit    exit.c    factorial    factorial.c    factorial.cpp    feof    feof.c    file    file.c    fileHole    fileHole.c    for    for.c    fork    fork.c    fp    fp.c    fread    fread.c    func    func.c    func.o    func.s    func_ptr    func_ptr.c    gnome-terminal    hack_ptr    hack_ptr.c    hello    hello1.c    hello1.o    hello2.c    hello2.o    hello3.c    hello3.o    hello4.c    hello4.o    helloWorld    helloWorld.c    hide    hide.c    input    intrstin    intrstin.c    keylogger    keylogger.c    lib    libshared1.so.1    libshared1.so.1.0    libshared1.so.1.1    libshared2.so.1    libshared2.so.1.1    libshared3.so    libshared3.so.1    libshared3.so.1.0    libshared4.so    libshared4.so.1    libshared4.so.1.0    libshared5.so.1.0    lkm.c    lkm.ko    lkm.mod.c    lkm1.c    lkm1.ko    lkm1.mod.c    lkm1.mod.o    lkm1.o    longservice    longservice.c    longservicewithThreads    longservicewithThreads.c    lstest    lstest.c    macro.c    main.c    Makefile    main.cpp    main.o    Makefile_tmp    Module.symvers    modules.order    my_ls    my_ls.c    my_ls.c~    my_ls_alpha.c    my_makefile    namepid    namepid.c    negative    negative.c    nestedLoop    nestedLoop.c    newFile    newpProcess    newpProcess.c    newsc    newsocketcli.c    newsocketserv.c    newss    null    o.txt    ob.txt    obfuscated    obfuscated.c    output    output.c    p    p1    p1.c    p2    p2.c    pcap    pcap.c    player.txt    pointer_to_constant    pointer_to_constant.c    pointers    pointers.c    print    print.c    print.i    print.o    print.s    printf    printf.c    proc.c    proc.ko    proc.mod.c    proc.mod.o    proc.o    ps.txt    ptr_diff    ptr_diff.c    rcrsn    rcrsn.c    reloc    reloc.c    reloc.o    safeguard    scanf    scanf.c    script.sh    segfault    segfault.c    shared.c    shared.o    shared1.c    shared1.o    shared2.c    shared2.o    shared3.c    shared3.o    shared4.c    shared4.o    shared5.c    shared5.o    shell    shell.c    shelll    signal    signal.c    signed    signed.c    small_signal    small_signal.c    sniffer    sniffer.c    socket_cli    socket_cli.c    socket_serv    socket_serv.c    stack_curr    stack_curr.c    stk    stk.c    strucarr    strucarr.c    test.c    test.h    test.i    test.jpg    test.o    test.s    test1.c    test1.i    test1.o    test1.s    test2.c    test_valgrind    testp1    testp1.c    threadssync    threadssync.c    threadswithoutsync    threadswithoutsync.c    typecast    typecast.c    typecast1    typecast1.c    typemismatch.c    val    valgrind    valgrind.c    var_arg    var_arg.c    virtual    virtual.c    vlc    voidmain    voidmain.c

As you can see above the output came out to be in alphabetical order.

Next I am working on improving the above code so that it is displayed in a way like :

Code:


 $ ls
 1                  crack          func_ptr.c        libshared4.so            namepid.c              print.c    shared4.o      test.h
 1.c                crack.c        func.s            libshared4.so.1          negative              printf      shared5.c      test.i
 abc                curl.c        gnome-terminal    libshared4.so.1.0        negative.c            printf.c    shared5.o      test.jpg
 acii.c              data.txt      hack_ptr          libshared5.so.1.0        nestedLoop            print.i    shared.c        test.o
 alpha              direntry      hack_ptr.c        lkm1.c                    nestedLoop.c          print.o    shared.o        testp1
 aneesh              direntry.c    hello              lkm1.ko                  newFile                print.s    shell          testp1.c
 aneesh.c            elfvirus      hello1.c          lkm1.mod.c                newpProcess            proc.c      shell.c        test.s
 a.out              environ        hello1.o          lkm1.mod.o                newpProcess.c          proc.ko    shelll          test_valgrind
 array_pointer      environ.c      hello2.c          lkm1.o                    newsc                  proc.mod.c  signal          threadssync
 array_pointer.c    exit          hello2.o          lkm.c                    newsocketcli.c        proc.mod.o  signal.c        threadssync.c
 ascii              exit.c        hello3.c          lkm.ko                    newsocketserv.c        proc.o      signed          threadswithoutsync
 ascii.c            factorial      hello3.o          lkm.mod.c                newss                  ps.txt      signed.c        threadswithoutsync.c
 basicBO            factorial.c    hello4.c          longservice              null                  ptr_diff    small_signal    typecast
 basicBO.c          factorial.cpp  hello4.o          longservice.c            obfuscated            ptr_diff.c  small_signal.c  typecast1
 bufferoverflow      feof          helloWorld        longservicewithThreads    obfuscated.c          rcrsn      sniffer        typecast1.c
 bufferoverflow.c    feof.c        helloWorld.c      longservicewithThreads.c  ob.txt                rcrsn.c    sniffer.c      typecast.c
 bus                file          hide              lstest                    o.txt                  reloc      socket_cli      typemismatch.c
 bus.c              file.c        hide.c            lstest.c                  output                reloc.c    socket_cli.c    val
 byteorder          fileHole      input              macro.c                  output.c              reloc.o    socket_serv    valgrind
 byteorder.c        fileHole.c    intrstin          main.c                    p                      safeguard  socket_serv.c  valgrind.c
 charptrarr          for            intrstin.c        main.cpp                  p1                    scanf      stack_curr      var_arg
 charptrarr.c        for.c          keylogger          main.o                    p1.c                  scanf.c    stack_curr.c    var_arg.c
 cmdline            fork          keylogger.c        Makefile                  p2                    script.sh  stk            virtual
 cmdline.c          fork.c        lib                Makefile_tmp              p2.c                  segfault    stk.c          virtual.c
 code                fp            libshared1.so.1    modules.order            pcap                  segfault.c  strucarr        vlc
 constant_pointer    fp.c          libshared1.so.1.0  Module.symvers            pcap.c                shared1.c  strucarr.c      voidmain
 constant_pointer.c  fread          libshared1.so.1.1  my_ls                    player.txt            shared1.o  test1.c        voidmain.c
 copy                fread.c        libshared2.so.1    my_ls_alpha.c            pointers              shared2.c  test1.i
 copy_getc_putc      func          libshared2.so.1.1  my_ls.c                  pointers.c            shared2.o  test1.o
 copy_getc_putc.c    func.c        libshared3.so      my_ls.c~                  pointer_to_constant    shared3.c  test1.s
 copy_unbuffered    func.o        libshared3.so.1    my_makefile              pointer_to_constant.c  shared3.o  test2.c
 copy_unbuffered.c  func_ptr      libshared3.so.1.0  namepid                  print                  shared4.c  test.c

The above output is from the standard ls utility.

Please compare the above two outputs and see If we have achieved the goal that was set in this article. If you see any discrepancy, kindly report here so that I can solve it and others can learn from it.

Conclusion



To conclude, in this article we wrote a logic through which the outputs that are generated by 'my_ls' is in alphabetical order. We extended the code written in part I of this series. Next up, we will learn how to display the output correctly on screen.

Stay tuned for more!!!


All times are GMT +5.5. The time now is 18:17.