Search code examples
arrayscsortingc-stringsbubble-sort

Can't sort array in alphabetical order


I'm trying to make a program that simulates the ls command and then sorts the files in case insensitive alphabetical order. So far, all of the file names go into the words array, but when I try to compile, there is a problem.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>

// This program is pretty much a simulation of the ls command. Find out how to scan all of the files
// in the directory it's being run in and print out all the file names. Has to be in order.

int main(int argc, char* argv[])
{
    char **words = calloc(1000, sizeof(*words));
    char **words2 = calloc(1000, sizeof(*words2));
    
    DIR *d;
    struct dirent *dir;  // Pointer for directory entry 
    d = opendir(".");
    char* a = ".";
    char* b = "..";
    char* c = "ls";
    int ret1, ret2, ret3, count = 0;
    
    
    if (d)  // opendir returns NULL if couldn't open directory
    {
        while ((dir = readdir(d)) != NULL)
        {
            ret1 = strcmp(dir->d_name, a); // Compare with the parent directory.
            ret2 = strcmp(dir->d_name, b); // Compare with the parent directory.
            ret3 = strcmp(dir->d_name, c); // Compare with the ls
            
            if (ret1 == 0 || ret2 == 0 || ret3 == 0)
            {
                // Skip the ., .., and ls
            }
            else
            {
                words[count] = dir->d_name; // Put the file name in the array.
                count++;
            }
        }
        
        for (int i = 1; i < 10; i++) // Start readjusting the array in alphabetical order.
        {
            for (int j = 1; j < 10; j++)
            {
                if (strcmp(words[j - 1], words[j]) > 0)
                {
                    strcpy(words2, words[j - 1]);
                    strcpy(words[j - 1], words[j]);
                    strcpy(words[j], words2);
                }
            }
        }
        
        // Print every word in the array.
        while (count != 0)
        {
            printf("%s\n", words[count - 1]);
            count--;
        }
        
    }   
    // Closing and freeing
    closedir(d);
    for (int a = 0; a < 1000; ++a)
    {
        free(words[a]);
    }
    for (int a = 0; a < 1000; ++a)
    {
        free(words2[a]);
    }
    free(words);
    free(words2);
   
    return 0;
}

When I compile, this is the error message that I get below. It happens during the sequence when I'm trying to sort the array. Is there something I can do to fix this issue?

ls.c: In function ‘main’:
ls.c:52:13: warning: passing argument 1 of ‘strcpy’ from incompatible pointer type [-Wincompatible-pointer-types]
   52 |      strcpy(words2, words[j - 1]);
      |             ^~~~~~
      |             |
      |             char **
In file included from ls.c:3:
/usr/include/string.h:122:14: note: expected ‘char * restrict’ but argument is of type ‘char **’
  122 | extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
      |              ^~~~~~
ls.c:54:23: warning: passing argument 2 of ‘strcpy’ from incompatible pointer type [-Wincompatible-pointer-types]
   54 |      strcpy(words[j], words2);
      |                       ^~~~~~
      |                       |
      |                       char **
In file included from ls.c:3:
/usr/include/string.h:122:14: note: expected ‘const char * restrict’ but argument is of type ‘char **’
  122 | extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
      |              ^~~~~~

Solution

  • The answer of Vlad from Moscow is totally on the point. Let me add that there is another very nasty bug in your code: the d_name field of the DIR structure is not newly allocated, so you really need to allocate and copy the name into the words sequence:

    words[count] = strdup(dir->d_name); // Put the file name in the array.
    

    Moreover, that count is really important for your sequence. Use it for sorting and don't thrash it when printing the array contents:

            // Print every word in the array.
            for (int i = 0; i < count; ++i) {
                printf("%s\n", words[i]);
            }
    

    Finally, you assume that all words have the same length, but that's not right. So what you want to swap are the pointers, not the chars they point to. And if you want an easy to implement sort, use selection sort: it's as slow as bubble sort, but with less swapping. By the way, your bubble sort is wrong, because you don't track the swapping, so you are losing the only advantage of bubble sort, that is early stopping. A possible implementation of selection sort in your case could be:

    for (int i = 0; i < count; ++i) {
        int imin = i;
        for (int j = i + 1; j < count; ++j) {
            if (strcmp(words[j], words[imin]) < 0) {
                imin = j;
            }
        }
        char *tmp = words[i];
        words[i] = words[imin];
        words[imin] = tmp;
    }
    

    Then, don't use words2 anymore, and free only up to count.