Index: editors/Makefile.in =================================================================== --- editors/Makefile.in (revision 10144) +++ editors/Makefile.in (working copy) @@ -24,8 +24,9 @@ srcdir=$(top_srcdir)/editors EDITOR-y:= -EDITOR-$(CONFIG_AWK) += awk.o -EDITOR-$(CONFIG_PATCH) += patch.o +EDITOR-$(CONFIG_AWK) += awk.o +EDITOR-$(CONFIG_ED) += ed.o +EDITOR-$(CONFIG_PATCH) += patch.o EDITOR-$(CONFIG_SED) += sed.o EDITOR-$(CONFIG_VI) += vi.o EDITOR_SRC:= $(EDITOR-y) Index: editors/Config.in =================================================================== --- editors/Config.in (revision 10144) +++ editors/Config.in (working copy) @@ -20,6 +20,12 @@ Enable math functions of the Awk programming language. NOTE: This will require libm to be present for linking. +config CONFIG_ED + bool "ed" + default n + help + ed + config CONFIG_PATCH bool "patch" default n Index: include/usage.h =================================================================== --- include/usage.h (revision 10151) +++ include/usage.h (working copy) @@ -556,6 +561,9 @@ "$ echo \"Erik\\nis\\ncool\"\n" \ "Erik\\nis\\ncool\n") +#define ed_trivial_usage "" +#define ed_full_usage "" + #define env_trivial_usage \ "[-iu] [-] [name=value]... [command]" #define env_full_usage \ Index: include/applets.h =================================================================== --- include/applets.h (revision 10151) +++ include/applets.h (working copy) @@ -179,6 +179,9 @@ #ifdef CONFIG_ECHO APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif +#ifdef CONFIG_ED + APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER) +#endif #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS) APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif --- /dev/null 2005-04-24 01:00:01.350003056 -0400 +++ ed.c 2005-04-24 01:38:51.000000000 -0400 @@ -0,0 +1,1425 @@ +/* + * Copyright (c) 2002 by David I. Bell + * Permission is granted to use, distribute, or modify this source, + * provided that this copyright notice remains intact. + * + * The "ed" built-in command (much simplified) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define USERSIZE 1024 /* max line length typed in by user */ +#define INITBUF_SIZE 1024 /* initial buffer size */ + +typedef int BOOL; +typedef int NUM; +typedef int LEN; + +typedef struct LINE LINE; +struct LINE { + LINE *next; + LINE *prev; + LEN len; + char data[1]; +}; + +static LINE lines; +static LINE *curLine; +static NUM curNum; +static NUM lastNum; +static NUM marks[26]; +static BOOL dirty; +static char *fileName; +static char searchString[USERSIZE]; + +static char *bufBase; +static char *bufPtr; +static LEN bufUsed; +static LEN bufSize; + +static void doCommands(void); +static void subCommand(const char * cmd, NUM num1, NUM num2); +static BOOL getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum); +static BOOL setCurNum(NUM num); +static BOOL initEdit(void); +static void termEdit(void); +static void addLines(NUM num); +static BOOL insertLine(NUM num, const char * data, LEN len); +static BOOL deleteLines(NUM num1, NUM num2); +static BOOL printLines(NUM num1, NUM num2, BOOL expandFlag); +static BOOL writeLines(const char * file, NUM num1, NUM num2); +static BOOL readLines(const char * file, NUM num); +static NUM searchLines(const char * str, NUM num1, NUM num2); +static LINE * findLine(NUM num); + +static LEN findString(const LINE * lp, const char * str, LEN len, LEN offset); + +int ed_main(int argc, char **argv) +{ + if (!initEdit()) + return EXIT_FAILURE; + + if (argc > 1) { + fileName = strdup(argv[1]); + + if (fileName == NULL) { + bb_error_msg("No memory"); + termEdit(); + return EXIT_SUCCESS; + } + + if (!readLines(fileName, 1)) { + termEdit(); + return EXIT_SUCCESS; + } + + if (lastNum) + setCurNum(1); + + dirty = FALSE; + } + + doCommands(); + + termEdit(); + return EXIT_SUCCESS; +} + +/* + * Read commands until we are told to stop. + */ +static void doCommands(void) +{ + const char * cp; + char * endbuf; + char * newname; + int len; + NUM num1; + NUM num2; + BOOL have1; + BOOL have2; + char buf[USERSIZE]; + + while (TRUE) + { + printf(": "); + fflush(stdout); + + if (fgets(buf, sizeof(buf), stdin) == NULL) + return; + + len = strlen(buf); + + if (len == 0) + return; + + endbuf = &buf[len - 1]; + + if (*endbuf != '\n') + { + bb_error_msg("Command line too long"); + + do + { + len = fgetc(stdin); + } + while ((len != EOF) && (len != '\n')); + + continue; + } + + while ((endbuf > buf) && isblank(endbuf[-1])) + endbuf--; + + *endbuf = '\0'; + + cp = buf; + + while (isblank(*cp)) + cp++; + + have1 = FALSE; + have2 = FALSE; + + if ((curNum == 0) && (lastNum > 0)) + { + curNum = 1; + curLine = lines.next; + } + + if (!getNum(&cp, &have1, &num1)) + continue; + + while (isblank(*cp)) + cp++; + + if (*cp == ',') + { + cp++; + + if (!getNum(&cp, &have2, &num2)) + continue; + + if (!have1) + num1 = 1; + + if (!have2) + num2 = lastNum; + + have1 = TRUE; + have2 = TRUE; + } + + if (!have1) + num1 = curNum; + + if (!have2) + num2 = num1; + + switch (*cp++) + { + case 'a': + addLines(num1 + 1); + break; + + case 'c': + deleteLines(num1, num2); + addLines(num1); + break; + + case 'd': + deleteLines(num1, num2); + break; + + case 'f': + if (*cp && !isblank(*cp)) + { + bb_error_msg("Bad file command"); + break; + } + + while (isblank(*cp)) + cp++; + + if (*cp == '\0') + { + if (fileName) + printf("\"%s\"\n", fileName); + else + printf("No file name\n"); + + break; + } + + newname = strdup(cp); + + if (newname == NULL) + { + bb_error_msg("No memory for file name"); + break; + } + + if (fileName) + free(fileName); + + fileName = newname; + break; + + case 'i': + addLines(num1); + break; + + case 'k': + while (isblank(*cp)) + cp++; + + if ((*cp < 'a') || (*cp > 'a') || cp[1]) + { + bb_error_msg("Bad mark name"); + break; + } + + marks[*cp - 'a'] = num2; + break; + + case 'l': + printLines(num1, num2, TRUE); + break; + + case 'p': + printLines(num1, num2, FALSE); + break; + + case 'q': + while (isblank(*cp)) + cp++; + + if (have1 || *cp) + { + bb_error_msg("Bad quit command"); + break; + } + + if (!dirty) + return; + + printf("Really quit? "); + fflush(stdout); + + buf[0] = '\0'; + fgets(buf, sizeof(buf), stdin); + cp = buf; + + while (isblank(*cp)) + cp++; + + if ((*cp == 'y') || (*cp == 'Y')) + return; + + break; + + case 'r': + if (*cp && !isblank(*cp)) + { + bb_error_msg("Bad read command"); + break; + } + + while (isblank(*cp)) + cp++; + + if (*cp == '\0') + { + bb_error_msg("No file name"); + break; + } + + if (!have1) + num1 = lastNum; + + if (readLines(cp, num1 + 1)) + break; + + if (fileName == NULL) + fileName = strdup(cp); + + break; + + case 's': + subCommand(cp, num1, num2); + break; + + case 'w': + if (*cp && !isblank(*cp)) + { + bb_error_msg("Bad write command"); + break; + } + + while (isblank(*cp)) + cp++; + + if (!have1) { + num1 = 1; + num2 = lastNum; + } + + if (*cp == '\0') + cp = fileName; + + if (cp == NULL) + { + bb_error_msg("No file name specified"); + break; + } + + writeLines(cp, num1, num2); + break; + + case 'z': + switch (*cp) + { + case '-': + printLines(curNum-21, curNum, FALSE); + break; + case '.': + printLines(curNum-11, curNum+10, FALSE); + break; + default: + printLines(curNum, curNum+21, FALSE); + break; + } + break; + + case '.': + if (have1) + { + bb_error_msg("No arguments allowed"); + break; + } + + printLines(curNum, curNum, FALSE); + break; + + case '-': + if (setCurNum(curNum - 1)) + printLines(curNum, curNum, FALSE); + + break; + + case '=': + printf("%d\n", num1); + break; + + case '\0': + if (have1) + { + printLines(num2, num2, FALSE); + break; + } + + if (setCurNum(curNum + 1)) + printLines(curNum, curNum, FALSE); + + break; + + default: + bb_error_msg("Unimplemented command"); + break; + } + } +} + + +/* + * Do the substitute command. + * The current line is set to the last substitution done. + */ +static void +subCommand(const char * cmd, NUM num1, NUM num2) +{ + int delim; + char * cp; + char * oldStr; + char * newStr; + LEN oldLen; + LEN newLen; + LEN deltaLen; + LEN offset; + LINE * lp; + LINE * nlp; + BOOL globalFlag; + BOOL printFlag; + BOOL didSub; + BOOL needPrint; + char buf[USERSIZE]; + + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) + { + bb_error_msg("Bad line range for substitute"); + + return; + } + + globalFlag = FALSE; + printFlag = FALSE; + didSub = FALSE; + needPrint = FALSE; + + /* + * Copy the command so we can modify it. + */ + strcpy(buf, cmd); + cp = buf; + + if (isblank(*cp) || (*cp == '\0')) + { + bb_error_msg("Bad delimiter for substitute"); + + return; + } + + delim = *cp++; + oldStr = cp; + + cp = strchr(cp, delim); + + if (cp == NULL) + { + bb_error_msg("Missing 2nd delimiter for substitute"); + + return; + } + + *cp++ = '\0'; + + newStr = cp; + cp = strchr(cp, delim); + + if (cp) + *cp++ = '\0'; + else + cp = ""; + + while (*cp) switch (*cp++) + { + case 'g': + globalFlag = TRUE; + break; + + case 'p': + printFlag = TRUE; + break; + + default: + bb_error_msg("Unknown option for substitute"); + + return; + } + + if (*oldStr == '\0') + { + if (searchString[0] == '\0') + { + bb_error_msg("No previous search string"); + + return; + } + + oldStr = searchString; + } + + if (oldStr != searchString) + strcpy(searchString, oldStr); + + lp = findLine(num1); + + if (lp == NULL) + return; + + oldLen = strlen(oldStr); + newLen = strlen(newStr); + deltaLen = newLen - oldLen; + offset = 0; + nlp = NULL; + + while (num1 <= num2) + { + offset = findString(lp, oldStr, oldLen, offset); + + if (offset < 0) + { + if (needPrint) + { + printLines(num1, num1, FALSE); + needPrint = FALSE; + } + + offset = 0; + lp = lp->next; + num1++; + + continue; + } + + needPrint = printFlag; + didSub = TRUE; + dirty = TRUE; + + /* + * If the replacement string is the same size or shorter + * than the old string, then the substitution is easy. + */ + if (deltaLen <= 0) + { + memcpy(&lp->data[offset], newStr, newLen); + + if (deltaLen) + { + memcpy(&lp->data[offset + newLen], + &lp->data[offset + oldLen], + lp->len - offset - oldLen); + + lp->len += deltaLen; + } + + offset += newLen; + + if (globalFlag) + continue; + + if (needPrint) + { + printLines(num1, num1, FALSE); + needPrint = FALSE; + } + + lp = lp->next; + num1++; + + continue; + } + + /* + * The new string is larger, so allocate a new line + * structure and use that. Link it in in place of + * the old line structure. + */ + nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen); + + if (nlp == NULL) + { + bb_error_msg("Cannot get memory for line"); + + return; + } + + nlp->len = lp->len + deltaLen; + + memcpy(nlp->data, lp->data, offset); + + memcpy(&nlp->data[offset], newStr, newLen); + + memcpy(&nlp->data[offset + newLen], + &lp->data[offset + oldLen], + lp->len - offset - oldLen); + + nlp->next = lp->next; + nlp->prev = lp->prev; + nlp->prev->next = nlp; + nlp->next->prev = nlp; + + if (curLine == lp) + curLine = nlp; + + free(lp); + lp = nlp; + + offset += newLen; + + if (globalFlag) + continue; + + if (needPrint) + { + printLines(num1, num1, FALSE); + needPrint = FALSE; + } + + lp = lp->next; + num1++; + } + + if (!didSub) + bb_error_msg("No substitutions found for \"%s\"", oldStr); +} + + +/* + * Search a line for the specified string starting at the specified + * offset in the line. Returns the offset of the found string, or -1. + */ +static LEN +findString( const LINE * lp, const char * str, LEN len, LEN offset) +{ + LEN left; + const char * cp; + const char * ncp; + + cp = &lp->data[offset]; + left = lp->len - offset; + + while (left >= len) + { + ncp = memchr(cp, *str, left); + + if (ncp == NULL) + return -1; + + left -= (ncp - cp); + + if (left < len) + return -1; + + cp = ncp; + + if (memcmp(cp, str, len) == 0) + return (cp - lp->data); + + cp++; + left--; + } + + return -1; +} + + +/* + * Add lines which are typed in by the user. + * The lines are inserted just before the specified line number. + * The lines are terminated by a line containing a single dot (ugly!), + * or by an end of file. + */ +static void +addLines(NUM num) +{ + int len; + char buf[USERSIZE + 1]; + + while (fgets(buf, sizeof(buf), stdin)) + { + if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) + return; + + len = strlen(buf); + + if (len == 0) + return; + + if (buf[len - 1] != '\n') + { + bb_error_msg("Line too long"); + + do + { + len = fgetc(stdin); + } + while ((len != EOF) && (len != '\n')); + + return; + } + + if (!insertLine(num++, buf, len)) + return; + } +} + + +/* + * Parse a line number argument if it is present. This is a sum + * or difference of numbers, '.', '$', 'x, or a search string. + * Returns TRUE if successful (whether or not there was a number). + * Returns FALSE if there was a parsing error, with a message output. + * Whether there was a number is returned indirectly, as is the number. + * The character pointer which stopped the scan is also returned. + */ +static BOOL +getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum) +{ + const char * cp; + char * endStr; + char str[USERSIZE]; + BOOL haveNum; + NUM value; + NUM num; + NUM sign; + + cp = *retcp; + haveNum = FALSE; + value = 0; + sign = 1; + + while (TRUE) + { + while (isblank(*cp)) + cp++; + + switch (*cp) + { + case '.': + haveNum = TRUE; + num = curNum; + cp++; + break; + + case '$': + haveNum = TRUE; + num = lastNum; + cp++; + break; + + case '\'': + cp++; + + if ((*cp < 'a') || (*cp > 'z')) + { + bb_error_msg("Bad mark name"); + + return FALSE; + } + + haveNum = TRUE; + num = marks[*cp++ - 'a']; + break; + + case '/': + strcpy(str, ++cp); + endStr = strchr(str, '/'); + + if (endStr) + { + *endStr++ = '\0'; + cp += (endStr - str); + } + else + cp = ""; + + num = searchLines(str, curNum, lastNum); + + if (num == 0) + return FALSE; + + haveNum = TRUE; + break; + + default: + if (!isdigit(*cp)) + { + *retcp = cp; + *retHaveNum = haveNum; + *retNum = value; + + return TRUE; + } + + num = 0; + + while (isdigit(*cp)) + num = num * 10 + *cp++ - '0'; + + haveNum = TRUE; + break; + } + + value += num * sign; + + while (isblank(*cp)) + cp++; + + switch (*cp) + { + case '-': + sign = -1; + cp++; + break; + + case '+': + sign = 1; + cp++; + break; + + default: + *retcp = cp; + *retHaveNum = haveNum; + *retNum = value; + + return TRUE; + } + } +} + + +/* + * Initialize everything for editing. + */ +static BOOL +initEdit(void) +{ + int i; + + bufSize = INITBUF_SIZE; + bufBase = malloc(bufSize); + + if (bufBase == NULL) + { + bb_error_msg("No memory for buffer"); + + return FALSE; + } + + bufPtr = bufBase; + bufUsed = 0; + + lines.next = &lines; + lines.prev = &lines; + + curLine = NULL; + curNum = 0; + lastNum = 0; + dirty = FALSE; + fileName = NULL; + searchString[0] = '\0'; + + for (i = 0; i < 26; i++) + marks[i] = 0; + + return TRUE; +} + + +/* + * Finish editing. + */ +static void +termEdit(void) +{ + if (bufBase) + free(bufBase); + + bufBase = NULL; + bufPtr = NULL; + bufSize = 0; + bufUsed = 0; + + if (fileName) + free(fileName); + + fileName = NULL; + + searchString[0] = '\0'; + + if (lastNum) + deleteLines(1, lastNum); + + lastNum = 0; + curNum = 0; + curLine = NULL; +} + + +/* + * Read lines from a file at the specified line number. + * Returns TRUE if the file was successfully read. + */ +static BOOL +readLines(const char * file, NUM num) +{ + int fd; + int cc; + LEN len; + LEN lineCount; + LEN charCount; + char * cp; + + if ((num < 1) || (num > lastNum + 1)) + { + bb_error_msg("Bad line for read"); + + return FALSE; + } + + fd = open(file, 0); + + if (fd < 0) + { + perror(file); + + return FALSE; + } + + bufPtr = bufBase; + bufUsed = 0; + lineCount = 0; + charCount = 0; + cc = 0; + + printf("\"%s\", ", file); + fflush(stdout); + + do + { + cp = memchr(bufPtr, '\n', bufUsed); + + if (cp) + { + len = (cp - bufPtr) + 1; + + if (!insertLine(num, bufPtr, len)) + { + close(fd); + + return FALSE; + } + + bufPtr += len; + bufUsed -= len; + charCount += len; + lineCount++; + num++; + + continue; + } + + if (bufPtr != bufBase) + { + memcpy(bufBase, bufPtr, bufUsed); + bufPtr = bufBase + bufUsed; + } + + if (bufUsed >= bufSize) + { + len = (bufSize * 3) / 2; + cp = realloc(bufBase, len); + + if (cp == NULL) + { + bb_error_msg("No memory for buffer"); + close(fd); + + return FALSE; + } + + bufBase = cp; + bufPtr = bufBase + bufUsed; + bufSize = len; + } + + cc = read(fd, bufPtr, bufSize - bufUsed); + bufUsed += cc; + bufPtr = bufBase; + + } + while (cc > 0); + + if (cc < 0) + { + perror(file); + close(fd); + + return FALSE; + } + + if (bufUsed) + { + if (!insertLine(num, bufPtr, bufUsed)) + { + close(fd); + + return -1; + } + + lineCount++; + charCount += bufUsed; + } + + close(fd); + + printf("%d lines%s, %d chars\n", lineCount, + (bufUsed ? " (incomplete)" : ""), charCount); + + return TRUE; +} + + +/* + * Write the specified lines out to the specified file. + * Returns TRUE if successful, or FALSE on an error with a message output. + */ +static BOOL +writeLines(const char * file, NUM num1, NUM num2) +{ + int fd; + LINE * lp; + LEN lineCount; + LEN charCount; + + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) + { + bb_error_msg("Bad line range for write"); + + return FALSE; + } + + lineCount = 0; + charCount = 0; + + fd = creat(file, 0666); + + if (fd < 0) { + perror(file); + + return FALSE; + } + + printf("\"%s\", ", file); + fflush(stdout); + + lp = findLine(num1); + + if (lp == NULL) + { + close(fd); + + return FALSE; + } + + while (num1++ <= num2) + { + if (write(fd, lp->data, lp->len) != lp->len) + { + perror(file); + close(fd); + + return FALSE; + } + + charCount += lp->len; + lineCount++; + lp = lp->next; + } + + if (close(fd) < 0) + { + perror(file); + + return FALSE; + } + + printf("%d lines, %d chars\n", lineCount, charCount); + + return TRUE; +} + + +/* + * Print lines in a specified range. + * The last line printed becomes the current line. + * If expandFlag is TRUE, then the line is printed specially to + * show magic characters. + */ +static BOOL +printLines(NUM num1, NUM num2, BOOL expandFlag) +{ + const LINE * lp; + const unsigned char * cp; + int ch; + LEN count; + + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) + { + bb_error_msg("Bad line range for print"); + + return FALSE; + } + + lp = findLine(num1); + + if (lp == NULL) + return FALSE; + + while (num1 <= num2) + { + if (!expandFlag) + { + write(1, lp->data, lp->len); + setCurNum(num1++); + lp = lp->next; + + continue; + } + + /* + * Show control characters and characters with the + * high bit set specially. + */ + cp = lp->data; + count = lp->len; + + if ((count > 0) && (cp[count - 1] == '\n')) + count--; + + while (count-- > 0) + { + ch = *cp++; + + if (ch & 0x80) + { + fputs("M-", stdout); + ch &= 0x7f; + } + + if (ch < ' ') + { + fputc('^', stdout); + ch += '@'; + } + + if (ch == 0x7f) + { + fputc('^', stdout); + ch = '?'; + } + + fputc(ch, stdout); + } + + fputs("$\n", stdout); + + setCurNum(num1++); + lp = lp->next; + } + + return TRUE; +} + + +/* + * Insert a new line with the specified text. + * The line is inserted so as to become the specified line, + * thus pushing any existing and further lines down one. + * The inserted line is also set to become the current line. + * Returns TRUE if successful. + */ +static BOOL +insertLine(NUM num, const char * data, LEN len) +{ + LINE * newLp; + LINE * lp; + + if ((num < 1) || (num > lastNum + 1)) + { + bb_error_msg("Inserting at bad line number"); + + return FALSE; + } + + newLp = (LINE *) malloc(sizeof(LINE) + len - 1); + + if (newLp == NULL) + { + bb_error_msg("Failed to allocate memory for line"); + + return FALSE; + } + + memcpy(newLp->data, data, len); + newLp->len = len; + + if (num > lastNum) + lp = &lines; + else + { + lp = findLine(num); + + if (lp == NULL) + { + free((char *) newLp); + + return FALSE; + } + } + + newLp->next = lp; + newLp->prev = lp->prev; + lp->prev->next = newLp; + lp->prev = newLp; + + lastNum++; + dirty = TRUE; + + return setCurNum(num); +} + + +/* + * Delete lines from the given range. + */ +static BOOL +deleteLines(NUM num1, NUM num2) +{ + LINE * lp; + LINE * nlp; + LINE * plp; + NUM count; + + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) + { + bb_error_msg("Bad line numbers for delete"); + + return FALSE; + } + + lp = findLine(num1); + + if (lp == NULL) + return FALSE; + + if ((curNum >= num1) && (curNum <= num2)) + { + if (num2 < lastNum) + setCurNum(num2 + 1); + else if (num1 > 1) + setCurNum(num1 - 1); + else + curNum = 0; + } + + count = num2 - num1 + 1; + + if (curNum > num2) + curNum -= count; + + lastNum -= count; + + while (count-- > 0) + { + nlp = lp->next; + plp = lp->prev; + plp->next = nlp; + nlp->prev = plp; + lp->next = NULL; + lp->prev = NULL; + lp->len = 0; + free(lp); + lp = nlp; + } + + dirty = TRUE; + + return TRUE; +} + + +/* + * Search for a line which contains the specified string. + * If the string is NULL, then the previously searched for string + * is used. The currently searched for string is saved for future use. + * Returns the line number which matches, or 0 if there was no match + * with an error printed. + */ +static NUM +searchLines(const char * str, NUM num1, NUM num2) +{ + const LINE * lp; + int len; + + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) + { + bb_error_msg("Bad line numbers for search"); + + return 0; + } + + if (*str == '\0') + { + if (searchString[0] == '\0') + { + bb_error_msg("No previous search string"); + + return 0; + } + + str = searchString; + } + + if (str != searchString) + strcpy(searchString, str); + + len = strlen(str); + + lp = findLine(num1); + + if (lp == NULL) + return 0; + + while (num1 <= num2) + { + if (findString(lp, str, len, 0) >= 0) + return num1; + + num1++; + lp = lp->next; + } + + bb_error_msg("Cannot find string \"%s\"", str); + + return 0; +} + + +/* + * Return a pointer to the specified line number. + */ +static LINE * +findLine(NUM num) +{ + LINE * lp; + NUM lnum; + + if ((num < 1) || (num > lastNum)) + { + bb_error_msg("Line number %d does not exist", num); + + return NULL; + } + + if (curNum <= 0) + { + curNum = 1; + curLine = lines.next; + } + + if (num == curNum) + return curLine; + + lp = curLine; + lnum = curNum; + + if (num < (curNum / 2)) + { + lp = lines.next; + lnum = 1; + } + else if (num > ((curNum + lastNum) / 2)) + { + lp = lines.prev; + lnum = lastNum; + } + + while (lnum < num) + { + lp = lp->next; + lnum++; + } + + while (lnum > num) + { + lp = lp->prev; + lnum--; + } + + return lp; +} + + +/* + * Set the current line number. + * Returns TRUE if successful. + */ +static BOOL +setCurNum(NUM num) +{ + LINE * lp; + + lp = findLine(num); + + if (lp == NULL) + return FALSE; + + curNum = num; + curLine = lp; + + return TRUE; +} + +/* END CODE */