home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 28 / amigaformatcd28.iso / -seriously_amiga- / programming / other / sashimi / source / sashimi.c next >
Encoding:
C/C++ Source or Header  |  1998-04-27  |  22.2 KB  |  964 lines

  1. /*
  2.  * $Id: sashimi.c 1.5 1998/04/07 21:16:28 olsen Exp olsen $
  3.  *
  4.  * Sashimi -- intercepts raw serial debugging output on your own machine
  5.  *
  6.  * Written by Olaf `Olsen' Barthel <olsen@sourcery.han.de>
  7.  * Public Domain
  8.  *
  9.  * :ts=4
  10.  */
  11.  
  12. #include <exec/memory.h>
  13.  
  14. #include <devices/timer.h>
  15.  
  16. #include <dos/dosextens.h>
  17. #include <dos/rdargs.h>
  18.  
  19. #include <clib/exec_protos.h>
  20. #include <clib/dos_protos.h>
  21.  
  22. #include <pragmas/exec_sysbase_pragmas.h>
  23. #include <pragmas/dos_pragmas.h>
  24.  
  25. #include <string.h>
  26. #include <stdio.h>
  27.  
  28. /****************************************************************************/
  29.  
  30. STRPTR Version = "$VER: Sashimi 1.3 (7.4.98)\r\n";
  31.  
  32. /****************************************************************************/
  33.  
  34. #define OK        (0)
  35. #define NOT        !
  36. #define BUSY    NULL
  37.  
  38. /****************************************************************************/
  39.  
  40. #define MILLION 1000000
  41.  
  42. /****************************************************************************/
  43.  
  44. extern struct Library * SysBase;
  45. extern struct Library * DOSBase;
  46.  
  47. /****************************************************************************/
  48.  
  49. typedef LONG    SWITCH;
  50. typedef LONG *    NUMBER;
  51. typedef STRPTR    KEY;
  52.  
  53. struct
  54. {
  55.     /* Startup options */
  56.     SWITCH    On;            /* Ignored */
  57.     NUMBER    BufferK;    /* Buffer size, to the power of two */
  58.     NUMBER    BufferSize;    /* Buffer size in bytes */
  59.     SWITCH    NoPrompt;    /* Do not show the initial prompt message */
  60.     SWITCH    Quiet;        /* Do not produce any output at all */
  61.     SWITCH    AskExit;    /* Ask whether to exit the program */
  62.     SWITCH    AskSave;    /* Ask for a file to save the buffer to when exiting */
  63.     SWITCH    TimerOn;    /* Check the ring buffer every 1/10 of a second */
  64.     SWITCH    Console;    /* Open a console window for I/O */
  65.     KEY        Window;        /* Console window specifier */
  66.  
  67.     /* Runtime options */
  68.     SWITCH    Off;        /* Turn Sashimi off */
  69.     SWITCH    Save;        /* Save the ring buffer contents */
  70.     KEY        SaveAs;        /* Save the ring buffer contents under a specific name */
  71.     SWITCH    Empty;        /* Empty the ring buffer */
  72. } ShellArguments;
  73.  
  74. const STRPTR ShellTemplate = "ON/S,BUFK/N,BUFFERSIZE/N,NOPROMPT/S,QUIET/S,ASKEXIT/S,"
  75.                              "ASKSAVE/S,TIMERON/S,CONSOLE/S,WINDOW/K,"
  76.                              "OFF/S,SAVE/S,SAVEAS/K,EMPTY/S";
  77.  
  78. /****************************************************************************/
  79.  
  80. struct SashimiResource
  81. {
  82.     struct Library    sr_Library;            /* Global link */
  83.  
  84.     struct Task *    sr_Owner;            /* Current owner of the patches */
  85.     LONG            sr_OwnerSigBit;
  86.     ULONG            sr_OwnerSigMask;    /* Signal mask to send when a new line is in the buffer. */
  87.  
  88.     UBYTE *            sr_FIFO;            /* The message buffer */
  89.     ULONG            sr_FIFOTotalSize;    /* Number of bytes allocated for the buffer */
  90.     ULONG            sr_FIFOReadIndex;    /* Read index counter */
  91.     ULONG            sr_FIFOWriteIndex;    /* Write index counter */
  92.     ULONG            sr_FIFOBytesStored;    /* Number of bytes in the FIFO */
  93.     BOOL            sr_FIFOOverrun;        /* TRUE if the write index counter has
  94.                                          * overrun the read index counter.
  95.                                          */
  96.     BOOL            sr_FIFOWrapped;        /* TRUE if the write index counter has
  97.                                          * wrapped around the ring buffer.
  98.                                          */
  99. };
  100.  
  101. struct SashimiResource * SashimiResource;
  102. const STRPTR SashimiResourceName = "sashimi.resource";
  103.  
  104. /****************************************************************************/
  105.  
  106. extern LONG __far LVORawIOInit;
  107. extern LONG __far LVORawMayGetChar;
  108. extern LONG __far LVORawPutChar;
  109.  
  110. /****************************************************************************/
  111.  
  112. APTR OldRawIOInit;
  113. APTR OldRawMayGetChar;
  114. APTR OldRawPutChar;
  115.  
  116. /****************************************************************************/
  117.  
  118. VOID __saveds __asm
  119. NewRawIOInit(VOID)
  120. {
  121. }
  122.  
  123. LONG __saveds __asm
  124. NewRawMayGetChar(VOID)
  125. {
  126.     return('y');
  127. }
  128.  
  129. /****************************************************************************/
  130.  
  131. VOID __saveds __asm
  132. NewRawPutChar(register __d0 UBYTE c)
  133. {
  134.     /* Do not store NUL bytes. */
  135.     if(c != '\0')
  136.     {
  137.         STATIC ULONG Position = 0;
  138.  
  139.         /* Filter out extra <cr> characters. */
  140.         if(c != '\r' || Position > 0)
  141.         {
  142.             struct SashimiResource * sr = SashimiResource;
  143.  
  144.             /* Store another bbyte in the buffer */
  145.             sr->sr_FIFO[sr->sr_FIFOWriteIndex] = c;
  146.             sr->sr_FIFOWriteIndex = (sr->sr_FIFOWriteIndex + 1) % sr->sr_FIFOTotalSize;
  147.  
  148.             /* Check if the ring buffer was overrun */
  149.             sr->sr_FIFOBytesStored++;
  150.             if(sr->sr_FIFOBytesStored >= sr->sr_FIFOTotalSize)
  151.             {
  152.                 sr->sr_FIFOOverrun = TRUE;
  153.  
  154.                 /* Move the read index to the same position as
  155.                  * the write index.
  156.                  */
  157.                 sr->sr_FIFOReadIndex = sr->sr_FIFOWriteIndex;
  158.             }
  159.  
  160.             /* If the buffer wraps around, remember it. */
  161.             if(sr->sr_FIFOWriteIndex == 0)
  162.                 sr->sr_FIFOWrapped = TRUE;
  163.  
  164.             /* Notify Sashimi every time there is an end of line
  165.              * character in the stream.
  166.              */
  167.             if(c == '\n' || c == '\r')
  168.                 Signal(sr->sr_Owner,sr->sr_OwnerSigMask);
  169.         }
  170.  
  171.         if(c == '\r' || c == '\n')
  172.             Position = 0;
  173.         else
  174.             Position++;
  175.     }
  176. }
  177.  
  178. /****************************************************************************/
  179.  
  180. VOID
  181. RemovePatches(VOID)
  182. {
  183.     APTR res;
  184.  
  185.     /* We disable the interrupts because the raw I/O routines can
  186.      * be called from within interrupt code.
  187.      */
  188.     Disable();
  189.  
  190.     /* For every patch planted, remove it and check whether the code
  191.      * had been patched before. If it has, restore the patch. Note that
  192.      * this is not bullet proof :(
  193.      */
  194.     res = SetFunction(SysBase,(LONG)&LVORawIOInit,(ULONG (*)())OldRawIOInit);
  195.     if(res != (APTR)NewRawIOInit)
  196.         SetFunction(SysBase,(LONG)&LVORawIOInit,(ULONG (*)())res);
  197.  
  198.     res = SetFunction(SysBase,(LONG)&LVORawMayGetChar,(ULONG (*)())OldRawMayGetChar);
  199.     if(res != (APTR)NewRawMayGetChar)
  200.         SetFunction(SysBase,(LONG)&LVORawMayGetChar,(ULONG (*)())res);
  201.  
  202.     res = SetFunction(SysBase,(LONG)&LVORawPutChar,(ULONG (*)())OldRawPutChar);
  203.     if(res != (APTR)NewRawPutChar)
  204.         SetFunction(SysBase,(LONG)&LVORawPutChar,(ULONG (*)())res);
  205.  
  206.     Enable();
  207. }
  208.  
  209. VOID
  210. InstallPatches(VOID)
  211. {
  212.     /* We disable the interrupts because the raw I/O routines can
  213.      * be called from within interrupt code.
  214.      */
  215.     Disable();
  216.  
  217.     OldRawIOInit        = SetFunction(SysBase,(LONG)&LVORawIOInit,        (ULONG (*)())NewRawIOInit);
  218.     OldRawMayGetChar    = SetFunction(SysBase,(LONG)&LVORawMayGetChar,    (ULONG (*)())NewRawMayGetChar);
  219.     OldRawPutChar        = SetFunction(SysBase,(LONG)&LVORawPutChar,        (ULONG (*)())NewRawPutChar);
  220.  
  221.     Enable();
  222. }
  223.  
  224. /****************************************************************************/
  225.  
  226. VOID
  227. FreeSashimiResource(struct SashimiResource * resource)
  228. {
  229.     if(resource != NULL)
  230.     {
  231.         FreeSignal(resource->sr_OwnerSigBit);
  232.  
  233.         FreeVec(resource->sr_FIFO);
  234.         FreeVec(resource);
  235.     }
  236. }
  237.  
  238. LONG
  239. RemoveSashimiResource(struct SashimiResource * resource)
  240. {
  241.     LONG error = OK;
  242.  
  243.     if(resource != NULL)
  244.     {
  245.         Forbid();
  246.  
  247.         /* Allow the resource to be removed only if
  248.          * there are no customers using it.
  249.          */
  250.         if(resource->sr_Library.lib_OpenCnt == 0)
  251.         {
  252.             RemResource(resource);
  253.         }
  254.         else
  255.         {
  256.             error = ERROR_OBJECT_IN_USE;
  257.         }
  258.  
  259.         Permit();
  260.     }
  261.  
  262.     return(error);
  263. }
  264.  
  265. LONG
  266. AddSashimiResource(ULONG bufferSize,struct SashimiResource ** resourcePtr)
  267. {
  268.     struct SashimiResource * resource;
  269.     LONG error = OK;
  270.  
  271.     resource = AllocVec(sizeof(*resource),MEMF_ANY|MEMF_CLEAR|MEMF_PUBLIC);
  272.     if(resource != NULL)
  273.     {
  274.         resource->sr_Library.lib_Node.ln_Name    = (char *)SashimiResourceName;
  275.         resource->sr_Library.lib_Node.ln_Type    = NT_RESOURCE;
  276.         resource->sr_Owner                        = FindTask(NULL);
  277.         resource->sr_FIFOTotalSize                = bufferSize;
  278.  
  279.         resource->sr_OwnerSigBit = AllocSignal(-1);
  280.         if(resource->sr_OwnerSigBit != -1)
  281.         {
  282.             resource->sr_OwnerSigMask = (1UL << resource->sr_OwnerSigBit);
  283.  
  284.             resource->sr_FIFO = AllocVec(resource->sr_FIFOTotalSize,MEMF_ANY|MEMF_PUBLIC);
  285.             if(resource->sr_FIFO != NULL)
  286.             {
  287.                 Forbid();
  288.  
  289.                 /* Do not add the resource if it has already been installed. */
  290.                 if(OpenResource((STRPTR)SashimiResourceName) == NULL)
  291.                 {
  292.                     AddResource(resource);
  293.                 }
  294.                 else
  295.                 {
  296.                     error = ERROR_OBJECT_EXISTS;
  297.                 }
  298.  
  299.                 Permit();
  300.             }
  301.             else
  302.             {
  303.                 error = ERROR_NO_FREE_STORE;
  304.             }
  305.         }
  306.         else
  307.         {
  308.             error = ERROR_NO_FREE_STORE;
  309.         }
  310.     }
  311.     else
  312.     {
  313.         error = ERROR_NO_FREE_STORE;
  314.     }
  315.  
  316.     if(error != OK)
  317.     {
  318.         FreeSashimiResource(resource);
  319.         resource = NULL;
  320.     }
  321.  
  322.     *resourcePtr = resource;
  323.  
  324.     return(error);
  325. }
  326.  
  327. /****************************************************************************/
  328.  
  329. VOID
  330. CloseSashimiResource(struct SashimiResource * resource)
  331. {
  332.     if(resource != NULL)
  333.     {
  334.         Forbid();
  335.  
  336.         resource->sr_Library.lib_OpenCnt--;
  337.  
  338.         Permit();
  339.     }
  340. }
  341.  
  342. struct SashimiResource *
  343. OpenSashimiResource(VOID)
  344. {
  345.     struct SashimiResource * resource;
  346.  
  347.     Forbid();
  348.  
  349.     resource = OpenResource((STRPTR)SashimiResourceName);
  350.     if(resource != NULL)
  351.     {
  352.         resource->sr_Library.lib_OpenCnt++;
  353.     }
  354.  
  355.     Permit();
  356.  
  357.     return(resource);
  358. }
  359.  
  360. /****************************************************************************/
  361.  
  362. VOID
  363. EmptyBuffer(struct SashimiResource * resource)
  364. {
  365.     Disable();
  366.  
  367.     resource->sr_FIFOReadIndex        = 0;
  368.     resource->sr_FIFOWriteIndex        = 0;
  369.     resource->sr_FIFOBytesStored    = 0;
  370.     resource->sr_FIFOOverrun        = FALSE;
  371.     resource->sr_FIFOWrapped        = FALSE;
  372.  
  373.     Enable();
  374. }
  375.  
  376. LONG
  377. SaveBuffer(const STRPTR name,struct SashimiResource * resource)
  378. {
  379.     STRPTR bufferCopy;
  380.     LONG totalSize;
  381.     LONG error = OK;
  382.  
  383.     totalSize = resource->sr_FIFOTotalSize;
  384.  
  385.     /* We allocate a temporary buffer to store the ring
  386.      * buffer data in.
  387.      */
  388.     bufferCopy = AllocVec(totalSize,MEMF_ANY|MEMF_PUBLIC);
  389.     if(bufferCopy != NULL)
  390.     {
  391.         BOOL wrapped;
  392.         BOOL overrun;
  393.         LONG writeIndex;
  394.         LONG bytesInBuffer;
  395.         BPTR file;
  396.  
  397.         wrapped = resource->sr_FIFOWrapped;
  398.         overrun = resource->sr_FIFOOverrun;
  399.  
  400.         /* The index counter indicates how many bytes
  401.          * have been written to the ring buffer, unless
  402.          * the buffer index has wrapped around already.
  403.          */
  404.         writeIndex = bytesInBuffer = resource->sr_FIFOWriteIndex;
  405.  
  406.         /* If the buffer index has wrapped around, then
  407.          * the entire buffer is filled with data.
  408.          */
  409.         if(wrapped)
  410.             bytesInBuffer = totalSize;
  411.  
  412.         /* Make a copy of the current buffer contents. */
  413.         if(bytesInBuffer > 0)
  414.         {
  415.             Forbid();
  416.  
  417.             /* For a wrapped buffer, "unfold" the FIFO. */
  418.             if(writeIndex < bytesInBuffer && wrapped)
  419.             {
  420.                 /* Store the oldest buffer contents first. */
  421.                 CopyMem(resource->sr_FIFO + writeIndex,bufferCopy,bytesInBuffer - writeIndex);
  422.  
  423.                 /* Add the most recent data. */
  424.                 CopyMem(resource->sr_FIFO,bufferCopy + bytesInBuffer - writeIndex,writeIndex);
  425.             }
  426.             else
  427.             {
  428.                 CopyMem(resource->sr_FIFO,bufferCopy,bytesInBuffer);
  429.             }
  430.  
  431.             Permit();
  432.         }
  433.  
  434.         /* Write the buffer contents. */
  435.         file = Open((STRPTR)name,MODE_NEWFILE);
  436.         if(file != NULL)
  437.         {
  438.             if(overrun)
  439.             {
  440.                 if(FPrintf(file,"BUFFER WAS OVERRUN - Data may have been lost\n") < 0)
  441.                     error = IoErr();
  442.             }
  443.  
  444.             if(error == OK && wrapped)
  445.             {
  446.                 if(FPrintf(file,"BUFFER WRAPPED - This is the most recent captured data\n\n") < 0)
  447.                     error = IoErr();
  448.             }
  449.  
  450.             /* FPrintf() is a buffered I/O routine, this is why we need to flush the
  451.              * output buffer here. Otherwise, it would be flushed after the Write()
  452.              * command below is finished and the file is closed. This is not what
  453.              * we want as that would have the effect of adding the messages above
  454.              * to the end of the file.
  455.              */
  456.             if(error == OK)
  457.             {
  458.                 Flush(file);
  459.             }
  460.  
  461.             if(error == OK && bytesInBuffer > 0)
  462.             {
  463.                 if(Write(file,bufferCopy,bytesInBuffer) != bytesInBuffer)
  464.                     error = IoErr();
  465.             }
  466.  
  467.             Close(file);
  468.         }
  469.         else
  470.         {
  471.             error = IoErr();
  472.         }
  473.  
  474.         FreeVec(bufferCopy);
  475.     }
  476.     else
  477.     {
  478.         error = ERROR_NO_FREE_STORE;
  479.     }
  480.  
  481.     return(error);
  482. }
  483.  
  484. /****************************************************************************/
  485.  
  486. int
  487. main(int argc,char **argv)
  488. {
  489.     int result = RETURN_FAIL;
  490.  
  491.     /* Kickstart 2.04 and a Shell window are required. */
  492.     if(DOSBase->lib_Version >= 37 && argc > 0)
  493.     {
  494.         struct RDArgs * rdargs;
  495.  
  496.         rdargs = ReadArgs((STRPTR)ShellTemplate,(LONG *)&ShellArguments,NULL);
  497.         if(rdargs != NULL)
  498.         {
  499.             struct SashimiResource * resource;
  500.             struct MsgPort * timePort = NULL;
  501.             struct timerequest * timeRequest = NULL;
  502.             BOOL added = FALSE;
  503.             BOOL opened = FALSE;
  504.             LONG error = OK;
  505.             BPTR oldOutput = NULL;
  506.             BPTR newOutput = NULL;
  507.             BPTR oldInput = NULL;
  508.             BPTR newInput = NULL;
  509.             struct MsgPort * oldConsoleTask = NULL;
  510.             STRPTR saveFile;
  511.  
  512.             /* Fill in the save file name, we might need it later. */
  513.             if(ShellArguments.SaveAs != NULL)
  514.                 saveFile = ShellArguments.SaveAs;
  515.             else
  516.                 saveFile = "T:sashimi.out";
  517.  
  518.             /* Try to open the resource, and if that fails, create one. */
  519.             resource = OpenSashimiResource();
  520.             if(resource != NULL)
  521.             {
  522.                 opened = TRUE;
  523.             }
  524.             else
  525.             {
  526.                 ULONG bufferSize;
  527.  
  528.                 /* The default buffer size is 32K. */
  529.                 bufferSize = 32 * 1024;
  530.  
  531.                 /* Check for a specific buffer size (power of two). */
  532.                 if(ShellArguments.BufferK != NULL)
  533.                     bufferSize = 1024 * (*ShellArguments.BufferK);
  534.  
  535.                 /* Check for a specific buffer size. */
  536.                 if(ShellArguments.BufferSize != NULL)
  537.                     bufferSize = (ULONG)(*ShellArguments.BufferSize);
  538.  
  539.                 /* Don't make the buffer too small. */
  540.                 if(bufferSize < 4096)
  541.                     bufferSize = 4096;
  542.  
  543.                 /* Add the resource to the public list. Note that
  544.                  * the patches are not installed yet.
  545.                  */
  546.                 error = AddSashimiResource(bufferSize,&resource);
  547.                 if(error == OK)
  548.                 {
  549.                     added = TRUE;
  550.  
  551.                     /* Check if we should periodically check
  552.                      * the ring buffer.
  553.                      */
  554.                     if(ShellArguments.TimerOn)
  555.                     {
  556.                         error = ERROR_NO_FREE_STORE;
  557.  
  558.                         /* Set up the timer.device interface. */
  559.                         timePort = CreateMsgPort();
  560.                         if(timePort != NULL)
  561.                         {
  562.                             timeRequest = (struct timerequest *)CreateIORequest(timePort,sizeof(*timeRequest));
  563.                             if(timeRequest != NULL)
  564.                             {
  565.                                 if(OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)timeRequest,0) == OK)
  566.                                     error = OK;
  567.                             }
  568.                         }
  569.                     }
  570.                 }
  571.             }
  572.  
  573.             /* Did we get everything we wanted? */
  574.             if(error != OK)
  575.             {
  576.                 PrintFault(error,"Sashimi");
  577.                 result = RETURN_ERROR;
  578.             }
  579.             else
  580.             {
  581.                 if(opened)
  582.                 {
  583.                     /* Save the current ring buffer contents? */
  584.                     if(ShellArguments.SaveAs != NULL || ShellArguments.Save)
  585.                     {
  586.                         LONG error;
  587.  
  588.                         error = SaveBuffer(saveFile,resource);
  589.                         if(error == OK)
  590.                             Printf("Sashimi buffer saved as \"%s\".\n",saveFile);
  591.                         else
  592.                             PrintFault(error,saveFile);
  593.                     }
  594.  
  595.                     /* Empty the ring buffer? */
  596.                     if(ShellArguments.Empty)
  597.                     {
  598.                         EmptyBuffer(resource);
  599.  
  600.                         Printf("Sashimi buffer cleared.\n");
  601.                     }
  602.  
  603.                     /* Turn off Sashimi? */
  604.                     if(ShellArguments.Off)
  605.                     {
  606.                         struct Task * owner;
  607.  
  608.                         Forbid();
  609.  
  610.                         /* We cannot tell Sashimi to quit
  611.                          * if there is a single customer
  612.                          * left, such as us. This is why
  613.                          * we close the resource and
  614.                          * signal Sashimi to quit.
  615.                          */
  616.                         owner = resource->sr_Owner;
  617.                         CloseSashimiResource(resource);
  618.                         resource = NULL;
  619.  
  620.                         Signal(owner,SIGBREAKF_CTRL_C);
  621.  
  622.                         Permit();
  623.                     }
  624.                 }
  625.  
  626.                 if(added)
  627.                 {
  628.                     ULONG signals,signalMask;
  629.                     BOOL done;
  630.  
  631.                     /* Open a console window? */
  632.                     if(ShellArguments.Console)
  633.                     {
  634.                         STRPTR consoleWindow;
  635.                         LONG error = OK;
  636.  
  637.                         if(ShellArguments.Window != NULL)
  638.                             consoleWindow = ShellArguments.Window;
  639.                         else
  640.                             consoleWindow = "CON:0/20/640/100/Sashimi  [Ctrl]+E=Empty  [Ctrl]+F=File  [Ctrl]+D=Reset console/AUTO/CLOSE/WAIT/INACTIVE";
  641.  
  642.                         /* Open the window and make it the default
  643.                          * I/O stream.
  644.                          */
  645.                         newInput = Open(consoleWindow,MODE_NEWFILE);
  646.                         if(newInput != NULL)
  647.                         {
  648.                             oldConsoleTask = SetConsoleTask(((struct FileHandle *)BADDR(newInput))->fh_Type);
  649.                             newOutput = Open("CONSOLE:",MODE_OLDFILE);
  650.                             if(newOutput != NULL)
  651.                             {
  652.                                 oldInput = SelectInput(newInput);
  653.                                 oldOutput = SelectOutput(newOutput);
  654.                             }
  655.                             else
  656.                             {
  657.                                 error = IoErr();
  658.  
  659.                                 /* Return to the original console task. */
  660.                                 SetConsoleTask(oldConsoleTask);
  661.                                 oldConsoleTask = NULL;
  662.                             }
  663.                         }
  664.                         else
  665.                         {
  666.                             error = IoErr();
  667.                         }
  668.  
  669.                         if(error != OK)
  670.                             PrintFault(error,consoleWindow);
  671.                     }
  672.  
  673.                     /* Show the banner message. */
  674.                     if(NOT ShellArguments.NoPrompt && NOT ShellArguments.Quiet)
  675.                     {
  676.                         struct Process * cli = (struct Process *)FindTask(NULL);
  677.                         LONG maxCli,thisCli = 1,i;
  678.  
  679.                         /* Find our current CLI process number. */
  680.                         maxCli = MaxCli();
  681.                         for(i = 1 ; i <= maxCli ; i++)
  682.                         {
  683.                             if(FindCliProc(i) == cli)
  684.                             {
  685.                                 thisCli = i;
  686.                                 break;
  687.                             }
  688.                         }
  689.  
  690.                         Printf("Sashimi installed ([Ctrl]+C or \"Break %ld\" to remove)\n",thisCli);
  691.                     }
  692.  
  693.                     SashimiResource = resource;
  694.                     InstallPatches();
  695.  
  696.                     signalMask = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E |
  697.                                  SIGBREAKF_CTRL_F | resource->sr_OwnerSigMask;
  698.  
  699.                     /* Start the timer. */
  700.                     if(timePort != NULL)
  701.                     {
  702.                         signalMask |= (1UL << timePort->mp_SigBit);
  703.  
  704.                         timeRequest->tr_node.io_Command    = TR_ADDREQUEST;
  705.                         timeRequest->tr_time.tv_secs    = 0;
  706.                         timeRequest->tr_time.tv_micro    = MILLION / 10;
  707.  
  708.                         SendIO((struct IORequest *)timeRequest);
  709.                     }
  710.  
  711.                     done = FALSE;
  712.                     do
  713.                     {
  714.                         signals = Wait(signalMask);
  715.  
  716.                         /* Check if we should test the buffer. */
  717.                         if(timePort != NULL)
  718.                         {
  719.                             if(signals & (1UL << timePort->mp_SigBit))
  720.                             {
  721.                                 signals |= resource->sr_OwnerSigMask;
  722.  
  723.                                 WaitIO((struct IORequest *)timeRequest);
  724.  
  725.                                 /* Restart the timer. */
  726.                                 timeRequest->tr_node.io_Command    = TR_ADDREQUEST;
  727.                                 timeRequest->tr_time.tv_secs    = 0;
  728.                                 timeRequest->tr_time.tv_micro    = MILLION / 10;
  729.  
  730.                                 SendIO((struct IORequest *)timeRequest);
  731.                             }
  732.                         }
  733.  
  734.                         /* Check if we should test the buffer. */
  735.                         if(signals & resource->sr_OwnerSigMask)
  736.                         {
  737.                             if(NOT ShellArguments.Quiet)
  738.                             {
  739.                                 struct SashimiResource * sr = SashimiResource;
  740.                                 UBYTE localBuffer[256];
  741.                                 LONG filled = 0;
  742.  
  743.                                 /* Try to empty the ring buffer. */
  744.                                 while(sr->sr_FIFOBytesStored > 0)
  745.                                 {
  746.                                     /* Read the next byte. */
  747.                                     localBuffer[filled++] = sr->sr_FIFO[sr->sr_FIFOReadIndex];
  748.  
  749.                                     sr->sr_FIFOBytesStored--;
  750.                                     sr->sr_FIFOReadIndex = (sr->sr_FIFOReadIndex + 1) % sr->sr_FIFOTotalSize;
  751.  
  752.                                     /* Has the buffer been filled to the brim? */
  753.                                     if(filled == sizeof(localBuffer))
  754.                                     {
  755.                                         ULONG moreSignals;
  756.  
  757.                                         /* Check if there is a message for us. */
  758.                                         moreSignals = SetSignal(0,SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F);
  759.  
  760.                                         /* Save the ring buffer to a file? */
  761.                                         if(moreSignals & SIGBREAKF_CTRL_F)
  762.                                         {
  763.                                             LONG error;
  764.  
  765.                                             error = SaveBuffer(saveFile,resource);
  766.                                             if(error == OK)
  767.                                                 Printf("Sashimi buffer saved as \"%s\".\n",saveFile);
  768.                                             else
  769.                                                 PrintFault(error,saveFile);
  770.                                         }
  771.  
  772.                                         /* Empty the ring buffer? */
  773.                                         if(moreSignals & SIGBREAKF_CTRL_E)
  774.                                         {
  775.                                             EmptyBuffer(resource);
  776.  
  777.                                             Printf("Sashimi buffer cleared.\n");
  778.                                         }
  779.  
  780.                                         /* Stop Sashimi? */
  781.                                         if(moreSignals & SIGBREAKF_CTRL_C)
  782.                                         {
  783.                                             signals |= SIGBREAKF_CTRL_C;
  784.  
  785.                                             filled = 0;
  786.                                             break;
  787.                                         }
  788.  
  789.                                         /* Write the buffer to the file. */
  790.                                         Write(Output(),localBuffer,filled);
  791.                                         filled = 0;
  792.                                     }
  793.                                 }
  794.  
  795.                                 /* Store the remaining buffer contents. */
  796.                                 if(filled > 0)
  797.                                     Write(Output(),localBuffer,filled);
  798.                             }
  799.                         }
  800.  
  801.                         /* Save current buffer to file. */
  802.                         if(signals & SIGBREAKF_CTRL_F)
  803.                         {
  804.                             LONG error;
  805.  
  806.                             error = SaveBuffer(saveFile,resource);
  807.                             if(error == OK)
  808.                                 Printf("Sashimi buffer saved as \"%s\".\n",saveFile);
  809.                             else
  810.                                 PrintFault(error,saveFile);
  811.                         }
  812.  
  813.                         /* Empty the buffer. */
  814.                         if(signals & SIGBREAKF_CTRL_E)
  815.                         {
  816.                             EmptyBuffer(resource);
  817.  
  818.                             Printf("Sashimi buffer cleared.\n");
  819.                         }
  820.  
  821.                         /* Reset the terminal. */
  822.                         if(signals & SIGBREAKF_CTRL_D)
  823.                         {
  824.                             Printf("\033c");
  825.                             Flush(Output());
  826.                         }
  827.  
  828.                         /* Terminate the program. */
  829.                         if(signals & SIGBREAKF_CTRL_C)
  830.                         {
  831.                             BOOL terminate = FALSE;
  832.  
  833.                             if(ShellArguments.AskExit)
  834.                             {
  835.                                 UBYTE buffer[4];
  836.  
  837.                                 Printf("\nSashimi: stop signal received -- really exit (y or n)? ");
  838.                                 Flush(Output());
  839.  
  840.                                 buffer[0] = '\0';
  841.  
  842.                                 if(FGets(Input(),buffer,sizeof(buffer)-1) != NULL)
  843.                                 {
  844.                                     if(buffer[0] == 'y' || buffer[0] == 'Y')
  845.                                         terminate = TRUE;
  846.                                 }
  847.                             }
  848.                             else
  849.                             {
  850.                                 terminate = TRUE;
  851.                             }
  852.  
  853.                             if(terminate)
  854.                             {
  855.                                 if(RemoveSashimiResource(resource) == OK)
  856.                                 {
  857.                                     Printf("Sashimi removed.\n");
  858.                                     done = TRUE;
  859.                                 }
  860.                             }
  861.                         }
  862.                     }
  863.                     while(NOT done);
  864.  
  865.                     RemovePatches();
  866.  
  867.                     /* Stop the timer. */
  868.                     if(timePort != NULL)
  869.                     {
  870.                         if(CheckIO((struct IORequest *)timeRequest) == BUSY)
  871.                             AbortIO((struct IORequest *)timeRequest);
  872.  
  873.                         WaitIO((struct IORequest *)timeRequest);
  874.                     }
  875.  
  876.                     /* Check if we should and could save the ring buffer. */
  877.                     if(ShellArguments.AskSave && (resource->sr_FIFOWrapped || resource->sr_FIFOWriteIndex > 0))
  878.                     {
  879.                         UBYTE name[256];
  880.  
  881.                         Printf("Enter name to save the buffer, or hit [Return] to cancel: ");
  882.                         Flush(Output());
  883.  
  884.                         name[0] = '\0';
  885.  
  886.                         if(FGets(Input(),name,sizeof(name)-1) != NULL)
  887.                         {
  888.                             LONG error;
  889.                             int i;
  890.  
  891.                             for(i = strlen(name)-1 ; i >= 0 ; i--)
  892.                             {
  893.                                 if(name[i] == '\n')
  894.                                     name[i] = '\0';
  895.                             }
  896.  
  897.                             error = SaveBuffer(name,resource);
  898.                             if(error == OK)
  899.                                 Printf("Sashimi buffer saved as \"%s\".\n",name);
  900.                             else
  901.                                 PrintFault(error,name);
  902.                         }
  903.                     }
  904.  
  905.                     FreeSashimiResource(resource);
  906.                     resource = NULL;
  907.                 }
  908.  
  909.                 result = RETURN_OK;
  910.             }
  911.  
  912.             /* Close the resource, if we opened it. */
  913.             if(opened)
  914.             {
  915.                 CloseSashimiResource(resource);
  916.             }
  917.  
  918.             /* Remove and free the resource if we added it. */
  919.             if(added)
  920.             {
  921.                 RemoveSashimiResource(resource);
  922.                 FreeSashimiResource(resource);
  923.             }
  924.  
  925.             /* Clean up the timer.device interface. */
  926.             if(timeRequest != NULL)
  927.             {
  928.                 if(timeRequest->tr_node.io_Device != NULL)
  929.                     CloseDevice((struct IORequest *)timeRequest);
  930.  
  931.                 DeleteIORequest((struct IORequest *)timeRequest);
  932.             }
  933.  
  934.             DeleteMsgPort(timePort);
  935.  
  936.             /* Reset and clean up the console I/O streams. */
  937.             if(oldOutput != NULL)
  938.                 SelectOutput(oldOutput);
  939.  
  940.             if(oldInput != NULL)
  941.                 SelectInput(oldInput);
  942.  
  943.             if(newOutput != NULL)
  944.                 Close(newOutput);
  945.  
  946.             if(oldConsoleTask)
  947.                 SetConsoleTask(oldConsoleTask);
  948.  
  949.             if(newInput != NULL)
  950.                 Close(newInput);
  951.  
  952.             FreeArgs(rdargs);
  953.         }
  954.         else
  955.         {
  956.             PrintFault(IoErr(),"Sashimi");
  957.  
  958.             result = RETURN_ERROR;
  959.         }
  960.     }
  961.  
  962.     return(result);
  963. }
  964.