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