home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff232.lzh / Dbug / dbug.c < prev    next >
C/C++ Source or Header  |  1989-08-02  |  45KB  |  1,858 lines

  1. /******************************************************************************
  2.  *                                          *
  3.  *                               N O T I C E                      *
  4.  *                                          *
  5.  *                  Copyright Abandoned, 1987, Fred Fish              *
  6.  *                                          *
  7.  *                                          *
  8.  *    This previously copyrighted work has been placed into the  public     *
  9.  *    domain  by  the  author  and  may be freely used for any purpose,     *
  10.  *    private or commercial.                              *
  11.  *                                          *
  12.  *    Because of the number of inquiries I was receiving about the  use     *
  13.  *    of this product in commercially developed works I have decided to     *
  14.  *    simply make it public domain to further its unrestricted use.   I     *
  15.  *    specifically  would  be  most happy to see this material become a     *
  16.  *    part of the standard Unix distributions by AT&T and the  Berkeley     *
  17.  *    Computer  Science  Research Group, and a standard part of the GNU     *
  18.  *    system from the Free Software Foundation.                  *
  19.  *                                          *
  20.  *    I would appreciate it, as a courtesy, if this notice is  left  in     *
  21.  *    all copies and derivative works.  Thank you.                  *
  22.  *                                          *
  23.  *    The author makes no warranty of any kind  with  respect  to  this     *
  24.  *    product  and  explicitly disclaims any implied warranties of mer-     *
  25.  *    chantability or fitness for any particular purpose.              *
  26.  *                                          *
  27.  ******************************************************************************
  28.  */
  29.  
  30.  
  31. /*
  32.  *  FILE
  33.  *
  34.  *    dbug.c   runtime support routines for dbug package
  35.  *
  36.  *  SCCS
  37.  *
  38.  *    @(#)dbug.c    1.25    7/25/89
  39.  *
  40.  *  DESCRIPTION
  41.  *
  42.  *    These are the runtime support routines for the dbug package.
  43.  *    The dbug package has two main components; the user include
  44.  *    file containing various macro definitions, and the runtime
  45.  *    support routines which are called from the macro expansions.
  46.  *
  47.  *    Externally visible functions in the runtime support module
  48.  *    use the naming convention pattern "_db_xx...xx_", thus
  49.  *    they are unlikely to collide with user defined function names.
  50.  *
  51.  *  AUTHOR(S)
  52.  *
  53.  *    Fred Fish        (base code)
  54.  *    Enhanced Software Technologies, Tempe, AZ
  55.  *    asuvax!mcdphx!estinc!fnf
  56.  *
  57.  *    Binayak Banerjee    (profiling enhancements)
  58.  *    seismo!bpa!sjuvax!bbanerje
  59.  */
  60.  
  61.  
  62. #include <stdio.h>
  63.  
  64. #if NO_VARARGS
  65. #include "vargs.h"        /* Use our "fake" varargs */
  66. #else
  67. #include <varargs.h>        /* Use system supplied varargs package */
  68. #endif
  69.  
  70. #if amiga
  71. #define HZ (50)            /* Probably in some header somewhere */
  72. #endif
  73.  
  74. #ifdef M_XENIX            /* Some xenix compilers predefine this */
  75. #ifndef xenix
  76. #define xenix 1
  77. #endif
  78. #endif
  79.  
  80. /*
  81.  *    Manifest constants that should not require any changes.
  82.  */
  83.  
  84. #define FALSE        0    /* Boolean FALSE */
  85. #define TRUE        1    /* Boolean TRUE */
  86. #define EOS        '\000'    /* End Of String marker */
  87.  
  88. /*
  89.  *    Manifest constants which may be "tuned" if desired.
  90.  */
  91.  
  92. #define PRINTBUF    1024    /* Print buffer size */
  93. #define INDENT        4    /* Indentation per trace level */
  94. #define MAXDEPTH    200    /* Maximum trace depth default */
  95.  
  96. /*
  97.  *    The following flags are used to determine which
  98.  *    capabilities the user has enabled with the state
  99.  *    push macro.
  100.  */
  101.  
  102. #define TRACE_ON    000001    /* Trace enabled */
  103. #define DEBUG_ON    000002    /* Debug enabled */
  104. #define FILE_ON     000004    /* File name print enabled */
  105. #define LINE_ON        000010    /* Line number print enabled */
  106. #define DEPTH_ON    000020    /* Function nest level print enabled */
  107. #define PROCESS_ON    000040    /* Process name print enabled */
  108. #define NUMBER_ON    000100    /* Number each line of output */
  109. #define PROFILE_ON    000200    /* Print out profiling code */
  110. #define PID_ON        000400    /* Identify each line with process id */
  111.  
  112. #define TRACING (stack -> flags & TRACE_ON)
  113. #define DEBUGGING (stack -> flags & DEBUG_ON)
  114. #define PROFILING (stack -> flags & PROFILE_ON)
  115. #define STREQ(a,b) (strcmp(a,b) == 0)
  116.  
  117. /*
  118.  *    Typedefs to make things more obvious.
  119.  */
  120.  
  121. #define VOID void        /* Can't use typedef for most compilers */
  122. typedef int BOOLEAN;
  123.  
  124. /*
  125.  *    Make it easy to change storage classes if necessary.
  126.  */
  127.  
  128. #define LOCAL static        /* Names not needed by outside world */
  129. #define IMPORT extern        /* Names defined externally */
  130. #define EXPORT            /* Allocated here, available globally */
  131. #define AUTO auto        /* Names to be allocated on stack */
  132. #define REGISTER register    /* Names to be placed in registers */
  133.  
  134. /*
  135.  * The default file for profiling.  Could also add another flag
  136.  * (G?) which allowed the user to specify this.
  137.  *
  138.  * If the automatic variables get allocated on the stack in
  139.  * reverse order from their declarations, then define AUTOS_REVERSE.
  140.  * This is used by the code that keeps track of stack usage.  For
  141.  * forward allocation, the difference in the dbug frame pointers 
  142.  * represents stack used by the callee function.  For reverse allocation,
  143.  * the difference represents stack used by the caller function.
  144.  *
  145.  */
  146.  
  147. #define PROF_FILE    "dbugmon.out"
  148. #define PROF_EFMT    "E\t%ld\t%s\n"
  149. #define PROF_SFMT    "S\t%lx\t%lx\t%s\n"
  150. #define PROF_XFMT    "X\t%ld\t%s\n"
  151.  
  152. #if M_I386        /* predefined by xenix 386 compiler */
  153. #define AUTOS_REVERSE 1
  154. #endif
  155.  
  156. /*
  157.  *    Variables which are available externally but should only
  158.  *    be accessed via the macro package facilities.
  159.  */
  160.  
  161. EXPORT FILE *_db_fp_ = stderr;        /* Output stream, default stderr */
  162. EXPORT FILE *_db_pfp_ = (FILE *)0;    /* Profile stream, 'dbugmon.out' */
  163. EXPORT char *_db_process_ = "dbug";    /* Pointer to process name; argv[0] */
  164. EXPORT BOOLEAN _db_on_ = FALSE;        /* TRUE if debugging currently on */
  165. EXPORT BOOLEAN _db_pon_ = FALSE;    /* TRUE if debugging currently on */
  166.  
  167. /*
  168.  *    Externally supplied functions.
  169.  */
  170.  
  171. #if (unix || xenix)        /* Only needed for unix */
  172. IMPORT VOID perror ();        /* Print system/library error */
  173. IMPORT int chown ();        /* Change owner of a file */
  174. IMPORT int getgid ();        /* Get real group id */
  175. IMPORT int getuid ();        /* Get real user id */
  176. IMPORT int access ();        /* Test file for access */
  177. #else
  178. #if !(amiga && LATTICE)
  179. LOCAL VOID perror ();        /* Fake system/library error print routine */
  180. #endif
  181. #endif
  182.  
  183. # if BSD4_3 || sun
  184. IMPORT int getrusage ();
  185. #endif
  186.  
  187. IMPORT int atoi ();        /* Convert ascii to integer */
  188. IMPORT VOID exit ();        /* Terminate execution */
  189. IMPORT int fclose ();        /* Close a stream */
  190. IMPORT FILE *fopen ();        /* Open a stream */
  191. IMPORT int fprintf ();        /* Formatted print on file */
  192. IMPORT VOID free ();
  193. IMPORT char *malloc ();        /* Allocate memory */
  194. IMPORT int strcmp ();        /* Compare strings */
  195. IMPORT char *strcpy ();        /* Copy strings around */
  196. IMPORT int strlen ();        /* Find length of string */
  197.  
  198. #if NO_VARARGS
  199. static int vfprintf ();        /* Use our local fake version */
  200. #else
  201. IMPORT int vfprintf ();        /* Varargs form of fprintf */
  202. #endif
  203.  
  204. #ifndef fflush            /* This is sometimes a macro */
  205. IMPORT int fflush ();        /* Flush output for stream */
  206. #endif
  207.  
  208.  
  209. /*
  210.  *    The user may specify a list of functions to trace or 
  211.  *    debug.  These lists are kept in a linear linked list,
  212.  *    a very simple implementation.
  213.  */
  214.  
  215. struct link {
  216.     char *string;        /* Pointer to link's contents */
  217.     struct link *next_link;    /* Pointer to the next link */
  218. };
  219.  
  220.  
  221. /*
  222.  *    Debugging states can be pushed or popped off of a
  223.  *    stack which is implemented as a linked list.  Note
  224.  *    that the head of the list is the current state and the
  225.  *    stack is pushed by adding a new state to the head of the
  226.  *    list or popped by removing the first link.
  227.  */
  228.  
  229. struct state {
  230.     int flags;                /* Current state flags */
  231.     int maxdepth;            /* Current maximum trace depth */
  232.     unsigned int delay;            /* Delay after each output line */
  233.     int level;                /* Current function nesting level */
  234.     FILE *out_file;            /* Current output stream */
  235.     FILE *prof_file;            /* Current profiling stream */
  236.     struct link *functions;        /* List of functions */
  237.     struct link *p_functions;        /* List of profiled functions */
  238.     struct link *keywords;        /* List of debug keywords */
  239.     struct link *processes;        /* List of process names */
  240.     struct state *next_state;        /* Next state in the list */
  241. };
  242.  
  243. LOCAL struct state *stack = NULL;    /* Linked list of stacked states */
  244.  
  245. /*
  246.  *    Local variables not seen by user.
  247.  */
  248.  
  249. LOCAL int lineno = 0;        /* Current debugger output line number */
  250. LOCAL char *func = "?func";    /* Name of current user function */
  251. LOCAL char *file = "?file";    /* Name of current user file */
  252. LOCAL BOOLEAN init_done = FALSE;/* Set to TRUE when initialization done */
  253. LOCAL char **framep = NULL;    /* Pointer to current frame */
  254.  
  255. #if (unix || xenix || amiga)
  256. LOCAL int jmplevel;        /* Remember nesting level at setjmp () */
  257. LOCAL char *jmpfunc;        /* Remember current function for setjmp */
  258. LOCAL char *jmpfile;        /* Remember current file for setjmp */
  259. #endif
  260.  
  261. LOCAL struct link *ListParse ();/* Parse a debug command string */
  262. LOCAL char *StrDup ();        /* Make a fresh copy of a string */
  263. LOCAL VOID OpenFile ();        /* Open debug output stream */
  264. LOCAL VOID OpenProfile ();    /* Open profile output stream */
  265. LOCAL VOID CloseFile ();    /* Close debug output stream */
  266. LOCAL VOID PushState ();    /* Push current debug state */
  267. LOCAL VOID ChangeOwner ();    /* Change file owner and group */
  268. LOCAL BOOLEAN DoTrace ();    /* Test for tracing enabled */
  269. LOCAL BOOLEAN Writable ();    /* Test to see if file is writable */
  270. LOCAL unsigned long Clock ();    /* Return current user time (ms) */
  271. LOCAL long *DbugMalloc ();    /* Allocate memory for runtime support */
  272. LOCAL char *BaseName ();    /* Remove leading pathname components */
  273. LOCAL VOID DoPrefix ();        /* Print debugger line prefix */
  274. LOCAL VOID FreeList ();        /* Free memory from linked list */
  275. LOCAL VOID Indent ();        /* Indent line to specified indent */
  276.  
  277.                 /* Supplied in Sys V runtime environ */
  278. LOCAL char *strtok ();        /* Break string into tokens */
  279. LOCAL char *strrchr ();        /* Find last occurance of char */
  280.  
  281. /*
  282.  *    The following local variables are used to hold the state information
  283.  *    between the call to _db_pargs_() and _db_doprnt_(), during
  284.  *    expansion of the DBUG_PRINT macro.  This is the only macro
  285.  *    that currently uses these variables.
  286.  *
  287.  *    These variables are currently used only by _db_pargs_() and
  288.  *    _db_doprnt_().
  289.  */
  290.  
  291. LOCAL int u_line = 0;        /* User source code line number */
  292. LOCAL char *u_keyword = "?";    /* Keyword for current macro */
  293.  
  294. /*
  295.  *    Miscellaneous printf format strings.
  296.  */
  297.  
  298. #define ERR_MISSING_RETURN "%s: missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
  299. #define ERR_OPEN "%s: can't open debug output stream \"%s\": "
  300. #define ERR_CLOSE "%s: can't close debug file: "
  301. #define ERR_ABORT "%s: debugger aborting because %s\n"
  302. #define ERR_CHOWN "%s: can't change owner/group of \"%s\": "
  303.  
  304. /*
  305.  *    Macros and defines for testing file accessibility under UNIX.
  306.  */
  307.  
  308. #if (unix || xenix)
  309. #  define A_EXISTS    00        /* Test for file existance */
  310. #  define A_EXECUTE    01        /* Test for execute permission */
  311. #  define A_WRITE    02        /* Test for write access */
  312. #  define A_READ    03        /* Test for read access */
  313. #  define EXISTS(pathname) (access (pathname, A_EXISTS) == 0)
  314. #  define WRITABLE(pathname) (access (pathname, A_WRITE) == 0)
  315. #else
  316. #  define EXISTS(pathname) (FALSE)    /* Assume no existance */
  317. #endif
  318.  
  319. /*
  320.  *    Translate some calls among different systems.
  321.  */
  322.  
  323. #if (unix || xenix)
  324. # define Delay sleep
  325. IMPORT unsigned int sleep ();    /* Pause for given number of seconds */
  326. #endif
  327.  
  328. #if amiga
  329. IMPORT int Delay ();        /* Pause for given number of ticks */
  330. #endif
  331.  
  332.  
  333. /*
  334.  *  FUNCTION
  335.  *
  336.  *    _db_push_    push current debugger state and set up new one
  337.  *
  338.  *  SYNOPSIS
  339.  *
  340.  *    VOID _db_push_ (control)
  341.  *    char *control;
  342.  *
  343.  *  DESCRIPTION
  344.  *
  345.  *    Given pointer to a debug control string in "control", pushes
  346.  *    the current debug state, parses the control string, and sets
  347.  *    up a new debug state.
  348.  *
  349.  *    The only attribute of the new state inherited from the previous
  350.  *    state is the current function nesting level.  This can be
  351.  *    overridden by using the "r" flag in the control string.
  352.  *
  353.  *    The debug control string is a sequence of colon separated fields
  354.  *    as follows:
  355.  *
  356.  *        <field_1>:<field_2>:...:<field_N>
  357.  *
  358.  *    Each field consists of a mandatory flag character followed by
  359.  *    an optional "," and comma separated list of modifiers:
  360.  *
  361.  *        flag[,modifier,modifier,...,modifier]
  362.  *
  363.  *    The currently recognized flag characters are:
  364.  *
  365.  *        d    Enable output from DBUG_<N> macros for
  366.  *            for the current state.  May be followed
  367.  *            by a list of keywords which selects output
  368.  *            only for the DBUG macros with that keyword.
  369.  *            A null list of keywords implies output for
  370.  *            all macros.
  371.  *
  372.  *        D    Delay after each debugger output line.
  373.  *            The argument is the number of tenths of seconds
  374.  *            to delay, subject to machine capabilities.
  375.  *            I.E.  -#D,20 is delay two seconds.
  376.  *
  377.  *        f    Limit debugging and/or tracing, and profiling to the
  378.  *            list of named functions.  Note that a null list will
  379.  *            disable all functions.  The appropriate "d" or "t"
  380.  *            flags must still be given, this flag only limits their
  381.  *            actions if they are enabled.
  382.  *
  383.  *        F    Identify the source file name for each
  384.  *            line of debug or trace output.
  385.  *
  386.  *        i    Identify the process with the pid for each line of
  387.  *            debug or trace output.
  388.  *
  389.  *        g    Enable profiling.  Create a file called 'dbugmon.out'
  390.  *            containing information that can be used to profile
  391.  *            the program.  May be followed by a list of keywords
  392.  *            that select profiling only for the functions in that
  393.  *            list.  A null list implies that all functions are
  394.  *            considered.
  395.  *
  396.  *        L    Identify the source file line number for
  397.  *            each line of debug or trace output.
  398.  *
  399.  *        n    Print the current function nesting depth for
  400.  *            each line of debug or trace output.
  401.  *    
  402.  *        N    Number each line of dbug output.
  403.  *
  404.  *        o    Redirect the debugger output stream to the
  405.  *            specified file.  The default output is stderr.
  406.  *
  407.  *        p    Limit debugger actions to specified processes.
  408.  *            A process must be identified with the
  409.  *            DBUG_PROCESS macro and match one in the list
  410.  *            for debugger actions to occur.
  411.  *
  412.  *        P    Print the current process name for each
  413.  *            line of debug or trace output.
  414.  *
  415.  *        r    When pushing a new state, do not inherit
  416.  *            the previous state's function nesting level.
  417.  *            Useful when the output is to start at the
  418.  *            left margin.
  419.  *
  420.  *        t    Enable function call/exit trace lines.
  421.  *            May be followed by a list (containing only
  422.  *            one modifier) giving a numeric maximum
  423.  *            trace level, beyond which no output will
  424.  *            occur for either debugging or tracing
  425.  *            macros.  The default is a compile time
  426.  *            option.
  427.  *
  428.  *    Some examples of debug control strings which might appear
  429.  *    on a shell command line (the "-#" is typically used to
  430.  *    introduce a control string to an application program) are:
  431.  *
  432.  *        -#d:t
  433.  *        -#d:f,main,subr1:F:L:t,20
  434.  *        -#d,input,output,files:n
  435.  *
  436.  *    For convenience, any leading "-#" is stripped off.
  437.  *
  438.  */
  439.  
  440.  
  441. VOID _db_push_ (control)
  442. char *control;
  443. {
  444.     REGISTER char *scan;
  445.     REGISTER struct link *temp;
  446.  
  447.     if (control && *control == '-') {
  448.     if (*++control == '#') {
  449.         control++;
  450.     }    
  451.     }
  452.     control = StrDup (control);
  453.     PushState ();
  454.     scan = strtok (control, ":");
  455.     for (; scan != NULL; scan = strtok ((char *)NULL, ":")) {
  456.     switch (*scan++) {
  457.         case 'd': 
  458.         _db_on_ = TRUE;
  459.         stack -> flags |= DEBUG_ON;
  460.         if (*scan++ == ',') {
  461.             stack -> keywords = ListParse (scan);
  462.         }
  463.             break;
  464.         case 'D': 
  465.         stack -> delay = 0;
  466.         if (*scan++ == ',') {
  467.             temp = ListParse (scan);
  468.             stack -> delay = DelayArg (atoi (temp -> string));
  469.             FreeList (temp);
  470.         }
  471.         break;
  472.         case 'f': 
  473.         if (*scan++ == ',') {
  474.             stack -> functions = ListParse (scan);
  475.         }
  476.         break;
  477.         case 'F': 
  478.         stack -> flags |= FILE_ON;
  479.         break;
  480.         case 'i':
  481.         stack -> flags |= PID_ON;
  482.         break;
  483.         case 'g': 
  484.         _db_pon_ = TRUE;
  485.         OpenProfile(PROF_FILE);
  486.         stack -> flags |= PROFILE_ON;
  487.         if (*scan++ == ',') {
  488.             stack -> p_functions = ListParse (scan);
  489.         }
  490.         break;
  491.         case 'L': 
  492.         stack -> flags |= LINE_ON;
  493.         break;
  494.         case 'n': 
  495.         stack -> flags |= DEPTH_ON;
  496.         break;
  497.         case 'N':
  498.         stack -> flags |= NUMBER_ON;
  499.         break;
  500.         case 'o': 
  501.         if (*scan++ == ',') {
  502.             temp = ListParse (scan);
  503.             OpenFile (temp -> string);
  504.             FreeList (temp);
  505.         } else {
  506.             OpenFile ("-");
  507.         }
  508.         break;
  509.         case 'p':
  510.         if (*scan++ == ',') {
  511.             stack -> processes = ListParse (scan);
  512.         }
  513.         break;
  514.         case 'P': 
  515.         stack -> flags |= PROCESS_ON;
  516.         break;
  517.         case 'r': 
  518.         stack -> level = 0;
  519.         break;
  520.         case 't': 
  521.         stack -> flags |= TRACE_ON;
  522.         if (*scan++ == ',') {
  523.             temp = ListParse (scan);
  524.             stack -> maxdepth = atoi (temp -> string);
  525.             FreeList (temp);
  526.         }
  527.         break;
  528.     }
  529.     }
  530.     free (control);
  531. }
  532.  
  533.  
  534.  
  535. /*
  536.  *  FUNCTION
  537.  *
  538.  *    _db_pop_    pop the debug stack
  539.  *
  540.  *  DESCRIPTION
  541.  *
  542.  *    Pops the debug stack, returning the debug state to its
  543.  *    condition prior to the most recent _db_push_ invocation.
  544.  *    Note that the pop will fail if it would remove the last
  545.  *    valid state from the stack.  This prevents user errors
  546.  *    in the push/pop sequence from screwing up the debugger.
  547.  *    Maybe there should be some kind of warning printed if the
  548.  *    user tries to pop too many states.
  549.  *
  550.  */
  551.  
  552. VOID _db_pop_ ()
  553. {
  554.     REGISTER struct state *discard;
  555.  
  556.     discard = stack;
  557.     if (discard != NULL && discard -> next_state != NULL) {
  558.     stack = discard -> next_state;
  559.     _db_fp_ = stack -> out_file;
  560.     _db_pfp_ = stack -> prof_file;
  561.     if (discard -> keywords != NULL) {
  562.         FreeList (discard -> keywords);
  563.     }
  564.     if (discard -> functions != NULL) {
  565.         FreeList (discard -> functions);
  566.     }
  567.     if (discard -> processes != NULL) {
  568.         FreeList (discard -> processes);
  569.     }
  570.     if (discard -> p_functions != NULL) {
  571.         FreeList (discard -> p_functions);
  572.     }
  573.     CloseFile (discard -> out_file);
  574.     CloseFile (discard -> prof_file);
  575.     free ((char *) discard);
  576.     }
  577. }
  578.  
  579.  
  580. /*
  581.  *  FUNCTION
  582.  *
  583.  *    _db_enter_    process entry point to user function
  584.  *
  585.  *  SYNOPSIS
  586.  *
  587.  *    VOID _db_enter_ (_func_, _file_, _line_,
  588.  *             _sfunc_, _sfile_, _slevel_, _sframep_)
  589.  *    char *_func_;        points to current function name
  590.  *    char *_file_;        points to current file name
  591.  *    int _line_;        called from source line number
  592.  *    char **_sfunc_;        save previous _func_
  593.  *    char **_sfile_;        save previous _file_
  594.  *    int *_slevel_;        save previous nesting level
  595.  *    char ***_sframep_;    save previous frame pointer
  596.  *
  597.  *  DESCRIPTION
  598.  *
  599.  *    Called at the beginning of each user function to tell
  600.  *    the debugger that a new function has been entered.
  601.  *    Note that the pointers to the previous user function
  602.  *    name and previous user file name are stored on the
  603.  *    caller's stack (this is why the ENTER macro must be
  604.  *    the first "executable" code in a function, since it
  605.  *    allocates these storage locations).  The previous nesting
  606.  *    level is also stored on the callers stack for internal
  607.  *    self consistency checks.
  608.  *
  609.  *    Also prints a trace line if tracing is enabled and
  610.  *    increments the current function nesting depth.
  611.  *
  612.  *    Note that this mechanism allows the debugger to know
  613.  *    what the current user function is at all times, without
  614.  *    maintaining an internal stack for the function names.
  615.  *
  616.  */
  617.  
  618. VOID _db_enter_ (_func_, _file_, _line_, _sfunc_, _sfile_, _slevel_,
  619.          _sframep_)
  620. char *_func_;
  621. char *_file_;
  622. int _line_;
  623. char **_sfunc_;
  624. char **_sfile_;
  625. int *_slevel_;
  626. char ***_sframep_;
  627. {
  628.     long stackused;
  629.  
  630.     if (!init_done) {
  631.     _db_push_ ("");
  632.     }
  633.     *_sfunc_ = func;
  634.     func = _func_;
  635.     *_sfile_ = file;
  636.     file = BaseName (_file_);
  637.     stack -> level++;
  638.     *_slevel_ = stack -> level;
  639.     *_sframep_ = framep;
  640.     framep = (char **) _sframep_;
  641.     if (DoProfile ()) {
  642.     if (*framep == NULL) {
  643.         stackused = 0;
  644.     } else {
  645.         stackused = ((long)(*framep)) - ((long)(framep));
  646.         stackused = stackused > 0 ? stackused : -stackused;
  647.     }
  648.     (VOID) fprintf (_db_pfp_, PROF_EFMT , Clock (), func);
  649. #if AUTOS_REVERSE
  650.     (VOID) fprintf (_db_pfp_, PROF_SFMT, framep, stackused, *_sfunc_);
  651. #else
  652.     (VOID) fprintf (_db_pfp_, PROF_SFMT, framep, stackused, func);
  653. #endif
  654.     (VOID) fflush (_db_pfp_);
  655.     }
  656.     if (DoTrace ()) {
  657.     DoPrefix (_line_);
  658.     Indent (stack -> level);
  659.     (VOID) fprintf (_db_fp_, ">%s\n", func);
  660.     (VOID) fflush (_db_fp_);
  661.     (VOID) Delay (stack -> delay);
  662.     }
  663. }
  664.  
  665.  
  666. /*
  667.  *  FUNCTION
  668.  *
  669.  *    _db_return_    process exit from user function
  670.  *
  671.  *  SYNOPSIS
  672.  *
  673.  *    VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
  674.  *    int _line_;        current source line number
  675.  *    char **_sfunc_;        where previous _func_ is to be retrieved
  676.  *    char **_sfile_;        where previous _file_ is to be retrieved
  677.  *    int *_slevel_;        where previous level was stashed
  678.  *
  679.  *  DESCRIPTION
  680.  *
  681.  *    Called just before user function executes an explicit or implicit
  682.  *    return.  Prints a trace line if trace is enabled, decrements
  683.  *    the current nesting level, and restores the current function and
  684.  *    file names from the defunct function's stack.
  685.  *
  686.  */
  687.  
  688. VOID _db_return_ (_line_, _sfunc_, _sfile_, _slevel_)
  689. int _line_;
  690. char **_sfunc_;
  691. char **_sfile_;
  692. int *_slevel_;
  693. {
  694.     if (!init_done) {
  695.     _db_push_ ("");
  696.     }
  697.     if (stack -> level != *_slevel_ && (TRACING || DEBUGGING || PROFILING)) {
  698.     (VOID) fprintf (_db_fp_, ERR_MISSING_RETURN, _db_process_, func);
  699.     } else if (DoProfile ()) {
  700.     (VOID) fprintf (_db_pfp_, PROF_XFMT, Clock(), func);
  701.     } else if (DoTrace ()) {
  702.     DoPrefix (_line_);
  703.     Indent (stack -> level);
  704.     (VOID) fprintf (_db_fp_, "<%s\n", func);
  705.     }
  706.     (VOID) fflush (_db_fp_);
  707.     (VOID) Delay (stack -> delay);
  708.     stack -> level = *_slevel_ - 1;
  709.     func = *_sfunc_;
  710.     file = *_sfile_;
  711.     if (framep != NULL) {
  712.     framep = (char **) *framep;
  713.     }
  714. }
  715.  
  716.  
  717. /*
  718.  *  FUNCTION
  719.  *
  720.  *    _db_pargs_    log arguments for subsequent use by _db_doprnt_()
  721.  *
  722.  *  SYNOPSIS
  723.  *
  724.  *    VOID _db_pargs_ (_line_, keyword)
  725.  *    int _line_;
  726.  *    char *keyword;
  727.  *
  728.  *  DESCRIPTION
  729.  *
  730.  *    The new universal printing macro DBUG_PRINT, which replaces
  731.  *    all forms of the DBUG_N macros, needs two calls to runtime
  732.  *    support routines.  The first, this function, remembers arguments
  733.  *    that are used by the subsequent call to _db_doprnt_().
  734. *
  735.  */
  736.  
  737. VOID _db_pargs_ (_line_, keyword)
  738. int _line_;
  739. char *keyword;
  740. {
  741.     u_line = _line_;
  742.     u_keyword = keyword;
  743. }
  744.  
  745.  
  746. /*
  747.  *  FUNCTION
  748.  *
  749.  *    _db_doprnt_    handle print of debug lines
  750.  *
  751.  *  SYNOPSIS
  752.  *
  753.  *    VOID _db_doprnt_ (format, va_alist)
  754.  *    char *format;
  755.  *    va_dcl;
  756.  *
  757.  *  DESCRIPTION
  758.  *
  759.  *    When invoked via one of the DBUG macros, tests the current keyword
  760.  *    set by calling _db_pargs_() to see if that macro has been selected
  761.  *    for processing via the debugger control string, and if so, handles
  762.  *    printing of the arguments via the format string.  The line number
  763.  *    of the DBUG macro in the source is found in u_line.
  764.  *
  765.  *    Note that the format string SHOULD NOT include a terminating
  766.  *    newline, this is supplied automatically.
  767.  *
  768.  */
  769.  
  770. /*VARARGS1*/
  771. VOID _db_doprnt_ (format, va_alist)
  772. char *format;
  773. va_dcl
  774. {
  775.     va_list args;
  776.  
  777.     va_start (args);
  778.     if (_db_keyword_ (u_keyword)) {
  779.     DoPrefix (u_line);
  780.     if (TRACING) {
  781.         Indent (stack -> level + 1);
  782.     } else {
  783.         (VOID) fprintf (_db_fp_, "%s: ", func);
  784.     }
  785.     (VOID) fprintf (_db_fp_, "%s: ", u_keyword);
  786.     (VOID) vfprintf (_db_fp_, format, args);
  787.     (VOID) fprintf (_db_fp_, "\n");
  788.     (VOID) fflush (_db_fp_);
  789.     (VOID) Delay (stack -> delay);
  790.     }
  791.     va_end (args);
  792. }
  793.  
  794.  
  795. /*
  796.  *  FUNCTION
  797.  *
  798.  *    ListParse    parse list of modifiers in debug control string
  799.  *
  800.  *  SYNOPSIS
  801.  *
  802.  *    LOCAL struct link *ListParse (ctlp)
  803.  *    char *ctlp;
  804.  *
  805.  *  DESCRIPTION
  806.  *
  807.  *    Given pointer to a comma separated list of strings in "cltp",
  808.  *    parses the list, building a list and returning a pointer to it.
  809.  *    The original comma separated list is destroyed in the process of
  810.  *    building the linked list, thus it had better be a duplicate
  811.  *    if it is important.
  812.  *
  813.  *    Note that since each link is added at the head of the list,
  814.  *    the final list will be in "reverse order", which is not
  815.  *    significant for our usage here.
  816.  *
  817.  */
  818.  
  819. LOCAL struct link *ListParse (ctlp)
  820. char *ctlp;
  821. {
  822.     REGISTER char *start;
  823.     REGISTER struct link *new;
  824.     REGISTER struct link *head;
  825.  
  826.     head = NULL;
  827.     while (*ctlp != EOS) {
  828.     start = ctlp;
  829.     while (*ctlp != EOS && *ctlp != ',') {
  830.         ctlp++;
  831.     }
  832.     if (*ctlp == ',') {
  833.         *ctlp++ = EOS;
  834.     }
  835.     new = (struct link *) DbugMalloc (sizeof (struct link));
  836.     new -> string = StrDup (start);
  837.     new -> next_link = head;
  838.     head = new;
  839.     }
  840.     return (head);
  841. }
  842.  
  843.  
  844. /*
  845.  *  FUNCTION
  846.  *
  847.  *    InList    test a given string for member of a given list
  848.  *
  849.  *  SYNOPSIS
  850.  *
  851.  *    LOCAL BOOLEAN InList (linkp, cp)
  852.  *    struct link *linkp;
  853.  *    char *cp;
  854.  *
  855.  *  DESCRIPTION
  856.  *
  857.  *    Tests the string pointed to by "cp" to determine if it is in
  858.  *    the list pointed to by "linkp".  Linkp points to the first
  859.  *    link in the list.  If linkp is NULL then the string is treated
  860.  *    as if it is in the list (I.E all strings are in the null list).
  861.  *    This may seem rather strange at first but leads to the desired
  862.  *    operation if no list is given.  The net effect is that all
  863.  *    strings will be accepted when there is no list, and when there
  864.  *    is a list, only those strings in the list will be accepted.
  865.  *
  866.  */
  867.  
  868. LOCAL BOOLEAN InList (linkp, cp)
  869. struct link *linkp;
  870. char *cp;
  871. {
  872.     REGISTER struct link *scan;
  873.     REGISTER BOOLEAN accept;
  874.  
  875.     if (linkp == NULL) {
  876.     accept = TRUE;
  877.     } else {
  878.     accept = FALSE;
  879.     for (scan = linkp; scan != NULL; scan = scan -> next_link) {
  880.         if (STREQ (scan -> string, cp)) {
  881.         accept = TRUE;
  882.         break;
  883.         }
  884.     }
  885.     }
  886.     return (accept);
  887. }
  888.  
  889.  
  890. /*
  891.  *  FUNCTION
  892.  *
  893.  *    PushState    push current state onto stack and set up new one
  894.  *
  895.  *  SYNOPSIS
  896.  *
  897.  *    LOCAL VOID PushState ()
  898.  *
  899.  *  DESCRIPTION
  900.  *
  901.  *    Pushes the current state on the state stack, and initializes
  902.  *    a new state.  The only parameter inherited from the previous
  903.  *    state is the function nesting level.  This action can be
  904.  *    inhibited if desired, via the "r" flag.
  905.  *
  906.  *    The state stack is a linked list of states, with the new
  907.  *    state added at the head.  This allows the stack to grow
  908.  *    to the limits of memory if necessary.
  909.  *
  910.  */
  911.  
  912. LOCAL VOID PushState ()
  913. {
  914.     REGISTER struct state *new;
  915.  
  916.     new = (struct state *) DbugMalloc (sizeof (struct state));
  917.     new -> flags = 0;
  918.     new -> delay = 0;
  919.     new -> maxdepth = MAXDEPTH;
  920.     if (stack != NULL) {
  921.     new -> level = stack -> level;
  922.     } else {
  923.     new -> level = 0;
  924.     }
  925.     new -> out_file = stderr;
  926.     new -> functions = NULL;
  927.     new -> p_functions = NULL;
  928.     new -> keywords = NULL;
  929.     new -> processes = NULL;
  930.     new -> next_state = stack;
  931.     stack = new;
  932.     init_done = TRUE;
  933. }
  934.  
  935.  
  936. /*
  937.  *  FUNCTION
  938.  *
  939.  *    DoTrace    check to see if tracing is current enabled
  940.  *
  941.  *  SYNOPSIS
  942.  *
  943.  *    LOCAL BOOLEAN DoTrace ()
  944.  *
  945.  *  DESCRIPTION
  946.  *
  947.  *    Checks to see if tracing is enabled based on whether the
  948.  *    user has specified tracing, the maximum trace depth has
  949.  *    not yet been reached, the current function is selected,
  950.  *    and the current process is selected.  Returns TRUE if
  951.  *    tracing is enabled, FALSE otherwise.
  952.  *
  953.  */
  954.  
  955. LOCAL BOOLEAN DoTrace ()
  956. {
  957.     REGISTER BOOLEAN trace;
  958.  
  959.     trace = FALSE;
  960.     if (TRACING) {
  961.     if (stack -> level <= stack -> maxdepth) {
  962.         if (InList (stack -> functions, func)) {
  963.         if (InList (stack -> processes, _db_process_)) {
  964.             trace = TRUE;
  965.         }
  966.         }
  967.     }
  968.     }
  969.     return (trace);
  970. }
  971.  
  972.  
  973. /*
  974.  *  FUNCTION
  975.  *
  976.  *    DoProfile    check to see if profiling is current enabled
  977.  *
  978.  *  SYNOPSIS
  979.  *
  980.  *    LOCAL BOOLEAN DoProfile ()
  981.  *
  982.  *  DESCRIPTION
  983.  *
  984.  *    Checks to see if profiling is enabled based on whether the
  985.  *    user has specified profiling, the maximum trace depth has
  986.  *    not yet been reached, the current function is selected,
  987.  *    and the current process is selected.  Returns TRUE if
  988.  *    profiling is enabled, FALSE otherwise.
  989.  *
  990.  */
  991.  
  992. LOCAL BOOLEAN DoProfile ()
  993. {
  994.     REGISTER BOOLEAN profile;
  995.  
  996.     profile = FALSE;
  997.     if (PROFILING) {
  998.     if (stack -> level <= stack -> maxdepth) {
  999.         if (InList (stack -> p_functions, func)) {
  1000.         if (InList (stack -> processes, _db_process_)) {
  1001.             profile = TRUE;
  1002.         }
  1003.         }
  1004.     }
  1005.     }
  1006.     return (profile);
  1007. }
  1008.  
  1009.  
  1010. /*
  1011.  *  FUNCTION
  1012.  *
  1013.  *    _db_keyword_    test keyword for member of keyword list
  1014.  *
  1015.  *  SYNOPSIS
  1016.  *
  1017.  *    BOOLEAN _db_keyword_ (keyword)
  1018.  *    char *keyword;
  1019.  *
  1020.  *  DESCRIPTION
  1021.  *
  1022.  *    Test a keyword to determine if it is in the currently active
  1023.  *    keyword list.  As with the function list, a keyword is accepted
  1024.  *    if the list is null, otherwise it must match one of the list
  1025.  *    members.  When debugging is not on, no keywords are accepted.
  1026.  *    After the maximum trace level is exceeded, no keywords are
  1027.  *    accepted (this behavior subject to change).  Additionally,
  1028.  *    the current function and process must be accepted based on
  1029.  *    their respective lists.
  1030.  *
  1031.  *    Returns TRUE if keyword accepted, FALSE otherwise.
  1032.  *
  1033.  */
  1034.  
  1035. BOOLEAN _db_keyword_ (keyword)
  1036. char *keyword;
  1037. {
  1038.     REGISTER BOOLEAN accept;
  1039.  
  1040.     if (!init_done) {
  1041.     _db_push_ ("");
  1042.     }
  1043.     accept = FALSE;
  1044.     if (DEBUGGING) {
  1045.     if (stack -> level <= stack -> maxdepth) {
  1046.         if (InList (stack -> functions, func)) {
  1047.         if (InList (stack -> keywords, keyword)) {
  1048.             if (InList (stack -> processes, _db_process_)) {
  1049.             accept = TRUE;
  1050.             }
  1051.         }
  1052.         }
  1053.     }
  1054.     }
  1055.     return (accept);
  1056. }
  1057.  
  1058.  
  1059. /*
  1060.  *  FUNCTION
  1061.  *
  1062.  *    Indent    indent a line to the given indentation level
  1063.  *
  1064.  *  SYNOPSIS
  1065.  *
  1066.  *    LOCAL VOID Indent (indent)
  1067.  *    int indent;
  1068.  *
  1069.  *  DESCRIPTION
  1070.  *
  1071.  *    Indent a line to the given level.  Note that this is
  1072.  *    a simple minded but portable implementation.
  1073.  *    There are better ways.
  1074.  *
  1075.  *    Also, the indent must be scaled by the compile time option
  1076.  *    of character positions per nesting level.
  1077.  *
  1078.  */
  1079.  
  1080. LOCAL VOID Indent (indent)
  1081. int indent;
  1082. {
  1083.     REGISTER int count;
  1084.     AUTO char buffer[PRINTBUF];
  1085.  
  1086.     indent *= INDENT;
  1087.     for (count = 0; (count < (indent - INDENT)) && (count < (PRINTBUF - 1)); count++) {
  1088.     if ((count % INDENT) == 0) {
  1089.         buffer[count] = '|';
  1090.     } else {
  1091.         buffer[count] = ' ';
  1092.     }
  1093.     }
  1094.     buffer[count] = EOS;
  1095.     (VOID) fprintf (_db_fp_, buffer);
  1096.     (VOID) fflush (_db_fp_);
  1097. }
  1098.  
  1099.  
  1100. /*
  1101.  *  FUNCTION
  1102.  *
  1103.  *    FreeList    free all memory associated with a linked list
  1104.  *
  1105.  *  SYNOPSIS
  1106.  *
  1107.  *    LOCAL VOID FreeList (linkp)
  1108.  *    struct link *linkp;
  1109.  *
  1110.  *  DESCRIPTION
  1111.  *
  1112.  *    Given pointer to the head of a linked list, frees all
  1113.  *    memory held by the list and the members of the list.
  1114.  *
  1115.  */
  1116.  
  1117. LOCAL VOID FreeList (linkp)
  1118. struct link *linkp;
  1119. {
  1120.     REGISTER struct link *old;
  1121.  
  1122.     while (linkp != NULL) {
  1123.     old = linkp;
  1124.     linkp = linkp -> next_link;
  1125.     if (old -> string != NULL) {
  1126.         free (old -> string);
  1127.     }
  1128.     free ((char *) old);
  1129.     }
  1130. }
  1131.  
  1132.  
  1133. /*
  1134.  *  FUNCTION
  1135.  *
  1136.  *    StrDup   make a duplicate of a string in new memory
  1137.  *
  1138.  *  SYNOPSIS
  1139.  *
  1140.  *    LOCAL char *StrDup (string)
  1141.  *    char *string;
  1142.  *
  1143.  *  DESCRIPTION
  1144.  *
  1145.  *    Given pointer to a string, allocates sufficient memory to make
  1146.  *    a duplicate copy, and copies the string to the newly allocated
  1147.  *    memory.  Failure to allocated sufficient memory is immediately
  1148.  *    fatal.
  1149.  *
  1150.  */
  1151.  
  1152.  
  1153. LOCAL char *StrDup (string)
  1154. char *string;
  1155. {
  1156.     REGISTER char *new;
  1157.  
  1158.     new = (char *) DbugMalloc (strlen (string) + 1);
  1159.     (VOID) strcpy (new, string);
  1160.     return (new);
  1161. }
  1162.  
  1163.  
  1164. /*
  1165.  *  FUNCTION
  1166.  *
  1167.  *    DoPrefix    print debugger line prefix prior to indentation
  1168.  *
  1169.  *  SYNOPSIS
  1170.  *
  1171.  *    LOCAL VOID DoPrefix (_line_)
  1172.  *    int _line_;
  1173.  *
  1174.  *  DESCRIPTION
  1175.  *
  1176.  *    Print prefix common to all debugger output lines, prior to
  1177.  *    doing indentation if necessary.  Print such information as
  1178.  *    current process name, current source file name and line number,
  1179.  *    and current function nesting depth.
  1180.  *
  1181.  */
  1182.   
  1183.  
  1184. LOCAL VOID DoPrefix (_line_)
  1185. int _line_;
  1186. {
  1187. #if (unix || xenix)
  1188.     extern int getpid ();
  1189. #endif
  1190.  
  1191.     lineno++;
  1192. #if (unix || xenix)
  1193.     if (stack -> flags & PID_ON) {
  1194.     (VOID) fprintf (_db_fp_, "%5d: ", getpid ());
  1195.     }
  1196. #endif
  1197.     if (stack -> flags & NUMBER_ON) {
  1198.     (VOID) fprintf (_db_fp_, "%5d: ", lineno);
  1199.     }
  1200.     if (stack -> flags & PROCESS_ON) {
  1201.     (VOID) fprintf (_db_fp_, "%s: ", _db_process_);
  1202.     }
  1203.     if (stack -> flags & FILE_ON) {
  1204.     (VOID) fprintf (_db_fp_, "%14s: ", file);
  1205.     }
  1206.     if (stack -> flags & LINE_ON) {
  1207.     (VOID) fprintf (_db_fp_, "%5d: ", _line_);
  1208.     }
  1209.     if (stack -> flags & DEPTH_ON) {
  1210.     (VOID) fprintf (_db_fp_, "%4d: ", stack -> level);
  1211.     }
  1212.     (VOID) fflush (_db_fp_);
  1213. }
  1214.  
  1215.  
  1216. /*
  1217.  *  FUNCTION
  1218.  *
  1219.  *    OpenFile    open new output stream for debugger output
  1220.  *
  1221.  *  SYNOPSIS
  1222.  *
  1223.  *    LOCAL VOID OpenFile (name)
  1224.  *    char *name;
  1225.  *
  1226.  *  DESCRIPTION
  1227.  *
  1228.  *    Given name of a new file (or "-" for stdout) opens the file
  1229.  *    and sets the output stream to the new file.
  1230.  *
  1231.  */
  1232.  
  1233. LOCAL VOID OpenFile (name)
  1234. char *name;
  1235. {
  1236.     REGISTER FILE *fp;
  1237.     REGISTER BOOLEAN newfile;
  1238.  
  1239.     if (name != NULL) {
  1240.     if (strcmp (name, "-") == 0) {
  1241.         _db_fp_ = stdout;
  1242.         stack -> out_file = _db_fp_;
  1243.     } else {
  1244.         if (!Writable (name)) {
  1245.         (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1246.         perror ("");
  1247.         (VOID) fflush (_db_fp_);
  1248.         (VOID) Delay (stack -> delay);
  1249.         } else {
  1250.         if (EXISTS (name)) {
  1251.             newfile = FALSE;
  1252.         } else {
  1253.             newfile = TRUE;
  1254.         }
  1255.         fp = fopen (name, "a");
  1256.         if (fp == NULL) {
  1257.              (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1258.             perror ("");
  1259.             (VOID) fflush (_db_fp_);
  1260.             (VOID) Delay (stack -> delay);
  1261.         } else {
  1262.             _db_fp_ = fp;
  1263.             stack -> out_file = fp;
  1264.             if (newfile) {
  1265.             ChangeOwner (name);
  1266.             }
  1267.         }
  1268.         }
  1269.     }
  1270.     }
  1271. }
  1272.  
  1273.  
  1274. /*
  1275.  *  FUNCTION
  1276.  *
  1277.  *    OpenProfile    open new output stream for profiler output
  1278.  *
  1279.  *  SYNOPSIS
  1280.  *
  1281.  *    LOCAL VOID OpenProfile (name)
  1282.  *    char *name;
  1283.  *
  1284.  *  DESCRIPTION
  1285.  *
  1286.  *    Given name of a new file, opens the file
  1287.  *    and sets the profiler output stream to the new file.
  1288.  *
  1289.  *    It is currently unclear whether the prefered behavior is
  1290.  *    to truncate any existing file, or simply append to it.
  1291.  *    The latter behavior would be desirable for collecting
  1292.  *    accumulated runtime history over a number of separate
  1293.  *    runs.  It might take some changes to the analyzer program
  1294.  *    though, and the notes that Binayak sent with the profiling
  1295.  *    diffs indicated that append was the normal mode, but this
  1296.  *    does not appear to agree with the actual code. I haven't
  1297.  *    investigated at this time [fnf; 24-Jul-87].
  1298.  */
  1299.  
  1300. LOCAL VOID OpenProfile (name)
  1301. char *name;
  1302. {
  1303.     REGISTER FILE *fp;
  1304.     REGISTER BOOLEAN newfile;
  1305.  
  1306.     if (name != NULL) {
  1307.     if (!Writable (name)) {
  1308.         (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1309.         perror ("");
  1310.         (VOID) fflush (_db_fp_);
  1311.         (VOID) Delay (stack -> delay);
  1312.     } else {
  1313.         if (EXISTS (name)) {
  1314.         newfile = FALSE;
  1315.         } else {
  1316.         newfile = TRUE;
  1317.         }
  1318.         fp = fopen (name, "w");
  1319.         if (fp == NULL) {
  1320.         (VOID) fprintf (_db_fp_, ERR_OPEN, _db_process_, name);
  1321.         perror ("");
  1322.         (VOID) fflush (_db_fp_);
  1323.         (VOID) Delay (stack -> delay);
  1324.         } else {
  1325.         _db_pfp_ = fp;
  1326.         stack -> prof_file = fp;
  1327.         if (newfile) {
  1328.             ChangeOwner (name);
  1329.         }
  1330.         }
  1331.     }
  1332.     }
  1333. }
  1334.  
  1335.  
  1336. /*
  1337.  *  FUNCTION
  1338.  *
  1339.  *    CloseFile    close the debug output stream
  1340.  *
  1341.  *  SYNOPSIS
  1342.  *
  1343.  *    LOCAL VOID CloseFile (fp)
  1344.  *    FILE *fp;
  1345.  *
  1346.  *  DESCRIPTION
  1347.  *
  1348.  *    Closes the debug output stream unless it is standard output
  1349.  *    or standard error.
  1350.  *
  1351.  */
  1352.  
  1353. LOCAL VOID CloseFile (fp)
  1354. FILE *fp;
  1355. {
  1356.     if (fp != stderr && fp != stdout) {
  1357.     if (fclose (fp) == EOF) {
  1358.         (VOID) fprintf (stderr, ERR_CLOSE, _db_process_);
  1359.         perror ("");
  1360.         (VOID) fflush (stderr);
  1361.         (VOID) Delay (stack -> delay);
  1362.     }
  1363.     }
  1364. }
  1365.  
  1366.  
  1367. /*
  1368.  *  FUNCTION
  1369.  *
  1370.  *    DbugExit    print error message and exit
  1371.  *
  1372.  *  SYNOPSIS
  1373.  *
  1374.  *    LOCAL VOID DbugExit (why)
  1375.  *    char *why;
  1376.  *
  1377.  *  DESCRIPTION
  1378.  *
  1379.  *    Prints error message using current process name, the reason for
  1380.  *    aborting (typically out of memory), and exits with status 1.
  1381.  *    This should probably be changed to use a status code
  1382.  *    defined in the user's debugger include file.
  1383.  *
  1384.  */
  1385.  
  1386. LOCAL VOID DbugExit (why)
  1387. char *why;
  1388. {
  1389.     (VOID) fprintf (stderr, ERR_ABORT, _db_process_, why);
  1390.     (VOID) fflush (stderr);
  1391.     (VOID) Delay (stack -> delay);
  1392.     exit (1);
  1393. }
  1394.  
  1395.  
  1396. /*
  1397.  *  FUNCTION
  1398.  *
  1399.  *    DbugMalloc    allocate memory for debugger runtime support
  1400.  *
  1401.  *  SYNOPSIS
  1402.  *
  1403.  *    LOCAL long *DbugMalloc (size)
  1404.  *    int size;
  1405.  *
  1406.  *  DESCRIPTION
  1407.  *
  1408.  *    Allocate more memory for debugger runtime support functions.
  1409.  *    Failure to to allocate the requested number of bytes is
  1410.  *    immediately fatal to the current process.  This may be
  1411.  *    rather unfriendly behavior.  It might be better to simply
  1412.  *    print a warning message, freeze the current debugger state,
  1413.  *    and continue execution.
  1414.  *
  1415.  */
  1416.  
  1417. LOCAL long *DbugMalloc (size)
  1418. int size;
  1419. {
  1420.     register long *new;
  1421.  
  1422.     new = (long *) malloc ((unsigned int) size);
  1423.     if (new == NULL) {
  1424.     DbugExit ("out of memory");
  1425.     }
  1426.     return (new);
  1427. }
  1428.  
  1429.  
  1430. /*
  1431.  *    This function may be eliminated when strtok is available
  1432.  *    in the runtime environment (missing from BSD4.1).
  1433.  */
  1434.  
  1435. LOCAL char *strtok (s1, s2)
  1436. char *s1, *s2;
  1437. {
  1438.     static char *end = NULL;
  1439.     REGISTER char *rtnval;
  1440.  
  1441.     rtnval = NULL;
  1442.     if (s2 != NULL) {
  1443.     if (s1 != NULL) {
  1444.         end = s1;
  1445.         rtnval = strtok ((char *) NULL, s2);
  1446.     } else if (end != NULL) {
  1447.         if (*end != EOS) {
  1448.         rtnval = end;
  1449.         while (*end != *s2 && *end != EOS) {end++;}
  1450.         if (*end != EOS) {
  1451.             *end++ = EOS;
  1452.         }
  1453.         }
  1454.     }
  1455.     }
  1456.     return (rtnval);
  1457. }
  1458.  
  1459.  
  1460. /*
  1461.  *  FUNCTION
  1462.  *
  1463.  *    BaseName    strip leading pathname components from name
  1464.  *
  1465.  *  SYNOPSIS
  1466.  *
  1467.  *    LOCAL char *BaseName (pathname)
  1468.  *    char *pathname;
  1469.  *
  1470.  *  DESCRIPTION
  1471.  *
  1472.  *    Given pointer to a complete pathname, locates the base file
  1473.  *    name at the end of the pathname and returns a pointer to
  1474.  *    it.
  1475.  *
  1476.  */
  1477.  
  1478. LOCAL char *BaseName (pathname)
  1479. char *pathname;
  1480. {
  1481.     register char *base;
  1482.  
  1483.     base = strrchr (pathname, '/');
  1484.     if (base++ == NULL) {
  1485.     base = pathname;
  1486.     }
  1487.     return (base);
  1488. }
  1489.  
  1490.  
  1491. /*
  1492.  *  FUNCTION
  1493.  *
  1494.  *    Writable    test to see if a pathname is writable/creatable
  1495.  *
  1496.  *  SYNOPSIS
  1497.  *
  1498.  *    LOCAL BOOLEAN Writable (pathname)
  1499.  *    char *pathname;
  1500.  *
  1501.  *  DESCRIPTION
  1502.  *
  1503.  *    Because the debugger might be linked in with a program that
  1504.  *    runs with the set-uid-bit (suid) set, we have to be careful
  1505.  *    about opening a user named file for debug output.  This consists
  1506.  *    of checking the file for write access with the real user id,
  1507.  *    or checking the directory where the file will be created.
  1508.  *
  1509.  *    Returns TRUE if the user would normally be allowed write or
  1510.  *    create access to the named file.  Returns FALSE otherwise.
  1511.  *
  1512.  */
  1513.  
  1514. LOCAL BOOLEAN Writable (pathname)
  1515. char *pathname;
  1516. {
  1517.     REGISTER BOOLEAN granted;
  1518. #if (unix || xenix)
  1519.     REGISTER char *lastslash;
  1520. #endif
  1521.  
  1522. #if (!unix && !xenix)
  1523.     granted = TRUE;
  1524. #else
  1525.     granted = FALSE;
  1526.     if (EXISTS (pathname)) {
  1527.     if (WRITABLE (pathname)) {
  1528.         granted = TRUE;
  1529.     }
  1530.     } else {
  1531.     lastslash = strrchr (pathname, '/');
  1532.     if (lastslash != NULL) {
  1533.         *lastslash = EOS;
  1534.     } else {
  1535.         pathname = ".";
  1536.     }
  1537.     if (WRITABLE (pathname)) {
  1538.         granted = TRUE;
  1539.     }
  1540.     if (lastslash != NULL) {
  1541.         *lastslash = '/';
  1542.     }
  1543.     }
  1544. #endif
  1545.     return (granted);
  1546. }
  1547.  
  1548.  
  1549. /*
  1550.  *    This function may be eliminated when strrchr is available
  1551.  *    in the runtime environment (missing from BSD4.1).
  1552.  *    Alternately, you can use rindex() on BSD systems.
  1553.  */
  1554.  
  1555. LOCAL char *strrchr (s, c)
  1556. char *s;
  1557. char c;
  1558. {
  1559.     REGISTER char *scan;
  1560.  
  1561.     for (scan = s; *scan != EOS; scan++) {;}
  1562.     while (scan > s && *--scan != c) {;}
  1563.     if (*scan != c) {
  1564.     scan = NULL;
  1565.     }
  1566.     return (scan);
  1567. }
  1568.  
  1569.  
  1570. /*
  1571.  *  FUNCTION
  1572.  *
  1573.  *    ChangeOwner    change owner to real user for suid programs
  1574.  *
  1575.  *  SYNOPSIS
  1576.  *
  1577.  *    LOCAL VOID ChangeOwner (pathname)
  1578.  *
  1579.  *  DESCRIPTION
  1580.  *
  1581.  *    For unix systems, change the owner of the newly created debug
  1582.  *    file to the real owner.  This is strictly for the benefit of
  1583.  *    programs that are running with the set-user-id bit set.
  1584.  *
  1585.  *    Note that at this point, the fact that pathname represents
  1586.  *    a newly created file has already been established.  If the
  1587.  *    program that the debugger is linked to is not running with
  1588.  *    the suid bit set, then this operation is redundant (but
  1589.  *    harmless).
  1590.  *
  1591.  */
  1592.  
  1593. LOCAL VOID ChangeOwner (pathname)
  1594. char *pathname;
  1595. {
  1596. #if (unix || xenix)
  1597.     if (chown (pathname, getuid (), getgid ()) == -1) {
  1598.     (VOID) fprintf (stderr, ERR_CHOWN, _db_process_, pathname);
  1599.     perror ("");
  1600.     (VOID) fflush (stderr);
  1601.     (VOID) Delay (stack -> delay);
  1602.     }
  1603. #endif
  1604. }
  1605.  
  1606.  
  1607. /*
  1608.  *  FUNCTION
  1609.  *
  1610.  *    _db_setjmp_    save debugger environment
  1611.  *
  1612.  *  SYNOPSIS
  1613.  *
  1614.  *    VOID _db_setjmp_ ()
  1615.  *
  1616.  *  DESCRIPTION
  1617.  *
  1618.  *    Invoked as part of the user's DBUG_SETJMP macro to save
  1619.  *    the debugger environment in parallel with saving the user's
  1620.  *    environment.
  1621.  *
  1622.  */
  1623.  
  1624. VOID _db_setjmp_ ()
  1625. {
  1626.    jmplevel = stack -> level;
  1627.    jmpfunc = func;
  1628.    jmpfile = file;
  1629. }
  1630.  
  1631.  
  1632. /*
  1633.  *  FUNCTION
  1634.  *
  1635.  *    _db_longjmp_    restore previously saved debugger environment
  1636.  *
  1637.  *  SYNOPSIS
  1638.  *
  1639.  *    VOID _db_longjmp_ ()
  1640.  *
  1641.  *  DESCRIPTION
  1642.  *
  1643.  *    Invoked as part of the user's DBUG_LONGJMP macro to restore
  1644.  *    the debugger environment in parallel with restoring the user's
  1645.  *    previously saved environment.
  1646.  *
  1647.  */
  1648.  
  1649. VOID _db_longjmp_ ()
  1650. {
  1651.     stack -> level = jmplevel;
  1652.     if (jmpfunc) {
  1653.     func = jmpfunc;
  1654.     }
  1655.     if (jmpfile) {
  1656.     file = jmpfile;
  1657.     }
  1658. }
  1659.  
  1660.  
  1661. /*
  1662.  *  FUNCTION
  1663.  *
  1664.  *    DelayArg   convert D flag argument to appropriate value
  1665.  *
  1666.  *  SYNOPSIS
  1667.  *
  1668.  *    LOCAL int DelayArg (value)
  1669.  *    int value;
  1670.  *
  1671.  *  DESCRIPTION
  1672.  *
  1673.  *    Converts delay argument, given in tenths of a second, to the
  1674.  *    appropriate numerical argument used by the system to delay
  1675.  *    that that many tenths of a second.  For example, on the
  1676.  *    amiga, there is a system call "Delay()" which takes an
  1677.  *    argument in ticks (50 per second).  On unix, the sleep
  1678.  *    command takes seconds.  Thus a value of "10", for one
  1679.  *    second of delay, gets converted to 50 on the amiga, and 1
  1680.  *    on unix.  Other systems will need to use a timing loop.
  1681.  *
  1682.  */
  1683.  
  1684. LOCAL int DelayArg (value)
  1685. int value;
  1686. {
  1687.     unsigned int delayarg = 0;
  1688.     
  1689. #if (unix || xenix)
  1690.     delayarg = value / 10;        /* Delay is in seconds for sleep () */
  1691. #endif
  1692. #if amiga
  1693.     delayarg = (HZ * value) / 10;    /* Delay in ticks for Delay () */
  1694. #endif
  1695.     return ((int) delayarg);
  1696. }
  1697.  
  1698.  
  1699. /*
  1700.  *    A dummy delay stub for systems that do not support delays.
  1701.  *    With a little work, this can be turned into a timing loop.
  1702.  */
  1703.  
  1704. #if (!unix && !xenix && !amiga)
  1705. Delay ()
  1706. {
  1707. }
  1708. #endif
  1709.  
  1710.  
  1711. /*
  1712.  *  FUNCTION
  1713.  *
  1714.  *    perror    perror simulation for systems that don't have it
  1715.  *
  1716.  *  SYNOPSIS
  1717.  *
  1718.  *    LOCAL VOID perror (s)
  1719.  *    char *s;
  1720.  *
  1721.  *  DESCRIPTION
  1722.  *
  1723.  *    Perror produces a message on the standard error stream which
  1724.  *    provides more information about the library or system error
  1725.  *    just encountered.  The argument string s is printed, followed
  1726.  *    by a ':', a blank, and then a message and a newline.
  1727.  *
  1728.  *    An undocumented feature of the unix perror is that if the string
  1729.  *    's' is a null string (NOT a NULL pointer!), then the ':' and
  1730.  *    blank are not printed.
  1731.  *
  1732.  *    This version just complains about an "unknown system error".
  1733.  *
  1734.  */
  1735.  
  1736. #if !unix && !xenix && !(amiga && LATTICE)
  1737.  
  1738. LOCAL VOID perror (s)
  1739. char *s;
  1740. {
  1741.     if (s && *s != EOS) {
  1742.     (VOID) fprintf (stderr, "%s: ", s);
  1743.     }
  1744.     (VOID) fprintf (stderr, "<unknown system error>\n");
  1745. }
  1746.  
  1747. #endif    /* !unix && !xenix && !(amiga && LATTICE) */
  1748.  
  1749. /*
  1750.  * Here we need the definitions of the clock routine.  Add your
  1751.  * own for whatever system that you have.
  1752.  */
  1753.  
  1754. #if (unix || xenix)
  1755.  
  1756. # include <sys/param.h>
  1757. # if BSD4_3 || sun
  1758.  
  1759. /*
  1760.  * Definition of the Clock() routine for 4.3 BSD.
  1761.  */
  1762.  
  1763. #include <sys/time.h>
  1764. #include <sys/resource.h>
  1765.  
  1766. /*
  1767.  * Returns the user time in milliseconds used by this process so
  1768.  * far.
  1769.  */
  1770.  
  1771. LOCAL unsigned long Clock ()
  1772. {
  1773.     struct rusage ru;
  1774.  
  1775.     (VOID) getrusage (RUSAGE_SELF, &ru);
  1776.     return ((ru.ru_utime.tv_sec * 1000) + (ru.ru_utime.tv_usec / 1000));
  1777. }
  1778.  
  1779. #else
  1780.  
  1781. LOCAL unsigned long Clock ()
  1782. {
  1783.     return (0);
  1784. }
  1785.  
  1786. # endif
  1787.  
  1788. #else
  1789.  
  1790. #if amiga
  1791.  
  1792. struct DateStamp {        /* Yes, this is a hack, but doing it right */
  1793.     long ds_Days;        /* is incredibly ugly without splitting this */
  1794.     long ds_Minute;        /* off into a separate file */
  1795.     long ds_Tick;
  1796. };
  1797.  
  1798. static int first_clock = TRUE;
  1799. static struct DateStamp begin;
  1800. static struct DateStamp elapsed;
  1801.  
  1802. LOCAL unsigned long Clock ()
  1803. {
  1804.     register struct DateStamp *now;
  1805.     register unsigned long millisec = 0;
  1806.     extern VOID *AllocMem ();
  1807.  
  1808.     now = (struct DateStamp *) AllocMem ((long) sizeof (struct DateStamp), 0L);
  1809.     if (now != NULL) {
  1810.     if (first_clock == TRUE) {
  1811.         first_clock = FALSE;
  1812.         (VOID) DateStamp (now);
  1813.         begin = *now;
  1814.     }
  1815.     (VOID) DateStamp (now);
  1816.     millisec = 24 * 3600 * (1000 / HZ) * (now -> ds_Days - begin.ds_Days);
  1817.     millisec += 60 * (1000 / HZ) * (now -> ds_Minute - begin.ds_Minute);
  1818.     millisec += (1000 / HZ) * (now -> ds_Tick - begin.ds_Tick);
  1819.     (VOID) FreeMem (now, (long) sizeof (struct DateStamp));
  1820.     }
  1821.     return (millisec);
  1822. }
  1823.  
  1824. #endif    /* amiga */
  1825. #endif    /* unix */
  1826.  
  1827. #if NO_VARARGS
  1828.  
  1829. /*
  1830.  *    Fake vfprintf for systems that don't support it.  If this 
  1831.  *    doesn't work, you are probably SOL...
  1832.  */
  1833.  
  1834. static int vfprintf (stream, format, ap)
  1835. FILE *stream;
  1836. char *format;
  1837. va_list ap;
  1838. {
  1839.     int rtnval;
  1840.     extern int fprintf ();
  1841.     ARGS_DCL;
  1842.  
  1843.     ARG0 =  va_arg (ap, ARGS_TYPE);
  1844.     ARG1 =  va_arg (ap, ARGS_TYPE);
  1845.     ARG2 =  va_arg (ap, ARGS_TYPE);
  1846.     ARG3 =  va_arg (ap, ARGS_TYPE);
  1847.     ARG4 =  va_arg (ap, ARGS_TYPE);
  1848.     ARG5 =  va_arg (ap, ARGS_TYPE);
  1849.     ARG6 =  va_arg (ap, ARGS_TYPE);
  1850.     ARG7 =  va_arg (ap, ARGS_TYPE);
  1851.     ARG8 =  va_arg (ap, ARGS_TYPE);
  1852.     ARG9 =  va_arg (ap, ARGS_TYPE);
  1853.     rtnval = fprintf (stream, format, ARGS_LIST);
  1854.     return (rtnval);
  1855. }
  1856.  
  1857. #endif    /* NO_VARARGS */
  1858.