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