diff options
Diffstat (limited to 'private/crt32/startup/crt0dat.c')
-rw-r--r-- | private/crt32/startup/crt0dat.c | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/private/crt32/startup/crt0dat.c b/private/crt32/startup/crt0dat.c new file mode 100644 index 000000000..42b6854bc --- /dev/null +++ b/private/crt32/startup/crt0dat.c @@ -0,0 +1,794 @@ +/*** +*crt0dat.c - 32-bit C run-time initialization/termination routines +* +* Copyright (c) 1986-1992, Microsoft Corporation. All rights reserved. +* +*Purpose: +* This module contains the routines _cinit, exit, and _exit +* for C run-time startup and termination. _cinit and exit +* are called from the _astart code in crt0.asm. +* This module also defines several data variables used by the +* runtime. +* +* [NOTE: Lock segment definitions are at end of module.] +* +* *** FLOATING POINT INITIALIZATION AND TERMINATION ARE NOT *** +* *** YET IMPLEMENTED IN THIS FILE *** +* +*Revision History: +* 06-28-89 PHG Module created, based on asm version +* 04-09-90 GJF Added #include <cruntime.h>. Made calling type +* explicit (_CALLTYPE1 or _CALLTYPE4). Also, fixed +* the copyright. +* 04-10-90 GJF Fixed compiler warnings (-W3). +* 05-21-90 GJF Added #undef _NFILE_ (temporary hack) and fixed the +* indents. +* 08-31-90 GJF Removed 32 from API names. +* 09-25-90 GJF Merged tree version with local (8-31 and 5-21 changes). +* 10-08-90 GJF New-style function declarators. +* 10-12-90 GJF Removed divide by 0 stuff. +* 10-18-90 GJF Added _pipech[] array. +* 11-05-90 GJF Added _umaskval. +* 12-04-90 GJF Added _osfinfo[] definition for Win32 target. Note that +* the Win32 support is still incomplete! +* 12-04-90 SRW Changed to include <oscalls.h> instead of <doscalls.h> +* 12-04-90 SRW Added _osfile back for win32. Changed _osfinfo from +* an array of structures to an array of 32-bit handles +* (_osfhnd) +* 12-28-90 SRW Added _CRUISER_ conditional around pack pragmas +* 01-29-91 GJF ANSI naming. +* 01-29-91 SRW Added call to GetFileType [_WIN32_] +* 02-18-91 SRW Removed duplicate defintion of _NFILE_ (see os2dll.h) +* 04-04-91 GJF Added definitions for _base[version|major|minor] +* (_WIN32_). +* 04-08-91 GJF Temporary hack for Win32/DOS folks - added HeapDestroy +* call to doexit to tear down the OS heap used by C +* heap. +* 04-09-91 PNT Added _MAC_ conditional +* 04-26-91 SRW Removed level 3 warnings +* 07-16-91 GJF Added fp initialization test-and-call [_WIN32_]. +* 07-26-91 GJF Revised initialization and termination stuff. In +* particular, removed need for win32ini.c [_WIN32_]. +* 08-07-91 GJF Added init. for FORTRAN runtime, if present [_WIN32_]. +* 08-21-91 GJF Test _prmtmp against NULL, not _prmtmp(). +* 08-21-91 JCR Added _exitflag, _endstdio, _cpumode, etc. +* 09-09-91 GJF Revised _doinitterm for C++ init. support and to make +* _onexit/atexit compatible with C++ needs. +* 09-16-91 GJF Must test __onexitend before calling _doinitterm. +* 10-29-91 GJF Force in floating point initialization for MIPS +* compiler [_WIN32_]. +* 11-13-91 GJF FORTRAN needs _onexit/atexit init. before call thru +* _pFFinit. +* 01-10-92 GJF Merged. Also, added _C_Termination_Done [_WIN32_]. +* 02-13-92 GJF Moved all lowio initialization to ioinit.c for Win32. +* 03-12-92 SKS Major changes to initialization/termination scheme +* 04-16-92 DJM POSIX support. +* 04-17-92 SKS Export _initterm() for CRTDLL model +* 05-07-92 DJM Removed _exit() from POSIX build. +* 06-03-92 GJF Temporarily restored call to FORTRAN init. +* 08-26-92 SKS Add _osver, _winver, _winmajor, _winminor +* 08-28-92 GJF Use unistd.h for POSIX build. +* 09-02-92 SKS Fix _onexit table traversal to be LIFO. +* Since table is built forward (my changes 03-12-92) +* the table must be traversed in reverse order. +* 10-30-92 TVB Force in floating point initialization for ALPHA +* compiler as was done for MIPS. [_WIN32_]. +* 11-12-92 SKS Remove hard-coded call to FORTRAN initializer +* 05-11-93 SKS _C_Termination_Done is now used by DLLs in LIBC/LIBCMT +* models, not just in CRTDLL.DLL. +* Remove obsolete variable _child. +* 07-16-93 SRW ALPHA Merge +* +*******************************************************************************/ + +#include <cruntime.h> +#ifdef _POSIX_ +#include <unistd.h> +#else +#include <msdos.h> +#endif +#include <dos.h> +#include <oscalls.h> +#include <os2dll.h> +#include <internal.h> +#include <stdio.h> +#include <stdlib.h> +#include <process.h> + +/* define errno */ +#ifndef MTHREAD +int _VARTYPE1 errno = 0; /* libc error value */ +#ifdef _CRUISER_ +int _VARTYPE1 _doserrno = 0; /* OS system error value */ +#else /* ndef _CRUISER_ */ +#ifdef _WIN32_ +unsigned long _VARTYPE1 _doserrno = 0; /* OS system error value */ +#endif /* _WIN32_ */ +#endif /* _CRUISER_ */ +#endif /* MTHREAD */ + +/* define umask */ +int _umaskval = 0; + +/* define version info variables */ + +unsigned int _VARTYPE1 _osver = 0; +unsigned int _VARTYPE1 _winver = 0; +unsigned int _VARTYPE1 _winmajor = 0; +unsigned int _VARTYPE1 _winminor = 0; + +/* --------- The following block is OBSOLETE --------- */ + +unsigned int _VARTYPE1 _osversion = 0; +unsigned int _VARTYPE1 _osmajor = 0; +unsigned int _VARTYPE1 _osminor = 0; + +#if defined(_WIN32_) || defined(_POSIX_) +unsigned int _VARTYPE1 _baseversion = 0; +unsigned int _VARTYPE1 _basemajor = 0; +unsigned int _VARTYPE1 _baseminor = 0; +#endif /* _WIN32_ || _POSIX_ */ + +/* define _osmode/_cpumode */ + +#ifdef _CRUISER_ +unsigned char _osmode = _OS2_20_MODE; +#else /* ndef _CRUISER_ */ +#ifdef _DOSX32_ +unsigned char _osmode = _DOSX32_MODE; +#else /* ndef _DOSX32_ */ +#ifdef _WIN32_ +unsigned char _osmode = _WIN_MODE; +#else /* ndef _WIN32_ */ +#ifdef _POSIX_ +unsigned char _osmode = _POSIX_MODE_; +#endif /* _POSIX_ */ +#endif /* _WIN32DOS_ */ +#endif /* _WIN32_ */ +#endif /* _CRUISER_ */ + +unsigned char _cpumode = _FLAT_MODE; + +/* --------- The preceding block is OBSOLETE --------- */ + +#ifdef _CRUISER_ + +/* number of allowable file handles */ +int _nfile = _NFILE_; + +/* file handle database -- stdout, stdin, stderr are open */ +char _osfile[_NFILE_] = {FOPEN+FTEXT, FOPEN+FTEXT, FOPEN+FTEXT}; + +#endif /* _CRUISER_ */ + +#ifdef _WIN32DOS_ +extern HANDLE _HeapHandle; +#endif /* _WIN32DOS_ */ + +#ifdef _CRUISER_ + +/* peek-ahead buffers for pipes, each initialized to a newline */ + +char _pipech[_NFILE_] = { + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10 +#ifdef MTHREAD + , 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 +#endif + }; +#endif /* _CRUISER_ */ + +/* argument vector and environment */ +int __argc = 0; +char **__argv = NULL; +#ifdef _POSIX_ +char **environ = NULL; +#else +char **_environ = NULL; +#endif +char *_pgmptr; /* ptr to program name */ + +/* callable exit flag */ +char _exitflag = 0; + +#if defined(_WIN32_) +/* + * flag indicating if C runtime termination has been done. set if exit, + * _exit, _cexit or _c_exit has been called. checked when _CRTDLL_INIT + * or user DLL's _CRT_INIT is called with DLL_PROCESS_DETACH. + */ +int _C_Termination_Done = FALSE; +#endif + +/* + * useful type for initialization and termination declarations + */ +typedef void (_CALLTYPE1 *PF)(void); + +#ifdef _CRUISER_ + +PF *__onexittable = NULL; /* ptr to on exit function table */ + +#else /* ndef _CRUISER_ */ + +#if defined(_WIN32_) || defined(_POSIX_) + +/* + * NOTE: THE USE OF THE POINTERS DECLARED BELOW DEPENDS ON THE PROPERTIES + * OF C COMMUNAL VARIABLES. SPECIFICALLY, THEY ARE NON-NULL IFF THERE EXISTS + * A DEFINITION ELSEWHERE INITIALIZING THEM TO NON-NULL VALUES. + */ + +/* + * pointers to initialization functions + */ + +PF _FPinit; /* floating point init. */ + +/* + * pointers to initialization sections + */ + +extern PF __xi_a[], __xi_z[]; /* C initializers */ +extern PF __xc_a[], __xc_z[]; /* C++ initializers */ +extern PF __xp_a[], __xp_z[]; /* C pre-terminators */ +extern PF __xt_a[], __xt_z[]; /* C terminators */ + +#if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) +/* + * For MIPS or ALPHA compilers, must explicitly force in and call the floating + * point initialization (those system always have floating-point hardware). + */ +extern void _CALLTYPE1 _fpmath(void); +#endif + +/* + * pointers to the start and finish of the _onexit/atexit table + */ +PF *__onexitbegin; +PF *__onexitend; + + +/* + * static (internal) function that walks a table of function pointers, + * calling each entry between the two pointers, skipping NULL entries + * + * Needs to be exported for CRT DLL so that C++ initializers in the + * client EXE / DLLs can be initialized + */ +#ifdef CRTDLL +void _CALLTYPE1 _initterm(PF *, PF *); +#else +static void _CALLTYPE4 _initterm(PF *, PF *); +#endif + +#else /* ndef _WIN32_ || _POSIX_ */ + +#error ERROR - ONLY CRUISER, WIN32, OR POSIX TARGET SUPPORTED! + +#endif /* _WIN32_ || _POSIX_ */ + +#endif /* _CRUISER_ */ + + +/*** +*_cinit - C initialization +* +*Purpose: +* This routine performs the shared DOS and Windows initialization. +* The following order of initialization must be preserved - +* +ifdef MTHREAD +* 0. Call OS2 to bump max file count (mthread only) +endif +* 1. Check for devices for file handles 0 - 2 +* 2. Integer divide interrupt vector setup +* 3. General C initializer routines +* +*Entry: +* No parameters: Called from __crtstart and assumes data +* set up correctly there. +* +*Exit: +* Initializes C runtime data. +* +*Exceptions: +* +*******************************************************************************/ + +void _CALLTYPE1 _cinit ( + void + ) +{ +#ifdef _CRUISER_ + int fh; /* file handle we're checking */ + ULONG htype; /* handle type */ + +#ifdef MTHREAD + /* 0. Call OS/2 to bump max file count -- + even though the IO database supports _NFILE_ (256) files, + only bump the count to _NFILE_MAXFH_ (40) */ + + DOSSETMAXFH(_NFILE_MAXFH_); +#else + #if 0 + /* This code is intentionally commented out -- to access more + than 20 files re-enable this code */ + DOSSETMAXFH(_NFILE_); + #endif +#endif + + /* 1. check for devices on file handles 0 - 2 */ + for (fh = 0; fh <= 2; ++fh) { + ULONG devattr; /* device attributes */ + + /* default: neither pipe nor device */ + _osfile[fh] &= ~(FDEV | FPIPE); + + if (DOSQUERYHTYPE(fh, &htype, &devattr) == 0) { + /* check returned handle type -- only lo byte */ + if ((htype & 0xFF) == HANDTYPE_DEVICE) + _osfile[fh] |= FDEV; + else if ((htype & 0xFF) == HANDTYPE_PIPE) + _osfile[fh] |= FPIPE; + } + } + + /* 2. general C initializer routines */ + +/******* FLOATING POINT INIT SHOULD GO HERE (before initclock) ******/ + + __doinits(); /* execute initializers */ + +#else /* ndef _CRUISER_ */ + +#if defined(_WIN32_) || defined(_POSIX_) + /* + * initialize floating point package, if present + */ +#if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) + /* + * The Mips, Alpha, and PPC compilers don't emit external reference to + * _fltused. Therefore, must always force in the floating point + * initialization. + */ + _fpmath(); +#else + if ( _FPinit != NULL ) + (*_FPinit)(); +#endif + + /* + * do initializations + */ + _initterm( __xi_a, __xi_z ); + + /* + * do C++ initializations + */ + _initterm( __xc_a, __xc_z ); + +#else /* ndef _WIN32_ || _POSIX_ */ + +#ifdef _MAC_ + + TBD(); + +#else /* ndef _MAC_ */ + +#error ERROR - ONLY CRUISER, WIN32, MAC, OR POSIX TARGET SUPPORTED! + +#endif /* _MAC_ */ + +#endif /* _WIN32_ */ + +#endif /* _CRUISER_ */ + +} + + +/*** +*exit(status), _exit(status), _cexit(void), _c_exit(void) - C termination +* +*Purpose: +* +* Entry points: +* +* exit(code): Performs all the C termination functions +* and terminates the process with the return code +* supplied by the user. +* +* _exit(code): Performs a quick exit routine that does not +* do certain 'high-level' exit processing. The _exit +* routine terminates the process with the return code +* supplied by the user. +* +* _cexit(): Performs the same C lib termination processing +* as exit(code) but returns control to the caller +* when done (i.e., does NOT terminate the process). +* +* _c_exit(): Performs the same C lib termination processing +* as _exit(code) but returns control to the caller +* when done (i.e., does NOT terminate the process). +* +* Termination actions: +* +* exit(), _cexit(): +* +* 1. call user's terminator routines +* 2. call C runtime preterminators +* +* _exit(), _c_exit(): +* +* 3. call C runtime terminators +* 4. return to DOS or caller +* +* Notes: +* +* The termination sequence is complicated due to the multiple entry +* points sharing the common code body while having different entry/exit +* sequences. +* +* Multi-thread notes: +* +* 1. exit() should NEVER be called when mthread locks are held. +* The exit() routine can make calls that try to get mthread locks. +* +* 2. _exit()/_c_exit() can be called from anywhere, with or without locks held. +* Thus, _exit() can NEVER try to get locks (otherwise, deadlock +* may occur). _exit() should always 'work' (i.e., the process +* should always terminate successfully). +* +* 3. Only one thread is allowed into the exit code (see _lockexit() +* and _unlockexit() routines). +* +*Entry: +* exit(), _exit() +* int status - exit status (0-255) +* +* _cexit(), _c_exit() +* <no input> +* +*Exit: +* exit(), _exit() +* <EXIT to DOS> +* +* _cexit(), _c_exit() +* Return to caller +* +*Uses: +* +*Exceptions: +* +*******************************************************************************/ + +/* worker routine prototype */ +static void _CALLTYPE4 doexit (int code, int quick, int retcaller); + +void _CALLTYPE1 exit ( + int code + ) +{ + doexit(code, 0, 0); /* full term, kill process */ +} + +#ifndef _POSIX_ + +void _CALLTYPE1 _exit ( + int code + ) +{ + doexit(code, 1, 0); /* quick term, kill process */ +} + +void _CALLTYPE1 _cexit ( + void + ) +{ + doexit(0, 0, 1); /* full term, return to caller */ +} + +void _CALLTYPE1 _c_exit ( + void + ) +{ + doexit(0, 1, 1); /* quick term, return to caller */ +} + +#endif /* _POSIX_ */ + + +static void _CALLTYPE4 doexit ( + int code, + int quick, + int retcaller + ) +{ +#ifdef MTHREAD + _lockexit(); /* assure only 1 thread in exit path */ +#endif + +#if defined(_WIN32_) && defined(CRTDLL) + _C_Termination_Done = TRUE; +#endif + + /* save callable exit flag (for use by terminators) */ + _exitflag = (char) retcaller; /* 0 = term, !0 = callable exit */ + + if (!quick) { + +#ifdef _CRUISER_ + /* + * do _onexit/atexit() terminators + */ + if ( __onexitend != NULL ) + _doinitterm(__onexitend); + + /* call pre-terminators (flushall, rmtmp) */ + __dopreterms(); + } + + /* do C terminators */ + __doterms(); + +#else /* ndef _CRUISER_ */ + +#if defined(_WIN32_) || defined(_POSIX_) + /* + * do _onexit/atexit() terminators + * (if there are any) + * + * These terminators MUST be executed in reverse order (LIFO)! + * + * NOTE: + * This code assumes that __onexitbegin points + * to the first valid onexit() entry and that + * __onexitend points past the last valid entry. + * If __onexitbegin == __onexitend, the table + * is empty and there are no routines to call. + */ + + if (__onexitbegin) { + PF * pfend = __onexitend; + + while ( -- pfend >= __onexitbegin ) + /* + * if current table entry is non-NULL, + * call thru it. + */ + if ( *pfend != NULL ) + (**pfend)(); + } + + /* + * do pre-terminators + */ + _initterm(__xp_a, __xp_z); + } + + /* + * do terminators + */ + _initterm(__xt_a, __xt_z); + +#else /* ndef _WIN32_ || _POSIX_ */ + +#error ERROR - ONLY CRUISER, WIN32, OR POSIX TARGET SUPPORTED! + +#endif /* _WIN32_ || _POSIX_ */ + +#endif /* _CRUISER_ */ + + +#ifdef _WIN32DOS_ + +/* + * TEMPORARY HACK! THE CODE BELOW IS INTENDED TO ALLOW LIMITED USE OF THE + * C RUNTIME ON WIN32/DOS. IT WILL BE DELETED AS SOON AS THEY IMPLEMENT + * VirtualAlloc()! + */ + + HeapDestroy(_HeapHandle); + +#endif /* _WIN32DOS_ */ + + +/********** FLOATING POINT TERMINATION SHOULD GO HERE ************/ + + /* return to OS/2 or to caller */ + if (retcaller) { +#ifdef MTHREAD + _unlockexit(); /* unlock the exit code path */ +#endif + return; + } + +#ifdef _CRUISER_ + + DOSEXIT(EXIT_PROCESS, code); + +#else /* ndef _CRUISER_ */ + +#ifdef _WIN32_ + + ExitProcess(code); + +#else /* ndef _WIN32_ */ + +#ifdef _POSIX_ + + _exit(code); + +#else /* ndef _POSIX_ */ + +#ifdef _MAC_ + + TBD(); + +#else /* ndef _MAC_ */ + +#error ERROR - ONLY CRUISER, WIN32, MAC, OR POSIX TARGET SUPPORTED! + +#endif /* _POSIX_ */ + +#endif /* _MAC_ */ + +#endif /* _WIN32_ */ + +#endif /* _CRUISER_ */ +} + +#ifdef MTHREAD +/*** +* _lockexit - Aquire the exit code lock +* +*Purpose: +* Makes sure only one thread is in the exit code at a time. +* If a thread is already in the exit code, it must be allowed +* to continue. All other threads must pend. +* +* Notes: +* +* (1) It is legal for a thread that already has the lock to +* try and get it again(!). That is, consider the following +* sequence: +* +* (a) program calls exit() +* (b) thread locks exit code +* (c) user onexit() routine calls _exit() +* (d) same thread tries to lock exit code +* +* Since _exit() must ALWAYS be able to work (i.e., can be called +* from anywhere with no regard for locking), we must make sure the +* program does not deadlock at step (d) above. +* +* (2) If a thread executing exit() or _exit() aquires the exit lock, +* other threads trying to get the lock will pend forever. That is, +* since exit() and _exit() terminate the process, there is not need +* for them to unlock the exit code path. +* +* (3) Note that onexit()/atexit() routines call _lockexit/_unlockexit +* to protect mthread access to the onexit table. +* +* (4) The _lockexit/_unlockexit routines are very complicated in 286 +* OS/2 since a thread that held a lock could not request the lock again. +* The 32-bit OS/2 semaphore calls DO allow a single thread to aquire the +* same lock multiple times* thus, this version is straight forward. +* +*Entry: <none> +* +*Exit: +* Calling thread has exit code path locked on return. +* +*Exceptions: +* +*******************************************************************************/ + +void _CALLTYPE1 _lockexit ( + void + ) +{ + _mlock(_EXIT_LOCK1); +} + +/*** +* _unlockexit - Release exit code lock +* +*Purpose: +* [See _lockexit() description above.] +* +* This routine is called by _cexit(), _c_exit(), and onexit()/atexit(). +* The exit() and _exit() routines never unlock the exit code path since +* they are terminating the process. +* +*Entry: +* Exit code path is unlocked. +* +*Exit: +* +*Exceptions: +* +*******************************************************************************/ + +void _CALLTYPE1 _unlockexit ( + void + ) +{ + _munlock(_EXIT_LOCK1); +} + +#endif /* MTHREAD */ + + +#if defined(_WIN32_) || defined(_POSIX_) + +/*** +* static void _initterm(PF * pfbegin, PF * pfend) - call entries in +* function pointer table +* +*Purpose: +* Walk a table of function pointers, calling each entry, as follows: +* +* 1. walk from beginning to end, pfunctbl is assumed to point +* to the beginning of the table, which is currently a null entry, +* as is the end entry. +* 2. skip NULL entries +* 3. stop walking when the end of the table is encountered +* +*Entry: +* PF *pfbegin - pointer to the beginning of the table (first valid entry). +* PF *pfend - pointer to the end of the table (after last valid entry). +* +*Exit: +* No return value +* +*Notes: +* This routine must be exported in the CRT DLL model so that the client +* EXE and client DLL(s) can call it to initialize their C++ constructors. +* +*Exceptions: +* If either pfbegin or pfend is NULL, or invalid, all bets are off! +* +*******************************************************************************/ + +#ifdef CRTDLL +void _CALLTYPE1 _initterm ( +#else +static void _CALLTYPE4 _initterm ( +#endif + PF * pfbegin, + PF * pfend + ) +{ + /* + * walk the table of function pointers from the bottom up, until + * the end is encountered. Do not skip the first entry. The initial + * value of pfbegin points to the first valid entry. Do not try to + * execute what pfend points to. Only entries before pfend are valid. + */ + while ( pfbegin < pfend ) + { + /* + * if current table entry is non-NULL, call thru it. + */ + if ( *pfbegin != NULL ) + (**pfbegin)(); + ++pfbegin; + } +} + +#endif /* _WIN32_ || _POSIX_ */ |