[comp.sys.amiga] Cmd.c - sources and icon

carolyn@cbmvax.UUCP (05/28/87)

   For those of you who have asked for the ability to capture printer
output in a file...
 
   Cmd redirects serial.device or parallel.device output to a file.
This allows you to capture the printer output of many applications
(including screen dumps) in a file.

   Information on usage and options can be found in the initial comments
and usage strings (u1[], u2[], ...) of Cmd.c.

   What follows are 4 files:
      Cmd.info.uu - Uuencoded icon with Tooltypes
      Cmd.with ---- Linkage info (Lattice) for Cmd
      Cmda.asm ---- Assembler device function interface code
      Cmd.c ------- The main program
 
---------------------------- cut here ----------------------------------
begin 777 Cmd.Info
MXQ```0``````>``,`$X`$``$``,``0`@DM``````````````````````````@
M`````P@``````"%OD````&8````!``````````````````````!.``\``@`!R
MG%`#`````````````````````````````````````````#Y_Y@`````````^0
M?^>`"JJJ@`8`/@`'P`U558`'@#___\``````_^`____``````/_X/___P```:
M``#_X#___\`,S,S`!X`____`!S,S@`8`/___P````````#___\`````````_P
M___`````````````````````````````````````````````````____````[
M`````,&`&<`?___```#!@!AP-5558`8`P?_X,#*JJF`'@,```#`?___`_^#`&
M```P`````/_XP```,!___^#_X,```#`S,S,P!X#````P&,S,8`8`P```,`__W
M_\```,```#````````#````P````````____\``````````````````8````D
M$$1%5DE#13UP87)A;&QE;``````21DE,13UR86TZ0TU$7V9I;&4`````"U-+O
M25`]1D%,4T4`````#TU53%1)4$Q%/49!3%-%``````U.3U1)1ED]1D%,4T4`R
``
end
---------------------------- cut here ----------------------------------
FROM    LIB:Astartup.obj, cmd.o, cmda.o
TO      cmd
LIBRARY LIB:Amiga.lib,LIB:LC.lib
---------------------------- cut here ----------------------------------

*   cmda.asm  --- assembler interface for Cmd.c
*                 Carolyn Scheppner --- CBM  05/87

   INCLUDE   'exec/types.i'

   XREF   _AbsExecBase
   XREF   _MyBeginIO
   XREF   _MyClose
   XREF   _RealClose

   XDEF   _myBeginIO
   XDEF   _myClose
   XDEF   _myExpunge

   CODE


_myBeginIO:
   movem.l d0-d7/a0-a6,-(a7)   ;save registers

   move.l  a1,-(a7)            ;push ptr to io request
   jsr     _MyBeginIO
   addq.l  #4,a7

   movem.l (a7)+,d0-d7/a0-a6   ;restore registers
   rts


_myClose:
   movem.l d0-d7/a0-a6,-(a7)   ;save registers

   move.l  a1,-(a7)            ;push ptr to io request
   jsr     _MyClose
   addq.l  #4,a7

   movem.l (a7)+,d0-d7/a0-a6   ;restore registers

   move.l  _RealClose,a0       ;continue to real Close
   jmp     (a0)


_myExpunge:
   moveq.l #0,d0               ;means unable to expunge
   rts                         ;keep changed device from being expunged

   END

---------------------------- cut here ----------------------------------

