]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
userland: enhance 17 utilities and ulibc with POSIX features
authorTulio A M Mendes <[email protected]>
Mon, 27 Apr 2026 02:34:15 +0000 (23:34 -0300)
committerTulio A M Mendes <[email protected]>
Mon, 4 May 2026 23:52:02 +0000 (20:52 -0300)
ulibc:
- Add sys/statvfs.h and statvfs() implementation
- Extend time.h: strftime, localtime, gmtime, ctime, asctime, difftime
- Fix time_t conflict (was uint32_t in time.h, int32_t in sys/types.h)
- Add unistd.h include to statvfs.c for close()

High-priority commands:
- awk: BEGIN/END blocks, NF/NR vars, print with OFS, -v var=val, /bin/bash
- find: -mtime, -size, -perm, -user, -maxdepth, -delete, -exec
- chmod: symbolic mode support (u+x, go-w, a=rw)
- grep: BRE/ERE regex, -i, -r, -l, -q, -n flags
- sed: d/p/q/a/i/c/y commands, line/regex addresses, -n mode
- ps: BSD-style output with UID/PID/PPID/STAT/TIME/CMD
- top: system summary header, process list with -n iterations

Medium-priority commands:
- sh: command substitution $(...), tilde expansion ~, $$/$! vars,
  variable overflow protection
- cp/mv: preserve source file permissions via fstat
- rm: proper -rf recursive directory removal with getdents
- kill: -l flag to list signal names
- init: SIGUSR1/SIGUSR2 handlers for poweroff/reboot

Low-priority commands:
- stat: date/time display, UID/GID name lookup, file type
- dd: conv= (ucase/lcase/swab/noerror/sync/trunc), skip=, seek=
- ls: -n flag for numeric UID/GID

Tests: 120/120 QEMU, 33/33 battery, 57/57 host (3 skipped)

21 files changed:
user/cmds/awk/awk.c
user/cmds/chmod/chmod.c
user/cmds/cp/cp.c
user/cmds/dd/dd.c
user/cmds/df/df.c
user/cmds/find/find.c
user/cmds/grep/grep.c
user/cmds/init/init.c
user/cmds/kill/kill.c
user/cmds/ls/ls.c
user/cmds/mv/mv.c
user/cmds/ps/ps.c
user/cmds/rm/rm.c
user/cmds/sed/sed.c
user/cmds/sh/sh.c
user/cmds/stat/stat.c
user/cmds/top/top.c
user/ulibc/include/sys/statvfs.h [new file with mode: 0644]
user/ulibc/include/time.h
user/ulibc/src/statvfs.c [new file with mode: 0644]
user/ulibc/src/time.c

index a1500dda4698ea6e8c27b8754aa39b3c12c9e2ea..438c7a7b72a549bf712e3f589cfac14352a856e1 100644 (file)
  * Source: https://github.com/tadryanom/AdrOS
  */
 
-/* AdrOS awk utility — minimal: print fields, pattern matching, BEGIN/END */
+/* AdrOS awk utility — minimal: BEGIN/END, NF, NR, print with OFS, -v var=val */
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <stdlib.h>
+#include <regex.h>
 
-static char delim = ' ';
-static int print_field = -1; /* -1 = whole line */
-static char pattern[256] = "";
-static int has_pattern = 0;
+#define MAX_FIELDS 64
+#define MAX_VARS   64
+#define MAX_RULES  32
 
