/*
* generate a list of files and their metadata
* using a given proto file.
*/
#include "all.h"

int changesonly;
char *uid;
Db *db;
Biobuf blog;
ulong now;
int n;
char **x;
int nx;
int justlog;
char *root=".";
char **match;
int nmatch;

int
ismatch(char *s)
{
       int i, len;

       if(nmatch == 0)
               return 1;
       for(i=0; i<nmatch; i++){
               if(strcmp(s, match[i]) == 0)
                       return 1;
               len = strlen(match[i]);
               if(strncmp(s, match[i], len) == 0 && s[len]=='/')
                       return 1;
       }
       return 0;
}

void
xlog(int c, char *name, Dir *d)
{
       char *dname;

       dname = d->name;
       if(strcmp(dname, name) == 0)
               dname = "-";
       if(!justlog)
               Bprint(&blog, "%lud %d ", now, n++);
       Bprint(&blog, "%c %q %q %luo %q %q %lud %lld\n",
               c, name, dname, d->mode, uid ? uid : d->uid, d->gid, d->mtime, d->length);
}

void
walk(char *new, char *old, Dir *xd, void*)
{
       int i, change, len;
       Dir od, d;

       if(!ismatch(new))
               return;
       old = unroot(old, root);
       assert(new[0]=='/');
       new++;
       assert(old[0]=='/');
       old++;
       for(i=0; i<nx; i++){
               if(strcmp(new, x[i]) == 0)
                       return;
               len = strlen(x[i]);
               if(strncmp(new, x[i], len)==0 && new[len]=='/')
                       return;
       }

       d = *xd;
       d.name = old;
       memset(&od, 0, sizeof od);
       change = 0;
       if(markdb(db, new, &od) < 0){
               if(!changesonly){
                       xlog('a', new, &d);
                       change = 1;
               }
       }else{
               if((d.mode&DMDIR)==0 && (od.mtime!=d.mtime || od.length!=d.length)){
                       xlog('c', new, &d);
                       change = 1;
               }
               if((!uid&&strcmp(od.uid,d.uid)!=0)
               || strcmp(od.gid,d.gid)!=0
               || od.mode!=d.mode){
                       xlog('m', new, &d);
                       change = 1;
               }
       }
       if(!justlog && change){
               if(uid)
                       d.uid = uid;
               d.muid = "mark";        /* mark bit */
               insertdb(db, new, &d);
       }
}

void
usage(void)
{
       fprint(2, "usage: replica/updatedb [-c] [-p proto] [-r root] [-t now n] [-u uid] [-x path]... db [paths]\n");
       exits("usage");
}

void
main(int argc, char **argv)
{
       char *proto;
       Avlwalk *w;
       Dir d;
       Entry *e;

       quotefmtinstall();
       proto = "/sys/lib/sysconfig/proto/allproto";
       now = time(0);
       Binit(&blog, 1, OWRITE);
       ARGBEGIN{
       case 'c':
               changesonly = 1;
               break;
       case 'l':
               justlog = 1;
               break;
       case 'p':
               proto = EARGF(usage());
               break;
       case 'r':
               root = EARGF(usage());
               break;
       case 't':
               now = strtoul(EARGF(usage()), 0, 0);
               n = atoi(EARGF(usage()));
               break;
       case 'u':
               uid = EARGF(usage());
               break;
       case 'x':
               if(nx%16 == 0)
                       x = erealloc(x, (nx+16)*sizeof(x[0]));
               x[nx++] = EARGF(usage());
               break;
       default:
               usage();
       }ARGEND

       if(argc <1)
               usage();

       match = argv+1;
       nmatch = argc-1;

       db = opendb(argv[0]);
       if(rdproto(proto, root, walk, nil, nil) < 0)
               sysfatal("rdproto: %r");

       if(!changesonly){
               w = avlwalk(db->avl);
               while(e = (Entry*)avlnext(w)){
                       if(!ismatch(e->name))
                               continue;
                       if(!e->d.mark){         /* not visited during walk */
                               memset(&d, 0, sizeof d);
                               d.name = e->d.name;
                               d.uid = e->d.uid;
                               d.gid = e->d.gid;
                               d.mtime = e->d.mtime;
                               d.mode = e->d.mode;
                               xlog('d', e->name, &d);
                               if(!justlog)
                                       removedb(db, e->name);
                       }
               }
       }

       if(Bterm(&blog) < 0)
               sysfatal("writing output: %r");

       exits(nil);
}