summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjasper <jasper@openbsd.org>2015-07-17 20:38:57 +0000
committerjasper <jasper@openbsd.org>2015-07-17 20:38:57 +0000
commit96ea96d2baf8fdfacea36cb724367285d1749ab7 (patch)
tree8112b30bee49b3899c85b8fd0a9d85f8f60d9ae8
parentLike bgpd and ospfd filter routes by RTF_LLINFO and RTF_BROADCAST and use (diff)
downloadwireguard-openbsd-96ea96d2baf8fdfacea36cb724367285d1749ab7.tar.xz
wireguard-openbsd-96ea96d2baf8fdfacea36cb724367285d1749ab7.zip
add -i flag to sed to do in-place editing; mostly based on freebsd
feedback/ok deraadt@ millert@
-rw-r--r--usr.bin/sed/defs.h3
-rw-r--r--usr.bin/sed/extern.h10
-rw-r--r--usr.bin/sed/main.c227
-rw-r--r--usr.bin/sed/process.c121
-rw-r--r--usr.bin/sed/sed.123
5 files changed, 276 insertions, 108 deletions
diff --git a/usr.bin/sed/defs.h b/usr.bin/sed/defs.h
index 8694f181ad8..eaea6b5564e 100644
--- a/usr.bin/sed/defs.h
+++ b/usr.bin/sed/defs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: defs.h,v 1.5 2015/01/19 15:30:52 krw Exp $ */
+/* $OpenBSD: defs.h,v 1.6 2015/07/17 20:38:57 jasper Exp $ */
/*-
* Copyright (c) 1992 Diomidis Spinellis.
* Copyright (c) 1992, 1993
@@ -128,6 +128,7 @@ typedef struct {
char *space; /* Current space pointer. */
size_t len; /* Current length. */
int deleted; /* If deleted. */
+ int append_newline; /* If originally terminated by \n. */
char *back; /* Backing memory. */
size_t blen; /* Backing memory length. */
} SPACE;
diff --git a/usr.bin/sed/extern.h b/usr.bin/sed/extern.h
index 6d2c0324cfd..16b58ac94e1 100644
--- a/usr.bin/sed/extern.h
+++ b/usr.bin/sed/extern.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: extern.h,v 1.9 2015/04/13 05:11:23 deraadt Exp $ */
+/* $OpenBSD: extern.h,v 1.10 2015/07/17 20:38:57 jasper Exp $ */
/*-
* Copyright (c) 1992 Diomidis Spinellis.
* Copyright (c) 1992, 1993
@@ -40,17 +40,19 @@ extern regmatch_t *match;
extern size_t maxnsub;
extern u_long linenum;
extern size_t appendnum;
-extern int lastline;
extern int Eflag, aflag, eflag, nflag;
-extern char *fname;
+extern const char *fname, *outfname;
+extern FILE *infile, *outfile;
void cfclose(struct s_command *, struct s_command *);
void compile(void);
-void cspace(SPACE *, char *, size_t, enum e_spflag);
+void cspace(SPACE *, const char *, size_t, enum e_spflag);
char *cu_fgets(char **, size_t *);
void err(int, const char *, ...);
int mf_fgets(SPACE *, enum e_spflag);
+int lastline(void);
void process(void);
+void resetranges(void);
char *strregerror(int, regex_t *);
void *xmalloc(size_t);
void *xreallocarray(void *, size_t, size_t);
diff --git a/usr.bin/sed/main.c b/usr.bin/sed/main.c
index d7cc3743161..19892d34915 100644
--- a/usr.bin/sed/main.c
+++ b/usr.bin/sed/main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: main.c,v 1.18 2014/11/26 18:34:51 millert Exp $ */
+/* $OpenBSD: main.c,v 1.19 2015/07/17 20:38:57 jasper Exp $ */
/*-
* Copyright (c) 1992 Diomidis Spinellis.
@@ -34,6 +34,7 @@
*/
#include <sys/types.h>
+#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
@@ -45,6 +46,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <libgen.h>
#include "defs.h"
#include "extern.h"
@@ -78,15 +80,23 @@ struct s_flist {
*/
static struct s_flist *files, **fl_nextp = &files;
+FILE *infile; /* Current input file */
+FILE *outfile; /* Current output file */
+
int Eflag, aflag, eflag, nflag;
+static int rval; /* Exit status */
/*
* Current file and line number; line numbers restart across compilation
- * units, but span across input files.
+ * units, but span across input files. The latter is optional if editing
+ * in place.
*/
-char *fname; /* File name. */
+const char *fname; /* File name. */
+const char *outfname; /* Output file name */
+static char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */
+static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */
+char *inplace; /* Inplace edit file extension */
u_long linenum;
-int lastline; /* TRUE on the last line of the last file */
static void add_compunit(enum e_cut, char *);
static void add_file(char *);
@@ -97,7 +107,8 @@ main(int argc, char *argv[])
int c, fflag;
fflag = 0;
- while ((c = getopt(argc, argv, "Eae:f:nru")) != -1)
+ inplace = NULL;
+ while ((c = getopt(argc, argv, "Eae:f:i::nru")) != -1)
switch (c) {
case 'E':
case 'r':
@@ -114,6 +125,9 @@ main(int argc, char *argv[])
fflag = 1;
add_compunit(CU_FILE, optarg);
break;
+ case 'i':
+ inplace = optarg ? optarg : "";
+ break;
case 'n':
nflag = 1;
break;
@@ -123,8 +137,8 @@ main(int argc, char *argv[])
default:
case '?':
(void)fprintf(stderr,
- "usage: sed [-aEnru] command [file ...]\n"
- " sed [-aEnru] [-e command] [-f command_file] [file ...]\n");
+ "usage: sed [-aEnru] [-i [extension]] command [file ...]\n"
+ " sed [-aEnru] [-i [extension]] [-e command] [-f command_file] [file ...]\n");
exit(1);
}
argc -= optind;
@@ -148,7 +162,7 @@ main(int argc, char *argv[])
cfclose(prog, NULL);
if (fclose(stdout))
err(FATAL, "stdout: %s", strerror(errno));
- exit (0);
+ exit (rval);
}
/*
@@ -258,69 +272,128 @@ again:
int
mf_fgets(SPACE *sp, enum e_spflag spflag)
{
- static FILE *f; /* Current open file */
+ struct stat sb;
size_t len;
char *p;
- int c;
+ int c, fd;
+ static int firstfile;
- if (f == NULL)
- /* Advance to first non-empty file */
- for (;;) {
- if (files == NULL) {
- lastline = 1;
- return (0);
- }
- if (files->fname == NULL) {
- f = stdin;
- fname = "stdin";
- } else {
- fname = files->fname;
- if ((f = fopen(fname, "r")) == NULL)
- err(FATAL, "%s: %s",
- fname, strerror(errno));
+ if (infile == NULL) {
+ /* stdin? */
+ if (files->fname == NULL) {
+ if (inplace != NULL)
+ err(FATAL, "-i may not be used with stdin");
+ infile = stdin;
+ fname = "stdin";
+ outfile = stdout;
+ outfname = "stdout";
+ }
+
+ firstfile = 1;
+ }
+
+ for (;;) {
+ if (infile != NULL && (c = getc(infile)) != EOF) {
+ (void)ungetc(c, infile);
+ break;
+ }
+ /* If we are here then either eof or no files are open yet */
+ if (infile == stdin) {
+ sp->len = 0;
+ return (0);
+ }
+ if (infile != NULL) {
+ fclose(infile);
+ if (*oldfname != '\0') {
+ if (rename(fname, oldfname) != 0) {
+ err(WARNING, "rename()");
+ unlink(tmpfname);
+ exit(1);
+ }
+ *oldfname = '\0';
}
- if ((c = getc(f)) != EOF) {
- (void)ungetc(c, f);
- break;
+ if (*tmpfname != '\0') {
+ if (outfile != NULL && outfile != stdout)
+ fclose(outfile);
+ outfile = NULL;
+ rename(tmpfname, fname);
+ *tmpfname = '\0';
}
- (void)fclose(f);
+ outfname = NULL;
+ }
+ if (firstfile == 0)
files = files->next;
+ else
+ firstfile = 0;
+ if (files == NULL) {
+ sp->len = 0;
+ return (0);
+ }
+ fname = files->fname;
+ if (inplace != NULL) {
+ if (lstat(fname, &sb) != 0)
+ err(1, "%s", fname);
+ if (!S_ISREG(sb.st_mode))
+ err(FATAL, "%s: %s %s", fname,
+ "in-place editing only",
+ "works for regular files");
+ if (*inplace != '\0') {
+ strlcpy(oldfname, fname,
+ sizeof(oldfname));
+ len = strlcat(oldfname, inplace,
+ sizeof(oldfname));
+ if (len > sizeof(oldfname))
+ err(FATAL, "%s: name too long", fname);
+ }
+ len = snprintf(tmpfname, sizeof(tmpfname), "%s/sedXXXXXXXXXX",
+ dirname(fname));
+ if (len >= sizeof(tmpfname))
+ err(FATAL, "%s: name too long", fname);
+ if ((fd = mkstemp(tmpfname)) == -1)
+ err(FATAL, "%s", fname);
+ if ((outfile = fdopen(fd, "w")) == NULL) {
+ unlink(tmpfname);
+ err(FATAL, "%s", fname);
+ }
+ fchown(fileno(outfile), sb.st_uid, sb.st_gid);
+ fchmod(fileno(outfile), sb.st_mode & ALLPERMS);
+ outfname = tmpfname;
+ linenum = 0;
+ resetranges();
+ } else {
+ outfile = stdout;
+ outfname = "stdout";
+ }
+ if ((infile = fopen(fname, "r")) == NULL) {
+ err(WARNING, "%s", fname);
+ rval = 1;
+ continue;
}
-
- if (lastline) {
- sp->len = 0;
- return (0);
}
/*
+ * We are here only when infile is open and we still have something
+ * to read from it.
+ *
* Use fgetln so that we can handle essentially infinite input data.
* Can't use the pointer into the stdio buffer as the process space
* because the ungetc() can cause it to move.
*/
- p = fgetln(f, &len);
- if (ferror(f))
+ p = fgetln(infile, &len);
+ if (ferror(infile))
err(FATAL, "%s: %s", fname, strerror(errno ? errno : EIO));
+ if (len != 0 && p[len - 1] == '\n') {
+ sp->append_newline = 1;
+ len--;
+ } else if (!lastline()) {
+ sp->append_newline = 1;
+ } else {
+ sp->append_newline = 0;
+ }
cspace(sp, p, len, spflag);
linenum++;
- /* Advance to next non-empty file */
- while ((c = getc(f)) == EOF) {
- (void)fclose(f);
- files = files->next;
- if (files == NULL) {
- lastline = 1;
- return (1);
- }
- if (files->fname == NULL) {
- f = stdin;
- fname = "stdin";
- } else {
- fname = files->fname;
- if ((f = fopen(fname, "r")) == NULL)
- err(FATAL, "%s: %s", fname, strerror(errno));
- }
- }
- (void)ungetc(c, f);
+
return (1);
}
@@ -354,3 +427,51 @@ add_file(char *s)
fp->fname = s;
fl_nextp = &fp->next;
}
+
+
+static int
+next_files_have_lines()
+{
+ struct s_flist *file;
+ FILE *file_fd;
+ int ch;
+
+ file = files;
+ while ((file = file->next) != NULL) {
+ if ((file_fd = fopen(file->fname, "r")) == NULL)
+ continue;
+
+ if ((ch = getc(file_fd)) != EOF) {
+ /*
+ * This next file has content, therefore current
+ * file doesn't contains the last line.
+ */
+ ungetc(ch, file_fd);
+ fclose(file_fd);
+ return (1);
+ }
+
+ fclose(file_fd);
+ }
+
+ return (0);
+}
+
+int
+lastline(void)
+{
+ int ch;
+
+ if (feof(infile)) {
+ return !(
+ (inplace == NULL) &&
+ next_files_have_lines());
+ }
+ if ((ch = getc(infile)) == EOF) {
+ return !(
+ (inplace == NULL) &&
+ next_files_have_lines());
+ }
+ ungetc(ch, infile);
+ return (0);
+}
diff --git a/usr.bin/sed/process.c b/usr.bin/sed/process.c
index aa7ea51d0c4..d61249e0a26 100644
--- a/usr.bin/sed/process.c
+++ b/usr.bin/sed/process.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: process.c,v 1.23 2015/04/18 18:28:37 deraadt Exp $ */
+/* $OpenBSD: process.c,v 1.24 2015/07/17 20:38:57 jasper Exp $ */
/*-
* Copyright (c) 1992 Diomidis Spinellis.
@@ -55,6 +55,7 @@ static SPACE HS, PS, SS;
#define pd PS.deleted
#define ps PS.space
#define psl PS.len
+#define psanl PS.append_newline
#define hs HS.space
#define hsl HS.len
@@ -76,7 +77,10 @@ static regex_t *defpreg;
size_t maxnsub;
regmatch_t *match;
-#define OUT(s) do { fwrite(s, sizeof(u_char), psl, stdout); } while (0)
+#define OUT() do {\
+ fwrite(ps, 1, psl, outfile);\
+ if (psanl) fputc('\n', outfile);\
+} while (0)
void
process(void)
@@ -85,6 +89,7 @@ process(void)
SPACE tspace;
size_t len, oldpsl;
char *p;
+ int oldpsanl;
for (linenum = 0; mf_fgets(&PS, REPLACE);) {
pd = 0;
@@ -118,8 +123,8 @@ redirect:
case 'c':
pd = 1;
psl = 0;
- if (cp->a2 == NULL || lastaddr)
- (void)printf("%s", cp->t);
+ if (cp->a2 == NULL || lastaddr || lastline())
+ (void)fprintf(outfile, "%s", cp->t);
break;
case 'd':
pd = 1;
@@ -128,7 +133,7 @@ redirect:
if (pd)
goto new;
if (psl == 0 ||
- (p = memchr(ps, '\n', psl - 1)) == NULL) {
+ (p = memchr(ps, '\n', psl)) == NULL) {
pd = 1;
goto new;
} else {
@@ -140,25 +145,25 @@ redirect:
cspace(&PS, hs, hsl, REPLACE);
break;
case 'G':
- if (hs == NULL)
- cspace(&HS, "\n", 1, REPLACE);
+ cspace(&PS, "\n", 1, 0);
cspace(&PS, hs, hsl, 0);
break;
case 'h':
cspace(&HS, ps, psl, REPLACE);
break;
case 'H':
+ cspace(&HS, "\n", 1, 0);
cspace(&HS, ps, psl, 0);
break;
case 'i':
- (void)printf("%s", cp->t);
+ (void)fprintf(outfile, "%s", cp->t);
break;
case 'l':
lputs(ps);
break;
case 'n':
if (!nflag && !pd)
- OUT(ps);
+ OUT();
flush_appends();
if (!mf_fgets(&PS, REPLACE))
exit(0);
@@ -166,33 +171,32 @@ redirect:
break;
case 'N':
flush_appends();
- if (!mf_fgets(&PS, 0)) {
- if (!nflag && !pd)
- OUT(ps);
+ cspace(&PS, "\n", 1, 0);
+ if (!mf_fgets(&PS, 0))
exit(0);
- }
break;
case 'p':
if (pd)
break;
- OUT(ps);
+ OUT();
break;
case 'P':
if (pd)
break;
- if (psl != 0 &&
- (p = memchr(ps, '\n', psl - 1)) != NULL) {
+ if ((p = memchr(ps, '\n', psl)) != NULL) {
oldpsl = psl;
- psl = (p + 1) - ps;
- OUT(ps);
+ oldpsanl = psanl;
+ psl = p - ps;
+ psanl = 1;
+ OUT();
psl = oldpsl;
} else {
- OUT(ps);
+ OUT();
}
break;
case 'q':
if (!nflag && !pd)
- OUT(ps);
+ OUT();
flush_appends();
exit(0);
case 'r':
@@ -225,34 +229,36 @@ redirect:
DEFFILEMODE)) == -1)
err(FATAL, "%s: %s",
cp->t, strerror(errno));
- if (write(cp->u.fd, ps, psl) != psl)
+ if (write(cp->u.fd, ps, psl) != psl ||
+ write(cp->u.fd, "\n", 1) != 1)
err(FATAL, "%s: %s",
cp->t, strerror(errno));
break;
case 'x':
if (hs == NULL)
- cspace(&HS, "\n", 1, REPLACE);
+ cspace(&HS, "", 0, REPLACE);
tspace = PS;
PS = HS;
+ psanl = tspace.append_newline;
HS = tspace;
break;
case 'y':
if (pd || psl == 0)
break;
- for (p = ps, len = psl; --len; ++p)
+ for (p = ps, len = psl; len--; ++p)
*p = cp->u.y[(unsigned char)*p];
break;
case ':':
case '}':
break;
case '=':
- (void)printf("%lu\n", linenum);
+ (void)fprintf(outfile, "%lu\n", linenum);
}
cp = cp->next;
} /* for all cp */
new: if (!nflag && !pd)
- OUT(ps);
+ OUT();
flush_appends();
} /* for all lines */
}
@@ -263,7 +269,7 @@ new: if (!nflag && !pd)
*/
#define MATCH(a) \
(a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, psl) : \
- (a)->type == AT_LINE ? linenum == (a)->u.l : lastline
+ (a)->type == AT_LINE ? linenum == (a)->u.l : lastline()
/*
* Return TRUE if the command applies to the current line. Sets the inrange
@@ -305,6 +311,19 @@ applies(struct s_command *cp)
}
/*
+ * Reset all inrange markers.
+ */
+void
+resetranges(void)
+{
+ struct s_command *cp;
+
+ for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next)
+ if (cp->a2)
+ cp->inrange = 0;
+}
+
+/*
* substitute --
* Do substitutions in the pattern space. Currently, we build a
* copy of the new pattern space in the substitute space structure
@@ -392,19 +411,21 @@ substitute(struct s_command *cp)
*/
tspace = PS;
PS = SS;
+ psanl = tspace.append_newline;
SS = tspace;
SS.space = SS.back;
/* Handle the 'p' flag. */
if (cp->u.s->p)
- OUT(ps);
+ OUT();
/* Handle the 'w' flag. */
if (cp->u.s->wfile && !pd) {
if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile,
O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1)
err(FATAL, "%s: %s", cp->u.s->wfile, strerror(errno));
- if (write(cp->u.s->wfd, ps, psl) != psl)
+ if (write(cp->u.s->wfd, ps, psl) != psl ||
+ write(cp->u.s->wfd, "\n", 1) != 1)
err(FATAL, "%s: %s", cp->u.s->wfile, strerror(errno));
}
return (1);
@@ -425,7 +446,7 @@ flush_appends(void)
switch (appends[i].type) {
case AP_STRING:
fwrite(appends[i].s, sizeof(char), appends[i].len,
- stdout);
+ outfile);
break;
case AP_FILE:
/*
@@ -439,12 +460,12 @@ flush_appends(void)
if ((f = fopen(appends[i].s, "r")) == NULL)
break;
while ((count = fread(buf, sizeof(char), sizeof(buf), f)))
- (void)fwrite(buf, sizeof(char), count, stdout);
+ (void)fwrite(buf, sizeof(char), count, outfile);
(void)fclose(f);
break;
}
- if (ferror(stdout))
- err(FATAL, "stdout: %s", strerror(errno ? errno : EIO));
+ if (ferror(outfile))
+ err(FATAL, "%s: %s", outfname, strerror(errno ? errno : EIO));
appendx = sdone = 0;
}
@@ -452,10 +473,14 @@ static void
lputs(char *s)
{
int count;
- char *escapes, *p;
+ const char *escapes;
+ char *p;
struct winsize win;
static int termwidth = -1;
+ if (outfile != stdout)
+ termwidth = 60;
+
if (termwidth == -1) {
termwidth = 0;
if ((p = getenv("COLUMNS")))
@@ -470,29 +495,33 @@ lputs(char *s)
for (count = 0; *s; ++s) {
if (count >= termwidth) {
- (void)printf("\\\n");
+ (void)fprintf(outfile, "\\\n");
count = 0;
}
if (isascii((unsigned char)*s) && isprint((unsigned char)*s)
&& *s != '\\') {
- (void)putchar(*s);
+ (void)fputc(*s, outfile);
count++;
- } else if (*s != '\n') {
+ } else if (*s == '\n') {
+ (void)fputc('$', outfile);
+ (void)fputc('\n', outfile);
+ count = 0;
+ } else {
escapes = "\\\a\b\f\r\t\v";
- (void)putchar('\\');
+ (void)fputc('\\', outfile);
if ((p = strchr(escapes, *s))) {
- (void)putchar("\\abfrtv"[p - escapes]);
+ (void)fputc("\\abfrtv"[p - escapes], outfile);
count += 2;
} else {
- (void)printf("%03o", *(u_char *)s);
+ (void)fprintf(outfile, "%03o", *(u_char *)s);
count += 4;
}
}
}
- (void)putchar('$');
- (void)putchar('\n');
- if (ferror(stdout))
- err(FATAL, "stdout: %s", strerror(errno ? errno : EIO));
+ (void)fputc('$', outfile);
+ (void)fputc('\n', outfile);
+ if (ferror(outfile))
+ err(FATAL, "%s: %s", outfname, strerror(errno ? errno : EIO));
}
static inline int
@@ -507,9 +536,7 @@ regexec_e(regex_t *preg, const char *string, int eflags,
} else
defpreg = preg;
- /* Set anchors, discounting trailing newline (if any). */
- if (slen > 0 && string[slen - 1] == '\n')
- slen--;
+ /* Set anchors */
match[0].rm_so = 0;
match[0].rm_eo = slen;
@@ -575,7 +602,7 @@ regsub(SPACE *sp, char *string, char *src)
* space as necessary.
*/
void
-cspace(SPACE *sp, char *p, size_t len, enum e_spflag spflag)
+cspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag)
{
size_t tlen;
diff --git a/usr.bin/sed/sed.1 b/usr.bin/sed/sed.1
index 91f235d981e..a5307bef7d2 100644
--- a/usr.bin/sed/sed.1
+++ b/usr.bin/sed/sed.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: sed.1,v 1.44 2014/10/22 23:23:22 schwarze Exp $
+.\" $OpenBSD: sed.1,v 1.45 2015/07/17 20:38:57 jasper Exp $
.\"
.\" Copyright (c) 1992, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -32,7 +32,7 @@
.\"
.\" from: @(#)sed.1 8.2 (Berkeley) 12/30/93
.\"
-.Dd $Mdocdate: October 22 2014 $
+.Dd $Mdocdate: July 17 2015 $
.Dt SED 1
.Os
.Sh NAME
@@ -47,6 +47,7 @@
.Op Fl aEnru
.Op Fl e Ar command
.Op Fl f Ar command_file
+.Op Fl i Ns Op Ar extension
.Op Ar
.Sh DESCRIPTION
The
@@ -94,6 +95,16 @@ Append the editing commands found in the file
.Ar command_file
to the list of commands.
The editing commands should each be listed on a separate line.
+.It Fl i Ar extension
+Edit files in-place, saving backups with the specified
+.Ar extension .
+If a zero-length
+.Ar extension
+is given, no backup will be saved.
+It is not recommended to give a zero-length
+.Ar extension
+when in-place editing files, as you risk corruption or partial content
+in situations where disk space is exhausted, etc.
.It Fl r
An alias for
.Fl E ,
@@ -510,6 +521,12 @@ command,
squeezing excess empty lines from standard input:
.Bd -literal -offset indent
$ sed -n '
+.Pp
+The
+.Fl i
+option is a non-standard
+.Fx
+extension and may not be available on other operating systems.
# Write non-empty lines.
/./ {
p
@@ -543,7 +560,7 @@ utility is compliant with the
specification.
.Pp
The flags
-.Op Fl aEru
+.Op Fl aEiru
are extensions to that specification.
.Pp
The use of newlines to separate multiple commands on the command line