Sitemap

LS implementation

10 min readSep 19, 2024

--

Overview

The ls command in Unix-like operating systems is used to list the contents of a directory. It displays the names of files and directories within the specified directory, allowing users to quickly view and manage their files. The command can be customized with various options to show detailed information such as file permissions, sizes, modification times, and hidden files. This flexibility makes ls a fundamental tool for navigating and organizing the file system.

ls -a (List All)

The ls -a command in Linux lists all files and directories within the current directory, including hidden files. In Linux, hidden files and directories start with a dot (.) Which is mean that ls -a = ls + hidden files.

void list_all(const char *path, int show_all)
  • Takes a directory path as a string.
  • Takes an integer show_all to decide whether to display hidden files (show_all is a Flag — > 1 : show all hidden Files | 0 : normal ls).
    DIR *dir;
struct dirent *entry;
dir = opendir(path);

. dir is a pointer to a DIR structure representing the directory stream which is the same type of the Return of opendir().

entry is a pointer to a struct dirent which represents directory entries.

while ((entry = readdir(dir)) != NULL) {
if (!show_all && entry->d_name[0] == '.') {
continue;
}
....
closedir(dir);}
  • Loops through each entry in the directory using readdir.
  • Skips hidden files if show_all is not set.
  • Closes the directory stream using closedir

ls -l (List Long)

The ls -l command in Linux lists the files and directories in the current directory (or a specified directory) in a detailed, long format. This format provides additional information for each file or directory.

        printf((S_ISDIR(file_stat.st_mode)) ? "d" : "-");
printf((file_stat.st_mode & S_IRUSR) ? "r" : "-");
printf((file_stat.st_mode & S_IWUSR) ? "w" : "-");
printf((file_stat.st_mode & S_IXUSR) ? "x" : "-");
printf((file_stat.st_mode & S_IRGRP) ? "r" : "-");
printf((file_stat.st_mode & S_IWGRP) ? "w" : "-");
printf((file_stat.st_mode & S_IXGRP) ? "x" : "-");
printf((file_stat.st_mode & S_IROTH) ? "r" : "-");
printf((file_stat.st_mode & S_IWOTH) ? "w" : "-");
printf((file_stat.st_mode & S_IXOTH) ? "x" : "-");
printf(" %ld", file_stat.st_nlink);
struct passwd *pw = getpwuid(file_stat.st_uid);
struct group *gr = getgrgid(file_stat.st_gid);
printf(" %s %s", pw ? pw->pw_name : "-", gr ? gr->gr_name : "-");
printf(" %5ld", file_stat.st_size);
char time_str[20];
strftime(time_str, sizeof(time_str), "%b %d %H:%M", localtime(&file_stat.st_mtime));
printf(" %s", time_str);

Loops through each sorted entry to print detailed information:

  • File type and permissions: Checks the st_mode field in file_stat to print file type and permission bits.
  • Number of links: Prints the number of hard links to the file (st_nlink).
  • Owner and group: Retrieves and prints the owner’s username and group name using getpwuid and getgrgid.
  • File size: Prints the file size in bytes (st_size).
  • Modification time: Formats and prints the file’s last modification time (st_mtime).
  • File name: If color output is enabled, it prints the file name in color using print_colored. Otherwise, it prints it as plain text.

Frees each entry’s memory after printing.

you can get this Information from <sys/stat.h> library , The <sys/stat.h> header defines the structure of the data returned by the functions fstat(), lstat(), and stat().

ls -d (List Directory)

When you use ls -d without specifying -l, the command will simply list the directory name itself rather than the contents of the directory. This is useful when you want to view the directory entry, not its contents.

   
if (stat(path, &path_stat) == -1) {
perror("stat");
return; }

Calls stat to retrieve information about the path. If stat returns -1, an error occurred, and perror prints the error message. The function returns early in this case, as it can't proceed without the file status.

S_ISDIR(path_stat.st_mode): Checks if the path refers to a directory using the st_mode field from the stat structure.

ls -i (List by Inode number)

The ls -i command in Linux is used to display the inode numbers of files and directories. An inode number is a unique identifier assigned to each file or directory in a filesystem. It contains metadata about the file, such as its size, permissions, and location on disk, but not the filename itself.

for (size_t i = 0; i < size; i++) {
printf("%lu %s\n", entries[i].inode, entries[i].name);
}

Purpose: Implements the list_inode function to list files in a directory with their inode numbers:

ls -t (List by Time)

The ls -t command in Linux lists files and directories in the current directory (or a specified directory) sorted by modification time, with the most recently modified files appearing first.

First, we need to explain Sorting.

struct file_info {
char *name; // File name
time_t mod_time; // File modification time
};

