1174 lines
24 KiB
C
1174 lines
24 KiB
C
/* amigaio.c mixes amigaos and perl APIs,
|
|
* as opposed to amigaos.c which is pure amigaos */
|
|
|
|
#include "EXTERN.h"
|
|
#include "perl.h"
|
|
|
|
#include "amigaos4/amigaio.h"
|
|
#include "amigaos.h"
|
|
|
|
#ifdef WORD
|
|
# undef WORD
|
|
# define WORD int16
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <exec/semaphores.h>
|
|
#include <exec/exectags.h>
|
|
#include <proto/exec.h>
|
|
#include <proto/dos.h>
|
|
#include <proto/utility.h>
|
|
#include <dos/dos.h>
|
|
|
|
extern struct SignalSemaphore popen_sema;
|
|
extern unsigned int pipenum;
|
|
|
|
extern int32 myruncommand(BPTR seglist, int stack, char *command, int length, char **envp);
|
|
|
|
void amigaos_stdio_get(pTHX_ StdioStore *store)
|
|
{
|
|
store->astdin =
|
|
amigaos_get_file(PerlIO_fileno(IoIFP(GvIO(PL_stdingv))));
|
|
store->astderr =
|
|
amigaos_get_file(PerlIO_fileno(IoIFP(GvIO(PL_stderrgv))));
|
|
store->astdout = amigaos_get_file(
|
|
PerlIO_fileno(IoIFP(GvIO(gv_fetchpv("STDOUT", TRUE, SVt_PVIO)))));
|
|
}
|
|
|
|
void amigaos_stdio_save(pTHX_ StdioStore *store)
|
|
{
|
|
amigaos_stdio_get(aTHX_ store);
|
|
store->oldstdin = IDOS->SelectInput(store->astdin);
|
|
store->oldstderr = IDOS->SelectErrorOutput(store->astderr);
|
|
store->oldstdout = IDOS->SelectOutput(store->astdout);
|
|
}
|
|
|
|
void amigaos_stdio_restore(pTHX_ const StdioStore *store)
|
|
{
|
|
IDOS->SelectInput(store->oldstdin);
|
|
IDOS->SelectErrorOutput(store->oldstderr);
|
|
IDOS->SelectOutput(store->oldstdout);
|
|
}
|
|
|
|
void amigaos_post_exec(int fd, int do_report)
|
|
{
|
|
/* We *must* write something to our pipe or else
|
|
* the other end hangs */
|
|
if (do_report)
|
|
{
|
|
int e = errno;
|
|
PerlLIO_write(fd, (void *)&e, sizeof(e));
|
|
PerlLIO_close(fd);
|
|
}
|
|
}
|
|
|
|
|
|
struct popen_data
|
|
{
|
|
struct Task *parent;
|
|
STRPTR command;
|
|
};
|
|
|
|
static int popen_result = 0;
|
|
|
|
int popen_child()
|
|
{
|
|
struct Task *thisTask = IExec->FindTask(0);
|
|
struct popen_data *pd = (struct popen_data *)thisTask->tc_UserData;
|
|
const char *argv[4];
|
|
|
|
argv[0] = "sh";
|
|
argv[1] = "-c";
|
|
argv[2] = pd->command ? pd->command : NULL;
|
|
argv[3] = NULL;
|
|
|
|
// adebug("%s %ld %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
|
|
|
|
/* We need to give this to sh via execvp, execvp expects filename,
|
|
* argv[]
|
|
*/
|
|
IExec->ObtainSemaphore(&popen_sema);
|
|
|
|
IExec->Signal(pd->parent,SIGBREAKF_CTRL_F);
|
|
|
|
popen_result = myexecvp(FALSE, argv[0], (char **)argv);
|
|
if (pd->command)
|
|
IExec->FreeVec(pd->command);
|
|
IExec->FreeVec(pd);
|
|
|
|
IExec->ReleaseSemaphore(&popen_sema);
|
|
IExec->Forbid();
|
|
return 0;
|
|
}
|
|
|
|
|
|
PerlIO *Perl_my_popen(pTHX_ const char *cmd, const char *mode)
|
|
{
|
|
|
|
PERL_FLUSHALL_FOR_CHILD;
|
|
PerlIO *result = NULL;
|
|
char pipe_name[50];
|
|
char unix_pipe[50];
|
|
char ami_pipe[50];
|
|
BPTR input = 0;
|
|
BPTR output = 0;
|
|
struct Process *proc = NULL;
|
|
struct Task *thisTask = IExec->FindTask(0);
|
|
struct popen_data * pd = NULL;
|
|
|
|
/* First we need to check the mode
|
|
* We can only have unidirectional pipes
|
|
*/
|
|
// adebug("%s %ld cmd %s mode %s \n",__FUNCTION__,__LINE__,cmd,
|
|
// mode);
|
|
|
|
switch (mode[0])
|
|
{
|
|
case 'r':
|
|
case 'w':
|
|
break;
|
|
|
|
default:
|
|
|
|
errno = EINVAL;
|
|
return result;
|
|
}
|
|
|
|
/* Make a unique pipe name
|
|
* we need a unix one and an amigaos version (of the same pipe!)
|
|
* as were linking with libunix.
|
|
*/
|
|
|
|
sprintf(pipe_name, "%x%08lx/4096/0", pipenum++,
|
|
IUtility->GetUniqueID());
|
|
sprintf(unix_pipe, "/PIPE/%s", pipe_name);
|
|
sprintf(ami_pipe, "PIPE:%s", pipe_name);
|
|
|
|
/* Now we open the AmigaOs Filehandles That we wil pass to our
|
|
* Sub process
|
|
*/
|
|
|
|
if (mode[0] == 'r')
|
|
{
|
|
/* A read mode pipe: Output from pipe input from Output() or NIL:*/
|
|
/* First attempt to DUP Output() */
|
|
input = IDOS->DupFileHandle(IDOS->Input());
|
|
if(input == 0)
|
|
{
|
|
input = IDOS->Open("NIL:", MODE_READWRITE);
|
|
}
|
|
if (input != 0)
|
|
{
|
|
output = IDOS->Open(ami_pipe, MODE_NEWFILE);
|
|
}
|
|
result = PerlIO_open(unix_pipe, mode);
|
|
}
|
|
else
|
|
{
|
|
/* Open the write end first! */
|
|
|
|
result = PerlIO_open(unix_pipe, mode);
|
|
|
|
input = IDOS->Open(ami_pipe, MODE_OLDFILE);
|
|
if (input != 0)
|
|
{
|
|
output = IDOS->DupFileHandle(IDOS->Output());
|
|
if(output == 0)
|
|
{
|
|
output = IDOS->Open("NIL:", MODE_READWRITE);
|
|
}
|
|
}
|
|
}
|
|
if ((input == 0) || (output == 0) || (result == NULL))
|
|
{
|
|
/* Ouch stream opening failed */
|
|
/* Close and bail */
|
|
if (input)
|
|
IDOS->Close(input);
|
|
if (output)
|
|
IDOS->Close(output);
|
|
if(result)
|
|
{
|
|
PerlIO_close(result);
|
|
result = NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* We have our streams now start our new process
|
|
* We're using a new process so that execve can modify the environment
|
|
* with messing things up for the shell that launched perl
|
|
* Copy cmd before we launch the subprocess as perl seems to waste
|
|
* no time in overwriting it! The subprocess will free the copy.
|
|
*/
|
|
|
|
if((pd = (struct popen_data*)IExec->AllocVecTags(sizeof(struct popen_data),AVT_Type,MEMF_SHARED,TAG_DONE)))
|
|
{
|
|
pd->parent = thisTask;
|
|
if ((pd->command = mystrdup(cmd)))
|
|
{
|
|
// adebug("%s %ld
|
|
// %s\n",__FUNCTION__,__LINE__,cmd_copy?cmd_copy:"NULL");
|
|
proc = IDOS->CreateNewProcTags(
|
|
NP_Entry, popen_child, NP_Child, TRUE, NP_StackSize,
|
|
((struct Process *)thisTask)->pr_StackSize, NP_Input, input,
|
|
NP_Output, output, NP_Error, IDOS->ErrorOutput(),
|
|
NP_CloseError, FALSE, NP_Cli, TRUE, NP_Name,
|
|
"Perl: popen process", NP_UserData, (int)pd,
|
|
TAG_DONE);
|
|
}
|
|
}
|
|
if(proc)
|
|
{
|
|
/* wait for the child be setup right */
|
|
IExec->Wait(SIGBREAKF_CTRL_F);
|
|
}
|
|
if (!proc)
|
|
{
|
|
/* New Process Failed to start
|
|
* Close and bail out
|
|
*/
|
|
if(pd)
|
|
{
|
|
if(pd->command)
|
|
{
|
|
IExec->FreeVec(pd->command);
|
|
}
|
|
IExec->FreeVec(pd);
|
|
}
|
|
if (input)
|
|
IDOS->Close(input);
|
|
if (output)
|
|
IDOS->Close(output);
|
|
if(result)
|
|
{
|
|
PerlIO_close(result);
|
|
result = NULL;
|
|
}
|
|
}
|
|
|
|
/* Our new process is running and will close it streams etc
|
|
* once its done. All we need to is open the pipe via stdio
|
|
*/
|
|
|
|
return result;
|
|
}
|
|
|
|
I32
|
|
Perl_my_pclose(pTHX_ PerlIO *ptr)
|
|
{
|
|
int result = -1;
|
|
/* close the file before obtaining the semaphore else we might end up
|
|
hanging waiting for the child to read the last bit from the pipe */
|
|
PerlIO_close(ptr);
|
|
IExec->ObtainSemaphore(&popen_sema);
|
|
result = popen_result;
|
|
IExec->ReleaseSemaphore(&popen_sema);
|
|
return result;
|
|
}
|
|
|
|
|
|
#ifdef USE_ITHREADS
|
|
|
|
/* An arbitrary number to start with, should work out what the real max should
|
|
* be */
|
|
|
|
#ifndef MAX_THREADS
|
|
# define MAX_THREADS 64
|
|
#endif
|
|
|
|
#define REAPED 0
|
|
#define ACTIVE 1
|
|
#define EXITED -1
|
|
|
|
struct thread_info
|
|
{
|
|
pthread_t ti_pid;
|
|
int ti_children;
|
|
pthread_t ti_parent;
|
|
struct MsgPort *ti_port;
|
|
struct Process *ti_Process;
|
|
};
|
|
|
|
static struct thread_info pseudo_children[MAX_THREADS];
|
|
static int num_pseudo_children = 0;
|
|
static struct SignalSemaphore fork_array_sema;
|
|
|
|
void amigaos4_init_fork_array()
|
|
{
|
|
IExec->InitSemaphore(&fork_array_sema);
|
|
pseudo_children[0].ti_pid = (pthread_t)IExec->FindTask(0);
|
|
pseudo_children[0].ti_parent = -1;
|
|
pseudo_children[0].ti_port =
|
|
(struct MsgPort *)IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE);
|
|
}
|
|
|
|
void amigaos4_dispose_fork_array()
|
|
{
|
|
while (pseudo_children[0].ti_children > 0)
|
|
{
|
|
void *msg;
|
|
IExec->WaitPort(pseudo_children[0].ti_port);
|
|
msg = IExec->GetMsg(pseudo_children[0].ti_port);
|
|
if (msg)
|
|
IExec->FreeSysObject(ASOT_MESSAGE, msg);
|
|
pseudo_children[0].ti_children--;
|
|
}
|
|
IExec->FreeSysObject(ASOT_PORT, pseudo_children[0].ti_port);
|
|
}
|
|
|
|
struct thread_exit_message
|
|
{
|
|
struct Message tem_Message;
|
|
pthread_t tem_pid;
|
|
int tem_status;
|
|
};
|
|
|
|
int getnextchild()
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_THREADS; i++)
|
|
{
|
|
if (pseudo_children[i].ti_pid == 0)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int findparent(pthread_t pid)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAX_THREADS; i++)
|
|
{
|
|
if (pseudo_children[i].ti_pid == pid)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
struct child_arg
|
|
{
|
|
struct Task *ca_parent_task;
|
|
pthread_t ca_parent;
|
|
PerlInterpreter *ca_interp;
|
|
};
|
|
|
|
#undef kill
|
|
|
|
/* FIXME: Is here's a chance, albeit it small of a clash between our pseudo pid */
|
|
/* derived from the pthread API and the dos.library pid that newlib kill uses? */
|
|
/* clib2 used the Process address so there was no issue */
|
|
|
|
int amigaos_kill(Pid_t pid, int signal)
|
|
{
|
|
int i;
|
|
BOOL thistask = FALSE;
|
|
Pid_t realpid = pid; // Perhaps we have a real pid from else where?
|
|
/* Look for our DOS pid */
|
|
IExec->ObtainSemaphore(&fork_array_sema);
|
|
for (i = 0; i < MAX_THREADS; i++)
|
|
{
|
|
if (pseudo_children[i].ti_pid == pid)
|
|
{
|
|
realpid = (Pid_t)IDOS->GetPID(pseudo_children[i].ti_Process,GPID_PROCESS);
|
|
if(pseudo_children[i].ti_Process == (struct Process *)IExec->FindTask(NULL))
|
|
{
|
|
thistask = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
IExec->ReleaseSemaphore(&fork_array_sema);
|
|
/* Allow the C library to work out which signals are realy valid */
|
|
if(thistask)
|
|
{
|
|
/* A quirk in newlib kill handling means it's better to call raise() rather than kill on out own task. */
|
|
return raise(signal);
|
|
}
|
|
else
|
|
{
|
|
return kill(realpid,signal);
|
|
}
|
|
}
|
|
|
|
static THREAD_RET_TYPE amigaos4_start_child(void *arg)
|
|
{
|
|
|
|
PerlInterpreter *my_perl =
|
|
(PerlInterpreter *)((struct child_arg *)arg)->ca_interp;
|
|
;
|
|
|
|
GV *tmpgv;
|
|
int status;
|
|
int parent;
|
|
int nextchild;
|
|
pthread_t pseudo_id = pthread_self();
|
|
|
|
#ifdef PERL_SYNC_FORK
|
|
static long sync_fork_id = 0;
|
|
long id = ++sync_fork_id;
|
|
#endif
|
|
|
|
/* before we do anything set up our process semaphore and add
|
|
a new entry to the pseudochildren */
|
|
|
|
/* get next available slot */
|
|
/* should not fail here! */
|
|
|
|
IExec->ObtainSemaphore(&fork_array_sema);
|
|
|
|
nextchild = getnextchild();
|
|
|
|
pseudo_children[nextchild].ti_pid = pseudo_id;
|
|
pseudo_children[nextchild].ti_Process = (struct Process *)IExec->FindTask(NULL);
|
|
pseudo_children[nextchild].ti_parent =
|
|
((struct child_arg *)arg)->ca_parent;
|
|
pseudo_children[nextchild].ti_port =
|
|
(struct MsgPort *)IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE);
|
|
|
|
num_pseudo_children++;
|
|
IExec->ReleaseSemaphore(&fork_array_sema);
|
|
|
|
/* We're set up let the parent continue */
|
|
|
|
IExec->Signal(((struct child_arg *)arg)->ca_parent_task,
|
|
SIGBREAKF_CTRL_F);
|
|
|
|
PERL_SET_THX(my_perl);
|
|
if ((tmpgv = gv_fetchpv("$", TRUE, SVt_PV)))
|
|
{
|
|
SV *sv = GvSV(tmpgv);
|
|
SvREADONLY_off(sv);
|
|
sv_setiv(sv, (IV)pseudo_id);
|
|
SvREADONLY_on(sv);
|
|
}
|
|
hv_clear(PL_pidstatus);
|
|
|
|
/* push a zero on the stack (we are the child) */
|
|
{
|
|
dSP;
|
|
dTARGET;
|
|
PUSHi(0);
|
|
PUTBACK;
|
|
}
|
|
|
|
/* continue from next op */
|
|
PL_op = PL_op->op_next;
|
|
|
|
{
|
|
dJMPENV;
|
|
volatile int oldscope = PL_scopestack_ix;
|
|
|
|
restart:
|
|
JMPENV_PUSH(status);
|
|
switch (status)
|
|
{
|
|
case 0:
|
|
CALLRUNOPS(aTHX);
|
|
status = 0;
|
|
break;
|
|
case 2:
|
|
while (PL_scopestack_ix > oldscope)
|
|
{
|
|
LEAVE;
|
|
}
|
|
FREETMPS;
|
|
PL_curstash = PL_defstash;
|
|
if (PL_endav && !PL_minus_c)
|
|
call_list(oldscope, PL_endav);
|
|
status = STATUS_EXIT;
|
|
break;
|
|
case 3:
|
|
if (PL_restartop)
|
|
{
|
|
POPSTACK_TO(PL_mainstack);
|
|
PL_op = PL_restartop;
|
|
PL_restartop = (OP *)NULL;
|
|
;
|
|
goto restart;
|
|
}
|
|
PerlIO_printf(Perl_error_log, "panic: restartop\n");
|
|
FREETMPS;
|
|
status = 1;
|
|
break;
|
|
}
|
|
JMPENV_POP;
|
|
|
|
/* XXX hack to avoid perl_destruct() freeing optree */
|
|
PL_main_root = (OP *)NULL;
|
|
}
|
|
|
|
{
|
|
do_close(PL_stdingv, FALSE);
|
|
do_close(gv_fetchpv("STDOUT", TRUE, SVt_PVIO),
|
|
FALSE); /* PL_stdoutgv - ISAGN */
|
|
do_close(PL_stderrgv, FALSE);
|
|
}
|
|
|
|
/* destroy everything (waits for any pseudo-forked children) */
|
|
|
|
/* wait for any remaining children */
|
|
|
|
while (pseudo_children[nextchild].ti_children > 0)
|
|
{
|
|
if (IExec->WaitPort(pseudo_children[nextchild].ti_port))
|
|
{
|
|
void *msg =
|
|
IExec->GetMsg(pseudo_children[nextchild].ti_port);
|
|
IExec->FreeSysObject(ASOT_MESSAGE, msg);
|
|
pseudo_children[nextchild].ti_children--;
|
|
}
|
|
}
|
|
if (PL_scopestack_ix <= 1)
|
|
{
|
|
perl_destruct(my_perl);
|
|
}
|
|
perl_free(my_perl);
|
|
|
|
IExec->ObtainSemaphore(&fork_array_sema);
|
|
parent = findparent(pseudo_children[nextchild].ti_parent);
|
|
pseudo_children[nextchild].ti_pid = 0;
|
|
pseudo_children[nextchild].ti_parent = 0;
|
|
IExec->FreeSysObject(ASOT_PORT, pseudo_children[nextchild].ti_port);
|
|
pseudo_children[nextchild].ti_port = NULL;
|
|
|
|
IExec->ReleaseSemaphore(&fork_array_sema);
|
|
|
|
{
|
|
if (parent >= 0)
|
|
{
|
|
struct thread_exit_message *tem =
|
|
(struct thread_exit_message *)
|
|
IExec->AllocSysObjectTags(
|
|
ASOT_MESSAGE, ASOMSG_Size,
|
|
sizeof(struct thread_exit_message),
|
|
ASOMSG_Length,
|
|
sizeof(struct thread_exit_message));
|
|
if (tem)
|
|
{
|
|
tem->tem_pid = pseudo_id;
|
|
tem->tem_status = status;
|
|
IExec->PutMsg(pseudo_children[parent].ti_port,
|
|
(struct Message *)tem);
|
|
}
|
|
}
|
|
}
|
|
#ifdef PERL_SYNC_FORK
|
|
return id;
|
|
#else
|
|
return (void *)status;
|
|
#endif
|
|
}
|
|
|
|
#endif /* USE_ITHREADS */
|
|
|
|
Pid_t amigaos_fork()
|
|
{
|
|
dTHX;
|
|
pthread_t id;
|
|
int handle;
|
|
struct child_arg arg;
|
|
if (num_pseudo_children >= MAX_THREADS)
|
|
{
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
arg.ca_interp = perl_clone((PerlInterpreter *)aTHX, CLONEf_COPY_STACKS);
|
|
arg.ca_parent_task = IExec->FindTask(NULL);
|
|
arg.ca_parent =
|
|
pthread_self() ? pthread_self() : (pthread_t)IExec->FindTask(0);
|
|
|
|
handle = pthread_create(&id, NULL, amigaos4_start_child, (void *)&arg);
|
|
pseudo_children[findparent(arg.ca_parent)].ti_children++;
|
|
|
|
IExec->Wait(SIGBREAKF_CTRL_F);
|
|
|
|
PERL_SET_THX(aTHX); /* XXX perl_clone*() set TLS */
|
|
if (handle)
|
|
{
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
Pid_t amigaos_waitpid(pTHX_ int optype, Pid_t pid, void *argflags)
|
|
{
|
|
int result;
|
|
if (PL_signals & PERL_SIGNALS_UNSAFE_FLAG)
|
|
{
|
|
result = pthread_join(pid, (void **)argflags);
|
|
}
|
|
else
|
|
{
|
|
while ((result = pthread_join(pid, (void **)argflags)) == -1 &&
|
|
errno == EINTR)
|
|
{
|
|
// PERL_ASYNC_CHECK();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void amigaos_fork_set_userdata(
|
|
pTHX_ struct UserData *userdata, I32 did_pipes, int pp, SV **sp, SV **mark)
|
|
{
|
|
userdata->parent = IExec->FindTask(0);
|
|
userdata->did_pipes = did_pipes;
|
|
userdata->pp = pp;
|
|
userdata->sp = sp;
|
|
userdata->mark = mark;
|
|
userdata->my_perl = aTHX;
|
|
}
|
|
|
|
/* AmigaOS specific versions of #?exec#? solely for use in amigaos_system_child
|
|
*/
|
|
|
|
static void S_exec_failed(pTHX_ const char *cmd, int fd, int do_report)
|
|
{
|
|
const int e = errno;
|
|
// PERL_ARGS_ASSERT_EXEC_FAILED;
|
|
if (e)
|
|
{
|
|
if (ckWARN(WARN_EXEC))
|
|
Perl_warner(aTHX_ packWARN(WARN_EXEC),
|
|
"Can't exec \"%s\": %s", cmd, Strerror(e));
|
|
}
|
|
if (do_report)
|
|
{
|
|
/* XXX silently ignore failures */
|
|
PERL_UNUSED_RESULT(PerlLIO_write(fd, (void *)&e, sizeof(int)));
|
|
PerlLIO_close(fd);
|
|
}
|
|
}
|
|
|
|
static I32 S_do_amigaos_exec3(pTHX_ const char *incmd, int fd, int do_report)
|
|
{
|
|
dVAR;
|
|
const char **argv, **a;
|
|
char *s;
|
|
char *buf;
|
|
char *cmd;
|
|
/* Make a copy so we can change it */
|
|
const Size_t cmdlen = strlen(incmd) + 1;
|
|
I32 result = -1;
|
|
|
|
PERL_ARGS_ASSERT_DO_EXEC3;
|
|
|
|
ENTER;
|
|
Newx(buf, cmdlen, char);
|
|
SAVEFREEPV(buf);
|
|
cmd = buf;
|
|
memcpy(cmd, incmd, cmdlen);
|
|
|
|
while (*cmd && isSPACE(*cmd))
|
|
cmd++;
|
|
|
|
/* see if there are shell metacharacters in it */
|
|
|
|
if (*cmd == '.' && isSPACE(cmd[1]))
|
|
goto doshell;
|
|
|
|
if (strBEGINs(cmd, "exec") && isSPACE(cmd[4]))
|
|
goto doshell;
|
|
|
|
s = cmd;
|
|
while (isWORDCHAR(*s))
|
|
s++; /* catch VAR=val gizmo */
|
|
if (*s == '=')
|
|
goto doshell;
|
|
|
|
for (s = cmd; *s; s++)
|
|
{
|
|
if (*s != ' ' && !isALPHA(*s) &&
|
|
strchr("$&*(){}[]'\";\\|?<>~`\n", *s))
|
|
{
|
|
if (*s == '\n' && !s[1])
|
|
{
|
|
*s = '\0';
|
|
break;
|
|
}
|
|
/* handle the 2>&1 construct at the end */
|
|
if (*s == '>' && s[1] == '&' && s[2] == '1' &&
|
|
s > cmd + 1 && s[-1] == '2' && isSPACE(s[-2]) &&
|
|
(!s[3] || isSPACE(s[3])))
|
|
{
|
|
const char *t = s + 3;
|
|
|
|
while (*t && isSPACE(*t))
|
|
++t;
|
|
if (!*t && (PerlLIO_dup2(1, 2) != -1))
|
|
{
|
|
s[-2] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
doshell:
|
|
PERL_FPU_PRE_EXEC
|
|
result = myexecl(FALSE, PL_sh_path, "sh", "-c", cmd,
|
|
(char *)NULL);
|
|
PERL_FPU_POST_EXEC
|
|
S_exec_failed(aTHX_ PL_sh_path, fd, do_report);
|
|
amigaos_post_exec(fd, do_report);
|
|
goto leave;
|
|
}
|
|
}
|
|
|
|
Newx(argv, (s - cmd) / 2 + 2, const char *);
|
|
SAVEFREEPV(argv);
|
|
cmd = savepvn(cmd, s - cmd);
|
|
SAVEFREEPV(cmd);
|
|
a = argv;
|
|
for (s = cmd; *s;)
|
|
{
|
|
while (isSPACE(*s))
|
|
s++;
|
|
if (*s)
|
|
*(a++) = s;
|
|
while (*s && !isSPACE(*s))
|
|
s++;
|
|
if (*s)
|
|
*s++ = '\0';
|
|
}
|
|
*a = NULL;
|
|
if (argv[0])
|
|
{
|
|
PERL_FPU_PRE_EXEC
|
|
result = myexecvp(FALSE, argv[0], EXEC_ARGV_CAST(argv));
|
|
PERL_FPU_POST_EXEC
|
|
if (errno == ENOEXEC) /* for system V NIH syndrome */
|
|
goto doshell;
|
|
S_exec_failed(aTHX_ argv[0], fd, do_report);
|
|
amigaos_post_exec(fd, do_report);
|
|
}
|
|
leave:
|
|
LEAVE;
|
|
return result;
|
|
}
|
|
|
|
I32 S_do_amigaos_aexec5(
|
|
pTHX_ SV *really, SV **mark, SV **sp, int fd, int do_report)
|
|
{
|
|
dVAR;
|
|
I32 result = -1;
|
|
PERL_ARGS_ASSERT_DO_AEXEC5;
|
|
ENTER;
|
|
if (sp > mark)
|
|
{
|
|
const char **argv, **a;
|
|
const char *tmps = NULL;
|
|
Newx(argv, sp - mark + 1, const char *);
|
|
SAVEFREEPV(argv);
|
|
a = argv;
|
|
|
|
while (++mark <= sp)
|
|
{
|
|
if (*mark) {
|
|
char *arg = savepv(SvPV_nolen_const(*mark));
|
|
SAVEFREEPV(arg);
|
|
*a++ = arg;
|
|
} else
|
|
*a++ = "";
|
|
}
|
|
*a = NULL;
|
|
if (really) {
|
|
tmps = savepv(SvPV_nolen_const(really));
|
|
SAVEFREEPV(tmps);
|
|
}
|
|
if ((!really && *argv[0] != '/') ||
|
|
(really && *tmps != '/')) /* will execvp use PATH? */
|
|
TAINT_ENV(); /* testing IFS here is overkill, probably
|
|
*/
|
|
PERL_FPU_PRE_EXEC
|
|
if (really && *tmps)
|
|
{
|
|
result = myexecvp(FALSE, tmps, EXEC_ARGV_CAST(argv));
|
|
}
|
|
else
|
|
{
|
|
result = myexecvp(FALSE, argv[0], EXEC_ARGV_CAST(argv));
|
|
}
|
|
PERL_FPU_POST_EXEC
|
|
S_exec_failed(aTHX_(really ? tmps : argv[0]), fd, do_report);
|
|
}
|
|
amigaos_post_exec(fd, do_report);
|
|
LEAVE;
|
|
return result;
|
|
}
|
|
|
|
void *amigaos_system_child(void *userdata)
|
|
{
|
|
struct Task *parent;
|
|
I32 did_pipes;
|
|
int pp;
|
|
I32 value;
|
|
STRLEN n_a;
|
|
/* these next are declared by macros else where but I may be
|
|
* passing modified values here so declare them explictly but
|
|
* still referred to by macro below */
|
|
|
|
register SV **sp;
|
|
register SV **mark;
|
|
register PerlInterpreter *my_perl;
|
|
|
|
StdioStore store;
|
|
|
|
struct UserData *ud = (struct UserData *)userdata;
|
|
|
|
did_pipes = ud->did_pipes;
|
|
parent = ud->parent;
|
|
pp = ud->pp;
|
|
SP = ud->sp;
|
|
MARK = ud->mark;
|
|
my_perl = ud->my_perl;
|
|
PERL_SET_THX(my_perl);
|
|
|
|
amigaos_stdio_save(aTHX_ & store);
|
|
|
|
if (did_pipes)
|
|
{
|
|
// PerlLIO_close(pp[0]);
|
|
}
|
|
if (PL_op->op_flags & OPf_STACKED)
|
|
{
|
|
SV *really = *++MARK;
|
|
value = (I32)S_do_amigaos_aexec5(aTHX_ really, MARK, SP, pp,
|
|
did_pipes);
|
|
}
|
|
else if (SP - MARK != 1)
|
|
{
|
|
value = (I32)S_do_amigaos_aexec5(aTHX_ NULL, MARK, SP, pp,
|
|
did_pipes);
|
|
}
|
|
else
|
|
{
|
|
value = (I32)S_do_amigaos_exec3(
|
|
aTHX_ SvPVx(sv_mortalcopy(*SP), n_a), pp, did_pipes);
|
|
}
|
|
|
|
// Forbid();
|
|
// Signal(parent, SIGBREAKF_CTRL_F);
|
|
|
|
amigaos_stdio_restore(aTHX_ & store);
|
|
|
|
return (void *)value;
|
|
}
|
|
|
|
static BOOL contains_whitespace(char *string)
|
|
{
|
|
|
|
if (string)
|
|
{
|
|
|
|
if (strchr(string, ' '))
|
|
return TRUE;
|
|
if (strchr(string, '\t'))
|
|
return TRUE;
|
|
if (strchr(string, '\n'))
|
|
return TRUE;
|
|
if (strchr(string, 0xA0))
|
|
return TRUE;
|
|
if (strchr(string, '"'))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static int no_of_escapes(char *string)
|
|
{
|
|
int cnt = 0;
|
|
char *p;
|
|
for (p = string; p < string + strlen(string); p++)
|
|
{
|
|
if (*p == '"')
|
|
cnt++;
|
|
if (*p == '*')
|
|
cnt++;
|
|
if (*p == '\n')
|
|
cnt++;
|
|
if (*p == '\t')
|
|
cnt++;
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
struct command_data
|
|
{
|
|
STRPTR args;
|
|
BPTR seglist;
|
|
struct Task *parent;
|
|
};
|
|
|
|
#undef fopen
|
|
#undef fgetc
|
|
#undef fgets
|
|
#undef fclose
|
|
|
|
#define __USE_RUNCOMMAND__
|
|
|
|
int myexecve(bool isperlthread,
|
|
const char *filename,
|
|
char *argv[],
|
|
char *envp[])
|
|
{
|
|
FILE *fh;
|
|
char buffer[1000];
|
|
int size = 0;
|
|
char **cur;
|
|
char *interpreter = 0;
|
|
char *interpreter_args = 0;
|
|
char *full = 0;
|
|
char *filename_conv = 0;
|
|
char *interpreter_conv = 0;
|
|
// char *tmp = 0;
|
|
char *fname;
|
|
// int tmpint;
|
|
// struct Task *thisTask = IExec->FindTask(0);
|
|
int result = -1;
|
|
|
|
StdioStore store;
|
|
|
|
pTHX = NULL;
|
|
|
|
if (isperlthread)
|
|
{
|
|
aTHX = PERL_GET_THX;
|
|
/* Save away our stdio */
|
|
amigaos_stdio_save(aTHX_ & store);
|
|
}
|
|
|
|
// adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
|
|
|
|
/* Calculate the size of filename and all args, including spaces and
|
|
* quotes */
|
|
size = 0; // strlen(filename) + 1;
|
|
for (cur = (char **)argv /* +1 */; *cur; cur++)
|
|
{
|
|
size +=
|
|
strlen(*cur) + 1 +
|
|
(contains_whitespace(*cur) ? (2 + no_of_escapes(*cur)) : 0);
|
|
}
|
|
/* Check if it's a script file */
|
|
IExec->DebugPrintF("%s %ld %08lx %c %c\n",__FILE__,__LINE__,filename,filename[0],filename[1]);
|
|
fh = fopen(filename, "r");
|
|
if (fh)
|
|
{
|
|
if (fgetc(fh) == '#' && fgetc(fh) == '!')
|
|
{
|
|
char *p;
|
|
char *q;
|
|
fgets(buffer, 999, fh);
|
|
p = buffer;
|
|
while (*p == ' ' || *p == '\t')
|
|
p++;
|
|
if (buffer[strlen(buffer) - 1] == '\n')
|
|
buffer[strlen(buffer) - 1] = '\0';
|
|
if ((q = strchr(p, ' ')))
|
|
{
|
|
*q++ = '\0';
|
|
if (*q != '\0')
|
|
{
|
|
interpreter_args = mystrdup(q);
|
|
}
|
|
}
|
|
else
|
|
interpreter_args = mystrdup("");
|
|
|
|
interpreter = mystrdup(p);
|
|
size += strlen(interpreter) + 1;
|
|
size += strlen(interpreter_args) + 1;
|
|
}
|
|
|
|
fclose(fh);
|
|
}
|
|
else
|
|
{
|
|
/* We couldn't open this why not? */
|
|
if (errno == ENOENT)
|
|
{
|
|
/* file didn't exist! */
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Allocate the command line */
|
|
filename_conv = convert_path_u2a(filename);
|
|
|
|
if (filename_conv)
|
|
size += strlen(filename_conv);
|
|
size += 1;
|
|
full = (char *)IExec->AllocVecTags(size + 10, AVT_ClearWithValue, 0 ,TAG_DONE);
|
|
if (full)
|
|
{
|
|
if (interpreter)
|
|
{
|
|
interpreter_conv = convert_path_u2a(interpreter);
|
|
#if !defined(__USE_RUNCOMMAND__)
|
|
#warning(using system!)
|
|
sprintf(full, "%s %s %s ", interpreter_conv,
|
|
interpreter_args, filename_conv);
|
|
#else
|
|
sprintf(full, "%s %s ", interpreter_args,
|
|
filename_conv);
|
|
#endif
|
|
IExec->FreeVec(interpreter);
|
|
IExec->FreeVec(interpreter_args);
|
|
|
|
if (filename_conv)
|
|
IExec->FreeVec(filename_conv);
|
|
fname = mystrdup(interpreter_conv);
|
|
|
|
if (interpreter_conv)
|
|
IExec->FreeVec(interpreter_conv);
|
|
}
|
|
else
|
|
{
|
|
#ifndef __USE_RUNCOMMAND__
|
|
sprintf(full, "%s ", filename_conv);
|
|
#else
|
|
sprintf(full, "");
|
|
#endif
|
|
fname = mystrdup(filename_conv);
|
|
if (filename_conv)
|
|
IExec->FreeVec(filename_conv);
|
|
}
|
|
|
|
for (cur = (char **)(argv + 1); *cur != 0; cur++)
|
|
{
|
|
if (contains_whitespace(*cur))
|
|
{
|
|
int esc = no_of_escapes(*cur);
|
|
|
|
if (esc > 0)
|
|
{
|
|
char *buff = (char *)IExec->AllocVecTags(
|
|
strlen(*cur) + 4 + esc,
|
|
AVT_ClearWithValue,0,
|
|
TAG_DONE);
|
|
char *p = *cur;
|
|
char *q = buff;
|
|
|
|
*q++ = '"';
|
|
while (*p != '\0')
|
|
{
|
|
|
|
if (*p == '\n')
|
|
{
|
|
*q++ = '*';
|
|
*q++ = 'N';
|
|
p++;
|
|
continue;
|
|
}
|
|
else if (*p == '"')
|
|
{
|
|
*q++ = '*';
|
|
*q++ = '"';
|
|
p++;
|
|
continue;
|
|
}
|
|
else if (*p == '*')
|
|
{
|
|
*q++ = '*';
|
|
}
|
|
*q++ = *p++;
|
|
}
|
|
*q++ = '"';
|
|
*q++ = ' ';
|
|
*q = '\0';
|
|
strcat(full, buff);
|
|
IExec->FreeVec(buff);
|
|
}
|
|
else
|
|
{
|
|
strcat(full, "\"");
|
|
strcat(full, *cur);
|
|
strcat(full, "\" ");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strcat(full, *cur);
|
|
strcat(full, " ");
|
|
}
|
|
}
|
|
strcat(full, "\n");
|
|
|
|
// if(envp)
|
|
// createvars(envp);
|
|
|
|
#ifndef __USE_RUNCOMMAND__
|
|
result = IDOS->SystemTags(
|
|
full, SYS_UserShell, TRUE, NP_StackSize,
|
|
((struct Process *)thisTask)->pr_StackSize, SYS_Input,
|
|
((struct Process *)thisTask)->pr_CIS, SYS_Output,
|
|
((struct Process *)thisTask)->pr_COS, SYS_Error,
|
|
((struct Process *)thisTask)->pr_CES, TAG_DONE);
|
|
#else
|
|
|
|
if (fname)
|
|
{
|
|
BPTR seglist = IDOS->LoadSeg(fname);
|
|
if (seglist)
|
|
{
|
|
/* check if we have an executable! */
|
|
struct PseudoSegList *ps = NULL;
|
|
if (!IDOS->GetSegListInfoTags(
|
|
seglist, GSLI_Native, &ps, TAG_DONE))
|
|
{
|
|
IDOS->GetSegListInfoTags(
|
|
seglist, GSLI_68KPS, &ps, TAG_DONE);
|
|
}
|
|
if (ps != NULL)
|
|
{
|
|
// adebug("%s %ld %s
|
|
// %s\n",__FUNCTION__,__LINE__,fname,full);
|
|
IDOS->SetCliProgramName(fname);
|
|
// result=RunCommand(seglist,8*1024,full,strlen(full));
|
|
// result=myruncommand(seglist,8*1024,full,strlen(full),envp);
|
|
result = myruncommand(seglist, 8 * 1024,
|
|
full, -1, envp);
|
|
errno = 0;
|
|
}
|
|
else
|
|
{
|
|
errno = ENOEXEC;
|
|
}
|
|
IDOS->UnLoadSeg(seglist);
|
|
}
|
|
else
|
|
{
|
|
errno = ENOEXEC;
|
|
}
|
|
IExec->FreeVec(fname);
|
|
}
|
|
|
|
#endif /* USE_RUNCOMMAND */
|
|
|
|
IExec->FreeVec(full);
|
|
if (errno == ENOEXEC)
|
|
{
|
|
result = -1;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
if (interpreter)
|
|
IExec->FreeVec(interpreter);
|
|
if (filename_conv)
|
|
IExec->FreeVec(filename_conv);
|
|
|
|
errno = ENOMEM;
|
|
|
|
out:
|
|
if (isperlthread)
|
|
{
|
|
amigaos_stdio_restore(aTHX_ & store);
|
|
STATUS_NATIVE_CHILD_SET(result);
|
|
PL_exit_flags |= PERL_EXIT_EXPECTED;
|
|
if (result != -1)
|
|
my_exit(result);
|
|
}
|
|
return (result);
|
|
}
|