/* Cmd.c --- v1 --- Carolyn Scheppner  CBM  05/87
 *
 * Copyright (c) 1987  Commodore Business Machines - All Rights Reserved
 *    This code may be freely non-commercially redistributed.
 *
 * Redirects exec serial or parallel device CMD_WRITEs to a file
 *  (for the purpose of capturing printer output in a file)
 * Built upon fragments of Read (author?) and NoFastMem (Andy Finkel)
 *
 * CLI Usage:  [run] cmd [-s] [-m] [-n] devicename filename
 *   -s (Skip) skips any short initial write (usually a Reset if screendump)
 *   -m (Multiple) causes cmd to remain installed for multiple files
 *   -n (Notify) enables helpful progress messages 
 *   devicename serial or parallel
 *
 * WB Usage:  Just doubleclick.
 *            Specify the args in your icon's ToolTypes (use WB Info)
 *            Built-in defaults are:
 *               DEVICE=parallel
 *               FILE=ram:CMD_file
 *               SKIP=FALSE
 *               MULTIPLE=FALSE
 *               NOTIFY=FALSE
 *
 *   Note: On a screen dump, first CMD_WRITE is usually a printer RESET.
 *         The printer device then delays long enough for the reset
 *         to complete, to prevent the loss of subsequent output data.
 *         When the dump is instead captured in a file, this delay
 *         is of course lost.  If your printer driver outputs a reset
 *         at the start of a dump (as ours do), use the -s (SKIP) option
 *         to keep the initial CMD_WRITE out of the file.
 *
 * Sorry about the busywait synchronization of the device wedge
 * and the main process.  The purpose was to avoid unnecessary
 * meddling with the message structures and the device's signals.
 *
 * Linkage info (requires assembler module cmda):
 *    FROM    LIB:Astartup.obj, cmd.o, cmda.o
 *    TO      cmd
 *    LIBRARY LIB:Amiga.lib,LIB:LC.lib
 */

#include "exec/types.h" 
#include "exec/memory.h" 
#include "exec/io.h"
#include "exec/libraries.h"
#include "libraries/dos.h" 
#include "libraries/dosextens.h"
#include "workbench/startup.h"
#include "workbench/workbench.h"
#include "devices/serial.h" 
#include "devices/parallel.h" 
 
#define TOUPPER(c)      ((c)>='a'&&(c)<='z'?(c)-'a'+'A':(c)) 
#define HIGHER(x,y)     ((x)>(y)?(x):(y))
#define INBUFLEN  40
#define REQSIZE  120    /* should be big enough or any OpenDevice */

#define DEV_CLOSE    LIB_CLOSE
#define DEV_EXPUNGE  LIB_EXPUNGE
/*      DEV_BEGINIO  (-30)       defined in exec/io.h */

#define OPEN_SIG   SIGBREAKF_CTRL_E
#define WRITE_SIG  SIGBREAKF_CTRL_F
#define CLOSE_SIG  SIGBREAKF_CTRL_D
#define BREAK_SIG  SIGBREAKF_CTRL_C

#define SHORT_WRITE (8L)

extern VOID  myBeginIO();  /* The assembler entry */
extern VOID  myClose();    /* The assembler entry */
extern VOID  myExpunge();  /* The assembler routine */

extern struct MsgPort *CreatePort(); 
extern struct WBStartup *WBenchMsg;

ULONG  RealBeginIO, NewBeginIO;
ULONG  RealClose,   NewClose;
ULONG  RealExpunge, NewExpunge;

char *noMem      = "Out of memory\n";
char *portName   = "cas_TMP_CMD_PORT";
char *conSpec    = "CON:20/20/600/40/ CMD ";

char u1[]={"\nCLI Usage: [run] [-s] [-m] [-n] Cmd devicename filename\n"};
char u2[]={"  devicename = serial or parallel\n"};
char u3[]={"  -s = SKIP any short initial write (usually a reset if screendump)\n"};
char u4[]={"  -m = installed for MULTIPLE files until Break or CTRL_C\n"};
char u5[]={"  -n = enables NOTIFY (helpful progress messages)\n\n"};
char u6[]={"WB Tooltypes: DEVICE, FILE, and booleans SKIP,MULTIPLE,NOTIFY\n"};
char u7[]={"   Cancel installation for multiple files by reclicking\n\n"};
char *us[7] = {u1,u2,u3,u4,u5,u6,u7};
char *morehelp = "Type  cmd ?  for more help\n\n";

