home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d0xx / d041 / dbug.lha / Dbug / dbug.c next >
Encoding:
C/C++ Source or Header  |  1986-11-27  |  36.4 KB  |  1,558 lines

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