--- ed.1 | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++- ed.c | 250 ++++++++++++++++++++++++++++++++++--------------------------------- 2 files changed, 331 insertions(+), 123 deletions(-) diff --git a/ed.1 b/ed.1 index 93e3012..520ac5f 100644 --- a/ed.1 +++ b/ed.1 _AT_@ -6,4 +6,206 @@ .Nd text editor .Sh SYNOPSIS .Nm -is the standard text editor. +.Op Fl s +.Op Fl p Ar string +.Op Ar file +.Sh DESCRIPTION +.Nm +is the standard text editor. It performs line-oriented operations on a buffer; +The buffer's contents are manipulated in command mode and text is written to the +buffer in input mode. Command mode is the default. To exit input mode enter a +dot ('.') on a line of its own. + +If +.Nm +is invoked with a file as an argument, it will simulate an edit command and read +the file's contents into a buffer. Changes to this buffer are local to +.Nm +until a write command is given. + +.Nm +uses the basic regular expression syntax and allows any character but space and +newline to be used as a delimiter in regular expressions. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl s +Suppress diagnostic messages +.It Fl p Ar string +Use +.Ar string +as a prompt when in command mode +.El +.Sh EXTENDED DESCRIPTION +.Ss Addresses +Commands operate on addresses. Addresses are used to refer to lines +within the buffer. Address ranges may have spaces before and after the separator. +Unless otherwise specified, 0 is an invalid address. The following symbols are +valid addresses: +.Bl -tag -width Ds +.It n +The nth line. +.It . +The current line, or "dot". +.It $ +The last line. +.It + +The next line. +.It +n +The nth next line. +.It ^ or - +The previous line. +.It ^n or -n +The nth previous line. +.It x,y +The range of lines from x to y. The default value of x is 1, and the default +value of y is $. +.It x;y +As above, except that the current line is set to x. Omitting x in this case uses +the current line as the default value. +.It /re/ +The next line matching re. +.It ?re? +The last line matching re. +.It 'c +The line marked by c. See k below. +.El +.Ss Commands. +.Nm +expects to see one command per line, with the following exception: commands may +be suffixed with either a list, number, or print command. These suffixed +commands are run after the command they're suffixed to has executed. + +The following is the list of commands that +.Nm +knows about. The parentheses contain the default addresses that a command uses. +.Bl -tag -width Ds +.It (.)a +Append text after the addressed line. The dot is set to the last line +entered. If no text was entered, the dot is set to the addressed line. An +address of 0 appends to the start of the buffer. +.It (.,.)c +Delete the addressed lines and then accept input to replace them. The dot +is set to the last line entered. If no text was entered, the dot is set to +the line before the deleted lines. +.It (.,.)d +Delete the addressed lines. If there is a line after the deleted range, the +dot is set to it. Otherwise, the dot is set to the line before the deleted range. +.It e Ar file +Delete the contents of the buffer and load in +.Ar file +for editing, printing the bytes read to standard output. If no filename is +given, +.Nm +uses the currently remembered filename. The remembered filename is set to +.Ar file +for later use. +.It E Ar file +As above, but without warning if the current buffer has unsaved changes. +.It f Ar file +Set the currently remembered filename to +.Ar +file +, or print the currently remembered filename if +.Ar +file is omitted. +.It (1,$)g/re/command +Apply command to lines matching re. The dot is set to the matching line before +command is executed. When each matching line has been operated on, the dot is +set to the last line operated on. If no lines match then the dot remains +unchanged. The command used may not be g, G, v, or V. +.It (1,$)G/re/ +Interactively edit the range of line addresses that match re. The dot is set to +the matching line and printed before a command is input. When each matching line +has been operated on, the dot is set to the last line operated on. If no lines +match then the dot remains unchanged. The command used may not be a, c, i, g, +G, v, or V. +.It h +Print the reason for the most recent error. +.It H +Toggle error explanations. If on, the above behaviour is produced on all +subsequent errors. +.It (.)i +Insert text into the buffer before the addressed line. The dot is set to the +last line entered. If no text was entered, the dot is set to the addressed line +.It (.,.+1)j +Join two lines together. If only one address is given, nothing happens. The dot +is set to the newly joined line. +.It (.)kc +Mark the line with the lower case character c. The dot is unchanged. +.It (.,.)l +Unambiguously print the addressed lines. The dot is set to the last line written. +.It (.,.)m(.) +Move lines in the buffer to the line address on the right hand side. An address +of 0 on the right hand side moves to the start of the buffer. The dot is set to +the last line moved. +.It (.,.)n +Print the addressed lines and their numbers. The dot is set to the last line +printed. +.It (.,.)p +Print the addressed lines. The dot is set to the last line printed. +.It P +Toggle the prompt. Defaults to off, but is switched on if the -p flag is used. +.It q +Quit +.Nm +, warning if there are unsaved changes. +.It Q +As above, but without warning if the current buffer has unsaved changes. +.It ($)r Ar file +Read in +.Ar file +and append it to the current buffer, printing the bytes read to standard output. +The currently remembered filename isn't changed unless it's empty. An address of +0 reads the file into the start of the buffer. +.It (.,.)s/re/replacement/flags +Substitute re for replacement in lines matching re. An & within replacement is +replaced with the whole string matched by re. Backrefs can be used with the form +\\n, where n is a positive non-zero integer. When % is the only character in +replacement, it is substituted for the replacement string from the last +substitute command. If a newline is part of replacement then the matched string +is split into two lines; this cannot be done as part of a g or v command. If +flags contains an integer n, then the nth match is replaced. If flags contains +g, all matches are replaced. The dot is set to the last line matched. +.It (.,.)t(.) +As m, but copying instead of moving. The dot is set to the last line added. +.It u +Undo the last change. The dot is set to whatever it was before the undone +command was performed. +.It (1.$)v/re/command +As with g, but operating on lines that don't match re. +.It (1.$)V/re/ +As with G, but operating on lines that don't match re. +.It (1,$)w Ar file +Write the addressed lines to +.Ar file +, overwriting its previous contents if the file exists, and print the number of +bytes written. If no filename is given the currently remembered filename will be +used instead. The dot is unchanged. +.It (1,$)W Ar file +As above, but instead of overwriting the contents of +.Ar file +the addressed lines are appended to +.Ar file +instead. +.It (.+1)\\n +Print the addressed line. Sets the dot to that line. +.It ($)= +Print the line number of the addressed line. The dot is unchanged. +.It & +Repeat the last command. +.It ! Ar command +Execute +.Ar command +using sh. If the first character of +.Ar command +is '!' then it is replaced with the text of the previous command. An unescaped % +is replaced with the currently remembered filename. ! does not process escape +characters. When +.Ar command +returns a '!' is printed. The dot is unchanged. +.El +.Sh BUGS +g and v operate on single commands rather than lists delimited with '\\'. +.Sh SEE ALSO +.Xr sed 1 +.Xr regexp 3 diff --git a/ed.c b/ed.c index 184ed30..496b24f 100644 --- a/ed.c +++ b/ed.c _AT_@ -119,7 +119,8 @@ addchar(char c, char *t, size_t *capacity, size_t *size) if (siz >= cap && (cap > SIZE_MAX - LINESIZE || (t = realloc(t, cap += LINESIZE)) == NULL)) - error("out of memory"); + error("out of memory"); + t[siz++] = c; *size = siz; *capacity = cap; _AT_@ -297,13 +298,17 @@ undo(void) } static void -inject(char *s) +inject(char *s, int join) { int off, k, begin, end; - begin = getindex(curln); - end = getindex(nextln(curln)); - + if (join) { + begin = getindex(curln-1); + end = getindex(nextln(curln-1)); + } else { + begin = getindex(curln); + end = getindex(nextln(curln)); + } while (*s) { k = makeline(s, &off); s += off; _AT_@ -634,7 +639,7 @@ doread(char *fname) s[n-1] = '\n'; s[n] = '\0'; } - inject(s); + inject(s, 0); } if (optdiag) printf("%zu\n", cnt); _AT_@ -751,7 +756,7 @@ append(int num) while (getline(&s, &len, stdin) > 0) { if (*s == '.' && s[1] == '\n') break; - inject(s); + inject(s, 0); } free(s); } _AT_@ -767,7 +772,7 @@ delete(int from, int to) lfrom = getindex(prevln(from)); lto = getindex(nextln(to)); lastln -= to - from + 1; - curln = (from > lastln) ? lastln : from;; + curln = (from > lastln) ? lastln : from; relink(lto, lfrom, lto, lfrom); } _AT_@ -803,18 +808,20 @@ join(void) int i; char *t, c; size_t len = 0, cap = 0; - static char *s; + char *s; - free(s); for (s = NULL, i = line1; i <= line2; i = nextln(i)) { for (t = gettxt(i); (c = *t) != '\n'; ++t) s = addchar(*t, s, &cap, &len); + /* prevent infinite loop when there are only two lines */ + if (i == line2) + break; } s = addchar('\n', s, &cap, &len); s = addchar('\0', s, &cap, &len); delete(line1, line2); - inject(s); + inject(s, 1); free(s); } _AT_@ -841,7 +848,7 @@ copy(int where) curln = where; for (i = line1; i <= line2; ++i) - inject(gettxt(i)); + inject(gettxt(i), 0); } static void _AT_@ -1021,7 +1028,7 @@ subline(int num, int nth) addpost(&s, &cap, &siz); delete(num, num); curln = prevln(num); - inject(s); + inject(s, 0); } static void _AT_@ -1170,9 +1177,8 @@ repeat: case 'j': chkprint(1); deflines(curln, curln+1); - if (!line1) - goto bad_address; - join(); + if (line1 != line2) + join(); break; case 'z': if (nlines > 1) _AT_@ -1299,126 +1305,126 @@ chkglobal(void) return 1; } -static void -doglobal(void) -{ - int i, k; + static void + doglobal(void) + { + int i, k; - skipblank(); - cmdsiz = 0; - gflag = 1; - if (uflag) - chkprint(0); - - for (i = 1; i <= lastln; i++) { - k = getindex(i); - if (!zero[k].global) - continue; - curln = i; - nlines = 0; - if (uflag) { - line1 = line2 = i; - pflag = 0; - doprint(); + skipblank(); + cmdsiz = 0; + gflag = 1; + if (uflag) + chkprint(0); + + for (i = 1; i <= lastln; i++) { + k = getindex(i); + if (!zero[k].global) + continue; + curln = i; + nlines = 0; + if (uflag) { + line1 = line2 = i; + pflag = 0; + doprint(); + } + docmd(); } - docmd(); + discard(); /* cover the case of not matching anything */ } - discard(); /* cover the case of not matching anything */ -} -static void -usage(void) -{ - eprintf("usage: %s [-s] [-p] [file]\n", argv0); -} - -static void -sigintr(int n) -{ - signal(SIGINT, sigintr); - error("interrupt"); -} + static void + usage(void) + { + eprintf("usage: %s [-s] [-p] [file]\n", argv0); + } -static void -sighup(int dummy) -{ - int n; - char *home = getenv("HOME"), fname[FILENAME_MAX]; + static void + sigintr(int n) + { + signal(SIGINT, sigintr); + error("interrupt"); + } - if (modflag) { - line1 = nextln(0); - line2 = lastln; - if (!setjmp(savesp)) { - dowrite("ed.hup", 1); - } else if (home && !setjmp(savesp)) { - n = snprintf(fname, - sizeof(fname), "%s/%s", home, "ed.hup"); - if (n < sizeof(fname) && n > 0) - dowrite(fname, 1); + static void + sighup(int dummy) + { + int n; + char *home = getenv("HOME"), fname[FILENAME_MAX]; + + if (modflag) { + line1 = nextln(0); + line2 = lastln; + if (!setjmp(savesp)) { + dowrite("ed.hup", 1); + } else if (home && !setjmp(savesp)) { + n = snprintf(fname, + sizeof(fname), "%s/%s", home, "ed.hup"); + if (n < sizeof(fname) && n > 0) + dowrite(fname, 1); + } } + exstatus = 1; + quit(); } - exstatus = 1; - quit(); -} -static void -edit(void) -{ - setjmp(savesp); - for (;;) { - newcmd = 1; - ocurln = curln; - cmdsiz = 0; - repidx = -1; - if (optprompt) - fputs(prompt, stdout); - getlst(); - chkglobal() ? doglobal() : docmd(); + static void + edit(void) + { + setjmp(savesp); + for (;;) { + newcmd = 1; + ocurln = curln; + cmdsiz = 0; + repidx = -1; + if (optprompt) + fputs(prompt, stdout); + getlst(); + chkglobal() ? doglobal() : docmd(); + } } -} -static void -init(char *fname) -{ - size_t len; + static void + init(char *fname) + { + size_t len; - if (setjmp(savesp)) - return; - setscratch(); - if (!fname) - return; - if ((len = strlen(fname)) >= FILENAME_MAX || len == 0) - error("incorrect filename"); - memcpy(savfname, fname, len); - doread(fname); - clearundo(); -} + if (setjmp(savesp)) + return; + setscratch(); + if (!fname) + return; + if ((len = strlen(fname)) >= FILENAME_MAX || len == 0) + error("incorrect filename"); + memcpy(savfname, fname, len); + doread(fname); + clearundo(); + } -int -main(int argc, char *argv[]) -{ - ARGBEGIN { - case 'p': - prompt = EARGF(usage()); - optprompt = 1; - break; - case 's': - optdiag = 0; - break; - default: - usage(); - } ARGEND + int + main(int argc, char *argv[]) + { + ARGBEGIN { + case 'p': + prompt = EARGF(usage()); + optprompt = 1; + break; + case 's': + optdiag = 0; + break; + default: + usage(); + } ARGEND - if (argc > 1) - usage(); + if (argc > 1) + usage(); - signal(SIGINT, sigintr); - signal(SIGHUP, sighup); - signal(SIGQUIT, SIG_IGN); + signal(SIGINT, sigintr); + signal(SIGHUP, sighup); + signal(SIGQUIT, SIG_IGN); - init(*argv); - edit(); + init(*argv); + edit(); - /* not reached */ - return 0; -} + /* not reached */ + return 0; + } -- 2.9.2Received on Tue Sep 20 2016 - 08:50:22 CEST
This archive was generated by hypermail 2.3.0 : Tue Sep 20 2016 - 09:00:16 CEST