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 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 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 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 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 379 jumpback(int sig) 380 { 381 longjmp(env, 1); 382 } 383 384 BOOL 385 search(void) 386 { 387 char *volatile egreperror = NULL; /* egrep error message */ 388 FINDINIT volatile 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(3C) 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 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 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 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 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 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 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 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 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 * 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