xref: /illumos-gate/usr/src/tools/cscope-fast/display.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1988 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*
34  *	cscope - interactive C symbol cross-reference
35  *
36  *	display functions
37  */
38 
39 #include "global.h"
40 #include "version.h"	/* FILEVERSION and FIXVERSION */
41 #include <curses.h>	/* COLS and LINES */
42 #include <setjmp.h>	/* jmp_buf */
43 #include <string.h>
44 #include <errno.h>
45 
46 /* see if the function column should be displayed */
47 #define	displayfcn()	(field <= ASSIGN)
48 
49 #define	MINCOLS	68	/* minimum columns for 3 digit Lines message numbers */
50 
51 int	*displine;		/* screen line of displayed reference */
52 int	disprefs;		/* displayed references */
53 int	field;			/* input field */
54 unsigned fldcolumn;		/* input field column */
55 int	mdisprefs;		/* maximum displayed references */
56 int	selectlen;		/* selection number field length */
57 int	nextline;		/* next line to be shown */
58 int	topline = 1;		/* top line of page */
59 int	bottomline;		/* bottom line of page */
60 int	totallines;		/* total reference lines */
61 FILE	*refsfound;		/* references found file */
62 FILE	*nonglobalrefs;		/* non-global references file */
63 
64 static	int	fldline;		/* input field line */
65 static	int	subsystemlen;		/* OGS subsystem name display */
66 					/* field length */
67 static	int	booklen;		/* OGS book name display field length */
68 static	int	filelen;		/* file name display field length */
69 static	int	fcnlen;			/* function name display field length */
70 static	jmp_buf	env;			/* setjmp/longjmp buffer */
71 static	int	lastdispline;		/* last displayed reference line */
72 static	char	lastmsg[MSGLEN + 1];	/* last message displayed */
73 static	int	numlen;			/* line number display field length */
74 static	char	depthstring[] = "Depth: ";
75 static	char	helpstring[] = "Press the ? key for help";
76 
77 
78 typedef char *(*FP)();	/* pointer to function returning a character pointer */
79 
80 static	struct	{
81 	char	*text1;
82 	char	*text2;
83 	FP	findfcn;
84 	enum {
85 		EGREP,
86 		REGCMP
87 	} patterntype;
88 } fields[FIELDS + 1] = {
89 	/* last search is not part of the cscope display */
90 	{ "Find this", "C symbol",
91 	    (FP) findsymbol, REGCMP},
92 	{ "Find this", "definition",
93 	    (FP) finddef, REGCMP},
94 	{ "Find", "functions called by this function",
95 	    (FP) findcalledby, REGCMP},
96 	{ "Find", "functions calling this function",
97 	    (FP) findcalling, REGCMP},
98 	{ "Find", "assignments to",
99 	    (FP) findassignments, REGCMP},
100 	{ "Change this", "grep pattern",
101 	    findgreppat, EGREP},
102 	{ "Find this", "egrep pattern",
103 	    findegreppat, EGREP},
104 	{ "Find this", "file",
105 	    (FP) findfile, REGCMP},
106 	{ "Find", "files #including this file",
107 	    (FP) findinclude, REGCMP},
108 	{ "Find all", "function/class definitions",
109 	    (FP) findallfcns, REGCMP},
110 };
111 
112 /* initialize display parameters */
113 
114 void
115 dispinit(void)
116 {
117 	/* calculate the maximum displayed reference lines */
118 	lastdispline = FLDLINE - 2;
119 	mdisprefs = lastdispline - REFLINE + 1;
120 	if (mdisprefs <= 0) {
121 		(void) printw("cscope: window must be at least %d lines high",
122 		    FIELDS + 6);
123 		myexit(1);
124 	}
125 	if (COLS < MINCOLS) {
126 		(void) printw("cscope: window must be at least %d columns wide",
127 		    MINCOLS);
128 		myexit(1);
129 	}
130 	if (!mouse) {
131 		if (returnrequired == NO && mdisprefs > 9) {
132 			mdisprefs = 9;	/* single digit selection number */
133 		}
134 		/* calculate the maximum selection number width */
135 		(void) sprintf(newpat, "%d", mdisprefs);
136 		selectlen = strlen(newpat);
137 	}
138 	/* allocate the displayed line array */
139 	displine = (int *)mymalloc(mdisprefs * sizeof (int));
140 }
141 
142 /* display a page of the references */
143 
144 void
145 display(void)
146 {
147 	char	*subsystem;		/* OGS subsystem name */
148 	char	*book;			/* OGS book name */
149 	char	file[PATHLEN + 1];	/* file name */
150 	char	function[PATLEN + 1];	/* function name */
151 	char	linenum[NUMLEN + 1];	/* line number */
152 	int	screenline;		/* screen line number */
153 	int	width;			/* source line display width */
154 	int	i;
155 	char	*s;
156 
157 	(void) erase();
158 
159 	/* if there are no references */
160 	if (totallines == 0) {
161 		if (*lastmsg != '\0') {
162 			(void) addstr(lastmsg);	/* redisplay any message */
163 		} else {
164 			(void) printw("Cscope version %d%s", FILEVERSION,
165 			    FIXVERSION);
166 			(void) move(0, COLS - (int)sizeof (helpstring));
167 			(void) addstr(helpstring);
168 		}
169 	} else {	/* display the pattern */
170 		if (changing == YES) {
171 			(void) printw("Change \"%s\" to \"%s\"",
172 			    pattern, newpat);
173 		} else {
174 			(void) printw("%c%s: %s",
175 			    toupper(fields[field].text2[0]),
176 			    fields[field].text2 + 1, pattern);
177 		}
178 		/* display the cscope invocation nesting depth */
179 		if (cscopedepth > 1) {
180 			(void) move(0, COLS - (int)sizeof (depthstring) - 2);
181 			(void) addstr(depthstring);
182 			(void) printw("%d", cscopedepth);
183 		}
184 		/* display the column headings */
185 		(void) move(2, selectlen + 1);
186 		if (ogs == YES && field != FILENAME) {
187 			(void) printw("%-*s ", subsystemlen, "Subsystem");
188 			(void) printw("%-*s ", booklen, "Book");
189 		}
190 		if (dispcomponents > 0) {
191 			(void) printw("%-*s ", filelen, "File");
192 		}
193 		if (displayfcn()) {
194 			(void) printw("%-*s ", fcnlen, "Function");
195 		}
196 		if (field != FILENAME) {
197 			(void) addstr("Line");
198 		}
199 		(void) addch('\n');
200 
201 		/* if at end of file go back to beginning */
202 		if (nextline > totallines) {
203 			seekline(1);
204 		}
205 		/* calculate the source text column */
206 		width = COLS - selectlen - numlen - 2;
207 		if (ogs == YES) {
208 			width -= subsystemlen + booklen + 2;
209 		}
210 		if (dispcomponents > 0) {
211 			width -= filelen + 1;
212 		}
213 		if (displayfcn()) {
214 			width -= fcnlen + 1;
215 		}
216 		/*
217 		 * until the max references have been displayed or
218 		 * there is no more room
219 		 */
220 		topline = nextline;
221 		for (disprefs = 0, screenline = REFLINE;
222 		    disprefs < mdisprefs && screenline <= lastdispline;
223 		    ++disprefs, ++screenline) {
224 			/* read the reference line */
225 			if (fscanf(refsfound, "%s%s%s %[^\n]", file, function,
226 			    linenum, yytext) < 4) {
227 				break;
228 			}
229 			++nextline;
230 			displine[disprefs] = screenline;
231 
232 			/* if no mouse, display the selection number */
233 			if (!mouse) {
234 				(void) printw("%*d", selectlen, disprefs + 1);
235 			}
236 			/* display any change mark */
237 			if (changing == YES &&
238 			    change[topline + disprefs - 1] == YES) {
239 				(void) addch('>');
240 			} else {
241 				(void) addch(' ');
242 			}
243 			/* display the file name */
244 			if (field == FILENAME) {
245 				(void) printw("%-.*s\n", COLS - 3, file);
246 				continue;
247 			}
248 			/* if OGS, display the subsystem and book names */
249 			if (ogs == YES) {
250 				ogsnames(file, &subsystem, &book);
251 				(void) printw("%-*.*s ", subsystemlen,
252 				    subsystemlen, subsystem);
253 				(void) printw("%-*.*s ", booklen, booklen,
254 				    book);
255 			}
256 			/* display the requested path components */
257 			if (dispcomponents > 0) {
258 				(void) printw("%-*.*s ", filelen, filelen,
259 				    pathcomponents(file, dispcomponents));
260 			}
261 			/* display the function name */
262 			if (displayfcn()) {
263 				(void) printw("%-*.*s ", fcnlen, fcnlen,
264 				    function);
265 			}
266 			/* display the line number */
267 			(void) printw("%*s ", numlen, linenum);
268 
269 			/* there may be tabs in egrep output */
270 			while ((s = strchr(yytext, '\t')) != NULL) {
271 				*s = ' ';
272 			}
273 			/* display the source line */
274 			s = yytext;
275 			for (;;) {
276 				/* see if the source line will fit */
277 				if ((i = strlen(s)) > width) {
278 					/* find the nearest blank */
279 					for (i = width; s[i] != ' ' && i > 0;
280 					    --i) {
281 					}
282 					if (i == 0) {
283 						i = width;	/* no blank */
284 					}
285 				}
286 				/* print up to this point */
287 				(void) printw("%.*s", i, s);
288 				s += i;
289 
290 				/* if line didn't wrap around */
291 				if (i < width) {
292 					/* go to next line */
293 					(void) addch('\n');
294 				}
295 				/* skip blanks */
296 				while (*s == ' ') {
297 					++s;
298 				}
299 				/* see if there is more text */
300 				if (*s == '\0') {
301 					break;
302 				}
303 				/* if the source line is too long */
304 				if (++screenline > lastdispline) {
305 					/*
306 					 * if this is the first displayed line,
307 					 * display what will fit on the screen
308 					 */
309 					if (topline == nextline - 1) {
310 						goto endrefs;
311 					}
312 					/* erase the reference */
313 					while (--screenline >=
314 					    displine[disprefs]) {
315 						(void) move(screenline, 0);
316 						(void) clrtoeol();
317 					}
318 					++screenline;
319 
320 					/*
321 					 * go back to the beginning of this
322 					 * reference
323 					 */
324 					--nextline;
325 					seekline(nextline);
326 					goto endrefs;
327 				}
328 				/* indent the continued source line */
329 				(void) move(screenline, COLS - width);
330 			}
331 
332 		}
333 	endrefs:
334 		/* check for more references */
335 		bottomline = nextline;
336 		if (bottomline - topline < totallines) {
337 			(void) move(FLDLINE - 1, 0);
338 			(void) standout();
339 			(void) printw("%*s", selectlen + 1, "");
340 			if (bottomline - 1 == topline) {
341 				(void) printw("Line %d", topline);
342 			} else {
343 				(void) printw("Lines %d-%d", topline,
344 				    bottomline - 1);
345 			}
346 			(void) printw(" of %d, press the space bar to "
347 			    "display next lines", totallines);
348 			(void) standend();
349 		}
350 	}
351 	/* display the input fields */
352 	(void) move(FLDLINE, 0);
353 	for (i = 0; i < FIELDS; ++i) {
354 		(void) printw("%s %s:\n", fields[i].text1, fields[i].text2);
355 	}
356 	drawscrollbar(topline, nextline, totallines);
357 }
358 
359 /* set the cursor position for the field */
360 void
361 setfield(void)
362 {
363 	fldline = FLDLINE + field;
364 	fldcolumn = strlen(fields[field].text1) +
365 	    strlen(fields[field].text2) + 3;
366 }
367 
368 /* move to the current input field */
369 
370 void
371 atfield(void)
372 {
373 	(void) move(fldline, (int)fldcolumn);
374 }
375 
376 /* search for the symbol or text pattern */
377 
378 /*ARGSUSED*/
379 SIGTYPE
380 jumpback(int sig)
381 {
382 	longjmp(env, 1);
383 }
384 
385 BOOL
386 search(void)
387 {
388 	char	*egreperror = NULL;	/* egrep error message */
389 	FINDINIT rc = NOERROR;		/* findinit return code */
390 	SIGTYPE	(*savesig)();		/* old value of signal */
391 	FP	f;			/* searching function */
392 	char	*s;
393 	int	c;
394 
395 	/* note: the pattern may have been a cscope argument */
396 	if (caseless == YES) {
397 		for (s = pattern; *s != '\0'; ++s) {
398 			*s = tolower(*s);
399 		}
400 	}
401 	/* open the references found file for writing */
402 	if (writerefsfound() == NO) {
403 		return (NO);
404 	}
405 	/* find the pattern - stop on an interrupt */
406 	if (linemode == NO) {
407 		putmsg("Searching");
408 	}
409 	initprogress();
410 	if (setjmp(env) == 0) {
411 		savesig = signal(SIGINT, jumpback);
412 		f = fields[field].findfcn;
413 		if (fields[field].patterntype == EGREP) {
414 			egreperror = (*f)(pattern);
415 		} else {
416 			if ((nonglobalrefs = fopen(temp2, "w")) == NULL) {
417 				cannotopen(temp2);
418 				return (NO);
419 			}
420 			if ((rc = findinit()) == NOERROR) {
421 				(void) dbseek(0L); /* goto the first block */
422 				(*f)();
423 				findcleanup();
424 
425 				/* append the non-global references */
426 				(void) freopen(temp2, "r", nonglobalrefs);
427 				while ((c = getc(nonglobalrefs)) != EOF) {
428 					(void) putc(c, refsfound);
429 				}
430 			}
431 			(void) fclose(nonglobalrefs);
432 		}
433 	}
434 	(void) signal(SIGINT, savesig);
435 	/* reopen the references found file for reading */
436 	(void) freopen(temp1, "r", refsfound);
437 	nextline = 1;
438 	totallines = 0;
439 
440 	/* see if it is empty */
441 	if ((c = getc(refsfound)) == EOF) {
442 		if (egreperror != NULL) {
443 			(void) sprintf(lastmsg, "Egrep %s in this pattern: %s",
444 			    egreperror, pattern);
445 		} else if (rc == NOTSYMBOL) {
446 			(void) sprintf(lastmsg, "This is not a C symbol: %s",
447 			    pattern);
448 		} else if (rc == REGCMPERROR) {
449 			(void) sprintf(lastmsg,
450 			    "Error in this regcmp(3X) regular expression: %s",
451 			    pattern);
452 		} else {
453 			(void) sprintf(lastmsg, "Could not find the %s: %s",
454 			    fields[field].text2, pattern);
455 		}
456 		return (NO);
457 	}
458 	/* put back the character read */
459 	(void) ungetc(c, refsfound);
460 
461 	countrefs();
462 	return (YES);
463 }
464 
465 /* open the references found file for writing */
466 
467 BOOL
468 writerefsfound(void)
469 {
470 	if (refsfound == NULL) {
471 		if ((refsfound = fopen(temp1, "w")) == NULL) {
472 			cannotopen(temp1);
473 			return (NO);
474 		}
475 	} else if (freopen(temp1, "w", refsfound) == NULL) {
476 		putmsg("Cannot reopen temporary file");
477 		return (NO);
478 	}
479 	return (YES);
480 }
481 
482 /* count the references found */
483 
484 void
485 countrefs(void)
486 {
487 	char	*subsystem;		/* OGS subsystem name */
488 	char 	*book;			/* OGS book name */
489 	char	file[PATHLEN + 1];	/* file name */
490 	char	function[PATLEN + 1];	/* function name */
491 	char	linenum[NUMLEN + 1];	/* line number */
492 	int	i;
493 
494 	/*
495 	 * count the references found and find the length of the file,
496 	 * function, and line number display fields
497 	 */
498 	subsystemlen = 9;	/* strlen("Subsystem") */
499 	booklen = 4;		/* strlen("Book") */
500 	filelen = 4;		/* strlen("File") */
501 	fcnlen = 8;		/* strlen("Function") */
502 	numlen = 0;
503 	while ((i = fscanf(refsfound, "%250s%250s%6s %5000[^\n]", file,
504 	    function, linenum, yytext)) != EOF) {
505 		if (i != 4 || !isgraph(*file) ||
506 		    !isgraph(*function) || !isdigit(*linenum)) {
507 			putmsg("File does not have expected format");
508 			totallines = 0;
509 			return;
510 		}
511 		if ((i = strlen(pathcomponents(file,
512 		    dispcomponents))) > filelen) {
513 			filelen = i;
514 		}
515 		if (ogs == YES) {
516 			ogsnames(file, &subsystem, &book);
517 			if ((i = strlen(subsystem)) > subsystemlen) {
518 				subsystemlen = i;
519 			}
520 			if ((i = strlen(book)) > booklen) {
521 				booklen = i;
522 			}
523 		}
524 		if ((i = strlen(function)) > fcnlen) {
525 			fcnlen = i;
526 		}
527 		if ((i = strlen(linenum)) > numlen) {
528 			numlen = i;
529 		}
530 		++totallines;
531 	}
532 	rewind(refsfound);
533 
534 	/* restrict the width of displayed columns */
535 	i = (COLS - 5) / 3;
536 	if (ogs == YES) {
537 		i = (COLS - 7) / 5;
538 	}
539 	if (filelen > i && i > 4) {
540 		filelen = i;
541 	}
542 	if (subsystemlen > i && i > 9) {
543 		subsystemlen = i;
544 	}
545 	if (booklen > i && i > 4) {
546 		booklen = i;
547 	}
548 	if (fcnlen > i && i > 8) {
549 		fcnlen = i;
550 	}
551 }
552 
553 /* print error message on system call failure */
554 
555 void
556 myperror(char *text)
557 {
558 	char	msg[MSGLEN + 1];	/* message */
559 
560 	(void) sprintf(msg, "%s: %s", text, strerror(errno));
561 	putmsg(msg);
562 }
563 
564 /* putmsg clears the message line and prints the message */
565 
566 void
567 putmsg(char *msg)
568 {
569 	if (incurses == NO) {
570 		*msg = tolower(*msg);
571 		(void) fprintf(stderr, "cscope: %s\n", msg);
572 	} else {
573 		(void) move(MSGLINE, 0);
574 		(void) clrtoeol();
575 		(void) addstr(msg);
576 		(void) refresh();
577 	}
578 	(void) strncpy(lastmsg, msg, sizeof (lastmsg) - 1);
579 }
580 
581 /* clearmsg2 clears the second message line */
582 
583 void
584 clearmsg2(void)
585 {
586 	if (incurses == YES) {
587 		(void) move(MSGLINE + 1, 0);
588 		(void) clrtoeol();
589 	}
590 }
591 
592 /* putmsg2 clears the second message line and prints the message */
593 
594 void
595 putmsg2(char *msg)
596 {
597 	if (incurses == NO) {
598 		putmsg(msg);
599 	} else {
600 		clearmsg2();
601 		(void) addstr(msg);
602 		(void) refresh();
603 	}
604 }
605 
606 /* position the references found file at the specified line */
607 
608 void
609 seekline(int line)
610 {
611 	int	c;
612 
613 	/* verify that there is a references found file */
614 	if (refsfound == NULL) {
615 		return;
616 	}
617 	/* go to the beginning of the file */
618 	rewind(refsfound);
619 
620 	/* find the requested line */
621 	nextline = 1;
622 	while (nextline < line && (c = getc(refsfound)) != EOF) {
623 		if (c == '\n') {
624 			nextline++;
625 		}
626 	}
627 }
628 
629 /* get the OGS subsystem and book names */
630 
631 void
632 ogsnames(char *file, char **subsystem, char **book)
633 {
634 	static	char	buf[PATHLEN + 1];
635 	char	*s, *slash;
636 
637 	*subsystem = *book = "";
638 	(void) strcpy(buf, file);
639 	s = buf;
640 	if (*s == '/') {
641 		++s;
642 	}
643 	while ((slash = strchr(s, '/')) != NULL) {
644 		*slash = '\0';
645 		if ((int)strlen(s) >= 3 && strncmp(slash - 3, ".ss", 3) == 0) {
646 			*subsystem = s;
647 			s = slash + 1;
648 			if ((slash = strchr(s, '/')) != NULL) {
649 				*book = s;
650 				*slash = '\0';
651 			}
652 			break;
653 		}
654 		s = slash + 1;
655 	}
656 }
657 
658 /* get the requested path components */
659 
660 char *
661 pathcomponents(char *path, int components)
662 {
663 	int	i;
664 	char	*s;
665 
666 	s = path + strlen(path) - 1;
667 	for (i = 0; i < components; ++i) {
668 		while (s > path && *--s != '/') {
669 			;
670 		}
671 	}
672 	if (s > path && *s == '/') {
673 		++s;
674 	}
675 	return (s);
676 }
677