Update to lasso handling. Adjust scroll amount based on difference between mouse...
[AROS.git] / rom / dos / createnewproc.c
blob86aa83db5e48bcfa42e45f1c39797e9dca3d4e7b
1 /*
2 Copyright © 1995-2007, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Create a new process
6 Lang: English
7 */
8 #include <exec/memory.h>
9 #include <exec/lists.h>
10 #include <proto/exec.h>
11 #include <dos/dosextens.h>
12 #include <dos/filesystem.h>
13 #include <dos/dostags.h>
14 #include <dos/stdio.h>
15 #define __DOS_NOLIBBASE__
16 #include <proto/dos.h>
17 #include <utility/tagitem.h>
18 #include <aros/symbolsets.h>
19 #include <proto/utility.h>
20 #include "dos_intern.h"
21 #include LC_LIBDEFS_FILE
22 #include <string.h>
24 static void KillCurrentProcess(void);
25 struct Process *AddProcess(struct Process *process, STRPTR argPtr,
26 ULONG argSize, APTR initialPC, APTR finalPC, struct DosLibrary *DOSBase);
28 static void freeLocalVars(struct Process *process, struct DosLibrary *DOSBase);
30 BOOL copyVars(struct Process *fromProcess, struct Process *toProcess, struct DosLibrary * DOSBase);
32 void internal_ChildWait(struct Task *task, struct DosLibrary * DOSBase);
33 void internal_ChildFree(APTR tid, struct DosLibrary * DOSBase);
35 #include <aros/debug.h>
37 /* Temporary macro */
38 #define P(x)
39 /*****************************************************************************
41 NAME */
42 #include <proto/dos.h>
44 AROS_LH1(struct Process *, CreateNewProc,
46 /* SYNOPSIS */
47 AROS_LHA(struct TagItem *, tags, D1),
49 /* LOCATION */
50 struct DosLibrary *, DOSBase, 83, Dos)
52 /* FUNCTION
53 Create a new process using the tagitem array.
55 INPUTS
56 tags - information on the new process.
58 RESULT
59 Pointer to the new process or NULL on error.
61 NOTES
63 EXAMPLE
65 BUGS
67 SEE ALSO
69 INTERNALS
71 *****************************************************************************/
73 AROS_LIBFUNC_INIT
75 /* Allocated resources */
76 struct Process *process = NULL;
77 BPTR input = 0, output = 0, ces = 0, curdir = 0, homedir = 0;
78 STRPTR stack = NULL, name = NULL, argptr = NULL;
79 ULONG namesize = 0, argsize = 0;
80 struct MemList *memlist = NULL;
81 struct CommandLineInterface *cli = NULL;
82 struct Process *me = (struct Process *)FindTask(NULL);
83 STRPTR s;
84 ULONG old_sig;
86 /* TODO: NP_CommandName, NP_ConsoleTask, NP_NotifyOnDeath */
88 #define TAGDATA_NOT_SPECIFIED ~0ul
90 struct TagItem defaults[]=
92 /* 0 */ { NP_Seglist , 0 },
93 /* 1 */ { NP_Entry , (IPTR)NULL },
94 /* 2 */ { NP_Input , TAGDATA_NOT_SPECIFIED },
95 /* 3 */ { NP_CloseInput , 1 },
96 /* 4 */ { NP_Output , TAGDATA_NOT_SPECIFIED },
97 /* 5 */ { NP_CloseOutput , 1 },
98 /* 6 */ { NP_Error , TAGDATA_NOT_SPECIFIED },
99 /* 7 */ { NP_CloseError , 1 },
100 /* 8 */ { NP_CurrentDir , TAGDATA_NOT_SPECIFIED },
101 /* 9 */ { NP_StackSize , AROS_STACKSIZE },
102 /*10 */ { NP_Name , (IPTR)"New Process" },
103 /*11 */ { NP_Priority , me->pr_Task.tc_Node.ln_Pri },
104 /*12 */ { NP_Arguments , (IPTR)NULL },
105 /*13 */ { NP_Cli , 0 },
106 /*14 */ { NP_UserData , (IPTR)NULL },
107 /*15 */ { NP_ExitCode , (IPTR)NULL },
108 /*16 */ { NP_ExitData , (IPTR)NULL },
109 /*17 */ { NP_WindowPtr , (IPTR)NULL }, /* Default: default public screen */
110 /*18 */ { NP_CopyVars , (IPTR)TRUE },
111 /*19 */ { NP_Synchronous , (IPTR)FALSE },
112 /*20 */ { NP_FreeSeglist , (IPTR)TRUE },
113 /*21 */ { NP_HomeDir , TAGDATA_NOT_SPECIFIED },
114 /*22 */ { NP_Path , TAGDATA_NOT_SPECIFIED }, /* Default: copy path from parent */
115 /*23 */ { NP_NotifyOnDeath , (IPTR)FALSE },
116 { TAG_END , 0 }
119 /* C has no exceptions. This is a simple replacement. */
120 #define ERROR_IF(a) if(a) goto error /* Throw a generic error. */
121 #define ENOMEM_IF(a) if (a) goto enomem /* Throw out of memory. */
122 /* Inherit the parent process' stacksize if possible */
123 if (__is_process(me))
125 struct CommandLineInterface *cli = Cli();
127 if (cli)
129 LONG parentstack = cli->cli_DefaultStack * CLI_DEFAULTSTACK_UNIT;
131 if (parentstack > AROS_STACKSIZE)
133 defaults[9].ti_Data = parentstack;
138 ApplyTagChanges(defaults, tags);
140 /* If both the seglist and the entry are specified, make sure that the entry resides in the seglist */
141 if (defaults[0].ti_Data && defaults[1].ti_Data)
143 BPTR seg;
145 for (seg = (BPTR) defaults[0].ti_Data; seg; seg = *(BPTR *)BADDR(seg))
149 (UBYTE *)defaults[1].ti_Data >= (UBYTE *)BADDR(seg) &&
150 (UBYTE *)defaults[1].ti_Data <= ((UBYTE *)BADDR(seg) + *((ULONG *)BADDR(seg) - 1) - sizeof(BPTR))
153 break;
157 if (!seg)
158 return NULL;
161 process = (struct Process *)AllocMem(sizeof(struct Process),
162 MEMF_PUBLIC | MEMF_CLEAR);
163 ENOMEM_IF(process == NULL);
165 /* Do this early to ease implementation of failure code */
166 NEWLIST((struct List *)&process->pr_LocalVars);
168 /* We need a minimum stack to handle interrupt contexts */
169 if (defaults[9].ti_Data < AROS_STACKSIZE)
171 defaults[9].ti_Data = AROS_STACKSIZE;
174 stack = AllocMem(defaults[9].ti_Data, MEMF_PUBLIC);
175 ENOMEM_IF(stack == NULL);
177 s = (STRPTR)defaults[10].ti_Data;
179 while (*s++);
181 namesize = s - (STRPTR)defaults[10].ti_Data;
183 name = AllocMem(namesize, MEMF_PUBLIC);
184 ENOMEM_IF(name == NULL);
186 /* NP_Arguments */
187 s = (STRPTR)defaults[12].ti_Data;
189 if (s != NULL)
191 while(*s++);
193 argsize = s - (STRPTR)defaults[12].ti_Data;
194 argptr = (STRPTR)AllocVec(argsize, MEMF_PUBLIC);
195 ENOMEM_IF(argptr == NULL);
198 memlist = AllocMem(sizeof(struct MemList) + 2*sizeof(struct MemEntry),
199 MEMF_ANY);
200 ENOMEM_IF(memlist == NULL);
202 /* NP_Cli */
203 if (defaults[13].ti_Data != 0)
205 BPTR *oldpath = NULL;
207 /* Don't forget to pass tags to AllocDosObject() */
208 cli = (struct CommandLineInterface *)AllocDosObject(DOS_CLI, tags);
209 ENOMEM_IF(cli == NULL);
211 cli->cli_DefaultStack = (defaults[9].ti_Data + CLI_DEFAULTSTACK_UNIT - 1) / CLI_DEFAULTSTACK_UNIT;
213 if (__is_process(me))
215 struct CommandLineInterface *oldcli = Cli();
217 if (oldcli != NULL)
219 LONG oldlen = AROS_BSTR_strlen(oldcli->cli_Prompt);
220 LONG newlen = GetTagData(ADO_PromptLen, 255, tags);
222 oldpath = BADDR(oldcli->cli_CommandDir);
224 CopyMem(BADDR(oldcli->cli_Prompt), BADDR(cli->cli_Prompt), (newlen<oldlen?newlen:oldlen) + 1);
229 if (defaults[22].ti_Data != TAGDATA_NOT_SPECIFIED)
231 cli->cli_CommandDir = (BPTR) defaults[22].ti_Data;
233 else
235 BPTR *nextpath,
236 *newpath = &cli->cli_CommandDir;
238 while (oldpath != NULL)
240 nextpath = AllocVec(2*sizeof(BPTR), MEMF_CLEAR);
241 ENOMEM_IF(nextpath == NULL);
243 newpath[0] = MKBADDR(nextpath);
244 nextpath[1] = DupLock(oldpath[1]);
245 ERROR_IF(!nextpath[1]);
247 newpath = nextpath;
248 oldpath = BADDR(oldpath[0]);
253 /* NP_Input */
255 if (defaults[2].ti_Data == TAGDATA_NOT_SPECIFIED)
257 if (__is_process(me))
259 input = Open("NIL:", MODE_OLDFILE);
260 ERROR_IF(!input);
262 defaults[2].ti_Data = (IPTR)input;
264 else
266 defaults[2].ti_Data = 0;
270 /* NP_Output */
272 if (defaults[4].ti_Data == TAGDATA_NOT_SPECIFIED)
274 if (__is_process(me))
276 output = Open("NIL:", MODE_NEWFILE);
277 ERROR_IF(!output);
279 defaults[4].ti_Data = (IPTR)output;
281 else
283 defaults[4].ti_Data = 0;
287 /* NP_Error */
289 if (defaults[6].ti_Data == TAGDATA_NOT_SPECIFIED)
291 if (__is_process(me))
293 ces = Open("NIL:", MODE_NEWFILE);
294 ERROR_IF(!ces);
296 defaults[6].ti_Data = (IPTR)ces;
298 else
300 defaults[6].ti_Data = 0;
304 if (defaults[6].ti_Data)
306 /* unbuffered to conform to widespread clib behavior */
307 SetVBuf((BPTR) defaults[6].ti_Data, NULL, BUF_NONE, -1);
310 /* NP_CurrentDir */
312 if (defaults[8].ti_Data == TAGDATA_NOT_SPECIFIED)
314 if (__is_process(me) && me->pr_CurrentDir)
316 curdir = Lock("", SHARED_LOCK);
317 ERROR_IF(!curdir);
319 defaults[8].ti_Data = (IPTR)curdir;
321 else
323 defaults[8].ti_Data = 0;
327 /* NP_HomeDir */
329 if (defaults[21].ti_Data == TAGDATA_NOT_SPECIFIED)
331 defaults[21].ti_Data = 0;
333 if (__is_process(me))
335 if (me->pr_HomeDir)
337 homedir = DupLock(me->pr_HomeDir);
338 ERROR_IF(!homedir);
340 defaults[21].ti_Data = (IPTR)homedir;
345 CopyMem((APTR)defaults[10].ti_Data, name, namesize);
346 CopyMem((APTR)defaults[12].ti_Data, argptr, argsize);
348 process->pr_Task.tc_Node.ln_Type = NT_PROCESS;
349 process->pr_Task.tc_Node.ln_Name = name;
350 process->pr_Task.tc_Node.ln_Pri = defaults[11].ti_Data;
351 process->pr_Task.tc_SPLower = stack;
352 process->pr_Task.tc_SPUpper = stack + defaults[9].ti_Data;
354 /* process->pr_ReturnAddr; */
355 NEWLIST(&process->pr_Task.tc_MemEntry);
357 memlist->ml_NumEntries = 3;
358 memlist->ml_ME[0].me_Addr = process;
359 memlist->ml_ME[0].me_Length = sizeof(struct Process);
360 memlist->ml_ME[1].me_Addr = stack;
361 memlist->ml_ME[1].me_Length = defaults[9].ti_Data;
362 memlist->ml_ME[2].me_Addr = name;
363 memlist->ml_ME[2].me_Length = namesize;
365 AddHead(&process->pr_Task.tc_MemEntry, &memlist->ml_Node);
367 process->pr_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
368 process->pr_MsgPort.mp_Flags = PA_SIGNAL;
369 process->pr_MsgPort.mp_SigBit = SIGB_DOS;
370 process->pr_MsgPort.mp_SigTask = process;
372 NEWLIST(&process->pr_MsgPort.mp_MsgList);
374 process->pr_SegList = (BPTR)defaults[0].ti_Data;
375 process->pr_StackSize = defaults[9].ti_Data;
376 process->pr_GlobVec = NULL; /* Unused BCPL crap */
377 process->pr_StackBase = MKBADDR(process->pr_Task.tc_SPUpper);
378 process->pr_Result2 = 0;
379 process->pr_CurrentDir = (BPTR)defaults[8].ti_Data;
380 process->pr_CIS = (BPTR)defaults[2].ti_Data;
381 process->pr_COS = (BPTR)defaults[4].ti_Data;
382 process->pr_CES = (BPTR)defaults[6].ti_Data;
383 process->pr_Task.tc_UserData = (APTR)defaults[14].ti_Data;
385 /* process->pr_ConsoleTask=; */
386 /* process->pr_FileSystemTask=; */
387 process->pr_CLI = MKBADDR(cli);
389 /* Set the name of this program */
390 internal_SetProgramName(cli, name, DOSBase);
391 D(bug("Calling internal_SetProgramName() with name = %s\n", name));
393 process->pr_PktWait = NULL;
394 process->pr_WindowPtr = (struct Window *)defaults[17].ti_Data;
395 process->pr_HomeDir = (BPTR)defaults[21].ti_Data;
396 process->pr_Flags = (defaults[3].ti_Data ? PRF_CLOSEINPUT : 0) |
397 (defaults[5].ti_Data ? PRF_CLOSEOUTPUT : 0) |
398 (defaults[7].ti_Data ? PRF_CLOSEERROR : 0) |
399 (defaults[13].ti_Data ? PRF_FREECLI : 0) |
400 (defaults[19].ti_Data ? PRF_SYNCHRONOUS : 0) |
401 (defaults[20].ti_Data ? PRF_FREESEGLIST : 0) |
402 (defaults[23].ti_Data ? PRF_NOTIFYONDEATH : 0) |
403 PRF_FREEARGS | PRF_FREECURRDIR;
404 process->pr_ExitCode = (APTR)defaults[15].ti_Data;
405 process->pr_ExitData = defaults[16].ti_Data;
406 process->pr_Arguments = argptr;
408 if ((BOOL)defaults[18].ti_Data) /* NP_CopyVars */
410 BOOL res = copyVars(me, process, DOSBase);
412 ENOMEM_IF(res == FALSE);
415 process->pr_ShellPrivate = 0;
418 if (defaults[19].ti_Data)
420 me->pr_Flags |= PRF_WAITINGFORCHILD;
422 old_sig = SetSignal(0L, SIGF_SINGLE) & SIGF_SINGLE;
425 /* argsize variable includes trailing 0 byte, but is supposed
426 not to. */
428 if (argsize) argsize--;
433 AddProcess
435 process, argptr, argsize,
436 defaults[1].ti_Data ?
437 (APTR)defaults[1].ti_Data:
438 (APTR)((BPTR *)BADDR(defaults[0].ti_Data) + 1),
439 KillCurrentProcess, DOSBase
443 /* NP_Synchronous */
444 if (defaults[19].ti_Data)
446 P(kprintf("Calling ChildWait()\n"));
447 internal_ChildWait(FindTask(NULL), DOSBase);
448 P(kprintf("Returned from ChildWait()\n"));
451 goto end;
454 /* Fall through */
455 enomem:
456 if (__is_process(me))
458 SetIoErr(ERROR_NO_FREE_STORE);
461 freeLocalVars(process, DOSBase);
463 error:
464 if (cli != NULL)
466 FreeDosObject(DOS_CLI, cli);
469 if (homedir != NULL)
471 UnLock(homedir);
474 if (curdir != NULL)
476 UnLock(curdir);
479 if (output != NULL)
481 Close(output);
484 if (input != NULL)
486 Close(input);
489 if (ces != NULL)
491 Close(ces);
494 if (argptr != NULL)
496 FreeVec(argptr);
499 if (memlist != NULL)
501 FreeMem(memlist, sizeof(struct MemList) + 2*sizeof(struct MemEntry));
504 if (name != NULL)
506 FreeMem(name, namesize);
509 if (stack != NULL)
511 FreeMem(stack, defaults[9].ti_Data);
514 if (process != NULL)
516 FreeMem(process, sizeof(struct Process));
518 process = NULL;
521 end:
523 if (defaults[19].ti_Data)
524 SetSignal(SIGF_SINGLE, old_sig);
526 return process;
528 AROS_LIBFUNC_EXIT
529 } /* CreateNewProc */
535 static void freeLocalVars(struct Process *process, struct DosLibrary *DOSBase)
537 struct LocalVar *varNode;
538 struct Node *tempNode;
540 ForeachNodeSafe(&process->pr_LocalVars,
541 varNode, tempNode)
543 P(kprintf("Freeing variable %s with value %s at %p\n",
544 varNode->lv_Node.ln_Name, varNode->lv_Value, varNode));
545 FreeMem(varNode->lv_Value, varNode->lv_Len);
546 Remove((struct Node *)varNode);
547 FreeVec(varNode);
552 void internal_ChildWait(struct Task *task, struct DosLibrary * DOSBase)
554 while (((struct Process *)task)->pr_Flags & PRF_WAITINGFORCHILD)
555 Wait(SIGF_SINGLE);
559 void internal_ChildFree(APTR tid, struct DosLibrary * DOSBase)
561 struct Task *task = (struct Task *)tid;
562 struct Process *parent = (struct Process *)(GetETask(task)->et_Parent);
564 D(bug("Awakening the parent task %p (called %s)\n", parent, parent->pr_Task.tc_Node.ln_Name));
566 parent->pr_Flags &= ~PRF_WAITINGFORCHILD;
567 Signal(&parent->pr_Task, SIGF_SINGLE);
571 BOOL copyVars(struct Process *fromProcess, struct Process *toProcess, struct DosLibrary * DOSBase)
573 /* We must have variables to copy... */
574 if (__is_process(fromProcess))
576 struct LocalVar *varNode;
577 struct LocalVar *newVar;
579 /* We use the same strategy as in the ***Var() functions */
580 ForeachNode(&fromProcess->pr_LocalVars, varNode)
582 LONG copyLength = strlen(varNode->lv_Node.ln_Name) + 1 +
583 sizeof(struct LocalVar);
585 newVar = (struct LocalVar *)AllocVec(copyLength,
586 MEMF_PUBLIC | MEMF_CLEAR);
587 if (newVar == NULL)
588 return FALSE;
590 CopyMem(varNode, newVar, copyLength);
591 newVar->lv_Node.ln_Name = (char *)newVar +
592 sizeof(struct LocalVar);
593 P(kprintf("Variable with name %s copied.\n",
594 newVar->lv_Node.ln_Name));
596 if (varNode->lv_Len)
598 newVar->lv_Value = AllocMem(varNode->lv_Len, MEMF_PUBLIC);
600 if (newVar->lv_Value == NULL)
602 /* Free variable node before shutting down */
603 FreeVec(newVar);
605 return FALSE;
608 CopyMem(varNode->lv_Value, newVar->lv_Value, varNode->lv_Len);
611 AddTail((struct List *)&toProcess->pr_LocalVars,
612 (struct Node *)newVar);
616 return TRUE;
619 #warning Q: Is there a better way to pass DOSBase to KillCurrentProcess ?
620 static struct DosLibrary *DOSBase;
622 static int SetDosBase(LIBBASETYPEPTR __DOSBase)
624 D(bug("SetDosBase\n"));
625 DOSBase = __DOSBase;
626 return TRUE;
629 /* At pri -1 so it is executed before the DosInit in dos_init.c */
630 ADD2INITLIB(SetDosBase, -1)
632 static void KillCurrentProcess(void)
634 struct Process *me;
636 me = (struct Process *)FindTask(NULL);
638 /* Call user defined exit function before shutting down. */
639 if (me->pr_ExitCode != NULL)
642 The Ralph Bebel's guru book says that pr_ExitCode
643 is passed the process return code in D0 and pr_ExitData in D1,
644 but the Matt Dillon's DICE C implementation of vfork shows that
645 those parameters are passed also on the stack.
647 The AROS macros for functions with register parameters don't
648 support both register and stack parameters at once, so we use
649 the stack only. This oughta be fixed somehow.
651 me->pr_ExitCode(me->pr_Task.tc_UserData, me->pr_ExitData);
654 P(kprintf("Deleting local variables\n"));
656 /* Clean up */
657 freeLocalVars(me, DOSBase);
659 P(kprintf("Closing input stream\n"));
661 if (me->pr_Flags & PRF_CLOSEINPUT)
663 Close(me->pr_CIS);
666 P(kprintf("Closing output stream\n"));
668 if (me->pr_Flags & PRF_CLOSEOUTPUT)
670 Close(me->pr_COS);
673 P(kprintf("Closing error stream\n"));
675 if (me->pr_Flags & PRF_CLOSEERROR)
677 Close(me->pr_CES);
680 P(kprintf("Freeing arguments\n"));
682 if (me->pr_Flags & PRF_FREEARGS)
684 FreeVec(me->pr_Arguments);
687 P(kprintf("Unloading segment\n"));
689 if (me->pr_Flags & PRF_FREESEGLIST)
691 UnLoadSeg(me->pr_SegList);
694 P(kprintf("Unlocking current dir\n"));
696 if (me->pr_Flags & PRF_FREECURRDIR)
698 UnLock(me->pr_CurrentDir);
701 P(kprintf("Unlocking home dir\n"));
702 UnLock(me->pr_HomeDir);
704 P(kprintf("Freeing cli structure\n"));
706 if (me->pr_Flags & PRF_FREECLI)
708 FreeDosObject(DOS_CLI, BADDR(me->pr_CLI));
711 /* To implement NP_Synchronous and NP_NotifyOnDeath I need Child***()
712 here */
714 // if(me->pr_Flags & PRF_NOTIFYONDEATH)
715 // Signal(GetETask(me)->iet_Parent, SIGF_CHILD);
717 if (me->pr_Flags & PRF_SYNCHRONOUS)
719 P(kprintf("Calling ChildFree()\n"));
721 // ChildStatus(me);
722 internal_ChildFree(me, DOSBase);
725 removefromrootnode(me, DOSBase);
727 RemTask(NULL);
729 CloseLibrary((struct Library * )DOSBase);