aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlandley <landley@69ca8d6d-28ef-0310-b511-8ec308f3f277>2006-05-04 20:56:43 +0000
committerlandley <landley@69ca8d6d-28ef-0310-b511-8ec308f3f277>2006-05-04 20:56:43 +0000
commitbfdd6f05f0ee77ade8261c777aa734a2185a2e0c (patch)
tree0793ca73b2cbceca33e678571de77178364c5b6b
parent503c1dbd394fd542e809d9fe68ffe3c467c09aaf (diff)
downloadbusybox-w32-bfdd6f05f0ee77ade8261c777aa734a2185a2e0c.tar.gz
busybox-w32-bfdd6f05f0ee77ade8261c777aa734a2185a2e0c.tar.bz2
busybox-w32-bfdd6f05f0ee77ade8261c777aa734a2185a2e0c.zip
It was sitting there, in the patches directory, for years. It was delete it
or apply it. It's small, simple, evil, part of SUSv3, and we can switch it off. git-svn-id: svn://busybox.net/trunk/busybox@14999 69ca8d6d-28ef-0310-b511-8ec308f3f277
-rw-r--r--editors/Config.in8
-rw-r--r--editors/Makefile.in5
-rw-r--r--editors/ed.c1363
-rw-r--r--include/applets.h7
-rw-r--r--include/usage.h3
-rw-r--r--patches/ed.patch1489
6 files changed, 1381 insertions, 1494 deletions
diff --git a/editors/Config.in b/editors/Config.in
index a30879c63..14c316c08 100644
--- a/editors/Config.in
+++ b/editors/Config.in
@@ -20,6 +20,14 @@ config CONFIG_FEATURE_AWK_MATH
20 Enable math functions of the Awk programming language. 20 Enable math functions of the Awk programming language.
21 NOTE: This will require libm to be present for linking. 21 NOTE: This will require libm to be present for linking.
22 22
23config CONFIG_ED
24 bool "ed"
25 default n
26 help
27 The original 1970's Unix text editor, from the days of teletypes.
28 Small, simple, evil. Part of SUSv3. If you're not already using
29 this, you don't need it.
30
23config CONFIG_PATCH 31config CONFIG_PATCH
24 bool "patch" 32 bool "patch"
25 default n 33 default n
diff --git a/editors/Makefile.in b/editors/Makefile.in
index 805017dcc..9a46e32c2 100644
--- a/editors/Makefile.in
+++ b/editors/Makefile.in
@@ -11,8 +11,9 @@ endif
11srcdir=$(top_srcdir)/editors 11srcdir=$(top_srcdir)/editors
12 12
13EDITOR-y:= 13EDITOR-y:=
14EDITOR-$(CONFIG_AWK) += awk.o 14EDITOR-$(CONFIG_AWK) += awk.o
15EDITOR-$(CONFIG_PATCH) += patch.o 15EDITOR-$(CONFIG_ED) += ed.o
16EDITOR-$(CONFIG_PATCH) += patch.o
16EDITOR-$(CONFIG_SED) += sed.o 17EDITOR-$(CONFIG_SED) += sed.o
17EDITOR-$(CONFIG_VI) += vi.o 18EDITOR-$(CONFIG_VI) += vi.o
18 19
diff --git a/editors/ed.c b/editors/ed.c
new file mode 100644
index 000000000..0414bfc9d
--- /dev/null
+++ b/editors/ed.c
@@ -0,0 +1,1363 @@
1/*
2 * Copyright (c) 2002 by David I. Bell
3 * Permission is granted to use, distribute, or modify this source,
4 * provided that this copyright notice remains intact.
5 *
6 * The "ed" built-in command (much simplified)
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <unistd.h>
12#include <fcntl.h>
13#include <string.h>
14#include <memory.h>
15#include <time.h>
16#include <ctype.h>
17#include <sys/param.h>
18#include <malloc.h>
19#include "busybox.h"
20
21#define USERSIZE 1024 /* max line length typed in by user */
22#define INITBUF_SIZE 1024 /* initial buffer size */
23typedef struct LINE {
24 struct LINE *next;
25 struct LINE *prev;
26 int len;
27 char data[1];
28} LINE;
29
30static LINE lines, *curLine;
31static int curNum, lastNum, marks[26], dirty;
32static char *bufBase, *bufPtr, *fileName, searchString[USERSIZE];
33static int bufUsed, bufSize;
34
35static void doCommands(void);
36static void subCommand(const char *cmd, int num1, int num2);
37static int getNum(const char **retcp, int *retHaveNum, int *retNum);
38static int setCurNum(int num);
39static int initEdit(void);
40static void termEdit(void);
41static void addLines(int num);
42static int insertLine(int num, const char *data, int len);
43static int deleteLines(int num1, int num2);
44static int printLines(int num1, int num2, int expandFlag);
45static int writeLines(const char *file, int num1, int num2);
46static int readLines(const char *file, int num);
47static int searchLines(const char *str, int num1, int num2);
48static LINE *findLine(int num);
49
50static int findString(const LINE *lp, const char * str, int len, int offset);
51
52int ed_main(int argc, char **argv)
53{
54 if (!initEdit())
55 return EXIT_FAILURE;
56
57 if (argc > 1) {
58 fileName = strdup(argv[1]);
59
60 if (fileName == NULL) {
61 bb_error_msg("No memory");
62 termEdit();
63 return EXIT_SUCCESS;
64 }
65
66 if (!readLines(fileName, 1)) {
67 termEdit();
68 return EXIT_SUCCESS;
69 }
70
71 if (lastNum)
72 setCurNum(1);
73
74 dirty = FALSE;
75 }
76
77 doCommands();
78
79 termEdit();
80 return EXIT_SUCCESS;
81}
82
83/*
84 * Read commands until we are told to stop.
85 */
86static void doCommands(void)
87{
88 const char *cp;
89 char *endbuf, *newname, buf[USERSIZE];
90 int len, num1, num2, have1, have2;
91
92 while (TRUE)
93 {
94 printf(": ");
95 fflush(stdout);
96
97 if (fgets(buf, sizeof(buf), stdin) == NULL)
98 return;
99
100 len = strlen(buf);
101
102 if (len == 0)
103 return;
104
105 endbuf = &buf[len - 1];
106
107 if (*endbuf != '\n')
108 {
109 bb_error_msg("Command line too long");
110
111 do
112 {
113 len = fgetc(stdin);
114 }
115 while ((len != EOF) && (len != '\n'));
116
117 continue;
118 }
119
120 while ((endbuf > buf) && isblank(endbuf[-1]))
121 endbuf--;
122
123 *endbuf = '\0';
124
125 cp = buf;
126
127 while (isblank(*cp))
128 cp++;
129
130 have1 = FALSE;
131 have2 = FALSE;
132
133 if ((curNum == 0) && (lastNum > 0))
134 {
135 curNum = 1;
136 curLine = lines.next;
137 }
138
139 if (!getNum(&cp, &have1, &num1))
140 continue;
141
142 while (isblank(*cp))
143 cp++;
144
145 if (*cp == ',')
146 {
147 cp++;
148
149 if (!getNum(&cp, &have2, &num2))
150 continue;
151
152 if (!have1)
153 num1 = 1;
154
155 if (!have2)
156 num2 = lastNum;
157
158 have1 = TRUE;
159 have2 = TRUE;
160 }
161
162 if (!have1)
163 num1 = curNum;
164
165 if (!have2)
166 num2 = num1;
167
168 switch (*cp++)
169 {
170 case 'a':
171 addLines(num1 + 1);
172 break;
173
174 case 'c':
175 deleteLines(num1, num2);
176 addLines(num1);
177 break;
178
179 case 'd':
180 deleteLines(num1, num2);
181 break;
182
183 case 'f':
184 if (*cp && !isblank(*cp))
185 {
186 bb_error_msg("Bad file command");
187 break;
188 }
189
190 while (isblank(*cp))
191 cp++;
192
193 if (*cp == '\0')
194 {
195 if (fileName)
196 printf("\"%s\"\n", fileName);
197 else
198 printf("No file name\n");
199
200 break;
201 }
202
203 newname = strdup(cp);
204
205 if (newname == NULL)
206 {
207 bb_error_msg("No memory for file name");
208 break;
209 }
210
211 if (fileName)
212 free(fileName);
213
214 fileName = newname;
215 break;
216
217 case 'i':
218 addLines(num1);
219 break;
220
221 case 'k':
222 while (isblank(*cp))
223 cp++;
224
225 if ((*cp < 'a') || (*cp > 'a') || cp[1])
226 {
227 bb_error_msg("Bad mark name");
228 break;
229 }
230
231 marks[*cp - 'a'] = num2;
232 break;
233
234 case 'l':
235 printLines(num1, num2, TRUE);
236 break;
237
238 case 'p':
239 printLines(num1, num2, FALSE);
240 break;
241
242 case 'q':
243 while (isblank(*cp))
244 cp++;
245
246 if (have1 || *cp)
247 {
248 bb_error_msg("Bad quit command");
249 break;
250 }
251
252 if (!dirty)
253 return;
254
255 printf("Really quit? ");
256 fflush(stdout);
257
258 buf[0] = '\0';
259 fgets(buf, sizeof(buf), stdin);
260 cp = buf;
261
262 while (isblank(*cp))
263 cp++;
264
265 if ((*cp == 'y') || (*cp == 'Y'))
266 return;
267
268 break;
269
270 case 'r':
271 if (*cp && !isblank(*cp))
272 {
273 bb_error_msg("Bad read command");
274 break;
275 }
276
277 while (isblank(*cp))
278 cp++;
279
280 if (*cp == '\0')
281 {
282 bb_error_msg("No file name");
283 break;
284 }
285
286 if (!have1)
287 num1 = lastNum;
288
289 if (readLines(cp, num1 + 1))
290 break;
291
292 if (fileName == NULL)
293 fileName = strdup(cp);
294
295 break;
296
297 case 's':
298 subCommand(cp, num1, num2);
299 break;
300
301 case 'w':
302 if (*cp && !isblank(*cp))
303 {
304 bb_error_msg("Bad write command");
305 break;
306 }
307
308 while (isblank(*cp))
309 cp++;
310
311 if (!have1) {
312 num1 = 1;
313 num2 = lastNum;
314 }
315
316 if (*cp == '\0')
317 cp = fileName;
318
319 if (cp == NULL)
320 {
321 bb_error_msg("No file name specified");
322 break;
323 }
324
325 writeLines(cp, num1, num2);
326 break;
327
328 case 'z':
329 switch (*cp)
330 {
331 case '-':
332 printLines(curNum-21, curNum, FALSE);
333 break;
334 case '.':
335 printLines(curNum-11, curNum+10, FALSE);
336 break;
337 default:
338 printLines(curNum, curNum+21, FALSE);
339 break;
340 }
341 break;
342
343 case '.':
344 if (have1)
345 {
346 bb_error_msg("No arguments allowed");
347 break;
348 }
349
350 printLines(curNum, curNum, FALSE);
351 break;
352
353 case '-':
354 if (setCurNum(curNum - 1))
355 printLines(curNum, curNum, FALSE);
356
357 break;
358
359 case '=':
360 printf("%d\n", num1);
361 break;
362
363 case '\0':
364 if (have1)
365 {
366 printLines(num2, num2, FALSE);
367 break;
368 }
369
370 if (setCurNum(curNum + 1))
371 printLines(curNum, curNum, FALSE);
372
373 break;
374
375 default:
376 bb_error_msg("Unimplemented command");
377 break;
378 }
379 }
380}
381
382
383/*
384 * Do the substitute command.
385 * The current line is set to the last substitution done.
386 */
387static void subCommand(const char * cmd, int num1, int num2)
388{
389 char *cp, *oldStr, *newStr, buf[USERSIZE];
390 int delim, oldLen, newLen, deltaLen, offset;
391 LINE *lp, *nlp;
392 int globalFlag, printFlag, didSub, needPrint;
393
394 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
395 {
396 bb_error_msg("Bad line range for substitute");
397
398 return;
399 }
400
401 globalFlag = FALSE;
402 printFlag = FALSE;
403 didSub = FALSE;
404 needPrint = FALSE;
405
406 /*
407 * Copy the command so we can modify it.
408 */
409 strcpy(buf, cmd);
410 cp = buf;
411
412 if (isblank(*cp) || (*cp == '\0'))
413 {
414 bb_error_msg("Bad delimiter for substitute");
415
416 return;
417 }
418
419 delim = *cp++;
420 oldStr = cp;
421
422 cp = strchr(cp, delim);
423
424 if (cp == NULL)
425 {
426 bb_error_msg("Missing 2nd delimiter for substitute");
427
428 return;
429 }
430
431 *cp++ = '\0';
432
433 newStr = cp;
434 cp = strchr(cp, delim);
435
436 if (cp)
437 *cp++ = '\0';
438 else
439 cp = "";
440
441 while (*cp) switch (*cp++)
442 {
443 case 'g':
444 globalFlag = TRUE;
445 break;
446
447 case 'p':
448 printFlag = TRUE;
449 break;
450
451 default:
452 bb_error_msg("Unknown option for substitute");
453
454 return;
455 }
456
457 if (*oldStr == '\0')
458 {
459 if (searchString[0] == '\0')
460 {
461 bb_error_msg("No previous search string");
462
463 return;
464 }
465
466 oldStr = searchString;
467 }
468
469 if (oldStr != searchString)
470 strcpy(searchString, oldStr);
471
472 lp = findLine(num1);
473
474 if (lp == NULL)
475 return;
476
477 oldLen = strlen(oldStr);
478 newLen = strlen(newStr);
479 deltaLen = newLen - oldLen;
480 offset = 0;
481 nlp = NULL;
482
483 while (num1 <= num2)
484 {
485 offset = findString(lp, oldStr, oldLen, offset);
486
487 if (offset < 0)
488 {
489 if (needPrint)
490 {
491 printLines(num1, num1, FALSE);
492 needPrint = FALSE;
493 }
494
495 offset = 0;
496 lp = lp->next;
497 num1++;
498
499 continue;
500 }
501
502 needPrint = printFlag;
503 didSub = TRUE;
504 dirty = TRUE;
505
506 /*
507 * If the replacement string is the same size or shorter
508 * than the old string, then the substitution is easy.
509 */
510 if (deltaLen <= 0)
511 {
512 memcpy(&lp->data[offset], newStr, newLen);
513
514 if (deltaLen)
515 {
516 memcpy(&lp->data[offset + newLen],
517 &lp->data[offset + oldLen],
518 lp->len - offset - oldLen);
519
520 lp->len += deltaLen;
521 }
522
523 offset += newLen;
524
525 if (globalFlag)
526 continue;
527
528 if (needPrint)
529 {
530 printLines(num1, num1, FALSE);
531 needPrint = FALSE;
532 }
533
534 lp = lp->next;
535 num1++;
536
537 continue;
538 }
539
540 /*
541 * The new string is larger, so allocate a new line
542 * structure and use that. Link it in in place of
543 * the old line structure.
544 */
545 nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
546
547 if (nlp == NULL)
548 {
549 bb_error_msg("Cannot get memory for line");
550
551 return;
552 }
553
554 nlp->len = lp->len + deltaLen;
555
556 memcpy(nlp->data, lp->data, offset);
557
558 memcpy(&nlp->data[offset], newStr, newLen);
559
560 memcpy(&nlp->data[offset + newLen],
561 &lp->data[offset + oldLen],
562 lp->len - offset - oldLen);
563
564 nlp->next = lp->next;
565 nlp->prev = lp->prev;
566 nlp->prev->next = nlp;
567 nlp->next->prev = nlp;
568
569 if (curLine == lp)
570 curLine = nlp;
571
572 free(lp);
573 lp = nlp;
574
575 offset += newLen;
576
577 if (globalFlag)
578 continue;
579
580 if (needPrint)
581 {
582 printLines(num1, num1, FALSE);
583 needPrint = FALSE;
584 }
585
586 lp = lp->next;
587 num1++;
588 }
589
590 if (!didSub)
591 bb_error_msg("No substitutions found for \"%s\"", oldStr);
592}
593
594
595/*
596 * Search a line for the specified string starting at the specified
597 * offset in the line. Returns the offset of the found string, or -1.
598 */
599static int findString( const LINE * lp, const char * str, int len, int offset)
600{
601 int left;
602 const char *cp, *ncp;
603
604 cp = &lp->data[offset];
605 left = lp->len - offset;
606
607 while (left >= len)
608 {
609 ncp = memchr(cp, *str, left);
610
611 if (ncp == NULL)
612 return -1;
613
614 left -= (ncp - cp);
615
616 if (left < len)
617 return -1;
618
619 cp = ncp;
620
621 if (memcmp(cp, str, len) == 0)
622 return (cp - lp->data);
623
624 cp++;
625 left--;
626 }
627
628 return -1;
629}
630
631
632/*
633 * Add lines which are typed in by the user.
634 * The lines are inserted just before the specified line number.
635 * The lines are terminated by a line containing a single dot (ugly!),
636 * or by an end of file.
637 */
638static void addLines(int num)
639{
640 int len;
641 char buf[USERSIZE + 1];
642
643 while (fgets(buf, sizeof(buf), stdin))
644 {
645 if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
646 return;
647
648 len = strlen(buf);
649
650 if (len == 0)
651 return;
652
653 if (buf[len - 1] != '\n')
654 {
655 bb_error_msg("Line too long");
656
657 do
658 {
659 len = fgetc(stdin);
660 }
661 while ((len != EOF) && (len != '\n'));
662
663 return;
664 }
665
666 if (!insertLine(num++, buf, len))
667 return;
668 }
669}
670
671
672/*
673 * Parse a line number argument if it is present. This is a sum
674 * or difference of numbers, '.', '$', 'x, or a search string.
675 * Returns TRUE if successful (whether or not there was a number).
676 * Returns FALSE if there was a parsing error, with a message output.
677 * Whether there was a number is returned indirectly, as is the number.
678 * The character pointer which stopped the scan is also returned.
679 */
680static int getNum(const char **retcp, int *retHaveNum, int *retNum)
681{
682 const char *cp;
683 char *endStr, str[USERSIZE];
684 int haveNum, value, num, sign;
685
686 cp = *retcp;
687 haveNum = FALSE;
688 value = 0;
689 sign = 1;
690
691 while (TRUE)
692 {
693 while (isblank(*cp))
694 cp++;
695
696 switch (*cp)
697 {
698 case '.':
699 haveNum = TRUE;
700 num = curNum;
701 cp++;
702 break;
703
704 case '$':
705 haveNum = TRUE;
706 num = lastNum;
707 cp++;
708 break;
709
710 case '\'':
711 cp++;
712
713 if ((*cp < 'a') || (*cp > 'z'))
714 {
715 bb_error_msg("Bad mark name");
716
717 return FALSE;
718 }
719
720 haveNum = TRUE;
721 num = marks[*cp++ - 'a'];
722 break;
723
724 case '/':
725 strcpy(str, ++cp);
726 endStr = strchr(str, '/');
727
728 if (endStr)
729 {
730 *endStr++ = '\0';
731 cp += (endStr - str);
732 }
733 else
734 cp = "";
735
736 num = searchLines(str, curNum, lastNum);
737
738 if (num == 0)
739 return FALSE;
740
741 haveNum = TRUE;
742 break;
743
744 default:
745 if (!isdigit(*cp))
746 {
747 *retcp = cp;
748 *retHaveNum = haveNum;
749 *retNum = value;
750
751 return TRUE;
752 }
753
754 num = 0;
755
756 while (isdigit(*cp))
757 num = num * 10 + *cp++ - '0';
758
759 haveNum = TRUE;
760 break;
761 }
762
763 value += num * sign;
764
765 while (isblank(*cp))
766 cp++;
767
768 switch (*cp)
769 {
770 case '-':
771 sign = -1;
772 cp++;
773 break;
774
775 case '+':
776 sign = 1;
777 cp++;
778 break;
779
780 default:
781 *retcp = cp;
782 *retHaveNum = haveNum;
783 *retNum = value;
784
785 return TRUE;
786 }
787 }
788}
789
790
791/*
792 * Initialize everything for editing.
793 */
794static int initEdit(void)
795{
796 int i;
797
798 bufSize = INITBUF_SIZE;
799 bufBase = malloc(bufSize);
800
801 if (bufBase == NULL)
802 {
803 bb_error_msg("No memory for buffer");
804
805 return FALSE;
806 }
807
808 bufPtr = bufBase;
809 bufUsed = 0;
810
811 lines.next = &lines;
812 lines.prev = &lines;
813
814 curLine = NULL;
815 curNum = 0;
816 lastNum = 0;
817 dirty = FALSE;
818 fileName = NULL;
819 searchString[0] = '\0';
820
821 for (i = 0; i < 26; i++)
822 marks[i] = 0;
823
824 return TRUE;
825}
826
827
828/*
829 * Finish editing.
830 */
831static void termEdit(void)
832{
833 if (bufBase)
834 free(bufBase);
835
836 bufBase = NULL;
837 bufPtr = NULL;
838 bufSize = 0;
839 bufUsed = 0;
840
841 if (fileName)
842 free(fileName);
843
844 fileName = NULL;
845
846 searchString[0] = '\0';
847
848 if (lastNum)
849 deleteLines(1, lastNum);
850
851 lastNum = 0;
852 curNum = 0;
853 curLine = NULL;
854}
855
856
857/*
858 * Read lines from a file at the specified line number.
859 * Returns TRUE if the file was successfully read.
860 */
861static int readLines(const char * file, int num)
862{
863 int fd, cc;
864 int len, lineCount, charCount;
865 char *cp;
866
867 if ((num < 1) || (num > lastNum + 1))
868 {
869 bb_error_msg("Bad line for read");
870
871 return FALSE;
872 }
873
874 fd = open(file, 0);
875
876 if (fd < 0)
877 {
878 perror(file);
879
880 return FALSE;
881 }
882
883 bufPtr = bufBase;
884 bufUsed = 0;
885 lineCount = 0;
886 charCount = 0;
887 cc = 0;
888
889 printf("\"%s\", ", file);
890 fflush(stdout);
891
892 do
893 {
894 cp = memchr(bufPtr, '\n', bufUsed);
895
896 if (cp)
897 {
898 len = (cp - bufPtr) + 1;
899
900 if (!insertLine(num, bufPtr, len))
901 {
902 close(fd);
903
904 return FALSE;
905 }
906
907 bufPtr += len;
908 bufUsed -= len;
909 charCount += len;
910 lineCount++;
911 num++;
912
913 continue;
914 }
915
916 if (bufPtr != bufBase)
917 {
918 memcpy(bufBase, bufPtr, bufUsed);
919 bufPtr = bufBase + bufUsed;
920 }
921
922 if (bufUsed >= bufSize)
923 {
924 len = (bufSize * 3) / 2;
925 cp = realloc(bufBase, len);
926
927 if (cp == NULL)
928 {
929 bb_error_msg("No memory for buffer");
930 close(fd);
931
932 return FALSE;
933 }
934
935 bufBase = cp;
936 bufPtr = bufBase + bufUsed;
937 bufSize = len;
938 }
939
940 cc = read(fd, bufPtr, bufSize - bufUsed);
941 bufUsed += cc;
942 bufPtr = bufBase;
943
944 }
945 while (cc > 0);
946
947 if (cc < 0)
948 {
949 perror(file);
950 close(fd);
951
952 return FALSE;
953 }
954
955 if (bufUsed)
956 {
957 if (!insertLine(num, bufPtr, bufUsed))
958 {
959 close(fd);
960
961 return -1;
962 }
963
964 lineCount++;
965 charCount += bufUsed;
966 }
967
968 close(fd);
969
970 printf("%d lines%s, %d chars\n", lineCount,
971 (bufUsed ? " (incomplete)" : ""), charCount);
972
973 return TRUE;
974}
975
976
977/*
978 * Write the specified lines out to the specified file.
979 * Returns TRUE if successful, or FALSE on an error with a message output.
980 */
981static int writeLines(const char * file, int num1, int num2)
982{
983 LINE *lp;
984 int fd, lineCount, charCount;
985
986 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
987 {
988 bb_error_msg("Bad line range for write");
989
990 return FALSE;
991 }
992
993 lineCount = 0;
994 charCount = 0;
995
996 fd = creat(file, 0666);
997
998 if (fd < 0) {
999 perror(file);
1000
1001 return FALSE;
1002 }
1003
1004 printf("\"%s\", ", file);
1005 fflush(stdout);
1006
1007 lp = findLine(num1);
1008
1009 if (lp == NULL)
1010 {
1011 close(fd);
1012
1013 return FALSE;
1014 }
1015
1016 while (num1++ <= num2)
1017 {
1018 if (write(fd, lp->data, lp->len) != lp->len)
1019 {
1020 perror(file);
1021 close(fd);
1022
1023 return FALSE;
1024 }
1025
1026 charCount += lp->len;
1027 lineCount++;
1028 lp = lp->next;
1029 }
1030
1031 if (close(fd) < 0)
1032 {
1033 perror(file);
1034
1035 return FALSE;
1036 }
1037
1038 printf("%d lines, %d chars\n", lineCount, charCount);
1039
1040 return TRUE;
1041}
1042
1043
1044/*
1045 * Print lines in a specified range.
1046 * The last line printed becomes the current line.
1047 * If expandFlag is TRUE, then the line is printed specially to
1048 * show magic characters.
1049 */
1050static int printLines(int num1, int num2, int expandFlag)
1051{
1052 const LINE *lp;
1053 const char *cp;
1054 int ch, count;
1055
1056 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
1057 {
1058 bb_error_msg("Bad line range for print");
1059
1060 return FALSE;
1061 }
1062
1063 lp = findLine(num1);
1064
1065 if (lp == NULL)
1066 return FALSE;
1067
1068 while (num1 <= num2)
1069 {
1070 if (!expandFlag)
1071 {
1072 write(1, lp->data, lp->len);
1073 setCurNum(num1++);
1074 lp = lp->next;
1075
1076 continue;
1077 }
1078
1079 /*
1080 * Show control characters and characters with the
1081 * high bit set specially.
1082 */
1083 cp = lp->data;
1084 count = lp->len;
1085
1086 if ((count > 0) && (cp[count - 1] == '\n'))
1087 count--;
1088
1089 while (count-- > 0)
1090 {
1091 ch = *cp++;
1092
1093 if (ch & 0x80)
1094 {
1095 fputs("M-", stdout);
1096 ch &= 0x7f;
1097 }
1098
1099 if (ch < ' ')
1100 {
1101 fputc('^', stdout);
1102 ch += '@';
1103 }
1104
1105 if (ch == 0x7f)
1106 {
1107 fputc('^', stdout);
1108 ch = '?';
1109 }
1110
1111 fputc(ch, stdout);
1112 }
1113
1114 fputs("$\n", stdout);
1115
1116 setCurNum(num1++);
1117 lp = lp->next;
1118 }
1119
1120 return TRUE;
1121}
1122
1123
1124/*
1125 * Insert a new line with the specified text.
1126 * The line is inserted so as to become the specified line,
1127 * thus pushing any existing and further lines down one.
1128 * The inserted line is also set to become the current line.
1129 * Returns TRUE if successful.
1130 */
1131static int insertLine(int num, const char * data, int len)
1132{
1133 LINE *newLp, *lp;
1134
1135 if ((num < 1) || (num > lastNum + 1))
1136 {
1137 bb_error_msg("Inserting at bad line number");
1138
1139 return FALSE;
1140 }
1141
1142 newLp = (LINE *) malloc(sizeof(LINE) + len - 1);
1143
1144 if (newLp == NULL)
1145 {
1146 bb_error_msg("Failed to allocate memory for line");
1147
1148 return FALSE;
1149 }
1150
1151 memcpy(newLp->data, data, len);
1152 newLp->len = len;
1153
1154 if (num > lastNum)
1155 lp = &lines;
1156 else
1157 {
1158 lp = findLine(num);
1159
1160 if (lp == NULL)
1161 {
1162 free((char *) newLp);
1163
1164 return FALSE;
1165 }
1166 }
1167
1168 newLp->next = lp;
1169 newLp->prev = lp->prev;
1170 lp->prev->next = newLp;
1171 lp->prev = newLp;
1172
1173 lastNum++;
1174 dirty = TRUE;
1175
1176 return setCurNum(num);
1177}
1178
1179
1180/*
1181 * Delete lines from the given range.
1182 */
1183static int deleteLines(int num1, int num2)
1184{
1185 LINE *lp, *nlp, *plp;
1186 int count;
1187
1188 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
1189 {
1190 bb_error_msg("Bad line numbers for delete");
1191
1192 return FALSE;
1193 }
1194
1195 lp = findLine(num1);
1196
1197 if (lp == NULL)
1198 return FALSE;
1199
1200 if ((curNum >= num1) && (curNum <= num2))
1201 {
1202 if (num2 < lastNum)
1203 setCurNum(num2 + 1);
1204 else if (num1 > 1)
1205 setCurNum(num1 - 1);
1206 else
1207 curNum = 0;
1208 }
1209
1210 count = num2 - num1 + 1;
1211
1212 if (curNum > num2)
1213 curNum -= count;
1214
1215 lastNum -= count;
1216
1217 while (count-- > 0)
1218 {
1219 nlp = lp->next;
1220 plp = lp->prev;
1221 plp->next = nlp;
1222 nlp->prev = plp;
1223 lp->next = NULL;
1224 lp->prev = NULL;
1225 lp->len = 0;
1226 free(lp);
1227 lp = nlp;
1228 }
1229
1230 dirty = TRUE;
1231
1232 return TRUE;
1233}
1234
1235
1236/*
1237 * Search for a line which contains the specified string.
1238 * If the string is NULL, then the previously searched for string
1239 * is used. The currently searched for string is saved for future use.
1240 * Returns the line number which matches, or 0 if there was no match
1241 * with an error printed.
1242 */
1243static int searchLines(const char *str, int num1, int num2)
1244{
1245 const LINE *lp;
1246 int len;
1247
1248 if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
1249 {
1250 bb_error_msg("Bad line numbers for search");
1251
1252 return 0;
1253 }
1254
1255 if (*str == '\0')
1256 {
1257 if (searchString[0] == '\0')
1258 {
1259 bb_error_msg("No previous search string");
1260
1261 return 0;
1262 }
1263
1264 str = searchString;
1265 }
1266
1267 if (str != searchString)
1268 strcpy(searchString, str);
1269
1270 len = strlen(str);
1271
1272 lp = findLine(num1);
1273
1274 if (lp == NULL)
1275 return 0;
1276
1277 while (num1 <= num2)
1278 {
1279 if (findString(lp, str, len, 0) >= 0)
1280 return num1;
1281
1282 num1++;
1283 lp = lp->next;
1284 }
1285
1286 bb_error_msg("Cannot find string \"%s\"", str);
1287
1288 return 0;
1289}
1290
1291
1292/*
1293 * Return a pointer to the specified line number.
1294 */
1295static LINE *findLine(int num)
1296{
1297 LINE *lp;
1298 int lnum;
1299
1300 if ((num < 1) || (num > lastNum))
1301 {
1302 bb_error_msg("Line number %d does not exist", num);
1303
1304 return NULL;
1305 }
1306
1307 if (curNum <= 0)
1308 {
1309 curNum = 1;
1310 curLine = lines.next;
1311 }
1312
1313 if (num == curNum)
1314 return curLine;
1315
1316 lp = curLine;
1317 lnum = curNum;
1318
1319 if (num < (curNum / 2))
1320 {
1321 lp = lines.next;
1322 lnum = 1;
1323 }
1324 else if (num > ((curNum + lastNum) / 2))
1325 {
1326 lp = lines.prev;
1327 lnum = lastNum;
1328 }
1329
1330 while (lnum < num)
1331 {
1332 lp = lp->next;
1333 lnum++;
1334 }
1335
1336 while (lnum > num)
1337 {
1338 lp = lp->prev;
1339 lnum--;
1340 }
1341
1342 return lp;
1343}
1344
1345
1346/*
1347 * Set the current line number.
1348 * Returns TRUE if successful.
1349 */
1350static int setCurNum(int num)
1351{
1352 LINE *lp;
1353
1354 lp = findLine(num);
1355
1356 if (lp == NULL)
1357 return FALSE;
1358
1359 curNum = num;
1360 curLine = lp;
1361
1362 return TRUE;
1363}
diff --git a/include/applets.h b/include/applets.h
index 06bd9e7f5..b83e8ed79 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -95,10 +95,11 @@ USE_DPKG(APPLET(dpkg, dpkg_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
95USE_DPKG_DEB(APPLET_ODDNAME(dpkg-deb, dpkg_deb_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER, dpkg_deb)) 95USE_DPKG_DEB(APPLET_ODDNAME(dpkg-deb, dpkg_deb_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER, dpkg_deb))
96USE_DU(APPLET(du, du_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 96USE_DU(APPLET(du, du_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
97USE_DUMPKMAP(APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER)) 97USE_DUMPKMAP(APPLET(dumpkmap, dumpkmap_main, _BB_DIR_BIN, _BB_SUID_NEVER))
98USE_DUMPLEASES(APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 98//USE_DUMPLEASES(APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
99USE_E2FSCK(APPLET(e2fsck, e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)) 99USE_E2FSCK(APPLET(e2fsck, e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
100USE_E2LABEL(APPLET_NOUSAGE(e2label, tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER)) 100USE_E2LABEL(APPLET_NOUSAGE(e2label, tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
101USE_ECHO(APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER)) 101USE_ECHO(APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER))
102USE_ED(APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER))
102USE_FEATURE_GREP_EGREP_ALIAS(APPLET_NOUSAGE(egrep, grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)) 103USE_FEATURE_GREP_EGREP_ALIAS(APPLET_NOUSAGE(egrep, grep_main, _BB_DIR_BIN, _BB_SUID_NEVER))
103USE_EJECT(APPLET(eject, eject_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 104USE_EJECT(APPLET(eject, eject_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
104USE_ENV(APPLET(env, env_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 105USE_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
273USE_TRUE(APPLET(true, true_main, _BB_DIR_BIN, _BB_SUID_NEVER)) 274USE_TRUE(APPLET(true, true_main, _BB_DIR_BIN, _BB_SUID_NEVER))
274USE_TTY(APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 275USE_TTY(APPLET(tty, tty_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
275USE_TUNE2FS(APPLET(tune2fs, tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER)) 276USE_TUNE2FS(APPLET(tune2fs, tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
276USE_UDHCPC(APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER)) 277//USE_UDHCPC(APPLET(udhcpc, udhcpc_main, _BB_DIR_SBIN, _BB_SUID_NEVER))
277USE_UDHCPD(APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER)) 278//USE_UDHCPD(APPLET(udhcpd, udhcpd_main, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
278USE_UMOUNT(APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER)) 279USE_UMOUNT(APPLET(umount, umount_main, _BB_DIR_BIN, _BB_SUID_NEVER))
279USE_UNAME(APPLET(uname, uname_main, _BB_DIR_BIN, _BB_SUID_NEVER)) 280USE_UNAME(APPLET(uname, uname_main, _BB_DIR_BIN, _BB_SUID_NEVER))
280USE_UNCOMPRESS(APPLET(uncompress, uncompress_main, _BB_DIR_BIN, _BB_SUID_NEVER)) 281USE_UNCOMPRESS(APPLET(uncompress, uncompress_main, _BB_DIR_BIN, _BB_SUID_NEVER))
diff --git a/include/usage.h b/include/usage.h
index 65a7874fa..d09c1108e 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -626,6 +626,9 @@ USE_FEATURE_DATE_ISOFMT( \
626 "Options:\n" \ 626 "Options:\n" \
627 "\t-t\tclose tray" 627 "\t-t\tclose tray"
628 628
629#define ed_trivial_usage ""
630#define ed_full_usage ""
631
629#define env_trivial_usage \ 632#define env_trivial_usage \
630 "[-iu] [-] [name=value]... [command]" 633 "[-iu] [-] [name=value]... [command]"
631#define env_full_usage \ 634#define env_full_usage \
diff --git a/patches/ed.patch b/patches/ed.patch
deleted file mode 100644
index 6d51830a5..000000000
--- a/patches/ed.patch
+++ /dev/null
@@ -1,1489 +0,0 @@
1Index: editors/Makefile.in
2===================================================================
3--- editors/Makefile.in (revision 10144)
4+++ editors/Makefile.in (working copy)
5@@ -24,8 +24,9 @@
6 srcdir=$(top_srcdir)/editors
7
8 EDITOR-y:=
9-EDITOR-$(CONFIG_AWK) += awk.o
10-EDITOR-$(CONFIG_PATCH) += patch.o
11+EDITOR-$(CONFIG_AWK) += awk.o
12+EDITOR-$(CONFIG_ED) += ed.o
13+EDITOR-$(CONFIG_PATCH) += patch.o
14 EDITOR-$(CONFIG_SED) += sed.o
15 EDITOR-$(CONFIG_VI) += vi.o
16 EDITOR_SRC:= $(EDITOR-y)
17Index: editors/Config.in
18===================================================================
19--- editors/Config.in (revision 10144)
20+++ editors/Config.in (working copy)
21@@ -20,6 +20,12 @@
22 Enable math functions of the Awk programming language.
23 NOTE: This will require libm to be present for linking.
24
25+config CONFIG_ED
26+ bool "ed"
27+ default n
28+ help
29+ ed
30+
31 config CONFIG_PATCH
32 bool "patch"
33 default n
34Index: include/usage.h
35===================================================================
36--- include/usage.h (revision 10151)
37+++ include/usage.h (working copy)
38@@ -556,6 +561,9 @@
39 "$ echo \"Erik\\nis\\ncool\"\n" \
40 "Erik\\nis\\ncool\n")
41
42+#define ed_trivial_usage ""
43+#define ed_full_usage ""
44+
45 #define env_trivial_usage \
46 "[-iu] [-] [name=value]... [command]"
47 #define env_full_usage \
48Index: include/applets.h
49===================================================================
50--- include/applets.h (revision 10151)
51+++ include/applets.h (working copy)
52@@ -179,6 +179,9 @@
53 #ifdef CONFIG_ECHO
54 APPLET(echo, echo_main, _BB_DIR_BIN, _BB_SUID_NEVER)
55 #endif
56+#ifdef CONFIG_ED
57+ APPLET(ed, ed_main, _BB_DIR_BIN, _BB_SUID_NEVER)
58+#endif
59 #if defined(CONFIG_FEATURE_GREP_EGREP_ALIAS)
60 APPLET_NOUSAGE("egrep", grep_main, _BB_DIR_BIN, _BB_SUID_NEVER)
61 #endif
62--- /dev/null 2005-04-24 01:00:01.350003056 -0400
63+++ ed.c 2005-04-24 01:38:51.000000000 -0400
64@@ -0,0 +1,1425 @@
65+/*
66+ * Copyright (c) 2002 by David I. Bell
67+ * Permission is granted to use, distribute, or modify this source,
68+ * provided that this copyright notice remains intact.
69+ *
70+ * The "ed" built-in command (much simplified)
71+ */
72+
73+#include <stdio.h>
74+#include <stdlib.h>
75+#include <unistd.h>
76+#include <fcntl.h>
77+#include <string.h>
78+#include <memory.h>
79+#include <time.h>
80+#include <ctype.h>
81+#include <sys/param.h>
82+#include <malloc.h>
83+#include "busybox.h"
84+
85+#define USERSIZE 1024 /* max line length typed in by user */
86+#define INITBUF_SIZE 1024 /* initial buffer size */
87+
88+typedef int BOOL;
89+typedef int NUM;
90+typedef int LEN;
91+
92+typedef struct LINE LINE;
93+struct LINE {
94+ LINE *next;
95+ LINE *prev;
96+ LEN len;
97+ char data[1];
98+};
99+
100+static LINE lines;
101+static LINE *curLine;
102+static NUM curNum;
103+static NUM lastNum;
104+static NUM marks[26];
105+static BOOL dirty;
106+static char *fileName;
107+static char searchString[USERSIZE];
108+
109+static char *bufBase;
110+static char *bufPtr;
111+static LEN bufUsed;
112+static LEN bufSize;
113+
114+static void doCommands(void);
115+static void subCommand(const char * cmd, NUM num1, NUM num2);
116+static BOOL getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum);
117+static BOOL setCurNum(NUM num);
118+static BOOL initEdit(void);
119+static void termEdit(void);
120+static void addLines(NUM num);
121+static BOOL insertLine(NUM num, const char * data, LEN len);
122+static BOOL deleteLines(NUM num1, NUM num2);
123+static BOOL printLines(NUM num1, NUM num2, BOOL expandFlag);
124+static BOOL writeLines(const char * file, NUM num1, NUM num2);
125+static BOOL readLines(const char * file, NUM num);
126+static NUM searchLines(const char * str, NUM num1, NUM num2);
127+static LINE * findLine(NUM num);
128+
129+static LEN findString(const LINE * lp, const char * str, LEN len, LEN offset);
130+
131+int ed_main(int argc, char **argv)
132+{
133+ if (!initEdit())
134+ return EXIT_FAILURE;
135+
136+ if (argc > 1) {
137+ fileName = strdup(argv[1]);
138+
139+ if (fileName == NULL) {
140+ bb_error_msg("No memory");
141+ termEdit();
142+ return EXIT_SUCCESS;
143+ }
144+
145+ if (!readLines(fileName, 1)) {
146+ termEdit();
147+ return EXIT_SUCCESS;
148+ }
149+
150+ if (lastNum)
151+ setCurNum(1);
152+
153+ dirty = FALSE;
154+ }
155+
156+ doCommands();
157+
158+ termEdit();
159+ return EXIT_SUCCESS;
160+}
161+
162+/*
163+ * Read commands until we are told to stop.
164+ */
165+static void doCommands(void)
166+{
167+ const char * cp;
168+ char * endbuf;
169+ char * newname;
170+ int len;
171+ NUM num1;
172+ NUM num2;
173+ BOOL have1;
174+ BOOL have2;
175+ char buf[USERSIZE];
176+
177+ while (TRUE)
178+ {
179+ printf(": ");
180+ fflush(stdout);
181+
182+ if (fgets(buf, sizeof(buf), stdin) == NULL)
183+ return;
184+
185+ len = strlen(buf);
186+
187+ if (len == 0)
188+ return;
189+
190+ endbuf = &buf[len - 1];
191+
192+ if (*endbuf != '\n')
193+ {
194+ bb_error_msg("Command line too long");
195+
196+ do
197+ {
198+ len = fgetc(stdin);
199+ }
200+ while ((len != EOF) && (len != '\n'));
201+
202+ continue;
203+ }
204+
205+ while ((endbuf > buf) && isblank(endbuf[-1]))
206+ endbuf--;
207+
208+ *endbuf = '\0';
209+
210+ cp = buf;
211+
212+ while (isblank(*cp))
213+ cp++;
214+
215+ have1 = FALSE;
216+ have2 = FALSE;
217+
218+ if ((curNum == 0) && (lastNum > 0))
219+ {
220+ curNum = 1;
221+ curLine = lines.next;
222+ }
223+
224+ if (!getNum(&cp, &have1, &num1))
225+ continue;
226+
227+ while (isblank(*cp))
228+ cp++;
229+
230+ if (*cp == ',')
231+ {
232+ cp++;
233+
234+ if (!getNum(&cp, &have2, &num2))
235+ continue;
236+
237+ if (!have1)
238+ num1 = 1;
239+
240+ if (!have2)
241+ num2 = lastNum;
242+
243+ have1 = TRUE;
244+ have2 = TRUE;
245+ }
246+
247+ if (!have1)
248+ num1 = curNum;
249+
250+ if (!have2)
251+ num2 = num1;
252+
253+ switch (*cp++)
254+ {
255+ case 'a':
256+ addLines(num1 + 1);
257+ break;
258+
259+ case 'c':
260+ deleteLines(num1, num2);
261+ addLines(num1);
262+ break;
263+
264+ case 'd':
265+ deleteLines(num1, num2);
266+ break;
267+
268+ case 'f':
269+ if (*cp && !isblank(*cp))
270+ {
271+ bb_error_msg("Bad file command");
272+ break;
273+ }
274+
275+ while (isblank(*cp))
276+ cp++;
277+
278+ if (*cp == '\0')
279+ {
280+ if (fileName)
281+ printf("\"%s\"\n", fileName);
282+ else
283+ printf("No file name\n");
284+
285+ break;
286+ }
287+
288+ newname = strdup(cp);
289+
290+ if (newname == NULL)
291+ {
292+ bb_error_msg("No memory for file name");
293+ break;
294+ }
295+
296+ if (fileName)
297+ free(fileName);
298+
299+ fileName = newname;
300+ break;
301+
302+ case 'i':
303+ addLines(num1);
304+ break;
305+
306+ case 'k':
307+ while (isblank(*cp))
308+ cp++;
309+
310+ if ((*cp < 'a') || (*cp > 'a') || cp[1])
311+ {
312+ bb_error_msg("Bad mark name");
313+ break;
314+ }
315+
316+ marks[*cp - 'a'] = num2;
317+ break;
318+
319+ case 'l':
320+ printLines(num1, num2, TRUE);
321+ break;
322+
323+ case 'p':
324+ printLines(num1, num2, FALSE);
325+ break;
326+
327+ case 'q':
328+ while (isblank(*cp))
329+ cp++;
330+
331+ if (have1 || *cp)
332+ {
333+ bb_error_msg("Bad quit command");
334+ break;
335+ }
336+
337+ if (!dirty)
338+ return;
339+
340+ printf("Really quit? ");
341+ fflush(stdout);
342+
343+ buf[0] = '\0';
344+ fgets(buf, sizeof(buf), stdin);
345+ cp = buf;
346+
347+ while (isblank(*cp))
348+ cp++;
349+
350+ if ((*cp == 'y') || (*cp == 'Y'))
351+ return;
352+
353+ break;
354+
355+ case 'r':
356+ if (*cp && !isblank(*cp))
357+ {
358+ bb_error_msg("Bad read command");
359+ break;
360+ }
361+
362+ while (isblank(*cp))
363+ cp++;
364+
365+ if (*cp == '\0')
366+ {
367+ bb_error_msg("No file name");
368+ break;
369+ }
370+
371+ if (!have1)
372+ num1 = lastNum;
373+
374+ if (readLines(cp, num1 + 1))
375+ break;
376+
377+ if (fileName == NULL)
378+ fileName = strdup(cp);
379+
380+ break;
381+
382+ case 's':
383+ subCommand(cp, num1, num2);
384+ break;
385+
386+ case 'w':
387+ if (*cp && !isblank(*cp))
388+ {
389+ bb_error_msg("Bad write command");
390+ break;
391+ }
392+
393+ while (isblank(*cp))
394+ cp++;
395+
396+ if (!have1) {
397+ num1 = 1;
398+ num2 = lastNum;
399+ }
400+
401+ if (*cp == '\0')
402+ cp = fileName;
403+
404+ if (cp == NULL)
405+ {
406+ bb_error_msg("No file name specified");
407+ break;
408+ }
409+
410+ writeLines(cp, num1, num2);
411+ break;
412+
413+ case 'z':
414+ switch (*cp)
415+ {
416+ case '-':
417+ printLines(curNum-21, curNum, FALSE);
418+ break;
419+ case '.':
420+ printLines(curNum-11, curNum+10, FALSE);
421+ break;
422+ default:
423+ printLines(curNum, curNum+21, FALSE);
424+ break;
425+ }
426+ break;
427+
428+ case '.':
429+ if (have1)
430+ {
431+ bb_error_msg("No arguments allowed");
432+ break;
433+ }
434+
435+ printLines(curNum, curNum, FALSE);
436+ break;
437+
438+ case '-':
439+ if (setCurNum(curNum - 1))
440+ printLines(curNum, curNum, FALSE);
441+
442+ break;
443+
444+ case '=':
445+ printf("%d\n", num1);
446+ break;
447+
448+ case '\0':
449+ if (have1)
450+ {
451+ printLines(num2, num2, FALSE);
452+ break;
453+ }
454+
455+ if (setCurNum(curNum + 1))
456+ printLines(curNum, curNum, FALSE);
457+
458+ break;
459+
460+ default:
461+ bb_error_msg("Unimplemented command");
462+ break;
463+ }
464+ }
465+}
466+
467+
468+/*
469+ * Do the substitute command.
470+ * The current line is set to the last substitution done.
471+ */
472+static void
473+subCommand(const char * cmd, NUM num1, NUM num2)
474+{
475+ int delim;
476+ char * cp;
477+ char * oldStr;
478+ char * newStr;
479+ LEN oldLen;
480+ LEN newLen;
481+ LEN deltaLen;
482+ LEN offset;
483+ LINE * lp;
484+ LINE * nlp;
485+ BOOL globalFlag;
486+ BOOL printFlag;
487+ BOOL didSub;
488+ BOOL needPrint;
489+ char buf[USERSIZE];
490+
491+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
492+ {
493+ bb_error_msg("Bad line range for substitute");
494+
495+ return;
496+ }
497+
498+ globalFlag = FALSE;
499+ printFlag = FALSE;
500+ didSub = FALSE;
501+ needPrint = FALSE;
502+
503+ /*
504+ * Copy the command so we can modify it.
505+ */
506+ strcpy(buf, cmd);
507+ cp = buf;
508+
509+ if (isblank(*cp) || (*cp == '\0'))
510+ {
511+ bb_error_msg("Bad delimiter for substitute");
512+
513+ return;
514+ }
515+
516+ delim = *cp++;
517+ oldStr = cp;
518+
519+ cp = strchr(cp, delim);
520+
521+ if (cp == NULL)
522+ {
523+ bb_error_msg("Missing 2nd delimiter for substitute");
524+
525+ return;
526+ }
527+
528+ *cp++ = '\0';
529+
530+ newStr = cp;
531+ cp = strchr(cp, delim);
532+
533+ if (cp)
534+ *cp++ = '\0';
535+ else
536+ cp = "";
537+
538+ while (*cp) switch (*cp++)
539+ {
540+ case 'g':
541+ globalFlag = TRUE;
542+ break;
543+
544+ case 'p':
545+ printFlag = TRUE;
546+ break;
547+
548+ default:
549+ bb_error_msg("Unknown option for substitute");
550+
551+ return;
552+ }
553+
554+ if (*oldStr == '\0')
555+ {
556+ if (searchString[0] == '\0')
557+ {
558+ bb_error_msg("No previous search string");
559+
560+ return;
561+ }
562+
563+ oldStr = searchString;
564+ }
565+
566+ if (oldStr != searchString)
567+ strcpy(searchString, oldStr);
568+
569+ lp = findLine(num1);
570+
571+ if (lp == NULL)
572+ return;
573+
574+ oldLen = strlen(oldStr);
575+ newLen = strlen(newStr);
576+ deltaLen = newLen - oldLen;
577+ offset = 0;
578+ nlp = NULL;
579+
580+ while (num1 <= num2)
581+ {
582+ offset = findString(lp, oldStr, oldLen, offset);
583+
584+ if (offset < 0)
585+ {
586+ if (needPrint)
587+ {
588+ printLines(num1, num1, FALSE);
589+ needPrint = FALSE;
590+ }
591+
592+ offset = 0;
593+ lp = lp->next;
594+ num1++;
595+
596+ continue;
597+ }
598+
599+ needPrint = printFlag;
600+ didSub = TRUE;
601+ dirty = TRUE;
602+
603+ /*
604+ * If the replacement string is the same size or shorter
605+ * than the old string, then the substitution is easy.
606+ */
607+ if (deltaLen <= 0)
608+ {
609+ memcpy(&lp->data[offset], newStr, newLen);
610+
611+ if (deltaLen)
612+ {
613+ memcpy(&lp->data[offset + newLen],
614+ &lp->data[offset + oldLen],
615+ lp->len - offset - oldLen);
616+
617+ lp->len += deltaLen;
618+ }
619+
620+ offset += newLen;
621+
622+ if (globalFlag)
623+ continue;
624+
625+ if (needPrint)
626+ {
627+ printLines(num1, num1, FALSE);
628+ needPrint = FALSE;
629+ }
630+
631+ lp = lp->next;
632+ num1++;
633+
634+ continue;
635+ }
636+
637+ /*
638+ * The new string is larger, so allocate a new line
639+ * structure and use that. Link it in in place of
640+ * the old line structure.
641+ */
642+ nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen);
643+
644+ if (nlp == NULL)
645+ {
646+ bb_error_msg("Cannot get memory for line");
647+
648+ return;
649+ }
650+
651+ nlp->len = lp->len + deltaLen;
652+
653+ memcpy(nlp->data, lp->data, offset);
654+
655+ memcpy(&nlp->data[offset], newStr, newLen);
656+
657+ memcpy(&nlp->data[offset + newLen],
658+ &lp->data[offset + oldLen],
659+ lp->len - offset - oldLen);
660+
661+ nlp->next = lp->next;
662+ nlp->prev = lp->prev;
663+ nlp->prev->next = nlp;
664+ nlp->next->prev = nlp;
665+
666+ if (curLine == lp)
667+ curLine = nlp;
668+
669+ free(lp);
670+ lp = nlp;
671+
672+ offset += newLen;
673+
674+ if (globalFlag)
675+ continue;
676+
677+ if (needPrint)
678+ {
679+ printLines(num1, num1, FALSE);
680+ needPrint = FALSE;
681+ }
682+
683+ lp = lp->next;
684+ num1++;
685+ }
686+
687+ if (!didSub)
688+ bb_error_msg("No substitutions found for \"%s\"", oldStr);
689+}
690+
691+
692+/*
693+ * Search a line for the specified string starting at the specified
694+ * offset in the line. Returns the offset of the found string, or -1.
695+ */
696+static LEN
697+findString( const LINE * lp, const char * str, LEN len, LEN offset)
698+{
699+ LEN left;
700+ const char * cp;
701+ const char * ncp;
702+
703+ cp = &lp->data[offset];
704+ left = lp->len - offset;
705+
706+ while (left >= len)
707+ {
708+ ncp = memchr(cp, *str, left);
709+
710+ if (ncp == NULL)
711+ return -1;
712+
713+ left -= (ncp - cp);
714+
715+ if (left < len)
716+ return -1;
717+
718+ cp = ncp;
719+
720+ if (memcmp(cp, str, len) == 0)
721+ return (cp - lp->data);
722+
723+ cp++;
724+ left--;
725+ }
726+
727+ return -1;
728+}
729+
730+
731+/*
732+ * Add lines which are typed in by the user.
733+ * The lines are inserted just before the specified line number.
734+ * The lines are terminated by a line containing a single dot (ugly!),
735+ * or by an end of file.
736+ */
737+static void
738+addLines(NUM num)
739+{
740+ int len;
741+ char buf[USERSIZE + 1];
742+
743+ while (fgets(buf, sizeof(buf), stdin))
744+ {
745+ if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
746+ return;
747+
748+ len = strlen(buf);
749+
750+ if (len == 0)
751+ return;
752+
753+ if (buf[len - 1] != '\n')
754+ {
755+ bb_error_msg("Line too long");
756+
757+ do
758+ {
759+ len = fgetc(stdin);
760+ }
761+ while ((len != EOF) && (len != '\n'));
762+
763+ return;
764+ }
765+
766+ if (!insertLine(num++, buf, len))
767+ return;
768+ }
769+}
770+
771+
772+/*
773+ * Parse a line number argument if it is present. This is a sum
774+ * or difference of numbers, '.', '$', 'x, or a search string.
775+ * Returns TRUE if successful (whether or not there was a number).
776+ * Returns FALSE if there was a parsing error, with a message output.
777+ * Whether there was a number is returned indirectly, as is the number.
778+ * The character pointer which stopped the scan is also returned.
779+ */
780+static BOOL
781+getNum(const char ** retcp, BOOL * retHaveNum, NUM * retNum)
782+{
783+ const char * cp;
784+ char * endStr;
785+ char str[USERSIZE];
786+ BOOL haveNum;
787+ NUM value;
788+ NUM num;
789+ NUM sign;
790+
791+ cp = *retcp;
792+ haveNum = FALSE;
793+ value = 0;
794+ sign = 1;
795+
796+ while (TRUE)
797+ {
798+ while (isblank(*cp))
799+ cp++;
800+
801+ switch (*cp)
802+ {
803+ case '.':
804+ haveNum = TRUE;
805+ num = curNum;
806+ cp++;
807+ break;
808+
809+ case '$':
810+ haveNum = TRUE;
811+ num = lastNum;
812+ cp++;
813+ break;
814+
815+ case '\'':
816+ cp++;
817+
818+ if ((*cp < 'a') || (*cp > 'z'))
819+ {
820+ bb_error_msg("Bad mark name");
821+
822+ return FALSE;
823+ }
824+
825+ haveNum = TRUE;
826+ num = marks[*cp++ - 'a'];
827+ break;
828+
829+ case '/':
830+ strcpy(str, ++cp);
831+ endStr = strchr(str, '/');
832+
833+ if (endStr)
834+ {
835+ *endStr++ = '\0';
836+ cp += (endStr - str);
837+ }
838+ else
839+ cp = "";
840+
841+ num = searchLines(str, curNum, lastNum);
842+
843+ if (num == 0)
844+ return FALSE;
845+
846+ haveNum = TRUE;
847+ break;
848+
849+ default:
850+ if (!isdigit(*cp))
851+ {
852+ *retcp = cp;
853+ *retHaveNum = haveNum;
854+ *retNum = value;
855+
856+ return TRUE;
857+ }
858+
859+ num = 0;
860+
861+ while (isdigit(*cp))
862+ num = num * 10 + *cp++ - '0';
863+
864+ haveNum = TRUE;
865+ break;
866+ }
867+
868+ value += num * sign;
869+
870+ while (isblank(*cp))
871+ cp++;
872+
873+ switch (*cp)
874+ {
875+ case '-':
876+ sign = -1;
877+ cp++;
878+ break;
879+
880+ case '+':
881+ sign = 1;
882+ cp++;
883+ break;
884+
885+ default:
886+ *retcp = cp;
887+ *retHaveNum = haveNum;
888+ *retNum = value;
889+
890+ return TRUE;
891+ }
892+ }
893+}
894+
895+
896+/*
897+ * Initialize everything for editing.
898+ */
899+static BOOL
900+initEdit(void)
901+{
902+ int i;
903+
904+ bufSize = INITBUF_SIZE;
905+ bufBase = malloc(bufSize);
906+
907+ if (bufBase == NULL)
908+ {
909+ bb_error_msg("No memory for buffer");
910+
911+ return FALSE;
912+ }
913+
914+ bufPtr = bufBase;
915+ bufUsed = 0;
916+
917+ lines.next = &lines;
918+ lines.prev = &lines;
919+
920+ curLine = NULL;
921+ curNum = 0;
922+ lastNum = 0;
923+ dirty = FALSE;
924+ fileName = NULL;
925+ searchString[0] = '\0';
926+
927+ for (i = 0; i < 26; i++)
928+ marks[i] = 0;
929+
930+ return TRUE;
931+}
932+
933+
934+/*
935+ * Finish editing.
936+ */
937+static void
938+termEdit(void)
939+{
940+ if (bufBase)
941+ free(bufBase);
942+
943+ bufBase = NULL;
944+ bufPtr = NULL;
945+ bufSize = 0;
946+ bufUsed = 0;
947+
948+ if (fileName)
949+ free(fileName);
950+
951+ fileName = NULL;
952+
953+ searchString[0] = '\0';
954+
955+ if (lastNum)
956+ deleteLines(1, lastNum);
957+
958+ lastNum = 0;
959+ curNum = 0;
960+ curLine = NULL;
961+}
962+
963+
964+/*
965+ * Read lines from a file at the specified line number.
966+ * Returns TRUE if the file was successfully read.
967+ */
968+static BOOL
969+readLines(const char * file, NUM num)
970+{
971+ int fd;
972+ int cc;
973+ LEN len;
974+ LEN lineCount;
975+ LEN charCount;
976+ char * cp;
977+
978+ if ((num < 1) || (num > lastNum + 1))
979+ {
980+ bb_error_msg("Bad line for read");
981+
982+ return FALSE;
983+ }
984+
985+ fd = open(file, 0);
986+
987+ if (fd < 0)
988+ {
989+ perror(file);
990+
991+ return FALSE;
992+ }
993+
994+ bufPtr = bufBase;
995+ bufUsed = 0;
996+ lineCount = 0;
997+ charCount = 0;
998+ cc = 0;
999+
1000+ printf("\"%s\", ", file);
1001+ fflush(stdout);
1002+
1003+ do
1004+ {
1005+ cp = memchr(bufPtr, '\n', bufUsed);
1006+
1007+ if (cp)
1008+ {
1009+ len = (cp - bufPtr) + 1;
1010+
1011+ if (!insertLine(num, bufPtr, len))
1012+ {
1013+ close(fd);
1014+
1015+ return FALSE;
1016+ }
1017+
1018+ bufPtr += len;
1019+ bufUsed -= len;
1020+ charCount += len;
1021+ lineCount++;
1022+ num++;
1023+
1024+ continue;
1025+ }
1026+
1027+ if (bufPtr != bufBase)
1028+ {
1029+ memcpy(bufBase, bufPtr, bufUsed);
1030+ bufPtr = bufBase + bufUsed;
1031+ }
1032+
1033+ if (bufUsed >= bufSize)
1034+ {
1035+ len = (bufSize * 3) / 2;
1036+ cp = realloc(bufBase, len);
1037+
1038+ if (cp == NULL)
1039+ {
1040+ bb_error_msg("No memory for buffer");
1041+ close(fd);
1042+
1043+ return FALSE;
1044+ }
1045+
1046+ bufBase = cp;
1047+ bufPtr = bufBase + bufUsed;
1048+ bufSize = len;
1049+ }
1050+
1051+ cc = read(fd, bufPtr, bufSize - bufUsed);
1052+ bufUsed += cc;
1053+ bufPtr = bufBase;
1054+
1055+ }
1056+ while (cc > 0);
1057+
1058+ if (cc < 0)
1059+ {
1060+ perror(file);
1061+ close(fd);
1062+
1063+ return FALSE;
1064+ }
1065+
1066+ if (bufUsed)
1067+ {
1068+ if (!insertLine(num, bufPtr, bufUsed))
1069+ {
1070+ close(fd);
1071+
1072+ return -1;
1073+ }
1074+
1075+ lineCount++;
1076+ charCount += bufUsed;
1077+ }
1078+
1079+ close(fd);
1080+
1081+ printf("%d lines%s, %d chars\n", lineCount,
1082+ (bufUsed ? " (incomplete)" : ""), charCount);
1083+
1084+ return TRUE;
1085+}
1086+
1087+
1088+/*
1089+ * Write the specified lines out to the specified file.
1090+ * Returns TRUE if successful, or FALSE on an error with a message output.
1091+ */
1092+static BOOL
1093+writeLines(const char * file, NUM num1, NUM num2)
1094+{
1095+ int fd;
1096+ LINE * lp;
1097+ LEN lineCount;
1098+ LEN charCount;
1099+
1100+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
1101+ {
1102+ bb_error_msg("Bad line range for write");
1103+
1104+ return FALSE;
1105+ }
1106+
1107+ lineCount = 0;
1108+ charCount = 0;
1109+
1110+ fd = creat(file, 0666);
1111+
1112+ if (fd < 0) {
1113+ perror(file);
1114+
1115+ return FALSE;
1116+ }
1117+
1118+ printf("\"%s\", ", file);
1119+ fflush(stdout);
1120+
1121+ lp = findLine(num1);
1122+
1123+ if (lp == NULL)
1124+ {
1125+ close(fd);
1126+
1127+ return FALSE;
1128+ }
1129+
1130+ while (num1++ <= num2)
1131+ {
1132+ if (write(fd, lp->data, lp->len) != lp->len)
1133+ {
1134+ perror(file);
1135+ close(fd);
1136+
1137+ return FALSE;
1138+ }
1139+
1140+ charCount += lp->len;
1141+ lineCount++;
1142+ lp = lp->next;
1143+ }
1144+
1145+ if (close(fd) < 0)
1146+ {
1147+ perror(file);
1148+
1149+ return FALSE;
1150+ }
1151+
1152+ printf("%d lines, %d chars\n", lineCount, charCount);
1153+
1154+ return TRUE;
1155+}
1156+
1157+
1158+/*
1159+ * Print lines in a specified range.
1160+ * The last line printed becomes the current line.
1161+ * If expandFlag is TRUE, then the line is printed specially to
1162+ * show magic characters.
1163+ */
1164+static BOOL
1165+printLines(NUM num1, NUM num2, BOOL expandFlag)
1166+{
1167+ const LINE * lp;
1168+ const unsigned char * cp;
1169+ int ch;
1170+ LEN count;
1171+
1172+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
1173+ {
1174+ bb_error_msg("Bad line range for print");
1175+
1176+ return FALSE;
1177+ }
1178+
1179+ lp = findLine(num1);
1180+
1181+ if (lp == NULL)
1182+ return FALSE;
1183+
1184+ while (num1 <= num2)
1185+ {
1186+ if (!expandFlag)
1187+ {
1188+ write(1, lp->data, lp->len);
1189+ setCurNum(num1++);
1190+ lp = lp->next;
1191+
1192+ continue;
1193+ }
1194+
1195+ /*
1196+ * Show control characters and characters with the
1197+ * high bit set specially.
1198+ */
1199+ cp = lp->data;
1200+ count = lp->len;
1201+
1202+ if ((count > 0) && (cp[count - 1] == '\n'))
1203+ count--;
1204+
1205+ while (count-- > 0)
1206+ {
1207+ ch = *cp++;
1208+
1209+ if (ch & 0x80)
1210+ {
1211+ fputs("M-", stdout);
1212+ ch &= 0x7f;
1213+ }
1214+
1215+ if (ch < ' ')
1216+ {
1217+ fputc('^', stdout);
1218+ ch += '@';
1219+ }
1220+
1221+ if (ch == 0x7f)
1222+ {
1223+ fputc('^', stdout);
1224+ ch = '?';
1225+ }
1226+
1227+ fputc(ch, stdout);
1228+ }
1229+
1230+ fputs("$\n", stdout);
1231+
1232+ setCurNum(num1++);
1233+ lp = lp->next;
1234+ }
1235+
1236+ return TRUE;
1237+}
1238+
1239+
1240+/*
1241+ * Insert a new line with the specified text.
1242+ * The line is inserted so as to become the specified line,
1243+ * thus pushing any existing and further lines down one.
1244+ * The inserted line is also set to become the current line.
1245+ * Returns TRUE if successful.
1246+ */
1247+static BOOL
1248+insertLine(NUM num, const char * data, LEN len)
1249+{
1250+ LINE * newLp;
1251+ LINE * lp;
1252+
1253+ if ((num < 1) || (num > lastNum + 1))
1254+ {
1255+ bb_error_msg("Inserting at bad line number");
1256+
1257+ return FALSE;
1258+ }
1259+
1260+ newLp = (LINE *) malloc(sizeof(LINE) + len - 1);
1261+
1262+ if (newLp == NULL)
1263+ {
1264+ bb_error_msg("Failed to allocate memory for line");
1265+
1266+ return FALSE;
1267+ }
1268+
1269+ memcpy(newLp->data, data, len);
1270+ newLp->len = len;
1271+
1272+ if (num > lastNum)
1273+ lp = &lines;
1274+ else
1275+ {
1276+ lp = findLine(num);
1277+
1278+ if (lp == NULL)
1279+ {
1280+ free((char *) newLp);
1281+
1282+ return FALSE;
1283+ }
1284+ }
1285+
1286+ newLp->next = lp;
1287+ newLp->prev = lp->prev;
1288+ lp->prev->next = newLp;
1289+ lp->prev = newLp;
1290+
1291+ lastNum++;
1292+ dirty = TRUE;
1293+
1294+ return setCurNum(num);
1295+}
1296+
1297+
1298+/*
1299+ * Delete lines from the given range.
1300+ */
1301+static BOOL
1302+deleteLines(NUM num1, NUM num2)
1303+{
1304+ LINE * lp;
1305+ LINE * nlp;
1306+ LINE * plp;
1307+ NUM count;
1308+
1309+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
1310+ {
1311+ bb_error_msg("Bad line numbers for delete");
1312+
1313+ return FALSE;
1314+ }
1315+
1316+ lp = findLine(num1);
1317+
1318+ if (lp == NULL)
1319+ return FALSE;
1320+
1321+ if ((curNum >= num1) && (curNum <= num2))
1322+ {
1323+ if (num2 < lastNum)
1324+ setCurNum(num2 + 1);
1325+ else if (num1 > 1)
1326+ setCurNum(num1 - 1);
1327+ else
1328+ curNum = 0;
1329+ }
1330+
1331+ count = num2 - num1 + 1;
1332+
1333+ if (curNum > num2)
1334+ curNum -= count;
1335+
1336+ lastNum -= count;
1337+
1338+ while (count-- > 0)
1339+ {
1340+ nlp = lp->next;
1341+ plp = lp->prev;
1342+ plp->next = nlp;
1343+ nlp->prev = plp;
1344+ lp->next = NULL;
1345+ lp->prev = NULL;
1346+ lp->len = 0;
1347+ free(lp);
1348+ lp = nlp;
1349+ }
1350+
1351+ dirty = TRUE;
1352+
1353+ return TRUE;
1354+}
1355+
1356+
1357+/*
1358+ * Search for a line which contains the specified string.
1359+ * If the string is NULL, then the previously searched for string
1360+ * is used. The currently searched for string is saved for future use.
1361+ * Returns the line number which matches, or 0 if there was no match
1362+ * with an error printed.
1363+ */
1364+static NUM
1365+searchLines(const char * str, NUM num1, NUM num2)
1366+{
1367+ const LINE * lp;
1368+ int len;
1369+
1370+ if ((num1 < 1) || (num2 > lastNum) || (num1 > num2))
1371+ {
1372+ bb_error_msg("Bad line numbers for search");
1373+
1374+ return 0;
1375+ }
1376+
1377+ if (*str == '\0')
1378+ {
1379+ if (searchString[0] == '\0')
1380+ {
1381+ bb_error_msg("No previous search string");
1382+
1383+ return 0;
1384+ }
1385+
1386+ str = searchString;
1387+ }
1388+
1389+ if (str != searchString)
1390+ strcpy(searchString, str);
1391+
1392+ len = strlen(str);
1393+
1394+ lp = findLine(num1);
1395+
1396+ if (lp == NULL)
1397+ return 0;
1398+
1399+ while (num1 <= num2)
1400+ {
1401+ if (findString(lp, str, len, 0) >= 0)
1402+ return num1;
1403+
1404+ num1++;
1405+ lp = lp->next;
1406+ }
1407+
1408+ bb_error_msg("Cannot find string \"%s\"", str);
1409+
1410+ return 0;
1411+}
1412+
1413+
1414+/*
1415+ * Return a pointer to the specified line number.
1416+ */
1417+static LINE *
1418+findLine(NUM num)
1419+{
1420+ LINE * lp;
1421+ NUM lnum;
1422+
1423+ if ((num < 1) || (num > lastNum))
1424+ {
1425+ bb_error_msg("Line number %d does not exist", num);
1426+
1427+ return NULL;
1428+ }
1429+
1430+ if (curNum <= 0)
1431+ {
1432+ curNum = 1;
1433+ curLine = lines.next;
1434+ }
1435+
1436+ if (num == curNum)
1437+ return curLine;
1438+
1439+ lp = curLine;
1440+ lnum = curNum;
1441+
1442+ if (num < (curNum / 2))
1443+ {
1444+ lp = lines.next;
1445+ lnum = 1;
1446+ }
1447+ else if (num > ((curNum + lastNum) / 2))
1448+ {
1449+ lp = lines.prev;
1450+ lnum = lastNum;
1451+ }
1452+
1453+ while (lnum < num)
1454+ {
1455+ lp = lp->next;
1456+ lnum++;
1457+ }
1458+
1459+ while (lnum > num)
1460+ {
1461+ lp = lp->prev;
1462+ lnum--;
1463+ }
1464+
1465+ return lp;
1466+}
1467+
1468+
1469+/*
1470+ * Set the current line number.
1471+ * Returns TRUE if successful.
1472+ */
1473+static BOOL
1474+setCurNum(NUM num)
1475+{
1476+ LINE * lp;
1477+
1478+ lp = findLine(num);
1479+
1480+ if (lp == NULL)
1481+ return FALSE;
1482+
1483+ curNum = num;
1484+ curLine = lp;
1485+
1486+ return TRUE;
1487+}
1488+
1489+/* END CODE */