This commit is contained in:
alexvoste
2026-05-07 02:22:25 +03:00
commit 1a9fd27a31
226 changed files with 29188 additions and 0 deletions
+179
View File
@@ -0,0 +1,179 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/cervus.h>
#include <cervus_util.h>
#define A_INVERT "\x1b[7m"
#define A_BOLD "\x1b[1m"
#define A_RESET "\x1b[0m"
static const char *MNAME[12] = {
"January","February","March","April","May","June",
"July","August","September","October","November","December"
};
static int MDAYS[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
static int is_leap(int y)
{
return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
}
static int days_in_month(int y, int m)
{
if (m == 2 && is_leap(y)) return 29;
return MDAYS[m - 1];
}
static int first_dow(int y, int m)
{
int64_t days = 0;
for (int yr = 1970; yr < y; yr++) days += is_leap(yr) ? 366 : 365;
int md[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
md[1] = is_leap(y) ? 29 : 28;
for (int mo = 0; mo < m - 1; mo++) days += md[mo];
return (int)((days + 4) % 7);
}
static void get_today(int *out_y, int *out_m, int *out_d)
{
*out_y = 0; *out_m = 0; *out_d = 0;
cervus_timespec_t ts;
if (cervus_clock_gettime(CLOCK_REALTIME, &ts) != 0 || ts.tv_sec <= 0)
return;
int64_t t = ts.tv_sec;
int64_t days = t / 86400;
int y = 1970;
while (1) {
int dy = is_leap(y) ? 366 : 365;
if (days < dy) break;
days -= dy;
y++;
}
int md[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
md[1] = is_leap(y) ? 29 : 28;
int mo = 0;
while (mo < 12) { if (days < md[mo]) break; days -= md[mo]; mo++; }
*out_y = y;
*out_m = mo + 1;
*out_d = (int)(days + 1);
}
static void print_header(int y, int m, int today_y, int today_m)
{
char title[64];
snprintf(title, sizeof(title), "%s %d", MNAME[m - 1], y);
int tlen = (int)strlen(title);
int pad = (20 - tlen) / 2;
for (int i = 0; i < pad; i++) putchar(' ');
if (y == today_y && m == today_m) {
fputs(A_INVERT A_BOLD, stdout);
fputs(title, stdout);
fputs(A_RESET, stdout);
} else {
fputs(A_BOLD, stdout);
fputs(title, stdout);
fputs(A_RESET, stdout);
}
putchar('\n');
fputs(C_GRAY " Su Mo Tu We Th Fr Sa" A_RESET "\n", stdout);
}
static void print_month(int y, int m, int today_y, int today_m, int today_d)
{
print_header(y, m, today_y, today_m);
int mdays = days_in_month(y, m);
int dow = first_dow(y, m);
for (int i = 0; i < dow; i++) fputs(" ", stdout);
for (int d = 1; d <= mdays; d++) {
int is_today = (y == today_y && m == today_m && d == today_d);
if (is_today) {
fprintf(stdout, " " A_INVERT A_BOLD "%2d" A_RESET, d);
} else {
fprintf(stdout, " %2d", d);
}
if (++dow == 7) {
putchar('\n');
dow = 0;
}
}
if (dow != 0) putchar('\n');
}
static void print_help(void)
{
fputs(
"Usage: cal [OPTION] [[MONTH] YEAR]\n"
"Display a calendar.\n"
"\n"
" (no args) current month\n"
" YEAR all 12 months of YEAR\n"
" MONTH YEAR specific month (MONTH = 1-12)\n"
" --help display this help and exit\n"
"\n"
"Today's day is highlighted with " A_INVERT "inverted colours" A_RESET ".\n"
"The current month header is also " A_INVERT "inverted" A_RESET ".\n",
stdout
);
}
int main(int argc, char **argv)
{
int today_y, today_m, today_d;
get_today(&today_y, &today_m, &today_d);
int year = today_y ? today_y : 2025;
int month = today_m ? today_m : 1;
const char *args[2] = {NULL, NULL};
int real_argc = 0;
int flag_help = 0;
for (int i = 1; i < argc; i++) {
if (is_shell_flag(argv[i])) continue;
if (strcmp(argv[i], "--help") == 0) { flag_help = 1; continue; }
if (real_argc < 2) args[real_argc] = argv[i];
real_argc++;
}
if (flag_help) { print_help(); return 0; }
if (real_argc == 2) {
month = atoi(args[0]);
year = atoi(args[1]);
} else if (real_argc == 1) {
year = atoi(args[0]);
putchar('\n');
fprintf(stdout, " " A_BOLD "%d" A_RESET "\n\n", year);
for (int m = 1; m <= 12; m++) {
print_month(year, m, today_y, today_m, today_d);
putchar('\n');
}
return 0;
}
if (month < 1 || month > 12) {
fputs("cal: invalid month (must be 1-12)\n", stderr);
return 1;
}
putchar('\n');
print_month(year, month, today_y, today_m, today_d);
putchar('\n');
return 0;
}
+206
View File
@@ -0,0 +1,206 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <cervus_util.h>
#define SCALE 1000000LL
typedef long long fx;
static const char *p;
static void skip_ws(void) { while (*p && isspace((unsigned char)*p)) p++; }
static fx parse_number(int *err)
{
skip_ws();
long long intpart = 0, frac = 0, fscale = 1;
int had_digit = 0;
while (isdigit((unsigned char)*p)) {
intpart = intpart * 10 + (*p - '0');
had_digit = 1; p++;
}
if (*p == '.') {
p++;
while (isdigit((unsigned char)*p)) {
if (fscale < SCALE) { frac = frac * 10 + (*p - '0'); fscale *= 10; }
had_digit = 1; p++;
}
}
if (!had_digit) { *err = 1; return 0; }
return intpart * SCALE + (frac * SCALE) / fscale;
}
static fx parse_expr(int *err);
static fx parse_primary(int *err)
{
skip_ws();
if (*p == '(') {
p++;
fx v = parse_expr(err);
skip_ws();
if (*p == ')') p++;
else *err = 1;
return v;
}
if (*p == '-') { p++; return -parse_primary(err); }
if (*p == '+') { p++; return parse_primary(err); }
return parse_number(err);
}
static fx parse_term(int *err)
{
fx v = parse_primary(err);
while (!*err) {
skip_ws();
if (*p == '*') {
p++;
fx r = parse_primary(err);
v = (v * r) / SCALE;
} else if (*p == '/') {
p++;
fx r = parse_primary(err);
if (r == 0) { *err = 2; return 0; }
v = (v * SCALE) / r;
} else break;
}
return v;
}
static fx parse_expr(int *err)
{
fx v = parse_term(err);
while (!*err) {
skip_ws();
if (*p == '+') { p++; v += parse_term(err); }
else if (*p == '-') { p++; v -= parse_term(err); }
else break;
}
return v;
}
static void print_fx(fx v)
{
if (v < 0) { putchar('-'); v = -v; }
long long ip = v / SCALE;
long long fp = v % SCALE;
char ibuf[32];
int ilen = 0;
if (ip == 0) {
ibuf[ilen++] = '0';
} else {
long long tmp = ip;
while (tmp > 0) { ibuf[ilen++] = '0' + (int)(tmp % 10); tmp /= 10; }
for (int a = 0, b = ilen - 1; a < b; a++, b--) {
char t = ibuf[a]; ibuf[a] = ibuf[b]; ibuf[b] = t;
}
}
ibuf[ilen] = '\0';
fputs(ibuf, stdout);
if (fp != 0) {
char fbuf[8];
int flen = 0;
long long tmp = fp;
long long scale = SCALE;
fbuf[flen++] = '.';
while (scale > 1) {
scale /= 10;
fbuf[flen++] = '0' + (int)(tmp / scale);
tmp %= scale;
}
fbuf[flen] = '\0';
while (flen > 1 && fbuf[flen - 1] == '0') { fbuf[--flen] = '\0'; }
fputs(fbuf, stdout);
}
putchar('\n');
}
static void calc_help(void)
{
putchar('\n');
fputs(C_CYAN "Cervus calc" C_RESET " - fixed-point calculator (6 decimal digits)\n", stdout);
fputs(C_GRAY "-------------------------------------------" C_RESET "\n", stdout);
fputs(C_BOLD "Operators:" C_RESET " + - * / ( )\n", stdout);
fputs(C_GRAY "-------------------------------------------" C_RESET "\n", stdout);
fputs(C_BOLD "Examples:" C_RESET "\n", stdout);
fputs(" calc> " C_YELLOW "3.14 + 5" C_RESET " = 8.14\n", stdout);
fputs(" calc> " C_YELLOW "10 / 3" C_RESET " = 3.333333\n", stdout);
fputs(" calc> " C_YELLOW "2 * (3 + 4)" C_RESET " = 14\n", stdout);
fputs(" calc> " C_YELLOW "(1.5 + 2.5) * 4" C_RESET " = 16\n", stdout);
fputs(" calc> " C_YELLOW "100 / 7" C_RESET " = 14.285714\n", stdout);
fputs(" calc> " C_YELLOW "-5 + 3" C_RESET " = -2\n", stdout);
fputs(C_GRAY "-------------------------------------------" C_RESET "\n", stdout);
fputs(" Type " C_BOLD "q" C_RESET " or " C_BOLD "exit" C_RESET " to quit.\n", stdout);
putchar('\n');
}
static int readline_calc(char *buf, int maxlen)
{
int i = 0;
for (;;) {
char c;
ssize_t r = read(0, &c, 1);
if (r <= 0) {
buf[i] = '\0';
return (i > 0) ? i : -1;
}
if (c == '\r') continue;
if (c == '\n') {
write(1, "\n", 1);
buf[i] = '\0';
return i;
}
if (c == '\b' || c == 0x7F) {
if (i > 0) {
i--;
write(1, "\b \b", 3);
}
continue;
}
if (c == 0x03) {
write(1, "^C\n", 3);
buf[0] = '\0';
return 0;
}
if (c >= 0x20 && c < 0x7F && i < maxlen - 1) {
buf[i++] = c;
write(1, &c, 1);
}
}
}
int main(int argc, char **argv)
{
(void)argc; (void)argv;
fputs(C_CYAN "Cervus calc" C_RESET " - fixed-point (6 digits). "
"Type " C_BOLD "help" C_RESET " for examples, " C_BOLD "q" C_RESET " to exit.\n", stdout);
char line[256];
for (;;) {
fputs("calc> ", stdout);
int n = readline_calc(line, sizeof(line));
if (n < 0) { putchar('\n'); break; }
while (n > 0 && isspace((unsigned char)line[n - 1])) line[--n] = '\0';
if (n == 0) continue;
if (strcmp(line, "q") == 0 || strcmp(line, "quit") == 0 ||
strcmp(line, "exit") == 0) break;
if (strcmp(line, "help") == 0) { calc_help(); continue; }
p = line;
int err = 0;
fx v = parse_expr(&err);
skip_ws();
if (*p != '\0') err = 1;
if (err == 1) fputs(C_RED " parse error\n" C_RESET, stdout);
else if (err == 2) fputs(C_RED " division by zero\n" C_RESET, stdout);
else { fputs(" = ", stdout); print_fx(v); }
}
putchar('\n');
return 0;
}
+98
View File
@@ -0,0 +1,98 @@
#include <stdio.h>
#include <stdint.h>
#include <sys/cervus.h>
static const int MDAYS[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
static const char *MNAME[12] = {
"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"
};
static const char *WDAY[7] = {"Thu","Fri","Sat","Sun","Mon","Tue","Wed"};
static int is_leap(int y) { return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); }
static int bcd2bin(int v) { return (v & 0x0F) + ((v >> 4) * 10); }
static int cmos_read(int reg)
{
if (cervus_ioport_write(0x70, 1, (uint32_t)(reg & 0x7F)) < 0) return -1;
return (int)(cervus_ioport_read(0x71, 1) & 0xFF);
}
static void cmos_wait_ready(void)
{
for (int i = 0; i < 2000; i++) {
cervus_ioport_write(0x70, 1, 0x0A);
uint32_t sta = cervus_ioport_read(0x71, 1);
if (!(sta & 0x80)) return;
}
}
static int64_t rtc_read_unix(void)
{
cmos_wait_ready();
int sec = cmos_read(0x00);
int min = cmos_read(0x02);
int hour = cmos_read(0x04);
int mday = cmos_read(0x07);
int mon = cmos_read(0x08);
int year = cmos_read(0x09);
if (sec < 0 || min < 0 || hour < 0 || mday < 0 || mon < 0 || year < 0) return 0;
cervus_ioport_write(0x70, 1, 0x0B);
int regb = (int)cervus_ioport_read(0x71, 1);
int binary_mode = (regb >= 0) && (regb & 0x04);
int hour24 = (regb >= 0) && (regb & 0x02);
if (!binary_mode) {
sec = bcd2bin(sec);
min = bcd2bin(min);
mday = bcd2bin(mday);
mon = bcd2bin(mon);
year = bcd2bin(year);
if (!hour24 && (hour & 0x80)) hour = bcd2bin(hour & 0x7F) + 12;
else hour = bcd2bin(hour);
}
year += (year < 70) ? 2000 : 1900;
if (sec < 0 || sec > 59 || min < 0 || min > 59) return 0;
if (hour < 0 || hour > 23) return 0;
if (mday < 1 || mday > 31) return 0;
if (mon < 1 || mon > 12) return 0;
if (year < 2000) return 0;
int64_t days = 0;
for (int y = 1970; y < year; y++) days += is_leap(y) ? 366 : 365;
for (int m = 1; m < mon; m++) days += MDAYS[m-1] + (m == 2 && is_leap(year) ? 1 : 0);
days += mday - 1;
return days * 86400LL + (int64_t)hour * 3600LL + (int64_t)min * 60LL + (int64_t)sec;
}
int main(int argc, char **argv)
{
(void)argc; (void)argv;
int64_t t = rtc_read_unix();
if (t <= 0) {
uint64_t up = cervus_uptime_ns() / 1000000000ULL;
fputs(" RTC not available.\n", stdout);
printf(" Uptime: %lus\n", (unsigned long)up);
return 0;
}
int wday = (int)((t / 86400 + 4) % 7);
int64_t days = t / 86400;
int64_t rem = t % 86400;
if (rem < 0) { rem += 86400; days--; }
int hour = (int)(rem / 3600);
int min = (int)((rem % 3600) / 60);
int sec = (int)(rem % 60);
int year = 1970;
while (1) { int dy = is_leap(year) ? 366 : 365; if (days < dy) break; days -= dy; year++; }
int mon = 0;
while (mon < 12) {
int dm = MDAYS[mon] + (mon == 1 && is_leap(year) ? 1 : 0);
if (days < dm) break;
days -= dm; mon++;
}
int mday = (int)days + 1;
printf(" %s %s %2d %02d:%02d:%02d UTC %04d\n",
WDAY[wday], MNAME[mon], mday, hour, min, sec, year);
return 0;
}
+15
View File
@@ -0,0 +1,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
puts("=== execve_target: STARTED ===");
printf(" PID: %d\n", (int)getpid());
printf(" PPID: %d\n", (int)getppid());
printf(" argc: %d\n", argc);
for (int i = 0; i < argc; i++)
printf(" argv[%d] = %s\n", i, argv[i]);
puts("=== execve_target: DONE, exit(99) ===");
return 99;
}
+93
View File
@@ -0,0 +1,93 @@
#include <stdio.h>
#include <stdint.h>
#include <sys/cervus.h>
#include <cervus_util.h>
static const char *logo[] = {
" L ",
" 'k.i , ",
" \";\"+U., ",
" \\_' -. ",
" .f ,_.;.",
" I ,f ",
" ' ",
NULL
};
static void cpuid_leaf(uint32_t leaf, uint32_t *a, uint32_t *b,
uint32_t *c, uint32_t *d)
{
asm volatile ("cpuid"
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
: "0"(leaf), "2"(0));
}
static void print_uptime(void)
{
uint64_t ns = cervus_uptime_ns();
uint64_t total_s = ns / 1000000000ULL;
uint64_t ms = (ns / 1000000ULL) % 1000ULL;
uint64_t secs = total_s % 60;
uint64_t mins = (total_s / 60) % 60;
uint64_t hours = (total_s / 3600) % 24;
uint64_t days = total_s / 86400;
fputs("uptime: ", stdout);
if (days > 0) printf("%lud, ", (unsigned long)days);
printf("%02lu:%02lu:%02lu (%lus %lums)",
(unsigned long)hours, (unsigned long)mins, (unsigned long)secs,
(unsigned long)total_s, (unsigned long)ms);
}
static void print_cpu(void)
{
uint32_t a, b, c, d;
cpuid_leaf(0x80000000, &a, &b, &c, &d);
if (a >= 0x80000004) {
char brand[49];
uint32_t *p = (uint32_t *)brand;
cpuid_leaf(0x80000002, &p[0], &p[1], &p[2], &p[3]);
cpuid_leaf(0x80000003, &p[4], &p[5], &p[6], &p[7]);
cpuid_leaf(0x80000004, &p[8], &p[9], &p[10], &p[11]);
brand[48] = '\0';
const char *br = brand;
while (*br == ' ') br++;
printf("cpu: %s", br);
}
}
static void print_mem(void)
{
cervus_meminfo_t mi;
if (cervus_meminfo(&mi) != 0) return;
uint64_t used = mi.used_bytes;
uint64_t total = mi.total_bytes;
const uint64_t MiB = 1024ULL * 1024;
const uint64_t GiB = 1024ULL * 1024 * 1024;
fputs("mem: ", stdout);
if (total >= GiB)
printf("%lu.%02lu / %lu.%02lu GiB",
(unsigned long)(used / GiB), (unsigned long)((used % GiB) * 100 / GiB),
(unsigned long)(total / GiB), (unsigned long)((total % GiB) * 100 / GiB));
else
printf("%lu / %lu MiB",
(unsigned long)(used / MiB), (unsigned long)(total / MiB));
}
int main(int argc, char **argv)
{
(void)argc; (void)argv;
putchar('\n');
for (int i = 0; logo[i]; i++) {
printf(" %s ", logo[i]);
switch (i) {
case 1: fputs("os: Cervus OS", stdout); break;
case 2: print_uptime(); break;
case 3: print_cpu(); break;
case 4: fputs(C_RESET "shell: CSH", stdout); break;
case 5: print_mem(); break;
}
putchar('\n');
}
putchar('\n');
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
(void)argv;
printf("Hello, Cervus world!\n");
printf("argc = %d\n", argc);
return 0;
}
+908
View File
@@ -0,0 +1,908 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <cervus_util.h>
#define NEO_VERSION "0.1"
#define NEO_TABSTOP 4
#define NEO_QUIT_CONFIRM 1
#define KEY_NONE 0
#define KEY_ESC 0x1B
#define KEY_BACKSPACE 127
#define KEY_CTRL(k) ((k) & 0x1f)
#define KEY_ARROW_UP 1000
#define KEY_ARROW_DOWN 1001
#define KEY_ARROW_LEFT 1002
#define KEY_ARROW_RIGHT 1003
#define KEY_HOME 1004
#define KEY_END 1005
#define KEY_DEL 1006
#define KEY_PAGE_UP 1007
#define KEY_PAGE_DOWN 1008
#define TIOCGWINSZ 0x5413
typedef struct { uint16_t ws_row, ws_col, ws_xpixel, ws_ypixel; } neo_winsize_t;
typedef struct {
int size;
int cap;
char *chars;
int rsize;
char *render;
} neo_row_t;
typedef struct {
int cx, cy;
int rx;
int rowoff;
int coloff;
int screenrows;
int screencols;
int numrows;
int rowscap;
neo_row_t *row;
int dirty;
char *filename;
char statusmsg[256];
int statusmsg_visible;
int quit_pending;
long disk_size;
struct termios orig_termios;
} neo_t;
static neo_t E;
static const char *g_cwd = "/";
static void die(const char *msg)
{
write(1, "\x1b[2J", 4);
write(1, "\x1b[H", 3);
if (msg) {
write(2, "neo: ", 5);
write(2, msg, strlen(msg));
write(2, "\n", 1);
}
exit(1);
}
static void disable_raw_mode(void)
{
tcsetattr(0, TCSAFLUSH, &E.orig_termios);
write(1, "\x1b[?7h\x1b[?25h", 11);
}
static void enable_raw_mode(void)
{
if (tcgetattr(0, &E.orig_termios) < 0) die("tcgetattr");
atexit(disable_raw_mode);
struct termios raw = E.orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSAFLUSH, &raw) < 0) die("tcsetattr");
write(1, "\x1b[?7l", 5);
}
static int read_key(void)
{
char c;
ssize_t n;
while ((n = read(0, &c, 1)) == 0) { }
if (n < 0) return KEY_NONE;
if (c != 0x1B) return (unsigned char)c;
char seq[4];
if (read(0, &seq[0], 1) != 1) return KEY_ESC;
if (read(0, &seq[1], 1) != 1) return KEY_ESC;
if (seq[0] == '[') {
if (seq[1] >= '0' && seq[1] <= '9') {
if (read(0, &seq[2], 1) != 1) return KEY_ESC;
if (seq[2] == '~') {
switch (seq[1]) {
case '1':
case '7': return KEY_HOME;
case '3': return KEY_DEL;
case '4':
case '8': return KEY_END;
case '5': return KEY_PAGE_UP;
case '6': return KEY_PAGE_DOWN;
}
}
} else {
switch (seq[1]) {
case 'A': return KEY_ARROW_UP;
case 'B': return KEY_ARROW_DOWN;
case 'C': return KEY_ARROW_RIGHT;
case 'D': return KEY_ARROW_LEFT;
case 'H': return KEY_HOME;
case 'F': return KEY_END;
}
}
} else if (seq[0] == 'O') {
switch (seq[1]) {
case 'H': return KEY_HOME;
case 'F': return KEY_END;
}
}
return KEY_ESC;
}
static void get_window_size(void)
{
neo_winsize_t ws;
if (syscall3(SYS_IOCTL, 1, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0 && ws.ws_row > 0) {
E.screencols = ws.ws_col;
E.screenrows = ws.ws_row;
} else {
E.screencols = 80;
E.screenrows = 24;
}
E.screenrows -= 2;
if (E.screenrows < 1) E.screenrows = 1;
}
static int row_cx_to_rx(neo_row_t *row, int cx)
{
int rx = 0;
for (int j = 0; j < cx && j < row->size; j++) {
if (row->chars[j] == '\t') rx += (NEO_TABSTOP - (rx % NEO_TABSTOP));
else rx++;
}
return rx;
}
static int row_rx_to_cx(neo_row_t *row, int rx)
{
int cur_rx = 0;
int cx;
for (cx = 0; cx < row->size; cx++) {
if (row->chars[cx] == '\t') cur_rx += (NEO_TABSTOP - (cur_rx % NEO_TABSTOP));
else cur_rx++;
if (cur_rx > rx) return cx;
}
return cx;
}
static void row_update(neo_row_t *row)
{
int tabs = 0;
for (int j = 0; j < row->size; j++) if (row->chars[j] == '\t') tabs++;
free(row->render);
row->render = malloc(row->size + tabs * (NEO_TABSTOP - 1) + 1);
int idx = 0;
for (int j = 0; j < row->size; j++) {
if (row->chars[j] == '\t') {
row->render[idx++] = ' ';
while (idx % NEO_TABSTOP != 0) row->render[idx++] = ' ';
} else {
row->render[idx++] = row->chars[j];
}
}
row->render[idx] = '\0';
row->rsize = idx;
}
static void rows_reserve(int want)
{
if (want <= E.rowscap) return;
int nc = E.rowscap ? E.rowscap * 2 : 32;
while (nc < want) nc *= 2;
neo_row_t *nr = malloc(sizeof(neo_row_t) * nc);
if (!nr) die("out of memory");
if (E.row) {
memcpy(nr, E.row, sizeof(neo_row_t) * E.numrows);
}
for (int i = E.numrows; i < nc; i++) {
nr[i].size = 0; nr[i].cap = 0; nr[i].chars = NULL;
nr[i].rsize = 0; nr[i].render = NULL;
}
E.row = nr;
E.rowscap = nc;
}
static void row_insert_at(int at, const char *s, int len)
{
if (at < 0 || at > E.numrows) return;
rows_reserve(E.numrows + 1);
for (int i = E.numrows; i > at; i--) E.row[i] = E.row[i - 1];
neo_row_t *r = &E.row[at];
r->size = len;
r->cap = len + 1;
r->chars = malloc(r->cap);
if (!r->chars) die("out of memory");
if (len > 0) memcpy(r->chars, s, len);
r->chars[len] = '\0';
r->render = NULL;
r->rsize = 0;
row_update(r);
E.numrows++;
E.dirty = 1;
}
static void row_free(neo_row_t *r)
{
free(r->chars);
free(r->render);
r->chars = NULL; r->render = NULL;
r->size = 0; r->cap = 0; r->rsize = 0;
}
static void row_delete_at(int at)
{
if (at < 0 || at >= E.numrows) return;
row_free(&E.row[at]);
for (int i = at; i < E.numrows - 1; i++) E.row[i] = E.row[i + 1];
E.numrows--;
E.dirty = 1;
}
static void row_reserve(neo_row_t *r, int want)
{
if (want <= r->cap) return;
int nc = r->cap ? r->cap * 2 : 16;
while (nc < want) nc *= 2;
char *nb = malloc(nc);
if (!nb) die("out of memory");
if (r->chars) memcpy(nb, r->chars, r->size);
nb[r->size] = '\0';
free(r->chars);
r->chars = nb;
r->cap = nc;
}
static void row_insert_char(neo_row_t *r, int at, int ch)
{
if (at < 0 || at > r->size) at = r->size;
row_reserve(r, r->size + 2);
memmove(&r->chars[at + 1], &r->chars[at], r->size - at + 1);
r->chars[at] = (char)ch;
r->size++;
row_update(r);
E.dirty = 1;
}
static void row_append_string(neo_row_t *r, const char *s, int len)
{
row_reserve(r, r->size + len + 1);
memcpy(&r->chars[r->size], s, len);
r->size += len;
r->chars[r->size] = '\0';
row_update(r);
E.dirty = 1;
}
static void row_delete_char(neo_row_t *r, int at)
{
if (at < 0 || at >= r->size) return;
memmove(&r->chars[at], &r->chars[at + 1], r->size - at);
r->size--;
row_update(r);
E.dirty = 1;
}
static void editor_insert_char(int ch)
{
if (E.cy == E.numrows) row_insert_at(E.numrows, "", 0);
row_insert_char(&E.row[E.cy], E.cx, ch);
E.cx++;
}
static void editor_insert_newline(void)
{
if (E.cx == 0) {
row_insert_at(E.cy, "", 0);
} else {
neo_row_t *r = &E.row[E.cy];
row_insert_at(E.cy + 1, &r->chars[E.cx], r->size - E.cx);
r = &E.row[E.cy];
r->size = E.cx;
r->chars[r->size] = '\0';
row_update(r);
}
E.cy++;
E.cx = 0;
}
static void editor_delete_char(void)
{
if (E.cy == E.numrows) return;
if (E.cx == 0 && E.cy == 0) return;
neo_row_t *r = &E.row[E.cy];
if (E.cx > 0) {
row_delete_char(r, E.cx - 1);
E.cx--;
} else {
E.cx = E.row[E.cy - 1].size;
row_append_string(&E.row[E.cy - 1], r->chars, r->size);
row_delete_at(E.cy);
E.cy--;
}
}
static char *rows_to_string(int *len)
{
int total = 0;
for (int j = 0; j < E.numrows; j++) total += E.row[j].size + 1;
char *buf = malloc(total + 1);
if (!buf) die("out of memory");
char *p = buf;
for (int j = 0; j < E.numrows; j++) {
memcpy(p, E.row[j].chars, E.row[j].size);
p += E.row[j].size;
*p++ = '\n';
}
*p = '\0';
*len = total;
return buf;
}
static void set_status(const char *fmt, ...);
static void editor_open(const char *filename)
{
free(E.filename);
size_t fl = strlen(filename);
E.filename = malloc(fl + 1);
memcpy(E.filename, filename, fl + 1);
char full[512];
resolve_path(g_cwd, filename, full, sizeof(full));
int fd = open(full, O_RDONLY, 0);
if (fd < 0) {
E.disk_size = -1;
set_status("New file: %s", filename);
return;
}
struct stat st;
if (fstat(fd, &st) < 0) { close(fd); E.disk_size = -1; return; }
size_t sz = (size_t)st.st_size;
E.disk_size = (long)sz;
char *buf = malloc(sz + 1);
if (!buf) { close(fd); die("out of memory"); }
size_t total = 0;
while (total < sz) {
ssize_t r = read(fd, buf + total, sz - total);
if (r <= 0) break;
total += (size_t)r;
}
close(fd);
buf[total] = '\0';
size_t i = 0;
while (i < total) {
size_t start = i;
while (i < total && buf[i] != '\n' && buf[i] != '\r') i++;
int len = (int)(i - start);
row_insert_at(E.numrows, buf + start, len);
if (i < total && buf[i] == '\r') i++;
if (i < total && buf[i] == '\n') i++;
}
free(buf);
E.dirty = 0;
}
static char *prompt(const char *prompt_fmt);
static int editor_save(void)
{
if (!E.filename) {
char *name = prompt("Save as (ESC to cancel): %s");
if (!name) {
set_status("Save cancelled");
return -1;
}
if (name[0] == '\0') {
free(name);
set_status("Save cancelled (empty filename)");
return -1;
}
E.filename = name;
E.disk_size = -1;
}
char full[512];
resolve_path(g_cwd, E.filename, full, sizeof(full));
if (E.disk_size >= 0) {
struct stat st;
if (stat(full, &st) == 0 && (long)st.st_size != E.disk_size) {
char *ans = prompt("File changed on disk! Overwrite? [y/N]: %s");
if (!ans) { set_status("Save cancelled"); return -1; }
int yes = (ans[0] == 'y' || ans[0] == 'Y');
free(ans);
if (!yes) { set_status("Save cancelled"); return -1; }
}
}
int len = 0;
char *buf = rows_to_string(&len);
int fd = open(full, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
free(buf);
set_status("Save failed: errno=%d (%s)", errno, full);
return -1;
}
ssize_t written = 0;
while (written < len) {
ssize_t w = write(fd, buf + written, len - written);
if (w <= 0) { close(fd); free(buf); set_status("Save failed (write err)"); return -1; }
written += w;
}
close(fd);
free(buf);
E.dirty = 0;
E.disk_size = len;
set_status("Saved %d bytes to %s", len, E.filename);
return 0;
}
typedef struct { char *b; int len; int cap; } abuf_t;
static void ab_append(abuf_t *ab, const char *s, int len)
{
if (ab->len + len > ab->cap) {
int nc = ab->cap ? ab->cap * 2 : 1024;
while (nc < ab->len + len) nc *= 2;
char *nb = malloc(nc);
if (!nb) die("out of memory");
if (ab->b) memcpy(nb, ab->b, ab->len);
free(ab->b);
ab->b = nb;
ab->cap = nc;
}
memcpy(ab->b + ab->len, s, len);
ab->len += len;
}
static void ab_free(abuf_t *ab) { free(ab->b); ab->b = NULL; ab->len = 0; ab->cap = 0; }
static void scroll(void)
{
E.rx = 0;
if (E.cy < E.numrows) E.rx = row_cx_to_rx(&E.row[E.cy], E.cx);
if (E.cy < E.rowoff) E.rowoff = E.cy;
if (E.cy >= E.rowoff + E.screenrows) E.rowoff = E.cy - E.screenrows + 1;
if (E.rx < E.coloff) E.coloff = E.rx;
if (E.rx >= E.coloff + E.screencols) E.coloff = E.rx - E.screencols + 1;
}
static void draw_rows(abuf_t *ab)
{
char pos[16];
int limit = E.screencols - 1;
if (limit < 1) limit = 1;
for (int y = 0; y < E.screenrows; y++) {
int n = snprintf(pos, sizeof(pos), "\x1b[%d;1H", y + 1);
ab_append(ab, pos, n);
int filerow = y + E.rowoff;
if (filerow >= E.numrows) {
if (E.numrows == 0 && y == E.screenrows / 3) {
char welcome[80];
int wl = snprintf(welcome, sizeof(welcome),
"neo editor -- version %s -- press ESC to exit", NEO_VERSION);
if (wl > limit) wl = limit;
int padding = (limit - wl) / 2;
if (padding > 0) { ab_append(ab, "~", 1); padding--; }
while (padding-- > 0) ab_append(ab, " ", 1);
ab_append(ab, welcome, wl);
} else {
ab_append(ab, "~", 1);
}
} else {
int len = E.row[filerow].rsize - E.coloff;
if (len < 0) len = 0;
if (len > limit) len = limit;
ab_append(ab, E.row[filerow].render + E.coloff, len);
}
ab_append(ab, "\x1b[K", 3);
}
}
static void draw_status(abuf_t *ab)
{
char pos[16];
int n = snprintf(pos, sizeof(pos), "\x1b[%d;1H", E.screenrows + 1);
ab_append(ab, pos, n);
ab_append(ab, "\x1b[7m", 4);
char status[256], rstatus[80];
int len = snprintf(status, sizeof(status), " %.40s%s ",
E.filename ? E.filename : "[No Name]",
E.dirty ? " [modified]" : "");
int rlen = snprintf(rstatus, sizeof(rstatus), "%d/%d ",
E.cy + 1, E.numrows);
int limit = E.screencols - 1;
if (limit < 1) limit = 1;
if (len > limit) len = limit;
ab_append(ab, status, len);
while (len < limit) {
if (limit - len == rlen) { ab_append(ab, rstatus, rlen); break; }
ab_append(ab, " ", 1);
len++;
}
ab_append(ab, "\x1b[m", 3);
ab_append(ab, "\x1b[K", 3);
}
static void draw_message(abuf_t *ab)
{
char pos[16];
int n = snprintf(pos, sizeof(pos), "\x1b[%d;1H", E.screenrows + 2);
ab_append(ab, pos, n);
ab_append(ab, "\x1b[K", 3);
if (E.statusmsg_visible) {
int mlen = strlen(E.statusmsg);
if (mlen > E.screencols) mlen = E.screencols;
ab_append(ab, E.statusmsg, mlen);
} else {
const char *hint = " ^S=save ^Q=quit ^F=find ^G=goto ^B=top ^E=end ESC=exit";
int mlen = strlen(hint);
if (mlen > E.screencols) mlen = E.screencols;
ab_append(ab, hint, mlen);
}
}
static void refresh_screen(void)
{
scroll();
abuf_t ab = {0};
ab_append(&ab, "\x1b[?25l", 6);
draw_rows(&ab);
draw_status(&ab);
draw_message(&ab);
int cursor_row = (E.cy - E.rowoff) + 1;
int cursor_col = (E.rx - E.coloff) + 1;
char curbuf[32];
int n = snprintf(curbuf, sizeof(curbuf), "\x1b[%d;%dH", cursor_row, cursor_col);
ab_append(&ab, curbuf, n);
ab_append(&ab, "\x1b[?25h", 6);
write(1, ab.b, ab.len);
ab_free(&ab);
}
static void set_status(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(E.statusmsg, sizeof(E.statusmsg), fmt, ap);
va_end(ap);
E.statusmsg_visible = 1;
}
static char *prompt_cb(const char *prompt_fmt, void (*callback)(char *, int))
{
size_t bufcap = 128;
size_t buflen = 0;
char *buf = malloc(bufcap);
buf[0] = '\0';
for (;;) {
set_status(prompt_fmt, buf);
refresh_screen();
int c = read_key();
if (c == KEY_DEL || c == KEY_CTRL('h') || c == KEY_BACKSPACE) {
if (buflen > 0) buf[--buflen] = '\0';
} else if (c == KEY_ESC) {
set_status("");
E.statusmsg_visible = 0;
if (callback) callback(buf, c);
free(buf);
return NULL;
} else if (c == '\r' || c == '\n') {
if (buflen != 0 || callback) {
set_status("");
E.statusmsg_visible = 0;
if (callback) callback(buf, c);
return buf;
}
} else if (!iscntrl(c) && c < 128) {
if (buflen + 1 >= bufcap) {
bufcap *= 2;
char *nb = malloc(bufcap);
memcpy(nb, buf, buflen);
free(buf);
buf = nb;
}
buf[buflen++] = (char)c;
buf[buflen] = '\0';
}
if (callback) callback(buf, c);
}
}
static char *prompt(const char *prompt_fmt)
{
return prompt_cb(prompt_fmt, NULL);
}
static void move_cursor(int key)
{
neo_row_t *row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
switch (key) {
case KEY_ARROW_LEFT:
if (E.cx > 0) E.cx--;
else if (E.cy > 0) { E.cy--; E.cx = E.row[E.cy].size; }
break;
case KEY_ARROW_RIGHT:
if (row && E.cx < row->size) E.cx++;
else if (row && E.cx == row->size) { E.cy++; E.cx = 0; }
break;
case KEY_ARROW_UP:
if (E.cy > 0) E.cy--;
break;
case KEY_ARROW_DOWN:
if (E.cy < E.numrows) E.cy++;
break;
}
row = (E.cy >= E.numrows) ? NULL : &E.row[E.cy];
int rowlen = row ? row->size : 0;
if (E.cx > rowlen) E.cx = rowlen;
}
static void goto_line(void)
{
char *p = prompt("Go to line: %s (ESC cancels)");
if (!p) return;
int n = atoi(p);
free(p);
if (n < 1) n = 1;
if (n > E.numrows) n = E.numrows == 0 ? 1 : E.numrows;
E.cy = n - 1;
E.cx = 0;
}
static int __find_last_match = -1;
static int __find_direction = 1;
static int __find_saved_cx, __find_saved_cy;
static int __find_saved_rowoff, __find_saved_coloff;
static char *__find_last_query = NULL;
static void editor_find_callback(char *query, int key)
{
if (key == '\r' || key == '\n' || key == KEY_ESC) {
__find_last_match = -1;
__find_direction = 1;
return;
}
if (key == KEY_ARROW_DOWN || key == KEY_ARROW_RIGHT) {
__find_direction = 1;
} else if (key == KEY_ARROW_UP || key == KEY_ARROW_LEFT) {
__find_direction = -1;
} else {
__find_last_match = -1;
__find_direction = 1;
}
if (!query || !query[0]) return;
int current = __find_last_match;
if (current == -1) current = E.cy;
int qlen = (int)strlen(query);
for (int i = 0; i < E.numrows; i++) {
current += __find_direction;
if (current == -1) current = E.numrows - 1;
else if (current == E.numrows) current = 0;
neo_row_t *row = &E.row[current];
char *match = strstr(row->render, query);
if (match) {
__find_last_match = current;
E.cy = current;
int rx = (int)(match - row->render);
E.cx = row_rx_to_cx(row, rx);
E.rowoff = E.numrows;
(void)qlen;
return;
}
}
}
static void editor_find(void)
{
__find_saved_cx = E.cx;
__find_saved_cy = E.cy;
__find_saved_rowoff = E.rowoff;
__find_saved_coloff = E.coloff;
__find_last_match = -1;
__find_direction = 1;
char *query = prompt_cb(
"Search: %s (Up/Down=prev/next, Enter=keep, ESC=cancel)",
editor_find_callback);
if (query) {
if (query[0] == '\0' && __find_last_query) {
free(query);
query = strdup(__find_last_query);
if (query) {
editor_find_callback(query, 0);
}
}
if (query && query[0]) {
free(__find_last_query);
__find_last_query = strdup(query);
}
if (query) free(query);
} else {
E.cx = __find_saved_cx;
E.cy = __find_saved_cy;
E.rowoff = __find_saved_rowoff;
E.coloff = __find_saved_coloff;
}
}
static int process_key(void)
{
int c = read_key();
switch (c) {
case '\r':
case '\n':
editor_insert_newline();
break;
case KEY_ESC:
if (E.dirty && NEO_QUIT_CONFIRM && !E.quit_pending) {
set_status("Unsaved changes. ESC again to exit without saving, Ctrl-S to save.");
E.quit_pending = 1;
return 1;
}
return 0;
case KEY_CTRL('s'):
editor_save();
E.quit_pending = 0;
break;
case KEY_CTRL('q'):
if (E.dirty && NEO_QUIT_CONFIRM && !E.quit_pending) {
set_status("Unsaved changes. Ctrl-Q again to force quit.");
E.quit_pending = 1;
return 1;
}
return 0;
case KEY_CTRL('g'):
goto_line();
break;
case KEY_CTRL('f'):
editor_find();
break;
case KEY_CTRL('b'):
E.cy = 0;
E.cx = 0;
E.rowoff = 0;
E.coloff = 0;
break;
case KEY_CTRL('e'):
E.cy = E.numrows == 0 ? 0 : E.numrows - 1;
if (E.cy < E.numrows) E.cx = E.row[E.cy].size;
else E.cx = 0;
break;
case KEY_HOME:
E.cx = 0;
break;
case KEY_END:
if (E.cy < E.numrows) E.cx = E.row[E.cy].size;
break;
case KEY_BACKSPACE:
case KEY_CTRL('h'):
editor_delete_char();
break;
case KEY_DEL:
move_cursor(KEY_ARROW_RIGHT);
editor_delete_char();
break;
case KEY_PAGE_UP:
case KEY_PAGE_DOWN: {
if (c == KEY_PAGE_UP) {
E.cy = E.rowoff;
} else {
E.cy = E.rowoff + E.screenrows - 1;
if (E.cy > E.numrows) E.cy = E.numrows;
}
int times = E.screenrows;
while (times--) move_cursor(c == KEY_PAGE_UP ? KEY_ARROW_UP : KEY_ARROW_DOWN);
break;
}
case KEY_ARROW_UP:
case KEY_ARROW_DOWN:
case KEY_ARROW_LEFT:
case KEY_ARROW_RIGHT:
move_cursor(c);
break;
case KEY_CTRL('l'):
case 0:
break;
default:
if (c >= 32 && c < 127) editor_insert_char(c);
else if (c == '\t') editor_insert_char('\t');
break;
}
E.quit_pending = 0;
return 1;
}
static void init_editor(void)
{
E.cx = 0; E.cy = 0; E.rx = 0;
E.rowoff = 0; E.coloff = 0;
E.numrows = 0; E.rowscap = 0; E.row = NULL;
E.dirty = 0;
E.filename = NULL;
E.statusmsg[0] = '\0';
E.statusmsg_visible = 0;
E.quit_pending = 0;
E.disk_size = -1;
get_window_size();
}
int main(int argc, char **argv)
{
g_cwd = get_cwd_flag(argc, argv);
init_editor();
enable_raw_mode();
const char *file_to_open = NULL;
for (int i = 1; i < argc; i++) {
if (is_shell_flag(argv[i])) continue;
file_to_open = argv[i];
break;
}
if (file_to_open) editor_open(file_to_open);
write(1, "\x1b[2J", 4);
write(1, "\x1b[H", 3);
refresh_screen();
while (process_key()) {
refresh_screen();
}
write(1, "\x1b[2J", 4);
write(1, "\x1b[H", 3);
disable_raw_mode();
return 0;
}
+975
View File
@@ -0,0 +1,975 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/cervus.h>
#include <sys/syscall.h>
#include <cervus_util.h>
#ifndef VFS_MAX_PATH
#define VFS_MAX_PATH 1024
#endif
#define TIOCGWINSZ 0x5413
#define TIOCGCURSOR 0x5480
typedef struct { uint16_t ws_row, ws_col, ws_xpixel, ws_ypixel; } cervus_winsize_t;
typedef struct { uint32_t row, col; } cervus_cursor_pos_t;
static inline int sh_ioctl(int fd, unsigned long req, void *arg) {
return (int)syscall3(SYS_IOCTL, fd, req, arg);
}
static int g_cols = 80;
static int g_rows = 25;
static void term_update_size(void) {
cervus_winsize_t ws;
if (sh_ioctl(1, TIOCGWINSZ, &ws) == 0 && ws.ws_col >= 8 && ws.ws_row >= 2) {
g_cols = (int)ws.ws_col;
g_rows = (int)ws.ws_row;
}
}
static int term_get_cursor_row(void) {
cervus_cursor_pos_t cp;
if (sh_ioctl(1, TIOCGCURSOR, &cp) == 0) return (int)cp.row;
return 0;
}
static void vt_goto(int row, int col) {
char b[24];
snprintf(b, sizeof(b), "\x1b[%d;%dH", row + 1, col + 1);
fputs(b, stdout);
}
static void vt_eol(void) { fputs("\x1b[K", stdout); }
#define HIST_MAX 1024
#define LINE_MAX 1024
static char history[HIST_MAX][LINE_MAX];
static int hist_count = 0, hist_head = 0;
static const char *g_hist_file = NULL;
static void hist_load(const char *path) {
int fd = open(path, O_RDONLY, 0);
if (fd < 0) return;
char line[LINE_MAX];
int li = 0;
char ch;
while (read(fd, &ch, 1) > 0) {
if (ch == '\n' || li >= LINE_MAX - 1) {
line[li] = '\0';
if (li > 0) {
int idx = (hist_head + hist_count) % HIST_MAX;
strncpy(history[idx], line, LINE_MAX - 1);
history[idx][LINE_MAX - 1] = '\0';
if (hist_count < HIST_MAX) hist_count++;
else hist_head = (hist_head + 1) % HIST_MAX;
}
li = 0;
} else {
line[li++] = ch;
}
}
close(fd);
}
static void hist_save_entry(const char *path, const char *l) {
int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0600);
if (fd < 0) return;
int n = 0;
while (l[n]) n++;
write(fd, l, n);
write(fd, "\n", 1);
close(fd);
}
static void hist_push(const char *l) {
if (!l[0]) return;
if (hist_count > 0) {
int last = (hist_head + hist_count - 1) % HIST_MAX;
if (strcmp(history[last], l) == 0) return;
}
int idx = (hist_head + hist_count) % HIST_MAX;
strncpy(history[idx], l, LINE_MAX - 1);
history[idx][LINE_MAX - 1] = '\0';
if (hist_count < HIST_MAX) hist_count++;
else hist_head = (hist_head + 1) % HIST_MAX;
if (g_hist_file) hist_save_entry(g_hist_file, l);
}
static const char *hist_get(int n) {
if (n < 1 || n > hist_count) return NULL;
return history[(hist_head + hist_count - n) % HIST_MAX];
}
#define ENV_MAX_VARS 128
#define ENV_NAME_MAX 64
#define ENV_VAL_MAX 512
typedef struct { char name[ENV_NAME_MAX]; char value[ENV_VAL_MAX]; } env_var_t;
static env_var_t g_env[ENV_MAX_VARS];
static int g_env_count = 0;
static int env_find(const char *name) {
for (int i = 0; i < g_env_count; i++)
if (strcmp(g_env[i].name, name) == 0) return i;
return -1;
}
static const char *env_get(const char *name) {
int i = env_find(name);
return i >= 0 ? g_env[i].value : "";
}
static void env_set(const char *name, const char *value) {
int i = env_find(name);
if (i >= 0) { strncpy(g_env[i].value, value, ENV_VAL_MAX - 1); return; }
if (g_env_count >= ENV_MAX_VARS) return;
strncpy(g_env[g_env_count].name, name, ENV_NAME_MAX - 1);
strncpy(g_env[g_env_count].value, value, ENV_VAL_MAX - 1);
g_env_count++;
}
static void env_unset(const char *name) {
int i = env_find(name);
if (i < 0) return;
g_env[i] = g_env[--g_env_count];
}
static int g_last_rc = 0;
static void expand_vars(const char *src, char *dst, size_t dsz) {
size_t di = 0;
for (const char *p = src; *p && di + 1 < dsz; ) {
if (*p != '$') { dst[di++] = *p++; continue; }
p++;
if (*p == '?') {
char tmp[12];
int len = snprintf(tmp, sizeof(tmp), "%d", g_last_rc);
for (int x = 0; x < len && di + 1 < dsz; x++) dst[di++] = tmp[x];
p++; continue;
}
int braced = (*p == '{');
if (braced) p++;
char name[ENV_NAME_MAX]; int ni = 0;
while (*p && ni + 1 < (int)ENV_NAME_MAX) {
char c = *p;
if (braced) { if (c == '}') { p++; break; } }
else if (!isalnum((unsigned char)c) && c != '_') break;
name[ni++] = c; p++;
}
name[ni] = '\0';
if (ni == 0) { dst[di++] = '$'; continue; }
const char *val = env_get(name);
for (; *val && di + 1 < dsz; val++) dst[di++] = *val;
}
dst[di] = '\0';
}
static char cwd[VFS_MAX_PATH];
static int prompt_len = 0;
static const char *display_path(void) {
static char dpbuf[VFS_MAX_PATH];
const char *home = env_get("HOME");
size_t hlen = home ? strlen(home) : 0;
if (hlen > 1 && strncmp(cwd, home, hlen) == 0 &&
(cwd[hlen] == '/' || cwd[hlen] == '\0')) {
dpbuf[0] = '~';
strncpy(dpbuf + 1, cwd + hlen, sizeof(dpbuf) - 2);
dpbuf[sizeof(dpbuf) - 1] = '\0';
if (dpbuf[1] == '\0') { dpbuf[0] = '~'; dpbuf[1] = '\0'; }
return dpbuf;
}
return cwd;
}
static void print_prompt(void) {
const char *dp = display_path();
fputs(C_GREEN "cervus" C_RESET ":" C_BLUE, stdout);
fputs(dp, stdout);
fputs(C_RESET "$ ", stdout);
prompt_len = 9 + (int)strlen(dp);
}
static int g_start_row = 0;
static void sync_start_row(int cur_logical_pos) {
int real_row = term_get_cursor_row();
int row_offset = (prompt_len + cur_logical_pos) / g_cols;
g_start_row = real_row - row_offset;
if (g_start_row < 0) g_start_row = 0;
}
static void input_pos_to_screen(int pos, int *row, int *col) {
int abs = prompt_len + pos;
*row = g_start_row + abs / g_cols;
*col = abs % g_cols;
}
static void cursor_to(int pos) {
int row, col;
input_pos_to_screen(pos, &row, &col);
if (row >= g_rows) row = g_rows - 1;
if (row < 0) row = 0;
vt_goto(row, col);
}
static int last_row_of(int len) {
int abs = prompt_len + len;
return g_start_row + (abs > 0 ? (abs - 1) : 0) / g_cols;
}
static void redraw(const char *buf, int from, int new_len, int old_len, int pos) {
cursor_to(from);
if (new_len > from) write(1, buf + from, new_len - from);
sync_start_row(new_len);
if (old_len > new_len) {
int old_last = last_row_of(old_len);
int new_last = last_row_of(new_len);
cursor_to(new_len); vt_eol();
for (int r = new_last + 1; r <= old_last; r++) {
if (r >= g_rows) break;
vt_goto(r, 0); vt_eol();
}
}
cursor_to(pos);
}
static void replace_line(char *buf, int *len, int *pos, const char *newtext, int newlen) {
int old_len = *len;
for (int i = 0; i < newlen; i++) buf[i] = newtext[i];
buf[newlen] = '\0';
*len = newlen;
*pos = newlen;
redraw(buf, 0, newlen, old_len, newlen);
}
static void insert_str(char *buf, int *len, int *pos, int maxlen, const char *s, int slen) {
if (*len + slen >= maxlen) return;
int old_len = *len;
for (int i = *len; i >= *pos; i--) buf[i + slen] = buf[i];
for (int i = 0; i < slen; i++) buf[*pos + i] = s[i];
*len += slen;
buf[*len] = '\0';
cursor_to(*pos);
write(1, buf + *pos, *len - *pos);
sync_start_row(*len);
*pos += slen;
cursor_to(*pos);
}
static int find_word_start(const char *buf, int pos) {
int p = pos;
while (p > 0 && buf[p - 1] != ' ') p--;
return p;
}
static void list_dir_matches(const char *dir, const char *prefix, int plen,
char matches[][256], int *nmatch, int max) {
DIR *d = opendir(dir);
if (!d) return;
struct dirent *de;
while (*nmatch < max && (de = readdir(d)) != NULL) {
if (strncmp(de->d_name, prefix, plen) == 0) {
strncpy(matches[*nmatch], de->d_name, 255);
matches[*nmatch][255] = '\0';
(*nmatch)++;
}
}
closedir(d);
}
static void do_tab_complete(char *buf, int *len, int *pos, int maxlen) {
int ws_start = find_word_start(buf, *pos);
int wlen = *pos - ws_start;
if (wlen <= 0) {
insert_str(buf, len, pos, maxlen, " ", 4);
return;
}
char word[256];
if (wlen > 255) wlen = 255;
memcpy(word, buf + ws_start, wlen);
word[wlen] = '\0';
int is_first_word = 1;
for (int i = 0; i < ws_start; i++) {
if (buf[i] != ' ') { is_first_word = 0; break; }
}
char matches[32][256];
int nmatch = 0;
if (is_first_word) {
const char *pathvar = env_get("PATH");
char ptmp[ENV_VAL_MAX];
strncpy(ptmp, pathvar, sizeof(ptmp) - 1);
char *p = ptmp;
while (*p && nmatch < 32) {
char *seg = p;
while (*p && *p != ':') p++;
if (*p == ':') *p++ = '\0';
if (seg[0]) list_dir_matches(seg, word, wlen, matches, &nmatch, 32);
}
const char *builtins[] = {"help","exit","cd","export","unset",NULL};
for (int i = 0; builtins[i] && nmatch < 32; i++) {
if (strncmp(builtins[i], word, wlen) == 0) {
strncpy(matches[nmatch], builtins[i], 255);
nmatch++;
}
}
} else {
char dirp[VFS_MAX_PATH];
const char *prefix = word;
char *last_slash = NULL;
for (int i = 0; word[i]; i++) if (word[i] == '/') last_slash = &word[i];
if (last_slash) {
int dlen = (int)(last_slash - word);
char raw_dir[256];
memcpy(raw_dir, word, dlen);
raw_dir[dlen] = '\0';
if (raw_dir[0] == '\0') strcpy(raw_dir, "/");
resolve_path(cwd, raw_dir, dirp, sizeof(dirp));
prefix = last_slash + 1;
} else {
strncpy(dirp, cwd, sizeof(dirp) - 1);
}
int plen = (int)strlen(prefix);
list_dir_matches(dirp, prefix, plen, matches, &nmatch, 32);
}
if (nmatch == 0) {
insert_str(buf, len, pos, maxlen, " ", 4);
return;
}
if (nmatch == 1) {
const char *m = matches[0];
int mlen = (int)strlen(m);
int tail = mlen - wlen;
if (tail > 0) {
const char *suffix = m + wlen;
if (is_first_word) {
int need = tail;
if (*len + need >= maxlen) return;
int old_len = *len;
for (int i = *len; i >= *pos; i--) buf[i + need] = buf[i];
for (int i = 0; i < tail; i++) buf[*pos + i] = suffix[i];
*len += need;
buf[*len] = '\0';
cursor_to(*pos);
write(1, buf + *pos, *len - *pos);
sync_start_row(*len);
*pos += need;
cursor_to(*pos);
} else {
insert_str(buf, len, pos, maxlen, suffix, tail);
}
}
return;
}
int common = (int)strlen(matches[0]);
for (int i = 1; i < nmatch; i++) {
int j = 0;
while (j < common && matches[0][j] == matches[i][j]) j++;
common = j;
}
int extra = common - wlen;
if (extra > 0) {
insert_str(buf, len, pos, maxlen, matches[0] + wlen, extra);
return;
}
putchar(10);
for (int i = 0; i < nmatch; i++) {
fputs(" ", stdout); fputs(matches[i], stdout);
}
putchar(10);
print_prompt();
write(1, buf, *len);
sync_start_row(*len);
cursor_to(*pos);
}
static int readline_edit(char *buf, int maxlen) {
term_update_size();
{
int real_row = term_get_cursor_row();
g_start_row = real_row - prompt_len / g_cols;
if (g_start_row < 0) g_start_row = 0;
}
int len = 0, pos = 0, hidx = 0;
static char saved[LINE_MAX];
saved[0] = '\0'; buf[0] = '\0';
for (;;) {
char c;
if (read(0, &c, 1) <= 0) return -1;
if (c == '\x1b') {
char s[4];
if (read(0, &s[0], 1) <= 0) continue;
if (s[0] != '[') continue;
if (read(0, &s[1], 1) <= 0) continue;
if (s[1] == 'A') {
if (hidx == 0) strncpy(saved, buf, LINE_MAX - 1);
if (hidx < hist_count) {
hidx++;
const char *h = hist_get(hidx);
if (h) {
int hl = (int)strlen(h);
if (hl > maxlen - 1) hl = maxlen - 1;
replace_line(buf, &len, &pos, h, hl);
}
}
continue;
}
if (s[1] == 'B') {
if (hidx > 0) {
hidx--;
const char *h = hidx == 0 ? saved : hist_get(hidx);
if (!h) h = "";
int hl = (int)strlen(h);
if (hl > maxlen - 1) hl = maxlen - 1;
replace_line(buf, &len, &pos, h, hl);
}
continue;
}
if (s[1] == 'C') { if (pos < len) { pos++; cursor_to(pos); } continue; }
if (s[1] == 'D') { if (pos > 0) { pos--; cursor_to(pos); } continue; }
if (s[1] == 'H') { pos = 0; cursor_to(0); continue; }
if (s[1] == 'F') { pos = len; cursor_to(len); continue; }
if (s[1] >= '1' && s[1] <= '6') {
char tilde; read(0, &tilde, 1);
if (tilde != '~') continue;
if (s[1] == '3' && pos < len) {
for (int i = pos; i < len - 1; i++) buf[i] = buf[i + 1];
len--; buf[len] = '\0';
redraw(buf, pos, len, len + 1, pos);
} else if (s[1] == '1') { pos = 0; cursor_to(0); }
else if (s[1] == '4') { pos = len; cursor_to(len); }
}
continue;
}
if (c == '\n' || c == '\r') { buf[len] = '\0'; cursor_to(len); putchar(10); return len; }
if (c == 3) { fputs("^C", stdout); putchar(10); buf[0] = '\0'; return 0; }
if (c == 4) { if (len == 0) return -1; continue; }
if (c == 1) { pos = 0; cursor_to(0); continue; }
if (c == 5) { pos = len; cursor_to(len); continue; }
if (c == '\t') {
do_tab_complete(buf, &len, &pos, maxlen);
continue;
}
if (c == 11) {
if (pos < len) { int old_len = len; len = pos; buf[len] = '\0'; redraw(buf, pos, len, old_len, pos); }
continue;
}
if (c == 21) {
if (pos > 0) {
int old_len = len, del = pos;
for (int i = 0; i < len - del; i++) buf[i] = buf[i + del];
len -= del; pos = 0; buf[len] = '\0';
redraw(buf, 0, len, old_len, 0);
}
continue;
}
if (c == 23) {
if (pos > 0) {
int p = pos;
while (p > 0 && buf[p - 1] == ' ') p--;
while (p > 0 && buf[p - 1] != ' ') p--;
int old_len = len, del = pos - p;
for (int i = p; i < len - del; i++) buf[i] = buf[i + del];
len -= del; pos = p; buf[len] = '\0';
redraw(buf, p, len, old_len, p);
}
continue;
}
if (c == '\b' || c == 0x7F) {
if (pos > 0) {
int old_len = len;
for (int i = pos - 1; i < len - 1; i++) buf[i] = buf[i + 1];
len--; pos--; buf[len] = '\0';
redraw(buf, pos, len, old_len, pos);
}
continue;
}
if (c >= 0x20 && c < 0x7F) {
if (len >= maxlen - 1) continue;
for (int i = len; i > pos; i--) buf[i] = buf[i - 1];
buf[pos] = c; len++; buf[len] = '\0';
cursor_to(pos); write(1, buf + pos, len - pos);
sync_start_row(len); pos++;
cursor_to(pos);
}
}
}
#define MAX_ARGS 32
static int tokenize(char *line, char *argv[], int maxargs) {
int argc = 0;
char *p = line;
while (*p) {
while (isspace((unsigned char)*p)) p++;
if (!*p) break;
if (argc >= maxargs - 1) break;
char *out = p;
argv[argc++] = out;
int in_dquote = 0, in_squote = 0;
while (*p) {
if (in_dquote) {
if (*p == '"') { in_dquote = 0; p++; }
else { *out++ = *p++; }
} else if (in_squote) {
if (*p == '\'') { in_squote = 0; p++; }
else { *out++ = *p++; }
} else {
if (*p == '"') { in_dquote = 1; p++; }
else if (*p == '\'') { in_squote = 1; p++; }
else if (isspace((unsigned char)*p)) { p++; break; }
else { *out++ = *p++; }
}
}
*out = '\0';
if (in_dquote || in_squote) { argv[argc] = NULL; return -1; }
}
argv[argc] = NULL;
return argc;
}
static void cmd_help(void) {
putchar(10);
fputs(" " C_CYAN "Cervus Shell" C_RESET " - commands\n", stdout);
fputs(" " C_GRAY "-----------------------------------" C_RESET "\n", stdout);
fputs(" " C_BOLD "help" C_RESET " show this message\n", stdout);
fputs(" " C_BOLD "cd" C_RESET " <dir> change directory\n", stdout);
fputs(" " C_BOLD "export" C_RESET " N=V set variable N to value V\n", stdout);
fputs(" " C_BOLD "unset" C_RESET " N delete variable N\n", stdout);
fputs(" " C_BOLD "env" C_RESET " list variables\n", stdout);
fputs(" " C_BOLD "exit" C_RESET " quit shell\n", stdout);
fputs(" " C_GRAY "-----------------------------------" C_RESET "\n", stdout);
fputs(" " C_BOLD "Programs:" C_RESET " ls, cat, echo, pwd, clear, uname\n", stdout);
fputs(" meminfo, cpuinfo, ps, kill, find, stat, wc, yes, sleep\n", stdout);
fputs(" mount, umount, mkfs, lsblk, mv, rm, mkdir, touch\n", stdout);
fputs(" " C_RED "shutdown" C_RESET ", " C_CYAN "reboot" C_RESET "\n", stdout);
fputs(" " C_GRAY "-----------------------------------" C_RESET "\n", stdout);
fputs(" " C_BOLD "Operators:" C_RESET " cmd1 " C_YELLOW ";" C_RESET
" cmd2 " C_YELLOW "&&" C_RESET " " C_YELLOW "||" C_RESET "\n", stdout);
fputs(" " C_BOLD "Tab" C_RESET " auto-complete / 4 spaces\n", stdout);
fputs(" " C_BOLD "Ctrl+C" C_RESET " interrupt\n", stdout);
fputs(" " C_BOLD "Ctrl+A/E" C_RESET " beginning/end of line\n", stdout);
fputs(" " C_BOLD "Ctrl+K/U" C_RESET " delete to end/beginning\n", stdout);
fputs(" " C_BOLD "Ctrl+W" C_RESET " delete word\n", stdout);
fputs(" " C_BOLD "Arrows" C_RESET " cursor / history\n", stdout);
fputs(" " C_GRAY "-----------------------------------" C_RESET "\n", stdout);
putchar(10);
}
static int cmd_cd(const char *path) {
if (!path || !path[0] || strcmp(path, "~") == 0) {
const char *home = env_get("HOME");
path = (home && home[0]) ? home : "/";
}
char np[VFS_MAX_PATH];
resolve_path(cwd, path, np, sizeof(np));
struct stat st;
if (stat(np, &st) < 0) { fputs(C_RED "cd: not found: " C_RESET, stdout); fputs(path, stdout); putchar(10); return 1; }
if (st.st_type != 1) { fputs(C_RED "cd: not a dir: " C_RESET, stdout); fputs(path, stdout); putchar(10); return 1; }
strncpy(cwd, np, sizeof(cwd) - 1);
cwd[sizeof(cwd) - 1] = '\0';
chdir(np);
return 0;
}
static int valid_varname(const char *s) {
if (!s || !*s) return 0;
if (!isalpha((unsigned char)*s) && *s != '_') return 0;
for (s++; *s; s++)
if (!isalnum((unsigned char)*s) && *s != '_') return 0;
return 1;
}
static int cmd_export(int argc, char *argv[]) {
if (argc < 2) { fputs(C_RED "export: usage: export NAME=VALUE\n" C_RESET, stdout); return 1; }
if (argc > 2) { fputs(C_RED "export: invalid syntax\n" C_RESET, stdout); return 1; }
char *arg = argv[1];
char *eq_pos = strchr(arg, '=');
if (!eq_pos) {
if (!valid_varname(arg)) { fputs(C_RED "export: not a valid identifier\n" C_RESET, stdout); return 1; }
env_set(arg, ""); return 0;
}
*eq_pos = '\0';
const char *name = arg, *val = eq_pos + 1;
if (!valid_varname(name)) { *eq_pos = '='; fputs(C_RED "export: not a valid identifier\n" C_RESET, stdout); return 1; }
env_set(name, val);
*eq_pos = '=';
return 0;
}
static int cmd_unset(int argc, char *argv[]) {
if (argc < 2) { fputs(C_RED "unset: usage: unset NAME\n" C_RESET, stdout); return 1; }
for (int i = 1; i < argc; i++) env_unset(argv[i]);
return 0;
}
static int find_in_path(const char *cmd, char *out, size_t outsz) {
const char *pathvar = env_get("PATH");
if (!pathvar || !pathvar[0]) {
path_join("/bin", cmd, out, outsz);
struct stat st;
return stat(out, &st) == 0 && st.st_type != 1;
}
char tmp[ENV_VAL_MAX];
strncpy(tmp, pathvar, sizeof(tmp) - 1);
char *p = tmp;
while (*p) {
char *seg = p;
while (*p && *p != ':') p++;
if (*p == ':') *p++ = '\0';
if (!seg[0]) continue;
char candidate[VFS_MAX_PATH];
path_join(seg, cmd, candidate, sizeof(candidate));
struct stat st;
if (stat(candidate, &st) == 0 && st.st_type != 1) { strncpy(out, candidate, outsz - 1); return 1; }
}
return 0;
}
typedef enum { REDIR_NONE = 0, REDIR_OUT, REDIR_APPEND, REDIR_IN } redir_type_t;
typedef struct {
redir_type_t type;
char path[VFS_MAX_PATH];
} redir_t;
static int parse_redirects(char *argv[], int *argc, redir_t redirs[], int max_redirs, int *nredirs) {
*nredirs = 0;
int new_argc = 0;
for (int i = 0; i < *argc; i++) {
const char *a = argv[i];
redir_type_t rt = REDIR_NONE;
const char *target = NULL;
if (strcmp(a, ">>") == 0) {
rt = REDIR_APPEND;
if (i + 1 < *argc) target = argv[++i];
} else if (strcmp(a, ">") == 0) {
rt = REDIR_OUT;
if (i + 1 < *argc) target = argv[++i];
} else if (strcmp(a, "<") == 0) {
rt = REDIR_IN;
if (i + 1 < *argc) target = argv[++i];
} else if (a[0] == '>' && a[1] == '>' && a[2] != '\0') {
rt = REDIR_APPEND; target = a + 2;
} else if (a[0] == '>' && a[1] != '>' && a[1] != '\0') {
rt = REDIR_OUT; target = a + 1;
} else if (a[0] == '<' && a[1] != '\0') {
rt = REDIR_IN; target = a + 1;
} else {
argv[new_argc++] = argv[i];
continue;
}
if (!target || !target[0]) {
fputs(C_RED "syntax error: missing redirection target\n" C_RESET, stdout);
return -1;
}
if (*nredirs < max_redirs) {
redirs[*nredirs].type = rt;
char resolved[VFS_MAX_PATH];
resolve_path(cwd, target, resolved, sizeof(resolved));
strncpy(redirs[*nredirs].path, resolved, VFS_MAX_PATH - 1);
redirs[*nredirs].path[VFS_MAX_PATH - 1] = '\0';
(*nredirs)++;
}
}
argv[new_argc] = NULL;
*argc = new_argc;
return 0;
}
static int run_single(char *line) {
char expanded[LINE_MAX];
expand_vars(line, expanded, sizeof(expanded));
char buf[LINE_MAX];
strncpy(buf, expanded, LINE_MAX - 1);
char *argv[MAX_ARGS];
int argc = tokenize(buf, argv, MAX_ARGS);
if (argc < 0) { fputs(C_RED "syntax error: unclosed quote\n" C_RESET, stdout); return 1; }
if (!argc) return 0;
redir_t redirs[8];
int nredirs = 0;
if (parse_redirects(argv, &argc, redirs, 8, &nredirs) < 0) return 1;
if (!argc) return 0;
const char *cmd = argv[0];
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
if (strcmp(cmd, "exit") == 0) { fputs("Goodbye!\n", stdout); exit(0); }
if (strcmp(cmd, "cd") == 0) return cmd_cd(argc > 1 ? argv[1] : NULL);
if (strcmp(cmd, "export") == 0) return cmd_export(argc, argv);
if (strcmp(cmd, "unset") == 0) return cmd_unset(argc, argv);
char binpath[VFS_MAX_PATH];
if (cmd[0] == '/') {
strncpy(binpath, cmd, sizeof(binpath) - 1);
binpath[sizeof(binpath) - 1] = '\0';
} else if (cmd[0] == '.') {
resolve_path(cwd, cmd, binpath, sizeof(binpath));
} else {
if (!find_in_path(cmd, binpath, sizeof(binpath))) {
char t_cwd[VFS_MAX_PATH];
resolve_path(cwd, cmd, t_cwd, sizeof(t_cwd));
struct stat st;
if (stat(t_cwd, &st) == 0 && st.st_type != 1) {
strncpy(binpath, t_cwd, sizeof(binpath) - 1);
binpath[sizeof(binpath) - 1] = '\0';
} else {
fputs(C_RED "not found: " C_RESET, stdout); fputs(cmd, stdout); putchar(10); return 127;
}
}
}
#define REAL_ARGV_MAX (MAX_ARGS + ENV_MAX_VARS + 4)
char *real_argv_buf[REAL_ARGV_MAX];
static char _cwd_flag[VFS_MAX_PATH + 8];
static char _env_flags[ENV_MAX_VARS][ENV_NAME_MAX + ENV_VAL_MAX + 8];
int ri = 0;
real_argv_buf[ri++] = binpath;
for (int i = 1; i < argc; i++) real_argv_buf[ri++] = argv[i];
snprintf(_cwd_flag, sizeof(_cwd_flag), "--cwd=%s", cwd);
real_argv_buf[ri++] = _cwd_flag;
for (int ei = 0; ei < g_env_count && ri < REAL_ARGV_MAX - 1; ei++) {
snprintf(_env_flags[ei], sizeof(_env_flags[ei]), "--env:%s=%s",
g_env[ei].name, g_env[ei].value);
real_argv_buf[ri++] = _env_flags[ei];
}
real_argv_buf[ri] = NULL;
pid_t child = fork();
if (child < 0) { fputs(C_RED "fork failed" C_RESET "\n", stdout); return 1; }
if (child == 0) {
for (int i = 0; i < nredirs; i++) {
int fd = -1;
int target_fd = -1;
if (redirs[i].type == REDIR_OUT) {
fd = open(redirs[i].path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
target_fd = 1;
} else if (redirs[i].type == REDIR_APPEND) {
fd = open(redirs[i].path, O_WRONLY | O_CREAT | O_APPEND, 0644);
target_fd = 1;
} else if (redirs[i].type == REDIR_IN) {
fd = open(redirs[i].path, O_RDONLY, 0);
target_fd = 0;
}
if (fd < 0) {
fputs(C_RED "redirect: cannot open: " C_RESET, stdout);
fputs(redirs[i].path, stdout);
putchar('\n');
exit(1);
}
dup2(fd, target_fd);
close(fd);
}
execve(binpath, (char *const *)real_argv_buf, NULL);
fputs(C_RED "exec failed: " C_RESET, stdout); fputs(binpath, stdout); putchar(10); exit(127);
}
int status = 0;
waitpid(child, &status, 0);
return (status >> 8) & 0xFF;
}
typedef enum { CH_NONE = 0, CH_SEQ, CH_AND, CH_OR } chain_t;
static void run_command(char *line) {
char work[LINE_MAX];
strncpy(work, line, LINE_MAX - 1);
char *segs[64]; chain_t ops[64]; int ns = 1;
segs[0] = work; ops[0] = CH_NONE;
char *p = work;
while (*p) {
if (*p == '"') { p++; while (*p && *p != '"') p++; if (*p) p++; continue; }
if (*p == '\'') { p++; while (*p && *p != '\'') p++; if (*p) p++; continue; }
if (*p == '&' && *(p+1) == '&') { *p='\0'; p+=2; while(isspace((unsigned char)*p))p++; ops[ns]=CH_AND; segs[ns]=p; ns++; continue; }
if (*p == '|' && *(p+1) == '|') { *p='\0'; p+=2; while(isspace((unsigned char)*p))p++; ops[ns]=CH_OR; segs[ns]=p; ns++; continue; }
if (*p == ';') { *p='\0'; p++; while(isspace((unsigned char)*p))p++; ops[ns]=CH_SEQ; segs[ns]=p; ns++; continue; }
p++;
}
int rc = 0;
for (int i = 0; i < ns; i++) {
char *s = segs[i];
while (isspace((unsigned char)*s)) s++;
size_t sl = strlen(s);
while (sl > 0 && isspace((unsigned char)s[sl - 1])) s[--sl] = '\0';
if (!s[0]) continue;
if (i > 0) {
if (ops[i] == CH_AND && rc != 0) continue;
if (ops[i] == CH_OR && rc == 0) continue;
}
rc = run_single(s);
}
g_last_rc = rc;
}
static void print_motd(void) {
int fd = open("/mnt/etc/motd", O_RDONLY, 0);
if (fd < 0) fd = open("/etc/motd", O_RDONLY, 0);
if (fd < 0) { putchar(10); fputs(" Cervus OS v0.0.2\n Type 'help' for commands.\n", stdout); putchar(10); return; }
char buf[1024];
ssize_t n = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (n > 0) { buf[n] = '\0'; write(1, buf, n); }
}
static int g_installed = 0;
static int sys_disk_mount(const char *dev, const char *path) {
return (int)syscall2(SYS_DISK_MOUNT, dev, path);
}
static int launch_installer(void) {
const char *path = "/bin/install-on-disk";
struct stat st;
if (stat(path, &st) != 0) {
fputs(C_RED " install-on-disk not found on system.\n" C_RESET, stdout);
return -1;
}
const char *argv[4];
argv[0] = path;
argv[1] = "--env:MODE=live";
argv[2] = "--cwd=/";
argv[3] = NULL;
pid_t child = fork();
if (child < 0) {
fputs(C_RED " fork failed\n" C_RESET, stdout);
return -1;
}
if (child == 0) {
execve(path, (char *const *)argv, NULL);
fputs(C_RED " exec install-on-disk failed\n" C_RESET, stdout);
exit(127);
}
int status = 0;
waitpid(child, &status, 0);
return (status >> 8) & 0xFF;
}
static int ask_install_or_live(void) {
fputs("\x1b[2J\x1b[H", stdout);
fputs("\n", stdout);
fputs(C_CYAN " Cervus OS" C_RESET " - Live ISO\n", stdout);
fputs(C_GRAY " -----------------------------------" C_RESET "\n\n", stdout);
fputs(" A disk has been detected on this machine.\n", stdout);
fputs(" What would you like to do?\n\n", stdout);
fputs(" [" C_BOLD "1" C_RESET "] Install Cervus to disk\n", stdout);
fputs(" [" C_BOLD "2" C_RESET "] Continue in Live mode\n\n", stdout);
fputs(" Choice [1-2]: ", stdout);
char c = 0;
while (1) {
if (read(0, &c, 1) <= 0) continue;
if (c == '1' || c == '2') { putchar(c); putchar(10); break; }
}
return (c == '1') ? 1 : 0;
}
int main(int argc, char **argv) {
(void)argc; (void)argv;
struct stat dev_st;
int has_disk = (stat("/dev/hda", &dev_st) == 0);
int has_hda2 = (stat("/dev/hda2", &dev_st) == 0);
int has_hda_legacy = 0;
int disk_mounted = 0;
if (has_hda2) {
int mr = sys_disk_mount("hda2", "/mnt");
if (mr == 0) {
disk_mounted = 1;
g_installed = 1;
}
} else if (has_disk) {
int mr = sys_disk_mount("hda", "/mnt");
if (mr == 0) {
disk_mounted = 1;
g_installed = 1;
has_hda_legacy = 1;
}
}
(void)has_hda_legacy;
if (!disk_mounted && has_disk) {
if (ask_install_or_live() == 1) {
launch_installer();
struct stat retry_st;
if (stat("/dev/hda2", &retry_st) == 0) {
if (sys_disk_mount("hda2", "/mnt") == 0) {
disk_mounted = 1;
g_installed = 1;
}
}
}
}
if (disk_mounted) {
strncpy(cwd, "/mnt/home", sizeof(cwd));
env_set("HOME", "/mnt/home");
env_set("PATH", "/mnt/bin:/mnt/apps:/mnt/usr/bin");
env_set("SHELL", "/mnt/bin/shell");
} else {
strncpy(cwd, "/", sizeof(cwd));
env_set("HOME", "/");
env_set("PATH", "/bin:/apps:/usr/bin");
env_set("SHELL", "/bin/shell");
}
if (!disk_mounted && has_disk) env_set("MODE", "live");
else if (!has_disk) env_set("MODE", "live");
else env_set("MODE", "installed");
print_motd();
{
static char hist_path[VFS_MAX_PATH];
const char *h = env_get("HOME");
if (h && h[0]) {
path_join(h, ".history", hist_path, sizeof(hist_path));
g_hist_file = hist_path;
hist_load(hist_path);
}
}
if (!has_disk) {
fputs(C_YELLOW " [Live Mode]" C_RESET " No disk detected. All changes are in RAM.\n\n", stdout);
} else if (!disk_mounted) {
fputs(C_YELLOW " [Live Mode]" C_RESET " Disk not mounted.\n\n", stdout);
}
char line[LINE_MAX];
for (;;) {
print_prompt();
int n = readline_edit(line, LINE_MAX);
if (n < 0) {
fputs("\nSession ended. Restarting shell...\n", stdout);
const char *h = env_get("HOME");
strncpy(cwd, (h && h[0]) ? h : "/", sizeof(cwd));
print_motd();
continue;
}
int len = (int)strlen(line);
while (len > 0 && isspace((unsigned char)line[len - 1])) line[--len] = '\0';
if (len > 0) { hist_push(line); run_command(line); }
}
}
+91
View File
@@ -0,0 +1,91 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/cervus.h>
static int failed = 0;
static void ok(const char *s) { printf(" [OK] %s\n", s); }
static void fail(const char *s) { printf(" [FAIL] %s\n", s); failed = 1; }
static int file_exists(const char *path)
{
struct stat st;
return stat(path, &st) == 0;
}
static const char *resolve_app(const char *name, char *buf, size_t bufsz)
{
const char *prefixes[] = { "/mnt/apps/", "/apps/", "/mnt/bin/", "/bin/", NULL };
for (int i = 0; prefixes[i]; i++) {
size_t pl = strlen(prefixes[i]);
size_t nl = strlen(name);
if (pl + nl + 1 > bufsz) continue;
memcpy(buf, prefixes[i], pl);
memcpy(buf + pl, name, nl + 1);
if (file_exists(buf)) return buf;
}
return NULL;
}
int main(int argc, char **argv)
{
(void)argc; (void)argv;
puts("--- test_execve ---");
char target[256];
if (!resolve_app("execve_target", target, sizeof(target))) {
printf(" [SKIP] execve_target not found\n");
puts("--- test_execve done ---");
return 0;
}
{
char *const cargv[] = { target, (char *)"hello", (char *)"from", (char *)"execve", NULL };
pid_t child = fork();
if (child < 0) { fail("fork"); return 1; }
if (child == 0) {
execve(target, cargv, NULL);
printf(" [FATAL] execve failed\n");
return 127;
}
int status = 0;
waitpid(child, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 99) ok("execve target exits 99");
else {
printf(" exit status = %d\n", WEXITSTATUS(status));
fail("execve exit code");
}
}
{
pid_t child = fork();
if (child < 0) { fail("fork"); return 1; }
if (child == 0) {
char *const cargv[] = { (char *)"/no/such/binary", NULL };
int r = execve("/no/such/binary", cargv, NULL);
return (r < 0) ? 0 : 1;
}
int status = 0;
waitpid(child, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) ok("execve nonexistent returns error");
else fail("execve nonexistent should return error");
}
{
pid_t child = fork();
if (child < 0) { fail("fork"); return 1; }
if (child == 0) {
execve(target, NULL, NULL);
return 127;
}
int status = 0;
waitpid(child, &status, 0);
if (WIFEXITED(status) && WEXITSTATUS(status) == 99) ok("execve with NULL argv");
else fail("execve NULL argv");
}
puts("--- test_execve done ---");
return failed ? 1 : 0;
}
+164
View File
@@ -0,0 +1,164 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
static int failed = 0;
static void ok(const char *s) { printf(" [OK] %s\n", s); }
static void fail(const char *s) { printf(" [FAIL] %s\n", s); failed = 1; }
static int file_exists(const char *path)
{
struct stat st;
return stat(path, &st) == 0;
}
static const char *resolve_etc(const char *name, char *buf, size_t bufsz)
{
const char *prefixes[] = { "/mnt/etc/", "/etc/", NULL };
for (int i = 0; prefixes[i]; i++) {
size_t pl = strlen(prefixes[i]);
size_t nl = strlen(name);
if (pl + nl + 1 > bufsz) continue;
memcpy(buf, prefixes[i], pl);
memcpy(buf + pl, name, nl + 1);
if (file_exists(buf)) return buf;
}
return NULL;
}
int main(int argc, char **argv)
{
(void)argc; (void)argv;
puts("--- test_files ---");
char hostname_path[256], passwd_path[256];
const char *hostname = resolve_etc("hostname", hostname_path, sizeof(hostname_path));
const char *passwd = resolve_etc("passwd", passwd_path, sizeof(passwd_path));
if (!hostname) printf(" [WARN] hostname not found in /etc or /mnt/etc\n");
if (!passwd) printf(" [WARN] passwd not found in /etc or /mnt/etc\n");
{
const char *msg = "hello from test_files\n";
ssize_t w = write(1, msg, strlen(msg));
if (w == (ssize_t)strlen(msg)) ok("write to stdout");
else fail("write to stdout");
}
{
const char *msg = "stderr line\n";
ssize_t w = write(2, msg, strlen(msg));
if (w == (ssize_t)strlen(msg)) ok("write to stderr");
else fail("write to stderr");
}
if (hostname) {
int fd = open(hostname, O_RDONLY);
if (fd >= 0) {
ok("open hostname");
char buf[64];
memset(buf, 0, sizeof(buf));
ssize_t r = read(fd, buf, sizeof(buf) - 1);
if (r > 0) { printf(" hostname = '%s'\n", buf); ok("read hostname"); }
else fail("read hostname");
struct stat st;
if (fstat(fd, &st) == 0) {
printf(" fstat: size=%lu\n", (unsigned long)st.st_size);
ok("fstat");
} else fail("fstat");
close(fd);
ok("close");
} else fail("open hostname");
}
if (passwd) {
struct stat st;
if (stat(passwd, &st) == 0) {
printf(" stat: ino=%lu size=%lu\n",
(unsigned long)st.st_ino, (unsigned long)st.st_size);
ok("stat passwd");
} else fail("stat passwd");
}
{
int fd = open("/no/such/file", O_RDONLY);
if (fd < 0) ok("open nonexistent returns error");
else { close(fd); fail("open nonexistent should fail"); }
}
if (hostname) {
int fd = open(hostname, O_RDONLY);
if (fd >= 0) {
char buf[4]; memset(buf, 0, sizeof(buf));
read(fd, buf, 2);
off_t pos = lseek(fd, 0, SEEK_CUR);
if (pos == 2) ok("lseek SEEK_CUR");
else fail("lseek SEEK_CUR");
lseek(fd, 0, SEEK_SET);
char buf2[4]; memset(buf2, 0, sizeof(buf2));
read(fd, buf2, 2);
if (buf[0] == buf2[0] && buf[1] == buf2[1]) ok("lseek SEEK_SET re-read");
else fail("lseek SEEK_SET re-read");
close(fd);
} else fail("open for lseek test");
}
if (hostname) {
int fd = open(hostname, O_RDONLY);
if (fd >= 0) {
int fd2 = dup(fd);
if (fd2 >= 0 && fd2 != fd) {
ok("dup");
char buf[32]; memset(buf, 0, sizeof(buf));
ssize_t r = read(fd2, buf, sizeof(buf) - 1);
if (r > 0) ok("read from dup'd fd");
else fail("read from dup'd fd");
close(fd2);
} else fail("dup");
close(fd);
} else fail("open for dup test");
}
if (hostname) {
int fd = open(hostname, O_RDONLY);
if (fd >= 0) {
int r = dup2(fd, 10);
if (r == 10) {
ok("dup2");
char buf[32]; memset(buf, 0, sizeof(buf));
ssize_t n = read(10, buf, sizeof(buf) - 1);
if (n > 0) ok("read from dup2 fd");
else fail("read from dup2 fd");
close(10);
} else fail("dup2");
close(fd);
} else fail("open for dup2 test");
}
{
int fd = open("/dev/null", O_WRONLY);
if (fd >= 0) {
ssize_t w = write(fd, "garbage", 7);
if (w == 7) ok("write to /dev/null");
else fail("write to /dev/null");
close(fd);
} else fail("open /dev/null");
}
{
int fd = open("/dev/zero", O_RDONLY);
if (fd >= 0) {
char buf[8]; memset(buf, 0xFF, 8);
ssize_t r = read(fd, buf, 8);
int all_zero = 1;
for (int i = 0; i < 8; i++) if (buf[i]) all_zero = 0;
if (r == 8 && all_zero) ok("read from /dev/zero");
else fail("read from /dev/zero");
close(fd);
} else fail("open /dev/zero");
}
puts("--- test_files done ---");
return failed ? 1 : 0;
}
+78
View File
@@ -0,0 +1,78 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/cervus.h>
static int failed = 0;
static void ok(const char *s) { printf(" [OK] %s\n", s); }
static void fail(const char *s) { printf(" [FAIL] %s\n", s); failed = 1; }
int main(int argc, char **argv)
{
(void)argc; (void)argv;
puts("--- test_mem ---");
void *brk0 = sbrk(0);
printf(" initial brk = %p\n", brk0);
if ((uintptr_t)brk0 > 0) ok("sbrk(0) returns valid address");
else fail("sbrk(0)");
void *old = sbrk(4096);
if (old == brk0) ok("sbrk(4096) returns old brk");
else fail("sbrk(4096)");
volatile uint8_t *heap = (volatile uint8_t *)brk0;
heap[0] = 0xAB; heap[4095] = 0xCD;
if (heap[0] == 0xAB && heap[4095] == 0xCD) ok("write/read heap page");
else fail("write/read heap page");
void *cur = sbrk(0);
sbrk(-4096);
void *after = sbrk(0);
if ((uintptr_t)after == (uintptr_t)cur - 4096) ok("sbrk shrink");
else fail("sbrk shrink");
void *m = mmap(NULL, 8192, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (m != MAP_FAILED) {
ok("mmap anonymous 8 KiB");
volatile uint8_t *p = (volatile uint8_t *)m;
p[0] = 0x11; p[8191] = 0x22;
if (p[0] == 0x11 && p[8191] == 0x22) ok("write/read mmap pages");
else fail("write/read mmap pages");
if (munmap(m, 8192) == 0) ok("munmap");
else fail("munmap");
} else fail("mmap anonymous");
void *big = mmap(NULL, 1024 * 1024, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (big != MAP_FAILED) {
ok("mmap 1 MiB");
volatile uint8_t *bp = (volatile uint8_t *)big;
int ok_all = 1;
for (int i = 0; i < 256; i++) {
bp[i * 4096] = (uint8_t)i;
if (bp[i * 4096] != (uint8_t)i) ok_all = 0;
}
if (ok_all) ok("touch 256 pages of 1 MiB mmap");
else fail("touch mmap pages");
munmap(big, 1024 * 1024);
} else fail("mmap 1 MiB");
{
void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr != MAP_FAILED) {
munmap(addr, 4096);
void *fixed = mmap(addr, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (fixed == addr) ok("MAP_FIXED at specific address");
else fail("MAP_FIXED");
if (fixed != MAP_FAILED) munmap(fixed, 4096);
}
}
puts("--- test_mem done ---");
return failed ? 1 : 0;
}
+82
View File
@@ -0,0 +1,82 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/cervus.h>
static int failed = 0;
static void ok(const char *s) { printf(" [OK] %s\n", s); }
static void fail(const char *s) { printf(" [FAIL] %s\n", s); failed = 1; }
int main(int argc, char **argv)
{
(void)argc; (void)argv;
puts("--- test_pipe ---");
int fds[2] = { -1, -1 };
if (pipe(fds) == 0 && fds[0] >= 0 && fds[1] >= 0) ok("pipe() creates two fds");
else { fail("pipe() failed"); return 1; }
printf(" read_fd=%d write_fd=%d\n", fds[0], fds[1]);
{
const char *msg = "hello pipe";
ssize_t w = write(fds[1], msg, strlen(msg));
if (w == (ssize_t)strlen(msg)) ok("write to pipe");
else fail("write to pipe");
char buf[32];
memset(buf, 0, sizeof(buf));
ssize_t r = read(fds[0], buf, sizeof(buf) - 1);
if (r == (ssize_t)strlen(msg) && strcmp(buf, msg) == 0)
ok("read from pipe matches written data");
else fail("read from pipe");
}
close(fds[0]); close(fds[1]);
{
int p[2];
if (pipe(p) != 0) { fail("pipe for fork test"); return 1; }
pid_t child = fork();
if (child < 0) { fail("fork"); return 1; }
if (child == 0) {
close(p[0]);
const char *s = "world";
write(p[1], s, strlen(s));
close(p[1]);
return 0;
}
close(p[1]);
char buf[32]; memset(buf, 0, sizeof(buf));
ssize_t r = read(p[0], buf, sizeof(buf) - 1);
close(p[0]);
int status = 0; waitpid(child, &status, 0);
if (r > 0 && strcmp(buf, "world") == 0) ok("pipe IPC parent<-child");
else fail("pipe IPC");
}
{
int p[2];
if (pipe(p) != 0) { fail("pipe for dup2 test"); return 1; }
pid_t child = fork();
if (child < 0) { fail("fork for dup2"); return 1; }
if (child == 0) {
close(p[0]);
dup2(p[1], 1);
close(p[1]);
const char *s = "from child stdout\n";
write(1, s, strlen(s));
return 0;
}
close(p[1]);
char buf[64]; memset(buf, 0, sizeof(buf));
ssize_t r = read(p[0], buf, sizeof(buf) - 1);
close(p[0]);
int status = 0; waitpid(child, &status, 0);
if (r > 0 && strncmp(buf, "from child stdout", 17) == 0)
ok("dup2 redirects child stdout through pipe");
else fail("dup2 redirect");
}
puts("--- test_pipe done ---");
return failed ? 1 : 0;
}
+48
View File
@@ -0,0 +1,48 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/cervus.h>
static int failed = 0;
static void ok(const char *s) { printf(" [OK] %s\n", s); }
static void fail(const char *s) { printf(" [FAIL] %s\n", s); failed = 1; }
int main(int argc, char **argv)
{
(void)argc; (void)argv;
puts("--- test_process ---");
pid_t my_pid = getpid();
if (my_pid > 0) ok("getpid > 0"); else fail("getpid > 0");
pid_t my_ppid = getppid();
if (my_ppid > 0) ok("getppid > 0"); else fail("getppid > 0");
pid_t child = fork();
if (child < 0) { fail("fork"); return 1; }
if (child == 0) {
if (getppid() == my_pid) ok("child: ppid == parent pid");
else fail("child: ppid == parent pid");
return 42;
}
int status = 0;
pid_t reaped = waitpid(child, &status, 0);
if (reaped == child) ok("waitpid returns child pid");
else fail("waitpid returns child pid");
if (WIFEXITED(status)) ok("WIFEXITED");
else fail("WIFEXITED");
if (WEXITSTATUS(status) == 42) ok("exit code 42");
else { printf(" exit code = %d\n", WEXITSTATUS(status)); fail("exit code 42"); }
pid_t r = waitpid(-1, &status, WNOHANG);
if (r == 0) ok("WNOHANG returns 0 when no zombie");
else fail("WNOHANG returns 0 when no zombie");
if (getuid() == 0) ok("getuid == 0 (root)");
else fail("getuid == 0");
puts("--- test_process done ---");
return failed ? 1 : 0;
}
+22
View File
@@ -0,0 +1,22 @@
#include <stdio.h>
#include <sys/cervus.h>
int main(int argc, char **argv)
{
(void)argc; (void)argv;
uint64_t ns = cervus_uptime_ns();
uint64_t total_s = ns / 1000000000ULL;
uint64_t ms = (ns / 1000000ULL) % 1000ULL;
uint64_t secs = total_s % 60;
uint64_t mins = (total_s / 60) % 60;
uint64_t hours = (total_s / 3600) % 24;
uint64_t days = total_s / 86400;
fputs(" Uptime: ", stdout);
if (days > 0)
printf("%lu day%s, ", (unsigned long)days, days != 1 ? "s" : "");
printf("%02lu:%02lu:%02lu (%lus %lums)\n",
(unsigned long)hours, (unsigned long)mins, (unsigned long)secs,
(unsigned long)total_s, (unsigned long)ms);
return 0;
}
+592
View File
@@ -0,0 +1,592 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/cervus.h>
#include <sys/syscall.h>
#include <cervus_util.h>
#define MAX_ENTRIES 128
#define MBR_TYPE_FAT32_LBA 0x0C
#define MBR_TYPE_LINUX 0x83
#define MBR_TYPE_LINUX_SWAP 0x82
typedef struct {
char name[32];
char model[41];
uint64_t sectors;
uint64_t size_bytes;
} disk_summary_t;
typedef struct {
char name[64];
uint8_t type;
} dir_entry_t;
static void safe_strcpy(char *dst, size_t dsz, const char *src)
{
if (!dst || dsz == 0) return;
if (!src) { dst[0] = '\0'; return; }
size_t i = 0;
while (i + 1 < dsz && src[i]) { dst[i] = src[i]; i++; }
dst[i] = '\0';
}
static void clear_screen(void)
{
fputs("\x1b[2J\x1b[H", stdout);
}
static int ensure_dir(const char *path)
{
struct stat st;
if (stat(path, &st) == 0) return 0;
return mkdir(path, 0755);
}
static int ensure_parent_dir(const char *path)
{
char tmp[512];
size_t len = strlen(path);
if (len >= sizeof(tmp)) return -1;
int last_slash = -1;
for (int i = (int)len - 1; i >= 0; i--) {
if (path[i] == '/') { last_slash = i; break; }
}
if (last_slash <= 0) return 0;
for (int i = 0; i < last_slash; i++) tmp[i] = path[i];
tmp[last_slash] = '\0';
int depth = 0;
int starts[32];
starts[depth++] = 0;
for (int i = 1; i < last_slash && depth < 32; i++) {
if (tmp[i] == '/') starts[depth++] = i;
}
for (int d = 0; d < depth; d++) {
int end = (d + 1 < depth) ? starts[d + 1] : last_slash;
char part[512];
for (int i = 0; i < end; i++) part[i] = tmp[i];
part[end] = '\0';
if (part[0] == '\0') continue;
struct stat st;
if (stat(part, &st) != 0) syscall2(SYS_MKDIR, part, 0755);
}
return 0;
}
static int copy_one_file_progress(const char *src, const char *dst, const char *display_name)
{
int sfd = open(src, O_RDONLY, 0);
if (sfd < 0) return sfd;
ensure_parent_dir(dst);
int dfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0755);
if (dfd < 0) { close(sfd); return dfd; }
struct stat st;
uint64_t total = 0;
if (stat(src, &st) == 0) total = st.st_size;
static char fbuf[4096];
ssize_t n;
int rc = 0;
uint64_t written = 0;
int last_pct = -1;
int spinner = 0;
while ((n = read(sfd, fbuf, sizeof(fbuf))) > 0) {
ssize_t w = write(dfd, fbuf, (size_t)n);
if (w < 0) { rc = (int)w; break; }
written += (uint64_t)w;
if (total > 0 && display_name) {
int pct = (int)((written * 100) / total);
if (pct != last_pct) {
static const char glyphs[4] = { '|', '/', '-', '\\' };
fputs("\r\033[K ", stdout);
putchar(glyphs[spinner & 3]);
fputs(" ", stdout);
fputs(display_name, stdout);
fputs(" ", stdout);
char pb[8];
int bi = 0;
if (pct >= 100) { pb[bi++]='1'; pb[bi++]='0'; pb[bi++]='0'; }
else if (pct >= 10) { pb[bi++]=(char)('0'+pct/10); pb[bi++]=(char)('0'+pct%10); }
else { pb[bi++]=(char)('0'+pct); }
pb[bi++]='%';
pb[bi]='\0';
fputs(pb, stdout);
spinner++;
last_pct = pct;
}
}
}
close(sfd);
close(dfd);
if (display_name) {
fputs("\r\033[K ", stdout);
fputs(display_name, stdout);
fputs(rc < 0 ? " FAILED\n" : " done\n", stdout);
}
return rc;
}
static int copy_one_file(const char *src, const char *dst)
{
return copy_one_file_progress(src, dst, NULL);
}
static int read_dir_entries(const char *path, dir_entry_t *out, int max)
{
DIR *d = opendir(path);
if (!d) return 0;
int count = 0;
struct dirent *de;
while (count < max && (de = readdir(d)) != NULL) {
if (de->d_name[0] == '.' && (de->d_name[1] == '\0' ||
(de->d_name[1] == '.' && de->d_name[2] == '\0'))) continue;
safe_strcpy(out[count].name, sizeof(out[count].name), de->d_name);
out[count].type = de->d_type;
count++;
}
closedir(d);
return count;
}
static int should_exclude_from_copy(const char *name)
{
if (strcmp(name, "install-on-disk") == 0) return 1;
return 0;
}
static void copy_tree(const char *src_dir, const char *dst_dir)
{
dir_entry_t entries[MAX_ENTRIES];
int n = read_dir_entries(src_dir, entries, MAX_ENTRIES);
if (n == 0) return;
ensure_dir(dst_dir);
for (int i = 0; i < n; i++) {
if (should_exclude_from_copy(entries[i].name)) {
fputs(C_GRAY " skip (installer): " C_RESET, stdout);
fputs(entries[i].name, stdout);
putchar(10);
continue;
}
char sp[256], dp[256];
path_join(src_dir, entries[i].name, sp, sizeof(sp));
path_join(dst_dir, entries[i].name, dp, sizeof(dp));
if (entries[i].type == 1) {
ensure_dir(dp);
copy_tree(sp, dp);
} else {
copy_one_file_progress(sp, dp, sp);
}
}
}
static int list_disks(disk_summary_t out[4])
{
int found = 0;
for (int i = 0; i < 4; i++) {
struct {
char name[32];
uint64_t sectors;
uint64_t size_bytes;
char model[41];
uint8_t present;
uint8_t _pad[6];
} info;
memset(&info, 0, sizeof(info));
int r = (int)syscall2(SYS_DISK_INFO, (uint64_t)i, (uint64_t)&info);
if (r < 0 || !info.present) continue;
size_t nlen = strlen(info.name);
int is_part = 0;
for (size_t k = 0; k < nlen; k++) {
if (info.name[k] >= '0' && info.name[k] <= '9') { is_part = 1; break; }
}
if (is_part) continue;
safe_strcpy(out[found].name, sizeof(out[found].name), info.name);
safe_strcpy(out[found].model, sizeof(out[found].model), info.model);
out[found].sectors = info.sectors;
out[found].size_bytes = info.size_bytes;
found++;
if (found >= 4) break;
}
return found;
}
static int ask_choose_disk(char *out_name, size_t out_cap)
{
disk_summary_t disks[4];
int n = list_disks(disks);
if (n == 0) {
fputs(C_RED " No disks detected!" C_RESET "\n", stdout);
return -1;
}
fputs("\n", stdout);
fputs(C_CYAN " Available disks:" C_RESET "\n", stdout);
for (int i = 0; i < n; i++) {
fputs(" ", stdout);
putchar((char)('1' + i));
fputs(") ", stdout);
fputs(C_BOLD, stdout);
fputs(disks[i].name, stdout);
fputs(C_RESET, stdout);
fputs(" ", stdout);
uint64_t mb = disks[i].size_bytes / (1024 * 1024);
char buf[32];
int bi = 0;
if (mb == 0) {
buf[bi++] = '0';
} else {
char rev[32];
int ri = 0;
uint64_t v = mb;
while (v) { rev[ri++] = (char)('0' + (v % 10)); v /= 10; }
while (ri) buf[bi++] = rev[--ri];
}
buf[bi] = '\0';
fputs(buf, stdout);
fputs(" MB ", stdout);
fputs(disks[i].model, stdout);
fputs("\n", stdout);
}
fputs("\n Select disk [1-", stdout);
putchar((char)('0' + n));
fputs("] (q to cancel): ", stdout);
char c = 0;
while (1) {
if (read(0, &c, 1) <= 0) continue;
if (c >= '1' && c <= (char)('0' + n)) { putchar(c); putchar(10); break; }
if (c == 'q' || c == 'Q') { putchar(c); putchar(10); return -1; }
}
int idx = c - '1';
safe_strcpy(out_name, out_cap, disks[idx].name);
return 0;
}
static void progress_done(const char *msg)
{
putchar('\r');
fputs(C_GREEN " ", stdout);
fputs(msg, stdout);
fputs(C_RESET " \n", stdout);
}
static void progress_fail(const char *msg)
{
putchar('\r');
fputs(C_RED " ", stdout);
fputs(msg, stdout);
fputs(C_RESET " \n", stdout);
}
static int write_limine_conf(const char *path)
{
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) return fd;
const char *conf =
"timeout: 5\n"
"default_entry: 1\n"
"interface_branding: Cervus\n"
"wallpaper: boot():/boot/wallpaper.png\n"
"\n"
"/Cervus v0.0.2 Alpha\n"
" protocol: limine\n"
" path: boot():/kernel\n"
" module_path: boot():/shell.elf\n"
" module_cmdline: init\n";
write(fd, conf, strlen(conf));
close(fd);
return 0;
}
static int do_install(void)
{
clear_screen();
fputs(C_CYAN " Cervus OS Installer" C_RESET "\n", stdout);
fputs(C_GRAY " -----------------------------------" C_RESET "\n", stdout);
char chosen_disk_name[32];
if (ask_choose_disk(chosen_disk_name, sizeof(chosen_disk_name)) < 0) {
fputs("\n Cancelled.\n\n", stdout);
return 1;
}
fputs("\n Target disk: " C_BOLD, stdout);
fputs(chosen_disk_name, stdout);
fputs(C_RESET "\n", stdout);
fputs(" Layout: ESP (FAT32, 64 MB) + root (ext2) + swap (16 MB)\n", stdout);
fputs("\n", stdout);
fputs(C_RED " WARNING: This will erase ALL data on " C_RESET, stdout);
fputs(C_BOLD, stdout);
fputs(chosen_disk_name, stdout);
fputs(C_RESET C_RED "!" C_RESET "\n\n", stdout);
fputs(" Continue? [y/n]: ", stdout);
char c = 0;
while (1) {
if (read(0, &c, 1) <= 0) continue;
if (c == 'y' || c == 'Y' || c == 'n' || c == 'N') { putchar(c); putchar(10); break; }
}
if (c == 'n' || c == 'N') {
fputs("\n Cancelled.\n\n", stdout);
return 1;
}
disk_summary_t disks[4];
int n_disks = list_disks(disks);
uint64_t total_sectors = 0;
for (int i = 0; i < n_disks; i++) {
if (strcmp(disks[i].name, chosen_disk_name) == 0) {
total_sectors = disks[i].sectors;
break;
}
}
if (total_sectors < 300000) {
fputs(C_RED " Disk too small (need at least 150 MB)" C_RESET "\n\n", stdout);
return 1;
}
uint32_t esp_start = 2048;
uint32_t esp_size = 131072;
uint32_t swap_size = 32768;
uint32_t root_start = esp_start + esp_size;
uint32_t avail = (uint32_t)total_sectors - root_start - swap_size;
uint32_t root_size = avail;
uint32_t swap_start = root_start + root_size;
fputs("\n [1/8] Writing partition table...\n", stdout);
cervus_mbr_part_t specs[4];
memset(specs, 0, sizeof(specs));
specs[0].boot_flag = 1;
specs[0].type = MBR_TYPE_FAT32_LBA;
specs[0].lba_start = esp_start;
specs[0].sector_count = esp_size;
specs[1].boot_flag = 0;
specs[1].type = MBR_TYPE_LINUX;
specs[1].lba_start = root_start;
specs[1].sector_count = root_size;
specs[2].boot_flag = 0;
specs[2].type = MBR_TYPE_LINUX_SWAP;
specs[2].lba_start = swap_start;
specs[2].sector_count = swap_size;
if (cervus_disk_partition(chosen_disk_name, specs, 3) < 0) {
progress_fail("Failed to write partition table!");
return 1;
}
progress_done("partition table written");
char part1[32], part2[32];
snprintf(part1, sizeof(part1), "%s1", chosen_disk_name);
snprintf(part2, sizeof(part2), "%s2", chosen_disk_name);
fputs(" [2/8] Formatting ", stdout);
fputs(part1, stdout);
fputs(" as FAT32 (ESP)...\n", stdout);
if (cervus_disk_mkfs_fat32(part1, "CERVUS-ESP") < 0) {
progress_fail("mkfs.fat32 failed!");
return 1;
}
progress_done("FAT32 ESP created");
fputs(" [3/8] Formatting ", stdout);
fputs(part2, stdout);
fputs(" as ext2 (root)...\n", stdout);
if (cervus_disk_format(part2, "cervus-root") < 0) {
progress_fail("mkfs.ext2 failed!");
return 1;
}
progress_done("ext2 root created");
fputs(" [4/8] Mounting partitions...\n", stdout);
ensure_dir("/mnt/esp");
ensure_dir("/mnt/root");
if (cervus_disk_mount(part1, "/mnt/esp") < 0) {
progress_fail("mount ESP failed");
return 1;
}
if (cervus_disk_mount(part2, "/mnt/root") < 0) {
progress_fail("mount root failed");
cervus_disk_umount("/mnt/esp");
return 1;
}
progress_done("mounted");
fputs(" [5/8] Copying boot files to ESP...\n", stdout);
ensure_dir("/mnt/esp/boot");
ensure_dir("/mnt/esp/boot/limine");
ensure_dir("/mnt/esp/EFI");
ensure_dir("/mnt/esp/EFI/BOOT");
struct { const char *src; const char *dst; int required; } boot_files[] = {
{ "/boot/kernel", "/mnt/esp/boot/kernel", 1 },
{ "/boot/kernel", "/mnt/esp/kernel", 0 },
{ "/boot/shell.elf", "/mnt/esp/boot/shell.elf", 1 },
{ "/boot/shell.elf", "/mnt/esp/shell.elf", 0 },
{ "/boot/limine-bios.sys", "/mnt/esp/boot/limine/limine-bios.sys", 0 },
{ "/boot/limine-bios-hdd.bin", "/mnt/esp/boot/limine/limine-bios-hdd.bin", 0 },
{ "/boot/BOOTX64.EFI", "/mnt/esp/EFI/BOOT/BOOTX64.EFI", 0 },
{ "/boot/BOOTIA32.EFI", "/mnt/esp/EFI/BOOT/BOOTIA32.EFI", 0 },
{ "/boot/wallpaper.png", "/mnt/esp/boot/wallpaper.png", 0 },
{ "/boot/wallpaper.png", "/mnt/esp/wallpaper.png", 0 },
{ NULL, NULL, 0 }
};
for (int i = 0; boot_files[i].src; i++) {
struct stat st;
if (stat(boot_files[i].src, &st) != 0) {
if (boot_files[i].required) {
fputs(C_RED " MISSING required: " C_RESET, stdout);
fputs(boot_files[i].src, stdout);
putchar(10);
} else {
fputs(C_YELLOW " skip (missing): " C_RESET, stdout);
fputs(boot_files[i].src, stdout);
putchar(10);
}
continue;
}
if (copy_one_file_progress(boot_files[i].src, boot_files[i].dst, boot_files[i].src) < 0) {
fputs(C_RED " FAILED: " C_RESET, stdout);
fputs(boot_files[i].dst, stdout);
putchar(10);
} else {
fputs(C_GREEN " " C_RESET, stdout);
fputs(boot_files[i].dst, stdout);
putchar(10);
}
}
fputs(" [6/8] Writing limine.conf...\n", stdout);
int ok1 = write_limine_conf("/mnt/esp/boot/limine/limine.conf");
int ok2 = write_limine_conf("/mnt/esp/EFI/BOOT/limine.conf");
int ok3 = write_limine_conf("/mnt/esp/limine.conf");
if (ok1 < 0 && ok2 < 0 && ok3 < 0)
progress_fail("failed to write limine.conf");
else
progress_done("limine.conf written (3 locations)");
fputs(" [7/8] Populating root filesystem...\n", stdout);
const char *rdirs[] = {
"/mnt/root/bin", "/mnt/root/apps", "/mnt/root/etc",
"/mnt/root/home", "/mnt/root/tmp", "/mnt/root/var",
"/mnt/root/usr",
NULL
};
for (int i = 0; rdirs[i]; i++) ensure_dir(rdirs[i]);
fputs(" copying /bin...\n", stdout);
copy_tree("/bin", "/mnt/root/bin");
fputs(" copying /apps...\n", stdout);
copy_tree("/apps", "/mnt/root/apps");
struct stat ust;
if (stat("/usr", &ust) == 0) {
fputs(" copying /usr (sysroot)...\n", stdout);
copy_tree("/usr", "/mnt/root/usr");
} else {
fputs(" /usr not present in live image — sysroot skipped\n", stdout);
}
struct stat est;
if (stat("/etc", &est) == 0) {
fputs(" copying /etc...\n", stdout);
static dir_entry_t etc_entries[MAX_ENTRIES];
int nn = read_dir_entries("/etc", etc_entries, MAX_ENTRIES);
for (int i = 0; i < nn; i++) {
const char *nm = etc_entries[i].name;
size_t nl = strlen(nm);
int is_txt = (nl >= 5 && strcmp(nm + nl - 4, ".txt") == 0);
if (etc_entries[i].type == 0) {
char sp[256], dp[256];
path_join("/etc", nm, sp, sizeof(sp));
if (is_txt) path_join("/mnt/root/home", nm, dp, sizeof(dp));
else path_join("/mnt/root/etc", nm, dp, sizeof(dp));
copy_one_file(sp, dp);
} else if (etc_entries[i].type == 1) {
char sp[256], dp[256];
path_join("/etc", nm, sp, sizeof(sp));
path_join("/mnt/root/etc", nm, dp, sizeof(dp));
copy_tree(sp, dp);
}
}
}
struct stat hst;
if (stat("/home", &hst) == 0) {
fputs(" copying /home...\n", stdout);
copy_tree("/home", "/mnt/root/home");
}
fputs(" [8/8] Installing BIOS stage1 to MBR...\n", stdout);
{
static uint8_t sys_buf[300 * 1024];
int fd = open("/mnt/esp/boot/limine/limine-bios-hdd.bin", O_RDONLY, 0);
if (fd < 0) {
progress_fail("limine-bios-hdd.bin not found on ESP");
} else {
struct stat st;
int sr = stat("/mnt/esp/boot/limine/limine-bios-hdd.bin", &st);
uint32_t sys_size = (sr == 0) ? (uint32_t)st.st_size : 0;
if (sys_size == 0 || sys_size > sizeof(sys_buf)) {
close(fd);
progress_fail("bad limine-bios-hdd.bin size");
} else {
uint32_t got = 0;
int ok = 1;
while (got < sys_size) {
ssize_t n = read(fd, sys_buf + got, sys_size - got);
if (n <= 0) { ok = 0; break; }
got += (uint32_t)n;
}
close(fd);
if (!ok || got != sys_size) {
progress_fail("short read on limine-bios-hdd.bin");
} else {
long r = cervus_disk_bios_install(chosen_disk_name, sys_buf, sys_size);
if (r < 0) progress_fail("BIOS install syscall failed");
else progress_done("BIOS stage1 installed");
}
}
}
}
cervus_disk_umount("/mnt/esp");
cervus_disk_umount("/mnt/root");
clear_screen();
fputs("\n" C_GREEN " Installation complete!" C_RESET "\n", stdout);
fputs(" The system will reboot in 3 seconds.\n\n", stdout);
for (int i = 3; i > 0; i--) {
fputs(" Rebooting in ", stdout);
putchar((char)('0' + i));
fputs("...\r", stdout);
syscall1(SYS_SLEEP_NS, 1000000000ULL);
}
fputs("\n", stdout);
syscall0(SYS_REBOOT);
return 0;
}
int main(int argc, char **argv)
{
const char *mode = getenv_argv(argc, argv, "MODE", "");
if (strcmp(mode, "live") != 0) {
fputs(C_RED "install-on-disk: this command is only available in Live mode.\n" C_RESET, stderr);
fputs("The system is already installed on disk.\n", stderr);
return 1;
}
do_install();
return 0;
}
+349
View File
@@ -0,0 +1,349 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/cervus.h>
extern int __cervus_errno;
static long __compat_sys_ret(long r)
{
if (r < 0 && r > -4096) {
__cervus_errno = (int)-r;
return -1;
}
return r;
}
int access(const char *path, int mode)
{
struct stat st;
if (!path) { __cervus_errno = EFAULT; return -1; }
long r = syscall2(SYS_STAT, path, &st);
(void)mode;
if (r < 0 && r > -4096) { __cervus_errno = (int)-r; return -1; }
return 0;
}
static char __cervus_cwd[512] = "/";
int fchdir(int fd)
{
(void)fd;
__cervus_errno = ENOSYS;
return -1;
}
int symlink(const char *target, const char *linkpath)
{
(void)target; (void)linkpath;
__cervus_errno = ENOSYS;
return -1;
}
ssize_t readlink(const char *path, char *buf, size_t bufsiz)
{
(void)path; (void)buf; (void)bufsiz;
__cervus_errno = ENOSYS;
return -1;
}
int sched_yield(void)
{
syscall0(SYS_YIELD);
return 0;
}
long pathconf(const char *path, int name)
{
(void)path;
switch (name) {
case 0: return 255;
case 1: return 512;
default: __cervus_errno = EINVAL; return -1;
}
}
long fpathconf(int fd, int name)
{
(void)fd;
switch (name) {
case 0: return 255;
case 1: return 512;
default: __cervus_errno = EINVAL; return -1;
}
}
int mprotect(void *addr, size_t len, int prot)
{
return (int)__compat_sys_ret(syscall3(SYS_MPROTECT, addr, len, prot));
}
char *realpath(const char *path, char *resolved)
{
if (!path) { __cervus_errno = EINVAL; return NULL; }
static char sbuf[512];
char *out = resolved ? resolved : sbuf;
if (path[0] == '/') {
strncpy(out, path, 511);
out[511] = '\0';
} else {
strncpy(out, __cervus_cwd, 511);
out[511] = '\0';
size_t bl = strlen(out);
if (bl > 0 && out[bl - 1] != '/' && bl < 510) {
out[bl++] = '/';
out[bl] = '\0';
}
strncat(out, path, 511 - strlen(out));
}
char tmp[512];
strncpy(tmp, out, 511);
tmp[511] = '\0';
char *parts[64];
int np = 0;
char *p = tmp;
while (*p) {
while (*p == '/') p++;
if (!*p) break;
char *s = p;
while (*p && *p != '/') p++;
if (*p) *p++ = '\0';
if (strcmp(s, ".") == 0) continue;
if (strcmp(s, "..") == 0) { if (np > 0) np--; continue; }
if (np < 64) parts[np++] = s;
}
size_t ol = 0;
for (int i = 0; i < np; i++) {
out[ol++] = '/';
size_t pl = strlen(parts[i]);
if (ol + pl >= 511) break;
memcpy(out + ol, parts[i], pl);
ol += pl;
}
out[ol] = '\0';
if (ol == 0) { out[0] = '/'; out[1] = '\0'; }
return out;
}
char *mkdtemp(char *tmpl)
{
if (!tmpl) { __cervus_errno = EINVAL; return NULL; }
size_t len = strlen(tmpl);
if (len < 6 || strcmp(tmpl + len - 6, "XXXXXX") != 0) {
__cervus_errno = EINVAL;
return NULL;
}
static uint64_t seq = 0;
uint64_t pid = (uint64_t)getpid();
const char *alpha = "0123456789abcdefghijklmnopqrstuvwxyz";
for (int attempt = 0; attempt < 100; attempt++) {
uint64_t seed = (cervus_uptime_ns() ^ (pid << 32)) + (seq++);
for (int i = 0; i < 6; i++) {
tmpl[len - 6 + i] = alpha[seed % 36];
seed /= 36;
}
if (mkdir(tmpl, 0700) == 0) return tmpl;
if (__cervus_errno != EEXIST) return NULL;
}
__cervus_errno = EEXIST;
return NULL;
}
static char **__env_table = NULL;
static int __env_count = 0;
static int __env_cap = 0;
int putenv(char *str)
{
if (!str) return -1;
char *eq = strchr(str, '=');
if (!eq) return -1;
size_t nl = (size_t)(eq - str);
for (int i = 0; i < __env_count; i++) {
if (strncmp(__env_table[i], str, nl) == 0 && __env_table[i][nl] == '=') {
__env_table[i] = str;
return 0;
}
}
if (__env_count >= __env_cap) {
int nc = __env_cap ? __env_cap * 2 : 16;
char **nt = (char **)realloc(__env_table, (size_t)nc * sizeof(char *));
if (!nt) return -1;
__env_table = nt;
__env_cap = nc;
}
__env_table[__env_count++] = str;
return 0;
}
int setenv(const char *name, const char *value, int overwrite)
{
if (!name || !value) { __cervus_errno = EINVAL; return -1; }
size_t nl = strlen(name);
size_t vl = strlen(value);
for (int i = 0; i < __env_count; i++) {
if (strncmp(__env_table[i], name, nl) == 0 && __env_table[i][nl] == '=') {
if (!overwrite) return 0;
char *nv = (char *)malloc(nl + vl + 2);
if (!nv) return -1;
memcpy(nv, name, nl);
nv[nl] = '=';
memcpy(nv + nl + 1, value, vl + 1);
__env_table[i] = nv;
return 0;
}
}
char *nv = (char *)malloc(nl + vl + 2);
if (!nv) return -1;
memcpy(nv, name, nl);
nv[nl] = '=';
memcpy(nv + nl + 1, value, vl + 1);
return putenv(nv);
}
int unsetenv(const char *name)
{
if (!name) { __cervus_errno = EINVAL; return -1; }
size_t nl = strlen(name);
for (int i = 0; i < __env_count; i++) {
if (strncmp(__env_table[i], name, nl) == 0 && __env_table[i][nl] == '=') {
__env_table[i] = __env_table[--__env_count];
return 0;
}
}
return 0;
}
int uname(struct utsname *buf)
{
if (!buf) { __cervus_errno = EFAULT; return -1; }
strncpy(buf->sysname, "Cervus", _UTSNAME_LENGTH - 1);
strncpy(buf->nodename, "cervus", _UTSNAME_LENGTH - 1);
strncpy(buf->release, "0.0.2", _UTSNAME_LENGTH - 1);
strncpy(buf->version, "#1", _UTSNAME_LENGTH - 1);
strncpy(buf->machine, "x86_64", _UTSNAME_LENGTH - 1);
buf->sysname[_UTSNAME_LENGTH - 1] = '\0';
buf->nodename[_UTSNAME_LENGTH - 1] = '\0';
buf->release[_UTSNAME_LENGTH - 1] = '\0';
buf->version[_UTSNAME_LENGTH - 1] = '\0';
buf->machine[_UTSNAME_LENGTH - 1] = '\0';
return 0;
}
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *))
{
const unsigned char *lo = (const unsigned char *)base;
const unsigned char *hi = lo + nmemb * size;
while (lo < hi) {
size_t half = (size_t)((hi - lo) / (ptrdiff_t)size) / 2;
const unsigned char *mid = lo + half * size;
int r = cmp(key, mid);
if (r == 0) return (void *)mid;
if (r < 0) hi = mid;
else lo = mid + size;
}
return NULL;
}
struct __cervus_FILE {
int fd;
int eof;
int err;
int flags;
char *buf;
size_t buf_size;
size_t buf_pos;
};
FILE *fdopen(int fd, const char *mode)
{
(void)mode;
FILE *f = (FILE *)malloc(sizeof(FILE));
if (!f) { __cervus_errno = ENOMEM; return NULL; }
f->fd = fd;
f->eof = 0;
f->err = 0;
f->flags = 0;
f->buf = NULL;
f->buf_size = 0;
f->buf_pos = 0;
return f;
}
FILE *popen(const char *cmd, const char *type)
{
if (!cmd || !type) { __cervus_errno = EINVAL; return NULL; }
int fds[2];
if (pipe(fds) < 0) return NULL;
pid_t pid = fork();
if (pid < 0) {
close(fds[0]);
close(fds[1]);
return NULL;
}
if (pid == 0) {
if (type[0] == 'r') { dup2(fds[1], 1); }
else { dup2(fds[0], 0); }
close(fds[0]);
close(fds[1]);
char *argv[] = { "/bin/sh", "-c", (char *)cmd, NULL };
execve("/bin/sh", argv, NULL);
_exit(127);
}
if (type[0] == 'r') {
close(fds[1]);
return fdopen(fds[0], "r");
} else {
close(fds[0]);
return fdopen(fds[1], "w");
}
}
int pclose(FILE *f)
{
if (!f) return -1;
int fd = f->fd;
free(f);
close(fd);
int status = 0;
waitpid(-1, &status, 0);
return status;
}
int ungetc(int c, FILE *f)
{
(void)c; (void)f;
return EOF;
}
void rewind(FILE *f)
{
if (f) {
syscall3(SYS_SEEK, f->fd, 0, 0);
f->eof = 0;
f->err = 0;
}
}
char *tmpnam(char *buf)
{
static char sbuf[32];
static uint64_t seq = 0;
char *out = buf ? buf : sbuf;
uint64_t seed = cervus_uptime_ns() ^ (seq++);
snprintf(out, 32, "/tmp/tmp%llu", (unsigned long long)seed);
return out;
}
+41
View File
@@ -0,0 +1,41 @@
BITS 64
DEFAULT REL
section .text
global _start
extern main
extern __cervus_argc
extern __cervus_argv
extern __cervus_filter_args
extern __cervus_filtered_argv
_start:
xor rbp, rbp
mov rdi, [rsp]
lea rsi, [rsp + 8]
lea rax, [rel __cervus_argc]
mov dword [rax], edi
lea rax, [rel __cervus_argv]
mov qword [rax], rsi
and rsp, -16
movsxd rdi, dword [rel __cervus_argc]
mov rsi, qword [rel __cervus_argv]
call __cervus_filter_args
movsxd rdi, eax
lea rsi, [rel __cervus_filtered_argv]
call main
movsxd rdi, eax
xor rax, rax
syscall
.hang:
hlt
jmp .hang
section .note.GNU-stack noalloc noexec nowrite progbits
+16
View File
@@ -0,0 +1,16 @@
#include <ctype.h>
int isdigit(int c) { return c >= '0' && c <= '9'; }
int isalpha(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
int isalnum(int c) { return isdigit(c) || isalpha(c); }
int isupper(int c) { return c >= 'A' && c <= 'Z'; }
int islower(int c) { return c >= 'a' && c <= 'z'; }
int isspace(int c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; }
int isprint(int c) { return (unsigned char)c >= 0x20 && (unsigned char)c < 0x7F; }
int isgraph(int c) { return (unsigned char)c > 0x20 && (unsigned char)c < 0x7F; }
int ispunct(int c) { return isgraph(c) && !isalnum(c); }
int isxdigit(int c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); }
int iscntrl(int c) { return (unsigned char)c < 0x20 || c == 0x7F; }
int isblank(int c) { return c == ' ' || c == '\t'; }
int toupper(int c) { return islower(c) ? c - 'a' + 'A' : c; }
int tolower(int c) { return isupper(c) ? c - 'A' + 'a' : c; }
+59
View File
@@ -0,0 +1,59 @@
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/syscall.h>
typedef struct {
uint64_t d_ino;
uint8_t d_type;
char d_name[256];
} __kernel_dirent_t;
struct __cervus_DIR {
int fd;
struct dirent buf;
};
DIR *opendir(const char *path)
{
int fd = open(path, O_RDONLY | O_DIRECTORY, 0);
if (fd < 0) return NULL;
DIR *d = (DIR *)malloc(sizeof(DIR));
if (!d) { close(fd); return NULL; }
d->fd = fd;
return d;
}
struct dirent *readdir(DIR *dirp)
{
if (!dirp) return NULL;
__kernel_dirent_t kde;
int r = (int)syscall2(SYS_READDIR, dirp->fd, &kde);
if (r != 0) return NULL;
dirp->buf.d_ino = kde.d_ino;
dirp->buf.d_type = kde.d_type;
size_t nl = strlen(kde.d_name);
if (nl >= sizeof(dirp->buf.d_name)) nl = sizeof(dirp->buf.d_name) - 1;
memcpy(dirp->buf.d_name, kde.d_name, nl);
dirp->buf.d_name[nl] = '\0';
return &dirp->buf;
}
int closedir(DIR *dirp)
{
if (!dirp) return -1;
int fd = dirp->fd;
free(dirp);
return close(fd);
}
void rewinddir(DIR *dirp)
{
if (!dirp) return;
lseek(dirp->fd, 0, SEEK_SET);
}
int dirfd(DIR *dirp) { return dirp ? dirp->fd : -1; }
+461
View File
@@ -0,0 +1,461 @@
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/cervus.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <dirent.h>
#include <cervus_util.h>
int __cervus_argc;
char **__cervus_argv;
int __cervus_errno = 0;
static long __sys_ret(long r)
{
if (r < 0 && r > -4096) {
__cervus_errno = (int)-r;
return -1;
}
return r;
}
#define CERVUS_PATH_MAX 512
static char __cervus_cwd[CERVUS_PATH_MAX];
static int __cervus_cwd_inited = 0;
static const char *__cervus_get_cwd(void)
{
if (!__cervus_cwd_inited) {
const char *c = get_cwd_flag(__cervus_argc, __cervus_argv);
if (!c || !*c) c = "/";
size_t n = strlen(c);
if (n >= sizeof(__cervus_cwd)) n = sizeof(__cervus_cwd) - 1;
memcpy(__cervus_cwd, c, n);
__cervus_cwd[n] = '\0';
__cervus_cwd_inited = 1;
}
return __cervus_cwd;
}
static const char *__cervus_resolve(const char *path, char *buf, size_t bufsz)
{
if (!path) return path;
if (path[0] == '/') return path;
resolve_path(__cervus_get_cwd(), path, buf, bufsz);
return buf;
}
ssize_t read(int fd, void *buf, size_t n)
{
return (ssize_t)__sys_ret(syscall3(SYS_READ, fd, buf, n));
}
ssize_t write(int fd, const void *buf, size_t n)
{
return (ssize_t)__sys_ret(syscall3(SYS_WRITE, fd, buf, n));
}
int close(int fd)
{
return (int)__sys_ret(syscall1(SYS_CLOSE, fd));
}
off_t lseek(int fd, off_t off, int whence)
{
return (off_t)__sys_ret(syscall3(SYS_SEEK, fd, (uint64_t)off, whence));
}
int dup(int fd)
{
return (int)__sys_ret(syscall1(SYS_DUP, fd));
}
int dup2(int oldfd, int newfd)
{
return (int)__sys_ret(syscall2(SYS_DUP2, oldfd, newfd));
}
int pipe(int fds[2])
{
return (int)__sys_ret(syscall1(SYS_PIPE, fds));
}
int ioctl(int fd, unsigned long request, ...)
{
va_list ap;
va_start(ap, request);
void *arg = va_arg(ap, void *);
va_end(ap);
return (int)__sys_ret(syscall3(SYS_IOCTL, fd, request, arg));
}
int isatty(int fd)
{
struct winsize ws;
long r = syscall3(SYS_IOCTL, fd, TIOCGWINSZ, &ws);
if (r < 0) {
__cervus_errno = (int)-r;
return 0;
}
return 1;
}
int unlink(const char *path)
{
char abs[CERVUS_PATH_MAX];
path = __cervus_resolve(path, abs, sizeof(abs));
return (int)__sys_ret(syscall1(SYS_UNLINK, path));
}
int rmdir(const char *path)
{
char abs[CERVUS_PATH_MAX];
path = __cervus_resolve(path, abs, sizeof(abs));
return (int)__sys_ret(syscall1(SYS_RMDIR, path));
}
pid_t getpid(void) { return (pid_t)syscall0(SYS_GETPID); }
pid_t getppid(void) { return (pid_t)syscall0(SYS_GETPPID); }
uid_t getuid(void) { return (uid_t)syscall0(SYS_GETUID); }
gid_t getgid(void) { return (gid_t)syscall0(SYS_GETGID); }
int setuid(uid_t u) { return (int)__sys_ret(syscall1(SYS_SETUID, u)); }
int setgid(gid_t g) { return (int)__sys_ret(syscall1(SYS_SETGID, g)); }
pid_t fork(void) { return (pid_t)__sys_ret(syscall0(SYS_FORK)); }
int execve(const char *path, char *const argv[], char *const envp[])
{
char abs[CERVUS_PATH_MAX];
path = __cervus_resolve(path, abs, sizeof(abs));
return (int)__sys_ret(syscall3(SYS_EXECVE, path, argv, envp));
}
int execv(const char *path, char *const argv[])
{
char *empty[] = { NULL };
return execve(path, argv, empty);
}
int execvp(const char *file, char *const argv[])
{
if (!file || !*file) { __cervus_errno = ENOENT; return -1; }
int has_slash = 0;
for (const char *p = file; *p; p++) if (*p == '/') { has_slash = 1; break; }
if (has_slash) return execve(file, argv, NULL);
const char *path = getenv("PATH");
if (!path || !*path) path = "/bin:/apps";
char buf[512];
const char *p = path;
while (*p) {
const char *colon = p;
while (*colon && *colon != ':') colon++;
size_t dlen = (size_t)(colon - p);
size_t flen = strlen(file);
if (dlen + 1 + flen + 1 <= sizeof(buf)) {
memcpy(buf, p, dlen);
buf[dlen] = '/';
memcpy(buf + dlen + 1, file, flen);
buf[dlen + 1 + flen] = '\0';
execve(buf, argv, NULL);
}
p = colon;
if (*p == ':') p++;
}
__cervus_errno = ENOENT;
return -1;
}
void _exit(int status) { syscall1(SYS_EXIT, status); __builtin_unreachable(); }
unsigned int sleep(unsigned int sec)
{
cervus_nanosleep((uint64_t)sec * 1000000000ULL);
return 0;
}
int usleep(unsigned int usec)
{
return cervus_nanosleep((uint64_t)usec * 1000ULL);
}
void sched_yield_cervus(void) { syscall0(SYS_YIELD); }
char *getcwd(char *buf, size_t size)
{
if (!buf || size < 2) { __cervus_errno = EINVAL; return NULL; }
const char *c = __cervus_get_cwd();
size_t n = strlen(c);
if (n + 1 > size) { __cervus_errno = ERANGE; return NULL; }
memcpy(buf, c, n + 1);
return buf;
}
int chdir(const char *path)
{
if (!path || !*path) { __cervus_errno = ENOENT; return -1; }
char abs[CERVUS_PATH_MAX];
const char *p = __cervus_resolve(path, abs, sizeof(abs));
struct stat st;
if ((int)__sys_ret(syscall2(SYS_STAT, p, &st)) < 0) return -1;
if (!S_ISDIR(st.st_mode)) { __cervus_errno = ENOTDIR; return -1; }
size_t n = strlen(p);
if (n >= sizeof(__cervus_cwd)) { __cervus_errno = ENAMETOOLONG; return -1; }
memcpy(__cervus_cwd, p, n + 1);
__cervus_cwd_inited = 1;
return 0;
}
void *sbrk(intptr_t incr)
{
uintptr_t cur = (uintptr_t)syscall1(SYS_BRK, 0);
if (incr == 0) return (void *)cur;
uintptr_t nw = (uintptr_t)syscall1(SYS_BRK, cur + (uintptr_t)incr);
if (nw != cur + (uintptr_t)incr) {
__cervus_errno = ENOMEM;
return (void *)-1;
}
return (void *)cur;
}
int brk(void *addr)
{
uintptr_t r = (uintptr_t)syscall1(SYS_BRK, (uintptr_t)addr);
if (r != (uintptr_t)addr) { __cervus_errno = ENOMEM; return -1; }
return 0;
}
int open(const char *path, int flags, ...)
{
mode_t mode = 0;
if (flags & O_CREAT) {
va_list ap;
va_start(ap, flags);
mode = va_arg(ap, mode_t);
va_end(ap);
}
char abs[CERVUS_PATH_MAX];
path = __cervus_resolve(path, abs, sizeof(abs));
return (int)__sys_ret(syscall3(SYS_OPEN, path, flags, mode));
}
int creat(const char *path, mode_t mode)
{
return open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
}
int fcntl(int fd, int cmd, ...)
{
va_list ap;
va_start(ap, cmd);
long arg = va_arg(ap, long);
va_end(ap);
return (int)__sys_ret(syscall3(SYS_FCNTL, fd, cmd, arg));
}
int stat(const char *path, struct stat *out)
{
char abs[CERVUS_PATH_MAX];
path = __cervus_resolve(path, abs, sizeof(abs));
return (int)__sys_ret(syscall2(SYS_STAT, path, out));
}
int fstat(int fd, struct stat *out)
{
return (int)__sys_ret(syscall2(SYS_FSTAT, fd, out));
}
int mkdir(const char *path, mode_t mode)
{
char abs[CERVUS_PATH_MAX];
path = __cervus_resolve(path, abs, sizeof(abs));
return (int)__sys_ret(syscall2(SYS_MKDIR, path, mode));
}
int chmod(const char *path, mode_t mode)
{
(void)path; (void)mode;
return 0;
}
int rename(const char *oldp, const char *newp)
{
char absa[CERVUS_PATH_MAX], absb[CERVUS_PATH_MAX];
oldp = __cervus_resolve(oldp, absa, sizeof(absa));
newp = __cervus_resolve(newp, absb, sizeof(absb));
return (int)__sys_ret(syscall2(SYS_RENAME, oldp, newp));
}
void *mmap(void *a, size_t l, int p, int f, int fd, off_t o)
{
long r = syscall6(SYS_MMAP, a, l, p, f, fd, (uint64_t)o);
if (r < 0 && r > -4096) { __cervus_errno = (int)-r; return MAP_FAILED; }
return (void *)r;
}
int munmap(void *a, size_t l) { return (int)__sys_ret(syscall2(SYS_MUNMAP, a, l)); }
pid_t waitpid(pid_t p, int *s, int f) { return (pid_t)__sys_ret(syscall3(SYS_WAIT, p, s, f)); }
pid_t wait(int *s) { return waitpid(-1, s, 0); }
ssize_t cervus_dbg_print(const char *b, size_t n) { return (ssize_t)syscall2(SYS_DBG_PRINT, b, n); }
char *optarg = NULL;
int optind = 1;
int optopt = 0;
int opterr = 1;
static int __opt_subidx = 1;
int getopt(int argc, char *const argv[], const char *optstring)
{
if (!optstring) optstring = "";
int colon_mode = (optstring[0] == ':');
const char *opts = colon_mode ? optstring + 1 : optstring;
optarg = NULL;
if (optind >= argc) return -1;
char *cur = argv[optind];
if (!cur || cur[0] != '-' || cur[1] == '\0') return -1;
if (cur[0] == '-' && cur[1] == '-' && cur[2] == '\0') {
optind++;
return -1;
}
char ch = cur[__opt_subidx];
if (ch == '\0') {
optind++;
__opt_subidx = 1;
return getopt(argc, argv, optstring);
}
const char *pp = opts;
while (*pp && *pp != ch) pp++;
if (*pp == '\0' || ch == ':') {
optopt = ch;
if (opterr && !colon_mode) {
const char *prog = argv[0] ? argv[0] : "?";
fprintf(stderr, "%s: invalid option -- '%c'\n", prog, ch);
}
__opt_subidx++;
if (cur[__opt_subidx] == '\0') {
optind++;
__opt_subidx = 1;
}
return '?';
}
if (pp[1] == ':') {
if (cur[__opt_subidx + 1] != '\0') {
optarg = &cur[__opt_subidx + 1];
optind++;
__opt_subidx = 1;
return ch;
}
if (optind + 1 >= argc) {
optopt = ch;
optind++;
__opt_subidx = 1;
if (opterr && !colon_mode) {
const char *prog = argv[0] ? argv[0] : "?";
fprintf(stderr, "%s: option requires an argument -- '%c'\n", prog, ch);
}
return colon_mode ? ':' : '?';
}
optarg = argv[optind + 1];
optind += 2;
__opt_subidx = 1;
return ch;
}
__opt_subidx++;
if (cur[__opt_subidx] == '\0') {
optind++;
__opt_subidx = 1;
}
return ch;
}
int tcgetattr(int fd, struct termios *t)
{
if (!t) { __cervus_errno = EINVAL; return -1; }
return (int)__sys_ret(syscall3(SYS_IOCTL, fd, TCGETS, t));
}
int tcsetattr(int fd, int optional_actions, const struct termios *t)
{
if (!t) { __cervus_errno = EINVAL; return -1; }
unsigned long req;
switch (optional_actions) {
case TCSADRAIN: req = TCSETSW; break;
case TCSAFLUSH: req = TCSETSF; break;
case TCSANOW:
default: req = TCSETS; break;
}
return (int)__sys_ret(syscall3(SYS_IOCTL, fd, req, t));
}
int cervus_task_info(pid_t p, cervus_task_info_t *o) { return (int)__sys_ret(syscall2(SYS_TASK_INFO, p, o)); }
int cervus_task_kill(pid_t p) { return (int)__sys_ret(syscall1(SYS_TASK_KILL, p)); }
uint64_t cervus_cap_get(void) { return (uint64_t)syscall0(SYS_CAP_GET); }
int cervus_cap_drop(uint64_t m) { return (int)__sys_ret(syscall1(SYS_CAP_DROP, m)); }
int cervus_meminfo(cervus_meminfo_t *m) { return (int)__sys_ret(syscall1(SYS_MEMINFO, m)); }
uint64_t cervus_uptime_ns(void) { return (uint64_t)syscall0(SYS_UPTIME); }
int cervus_clock_gettime(int id, cervus_timespec_t *t){ return (int)__sys_ret(syscall2(SYS_CLOCK_GET, id, t)); }
int cervus_nanosleep(uint64_t ns) { return (int)__sys_ret(syscall1(SYS_SLEEP_NS, ns)); }
int cervus_shutdown(void) { return (int)__sys_ret(syscall0(SYS_SHUTDOWN)); }
int cervus_reboot(void) { return (int)__sys_ret(syscall0(SYS_REBOOT)); }
int cervus_disk_info(int i, cervus_disk_info_t *o) { return (int)__sys_ret(syscall2(SYS_DISK_INFO, (uint64_t)i, o)); }
int cervus_disk_mount(const char *d, const char *p) { return (int)__sys_ret(syscall2(SYS_DISK_MOUNT, d, p)); }
int cervus_disk_umount(const char *p) { return (int)__sys_ret(syscall1(SYS_DISK_UMOUNT, p)); }
int cervus_disk_format(const char *d, const char *l) { return (int)__sys_ret(syscall2(SYS_DISK_FORMAT, d, l)); }
int cervus_disk_mkfs_fat32(const char *d, const char *l) { return (int)__sys_ret(syscall2(SYS_DISK_MKFS_FAT32, d, l)); }
int cervus_disk_partition(const char *d, const cervus_mbr_part_t *s, uint64_t n) { return (int)__sys_ret(syscall3(SYS_DISK_PARTITION, d, s, n)); }
int cervus_disk_read_raw(const char *d, uint64_t lba, uint64_t c, void *b) { return (int)__sys_ret(syscall4(SYS_DISK_READ_RAW, d, lba, c, b)); }
int cervus_disk_write_raw(const char *d, uint64_t lba, uint64_t c, const void *b){ return (int)__sys_ret(syscall4(SYS_DISK_WRITE_RAW, d, lba, c, b)); }
long cervus_disk_list_parts(cervus_part_info_t *o, int m) { return __sys_ret(syscall2(SYS_DISK_LIST_PARTS, o, m)); }
long cervus_disk_bios_install(const char *d, const void *sd, uint32_t ss) { return __sys_ret(syscall3(SYS_DISK_BIOS_INSTALL, d, sd, ss)); }
long cervus_list_mounts(cervus_mount_info_t *o, int m) { return __sys_ret(syscall2(SYS_LIST_MOUNTS, o, m)); }
long cervus_statvfs(const char *p, cervus_statvfs_t *o) { return __sys_ret(syscall2(SYS_STATVFS, p, o)); }
uint32_t cervus_ioport_read(uint16_t p, int w) { return (uint32_t)syscall2(SYS_IOPORT_READ, p, w); }
int cervus_ioport_write(uint16_t p, int w, uint32_t v) { return (int)__sys_ret(syscall3(SYS_IOPORT_WRITE, p, w, v)); }
void __cervus_assert_fail(const char *expr, const char *file, int line, const char *func)
{
printf("assertion failed: %s (%s:%d, %s)\n",
expr ? expr : "(null)",
file ? file : "(null)",
line,
func ? func : "(null)");
syscall1(SYS_EXIT, 134);
for (;;) { }
}
#define _CERVUS_FILT_MAX 128
char *__cervus_filtered_argv[_CERVUS_FILT_MAX + 1];
int __cervus_filter_args(int argc, char **argv)
{
int out = 0;
for (int i = 0; i < argc && out < _CERVUS_FILT_MAX; i++) {
const char *a = argv[i];
if (i > 0 && a && a[0] == '-' && a[1] == '-' &&
((a[2]=='c' && a[3]=='w' && a[4]=='d' && a[5]=='=') ||
(a[2]=='e' && a[3]=='n' && a[4]=='v' && a[5]==':'))) {
continue;
}
__cervus_filtered_argv[out++] = (char *)a;
}
__cervus_filtered_argv[out] = NULL;
return out;
}
+5
View File
@@ -0,0 +1,5 @@
#include <math.h>
int abs(int n) {
return n >= 0 ? n : -n;
}
+12
View File
@@ -0,0 +1,12 @@
#include <math.h>
#include <stdint.h>
double fabs(double x) {
union {
double d;
uint64_t i;
} u = { .d = x };
u.i &= 0x7FFFFFFFFFFFFFFFULL;
return u.d;
}
+14
View File
@@ -0,0 +1,14 @@
#include <math.h>
#include <stdint.h>
int isinf(double x) {
union {
double f;
uint64_t i;
} u = { .f = x };
uint64_t exp = (u.i >> 52) & 0x7FF;
uint64_t mantissa = u.i & 0xFFFFFFFFFFFFFULL;
return (exp == 0x7FF) && (mantissa == 0);
}
+14
View File
@@ -0,0 +1,14 @@
#include <math.h>
#include <stdint.h>
int isnan(double x) {
union {
double f;
uint64_t i;
} u = { .f = x };
uint64_t exp = (u.i >> 52) & 0x7FF;
uint64_t mantissa = u.i & 0xFFFFFFFFFFFFFULL;
return (exp == 0x7FF) && (mantissa != 0);
}
+37
View File
@@ -0,0 +1,37 @@
#include <math.h>
double pow(double x, double y) {
if (y == 0.0) return 1.0;
if (y == (int)y) {
int n = (int)y;
double result = 1.0;
if (n > 0) {
for (int i = 0; i < n; i++) {
result *= x;
}
} else {
for (int i = 0; i < -n; i++) {
result /= x;
}
}
return result;
}
if (x > 0.0) {
int int_part = (int)y;
double frac_part = y - int_part;
double int_pow = pow(x, int_part);
if (frac_part > 0.0) {
return int_pow * (1.0 + frac_part * (x - 1.0));
} else {
return int_pow / (1.0 - frac_part * (x - 1.0));
}
}
return NAN;
}
+17
View File
@@ -0,0 +1,17 @@
#include <math.h>
double pow10(int n) {
double result = 1.0;
if (n >= 0) {
for (int i = 0; i < n; i++) {
result *= 10.0;
}
} else {
for (int i = 0; i < -n; i++) {
result /= 10.0;
}
}
return result;
}
+219
View File
@@ -0,0 +1,219 @@
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
extern int __cervus_errno;
typedef struct __mblock {
size_t size;
size_t prev_size;
} __mblock_t;
#define MB_HDR_SZ (sizeof(__mblock_t))
#define MB_ALIGN 16
#define MB_MIN_TOTAL 32
#define MB_FREE_BIT ((size_t)1)
#define MB_SIZE(b) ((b)->size & ~MB_FREE_BIT)
#define MB_IS_FREE(b) (((b)->size & MB_FREE_BIT) != 0)
#define MB_USER(b) ((void *)((char *)(b) + MB_HDR_SZ))
#define MB_FROM_USER(p) ((__mblock_t *)((char *)(p) - MB_HDR_SZ))
static __mblock_t *__heap_start = NULL;
static __mblock_t *__heap_end = NULL;
static inline size_t __align_up(size_t n, size_t a) {
return (n + a - 1) & ~(a - 1);
}
static inline __mblock_t *__mb_next(__mblock_t *b) {
return (__mblock_t *)((char *)b + MB_SIZE(b));
}
static inline __mblock_t *__mb_prev(__mblock_t *b) {
if (b->prev_size == 0) return NULL;
return (__mblock_t *)((char *)b - b->prev_size);
}
static __mblock_t *__heap_grow(size_t need)
{
size_t chunk = __align_up(need + MB_HDR_SZ, 65536);
if (!__heap_start) {
void *base = sbrk((intptr_t)chunk);
if (base == (void *)-1) return NULL;
uintptr_t addr = (uintptr_t)base;
uintptr_t aligned = (addr + MB_ALIGN - 1) & ~(uintptr_t)(MB_ALIGN - 1);
size_t lost = aligned - addr;
if (lost >= chunk - MB_MIN_TOTAL - MB_HDR_SZ) {
__cervus_errno = ENOMEM;
return NULL;
}
__heap_start = (__mblock_t *)aligned;
size_t usable = chunk - lost;
__mblock_t *first = __heap_start;
size_t first_sz = usable - MB_HDR_SZ;
first->size = first_sz | MB_FREE_BIT;
first->prev_size = 0;
__heap_end = (__mblock_t *)((char *)first + first_sz);
__heap_end->size = 0;
__heap_end->prev_size = first_sz;
return first;
}
void *p = sbrk((intptr_t)chunk);
if (p == (void *)-1) return NULL;
if ((uintptr_t)p != (uintptr_t)__heap_end + MB_HDR_SZ) {
__cervus_errno = ENOMEM;
return NULL;
}
__mblock_t *new_block = __heap_end;
new_block->size = chunk | MB_FREE_BIT;
__mblock_t *new_end = (__mblock_t *)((char *)new_block + MB_SIZE(new_block));
new_end->size = 0;
new_end->prev_size = MB_SIZE(new_block);
__heap_end = new_end;
__mblock_t *prev = __mb_prev(new_block);
if (prev && MB_IS_FREE(prev)) {
size_t merged_sz = MB_SIZE(prev) + MB_SIZE(new_block);
prev->size = merged_sz | MB_FREE_BIT;
__heap_end->prev_size = merged_sz;
return prev;
}
return new_block;
}
static void __mb_split(__mblock_t *b, size_t need)
{
size_t cur = MB_SIZE(b);
if (cur < need + MB_MIN_TOTAL) {
b->size = cur;
return;
}
b->size = need;
__mblock_t *rest = (__mblock_t *)((char *)b + need);
rest->size = (cur - need) | MB_FREE_BIT;
rest->prev_size = need;
__mblock_t *after = __mb_next(rest);
if (after) after->prev_size = MB_SIZE(rest);
}
void *malloc(size_t n)
{
if (n == 0) n = 1;
size_t need = __align_up(n + MB_HDR_SZ, MB_ALIGN);
if (need < MB_MIN_TOTAL) need = MB_MIN_TOTAL;
for (__mblock_t *b = __heap_start; b && b != __heap_end; b = __mb_next(b)) {
if (MB_IS_FREE(b) && MB_SIZE(b) >= need) {
__mb_split(b, need);
return MB_USER(b);
}
}
__mblock_t *grown = __heap_grow(need);
if (!grown) return NULL;
if (MB_SIZE(grown) < need) {
__cervus_errno = ENOMEM;
return NULL;
}
__mb_split(grown, need);
return MB_USER(grown);
}
void *calloc(size_t nm, size_t sz)
{
size_t t = nm * sz;
if (nm && t / nm != sz) { __cervus_errno = ENOMEM; return NULL; }
void *p = malloc(t);
if (p) memset(p, 0, t);
return p;
}
void free(void *p)
{
if (!p) return;
__mblock_t *b = MB_FROM_USER(p);
b->size = MB_SIZE(b) | MB_FREE_BIT;
__mblock_t *next = __mb_next(b);
if (next != __heap_end && MB_IS_FREE(next)) {
size_t merged = MB_SIZE(b) + MB_SIZE(next);
b->size = merged | MB_FREE_BIT;
__mblock_t *after = __mb_next(b);
if (after) after->prev_size = merged;
}
__mblock_t *prev = __mb_prev(b);
if (prev && MB_IS_FREE(prev)) {
size_t merged = MB_SIZE(prev) + MB_SIZE(b);
prev->size = merged | MB_FREE_BIT;
__mblock_t *after = __mb_next(prev);
if (after) after->prev_size = merged;
}
}
void *realloc(void *p, size_t n)
{
if (!p) return malloc(n);
if (n == 0) { free(p); return NULL; }
__mblock_t *b = MB_FROM_USER(p);
size_t cur_total = MB_SIZE(b);
size_t cur_user = cur_total - MB_HDR_SZ;
size_t need = __align_up(n + MB_HDR_SZ, MB_ALIGN);
if (need < MB_MIN_TOTAL) need = MB_MIN_TOTAL;
if (need <= cur_total) {
if (cur_total >= need + MB_MIN_TOTAL) {
b->size = need;
__mblock_t *rest = (__mblock_t *)((char *)b + need);
rest->size = (cur_total - need) | MB_FREE_BIT;
rest->prev_size = need;
__mblock_t *after = __mb_next(rest);
if (after) after->prev_size = MB_SIZE(rest);
if (after != __heap_end && MB_IS_FREE(after)) {
size_t merged = MB_SIZE(rest) + MB_SIZE(after);
rest->size = merged | MB_FREE_BIT;
__mblock_t *aft2 = __mb_next(rest);
if (aft2) aft2->prev_size = merged;
}
}
return p;
}
__mblock_t *next = __mb_next(b);
if (next != __heap_end && MB_IS_FREE(next) &&
cur_total + MB_SIZE(next) >= need)
{
size_t combined = cur_total + MB_SIZE(next);
b->size = combined;
__mblock_t *after = __mb_next(b);
if (after) after->prev_size = combined;
if (combined >= need + MB_MIN_TOTAL) {
b->size = need;
__mblock_t *rest = (__mblock_t *)((char *)b + need);
rest->size = (combined - need) | MB_FREE_BIT;
rest->prev_size = need;
__mblock_t *aft = __mb_next(rest);
if (aft) aft->prev_size = MB_SIZE(rest);
}
return p;
}
void *np = malloc(n);
if (!np) return NULL;
memcpy(np, p, cur_user);
free(p);
return np;
}
+38
View File
@@ -0,0 +1,38 @@
BITS 64
DEFAULT REL
section .text
global setjmp
global longjmp
setjmp:
mov [rdi + 0x00], rbx
mov [rdi + 0x08], rbp
mov [rdi + 0x10], r12
mov [rdi + 0x18], r13
mov [rdi + 0x20], r14
mov [rdi + 0x28], r15
lea rax, [rsp + 8]
mov [rdi + 0x30], rax
mov rax, [rsp]
mov [rdi + 0x38], rax
xor eax, eax
ret
longjmp:
mov rbx, [rdi + 0x00]
mov rbp, [rdi + 0x08]
mov r12, [rdi + 0x10]
mov r13, [rdi + 0x18]
mov r14, [rdi + 0x20]
mov r15, [rdi + 0x28]
mov rsp, [rdi + 0x30]
mov eax, esi
test eax, eax
jnz .done
mov eax, 1
.done:
jmp qword [rdi + 0x38]
section .note.GNU-stack noalloc noexec nowrite progbits
+39
View File
@@ -0,0 +1,39 @@
#include <signal.h>
#include <string.h>
sighandler_t signal(int signum, sighandler_t handler)
{
(void)signum;
(void)handler;
return SIG_DFL;
}
int raise(int sig)
{
(void)sig;
return 0;
}
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact)
{
(void)sig; (void)act;
if (oldact) {
oldact->sa_handler = SIG_DFL;
memset(&oldact->sa_mask, 0, sizeof(oldact->sa_mask));
oldact->sa_flags = 0;
}
return 0;
}
int sigemptyset(sigset_t *set) { if (set) memset(set, 0, sizeof(*set)); return 0; }
int sigfillset(sigset_t *set) { if (set) memset(set, 0xFF, sizeof(*set)); return 0; }
int sigaddset(sigset_t *set, int sig) { (void)set; (void)sig; return 0; }
int sigdelset(sigset_t *set, int sig) { (void)set; (void)sig; return 0; }
int sigismember(const sigset_t *set, int sig) { (void)set; (void)sig; return 0; }
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
{
(void)how; (void)set;
if (oldset) memset(oldset, 0, sizeof(*oldset));
return 0;
}
+252
View File
@@ -0,0 +1,252 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
extern uint64_t __cervus_strtod_bits(const char *s, char **endptr);
static int __is_space(int c)
{
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
}
int vsscanf(const char *str, const char *fmt, va_list ap)
{
if (!str || !fmt) return -1;
const char *s = str;
const char *f = fmt;
int matched = 0;
int saw_anything = 0;
while (*f) {
if (__is_space((unsigned char)*f)) {
while (__is_space((unsigned char)*s)) s++;
f++;
continue;
}
if (*f != '%') {
if (*s != *f) return saw_anything ? matched : (matched ? matched : -1);
s++; f++;
continue;
}
f++;
int suppress = 0;
if (*f == '*') { suppress = 1; f++; }
int width = 0;
int has_width = 0;
while (*f >= '0' && *f <= '9') {
width = width * 10 + (*f - '0');
has_width = 1;
f++;
}
if (!has_width) width = 0;
int len_mod = 0;
if (*f == 'h') { f++; if (*f == 'h') { f++; len_mod = 2; } else len_mod = 1; }
else if (*f == 'l') { f++; if (*f == 'l') { f++; len_mod = 4; } else len_mod = 3; }
else if (*f == 'z') { f++; len_mod = 5; }
else if (*f == 'j' || *f == 't') { f++; len_mod = 4; }
char conv = *f;
if (!conv) break;
f++;
if (conv == '%') {
if (*s != '%') return matched ? matched : -1;
s++;
continue;
}
if (conv == 'n') {
if (!suppress) {
int read = (int)(s - str);
if (len_mod == 3 || len_mod == 5) *va_arg(ap, long *) = read;
else if (len_mod == 4) *va_arg(ap, long long *) = read;
else *va_arg(ap, int *) = read;
}
continue;
}
if (conv == 'c') {
int w = has_width ? width : 1;
char *out = suppress ? NULL : va_arg(ap, char *);
for (int i = 0; i < w; i++) {
if (!*s) return matched ? matched : -1;
if (out) out[i] = *s;
s++;
}
if (!suppress) matched++;
saw_anything = 1;
continue;
}
if (conv != '[') {
while (__is_space((unsigned char)*s)) s++;
}
if (conv == 's') {
int w = has_width ? width : 0x7FFFFFFF;
char *out = suppress ? NULL : va_arg(ap, char *);
int wrote = 0;
if (!*s) return matched ? matched : -1;
while (*s && !__is_space((unsigned char)*s) && wrote < w) {
if (out) out[wrote] = *s;
s++; wrote++;
}
if (out) out[wrote] = '\0';
if (!suppress) matched++;
saw_anything = 1;
continue;
}
if (conv == '[') {
int negate = 0;
if (*f == '^') { negate = 1; f++; }
char set[256];
memset(set, 0, sizeof(set));
if (*f == ']') { set[(unsigned char)']'] = 1; f++; }
while (*f && *f != ']') { set[(unsigned char)*f] = 1; f++; }
if (*f == ']') f++;
int w = has_width ? width : 0x7FFFFFFF;
char *out = suppress ? NULL : va_arg(ap, char *);
int wrote = 0;
while (*s && wrote < w) {
int in = set[(unsigned char)*s] != 0;
if (negate) in = !in;
if (!in) break;
if (out) out[wrote] = *s;
s++; wrote++;
}
if (out) out[wrote] = '\0';
if (wrote == 0) return matched ? matched : -1;
if (!suppress) matched++;
saw_anything = 1;
continue;
}
if (conv == 'd' || conv == 'i' || conv == 'u' ||
conv == 'o' || conv == 'x' || conv == 'X')
{
int base = 10;
int allow_neg = (conv == 'd' || conv == 'i');
if (conv == 'o') base = 8;
else if (conv == 'x' || conv == 'X') base = 16;
const char *start = s;
int neg = 0;
if (allow_neg && (*s == '+' || *s == '-')) { neg = (*s == '-'); s++; }
else if (conv == 'u' && (*s == '+')) s++;
if (conv == 'i' || conv == 'x' || conv == 'X') {
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
base = 16; s += 2;
} else if (conv == 'i' && s[0] == '0' && s[1] >= '0' && s[1] <= '7') {
base = 8; s++;
}
}
unsigned long long val = 0;
int got_digit = 0;
int wleft = has_width ? width - (int)(s - start) : 0x7FFFFFFF;
while (*s && wleft > 0) {
int d = -1;
if (*s >= '0' && *s <= '9') d = *s - '0';
else if (*s >= 'a' && *s <= 'f') d = *s - 'a' + 10;
else if (*s >= 'A' && *s <= 'F') d = *s - 'A' + 10;
if (d < 0 || d >= base) break;
val = val * base + (unsigned)d;
s++; wleft--;
got_digit = 1;
}
if (!got_digit) return matched ? matched : -1;
if (!suppress) {
if (allow_neg && neg) {
long long sv = -(long long)val;
if (len_mod == 4) *va_arg(ap, long long *) = sv;
else if (len_mod == 3 || len_mod == 5) *va_arg(ap, long *) = (long)sv;
else if (len_mod == 1) *va_arg(ap, short *) = (short)sv;
else if (len_mod == 2) *va_arg(ap, signed char *) = (signed char)sv;
else *va_arg(ap, int *) = (int)sv;
} else {
if (len_mod == 4) *va_arg(ap, unsigned long long *) = val;
else if (len_mod == 3 || len_mod == 5) *va_arg(ap, unsigned long *) = (unsigned long)val;
else if (len_mod == 1) *va_arg(ap, unsigned short *) = (unsigned short)val;
else if (len_mod == 2) *va_arg(ap, unsigned char *) = (unsigned char)val;
else *va_arg(ap, unsigned int *) = (unsigned int)val;
}
matched++;
}
saw_anything = 1;
continue;
}
if (conv == 'f' || conv == 'e' || conv == 'g' ||
conv == 'E' || conv == 'G' || conv == 'a' || conv == 'A')
{
char *endp;
uint64_t bits = __cervus_strtod_bits(s, &endp);
if (endp == s) return matched ? matched : -1;
if (!suppress) {
if (len_mod == 3) {
double v;
__builtin_memcpy(&v, &bits, sizeof(double));
*va_arg(ap, double *) = v;
} else {
double dv;
__builtin_memcpy(&dv, &bits, sizeof(double));
*va_arg(ap, float *) = (float)dv;
}
matched++;
}
s = endp;
saw_anything = 1;
continue;
}
return matched ? matched : -1;
}
return matched;
}
int sscanf(const char *str, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int n = vsscanf(str, fmt, ap);
va_end(ap);
return n;
}
int vscanf(const char *fmt, va_list ap)
{
char buf[1024];
if (!fgets(buf, (int)sizeof(buf), stdin)) return EOF;
return vsscanf(buf, fmt, ap);
}
int scanf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int n = vscanf(fmt, ap);
va_end(ap);
return n;
}
int vfscanf(FILE *stream, const char *fmt, va_list ap)
{
char buf[1024];
if (!fgets(buf, (int)sizeof(buf), stream)) return EOF;
return vsscanf(buf, fmt, ap);
}
int fscanf(FILE *stream, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int n = vfscanf(stream, fmt, ap);
va_end(ap);
return n;
}
+430
View File
@@ -0,0 +1,430 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/cervus.h>
extern int __cervus_errno;
struct __cervus_FILE {
int fd;
int eof;
int err;
int flags;
char *buf;
size_t buf_size;
size_t buf_pos;
};
static struct __cervus_FILE __stdin_s = { 0, 0, 0, 0, NULL, 0, 0 };
static struct __cervus_FILE __stdout_s = { 1, 0, 0, 0, NULL, 0, 0 };
static struct __cervus_FILE __stderr_s = { 2, 0, 0, 0, NULL, 0, 0 };
FILE *stdin = &__stdin_s;
FILE *stdout = &__stdout_s;
FILE *stderr = &__stderr_s;
int fileno(FILE *s) { return s ? s->fd : -1; }
int feof(FILE *s) { return s ? s->eof : 1; }
int ferror(FILE *s) { return s ? s->err : 1; }
void clearerr(FILE *s) { if (s) { s->eof = 0; s->err = 0; } }
FILE *fopen(const char *path, const char *mode)
{
if (!path || !mode) return NULL;
int flags = 0;
int has_plus = 0;
for (const char *m = mode + 1; *m; m++) if (*m == '+') has_plus = 1;
switch (mode[0]) {
case 'r': flags = has_plus ? O_RDWR : O_RDONLY; break;
case 'w': flags = (has_plus ? O_RDWR : O_WRONLY) | O_CREAT | O_TRUNC; break;
case 'a': flags = (has_plus ? O_RDWR : O_WRONLY) | O_CREAT | O_APPEND; break;
default: return NULL;
}
int fd = open(path, flags, 0644);
if (fd < 0) return NULL;
FILE *f = (FILE *)malloc(sizeof(FILE));
if (!f) { close(fd); return NULL; }
f->fd = fd;
f->eof = 0;
f->err = 0;
f->flags = 1;
f->buf = NULL;
f->buf_size = 0;
f->buf_pos = 0;
return f;
}
int fclose(FILE *s)
{
if (!s) return EOF;
int fd = s->fd;
int owned = s->flags & 1;
close(fd);
if (owned) free(s);
return 0;
}
int fflush(FILE *s) { (void)s; return 0; }
size_t fread(void *buf, size_t size, size_t nmemb, FILE *s)
{
if (!s || size == 0 || nmemb == 0) return 0;
size_t total = size * nmemb;
size_t got = 0;
while (got < total) {
ssize_t r = read(s->fd, (char *)buf + got, total - got);
if (r < 0) { s->err = 1; break; }
if (r == 0) { s->eof = 1; break; }
got += (size_t)r;
}
return got / size;
}
size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *s)
{
if (!s || size == 0 || nmemb == 0) return 0;
size_t total = size * nmemb;
size_t sent = 0;
while (sent < total) {
ssize_t w = write(s->fd, (const char *)buf + sent, total - sent);
if (w < 0) { s->err = 1; break; }
if (w == 0) break;
sent += (size_t)w;
}
return sent / size;
}
int fseek(FILE *s, long off, int whence)
{
if (!s) return -1;
off_t r = lseek(s->fd, (off_t)off, whence);
if (r == (off_t)-1) { s->err = 1; return -1; }
s->eof = 0;
return 0;
}
long ftell(FILE *s)
{
if (!s) return -1;
return (long)lseek(s->fd, 0, SEEK_CUR);
}
int fputc(int c, FILE *s)
{
unsigned char ch = (unsigned char)c;
if (fwrite(&ch, 1, 1, s) != 1) return EOF;
return (int)ch;
}
int fgetc(FILE *s)
{
unsigned char ch;
if (fread(&ch, 1, 1, s) != 1) return EOF;
return (int)ch;
}
int fputs(const char *str, FILE *s)
{
if (!str) return EOF;
size_t n = strlen(str);
if (fwrite(str, 1, n, s) != n) return EOF;
return 0;
}
char *fgets(char *str, int n, FILE *s)
{
if (!str || n <= 0 || !s) return NULL;
int i = 0;
while (i < n - 1) {
int c = fgetc(s);
if (c == EOF) {
if (i == 0) return NULL;
break;
}
str[i++] = (char)c;
if (c == '\n') break;
}
str[i] = '\0';
return str;
}
int putchar(int c) { return fputc(c, stdout); }
int getchar(void) { return fgetc(stdin); }
int puts(const char *s)
{
if (fputs(s, stdout) == EOF) return EOF;
if (fputc('\n', stdout) == EOF) return EOF;
return 1;
}
static void __u64_to_str(uint64_t v, char *out, int base, int upper)
{
char tmp[32];
int i = 0;
const char *digs = upper ? "0123456789ABCDEF" : "0123456789abcdef";
if (v == 0) { out[0] = '0'; out[1] = 0; return; }
while (v) { tmp[i++] = digs[v % (uint64_t)base]; v /= (uint64_t)base; }
int j = 0;
while (i > 0) out[j++] = tmp[--i];
out[j] = 0;
}
int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap)
{
size_t pos = 0;
#define __PUT(s, n) do { \
size_t __n = (n); const char *__s = (s); \
for (size_t __i = 0; __i < __n; __i++) { \
if (pos + 1 < sz) buf[pos] = __s[__i]; \
pos++; \
} \
} while (0)
while (*fmt) {
if (*fmt != '%') { __PUT(fmt, 1); fmt++; continue; }
fmt++;
int pad_zero = 0, left_align = 0, plus_flag = 0;
while (*fmt == '0' || *fmt == '-' || *fmt == '+' || *fmt == ' ' || *fmt == '#') {
if (*fmt == '0') pad_zero = 1;
else if (*fmt == '-') left_align = 1;
else if (*fmt == '+') plus_flag = 1;
fmt++;
}
int width = 0;
if (*fmt == '*') { width = va_arg(ap, int); if (width < 0) { left_align = 1; width = -width; } fmt++; }
else while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; }
int prec = -1;
if (*fmt == '.') {
fmt++;
prec = 0;
if (*fmt == '*') { prec = va_arg(ap, int); if (prec < 0) prec = 0; fmt++; }
else while (*fmt >= '0' && *fmt <= '9') { prec = prec * 10 + (*fmt - '0'); fmt++; }
}
int is_long = 0, is_size_t = 0;
while (*fmt == 'l') { is_long++; fmt++; }
if (*fmt == 'z') { is_size_t = 1; fmt++; }
if (*fmt == 'h') { fmt++; }
char nb[40];
switch (*fmt) {
case 's': {
const char *s = va_arg(ap, const char *);
if (!s) s = "(null)";
size_t l = strlen(s);
if (prec >= 0 && (size_t)prec < l) l = (size_t)prec;
int pad = (int)(width > (int)l ? width - (int)l : 0);
if (!left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
__PUT(s, l);
if (left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
break;
}
case 'd': case 'i': {
int64_t v;
if (is_long >= 2) v = va_arg(ap, long long);
else if (is_long) v = va_arg(ap, long);
else if (is_size_t) v = (int64_t)va_arg(ap, size_t);
else v = va_arg(ap, int);
int neg = v < 0;
uint64_t u = neg ? (uint64_t)(-v) : (uint64_t)v;
__u64_to_str(u, nb, 10, 0);
int numlen = (int)strlen(nb) + (neg || plus_flag ? 1 : 0);
int pad = width > numlen ? width - numlen : 0;
if (!left_align && !pad_zero) for (int i = 0; i < pad; i++) __PUT(" ", 1);
if (neg) __PUT("-", 1);
else if (plus_flag) __PUT("+", 1);
if (!left_align && pad_zero) for (int i = 0; i < pad; i++) __PUT("0", 1);
__PUT(nb, strlen(nb));
if (left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
break;
}
case 'u': {
uint64_t v;
if (is_long >= 2) v = va_arg(ap, unsigned long long);
else if (is_long) v = va_arg(ap, unsigned long);
else if (is_size_t) v = va_arg(ap, size_t);
else v = va_arg(ap, unsigned);
__u64_to_str(v, nb, 10, 0);
int numlen = (int)strlen(nb);
int pad = width > numlen ? width - numlen : 0;
if (!left_align) for (int i = 0; i < pad; i++) __PUT(pad_zero ? "0" : " ", 1);
__PUT(nb, strlen(nb));
if (left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
break;
}
case 'x': case 'X': {
uint64_t v;
if (is_long >= 2) v = va_arg(ap, unsigned long long);
else if (is_long) v = va_arg(ap, unsigned long);
else if (is_size_t) v = va_arg(ap, size_t);
else v = va_arg(ap, unsigned);
__u64_to_str(v, nb, 16, *fmt == 'X');
int numlen = (int)strlen(nb);
int pad = width > numlen ? width - numlen : 0;
if (!left_align) for (int i = 0; i < pad; i++) __PUT(pad_zero ? "0" : " ", 1);
__PUT(nb, strlen(nb));
if (left_align) for (int i = 0; i < pad; i++) __PUT(" ", 1);
break;
}
case 'o': {
uint64_t v;
if (is_long >= 2) v = va_arg(ap, unsigned long long);
else if (is_long) v = va_arg(ap, unsigned long);
else v = va_arg(ap, unsigned);
__u64_to_str(v, nb, 8, 0);
__PUT(nb, strlen(nb));
break;
}
case 'p': {
uint64_t v = (uint64_t)(uintptr_t)va_arg(ap, void *);
__PUT("0x", 2);
__u64_to_str(v, nb, 16, 0);
__PUT(nb, strlen(nb));
break;
}
case 'c': {
char c = (char)va_arg(ap, int);
__PUT(&c, 1);
break;
}
case '%': __PUT("%", 1); break;
default: {
char c = *fmt;
__PUT("%", 1);
__PUT(&c, 1);
break;
}
}
fmt++;
}
if (sz > 0) buf[pos < sz ? pos : sz - 1] = '\0';
#undef __PUT
return (int)pos;
}
int snprintf(char *buf, size_t sz, const char *fmt, ...)
{
va_list ap; va_start(ap, fmt);
int n = vsnprintf(buf, sz, fmt, ap);
va_end(ap);
return n;
}
int sprintf(char *buf, const char *fmt, ...)
{
va_list ap; va_start(ap, fmt);
int n = vsnprintf(buf, (size_t)-1, fmt, ap);
va_end(ap);
return n;
}
int vsprintf(char *buf, const char *fmt, va_list ap)
{
return vsnprintf(buf, (size_t)-1, fmt, ap);
}
int vfprintf(FILE *s, const char *fmt, va_list ap)
{
char small[512];
va_list ap2;
va_copy(ap2, ap);
int needed = vsnprintf(small, sizeof(small), fmt, ap2);
va_end(ap2);
if (needed < (int)sizeof(small)) {
fwrite(small, 1, (size_t)needed, s);
return needed;
}
char *big = (char *)malloc((size_t)needed + 1);
if (!big) {
fwrite(small, 1, sizeof(small) - 1, s);
return (int)sizeof(small) - 1;
}
vsnprintf(big, (size_t)needed + 1, fmt, ap);
fwrite(big, 1, (size_t)needed, s);
free(big);
return needed;
}
int fprintf(FILE *s, const char *fmt, ...)
{
va_list ap; va_start(ap, fmt);
int n = vfprintf(s, fmt, ap);
va_end(ap);
return n;
}
int vprintf(const char *fmt, va_list ap) { return vfprintf(stdout, fmt, ap); }
int printf(const char *fmt, ...)
{
va_list ap; va_start(ap, fmt);
int n = vfprintf(stdout, fmt, ap);
va_end(ap);
return n;
}
void perror(const char *msg)
{
if (msg && *msg) { fputs(msg, stderr); fputs(": ", stderr); }
fputs(strerror(__cervus_errno), stderr);
fputc('\n', stderr);
}
int remove(const char *path) { return unlink(path); }
int mkstemp(char *template)
{
if (!template) { __cervus_errno = EINVAL; return -1; }
size_t len = strlen(template);
if (len < 6) { __cervus_errno = EINVAL; return -1; }
char *suf = template + len - 6;
for (int i = 0; i < 6; i++) {
if (suf[i] != 'X') { __cervus_errno = EINVAL; return -1; }
}
static uint64_t __mkstemp_seq = 0;
uint64_t pid = (uint64_t)getpid();
for (int attempt = 0; attempt < 100; attempt++) {
uint64_t seed = (cervus_uptime_ns() ^ (pid << 32)) + (__mkstemp_seq++);
const char *alpha = "0123456789abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < 6; i++) {
suf[i] = alpha[seed % 36];
seed /= 36;
}
struct stat st;
if (stat(template, &st) == 0) continue;
int fd = open(template, O_RDWR | O_CREAT, 0600);
if (fd >= 0) return fd;
}
__cervus_errno = EEXIST;
return -1;
}
FILE *tmpfile(void)
{
char tmpl[64];
strcpy(tmpl, "/mnt/tmp/tmpXXXXXX");
int fd = mkstemp(tmpl);
if (fd < 0) {
strcpy(tmpl, "/tmp/tmpXXXXXX");
fd = mkstemp(tmpl);
if (fd < 0) return NULL;
}
unlink(tmpl);
FILE *f = (FILE *)malloc(sizeof(FILE));
if (!f) { close(fd); return NULL; }
f->fd = fd;
f->eof = 0;
f->err = 0;
f->flags = 1;
f->buf = NULL;
f->buf_size = 0;
f->buf_pos = 0;
return f;
}
+138
View File
@@ -0,0 +1,138 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <sys/syscall.h>
extern int __cervus_errno;
extern int __cervus_argc;
extern char **__cervus_argv;
int abs(int x) { return x < 0 ? -x : x; }
long labs(long x) { return x < 0 ? -x : x; }
long long llabs(long long x){ return x < 0 ? -x : x; }
static long long __parse_signed(const char *s, char **end, int base, int is_unsigned)
{
while (isspace((unsigned char)*s)) s++;
int neg = 0;
if (*s == '-') { neg = 1; s++; }
else if (*s == '+') s++;
if ((base == 0 || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
s += 2; base = 16;
} else if (base == 0 && *s == '0') {
s++; base = 8;
} else if (base == 0) {
base = 10;
}
unsigned long long v = 0;
while (*s) {
int d;
if (isdigit((unsigned char)*s)) d = *s - '0';
else if (*s >= 'a' && *s <= 'z') d = *s - 'a' + 10;
else if (*s >= 'A' && *s <= 'Z') d = *s - 'A' + 10;
else break;
if (d >= base) break;
v = v * (unsigned long long)base + (unsigned long long)d;
s++;
}
if (end) *end = (char *)s;
if (is_unsigned) return (long long)v;
return neg ? -(long long)v : (long long)v;
}
long strtol(const char *s, char **e, int b) { return (long)__parse_signed(s, e, b, 0); }
long long strtoll(const char *s, char **e, int b) { return __parse_signed(s, e, b, 0); }
unsigned long strtoul(const char *s, char **e, int b) { return (unsigned long)__parse_signed(s, e, b, 1); }
unsigned long long strtoull(const char *s, char **e, int b) { return (unsigned long long)__parse_signed(s, e, b, 1); }
int atoi(const char *s) { return (int)strtol(s, NULL, 10); }
long atol(const char *s) { return strtol(s, NULL, 10); }
long long atoll(const char *s) { return strtoll(s, NULL, 10); }
static unsigned long __rand_state = 1;
int rand(void) { __rand_state = __rand_state * 1103515245UL + 12345UL; return (int)((__rand_state >> 16) & 0x7FFF); }
void srand(unsigned int seed) { __rand_state = seed; }
static void __qswap(void *a, void *b, size_t sz)
{
unsigned char tmp;
unsigned char *pa = (unsigned char *)a;
unsigned char *pb = (unsigned char *)b;
while (sz--) { tmp = *pa; *pa++ = *pb; *pb++ = tmp; }
}
void qsort(void *base, size_t nmemb, size_t sz, int (*cmp)(const void *, const void *))
{
if (nmemb < 2) return;
unsigned char *arr = (unsigned char *)base;
unsigned char *pivot = arr + (nmemb - 1) * sz;
size_t i = 0;
for (size_t j = 0; j < nmemb - 1; j++) {
if (cmp(arr + j * sz, pivot) <= 0) {
if (i != j) __qswap(arr + i * sz, arr + j * sz, sz);
i++;
}
}
__qswap(arr + i * sz, pivot, sz);
qsort(arr, i, sz, cmp);
qsort(arr + (i + 1) * sz, nmemb - i - 1, sz, cmp);
}
char *getenv(const char *name)
{
if (!name) return NULL;
size_t nl = strlen(name);
for (int i = 1; i < __cervus_argc; i++) {
const char *a = __cervus_argv[i];
if (a && a[0] == '-' && a[1] == '-' &&
a[2] == 'e' && a[3] == 'n' && a[4] == 'v' && a[5] == ':') {
const char *kv = a + 6;
if (strncmp(kv, name, nl) == 0 && kv[nl] == '=')
return (char *)(kv + nl + 1);
}
}
return NULL;
}
int system(const char *cmd)
{
(void)cmd;
__cervus_errno = ENOSYS;
return -1;
}
#define ATEXIT_MAX 32
static void (*__atexit_fns[ATEXIT_MAX])(void);
static int __atexit_cnt = 0;
int atexit(void (*fn)(void))
{
if (__atexit_cnt >= ATEXIT_MAX) return -1;
__atexit_fns[__atexit_cnt++] = fn;
return 0;
}
int fflush(FILE *stream);
void exit(int status)
{
while (__atexit_cnt > 0) {
__atexit_cnt--;
if (__atexit_fns[__atexit_cnt]) __atexit_fns[__atexit_cnt]();
}
extern FILE *stdout;
extern FILE *stderr;
fflush(stdout);
fflush(stderr);
syscall1(SYS_EXIT, status);
__builtin_unreachable();
}
void abort(void)
{
syscall1(SYS_EXIT, 134);
__builtin_unreachable();
}
+170
View File
@@ -0,0 +1,170 @@
#include <stdint.h>
#include <stddef.h>
uint64_t __cervus_strtod_bits(const char *s, char **endptr)
{
if (!s) {
if (endptr) *endptr = (char *)s;
return 0;
}
const char *p = s;
while (*p == ' ' || *p == '\t' || *p == '\n' ||
*p == '\r' || *p == '\f' || *p == '\v') p++;
int sign = 0;
if (*p == '+') p++;
else if (*p == '-') { sign = 1; p++; }
if ((p[0] == 'i' || p[0] == 'I') &&
(p[1] == 'n' || p[1] == 'N') &&
(p[2] == 'f' || p[2] == 'F')) {
p += 3;
if ((p[0] == 'i' || p[0] == 'I') &&
(p[1] == 'n' || p[1] == 'N') &&
(p[2] == 'i' || p[2] == 'I') &&
(p[3] == 't' || p[3] == 'T') &&
(p[4] == 'y' || p[4] == 'Y')) p += 5;
if (endptr) *endptr = (char *)p;
return ((uint64_t)sign << 63) | 0x7FF0000000000000ULL;
}
if ((p[0] == 'n' || p[0] == 'N') &&
(p[1] == 'a' || p[1] == 'A') &&
(p[2] == 'n' || p[2] == 'N')) {
p += 3;
if (endptr) *endptr = (char *)p;
return 0x7FF8000000000000ULL;
}
uint64_t mant = 0;
int dec_exp = 0;
int seen_digit = 0;
while (*p >= '0' && *p <= '9') {
seen_digit = 1;
if (mant <= (UINT64_MAX - 9) / 10) {
mant = mant * 10 + (uint64_t)(*p - '0');
} else {
dec_exp++;
}
p++;
}
if (*p == '.') {
p++;
while (*p >= '0' && *p <= '9') {
seen_digit = 1;
if (mant <= (UINT64_MAX - 9) / 10) {
mant = mant * 10 + (uint64_t)(*p - '0');
dec_exp--;
}
p++;
}
}
if (!seen_digit) {
if (endptr) *endptr = (char *)s;
return 0;
}
if (*p == 'e' || *p == 'E') {
const char *ep = p + 1;
int esign = 0;
if (*ep == '+') ep++;
else if (*ep == '-') { esign = 1; ep++; }
if (*ep >= '0' && *ep <= '9') {
int eval = 0;
while (*ep >= '0' && *ep <= '9') {
if (eval < 10000) eval = eval * 10 + (*ep - '0');
ep++;
}
dec_exp += esign ? -eval : eval;
p = ep;
}
}
if (endptr) *endptr = (char *)p;
if (mant == 0) {
return (uint64_t)sign << 63;
}
int bin_exp = 0;
while ((mant >> 63) == 0) {
mant <<= 1;
bin_exp--;
}
while (dec_exp > 0) {
uint64_t a_hi = mant >> 32;
uint64_t a_lo = mant & 0xFFFFFFFFULL;
uint64_t p_lo = a_lo * 10;
uint64_t p_hi = a_hi * 10;
uint64_t mid_carry = p_lo >> 32;
uint64_t lo = ((p_hi + mid_carry) << 32) | (p_lo & 0xFFFFFFFFULL);
uint64_t hi = (p_hi + mid_carry) >> 32;
while (hi != 0) {
lo = (lo >> 1) | (hi << 63);
hi >>= 1;
bin_exp++;
}
mant = lo;
while ((mant >> 63) == 0) {
mant <<= 1;
bin_exp--;
}
dec_exp--;
}
while (dec_exp < 0) {
mant = mant / 10;
if (mant == 0) break;
while ((mant >> 63) == 0) {
mant <<= 1;
bin_exp--;
}
dec_exp++;
}
int ieee_exp = bin_exp + 63 + 1023;
uint64_t no_implicit = mant & 0x7FFFFFFFFFFFFFFFULL;
uint64_t round_bit = (no_implicit >> 10) & 1;
uint64_t sticky = (no_implicit & 0x3FFULL) ? 1 : 0;
uint64_t frac = no_implicit >> 11;
if (round_bit && (sticky || (frac & 1))) {
frac++;
if (frac == (1ULL << 52)) {
frac = 0;
ieee_exp++;
}
}
if (ieee_exp >= 0x7FF) {
return ((uint64_t)sign << 63) | 0x7FF0000000000000ULL;
}
if (ieee_exp <= 0) {
return (uint64_t)sign << 63;
}
return ((uint64_t)sign << 63) |
((uint64_t)ieee_exp << 52) |
(frac & 0xFFFFFFFFFFFFFULL);
}
double strtod(const char *s, char **endptr)
{
uint64_t bits = __cervus_strtod_bits(s, endptr);
double result;
__builtin_memcpy(&result, &bits, sizeof(double));
return result;
}
float strtof(const char *s, char **endptr)
{
return (float)strtod(s, endptr);
}
long double strtold(const char *s, char **endptr)
{
return (long double)strtod(s, endptr);
}
double atof(const char *s)
{
return strtod(s, NULL);
}
+296
View File
@@ -0,0 +1,296 @@
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
void *memset(void *d, int c, size_t n)
{
unsigned char *p = (unsigned char *)d;
while (n--) *p++ = (unsigned char)c;
return d;
}
void *memcpy(void *d, const void *s, size_t n)
{
unsigned char *dd = (unsigned char *)d;
const unsigned char *ss = (const unsigned char *)s;
while (n--) *dd++ = *ss++;
return d;
}
void *memmove(void *d, const void *s, size_t n)
{
unsigned char *dd = (unsigned char *)d;
const unsigned char *ss = (const unsigned char *)s;
if (dd < ss) { while (n--) *dd++ = *ss++; }
else { dd += n; ss += n; while (n--) *--dd = *--ss; }
return d;
}
int memcmp(const void *a, const void *b, size_t n)
{
const unsigned char *x = (const unsigned char *)a;
const unsigned char *y = (const unsigned char *)b;
for (size_t i = 0; i < n; i++)
if (x[i] != y[i]) return x[i] - y[i];
return 0;
}
void *memchr(const void *s, int c, size_t n)
{
const unsigned char *p = (const unsigned char *)s;
while (n--) { if (*p == (unsigned char)c) return (void *)p; p++; }
return NULL;
}
void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen)
{
if (nlen == 0) return (void *)haystack;
if (hlen < nlen) return NULL;
const unsigned char *h = (const unsigned char *)haystack;
const unsigned char *n = (const unsigned char *)needle;
size_t last = hlen - nlen;
for (size_t i = 0; i <= last; i++) {
if (h[i] == n[0] && memcmp(h + i, n, nlen) == 0)
return (void *)(h + i);
}
return NULL;
}
size_t strlen(const char *s) { size_t n = 0; while (s[n]) n++; return n; }
size_t strnlen(const char *s, size_t max)
{
size_t n = 0;
while (n < max && s[n]) n++;
return n;
}
int strcmp(const char *a, const char *b)
{
while (*a && *a == *b) { a++; b++; }
return (unsigned char)*a - (unsigned char)*b;
}
int strncmp(const char *a, const char *b, size_t n)
{
for (size_t i = 0; i < n; i++) {
if (a[i] != b[i]) return (unsigned char)a[i] - (unsigned char)b[i];
if (!a[i]) return 0;
}
return 0;
}
int strcasecmp(const char *a, const char *b)
{
while (*a && tolower((unsigned char)*a) == tolower((unsigned char)*b)) {
a++; b++;
}
return tolower((unsigned char)*a) - tolower((unsigned char)*b);
}
int strncasecmp(const char *a, const char *b, size_t n)
{
for (size_t i = 0; i < n; i++) {
int ca = tolower((unsigned char)a[i]);
int cb = tolower((unsigned char)b[i]);
if (ca != cb) return ca - cb;
if (!a[i]) return 0;
}
return 0;
}
char *strcpy(char *d, const char *s)
{
char *r = d;
while ((*d++ = *s++)) { }
return r;
}
char *strncpy(char *d, const char *s, size_t n)
{
size_t i;
for (i = 0; i < n && s[i]; i++) d[i] = s[i];
for (; i < n; i++) d[i] = 0;
return d;
}
char *strcat(char *d, const char *s)
{
char *r = d;
while (*d) d++;
while ((*d++ = *s++)) { }
return r;
}
char *strncat(char *d, const char *s, size_t n)
{
char *r = d;
while (*d) d++;
for (size_t i = 0; i < n && s[i]; i++) *d++ = s[i];
*d = '\0';
return r;
}
char *strchr(const char *s, int c)
{
for (; *s; s++) if (*s == (char)c) return (char *)s;
return c == 0 ? (char *)s : NULL;
}
char *strrchr(const char *s, int c)
{
const char *r = NULL;
for (; *s; s++) if (*s == (char)c) r = s;
return (char *)(c == 0 ? s : r);
}
char *strstr(const char *h, const char *n)
{
if (!*n) return (char *)h;
for (; *h; h++) {
const char *a = h, *b = n;
while (*a && *b && *a == *b) { a++; b++; }
if (!*b) return (char *)h;
}
return NULL;
}
size_t strspn(const char *s, const char *accept)
{
size_t n = 0;
while (s[n]) {
int found = 0;
for (size_t i = 0; accept[i]; i++)
if (s[n] == accept[i]) { found = 1; break; }
if (!found) break;
n++;
}
return n;
}
size_t strcspn(const char *s, const char *reject)
{
size_t n = 0;
while (s[n]) {
for (size_t i = 0; reject[i]; i++)
if (s[n] == reject[i]) return n;
n++;
}
return n;
}
char *strpbrk(const char *s, const char *accept)
{
while (*s) {
for (const char *a = accept; *a; a++)
if (*s == *a) return (char *)s;
s++;
}
return NULL;
}
char *strdup(const char *s)
{
size_t n = strlen(s) + 1;
char *p = (char *)malloc(n);
if (!p) return NULL;
memcpy(p, s, n);
return p;
}
char *strndup(const char *s, size_t n)
{
size_t len = strnlen(s, n);
char *p = (char *)malloc(len + 1);
if (!p) return NULL;
memcpy(p, s, len);
p[len] = '\0';
return p;
}
char *strtok_r(char *str, const char *delim, char **saveptr)
{
char *s = str ? str : (saveptr ? *saveptr : NULL);
if (!s || !delim) return NULL;
while (*s) {
const char *d;
for (d = delim; *d; d++) if (*s == *d) break;
if (!*d) break;
s++;
}
if (!*s) {
if (saveptr) *saveptr = NULL;
return NULL;
}
char *tok = s;
while (*s) {
const char *d;
for (d = delim; *d; d++) if (*s == *d) break;
if (*d) {
*s = '\0';
if (saveptr) *saveptr = s + 1;
return tok;
}
s++;
}
if (saveptr) *saveptr = NULL;
return tok;
}
static char *__strtok_save = NULL;
char *strtok(char *str, const char *delim)
{
return strtok_r(str, delim, &__strtok_save);
}
char *strerror(int err)
{
switch (err) {
case 0: return "Success";
case EPERM: return "Operation not permitted";
case ENOENT: return "No such file or directory";
case ESRCH: return "No such process";
case EINTR: return "Interrupted system call";
case EIO: return "Input/output error";
case EBADF: return "Bad file descriptor";
case ECHILD: return "No child processes";
case EAGAIN: return "Resource temporarily unavailable";
case ENOMEM: return "Cannot allocate memory";
case EACCES: return "Permission denied";
case EFAULT: return "Bad address";
case EBUSY: return "Device or resource busy";
case EEXIST: return "File exists";
case ENODEV: return "No such device";
case ENOTDIR: return "Not a directory";
case EISDIR: return "Is a directory";
case EINVAL: return "Invalid argument";
case EMFILE: return "Too many open files";
case ENOTTY: return "Inappropriate ioctl for device";
case ENOSPC: return "No space left on device";
case EPIPE: return "Broken pipe";
case ENOSYS: return "Function not implemented";
default: return "Unknown error";
}
}
char *strsignal(int sig)
{
switch (sig) {
case 1: return "Hangup";
case 2: return "Interrupt";
case 3: return "Quit";
case 4: return "Illegal instruction";
case 6: return "Aborted";
case 8: return "Floating point exception";
case 9: return "Killed";
case 11: return "Segmentation fault";
case 13: return "Broken pipe";
case 14: return "Alarm clock";
case 15: return "Terminated";
default: return "Unknown signal";
}
}
+205
View File
@@ -0,0 +1,205 @@
#include <time.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
extern int __cervus_errno;
typedef struct { int64_t tv_sec; int64_t tv_nsec; } __cervus_ts_raw_t;
time_t time(time_t *t)
{
__cervus_ts_raw_t ts = {0, 0};
syscall2(SYS_CLOCK_GET, 0, &ts);
time_t v = (time_t)ts.tv_sec;
if (t) *t = v;
return v;
}
int clock_gettime(int clk, struct timespec *tp)
{
if (!tp) { __cervus_errno = EINVAL; return -1; }
__cervus_ts_raw_t ts = {0, 0};
long r = syscall2(SYS_CLOCK_GET, clk, &ts);
if (r < 0 && r > -4096) { __cervus_errno = (int)-r; return -1; }
tp->tv_sec = (time_t)ts.tv_sec;
tp->tv_nsec = (long)ts.tv_nsec;
return 0;
}
clock_t clock(void)
{
uint64_t up_ns = (uint64_t)syscall0(SYS_UPTIME);
return (clock_t)(up_ns / 1000ULL);
}
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
(void)tz;
if (!tv) { __cervus_errno = EINVAL; return -1; }
__cervus_ts_raw_t ts = {0, 0};
syscall2(SYS_CLOCK_GET, 0, &ts);
tv->tv_sec = (time_t)ts.tv_sec;
tv->tv_usec = (long)(ts.tv_nsec / 1000);
return 0;
}
int nanosleep(const struct timespec *req, struct timespec *rem)
{
if (!req) { __cervus_errno = EINVAL; return -1; }
uint64_t ns = (uint64_t)req->tv_sec * 1000000000ULL + (uint64_t)req->tv_nsec;
long r = syscall1(SYS_SLEEP_NS, ns);
if (rem) { rem->tv_sec = 0; rem->tv_nsec = 0; }
if (r < 0 && r > -4096) { __cervus_errno = (int)-r; return -1; }
return 0;
}
static int __is_leap(int y)
{
return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
}
static const int __days_in_mon[2][12] = {
{31,28,31,30,31,30,31,31,30,31,30,31},
{31,29,31,30,31,30,31,31,30,31,30,31},
};
static struct tm __tm_buf;
struct tm *gmtime(const time_t *t)
{
if (!t) return NULL;
long long sec = (long long)*t;
long days = (long)(sec / 86400);
long rem = (long)(sec % 86400);
if (rem < 0) { rem += 86400; days--; }
__tm_buf.tm_hour = rem / 3600;
rem -= __tm_buf.tm_hour * 3600;
__tm_buf.tm_min = rem / 60;
__tm_buf.tm_sec = rem - __tm_buf.tm_min * 60;
__tm_buf.tm_wday = (int)((days + 4) % 7);
if (__tm_buf.tm_wday < 0) __tm_buf.tm_wday += 7;
int year = 1970;
while (1) {
int ly = __is_leap(year);
int dy = ly ? 366 : 365;
if (days >= dy) { days -= dy; year++; }
else if (days < 0) { year--; days += __is_leap(year) ? 366 : 365; }
else break;
}
__tm_buf.tm_year = year - 1900;
__tm_buf.tm_yday = (int)days;
int ly = __is_leap(year);
int m = 0;
while (m < 12 && days >= __days_in_mon[ly][m]) {
days -= __days_in_mon[ly][m];
m++;
}
__tm_buf.tm_mon = m;
__tm_buf.tm_mday = (int)days + 1;
__tm_buf.tm_isdst = 0;
return &__tm_buf;
}
struct tm *localtime(const time_t *t) { return gmtime(t); }
time_t mktime(struct tm *tm)
{
if (!tm) return (time_t)-1;
int year = tm->tm_year + 1900;
int mon = tm->tm_mon;
long days = 0;
for (int y = 1970; y < year; y++) days += __is_leap(y) ? 366 : 365;
int ly = __is_leap(year);
for (int m = 0; m < mon; m++) days += __days_in_mon[ly][m];
days += tm->tm_mday - 1;
long long sec = (long long)days * 86400LL
+ (long long)tm->tm_hour * 3600LL
+ (long long)tm->tm_min * 60LL
+ (long long)tm->tm_sec;
return (time_t)sec;
}
static const char *__wday_name[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static const char *__mon_name[] = {"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"};
static char __asctime_buf[32];
char *asctime(const struct tm *tm)
{
if (!tm) return NULL;
int wday = tm->tm_wday; if (wday < 0 || wday > 6) wday = 0;
int mon = tm->tm_mon; if (mon < 0 || mon > 11) mon = 0;
int y = tm->tm_year + 1900;
int pos = 0;
const char *w = __wday_name[wday];
const char *mn = __mon_name[mon];
__asctime_buf[pos++] = w[0]; __asctime_buf[pos++] = w[1]; __asctime_buf[pos++] = w[2];
__asctime_buf[pos++] = ' ';
__asctime_buf[pos++] = mn[0]; __asctime_buf[pos++] = mn[1]; __asctime_buf[pos++] = mn[2];
__asctime_buf[pos++] = ' ';
int md = tm->tm_mday;
__asctime_buf[pos++] = (char)('0' + (md/10 % 10));
__asctime_buf[pos++] = (char)('0' + (md % 10));
__asctime_buf[pos++] = ' ';
int hh = tm->tm_hour, mm = tm->tm_min, ss = tm->tm_sec;
__asctime_buf[pos++] = (char)('0' + (hh/10 % 10));
__asctime_buf[pos++] = (char)('0' + (hh % 10));
__asctime_buf[pos++] = ':';
__asctime_buf[pos++] = (char)('0' + (mm/10 % 10));
__asctime_buf[pos++] = (char)('0' + (mm % 10));
__asctime_buf[pos++] = ':';
__asctime_buf[pos++] = (char)('0' + (ss/10 % 10));
__asctime_buf[pos++] = (char)('0' + (ss % 10));
__asctime_buf[pos++] = ' ';
__asctime_buf[pos++] = (char)('0' + (y/1000 % 10));
__asctime_buf[pos++] = (char)('0' + (y/100 % 10));
__asctime_buf[pos++] = (char)('0' + (y/10 % 10));
__asctime_buf[pos++] = (char)('0' + (y % 10));
__asctime_buf[pos++] = '\n';
__asctime_buf[pos] = '\0';
return __asctime_buf;
}
char *ctime(const time_t *t) { return asctime(gmtime(t)); }
size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm)
{
if (!s || !fmt || !tm || max == 0) return 0;
size_t i = 0;
while (*fmt && i + 1 < max) {
if (*fmt != '%') { s[i++] = *fmt++; continue; }
fmt++;
char tmp[16];
int n = 0;
switch (*fmt) {
case 'Y': {
int y = tm->tm_year + 1900;
n = 4;
tmp[0] = (char)('0' + (y/1000)%10);
tmp[1] = (char)('0' + (y/100)%10);
tmp[2] = (char)('0' + (y/10)%10);
tmp[3] = (char)('0' + y%10);
break;
}
case 'm': { int v=tm->tm_mon+1; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
case 'd': { int v=tm->tm_mday; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
case 'H': { int v=tm->tm_hour; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
case 'M': { int v=tm->tm_min; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
case 'S': { int v=tm->tm_sec; tmp[0]=(char)('0'+v/10); tmp[1]=(char)('0'+v%10); n=2; break; }
case '%': tmp[0]='%'; n=1; break;
default: tmp[0]='%'; tmp[1]=*fmt; n = (*fmt ? 2 : 1); break;
}
for (int k = 0; k < n && i + 1 < max; k++) s[i++] = tmp[k];
if (*fmt) fmt++;
}
s[i] = '\0';
return i;
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef _ASSERT_H
#define _ASSERT_H
#ifdef NDEBUG
#define assert(cond) ((void)0)
#else
void __cervus_assert_fail(const char *expr, const char *file, int line, const char *func)
__attribute__((noreturn));
#define assert(cond) \
((cond) ? (void)0 : __cervus_assert_fail(#cond, __FILE__, __LINE__, __func__))
#endif
#endif
+162
View File
@@ -0,0 +1,162 @@
#ifndef _CERVUS_UTIL_H
#define _CERVUS_UTIL_H
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#define C_RESET "\x1b[0m"
#define C_BOLD "\x1b[1m"
#define C_RED "\x1b[1;31m"
#define C_GREEN "\x1b[1;32m"
#define C_YELLOW "\x1b[1;33m"
#define C_BLUE "\x1b[1;34m"
#define C_MAGENTA "\x1b[1;35m"
#define C_CYAN "\x1b[1;36m"
#define C_GRAY "\x1b[90m"
static inline void path_join(const char *base, const char *name,
char *out, size_t sz)
{
if (!name || !name[0]) {
strncpy(out, base ? base : "", sz - 1);
out[sz - 1] = '\0';
return;
}
if (name[0] == '/') {
strncpy(out, name, sz - 1);
out[sz - 1] = '\0';
return;
}
strncpy(out, base ? base : "", sz - 1);
out[sz - 1] = '\0';
size_t bl = strlen(out);
if (bl > 0 && out[bl - 1] != '/' && bl + 1 < sz) {
out[bl++] = '/';
out[bl] = '\0';
}
size_t nl = strlen(name);
if (bl + nl + 1 < sz) memcpy(out + bl, name, nl + 1);
else out[sz - 1] = '\0';
}
static inline void path_norm(char *path)
{
char tmp[512];
strncpy(tmp, path, 511);
tmp[511] = '\0';
char *parts[64];
int np = 0;
char *p = tmp;
while (*p) {
while (*p == '/') p++;
if (!*p) break;
char *s = p;
while (*p && *p != '/') p++;
if (*p) *p++ = '\0';
if (strcmp(s, ".") == 0) continue;
if (strcmp(s, "..") == 0) { if (np > 0) np--; continue; }
if (np < 64) parts[np++] = s;
}
char out[512];
size_t ol = 0;
for (int i = 0; i < np; i++) {
if (ol + 1 >= sizeof(out)) break;
out[ol++] = '/';
size_t pl = strlen(parts[i]);
if (ol + pl >= sizeof(out)) pl = sizeof(out) - 1 - ol;
memcpy(out + ol, parts[i], pl);
ol += pl;
}
out[ol] = '\0';
if (ol == 0) { out[0] = '/'; out[1] = '\0'; }
strncpy(path, out, 512);
}
static inline void resolve_path(const char *cwd, const char *path,
char *out, size_t sz)
{
if (!path || !path[0]) {
strncpy(out, cwd ? cwd : "/", sz - 1);
out[sz - 1] = '\0';
return;
}
if (path[0] == '/') {
strncpy(out, path, sz - 1);
out[sz - 1] = '\0';
return;
}
path_join(cwd, path, out, sz);
path_norm(out);
}
extern int __cervus_argc;
extern char **__cervus_argv;
static inline int is_shell_flag(const char *a)
{
if (!a) return 0;
if (a[0] == '-' && a[1] == '-' &&
a[2] == 'c' && a[3] == 'w' && a[4] == 'd' && a[5] == '=') return 1;
if (a[0] == '-' && a[1] == '-' &&
a[2] == 'e' && a[3] == 'n' && a[4] == 'v' && a[5] == ':') return 1;
return 0;
}
static inline const char *get_cwd_flag(int argc, char **argv)
{
(void)argc; (void)argv;
for (int i = 1; i < __cervus_argc; i++) {
char *a = __cervus_argv[i];
if (a && a[0] == '-' && a[1] == '-' &&
a[2] == 'c' && a[3] == 'w' && a[4] == 'd' && a[5] == '=')
return a + 6;
}
return "/";
}
static inline const char *getenv_argv(int argc, char **argv,
const char *name, const char *def)
{
(void)argc; (void)argv;
if (!name) return def;
size_t nl = strlen(name);
for (int i = 1; i < __cervus_argc; i++) {
const char *a = __cervus_argv[i];
if (!a) continue;
if (a[0] == '-' && a[1] == '-' &&
a[2] == 'e' && a[3] == 'n' && a[4] == 'v' && a[5] == ':') {
const char *kv = a + 6;
if (strncmp(kv, name, nl) == 0 && kv[nl] == '=')
return kv + nl + 1;
}
}
return def;
}
static inline int util_readline(int fd, char *buf, int maxlen)
{
int i = 0;
while (i < maxlen - 1) {
char c;
ssize_t r = read(fd, &c, 1);
if (r <= 0) { buf[i] = '\0'; return i > 0 ? i : -1; }
if (c == '\r') continue;
buf[i++] = c;
if (c == '\n') break;
}
buf[i] = '\0';
return i;
}
static inline void util_write_stdout(const char *s)
{
write(1, s, strlen(s));
}
static inline void util_write_stderr(const char *s)
{
write(2, s, strlen(s));
}
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef _CTYPE_H
#define _CTYPE_H
int isdigit(int c);
int isalpha(int c);
int isalnum(int c);
int isupper(int c);
int islower(int c);
int isspace(int c);
int isprint(int c);
int isgraph(int c);
int ispunct(int c);
int isxdigit(int c);
int iscntrl(int c);
int isblank(int c);
int toupper(int c);
int tolower(int c);
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef _DIRENT_H
#define _DIRENT_H
#include <sys/types.h>
#define DT_UNKNOWN 0
#define DT_FILE 0
#define DT_REG 0
#define DT_DIR 1
#define DT_CHR 2
#define DT_BLK 3
#define DT_LNK 4
#define DT_PIPE 5
#define DT_FIFO 5
struct dirent {
ino_t d_ino;
uint8_t d_type;
char d_name[256];
};
typedef struct __cervus_DIR DIR;
DIR *opendir(const char *path);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);
void rewinddir(DIR *dirp);
int dirfd(DIR *dirp);
#endif
+45
View File
@@ -0,0 +1,45 @@
#ifndef _ERRNO_H
#define _ERRNO_H
extern int __cervus_errno;
#define errno __cervus_errno
#define EPERM 1
#define ENOENT 2
#define ESRCH 3
#define EINTR 4
#define EIO 5
#define ENXIO 6
#define E2BIG 7
#define ENOEXEC 8
#define EBADF 9
#define ECHILD 10
#define EAGAIN 11
#define ENOMEM 12
#define EACCES 13
#define EFAULT 14
#define EBUSY 16
#define EEXIST 17
#define EXDEV 18
#define ENODEV 19
#define ENOTDIR 20
#define EISDIR 21
#define EINVAL 22
#define ENFILE 23
#define EMFILE 24
#define ENOTTY 25
#define EFBIG 27
#define ENOSPC 28
#define ESPIPE 29
#define EROFS 30
#define EMLINK 31
#define EPIPE 32
#define EDOM 33
#define ERANGE 34
#define ENAMETOOLONG 36
#define ENOSYS 38
#define ENOTEMPTY 39
#define ELOOP 40
#define EOVERFLOW 75
#endif
+27
View File
@@ -0,0 +1,27 @@
#ifndef _FCNTL_H
#define _FCNTL_H
#include <sys/types.h>
#define O_RDONLY 0x000
#define O_WRONLY 0x001
#define O_RDWR 0x002
#define O_ACCMODE 0x003
#define O_CREAT 0x040
#define O_TRUNC 0x200
#define O_APPEND 0x400
#define O_NONBLOCK 0x800
#define O_DIRECTORY 0x10000
#define O_CLOEXEC 0x80000
#define F_GETFD 1
#define F_SETFD 2
#define F_GETFL 3
#define F_SETFL 4
#define FD_CLOEXEC 1
int open(const char *path, int flags, ...);
int fcntl(int fd, int cmd, ...);
int creat(const char *path, mode_t mode);
#endif
+76
View File
@@ -0,0 +1,76 @@
#ifndef _INTTYPES_H
#define _INTTYPES_H
#include <stdint.h>
#define PRId8 "d"
#define PRId16 "d"
#define PRId32 "d"
#define PRId64 "ld"
#define PRIu8 "u"
#define PRIu16 "u"
#define PRIu32 "u"
#define PRIu64 "lu"
#define PRIx8 "x"
#define PRIx16 "x"
#define PRIx32 "x"
#define PRIx64 "lx"
#define PRIX8 "X"
#define PRIX16 "X"
#define PRIX32 "X"
#define PRIX64 "lX"
#define PRIi8 "i"
#define PRIi16 "i"
#define PRIi32 "i"
#define PRIi64 "li"
#define PRIo8 "o"
#define PRIo16 "o"
#define PRIo32 "o"
#define PRIo64 "lo"
#define PRIdMAX "ld"
#define PRIuMAX "lu"
#define PRIxMAX "lx"
#define PRIiMAX "li"
#define PRIdPTR "ld"
#define PRIuPTR "lu"
#define PRIxPTR "lx"
#define SCNd8 "hhd"
#define SCNd16 "hd"
#define SCNd32 "d"
#define SCNd64 "ld"
#define SCNu8 "hhu"
#define SCNu16 "hu"
#define SCNu32 "u"
#define SCNu64 "lu"
#define SCNx8 "hhx"
#define SCNx16 "hx"
#define SCNx32 "x"
#define SCNx64 "lx"
typedef struct {
intmax_t quot;
intmax_t rem;
} imaxdiv_t;
static inline intmax_t imaxabs(intmax_t j) {
return j < 0 ? -j : j;
}
static inline imaxdiv_t imaxdiv(intmax_t numer, intmax_t denom) {
imaxdiv_t r;
r.quot = numer / denom;
r.rem = numer % denom;
return r;
}
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef _LIMITS_H
#define _LIMITS_H
#define CHAR_BIT 8
#define SCHAR_MIN (-128)
#define SCHAR_MAX (127)
#define UCHAR_MAX (255)
#define CHAR_MIN SCHAR_MIN
#define CHAR_MAX SCHAR_MAX
#define SHRT_MIN (-32768)
#define SHRT_MAX (32767)
#define USHRT_MAX (65535)
#define INT_MIN (-2147483647 - 1)
#define INT_MAX (2147483647)
#define UINT_MAX (4294967295u)
#define LONG_MIN (-9223372036854775807L - 1)
#define LONG_MAX (9223372036854775807L)
#define ULONG_MAX (18446744073709551615UL)
#define LLONG_MIN LONG_MIN
#define LLONG_MAX LONG_MAX
#define ULLONG_MAX ULONG_MAX
#define PATH_MAX 512
#define NAME_MAX 255
#endif
+77
View File
@@ -0,0 +1,77 @@
#ifndef _MATH_H
#define _MATH_H
#include <stdint.h>
#define INFINITY (1.0/0.0)
#define NAN (0.0/0.0)
int abs(int x);
double fabs(double x);
double pow(double base, double exp);
double pow10(int n);
int isinf(double x);
int isnan(double x);
static inline double ldexp(double x, int exp) {
union { double d; uint64_t u; } v;
v.d = x;
int e = (int)((v.u >> 52) & 0x7FF);
if (e == 0 || e == 0x7FF)
return x;
e += exp;
if (e <= 0)
return 0.0;
if (e >= 0x7FF)
return INFINITY;
v.u = (v.u & ~((uint64_t)0x7FF << 52)) | ((uint64_t)e << 52);
return v.d;
}
static inline double floor(double x) {
int64_t i = (int64_t)x;
return (double)(i - (x < (double)i));
}
static inline double ceil(double x) {
int64_t i = (int64_t)x;
return (double)(i + (x > (double)i));
}
static inline double round(double x) {
return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}
static inline double sqrt(double x) {
double result;
asm volatile ("sqrtsd %1, %0" : "=x"(result) : "x"(x));
return result;
}
static inline float sqrtf(float x) {
float result;
asm volatile ("sqrtss %1, %0" : "=x"(result) : "x"(x));
return result;
}
static inline double log2(double x) {
double result;
asm volatile (
"fld1\n\t"
"fld %1\n\t"
"fyl2x\n\t"
"fstp %0\n\t"
: "=m"(result) : "m"(x)
);
return result;
}
#define MIN(a, b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a < _b ? _a : _b; })
#define MAX(a, b) ({ __typeof__(a) _a = (a); __typeof__(b) _b = (b); _a > _b ? _a : _b; })
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
#define IS_POWER_OF_TWO(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
#endif
+13
View File
@@ -0,0 +1,13 @@
#ifndef _SETJMP_H
#define _SETJMP_H
typedef long jmp_buf[8];
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val) __attribute__((noreturn));
#define sigjmp_buf jmp_buf
#define sigsetjmp(env, savesigs) setjmp(env)
#define siglongjmp(env, val) longjmp(env, val)
#endif
+169
View File
@@ -0,0 +1,169 @@
#ifndef _SIGNAL_H
#define _SIGNAL_H
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
typedef int sig_atomic_t;
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT SIGABRT
#define SIGBUS 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGCHLD 17
#define SIGCONT 18
#define SIGSTOP 19
#define SIGTSTP 20
#define SIGTTIN 21
#define SIGTTOU 22
#define SIGWINCH 28
#define NSIG 32
typedef void (*sighandler_t)(int);
#define SIG_DFL ((sighandler_t)0)
#define SIG_IGN ((sighandler_t)1)
#define SIG_ERR ((sighandler_t)-1)
#define SA_NOCLDSTOP 0x00000001
#define SA_NOCLDWAIT 0x00000002
#define SA_SIGINFO 0x00000004
#define SA_ONSTACK 0x08000000
#define SA_RESTART 0x10000000
#define SA_NODEFER 0x40000000
#define SA_RESETHAND 0x80000000
#define SI_USER 0
#define SI_KERNEL 0x80
#define SI_QUEUE -1
#define SI_TIMER -2
#define SI_MESGQ -3
#define SI_ASYNCIO -4
#define SI_SIGIO -5
#define SI_TKILL -6
#define FPE_INTDIV 1
#define FPE_INTOVF 2
#define FPE_FLTDIV 3
#define FPE_FLTOVF 4
#define FPE_FLTUND 5
#define FPE_FLTRES 6
#define FPE_FLTINV 7
#define FPE_FLTSUB 8
#define ILL_ILLOPC 1
#define ILL_ILLOPN 2
#define ILL_ILLADR 3
#define ILL_ILLTRP 4
#define ILL_PRVOPC 5
#define ILL_PRVREG 6
#define ILL_COPROC 7
#define ILL_BADSTK 8
#define SEGV_MAPERR 1
#define SEGV_ACCERR 2
#define BUS_ADRALN 1
#define BUS_ADRERR 2
#define BUS_OBJERR 3
typedef struct {
unsigned long __bits[2];
} sigset_t;
union sigval {
int sival_int;
void *sival_ptr;
};
typedef struct {
int si_signo;
int si_errno;
int si_code;
union {
struct {
pid_t si_pid;
uid_t si_uid;
} _kill;
struct {
int si_tid;
int si_overrun;
union sigval si_sigval;
} _timer;
struct {
pid_t si_pid;
uid_t si_uid;
union sigval si_sigval;
} _rt;
struct {
pid_t si_pid;
uid_t si_uid;
int si_status;
} _sigchld;
struct {
void *si_addr;
} _sigfault;
struct {
long si_band;
int si_fd;
} _sigpoll;
} _sifields;
} siginfo_t;
#define si_pid _sifields._kill.si_pid
#define si_uid _sifields._kill.si_uid
#define si_status _sifields._sigchld.si_status
#define si_addr _sifields._sigfault.si_addr
#define si_band _sifields._sigpoll.si_band
#define si_fd _sifields._sigpoll.si_fd
typedef struct {
void *ss_sp;
int ss_flags;
size_t ss_size;
} stack_t;
#define SS_ONSTACK 1
#define SS_DISABLE 2
struct sigaction {
union {
sighandler_t sa_handler;
void (*sa_sigaction)(int, siginfo_t *, void *);
};
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
sighandler_t signal(int signum, sighandler_t handler);
int raise(int sig);
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact);
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);
int sigismember(const sigset_t *set, int sig);
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigaltstack(const stack_t *ss, stack_t *oss);
#define SIG_BLOCK 0
#define SIG_UNBLOCK 1
#define SIG_SETMASK 2
#endif
+11
View File
@@ -0,0 +1,11 @@
#ifndef _STDARG_H
#define _STDARG_H
typedef __builtin_va_list va_list;
#define va_start(ap, last) __builtin_va_start(ap, last)
#define va_arg(ap, t) __builtin_va_arg(ap, t)
#define va_end(ap) __builtin_va_end(ap)
#define va_copy(d, s) __builtin_va_copy(d, s)
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef _STDBOOL_H
#define _STDBOOL_H
#ifndef __cplusplus
#define bool _Bool
#define true 1
#define false 0
#endif
#define __bool_true_false_are_defined 1
#endif
+15
View File
@@ -0,0 +1,15 @@
#ifndef _STDDEF_H
#define _STDDEF_H
#ifndef NULL
#define NULL ((void*)0)
#endif
typedef unsigned long size_t;
typedef long ssize_t;
typedef long ptrdiff_t;
typedef int wchar_t;
#define offsetof(t, m) __builtin_offsetof(t, m)
#endif
+48
View File
@@ -0,0 +1,48 @@
#ifndef _STDINT_H
#define _STDINT_H
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef long int64_t;
typedef unsigned long uint64_t;
typedef long intptr_t;
typedef unsigned long uintptr_t;
typedef long intmax_t;
typedef unsigned long uintmax_t;
#define INT8_MIN (-128)
#define INT8_MAX (127)
#define INT16_MIN (-32768)
#define INT16_MAX (32767)
#define INT32_MIN (-2147483647 - 1)
#define INT32_MAX (2147483647)
#define INT64_MIN (-9223372036854775807L - 1)
#define INT64_MAX (9223372036854775807L)
#define UINT8_MAX (0xFFu)
#define UINT16_MAX (0xFFFFu)
#define UINT32_MAX (0xFFFFFFFFu)
#define UINT64_MAX (0xFFFFFFFFFFFFFFFFUL)
#define INTPTR_MIN INT64_MIN
#define INTPTR_MAX INT64_MAX
#define UINTPTR_MAX UINT64_MAX
#define SIZE_MAX UINT64_MAX
#define INT8_C(v) v
#define INT16_C(v) v
#define INT32_C(v) v
#define INT64_C(v) v ## L
#define UINT8_C(v) v ## u
#define UINT16_C(v) v ## u
#define UINT32_C(v) v ## u
#define UINT64_C(v) v ## UL
#endif
+66
View File
@@ -0,0 +1,66 @@
#ifndef _STDIO_H
#define _STDIO_H
#include <stddef.h>
#include <stdarg.h>
#define EOF (-1)
#define BUFSIZ 1024
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
typedef struct __cervus_FILE FILE;
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
int putchar(int c);
int getchar(void);
int puts(const char *s);
int fputs(const char *s, FILE *stream);
int fputc(int c, FILE *stream);
int fgetc(FILE *stream);
char *fgets(char *s, int n, FILE *stream);
int ungetc(int c, FILE *stream);
int printf(const char *fmt, ...);
int fprintf(FILE *stream, const char *fmt, ...);
int sprintf(char *buf, const char *fmt, ...);
int snprintf(char *buf, size_t sz, const char *fmt, ...);
int vsprintf(char *buf, const char *fmt, va_list ap);
int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap);
int vfprintf(FILE *stream, const char *fmt, va_list ap);
int vprintf(const char *fmt, va_list ap);
int sscanf(const char *str, const char *fmt, ...);
int vsscanf(const char *str, const char *fmt, va_list ap);
int fscanf(FILE *stream, const char *fmt, ...);
int scanf(const char *fmt, ...);
FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fd, const char *mode);
FILE *tmpfile(void);
int fclose(FILE *stream);
size_t fread(void *buf, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *buf, size_t size, size_t nmemb, FILE *stream);
int fflush(FILE *stream);
int fseek(FILE *stream, long off, int whence);
long ftell(FILE *stream);
void rewind(FILE *stream);
int feof(FILE *stream);
int ferror(FILE *stream);
void clearerr(FILE *stream);
int fileno(FILE *stream);
FILE *popen(const char *cmd, const char *type);
int pclose(FILE *stream);
void perror(const char *msg);
int rename(const char *oldp, const char *newp);
int remove(const char *path);
char *tmpnam(char *buf);
#endif
+76
View File
@@ -0,0 +1,76 @@
#ifndef _STDLIB_H
#define _STDLIB_H
#include <stddef.h>
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
#define RAND_MAX 0x7FFFFFFF
void *malloc(size_t n);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *p, size_t n);
void free(void *p);
void exit(int status) __attribute__((noreturn));
void abort(void) __attribute__((noreturn));
int atexit(void (*fn)(void));
int atoi(const char *s);
long atol(const char *s);
long long atoll(const char *s);
long strtol(const char *s, char **end, int base);
unsigned long strtoul(const char *s, char **end, int base);
long long strtoll(const char *s, char **end, int base);
unsigned long long strtoull(const char *s, char **end, int base);
#include <stdint.h>
uint64_t __cervus_strtod_bits(const char *s, char **endptr);
static __inline__ double strtod(const char *s, char **endp)
{
uint64_t b = __cervus_strtod_bits(s, endp);
double d;
__builtin_memcpy(&d, &b, sizeof(d));
return d;
}
static __inline__ float strtof(const char *s, char **endp)
{
return (float)strtod(s, endp);
}
static __inline__ long double strtold(const char *s, char **endp)
{
return (long double)strtod(s, endp);
}
static __inline__ double atof(const char *s)
{
return strtod(s, (char **)0);
}
int abs(int x);
long labs(long x);
long long llabs(long long x);
int rand(void);
void srand(unsigned int seed);
char *getenv(const char *name);
int putenv(char *str);
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);
void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *));
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int (*cmp)(const void *, const void *));
int system(const char *cmd);
int mkstemp(char *template);
char *mkdtemp(char *template);
char *realpath(const char *path, char *resolved);
#endif
+36
View File
@@ -0,0 +1,36 @@
#ifndef _STRING_H
#define _STRING_H
#include <stddef.h>
void *memset(void *dst, int c, size_t n);
void *memcpy(void *dst, const void *src, size_t n);
void *memmove(void *dst, const void *src, size_t n);
int memcmp(const void *a, const void *b, size_t n);
void *memchr(const void *s, int c, size_t n);
void *memmem(const void *haystack, size_t hlen, const void *needle, size_t nlen);
size_t strlen(const char *s);
size_t strnlen(const char *s, size_t max);
int strcmp(const char *a, const char *b);
int strncmp(const char *a, const char *b, size_t n);
int strcasecmp(const char *a, const char *b);
int strncasecmp(const char *a, const char *b, size_t n);
char *strcpy(char *dst, const char *src);
char *strncpy(char *dst, const char *src, size_t n);
char *strcat(char *dst, const char *src);
char *strncat(char *dst, const char *src, size_t n);
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);
char *strstr(const char *hay, const char *needle);
char *strdup(const char *s);
char *strndup(const char *s, size_t n);
size_t strspn(const char *s, const char *accept);
size_t strcspn(const char *s, const char *reject);
char *strpbrk(const char *s, const char *accept);
char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
char *strerror(int errnum);
char *strsignal(int sig);
#endif
+140
View File
@@ -0,0 +1,140 @@
#ifndef _SYS_CERVUS_H
#define _SYS_CERVUS_H
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#define CAP_IOPORT (1ULL << 0)
#define CAP_RAWMEM (1ULL << 1)
#define CAP_KILL_ANY (1ULL << 4)
#define CAP_SET_PRIO (1ULL << 5)
#define CAP_TASK_SPAWN (1ULL << 6)
#define CAP_TASK_INFO (1ULL << 7)
#define CAP_MMAP_EXEC (1ULL << 8)
#define CAP_SETUID (1ULL << 17)
#define CAP_DBG_SERIAL (1ULL << 20)
typedef struct __attribute__((packed)) {
uint8_t boot_flag;
uint8_t type;
uint32_t lba_start;
uint32_t sector_count;
} cervus_mbr_part_t;
typedef struct __attribute__((packed)) {
char disk_name[32];
char part_name[32];
uint32_t part_num;
uint8_t type;
uint8_t bootable;
uint64_t lba_start;
uint64_t sector_count;
uint64_t size_bytes;
} cervus_part_info_t;
typedef struct {
char name[32];
uint64_t sectors;
uint64_t size_bytes;
char model[41];
uint8_t present;
uint8_t _pad[6];
} cervus_disk_info_t;
typedef struct {
uint32_t pid, ppid, uid, gid;
uint64_t capabilities;
char name[32];
uint32_t state;
uint32_t priority;
uint64_t total_runtime_ns;
} cervus_task_info_t;
typedef struct {
uint64_t total_bytes;
uint64_t free_bytes;
uint64_t used_bytes;
uint64_t usable_bytes;
uint64_t page_size;
} cervus_meminfo_t;
typedef struct {
int64_t tv_sec;
int64_t tv_nsec;
} cervus_timespec_t;
typedef struct {
char path[512];
char device[32];
char fstype[16];
uint32_t flags;
} cervus_mount_info_t;
typedef struct {
uint64_t f_bsize;
uint64_t f_blocks;
uint64_t f_bfree;
uint64_t f_bavail;
uint64_t f_files;
uint64_t f_ffree;
uint32_t f_flag;
uint32_t f_namemax;
} cervus_statvfs_t;
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
#define WNOHANG 0x1
#define WEXITSTATUS(s) (((s) >> 8) & 0xFF)
#define WIFEXITED(s) (((s) & 0x7F) == 0)
int cervus_task_info(pid_t pid, cervus_task_info_t *out);
int cervus_task_kill(pid_t pid);
uint64_t cervus_cap_get(void);
int cervus_cap_drop(uint64_t mask);
int cervus_meminfo(cervus_meminfo_t *out);
uint64_t cervus_uptime_ns(void);
int cervus_clock_gettime(int id, cervus_timespec_t *ts);
int cervus_nanosleep(uint64_t ns);
int cervus_shutdown(void);
int cervus_reboot(void);
int cervus_disk_info(int index, cervus_disk_info_t *out);
int cervus_disk_mount(const char *dev, const char *path);
int cervus_disk_umount(const char *path);
int cervus_disk_format(const char *dev, const char *label);
int cervus_disk_mkfs_fat32(const char *dev, const char *label);
int cervus_disk_partition(const char *dev, const cervus_mbr_part_t *specs, uint64_t n);
int cervus_disk_read_raw(const char *dev, uint64_t lba, uint64_t count, void *buf);
int cervus_disk_write_raw(const char *dev, uint64_t lba, uint64_t count, const void *buf);
long cervus_disk_list_parts(cervus_part_info_t *out, int max);
long cervus_disk_bios_install(const char *disk, const void *sys_data, uint32_t sys_size);
long cervus_list_mounts(cervus_mount_info_t *out, int max);
long cervus_statvfs(const char *path, cervus_statvfs_t *out);
uint32_t cervus_ioport_read(uint16_t port, int width);
int cervus_ioport_write(uint16_t port, int width, uint32_t val);
#define PROT_NONE 0x0
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define MAP_PRIVATE 0x02
#define MAP_ANONYMOUS 0x20
#define MAP_FIXED 0x10
#define MAP_FAILED ((void *)-1)
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off);
int munmap(void *addr, size_t len);
pid_t waitpid(pid_t pid, int *status, int options);
pid_t wait(int *status);
ssize_t cervus_dbg_print(const char *buf, size_t n);
#endif
+29
View File
@@ -0,0 +1,29 @@
#ifndef _SYS_IOCTL_H
#define _SYS_IOCTL_H
#include <stdint.h>
#include <sys/syscall.h>
#define TIOCGWINSZ 0x5413
#define TIOCSWINSZ 0x5414
#define TIOCGPGRP 0x540F
#define TIOCSPGRP 0x5410
#define TIOCGPTN 0x80045430
#define TIOCSPTLCK 0x40045431
#define FIOCLEX 0x5451
#define FIONCLEX 0x5450
#define FIONREAD 0x541B
#define FIONBIO 0x5421
#define FIOASYNC 0x5452
struct winsize {
uint16_t ws_row;
uint16_t ws_col;
uint16_t ws_xpixel;
uint16_t ws_ypixel;
};
int ioctl(int fd, unsigned long request, ...);
#endif
+24
View File
@@ -0,0 +1,24 @@
#ifndef _SYS_MMAN_H
#define _SYS_MMAN_H
#include <stddef.h>
#include <sys/types.h>
#define PROT_NONE 0x0
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define PROT_EXEC 0x4
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *)-1)
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off);
int munmap(void *addr, size_t len);
int mprotect(void *addr, size_t len, int prot);
#endif
+50
View File
@@ -0,0 +1,50 @@
#ifndef _SYS_STAT_H
#define _SYS_STAT_H
#include <sys/types.h>
#define S_IFMT 0170000
#define S_IFREG 0100000
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFBLK 0060000
#define S_IFIFO 0010000
#define S_IFLNK 0120000
#define S_IFSOCK 0140000
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
struct stat {
ino_t st_ino;
uint32_t st_type;
mode_t st_mode;
uid_t st_uid;
gid_t st_gid;
off_t st_size;
blkcnt_t st_blocks;
};
int stat(const char *path, struct stat *out);
int fstat(int fd, struct stat *out);
int mkdir(const char *path, mode_t mode);
int chmod(const char *path, mode_t mode);
#endif
+98
View File
@@ -0,0 +1,98 @@
#ifndef _SYS_SYSCALL_H
#define _SYS_SYSCALL_H
#include <stdint.h>
#define SYS_EXIT 0
#define SYS_EXIT_GROUP 1
#define SYS_GETPID 2
#define SYS_GETPPID 3
#define SYS_FORK 4
#define SYS_WAIT 5
#define SYS_YIELD 6
#define SYS_GETUID 7
#define SYS_GETGID 8
#define SYS_SETUID 9
#define SYS_SETGID 10
#define SYS_CAP_GET 11
#define SYS_CAP_DROP 12
#define SYS_TASK_INFO 13
#define SYS_EXECVE 14
#define SYS_READ 20
#define SYS_WRITE 21
#define SYS_OPEN 22
#define SYS_CLOSE 23
#define SYS_SEEK 24
#define SYS_STAT 25
#define SYS_FSTAT 26
#define SYS_IOCTL 27
#define SYS_DUP 28
#define SYS_DUP2 29
#define SYS_PIPE 30
#define SYS_FCNTL 31
#define SYS_READDIR 32
#define SYS_MMAP 40
#define SYS_MUNMAP 41
#define SYS_MPROTECT 42
#define SYS_BRK 43
#define SYS_CLOCK_GET 60
#define SYS_SLEEP_NS 61
#define SYS_UPTIME 62
#define SYS_MEMINFO 63
#define SYS_FUTEX_WAIT 80
#define SYS_FUTEX_WAKE 81
#define SYS_DBG_PRINT 512
#define SYS_TASK_KILL 515
#define SYS_IOPORT_READ 521
#define SYS_IOPORT_WRITE 522
#define SYS_SHUTDOWN 523
#define SYS_REBOOT 524
#define SYS_DISK_MOUNT 530
#define SYS_DISK_UMOUNT 531
#define SYS_DISK_FORMAT 532
#define SYS_DISK_INFO 533
#define SYS_UNLINK 534
#define SYS_RMDIR 535
#define SYS_MKDIR 536
#define SYS_RENAME 537
#define SYS_DISK_READ_RAW 540
#define SYS_DISK_WRITE_RAW 541
#define SYS_DISK_PARTITION 542
#define SYS_DISK_MKFS_FAT32 543
#define SYS_DISK_LIST_PARTS 544
#define SYS_DISK_BIOS_INSTALL 545
#define SYS_LIST_MOUNTS 546
#define SYS_STATVFS 547
static inline int64_t
__syscall6(uint64_t nr, uint64_t a1, uint64_t a2, uint64_t a3,
uint64_t a4, uint64_t a5, uint64_t a6)
{
int64_t ret;
register uint64_t r10 asm("r10") = a4;
register uint64_t r8 asm("r8") = a5;
register uint64_t r9 asm("r9") = a6;
asm volatile ("syscall"
: "=a"(ret)
: "0"(nr), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
: "rcx", "r11", "memory");
return ret;
}
#define syscall0(n) __syscall6((n), 0, 0, 0, 0, 0, 0)
#define syscall1(n,a) __syscall6((n), (uint64_t)(a), 0, 0, 0, 0, 0)
#define syscall2(n,a,b) __syscall6((n), (uint64_t)(a), (uint64_t)(b), 0, 0, 0, 0)
#define syscall3(n,a,b,c) __syscall6((n), (uint64_t)(a), (uint64_t)(b), (uint64_t)(c), 0, 0, 0)
#define syscall4(n,a,b,c,d) __syscall6((n), (uint64_t)(a), (uint64_t)(b), (uint64_t)(c), (uint64_t)(d), 0, 0)
#define syscall5(n,a,b,c,d,e) __syscall6((n), (uint64_t)(a), (uint64_t)(b), (uint64_t)(c), (uint64_t)(d), (uint64_t)(e), 0)
#define syscall6(n,a,b,c,d,e,f) __syscall6((n), (uint64_t)(a), (uint64_t)(b), (uint64_t)(c), (uint64_t)(d), (uint64_t)(e), (uint64_t)(f))
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef _SYS_TIME_H
#define _SYS_TIME_H
#include <sys/types.h>
#include <time.h>
struct timeval {
time_t tv_sec;
long tv_usec;
};
struct timezone {
int tz_minuteswest;
int tz_dsttime;
};
int gettimeofday(struct timeval *tv, struct timezone *tz);
#endif
+18
View File
@@ -0,0 +1,18 @@
#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H
#include <stdint.h>
#include <stddef.h>
typedef int32_t pid_t;
typedef uint32_t uid_t;
typedef uint32_t gid_t;
typedef int64_t off_t;
typedef uint64_t ino_t;
typedef uint32_t mode_t;
typedef uint32_t dev_t;
typedef uint64_t blkcnt_t;
typedef uint64_t blksize_t;
typedef int64_t time_t;
#endif
+67
View File
@@ -0,0 +1,67 @@
#ifndef _SYS_UCONTEXT_H
#define _SYS_UCONTEXT_H
#include <stdint.h>
#include <signal.h>
typedef unsigned long greg_t;
#define NGREG 23
typedef greg_t gregset_t[NGREG];
enum {
REG_R8 = 0,
REG_R9 = 1,
REG_R10 = 2,
REG_R11 = 3,
REG_R12 = 4,
REG_R13 = 5,
REG_R14 = 6,
REG_R15 = 7,
REG_RDI = 8,
REG_RSI = 9,
REG_RBP = 10,
REG_RBX = 11,
REG_RDX = 12,
REG_RAX = 13,
REG_RCX = 14,
REG_RSP = 15,
REG_RIP = 16,
REG_EFL = 17,
REG_CSGSFS = 18,
REG_ERR = 19,
REG_TRAPNO = 20,
REG_OLDMASK = 21,
REG_CR2 = 22,
};
typedef struct {
uint16_t cwd;
uint16_t swd;
uint16_t ftw;
uint16_t fop;
uint64_t rip;
uint64_t rdp;
uint32_t mxcsr;
uint32_t mxcr_mask;
uint32_t st_space[32];
uint32_t xmm_space[64];
uint32_t padding[24];
} fpregset_t;
typedef struct {
gregset_t gregs;
fpregset_t *fpregs;
uint64_t __reserved[8];
} mcontext_t;
typedef struct ucontext {
uint64_t uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
sigset_t uc_sigmask;
} ucontext_t;
#endif
+16
View File
@@ -0,0 +1,16 @@
#ifndef _SYS_UTSNAME_H
#define _SYS_UTSNAME_H
#define _UTSNAME_LENGTH 65
struct utsname {
char sysname[_UTSNAME_LENGTH];
char nodename[_UTSNAME_LENGTH];
char release[_UTSNAME_LENGTH];
char version[_UTSNAME_LENGTH];
char machine[_UTSNAME_LENGTH];
};
int uname(struct utsname *buf);
#endif
+19
View File
@@ -0,0 +1,19 @@
#ifndef _SYS_WAIT_H
#define _SYS_WAIT_H
#include <sys/types.h>
#define WNOHANG 0x1
#define WUNTRACED 0x2
#define WIFEXITED(s) (((s) & 0x7F) == 0)
#define WEXITSTATUS(s) (((s) >> 8) & 0xFF)
#define WIFSIGNALED(s) (((s) & 0x7F) != 0 && ((s) & 0x7F) != 0x7F)
#define WTERMSIG(s) ((s) & 0x7F)
#define WIFSTOPPED(s) (((s) & 0xFF) == 0x7F)
#define WSTOPSIG(s) (((s) >> 8) & 0xFF)
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
#endif
+58
View File
@@ -0,0 +1,58 @@
#ifndef _TERMIOS_H
#define _TERMIOS_H
#include <stdint.h>
typedef unsigned int tcflag_t;
typedef unsigned char cc_t;
typedef unsigned int speed_t;
#define NCCS 32
struct termios {
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
};
#define BRKINT 0x0001
#define ICRNL 0x0002
#define INPCK 0x0004
#define ISTRIP 0x0008
#define IXON 0x0010
#define OPOST 0x0001
#define CS8 0x0030
#define ISIG 0x0001
#define ICANON 0x0002
#define ECHO 0x0008
#define ECHOE 0x0010
#define ECHOK 0x0020
#define ECHONL 0x0040
#define IEXTEN 0x0100
#define VMIN 6
#define VTIME 5
#define VINTR 0
#define VQUIT 1
#define VERASE 2
#define VKILL 3
#define VEOF 4
#define TCSANOW 0
#define TCSADRAIN 1
#define TCSAFLUSH 2
#define TCGETS 0x5401
#define TCSETS 0x5402
#define TCSETSW 0x5403
#define TCSETSF 0x5404
int tcgetattr(int fd, struct termios *t);
int tcsetattr(int fd, int optional_actions, const struct termios *t);
#endif
+47
View File
@@ -0,0 +1,47 @@
#ifndef _TIME_H
#define _TIME_H
#include <stddef.h>
#include <sys/types.h>
typedef long clock_t;
#define CLOCKS_PER_SEC 1000000L
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
struct timespec {
time_t tv_sec;
long tv_nsec;
};
time_t time(time_t *t);
clock_t clock(void);
struct tm *localtime(const time_t *t);
struct tm *gmtime(const time_t *t);
time_t mktime(struct tm *tm);
size_t strftime(char *s, size_t max, const char *fmt, const struct tm *tm);
char *asctime(const struct tm *tm);
char *ctime(const time_t *t);
int nanosleep(const struct timespec *req, struct timespec *rem);
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
int clock_gettime(int clk, struct timespec *tp);
static inline long difftime_l(time_t a, time_t b) { return (long)(a - b); }
#endif
+68
View File
@@ -0,0 +1,68 @@
#ifndef _UNISTD_H
#define _UNISTD_H
#include <stddef.h>
#include <sys/types.h>
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#define F_OK 0
#define X_OK 1
#define W_OK 2
#define R_OK 4
ssize_t read(int fd, void *buf, size_t n);
ssize_t write(int fd, const void *buf, size_t n);
int close(int fd);
off_t lseek(int fd, off_t off, int whence);
int dup(int fd);
int dup2(int oldfd, int newfd);
int pipe(int fds[2]);
int unlink(const char *path);
int rmdir(const char *path);
int access(const char *path, int mode);
int chdir(const char *path);
int fchdir(int fd);
char *getcwd(char *buf, size_t size);
int symlink(const char *target, const char *linkpath);
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
pid_t getpid(void);
pid_t getppid(void);
uid_t getuid(void);
gid_t getgid(void);
int setuid(uid_t uid);
int setgid(gid_t gid);
pid_t fork(void);
int execve(const char *path, char *const argv[], char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
void _exit(int status) __attribute__((noreturn));
unsigned int sleep(unsigned int sec);
int usleep(unsigned int usec);
void *sbrk(intptr_t increment);
int brk(void *addr);
void sched_yield_cervus(void);
int sched_yield(void);
int isatty(int fd);
long pathconf(const char *path, int name);
long fpathconf(int fd, int name);
extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
int getopt(int argc, char *const argv[], const char *optstring);
#endif
@@ -0,0 +1 @@
Hello from sysroot!