// Function to compare files by modification time for qsort
// Sorts in descending order (most recently modified files first)
int compare_mod_time(const void *a, const void *b) {
struct file_info *file_a = (struct file_info *)a;
struct file_info *file_b = (struct file_info *)b;
return (file_b->mod_time - file_a->mod_time); // Return difference in modification times
}
  • int compare_mod_time: The function is named compare_mod_time.
  • const void *a and const void *b: These are pointers to the elements being compared. qsort requires the comparator function to use void * pointers because it can handle any type of data. The specific type of the data being compared is determined within the function.
  • (struct file_info *)a: Casts the void * pointer a to a struct file_info *. This is necessary because qsort only provides generic void * pointers, and you need to cast them to the appropriate type.
  • (struct file_info *)b: Similarly casts b to a struct file_info *.
  • file_b->mod_time: Accesses the modification time of the file pointed to by file_b.
  • file_a->mod_time: Accesses the modification time of the file pointed to by file_a.
  • file_b->mod_time - file_a->mod_time: Calculates the difference between the modification times of file_b and file_a.

Return Value

  • return (file_b->mod_time - file_a->mod_time): Returns the difference between the modification times of file_b and file_a:
  • If the result is positive, file_b is considered to be less than file_a (i.e., file_b is more recent than file_a).
  • If the result is negative, file_b is considered to be greater than file_a (i.e., file_a is more recent than file_b).
  • If the result is zero, both files have the same modification time.
// Store file information (name and modification time)
if (file_count >= capacity) { // If current capacity is exceeded, double the capacity
capacity *= 2;
files = realloc(files, capacity * sizeof(struct file_info));
if (files == NULL) { // Error handling for memory reallocation
perror("realloc");
closedir(dir);
return;
}
}
files[file_count].name = strdup(entry->d_name); // Duplicate the file name
if (files[file_count].name == NULL) { // Error handling for strdup
perror("strdup");
continue;
}
files[file_count].mod_time = file_stat.st_mtime; // Store modification time
file_count++;
}

// Sort files by modification time using qsort
qsort(files, file_count, sizeof(struct file_info), compare_mod_time);
  • if (file_count >= capacity): Checks if the number of files (file_count) has reached or exceeded the current capacity of the files array. This is needed because arrays in C are statically allocated, and the initial allocation might not be sufficient as more files are processed.
  • capacity *= 2: Doubles the capacity to accommodate more files. This is a common strategy to minimize the number of reallocations needed as more files are added.
  • files = realloc(files, capacity * sizeof(struct file_info)): Resizes the files array to the new capacity. realloc reallocates the memory block pointed to by files to a larger size. If the function fails, files will be NULL.
  • if (files == NULL): Checks if realloc failed to allocate memory. If so, it prints an error message and closes the directory, then returns from the function to prevent further execution.
  • files[file_count].name = strdup(entry->d_name): Allocates memory for the file name and copies the file name into this newly allocated memory. strdup is used to duplicate the string entry->d_name. This avoids modifying the original string or overwriting it.
  • if (files[file_count].name == NULL): Checks if strdup failed to allocate memory for the file name. If it did, it prints an error message and continues to the next iteration of the loop, skipping the current file.
  • files[file_count].mod_time = file_stat.st_mtime: Stores the modification time of the file in the mod_time field of the file_info structure.
  • file_count++: Increments the file_count variable, indicating that another file entry has been added.
  • qsort(files, file_count, sizeof(struct file_info), compare_mod_time): Uses the qsort function to sort the files array.
  • files: Pointer to the array of file_info structures to be sorted.
  • file_count: Number of elements in the files array.
  • sizeof(struct file_info): Size of each element in the array.
  • compare_mod_time: Pointer to the comparator function that determines the order of elements based on their modification times. The comparator function is used by qsort to arrange the elements in the desired order.

so, Store File Info: Uses stat to get file modification time and stores file names and times in the files array. Resizes the array dynamically if needed.

Close Directory: Closes the directory after reading entries.

Sort Files: Sorts the files array by modification time using qsort and the compare_mod_time function.

Print Files: Prints file names sorted by modification time

ls -u (List by Access Time)

The ls -u command in Linux lists files and directories sorted by the last access time (instead of modification time). This option is useful when you want to see which files or directories were last accessed most recently.

ls -u is like ls -t in implementation but,

  • ls -u: Sorts by the last access time of files and directories.
  • ls -t: Sorts by the last modification time of files and directories.

Choose between -u and -t based on whether you're interested in the time a file was accessed or the time it was modified.

ls -f (List with no-sorting)

The ls -f command in Linux is used to list files and directories in a directory without sorting them. This option displays the files and directories in the order they are stored in the directory, which is typically the order in which they were created or added. It also appends special characters to indicate the type of each file or directory.

This is useful for viewing files in the order they were added to a directory or when sorting is not required.

ls -1 (List File in a single-column format)

