/* callfunc - call shared library function in a shell script
 *
 * Copyright (C) 1995 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  This software is provided "as is" without express or
 * implied warranty.
 *
 * V1.0: 12/Jan/95 first version
 */
#define THIS_PROGRAM    "callfunc"
#define THIS_VERSION    "1.0"

static const char amiga_version[] = "\0$VER: " THIS_PROGRAM " " THIS_VERSION " (" __COMMODORE_DATE__ ")";

#include <exec/types.h>
#include <exec/libraries.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <stdlib.h>
#include <string.h>

#define SysBase_DECLARED
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

extern struct Library *SysBase;

extern __stkargs LONG CallFunction(struct Library *base, LONG offset,
                                    LONG d0, LONG d1, LONG d2, LONG d3, LONG d4,
                                    LONG d5, LONG d6, LONG d7,
                                    APTR a0, APTR a1, APTR a2, APTR a3, APTR a4);


const char Template[] =
    "OFFSET/N,FROM/K,D0/K,D1/K,D2/K,D3/K,D4/K,D5/K,D6/K,D7/K,A0/K,A1/K,A2/K,A3/K,A4/K,VOID/S,OPEN/K,CLOSE/K,VER=VERSION/K/N";

__aligned struct {
    LONG *  offset;
    STRPTR  libbase;
    STRPTR  D0, D1, D2, D3, D4, D5, D6, D7;
    STRPTR  A0, A1, A2, A3, A4;
    LONG    noresult;   /* 'void' */
    STRPTR  open;
    STRPTR  close;
    LONG *  libversion;
} Args;



static APTR mkaddr(STRPTR str);

void
_main()
{
    struct RDArgs *rdargs;
    LONG rc = RETURN_OK;

    struct Library *cfbase;
    char *dummy;
    LONG result;

    char buf[16] = {0};
    /* RawDoFmt() 'put' function
     * MC68000 machine code:
     *      move.b d0,(a3)+
     *      rts
     * Idea by Doug Walker, walker@unx.sas.com
     */
    __aligned const unsigned char rawdocode[] = { 0x16, 0xC0, 0x4E, 0x75 };


    if( SysBase->lib_Version < 37 ) {
        #define MESSAGE "requires AmigaOS 2.04 or higher\n"
        Write(Output(), MESSAGE, sizeof(MESSAGE)-1);
        _exit(RETURN_FAIL);
        #undef MESSAGE
    }

    if( rdargs = ReadArgs(Template, (LONG *)&Args, NULL) ) {
        LONG libversion = 0;
        if( Args.libversion )
            libversion = *(Args.libversion);

        if( Args.open ) {
            cfbase = OpenLibrary(Args.open, libversion);
            RawDoFmt("%ld\n", (APTR)&cfbase, (void (*)())rawdocode, buf);
            PutStr(buf);
            if( !cfbase )
                rc = RETURN_WARN;
        }
        else
        if( Args.close ) {
            cfbase = (struct Library *)strtoul(Args.close, &dummy, 0);
            if( cfbase )
                CloseLibrary(cfbase);
            else {
                PutStr("invalid library base\n");
                rc = RETURN_WARN;
            }
        }
        else {
            BOOL closelib = FALSE;

            cfbase = (struct Library *)strtoul(Args.libbase, &dummy, 0);
            if( !cfbase || *dummy ) {
                cfbase = OpenLibrary(Args.libbase, libversion);
                closelib = TRUE;
            }

            if( !cfbase ) {
                PutStr("invalid library name or base\n");
                rc = RETURN_ERROR;
                cfbase = NULL;
            }
            else {
                LONG d0 = 0, d1 = 0, d2 = 0, d3 = 0, d4 = 0, d5 = 0, d6 = 0, d7 = 0;
                APTR a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0;
                LONG offset = 0;

                if( Args.offset )
                    offset = *(Args.offset);
                if( offset > 0 )
                    offset = -offset;
                if( (offset % 6) || (offset > -30) ) {
                    PutStr("invalid library offset\n");
                    rc = RETURN_ERROR;
                }
                else {
                    d0 = (LONG)mkaddr(Args.D0);
                    d1 = (LONG)mkaddr(Args.D1);
                    d2 = (LONG)mkaddr(Args.D2);
                    d3 = (LONG)mkaddr(Args.D3);
                    d4 = (LONG)mkaddr(Args.D4);
                    d5 = (LONG)mkaddr(Args.D5);
                    d6 = (LONG)mkaddr(Args.D6);
                    d7 = (LONG)mkaddr(Args.D7);
                    a0 = mkaddr(Args.A0);
                    a1 = mkaddr(Args.A1);
                    a2 = mkaddr(Args.A2);
                    a3 = mkaddr(Args.A3);
                    a4 = mkaddr(Args.A4);

                    result = CallFunction(cfbase, offset, d0, d1, d2, d3, d4, d5, d6, d7,
                                            a0, a1, a2, a3, a4);
                    if( !Args.noresult ) {
                        RawDoFmt("%ld\n", (APTR)&result, (void (*)())rawdocode, buf);
                        PutStr(buf);
                    }
                }
                if( closelib )
                    CloseLibrary(cfbase);
            }
        }
        FreeArgs(rdargs);
    }
    else {
        PutStr("required argument missing\n");
        rc = RETURN_ERROR;
    }
    _exit((int)rc);
}



static APTR
mkaddr(STRPTR str)
{
    APTR adr = NULL;

    if( str ) {
        if( str[0] == '@' )
            adr = (APTR)(str+1);
        else {
            char *dummy;
            adr = (APTR)strtoul(str, &dummy, 0);
        }
    }
    return adr;
}

