[Contents] [Index] [Help] [Retrace] [Browse <] [Browse >]

; /* LockRecord.c - Execute me to compile me with SAS/C 6.56
sc NMINC STRMERGE NOSTKCHK NODEBUG DATA=FAR IGNORE=73 LockRecord.c
slink FROM LockRecord.o to LockRecord LIB lib:amiga.lib
quit
*
* AmigaMail LockRecord()/UnLockRecord() example.
*/

/*
(c)  Copyright 1992-1999 Amiga, Inc.   All rights reserved.
The information contained herein is subject to change without notice,
and is provided "as is" without warranty of any kind, either expressed
or implied.  The entire risk as to the use of this information is
assumed by the user.
*/

#include <exec/memory.h>
#include <exec/lists.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <dos/record.h>
#include <utility/tagitem.h>

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

void GetCommandLine(BPTR, struct RDArgs *rdargs, UBYTE *cmdbuffer);
void DoLockRecord(BPTR, struct RDArgs *rdargs);
void DoUnLockRecord(BPTR fh, struct RDArgs *rdargs);
void ListRecordLocks(void);
struct LockNode *FindRecordLock(ULONG offset, ULONG length);

/* List and node structures to keep track of record locks */
struct LockNode {
    struct LockNode *ln_Succ;
    struct LockNode *ln_Pred;
    ULONG ln_Counter;
    ULONG ln_Offset;
    ULONG ln_Length;
    ULONG ln_Mode;
};

struct LockList {
    struct LockNode *lh_Head;
    struct LockNode *lh_Tail;
    struct LockNode *lh_TailPred;
    ULONG lh_Counter;
};

/* Pseudo data file */
#define TESTFILE "t:locktest"

#define LOCK_TEMPLATE "OFFSET/K/N,LENGTH/K/N,EXCLUSIVE/S,IMMEDIATE/S,TIMEOUT/K/N"
#define UNLOCK_TEMPLATE "OFFSET/K/N,LENGTH/K/N"

#define OFFSET_POS      0
#define LENGTH_POS      1
#define EXCLUSIVE_POS   2
#define IMMEDIATE_POS   3
#define TIMEOUT_POS     4

struct Library *SysBase;
struct DosLibrary *DOSBase;
struct Library *UtilityBase;

struct LockList *locklist;

