X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=chiark-utils.git;a=blobdiff_plain;f=cprogs%2Fsummer.c;h=762b5d68812ac0b5e20d86ce65745bb40b6a5e06;hp=ca65fc684279ddfd08402a49e1f54bd9e195fe12;hb=c72a0f46e7e39f4106fc9967cd3d233e3c97b504;hpb=dbc59f756cef8f25838f0b650be2314e539ce063 diff --git a/cprogs/summer.c b/cprogs/summer.c index ca65fc6..762b5d6 100644 --- a/cprogs/summer.c +++ b/cprogs/summer.c @@ -1,13 +1,34 @@ /* + * summer - program for summarising (with md5 checksums) filesystem trees + * * usage: * cat startpoints.list | summer >data.list * summer startpoints... >data.list * prints md5sum of data-list to stderr */ +/* + * Copyright (C) 2003,2006-2007 Ian Jackson + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this file; if not, consult the Free Software + * Foundation's website at www.fsf.org, or the GNU Project website at + * www.gnu.org. + */ #define _GNU_SOURCE -#include +#include +#include #include #include #include @@ -25,9 +46,27 @@ #define MAXDEPTH 1024 #define CSUMXL 32 -static int quiet=0; +static int quiet=0, hidectime=0, hideatime=0, hidemtime=0; +static int hidedirsize=0, hidelinkmtime=0, hidextime=0, onefilesystem=0; +static int filenamefieldsep=' '; static FILE *errfile; +#define nodeflag_fsvalid 1u + +static void malloc_fail(void) { perror("summer: alloc failed"); exit(12); } + +static void *mmalloc(size_t sz) { + void *r; + r= malloc(sz); if (!r) malloc_fail(); + return r; +} + +static void *mrealloc(void *p, size_t sz) { + void *r; + r= realloc(p, sz); if (!r && sz) malloc_fail(); + return r; +} + static void fn_escaped(FILE *f, const char *fn) { int c; while ((c= *fn++)) { @@ -119,38 +158,94 @@ static void csum_str(const char *s) { printf("%-*s", CSUMXL, s); } -struct FTW; +static void linktargpath(const char *linktarg) { + printf(" -> "); + fn_escaped(stdout, linktarg); +} -static int item(const char *path, const struct stat *stab, - int flag, struct FTW *ftws) { - char linktarg[MAXFN+1]; +static void pu10(void) { printf(" %10s", "?"); } + +#define PTIME(stab, memb) ((stab) ? ptime((stab), (stab)->memb) : pu10()) + +static void ptime(const struct stat *stab, unsigned long val) { + const char *instead; + + if (!hidextime) goto justprint; + else if (S_ISCHR(stab->st_mode)) instead= "char"; + else if (S_ISBLK(stab->st_mode)) instead= "block"; + else if (S_ISLNK(stab->st_mode)) instead= "link"; + else if (S_ISSOCK(stab->st_mode)) instead= "sock"; + else if (S_ISFIFO(stab->st_mode)) instead= "pipe"; + else { + justprint: + printf(" %10lu", val); + return; + } + + printf(" %10s",instead); +} - switch (flag) { - case FTW_D: - case FTW_F: - case FTW_SL: - if (S_ISREG(stab->st_mode)) csum_file(path); - else if (S_ISDIR(stab->st_mode)) csum_str("dir"); - else if (S_ISCHR(stab->st_mode)) csum_dev('c',stab); - else if (S_ISBLK(stab->st_mode)) csum_dev('b',stab); - else if (S_ISFIFO(stab->st_mode)) csum_str("pipe"); - else if (S_ISLNK(stab->st_mode)) csum_str("link"); - else if (S_ISSOCK(stab->st_mode)) csum_str("sock"); - else problem(path,CSUMXL,"badobj: 0x%lx", (unsigned long)stab->st_mode); - break; +struct hardlink { + dev_t dev; + ino_t ino; + char path[1]; +}; +static void *hardlinks; + +static int hardlink_compar(const void *av, const void *bv) { + const struct hardlink *a=av, *b=bv; + if (a->ino != b->ino) return b->ino - a->ino; + return b->dev - a->dev; +} - case FTW_NS: - case FTW_DNR: - problem_e(path,CSUMXL,"inaccessible"); - break; +static void recurse(const char *path, unsigned nodeflags, dev_t fs); - default: - problem(path,CSUMXL,"ftw flag 0x%x: %s",flag); +static void node(const char *path, unsigned nodeflags, dev_t fs) { + char linktarg[MAXFN+1]; + struct hardlink *foundhl; + const struct stat *stab; + struct stat stabuf; + int r, mountpoint=0; + + r= lstat(path, &stabuf); + stab= r ? 0 : &stabuf; + + foundhl= 0; + if (stab && stab->st_nlink>1) { + struct hardlink *newhl, **foundhl_node; + newhl= mmalloc(sizeof(*newhl) + strlen(path)); + newhl->dev= stab->st_dev; + newhl->ino= stab->st_ino; + foundhl_node= tsearch(newhl, &hardlinks, hardlink_compar); + if (!foundhl_node) malloc_fail(); + foundhl= *foundhl_node; + if (foundhl!=newhl) { + free(newhl); /* hardlink to an earlier object */ + } else { + foundhl= 0; /* new object with link count>1 */ + strcpy(newhl->path, path); + } } - if (S_ISLNK(stab->st_mode)) { - int r; + if (stab) { + if ((nodeflags & nodeflag_fsvalid) && stab->st_dev != fs) + mountpoint= 1; + fs= stab->st_dev; + nodeflags |= nodeflag_fsvalid; + } + if (!stab) problem_e(path,CSUMXL,"inaccessible"); + else if (foundhl) csum_str("hardlink"); + else if (S_ISREG(stab->st_mode)) csum_file(path); + else if (S_ISCHR(stab->st_mode)) csum_dev('c',stab); + else if (S_ISBLK(stab->st_mode)) csum_dev('b',stab); + else if (S_ISFIFO(stab->st_mode)) csum_str("pipe"); + else if (S_ISLNK(stab->st_mode)) csum_str("symlink"); + else if (S_ISSOCK(stab->st_mode)) csum_str("sock"); + else if (S_ISDIR(stab->st_mode)) csum_str(mountpoint ? "mountpoint" : "dir"); + else problem(path,CSUMXL,"badobj: 0x%lx", (unsigned long)stab->st_mode); + + if (stab && S_ISLNK(stab->st_mode)) { r= readlink(path, linktarg, sizeof(linktarg)-1); if (r==sizeof(linktarg)) { problem(path,-1,"readlink too big"); r=-1; } else if (r<0) { problem_e(path,-1,"readlink"); } @@ -160,34 +255,110 @@ static int item(const char *path, const struct stat *stab, else linktarg[r]= 0; } - printf(" %10lu %4d %4o %10ld %10ld %10lu %10lu %10lu ", - (unsigned long)stab->st_size, - (int)stab->st_nlink, - (unsigned)stab->st_mode & 07777U, - (unsigned long)stab->st_uid, - (unsigned long)stab->st_gid, - (unsigned long)stab->st_atime, - (unsigned long)stab->st_mtime, - (unsigned long)stab->st_ctime); - fn_escaped(stdout, path); + if (stab) { + if (S_ISDIR(stab->st_mode) && hidedirsize) + printf(" %10s","dir"); + else + printf(" %10lu", + (unsigned long)stab->st_size); + + printf(" %4o %10ld %10ld", + (unsigned)stab->st_mode & 07777U, + (unsigned long)stab->st_uid, + (unsigned long)stab->st_gid); + } else { + printf(" %10s %4s %10s %10s", "?","?","?","?"); + } - if (S_ISLNK(stab->st_mode)) { - printf(" -> "); - fn_escaped(stdout, linktarg); + if (!hideatime) + PTIME(stab, st_atime); + + if (!hidemtime) { + if (stab && S_ISLNK(stab->st_mode) && hidelinkmtime) + printf(" %10s","link"); + else + PTIME(stab, st_mtime); } + + if (!hidectime) + PTIME(stab, st_ctime); + + putchar(filenamefieldsep); + fn_escaped(stdout, path); + + if (foundhl) linktargpath(foundhl->path); + if (stab && S_ISLNK(stab->st_mode)) linktargpath(linktarg); + putchar('\n'); if (ferror(stdout)) { perror("summer: stdout"); exit(12); } - return 0; + + if (stab && S_ISDIR(stab->st_mode) && !(mountpoint && onefilesystem)) + recurse(path, nodeflags, fs); } static void process(const char *startpoint) { - int r; if (!quiet) fprintf(stderr,"summer: processing: %s\n",startpoint); - r= nftw(startpoint, item, MAXDEPTH, FTW_MOUNT|FTW_PHYS); - if (r) { fprintf(stderr, "summer: nftw failed: %s: %s\n", - strerror(errno), startpoint); exit(4); } + node(startpoint, 0,0); + tdestroy(hardlinks,free); + hardlinks= 0; +} + +static int recurse_maxlen; + +static int recurse_filter(const struct dirent *de) { + int l; + if (de->d_name[0]=='.' && + (de->d_name[1]==0 || + (de->d_name[1]=='.' && + de->d_name[2]==0))) + return 0; + l= strlen(de->d_name); + if (l > recurse_maxlen) recurse_maxlen= l; + return 1; +} + +static int recurse_compar(const struct dirent **a, const struct dirent **b) { + return strcmp((*a)->d_name, (*b)->d_name); +} + +static void recurse(const char *path_or_buf, unsigned nodeflags, dev_t fs) { + static char *buf; + static int buf_allocd; + + struct dirent **namelist, *const *de; + const char *path_or_0= path_or_buf==buf ? 0 : path_or_buf; + int nentries, pathl, esave, buf_want, i; + + pathl= strlen(path_or_buf); + recurse_maxlen= 2; + nentries= scandir(path_or_buf, &namelist, recurse_filter, recurse_compar); + esave= errno; + + buf_want= pathl+1+recurse_maxlen+1; + if (buf_want > buf_allocd) { + buf= mrealloc(buf, buf_want); + buf_allocd= buf_want; + } + /* NOTE that path_or_buf is invalid after this point because + * it might have been realloc'd ! */ + if (path_or_0) strcpy(buf,path_or_0); + + buf[pathl]= '/'; + pathl++; + if (nentries < 0) { + buf[pathl]= 0; errno= esave; + problem_e(buf,CSUMXL+72,"scandir failed"); + fn_escaped(stdout,buf); putchar('\n'); + return; + } + for (i=0, de=namelist; id_name); + node(buf, nodeflags, fs); + free(*de); + } + free(namelist); } static void from_stdin(void) { @@ -215,7 +386,7 @@ int main(int argc, const char *const *argv) { errfile= stderr; - if ((arg=argv[1]) && *arg++=='-') { + while ((arg=argv[1]) && *arg++=='-') { while ((c=*arg++)) { switch (c) { case 'h': @@ -226,6 +397,30 @@ int main(int argc, const char *const *argv) { case 'q': quiet= 1; break; + case 't': + filenamefieldsep= '\t'; + break; + case 'D': + hidedirsize= 1; + break; + case 'b': + hidelinkmtime= 1; + break; + case 'B': + hidextime= 1; + break; + case 'x': + onefilesystem= 1; + break; + case 'C': + hidectime= 1; + break; + case 'A': + hideatime= 1; + break; + case 'M': + hidemtime= 1; + break; case 'f': errfile= stdout; break;