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