/* 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 */