shithub: scc

Download patch

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