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