char *prevTaskName = NULL;
char *outFileName, *deviceName;
char mainTaskName[40];
char wbDev[INBUFLEN], wbFile[INBUFLEN];
char sbuf[120];

struct Device *TheDevice;
struct Task   *otherTask, *mainTask;

struct IOStdReq *myReq, *ioR;
struct MsgPort  *port; 


LONG  wLen = 1, outFile = NULL;
ULONG total = 0;
ULONG IconBase = NULL;
BOOL  Error1 = TRUE, Skip = FALSE, Multiple = FALSE, Notify = FALSE;
BOOL  Done = FALSE, FromWb = FALSE, MainBusy = FALSE;
int   reqcnt = 0, writecnt = 0, filecnt = 0; fnLen;


VOID MyBeginIO(ior)
struct IOStdReq *ior;
   {
   reqcnt += 1;
   if(ior->io_Command == CMD_WRITE)
      {
      if(!writecnt)
         {
         while(MainBusy);
         MainBusy = TRUE;
         Signal(mainTask,OPEN_SIG);
         while(MainBusy);
         }

      if((!Skip)||(writecnt>0)||(ior->io_Length > SHORT_WRITE))
         {
         while(MainBusy);
         MainBusy = TRUE;
         ioR = ior;
         Signal(mainTask,WRITE_SIG);  /* Signal write */
         while(MainBusy);
         }
      writecnt += 1;
      ior->io_Actual = ior->io_Length;
      }
   if(!(ior->io_Flags & IOF_QUICK))  ReplyMsg(ior);
   }

VOID MyClose(ior)
struct IOStdReq *ior;
   {
   /* Note - Exec has us in a forbid here */
   if(reqcnt) /* Ignores DOS's initial Open/Close/Open */
      {
      Signal(mainTask,CLOSE_SIG);  /* Signal Close */
      }
   }

