4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
26 /* Copyright (c) 1988 AT&T */
27 /* All Rights Reserved */
30 * cscope - interactive C symbol or text cross-reference
35 #include <curses.h> /* KEY_.* */
36 #include <fcntl.h> /* O_RDONLY */
42 BOOL caseless
; /* ignore letter case when searching */
43 BOOL
*change
; /* change this line */
44 BOOL changing
; /* changing text */
45 char newpat
[PATLEN
+ 1]; /* new pattern */
46 char pattern
[PATLEN
+ 1]; /* symbol or text pattern */
48 static char appendprompt
[] = "Append to file: ";
49 static char pipeprompt
[] = "Pipe to shell command: ";
50 static char readprompt
[] = "Read from file: ";
51 static char selectionprompt
[] = "Selection: ";
52 static char toprompt
[] = "To: ";
54 static void scrollbar(MOUSEEVENT
*p
);
56 /* execute the command */
61 char filename
[PATHLEN
+ 1]; /* file path name */
62 MOUSEEVENT
*p
; /* mouse data */
65 HISTORY
*curritem
, *item
; /* command history */
70 case ctrl('C'): /* toggle caseless mode */
73 putmsg2("Caseless mode is now ON");
76 putmsg2("Caseless mode is now OFF");
78 egrepcaseless(caseless
); /* turn on/off -i flag */
81 case ctrl('R'): /* rebuild the cross reference */
82 if (isuptodate
== YES
) {
83 putmsg("The -d option prevents rebuilding the "
88 freefilelist(); /* remake the source file list */
91 if (errorsfound
== YES
) {
96 putmsg(""); /* clear any previous message */
98 topline
= nextline
= 1;
101 case ctrl('X'): /* mouse selection */
102 if ((p
= getmouseevent()) == NULL
) {
103 return (NO
); /* unknown control sequence */
105 /* if the button number is a scrollbar tag */
106 if (p
->button
== '0') {
114 /* if this is a line selection */
115 if (p
->y1
< FLDLINE
) {
117 /* find the selected line */
118 /* note: the selection is forced into range */
119 for (i
= disprefs
- 1; i
> 0; --i
) {
120 if (p
->y1
>= displine
[i
]) {
124 /* display it in the file with the editor */
126 } else { /* this is an input field selection */
127 field
= mouseselection(p
, FLDLINE
, FIELDS
);
134 case '\t': /* go to next input field */
141 field
= (field
+ 1) % FIELDS
;
146 case ctrl('P'): /* go to previous input field */
149 field
= (field
+ (FIELDS
- 1)) % FIELDS
;
153 case KEY_HOME
: /* go to first input field */
159 case KEY_LL
: /* go to last input field */
164 case ' ': /* display next page */
168 /* don't redisplay if there are no lines */
169 if (totallines
== 0) {
173 * note: seekline() is not used to move to the next
174 * page because display() leaves the file pointer at
175 * the next page to optimize paging forward
179 case '-': /* display previous page */
181 /* don't redisplay if there are no lines */
182 if (totallines
== 0) {
185 i
= topline
; /* save the current top line */
186 nextline
= topline
; /* go back to this page */
188 /* if on first page but not at beginning, go to beginning */
189 if (nextline
> 1 && nextline
<= mdisprefs
) {
191 } else { /* go back the maximum displayable lines */
192 nextline
-= mdisprefs
;
194 /* if this was the first page, go to the last page */
196 nextline
= totallines
- mdisprefs
+ 1;
200 /* old top is past last line */
205 * move down til the bottom line is just before the
212 if (i
- bottomline
<= 0) {
217 return (NO
); /* display already up to date */
219 case '>': /* write or append the lines to a file */
220 if (totallines
== 0) {
221 putmsg("There are no lines to write to a file");
222 } else { /* get the file name */
223 (void) move(PRLINE
, 0);
224 (void) addstr("Write to file: ");
226 if ((c
= mygetch()) == '>') {
227 (void) move(PRLINE
, 0);
228 (void) addstr(appendprompt
);
232 if (c
!= '\r' && c
!= '\n' && c
!= KEY_ENTER
&&
234 getaline(newpat
, COLS
- sizeof (appendprompt
), c
,
236 shellpath(filename
, sizeof (filename
), newpat
);
237 if ((file
= fopen(filename
, s
)) == NULL
) {
238 cannotopen(filename
);
241 while ((c
= getc(refsfound
)) != EOF
) {
242 (void) putc(c
, file
);
250 return (NO
); /* return to the previous field */
252 case '<': /* read lines from a file */
253 (void) move(PRLINE
, 0);
254 (void) addstr(readprompt
);
255 if (getaline(newpat
, COLS
- sizeof (readprompt
), '\0',
258 shellpath(filename
, sizeof (filename
), newpat
);
259 if (readrefs(filename
) == NO
) {
260 putmsg2("Ignoring an empty file");
268 case '^': /* pipe the lines through a shell command */
269 case '|': /* pipe the lines to a shell command */
270 if (totallines
== 0) {
271 putmsg("There are no lines to pipe to a shell command");
274 /* get the shell command */
275 (void) move(PRLINE
, 0);
276 (void) addstr(pipeprompt
);
278 COLS
- sizeof (pipeprompt
), '\0', NO
) == 0) {
282 /* if the ^ command, redirect output to a temp file */
283 if (commandc
== '^') {
284 (void) strcat(strcat(newpat
, " >"), temp2
);
287 if ((file
= mypopen(newpat
, "w")) == NULL
) {
288 (void) fprintf(stderr
,
289 "cscope: cannot open pipe to shell command: %s\n",
293 while ((c
= getc(refsfound
)) != EOF
) {
294 (void) putc(c
, file
);
297 (void) mypclose(file
);
299 if (commandc
== '^') {
300 if (readrefs(temp2
) == NO
) {
301 putmsg("Ignoring empty output of ^ command");
308 case ctrl('L'): /* redraw screen */
310 (void) clearok(curscr
, TRUE
);
311 (void) wrefresh(curscr
);
312 drawscrollbar(topline
, bottomline
, totallines
);
315 case '!': /* shell escape */
316 (void) execute(shell
, shell
, (char *)NULL
);
327 case ctrl('E'): /* edit all lines */
331 case ctrl('A'): /* repeat last pattern */
332 case ctrl('Y'): /* (old command) */
333 if (*pattern
!= '\0') {
334 (void) addstr(pattern
);
339 case ctrl('B'): /* cmd history back */
340 case ctrl('F'): /* cmd history fwd */
341 curritem
= currentcmd();
342 item
= (commandc
== ctrl('F')) ? nextcmd() : prevcmd();
344 if (curritem
== item
) {
345 /* inform user that we're at history end */
347 "End of input field and search pattern history");
353 (void) addstr(item
->text
);
354 (void) strcpy(pattern
, item
->text
);
355 switch (c
= mygetch()) {
363 (void) clrtoeol(); /* clear current field */
369 case '\\': /* next character is not a command */
370 (void) addch('\\'); /* display the quote character */
372 /* get a character from the terminal */
373 if ((commandc
= mygetch()) == EOF
) {
374 return (NO
); /* quit */
376 (void) addstr("\b \b"); /* erase the quote character */
380 atfield(); /* move back to the input field */
383 /* edit a selected line */
384 if (isdigit(commandc
) && commandc
!= '0' && !mouse
) {
385 if (returnrequired
== NO
) {
386 editref(commandc
- '1');
388 (void) move(PRLINE
, 0);
389 (void) addstr(selectionprompt
);
391 COLS
- sizeof (selectionprompt
), commandc
,
393 (i
= atoi(newpat
)) > 0) {
398 } else if (isprint(commandc
)) {
399 /* this is the start of a pattern */
401 if (getaline(newpat
, COLS
- fldcolumn
- 1, commandc
,
403 (void) strcpy(pattern
, newpat
);
404 resetcmd(); /* reset history */
406 addcmd(field
, pattern
); /* add to history */
407 if (field
== CHANGE
) {
408 /* prompt for the new text */
409 (void) move(PRLINE
, 0);
410 (void) addstr(toprompt
);
411 (void) getaline(newpat
,
412 COLS
- sizeof (toprompt
), '\0', NO
);
414 /* search for the pattern */
415 if (search() == YES
) {
419 if (totallines
> 1) {
426 return (changestring());
428 } else if (field
== FILENAME
&&
429 access(newpat
, READ
) == 0) {
430 /* try to edit the file anyway */
433 } else { /* no pattern--the input was erased */
436 } else { /* control character */
443 /* clear the prompt line */
448 (void) move(PRLINE
, 0);
452 /* read references from a file */
455 readrefs(char *filename
)
460 if ((file
= fopen(filename
, "r")) == NULL
) {
461 cannotopen(filename
);
464 if ((c
= getc(file
)) == EOF
) { /* if file is empty */
469 if (writerefsfound() == YES
) {
470 (void) putc(c
, refsfound
);
471 while ((c
= getc(file
)) != EOF
) {
472 (void) putc(c
, refsfound
);
475 (void) freopen(temp1
, "r", refsfound
);
481 /* change one text string to another */
486 char buf
[PATLEN
+ 1]; /* input buffer */
487 char newfile
[PATHLEN
+ 1]; /* new file name */
488 char oldfile
[PATHLEN
+ 1]; /* old file name */
489 char linenum
[NUMLEN
+ 1]; /* file line number */
490 char msg
[MSGLEN
+ 1]; /* message */
491 FILE *script
; /* shell script file */
492 BOOL anymarked
= NO
; /* any line marked */
493 MOUSEEVENT
*p
; /* mouse data */
497 /* open the temporary file */
498 if ((script
= fopen(temp2
, "w")) == NULL
) {
502 /* create the line change indicators */
503 change
= (BOOL
*)mycalloc((unsigned)totallines
, sizeof (BOOL
));
507 /* until the quit command is entered */
509 /* display the current page of lines */
512 /* get a character from the terminal */
513 (void) move(PRLINE
, 0);
515 "Select lines to change (press the ? key for help): ");
516 if ((c
= mygetch()) == EOF
|| c
== ctrl('D') ||
518 break; /* change lines */
520 /* see if the input character is a command */
522 case ' ': /* display next page */
526 case '-': /* display previous page */
528 case '!': /* shell escape */
533 case ctrl('L'): /* redraw screen */
538 case ESC
: /* kept for backwards compatibility */
541 case '\r': /* don't change lines */
549 case '*': /* mark/unmark all displayed lines */
550 for (i
= 0; topline
+ i
< nextline
; ++i
) {
555 case 'a': /* mark/unmark all lines */
556 for (i
= 0; i
< totallines
; ++i
) {
557 if (change
[i
] == NO
) {
563 /* show that all have been marked */
564 seekline(totallines
);
566 case ctrl('X'): /* mouse selection */
567 if ((p
= getmouseevent()) == NULL
) {
568 goto same
; /* unknown control sequence */
570 /* if the button number is a scrollbar tag */
571 if (p
->button
== '0') {
575 /* find the selected line */
576 /* note: the selection is forced into range */
577 for (i
= disprefs
- 1; i
> 0; --i
) {
578 if (p
->y1
>= displine
[i
]) {
585 /* if a line was selected */
586 if (isdigit(c
) && c
!= '0' && !mouse
) {
587 if (returnrequired
== NO
) {
591 (void) move(PRLINE
, 0);
592 (void) addstr(selectionprompt
);
594 COLS
- sizeof (selectionprompt
), c
,
596 (i
= atoi(buf
)) > 0) {
604 /* for each line containing the old text */
605 (void) fprintf(script
, "ed - <<\\!\nH\n");
608 for (i
= 0; fscanf(refsfound
, "%s%*s%s%*[^\n]", newfile
, linenum
) == 2;
610 /* see if the line is to be changed */
611 if (change
[i
] == YES
) {
614 /* if this is a new file */
615 if (strcmp(newfile
, oldfile
) != 0) {
617 /* make sure it can be changed */
618 if (access(newfile
, WRITE
) != 0) {
620 "Cannot write to file %s",
626 /* if there was an old file */
627 if (*oldfile
!= '\0') {
628 (void) fprintf(script
,
629 "w\n"); /* save it */
631 /* edit the new file */
632 (void) strcpy(oldfile
, newfile
);
633 (void) fprintf(script
, "e %s\n", oldfile
);
635 /* output substitute command */
636 (void) fprintf(script
,
637 "%ss/", linenum
); /* change */
638 for (s
= pattern
; *s
!= '\0'; ++s
) { /* old text */
640 (void) putc('\\', script
);
642 (void) putc(*s
, script
);
644 (void) putc('/', script
); /* to */
645 for (s
= newpat
; *s
!= '\0'; ++s
) { /* new text */
646 if (strchr("/\\&", *s
) != NULL
) {
647 (void) putc('\\', script
);
649 (void) putc(*s
, script
);
651 (void) fprintf(script
, "/gp\n"); /* and print */
654 (void) fprintf(script
, "w\nq\n!\n"); /* write and quit */
655 (void) fclose(script
);
658 /* if any line was marked */
659 if (anymarked
== YES
) {
662 (void) fprintf(stderr
, "Changed lines:\n\r");
663 (void) execute(shell
, shell
, temp2
, (char *)NULL
);
671 return (YES
); /* clear any marks on exit without change */
674 /* mark/unmark this displayed line to be changed */
682 if (j
< totallines
) {
683 (void) move(displine
[i
], selectlen
);
684 if (change
[j
] == NO
) {
694 /* scrollbar actions */
697 scrollbar(MOUSEEVENT
*p
)
699 /* reposition list if it makes sense */
700 if (totallines
== 0) {
703 switch (p
->percent
) {
705 case 101: /* scroll down one page */
706 if (nextline
+ mdisprefs
> totallines
) {
707 nextline
= totallines
- mdisprefs
+ 1;
711 case 102: /* scroll up one page */
712 nextline
= topline
- mdisprefs
;
718 case 103: /* scroll down one line */
719 nextline
= topline
+ 1;
722 case 104: /* scroll up one line */
724 nextline
= topline
- 1;
728 nextline
= p
->percent
* totallines
/ 100;