LONG main(void)
{
    BPTR fh;
    struct RDArgs *rdargs;
    struct CSource *csource;
    UBYTE *cmdbuffer;
    struct LockNode *lnode, *nnode;
    LONG error = RETURN_OK;

    SysBase = (*((struct Library **) 4));

    /* Fails silently if < 37 */
    if (DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37))
    {
        UtilityBase = DOSBase->dl_UtilityBase;

        if (locklist = AllocMem(sizeof(struct LockList), MEMF_CLEAR))
        {
            NewList((struct List *)locklist);

            /* Allocate RDArgs structure to parse command lines */
            if (rdargs = AllocDosObject(DOS_RDARGS, TAG_END))
            {
                csource = &rdargs->RDA_Source;

                /* Get buffer to read command lines in */
                if (csource->CS_Buffer = AllocMem(512, MEMF_CLEAR))
                {
                    csource->CS_Length = 512;
                    csource->CS_CurChr = 0;

                    /* Buffer to isolate command keyword */
                    if (cmdbuffer = AllocMem(80, MEMF_CLEAR))
                    {

                        /* Open a testfile, create it if necessary */
                        if (fh = Open(TESTFILE, MODE_READWRITE))
                        {

                            /* Process command lines */
                            GetCommandLine(fh, rdargs, cmdbuffer);

                            /* Try to get rid of outstanding record locks */
                            lnode = locklist->lh_Head;
                            while (nnode = lnode->ln_Succ)
                            {

                                /* Try to unlock pending locks */
                                if ((UnLockRecord(fh,
                                                  lnode->ln_Offset,
                                                  lnode->ln_Length)) == DOSFALSE)
                                {
                   Printf("Error unlocking record %ld with offset %ld length %ld\n",
                                        lnode->ln_Counter,
                                        lnode->ln_Offset,
                                        lnode->ln_Length);
                                    if (IoErr())
                                        PrintFault(IoErr(), NULL);
                                }
                                /* Remove node no matter what */
                                FreeMem(lnode, sizeof(struct LockNode));
                                lnode = nnode;
                            };

                            Close(fh);
                        }
                        FreeMem(cmdbuffer, 80);
                    } else
                        SetIoErr(ERROR_NO_FREE_STORE);

                    FreeMem(csource->CS_Buffer, 512);
                } else
                    SetIoErr(ERROR_NO_FREE_STORE);

                FreeDosObject(DOS_RDARGS, rdargs);
            } else
                SetIoErr(ERROR_NO_FREE_STORE);

            FreeMem(locklist, sizeof(struct LockList));
        } else
            SetIoErr(ERROR_NO_FREE_STORE);


        error = IoErr();
        if (error)
        {
            PrintFault(IoErr(), NULL);
            error = RETURN_FAIL;
        }

        CloseLibrary((struct Library *)DOSBase);
    }
    return(error);
}
void GetCommandLine(BPTR fh, struct RDArgs *rdargs, UBYTE *cmdbuffer)
{
    struct CSource *csource = &rdargs->RDA_Source;
    UBYTE *cmdlinebuffer = csource->CS_Buffer;
    LONG error;

    /* Prompt for command line */
    Write(Output(), "Cmd> ", 5);

    /* Loop forever, waiting for commands */
    for (;;)
    {
        /* Get command line */
        if ((FGets(Input(), cmdlinebuffer, 512)) != NULL)
        {

            /* Use ReadItem() to isolate actual command */
            error = ReadItem(cmdbuffer, 80, csource);

            /* Break on error */
            if (error == ITEM_ERROR)
                break;

            /* Make sure I've got something */
            else if (error != ITEM_NOTHING)
            {
                /* cmdbuffer now contains the command:
                 *
                 * KNOWN COMMANDS:
                 * QUIT
                 * LIST
                 * LOCKRECORD
                 * UNLOCKRECORD
                 */

                if ((Stricmp("QUIT", cmdbuffer)) == 0)
                    break;
                else if ((Stricmp("HELP", cmdbuffer)) == 0)
                {
                    Printf("Available commands:\n");
                    Printf("LOCKRECORD %s\n", LOCK_TEMPLATE);
                    Printf("UNLOCKRECORD %s\n", UNLOCK_TEMPLATE);
                    Printf("LIST\n");
                    Printf("QUIT\n");
                }
                else if ((Stricmp("LIST", cmdbuffer)) == 0)
                    ListRecordLocks();  /* Show all current locks */
                else
                {

                    /* Note that I've already isolated the command
                     * keyword, so I'm using Source->CS_CurChr to point
                     * after it.
                     */
                    csource->CS_Buffer += csource->CS_CurChr;
                    csource->CS_CurChr = 0;

                    if ((Stricmp("LOCKRECORD", cmdbuffer)) == 0)
                        DoLockRecord(fh, rdargs);
                    else if ((Stricmp("UNLOCKRECORD", cmdbuffer)) == 0)
                        DoUnLockRecord(fh, rdargs);
                    else
                        PrintFault(ERROR_NOT_IMPLEMENTED, cmdbuffer);

                    /* Reset CSource */
                    csource->CS_Buffer = cmdlinebuffer;
                }

                /* Output new prompt. Make sure csource is OK. */
                Write(Output(), "Cmd> ", 5);
                csource->CS_CurChr = 0;
            }
        } else
            break;
    }
}

void DoLockRecord(BPTR fh, struct RDArgs *rdargs)
{
    struct RDArgs *readargs;
    LONG rargs[5];
    ULONG offset, length, timeout, mode;
    ULONG result;
    struct LockNode *lnode;

    offset = length = timeout = mode = 0;
    rargs[0] = rargs[1] = rargs[2] = rargs[3] = rargs[4] = 0;

    if (readargs = ReadArgs(LOCK_TEMPLATE, rargs, rdargs))
    {

        if (rargs[OFFSET_POS])
            offset = *((LONG *)rargs[OFFSET_POS]);
        if (rargs[LENGTH_POS])
            length = *((LONG *)rargs[LENGTH_POS]);
        if (rargs[TIMEOUT_POS])
            timeout = *((LONG *)rargs[TIMEOUT_POS]);

        /* Type of locking */
        if (rargs[EXCLUSIVE_POS])
        {
            if (rargs[IMMEDIATE_POS])
                mode = REC_EXCLUSIVE_IMMED;
            else
                mode = REC_EXCLUSIVE;
        }
        else
        {
            if (rargs[IMMEDIATE_POS])
                mode = REC_SHARED_IMMED;
            else
                mode = REC_SHARED;
        }

        rargs[0] = offset;
        rargs[1] = length;
        switch (mode)
        {
            case REC_EXCLUSIVE_IMMED:
                rargs[2] = (LONG)"REC_EXCLUSIVE_IMMED";
                break;
            case REC_EXCLUSIVE:
                rargs[2] = (LONG)"REC_EXCLUSIVE";
                break;
            case REC_SHARED_IMMED:
                rargs[2] = (LONG)"REC_SHARED_IMMED";
                break;
            case REC_SHARED:
                rargs[2] = (LONG)"REC_SHARED";
                break;
        }
        rargs[3] = timeout;


        /* Show what I'm going to do */
        VFPrintf(Output(),
            "LockRecord: Offset %ld, Length %ld, Mode %s, Timeout %ld...",
            rargs);
        Flush(Output());

        /* Lock the record. Parameters are not checked. It is f.e. possible to
         * specify an offset larger than the size of the file. Possible since
         * Record Locks are not related to the file itself, only the means for
         * you to do arbitration.
         *
         * Note that the timeout value is in ticks...
         */
        result = LockRecord(fh, offset, length, mode, timeout);

        if (result == DOSTRUE)
        {
            Write(Output(), "OK\n", 3);

            /* Add a node to track this record lock */
            if (lnode = AllocMem(sizeof(struct LockNode), MEMF_CLEAR))
            {
                lnode->ln_Counter = locklist->lh_Counter++;
                lnode->ln_Offset = offset;
                lnode->ln_Length = length;
                lnode->ln_Mode = mode;

                AddTail((struct List *)locklist, (struct Node *)lnode);
            }
            else
            {
                /* Not enough memory for node. You're on your own... */
                Write(Output(), "Not enough memory to track record lock.\n", 40);
            }
        }
        else
        {
            Write(Output(), "FAILED\n", 7);
            if (IoErr())
                PrintFault(IoErr(), NULL);
        }

        /* Release memory associated with readargs */
        FreeArgs(readargs);
    } else
        PrintFault(IoErr(), NULL);
}

