home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 100-199 / ff113.lzh / DosDev / device.c < prev    next >
C/C++ Source or Header  |  1987-11-21  |  33KB  |  1,225 lines

  1.  
  2. /*
  3.  *  DOSDEVICE.C     V1.10    2 November 1987
  4.  *
  5.  *  EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C   PUBLIC DOMAIN.
  6.  *
  7.  *  By Matthew Dillon.
  8.  *
  9.  *  Debugging routines are disabled by simply attempting to open the
  10.  *  file "debugoff", turned on again with "debugon".  No prefix may be
  11.  *  attached to these names (you must be CD'd to TEST:).
  12.  *
  13.  *  See Documentation for a detailed discussion.
  14.  *
  15.  *  BUGS:
  16.  *    Currently the only known bug is with the implementation of the
  17.  *    RAM disk itself.  Specifically, if filehandle A is at the end of
  18.  *    the file, and somebody appends to the file with another filehandle,
  19.  *    B, filehandle A will get confused as to it's current position in
  20.  *    the file.
  21.  *
  22.  *    I am probably not updating all the right timestamps.  This is
  23.  *    easy to fix... All you have to do is fool with the floppy and
  24.  *    see which datestamps get updated for certain operations.
  25.  */
  26.  
  27. #include "dos.h"
  28.  
  29. /*
  30.  *  Since this code might be called several times in a row without being
  31.  *  unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!!  This also goes
  32.  *  for any global/static assignments that might be changed by running the
  33.  *  code.
  34.  */
  35.  
  36. PROC    *DosProc;   /*    Our Process                    */
  37. DEVNODE *DosNode;   /*    Our DOS node.. created by DOS for us        */
  38. DEVLIST *DevList;   /*    Device List structure for our volume node   */
  39.  
  40. void    *SysBase;   /*    EXEC library base            */
  41. DOSLIB    *DOSBase;   /*    DOS library base for debug process    */
  42. RAMFILE RFRoot;     /*    Directory/File structure    (root node) */
  43. LIST    FHBase;     /*    Open Files                */
  44. LIST    LCBase;     /*    Open Locks                */
  45.  
  46. long    TotalBytes; /*    total bytes of data in filesystem    */
  47.  
  48.  
  49.             /*    DEBUGGING            */
  50. PORT *Dbport;        /*    owned by the debug process    */
  51. PORT *Dback;        /*    owned by the DOS device driver    */
  52. short DBDisable;
  53. MSG DummyMsg;        /*    Dummy message that debug proc can use    */
  54.  
  55. /*
  56.  *  Don't call the entry point main().  This way, if you make a mistake
  57.  *  with the compile options you'll get a link error.
  58.  */
  59.  
  60. void
  61. noname()
  62. {
  63.     register PACKET *packet;
  64.     register short   error;
  65.     MSG     *msg;
  66.     ubyte   notdone;
  67.     ubyte   buf[256];
  68.     void    *tmp;
  69.  
  70.     /*
  71.      *    Initialize all global variables.  SysBase MUST be initialized before
  72.      *    we can make Exec calls.  AbsExecBase is a library symbol
  73.      *    referencing absolute memory location 4.  The DOS library is openned
  74.      *    for the debug process only.
  75.      */
  76.  
  77.     DBDisable = 0;                /*  Init. globals    */
  78.     Dbport = Dback = NULL;
  79.     TotalBytes = 0;
  80.     SysBase = AbsExecBase;
  81.     DOSBase = OpenLibrary("dos.library",0);
  82.     DosProc = FindTask(NULL);
  83.     {
  84.     WaitPort(&DosProc->pr_MsgPort);     /*  Get Startup Packet    */
  85.     msg = GetMsg(&DosProc->pr_MsgPort);
  86.     packet = (PACKET *)msg->mn_Node.ln_Name;
  87.  
  88.     /*
  89.      *  Loading DosNode->dn_Task causes DOS *NOT* to startup a new
  90.      *  instance of the device driver for every reference.    E.G. if
  91.      *  you were writing a CON device you would want this field to
  92.      *  be NULL.
  93.      */
  94.  
  95.     if (DOSBase) {
  96.         DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
  97.         register DEVLIST *dl = dosalloc(sizeof(DEVLIST));
  98.  
  99.         DosNode = BTOC(packet->dp_Arg3);
  100.  
  101.         /*
  102.          *    Create Volume node and add to the device list.    This will
  103.          *    cause the WORKBENCH to recognize us as a disk.    If we don't
  104.          *    create a Volume node, Wb will not recognize us.  However,
  105.          *    we are a RAM: disk, Volume node or not.
  106.          */
  107.  
  108.         DevList = dl;
  109.         dl->dl_Type = DLT_VOLUME;
  110.         dl->dl_Task = &DosProc->pr_MsgPort;
  111.         dl->dl_DiskType = ID_DOS_DISK;
  112.         dl->dl_Name = (void *)DosNode->dn_Name;
  113.         dl->dl_Next = di->di_DevInfo;
  114.         di->di_DevInfo = (long)CTOB(dl);
  115.  
  116.         /*
  117.          *    Set dn_Task field which tells DOS not to startup a new
  118.          *    process on every reference.
  119.          */
  120.  
  121.         DosNode->dn_Task = &DosProc->pr_MsgPort;
  122.         packet->dp_Res1 = DOS_TRUE;
  123.         packet->dp_Res2 = 0;
  124.     } else {                /*    couldn't open dos.library   */
  125.         packet->dp_Res1 = DOS_FALSE;
  126.         returnpacket(packet);
  127.         return;                /*    exit process            */
  128.     }
  129.     returnpacket(packet);
  130.     }
  131.  
  132.     /*
  133.      *    Initialize debugging code
  134.      */
  135.  
  136.     dbinit();        /* this can be removed  */
  137.  
  138.     /*    Initialize  RAM disk    */
  139.  
  140.     {
  141.     ubyte *ptr = BTOC(DosNode->dn_Name);
  142.     short len = *ptr;
  143.  
  144.     NewList(&FHBase);                /*    more globals    */
  145.     NewList(&LCBase);
  146.     bzero(&RFRoot,sizeof(RFRoot));
  147.     RFRoot.type = FILE_DIR;             /*    root directory    */
  148.     DateStamp(&RFRoot.date);            /*    datestamp    */
  149.     NewList(&RFRoot.list);                /*    sub dirs    */
  150.     RFRoot.name = AllocMem(len+1, MEMF_PUBLIC); /*    Root NAME    */
  151.     bmov(ptr+1,RFRoot.name,len);
  152.     RFRoot.name[len] = 0;
  153.     dbprintf("ROOT NAME: %ld '%s'\n", len, RFRoot.name);
  154.     }
  155.  
  156.     /*
  157.      *    Here begins the endless loop, waiting for requests over our
  158.      *    message port and executing them.  Since requests are sent over
  159.      *    our message port, this precludes being able to call DOS functions
  160.      *    ourselves (that is why the debugging routines are a separate process)
  161.      */
  162.  
  163. top:
  164.     for (notdone = 1; notdone;) {
  165.     WaitPort(&DosProc->pr_MsgPort);
  166.     while (msg = GetMsg(&DosProc->pr_MsgPort)) {
  167.         register ubyte *ptr;
  168.         packet = (PACKET *)msg->mn_Node.ln_Name;
  169.         packet->dp_Res1 = DOS_TRUE;
  170.         packet->dp_Res2 = 0;
  171.         error = 0;
  172.         dbprintf("Packet: %3ld %08lx %08lx %08lx %10s ",
  173.         packet->dp_Type,
  174.         packet->dp_Arg1, packet->dp_Arg2,
  175.         packet->dp_Arg3,
  176.         typetostr(packet->dp_Type)
  177.         );
  178.  
  179.         switch(packet->dp_Type) {
  180.         case ACTION_DIE:        /*    attempt to die?             */
  181.         notdone = 0;        /*    try to die                */
  182.         break;
  183.         case ACTION_OPENRW:     /*    FileHandle,Lock,Name        Bool    */
  184.         case ACTION_OPENOLD:    /*    FileHandle,Lock,Name        Bool    */
  185.         case ACTION_OPENNEW:    /*    FileHandle,Lock,Name        Bool    */
  186.         {
  187.             register RAMFILE *ramfile;
  188.             RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
  189.             char    *ptr;
  190.  
  191.             btos(packet->dp_Arg3,buf);
  192.             dbprintf("'%s' ", buf);
  193.             if (strcmp(buf,"debugoff") == 0)
  194.             DBDisable = 1;
  195.             if (strcmp(buf,"debugon") == 0)
  196.             DBDisable = 0;
  197.             if (ramfile = searchpath(&parentdir,buf,&ptr)) {
  198.             if (ramfile->type == FILE_DIR) {
  199.                 error = ERROR_OBJECT_WRONG_TYPE;
  200.                 goto openbreak;
  201.             }
  202.             if (ramfile->locks < 0) {
  203.                 error = ERROR_OBJECT_IN_USE;
  204.                 goto openbreak;
  205.             }
  206.             if (packet->dp_Type == ACTION_OPENOLD) {
  207.                 ++ramfile->locks;
  208.             } else {
  209.                 if (ramfile->locks > 0) {
  210.                 error = ERROR_OBJECT_IN_USE;
  211.                 } else {
  212.                 if (packet->dp_Type == ACTION_OPENNEW) {
  213.                     freedata(ramfile);
  214.                     ramfile->protection = 0;
  215.                 }
  216.                 --ramfile->locks;
  217.                 }
  218.             }
  219.             } else {
  220.             if (!parentdir) {
  221.                 error = ERROR_INVALID_COMPONENT_NAME;
  222.                 goto openbreak;
  223.             }
  224.             if (packet->dp_Type == ACTION_OPENNEW) {
  225.                 ramfile = createramfile(parentdir, FILE_FILE, ptr);
  226.                 --ramfile->locks;
  227.             } else {
  228.                 error = ERROR_OBJECT_NOT_FOUND;
  229.             }
  230.             }
  231.             if (!error) {
  232.             register MYFH *mfh = AllocMem(sizeof(MYFH), MEMF_PUBLIC|MEMF_CLEAR);
  233.             ((FH *)BTOC(packet->dp_Arg1))->fh_Arg1 = (long)mfh;
  234.             mfh->file = ramfile;
  235.             mfh->fentry = GetHead(&ramfile->list);
  236.             AddHead(&FHBase,mfh);
  237.             }
  238.         }
  239.           openbreak:
  240.         if (!GetHead(&FHBase) && !GetHead(&LCBase))
  241.             notdone = 0;
  242.         break;
  243.         case ACTION_READ:        /*     FHArg1,CPTRBuffer,Length   ActLength  */
  244.         {
  245.             register MYFH   *mfh = (MYFH *)packet->dp_Arg1;
  246.             register FENTRY *fen = mfh->fentry;
  247.             register ubyte  *ptr = (ubyte *)packet->dp_Arg2;
  248.             register long   left = packet->dp_Arg3;
  249.             register long   scr;
  250.  
  251.             while (left && fen) {
  252.             scr = fen->bytes - mfh->offset;
  253.             if (left < scr) {
  254.                 bmov(fen->buf + mfh->offset, ptr, left);
  255.                 mfh->offset += left;
  256.                 left = 0;
  257.             } else {
  258.                 bmov(fen->buf + mfh->offset, ptr, scr);
  259.                 left -= scr;
  260.                 ptr += scr;
  261.                 mfh->base += fen->bytes;
  262.                 mfh->offset = 0;
  263.                 fen = NextNode(fen);
  264.             }
  265.             }
  266.             mfh->fentry = fen;
  267.             packet->dp_Res1 = packet->dp_Arg3 - left;
  268.         }
  269.         break;
  270.         case ACTION_WRITE:        /*     FHArg1,CPTRBuffer,Length   ActLength  */
  271.         {
  272.             register MYFH   *mfh = (MYFH *)packet->dp_Arg1;
  273.             register FENTRY *fen = (FENTRY *)mfh->fentry;
  274.             ubyte  *ptr = (ubyte *)packet->dp_Arg2;
  275.             long   left = packet->dp_Arg3;
  276.             long   scr;
  277.  
  278.             /*
  279.              *    Doesn't work right if multiple readers/appenders.
  280.              */
  281.  
  282.             while (left) {
  283.             if (fen) {
  284.                 dbprintf("FEN: %ld  left: %ld\n", fen->bytes, left);
  285.                 scr = fen->bytes - mfh->offset;
  286.                 if (left < scr) {
  287.                 if (fen->bytes < mfh->offset + left)
  288.                     dbprintf("PANIC! AWR0\n");
  289.                 else
  290.                     bmov(ptr, fen->buf + mfh->offset, left);
  291.                 mfh->offset += left;
  292.                 left = 0;
  293.                 } else {
  294.                 if (fen->bytes < mfh->offset + scr)
  295.                     dbprintf("PANIC! AWR1\n");
  296.                 else
  297.                     bmov(ptr, fen->buf + mfh->offset, scr);
  298.                 ptr += scr;
  299.                 left -= scr;
  300.                 mfh->base += fen->bytes;
  301.                 mfh->offset = 0;
  302.                 fen = NextNode(fen);
  303.                 }
  304.             } else {
  305.                 fen = AllocMem(sizeof(FENTRY), MEMF_PUBLIC);
  306.                 if (fen->buf = AllocMem(left, MEMF_PUBLIC)) {
  307.                 fen->bytes = left;
  308.                 mfh->file->bytes += left;
  309.                 mfh->base  += left;
  310.                 mfh->offset = 0;
  311.                 TotalBytes += left;
  312.                 AddTail(&mfh->file->list, fen);
  313.                 dbprintf("NEWFEN: (%ld)\n", fen->bytes);
  314.                 bmov(ptr, fen->buf, left);
  315.                 left = 0;
  316.                 } else {
  317.                 FreeMem(fen, sizeof(FENTRY));
  318.                 dbprintf("NEWFEN: ****** Unable to allocate buffer %ld\n", left);
  319.                 mfh->offset = 0;
  320.                 break;
  321.                 }
  322.                 fen = NULL;     /*    cause append    */
  323.             }
  324.             }
  325.             packet->dp_Res1 = packet->dp_Arg3 - left;
  326.             mfh->fentry = fen;
  327.         }
  328.         break;
  329.         case ACTION_CLOSE:        /*     FHArg1             Bool:TRUE  */
  330.         {
  331.             register MYFH   *mfh = (MYFH *)packet->dp_Arg1;
  332.             register RAMFILE *file = mfh->file;
  333.  
  334.             Remove(mfh);
  335.             FreeMem(mfh,sizeof(*mfh));
  336.             if (--file->locks < 0)
  337.             file->locks = 0;
  338.         }
  339.         if (!GetHead(&FHBase) && !GetHead(&LCBase))
  340.             notdone = 0;
  341.         break;
  342.         case ACTION_SEEK:        /*     FHArg1,Position,Mode        OldPosition*/
  343.         {
  344.             register MYFH *mfh = (MYFH *)packet->dp_Arg1;
  345.             register FENTRY *fen;
  346.             register long absseek;
  347.  
  348.             packet->dp_Res1 = mfh->base + mfh->offset;
  349.             absseek = packet->dp_Arg2;
  350.             if (packet->dp_Arg3 == 0)
  351.             absseek += mfh->base + mfh->offset;
  352.             if (packet->dp_Arg3 == 1)
  353.             absseek = mfh->file->bytes + absseek;
  354.             if (absseek < 0 || absseek > mfh->file->bytes) {
  355.             error = ERROR_SEEK_ERROR;
  356.             break;
  357.             }
  358.             mfh->base = mfh->offset = 0;
  359.  
  360.             /*
  361.              *    Stupid way to do it but....
  362.              */
  363.  
  364.             for (fen = GetHead(&mfh->file->list); fen; fen = NextNode(fen)) {
  365.             if (mfh->base + fen->bytes > absseek) {
  366.                 mfh->offset = absseek - mfh->base;
  367.                 break;
  368.             }
  369.             mfh->base += fen->bytes;
  370.             }
  371.             mfh->fentry = fen;
  372.         }
  373.         break;
  374.         /*
  375.          *    This implementation sucks.  The right way to do it is with
  376.          *    a hash table.  The directory must be searched for the file
  377.          *    name, then the next entry retrieved.  If the next entry is
  378.          *    NULL there are no more entries.  If the filename could not
  379.          *    be found we return the first entry, if any.
  380.          *
  381.          *    You can't simply keep a pointer around to the next node
  382.          *    because it can be moved or removed at any time.
  383.          */
  384.  
  385.         case ACTION_EXAMINE_NEXT: /*   Lock,Fib              Bool     */
  386.         {
  387.             register FIB *fib = BTOC(packet->dp_Arg2);
  388.             register RAMFILE *dir = getlockfile(packet->dp_Arg1);
  389.             register RAMFILE *file;
  390.  
  391.             if (dir->type == FILE_FILE) {
  392.             error = ERROR_OBJECT_WRONG_TYPE;
  393.             break;
  394.             }
  395.             file = GetHead(&dir->list);
  396.             if (fib->fib_DiskKey) {
  397.             register int len = *(ubyte *)fib->fib_FileName;
  398.             for (; file; file = NextNode(file)) {
  399.                 if (strlen(file->name) == len && nccmp(file->name, fib->fib_FileName+1, len))
  400.                 break;
  401.             }
  402.             if (file)
  403.                 file = NextNode(file);
  404.             else
  405.                 file = GetHead(&dir->list);
  406.             }
  407.             fib->fib_DiskKey = 1;
  408.             error = -1;
  409.             if (!(tmp=file)) {
  410.             error = ERROR_NO_MORE_ENTRIES;
  411.             break;
  412.             }
  413.         }
  414.         /*  fall through    */
  415.         case ACTION_EXAMINE_OBJECT: /*   Lock,Fib            Bool       */
  416.         {
  417.             register FIB *fib;
  418.             register RAMFILE *file;
  419.             register RAMFILE *dummy;
  420.  
  421.             fib = BTOC(packet->dp_Arg2);
  422.             if (error) {
  423.             file = tmp;    /*  fall through from above */
  424.             } else {
  425.             file = getlockfile(packet->dp_Arg1);
  426.             fib->fib_DiskKey = 0;
  427.             }
  428.             error = 0;
  429.             fib->fib_DirEntryType = file->type;
  430.             strcpy(fib->fib_FileName+1, file->name);
  431.             fib->fib_FileName[0] = strlen(file->name);
  432.             fib->fib_Protection = file->protection;
  433.             fib->fib_EntryType = NULL;
  434.             fib->fib_Size = file->bytes;
  435.             fib->fib_NumBlocks = file->bytes >> 9;
  436.             fib->fib_Date = file->date;
  437.             if (file->comment) {
  438.             strcpy(fib->fib_Comment+1, file->comment);
  439.             fib->fib_Comment[0] = strlen(file->comment);
  440.             } else {
  441.             fib->fib_Comment[0] = 0;
  442.             }
  443.         }
  444.         break;
  445.         case ACTION_INFO:        /*    Lock, InfoData      Bool:TRUE    */
  446.         tmp = BTOC(packet->dp_Arg2);
  447.         error = -1;
  448.         /*  fall through    */
  449.         case ACTION_DISK_INFO:  /*    InfoData      Bool:TRUE    */
  450.         {
  451.             register INFODATA *id;
  452.  
  453.             /*
  454.              *    Note:    id_NumBlocks is never 0, but only to get
  455.              *    around a bug I found in my shell (where I divide
  456.              *    by id_NumBlocks).  Other programs probably break
  457.              *    as well.
  458.              */
  459.  
  460.             (error) ? (id = tmp) : (id = BTOC(packet->dp_Arg1));
  461.             error = 0;
  462.             bzero(id, sizeof(*id));
  463.             id->id_DiskState = ID_VALIDATED;
  464.             id->id_NumBlocks     = (TotalBytes >> 9) + 1;
  465.             id->id_NumBlocksUsed = (TotalBytes >> 9) + 1;
  466.             id->id_BytesPerBlock = 512;
  467.             id->id_DiskType = ID_DOS_DISK;
  468.             id->id_VolumeNode = (long)CTOB(DosNode);
  469.             id->id_InUse = (long)GetHead(&LCBase);
  470.         }
  471.         break;
  472.         case ACTION_PARENT:     /*     Lock                ParentLock */
  473.         {
  474.             register RAMFILE *file = getlockfile(packet->dp_Arg1);
  475.             if (file->type == FILE_FILE) {
  476.             error = ERROR_OBJECT_NOT_FOUND;
  477.             break;
  478.             }
  479.             if (file->locks < 0) {
  480.             error = ERROR_OBJECT_IN_USE;
  481.             break;
  482.             }
  483.             if (file->parent)
  484.             packet->dp_Res1 = (long)CTOB(ramlock(file->parent, ACCESS_READ));
  485.             else
  486.             error = ERROR_OBJECT_NOT_FOUND;
  487.         }
  488.         break;
  489.         case ACTION_DELETE_OBJECT: /*Lock,Name            Bool       */
  490.         {
  491.             RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
  492.             RAMFILE *ramfile;
  493.  
  494.             btos(packet->dp_Arg2, buf);
  495.             if (ramfile = searchpath(&parentdir,buf,NULL)) {
  496.             if (ramfile->locks || ramfile == &RFRoot) {
  497.                 error = ERROR_OBJECT_IN_USE;
  498.                 break;
  499.             }
  500.             if (ramfile->type == FILE_DIR) {
  501.                 if (GetHead(&ramfile->list))
  502.                 error = ERROR_DIRECTORY_NOT_EMPTY;
  503.             } else {
  504.                 freedata(ramfile);
  505.             }
  506.             if (!error) {
  507.                 freeramfile(ramfile);
  508.                 DateStamp(&parentdir->date);
  509.             }
  510.             } else {
  511.             if (!parentdir)
  512.                 error = ERROR_INVALID_COMPONENT_NAME;
  513.             else
  514.                 error = ERROR_OBJECT_NOT_FOUND;
  515.             }
  516.         }
  517.         if (!GetHead(&FHBase) && !GetHead(&LCBase))
  518.             notdone = 0;
  519.         break;
  520.         case ACTION_CREATE_DIR: /*     Lock,Name            Lock       */
  521.         {
  522.             RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
  523.             RAMFILE *ramfile;
  524.             char *ptr;
  525.  
  526.             btos(packet->dp_Arg2, buf);
  527.             if (ramfile = searchpath(&parentdir,buf,&ptr)) {
  528.             error = ERROR_OBJECT_EXISTS;
  529.             break;
  530.             }
  531.             if (!parentdir) {
  532.             error = ERROR_INVALID_COMPONENT_NAME;
  533.             break;
  534.             }
  535.             ramfile = createramfile(parentdir, FILE_DIR, ptr);
  536.             packet->dp_Res1 = (long)CTOB(ramlock(ramfile, ACCESS_WRITE));
  537.         }
  538.         break;
  539.         case ACTION_LOCATE_OBJECT:    /*   Lock,Name,Mode        Lock       */
  540.         {
  541.             RAMFILE *parentdir = getlockfile(packet->dp_Arg1);
  542.             RAMFILE *ramfile;
  543.  
  544.             btos(packet->dp_Arg2, buf);
  545.             dbprintf("'%s' %ld ", buf, packet->dp_Arg3);
  546.             if (ramfile = searchpath(&parentdir,buf,NULL)) {
  547.             if (ramfile->locks < 0 || (ramfile->locks && packet->dp_Arg3 == ACCESS_WRITE)) {
  548.                 error = ERROR_OBJECT_IN_USE;
  549.                 break;
  550.             }
  551.             packet->dp_Res1 = (long)CTOB(ramlock(ramfile, packet->dp_Arg3));
  552.             } else {
  553.             if (!parentdir)
  554.                 error = ERROR_INVALID_COMPONENT_NAME;
  555.             else
  556.                 error = ERROR_OBJECT_NOT_FOUND;
  557.             }
  558.         }
  559.         break;
  560.         case ACTION_COPY_DIR:   /*     Lock,                Lock       */
  561.         {
  562.             register RAMFILE *ramfile = getlockfile(packet->dp_Arg1);
  563.             if (ramfile->locks < 0)
  564.             error = ERROR_OBJECT_IN_USE;
  565.             else
  566.             packet->dp_Res1 = (long)CTOB(ramlock(ramfile, ACCESS_READ));
  567.         }
  568.         break;
  569.         case ACTION_FREE_LOCK:  /*     Lock,                Bool       */
  570.         if (packet->dp_Arg1);
  571.             ramunlock(BTOC(packet->dp_Arg1));
  572.         if (!GetHead(&FHBase) && !GetHead(&LCBase))
  573.             notdone = 0;
  574.         break;
  575.         case ACTION_SET_PROTECT:/*     -,Lock,Name,Mask       Bool       */
  576.         {
  577.             register RAMFILE *ramfile;
  578.             RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
  579.             char *ptr;
  580.  
  581.             btos(packet->dp_Arg3, buf);
  582.             if (ramfile = searchpath(&parentdir,buf,&ptr)) {
  583.             ramfile->protection = packet->dp_Arg4;
  584.             } else {
  585.             if (parentdir)
  586.                 error = ERROR_OBJECT_NOT_FOUND;
  587.             else
  588.                 error = ERROR_INVALID_COMPONENT_NAME;
  589.             }
  590.         }
  591.         break;
  592.         case ACTION_SET_COMMENT:/*     -,Lock,Name,Comment       Bool       */
  593.         {
  594.             register RAMFILE *ramfile;
  595.             RAMFILE *parentdir = getlockfile(packet->dp_Arg2);
  596.             char *ptr;
  597.  
  598.             btos(packet->dp_Arg3, buf);
  599.             if (ramfile = searchpath(&parentdir,buf,&ptr)) {
  600.             btos(packet->dp_Arg4, buf);
  601.             if (ramfile->comment)
  602.                 FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
  603.             ramfile->comment = AllocMem(strlen(buf)+1, MEMF_PUBLIC);
  604.             strcpy(ramfile->comment, buf);
  605.             } else {
  606.             if (parentdir)
  607.                 error = ERROR_OBJECT_NOT_FOUND;
  608.             else
  609.                 error = ERROR_INVALID_COMPONENT_NAME;
  610.             }
  611.         }
  612.         break;
  613.         case ACTION_RENAME_OBJECT:/* SLock,SName,DLock,DName    Bool       */
  614.         {
  615.             register RAMFILE *file1;
  616.             RAMFILE *sourcedir = getlockfile(packet->dp_Arg1);
  617.             RAMFILE *destdir   = getlockfile(packet->dp_Arg3);
  618.             char *ptr;
  619.  
  620.             btos(packet->dp_Arg2,buf);
  621.             dbprintf("\nRENAME '%s' (%ld)  ", buf, strlen(buf));
  622.             if (file1 = searchpath(&sourcedir,buf,NULL)) {
  623.             btos(packet->dp_Arg4,buf);
  624.             dbprintf("TO '%s' (%ld)", buf, strlen(buf));
  625.             if (searchpath(&destdir,buf,&ptr)) {
  626.                 error = ERROR_OBJECT_EXISTS;
  627.             } else {
  628.                 if (destdir) {
  629.                 if (file1 == destdir) { /* moving inside self */
  630.                     error = ERROR_OBJECT_IN_USE;
  631.                     break;
  632.                 }
  633.                 dbprintf("REN '%s' %ld", ptr, strlen(ptr));
  634.                 DateStamp(&sourcedir->date);
  635.                 DateStamp(&destdir->date);
  636.                 /*FreeMem(file1->name, strlen(file1->name)+1);*/
  637.                 Remove(file1);
  638.                 file1->name = AllocMem(strlen(ptr)+1,MEMF_PUBLIC);
  639.                 file1->parent = destdir;
  640.                 strcpy(file1->name, ptr);
  641.                 AddHead(&destdir->list, file1);
  642.                 } else {
  643.                 error = ERROR_INVALID_COMPONENT_NAME;
  644.                 }
  645.             }
  646.             } else {
  647.             if (sourcedir)
  648.                 error = ERROR_OBJECT_NOT_FOUND;
  649.             else
  650.                 error = ERROR_INVALID_COMPONENT_NAME;
  651.             }
  652.         }
  653.         break;
  654.         /*
  655.          *    A few other packet types which we do not support
  656.          */
  657.         case ACTION_INHIBIT:    /*     Bool                Bool       */
  658.         /*  Return success for the hell of it    */
  659.         break;
  660.         case ACTION_RENAME_DISK:/*     BSTR:NewName            Bool       */
  661.         case ACTION_MORECACHE:  /*     #BufsToAdd            Bool       */
  662.         case ACTION_WAIT_CHAR:  /*     Timeout, ticks         Bool       */
  663.         case ACTION_FLUSH:        /*     writeout bufs, disk motor off           */
  664.         case ACTION_RAWMODE:    /*     Bool(-1:RAW 0:CON)        OldState   */
  665.         default:
  666.         error = ERROR_ACTION_NOT_KNOWN;
  667.         break;
  668.         }
  669.         if (packet) {
  670.         if (error) {
  671.             dbprintf("ERR=%ld\n", error);
  672.             packet->dp_Res1 = DOS_FALSE;
  673.             packet->dp_Res2 = error;
  674.         } else {
  675.             dbprintf("RES=%06lx\n", packet->dp_Res1);
  676.         }
  677.         returnpacket(packet);
  678.         }
  679.     }
  680.     }
  681.     dbprintf("Can we remove ourselves? ");
  682.     Delay(50);        /*    I wanna even see the debug message! */
  683.     Forbid();
  684.     if (packetsqueued(DosProc) || GetHead(&FHBase) || GetHead(&LCBase)
  685.       || GetHead(&RFRoot.list)) {
  686.     Permit();
  687.     dbprintf(" ..  not yet!\n");
  688.     goto top;        /*  sorry... can't exit     */
  689.     }
  690.  
  691.     /*
  692.      *    Causes a new process to be created on next reference
  693.      */
  694.  
  695.     DosNode->dn_Task = FALSE;
  696.  
  697.     /*
  698.      *    Remove Volume entry.  Since DOS uses singly linked lists, we
  699.      *    must (ugg) search it manually to find the link before our
  700.      *    Volume entry.
  701.      */
  702.  
  703.     {
  704.     DOSINFO *di = BTOC(((ROOTNODE *)DOSBase->dl_Root)->rn_Info);
  705.     register DEVLIST *dl;
  706.     register void *dlp;
  707.  
  708.     dlp = &di->di_DevInfo;
  709.     for (dl = BTOC(di->di_DevInfo); dl && dl != DevList; dl = BTOC(dl->dl_Next))
  710.         dlp = &dl->dl_Next;
  711.     if (dl == DevList) {
  712.         *(BPTR *)dlp = dl->dl_Next;
  713.         dosfree(dl);
  714.     } else {
  715.         dbprintf("****PANIC: Unable to find volume node\n");
  716.     }
  717.     }
  718.  
  719.     /*
  720.      *    Remove debug process, closedown, fall of the end of the world
  721.      *    (which is how you kill yourself if a PROCESS.  A TASK would have
  722.      *    had to RemTask(NULL) itself).
  723.      */
  724.  
  725.     dbuninit();
  726.     CloseLibrary(DOSBase);
  727. }
  728.  
  729.  
  730. /*
  731.  *  PACKET ROUTINES.    Dos Packets are in a rather strange format as you
  732.  *  can see by this and how the PACKET structure is extracted in the
  733.  *  GetMsg() of the main routine.
  734.  */
  735.  
  736. void
  737. returnpacket(packet)
  738. register struct DosPacket *packet;
  739. {
  740.     register struct Message *mess;
  741.     register struct MsgPort *replyport;
  742.  
  743.     replyport             = packet->dp_Port;
  744.     mess             = packet->dp_Link;
  745.     packet->dp_Port         = &DosProc->pr_MsgPort;
  746.     mess->mn_Node.ln_Name    = (char *)packet;
  747.     mess->mn_Node.ln_Succ    = NULL;
  748.     mess->mn_Node.ln_Pred    = NULL;
  749.     PutMsg(replyport, mess);
  750. }
  751.  
  752. /*
  753.  *  Are there any packets queued to our device?
  754.  */
  755.  
  756. packetsqueued()
  757. {
  758.     return ((void *)DosProc->pr_MsgPort.mp_MsgList.lh_Head !=
  759.         (void *)&DosProc->pr_MsgPort.mp_MsgList.lh_Tail);
  760. }
  761.  
  762. /*
  763.  *  DOS MEMORY ROUTINES
  764.  *
  765.  *  DOS makes certain assumptions about LOCKS.    A lock must minimally be
  766.  *  a FileLock structure, with additional private information after the
  767.  *  FileLock structure.  The longword before the beginning of the structure
  768.  *  must contain the length of structure + 4.
  769.  *
  770.  *  NOTE!!!!! The workbench does not follow the rules and assumes it can
  771.  *  copy lock structures.  This means that if you want to be workbench
  772.  *  compatible, your lock structures must be EXACTLY sizeof(struct FileLock).
  773.  */
  774.  
  775. void *
  776. dosalloc(bytes)
  777. register ulong bytes;
  778. {
  779.     register ulong *ptr;
  780.  
  781.     bytes += 4;
  782.     ptr = AllocMem(bytes, MEMF_PUBLIC|MEMF_CLEAR);
  783.     *ptr = bytes;
  784.     return(ptr+1);
  785. }
  786.  
  787. dosfree(ptr)
  788. register ulong *ptr;
  789. {
  790.     --ptr;
  791.     FreeMem(ptr, *ptr);
  792. }
  793.  
  794. /*
  795.  *  Convert a BSTR into a normal string.. copying the string into buf.
  796.  *  I use normal strings for internal storage, and convert back and forth
  797.  *  when required.
  798.  */
  799.  
  800. void
  801. btos(bstr,buf)
  802. ubyte *bstr;
  803. ubyte *buf;
  804. {
  805.     bstr = BTOC(bstr);
  806.     bmov(bstr+1,buf,*bstr);
  807.     buf[*bstr] = 0;
  808. }
  809.  
  810. /*
  811.  *  Some EXEC list handling routines not found in the EXEC library.
  812.  */
  813.  
  814. void *
  815. NextNode(node)
  816. NODE *node;
  817. {
  818.     node = node->mln_Succ;
  819.     if (node->mln_Succ == NULL)
  820.     return(NULL);
  821.     return(node);
  822. }
  823.  
  824. void *
  825. GetHead(list)
  826. LIST *list;
  827. {
  828.     if ((void *)list->mlh_Head != (void *)&list->mlh_Tail)
  829.     return(list->mlh_Head);
  830.     return(NULL);
  831. }
  832.  
  833. /*
  834.  *  Compare two names which are at least n characters long each,
  835.  *  ignoring case.
  836.  */
  837.  
  838. nccmp(p1,p2,n)
  839. register ubyte *p1, *p2;
  840. register short n;
  841. {
  842.     while (--n >= 0) {
  843.     if ((p1[n]|0x20) != (p2[n]|0x20))
  844.         return(0);
  845.     }
  846.     return(1);
  847. }
  848.  
  849. /*
  850.  *  Create a file or directory and link it into it's parent directory.
  851.  */
  852.  
  853. RAMFILE *
  854. createramfile(parentdir, type, name)
  855. RAMFILE *parentdir;
  856. char *name;
  857. {
  858.     register RAMFILE *ramfile;
  859.  
  860.     ramfile = AllocMem(sizeof(RAMFILE), MEMF_CLEAR|MEMF_PUBLIC);
  861.     AddTail(&parentdir->list, ramfile);
  862.     ramfile->parent = parentdir;
  863.     ramfile->name = AllocMem(strlen(name)+1, MEMF_PUBLIC);
  864.     strcpy(ramfile->name, name);
  865.     ramfile->type = type;
  866.     ramfile->protection = 0;
  867.     NewList(&ramfile->list);
  868.     DateStamp(&ramfile->date);
  869.     DateStamp(&ramfile->parent->date);
  870.     return(ramfile);
  871. }
  872.  
  873. /*
  874.  *  Free all data associated with a file
  875.  */
  876.  
  877. void
  878. freedata(ramfile)
  879. RAMFILE *ramfile;
  880. {
  881.     FENTRY *fen;
  882.  
  883.     TotalBytes -= ramfile->bytes;
  884.     while (fen = RemHead(&ramfile->list)) {
  885.     dbprintf("FREE FEN: %08lx %08lx %ld\n", fen, fen->buf, fen->bytes);
  886.     FreeMem(fen->buf, fen->bytes);
  887.     FreeMem(fen, sizeof(*fen));
  888.     }
  889.     ramfile->bytes = 0;
  890.     DateStamp(&ramfile->date);
  891.     DateStamp(&ramfile->parent->date);
  892. }
  893.  
  894. /*
  895.  *  Unlink and remove a file.  Any data associated with the file or
  896.  *  directory has already been freed up.
  897.  */
  898.  
  899. void
  900. freeramfile(ramfile)
  901. RAMFILE *ramfile;
  902. {
  903.     Remove(ramfile);        /*  unlink from parent directory    */
  904.     if (ramfile->name)
  905.     FreeMem(ramfile->name,strlen(ramfile->name)+1);
  906.     if (ramfile->comment)
  907.     FreeMem(ramfile->comment,strlen(ramfile->comment)+1);
  908.     FreeMem(ramfile,sizeof(*ramfile));
  909. }
  910.  
  911. /*
  912.  *  The lock function.    The file has already been checked to see if it
  913.  *  is lockable given the mode.
  914.  */
  915.  
  916. LOCK *
  917. ramlock(ramfile, mode)
  918. RAMFILE *ramfile;
  919. {
  920.     LOCK *lock = dosalloc(sizeof(LOCK));
  921.     LOCKLINK *ln;
  922.  
  923.     if (mode != ACCESS_WRITE)
  924.     mode = ACCESS_READ;
  925.     ln = AllocMem(sizeof(LOCKLINK), MEMF_PUBLIC);
  926.     AddHead(&LCBase,ln);
  927.     ln->lock = lock;
  928.     lock->fl_Link= (long)ln;
  929.     lock->fl_Key = (long)ramfile;
  930.     lock->fl_Access = mode;
  931.     lock->fl_Task = &DosProc->pr_MsgPort;
  932.     lock->fl_Volume = (BPTR)CTOB(DosNode);
  933.     if (mode == ACCESS_READ)
  934.     ++ramfile->locks;
  935.     else
  936.     ramfile->locks = -1;
  937.     return(lock);
  938. }
  939.  
  940. void
  941. ramunlock(lock)
  942. LOCK *lock;
  943. {
  944.     RAMFILE *file = (RAMFILE *)lock->fl_Key;
  945.  
  946.     Remove(lock->fl_Link);            /* unlink from list */
  947.     FreeMem(lock->fl_Link, sizeof(LOCKLINK));    /* free link node   */
  948.     if (lock->fl_Access == ACCESS_READ)     /* undo lock effect */
  949.     --file->locks;
  950.     else
  951.     file->locks = 0;
  952.     dosfree(lock);                /* free lock        */
  953. }
  954.  
  955. /*
  956.  *  GETLOCKFILE(bptrlock)
  957.  *
  958.  *  Return the RAMFILE entry (file or directory) associated with the
  959.  *  given lock, which is passed as a BPTR.
  960.  *
  961.  *  According to the DOS spec, the only way a NULL lock will ever be
  962.  *  passed to you is if the DosNode->dn_Lock is NULL, but I'm not sure.
  963.  *  In anycase, If a NULL lock is passed to me I simply assume it means
  964.  *  the root directory of the RAM disk.
  965.  */
  966.  
  967. RAMFILE *
  968. getlockfile(lock)
  969. void *lock;        /*  actually BPTR to LOCK */
  970. {
  971.     register LOCK *rl = BTOC(lock);
  972.  
  973.     if (rl)
  974.     return((RAMFILE *)rl->fl_Key);
  975.     return(&RFRoot);
  976. }
  977.  
  978. /*
  979.  *  Search the specified path beginning at the specified directory.
  980.  *  The directory pointer is updated to the directory containing the
  981.  *  actual file.  Return the file node or NULL if not found.  If the
  982.  *  path is illegal (an intermediate directory was not found), set *ppar
  983.  *  to NULL and return NULL.
  984.  *
  985.  *  *ppar may also be set to NULL if the search path IS the root.
  986.  *
  987.  *  If pptr not NULL, Set *pptr to the final component in the path.
  988.  */
  989.  
  990. RAMFILE *
  991. searchpath(ppar,buf,pptr)
  992. RAMFILE **ppar;
  993. char *buf;
  994. char **pptr;
  995. {
  996.     RAMFILE *file = *ppar;
  997.     RAMFILE *srch;
  998.     short len;
  999.     char *ptr;
  1000.  
  1001.     *ppar = NULL;
  1002.     for (;*buf && file;) {
  1003.     ptr = getpathelement(&buf,&len);
  1004.     if (buf[0] == ':') {    /*  go to root          */
  1005.         ++buf;
  1006.         file = &RFRoot;
  1007.         continue;
  1008.     }
  1009.     if (*ptr == '/') {          /*  go back a directory */
  1010.         if (!file->parent) {    /*    no parent directory */
  1011.         return(NULL);
  1012.         }
  1013.         file = file->parent;
  1014.         continue;
  1015.     }
  1016.     if (file->type == FILE_FILE)
  1017.         return(NULL);
  1018.     for (srch = GetHead(&file->list); srch; srch = NextNode(srch)) {
  1019.         if (srch->type && strlen(srch->name) == len && nccmp(srch->name, ptr, len)) {
  1020.         file = srch;        /*    element found        */
  1021.         break;
  1022.         }
  1023.     }
  1024.     if (srch == NULL) {
  1025.         if (*buf == 0)    /*  Element not found.    If it was the final */
  1026.         *ppar = file;    /*  element the parent directory is valid   */
  1027.         if (pptr)
  1028.         *pptr = ptr;
  1029.         return(NULL);
  1030.     }
  1031.     }
  1032.     if (pptr)
  1033.     *pptr = ptr;
  1034.     *ppar = file->parent;
  1035.     return(file);
  1036. }
  1037.  
  1038. /*
  1039.  *  Return the next path element in the string.  The routine effectively
  1040.  *  removes any trailing '/'s, but treats ':' as part of the next component
  1041.  *  (i.e. ':' is checked and skipped in SEARCHPATH()).
  1042.  */
  1043.  
  1044. char *
  1045. getpathelement(pstr,plen)
  1046. char **pstr;
  1047. short *plen;
  1048. {
  1049.     char *base;
  1050.     register char *ptr = *pstr;
  1051.     register short len = 0;
  1052.  
  1053.     if (*(base = ptr)) {
  1054.     if (*ptr == '/') {
  1055.         ++ptr;
  1056.         ++len;
  1057.     } else {
  1058.         while (*ptr && *ptr != '/' && *ptr != ':') {
  1059.         ++ptr;
  1060.         ++len;
  1061.         }
  1062.         if (*ptr == '/')
  1063.         ++ptr;
  1064.     }
  1065.     }
  1066.     *pstr = ptr;
  1067.     *plen = len;
  1068.     return(base);
  1069. }
  1070.  
  1071.  
  1072. char *
  1073. typetostr(ty)
  1074. {
  1075.     switch(ty) {
  1076.     case ACTION_DIE:        return("DIE");
  1077.     case ACTION_OPENRW:     return("OPEN-RW");
  1078.     case ACTION_OPENOLD:    return("OPEN-OLD");
  1079.     case ACTION_OPENNEW:    return("OPEN-NEW");
  1080.     case ACTION_READ:        return("READ");
  1081.     case ACTION_WRITE:        return("WRITE");
  1082.     case ACTION_CLOSE:        return("CLOSE");
  1083.     case ACTION_SEEK:        return("SEEK");
  1084.     case ACTION_EXAMINE_NEXT:    return("EXAMINE NEXT");
  1085.     case ACTION_EXAMINE_OBJECT: return("EXAMINE OBJ");
  1086.     case ACTION_INFO:        return("INFO");
  1087.     case ACTION_DISK_INFO:    return("DISK INFO");
  1088.     case ACTION_PARENT:     return("PARENTDIR");
  1089.     case ACTION_DELETE_OBJECT:    return("DELETE");
  1090.     case ACTION_CREATE_DIR:    return("CREATEDIR");
  1091.     case ACTION_LOCATE_OBJECT:    return("LOCK");
  1092.     case ACTION_COPY_DIR:    return("DUPLOCK");
  1093.     case ACTION_FREE_LOCK:    return("FREELOCK");
  1094.     case ACTION_SET_PROTECT:    return("SETPROTECT");
  1095.     case ACTION_SET_COMMENT:    return("SETCOMMENT");
  1096.     case ACTION_RENAME_OBJECT:    return("RENAME");
  1097.     case ACTION_INHIBIT:    return("INHIBIT");
  1098.     case ACTION_RENAME_DISK:    return("RENAME DISK");
  1099.     case ACTION_MORECACHE:    return("MORE CACHE");
  1100.     case ACTION_WAIT_CHAR:    return("WAIT FOR CHAR");
  1101.     case ACTION_FLUSH:        return("FLUSH");
  1102.     case ACTION_RAWMODE:    return("RAWMODE");
  1103.     default:            return("---------UNKNOWN-------");
  1104.     }
  1105. }
  1106.  
  1107. /*
  1108.  *  DEBUGGING CODE.    You cannot make DOS library calls that access other
  1109.  *  devices from within a DOS device driver because they use the same
  1110.  *  message port as the driver.  If you need to make such calls you must
  1111.  *  create a port and construct the DOS messages yourself.  I do not
  1112.  *  do this.  To get debugging info out another PROCESS is created to which
  1113.  *  debugging messages can be sent.
  1114.  *
  1115.  *  You want the priority of the debug process to be larger than the
  1116.  *  priority of your DOS handler.  This is so if your DOS handler crashes
  1117.  *  you have a better idea of where it died from the debugging messages
  1118.  *  (remember that the two processes are asyncronous from each other).
  1119.  */
  1120.  
  1121. extern void debugproc();
  1122.  
  1123. dbinit()
  1124. {
  1125.     TASK *task = FindTask(NULL);
  1126.  
  1127.     Dback = CreatePort(NULL,NULL);
  1128.     CreateProc("DEV_DB", task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096);
  1129.     WaitPort(Dback);                    /* handshake startup    */
  1130.     GetMsg(Dback);                    /* remove dummy msg     */
  1131.     dbprintf("Debugger running V1.10, 2 November 1987\n");
  1132.     dbprintf("Works with WORKBENCH!\n");
  1133. }
  1134.  
  1135. dbuninit()
  1136. {
  1137.     MSG killmsg;
  1138.  
  1139.     if (Dbport) {
  1140.     killmsg.mn_Length = 0;        /*    0 means die        */
  1141.     PutMsg(Dbport,&killmsg);
  1142.     WaitPort(Dback);        /*    He's dead jim!      */
  1143.     GetMsg(Dback);
  1144.     DeletePort(Dback);
  1145.  
  1146.     /*
  1147.      *  Since the debug process is running at a greater priority, I
  1148.      *  am pretty sure that it is guarenteed to be completely removed
  1149.      *  before this task gets control again.  Still, it doesn't hurt...
  1150.      */
  1151.  
  1152.     Delay(50);            /*    ensure he's dead    */
  1153.     }
  1154. }
  1155.  
  1156. dbprintf(a,b,c,d,e,f,g,h,i,j)
  1157. {
  1158.     char buf[256];
  1159.     MSG *msg;
  1160.  
  1161.     if (Dbport && !DBDisable) {
  1162.     sprintf(buf,a,b,c,d,e,f,g,h,i,j);
  1163.     msg = AllocMem(sizeof(MSG)+strlen(buf)+1, MEMF_PUBLIC|MEMF_CLEAR);
  1164.     msg->mn_Length = strlen(buf)+1;     /*    Length NEVER 0    */
  1165.     strcpy(msg+1,buf);
  1166.     PutMsg(Dbport,msg);
  1167.     }
  1168. }
  1169.  
  1170. /*
  1171.  *  BTW, the DOS library used by debugmain() was actually openned by
  1172.  *  the device driver.    Note: DummyMsg cannot be on debugmain()'s stack
  1173.  *  since debugmain() goes away on the final handshake.
  1174.  */
  1175.  
  1176. debugmain()
  1177. {
  1178.     MSG *msg;
  1179.     short len;
  1180.     void *fh;
  1181.  
  1182.     Dbport = CreatePort(NULL,NULL);
  1183.     fh = Open("con:0/0/640/100/debugwindow", 1006);
  1184.     PutMsg(Dback, &DummyMsg);
  1185.     for (;;) {
  1186.     WaitPort(Dbport);
  1187.     msg = GetMsg(Dbport);
  1188.     len = msg->mn_Length;
  1189.     if (len == 0)
  1190.         break;
  1191.     --len;                  /*  Fix length up   */
  1192.     Write(fh, msg+1, len);
  1193.     FreeMem(msg,sizeof(MSG)+len+1);
  1194.     }
  1195.     Close(fh);
  1196.     DeletePort(Dbport);
  1197.     PutMsg(Dback,&DummyMsg);          /*  Kill handshake  */
  1198. }
  1199.  
  1200. /*
  1201.  *  The assembly tag for the DOS process:  CNOP causes alignment problems
  1202.  *  with the Aztec assembler for some reason.  I assume then, that the
  1203.  *  alignment is unknown.  Since the BCPL conversion basically zero's the
  1204.  *  lower two bits of the address the actual code may start anywhere around
  1205.  *  the label....  Sigh....  (see CreatProc() above).
  1206.  */
  1207.  
  1208. #asm
  1209.     public    _debugproc
  1210.     public    _debugmain
  1211.  
  1212.     cseg
  1213.     nop
  1214.     nop
  1215.     nop
  1216. _debugproc:
  1217.     nop
  1218.     nop
  1219.     movem.l D2-D7/A2-A6,-(sp)
  1220.     jsr    _debugmain
  1221.     movem.l (sp)+,D2-D7/A2-A6
  1222.     rts
  1223. #endasm
  1224.  
  1225.