Imported Upstream version 15.8a
[platform/upstream/cscope.git] / src / display.c
1 /*===========================================================================
2  Copyright (c) 1998-2000, The Santa Cruz Operation 
3  All rights reserved.
4  
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7
8  *Redistributions of source code must retain the above copyright notice,
9  this list of conditions and the following disclaimer.
10
11  *Redistributions in binary form must reproduce the above copyright notice,
12  this list of conditions and the following disclaimer in the documentation
13  and/or other materials provided with the distribution.
14
15  *Neither name of The Santa Cruz Operation nor the names of its contributors
16  may be used to endorse or promote products derived from this software
17  without specific prior written permission. 
18
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  INTERRUPTION)
27  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30  DAMAGE. 
31  =========================================================================*/
32
33 /*      cscope - interactive C symbol cross-reference
34  *
35  *      display functions
36  */
37
38 #include "global.h"
39 #include "build.h"
40 #include "alloc.h"
41
42 #ifdef CCS
43 #include "sgs.h"        /* ESG_PKG and ESG_REL */
44 #else
45 #include "version.h"    /* FILEVERSION and FIXVERSION */
46 #endif
47
48 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
49 #include <ncurses.h>
50 #else
51 #include <curses.h>
52 #endif
53 #include <setjmp.h>     /* jmp_buf */
54 #include <stdarg.h>     /* va_list stuff */
55 #include <time.h>
56 #include <errno.h>
57 #include <stdarg.h>
58
59 #ifndef HAVE_SIGSETJMP
60 # define sigsetjmp(a,b) setjmp(a)
61 # define siglongjmp(a,b) longjmp(a,b)
62 typedef jmp_buf sigjmp_buf;
63 #endif
64
65 static char const rcsid[] = "$Id: display.c,v 1.33 2012/05/20 12:24:17 broeker Exp $";
66
67 int     booklen;                /* OGS book name display field length */
68 int     *displine;              /* screen line of displayed reference */
69 unsigned int disprefs;          /* displayed references */
70 int     field;                  /* input field */
71 int     filelen;                /* file name display field length */
72 int     fcnlen;                 /* function name display field length */
73 unsigned int mdisprefs;         /* maximum displayed references */
74 unsigned int nextline;          /* next line to be shown */
75 FILE    *nonglobalrefs;         /* non-global references file */
76 int     numlen;                 /* line number display field length */
77 unsigned int topline = 1;               /* top line of page */
78 int     bottomline;             /* bottom line of page */
79 long    searchcount;            /* count of files searched */
80 int     subsystemlen;           /* OGS subsystem name display field length */
81 unsigned int totallines;        /* total reference lines */
82 unsigned fldcolumn;             /* input field column */
83
84 const char      dispchars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
85
86 static  int     fldline;                /* input field line */
87 static  sigjmp_buf      env;            /* setjmp/longjmp buffer */
88 static  int     lastdispline;           /* last displayed reference line */
89 static  char    lastmsg[MSGLEN + 1];    /* last message displayed */
90 static  char    helpstring[] = "Press the ? key for help";
91 static  char    selprompt[] = 
92         "Select lines to change (press the ? key for help): ";
93
94 typedef char * (*FP)(char *);   /* pointer to function returning a character pointer */
95
96 /* HBB 2000/05/05: I removed the casts to function pointer type. It is
97  * fundamentally unsafe to call a function through a pointer of a
98  * different type ('undefined behaviour' in the words of the ANSI/ISO
99  * C standard).  Instead, I made all the find...() functions adhere to
100  * the same function type, by changing argument passing a bit. */
101 static  struct  {               /* text of input fields */
102         char    *text1;
103         char    *text2;
104         FP      findfcn;
105 } fields[FIELDS + 1] = {        /* samuel has a search that is not part of the cscope display */
106         {"Find this", "C symbol",                       findsymbol},
107         {"Find this", "global definition",              finddef},
108         {"Find", "functions called by this function",   findcalledby},
109         {"Find", "functions calling this function",     findcalling},
110         {"Find this", "text string",                    findstring},
111         {"Change this", "text string",                  findstring},
112         {"Find this", "egrep pattern",                  findregexp},
113         {"Find this", "file",                           findfile},
114         {"Find", "files #including this file",          findinclude},
115         {"Find", "assignments to this symbol",          findassign},
116         {"Find all", "function definitions",            findallfcns},   /* samuel only */
117 };
118
119 /* Internal prototypes: */
120 static  RETSIGTYPE      jumpback(int sig);
121
122 /* initialize display parameters */
123
124 void
125 dispinit(void)
126 {
127         /* calculate the maximum displayed reference lines */
128         lastdispline = FLDLINE - 3;
129         mdisprefs = lastdispline - REFLINE + 1;
130
131
132         if (mdisprefs <= 0) {
133                 postfatal("%s: screen too small\n", argv0);
134                 /* NOTREACHED */
135         }
136
137         if (mouse == NO && mdisprefs > strlen(dispchars))
138                 mdisprefs = strlen(dispchars);
139
140         /* allocate the displayed line array */
141         displine = mymalloc(mdisprefs * sizeof(int));
142 }
143
144 /* display a page of the references */
145
146 void
147 display(void)
148 {
149     char    *subsystem;             /* OGS subsystem name */
150     char    *book;                  /* OGS book name */
151     char    file[PATHLEN + 1];      /* file name */
152     char    function[PATLEN + 1];   /* function name */
153     char    linenum[NUMLEN + 1];    /* line number */
154     int     screenline;             /* screen line number */
155     int     width;                  /* source line display width */
156     int     i;
157     char    *s;
158
159     /* see if this is the initial display */
160     erase();
161     if (refsfound == NULL) {
162 #if CCS
163         if (displayversion == YES) {
164             printw("cscope %s", ESG_REL);
165         }
166         else {
167             printw("cscope");
168         }
169 #else
170         printw("Cscope version %d%s", FILEVERSION, FIXVERSION);
171 #endif
172         move(0, COLS - (int) sizeof(helpstring));
173         addstr(helpstring);
174     } else if (totallines == 0) {
175         /* if no references were found */
176         /* redisplay the last message */
177         addstr(lastmsg);
178     } else {
179         /* display the pattern */
180         if (changing == YES) {
181             printw("Change \"%s\" to \"%s\"", Pattern, newpat);
182         } else {
183             printw("%c%s: %s", toupper((unsigned char)fields[field].text2[0]),
184                    fields[field].text2 + 1, Pattern);
185         }
186         /* display the column headings */
187         move(2, 2);
188         if (ogs == YES && field != FILENAME) {
189             printw("%-*s ", subsystemlen, "Subsystem");
190             printw("%-*s ", booklen, "Book");
191         }
192         if (dispcomponents > 0)
193             printw("%-*s ", filelen, "File");
194
195         if (field == SYMBOL || field == CALLEDBY || field == CALLING) {
196             printw("%-*s ", fcnlen, "Function");
197         }
198         if (field != FILENAME) {
199             addstr("Line");
200         }
201         addch('\n');
202
203         /* if at end of file go back to beginning */
204         if (nextline > totallines) {
205             seekline(1);
206         }
207         /* calculate the source text column */
208
209         width = COLS - numlen - 3;
210
211         if (ogs == YES) {
212             width -= subsystemlen + booklen + 2;
213         }
214         if (dispcomponents > 0) {
215             width -= filelen + 1;
216         }
217         if (field == SYMBOL || field == CALLEDBY || field == CALLING) {
218             width -= fcnlen + 1;
219         }
220
221         /* until the max references have been displayed or 
222            there is no more room */
223         topline = nextline;
224         for (disprefs = 0, screenline = REFLINE;
225              disprefs < mdisprefs && screenline <= lastdispline;
226              ++disprefs, ++screenline) {
227             /* read the reference line */
228             if (fscanf(refsfound, "%" PATHLEN_STR "s%" PATHLEN_STR "s%" NUMLEN_STR "s %" TEMPSTRING_LEN_STR "[^\n]", file, function, 
229                        linenum, tempstring) < 4) {
230                 break;
231             }
232             ++nextline;
233             displine[disprefs] = screenline;
234                         
235             /* if no mouse, display the selection number */
236             if (mouse == YES) {
237                 addch(' ');
238             } else {
239                 printw("%c", dispchars[disprefs]);
240             }
241
242             /* display any change mark */
243             if (changing == YES && 
244                 change[topline + disprefs - 1] == YES) {
245                 addch('>');
246             } else {
247                 addch(' ');
248             }
249
250             /* display the file name */
251             if (field == FILENAME) {
252                 printw("%-*s ", filelen, file);
253             } else {
254                 /* if OGS, display the subsystem and book names */
255                 if (ogs == YES) {
256                     ogsnames(file, &subsystem, &book);
257                     printw("%-*.*s ", subsystemlen, subsystemlen, subsystem);
258                     printw("%-*.*s ", booklen, booklen, book);
259                 }
260                 /* display the requested path components */
261                 if (dispcomponents > 0) {
262                     printw("%-*.*s ", filelen, filelen,
263                            pathcomponents(file, dispcomponents));
264                 }
265             } /* else(field == FILENAME) */
266
267             /* display the function name */
268             if (field == SYMBOL || field == CALLEDBY || field == CALLING) {
269                 printw("%-*.*s ", fcnlen, fcnlen, function);
270             }
271             if (field == FILENAME) {
272                 addch('\n');    /* go to next line */
273                 continue;
274             }
275
276             /* display the line number */
277             printw("%*s ", numlen, linenum);
278             /* there may be tabs in egrep output */
279             while ((s = strchr(tempstring, '\t')) != NULL) {
280                 *s = ' ';
281             }
282
283             /* display the source line */
284             s = tempstring;
285             for (;;) {
286                 /* see if the source line will fit */
287                 if ((i = strlen(s)) > width) {
288                                         
289                     /* find the nearest blank */
290                     for (i = width; s[i] != ' ' && i > 0; --i) {
291                         ;
292                     }
293                     if (i == 0) {
294                         i = width;      /* no blank */
295                     }
296                 }
297                 /* print up to this point */
298                 printw("%.*s", i, s);
299                 s += i;
300                                 
301                 /* if line didn't wrap around */
302                 if (i < width) {
303                     addch('\n');        /* go to next line */
304                 }
305                 /* skip blanks */
306                 while (*s == ' ') {
307                     ++s;
308                 }
309                 /* see if there is more text */
310                 if (*s == '\0') {
311                     break;
312                 }
313                 /* if the source line is too long */
314                 if (++screenline > lastdispline) {
315
316                     /* if this is the first displayed line,
317                        display what will fit on the screen */
318                     if (topline == nextline -1) {
319                         disprefs++;
320                         /* break out of two loops */
321                         goto endrefs;
322                     }
323                                         
324                     /* erase the reference */
325                     while (--screenline >= displine[disprefs]) {
326                         move(screenline, 0);
327                         clrtoeol();
328                     }
329                     ++screenline;
330                                          
331                     /* go back to the beginning of this reference */
332                     --nextline;
333                     seekline(nextline);
334                     goto endrefs;
335                 }
336                 /* indent the continued source line */
337                 move(screenline, COLS - width);
338             } /* for(ever) */
339         } /* for(reference output lines) */
340     endrefs:
341         /* position the cursor for the message */
342         i = FLDLINE - 1;
343         if (screenline < i) {
344             addch('\n');
345         }
346         else {
347             move(i, 0);
348         }
349         /* check for more references */
350         i = totallines - nextline + 1;
351         bottomline = nextline;
352         if (i > 0) {
353             printw("* Lines %d-%d of %d, %d more - press the space bar to display more *", topline, bottomline, totallines, i);
354         }
355         /* if this is the last page of references */
356         else if (topline > 1 && nextline > totallines) {
357             addstr("* Press the space bar to display the first lines again *");
358         }
359     }
360     /* display the input fields */
361     move(FLDLINE, 0);
362     for (i = 0; i < FIELDS; ++i) {
363         printw("%s %s:\n", fields[i].text1, fields[i].text2);
364     }
365     /* display any prompt */
366     if (changing == YES) {
367         move(PRLINE, 0);
368         addstr(selprompt);
369     }
370     drawscrollbar(topline, nextline);   /* display the scrollbar */
371     refresh();
372 }
373
374 /* set the cursor position for the field */
375 void
376 setfield(void)
377 {
378         fldline = FLDLINE + field;
379         fldcolumn = strlen(fields[field].text1) + strlen(fields[field].text2) + 3;
380 }
381
382 /* move to the current input field */
383
384 void
385 atfield(void)
386 {
387         move(fldline, fldcolumn);
388 }
389
390 /* move to the changing lines prompt */
391
392 void
393 atchange(void)
394 {
395         move(PRLINE, (int) sizeof(selprompt) - 1);
396 }
397
398 /* search for the symbol or text pattern */
399
400 /*ARGSUSED*/
401 static RETSIGTYPE
402 jumpback(int sig)
403 {
404         /* HBB NEW 20031008: try whether reinstating signal handler
405          * helps... */
406         signal(sig, jumpback);
407         siglongjmp(env, 1);
408 }
409
410 BOOL
411 search(void)
412 {
413         char    *findresult = NULL;     /* find function output */
414         BOOL    funcexist = YES;                /* find "function" error */
415         FINDINIT rc = NOERROR;          /* findinit return code */
416         sighandler_t savesig;           /* old value of signal */
417         FP      f;                      /* searching function */
418         int     c;
419         
420         /* open the references found file for writing */
421         if (writerefsfound() == NO) {
422                 return(NO);
423         }
424         /* find the pattern - stop on an interrupt */
425         if (linemode == NO) {
426                 postmsg("Searching");
427         }
428         searchcount = 0;
429         savesig = signal(SIGINT, jumpback);
430         if (sigsetjmp(env, 1) == 0) {
431                 f = fields[field].findfcn;
432                 if (f == findregexp || f == findstring) {
433                         findresult = (*f)(Pattern);
434                 } else {
435                         if ((nonglobalrefs = myfopen(temp2, "wb")) == NULL) {
436                                 cannotopen(temp2);
437                                 return(NO);
438                         }
439                         if ((rc = findinit(Pattern)) == NOERROR) {
440                                 (void) dbseek(0L); /* read the first block */
441                                 findresult = (*f)(Pattern);
442                                 if (f == findcalledby) 
443                                         funcexist = (*findresult == 'y');
444                                 findcleanup();
445
446                                 /* append the non-global references */
447                                 (void) fclose(nonglobalrefs);
448                                 if ((nonglobalrefs = myfopen(temp2, "rb"))
449                                      == NULL) {
450                                         cannotopen(temp2);
451                                         return(NO);
452                                 }
453                                 while ((c = getc(nonglobalrefs)) != EOF) {
454                                         (void) putc(c, refsfound);
455                                 }
456                         }
457                         (void) fclose(nonglobalrefs);
458                 }
459         }
460         signal(SIGINT, savesig);
461
462         /* rewind the cross-reference file */
463         (void) lseek(symrefs, (long) 0, 0);
464         
465         /* reopen the references found file for reading */
466         (void) fclose(refsfound);
467         if ((refsfound = myfopen(temp1, "rb")) == NULL) {
468                 cannotopen(temp1);
469                 return(NO);
470         }
471         nextline = 1;
472         totallines = 0;
473         disprefs = 0;
474         
475         /* see if it is empty */
476         if ((c = getc(refsfound)) == EOF) {
477                 if (findresult != NULL) {
478                         (void) snprintf(lastmsg, sizeof(lastmsg), "Egrep %s in this pattern: %s", 
479                                        findresult, Pattern);
480                 } else if (rc == NOTSYMBOL) {
481                         (void) snprintf(lastmsg, sizeof(lastmsg), "This is not a C symbol: %s", 
482                                        Pattern);
483                 } else if (rc == REGCMPERROR) {
484                         (void) snprintf(lastmsg, sizeof(lastmsg), "Error in this regcomp(3) regular expression: %s", 
485                                        Pattern);
486                         
487                 } else if (funcexist == NO) {
488                         (void) snprintf(lastmsg, sizeof(lastmsg), "Function definition does not exist: %s", 
489                                        Pattern);
490                 } else {
491                         (void) snprintf(lastmsg, sizeof(lastmsg), "Could not find the %s: %s", 
492                                        fields[field].text2, Pattern);
493                 }
494                 return(NO);
495         }
496         /* put back the character read */
497         (void) ungetc(c, refsfound);
498
499         /* HBB 20041027: this used to hold a copy of the code of 
500          * countrefs(), but with the crucial display width adjustments
501          * missing.  Just call the real thing instead! */
502         countrefs();
503         return(YES);
504 }
505
506 /* display search progress with default custom format */
507
508 void
509 progress(char *what, long current, long max)
510 {
511         static  long    start;
512         long    now;
513         char    msg[MSGLEN + 1];
514         int     i;
515
516         /* save the start time */
517         if (searchcount == 0) {
518                 start = time(NULL);
519         }
520         if ((now = time(NULL)) - start >= 1)
521         {
522                 if (linemode == NO)
523                 {
524                         move(MSGLINE, 0);
525                         clrtoeol();
526                         addstr(what);
527                         snprintf(msg, sizeof(msg), "%ld", current);
528                         move(MSGLINE, (COLS / 2) - (strlen(msg) / 2));
529                         addstr(msg);
530                         snprintf(msg, sizeof(msg), "%ld", max);
531                         move(MSGLINE, COLS - strlen(msg));
532                         addstr(msg);
533                         refresh();
534                 }
535                 else if (verbosemode == YES)
536                 {
537                         snprintf(msg, sizeof(msg), "> %s %ld of %ld", what, current, max);
538                 }
539
540                 start = now;
541                 if ((linemode == NO) && (incurses == YES))
542                 {
543                         move(MSGLINE, 0);
544                         i = (float)COLS * (float)current / (float)max;
545
546                         standout();
547                         for (; i > 0; i--)
548                                 addch(inch());
549                         standend();
550                         refresh();
551                 }
552                 else
553                         if (linemode == NO || verbosemode == YES)
554                                 postmsg(msg);
555         }
556         ++searchcount;
557 }
558
559 /* print error message on system call failure */
560
561 void
562 myperror(char *text) 
563 {
564         char    msg[MSGLEN + 1];        /* message */
565         char    *s;
566
567         s = "Unknown error";
568 #ifdef HAVE_STRERROR
569         s = strerror(errno);
570 #else
571         if (errno < sys_nerr) {
572                 s = sys_errlist[errno];
573         }
574 #endif
575         (void) snprintf(msg, sizeof(msg), "%s: %s", text, s);
576         postmsg(msg);
577 }
578
579 /* postmsg clears the message line and prints the message */
580
581 /* VARARGS */
582 void
583 postmsg(char *msg) 
584 {
585         if (linemode == YES || incurses == NO) {
586                 (void) printf("%s\n", msg);
587                 fflush(stdout);
588         }
589         else {
590                 clearmsg();
591                 addstr(msg);
592                 refresh();
593         }
594         (void) strncpy(lastmsg, msg, sizeof(lastmsg) - 1);
595 }
596
597 /* clearmsg clears the first message line */
598
599 void
600 clearmsg(void)
601 {
602         if (linemode == NO) {
603                 move(MSGLINE, 0);
604                 clrtoeol();
605         }
606 }
607
608 /* clearmsg2 clears the second message line */
609
610 void
611 clearmsg2(void)
612 {
613         if (linemode == NO) {
614                 move(MSGLINE + 1, 0);
615                 clrtoeol();
616         }
617 }
618
619 /* postmsg2 clears the second message line and prints the message */
620
621 void
622 postmsg2(char *msg) 
623 {
624         if (linemode == YES) {
625                 (void) printf("%s\n", msg);
626         }
627         else {
628                 clearmsg2();
629                 addstr(msg);
630                 refresh();
631         }
632 }
633
634 /* display an error mesg - stdout or on second msg line */
635 void
636 posterr(char *msg, ...) 
637 {
638     va_list ap;
639     char errbuf[MSGLEN];
640     
641     va_start(ap, msg);
642     if (linemode == YES || incurses == NO)
643     {
644         (void) vfprintf(stderr, msg, ap); 
645         (void) fputc('\n', stderr);
646     } else {
647         vsnprintf(errbuf, sizeof(errbuf), msg, ap);
648         postmsg2(errbuf); 
649     }
650 }
651
652 /* display a fatal error mesg -- stderr *after* shutting down curses */
653 void
654 postfatal(const char *msg, ...)
655 {
656         va_list ap;
657         char errbuf[MSGLEN];
658
659         va_start(ap, msg);
660         vsnprintf(errbuf, sizeof(errbuf), msg, ap);
661         /* restore the terminal to its original mode */
662         if (incurses == YES) {
663                 exitcurses();
664         }
665
666         /* display fatal error messages */
667         fprintf(stderr,"%s",errbuf);
668
669         /* shut down */
670         myexit(1);
671 }
672
673 \f/* position references found file at specified line */
674
675 void
676 seekline(unsigned int line) 
677 {
678         int     c;
679
680         /* verify that there is a references found file */
681         if (refsfound == NULL) {
682                 return;
683         }
684         /* go to the beginning of the file */
685         rewind(refsfound);
686         
687         /* find the requested line */
688         nextline = 1;
689         while (nextline < line && (c = getc(refsfound)) != EOF) {
690                 if (c == '\n') {
691                         nextline++;
692                 }
693         }
694 }
695
696 /* get the OGS subsystem and book names */
697
698 void
699 ogsnames(char *file, char **subsystem, char **book)
700 {
701         static  char    buf[PATHLEN + 1];
702         char    *s, *slash;
703
704         *subsystem = *book = "";
705         (void) strcpy(buf,file);
706         s = buf;
707         if (*s == '/') {
708                 ++s;
709         }
710         while ((slash = strchr(s, '/')) != NULL) {
711                 *slash = '\0';
712                 if ((int)strlen(s) >= 3 && strncmp(slash - 3, ".ss", 3) == 0) {
713                         *subsystem = s;
714                         s = slash + 1;
715                         if ((slash = strchr(s, '/')) != NULL) {
716                                 *book = s;
717                                 *slash = '\0';
718                         }
719                         break;
720                 }
721                 s = slash + 1;
722         }
723 }
724
725 /* get the requested path components */
726
727 char *
728 pathcomponents(char *path, int components)
729 {
730         int     i;
731         char    *s;
732         
733         s = path + strlen(path) - 1;
734         for (i = 0; i < components; ++i) {
735                 while (s > path && *--s != '/') {
736                         ;
737                 }
738         }
739         if (s > path && *s == '/') {
740                 ++s;
741         }
742         return(s);
743 }
744
745 /* open the references found file for writing */
746
747 BOOL
748 writerefsfound(void)
749 {
750         if (refsfound == NULL) {
751                 if ((refsfound = myfopen(temp1, "wb")) == NULL) {
752                         cannotopen(temp1);
753                         return(NO);
754                 }
755         } else {
756                 (void) fclose(refsfound);
757                 if ( (refsfound = myfopen(temp1, "wb")) == NULL) {
758                         postmsg("Cannot reopen temporary file");
759                         return(NO);
760                 }
761         }
762         return(YES);
763 }