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