void DoUnLockRecord(BPTR fh, struct RDArgs *rdargs)
{
    struct RDArgs *readargs;
    LONG rargs[2];
    ULONG offset, length;
    ULONG result;
    struct LockNode *lnode;

    offset = length = 0;
    rargs[0] = rargs[1] = 0;

    if (readargs = ReadArgs(LOCK_TEMPLATE, rargs, rdargs))
    {

        if (rargs[OFFSET_POS])
            offset = *((LONG *)rargs[OFFSET_POS]);
        if (rargs[LENGTH_POS])
            length = *((LONG *)rargs[LENGTH_POS]);

        rargs[0] = offset;
        rargs[1] = length;

        /* Show what I'm going to do */
        VFPrintf(Output(), "UnLockRecord: Offset %ld, Length %ld...", rargs);
        Flush(Output());

        /* Unlock indicated record with indicated offset and length.
         * If the same record (same offset/length) is locked multiple times,
         * only one, the first one in the list , will be unlocked.
         */
        result = UnLockRecord(fh, offset, length);

        if (result == DOSTRUE) {
            Write(Output(), "OK\n", 3);

            /* Remove node associated with this lock */
            if (lnode = FindRecordLock(offset, length))
            {
                Remove((struct Node *)lnode);
                FreeMem(lnode, sizeof(struct LockNode));
            }
        }
        else
        {
            Write(Output(), "FAILED\n", 7); /* Keep locknode */
            if (IoErr())
                PrintFault(IoErr(), NULL);
        }
        /* Release memory associated with readargs */
        FreeArgs(readargs);
    } else
        PrintFault(IoErr(), NULL);
}

void ListRecordLocks(void)
{
    struct LockNode *lnode;
    LONG rargs[4];

    for (lnode = locklist->lh_Head; lnode->ln_Succ; lnode = lnode->ln_Succ)
    {
        rargs[0] = lnode->ln_Counter;
        rargs[1] = lnode->ln_Offset;
        rargs[2] = lnode->ln_Length;

        switch (lnode->ln_Mode)
        {
            case REC_EXCLUSIVE_IMMED:
                rargs[3] = (LONG)"REC_EXCLUSIVE_IMMED";
                break;
            case REC_EXCLUSIVE:
                rargs[3] = (LONG)"REC_EXCLUSIVE";
                break;
            case REC_SHARED_IMMED:
                rargs[3] = (LONG)"REC_SHARED_IMMED";
                break;
            case REC_SHARED:
                rargs[3] = (LONG)"REC_SHARED";
                break;
        }

        VFPrintf(Output(), "RecordLock #%ld: Offset %ld Length %ld Mode %s\n", rargs);
    }
    Flush(Output());
}

struct LockNode *FindRecordLock(ULONG offset, ULONG length)
{
    struct LockNode *lnode;

    for (lnode = locklist->lh_Head; lnode->ln_Succ; lnode = lnode->ln_Succ)
    {
        if ((lnode->ln_Offset == offset) && lnode->ln_Length == length)
            return(lnode);
    }
    return(NULL);
}