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  *  FILE
32  *
33  *      dbug.c   runtime support routines for dbug package
34  *
35  *  SCCS
36  *
37  *      @(#)dbug.c      1.25    7/25/89
38  *
39  *  DESCRIPTION
40  *
41  *      These are the runtime support routines for the dbug package.
42  *      The dbug package has two main components; the user include
43  *      file containing various macro definitions, and the runtime
44  *      support routines which are called from the macro expansions.
45  *
46  *      Externally visible functions in the runtime support module
47  *      use the naming convention pattern "_db_xx...xx_", thus
48  *      they are unlikely to collide with user defined function names.
49  *
50  *  AUTHOR(S)
51  *
52  *      Fred Fish               (base code)
53  *      Enhanced Software Technologies, Tempe, AZ
54  *      asuvax!mcdphx!estinc!fnf
55  *
56  *      Michael Widenius:
57  *        DBUG_DUMP       - To dump a block of memory.
58  *        PUSH_FLAG "O"   - To be used insted of "o" if we
59  *                          want flushing after each write
60  *        PUSH_FLAG "A"   - as 'O', but we will append to the out file instead
61  *                          of creating a new one.
62  *        Check of malloc on entry/exit (option "S")
63  *
64  *      Sergei Golubchik:
65  *        DBUG_EXECUTE_IF
66  *        incremental mode (-#+t:-d,info ...)
67  *        DBUG_SET, _db_explain_
68  *        thread-local settings
69  *        negative lists (-#-d,info => everything but "info")
70  *
71  *        function/ syntax
72  *        (the logic is - think of a call stack as of a path.
73  *        "function" means only this function, "function/" means the hierarchy.
74  *        in the future, filters like function1/function2 could be supported.
75  *        following this logic glob(7) wildcards are supported.)
76  *
77  */
78 
79 /*
80   We can't have SAFE_MUTEX defined here as this will cause recursion
81   in pthread_mutex_lock
82 */
83 
84 #include <my_global.h>
85 #undef SAFE_MUTEX
86 #include <m_string.h>
87 #include <errno.h>
88 
89 #ifndef DBUG_OFF
90 
91 #ifdef HAVE_FNMATCH_H
92 #include <fnmatch.h>
93 #else
94 #define fnmatch(A,B,C) strcmp(A,B)
95 #endif
96 
97 #if defined(__WIN__)
98 #include <process.h>
99 #endif
100 
101 /*
102  *            Manifest constants which may be "tuned" if desired.
103  */
104 
105 #define PRINTBUF              1024    /* Print buffer size */
106 #define INDENT                2       /* Indentation per trace level */
107 #define MAXDEPTH              200     /* Maximum trace depth default */
108 
109 /*
110  *      The following flags are used to determine which
111  *      capabilities the user has enabled with the settings
112  *      push macro.
113  *
114  *      TRACE_ON is also used in _db_stack_frame_->level
115  *      (until we add flags to _db_stack_frame_, increasing it by 4 bytes)
116  */
117 
118 #define DEBUG_ON        (1U <<  1) /* Debug enabled */
119 #define FILE_ON         (1U <<  2) /* File name print enabled */
120 #define LINE_ON         (1U <<  3) /* Line number print enabled */
121 #define DEPTH_ON        (1U <<  4) /* Function nest level print enabled */
122 #define PROCESS_ON      (1U <<  5) /* Process name print enabled */
123 #define NUMBER_ON       (1U <<  6) /* Number each line of output */
124 #define PID_ON          (1U <<  8) /* Identify each line with process id */
125 #define TIMESTAMP_ON    (1U <<  9) /* timestamp every line of output */
126 #define FLUSH_ON_WRITE  (1U << 10) /* Flush on every write */
127 #define OPEN_APPEND     (1U << 11) /* Open for append      */
128 #define SANITY_CHECK_ON (1U << 12) /* Check memory on every DBUG_ENTER/RETURN */
129 #define TRACE_ON        (1U << 31) /* Trace enabled. MUST be the highest bit!*/
130 
131 #define TRACING (cs->stack->flags & TRACE_ON)
132 #define DEBUGGING (cs->stack->flags & DEBUG_ON)
133 
134 /*
135  *      Typedefs to make things more obvious.
136  */
137 
138 #define BOOLEAN my_bool
139 
140 /*
141  *      Externally supplied functions.
142  */
143 
144 #ifndef HAVE_PERROR
145 static void perror(char *s)
146 {
147   if (s && *s != '\0')
148     (void) fprintf(stderr, "%s: ", s);
149   (void) fprintf(stderr, "<unknown system error>\n");
150 }
151 #endif
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     struct link *next_link;   /* Pointer to the next link */
161     char   flags;
162     char   str[1];            /* Pointer to link's contents */
163 };
164 
165 /* flags for struct link and return flags of InList */
166 #define SUBDIR          1 /* this MUST be 1 */
167 #define INCLUDE         2
168 #define EXCLUDE         4
169 /* this is not a struct link flag, but only a return flags of InList */
170 #define MATCHED     65536
171 #define NOT_MATCHED     0
172 
173 /*
174  * Debugging settings can be shared between threads.
175  * But FILE* streams cannot normally be shared - what if
176  * one thread closes a stream, while another thread still uses it?
177  * As a workaround, we have shared FILE pointers with reference counters
178  */
179 typedef struct {
180   FILE *file;
181   uint used;
182 } sFILE;
183 
184 sFILE shared_stdout = { 0, 1 << 30 }, *sstdout = &shared_stdout;
185 sFILE shared_stderr = { 0, 1 << 30 }, *sstderr = &shared_stderr;
186 
187 /*
188  *      Debugging settings can be pushed or popped off of a
189  *      stack which is implemented as a linked list.  Note
190  *      that the head of the list is the current settings and the
191  *      stack is pushed by adding a new settings to the head of the
192  *      list or popped by removing the first link.
193  *
194  *      Note: if out_file is NULL, the other fields are not initialized at all!
195  */
196 
197 struct settings {
198   uint flags;                   /* Current settings flags               */
199   uint maxdepth;                /* Current maximum trace depth          */
200   uint delay;                   /* Delay after each output line         */
201   uint sub_level;               /* Sub this from code_state->level      */
202   sFILE *out_file;              /* Current output stream                */
203   char name[FN_REFLEN];         /* Name of output file                  */
204   struct link *functions;       /* List of functions                    */
205   struct link *keywords;        /* List of debug keywords               */
206   struct link *processes;       /* List of process names                */
207   struct settings *next;        /* Next settings in the list            */
208 };
209 
210 #define is_shared(S, V) ((S)->next && (S)->next->V == (S)->V)
211 
212 /*
213  *      Local variables not seen by user.
214  */
215 
216 
217 static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */
218 static struct settings init_settings;
219 static const char *db_process= 0;/* Pointer to process name; argv[0] */
220 my_bool _dbug_on_= TRUE;	 /* FALSE if no debugging at all */
221 
222 typedef struct _db_code_state_ {
223   const char *process;          /* Pointer to process name; usually argv[0] */
224   const char *func;             /* Name of current user function            */
225   const char *file;             /* Name of current user file                */
226   struct _db_stack_frame_ *framep; /* Pointer to current frame              */
227   struct settings *stack;       /* debugging settings                       */
228   int lineno;                   /* Current debugger output line number      */
229   uint level;                   /* Current function nesting level           */
230 
231 /*
232  *      The following variables are used to hold the state information
233  *      between the call to _db_pargs_() and _db_doprnt_(), during
234  *      expansion of the DBUG_PRINT macro.  This is the only macro
235  *      that currently uses these variables.
236  *
237  *      These variables are currently used only by _db_pargs_() and
238  *      _db_doprnt_().
239  */
240 
241   uint u_line;                  /* User source code line number */
242   int  locked;                  /* If locked with _db_lock_file_ */
243   const char *u_keyword;        /* Keyword for current macro */
244 } CODE_STATE;
245 
246 /*
247   The test below is so we could call functions with DBUG_ENTER before
248   my_thread_init().
249 */
250 #define get_code_state_if_not_set_or_return if (!cs && !((cs=code_state()))) return
251 #define get_code_state_or_return if (!((cs=code_state()))) return
252 
253         /* Handling lists */
254 #define ListAdd(A,B,C) ListAddDel(A,B,C,INCLUDE)
255 #define ListDel(A,B,C) ListAddDel(A,B,C,EXCLUDE)
256 static struct link *ListAddDel(struct link *, const char *, const char *, int);
257 static struct link *ListCopy(struct link *);
258 static int InList(struct link *linkp,const char *cp,int exact_match);
259 static uint ListFlags(struct link *linkp);
260 static void FreeList(struct link *linkp);
261 
262         /* OpenClose debug output stream */
263 static void DBUGOpenFile(CODE_STATE *,const char *, const char *, int);
264 static void DBUGCloseFile(CODE_STATE *cs, sFILE *new_value);
265         /* Push current debug settings */
266 static void PushState(CODE_STATE *cs);
267 	/* Free memory associated with debug state. */
268 static void FreeState (CODE_STATE *cs, int free_state);
269         /* Test for tracing enabled */
270 static int DoTrace(CODE_STATE *cs);
271 static int default_my_dbug_sanity(void);
272 
273 int (*dbug_sanity)(void)= default_my_dbug_sanity;
274 
275 
276 /*
277   return values of DoTrace.
278   Can also be used as bitmask: ret & DO_TRACE
279 */
280 #define DO_TRACE        1
281 #define DONT_TRACE      2
282 #define ENABLE_TRACE    3
283 #define DISABLE_TRACE   4
284 
285         /* Test to see if file is writable */
286 #if defined(HAVE_ACCESS)
287 static BOOLEAN Writable(const char *pathname);
288 #endif
289 
290 static void DoPrefix(CODE_STATE *cs, uint line);
291 
292 static char *DbugMalloc(size_t size);
293 static const char *BaseName(const char *pathname);
294 static void Indent(CODE_STATE *cs, int indent);
295 static void DbugFlush(CODE_STATE *);
296 static void DbugExit(const char *why);
297 static const char *DbugStrTok(const char *s);
298 static void DbugVfprintf(FILE *stream, const char* format, va_list args);
299 
300 /*
301  *      Miscellaneous printf format strings.
302  */
303 
304 #define ERR_MISSING_RETURN "missing DBUG_RETURN or DBUG_VOID_RETURN macro in function \"%s\"\n"
305 #define ERR_OPEN "%s: can't open debug output stream \"%s\": "
306 #define ERR_CLOSE "%s: can't close debug file: "
307 #define ERR_ABORT "%s: debugger aborting because %s\n"
308 
309 /*
310  *      Macros and defines for testing file accessibility under UNIX and MSDOS.
311  */
312 
313 #undef EXISTS
314 #if !defined(HAVE_ACCESS)
315 #define EXISTS(pathname) (FALSE)        /* Assume no existence */
316 #define Writable(name) (TRUE)
317 #else
318 #define EXISTS(pathname)         (access(pathname, F_OK) == 0)
319 #define WRITABLE(pathname)       (access(pathname, W_OK) == 0)
320 #endif
321 
322 /*
323 ** Macros to allow dbugging with threads
324 */
325 
326 #include <my_pthread.h>
327 /*
328 ** Protects writing to all file descriptors, init_settings.keywords
329 ** pointer and it's pointee - a linked list with keywords.
330 */
331 static pthread_mutex_t THR_LOCK_dbug;
332 
333 static void LockMutex(CODE_STATE *cs)
334 {
335   if (!cs->locked)
336     pthread_mutex_lock(&THR_LOCK_dbug);
337   cs->locked++;
338 }
339 static void UnlockMutex(CODE_STATE *cs)
340 {
341   --cs->locked;
342   assert(cs->locked >= 0);
343   if (cs->locked == 0)
344     pthread_mutex_unlock(&THR_LOCK_dbug);
345 }
346 static void LockIfInitSettings(CODE_STATE *cs)
347 {
348   if (cs->stack == &init_settings)
349     LockMutex(cs);
350 }
351 static void UnlockIfInitSettings(CODE_STATE *cs)
352 {
353   if (cs->stack == &init_settings)
354     UnlockMutex(cs);
355 }
356 
357 static CODE_STATE *code_state(void)
358 {
359   CODE_STATE *cs, **cs_ptr;
360 
361   /*
362     _dbug_on_ is reset if we don't plan to use any debug commands at all and
363     we want to run on maximum speed
364    */
365   if (!_dbug_on_)
366     return 0;
367 
368   if (!init_done)
369   {
370     init_done=TRUE;
371     sstdout->file= stdout;
372     sstderr->file= stderr;
373     pthread_mutex_init(&THR_LOCK_dbug, NULL);
374     bzero(&init_settings, sizeof(init_settings));
375     init_settings.out_file= sstderr;
376     init_settings.flags=OPEN_APPEND;
377   }
378 
379   if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug()))
380     return 0;                                   /* Thread not initialised */
381   if (!(cs= *cs_ptr))
382   {
383     cs=(CODE_STATE*) DbugMalloc(sizeof(*cs));
384     bzero((uchar*) cs,sizeof(*cs));
385     cs->process= db_process ? db_process : "dbug";
386     cs->func= "?func";
387     cs->file= "?file";
388     cs->stack=&init_settings;
389     *cs_ptr= cs;
390   }
391   return cs;
392 }
393 
394 void
395 dbug_swap_code_state(void **code_state_store)
396 {
397   CODE_STATE *cs, **cs_ptr;
398 
399   if (!(cs_ptr= (CODE_STATE**) my_thread_var_dbug()))
400     return;
401   cs= *cs_ptr;
402   *cs_ptr= *code_state_store;
403   *code_state_store= cs;
404 }
405 
406 void dbug_free_code_state(void **code_state_store)
407 {
408   if (*code_state_store)
409   {
410     free(*code_state_store);
411     *code_state_store= NULL;
412   }
413 }
414 
415 /*
416  *      Translate some calls among different systems.
417  */
418 
419 #ifdef HAVE_SLEEP
420 /* sleep() wants seconds */
421 #define Delay(A) sleep(((uint) A)/10)
422 #else
423 #define Delay(A) (0)
424 #endif
425 
426 /*
427  *  FUNCTION
428  *
429  *      _db_process_       give the name to the current process/thread
430  *
431  *  SYNOPSIS
432  *
433  *      VOID _process_(name)
434  *      char *name;
435  *
436  */
437 
438 void _db_process_(const char *name)
439 {
440   CODE_STATE *cs;
441 
442   if (!db_process)
443     db_process= name;
444 
445   get_code_state_or_return;
446   cs->process= name;
447 }
448 
449 /*
450  *  FUNCTION
451  *
452  *      DbugParse  parse control string and set current debugger settings
453  *
454  *  DESCRIPTION
455  *
456  *      Given pointer to a debug control string in "control",
457  *      parses the control string, and sets
458  *      up a current debug settings.
459  *
460  *      The debug control string is a sequence of colon separated fields
461  *      as follows:
462  *
463  *              [+]<field_1>:<field_2>:...:<field_N>
464  *
465  *      Each field consists of a mandatory flag character followed by
466  *      an optional "," and comma separated list of modifiers:
467  *
468  *              [sign]flag[,modifier,modifier,...,modifier]
469  *
470  *      See the manual for the list of supported signs, flags, and modifiers
471  *
472  *      For convenience, any leading "-#" is stripped off.
473  *
474  *  RETURN
475  *      1 - a list of functions ("f" flag) was possibly changed
476  *      0 - a list of functions was not changed
477  */
478 
479 static int DbugParse(CODE_STATE *cs, const char *control)
480 {
481   const char *end;
482   int rel, f_used=0;
483   struct settings *stack;
484 
485   stack= cs->stack;
486 
487   if (control[0] == '-' && control[1] == '#')
488     control+=2;
489 
490   rel= control[0] == '+' || control[0] == '-';
491   if ((!rel || (!stack->out_file && !stack->next)))
492   {
493     LockIfInitSettings(cs);
494     FreeState(cs, 0);
495     stack->flags= 0;
496     stack->delay= 0;
497     stack->maxdepth= 0;
498     stack->sub_level= 0;
499     stack->out_file= sstderr;
500     stack->functions= NULL;
501     stack->keywords= NULL;
502     stack->processes= NULL;
503     UnlockIfInitSettings(cs);
504   }
505   else if (!stack->out_file)
506   {
507     stack->flags= stack->next->flags;
508     stack->delay= stack->next->delay;
509     stack->maxdepth= stack->next->maxdepth;
510     stack->sub_level= stack->next->sub_level;
511     strcpy(stack->name, stack->next->name);
512     stack->out_file= stack->next->out_file;
513     stack->out_file->used++;
514     if (stack->next == &init_settings)
515     {
516       /* never share with the global parent - it can change under your feet */
517       stack->functions= ListCopy(init_settings.functions);
518       LockMutex(cs);
519       stack->keywords= ListCopy(init_settings.keywords);
520       UnlockMutex(cs);
521       stack->processes= ListCopy(init_settings.processes);
522     }
523     else
524     {
525       stack->functions= stack->next->functions;
526       stack->keywords= stack->next->keywords;
527       stack->processes= stack->next->processes;
528     }
529   }
530 
531   end= DbugStrTok(control);
532   while (control < end)
533   {
534     int c, sign= (*control == '+') ? 1 : (*control == '-') ? -1 : 0;
535     if (sign) control++;
536     c= *control++;
537     if (*control == ',')
538       control++;
539     /* XXX when adding new cases here, don't forget _db_explain_ ! */
540     switch (c) {
541     case 'd':
542       if (sign < 0 && control == end)
543       {
544         LockIfInitSettings(cs);
545         if (!is_shared(stack, keywords))
546           FreeList(stack->keywords);
547         stack->keywords=NULL;
548         UnlockIfInitSettings(cs);
549         stack->flags &= ~DEBUG_ON;
550         break;
551       }
552       LockIfInitSettings(cs);
553       if (rel && is_shared(stack, keywords))
554         stack->keywords= ListCopy(stack->keywords);
555       UnlockIfInitSettings(cs);
556       if (sign < 0)
557       {
558         if (DEBUGGING)
559         {
560           LockIfInitSettings(cs);
561           stack->keywords= ListDel(stack->keywords, control, end);
562           UnlockIfInitSettings(cs);
563         }
564         break;
565       }
566       LockIfInitSettings(cs);
567       stack->keywords= ListAdd(stack->keywords, control, end);
568       UnlockIfInitSettings(cs);
569       stack->flags |= DEBUG_ON;
570       break;
571     case 'D':
572       stack->delay= atoi(control);
573       break;
574     case 'f':
575       f_used= 1;
576       if (sign < 0 && control == end)
577       {
578         if (!is_shared(stack,functions))
579           FreeList(stack->functions);
580         stack->functions=NULL;
581         break;
582       }
583       if (rel && is_shared(stack,functions))
584         stack->functions= ListCopy(stack->functions);
585       if (sign < 0)
586         stack->functions= ListDel(stack->functions, control, end);
587       else
588         stack->functions= ListAdd(stack->functions, control, end);
589       break;
590     case 'F':
591       if (sign < 0)
592         stack->flags &= ~FILE_ON;
593       else
594         stack->flags |= FILE_ON;
595       break;
596     case 'i':
597       if (sign < 0)
598         stack->flags &= ~PID_ON;
599       else
600         stack->flags |= PID_ON;
601       break;
602     case 'L':
603       if (sign < 0)
604         stack->flags &= ~LINE_ON;
605       else
606         stack->flags |= LINE_ON;
607       break;
608     case 'n':
609       if (sign < 0)
610         stack->flags &= ~DEPTH_ON;
611       else
612         stack->flags |= DEPTH_ON;
613       break;
614     case 'N':
615       if (sign < 0)
616         stack->flags &= ~NUMBER_ON;
617       else
618         stack->flags |= NUMBER_ON;
619       break;
620     case 'A':
621     case 'O':
622       stack->flags |= FLUSH_ON_WRITE;
623       /* fall through */
624     case 'a':
625     case 'o':
626       if (sign < 0)
627       {
628         DBUGCloseFile(cs, sstderr);
629         stack->flags &= ~FLUSH_ON_WRITE;
630         break;
631       }
632       if (c == 'a' || c == 'A')
633         stack->flags |= OPEN_APPEND;
634       else
635         stack->flags &= ~OPEN_APPEND;
636       if (control != end)
637         DBUGOpenFile(cs, control, end, stack->flags & OPEN_APPEND);
638       else
639         DBUGOpenFile(cs, "-",0,0);
640       break;
641     case 'p':
642       if (sign < 0 && control == end)
643       {
644         if (!is_shared(stack,processes))
645           FreeList(stack->processes);
646         stack->processes=NULL;
647         break;
648       }
649       if (rel && is_shared(stack, processes))
650         stack->processes= ListCopy(stack->processes);
651       if (sign < 0)
652         stack->processes= ListDel(stack->processes, control, end);
653       else
654         stack->processes= ListAdd(stack->processes, control, end);
655       break;
656     case 'P':
657       if (sign < 0)
658         stack->flags &= ~PROCESS_ON;
659       else
660         stack->flags |= PROCESS_ON;
661       break;
662     case 'r':
663       stack->sub_level= cs->level;
664       break;
665     case 't':
666       if (sign < 0)
667       {
668         if (control != end)
669           stack->maxdepth-= atoi(control);
670         else
671           stack->maxdepth= 0;
672       }
673       else
674       {
675         if (control != end)
676           stack->maxdepth+= atoi(control);
677         else
678           stack->maxdepth= MAXDEPTH;
679       }
680       if (stack->maxdepth > 0)
681         stack->flags |= TRACE_ON;
682       else
683         stack->flags &= ~TRACE_ON;
684       break;
685     case 'T':
686       if (sign < 0)
687         stack->flags &= ~TIMESTAMP_ON;
688       else
689         stack->flags |= TIMESTAMP_ON;
690       break;
691     case 'S':
692       if (sign < 0)
693         stack->flags &= ~SANITY_CHECK_ON;
694       else
695         stack->flags |= SANITY_CHECK_ON;
696       break;
697     }
698     if (!*end)
699       break;
700     control=end+1;
701     end= DbugStrTok(control);
702   }
703   return !rel || f_used;
704 }
705 
706 #define framep_trace_flag(cs, frp) (frp ?                                    \
707                                      frp->level & TRACE_ON :                 \
708                               (ListFlags(cs->stack->functions) & INCLUDE) ?  \
709                                        0 : (uint)TRACE_ON)
710 
711 static void FixTraceFlags_helper(CODE_STATE *cs, const char *func,
712                                  struct _db_stack_frame_ *framep)
713 {
714   if (framep->prev)
715     FixTraceFlags_helper(cs, framep->func, framep->prev);
716 
717   cs->func= func;
718   cs->level= framep->level & ~TRACE_ON;
719   framep->level= cs->level | framep_trace_flag(cs, framep->prev);
720   /*
721     we don't set cs->framep correctly, even though DoTrace uses it.
722     It's ok, because cs->framep may only affect DO_TRACE/DONT_TRACE return
723     values, but we ignore them here anyway
724   */
725   switch(DoTrace(cs)) {
726   case ENABLE_TRACE:
727     framep->level|= TRACE_ON;
728     break;
729   case DISABLE_TRACE:
730     framep->level&= ~TRACE_ON;
731     break;
732   }
733 }
734 
735 #define fflags(cs) cs->stack->out_file ? ListFlags(cs->stack->functions) : TRACE_ON;
736 
737 static void FixTraceFlags(uint old_fflags, CODE_STATE *cs)
738 {
739   const char *func;
740   uint new_fflags, traceon, level;
741   struct _db_stack_frame_ *framep;
742 
743   /*
744     first (a.k.a. safety) check:
745     if we haven't started tracing yet, no call stack at all - we're safe.
746   */
747   framep=cs->framep;
748   if (framep == 0)
749     return;
750 
751   /*
752     Ok, the tracing has started, call stack isn't empty.
753 
754     second check: does the new list have a SUBDIR rule ?
755   */
756   new_fflags=fflags(cs);
757   if (new_fflags & SUBDIR)
758     goto yuck;
759 
760   /*
761     Ok, new list doesn't use SUBDIR.
762 
763     third check: we do NOT need to re-scan if
764     neither old nor new lists used SUBDIR flag and if a default behavior
765     (whether an unlisted function is traced) hasn't changed.
766     Default behavior depends on whether there're INCLUDE elements in the list.
767   */
768   if (!(old_fflags & SUBDIR) && !((new_fflags^old_fflags) & INCLUDE))
769     return;
770 
771   /*
772     Ok, old list may've used SUBDIR, or defaults could've changed.
773 
774     fourth check: are we inside a currently active SUBDIR rule ?
775     go up the call stack, if TRACE_ON flag ever changes its value - we are.
776   */
777   for (traceon=framep->level; framep; framep=framep->prev)
778     if ((traceon ^ framep->level) & TRACE_ON)
779       goto yuck;
780 
781   /*
782     Ok, TRACE_ON flag doesn't change in the call stack.
783 
784     fifth check: but is the top-most value equal to a default one ?
785   */
786   if (((traceon & TRACE_ON) != 0) == ((new_fflags & INCLUDE) == 0))
787     return;
788 
789 yuck:
790   /*
791     Yuck! function list was changed, and one of the currently active rules
792     was possibly affected. For example, a tracing could've been enabled or
793     disabled for a function somewhere up the call stack.
794     To react correctly, we must go up the call stack all the way to
795     the top and re-match rules to set TRACE_ON bit correctly.
796 
797     We must traverse the stack forwards, not backwards.
798     That's what a recursive helper is doing.
799     It'll destroy two CODE_STATE fields, save them now.
800   */
801   func= cs->func;
802   level= cs->level;
803   FixTraceFlags_helper(cs, func, cs->framep);
804   /* now we only need to restore CODE_STATE fields, and we're done */
805   cs->func= func;
806   cs->level= level;
807 }
808 
809 /*
810  *  FUNCTION
811  *
812  *      _db_set_       set current debugger settings
813  *
814  *  SYNOPSIS
815  *
816  *      VOID _db_set_(control)
817  *      char *control;
818  *
819  *  DESCRIPTION
820  *
821  *      Given pointer to a debug control string in "control",
822  *      parses the control string, and sets up a current debug
823  *      settings. Pushes a new debug settings if the current is
824  *      set to the initial debugger settings.
825  *
826  */
827 
828 void _db_set_(const char *control)
829 {
830   CODE_STATE *cs;
831   uint old_fflags;
832   get_code_state_or_return;
833   old_fflags=fflags(cs);
834   if (cs->stack == &init_settings)
835     PushState(cs);
836   if (DbugParse(cs, control))
837     FixTraceFlags(old_fflags, cs);
838 }
839 
840 /*
841  *  FUNCTION
842  *
843  *      _db_push_       push current debugger settings and set up new one
844  *
845  *  SYNOPSIS
846  *
847  *      VOID _db_push_(control)
848  *      char *control;
849  *
850  *  DESCRIPTION
851  *
852  *      Given pointer to a debug control string in "control", pushes
853  *      the current debug settings, parses the control string, and sets
854  *      up a new debug settings
855  *
856  */
857 
858 void _db_push_(const char *control)
859 {
860   CODE_STATE *cs;
861   uint old_fflags;
862   get_code_state_or_return;
863   old_fflags=fflags(cs);
864   PushState(cs);
865   if (DbugParse(cs, control))
866     FixTraceFlags(old_fflags, cs);
867 }
868 
869 
870 /**
871   Returns TRUE if session-local settings have been set.
872 */
873 
874 int _db_is_pushed_()
875 {
876   CODE_STATE *cs= NULL;
877   get_code_state_or_return FALSE;
878   return (cs->stack != &init_settings);
879 }
880 
881 /*
882  *  FUNCTION
883  *
884  *      _db_set_init_       set initial debugger settings
885  *
886  *  SYNOPSIS
887  *
888  *      VOID _db_set_init_(control)
889  *      char *control;
890  *
891  *  DESCRIPTION
892  *      see _db_set_
893  *
894  */
895 
896 void _db_set_init_(const char *control)
897 {
898   CODE_STATE tmp_cs;
899   bzero((uchar*) &tmp_cs, sizeof(tmp_cs));
900   tmp_cs.stack= &init_settings;
901   tmp_cs.process= db_process ? db_process : "dbug";
902   DbugParse(&tmp_cs, control);
903 }
904 
905 /*
906  *  FUNCTION
907  *
908  *      _db_pop_    pop the debug stack
909  *
910  *  DESCRIPTION
911  *
912  *      Pops the debug stack, returning the debug settings to its
913  *      condition prior to the most recent _db_push_ invocation.
914  *      Note that the pop will fail if it would remove the last
915  *      valid settings from the stack.  This prevents user errors
916  *      in the push/pop sequence from screwing up the debugger.
917  *      Maybe there should be some kind of warning printed if the
918  *      user tries to pop too many states.
919  *
920  */
921 
922 void _db_pop_()
923 {
924   uint old_fflags;
925   CODE_STATE *cs;
926 
927   get_code_state_or_return;
928 
929   if (cs->stack != &init_settings)
930   {
931     old_fflags=fflags(cs);
932     FreeState(cs, 1);
933     FixTraceFlags(old_fflags, cs);
934   }
935 }
936 
937 /*
938  *  FUNCTION
939  *
940  *      _db_explain_    generates 'control' string for the current settings
941  *
942  *  RETURN
943  *      0 - ok
944  *      1  - buffer too short, output truncated
945  *
946  */
947 
948 /* helper macros */
949 #define char_to_buf(C)    do {                  \
950         *buf++=(C);                             \
951         if (buf >= end) goto overflow;          \
952       } while (0)
953 #define str_to_buf(S)    do {                   \
954         char_to_buf(',');                       \
955         buf=strnmov(buf, (S), (uint) (end-buf)); \
956         if (buf >= end) goto overflow;          \
957       } while (0)
958 #define list_to_buf(l, f)  do {                 \
959         struct link *listp=(l);                 \
960         while (listp)                           \
961         {                                       \
962           if (listp->flags & (f))               \
963           {                                     \
964             str_to_buf(listp->str);             \
965             if (listp->flags & SUBDIR)          \
966               char_to_buf('/');                 \
967           }                                     \
968           listp=listp->next_link;               \
969         }                                       \
970       } while (0)
971 #define int_to_buf(i)  do {                     \
972         char b[50];                             \
973         int10_to_str((i), b, 10);               \
974         str_to_buf(b);                          \
975       } while (0)
976 #define colon_to_buf   do {                     \
977         if (buf != start) char_to_buf(':');     \
978       } while(0)
979 #define op_int_to_buf(C, val, def) do {         \
980         if ((val) != (def))                     \
981         {                                       \
982           colon_to_buf;                         \
983           char_to_buf((C));                     \
984           int_to_buf(val);                      \
985         }                                       \
986       } while (0)
987 #define op_intf_to_buf(C, val, def, cond) do {  \
988         if ((cond))                             \
989         {                                       \
990           colon_to_buf;                         \
991           char_to_buf((C));                     \
992           if ((val) != (def)) int_to_buf(val);  \
993         }                                       \
994       } while (0)
995 #define op_str_to_buf(C, val, cond) do {        \
996         if ((cond))                             \
997         {                                       \
998           char *s=(val);                        \
999           colon_to_buf;                         \
1000           char_to_buf((C));                     \
1001           if (*s) str_to_buf(s);                \
1002         }                                       \
1003       } while (0)
1004 #define op_list_to_buf(C, val, cond) do {       \
1005         if ((cond))                             \
1006         {                                       \
1007           int f=ListFlags(val);                 \
1008           colon_to_buf;                         \
1009           char_to_buf((C));                     \
1010           if (f & INCLUDE)                      \
1011             list_to_buf(val, INCLUDE);          \
1012           if (f & EXCLUDE)                      \
1013           {                                     \
1014             colon_to_buf;                       \
1015             char_to_buf('-');                   \
1016             char_to_buf((C));                   \
1017             list_to_buf(val, EXCLUDE);          \
1018           }                                     \
1019         }                                       \
1020       } while (0)
1021 #define op_bool_to_buf(C, cond) do {            \
1022         if ((cond))                             \
1023         {                                       \
1024           colon_to_buf;                         \
1025           char_to_buf((C));                     \
1026         }                                       \
1027       } while (0)
1028 
1029 int _db_explain_ (CODE_STATE *cs, char *buf, size_t len)
1030 {
1031   char *start=buf, *end=buf+len-4;
1032 
1033   get_code_state_if_not_set_or_return *buf=0;
1034 
1035   LockIfInitSettings(cs);
1036   op_list_to_buf('d', cs->stack->keywords, DEBUGGING);
1037   UnlockIfInitSettings(cs);
1038   op_int_to_buf ('D', cs->stack->delay, 0);
1039   op_list_to_buf('f', cs->stack->functions, cs->stack->functions);
1040   op_bool_to_buf('F', cs->stack->flags & FILE_ON);
1041   op_bool_to_buf('i', cs->stack->flags & PID_ON);
1042   op_bool_to_buf('L', cs->stack->flags & LINE_ON);
1043   op_bool_to_buf('n', cs->stack->flags & DEPTH_ON);
1044   op_bool_to_buf('N', cs->stack->flags & NUMBER_ON);
1045   op_str_to_buf(
1046     ((cs->stack->flags & FLUSH_ON_WRITE ? 0 : 32) |
1047      (cs->stack->flags & OPEN_APPEND ? 'A' : 'O')),
1048     cs->stack->name, cs->stack->out_file != sstderr);
1049   op_list_to_buf('p', cs->stack->processes, cs->stack->processes);
1050   op_bool_to_buf('P', cs->stack->flags & PROCESS_ON);
1051   op_bool_to_buf('r', cs->stack->sub_level != 0);
1052   op_intf_to_buf('t', cs->stack->maxdepth, MAXDEPTH, TRACING);
1053   op_bool_to_buf('T', cs->stack->flags & TIMESTAMP_ON);
1054   op_bool_to_buf('S', cs->stack->flags & SANITY_CHECK_ON);
1055 
1056   *buf= '\0';
1057   return 0;
1058 
1059 overflow:
1060   *end++= '.';
1061   *end++= '.';
1062   *end++= '.';
1063   *end=   '\0';
1064   return 1;
1065 }
1066 
1067 #undef char_to_buf
1068 #undef str_to_buf
1069 #undef list_to_buf
1070 #undef int_to_buf
1071 #undef colon_to_buf
1072 #undef op_int_to_buf
1073 #undef op_intf_to_buf
1074 #undef op_str_to_buf
1075 #undef op_list_to_buf
1076 #undef op_bool_to_buf
1077 
1078 /*
1079  *  FUNCTION
1080  *
1081  *      _db_explain_init_       explain initial debugger settings
1082  *
1083  *  DESCRIPTION
1084  *      see _db_explain_
1085  */
1086 
1087 int _db_explain_init_(char *buf, size_t len)
1088 {
1089   CODE_STATE cs;
1090   bzero((uchar*) &cs,sizeof(cs));
1091   cs.stack=&init_settings;
1092   return _db_explain_(&cs, buf, len);
1093 }
1094 
1095 /*
1096  *  FUNCTION
1097  *
1098  *      _db_enter_    process entry point to user function
1099  *
1100  *  SYNOPSIS
1101  *
1102  *      VOID _db_enter_(_func_, _file_, _line_, _stack_frame_)
1103  *      char *_func_;           points to current function name
1104  *      char *_file_;           points to current file name
1105  *      int _line_;             called from source line number
1106  *      struct _db_stack_frame_ allocated on the caller's stack
1107  *
1108  *  DESCRIPTION
1109  *
1110  *      Called at the beginning of each user function to tell
1111  *      the debugger that a new function has been entered.
1112  *      Note that the pointers to the previous user function
1113  *      name and previous user file name are stored on the
1114  *      caller's stack (this is why the ENTER macro must be
1115  *      the first "executable" code in a function, since it
1116  *      allocates these storage locations).  The previous nesting
1117  *      level is also stored on the callers stack for internal
1118  *      self consistency checks.
1119  *
1120  *      Also prints a trace line if tracing is enabled and
1121  *      increments the current function nesting depth.
1122  *
1123  *      Note that this mechanism allows the debugger to know
1124  *      what the current user function is at all times, without
1125  *      maintaining an internal stack for the function names.
1126  *
1127  */
1128 
1129 void _db_enter_(const char *_func_, const char *_file_,
1130                 uint _line_, struct _db_stack_frame_ *_stack_frame_)
1131 {
1132   CODE_STATE *cs;
1133   if (!((cs=code_state())))
1134   {
1135     _stack_frame_->level= 0; /* Set to avoid valgrind warnings if dbug is enabled later */
1136     _stack_frame_->prev= 0;
1137     return;
1138   }
1139 
1140   _stack_frame_->line= -1;
1141   _stack_frame_->func= cs->func;
1142   _stack_frame_->file= cs->file;
1143   cs->func=  _func_;
1144   cs->file=  _file_;
1145   _stack_frame_->prev= cs->framep;
1146   _stack_frame_->level= ++cs->level | framep_trace_flag(cs, cs->framep);
1147   cs->framep= _stack_frame_;
1148 
1149   switch (DoTrace(cs)) {
1150   case ENABLE_TRACE:
1151     cs->framep->level|= TRACE_ON;
1152     if (!TRACING) break;
1153     /* fall through */
1154   case DO_TRACE:
1155     if ((cs->stack->flags & SANITY_CHECK_ON) && (*dbug_sanity)())
1156       cs->stack->flags &= ~SANITY_CHECK_ON;
1157     if (TRACING)
1158     {
1159       int save_errno= errno;
1160       LockMutex(cs);
1161       DoPrefix(cs, _line_);
1162       Indent(cs, cs->level);
1163       (void) fprintf(cs->stack->out_file->file, ">%s\n", cs->func);
1164       UnlockMutex(cs);
1165       DbugFlush(cs);
1166       errno=save_errno;
1167     }
1168     break;
1169   case DISABLE_TRACE:
1170     cs->framep->level&= ~TRACE_ON;
1171     /* fall through */
1172   case DONT_TRACE:
1173     break;
1174   }
1175 }
1176 
1177 /*
1178  *  FUNCTION
1179  *
1180  *      _db_return_    process exit from user function
1181  *
1182  *  SYNOPSIS
1183  *
1184  *      VOID _db_return_(_line_, _stack_frame_)
1185  *      int _line_;             current source line number
1186  *      struct _db_stack_frame_ allocated on the caller's stack
1187  *
1188  *  DESCRIPTION
1189  *
1190  *      Called just before user function executes an explicit or implicit
1191  *      return.  Prints a trace line if trace is enabled, decrements
1192  *      the current nesting level, and restores the current function and
1193  *      file names from the defunct function's stack.
1194  *
1195  */
1196 
1197 void _db_return_(struct _db_stack_frame_ *_stack_frame_)
1198 {
1199   uint _slevel_= _stack_frame_->level & ~TRACE_ON;
1200   CODE_STATE *cs;
1201   get_code_state_or_return;
1202 
1203   if (_stack_frame_->line == 0)
1204     return;
1205 
1206   if (_stack_frame_->line == -1 || cs->framep != _stack_frame_)
1207   {
1208     char buf[512];
1209     my_snprintf(buf, sizeof(buf), ERR_MISSING_RETURN, cs->func);
1210     DbugExit(buf);
1211   }
1212 
1213   if (DoTrace(cs) & DO_TRACE)
1214   {
1215     if ((cs->stack->flags & SANITY_CHECK_ON) && (*dbug_sanity)())
1216       cs->stack->flags &= ~SANITY_CHECK_ON;
1217     if (TRACING)
1218     {
1219       int save_errno=errno;
1220       LockMutex(cs);
1221       DoPrefix(cs, _stack_frame_->line);
1222       Indent(cs, cs->level);
1223       (void) fprintf(cs->stack->out_file->file, "<%s\n", cs->func);
1224       UnlockMutex(cs);
1225       DbugFlush(cs);
1226       errno=save_errno;
1227     }
1228   }
1229   /*
1230     Check to not set level < 0. This can happen if DBUG was disabled when
1231     function was entered and enabled in function.
1232   */
1233   cs->level= _slevel_ != 0 ? _slevel_ - 1 : 0;
1234   cs->func= _stack_frame_->func;
1235   cs->file= _stack_frame_->file;
1236   if (cs->framep != NULL)
1237     cs->framep= cs->framep->prev;
1238 }
1239 
1240 
1241 /*
1242  *  FUNCTION
1243  *
1244  *      _db_pargs_    log arguments for subsequent use by _db_doprnt_()
1245  *
1246  *  SYNOPSIS
1247  *
1248  *      int _db_pargs_(_line_, keyword)
1249  *      int _line_;
1250  *      char *keyword;
1251  *
1252  *  DESCRIPTION
1253  *
1254  *      The new universal printing macro DBUG_PRINT, which replaces
1255  *      all forms of the DBUG_N macros, needs two calls to runtime
1256  *      support routines.  The first, this function, remembers arguments
1257  *      that are used by the subsequent call to _db_doprnt_().
1258  *
1259  */
1260 
1261 int _db_pargs_(uint _line_, const char *keyword)
1262 {
1263   CODE_STATE *cs;
1264   get_code_state_or_return 0;
1265   cs->u_line= _line_;
1266   cs->u_keyword= keyword;
1267 
1268   return DEBUGGING && _db_keyword_(cs, cs->u_keyword, 0);
1269 }
1270 
1271 
1272 /*
1273  *  FUNCTION
1274  *
1275  *      _db_doprnt_    handle print of debug lines
1276  *
1277  *  SYNOPSIS
1278  *
1279  *      VOID _db_doprnt_(format, va_alist)
1280  *      char *format;
1281  *      va_dcl;
1282  *
1283  *  DESCRIPTION
1284  *
1285  *      When invoked via one of the DBUG macros, tests the current keyword
1286  *      set by calling _db_pargs_() to see if that macro has been selected
1287  *      for processing via the debugger control string, and if so, handles
1288  *      printing of the arguments via the format string.  The line number
1289  *      of the DBUG macro in the source is found in u_line.
1290  *
1291  *      Note that the format string SHOULD NOT include a terminating
1292  *      newline, this is supplied automatically.
1293  *
1294  */
1295 
1296 #include <stdarg.h>
1297 
1298 void _db_doprnt_(const char *format,...)
1299 {
1300   va_list args;
1301   CODE_STATE *cs;
1302   int save_errno;
1303 
1304   get_code_state_or_return;
1305 
1306   va_start(args,format);
1307 
1308   save_errno=errno;
1309   LockMutex(cs);
1310   DoPrefix(cs, cs->u_line);
1311   if (TRACING)
1312     Indent(cs, cs->level + 1);
1313   else
1314     (void) fprintf(cs->stack->out_file->file, "%s: ", cs->func);
1315   (void) fprintf(cs->stack->out_file->file, "%s: ", cs->u_keyword);
1316   DbugVfprintf(cs->stack->out_file->file, format, args);
1317   UnlockMutex(cs);
1318   DbugFlush(cs);
1319   errno=save_errno;
1320 
1321   va_end(args);
1322 }
1323 
1324 /*
1325  * This function is intended as a
1326  * vfprintf clone with consistent, platform independent output for
1327  * problematic formats like %p, %zd and %lld.
1328  */
1329 static void DbugVfprintf(FILE *stream, const char* format, va_list args)
1330 {
1331   char cvtbuf[1024];
1332   (void) my_vsnprintf(cvtbuf, sizeof(cvtbuf), format, args);
1333   (void) fprintf(stream, "%s\n", cvtbuf);
1334 }
1335 
1336 
1337 /*
1338  *  FUNCTION
1339  *
1340  *            _db_dump_    dump a string in hex
1341  *
1342  *  SYNOPSIS
1343  *
1344  *            void _db_dump_(_line_,keyword,memory,length)
1345  *            int _line_;               current source line number
1346  *            char *keyword;
1347  *            char *memory;             Memory to print
1348  *            int length;               Bytes to print
1349  *
1350  *  DESCRIPTION
1351  *  Dump N characters in a binary array.
1352  *  Is used to examine corrupted memory or arrays.
1353  */
1354 
1355 void _db_dump_(uint _line_, const char *keyword,
1356                const unsigned char *memory, size_t length)
1357 {
1358   int pos;
1359   CODE_STATE *cs;
1360   get_code_state_or_return;
1361 
1362   if (_db_keyword_(cs, keyword, 0))
1363   {
1364     LockMutex(cs);
1365     DoPrefix(cs, _line_);
1366     if (TRACING)
1367     {
1368       Indent(cs, cs->level + 1);
1369       pos= MY_MIN(MY_MAX(cs->level-cs->stack->sub_level,0)*INDENT,80);
1370     }
1371     else
1372     {
1373       fprintf(cs->stack->out_file->file, "%s: ", cs->func);
1374     }
1375     (void) fprintf(cs->stack->out_file->file, "%s: Memory: %p  Bytes: (%ld)\n",
1376             keyword, memory, (long) length);
1377 
1378     pos=0;
1379     while (length-- > 0)
1380     {
1381       uint tmp= *((unsigned char*) memory++);
1382       if ((pos+=3) >= 80)
1383       {
1384         fputc('\n',cs->stack->out_file->file);
1385         pos=3;
1386       }
1387       fputc(_dig_vec_upper[((tmp >> 4) & 15)], cs->stack->out_file->file);
1388       fputc(_dig_vec_upper[tmp & 15], cs->stack->out_file->file);
1389       fputc(' ',cs->stack->out_file->file);
1390     }
1391     (void) fputc('\n',cs->stack->out_file->file);
1392     UnlockMutex(cs);
1393     DbugFlush(cs);
1394   }
1395 }
1396 
1397 
1398 /*
1399  *  FUNCTION
1400  *
1401  *      ListAddDel    modify the list according to debug control string
1402  *
1403  *  DESCRIPTION
1404  *
1405  *      Given pointer to a comma separated list of strings in "cltp",
1406  *      parses the list, and modifies "listp", returning a pointer
1407  *      to the new list.
1408  *
1409  *      The mode of operation is defined by "todo" parameter.
1410  *
1411  *      If it is INCLUDE, elements (strings from "cltp") are added to the
1412  *      list, they will have INCLUDE flag set. If the list already contains
1413  *      the string in question, new element is not added, but a flag of
1414  *      the existing element is adjusted (INCLUDE bit is set, EXCLUDE bit
1415  *      is removed).
1416  *
1417  *      If it is EXCLUDE, elements are added to the list with the EXCLUDE
1418  *      flag set. If the list already contains the string in question,
1419  *      it is removed, new element is not added.
1420  */
1421 
1422 static struct link *ListAddDel(struct link *head, const char *ctlp,
1423                                const char *end, int todo)
1424 {
1425   const char *start;
1426   struct link **cur;
1427   size_t len;
1428   int subdir;
1429 
1430   ctlp--;
1431 next:
1432   while (++ctlp < end)
1433   {
1434     start= ctlp;
1435     subdir=0;
1436     while (ctlp < end && *ctlp != ',')
1437       ctlp++;
1438     len= (int) (ctlp-start);
1439     if (start[len-1] == '/')
1440     {
1441       len--;
1442       subdir=SUBDIR;
1443     }
1444     if (len == 0) continue;
1445     for (cur=&head; *cur; cur=&((*cur)->next_link))
1446     {
1447       if (!strncmp((*cur)->str, start, len))
1448       {
1449         if ((*cur)->flags & todo)  /* same action ? */
1450           (*cur)->flags|= subdir;  /* just merge the SUBDIR flag */
1451         else if (todo == EXCLUDE)
1452         {
1453           struct link *delme=*cur;
1454           *cur=(*cur)->next_link;
1455           free((void*) delme);
1456         }
1457         else
1458         {
1459           (*cur)->flags&=~(EXCLUDE & SUBDIR);
1460           (*cur)->flags|=INCLUDE | subdir;
1461         }
1462         goto next;
1463       }
1464     }
1465     *cur= (struct link *) DbugMalloc(sizeof(struct link)+len);
1466     memcpy((*cur)->str, start, len);
1467     (*cur)->str[len]=0;
1468     (*cur)->flags=todo | subdir;
1469     (*cur)->next_link=0;
1470   }
1471   return head;
1472 }
1473 
1474 /*
1475  *  FUNCTION
1476  *
1477  *      ListCopy    make a copy of the list
1478  *
1479  *  SYNOPSIS
1480  *
1481  *      static struct link *ListCopy(orig)
1482  *      struct link *orig;
1483  *
1484  *  DESCRIPTION
1485  *
1486  *      Given pointer to list, which contains a copy of every element from
1487  *      the original list.
1488  *
1489  *      the orig pointer can be NULL
1490  *
1491  *      Note that since each link is added at the head of the list,
1492  *      the final list will be in "reverse order", which is not
1493  *      significant for our usage here.
1494  *
1495  */
1496 
1497 static struct link *ListCopy(struct link *orig)
1498 {
1499   struct link *new_malloc;
1500   struct link *head;
1501   size_t len;
1502 
1503   head= NULL;
1504   while (orig != NULL)
1505   {
1506     len= strlen(orig->str);
1507     new_malloc= (struct link *) DbugMalloc(sizeof(struct link)+len);
1508     memcpy(new_malloc->str, orig->str, len);
1509     new_malloc->str[len]= 0;
1510     new_malloc->flags=orig->flags;
1511     new_malloc->next_link= head;
1512     head= new_malloc;
1513     orig= orig->next_link;
1514   }
1515   return head;
1516 }
1517 
1518 /*
1519  *  FUNCTION
1520  *
1521  *      InList    test a given string for member of a given list
1522  *
1523  *  DESCRIPTION
1524  *
1525  *      Tests the string pointed to by "cp" to determine if it is in
1526  *      the list pointed to by "linkp".  Linkp points to the first
1527  *      link in the list.  If linkp is NULL or contains only EXCLUDE
1528  *      elements then the string is treated as if it is in the list.
1529  *      This may seem rather strange at first but leads to the desired
1530  *      operation if no list is given.  The net effect is that all
1531  *      strings will be accepted when there is no list, and when there
1532  *      is a list, only those strings in the list will be accepted.
1533  *
1534  *  RETURN
1535  *      combination of SUBDIR, INCLUDE, EXCLUDE, MATCHED flags
1536  *
1537  */
1538 
1539 static int InList(struct link *linkp, const char *cp, int exact_match)
1540 {
1541   int result;
1542   for (result=MATCHED; linkp != NULL; linkp= linkp->next_link)
1543   {
1544     if (!(exact_match ? strcmp(linkp->str,cp) : fnmatch(linkp->str, cp, 0)))
1545     {
1546       result= linkp->flags;
1547       break;
1548     }
1549     if (!(linkp->flags & EXCLUDE))
1550       result=NOT_MATCHED;
1551     if (linkp->flags & SUBDIR)
1552       result|=SUBDIR;
1553   }
1554   return result;
1555 }
1556 
1557 /*
1558  *  FUNCTION
1559  *
1560  *      ListFlags    returns aggregated list flags (ORed over all elements)
1561  *
1562  */
1563 
1564 static uint ListFlags(struct link *linkp)
1565 {
1566   uint f;
1567   for (f=0; linkp != NULL; linkp= linkp->next_link)
1568     f|= linkp->flags;
1569   return f;
1570 }
1571 
1572 /*
1573  *  FUNCTION
1574  *
1575  *      PushState    push current settings onto stack and set up new one
1576  *
1577  *  SYNOPSIS
1578  *
1579  *      static VOID PushState()
1580  *
1581  *  DESCRIPTION
1582  *
1583  *      Pushes the current settings on the settings stack, and creates
1584  *      a new settings. The new settings is NOT initialized
1585  *
1586  *      The settings stack is a linked list of settings, with the new
1587  *      settings added at the head.  This allows the stack to grow
1588  *      to the limits of memory if necessary.
1589  *
1590  */
1591 
1592 static void PushState(CODE_STATE *cs)
1593 {
1594   struct settings *new_malloc;
1595 
1596   new_malloc= (struct settings *) DbugMalloc(sizeof(struct settings));
1597   bzero(new_malloc, sizeof(*new_malloc));
1598   new_malloc->next= cs->stack;
1599   cs->stack= new_malloc;
1600 }
1601 
1602 /*
1603  *  FUNCTION
1604  *
1605  *	FreeState    Free memory associated with a struct state.
1606  *
1607  *  SYNOPSIS
1608  *
1609  *	static void FreeState (state)
1610  *	struct state *state;
1611  *      int free_state;
1612  *
1613  *  DESCRIPTION
1614  *
1615  *	Deallocates the memory allocated for various information in a
1616  *	state. If free_state is set, also free 'state'
1617  *
1618  */
1619 static void FreeState(CODE_STATE *cs, int free_state)
1620 {
1621   struct settings *state= cs->stack;
1622   LockIfInitSettings(cs);
1623   if (!is_shared(state, keywords))
1624   {
1625     FreeList(state->keywords);
1626     state->keywords= NULL;
1627   }
1628   UnlockIfInitSettings(cs);
1629   if (!is_shared(state, functions))
1630     FreeList(state->functions);
1631   if (!is_shared(state, processes))
1632     FreeList(state->processes);
1633 
1634   DBUGCloseFile(cs, NULL);
1635 
1636   if (free_state)
1637   {
1638     struct settings *next= state->next;
1639     free(state);
1640     cs->stack= next;
1641   }
1642 }
1643 
1644 
1645 /*
1646  *  FUNCTION
1647  *
1648  *	_db_end_    End debugging, freeing state stack memory.
1649  *
1650  *  SYNOPSIS
1651  *
1652  *	static VOID _db_end_ ()
1653  *
1654  *  DESCRIPTION
1655  *
1656  *	Ends debugging, de-allocating the memory allocated to the
1657  *	state stack.
1658  *
1659  *	To be called at the very end of the program.
1660  *
1661  */
1662 void _db_end_()
1663 {
1664   CODE_STATE *cs, dummy_cs;
1665   /*
1666     Set _dbug_on_ to be able to do full reset even when DEBUGGER_OFF was
1667     called after dbug was initialized
1668   */
1669   _dbug_on_= 1;
1670   cs= code_state();
1671 
1672   if (cs)
1673   {
1674     while (cs->stack && cs->stack != &init_settings)
1675       FreeState(cs, 1);
1676   }
1677   else
1678   {
1679     cs= &dummy_cs;
1680     bzero(cs, sizeof(*cs));
1681   }
1682 
1683   cs->stack= &init_settings;
1684   FreeState(cs, 0);
1685   pthread_mutex_destroy(&THR_LOCK_dbug);
1686   init_done= 0;
1687   _dbug_on_= 0;
1688 }
1689 
1690 
1691 /*
1692  *  FUNCTION
1693  *
1694  *      DoTrace    check to see if tracing is current enabled
1695  *
1696  *  DESCRIPTION
1697  *
1698  *      Checks to see if dbug in this function is enabled based on
1699  *      whether the maximum trace depth has been reached, the current
1700  *      function is selected, and the current process is selected.
1701  *
1702  */
1703 
1704 static int DoTrace(CODE_STATE *cs)
1705 {
1706   int res= DONT_TRACE;
1707   if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) &&
1708       InList(cs->stack->processes, cs->process, 0) & (MATCHED|INCLUDE))
1709   {
1710     switch(InList(cs->stack->functions, cs->func, 0)) {
1711     case INCLUDE|SUBDIR:
1712       res= ENABLE_TRACE;
1713       break;
1714     case INCLUDE:
1715       res= DO_TRACE;
1716       break;
1717     case MATCHED|SUBDIR:
1718     case NOT_MATCHED|SUBDIR:
1719     case MATCHED:
1720       res= (framep_trace_flag(cs, cs->framep) ? DO_TRACE : DONT_TRACE);
1721       break;
1722     case EXCLUDE:
1723     case NOT_MATCHED:
1724       res= DONT_TRACE;
1725       break;
1726     case EXCLUDE|SUBDIR:
1727       res= DISABLE_TRACE;
1728       break;
1729     }
1730   }
1731   return res;
1732 }
1733 
1734 
1735 FILE *_db_fp_(void)
1736 {
1737   CODE_STATE *cs;
1738   get_code_state_or_return NULL;
1739   return cs->stack->out_file->file;
1740 }
1741 
1742 /*
1743  *  FUNCTION
1744  *
1745  *      _db_keyword_    test keyword for member of keyword list
1746  *
1747  *  DESCRIPTION
1748  *
1749  *      Test a keyword to determine if it is in the currently active
1750  *      keyword list.  If strict=0, a keyword is accepted
1751  *      if the list is null, otherwise it must match one of the list
1752  *      members.  When debugging is not on, no keywords are accepted.
1753  *      After the maximum trace level is exceeded, no keywords are
1754  *      accepted (this behavior subject to change).  Additionally,
1755  *      the current function and process must be accepted based on
1756  *      their respective lists.
1757  *
1758  *      Returns TRUE if keyword accepted, FALSE otherwise.
1759  *
1760  */
1761 
1762 BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict)
1763 {
1764   int match= strict ? INCLUDE : INCLUDE|MATCHED;
1765   int res;
1766   get_code_state_if_not_set_or_return FALSE;
1767 
1768   if (!(DEBUGGING && (DoTrace(cs) & DO_TRACE)))
1769     return 0;
1770   LockIfInitSettings(cs);
1771   res= (InList(cs->stack->keywords, keyword, strict) & match);
1772   UnlockIfInitSettings(cs);
1773   return res != 0;
1774 }
1775 
1776 /*
1777  *  FUNCTION
1778  *
1779  *      Indent    indent a line to the given indentation level
1780  *
1781  *  SYNOPSIS
1782  *
1783  *      static VOID Indent(indent)
1784  *      int indent;
1785  *
1786  *  DESCRIPTION
1787  *
1788  *      Indent a line to the given level.  Note that this is
1789  *      a simple minded but portable implementation.
1790  *      There are better ways.
1791  *
1792  *      Also, the indent must be scaled by the compile time option
1793  *      of character positions per nesting level.
1794  *
1795  */
1796 
1797 static void Indent(CODE_STATE *cs, int indent)
1798 {
1799   int count;
1800 
1801   indent= MY_MAX(indent-1-cs->stack->sub_level,0)*INDENT;
1802   for (count= 0; count < indent ; count++)
1803   {
1804     if ((count % INDENT) == 0)
1805       fputc('|',cs->stack->out_file->file);
1806     else
1807       fputc(' ',cs->stack->out_file->file);
1808   }
1809 }
1810 
1811 
1812 /*
1813  *  FUNCTION
1814  *
1815  *      FreeList    free all memory associated with a linked list
1816  *
1817  *  SYNOPSIS
1818  *
1819  *      static VOID FreeList(linkp)
1820  *      struct link *linkp;
1821  *
1822  *  DESCRIPTION
1823  *
1824  *      Given pointer to the head of a linked list, frees all
1825  *      memory held by the list and the members of the list.
1826  *
1827  */
1828 
1829 static void FreeList(struct link *linkp)
1830 {
1831   struct link *old;
1832 
1833   while (linkp != NULL)
1834   {
1835     old= linkp;
1836     linkp= linkp->next_link;
1837     free((void*) old);
1838   }
1839 }
1840 
1841 
1842 /*
1843  *  FUNCTION
1844  *
1845  *      DoPrefix    print debugger line prefix prior to indentation
1846  *
1847  *  SYNOPSIS
1848  *
1849  *      static VOID DoPrefix(_line_)
1850  *      int _line_;
1851  *
1852  *  DESCRIPTION
1853  *
1854  *      Print prefix common to all debugger output lines, prior to
1855  *      doing indentation if necessary.  Print such information as
1856  *      current process name, current source file name and line number,
1857  *      and current function nesting depth.
1858  *
1859  */
1860 
1861 static void DoPrefix(CODE_STATE *cs, uint _line_)
1862 {
1863   cs->lineno++;
1864   if (cs->stack->flags & PID_ON)
1865   {
1866     (void) fprintf(cs->stack->out_file->file, "%-7s: ", my_thread_name());
1867   }
1868   if (cs->stack->flags & NUMBER_ON)
1869     (void) fprintf(cs->stack->out_file->file, "%5d: ", cs->lineno);
1870   if (cs->stack->flags & TIMESTAMP_ON)
1871   {
1872 #ifdef __WIN__
1873     /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
1874        in system ticks, 10 ms intervals. See my_getsystime.c for high res */
1875     SYSTEMTIME loc_t;
1876     GetLocalTime(&loc_t);
1877     (void) fprintf (cs->stack->out_file->file,
1878                     /* "%04d-%02d-%02d " */
1879                     "%02d:%02d:%02d.%06d ",
1880                     /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
1881                     loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
1882 #else
1883     struct timeval tv;
1884     struct tm *tm_p;
1885     if (gettimeofday(&tv, NULL) != -1)
1886     {
1887       if ((tm_p= localtime((const time_t *)&tv.tv_sec)))
1888       {
1889         (void) fprintf (cs->stack->out_file->file,
1890                         /* "%04d-%02d-%02d " */
1891                         "%02d:%02d:%02d.%06d ",
1892                         /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
1893                         tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
1894                         (int) (tv.tv_usec));
1895       }
1896     }
1897 #endif
1898   }
1899   if (cs->stack->flags & PROCESS_ON)
1900     (void) fprintf(cs->stack->out_file->file, "%s: ", cs->process);
1901   if (cs->stack->flags & FILE_ON)
1902     (void) fprintf(cs->stack->out_file->file, "%14s: ", BaseName(cs->file));
1903   if (cs->stack->flags & LINE_ON)
1904     (void) fprintf(cs->stack->out_file->file, "%5d: ", _line_);
1905   if (cs->stack->flags & DEPTH_ON)
1906     (void) fprintf(cs->stack->out_file->file, "%4d: ", cs->level);
1907 }
1908 
1909 
1910 /*
1911  *  FUNCTION
1912  *
1913  *      DBUGOpenFile    open new output stream for debugger output
1914  *
1915  *  SYNOPSIS
1916  *
1917  *      static VOID DBUGOpenFile(name)
1918  *      char *name;
1919  *
1920  *  DESCRIPTION
1921  *
1922  *      Given name of a new file (or "-" for stdout) opens the file
1923  *      and sets the output stream to the new file.
1924  *
1925  */
1926 
1927 static void DBUGOpenFile(CODE_STATE *cs,
1928                          const char *name,const char *end,int append)
1929 {
1930   FILE *fp;
1931 
1932   if (name != NULL)
1933   {
1934     if (end)
1935     {
1936       size_t len=end-name;
1937       memcpy(cs->stack->name, name, len);
1938       cs->stack->name[len]=0;
1939     }
1940     else
1941       strmov(cs->stack->name,name);
1942     name=cs->stack->name;
1943     if (strcmp(name, "-") == 0)
1944     {
1945       DBUGCloseFile(cs, sstdout);
1946       cs->stack->flags |= FLUSH_ON_WRITE;
1947       cs->stack->name[0]=0;
1948     }
1949     else
1950     {
1951       if (!Writable(name))
1952       {
1953         (void) fprintf(stderr, ERR_OPEN, cs->process, name);
1954         perror("");
1955         fflush(stderr);
1956       }
1957       else
1958       {
1959         if (!(fp= fopen(name, append ? "a+" : "w")))
1960         {
1961           (void) fprintf(stderr, ERR_OPEN, cs->process, name);
1962           perror("");
1963           fflush(stderr);
1964         }
1965         else
1966         {
1967           sFILE *sfp= (sFILE *)DbugMalloc(sizeof(sFILE));
1968           sfp->file= fp;
1969           sfp->used= 1;
1970           DBUGCloseFile(cs, sfp);
1971         }
1972       }
1973     }
1974   }
1975 }
1976 
1977 /*
1978  *  FUNCTION
1979  *
1980  *      DBUGCloseFile    close the debug output stream
1981  *
1982  *  SYNOPSIS
1983  *
1984  *      static VOID DBUGCloseFile(fp)
1985  *      FILE *fp;
1986  *
1987  *  DESCRIPTION
1988  *
1989  *      Closes the debug output stream unless it is standard output
1990  *      or standard error.
1991  *
1992  */
1993 
1994 static void DBUGCloseFile(CODE_STATE *cs, sFILE *new_value)
1995 {
1996   sFILE *fp;
1997   if (!cs || !cs->stack || !cs->stack->out_file)
1998     return;
1999 
2000   fp= cs->stack->out_file;
2001   if (--fp->used == 0)
2002   {
2003     if (fclose(fp->file) == EOF)
2004     {
2005       LockMutex(cs);
2006       (void) fprintf(stderr, ERR_CLOSE, cs->process);
2007       perror("");
2008       UnlockMutex(cs);
2009     }
2010     else
2011     {
2012       free(fp);
2013     }
2014   }
2015   cs->stack->out_file= new_value;
2016 }
2017 
2018 
2019 /*
2020  *  FUNCTION
2021  *
2022  *      DbugExit    print error message and exit
2023  *
2024  *  SYNOPSIS
2025  *
2026  *      static VOID DbugExit(why)
2027  *      char *why;
2028  *
2029  *  DESCRIPTION
2030  *
2031  *      Prints error message using current process name, the reason for
2032  *      aborting (typically out of memory), and exits with status 1.
2033  *      This should probably be changed to use a status code
2034  *      defined in the user's debugger include file.
2035  *
2036  */
2037 
2038 static void DbugExit(const char *why)
2039 {
2040   CODE_STATE *cs=code_state();
2041   (void) fprintf(stderr, ERR_ABORT, cs ? cs->process : "(null)", why);
2042   (void) fflush(stderr);
2043   DBUG_ABORT();
2044 }
2045 
2046 
2047 /*
2048  *  FUNCTION
2049  *
2050  *      DbugMalloc    allocate memory for debugger runtime support
2051  *
2052  *  SYNOPSIS
2053  *
2054  *      static long *DbugMalloc(size)
2055  *      int size;
2056  *
2057  *  DESCRIPTION
2058  *
2059  *      Allocate more memory for debugger runtime support functions.
2060  *      Failure to to allocate the requested number of bytes is
2061  *      immediately fatal to the current process.  This may be
2062  *      rather unfriendly behavior.  It might be better to simply
2063  *      print a warning message, freeze the current debugger cs,
2064  *      and continue execution.
2065  *
2066  */
2067 
2068 static char *DbugMalloc(size_t size)
2069 {
2070   char *new_malloc;
2071 
2072   if (!(new_malloc= (char*) malloc(size)))
2073     DbugExit("out of memory");
2074   return new_malloc;
2075 }
2076 
2077 
2078 /*
2079  *     strtok lookalike - splits on ':', magically handles ::, :\ and :/
2080  */
2081 
2082 static const char *DbugStrTok(const char *s)
2083 {
2084   while (s[0] && (s[0] != ':' ||
2085                   (s[1] == '\\' || s[1] == '/' || (s[1] == ':' && s++))))
2086     s++;
2087   return s;
2088 }
2089 
2090 
2091 /*
2092  *  FUNCTION
2093  *
2094  *      BaseName    strip leading pathname components from name
2095  *
2096  *  SYNOPSIS
2097  *
2098  *      static char *BaseName(pathname)
2099  *      char *pathname;
2100  *
2101  *  DESCRIPTION
2102  *
2103  *      Given pointer to a complete pathname, locates the base file
2104  *      name at the end of the pathname and returns a pointer to
2105  *      it.
2106  *
2107  */
2108 
2109 static const char *BaseName(const char *pathname)
2110 {
2111   const char *base;
2112 
2113   base= strrchr(pathname, FN_LIBCHAR);
2114   if (base++ == NullS)
2115     base= pathname;
2116   return base;
2117 }
2118 
2119 
2120 /*
2121  *  FUNCTION
2122  *
2123  *      Writable    test to see if a pathname is writable/creatable
2124  *
2125  *  SYNOPSIS
2126  *
2127  *      static BOOLEAN Writable(pathname)
2128  *      char *pathname;
2129  *
2130  *  DESCRIPTION
2131  *
2132  *      Because the debugger might be linked in with a program that
2133  *      runs with the set-uid-bit (suid) set, we have to be careful
2134  *      about opening a user named file for debug output.  This consists
2135  *      of checking the file for write access with the real user id,
2136  *      or checking the directory where the file will be created.
2137  *
2138  *      Returns TRUE if the user would normally be allowed write or
2139  *      create access to the named file.  Returns FALSE otherwise.
2140  *
2141  */
2142 
2143 
2144 #ifndef Writable
2145 
2146 static BOOLEAN Writable(const char *pathname)
2147 {
2148   BOOLEAN granted;
2149   char *lastslash;
2150 
2151   granted= FALSE;
2152   if (EXISTS(pathname))
2153   {
2154     if (WRITABLE(pathname))
2155       granted= TRUE;
2156   }
2157   else
2158   {
2159     lastslash= strrchr(pathname, '/');
2160     if (lastslash != NULL)
2161       *lastslash= '\0';
2162     else
2163       pathname= ".";
2164     if (WRITABLE(pathname))
2165       granted= TRUE;
2166     if (lastslash != NULL)
2167       *lastslash= '/';
2168   }
2169   return granted;
2170 }
2171 #endif
2172 
2173 /*
2174   flush dbug-stream, free mutex lock & wait delay
2175   This is because some systems (MSDOS!!) doesn't flush fileheader
2176   and dbug-file isn't readable after a system crash !!
2177 */
2178 
2179 static void DbugFlush(CODE_STATE *cs)
2180 {
2181   if (cs->stack->flags & FLUSH_ON_WRITE)
2182   {
2183     (void) fflush(cs->stack->out_file->file);
2184     if (cs->stack->delay)
2185       (void) Delay(cs->stack->delay);
2186   }
2187 } /* DbugFlush */
2188 
2189 
2190 /* For debugging */
2191 
2192 void _db_flush_()
2193 {
2194   CODE_STATE *cs;
2195   get_code_state_or_return;
2196   if (DEBUGGING)
2197   {
2198     (void) fflush(cs->stack->out_file->file);
2199   }
2200 }
2201 
2202 
2203 #ifndef __WIN__
2204 void _db_suicide_()
2205 {
2206   int retval;
2207   sigset_t new_mask;
2208   sigfillset(&new_mask);
2209 
2210   fprintf(stderr, "SIGKILL myself\n");
2211   fflush(stderr);
2212 
2213   retval= kill(getpid(), SIGKILL);
2214   assert(retval == 0);
2215   retval= sigsuspend(&new_mask);
2216   fprintf(stderr, "sigsuspend returned %d errno %d \n", retval, errno);
2217   assert(FALSE); /* With full signal mask, we should never return here. */
2218 }
2219 #endif  /* ! __WIN__ */
2220 
2221 
2222 void _db_lock_file_()
2223 {
2224   CODE_STATE *cs;
2225   get_code_state_or_return;
2226   LockMutex(cs);
2227 }
2228 
2229 void _db_unlock_file_()
2230 {
2231   CODE_STATE *cs;
2232   get_code_state_or_return;
2233   UnlockMutex(cs);
2234 }
2235 
2236 const char* _db_get_func_(void)
2237 {
2238   CODE_STATE *cs;
2239   get_code_state_or_return NULL;
2240   return cs->func;
2241 }
2242 
2243 
2244 static int default_my_dbug_sanity(void)
2245 {
2246   return 0;
2247 }
2248 
2249 extern my_bool my_assert;
2250 ATTRIBUTE_COLD
2251 my_bool _db_my_assert(const char *file, int line, const char *msg)
2252 {
2253   my_bool a = my_assert;
2254   _db_flush_();
2255   if (!a)
2256     fprintf(stderr, "%s:%d: assert: %s\n", file, line, msg);
2257   return a;
2258 }
2259 #else
2260 
2261 /*
2262  * Dummy function, workaround for build failure on a platform where linking
2263  * with an empty archive fails.
2264  */
2265 int i_am_a_dummy_function() {
2266   return 0;
2267 }
2268 
2269 #endif /* DBUG_OFF */
2270 
2271 /*
2272   This function is called by default on DBUG_ASSERT() when compiled with
2273   DBUG_ASSERT_AS_PRINTF
2274 */
2275 
2276 #ifdef DBUG_ASSERT_AS_PRINTF
2277 
2278 static void default_my_dbug_assert_failed(const char *assert_expr,
2279                                           const char *file,
2280                                           unsigned long line)
2281 {
2282   fprintf(stderr, "Warning: assertion failed: %s at %s line %lu\n",
2283           assert_expr, file, line);
2284 }
2285 
2286 void (*my_dbug_assert_failed)(const char *assert_expr, const char* file,
2287                               unsigned long line)= default_my_dbug_assert_failed;
2288 
2289 #endif /* DBUG_ASSERT_AS_PRINTF */
2290