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
OpenInterface(CONST_STRPTR libname,uint32 libver)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
CloseInterface(struct Interface * iface)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
___openinterfaces()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
___closeinterfaces()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
__myrc(char * arg)105 int __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
myruncommand(BPTR seglist,int stack,char * command,int length,char ** envp)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
mystrdup(const char * s)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->AllocVecTags(size, TAG_DONE)))
158 {
159 memmove(result, s, size);
160 }
161 return result;
162 }
163
164 unsigned int pipenum = 0;
165
pipe(int filedes[2])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
fork(void)196 int fork(void)
197 {
198 fprintf(stderr, "Can not bloody fork\n");
199 errno = ENOMEM;
200 return -1;
201 }
202
wait(int * status)203 int wait(__attribute__((unused))int *status)
204 {
205 fprintf(stderr, "No wait try waitpid instead\n");
206 errno = ECHILD;
207 return -1;
208 }
209
convert_path_a2u(const char * filename)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 }
convert_path_u2a(const char * filename)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 struct SignalSemaphore environ_sema;
244 struct SignalSemaphore popen_sema;
245
246
amigaos4_init_environ_sema()247 void amigaos4_init_environ_sema()
248 {
249 IExec->InitSemaphore(&environ_sema);
250 IExec->InitSemaphore(&popen_sema);
251 }
252
amigaos4_obtain_environ()253 void amigaos4_obtain_environ()
254 {
255 IExec->ObtainSemaphore(&environ_sema);
256 }
257
amigaos4_release_environ()258 void amigaos4_release_environ()
259 {
260 IExec->ReleaseSemaphore(&environ_sema);
261 }
262
createvars(char ** envp)263 static 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
309 struct command_data
310 {
311 STRPTR args;
312 BPTR seglist;
313 struct Task *parent;
314 };
315
316
myexecvp(bool isperlthread,const char * filename,char * argv[])317 int 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
myexecv(bool isperlthread,const char * path,char * argv[])387 int myexecv(bool isperlthread, const char *path, char *argv[])
388 {
389 return myexecve(isperlthread, path, argv, myenviron);
390 }
391
myexecl(bool isperlthread,const char * path,...)392 int 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
pause(void)412 int pause(void)
413 {
414 fprintf(stderr, "Pause not implemented\n");
415
416 errno = EINTR;
417 return -1;
418 }
419
size_env(struct Hook * hook,APTR userdata,struct ScanVarsMsg * message)420 uint32 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
copy_env(struct Hook * hook,APTR userdata,struct ScanVarsMsg * message)429 uint32 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
___makeenviron()449 void ___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
___freeenviron()499 void ___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
afstat(int fd,struct stat * statb)531 int 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
amigaos_get_file(int fd)632 BPTR 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
amigaos_flock(int fd,int oper)663 int 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