Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/editors/ed.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (hide annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 20099 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

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