This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
correct error returns from fast_abs_path()
[perl5.git] / amigaos4 / amigaio.c
1 /* amigaio.c mixes amigaos and perl APIs,
2  * as opposed to amigaos.c which is pure amigaos */
3
4 #include "EXTERN.h"
5 #include "perl.h"
6
7 #include "amigaos4/amigaio.h"
8 #include "amigaos.h"
9
10 #ifdef WORD
11 #  undef WORD
12 #  define WORD int16
13 #endif
14
15 #include <stdio.h>
16
17 #include <exec/semaphores.h>
18 #include <exec/exectags.h>
19 #include <proto/exec.h>
20 #include <proto/dos.h>
21 #include <proto/utility.h>
22 #include <dos/dos.h>
23
24 extern struct SignalSemaphore popen_sema;
25 extern unsigned int  pipenum;
26
27 extern int32 myruncommand(BPTR seglist, int stack, char *command, int length, char **envp);
28
29 void amigaos_stdio_get(pTHX_ StdioStore *store)
30 {
31         store->astdin =
32             amigaos_get_file(PerlIO_fileno(IoIFP(GvIO(PL_stdingv))));
33         store->astderr =
34             amigaos_get_file(PerlIO_fileno(IoIFP(GvIO(PL_stderrgv))));
35         store->astdout = amigaos_get_file(
36                              PerlIO_fileno(IoIFP(GvIO(gv_fetchpv("STDOUT", TRUE, SVt_PVIO)))));
37 }
38
39 void amigaos_stdio_save(pTHX_ StdioStore *store)
40 {
41         amigaos_stdio_get(aTHX_ store);
42         store->oldstdin = IDOS->SelectInput(store->astdin);
43         store->oldstderr = IDOS->SelectErrorOutput(store->astderr);
44         store->oldstdout = IDOS->SelectOutput(store->astdout);
45 }
46
47 void amigaos_stdio_restore(pTHX_ const StdioStore *store)
48 {
49         IDOS->SelectInput(store->oldstdin);
50         IDOS->SelectErrorOutput(store->oldstderr);
51         IDOS->SelectOutput(store->oldstdout);
52 }
53
54 void amigaos_post_exec(int fd, int do_report)
55 {
56         /* We *must* write something to our pipe or else
57          * the other end hangs */
58         if (do_report)
59         {
60                 int e = errno;
61                 PerlLIO_write(fd, (void *)&e, sizeof(e));
62                 PerlLIO_close(fd);
63         }
64 }
65
66
67 struct popen_data
68 {
69         struct Task *parent;
70         STRPTR command;
71 };
72
73 static int popen_result = 0;
74
75 int popen_child()
76 {
77         struct Task *thisTask = IExec->FindTask(0);
78         struct popen_data *pd = (struct popen_data *)thisTask->tc_UserData;
79         const char *argv[4];
80
81         argv[0] = "sh";
82         argv[1] = "-c";
83         argv[2] = pd->command ? pd->command : NULL;
84         argv[3] = NULL;
85
86         // adebug("%s %ld  %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
87
88         /* We need to give this to sh via execvp, execvp expects filename,
89          * argv[]
90          */
91         IExec->ObtainSemaphore(&popen_sema);
92
93         IExec->Signal(pd->parent,SIGBREAKF_CTRL_F);
94
95         popen_result = myexecvp(FALSE, argv[0], (char **)argv);
96         if (pd->command)
97                 IExec->FreeVec(pd->command);
98         IExec->FreeVec(pd);
99
100         IExec->ReleaseSemaphore(&popen_sema);
101         IExec->Forbid();
102         return 0;
103 }
104
105
106 PerlIO *Perl_my_popen(pTHX_ const char *cmd, const char *mode)
107 {
108
109         PERL_FLUSHALL_FOR_CHILD;
110         PerlIO *result = NULL;
111         char pipe_name[50];
112         char unix_pipe[50];
113         char ami_pipe[50];
114         BPTR input = 0;
115         BPTR output = 0;
116         struct Process *proc = NULL;
117         struct Task *thisTask = IExec->FindTask(0);
118         struct popen_data * pd = NULL;
119
120         /* First we need to check the mode
121          * We can only have unidirectional pipes
122          */
123         //    adebug("%s %ld cmd %s mode %s \n",__FUNCTION__,__LINE__,cmd,
124         //    mode);
125
126         switch (mode[0])
127         {
128         case 'r':
129         case 'w':
130                 break;
131
132         default:
133
134                 errno = EINVAL;
135                 return result;
136         }
137
138         /* Make a unique pipe name
139          * we need a unix one and an amigaos version (of the same pipe!)
140          * as were linking with libunix.
141          */
142
143         sprintf(pipe_name, "%x%08lx/4096/0", pipenum++,
144                 IUtility->GetUniqueID());
145         sprintf(unix_pipe, "/PIPE/%s", pipe_name);
146         sprintf(ami_pipe, "PIPE:%s", pipe_name);
147
148         /* Now we open the AmigaOs Filehandles That we wil pass to our
149          * Sub process
150          */
151
152         if (mode[0] == 'r')
153         {
154                 /* A read mode pipe: Output from pipe input from Output() or NIL:*/
155                 /* First attempt to DUP Output() */
156                 input = IDOS->DupFileHandle(IDOS->Input());
157                 if(input == 0)
158                 {
159                         input = IDOS->Open("NIL:", MODE_READWRITE);
160                 }
161                 if (input != 0)
162                 {
163                         output = IDOS->Open(ami_pipe, MODE_NEWFILE);
164                 }
165                 result = PerlIO_open(unix_pipe, mode);
166         }
167         else
168         {
169                 /* Open the write end first! */
170
171                 result = PerlIO_open(unix_pipe, mode);
172
173                 input = IDOS->Open(ami_pipe, MODE_OLDFILE);
174                 if (input != 0)
175                 {
176                         output = IDOS->DupFileHandle(IDOS->Output());
177                         if(output == 0)
178                         {
179                                 output = IDOS->Open("NIL:", MODE_READWRITE);
180                         }
181                 }
182         }
183         if ((input == 0) || (output == 0) || (result == NULL))
184         {
185                 /* Ouch stream opening failed */
186                 /* Close and bail */
187                 if (input)
188                         IDOS->Close(input);
189                 if (output)
190                         IDOS->Close(output);
191                 if(result)
192                 {
193                         PerlIO_close(result);
194                         result = NULL;
195                 }
196                 return result;
197         }
198
199         /* We have our streams now start our new process
200          * We're using a new process so that execve can modify the environment
201          * with messing things up for the shell that launched perl
202          * Copy cmd before we launch the subprocess as perl seems to waste
203          * no time in overwriting it! The subprocess will free the copy.
204          */
205
206         if((pd = (struct popen_data*)IExec->AllocVecTags(sizeof(struct popen_data),AVT_Type,MEMF_SHARED,TAG_DONE)))
207         {
208                 pd->parent = thisTask;
209                 if ((pd->command  = mystrdup(cmd)))
210                 {
211                         // adebug("%s %ld
212                         // %s\n",__FUNCTION__,__LINE__,cmd_copy?cmd_copy:"NULL");
213                         proc = IDOS->CreateNewProcTags(
214                                    NP_Entry, popen_child, NP_Child, TRUE, NP_StackSize,
215                                    ((struct Process *)thisTask)->pr_StackSize, NP_Input, input,
216                                    NP_Output, output, NP_Error, IDOS->ErrorOutput(),
217                                    NP_CloseError, FALSE, NP_Cli, TRUE, NP_Name,
218                                    "Perl: popen process", NP_UserData, (int)pd,
219                                    TAG_DONE);
220                 }
221         }
222         if(proc)
223         {
224                 /* wait for the child be setup right */
225                 IExec->Wait(SIGBREAKF_CTRL_F);
226         }
227         if (!proc)
228         {
229                 /* New Process Failed to start
230                  * Close and bail out
231                  */
232                 if(pd)
233                 {
234                         if(pd->command)
235                         {
236                                 IExec->FreeVec(pd->command);
237                         }
238                         IExec->FreeVec(pd);
239                 }
240                 if (input)
241                         IDOS->Close(input);
242                 if (output)
243                         IDOS->Close(output);
244                 if(result)
245                 {
246                         PerlIO_close(result);
247                         result = NULL;
248                 }
249         }
250
251         /* Our new process is running and will close it streams etc
252          * once its done. All we need to is open the pipe via stdio
253          */
254
255         return result;
256 }
257
258 I32
259 Perl_my_pclose(pTHX_ PerlIO *ptr)
260 {
261         int result = -1;
262         /* close the file before obtaining the semaphore else we might end up
263            hanging waiting for the child to read the last bit from the pipe */
264         PerlIO_close(ptr);
265         IExec->ObtainSemaphore(&popen_sema);
266         result = popen_result;
267         IExec->ReleaseSemaphore(&popen_sema);
268         return result;
269 }
270
271
272 #ifdef USE_ITHREADS
273
274 /* An arbitrary number to start with, should work out what the real max should
275  * be */
276
277 #ifndef MAX_THREADS
278 #  define MAX_THREADS 64
279 #endif
280
281 #define REAPED 0
282 #define ACTIVE 1
283 #define EXITED -1
284
285 struct thread_info
286 {
287         pthread_t ti_pid;
288         int ti_children;
289         pthread_t ti_parent;
290         struct MsgPort *ti_port;
291         struct Process *ti_Process;
292 };
293
294 static struct thread_info pseudo_children[MAX_THREADS];
295 static int num_pseudo_children = 0;
296 static struct SignalSemaphore fork_array_sema;
297
298 void amigaos4_init_fork_array()
299 {
300         IExec->InitSemaphore(&fork_array_sema);
301         pseudo_children[0].ti_pid = (pthread_t)IExec->FindTask(0);
302         pseudo_children[0].ti_parent = -1;
303         pseudo_children[0].ti_port =
304             (struct MsgPort *)IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE);
305 }
306
307 void amigaos4_dispose_fork_array()
308 {
309         while (pseudo_children[0].ti_children > 0)
310         {
311                 void *msg;
312                 IExec->WaitPort(pseudo_children[0].ti_port);
313                 msg = IExec->GetMsg(pseudo_children[0].ti_port);
314                 if (msg)
315                         IExec->FreeSysObject(ASOT_MESSAGE, msg);
316                 pseudo_children[0].ti_children--;
317         }
318         IExec->FreeSysObject(ASOT_PORT, pseudo_children[0].ti_port);
319 }
320
321 struct thread_exit_message
322 {
323         struct Message tem_Message;
324         pthread_t tem_pid;
325         int tem_status;
326 };
327
328 int getnextchild()
329 {
330         int i;
331         for (i = 0; i < MAX_THREADS; i++)
332         {
333                 if (pseudo_children[i].ti_pid == 0)
334                         return i;
335         }
336         return -1;
337 }
338
339 int findparent(pthread_t pid)
340 {
341         int i;
342         for (i = 0; i < MAX_THREADS; i++)
343         {
344                 if (pseudo_children[i].ti_pid == pid)
345                         return i;
346         }
347         return -1;
348 }
349
350 struct child_arg
351 {
352         struct Task *ca_parent_task;
353         pthread_t ca_parent;
354         PerlInterpreter *ca_interp;
355 };
356
357 #undef kill
358
359 /* FIXME: Is here's a chance, albeit it small of a clash between our pseudo pid */
360 /* derived from the pthread API  and the dos.library pid that newlib kill uses? */
361 /* clib2 used the Process address so there was no issue */
362
363 int amigaos_kill(Pid_t pid, int signal)
364 {
365         int i;
366         BOOL thistask = FALSE;
367         Pid_t realpid = pid; // Perhaps we have a real pid from else where?
368         /* Look for our DOS pid */
369         IExec->ObtainSemaphore(&fork_array_sema);
370         for (i = 0; i < MAX_THREADS; i++)
371         {
372                 if (pseudo_children[i].ti_pid == pid)
373                 {
374                         realpid = (Pid_t)IDOS->GetPID(pseudo_children[i].ti_Process,GPID_PROCESS);
375                         if(pseudo_children[i].ti_Process == (struct Process *)IExec->FindTask(NULL))
376                         {
377                                 thistask = TRUE;
378                         }
379                         break;
380                 }
381         }
382         IExec->ReleaseSemaphore(&fork_array_sema);
383         /* Allow the C library to work out which signals are realy valid */
384         if(thistask)
385         {
386                 /* A quirk in newlib kill handling means it's better to call raise() rather than kill on out own task. */
387                 return raise(signal);
388         }
389         else
390         {
391                 return kill(realpid,signal);
392         }
393 }
394
395 static THREAD_RET_TYPE amigaos4_start_child(void *arg)
396 {
397
398         PerlInterpreter *my_perl =
399             (PerlInterpreter *)((struct child_arg *)arg)->ca_interp;
400         ;
401
402         GV *tmpgv;
403         int status;
404         int parent;
405         int nextchild;
406         pthread_t pseudo_id = pthread_self();
407
408 #ifdef PERL_SYNC_FORK
409         static long sync_fork_id = 0;
410         long id = ++sync_fork_id;
411 #endif
412
413         /* before we do anything set up our process semaphore and add
414            a new entry to the pseudochildren */
415
416         /* get next available slot */
417         /* should not fail here! */
418
419         IExec->ObtainSemaphore(&fork_array_sema);
420
421         nextchild = getnextchild();
422
423         pseudo_children[nextchild].ti_pid = pseudo_id;
424         pseudo_children[nextchild].ti_Process = (struct Process *)IExec->FindTask(NULL);
425         pseudo_children[nextchild].ti_parent =
426             ((struct child_arg *)arg)->ca_parent;
427         pseudo_children[nextchild].ti_port =
428             (struct MsgPort *)IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE);
429
430         num_pseudo_children++;
431         IExec->ReleaseSemaphore(&fork_array_sema);
432
433         /* We're set up let the parent continue */
434
435         IExec->Signal(((struct child_arg *)arg)->ca_parent_task,
436                       SIGBREAKF_CTRL_F);
437
438         PERL_SET_THX(my_perl);
439         if ((tmpgv = gv_fetchpv("$", TRUE, SVt_PV)))
440         {
441                 SV *sv = GvSV(tmpgv);
442                 SvREADONLY_off(sv);
443                 sv_setiv(sv, (IV)pseudo_id);
444                 SvREADONLY_on(sv);
445         }
446         hv_clear(PL_pidstatus);
447
448         /* push a zero on the stack (we are the child) */
449         {
450                 dSP;
451                 dTARGET;
452                 PUSHi(0);
453                 PUTBACK;
454         }
455
456         /* continue from next op */
457         PL_op = PL_op->op_next;
458
459         {
460                 dJMPENV;
461                 volatile int oldscope = PL_scopestack_ix;
462
463 restart:
464                 JMPENV_PUSH(status);
465                 switch (status)
466                 {
467                 case 0:
468                         CALLRUNOPS(aTHX);
469                         status = 0;
470                         break;
471                 case 2:
472                         while (PL_scopestack_ix > oldscope)
473                         {
474                                 LEAVE;
475                         }
476                         FREETMPS;
477                         PL_curstash = PL_defstash;
478                         if (PL_endav && !PL_minus_c)
479                                 call_list(oldscope, PL_endav);
480                         status = STATUS_EXIT;
481                         break;
482                 case 3:
483                         if (PL_restartop)
484                         {
485                                 POPSTACK_TO(PL_mainstack);
486                                 PL_op = PL_restartop;
487                                 PL_restartop = (OP *)NULL;
488                                 ;
489                                 goto restart;
490                         }
491                         PerlIO_printf(Perl_error_log, "panic: restartop\n");
492                         FREETMPS;
493                         status = 1;
494                         break;
495                 }
496                 JMPENV_POP;
497
498                 /* XXX hack to avoid perl_destruct() freeing optree */
499                 PL_main_root = (OP *)NULL;
500         }
501
502         {
503                 do_close(PL_stdingv, FALSE);
504                 do_close(gv_fetchpv("STDOUT", TRUE, SVt_PVIO),
505                          FALSE); /* PL_stdoutgv - ISAGN */
506                 do_close(PL_stderrgv, FALSE);
507         }
508
509         /* destroy everything (waits for any pseudo-forked children) */
510
511         /* wait for any remaining children */
512
513         while (pseudo_children[nextchild].ti_children > 0)
514         {
515                 if (IExec->WaitPort(pseudo_children[nextchild].ti_port))
516                 {
517                         void *msg =
518                             IExec->GetMsg(pseudo_children[nextchild].ti_port);
519                         IExec->FreeSysObject(ASOT_MESSAGE, msg);
520                         pseudo_children[nextchild].ti_children--;
521                 }
522         }
523         if (PL_scopestack_ix <= 1)
524         {
525                 perl_destruct(my_perl);
526         }
527         perl_free(my_perl);
528
529         IExec->ObtainSemaphore(&fork_array_sema);
530         parent = findparent(pseudo_children[nextchild].ti_parent);
531         pseudo_children[nextchild].ti_pid = 0;
532         pseudo_children[nextchild].ti_parent = 0;
533         IExec->FreeSysObject(ASOT_PORT, pseudo_children[nextchild].ti_port);
534         pseudo_children[nextchild].ti_port = NULL;
535
536         IExec->ReleaseSemaphore(&fork_array_sema);
537
538         {
539                 if (parent >= 0)
540                 {
541                         struct thread_exit_message *tem =
542                             (struct thread_exit_message *)
543                             IExec->AllocSysObjectTags(
544                                 ASOT_MESSAGE, ASOMSG_Size,
545                                 sizeof(struct thread_exit_message),
546                                 ASOMSG_Length,
547                                 sizeof(struct thread_exit_message));
548                         if (tem)
549                         {
550                                 tem->tem_pid = pseudo_id;
551                                 tem->tem_status = status;
552                                 IExec->PutMsg(pseudo_children[parent].ti_port,
553                                               (struct Message *)tem);
554                         }
555                 }
556         }
557 #ifdef PERL_SYNC_FORK
558         return id;
559 #else
560         return (void *)status;
561 #endif
562 }
563
564 #endif /* USE_ITHREADS */
565
566 Pid_t amigaos_fork()
567 {
568         dTHX;
569         pthread_t id;
570         int handle;
571         struct child_arg arg;
572         if (num_pseudo_children >= MAX_THREADS)
573         {
574                 errno = EAGAIN;
575                 return -1;
576         }
577         arg.ca_interp = perl_clone((PerlInterpreter *)aTHX, CLONEf_COPY_STACKS);
578         arg.ca_parent_task = IExec->FindTask(NULL);
579         arg.ca_parent =
580             pthread_self() ? pthread_self() : (pthread_t)IExec->FindTask(0);
581
582         handle = pthread_create(&id, NULL, amigaos4_start_child, (void *)&arg);
583         pseudo_children[findparent(arg.ca_parent)].ti_children++;
584
585         IExec->Wait(SIGBREAKF_CTRL_F);
586
587         PERL_SET_THX(aTHX); /* XXX perl_clone*() set TLS */
588         if (handle)
589         {
590                 errno = EAGAIN;
591                 return -1;
592         }
593         return id;
594 }
595
596 Pid_t amigaos_waitpid(pTHX_ int optype, Pid_t pid, void *argflags)
597 {
598         int result;
599         if (PL_signals & PERL_SIGNALS_UNSAFE_FLAG)
600         {
601                 result = pthread_join(pid, (void **)argflags);
602         }
603         else
604         {
605                 while ((result = pthread_join(pid, (void **)argflags)) == -1 &&
606                         errno == EINTR)
607                 {
608                         //          PERL_ASYNC_CHECK();
609                 }
610         }
611         return result;
612 }
613
614 void amigaos_fork_set_userdata(
615     pTHX_ struct UserData *userdata, I32 did_pipes, int pp, SV **sp, SV **mark)
616 {
617         userdata->parent = IExec->FindTask(0);
618         userdata->did_pipes = did_pipes;
619         userdata->pp = pp;
620         userdata->sp = sp;
621         userdata->mark = mark;
622         userdata->my_perl = aTHX;
623 }
624
625 /* AmigaOS specific versions of #?exec#? solely for use in amigaos_system_child
626  */
627
628 static void S_exec_failed(pTHX_ const char *cmd, int fd, int do_report)
629 {
630         const int e = errno;
631 //    PERL_ARGS_ASSERT_EXEC_FAILED;
632         if (e)
633         {
634                 if (ckWARN(WARN_EXEC))
635                         Perl_warner(aTHX_ packWARN(WARN_EXEC),
636                                     "Can't exec \"%s\": %s", cmd, Strerror(e));
637         }
638         if (do_report)
639         {
640                 /* XXX silently ignore failures */
641                 PERL_UNUSED_RESULT(PerlLIO_write(fd, (void *)&e, sizeof(int)));
642                 PerlLIO_close(fd);
643         }
644 }
645
646 static I32 S_do_amigaos_exec3(pTHX_ const char *incmd, int fd, int do_report)
647 {
648         dVAR;
649         const char **argv, **a;
650         char *s;
651         char *buf;
652         char *cmd;
653         /* Make a copy so we can change it */
654         const Size_t cmdlen = strlen(incmd) + 1;
655         I32 result = -1;
656
657         PERL_ARGS_ASSERT_DO_EXEC3;
658
659         ENTER;
660         Newx(buf, cmdlen, char);
661         SAVEFREEPV(buf);
662         cmd = buf;
663         memcpy(cmd, incmd, cmdlen);
664
665         while (*cmd && isSPACE(*cmd))
666                 cmd++;
667
668         /* see if there are shell metacharacters in it */
669
670         if (*cmd == '.' && isSPACE(cmd[1]))
671                 goto doshell;
672
673         if (strBEGINs(cmd, "exec") && isSPACE(cmd[4]))
674                 goto doshell;
675
676         s = cmd;
677         while (isWORDCHAR(*s))
678                 s++; /* catch VAR=val gizmo */
679         if (*s == '=')
680                 goto doshell;
681
682         for (s = cmd; *s; s++)
683         {
684                 if (*s != ' ' && !isALPHA(*s) &&
685                         strchr("$&*(){}[]'\";\\|?<>~`\n", *s))
686                 {
687                         if (*s == '\n' && !s[1])
688                         {
689                                 *s = '\0';
690                                 break;
691                         }
692                         /* handle the 2>&1 construct at the end */
693                         if (*s == '>' && s[1] == '&' && s[2] == '1' &&
694                                 s > cmd + 1 && s[-1] == '2' && isSPACE(s[-2]) &&
695                                 (!s[3] || isSPACE(s[3])))
696                         {
697                                 const char *t = s + 3;
698
699                                 while (*t && isSPACE(*t))
700                                         ++t;
701                                 if (!*t && (PerlLIO_dup2(1, 2) != -1))
702                                 {
703                                         s[-2] = '\0';
704                                         break;
705                                 }
706                         }
707 doshell:
708                         PERL_FPU_PRE_EXEC
709                         result = myexecl(FALSE, PL_sh_path, "sh", "-c", cmd,
710                                          (char *)NULL);
711                         PERL_FPU_POST_EXEC
712                         S_exec_failed(aTHX_ PL_sh_path, fd, do_report);
713                         amigaos_post_exec(fd, do_report);
714                         goto leave;
715                 }
716         }
717
718         Newx(argv, (s - cmd) / 2 + 2, const char *);
719         SAVEFREEPV(argv);
720         cmd = savepvn(cmd, s - cmd);
721         SAVEFREEPV(cmd);
722         a = argv;
723         for (s = cmd; *s;)
724         {
725                 while (isSPACE(*s))
726                         s++;
727                 if (*s)
728                         *(a++) = s;
729                 while (*s && !isSPACE(*s))
730                         s++;
731                 if (*s)
732                         *s++ = '\0';
733         }
734         *a = NULL;
735         if (argv[0])
736         {
737                 PERL_FPU_PRE_EXEC
738                 result = myexecvp(FALSE, argv[0], EXEC_ARGV_CAST(argv));
739                 PERL_FPU_POST_EXEC
740                 if (errno == ENOEXEC) /* for system V NIH syndrome */
741                         goto doshell;
742                 S_exec_failed(aTHX_ argv[0], fd, do_report);
743                 amigaos_post_exec(fd, do_report);
744         }
745 leave:
746         LEAVE;
747         return result;
748 }
749
750 I32 S_do_amigaos_aexec5(
751     pTHX_ SV *really, SV **mark, SV **sp, int fd, int do_report)
752 {
753         dVAR;
754         I32 result = -1;
755         PERL_ARGS_ASSERT_DO_AEXEC5;
756         ENTER;
757         if (sp > mark)
758         {
759                 const char **argv, **a;
760                 const char *tmps = NULL;
761                 Newx(argv, sp - mark + 1, const char *);
762                 SAVEFREEPV(argv);
763                 a = argv;
764
765                 while (++mark <= sp)
766                 {
767                         if (*mark) {
768                                 char *arg = savepv(SvPV_nolen_const(*mark));
769                                 SAVEFREEPV(arg);
770                                 *a++ = arg;
771                         } else
772                                 *a++ = "";
773                 }
774                 *a = NULL;
775                 if (really) {
776                         tmps = savepv(SvPV_nolen_const(really));
777                         SAVEFREEPV(tmps);
778                 }
779                 if ((!really && *argv[0] != '/') ||
780                         (really && *tmps != '/')) /* will execvp use PATH? */
781                         TAINT_ENV(); /* testing IFS here is overkill, probably
782                                         */
783                 PERL_FPU_PRE_EXEC
784                 if (really && *tmps)
785                 {
786                         result = myexecvp(FALSE, tmps, EXEC_ARGV_CAST(argv));
787                 }
788                 else
789                 {
790                         result = myexecvp(FALSE, argv[0], EXEC_ARGV_CAST(argv));
791                 }
792                 PERL_FPU_POST_EXEC
793                 S_exec_failed(aTHX_(really ? tmps : argv[0]), fd, do_report);
794         }
795         amigaos_post_exec(fd, do_report);
796         LEAVE;
797         return result;
798 }
799
800 void *amigaos_system_child(void *userdata)
801 {
802         struct Task *parent;
803         I32 did_pipes;
804         int pp;
805         I32 value;
806         STRLEN n_a;
807         /* these next are declared by macros else where but I may be
808          * passing modified values here so declare them explictly but
809          * still referred to by macro below */
810
811         register SV **sp;
812         register SV **mark;
813         register PerlInterpreter *my_perl;
814
815         StdioStore store;
816
817         struct UserData *ud = (struct UserData *)userdata;
818
819         did_pipes = ud->did_pipes;
820         parent = ud->parent;
821         pp = ud->pp;
822         SP = ud->sp;
823         MARK = ud->mark;
824         my_perl = ud->my_perl;
825         PERL_SET_THX(my_perl);
826
827         amigaos_stdio_save(aTHX_ & store);
828
829         if (did_pipes)
830         {
831                 //    PerlLIO_close(pp[0]);
832         }
833         if (PL_op->op_flags & OPf_STACKED)
834         {
835                 SV *really = *++MARK;
836                 value = (I32)S_do_amigaos_aexec5(aTHX_ really, MARK, SP, pp,
837                                                  did_pipes);
838         }
839         else if (SP - MARK != 1)
840         {
841                 value = (I32)S_do_amigaos_aexec5(aTHX_ NULL, MARK, SP, pp,
842                                                  did_pipes);
843         }
844         else
845         {
846                 value = (I32)S_do_amigaos_exec3(
847                             aTHX_ SvPVx(sv_mortalcopy(*SP), n_a), pp, did_pipes);
848         }
849
850         //    Forbid();
851         //    Signal(parent, SIGBREAKF_CTRL_F);
852
853         amigaos_stdio_restore(aTHX_ & store);
854
855         return (void *)value;
856 }
857
858 static BOOL contains_whitespace(char *string)
859 {
860
861         if (string)
862         {
863
864                 if (strchr(string, ' '))
865                         return TRUE;
866                 if (strchr(string, '\t'))
867                         return TRUE;
868                 if (strchr(string, '\n'))
869                         return TRUE;
870                 if (strchr(string, 0xA0))
871                         return TRUE;
872                 if (strchr(string, '"'))
873                         return TRUE;
874         }
875         return FALSE;
876 }
877
878 static int no_of_escapes(char *string)
879 {
880         int cnt = 0;
881         char *p;
882         for (p = string; p < string + strlen(string); p++)
883         {
884                 if (*p == '"')
885                         cnt++;
886                 if (*p == '*')
887                         cnt++;
888                 if (*p == '\n')
889                         cnt++;
890                 if (*p == '\t')
891                         cnt++;
892         }
893         return cnt;
894 }
895
896 struct command_data
897 {
898         STRPTR args;
899         BPTR seglist;
900         struct Task *parent;
901 };
902
903 #undef fopen
904 #undef fgetc
905 #undef fgets
906 #undef fclose
907
908 #define __USE_RUNCOMMAND__
909
910 int myexecve(bool isperlthread,
911              const char *filename,
912              char *argv[],
913              char *envp[])
914 {
915         FILE *fh;
916         char buffer[1000];
917         int size = 0;
918         char **cur;
919         char *interpreter = 0;
920         char *interpreter_args = 0;
921         char *full = 0;
922         char *filename_conv = 0;
923         char *interpreter_conv = 0;
924         //        char *tmp = 0;
925         char *fname;
926         //        int tmpint;
927         //        struct Task *thisTask = IExec->FindTask(0);
928         int result = -1;
929
930         StdioStore store;
931
932         pTHX = NULL;
933
934         if (isperlthread)
935         {
936                 aTHX = PERL_GET_THX;
937                 /* Save away our stdio */
938                 amigaos_stdio_save(aTHX_ & store);
939         }
940
941         // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
942
943         /* Calculate the size of filename and all args, including spaces and
944          * quotes */
945         size = 0; // strlen(filename) + 1;
946         for (cur = (char **)argv /* +1 */; *cur; cur++)
947         {
948                 size +=
949                     strlen(*cur) + 1 +
950                     (contains_whitespace(*cur) ? (2 + no_of_escapes(*cur)) : 0);
951         }
952         /* Check if it's a script file */
953         IExec->DebugPrintF("%s %ld %08lx %c %c\n",__FILE__,__LINE__,filename,filename[0],filename[1]);
954         fh = fopen(filename, "r");
955         if (fh)
956         {
957                 if (fgetc(fh) == '#' && fgetc(fh) == '!')
958                 {
959                         char *p;
960                         char *q;
961                         fgets(buffer, 999, fh);
962                         p = buffer;
963                         while (*p == ' ' || *p == '\t')
964                                 p++;
965                         if (buffer[strlen(buffer) - 1] == '\n')
966                                 buffer[strlen(buffer) - 1] = '\0';
967                         if ((q = strchr(p, ' ')))
968                         {
969                                 *q++ = '\0';
970                                 if (*q != '\0')
971                                 {
972                                         interpreter_args = mystrdup(q);
973                                 }
974                         }
975                         else
976                                 interpreter_args = mystrdup("");
977
978                         interpreter = mystrdup(p);
979                         size += strlen(interpreter) + 1;
980                         size += strlen(interpreter_args) + 1;
981                 }
982
983                 fclose(fh);
984         }
985         else
986         {
987                 /* We couldn't open this why not? */
988                 if (errno == ENOENT)
989                 {
990                         /* file didn't exist! */
991                         goto out;
992                 }
993         }
994
995         /* Allocate the command line */
996         filename_conv = convert_path_u2a(filename);
997
998         if (filename_conv)
999                 size += strlen(filename_conv);
1000         size += 1;
1001         full = (char *)IExec->AllocVecTags(size + 10, AVT_ClearWithValue, 0 ,TAG_DONE);
1002         if (full)
1003         {
1004                 if (interpreter)
1005                 {
1006                         interpreter_conv = convert_path_u2a(interpreter);
1007 #if !defined(__USE_RUNCOMMAND__)
1008 #warning(using system!)
1009                         sprintf(full, "%s %s %s ", interpreter_conv,
1010                                 interpreter_args, filename_conv);
1011 #else
1012                         sprintf(full, "%s %s ", interpreter_args,
1013                                 filename_conv);
1014 #endif
1015                         IExec->FreeVec(interpreter);
1016                         IExec->FreeVec(interpreter_args);
1017
1018                         if (filename_conv)
1019                                 IExec->FreeVec(filename_conv);
1020                         fname = mystrdup(interpreter_conv);
1021
1022                         if (interpreter_conv)
1023                                 IExec->FreeVec(interpreter_conv);
1024                 }
1025                 else
1026                 {
1027 #ifndef __USE_RUNCOMMAND__
1028                         sprintf(full, "%s ", filename_conv);
1029 #else
1030                         sprintf(full, "");
1031 #endif
1032                         fname = mystrdup(filename_conv);
1033                         if (filename_conv)
1034                                 IExec->FreeVec(filename_conv);
1035                 }
1036
1037                 for (cur = (char **)(argv + 1); *cur != 0; cur++)
1038                 {
1039                         if (contains_whitespace(*cur))
1040                         {
1041                                 int esc = no_of_escapes(*cur);
1042
1043                                 if (esc > 0)
1044                                 {
1045                                         char *buff = (char *)IExec->AllocVecTags(
1046                                                          strlen(*cur) + 4 + esc,
1047                                                          AVT_ClearWithValue,0,
1048                                                          TAG_DONE);
1049                                         char *p = *cur;
1050                                         char *q = buff;
1051
1052                                         *q++ = '"';
1053                                         while (*p != '\0')
1054                                         {
1055
1056                                                 if (*p == '\n')
1057                                                 {
1058                                                         *q++ = '*';
1059                                                         *q++ = 'N';
1060                                                         p++;
1061                                                         continue;
1062                                                 }
1063                                                 else if (*p == '"')
1064                                                 {
1065                                                         *q++ = '*';
1066                                                         *q++ = '"';
1067                                                         p++;
1068                                                         continue;
1069                                                 }
1070                                                 else if (*p == '*')
1071                                                 {
1072                                                         *q++ = '*';
1073                                                 }
1074                                                 *q++ = *p++;
1075                                         }
1076                                         *q++ = '"';
1077                                         *q++ = ' ';
1078                                         *q = '\0';
1079                                         strcat(full, buff);
1080                                         IExec->FreeVec(buff);
1081                                 }
1082                                 else
1083                                 {
1084                                         strcat(full, "\"");
1085                                         strcat(full, *cur);
1086                                         strcat(full, "\" ");
1087                                 }
1088                         }
1089                         else
1090                         {
1091                                 strcat(full, *cur);
1092                                 strcat(full, " ");
1093                         }
1094                 }
1095                 strcat(full, "\n");
1096
1097 //            if(envp)
1098 //                 createvars(envp);
1099
1100 #ifndef __USE_RUNCOMMAND__
1101                 result = IDOS->SystemTags(
1102                              full, SYS_UserShell, TRUE, NP_StackSize,
1103                              ((struct Process *)thisTask)->pr_StackSize, SYS_Input,
1104                              ((struct Process *)thisTask)->pr_CIS, SYS_Output,
1105                              ((struct Process *)thisTask)->pr_COS, SYS_Error,
1106                              ((struct Process *)thisTask)->pr_CES, TAG_DONE);
1107 #else
1108
1109                 if (fname)
1110                 {
1111                         BPTR seglist = IDOS->LoadSeg(fname);
1112                         if (seglist)
1113                         {
1114                                 /* check if we have an executable! */
1115                                 struct PseudoSegList *ps = NULL;
1116                                 if (!IDOS->GetSegListInfoTags(
1117                                             seglist, GSLI_Native, &ps, TAG_DONE))
1118                                 {
1119                                         IDOS->GetSegListInfoTags(
1120                                             seglist, GSLI_68KPS, &ps, TAG_DONE);
1121                                 }
1122                                 if (ps != NULL)
1123                                 {
1124                                         //                    adebug("%s %ld %s
1125                                         //                    %s\n",__FUNCTION__,__LINE__,fname,full);
1126                                         IDOS->SetCliProgramName(fname);
1127                                         //                        result=RunCommand(seglist,8*1024,full,strlen(full));
1128                                         //                        result=myruncommand(seglist,8*1024,full,strlen(full),envp);
1129                                         result = myruncommand(seglist, 8 * 1024,
1130                                                               full, -1, envp);
1131                                         errno = 0;
1132                                 }
1133                                 else
1134                                 {
1135                                         errno = ENOEXEC;
1136                                 }
1137                                 IDOS->UnLoadSeg(seglist);
1138                         }
1139                         else
1140                         {
1141                                 errno = ENOEXEC;
1142                         }
1143                         IExec->FreeVec(fname);
1144                 }
1145
1146 #endif /* USE_RUNCOMMAND */
1147
1148                 IExec->FreeVec(full);
1149                 if (errno == ENOEXEC)
1150                 {
1151                         result = -1;
1152                 }
1153                 goto out;
1154         }
1155
1156         if (interpreter)
1157                 IExec->FreeVec(interpreter);
1158         if (filename_conv)
1159                 IExec->FreeVec(filename_conv);
1160
1161         errno = ENOMEM;
1162
1163 out:
1164         if (isperlthread)
1165         {
1166                 amigaos_stdio_restore(aTHX_ & store);
1167                 STATUS_NATIVE_CHILD_SET(result);
1168                 PL_exit_flags |= PERL_EXIT_EXPECTED;
1169                 if (result != -1)
1170                         my_exit(result);
1171         }
1172         return (result);
1173 }