scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | Submodules | README | LICENSE

commit 1ae4f9227899be75c7902cd82a5dc50c7d73a84a
parent 912d4bdc80191728ba6dfb225acd71951acde04f
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date:   Wed, 26 Sep 2018 06:35:58 +0100

[lib/c] Add first version of ime functions

These functions are barely tested and they don't parse TZ variable
yet.

Diffstat:
Alib/c/_daysyear.c | 31+++++++++++++++++++++++++++++++
Alib/c/asctime.c | 12++++++++++++
Alib/c/ctime.c | 8++++++++
Alib/c/difftime.c | 8++++++++
Alib/c/gmtime.c | 35+++++++++++++++++++++++++++++++++++
Mlib/c/libc.h | 30++++++++++++++++++++++++++++++
Alib/c/localtime.c | 21+++++++++++++++++++++
Alib/c/mktime.c | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/c/strftime.c | 246+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/c/target/amd64-sysv-netbsd/Makefile | 4+++-
Alib/c/target/amd64-sysv-netbsd/_gettimeofday.s | 5+++++
Mlib/c/target/amd64-sysv-netbsd/syscall.lst | 1+
Mlib/c/target/amd64-sysv-netbsd/syscall.mk | 2+-
Alib/c/target/posix/_tzone.c | 27+++++++++++++++++++++++++++
Mlib/c/target/posix/objlst.mk | 10++++++++--
Mlib/c/target/posix/signal.c | 3---
Alib/c/target/posix/time.c | 21+++++++++++++++++++++
Mlib/c/target/script/objlst.mk | 26++++++++++++++++++++++++++
Mlib/crt/amd64-sysv-netbsd/crt.s | 9+++++++--
Mroot/include/scc/bits/amd64-sysv/arch/time.h | 2++
Mroot/include/scc/bits/i386-sysv/arch/time.h | 2++
Mroot/include/scc/bits/z80-dos/arch/time.h | 2++
Mroot/include/scc/time.h | 5+++++
Mtests/libc/execute/libc-tests.lst | 1+
24 files changed, 618 insertions(+), 9 deletions(-)

diff --git a/lib/c/_daysyear.c b/lib/c/_daysyear.c @@ -0,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; +} + diff --git a/lib/c/asctime.c b/lib/c/asctime.c @@ -0,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; +} diff --git a/lib/c/ctime.c b/lib/c/ctime.c @@ -0,0 +1,8 @@ +#include <time.h> +#undef ctime + +char * +ctime(const time_t *t) +{ + return asctime(localtime(t)); +} diff --git a/lib/c/difftime.c b/lib/c/difftime.c @@ -0,0 +1,8 @@ +#include <time.h> +#undef difftime + +double +difftime(time_t t1, time_t t2) +{ + return (double) (t1 - t2); +} diff --git a/lib/c/gmtime.c b/lib/c/gmtime.c @@ -0,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; +} diff --git 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]; diff --git a/lib/c/localtime.c b/lib/c/localtime.c @@ -0,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; +} diff --git a/lib/c/mktime.c b/lib/c/mktime.c @@ -0,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; +} diff --git a/lib/c/strftime.c b/lib/c/strftime.c @@ -0,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; +} diff --git a/lib/c/target/amd64-sysv-netbsd/Makefile b/lib/c/target/amd64-sysv-netbsd/Makefile @@ -8,7 +8,9 @@ ARCH = amd64 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 diff --git a/lib/c/target/amd64-sysv-netbsd/_gettimeofday.s b/lib/c/target/amd64-sysv-netbsd/_gettimeofday.s @@ -0,0 +1,5 @@ +.global _gettimeofday +_gettimeofday: + movq $418,%rax + syscall + ret diff --git 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 diff --git a/lib/c/target/amd64-sysv-netbsd/syscall.mk b/lib/c/target/amd64-sysv-netbsd/syscall.mk @@ -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 diff --git a/lib/c/target/posix/_tzone.c b/lib/c/target/posix/_tzone.c @@ -0,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; +} diff --git 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 diff --git 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 diff --git a/lib/c/target/posix/time.c b/lib/c/target/posix/time.c @@ -0,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; +} diff --git a/lib/c/target/script/objlst.mk b/lib/c/target/script/objlst.mk @@ -20,6 +20,8 @@ LIBOBJ = bsearch.o qsort.o \ 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 @@ __llabs.o: ../../__llabs.c __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 @@ abort.o: ../../abort.c 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 @@ calloc.o: ../../calloc.c 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 @@ getchar.o: ../../getchar.c 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 @@ llabs.o: ../../llabs.c 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 @@ memmove.o: ../../memmove.c 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 @@ -263,6 +286,9 @@ strcspn.o: ../../strcspn.c 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 diff --git 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 @@ _environ: _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 diff --git a/root/include/scc/bits/amd64-sysv/arch/time.h b/root/include/scc/bits/amd64-sysv/arch/time.h @@ -3,4 +3,6 @@ typedef unsigned long size_t; #define _SIZET #endif +#define _MAXYEAR 9999 + typedef long int time_t; diff --git a/root/include/scc/bits/i386-sysv/arch/time.h b/root/include/scc/bits/i386-sysv/arch/time.h @@ -3,4 +3,6 @@ typedef unsigned long size_t; #define _SIZET #endif +#define _MAXYEAR 2037 + typedef long int time_t; diff --git a/root/include/scc/bits/z80-dos/arch/time.h b/root/include/scc/bits/z80-dos/arch/time.h @@ -3,4 +3,6 @@ typedef unsigned size_t; #define _SIZET #endif +#define _MAXYEAR 2037 + typedef long time_t; diff --git a/root/include/scc/time.h b/root/include/scc/time.h @@ -21,6 +21,11 @@ struct tm { int tm_wday; int tm_yday; int tm_isdst; + + /* fields used internally */ + + char *tm_zone; + long tm_gmtoff; }; extern clock_t clock(void); diff --git 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