amiga-tz/library/lib_base.c

588 lines
17 KiB
C

/*
* Copyright (c) 2015-2016 Carsten Larsen
* Copyright (c) 2002-2006 by Olaf Barthel <olsen (at) sourcery.han.de>
* 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.
* 3. Neither the name of Olaf Barthel nor the names of contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 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 OWNER 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 "time_header.h"
#include "time_version.h"
#include "lib.h"
#include "lib_map.h"
#include <exec/execbase.h>
#include <exec/resident.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <dos/dos.h>
LONG _start(VOID);
STATIC struct TimezoneBase *ASM lib_init(REG(d0,struct TimezoneBase *sb),REG(a0,BPTR segment_list),REG(a6,APTR whatever));
STATIC struct TimezoneBase *ASM lib_open(REG(a6,APTR base));
STATIC BPTR ASM lib_expunge(REG(a6,APTR base));
STATIC BPTR ASM lib_close(REG(a6,APTR base));
#if defined(__amigaos4__)
STATIC ULONG lib_default_obtain(struct Interface *self);
STATIC ULONG lib_default_release(struct Interface *self);
#else
STATIC LONG lib_reserved(VOID);
#endif
/* This is the first executable location of the library. It must return
an error to the unsuspecting caller who tries to run it as a program. */
LONG
_start(VOID)
{
return(-1);
}
/* This routine is called after the library has been loaded into
memory and its base data structure has been created. Here we
must initialize those parts of the base data structure which
have not been set up yet. As of Kickstart 2.0 the setup code
in ROM will have initialized the entire Library structure with
the exception of the lib_Revision field, i.e. everything that
could be gained from the Resident structure is now in the
Library structure. Earlier operating system releases would leave
the entire Library structure uninitialized.
The library initialization is single threaded, i.e. the operating
system processes library initialization in a queue. The longer it
takes for the following routine to set up the base data structure,
the longer will other Tasks and Processes have to wait to get their
disk resident libraries opened. This means, the initialization code
has to be as brief as possible. This particular example code opens
ROM resident libraries here, which is permitted. You should not
open disk resident libraries and devices or do anything else that
could take a long time to complete (in this context "long" is anything
that takes up a second or longer).
This routine must return the Library base pointer if it succeeds.
Failure is indicated by returning NULL instead. */
STATIC struct TimezoneBase * ASM
lib_init(
REG(d0, struct TimezoneBase* sb),
REG(a0, BPTR segment_list),
REG(a6, APTR whatever))
{
struct TimezoneBase * result = NULL;
struct Library * SysBase;
#if defined(__amigaos4__)
struct ExecIFace * IExec;
#endif /* __amigaos4__ */
#if defined(__amigaos4__)
{
IExec = (struct ExecIFace *)whatever;
SysBase = (struct Library *)IExec->Data.LibBase;
}
#else
{
SysBase = (struct Library *)whatever;
}
#endif /* __amigaos4__ */
/* This library implementation requires Kickstart 2.04 or
better to work. */
if(SysBase->lib_Version < 37)
{
sb->sb_UserData = NULL;
goto out;
}
sb->sb_SysBase = SysBase;
#if defined(__amigaos4__)
{
sb->sb_IExec = IExec;
}
#endif /* __amigaos4__ */
InitSemaphore(&sb->sb_Semaphore);
/* The segment pointer must be stored for later, when this library is
unloaded from memory again. */
sb->sb_SegmentList = segment_list;
sb->sb_Library.lib_Revision = REVISION_NO;
/* Allocate the implementation-specific user data structure. */
sb->sb_UserData = AllocMem(sizeof(*sb->sb_UserData),MEMF_ANY|MEMF_PUBLIC);
if(sb->sb_UserData == NULL)
goto out;
/* Now try the user-supplied library initialization function. */
if(UserLibInit(SysBase,sb,sb->sb_UserData) == FALSE)
goto out;
result = sb;
out:
/* Free the library data if the initialization failed. */
if(result == NULL)
{
/* First take care of the user data. */
if(sb->sb_UserData != NULL)
FreeMem(sb->sb_UserData,sizeof(*sb->sb_UserData));
/* Finally, free the library data. This is necessary for a library
of type RTF_AUTOINIT (see the "struct Resident" rt_Flags
initialization value for the LibTag declaration at the end of
this file). */
#if defined(__amigaos4__)
{
DeleteLibrary((struct Library *)sb);
}
#else
{
FreeMem(((BYTE *)sb) - sb->sb_Library.lib_NegSize,
(ULONG)(sb->sb_Library.lib_NegSize + sb->sb_Library.lib_PosSize));
}
#endif /* __amigaos4__ */
}
return(result);
}
/* This routine is called every time a Task or Process invokes
OpenLibrary() on this library. Task switching will be disabled
when control passes through this routine. Every action in this
routine happens on the context of the calling Task, so unlike
the library initialization routine one can open disk resident
libraries, devices, etc. and generally Wait() for something
to happen. The catch is that every busy operation that takes
a while to execute will "freeze" the machine until control
returns from this routine (Task switching is disabled). The Task
switching is disabled in order to avoid having two Tasks
execute this code at the same time. In a way, this makes the
library routine a critical region in the textbook sense.
This routine must return the Library base pointer if it succeeds.
Failure is indicated by returning NULL instead. */
STATIC struct TimezoneBase * ASM
lib_open(REG(a6,APTR base))
{
USE_LIBBASE(base);
USE_EXEC(sb);
struct TimezoneBase * result = NULL;
/* Mark the library as having another customer and
clear the delayed expunge flag. This flag is
referenced at the time the last customer closes
the library. If set, the library will be both
closed and removed from memory. We increment the
counter here since we will attempt to gain
access to a semaphore, which may have the effect
of causing the caller to wait. During the time
the caller is waiting somebody might trigger
the library expunge function which, if the
usage counter is zero, would unload the library. */
sb->sb_Library.lib_OpenCnt++;
sb->sb_Library.lib_Flags &= ~LIBF_DELEXP;
/* Now comes the really critical region of the code
Only one Task at a time may enter here. */
ObtainSemaphore(&sb->sb_Semaphore);
/* Invoke the user-supplied library open function. */
if(UserLibOpen(sb->sb_UserData) == FALSE)
goto out;
/* We have another customer. */
sb->sb_Library.lib_OpenCnt++;
result = sb;
out:
ReleaseSemaphore(&sb->sb_Semaphore);
sb->sb_Library.lib_OpenCnt--;
return(result);
}
/* This routine will be called when the library needs to be
removed from memory. If there are no Tasks or Processes
which have the library open, the routine must remove itself
from the public list of libraries, free the data that had
been allocated for it and return the segment list pointer
it had been given at initialization time so all its code
can be unloaded. This routine is the last to be called in
the life cycle of the library. It is called while Task
switching is disabled. */
STATIC BPTR ASM
lib_expunge(REG(a6,APTR base))
{
USE_LIBBASE(base);
USE_EXEC(sb);
BPTR result = ZERO;
/* If there are still customers which have the
library open, mark the library for delayed
expunge. This means, the library will be removed
as soon as the last customer closes it. */
if(sb->sb_Library.lib_OpenCnt > 0)
{
sb->sb_Library.lib_Flags |= LIBF_DELEXP;
}
else
{
/* Invoke the user-supplied expunge function. It must
not break a Forbid(), i.e. it must not call Wait()
directly or indirectly. */
UserLibExpunge(sb->sb_UserData);
/* Free the user data itself. */
FreeMem(sb->sb_UserData,sizeof(*sb->sb_UserData));
/* This is the segment list pointer we have to return. */
result = sb->sb_SegmentList;
/* Remove the library from the public list. */
Remove((struct Node *)sb);
/* Finally, free the data allocated for the
base data structure. */
#if defined(__amigaos4__)
{
DeleteLibrary((struct Library *)sb);
}
#else
{
FreeMem(((BYTE *)sb) - sb->sb_Library.lib_NegSize,
(ULONG)(sb->sb_Library.lib_NegSize + sb->sb_Library.lib_PosSize));
}
#endif /* __amigaos4__ */
}
return(result);
}
/* This routine is called every time CloseLibrary() is used
on the Library base data structure. It is called while
Task switching is disabled and has to return the result
code of the library expunge routine when the time is right. */
STATIC BPTR ASM
lib_close(REG(a6,APTR base))
{
USE_LIBBASE(base);
USE_EXEC(sb);
BPTR result = ZERO;
/* Now comes the really critical region of the code
Only one Task at a time may enter here. */
ObtainSemaphore(&sb->sb_Semaphore);
/* Invoke the user-supplied library close function. */
UserLibClose(sb->sb_UserData);
ReleaseSemaphore(&sb->sb_Semaphore);
/* One less customer. */
sb->sb_Library.lib_OpenCnt--;
/* Trigger the library expunge function, if desired. */
if(sb->sb_Library.lib_OpenCnt == 0 && (sb->sb_Library.lib_Flags & LIBF_DELEXP) != 0)
result = lib_expunge(base);
return(result);
}
#if defined(__amigaos4__)
STATIC ULONG
lib_default_obtain(struct Interface * self)
{
return(self->Data.RefCount++);
}
STATIC ULONG
lib_default_release(struct Interface * self)
{
return(self->Data.RefCount--);
}
STATIC CONST APTR manager_vectors[] =
{
lib_default_obtain,
lib_default_release,
NULL,
NULL,
lib_open,
lib_close,
lib_expunge,
NULL,
(APTR)-1,
};
STATIC CONST struct TagItem manager_tags[] =
{
{ MIT_Name, (ULONG)"__library" },
{ MIT_VectorTable, (ULONG)manager_vectors },
{ MIT_Version, 1 },
{ TAG_DONE, 0 }
};
STATIC CONST APTR main_vectors[]=
{
lib_default_obtain,
lib_default_release,
NULL,
NULL,
lib_tzset,
lib_tzalloc,
lib_tzfree,
lib_time,
lib_mktime,
lib_mktime_z,
lib_localtime,
lib_localtime_r,
lib_localtime_rz,
lib_gmtime,
lib_gmtime_r,
lib_ctime,
lib_ctime_r,
lib_ctime_rz,
lib_difftime,
lib_offtime,
lib_offtime_r,
lib_timeoff,
lib_asctime,
lib_asctime_r,
lib_strftime,
lib_strftime_l,
lib_strptime,
lib_strptime_l,
lib_timegm,
lib_timelocal,
lib_timelocal_z,
lib_daylight_c,
lib_timezone_c,
lib_altzone_c,
lib_tzname_c,
lib_time2posix,
lib_time2posix_z,
lib_posix2time,
lib_posix2time_z,
lib_getsystime,
lib_setsystime,
lib_gettimeofday,
lib_settimeofday,
lib_tzsetwall,
lib_tzgetname,
lib_tzgetlocation,
lib_gmtoffset,
lib_stou,
lib_utos,
(APTR)-1
};
STATIC CONST struct TagItem main_tags[] =
{
{ MIT_Name, (ULONG)"main" },
{ MIT_VectorTable, (ULONG)main_vectors },
{ MIT_Version, 1 },
{ TAG_DONE, 0 }
};
STATIC CONST APTR lib_interfaces[] =
{
(CONST APTR)manager_tags,
(CONST APTR)main_tags,
NULL
};
/* This is defined in the file "zoneinfo_68k.c". */
extern CONST APTR VecTable68K[];
CONST struct TagItem lib_create_tags[] =
{
{ CLT_DataSize, sizeof(struct TimezoneBase) },
{ CLT_Vector68K, (ULONG)VecTable68K },
{ CLT_Interfaces, (ULONG)lib_interfaces },
{ CLT_InitFunc, (ULONG)lib_init },
{ TAG_DONE, 0 }
};
CONST struct TagItem local_lib_create_tags[] =
{
{ CLT_DataSize, sizeof(struct TimezoneBase) },
{ CLT_Vector68K, (ULONG)VecTable68K },
{ CLT_Interfaces, (ULONG)lib_interfaces },
{ TAG_DONE, 0 }
};
/* This definition and how it is used makes sure that the library
version information will be visible even with an old 68k "Version"
command and not just with the OS4 "Version" command. */
STATIC CONST UBYTE version_string[] = "$VER: " VSTRING;
CONST struct Resident LibTag =
{
RTC_MATCHWORD,
(struct Resident *)&LibTag,
(struct Resident *)&LibTag+1,
RTF_AUTOINIT|RTF_NATIVE,
VERSION_NO,
NT_LIBRARY,
0,
"timezone.library",
(STRPTR)&version_string[6],
(struct TagItem *)lib_create_tags
};
#else
/* This is the reserved library entry point. It always
has to return 0. */
STATIC LONG
lib_reserved(VOID)
{
return(0);
}
/* The following data structures and data are responsible for
setting up the Library base data structure and the library
function vector. */
struct LibraryInitTable
{
ULONG lit_BaseSize; /* Size of the base data structure. */
APTR * lit_VectorTable; /* Points to the function vector. */
APTR lit_InitTable; /* Library base data structure setup table. */
APTR lit_InitRoutine; /* The address of the routine to do the setup. */
};
/* This is the table of functions that make up the library. The first
four are mandatory, everything following it are user callable
routines. The table is terminated by the value -1. */
STATIC CONST APTR lib_vectors[] =
{
lib_open,
lib_close,
lib_expunge,
lib_reserved,
lib_tzset,
lib_tzalloc,
lib_tzfree,
lib_time,
lib_mktime,
lib_mktime_z,
lib_localtime,
lib_localtime_r,
lib_localtime_rz,
lib_gmtime,
lib_gmtime_r,
lib_ctime,
lib_ctime_r,
lib_ctime_rz,
lib_difftime,
lib_offtime,
lib_offtime_r,
lib_timeoff,
lib_asctime,
lib_asctime_r,
lib_strftime,
lib_strftime_l,
lib_strptime,
lib_strptime_l,
lib_timegm,
lib_timelocal,
lib_timelocal_z,
lib_daylight_c,
lib_timezone_c,
lib_altzone_c,
lib_tzname_c,
lib_time2posix,
lib_time2posix_z,
lib_posix2time,
lib_posix2time_z,
lib_getsystime,
lib_setsystime,
lib_gettimeofday,
lib_settimeofday,
lib_tzsetwall,
lib_tzgetname,
lib_tzgetlocation,
lib_gmtoffset,
lib_stou,
lib_utos,
(APTR) -1
};
/* This finally sets up the library base data structure and the
function vector. */
STATIC CONST struct LibraryInitTable lib_init_table =
{
sizeof(struct TimezoneBase),
(APTR *)lib_vectors,
NULL,
lib_init
};
/* The library loader looks for this marker in the memory
the library code and data will occupy. It is responsible
setting up the Library base data structure. */
struct Resident LibTag =
{
RTC_MATCHWORD, /* Marker value. */
(struct Resident *)&LibTag, /* This points back to itself. */
(struct Resident *)&LibTag+1, /* This points behind this marker. */
RTF_AUTOINIT, /* The Library should be set up according to the given table. */
VERSION_NO, /* The version of this Library. */
NT_LIBRARY, /* This defines this module as a Library. */
0, /* Initialization priority of this Library; unused. */
"timezone.library", /* Points to the name of the Library. */
VSTRING, /* The identification string of this Library. */
(APTR)&lib_init_table /* This table is for initializing the Library. */
};
#endif