source: MondoRescue/branches/3.3/mindi-busybox/editors/ed.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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