source: MondoRescue/branches/stable/mindi-busybox/editors/ed.c@ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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