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