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 }