1/* vi: set sw=4 ts=4: */ 2/* 3 * Copyright (c) 2002 by David I. Bell 4 * Permission is granted to use, distribute, or modify this source, 5 * provided that this copyright notice remains intact. 6 * 7 * The "ed" built-in command (much simplified) 8 */ 9 10#include "libbb.h" 11 12#define searchString bb_common_bufsiz1 13 14enum { 15 USERSIZE = sizeof(searchString) > 1024 ? 1024 16 : sizeof(searchString) - 1, /* max line length typed in by user */ 17 INITBUF_SIZE = 1024, /* initial buffer size */ 18}; 19 20typedef struct LINE { 21 struct LINE *next; 22 struct LINE *prev; 23 int len; 24 char data[1]; 25} LINE; 26 27static LINE lines, *curLine; 28static int curNum, lastNum, marks[26], dirty; 29static char *bufBase, *bufPtr, *fileName; 30static int bufUsed, bufSize; 31 32static void doCommands(void); 33static void subCommand(const char *cmd, int num1, int num2); 34static int getNum(const char **retcp, int *retHaveNum, int *retNum); 35static int setCurNum(int num); 36static int initEdit(void); 37static void termEdit(void); 38static void addLines(int num); 39static int insertLine(int num, const char *data, int len); 40static int deleteLines(int num1, int num2); 41static int printLines(int num1, int num2, int expandFlag); 42static int writeLines(const char *file, int num1, int num2); 43static int readLines(const char *file, int num); 44static int searchLines(const char *str, int num1, int num2); 45static LINE *findLine(int num); 46 47static int findString(const LINE *lp, const char * str, int len, int offset); 48 49int ed_main(int argc, char **argv); 50int ed_main(int argc, char **argv) 51{ 52 if (!initEdit()) 53 return EXIT_FAILURE; 54 55 if (argc > 1) { 56 fileName = strdup(argv[1]); 57 58 if (fileName == NULL) { 59 bb_error_msg("no memory"); 60 termEdit(); 61 return EXIT_SUCCESS; 62 } 63 64 if (!readLines(fileName, 1)) { 65 termEdit(); 66 return EXIT_SUCCESS; 67 } 68 69 if (lastNum) 70 setCurNum(1); 71 72 dirty = FALSE; 73 } 74 75 doCommands(); 76 77 termEdit(); 78 return EXIT_SUCCESS; 79} 80 81/* 82 * Read commands until we are told to stop. 83 */ 84static void doCommands(void) 85{ 86 const char *cp; 87 char *endbuf, *newname, buf[USERSIZE]; 88 int len, num1, num2, have1, have2; 89 90 while (TRUE) { 91 printf(": "); 92 fflush(stdout); 93 94 if (fgets(buf, sizeof(buf), stdin) == NULL) 95 return; 96 97 len = strlen(buf); 98 99 if (len == 0) 100 return; 101 102 endbuf = &buf[len - 1]; 103 104 if (*endbuf != '\n') { 105 bb_error_msg("command line too long"); 106 107 do { 108 len = fgetc(stdin); 109 } while ((len != EOF) && (len != '\n')); 110 111 continue; 112 } 113 114 while ((endbuf > buf) && isblank(endbuf[-1])) 115 endbuf--; 116 117 *endbuf = '\0'; 118 119 cp = buf; 120 121 while (isblank(*cp)) 122 cp++; 123 124 have1 = FALSE; 125 have2 = FALSE; 126 127 if ((curNum == 0) && (lastNum > 0)) { 128 curNum = 1; 129 curLine = lines.next; 130 } 131 132 if (!getNum(&cp, &have1, &num1)) 133 continue; 134 135 while (isblank(*cp)) 136 cp++; 137 138 if (*cp == ',') { 139 cp++; 140 141 if (!getNum(&cp, &have2, &num2)) 142 continue; 143 144 if (!have1) 145 num1 = 1; 146 147 if (!have2) 148 num2 = lastNum; 149 150 have1 = TRUE; 151 have2 = TRUE; 152 } 153 154 if (!have1) 155 num1 = curNum; 156 157 if (!have2) 158 num2 = num1; 159 160 switch (*cp++) { 161 case 'a': 162 addLines(num1 + 1); 163 break; 164 165 case 'c': 166 deleteLines(num1, num2); 167 addLines(num1); 168 break; 169 170 case 'd': 171 deleteLines(num1, num2); 172 break; 173 174 case 'f': 175 if (*cp && !isblank(*cp)) { 176 bb_error_msg("bad file command"); 177 break; 178 } 179 180 while (isblank(*cp)) 181 cp++; 182 183 if (*cp == '\0') { 184 if (fileName) 185 printf("\"%s\"\n", fileName); 186 else 187 printf("No file name\n"); 188 break; 189 } 190 191 newname = strdup(cp); 192 193 if (newname == NULL) { 194 bb_error_msg("no memory for file name"); 195 break; 196 } 197 198 if (fileName) 199 free(fileName); 200 201 fileName = newname; 202 break; 203 204 case 'i': 205 addLines(num1); 206 break; 207 208 case 'k': 209 while (isblank(*cp)) 210 cp++; 211 212 if ((*cp < 'a') || (*cp > 'a') || cp[1]) { 213 bb_error_msg("bad mark name"); 214 break; 215 } 216 217 marks[*cp - 'a'] = num2; 218 break; 219 220 case 'l': 221 printLines(num1, num2, TRUE); 222 break; 223 224 case 'p': 225 printLines(num1, num2, FALSE); 226 break; 227 228 case 'q': 229 while (isblank(*cp)) 230 cp++; 231 232 if (have1 || *cp) { 233 bb_error_msg("bad quit command"); 234 break; 235 } 236 237 if (!dirty) 238 return; 239 240 printf("Really quit? "); 241 fflush(stdout); 242 243 buf[0] = '\0'; 244 fgets(buf, sizeof(buf), stdin); 245 cp = buf; 246 247 while (isblank(*cp)) 248 cp++; 249 250 if ((*cp == 'y') || (*cp == 'Y')) 251 return; 252 253 break; 254 255 case 'r': 256 if (*cp && !isblank(*cp)) { 257 bb_error_msg("bad read command"); 258 break; 259 } 260 261 while (isblank(*cp)) 262 cp++; 263 264 if (*cp == '\0') { 265 bb_error_msg("no file name"); 266 break; 267 } 268 269 if (!have1) 270 num1 = lastNum; 271 272 if (readLines(cp, num1 + 1)) 273 break; 274 275 if (fileName == NULL) 276 fileName = strdup(cp); 277 278 break; 279 280 case 's': 281 subCommand(cp, num1, num2); 282 break; 283 284 case 'w': 285 if (*cp && !isblank(*cp)) { 286 bb_error_msg("bad write command"); 287 break; 288 } 289 290 while (isblank(*cp)) 291 cp++; 292 293 if (!have1) { 294 num1 = 1; 295 num2 = lastNum; 296 } 297 298 if (*cp == '\0') 299 cp = fileName; 300 301 if (cp == NULL) { 302 bb_error_msg("no file name specified"); 303 break; 304 } 305 306 writeLines(cp, num1, num2); 307 break; 308 309 case 'z': 310 switch (*cp) { 311 case '-': 312 printLines(curNum-21, curNum, FALSE); 313 break; 314 case '.': 315 printLines(curNum-11, curNum+10, FALSE); 316 break; 317 default: 318 printLines(curNum, curNum+21, FALSE); 319 break; 320 } 321 break; 322 323 case '.': 324 if (have1) { 325 bb_error_msg("no arguments allowed"); 326 break; 327 } 328 329 printLines(curNum, curNum, FALSE); 330 break; 331 332 case '-': 333 if (setCurNum(curNum - 1)) 334 printLines(curNum, curNum, FALSE); 335 336 break; 337 338 case '=': 339 printf("%d\n", num1); 340 break; 341 342 case '\0': 343 if (have1) { 344 printLines(num2, num2, FALSE); 345 break; 346 } 347 348 if (setCurNum(curNum + 1)) 349 printLines(curNum, curNum, FALSE); 350 351 break; 352 353 default: 354 bb_error_msg("unimplemented command"); 355 break; 356 } 357 } 358} 359 360 361/* 362 * Do the substitute command. 363 * The current line is set to the last substitution done. 364 */ 365static void subCommand(const char * cmd, int num1, int num2) 366{ 367 char *cp, *oldStr, *newStr, buf[USERSIZE]; 368 int delim, oldLen, newLen, deltaLen, offset; 369 LINE *lp, *nlp; 370 int globalFlag, printFlag, didSub, needPrint; 371 372 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 373 bb_error_msg("bad line range for substitute"); 374 return; 375 } 376 377 globalFlag = FALSE; 378 printFlag = FALSE; 379 didSub = FALSE; 380 needPrint = FALSE; 381 382 /* 383 * Copy the command so we can modify it. 384 */ 385 strcpy(buf, cmd); 386 cp = buf; 387 388 if (isblank(*cp) || (*cp == '\0')) { 389 bb_error_msg("bad delimiter for substitute"); 390 return; 391 } 392 393 delim = *cp++; 394 oldStr = cp; 395 396 cp = strchr(cp, delim); 397 398 if (cp == NULL) { 399 bb_error_msg("missing 2nd delimiter for substitute"); 400 return; 401 } 402 403 *cp++ = '\0'; 404 405 newStr = cp; 406 cp = strchr(cp, delim); 407 408 if (cp) 409 *cp++ = '\0'; 410 else 411 cp = (char*)""; 412 413 while (*cp) switch (*cp++) { 414 case 'g': 415 globalFlag = TRUE; 416 break; 417 418 case 'p': 419 printFlag = TRUE; 420 break; 421 422 default: 423 bb_error_msg("unknown option for substitute"); 424 return; 425 } 426 427 if (*oldStr == '\0') { 428 if (searchString[0] == '\0') { 429 bb_error_msg("no previous search string"); 430 return; 431 } 432 433 oldStr = searchString; 434 } 435 436 if (oldStr != searchString) 437 strcpy(searchString, oldStr); 438 439 lp = findLine(num1); 440 441 if (lp == NULL) 442 return; 443 444 oldLen = strlen(oldStr); 445 newLen = strlen(newStr); 446 deltaLen = newLen - oldLen; 447 offset = 0; 448 nlp = NULL; 449 450 while (num1 <= num2) { 451 offset = findString(lp, oldStr, oldLen, offset); 452 453 if (offset < 0) { 454 if (needPrint) { 455 printLines(num1, num1, FALSE); 456 needPrint = FALSE; 457 } 458 459 offset = 0; 460 lp = lp->next; 461 num1++; 462 463 continue; 464 } 465 466 needPrint = printFlag; 467 didSub = TRUE; 468 dirty = TRUE; 469 470 /* 471 * If the replacement string is the same size or shorter 472 * than the old string, then the substitution is easy. 473 */ 474 if (deltaLen <= 0) { 475 memcpy(&lp->data[offset], newStr, newLen); 476 477 if (deltaLen) { 478 memcpy(&lp->data[offset + newLen], 479 &lp->data[offset + oldLen], 480 lp->len - offset - oldLen); 481 482 lp->len += deltaLen; 483 } 484 485 offset += newLen; 486 487 if (globalFlag) 488 continue; 489 490 if (needPrint) { 491 printLines(num1, num1, FALSE); 492 needPrint = FALSE; 493 } 494 495 lp = lp->next; 496 num1++; 497 498 continue; 499 } 500 501 /* 502 * The new string is larger, so allocate a new line 503 * structure and use that. Link it in in place of 504 * the old line structure. 505 */ 506 nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen); 507 508 if (nlp == NULL) { 509 bb_error_msg("cannot get memory for line"); 510 return; 511 } 512 513 nlp->len = lp->len + deltaLen; 514 515 memcpy(nlp->data, lp->data, offset); 516 517 memcpy(&nlp->data[offset], newStr, newLen); 518 519 memcpy(&nlp->data[offset + newLen], 520 &lp->data[offset + oldLen], 521 lp->len - offset - oldLen); 522 523 nlp->next = lp->next; 524 nlp->prev = lp->prev; 525 nlp->prev->next = nlp; 526 nlp->next->prev = nlp; 527 528 if (curLine == lp) 529 curLine = nlp; 530 531 free(lp); 532 lp = nlp; 533 534 offset += newLen; 535 536 if (globalFlag) 537 continue; 538 539 if (needPrint) { 540 printLines(num1, num1, FALSE); 541 needPrint = FALSE; 542 } 543 544 lp = lp->next; 545 num1++; 546 } 547 548 if (!didSub) 549 bb_error_msg("no substitutions found for \"%s\"", oldStr); 550} 551 552 553/* 554 * Search a line for the specified string starting at the specified 555 * offset in the line. Returns the offset of the found string, or -1. 556 */ 557static int findString( const LINE * lp, const char * str, int len, int offset) 558{ 559 int left; 560 const char *cp, *ncp; 561 562 cp = &lp->data[offset]; 563 left = lp->len - offset; 564 565 while (left >= len) { 566 ncp = memchr(cp, *str, left); 567 568 if (ncp == NULL) 569 return -1; 570 571 left -= (ncp - cp); 572 573 if (left < len) 574 return -1; 575 576 cp = ncp; 577 578 if (memcmp(cp, str, len) == 0) 579 return (cp - lp->data); 580 581 cp++; 582 left--; 583 } 584 585 return -1; 586} 587 588 589/* 590 * Add lines which are typed in by the user. 591 * The lines are inserted just before the specified line number. 592 * The lines are terminated by a line containing a single dot (ugly!), 593 * or by an end of file. 594 */ 595static void addLines(int num) 596{ 597 int len; 598 char buf[USERSIZE + 1]; 599 600 while (fgets(buf, sizeof(buf), stdin)) { 601 if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) 602 return; 603 604 len = strlen(buf); 605 606 if (len == 0) 607 return; 608 609 if (buf[len - 1] != '\n') { 610 bb_error_msg("line too long"); 611 do { 612 len = fgetc(stdin); 613 } while ((len != EOF) && (len != '\n')); 614 return; 615 } 616 617 if (!insertLine(num++, buf, len)) 618 return; 619 } 620} 621 622 623/* 624 * Parse a line number argument if it is present. This is a sum 625 * or difference of numbers, '.', '$', 'x, or a search string. 626 * Returns TRUE if successful (whether or not there was a number). 627 * Returns FALSE if there was a parsing error, with a message output. 628 * Whether there was a number is returned indirectly, as is the number. 629 * The character pointer which stopped the scan is also returned. 630 */ 631static int getNum(const char **retcp, int *retHaveNum, int *retNum) 632{ 633 const char *cp; 634 char *endStr, str[USERSIZE]; 635 int haveNum, value, num, sign; 636 637 cp = *retcp; 638 haveNum = FALSE; 639 value = 0; 640 sign = 1; 641 642 while (TRUE) { 643 while (isblank(*cp)) 644 cp++; 645 646 switch (*cp) { 647 case '.': 648 haveNum = TRUE; 649 num = curNum; 650 cp++; 651 break; 652 653 case '$': 654 haveNum = TRUE; 655 num = lastNum; 656 cp++; 657 break; 658 659 case '\'': 660 cp++; 661 662 if ((*cp < 'a') || (*cp > 'z')) { 663 bb_error_msg("bad mark name"); 664 return FALSE; 665 } 666 667 haveNum = TRUE; 668 num = marks[*cp++ - 'a']; 669 break; 670 671 case '/': 672 strcpy(str, ++cp); 673 endStr = strchr(str, '/'); 674 675 if (endStr) { 676 *endStr++ = '\0'; 677 cp += (endStr - str); 678 } 679 else 680 cp = ""; 681 682 num = searchLines(str, curNum, lastNum); 683 684 if (num == 0) 685 return FALSE; 686 687 haveNum = TRUE; 688 break; 689 690 default: 691 if (!isdigit(*cp)) { 692 *retcp = cp; 693 *retHaveNum = haveNum; 694 *retNum = value; 695 return TRUE; 696 } 697 698 num = 0; 699 700 while (isdigit(*cp)) 701 num = num * 10 + *cp++ - '0'; 702 703 haveNum = TRUE; 704 break; 705 } 706 707 value += num * sign; 708 709 while (isblank(*cp)) 710 cp++; 711 712 switch (*cp) { 713 case '-': 714 sign = -1; 715 cp++; 716 break; 717 718 case '+': 719 sign = 1; 720 cp++; 721 break; 722 723 default: 724 *retcp = cp; 725 *retHaveNum = haveNum; 726 *retNum = value; 727 return TRUE; 728 } 729 } 730} 731 732 733/* 734 * Initialize everything for editing. 735 */ 736static int initEdit(void) 737{ 738 int i; 739 740 bufSize = INITBUF_SIZE; 741 bufBase = malloc(bufSize); 742 743 if (bufBase == NULL) { 744 bb_error_msg("no memory for buffer"); 745 return FALSE; 746 } 747 748 bufPtr = bufBase; 749 bufUsed = 0; 750 751 lines.next = &lines; 752 lines.prev = &lines; 753 754 curLine = NULL; 755 curNum = 0; 756 lastNum = 0; 757 dirty = FALSE; 758 fileName = NULL; 759 searchString[0] = '\0'; 760 761 for (i = 0; i < 26; i++) 762 marks[i] = 0; 763 764 return TRUE; 765} 766 767 768/* 769 * Finish editing. 770 */ 771static void termEdit(void) 772{ 773 if (bufBase) 774 free(bufBase); 775 776 bufBase = NULL; 777 bufPtr = NULL; 778 bufSize = 0; 779 bufUsed = 0; 780 781 if (fileName) 782 free(fileName); 783 784 fileName = NULL; 785 786 searchString[0] = '\0'; 787 788 if (lastNum) 789 deleteLines(1, lastNum); 790 791 lastNum = 0; 792 curNum = 0; 793 curLine = NULL; 794} 795 796 797/* 798 * Read lines from a file at the specified line number. 799 * Returns TRUE if the file was successfully read. 800 */ 801static int readLines(const char * file, int num) 802{ 803 int fd, cc; 804 int len, lineCount, charCount; 805 char *cp; 806 807 if ((num < 1) || (num > lastNum + 1)) { 808 bb_error_msg("bad line for read"); 809 return FALSE; 810 } 811 812 fd = open(file, 0); 813 814 if (fd < 0) { 815 perror(file); 816 return FALSE; 817 } 818 819 bufPtr = bufBase; 820 bufUsed = 0; 821 lineCount = 0; 822 charCount = 0; 823 cc = 0; 824 825 printf("\"%s\", ", file); 826 fflush(stdout); 827 828 do { 829 cp = memchr(bufPtr, '\n', bufUsed); 830 831 if (cp) { 832 len = (cp - bufPtr) + 1; 833 834 if (!insertLine(num, bufPtr, len)) { 835 close(fd); 836 return FALSE; 837 } 838 839 bufPtr += len; 840 bufUsed -= len; 841 charCount += len; 842 lineCount++; 843 num++; 844 845 continue; 846 } 847 848 if (bufPtr != bufBase) { 849 memcpy(bufBase, bufPtr, bufUsed); 850 bufPtr = bufBase + bufUsed; 851 } 852 853 if (bufUsed >= bufSize) { 854 len = (bufSize * 3) / 2; 855 cp = realloc(bufBase, len); 856 857 if (cp == NULL) { 858 bb_error_msg("no memory for buffer"); 859 close(fd); 860 return FALSE; 861 } 862 863 bufBase = cp; 864 bufPtr = bufBase + bufUsed; 865 bufSize = len; 866 } 867 868 cc = read(fd, bufPtr, bufSize - bufUsed); 869 bufUsed += cc; 870 bufPtr = bufBase; 871 872 } while (cc > 0); 873 874 if (cc < 0) { 875 perror(file); 876 close(fd); 877 return FALSE; 878 } 879 880 if (bufUsed) { 881 if (!insertLine(num, bufPtr, bufUsed)) { 882 close(fd); 883 return -1; 884 } 885 886 lineCount++; 887 charCount += bufUsed; 888 } 889 890 close(fd); 891 892 printf("%d lines%s, %d chars\n", lineCount, 893 (bufUsed ? " (incomplete)" : ""), charCount); 894 895 return TRUE; 896} 897 898 899/* 900 * Write the specified lines out to the specified file. 901 * Returns TRUE if successful, or FALSE on an error with a message output. 902 */ 903static int writeLines(const char * file, int num1, int num2) 904{ 905 LINE *lp; 906 int fd, lineCount, charCount; 907 908 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 909 bb_error_msg("bad line range for write"); 910 return FALSE; 911 } 912 913 lineCount = 0; 914 charCount = 0; 915 916 fd = creat(file, 0666); 917 918 if (fd < 0) { 919 perror(file); 920 return FALSE; 921 } 922 923 printf("\"%s\", ", file); 924 fflush(stdout); 925 926 lp = findLine(num1); 927 928 if (lp == NULL) { 929 close(fd); 930 return FALSE; 931 } 932 933 while (num1++ <= num2) { 934 if (write(fd, lp->data, lp->len) != lp->len) { 935 perror(file); 936 close(fd); 937 return FALSE; 938 } 939 940 charCount += lp->len; 941 lineCount++; 942 lp = lp->next; 943 } 944 945 if (close(fd) < 0) { 946 perror(file); 947 return FALSE; 948 } 949 950 printf("%d lines, %d chars\n", lineCount, charCount); 951 return TRUE; 952} 953 954 955/* 956 * Print lines in a specified range. 957 * The last line printed becomes the current line. 958 * If expandFlag is TRUE, then the line is printed specially to 959 * show magic characters. 960 */ 961static int printLines(int num1, int num2, int expandFlag) 962{ 963 const LINE *lp; 964 const char *cp; 965 int ch, count; 966 967 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 968 bb_error_msg("bad line range for print"); 969 return FALSE; 970 } 971 972 lp = findLine(num1); 973 974 if (lp == NULL) 975 return FALSE; 976 977 while (num1 <= num2) { 978 if (!expandFlag) { 979 write(1, lp->data, lp->len); 980 setCurNum(num1++); 981 lp = lp->next; 982 983 continue; 984 } 985 986 /* 987 * Show control characters and characters with the 988 * high bit set specially. 989 */ 990 cp = lp->data; 991 count = lp->len; 992 993 if ((count > 0) && (cp[count - 1] == '\n')) 994 count--; 995 996 while (count-- > 0) { 997 ch = *cp++; 998 999 if (ch & 0x80) { 1000 fputs("M-", stdout); 1001 ch &= 0x7f; 1002 } 1003 1004 if (ch < ' ') { 1005 fputc('^', stdout); 1006 ch += '@'; 1007 } 1008 1009 if (ch == 0x7f) { 1010 fputc('^', stdout); 1011 ch = '?'; 1012 } 1013 1014 fputc(ch, stdout); 1015 } 1016 1017 fputs("$\n", stdout); 1018 1019 setCurNum(num1++); 1020 lp = lp->next; 1021 } 1022 1023 return TRUE; 1024} 1025 1026 1027/* 1028 * Insert a new line with the specified text. 1029 * The line is inserted so as to become the specified line, 1030 * thus pushing any existing and further lines down one. 1031 * The inserted line is also set to become the current line. 1032 * Returns TRUE if successful. 1033 */ 1034static int insertLine(int num, const char * data, int len) 1035{ 1036 LINE *newLp, *lp; 1037 1038 if ((num < 1) || (num > lastNum + 1)) { 1039 bb_error_msg("inserting at bad line number"); 1040 return FALSE; 1041 } 1042 1043 newLp = malloc(sizeof(LINE) + len - 1); 1044 1045 if (newLp == NULL) { 1046 bb_error_msg("failed to allocate memory for line"); 1047 return FALSE; 1048 } 1049 1050 memcpy(newLp->data, data, len); 1051 newLp->len = len; 1052 1053 if (num > lastNum) 1054 lp = &lines; 1055 else { 1056 lp = findLine(num); 1057 1058 if (lp == NULL) { 1059 free((char *) newLp); 1060 return FALSE; 1061 } 1062 } 1063 1064 newLp->next = lp; 1065 newLp->prev = lp->prev; 1066 lp->prev->next = newLp; 1067 lp->prev = newLp; 1068 1069 lastNum++; 1070 dirty = TRUE; 1071 return setCurNum(num); 1072} 1073 1074 1075/* 1076 * Delete lines from the given range. 1077 */ 1078static int deleteLines(int num1, int num2) 1079{ 1080 LINE *lp, *nlp, *plp; 1081 int count; 1082 1083 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 1084 bb_error_msg("bad line numbers for delete"); 1085 return FALSE; 1086 } 1087 1088 lp = findLine(num1); 1089 1090 if (lp == NULL) 1091 return FALSE; 1092 1093 if ((curNum >= num1) && (curNum <= num2)) { 1094 if (num2 < lastNum) 1095 setCurNum(num2 + 1); 1096 else if (num1 > 1) 1097 setCurNum(num1 - 1); 1098 else 1099 curNum = 0; 1100 } 1101 1102 count = num2 - num1 + 1; 1103 1104 if (curNum > num2) 1105 curNum -= count; 1106 1107 lastNum -= count; 1108 1109 while (count-- > 0) { 1110 nlp = lp->next; 1111 plp = lp->prev; 1112 plp->next = nlp; 1113 nlp->prev = plp; 1114 lp->next = NULL; 1115 lp->prev = NULL; 1116 lp->len = 0; 1117 free(lp); 1118 lp = nlp; 1119 } 1120 1121 dirty = TRUE; 1122 1123 return TRUE; 1124} 1125 1126 1127/* 1128 * Search for a line which contains the specified string. 1129 * If the string is NULL, then the previously searched for string 1130 * is used. The currently searched for string is saved for future use. 1131 * Returns the line number which matches, or 0 if there was no match 1132 * with an error printed. 1133 */ 1134static int searchLines(const char *str, int num1, int num2) 1135{ 1136 const LINE *lp; 1137 int len; 1138 1139 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { 1140 bb_error_msg("bad line numbers for search"); 1141 return 0; 1142 } 1143 1144 if (*str == '\0') { 1145 if (searchString[0] == '\0') { 1146 bb_error_msg("no previous search string"); 1147 return 0; 1148 } 1149 1150 str = searchString; 1151 } 1152 1153 if (str != searchString) 1154 strcpy(searchString, str); 1155 1156 len = strlen(str); 1157 1158 lp = findLine(num1); 1159 1160 if (lp == NULL) 1161 return 0; 1162 1163 while (num1 <= num2) { 1164 if (findString(lp, str, len, 0) >= 0) 1165 return num1; 1166 1167 num1++; 1168 lp = lp->next; 1169 } 1170 1171 bb_error_msg("cannot find string \"%s\"", str); 1172 return 0; 1173} 1174 1175 1176/* 1177 * Return a pointer to the specified line number. 1178 */ 1179static LINE *findLine(int num) 1180{ 1181 LINE *lp; 1182 int lnum; 1183 1184 if ((num < 1) || (num > lastNum)) { 1185 bb_error_msg("line number %d does not exist", num); 1186 return NULL; 1187 } 1188 1189 if (curNum <= 0) { 1190 curNum = 1; 1191 curLine = lines.next; 1192 } 1193 1194 if (num == curNum) 1195 return curLine; 1196 1197 lp = curLine; 1198 lnum = curNum; 1199 1200 if (num < (curNum / 2)) { 1201 lp = lines.next; 1202 lnum = 1; 1203 } 1204 else if (num > ((curNum + lastNum) / 2)) { 1205 lp = lines.prev; 1206 lnum = lastNum; 1207 } 1208 1209 while (lnum < num) { 1210 lp = lp->next; 1211 lnum++; 1212 } 1213 1214 while (lnum > num) { 1215 lp = lp->prev; 1216 lnum--; 1217 } 1218 return lp; 1219} 1220 1221 1222/* 1223 * Set the current line number. 1224 * Returns TRUE if successful. 1225 */ 1226static int setCurNum(int num) 1227{ 1228 LINE *lp; 1229 1230 lp = findLine(num); 1231 1232 if (lp == NULL) 1233 return FALSE; 1234 1235 curNum = num; 1236 curLine = lp; 1237 return TRUE; 1238} 1239