Skip to content

Commit

Permalink
Add basic ls implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Sigmanificient committed Apr 19, 2024
1 parent 747d814 commit 4583fa7
Show file tree
Hide file tree
Showing 9 changed files with 447 additions and 7 deletions.
6 changes: 5 additions & 1 deletion src/ls/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
OUT := ls

SRC := ls.c
SRC := main.c
SRC += list_directories.c
SRC += print_info.c
SRC += recursive_walk.c
SRC += sort_entries.c

include ../shared.mk
107 changes: 107 additions & 0 deletions src/ls/list_directories.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include <dirent.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "my_ls.h"

static
void get_file_info(const char *path, entry_t *entry)
{
if (stat(path, &entry->stat) < 0)
return;
entry->passwd = getpwuid(entry->stat.st_uid);
entry->group = getgrgid(entry->stat.st_gid);
}

static
int read_directory(dirbuff_t *db, DIR *dir, char flags)
{
static char path[PATH_MAX];
int i = 0;

if (dir == NULL)
return -1;
for (struct dirent *dirent = readdir(dir); dirent; dirent = readdir(dir)) {
if (dirent->d_name[0] == '.' && ~flags & F_ALL_FILES)
continue;
if (i == db->size) {
db->size <<= 1;
db->entries = realloc(
db->entries, db->size * sizeof(*db->entries));
}
strcpy(db->entries[i].name, dirent->d_name);
if (flags & (F_LONG_FORM | F_SORT_TIME | F_RECURSIVE))
get_file_info(path_concat(path, db->name, db->entries[i].name),
&db->entries[i]);
i++;
}
return i;
}

static
void print_error(char *dirname)
{
switch (errno) {
case ENOENT:
fprintf(stderr,
"ls: cannot access '%s': No such file or directory\n",
dirname);
return;
case EACCES:
fprintf(stderr,
"ls: cannot open directory '%s': Permission denied\n",
dirname);
return;
default:
fprintf(stderr, "Unknown error\n");
}
}

static
int read_arg(dirbuff_t *db, char flags)
{
struct stat fi;
int count = 1;
DIR *dir;

db->is_file = 0;
if (stat(db->name, &fi) < 0) {
print_error(db->name);
return -1;
}
if (S_ISDIR(fi.st_mode) && ~flags & F_DIRECTORY) {
dir = opendir(db->name);
count = read_directory(db, dir, flags);
closedir(dir);
} else {
strcpy(db->entries[0].name, db->name);
get_file_info(db->name, &db->entries[0]);
db->is_file = 1;
}
return count;
}

int list_dir(dirbuff_t *db, char flags)
{
int count = read_arg(db, flags);

if (count == -1)
return -1;
sort_entries(db->entries, count);
if (flags & F_SORT_TIME)
sort_entries_by_time(db->entries, count);
if (flags & (F_SHOW_DIRS | F_RECURSIVE) && !db->is_file)
printf("%s:\n", db->name);
print_entries(db->entries, count, flags);
if (flags & F_RECURSIVE && !db->is_file)
recurse(db, count, flags);
return 0;
}
6 changes: 0 additions & 6 deletions src/ls/ls.c

This file was deleted.

65 changes: 65 additions & 0 deletions src/ls/ls.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#ifndef MY_LS_H
#define MY_LS_H

#include <stddef.h>

#define ZERO_OR(expr, default) ((!!(expr)) * default)

#define __USE_XOPEN2K8
#define __USE_MISC

#include <linux/limits.h>
#include <sys/stat.h>

#define MIN_ALLOCATED_ENTRY (1024)

enum {
EXIT_OK = 0,
EXIT_KO = 84
};

enum {
F_ALL_FILES = 1 << 0,
F_LONG_FORM = 1 << 1,
F_RECURSIVE = 1 << 2,
F_DIRECTORY = 1 << 3,
F_REV_ORDER = 1 << 4,
F_SORT_TIME = 1 << 5,
F_SHOW_DIRS = 1 << 6,
};

typedef struct {
struct stat stat;
struct passwd *passwd;
struct group *group;
char name[NAME_MAX + 1];
} entry_t;

typedef struct {
char *name;
entry_t *entries;
int size;
int is_file;
} dirbuff_t;

inline
int stridx(const char *str, char c)
{
for (const char *p = str; *p != '\0'; p++)
if (*p == c)
return p - str;
return -1;
}

char *strdup(char const *s);

int list_dir(dirbuff_t *db, char flags);
int recurse(dirbuff_t *db, int count, char flags);

void print_entries(entry_t *entry, int count, char flags);
char *path_concat(char *dest, char *basepath, char *suffix);

void sort_entries(entry_t *entries, int count);
void sort_entries_by_time(entry_t *entries, int count);

#endif
68 changes: 68 additions & 0 deletions src/ls/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <stdlib.h>

#include "ls.h"

static const char *FLAGLIST = "alRdrt";
static char DEFAULT_LOCATION[2] = ".";

