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 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 446 clearprompt(void) 447 { 448 (void) move(PRLINE, 0); 449 (void) clrtoeol(); 450 } 451 452 /* read references from a file */ 453 454 BOOL 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 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 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 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