home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 January / Chip_2001-01_cd1.bin / tema / mysql / mysql-3.23.28g-win-source.exe / dbug / dbug.c < prev    next >
C/C++ Source or Header  |  2000-08-31  |  50KB  |  2,077 lines

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