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