main(argc, argv) 
UWORD argc; 
TEXT *argv[]; 
   { 
   ULONG signals;
   int k;

   FromWb = (argc==0) ? TRUE : FALSE;

   if(FromWb)
      {
      getWbArgs(WBenchMsg);
      deviceName  = wbDev;
      outFileName = wbFile;
      }
   else
      {
      if(strEqu(argv[1], "?"))  usageHelpExit();
      if(argc<3) usageExit();

      for(k=1; argv[k][0]=='-'; k++)
         {
         if(argv[k][1] == 's')  Skip = TRUE;
         if(argv[k][1] == 'm')  Multiple = TRUE;
         if(argv[k][1] == 'n')  Notify = TRUE;
         }
      if(argc-k < 2)  usageExit();
      deviceName  = argv[k++];
      outFileName = argv[k];
      }

   fnLen = strlen(outFileName); /* Used if Multiple extension added */

   /* Result will be mainTaskName = "cas_CMD_whatever.device"
    *   with deviceName pointing to the eighth character
    */
   strcpy(&mainTaskName[0],"cas_CMD_");
   strcpy(&mainTaskName[strlen(mainTaskName)],deviceName);
   strcpy(&mainTaskName[strlen(mainTaskName)],".device");
   deviceName = &mainTaskName[8];

   Forbid();
   if(otherTask = (struct Task *)FindTask(mainTaskName))
      {
      Permit();
      if(FromWb) Signal(otherTask,BREAK_SIG);
      else printf("Device already redirected... exiting\n");
      cleanexit();
      }

   mainTask = (struct Task *)FindTask(NULL);
   prevTaskName = mainTask->tc_Node.ln_Name;
   mainTask->tc_Node.ln_Name = mainTaskName;
   Permit();
     
   /* initialize */
   if(!(port = CreatePort(portName, 0)))  cleanexit("Can't open port\n");
 
   myReq = (struct IOStdReq *)AllocMem(REQSIZE,MEMF_CLEAR|MEMF_PUBLIC);
   if (!myReq)  cleanexit(noMem);

   myReq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
   myReq->io_Message.mn_ReplyPort = port;

   if(OpenDevice(deviceName, 0, myReq, 0))
      {
      sprintf(sbuf,"Can't open %s\n",deviceName);
      cleanexit(sbuf);
      }
   TheDevice = myReq->io_Device;

   /* Install device IO redirection */

   Forbid();
   RealBeginIO = SetFunction(TheDevice, DEV_BEGINIO, myBeginIO);
   RealClose   = SetFunction(TheDevice, DEV_CLOSE,   myClose);
   RealExpunge = SetFunction(TheDevice, DEV_EXPUNGE, myExpunge);
   Permit();

   /* Expunge disabled, CloseDevice so another can open it */
   CloseDevice(myReq);

   if(Notify)
      {
      sprintf(sbuf,"Cmd redirection of %s installed\n",deviceName);
      message(sbuf);
      }

   while(!Done)
      {
      signals = Wait(OPEN_SIG|WRITE_SIG|CLOSE_SIG|BREAK_SIG);

      if(signals & OPEN_SIG)   /* Open */
         {
         if(!outFile)   /* No output file currently open */
            {
            if(Multiple)  /* If Multiple, add .n extension to filename */
               {
               filecnt++;
               sprintf(&outFileName[fnLen],".%ld",filecnt);
               }
             /* open output file */
            outFile = Open(outFileName, MODE_NEWFILE);
            wLen = 1;
            total = 0;
            Error1 = TRUE;

            if(Notify)
               {
               sprintf(sbuf,"Redirecting %s to %s\n",
                         deviceName,outFileName);
               message(sbuf);
               }

            }
         }

      if(signals & WRITE_SIG)   /* Write */
         {
         if((outFile)&&(wLen > -1))
            {
            wLen = Write(outFile,ioR->io_Data,ioR->io_Length);
            if(wLen > -1) total += wLen;
            }
         else if(Error1)
            {
            message("Cmd file error: Cancel device output\n");
            Error1 = FALSE;
            }
         }

      if(signals & (CLOSE_SIG|BREAK_SIG))
         {
         /* Close file now so user can copy even if something is wrong */
         /* Null the handle - to prevent Write or re-Close */
         if(!Multiple)  signals |= BREAK_SIG;
         if(outFile)
            {
            Close(outFile);
            outFile = NULL;
            writecnt = 0;
            reqcnt = 0;
            if((!Multiple)||(Notify))
               {
               sprintf(sbuf,"Redirected %ld bytes from %s to %s\n",
                          total,deviceName,outFileName);
               message(sbuf);
               }
            }
         }

      if(signals & BREAK_SIG)
         {
         while(!Done)
            {
            /* Wait till we can reopen the device */
            while(OpenDevice(deviceName, 0L, myReq, 0L))  Delay(50L);

            /* If it's been re-loaded, we can leave            */
            /* Shouldn't be possible since we disabled Expunge */
            if((ULONG)myReq->io_Device != (ULONG)TheDevice)
               {
               Done = TRUE;
               }
            else
               {
               Forbid();

               NewBeginIO = SetFunction(TheDevice, DEV_BEGINIO, RealBeginIO);
               NewClose   = SetFunction(TheDevice, DEV_CLOSE,   RealClose);
               NewExpunge = SetFunction(TheDevice, DEV_EXPUNGE, RealExpunge);

               if((NewBeginIO != (ULONG)myBeginIO)
                   ||(NewClose != (ULONG)myClose)
                     ||(NewExpunge != (ULONG)myExpunge))
                  {
                  /* Someone else has changed the vectors */
                  /* We put theirs back - can't exit yet  */
                  SetFunction(TheDevice, DEV_BEGINIO, NewBeginIO);                  SetFunction(TheDevice, DEV_CLOSE  , NewClose);
                  SetFunction(TheDevice, DEV_CLOSE,   NewClose);                  SetFunction(TheDevice, DEV_CLOSE  , NewClose);
                  SetFunction(TheDevice, DEV_EXPUNGE, NewExpunge);
                  }
               else
                  {
                  Done = TRUE;
                  }
               Permit();
               }
            CloseDevice(myReq);
            if(!Done)  message("Vectors have changed - can't restore\n");
            }
         }
      MainBusy = FALSE;
      }

   sprintf(sbuf,"\nCmd redirection of %s removed\n", deviceName);
   cleanexit(sbuf);
   }


