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