libc

/* $Id$ */
/* Copyright (c) 2005-2019 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS System libc */
/* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "sys/resource.h"
#include "sys/time.h"
#include "ctype.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "errno.h"
#include "syscalls.h"
#include "time.h"
#ifndef min
# define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/* private */
/* variables */
static char const * _months[] =
{
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
static char const * _months_long[] =
{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
};
static char const * _days[] =
{
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
};
static char const * _days_long[] =
{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};
/* prototypes */
static time_t _time_seconds_per_month(int month, int year);
static time_t _time_seconds_per_year(int year);
/* public */
/* variables */
int daylight = 0;
long timezone = 0;
char * tzname[2] = { "UTC", "UTC" };
int getdate_err = 0;
/* functions */
/* clock */
clock_t clock(void)
{
#ifdef SYS_getrusage
struct rusage ru;
if(getrusage(RUSAGE_SELF, &ru) != 0)
return (clock_t)-1;
return ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) * CLOCKS_PER_SEC)
+ ((ru.ru_utime.tv_usec + ru.ru_stime.tv_usec)
* (CLOCKS_PER_SEC / 1000000));
#else
errno = ENOSYS;
return (clock_t)-1;
#endif
}
/* clock_getres */
#ifndef SYS_clock_getres
int clock_getres(clockid_t clock_id, struct timespec * tp)
{
errno = ENOSYS;
return -1;
}
#endif
/* clock_gettime */
#ifndef SYS_clock_gettime
int clock_gettime(clockid_t clock_id, struct timespec * tp)
{
errno = ENOSYS;
return -1;
}
#endif
/* clock_settime */
#ifndef SYS_clock_settime
int clock_settime(clockid_t clock_id, struct timespec * tp)
{
errno = ENOSYS;
return -1;
}
#endif
/* difftime */
double difftime(time_t time1, time_t time0)
{
return (double)time1 - (double)time0;
}
/* getdate */
struct tm * getdate(char const * string)
{
struct tm * ret = NULL;
static struct tm tm;
char const * datemsk;
FILE * fp;
char buf[80];
size_t len;
char * p;
if((datemsk = getenv("DATEMSK")) == NULL)
{
getdate_err = 1;
return NULL;
}
if((fp = fopen(datemsk, "r")) == NULL)
{
getdate_err = 2;
return NULL;
}
while(fgets(buf, sizeof(buf), fp) != NULL)
{
if((len = strlen(buf)) > 0)
buf[len - 1] = '\0';
memset(&ret, 0, sizeof(ret));
if((p = strptime(string, buf, &tm)) != NULL && *p == '\0')
{
ret = &tm;
break;
}
}
if(ret == NULL)
{
if(ferror(fp))
getdate_err = 5;
else
getdate_err = 7;
}
fclose(fp);
return ret;
}
/* gmtime */
struct tm * gmtime(time_t const * t)
{
static struct tm ret;
return gmtime_r(t, &ret);
}
/* gmtime_r */
struct tm * gmtime_r(time_t const * t, struct tm * ret)
{
time_t u;
time_t v;
int i;
int j;
if(t == NULL || ret == NULL)
{
errno = EINVAL;
return NULL;
}
u = *t;
ret->tm_year = 0;
for(i = 1970;; i++)
{
v = _time_seconds_per_year(i);
if(u < v)
break;
u -= v;
}
ret->tm_year = i - 1900;
for(j = 0; j < 12; j++)
{
v = _time_seconds_per_month(j + 1, i);
if(u < v)
break;
u -= v;
}
ret->tm_mon = j;
ret->tm_sec = u % 60;
u = (u - ret->tm_sec) / 60;
ret->tm_min = u % 60;
u = (u - ret->tm_min) / 60;
ret->tm_hour = u % 24;
u = (u - ret->tm_hour) / 24;
ret->tm_mday = u + 1;
/* FIXME implement this correctly */
ret->tm_wday = (u + 4) % 7;
u = (u - ret->tm_wday) / 7;
/* FIXME implement the rest */
ret->tm_yday = 0;
ret->tm_isdst = 0;
/* FIXME implement this?
ret->tm_gmtoff = 0; */
return ret;
}
/* localtime */
struct tm * localtime(time_t const * t)
{
static struct tm ret;
tzset();
if(localtime_r(t, &ret) == NULL)
return NULL;
return &ret;
}
/* localtime_r */
struct tm * localtime_r(time_t const * t, struct tm * ret)
{
if(gmtime_r(t, ret) == NULL)
return NULL;
/* FIXME apply timezone offset */
return ret;
}
/* mktime */
time_t mktime(struct tm * timep)
{
time_t ret;
const time_t min = 60;
const time_t hour = min * 60;
const time_t day = hour * 24;
int i;
int j;
if(timep->tm_year < 70)
return 0;
ret = timep->tm_sec;
/* FIXME not accurate */
ret += timep->tm_min * min;
ret += timep->tm_hour * hour;
ret += (timep->tm_mday - 1) * day;
for(i = 1970; i < timep->tm_year + 1900; i++)
ret += _time_seconds_per_year(i);
for(j = 0; j < timep->tm_mon; j++)
ret += _time_seconds_per_month(j + 1, i);
return ret;
}
/* nanosleep */
#ifndef SYS_nanosleep
# warning Unsupported platform: nanosleep() is missing
int nanosleep(struct timespec * requested, struct timespec * remaining)
{
errno = ENOSYS;
return -1;
}
#endif
/* strftime */
static char * _strftime_print(char * s, size_t * maxsize, char const * str,
size_t size);
static char * _strftime_print_int(char * s, size_t * maxsize,
char const * format, int i);
size_t strftime(char * s, size_t maxsize, char const * format, struct tm * t)
{
char * q = s;
size_t pos;
char const * p;
for(pos = 0; format[pos] != '\0'; pos++)
{
if(format[pos] != '%')
{
q = _strftime_print(q, &maxsize, &format[pos], 1);
continue;
}
switch(format[++pos])
{
case '%':
q = _strftime_print(q, &maxsize, &format[pos],
1);
break;
case 'A':
if(t->tm_wday >= (int)(sizeof(_days_long)
/ sizeof(*_days_long)))
break;
p = _days_long[t->tm_wday];
q = _strftime_print(q, &maxsize, p, strlen(p));
break;
case 'a':
if(t->tm_wday >= (int)(sizeof(_days)
/ sizeof(*_days)))
break;
p = _days[t->tm_wday];
q = _strftime_print(q, &maxsize, p, strlen(p));
break;
case 'B':
if(t->tm_mon >= (int)(sizeof(_months_long)
/ sizeof(*_months_long)))
break;
p = _months_long[t->tm_mon];
q = _strftime_print(q, &maxsize, p, strlen(p));
break;
case 'b':
case 'h':
if(t->tm_mon >= (int)(sizeof(_months)
/ sizeof(*_months)))
break;
p = _months[t->tm_mon];
q = _strftime_print(q, &maxsize, p, strlen(p));
break;
case 'C':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_year / 100);
break;
case 'D':
case 'x':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_mon);
q = _strftime_print(q, &maxsize, "/", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_mday);
q = _strftime_print(q, &maxsize, "/", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_year);
break;
case 'd':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_mday);
break;
case 'e':
q = _strftime_print_int(q, &maxsize, "% 2d",
t->tm_mday);
break;
case 'F':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_year);
q = _strftime_print(q, &maxsize, "-", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_mon);
q = _strftime_print(q, &maxsize, "-", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_mday);
break;
case 'H':
q = _strftime_print_int(q, &maxsize, "%02d",
t->tm_hour);
break;
case 'I':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_hour % 12);
break;
case 'j':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_yday);
break;
case 'k':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_hour);
if(q[0] == '0')
q[0] = ' ';
break;
case 'l':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_hour % 12);
if(q[0] == '0')
q[0] = ' ';
break;
case 'M':
q = _strftime_print_int(q, &maxsize, "%02d",
t->tm_min);
break;
case 'm':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_mon);
break;
case 'n':
q = _strftime_print(q, &maxsize, "\n", 1);
break;
case 'p':
q = _strftime_print(q, &maxsize, (t->tm_hour
< 12) ? "AM" : "PM", 2);
break;
case 'R':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_hour);
q = _strftime_print(q, &maxsize, ":", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_min);
break;
case 'r':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_hour % 12);
q = _strftime_print(q, &maxsize, ":", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_min);
q = _strftime_print(q, &maxsize, ":", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_sec);
q = _strftime_print(q, &maxsize, " ", 1);
q = _strftime_print(q, &maxsize,
(t->tm_hour < 12) ? "AM" : "PM",
2);
break;
case 'S':
q = _strftime_print_int(q, &maxsize, "%02d",
t->tm_sec);
break;
case 's':
q = _strftime_print_int(q, &maxsize, NULL,
time(NULL));
break;
case 'T':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_hour);
q = _strftime_print(q, &maxsize, ":", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_min);
q = _strftime_print(q, &maxsize, ":", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_sec);
break;
case 't':
q = _strftime_print(q, &maxsize, "\t", 1);
break;
case 'X':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_hour);
q = _strftime_print(q, &maxsize, ":", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_min);
q = _strftime_print(q, &maxsize, ":", 1);
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_sec);
break;
case 'u':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_wday);
break;
case 'w':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_wday);
break;
case 'Y':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_year + 1900);
break;
case 'y':
q = _strftime_print_int(q, &maxsize, NULL,
t->tm_year);
break;
case 'Z':
break;
/* FIXME implement the rest */
default:
errno = EINVAL;
return 0;
}
}
if(q == NULL || maxsize == 0)
return 0;
*q = '\0';
return strlen(s);
}
static char * _strftime_print(char * s, size_t * maxsize, char const * str,
size_t size)
{
if(s == NULL)
return NULL;
if(size == 0)
size = strlen(str);
size = min(*maxsize, size);
strncpy(s, str, size);
*maxsize -= size;
return s + size;
}
static char * _strftime_print_int(char * s, size_t * maxsize,
char const * format, int i)
{
char buf[16];
int len;
format = (format != NULL) ? format : "%d";
if((len = snprintf(buf, sizeof(buf), format, i)) <= 0)
return s;
return _strftime_print(s, maxsize, buf, len);
}
/* strptime */
char * strptime(char const * buf, char const * format, struct tm * tm)
{
int e = errno;
char * p;
char const * q;
memset(tm, 0, sizeof(*tm));
for(p = (char *)buf, q = format; *p != '\0' && *q != '\0'; q++)
{
if(isspace(*q))
{
for(; isspace(*p); p++);
continue;
}
if(*q != '%')
{
if(*p != *q)
break;
p++;
continue;
}
switch(*(++q))
{
case 'd':
/* day of the month */
errno = 0;
tm->tm_mday = strtol(p, &p, 10);
if(errno != 0 || tm->tm_mday < 1
|| tm->tm_mday > 31)
return NULL;
break;
case 'H':
/* hour (24-hour clock) */
errno = 0;
tm->tm_hour = strtol(p, &p, 10);
if(errno != 0 || tm->tm_hour < 0
|| tm->tm_hour > 23)
return NULL;
break;
case 'M':
/* minute */
errno = 0;
tm->tm_min = strtol(p, &p, 10);
if(errno != 0 || tm->tm_min < 0
|| tm->tm_min > 60)
return NULL;
break;
case 'm':
/* month number */
errno = 0;
tm->tm_mon = strtol(p, &p, 10);
if(errno != 0 || tm->tm_mon < 1
|| tm->tm_mon > 12)
return NULL;
tm->tm_mon--;
break;
case 'S':
/* seconds */
errno = 0;
tm->tm_sec = strtol(p, &p, 10);
if(errno != 0 || tm->tm_sec < 0
|| tm->tm_sec > 60)
return NULL;
break;
case 'Y':
/* year, including the century */
errno = 0;
tm->tm_year = strtol(p, &p, 10);
if(errno != 0)
return NULL;
tm->tm_year -= 1970;
break;
default:
/* FIXME implement more */
errno = ENOSYS;
return NULL;
}
}
if(*q != '\0')
return NULL;
errno = e;
return p;
}
/* time */
time_t time(time_t * t)
{
struct timeval tv;
if(gettimeofday(&tv, NULL) != 0)
return -1;
if(t != NULL)
*t = tv.tv_sec;
return tv.tv_sec;
}
/* tzset */
void tzset(void)
{
/* FIXME implement, set daylight, timezone and tzname */
}
/* private */
/* functions */
/* time_seconds_per_month */
static time_t _time_seconds_per_month(int month, int year)
{
const time_t day = 60 * 60 * 24;
const time_t month28 = day * 28;
const time_t month29 = day * 29;
const time_t month30 = day * 30;
const time_t month31 = day * 31;
switch(month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
return month31;
case 2:
if((year & 0x3) == 0 && (year % 400) != 0)
return month29;
return month28;
case 4:
case 6:
case 9:
case 11:
return month30;
default:
return 0;
}
}
/* time_seconds_per_year */
static time_t _time_seconds_per_year(int year)
{
const time_t day = 60 * 60 * 24;
const time_t year365 = day * 365;
const time_t year366 = day * 366;
if((year & 0x3) == 0 && (year % 400) != 0)
return year366;
return year365;
}