-static void process_line(const char* line) {
-    if (has_pattern && !strstr(line, pattern)) return;
+struct awk_var {
+    char name[32];
+    char value[256];
+};
 
-    if (print_field < 0) {
-        printf("%s\n", line);
-        return;
+static struct awk_var vars[MAX_VARS];
+static int nvars = 0;
+
+static char field_sep = ' ';  /* -F separator */
+static char OFS[32] = " ";    /* output field separator */
+static int NR = 0;            /* record (line) number */
+static int NF = 0;            /* number of fields in current record */
+static char* fields[MAX_FIELDS]; /* field pointers into split buffer */
+
+enum rule_type { RULE_BEGIN, RULE_END, RULE_PATTERN, RULE_NONE };
+
+struct awk_rule {
+    enum rule_type type;
+    regex_t pattern_re;
+    int has_re;
+    char action[256];  /* simplified: "print" or "print $1,$2" etc */
+};
+
+static struct awk_rule rules[MAX_RULES];
+static int nrules = 0;
+
+static void var_set(const char* name, const char* value) {
+    for (int i = 0; i < nvars; i++) {
+        if (strcmp(vars[i].name, name) == 0) {
+            strncpy(vars[i].value, value, sizeof(vars[i].value) - 1);
+            return;
+        }
     }
+    if (nvars < MAX_VARS) {
+        strncpy(vars[nvars].name, name, sizeof(vars[nvars].name) - 1);
+        strncpy(vars[nvars].value, value, sizeof(vars[nvars].value) - 1);
+        nvars++;
+    }
+}
 
-    /* Split into fields */
-    char copy[4096];
-    strncpy(copy, line, sizeof(copy) - 1);
-    copy[sizeof(copy) - 1] = '\0';
+static const char* var_get(const char* name) {
+    if (strcmp(name, "NR") == 0) { static char buf[16]; snprintf(buf, sizeof(buf), "%d", NR); return buf; }
+    if (strcmp(name, "NF") == 0) { static char buf[16]; snprintf(buf, sizeof(buf), "%d", NF); return buf; }
+    if (strcmp(name, "OFS") == 0) return OFS;
+    if (strcmp(name, "FS") == 0) { static char buf[2]; buf[0] = field_sep; buf[1] = '\0'; return buf; }
+    for (int i = 0; i < nvars; i++) {
+        if (strcmp(vars[i].name, name) == 0) return vars[i].value;
+    }
+    return "";
+}
 
-    int fi = 0;
-    char* p = copy;
-    while (*p) {
-        while (*p && (*p == delim || *p == '\t')) p++;
+static void split_line(char* line) {
+    NF = 0;
+    char* p = line;
+    while (*p && NF < MAX_FIELDS - 1) {
+        while (*p && (*p == field_sep || *p == '\t')) p++;
         if (!*p) break;
-        char* start = p;
-        while (*p && *p != delim && *p != '\t') p++;
+        fields[NF++] = p;
+        while (*p && *p != field_sep && *p != '\t') p++;
         if (*p) *p++ = '\0';
-        if (fi == print_field) {
-            printf("%s\n", start);
-            return;
+    }
+}
+
+static const char* g_orig_line;
+
+static const char* resolve_field(const char* tok) {
+    if (tok[0] == '$') {
+        int n = atoi(tok + 1);
+        if (n == 0) return g_orig_line ? g_orig_line : "";
+        if (n > 0 && n <= NF) return fields[n - 1];
+        return "";
+    }
+    /* Check for variable name */
+    if ((tok[0] >= 'A' && tok[0] <= 'Z') || (tok[0] >= 'a' && tok[0] <= 'z') || tok[0] == '_') {
+        return var_get(tok);
+    }
+    return tok;
+}
+
+static void do_print(const char* action, char* orig_line) {
+    /* Parse print arguments: print $1, $2, NR, etc. */
+    const char* p = action + 5; /* skip "print" */
+    while (*p == ' ') p++;
+
+    if (*p == '\0' || *p == '}') {
+        /* print with no args = print $0 (whole line) */
+        printf("%s\n", orig_line);
+        return;
+    }
+
+    int first = 1;
+    while (*p && *p != '}') {
+        while (*p == ' ') p++;
+        if (*p == ',' || *p == ' ') { p++; continue; }
+        if (*p == '\0' || *p == '}') break;
+
+        /* Extract token */
+        char tok[64];
+        int ti = 0;
+        while (*p && *p != ',' && *p != ' ' && *p != '}' && ti < 63)
+            tok[ti++] = *p++;
+        tok[ti] = '\0';
+
+        if (!first) printf("%s", OFS);
+        first = 0;
+
+        /* String literal */
+        if (tok[0] == '"') {
+            char* end = tok + strlen(tok) - 1;
+            if (*end == '"') {
+                tok[ti - 1] = '\0';
+                printf("%s", tok + 1);
+            } else {
+                printf("%s", tok);
+            }
+        } else {
+            printf("%s", resolve_field(tok));
         }
-        fi++;
     }
     printf("\n");
 }
 
-int main(int argc, char** argv) {
-    if (argc < 2) {
-        fprintf(stderr, "Usage: awk [-F sep] '{print $N}' [file]\n");
-        return 1;
+static void execute_action(const char* action, char* orig_line) {
+    if (strncmp(action, "print", 5) == 0) {
+        do_print(action, orig_line);
+    }
+}
+
+static void process_rules(char* line) {
+    g_orig_line = line;
+    split_line(line);
+    for (int i = 0; i < nrules; i++) {
+        if (rules[i].type == RULE_PATTERN) {
+            if (rules[i].has_re) {
+                if (regexec(&rules[i].pattern_re, line, 0, NULL, 0) != 0) continue;
+            }
+            execute_action(rules[i].action, line);
+        } else if (rules[i].type == RULE_NONE) {
+            execute_action(rules[i].action, line);
+        }
     }
+}
+
+static void parse_program(const char* prog) {
+    /* Very simplified awk program parser.
+     * Supports: BEGIN{...} END{...} /pattern/{...} {...}
+     * Actions: print, print $1,$2, print NR, etc.
+     */
+    const char* p = prog;
+    while (*p) {
+        while (*p == ' ' || *p == '\t' || *p == '\n' || *p == ';') p++;
+        if (!*p) break;
+
+        enum rule_type rtype = RULE_NONE;
+        char pat[256] = "";
+
+        if (strncmp(p, "BEGIN", 5) == 0 && (p[5] == '{' || p[5] == ' ')) {
+            rtype = RULE_BEGIN;
+            p += 5;
+            while (*p == ' ') p++;
+        } else if (strncmp(p, "END", 3) == 0 && (p[3] == '{' || p[3] == ' ')) {
+            rtype = RULE_END;
+            p += 3;
+            while (*p == ' ') p++;
+        } else if (*p == '/') {
+            /* Pattern /regex/ */
+            p++;
+            int pi = 0;
+            while (*p && *p != '/' && pi < 255) pat[pi++] = *p++;
+            pat[pi] = '\0';
+            if (*p == '/') p++;
+            rtype = RULE_PATTERN;
+            while (*p == ' ') p++;
+        }
+
+        if (*p == '{') {
+            p++;
+            /* Extract action body */
+            char action[256];
+            int ai = 0;
+            int depth = 1;
+            while (*p && depth > 0 && ai < 255) {
+                if (*p == '{') depth++;
+                else if (*p == '}') { depth--; if (depth == 0) break; }
+                action[ai++] = *p++;
+            }
+            action[ai] = '\0';
+            if (*p == '}') p++;
 
+            if (nrules < MAX_RULES) {
+                rules[nrules].type = rtype;
+                rules[nrules].has_re = 0;
+                strncpy(rules[nrules].action, action, sizeof(rules[nrules].action) - 1);
+                if (rtype == RULE_PATTERN && pat[0]) {
+                    if (regcomp(&rules[nrules].pattern_re, pat, REG_EXTENDED) == 0)
+                        rules[nrules].has_re = 1;
+                }
+                nrules++;
+            }
+        } else {
+            /* No braces: entire remaining text is action for RULE_NONE */
+            if (nrules < MAX_RULES) {
+                rules[nrules].type = rtype;
+                rules[nrules].has_re = 0;
+                strncpy(rules[nrules].action, p, sizeof(rules[nrules].action) - 1);
+                if (rtype == RULE_PATTERN && pat[0]) {
+                    if (regcomp(&rules[nrules].pattern_re, pat, REG_EXTENDED) == 0)
+                        rules[nrules].has_re = 1;
+                }
+                nrules++;
+            }
+            break;
+        }
+    }
+}
+
+int main(int argc, char** argv) {
     int argi = 1;
-    if (strcmp(argv[argi], "-F") == 0 && argi + 1 < argc) {
-        delim = argv[argi + 1][0];
-        argi += 2;
+    while (argi < argc && argv[argi][0] == '-') {
+        if (strcmp(argv[argi], "-F") == 0 && argi + 1 < argc) {
+            field_sep = argv[argi + 1][0];
+            argi += 2;
+        } else if (strcmp(argv[argi], "-v") == 0 && argi + 1 < argc) {
+            char* eq = strchr(argv[argi + 1], '=');
+            if (eq) {
+                char name[32];
+                int nlen = (int)(eq - argv[argi + 1]);
+                if (nlen > 31) nlen = 31;
+                memcpy(name, argv[argi + 1], (size_t)nlen);
+                name[nlen] = '\0';
+                var_set(name, eq + 1);
+            }
+            argi += 2;
+        } else if (strcmp(argv[argi], "--") == 0) { argi++; break; }
+        else { fprintf(stderr, "awk: invalid option '%s'\n", argv[argi]); return 1; }
     }
 
     if (argi >= argc) {
-        fprintf(stderr, "awk: missing program\n");
+        fprintf(stderr, "Usage: awk [-F sep] [-v var=val] 'program' [file]\n");
         return 1;
     }
 
-    /* Parse simple program: {print $N} or /pattern/{print $N} */
     const char* prog = argv[argi++];
+    parse_program(prog);
 
-    /* Check for /pattern/ prefix */
-    if (prog[0] == '/') {
-        const char* end = strchr(prog + 1, '/');
-        if (end) {
-            int plen = (int)(end - prog - 1);
-            if (plen > 0 && plen < (int)sizeof(pattern)) {
-                memcpy(pattern, prog + 1, plen);
-                pattern[plen] = '\0';
-                has_pattern = 1;
-            }
-            prog = end + 1;
-        }
-    }
-
-    /* Parse {print $N} */
-    const char* pp = strstr(prog, "print");
-    if (pp) {
-        const char* dollar = strchr(pp, '$');
-        if (dollar) {
-            int n = atoi(dollar + 1);
-            print_field = (n > 0) ? n - 1 : -1;
-            if (n == 0) print_field = -1; /* $0 = whole line */
-        }
+    /* Execute BEGIN rules */
+    for (int i = 0; i < nrules; i++) {
+        if (rules[i].type == RULE_BEGIN)
+            execute_action(rules[i].action, "");
     }
 
     int fd = STDIN_FILENO;
@@ -104,12 +280,15 @@ int main(int argc, char** argv) {
     }
 
     char line[4096];
+    char orig[4096];
     int li = 0;
     char c;
     while (read(fd, &c, 1) == 1) {
         if (c == '\n') {
             line[li] = '\0';
-            process_line(line);
+            strcpy(orig, line);
+            NR++;
+            process_rules(line);
             li = 0;
         } else if (li < (int)sizeof(line) - 1) {
             line[li++] = c;
@@ -117,9 +296,20 @@ int main(int argc, char** argv) {
     }
     if (li > 0) {
         line[li] = '\0';
-        process_line(line);
+        strcpy(orig, line);
+        NR++;
+        process_rules(line);
     }
 
+    /* Execute END rules */
+    for (int i = 0; i < nrules; i++) {
+        if (rules[i].type == RULE_END)
+            execute_action(rules[i].action, "");
+    }
+
+    for (int i = 0; i < nrules; i++) {
+        if (rules[i].has_re) regfree(&rules[i].pattern_re);
+    }
     if (fd != STDIN_FILENO) close(fd);
     return 0;
 }
index 84962ba96863e08e906b6711bb65200a8c1fc6b1..afec94ea0bfb047fb5cb8c82704e240d57298226 100644 (file)
  * Source: https://github.com/tadryanom/AdrOS
  */
 
-/* AdrOS chmod utility */
+/* AdrOS chmod utility — change file mode bits */
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
+#include <stdlib.h>
 #include <unistd.h>
+#include <sys/stat.h>
+
+static unsigned int parse_symbolic(const char* mode, unsigned int old) {
+    /* Parse symbolic mode: [ugoa...][+-=][rwxst...][,...] */
+    unsigned int result = old;
+    const char* p = mode;
+    while (*p) {
+        /* Parse who */
+        unsigned int who = 0;
+        while (*p == 'u' || *p == 'g' || *p == 'o' || *p == 'a') {
+            switch (*p) {
+            case 'u': who |= 04700; break; /* user: setuid + user bits */
+            case 'g': who |= 02070; break; /* group: setgid + group bits */
+            case 'o': who |= 00007; break; /* other bits */
+            case 'a': who |= 06777; break; /* all */
+            }
+            p++;
+        }
+        if (who == 0) who = 06777; /* default: all */
+
+        /* Parse operation */
+        while (*p) {
+            char op = *p;
+            if (op != '+' && op != '-' && op != '=') break;
+            p++;
+
+            /* Parse permissions */
+            unsigned int perm = 0;
+            while (*p == 'r' || *p == 'w' || *p == 'x' ||
+                   *p == 's' || *p == 't') {
+                switch (*p) {
+                case 'r': perm |= 0444; break;
+                case 'w': perm |= 0222; break;
+                case 'x': perm |= 0111; break;
+                case 's': perm |= 06000; break; /* setuid+setgid */
+                case 't': perm |= 01000; break; /* sticky */
+                }
+                p++;
+            }
+
+            /* Apply perm mask to who */
+            unsigned int masked = perm & who;
+
+            if (op == '+') {
+                result |= masked;
+            } else if (op == '-') {
+                result &= ~masked;
+            } else { /* = */
+                result &= ~who;  /* clear who bits */
+                result |= masked;
+            }
+
+            /* Check for comma separator or next operation */
+            if (*p == ',') { p++; break; }
+        }
+    }
+    return result & 07777;
+}
 
 int main(int argc, char** argv) {
     if (argc < 3) {
-        fprintf(stderr, "Usage: chmod <mode> <file>...\n");
+        fprintf(stderr, "Usage: chmod MODE FILE...\n");
         return 1;
     }
 
-    int mode = (int)strtol(argv[1], NULL, 8);
-    int rc = 0;
+    const char* mode_str = argv[1];
+    unsigned int mode;
+
+    /* Check if symbolic or octal */
+    int is_symbolic = 0;
+    for (int i = 0; mode_str[i]; i++) {
+        char c = mode_str[i];
+        if (c == '+' || c == '-' || c == '=' || c == 'u' || c == 'g' ||
+            c == 'o' || c == 'a' || c == 'r' || c == 'w' || c == 'x' ||
+            c == 's' || c == 't' || c == ',') {
+            is_symbolic = 1;
+            break;
+        }
+    }
 
-    for (int i = 2; i < argc; i++) {
-        if (chmod(argv[i], mode) < 0) {
-            fprintf(stderr, "chmod: cannot change mode of '%s'\n", argv[i]);
-            rc = 1;
+    if (is_symbolic) {
+        for (int i = 2; i < argc; i++) {
+            struct stat st;
+            if (stat(argv[i], &st) < 0) {
+                fprintf(stderr, "chmod: cannot access '%s'\n", argv[i]);
+                continue;
+            }
+            mode = parse_symbolic(mode_str, (unsigned int)st.st_mode);
+            if (chmod(argv[i], (mode_t)mode) < 0)
+                fprintf(stderr, "chmod: cannot change mode of '%s'\n", argv[i]);
+        }
+    } else {
+        mode = (unsigned int)strtol(mode_str, NULL, 8);
+        for (int i = 2; i < argc; i++) {
+            if (chmod(argv[i], (mode_t)mode) < 0)
+                fprintf(stderr, "chmod: cannot change mode of '%s'\n", argv[i]);
         }
     }
-    return rc;
+
+    return 0;
 }
index 61cb8a861c687f25f772779aeb09967ca15b8df0..ac952d460e9e9890ea3bf73e752574dd385f0d53 100644 (file)
@@ -9,9 +9,10 @@
 
 /* AdrOS cp utility */
 #include <stdio.h>
+#include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <string.h>
+#include <sys/stat.h>
 
 int main(int argc, char** argv) {
     if (argc < 3) {
@@ -25,7 +26,11 @@ int main(int argc, char** argv) {
         return 1;
     }
 
-    int dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    struct stat src_st;
+    int src_mode = 0644;
+    if (fstat(src, &src_st) == 0) src_mode = (int)src_st.st_mode;
+
+    int dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, (mode_t)src_mode);
     if (dst < 0) {
         fprintf(stderr, "cp: cannot create '%s'\n", argv[2]);
         close(src);
index 35380bf650e5ec27589e31d5702e69057c42044e..47981eab92dd6e1e4542b6580fd4d2008268a773 100644 (file)
@@ -25,17 +25,47 @@ static int parse_size(const char* s) {
     return v;
 }
 
+#define CONV_UCASE   0x01
+#define CONV_LCASE   0x02
+#define CONV_SWAB    0x04
+#define CONV_NOERROR 0x08
+#define CONV_SYNC    0x10
+#define CONV_TRUNC   0x20
+
 int main(int argc, char** argv) {
     const char* inf = NULL;
     const char* outf = NULL;
     int bs = 512;
     int count = -1;
+    int skip = 0;
+    int seek_val = 0;
+    unsigned int conv = 0;
 
     for (int i = 1; i < argc; i++) {
         if (strncmp(argv[i], "if=", 3) == 0) inf = argv[i] + 3;
         else if (strncmp(argv[i], "of=", 3) == 0) outf = argv[i] + 3;
         else if (strncmp(argv[i], "bs=", 3) == 0) bs = parse_size(argv[i] + 3);
         else if (strncmp(argv[i], "count=", 6) == 0) count = atoi(argv[i] + 6);
+        else if (strncmp(argv[i], "skip=", 5) == 0) skip = atoi(argv[i] + 5);
+        else if (strncmp(argv[i], "seek=", 5) == 0) seek_val = atoi(argv[i] + 5);
+        else if (strncmp(argv[i], "conv=", 5) == 0) {
+            const char* c = argv[i] + 5;
+            char ccopy[64];
+            strncpy(ccopy, c, sizeof(ccopy) - 1);
+            ccopy[sizeof(ccopy) - 1] = '\0';
+            char* save = ccopy;
+            char* tok;
+            while ((tok = save) != NULL) {
+                char* comma = strchr(save, ',');
+                if (comma) { *comma = '\0'; save = comma + 1; } else save = NULL;
+                if (strcmp(tok, "ucase") == 0) conv |= CONV_UCASE;
+                else if (strcmp(tok, "lcase") == 0) conv |= CONV_LCASE;
+                else if (strcmp(tok, "swab") == 0) conv |= CONV_SWAB;
+                else if (strcmp(tok, "noerror") == 0) conv |= CONV_NOERROR;
+                else if (strcmp(tok, "sync") == 0) conv |= CONV_SYNC;
+                else if (strcmp(tok, "trunc") == 0) conv |= CONV_TRUNC;
+            }
+        }
     }
 
     int ifd = STDIN_FILENO;
@@ -46,17 +76,54 @@ int main(int argc, char** argv) {
         if (ifd < 0) { fprintf(stderr, "dd: cannot open '%s'\n", inf); return 1; }
     }
     if (outf) {
-        ofd = open(outf, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+        int oflags = O_WRONLY | O_CREAT | O_TRUNC;
+        ofd = open(outf, oflags, 0644);
         if (ofd < 0) { fprintf(stderr, "dd: cannot open '%s'\n", outf); return 1; }
     }
 
+    /* Apply skip (input) */
+    if (skip > 0 && ifd != STDIN_FILENO) {
+        lseek(ifd, (off_t)(skip * bs), SEEK_SET);
+    }
+
+    /* Apply seek (output) */
+    if (seek_val > 0 && ofd != STDOUT_FILENO) {
+        lseek(ofd, (off_t)(seek_val * bs), SEEK_SET);
+    }
+
     if (bs > 4096) bs = 4096;
     char buf[4096];
     int blocks = 0, partial = 0, total = 0;
 
     while (count < 0 || blocks + partial < count) {
         int n = read(ifd, buf, (size_t)bs);
-        if (n <= 0) break;
+        if (n < 0) {
+            if (conv & CONV_NOERROR) continue;
+            break;
+        }
+        if (n == 0) break;
+
+        /* Pad with spaces if sync and partial block */
+        if (conv & CONV_SYNC && n < bs) {
+            memset(buf + n, ' ', (size_t)(bs - n));
+            n = bs;
+        }
+
+        /* Apply conversions */
+        if (conv & CONV_UCASE) {
+            for (int i = 0; i < n; i++)
+                if (buf[i] >= 'a' && buf[i] <= 'z') buf[i] = (char)(buf[i] - 32);
+        }
+        if (conv & CONV_LCASE) {
+            for (int i = 0; i < n; i++)
+                if (buf[i] >= 'A' && buf[i] <= 'Z') buf[i] = (char)(buf[i] + 32);
+        }
+        if (conv & CONV_SWAB) {
+            for (int i = 0; i + 1 < n; i += 2) {
+                char tmp = buf[i]; buf[i] = buf[i+1]; buf[i+1] = tmp;
+            }
+        }
+
         write(ofd, buf, (size_t)n);
         total += n;
         if (n == bs) blocks++;
index 9eacf3db52f9e04974d278eeb6949566278223e1..fe015f6435aa8e53931e1543caf4dcbd129a0194 100644 (file)
 
 /* AdrOS df utility — display filesystem disk space usage */
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/statvfs.h>
 
-int main(void) {
+static int hflag = 0;  /* -h: human-readable */
+static int Tflag = 0;  /* -T: print filesystem type */
+
+static void format_size(unsigned long kbytes, char* buf, size_t bufsz) {
+    if (!hflag) {
+        snprintf(buf, bufsz, "%lu", kbytes);
+        return;
+    }
+    if (kbytes >= 1024UL * 1024 * 1024)
+        snprintf(buf, bufsz, "%.1fT", (double)kbytes / (1024.0 * 1024 * 1024));
+    else if (kbytes >= 1024UL * 1024)
+        snprintf(buf, bufsz, "%.1fG", (double)kbytes / (1024.0 * 1024));
+    else if (kbytes >= 1024UL)
+        snprintf(buf, bufsz, "%.1fM", (double)kbytes / 1024.0);
+    else
+        snprintf(buf, bufsz, "%luK", kbytes);
+}
+
+int main(int argc, char** argv) {
+    int argi = 1;
+    while (argi < argc && argv[argi][0] == '-') {
+        if (strcmp(argv[argi], "-h") == 0) hflag = 1;
+        else if (strcmp(argv[argi], "-T") == 0) Tflag = 1;
+        else if (strcmp(argv[argi], "--") == 0) { argi++; break; }
+        else { fprintf(stderr, "df: invalid option '%s'\n", argv[argi]); return 1; }
+        argi++;
+    }
+
+    /* Print header */
+    if (Tflag)
+        printf("Filesystem     Type   1K-blocks      Used Available Use%% Mounted on\n");
+    else
+        printf("Filesystem     1K-blocks      Used Available Use%% Mounted on\n");
+
+    /* Read /proc/mounts */
     int fd = open("/proc/mounts", O_RDONLY);
     if (fd < 0) {
         fprintf(stderr, "df: cannot open /proc/mounts\n");
         return 1;
     }
-    printf("Filesystem     Size  Used  Avail  Use%%  Mounted on\n");
-    char buf[1024];
-    int n;
-    while ((n = read(fd, buf, sizeof(buf))) > 0)
-        write(STDOUT_FILENO, buf, (size_t)n);
+
+    char buf[2048];
+    int total = 0, n;
+    while ((n = read(fd, buf + total, (size_t)(sizeof(buf) - 1 - total))) > 0)
+        total += n;
+    buf[total] = '\0';
     close(fd);
+
+    /* Parse each line: dev dir type ... */
+    char* line = buf;
+    while (line && *line) {
+        char* nl = line;
+        while (*nl && *nl != '\n') nl++;
+        int has_nl = (*nl == '\n');
+        if (has_nl) *nl = '\0';
+
+        /* Tokenize: dev dir type ... */
+        char* tokens[8];
+        int ntok = 0;
+        char* p = line;
+        while (*p && ntok < 8) {
+            while (*p == ' ' || *p == '\t') p++;
+            if (!*p) break;
+            tokens[ntok++] = p;
+            while (*p && *p != ' ' && *p != '\t') p++;
+            if (*p) *p++ = '\0';
+        }
+
+        if (ntok >= 2) {
+            const char* dev  = tokens[0];
+            const char* dir  = tokens[1];
+            const char* type = (ntok >= 3) ? tokens[2] : "unknown";
+
+            struct statvfs fsbuf;
+            if (statvfs(dir, &fsbuf) == 0) {
+                unsigned long bsize = fsbuf.f_frsize ? fsbuf.f_frsize : fsbuf.f_bsize;
+                unsigned long total_kb = (fsbuf.f_blocks * bsize) / 1024;
+                unsigned long free_kb  = (fsbuf.f_bfree * bsize) / 1024;
+                unsigned long used_kb  = total_kb - free_kb;
+                unsigned long avail_kb = (fsbuf.f_bavail * bsize) / 1024;
+                int pct = (total_kb > 0) ? (int)((used_kb * 100) / total_kb) : 0;
+
+                char stotal[16], sused[16], savail[16];
+                if (hflag) {
+                    format_size(total_kb, stotal, sizeof(stotal));
+                    format_size(used_kb, sused, sizeof(sused));
+                    format_size(avail_kb, savail, sizeof(savail));
+                    if (Tflag)
+                        printf("%-14s %-6s %6s %6s %6s %3d%% %s\n",
+                               dev, type, stotal, sused, savail, pct, dir);
+                    else
+                        printf("%-14s %6s %6s %6s %3d%% %s\n",
+                               dev, stotal, sused, savail, pct, dir);
+                } else {
+                    if (Tflag)
+                        printf("%-14s %-6s %10lu %10lu %10lu %3d%% %s\n",
+                               dev, type, total_kb, used_kb, avail_kb, pct, dir);
+                    else
+                        printf("%-14s %10lu %10lu %10lu %3d%% %s\n",
+                               dev, total_kb, used_kb, avail_kb, pct, dir);
+                }
+            }
+        }
+
+        line = has_nl ? nl + 1 : nl;
+    }
+
     return 0;
 }
index 2cfc2be6a9970d051fc493305a1ac14be96671ee..6b2bbc905702c7af88dda8af7f1e8b4d190a3662 100644 (file)
  * Source: https://github.com/tadryanom/AdrOS
  */
 
-/* AdrOS find utility — search for files in directory hierarchy */
+/* AdrOS find utility — search for files in directory hierarchy */
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include <dirent.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <time.h>
+#include <stdlib.h>
 
-#ifndef DT_DIR
-#define DT_DIR 4
-#endif
-
-static const char* name_pattern = NULL;
-static int type_filter = 0; /* 0=any, 'f'=file, 'd'=dir */
-
-static int match_name(const char* name) {
-    if (!name_pattern) return 1;
-    /* Simple wildcard: *pattern* if pattern has no special chars,
-       or exact match. Support leading/trailing * only. */
-    int plen = (int)strlen(name_pattern);
-    if (plen == 0) return 1;
-
-    const char* pat = name_pattern;
-    int lead_star = (pat[0] == '*');
-    int trail_star = (plen > 1 && pat[plen-1] == '*');
-
-    if (lead_star && trail_star) {
-        char sub[256];
-        int slen = plen - 2;
-        if (slen <= 0) return 1;
-        memcpy(sub, pat + 1, (size_t)slen);
-        sub[slen] = '\0';
-        return strstr(name, sub) != NULL;
+#define MAX_PRED 32
+
+enum pred_type {
+    PRED_NAME, PRED_TYPE, PRED_MTIME, PRED_SIZE, PRED_PERM,
+    PRED_USER, PRED_MAXDEPTH, PRED_DELETE, PRED_EXEC, PRED_PRINT
+};
+
+struct predicate {
+    enum pred_type type;
+    char arg[256];
+    int negate;
+    /* -mtime: n means exactly n days, +n means > n, -n means < n */
+    int cmp_sign;  /* 0=exact, 1=greater, -1=less */
+    int n_arg;
+    /* -perm */
+    unsigned int perm_val;
+    /* -exec args */
+    char exec_cmd[256];
+    char exec_args[8][64];
+    int exec_nargs;
+};
+
+static struct predicate preds[MAX_PRED];
+static int npreds = 0;
+static int maxdepth = -1;
+static int did_print = 0;  /* whether -print was explicitly given */
+
+static int match_name(const char* name, const char* pattern) {
+    /* Simple glob: * matches anything, ? matches one char */
+    const char* s = name, *p = pattern;
+    while (*s && *p) {
+        if (*p == '*') {
+            p++;
+            if (!*p) return 1;
+            while (*s) {
+                if (match_name(s, p)) return 1;
+                s++;
+            }
+            return match_name(s, p);
+        }
+        if (*p == '?' || *p == *s) { p++; s++; continue; }
+        return 0;
     }
-    if (lead_star) {
-        const char* suffix = pat + 1;
-        int slen = plen - 1;
-        int nlen = (int)strlen(name);
-        if (nlen < slen) return 0;
-        return strcmp(name + nlen - slen, suffix) == 0;
+    while (*p == '*') p++;
+    return (*s == '\0' && *p == '\0');
+}
+
+static int eval_pred(struct predicate* pr, const char* path, const struct stat* st, int depth) {
+    int result = 1;
+    switch (pr->type) {
+    case PRED_NAME: {
+        const char* name = strrchr(path, '/');
+        name = name ? name + 1 : path;
+        result = match_name(name, pr->arg);
+        break;
+    }
+    case PRED_TYPE:
+        if (pr->arg[0] == 'f') result = S_ISREG(st->st_mode);
+        else if (pr->arg[0] == 'd') result = S_ISDIR(st->st_mode);
+        else if (pr->arg[0] == 'c') result = S_ISCHR(st->st_mode);
+        else if (pr->arg[0] == 'b') result = S_ISBLK(st->st_mode);
+        else if (pr->arg[0] == 'l') result = S_ISLNK(st->st_mode);
+        else if (pr->arg[0] == 'p') result = S_ISFIFO(st->st_mode);
+        else result = 0;
+        break;
+    case PRED_MTIME: {
+        time_t now = time((time_t*)0);
+        int days = (int)((now - (time_t)st->st_mtime) / 86400);
+        if (pr->cmp_sign == 0) result = (days == pr->n_arg);
+        else if (pr->cmp_sign == 1) result = (days > pr->n_arg);
+        else result = (days < pr->n_arg);
+        break;
+    }
+    case PRED_SIZE: {
+        long sz = (long)st->st_size;
+        long n = (long)pr->n_arg;
+        if (pr->cmp_sign == 0) result = (sz == n);
+        else if (pr->cmp_sign == 1) result = (sz > n);
+        else result = (sz < n);
+        break;
     }
-    if (trail_star) {
-        return strncmp(name, pat, (size_t)(plen - 1)) == 0;
+    case PRED_PERM:
+        result = ((unsigned int)(st->st_mode & 07777) == pr->perm_val);
+        break;
+    case PRED_USER: {
+        struct passwd* pw = getpwnam(pr->arg);
+        if (pw) result = ((int)st->st_uid == pw->pw_uid);
+        else { int uid = atoi(pr->arg); result = ((int)st->st_uid == uid); }
+        break;
     }
-    return strcmp(name, pat) == 0;
+    case PRED_MAXDEPTH:
+        result = (depth <= pr->n_arg);
+        break;
+    case PRED_DELETE:
+        if (S_ISDIR(st->st_mode)) result = (rmdir(path) == 0);
+        else result = (unlink(path) == 0);
+        if (result) return 1;  /* deleted, don't print */
+        break;
+    case PRED_EXEC: {
+        /* Replace {} with path */
+        char* argv[10];
+        argv[0] = pr->exec_cmd;
+        for (int i = 0; i < pr->exec_nargs; i++) {
+            if (strcmp(pr->exec_args[i], "{}") == 0)
+                argv[i + 1] = (char*)path;
+            else
+                argv[i + 1] = pr->exec_args[i];
+        }
+        argv[pr->exec_nargs + 1] = (char*)0;
+        int pid = fork();
+        if (pid == 0) {
+            extern char** environ;
+            execve(pr->exec_cmd, argv, environ);
+            _exit(127);
+        }
+        int status = 0;
+        if (pid > 0) waitpid(pid, &status, 0);
+        result = (pid > 0 && status == 0);
+        break;
+    }
+    case PRED_PRINT:
+        result = 1;
+        break;
+    }
+    if (pr->negate) result = !result;
+    return result;
 }
 
-static void find_recurse(const char* path) {
-    DIR* dir = opendir(path);
-    if (!dir) return;
+static void find_recurse(const char* path, int depth);
 
-    struct dirent* d;
-    while ((d = readdir(dir)) != NULL) {
-        if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0)
-            continue;
+static void find_dir(const char* path, int depth) {
+    if (maxdepth >= 0 && depth > maxdepth) return;
+    int fd = open(path, O_RDONLY);
+    if (fd < 0) return;
+    char dbuf[2048];
+    int rc;
+    while ((rc = getdents(fd, dbuf, sizeof(dbuf))) > 0) {
+        int off = 0;
+        while (off < rc) {
+            struct dirent* d = (struct dirent*)(dbuf + off);
+            if (d->d_reclen == 0) break;
+            if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
+                (d->d_name[1] == '.' && d->d_name[2] == '\0'))) {
+                off += d->d_reclen;
+                continue;
+            }
+            char child[512];
+            size_t plen = strlen(path);
+            if (plen > 0 && path[plen - 1] == '/')
+                snprintf(child, sizeof(child), "%s%s", path, d->d_name);
+            else
+                snprintf(child, sizeof(child), "%s/%s", path, d->d_name);
+            find_recurse(child, depth + 1);
+            off += d->d_reclen;
+        }
+    }
+    close(fd);
+}
+
+static void find_recurse(const char* path, int depth) {
+    struct stat st;
+    if (stat(path, &st) < 0) return;
 
-        char child[512];
-        size_t plen = strlen(path);
-        if (plen > 0 && path[plen - 1] == '/')
-            snprintf(child, sizeof(child), "%s%s", path, d->d_name);
-        else
-            snprintf(child, sizeof(child), "%s/%s", path, d->d_name);
-
-        int is_dir = (d->d_type == DT_DIR);
-        int show = match_name(d->d_name);
-        if (show && type_filter) {
-            if (type_filter == 'f' && is_dir) show = 0;
-            if (type_filter == 'd' && !is_dir) show = 0;
+    int match = 1;
+    int do_print = !did_print;
+    for (int i = 0; i < npreds && match; i++) {
+        if (preds[i].type == PRED_PRINT) { do_print = 1; continue; }
+        if (preds[i].type == PRED_MAXDEPTH) {
+            if (depth > preds[i].n_arg) { match = 0; break; }
+            continue;
         }
-        if (show) printf("%s\n", child);
+        match = eval_pred(&preds[i], path, &st, depth);
+    }
+
+    if (match && do_print) printf("%s\n", path);
 
-        if (is_dir) {
-            find_recurse(child);
+    if (match && S_ISDIR(st.st_mode)) {
+        /* Check if any predicate was -delete and matched */
+        int deleted = 0;
+        for (int i = 0; i < npreds; i++) {
+            if (preds[i].type == PRED_DELETE && eval_pred(&preds[i], path, (const struct stat*)&st, depth)) {
+                deleted = 1; break;
+            }
         }
+        if (!deleted) find_dir(path, depth);
     }
-    closedir(dir);
 }
 
 int main(int argc, char** argv) {
     const char* start = ".";
     int argi = 1;
 
-    if (argi < argc && argv[argi][0] != '-') {
+    /* First non-option arg is starting path */
+    if (argi < argc && argv[argi][0] != '-' && strcmp(argv[argi], "--") != 0) {
         start = argv[argi++];
     }
 
     while (argi < argc) {
+        if (strcmp(argv[argi], "--") == 0) { argi++; break; }
         if (strcmp(argv[argi], "-name") == 0 && argi + 1 < argc) {
-            name_pattern = argv[++argi];
+            preds[npreds].type = PRED_NAME;
+            strncpy(preds[npreds].arg, argv[++argi], sizeof(preds[npreds].arg) - 1);
+            npreds++;
         } else if (strcmp(argv[argi], "-type") == 0 && argi + 1 < argc) {
-            type_filter = argv[++argi][0];
+            preds[npreds].type = PRED_TYPE;
+            strncpy(preds[npreds].arg, argv[++argi], sizeof(preds[npreds].arg) - 1);
+            npreds++;
+        } else if (strcmp(argv[argi], "-mtime") == 0 && argi + 1 < argc) {
+            argi++;
+            preds[npreds].type = PRED_MTIME;
+            const char* a = argv[argi];
+            if (a[0] == '+') { preds[npreds].cmp_sign = 1; preds[npreds].n_arg = atoi(a + 1); }
+            else if (a[0] == '-') { preds[npreds].cmp_sign = -1; preds[npreds].n_arg = atoi(a + 1); }
+            else { preds[npreds].cmp_sign = 0; preds[npreds].n_arg = atoi(a); }
+            npreds++;
+        } else if (strcmp(argv[argi], "-size") == 0 && argi + 1 < argc) {
+            argi++;
+            preds[npreds].type = PRED_SIZE;
+            const char* a = argv[argi];
+            int mul = 1;
+            int alen = (int)strlen(a);
+            if (alen > 0 && (a[alen-1] == 'c' || a[alen-1] == 'C')) mul = 1;
+            else if (alen > 0 && a[alen-1] == 'k') mul = 1024;
+            else if (alen > 0 && a[alen-1] == 'M') mul = 1024*1024;
+            else mul = 512; /* default: 512-byte blocks */
+            if (a[0] == '+') { preds[npreds].cmp_sign = 1; preds[npreds].n_arg = atoi(a + 1) * mul; }
+            else if (a[0] == '-') { preds[npreds].cmp_sign = -1; preds[npreds].n_arg = atoi(a + 1) * mul; }
+            else { preds[npreds].cmp_sign = 0; preds[npreds].n_arg = atoi(a) * mul; }
+            npreds++;
+        } else if (strcmp(argv[argi], "-perm") == 0 && argi + 1 < argc) {
+            preds[npreds].type = PRED_PERM;
+            preds[npreds].perm_val = (unsigned int)strtol(argv[++argi], NULL, 8);
+            npreds++;
+        } else if (strcmp(argv[argi], "-user") == 0 && argi + 1 < argc) {
+            preds[npreds].type = PRED_USER;
+            strncpy(preds[npreds].arg, argv[++argi], sizeof(preds[npreds].arg) - 1);
+            npreds++;
+        } else if (strcmp(argv[argi], "-maxdepth") == 0 && argi + 1 < argc) {
+            preds[npreds].type = PRED_MAXDEPTH;
+            preds[npreds].n_arg = atoi(argv[++argi]);
+            maxdepth = preds[npreds].n_arg;
+            npreds++;
+        } else if (strcmp(argv[argi], "-delete") == 0) {
+            preds[npreds].type = PRED_DELETE;
+            npreds++;
+        } else if (strcmp(argv[argi], "-print") == 0) {
+            preds[npreds].type = PRED_PRINT;
+            did_print = 1;
+            npreds++;
+        } else if (strcmp(argv[argi], "-exec") == 0 && argi + 1 < argc) {
+            preds[npreds].type = PRED_EXEC;
+            strncpy(preds[npreds].exec_cmd, argv[++argi], sizeof(preds[npreds].exec_cmd) - 1);
+            preds[npreds].exec_nargs = 0;
+            while (argi + 1 < argc && strcmp(argv[argi + 1], ";") != 0 &&
+                   preds[npreds].exec_nargs < 7) {
+                argi++;
+                strncpy(preds[npreds].exec_args[preds[npreds].exec_nargs],
+                        argv[argi], sizeof(preds[npreds].exec_args[0]) - 1);
+                preds[npreds].exec_nargs++;
+            }
+            if (argi + 1 < argc && strcmp(argv[argi + 1], ";") == 0) argi++;
+            npreds++;
+        } else if (strcmp(argv[argi], "!") == 0 && npreds > 0) {
+            preds[npreds - 1].negate = 1;
+        } else {
+            fprintf(stderr, "find: invalid predicate '%s'\n", argv[argi]);
+            return 1;
         }
         argi++;
     }
 
-    printf("%s\n", start);
-    find_recurse(start);
+    find_recurse(start, 0);
     return 0;
 }
index 87495eb22b46e586c7748d0d92943d565773c92e..1b56dc96232cf8a6f2711e6f39ad11a345fd0881 100644 (file)
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <regex.h>
+#include <ctype.h>
 
-static int match_simple(const char* text, const char* pat) {
-    /* Simple substring match (no regex) */
-    return strstr(text, pat) != NULL;
+static int use_regex = 1;  /* 1=BRE, 2=ERE, 0=fixed string */
+static int icase = 0;
+static int show_name = 0;
+static int invert = 0;
+static int count_only = 0;
+static int line_num = 0;
+static int list_files = 0;   /* -l: print files with match */
+static int quiet = 0;        /* -q: no output, just exit code */
+static int recursive = 0;    /* -r: recurse directories */
+
+static regex_t compiled_re;
+static int re_compiled = 0;
+static char fixed_pattern[256];
+
+static int match_line(const char* text) {
+    if (use_regex == 0) {
+        /* Fixed string match */
+        if (icase) {
+            const char* p = text;
+            size_t plen = strlen(fixed_pattern);
+            while (*p) {
+                int i;
+                for (i = 0; i < (int)plen && p[i]; i++) {
+                    char a = (char)toupper((unsigned char)p[i]);
+                    char b = (char)toupper((unsigned char)fixed_pattern[i]);
+                    if (a != b) break;
+                }
+                if (i == (int)plen) return 1;
+                p++;
+            }
+            return 0;
+        }
+        return strstr(text, fixed_pattern) != NULL;
+    }
+    /* Regex match */
+    int eflags = 0;
+    int rc = regexec(&compiled_re, text, 0, NULL, eflags);
+    return (rc == 0);
 }
 
-static int grep_fd(int fd, const char* pattern, const char* fname, int show_name, int invert, int count_only, int line_num) {
+static int grep_fd(int fd, const char* fname) {
     char buf[4096];
     int pos = 0, n, matches = 0, lnum = 0;
     while ((n = read(fd, buf + pos, (size_t)(sizeof(buf) - 1 - pos))) > 0) {
@@ -29,11 +68,12 @@ static int grep_fd(int fd, const char* pattern, const char* fname, int show_name
         while ((nl = strchr(start, '\n')) != NULL) {
             *nl = '\0';
             lnum++;
-            int m = match_simple(start, pattern);
+            int m = match_line(start);
             if (invert) m = !m;
             if (m) {
                 matches++;
-                if (!count_only) {
+                if (list_files) return 1;
+                if (!count_only && !quiet) {
                     if (show_name) printf("%s:", fname);
                     if (line_num) printf("%d:", lnum);
                     printf("%s\n", start);
@@ -48,41 +88,139 @@ static int grep_fd(int fd, const char* pattern, const char* fname, int show_name
     if (pos > 0) {
         buf[pos] = '\0';
         lnum++;
-        int m = match_simple(buf, pattern);
+        int m = match_line(buf);
         if (invert) m = !m;
         if (m) {
             matches++;
-            if (!count_only) {
+            if (list_files) return 1;
+            if (!count_only && !quiet) {
                 if (show_name) printf("%s:", fname);
                 if (line_num) printf("%d:", lnum);
                 printf("%s\n", buf);
             }
         }
     }
-    if (count_only) printf("%s%s%d\n", show_name ? fname : "", show_name ? ":" : "", matches);
+    if (list_files && matches > 0) {
+        printf("%s\n", fname);
+        return 0;
+    }
+    if (count_only && !quiet)
+        printf("%s%s%d\n", show_name ? fname : "", show_name ? ":" : "", matches);
     return matches > 0 ? 0 : 1;
 }
 
+static void grep_recursive(const char* path, const char* fname);
+
+static void grep_dir(const char* path, const char* prefix) {
+    int fd = open(path, O_RDONLY);
+    if (fd < 0) return;
+    char dbuf[2048];
+    int rc;
+    while ((rc = getdents(fd, dbuf, sizeof(dbuf))) > 0) {
+        int off = 0;
+        while (off < rc) {
+            struct dirent* d = (struct dirent*)(dbuf + off);
+            if (d->d_reclen == 0) break;
+            if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
+                (d->d_name[1] == '.' && d->d_name[2] == '\0'))) {
+                off += d->d_reclen;
+                continue;
+            }
+            char child[512];
+            size_t plen = strlen(path);
+            if (plen > 0 && path[plen - 1] == '/')
+                snprintf(child, sizeof(child), "%s%s", path, d->d_name);
+            else
+                snprintf(child, sizeof(child), "%s/%s", path, d->d_name);
+            grep_recursive(child, prefix);
+            off += d->d_reclen;
+        }
+    }
+    close(fd);
+}
+
+static void grep_recursive(const char* path, const char* prefix) {
+    struct stat st;
+    if (stat(path, &st) < 0) {
+        fprintf(stderr, "grep: %s: No such file or directory\n", path);
+        return;
+    }
+    if (S_ISDIR(st.st_mode)) {
+        grep_dir(path, prefix);
+    } else {
+        int fd = open(path, O_RDONLY);
+        if (fd < 0) {
+            fprintf(stderr, "grep: %s: No such file or directory\n", path);
+            return;
+        }
+        grep_fd(fd, path);
+        close(fd);
+    }
+}
+
 int main(int argc, char** argv) {
-    int invert = 0, count_only = 0, line_num = 0;
     int i = 1;
-    while (i < argc && argv[i][0] == '-') {
+    while (i < argc && argv[i][0] == '-' && argv[i][1]) {
+        if (strcmp(argv[i], "--") == 0) { i++; break; }
         for (int j = 1; argv[i][j]; j++) {
             if (argv[i][j] == 'v') invert = 1;
             else if (argv[i][j] == 'c') count_only = 1;
             else if (argv[i][j] == 'n') line_num = 1;
+            else if (argv[i][j] == 'i') icase = 1;
+            else if (argv[i][j] == 'l') list_files = 1;
+            else if (argv[i][j] == 'q') quiet = 1;
+            else if (argv[i][j] == 'r') recursive = 1;
+            else if (argv[i][j] == 'E') use_regex = 2;
+            else if (argv[i][j] == 'F') use_regex = 0;
+            else if (argv[i][j] == 'e' && j == 1) {
+                /* -e PATTERN */
+                if (argv[i][j+1]) { /* pattern follows in same arg */ }
+                /* handled below */
+            }
+            else {
+                fprintf(stderr, "grep: invalid option -- '%c'\n", argv[i][j]);
+                return 2;
+            }
         }
         i++;
     }
-    if (i >= argc) { fprintf(stderr, "usage: grep [-vcn] PATTERN [FILE...]\n"); return 2; }
+    if (i >= argc) { fprintf(stderr, "usage: grep [-vcnlqiErF] PATTERN [FILE...]\n"); return 2; }
     const char* pattern = argv[i++];
-    if (i >= argc) return grep_fd(STDIN_FILENO, pattern, "(stdin)", 0, invert, count_only, line_num);
-    int rc = 1, nfiles = argc - i;
+
+    /* Compile regex or set fixed pattern */
+    if (use_regex == 0) {
+        strncpy(fixed_pattern, pattern, sizeof(fixed_pattern) - 1);
+        fixed_pattern[sizeof(fixed_pattern) - 1] = '\0';
+    } else {
+        int cflags = (use_regex == 2) ? REG_EXTENDED : 0;
+        if (icase) cflags |= REG_ICASE;
+        if (regcomp(&compiled_re, pattern, cflags) != 0) {
+            fprintf(stderr, "grep: invalid regex: %s\n", pattern);
+            return 2;
+        }
+        re_compiled = 1;
+    }
+
+    if (i >= argc) {
+        int rc = grep_fd(STDIN_FILENO, "(stdin)");
+        if (re_compiled) regfree(&compiled_re);
+        return rc;
+    }
+
+    int nfiles = argc - i;
+    if (nfiles > 1 || recursive) show_name = 1;
+    int rc = 1;
     for (; i < argc; i++) {
-        int fd = open(argv[i], O_RDONLY);
-        if (fd < 0) { fprintf(stderr, "grep: %s: No such file or directory\n", argv[i]); continue; }
-        if (grep_fd(fd, pattern, argv[i], nfiles > 1, invert, count_only, line_num) == 0) rc = 0;
-        close(fd);
+        if (recursive) {
+            grep_recursive(argv[i], argv[i]);
+            rc = 0;  /* simplified */
+        } else {
+            int fd = open(argv[i], O_RDONLY);
+            if (fd < 0) { fprintf(stderr, "grep: %s: No such file or directory\n", argv[i]); continue; }
+            if (grep_fd(fd, argv[i]) == 0) rc = 0;
+            close(fd);
+        }
     }
+    if (re_compiled) regfree(&compiled_re);
     return rc;
 }
index 62eb150c39d8beffd32d5483d16a6a789eccef6a..38bba9c5eea93c010fc55ea428e5ade42fbffaa2 100644 (file)
@@ -50,6 +50,10 @@ struct inittab_entry {
 static struct inittab_entry entries[MAX_ENTRIES];
 static int nentries = 0;
 static int current_runlevel = 3;  /* Default: multi-user */
+static volatile int do_shutdown = 0;   /* 1=poweroff, 2=reboot */
+
+static void sigusr1_handler(int sig) { (void)sig; do_shutdown = 1; }
+static void sigusr2_handler(int sig) { (void)sig; do_shutdown = 2; }
 
 static enum action parse_action(const char* s) {
     if (strcmp(s, "sysinit") == 0) return ACT_SYSINIT;
@@ -270,6 +274,12 @@ int main(int argc, char** argv) {
     sigaction(SIGINT, &sa, NULL);
     sigaction(SIGTERM, &sa, NULL);
 
+    /* SIGUSR1 = poweroff, SIGUSR2 = reboot */
+    sa.sa_handler = (uintptr_t)sigusr1_handler;
+    sigaction(SIGUSR1, &sa, NULL);
+    sa.sa_handler = (uintptr_t)sigusr2_handler;
+    sigaction(SIGUSR2, &sa, NULL);
+
     printf("AdrOS init starting (PID %d)\n", getpid());
 
     /* Try to parse inittab */
@@ -296,6 +306,25 @@ int main(int argc, char** argv) {
 
     /* Main loop: reap children and respawn */
     while (1) {
+        if (do_shutdown) {
+            printf("init: shutting down (%s)\n",
+                   do_shutdown == 1 ? "poweroff" : "reboot");
+            run_action(ACT_SHUTDOWN, 1);
+            /* Kill all processes */
+            kill(-1, SIGTERM);
+            struct timespec ts = {2, 0};
+            nanosleep(&ts, NULL);
+            kill(-1, SIGKILL);
+            if (do_shutdown == 1) {
+                printf("init: System halted\n");
+                _exit(0);
+            } else {
+                printf("init: Rebooting...\n");
+                /* Attempt reboot via triple-fork or just exit */
+                _exit(0);
+            }
+        }
+
         int st;
         int pid = waitpid(-1, &st, 0);
 
index 34e1bdb8583b7cfa68403429dd1155a184529970..2274949057e56386d5c13606473331235db62f90 100644 (file)
@@ -24,6 +24,16 @@ int main(int argc, char** argv) {
 
     if (argv[1][0] == '-') {
         const char* s = argv[1] + 1;
+        if (strcmp(s, "l") == 0) {
+            /* List signal names */
+            printf(" 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP\n");
+            printf(" 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1\n");
+            printf("11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM\n");
+            printf("16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP\n");
+            printf("21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ\n");
+            printf("26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR\n");
+            return 0;
+        }
         if (strcmp(s, "9") == 0 || strcmp(s, "KILL") == 0) sig = 9;
         else if (strcmp(s, "15") == 0 || strcmp(s, "TERM") == 0) sig = 15;
         else if (strcmp(s, "2") == 0 || strcmp(s, "INT") == 0) sig = 2;
index 396efdf95007c8e365412f2a6696c542d3f2dbb1..6f261e26f2bd13544adc3fa5f909b579fd4eb906 100644 (file)
 #include <fcntl.h>
 #include <dirent.h>
 #include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
 
 static int aflag = 0;   /* -a: show hidden files */
 static int lflag = 0;   /* -l: long format */
+static int nflag = 0;   /* -n: numeric UID/GID */
 
 #define LS_MAX_ENTRIES 512
 
@@ -33,6 +37,20 @@ static int cmp_entry(const void* a, const void* b) {
                   ((const struct ls_entry*)b)->name);
 }
 
+static void uid_to_name(unsigned uid, char* buf, size_t bufsz) {
+    if (nflag) { snprintf(buf, bufsz, "%u", uid); return; }
+    struct passwd* pw = getpwuid((int)uid);
+    if (pw) { snprintf(buf, bufsz, "%s", pw->pw_name); }
+    else    { snprintf(buf, bufsz, "%u", uid); }
+}
+
+static void gid_to_name(unsigned gid, char* buf, size_t bufsz) {
+    if (nflag) { snprintf(buf, bufsz, "%u", gid); return; }
+    struct group* gr = getgrgid((int)gid);
+    if (gr) { snprintf(buf, bufsz, "%s", gr->gr_name); }
+    else    { snprintf(buf, bufsz, "%u", gid); }
+}
+
 static void ls_dir(const char* path) {
     int fd = open(path, O_RDONLY);
     if (fd < 0) {
@@ -105,8 +123,20 @@ static void ls_dir(const char* path) {
             unsigned long sz = have_stat ? (unsigned long)st.st_size : 0;
             unsigned nlink = have_stat ? (unsigned)st.st_nlink : 1;
 
-            printf("%c%s %2u root root %8lu %s\n",
-                   type, perms, nlink, sz, entries[i].name);
+            char owner[32], group[32];
+            uid_to_name(have_stat ? (unsigned)st.st_uid : 0, owner, sizeof(owner));
+            gid_to_name(have_stat ? (unsigned)st.st_gid : 0, group, sizeof(group));
+
+            char timebuf[32];
+            if (have_stat) {
+                time_t mtime = (time_t)st.st_mtime;
+                strftime(timebuf, sizeof(timebuf), "%b %d %H:%M", localtime(&mtime));
+            } else {
+                strcpy(timebuf, "?");
+            }
+
+            printf("%c%s %2u %-8s %-8s %8lu %s %s\n",
+                   type, perms, nlink, owner, group, sz, timebuf, entries[i].name);
         } else {
             printf("%s\n", entries[i].name);
         }
@@ -123,6 +153,7 @@ int main(int argc, char** argv) {
             while (*f) {
                 if (*f == 'a') aflag = 1;
                 else if (*f == 'l') lflag = 1;
+                else if (*f == 'n') nflag = 1;
                 else {
                     fprintf(stderr, "ls: invalid option -- '%c'\n", *f);
                     return 1;
index 16917a4f3b1b1b0779d62be7dc85f24a2c617932..8004ea219296b5290d4b668ff8ac5937e9a2728b 100644 (file)
@@ -12,6 +12,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
+#include <sys/stat.h>
 
 int main(int argc, char** argv) {
     if (argc < 3) {
@@ -30,7 +31,10 @@ int main(int argc, char** argv) {
         return 1;
     }
 
-    int dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    struct stat src_st;
+    int src_mode = 0644;
+    if (fstat(src, &src_st) == 0) src_mode = (int)src_st.st_mode;
+    int dst = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, (mode_t)src_mode);
     if (dst < 0) {
         fprintf(stderr, "mv: cannot create '%s'\n", argv[2]);
         close(src);
index af3f55e47f406522bc7910afb2f04146b6b640ef..5221dabccba1d669f7957e5bdf70011909ede362 100644 (file)
  * Source: https://github.com/tadryanom/AdrOS
  */
 
-/* AdrOS ps utility — list processes from /proc */
+/* AdrOS ps utility — process status */
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <pwd.h>
 #include <dirent.h>
 
-static int is_digit(char c) { return c >= '0' && c <= '9'; }
+static int eflag = 0;  /* -e: all processes (same as -A) */
+static int fflag = 0;  /* -f: full format */
+static int aflag = 0;  /* a: BSD all with tty */
+static int uflag = 0;  /* u: BSD user-oriented */
+static int xflag = 0;  /* x: BSD include no-tty */
 
-int main(void) {
-    printf("  PID CMD\n");
+int main(int argc, char** argv) {
+    for (int i = 1; i < argc; i++) {
+        if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "-A") == 0) eflag = 1;
+        else if (strcmp(argv[i], "-f") == 0) fflag = 1;
+        else if (strcmp(argv[i], "-ef") == 0 || strcmp(argv[i], "-fe") == 0) { eflag = 1; fflag = 1; }
+        else if (strcmp(argv[i], "aux") == 0 || strcmp(argv[i], "-aux") == 0) { aflag = 1; uflag = 1; xflag = 1; }
+        else if (strcmp(argv[i], "ax") == 0 || strcmp(argv[i], "-ax") == 0) { aflag = 1; xflag = 1; }
+        else if (strcmp(argv[i], "-u") == 0) uflag = 1;
+        else if (strcmp(argv[i], "--") == 0) break;
+    }
+
+    /* Read /proc */
     int fd = open("/proc", O_RDONLY);
     if (fd < 0) {
         fprintf(stderr, "ps: cannot open /proc\n");
         return 1;
     }
-    char buf[512];
+
+    /* Print header */
+    if (fflag) {
+        printf("%-8s %5s %5s %5s %-8s %-8s %5s %-6s %s\n",
+               "UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "STAT", "CMD");
+    } else if (uflag) {
+        printf("%-8s %5s %5s %5s %5s %-8s %5s %-6s %s\n",
+               "USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "STAT", "COMMAND");
+    } else {
+        printf("%5s %5s %-6s %-8s %s\n",
+               "PID", "PPID", "STAT", "TTY", "CMD");
+    }
+
+    char dbuf[2048];
     int rc;
-    while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
+    while ((rc = getdents(fd, dbuf, sizeof(dbuf))) > 0) {
         int off = 0;
         while (off < rc) {
-            struct dirent* d = (struct dirent*)(buf + off);
+            struct dirent* d = (struct dirent*)(dbuf + off);
             if (d->d_reclen == 0) break;
-            if (is_digit(d->d_name[0])) {
+
+            /* Only process numeric directories */
+            int is_pid = 1;
+            for (int i = 0; d->d_name[i]; i++) {
+                if (d->d_name[i] < '0' || d->d_name[i] > '9') { is_pid = 0; break; }
+            }
+            if (is_pid && d->d_name[0] >= '0' && d->d_name[0] <= '9') {
+                int pid = atoi(d->d_name);
+
+                /* Read /proc/<pid>/status */
                 char path[64];
-                snprintf(path, sizeof(path), "/proc/%s/cmdline", d->d_name);
-                int cfd = open(path, O_RDONLY);
-                char cmd[64] = "?";
-                if (cfd >= 0) {
-                    int n = read(cfd, cmd, sizeof(cmd) - 1);
-                    if (n > 0) {
-                        cmd[n] = '\0';
-                        while (n > 0 && (cmd[n-1] == '\n' || cmd[n-1] == '\0')) {
-                            cmd[--n] = '\0';
+                snprintf(path, sizeof(path), "/proc/%d/status", pid);
+                int sfd = open(path, O_RDONLY);
+                if (sfd >= 0) {
+                    char sbuf[512];
+                    int sn = read(sfd, sbuf, sizeof(sbuf) - 1);
+                    close(sfd);
+                    if (sn > 0) {
+                        sbuf[sn] = '\0';
+                        /* Parse: PID PPID STATE CMD ... */
+                        int ppid = 0, uid = 0;
+                        char state = '?';
+                        char cmd[128] = "";
+                        char tty[8] = "?";
+
+                        /* Simple parse: fields separated by spaces */
+                        char* sp = sbuf;
+                        int field = 0;
+                        while (*sp) {
+                            while (*sp == ' ') sp++;
+                            if (!*sp) break;
+                            char* start = sp;
+                            while (*sp && *sp != ' ' && *sp != '\n') sp++;
+                            char saved = *sp;
+                            *sp = '\0';
+                            switch (field) {
+                            case 0: /* PID — already have it */ break;
+                            case 1: ppid = atoi(start); break;
+                            case 2: state = start[0]; break;
+                            case 3: strncpy(cmd, start, sizeof(cmd) - 1); break;
+                            case 4: uid = atoi(start); break;
+                            case 5: strncpy(tty, start, sizeof(tty) - 1); break;
+                            }
+                            *sp = saved;
+                            field++;
+                            if (saved == '\n') break;
+                        }
+
+                        /* Resolve uid */
+                        char uname[32];
+                        struct passwd* pw = getpwuid(uid);
+                        if (pw) strncpy(uname, pw->pw_name, sizeof(uname) - 1);
+                        else snprintf(uname, sizeof(uname), "%d", uid);
+
+                        if (fflag) {
+                            printf("%-8s %5d %5d %5d %-8s %-8s %5s %-6c %s\n",
+                                   uname, pid, ppid, 0, "00:00", tty, "00:00", state, cmd);
+                        } else if (uflag) {
+                            printf("%-8s %5d %5s %5s %5s %-8s %5s %-6c %s\n",
+                                   uname, pid, "0.0", "0.0", "0", "0", tty, state, cmd);
+                        } else {
+                            printf("%5d %5d %-6c %-8s %s\n",
+                                   pid, ppid, state, tty, cmd);
                         }
                     }
-                    if (n <= 0) strcpy(cmd, "[kernel]");
-                    close(cfd);
                 }
-                printf("%5s %s\n", d->d_name, cmd);
             }
             off += d->d_reclen;
         }
index d99348a82172c028af2e86293041e30494d35ce6..135ce5d5c4d7bf2465e21ea0ba8f8ffc8f87d0e5 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
 
 static int rflag = 0;   /* -r: recursive */
 static int fflag = 0;   /* -f: force (no errors) */
 static int dflag = 0;   /* -d: remove empty directories */
 
+static int rm_recursive(const char* path) {
+    struct stat st;
+    if (stat(path, &st) < 0) {
+        if (!fflag) fprintf(stderr, "rm: cannot stat '%s'\n", path);
+        return -1;
+    }
+    if (!S_ISDIR(st.st_mode)) {
+        int r = unlink(path);
+        if (r < 0 && !fflag) fprintf(stderr, "rm: cannot remove '%s'\n", path);
+        return r;
+    }
+    /* Directory: remove contents first */
+    int fd = open(path, O_RDONLY);
+    if (fd < 0) {
+        if (!fflag) fprintf(stderr, "rm: cannot open directory '%s'\n", path);
+        return -1;
+    }
+    char dbuf[2048];
+    int rc;
+    while ((rc = getdents(fd, dbuf, sizeof(dbuf))) > 0) {
+        int off = 0;
+        while (off < rc) {
+            struct dirent* d = (struct dirent*)(dbuf + off);
+            if (d->d_reclen == 0) break;
+            if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
+                (d->d_name[1] == '.' && d->d_name[2] == '\0'))) {
+                off += d->d_reclen;
+                continue;
+            }
+            char child[512];
+            size_t plen = strlen(path);
+            if (plen > 0 && path[plen - 1] == '/')
+                snprintf(child, sizeof(child), "%s%s", path, d->d_name);
+            else
+                snprintf(child, sizeof(child), "%s/%s", path, d->d_name);
+            rm_recursive(child);
+            off += d->d_reclen;
+        }
+    }
+    close(fd);
+    int r = rmdir(path);
+    if (r < 0 && !fflag) fprintf(stderr, "rm: cannot remove directory '%s'\n", path);
+    return r;
+}
+
 int main(int argc, char** argv) {
     if (argc <= 1) {
         fprintf(stderr, "rm: missing operand\n");
@@ -44,13 +92,17 @@ int main(int argc, char** argv) {
 
     int rc = 0;
     for (int i = start; i < argc; i++) {
-        int r = unlink(argv[i]);
-        if (r < 0 && (rflag || dflag)) {
-            r = rmdir(argv[i]);
-        }
-        if (r < 0 && !fflag) {
-            fprintf(stderr, "rm: cannot remove '%s'\n", argv[i]);
-            rc = 1;
+        if (rflag) {
+            if (rm_recursive(argv[i]) < 0) rc = 1;
+        } else {
+            int r = unlink(argv[i]);
+            if (r < 0 && dflag) {
+                r = rmdir(argv[i]);
+            }
+            if (r < 0 && !fflag) {
+                fprintf(stderr, "rm: cannot remove '%s'\n", argv[i]);
+                rc = 1;
+            }
         }
     }
     return rc;
index e15f1b25b40d298bf63134133f89e9eb327cd6b2..9e0622791480f2e960d39cb753b7dea6bf880504 100644 (file)
  * Source: https://github.com/tadryanom/AdrOS
  */
 
-/* AdrOS sed utility — minimal stream editor (s/pattern/replacement/g only) */
+/* AdrOS sed utility — stream editor with address support and multiple commands */
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <regex.h>
 
-static int match_at(const char* s, const char* pat, int patlen) {
-    for (int i = 0; i < patlen; i++) {
-        if (s[i] == '\0' || s[i] != pat[i]) return 0;
+#define MAX_CMDS 64
+#define MAX_TEXT 256
+
+enum cmd_type { CMD_S, CMD_D, CMD_P, CMD_Q, CMD_A, CMD_I, CMD_C, CMD_Y };
+
+struct addr {
+    int type;       /* 0=none, 1=line, 2=last line ($), 3=regex */
+    int line;
+    regex_t re;
+    int has_re;
+};
+
+struct sed_cmd {
+    struct addr addr1;
+    struct addr addr2;  /* second address (range) */
+    int has_addr2;
+    enum cmd_type cmd;
+    /* s command */
+    char s_pat[256];
+    char s_rep[256];
+    int  s_global;
+    regex_t s_re;
+    int  s_has_re;
+    /* a/i/c text */
+    char text[MAX_TEXT];
+    /* y command */
+    char y_src[256];
+    char y_dst[256];
+};
+
+static struct sed_cmd cmds[MAX_CMDS];
+static int ncmds = 0;
+static int nflag = 0;  /* -n: suppress auto-print */
+
+static int parse_addr(const char** pp, struct addr* a) {
+    a->type = 0;
+    a->has_re = 0;
+    const char* p = *pp;
+    if (*p == '$') { a->type = 2; p++; *pp = p; return 0; }
+    if (*p == '/') {
+        p++;
+        char pat[256]; int pi = 0;
+        while (*p && *p != '/' && pi < 255) pat[pi++] = *p++;
+        pat[pi] = '\0';
+        if (*p == '/') p++;
+        if (regcomp(&a->re, pat, 0) != 0) return -1;
+        a->has_re = 1;
+        a->type = 3;
+        *pp = p;
+        return 0;
     }
-    return 1;
+    if (*p >= '0' && *p <= '9') {
+        int n = 0;
+        while (*p >= '0' && *p <= '9') n = n * 10 + (*p++ - '0');
+        a->type = 1;
+        a->line = n;
+        *pp = p;
+        return 0;
+    }
+    *pp = p;
+    return 0;
 }
 
-static void sed_substitute(const char* line, const char* pat, int patlen,
-                           const char* rep, int replen, int global) {
-    const char* p = line;
-    while (*p) {
-        if (match_at(p, pat, patlen)) {
-            write(STDOUT_FILENO, rep, replen);
-            p += patlen;
-            if (!global) {
-                write(STDOUT_FILENO, p, strlen(p));
-                return;
-            }
-        } else {
-            write(STDOUT_FILENO, p, 1);
+static int parse_cmd(const char* expr, struct sed_cmd* c) {
+    memset(c, 0, sizeof(*c));
+    const char* p = expr;
+    while (*p == ' ' || *p == '\t') p++;
+    /* Parse first address */
+    if (*p && *p != 's' && *p != 'd' && *p != 'p' && *p != 'q' &&
+        *p != 'a' && *p != 'i' && *p != 'c' && *p != 'y') {
+        if (parse_addr(&p, &c->addr1) < 0) return -1;
+        while (*p == ' ' || *p == '\t') p++;
+        if (*p == ',') {
             p++;
+            while (*p == ' ' || *p == '\t') p++;
+            if (parse_addr(&p, &c->addr2) < 0) return -1;
+            c->has_addr2 = 1;
+            while (*p == ' ' || *p == '\t') p++;
         }
     }
+    if (!*p) return -1;
+    switch (*p) {
+    case 's': {
+        c->cmd = CMD_S;
+        p++;
+        if (!*p) return -1;
+        char delim = *p++;
+        int pi = 0;
+        while (*p && *p != delim && pi < 255) c->s_pat[pi++] = *p++;
+        c->s_pat[pi] = '\0';
+        if (*p != delim) return -1;
+        p++;
+        int ri = 0;
+        while (*p && *p != delim && ri < 255) c->s_rep[ri++] = *p++;
+        c->s_rep[ri] = '\0';
+        if (*p == delim) { p++; if (*p == 'g') c->s_global = 1; }
+        if (regcomp(&c->s_re, c->s_pat, 0) != 0) return -1;
+        c->s_has_re = 1;
+        break;
+    }
+    case 'd': c->cmd = CMD_D; p++; break;
+    case 'p': c->cmd = CMD_P; p++; break;
+    case 'q': c->cmd = CMD_Q; p++; break;
+    case 'a':
+        c->cmd = CMD_A; p++;
+        while (*p == ' ' || *p == '\t' || *p == '\\') p++;
+        strncpy(c->text, p, MAX_TEXT - 1);
+        c->text[MAX_TEXT - 1] = '\0';
+        break;
+    case 'i':
+        c->cmd = CMD_I; p++;
+        while (*p == ' ' || *p == '\t' || *p == '\\') p++;
+        strncpy(c->text, p, MAX_TEXT - 1);
+        c->text[MAX_TEXT - 1] = '\0';
+        break;
+    case 'c':
+        c->cmd = CMD_C; p++;
+        while (*p == ' ' || *p == '\t' || *p == '\\') p++;
+        strncpy(c->text, p, MAX_TEXT - 1);
+        c->text[MAX_TEXT - 1] = '\0';
+        break;
+    case 'y': {
+        c->cmd = CMD_Y; p++;
+        if (!*p) return -1;
+        char delim = *p++;
+        int si = 0;
+        while (*p && *p != delim && si < 255) c->y_src[si++] = *p++;
+        c->y_src[si] = '\0';
+        if (*p != delim) return -1;
+        p++;
+        int di = 0;
+        while (*p && *p != delim && di < 255) c->y_dst[di++] = *p++;
+        c->y_dst[di] = '\0';
+        break;
+    }
+    default: return -1;
+    }
+    return 0;
 }
 
-static int parse_s_cmd(const char* expr, char* pat, int* patlen,
-                       char* rep, int* replen, int* global) {
-    if (expr[0] != 's' || expr[1] == '\0') return -1;
-    char delim = expr[1];
-    const char* p = expr + 2;
-    int pi = 0;
-    while (*p && *p != delim && pi < 255) pat[pi++] = *p++;
-    pat[pi] = '\0'; *patlen = pi;
-    if (*p != delim) return -1;
-    p++;
-    int ri = 0;
-    while (*p && *p != delim && ri < 255) rep[ri++] = *p++;
-    rep[ri] = '\0'; *replen = ri;
-    *global = 0;
-    if (*p == delim) { p++; if (*p == 'g') *global = 1; }
+static int addr_match(struct addr* a, int lnum, int lastline, const char* line) {
+    if (a->type == 0) return 1;
+    if (a->type == 1) return (lnum == a->line);
+    if (a->type == 2) return lastline;
+    if (a->type == 3 && a->has_re)
+        return (regexec(&a->re, line, 0, NULL, 0) == 0);
     return 0;
 }
 
-int main(int argc, char** argv) {
-    if (argc < 2) {
-        fprintf(stderr, "Usage: sed 's/pattern/replacement/[g]' [file]\n");
+static int cmd_match(struct sed_cmd* c, int lnum, int lastline, const char* line) {
+    if (c->addr1.type == 0) return 1;
+    int m1 = addr_match(&c->addr1, lnum, lastline, line);
+    if (!c->has_addr2) return m1;
+    /* Range: match from addr1 to addr2 */
+    static int in_range[MAX_CMDS];
+    if (m1 && !in_range[c - cmds]) { in_range[c - cmds] = 1; return 1; }
+    if (in_range[c - cmds]) {
+        int m2 = addr_match(&c->addr2, lnum, lastline, line);
+        if (m2) { in_range[c - cmds] = 0; return 1; }
         return 1;
     }
+    return 0;
+}
 
-    char pat[256], rep[256];
-    int patlen, replen, global;
-    int ei = 1;
-    if (strcmp(argv[1], "-e") == 0 && argc > 2) ei = 2;
+static void do_s(struct sed_cmd* c, char* line) {
+    regmatch_t match;
+    if (regexec(&c->s_re, line, 1, &match, 0) != 0) return;
+    char out[4096];
+    int oi = 0;
+    int again = 1;
+    int offset = 0;
+    while (again) {
+        again = 0;
+        if (regexec(&c->s_re, line + offset, 1, &match, 0) != 0) break;
+        for (int i = offset; i < offset + match.rm_so && oi < 4095; i++)
+            out[oi++] = line[i];
+        for (int i = 0; c->s_rep[i] && oi < 4095; i++)
+            out[oi++] = c->s_rep[i];
+        offset += match.rm_eo;
+        if (c->s_global) again = 1;
+    }
+    for (int i = offset; line[i] && oi < 4095; i++)
+        out[oi++] = line[i];
+    out[oi] = '\0';
+    strcpy(line, out);
+}
 
-    if (parse_s_cmd(argv[ei], pat, &patlen, rep, &replen, &global) < 0) {
-        fprintf(stderr, "sed: invalid expression: %s\n", argv[ei]);
+static void do_y(struct sed_cmd* c, char* line) {
+    int slen = (int)strlen(c->y_src);
+    for (int i = 0; line[i]; i++) {
+        for (int j = 0; j < slen; j++) {
+            if (line[i] == c->y_src[j]) {
+                line[i] = c->y_dst[j];
+                break;
+            }
+        }
+    }
+}
+
+int main(int argc, char** argv) {
+    int ei = 1;
+    while (ei < argc && argv[ei][0] == '-') {
+        if (strcmp(argv[ei], "-n") == 0) nflag = 1;
+        else if (strcmp(argv[ei], "-e") == 0) { ei++; break; }
+        else if (strcmp(argv[ei], "--") == 0) { ei++; break; }
+        else { fprintf(stderr, "sed: invalid option '%s'\n", argv[ei]); return 1; }
+        ei++;
+    }
+    if (ei >= argc) {
+        fprintf(stderr, "Usage: sed [-n] [-e] 'command' [file]\n");
         return 1;
     }
 
+    /* Parse commands (semicolon-separated) */
+    const char* expr = argv[ei++];
+    char ebuf[1024];
+    strncpy(ebuf, expr, sizeof(ebuf) - 1);
+    ebuf[sizeof(ebuf) - 1] = '\0';
+    char* tok = ebuf;
+    while (tok && *tok) {
+        char* semi = strchr(tok, ';');
+        if (semi) *semi++ = '\0';
+        while (*tok == ' ' || *tok == '\t') tok++;
+        if (*tok && ncmds < MAX_CMDS) {
+            if (parse_cmd(tok, &cmds[ncmds]) < 0) {
+                fprintf(stderr, "sed: invalid command: %s\n", tok);
+                return 1;
+            }
+            ncmds++;
+        }
+        tok = semi;
+    }
+
     int fd = STDIN_FILENO;
-    if (argc > ei + 1) {
-        fd = open(argv[ei + 1], O_RDONLY);
+    if (ei < argc) {
+        fd = open(argv[ei], O_RDONLY);
         if (fd < 0) {
-            fprintf(stderr, "sed: %s: No such file or directory\n", argv[ei + 1]);
+            fprintf(stderr, "sed: %s: No such file or directory\n", argv[ei]);
             return 1;
         }
     }
 
     char line[4096];
-    int li = 0;
+    int li = 0, lnum = 0;
     char c;
     while (read(fd, &c, 1) == 1) {
         if (c == '\n') {
             line[li] = '\0';
-            sed_substitute(line, pat, patlen, rep, replen, global);
-            write(STDOUT_FILENO, "\n", 1);
+            lnum++;
+            int deleted = 0;
+            int quit = 0;
+            for (int i = 0; i < ncmds; i++) {
+                if (!cmd_match(&cmds[i], lnum, 0, line)) continue;
+                switch (cmds[i].cmd) {
+                case CMD_S: do_s(&cmds[i], line); break;
+                case CMD_D: deleted = 1; break;
+                case CMD_P: printf("%s\n", line); break;
+                case CMD_Q: quit = 1; break;
+                case CMD_A: printf("%s\n", cmds[i].text); break;
+                case CMD_I: printf("%s\n", cmds[i].text); break;
+                case CMD_C: printf("%s\n", cmds[i].text); deleted = 1; break;
+                case CMD_Y: do_y(&cmds[i], line); break;
+                }
+                if (deleted || quit) break;
+            }
+            if (!deleted && !nflag) printf("%s\n", line);
+            if (quit) goto done;
             li = 0;
         } else if (li < (int)sizeof(line) - 1) {
             line[li++] = c;
@@ -96,9 +288,30 @@ int main(int argc, char** argv) {
     }
     if (li > 0) {
         line[li] = '\0';
-        sed_substitute(line, pat, patlen, rep, replen, global);
+        lnum++;
+        int deleted = 0;
+        for (int i = 0; i < ncmds; i++) {
+            if (!cmd_match(&cmds[i], lnum, 1, line)) continue;
+            switch (cmds[i].cmd) {
+            case CMD_S: do_s(&cmds[i], line); break;
+            case CMD_D: deleted = 1; break;
+            case CMD_P: printf("%s\n", line); break;
+            case CMD_A: printf("%s\n", cmds[i].text); break;
+            case CMD_I: printf("%s\n", cmds[i].text); break;
+            case CMD_C: printf("%s\n", cmds[i].text); deleted = 1; break;
+            case CMD_Y: do_y(&cmds[i], line); break;
+            default: break;
+            }
+            if (deleted) break;
+        }
+        if (!deleted && !nflag) printf("%s\n", line);
+    }
+done:
+    for (int i = 0; i < ncmds; i++) {
+        if (cmds[i].addr1.has_re) regfree(&cmds[i].addr1.re);
+        if (cmds[i].has_addr2 && cmds[i].addr2.has_re) regfree(&cmds[i].addr2.re);
+        if (cmds[i].s_has_re) regfree(&cmds[i].s_re);
     }
-
     if (fd != STDIN_FILENO) close(fd);
     return 0;
 }
index c7443e3e01aed16637a83f3452b6e43afa9e92e3..3c60632242509560d50c0f25fd96843c8343c1df 100644 (file)
@@ -32,6 +32,7 @@
 #include <signal.h>
 #include <sys/ioctl.h>
 #include <sys/wait.h>
+#include <pwd.h>
 
 static struct termios orig_termios;
 
@@ -79,14 +80,16 @@ static void var_set(const char* name, const char* value, int exported) {
             return;
         }
     }
-    if (nvar < MAX_VARS) {
-        strncpy(vars[nvar].name, name, 63);
-        vars[nvar].name[63] = '\0';
-        strncpy(vars[nvar].value, value, 255);
-        vars[nvar].value[255] = '\0';
-        vars[nvar].exported = exported;
-        nvar++;
+    if (nvar >= MAX_VARS) {
+        fprintf(stderr, "sh: too many variables\n");
+        return;
     }
+    strncpy(vars[nvar].name, name, 63);
+    vars[nvar].name[63] = '\0';
+    strncpy(vars[nvar].value, value, 255);
+    vars[nvar].value[255] = '\0';
+    vars[nvar].exported = exported;
+    nvar++;
 }
 
 static void var_unset(const char* name) {
@@ -452,11 +455,62 @@ static int read_line_edit(void) {
 static void expand_vars(const char* src, char* dst, int maxlen) {
     int di = 0;
     while (*src && di < maxlen - 1) {
-        if (*src == '$') {
+        if (*src == '$' && *(src + 1) == '(') {
+            /* Command substitution $(cmd...) */
+            src += 2;
+            const char* start = src;
+            int depth = 1;
+            while (*src && depth > 0) {
+                if (*src == '(') depth++;
+                else if (*src == ')') { depth--; if (depth == 0) break; }
+                src++;
+            }
+            int cmdlen = (int)(src - start);
+            if (*src == ')') src++;
+            /* Execute command and capture output */
+            if (cmdlen > 0 && cmdlen < 256) {
+                char cmd[258];
+                cmd[0] = '(';  /* wrap in subshell */
+                memcpy(cmd + 1, start, (size_t)cmdlen);
+                cmd[1 + cmdlen] = '\0';
+                int pfd[2];
+                if (pipe(pfd) == 0) {
+                    int pid = fork();
+                    if (pid == 0) {
+                        close(pfd[0]);
+                        dup2(pfd[1], STDOUT_FILENO);
+                        close(pfd[1]);
+                        /* Execute via sh -c */
+                        char* sh_argv[] = { "/bin/sh", "-c", cmd, NULL };
+                        char** envp = build_envp();
+                        execve("/bin/sh", sh_argv, envp);
+                        _exit(127);
+                    }
+                    close(pfd[1]);
+                    char buf[512];
+                    int n;
+                    while ((n = read(pfd[0], buf, sizeof(buf))) > 0) {
+                        for (int i = 0; i < n && di < maxlen - 1; i++) {
+                            if (buf[i] == '\n') continue;  /* strip trailing newlines */
+                            dst[di++] = buf[i];
+                        }
+                    }
+                    close(pfd[0]);
+                    int st;
+                    waitpid(pid, &st, 0);
+                }
+            }
+        } else if (*src == '$') {
             src++;
             if (*src == '?') {
                 di += snprintf(dst + di, (size_t)(maxlen - di), "%d", last_status);
                 src++;
+            } else if (*src == '$') {
+                di += snprintf(dst + di, (size_t)(maxlen - di), "%d", getpid());
+                src++;
+            } else if (*src == '!') {
+                di += snprintf(dst + di, (size_t)(maxlen - di), "%d", 0);
+                src++;
             } else if (*src == '{') {
                 src++;
                 char name[64];
@@ -491,6 +545,47 @@ static void expand_vars(const char* src, char* dst, int maxlen) {
     dst[di] = '\0';
 }
 
+/* Tilde expansion: ~ -> $HOME, ~user -> user's home */
+static void expand_tilde(char* src, char* dst, int maxlen) {
+    int di = 0;
+    char* p = src;
+    /* Only expand tilde at the start of a word */
+    int word_start = 1;
+    while (*p && di < maxlen - 1) {
+        if (*p == '~' && word_start) {
+            p++;
+            if (*p == '/' || *p == '\0' || *p == ' ' || *p == '\t') {
+                /* ~ alone = $HOME */
+                const char* home = var_get("HOME");
+                if (!home) home = "/";
+                int hl = (int)strlen(home);
+                if (di + hl < maxlen) { memcpy(dst + di, home, (size_t)hl); di += hl; }
+            } else {
+                /* ~user = lookup user's home */
+                char uname[64];
+                int ui = 0;
+                while (*p && *p != '/' && *p != ' ' && *p != '\t' && ui < 63)
+                    uname[ui++] = *p++;
+                uname[ui] = '\0';
+                struct passwd* pw = getpwnam(uname);
+                if (pw) {
+                    int hl = (int)strlen(pw->pw_dir);
+                    if (di + hl < maxlen) { memcpy(dst + di, pw->pw_dir, (size_t)hl); di += hl; }
+                } else {
+                    /* Unknown user, keep ~user literal */
+                    dst[di++] = '~';
+                    if (di + ui < maxlen) { memcpy(dst + di, uname, (size_t)ui); di += ui; }
+                }
+            }
+        } else {
+            dst[di++] = *p;
+            word_start = (*p == ' ' || *p == '\t' || *p == '=');
+            p++;
+        }
+    }
+    dst[di] = '\0';
+}
+
 /* ---- Argument parsing with quote handling ---- */
 
 static int parse_args(char* cmd, char** argv, int max) {
@@ -564,9 +659,12 @@ static void run_simple(char* cmd) {
     /* Expand variables */
     char expanded[LINE_MAX];
     expand_vars(cmd, expanded, LINE_MAX);
+    /* Expand tildes */
+    char texpanded[LINE_MAX];
+    expand_tilde(expanded, texpanded, LINE_MAX);
 
     char* argv[MAX_ARGS];
-    int argc = parse_args(expanded, argv, MAX_ARGS);
+    int argc = parse_args(texpanded, argv, MAX_ARGS);
     if (argc == 0) return;
 
     /* Check for variable assignment (no command, just VAR=value) */
index fdd33fff48e944ebc08201d2f32482a2ed14ce24..923e4c9a22f5f933fdfe0e335af209659e11939a 100644 (file)
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+
+static void format_time(time_t t, char* buf, size_t bufsz) {
+    strftime(buf, bufsz, "%Y-%m-%d %H:%M:%S", localtime(&t));
+}
 
 int main(int argc, char** argv) {
     if (argc <= 1) {
@@ -26,9 +33,39 @@ int main(int argc, char** argv) {
             rc = 1;
             continue;
         }
+
+        /* File type */
+        const char* type = "unknown";
+        if (S_ISREG(st.st_mode)) type = "regular file";
+        else if (S_ISDIR(st.st_mode)) type = "directory";
+        else if (S_ISCHR(st.st_mode)) type = "character device";
+        else if (S_ISBLK(st.st_mode)) type = "block device";
+        else if (S_ISFIFO(st.st_mode)) type = "fifo";
+        else if (S_ISLNK(st.st_mode)) type = "symbolic link";
+
+        /* UID/GID names */
+        char owner[32], group[32];
+        struct passwd* pw = getpwuid(st.st_uid);
+        if (pw) snprintf(owner, sizeof(owner), "%s (%u)", pw->pw_name, (unsigned)st.st_uid);
+        else snprintf(owner, sizeof(owner), "(%u)", (unsigned)st.st_uid);
+        struct group* gr = getgrgid(st.st_gid);
+        if (gr) snprintf(group, sizeof(group), "%s (%u)", gr->gr_name, (unsigned)st.st_gid);
+        else snprintf(group, sizeof(group), "(%u)", (unsigned)st.st_gid);
+
+        /* Format times */
+        char mtime_str[32], atime_str[32], ctime_str[32];
+        format_time((time_t)st.st_mtime, mtime_str, sizeof(mtime_str));
+        format_time((time_t)st.st_atime, atime_str, sizeof(atime_str));
+        format_time((time_t)st.st_ctime, ctime_str, sizeof(ctime_str));
+
         printf("  File: %s\n", argv[i]);
-        printf("  Size: %u\tInode: %u\n", (unsigned)st.st_size, (unsigned)st.st_ino);
-        printf("  Mode: %o\tUid: %u\tGid: %u\n", (unsigned)st.st_mode, (unsigned)st.st_uid, (unsigned)st.st_gid);
+        printf("  Size: %u\t\tBlocks: %u\t\t%s\n", (unsigned)st.st_size, (unsigned)st.st_blocks, type);
+        printf("  Inode: %u\t\tLinks: %u\n", (unsigned)st.st_ino, (unsigned)st.st_nlink);
+        printf("  Access: (%04o/%s)\tUid: %s\tGid: %s\n",
+               (unsigned)(st.st_mode & 07777), type, owner, group);
+        printf("  Access: %s\n", atime_str);
+        printf("  Modify: %s\n", mtime_str);
+        printf("  Change: %s\n", ctime_str);
     }
     return rc;
 }
index 17a94584f23d36557e42e59ca260984bc4977036..36744daf2d400bca0ad14fbe042e4b633cfabfa9 100644 (file)
  * Source: https://github.com/tadryanom/AdrOS
  */
 
-/* AdrOS top utility — one-shot process listing with basic info */
+/* AdrOS top utility — one-shot process listing with system summary */
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <dirent.h>
+#include <time.h>
 
 static int is_digit(char c) { return c >= '0' && c <= '9'; }
 
-int main(void) {
-    printf("  PID  STATE CMD\n");
-    int fd = open("/proc", O_RDONLY);
-    if (fd < 0) {
-        fprintf(stderr, "top: cannot open /proc\n");
-        return 1;
+int main(int argc, char** argv) {
+    int n_iter = 1;  /* default: one-shot */
+    for (int i = 1; i < argc; i++) {
+        if (strcmp(argv[i], "-n") == 0 && i + 1 < argc) { n_iter = atoi(argv[++i]); }
+        else if (strcmp(argv[i], "--") == 0) break;
     }
-    char buf[512];
-    int rc;
-    while ((rc = getdents(fd, buf, sizeof(buf))) > 0) {
-        int off = 0;
-        while (off < rc) {
-            struct dirent* d = (struct dirent*)(buf + off);
-            if (d->d_reclen == 0) break;
-            if (is_digit(d->d_name[0])) {
-                char path[64];
 
-                /* Read cmdline */
-                snprintf(path, sizeof(path), "/proc/%s/cmdline", d->d_name);
-                int cfd = open(path, O_RDONLY);
-                char cmd[64] = "[kernel]";
-                if (cfd >= 0) {
-                    int n = read(cfd, cmd, sizeof(cmd) - 1);
-                    if (n > 0) {
-                        cmd[n] = '\0';
-                        while (n > 0 && (cmd[n-1] == '\n' || cmd[n-1] == '\0')) cmd[--n] = '\0';
+    for (int iter = 0; iter < n_iter; iter++) {
+        if (iter > 0) sleep(2);
+
+        /* Print system summary header */
+        time_t now = time((time_t*)0);
+        char timebuf[32];
+        strftime(timebuf, sizeof(timebuf), "%H:%M:%S", localtime(&now));
+
+        /* Count processes */
+        int nproc = 0, nrunning = 0, nsleeping = 0;
+        int pfd = open("/proc", O_RDONLY);
+        if (pfd >= 0) {
+            char dbuf[2048];
+            int rc;
+            while ((rc = getdents(pfd, dbuf, sizeof(dbuf))) > 0) {
+                int off = 0;
+                while (off < rc) {
+                    struct dirent* d = (struct dirent*)(dbuf + off);
+                    if (d->d_reclen == 0) break;
+                    if (is_digit(d->d_name[0])) {
+                        nproc++;
+                        char spath[64];
+                        snprintf(spath, sizeof(spath), "/proc/%s/status", d->d_name);
+                        int sfd = open(spath, O_RDONLY);
+                        if (sfd >= 0) {
+                            char sbuf[256];
+                            int sn = read(sfd, sbuf, sizeof(sbuf) - 1);
+                            close(sfd);
+                            if (sn > 0) {
+                                sbuf[sn] = '\0';
+                                /* Parse state field: "PID PPID STATE ..." */
+                                char* sp = sbuf;
+                                int field = 0;
+                                while (*sp) {
+                                    while (*sp == ' ') sp++;
+                                    if (!*sp) break;
+                                    char* start = sp;
+                                    while (*sp && *sp != ' ' && *sp != '\n') sp++;
+                                    if (field == 2) {
+                                        char st = start[0];
+                                        if (st == 'R') nrunning++;
+                                        else nsleeping++;
+                                    }
+                                    field++;
+                                    if (*sp == '\n') break;
+                                }
+                            }
+                        }
                     }
-                    if (n <= 0) strcpy(cmd, "[kernel]");
-                    close(cfd);
+                    off += d->d_reclen;
                 }
+            }
+            close(pfd);
+        }
 
-                /* Read status for state */
-                snprintf(path, sizeof(path), "/proc/%s/status", d->d_name);
-                int sfd = open(path, O_RDONLY);
-                char state[16] = "?";
-                if (sfd >= 0) {
-                    char sbuf[256];
-                    int sn = read(sfd, sbuf, sizeof(sbuf) - 1);
-                    if (sn > 0) {
-                        sbuf[sn] = '\0';
-                        char* st = strstr(sbuf, "State:");
-                        if (st) {
-                            st += 6;
-                            while (*st == ' ' || *st == '\t') st++;
-                            int si = 0;
-                            while (*st && *st != '\n' && si < 15) state[si++] = *st++;
-                            state[si] = '\0';
+        printf("top - %s up ?,  %d users,  load average: 0.00\n", timebuf, 1);
+        printf("Tasks: %d total, %d running, %d sleeping\n", nproc, nrunning, nsleeping);
+        printf("%%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni, 100.0 id\n");
+        printf("MiB Mem:  0.0 total, 0.0 free, 0.0 used\n");
+        printf("\n");
+        printf("%-8s %5s %5s %-6s %-8s %5s %s\n",
+               "USER", "PID", "PPID", "STAT", "TTY", "TIME", "CMD");
+
+        /* List processes */
+        pfd = open("/proc", O_RDONLY);
+        if (pfd < 0) {
+            fprintf(stderr, "top: cannot open /proc\n");
+            return 1;
+        }
+        char buf[2048];
+        int rc;
+        while ((rc = getdents(pfd, buf, sizeof(buf))) > 0) {
+            int off = 0;
+            while (off < rc) {
+                struct dirent* d = (struct dirent*)(buf + off);
+                if (d->d_reclen == 0) break;
+                if (is_digit(d->d_name[0])) {
+                    char path[64];
+                    int pid = atoi(d->d_name);
+
+                    /* Read status */
+                    snprintf(path, sizeof(path), "/proc/%s/status", d->d_name);
+                    int sfd = open(path, O_RDONLY);
+                    if (sfd >= 0) {
+                        char sbuf[512];
+                        int sn = read(sfd, sbuf, sizeof(sbuf) - 1);
+                        close(sfd);
+                        if (sn > 0) {
+                            sbuf[sn] = '\0';
+                            int ppid = 0, uid = 0;
+                            char state = '?';
+                            char cmd[128] = "";
+                            char tty[8] = "?";
+                            char* sp = sbuf;
+                            int field = 0;
+                            while (*sp) {
+                                while (*sp == ' ') sp++;
+                                if (!*sp) break;
+                                char* start = sp;
+                                while (*sp && *sp != ' ' && *sp != '\n') sp++;
+                                char saved = *sp;
+                                *sp = '\0';
+                                switch (field) {
+                                case 1: ppid = atoi(start); break;
+                                case 2: state = start[0]; break;
+                                case 3: strncpy(cmd, start, sizeof(cmd) - 1); break;
+                                case 4: uid = atoi(start); break;
+                                case 5: strncpy(tty, start, sizeof(tty) - 1); break;
+                                }
+                                *sp = saved;
+                                field++;
+                                if (saved == '\n') break;
+                            }
+                            char uname[16] = "root";
+                            if (uid != 0) snprintf(uname, sizeof(uname), "%d", uid);
+                            printf("%-8s %5d %5d %-6c %-8s %5s %s\n",
+                                   uname, pid, ppid, state, tty, "0:00", cmd);
                         }
                     }
-                    close(sfd);
                 }
-
-                printf("%5s %6s %s\n", d->d_name, state, cmd);
+                off += d->d_reclen;
             }
-            off += d->d_reclen;
         }
+        close(pfd);
+
+        if (iter < n_iter - 1) printf("\n");
     }
-    close(fd);
     return 0;
 }
diff --git a/user/ulibc/include/sys/statvfs.h b/user/ulibc/include/sys/statvfs.h
new file mode 100644 (file)
index 0000000..38d65c0
--- /dev/null
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2018, Tulio A M Mendes <[email protected]>
+ * All rights reserved.
+ * See LICENSE for details.
+ *
+ * Source: https://github.com/tadryanom/AdrOS
+ */
+
+#ifndef ULIBC_SYS_STATVFS_H
+#define ULIBC_SYS_STATVFS_H
+
+#include <sys/types.h>
+
+struct statvfs {
+    unsigned long f_bsize;    /* Filesystem block size */
+    unsigned long f_frsize;   /* Fragment size */
+    unsigned long f_blocks;   /* Size of fs in f_frsize units */
+    unsigned long f_bfree;    /* Number of free blocks */
+    unsigned long f_bavail;   /* Free blocks for unprivileged users */
+    unsigned long f_files;    /* Number of inodes */
+    unsigned long f_ffree;    /* Number of free inodes */
+    unsigned long f_favail;   /* Free inodes for unprivileged users */
+    unsigned long f_fsid;     /* Filesystem ID */
+    unsigned long f_flag;     /* Mount flags */
+    unsigned long f_namemax;  /* Maximum filename length */
+};
+
+int statvfs(const char* path, struct statvfs* buf);
+
+#endif
index 1d2c60ea9a94fbb13ba1359adf83d6b0f65530fc..63ff1f34f35addb5a7f06a31be58792880f07711 100644 (file)
 #define ULIBC_TIME_H
 
 #include <stdint.h>
+#include <stddef.h>
+#include <sys/types.h>  /* for time_t */
 
 struct timespec {
-    uint32_t tv_sec;
-    uint32_t tv_nsec;
+    time_t tv_sec;
+    long   tv_nsec;
+};
+
+struct tm {
+    int tm_sec;    /* seconds [0,60] */
+    int tm_min;    /* minutes [0,59] */
+    int tm_hour;   /* hours [0,23] */
+    int tm_mday;   /* day of month [1,31] */
+    int tm_mon;    /* month [0,11] */
+    int tm_year;   /* years since 1900 */
+    int tm_wday;   /* day of week [0,6] (Sun=0) */
+    int tm_yday;   /* day of year [0,365] */
+    int tm_isdst;  /* daylight savings flag */
 };
 
 #define CLOCK_REALTIME  0
@@ -23,4 +37,12 @@ struct timespec {
 int nanosleep(const struct timespec* req, struct timespec* rem);
 int clock_gettime(int clk_id, struct timespec* tp);
 
+time_t     time(time_t* tloc);
+struct tm* localtime(const time_t* timep);
+struct tm* gmtime(const time_t* timep);
+char*      ctime(const time_t* timep);
+size_t     strftime(char* s, size_t max, const char* fmt, const struct tm* tm);
+char*      asctime(const struct tm* tm);
+double     difftime(time_t time1, time_t time0);
+
 #endif
diff --git a/user/ulibc/src/statvfs.c b/user/ulibc/src/statvfs.c
new file mode 100644 (file)
index 0000000..4630c33
--- /dev/null
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2018, Tulio A M Mendes <[email protected]>
+ * All rights reserved.
+ * See LICENSE for details.
+ *
+ * Source: https://github.com/tadryanom/AdrOS
+ */
+
+#include "sys/statvfs.h"
+#include "sys/stat.h"
+#include "syscall.h"
+#include "errno.h"
+#include "unistd.h"
+
+int statvfs(const char* path, struct statvfs* buf) {
+    struct stat st;
+    if (stat(path, &st) < 0)
+        return -1;
+
+    /* Build a pseudo statvfs from stat info.
+     * AdrOS has no f_bsize/f_blocks syscall, so we estimate from st_blksize. */
+    unsigned long bsize = (unsigned long)st.st_blksize;
+    if (bsize == 0) bsize = 512;
+
+    buf->f_bsize   = bsize;
+    buf->f_frsize  = bsize;
+    /* For initrd/overlayfs, report the file size as "used" and assume
+     * some free space.  This is a best-effort approximation. */
+    buf->f_blocks  = (unsigned long)st.st_size / bsize + 256;
+    buf->f_bfree   = 128;
+    buf->f_bavail  = 128;
+    buf->f_files   = 128;
+    buf->f_ffree   = 64;
+    buf->f_favail  = 64;
+    buf->f_fsid    = (unsigned long)st.st_dev;
+    buf->f_flag    = 0;
+    buf->f_namemax = 255;
+
+    /* Try to read actual values from /proc/mounts if available */
+    int fd = open("/proc/mounts", 0 /* O_RDONLY */);
+    if (fd >= 0) {
+        char mbuf[1024];
+        int n = read(fd, mbuf, sizeof(mbuf) - 1);
+        close(fd);
+        if (n > 0) {
+            mbuf[n] = '\0';
+            /* Look for the mount point in the output */
+            int plen = 0;
+            const char* p = path;
+            while (*p++) plen++;
+            /* Parse lines: dev dir type ... */
+            char* line = mbuf;
+            while (line && *line) {
+                char* nl = line;
+                while (*nl && *nl != '\n') nl++;
+                int end = (*nl == '\n');
+                if (end) *nl = '\0';
+                /* Check if this line's mount point matches path */
+                char* sp1 = line;
+                while (*sp1 && *sp1 != ' ') sp1++;
+                if (*sp1) sp1++;
+                /* sp1 now points to mount dir */
+                int match = 1;
+                const char* mp = sp1;
+                const char* pp = path;
+                while (*mp != ' ' && *mp != '\0' && *pp) {
+                    if (*mp != *pp) { match = 0; break; }
+                    mp++; pp++;
+                }
+                if (match && *mp == ' ' && *pp == '\0') {
+                    /* Found matching mount — use default estimates */
+                    break;
+                }
+                line = end ? nl + 1 : nl;
+            }
+        }
+    }
+
+    return 0;
+}
index 7f9f19b4983468f69959887ff0d334c927df42bc..d919a71b325c1e0b30c0639ddb0a5d88d557cee6 100644 (file)
 #include "sys/time.h"
 #include "syscall.h"
 #include "errno.h"
+#include "string.h"
+#include "stdio.h"
+
+static int _is_leap(int y) {
+    return ((y % 4) == 0 && (y % 100) != 0) || ((y % 400) == 0);
+}
+
+static int _days_in_month(int m, int y) {
+    static const int d[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
+    if (m == 1 && _is_leap(y)) return 29;
+    return d[m];
+}
+
+static struct tm _tm_buf;
+
+time_t time(time_t* tloc) {
+    struct timeval tv;
+    if (gettimeofday(&tv, (void*)0) < 0) {
+        if (tloc) *tloc = 0;
+        return 0;
+    }
+    if (tloc) *tloc = (time_t)tv.tv_sec;
+    return (time_t)tv.tv_sec;
+}
+
+struct tm* gmtime(const time_t* timep) {
+    if (!timep) return (struct tm*)0;
+    time_t t = *timep;
+    int days = (int)(t / 86400);
+    int secs = (int)(t % 86400);
+    if (secs < 0) { secs += 86400; days--; }
+
+    _tm_buf.tm_sec  = secs % 60;
+    _tm_buf.tm_min  = (secs / 60) % 60;
+    _tm_buf.tm_hour = secs / 3600;
+    _tm_buf.tm_wday = (days + 4) % 7;  /* Jan 1 1970 = Thursday */
+    if (_tm_buf.tm_wday < 0) _tm_buf.tm_wday += 7;
+
+    /* Compute year */
+    int y = 1970;
+    for (;;) {
+        int dy = _is_leap(y) ? 366 : 365;
+        if (days < dy) break;
+        days -= dy;
+        y++;
+    }
+    _tm_buf.tm_year = y - 1900;
+    _tm_buf.tm_yday = days;
+
+    /* Compute month */
+    int m = 0;
+    for (;;) {
+        int dm = _days_in_month(m, y);
+        if (days < dm) break;
+        days -= dm;
+        m++;
+    }
+    _tm_buf.tm_mon  = m;
+    _tm_buf.tm_mday = days + 1;
+    _tm_buf.tm_isdst = 0;
+    return &_tm_buf;
+}
+
+struct tm* localtime(const time_t* timep) {
+    /* AdrOS has no timezone support, same as gmtime */
+    return gmtime(timep);
+}
+
+char* asctime(const struct tm* tm) {
+    static char buf[26];
+    static const char* wday[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+    static const char* mon[] = {"Jan","Feb","Mar","Apr","May","Jun",
+                                "Jul","Aug","Sep","Oct","Nov","Dec"};
+    if (!tm) return (char*)0;
+    snprintf(buf, sizeof(buf), "%.3s %.3s%3d %02d:%02d:%02d %d\n",
+             wday[tm->tm_wday >= 0 && tm->tm_wday < 7 ? tm->tm_wday : 0],
+             mon[tm->tm_mon >= 0 && tm->tm_mon < 12 ? tm->tm_mon : 0],
+             tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
+             1900 + tm->tm_year);
+    return buf;
+}
+
+char* ctime(const time_t* timep) {
+    return asctime(localtime(timep));
+}
+
+double difftime(time_t time1, time_t time0) {
+    return (double)time1 - (double)time0;
+}
+
+size_t strftime(char* s, size_t max, const char* fmt, const struct tm* tm) {
+    if (!s || !fmt || !tm || max == 0) return 0;
+    static const char* wday[] = {"Sunday","Monday","Tuesday","Wednesday",
+                                  "Thursday","Friday","Saturday"};
+    static const char* wday_s[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
+    static const char* mon[] = {"January","February","March","April","May","June",
+                                "July","August","September","October","November","December"};
+    static const char* mon_s[] = {"Jan","Feb","Mar","Apr","May","Jun",
+                                  "Jul","Aug","Sep","Oct","Nov","Dec"};
+    size_t pos = 0;
+#define PUTC(c) do { if (pos + 1 < max) s[pos] = (c); pos++; } while(0)
+#define PUTS(str) do { const char* _p = (str); while (*_p) { PUTC(*_p); _p++; } } while(0)
+
+    while (*fmt && pos < max) {
+        if (*fmt != '%') { PUTC(*fmt); fmt++; continue; }
+        fmt++;
+        char tmp[16];
+        switch (*fmt) {
+        case 'Y':
+            snprintf(tmp, sizeof(tmp), "%d", 1900 + tm->tm_year);
+            PUTS(tmp);
+            break;
+        case 'm':
+            snprintf(tmp, sizeof(tmp), "%02d", tm->tm_mon + 1);
+            PUTS(tmp);
+            break;
+        case 'd':
+            snprintf(tmp, sizeof(tmp), "%02d", tm->tm_mday);
+            PUTS(tmp);
+            break;
+        case 'H':
+            snprintf(tmp, sizeof(tmp), "%02d", tm->tm_hour);
+            PUTS(tmp);
+            break;
+        case 'M':
+            snprintf(tmp, sizeof(tmp), "%02d", tm->tm_min);
+            PUTS(tmp);
+            break;
+        case 'S':
+            snprintf(tmp, sizeof(tmp), "%02d", tm->tm_sec);
+            PUTS(tmp);
+            break;
+        case 'b': PUTS(mon_s[tm->tm_mon >= 0 && tm->tm_mon < 12 ? tm->tm_mon : 0]); break;
+        case 'B': PUTS(mon[tm->tm_mon >= 0 && tm->tm_mon < 12 ? tm->tm_mon : 0]); break;
+        case 'a': PUTS(wday_s[tm->tm_wday >= 0 && tm->tm_wday < 7 ? tm->tm_wday : 0]); break;
+        case 'A': PUTS(wday[tm->tm_wday >= 0 && tm->tm_wday < 7 ? tm->tm_wday : 0]); break;
+        case 'e':
+            snprintf(tmp, sizeof(tmp), "%2d", tm->tm_mday);
+            PUTS(tmp);
+            break;
+        case 'j':
+            snprintf(tmp, sizeof(tmp), "%03d", tm->tm_yday + 1);
+            PUTS(tmp);
+            break;
+        case 'w':
+            PUTC('0' + tm->tm_wday);
+            break;
+        case 'I':
+            snprintf(tmp, sizeof(tmp), "%02d",
+                     tm->tm_hour == 0 ? 12 : (tm->tm_hour > 12 ? tm->tm_hour - 12 : tm->tm_hour));
+            PUTS(tmp);
+            break;
+        case 'p':
+            PUTS(tm->tm_hour < 12 ? "AM" : "PM");
+            break;
+        case 'c':
+            /* Date+time: Thu Aug 23 14:55:02 2001 */
+            PUTS(wday_s[tm->tm_wday >= 0 && tm->tm_wday < 7 ? tm->tm_wday : 0]);
+            PUTC(' ');
+            PUTS(mon_s[tm->tm_mon >= 0 && tm->tm_mon < 12 ? tm->tm_mon : 0]);
+            PUTC(' ');
+            snprintf(tmp, sizeof(tmp), "%2d", tm->tm_mday);
+            PUTS(tmp);
+            PUTC(' ');
+            snprintf(tmp, sizeof(tmp), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
+            PUTS(tmp);
+            PUTC(' ');
+            snprintf(tmp, sizeof(tmp), "%d", 1900 + tm->tm_year);
+            PUTS(tmp);
+            break;
+        case 'F':
+            snprintf(tmp, sizeof(tmp), "%d-%02d-%02d",
+                     1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday);
+            PUTS(tmp);
+            break;
+        case 'T':
+            snprintf(tmp, sizeof(tmp), "%02d:%02d:%02d",
+                     tm->tm_hour, tm->tm_min, tm->tm_sec);
+            PUTS(tmp);
+            break;
+        case 'R':
+            snprintf(tmp, sizeof(tmp), "%02d:%02d", tm->tm_hour, tm->tm_min);
+            PUTS(tmp);
+            break;
+        case 'D':
+            snprintf(tmp, sizeof(tmp), "%02d/%02d/%02d",
+                     tm->tm_mon + 1, tm->tm_mday, (1900 + tm->tm_year) % 100);
+            PUTS(tmp);
+            break;
+        case '%':
+            PUTC('%');
+            break;
+        default:
+            PUTC('%');
+            if (*fmt) PUTC(*fmt);
+            break;
+        }
+        if (*fmt) fmt++;
+    }
+    if (pos < max) s[pos] = '\0';
+    else if (max > 0) s[max - 1] = '\0';
+    return pos;
+#undef PUTC
+#undef PUTS
+}
 
 int nanosleep(const struct timespec* req, struct timespec* rem) {
     return __syscall_ret(_syscall2(SYS_NANOSLEEP, (int)req, (int)rem));