1 /* 2 * Part 2 of the mined editor. 3 */ 4 5 /* ======================================================================== * 6 * Move Commands * 7 * ======================================================================== */ 8 9 #include "mined.h" 10 #include <string.h> 11 12 /* 13 * Move one line up. 14 */ 15 void UP1(void) 16 { 17 if (y == 0) { /* Top line of screen. Scroll one line */ 18 (void) reverse_scroll(); 19 move_to(x, y); 20 } 21 else /* Move to previous line */ 22 move_to(x, y - 1); 23 } 24 25 /* 26 * Move one line down. 27 */ 28 void DN1(void) 29 { 30 if (y == last_y) { /* Last line of screen. Scroll one line */ 31 if (bot_line->next == tail && bot_line->text[0] != '\n') { 32 dummy_line(); /* Create new empty line */ 33 DN1(); 34 return; 35 } 36 else { 37 (void) forward_scroll(); 38 move_to(x, y); 39 } 40 } 41 else /* Move to next line */ 42 move_to(x, y + 1); 43 } 44 45 /* 46 * Move left one position. 47 */ 48 void LF1(void) 49 { 50 if (x == 0 && get_shift(cur_line->shift_count) == 0) {/* Begin of line */ 51 if (cur_line->prev != header) { 52 UP1(); /* Move one line up */ 53 move_to(LINE_END, y); 54 } 55 } 56 else 57 move_to(x - 1, y); 58 } 59 60 /* 61 * Move right one position. 62 */ 63 void RT1(void) 64 { 65 if (*cur_text == '\n') { 66 if (cur_line->next != tail) { /* Last char of file */ 67 DN1(); /* Move one line down */ 68 move_to(LINE_START, y); 69 } 70 } 71 else 72 move_to(x + 1, y); 73 } 74 75 /* 76 * Move to coordinates [0, 0] on screen. 77 */ 78 void HIGH(void) 79 { 80 move_to(0, 0); 81 } 82 83 /* 84 * Move to coordinates [0, YMAX] on screen. 85 */ 86 void LOW(void) 87 { 88 move_to(0, last_y); 89 } 90 91 /* 92 * Move to begin of line. 93 */ 94 void BL(void) 95 { 96 move_to(LINE_START, y); 97 } 98 99 /* 100 * Move to end of line. 101 */ 102 void EL(void) 103 { 104 move_to(LINE_END, y); 105 } 106 107 /* 108 * GOTO() prompts for a linenumber and moves to that line. 109 */ 110 void GOTO(void) 111 { 112 int number; 113 LINE *line; 114 115 if (get_number("Please enter line number.", &number) == ERRORS) 116 return; 117 118 if (number <= 0 || (line = proceed(header->next, number - 1)) == tail) 119 error("Illegal line number: ", num_out((long) number)); 120 else 121 move_to(x, find_y(line)); 122 } 123 124 /* 125 * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes 126 * top_line of display.) Try to leave the cursor on the same line. If this is 127 * not possible, leave cursor on the line halfway the page. 128 */ 129 void PD(void) 130 { 131 register int i; 132 133 for (i = 0; i < screenmax; i++) 134 if (forward_scroll() == ERRORS) 135 break; /* EOF reached */ 136 if (y - i < 0) /* Line no longer on screen */ 137 move_to(0, screenmax >> 1); 138 else 139 move_to(0, y - i); 140 } 141 142 143 /* 144 * Scroll backwards one page or to top of file, whatever comes first. (Top_line 145 * becomes bot_line of display). The very bottom line (YMAX) is always blank. 146 * Try to leave the cursor on the same line. If this is not possible, leave 147 * cursor on the line halfway the page. 148 */ 149 void PU(void) 150 { 151 register int i; 152 153 for (i = 0; i < screenmax; i++) 154 if (reverse_scroll() == ERRORS) 155 break; /* Top of file reached */ 156 set_cursor(0, ymax); /* Erase very bottom line */ 157 tputs(CE, 0, _putch); 158 if (y + i > screenmax) /* line no longer on screen */ 159 move_to(0, screenmax >> 1); 160 else 161 move_to(0, y + i); 162 } 163 164 /* 165 * Go to top of file, scrolling if possible, else redrawing screen. 166 */ 167 void HO(void) 168 { 169 if (proceed(top_line, -screenmax) == header) 170 PU(); /* It fits. Let PU do it */ 171 else { 172 reset(header->next, 0);/* Reset top_line, etc. */ 173 RD(); /* Display full page */ 174 } 175 move_to(LINE_START, 0); 176 } 177 178 /* 179 * Go to last line of file, scrolling if possible, else redrawing screen 180 */ 181 void EF(void) 182 { 183 if (tail->prev->text[0] != '\n') 184 dummy_line(); 185 if (proceed(bot_line, screenmax) == tail) 186 PD(); /* It fits. Let PD do it */ 187 else { 188 reset(proceed(tail->prev, -screenmax), screenmax); 189 RD(); /* Display full page */ 190 } 191 move_to(LINE_START, last_y); 192 } 193 194 /* 195 * Scroll one line up. Leave the cursor on the same line (if possible). 196 */ 197 void SU(void) 198 { 199 if (top_line->prev == header) /* Top of file. Can't scroll */ 200 return; 201 202 (void) reverse_scroll(); 203 set_cursor(0, ymax); /* Erase very bottom line */ 204 tputs(CE, 0, _putch); 205 move_to(x, (y == screenmax) ? screenmax : y + 1); 206 } 207 208 /* 209 * Scroll one line down. Leave the cursor on the same line (if possible). 210 */ 211 void SD(void) 212 { 213 if (forward_scroll() != ERRORS) 214 move_to(x, (y == 0) ? 0 : y - 1); 215 else 216 set_cursor(x, y); 217 } 218 219 /* 220 * Perform a forward scroll. It returns ERRORS if we're at the last line of the 221 * file. 222 */ 223 int forward_scroll(void) 224 { 225 if (bot_line->next == tail) /* Last line of file. No dice */ 226 return ERRORS; 227 top_line = top_line->next; 228 bot_line = bot_line->next; 229 cur_line = cur_line->next; 230 set_cursor(0, ymax); 231 line_print(bot_line); 232 233 return FINE; 234 } 235 236 /* 237 * Perform a backwards scroll. It returns ERRORS if we're at the first line 238 * of the file. 239 */ 240 int reverse_scroll(void) 241 { 242 if (top_line->prev == header) 243 return ERRORS; /* Top of file. Can't scroll */ 244 245 if (last_y != screenmax) /* Reset last_y if necessary */ 246 last_y++; 247 else 248 bot_line = bot_line->prev; /* Else adjust bot_line */ 249 top_line = top_line->prev; 250 cur_line = cur_line->prev; 251 252 /* Perform the scroll */ 253 set_cursor(0, 0); 254 tputs(AL, 0, _putch); 255 set_cursor(0, 0); 256 line_print(top_line); 257 258 return FINE; 259 } 260 261 /* 262 * A word is defined as a number of non-blank characters separated by tabs 263 * spaces or linefeeds. 264 */ 265 266 /* 267 * MP() moves to the start of the previous word. A word is defined as a 268 * number of non-blank characters separated by tabs spaces or linefeeds. 269 */ 270 void MP(void) 271 { 272 move_previous_word(NO_DELETE); 273 } 274 275 void move_previous_word(FLAG remove) 276 { 277 register char *begin_line; 278 register char *textp; 279 char start_char = *cur_text; 280 char *start_pos = cur_text; 281 282 /* Fist check if we're at the beginning of line. */ 283 if (cur_text == cur_line->text) { 284 if (cur_line->prev == header) 285 return; 286 start_char = '\0'; 287 } 288 289 LF1(); 290 291 begin_line = cur_line->text; 292 textp = cur_text; 293 294 /* Check if we're in the middle of a word. */ 295 if (!alpha(*textp) || !alpha(start_char)) { 296 while (textp != begin_line && (white_space(*textp) || *textp == '\n')) 297 textp--; 298 } 299 300 /* Now we're at the end of previous word. Skip non-blanks until a blank comes */ 301 while (textp != begin_line && alpha(*textp)) 302 textp--; 303 304 /* Go to the next char if we're not at the beginning of the line */ 305 if (textp != begin_line && *textp != '\n') 306 textp++; 307 308 /* Find the x-coordinate of this address, and move to it */ 309 move_address(textp); 310 if (remove == DELETE) 311 delete(cur_line, textp, cur_line, start_pos); 312 } 313 314 /* 315 * MN() moves to the start of the next word. A word is defined as a number of 316 * non-blank characters separated by tabs spaces or linefeeds. Always keep in 317 * mind that the pointer shouldn't pass the '\n'. 318 */ 319 void MN(void) 320 { 321 move_next_word(NO_DELETE); 322 } 323 324 void move_next_word(FLAG remove) 325 { 326 register char *textp = cur_text; 327 328 /* Move to the end of the current word. */ 329 while (*textp != '\n' && alpha(*textp)) 330 textp++; 331 332 /* Skip all white spaces */ 333 while (*textp != '\n' && white_space(*textp)) 334 textp++; 335 /* If we're deleting. delete the text in between */ 336 if (remove == DELETE) { 337 delete(cur_line, cur_text, cur_line, textp); 338 return; 339 } 340 341 /* If we're at end of line. move to the first word on the next line. */ 342 if (*textp == '\n' && cur_line->next != tail) { 343 DN1(); 344 move_to(LINE_START, y); 345 textp = cur_text; 346 while (*textp != '\n' && white_space(*textp)) 347 textp++; 348 } 349 move_address(textp); 350 } 351 352 /* ======================================================================== * 353 * Modify Commands * 354 * ======================================================================== */ 355 356 /* 357 * DCC deletes the character under the cursor. If this character is a '\n' the 358 * current line is joined with the next one. 359 * If this character is the only character of the line, the current line will 360 * be deleted. 361 */ 362 void DCC(void) 363 { 364 if (*cur_text == '\n') 365 delete(cur_line,cur_text, cur_line->next,cur_line->next->text); 366 else 367 delete(cur_line, cur_text, cur_line, cur_text + 1); 368 } 369 370 /* 371 * DPC deletes the character on the left side of the cursor. If the cursor is 372 * at the beginning of the line, the last character if the previous line is 373 * deleted. 374 */ 375 void DPC(void) 376 { 377 if (x == 0 && cur_line->prev == header) 378 return; /* Top of file */ 379 380 LF1(); /* Move one left */ 381 DCC(); /* Delete character under cursor */ 382 } 383 384 /* 385 * DLN deletes all characters until the end of the line. If the current 386 * character is a '\n', then delete that char. 387 */ 388 void DLN(void) 389 { 390 if (*cur_text == '\n') 391 DCC(); 392 else 393 delete(cur_line, cur_text, cur_line, cur_text + length_of(cur_text) -1); 394 } 395 396 /* 397 * DNW() deletes the next word (as described in MN()) 398 */ 399 void DNW(void) 400 { 401 if (*cur_text == '\n') 402 DCC(); 403 else 404 move_next_word(DELETE); 405 } 406 407 /* 408 * DPW() deletes the next word (as described in MP()) 409 */ 410 void DPW(void) 411 { 412 if (cur_text == cur_line->text) 413 DPC(); 414 else 415 move_previous_word(DELETE); 416 } 417 418 /* 419 * Insert character `character' at current location. 420 */ 421 void S(int character) 422 { 423 static char buffer[2]; 424 425 buffer[0] = character; 426 /* Insert the character */ 427 if (insert(cur_line, cur_text, buffer) == ERRORS) 428 return; 429 430 /* Fix screen */ 431 if (character == '\n') { 432 set_cursor(0, y); 433 if (y == screenmax) { /* Can't use display */ 434 line_print(cur_line); 435 (void) forward_scroll(); 436 } 437 else { 438 reset(top_line, y); /* Reset pointers */ 439 display(0, y, cur_line, last_y - y); 440 } 441 move_to(0, (y == screenmax) ? y : y + 1); 442 } 443 else if (x + 1 == XBREAK)/* If line must be shifted, just call move_to*/ 444 move_to(x + 1, y); 445 else { /* else display rest of line */ 446 put_line(cur_line, x, FALSE); 447 move_to(x + 1, y); 448 } 449 } 450 451 /* 452 * CTL inserts a control-char at the current location. A message that this 453 * function is called is displayed at the status line. 454 */ 455 void CTL(void) 456 { 457 register char ctrl; 458 459 status_line("Enter control character.", NULL); 460 if ((ctrl = getch()) >= '\01' && ctrl <= '\037') { 461 S(ctrl); /* Insert the char */ 462 clear_status(); 463 } 464 else 465 error ("Unknown control character", NULL); 466 } 467 468 /* 469 * LIB insert a line at the current position and moves back to the end of 470 * the previous line. 471 */ 472 void LIB(void) 473 { 474 S('\n'); /* Insert the line */ 475 UP1(); /* Move one line up */ 476 move_to(LINE_END, y); /* Move to end of this line */ 477 } 478 479 /* 480 * Line_insert() inserts a new line with text pointed to by `string'. 481 * It returns the address of the new line. 482 */ 483 LINE *line_insert(register LINE *line, char *string, int len) 484 { 485 register LINE *new_line; 486 487 /* Allocate space for LINE structure and text */ 488 new_line = install_line(string, len); 489 490 /* Install the line into the double linked list */ 491 new_line->prev = line; 492 new_line->next = line->next; 493 line->next = new_line; 494 new_line->next->prev = new_line; 495 496 /* Increment nlines */ 497 nlines++; 498 499 return new_line; 500 } 501 502 /* 503 * Insert() insert the string `string' at the given line and location. 504 */ 505 int insert(register LINE *line, char *location, char *string) 506 { 507 register char *bufp = text_buffer; /* Buffer for building line */ 508 register char *textp = line->text; 509 510 if (length_of(textp) + length_of(string) >= MAX_CHARS) { 511 error("Line too long", NULL); 512 return ERRORS; 513 } 514 515 modified = TRUE; /* File has been modified */ 516 517 /* Copy part of line until `location' has been reached */ 518 while (textp != location) 519 *bufp++ = *textp++; 520 521 /* Insert string at this location */ 522 while (*string != '\0') 523 *bufp++ = *string++; 524 *bufp = '\0'; 525 526 if (*(string - 1) == '\n') /* Insert a new line */ 527 (void) line_insert(line, location, length_of(location)); 528 else /* Append last part of line */ 529 copy_string(bufp, location); 530 531 /* Install the new text in this line */ 532 free_space(line->text); 533 line->text = alloc(length_of(text_buffer) + 1); 534 copy_string(line->text, text_buffer); 535 536 return FINE; 537 } 538 539 /* 540 * Line_delete() deletes the argument line out of the line list. The pointer to 541 * the next line is returned. 542 */ 543 LINE *line_delete(register LINE *line) 544 { 545 register LINE *next_line = line->next; 546 547 /* Delete the line */ 548 line->prev->next = line->next; 549 line->next->prev = line->prev; 550 551 /* Free allocated space */ 552 free_space(line->text); 553 free_space((char*)line); 554 555 /* Decrement nlines */ 556 nlines--; 557 558 return next_line; 559 } 560 561 /* 562 * Delete() deletes all the characters (including newlines) between the 563 * startposition and endposition and fixes the screen accordingly. It 564 * returns the number of lines deleted. 565 */ 566 void delete(register LINE *start_line, char *start_textp, LINE *end_line, 567 char *end_textp) 568 { 569 register char *textp = start_line->text; 570 register char *bufp = text_buffer; /* Storage for new line->text */ 571 LINE *line, *stop; 572 int line_cnt = 0; /* Nr of lines deleted */ 573 int count = 0; 574 int shift = 0; /* Used in shift calculation */ 575 int nx = x; 576 577 modified = TRUE; /* File has been modified */ 578 579 /* Set up new line. Copy first part of start line until start_position. */ 580 while (textp < start_textp) { 581 *bufp++ = *textp++; 582 count++; 583 } 584 585 /* Check if line doesn't exceed MAX_CHARS */ 586 if (count + length_of(end_textp) >= MAX_CHARS) { 587 error("Line too long", NULL); 588 return; 589 } 590 591 /* Copy last part of end_line if end_line is not tail */ 592 copy_string(bufp, (end_textp != NULL) ? end_textp : "\n"); 593 594 /* Delete all lines between start and end_position (including end_line) */ 595 line = start_line->next; 596 stop = end_line->next; 597 while (line != stop && line != tail) { 598 line = line_delete(line); 599 line_cnt++; 600 } 601 602 /* Check if last line of file should be deleted */ 603 if (end_textp == NULL && length_of(start_line->text) == 1 && nlines > 1) { 604 start_line = start_line->prev; 605 (void) line_delete(start_line->next); 606 line_cnt++; 607 } 608 else { /* Install new text */ 609 free_space(start_line->text); 610 start_line->text = alloc(length_of(text_buffer) + 1); 611 copy_string(start_line->text, text_buffer); 612 } 613 614 /* Fix screen. First check if line is shifted. Perhaps we should shift it back*/ 615 if (get_shift(start_line->shift_count)) { 616 shift = (XBREAK - count_chars(start_line)) / SHIFT_SIZE; 617 if (shift > 0) { /* Shift line `shift' back */ 618 if (shift >= get_shift(start_line->shift_count)) 619 start_line->shift_count = 0; 620 else 621 start_line->shift_count -= shift; 622 nx += shift * SHIFT_SIZE;/* Reset x value */ 623 } 624 } 625 626 if (line_cnt == 0) { /* Check if only one line changed */ 627 if (shift > 0) { /* Reprint whole line */ 628 set_cursor(0, y); 629 line_print(start_line); 630 } 631 else { /* Just display last part of line */ 632 set_cursor(x, y); 633 put_line(start_line, x, TRUE); 634 } 635 move_to(nx, y); /* Reset cur_text */ 636 return; 637 } 638 639 shift = last_y; /* Save value */ 640 reset(top_line, y); 641 display(0, y, start_line, shift - y); 642 move_to((line_cnt == 1) ? nx : 0, y); 643 } 644 645 /* ======================================================================== * 646 * Yank Commands * 647 * ======================================================================== */ 648 649 LINE *mark_line; /* For marking position. */ 650 char *mark_text; 651 int lines_saved; /* Nr of lines in buffer */ 652 653 /* 654 * PT() inserts the buffer at the current location. 655 */ 656 void PT(void) 657 { 658 register int fd; /* File descriptor for buffer */ 659 660 if ((fd = scratch_file(READ)) == ERRORS) 661 error("Buffer is empty.", NULL); 662 else { 663 file_insert(fd, FALSE);/* Insert the buffer */ 664 (void) close(fd); 665 } 666 } 667 668 /* 669 * IF() prompt for a filename and inserts the file at the current location 670 * in the file. 671 */ 672 void IF(void) 673 { 674 register int fd; /* File descriptor of file */ 675 char name[LINE_LEN]; /* Buffer for file name */ 676 677 /* Get the file name */ 678 if (get_file("Get and insert file:", name) != FINE) 679 return; 680 681 if ((fd = open(name, 0)) < 0) 682 error("Cannot open ", name); 683 else { 684 file_insert(fd, TRUE); /* Insert the file */ 685 (void) close(fd); 686 } 687 } 688 689 /* 690 * File_insert() inserts a an opened file (as given by filedescriptor fd) 691 * at the current location. 692 */ 693 void file_insert(int fd, FLAG old_pos) 694 { 695 char line_buffer[MAX_CHARS]; /* Buffer for next line */ 696 register LINE *line = cur_line; 697 register int line_count = nlines; /* Nr of lines inserted */ 698 LINE *page = cur_line; 699 int ret = ERRORS; 700 701 /* Get the first piece of text (might be ended with a '\n') from fd */ 702 if (get_line(fd, line_buffer) == ERRORS) 703 return; /* Empty file */ 704 705 /* Insert this text at the current location. */ 706 if (insert(line, cur_text, line_buffer) == ERRORS) 707 return; 708 709 /* Repeat getting lines (and inserting lines) until EOF is reached */ 710 while ((ret = get_line(fd, line_buffer)) != ERRORS && ret != NO_LINE) 711 line = line_insert(line, line_buffer, ret); 712 713 if (ret == NO_LINE) { /* Last line read not ended by a '\n' */ 714 line = line->next; 715 (void) insert(line, line->text, line_buffer); 716 } 717 718 /* Calculate nr of lines added */ 719 line_count = nlines - line_count; 720 721 /* Fix the screen */ 722 if (line_count == 0) { /* Only one line changed */ 723 set_cursor(0, y); 724 line_print(line); 725 move_to((old_pos == TRUE) ? x : x + length_of(line_buffer), y); 726 } 727 else { /* Several lines changed */ 728 reset(top_line, y); /* Reset pointers */ 729 while (page != line && page != bot_line->next) 730 page = page->next; 731 if (page != bot_line->next || old_pos == TRUE) 732 display(0, y, cur_line, screenmax - y); 733 if (old_pos == TRUE) 734 move_to(x, y); 735 else if (ret == NO_LINE) 736 move_to(length_of(line_buffer), find_y(line)); 737 else 738 move_to(0, find_y(line->next)); 739 } 740 741 /* If nr of added line >= REPORT, print the count */ 742 if (line_count >= REPORT) 743 status_line(num_out((long) line_count), " lines added."); 744 } 745 746 /* 747 * WB() writes the buffer (yank_file) into another file, which 748 * is prompted for. 749 */ 750 void WB(void) 751 { 752 register int new_fd; /* Filedescriptor to copy file */ 753 int yank_fd; /* Filedescriptor to buffer */ 754 register int cnt; /* Count check for read/write */ 755 int ret = 0; /* Error check for write */ 756 char file[LINE_LEN]; /* Output file */ 757 758 /* Checkout the buffer */ 759 if ((yank_fd = scratch_file(READ)) == ERRORS) { 760 error("Buffer is empty.", NULL); 761 return; 762 } 763 764 /* Get file name */ 765 if (get_file("Write buffer to file:", file) != FINE) 766 return; 767 768 /* Creat the new file */ 769 if ((new_fd = creat(file, 0644)) < 0) { 770 error("Cannot create ", file); 771 return; 772 } 773 774 status_line("Writing ", file); 775 776 /* Copy buffer into file */ 777 while ((cnt = read(yank_fd, text_buffer, sizeof(text_buffer))) > 0) 778 if (write(new_fd, text_buffer, cnt) != cnt) { 779 bad_write(new_fd); 780 ret = ERRORS; 781 break; 782 } 783 784 /* Clean up open files and status_line */ 785 (void) close(new_fd); 786 (void) close(yank_fd); 787 788 if (ret != ERRORS) /* Bad write */ 789 file_status("Wrote", chars_saved, file, lines_saved, TRUE, FALSE); 790 } 791 792 /* 793 * MA sets mark_line (mark_text) to the current line (text pointer). 794 */ 795 void MA(void) 796 { 797 mark_line = cur_line; 798 mark_text = cur_text; 799 status_line("Mark set", NULL); 800 } 801 802 /* 803 * YA() puts the text between the marked position and the current 804 * in the buffer. 805 */ 806 void YA(void) 807 { 808 set_up(NO_DELETE); 809 } 810 811 /* 812 * DT() is essentially the same as YA(), but in DT() the text is deleted. 813 */ 814 void DT(void) 815 { 816 set_up(DELETE); 817 } 818 819 /* 820 * Set_up is an interface to the actual yank. It calls checkmark () to check 821 * if the marked position is still valid. If it is, yank is called with the 822 * arguments in the right order. 823 */ 824 void set_up(FLAG remove) 825 { 826 switch (checkmark()) { 827 case NOT_VALID : 828 error("Mark not set.", NULL); 829 return; 830 case SMALLER : 831 yank(mark_line, mark_text, cur_line, cur_text, remove); 832 break; 833 case BIGGER : 834 yank(cur_line, cur_text, mark_line, mark_text, remove); 835 break; 836 case SAME : /* Ignore stupid behaviour */ 837 yank_status = EMPTY; 838 chars_saved = 0L; 839 status_line("0 characters saved in buffer.", NULL); 840 break; 841 } 842 } 843 844 /* 845 * Check_mark() checks if mark_line and mark_text are still valid pointers. If 846 * they are it returns SMALLER if the marked position is before the current, 847 * BIGGER if it isn't or SAME if somebody didn't get the point. 848 * NOT_VALID is returned when mark_line and/or mark_text are no longer valid. 849 * Legal() checks if mark_text is valid on the mark_line. 850 */ 851 FLAG checkmark(void) 852 { 853 register LINE *line; 854 FLAG cur_seen = FALSE; 855 856 /* Special case: check is mark_line and cur_line are the same. */ 857 if (mark_line == cur_line) { 858 if (mark_text == cur_text) /* Even same place */ 859 return SAME; 860 if (legal() == ERRORS) /* mark_text out of range */ 861 return NOT_VALID; 862 return (mark_text < cur_text) ? SMALLER : BIGGER; 863 } 864 865 /* Start looking for mark_line in the line structure */ 866 for (line = header->next; line != tail; line = line->next) { 867 if (line == cur_line) 868 cur_seen = TRUE; 869 else if (line == mark_line) 870 break; 871 } 872 873 /* If we found mark_line (line != tail) check for legality of mark_text */ 874 if (line == tail || legal() == ERRORS) 875 return NOT_VALID; 876 877 /* cur_seen is TRUE if cur_line is before mark_line */ 878 return (cur_seen == TRUE) ? BIGGER : SMALLER; 879 } 880 881 /* 882 * Legal() checks if mark_text is still a valid pointer. 883 */ 884 int legal(void) 885 { 886 register char *textp = mark_line->text; 887 888 /* Locate mark_text on mark_line */ 889 while (textp != mark_text && *textp++ != '\0') 890 ; 891 return (*textp == '\0') ? ERRORS : FINE; 892 } 893 894 /* 895 * Yank puts all the text between start_position and end_position into 896 * the buffer. 897 * The caller must check that the arguments to yank() are valid. (E.g. in 898 * the right order) 899 */ 900 void yank(LINE *start_line, char *start_textp, LINE *end_line, char *end_textp, 901 FLAG remove) 902 { 903 register LINE *line = start_line; 904 register char *textp = start_textp; 905 int fd; 906 907 /* Creat file to hold buffer */ 908 if ((fd = scratch_file(WRITE)) == ERRORS) 909 return; 910 911 chars_saved = 0L; 912 lines_saved = 0; 913 status_line("Saving text.", NULL); 914 915 /* Keep writing chars until the end_location is reached. */ 916 while (textp != end_textp) { 917 if (write_char(fd, *textp) == ERRORS) { 918 (void) close(fd); 919 return; 920 } 921 if (*textp++ == '\n') { /* Move to the next line */ 922 line = line->next; 923 textp = line->text; 924 lines_saved++; 925 } 926 chars_saved++; 927 } 928 929 /* Flush the I/O buffer and close file */ 930 if (flush_buffer(fd) == ERRORS) { 931 (void) close(fd); 932 return; 933 } 934 (void) close(fd); 935 yank_status = VALID; 936 937 /* 938 * Check if the text should be deleted as well. If it should, the following 939 * hack is used to save a lot of code. First move back to the start_position. 940 * (This might be the location we're on now!) and them delete the text. 941 * It might be a bit confusing the first time somebody uses it. 942 * Delete() will fix the screen. 943 */ 944 if (remove == DELETE) { 945 move_to(find_x(start_line, start_textp), find_y(start_line)); 946 delete(start_line, start_textp, end_line, end_textp); 947 } 948 949 status_line(num_out(chars_saved), " characters saved in buffer."); 950 } 951 952 /* 953 * Scratch_file() creates a uniq file in /usr/tmp. If the file couldn't 954 * be created other combinations of files are tried until a maximum 955 * of MAXTRAILS times. After MAXTRAILS times, an error message is given 956 * and ERRORS is returned. 957 */ 958 959 #define MAXTRAILS 26 960 961 int scratch_file(FLAG mode) 962 { 963 static int trials = 0; /* Keep track of trails */ 964 register char *y_ptr, *n_ptr; 965 int fd; /* Filedescriptor to buffer */ 966 967 /* If yank_status == NOT_VALID, scratch_file is called for the first time */ 968 if (yank_status == NOT_VALID && mode == WRITE) { /* Create new file */ 969 /* Generate file name. */ 970 y_ptr = &yank_file[11]; 971 n_ptr = num_out((long) getpid()); 972 while ((*y_ptr = *n_ptr++) != '\0') 973 y_ptr++; 974 *y_ptr++ = 'a' + trials; 975 *y_ptr = '\0'; 976 /* Check file existence */ 977 if (access(yank_file, 0) == 0 || (fd = creat(yank_file, 0644)) < 0) { 978 if (trials++ >= MAXTRAILS) { 979 error("Unable to creat scratchfile.", NULL); 980 return ERRORS; 981 } 982 else 983 return scratch_file(mode);/* Have another go */ 984 } 985 } 986 else if ((mode == READ && (fd = open(yank_file, 0)) < 0) || 987 (mode == WRITE && (fd = creat(yank_file, 0644)) < 0)) { 988 yank_status = NOT_VALID; 989 return ERRORS; 990 } 991 992 clear_buffer(); 993 return fd; 994 } 995 996 /* ======================================================================== * 997 * Search Routines * 998 * ======================================================================== */ 999 1000 /* 1001 * A regular expression consists of a sequence of: 1002 * 1. A normal character matching that character. 1003 * 2. A . matching any character. 1004 * 3. A ^ matching the begin of a line. 1005 * 4. A $ (as last character of the pattern) mathing the end of a line. 1006 * 5. A \<character> matching <character>. 1007 * 6. A number of characters enclosed in [] pairs matching any of these 1008 * characters. A list of characters can be indicated by a '-'. So 1009 * [a-z] matches any letter of the alphabet. If the first character 1010 * after the '[' is a '^' then the set is negated (matching none of 1011 * the characters). 1012 * A ']', '^' or '-' can be escaped by putting a '\' in front of it. 1013 * 7. If one of the expressions as described in 1-6 is followed by a 1014 * '*' than that expressions matches a sequence of 0 or more of 1015 * that expression. 1016 */ 1017 1018 char typed_expression[LINE_LEN]; /* Holds previous expr. */ 1019 1020 /* 1021 * SF searches forward for an expression. 1022 */ 1023 void SF(void) 1024 { 1025 search("Search forward:", FORWARD); 1026 } 1027 1028 /* 1029 * SF searches backwards for an expression. 1030 */ 1031 void SR(void) 1032 { 1033 search("Search reverse:", REVERSE); 1034 } 1035 1036 /* 1037 * Get_expression() prompts for an expression. If just a return is typed, the 1038 * old expression is used. If the expression changed, compile() is called and 1039 * the returning REGEX structure is returned. It returns NULL upon error. 1040 * The save flag indicates whether the expression should be appended at the 1041 * message pointer. 1042 */ 1043 REGEX *get_expression(char *message) 1044 { 1045 static REGEX program; /* Program of expression */ 1046 char exp_buf[LINE_LEN]; /* Buffer for new expr. */ 1047 1048 if (get_string(message, exp_buf, FALSE) == ERRORS) 1049 return NULL; 1050 1051 if (exp_buf[0] == '\0' && typed_expression[0] == '\0') { 1052 error("No previous expression.", NULL); 1053 return NULL; 1054 } 1055 1056 if (exp_buf[0] != '\0') { /* A new expr. is typed */ 1057 copy_string(typed_expression, exp_buf);/* Save expr. */ 1058 compile(exp_buf, &program); /* Compile new expression */ 1059 } 1060 1061 if (program.status == REG_ERROR) { /* Error during compiling */ 1062 error(program.result.err_mess, NULL); 1063 return NULL; 1064 } 1065 return &program; 1066 } 1067 1068 /* 1069 * GR() a replaces all matches from the current position until the end 1070 * of the file. 1071 */ 1072 void GR(void) 1073 { 1074 change("Global replace:", VALID); 1075 } 1076 1077 /* 1078 * LR() replaces all matches on the current line. 1079 */ 1080 void LR(void) 1081 { 1082 change("Line replace:", NOT_VALID); 1083 } 1084 1085 /* 1086 * Change() prompts for an expression and a substitution pattern and changes 1087 * all matches of the expression into the substitution. change() start looking 1088 * for expressions at the current line and continues until the end of the file 1089 * if the FLAG file is VALID. 1090 */ 1091 void change(char *message, FLAG file) 1092 { 1093 char mess_buf[LINE_LEN]; /* Buffer to hold message */ 1094 char replacement[LINE_LEN]; /* Buffer to hold subst. pattern */ 1095 REGEX *program; /* Program resulting from compilation */ 1096 register LINE *line = cur_line; 1097 register char *textp; 1098 long lines = 0L; /* Nr of lines on which subs occurred */ 1099 long subs = 0L; /* Nr of subs made */ 1100 int page = y; /* Index to check if line is on screen*/ 1101 1102 /* Save message and get expression */ 1103 copy_string(mess_buf, message); 1104 if ((program = get_expression(mess_buf)) == NULL) 1105 return; 1106 1107 /* Get substitution pattern */ 1108 build_string(mess_buf, "%s %s by:", mess_buf, typed_expression); 1109 if (get_string(mess_buf, replacement, FALSE) == ERRORS) 1110 return; 1111 1112 set_cursor(0, ymax); 1113 flush(); 1114 /* Substitute until end of file */ 1115 do { 1116 if (line_check(program, line->text, FORWARD)) { 1117 lines++; 1118 /* Repeat sub. on this line as long as we find a match*/ 1119 do { 1120 subs++; /* Increment subs */ 1121 if ((textp = substitute(line, program,replacement)) 1122 == NULL) 1123 return; /* Line too long */ 1124 } while ((program->status & BEGIN_LINE) != BEGIN_LINE && 1125 (program->status & END_LINE) != END_LINE && 1126 line_check(program, textp, FORWARD)); 1127 /* Check to see if we can print the result */ 1128 if (page <= screenmax) { 1129 set_cursor(0, page); 1130 line_print(line); 1131 } 1132 } 1133 if (page <= screenmax) 1134 page++; 1135 line = line->next; 1136 } while (line != tail && file == VALID && quit == FALSE); 1137 1138 copy_string(mess_buf, (quit == TRUE) ? "(Aborted) " : ""); 1139 /* Fix the status line */ 1140 if (subs == 0L && quit == FALSE) 1141 error("Pattern not found.", NULL); 1142 else if (lines >= REPORT || quit == TRUE) { 1143 build_string(mess_buf, "%s %D substitutions on %D lines.", mess_buf, 1144 subs, lines); 1145 status_line(mess_buf, NULL); 1146 } 1147 else if (file == NOT_VALID && subs >= REPORT) 1148 status_line(num_out(subs), " substitutions."); 1149 else 1150 clear_status(); 1151 move_to (x, y); 1152 } 1153 1154 /* 1155 * Substitute() replaces the match on this line by the substitute pattern 1156 * as indicated by the program. Every '&' in the replacement is replaced by 1157 * the original match. A \ in the replacement escapes the next character. 1158 */ 1159 char *substitute(LINE *line, REGEX *program, char *replacement) 1160 { 1161 register char *textp = text_buffer; 1162 register char *subp = replacement; 1163 char *linep = line->text; 1164 char *amp; 1165 1166 modified = TRUE; 1167 1168 /* Copy part of line until the beginning of the match */ 1169 while (linep != program->start_ptr) 1170 *textp++ = *linep++; 1171 1172 /* 1173 * Replace the match by the substitution pattern. Each occurrence of '&' is 1174 * replaced by the original match. A \ escapes the next character. 1175 */ 1176 while (*subp != '\0' && textp < &text_buffer[MAX_CHARS]) { 1177 if (*subp == '&') { /* Replace the original match */ 1178 amp = program->start_ptr; 1179 while (amp < program->end_ptr && textp<&text_buffer[MAX_CHARS]) 1180 *textp++ = *amp++; 1181 subp++; 1182 } 1183 else { 1184 if (*subp == '\\' && *(subp + 1) != '\0') 1185 subp++; 1186 *textp++ = *subp++; 1187 } 1188 } 1189 1190 /* Check for line length not exceeding MAX_CHARS */ 1191 if (length_of(text_buffer) + length_of(program->end_ptr) >= MAX_CHARS) { 1192 error("Substitution result: line too big", NULL); 1193 return NULL; 1194 } 1195 1196 /* Append last part of line to the new build line */ 1197 copy_string(textp, program->end_ptr); 1198 1199 /* Free old line and install new one */ 1200 free_space(line->text); 1201 line->text = alloc(length_of(text_buffer) + 1); 1202 copy_string(line->text, text_buffer); 1203 1204 return(line->text + (textp - text_buffer)); 1205 } 1206 1207 /* 1208 * Search() calls get_expression to fetch the expression. If this went well, 1209 * the function match() is called which returns the line with the next match. 1210 * If this line is the NULL, it means that a match could not be found. 1211 * Find_x() and find_y() display the right page on the screen, and return 1212 * the right coordinates for x and y. These coordinates are passed to move_to() 1213 */ 1214 void search(char *message, FLAG method) 1215 { 1216 register REGEX *program; 1217 register LINE *match_line; 1218 1219 /* Get the expression */ 1220 if ((program = get_expression(message)) == NULL) 1221 return; 1222 1223 set_cursor(0, ymax); 1224 flush(); 1225 /* Find the match */ 1226 if ((match_line = match(program, cur_text, method)) == NULL) { 1227 if (quit == TRUE) 1228 status_line("Aborted", NULL); 1229 else 1230 status_line("Pattern not found.", NULL); 1231 return; 1232 } 1233 1234 move(0, program->start_ptr, find_y(match_line)); 1235 clear_status(); 1236 } 1237 1238 /* 1239 * find_y() checks if the matched line is on the current page. If it is, it 1240 * returns the new y coordinate, else it displays the correct page with the 1241 * matched line in the middle and returns the new y value; 1242 */ 1243 int find_y(LINE *match_line) 1244 { 1245 register LINE *line; 1246 register int count = 0; 1247 1248 /* Check if match_line is on the same page as currently displayed. */ 1249 for (line = top_line; line != match_line && line != bot_line->next; 1250 line = line->next) 1251 count++; 1252 if (line != bot_line->next) 1253 return count; 1254 1255 /* Display new page, with match_line in center. */ 1256 if ((line = proceed(match_line, -(screenmax >> 1))) == header) { 1257 /* Can't display in the middle. Make first line of file top_line */ 1258 count = 0; 1259 for (line = header->next; line != match_line; line = line->next) 1260 count++; 1261 line = header->next; 1262 } 1263 else /* New page is displayed. Set cursor to middle of page */ 1264 count = screenmax >> 1; 1265 1266 /* Reset pointers and redraw the screen */ 1267 reset(line, 0); 1268 RD(); 1269 1270 return count; 1271 } 1272 1273 /* Opcodes for characters */ 1274 #define NORMAL 0x0200 1275 #define DOT 0x0400 1276 #define EOLN 0x0800 1277 #define STAR 0x1000 1278 #define BRACKET 0x2000 1279 #define NEGATE 0x0100 1280 #define DONE 0x4000 1281 1282 /* Mask for opcodes and characters */ 1283 #define LOW_BYTE 0x00FF 1284 #define HIGH_BYTE 0xFF00 1285 1286 /* Previous is the contents of the previous address (ptr) points to */ 1287 #define previous(ptr) (*((ptr) - 1)) 1288 1289 /* Buffer to store outcome of compilation */ 1290 int exp_buffer[BLOCK_SIZE]; 1291 1292 /* Errors often used */ 1293 char *too_long = "Regular expression too long"; 1294 1295 /* 1296 * Reg_error() is called by compile() is something went wrong. It set the 1297 * status of the structure to error, and assigns the error field of the union. 1298 */ 1299 #define reg_error(str) program->status = REG_ERROR, \ 1300 program->result.err_mess = (str) 1301 /* 1302 * Finished() is called when everything went right during compilation. It 1303 * allocates space for the expression, and copies the expression buffer into 1304 * this field. 1305 */ 1306 void finished(register REGEX *program, int *last_exp) 1307 { 1308 register int length = (last_exp - exp_buffer) * sizeof(int); 1309 1310 /* Allocate space */ 1311 program->result.expression = (int *) alloc(length); 1312 /* Copy expression. (expression consists of ints!) */ 1313 bcopy(exp_buffer, program->result.expression, length); 1314 } 1315 1316 /* 1317 * Compile compiles the pattern into a more comprehensible form and returns a 1318 * REGEX structure. If something went wrong, the status field of the structure 1319 * is set to REG_ERROR and an error message is set into the err_mess field of 1320 * the union. If all went well the expression is saved and the expression 1321 * pointer is set to the saved (and compiled) expression. 1322 */ 1323 void compile(register char *pattern, REGEX *program) 1324 { 1325 register int *expression = exp_buffer; 1326 int *prev_char; /* Pointer to previous compiled atom */ 1327 int *acct_field; /* Pointer to last BRACKET start */ 1328 FLAG negate; /* Negate flag for BRACKET */ 1329 char low_char; /* Index for chars in BRACKET */ 1330 char c; 1331 1332 /* Check for begin of line */ 1333 if (*pattern == '^') { 1334 program->status = BEGIN_LINE; 1335 pattern++; 1336 } 1337 else { 1338 program->status = 0; 1339 /* If the first character is a '*' we have to assign it here. */ 1340 if (*pattern == '*') { 1341 *expression++ = '*' + NORMAL; 1342 pattern++; 1343 } 1344 } 1345 1346 for (; ;) { 1347 switch (c = *pattern++) { 1348 case '.' : 1349 *expression++ = DOT; 1350 break; 1351 case '$' : 1352 /* 1353 * Only means EOLN if it is the last char of the pattern 1354 */ 1355 if (*pattern == '\0') { 1356 *expression++ = EOLN | DONE; 1357 program->status |= END_LINE; 1358 finished(program, expression); 1359 return; 1360 } 1361 else 1362 *expression++ = NORMAL + '$'; 1363 break; 1364 case '\0' : 1365 *expression++ = DONE; 1366 finished(program, expression); 1367 return; 1368 case '\\' : 1369 /* If last char, it must! mean a normal '\' */ 1370 if (*pattern == '\0') 1371 *expression++ = NORMAL + '\\'; 1372 else 1373 *expression++ = NORMAL + *pattern++; 1374 break; 1375 case '*' : 1376 /* 1377 * If the previous expression was a [] find out the 1378 * begin of the list, and adjust the opcode. 1379 */ 1380 prev_char = expression - 1; 1381 if (*prev_char & BRACKET) 1382 *(expression - (*acct_field & LOW_BYTE))|= STAR; 1383 else 1384 *prev_char |= STAR; 1385 break; 1386 case '[' : 1387 /* 1388 * First field in expression gives information about 1389 * the list. 1390 * The opcode consists of BRACKET and if necessary 1391 * NEGATE to indicate that the list should be negated 1392 * and/or STAR to indicate a number of sequence of this 1393 * list. 1394 * The lower byte contains the length of the list. 1395 */ 1396 acct_field = expression++; 1397 if (*pattern == '^') { /* List must be negated */ 1398 pattern++; 1399 negate = TRUE; 1400 } 1401 else 1402 negate = FALSE; 1403 while (*pattern != ']') { 1404 if (*pattern == '\0') { 1405 reg_error("Missing ]"); 1406 return; 1407 } 1408 if (*pattern == '\\') 1409 pattern++; 1410 *expression++ = *pattern++; 1411 if (*pattern == '-') { 1412 /* Make list of chars */ 1413 low_char = previous(pattern); 1414 pattern++; /* Skip '-' */ 1415 if (low_char++ > *pattern) { 1416 reg_error("Bad range in [a-z]"); 1417 return; 1418 } 1419 /* Build list */ 1420 while (low_char <= *pattern) 1421 *expression++ = low_char++; 1422 pattern++; 1423 } 1424 if (expression >= &exp_buffer[BLOCK_SIZE]) { 1425 reg_error(too_long); 1426 return; 1427 } 1428 } 1429 pattern++; /* Skip ']' */ 1430 /* Assign length of list in acct field */ 1431 if ((*acct_field = (expression - acct_field)) == 1) { 1432 reg_error("Empty []"); 1433 return; 1434 } 1435 /* Assign negate and bracket field */ 1436 *acct_field |= BRACKET; 1437 if (negate == TRUE) 1438 *acct_field |= NEGATE; 1439 /* 1440 * Add BRACKET to opcode of last char in field because 1441 * a '*' may be following the list. 1442 */ 1443 previous(expression) |= BRACKET; 1444 break; 1445 default : 1446 *expression++ = c + NORMAL; 1447 } 1448 if (expression == &exp_buffer[BLOCK_SIZE]) { 1449 reg_error(too_long); 1450 return; 1451 } 1452 } 1453 /* NOTREACHED */ 1454 } 1455 1456 /* 1457 * Match gets as argument the program, pointer to place in current line to 1458 * start from and the method to search for (either FORWARD or REVERSE). 1459 * Match() will look through the whole file until a match is found. 1460 * NULL is returned if no match could be found. 1461 */ 1462 LINE *match(REGEX *program, char *string, register FLAG method) 1463 { 1464 register LINE *line = cur_line; 1465 char old_char; /* For saving chars */ 1466 1467 /* Corrupted program */ 1468 if (program->status == REG_ERROR) 1469 return NULL; 1470 1471 /* Check part of text first */ 1472 if (!(program->status & BEGIN_LINE)) { 1473 if (method == FORWARD) { 1474 if (line_check(program, string + 1, method) == MATCH) 1475 return cur_line; /* Match found */ 1476 } 1477 else if (!(program->status & END_LINE)) { 1478 old_char = *string; /* Save char and */ 1479 *string = '\n'; /* Assign '\n' for line_check */ 1480 if (line_check(program, line->text, method) == MATCH) { 1481 *string = old_char; /* Restore char */ 1482 return cur_line; /* Found match */ 1483 } 1484 *string = old_char; /* No match, but restore char */ 1485 } 1486 } 1487 1488 /* No match in last (or first) part of line. Check out rest of file */ 1489 do { 1490 line = (method == FORWARD) ? line->next : line->prev; 1491 if (line->text == NULL) /* Header/tail */ 1492 continue; 1493 if (line_check(program, line->text, method) == MATCH) 1494 return line; 1495 } while (line != cur_line && quit == FALSE); 1496 1497 /* No match found. */ 1498 return NULL; 1499 } 1500 1501 /* 1502 * Line_check() checks the line (or rather string) for a match. Method 1503 * indicates FORWARD or REVERSE search. It scans through the whole string 1504 * until a match is found, or the end of the string is reached. 1505 */ 1506 int line_check(register REGEX *program, char *string, FLAG method) 1507 { 1508 register char *textp = string; 1509 1510 /* Assign start_ptr field. We might find a match right away! */ 1511 program->start_ptr = textp; 1512 1513 /* If the match must be anchored, just check the string. */ 1514 if (program->status & BEGIN_LINE) 1515 return check_string(program, string, NULL); 1516 1517 if (method == REVERSE) { 1518 /* First move to the end of the string */ 1519 for (textp = string; *textp != '\n'; textp++) 1520 ; 1521 /* Start checking string until the begin of the string is met */ 1522 while (textp >= string) { 1523 program->start_ptr = textp; 1524 if (check_string(program, textp--, NULL)) 1525 return MATCH; 1526 } 1527 } 1528 else { 1529 /* Move through the string until the end of is found */ 1530 while (quit == FALSE && *textp != '\0') { 1531 program->start_ptr = textp; 1532 if (check_string(program, textp, NULL)) 1533 return MATCH; 1534 if (*textp == '\n') 1535 break; 1536 textp++; 1537 } 1538 } 1539 1540 return NO_MATCH; 1541 } 1542 1543 /* 1544 * Check() checks of a match can be found in the given string. Whenever a STAR 1545 * is found during matching, then the begin position of the string is marked 1546 * and the maximum number of matches is performed. Then the function star() 1547 * is called which starts to finish the match from this position of the string 1548 * (and expression). Check() return MATCH for a match, NO_MATCH is the string 1549 * couldn't be matched or REG_ERROR for an illegal opcode in expression. 1550 */ 1551 int check_string(REGEX *program, register char *string, int *expression) 1552 { 1553 register int opcode; /* Holds opcode of next expr. atom */ 1554 char c; /* Char that must be matched */ 1555 char *mark; /* For marking position */ 1556 int star_fl; /* A star has been born */ 1557 1558 if (expression == NULL) 1559 expression = program->result.expression; 1560 1561 /* Loop until end of string or end of expression */ 1562 while (quit == FALSE && !(*expression & DONE) && 1563 *string != '\0' && *string != '\n') { 1564 c = *expression & LOW_BYTE; /* Extract match char */ 1565 opcode = *expression & HIGH_BYTE; /* Extract opcode */ 1566 if ((star_fl = (opcode & STAR))) { /* Check star occurrence */ 1567 opcode &= ~STAR; /* Strip opcode */ 1568 mark = string; /* Mark current position */ 1569 } 1570 expression++; /* Increment expr. */ 1571 switch (opcode) { 1572 case NORMAL : 1573 if (star_fl) 1574 while (*string++ == c) /* Skip all matches */ 1575 ; 1576 else if (*string++ != c) 1577 return NO_MATCH; 1578 break; 1579 case DOT : 1580 string++; 1581 if (star_fl) /* Skip to eoln */ 1582 while (*string != '\0' && *string++ != '\n') 1583 ; 1584 break; 1585 case NEGATE | BRACKET: 1586 case BRACKET : 1587 if (star_fl) 1588 while (in_list(expression, *string++, c, opcode) 1589 == MATCH) 1590 ; 1591 else if (in_list(expression, *string++, c, opcode) == NO_MATCH) 1592 return NO_MATCH; 1593 expression += c - 1; /* Add length of list */ 1594 break; 1595 default : 1596 panic("Corrupted program in check_string()"); 1597 } 1598 if (star_fl) 1599 return star(program, mark, string, expression); 1600 } 1601 if (*expression & DONE) { 1602 program->end_ptr = string; /* Match ends here */ 1603 /* 1604 * We might have found a match. The last thing to do is check 1605 * whether a '$' was given at the end of the expression, or 1606 * the match was found on a null string. (E.g. [a-z]* always 1607 * matches) unless a ^ or $ was included in the pattern. 1608 */ 1609 if ((*expression & EOLN) && *string != '\n' && *string != '\0') 1610 return NO_MATCH; 1611 if (string == program->start_ptr && !(program->status & BEGIN_LINE) 1612 && !(*expression & EOLN)) 1613 return NO_MATCH; 1614 return MATCH; 1615 } 1616 return NO_MATCH; 1617 } 1618 1619 /* 1620 * Star() calls check_string() to find out the longest match possible. 1621 * It searches backwards until the (in check_string()) marked position 1622 * is reached, or a match is found. 1623 */ 1624 int star(REGEX *program, register char *end_position, register char *string, 1625 int *expression) 1626 { 1627 do { 1628 string--; 1629 if (check_string(program, string, expression)) 1630 return MATCH; 1631 } while (string != end_position); 1632 1633 return NO_MATCH; 1634 } 1635 1636 /* 1637 * In_list() checks if the given character is in the list of []. If it is 1638 * it returns MATCH. if it isn't it returns NO_MATCH. These returns values 1639 * are reversed when the NEGATE field in the opcode is present. 1640 */ 1641 int in_list(int *list, int c, register int list_length, int opcode) 1642 { 1643 if (c == '\0' || c == '\n') /* End of string, never matches */ 1644 return NO_MATCH; 1645 while (list_length-- > 1) { /* > 1, don't check acct_field */ 1646 if ((*list & LOW_BYTE) == c) 1647 return (opcode & NEGATE) ? NO_MATCH : MATCH; 1648 list++; 1649 } 1650 return (opcode & NEGATE) ? MATCH : NO_MATCH; 1651 } 1652 1653 /* 1654 * Dummy_line() adds an empty line at the end of the file. This is sometimes 1655 * useful in combination with the EF and DN command in combination with the 1656 * Yank command set. 1657 */ 1658 void dummy_line(void) 1659 { 1660 (void) line_insert(tail->prev, "\n", 1); 1661 tail->prev->shift_count = DUMMY; 1662 if (last_y != screenmax) { 1663 last_y++; 1664 bot_line = bot_line->next; 1665 } 1666 } 1667