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 [URL=http://www.go4expert.com/articles/valgrind-memory-leak-detection-tutorial-t26780/]valgrind[/URL] 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!!!