It was sitting there, in the patches directory, for years. It was delete it
authorRob Landley <rob@landley.net>
Thu, 4 May 2006 20:56:43 +0000 (20:56 -0000)
committerRob Landley <rob@landley.net>
Thu, 4 May 2006 20:56:43 +0000 (20:56 -0000)
or apply it.  It's small, simple, evil, part of SUSv3, and we can switch it
off.

editors/Config.in
editors/Makefile.in
editors/ed.c [new file with mode: 0644]
include/applets.h
include/usage.h
patches/ed.patch [deleted file]

index a30879c..14c316c 100644 (file)
@@ -20,6 +20,14 @@ config CONFIG_FEATURE_AWK_MATH
          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
+         The original 1970's Unix text editor, from the days of teletypes.
+         Small, simple, evil.  Part of SUSv3.  If you're not already using
+         this, you don't need it.
+
 config CONFIG_PATCH
        bool "patch"
        default n
index 805017d..9a46e32 100644 (file)
@@ -11,8 +11,9 @@ endif
 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
 
diff --git a/editors/ed.c b/editors/ed.c
new file mode 100644 (file)
index 0000000..0414bfc
--- /dev/null
@@ -0,0 +1,1363 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <memory.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <malloc.h>
+#include "busybox.h"
+
+#define        USERSIZE        1024    /* max line length typed in by user */
+#define        INITBUF_SIZE    1024    /* initial buffer size */
+typedef struct LINE {
+       struct LINE *next;
+       struct LINE *prev;
+       int len;
+       char data[1];
+} LINE;
+
+static LINE lines, *curLine;
+static int curNum, lastNum, marks[26], dirty;
+static char *bufBase, *bufPtr, *fileName, searchString[USERSIZE];
+static int bufUsed, bufSize;
+
+static void doCommands(void);
+static void subCommand(const char *cmd, int num1, int num2);
+static int getNum(const char **retcp, int *retHaveNum, int *retNum);
+static int setCurNum(int num);
+static int initEdit(void);
+static void termEdit(void);
+static void addLines(int num);
+static int insertLine(int num, const char *data, int len);
+static int deleteLines(int num1, int num2);
+static int printLines(int num1, int num2, int expandFlag);
+static int writeLines(const char *file, int num1, int num2);
+static int readLines(const char *file, int num);
+static int searchLines(const char *str, int num1, int num2);
+static LINE *findLine(int num);
+
+static int findString(const LINE *lp, const char * str, int len, int 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, *newname, buf[USERSIZE];
+       int len, num1, num2, have1, have2;
+
+       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, int num1, int num2)
+{
+       char *cp, *oldStr, *newStr, buf[USERSIZE];
+       int     delim, oldLen, newLen, deltaLen, offset;
+       LINE *lp, *nlp;
+       int globalFlag, printFlag, didSub, needPrint;
+
+       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 int findString( const LINE * lp, const char * str, int len, int offset)
+{
+       int left;
+       const char *cp, *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(int 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 int getNum(const char **retcp, int *retHaveNum, int *retNum)
+{
+       const char *cp;
+       char *endStr, str[USERSIZE];
+       int haveNum, value, 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 int 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 int readLines(const char * file, int num)
+{
+       int     fd, cc;
+       int len, lineCount, 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 int writeLines(const char * file, int num1, int num2)
+{
+       LINE *lp;
+       int     fd, lineCount, 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 int printLines(int num1, int num2, int expandFlag)
+{
+       const LINE *lp;
+       const char *cp;
+       int ch, 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 int insertLine(int num, const char * data, int len)
+{
+       LINE *newLp, *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 int deleteLines(int num1, int num2)
+{
+       LINE *lp, *nlp, *plp;
+       int 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 int searchLines(const char *str, int num1, int 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(int num)
+{
+       LINE *lp;
+       int 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 int setCurNum(int num)
+{
+       LINE *lp;
+
+       lp = findLine(num);
+
+       if (lp == NULL)
+               return FALSE;
+
+       curNum = num;
+       curLine = lp;
+
+       return TRUE;
+}
index 06bd9e7..b83e8ed 100644 (file)
@@ -95,10 +95,11 @@ USE_DPKG(APPLET(dpkg, dpkg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_DPKG_DEB(APPLET_ODDNAME(dpkg-deb, dpkg_deb_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER, dpkg_deb))
 USE_DU(APPLET(du, du_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_DUMPKMAP(APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER))
-USE_DUMPLEASES(APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+//USE_DUMPLEASES(APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_E2FSCK(APPLET(e2fsck, e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
 USE_E2LABEL(APPLET_NOUSAGE(e2label, tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
 USE_ECHO(APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER))
+USE_ED(APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_FEATURE_GREP_EGREP_ALIAS(APPLET_NOUSAGE(egrep, grep_main, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_EJECT(APPLET(eject, eject_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_ENV(APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
@@ -273,8 +274,8 @@ USE_TRACEROUTE(APPLET(traceroute, traceroute_main, _BB_DIR_USR_BIN, _BB_SUID_MAY
 USE_TRUE(APPLET(true, true_main, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_TTY(APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_TUNE2FS(APPLET(tune2fs, tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
-USE_UDHCPC(APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
-USE_UDHCPD(APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
+//USE_UDHCPC(APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
+//USE_UDHCPD(APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
 USE_UMOUNT(APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_UNAME(APPLET(uname, uname_main, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_UNCOMPRESS(APPLET(uncompress, uncompress_main, _BB_DIR_BIN, _BB_SUID_NEVER))
index 65a7874..d09c110 100644 (file)
@@ -626,6 +626,9 @@ USE_FEATURE_DATE_ISOFMT( \
        "Options:\n" \
        "\t-t\tclose tray"
 
+#define ed_trivial_usage ""
+#define ed_full_usage ""
+
 #define env_trivial_usage \
        "[-iu] [-] [name=value]... [command]"
 #define env_full_usage \
diff --git a/patches/ed.patch b/patches/ed.patch
deleted file mode 100644 (file)
index 6d51830..0000000
+++ /dev/null
@@ -1,1489 +0,0 @@
-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 <stdio.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <fcntl.h>
-+#include <string.h>
-+#include <memory.h>
-+#include <time.h>
-+#include <ctype.h>
-+#include <sys/param.h>
-+#include <malloc.h>
-+#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 */