$ man tshabbic.projects.my-ls
MY-LS(8)Simple Ls ImplementationMY-LS(8)
Name

my-ls -- simple ls(1) implementation

Synopsis

my-ls [ -AacdFfhiklnqRrSstuw] [file ...]

Description

[See the Source Code!] [See the actual Manual Page]


Written for NetBSD 10.1+ over the course of a month as the midterm for my Advanced Programming in the Unix Environment class, this implementation of ls(1), while not as encompassing as ls(1) on Linux systems, features and supports ~20 flags. When this was first assigned, I got to work immediately, only to learn about two weeks later that there existed a standard C library, that would largely and more or less effortlessly handle the recursive directory traversal, in addition to sorting output, and much more. Despite this however, I decided to not use the fts library such that I could further hone my skills by implementing what was necessary manually. Note that in a production environment, such as a real product, etc., I would have refactored the codebase to use fts without question. (It would have also prevented most bugs found below that I didn't catch prior to final submission).

One of the biggest challenges by far was dealing with PATH_MAX, which on NetBSD is an immutable, system-defined constant found in that specifies the maximum amount of characters a path name can be, but of course the operating system and file system still allows the creation of as many nested directories as desired. The approach I took to bypass this seemingly hard limitation is a "cd crawl", where we cd into one directory at a time and only use the relative path name until we get to the final directory and file. For example, if a file resided at /one/two/three/file, the crawl would execute cd /one, then cd ./two, then cd ./three, and then access the file itself (and of course my-ls would set it's current working directory back to what it was before the crawl, for obvious reasons). This worked quite well on my machine, directories tens of thousands of characters long (far more than NetBSD's PATH_MAX of 4096 with and without files within them worked without issue; though, as mentioned below, my Professor did find some instances where it failed that I could not reproduce on my system -- something easily avoided with the use of fts.

Other challenges included implementing the copious amounts of features desired by the assignment spec, including but not limited to having the environ(7) affect program behavior (e.g., setting the timezone to print the last modified/accessed/file status change), determining how certain flags should behave with each other when the manual page didn't describe it, etc. Many of these nuanced decisions, among other experiences, are documented as comments in the source code linked above if you'd like to learn more.

Usage

See the provided manual page for usage details.

Bugs

> buf[i] = (ls_options.nonprint_mode == 'q' && !isprint(c)) ? '?' : c; the nonprint mode is being checked for every character whereas it can be done once earlier, potentially increasing performance a bit.

> ./my-ls -d / yields an empty line due to special handling of the root directory.

> Pathnames are stripped, yielding only file names. (e.g. $ ./my-ls /etc/passwd yields "passwd").

> Handling of deeply nested directory structures, notably involving large file names, can fail on some NetBSD system configurations.

History

This project was done during Fall of 2025 for CS 631 - Advanced Programming in the Unix Environment @ Stevens Institute of Technology.