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