Add build options to control SuS compatability, allows numeric
[oweals/busybox.git] / patches / ed.patch
1 Index: 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)
17 Index: 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
34 Index: 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 \
48 Index: 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 */