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