diff options
-rw-r--r-- | Makefile | 67 | ||||
-rw-r--r-- | src/lass.c | 177 |
2 files changed, 244 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6da7a4d --- /dev/null +++ b/Makefile @@ -0,0 +1,67 @@ +# Compiler and flags +CC = gcc +CFLAGS = -Wall -O2 -lm + +# Directories +SRC_DIR = src +BIN_DIR = bin + +# Source and target names +TARGET = lass +SRC = $(SRC_DIR)/$(TARGET).c +PRE = $(BIN_DIR)/$(TARGET).i +ASM = $(BIN_DIR)/$(TARGET).s +OBJ = $(BIN_DIR)/$(TARGET).o +BIN = $(BIN_DIR)/$(TARGET) + +# Example argument for test +ARG = + +# Default: build executable +all: $(BIN) + +# Ensure bin directory exists +$(BIN_DIR): + mkdir -p $(BIN_DIR) + +# Stage 1: Preprocess +$(PRE): $(SRC) | $(BIN_DIR) + $(CC) -E $(CFLAGS) $< -o $@ + +# Stage 2: Compile to assembly +$(ASM): $(PRE) + $(CC) -S $(CFLAGS) $< -o $@ + +# Stage 3: Assemble to object file +$(OBJ): $(ASM) + $(CC) -c $(CFLAGS) $< -o $@ + +# Stage 4: Link object to executable +$(BIN): $(OBJ) + $(CC) $(CFLAGS) $< -o $@ + +# Run tests +define run_test + @name="$(1) $(2)"; \ + width=40; \ + printf "%s" "$$name"; \ + dots=$$((width - $${#name})); \ + for i in $$(seq 1 $$dots); do printf "."; done; \ + ./$(BIN) $(2) >/dev/null 2>&1; \ + ec=$$?; \ + if [ $$ec -eq 0 ] || [ $$ec -eq 2 ]; then \ + echo " OK"; \ + else \ + echo " FAILED (exit $$ec)"; \ + exit 1; \ + fi +endef + +test: $(BIN) + $(call run_test,$(TARGET),no args) + $(call run_test,$(TARGET),$(ARG)) + +# Clean all generated files +clean: + rm -v -f $(BIN_DIR)/$(TARGET).* $(BIN) + diff --git a/src/lass.c b/src/lass.c new file mode 100644 index 0000000..ddbced0 --- /dev/null +++ b/src/lass.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * lass.c - list files and directories in kernel style + */ + +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <string.h> +#include <sys/stat.h> +#include <pwd.h> +#include <grp.h> +#include <time.h> +#include <math.h> +#include <limits.h> + +static int cmp_entries(const void *a, const void *b) +{ + const char *fa = *(const char **)a; + const char *fb = *(const char **)b; + + return strcmp(fa, fb); +} + +static void print_file_info(const char *path, const char *name, int width_uid, + int width_gid, int width_size) +{ + struct stat st; + struct passwd *pw; + struct group *gr; + struct tm *timeinfo; + time_t rawtime; + char time_buffer[20]; + + if (stat(path, &st)) + return; + + printf(S_ISDIR(st.st_mode) ? "d" : + S_ISSOCK(st.st_mode) ? "s" : + S_ISREG(st.st_mode) ? "-" : + S_ISLNK(st.st_mode) ? "l" : + S_ISCHR(st.st_mode) ? "c" : + S_ISBLK(st.st_mode) ? "b" : + S_ISFIFO(st.st_mode) ? "p" : "?"); + + printf((st.st_mode & 0400) ? "r" : "-"); + printf((st.st_mode & 0200) ? "w" : "-"); + printf((st.st_mode & 0100) ? "x" : "-"); + printf((st.st_mode & 0040) ? "r" : "-"); + printf((st.st_mode & 0020) ? "w" : "-"); + printf((st.st_mode & 0010) ? "x" : "-"); + printf((st.st_mode & 0004) ? "r" : "-"); + printf((st.st_mode & 0002) ? "w" : "-"); + printf((st.st_mode & 0001) ? "x" : "-"); + + pw = getpwuid(st.st_uid); + gr = getgrgid(st.st_gid); + + printf(" %*s %*s ", width_uid, pw ? pw->pw_name : "?", + width_gid, gr ? gr->gr_name : "?"); + + time(&rawtime); + timeinfo = localtime(&rawtime); + + strftime(time_buffer, sizeof(time_buffer), + "%a %b %d %H:%M:%S %Y", timeinfo); + + printf("%s ", time_buffer); + + if (S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode)) + printf("%*d ", width_size, 0); + else + printf("%*zu ", width_size, st.st_size); + + printf("%s\n", name); +} + +static int list_files(const char *path) +{ + DIR *dir; + struct dirent *entry; + char **entries; + size_t file_count; + size_t capacity; + int width_uid; + int width_gid; + int width_size; + int size_digits; + struct stat st; + struct passwd *pw; + struct group *gr; + char fullpath[PATH_MAX]; + + file_count = 0; + capacity = 2; + width_uid = 0; + width_gid = 0; + width_size = 0; + + if (stat(path, &st)) + return 1; + + if (!S_ISDIR(st.st_mode)) { + pw = getpwuid(st.st_uid); + gr = getgrgid(st.st_gid); + width_uid = pw ? strlen(pw->pw_name) : 1; + width_gid = gr ? strlen(gr->gr_name) : 1; + width_size = (st.st_size == 0) ? 1 : (int)log10(st.st_size) + 1; + print_file_info(path, path, width_uid, width_gid, width_size); + return 0; + } + + dir = opendir(path); + if (!dir) + return 1; + + entries = malloc(capacity * sizeof(char *)); + if (!entries) { + closedir(dir); + return 1; + } + + while ((entry = readdir(dir)) != NULL) { + if (file_count == capacity) { + capacity *= 2; + entries = realloc(entries, capacity * sizeof(char *)); + if (!entries) { + closedir(dir); + return 1; + } + } + entries[file_count] = strdup(entry->d_name); + if (!entries[file_count]) + continue; + + snprintf(fullpath, sizeof(fullpath), "%s/%s", path, + entry->d_name); + if (stat(fullpath, &st)) + continue; + + pw = getpwuid(st.st_uid); + gr = getgrgid(st.st_gid); + + size_digits = (st.st_size == 0) ? 1 : (int)log10(st.st_size)+1; + + if (size_digits > width_size) + width_size = size_digits; + if (pw && (int)strlen(pw->pw_name) > width_uid) + width_uid = strlen(pw->pw_name); + if (gr && (int)strlen(gr->gr_name) > width_gid) + width_gid = strlen(gr->gr_name); + + file_count++; + } + + closedir(dir); + + qsort(entries, file_count, sizeof(char *), cmp_entries); + + for (size_t i = 0; i < file_count; i++) { + snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entries[i]); + print_file_info(fullpath, entries[i], width_uid, width_gid, + width_size); + free(entries[i]); + } + + free(entries); + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc > 1) + return list_files(argv[1]); + return list_files("."); +} + |