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