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>
24 #include <proto/dos.h>
25 #include <proto/exec.h>
26 #include <proto/utility.h>
30 struct UtilityIFace *IUtility = NULL;
32 /***************************************************************************/
34 struct Interface *OpenInterface(CONST_STRPTR libname, uint32 libver)
36 struct Library *base = IExec->OpenLibrary(libname, libver);
37 struct Interface *iface = IExec->GetInterface(base, "main", 1, NULL);
40 // We should probably post some kind of error message here.
42 IExec->CloseLibrary(base);
48 /***************************************************************************/
50 void CloseInterface(struct Interface *iface)
54 struct Library *base = iface->Data.LibBase;
55 IExec->DropInterface(iface);
56 IExec->CloseLibrary(base);
60 BOOL __unlink_retries = FALSE;
62 void ___makeenviron() __attribute__((constructor));
63 void ___freeenviron() __attribute__((destructor));
65 void ___openinterfaces() __attribute__((constructor));
66 void ___closeinterfaces() __attribute__((destructor));
68 void ___openinterfaces()
71 IDOS = (struct DOSIFace *)OpenInterface("dos.library", 53);
74 (struct UtilityIFace *)OpenInterface("utility.library", 53);
77 void ___closeinterfaces()
79 CloseInterface((struct Interface *)IDOS);
80 CloseInterface((struct Interface *)IUtility);
82 int VARARGS68K araddebug(UBYTE *fmt, ...);
83 int VARARGS68K adebug(UBYTE *fmt, ...);
85 #define __USE_RUNCOMMAND__
87 char **myenviron = NULL;
88 char **origenviron = NULL;
90 int myexecve(const char *path, char *argv[], char *envp[]);
91 static void createvars(char **envp);
103 int __myrc(char *arg)
105 struct Task *thisTask = IExec->FindTask(0);
106 struct args *myargs = (struct args *)thisTask->tc_UserData;
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);
116 BPTR seglist, int stack, char *command, int length, char **envp)
119 struct Task *thisTask = IExec->FindTask(0);
120 struct Process *proc;
122 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
124 myargs.seglist = seglist;
125 myargs.stack = stack;
126 myargs.command = command;
127 myargs.length = length;
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,
137 // NP_StackSize, ((struct Process
138 // *)myargs.parent)->pr_StackSize,
139 NP_Cli, TRUE, NP_UserData, (int)&myargs,
140 NP_NotifyOnDeathSigTask, thisTask, TAG_DONE)))
143 IExec->Wait(SIGF_CHILD);
145 return myargs.result;
148 static char *mystrdup(const char *s)
153 size = strlen(s) + 1;
155 if ((result = (char *)IExec->AllocVec(size, MEMF_ANY)))
157 memmove(result, s, size);
162 static int pipenum = 0;
164 int pipe(int filedes[2])
166 char pipe_name[1024];
168 // adebug("%s %ld \n",__FUNCTION__,__LINE__);
170 sprintf(pipe_name, "/T/%x.%08lx", pipenum++, IUtility->GetUniqueID());
172 sprintf(pipe_name, "/PIPE/%x%08lx/4096/0", pipenum++,
173 IUtility->GetUniqueID());
176 /* printf("pipe: %s \n", pipe_name);*/
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)
182 if (filedes[0] != -1)
184 if (filedes[1] != -1)
188 /* printf("filedes %d %d\n", filedes[0],
189 * filedes[1]);fflush(stdout);*/
196 fprintf(stderr, "Can not bloody fork\n");
201 int wait(int *status)
203 fprintf(stderr, "No wait try waitpid instead\n");
208 char *convert_path_a2u(const char *filename)
210 struct NameTranslationInfo nti;
217 __translate_amiga_to_unix_path_name(&filename, &nti);
219 return mystrdup(filename);
221 char *convert_path_u2a(const char *filename)
223 struct NameTranslationInfo nti;
230 if (strcmp(filename, "/dev/tty") == 0)
232 return mystrdup("CONSOLE:");
236 __translate_unix_to_amiga_path_name(&filename, &nti);
238 return mystrdup(filename);
241 static struct SignalSemaphore environ_sema;
243 void amigaos4_init_environ_sema() { IExec->InitSemaphore(&environ_sema); }
245 void amigaos4_obtain_environ() { IExec->ObtainSemaphore(&environ_sema); }
247 void amigaos4_release_environ() { IExec->ReleaseSemaphore(&environ_sema); }
249 static void createvars(char **envp)
253 /* Set a local var to indicate to any subsequent sh that it is
255 * the top level shell and so should only inherit local amigaos
257 IDOS->SetVar("ABCSH_IMPORT_LOCAL", "TRUE", 5, GVF_LOCAL_ONLY);
259 amigaos4_obtain_environ();
263 while ((envp != NULL) && (*envp != NULL))
268 if ((len = strlen(*envp)))
270 if ((var = (char *)IExec->AllocVec(
271 len + 1, MEMF_ANY | MEMF_CLEAR)))
275 val = strchr(var, '=');
292 amigaos4_release_environ();
296 static BOOL contains_whitespace(char *string)
302 if (strchr(string, ' '))
304 if (strchr(string, '\t'))
306 if (strchr(string, '\n'))
308 if (strchr(string, 0xA0))
310 if (strchr(string, '"'))
316 static int no_of_escapes(char *string)
320 for (p = string; p < string + strlen(string); p++)
341 int myexecvp(const char *filename, char *argv[])
344 //%s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
345 /* if there's a slash or a colon consider filename a path and skip
348 if ((strchr(filename, '/') == NULL) && (strchr(filename, ':') == NULL))
357 if (!(path = getenv("PATH")))
359 path = ".:/bin:/usr/bin:/c";
362 len = strlen(filename) + 1;
363 name = (char *)alloca(strlen(path) + len);
364 pathpart = (char *)alloca(strlen(path) + 1);
370 if (!(p = strchr(path, ':')))
372 p = strchr(path, '\0');
375 memcpy(pathpart, path, p - path);
376 pathpart[p - path] = '\0';
377 if (!(strlen(pathpart) == 0))
379 sprintf(name, "%s/%s", pathpart, filename);
382 sprintf(name, "%s", filename);
384 if ((stat(name, &st) == 0) && (S_ISREG(st.st_mode)))
386 /* we stated it and it's a regular file */
392 } while (*p++ != '\0');
394 res = myexecve(filename, argv, myenviron);
398 int myexecv(const char *path, char *argv[])
400 return myexecve(path, argv, myenviron);
403 int myexecl(const char *path, ...)
406 char *argv[1024]; /* 1024 enough? let's hope so! */
408 // adebug("%s %ld\n",__FUNCTION__,__LINE__);
415 argv[i] = va_arg(va, char *);
416 } while (argv[i++] != NULL);
419 return myexecve(path, argv, myenviron);
422 int myexecve(const char *filename, char *argv[], char *envp[])
428 char *interpreter = 0;
429 char *interpreter_args = 0;
431 char *filename_conv = 0;
432 char *interpreter_conv = 0;
436 // struct Task *thisTask = IExec->FindTask(0);
439 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
441 /* Calculate the size of filename and all args, including spaces and
443 size = 0; // strlen(filename) + 1;
444 for (cur = (char **)argv /* +1 */; *cur; cur++)
448 (contains_whitespace(*cur) ? (2 + no_of_escapes(*cur)) : 0);
450 /* Check if it's a script file */
452 fh = fopen(filename, "r");
455 if (fgetc(fh) == '#' && fgetc(fh) == '!')
459 fgets(buffer, 999, fh);
461 while (*p == ' ' || *p == '\t')
463 if (buffer[strlen(buffer) - 1] == '\n')
464 buffer[strlen(buffer) - 1] = '\0';
465 if ((q = strchr(p, ' ')))
470 interpreter_args = mystrdup(q);
474 interpreter_args = mystrdup("");
476 interpreter = mystrdup(p);
477 size += strlen(interpreter) + 1;
478 size += strlen(interpreter_args) + 1;
485 /* We couldn't open this why not? */
488 /* file didn't exist! */
493 /* Allocate the command line */
494 filename_conv = convert_path_u2a(filename);
497 size += strlen(filename_conv);
499 full = (char *)IExec->AllocVec(size + 10, MEMF_ANY | MEMF_CLEAR);
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);
510 sprintf(full, "%s %s ", interpreter_args,
513 IExec->FreeVec(interpreter);
514 IExec->FreeVec(interpreter_args);
517 IExec->FreeVec(filename_conv);
518 fname = mystrdup(interpreter_conv);
520 if (interpreter_conv)
521 IExec->FreeVec(interpreter_conv);
525 #ifndef __USE_RUNCOMMAND__
526 sprintf(full, "%s ", filename_conv);
530 fname = mystrdup(filename_conv);
532 IExec->FreeVec(filename_conv);
535 for (cur = (char **)(argv + 1); *cur != 0; cur++)
537 if (contains_whitespace(*cur))
539 int esc = no_of_escapes(*cur);
543 char *buff = IExec->AllocVec(
544 strlen(*cur) + 4 + esc,
545 MEMF_ANY | MEMF_CLEAR);
577 IExec->FreeVec(buff);
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);
608 BPTR seglist = IDOS->LoadSeg(fname);
611 /* check if we have an executable! */
612 struct PseudoSegList *ps = NULL;
613 if (!IDOS->GetSegListInfoTags(
614 seglist, GSLI_Native, &ps, TAG_DONE))
616 IDOS->GetSegListInfoTags(
617 seglist, GSLI_68KPS, &ps, TAG_DONE);
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,
634 IDOS->UnLoadSeg(seglist);
640 IExec->FreeVec(fname);
643 #endif /* USE_RUNCOMMAND */
645 IExec->FreeVec(full);
646 if (errno == ENOEXEC)
652 IExec->FreeVec(interpreter);
654 IExec->FreeVec(filename_conv);
663 fprintf(stderr, "Pause not implemented\n");
669 uint32 size_env(struct Hook *hook, APTR userdata, struct ScanVarsMsg *message)
671 if (strlen(message->sv_GDir) <= 4)
673 hook->h_Data = (APTR)(((uint32)hook->h_Data) + 1);
678 uint32 copy_env(struct Hook *hook, APTR userdata, struct ScanVarsMsg *message)
680 if (strlen(message->sv_GDir) <= 4)
682 char **env = (char **)hook->h_Data;
684 strlen(message->sv_Name) + 1 + message->sv_VarLen + 1 + 1;
685 char *buffer = (char *)IExec->AllocVec((uint32)size,
686 MEMF_ANY | MEMF_CLEAR);
688 snprintf(buffer, size - 1, "%s=%s", message->sv_Name,
698 void ___makeenviron()
705 struct DOSIFace *myIDOS =
706 (struct DOSIFace *)OpenInterface("dos.library", 53);
709 if (myIDOS->GetVar("ABCSH_IMPORT_LOCAL", varbuf, 8,
712 flags = GVF_LOCAL_ONLY;
716 flags = GVF_GLOBAL_ONLY;
719 hook.h_Entry = size_env;
722 myIDOS->ScanVars(&hook, flags, 0);
723 hook.h_Data = (APTR)(((uint32)hook.h_Data) + 1);
725 myenviron = (char **)IExec->AllocVec((uint32)hook.h_Data *
727 MEMF_ANY | MEMF_CLEAR);
728 origenviron = myenviron;
733 hook.h_Entry = copy_env;
734 hook.h_Data = myenviron;
736 myIDOS->ScanVars(&hook, flags, 0);
737 CloseInterface((struct Interface *)myIDOS);
741 void ___freeenviron()
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);
750 myenviron = origenviron;
754 for (i = myenviron; *i != NULL; i++)
758 IExec->FreeVec(myenviron);
761 CloseInterface((struct Interface *)myIDOS);
765 /* reimplementation of popen, clib2's doesn't do all we want */
767 static BOOL is_final_quote_character(const char *str)
771 result = (BOOL)(str[0] == '\"' && (str[1] == '\0' || isspace(str[1])));
776 static BOOL is_final_squote_character(const char *str)
780 result = (BOOL)(str[0] == '\'' && (str[1] == '\0' || isspace(str[1])));
787 struct Task *thisTask = IExec->FindTask(0);
789 char *command = (char *)thisTask->tc_UserData;
793 int number_of_arguments;
798 argv[2] = command ? command : NULL;
801 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,command?command:"NULL");
803 /* We need to give this to sh via execvp, execvp expects filename,
807 myexecvp(argv[0], argv);
809 IExec->FreeVec(command);
815 FILE *amigaos_popen(const char *cmd, const char *mode)
824 struct Process *proc = NULL;
825 struct Task *thisTask = IExec->FindTask(0);
827 /* First we need to check the mode
828 * We can only have unidirectional pipes
830 // adebug("%s %ld cmd %s mode %s \n",__FUNCTION__,__LINE__,cmd,
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.
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);
855 /* Now we open the AmigaOs Filehandles That we wil pass to our
861 /* A read mode pipe: Output from pipe input from NIL:*/
862 input = IDOS->Open("NIL:", MODE_NEWFILE);
865 output = IDOS->Open(ami_pipe, MODE_NEWFILE);
871 input = IDOS->Open(ami_pipe, MODE_NEWFILE);
874 output = IDOS->Open("NIL:", MODE_NEWFILE);
877 if ((input == 0) || (output == 0))
879 /* Ouch stream opening failed */
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.
895 if ((cmd_copy = mystrdup(cmd)))
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,
909 /* New Process Failed to start
917 IExec->FreeVec(cmd_copy);
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
924 return fopen(unix_pipe, mode);
927 /* Work arround for clib2 fstat */
929 #define S_IFCHR 0x0020000
932 #define SET_FLAG(u, v) ((void)((u) |= (v)))
934 int afstat(int fd, struct stat *statb)
940 /* In the first instance pass it to fstat */
941 // adebug("fd %ld ad %ld\n",fd,amigaos_get_file(fd));
943 if ((result = fstat(fd, statb) >= 0))
946 /* Now we've got a file descriptor but we failed to stat it */
947 /* Could be a nil: or could be a std#? */
949 /* if get_default_file fails we had a dud fd so return failure */
950 #if !defined(__CLIB2__)
952 fh = amigaos_get_file(fd);
954 /* if nil: return failure*/
958 /* Now compare with our process Input() Output() etc */
959 /* if these were regular files sockets or pipes we had already
961 /* so we can guess they a character special console.... I hope */
963 struct ExamineData *data;
967 data = IDOS->ExamineObjectTags(EX_FileHandleInput, fh, TAG_END);
971 IUtility->Strlcpy(name, data->Name, sizeof(name));
973 IDOS->FreeDosObject(DOS_EXAMINEDATA, data);
976 // adebug("ad %ld '%s'\n",amigaos_get_file(fd),name);
979 if (fh == IDOS->Input())
982 SET_FLAG(mode, S_IRUSR);
983 SET_FLAG(mode, S_IRGRP);
984 SET_FLAG(mode, S_IROTH);
986 else if (fh == IDOS->Output() || fh == IDOS->ErrorOutput())
989 SET_FLAG(mode, S_IWUSR);
990 SET_FLAG(mode, S_IWGRP);
991 SET_FLAG(mode, S_IWOTH);
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,
1003 (20060920)) // Ugh yes I know nasty.....
1007 IDOS->FreeDosObject(DOS_EXAMINEDATA, exd);
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);
1021 "unhandled filehandle in afstat()\n");
1027 memset(statb, 0, sizeof(statb));
1029 statb->st_mode = mode;
1035 BPTR amigaos_get_file(int fd)
1037 BPTR fh = (BPTR)NULL;
1038 if (!(fh = _get_osfhandle(fd)))
1046 fh = IDOS->Output();
1049 fh = IDOS->ErrorOutput();