* 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;
}
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;
}
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;
}
* 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;
}
/* 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) {
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);
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;
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++;
/* 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;
}
* Source: https://github.com/tadryanom/AdrOS
*/
-/* AdrOS find utility — search for files in directory hierarchy */
+/* AdrOS find utility — search for files in a 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;
}
#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) {
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);
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;
}
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;
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 */
/* 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);
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;
#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
((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) {
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);
}
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;
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
+#include <sys/stat.h>
int main(int argc, char** argv) {
if (argc < 3) {
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);
* 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;
}
#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");
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;
* 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;
}
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;
}
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
+#include <pwd.h>
static struct termios orig_termios;
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) {
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];
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) {
/* 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) */
#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) {
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;
}
* 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;
}
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * 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
#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
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
--- /dev/null
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * 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;
+}
#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));