1 /*===========================================================================
2 Copyright (c) 1998-2000, The Santa Cruz Operation
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 *Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
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.
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.
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
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
31 =========================================================================*/
33 /* cscope - interactive C symbol or text cross-reference
39 #include "build.h" /* for rebuild() */
43 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
50 static char const rcsid[] = "$Id: command.c,v 1.36 2012/05/20 13:58:53 broeker Exp $";
54 unsigned int curdispline = 0;
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 */
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: ";
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);
78 /* execute the command */
82 char filename[PATHLEN + 1]; /* file path name */
83 MOUSE *p; /* mouse data */
86 struct cmd *curritem, *item; /* command history */
90 case ctrl('C'): /* toggle caseless mode */
93 postmsg2("Caseless mode is now ON");
96 postmsg2("Caseless mode is now OFF");
98 egrepcaseless(caseless); /* turn on/off -i flag */
101 case ctrl('R'): /* rebuild the cross reference */
102 if (isuptodate == YES) {
103 postmsg("The -d option prevents rebuilding the symbol database");
107 freefilelist(); /* remake the source file list */
110 if (errorsfound == YES) {
115 clearmsg(); /* clear any previous message */
118 topline = nextline = 1;
123 case ESC: /* possible unixpc mouse selection */
125 case ctrl('X'): /* mouse selection */
126 if ((p = getmouseaction(DUMMYCHAR)) == NULL) {
127 return(NO); /* unknown control sequence */
129 /* if the button number is a scrollbar tag */
130 if (p->button == '0') {
138 /* if this is a line selection */
139 if (p->y1 < FLDLINE) {
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]) {
148 /* display it in the file with the editor */
150 } else { /* this is an input field selection */
151 field = p->y1 - FLDLINE;
152 /* force it into range */
153 if (field >= FIELDS) {
162 case '\t': /* go to next input field */
164 selecting = !selecting;
166 move(displine[curdispline], 0);
179 case '\n': /* go to reference */
181 editref(curdispline);
194 if ((curdispline + 1) < disprefs) {
195 move(displine[++curdispline], 0);
199 field = (field + 1) % FIELDS;
206 case ctrl('P'): /* go to previous input field */
215 move(displine[--curdispline], 0);
219 field = (field + (FIELDS - 1)) % FIELDS;
226 case KEY_HOME: /* go to first input field */
241 case KEY_LL: /* go to last input field */
243 move(displine[disprefs - 1], 0);
252 #endif /* def(KEY_LL) */
254 case ' ': /* display next page */
260 /* don't redisplay if there are no lines */
261 if (totallines == 0) {
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
272 case '-': /* display previous page */
276 /* don't redisplay if there are no lines */
277 if (totallines == 0) {
283 /* if there are only two pages, just go to the other one */
284 if (totallines <= 2 * mdisprefs) {
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) {
292 nextline -= mdisprefs;
294 nextline = totallines - mdisprefs + 1;
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 */
308 addstr("Write to file: ");
310 if ((c = mygetch()) == '>') {
312 addstr(appendprompt);
317 mygetline("", newpat,
318 COLS - sizeof(appendprompt), c, NO) > 0
320 shellpath(filename, sizeof(filename), newpat);
321 if ((file = myfopen(filename, s)) == NULL) {
322 cannotopen(filename);
325 while ((c = getc(refsfound)) != EOF) {
334 return(NO); /* return to the previous field */
336 case '<': /* read lines from a file */
339 if (mygetline("", newpat, COLS - sizeof(readprompt),
342 shellpath(filename, sizeof(filename), newpat);
343 if (readrefs(filename) == NO) {
344 postmsg2("Ignoring an empty file");
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");
358 /* get the shell command */
361 if (mygetline("", newpat, COLS - sizeof(pipeprompt), '\0', NO)
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
377 if ((file = mypopen(newpat, "w")) == NULL) {
379 cscope: cannot open pipe to shell command: %s\n", newpat);
382 while ((c = getc(refsfound)) != EOF) {
388 if (commandc == '^') {
389 if (readrefs(temp2) == NO) {
390 postmsg("Ignoring empty output of ^ command");
396 #if defined(KEY_RESIZE) && !defined(__DJGPP__)
402 keypad(stdscr, TRUE); /* enable the keypad */
403 #ifdef HAVE_FIXKEYPAD
404 fixkeypad(); /* fix for getch() intermittently returning garbage */
408 standend(); /* turn off reverse video */
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 */
416 case ctrl('L'): /* redraw screen */
421 clearok(curscr, TRUE);
423 drawscrollbar(topline, bottomline);
426 case '!': /* shell escape */
427 execute(shell, shell, NULL);
438 case ctrl('E'): /* edit all lines */
442 case ctrl('A'): /* HBB 20050428: added alt. keymapping */
443 case ctrl('Y'): /* repeat last pattern */
444 if (*Pattern != '\0') {
450 case ctrl('B'): /* cmd history back */
451 case ctrl('F'): /* cmd history fwd */
456 curritem = currentcmd();
457 item = (commandc == ctrl('F')) ? nextcmd() : prevcmd();
459 if (curritem == item) { /* inform user that we're at history end */
460 postmsg2("End of input field and search pattern history");
467 strcpy(Pattern, item->text);
468 switch (c = mygetch()) {
476 clrtoeol(); /* clear current field */
480 if (mygetline(Pattern, newpat, COLS - fldcolumn - 1, '\0', caseless )) {
481 strcpy (Pattern, newpat);
490 case '\\': /* next character is not a command */
491 addch('\\'); /* display the quote character */
493 /* get a character from the terminal */
494 if ((commandc = mygetch()) == EOF) {
495 return(NO); /* quit */
497 addstr("\b \b"); /* erase the quote character */
501 postmsg("The . command has been replaced by ^Y");
502 atfield(); /* move back to the input field */
505 if (selecting && !mouse) {
508 if ((c = strchr(dispchars, commandc)))
509 editref(c - dispchars);
511 /* if this is the start of a pattern */
512 } else if (isprint(commandc)) {
514 if (mygetline("", newpat, COLS - fldcolumn - 1,
515 commandc, caseless) > 0) {
516 strcpy(Pattern, newpat);
517 resetcmd(); /* reset command history */
519 addcmd(field, Pattern); /* add to command history */
520 if (field == CHANGE) {
521 /* prompt for the new text */
524 mygetline("", newpat,
525 COLS - sizeof(toprompt),
528 /* search for the pattern */
529 if (search() == YES) {
536 if (totallines > 1) {
543 return(changestring());
546 } else if (field == FILENAME &&
547 access(newpat, READ) == 0) {
548 /* try to edit the file anyway */
551 } else { /* no pattern--the input was erased */
554 } else { /* control character */
557 } /* switch(commandc) */
561 /* clear the prompt line */
570 /* read references from a file */
573 readrefs(char *filename)
578 if ((file = myfopen(filename, "rb")) == NULL) {
579 cannotopen(filename);
582 if ((c = getc(file)) == EOF) { /* if file is empty */
588 if (writerefsfound() == YES) {
590 while ((c = getc(file)) != EOF) {
595 if ( (refsfound = myfopen(temp1, "rb")) == NULL) {
604 /* change one text string to another */
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 */
620 /* open the temporary file */
621 if ((script = myfopen(temp2, "w")) == NULL) {
625 /* create the line change indicators */
626 change = mycalloc(totallines, sizeof(BOOL));
630 /* until the quit command is entered */
632 /* display the current page of lines */
637 /* get a character from the terminal */
638 if ((c = mygetch()) == EOF || c == ctrl('D')) {
639 break; /* change lines */
641 if (c == ctrl('Z')) {
646 break; /* change lines */
649 /* see if the input character is a command */
651 case ' ': /* display next page */
657 case '-': /* display previous page */
661 case '!': /* shell escape */
666 case ctrl('L'): /* redraw screen */
673 case ESC: /* don't change lines */
675 if((p = getmouseaction(DUMMYCHAR)) == NULL) {
676 goto nochange; /* unknown escape sequence */
683 case '*': /* mark/unmark all displayed lines */
684 for (i = 0; topline + i < nextline; ++i) {
689 case ctrl('A'): /* mark/unmark all lines */
690 for (i = 0; i < totallines; ++i) {
691 if (change[i] == NO) {
697 /* show that all have been marked */
698 seekline(totallines);
701 case ctrl('X'): /* mouse selection */
702 if ((p = getmouseaction(DUMMYCHAR)) == NULL) {
703 goto same; /* unknown control sequence */
705 /* if the button number is a scrollbar tag */
706 if (p->button == '0') {
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]) {
722 /* if a line was selected */
725 if ((cc = strchr(dispchars, c)))
726 mark(cc - dispchars);
730 } /* switch(change code character) */
733 /* for each line containing the old text */
734 fprintf(script, "ed - <<\\!\n");
738 fscanf(refsfound, "%" PATHLEN_STR "s%*s%" NUMLEN_STR "s%*[^\n]", newfile, linenum) == 2;
740 /* see if the line is to be changed */
741 if (change[i] == YES) {
744 /* if this is a new file */
745 if (strcmp(newfile, oldfile) != 0) {
747 /* make sure it can be changed */
748 if (access(newfile, WRITE) != 0) {
749 snprintf(msg, sizeof(msg), "Cannot write to file %s", newfile);
754 /* if there was an old file */
755 if (*oldfile != '\0') {
756 fprintf(script, "w\n"); /* save it */
758 /* edit the new file */
759 strcpy(oldfile, newfile);
760 fprintf(script, "e %s\n", oldfile);
762 /* output substitute command */
763 fprintf(script, "%ss/", linenum); /* change */
764 for (s = Pattern; *s != '\0'; ++s) {
766 if (strchr("/\\[.^*", *s) != NULL) {
769 if (caseless == YES && isalpha((unsigned char)*s)) {
771 if(islower((unsigned char)*s)) {
772 putc(toupper((unsigned char)*s), script);
776 putc(tolower((unsigned char)*s), script);
782 putc('/', script); /* to */
783 for (s = newpat; *s != '\0'; ++s) { /* new text */
784 if (strchr("/\\&", *s) != NULL) {
789 fprintf(script, "/gp\n"); /* and print */
792 fprintf(script, "w\nq\n!\n"); /* write and quit */
795 /* if any line was marked */
796 if (anymarked == YES) {
801 fprintf(stderr, "Changed lines:\n\r");
802 execute("sh", "sh", temp2, NULL);
816 /* mark/unmark this displayed line to be changed */
823 if (j < totallines) {
824 move(displine[i], 1);
826 if (change[j] == NO) {
837 /* scrollbar actions */
841 /* reposition list if it makes sense */
842 if (totallines == 0) {
845 switch (p->percent) {
847 case 101: /* scroll down one page */
848 if (nextline + mdisprefs > totallines) {
849 nextline = totallines - mdisprefs + 1;
853 case 102: /* scroll up one page */
854 nextline = topline - mdisprefs;
860 case 103: /* scroll down one line */
861 nextline = topline + 1;
864 case 104: /* scroll up one line */
866 nextline = topline - 1;
870 nextline = p->percent * totallines / 100;
876 /* count the references found */
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 */
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") */
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
904 || !isgraph((unsigned char) *file)
905 || !isgraph((unsigned char) *function)
906 || !isdigit((unsigned char) *linenum)
908 postmsg("File does not have expected format");
913 if ((i = strlen(pathcomponents(file, dispcomponents))) > filelen) {
917 ogsnames(file, &subsystem, &book);
918 if ((i = strlen(subsystem)) > subsystemlen) {
921 if ((i = strlen(book)) > booklen) {
925 if ((i = strlen(function)) > fcnlen) {
928 if ((i = strlen(linenum)) > numlen) {
935 /* restrict the width of displayed columns */
936 /* HBB FIXME 20060419: magic number alert! */
941 if (filelen > i && i > 4) {
944 if (subsystemlen > i && i > 9) {
947 if (booklen > i && i > 4) {
950 if (fcnlen > i && i > 8) {