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 }