Developing Linux Utility - Part II Arranging Output in Alphabetical Order

poornaMoksha's Avatar author of Developing Linux Utility - Part II Arranging Output in Alphabetical Order
This is an article on Developing Linux Utility - Part II Arranging Output in Alphabetical Order in C.
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!!!