This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
amigaos4: add amigaos the glue code
[perl5.git] / amigaos4 / amigaos.c
1 /* amigaos.c uses only amigaos APIs,
2  * as opposed to amigaio.c which mixes amigaos and perl APIs */
3
4 #include <string.h>
5
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <assert.h>
9
10 #include <errno.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #if defined(__CLIB2__)
14 #  include <dos.h>
15 #endif
16 #if defined(__NEWLIB__)
17 #  include <amiga_platform.h>
18 #endif
19 #include <fcntl.h>
20 #include <ctype.h>
21 #include <stdarg.h>
22
23 #include <dos/dos.h>
24 #include <proto/dos.h>
25 #include <proto/exec.h>
26 #include <proto/utility.h>
27
28 #include "amigaos.h"
29
30 struct UtilityIFace *IUtility = NULL;
31
32 /***************************************************************************/
33
34 struct Interface *OpenInterface(CONST_STRPTR libname, uint32 libver)
35 {
36         struct Library *base = IExec->OpenLibrary(libname, libver);
37         struct Interface *iface = IExec->GetInterface(base, "main", 1, NULL);
38         if (iface == NULL)
39         {
40                 // We should probably post some kind of error message here.
41
42                 IExec->CloseLibrary(base);
43         }
44
45         return iface;
46 }
47
48 /***************************************************************************/
49
50 void CloseInterface(struct Interface *iface)
51 {
52         if (iface != NULL)
53         {
54                 struct Library *base = iface->Data.LibBase;
55                 IExec->DropInterface(iface);
56                 IExec->CloseLibrary(base);
57         }
58 }
59
60 BOOL __unlink_retries = FALSE;
61
62 void ___makeenviron() __attribute__((constructor));
63 void ___freeenviron() __attribute__((destructor));
64
65 void ___openinterfaces() __attribute__((constructor));
66 void ___closeinterfaces() __attribute__((destructor));
67
68 void ___openinterfaces()
69 {
70         if (!IDOS)
71                 IDOS = (struct DOSIFace *)OpenInterface("dos.library", 53);
72         if (!IUtility)
73                 IUtility =
74                     (struct UtilityIFace *)OpenInterface("utility.library", 53);
75 }
76
77 void ___closeinterfaces()
78 {
79         CloseInterface((struct Interface *)IDOS);
80         CloseInterface((struct Interface *)IUtility);
81 }
82 int VARARGS68K araddebug(UBYTE *fmt, ...);
83 int VARARGS68K adebug(UBYTE *fmt, ...);
84
85 #define __USE_RUNCOMMAND__
86
87 char **myenviron = NULL;
88 char **origenviron = NULL;
89
90 int myexecve(const char *path, char *argv[], char *envp[]);
91 static void createvars(char **envp);
92
93 struct args
94 {
95         BPTR seglist;
96         int stack;
97         char *command;
98         int length;
99         int result;
100         char **envp;
101 };
102
103 int __myrc(char *arg)
104 {
105         struct Task *thisTask = IExec->FindTask(0);
106         struct args *myargs = (struct args *)thisTask->tc_UserData;
107         if (myargs->envp)
108                 createvars(myargs->envp);
109         // adebug("%s %ld %s \n",__FUNCTION__,__LINE__,myargs->command);
110         myargs->result = IDOS->RunCommand(myargs->seglist, myargs->stack,
111                                           myargs->command, myargs->length);
112         return 0;
113 }
114
115 int32 myruncommand(
116     BPTR seglist, int stack, char *command, int length, char **envp)
117 {
118         struct args myargs;
119         struct Task *thisTask = IExec->FindTask(0);
120         struct Process *proc;
121
122         // adebug("%s %ld  %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
123
124         myargs.seglist = seglist;
125         myargs.stack = stack;
126         myargs.command = command;
127         myargs.length = length;
128         myargs.result = -1;
129         myargs.envp = envp;
130
131         if ((proc = IDOS->CreateNewProcTags(
132                  NP_Entry, __myrc, NP_Child, TRUE, NP_Input, IDOS->Input(),
133                  NP_Output, IDOS->Output(), NP_Error, IDOS->ErrorOutput(),
134                  NP_CloseInput, FALSE, NP_CloseOutput, FALSE, NP_CloseError,
135                  FALSE, NP_CopyVars, FALSE,
136
137                  //           NP_StackSize,           ((struct Process
138                  //           *)myargs.parent)->pr_StackSize,
139                  NP_Cli, TRUE, NP_UserData, (int)&myargs,
140                  NP_NotifyOnDeathSigTask, thisTask, TAG_DONE)))
141
142         {
143                 IExec->Wait(SIGF_CHILD);
144         }
145         return myargs.result;
146 }
147
148 static char *mystrdup(const char *s)
149 {
150         char *result = NULL;
151         size_t size;
152
153         size = strlen(s) + 1;
154
155         if ((result = (char *)IExec->AllocVec(size, MEMF_ANY)))
156         {
157                 memmove(result, s, size);
158         }
159         return result;
160 }
161
162 static int pipenum = 0;
163
164 int pipe(int filedes[2])
165 {
166         char pipe_name[1024];
167
168 //   adebug("%s %ld \n",__FUNCTION__,__LINE__);
169 #ifdef USE_TEMPFILES
170         sprintf(pipe_name, "/T/%x.%08lx", pipenum++, IUtility->GetUniqueID());
171 #else
172         sprintf(pipe_name, "/PIPE/%x%08lx/4096/0", pipenum++,
173                 IUtility->GetUniqueID());
174 #endif
175
176         /*      printf("pipe: %s \n", pipe_name);*/
177
178         filedes[1] = open(pipe_name, O_WRONLY | O_CREAT);
179         filedes[0] = open(pipe_name, O_RDONLY);
180         if (filedes[0] == -1 || filedes[1] == -1)
181         {
182                 if (filedes[0] != -1)
183                         close(filedes[0]);
184                 if (filedes[1] != -1)
185                         close(filedes[1]);
186                 return -1;
187         }
188         /*      printf("filedes %d %d\n", filedes[0],
189          * filedes[1]);fflush(stdout);*/
190
191         return 0;
192 }
193
194 int fork(void)
195 {
196         fprintf(stderr, "Can not bloody fork\n");
197         errno = ENOMEM;
198         return -1;
199 }
200
201 int wait(int *status)
202 {
203         fprintf(stderr, "No wait try waitpid instead\n");
204         errno = ECHILD;
205         return -1;
206 }
207
208 char *convert_path_a2u(const char *filename)
209 {
210         struct NameTranslationInfo nti;
211
212         if (!filename)
213         {
214                 return 0;
215         }
216
217         __translate_amiga_to_unix_path_name(&filename, &nti);
218
219         return mystrdup(filename);
220 }
221 char *convert_path_u2a(const char *filename)
222 {
223         struct NameTranslationInfo nti;
224
225         if (!filename)
226         {
227                 return 0;
228         }
229
230         if (strcmp(filename, "/dev/tty") == 0)
231         {
232                 return mystrdup("CONSOLE:");
233                 ;
234         }
235
236         __translate_unix_to_amiga_path_name(&filename, &nti);
237
238         return mystrdup(filename);
239 }
240
241 static struct SignalSemaphore environ_sema;
242
243 void amigaos4_init_environ_sema() { IExec->InitSemaphore(&environ_sema); }
244
245 void amigaos4_obtain_environ() { IExec->ObtainSemaphore(&environ_sema); }
246
247 void amigaos4_release_environ() { IExec->ReleaseSemaphore(&environ_sema); }
248
249 static void createvars(char **envp)
250 {
251         if (envp)
252         {
253                 /* Set a local var to indicate to any subsequent sh that it is
254                 * not
255                 * the top level shell and so should only inherit local amigaos
256                 * vars */
257                 IDOS->SetVar("ABCSH_IMPORT_LOCAL", "TRUE", 5, GVF_LOCAL_ONLY);
258
259                 amigaos4_obtain_environ();
260
261                 envp = myenviron;
262
263                 while ((envp != NULL) && (*envp != NULL))
264                 {
265                         int len;
266                         char *var;
267                         char *val;
268                         if ((len = strlen(*envp)))
269                         {
270                                 if ((var = (char *)IExec->AllocVec(
271                                          len + 1, MEMF_ANY | MEMF_CLEAR)))
272                                 {
273                                         strcpy(var, *envp);
274
275                                         val = strchr(var, '=');
276                                         if (val)
277                                         {
278                                                 *val++ = '\0';
279                                                 if (*val)
280                                                 {
281                                                         IDOS->SetVar(
282                                                             var, val,
283                                                             strlen(val) + 1,
284                                                             GVF_LOCAL_ONLY);
285                                                 }
286                                         }
287                                         IExec->FreeVec(var);
288                                 }
289                         }
290                         envp++;
291                 }
292                 amigaos4_release_environ();
293         }
294 }
295
296 static BOOL contains_whitespace(char *string)
297 {
298
299         if (string)
300         {
301
302                 if (strchr(string, ' '))
303                         return TRUE;
304                 if (strchr(string, '\t'))
305                         return TRUE;
306                 if (strchr(string, '\n'))
307                         return TRUE;
308                 if (strchr(string, 0xA0))
309                         return TRUE;
310                 if (strchr(string, '"'))
311                         return TRUE;
312         }
313         return FALSE;
314 }
315
316 static int no_of_escapes(char *string)
317 {
318         int cnt = 0;
319         char *p;
320         for (p = string; p < string + strlen(string); p++)
321         {
322                 if (*p == '"')
323                         cnt++;
324                 if (*p == '*')
325                         cnt++;
326                 if (*p == '\n')
327                         cnt++;
328                 if (*p == '\t')
329                         cnt++;
330         }
331         return cnt;
332 }
333
334 struct command_data
335 {
336         STRPTR args;
337         BPTR seglist;
338         struct Task *parent;
339 };
340
341 int myexecvp(const char *filename, char *argv[])
342 {
343         //      adebug("%s %ld
344         //%s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
345         /* if there's a slash or a colon consider filename a path and skip
346          * search */
347         int res;
348         if ((strchr(filename, '/') == NULL) && (strchr(filename, ':') == NULL))
349         {
350                 char *path;
351                 char *name;
352                 char *pathpart;
353                 char *p;
354                 size_t len;
355                 struct stat st;
356
357                 if (!(path = getenv("PATH")))
358                 {
359                         path = ".:/bin:/usr/bin:/c";
360                 }
361
362                 len = strlen(filename) + 1;
363                 name = (char *)alloca(strlen(path) + len);
364                 pathpart = (char *)alloca(strlen(path) + 1);
365                 p = path;
366                 do
367                 {
368                         path = p;
369
370                         if (!(p = strchr(path, ':')))
371                         {
372                                 p = strchr(path, '\0');
373                         }
374
375                         memcpy(pathpart, path, p - path);
376                         pathpart[p - path] = '\0';
377                         if (!(strlen(pathpart) == 0))
378                         {
379                                 sprintf(name, "%s/%s", pathpart, filename);
380                         }
381                         else
382                                 sprintf(name, "%s", filename);
383
384                         if ((stat(name, &st) == 0) && (S_ISREG(st.st_mode)))
385                         {
386                                 /* we stated it and it's a regular file */
387                                 /* let's boogie! */
388                                 filename = name;
389                                 break;
390                         }
391
392                 } while (*p++ != '\0');
393         }
394         res = myexecve(filename, argv, myenviron);
395         return res;
396 }
397
398 int myexecv(const char *path, char *argv[])
399 {
400         return myexecve(path, argv, myenviron);
401 }
402
403 int myexecl(const char *path, ...)
404 {
405         va_list va;
406         char *argv[1024]; /* 1024 enough? let's hope so! */
407         int i = 0;
408         // adebug("%s %ld\n",__FUNCTION__,__LINE__);
409
410         va_start(va, path);
411         i = 0;
412
413         do
414         {
415                 argv[i] = va_arg(va, char *);
416         } while (argv[i++] != NULL);
417
418         va_end(va);
419         return myexecve(path, argv, myenviron);
420 }
421
422 int myexecve(const char *filename, char *argv[], char *envp[])
423 {
424         FILE *fh;
425         char buffer[1000];
426         int size = 0;
427         char **cur;
428         char *interpreter = 0;
429         char *interpreter_args = 0;
430         char *full = 0;
431         char *filename_conv = 0;
432         char *interpreter_conv = 0;
433         //        char *tmp = 0;
434         char *fname;
435         //        int tmpint;
436         //        struct Task *thisTask = IExec->FindTask(0);
437         int result = -1;
438
439         // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
440
441         /* Calculate the size of filename and all args, including spaces and
442          * quotes */
443         size = 0; // strlen(filename) + 1;
444         for (cur = (char **)argv /* +1 */; *cur; cur++)
445         {
446                 size +=
447                     strlen(*cur) + 1 +
448                     (contains_whitespace(*cur) ? (2 + no_of_escapes(*cur)) : 0);
449         }
450         /* Check if it's a script file */
451
452         fh = fopen(filename, "r");
453         if (fh)
454         {
455                 if (fgetc(fh) == '#' && fgetc(fh) == '!')
456                 {
457                         char *p;
458                         char *q;
459                         fgets(buffer, 999, fh);
460                         p = buffer;
461                         while (*p == ' ' || *p == '\t')
462                                 p++;
463                         if (buffer[strlen(buffer) - 1] == '\n')
464                                 buffer[strlen(buffer) - 1] = '\0';
465                         if ((q = strchr(p, ' ')))
466                         {
467                                 *q++ = '\0';
468                                 if (*q != '\0')
469                                 {
470                                         interpreter_args = mystrdup(q);
471                                 }
472                         }
473                         else
474                                 interpreter_args = mystrdup("");
475
476                         interpreter = mystrdup(p);
477                         size += strlen(interpreter) + 1;
478                         size += strlen(interpreter_args) + 1;
479                 }
480
481                 fclose(fh);
482         }
483         else
484         {
485                 /* We couldn't open this why not? */
486                 if (errno == ENOENT)
487                 {
488                         /* file didn't exist! */
489                         return -1;
490                 }
491         }
492
493         /* Allocate the command line */
494         filename_conv = convert_path_u2a(filename);
495
496         if (filename_conv)
497                 size += strlen(filename_conv);
498         size += 1;
499         full = (char *)IExec->AllocVec(size + 10, MEMF_ANY | MEMF_CLEAR);
500         if (full)
501         {
502                 if (interpreter)
503                 {
504                         interpreter_conv = convert_path_u2a(interpreter);
505 #if !defined(__USE_RUNCOMMAND__)
506 #warning(using system!)
507                         sprintf(full, "%s %s %s ", interpreter_conv,
508                                 interpreter_args, filename_conv);
509 #else
510                         sprintf(full, "%s %s ", interpreter_args,
511                                 filename_conv);
512 #endif
513                         IExec->FreeVec(interpreter);
514                         IExec->FreeVec(interpreter_args);
515
516                         if (filename_conv)
517                                 IExec->FreeVec(filename_conv);
518                         fname = mystrdup(interpreter_conv);
519
520                         if (interpreter_conv)
521                                 IExec->FreeVec(interpreter_conv);
522                 }
523                 else
524                 {
525 #ifndef __USE_RUNCOMMAND__
526                         sprintf(full, "%s ", filename_conv);
527 #else
528                         sprintf(full, "");
529 #endif
530                         fname = mystrdup(filename_conv);
531                         if (filename_conv)
532                                 IExec->FreeVec(filename_conv);
533                 }
534
535                 for (cur = (char **)(argv + 1); *cur != 0; cur++)
536                 {
537                         if (contains_whitespace(*cur))
538                         {
539                                 int esc = no_of_escapes(*cur);
540
541                                 if (esc > 0)
542                                 {
543                                         char *buff = IExec->AllocVec(
544                                             strlen(*cur) + 4 + esc,
545                                             MEMF_ANY | MEMF_CLEAR);
546                                         char *p = *cur;
547                                         char *q = buff;
548
549                                         *q++ = '"';
550                                         while (*p != '\0')
551                                         {
552
553                                                 if (*p == '\n')
554                                                 {
555                                                         *q++ = '*';
556                                                         *q++ = 'N';
557                                                         p++;
558                                                         continue;
559                                                 }
560                                                 else if (*p == '"')
561                                                 {
562                                                         *q++ = '*';
563                                                         *q++ = '"';
564                                                         p++;
565                                                         continue;
566                                                 }
567                                                 else if (*p == '*')
568                                                 {
569                                                         *q++ = '*';
570                                                 }
571                                                 *q++ = *p++;
572                                         }
573                                         *q++ = '"';
574                                         *q++ = ' ';
575                                         *q = '\0';
576                                         strcat(full, buff);
577                                         IExec->FreeVec(buff);
578                                 }
579                                 else
580                                 {
581                                         strcat(full, "\"");
582                                         strcat(full, *cur);
583                                         strcat(full, "\" ");
584                                 }
585                         }
586                         else
587                         {
588                                 strcat(full, *cur);
589                                 strcat(full, " ");
590                         }
591                 }
592                 strcat(full, "\n");
593
594 //            if(envp)
595 //                 createvars(envp);
596
597 #ifndef __USE_RUNCOMMAND__
598                 result = IDOS->SystemTags(
599                     full, SYS_UserShell, TRUE, NP_StackSize,
600                     ((struct Process *)thisTask)->pr_StackSize, SYS_Input,
601                     ((struct Process *)thisTask)->pr_CIS, SYS_Output,
602                     ((struct Process *)thisTask)->pr_COS, SYS_Error,
603                     ((struct Process *)thisTask)->pr_CES, TAG_DONE);
604 #else
605
606                 if (fname)
607                 {
608                         BPTR seglist = IDOS->LoadSeg(fname);
609                         if (seglist)
610                         {
611                                 /* check if we have an executable! */
612                                 struct PseudoSegList *ps = NULL;
613                                 if (!IDOS->GetSegListInfoTags(
614                                         seglist, GSLI_Native, &ps, TAG_DONE))
615                                 {
616                                         IDOS->GetSegListInfoTags(
617                                             seglist, GSLI_68KPS, &ps, TAG_DONE);
618                                 }
619                                 if (ps != NULL)
620                                 {
621                                         //                    adebug("%s %ld %s
622                                         //                    %s\n",__FUNCTION__,__LINE__,fname,full);
623                                         IDOS->SetCliProgramName(fname);
624                                         //                        result=RunCommand(seglist,8*1024,full,strlen(full));
625                                         //                        result=myruncommand(seglist,8*1024,full,strlen(full),envp);
626                                         result = myruncommand(seglist, 8 * 1024,
627                                                               full, -1, envp);
628                                         errno = 0;
629                                 }
630                                 else
631                                 {
632                                         errno = ENOEXEC;
633                                 }
634                                 IDOS->UnLoadSeg(seglist);
635                         }
636                         else
637                         {
638                                 errno = ENOEXEC;
639                         }
640                         IExec->FreeVec(fname);
641                 }
642
643 #endif /* USE_RUNCOMMAND */
644
645                 IExec->FreeVec(full);
646                 if (errno == ENOEXEC)
647                         return -1;
648                 return result;
649         }
650
651         if (interpreter)
652                 IExec->FreeVec(interpreter);
653         if (filename_conv)
654                 IExec->FreeVec(filename_conv);
655
656         errno = ENOMEM;
657
658         return -1;
659 }
660
661 int pause(void)
662 {
663         fprintf(stderr, "Pause not implemented\n");
664
665         errno = EINTR;
666         return -1;
667 }
668
669 uint32 size_env(struct Hook *hook, APTR userdata, struct ScanVarsMsg *message)
670 {
671         if (strlen(message->sv_GDir) <= 4)
672         {
673                 hook->h_Data = (APTR)(((uint32)hook->h_Data) + 1);
674         }
675         return 0;
676 }
677
678 uint32 copy_env(struct Hook *hook, APTR userdata, struct ScanVarsMsg *message)
679 {
680         if (strlen(message->sv_GDir) <= 4)
681         {
682                 char **env = (char **)hook->h_Data;
683                 uint32 size =
684                     strlen(message->sv_Name) + 1 + message->sv_VarLen + 1 + 1;
685                 char *buffer = (char *)IExec->AllocVec((uint32)size,
686                                                        MEMF_ANY | MEMF_CLEAR);
687
688                 snprintf(buffer, size - 1, "%s=%s", message->sv_Name,
689                          message->sv_Var);
690
691                 *env = buffer;
692                 env++;
693                 hook->h_Data = env;
694         }
695         return 0;
696 }
697
698 void ___makeenviron()
699 {
700         struct Hook hook;
701
702         char varbuf[8];
703         uint32 flags = 0;
704
705         struct DOSIFace *myIDOS =
706             (struct DOSIFace *)OpenInterface("dos.library", 53);
707         if (myIDOS)
708         {
709                 if (myIDOS->GetVar("ABCSH_IMPORT_LOCAL", varbuf, 8,
710                                    GVF_LOCAL_ONLY) > 0)
711                 {
712                         flags = GVF_LOCAL_ONLY;
713                 }
714                 else
715                 {
716                         flags = GVF_GLOBAL_ONLY;
717                 }
718
719                 hook.h_Entry = size_env;
720                 hook.h_Data = 0;
721
722                 myIDOS->ScanVars(&hook, flags, 0);
723                 hook.h_Data = (APTR)(((uint32)hook.h_Data) + 1);
724
725                 myenviron = (char **)IExec->AllocVec((uint32)hook.h_Data *
726                                                          sizeof(char **),
727                                                      MEMF_ANY | MEMF_CLEAR);
728                 origenviron = myenviron;
729                 if (!myenviron)
730                 {
731                         return;
732                 }
733                 hook.h_Entry = copy_env;
734                 hook.h_Data = myenviron;
735
736                 myIDOS->ScanVars(&hook, flags, 0);
737                 CloseInterface((struct Interface *)myIDOS);
738         }
739 }
740
741 void ___freeenviron()
742 {
743         char **i;
744         /* perl might change environ, it puts it back except for ctrl-c */
745         /* so restore our own copy here */
746         struct DOSIFace *myIDOS =
747             (struct DOSIFace *)OpenInterface("dos.library", 53);
748         if (myIDOS)
749         {
750                 myenviron = origenviron;
751
752                 if (myenviron)
753                 {
754                         for (i = myenviron; *i != NULL; i++)
755                         {
756                                 IExec->FreeVec(*i);
757                         }
758                         IExec->FreeVec(myenviron);
759                         myenviron = NULL;
760                 }
761                 CloseInterface((struct Interface *)myIDOS);
762         }
763 }
764
765 /* reimplementation of popen, clib2's doesn't do all we want */
766
767 static BOOL is_final_quote_character(const char *str)
768 {
769         BOOL result;
770
771         result = (BOOL)(str[0] == '\"' && (str[1] == '\0' || isspace(str[1])));
772
773         return (result);
774 }
775
776 static BOOL is_final_squote_character(const char *str)
777 {
778         BOOL result;
779
780         result = (BOOL)(str[0] == '\'' && (str[1] == '\0' || isspace(str[1])));
781
782         return (result);
783 }
784
785 int popen_child()
786 {
787         struct Task *thisTask = IExec->FindTask(0);
788
789         char *command = (char *)thisTask->tc_UserData;
790         size_t len;
791         char *str;
792         int argc;
793         int number_of_arguments;
794         char *argv[4];
795
796         argv[0] = "sh";
797         argv[1] = "-c";
798         argv[2] = command ? command : NULL;
799         argv[3] = NULL;
800
801         // adebug("%s %ld  %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
802
803         /* We need to give this to sh via execvp, execvp expects filename,
804          * argv[]
805          */
806
807         myexecvp(argv[0], argv);
808         if (command)
809                 IExec->FreeVec(command);
810
811         IExec->Forbid();
812         return 0;
813 }
814
815 FILE *amigaos_popen(const char *cmd, const char *mode)
816 {
817         FILE *result = NULL;
818         char pipe_name[50];
819         char unix_pipe[50];
820         char ami_pipe[50];
821         char *cmd_copy;
822         BPTR input = 0;
823         BPTR output = 0;
824         struct Process *proc = NULL;
825         struct Task *thisTask = IExec->FindTask(0);
826
827         /* First we need to check the mode
828          * We can only have unidirectional pipes
829          */
830         //    adebug("%s %ld cmd %s mode %s \n",__FUNCTION__,__LINE__,cmd,
831         //    mode);
832
833         switch (mode[0])
834         {
835         case 'r':
836         case 'w':
837                 break;
838
839         default:
840
841                 errno = EINVAL;
842                 return result;
843         }
844
845         /* Make a unique pipe name
846          * we need a unix one and an amigaos version (of the same pipe!)
847          * as were linking with libunix.
848          */
849
850         sprintf(pipe_name, "%x%08lx/4096/0", pipenum++,
851                 IUtility->GetUniqueID());
852         sprintf(unix_pipe, "/PIPE/%s", pipe_name);
853         sprintf(ami_pipe, "PIPE:%s", pipe_name);
854
855         /* Now we open the AmigaOs Filehandles That we wil pass to our
856          * Sub process
857          */
858
859         if (mode[0] == 'r')
860         {
861                 /* A read mode pipe: Output from pipe input from NIL:*/
862                 input = IDOS->Open("NIL:", MODE_NEWFILE);
863                 if (input != 0)
864                 {
865                         output = IDOS->Open(ami_pipe, MODE_NEWFILE);
866                 }
867         }
868         else
869         {
870
871                 input = IDOS->Open(ami_pipe, MODE_NEWFILE);
872                 if (input != 0)
873                 {
874                         output = IDOS->Open("NIL:", MODE_NEWFILE);
875                 }
876         }
877         if ((input == 0) || (output == 0))
878         {
879                 /* Ouch stream opening failed */
880                 /* Close and bail */
881                 if (input)
882                         IDOS->Close(input);
883                 if (output)
884                         IDOS->Close(output);
885                 return result;
886         }
887
888         /* We have our streams now start our new process
889          * We're using a new process so that execve can modify the environment
890          * with messing things up for the shell that launched perl
891          * Copy cmd before we launch the subprocess as perl seems to waste
892          * no time in overwriting it! The subprocess will free the copy.
893          */
894
895         if ((cmd_copy = mystrdup(cmd)))
896         {
897                 // adebug("%s %ld
898                 // %s\n",__FUNCTION__,__LINE__,cmd_copy?cmd_copy:"NULL");
899                 proc = IDOS->CreateNewProcTags(
900                     NP_Entry, popen_child, NP_Child, TRUE, NP_StackSize,
901                     ((struct Process *)thisTask)->pr_StackSize, NP_Input, input,
902                     NP_Output, output, NP_Error, IDOS->ErrorOutput(),
903                     NP_CloseError, FALSE, NP_Cli, TRUE, NP_Name,
904                     "Perl: popen process", NP_UserData, (int)cmd_copy,
905                     TAG_DONE);
906         }
907         if (!proc)
908         {
909                 /* New Process Failed to start
910                  * Close and bail out
911                  */
912                 if (input)
913                         IDOS->Close(input);
914                 if (output)
915                         IDOS->Close(output);
916                 if (cmd_copy)
917                         IExec->FreeVec(cmd_copy);
918         }
919
920         /* Our new process is running and will close it streams etc
921          * once its done. All we need to is open the pipe via stdio
922          */
923
924         return fopen(unix_pipe, mode);
925 }
926
927 /* Work arround for clib2 fstat */
928 #ifndef S_IFCHR
929 #define S_IFCHR 0x0020000
930 #endif
931
932 #define SET_FLAG(u, v) ((void)((u) |= (v)))
933
934 int afstat(int fd, struct stat *statb)
935 {
936         int result;
937         BPTR fh;
938         int mode;
939         BOOL input;
940         /* In the first instance pass it to fstat */
941         // adebug("fd %ld ad %ld\n",fd,amigaos_get_file(fd));
942
943         if ((result = fstat(fd, statb) >= 0))
944                 return result;
945
946 /* Now we've got a file descriptor but we failed to stat it */
947 /* Could be a nil: or could be a std#? */
948
949 /* if get_default_file fails we had a dud fd so return failure */
950 #if !defined(__CLIB2__)
951
952         fh = amigaos_get_file(fd);
953
954         /* if nil: return failure*/
955         if (fh == 0)
956                 return -1;
957
958         /* Now compare with our process Input() Output() etc */
959         /* if these were regular files sockets or pipes we had already
960          * succeeded */
961         /* so we can guess they a character special console.... I hope */
962
963         struct ExamineData *data;
964         char name[120];
965         name[0] = '\0';
966
967         data = IDOS->ExamineObjectTags(EX_FileHandleInput, fh, TAG_END);
968         if (data != NULL)
969         {
970
971                 IUtility->Strlcpy(name, data->Name, sizeof(name));
972
973                 IDOS->FreeDosObject(DOS_EXAMINEDATA, data);
974         }
975
976         // adebug("ad %ld '%s'\n",amigaos_get_file(fd),name);
977         mode = S_IFCHR;
978
979         if (fh == IDOS->Input())
980         {
981                 input = TRUE;
982                 SET_FLAG(mode, S_IRUSR);
983                 SET_FLAG(mode, S_IRGRP);
984                 SET_FLAG(mode, S_IROTH);
985         }
986         else if (fh == IDOS->Output() || fh == IDOS->ErrorOutput())
987         {
988                 input = FALSE;
989                 SET_FLAG(mode, S_IWUSR);
990                 SET_FLAG(mode, S_IWGRP);
991                 SET_FLAG(mode, S_IWOTH);
992         }
993         else
994         {
995                 /* we got a filehandle not handle by fstat or the above */
996                 /* most likely it's NIL: but lets check */
997                 struct ExamineData *exd = NULL;
998                 if ((exd = IDOS->ExamineObjectTags(EX_FileHandleInput, fh,
999                                                    TAG_DONE)))
1000                 {
1001                         BOOL isnil = FALSE;
1002                         if (exd->Type ==
1003                             (20060920)) // Ugh yes I know nasty.....
1004                         {
1005                                 isnil = TRUE;
1006                         }
1007                         IDOS->FreeDosObject(DOS_EXAMINEDATA, exd);
1008                         if (isnil)
1009                         {
1010                                 /* yep we got NIL: */
1011                                 SET_FLAG(mode, S_IRUSR);
1012                                 SET_FLAG(mode, S_IRGRP);
1013                                 SET_FLAG(mode, S_IROTH);
1014                                 SET_FLAG(mode, S_IWUSR);
1015                                 SET_FLAG(mode, S_IWGRP);
1016                                 SET_FLAG(mode, S_IWOTH);
1017                         }
1018                         else
1019                         {
1020                                 IExec->DebugPrintF(
1021                                     "unhandled filehandle in afstat()\n");
1022                                 return -1;
1023                         }
1024                 }
1025         }
1026
1027         memset(statb, 0, sizeof(statb));
1028
1029         statb->st_mode = mode;
1030
1031 #endif
1032         return 0;
1033 }
1034
1035 BPTR amigaos_get_file(int fd)
1036 {
1037         BPTR fh = (BPTR)NULL;
1038         if (!(fh = _get_osfhandle(fd)))
1039         {
1040                 switch (fd)
1041                 {
1042                 case 0:
1043                         fh = IDOS->Input();
1044                         break;
1045                 case 1:
1046                         fh = IDOS->Output();
1047                         break;
1048                 case 2:
1049                         fh = IDOS->ErrorOutput();
1050                         break;
1051                 default:
1052                         break;
1053                 }
1054         }
1055         return fh;
1056 }