home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 19 / AACD19.BIN / AACD / System / SnoopDos / SnoopDos_Src / SnoopDos_Source / buffer.c next >
C/C++ Source or Header  |  2001-02-04  |  45KB  |  1,072 lines

  1. /*
  2.  *              BUFFER.C                                                                                        vi:ts=4
  3.  *
  4.  *      Copyright (c) Eddy Carroll, September 1994.
  5.  *      Updated 5.3.2000 Thomas Richter, THOR
  6.  *
  7.  *              This module maintains the review buffer used by SnoopDos to record
  8.  *              the past events that have occurred.
  9.  */             
  10.  
  11. #define TESTING         0
  12.  
  13. #include "system.h"
  14. #include "snoopdos.h"
  15. #include "patches.h"
  16.  
  17. #define INVALID_HUNK            ((USHORT)(-1))
  18.  
  19. #if 0
  20. #define DB(s)   KPrintF(s)
  21. #else
  22. #define DB(s)
  23. #endif
  24.  
  25. static UBYTE       *BufferStart;                /* Ptr to start of our buffer           */
  26. static UBYTE       *BufferEnd;                  /* Ptr to end of our buffer                     */
  27. static UBYTE       *BufferCur;                  /* Ptr to next free loc in buffer       */
  28. static ULONG            BufferSize;                     /* Size of current buffer                       */
  29.  
  30. /*
  31.  *              This is the mapping that assigns format ID's to the various
  32.  *              fields we can output.
  33.  */
  34. FieldInit FieldTypes[] = {
  35.         EF_ACTION,              'a',    10,             MSG_ACTION_COL,
  36.         EF_CALLADDR,    'c',    8,              MSG_CALL_COL,
  37.         EF_DATE,                'd',    9,              MSG_DATE_COL,
  38.         EF_HUNKOFFSET,  'h',    11,             MSG_HUNK_COL,
  39.         EF_PROCID,              'i',    8,              MSG_PROCESSID_COL,
  40.         EF_FILENAME,    'n',    27,             MSG_FILENAME_COL,
  41.         EF_OPTIONS,             'o',    7,              MSG_OPTIONS_COL,
  42.         EF_PROCNAME,    'p',    18,             MSG_PROCNAME_COL,
  43.         EF_RESULT,              'r',    4,              MSG_RESULT_COL,
  44.         EF_SEGNAME,             's',    20,             MSG_SEGNAME_COL,
  45.         EF_TIME,                't',    8,              MSG_TIME_COL,
  46.         EF_COUNT,               'u',    5,              MSG_COUNT_COL,
  47.         EF_END,                 0,              0,              0
  48. };
  49.  
  50. /*
  51.  *              InitBuffers()
  52.  *
  53.  *              Initialises certain variables associated with the buffer routines.
  54.  *              Must be called before any of the buffer functions are called.
  55.  */
  56. void InitBuffers(void)
  57. {
  58.         InitSemaphore(&BufSem);
  59.         NewList(&EventList);
  60. }
  61.  
  62. /*
  63.  *              SetTotalBufferSize(size, alloctype)
  64.  *
  65.  *              This function is used to change the size of the buffer. If no buffer
  66.  *              has yet been created, one is allocated. If there is not enough room
  67.  *              to create a new buffer without freeing the existing buffer first,
  68.  *              then FALSE is returned, _unless_ the alloctype parameter is
  69.  *              SETBUF_FORCENEW, in which case it simply frees the existing buffer
  70.  *              first, losing any existing history info.
  71.  *
  72.  *              In general, the old buffer's contents are copied across to the
  73.  *              new buffer so that the user's history isn't destroyed.
  74.  *
  75.  *              The intention is that the caller tries to use this call with
  76.  *              the tryalways flag set to SETBUF_KEEPOLD first; if that fails,
  77.  *              then it can display a requester to the user saying that this will
  78.  *              cause the existing buffer info to be lost permanently if you go
  79.  *              ahead--Yes/No?  and then do a retry with alloctype set to
  80.  *              SETBUF_FORCENEW if the user so chooses.
  81.  *
  82.  *              In any case, when allocating a new buffer, the routine will always
  83.  *              allocate a buffer if possible, even if it can't allocate the full
  84.  *              size requested.
  85.  *
  86.  *              Returns FALSE if there wasn't enough room to allocate the new buffer
  87.  *              (the minimum buffer size is something like 4K.)
  88.  *
  89.  *              Note that if the size of the buffer is being reduced, the caller
  90.  *              is responsible for updating the display to reflect the new status.
  91.  *
  92.  *              Note also that the whole idea of only freeing the buffer if there's
  93.  *              no other option is great in principle; in practise, it turned out to
  94.  *              be too much work to support copying buffers for such an infrequent
  95.  *              operation, so we don't do it after all.
  96.  */
  97. int SetTotalBufferSize(ULONG newsize, int alloctype)
  98. {
  99.         UBYTE *oldbuf = BufferStart;
  100.         ULONG oldsize = BufferSize;
  101.         UBYTE *newbuf;
  102.  
  103.         ObtainSemaphore(&BufSem);
  104.         ClearBuffer();          /* Clear the existing buffer first */
  105.  
  106.         /*
  107.          *              First, work out if we have enough memory to satisfy the request
  108.          *              without freeing our old buffer first. If we don't, and if
  109.          *              alloctype is SETBUF_KEEPOLD, then we fail immediately.
  110.          */
  111.         if (newsize < MIN_BUF_SIZE)
  112.                 newsize = MIN_BUF_SIZE;
  113.  
  114.         newbuf = AllocMem(newsize, MEMF_ANY);
  115.         if (!newbuf) {
  116.                 /*
  117.                  *              No memory available -- we _may_ need to bomb out at this
  118.                  *              point. If we didn't have a buffer already, then we
  119.                  *              always go ahead and try and allocate some, regardless
  120.                  *              of alloctype.
  121.                  */
  122.                 if (oldbuf && alloctype == SETBUF_KEEPOLD) {
  123.                         ReleaseSemaphore(&BufSem);
  124.                         return (FALSE);
  125.                 }
  126.  
  127.                 /*
  128.                  *              Okay, try and allocate the memory even though it means
  129.                  *              freeing up the old buffer first.
  130.                  */
  131.                 if (oldbuf) {
  132.                         FreeMem(oldbuf, oldsize);
  133.                         oldbuf = NULL;
  134.                 }
  135.                 newbuf = AllocMem(newsize, MEMF_ANY);
  136.                 if (!newbuf) {
  137.                         /*
  138.                          *              Couldn't allocate memory on second attempt. Finally,
  139.                          *              we just grab the largest chunk we can and use that
  140.                          *              instead.
  141.                          */
  142.                         ULONG freesize;
  143.  
  144.                         Forbid();
  145.                         freesize = AvailMem(MEMF_ANY | MEMF_LARGEST);
  146.                         if (freesize < MIN_BUF_SIZE) {
  147.                                 Permit();
  148.                                 ReleaseSemaphore(&BufSem);
  149.                                 return (FALSE);
  150.                         }
  151.                         if (freesize < newsize) /* freesize > newsize _is_ possible! */
  152.                                 newsize = freesize;
  153.                         newbuf  = AllocMem(newsize, MEMF_ANY);
  154.                         Permit();
  155.                         if (!newbuf) {
  156.                                 /*
  157.                                  *              Should never happen, but better safe than sorry
  158.                                  */
  159.                                 ReleaseSemaphore(&BufSem);
  160.                                 return (FALSE);
  161.                         }
  162.                 }
  163.         }
  164.         /*
  165.          *              Okay, we finally have some memory so initialise the global
  166.          *              buffer variables for use by everyone else.
  167.          */
  168.         BufferStart = newbuf;
  169.         BufferSize  = newsize;
  170.         BufferEnd   = newbuf + newsize;
  171.         BufferCur   = BufferStart;
  172.  
  173.         if (oldbuf != NULL) {
  174.                 // CopyEvents(newbuf, newsize, oldbuf, oldsize);
  175.                 FreeMem(oldbuf, oldsize);
  176.         }
  177.         NewList(&EventList);            /* Probably need to delete this eventually */
  178.         ReleaseSemaphore(&BufSem);
  179.         return (TRUE);
  180. }
  181.  
  182. // void CopyEvents(UBYTE *newbuf, ULONG newsize,
  183. //                              UBYTE *oldbuf, ULONG oldsize) {}
  184.  
  185. /*
  186.  *              ClearBuffer()
  187.  *
  188.  *              Clears the current buffer by a very simple process -- it simply
  189.  *              erases the current list of linked items, and resets RealFirstSeq etc.
  190.  *              to be the next available event number. This means that any events
  191.  *              that were partially in progress will spot, on their completition,
  192.  *              that their associated event number is now < RealFirstSeq, and so
  193.  *              won't try and write into the now-cleared buffer, thus avoiding
  194.  *              corruption.
  195.  *
  196.  *              BaseSeq is used to allow the new buffer count to commence from
  197.  *              1 again, rather than keeping the old number (which might be
  198.  *              into the hundreds by this stage!)
  199.  *
  200.  *              Note: we are a little bit naughty in some of the patches in that
  201.  *              after allocating a brand new event, we sometimes write into it
  202.  *              immediately _without_ semaphore protection (to fill in the
  203.  *              initial details etc.) This is not terribly wise, but since there
  204.  *              is never anything to deliberately cause a task switch, it's
  205.  *              reasonably safe. Adding semaphore protection to all these cases
  206.  *              would complicate things quite a bit.
  207.  */
  208. void ClearBuffer(void)
  209. {
  210.         ObtainSemaphore(&BufSem);
  211.         Delay(5);       /* Just to be safe, let everyone else run for 0.1 seconds */
  212.  
  213.         NewList(&EventList);
  214.         BufferCur = BufferStart;
  215.  
  216.         BaseSeq                 =
  217.         FirstSeq                =
  218.         RealFirstSeq    =
  219.         TopSeq                  =
  220.         LastDrawnTopSeq =
  221.         BottomSeq               =
  222.         MaxScannedSeq   =
  223.         EndSeq                  =
  224.         EndCompleteSeq  = NextSeq;
  225.  
  226.         ReleaseSemaphore(&BufSem);
  227. }
  228.  
  229. /*
  230.  *              CleanupBuffers()
  231.  *
  232.  *              Frees up the buffer on exit, along with any other pertinent
  233.  *              resources that were allocated by this module.
  234.  */
  235. void CleanupBuffers(void)
  236. {
  237.         if (BufferStart) {
  238.                 ObtainSemaphore(&BufSem);
  239.                 FreeMem(BufferStart, BufferSize);
  240.                 if (BufferSize == 0)
  241.                         ShowError("Warning! BufferSize is zero for some reason!");
  242.                 BufferStart = NULL;
  243.                 BufferSize  = 0;
  244.                 NewList(&EventList);
  245.                 ReleaseSemaphore(&BufSem);
  246.         }
  247. }
  248.  
  249. /*
  250.  *              GetNewEvent(stringsize)
  251.  *
  252.  *              Allocates a new event structure from the list from the buffer
  253.  *              and returns a pointer to it. If necessary, one of the existing
  254.  *              buffers will be flushed to make room for the new event.
  255.  *
  256.  *              The stringsize is the maximum amount of extra space to allocate
  257.  *              after the end of the event to accomodate string storage. A pointer
  258.  *              to this data area can be obtained by accessing the first location
  259.  *              after the event itself. For example, GetNewEvent(20) would return
  260.  *              an event with 20 additional data bytes after it. Note that this
  261.  *              _includes_ any terminating zeros that may be present in the
  262.  *              strings.
  263.  */
  264. Event *GetNewEvent(int stringsize)
  265. {
  266.         UBYTE *startev;
  267.         ULONG totalsize;
  268.         Event *ev;
  269.  
  270.         /* Calculate total size and ensure it's a longword multiple */
  271.         totalsize = (sizeof(Event) + stringsize + 3) & ~3;
  272.  
  273.         if (BufferStart == NULL || totalsize > (MIN_BUF_SIZE/2))
  274.                 return (NULL);  /* Should never happen but doesn't hurt to check */
  275.  
  276.         DB(" [GNE-Go] ");
  277.         if (!AttemptSemaphoreHeavely(&BufSem))
  278.                 return NULL;
  279.  
  280.         /*
  281.         ObtainSemaphore(&BufSem);
  282.          */
  283. #if 0
  284.         ev = (Event *)BufferStart;
  285.         ReleaseSemaphore(&BufSem);
  286.         return (ev);
  287. #endif
  288.  
  289.         startev = HeadNode(&EventList);
  290.         if (IsListEmpty(&EventList)) {
  291.                 /*
  292.                  *              First time being called, so simply begin at the
  293.                  *              start of the buffer.
  294.                  */
  295.                 BufferCur = BufferStart;
  296.         } else {
  297.                 /*
  298.                  *              There are already some nodes. Try and allocate a new node,
  299.                  *              flushing nodes from the start of the list as necessary.
  300.                  */
  301.                 for (;;) {
  302.                         if (startev < BufferCur) {
  303.                                 /*
  304.                                  *              From BufferCur to the end of the buffer is free space,
  305.                                  *              so we can check for a quick fit. If we get one, then
  306.                                  *              we go for it, otherwise we move BufferCur back to the
  307.                                  *              start of the buffer and start flushing events from
  308.                                  *              there.
  309.                                  */
  310.                                 if (totalsize <= (BufferEnd - BufferCur))
  311.                                         break;  /* Got it */
  312.                                 BufferCur = BufferStart;        /* Else wrap to start of list */
  313.                         }
  314.                         /*
  315.                          *              Now delete events from here to the end of the buffer until
  316.                          *              we finally have enough room in the buffer. If we run off
  317.                          *              the end of the buffer, let the main loop go around again
  318.                          *              to reset everything.
  319.                          */
  320.                         while (startev >= BufferCur && (startev - BufferCur) < totalsize) {
  321.                                 RemHead(&EventList);
  322.                                 if (IsListEmpty(&EventList)) {
  323.                                         /* This really shouldn't be printed unless
  324.                                          * debugging is turned on. Fixed,
  325.                                          * THOR
  326.                                          */
  327.                                         DB("Uhoh --- list is empty and shouldn't be!\n");
  328.                                         Delay(50);
  329.                                         startev = BufferCur = BufferStart;
  330.                                         break;
  331.                                 }
  332.                                 startev = HeadNode(&EventList);
  333.                         }
  334.                         if (startev > BufferCur)
  335.                                 break;  /* Got it */
  336.                 }
  337.         }
  338.         ev                 = (Event *)BufferCur;
  339.         BufferCur += totalsize;
  340.  
  341.         /*
  342.          *              Allocate our new event from the current buffer block (which
  343.          *              we are now assured will have enough space in it)
  344.          */
  345.         ev->seqnum      = ++NextSeq;                    /* Give it a unique sequence number */
  346.         ev->status      = ES_CREATING;                  /* Mark event as not-to-be-touched      */
  347.         ev->flags   = EFLG_NONE;                        /* No date/segment lookup as yet        */
  348.         AddTail(&EventList, (Node *)ev);
  349.         /*
  350.          *              Update current first seq num so patch code knows when an
  351.          *              event currently being created has become invalid
  352.          */
  353.         RealFirstSeq = ((Event *)HeadNode(&EventList))->seqnum;
  354.         DateStamp(&ev->datestamp);                      /* Store current date                           */
  355.         DB(" [GNEEnd] ");
  356.         ReleaseSemaphore(&BufSem);
  357.         return (ev);
  358. }
  359.  
  360. /*
  361.  *              ParseFormatString(formatstr, evformat, maxfields)
  362.  *
  363.  *              Parses a text format string containing a list of format identifiers
  364.  *              (%d, %s, etc.) with optional field widths (%20s, %10d) into an
  365.  *              internal list of formatting codes that can be quickly processed
  366.  *              when displaying output.
  367.  *
  368.  *              formatstr points to the format string. evformat points to the
  369.  *              EventFormat[] array to hold the output.
  370.  *
  371.  *              maxfields is the maximum number of format fields that will
  372.  *              be recognised -- any more than this will be ignored. evformat[]
  373.  *              must be able to hold at least maxfields elements. If a field is
  374.  *              duplicated, only the first occurrance is recognised.
  375.  *
  376.  *              Returns the length any output produced using this format array will
  377.  *              have (including separating spaces)
  378.  */
  379. int ParseFormatString(char *formatstr, EventFormat *evformat, int maxfields)
  380. {
  381.         int evindex = 0;
  382.         int totlen  = 0;
  383.         char *p     = formatstr;
  384.  
  385.         while (*p) {
  386.                 int width = 0;
  387.                 int i;
  388.                 char type;
  389.                 char ch;
  390.  
  391.                 if (*p++ != '%')
  392.                         continue;
  393.  
  394.                 /*
  395.                  *              Got a format specifier, so now parse it.
  396.                  *
  397.                  *              We ignore any negative prefix -- in the real printf,
  398.                  *              it means left-align this field, and all our fields are
  399.                  *              left-aligned by default anyway.
  400.                  */
  401.                 if (*p == '-')
  402.                         p++;
  403.                 while (*p >= '0' && *p <= '9') {
  404.                         width = (width * 10) + *p - '0';
  405.                         p++;
  406.                 }
  407.                 if (!*p)
  408.                         break;
  409.  
  410.                 ch = *p++;
  411.                 if (ch >= 'A' && ch <= 'Z')
  412.                         ch |= 0x20;
  413.  
  414.                 for (i = 0; FieldTypes[i].type != EF_END; i++) {
  415.                         if (FieldTypes[i].idchar == ch)
  416.                                 break;
  417.                 }
  418.                 type = FieldTypes[i].type;
  419.                 if (type != EF_END) {
  420.                         /*
  421.                          *              Matched a field. Now try and insert it. But first,
  422.                          *              make sure we don't have a duplicate.
  423.                          */
  424.                         int j;
  425.  
  426.                         for (j = 0; j < evindex; j++)
  427.                                 if (evformat[j].type == type)   /* Got a duplicate */
  428.                                         break;
  429.  
  430.                         if (j < evindex)                /* Skip over duplicates */
  431.                                 continue;       
  432.  
  433.                         if (width == 0)
  434.                                 width = FieldTypes[i].defwidth;
  435.  
  436.                         if (width < 1)                          width = 1;
  437.                         if (width > MAX_FIELD_LEN)      width = MAX_FIELD_LEN;
  438.  
  439.                         evformat[evindex].type           = type;
  440.                         evformat[evindex].width          = width;
  441.                         evformat[evindex].titlemsgid = FieldTypes[i].titlemsgid;
  442.                         totlen += width + 1;
  443.                         evindex++;
  444.                         if (evindex >= (maxfields-1))
  445.                                 break;
  446.                 }
  447.         }
  448.         evformat[evindex].type = EF_END;
  449.         if (totlen)                             /* Compensate for the extra 'space' we counted */
  450.                 totlen--;
  451.  
  452.         return (totlen);
  453. }
  454.  
  455. /*
  456.  *              BuildFormatString(evformat, formatstr, maxlen)
  457.  *
  458.  *              Converts an existing event format array into an ascii string
  459.  *              corresponding to the format that can be parsed by ParseFormatString.
  460.  *              The string is guaranteed to be at most maxlen chars in length
  461.  *              (including terminating \0);
  462.  */
  463. void BuildFormatString(EventFormat *evformat, char *formatstr, int maxlen)
  464. {
  465.         char *p   = formatstr;
  466.         char *end = formatstr + maxlen - 6;     /* Max length of format element is 5 */
  467.  
  468.         while (evformat->type != EF_END) {
  469.                 int j;
  470.  
  471.                 if (p >= end)   /* Never exceed string length */
  472.                         break;
  473.                 /*
  474.                  *              Find the event type in our mapping array so that we can
  475.                  *              determine the default width. This lets us choose not
  476.                  *              to include the width in the ascii field, for neatness.
  477.                  */
  478.                 for (j = 0; FieldTypes[j].type != evformat->type; j++)
  479.                         ;
  480.                 *p++ = '%';
  481.                 if (FieldTypes[j].defwidth != evformat->width) {
  482.                         mysprintf(p, "%ld", evformat->width);
  483.                         p += strlen(p);
  484.                 }
  485.                 *p++ = FieldTypes[j].idchar;
  486.                 *p++ = ' ';     /* Space out entries for neatness */
  487.                 evformat++;
  488.         }
  489.         p[-1] = '\0';   /* Replace final space with a terminator instead. */
  490. }
  491.  
  492. /*
  493.  *              ltoa(buffer, val, numdigits)
  494.  *              ltoap(buffer, val, numdigits)
  495.  *
  496.  *              Converts the given value to an ascii hex string of up to numdigits
  497.  *              (with leading zeros). No zero termination is performed. The output
  498.  *              is stored in the first numdigits characters of buffer.
  499.  *
  500.  *              ltoap is similar, except that it pads leading zeros with spaces.
  501.  *
  502.  *              Returns a pointer to the buffer.
  503.  */
  504. char *ltoa(char *buffer, unsigned long val, int numdigits)
  505. {
  506.         static char HexDigits[] = "0123456789ABCDEF";
  507.         char *p = buffer + numdigits - 1;
  508.  
  509.         while (p >= buffer) {
  510.                 *p-- = HexDigits[val & 0x0F];
  511.                 val >>= 4;
  512.         }
  513.         return (buffer);
  514. }
  515.  
  516. char *ltoap(char *buffer, unsigned long val, int numdigits)
  517. {
  518.         static char HexDigits[] = "0123456789ABCDEF";
  519.         char *p = buffer + numdigits - 1;
  520.  
  521.         do {
  522.                 *p-- = HexDigits[val & 0x0F];
  523.                 val >>= 4;
  524.         } while (val && p >= buffer);
  525.  
  526.         while (p >= buffer)
  527.                 *p-- = ' ';
  528.  
  529.         return (buffer);
  530. }
  531.  
  532. /*
  533.  *              SetEventDateStr(event)
  534.  *
  535.  *              This function converts the datestamp in the passed event into a date
  536.  *              and time string, also stored in the event.
  537.  */
  538. void SetEventDateStr(Event *ev)
  539. {
  540.         struct DateTime datetime;
  541.  
  542.         datetime.dat_Stamp              = ev->datestamp;
  543.         datetime.dat_Format             = FORMAT_DOS;
  544.         datetime.dat_Flags              = 0;
  545.         datetime.dat_StrDay             = NULL;
  546.         datetime.dat_StrDate    = ev->date;
  547.         datetime.dat_StrTime    = ev->time;
  548.         DateToStr(&datetime);
  549. }
  550.  
  551. /*
  552.  *              CheckSegTracker()
  553.  *
  554.  *              Checks to see if SegTracker is loaded and sets the SegTracker
  555.  *              global variable accordingly. Should be called every now and
  556.  *              again to keep us up to date (e.g. whenever a new window is
  557.  *              opened).
  558.  */
  559. void CheckSegTracker(void)
  560. {
  561.         SegTrackerActive = (FindSemaphore("SegTracker") != NULL);
  562. }
  563.  
  564. /*
  565.  *              SetEventSegmentStr(event)
  566.  *
  567.  *              This function converts the calladdr in the passed event into a segment
  568.  *              name and hunk/offset pair if SegTracker is loaded, or into an invalid
  569.  *              string if SegTracker is not loaded.
  570.  *
  571.  *              See the Enforcer/Segtracker documentation (from Michael Sinz) for
  572.  *              more info on how this works.
  573.  *
  574.  *              Note that if the passed-in event->segname is NULL, then it will
  575.  *              be reset to an "unknown module" string and no attempt to locate
  576.  *              the module will be made. It is thus imperative that the _caller_
  577.  *              only calls this function once for each event. The easiest way
  578.  *              to ensure this is to use the EFLG_DONESEG flag in the ev->flags
  579.  *              register to track whether the call has been made or not.
  580.  *
  581.  *              New: we now detect ROM addresses and search the resident module
  582.  *                       list ourselves looking for the module that contains it.
  583.  */
  584. void SetEventSegmentStr(Event *ev)
  585. {
  586.         typedef char (*__asm SegTrack(reg_a0 ULONG,reg_a1 ULONG *,reg_a2 ULONG *));
  587.  
  588.         static struct {
  589.                 Semaphore       seg_Semaphore;
  590.                 SegTrack        *seg_Find;
  591.         } *segsem;
  592.  
  593.         ULONG hunk;
  594.         ULONG offset;
  595.         char *segname = NULL;
  596.  
  597.         ev->hunk = INVALID_HUNK;
  598.         if (ev->segname == NULL) {
  599.                 ev->segname     = "Unknown module";
  600.                 return;
  601.         }
  602.  
  603.         Forbid();
  604.         if (!segsem)
  605.                 segsem = (void *)FindSemaphore("SegTracker");
  606.  
  607.         if (segsem)
  608.                 segname = segsem->seg_Find(ev->calladdr, &hunk, &offset);
  609.  
  610.         if (segname) {
  611.                 strncpy(ev->segname, segname, MAX_SEGTRACKER_LEN);
  612.                 ev->segname[MAX_SEGTRACKER_LEN-1] = 0;
  613.                 ev->hunk   = hunk;
  614.                 ev->offset = offset;
  615.         } else {
  616.                 if (ev->calladdr >= RomStart && ev->calladdr <= RomEnd) {
  617.                         /*
  618.                          *              Let's search the resident list for it ourselves.
  619.                          *              and see if we can't do any better.
  620.                          */
  621.                         struct Resident *res;
  622.                         ULONG *pres = (ULONG *)SysBase->ResModules;
  623.                         UBYTE *p;
  624.  
  625.                         while (*pres) {
  626.                                 if (*pres & 0x80000000) {
  627.                                         pres = (ULONG *)(*pres & 0x7FFFFFFF);
  628.                                         continue;
  629.                                 }
  630.                                 res = (struct Resident *)*pres++;
  631.                                 if (ev->calladdr >= (ULONG)res
  632.                                     && ev->calladdr <= (ULONG)res->rt_EndSkip) {
  633.                                         ev->hunk    = 0;
  634.                                         ev->offset  = ev->calladdr - (ULONG)res;
  635.                                         strcpy(ev->segname, "ROM: ");
  636.                                         strncat(ev->segname + 5, res->rt_IdString,
  637.                                                                                      MAX_SEGTRACKER_LEN - 6);
  638.                                         ev->segname[MAX_SEGTRACKER_LEN-1] = 0;
  639.                                         /*
  640.                                          *              Now filter out any remaining carriage returns
  641.                                          *              and linefeeds, since they look ugly when printed
  642.                                          *              We also convert any open brackets into '\0's
  643.                                          *              since these are just dates which we don't care
  644.                                          *              about -- it looks neater to just display the
  645.                                          *              module name and version.
  646.                                          */
  647.                                         for (p = ev->segname; *p; p++) {
  648.                                                 if (*p < ' ')
  649.                                                         *p = ' ';
  650.                                                 if (*p == '(')
  651.                                                         *p = '\0';
  652.                                         }
  653.                                         break;
  654.                                 }
  655.                         }
  656.                 }
  657.                 if (ev->hunk == INVALID_HUNK)
  658.                         ev->segname = MSG(MSG_SEG_MODULE_NOT_FOUND);
  659.         }
  660.         Permit();
  661. }
  662.  
  663. /*
  664.  *              FormatEvent(eventformat, event, outputstr, start, end)
  665.  *
  666.  *              Formats the specified event according to the passed in format
  667.  *              array. Only that portion of the fully formatted event which
  668.  *              lies from the 'start' to 'end' characters is produced, and
  669.  *              output always starts at the beginning of the outputstr.
  670.  *
  671.  *              For example, a start of 4 and end of 8 will produce a formatted
  672.  *              string of exactly 5 characters.
  673.  *
  674.  *              Each entry in the eventformat array is decoded, and text which
  675.  *              corresponds to the entry type is copied to the output string. If
  676.  *              the text is longer than the entry width setting, it is truncated;
  677.  *              if shorter, it is padded with spaces.
  678.  *
  679.  *              Each event is separated by one space character.
  680.  *
  681.  *              If the event pointer is NULL, then the title headings are returned
  682.  *              instead of the event contents.
  683.  */
  684. void FormatEvent(EventFormat *eventformat, Event *event,
  685.                                  char *outstr, int start, int end)
  686. {
  687.         static char hexbuf8[]  = "00000000";
  688.         static char hexbuf13[] = "0000:00000000";
  689.         static char countbuf[] = "             ";
  690.  
  691.         EventFormat *ef = eventformat;
  692.         int savechar = 0;
  693.         char *savep;
  694.         char *p;
  695.         int col = 0;
  696.  
  697.         end++;          /* Make exclusive rather than inclusive */
  698.  
  699.         while (ef->type != EF_END && col < end) {
  700.                 int width = ef->width;
  701.  
  702.                 /*
  703.                  *              First skip over any entries that fall before desired section
  704.                  */
  705.                 if ((col + width) < start) {
  706.                         col += width + 1;
  707.                         if (col > start)
  708.                                 *outstr++ = ' ';        /* Add leading space */
  709.                         ef++;
  710.                         continue;
  711.                 }
  712.  
  713.                 /*
  714.                  *              Now parse our current entry
  715.                  */
  716.                 if (event) {
  717.                         switch (ef->type) {
  718.  
  719.                         case EF_PROCNAME:       p = event->procname;                                    break;
  720.                         case EF_ACTION:         p = event->action;                                              break;
  721.                         case EF_OPTIONS:        p = event->options;                                             break;
  722.                         case EF_RESULT:         p = event->result;                                              break;
  723.                         case EF_CALLADDR:
  724.                                 p = ltoap(hexbuf8, event->calladdr, 8);
  725.                                 if (*p == ' ')
  726.                                         p++;
  727.                                 break;
  728.  
  729.                         case EF_PROCID:
  730.                                 p = ltoap(hexbuf8, event->processid,8);
  731.                                 if (*p == ' ')
  732.                                         p++;
  733.                                 break;
  734.  
  735.                         case EF_FILENAME:
  736.                         {
  737.                                 /*
  738.                                  *              This is a little trickier since we allow the user
  739.                                  *              to view it left-aligned or right-aligned. For
  740.                                  *              right-aligned, we temporarily replace the first char
  741.                                  *              with a « to indicate there are additional characters
  742.                                  *              to the left, then restore it after we've copied the
  743.                                  *              string data later on. This is much quicker (and
  744.                                  *              safer) than copying the entire string to a temporary
  745.                                  *              just so we can patch a single character of it.
  746.                                  */
  747.                                 int len;
  748.  
  749.                                 p = event->filename;
  750.                                 if (RightAligned) {
  751.                                         len = strlen(p);
  752.                                         if (len > width) {
  753.                                                 p += len - width;
  754.                                                 if (width > 1) {
  755.                                                         savep    = p;
  756.                                                         savechar = *p;
  757.                                                         *p       = '«';
  758.                                                 }
  759.                                         }
  760.                                 }
  761.                                 break;
  762.                         }
  763.  
  764.                         case EF_DATE:
  765.                                 if ((event->flags & EFLG_DONEDATE) == 0) {
  766.                                         SetEventDateStr(event);
  767.                                         event->flags |= EFLG_DONEDATE;
  768.                                 }
  769.                                 p = event->date;
  770.                                 break;
  771.  
  772.                         case EF_TIME:
  773.                                 if ((event->flags & EFLG_DONEDATE) == 0) {
  774.                                         SetEventDateStr(event);
  775.                                         event->flags |= EFLG_DONEDATE;
  776.                                 }
  777.                                 p = event->time;
  778.                                 break;
  779.  
  780.                         case EF_SEGNAME:
  781.                                 if ((event->flags & EFLG_DONESEG) == 0) {
  782.                                         SetEventSegmentStr(event);
  783.                                         event->flags |= EFLG_DONESEG;
  784.                                 }
  785.                                 p = event->segname;
  786.                                 break;
  787.  
  788.                         case EF_COUNT:
  789.                                 /*
  790.                                  *              We subtract BaseSeq from the event number when
  791.                                  *              displaying it so that we have a convenient way of
  792.                                  *              making the event number appear to return to 1,
  793.                                  *              while internally still ensuring that the event
  794.                                  *              number is ALWAYS monotonically increasing, even
  795.                                  *              across buffer clears and the like.
  796.                                  */
  797.                                 mysprintf(countbuf, "%ld", event->seqnum - BaseSeq);
  798.                                 p = countbuf;
  799.                                 break;
  800.  
  801.                         case EF_HUNKOFFSET:
  802.                                 if ((event->flags & EFLG_DONESEG) == 0) {
  803.                                         SetEventSegmentStr(event);
  804.                                         event->flags |= EFLG_DONESEG;
  805.                                 }
  806.                                 if (event->hunk == INVALID_HUNK) {
  807.                                         /*
  808.                                          *              If we couldn't locate the module address,
  809.                                          *              then don't bother printing the hunk/offset
  810.                                          *              info since it will only be zero anyway.
  811.                                          */
  812.                                         p = ""; /* Couldn't find module so don't print hunk addr */
  813.                                 } else {
  814.                                         /*
  815.                                          *              Only output 6 digits of offset and 2 digits
  816.                                          *              of hunk, but arrange that we can handle a
  817.                                          *              full 8 / 4 combination if necessary
  818.                                          */
  819.                                         p = ltoa(hexbuf13+7, event->offset, 6);
  820.                                         *--p = ':';
  821.                                         p = ltoap(p-2, event->hunk, 2);
  822.                                 }
  823.                                 break;
  824.  
  825.                         default:
  826.                                 p = "<Invalid>";
  827.                                 break;
  828.                         }
  829.                 } else {
  830.                         /*
  831.                          *              Format column headings instead
  832.                          */
  833.                         p = MSG(ef->titlemsgid);
  834.                 }
  835.  
  836.                 /*
  837.                  *              Okay, so now we have p pointing to our string. We now need to
  838.                  *              copy a sufficient number of characters into the string to
  839.                  *              fulfill our need.
  840.                  */
  841.                 if (col < start) {
  842.                         /*
  843.                          *              Our first special case is where we have to skip over some of
  844.                          *              the early characters, i.e. chars to left are clipped.
  845.                          */
  846.                         while (col < start && *p) {
  847.                                 col++;
  848.                                 width--;
  849.                                 p++;
  850.                         }
  851.                         if (col < start) {
  852.                                 /*
  853.                                  *              Ran out of characters in our string, so pretend we
  854.                                  *              have a null string and let the following code pad
  855.                                  *              the remaining width with spaces.
  856.                                  */
  857.                                 width -= (start - col);
  858.                                 col    = start;
  859.                         }
  860.                 }
  861.  
  862.                 /*
  863.                  *              Now copy characters intact into string
  864.                  */
  865.                 if ((col + width) > end) {
  866.                         /*
  867.                          *              This field needs to be clipped. We do this quite simply
  868.                          *              by adjusting the width to be the remaining number of
  869.                          *              chars in the string.
  870.                          */
  871.                         width = end - col;
  872.                 }
  873.  
  874.                 /*
  875.                  *              Now copy width chars from our string to the output buffer,
  876.                  *              padding with spaces as necessary.
  877.                  */
  878.                 while (width && *p) {
  879.                         *outstr++ = *p++;
  880.                         width--;
  881.                         col++;
  882.                 }
  883.                 if (width) {
  884.                         memset(outstr, ' ', width);
  885.                         outstr += width;
  886.                         col    += width;
  887.                 }
  888.                 *outstr++ = ' ';                        /* Space in between output fields       */
  889.                 col++;                                          /* onto next column                                     */
  890.                 ef++;                                           /* Onto the next format field           */
  891.                 /*
  892.                  *              Finally, restore the character we replaced with « earlier
  893.                  *              (if any)
  894.                  */
  895.                 if (savechar) {
  896.                         *savep   = savechar;
  897.                         savechar = 0;
  898.                 }
  899.         }
  900.  
  901.         /*
  902.          *              Okay, we've generated the string as requested. Now check to see if
  903.          *              we need to pad the remainder of the string with spaces (i.e. the
  904.          *              number of fields listed wasn't as wide as the whole window).
  905.          *
  906.          *              Ideally, we will never be passed in a start/end pair wider than
  907.          *              the width of the formatted output, but you never know...
  908.          */
  909.         if (col <= end) {
  910.                 memset(outstr, ' ', end-col+1);
  911.                 outstr += end-col+1;
  912.         }
  913.         outstr[-1] = '\0';              /* Remove final terminating space */
  914. }
  915.  
  916. /*
  917.  *              UnderlineTitles(eventformat, outstr, underchar)
  918.  *
  919.  *              This function creates a string of underlines which matches the
  920.  *              event headings in the passed event format, such that if the
  921.  *              headings were printed out in full, the underline string would
  922.  *              line up properly. underchar is the char used for underlining.
  923.  *
  924.  *              The reason we can't just take the obvious approach and generate
  925.  *              a title string, then convert all non-space chars to minus, is
  926.  *              because we can have titles like "Process Name" and we want the
  927.  *              underline to be continuous, not break between process and name.
  928.  *
  929.  *              Returns a pointer to the newly formatted string (outstr). Outstr
  930.  *              must have room to store the entire width, as defined by the
  931.  *              event format.
  932.  */
  933. char *UnderlineTitles(EventFormat *ef, char *outstr, char underchar)
  934. {
  935.         char *p = outstr;
  936.  
  937.         while (ef->type != EF_END) {
  938.                 int width    = ef->width;
  939.                 int titleid  = ef->titlemsgid;
  940.                 int titlelen = strlen(MSG(titleid));
  941.  
  942.                 if (titlelen > width)
  943.                         titlelen = width;
  944.  
  945.                 memset(p, underchar, titlelen);
  946.                 p     += titlelen;
  947.                 width -= titlelen-1;            /* Include an extra space between cols */
  948.                 if (width) {
  949.                         memset(p, ' ', width);
  950.                         p += width;
  951.                 }
  952.                 ef++;
  953.         }
  954.         *--p = 0;                                               /* Replace final space with a null */
  955.         return (outstr);
  956. }
  957.  
  958. #if TESTING
  959. /*
  960.  *              TestEventFields()
  961.  *
  962.  *              Simple test function to check out our format conversion routines
  963.  */
  964. void TestEventFields(void)
  965. {
  966.         static char inline[200];
  967.         static char formatstr[200];
  968.         static EventFormat evformat[50];
  969.  
  970.         while (gets(inline) != NULL) {
  971.                 int len;
  972.  
  973.                 if (*inline == 'q')
  974.                         break;
  975.  
  976.                 len = ParseFormatString(inline, evformat, 50);
  977.                 BuildFormatString(evformat, formatstr, 200);
  978.  
  979.                 printf("Converted string: >>%s<<, length = %d\n", formatstr, len);
  980.         }
  981. }
  982.  
  983. /*
  984.  *              TestFormat
  985.  *
  986.  *              Test our text formatting routines
  987.  */
  988. void TestFormat()
  989. {
  990.         static char inline[200];
  991.         static char formatstr[200];
  992.         static EventFormat evformat[50];
  993.         static char outstr[500];
  994.         Event *ev;
  995.         int showtitles = 0;
  996.  
  997.         for (;;) {
  998.                 int len;
  999.  
  1000.                 printf("Enter a format string (q to quit): ");
  1001.                 gets(inline);
  1002.                 if (*inline == 'q')
  1003.                         return;
  1004.  
  1005.                 len = ParseFormatString(inline, evformat, 50);
  1006.                 BuildFormatString(evformat, formatstr, 200);
  1007.                 printf("Actual string is >> %s << (%d chars)\n", formatstr, len);
  1008.  
  1009.                 /*
  1010.                  *              Get ourselves a new event
  1011.                  */
  1012.                 ev = GetNewEvent(0);
  1013.                 if (!ev) {
  1014.                         printf("Uhoh! Couldn't allocate event!\n");
  1015.                         continue;
  1016.                 }
  1017.                 ev->procname  = "[ 1] SAS/C compiler";
  1018.                 ev->filename  = "s:startup-sequence";
  1019.                 ev->action    = "Open";
  1020.                 ev->result    = "Okay";
  1021.                 ev->calladdr  = 0x12345678;
  1022.                 ev->processid = 0xDEADBEEF;
  1023.                 DateStamp(&ev->datestamp);
  1024.  
  1025.                 for (;;) {
  1026.                         int start, end;
  1027.  
  1028.                         printf("Now enter start,end values (-1 to quit, s to toggle): ");
  1029.                         gets(inline);
  1030.                         if (*inline == 'q')
  1031.                                 return;
  1032.                         if (*inline == 's') {
  1033.                                 showtitles = !showtitles;
  1034.                                 printf("Now printing %s\n", showtitles ? "titles" : "contents");
  1035.                                 continue;
  1036.                         }
  1037.                         sscanf(inline, "%d,%d", &start, &end);
  1038.                         if (start == -1)
  1039.                                 break;
  1040.  
  1041.                         if (showtitles)
  1042.                                 FormatEvent(evformat, NULL, outstr, start, end);
  1043.                         else
  1044.                                 FormatEvent(evformat, ev, outstr, start, end);
  1045.                         printf("|%s|\n", outstr);
  1046.                 }
  1047.         }
  1048. }
  1049.  
  1050. /*
  1051.  *              TestEvents
  1052.  *
  1053.  *              Creates some random events, then prints them out
  1054.  */
  1055. void TestEvents(void)
  1056. {
  1057.         int howmany = 0;
  1058.         int i;
  1059.  
  1060.         printf("Generate how many events? ");
  1061.         scanf("%d", &howmany);
  1062.  
  1063.         for (i = 0; i < howmany; i++) {
  1064.                 Event *ev = GetNewEvent((rand() & 63));
  1065.  
  1066.                 printf("Event head: %8x   Event new: %8x\n",
  1067.                                 (UBYTE *)HeadNode(&EventList) - BufferStart,
  1068.                                 (UBYTE *)ev - BufferStart);
  1069.         }
  1070. }
  1071. #endif
  1072.