From e7958242d8d62165e98be71a659a9a58bfc579c8 Mon Sep 17 00:00:00 2001 From: draccut Date: Sun, 13 Sep 2026 20:52:58 +0200 Subject: [PATCH] rust ll --- lll.c | 713 ------------------------------------------------------ lll.rs | 742 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 742 insertions(+), 713 deletions(-) delete mode 100644 lll.c create mode 100755 lll.rs diff --git a/lll.c b/lll.c deleted file mode 100644 index e499581..0000000 --- a/lll.c +++ /dev/null @@ -1,713 +0,0 @@ -/** - * KOMPILATION - * =========== - * gcc -Og -fstack-protector-strong -D_FORTIFY_SOURCE=3 -fstrict-flex-arrays=3 -fno-strict-aliasing -fno-strict-overflow -fno-delete-null-pointer-checks -fcf-protection=full -Wall -Wextra -Wformat -Wformat-security -fPIE -pie -Wl,-z,relro,-z,now -Wl,-z,noexecstack -o lll lll.c - */ -#define _POSIX_C_SOURCE 200809L - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MOUNT_LOOPS 2 -#ifndef MAX_PATH -#define MAX_PATH 0xFFF -#endif -#define FILE_SYSTEM_BUFF_SIZE (MAX_PATH * MOUNT_LOOPS) -#define NR_OF_SUPPORTET_LANG 13 -#define CARD_LEN 10 -#define ERRR 0xFFFAFFFu - -#define LLL_HELP_MESSAGE_JANUAR_X \ - "-s --size sort by size\n" \ - "-l --ls ls like output\n" \ - "-h --help print help\n" \ - "-p --path set path default is current\n" \ - "-q --qqqq print all absolut paths of a folder\n" \ - "@COMPILER gcc (GCC) 15.2.0\n" - -typedef struct { - const char *name; - uint64_t countet_size; - int globalsumindex; -} Type; - -typedef struct { - time_t unixtimestamp; - char *row; -} TTAS; - -static Type types[NR_OF_SUPPORTET_LANG] = { - {"", 0ULL, 0}, - {"c", 0ULL, 1}, - {"cpp", 0ULL, 2}, - {"ino", 0ULL, 3}, - {"py", 0ULL, 4}, - {"java", 0ULL, 5}, - {"sh", 0ULL, 6}, - {"js", 0ULL, 7}, - {"html", 0ULL, 8}, - {"asm", 0ULL, 9}, - {"rs", 0ULL, 10}, - {"sql", 0ULL, 11}, - {"h", 0ULL, 1} -}; - -static const char *nameList[NR_OF_SUPPORTET_LANG] = { - "Undefined","C","C++","ESP","Python","Java","ShellScript","JavaScript","HTML","Assembly","Rust","SQL","C" -}; - -static const uint64_t colorList[NR_OF_SUPPORTET_LANG] = { - 0x2222077877CCULL, - 0x222205666666ULL, - 0x22220A2222FFULL, - 0x22220FEEAA00ULL, - 0x222200A8A862ULL, - 0x222200FFFF00ULL, - 0x222200FF0010ULL, - 0x222200BB0010ULL, - 0x222200CC66FFULL, - 0x222200FF8800ULL, - 0x222200DEA584ULL, - 0x2222000000FFULL, - 0x222205666666ULL -}; - -/** - * ANSI-Farbstring mit Hintergrund und Vordergrund erzeugen - */ -char *coloramaprintifarma(const char *ptext, uint64_t col) { - uint8_t bg_r = (col >> 40) & 0xFF; - uint8_t bg_g = (col >> 32) & 0xFF; - uint8_t bg_b = (col >> 24) & 0xFF; - uint8_t fg_r = (col >> 16) & 0xFF; - uint8_t fg_g = (col >> 8) & 0xFF; - uint8_t fg_b = col & 0xFF; - const char *suffix = "\033[0m"; - const char *fmt = "\033[38;2;%u;%u;%um\033[48;2;%u;%u;%um"; - int prefix_len = snprintf(NULL, 0, fmt, - fg_r, fg_g, fg_b, - bg_r, bg_g, bg_b); - if (prefix_len < 0) return NULL; - size_t text_len = strlen(ptext); - size_t suffix_len = strlen(suffix); - size_t total_len = (size_t)prefix_len + text_len + suffix_len + 1; - char *buf = malloc(total_len); - if (!buf) return NULL; - int written = snprintf(buf, total_len, fmt, - fg_r, fg_g, fg_b, - bg_r, bg_g, bg_b); - memcpy(buf + written, ptext, text_len); - memcpy(buf + written + text_len, suffix, suffix_len + 1); - return buf; -} - -/** - * UTF-8 Zeichenlänge (erstes Byte) - */ -static size_t utf8_char_len_local(unsigned char c) { - if (c < 0x80) return 1; - if ((c >> 5) == 0x6) return 2; - if ((c >> 4) == 0xE) return 3; - if ((c >> 3) == 0x1E) return 4; - return 1; -} - -/** - * Füllt Text auf CARD_LEN auf und färbt mit Hintergrund - */ -char *coloramaprintifarmafillup(const char *text, uint64_t advansed_color) { - if (!text) return NULL; - char *res = malloc(CARD_LEN + 1); - if (!res) return NULL; - size_t pos = 0; - size_t i = 0; - while (pos < CARD_LEN && text[i]) { - size_t clen = utf8_char_len_local((unsigned char)text[i]); - if (pos + clen > CARD_LEN) break; - memcpy(res + pos, text + i, clen); - pos += clen; - i += clen; - } - while (pos < CARD_LEN) res[pos++] = ' '; - res[CARD_LEN] = '\0'; - char *colored = coloramaprintifarma(res, advansed_color); - free(res); - return colored; -} - -/** - * Vordergrundfarbe für Text - */ -char *coll(uint64_t col, const char *ptext) { - uint8_t r = (col >> 16) & 0xFF; - uint8_t g = (col >> 8) & 0xFF; - uint8_t b = col & 0xFF; - const char *suffix = "\033[0m"; - const char *fmt = "\033[38;2;%u;%u;%um"; - int prefix_len = snprintf(NULL, 0, fmt, r, g, b); - if (prefix_len < 0) return NULL; - size_t text_len = strlen(ptext); - size_t suffix_len = strlen(suffix); - size_t total_len = (size_t)prefix_len + text_len + suffix_len + 1; - char *buffer = malloc(total_len); - if (!buffer) return NULL; - int written = snprintf(buffer, total_len, fmt, r, g, b); - memcpy(buffer + written, ptext, text_len); - memcpy(buffer + written + text_len, suffix, suffix_len + 1); - return buffer; -} - -/** - * Füllt Text auf max Länge und färbt mit Vordergrundfarbe - */ -char *chngg(const char *pp, uint64_t color, int more_space) { - int mse = (more_space == 1) ? (CARD_LEN * 3) : CARD_LEN; - char *rptr = malloc(mse + 1); - if (!rptr) return NULL; - size_t pos = 0; - size_t i = 0; - while (pos < (size_t)mse && pp[i]) { - size_t clen = utf8_char_len_local((unsigned char)pp[i]); - if (pos + clen > (size_t)mse) break; - memcpy(rptr + pos, pp + i, clen); - pos += clen; - i += clen; - } - while (pos < (size_t)mse) rptr[pos++] = ' '; - rptr[mse] = '\0'; - char *rrr = coll(color, rptr); - free(rptr); /* rptr freigeben, coll liefert neuen Buffer */ - return rrr; -} - -/** - * Wrapper: erzeugt gefülltes, gefärbtes Feld für Typnamen - */ -char *chngg_wrapper(int index, uint64_t unused_size) { - (void)unused_size; - if (index < 0 || index >= NR_OF_SUPPORTET_LANG) index = 0; - return coloramaprintifarmafillup(nameList[index], colorList[index]); -} - -/** - * Mode bits in ls -l Stil konvertieren - */ -void mode_to_str(mode_t m, char out[11]) { - out[0] = S_ISDIR(m) ? 'd' : S_ISLNK(m) ? 'l' : S_ISCHR(m) ? 'c' : - S_ISBLK(m) ? 'b' : S_ISFIFO(m) ? 'p' : S_ISSOCK(m) ? 's' : '-'; - out[1] = (m & S_IRUSR) ? 'r' : '-'; - out[2] = (m & S_IWUSR) ? 'w' : '-'; - out[3] = (m & S_IXUSR) ? 'x' : '-'; - out[4] = (m & S_IRGRP) ? 'r' : '-'; - out[5] = (m & S_IWGRP) ? 'w' : '-'; - out[6] = (m & S_IXGRP) ? 'x' : '-'; - out[7] = (m & S_IROTH) ? 'r' : '-'; - out[8] = (m & S_IWOTH) ? 'w' : '-'; - out[9] = (m & S_IXOTH) ? 'x' : '-'; - out[10] = '\0'; -} - -/** - * Prozessiert Größe in menschenlesbares Format - */ -void process_size(off_t size, char *buf, size_t buflen) { - const char *units[] = {"B","KB","MB","GB","TB"}; - double s = (double)size; - int u = 0; - while (s >= 1024 && u < 4) { s /= 1024; u++; } - snprintf(buf, buflen, "%.1f %s", s, units[u]); -} - -/** - * Sucht Index anhand Dateiendung - */ -int get_type_ind_by_name(const char *type_s) { - if (!type_s) return ERRR; - for (int i = 1; i < NR_OF_SUPPORTET_LANG; ++i) { - if (types[i].name && types[i].name[0] != '\0' && strcmp(types[i].name, type_s) == 0) return i; - } - return ERRR; -} - -/** - * Addiert Dateigröße zum Typ - */ -int add_type_value(int pp_index, size_t filesize) { - if (pp_index < 0 || pp_index >= NR_OF_SUPPORTET_LANG) return -1; - types[pp_index].countet_size += (uint64_t)filesize; - return 0; -} - -/** - * Ermittelt Typ aus Pfad und addiert Größe - */ -void get_type(const char *fullpath, int size, size_t filesize) { - if (!fullpath || size <= 0) return; - int point_index = -1; - for (int i = size - 1; i >= 0; --i) { - if (fullpath[i] == '.') { point_index = i; break; } - if (fullpath[i] == '/') { point_index = -1; break; } - } - if (point_index <= 0) return; - int len = size - point_index - 1; - if (len <= 0) return; - char *type_s = malloc(len + 1); - if (!type_s) return; - memcpy(type_s, fullpath + point_index + 1, len); - type_s[len] = '\0'; - int index = get_type_ind_by_name(type_s); - if (index != ERRR) add_type_value(index, filesize); - free(type_s); -} - -/** - * Rekursive Ordnergrößenberechnung und Typzählung - */ -uint64_t folder_size(const char *path) { - uint64_t total = 0; - DIR *d = opendir(path); - if (!d) return 0; - struct dirent *e; - while ((e = readdir(d)) != NULL) { - if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; - char full[FILE_SYSTEM_BUFF_SIZE]; - if (strlen(path) + strlen(e->d_name) > FILE_SYSTEM_BUFF_SIZE) continue; - snprintf(full, sizeof(full), "%s/%s", path, e->d_name); - struct stat st; - if (lstat(full, &st) != 0) continue; - if (S_ISDIR(st.st_mode)) total += folder_size(full); - else { - total += (uint64_t)st.st_size; - get_type(full, (int)strlen(full), (size_t)st.st_size); - } - } - closedir(d); - return total; -} - -/** - * Ermittelt dominanten Sprachindex basierend auf gesammelten Größen - */ -int get_dom_lang_index(void) { - uint64_t local_sums[NR_OF_SUPPORTET_LANG]; - for (int i = 0; i < NR_OF_SUPPORTET_LANG; ++i) local_sums[i] = 0; - for (int i = 1; i < NR_OF_SUPPORTET_LANG; ++i) { - int g = types[i].globalsumindex; - if (g >= 0 && g < NR_OF_SUPPORTET_LANG) local_sums[g] += types[i].countet_size; - } - int max_idx = 0; - uint64_t max_val = 0; - for (int i = 0; i < NR_OF_SUPPORTET_LANG; ++i) { - if (local_sums[i] > max_val) { max_val = local_sums[i]; max_idx = i; } - } - for (int i = 0; i < NR_OF_SUPPORTET_LANG; ++i) types[i].countet_size = 0; - return max_idx; -} - -/** - * Ermittelt Index für einzelne Datei anhand Extension - */ -int get_dom_single_file_index_from_path(const char *path) { - if (!path) return 0; - const char *dot = strrchr(path, '.'); - const char *ext = dot ? dot + 1 : ""; - for (int i = 0; i < NR_OF_SUPPORTET_LANG; ++i) { - if (types[i].name && types[i].name[0] != '\0' && strcmp(types[i].name, ext) == 0) return i; - } - return 0; -} - -/** - * Schreibt content in Zwischenablage via xclip - */ -void clipper(const char *content) { - if (!content) return; - FILE *p = popen("xclip -selection clipboard", "w"); - if (!p) return; - fwrite(content, 1, strlen(content), p); - pclose(p); -} - -/** - * Erzeugt absoluten Pfad aus aktuellem Arbeitsverzeichnis und name - */ -void getAbsolutPath(char *name, int nameLen) { - if (name == NULL) { - printf("No name passed\n"); - } - char path[FILE_SYSTEM_BUFF_SIZE]; - if (getcwd(path, sizeof(path)) != NULL) { - int pathLen = (int)strlen(path); - int len = pathLen + nameLen + 2; - char *absolutPath = malloc(len + 1); - if (!absolutPath) { perror("malloc"); return; } - absolutPath[len] = '\0'; - int k = 0; - for (int i = 0; i < len; ++i) { - if (i < pathLen) { - absolutPath[i] = path[k]; - ++k; - } - if (i > pathLen) { - absolutPath[i] = name[k]; - ++k; - } - if (i == pathLen) { - absolutPath[i] = '/'; - k = 0; - } - } - printf("%s\n", absolutPath); - clipper(absolutPath); - free(absolutPath); - } else { - perror("ERROR: getcwd"); - } -} - -/** - * Vertauscht zwei TTAS-Elemente - */ -void swapTTAS(TTAS *a, TTAS *b) { - TTAS temp = *a; - *a = *b; - *b = temp; -} - -/** - * Kehrt ein Array von TTAS um - */ -void reverseArray(TTAS *arr, int n) { - int left = 0; - int right = n - 1; - while (left < right) { - swapTTAS(&arr[left], &arr[right]); - left++; - right--; - } -} - -/** - * Sortiert nach Timestamp (neueste zuerst), nutzt Auswahl-Sort und reversiert Ergebnis - */ -void sortByTimestamp(time_t *unixTimestamps, char **rows, int n) { - if (unixTimestamps == NULL || rows == NULL || n <= 1) - return; - for (int i = 0; i < n - 1; ++i) { - int maxIndex = i; - for (int j = i + 1; j < n; ++j) { - if (unixTimestamps[j] > unixTimestamps[maxIndex]) { - maxIndex = j; - } - } - if (maxIndex != i) { - time_t tempTime = unixTimestamps[i]; - unixTimestamps[i] = unixTimestamps[maxIndex]; - unixTimestamps[maxIndex] = tempTime; - char *tempRow = rows[i]; - rows[i] = rows[maxIndex]; - rows[maxIndex] = tempRow; - } - } - TTAS *array = malloc(n * sizeof(TTAS)); - if (array == NULL) { - perror("malloc TTAS array"); - return; - } - for (int i = 0; i < n; ++i) { - array[i].unixtimestamp = unixTimestamps[i]; - array[i].row = rows[i]; - } - reverseArray(array, n); - for (int i = 0; i < n; ++i) { - unixTimestamps[i] = array[i].unixtimestamp; - rows[i] = array[i].row; - } - free(array); -} - -/** - * Sortiert Einträge nach Größe (absteigend) - */ -void sortBySize(uint64_t *sizes, char **rows, int n) { - if (sizes == NULL || rows == NULL || n <= 1) return; - for (int i = 0; i < n - 1; ++i) { - int maxIndex = i; - for (int j = i + 1; j < n; ++j) { - if (sizes[j] < sizes[maxIndex]) maxIndex = j; - } - if (maxIndex != i) { - uint64_t tmp = sizes[i]; - sizes[i] = sizes[maxIndex]; - sizes[maxIndex] = tmp; - char *trow = rows[i]; - rows[i] = rows[maxIndex]; - rows[maxIndex] = trow; - } - } -} - -/** - * Ermittelt dominanten Sprachindex und liefert Index und Summe (Wrapper) - */ -void getDomLangIndex(int *out_index, uintptr_t *out_size) { - int idx = get_dom_lang_index(); - *out_index = idx; - *out_size = 0; -} - -void print_absolut_paths(const char *path_to_process) { - DIR *d = opendir(path_to_process); - if (!d) { - perror("opendir"); - return; - } - struct dirent *e; - while ((e = readdir(d)) != NULL) { - if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; - char name[PATH_MAX]; - if (snprintf(name, sizeof(name), "%s", e->d_name) >= (int)sizeof(name)) continue; - char path[FILE_SYSTEM_BUFF_SIZE]; - char respath[PATH_MAX + FILE_SYSTEM_BUFF_SIZE + 2] = { 0 }; - if (path_to_process[0] == '.') { - if (!(getcwd(path, sizeof(path)) != NULL)) { - perror("ERROR: getcwd"); - } else { - snprintf(respath, sizeof(respath), "%s/%s\n", path, name); - } - } else { - snprintf(respath, sizeof(respath), "%s/%s\n", path_to_process, name); - } - printf("%s", respath); - } -} - -/** - * Hauptfunktion: listet Ordner ähnlich lll/main_lll, unterstützt Sortierung nach Zeit (default) oder Größe (-s) - */ -int main_lll_impl(int sort_by_size, const char *path_to_process) { - DIR *d = opendir(path_to_process); - if (!d) { - perror("opendir"); - return 1; - } - struct dirent *e; - char **rows = NULL; - uint64_t *sizes = NULL; - time_t *unixTimestamps = NULL; - size_t count = 0, cap = 0; - while ((e = readdir(d)) != NULL) { - if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; - char full[PATH_MAX]; - if (snprintf(full, sizeof(full), "%s/%s", path_to_process, e->d_name) >= (int)sizeof(full)) continue; - struct stat st; - if (lstat(full, &st) == -1) continue; - char perms[11]; - mode_to_str(st.st_mode, perms); - nlink_t links = st.st_nlink; - struct passwd *pw = getpwuid(st.st_uid); - struct group *gr = getgrgid(st.st_gid); - const char *owner = pw ? pw->pw_name : "unknown"; - const char *group = gr ? gr->gr_name : "unknown"; - char sizebuf[32]; - char timestr[64]; - struct tm tm; - localtime_r(&st.st_mtime, &tm); - strftime(timestr, sizeof(timestr), "%e. %b %H:%M", &tm); - time_t timestamp = st.st_mtime; - uint64_t raw_size = 0; - if (S_ISDIR(st.st_mode)) { - raw_size = folder_size(full); - process_size((off_t)raw_size, sizebuf, sizeof(sizebuf)); - get_type(full, (int)strlen(full), (size_t)raw_size); - } else { - raw_size = (uint64_t)st.st_size; - process_size((off_t)st.st_size, sizebuf, sizeof(sizebuf)); - get_type(full, (int)strlen(full), (size_t)st.st_size); - } - int ja_index = 0; - uintptr_t ja_size = 0; - getDomLangIndex(&ja_index, &ja_size); - char *line = NULL; - if (S_ISLNK(st.st_mode)) { - char target[PATH_MAX]; - ssize_t len = readlink(full, target, sizeof(target) - 1); - if (len != -1) { - target[len] = '\0'; - size_t needed = strlen(perms) + 128 + strlen(owner) + strlen(group) + strlen(sizebuf) + strlen(timestr) + strlen(e->d_name) + strlen(target) + 64; - line = malloc(needed); - if (!line) { closedir(d); return 1; } - snprintf(line, needed, "%s %3lu %s %s %8s %s %s -> %s", - perms, (unsigned long)links, owner, group, sizebuf, timestr, e->d_name, target); - } else { - size_t needed = strlen(perms) + 128 + strlen(owner) + strlen(group) + strlen(sizebuf) + strlen(timestr) + strlen(e->d_name) + 32; - line = malloc(needed); - if (!line) { closedir(d); return 1; } - snprintf(line, needed, "%s %3lu %s %s %8s %s %s", - perms, (unsigned long)links, owner, group, sizebuf, timestr, e->d_name); - } - } else { - char *owner_col = chngg(owner, 0x44FF44, 0); - char *group_col = chngg(group, 0x88FF88, 0); - char *size_col = chngg(sizebuf, 0xFF0000, 0); - char *time_col = chngg(timestr, 0x888866, 0); - char *type_fld = chngg_wrapper(ja_index, ja_size); - char *name_col = chngg(e->d_name, S_ISDIR(st.st_mode) ? 0x9999FF : 0xFF33FF, 1); - size_t needed = strlen(perms) + 128 + strlen(owner_col) + strlen(group_col) + strlen(size_col) + strlen(time_col) + strlen(type_fld) + strlen(name_col) + 64; - line = malloc(needed); - if (!line) { closedir(d); return 1; } - snprintf(line, needed, "%s %3lu %s %s %8s %s %s %s", - perms, (unsigned long)links, - owner_col, group_col, size_col, time_col, type_fld, name_col); - free(owner_col); free(group_col); free(size_col); free(time_col); free(type_fld); free(name_col); - } - if (count + 1 > cap) { - size_t ncap = cap ? cap * 2 : 64; - char **tmp = realloc(rows, ncap * sizeof(char*)); - uint64_t *tmp2 = realloc(sizes, ncap * sizeof(uint64_t)); - time_t *tmp3 = realloc(unixTimestamps, ncap * sizeof(time_t)); - if (!tmp || !tmp2 || !tmp3) { perror("realloc"); free(line); break; } - rows = tmp; sizes = tmp2; unixTimestamps = tmp3; - cap = ncap; - } - rows[count] = line; - sizes[count] = raw_size; - unixTimestamps[count] = timestamp; - count++; - } - closedir(d); - if (count > 0) { - if (sort_by_size) { - sortBySize(sizes, rows, (int)count); - } else { - sortByTimestamp(unixTimestamps, rows, (int)count); - } - } - for (size_t i = 0; i < count; ++i) { - printf("%s\n", rows[i]); - free(rows[i]); - } - free(rows); - free(sizes); - free(unixTimestamps); - return 0; -} - -/** - * ls-ähnliche Kurzliste: zeigt Typ-Feld und gekürzten Namen in Spalten - */ -int main_ls_impl(void) { - DIR *d = opendir("."); - if (!d) { perror("opendir"); return 1; } - struct dirent *e; - char **entries = NULL; - size_t count = 0, cap = 0; - while ((e = readdir(d)) != NULL) { - if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; - char full[PATH_MAX]; - if (snprintf(full, sizeof(full), "./%s", e->d_name) >= (int)sizeof(full)) continue; - struct stat st; - if (lstat(full, &st) != 0) continue; - if (S_ISDIR(st.st_mode)) folder_size(full); - nlink_t links = st.st_nlink; - int lang_idx = S_ISDIR(st.st_mode) ? get_dom_lang_index() : get_dom_single_file_index_from_path(full); - uint64_t name_color = S_ISDIR(st.st_mode) ? 0x9999FF : 0xFF33FF; - char *type_fld = chngg_wrapper(lang_idx, 0); - char *project_name = chngg(e->d_name, name_color, 1); - char gek[46]; memset(gek,0,sizeof(gek)); strncpy(gek, project_name, 45); gek[45]='\0'; - size_t needed = 5 + 1 + strlen(type_fld) + 1 + strlen(gek) + 32; - char *line = malloc(needed); - if (!line) { free(type_fld); free(project_name); break; } - snprintf(line, needed, "%3lu %8s %s", (unsigned long)links, type_fld, gek); - free(type_fld); free(project_name); - if (count + 1 > cap) { - size_t ncap = cap ? cap * 2 : 64; - char **tmp = realloc(entries, ncap * sizeof(char*)); - if (!tmp) { free(line); break; } - entries = tmp; cap = ncap; - } - entries[count++] = line; - } - closedir(d); - char *reset = coloramaprintifarma("", 0x000000000000ULL); - if (reset) free(reset); - for (size_t i = 0; i < count; i += 4) { - size_t end = i + 4; if (end > count) end = count; - size_t total_len = 0; - for (size_t j = i; j < end; ++j) total_len += strlen(entries[j]) + 1; - char *line = malloc(total_len + 1); - if (!line) continue; - line[0] = '\0'; - for (size_t j = i; j < end; ++j) { - if (j > i) strcat(line, " "); - strcat(line, entries[j]); - } - printf("%s\n", line); - free(line); - } - for (size_t i = 0; i < count; ++i) free(entries[i]); - free(entries); - return 0; -} - -/** - * Programmstart und CLI-Argumente Dispatcher - */ -int main(int argc, char **argv) { - if (argc == 1) { - if (main_lll_impl(0, ".") > 0) goto label_exit_error; - goto label_exit_end; - } - if (argc == 2 || argc == 3) { - if ((strcmp(argv[1], "-s") == 0) || (strcmp(argv[1], "--size") == 0)) goto label_size; - if ((strcmp(argv[1], "-l") == 0) || (strcmp(argv[1], "--ls") == 0)) goto label_ls; - if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) goto label_help; - if ((strcmp(argv[1], "-p") == 0) || (strcmp(argv[1], "--path") == 0)) goto label_path; - if ((strcmp(argv[1], "-q") == 0) || (strcmp(argv[1], "--qqqq") == 0)) goto label_list_absolut; - goto label_absolut_path; - } - label_size: - if (main_lll_impl(1, ".") > 0) goto label_exit_error; - goto label_exit_end; - label_ls: - main_ls_impl(); - goto label_exit_end; - label_path: - if (argc != 3) goto label_exit_error; - if (main_lll_impl(0, argv[2]) > 0) goto label_exit_error; - goto label_exit_end; - label_help: - printf(LLL_HELP_MESSAGE_JANUAR_X); - printf("MAX_PATH : %d\n", PATH_MAX); - goto label_exit_end; - label_absolut_path: - getAbsolutPath(argv[1], strlen(argv[1])); - goto label_exit_end; - label_list_absolut: - if (argc == 2) { - print_absolut_paths("."); - } else { - print_absolut_paths(argv[2]); - } - goto label_exit_end; - label_exit_error: - fprintf(stderr, "Unbekannter Parameter oder falsche Nutzung\n"); - return 1; - label_exit_end: - return 0; -} - diff --git a/lll.rs b/lll.rs new file mode 100755 index 0000000..f9a9f05 --- /dev/null +++ b/lll.rs @@ -0,0 +1,742 @@ +use std::env; +use std::fs; +use std::io::Write; +use std::os::unix::fs::MetadataExt; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::mem; +use std::fs::Metadata; +use std::time::UNIX_EPOCH; + +const FILE_SYSTEM_MAX_PATH: usize = 0xfff; +const NR_OF_SUPPORTET_LANG: usize = 13; +const CARD_LEN: usize = 12; +const ERRR: i32 = 0x0FFF_AFFF_u32 as i32; + +const LLL_CLI_HELP_MESSAGE: &str = "\ +rustc -O -C panic=abort lll.rs -o lll +-s --size sort by size +-l --ls ls like output +-h --help print help +-p --path set path default is current +-q --qqqq print all absolut paths of a folder +@VERSION 10.0.0 +"; + +#[repr(C)] +struct Passwd { + pw_name: *mut LllLibcChar, + pw_passwd: *mut LllLibcChar, + pw_uid: u32, + pw_gid: u32, + pw_gecos: *mut LllLibcChar, + pw_dir: *mut LllLibcChar, + pw_shell: *mut LllLibcChar, +} + +#[repr(C)] +struct Group { + gr_name: *mut LllLibcChar, + gr_passwd: *mut LllLibcChar, + gr_gid: u32, + gr_mem: *mut *mut LllLibcChar, +} + +type LllLibcChar = i8; + +extern "C" { + fn getpwuid(uid: u32) -> *mut Passwd; + fn getgrgid(gid: u32) -> *mut Group; +} + +#[derive(Clone)] +struct TypeEntry { + name: &'static str, + countet_size: u64, + globalsumindex: i32, +} + +static mut TYPES: [TypeEntry; NR_OF_SUPPORTET_LANG] = [ + TypeEntry { name: "", countet_size: 0, globalsumindex: 0 }, + TypeEntry { name: "c", countet_size: 0, globalsumindex: 1 }, + TypeEntry { name: "cpp", countet_size: 0, globalsumindex: 2 }, + TypeEntry { name: "ino", countet_size: 0, globalsumindex: 3 }, + TypeEntry { name: "py", countet_size: 0, globalsumindex: 4 }, + TypeEntry { name: "java", countet_size: 0, globalsumindex: 5 }, + TypeEntry { name: "sh", countet_size: 0, globalsumindex: 6 }, + TypeEntry { name: "js", countet_size: 0, globalsumindex: 7 }, + TypeEntry { name: "html", countet_size: 0, globalsumindex: 8 }, + TypeEntry { name: "asm", countet_size: 0, globalsumindex: 9 }, + TypeEntry { name: "rs", countet_size: 0, globalsumindex: 10 }, + TypeEntry { name: "sql", countet_size: 0, globalsumindex: 11 }, + TypeEntry { name: "h", countet_size: 0, globalsumindex: 1 }, +]; + +static NAME_LIST: [&str; NR_OF_SUPPORTET_LANG] = [ + "Undefined", "C", "C++", "ESP", "Python", "Java", "ShellScript", "JavaScript", "HTML", + "Assembly", "Rust", "SQL", "C", +]; + +static COLOR_LIST: [u64; NR_OF_SUPPORTET_LANG] = [ + 0x2222_0778_77CC, + 0x2222_0566_6666, + 0x2222_0A22_22FF, + 0x2222_0FEE_AA00, + 0x2222_00A8_A862, + 0x2222_00FF_FF00, + 0x2222_00FF_0010, + 0x2222_00BB_0010, + 0x2222_0088_8888, + 0x2222_00FF_8800, + 0x2222_00DE_A584, + 0x2222_0000_00FF, + 0x2222_0566_6666, +]; + +fn coloramaprintifarma(ptext: &str, col: u64) -> String { + let bg_r = ((col >> 40) & 0xFF) as u8; + let bg_g = ((col >> 32) & 0xFF) as u8; + let bg_b = ((col >> 24) & 0xFF) as u8; + let fg_r = ((col >> 16) & 0xFF) as u8; + let fg_g = ((col >> 8) & 0xFF) as u8; + let fg_b = (col & 0xFF) as u8; + format!( + "\x1b[38;2;{};{};{}m\x1b[48;2;{};{};{}m{}\x1b[0m", + fg_r, fg_g, fg_b, bg_r, bg_g, bg_b, ptext + ) +} + +fn utf8_char_len_local(c: u8) -> usize { + if c < 0x80 { + 1 + } else if (c >> 5) == 0x6 { + 2 + } else if (c >> 4) == 0xE { + 3 + } else if (c >> 3) == 0x1E { + 4 + } else { + 1 + } +} + +fn pad_utf8(text: &str, mse: usize) -> String { + let bytes = text.as_bytes(); + let mut res = Vec::with_capacity(mse + 4); + let mut pos = 0usize; + let mut i = 0usize; + while pos < mse && i < bytes.len() { + let clen = utf8_char_len_local(bytes[i]); + if pos + clen > mse { + break; + } + if i + clen > bytes.len() { + break; + } + res.extend_from_slice(&bytes[i..i + clen]); + pos += clen; + i += clen; + } + while pos < mse { + res.push(b' '); + pos += 1; + } + String::from_utf8_lossy(&res).into_owned() +} + +fn coloramaprintifarmafillup(text: &str, advansed_color: u64) -> String { + let padded = pad_utf8(text, CARD_LEN); + coloramaprintifarma(&padded, advansed_color) +} + +fn coll(col: u64, ptext: &str) -> String { + let r = ((col >> 16) & 0xFF) as u8; + let g = ((col >> 8) & 0xFF) as u8; + let b = (col & 0xFF) as u8; + format!("\x1b[38;2;{};{};{}m{}\x1b[0m", r, g, b, ptext) +} + +fn chngg(pp: &str, color: u64, more_space: i32) -> String { + let mse = match more_space { + 1 => CARD_LEN * 3, + 2 => CARD_LEN + 3, + 3 => CARD_LEN.saturating_sub(3), + _ => CARD_LEN, + }; + let padded = pad_utf8(pp, mse); + coll(color, &padded) +} + +fn chngg_wrapper(index: i32) -> String { + let idx = if index < 0 || index as usize >= NR_OF_SUPPORTET_LANG { + 0 + } else { + index as usize + }; + coloramaprintifarmafillup(NAME_LIST[idx], COLOR_LIST[idx]) +} + +fn mode_to_str(mode: u32) -> String { + let mut out = String::with_capacity(10); + let ft = mode & 0o170000; + out.push(match ft { + 0o040000 => 'd', + 0o120000 => 'l', + 0o020000 => 'c', + 0o060000 => 'b', + 0o010000 => 'p', + 0o140000 => 's', + _ => '-', + }); + out.push(if mode & 0o400 != 0 { 'r' } else { '-' }); + out.push(if mode & 0o200 != 0 { 'w' } else { '-' }); + out.push(if mode & 0o100 != 0 { 'x' } else { '-' }); + out.push(if mode & 0o040 != 0 { 'r' } else { '-' }); + out.push(if mode & 0o020 != 0 { 'w' } else { '-' }); + out.push(if mode & 0o010 != 0 { 'x' } else { '-' }); + out.push(if mode & 0o004 != 0 { 'r' } else { '-' }); + out.push(if mode & 0o002 != 0 { 'w' } else { '-' }); + out.push(if mode & 0o001 != 0 { 'x' } else { '-' }); + out +} + +fn process_size(size: u64) -> String { + let units = ["B", "KB", "MB", "GB", "TB"]; + let mut s = size as f64; + let mut u = 0usize; + while s >= 1024.0 && u < 4 { + s /= 1024.0; + u += 1; + } + format!("{:.1} {}", s, units[u]) +} + +fn get_type_ind_by_name(type_s: &str) -> i32 { + unsafe { + for i in 1..NR_OF_SUPPORTET_LANG { + if !TYPES[i].name.is_empty() && TYPES[i].name == type_s { + return i as i32; + } + } + } + ERRR +} + +fn add_type_value(pp_index: i32, filesize: u64) -> i32 { + if pp_index < 0 || pp_index as usize >= NR_OF_SUPPORTET_LANG { + return -1; + } + unsafe { + TYPES[pp_index as usize].countet_size = + TYPES[pp_index as usize].countet_size.saturating_add(filesize); + } + 0 +} + +fn get_type(fullpath: &str, filesize: u64) { + let bytes = fullpath.as_bytes(); + let size = bytes.len() as i32; + if size <= 0 { + return; + } + let mut point_index: i32 = -1; + let mut i = size - 1; + loop { + if bytes[i as usize] == b'.' { + point_index = i; + break; + } + if bytes[i as usize] == b'/' { + point_index = -1; + break; + } + if i == 0 { + break; + } + i -= 1; + } + if point_index <= 0 { + return; + } + let start = (point_index + 1) as usize; + if start >= bytes.len() { + return; + } + let type_s = match std::str::from_utf8(&bytes[start..]) { + Ok(s) => s, + Err(_) => return, + }; + let index = get_type_ind_by_name(type_s); + if index != ERRR { + add_type_value(index, filesize); + } +} + +fn is_mount_point(path: &Path) -> bool { + let st_path = match fs::metadata(path) { + Ok(m) => m, + Err(_) => return false, + }; + let parent = path.join(".."); + let st_parent = match fs::metadata(&parent) { + Ok(m) => m, + Err(_) => return false, + }; + st_path.dev() != st_parent.dev() +} + +fn store_modification_timestamp(metadata: &Metadata, timestamp: &mut i64) { + if let Ok(modified) = metadata.modified() { + if let Ok(duration) = modified.duration_since(UNIX_EPOCH) { + if *timestamp < duration.as_secs() as i64 { + *timestamp = duration.as_secs() as i64; + } + } + } +} + +fn folder_size(path: &Path, ts_max: &mut i64) -> u64 { + if is_mount_point(path) { + return 0; + } + let mut total: u64 = 0; + let rd = match fs::read_dir(path) { + Ok(d) => d, + Err(_) => return 0, + }; + for entry in rd.flatten() { + let name = entry.file_name(); + if name == "." || name == ".." { + continue; + } + let full = entry.path(); + let st = match fs::symlink_metadata(&full) { + Ok(m) => m, + Err(_) => continue, + }; + if st.file_type().is_dir() { + total = total.saturating_add(folder_size(&full, ts_max)); + } else if st.file_type().is_symlink() { + continue; + } else { + store_modification_timestamp(&st, ts_max); + let sz = st.len(); + total = total.saturating_add(sz); + if let Some(s) = full.to_str() { + get_type(s, sz); + } + } + } + total +} + +fn get_dom_lang_index() -> i32 { + let mut local_sums = [0u64; NR_OF_SUPPORTET_LANG]; + unsafe { + for i in 1..NR_OF_SUPPORTET_LANG { + let g = TYPES[i].globalsumindex; + if g >= 0 && (g as usize) < NR_OF_SUPPORTET_LANG { + local_sums[g as usize] = local_sums[g as usize].saturating_add(TYPES[i].countet_size); + } + } + } + let mut max_idx = 0i32; + let mut max_val = 0u64; + for i in 0..NR_OF_SUPPORTET_LANG { + if local_sums[i] > max_val { + max_val = local_sums[i]; + max_idx = i as i32; + } + } + unsafe { + for i in 0..NR_OF_SUPPORTET_LANG { + TYPES[i].countet_size = 0; + } + } + max_idx +} + +fn get_dom_single_file_index_from_path(path: &str) -> i32 { + let ext = Path::new(path) + .extension() + .and_then(|e| e.to_str()) + .unwrap_or(""); + unsafe { + for i in 0..NR_OF_SUPPORTET_LANG { + if !TYPES[i].name.is_empty() && TYPES[i].name == ext { + return i as i32; + } + } + } + 0 +} + +fn clipper(content: &str) { + let mut child = match Command::new("xclip") + .arg("-selection") + .arg("clipboard") + .stdin(Stdio::piped()) + .spawn() + { + Ok(c) => c, + Err(_) => return, + }; + if let Some(mut stdin) = child.stdin.take() { + let _ = stdin.write_all(content.as_bytes()); + } + let _ = child.wait(); +} + +fn uid_name(uid: u32) -> String { + unsafe { + let pw = getpwuid(uid); + if pw.is_null() { + return "unknown".to_string(); + } + let name = (*pw).pw_name; + if name.is_null() { + return "unknown".to_string(); + } + std::ffi::CStr::from_ptr(name) + .to_string_lossy() + .into_owned() + } +} + +fn gid_name(gid: u32) -> String { + unsafe { + let gr = getgrgid(gid); + if gr.is_null() { + return "unknown".to_string(); + } + let name = (*gr).gr_name; + if name.is_null() { + return "unknown".to_string(); + } + std::ffi::CStr::from_ptr(name) + .to_string_lossy() + .into_owned() + } +} + +fn format_mtime(secs: i64) -> String { + extern "C" { + fn localtime_r(timep: *const i64, result: *mut LibcTm) -> *mut LibcTm; + fn strftime( + s: *mut i8, + max: usize, + format: *const i8, + tm: *const LibcTm, + ) -> usize; + } + #[repr(C)] + struct LibcTm { + tm_sec: i32, + tm_min: i32, + tm_hour: i32, + tm_mday: i32, + tm_mon: i32, + tm_year: i32, + tm_wday: i32, + tm_yday: i32, + tm_isdst: i32, + tm_gmtoff: i64, + tm_zone: *const i8, + } + unsafe { + let mut tm: LibcTm = mem::zeroed(); + let t = secs; + if localtime_r(&t, &mut tm).is_null() { + return String::from("?"); + } + let mut buf = [0i8; 64]; + let fmt = b"%e. %b %H:%M\0"; + let n = strftime( + buf.as_mut_ptr(), + buf.len(), + fmt.as_ptr() as *const i8, + &tm, + ); + if n == 0 { + return String::from("?"); + } + std::ffi::CStr::from_ptr(buf.as_ptr()) + .to_string_lossy() + .trim() + .to_string() + } +} + +fn get_absolut_path(name: &str) { + let cwd = match env::current_dir() { + Ok(p) => p, + Err(e) => { + eprintln!("ERROR: getcwd: {}", e); + return; + } + }; + let mut absolut = cwd; + absolut.push(name); + let s = absolut.to_string_lossy().into_owned(); + println!("{}", s); + clipper(&s); +} + +fn sort_by_timestamp(unix_timestamps: &mut [i64], rows: &mut [String]) { + let n = unix_timestamps.len(); + if n <= 1 { + return; + } + for i in 0..n - 1 { + let mut max_index = i; + for j in i + 1..n { + if unix_timestamps[j] > unix_timestamps[max_index] { + max_index = j; + } + } + if max_index != i { + unix_timestamps.swap(i, max_index); + rows.swap(i, max_index); + } + } + rows.reverse(); + unix_timestamps.reverse(); +} + +fn sort_by_size(sizes: &mut [u64], rows: &mut [String]) { + let n = sizes.len(); + if n <= 1 { + return; + } + for i in 0..n - 1 { + let mut max_index = i; + for j in i + 1..n { + if sizes[j] < sizes[max_index] { + max_index = j; + } + } + if max_index != i { + sizes.swap(i, max_index); + rows.swap(i, max_index); + } + } +} + +fn print_absolut_paths(path_to_process: &str) { + let rd = match fs::read_dir(path_to_process) { + Ok(d) => d, + Err(e) => { + eprintln!("opendir: {}", e); + return; + } + }; + let is_dot = path_to_process.starts_with('.'); + let cwd = env::current_dir().ok(); + for entry in rd.flatten() { + let name = entry.file_name(); + if name == "." || name == ".." { + continue; + } + let name_s = name.to_string_lossy(); + if is_dot { + if let Some(ref p) = cwd { + println!("{}/{}", p.display(), name_s); + } + } else { + println!("{}/{}", path_to_process.trim_end_matches('/'), name_s); + } + } +} + +fn main_lll_impl(sort_by_size_flag: bool, path_to_process: &str) -> i32 { + let rd = match fs::read_dir(path_to_process) { + Ok(d) => d, + Err(e) => { + eprintln!("opendir: {}", e); + return 1; + } + }; + let mut rows: Vec = Vec::new(); + let mut sizes: Vec = Vec::new(); + let mut unix_timestamps: Vec = Vec::new(); + for entry in rd.flatten() { + let name_os = entry.file_name(); + if name_os == "." || name_os == ".." { + continue; + } + let name = name_os.to_string_lossy(); + let full: PathBuf = Path::new(path_to_process).join(&*name); + let st = match fs::symlink_metadata(&full) { + Ok(m) => m, + Err(_) => continue, + }; + let mode = st.mode(); + let perms = mode_to_str(mode); + let links = st.nlink(); + let owner = uid_name(st.uid()); + let group = gid_name(st.gid()); + let ft = st.file_type(); + let raw_size: u64; + let sizebuf: String; + let mut timestamp: i64 = 0; + if ft.is_dir() { + raw_size = folder_size(&full, &mut timestamp); + sizebuf = process_size(raw_size); + if let Some(s) = full.to_str() { + get_type(s, raw_size); + } + } else { + store_modification_timestamp(&st, &mut timestamp); + raw_size = st.len(); + sizebuf = process_size(raw_size); + if let Some(s) = full.to_str() { + get_type(s, raw_size); + } + } + let timestr = format_mtime(timestamp); + let ja_index = get_dom_lang_index(); + let line = if ft.is_symlink() { + match fs::read_link(&full) { + Ok(target) => { + let t = target.to_string_lossy(); + format!( + "{} {:3} {} {} {:>8} {} {} -> {}", + perms, links, owner, group, sizebuf, timestr, name, t + ) + } + Err(_) => format!( + "{} {:3} {} {} {:>8} {} {}", + perms, links, owner, group, sizebuf, timestr, name + ), + } + } else { + let owner_col = chngg(&owner, 0x44FF44, 0); + let group_col = chngg(&group, 0x88FF88, 0); + let size_col = chngg(&sizebuf, 0xFF0000, 3); + let time_col = chngg(×tr, 0x888866, 2); + let type_fld = chngg_wrapper(ja_index); + let name_col = chngg( + &name, + if ft.is_dir() { 0x9999FF } else { 0xFF33FF }, + 1, + ); + format!( + "{} {:3} {} {} {:>8} {} {} {}", + perms, links, owner_col, group_col, size_col, time_col, type_fld, name_col + ) + }; + rows.push(line); + sizes.push(raw_size); + unix_timestamps.push(timestamp); + } + if !rows.is_empty() { + if sort_by_size_flag { + sort_by_size(&mut sizes, &mut rows); + } else { + sort_by_timestamp(&mut unix_timestamps, &mut rows); + } + } + for row in &rows { + println!("{}", row); + } + 0 +} + +fn main_ls_impl() -> i32 { + let rd = match fs::read_dir(".") { + Ok(d) => d, + Err(e) => { + eprintln!("opendir: {}", e); + return 1; + } + }; + let mut entries: Vec = Vec::new(); + for entry in rd.flatten() { + let name_os = entry.file_name(); + if name_os == "." || name_os == ".." { + continue; + } + let name = name_os.to_string_lossy(); + let full = PathBuf::from(format!("./{}", name)); + let st = match fs::symlink_metadata(&full) { + Ok(m) => m, + Err(_) => continue, + }; + let ft = st.file_type(); + let mut max_date_ts: i64 = 0; + if ft.is_dir() { + let _ = folder_size(&full, &mut max_date_ts); + } + let links = st.nlink(); + let lang_idx = if ft.is_dir() { + get_dom_lang_index() + } else { + get_dom_single_file_index_from_path(full.to_str().unwrap_or("")) + }; + let name_color: u64 = if ft.is_dir() { 0x9999FF } else { 0xFF33FF }; + let type_fld = chngg_wrapper(lang_idx); + let project_name = chngg(&name, name_color, 1); + let _ = { + let b = project_name.as_bytes(); + let take = b.len().min(45); + String::from_utf8_lossy(&b[..take]).into_owned() + }; + let line = format!("{:5} {:>8} {:>46}", links, type_fld, project_name); + entries.push(line); + } + let mut i = 0usize; + while i < entries.len() { + let end = (i + 4).min(entries.len()); + let mut line = String::new(); + for j in i..end { + if j > i { + line.push(' '); + } + line.push_str(&entries[j]); + } + println!("{}", line); + i = end; + } + 0 +} + +fn main() { + let args: Vec = env::args().collect(); + let argc = args.len(); + let exit_code = if argc == 1 { + main_lll_impl(false, ".") + } else if argc == 2 || argc == 3 { + let a1 = args[1].as_str(); + if a1 == "-s" || a1 == "--size" { + main_lll_impl(true, ".") + } else if a1 == "-l" || a1 == "--ls" { + main_ls_impl() + } else if a1 == "-h" || a1 == "--help" { + print!("{}", LLL_CLI_HELP_MESSAGE); + println!("FILE_SYSTEM_MAX_PATH : {}", FILE_SYSTEM_MAX_PATH); + 0 + } else if a1 == "-p" || a1 == "--path" { + if argc != 3 { + eprintln!("Unbekannter Parameter oder falsche Nutzung"); + 1 + } else { + main_lll_impl(false, &args[2]) + } + } else if a1 == "-q" || a1 == "--qqqq" { + if argc == 2 { + print_absolut_paths("."); + } else { + print_absolut_paths(&args[2]); + } + 0 + } else { + get_absolut_path(&args[1]); + 0 + } + } else { + eprintln!("Unbekannter Parameter oder falsche Nutzung"); + 1 + }; + std::process::exit(exit_code); +} +