/*-- AutoRev header do NOT edit!
*
*   Program         :   dcback.c
*   Copyright       :   © 1991 Jaba Development
*   Author          :   Jan van den Baard
*   Creation Date   :   16-Nov-91
*   Current version :   1.2
*   Translator      :   DICE v2.6.
*
*   REVISION HISTORY
*
*   Date          Version         Comment
*   ---------     -------         ------------------------------------------
*   29-Feb-92     1.2             Now uses "SetArgStr()".
*   23-Nov-91     1.1             Command line promting now allowed.
*   16-Nov-91     1.0             Auto detach 2.0 argument parsing startup.
*
*-- REV_END --*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/libraries.h>
#include <exec/alerts.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <workbench/startup.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <lib/misc.h>

/*
 * --- These globals MUST be defined somewhere in your source!
 */
extern  UBYTE   *_procname;         /* process name */
extern  UBYTE   *_template;         /* commandline template */
extern  UBYTE   *_exthelp;          /* extended command line help */
extern  LONG     _stack;            /* process stack */
extern  LONG     _priority;         /* process priority */
extern  LONG     _BackGroundIO;     /* open background io channel */

/*
 * --- externally referenced structures.
 */
extern struct ExecBase  *SysBase;   /* to check for V36 minimum */
extern struct WBStartup *_WBMsg;    /* guess what.... */

/*
 * --- This is how both main() and wbmain() are called.
 */
extern long main( long, long * );
extern long wbmain( struct WBStartup * );

/*
 * --- Proto for the exit routine
 */
long exit( long );

/*
 * --- Data that isn't chucked in the BSS section.
 */
BPTR             _DetachDir      =       0l;    /* process current dir */
BPTR             _Backstdout     =       0l;    /* io channel */
UBYTE            _IsDetached     =       FALSE; /* detached flag */
ULONG           *_DosArray       =       0l;    /* commandline array */
UWORD            _NumArgs        =       0l;    /* max # of args */
UBYTE           *_Arguments      =       0l;    /* argument line */
struct RDArgs   *_DosSource      =       0l;    /* for the arguments */

/*
 * --- New _main() which checks if it is run from the shell or
 * --- the workbench.
 */
