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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 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
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#include "libbb.h"
11
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
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;
29static char *bufBase, *bufPtr, *fileName;
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
49int ed_main(int argc, char **argv);
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) {
59            bb_error_msg("no memory");
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
90    while (TRUE) {
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
104        if (*endbuf != '\n') {
105            bb_error_msg("command line too long");
106
107            do {
108                len = fgetc(stdin);
109            } while ((len != EOF) && (len != '\n'));
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
127        if ((curNum == 0) && (lastNum > 0)) {
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
138        if (*cp == ',') {
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
160        switch (*cp++) {
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':
175                if (*cp && !isblank(*cp)) {
176                    bb_error_msg("bad file command");
177                    break;
178                }
179
180                while (isblank(*cp))
181                    cp++;
182
183                if (*cp == '\0') {
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
193                if (newname == NULL) {
194                    bb_error_msg("no memory for file name");
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
212                if ((*cp < 'a') || (*cp > 'a') || cp[1]) {
213                    bb_error_msg("bad mark name");
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
232                if (have1 || *cp) {
233                    bb_error_msg("bad quit command");
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':
256                if (*cp && !isblank(*cp)) {
257                    bb_error_msg("bad read command");
258                    break;
259                }
260
261                while (isblank(*cp))
262                    cp++;
263
264                if (*cp == '\0') {
265                    bb_error_msg("no file name");
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':
285                if (*cp && !isblank(*cp)) {
286                    bb_error_msg("bad write command");
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
301                if (cp == NULL) {
302                    bb_error_msg("no file name specified");
303                    break;
304                }
305
306                writeLines(cp, num1, num2);
307                break;
308
309            case 'z':
310                switch (*cp) {
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 '.':
324                if (have1) {
325                    bb_error_msg("no arguments allowed");
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':
343                if (have1) {
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:
354                bb_error_msg("unimplemented command");
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];
368    int delim, oldLen, newLen, deltaLen, offset;
369    LINE *lp, *nlp;
370    int globalFlag, printFlag, didSub, needPrint;
371
372    if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
373        bb_error_msg("bad line range for substitute");
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
388    if (isblank(*cp) || (*cp == '\0')) {
389        bb_error_msg("bad delimiter for substitute");
390        return;
391    }
392
393    delim = *cp++;
394    oldStr = cp;
395
396    cp = strchr(cp, delim);
397
398    if (cp == NULL) {
399        bb_error_msg("missing 2nd delimiter for substitute");
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
411        cp = (char*)"";
412
413    while (*cp) switch (*cp++) {
414        case 'g':
415            globalFlag = TRUE;
416            break;
417
418        case 'p':
419            printFlag = TRUE;
420            break;
421
422        default:
423            bb_error_msg("unknown option for substitute");
424            return;
425    }
426
427    if (*oldStr == '\0') {
428        if (searchString[0] == '\0') {
429            bb_error_msg("no previous search string");
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
450    while (num1 <= num2) {
451        offset = findString(lp, oldStr, oldLen, offset);
452
453        if (offset < 0) {
454            if (needPrint) {
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         */
474        if (deltaLen <= 0) {
475            memcpy(&lp->data[offset], newStr, newLen);
476
477            if (deltaLen) {
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
490            if (needPrint) {
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
508        if (nlp == NULL) {
509            bb_error_msg("cannot get memory for line");
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
539        if (needPrint) {
540            printLines(num1, num1, FALSE);
541            needPrint = FALSE;
542        }
543
544        lp = lp->next;
545        num1++;
546    }
547
548    if (!didSub)
549        bb_error_msg("no substitutions found for \"%s\"", oldStr);
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
565    while (left >= len) {
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{
597    int len;
598    char buf[USERSIZE + 1];
599
600    while (fgets(buf, sizeof(buf), stdin)) {
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
609        if (buf[len - 1] != '\n') {
610            bb_error_msg("line too long");
611            do {
612                len = fgetc(stdin);
613            } while ((len != EOF) && (len != '\n'));
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
642    while (TRUE) {
643        while (isblank(*cp))
644            cp++;
645
646        switch (*cp) {
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
662                if ((*cp < 'a') || (*cp > 'z')) {
663                    bb_error_msg("bad mark name");
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
675                if (endStr) {
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:
691                if (!isdigit(*cp)) {
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
712        switch (*cp) {
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{
738    int i;
739
740    bufSize = INITBUF_SIZE;
741    bufBase = malloc(bufSize);
742
743    if (bufBase == NULL) {
744        bb_error_msg("no memory for buffer");
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{
803    int fd, cc;
804    int len, lineCount, charCount;
805    char *cp;
806
807    if ((num < 1) || (num > lastNum + 1)) {
808        bb_error_msg("bad line for read");
809        return FALSE;
810    }
811
812    fd = open(file, 0);
813
814    if (fd < 0) {
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
828    do {
829        cp = memchr(bufPtr, '\n', bufUsed);
830
831        if (cp) {
832            len = (cp - bufPtr) + 1;
833
834            if (!insertLine(num, bufPtr, len)) {
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
848        if (bufPtr != bufBase) {
849            memcpy(bufBase, bufPtr, bufUsed);
850            bufPtr = bufBase + bufUsed;
851        }
852
853        if (bufUsed >= bufSize) {
854            len = (bufSize * 3) / 2;
855            cp = realloc(bufBase, len);
856
857            if (cp == NULL) {
858                bb_error_msg("no memory for buffer");
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
872    } while (cc > 0);
873
874    if (cc < 0) {
875        perror(file);
876        close(fd);
877        return FALSE;
878    }
879
880    if (bufUsed) {
881        if (!insertLine(num, bufPtr, bufUsed)) {
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;
906    int fd, lineCount, charCount;
907
908    if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
909        bb_error_msg("bad line range for write");
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
928    if (lp == NULL) {
929        close(fd);
930        return FALSE;
931    }
932
933    while (num1++ <= num2) {
934        if (write(fd, lp->data, lp->len) != lp->len) {
935            perror(file);
936            close(fd);
937            return FALSE;
938        }
939
940        charCount += lp->len;
941        lineCount++;
942        lp = lp->next;
943    }
944
945    if (close(fd) < 0) {
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
967    if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
968        bb_error_msg("bad line range for print");
969        return FALSE;
970    }
971
972    lp = findLine(num1);
973
974    if (lp == NULL)
975        return FALSE;
976
977    while (num1 <= num2) {
978        if (!expandFlag) {
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
996        while (count-- > 0) {
997            ch = *cp++;
998
999            if (ch & 0x80) {
1000                fputs("M-", stdout);
1001                ch &= 0x7f;
1002            }
1003
1004            if (ch < ' ') {
1005                fputc('^', stdout);
1006                ch += '@';
1007            }
1008
1009            if (ch == 0x7f) {
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
1038    if ((num < 1) || (num > lastNum + 1)) {
1039        bb_error_msg("inserting at bad line number");
1040        return FALSE;
1041    }
1042
1043    newLp = malloc(sizeof(LINE) + len - 1);
1044
1045    if (newLp == NULL) {
1046        bb_error_msg("failed to allocate memory for line");
1047        return FALSE;
1048    }
1049
1050    memcpy(newLp->data, data, len);
1051    newLp->len = len;
1052
1053    if (num > lastNum)
1054        lp = &lines;
1055    else {
1056        lp = findLine(num);
1057
1058        if (lp == NULL) {
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
1083    if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
1084        bb_error_msg("bad line numbers for delete");
1085        return FALSE;
1086    }
1087
1088    lp = findLine(num1);
1089
1090    if (lp == NULL)
1091        return FALSE;
1092
1093    if ((curNum >= num1) && (curNum <= num2)) {
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
1109    while (count-- > 0) {
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
1139    if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
1140        bb_error_msg("bad line numbers for search");
1141        return 0;
1142    }
1143
1144    if (*str == '\0') {
1145        if (searchString[0] == '\0') {
1146            bb_error_msg("no previous search string");
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
1163    while (num1 <= num2) {
1164        if (findString(lp, str, len, 0) >= 0)
1165            return num1;
1166
1167        num1++;
1168        lp = lp->next;
1169    }
1170
1171    bb_error_msg("cannot find string \"%s\"", str);
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
1184    if ((num < 1) || (num > lastNum)) {
1185        bb_error_msg("line number %d does not exist", num);
1186        return NULL;
1187    }
1188
1189    if (curNum <= 0) {
1190        curNum = 1;
1191        curLine = lines.next;
1192    }
1193
1194    if (num == curNum)
1195        return curLine;
1196
1197    lp = curLine;
1198    lnum = curNum;
1199
1200    if (num < (curNum / 2)) {
1201        lp = lines.next;
1202        lnum = 1;
1203    }
1204    else if (num > ((curNum + lastNum) / 2)) {
1205        lp = lines.prev;
1206        lnum = lastNum;
1207    }
1208
1209    while (lnum < num) {
1210        lp = lp->next;
1211        lnum++;
1212    }
1213
1214    while (lnum > num) {
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.