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