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(__attribute__((unused))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->AllocVecTags(size, TAG_DONE)))
159 memmove(result, s, size);
164 static unsigned 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(__attribute__((unused))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;
244 static struct SignalSemaphore popen_sema;
247 void amigaos4_init_environ_sema()
249 IExec->InitSemaphore(&environ_sema);
250 IExec->InitSemaphore(&popen_sema);
253 void amigaos4_obtain_environ()
255 IExec->ObtainSemaphore(&environ_sema);
258 void amigaos4_release_environ()
260 IExec->ReleaseSemaphore(&environ_sema);
263 static void createvars(char **envp)
267 /* Set a local var to indicate to any subsequent sh that it is
269 * the top level shell and so should only inherit local amigaos
271 IDOS->SetVar("ABCSH_IMPORT_LOCAL", "TRUE", 5, GVF_LOCAL_ONLY);
273 amigaos4_obtain_environ();
277 while ((envp != NULL) && (*envp != NULL))
282 if ((len = strlen(*envp)))
284 if ((var = (char *)IExec->AllocVecTags(len + 1, AVT_ClearWithValue,0,TAG_DONE)))
288 val = strchr(var, '=');
305 amigaos4_release_environ();
317 int myexecvp(bool isperlthread, const char *filename, char *argv[])
320 //%s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
321 /* if there's a slash or a colon consider filename a path and skip
325 char *pathpart = NULL;
326 if ((strchr(filename, '/') == NULL) && (strchr(filename, ':') == NULL))
333 if (!(path = getenv("PATH")))
335 path = ".:/bin:/usr/bin:/c";
338 len = strlen(filename) + 1;
339 name = (char *)IExec->AllocVecTags(strlen(path) + len, AVT_ClearWithValue,0,AVT_Type,MEMF_SHARED,TAG_DONE);
340 pathpart = (char *)IExec->AllocVecTags(strlen(path) + 1, AVT_ClearWithValue,0,AVT_Type,MEMF_SHARED,TAG_DONE);
346 if (!(p = strchr(path, ':')))
348 p = strchr(path, '\0');
351 memcpy(pathpart, path, p - path);
352 pathpart[p - path] = '\0';
353 if (!(strlen(pathpart) == 0))
355 sprintf(name, "%s/%s", pathpart, filename);
358 sprintf(name, "%s", filename);
360 if ((stat(name, &st) == 0) && (S_ISREG(st.st_mode)))
362 /* we stated it and it's a regular file */
369 while (*p++ != '\0');
372 res = myexecve(isperlthread, filename, argv, myenviron);
376 IExec->FreeVec((APTR)name);
381 IExec->FreeVec((APTR)pathpart);
387 int myexecv(bool isperlthread, const char *path, char *argv[])
389 return myexecve(isperlthread, path, argv, myenviron);
392 int myexecl(bool isperlthread, const char *path, ...)
395 char *argv[1024]; /* 1024 enough? let's hope so! */
397 // adebug("%s %ld\n",__FUNCTION__,__LINE__);
404 argv[i] = va_arg(va, char *);
406 while (argv[i++] != NULL);
409 return myexecve(isperlthread, path, argv, myenviron);
414 fprintf(stderr, "Pause not implemented\n");
420 uint32 size_env(struct Hook *hook, __attribute__((unused))APTR userdata, struct ScanVarsMsg *message)
422 if (strlen(message->sv_GDir) <= 4)
424 hook->h_Data = (APTR)(((uint32)hook->h_Data) + 1);
429 uint32 copy_env(struct Hook *hook, __attribute__((unused))APTR userdata, struct ScanVarsMsg *message)
431 if (strlen(message->sv_GDir) <= 4)
433 char **env = (char **)hook->h_Data;
435 strlen(message->sv_Name) + 1 + message->sv_VarLen + 1 + 1;
436 char *buffer = (char *)IExec->AllocVecTags((uint32)size,AVT_ClearWithValue,0,TAG_DONE);
439 snprintf(buffer, size - 1, "%s=%s", message->sv_Name,
449 void ___makeenviron()
451 struct Hook *hook = (struct Hook *)IExec->AllocSysObjectTags(ASOT_HOOK,TAG_DONE);
458 struct DOSIFace *myIDOS =
459 (struct DOSIFace *)OpenInterface("dos.library", 53);
463 if (myIDOS->GetVar("ABCSH_IMPORT_LOCAL", varbuf, 8,
466 flags = GVF_LOCAL_ONLY;
470 flags = GVF_GLOBAL_ONLY;
473 hook->h_Entry = size_env;
476 myIDOS->ScanVars(hook, flags, 0);
477 size = ((uint32)hook->h_Data) + 1;
479 myenviron = (char **)IExec->AllocVecTags(size *
481 AVT_ClearWithValue,0,TAG_DONE);
482 origenviron = myenviron;
485 IExec->FreeSysObject(ASOT_HOOK,hook);
486 CloseInterface((struct Interface *)myIDOS);
489 hook->h_Entry = copy_env;
490 hook->h_Data = myenviron;
492 myIDOS->ScanVars(hook, flags, 0);
493 IExec->FreeSysObject(ASOT_HOOK,hook);
494 CloseInterface((struct Interface *)myIDOS);
499 void ___freeenviron()
502 /* perl might change environ, it puts it back except for ctrl-c */
503 /* so restore our own copy here */
504 struct DOSIFace *myIDOS =
505 (struct DOSIFace *)OpenInterface("dos.library", 53);
508 myenviron = origenviron;
512 for (i = myenviron; *i != NULL; i++)
516 IExec->FreeVec(myenviron);
519 CloseInterface((struct Interface *)myIDOS);
523 /* reimplementation of popen, clib2's doesn't do all we want */
531 static int popen_result = 0;
535 struct Task *thisTask = IExec->FindTask(0);
536 struct popen_data *pd = (struct popen_data *)thisTask->tc_UserData;
541 argv[2] = pd->command ? pd->command : NULL;
544 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
546 /* We need to give this to sh via execvp, execvp expects filename,
549 IExec->ObtainSemaphore(&popen_sema);
551 IExec->Signal(pd->parent,SIGBREAKF_CTRL_F);
553 popen_result = myexecvp(FALSE, argv[0], (char **)argv);
555 IExec->FreeVec(pd->command);
558 IExec->ReleaseSemaphore(&popen_sema);
564 FILE *amigaos_popen(const char *cmd, const char *mode)
572 struct Process *proc = NULL;
573 struct Task *thisTask = IExec->FindTask(0);
574 struct popen_data * pd = NULL;
576 /* First we need to check the mode
577 * We can only have unidirectional pipes
579 // adebug("%s %ld cmd %s mode %s \n",__FUNCTION__,__LINE__,cmd,
594 /* Make a unique pipe name
595 * we need a unix one and an amigaos version (of the same pipe!)
596 * as were linking with libunix.
599 sprintf(pipe_name, "%x%08lx/4096/0", pipenum++,
600 IUtility->GetUniqueID());
601 sprintf(unix_pipe, "/PIPE/%s", pipe_name);
602 sprintf(ami_pipe, "PIPE:%s", pipe_name);
604 /* Now we open the AmigaOs Filehandles That we wil pass to our
610 /* A read mode pipe: Output from pipe input from Output() or NIL:*/
611 /* First attempt to DUP Output() */
612 input = IDOS->DupFileHandle(IDOS->Input());
615 input = IDOS->Open("NIL:", MODE_READWRITE);
619 output = IDOS->Open(ami_pipe, MODE_NEWFILE);
621 result = fopen(unix_pipe, mode);
625 /* Open the write end first! */
627 result = fopen(unix_pipe, mode);
629 input = IDOS->Open(ami_pipe, MODE_OLDFILE);
632 output = IDOS->DupFileHandle(IDOS->Output());
635 output = IDOS->Open("NIL:", MODE_READWRITE);
639 if ((input == 0) || (output == 0) || (result == NULL))
641 /* Ouch stream opening failed */
655 /* We have our streams now start our new process
656 * We're using a new process so that execve can modify the environment
657 * with messing things up for the shell that launched perl
658 * Copy cmd before we launch the subprocess as perl seems to waste
659 * no time in overwriting it! The subprocess will free the copy.
662 if((pd = (struct popen_data*)IExec->AllocVecTags(sizeof(struct popen_data),AVT_Type,MEMF_SHARED,TAG_DONE)))
664 pd->parent = thisTask;
665 if ((pd->command = mystrdup(cmd)))
668 // %s\n",__FUNCTION__,__LINE__,cmd_copy?cmd_copy:"NULL");
669 proc = IDOS->CreateNewProcTags(
670 NP_Entry, popen_child, NP_Child, TRUE, NP_StackSize,
671 ((struct Process *)thisTask)->pr_StackSize, NP_Input, input,
672 NP_Output, output, NP_Error, IDOS->ErrorOutput(),
673 NP_CloseError, FALSE, NP_Cli, TRUE, NP_Name,
674 "Perl: popen process", NP_UserData, (int)pd,
680 /* wait for the child be setup right */
681 IExec->Wait(SIGBREAKF_CTRL_F);
685 /* New Process Failed to start
692 IExec->FreeVec(pd->command);
707 /* Our new process is running and will close it streams etc
708 * once its done. All we need to is open the pipe via stdio
714 int amigaos_pclose(FILE *f)
717 /* close the file before obtaining the semaphore else we might end up
718 hanging waiting for the child to read the last bit from the pipe */
720 IExec->ObtainSemaphore(&popen_sema);
721 result = popen_result;
722 IExec->ReleaseSemaphore(&popen_sema);
726 /* Work arround for clib2 fstat */
728 #define S_IFCHR 0x0020000
731 #define SET_FLAG(u, v) ((void)((u) |= (v)))
733 int afstat(int fd, struct stat *statb)
739 /* In the first instance pass it to fstat */
740 // adebug("fd %ld ad %ld\n",fd,amigaos_get_file(fd));
742 if ((result = fstat(fd, statb) >= 0))
745 /* Now we've got a file descriptor but we failed to stat it */
746 /* Could be a nil: or could be a std#? */
748 /* if get_default_file fails we had a dud fd so return failure */
749 #if !defined(__CLIB2__)
751 fh = amigaos_get_file(fd);
753 /* if nil: return failure*/
757 /* Now compare with our process Input() Output() etc */
758 /* if these were regular files sockets or pipes we had already
760 /* so we can guess they a character special console.... I hope */
762 struct ExamineData *data;
766 data = IDOS->ExamineObjectTags(EX_FileHandleInput, fh, TAG_END);
770 IUtility->Strlcpy(name, data->Name, sizeof(name));
772 IDOS->FreeDosObject(DOS_EXAMINEDATA, data);
775 // adebug("ad %ld '%s'\n",amigaos_get_file(fd),name);
778 if (fh == IDOS->Input())
781 SET_FLAG(mode, S_IRUSR);
782 SET_FLAG(mode, S_IRGRP);
783 SET_FLAG(mode, S_IROTH);
785 else if (fh == IDOS->Output() || fh == IDOS->ErrorOutput())
788 SET_FLAG(mode, S_IWUSR);
789 SET_FLAG(mode, S_IWGRP);
790 SET_FLAG(mode, S_IWOTH);
794 /* we got a filehandle not handle by fstat or the above */
795 /* most likely it's NIL: but lets check */
796 struct ExamineData *exd = NULL;
797 if ((exd = IDOS->ExamineObjectTags(EX_FileHandleInput, fh,
802 (20060920)) // Ugh yes I know nasty.....
806 IDOS->FreeDosObject(DOS_EXAMINEDATA, exd);
809 /* yep we got NIL: */
810 SET_FLAG(mode, S_IRUSR);
811 SET_FLAG(mode, S_IRGRP);
812 SET_FLAG(mode, S_IROTH);
813 SET_FLAG(mode, S_IWUSR);
814 SET_FLAG(mode, S_IWGRP);
815 SET_FLAG(mode, S_IWOTH);
820 "unhandled filehandle in afstat()\n");
826 memset(statb, 0, sizeof(statb));
828 statb->st_mode = mode;
834 BPTR amigaos_get_file(int fd)
836 BPTR fh = (BPTR)NULL;
837 if (!(fh = _get_osfhandle(fd)))
848 fh = IDOS->ErrorOutput();
857 /*########################################################################*/
859 #define LOCK_START 0xFFFFFFFFFFFFFFFELL
860 #define LOCK_LENGTH 1LL
862 // No wait forever option so lets wait for a loooong time.
863 #define TIMEOUT 0x7FFFFFFF
865 int amigaos_flock(int fd, int oper)
870 if (!(fh = amigaos_get_file(fd)))
880 if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
881 REC_SHARED | RECF_DOS_METHOD_ONLY,
890 if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
891 REC_EXCLUSIVE | RECF_DOS_METHOD_ONLY,
898 case LOCK_SH | LOCK_NB:
900 if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
901 REC_SHARED_IMMED | RECF_DOS_METHOD_ONLY,
912 case LOCK_EX | LOCK_NB:
914 if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
915 REC_EXCLUSIVE_IMMED | RECF_DOS_METHOD_ONLY,
928 if (IDOS->UnLockRecord(fh, LOCK_START, LOCK_LENGTH))