/* zero-handler - source of 0-bytes, like /dev/zero in Unix
 *
 * Copyright (C) 1994 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: 23/Oct/94 first version
 * V1.1: 06/Jan/95 added bytecount
 * V1.2: 09/Jan/95 minor cleanup
 */
#define THIS_PROGRAM    "zero-handler"
#define THIS_VERSION    "1.2"

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


#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <stdlib.h>
#include <string.h>
#include <clib/alib_protos.h>

static char * BSTR2C(BPTR bstr);


static struct MinList mylist;
typedef struct {
    struct MinNode  node;
    LONG            bytesleft;
} NODE;


static char *
BSTR2C(bstr)
    BPTR bstr;
{
    static char cstr[256];
    int len;
    UBYTE *p = (UBYTE *)BADDR(bstr);

    if( p ) {
        len = (int) *p++;
        strncpy(cstr, (char *)p, len);
        cstr[len] = '\0';
        return cstr;
    }
    return (char *)0;
}


void _main()
{
    struct DeviceNode *devnode;
    struct Process *myproc;
    struct DosPacket *pkt;
    NODE *node;
    struct FileHandle *fh;
    char *name;
    LONG err1, err2;

    NewList((struct List *)&mylist);

    myproc = (struct Process *)FindTask(NULL);

    /* get the startup message */
    pkt = WaitPkt();
    devnode = (struct DeviceNode *)BADDR(pkt->dp_Arg3);
    devnode->dn_Task = &(myproc->pr_MsgPort);
    ReplyPkt(pkt, DOSTRUE, 0);

    for(;;) {
        pkt = WaitPkt();

        err1 = DOSTRUE; err2 = 0;
        switch( pkt->dp_Type ) {
            case ACTION_FINDINPUT:
                fh = (struct FileHandle *)BADDR(pkt->dp_Arg1);
                fh->fh_Port = NULL;

                node = (NODE *)0;
                if( name = BSTR2C(pkt->dp_Arg3) ) {
                    char *p; LONG nbytes;
                    if( p = strchr(name, ':') )
                        name = ++p;
                    nbytes = strtol(name, &p, 10);
                    if( *p ) {             /* "zero:xxx" */
                        err1 = DOSFALSE;
                        err2 = ERROR_OBJECT_NOT_FOUND;
                    }
                    else
                    if( name == p )             /* "zero:\0" */
                        fh->fh_Arg1 = (LONG)0;
                    else                        /* "zero:<number>" */
                    if( node = AllocMem(sizeof(NODE), MEMF_ANY|MEMF_CLEAR) ) {
                        node->bytesleft = nbytes;
                        AddTail((struct List *)&mylist, (struct Node *)node);
                    }
                    else {
                        err1 = DOSFALSE;
                        err2 = ERROR_NO_FREE_STORE;
                    }
                }
                fh->fh_Arg1 = (LONG)node;
            break;

            case ACTION_END:
                if( node = (NODE *)(pkt->dp_Arg1) ) {
                    Remove((struct Node *)node);
                    FreeMem(node, sizeof(NODE));
                }
            break;

            case ACTION_READ:
                err1 = pkt->dp_Arg3;

                if( node = (NODE *)(pkt->dp_Arg1) ) {
                    if( err1 > node->bytesleft )
                        err1 = node->bytesleft;
                    node->bytesleft -= err1;
                }
                bzero((void *)(pkt->dp_Arg2), err1);
            break;

            case ACTION_IS_FILESYSTEM:
                err1 = DOSFALSE;
                /* err2 = 0; */
            break;

            default:
                err1 = DOSFALSE;
                err2 = ERROR_ACTION_NOT_KNOWN;
            break;
        }
        ReplyPkt(pkt, err1, err2);
    }
}

