1*da2e3ebdSchin /*********************************************************************** 2*da2e3ebdSchin * * 3*da2e3ebdSchin * This software is part of the ast package * 4*da2e3ebdSchin * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5*da2e3ebdSchin * and is licensed under the * 6*da2e3ebdSchin * Common Public License, Version 1.0 * 7*da2e3ebdSchin * by AT&T Knowledge Ventures * 8*da2e3ebdSchin * * 9*da2e3ebdSchin * A copy of the License is available at * 10*da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt * 11*da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*da2e3ebdSchin * * 13*da2e3ebdSchin * Information and Software Systems Research * 14*da2e3ebdSchin * AT&T Research * 15*da2e3ebdSchin * Florham Park NJ * 16*da2e3ebdSchin * * 17*da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> * 18*da2e3ebdSchin * David Korn <dgk@research.att.com> * 19*da2e3ebdSchin * * 20*da2e3ebdSchin ***********************************************************************/ 21*da2e3ebdSchin #pragma prototyped 22*da2e3ebdSchin /* 23*da2e3ebdSchin * Glenn Fowler 24*da2e3ebdSchin * AT&T Research 25*da2e3ebdSchin * 26*da2e3ebdSchin * sum -- list file checksum and size 27*da2e3ebdSchin */ 28*da2e3ebdSchin 29*da2e3ebdSchin static const char usage[] = 30*da2e3ebdSchin "[-?\n@(#)$Id: sum (AT&T Research) 2007-02-07 $\n]" 31*da2e3ebdSchin USAGE_LICENSE 32*da2e3ebdSchin "[+NAME?cksum,md5sum,sum - print file checksum and block count]" 33*da2e3ebdSchin "[+DESCRIPTION?\bsum\b lists the checksum, and for most methods the block" 34*da2e3ebdSchin " count, for each file argument. The standard input is read if there are" 35*da2e3ebdSchin " no \afile\a arguments. \bgetconf UNIVERSE\b determines the default" 36*da2e3ebdSchin " \bsum\b method: \batt\b for the \batt\b universe, \bbsd\b otherwise." 37*da2e3ebdSchin " The default for the other commands is the command name itself. The" 38*da2e3ebdSchin " \batt\b method is a true sum, all others are order dependent.]" 39*da2e3ebdSchin "[+?Method names consist of a leading identifier and 0 or more options" 40*da2e3ebdSchin " separated by -.]" 41*da2e3ebdSchin "[+?\bgetconf PATH_RESOLVE\b determines how symbolic links are handled. This" 42*da2e3ebdSchin " can be explicitly overridden by the \b--logical\b, \b--metaphysical\b," 43*da2e3ebdSchin " and \b--physical\b options below. \bPATH_RESOLVE\b can be one of:]{" 44*da2e3ebdSchin " [+logical?Follow all symbolic links.]" 45*da2e3ebdSchin " [+metaphysical?Follow command argument symbolic links," 46*da2e3ebdSchin " otherwise don't follow.]" 47*da2e3ebdSchin " [+physical?Don't follow symbolic links.]" 48*da2e3ebdSchin "}" 49*da2e3ebdSchin 50*da2e3ebdSchin "[a:all?List the checksum for all files. Use with \b--total\b to list both" 51*da2e3ebdSchin " individual and total checksums and block counts.]" 52*da2e3ebdSchin "[b:binary?Read files in binary mode. This is the default.]" 53*da2e3ebdSchin "[c:check?Each \afile\a is interpreted as the output from a previous \bsum\b." 54*da2e3ebdSchin " If \b--header\b or \b--permissions\b was specified in the previous" 55*da2e3ebdSchin " \bsum\b then the checksum method is automatically determined," 56*da2e3ebdSchin " otherwise \b--method\b must be specified. The listed checksum is" 57*da2e3ebdSchin " compared with the current value and a warning is issued for each file" 58*da2e3ebdSchin " that does not match. If \afile\a was generated by \b--permissions\b" 59*da2e3ebdSchin " then the file mode, user and group are also checked. Empty lines," 60*da2e3ebdSchin " lines starting with \b#<space>\b, or the line \b#\b are ignored. Lines" 61*da2e3ebdSchin " containing no blanks are interpreted as [no]]\aname\a[=\avalue\a]]" 62*da2e3ebdSchin " options:]{" 63*da2e3ebdSchin " [+method=name?Checksum method to apply to subsequent lines.]" 64*da2e3ebdSchin " [+permissions?Subsequent lines were generated with" 65*da2e3ebdSchin " \b--permissions\b.]" 66*da2e3ebdSchin "}" 67*da2e3ebdSchin "[h:header?Print the checksum method as the first output line. Used with" 68*da2e3ebdSchin " \b--check\b and \b--permissions\b.]" 69*da2e3ebdSchin "[l:list?Each \afile\a is interpreted as a list of files, one per line," 70*da2e3ebdSchin " that is checksummed.]" 71*da2e3ebdSchin "[p:permissions?If \b--check\b is not specified then list the file" 72*da2e3ebdSchin " mode, user and group between the checksum and path. User and group" 73*da2e3ebdSchin " matching the caller are output as \b-\b. If \b--check\b is" 74*da2e3ebdSchin " specified then the mode, user and group for each path in \afile\a" 75*da2e3ebdSchin " are updated if necessary to match those in \afile\a. A warning is" 76*da2e3ebdSchin " printed on the standard error for each changed file.]" 77*da2e3ebdSchin "[r:recursive?Recursively checksum the contents of directories.]" 78*da2e3ebdSchin "[s:silent|status?No output for \b--check\b; 0 exit status means all sums" 79*da2e3ebdSchin " matched, non-0 means at least one sum failed to match. Ignored for" 80*da2e3ebdSchin " \b--permissions\b.]" 81*da2e3ebdSchin "[t:total?List only the total checksum and block count of all files." 82*da2e3ebdSchin " \b--all\b \b--total\b lists each checksum and the total. The" 83*da2e3ebdSchin " total checksum and block count may be different from the checksum" 84*da2e3ebdSchin " and block count of the catenation of all files due to partial" 85*da2e3ebdSchin " blocks that may occur when the files are treated separately.]" 86*da2e3ebdSchin "[T:text?Read files in text mode (i.e., treat \b\\r\\n\b as \b\\n\b).]" 87*da2e3ebdSchin "[w!:warn?Warn about invalid \b--check\b lines.]" 88*da2e3ebdSchin "[x:method|algorithm?Specifies the checksum \amethod\a to" 89*da2e3ebdSchin " apply. Parenthesized method options are readonly implementation" 90*da2e3ebdSchin " details.]:[method]{\fmethods\f}" 91*da2e3ebdSchin "[L:logical|follow?Follow symbolic links when traversing directories. The" 92*da2e3ebdSchin " default is determined by \bgetconf PATH_RESOLVE\b.]" 93*da2e3ebdSchin "[H:metaphysical?Follow command argument symbolic links, otherwise don't" 94*da2e3ebdSchin " follow symbolic links when traversing directories. The default is" 95*da2e3ebdSchin " determined by \bgetconf PATH_RESOLVE\b.]" 96*da2e3ebdSchin "[P:physical?Don't follow symbolic links when traversing directories. The" 97*da2e3ebdSchin " default is determined by \bgetconf PATH_RESOLVE\b.]" 98*da2e3ebdSchin 99*da2e3ebdSchin "\n" 100*da2e3ebdSchin "\n[ file ... ]\n" 101*da2e3ebdSchin "\n" 102*da2e3ebdSchin 103*da2e3ebdSchin "[+SEE ALSO?\bgetconf\b(1), \btw\b(1), \buuencode\b(1)]" 104*da2e3ebdSchin ; 105*da2e3ebdSchin 106*da2e3ebdSchin #include <cmd.h> 107*da2e3ebdSchin #include <sum.h> 108*da2e3ebdSchin #include <ls.h> 109*da2e3ebdSchin #include <fts.h> 110*da2e3ebdSchin #include <error.h> 111*da2e3ebdSchin 112*da2e3ebdSchin typedef struct State_s /* program state */ 113*da2e3ebdSchin { 114*da2e3ebdSchin int all; /* list all items */ 115*da2e3ebdSchin Sfio_t* check; /* check previous output */ 116*da2e3ebdSchin gid_t gid; /* caller gid */ 117*da2e3ebdSchin int header; /* list method on output */ 118*da2e3ebdSchin int list; /* list file name too */ 119*da2e3ebdSchin Sum_t* oldsum; /* previous sum method */ 120*da2e3ebdSchin int permissions; /* include mode,uer,group */ 121*da2e3ebdSchin int haveperm; /* permissions in the input */ 122*da2e3ebdSchin int recursive; /* recursively descend dirs */ 123*da2e3ebdSchin unsigned long size; /* combined size of all files */ 124*da2e3ebdSchin int silent; /* silent check, 0 exit if ok */ 125*da2e3ebdSchin int (*sort)(FTSENT* const*, FTSENT* const*); 126*da2e3ebdSchin Sum_t* sum; /* sum method */ 127*da2e3ebdSchin int text; /* \r\n == \n */ 128*da2e3ebdSchin int total; /* list totals only */ 129*da2e3ebdSchin uid_t uid; /* caller uid */ 130*da2e3ebdSchin int warn; /* invalid check line warnings */ 131*da2e3ebdSchin } State_t; 132*da2e3ebdSchin 133*da2e3ebdSchin static void verify(State_t*, char*, char*, Sfio_t*); 134*da2e3ebdSchin 135*da2e3ebdSchin /* 136*da2e3ebdSchin * open path for read mode 137*da2e3ebdSchin */ 138*da2e3ebdSchin 139*da2e3ebdSchin static Sfio_t* 140*da2e3ebdSchin openfile(const char* path, const char* mode) 141*da2e3ebdSchin { 142*da2e3ebdSchin Sfio_t* sp; 143*da2e3ebdSchin 144*da2e3ebdSchin if (!path || streq(path, "-") || streq(path, "/dev/stdin") || streq(path, "/dev/fd/0")) 145*da2e3ebdSchin { 146*da2e3ebdSchin sp = sfstdin; 147*da2e3ebdSchin sfopen(sp, NiL, mode); 148*da2e3ebdSchin } 149*da2e3ebdSchin else if (!(sp = sfopen(NiL, path, mode))) 150*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot read", path); 151*da2e3ebdSchin return sp; 152*da2e3ebdSchin } 153*da2e3ebdSchin 154*da2e3ebdSchin /* 155*da2e3ebdSchin * close an openfile() stream 156*da2e3ebdSchin */ 157*da2e3ebdSchin 158*da2e3ebdSchin static int 159*da2e3ebdSchin closefile(Sfio_t* sp) 160*da2e3ebdSchin { 161*da2e3ebdSchin return sp == sfstdin ? 0 : sfclose(sp); 162*da2e3ebdSchin } 163*da2e3ebdSchin 164*da2e3ebdSchin /* 165*da2e3ebdSchin * compute and print sum on an open file 166*da2e3ebdSchin */ 167*da2e3ebdSchin 168*da2e3ebdSchin static void 169*da2e3ebdSchin pr(State_t* state, Sfio_t* op, Sfio_t* ip, char* file, int perm, struct stat* st, Sfio_t* check) 170*da2e3ebdSchin { 171*da2e3ebdSchin register char* p; 172*da2e3ebdSchin register char* r; 173*da2e3ebdSchin register char* e; 174*da2e3ebdSchin register int peek; 175*da2e3ebdSchin struct stat ss; 176*da2e3ebdSchin 177*da2e3ebdSchin if (check) 178*da2e3ebdSchin { 179*da2e3ebdSchin state->oldsum = state->sum; 180*da2e3ebdSchin while (p = sfgetr(ip, '\n', 1)) 181*da2e3ebdSchin verify(state, p, file, check); 182*da2e3ebdSchin state->sum = state->oldsum; 183*da2e3ebdSchin if (state->warn && !sfeof(ip)) 184*da2e3ebdSchin error(2, "%s: last line incomplete", file); 185*da2e3ebdSchin return; 186*da2e3ebdSchin } 187*da2e3ebdSchin suminit(state->sum); 188*da2e3ebdSchin if (state->text) 189*da2e3ebdSchin { 190*da2e3ebdSchin peek = 0; 191*da2e3ebdSchin while (p = sfreserve(ip, SF_UNBOUND, 0)) 192*da2e3ebdSchin { 193*da2e3ebdSchin e = p + sfvalue(ip); 194*da2e3ebdSchin if (peek) 195*da2e3ebdSchin { 196*da2e3ebdSchin peek = 0; 197*da2e3ebdSchin if (*p != '\n') 198*da2e3ebdSchin sumblock(state->sum, "\r", 1); 199*da2e3ebdSchin } 200*da2e3ebdSchin while (r = memchr(p, '\r', e - p)) 201*da2e3ebdSchin { 202*da2e3ebdSchin if (++r >= e) 203*da2e3ebdSchin { 204*da2e3ebdSchin e--; 205*da2e3ebdSchin peek = 1; 206*da2e3ebdSchin break; 207*da2e3ebdSchin } 208*da2e3ebdSchin sumblock(state->sum, p, r - p - (*r == '\n')); 209*da2e3ebdSchin p = r; 210*da2e3ebdSchin } 211*da2e3ebdSchin sumblock(state->sum, p, e - p); 212*da2e3ebdSchin } 213*da2e3ebdSchin if (peek) 214*da2e3ebdSchin sumblock(state->sum, "\r", 1); 215*da2e3ebdSchin } 216*da2e3ebdSchin else 217*da2e3ebdSchin while (p = sfreserve(ip, SF_UNBOUND, 0)) 218*da2e3ebdSchin sumblock(state->sum, p, sfvalue(ip)); 219*da2e3ebdSchin if (sfvalue(ip)) 220*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: read error", file); 221*da2e3ebdSchin sumdone(state->sum); 222*da2e3ebdSchin if (!state->total || state->all) 223*da2e3ebdSchin { 224*da2e3ebdSchin sumprint(state->sum, op, SUM_SIZE|SUM_SCALE); 225*da2e3ebdSchin if (perm >= 0) 226*da2e3ebdSchin { 227*da2e3ebdSchin if (perm) 228*da2e3ebdSchin { 229*da2e3ebdSchin if (!st && fstat(sffileno(ip), st = &ss)) 230*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot stat", file); 231*da2e3ebdSchin else 232*da2e3ebdSchin sfprintf(sfstdout, " %04o %s %s", 233*da2e3ebdSchin modex(st->st_mode & S_IPERM), 234*da2e3ebdSchin (st->st_uid != state->uid && ((st->st_mode & S_ISUID) || (st->st_mode & S_IRUSR) && !(st->st_mode & (S_IRGRP|S_IROTH)) || (st->st_mode & S_IXUSR) && !(st->st_mode & (S_IXGRP|S_IXOTH)))) ? fmtuid(st->st_uid) : "-", 235*da2e3ebdSchin (st->st_gid != state->gid && ((st->st_mode & S_ISGID) || (st->st_mode & S_IRGRP) && !(st->st_mode & S_IROTH) || (st->st_mode & S_IXGRP) && !(st->st_mode & S_IXOTH))) ? fmtgid(st->st_gid) : "-"); 236*da2e3ebdSchin } 237*da2e3ebdSchin if (ip != sfstdin) 238*da2e3ebdSchin sfprintf(op, " %s", file); 239*da2e3ebdSchin sfputc(op, '\n'); 240*da2e3ebdSchin } 241*da2e3ebdSchin } 242*da2e3ebdSchin } 243*da2e3ebdSchin 244*da2e3ebdSchin /* 245*da2e3ebdSchin * verify previous sum output 246*da2e3ebdSchin */ 247*da2e3ebdSchin 248*da2e3ebdSchin static void 249*da2e3ebdSchin verify(State_t* state, register char* s, char* check, Sfio_t* rp) 250*da2e3ebdSchin { 251*da2e3ebdSchin register char* t; 252*da2e3ebdSchin char* e; 253*da2e3ebdSchin char* file; 254*da2e3ebdSchin int attr; 255*da2e3ebdSchin int mode; 256*da2e3ebdSchin int uid; 257*da2e3ebdSchin int gid; 258*da2e3ebdSchin Sfio_t* sp; 259*da2e3ebdSchin struct stat st; 260*da2e3ebdSchin 261*da2e3ebdSchin if (!*s || *s == '#' && (!*(s + 1) || *(s + 1) == ' ' || *(s + 1) == '\t')) 262*da2e3ebdSchin return; 263*da2e3ebdSchin if (t = strchr(s, ' ')) 264*da2e3ebdSchin { 265*da2e3ebdSchin if ((t - s) > 10 || !(file = strchr(t + 1, ' '))) 266*da2e3ebdSchin file = t; 267*da2e3ebdSchin *file++ = 0; 268*da2e3ebdSchin attr = 0; 269*da2e3ebdSchin if ((mode = strtol(file, &e, 8)) && *e == ' ' && (e - file) == 4) 270*da2e3ebdSchin { 271*da2e3ebdSchin mode = modei(mode); 272*da2e3ebdSchin if (t = strchr(++e, ' ')) 273*da2e3ebdSchin { 274*da2e3ebdSchin if (*e == '-' && (t - e) == 1) 275*da2e3ebdSchin uid = -1; 276*da2e3ebdSchin else 277*da2e3ebdSchin { 278*da2e3ebdSchin *t = 0; 279*da2e3ebdSchin uid = struid(e); 280*da2e3ebdSchin *t = ' '; 281*da2e3ebdSchin } 282*da2e3ebdSchin if (e = strchr(++t, ' ')) 283*da2e3ebdSchin { 284*da2e3ebdSchin if (*t == '-' && (e - t) == 1) 285*da2e3ebdSchin gid = -1; 286*da2e3ebdSchin else 287*da2e3ebdSchin { 288*da2e3ebdSchin *e = 0; 289*da2e3ebdSchin gid = struid(t); 290*da2e3ebdSchin *e = ' '; 291*da2e3ebdSchin } 292*da2e3ebdSchin file = e + 1; 293*da2e3ebdSchin attr = 1; 294*da2e3ebdSchin } 295*da2e3ebdSchin } 296*da2e3ebdSchin } 297*da2e3ebdSchin if (sp = openfile(file, "rb")) 298*da2e3ebdSchin { 299*da2e3ebdSchin pr(state, rp, sp, file, -1, NiL, NiL); 300*da2e3ebdSchin if (!(t = sfstruse(rp))) 301*da2e3ebdSchin error(ERROR_SYSTEM|3, "out of space"); 302*da2e3ebdSchin if (!streq(s, t)) 303*da2e3ebdSchin { 304*da2e3ebdSchin if (state->silent) 305*da2e3ebdSchin error_info.errors++; 306*da2e3ebdSchin else 307*da2e3ebdSchin error(2, "%s: checksum changed", file); 308*da2e3ebdSchin } 309*da2e3ebdSchin else if (attr) 310*da2e3ebdSchin { 311*da2e3ebdSchin if (fstat(sffileno(sp), &st)) 312*da2e3ebdSchin { 313*da2e3ebdSchin if (state->silent) 314*da2e3ebdSchin error_info.errors++; 315*da2e3ebdSchin else 316*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot stat", file); 317*da2e3ebdSchin } 318*da2e3ebdSchin else 319*da2e3ebdSchin { 320*da2e3ebdSchin if (uid < 0 || uid == st.st_uid) 321*da2e3ebdSchin uid = -1; 322*da2e3ebdSchin else if (!state->permissions) 323*da2e3ebdSchin { 324*da2e3ebdSchin if (state->silent) 325*da2e3ebdSchin error_info.errors++; 326*da2e3ebdSchin else 327*da2e3ebdSchin error(2, "%s: uid should be %s", file, fmtuid(uid)); 328*da2e3ebdSchin } 329*da2e3ebdSchin if (gid < 0 || gid == st.st_gid) 330*da2e3ebdSchin gid = -1; 331*da2e3ebdSchin else if (!state->permissions) 332*da2e3ebdSchin { 333*da2e3ebdSchin if (state->silent) 334*da2e3ebdSchin error_info.errors++; 335*da2e3ebdSchin else 336*da2e3ebdSchin error(2, "%s: gid should be %s", file, fmtgid(gid)); 337*da2e3ebdSchin } 338*da2e3ebdSchin if (state->permissions && (uid >= 0 || gid >= 0)) 339*da2e3ebdSchin { 340*da2e3ebdSchin if (chown(file, uid, gid) < 0) 341*da2e3ebdSchin { 342*da2e3ebdSchin if (uid < 0) 343*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot change group to %s", file, fmtgid(gid)); 344*da2e3ebdSchin else if (gid < 0) 345*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot change user to %s", file, fmtuid(uid)); 346*da2e3ebdSchin else 347*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot change user to %s and group to %s", file, fmtuid(uid), fmtgid(gid)); 348*da2e3ebdSchin } 349*da2e3ebdSchin else 350*da2e3ebdSchin { 351*da2e3ebdSchin if (uid < 0) 352*da2e3ebdSchin error(1, "%s: changed group to %s", file, fmtgid(gid)); 353*da2e3ebdSchin else if (gid < 0) 354*da2e3ebdSchin error(1, "%s: changed user to %s", file, fmtuid(uid)); 355*da2e3ebdSchin else 356*da2e3ebdSchin error(1, "%s: changed user to %s and group to %s", file, fmtuid(uid), fmtgid(gid)); 357*da2e3ebdSchin } 358*da2e3ebdSchin } 359*da2e3ebdSchin if ((st.st_mode & S_IPERM) ^ mode) 360*da2e3ebdSchin { 361*da2e3ebdSchin if (state->permissions) 362*da2e3ebdSchin { 363*da2e3ebdSchin if (chmod(file, mode) < 0) 364*da2e3ebdSchin error(ERROR_SYSTEM|2, "%s: cannot change mode to %s", file, fmtmode(mode, 0)); 365*da2e3ebdSchin else 366*da2e3ebdSchin error(ERROR_SYSTEM|1, "%s: changed mode to %s", file, fmtmode(mode, 0)); 367*da2e3ebdSchin } 368*da2e3ebdSchin else if (state->silent) 369*da2e3ebdSchin error_info.errors++; 370*da2e3ebdSchin else 371*da2e3ebdSchin error(2, "%s: mode should be %s", file, fmtmode(mode, 0)); 372*da2e3ebdSchin } 373*da2e3ebdSchin } 374*da2e3ebdSchin } 375*da2e3ebdSchin closefile(sp); 376*da2e3ebdSchin } 377*da2e3ebdSchin } 378*da2e3ebdSchin else if (strneq(s, "method=", 7)) 379*da2e3ebdSchin { 380*da2e3ebdSchin s += 7; 381*da2e3ebdSchin if (state->sum != state->oldsum) 382*da2e3ebdSchin sumclose(state->sum); 383*da2e3ebdSchin if (!(state->sum = sumopen(s))) 384*da2e3ebdSchin error(3, "%s: %s: unknown checksum method", check, s); 385*da2e3ebdSchin } 386*da2e3ebdSchin else if (streq(s, "permissions")) 387*da2e3ebdSchin state->haveperm = 1; 388*da2e3ebdSchin else 389*da2e3ebdSchin error(1, "%s: %s: unknown option", check, s); 390*da2e3ebdSchin } 391*da2e3ebdSchin 392*da2e3ebdSchin /* 393*da2e3ebdSchin * sum the list of files in lp 394*da2e3ebdSchin */ 395*da2e3ebdSchin 396*da2e3ebdSchin static void 397*da2e3ebdSchin list(State_t* state, register Sfio_t* lp) 398*da2e3ebdSchin { 399*da2e3ebdSchin register char* file; 400*da2e3ebdSchin register Sfio_t* sp; 401*da2e3ebdSchin 402*da2e3ebdSchin while (file = sfgetr(lp, '\n', 1)) 403*da2e3ebdSchin if (sp = openfile(file, state->check ? "rt" : "rb")) 404*da2e3ebdSchin { 405*da2e3ebdSchin pr(state, sfstdout, sp, file, state->permissions, NiL, state->check); 406*da2e3ebdSchin closefile(sp); 407*da2e3ebdSchin } 408*da2e3ebdSchin } 409*da2e3ebdSchin 410*da2e3ebdSchin /* 411*da2e3ebdSchin * order child entries 412*da2e3ebdSchin */ 413*da2e3ebdSchin 414*da2e3ebdSchin static int 415*da2e3ebdSchin order(FTSENT* const* f1, FTSENT* const* f2) 416*da2e3ebdSchin { 417*da2e3ebdSchin return strcoll((*f1)->fts_name, (*f2)->fts_name); 418*da2e3ebdSchin } 419*da2e3ebdSchin 420*da2e3ebdSchin /* 421*da2e3ebdSchin * optget() info discipline function 422*da2e3ebdSchin */ 423*da2e3ebdSchin 424*da2e3ebdSchin static int 425*da2e3ebdSchin optinfo(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp) 426*da2e3ebdSchin { 427*da2e3ebdSchin if (streq(s, "methods")) 428*da2e3ebdSchin return sumusage(sp); 429*da2e3ebdSchin return 0; 430*da2e3ebdSchin } 431*da2e3ebdSchin 432*da2e3ebdSchin int 433*da2e3ebdSchin b_cksum(int argc, register char** argv, void* context) 434*da2e3ebdSchin { 435*da2e3ebdSchin register int flags; 436*da2e3ebdSchin register char* s; 437*da2e3ebdSchin char* file; 438*da2e3ebdSchin Sfio_t* sp; 439*da2e3ebdSchin FTS* fts; 440*da2e3ebdSchin FTSENT* ent; 441*da2e3ebdSchin Optdisc_t optdisc; 442*da2e3ebdSchin State_t state; 443*da2e3ebdSchin 444*da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); 445*da2e3ebdSchin memset(&state, 0, sizeof(state)); 446*da2e3ebdSchin setlocale(LC_ALL, ""); 447*da2e3ebdSchin flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER | FTS_NOSEEDOTDIR; 448*da2e3ebdSchin state.warn = 1; 449*da2e3ebdSchin optinit(&optdisc, optinfo); 450*da2e3ebdSchin for (;;) 451*da2e3ebdSchin { 452*da2e3ebdSchin switch (optget(argv, usage)) 453*da2e3ebdSchin { 454*da2e3ebdSchin case 'a': 455*da2e3ebdSchin state.all = 1; 456*da2e3ebdSchin continue; 457*da2e3ebdSchin case 'b': 458*da2e3ebdSchin state.text = 0; 459*da2e3ebdSchin continue; 460*da2e3ebdSchin case 'c': 461*da2e3ebdSchin if (!(state.check = sfstropen())) 462*da2e3ebdSchin error(3, "out of space [check]"); 463*da2e3ebdSchin continue; 464*da2e3ebdSchin case 'h': 465*da2e3ebdSchin state.header = 1; 466*da2e3ebdSchin continue; 467*da2e3ebdSchin case 'l': 468*da2e3ebdSchin state.list = 1; 469*da2e3ebdSchin continue; 470*da2e3ebdSchin case 'p': 471*da2e3ebdSchin state.permissions = 1; 472*da2e3ebdSchin continue; 473*da2e3ebdSchin case 'r': 474*da2e3ebdSchin flags &= ~FTS_TOP; 475*da2e3ebdSchin state.recursive = 1; 476*da2e3ebdSchin state.sort = order; 477*da2e3ebdSchin continue; 478*da2e3ebdSchin case 's': 479*da2e3ebdSchin state.silent = opt_info.num; 480*da2e3ebdSchin continue; 481*da2e3ebdSchin case 't': 482*da2e3ebdSchin state.total = 1; 483*da2e3ebdSchin continue; 484*da2e3ebdSchin case 'w': 485*da2e3ebdSchin state.warn = opt_info.num; 486*da2e3ebdSchin continue; 487*da2e3ebdSchin case 'x': 488*da2e3ebdSchin if (!(state.sum = sumopen(opt_info.arg))) 489*da2e3ebdSchin error(3, "%s: unknown checksum method", opt_info.arg); 490*da2e3ebdSchin continue; 491*da2e3ebdSchin case 'H': 492*da2e3ebdSchin flags |= FTS_META|FTS_PHYSICAL; 493*da2e3ebdSchin continue; 494*da2e3ebdSchin case 'L': 495*da2e3ebdSchin flags &= ~(FTS_META|FTS_PHYSICAL); 496*da2e3ebdSchin continue; 497*da2e3ebdSchin case 'P': 498*da2e3ebdSchin flags &= ~FTS_META; 499*da2e3ebdSchin flags |= FTS_PHYSICAL; 500*da2e3ebdSchin continue; 501*da2e3ebdSchin case 'T': 502*da2e3ebdSchin state.text = 1; 503*da2e3ebdSchin continue; 504*da2e3ebdSchin case '?': 505*da2e3ebdSchin error(ERROR_USAGE|4, "%s", opt_info.arg); 506*da2e3ebdSchin break; 507*da2e3ebdSchin case ':': 508*da2e3ebdSchin error(2, "%s", opt_info.arg); 509*da2e3ebdSchin break; 510*da2e3ebdSchin } 511*da2e3ebdSchin break; 512*da2e3ebdSchin } 513*da2e3ebdSchin argv += opt_info.index; 514*da2e3ebdSchin if (error_info.errors) 515*da2e3ebdSchin error(ERROR_USAGE|4, "%s", optusage(NiL)); 516*da2e3ebdSchin 517*da2e3ebdSchin /* 518*da2e3ebdSchin * check the method 519*da2e3ebdSchin */ 520*da2e3ebdSchin 521*da2e3ebdSchin if (!state.sum && !(state.sum = sumopen(error_info.id)) && !(state.sum = sumopen(astconf("UNIVERSE", NiL, NiL)))) 522*da2e3ebdSchin state.sum = sumopen(NiL); 523*da2e3ebdSchin 524*da2e3ebdSchin /* 525*da2e3ebdSchin * do it 526*da2e3ebdSchin */ 527*da2e3ebdSchin 528*da2e3ebdSchin if (state.permissions) 529*da2e3ebdSchin { 530*da2e3ebdSchin state.uid = geteuid(); 531*da2e3ebdSchin state.gid = getegid(); 532*da2e3ebdSchin state.silent = 0; 533*da2e3ebdSchin } 534*da2e3ebdSchin if (!state.check && (state.header || state.permissions)) 535*da2e3ebdSchin { 536*da2e3ebdSchin sfprintf(sfstdout, "method=%s\n", state.sum->name); 537*da2e3ebdSchin if (state.permissions) 538*da2e3ebdSchin sfprintf(sfstdout, "permissions\n"); 539*da2e3ebdSchin } 540*da2e3ebdSchin if (state.list) 541*da2e3ebdSchin { 542*da2e3ebdSchin if (*argv) 543*da2e3ebdSchin { 544*da2e3ebdSchin while (file = *argv++) 545*da2e3ebdSchin if (sp = openfile(file, "rt")) 546*da2e3ebdSchin { 547*da2e3ebdSchin list(&state, sp); 548*da2e3ebdSchin closefile(sp); 549*da2e3ebdSchin } 550*da2e3ebdSchin } 551*da2e3ebdSchin else if (sp = openfile(NiL, "rt")) 552*da2e3ebdSchin { 553*da2e3ebdSchin list(&state, sp); 554*da2e3ebdSchin closefile(sp); 555*da2e3ebdSchin } 556*da2e3ebdSchin } 557*da2e3ebdSchin else if (!*argv && !state.recursive) 558*da2e3ebdSchin pr(&state, sfstdout, sfstdin, "/dev/stdin", state.permissions, NiL, state.check); 559*da2e3ebdSchin else if (!(fts = fts_open(argv, flags, state.sort))) 560*da2e3ebdSchin error(ERROR_system(1), "%s: not found", *argv); 561*da2e3ebdSchin else 562*da2e3ebdSchin { 563*da2e3ebdSchin while (!cmdquit() && (ent = fts_read(fts))) 564*da2e3ebdSchin switch (ent->fts_info) 565*da2e3ebdSchin { 566*da2e3ebdSchin case FTS_SL: 567*da2e3ebdSchin if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1) 568*da2e3ebdSchin fts_set(NiL, ent, FTS_FOLLOW); 569*da2e3ebdSchin break; 570*da2e3ebdSchin case FTS_F: 571*da2e3ebdSchin if (sp = openfile(ent->fts_accpath, "rb")) 572*da2e3ebdSchin { 573*da2e3ebdSchin pr(&state, sfstdout, sp, ent->fts_path, state.permissions, ent->fts_statp, state.check); 574*da2e3ebdSchin closefile(sp); 575*da2e3ebdSchin } 576*da2e3ebdSchin break; 577*da2e3ebdSchin case FTS_DC: 578*da2e3ebdSchin error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath); 579*da2e3ebdSchin break; 580*da2e3ebdSchin case FTS_DNR: 581*da2e3ebdSchin error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath); 582*da2e3ebdSchin break; 583*da2e3ebdSchin case FTS_DNX: 584*da2e3ebdSchin error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath); 585*da2e3ebdSchin break; 586*da2e3ebdSchin case FTS_NS: 587*da2e3ebdSchin error(ERROR_system(0), "%s: not found", ent->fts_accpath); 588*da2e3ebdSchin break; 589*da2e3ebdSchin } 590*da2e3ebdSchin fts_close(fts); 591*da2e3ebdSchin } 592*da2e3ebdSchin if (state.total) 593*da2e3ebdSchin { 594*da2e3ebdSchin sumprint(state.sum, sfstdout, SUM_TOTAL|SUM_SIZE|SUM_SCALE); 595*da2e3ebdSchin sfputc(sfstdout, '\n'); 596*da2e3ebdSchin } 597*da2e3ebdSchin sumclose(state.sum); 598*da2e3ebdSchin return error_info.errors != 0; 599*da2e3ebdSchin } 600