AmiTimeKeeper/amiga.c

744 lines
14 KiB
C

/*
** This file is in the public domain.
*/
#include "amiga.h"
#include "private.h"
#include <dos/var.h>
#include <devices/timer.h>
#include <libraries/locale.h>
#include <utility/date.h>
/* Amiga convert functions */
void Amiga2Tm(ULONG seconds, struct tm *tm);
ULONG Tm2Amiga(struct tm *tm);
ULONG Unix2Amiga(time_t t);
time_t Amiga2Unix(ULONG t);
void Tm2DateStamp(struct tm *tm, struct DateStamp *date);
void DateStamp2Tm(struct DateStamp *date, struct tm *tm);
void Unix2DateStamp(time_t *time, struct DateStamp *date);
void DateStamp2Unix(struct DateStamp *date, time_t *time);
void Tm2Date(struct tm *tm, struct ClockData *date);
void Date2Tm(struct ClockData *date, struct tm *tm);
void Date2Unix(struct ClockData *date, time_t *time);
void Unix2Date(time_t *time, struct ClockData *date);
static struct timerequest *TimeRequest;
static struct MsgPort *TimerPort;
const struct lc_time_T C_time_locale = {
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"},
{"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"},
{"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"},
{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"},
/* X_fmt */
"%H:%M:%S",
/*
** x_fmt
** C99 requires this format.
** Using just numbers (as here) makes Quakers happier;
** it's also compatible with SVR4.
*/
"%m/%d/%y",
/*
** c_fmt
** C99 requires this format.
** Previously this code used "%D %X", but we now conform to C99.
** Note that
** "%a %b %d %H:%M:%S %Y"
** is used by Solaris 2.3.
*/
"%a %b %e %T %Y",
/* t_fmt_ampm */
"TODO",
/* am */
"AM",
/* pm */
"PM",
/* am_pm */
{ "TODOAM", "TODOPM"},
/* date_fmt */
"%a %b %e %H:%M:%S %Z %Y"};
static int
CreateTimer()
{
int error;
TimerPort = CreateMsgPort();
if (TimerPort == NULL)
{
return 1;
}
TimeRequest = (struct timerequest *)CreateIORequest(
TimerPort,
sizeof(struct timerequest));
if (TimeRequest == NULL)
{
DeleteMsgPort(TimerPort);
return 2;
}
error = OpenDevice(
(STRPTR)TIMERNAME,
UNIT_MICROHZ,
(struct IORequest *)TimeRequest,
0);
if (error != 0)
{
DeleteIORequest((struct IORequest *)TimeRequest);
DeleteMsgPort(TimerPort);
return 3;
}
TimerBase = (struct Device *)TimeRequest->tr_node.io_Device;
return 0;
}
static void
DeleteTimer()
{
TimerBase = NULL;
if (TimeRequest != NULL)
{
if (!CheckIO((struct IORequest *)TimeRequest))
{
WaitIO((struct IORequest *)TimeRequest);
}
CloseDevice((struct IORequest *)TimeRequest);
DeleteIORequest((struct IORequest *)TimeRequest);
TimeRequest = NULL;
}
if (TimerPort != NULL)
{
DeleteMsgPort(TimerPort);
TimerPort = NULL;
}
}
int gmtoffset(time_t locale_time)
{
struct tm tm;
time_t time = locale_time;
localtime_r(&time, &tm);
return tm.tm_gmtoff;
}
int gettimeofday(struct timeval *tv, void *tz)
{
long zone;
if (tv == NULL)
{
return -1;
}
if (TimerSemaphore != NULL)
{
ObtainSemaphore(TimerSemaphore);
}
if (CreateTimer() != 0)
{
if (TimerSemaphore != NULL)
{
ReleaseSemaphore(TimerSemaphore);
}
return -1;
}
zone = gmtoffset(tv->tv_secs);
TimeRequest->tr_node.io_Command = TR_GETSYSTIME;
DoIO((struct IORequest *)TimeRequest);
tv->tv_secs = (long)TimeRequest->tr_time.tv_secs + EPOCH_OFFSET + zone;
tv->tv_micro = TimeRequest->tr_time.tv_micro;
DeleteTimer();
if (TimerSemaphore != NULL)
{
ReleaseSemaphore(TimerSemaphore);
}
return 0;
}
int settimeofday(const struct timeval *tv, void *tz)
{
long zone;
if (tv == NULL)
{
return -1;
}
if (TimerSemaphore != NULL)
{
ObtainSemaphore(TimerSemaphore);
}
if (CreateTimer() != 0)
{
if (TimerSemaphore != NULL)
{
ReleaseSemaphore(TimerSemaphore);
}
return -1;
}
zone = gmtoffset(tv->tv_secs);
TimeRequest->tr_node.io_Command = TR_SETSYSTIME;
TimeRequest->tr_time.tv_secs = (long)tv->tv_secs - EPOCH_OFFSET - zone;
TimeRequest->tr_time.tv_micro = tv->tv_micro;
DoIO((struct IORequest *)TimeRequest);
DeleteTimer();
if (TimerSemaphore != NULL)
{
ReleaseSemaphore(TimerSemaphore);
}
return 0;
}
int getsystime(struct timeval *tv)
{
if (tv == NULL)
{
return -1;
}
if (TimerSemaphore != NULL)
{
ObtainSemaphore(TimerSemaphore);
}
if (CreateTimer() != 0)
{
if (TimerSemaphore != NULL)
{
ReleaseSemaphore(TimerSemaphore);
}
tv->tv_secs = 0;
tv->tv_micro = 0;
return -1;
}
#ifdef AROS
TimeRequest->tr_node.io_Command = TR_GETSYSTIME;
DoIO((struct IORequest *)TimeRequest);
tv->tv_secs = TimeRequest->tr_time.tv_secs;
tv->tv_micro = 0;
#else
{
struct timeval tv1;
GetSysTime(&tv1);
*tv = tv1;
}
#endif
DeleteTimer();
if (TimerSemaphore != NULL)
{
ReleaseSemaphore(TimerSemaphore);
}
return 0;
}
int setsystime(struct timeval *tv)
{
if (tv == NULL)
{
return -1;
}
if (TimerSemaphore != NULL)
{
ObtainSemaphore(TimerSemaphore);
}
if (CreateTimer() != 0)
{
if (TimerSemaphore != NULL)
{
ReleaseSemaphore(TimerSemaphore);
}
return -1;
}
TimeRequest->tr_node.io_Command = TR_SETSYSTIME;
TimeRequest->tr_time.tv_secs = (long)tv->tv_secs;
TimeRequest->tr_time.tv_micro = tv->tv_micro;
DoIO((struct IORequest *)TimeRequest);
DeleteTimer();
if (TimerSemaphore != NULL)
{
ReleaseSemaphore(TimerSemaphore);
}
return 0;
}
#if defined(__AROS__)
int *__stdc_geterrnoptr(void)
{
return &errno;
}
#endif
int daylight_c(void)
{
return daylight;
}
long Timezone_c(void)
{
return Timezone;
}
long altzone_c(void)
{
return altzone;
}
char **tzname_c(void)
{
return (char **)tzname;
}
/*
struct lc_time_T *_current_locale()
{
return (struct lc_time_T *)&C_time_locale;
}
size_t
_strftime(const Timezone_t sp, char *const s, const size_t maxsize,
const char *const format, const struct tm *const t, struct lc_time_T *loc);
size_t
strftime_lz(const Timezone_t sp, char *const s, const size_t maxsize,
const char *const format, const struct tm *const t, locale_t locale)
{
size_t res;
struct lc_time_T *l = openlc(locale);
res = _strftime(sp, s, maxsize, format, t, l);
closelc(l);
return res;
}
*/
struct lc_time_T *openlc(locale_t locale)
{
int i, j;
struct lc_time_T *l = (struct lc_time_T *)AllocMem(
sizeof(struct lc_time_T),
MEMF_ANY | MEMF_CLEAR);
struct Locale *loc = locale;
if (!loc)
{
loc = OpenLocale(NULL);
}
l->X_fmt = (const char *)loc->loc_TimeFormat;
l->x_fmt = (const char *)loc->loc_ShortDateFormat;
l->c_fmt = (const char *)loc->loc_DateTimeFormat;
l->am = (const char *)GetLocaleStr(loc, AM_STR);
l->pm = (const char *)GetLocaleStr(loc, PM_STR);
l->date_fmt = "%a %b %e %H:%M:%S %Z %Y";
j = 0;
for (i = 1; i < 8; i++)
{
l->weekday[j++] = (const char *)GetLocaleStr(loc, i);
}
j = 0;
for (i = 8; i < 15; i++)
{
l->wday[j++] = (const char *)GetLocaleStr(loc, i);
}
j = 0;
for (i = 15; i < 27; i++)
{
l->month[j++] = (const char *)GetLocaleStr(loc, i);
}
j = 0;
for (i = 27; i < 39; i++)
{
l->mon[j++] = (const char *)GetLocaleStr(loc, i);
}
l->locale = loc;
return l;
}
void closelc(struct lc_time_T *lc)
{
if (lc->locale)
{
CloseLocale(lc->locale);
}
FreeMem(lc, sizeof(struct lc_time_T));
}
/* Similar to intuition.library CurrentTime */
void CurrentTimeGmt(ULONG *seconds, ULONG *micros)
{
ULONG sec, mic;
CurrentTime(&sec, &mic);
*seconds = sec + (daylight ? altzone : Timezone);
*micros = mic;
}
struct tm *AmigaLocalTime(ULONG *seconds, struct tm *tm)
{
time_t time;
if (tm == NULL)
{
return NULL;
}
if (seconds == NULL)
{
/* Amiga Epoch time */
Date2Tm(NULL, tm);
return tm;
}
time = Amiga2Unix(*seconds);
localtime_r(&time, tm);
return tm;
}
struct tm *AmigaLocalTimeZ(ULONG *seconds, struct tm *tm, const Timezone_t tz)
{
time_t time;
if (tm == NULL)
{
return NULL;
}
if (seconds == NULL)
{
/* Amiga Epoch time */
Date2Tm(NULL, tm);
return tm;
}
time = Amiga2Unix(*seconds);
localtime_rz(tz, &time, tm);
return tm;
}
/* Similar to locale.library FormatDate */
void FormatDateTm(char *const s, const char *format, struct tm *tm,
char *buffer, long maxsize)
{
strftime(s, maxsize, format, tm);
}
/* Similar to locale.library ParseDate */
int ParseDateTm(struct Locale *locale, const char *format, struct tm *tm,
const char *string)
{
// TODO
return 0;
}
/* Similar to utility.library Amiga2Date.
Fills in a tm structure with the date and time calculated
from a ULONG containing the number of seconds from 01-Jan-1978
to the date. */
void Amiga2Tm(ULONG seconds, struct tm *tm)
{
struct tm tmp;
time_t t;
if (tm == NULL)
{
return;
}
t = seconds + EPOCH_OFFSET;
localtime_r(&t, &tmp);
t = seconds + EPOCH_OFFSET - tmp.tm_gmtoff;
localtime_r(&t, tm);
}
/* Similar to utility.library Date2Amiga.
Calculates the number of seconds from 01-Jan-1978 to the date
specified in the tm structure. */
ULONG Tm2Amiga(struct tm *tm)
{
time_t time;
if (tm == NULL)
{
/* Amiga Epoch time */
return 0;
}
time = timelocal(tm);
return time - EPOCH_OFFSET;
}
/* Convert between Unix Time Epoch and Amiga Time Epoch */
ULONG Unix2Amiga(time_t t)
{
return t - EPOCH_OFFSET;
}
time_t Amiga2Unix(ULONG t)
{
return t + EPOCH_OFFSET;
}
/* Convert to and from DateStamp */
void Tm2DateStamp(struct tm *tm, struct DateStamp *date)
{
time_t time;
struct tm tmp = *tm;
if (date == NULL)
{
return;
}
if (tm == NULL)
{
/* UNIX Epoch time */
time = 0;
}
else
{
time = mktime(&tmp);
}
Unix2DateStamp(&time, date);
}
void DateStamp2Tm(struct DateStamp *date, struct tm *tm)
{
time_t time;
if (tm == NULL)
{
return;
}
if (date == NULL)
{
Date2Tm(NULL, tm);
return;
}
DateStamp2Unix(date, &time);
gmtime_r(&time, tm);
}
void Unix2DateStamp(time_t *time, struct DateStamp *date)
{
long t, d, m, q;
if (date == NULL)
{
return;
}
if (time == NULL)
{
/* Amiga Epoch time */
date->ds_Days = 0;
date->ds_Minute = 0;
date->ds_Tick = 0;
return;
}
t = *time - EPOCH_OFFSET;
d = t / SECONDSPERDAY;
m = (t - SECONDSPERDAY * d) / 60;
q = (t - SECONDSPERDAY * d - m * 60) * 60; // TODO: Ticks = 50
date->ds_Days = d;
date->ds_Minute = m;
date->ds_Tick = q;
}
void DateStamp2Unix(struct DateStamp *date, time_t *time)
{
if (time == NULL)
{
return;
}
if (date == NULL)
{
/* Amiga Epoch time */
*time = EPOCH_OFFSET;
return;
}
*time = date->ds_Days * SECONDSPERDAY +
date->ds_Minute * 60 +
date->ds_Tick / 60 +
EPOCH_OFFSET;
}
/* Convert from tm to ClockData */
void Tm2Date(struct tm *tm, struct ClockData *date)
{
if (date == NULL)
{
return;
}
if (tm == NULL)
{
/* UNIX Epoch time */
date->sec = 0;
date->min = 0;
date->hour = 0;
date->mday = 1;
date->month = 1;
date->year = EPOCH_YEAR;
date->wday = EPOCH_WDAY;
return;
}
date->sec = (UWORD)tm->tm_sec;
date->min = (UWORD)tm->tm_min;
date->hour = (UWORD)tm->tm_hour;
date->mday = (UWORD)tm->tm_mday;
date->month = (UWORD)tm->tm_mon + 1;
date->year = (UWORD)tm->tm_year + TM_YEAR_BASE + EPOCH_OFFSET_YEAR;
date->wday = (UWORD)tm->tm_wday;
/* tm allow seconds in interval [0-61] */
if (date->sec > 59)
{
date->sec = 59;
}
}
/* Convert from ClockData to tm */
void Date2Tm(struct ClockData *date, struct tm *tm)
{
if (tm == NULL)
{
return;
}
if (date == NULL)
{
/* Amiga Epoch time */
tm->tm_sec = 0;
tm->tm_min = 0;
tm->tm_hour = 0;
tm->tm_mday = 1;
tm->tm_mon = 0;
tm->tm_year = EPOCH_YEAR + EPOCH_OFFSET_YEAR - TM_YEAR_BASE;
mktime(tm);
return;
}
tm->tm_sec = (int)date->sec;
tm->tm_min = (int)date->min;
tm->tm_hour = (int)date->hour;
tm->tm_mday = (int)date->mday;
tm->tm_mon = (int)date->month - 1;
tm->tm_year = (int)date->year - TM_YEAR_BASE - EPOCH_OFFSET_YEAR;
tm->tm_wday = (int)date->wday;
mktime(tm);
}
void Date2Unix(struct ClockData *date, time_t *time)
{
struct tm tm;
if (time == NULL)
{
return;
}
if (date == NULL)
{
/* UNIX Epoch time */
*time = 0;
return;
}
Date2Tm(date, &tm);
mktime(&tm);
}
void Unix2Date(time_t *time, struct ClockData *date)
{
struct tm tm;
time_t t;
if (date == NULL)
{
return;
}
if (time == NULL)
{
/* UNIX Epoch time */
Tm2Date(NULL, date);
return;
}
t = *time;
gmtime_r(&t, &tm);
Tm2Date(&tm, date);
}
#undef time
#undef time_t
time_t
time(time_t *x)
{
time_t tloc;
ULONG sec, mic;
CurrentTime(&sec, &mic);
tloc = (time_t)(sec);
if (x)
{
*x = tloc;
}
return tloc;
}