* pinfo.h (_pinfo::set_exit_state): Declare new function.
(pinfo::exit): Move here from _pinfo::exit. * sigproc.cc (child_info::sync): Use new function to set exitcode and process_state. * pinfo.cc (_pinfo::exit): Ditto. (proc_waiter): Ditto. (_pinfo::set_exit_state): Define new function. (_pinfo::dup_proc_pipe): Close handle when there is no parent process around to care about the exit value. * dcrt0.cc (dll_crt0_0): Move subproc_ready synchronization later to make sure that myself is still mapped in parent. (do_exit): Reflect movement to pinfo::exit. (__api_fatal): Ditto. * exceptions.cc (signal_exit): Ditto. * errno.cc (errmap): Map PROC_NOT_FOUND. * init.cc (dll_entry): Release myself before exiting. * sigproc.cc (proc_can_be_signalled): Set errno appropriately. (sig_send): Ditto. Also remove ill-advised test for !myself->sendsig since this is an indication of a process which is still initializating -- it is not an error. (child_info::sync): Don't set exitcode here. Assume that will happen in proc_waiter, if necessary. * spawn.cc (spawn_guts): Delay "wait_for_myself" logic until later. Don't wait at all if the process has already exited. Reflect movement to pinfo::exit.
This commit is contained in:
parent
aa67a4484e
commit
2380dfe14c
|
@ -1,3 +1,31 @@
|
|||
2005-01-11 Christopher Faylor <cgf@timesys.com>
|
||||
|
||||
* pinfo.h (_pinfo::set_exit_state): Declare new function.
|
||||
(pinfo::exit): Move here from _pinfo::exit.
|
||||
* sigproc.cc (child_info::sync): Use new function to set exitcode and
|
||||
process_state.
|
||||
* pinfo.cc (_pinfo::exit): Ditto.
|
||||
(proc_waiter): Ditto.
|
||||
(_pinfo::set_exit_state): Define new function.
|
||||
(_pinfo::dup_proc_pipe): Close handle when there is no parent process
|
||||
around to care about the exit value.
|
||||
* dcrt0.cc (dll_crt0_0): Move subproc_ready synchronization later to
|
||||
make sure that myself is still mapped in parent.
|
||||
(do_exit): Reflect movement to pinfo::exit.
|
||||
(__api_fatal): Ditto.
|
||||
* exceptions.cc (signal_exit): Ditto.
|
||||
* errno.cc (errmap): Map PROC_NOT_FOUND.
|
||||
* init.cc (dll_entry): Release myself before exiting.
|
||||
* sigproc.cc (proc_can_be_signalled): Set errno appropriately.
|
||||
(sig_send): Ditto. Also remove ill-advised test for !myself->sendsig
|
||||
since this is an indication of a process which is still initializating
|
||||
-- it is not an error.
|
||||
(child_info::sync): Don't set exitcode here. Assume that will happen
|
||||
in proc_waiter, if necessary.
|
||||
* spawn.cc (spawn_guts): Delay "wait_for_myself" logic until later.
|
||||
Don't wait at all if the process has already exited. Reflect movement
|
||||
to pinfo::exit.
|
||||
|
||||
2005-01-11 Corinna Vinschen <corinna@vinschen.de>
|
||||
|
||||
* environ.cc (build_env): Disallow empty strings and strings starting
|
||||
|
|
|
@ -625,12 +625,8 @@ dll_crt0_0 ()
|
|||
else if (sizeof (fhandler_union) != child_proc_info->fhandler_union_cb)
|
||||
multiple_cygwin_problem ("fhandler size", child_proc_info->fhandler_union_cb, sizeof (fhandler_union));
|
||||
else
|
||||
{
|
||||
if (child_proc_info->type != _PROC_FORK)
|
||||
child_proc_info->ready (true);
|
||||
cygwin_user_h = child_proc_info->user_h;
|
||||
break;
|
||||
}
|
||||
cygwin_user_h = child_proc_info->user_h;
|
||||
break;
|
||||
default:
|
||||
system_printf ("unknown exec type %d", child_proc_info->type);
|
||||
/* intentionally fall through */
|
||||
|
@ -667,6 +663,8 @@ dll_crt0_0 ()
|
|||
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
|
||||
h = NULL;
|
||||
set_myself (h);
|
||||
if (child_proc_info->type != _PROC_FORK)
|
||||
child_proc_info->ready (true);
|
||||
__argc = spawn_info->moreinfo->argc;
|
||||
__argv = spawn_info->moreinfo->argv;
|
||||
envp = spawn_info->moreinfo->envp;
|
||||
|
@ -1060,7 +1058,7 @@ do_exit (int status)
|
|||
}
|
||||
|
||||
minimal_printf ("winpid %d, exit %d", GetCurrentProcessId (), n);
|
||||
myself->exit (n);
|
||||
myself.exit (n);
|
||||
}
|
||||
|
||||
static muto *atexit_lock;
|
||||
|
@ -1123,7 +1121,7 @@ __api_fatal (const char *fmt, ...)
|
|||
#ifdef DEBUGGING
|
||||
(void) try_to_debug ();
|
||||
#endif
|
||||
myself->exit (1);
|
||||
myself.exit (1);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -121,6 +121,7 @@ static NO_COPY struct
|
|||
X (DEVICE_DOOR_OPEN, EIO),
|
||||
X (IO_PENDING, EAGAIN),
|
||||
X (TOO_MANY_LINKS, EMLINK),
|
||||
X (PROC_NOT_FOUND, ESRCH),
|
||||
{ 0, NULL, 0}
|
||||
};
|
||||
|
||||
|
|
|
@ -1080,7 +1080,7 @@ signal_exit (int rc)
|
|||
{
|
||||
EnterCriticalSection (&exit_lock);
|
||||
if (exit_already++)
|
||||
myself->exit (rc);
|
||||
myself.exit (rc);
|
||||
|
||||
/* We'd like to stop the main thread from executing but when we do that it
|
||||
causes random, inexplicable hangs. So, instead, we set up the priority
|
||||
|
|
|
@ -111,6 +111,7 @@ extern "C" int WINAPI
|
|||
dll_entry (HANDLE h, DWORD reason, void *static_load)
|
||||
{
|
||||
BOOL is_64bit_machine = FALSE;
|
||||
extern HANDLE hExeced;
|
||||
|
||||
switch (reason)
|
||||
{
|
||||
|
@ -127,8 +128,12 @@ dll_entry (HANDLE h, DWORD reason, void *static_load)
|
|||
dll_crt0_0 ();
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (myself && myself->exitcode == EXITCODE_UNSET)
|
||||
myself->exitcode = 1 << 8;
|
||||
if (myself)
|
||||
{
|
||||
if (!hExeced && myself->exitcode == EXITCODE_UNSET)
|
||||
myself->exitcode = 1 << 8;
|
||||
myself.release ();
|
||||
}
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
munge_threadfunc ();
|
||||
|
|
|
@ -39,6 +39,8 @@ static char NO_COPY pinfo_dummy[sizeof (_pinfo)] = {0};
|
|||
|
||||
pinfo NO_COPY myself ((_pinfo *)&pinfo_dummy); // Avoid myself != NULL checks
|
||||
|
||||
bool is_toplevel_proc;
|
||||
|
||||
/* Initialize the process table.
|
||||
This is done once when the dll is first loaded. */
|
||||
|
||||
|
@ -101,40 +103,55 @@ pinfo_init (char **envp, int envc)
|
|||
debug_printf ("pid %d, pgid %d", myself->pid, myself->pgid);
|
||||
}
|
||||
|
||||
# define self (*this)
|
||||
void
|
||||
_pinfo::exit (UINT n, bool norecord)
|
||||
pinfo::set_exit_state (DWORD pidstate)
|
||||
{
|
||||
DWORD x = 0xdeadbeef;
|
||||
DWORD oexitcode = self->exitcode;
|
||||
if (hProcess && self->exitcode == EXITCODE_UNSET)
|
||||
{
|
||||
GetExitCodeProcess (hProcess, &x);
|
||||
self->exitcode = (x & 0xff) << 8;
|
||||
}
|
||||
sigproc_printf ("exit value - old %p, windows %p, cygwin %p", oexitcode, x,
|
||||
self->exitcode);
|
||||
if (self->exitcode != EXITCODE_NOSET)
|
||||
self->process_state = pidstate;
|
||||
}
|
||||
|
||||
void
|
||||
pinfo::exit (DWORD n)
|
||||
{
|
||||
exit_state = ES_FINAL;
|
||||
cygthread::terminate ();
|
||||
if (norecord)
|
||||
sigproc_terminate (); /* Just terminate signal and process stuff */
|
||||
else
|
||||
exitcode = n; /* We're really exiting. Record the UNIX exit code. */
|
||||
|
||||
if (this)
|
||||
if (n != EXITCODE_EXEC)
|
||||
{
|
||||
/* FIXME: There is a potential race between an execed process and its
|
||||
parent here. I hated to add a mutex just for this, though. */
|
||||
struct rusage r;
|
||||
fill_rusage (&r, hMainProc);
|
||||
add_rusage (&rusage_self, &r);
|
||||
|
||||
if (!norecord)
|
||||
{
|
||||
process_state = PID_EXITED;
|
||||
/* Ensure that the parent knows that this logical process has
|
||||
terminated. */
|
||||
myself->alert_parent (0);
|
||||
|
||||
}
|
||||
sigproc_terminate (); /* Just terminate signal and process stuff */
|
||||
self->exitcode = n; /* We're really exiting. Record the UNIX exit code. */
|
||||
}
|
||||
sigproc_printf ("1 hProcess %p, n %p, exitcode %p", hProcess, n, self->exitcode);
|
||||
|
||||
sigproc_printf ("Calling ExitProcess norecord %d, n %p, exitcode %p",
|
||||
norecord, n, exitcode);
|
||||
/* FIXME: There is a potential race between an execed process and its
|
||||
parent here. I hated to add a mutex just for this, though. */
|
||||
struct rusage r;
|
||||
fill_rusage (&r, hMainProc);
|
||||
add_rusage (&self->rusage_self, &r);
|
||||
|
||||
set_exit_state (PID_EXITED);
|
||||
sigproc_printf ("2 hProcess %p, n %p, exitcode %p, EXITCODE_EXEC %p", hProcess, n, self->exitcode, EXITCODE_EXEC);
|
||||
if (n != EXITCODE_EXEC)
|
||||
{sigproc_printf ("3 hProcess %p, n %p, exitcode %p, EXITCODE_EXE %pC", hProcess, n, self->exitcode, EXITCODE_EXEC);
|
||||
myself->alert_parent (0);
|
||||
}
|
||||
|
||||
_my_tls.stacklock = 0;
|
||||
_my_tls.stackptr = _my_tls.stack;
|
||||
ExitProcess (exitcode);
|
||||
sigproc_printf ("Calling ExitProcess hProcess %p, n %p, exitcode %p",
|
||||
hProcess, n, self->exitcode);
|
||||
ExitProcess (self->exitcode);
|
||||
}
|
||||
# undef self
|
||||
|
||||
void
|
||||
pinfo::init (pid_t n, DWORD flag, HANDLE in_h)
|
||||
|
@ -664,7 +681,6 @@ _pinfo::cmdline (size_t& n)
|
|||
static DWORD WINAPI
|
||||
proc_waiter (void *arg)
|
||||
{
|
||||
extern HANDLE hExeced;
|
||||
pinfo& vchild = *(pinfo *) arg;
|
||||
|
||||
siginfo_t si;
|
||||
|
@ -685,6 +701,8 @@ proc_waiter (void *arg)
|
|||
{
|
||||
DWORD nb;
|
||||
char buf = '\0';
|
||||
extern HANDLE hExeced;
|
||||
|
||||
if (!ReadFile (vchild.rd_proc_pipe, &buf, 1, &nb, NULL)
|
||||
&& GetLastError () != ERROR_BROKEN_PIPE)
|
||||
{
|
||||
|
@ -702,12 +720,7 @@ proc_waiter (void *arg)
|
|||
/* Child exited. Do some cleanup and signal myself. */
|
||||
CloseHandle (vchild.rd_proc_pipe);
|
||||
vchild.rd_proc_pipe = NULL;
|
||||
if (vchild->exitcode == EXITCODE_UNSET)
|
||||
{
|
||||
DWORD x;
|
||||
GetExitCodeProcess (vchild.hProcess, &x);
|
||||
vchild->exitcode = (x & 0xff) << 8;
|
||||
}
|
||||
vchild.set_exit_state (PID_ZOMBIE);
|
||||
if (WIFEXITED (vchild->exitcode))
|
||||
si.si_sigval.sival_int = CLD_EXITED;
|
||||
else if (WCOREDUMP (vchild->exitcode))
|
||||
|
@ -715,7 +728,6 @@ proc_waiter (void *arg)
|
|||
else
|
||||
si.si_sigval.sival_int = CLD_KILLED;
|
||||
si.si_status = vchild->exitcode;
|
||||
vchild->process_state = PID_ZOMBIE;
|
||||
break;
|
||||
case SIGTTIN:
|
||||
case SIGTTOU:
|
||||
|
@ -769,7 +781,7 @@ _pinfo::dup_proc_pipe (HANDLE hProcess)
|
|||
/* Grr. Can't set DUPLICATE_CLOSE_SOURCE for exec case because we could be
|
||||
execing a non-cygwin process and we need to set the exit value before the
|
||||
parent sees it. */
|
||||
if (this != myself)
|
||||
if (this != myself || is_toplevel_proc)
|
||||
flags |= DUPLICATE_CLOSE_SOURCE;
|
||||
bool res = DuplicateHandle (hMainProc, wr_proc_pipe, hProcess, &wr_proc_pipe,
|
||||
0, FALSE, flags);
|
||||
|
|
|
@ -27,6 +27,8 @@ enum picom
|
|||
};
|
||||
|
||||
#define EXITCODE_UNSET 0x80000000
|
||||
#define EXITCODE_NOSET EXITCODE_UNSET
|
||||
#define EXITCODE_EXEC EXITCODE_UNSET
|
||||
|
||||
class _pinfo
|
||||
{
|
||||
|
@ -83,8 +85,6 @@ public:
|
|||
HANDLE tothem;
|
||||
HANDLE fromthem;
|
||||
|
||||
void exit (UINT n, bool norecord = 0) __attribute__ ((noreturn, regparm(2)));
|
||||
|
||||
inline void set_has_pgid_children ()
|
||||
{
|
||||
if (pgid == pid)
|
||||
|
@ -155,6 +155,8 @@ public:
|
|||
if (destroy && procinfo)
|
||||
release ();
|
||||
}
|
||||
void exit (DWORD n) __attribute__ ((noreturn, regparm(2)));
|
||||
void set_exit_state (DWORD) __attribute__ ((regparm(2)));
|
||||
void initialize_lock () {InitializeCriticalSection (&_lock);}
|
||||
void lock () {EnterCriticalSection (&_lock);}
|
||||
void unlock () {LeaveCriticalSection (&_lock);}
|
||||
|
|
|
@ -170,7 +170,13 @@ proc_can_be_signalled (_pinfo *p)
|
|||
if (p->sendsig != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (p == myself_nowait || p == myself)
|
||||
return hwait_sig;
|
||||
if (hwait_sig)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
set_errno (EAGAIN);
|
||||
return hwait_sig;
|
||||
}
|
||||
|
||||
if (ISSTATE (p, PID_INITIALIZING) ||
|
||||
(((p)->process_state & (PID_ACTIVE | PID_IN_USE)) ==
|
||||
|
@ -544,9 +550,6 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
|
|||
sigpacket pack;
|
||||
|
||||
pack.wakeup = NULL;
|
||||
if (!myself->sendsig) // FIXME: This catches the exec case but what if the exec is going to fail?
|
||||
goto out;
|
||||
|
||||
bool wait_for_completion;
|
||||
if (!(its_me = (p == NULL || p == myself || p == myself_nowait)))
|
||||
wait_for_completion = false;
|
||||
|
@ -556,6 +559,7 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
|
|||
{
|
||||
sigproc_printf ("hwait_sig %p, myself->sendsig %p, exit_state %d",
|
||||
hwait_sig, myself->sendsig, exit_state);
|
||||
set_errno (EAGAIN);
|
||||
goto out; // Either exiting or not yet initializing
|
||||
}
|
||||
if (wait_sig_inited)
|
||||
|
@ -601,8 +605,8 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
|
|||
HANDLE hp = OpenProcess (PROCESS_DUP_HANDLE, false, dwProcessId);
|
||||
if (!hp)
|
||||
{
|
||||
sigproc_printf ("OpenProcess failed, %E");
|
||||
__seterrno ();
|
||||
sigproc_printf ("OpenProcess failed, %E");
|
||||
goto out;
|
||||
}
|
||||
VerifyHandle (hp);
|
||||
|
@ -651,8 +655,8 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
|
|||
process is exiting. */
|
||||
if (!its_me)
|
||||
{
|
||||
sigproc_printf ("WriteFile for pipe %p failed, %E", sendsig);
|
||||
__seterrno ();
|
||||
sigproc_printf ("WriteFile for pipe %p failed, %E", sendsig);
|
||||
ForceCloseHandle (sendsig);
|
||||
}
|
||||
else
|
||||
|
@ -662,6 +666,7 @@ sig_send (_pinfo *p, siginfo_t& si, _cygtls *tls)
|
|||
else if (!hExeced)
|
||||
system_printf ("error sending signal %d to pid %d, pipe handle %p, %E",
|
||||
si.si_signo, p->pid, sendsig);
|
||||
set_errno (EACCES);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
@ -796,15 +801,9 @@ child_info::sync (pinfo& vchild, DWORD howlong)
|
|||
res = true;
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
sigproc_printf ("process exited before subproc_ready");
|
||||
if (WaitForSingleObject (subproc_ready, 0) == WAIT_OBJECT_0)
|
||||
sigproc_printf ("should never happen. noticed subproc_ready after process exit");
|
||||
else
|
||||
{
|
||||
DWORD exitcode = 0;
|
||||
(void) GetExitCodeProcess (vchild.hProcess, &exitcode);
|
||||
vchild->exitcode = (exitcode & 0xff) << 8;
|
||||
sigproc_printf ("non-cygwin exit value is %p", exitcode);
|
||||
}
|
||||
res = false;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -645,7 +645,7 @@ spawn_guts (const char * prog_arg, const char *const *argv,
|
|||
after CreateProcess and before copying the datastructures to the child.
|
||||
So we have to start the child in suspend state, unfortunately, to avoid
|
||||
a race condition. */
|
||||
if (wincap.start_proc_suspended() || mode != _P_OVERLAY
|
||||
if (wincap.start_proc_suspended () || mode != _P_OVERLAY
|
||||
|| cygheap->fdtab.need_fixup_before ())
|
||||
flags |= CREATE_SUSPENDED;
|
||||
|
||||
|
@ -787,7 +787,7 @@ spawn_guts (const char * prog_arg, const char *const *argv,
|
|||
/* Name the handle similarly to proc_subproc. */
|
||||
ProtectHandle1 (pi.hProcess, childhProc);
|
||||
|
||||
bool wait_for_myself = false;
|
||||
bool synced;
|
||||
if (mode == _P_OVERLAY)
|
||||
{
|
||||
myself->dwProcessId = dwExeced = pi.dwProcessId;
|
||||
|
@ -807,12 +807,7 @@ spawn_guts (const char * prog_arg, const char *const *argv,
|
|||
on this fact when we exit. dup_proc_pipe also closes our end of the pipe.
|
||||
Note that wr_proc_pipe may also be == INVALID_HANDLE_VALUE. That will make
|
||||
dup_proc_pipe essentially a no-op. */
|
||||
if (!myself->wr_proc_pipe)
|
||||
{
|
||||
myself.remember (false);
|
||||
wait_for_myself = true;
|
||||
}
|
||||
else
|
||||
if (myself->wr_proc_pipe)
|
||||
{
|
||||
/* Make sure that we own wr_proc_pipe just in case we've been
|
||||
previously execed. */
|
||||
|
@ -855,42 +850,51 @@ spawn_guts (const char * prog_arg, const char *const *argv,
|
|||
}
|
||||
}
|
||||
|
||||
/* Start the child running */
|
||||
if (flags & CREATE_SUSPENDED)
|
||||
ResumeThread (pi.hThread);
|
||||
ForceCloseHandle (pi.hThread);
|
||||
/* Start the child running */
|
||||
if (flags & CREATE_SUSPENDED)
|
||||
ResumeThread (pi.hThread);
|
||||
ForceCloseHandle (pi.hThread);
|
||||
|
||||
sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
|
||||
sigproc_printf ("spawned windows pid %d", pi.dwProcessId);
|
||||
|
||||
ciresrv.sync (myself, INFINITE);
|
||||
synced = ciresrv.sync (myself, INFINITE);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case _P_OVERLAY:
|
||||
if (wait_for_myself)
|
||||
waitpid (myself->pid, &res, 0);
|
||||
myself->exit (res, 1);
|
||||
break;
|
||||
case _P_WAIT:
|
||||
case _P_SYSTEM:
|
||||
if (waitpid (cygpid, (int *) &res, 0) != cygpid)
|
||||
res = -1;
|
||||
break;
|
||||
case _P_DETACH:
|
||||
res = 0; /* Lost all memory of this child. */
|
||||
break;
|
||||
case _P_NOWAIT:
|
||||
case _P_NOWAITO:
|
||||
case _P_VFORK:
|
||||
res = cygpid;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (mode)
|
||||
{
|
||||
case _P_OVERLAY:
|
||||
if (!synced)
|
||||
/* let myself.exit handle this */;
|
||||
else if (myself->wr_proc_pipe)
|
||||
myself.hProcess = NULL;
|
||||
else
|
||||
{
|
||||
extern bool is_toplevel_proc;
|
||||
is_toplevel_proc = true;
|
||||
myself.remember (false);
|
||||
waitpid (myself->pid, &res, 0);
|
||||
}
|
||||
myself.exit (EXITCODE_EXEC);
|
||||
break;
|
||||
case _P_WAIT:
|
||||
case _P_SYSTEM:
|
||||
if (waitpid (cygpid, &res, 0) != cygpid)
|
||||
res = -1;
|
||||
break;
|
||||
case _P_DETACH:
|
||||
res = 0; /* Lost all memory of this child. */
|
||||
break;
|
||||
case _P_NOWAIT:
|
||||
case _P_NOWAITO:
|
||||
case _P_VFORK:
|
||||
res = cygpid;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
pthread_cleanup_pop (1);
|
||||
return (int) res;
|
||||
pthread_cleanup_pop (1);
|
||||
return (int) res;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
|
|
|
@ -125,7 +125,7 @@ strace::vsprntf (char *buf, const char *func, const char *infmt, va_list ap)
|
|||
int microsec = microseconds ();
|
||||
lmicrosec = microsec;
|
||||
|
||||
__small_sprintf (fmt, "%7d [%s] %s ", microsec, tn, "%s %d%s");
|
||||
__small_sprintf (fmt, "%7d [%s] %s ", microsec, tn, "%s %d/%d%s");
|
||||
|
||||
SetLastError (err);
|
||||
|
||||
|
@ -147,7 +147,7 @@ strace::vsprntf (char *buf, const char *func, const char *infmt, va_list ap)
|
|||
*p = '\000';
|
||||
p = progname;
|
||||
count = __small_sprintf (buf, fmt, p && *p ? p : "?",
|
||||
myself->pid ?: GetCurrentProcessId (),
|
||||
myself->pid ?: GetCurrentProcessId (), GetCurrentProcessId (),
|
||||
execing ? "!" : "");
|
||||
if (func)
|
||||
count += getfunc (buf + count, func);
|
||||
|
|
Loading…
Reference in New Issue