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 }