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