Imported Upstream version 15.8a
[platform/upstream/cscope.git] / src / command.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 or text cross-reference
34  *
35  *      command functions
36  */
37
38 #include "global.h"
39 #include "build.h"              /* for rebuild() */
40 #include "alloc.h"
41
42 #include <stdlib.h>
43 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
44 #include <ncurses.h>
45 #else
46 #include <curses.h>
47 #endif
48 #include <ctype.h>
49
50 static char const rcsid[] = "$Id: command.c,v 1.36 2012/05/20 13:58:53 broeker Exp $";
51
52
53 int     selecting;
54 unsigned int   curdispline = 0;
55
56 BOOL    caseless;               /* ignore letter case when searching */
57 BOOL    *change;                /* change this line */
58 BOOL    changing;               /* changing text */
59 char    newpat[PATLEN + 1];     /* new pattern */
60 /* HBB 20040430: renamed to avoid lots of clashes with function arguments
61  * also named 'pattern' */
62 char    Pattern[PATLEN + 1];    /* symbol or text pattern */
63
64 /* HBB FIXME 20060419: these should almost certainly be const */
65 static  char    appendprompt[] = "Append to file: ";
66 static  char    pipeprompt[] = "Pipe to shell command: ";
67 static  char    readprompt[] = "Read from file: ";
68 static  char    toprompt[] = "To: ";
69
70
71 /* Internal prototypes: */
72 static  BOOL    changestring(void);
73 static  void    clearprompt(void);
74 static  void    mark(unsigned int i);
75 static  void    scrollbar(MOUSE *p);
76
77
78 /* execute the command */
79 BOOL
80 command(int commandc)
81 {
82     char filename[PATHLEN + 1]; /* file path name */
83     MOUSE *p;                   /* mouse data */
84     int c, i;
85     FILE *file;
86     struct cmd *curritem, *item;        /* command history */
87     char *s;
88
89     switch (commandc) {
90     case ctrl('C'):     /* toggle caseless mode */
91         if (caseless == NO) {
92             caseless = YES;
93             postmsg2("Caseless mode is now ON");
94         } else {
95             caseless = NO;
96             postmsg2("Caseless mode is now OFF");
97         }
98         egrepcaseless(caseless);        /* turn on/off -i flag */
99         return(NO);
100
101     case ctrl('R'):     /* rebuild the cross reference */
102         if (isuptodate == YES) {
103             postmsg("The -d option prevents rebuilding the symbol database");
104             return(NO);
105         }
106         exitcurses();
107         freefilelist();         /* remake the source file list */
108         makefilelist();
109         rebuild();
110         if (errorsfound == YES) {
111             errorsfound = NO;
112             askforreturn();
113         }               
114         entercurses();
115         clearmsg();             /* clear any previous message */
116         totallines = 0;
117         disprefs = 0;   
118         topline = nextline = 1;
119         selecting = 0;
120         break;
121
122 #if UNIXPC
123     case ESC:   /* possible unixpc mouse selection */
124 #endif
125     case ctrl('X'):     /* mouse selection */
126         if ((p = getmouseaction(DUMMYCHAR)) == NULL) {
127             return(NO); /* unknown control sequence */
128         }
129         /* if the button number is a scrollbar tag */
130         if (p->button == '0') {
131             scrollbar(p);
132             break;
133         } 
134         /* ignore a sweep */
135         if (p->x2 >= 0) {
136             return(NO);
137         }
138         /* if this is a line selection */
139         if (p->y1 < FLDLINE) {
140
141             /* find the selected line */
142             /* note: the selection is forced into range */
143             for (i = disprefs - 1; i > 0; --i) {
144                 if (p->y1 >= displine[i]) {
145                     break;
146                 }
147             }
148             /* display it in the file with the editor */
149             editref(i);
150         } else {        /* this is an input field selection */
151             field = p->y1 - FLDLINE;
152             /* force it into range */
153             if (field >= FIELDS) {
154                 field = FIELDS - 1;
155             }
156             setfield();
157             resetcmd();
158             return(NO);
159         }
160         break;
161
162     case '\t':  /* go to next input field */
163         if (disprefs) {
164             selecting = !selecting;
165             if (selecting) {
166                 move(displine[curdispline], 0);
167                 refresh();
168             } else {
169                 atfield();
170                 resetcmd();
171             }
172         }
173         return(NO);
174
175 #ifdef KEY_ENTER
176     case KEY_ENTER:
177 #endif
178     case '\r':
179     case '\n':  /* go to reference */
180         if (selecting) {
181             editref(curdispline);
182             return(YES);
183         }
184         /* FALLTHROUGH */
185
186     case ctrl('N'):
187 #ifdef KEY_DOWN
188     case KEY_DOWN:
189 #endif          
190 #ifdef KEY_RIGHT
191     case KEY_RIGHT:
192 #endif
193         if (selecting) {
194             if ((curdispline + 1) < disprefs) {
195                 move(displine[++curdispline], 0);
196                 refresh();
197             }
198         } else {
199             field = (field + 1) % FIELDS;
200             setfield();
201             atfield();
202             resetcmd();
203         }
204         return(NO);
205
206     case ctrl('P'):     /* go to previous input field */
207 #ifdef KEY_UP
208     case KEY_UP:
209 #endif
210 #ifdef KEY_LEFT         
211     case KEY_LEFT:
212 #endif
213         if (selecting) {
214             if (curdispline) {
215                 move(displine[--curdispline], 0);
216                 refresh();
217             }
218         } else {
219             field = (field + (FIELDS - 1)) % FIELDS;
220             setfield();
221             atfield();
222             resetcmd();
223         }
224         return(NO);
225 #ifdef KEY_HOME
226     case KEY_HOME:      /* go to first input field */
227         if (selecting) {
228             curdispline = 0;
229             move(REFLINE, 0);
230             refresh();
231         } else {
232             field = 0;
233             setfield();
234             atfield();
235             resetcmd();
236         }
237         return(NO);
238 #endif
239
240 #ifdef KEY_LL
241     case KEY_LL:        /* go to last input field */
242         if (selecting) {
243             move(displine[disprefs - 1], 0);
244             refresh();
245         } else {
246             field = FIELDS - 1;
247             setfield();
248             atfield();
249             resetcmd();
250         }
251         return(NO);
252 #endif /* def(KEY_LL) */
253
254     case ' ':   /* display next page */
255     case '+':
256     case ctrl('V'):
257 #ifdef KEY_NPAGE
258     case KEY_NPAGE:
259 #endif
260         /* don't redisplay if there are no lines */
261         if (totallines == 0) {
262             return(NO);
263         }
264         /* note: seekline() is not used to move to the next 
265          * page because display() leaves the file pointer at
266          * the next page to optimize paging forward
267          */
268         curdispline = 0;
269         break;
270
271     case ctrl('H'):
272     case '-':   /* display previous page */
273 #ifdef KEY_PPAGE
274     case KEY_PPAGE:
275 #endif
276         /* don't redisplay if there are no lines */
277         if (totallines == 0) {
278             return(NO);
279         }
280
281         curdispline = 0;
282
283         /* if there are only two pages, just go to the other one */
284         if (totallines <= 2 * mdisprefs) {
285             break;
286         }
287         /* if on first page but not at beginning, go to beginning */
288         nextline -= mdisprefs;  /* already at next page */
289         if (nextline > 1 && nextline <= mdisprefs) {
290             nextline = 1;
291         } else {
292             nextline -= mdisprefs;
293             if (nextline < 1) {
294                 nextline = totallines - mdisprefs + 1;
295                 if (nextline < 1) {
296                     nextline = 1;
297                 }
298             }
299         }
300         seekline(nextline);
301         break;
302
303     case '>':   /* write or append the lines to a file */
304         if (totallines == 0) {
305             postmsg("There are no lines to write to a file");
306         } else {        /* get the file name */
307             move(PRLINE, 0);
308             addstr("Write to file: ");
309             s = "w";
310             if ((c = mygetch()) == '>') {
311                 move(PRLINE, 0);
312                 addstr(appendprompt);
313                 c = '\0';
314                 s = "a";
315             }
316             if (c != '\r' && 
317                 mygetline("", newpat,
318                           COLS - sizeof(appendprompt), c, NO) > 0
319                 ) {
320                 shellpath(filename, sizeof(filename), newpat);
321                 if ((file = myfopen(filename, s)) == NULL) {
322                     cannotopen(filename);
323                 } else {
324                     seekline(1);
325                     while ((c = getc(refsfound)) != EOF) {
326                         putc(c, file);
327                     }
328                     seekline(topline);
329                     fclose(file);
330                 }
331             }
332             clearprompt();
333         }
334         return(NO);     /* return to the previous field */
335
336     case '<':   /* read lines from a file */
337         move(PRLINE, 0);
338         addstr(readprompt);
339         if (mygetline("", newpat, COLS - sizeof(readprompt),
340                       '\0', NO) > 0) {
341             clearprompt();
342             shellpath(filename, sizeof(filename), newpat);
343             if (readrefs(filename) == NO) {
344                 postmsg2("Ignoring an empty file");
345                 return(NO);
346             }
347             return(YES);
348         }
349         clearprompt();
350         return(NO);
351
352     case '^':   /* pipe the lines through a shell command */
353     case '|':   /* pipe the lines to a shell command */
354         if (totallines == 0) {
355             postmsg("There are no lines to pipe to a shell command");
356             return(NO);
357         }
358         /* get the shell command */
359         move(PRLINE, 0);
360         addstr(pipeprompt);
361         if (mygetline("", newpat, COLS - sizeof(pipeprompt), '\0', NO)
362             == 0) {
363             clearprompt();
364             return(NO);
365         }
366         /* if the ^ command, redirect output to a temp file */
367         if (commandc == '^') {
368             strcat(strcat(newpat, " >"), temp2);
369             /* HBB 20020708: somebody might have even
370              * their non-interactive default shells
371              * complain about clobbering
372              * redirections... --> delete before
373              * overwriting */
374             remove(temp2);
375         }
376         exitcurses();
377         if ((file = mypopen(newpat, "w")) == NULL) {
378             fprintf(stderr, "\
379 cscope: cannot open pipe to shell command: %s\n", newpat);
380         } else {
381             seekline(1);
382             while ((c = getc(refsfound)) != EOF) {
383                 putc(c, file);
384             }
385             seekline(topline);
386             mypclose(file);
387         }
388         if (commandc == '^') {
389             if (readrefs(temp2) == NO) {
390                 postmsg("Ignoring empty output of ^ command");
391             }
392         }
393         askforreturn();
394         entercurses();
395         break;
396 #if defined(KEY_RESIZE) && !defined(__DJGPP__)
397     case KEY_RESIZE:
398         exitcurses();
399         initscr();
400         entercurses();
401 #if TERMINFO
402         keypad(stdscr, TRUE);   /* enable the keypad */
403 #ifdef HAVE_FIXKEYPAD
404         fixkeypad();    /* fix for getch() intermittently returning garbage */
405 #endif
406 #endif
407 #if UNIXPC
408         standend();     /* turn off reverse video */
409 #endif
410         dispinit();     /* initialize display parameters */
411         setfield();     /* set the initial cursor position */
412         postmsg("");    /* clear any build progress message */
413         display();      /* display the version number and input fields */
414         break;
415 #endif
416     case ctrl('L'):     /* redraw screen */
417 #ifdef KEY_CLEAR
418     case KEY_CLEAR:
419 #endif
420         clearmsg2();
421         clearok(curscr, TRUE);
422         wrefresh(curscr);
423         drawscrollbar(topline, bottomline);
424         return(NO);
425
426     case '!':   /* shell escape */
427         execute(shell, shell, NULL);
428         seekline(topline);
429         break;
430
431     case '?':   /* help */
432         clear();
433         help();
434         clear();
435         seekline(topline);
436         break;
437
438     case ctrl('E'):     /* edit all lines */
439         editall();
440         break;
441
442     case ctrl('A'):             /* HBB 20050428: added alt. keymapping */
443     case ctrl('Y'):     /* repeat last pattern */
444         if (*Pattern != '\0') {
445             addstr(Pattern);
446             goto repeat;
447         }
448         break;
449
450     case ctrl('B'):             /* cmd history back */
451     case ctrl('F'):             /* cmd history fwd */
452         if (selecting) {
453             selecting = 0;
454         }
455
456         curritem = currentcmd();
457         item = (commandc == ctrl('F')) ? nextcmd() : prevcmd();
458         clearmsg2();
459         if (curritem == item) { /* inform user that we're at history end */
460             postmsg2("End of input field and search pattern history");
461         }
462         if (item) {
463             field = item->field;
464             setfield();
465             atfield();
466             addstr(item->text);
467             strcpy(Pattern, item->text);
468             switch (c = mygetch()) {
469             case '\r':
470             case '\n':
471                 goto repeat;
472             case ctrl('F'):
473             case ctrl('B'):
474                 myungetch(c);
475                 atfield();
476                 clrtoeol();     /* clear current field */
477                 break;
478             default:
479                 myungetch(c);
480                 if (mygetline(Pattern, newpat, COLS - fldcolumn - 1, '\0', caseless )) {
481                     strcpy (Pattern, newpat);
482                     resetcmd();
483                 }
484                 goto repeat;
485                 break;
486             }
487         }
488         return(NO);
489
490     case '\\':  /* next character is not a command */
491         addch('\\');    /* display the quote character */
492
493         /* get a character from the terminal */
494         if ((commandc = mygetch()) == EOF) {
495             return(NO); /* quit */
496         }
497         addstr("\b \b");        /* erase the quote character */
498         goto ispat;
499
500     case '.':
501         postmsg("The . command has been replaced by ^Y");
502         atfield();      /* move back to the input field */
503         /* FALLTHROUGH */
504     default:
505         if (selecting && !mouse) {
506             char *c;
507
508             if ((c = strchr(dispchars, commandc)))
509                 editref(c - dispchars);
510
511             /* if this is the start of a pattern */
512         } else if (isprint(commandc)) {
513         ispat:
514             if (mygetline("", newpat, COLS - fldcolumn - 1,
515                           commandc, caseless) > 0) {
516                 strcpy(Pattern, newpat);
517                 resetcmd();     /* reset command history */
518             repeat:
519                 addcmd(field, Pattern); /* add to command history */
520                 if (field == CHANGE) {
521                     /* prompt for the new text */
522                     move(PRLINE, 0);
523                     addstr(toprompt);
524                     mygetline("", newpat,
525                               COLS - sizeof(toprompt),
526                               '\0', NO);
527                 }
528                 /* search for the pattern */
529                 if (search() == YES) {
530                     curdispline = 0;
531                     ++selecting;
532
533                     switch (field) {
534                     case DEFINITION:
535                     case FILENAME:
536                         if (totallines > 1) {
537                             break;
538                         }
539                         topline = 1;
540                         editref(0);
541                         break;
542                     case CHANGE:
543                         return(changestring());
544                     }
545
546                 } else if (field == FILENAME && 
547                            access(newpat, READ) == 0) {
548                     /* try to edit the file anyway */
549                     edit(newpat, "1");
550                 }
551             } else {    /* no pattern--the input was erased */
552                 return(NO);
553             }
554         } else {        /* control character */
555             return(NO);
556         }
557     } /* switch(commandc) */
558     return(YES);
559 }
560
561 /* clear the prompt line */
562
563 static void
564 clearprompt(void)
565 {
566         move(PRLINE, 0);
567         clrtoeol();
568 }
569
570 /* read references from a file */
571
572 BOOL
573 readrefs(char *filename)
574 {
575         FILE    *file;
576         int     c;
577
578         if ((file = myfopen(filename, "rb")) == NULL) {
579                 cannotopen(filename);
580                 return(NO);
581         }
582         if ((c = getc(file)) == EOF) {  /* if file is empty */
583                 return(NO);
584         }
585         totallines = 0;
586         disprefs = 0;
587         nextline = 1;
588         if (writerefsfound() == YES) {
589                 putc(c, refsfound);
590                 while ((c = getc(file)) != EOF) {
591                         putc(c, refsfound);
592                 }
593                 fclose(file);
594                 fclose(refsfound);
595                 if ( (refsfound = myfopen(temp1, "rb")) == NULL) {
596                         cannotopen(temp1);
597                         return(NO);
598                 }
599                 countrefs();
600         }
601         return(YES);
602 }
603
604 /* change one text string to another */
605
606 static BOOL
607 changestring(void)
608 {
609     char    newfile[PATHLEN + 1];   /* new file name */
610     char    oldfile[PATHLEN + 1];   /* old file name */
611     char    linenum[NUMLEN + 1];    /* file line number */
612     char    msg[MSGLEN + 1];        /* message */
613     FILE    *script;                /* shell script file */
614     BOOL    anymarked = NO;         /* any line marked */
615     MOUSE *p;                       /* mouse data */
616     int     c;
617     unsigned int i;
618     char    *s;
619
620     /* open the temporary file */
621     if ((script = myfopen(temp2, "w")) == NULL) {
622         cannotopen(temp2);
623         return(NO);
624     }
625     /* create the line change indicators */
626     change = mycalloc(totallines, sizeof(BOOL));
627     changing = YES;
628     mousemenu();
629
630     /* until the quit command is entered */
631     for (;;) {
632         /* display the current page of lines */
633         display();
634     same:
635         atchange();
636                 
637         /* get a character from the terminal */
638         if ((c = mygetch()) == EOF || c == ctrl('D')) {
639             break;      /* change lines */
640         }
641         if (c == ctrl('Z')) {
642 #ifdef SIGTSTP
643             kill(0, SIGTSTP);
644             goto same;
645 #else
646             break;      /* change lines */
647 #endif
648         }
649         /* see if the input character is a command */
650         switch (c) {
651         case ' ':       /* display next page */
652         case '+':
653         case ctrl('V'):
654 #ifdef KEY_NPAGE
655         case KEY_NPAGE:
656 #endif
657         case '-':       /* display previous page */
658 #ifdef KEY_PPAGE
659         case KEY_PPAGE:
660 #endif
661         case '!':       /* shell escape */
662         case '?':       /* help */
663             command(c);
664             break;
665
666         case ctrl('L'): /* redraw screen */
667 #ifdef KEY_CLEAR
668         case KEY_CLEAR:
669 #endif
670             command(c);
671             goto same;
672
673         case ESC:       /* don't change lines */
674 #if UNIXPC
675             if((p = getmouseaction(DUMMYCHAR)) == NULL) {
676                 goto nochange;  /* unknown escape sequence */
677             }
678             break;
679 #endif
680         case ctrl('G'):
681             goto nochange;
682
683         case '*':       /* mark/unmark all displayed lines */
684             for (i = 0; topline + i < nextline; ++i) {
685                 mark(i);
686             }
687             goto same;
688
689         case ctrl('A'): /* mark/unmark all lines */
690             for (i = 0; i < totallines; ++i) {
691                 if (change[i] == NO) {
692                     change[i] = YES;
693                 } else {
694                     change[i] = NO;
695                 }
696             }
697             /* show that all have been marked */
698             seekline(totallines);
699             break;
700
701         case ctrl('X'): /* mouse selection */
702             if ((p = getmouseaction(DUMMYCHAR)) == NULL) {
703                 goto same;      /* unknown control sequence */
704             }
705             /* if the button number is a scrollbar tag */
706             if (p->button == '0') {
707                 scrollbar(p);
708                 break;
709             }
710             /* find the selected line */
711             /* note: the selection is forced into range */
712             for (i = disprefs - 1; i > 0; --i) {
713                 if (p->y1 >= displine[i]) {
714                     break;
715                 }
716             }
717             mark(i);
718             goto same;
719
720         default:
721             {
722                 /* if a line was selected */
723                 char            *cc;
724
725                 if ((cc = strchr(dispchars, c)))
726                     mark(cc - dispchars);
727
728                 goto same;
729             } /* default case */
730         } /* switch(change code character) */
731     } /* for(ever) */
732
733     /* for each line containing the old text */
734     fprintf(script, "ed - <<\\!\n");
735     *oldfile = '\0';
736     seekline(1);
737     for (i = 0; 
738          fscanf(refsfound, "%" PATHLEN_STR "s%*s%" NUMLEN_STR "s%*[^\n]", newfile, linenum) == 2;
739          ++i) {
740         /* see if the line is to be changed */
741         if (change[i] == YES) {
742             anymarked = YES;
743                 
744             /* if this is a new file */
745             if (strcmp(newfile, oldfile) != 0) {
746                                 
747                 /* make sure it can be changed */
748                 if (access(newfile, WRITE) != 0) {
749                     snprintf(msg, sizeof(msg), "Cannot write to file %s", newfile);
750                     postmsg(msg);
751                     anymarked = NO;
752                     break;
753                 }
754                 /* if there was an old file */
755                 if (*oldfile != '\0') {
756                     fprintf(script, "w\n");     /* save it */
757                 }
758                 /* edit the new file */
759                 strcpy(oldfile, newfile);
760                 fprintf(script, "e %s\n", oldfile);
761             }
762             /* output substitute command */
763             fprintf(script, "%ss/", linenum);   /* change */
764             for (s = Pattern; *s != '\0'; ++s) {
765                 /* old text */
766                 if (strchr("/\\[.^*", *s) != NULL) {
767                     putc('\\', script);
768                 }
769                 if (caseless == YES && isalpha((unsigned char)*s)) {
770                     putc('[', script);
771                     if(islower((unsigned char)*s)) {
772                         putc(toupper((unsigned char)*s), script);
773                         putc(*s, script);
774                     } else {
775                         putc(*s, script);
776                         putc(tolower((unsigned char)*s), script);
777                     }
778                     putc(']', script);
779                 } else  
780                     putc(*s, script);
781             }
782             putc('/', script);                  /* to */
783             for (s = newpat; *s != '\0'; ++s) { /* new text */
784                 if (strchr("/\\&", *s) != NULL) {
785                     putc('\\', script);
786                 }
787                 putc(*s, script);
788             }
789             fprintf(script, "/gp\n");   /* and print */
790         }
791     }
792     fprintf(script, "w\nq\n!\n");       /* write and quit */
793     fclose(script);
794
795     /* if any line was marked */
796     if (anymarked == YES) {
797                 
798         /* edit the files */
799         clearprompt();
800         refresh();
801         fprintf(stderr, "Changed lines:\n\r");
802         execute("sh", "sh", temp2, NULL);
803         askforreturn();
804         seekline(1);
805     } else {
806     nochange:
807         clearprompt();
808     }
809     changing = NO;
810     mousemenu();
811     free(change);
812     return(anymarked);
813 }
814
815
816 /* mark/unmark this displayed line to be changed */
817 static void
818 mark(unsigned int i)
819 {
820     unsigned int j;
821         
822     j = i + topline - 1;
823     if (j < totallines) {
824         move(displine[i], 1);
825
826         if (change[j] == NO) {
827             change[j] = YES;
828             addch('>');
829         } else {
830             change[j] = NO;
831             addch(' ');
832         }
833     }
834 }
835
836
837 /* scrollbar actions */
838 static void
839 scrollbar(MOUSE *p)
840 {
841     /* reposition list if it makes sense */
842     if (totallines == 0) {
843         return;
844     }
845     switch (p->percent) {
846                 
847     case 101: /* scroll down one page */
848         if (nextline + mdisprefs > totallines) {
849             nextline = totallines - mdisprefs + 1;
850         }
851         break;
852                 
853     case 102: /* scroll up one page */
854         nextline = topline - mdisprefs;
855         if (nextline < 1) {
856             nextline = 1;
857         }
858         break;
859
860     case 103: /* scroll down one line */
861         nextline = topline + 1;
862         break;
863                 
864     case 104: /* scroll up one line */
865         if (topline > 1) {
866             nextline = topline - 1;
867         }
868         break;
869     default:
870         nextline = p->percent * totallines / 100;
871     }
872     seekline(nextline);
873 }
874
875
876 /* count the references found */
877 void
878 countrefs(void)
879 {
880     char    *subsystem;             /* OGS subsystem name */
881     char    *book;                  /* OGS book name */
882     char    file[PATHLEN + 1];      /* file name */
883     char    function[PATLEN + 1];   /* function name */
884     char    linenum[NUMLEN + 1];    /* line number */
885     int     i;
886
887     /* count the references found and find the length of the file,
888        function, and line number display fields */
889     subsystemlen = 9;   /* strlen("Subsystem") */
890     booklen = 4;                /* strlen("Book") */
891     filelen = 4;                /* strlen("File") */
892     fcnlen = 8;         /* strlen("Function") */
893     numlen = 0;
894     /* HBB NOTE 2012-04-07: it may look like we shouldn't assing tempstring here,
895      * since it's not used.  But it has to be assigned just so the return value
896      * of fscanf will actually reach 4. */
897     while (EOF != (i = fscanf(refsfound, 
898                               "%" PATHLEN_STR "s%" PATLEN_STR "s%" NUMLEN_STR "s %" TEMPSTRING_LEN_STR "[^\n]",
899                               file, function, linenum, tempstring
900                              )
901                   )
902           ) {
903         if (   (i != 4)
904             || !isgraph((unsigned char) *file)
905             || !isgraph((unsigned char) *function)
906             || !isdigit((unsigned char) *linenum)
907            ) {
908             postmsg("File does not have expected format");
909             totallines = 0;
910             disprefs = 0;
911             return;
912         }
913         if ((i = strlen(pathcomponents(file, dispcomponents))) > filelen) {
914             filelen = i;
915         }
916         if (ogs == YES) {
917             ogsnames(file, &subsystem, &book);
918             if ((i = strlen(subsystem)) > subsystemlen) {
919                 subsystemlen = i;
920             }
921             if ((i = strlen(book)) > booklen) {
922                 booklen = i;
923             }
924         }
925         if ((i = strlen(function)) > fcnlen) {
926             fcnlen = i;
927         }
928         if ((i = strlen(linenum)) > numlen) {
929             numlen = i;
930         }
931         ++totallines;
932     }
933     rewind(refsfound);
934
935     /* restrict the width of displayed columns */
936     /* HBB FIXME 20060419: magic number alert! */ 
937     i = (COLS - 5) / 3;
938     if (ogs == YES) {
939         i = (COLS - 7) / 5;
940     }
941     if (filelen > i && i > 4) {
942         filelen = i;
943     }
944     if (subsystemlen > i && i > 9) {
945         subsystemlen = i;
946     }
947     if (booklen > i && i > 4) {
948         booklen = i;
949     }
950     if (fcnlen > i && i > 8) {
951         fcnlen = i;
952     }
953 }