diff --git a/exercises/coreutils/README.md b/exercises/coreutils/README.md new file mode 100644 index 0000000..af71d01 --- /dev/null +++ b/exercises/coreutils/README.md @@ -0,0 +1,48 @@ +# 🛠️ Coreutils Reimplementations + +This directory contains reimplementations of classic **GNU Coreutils** commands in C, +using only **POSIX system calls** and the standard C library. + +The goal is to gain hands-on experience with **Linux system programming**, while practicing +error handling, portability, and clean coding. + +--- + +## 📂 Structure +Each command is implemented in its own folder: +``` + +coreutils/ +├── ls/ # list directory contents +├── cat/ # concatenate files +├── cp/ # copy files +├── echo/ # echo a string +├── sleep/ # wait X millis +```` + +Each subfolder contains: +- `main.c` → source code +- `Makefile` → to build the command + +--- + +## 🎯 Learning Objectives +- Practice **file and directory operations** +- Reinforce knowledge of **system calls**: `open`, `read`, `write`, `stat`, `opendir`, `unlink`, `link`, etc. +- Develop robust error handling +- Understand the **UNIX philosophy**: small tools, combined power + +--- + +## ⚙️ Build & Run +Example (`ls`): + +```bash +cd ls +make +./ls . +```` + +--- + +*Last updated: 2025-09-18* diff --git a/exercises/coreutils/ls/Makefile b/exercises/coreutils/ls/Makefile new file mode 100644 index 0000000..986ac7c --- /dev/null +++ b/exercises/coreutils/ls/Makefile @@ -0,0 +1,19 @@ +CC := gcc +CFLAGS := -Wall -Wextra -pedantic -std=c11 -pthread -g +TARGET := ls +SRC := main.c +OBJS := $(SRC:.c=.o) + +.PHONY: all clean valgrind + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) -o $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(TARGET) $(OBJS) + diff --git a/exercises/coreutils/ls/main.c b/exercises/coreutils/ls/main.c new file mode 100644 index 0000000..dad10e0 --- /dev/null +++ b/exercises/coreutils/ls/main.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char filetype(mode_t mode); +char *getpermissions(mode_t mode); + +int main(int argc, char *argv[]) +{ + char *dirname = "."; + if (argc > 1) + dirname = argv[1]; + + DIR *dir = opendir(dirname); + if (dir == NULL) { + int errsv = errno; + fprintf(stderr, "%s: cannot access %s: %s\n", argv[0], dirname, strerror(errsv)); + return EXIT_FAILURE; + } + + struct stat statbuf; + struct dirent *dirp; + while ((dirp = readdir(dir)) != NULL) { + // Skip current and previous directory symbols + if (strcmp(dirp->d_name, ".") == 0 || + strcmp(dirp->d_name, "..") == 0) + continue; + + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/%s", dirname, dirp->d_name); + if (stat(path, &statbuf) < 0) { + int errsv = errno; + fprintf(stderr, "%s: cannot stat %s: %s\n", argv[0], path, strerror(errsv)); + continue; + } + + char type = filetype(statbuf.st_mode); + char *permissions = getpermissions(statbuf.st_mode); + + /* + * Format output: + * TYPE: d (directory) | f (regular file) | l (symbolic link) | o (others) + * PERMISSIONS (ex. rwxrwxrwx) + * NR HARD LINKS + * USER + * GROUP + * SIZE + * NAME + */ + printf("%c%s %lu %u %u %6lu %s\n", + type, permissions, statbuf.st_nlink, + statbuf.st_uid, statbuf.st_gid, statbuf.st_size, + dirp->d_name); + + if (permissions != NULL) { + free(permissions); + permissions = NULL; + } + } + + if (closedir(dir) == -1) { + int errsv = errno; + fprintf(stderr, "%s: cannot close %s: %s\n", argv[0], dirname, strerror(errsv)); + return EXIT_FAILURE; + } +} + +char filetype(mode_t mode) +{ + if (S_ISREG(mode)) return '-'; + if (S_ISDIR(mode)) return 'd'; + if (S_ISLNK(mode)) return 'l'; + if (S_ISCHR(mode)) return 'c'; + if (S_ISBLK(mode)) return 'b'; + if (S_ISFIFO(mode)) return 'p'; +#ifdef S_ISSOCK + if (S_ISSOCK(mode)) return 's'; +#endif + return '?'; +} + +char checkpermission(mode_t mode, int perm, char c) +{ + return (mode & perm) ? c : '-'; +} + +char *getpermissions(mode_t mode) +{ + char *permissions = malloc(9 + 1); + + /* User */ + permissions[0] = checkpermission(mode, S_IRUSR, 'r'); + permissions[1] = checkpermission(mode, S_IWUSR, 'w'); + permissions[2] = checkpermission(mode, S_IXUSR, 'x'); + + /* Group */ + permissions[3] = checkpermission(mode, S_IRGRP, 'r'); + permissions[4] = checkpermission(mode, S_IWGRP, 'w'); + permissions[5] = checkpermission(mode, S_IXGRP, 'x'); + + /* Other */ + permissions[6] = checkpermission(mode, S_IROTH, 'r'); + permissions[7] = checkpermission(mode, S_IWOTH, 'w'); + permissions[8] = checkpermission(mode, S_IXOTH, 'x'); + + permissions[9] = '\0'; + return permissions; +}