1 /* @(#)calltree.c	1.50 21/08/20 Copyright 1985, 1999-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)calltree.c	1.50 21/08/20 Copyright 1985, 1999-2021 J. Schilling";
6 #endif
7 /*
8  *	A program to produce a static calltree for C-functions
9  *
10  *	Copyright (c) 1985, 1999-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/stdio.h>		/* Includes popen()/pclose()	*/
27 #include <schily/standard.h>
28 #include <schily/stdlib.h>
29 #include <schily/unistd.h>
30 #include <schily/string.h>
31 #define	GT_COMERR		/* #define comerr gtcomerr */
32 #define	GT_ERROR		/* #define error gterror   */
33 #include <schily/schily.h>
34 #define	VMS_VFORK_OK
35 #include <schily/vfork.h>
36 #include <schily/nlsdefs.h>
37 #include "strsubs.h"
38 #include "sym.h"
39 #include "clex.h"
40 #include "version.h"
41 
42 #ifdef	HAVE_FORK
43 #	include <schily/wait.h>
44 #	define	Pwait(f)	{ wait(0); fclose(f); }
45 #else
46 #	define	Pwait(f)	pclose(f)
47 #endif
48 
49 LOCAL	char	ct_version[] = VERSION;
50 
51 BOOL	bflag	= FALSE;		/* -b: print vertical bar for indent */
52 BOOL	rflag	= FALSE;		/* -r: inverted output print callers */
53 BOOL	fflag	= FALSE;		/* -f: flat output (summary per func)*/
54 BOOL	gflag	= FALSE;		/* -g: print filename of definition  */
55 BOOL	mflag	= FALSE;		/* -m: list 'main' only (like l=main)*/
56 BOOL	npflag	= FALSE;		/* -np: don't call preprocessor	    */
57 BOOL	pflag	= TRUE;			/* -p: call preprocessor (default)   */
58 BOOL	uflag	= FALSE;		/* -u: print funcs unused from 'main'*/
59 BOOL	eflag	= FALSE;		/* -e: print funcs unused completely */
60 BOOL	xflag	= FALSE;		/* -x: detect external functions    */
61 BOOL	vflag	= FALSE;		/* -v: be verbose		    */
62 BOOL	debug	= FALSE;		/* -debug: print debugging messages */
63 BOOL	xvcg	= FALSE;		/* -xvcg: format output for xvcg    */
64 BOOL	dot	= FALSE;		/* -dot: format output for graphviz */
65 
66 #define	DEFDEPTH	32000
67 
68 int	depth	= DEFDEPTH;		/* Stop at this call nesting depth  */
69 int	indents	= 4;			/* default indentation per nest depth*/
70 char	indent[256];			/* This is the indent string	    */
71 
72 int	nestlevel;			/* for {} nesting, used by parser   */
73 sym_t	*funcs;				/* global function table	    */
74 sym_t	*fnames;			/* global filename table	    */
75 sym_t	*listfuncs;			/* table of functions to list	    */
76 sym_t	*ignorefuncs;			/* table of functions to ignore	    */
77 char	*curfname;
78 
79 #define	MAXARGS		32
80 
81 #ifdef	_MSC_VER
82 int	Argc = 2;
83 char	*Argv [MAXARGS] = { "cl.exe", "-E" };
84 #else
85 #ifndef	HAVE_FORK
86 int	Argc = 2;
87 char	*Argv [MAXARGS] = { "cc", "-E" };
88 #else
89 int	Argc = 2;
90 char	*Argv [MAXARGS] = { "cpp", "-I/usr/include" };
91 #endif	/* HAVE_FORK */
92 #endif	/* _MSC_VER */
93 #ifdef	__EMX__
94 char	*Env[2] = { "PATH=/lib;/usr/ccs/lib;/usr/bin;/bin", 0 };
95 #else
96 char	*Env[2] = { "PATH=/lib:/usr/ccs/lib:/usr/bin:/bin", 0 };
97 #endif
98 
99 LOCAL	void	usage		__PR((int exitcode));
100 EXPORT	int	main		__PR((int ac, char **av));
101 LOCAL	FILE	*mkcpppipe	__PR((FILE *f, char *fname));
102 LOCAL	void	printfuncs	__PR((sym_t *table));
103 LOCAL	void	printafunc	__PR((sym_t *sym));
104 LOCAL	void	printusage	__PR((int indt, sym_t *tab));
105 LOCAL	void	printflatusage	__PR((sym_t *tab));
106 LOCAL	void	printxvcgusage	__PR((sym_t *caller, sym_t *tab));
107 LOCAL	void	printdotusage	__PR((sym_t *caller, sym_t *tab));
108 LOCAL	void	cleartree	__PR((sym_t *sym));
109 LOCAL	void	flattree	__PR((sym_t *tab));
110 LOCAL	BOOL	findclose	__PR((FILE *fp));
111 LOCAL	void	findfname	__PR((FILE *fp, char *fname));
112 LOCAL	int	checkfunc	__PR((FILE *fp, int *ftype));
113 LOCAL	int	findfunc	__PR((FILE *fp, char *name, char *fname));
114 LOCAL	void	nesterror	__PR((void));
115 LOCAL	void	parsefile	__PR((FILE *fp, char *filename));
116 LOCAL	int	readfuncs	__PR((char *filename, sym_t **tab));
117 LOCAL	int	got_cpp_arg	__PR((char *name, char *type));
118 
119 LOCAL void
usage(exitcode)120 usage(exitcode)
121 	int	exitcode;
122 {
123 	error("Usage:	calltree [calltree_options] [cpp_options] file1..filen\n");
124 	error("Options:\n");
125 	error("\t-b\t\tPrint a vertial Bar at each tab stop.\n");
126 	error("\t-r\t\tInvert the structure of the tree.\n");
127 	error("\t-f\t\tFlattened (cumulative) tree.\n");
128 	error("\t-g\t\tPrint file names past procedure names.\n");
129 	error("\t-m\t\tCall structure for main only.\n");
130 	error("\t-p\t\tUse C Preprocessor (default).\n");
131 	error("\t-np\t\tDon't use C Preprocessor.\n");
132 	error("\t-u\t\tList all functions not called via 'main'.\n");
133 	error("\t-e\t\tList all functions not called.\n");
134 	error("\t-x\t\tList all external functions.\n");
135 	error("\t-xvcg\t\tFormat output for xvcg.\n");
136 	error("\t-dot\t\tFormat output for graphviz.\n");
137 	error("\t-v\t\tBe verbose.\n");
138 	error("\t-help\t\tPrint this help.\n");
139 	error("\t-version\tPrint version number.\n");
140 	error("\tigorefile=file\tDon't list functions found in 'file'.\n");
141 	error("\tlistfile=file\tList only functions found in 'file'.\n");
142 	error("\tlist=name\tProduce call graph only for function 'name'.\n");
143 	error("\tdepth=#\t\tSet the maximum printed nesting depth to #.\n");
144 	error("\ts=#\t\tSet indentation to #.\n");
145 	error("ignorefile=, listfile= and depth= may be abbreviated by first letter.\n");
146 	error("list= may be abbreviated by lf=.\n");
147 	exit(exitcode);
148 }
149 
150 LOCAL	char	*opts = "b,r,f,g,m,p,np,u,e,x,dot,xvcg,v,ignorefile&,i&,listfile&,l&,list*,lf*,depth#,d#,s#,help,version,debug+,db+,I&,D&,U&";
151 
152 EXPORT int
main(ac,av)153 main(ac, av)
154 	int	ac;
155 	char	*av[];
156 {
157 	FILE	*f;
158 	int	i;
159 	int	help = 0;
160 	int	version = 0;
161 	char	*thisname = 0;
162 	int	cac;
163 	char *const * cav;
164 
165 	save_args(ac, av);
166 
167 	(void) setlocale(LC_ALL, "");
168 
169 #ifdef  USE_NLS
170 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
171 #define	TEXT_DOMAIN "calltree"	/* Use this only if it weren't */
172 #endif
173 	{ char	*dir;
174 	dir = searchfileinpath("share/locale", F_OK,
175 					SIP_ANY_FILE|SIP_NO_PATH, NULL);
176 	if (dir)
177 		(void) bindtextdomain(TEXT_DOMAIN, dir);
178 	else
179 #if defined(PROTOTYPES) && defined(INS_BASE)
180 	(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
181 #else
182 	(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
183 #endif
184 	(void) textdomain(TEXT_DOMAIN);
185 	}
186 #endif 	/* USE_NLS */
187 
188 	cac = --ac;
189 	cav = ++av;
190 
191 	/*
192 	 * the argument order it's important and it must be the same of opts
193 	 */
194 	if (getallargs(&cac, &cav, opts, &bflag, &rflag, &fflag, &gflag,
195 			&mflag, &pflag, &npflag, &uflag, &eflag,
196 			&xflag,
197 			&dot,
198 			&xvcg,
199 			&vflag,
200 			readfuncs, &ignorefuncs,
201 			readfuncs, &ignorefuncs,
202 			readfuncs, &listfuncs,
203 			readfuncs, &listfuncs,
204 			&thisname, &thisname,
205 			&depth, &depth,
206 			&indents, &help, &version,
207 			&debug, &debug,
208 			got_cpp_arg, "-I",
209 			got_cpp_arg, "-D",
210 			got_cpp_arg, "-U") < 0) {
211 		errmsgno(EX_BAD, "Bad Option: %s.\n", cav[0]);
212 		usage(EX_BAD);
213 	}
214 	if (help)
215 		usage(0);
216 	if (version) {
217 		gtprintf("Calltree release %s %s (%s-%s-%s) Copyright (C) 1985, 88-90, 95-99, 2000-2021 %s\n",
218 				ct_version, VERSION_DATE,
219 				HOST_CPU, HOST_VENDOR, HOST_OS,
220 				_("J�rg Schilling"));
221 		exit(0);
222 	}
223 
224 	if (npflag)
225 		pflag = FALSE;
226 	if (depth <= 0)
227 		depth = DEFDEPTH;
228 	if (uflag)
229 		mflag = fflag = TRUE;
230 	if (eflag)
231 		uflag = rflag = TRUE;
232 	if (xflag) {
233 		eflag = TRUE;  rflag = FALSE;
234 	}
235 	if (thisname)
236 		mflag = TRUE;
237 	if (thisname == 0)
238 		thisname = "main";
239 
240 	if (indents > sizeof (indent)-1) {
241 		indents = sizeof (indent)-1;
242 		errmsgno(EX_BAD, "Cannot indent more than %d spaces.\n", indents);
243 	}
244 	for (i = 0; i < indents; i++)
245 		indent[i] = ' ';
246 	if (bflag)
247 		indent[0] = '|';
248 	indent[indents] = '\0';
249 
250 	initkeyw();
251 
252 	cac = ac;
253 	cav = av;
254 	if (getfiles(&cac, &cav, opts) <= 0) {
255 		errmsgno(EX_BAD, "No input files.\n");
256 		usage(EX_BAD);
257 	}
258 
259 	if (vflag)
260 		error("Generating names ...\n");
261 
262 	for (; getfiles(&cac, &cav, opts); cac--, cav++) {
263 		if ((f = fileopen(cav[0], "r")) == (FILE *) NULL)
264 			comerr("Cannot open '%s'.\n", cav[0]);
265 
266 		if (pflag)
267 			f = mkcpppipe(f, cav[0]);
268 
269 		parsefile(f, cav[0]);
270 
271 		if (pflag) {
272 			Pwait(f);
273 		} else {
274 			fclose(f);
275 		}
276 	}
277 
278 	if (vflag)
279 		error("Generating tree ...\n");
280 
281 	if (xvcg) {
282 		/*
283 		 * Print header for xvcg.
284 		 */
285 		mflag = FALSE;
286 		printf("graph: {\norientation: left_to_right\n");
287 	}
288 	if (dot) {
289 		/*
290 		 * Print header for graphviz.
291 		 */
292 		mflag = FALSE;
293 		printf("digraph function_map {\nrankdir=LR;\nratio=fill;\nnode [style=filled]\n");
294 	}
295 	if (mflag) {
296 		/*
297 		 * Either -m or -r name, do one function only.
298 		 */
299 		sym_t	*this;
300 
301 		if ((this = lookup(thisname, &funcs, L_LOOK)) == NULL) {
302 			comerrno(EX_BAD, "Function '%s' not found.\n", thisname);
303 		} else {
304 			cleartree(funcs);
305 			if (uflag)
306 				this->s_flags |= S_USED;
307 			printafunc(this);
308 		}
309 	} else {
310 		/*
311 		 * Print all functions.
312 		 */
313 		printfuncs(funcs);
314 	}
315 	if (xvcg) {
316 		/*
317 		 * Print trailer for xvcg.
318 		 */
319 		printf("}\n");
320 	}
321 	if (dot) {
322 		/*
323 		 * Print trailer for graphviz.
324 		 */
325 		printf("}\n");
326 	}
327 	exit(0);
328 	/* NOTREACHED */
329 	return (0);	/* Keep lint happy */
330 }
331 
332 LOCAL FILE *
mkcpppipe(f,fname)333 mkcpppipe(f, fname)
334 	FILE	*f;
335 	char	*fname;
336 {
337 #ifdef	HAVE_FORK
338 	FILE	*fpp[2];
339 	int	i;
340 
341 	if (debug) {
342 		error("CPP args: ");
343 		for (i = 0; i < Argc; i++)
344 			error("'%s' ", Argv[i]);
345 		error("\n");
346 	}
347 	if (fpipe(fpp) == 0)
348 		comerr("Can not make pipe to C-preprocessor.\n");
349 
350 	if ((i = vfork()) == 0) {	/* child */
351 #ifdef	F_SETFD
352 		fcntl(fileno(pfd[0]), F_SETFD, FD_CLOEXEC);
353 #endif
354 		fexecve(Argv[0], f, fpp[1], stderr,
355 			Argv, Env);
356 		errmsg("Cannot execute '%s'.\n", Argv[0]);
357 #ifdef	HAVE_VFORK
358 		_exit(geterrno());
359 #else
360 		exit(geterrno());
361 #endif
362 	}
363 	if (i < 0)
364 		comerr("Fork failed.\n");
365 
366 	/*
367 	 * parent
368 	 */
369 	fclose(f);			/* don't need it here*/
370 	fclose(fpp[1]);
371 	return (fpp[0]);
372 #else					/* No fork(), try popen() */
373 	FILE	*fp;
374 	int	i;
375 	char	cmd[8192];
376 	char	*p;
377 
378 	if (debug) {
379 		error("CPP args: ");
380 		for (i = 0; i < Argc; i++)
381 			error("'%s' ", Argv[i]);
382 		error("\n");
383 	}
384 	cmd[0] = '\0';
385 	for (i = 0; i < Argc; i++) {
386 		p = cmd + strlen(cmd);
387 		js_snprintf(p, sizeof (cmd) + cmd - p,
388 			"%s ", Argv[i]);
389 	}
390 	p = cmd + strlen(cmd);
391 	js_snprintf(p, sizeof (cmd) + cmd - p,
392 		"%s ", fname);
393 	fp = popen(cmd, "r");
394 	fclose(f);
395 	return (fp);
396 #endif
397 }
398 
399 /*
400  * Print the known information for all functions
401  */
402 LOCAL void
printfuncs(tab)403 printfuncs(tab)
404 	sym_t	*tab;
405 {
406 	if (tab) {
407 		printfuncs(tab->s_left);
408 		cleartree(funcs);
409 		printafunc(tab);
410 		printfuncs(tab->s_right);
411 	}
412 }
413 
414 /*
415  * Print the known information for one function
416  */
417 LOCAL void
printafunc(sym)418 printafunc(sym)
419 	sym_t	*sym;
420 {
421 	if (listfuncs && !lookup(sym->s_name, &listfuncs, L_LOOK))
422 		return;
423 
424 	if (xvcg) {
425 		if (!eflag && !sym->s_uses)
426 			return;
427 		printf("node: { title: \"%s\" ", sym->s_name);
428 		if (gflag && sym->s_filename)
429 			printf("label: \"%s [%s:%d]\" ",
430 				sym->s_name, sym->s_filename, sym->s_lineno);
431 		if ((sym->s_flags & S_DEF) == 0)
432 			printf("shape: ellipse textcolor: lightgrey bordercolor: lightgrey ");
433 		printf("}\n");
434 		printxvcgusage(sym, sym->s_uses);
435 	} else if (dot) {
436 		if (!eflag && !sym->s_uses)
437 			return;
438 		printf(" %s [ shape=box ", sym->s_name);
439 		if (gflag && sym->s_filename)
440 			printf("label = \"%s [%s:%d]\" ",
441 				sym->s_name, sym->s_filename, sym->s_lineno);
442 		if ((sym->s_flags & S_DEF) == 0)
443 			printf("shape = ellipse, fontcolor = lightgrey ");
444 		printf("];\n");
445 		printdotusage(sym, sym->s_uses);
446 	} else if ((sym->s_flags & S_DEF) == 0) {
447 		printf("%s", sym->s_name);
448 		if (gflag && sym->s_filename) {
449 			printf(" [%s:%d]",
450 				sym->s_filename, sym->s_lineno);
451 		}
452 		if (eflag) {
453 			printf("\n");
454 		} else if (rflag) {
455 			printf(":\n%sNOT CALLED\n", indent);
456 		} else {
457 			printf(":\n%sEXTERNAL ROUTINE\n", indent);
458 		}
459 	} else if (!eflag) {
460 		printf("%s", sym->s_name);
461 		if (gflag && sym->s_filename) {
462 			printf(" [%s:%d]:\n",
463 					sym->s_filename,
464 					sym->s_lineno);
465 		} else {
466 			printf(":\n");
467 		}
468 		if (fflag) {
469 			flattree(sym->s_uses);
470 			printflatusage(funcs);
471 		} else {
472 			sym->s_flags |= S_RECURSE;
473 			printusage(1, sym->s_uses);
474 			sym->s_flags &= ~S_RECURSE;
475 		}
476 	}
477 }
478 
479 /*
480  * Print the known caller/callee information for one function.
481  */
482 LOCAL void
printusage(indt,tab)483 printusage(indt, tab)
484 	int	indt;
485 	sym_t	*tab;
486 {
487 	int	i;
488 	sym_t	*sym;
489 	BOOL	isrecurse = FALSE;
490 
491 	if (tab == NULL)
492 		return;
493 
494 	printusage(indt, tab->s_left);
495 	for (i = 0; i < indt; i++)
496 		printf("%s", indent);
497 
498 	if (tab->s_sym == NULL) {	/* Darf eigentlich nicht vorkommen */
499 		printf("FAKED CALL\n");
500 		return;
501 	}
502 	sym = tab->s_sym;
503 
504 	printf("%s", sym->s_name);
505 	if (gflag && sym->s_filename)
506 		printf(" [%s:%d]", sym->s_filename, sym->s_lineno);
507 	if ((sym->s_flags & S_RECURSE) != 0) {
508 		isrecurse = TRUE;
509 		printf(" ....\n");
510 	} else
511 		printf("\n");
512 	if ((--depth > 0) && sym != 0 && !isrecurse && !rflag) {
513 		sym->s_flags |= S_RECURSE;
514 		printusage(indt+1, sym->s_uses);
515 		sym->s_flags &= ~S_RECURSE;
516 	}
517 	depth++;
518 
519 	printusage(indt, tab->s_right);
520 }
521 
522 /*
523  * Print the known caller/callee information for one function in flat form.
524  */
525 LOCAL void
printflatusage(tab)526 printflatusage(tab)
527 	sym_t	*tab;
528 {
529 	if (tab) {
530 		printflatusage(tab->s_left);
531 		if (((tab->s_flags & S_USED) != 0 && !uflag) ||
532 		    ((tab->s_flags & S_USED) == 0 && uflag)) {
533 			printf("%s%s", indent, tab->s_name);
534 			if (gflag && tab->s_filename) {
535 				printf(" [%s:%d]\n",
536 					tab->s_filename, tab->s_lineno);
537 			} else {
538 				printf("\n");
539 			}
540 		}
541 		printflatusage(tab->s_right);
542 	}
543 }
544 
545 /*
546  * Print the known caller/callee information formatted for xvcg.
547  */
548 LOCAL void
printxvcgusage(caller,tab)549 printxvcgusage(caller, tab)
550 	sym_t	*caller;
551 	sym_t	*tab;
552 {
553 	if (tab) {
554 		sym_t	*called;
555 
556 		printxvcgusage(caller, tab->s_left);
557 		if (eflag ||
558 		    ((called = lookup(tab->s_name, &funcs, L_LOOK)) != NULL &&
559 		    called->s_uses))
560 			printf("edge: { sourcename: \"%s\" targetname: \"%s\" }\n",
561 					caller->s_name, tab->s_name);
562 		printxvcgusage(caller, tab->s_right);
563 	}
564 }
565 
566 LOCAL void
printdotusage(caller,tab)567 printdotusage(caller, tab)
568 	sym_t	*caller;
569 	sym_t	*tab;
570 {
571 	if (tab) {
572 		sym_t	*called;
573 
574 		printdotusage(caller, tab->s_left);
575 		if (eflag ||
576 		    ((called = lookup(tab->s_name, &funcs, L_LOOK)) != NULL &&
577 		    called->s_uses))
578 			printf("%s  -> %s;\n",
579 				caller->s_name, tab->s_name);
580 		printdotusage(caller, tab->s_right);
581 	}
582 }
583 /*
584  * Clear all markers in the symbol tree
585  */
586 LOCAL void
cleartree(sym)587 cleartree(sym)
588 	sym_t	*sym;
589 {
590 	if (sym) {
591 		cleartree(sym->s_left);
592 		sym->s_flags &= ~(S_USED|S_RECURSE);
593 		cleartree(sym->s_right);
594 	}
595 }
596 
597 /*
598  * Prepare a tree for flat (cumulative) printing.
599  */
600 LOCAL void
flattree(tab)601 flattree(tab)
602 	sym_t	*tab;
603 {
604 	sym_t	*sym;
605 
606 	if (tab) {
607 		flattree(tab->s_left);
608 		if (((sym = tab->s_sym)->s_flags & S_USED) == 0) {
609 			sym->s_flags |= S_USED;
610 			flattree(sym->s_uses);
611 		}
612 		flattree(tab->s_right);
613 	}
614 }
615 
616 /*
617  * Find a matching close bracket ')'.
618  */
619 LOCAL BOOL
findclose(fp)620 findclose(fp)
621 	FILE	*fp;
622 {
623 	register int	n = 1;
624 	register int	tktype;
625 
626 	while ((tktype = clex(fp)) != T_EOF) {
627 		switch (tktype) {
628 
629 		case T_LCURLY:
630 		case T_RCURLY:
631 			return (FALSE);
632 
633 		case T_OPEN:
634 			n++; break;
635 
636 		case T_CLOSE:
637 			n--; break;
638 		}
639 		if (n == 0)
640 			return (TRUE);
641 	}
642 	return (FALSE);
643 }
644 
645 /*
646  * Parse a lineno/filename directive from the C-preprocessor.
647  */
648 LOCAL void
findfname(fp,fname)649 findfname(fp, fname)
650 	FILE	*fp;
651 	char	*fname;
652 {
653 	register int	tktype;
654 		char	*p;
655 		int	line;
656 
657 	if ((tktype = clex(fp)) == T_EOF)
658 		return;
659 
660 	switch (tktype) {
661 
662 	case T_ALPHA:
663 		if (strcmp((char *)lexbuf, "line") != 0)
664 			return;
665 		if ((tktype = clex(fp)) == T_EOF)
666 			return;
667 		if (tktype != T_NUMBER)
668 			return;
669 		/* FALLTHROUGH */
670 	case T_NUMBER:
671 		astoi((char *)lexbuf, &line);
672 		lexline = line-1;
673 		if ((tktype = clex(fp)) == T_EOF)
674 			return;
675 		if (tktype == T_STRING) {
676 			p = (char *)lexbuf;
677 			p++;
678 			p[strlen(p)-1] = '\0';
679 			strcpy(fname, p);
680 
681 			/*
682 			 * The new GNU cpp v3.2 prints <stdin> for filename.
683 			 * For example the hash line in GNU cpp v2.96 (worked):
684 			 *
685 			 *  # 40 ""
686 			 *
687 			 * With GNU cpp v3.2 (fails without the fix below):
688 			 *
689 			 *  # 40 "<stdin>"
690 			 *
691 			 * The "<stdin>" string should be treated as a blank
692 			 * string instead of a filename.
693 			 */
694 			if (strcmp(fname, "<stdin>") == 0)
695 				fname[0] = '\0';
696 
697 			if (fname[0] != '\0')
698 				lexfile = fname;
699 			else
700 				lexfile = curfname;
701 		}
702 	}
703 }
704 
705 #define	FUNC_DEF	1
706 #define	FUNC_CALL	0
707 
708 /*
709  * Check for function call/definition.
710  */
711 LOCAL int
checkfunc(fp,ftypep)712 checkfunc(fp, ftypep)
713 	FILE	*fp;
714 	int	*ftypep;
715 {
716 	register int	tktype = T_NONE;
717 
718 	/*
719 	 * We come here if we found a T_ALPHA followed by T_OPEN
720 	 * e.g. 'testfunc ('
721 	 */
722 	if (nestlevel == 0) {				/* not in function */
723 
724 		if (!findclose(fp))
725 			return (-1);			/* maybe var def */
726 		tktype = clex(fp);
727 
728 		if (tktype != T_SEMI &&
729 		    tktype != T_COMMA) {		/* no external def */
730 
731 			if (tktype == T_LCURLY) {
732 				nestlevel++;
733 			} else if (tktype == T_RCURLY &&
734 				    --nestlevel < 0) {
735 				/*
736 				 * Bad syntax or rotten parser.
737 				 */
738 				nesterror();
739 			}
740 			*ftypep = FUNC_DEF;		/* func definition */
741 			return (tktype);
742 		}
743 		*ftypep = -1;				/* maybe a var def */
744 		return (tktype);
745 
746 	} else {					/* in function */
747 
748 #ifdef	nonono /* Hier wird Func call im Argument nicht erkannt */
749 		if (!findclose(fp))
750 			return (-1);			/* maybe var usage */
751 		if ((tktype = clex(fp)) == T_OPEN) {
752 			error("No func: '%s' ('%s') on line %d\n", name, lexbuf, lexline);
753 			return (-1);
754 		}
755 #endif
756 		*ftypep = FUNC_CALL;			/* func call */
757 		return (tktype);
758 	}
759 }
760 
761 /*
762  * Find functions in C-syntax.
763  */
764 LOCAL int
findfunc(fp,name,fname)765 findfunc(fp, name, fname)
766 	FILE	*fp;
767 	char	*name;
768 	char	*fname;
769 {
770 	register int	tktype;
771 		int	ftype = 0;		/* init to make GCC quiet */
772 
773 	while ((tktype = clex(fp)) != T_EOF) {
774 recheck:
775 		if (debug) {
776 			error("%s:%d{%d} %s: %s\n",
777 				lexfile, lexline, nestlevel, lextnames[tktype], lexbuf);
778 		}
779 		switch (tktype) {
780 
781 		case T_LCURLY:
782 			nestlevel++;
783 			break;
784 
785 		case T_RCURLY:
786 			if (--nestlevel < 0)
787 				nesterror();
788 			break;
789 
790 		case T_HASH:
791 			findfname(fp, fname);
792 			break;
793 
794 		case T_ALPHA:
795 			strcpy(name, (char *)lexbuf);
796 			if ((tktype = clex(fp)) == T_OPEN) {
797 				tktype = checkfunc(fp, &ftype);
798 				if (ftype < 0)
799 					goto recheck;
800 				return (ftype);
801 			}
802 			goto recheck;
803 
804 		default:
805 			break;
806 		}
807 	}
808 	return (-1);					/* EOF */
809 }
810 
811 /*
812  * Warn about a {} nesting error and reset nestlevel.
813  */
814 LOCAL void
nesterror()815 nesterror()
816 {
817 	error("Found '}' without open on line %d.\n", lexline);
818 	if (debug)
819 		error("lexbuf from line %d: %s\n", lexline, lexbuf);
820 	nestlevel = 0;
821 }
822 
823 /*
824  * Zuerst wird eine Funktionsdefinition gefunden und deren Wert in 'dfunc'
825  * gemerkt. Alle Funktionsaufrufe innerhalb dieser Funktion bekommen
826  * dann 'dfunc' als rufende Funktion vermerkt.
827  *
828  * Problem:
829  * Es gibt Struct Definitionen, bei denen calltree 'denkt' dasz ein
830  * Funktionsaufruf vorliegt, ohne dasz vorher eine Funktionsdefinition
831  * gefunden wurde. Dann ist der Zeiger auf die Rufende Funktion ein
832  * NULL Pointer.
833  * Richtige Abhilfe:
834  * Funktionsdecoder verbessern.
835  *
836  * Standard tree:
837  *
838  * <funcs (tree)> ... -> <sym_t 'name' caller> -> <list of callees (tree)>
839  *    ^							|
840  *    |_________________________________________________|
841  *	Backpointer to adjacent callee pointer in main tree
842  *
843  *
844  * Reversed tree:
845  *
846  * <funcs (tree)> ... -> <sym_t 'name' callee> -> <list of callers (tree)>
847  *    ^							|
848  *    |_________________________________________________|
849  *	Backpointer to adjacent caller pointer in main tree
850  *
851  */
852 LOCAL void
parsefile(fp,filename)853 parsefile(fp, filename)
854 	FILE	*fp;
855 	char	*filename;
856 {
857 	char	name[LEXBSIZE];
858 	char	fname[LEXBSIZE];
859 	int	ft;
860 	sym_t	*dsym = (sym_t *)0;	/* defined function */
861 	sym_t	*csym;			/* called function */
862 	sym_t	*usym;			/* this function from uses list */
863 
864 	if (vflag)
865 		error("%s\n", filename);
866 
867 	clexinit();
868 	fname[0] = '\0';
869 	lexfile = curfname = filename;
870 
871 	for (;;) {
872 		if ((ft = findfunc(fp, name, fname)) == FUNC_DEF) {
873 			if (lookup(name, &ignorefuncs, L_LOOK))
874 				continue;
875 			if (debug)
876 				error("define func '%s'\n", name);
877 
878 			dsym = lookup(name, &funcs, L_CREATE);
879 			if (fname[0] != '\0') {
880 				usym = lookup(fname, &fnames, L_CREATE);
881 				dsym->s_filename = usym->s_name;
882 			} else {
883 				dsym->s_filename = filename;
884 			}
885 			dsym->s_lineno = lexline;
886 			if ((dsym->s_flags & S_DEF) != 0) {
887 				/*
888 				 * Static functions of the same name
889 				 * are not yet handled.
890 				 */
891 				if ((dsym->s_flags & S_WARN) == 0)
892 					errmsgno(EX_BAD,
893 						"Warning: function: %s already defined\n", name);
894 				dsym->s_flags |= S_WARN;
895 			}
896 			dsym->s_flags |= S_DEF;
897 		} else if (ft == FUNC_CALL) {
898 			if (lookup(name, &ignorefuncs, L_LOOK))
899 				continue;
900 			if (debug)
901 				error("call func '%s'\n", name);
902 			if (dsym == (sym_t *)0) {	/* Not in Function */
903 				if (debug)
904 					error("Bad call: '%s'\n", name);
905 				continue;
906 			}
907 
908 			csym = lookup(name, &funcs, L_CREATE);
909 			if (rflag) {
910 				/*
911 				 * Insert the calling function into the
912 				 * function list of (this) called function.
913 				 */
914 				usym = lookup(dsym->s_name, &(csym->s_uses), funcs);
915 				usym->s_sym = dsym;	/* Back ptr to caller */
916 							/* in main funcs tree */
917 			} else {
918 				/*
919 				 * Insert (this) called function into the
920 				 * function list of the caller.
921 				 */
922 				usym = lookup(name, &(dsym->s_uses), funcs);
923 				usym->s_sym = csym;	/* Back ptr to callee */
924 							/* in main funcs tree */
925 			}
926 		} else {
927 			if (debug)
928 				error("EOF '%s'\n", name);
929 			break;
930 		}
931 	}
932 }
933 
934 /*
935  * Read a file containung a list of function names.
936  */
937 LOCAL int
readfuncs(filename,tab)938 readfuncs(filename, tab)
939 	char	*filename;
940 	sym_t	**tab;
941 {
942 	FILE	*fp;
943 	char	fname[LEXBSIZE];
944 
945 	if ((fp = fileopen(filename, "r")) == NULL)
946 		comerr("Cannot open file %s\n", filename);
947 
948 	while (fgetline(fp, fname, sizeof (fname)) >= 0)
949 		lookup(fname, tab, L_CREATE);
950 
951 	return (1);
952 }
953 
954 /*
955  * Handle CPP command line options.
956  */
957 LOCAL int
got_cpp_arg(name,type)958 got_cpp_arg(name, type)
959 	char	*name;
960 	char	*type;
961 {
962 	if (debug)
963 		error("Got CPP arg: %s %s\n", type, name);
964 
965 	if (Argc >= MAXARGS) {
966 		errmsgno(EX_BAD, "Too many Preprocessor options.\n");
967 		return (-2);
968 	}
969 	Argv[Argc++] = concat(type, name, (char *)NULL);
970 	return (1);
971 }
972