Contents of /trunk/mkinitrd-magellan/busybox/editors/ed.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 20099 byte(s)
Sat Sep 1 22:45:15 2007 UTC (17 years 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 | /* 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 | } |