push
This commit is contained in:
+179
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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; }
|
||||
@@ -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; }
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <math.h>
|
||||
|
||||
int abs(int n) {
|
||||
return n >= 0 ? n : -n;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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!
|
||||
Reference in New Issue
Block a user