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