* [9front] [PATCH] ptrap: implement attribute filters
@ 2020-12-05 16:52 kvik
2020-12-05 17:32 ` ori
0 siblings, 1 reply; 5+ messages in thread
From: kvik @ 2020-12-05 16:52 UTC (permalink / raw)
To: 9front
This adds support for filtering plumbs based on message
attributes. An example use case is to make a specific
acme instance display the manual pages produced by
man -b or plumbing the manual reference words:
ptrap edit '.*' +action '^showdata' +filename '^/man/'
---
Works fine on my machine, but I'd appreciate some eyes
and testing from more prolific ptrap users than myself.
diff -r 52827b94aac8 sys/man/4/ptrap
--- a/sys/man/4/ptrap Wed Dec 02 00:57:21 2020 +0100
+++ b/sys/man/4/ptrap Sat Dec 05 17:37:27 2020 +0100
@@ -7,10 +7,7 @@
.B ptrap
.I port
[\fB!\fR]\fIregexp\fR
-[
-.I port
-[\fB!\fR]\fIregexp\fR ...
-]
+[ +\fIattr\fR [\fB!\fR]\fIregexp\fR ... ] ...
.SH DESCRIPTION
.I Ptrap
is a program that mounts itself over a
@@ -20,20 +17,20 @@
and filters incoming messages according to the rules provided on the command line.
.PP
.I Ptrap
-accepts an arbitrary number of argument pairs; each pair consists of a port name
-.I port
-and a regular expression
-.I regexp
-(see
-.IR regexp (6)).
-Each incoming message that does not match
-.I regexp
-is discarded.
-The
-.I regexp
-can be optionally prefixed by
-.B !
-to indicate logical inversion (i.e. messages matching the regexp are discarded).
+accepts an arbitrary number of filters;
+each filter applies to a port, and may match over both the data and attributes of plumb messages.
+.PP
+A filter is formatted as a port name, a data filter, and a list of attribute filters.
+.PP
+The data filter is a
+.IR regex (6)
+that matches the plumbed data.
+The attribute filter consists of the attribute name prefixed with a '+', followed by a
+.IR regex (6)
+that matches the contents of the attribute.
+Any regex may be prefixed with a '!' in order to negate a match,
+causing all matches for that regex to be discarded.
+All parts of a filter must match in order for a plumb message to be forwarded.
.SH EXAMPLES
Start a
.IR sam (1)
@@ -52,6 +49,15 @@
ptrap edit '!^/sys/src/9/'
sam
.EE
+.PP
+Start an
+.IR acme (1)
+instance instance dedicated to reading plumbed manual pages:
+.IP
+.EX
+ptrap edit '.*' +action '^showdata' +filename '^/man/'
+acme -c1
+.EE
.SH SOURCE
.B /sys/src/cmd/ptrap.c
.SH SEE ALSO
diff -r 52827b94aac8 sys/src/cmd/ptrap.c
--- a/sys/src/cmd/ptrap.c Wed Dec 02 00:57:21 2020 +0100
+++ b/sys/src/cmd/ptrap.c Sat Dec 05 17:37:27 2020 +0100
@@ -8,6 +8,7 @@
typedef struct IOProc IOProc;
typedef struct PFilter PFilter;
+typedef struct FAttr FAttr;
typedef struct PFid PFid;
struct IOProc {
@@ -27,11 +28,19 @@
};
Qid rootqid = {.type QTDIR};
+struct FAttr {
+ char *name;
+ Reprog *filt;
+ int invert;
+ FAttr *next;
+};
+
struct PFilter {
char *name;
Reprog *filt;
+ int invert;
+ FAttr *attr;
PFilter *next;
- int invert;
};
PFilter *filters;
@@ -162,12 +171,32 @@
}
static int
+filter(PFilter *f, Plumbmsg *pm)
+{
+ FAttr *a;
+ char *value;
+
+ if(!(regexec(f->filt, pm->data, nil, 0) ^ f->invert))
+ return 0;
+ for(a = f->attr; a; a = a->next){
+ value = plumblookup(pm->attr, a->name);
+ if(value == nil)
+ return 0;
+ if(!(regexec(a->filt, value, nil, 0) ^ f->attr->invert))
+ return 0;
+ }
+ return 1;
+}
+
+static int
filterread(Req *r, PFid *pf)
{
int rc, len, more;
char *buf;
Plumbmsg *pm;
+ PFilter *f;
+ f = pf->filter;
for(;;){
if(pf->msg != nil){
rc = r->ifcall.count;
@@ -194,7 +223,7 @@
len += rc;
}
free(buf);
- if(regexec(pf->filter->filt, pm->data, nil, 0) ^ pf->filter->invert){
+ if(filter(f, pm)){
pf->msg = plumbpack(pm, &pf->msgn);
pf->msgp = 0;
}
@@ -341,7 +370,7 @@
void
usage(void)
{
- fprint(2, "usage: %s port regex [ port regex ... ]\n", argv0);
+ fprint(2, "usage: %s port regex [ +attr regex ... ] ...\n", argv0);
exits("usage");
}
@@ -349,6 +378,7 @@
threadmain(int argc, char **argv)
{
PFilter *f;
+ FAttr *fa;
char *p;
int i;
@@ -357,19 +387,40 @@
}ARGEND;
if(argc == 0 || argc % 2) usage();
- for(i = 0; i < argc; i += 2){
+ for(i = 0; i < argc; ){
+ p = argv[i++];
f = emalloc9p(sizeof(PFilter));
- f->name = strdup(argv[i]);
- p = argv[i+1];
- if(*p == '!'){
+ f->name = estrdup9p(p);
+ p = argv[i++];
+ if(p[0] == '!'){
p++;
f->invert = 1;
}
- f->filt = regcomp(p);
- if(f->filt == nil)
- sysfatal("%r");
+ if((f->filt = regcomp(p)) == nil)
+ sysfatal("regcomp: %r");
f->next = filters;
filters = f;
+
+next:
+ p = argv[i++];
+ if(p == nil || p[0] != '+'){
+ i--;
+ continue;
+ }
+ p++;
+ fa = emalloc9p(sizeof(FAttr));
+ fa->name = estrdup9p(p);
+ p = argv[i++];
+ if(p[0] == '!'){
+ p++;
+ fa->invert = 1;
+ }
+ if((fa->filt = regcomp(p)) == nil)
+ sysfatal("regcomp: %r");
+ fa->next = f->attr;
+ f->attr = fa;
+
+ goto next;
}
threadpostmountsrv(&ptrapsrv, nil, "/mnt/plumb", MREPL | MCREATE);
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [9front] [PATCH] ptrap: implement attribute filters
2020-12-05 16:52 [9front] [PATCH] ptrap: implement attribute filters kvik
@ 2020-12-05 17:32 ` ori
2020-12-05 19:07 ` kvik
0 siblings, 1 reply; 5+ messages in thread
From: ori @ 2020-12-05 17:32 UTC (permalink / raw)
To: kvik, 9front
Quoth kvik@a-b.xyz:
>
> This adds support for filtering plumbs based on message
> attributes. An example use case is to make a specific
> acme instance display the manual pages produced by
> man -b or plumbing the manual reference words:
>
> ptrap edit '.*' +action '^showdata' +filename '^/man/'
>
> ---
> +
> +next:
> + p = argv[i++];
> + if(p == nil || p[0] != '+'){
> + i--;
> + continue;
> + }
> + p++;
> + fa = emalloc9p(sizeof(FAttr));
> + fa->name = estrdup9p(p);
> + p = argv[i++];
> + if(p[0] == '!'){
> + p++;
> + fa->invert = 1;
> + }
> + if((fa->filt = regcomp(p)) == nil)
> + sysfatal("regcomp: %r");
> + fa->next = f->attr;
> + f->attr = fa;
> +
> + goto next;
> }
> threadpostmountsrv(&ptrapsrv, nil, "/mnt/plumb", MREPL | MCREATE);
>
>
Testing now, though I don't use the filtering *that* heavily,
I mainly use it to prevent acme from becoming an editor.
Looking over the code, only thought is that this bit may read
better as a nested loop, like:
for(; i < argc; i++){
...
}
Other than that, I think this looks good.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [9front] [PATCH] ptrap: implement attribute filters
2020-12-05 17:32 ` ori
@ 2020-12-05 19:07 ` kvik
2020-12-06 0:49 ` ori
0 siblings, 1 reply; 5+ messages in thread
From: kvik @ 2020-12-05 19:07 UTC (permalink / raw)
To: 9front
Quoth ori@eigenstate.org:
> Looking over the code, only thought is that this bit may read
> better as a nested loop, like:
That's quite true. Readability++ version follows.
diff -r 52827b94aac8 sys/man/4/ptrap
--- a/sys/man/4/ptrap Wed Dec 02 00:57:21 2020 +0100
+++ b/sys/man/4/ptrap Sat Dec 05 19:40:38 2020 +0100
@@ -7,10 +7,7 @@
.B ptrap
.I port
[\fB!\fR]\fIregexp\fR
-[
-.I port
-[\fB!\fR]\fIregexp\fR ...
-]
+[ +\fIattr\fR [\fB!\fR]\fIregexp\fR ... ] ...
.SH DESCRIPTION
.I Ptrap
is a program that mounts itself over a
@@ -20,20 +17,20 @@
and filters incoming messages according to the rules provided on the command line.
.PP
.I Ptrap
-accepts an arbitrary number of argument pairs; each pair consists of a port name
-.I port
-and a regular expression
-.I regexp
-(see
-.IR regexp (6)).
-Each incoming message that does not match
-.I regexp
-is discarded.
-The
-.I regexp
-can be optionally prefixed by
-.B !
-to indicate logical inversion (i.e. messages matching the regexp are discarded).
+accepts an arbitrary number of filters;
+each filter applies to a port, and may match over both the data and attributes of plumb messages.
+.PP
+A filter is formatted as a port name, a data filter, and a list of attribute filters.
+.PP
+The data filter is a
+.IR regex (6)
+that matches the plumbed data.
+The attribute filter consists of the attribute name prefixed with a '+', followed by a
+.IR regex (6)
+that matches the contents of the attribute.
+Any regex may be prefixed with a '!' in order to negate a match,
+causing all matches for that regex to be discarded.
+All parts of a filter must match in order for a plumb message to be forwarded.
.SH EXAMPLES
Start a
.IR sam (1)
@@ -52,6 +49,15 @@
ptrap edit '!^/sys/src/9/'
sam
.EE
+.PP
+Start an
+.IR acme (1)
+instance instance dedicated to reading plumbed manual pages:
+.IP
+.EX
+ptrap edit '.*' +action '^showdata' +filename '^/man/'
+acme -c1
+.EE
.SH SOURCE
.B /sys/src/cmd/ptrap.c
.SH SEE ALSO
diff -r 52827b94aac8 sys/src/cmd/ptrap.c
--- a/sys/src/cmd/ptrap.c Wed Dec 02 00:57:21 2020 +0100
+++ b/sys/src/cmd/ptrap.c Sat Dec 05 19:40:38 2020 +0100
@@ -8,6 +8,7 @@
typedef struct IOProc IOProc;
typedef struct PFilter PFilter;
+typedef struct FAttr FAttr;
typedef struct PFid PFid;
struct IOProc {
@@ -27,11 +28,19 @@
};
Qid rootqid = {.type QTDIR};
+struct FAttr {
+ char *name;
+ Reprog *filt;
+ int invert;
+ FAttr *next;
+};
+
struct PFilter {
char *name;
Reprog *filt;
+ int invert;
+ FAttr *attr;
PFilter *next;
- int invert;
};
PFilter *filters;
@@ -162,12 +171,32 @@
}
static int
+filter(PFilter *f, Plumbmsg *pm)
+{
+ FAttr *a;
+ char *value;
+
+ if(!(regexec(f->filt, pm->data, nil, 0) ^ f->invert))
+ return 0;
+ for(a = f->attr; a; a = a->next){
+ value = plumblookup(pm->attr, a->name);
+ if(value == nil)
+ return 0;
+ if(!(regexec(a->filt, value, nil, 0) ^ f->attr->invert))
+ return 0;
+ }
+ return 1;
+}
+
+static int
filterread(Req *r, PFid *pf)
{
int rc, len, more;
char *buf;
Plumbmsg *pm;
+ PFilter *f;
+ f = pf->filter;
for(;;){
if(pf->msg != nil){
rc = r->ifcall.count;
@@ -194,7 +223,7 @@
len += rc;
}
free(buf);
- if(regexec(pf->filter->filt, pm->data, nil, 0) ^ pf->filter->invert){
+ if(filter(f, pm)){
pf->msg = plumbpack(pm, &pf->msgn);
pf->msgp = 0;
}
@@ -341,7 +370,7 @@
void
usage(void)
{
- fprint(2, "usage: %s port regex [ port regex ... ]\n", argv0);
+ fprint(2, "usage: %s port regex [ +attr regex ... ] ...\n", argv0);
exits("usage");
}
@@ -349,6 +378,7 @@
threadmain(int argc, char **argv)
{
PFilter *f;
+ FAttr *fa;
char *p;
int i;
@@ -357,19 +387,33 @@
}ARGEND;
if(argc == 0 || argc % 2) usage();
- for(i = 0; i < argc; i += 2){
+ for(i = 0; i+1 < argc;){
+ p = argv[i];
f = emalloc9p(sizeof(PFilter));
- f->name = strdup(argv[i]);
+ f->name = estrdup9p(p);
p = argv[i+1];
- if(*p == '!'){
+ if(p[0] == '!'){
p++;
f->invert = 1;
}
- f->filt = regcomp(p);
- if(f->filt == nil)
- sysfatal("%r");
+ if((f->filt = regcomp(p)) == nil)
+ sysfatal("regcomp: %r");
f->next = filters;
filters = f;
+ for(i += 2; p = argv[i], i+1 < argc && p[0] == '+'; i += 2){
+ p++;
+ fa = emalloc9p(sizeof(FAttr));
+ fa->name = estrdup9p(p);
+ p = argv[i+1];
+ if(p[0] == '!'){
+ p++;
+ fa->invert = 1;
+ }
+ if((fa->filt = regcomp(p)) == nil)
+ sysfatal("regcomp: %r");
+ fa->next = f->attr;
+ f->attr = fa;
+ }
}
threadpostmountsrv(&ptrapsrv, nil, "/mnt/plumb", MREPL | MCREATE);
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [9front] [PATCH] ptrap: implement attribute filters
2020-12-05 19:07 ` kvik
@ 2020-12-06 0:49 ` ori
2020-12-06 20:55 ` kvik
0 siblings, 1 reply; 5+ messages in thread
From: ori @ 2020-12-06 0:49 UTC (permalink / raw)
To: kvik, 9front
Quoth kvik@a-b.xyz:
>
> Quoth ori@eigenstate.org:
> > Looking over the code, only thought is that this bit may read
> > better as a nested loop, like:
>
> That's quite true. Readability++ version follows.
ok from me.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [9front] [PATCH] ptrap: implement attribute filters
2020-12-06 0:49 ` ori
@ 2020-12-06 20:55 ` kvik
0 siblings, 0 replies; 5+ messages in thread
From: kvik @ 2020-12-06 20:55 UTC (permalink / raw)
To: 9front
Commited.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2020-12-06 20:56 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-05 16:52 [9front] [PATCH] ptrap: implement attribute filters kvik
2020-12-05 17:32 ` ori
2020-12-05 19:07 ` kvik
2020-12-06 0:49 ` ori
2020-12-06 20:55 ` kvik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).