Compare commits

...

3 commits

Author SHA1 Message Date
56c17f8131 Merge commit '54f244118d62d979601664cc58b4ffc747a893f3' as 'sys/src/cmd/watch' 2022-07-11 16:29:56 -04:00
54f244118d Squashed 'sys/src/cmd/watch/' content from commit 84cd9e7d1
git-subtree-dir: sys/src/cmd/watch
git-subtree-split: 84cd9e7d14ea8a168434aa222b8407220cd724fa
2022-07-11 16:29:56 -04:00
69fba0e0ba improve basic plumb rules 2022-07-10 20:18:49 -04:00
4 changed files with 303 additions and 8 deletions

View file

@ -36,7 +36,7 @@ plumb start window rc -c '''echo % mail '''$0'; mail '$0
# audio
type is text
data matches '[a-zA-Z¡-￿0-9_\-.,/]+'
data matches '([a-zA-Z¡-￿0-9_\-.,/]+)\.(mp3|MP3|ogg|OGG|flac|FLAC|wav|WAV|au|AU|mid|MID|mus|MUS|m3u|M3U|pls|PLS)'
data matches '([a-zA-Z¡-￿0-9_\-.,/]+)\.(mp3|MP3|ogg|OGG|flac|FLAC|wav|WAV|au|AU|mid|MID|mus|MUS|m3u|M3U|pls|PLS|opus|OPUS)'
arg isfile $0
plumb to audio
plumb start window -scroll play $file
@ -44,7 +44,7 @@ plumb start window -scroll play $file
# image files go to page
type is text
data matches '[a-zA-Z¡-￿0-9_\-.,/]+'
data matches '([a-zA-Z¡-￿0-9_\-.,/]+)\.(jpe?g|JPE?G|gif|GIF|tiff?|TIFF?|ppm|PPM|bit|BIT|png|PNG|pgm|PGM|bmp|BMP|yuv|YUV|tga|TGA)'
data matches '([a-zA-Z¡-￿0-9_\-.,/]+)\.(jpe?g|JPE?G|gif|GIF|tiff?|TIFF?|ppm|PPM|bit|BIT|png|PNG|pgm|PGM|bmp|BMP|yuv|YUV|tga|TGA|webp|WEBP|img|IMG)'
arg isfile $0
plumb to image
plumb client page -wi
@ -108,11 +108,11 @@ plumb start rc -c 'man -b '$2' '$1'
# RFC references are looked up in /lib/rfc and passed to editor
type is text
data matches 'RFC ?([0-9]+)'
arg isfile /lib/rfc/rfc$1
data set $file
plumb to edit
plumb client window $editor
data matches '[Rr][Ff][Cc] ?([0-9]+)'
arg isfile /lib/rfc/rfc$1.html
data set file:///$file
plumb to web
plumb client window $browser -a
# start rule for images without known suffixes
dst is image

14
sys/src/cmd/watch/mkfile Normal file
View file

@ -0,0 +1,14 @@
</$objtype/mkfile
TARG=watch
OFILES=$TARG.$O
BIN=$home/bin/$objtype
MAN=/sys/man/1
</sys/src/cmd/mkone
install:V: man
uninstall:V:
rm -f $BIN/$TARG
rm -f $MAN/$TARG

213
sys/src/cmd/watch/watch.c Normal file
View file

