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
dispinit(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
display(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
setfield(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
atfield(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
jumpback(int sig)379 jumpback(int sig)
380 {
381 longjmp(env, 1);
382 }
383
384 BOOL
search(void)385 search(void)
386 {
387 char *egreperror = NULL; /* egrep error message */
388 FINDINIT 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
writerefsfound(void)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
countrefs(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
myperror(char * text)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
putmsg(char * msg)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
clearmsg2(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
putmsg2(char * msg)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
seekline(int line)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
ogsnames(char * file,char ** subsystem,char ** book)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 *
pathcomponents(char * path,int components)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