Home | History | Annotate | Download | only in cscope-fast
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*	Copyright (c) 1988 AT&T	*/
     23 /*	  All Rights Reserved  	*/
     24 
     25 
     26 /*
     27  * Copyright (c) 1999 by Sun Microsystems, Inc.
     28  * All rights reserved.
     29  */
     30 
     31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     32 
     33 /*
     34  *	cscope - interactive C symbol or text cross-reference
     35  *
     36  *	command functions
     37  */
     38 
     39 #include <curses.h>	/* KEY_.* */
     40 #include <fcntl.h>	/* O_RDONLY */
     41 #include <unistd.h>
     42 #include <stdio.h>
     43 #include "global.h"
     44 #include "library.h"
     45 
     46 BOOL	caseless;		/* ignore letter case when searching */
     47 BOOL	*change;		/* change this line */
     48 BOOL	changing;		/* changing text */
     49 char	newpat[PATLEN + 1];	/* new pattern */
     50 char	pattern[PATLEN + 1];	/* symbol or text pattern */
     51 
     52 static	char	appendprompt[] = "Append to file: ";
     53 static	char	pipeprompt[] = "Pipe to shell command: ";
     54 static	char	readprompt[] = "Read from file: ";
     55 static	char	selectionprompt[] = "Selection: ";
     56 static	char	toprompt[] = "To: ";
     57 
     58 static void scrollbar(MOUSEEVENT *p);
     59 
     60 /* execute the command */
     61 
     62 BOOL
     63 command(int commandc)
     64 {
     65 	char	filename[PATHLEN + 1];	/* file path name */
     66 	MOUSEEVENT *p;			/* mouse data */
     67 	int	c, i;
     68 	FILE	*file;
     69 	HISTORY *curritem, *item;	/* command history */
     70 	char	*s;
     71 
     72 	switch (commandc) {
     73 
     74 	case ctrl('C'):	/* toggle caseless mode */
     75 		if (caseless == NO) {
     76 			caseless = YES;
     77 			putmsg2("Caseless mode is now ON");
     78 		} else {
     79 			caseless = NO;
     80 			putmsg2("Caseless mode is now OFF");
     81 		}
     82 		egrepcaseless(caseless);	/* turn on/off -i flag */
     83 		return (NO);
     84 
     85 	case ctrl('R'):	/* rebuild the cross reference */
     86 		if (isuptodate == YES) {
     87 			putmsg("The -d option prevents rebuilding the "
     88 			    "symbol database");
     89 			return (NO);
     90 		}
     91 		exitcurses();
     92 		freefilelist();		/* remake the source file list */
     93 		makefilelist();
     94 		rebuild();
     95 		if (errorsfound == YES) {
     96 			errorsfound = NO;
     97 			askforreturn();
     98 		}
     99 		entercurses();
    100 		putmsg("");		/* clear any previous message */
    101 		totallines = 0;
    102 		topline = nextline = 1;
    103 		break;
    104 
    105 	case ctrl('X'):	/* mouse selection */
    106 		if ((p = getmouseevent()) == NULL) {
    107 			return (NO);	/* unknown control sequence */
    108 		}
    109 		/* if the button number is a scrollbar tag */
    110 		if (p->button == '0') {
    111 			scrollbar(p);
    112 			break;
    113 		}
    114 		/* ignore a sweep */
    115 		if (p->x2 >= 0) {
    116 			return (NO);
    117 		}
    118 		/* if this is a line selection */
    119 		if (p->y1 < FLDLINE) {
    120 
    121 			/* find the selected line */
    122 			/* note: the selection is forced into range */
    123 			for (i = disprefs - 1; i > 0; --i) {
    124 				if (p->y1 >= displine[i]) {
    125 					break;
    126 				}
    127 			}
    128 			/* display it in the file with the editor */
    129 			editref(i);
    130 		} else {	/* this is an input field selection */
    131 			field = mouseselection(p, FLDLINE, FIELDS);
    132 			setfield();
    133 			resetcmd();
    134 			return (NO);
    135 		}
    136 		break;
    137 
    138 	case '\t':	/* go to next input field */
    139 	case '\n':
    140 	case '\r':
    141 	case ctrl('N'):
    142 	case KEY_DOWN:
    143 	case KEY_ENTER:
    144 	case KEY_RIGHT:
    145 		field = (field + 1) % FIELDS;
    146 		setfield();
    147 		resetcmd();
    148 		return (NO);
    149 
    150 	case ctrl('P'):	/* go to previous input field */
    151 	case KEY_UP:
    152 	case KEY_LEFT:
    153 		field = (field + (FIELDS - 1)) % FIELDS;
    154 		setfield();
    155 		resetcmd();
    156 		return (NO);
    157 	case KEY_HOME:	/* go to first input field */
    158 		field = 0;
    159 		setfield();
    160 		resetcmd();
    161 		return (NO);
    162 
    163 	case KEY_LL:	/* go to last input field */
    164 		field = FIELDS - 1;
    165 		setfield();
    166 		resetcmd();
    167 		return (NO);
    168 	case ' ':	/* display next page */
    169 	case '+':
    170 	case ctrl('V'):
    171 	case KEY_NPAGE:
    172 		/* don't redisplay if there are no lines */
    173 		if (totallines == 0) {
    174 			return (NO);
    175 		}
    176 		/*
    177 		 * note: seekline() is not used to move to the next
    178 		 * page because display() leaves the file pointer at
    179 		 * the next page to optimize paging forward
    180 		 */
    181 		break;
    182 
    183 	case '-':	/* display previous page */
    184 	case KEY_PPAGE:
    185 		/* don't redisplay if there are no lines */
    186 		if (totallines == 0) {
    187 			return (NO);
    188 		}
    189 		i = topline;		/* save the current top line */
    190 		nextline = topline;	/* go back to this page */
    191 
    192 		/* if on first page but not at beginning, go to beginning */
    193 		if (nextline > 1 && nextline <= mdisprefs) {
    194 			nextline = 1;
    195 		} else {	/* go back the maximum displayable lines */
    196 			nextline -= mdisprefs;
    197 
    198 			/* if this was the first page, go to the last page */
    199 			if (nextline < 1) {
    200 				nextline = totallines - mdisprefs + 1;
    201 				if (nextline < 1) {
    202 					nextline = 1;
    203 				}
    204 				/* old top is past last line */
    205 				i = totallines + 1;
    206 			}
    207 		}
    208 		/*
    209 		 * move down til the bottom line is just before the
    210 		 * previous top line
    211 		 */
    212 		c = nextline;
    213 		for (;;) {
    214 			seekline(nextline);
    215 			display();
    216 			if (i - bottomline <= 0) {
    217 				break;
    218 			}
    219 			nextline = ++c;
    220 		}
    221 		return (NO);	/* display already up to date */
    222 
    223 	case '>':	/* write or append the lines to a file */
    224 		if (totallines == 0) {
    225 			putmsg("There are no lines to write to a file");
    226 		} else {	/* get the file name */
    227 			(void) move(PRLINE, 0);
    228 			(void) addstr("Write to file: ");
    229 			s = "w";
    230 			if ((c = mygetch()) == '>') {
    231 				(void) move(PRLINE, 0);
    232 				(void) addstr(appendprompt);
    233 				c = '\0';
    234 				s = "a";
    235 			}
    236 			if (c != '\r' && c != '\n' && c != KEY_ENTER &&
    237 			    c != KEY_BREAK &&
    238 			    getline(newpat, COLS - sizeof (appendprompt), c,
    239 			    NO) > 0) {
    240 				shellpath(filename, sizeof (filename), newpat);
    241 				if ((file = fopen(filename, s)) == NULL) {
    242 					cannotopen(filename);
    243 				} else {
    244 					seekline(1);
    245 					while ((c = getc(refsfound)) != EOF) {
    246 						(void) putc(c, file);
    247 					}
    248 					seekline(topline);
    249 					(void) fclose(file);
    250 				}
    251 			}
    252 			clearprompt();
    253 		}
    254 		return (NO);	/* return to the previous field */
    255 
    256 	case '<':	/* read lines from a file */
    257 		(void) move(PRLINE, 0);
    258 		(void) addstr(readprompt);
    259 		if (getline(newpat, COLS - sizeof (readprompt), '\0',
    260 		    NO) > 0) {
    261 			clearprompt();
    262 			shellpath(filename, sizeof (filename), newpat);
    263 			if (readrefs(filename) == NO) {
    264 				putmsg2("Ignoring an empty file");
    265 				return (NO);
    266 			}
    267 			return (YES);
    268 		}
    269 		clearprompt();
    270 		return (NO);
    271 
    272 	case '^':	/* pipe the lines through a shell command */
    273 	case '|':	/* pipe the lines to a shell command */
    274 		if (totallines == 0) {
    275 			putmsg("There are no lines to pipe to a shell command");
    276 			return (NO);
    277 		}
    278 		/* get the shell command */
    279 		(void) move(PRLINE, 0);
    280 		(void) addstr(pipeprompt);
    281 		if (getline(newpat,
    282 		    COLS - sizeof (pipeprompt), '\0', NO) == 0) {
    283 			clearprompt();
    284 			return (NO);
    285 		}
    286 		/* if the ^ command, redirect output to a temp file */
    287 		if (commandc == '^') {
    288 			(void) strcat(strcat(newpat, " >"), temp2);
    289 		}
    290 		exitcurses();
    291 		if ((file = mypopen(newpat, "w")) == NULL) {
    292 			(void) fprintf(stderr,
    293 			    "cscope: cannot open pipe to shell command: %s\n",
    294 			    newpat);
    295 		} else {
    296 			seekline(1);
    297 			while ((c = getc(refsfound)) != EOF) {
    298 				(void) putc(c, file);
    299 			}
    300 			seekline(topline);
    301 			(void) mypclose(file);
    302 		}
    303 		if (commandc == '^') {
    304 			if (readrefs(temp2) == NO) {
    305 				putmsg("Ignoring empty output of ^ command");
    306 			}
    307 		}
    308 		askforreturn();
    309 		entercurses();
    310 		break;
    311 
    312 	case ctrl('L'):	/* redraw screen */
    313 	case KEY_CLEAR:
    314 		(void) clearok(curscr, TRUE);
    315 		(void) wrefresh(curscr);
    316 		drawscrollbar(topline, bottomline, totallines);
    317 		return (NO);
    318 
    319 	case '!':	/* shell escape */
    320 		(void) execute(shell, shell, (char *)NULL);
    321 		seekline(topline);
    322 		break;
    323 
    324 	case '?':	/* help */
    325 		(void) clear();
    326 		help();
    327 		(void) clear();
    328 		seekline(topline);
    329 		break;
    330 
    331 	case ctrl('E'):	/* edit all lines */
    332 		editall();
    333 		break;
    334 
    335 	case ctrl('A'):	/* repeat last pattern */
    336 	case ctrl('Y'):	/* (old command) */
    337 		if (*pattern != '\0') {
    338 			(void) addstr(pattern);
    339 			goto repeat;
    340 		}
    341 		break;
    342 
    343 	case ctrl('B'):		/* cmd history back */
    344 	case ctrl('F'):		/* cmd history fwd */
    345 		curritem = currentcmd();
    346 		item = (commandc == ctrl('F')) ? nextcmd() : prevcmd();
    347 		clearmsg2();
    348 		if (curritem == item) {
    349 			/* inform user that we're at history end */
    350 			putmsg2(
    351 			    "End of input field and search pattern history");
    352 		}
    353 		if (item) {
    354 			field = item->field;
    355 			setfield();
    356 			atfield();
    357 			(void) addstr(item->text);
    358 			(void) strcpy(pattern, item->text);
    359 			switch (c = mygetch()) {
    360 			case '\r':
    361 			case '\n':
    362 			case KEY_ENTER:
    363 				goto repeat;
    364 			default:
    365 				ungetch(c);
    366 				atfield();
    367 				(void) clrtoeol(); /* clear current field */
    368 				break;
    369 			}
    370 		}
    371 		return (NO);
    372 
    373 	case '\\':	/* next character is not a command */
    374 		(void) addch('\\');	/* display the quote character */
    375 
    376 		/* get a character from the terminal */
    377 		if ((commandc = mygetch()) == EOF) {
    378 			return (NO);	/* quit */
    379 		}
    380 		(void) addstr("\b \b");	/* erase the quote character */
    381 		goto ispat;
    382 
    383 	case '.':
    384 		atfield();	/* move back to the input field */
    385 		/* FALLTHROUGH */
    386 	default:
    387 		/* edit a selected line */
    388 		if (isdigit(commandc) && commandc != '0' && !mouse) {
    389 			if (returnrequired == NO) {
    390 				editref(commandc - '1');
    391 			} else {
    392 				(void) move(PRLINE, 0);
    393 				(void) addstr(selectionprompt);
    394 				if (getline(newpat,
    395 				    COLS - sizeof (selectionprompt), commandc,
    396 				    NO) > 0 &&
    397 				    (i = atoi(newpat)) > 0) {
    398 					editref(i - 1);
    399 				}
    400 				clearprompt();
    401 			}
    402 		} else if (isprint(commandc)) {
    403 			/* this is the start of a pattern */
    404 ispat:
    405 			if (getline(newpat, COLS - fldcolumn - 1, commandc,
    406 			    caseless) > 0) {
    407 					(void) strcpy(pattern, newpat);
    408 					resetcmd();	/* reset history */
    409 repeat:
    410 				addcmd(field, pattern);	/* add to history */
    411 				if (field == CHANGE) {
    412 					/* prompt for the new text */
    413 					(void) move(PRLINE, 0);
    414 					(void) addstr(toprompt);
    415 					(void) getline(newpat,
    416 					    COLS - sizeof (toprompt), '\0', NO);
    417 				}
    418 				/* search for the pattern */
    419 				if (search() == YES) {
    420 					switch (field) {
    421 					case DEFINITION:
    422 					case FILENAME:
    423 						if (totallines > 1) {
    424 							break;
    425 						}
    426 						topline = 1;
    427 						editref(0);
    428 						break;
    429 					case CHANGE:
    430 						return (changestring());
    431 					}
    432 				} else if (field == FILENAME &&
    433 				    access(newpat, READ) == 0) {
    434 					/* try to edit the file anyway */
    435 					edit(newpat, "1");
    436 				}
    437 			} else {	/* no pattern--the input was erased */
    438 				return (NO);
    439 			}
    440 		} else {	/* control character */
    441 			return (NO);
    442 		}
    443 	}
    444 	return (YES);
    445 }
    446 
    447 /* clear the prompt line */
    448 
    449 void
    450 clearprompt(void)
    451 {
    452 	(void) move(PRLINE, 0);
    453 	(void) clrtoeol();
    454 }
    455 
    456 /* read references from a file */
    457 
    458 BOOL
    459 readrefs(char *filename)
    460 {
    461 	FILE	*file;
    462 	int	c;
    463 
    464 	if ((file = fopen(filename, "r")) == NULL) {
    465 		cannotopen(filename);
    466 		return (NO);
    467 	}
    468 	if ((c = getc(file)) == EOF) {	/* if file is empty */
    469 		return (NO);
    470 	}
    471 	totallines = 0;
    472 	nextline = 1;
    473 	if (writerefsfound() == YES) {
    474 		(void) putc(c, refsfound);
    475 		while ((c = getc(file)) != EOF) {
    476 			(void) putc(c, refsfound);
    477 		}
    478 		(void) fclose(file);
    479 		(void) freopen(temp1, "r", refsfound);
    480 		countrefs();
    481 	}
    482 	return (YES);
    483 }
    484 
    485 /* change one text string to another */
    486 
    487 BOOL
    488 changestring(void)
    489 {
    490 	char	buf[PATLEN + 1];	/* input buffer */
    491 	char	newfile[PATHLEN + 1];	/* new file name */
    492 	char	oldfile[PATHLEN + 1];	/* old file name */
    493 	char	linenum[NUMLEN + 1];	/* file line number */
    494 	char	msg[MSGLEN + 1];	/* message */
    495 	FILE	*script;		/* shell script file */
    496 	BOOL	anymarked = NO;		/* any line marked */
    497 	MOUSEEVENT *p;			/* mouse data */
    498 	int	c, i;
    499 	char	*s;
    500 
    501 	/* open the temporary file */
    502 	if ((script = fopen(temp2, "w")) == NULL) {
    503 		cannotopen(temp2);
    504 		return (NO);
    505 	}
    506 	/* create the line change indicators */
    507 	change = (BOOL *)mycalloc((unsigned)totallines, sizeof (BOOL));
    508 	changing = YES;
    509 	initmenu();
    510 
    511 	/* until the quit command is entered */
    512 	for (;;) {
    513 		/* display the current page of lines */
    514 		display();
    515 	same:
    516 		/* get a character from the terminal */
    517 		(void) move(PRLINE, 0);
    518 		(void) addstr(
    519 		    "Select lines to change (press the ? key for help): ");
    520 		if ((c = mygetch()) == EOF || c == ctrl('D') ||
    521 		    c == ctrl('Z')) {
    522 			break;	/* change lines */
    523 		}
    524 		/* see if the input character is a command */
    525 		switch (c) {
    526 		case ' ':	/* display next page */
    527 		case '+':
    528 		case ctrl('V'):
    529 		case KEY_NPAGE:
    530 		case '-':	/* display previous page */
    531 		case KEY_PPAGE:
    532 		case '!':	/* shell escape */
    533 		case '?':	/* help */
    534 			(void) command(c);
    535 			break;
    536 
    537 		case ctrl('L'):	/* redraw screen */
    538 		case KEY_CLEAR:
    539 			(void) command(c);
    540 			goto same;
    541 
    542 		case ESC:	/* kept for backwards compatibility */
    543 			/* FALLTHROUGH */
    544 
    545 		case '\r':	/* don't change lines */
    546 		case '\n':
    547 		case KEY_ENTER:
    548 		case KEY_BREAK:
    549 		case ctrl('G'):
    550 			clearprompt();
    551 			goto nochange;
    552 
    553 		case '*':	/* mark/unmark all displayed lines */
    554 			for (i = 0; topline + i < nextline; ++i) {
    555 				mark(i);
    556 			}
    557 			goto same;
    558 
    559 		case 'a':	/* mark/unmark all lines */
    560 			for (i = 0; i < totallines; ++i) {
    561 				if (change[i] == NO) {
    562 					change[i] = YES;
    563 				} else {
    564 					change[i] = NO;
    565 				}
    566 			}
    567 			/* show that all have been marked */
    568 			seekline(totallines);
    569 			break;
    570 		case ctrl('X'):	/* mouse selection */
    571 			if ((p = getmouseevent()) == NULL) {
    572 				goto same;	/* unknown control sequence */
    573 			}
    574 			/* if the button number is a scrollbar tag */
    575 			if (p->button == '0') {
    576 				scrollbar(p);
    577 				break;
    578 			}
    579 			/* find the selected line */
    580 			/* note: the selection is forced into range */
    581 			for (i = disprefs - 1; i > 0; --i) {
    582 				if (p->y1 >= displine[i]) {
    583 					break;
    584 				}
    585 			}
    586 			mark(i);
    587 			goto same;
    588 		default:
    589 			/* if a line was selected */
    590 			if (isdigit(c) && c != '0' && !mouse) {
    591 				if (returnrequired == NO) {
    592 					mark(c - '1');
    593 				} else {
    594 					clearprompt();
    595 					(void) move(PRLINE, 0);
    596 					(void) addstr(selectionprompt);
    597 					if (getline(buf,
    598 					    COLS - sizeof (selectionprompt), c,
    599 					    NO) > 0 &&
    600 					    (i = atoi(buf)) > 0) {
    601 						mark(i - 1);
    602 					}
    603 				}
    604 			}
    605 			goto same;
    606 		}
    607 	}
    608 	/* for each line containing the old text */
    609 	(void) fprintf(script, "ed - <<\\!\nH\n");
    610 	*oldfile = '\0';
    611 	seekline(1);
    612 	for (i = 0; fscanf(refsfound, "%s%*s%s%*[^\n]", newfile, linenum) == 2;
    613 	    ++i) {
    614 		/* see if the line is to be changed */
    615 		if (change[i] == YES) {
    616 			anymarked = YES;
    617 
    618 			/* if this is a new file */
    619 			if (strcmp(newfile, oldfile) != 0) {
    620 
    621 				/* make sure it can be changed */
    622 				if (access(newfile, WRITE) != 0) {
    623 					(void) sprintf(msg,
    624 					    "Cannot write to file %s",
    625 					    newfile);
    626 					putmsg(msg);
    627 					anymarked = NO;
    628 					break;
    629 				}
    630 				/* if there was an old file */
    631 				if (*oldfile != '\0') {
    632 					(void) fprintf(script,
    633 					    "w\n");	/* save it */
    634 				}
    635 				/* edit the new file */
    636 				(void) strcpy(oldfile, newfile);
    637 				(void) fprintf(script, "e %s\n", oldfile);
    638 			}
    639 			/* output substitute command */
    640 			(void) fprintf(script,
    641 			    "%ss/", linenum);	/* change */
    642 			for (s = pattern; *s != '\0'; ++s) {	/* old text */
    643 				if (*s == '/') {
    644 					(void) putc('\\', script);
    645 				}
    646 				(void) putc(*s, script);
    647 			}
    648 			(void) putc('/', script);			/* to */
    649 			for (s = newpat; *s != '\0'; ++s) {	/* new text */
    650 				if (strchr("/\\&", *s) != NULL) {
    651 					(void) putc('\\', script);
    652 				}
    653 				(void) putc(*s, script);
    654 			}
    655 			(void) fprintf(script, "/gp\n");	/* and print */
    656 		}
    657 	}
    658 	(void) fprintf(script, "w\nq\n!\n");	/* write and quit */
    659 	(void) fclose(script);
    660 	clearprompt();
    661 
    662 	/* if any line was marked */
    663 	if (anymarked == YES) {
    664 		/* edit the files */
    665 		(void) refresh();
    666 		(void) fprintf(stderr, "Changed lines:\n\r");
    667 		(void) execute(shell, shell, temp2, (char *)NULL);
    668 		askforreturn();
    669 	}
    670 nochange:
    671 	changing = NO;
    672 	initmenu();
    673 	free(change);
    674 	seekline(topline);
    675 	return (YES);	/* clear any marks on exit without change */
    676 }
    677 
    678 /* mark/unmark this displayed line to be changed */
    679 
    680 void
    681 mark(int i)
    682 {
    683 	int	j;
    684 
    685 	j = i + topline - 1;
    686 	if (j < totallines) {
    687 		(void) move(displine[i], selectlen);
    688 		if (change[j] == NO) {
    689 			change[j] = YES;
    690 			(void) addch('>');
    691 		} else {
    692 			change[j] = NO;
    693 			(void) addch(' ');
    694 		}
    695 	}
    696 }
    697 
    698 /* scrollbar actions */
    699 
    700 static void
    701 scrollbar(MOUSEEVENT *p)
    702 {
    703 	/* reposition list if it makes sense */
    704 	if (totallines == 0) {
    705 		return;
    706 	}
    707 	switch (p->percent) {
    708 
    709 	case 101: /* scroll down one page */
    710 		if (nextline + mdisprefs > totallines) {
    711 			nextline = totallines - mdisprefs + 1;
    712 		}
    713 		break;
    714 
    715 	case 102: /* scroll up one page */
    716 		nextline = topline - mdisprefs;
    717 		if (nextline < 1) {
    718 			nextline = 1;
    719 		}
    720 		break;
    721 
    722 	case 103: /* scroll down one line */
    723 		nextline = topline + 1;
    724 		break;
    725 
    726 	case 104: /* scroll up one line */
    727 		if (topline > 1) {
    728 			nextline = topline - 1;
    729 		}
    730 		break;
    731 	default:
    732 		nextline = p->percent * totallines / 100;
    733 	}
    734 	seekline(nextline);
    735 }
    736