diff --git a/libs/libc/include/sys/syscall.h b/libs/libc/include/sys/syscall.h index 1a70c1e6..1af1a0ad 100644 --- a/libs/libc/include/sys/syscall.h +++ b/libs/libc/include/sys/syscall.h @@ -16,7 +16,7 @@ #define SYS_execv 12 #define SYS_fcntl 13 #define SYS_mprotect 14 -#define SYS_clock 15 +#define SYS_clock_gettime 15 #define SYS_mkdir 16 #define SYS_fork 17 #define SYS_waitpid 18 diff --git a/libs/libc/include/sys/types.h b/libs/libc/include/sys/types.h index e8d0135e..9e96a25f 100644 --- a/libs/libc/include/sys/types.h +++ b/libs/libc/include/sys/types.h @@ -34,4 +34,7 @@ typedef int uid_t; /* Type representing a group ID. */ typedef int gid_t; +/* Type representing a system clock. */ +typedef int clockid_t; + #endif \ No newline at end of file diff --git a/libs/libc/include/time.h b/libs/libc/include/time.h index cb66f7b3..24091b27 100644 --- a/libs/libc/include/time.h +++ b/libs/libc/include/time.h @@ -16,19 +16,27 @@ struct tm int tm_wday; int tm_yday; int tm_isdst; - - long tm_gmtoff; - const char* tm_zone; }; -// Captures elapsed time. +// Captures elapsed time with microsecond precision. struct timeval { time_t tv_sec; suseconds_t tv_usec; }; -#define CLOCKS_PER_SEC 1000 // Number of clock_t per second. +// Captures elapsed time with nanosecond precision. +struct timespec +{ + time_t tv_sec; + long tv_nsec; +}; + +#define CLOCKS_PER_SEC 10000000 // Number of clock_t per second. + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +#define CLOCK_PROCTIME 2 #ifdef __cplusplus extern "C" @@ -39,11 +47,36 @@ extern "C" * get the value in seconds. */ clock_t clock(void); - /* FIXME: For now, is an alias for clock(), but in seconds. */ + /* Returns the current UNIX time in seconds, which is also stored in tloc if it is nonnull. */ time_t time(time_t* tloc); - struct tm* localtime(const time_t* timep); // Not implemented. - struct tm* gmtime(const time_t* timep); // Not implemented. + /* Retrieves precise time from a specific system clock. */ + int clock_gettime(clockid_t clock_id, struct timespec* tp); + + /* Converts the UNIX timestamp time to broken-down time in UTC. */ + struct tm* gmtime(const time_t* time); + + /* Converts the UNIX timestamp time to broken-down time in UTC. Thread-safe. */ + struct tm* gmtime_r(const time_t* time, struct tm* result); + + /* Converts the UNIX timestamp time to broken-down time in local time. */ + struct tm* localtime(const time_t* time); + + /* Converts the UNIX timestamp time to broken-down time in local time. Thread-safe. */ + struct tm* localtime_r(const time_t* time, struct tm* result); + + /* Returns a string representation of the broken-down time in the time structure. */ + char* asctime(const struct tm* time); + + /* Fills buf with the string representation of the broken-down time in the time structure. Thread-safe. */ + char* asctime_r(const struct tm* time, char buf[26]); + + /* Returns a string representation of time. */ + char* ctime(const time_t* time); + + /* Fills buf with a string representation of time. Thread-safe. */ + char* ctime_r(const time_t* time, char buf[26]); + size_t strftime(char* str, size_t max, const char* format, const struct tm* time); // Not implemented. char* ctime(const time_t* timep); // Not implemented. diff --git a/libs/libc/src/syscall.cpp b/libs/libc/src/syscall.cpp index c527bba2..f835cbba 100644 --- a/libs/libc/src/syscall.cpp +++ b/libs/libc/src/syscall.cpp @@ -12,7 +12,6 @@ extern "C" long syscall(long number, ...) va_start(ap, number); switch (number) { - case SYS_clock: case SYS_yield: case SYS_fork: result = __luna_syscall0(number); break; case SYS_exit: @@ -26,6 +25,7 @@ extern "C" long syscall(long number, ...) case SYS_fstat: case SYS_stat: case SYS_dup2: + case SYS_clock_gettime: case SYS_setuid: case SYS_setgid: case SYS_pstat: { diff --git a/libs/libc/src/time.cpp b/libs/libc/src/time.cpp index add766b1..9cd7aab3 100644 --- a/libs/libc/src/time.cpp +++ b/libs/libc/src/time.cpp @@ -1,42 +1,164 @@ +#include #include #include +#include #include #include #include +#define SECONDS_PER_DAY 86400 + +static int isleap(int year) +{ + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); +} + +static int make_yday(int year, int month) +{ + static const short int upto[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + int yd; + + yd = upto[month - 1]; + if (month > 2 && isleap(year)) yd++; + return yd; +} + +static int day_of_week(int year, int mon, int day) +{ + static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + year -= mon < 3; + return (year + year / 4 - year / 100 + year / 400 + t[mon - 1] + day) % 7; +} + +static void time_to_struct_tm(time_t time, struct tm* result) +{ + result->tm_isdst = 0; // No DST/timezone support for now. + + int year = 1970; + + while (time > 0) + { + time_t seconds_in_year = (isleap(year) ? 366 : 365) * SECONDS_PER_DAY; + if (seconds_in_year <= time) + { + year++; + time -= seconds_in_year; + continue; + } + break; + } + + int month_days[] = {31, isleap(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + int month = 0; + + while (1) + { + time_t seconds_in_month = month_days[month] * SECONDS_PER_DAY; + if (seconds_in_month <= time) + { + month++; + time -= seconds_in_month; + continue; + } + break; + } + + int day = (int)(time / SECONDS_PER_DAY); + time %= SECONDS_PER_DAY; + + assert(day < month_days[month]); + + int hour = (int)(time / 3600); + time %= 3600; + + int min = (int)(time / 60); + time %= 60; + + result->tm_year = year - 1900; + result->tm_mon = month + 1; + result->tm_yday = make_yday(year, month + 1) + day; // FIXME: Support tm_wday. + result->tm_wday = day_of_week(year, month + 1, day + 1); + result->tm_mday = day + 1; + result->tm_hour = hour; + result->tm_min = min; + result->tm_sec = (int)time; +} + +const char* wday_names[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +const char* month_names[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + extern "C" { clock_t clock() { - return __lc_fast_syscall0(SYS_clock); + struct timespec tp; + clock_gettime(CLOCK_PROCTIME, &tp); + return (tp.tv_sec * CLOCKS_PER_SEC) + (tp.tv_nsec / 1000); } - time_t time( - time_t* tloc) // Very big FIXME: This is not the time of day, this just returns the same as clock() but in - // seconds. This is definitely wrong, but I'm not implementing a whole time system right now. + time_t time(time_t* tloc) { - time_t result = (time_t)(syscall(SYS_clock) / CLOCKS_PER_SEC); - if (tloc) { *tloc = result; } + struct timespec tp; + clock_gettime(CLOCK_REALTIME, &tp); + if (tloc) { *tloc = tp.tv_sec; } + return tp.tv_sec; + } + + int clock_gettime(clockid_t clock_id, struct timespec* tp) + { + return (int)__lc_fast_syscall2(SYS_clock_gettime, clock_id, tp); + } + + struct tm* localtime(const time_t* time) + { + static struct tm result; + return localtime_r(time, &result); + } + + struct tm* gmtime(const time_t* time) + { + static struct tm result; + return gmtime_r(time, &result); + } + + struct tm* localtime_r(const time_t* time, struct tm* result) + { + return gmtime_r(time, result); // FIXME: Implement timezones. + } + + struct tm* gmtime_r(const time_t* time, struct tm* result) + { + time_to_struct_tm(*time, result); return result; } - struct tm* localtime(const time_t*) + char* asctime_r(const struct tm* time, char buf[26]) { - NOT_IMPLEMENTED("localtime"); + snprintf(buf, 26, "%s %s %d %d:%d:%d %d\n", wday_names[time->tm_wday], month_names[time->tm_mon - 1], + time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec, time->tm_year + 1900); + return buf; } - struct tm* gmtime(const time_t*) + char* asctime(const struct tm* time) { - NOT_IMPLEMENTED("gmtime"); + static char buf[26]; + return asctime_r(time, buf); + } + + char* ctime_r(const time_t* time, char buf[26]) + { + struct tm stm; + return asctime_r(localtime_r(time, &stm), buf); + } + + char* ctime(const time_t* time) + { + return asctime(localtime(time)); } size_t strftime(char*, size_t, const char*, const struct tm*) { NOT_IMPLEMENTED("strftime"); } - - char* ctime(const time_t*) - { - NOT_IMPLEMENTED("ctime"); - } } \ No newline at end of file