1 /* amigaos.c uses only amigaos APIs,
2 * as opposed to amigaio.c which mixes amigaos and perl APIs */
13 #if defined(__CLIB2__)
16 #if defined(__NEWLIB__)
17 # include <amiga_platform.h>
27 #include <proto/dos.h>
28 #include <proto/exec.h>
29 #include <proto/utility.h>
33 struct UtilityIFace *IUtility = NULL;
35 /***************************************************************************/
37 struct Interface *OpenInterface(CONST_STRPTR libname, uint32 libver)
39 struct Library *base = IExec->OpenLibrary(libname, libver);
40 struct Interface *iface = IExec->GetInterface(base, "main", 1, NULL);
43 // We should probably post some kind of error message here.
45 IExec->CloseLibrary(base);
51 /***************************************************************************/
53 void CloseInterface(struct Interface *iface)
57 struct Library *base = iface->Data.LibBase;
58 IExec->DropInterface(iface);
59 IExec->CloseLibrary(base);
63 BOOL __unlink_retries = FALSE;
65 void ___makeenviron() __attribute__((constructor));
66 void ___freeenviron() __attribute__((destructor));
68 void ___openinterfaces() __attribute__((constructor));
69 void ___closeinterfaces() __attribute__((destructor));
71 void ___openinterfaces()
74 IDOS = (struct DOSIFace *)OpenInterface("dos.library", 53);
77 (struct UtilityIFace *)OpenInterface("utility.library", 53);
80 void ___closeinterfaces()
82 CloseInterface((struct Interface *)IDOS);
83 CloseInterface((struct Interface *)IUtility);
85 int VARARGS68K araddebug(UBYTE *fmt, ...);
86 int VARARGS68K adebug(UBYTE *fmt, ...);
88 #define __USE_RUNCOMMAND__
90 char **myenviron = NULL;
91 char **origenviron = NULL;
93 static void createvars(char **envp);
105 int __myrc(char *arg)
107 struct Task *thisTask = IExec->FindTask(0);
108 struct args *myargs = (struct args *)thisTask->tc_UserData;
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);
118 BPTR seglist, int stack, char *command, int length, char **envp)
121 struct Task *thisTask = IExec->FindTask(0);
122 struct Process *proc;
124 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
126 myargs.seglist = seglist;
127 myargs.stack = stack;
128 myargs.command = command;
129 myargs.length = length;
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,
139 // NP_StackSize, ((struct Process
140 // *)myargs.parent)->pr_StackSize,
141 NP_Cli, TRUE, NP_UserData, (int)&myargs,
142 NP_NotifyOnDeathSigTask, thisTask, TAG_DONE)))
145 IExec->Wait(SIGF_CHILD);
147 return myargs.result;
150 char *mystrdup(const char *s)
155 size = strlen(s) + 1;
157 if ((result = (char *)IExec->AllocVec(size, MEMF_ANY)))
159 memmove(result, s, size);
164 static int pipenum = 0;
166 int pipe(int filedes[2])
168 char pipe_name[1024];
170 // adebug("%s %ld \n",__FUNCTION__,__LINE__);
172 sprintf(pipe_name, "/T/%x.%08lx", pipenum++, IUtility->GetUniqueID());
174 sprintf(pipe_name, "/PIPE/%x%08lx/4096/0", pipenum++,
175 IUtility->GetUniqueID());
178 /* printf("pipe: %s \n", pipe_name);*/
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)
184 if (filedes[0] != -1)
186 if (filedes[1] != -1)
190 /* printf("filedes %d %d\n", filedes[0],
191 * filedes[1]);fflush(stdout);*/
198 fprintf(stderr, "Can not bloody fork\n");
203 int wait(int *status)
205 fprintf(stderr, "No wait try waitpid instead\n");
210 char *convert_path_a2u(const char *filename)
212 struct NameTranslationInfo nti;
219 __translate_amiga_to_unix_path_name(&filename, &nti);
221 return mystrdup(filename);
223 char *convert_path_u2a(const char *filename)
225 struct NameTranslationInfo nti;
232 if (strcmp(filename, "/dev/tty") == 0)
234 return mystrdup("CONSOLE:");
238 __translate_unix_to_amiga_path_name(&filename, &nti);
240 return mystrdup(filename);
243 static struct SignalSemaphore environ_sema;
245 void amigaos4_init_environ_sema()
247 IExec->InitSemaphore(&environ_sema);
250 void amigaos4_obtain_environ()
252 IExec->ObtainSemaphore(&environ_sema);
255 void amigaos4_release_environ()
257 IExec->ReleaseSemaphore(&environ_sema);
260 static void createvars(char **envp)
264 /* Set a local var to indicate to any subsequent sh that it is
266 * the top level shell and so should only inherit local amigaos
268 IDOS->SetVar("ABCSH_IMPORT_LOCAL", "TRUE", 5, GVF_LOCAL_ONLY);
270 amigaos4_obtain_environ();
274 while ((envp != NULL) && (*envp != NULL))
279 if ((len = strlen(*envp)))
281 if ((var = (char *)IExec->AllocVec(
282 len + 1, MEMF_ANY | MEMF_CLEAR)))
286 val = strchr(var, '=');
303 amigaos4_release_environ();
307 static BOOL contains_whitespace(char *string)
313 if (strchr(string, ' '))
315 if (strchr(string, '\t'))
317 if (strchr(string, '\n'))
319 if (strchr(string, 0xA0))
321 if (strchr(string, '"'))
327 static int no_of_escapes(char *string)
331 for (p = string; p < string + strlen(string); p++)
352 int myexecvp(bool isperlthread, const char *filename, char *argv[])
355 //%s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
356 /* if there's a slash or a colon consider filename a path and skip
359 if ((strchr(filename, '/') == NULL) && (strchr(filename, ':') == NULL))
368 if (!(path = getenv("PATH")))
370 path = ".:/bin:/usr/bin:/c";
373 len = strlen(filename) + 1;
374 name = (char *)alloca(strlen(path) + len);
375 pathpart = (char *)alloca(strlen(path) + 1);
381 if (!(p = strchr(path, ':')))
383 p = strchr(path, '\0');
386 memcpy(pathpart, path, p - path);
387 pathpart[p - path] = '\0';
388 if (!(strlen(pathpart) == 0))
390 sprintf(name, "%s/%s", pathpart, filename);
393 sprintf(name, "%s", filename);
395 if ((stat(name, &st) == 0) && (S_ISREG(st.st_mode)))
397 /* we stated it and it's a regular file */
404 while (*p++ != '\0');
406 res = myexecve(isperlthread, filename, argv, myenviron);
410 int myexecv(bool isperlthread, const char *path, char *argv[])
412 return myexecve(isperlthread, path, argv, myenviron);
415 int myexecl(bool isperlthread, const char *path, ...)
418 char *argv[1024]; /* 1024 enough? let's hope so! */
420 // adebug("%s %ld\n",__FUNCTION__,__LINE__);
427 argv[i] = va_arg(va, char *);
429 while (argv[i++] != NULL);
432 return myexecve(isperlthread, path, argv, myenviron);
437 int myexecve(const char *filename, char *argv[], char *envp[])
443 char *interpreter = 0;
444 char *interpreter_args = 0;
446 char *filename_conv = 0;
447 char *interpreter_conv = 0;
451 // struct Task *thisTask = IExec->FindTask(0);
457 if(aTHX) // I hope this is NULL when not on a interpreteer thread nor to level.
459 /* Save away our stdio */
460 amigaos_stdio_save(aTHX_ & store);
463 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
465 /* Calculate the size of filename and all args, including spaces and
467 size = 0; // strlen(filename) + 1;
468 for (cur = (char **)argv /* +1 */; *cur; cur++)
472 (contains_whitespace(*cur) ? (2 + no_of_escapes(*cur)) : 0);
474 /* Check if it's a script file */
476 fh = fopen(filename, "r");
479 if (fgetc(fh) == '#' && fgetc(fh) == '!')
483 fgets(buffer, 999, fh);
485 while (*p == ' ' || *p == '\t')
487 if (buffer[strlen(buffer) - 1] == '\n')
488 buffer[strlen(buffer) - 1] = '\0';
489 if ((q = strchr(p, ' ')))
494 interpreter_args = mystrdup(q);
498 interpreter_args = mystrdup("");
500 interpreter = mystrdup(p);
501 size += strlen(interpreter) + 1;
502 size += strlen(interpreter_args) + 1;
509 /* We couldn't open this why not? */
512 /* file didn't exist! */
517 /* Allocate the command line */
518 filename_conv = convert_path_u2a(filename);
521 size += strlen(filename_conv);
523 full = (char *)IExec->AllocVec(size + 10, MEMF_ANY | MEMF_CLEAR);
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);
534 sprintf(full, "%s %s ", interpreter_args,
537 IExec->FreeVec(interpreter);
538 IExec->FreeVec(interpreter_args);
541 IExec->FreeVec(filename_conv);
542 fname = mystrdup(interpreter_conv);
544 if (interpreter_conv)
545 IExec->FreeVec(interpreter_conv);
549 #ifndef __USE_RUNCOMMAND__
550 sprintf(full, "%s ", filename_conv);
554 fname = mystrdup(filename_conv);
556 IExec->FreeVec(filename_conv);
559 for (cur = (char **)(argv + 1); *cur != 0; cur++)
561 if (contains_whitespace(*cur))
563 int esc = no_of_escapes(*cur);
567 char *buff = IExec->AllocVec(
568 strlen(*cur) + 4 + esc,
569 MEMF_ANY | MEMF_CLEAR);
601 IExec->FreeVec(buff);
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);
632 BPTR seglist = IDOS->LoadSeg(fname);
635 /* check if we have an executable! */
636 struct PseudoSegList *ps = NULL;
637 if (!IDOS->GetSegListInfoTags(
638 seglist, GSLI_Native, &ps, TAG_DONE))
640 IDOS->GetSegListInfoTags(
641 seglist, GSLI_68KPS, &ps, TAG_DONE);
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,
658 IDOS->UnLoadSeg(seglist);
664 IExec->FreeVec(fname);
667 #endif /* USE_RUNCOMMAND */
669 IExec->FreeVec(full);
670 if (errno == ENOEXEC)
678 IExec->FreeVec(interpreter);
680 IExec->FreeVec(filename_conv);
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);
698 fprintf(stderr, "Pause not implemented\n");
704 uint32 size_env(struct Hook *hook, APTR userdata, struct ScanVarsMsg *message)
706 if (strlen(message->sv_GDir) <= 4)
708 hook->h_Data = (APTR)(((uint32)hook->h_Data) + 1);
713 uint32 copy_env(struct Hook *hook, APTR userdata, struct ScanVarsMsg *message)
715 if (strlen(message->sv_GDir) <= 4)
717 char **env = (char **)hook->h_Data;
719 strlen(message->sv_Name) + 1 + message->sv_VarLen + 1 + 1;
720 char *buffer = (char *)IExec->AllocVec((uint32)size,
721 MEMF_ANY | MEMF_CLEAR);
723 snprintf(buffer, size - 1, "%s=%s", message->sv_Name,
733 void ___makeenviron()
740 struct DOSIFace *myIDOS =
741 (struct DOSIFace *)OpenInterface("dos.library", 53);
744 if (myIDOS->GetVar("ABCSH_IMPORT_LOCAL", varbuf, 8,
747 flags = GVF_LOCAL_ONLY;
751 flags = GVF_GLOBAL_ONLY;
754 hook.h_Entry = size_env;
757 myIDOS->ScanVars(&hook, flags, 0);
758 hook.h_Data = (APTR)(((uint32)hook.h_Data) + 1);
760 myenviron = (char **)IExec->AllocVec((uint32)hook.h_Data *
762 MEMF_ANY | MEMF_CLEAR);
763 origenviron = myenviron;
768 hook.h_Entry = copy_env;
769 hook.h_Data = myenviron;
771 myIDOS->ScanVars(&hook, flags, 0);
772 CloseInterface((struct Interface *)myIDOS);
776 void ___freeenviron()
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);
785 myenviron = origenviron;
789 for (i = myenviron; *i != NULL; i++)
793 IExec->FreeVec(myenviron);
796 CloseInterface((struct Interface *)myIDOS);
800 /* reimplementation of popen, clib2's doesn't do all we want */
802 static BOOL is_final_quote_character(const char *str)
806 result = (BOOL)(str[0] == '\"' && (str[1] == '\0' || isspace(str[1])));
811 static BOOL is_final_squote_character(const char *str)
815 result = (BOOL)(str[0] == '\'' && (str[1] == '\0' || isspace(str[1])));
822 struct Task *thisTask = IExec->FindTask(0);
824 char *command = (char *)thisTask->tc_UserData;
828 int number_of_arguments;
833 argv[2] = command ? command : NULL;
836 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
838 /* We need to give this to sh via execvp, execvp expects filename,
842 myexecvp(FALSE, argv[0], argv);
844 IExec->FreeVec(command);
850 FILE *amigaos_popen(const char *cmd, const char *mode)
859 struct Process *proc = NULL;
860 struct Task *thisTask = IExec->FindTask(0);
862 /* First we need to check the mode
863 * We can only have unidirectional pipes
865 // adebug("%s %ld cmd %s mode %s \n",__FUNCTION__,__LINE__,cmd,
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.
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);
890 /* Now we open the AmigaOs Filehandles That we wil pass to our
896 /* A read mode pipe: Output from pipe input from NIL:*/
897 input = IDOS->Open("NIL:", MODE_NEWFILE);
900 output = IDOS->Open(ami_pipe, MODE_NEWFILE);
906 input = IDOS->Open(ami_pipe, MODE_NEWFILE);
909 output = IDOS->Open("NIL:", MODE_NEWFILE);
912 if ((input == 0) || (output == 0))
914 /* Ouch stream opening failed */
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.
930 if ((cmd_copy = mystrdup(cmd)))
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,
944 /* New Process Failed to start
952 IExec->FreeVec(cmd_copy);
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
959 return fopen(unix_pipe, mode);
962 /* Work arround for clib2 fstat */
964 #define S_IFCHR 0x0020000
967 #define SET_FLAG(u, v) ((void)((u) |= (v)))
969 int afstat(int fd, struct stat *statb)
975 /* In the first instance pass it to fstat */
976 // adebug("fd %ld ad %ld\n",fd,amigaos_get_file(fd));
978 if ((result = fstat(fd, statb) >= 0))
981 /* Now we've got a file descriptor but we failed to stat it */
982 /* Could be a nil: or could be a std#? */
984 /* if get_default_file fails we had a dud fd so return failure */
985 #if !defined(__CLIB2__)
987 fh = amigaos_get_file(fd);
989 /* if nil: return failure*/
993 /* Now compare with our process Input() Output() etc */
994 /* if these were regular files sockets or pipes we had already
996 /* so we can guess they a character special console.... I hope */
998 struct ExamineData *data;
1002 data = IDOS->ExamineObjectTags(EX_FileHandleInput, fh, TAG_END);
1006 IUtility->Strlcpy(name, data->Name, sizeof(name));
1008 IDOS->FreeDosObject(DOS_EXAMINEDATA, data);
1011 // adebug("ad %ld '%s'\n",amigaos_get_file(fd),name);
1014 if (fh == IDOS->Input())
1017 SET_FLAG(mode, S_IRUSR);
1018 SET_FLAG(mode, S_IRGRP);
1019 SET_FLAG(mode, S_IROTH);
1021 else if (fh == IDOS->Output() || fh == IDOS->ErrorOutput())
1024 SET_FLAG(mode, S_IWUSR);
1025 SET_FLAG(mode, S_IWGRP);
1026 SET_FLAG(mode, S_IWOTH);
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,
1038 (20060920)) // Ugh yes I know nasty.....
1042 IDOS->FreeDosObject(DOS_EXAMINEDATA, exd);
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);
1056 "unhandled filehandle in afstat()\n");
1062 memset(statb, 0, sizeof(statb));
1064 statb->st_mode = mode;
1070 BPTR amigaos_get_file(int fd)
1072 BPTR fh = (BPTR)NULL;
1073 if (!(fh = _get_osfhandle(fd)))
1081 fh = IDOS->Output();
1084 fh = IDOS->ErrorOutput();
1093 /*########################################################################*/
1095 #define LOCK_START 0xFFFFFFFFFFFFFFFELL
1096 #define LOCK_LENGTH 1LL
1098 // No wait forever option so lets wait for a loooong time.
1099 #define TIMEOUT 0x7FFFFFFF
1101 int amigaos_flock(int fd, int oper)
1106 if (!(fh = amigaos_get_file(fd)))
1116 if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
1117 REC_SHARED | RECF_DOS_METHOD_ONLY,
1126 if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
1127 REC_EXCLUSIVE | RECF_DOS_METHOD_ONLY,
1134 case LOCK_SH | LOCK_NB:
1136 if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
1137 REC_SHARED_IMMED | RECF_DOS_METHOD_ONLY,
1144 errno = EWOULDBLOCK;
1148 case LOCK_EX | LOCK_NB:
1150 if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
1151 REC_EXCLUSIVE_IMMED | RECF_DOS_METHOD_ONLY,
1158 errno = EWOULDBLOCK;
1164 if (IDOS->UnLockRecord(fh, LOCK_START, LOCK_LENGTH))