* The "ed" built-in command (much simplified)
*/
+//config:config ED
+//config: bool "ed"
+//config: default y
+//config: help
+//config: The original 1970's Unix text editor, from the days of teletypes.
+//config: Small, simple, evil. Part of SUSv3. If you're not already using
+//config: this, you don't need it.
+
+//kbuild:lib-$(CONFIG_ED) += ed.o
+
+//applet:IF_ED(APPLET(ed, BB_DIR_BIN, BB_SUID_DROP))
+
+//usage:#define ed_trivial_usage ""
+//usage:#define ed_full_usage ""
+
#include "libbb.h"
typedef struct LINE {
#define lines (G.lines )
#define marks (G.marks )
#define INIT_G() do { \
- PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
+ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
} while (0)
static char *skip_blank(const char *cp)
{
-// NB: fix comment in skip_whitespace!
while (isblank(*cp))
cp++;
return (char *)cp;
}
-int ed_main(int argc, char **argv);
-int ed_main(int argc, char **argv)
+int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ed_main(int argc UNUSED_PARAM, char **argv)
{
INIT_G();
lines.next = &lines;
lines.prev = &lines;
- if (argc > 1) {
+ if (argv[1]) {
fileName = xstrdup(argv[1]);
if (!readLines(fileName, 1)) {
return EXIT_SUCCESS;
static void doCommands(void)
{
const char *cp;
- char *endbuf, *newname, buf[USERSIZE];
+ char *endbuf, buf[USERSIZE];
int len, num1, num2;
smallint have1, have2;
while (TRUE) {
-// NB: fix comment in lineedit.c!
/* Returns:
* -1 on read errors or EOF, or on bare Ctrl-D.
* 0 on ctrl-C,
* >0 length of input string, including terminating '\n'
*/
- len = read_line_input(": ", buf, sizeof(buf), NULL);
+ len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1);
if (len <= 0)
return;
endbuf = &buf[len - 1];
num2 = num1;
switch (*cp++) {
- case 'a':
- addLines(num1 + 1);
- break;
+ case 'a':
+ addLines(num1 + 1);
+ break;
- case 'c':
- deleteLines(num1, num2);
- addLines(num1);
- break;
+ case 'c':
+ deleteLines(num1, num2);
+ addLines(num1);
+ break;
- case 'd':
- deleteLines(num1, num2);
- break;
+ case 'd':
+ deleteLines(num1, num2);
+ break;
- case 'f':
- if (*cp && !isblank(*cp)) {
- bb_error_msg("bad file command");
- break;
- }
- cp = skip_blank(cp);
- if (*cp == '\0') {
- if (fileName)
- printf("\"%s\"\n", fileName);
- else
- printf("No file name\n");
- break;
- }
- newname = strdup(cp);
- if (newname == NULL) {
- bb_error_msg("no memory for file name");
- break;
- }
- free(fileName);
- fileName = newname;
+ case 'f':
+ if (*cp && !isblank(*cp)) {
+ bb_error_msg("bad file command");
break;
-
- case 'i':
- addLines(num1);
+ }
+ cp = skip_blank(cp);
+ if (*cp == '\0') {
+ if (fileName)
+ printf("\"%s\"\n", fileName);
+ else
+ printf("No file name\n");
break;
+ }
+ free(fileName);
+ fileName = xstrdup(cp);
+ break;
- case 'k':
- cp = skip_blank(cp);
- if ((*cp < 'a') || (*cp > 'z') || cp[1]) {
- bb_error_msg("bad mark name");
- break;
- }
- marks[*cp - 'a'] = num2;
- break;
+ case 'i':
+ addLines(num1);
+ break;
- case 'l':
- printLines(num1, num2, TRUE);
+ case 'k':
+ cp = skip_blank(cp);
+ if ((*cp < 'a') || (*cp > 'z') || cp[1]) {
+ bb_error_msg("bad mark name");
break;
+ }
+ marks[*cp - 'a'] = num2;
+ break;
- case 'p':
- printLines(num1, num2, FALSE);
- break;
+ case 'l':
+ printLines(num1, num2, TRUE);
+ break;
- case 'q':
- cp = skip_blank(cp);
- if (have1 || *cp) {
- bb_error_msg("bad quit command");
- break;
- }
- if (!dirty)
- return;
- len = read_line_input("Really quit? ", buf, 16, NULL);
- /* read error/EOF - no way to continue */
- if (len < 0)
- return;
- cp = skip_blank(buf);
- if ((*cp | 0x20) == 'y') /* Y or y */
- return;
- break;
+ case 'p':
+ printLines(num1, num2, FALSE);
+ break;
- case 'r':
- if (*cp && !isblank(*cp)) {
- bb_error_msg("bad read command");
- break;
- }
- cp = skip_blank(cp);
- if (*cp == '\0') {
- bb_error_msg("no file name");
- break;
- }
- if (!have1)
- num1 = lastNum;
- if (readLines(cp, num1 + 1))
- break;
- if (fileName == NULL)
- fileName = strdup(cp);
+ case 'q':
+ cp = skip_blank(cp);
+ if (have1 || *cp) {
+ bb_error_msg("bad quit command");
break;
+ }
+ if (!dirty)
+ return;
+ len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1);
+ /* read error/EOF - no way to continue */
+ if (len < 0)
+ return;
+ cp = skip_blank(buf);
+ if ((*cp | 0x20) == 'y') /* Y or y */
+ return;
+ break;
- case 's':
- subCommand(cp, num1, num2);
+ case 'r':
+ if (*cp && !isblank(*cp)) {
+ bb_error_msg("bad read command");
break;
-
- case 'w':
- if (*cp && !isblank(*cp)) {
- bb_error_msg("bad write command");
- break;
- }
- cp = skip_blank(cp);
- if (!have1) {
- num1 = 1;
- num2 = lastNum;
- }
- if (*cp == '\0')
- cp = fileName;
- if (cp == NULL) {
- bb_error_msg("no file name specified");
- break;
- }
- writeLines(cp, num1, num2);
+ }
+ cp = skip_blank(cp);
+ if (*cp == '\0') {
+ bb_error_msg("no file name");
break;
-
- case 'z':
- switch (*cp) {
- case '-':
- printLines(curNum - 21, curNum, FALSE);
- break;
- case '.':
- printLines(curNum - 11, curNum + 10, FALSE);
- break;
- default:
- printLines(curNum, curNum + 21, FALSE);
- break;
- }
+ }
+ if (!have1)
+ num1 = lastNum;
+ if (readLines(cp, num1 + 1))
break;
+ if (fileName == NULL)
+ fileName = xstrdup(cp);
+ break;
- case '.':
- if (have1) {
- bb_error_msg("no arguments allowed");
- break;
- }
- printLines(curNum, curNum, FALSE);
+ case 's':
+ subCommand(cp, num1, num2);
+ break;
+
+ case 'w':
+ if (*cp && !isblank(*cp)) {
+ bb_error_msg("bad write command");
break;
+ }
+ cp = skip_blank(cp);
+ if (!have1) {
+ num1 = 1;
+ num2 = lastNum;
+ }
+ if (*cp == '\0')
+ cp = fileName;
+ if (cp == NULL) {
+ bb_error_msg("no file name specified");
+ break;
+ }
+ writeLines(cp, num1, num2);
+ break;
+ case 'z':
+ switch (*cp) {
case '-':
- if (setCurNum(curNum - 1))
- printLines(curNum, curNum, FALSE);
+ printLines(curNum - 21, curNum, FALSE);
break;
-
- case '=':
- printf("%d\n", num1);
+ case '.':
+ printLines(curNum - 11, curNum + 10, FALSE);
break;
- case '\0':
- if (have1) {
- printLines(num2, num2, FALSE);
- break;
- }
- if (setCurNum(curNum + 1))
- printLines(curNum, curNum, FALSE);
+ default:
+ printLines(curNum, curNum + 21, FALSE);
break;
+ }
+ break;
- default:
- bb_error_msg("unimplemented command");
+ case '.':
+ if (have1) {
+ bb_error_msg("no arguments allowed");
+ break;
+ }
+ printLines(curNum, curNum, FALSE);
+ break;
+
+ case '-':
+ if (setCurNum(curNum - 1))
+ printLines(curNum, curNum, FALSE);
+ break;
+
+ case '=':
+ printf("%d\n", num1);
+ break;
+ case '\0':
+ if (have1) {
+ printLines(num2, num2, FALSE);
break;
+ }
+ if (setCurNum(curNum + 1))
+ printLines(curNum, curNum, FALSE);
+ break;
+
+ default:
+ bb_error_msg("unimplemented command");
+ break;
}
}
}
/*
* The new string is larger, so allocate a new line
- * structure and use that. Link it in in place of
+ * structure and use that. Link it in place of
* the old line structure.
*/
- nlp = malloc(sizeof(LINE) + lp->len + deltaLen);
- if (nlp == NULL) {
- bb_error_msg("cannot get memory for line");
- return;
- }
+ nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen);
nlp->len = lp->len + deltaLen;
* 0 on ctrl-C,
* >0 length of input string, including terminating '\n'
*/
- len = read_line_input("", buf, sizeof(buf), NULL);
+ len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1);
if (len <= 0) {
/* Previously, ctrl-C was exiting to shell.
* Now we exit to ed prompt. Is in important? */
fd = open(file, 0);
if (fd < 0) {
- perror(file);
+ bb_simple_perror_msg(file);
return FALSE;
}
cc = 0;
printf("\"%s\", ", file);
- fflush(stdout);
+ fflush_all();
do {
cp = memchr(bufPtr, '\n', bufUsed);
if (bufUsed >= bufSize) {
len = (bufSize * 3) / 2;
- cp = realloc(bufBase, len);
- if (cp == NULL) {
- bb_error_msg("no memory for buffer");
- close(fd);
- return FALSE;
- }
+ cp = xrealloc(bufBase, len);
bufBase = cp;
bufPtr = bufBase + bufUsed;
bufSize = len;
} while (cc > 0);
if (cc < 0) {
- perror(file);
+ bb_simple_perror_msg(file);
close(fd);
return FALSE;
}
fd = creat(file, 0666);
if (fd < 0) {
- perror(file);
+ bb_simple_perror_msg(file);
return FALSE;
}
printf("\"%s\", ", file);
- fflush(stdout);
+ fflush_all();
lp = findLine(num1);
if (lp == NULL) {
while (num1++ <= num2) {
if (full_write(fd, lp->data, lp->len) != lp->len) {
- perror(file);
+ bb_simple_perror_msg(file);
close(fd);
return FALSE;
}
}
if (close(fd) < 0) {
- perror(file);
+ bb_simple_perror_msg(file);
return FALSE;
}
while (num1 <= num2) {
if (!expandFlag) {
- write(1, lp->data, lp->len);
+ write(STDOUT_FILENO, lp->data, lp->len);
setCurNum(num1++);
lp = lp->next;
continue;
count--;
while (count-- > 0) {
- ch = *cp++;
- if (ch & 0x80) {
- fputs("M-", stdout);
- ch &= 0x7f;
- }
- if (ch < ' ') {
- fputc('^', stdout);
- ch += '@';
- }
- if (ch == 0x7f) {
- fputc('^', stdout);
- ch = '?';
- }
- fputc(ch, stdout);
+ ch = (unsigned char) *cp++;
+ fputc_printable(ch | PRINTABLE_META, stdout);
}
fputs("$\n", stdout);
return FALSE;
}
- newLp = malloc(sizeof(LINE) + len - 1);
- if (newLp == NULL) {
- bb_error_msg("failed to allocate memory for line");
- return FALSE;
- }
+ newLp = xmalloc(sizeof(LINE) + len - 1);
memcpy(newLp->data, data, len);
newLp->len = len;
* Returns the line number which matches, or 0 if there was no match
* with an error printed.
*/
-static int searchLines(const char *str, int num1, int num2)
+static NOINLINE int searchLines(const char *str, int num1, int num2)
{
const LINE *lp;
int len;
lp = lp->next;
}
- bb_error_msg("cannot find string \"%s\"", str);
+ bb_error_msg("can't find string \"%s\"", str);
return 0;
}