1 /*===========================================================================
2 Copyright (c) 1998-2000, The Santa Cruz Operation
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 *Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
11 *Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
15 *Neither name of The Santa Cruz Operation nor the names of its contributors
16 may be used to endorse or promote products derived from this software
17 without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20 IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 =========================================================================*/
33 /* cscope - interactive C symbol cross-reference
43 #include "sgs.h" /* ESG_PKG and ESG_REL */
45 #include "version.h" /* FILEVERSION and FIXVERSION */
48 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
53 #include <setjmp.h> /* jmp_buf */
54 #include <stdarg.h> /* va_list stuff */
59 #ifndef HAVE_SIGSETJMP
60 # define sigsetjmp(a,b) setjmp(a)
61 # define siglongjmp(a,b) longjmp(a,b)
62 typedef jmp_buf sigjmp_buf;
65 static char const rcsid[] = "$Id: display.c,v 1.33 2012/05/20 12:24:17 broeker Exp $";
67 int booklen; /* OGS book name display field length */
68 int *displine; /* screen line of displayed reference */
69 unsigned int disprefs; /* displayed references */
70 int field; /* input field */
71 int filelen; /* file name display field length */
72 int fcnlen; /* function name display field length */
73 unsigned int mdisprefs; /* maximum displayed references */
74 unsigned int nextline; /* next line to be shown */
75 FILE *nonglobalrefs; /* non-global references file */
76 int numlen; /* line number display field length */
77 unsigned int topline = 1; /* top line of page */
78 int bottomline; /* bottom line of page */
79 long searchcount; /* count of files searched */
80 int subsystemlen; /* OGS subsystem name display field length */
81 unsigned int totallines; /* total reference lines */
82 unsigned fldcolumn; /* input field column */
84 const char dispchars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
86 static int fldline; /* input field line */
87 static sigjmp_buf env; /* setjmp/longjmp buffer */
88 static int lastdispline; /* last displayed reference line */
89 static char lastmsg[MSGLEN + 1]; /* last message displayed */
90 static char helpstring[] = "Press the ? key for help";
91 static char selprompt[] =
92 "Select lines to change (press the ? key for help): ";
94 typedef char * (*FP)(char *); /* pointer to function returning a character pointer */
96 /* HBB 2000/05/05: I removed the casts to function pointer type. It is
97 * fundamentally unsafe to call a function through a pointer of a
98 * different type ('undefined behaviour' in the words of the ANSI/ISO
99 * C standard). Instead, I made all the find...() functions adhere to
100 * the same function type, by changing argument passing a bit. */
101 static struct { /* text of input fields */
105 } fields[FIELDS + 1] = { /* samuel has a search that is not part of the cscope display */
106 {"Find this", "C symbol", findsymbol},
107 {"Find this", "global definition", finddef},
108 {"Find", "functions called by this function", findcalledby},
109 {"Find", "functions calling this function", findcalling},
110 {"Find this", "text string", findstring},
111 {"Change this", "text string", findstring},
112 {"Find this", "egrep pattern", findregexp},
113 {"Find this", "file", findfile},
114 {"Find", "files #including this file", findinclude},
115 {"Find", "assignments to this symbol", findassign},
116 {"Find all", "function definitions", findallfcns}, /* samuel only */
119 /* Internal prototypes: */
120 static RETSIGTYPE jumpback(int sig);
122 /* initialize display parameters */
127 /* calculate the maximum displayed reference lines */
128 lastdispline = FLDLINE - 3;
129 mdisprefs = lastdispline - REFLINE + 1;
132 if (mdisprefs <= 0) {
133 postfatal("%s: screen too small\n", argv0);
137 if (mouse == NO && mdisprefs > strlen(dispchars))
138 mdisprefs = strlen(dispchars);
140 /* allocate the displayed line array */
141 displine = mymalloc(mdisprefs * sizeof(int));
144 /* display a page of the references */
149 char *subsystem; /* OGS subsystem name */
150 char *book; /* OGS book name */
151 char file[PATHLEN + 1]; /* file name */
152 char function[PATLEN + 1]; /* function name */
153 char linenum[NUMLEN + 1]; /* line number */
154 int screenline; /* screen line number */
155 int width; /* source line display width */
159 /* see if this is the initial display */
161 if (refsfound == NULL) {
163 if (displayversion == YES) {
164 printw("cscope %s", ESG_REL);
170 printw("Cscope version %d%s", FILEVERSION, FIXVERSION);
172 move(0, COLS - (int) sizeof(helpstring));
174 } else if (totallines == 0) {
175 /* if no references were found */
176 /* redisplay the last message */
179 /* display the pattern */
180 if (changing == YES) {
181 printw("Change \"%s\" to \"%s\"", Pattern, newpat);
183 printw("%c%s: %s", toupper((unsigned char)fields[field].text2[0]),
184 fields[field].text2 + 1, Pattern);
186 /* display the column headings */
188 if (ogs == YES && field != FILENAME) {
189 printw("%-*s ", subsystemlen, "Subsystem");
190 printw("%-*s ", booklen, "Book");
192 if (dispcomponents > 0)
193 printw("%-*s ", filelen, "File");
195 if (field == SYMBOL || field == CALLEDBY || field == CALLING) {
196 printw("%-*s ", fcnlen, "Function");
198 if (field != FILENAME) {
203 /* if at end of file go back to beginning */
204 if (nextline > totallines) {
207 /* calculate the source text column */
209 width = COLS - numlen - 3;
212 width -= subsystemlen + booklen + 2;
214 if (dispcomponents > 0) {
215 width -= filelen + 1;
217 if (field == SYMBOL || field == CALLEDBY || field == CALLING) {
221 /* until the max references have been displayed or
222 there is no more room */
224 for (disprefs = 0, screenline = REFLINE;
225 disprefs < mdisprefs && screenline <= lastdispline;
226 ++disprefs, ++screenline) {
227 /* read the reference line */
228 if (fscanf(refsfound, "%" PATHLEN_STR "s%" PATHLEN_STR "s%" NUMLEN_STR "s %" TEMPSTRING_LEN_STR "[^\n]", file, function,
229 linenum, tempstring) < 4) {
233 displine[disprefs] = screenline;
235 /* if no mouse, display the selection number */
239 printw("%c", dispchars[disprefs]);
242 /* display any change mark */
243 if (changing == YES &&
244 change[topline + disprefs - 1] == YES) {
250 /* display the file name */
251 if (field == FILENAME) {
252 printw("%-*s ", filelen, file);
254 /* if OGS, display the subsystem and book names */
256 ogsnames(file, &subsystem, &book);
257 printw("%-*.*s ", subsystemlen, subsystemlen, subsystem);
258 printw("%-*.*s ", booklen, booklen, book);
260 /* display the requested path components */
261 if (dispcomponents > 0) {
262 printw("%-*.*s ", filelen, filelen,
263 pathcomponents(file, dispcomponents));
265 } /* else(field == FILENAME) */
267 /* display the function name */
268 if (field == SYMBOL || field == CALLEDBY || field == CALLING) {
269 printw("%-*.*s ", fcnlen, fcnlen, function);
271 if (field == FILENAME) {
272 addch('\n'); /* go to next line */
276 /* display the line number */
277 printw("%*s ", numlen, linenum);
278 /* there may be tabs in egrep output */
279 while ((s = strchr(tempstring, '\t')) != NULL) {
283 /* display the source line */
286 /* see if the source line will fit */
287 if ((i = strlen(s)) > width) {
289 /* find the nearest blank */
290 for (i = width; s[i] != ' ' && i > 0; --i) {
294 i = width; /* no blank */
297 /* print up to this point */
298 printw("%.*s", i, s);
301 /* if line didn't wrap around */
303 addch('\n'); /* go to next line */
309 /* see if there is more text */
313 /* if the source line is too long */
314 if (++screenline > lastdispline) {
316 /* if this is the first displayed line,
317 display what will fit on the screen */
318 if (topline == nextline -1) {
320 /* break out of two loops */
324 /* erase the reference */
325 while (--screenline >= displine[disprefs]) {
331 /* go back to the beginning of this reference */
336 /* indent the continued source line */
337 move(screenline, COLS - width);
339 } /* for(reference output lines) */
341 /* position the cursor for the message */
343 if (screenline < i) {
349 /* check for more references */
350 i = totallines - nextline + 1;
351 bottomline = nextline;
353 printw("* Lines %d-%d of %d, %d more - press the space bar to display more *", topline, bottomline, totallines, i);
355 /* if this is the last page of references */
356 else if (topline > 1 && nextline > totallines) {
357 addstr("* Press the space bar to display the first lines again *");
360 /* display the input fields */
362 for (i = 0; i < FIELDS; ++i) {
363 printw("%s %s:\n", fields[i].text1, fields[i].text2);
365 /* display any prompt */
366 if (changing == YES) {
370 drawscrollbar(topline, nextline); /* display the scrollbar */
374 /* set the cursor position for the field */
378 fldline = FLDLINE + field;
379 fldcolumn = strlen(fields[field].text1) + strlen(fields[field].text2) + 3;
382 /* move to the current input field */
387 move(fldline, fldcolumn);
390 /* move to the changing lines prompt */
395 move(PRLINE, (int) sizeof(selprompt) - 1);
398 /* search for the symbol or text pattern */
404 /* HBB NEW 20031008: try whether reinstating signal handler
406 signal(sig, jumpback);
413 char *findresult = NULL; /* find function output */
414 BOOL funcexist = YES; /* find "function" error */
415 FINDINIT rc = NOERROR; /* findinit return code */
416 sighandler_t savesig; /* old value of signal */
417 FP f; /* searching function */
420 /* open the references found file for writing */
421 if (writerefsfound() == NO) {
424 /* find the pattern - stop on an interrupt */
425 if (linemode == NO) {
426 postmsg("Searching");
429 savesig = signal(SIGINT, jumpback);
430 if (sigsetjmp(env, 1) == 0) {
431 f = fields[field].findfcn;
432 if (f == findregexp || f == findstring) {
433 findresult = (*f)(Pattern);
435 if ((nonglobalrefs = myfopen(temp2, "wb")) == NULL) {
439 if ((rc = findinit(Pattern)) == NOERROR) {
440 (void) dbseek(0L); /* read the first block */
441 findresult = (*f)(Pattern);
442 if (f == findcalledby)
443 funcexist = (*findresult == 'y');
446 /* append the non-global references */
447 (void) fclose(nonglobalrefs);
448 if ((nonglobalrefs = myfopen(temp2, "rb"))
453 while ((c = getc(nonglobalrefs)) != EOF) {
454 (void) putc(c, refsfound);
457 (void) fclose(nonglobalrefs);
460 signal(SIGINT, savesig);
462 /* rewind the cross-reference file */
463 (void) lseek(symrefs, (long) 0, 0);
465 /* reopen the references found file for reading */
466 (void) fclose(refsfound);
467 if ((refsfound = myfopen(temp1, "rb")) == NULL) {
475 /* see if it is empty */
476 if ((c = getc(refsfound)) == EOF) {
477 if (findresult != NULL) {
478 (void) snprintf(lastmsg, sizeof(lastmsg), "Egrep %s in this pattern: %s",
479 findresult, Pattern);
480 } else if (rc == NOTSYMBOL) {
481 (void) snprintf(lastmsg, sizeof(lastmsg), "This is not a C symbol: %s",
483 } else if (rc == REGCMPERROR) {
484 (void) snprintf(lastmsg, sizeof(lastmsg), "Error in this regcomp(3) regular expression: %s",
487 } else if (funcexist == NO) {
488 (void) snprintf(lastmsg, sizeof(lastmsg), "Function definition does not exist: %s",
491 (void) snprintf(lastmsg, sizeof(lastmsg), "Could not find the %s: %s",
492 fields[field].text2, Pattern);
496 /* put back the character read */
497 (void) ungetc(c, refsfound);
499 /* HBB 20041027: this used to hold a copy of the code of
500 * countrefs(), but with the crucial display width adjustments
501 * missing. Just call the real thing instead! */
506 /* display search progress with default custom format */
509 progress(char *what, long current, long max)
513 char msg[MSGLEN + 1];
516 /* save the start time */
517 if (searchcount == 0) {
520 if ((now = time(NULL)) - start >= 1)
527 snprintf(msg, sizeof(msg), "%ld", current);
528 move(MSGLINE, (COLS / 2) - (strlen(msg) / 2));
530 snprintf(msg, sizeof(msg), "%ld", max);
531 move(MSGLINE, COLS - strlen(msg));
535 else if (verbosemode == YES)
537 snprintf(msg, sizeof(msg), "> %s %ld of %ld", what, current, max);
541 if ((linemode == NO) && (incurses == YES))
544 i = (float)COLS * (float)current / (float)max;
553 if (linemode == NO || verbosemode == YES)
559 /* print error message on system call failure */
564 char msg[MSGLEN + 1]; /* message */
571 if (errno < sys_nerr) {
572 s = sys_errlist[errno];
575 (void) snprintf(msg, sizeof(msg), "%s: %s", text, s);
579 /* postmsg clears the message line and prints the message */
585 if (linemode == YES || incurses == NO) {
586 (void) printf("%s\n", msg);
594 (void) strncpy(lastmsg, msg, sizeof(lastmsg) - 1);
597 /* clearmsg clears the first message line */
602 if (linemode == NO) {
608 /* clearmsg2 clears the second message line */
613 if (linemode == NO) {
614 move(MSGLINE + 1, 0);
619 /* postmsg2 clears the second message line and prints the message */
624 if (linemode == YES) {
625 (void) printf("%s\n", msg);
634 /* display an error mesg - stdout or on second msg line */
636 posterr(char *msg, ...)
642 if (linemode == YES || incurses == NO)
644 (void) vfprintf(stderr, msg, ap);
645 (void) fputc('\n', stderr);
647 vsnprintf(errbuf, sizeof(errbuf), msg, ap);
652 /* display a fatal error mesg -- stderr *after* shutting down curses */
654 postfatal(const char *msg, ...)
660 vsnprintf(errbuf, sizeof(errbuf), msg, ap);
661 /* restore the terminal to its original mode */
662 if (incurses == YES) {
666 /* display fatal error messages */
667 fprintf(stderr,"%s",errbuf);
673 \f/* position references found file at specified line */
676 seekline(unsigned int line)
680 /* verify that there is a references found file */
681 if (refsfound == NULL) {
684 /* go to the beginning of the file */
687 /* find the requested line */
689 while (nextline < line && (c = getc(refsfound)) != EOF) {
696 /* get the OGS subsystem and book names */
699 ogsnames(char *file, char **subsystem, char **book)
701 static char buf[PATHLEN + 1];
704 *subsystem = *book = "";
705 (void) strcpy(buf,file);
710 while ((slash = strchr(s, '/')) != NULL) {
712 if ((int)strlen(s) >= 3 && strncmp(slash - 3, ".ss", 3) == 0) {
715 if ((slash = strchr(s, '/')) != NULL) {
725 /* get the requested path components */
728 pathcomponents(char *path, int components)
733 s = path + strlen(path) - 1;
734 for (i = 0; i < components; ++i) {
735 while (s > path && *--s != '/') {
739 if (s > path && *s == '/') {
745 /* open the references found file for writing */
750 if (refsfound == NULL) {
751 if ((refsfound = myfopen(temp1, "wb")) == NULL) {
756 (void) fclose(refsfound);
757 if ( (refsfound = myfopen(temp1, "wb")) == NULL) {
758 postmsg("Cannot reopen temporary file");