static
char compose_flaglist(int argc, char **argv)
{
int flags = 0;

for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-' || argv[i][1] == '\0')
continue;
for (int j = 1; argv[i][j] != '\0'; j++)
flags |= 1 << (stridx(FLAGLIST, argv[i][j]) + 1);
}
return (char)(flags >> 1);
}

static
int count_targets(int argc, char **argv)
{
int count = 0;

for (int i = 1; i < argc; i++)
if (argv[i][0] != '-' || argv[i][1] == '\0')
count++;
return count;
}

static
int list_dirs(dirbuff_t *db, int argc, char **argv, char flags)
{
int err = 0;
int count = count_targets(argc, argv);

if (count == 0) {
db->name = DEFAULT_LOCATION;
err |= list_dir(db, flags);
}
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][1] != '\0')
continue;
db->name = argv[i];
if (count > 1)
flags |= F_SHOW_DIRS;
err |= list_dir(db, flags);
}
return err;
}

int main(int argc, char **argv)
{
dirbuff_t db = { .size = MIN_ALLOCATED_ENTRY };
char flags = compose_flaglist(argc, argv);
int err = 0;

db.entries = malloc(db.size * sizeof(*db.entries));
if (db.entries == NULL)
return EXIT_KO;
if (flags & F_DIRECTORY)
flags &= ~F_RECURSIVE;
err |= list_dirs(&db, argc, argv, flags);
free(db.entries);
return err ? EXIT_KO : EXIT_OK;
}
105 changes: 105 additions & 0 deletions src/ls/print_info.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/sysmacros.h>
#include <sys/types.h>
#include <time.h>

#include "ls.h"

static
void get_file_right(char bits[], entry_t *entry)
{
mode_t mode = entry->stat.st_mode;
static const char *s = "-rwx";

bits[0] = s[(unsigned char)ZERO_OR(mode & S_IRUSR, 1)];
bits[1] = s[(unsigned char)ZERO_OR(mode & S_IWUSR, 2)];
bits[2] = s[(unsigned char)ZERO_OR(mode & S_IXUSR, 3)];
bits[3] = s[(unsigned char)ZERO_OR(mode & S_IRGRP, 1)];
bits[4] = s[(unsigned char)ZERO_OR(mode & S_IWGRP, 2)];
bits[5] = s[(unsigned char)ZERO_OR(mode & S_IXGRP, 3)];
bits[6] = s[(unsigned char)ZERO_OR(mode & S_IROTH, 1)];
bits[7] = s[(unsigned char)ZERO_OR(mode & S_IWOTH, 2)];
bits[8] = s[(unsigned char)ZERO_OR(mode & S_IXOTH, 3)];
if (mode & S_ISUID)
bits[1] = (mode & S_IXUSR) ? 's' : 'S';
if (mode & S_ISGID)
bits[3] = (mode & S_IXGRP) ? 's' : 'l';
if (mode & S_ISVTX)
bits[8] = (mode & S_IXOTH) ? 't' : 'T';
}

static
char get_file_type(entry_t *entry)
{
const char typ[] = {
[ S_IFBLK ] = 'b',
[ S_IFCHR ] = 'c',
[ S_IFDIR ] = 'd',
[ S_IFIFO ] = 'p',
[ S_IFLNK ] = 'l',
[ S_IFREG ] = '-',
[ S_IFSOCK ] = 's',
[ 0 ] = '?'
};

return typ[(entry->stat.st_mode & S_IFMT)];
}

static
char *get_creation_time(entry_t *entry)
{
static char fmt[12];
char *ct = ctime(&entry->stat.st_mtim.tv_sec);
time_t now = time(NULL);
const int six_month_sec = 6 * 24 * 3600 * 31;

if (entry->stat.st_mtim.tv_sec + six_month_sec < now) {
strncpy(fmt, ct + 4, 7);
strncpy(fmt + 7, ct + 19, 5);
} else
strncpy(fmt, ct + 4, 12);
return fmt;
}

static
void print_file_infos(entry_t *entry)
{
struct stat *fi = &entry->stat;
char perms[10] = { [0] = get_file_type(entry) };
const char *owner = (entry->passwd == NULL) ? "?" : entry->passwd->pw_name;
const char *grp = (entry->group == NULL) ? "?" : entry->group->gr_name;
char *time = get_creation_time(entry);

get_file_right(perms + 1, entry);
printf("%.10s %ld %s %s ", perms, fi->st_nlink, owner, grp);
if (stridx("bc", perms[0]) != -1)
printf("%d, %d", major(fi->st_rdev), minor(fi->st_rdev));
else
printf("%ld", fi->st_size);
printf(" %.12s ", time);
}

void print_entries(entry_t *entry, int count, char flags)
{
int d;

if (flags & F_REV_ORDER) {
d = -1;
entry += (count - 1);
} else
d = 1;
for (int i = 0; i < count; i++) {
if (flags & F_LONG_FORM)
print_file_infos(entry);
printf("%s", entry->name);
printf(((i + 1) == count || flags & F_LONG_FORM) ? "\n" : " ");
entry += d;
}
}
Loading

0 comments on commit 4583fa7

Please sign in to comment.