Merge commit '54f244118d62d979601664cc58b4ffc747a893f3' as 'sys/src/cmd/watch'
This commit is contained in:
commit
56c17f8131
3 changed files with 295 additions and 0 deletions
14
sys/src/cmd/watch/mkfile
Normal file
14
sys/src/cmd/watch/mkfile
Normal 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
213
sys/src/cmd/watch/watch.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
68
sys/src/cmd/watch/watch.man
Normal file
68
sys/src/cmd/watch/watch.man
Normal 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 .
|
Loading…
Reference in a new issue