diff options
-rw-r--r-- | editors/ed.c | 440 |
1 files changed, 135 insertions, 305 deletions
diff --git a/editors/ed.c b/editors/ed.c index e6576b4..f5b9c39 100644 --- a/editors/ed.c +++ b/editors/ed.c @@ -9,6 +9,14 @@ #include "libbb.h" +typedef struct LINE { + struct LINE *next; + struct LINE *prev; + int len; + char data[1]; +} LINE; + + #define searchString bb_common_bufsiz1 enum { @@ -17,64 +25,92 @@ enum { INITBUF_SIZE = 1024, /* initial buffer size */ }; -typedef struct LINE { - struct LINE *next; - struct LINE *prev; - int len; - char data[1]; -} LINE; +struct globals { + int curNum; + int lastNum; + int bufUsed; + int bufSize; + LINE *curLine; + char *bufBase; + char *bufPtr; + char *fileName; + LINE lines; + smallint dirty; + int marks[26]; +}; +#define G (*ptr_to_globals) +#define curLine (G.curLine ) +#define bufBase (G.bufBase ) +#define bufPtr (G.bufPtr ) +#define fileName (G.fileName ) +#define curNum (G.curNum ) +#define lastNum (G.lastNum ) +#define bufUsed (G.bufUsed ) +#define bufSize (G.bufSize ) +#define dirty (G.dirty ) +#define lines (G.lines ) +#define marks (G.marks ) +#define INIT_G() do { \ + PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ +} while (0) -static LINE lines, *curLine; -static int curNum, lastNum, marks[26], dirty; -static char *bufBase, *bufPtr, *fileName; -static int bufUsed, bufSize; static void doCommands(void); static void subCommand(const char *cmd, int num1, int num2); -static int getNum(const char **retcp, int *retHaveNum, int *retNum); +static int getNum(const char **retcp, smallint *retHaveNum, int *retNum); static int setCurNum(int num); -static int initEdit(void); -static void termEdit(void); static void addLines(int num); static int insertLine(int num, const char *data, int len); -static int deleteLines(int num1, int num2); +static void deleteLines(int num1, int num2); static int printLines(int num1, int num2, int expandFlag); static int writeLines(const char *file, int num1, int num2); static int readLines(const char *file, int num); static int searchLines(const char *str, int num1, int num2); static LINE *findLine(int num); - static int findString(const LINE *lp, const char * str, int len, int offset); + +static int bad_nums(int num1, int num2, const char *for_what) +{ + if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { + bb_error_msg("bad line range for %s", for_what); + return 1; + } + return 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) { - if (!initEdit()) - return EXIT_FAILURE; - - if (argc > 1) { - fileName = strdup(argv[1]); + INIT_G(); - if (fileName == NULL) { - bb_error_msg("no memory"); - termEdit(); - return EXIT_SUCCESS; - } + bufSize = INITBUF_SIZE; + bufBase = xmalloc(bufSize); + bufPtr = bufBase; + lines.next = &lines; + lines.prev = &lines; + if (argc > 1) { + fileName = xstrdup(argv[1]); if (!readLines(fileName, 1)) { - termEdit(); return EXIT_SUCCESS; } - if (lastNum) setCurNum(1); - dirty = FALSE; } doCommands(); - - termEdit(); return EXIT_SUCCESS; } @@ -85,42 +121,25 @@ static void doCommands(void) { const char *cp; char *endbuf, *newname, buf[USERSIZE]; - int len, num1, num2, have1, have2; + int len, num1, num2; + smallint have1, have2; while (TRUE) { - printf(": "); - fflush(stdout); - - if (fgets(buf, sizeof(buf), stdin) == NULL) - return; - - len = strlen(buf); - - if (len == 0) +// 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); + if (len <= 0) return; - endbuf = &buf[len - 1]; - - if (*endbuf != '\n') { - bb_error_msg("command line too long"); - - do { - len = fgetc(stdin); - } while ((len != EOF) && (len != '\n')); - - continue; - } - while ((endbuf > buf) && isblank(endbuf[-1])) endbuf--; - *endbuf = '\0'; - cp = buf; - - while (isblank(*cp)) - cp++; - + cp = skip_blank(buf); have1 = FALSE; have2 = FALSE; @@ -132,28 +151,21 @@ static void doCommands(void) if (!getNum(&cp, &have1, &num1)) continue; - while (isblank(*cp)) - cp++; + cp = skip_blank(cp); if (*cp == ',') { cp++; - if (!getNum(&cp, &have2, &num2)) continue; - if (!have1) num1 = 1; - if (!have2) num2 = lastNum; - have1 = TRUE; have2 = TRUE; } - if (!have1) num1 = curNum; - if (!have2) num2 = num1; @@ -176,10 +188,7 @@ static void doCommands(void) bb_error_msg("bad file command"); break; } - - while (isblank(*cp)) - cp++; - + cp = skip_blank(cp); if (*cp == '\0') { if (fileName) printf("\"%s\"\n", fileName); @@ -187,17 +196,12 @@ static void doCommands(void) printf("No file name\n"); break; } - newname = strdup(cp); - if (newname == NULL) { bb_error_msg("no memory for file name"); break; } - - if (fileName) - free(fileName); - + free(fileName); fileName = newname; break; @@ -206,14 +210,11 @@ static void doCommands(void) break; case 'k': - while (isblank(*cp)) - cp++; - - if ((*cp < 'a') || (*cp > 'a') || cp[1]) { + cp = skip_blank(cp); + if ((*cp < 'a') || (*cp > 'z') || cp[1]) { bb_error_msg("bad mark name"); break; } - marks[*cp - 'a'] = num2; break; @@ -226,30 +227,20 @@ static void doCommands(void) break; case 'q': - while (isblank(*cp)) - cp++; - + cp = skip_blank(cp); if (have1 || *cp) { bb_error_msg("bad quit command"); break; } - if (!dirty) return; - - printf("Really quit? "); - fflush(stdout); - - buf[0] = '\0'; - fgets(buf, sizeof(buf), stdin); - cp = buf; - - while (isblank(*cp)) - cp++; - - if ((*cp == 'y') || (*cp == 'Y')) + 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 'r': @@ -257,24 +248,17 @@ static void doCommands(void) bb_error_msg("bad read command"); break; } - - while (isblank(*cp)) - cp++; - + 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); - break; case 's': @@ -286,36 +270,30 @@ static void doCommands(void) bb_error_msg("bad write command"); break; } - - while (isblank(*cp)) - cp++; - + 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 '-': - printLines(curNum-21, curNum, FALSE); + printLines(curNum - 21, curNum, FALSE); break; case '.': - printLines(curNum-11, curNum+10, FALSE); + printLines(curNum - 11, curNum + 10, FALSE); break; default: - printLines(curNum, curNum+21, FALSE); + printLines(curNum, curNum + 21, FALSE); break; } break; @@ -325,29 +303,24 @@ static void doCommands(void) 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: @@ -362,17 +335,15 @@ static void doCommands(void) * Do the substitute command. * The current line is set to the last substitution done. */ -static void subCommand(const char * cmd, int num1, int num2) +static void subCommand(const char *cmd, int num1, int num2) { char *cp, *oldStr, *newStr, buf[USERSIZE]; int delim, oldLen, newLen, deltaLen, offset; LINE *lp, *nlp; int globalFlag, printFlag, didSub, needPrint; - if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { - bb_error_msg("bad line range for substitute"); + if (bad_nums(num1, num2, "substitute")) return; - } globalFlag = FALSE; printFlag = FALSE; @@ -394,7 +365,6 @@ static void subCommand(const char * cmd, int num1, int num2) oldStr = cp; cp = strchr(cp, delim); - if (cp == NULL) { bb_error_msg("missing 2nd delimiter for substitute"); return; @@ -414,11 +384,9 @@ static void subCommand(const char * cmd, int num1, int num2) case 'g': globalFlag = TRUE; break; - case 'p': printFlag = TRUE; break; - default: bb_error_msg("unknown option for substitute"); return; @@ -429,7 +397,6 @@ static void subCommand(const char * cmd, int num1, int num2) bb_error_msg("no previous search string"); return; } - oldStr = searchString; } @@ -437,7 +404,6 @@ static void subCommand(const char * cmd, int num1, int num2) strcpy(searchString, oldStr); lp = findLine(num1); - if (lp == NULL) return; @@ -455,11 +421,9 @@ static void subCommand(const char * cmd, int num1, int num2) printLines(num1, num1, FALSE); needPrint = FALSE; } - offset = 0; lp = lp->next; num1++; - continue; } @@ -473,7 +437,6 @@ static void subCommand(const char * cmd, int num1, int num2) */ if (deltaLen <= 0) { memcpy(&lp->data[offset], newStr, newLen); - if (deltaLen) { memcpy(&lp->data[offset + newLen], &lp->data[offset + oldLen], @@ -481,20 +444,15 @@ static void subCommand(const char * cmd, int num1, int num2) lp->len += deltaLen; } - offset += newLen; - if (globalFlag) continue; - if (needPrint) { printLines(num1, num1, FALSE); needPrint = FALSE; } - lp = lp->next; num1++; - continue; } @@ -503,8 +461,7 @@ static void subCommand(const char * cmd, int num1, int num2) * structure and use that. Link it in in place of * the old line structure. */ - nlp = (LINE *) malloc(sizeof(LINE) + lp->len + deltaLen); - + nlp = malloc(sizeof(LINE) + lp->len + deltaLen); if (nlp == NULL) { bb_error_msg("cannot get memory for line"); return; @@ -513,9 +470,7 @@ static void subCommand(const char * cmd, int num1, int num2) nlp->len = lp->len + deltaLen; memcpy(nlp->data, lp->data, offset); - memcpy(&nlp->data[offset], newStr, newLen); - memcpy(&nlp->data[offset + newLen], &lp->data[offset + oldLen], lp->len - offset - oldLen); @@ -554,7 +509,7 @@ static void subCommand(const char * cmd, int num1, int num2) * Search a line for the specified string starting at the specified * offset in the line. Returns the offset of the found string, or -1. */ -static int findString( const LINE * lp, const char * str, int len, int offset) +static int findString(const LINE *lp, const char *str, int len, int offset) { int left; const char *cp, *ncp; @@ -564,20 +519,14 @@ static int findString( const LINE * lp, const char * str, int len, int offset) while (left >= len) { ncp = memchr(cp, *str, left); - if (ncp == NULL) return -1; - left -= (ncp - cp); - if (left < len) return -1; - cp = ncp; - if (memcmp(cp, str, len) == 0) return (cp - lp->data); - cp++; left--; } @@ -597,23 +546,20 @@ static void addLines(int num) int len; char buf[USERSIZE + 1]; - while (fgets(buf, sizeof(buf), stdin)) { - if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) - return; - - len = strlen(buf); - - if (len == 0) - return; - - if (buf[len - 1] != '\n') { - bb_error_msg("line too long"); - do { - len = fgetc(stdin); - } while ((len != EOF) && (len != '\n')); + while (1) { + /* 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); + if (len <= 0) { + /* Previously, ctrl-C was exiting to shell. + * Now we exit to ed prompt. Is in important? */ return; } - + if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0')) + return; if (!insertLine(num++, buf, len)) return; } @@ -628,20 +574,20 @@ static void addLines(int num) * Whether there was a number is returned indirectly, as is the number. * The character pointer which stopped the scan is also returned. */ -static int getNum(const char **retcp, int *retHaveNum, int *retNum) +static int getNum(const char **retcp, smallint *retHaveNum, int *retNum) { const char *cp; char *endStr, str[USERSIZE]; - int haveNum, value, num, sign; + int value, num; + smallint haveNum, minus; cp = *retcp; - haveNum = FALSE; value = 0; - sign = 1; + haveNum = FALSE; + minus = 0; while (TRUE) { - while (isblank(*cp)) - cp++; + cp = skip_blank(cp); switch (*cp) { case '.': @@ -658,12 +604,10 @@ static int getNum(const char **retcp, int *retHaveNum, int *retNum) case '\'': cp++; - if ((*cp < 'a') || (*cp > 'z')) { bb_error_msg("bad mark name"); return FALSE; } - haveNum = TRUE; num = marks[*cp++ - 'a']; break; @@ -671,19 +615,14 @@ static int getNum(const char **retcp, int *retHaveNum, int *retNum) case '/': strcpy(str, ++cp); endStr = strchr(str, '/'); - if (endStr) { *endStr++ = '\0'; cp += (endStr - str); - } - else + } else cp = ""; - num = searchLines(str, curNum, lastNum); - if (num == 0) return FALSE; - haveNum = TRUE; break; @@ -694,29 +633,25 @@ static int getNum(const char **retcp, int *retHaveNum, int *retNum) *retNum = value; return TRUE; } - num = 0; - while (isdigit(*cp)) num = num * 10 + *cp++ - '0'; - haveNum = TRUE; break; } - value += num * sign; + value += (minus ? -num : num); - while (isblank(*cp)) - cp++; + cp = skip_blank(cp); switch (*cp) { case '-': - sign = -1; + minus = 1; cp++; break; case '+': - sign = 1; + minus = 0; cp++; break; @@ -731,74 +666,10 @@ static int getNum(const char **retcp, int *retHaveNum, int *retNum) /* - * Initialize everything for editing. - */ -static int initEdit(void) -{ - int i; - - bufSize = INITBUF_SIZE; - bufBase = malloc(bufSize); - - if (bufBase == NULL) { - bb_error_msg("no memory for buffer"); - return FALSE; - } - - bufPtr = bufBase; - bufUsed = 0; - - lines.next = &lines; - lines.prev = &lines; - - curLine = NULL; - curNum = 0; - lastNum = 0; - dirty = FALSE; - fileName = NULL; - searchString[0] = '\0'; - - for (i = 0; i < 26; i++) - marks[i] = 0; - - return TRUE; -} - - -/* - * Finish editing. - */ -static void termEdit(void) -{ - if (bufBase) - free(bufBase); - - bufBase = NULL; - bufPtr = NULL; - bufSize = 0; - bufUsed = 0; - - if (fileName) - free(fileName); - - fileName = NULL; - - searchString[0] = '\0'; - - if (lastNum) - deleteLines(1, lastNum); - - lastNum = 0; - curNum = 0; - curLine = NULL; -} - - -/* * Read lines from a file at the specified line number. * Returns TRUE if the file was successfully read. */ -static int readLines(const char * file, int num) +static int readLines(const char *file, int num) { int fd, cc; int len, lineCount, charCount; @@ -810,7 +681,6 @@ static int readLines(const char * file, int num) } fd = open(file, 0); - if (fd < 0) { perror(file); return FALSE; @@ -830,18 +700,15 @@ static int readLines(const char * file, int num) if (cp) { len = (cp - bufPtr) + 1; - if (!insertLine(num, bufPtr, len)) { close(fd); return FALSE; } - bufPtr += len; bufUsed -= len; charCount += len; lineCount++; num++; - continue; } @@ -853,19 +720,17 @@ static int readLines(const char * file, int num) 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; } - bufBase = cp; bufPtr = bufBase + bufUsed; bufSize = len; } - cc = read(fd, bufPtr, bufSize - bufUsed); + cc = safe_read(fd, bufPtr, bufSize - bufUsed); bufUsed += cc; bufPtr = bufBase; @@ -882,7 +747,6 @@ static int readLines(const char * file, int num) close(fd); return -1; } - lineCount++; charCount += bufUsed; } @@ -900,21 +764,18 @@ static int readLines(const char * file, int num) * Write the specified lines out to the specified file. * Returns TRUE if successful, or FALSE on an error with a message output. */ -static int writeLines(const char * file, int num1, int num2) +static int writeLines(const char *file, int num1, int num2) { LINE *lp; int fd, lineCount, charCount; - if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { - bb_error_msg("bad line range for write"); + if (bad_nums(num1, num2, "write")) return FALSE; - } lineCount = 0; charCount = 0; fd = creat(file, 0666); - if (fd < 0) { perror(file); return FALSE; @@ -924,19 +785,17 @@ static int writeLines(const char * file, int num1, int num2) fflush(stdout); lp = findLine(num1); - if (lp == NULL) { close(fd); return FALSE; } while (num1++ <= num2) { - if (write(fd, lp->data, lp->len) != lp->len) { + if (full_write(fd, lp->data, lp->len) != lp->len) { perror(file); close(fd); return FALSE; } - charCount += lp->len; lineCount++; lp = lp->next; @@ -964,13 +823,10 @@ static int printLines(int num1, int num2, int expandFlag) const char *cp; int ch, count; - if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { - bb_error_msg("bad line range for print"); + if (bad_nums(num1, num2, "print")) return FALSE; - } lp = findLine(num1); - if (lp == NULL) return FALSE; @@ -979,7 +835,6 @@ static int printLines(int num1, int num2, int expandFlag) write(1, lp->data, lp->len); setCurNum(num1++); lp = lp->next; - continue; } @@ -995,22 +850,18 @@ static int printLines(int num1, int num2, int expandFlag) 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); } @@ -1031,7 +882,7 @@ static int printLines(int num1, int num2, int expandFlag) * The inserted line is also set to become the current line. * Returns TRUE if successful. */ -static int insertLine(int num, const char * data, int len) +static int insertLine(int num, const char *data, int len) { LINE *newLp, *lp; @@ -1041,7 +892,6 @@ static int insertLine(int num, const char * data, int len) } newLp = malloc(sizeof(LINE) + len - 1); - if (newLp == NULL) { bb_error_msg("failed to allocate memory for line"); return FALSE; @@ -1054,7 +904,6 @@ static int insertLine(int num, const char * data, int len) lp = &lines; else { lp = findLine(num); - if (lp == NULL) { free((char *) newLp); return FALSE; @@ -1075,20 +924,17 @@ static int insertLine(int num, const char * data, int len) /* * Delete lines from the given range. */ -static int deleteLines(int num1, int num2) +static void deleteLines(int num1, int num2) { LINE *lp, *nlp, *plp; int count; - if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { - bb_error_msg("bad line numbers for delete"); - return FALSE; - } + if (bad_nums(num1, num2, "delete")) + return; lp = findLine(num1); - if (lp == NULL) - return FALSE; + return; if ((curNum >= num1) && (curNum <= num2)) { if (num2 < lastNum) @@ -1100,10 +946,8 @@ static int deleteLines(int num1, int num2) } count = num2 - num1 + 1; - if (curNum > num2) curNum -= count; - lastNum -= count; while (count-- > 0) { @@ -1111,22 +955,17 @@ static int deleteLines(int num1, int num2) plp = lp->prev; plp->next = nlp; nlp->prev = plp; - lp->next = NULL; - lp->prev = NULL; - lp->len = 0; free(lp); lp = nlp; } dirty = TRUE; - - return TRUE; } /* * Search for a line which contains the specified string. - * If the string is NULL, then the previously searched for string + * If the string is "", then the previously searched for string * is used. The currently searched for string is saved for future use. * Returns the line number which matches, or 0 if there was no match * with an error printed. @@ -1136,17 +975,14 @@ static int searchLines(const char *str, int num1, int num2) const LINE *lp; int len; - if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) { - bb_error_msg("bad line numbers for search"); + if (bad_nums(num1, num2, "search")) return 0; - } if (*str == '\0') { if (searchString[0] == '\0') { bb_error_msg("no previous search string"); return 0; } - str = searchString; } @@ -1156,14 +992,12 @@ static int searchLines(const char *str, int num1, int num2) len = strlen(str); lp = findLine(num1); - if (lp == NULL) return 0; while (num1 <= num2) { if (findString(lp, str, len, 0) >= 0) return num1; - num1++; lp = lp->next; } @@ -1196,12 +1030,10 @@ static LINE *findLine(int num) lp = curLine; lnum = curNum; - if (num < (curNum / 2)) { lp = lines.next; lnum = 1; - } - else if (num > ((curNum + lastNum) / 2)) { + } else if (num > ((curNum + lastNum) / 2)) { lp = lines.prev; lnum = lastNum; } @@ -1228,10 +1060,8 @@ static int setCurNum(int num) LINE *lp; lp = findLine(num); - if (lp == NULL) return FALSE; - curNum = num; curLine = lp; return TRUE; |