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