home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 10 / amigaformatcd10.iso / -look_here_1st!- / handy_tools / snoopdos / snoopdos_source / buffer.c next >
C/C++ Source or Header  |  1994-09-17  |  29KB  |  1,062 lines

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