__stkargs long _main( long len, char *lin )
{
    struct Process              *ThisTask;
    struct CommandLineInterface *cli;
    struct MemList              *mlist;
    BPTR                        *SegList, *TmpSeg, StdOut;
    ULONG                        SegCount = 0l, ArgCount = 0l;
    BYTE                         DetachRun = FALSE;
    UBYTE                       *Tmp;
    ULONG                        alert;

   /*
    * --- First we must see wether we are running
    * --- under 2.0 or not. We need 2.0 for the
    * --- commandline parsing!
    */
    if ((( struct Library *)SysBase)->lib_Version < 36 )
        return( RETURN_FAIL );

   /*
    * --- Get a pointer to our Task (Process)
    */
    ThisTask = ( struct Process * )SysBase->ThisTask;

   /*
    * --- When run from the shell the process has
    * --- a CLI structure. If this is the case
    * --- the 'DetachRun' flag is set to TRUE so we
    * --- we know that the program still has to be
    * --- detached.
    */
    if ( ThisTask->pr_CLI )
        DetachRun = TRUE;

   /*
    * --- When either the 'DetachRun' or '_IsDetached' flag
    * --- is TRUE it means that we are run from the shell.
    */
    if ( DetachRun || _IsDetached ) {
       /*
        * --- Check if we are detached yet...
        */
        if ( ! _IsDetached ) {
           /*
            * --- We arn't detached yet so we must parse
            * --- the arguments given to us by the shell.
            */
            if ( Tmp = _template ) {
               /*
                * --- There is a template string supplied
                */
                if ( *Tmp ) {
                   /*
                    * --- The template string even got characters in it!
                    * --- Now we must count the maximum number of arguments
                    * --- we can expect. This is done by counting the
                    * --- commas in the template string and then adding
                    * --- one to the result
                    */
                    while ( *Tmp ) {
                        if ( *Tmp == ',' ) ArgCount++;
                        Tmp++;
                    }
                    ArgCount++;
                }
            }

           /*
            * --- Allocate and parse the arguments.
            */
            if ( ArgCount ) {
               /*
                * --- There are arguments to be expected.
                */
                _NumArgs   = ArgCount; /* save argument count */
                _Arguments = lin;      /* save the argument line pointer */

               /*
                * --- Allocate our RDArgs structure.
                */
                if ( ! ( _DosSource = ( struct RDArgs * )AllocVec((long)sizeof( struct RDArgs ), MEMF_PUBLIC | MEMF_CLEAR ))) {
                    alert = AT_Recovery | AG_NoMemory | AO_Unknown;
                    goto suError;
                }

               /*
                * --- Setup the RDArgs structure for extended help.
                */
                _DosSource->RDA_ExtHelp          = _exthelp;

               /*
                * --- Try to allocate the array that will hold
                * --- the result of the parse.
                */
                if ( ! (_DosArray = (ULONG *)AllocVec( ArgCount * sizeof( LONG ), MEMF_PUBLIC | MEMF_CLEAR ))) {
                    alert = AT_Recovery | AG_NoMemory | AO_Unknown;
                    goto suError;
                }

               /*
                * --- Try to parse the arguments from STDIN.
                */
                if ( ! ReadArgs( _template, _DosArray, _DosSource )) {
                    if ( StdOut = Output())
                        Write( StdOut, "Bad Args\n", 9l );
                    alert = 0l;
                    goto suError;
                }
            }
        }

       /*
        * --- This code only get's executed when the program
        * --- was started from the shell and isn't detached yet.
        */
        if ( cli = ( struct CommandLineInterface * )BADDR( ThisTask->pr_CLI )) {
           /*
            * --- Get a copy of the lock to the current directory
            */
            CurrentDir( _DetachDir = CurrentDir( 0l ));
            _DetachDir  = DupLock( _DetachDir );

           /*
            * --- Mark us as detached ( a little premature but so what.. )
            */
            _IsDetached = TRUE;

           /*
            * --- Open a io channel if requested
            */
            if ( _BackGroundIO )
                _Backstdout = Open( "*", MODE_OLDFILE );

           /*
            * --- Sanity check. If the stack is 0 then make the stack 4096
            */
            if ( ! _stack )
                _stack = 4096l;

           /*
            * --- Try to launch us as a non-cli process
            */
            if ( CreateProc(  _procname, _priority, cli->cli_Module, _stack )) {
               /*
                * --- Don't rip the code out from under us
                */
                cli->cli_Module = 0l;

               /*
                * --- We must make sure that dos doesn't deallocate
                * --- our arguments before we get a chance to use
                * --- them.
                */
                SetArgStr( 0l );

                return ( RETURN_OK );
            } else {
                alert = AT_Recovery | AG_ProcCreate | AO_Unknown;
                goto suError;
            }
        } else {
           /*
            * --- Getting here means that we now run as a non-cli
            * --- process initially started from a shell.
            */
            if ( ! strcmp( ThisTask->pr_Task.tc_Node.ln_Name, _procname )) {

               /*
                * --- Now we are running detached we must make sure that
                * --- dos deallocates the commandline (if there) when
                * --- we exit.
                */
                SetArgStr( _Arguments );

               /*
                * --- Now we must make sure that we get deallocated
                * --- when the program exits. This is done by creating
                * --- a MemList with pointers to all our segments in
                * --- it and then AddTail'ing this to the Task it's
                * --- MemList which automatically get's deallocated
                * --- by the system.
                */
                SegList = ( BPTR * )BADDR( ThisTask->pr_SegList );
                SegList = ( BPTR * )BADDR( SegList[3] );
                TmpSeg  = SegList;

               /*
                * --- Count the number of segments we have.
                */
                while ( SegList ) {
                    SegList = ( BPTR * )BADDR( *SegList );
                    SegCount++;
                }

               /*
                * --- Try to allocate a MemList with enough MemEntry's
                * --- I didn't use AllocVec because the system deallocates
                * --- this structure itself with FreeMem() and not FreeVec()
                * --- (I think........)
                */
                if ( mlist = ( struct MemList * )AllocMem( sizeof( struct MemList ) + sizeof( struct MemEntry ) * ( SegCount - 1 ), MEMF_PUBLIC | MEMF_CLEAR )) {

                    SegList  = TmpSeg;
                    mlist->ml_NumEntries = SegCount;
                    SegCount = 0l;

                   /*
                    * --- Initialize all MemEntries
                    */
                    while ( SegList ) {
                        mlist->ml_me[ SegCount ].me_Addr   = (APTR)&SegList[ -1 ];
                        mlist->ml_me[ SegCount ].me_Length = SegList[ -1 ];
                        SegList = ( BPTR * )BADDR( *SegList );
                        SegCount++;
                    }

                   /*
                    * --- Add our MemList to the Task it's MemList.
                    */
                    AddTail( &ThisTask->pr_Task.tc_MemEntry, &mlist->ml_Node );

                   /*
                    * --- Set our current dir
                    */
                    CurrentDir( _DetachDir );

                   /*
                    * --- If an io channel was requested we must
                    * --- initialize the proper stdio structures
                    */
                    if ( _BackGroundIO ) {
                        ThisTask->pr_COS = _Backstdout;

                        _IoStaticFD[1].fd_Fh    =   _Backstdout;
                        _IoStaticFD[1].fd_Flags =   O_RDWR | O_NOCLOSE | O_ISOPEN;
                        _IoStaticFD[2].fd_Fh    =   _Backstdout;
                        _IoStaticFD[2].fd_Flags =   O_RDWR | O_NOCLOSE | O_ISOPEN;

                        _finitdesc( stdout,  1, __SIF_WRITE | __SIF_NOFREE );
                        _finitdesc( stderr,  2, __SIF_WRITE | __SIF_NOFREE );
                    }

                   /*
                    * --- Now we just exit with whatever main()
                    * --- returns to us.
                    */
                    exit( main( (long)_NumArgs, _DosArray ));

                } else {
                    alert = AT_Recovery | AG_NoMemory | AO_Unknown;
                    goto suError;
                }
            }
        }
    } else {
       /*
        * --- Getting here means we have been started from the workbench.
        */
        if ( _WBMsg->sm_ArgList )
           /*
            * --- CurrentDir to the directory we are started from
            */
            CurrentDir( _WBMsg->sm_ArgList->wa_Lock );

       /*
        * --- Now we just exit with whatever wbmain()
        * --- returns to us.
        */
        exit( wbmain( _WBMsg ));
    }

   /*
    * --- This is never called! It just brings in the code that
    * --- waits for the Workbench message for when we are started
    * --- from the workbench! See wbmain.a!
    */
    _waitwbmsg();

   /*
    * --- Here's where we land when something failed.
    * --- This cleans up the mess we made except when we
    * --- got here because the MemList could not be
    * --- allocated! If that's the case the segments wont
    * --- be deallocated. This shouldn't be a problem
    * --- because when the MemList cannot be allocated anymore
    * --- your system is really fuc#@$$##@ up!
    */
suError:
    if ( _DosSource ) {
        FreeArgs( _DosSource );
        FreeVec( _DosSource );
    }
    if ( _DosArray )    FreeVec( _DosArray );
    if ( _DetachDir )   UnLock( _DetachDir );
    if ( alert )        Alert( alert );
    return( RETURN_FAIL );
}

#include <lib/atexit.h>

typedef struct Process Process;

AtExit *_ExitBase;

/*
 * --- A New exit which cleans up the mess we made.
 */
long exit( long code )
{
    AtExit *eb;

   /*
    * --- If opened, close the io channel
    */
    if ( _Backstdout )
        Close( _Backstdout );

   /*
    * --- UnLock our current dir
    */
    if ( _DetachDir )
        UnLock( _DetachDir );

   /*
    * --- Deallocate our argument array
    */
    if ( _DosArray )
        FreeVec( _DosArray );

   /*
    * --- Cleanup our RDArgs structure
    */
    if ( _DosSource ) {
        FreeArgs( _DosSource );
        FreeVec( _DosSource );
    }

   /*
    * --- Ask Matthew...... I don't know!
    */
    for (eb = _ExitBase; eb; eb = eb->Next)
        (*eb->Func)();

   /*
    * --- Low level exit
    */
    _exit(code);
}
