Home | History | Annotate | Download | only in cscope-fast
      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