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    *egreperror = NULL;     /* egrep error message */
 388         FINDINIT 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(3X) 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 }