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