/* * HARCOPY.C copy CLI output to file as well as window. * * By Davide P. Cervone, Copywrite (c) 1987 * * Based heavily on a source by Phillip Lindsay, (c) 1987 Commodore-Amiga, Inc. * You may use this source as long as this copywrite notice is left intact. */ #include #include #include #include #include #include #ifdef MANX #include #endif /* * AmigaDOS uses task signal bit 8 for message signaling */ #define ONE 1L #define DOS_SIGNAL 8 #define DOS_MASK (ONE<dp_Arg1 #define ARG2 pkt->dp_Arg2 #define RES1 pkt->dp_Res1 /* * Program functions */ #define SHOW_USAGE 0 #define START_HARDCOPY 1 #define DO_MONITOR 2 #define END_HARDCOPY 3 /* * External routines and variables */ extern LONG AllocSignal(), Wait(); extern struct FileHandle *Open(); extern struct Message *PacketWait(), *GetMsg(); extern struct Process *FindTask(); extern struct DosLibrary *DOSBase; extern void GetDateTime(); /* * Our own variables */ static char *version = "HARDCOPY v1.0, 4/25/87"; static char *copywrite = "Copywrite (c) 1987 by Davide P. Cervone"; struct MsgPort *thePort; /* the port we will be monitoring */ struct Process *myProcess; /* pointer to our own process */ struct Process *ChosenProcess; /* the process we are monitoring */ ULONG WaitMask; /* the task signal mask */ struct SignalSemaphore CanReturn = {0}; /* coordinates PacketWait */ struct Message *theMessage; /* the message we received */ APTR OldPktWait; /* the old pr_PktWait routine */ APTR OldUserData; /* tc_UserData for monitored task */ #ifdef MANX LONG PWait(); /* this is the ASM stub that calls PacketWait() */ #else #define PWait PacketWait #endif #ifndef MANX Ctrl_C() /* Control-C Trap routine for Lattice */ { return(0); } #endif /* * PacketWait() * * This is the routine placed in the pr_PktWait field of the monitored * precess. It is run asynchronously by the monitored process, and is * called whenever AmigaDOS does a taskwait(). PacketWait() waits for * a message to come in and then signals the monitoring task that one has * arrived. It then attempts to obtain the semaphore, which will not be * released by the monitoring process until it is finished printing the * contents of the packet. */ struct Message *PacketWait() { #ifdef MANX /* * if MANX, make sure we can see our data */ geta4(); #endif SetSignal(FALSE,DOS_MASK); while(!(theMessage = GetMsg(thePort))) Wait(DOS_MASK); Signal(myProcess,WaitMask); ObtainSemaphore(&CanReturn); ReleaseSemaphore(&CanReturn); return(theMessage); } /* * printBUF() * * Prints a buffer to stdout. */ void printBUF(buf,len) char *buf; int len; { short i; char outbuf[81]; while (len > 0) { for (i=0; i<80 && len>0; i++,len--) outbuf[i] = *buf++; outbuf[i] = '\0'; printf("%s",outbuf); } } /* * PrintPkt() * * For READ and WRITE packets to/from the CON: window, print the buffer * to stdout. We recognize CON: packets because ARG1 is zero (this is * a real kludge, but it seems to work, except we get an extra ENDCLI * if you run HARDCOPY, then EMACS, then spawn a new CLI, then type ENDCLI * to get back to EMACS. Can't figure that one out). */ void PrintPkt(pkt) struct DosPacket *pkt; { switch(pkt->dp_Type) { case _ACTION_READ: if (ARG1 == 0) printBUF(ARG2,RES1); break; case _ACTION_WRITE: if (ARG1 == 0) printBUF(ARG2,RES1); break; default: /* Ignore anything else */ break; } } /* * GetFunction() * * Check the command-line arguments to see that they are valid. * The legal possibilities are: * * TO To begin HARDCOPY to a file * END To end the HARDCOPY session * MONITOR To begin menitoring the process pointed to by * . */ GetFunction(argc,argv) int argc; char *argv[]; { int function = SHOW_USAGE; if (argc == 3 && stricmp(argv[1],"TO") == 0) function = START_HARDCOPY; else if (argc == 3 && strcmp(argv[1],"MONITOR") == 0 && sscanf(argv[2],"%x",&ChosenProcess) == 1) function = DO_MONITOR; else if (argc == 2 && stricmp(argv[1],"END") == 0) function = END_HARDCOPY; return(function); } /* * SetupSignal() * * Allocate a signal to use for our inter-task communication, and * set up the mask for using it. */ void SetupSignal(theSignal) LONG *theSignal; { *theSignal = AllocSignal(-ONE); if (*theSignal == -ONE) { printf("Can't Allocate a Task Signal.\n"); exit(10); } WaitMask = (ONE << (*theSignal)); } /* * SetupProcess() * * Copy the process' name, and get its Message port. Set our priority * higher than the monitored process so we will be able to react to its * signals, then set the pr_PktWait field to our PacketWiat() routine so * that we will be signalled when it receives a packet (save the old * pr_PktWait so we can put it back when we're through). Set the tc_UserData * field of the monitored processes Task structure to point to us, so that * the monitored process can signal us when it wants us to stop hardcopying. * Finally, send a signal to the monitored process to show that we are ready * to monitor it. */ void SetupProcess(theProcess,name) struct Process *theProcess; char *name; { strcpy(name,theProcess->pr_Task.tc_Node.ln_Name); thePort = &theProcess->pr_MsgPort; Forbid(); SetTaskPri(myProcess,(ULONG)(theProcess->pr_Task.tc_Node.ln_Pri + 1)); OldPktWait = theProcess->pr_PktWait; theProcess->pr_PktWait = (APTR) PWait; OldUserData = PTR(Task,theProcess)->tc_UserData; PTR(Task,theProcess)->tc_UserData = (APTR) myProcess; Permit(); Signal(theProcess,SIGBREAKF_CTRL_C); } /* * MonitorProcess() * * Wait for the monitored process to receive a message (our PacketWait() * function signals us via theSignal when it has received a message), then * print out the contents of the message. A semaphore is used to coordinate * this routine with the PacketWait() routine (which is run asynchonously * by the monitored process). Phillip Lindsay says "there are probably a * hundred better was of doing this. I just went with the first one [that] * came to mind." I couldn't think of a better one, so I still use it. * Since our process is running at a higher priority than the monitored one, * we should obtain the semaphore first. The other process will block until * we release it (when we are done printing the contents). */ void MonitorProcess(name,theSignal) char *name; ULONG theSignal; { ULONG signals; struct DosPacket *thePacket; do { signals = Wait(SIGBREAKF_CTRL_C | WaitMask); ObtainSemaphore(&CanReturn); if (signals & WaitMask) { /* * PacketWait() signalled us so print the message it put in * theMessage. */ thePacket = PTR(DosPacket,theMessage->mn_Node.ln_Name); PrintPkt(thePacket); } ReleaseSemaphore(&CanReturn); } while(!(signals & SIGBREAKF_CTRL_C)); } /* * ClenUpProcess() * * Put everything back the way we found it, except that the monitored process * is still running our code... */ void CleanUpProcess(theProcess) struct Process *theProcess; { Forbid(); theProcess->pr_PktWait = OldPktWait; PTR(Task,theProcess)->tc_UserData = OldUserData; Permit(); SetTaskPri(myProcess,0L); } /* * DoMonitor() * * Get a signal for our PacketWait code to use to signal us when a packet is * ready for us to look at. Get a semaphore so that we can make the * PacketWait() routine wait for us to finish with the packet before it * returns the message to the monitored process. Set up the process so * that it includes our PacketWait() code. * * Monitor the packet traffic, and print the I/O to the monitored CLI. * We wait for a signal from the PacketWait() routine, or for a * CTRL-C. * * When we get a CTRL-C, we are done monitoring, so we remove our * PacketWait() code, but the monitored process may still be in our * waiting code, so we wait for a CTRL-E to verify that we are free to * die (and remove the PacketWait code from memory). */ void DoMonitor() { LONG TaskSignal; UBYTE ProcessName[81]; myProcess = FindTask(NULL); if (ChosenProcess != NULL) { #ifndef MANX onbreak(&Ctrl_C); /* Turn off CTRL-C for Lattice: we do our own */ #endif SetupSignal(&TaskSignal); InitSemaphore(&CanReturn); SetupProcess(ChosenProcess,ProcessName); MonitorProcess(ProcessName,TaskSignal); CleanUpProcess(ChosenProcess); Wait(SIGBREAKF_CTRL_E); FreeSignal(TaskSignal); } } /* * StartHardCopy() * * Creates the process that monitors the current process via a call to * Execute(). We execute a RUN command that runs HARDCOPY MONITOR and * passes a pointer to the current process as a parameter. When HARDCOPY * starts as the remote process, it looks up this parameter and monitors * that process. The output for HARDCOPY is re-directed to the file * that the user specified in the initial call to HARDCOPY. * * The spawned process will signal us with a CTRL-C when it is set up, * so wait for that signal. Finally, print out a message; this will * appear as the first thing in the output file. */ void StartHardCopy(file) char *file; { char cmd[200],time[19]; #define COMMAND "RUN NIL: HARDCOPY \"%s\" MONITOR 0x%X" sprintf(cmd,COMMAND,file,FindTask(NULL)); if (!Execute(cmd,NULL,NULL)) { printf("Can't create HARDCOPY process\n"); } else { #ifndef MANX onbreak(&Ctrl_C); /* Turn off CTRL-C for Lattice: we do our own */ #endif printf("Waiting for HARDCOPY monitor to start ...\n"); printf("[Press CTRL-E if any errors are reported]\n"); if (Wait(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E) == SIGBREAKF_CTRL_E) { printf("\nUser signalled abort!\n\n"); printf("To remove an unwanted HARDCOPY monitor process, first try\n"); printf("giving the command HARDCOPY END. If that doesn't work,\n"); printf("use STATUS to identify the process, and then use the BREAK\n"); printf("command to send a CTRL-C and then a CTRL-E to the HARDCOPY\n"); printf("monitor process.\n\n"); } else { GetDateTime(time); printf("\nHARDCOPY v1.0 recorded on %s to file \"%s\"\n", time,file); printf("To end the HARDCOPY session and close the file, "); printf("type HARDCOPY END\n\n"); } } } /* * EndHardCopy() * * Sends a CTRL-C and a CTRL-E to the monitoring HARDCOPY process, which * tell it to de-install the PacketWait() routine, and then die. The * monitoring process has stored its address in the UserData field of * our process' Task structure, so we know were to send the signals. */ void EndHardCopy() { struct Process *MonitoringProcess; myProcess = FindTask(NULL); MonitoringProcess = PTR(Process,PTR(Task,myProcess)->tc_UserData); if (MonitoringProcess == NULL) { printf("No HARDCOPY process in progress\n"); } else { Signal(MonitoringProcess,SIGBREAKF_CTRL_C); printf("HARDCOPY output complete.\n"); Signal(MonitoringProcess,SIGBREAKF_CTRL_E); } } void main(argc,argv) int argc; char *argv[]; { ChosenProcess = NULL; switch(GetFunction(argc,argv)) { case SHOW_USAGE: printf("Usage: HARDCOPY TO \n"); printf(" or: HARDCOPY END\n"); break; case START_HARDCOPY: StartHardCopy(argv[2]); break; case DO_MONITOR: DoMonitor(); break; case END_HARDCOPY: EndHardCopy(); break; } } /* * GetStrTime(time,date) * * translates the DateStamp stored in "date" into a character string and * copies it into the character string pointed to by "time", which should be * at least 19 characters long. GetStrTime properly accounts for leap years * every four years. Every four centuries, however, a leap day is supposed * to be skipped. AmigaDOS does not correctly interpret these non-leap * centuries, hence neither does GetStrTime. In the event that AmigaDOS is * ever corrected to fix this bug, you can remove the comment delimiters from * the line in GetStrTime that implements non-leap centuries. Unfortunately, * the year 2000 is a non-leap century, hence this bug may actually * come into play (if anyone still has Amigas in 14 years). */ static int DaysIn[] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; static char *NameOf[] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; #define CENTURY (100 YEARS + 25 LEAPDAYS) #define FOURCENTURIES (400 YEARS + 99 LEAPDAYS) #define FOURYEARS (4 YEARS + 1 LEAPDAY) #define LEAPYEAR (1 YEARS + 1 LEAPDAY) #define LEAPDAYS #define LEAPDAY #define YEARS * 365 #define YEAR 365 #define FEBDAYS 59 /* days until the end of February */ void GetStrTime(time,date) char time[19]; /* storage area for the return value */ struct DateStamp *date; /* the DateStamp to convert */ { int year,month,day,hour,minute,second; day = date->ds_Days + 78 YEARS + 20 LEAPDAYS; /* day += (day - FEBDAYS - CENTURY + FOURCENTURIES) / FOURCENTURIES; */ year = 4 * (day/FOURYEARS) + 1900; day %= FOURYEARS; day += (day - FEBDAYS - 1 LEAPDAY) / YEAR; year += day / LEAPYEAR; day %= LEAPYEAR; for (month=1; day >= DaysIn[month]; month++); day = day - DaysIn[month-1] + 1; hour = date->ds_Minute / 60; minute = date->ds_Minute - hour*60; second = date->ds_Tick / TICKS_PER_SECOND; sprintf(time,"%02d-%3s-%02d %02d:%02d:%02d", day,NameOf[month],(year % 100),hour,minute,second); } /* * GetDateTime(str) * * Uses GetStrTime() to get the current date and time as a string. * "str" must be at least 19 characters long. See GetStrTime for * more information. */ void GetDateTime(str) char *str; { struct DateStamp date; DateStamp(&date); GetStrTime(str,&date); } /* * This code stub has been known to save lives... */ #if MANX #asm XREF _PacketWait XDEF _PWait _PWait: movem.l a2/a3/a4,-(sp) jsr _PacketWait movem.l (sp)+,a2/a3/a4 rts #endasm #endif