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
50int	*displine;		/* screen line of displayed reference */
51int	disprefs;		/* displayed references */
52int	field;			/* input field */
53unsigned fldcolumn;		/* input field column */
54int	mdisprefs;		/* maximum displayed references */
55int	selectlen;		/* selection number field length */
56int	nextline;		/* next line to be shown */
57int	topline = 1;		/* top line of page */
58int	bottomline;		/* bottom line of page */
59int	totallines;		/* total reference lines */
60FILE	*refsfound;		/* references found file */
61FILE	*nonglobalrefs;		/* non-global references file */
62
63static	int	fldline;		/* input field line */
64static	int	subsystemlen;		/* OGS subsystem name display */
65					/* field length */
66static	int	booklen;		/* OGS book name display field length */
67static	int	filelen;		/* file name display field length */
68static	int	fcnlen;			/* function name display field length */
69static	jmp_buf	env;			/* setjmp/longjmp buffer */
70static	int	lastdispline;		/* last displayed reference line */
71static	char	lastmsg[MSGLEN + 1];	/* last message displayed */
72static	int	numlen;			/* line number display field length */
73static	char	depthstring[] = "Depth: ";
74static	char	helpstring[] = "Press the ? key for help";
75
76
77typedef char *(*FP)();	/* pointer to function returning a character pointer */
78
79static	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
113void
114dispinit(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
143void
144display(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 */
359void
360setfield(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
369void
370atfield(void)
371{
372	(void) move(fldline, (int)fldcolumn);
373}
374
375/* search for the symbol or text pattern */
376
377/*ARGSUSED*/
378SIGTYPE
379jumpback(int sig)
380{
381	longjmp(env, 1);
382}
383
384BOOL
385search(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
466BOOL
467writerefsfound(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
483void
484countrefs(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
554void
555myperror(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
565void
566putmsg(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
582void
583clearmsg2(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
593void
594putmsg2(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
607void
608seekline(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
630void
631ogsnames(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
659char *
660pathcomponents(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