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
33struct UtilityIFace *IUtility = NULL;
34
35/***************************************************************************/
36
37struct 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
53void 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
63BOOL __unlink_retries = FALSE;
64
65void ___makeenviron() __attribute__((constructor));
66void ___freeenviron() __attribute__((destructor));
67
68void ___openinterfaces() __attribute__((constructor));
69void ___closeinterfaces() __attribute__((destructor));
70
71void ___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
80void ___closeinterfaces()
81{
82        CloseInterface((struct Interface *)IDOS);
83        CloseInterface((struct Interface *)IUtility);
84}
85int VARARGS68K araddebug(UBYTE *fmt, ...);
86int VARARGS68K adebug(UBYTE *fmt, ...);
87
88#define __USE_RUNCOMMAND__
89
90char **myenviron = NULL;
91char **origenviron = NULL;
92
93static void createvars(char **envp);
94
95struct args
96{
97        BPTR seglist;
98        int stack;
99        char *command;
100        int length;
101        int result;
102        char **envp;
103};
104
105int __myrc(__attribute__((unused))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
117int32 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
150char *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->AllocVecTags(size, TAG_DONE)))
158        {
159                memmove(result, s, size);
160        }
161        return result;
162}
163
164unsigned int pipenum = 0;
165
166int 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
196int fork(void)
197{
198        fprintf(stderr, "Can not bloody fork\n");
199        errno = ENOMEM;
200        return -1;
201}
202
203int wait(__attribute__((unused))int *status)
204{
205        fprintf(stderr, "No wait try waitpid instead\n");
206        errno = ECHILD;
207        return -1;
208}
209
210char *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}
223char *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
243struct SignalSemaphore environ_sema;
244struct SignalSemaphore popen_sema;
245
246
247void amigaos4_init_environ_sema()
248{
249        IExec->InitSemaphore(&environ_sema);
250        IExec->InitSemaphore(&popen_sema);
251}
252
253void amigaos4_obtain_environ()
254{
255        IExec->ObtainSemaphore(&environ_sema);
256}
257
258void amigaos4_release_environ()
259{
260        IExec->ReleaseSemaphore(&environ_sema);
261}
262
263static void createvars(char **envp)
264{
265        if (envp)
266        {
267                /* Set a local var to indicate to any subsequent sh that it is
268                * not
269                * the top level shell and so should only inherit local amigaos
270                * vars */
271                IDOS->SetVar("ABCSH_IMPORT_LOCAL", "TRUE", 5, GVF_LOCAL_ONLY);
272
273                amigaos4_obtain_environ();
274
275                envp = myenviron;
276
277                while ((envp != NULL) && (*envp != NULL))
278                {
279                        int len;
280                        char *var;
281                        char *val;
282                        if ((len = strlen(*envp)))
283                        {
284                                if ((var = (char *)IExec->AllocVecTags(len + 1, AVT_ClearWithValue,0,TAG_DONE)))
285                                {
286                                        strcpy(var, *envp);
287
288                                        val = strchr(var, '=');
289                                        if (val)
290                                        {
291                                                *val++ = '\0';
292                                                if (*val)
293                                                {
294                                                        IDOS->SetVar(
295                                                            var, val,
296                                                            strlen(val) + 1,
297                                                            GVF_LOCAL_ONLY);
298                                                }
299                                        }
300                                        IExec->FreeVec(var);
301                                }
302                        }
303                        envp++;
304                }
305                amigaos4_release_environ();
306        }
307}
308
309struct command_data
310{
311        STRPTR args;
312        BPTR seglist;
313        struct Task *parent;
314};
315
316
317int myexecvp(bool isperlthread, const char *filename, char *argv[])
318{
319        //	adebug("%s %ld
320        //%s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
321        /* if there's a slash or a colon consider filename a path and skip
322         * search */
323        int res;
324        char *name = NULL;
325        char *pathpart = NULL;
326        if ((strchr(filename, '/') == NULL) && (strchr(filename, ':') == NULL))
327        {
328                const char *path;
329                const char *p;
330                size_t len;
331                struct stat st;
332
333                if (!(path = getenv("PATH")))
334                {
335                        path = ".:/bin:/usr/bin:/c";
336                }
337
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);
341                p = path;
342                do
343                {
344                        path = p;
345
346                        if (!(p = strchr(path, ':')))
347                        {
348                                p = strchr(path, '\0');
349                        }
350
351                        memcpy(pathpart, path, p - path);
352                        pathpart[p - path] = '\0';
353                        if (!(strlen(pathpart) == 0))
354                        {
355                                sprintf(name, "%s/%s", pathpart, filename);
356                        }
357                        else
358                                sprintf(name, "%s", filename);
359
360                        if ((stat(name, &st) == 0) && (S_ISREG(st.st_mode)))
361                        {
362                                /* we stated it and it's a regular file */
363                                /* let's boogie! */
364                                filename = name;
365                                break;
366                        }
367
368                }
369                while (*p++ != '\0');
370        }
371
372        res = myexecve(isperlthread, filename, argv, myenviron);
373
374        if(name)
375        {
376                IExec->FreeVec((APTR)name);
377                name = NULL;
378        }
379        if(pathpart)
380        {
381                IExec->FreeVec((APTR)pathpart);
382                pathpart = NULL;
383        }
384        return res;
385}
386
387int myexecv(bool isperlthread, const char *path, char *argv[])
388{
389        return myexecve(isperlthread, path, argv, myenviron);
390}
391
392int myexecl(bool isperlthread, const char *path, ...)
393{
394        va_list va;
395        char *argv[1024]; /* 1024 enough? let's hope so! */
396        int i = 0;
397        // adebug("%s %ld\n",__FUNCTION__,__LINE__);
398
399        va_start(va, path);
400        i = 0;
401
402        do
403        {
404                argv[i] = va_arg(va, char *);
405        }
406        while (argv[i++] != NULL);
407
408        va_end(va);
409        return myexecve(isperlthread, path, argv, myenviron);
410}
411
412int pause(void)
413{
414        fprintf(stderr, "Pause not implemented\n");
415
416        errno = EINTR;
417        return -1;
418}
419
420uint32 size_env(struct Hook *hook, __attribute__((unused))APTR userdata, struct ScanVarsMsg *message)
421{
422        if (strlen(message->sv_GDir) <= 4)
423        {
424                hook->h_Data = (APTR)(((uint32)hook->h_Data) + 1);
425        }
426        return 0;
427}
428
429uint32 copy_env(struct Hook *hook, __attribute__((unused))APTR userdata, struct ScanVarsMsg *message)
430{
431        if (strlen(message->sv_GDir) <= 4)
432        {
433                char **env = (char **)hook->h_Data;
434                uint32 size =
435                    strlen(message->sv_Name) + 1 + message->sv_VarLen + 1 + 1;
436                char *buffer = (char *)IExec->AllocVecTags((uint32)size,AVT_ClearWithValue,0,TAG_DONE);
437
438
439                snprintf(buffer, size - 1, "%s=%s", message->sv_Name,
440                         message->sv_Var);
441
442                *env = buffer;
443                env++;
444                hook->h_Data = env;
445        }
446        return 0;
447}
448
449void ___makeenviron()
450{
451        struct Hook *hook = (struct Hook *)IExec->AllocSysObjectTags(ASOT_HOOK,TAG_DONE);
452
453        if(hook)
454        {
455                char varbuf[8];
456                uint32 flags = 0;
457
458                struct DOSIFace *myIDOS =
459                    (struct DOSIFace *)OpenInterface("dos.library", 53);
460                if (myIDOS)
461                {
462                        uint32 size = 0;
463                        if (myIDOS->GetVar("ABCSH_IMPORT_LOCAL", varbuf, 8,
464                                           GVF_LOCAL_ONLY) > 0)
465                        {
466                                flags = GVF_LOCAL_ONLY;
467                        }
468                        else
469                        {
470                                flags = GVF_GLOBAL_ONLY;
471                        }
472
473                        hook->h_Entry = size_env;
474                        hook->h_Data = 0;
475
476                        myIDOS->ScanVars(hook, flags, 0);
477                        size  = ((uint32)hook->h_Data) + 1;
478
479                        myenviron = (char **)IExec->AllocVecTags(size *
480                                    sizeof(char **),
481                                    AVT_ClearWithValue,0,TAG_DONE);
482                        origenviron = myenviron;
483                        if (!myenviron)
484                        {
485                                IExec->FreeSysObject(ASOT_HOOK,hook);
486                                CloseInterface((struct Interface *)myIDOS);
487                                return;
488                        }
489                        hook->h_Entry = copy_env;
490                        hook->h_Data = myenviron;
491
492                        myIDOS->ScanVars(hook, flags, 0);
493                        IExec->FreeSysObject(ASOT_HOOK,hook);
494                        CloseInterface((struct Interface *)myIDOS);
495                }
496        }
497}
498
499void ___freeenviron()
500{
501        char **i;
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);
506        if (myIDOS)
507        {
508                myenviron = origenviron;
509
510                if (myenviron)
511                {
512                        for (i = myenviron; *i != NULL; i++)
513                        {
514                                IExec->FreeVec(*i);
515                        }
516                        IExec->FreeVec(myenviron);
517                        myenviron = NULL;
518                }
519                CloseInterface((struct Interface *)myIDOS);
520        }
521}
522
523
524/* Work around for clib2 fstat */
525#ifndef S_IFCHR
526#define S_IFCHR 0x0020000
527#endif
528
529#define SET_FLAG(u, v) ((void)((u) |= (v)))
530
531int afstat(int fd, struct stat *statb)
532{
533        int result;
534        BPTR fh;
535        int mode;
536        BOOL input;
537        /* In the first instance pass it to fstat */
538        // adebug("fd %ld ad %ld\n",fd,amigaos_get_file(fd));
539
540        if ((result = fstat(fd, statb) >= 0))
541                return result;
542
543        /* Now we've got a file descriptor but we failed to stat it */
544        /* Could be a nil: or could be a std#? */
545
546        /* if get_default_file fails we had a dud fd so return failure */
547#if !defined(__CLIB2__)
548
549        fh = amigaos_get_file(fd);
550
551        /* if nil: return failure*/
552        if (fh == 0)
553                return -1;
554
555        /* Now compare with our process Input() Output() etc */
556        /* if these were regular files sockets or pipes we had already
557         * succeeded */
558        /* so we can guess they a character special console.... I hope */
559
560        struct ExamineData *data;
561        char name[120];
562        name[0] = '\0';
563
564        data = IDOS->ExamineObjectTags(EX_FileHandleInput, fh, TAG_END);
565        if (data != NULL)
566        {
567
568                IUtility->Strlcpy(name, data->Name, sizeof(name));
569
570                IDOS->FreeDosObject(DOS_EXAMINEDATA, data);
571        }
572
573        // adebug("ad %ld '%s'\n",amigaos_get_file(fd),name);
574        mode = S_IFCHR;
575
576        if (fh == IDOS->Input())
577        {
578                input = TRUE;
579                SET_FLAG(mode, S_IRUSR);
580                SET_FLAG(mode, S_IRGRP);
581                SET_FLAG(mode, S_IROTH);
582        }
583        else if (fh == IDOS->Output() || fh == IDOS->ErrorOutput())
584        {
585                input = FALSE;
586                SET_FLAG(mode, S_IWUSR);
587                SET_FLAG(mode, S_IWGRP);
588                SET_FLAG(mode, S_IWOTH);
589        }
590        else
591        {
592                /* we got a filehandle not handle by fstat or the above */
593                /* most likely it's NIL: but lets check */
594                struct ExamineData *exd = NULL;
595                if ((exd = IDOS->ExamineObjectTags(EX_FileHandleInput, fh,
596                                                   TAG_DONE)))
597                {
598                        BOOL isnil = FALSE;
599                        if (exd->Type ==
600                                (20060920)) // Ugh yes I know nasty.....
601                        {
602                                isnil = TRUE;
603                        }
604                        IDOS->FreeDosObject(DOS_EXAMINEDATA, exd);
605                        if (isnil)
606                        {
607                                /* yep we got NIL: */
608                                SET_FLAG(mode, S_IRUSR);
609                                SET_FLAG(mode, S_IRGRP);
610                                SET_FLAG(mode, S_IROTH);
611                                SET_FLAG(mode, S_IWUSR);
612                                SET_FLAG(mode, S_IWGRP);
613                                SET_FLAG(mode, S_IWOTH);
614                        }
615                        else
616                        {
617                                IExec->DebugPrintF(
618                                    "unhandled filehandle in afstat()\n");
619                                return -1;
620                        }
621                }
622        }
623
624        memzero(statb, sizeof(statb));
625
626        statb->st_mode = mode;
627
628#endif
629        return 0;
630}
631
632BPTR amigaos_get_file(int fd)
633{
634        BPTR fh = (BPTR)NULL;
635        if (!(fh = _get_osfhandle(fd)))
636        {
637                switch (fd)
638                {
639                case 0:
640                        fh = IDOS->Input();
641                        break;
642                case 1:
643                        fh = IDOS->Output();
644                        break;
645                case 2:
646                        fh = IDOS->ErrorOutput();
647                        break;
648                default:
649                        break;
650                }
651        }
652        return fh;
653}
654
655/*########################################################################*/
656
657#define LOCK_START 0xFFFFFFFFFFFFFFFELL
658#define LOCK_LENGTH 1LL
659
660// No wait forever option so lets wait for a loooong time.
661#define TIMEOUT 0x7FFFFFFF
662
663int amigaos_flock(int fd, int oper)
664{
665        BPTR fh;
666        int32 success = -1;
667
668        if (!(fh = amigaos_get_file(fd)))
669        {
670                errno = EBADF;
671                return -1;
672        }
673
674        switch (oper)
675        {
676        case LOCK_SH:
677        {
678                if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
679                                     REC_SHARED | RECF_DOS_METHOD_ONLY,
680                                     TIMEOUT))
681                {
682                        success = 0;
683                }
684                break;
685        }
686        case LOCK_EX:
687        {
688                if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
689                                     REC_EXCLUSIVE | RECF_DOS_METHOD_ONLY,
690                                     TIMEOUT))
691                {
692                        success = 0;
693                }
694                break;
695        }
696        case LOCK_SH | LOCK_NB:
697        {
698                if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
699                                     REC_SHARED_IMMED | RECF_DOS_METHOD_ONLY,
700                                     TIMEOUT))
701                {
702                        success = 0;
703                }
704                else
705                {
706                        errno = EWOULDBLOCK;
707                }
708                break;
709        }
710        case LOCK_EX | LOCK_NB:
711        {
712                if (IDOS->LockRecord(fh, LOCK_START, LOCK_LENGTH,
713                                     REC_EXCLUSIVE_IMMED | RECF_DOS_METHOD_ONLY,
714                                     TIMEOUT))
715                {
716                        success = 0;
717                }
718                else
719                {
720                        errno = EWOULDBLOCK;
721                }
722                break;
723        }
724        case LOCK_UN:
725        {
726                if (IDOS->UnLockRecord(fh, LOCK_START, LOCK_LENGTH))
727                {
728                        success = 0;
729                }
730                break;
731        }
732        default:
733        {
734                errno = EINVAL;
735                return -1;
736        }
737        }
738        return success;
739}
740