The ls -1 (that's a lowercase "L" followed by a number one) command in Linux lists files and directories in a single-column format. Each file or directory is listed on a new line, without any additional details or formatting.

Lists files and directories in a simple single-column format, with each item on its own line. It’s useful for generating a straightforward list of items, especially for use in scripts or when you need to process filenames individually.

Extra Features

Colors

#define DEFAULT_COLOR "\033[0m"       // Default color (reset to terminal's default)
#define DIR_COLOR "\033[1;34m" // Color for directories (bold blue)
#define EXEC_COLOR "\033[1;32m" // Color for executable files (bold green)
#define LINK_COLOR "\033[1;36m" // Color for symbolic links (bold cyan)
#define SPECIAL_COLOR "\033[1;33m" // Color for special files (bold yellow)
const char *get_color(const struct stat *file_stat) {
if (S_ISDIR(file_stat->st_mode)) { // Check if the file is a directory
return DIR_COLOR; // Return the color for directories
} else if (S_ISLNK(file_stat->st_mode)) { // Check if the file is a symbolic link
return LINK_COLOR; // Return the color for symbolic links
} else if (file_stat->st_mode & S_IXUSR) { // Check if the file is executable by the user
return EXEC_COLOR; // Return the color for executables
} else if (S_ISCHR(file_stat->st_mode) || S_ISBLK(file_stat->st_mode)) { // Check if the file is a character or block device
return SPECIAL_COLOR; // Return the color for special files
}
return DEFAULT_COLOR; // Default color for other file types
}

get_color: Determines the color to use based on the file's status.

  • S_ISDIR(file_stat->st_mode): Checks if the file is a directory.
  • S_ISLNK(file_stat->st_mode): Checks if the file is a symbolic link.
  • file_stat->st_mode & S_IXUSR: Checks if the file is executable by the user.
  • S_ISCHR(file_stat->st_mode) || S_ISBLK(file_stat->st_mode): Checks if the file is a character device or block device.
  • return DEFAULT_COLOR: Returns the default color for files that don't match any of the above conditions.
void print_colored(const char *name, const struct stat *file_stat) {
const char *color = get_color(file_stat); // Get the appropriate color for the file
printf("%s%s%s", color, name, DEFAULT_COLOR); // Print the file name with the color and reset to default color
}

print_colored: Prints the file name with the appropriate color.

  • get_color(file_stat): Retrieves the color based on the file's status.
  • printf("%s%s%s", color, name, DEFAULT_COLOR): Prints the file name with the selected color and then resets the color to default.

Alphabetical order

// Function to list all files in a directory with optional color output and sorting
void list_all(const char *path, int show_all) {
DIR *dir;
int num_entries;
int color_output = isatty(STDOUT_FILENO); // Check if output is a terminal to enable color

// Open the directory
dir = opendir(path);
if (dir == NULL) {
perror("opendir"); // Print error if directory cannot be opened
return;
}

// Collect and sort directory entries
char **entries = collect_and_sort_entries(dir, show_all, &num_entries);
if (entries == NULL) {
closedir(dir);
return;
}

// Print the sorted entries
for (int i = 0; i < num_entries; i++) {
if (color_output) { // If terminal supports color output
struct stat file_stat;
if (stat(entries[i], &file_stat) == -1) {
perror("stat"); // Print error if file status cannot be retrieved
continue;
}
print_colored(entries[i], &file_stat); // Print the file name with color
} else {
printf("%s", entries[i]); // Print the file name without color
}
printf("\n"); // New line after each file name
free(entries[i]); // Free the duplicated string
}

// Clean up
free(entries); // Free the allocated memory for entries
closedir(dir); // Close the directory stream
}
  • char **entries = NULL;: Initializes a pointer to hold a list of file names.
  • int num_entries = 0;: Keeps track of the number of entries collected.
  • while ((entry = readdir(dir)) != NULL): Loops through each entry in the directory.
  • if (!show_all && entry->d_name[0] == '.'): Skips hidden files if show_all is not set.
  • entries = realloc(entries, sizeof(char *) * (num_entries + 1));: Resizes the entries array to accommodate a new entry. If realloc fails, the program prints an error message and exits.
  • entries[num_entries] = strdup(entry->d_name);: Duplicates the entry name and stores it in the entries array. If strdup fails, the program prints an error message and exits.
  • num_entries++;: Increments the count of entries.
  • sort_entries(entries, num_entries);: Calls a function to sort the array of file names. The sort_entries function is assumed to perform the sorting. The sorting is done alphabetically (or case-insensitively, depending on implementation), arranging the entries in order from A to Z.

Finally, The main function

    while ((opt = getopt(argc, argv, "adil1tucfh")) != -1) {
switch (opt) {
case 'a':
show_all = 1; // Include hidden files
break;

Option Parsing:

  • getopt is used to parse command-line options (-a, -d, -i, etc.).
  • The switch statement handles each option, setting corresponding flags.

Handling the Path Argument:

  • After parsing options, if there are remaining arguments (optind < argc), the first one is assumed to be the directory path.

Executing Listing Functions:

  • Based on the flags set, the appropriate function is called to list the directory contents:
  • list_directory(), list_inode(), list_long(), list_single_column(), list_time(), list_access(), list_creation(), list_unsorted(), or list_all().

you can find the fully implementation from here

--

--

No responses yet