mirror of
https://github.com/adtools/clib2.git
synced 2025-12-08 14:59:05 +00:00
files which initialize global data, such as the new "stdlib_program_name.c" and "stdlib_malloc.c". - Until I can find a way to invoke it from within the library, the __machine_test() function is no longer invoked. - Tagged global data with NOCOMMON attributes. - Added the __lib_init() and __lib_exit() functions, which are part of the thread-safe library and which can be used to hook up clib2 with standard Amiga shared library/device code. Some documentation on how to use them can be found in the <dos.h> header file. git-svn-id: file:///Users/olsen/Code/migration-svn-zu-git/logical-line-staging/clib2/trunk@14999 87f5fb63-7c3d-0410-a384-fd976d0f7a62
591 lines
15 KiB
C
591 lines
15 KiB
C
/*
|
|
* $Id: stdlib_main.c,v 1.25 2005-07-03 10:36:47 obarthel Exp $
|
|
*
|
|
* :ts=4
|
|
*
|
|
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
|
* Copyright (c) 2002-2005 by Olaf Barthel <olsen@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:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* - 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.
|
|
*/
|
|
|
|
#ifndef EXEC_EXECBASE_H
|
|
#include <exec/execbase.h>
|
|
#endif /* EXEC_EXECBASE_H */
|
|
|
|
/****************************************************************************/
|
|
|
|
#include <setjmp.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
/****************************************************************************/
|
|
|
|
#ifndef _STDLIB_HEADERS_H
|
|
#include "stdlib_headers.h"
|
|
#endif /* _STDLIB_HEADERS_H */
|
|
|
|
#ifndef _STDIO_HEADERS_H
|
|
#include "stdio_headers.h"
|
|
#endif /* _STDIO_HEADERS_H */
|
|
|
|
/****************************************************************************/
|
|
|
|
#ifndef _STDLIB_PROFILE_MONITORING_H
|
|
#include "stdlib_profile_monitoring.h"
|
|
#endif /* _STDLIB_PROFILE_MONITORING_H */
|
|
|
|
/****************************************************************************/
|
|
|
|
extern int main(int arg_c,char ** arg_v);
|
|
|
|
/****************************************************************************/
|
|
|
|
/* This will be set to TRUE in case a stack overflow was detected. */
|
|
BOOL NOCOMMON __stack_overflow;
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC int
|
|
call_main(void)
|
|
{
|
|
ENTER();
|
|
|
|
/* This plants the return buffer for _exit(). */
|
|
if(setjmp(__exit_jmp_buf) != 0)
|
|
goto out;
|
|
|
|
SHOWMSG("now invoking the constructors");
|
|
|
|
/* Go through the constructor list */
|
|
_init();
|
|
|
|
SHOWMSG("done.");
|
|
|
|
/* If the SAS/C profiling code is set up for printing function
|
|
call chains, switch it on now. */
|
|
__show_profile_names();
|
|
|
|
/* This can be helpful for debugging purposes: print the name of the current
|
|
directory, followed by the name of the command and all the parameters
|
|
passed to it. */
|
|
#ifndef NDEBUG
|
|
{
|
|
UBYTE value_str[10];
|
|
LONG value;
|
|
|
|
/* Careful: only echo this information if a global environment
|
|
variable is set to enable this feature! */
|
|
if(GetVar("_echo",value_str,sizeof(value_str),GVF_GLOBAL_ONLY) > 0 && StrToLong(value_str,&value) > 0 && value != 0)
|
|
{
|
|
struct Process * this_process = (struct Process *)FindTask(NULL);
|
|
UBYTE * arg_str = GetArgStr();
|
|
size_t arg_str_len = strlen(arg_str);
|
|
UBYTE * arg_str_copy = AllocVec(arg_str_len+1,MEMF_ANY);
|
|
UBYTE current_dir_name[256];
|
|
|
|
if(arg_str_copy != NULL && NameFromLock(this_process->pr_CurrentDir,current_dir_name,sizeof(current_dir_name)))
|
|
{
|
|
strcpy(arg_str_copy,arg_str);
|
|
|
|
while(arg_str_len > 0 && arg_str_copy[arg_str_len-1] <= ' ')
|
|
arg_str_copy[--arg_str_len] = '\0';
|
|
|
|
kprintf("[%s] %s %s\n",current_dir_name,__program_name,arg_str_copy);
|
|
}
|
|
|
|
FreeVec(arg_str_copy);
|
|
}
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
/* After all these preparations, get this show on the road... */
|
|
exit(main((int)__argc,(char **)__argv));
|
|
|
|
out:
|
|
|
|
/* Switch off function name printing, if it was enabled. */
|
|
__hide_profile_names();
|
|
|
|
/* From this point on, don't worry about ^C checking any more. */
|
|
__check_abort_enabled = FALSE;
|
|
|
|
/* If we end up here with the __stack_overflow variable
|
|
set then the stack overflow handler dropped into
|
|
longjmp() and _exit() did not get called. This
|
|
means that we will have to show the error message
|
|
and invoke _exit() all on our own. */
|
|
if(__stack_overflow)
|
|
{
|
|
SHOWMSG("we have a stack overflow");
|
|
|
|
/* Dump all currently unwritten data, especially to the console. */
|
|
__flush_all_files(-1);
|
|
|
|
__show_error("Stack overflow detected");
|
|
|
|
if(setjmp(__exit_jmp_buf) == 0)
|
|
_exit(RETURN_FAIL);
|
|
}
|
|
|
|
/* If necessary, print stack size usage information. */
|
|
#ifndef NDEBUG
|
|
{
|
|
__stack_usage_exit();
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
SHOWMSG("invoking the destructors");
|
|
|
|
/* If one of the destructors drops into exit(), either directly
|
|
or through a failed assert() call, processing will resume with
|
|
the next following destructor. */
|
|
(void)setjmp(__exit_jmp_buf);
|
|
|
|
/* Go through the destructor list */
|
|
_fini();
|
|
|
|
SHOWMSG("done.");
|
|
|
|
RETURN(__exit_value);
|
|
return(__exit_value);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC BOOL
|
|
open_libraries(VOID)
|
|
{
|
|
BOOL success = FALSE;
|
|
int os_version;
|
|
|
|
/* Check which minimum operating system version we actually require. */
|
|
os_version = 37;
|
|
if(__minimum_os_lib_version > 37)
|
|
os_version = __minimum_os_lib_version;
|
|
|
|
/* Open the minimum required libraries. */
|
|
DOSBase = (struct Library *)OpenLibrary("dos.library",os_version);
|
|
if(DOSBase == NULL)
|
|
goto out;
|
|
|
|
__UtilityBase = OpenLibrary("utility.library",os_version);
|
|
if(__UtilityBase == NULL)
|
|
goto out;
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
/* Obtain the interfaces for these libraries. */
|
|
IDOS = (struct DOSIFace *)GetInterface(DOSBase, "main", 1, 0);
|
|
if(IDOS == NULL)
|
|
goto out;
|
|
|
|
__IUtility = (struct UtilityIFace *)GetInterface(__UtilityBase, "main", 1, 0);
|
|
if(__IUtility == NULL)
|
|
goto out;
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
success = TRUE;
|
|
|
|
out:
|
|
|
|
return(success);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC VOID
|
|
close_libraries(VOID)
|
|
{
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(__IUtility != NULL)
|
|
{
|
|
DropInterface((struct Interface *)__IUtility);
|
|
__IUtility = NULL;
|
|
}
|
|
|
|
if(IDOS != NULL)
|
|
{
|
|
DropInterface((struct Interface *)IDOS);
|
|
IDOS = NULL;
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
if(__UtilityBase != NULL)
|
|
{
|
|
CloseLibrary(__UtilityBase);
|
|
__UtilityBase = NULL;
|
|
}
|
|
|
|
if(DOSBase != NULL)
|
|
{
|
|
CloseLibrary(DOSBase);
|
|
DOSBase = NULL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC VOID ASM
|
|
detach_cleanup(REG(d0, LONG UNUSED unused_return_code),REG(d1, BPTR segment_list))
|
|
{
|
|
#if NOT defined(__amigaos4__)
|
|
{
|
|
/* The following trick is necessary only under dos.library V40 and below. */
|
|
if(((struct Library *)DOSBase)->lib_Version < 50)
|
|
{
|
|
/* Now for the slightly shady part. We need to unload the segment
|
|
list this program was originally loaded with. We have to close
|
|
dos.library, though, which means that either we can close the
|
|
library or unload the code, but not both. But there's a loophole
|
|
in that we can enter Forbid(), unload the code, close the library
|
|
and exit and nobody will be able to allocate this program's
|
|
memory until after the process has been terminated. */
|
|
Forbid();
|
|
|
|
UnLoadSeg(segment_list);
|
|
}
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
close_libraries();
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
STATIC ULONG
|
|
get_stack_size(void)
|
|
{
|
|
struct Task * tc = FindTask(NULL);
|
|
ULONG upper,lower;
|
|
ULONG result;
|
|
|
|
/* How much stack size was provided? */
|
|
upper = (ULONG)tc->tc_SPUpper;
|
|
lower = (ULONG)tc->tc_SPLower;
|
|
|
|
result = upper - lower;
|
|
|
|
return(result);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
int
|
|
_main(void)
|
|
{
|
|
struct Process * volatile child_process = NULL;
|
|
struct WBStartup * volatile startup_message;
|
|
volatile APTR old_window_pointer = NULL;
|
|
volatile BOOL old_window_pointer_valid = FALSE;
|
|
struct Process * this_process;
|
|
int return_code = RETURN_FAIL;
|
|
ULONG current_stack_size;
|
|
|
|
SysBase = *(struct Library **)4;
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
/* Get exec interface */
|
|
IExec = (struct ExecIFace *)((struct ExecBase *)SysBase)->MainInterface;
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
/* Pick up the Workbench startup message, if available. */
|
|
this_process = (struct Process *)FindTask(NULL);
|
|
|
|
if(this_process->pr_CLI == ZERO)
|
|
{
|
|
struct MsgPort * mp = &this_process->pr_MsgPort;
|
|
|
|
WaitPort(mp);
|
|
|
|
startup_message = (struct WBStartup *)GetMsg(mp);
|
|
}
|
|
else
|
|
{
|
|
startup_message = NULL;
|
|
}
|
|
|
|
__WBenchMsg = (struct WBStartup *)startup_message;
|
|
|
|
/* Try to open the libraries we need to proceed. */
|
|
if(CANNOT open_libraries())
|
|
{
|
|
char * error_message;
|
|
|
|
/* If available, use the error message provided by the client. */
|
|
error_message = __minimum_os_lib_error;
|
|
|
|
#if defined(__amigaos4__)
|
|
{
|
|
if(error_message == NULL)
|
|
error_message = "This program requires AmigaOS 4.0 or higher.";
|
|
}
|
|
#else
|
|
{
|
|
if(error_message == NULL)
|
|
error_message = "This program requires AmigaOS 2.04 or higher.";
|
|
}
|
|
#endif /* __amigaos4__ */
|
|
|
|
__show_error(error_message);
|
|
goto out;
|
|
}
|
|
|
|
if(__disable_dos_requesters)
|
|
{
|
|
/* Don't display any requesters. */
|
|
old_window_pointer = __set_process_window((APTR)-1);
|
|
}
|
|
else
|
|
{
|
|
/* Just remember the original pointer. */
|
|
old_window_pointer = __set_process_window(NULL);
|
|
|
|
__set_process_window(old_window_pointer);
|
|
}
|
|
|
|
old_window_pointer_valid = TRUE;
|
|
|
|
/* If a callback was provided which can fill us in on which
|
|
minimum stack size should be used, invoke it now and
|
|
store its result in the global __stack_size variable. */
|
|
if(__get_default_stack_size != NULL)
|
|
{
|
|
unsigned int size;
|
|
|
|
size = (*__get_default_stack_size)();
|
|
if(size > 0)
|
|
__stack_size = size;
|
|
}
|
|
|
|
/* How much stack space was provided? */
|
|
current_stack_size = get_stack_size();
|
|
|
|
/* If this is a resident program, don't allow for the detach
|
|
code to run. Same goes for launching the program from
|
|
Workbench. */
|
|
if(__is_resident || startup_message != NULL)
|
|
{
|
|
__detach = FALSE;
|
|
}
|
|
else if (__check_detach != NULL)
|
|
{
|
|
/* Check if we may need to detach from the shell. */
|
|
__detach = (*__check_detach)();
|
|
}
|
|
|
|
/* The following code will be executed if the program is to keep
|
|
running in the shell or was launched from Workbench. */
|
|
if(DO_NOT __detach)
|
|
{
|
|
int old_priority = this_process->pr_Task.tc_Node.ln_Pri;
|
|
|
|
/* Change the task priority, if requested. */
|
|
if(-128 <= __priority && __priority <= 127)
|
|
SetTaskPri((struct Task *)this_process,__priority);
|
|
|
|
/* Was a minimum stack size requested and do we
|
|
need more stack space than was provided for? */
|
|
if(__stack_size > 0 && current_stack_size < (ULONG)__stack_size)
|
|
{
|
|
struct StackSwapStruct * stk;
|
|
unsigned int stack_size;
|
|
APTR new_stack;
|
|
|
|
/* Make the stack size a multiple of 32 bytes. */
|
|
stack_size = 32 + ((__stack_size + 31UL) & ~31UL);
|
|
|
|
/* Allocate the stack swapping data structure
|
|
and the stack space separately. */
|
|
stk = AllocVec(sizeof(*stk),MEMF_PUBLIC|MEMF_ANY);
|
|
if(stk == NULL)
|
|
goto out;
|
|
|
|
new_stack = AllocMem(stack_size,MEMF_PUBLIC|MEMF_ANY);
|
|
if(new_stack == NULL)
|
|
{
|
|
FreeVec(stk);
|
|
goto out;
|
|
}
|
|
|
|
/* Fill in the lower and upper bounds, then take care of
|
|
the stack pointer itself. */
|
|
stk->stk_Lower = new_stack;
|
|
stk->stk_Upper = (ULONG)(new_stack) + stack_size;
|
|
stk->stk_Pointer = (APTR)(stk->stk_Upper - 32);
|
|
|
|
/* If necessary, set up for stack size usage measurement. */
|
|
#ifndef NDEBUG
|
|
{
|
|
__stack_usage_init(stk);
|
|
}
|
|
#endif /* NDEBUG */
|
|
|
|
return_code = __swap_stack_and_call(stk,(APTR)call_main);
|
|
|
|
FreeMem(new_stack, stack_size);
|
|
FreeVec(stk);
|
|
}
|
|
else
|
|
{
|
|
/* We have enough room to make the call or just don't care. */
|
|
return_code = call_main();
|
|
}
|
|
|
|
/* Restore the task priority. */
|
|
SetTaskPri((struct Task *)this_process,old_priority);
|
|
}
|
|
else
|
|
{
|
|
struct CommandLineInterface * cli = Cli();
|
|
struct TagItem tags[12];
|
|
UBYTE program_name[256];
|
|
unsigned int stack_size;
|
|
int i;
|
|
|
|
/* Now for the interesting part: detach from the shell we're
|
|
currently executing in. This works only if the program is
|
|
not reentrant and has not been launched from Workbench. */
|
|
|
|
stack_size = __stack_size;
|
|
|
|
if(stack_size < current_stack_size)
|
|
stack_size = current_stack_size;
|
|
|
|
if(stack_size < cli->cli_DefaultStack * sizeof(LONG))
|
|
stack_size = cli->cli_DefaultStack * sizeof(LONG);
|
|
|
|
GetProgramName(program_name,sizeof(program_name));
|
|
|
|
i = 0;
|
|
|
|
tags[i]. ti_Tag = NP_Entry;
|
|
tags[i++]. ti_Data = (ULONG)call_main;
|
|
tags[i]. ti_Tag = NP_StackSize;
|
|
tags[i++]. ti_Data = stack_size;
|
|
tags[i]. ti_Tag = NP_Name;
|
|
tags[i++]. ti_Data = (ULONG)(__process_name != NULL ? (UBYTE *)__process_name : FilePart(program_name));
|
|
tags[i]. ti_Tag = NP_CommandName;
|
|
tags[i++]. ti_Data = (ULONG)FilePart(program_name);
|
|
tags[i]. ti_Tag = NP_Cli;
|
|
tags[i++]. ti_Data = TRUE;
|
|
tags[i]. ti_Tag = NP_Arguments;
|
|
tags[i++]. ti_Data = (ULONG)GetArgStr();
|
|
tags[i]. ti_Tag = NP_ExitCode;
|
|
tags[i++]. ti_Data = (ULONG)detach_cleanup;
|
|
tags[i]. ti_Tag = NP_ExitData;
|
|
tags[i++]. ti_Data = (ULONG)cli->cli_Module;
|
|
|
|
/* Use a predefined task priority, if requested. */
|
|
if(-128 <= __priority && __priority <= 127)
|
|
{
|
|
tags[i]. ti_Tag = NP_Priority;
|
|
tags[i++]. ti_Data = (ULONG)__priority;
|
|
}
|
|
|
|
/* dos.library V50 will free the segment list upon exit. */
|
|
if(((struct Library *)DOSBase)->lib_Version >= 50)
|
|
{
|
|
tags[i]. ti_Tag = NP_Seglist;
|
|
tags[i++]. ti_Data = (ULONG)cli->cli_Module;;
|
|
tags[i]. ti_Tag = NP_FreeSeglist;
|
|
tags[i++]. ti_Data = TRUE;
|
|
}
|
|
|
|
tags[i].ti_Tag = TAG_END;
|
|
|
|
Forbid();
|
|
|
|
child_process = CreateNewProc(tags);
|
|
if(child_process == NULL)
|
|
{
|
|
Permit();
|
|
|
|
PrintFault(IoErr(),program_name);
|
|
|
|
return_code = RETURN_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/* The standard I/O streams are no longer attached to a console. */
|
|
__no_standard_io = TRUE;
|
|
|
|
cli->cli_Module = ZERO;
|
|
|
|
return_code = RETURN_OK;
|
|
|
|
Permit();
|
|
}
|
|
|
|
out:
|
|
|
|
if(old_window_pointer_valid)
|
|
__set_process_window(old_window_pointer);
|
|
|
|
if(child_process == NULL)
|
|
close_libraries();
|
|
|
|
if(startup_message != NULL)
|
|
{
|
|
Forbid();
|
|
|
|
ReplyMsg((struct Message *)startup_message);
|
|
}
|
|
|
|
return(return_code);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
/* The following is automatically called by the main() function through code
|
|
inserted by GCC. In theory, this could be removed by updating the machine
|
|
definition, but for now we'll just keep this stub around. It is intended
|
|
to call the constructor functions, but we do this in our own _main()
|
|
anyway. */
|
|
|
|
#if defined(__GNUC__)
|
|
|
|
/****************************************************************************/
|
|
|
|
void __main(void);
|
|
|
|
/****************************************************************************/
|
|
|
|
void
|
|
__main(void)
|
|
{
|
|
}
|
|
|
|
/****************************************************************************/
|
|
|
|
#endif /* __GNUC__ */
|