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 /*
32 * cscope - interactive C symbol cross-reference
33 *
34 * display functions
35 */
36
37 #include "global.h"
38 #include "version.h" /* FILEVERSION and FIXVERSION */
39 #include <curses.h> /* COLS and LINES */
40 #include <setjmp.h> /* jmp_buf */
41 #include <string.h>
42 #include <errno.h>
43
44 /* see if the function column should be displayed */
45 #define displayfcn() (field <= ASSIGN)
46
47 #define MINCOLS 68 /* minimum columns for 3 digit Lines message numbers */
48
49 int *displine; /* screen line of displayed reference */
50 int disprefs; /* displayed references */
51 int field; /* input field */
52 unsigned fldcolumn; /* input field column */
53 int mdisprefs; /* maximum displayed references */
54 int selectlen; /* selection number field length */
55 int nextline; /* next line to be shown */
56 int topline = 1; /* top line of page */
57 int bottomline; /* bottom line of page */
58 int totallines; /* total reference lines */
59 FILE *refsfound; /* references found file */
60 FILE *nonglobalrefs; /* non-global references file */
61
62 static int fldline; /* input field line */
63 static int subsystemlen; /* OGS subsystem name display */
64 /* field length */
65 static int booklen; /* OGS book name display field length */
66 static int filelen; /* file name display field length */
67 static int fcnlen; /* function name display field length */
68 static jmp_buf env; /* setjmp/longjmp buffer */
69 static int lastdispline; /* last displayed reference line */
70 static char lastmsg[MSGLEN + 1]; /* last message displayed */
71 static int numlen; /* line number display field length */
72 static char depthstring[] = "Depth: ";
73 static char helpstring[] = "Press the ? key for help";
74
75
76 typedef char *(*FP)(); /* pointer to function returning a character pointer */
77
78 static struct {
79 char *text1;
80 char *text2;
81 FP findfcn;
82 enum {
83 EGREP,
84 REGCMP
85 } patterntype;
86 } fields[FIELDS + 1] = {
87 /* last search is not part of the cscope display */
88 { "Find this", "C symbol",
89 (FP) findsymbol, REGCMP},
90 { "Find this", "definition",
91 (FP) finddef, REGCMP},
92 { "Find", "functions called by this function",
93 (FP) findcalledby, REGCMP},
94 { "Find", "functions calling this function",
95 (FP) findcalling, REGCMP},
96 { "Find", "assignments to",
97 (FP) findassignments, REGCMP},
98 { "Change this", "grep pattern",
99 findgreppat, EGREP},
100 { "Find this", "egrep pattern",
101 findegreppat, EGREP},
102 { "Find this", "file",
103 (FP) findfile, REGCMP},
104 { "Find", "files #including this file",
105 (FP) findinclude, REGCMP},
106 { "Find all", "function/class definitions",
107 (FP) findallfcns, REGCMP},
108 };
109
110 /* initialize display parameters */
111
112 void
113 dispinit(void)
114 {
115 /* calculate the maximum displayed reference lines */
116 lastdispline = FLDLINE - 2;
117 mdisprefs = lastdispline - REFLINE + 1;
118 if (mdisprefs <= 0) {
119 (void) printw("cscope: window must be at least %d lines high",
120 FIELDS + 6);
121 myexit(1);
122 }
123 if (COLS < MINCOLS) {
124 (void) printw("cscope: window must be at least %d columns wide",
125 MINCOLS);
126 myexit(1);
127 }
128 if (!mouse) {
129 if (returnrequired == NO && mdisprefs > 9) {
130 mdisprefs = 9; /* single digit selection number */
131 }
132 /* calculate the maximum selection number width */
133 (void) sprintf(newpat, "%d", mdisprefs);
134 selectlen = strlen(newpat);
135 }
136 /* allocate the displayed line array */
137 displine = (int *)mymalloc(mdisprefs * sizeof (int));
138 }
139
140 /* display a page of the references */
141
142 void
143 display(void)
144 {
145 char *subsystem; /* OGS subsystem name */
146 char *book; /* OGS book name */
147 char file[PATHLEN + 1]; /* file name */
148 char function[PATLEN + 1]; /* function name */
149 char linenum[NUMLEN + 1]; /* line number */
150 int screenline; /* screen line number */
151 int width; /* source line display width */
152 int i;
153 char *s;
154
155 (void) erase();
156
157 /* if there are no references */
158 if (totallines == 0) {
159 if (*lastmsg != '\0') {
160 (void) addstr(lastmsg); /* redisplay any message */
161 } else {
162 (void) printw("Cscope version %d%s", FILEVERSION,
163 FIXVERSION);
164 (void) move(0, COLS - (int)sizeof (helpstring));
165 (void) addstr(helpstring);
166 }
167 } else { /* display the pattern */
168 if (changing == YES) {
169 (void) printw("Change \"%s\" to \"%s\"",
170 pattern, newpat);
171 } else {
172 (void) printw("%c%s: %s",
173 toupper(fields[field].text2[0]),
174 fields[field].text2 + 1, pattern);
175 }
176 /* display the cscope invocation nesting depth */
177 if (cscopedepth > 1) {
178 (void) move(0, COLS - (int)sizeof (depthstring) - 2);
179 (void) addstr(depthstring);
180 (void) printw("%d", cscopedepth);
181 }
182 /* display the column headings */
183 (void) move(2, selectlen + 1);
184 if (ogs == YES && field != FILENAME) {
185 (void) printw("%-*s ", subsystemlen, "Subsystem");
186 (void) printw("%-*s ", booklen, "Book");
187 }
188 if (dispcomponents > 0) {
189 (void) printw("%-*s ", filelen, "File");
190 }
191 if (displayfcn()) {
192 (void) printw("%-*s ", fcnlen, "Function");
193 }
194 if (field != FILENAME) {
195 (void) addstr("Line");
196 }
197 (void) addch('\n');
198
199 /* if at end of file go back to beginning */
200 if (nextline > totallines) {
201 seekline(1);
202 }
203 /* calculate the source text column */
204 width = COLS - selectlen - numlen - 2;
205 if (ogs == YES) {
206 width -= subsystemlen + booklen + 2;
207 }
208 if (dispcomponents > 0) {
209 width -= filelen + 1;
210 }
211 if (displayfcn()) {
212 width -= fcnlen + 1;
213 }
214 /*
215 * until the max references have been displayed or
216 * there is no more room
217 */
218 topline = nextline;
219 for (disprefs = 0, screenline = REFLINE;
220 disprefs < mdisprefs && screenline <= lastdispline;
221 ++disprefs, ++screenline) {
222 /* read the reference line */
223 if (fscanf(refsfound, "%s%s%s %[^\n]", file, function,
224 linenum, yytext) < 4) {
225 break;
226 }
227 ++nextline;
228 displine[disprefs] = screenline;
229
230 /* if no mouse, display the selection number */
231 if (!mouse) {
232 (void) printw("%*d", selectlen, disprefs + 1);
233 }
234 /* display any change mark */
235 if (changing == YES &&
236 change[topline + disprefs - 1] == YES) {
237 (void) addch('>');
238 } else {
239 (void) addch(' ');
240 }
241 /* display the file name */
242 if (field == FILENAME) {
243 (void) printw("%-.*s\n", COLS - 3, file);
244 continue;
245 }
246 /* if OGS, display the subsystem and book names */
247 if (ogs == YES) {
248 ogsnames(file, &subsystem, &book);
249 (void) printw("%-*.*s ", subsystemlen,
250 subsystemlen, subsystem);
251 (void) printw("%-*.*s ", booklen, booklen,
252 book);
253 }
254 /* display the requested path components */
255 if (dispcomponents > 0) {
256 (void) printw("%-*.*s ", filelen, filelen,
257 pathcomponents(file, dispcomponents));
258 }
259 /* display the function name */
260 if (displayfcn()) {
261 (void) printw("%-*.*s ", fcnlen, fcnlen,
262 function);
263 }
264 /* display the line number */
265 (void) printw("%*s ", numlen, linenum);
266
267 /* there may be tabs in egrep output */
268 while ((s = strchr(yytext, '\t')) != NULL) {
269 *s = ' ';
270 }
271 /* display the source line */
272 s = yytext;
273 for (;;) {
274 /* see if the source line will fit */
275 if ((i = strlen(s)) > width) {
276 /* find the nearest blank */
277 for (i = width; s[i] != ' ' && i > 0;
278 --i) {
279 }
280 if (i == 0) {
281 i = width; /* no blank */
282 }
283 }
284 /* print up to this point */
285 (void) printw("%.*s", i, s);
286 s += i;
287
288 /* if line didn't wrap around */
289 if (i < width) {
290 /* go to next line */
291 (void) addch('\n');
292 }
293 /* skip blanks */
294 while (*s == ' ') {
295 ++s;
296 }
297 /* see if there is more text */
298 if (*s == '\0') {
299 break;
300 }
301 /* if the source line is too long */
302 if (++screenline > lastdispline) {
303 /*
304 * if this is the first displayed line,
305 * display what will fit on the screen
306 */
307 if (topline == nextline - 1) {
308 goto endrefs;
309 }
310 /* erase the reference */
311 while (--screenline >=
312 displine[disprefs]) {
313 (void) move(screenline, 0);
314 (void) clrtoeol();
315 }
316 ++screenline;
317
318 /*
319 * go back to the beginning of this
320 * reference
321 */
322 --nextline;
323 seekline(nextline);
324 goto endrefs;
325 }
326 /* indent the continued source line */
327 (void) move(screenline, COLS - width);
328 }
329
330 }
331 endrefs:
332 /* check for more references */
333 bottomline = nextline;
334 if (bottomline - topline < totallines) {
335 (void) move(FLDLINE - 1, 0);
336 (void) standout();
337 (void) printw("%*s", selectlen + 1, "");
338 if (bottomline - 1 == topline) {
339 (void) printw("Line %d", topline);
340 } else {
341 (void) printw("Lines %d-%d", topline,
342 bottomline - 1);
343 }
344 (void) printw(" of %d, press the space bar to "
345 "display next lines", totallines);
346 (void) standend();
347 }
348 }
349 /* display the input fields */
350 (void) move(FLDLINE, 0);
351 for (i = 0; i < FIELDS; ++i) {
352 (void) printw("%s %s:\n", fields[i].text1, fields[i].text2);
353 }
354 drawscrollbar(topline, nextline, totallines);
355 }
356
357 /* set the cursor position for the field */
358 void
359 setfield(void)
360 {
361 fldline = FLDLINE + field;
362 fldcolumn = strlen(fields[field].text1) +
363 strlen(fields[field].text2) + 3;
364 }
365
366 /* move to the current input field */
367
368 void
369 atfield(void)
370 {
371 (void) move(fldline, (int)fldcolumn);
372 }
373
374 /* search for the symbol or text pattern */
375
376 /*ARGSUSED*/
377 SIGTYPE
378 jumpback(int sig)
379 {
380 longjmp(env, 1);
381 }
382
383 BOOL
384 search(void)
385 {
386 char *egreperror = NULL; /* egrep error message */
387 FINDINIT rc = NOERROR; /* findinit return code */
388 SIGTYPE (*savesig)(); /* old value of signal */
389 FP f; /* searching function */
390 char *s;
391 int c;
392
393 /* note: the pattern may have been a cscope argument */
394 if (caseless == YES) {
395 for (s = pattern; *s != '\0'; ++s) {
396 *s = tolower(*s);
397 }
398 }
399 /* open the references found file for writing */
400 if (writerefsfound() == NO) {
401 return (NO);
402 }
403 /* find the pattern - stop on an interrupt */
404 if (linemode == NO) {
405 putmsg("Searching");
406 }
407 initprogress();
408 if (setjmp(env) == 0) {
409 savesig = signal(SIGINT, jumpback);
410 f = fields[field].findfcn;
411 if (fields[field].patterntype == EGREP) {
412 egreperror = (*f)(pattern);
413 } else {
414 if ((nonglobalrefs = fopen(temp2, "w")) == NULL) {
415 cannotopen(temp2);
416 return (NO);
417 }
418 if ((rc = findinit()) == NOERROR) {
419 (void) dbseek(0L); /* goto the first block */
420 (*f)();
421 findcleanup();
422
423 /* append the non-global references */
424 (void) freopen(temp2, "r", nonglobalrefs);
425 while ((c = getc(nonglobalrefs)) != EOF) {
426 (void) putc(c, refsfound);
427 }
428 }
429 (void) fclose(nonglobalrefs);
430 }
431 }
432 (void) signal(SIGINT, savesig);
433 /* reopen the references found file for reading */
434 (void) freopen(temp1, "r", refsfound);
435 nextline = 1;
436 totallines = 0;
437
438 /* see if it is empty */
439 if ((c = getc(refsfound)) == EOF) {
440 if (egreperror != NULL) {
441 (void) sprintf(lastmsg, "Egrep %s in this pattern: %s",
442 egreperror, pattern);
443 } else if (rc == NOTSYMBOL) {
444 (void) sprintf(lastmsg, "This is not a C symbol: %s",
445 pattern);
446 } else if (rc == REGCMPERROR) {
447 (void) sprintf(lastmsg,
448 "Error in this regcmp(3X) regular expression: %s",
449 pattern);
450 } else {
451 (void) sprintf(lastmsg, "Could not find the %s: %s",
452 fields[field].text2, pattern);
453 }
454 return (NO);
455 }
456 /* put back the character read */
457 (void) ungetc(c, refsfound);
458
459 countrefs();
460 return (YES);
461 }
462
463 /* open the references found file for writing */
464
465 BOOL
466 writerefsfound(void)
467 {
468 if (refsfound == NULL) {
469 if ((refsfound = fopen(temp1, "w")) == NULL) {
470 cannotopen(temp1);
471 return (NO);
472 }
473 } else if (freopen(temp1, "w", refsfound) == NULL) {
474 putmsg("Cannot reopen temporary file");
475 return (NO);
476 }
477 return (YES);
478 }
479
480 /* count the references found */
481
482 void
483 countrefs(void)
484 {
485 char *subsystem; /* OGS subsystem name */
486 char *book; /* OGS book name */
487 char file[PATHLEN + 1]; /* file name */
488 char function[PATLEN + 1]; /* function name */
489 char linenum[NUMLEN + 1]; /* line number */
490 int i;
491
492 /*
493 * count the references found and find the length of the file,
494 * function, and line number display fields
495 */
496 subsystemlen = 9; /* strlen("Subsystem") */
497 booklen = 4; /* strlen("Book") */
498 filelen = 4; /* strlen("File") */
499 fcnlen = 8; /* strlen("Function") */
500 numlen = 0;
501 while ((i = fscanf(refsfound, "%250s%250s%6s %5000[^\n]", file,
502 function, linenum, yytext)) != EOF) {
503 if (i != 4 || !isgraph(*file) ||
504 !isgraph(*function) || !isdigit(*linenum)) {
505 putmsg("File does not have expected format");
506 totallines = 0;
507 return;
508 }
509 if ((i = strlen(pathcomponents(file,
510 dispcomponents))) > filelen) {
511 filelen = i;
512 }
513 if (ogs == YES) {
514 ogsnames(file, &subsystem, &book);
515 if ((i = strlen(subsystem)) > subsystemlen) {
516 subsystemlen = i;
517 }
518 if ((i = strlen(book)) > booklen) {
519 booklen = i;
520 }
521 }
522 if ((i = strlen(function)) > fcnlen) {
523 fcnlen = i;
524 }
525 if ((i = strlen(linenum)) > numlen) {
526 numlen = i;
527 }
528 ++totallines;
529 }
530 rewind(refsfound);
531
532 /* restrict the width of displayed columns */
533 i = (COLS - 5) / 3;
534 if (ogs == YES) {
535 i = (COLS - 7) / 5;
536 }
537 if (filelen > i && i > 4) {
538 filelen = i;
539 }
540 if (subsystemlen > i && i > 9) {
541 subsystemlen = i;
542 }
543 if (booklen > i && i > 4) {
544 booklen = i;
545 }
546 if (fcnlen > i && i > 8) {
547 fcnlen = i;
548 }
549 }
550
551 /* print error message on system call failure */
552
553 void
554 myperror(char *text)
555 {
556 char msg[MSGLEN + 1]; /* message */
557
558 (void) sprintf(msg, "%s: %s", text, strerror(errno));
559 putmsg(msg);
560 }
561
562 /* putmsg clears the message line and prints the message */
563
564 void
565 putmsg(char *msg)
566 {
567 if (incurses == NO) {
568 char *str = stralloc(msg);
569 *str = tolower(*str);
570 (void) fprintf(stderr, "cscope: %s\n", str);
571 (void) free(str);
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 }