ref: 63290180d8a5737eff403eddb6a28620ca1e2af5
parent: 0ca62af5f0df449dd940cbd5b7e5723f739df8a0
author: Roberto E. Vargas Caballero <k0ga@shike2.com>
date: Wed Sep 26 02:35:58 EDT 2018
[lib/c] Add first version of ime functions These functions are barely tested and they don't parse TZ variable yet.
--- /dev/null
+++ b/lib/c/_daysyear.c
@@ -1,0 +1,31 @@
+#include <time.h>
+#include "libc.h"
+
+int _daysmon[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+int
+_daysyear(int year)
+{
+ if (year%4 != 0)
+ return 365;
+ if (year%100 == 0 && year%400 != 0)
+ return 365;
+ return 366;
+}
+
+/*
+ * Happy New Year!!!!
+ */
+int
+_newyear(int year)
+{
+ int day;
+
+ year += 1900 - 1;
+ day = 1 + year + year/4;
+ day -= year/100;
+ day += year/400;
+
+ return day % 7;
+}
+
--- /dev/null
+++ b/lib/c/asctime.c
@@ -1,0 +1,12 @@
+#include <time.h>
+#undef asctime
+
+#include <stdio.h> // TODO: remove me!
+char *
+asctime(const struct tm *tm)
+{
+ static char buf[30];
+
+ strftime(buf, sizeof(buf), "%c\n", tm);
+ return buf;
+}
--- /dev/null
+++ b/lib/c/ctime.c
@@ -1,0 +1,8 @@
+#include <time.h>
+#undef ctime
+
+char *
+ctime(const time_t *t)
+{
+ return asctime(localtime(t));
+}
--- /dev/null
+++ b/lib/c/difftime.c
@@ -1,0 +1,8 @@
+#include <time.h>
+#undef difftime
+
+double
+difftime(time_t t1, time_t t2)
+{
+ return (double) (t1 - t2);
+}
--- /dev/null
+++ b/lib/c/gmtime.c
@@ -1,0 +1,35 @@
+#include <time.h>
+#include "libc.h"
+#undef gmtime
+
+struct tm *
+gmtime(const time_t *t)
+{
+ static struct tm tm;
+ time_t sec, min, hour, year, day;
+ int i;
+
+ tm.tm_sec = *t % SECDAY;
+ tm.tm_min = tm.tm_sec / 60;
+ tm.tm_sec %= 60;
+ tm.tm_hour = tm.tm_min / 60;
+ tm.tm_min %= 60;
+ day = *t / SECDAY;
+
+ tm.tm_wday = (day + THU) % 7; /* 1/1/1970 was Thursday */
+
+ for (i = EPOCH; day >= _yeardays(i); ++i)
+ day -= _yeardays(i);
+ tm.tm_year = i - 1900;
+ tm.tm_yday = day;
+
+ _daysmon[FEB] = FEBDAYS(tm.tm_year);
+ for (i = JAN; day > _daysmon[i]; i++)
+ day -= _daysmon[i];
+ tm.tm_mon = i;
+ tm.tm_mday = day + 1;
+
+ tm.tm_isdst = 0;
+
+ return &tm;
+}
--- a/lib/c/libc.h
+++ b/lib/c/libc.h
@@ -1,5 +1,35 @@
+#define SUN 0
+#define MON 1
+#define TUE 2
+#define WED 3
+#define THU 4
+#define FRI 5
+#define SAT 6
+
+#define JAN 0
+#define FEB 1
+#define DEC 11
+
+#define EPOCH 1970
+#define FEBDAYS(y) ((_yeardays(y) == 366) ? 29 : 28)
+#define SECMIN 60
+#define SECHOUR (60 * SECMIN) /* 3600 */
+#define SECDAY (24 * SECHOUR) /* 86400 */
+
#ifdef stdin
extern FILE *_fpopen(const char * restrict fname,
const char * restrict mode,
FILE * restrict fp);
#endif
+
+struct tzone {
+ char *name;
+ int gmtoff;
+ int isdst;
+};
+
+extern struct tzone *_tzone(struct tm *tm);
+extern int _daysyear(int year);
+extern int _newyear(int year);
+
+extern int _daysmon[12];
--- /dev/null
+++ b/lib/c/localtime.c
@@ -1,0 +1,21 @@
+#include <time.h>
+#include "libc.h"
+#undef localtime
+
+struct tm *
+localtime(const time_t *timep)
+{
+ struct tzone *tz;
+ struct tm *tm;
+ time_t t = *timep;
+
+ t += tz->gmtoff * 60;
+ t += tz->isdst * 60;
+ tm = gmtime(&t);
+ tz = _tzone(tm);
+ tm->tm_zone = tz->name;
+ tm->tm_isdst = tz->isdst;
+ tm->tm_gmtoff = tz->gmtoff;
+
+ return tm;
+}
--- /dev/null
+++ b/lib/c/mktime.c
@@ -1,0 +1,116 @@
+#include <limits.h>
+#include <time.h>
+#include "libc.h"
+#undef mktime
+
+static int
+norm(int *val, int *next, int qty)
+{
+ int v = *val, n = *next, d;
+
+ if (v < 0) {
+ d = -v / qty + 1;
+ v += d * qty;
+ if (n > INT_MAX - d)
+ return 0;
+ n += d;
+ }
+ if (v >= qty) {
+ d = v / qty;
+ v -= d * qty;
+ if (n < INT_MIN + d)
+ return 0;
+ n -= d;
+ }
+
+ *val = v;
+ *next = n;
+ return 1;
+}
+
+static int
+normalize(struct tm *tm)
+{
+ int mon, day, year;
+ struct tm aux = *tm;
+
+ if (!norm(&tm->tm_sec, &tm->tm_min, 60) ||
+ !norm(&tm->tm_min, &tm->tm_hour, 60) ||
+ !norm(&tm->tm_hour, &tm->tm_mday, 24) ||
+ !norm(&tm->tm_mon, &tm->tm_year, 12)) {
+ return 0;
+ }
+
+ day = tm->tm_mday;
+ year = EPOCH + tm->tm_year;
+ _daysmon[FEB] = FEBDAYS(year);
+
+ for (mon = tm->tm_mon; day < 1; --mon) {
+ day += _daysmon[mon];
+ if (mon == JAN) {
+ if (year == EPOCH)
+ return -1;
+ year--;
+ _daysmon[FEB] = FEBDAYS(year);
+ mon = DEC+1;
+ }
+ }
+
+ for (; day > _daysmon[mon]; ++mon) {
+ day -= _daysmon[mon];
+ if (mon == DEC) {
+ if (year == _MAXYEAR)
+ return -1;
+ year++;
+ _daysmon[FEB] = FEBDAYS(year);
+ mon = JAN-1;
+ }
+ }
+
+ tm->tm_mon = mon;
+ tm->tm_year = year - EPOCH;
+ tm->tm_mday = day;
+ tm->tm_wday = (_newyear(tm->tm_year) + tm->tm_yday) % 7;
+
+ return 1;
+}
+
+time_t
+mktime(struct tm *tm)
+{
+ int i, year, min, hour, dst;
+ time_t t;
+ struct tm *aux;
+
+ if (!normalize(tm))
+ return -1;
+
+ t = 0;
+ year = tm->tm_year + 1900;
+ for (i = EPOCH; i < year; i++)
+ t += _daysyear(i) * SECDAY;
+
+ for (i = 0; i < tm->tm_mon; i++)
+ t += _daysmon[i] * SECDAY;
+
+ t += tm->tm_sec;
+ t += tm->tm_min * SECMIN;
+ t += tm->tm_hour * SECHOUR;
+ t += (tm->tm_mday-1) * SECDAY;
+
+ aux = localtime(&t);
+
+ hour = aux->tm_gmtoff / SECHOUR;
+ min = aux->tm_gmtoff / SECMIN;
+ dst = 0;
+
+ if (tm->tm_isdst == 0 && aux->tm_isdst == 1)
+ dst = -1;
+ else if (tm->tm_isdst > 0 && aux->tm_isdst == 0)
+ dst = +1;
+
+ t += (hour +dst) * SECHOUR;
+ t -= min * SECMIN;
+
+ return t;
+}
--- /dev/null
+++ b/lib/c/strftime.c
@@ -1,0 +1,246 @@
+#include <time.h>
+#include <string.h>
+#include "libc.h"
+#undef strftime
+
+static char *days[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday",
+};
+
+static char *months[] = {
+ "January", "February", "March", "April",
+ "May", "June", "July", "August",
+ "September", "October", "November", "December"
+};
+
+static char *am_pm[] = {"AM", "PM"};
+
+static size_t
+sval(char *s, size_t siz, char **strs, int abrev, int idx, int max)
+{
+ char *str;
+ size_t len;
+
+ if (idx < 0 && idx >= max)
+ goto wrong;
+
+ str = strs[idx];
+ len = (!abrev) ? strlen(str) : 3;
+ if (len > siz)
+ goto wrong;
+
+ memcpy(s, str, len);
+ return len;
+
+wrong:
+ *s = '?';
+ return 1;
+}
+
+static size_t
+dval(char *s, size_t siz, int prec, int fill, int val)
+{
+ char *t;
+ int n;
+ static char digits[] = "0123456789";
+
+ if (prec > siz || val < 0) {
+ *s = '?';
+ return 1;
+ }
+
+ n = prec;
+ do {
+ s[--n] = digits[val % 10];
+ val /= 10;
+ } while (n > 0 && val > 0);
+
+ while (n > 0)
+ s[--n] = fill;
+
+ return prec;
+}
+
+static size_t
+timezone(char *s, size_t prec, const struct tm * restrict tm)
+{
+ long off = tm->tm_gmtoff;
+
+ if (prec < 5) {
+ *s = '?';
+ return 1;
+ }
+
+ if (off >= 0) {
+ *s++ = '+';
+ } else {
+ *s++ = '-';
+ off = -off;
+ }
+
+ dval(s, 2, 2, '0', off / 3600);
+ dval(s, 2, 2, '0', (off % 3600) / 60);
+
+ return 5;
+}
+
+size_t
+strftime(char * restrict s, size_t siz,
+ const char * restrict fmt,
+ const struct tm * restrict tm)
+{
+ int ch, abrev, val, fill, width;
+ size_t n, inc;
+ char *tfmt;
+
+ for (n = siz-1; (ch = *fmt++) && n > 0; s += inc, n -= inc) {
+ if (ch != '%') {
+ *s = ch;
+ inc = 1;
+ continue;
+ }
+
+ abrev = 0;
+ fill = '0';
+ width = 2;
+
+ switch (*fmt++) {
+ case 'Z':
+ if (!tm->tm_zone)
+ break;
+ inc = sval(s, n, &tm->tm_zone, 0, 0, 1);
+ break;
+ case 'a':
+ abrev = 1;
+ case 'A':
+ inc = sval(s, n, days, abrev, tm->tm_wday, 7);
+ break;
+ case 'h':
+ case 'b':
+ abrev = 1;
+ case 'B':
+ inc = sval(s, n, months, abrev, tm->tm_mon, 12);
+ break;
+ case 'p':
+ inc = sval(s, n, am_pm, 0, tm->tm_hour > 12, 2);
+ break;
+ case 'c':
+ tfmt = "%a %b %e %T %Y";
+ goto recursive;
+ case 'D':
+ tfmt = "%m/%d/%y";
+ goto recursive;
+ case 'F':
+ tfmt = "%Y-%m-%d";
+ goto recursive;
+ case 'R':
+ tfmt = "%H:%M";
+ goto recursive;
+ case 'X':
+ case 'T':
+ tfmt = "%H:%M:%S";
+ goto recursive;
+ case 'r':
+ tfmt = "%I:%M:%S %p";
+ goto recursive;
+ case 'x':
+ tfmt = "%m/%d/%y";
+ goto recursive;
+ recursive:
+ inc = strftime(s, n+1, tfmt, tm) - 1;
+ break;
+ case 'n':
+ val = '\n';
+ goto character;
+ case 't':
+ val = '\t';
+ goto character;
+ case '%':
+ val = '%';
+ character:
+ *s = val;
+ inc = 1;
+ break;
+ case 'e':
+ fill = ' ';
+ val = tm->tm_mday;
+ goto number;
+ case 'd':
+ val = tm->tm_mday;
+ goto number;
+ case 'V':
+ case 'g':
+ case 'G':
+ /* TODO */
+ break;
+ case 'C':
+ val = tm->tm_year / 100;
+ goto number;
+ case 'H':
+ val = tm->tm_hour;
+ goto number;
+ case 'I':
+ val = tm->tm_hour;
+ if (val == 0)
+ val = 12;
+ if (val > 12)
+ val -= 12;
+ goto number;
+ case 'j':
+ width = 3;
+ val = tm->tm_yday+1;
+ goto number;
+ case 'm':
+ val = tm->tm_mon+1;
+ goto number;
+ case 'M':
+ val = tm->tm_min;
+ goto number;
+ case 'S':
+ val = tm->tm_sec;
+ goto number;
+ case 'u':
+ width = 1;
+ val = tm->tm_wday+1;
+ goto number;
+ case 'U':
+ val = tm->tm_yday / 7;
+ if (_newyear(tm->tm_year) == SAT)
+ val++;
+ goto number;
+ case 'W':
+ val = tm->tm_yday / 7;
+ if (_newyear(tm->tm_year) == MON)
+ val++;
+ goto number;
+ case 'w':
+ width = 1;
+ val = tm->tm_wday;
+ goto number;
+ case 'y':
+ val = tm->tm_year%100;
+ goto number;
+ case 'Y':
+ width = 4;
+ val = 1900 + tm->tm_year;
+ number:
+ inc = dval(s, n, width, fill, val);
+ break;
+ case 'z':
+ inc = timezone(s, n, tm);
+ break;
+ case 'E':
+ case 'O':
+ if (*fmt != '\0')
+ fmt += 2;;
+ case '\0':
+ inc = 0;
+ --fmt;
+ break;
+ }
+ }
+ *s = '\0';
+
+ return siz - n;
+}
--- a/lib/c/target/amd64-sysv-netbsd/Makefile
+++ b/lib/c/target/amd64-sysv-netbsd/Makefile
@@ -8,7 +8,9 @@
ABI = sysv
SYSERRTBL = ../posix/netbsd.e
MORECFLAGS = -std=c99 -g -static -nostdinc
-SYSOBJ = getenv.o raise.o signal.o _sigaction.o _sigaction2.o _setcontext.o
+SYSOBJ = _tzone.o getenv.o raise.o signal.o \
+ _sigaction.o _sigaction2.o _setcontext.o \
+ time.o
include syscall.mk
include ../amd64-sysv/objlst.mk
--- /dev/null
+++ b/lib/c/target/amd64-sysv-netbsd/_gettimeofday.s
@@ -1,0 +1,5 @@
+.global _gettimeofday
+_gettimeofday:
+ movq $418,%rax
+ syscall
+ ret
--- a/lib/c/target/amd64-sysv-netbsd/syscall.lst
+++ b/lib/c/target/amd64-sysv-netbsd/syscall.lst
@@ -8,3 +8,4 @@
20 _getpid
37 _kill
199 _lseek
+418 _gettimeofday
--- a/lib/c/target/amd64-sysv-netbsd/syscall.mk
+++ b/lib/c/target/amd64-sysv-netbsd/syscall.mk
@@ -1,1 +1,1 @@
-SYSCALL = _Exit.o _read.o _write.o _open.o _close.o _brk.o _getpid.o _kill.o _lseek.o
+SYSCALL = _Exit.o _read.o _write.o _open.o _close.o _brk.o _getpid.o _kill.o _lseek.o _gettimeofday.o
--- /dev/null
+++ b/lib/c/target/posix/_tzone.c
@@ -1,0 +1,27 @@
+#include <stdlib.h>
+#include <time.h>
+#include "../../libc.h"
+
+struct tzone *
+_tzone(struct tm *tm)
+{
+ static struct tzone tz;
+ static int first = 1;
+
+ if (!first)
+ return &tz;
+
+ tz.name = getenv("TZ");
+ if (!tz.name || *tz.name == '\0') {
+ tz.name = NULL;
+ tz.gmtoff = 0;
+ tz.isdst = 0;
+ } else {
+ /* TODO: parse TZ string */
+ tz.gmtoff = 0;
+ tz.isdst = 0;
+ }
+ first = 0;
+
+ return &tz;
+}
--- a/lib/c/target/posix/objlst.mk
+++ b/lib/c/target/posix/objlst.mk
@@ -1,9 +1,15 @@
-raise.o: ../posix/raise.c ../../syscall.h
+raise.o: ../posix/raise.c
$(CC) $(SCC_CFLAGS) ../posix/raise.c -c
-signal.o: ../posix/signal.c ../../syscall.h
+signal.o: ../posix/signal.c
$(CC) $(SCC_CFLAGS) ../posix/signal.c -c
getenv.o: ../posix/getenv.c
$(CC) $(SCC_CFLAGS) ../posix/getenv.c -c
+
+time.o: ../posix/time.c
+ $(CC) $(SCC_CFLAGS) ../posix/time.c -c
+
+_tzone.o: ../posix/_tzone.c ../../libc.h
+ $(CC) $(SCC_CFLAGS) ../posix/_tzone.c -c
--- a/lib/c/target/posix/signal.c
+++ b/lib/c/target/posix/signal.c
@@ -1,9 +1,6 @@
#include <stddef.h>
#include <signal.h>
#include <sys.h>
-
-#include "../../syscall.h"
-
#undef signal
void
--- /dev/null
+++ b/lib/c/target/posix/time.c
@@ -1,0 +1,21 @@
+#include <time.h>
+
+struct timeval {
+ time_t tv_sec;
+ int tv_usec; /* TODO use a arch type */
+};
+
+int
+_gettimeofday(struct timeval * restrict tp, void * restrict tzp);
+
+time_t
+time(time_t *t)
+{
+ struct timeval tv;
+
+ if (_gettimeofday(&tv, NULL) == -1)
+ return -1;
+ if (t)
+ *t =tv.tv_sec;
+ return tv.tv_sec;
+}
--- a/lib/c/target/script/objlst.mk
+++ b/lib/c/target/script/objlst.mk
@@ -20,6 +20,8 @@
isgraph.o islower.o isprint.o ispunct.o isspace.o isupper.o \
isxdigit.o toupper.o tolower.o ctype.o setlocale.o \
localeconv.o atoi.o atol.o atoll.o atexit.o abort.o exit.o \
+ mktime.o localtime.o gmtime.o difftime.o \
+ _daysyear.o ctime.o asctime.o strftime.o \
errno.o _sys_errlist.o strnlen.o
#rules
@@ -41,6 +43,9 @@
__putc.o: ../../__putc.c
$(CC) $(SCC_CFLAGS) ../../__putc.c -c
+_daysyear.o: ../../_daysyear.c
+ $(CC) $(SCC_CFLAGS) ../../_daysyear.c -c
+
_flsbuf.o: ../../_flsbuf.c
$(CC) $(SCC_CFLAGS) ../../_flsbuf.c -c
@@ -53,6 +58,9 @@
abs.o: ../../abs.c
$(CC) $(SCC_CFLAGS) ../../abs.c -c
+asctime.o: ../../asctime.c
+ $(CC) $(SCC_CFLAGS) ../../asctime.c -c
+
atexit.o: ../../atexit.c
$(CC) $(SCC_CFLAGS) ../../atexit.c -c
@@ -74,9 +82,15 @@
clearerr.o: ../../clearerr.c
$(CC) $(SCC_CFLAGS) ../../clearerr.c -c
+ctime.o: ../../ctime.c
+ $(CC) $(SCC_CFLAGS) ../../ctime.c -c
+
ctype.o: ../../ctype.c
$(CC) $(SCC_CFLAGS) ../../ctype.c -c
+difftime.o: ../../difftime.c
+ $(CC) $(SCC_CFLAGS) ../../difftime.c -c
+
errno.o: ../../errno.c
$(CC) $(SCC_CFLAGS) ../../errno.c -c
@@ -131,6 +145,9 @@
gets.o: ../../gets.c
$(CC) $(SCC_CFLAGS) ../../gets.c -c
+gmtime.o: ../../gmtime.c
+ $(CC) $(SCC_CFLAGS) ../../gmtime.c -c
+
isalnum.o: ../../isalnum.c
$(CC) $(SCC_CFLAGS) ../../isalnum.c -c
@@ -179,6 +196,9 @@
localeconv.o: ../../localeconv.c
$(CC) $(SCC_CFLAGS) ../../localeconv.c -c
+localtime.o: ../../localtime.c
+ $(CC) $(SCC_CFLAGS) ../../localtime.c -c
+
malloc.o: ../../malloc.c
$(CC) $(SCC_CFLAGS) ../../malloc.c -c
@@ -197,6 +217,9 @@
memset.o: ../../memset.c
$(CC) $(SCC_CFLAGS) ../../memset.c -c
+mktime.o: ../../mktime.c
+ $(CC) $(SCC_CFLAGS) ../../mktime.c -c
+
perror.o: ../../perror.c
$(CC) $(SCC_CFLAGS) ../../perror.c -c
@@ -262,6 +285,9 @@
strerror.o: ../../strerror.c
$(CC) $(SCC_CFLAGS) ../../strerror.c -c
+
+strftime.o: ../../strftime.c
+ $(CC) $(SCC_CFLAGS) ../../strftime.c -c
strlen.o: ../../strlen.c
$(CC) $(SCC_CFLAGS) ../../strlen.c -c
--- a/lib/crt/amd64-sysv-netbsd/crt.s
+++ b/lib/crt/amd64-sysv-netbsd/crt.s
@@ -9,6 +9,7 @@
.long 200000000
.bss
+ .globl _environ
_environ:
.quad 0
@@ -18,8 +19,12 @@
_start:
movq %rsp,%rbp
andq $-16,%rsp
- movq 16(%rbp),%rbx
- movq %rbx,_environ
+
+ movq (%rbp),%rdi # rdi = argc
+ leaq 8(%rbp),%rsi # rsi = argv
+ leaq 16(%rbp,%rdi,8),%rdx # rdx = envp = argv +8*argc + 8
+ movq %rdx,_environ
+
call main
movl %eax,%edi
jmp exit
--- a/root/include/scc/bits/amd64-sysv/arch/time.h
+++ b/root/include/scc/bits/amd64-sysv/arch/time.h
@@ -3,4 +3,6 @@
#define _SIZET
#endif
+#define _MAXYEAR 9999
+
typedef long int time_t;
--- a/root/include/scc/bits/i386-sysv/arch/time.h
+++ b/root/include/scc/bits/i386-sysv/arch/time.h
@@ -3,4 +3,6 @@
#define _SIZET
#endif
+#define _MAXYEAR 2037
+
typedef long int time_t;
--- a/root/include/scc/bits/z80-dos/arch/time.h
+++ b/root/include/scc/bits/z80-dos/arch/time.h
@@ -3,4 +3,6 @@
#define _SIZET
#endif
+#define _MAXYEAR 2037
+
typedef long time_t;
--- a/root/include/scc/time.h
+++ b/root/include/scc/time.h
@@ -21,6 +21,11 @@
int tm_wday;
int tm_yday;
int tm_isdst;
+
+ /* fields used internally */
+
+ char *tm_zone;
+ long tm_gmtoff;
};
extern clock_t clock(void);
--- a/tests/libc/execute/libc-tests.lst
+++ b/tests/libc/execute/libc-tests.lst
@@ -34,3 +34,4 @@
0034-errno
0035-setlocale
0036-localeconv
+0037-time