/** * 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; }