@ -0,0 +1,213 @@
#include <u.h>
#include <libc.h>
#include <regexp.h>
typedef struct List List;
struct List {
List *next;
union {
Dir;
Reprog *re;
};
};
char pwd[1024];
int period = 1000;
int noregroup = 0;
int once = 0;
List *expl;
List *filel;
void
usage(void)
{
fprint(2, "usage: %s [-G] [[-e expr] ...] [-t sec] [cmd]\n", argv0);
exits("usage");
}
void*
emalloc(ulong sz)
{
void *v = malloc(sz);
if(v == nil)
sysfatal("malloc: %r");
setmalloctag(v, getcallerpc(&sz));
memset(v, 0, sz);
return v;
}
char*
estrdup(char *s)
{
if((s = strdup(s)) == nil)
sysfatal("strdup: %r");
setmalloctag(s, getcallerpc(&s));
return s;
}
char*
join(char **arg)
{
int i;
char *s, *p;
s = estrdup("");
for(i = 0; arg[i]; i++){
p = s;
if((s = smprint("%s %s", s, arg[i])) == nil)
sysfatal("smprint: %r");
free(p);
}
return s;
}
void
eadd(char *exp)
{
List *r;
r = emalloc(sizeof(*r));
r->re = regcomp(exp);
r->next = expl;
expl = r;
}
void
fadd(Dir *d)
{
List *f;
f = emalloc(sizeof(*f));
f->Dir = *d;
f->next = filel;
filel = f;
}
int
tracked(char *name)
{
List *r;
for(r = expl; r; r = r->next)
if(regexec(r->re, name, nil, 0))
return 1;
return 0;
}
int
changed(Dir *d)
{
List *f;
for(f = filel; f; f = f->next)
if(f->type == d->type)
if(f->dev == d->dev)
if(f->qid.path == d->qid.path)
if(f->qid.type == d->qid.type)
if(f->qid.vers == d->qid.vers)
return 0;
else{
f->Dir = *d;
return 1;
}
fadd(d);
return 1;
}
void
watch(void)
{
static int first = 1;
long fd, n;
Dir *d, *p;
for(;;){
sleep(period);
if((fd = open(pwd, OREAD)) < 0)
sysfatal("open: %r");
if((n = dirreadall(fd, &d)) < 0)
sysfatal("dirreadall: %r");
close(fd);
for(p = d; n--; p++){
if(tracked(p->name)){
if(first){
fadd(p);
continue;
}
if(changed(p)){
free(d);
return;
}
}
}
first = 0;
free(d);
}
}
void
rc(char *cmd)
{
Waitmsg *m;
switch(fork()){
case -1: sysfatal("fork: %r");
case 0:
execl("/bin/rc", "rc", "-c", cmd, nil);
sysfatal("execl: %r");
}
if((m = wait()) && m->msg[0])
fprint(2, "watch: %s\n", m->msg);
free(m);
}
void
regroup(void)
{
char *cmd;
cmd = smprint("cat /proc/%d/noteid >/proc/%d/noteid",
getppid(), getpid());
rc(cmd);
free(cmd);
}
void
main(int argc, char *argv[])
{
int t;
char *unit;
char *cmd;
cmd = "mk";
ARGBEGIN{
case 'e':
eadd(EARGF(usage())); break;
case 't':
t = strtol(EARGF(usage()), &unit, 10);
if(t < 0)
t = -t;
if(unit != nil && strncmp(unit, "ms", 2) == 0)
period = t;
else
period = t*1000;
break;
case 'G':
noregroup = 1; break;
case '1':
once = 1; break;
default: usage();
}ARGEND;
if(expl == nil)
eadd("\.[chsy]$");
if(argc > 0)
cmd = join(argv);
if(getwd(pwd, sizeof pwd) == nil)
sysfatal("getwd: %r");
if(noregroup == 0) regroup();
for(;;){
watch();
rc(cmd);
if(once) exits(nil);
}
}

View file

@ -0,0 +1,68 @@
.TH WATCH 1
.SH NAME
watch \- run a command on file change
.SH SYNOPSIS
.B watch
[
.B -G
] [
.B -t
sec
] [[
.B -e
pattern
] ... ] [
.B command
]
.SH DESCRIPTION
.PP
Run
.IR command
.RB ( mk
by default) when a change to files
in the current directory is detected.
.PP
The options are as follows:
.TF "-e pattern"
.TP
.BI -e pattern
Watch files matching a
.IR pattern ;
it may be given multiple times and
defaults to
.BR \e.[chsy]$
.TP
.BI -t period
Sets the polling
.IR period
in seconds (one second by default).
\'ms' can be appended to the value
to specify time in miliseconds.
.TP
.B -G
Prevents regrouping to the parent
process' note group. The default
to regroup was chosen to make it
easy to kill
.I watch
in common use by interrupting the
parent shell.
.SH EXAMPLES
.EX
watch -e '\e.man$' 'mk install; window man -P prog' &
.EE
.SH SEE ALSO
.SH SOURCE
.B git://code.a-b.xyz/watch
.SH BUGS
.I Watch
will not react on file removal.
.PP
.I Qid.vers
is watched for changes, which is
not maintained by every file server.
.PP
The polling period is actually
.I sec
plus the time it takes to run
.IR command .