/* Cleanup and exits */

usageHelpExit()
   { 
   int k;
   for(k=0; k<7; k++) printf(us[k]);
   exit(RETURN_OK);
   } 

usageExit()
   { 
   printf(u1);
   printf(morehelp);
   exit(RETURN_OK);
   } 

cleanexit(s)
   char  *s;
   {
   message(s);
   cleanup();
   exit(RETURN_OK);
   }

cleanup()
   {
   if(myReq)   FreeMem(myReq,REQSIZE);
   if(port)    DeletePort(port);
   if(outFile) Close(outFile);

   Forbid();
   if(prevTaskName) mainTask->tc_Node.ln_Name = prevTaskName;
   Permit();
   }


message(s)
char *s;
   {
   LONG con;

   if((!FromWb)&&(*s)) printf(s);
   if((FromWb)&&(*s)&&(con = Open(conSpec,MODE_OLDFILE)))
      {
      Write(con,s,strlen(s));
      Delay(120L);
      Close(con);
      }
   }


getWbArgs(wbMsg)
struct WBStartup *wbMsg;
   {
   struct WBArg  *wbArg;
   struct DiskObject *diskobj;
   char **toolarray;
   char *s;

   /* Defaults */
   strcpy(wbDev,"parallel");
   strcpy(wbFile,"ram:CMD_file");
   Skip = FALSE;
   Multiple = FALSE;
   Notify = FALSE;

   wbArg = wbMsg->sm_ArgList;

   if((IconBase = OpenLibrary("icon.library", 0)))
      {
      diskobj=(struct DiskObject *)GetDiskObject(wbArg->wa_Name);
      if(diskobj)
         {
         toolarray = (char **)diskobj->do_ToolTypes;

         if(s=(char *)FindToolType(toolarray,"DEVICE"))  strcpy(wbDev,s);
         if(s=(char *)FindToolType(toolarray,"FILE"))    strcpy(wbFile,s);
         if(s=(char *)FindToolType(toolarray,"SKIP"))
            {
            if(strEqu(s,"TRUE"))  Skip = TRUE;
            }
         if(s=(char *)FindToolType(toolarray,"MULTIPLE"))
            {
            if(strEqu(s,"TRUE"))  Multiple = TRUE;
            }
         if(s=(char *)FindToolType(toolarray,"NOTIFY"))
            {
            if(strEqu(s,"TRUE"))  Notify = TRUE;
            }
         FreeDiskObject(diskobj);
         }
      CloseLibrary(IconBase);
      }
   }


/* String functions */

strEqu(p, q) 
TEXT *p, *q; 
   { 
   while(TOUPPER(*p) == TOUPPER(*q))
      {
      if (*(p++) == 0)  return(TRUE);
      ++q; 
      }
   return(FALSE);
   } 

strlen(s)
char *s;
   {
   int i = 0;
   while(*s++) i++;
   return(i);
   }

strcpy(to,from)
char *to, *from;
   {
   do
      {
      *to++ = *from;
      }
   while(*from++);
   }

/* end */


-- 
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Carolyn Scheppner -- CBM   >>Amiga Technical Support<<
                     UUCP  ...{allegra,caip,ihnp4,seismo}!cbmvax!carolyn 
                     PHONE 215-431-9180
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=