1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1992-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril 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*3e14f97fSRoger A. Faulkner "[-?\n@(#)$Id: sum (AT&T Research) 2009-11-28 $\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.]"
537c2fbfb3SApril Chin "[B:scale?Block count scale (bytes per block) override for methods that"
547c2fbfb3SApril 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.]"
797c2fbfb3SApril Chin "[R:recursive?Recursively checksum the contents of directories.]"
807c2fbfb3SApril 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.]"
1007c2fbfb3SApril Chin "[r:bsd?Equivalent to \b--method=bsd --scale=512\b for compatibility with"
1017c2fbfb3SApril Chin " other \bsum\b(1) implementations.]"
1027c2fbfb3SApril Chin "[s:sysv?Equivalent to \b--method=sys5\b for compatibility with other"
1037c2fbfb3SApril 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>
1157c2fbfb3SApril Chin #include <modex.h>
116*3e14f97fSRoger A. Faulkner #include <fts_fix.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 */
1237c2fbfb3SApril 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 */
1317c2fbfb3SApril 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*
openfile(const char * path,const char * mode)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
closefile(Sfio_t * sp)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
pr(State_t * state,Sfio_t * op,Sfio_t * ip,char * file,int perm,struct stat * st,Sfio_t * check)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 {
2337c2fbfb3SApril 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
verify(State_t * state,register char * s,char * check,Sfio_t * rp)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
list(State_t * state,register Sfio_t * lp)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
order(FTSENT * const * f1,FTSENT * const * f2)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
optinfo(Opt_t * op,Sfio_t * sp,const char * s,Optdisc_t * dp)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
b_cksum(int argc,register char ** argv,void * context)442da2e3ebdSchin b_cksum(int argc, register char** argv, void* context)
443da2e3ebdSchin {
444da2e3ebdSchin register int flags;
445da2e3ebdSchin char* file;
4467c2fbfb3SApril Chin char* method;
447da2e3ebdSchin Sfio_t* sp;
448da2e3ebdSchin FTS* fts;
449da2e3ebdSchin FTSENT* ent;
45034f9b3eeSRoland Mainz int logical;
451da2e3ebdSchin Optdisc_t optdisc;
452da2e3ebdSchin State_t state;
453da2e3ebdSchin
454da2e3ebdSchin cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
455da2e3ebdSchin memset(&state, 0, sizeof(state));
456*3e14f97fSRoger A. Faulkner flags = fts_flags() | FTS_TOP | FTS_NOPOSTORDER;
4577c2fbfb3SApril Chin state.flags = SUM_SIZE;
458da2e3ebdSchin state.warn = 1;
45934f9b3eeSRoland Mainz logical = 1;
4607c2fbfb3SApril Chin method = 0;
461da2e3ebdSchin optinit(&optdisc, optinfo);
462da2e3ebdSchin for (;;)
463da2e3ebdSchin {
464da2e3ebdSchin switch (optget(argv, usage))
465da2e3ebdSchin {
466da2e3ebdSchin case 'a':
467da2e3ebdSchin state.all = 1;
468da2e3ebdSchin continue;
469da2e3ebdSchin case 'b':
470da2e3ebdSchin state.text = 0;
471da2e3ebdSchin continue;
4727c2fbfb3SApril Chin case 'B':
4737c2fbfb3SApril Chin state.scale = opt_info.num;
4747c2fbfb3SApril Chin continue;
475da2e3ebdSchin case 'c':
476da2e3ebdSchin if (!(state.check = sfstropen()))
477da2e3ebdSchin error(3, "out of space [check]");
478da2e3ebdSchin continue;
479da2e3ebdSchin case 'h':
480da2e3ebdSchin state.header = 1;
481da2e3ebdSchin continue;
482da2e3ebdSchin case 'l':
483da2e3ebdSchin state.list = 1;
484da2e3ebdSchin continue;
485da2e3ebdSchin case 'p':
486da2e3ebdSchin state.permissions = 1;
487da2e3ebdSchin continue;
488da2e3ebdSchin case 'r':
4897c2fbfb3SApril Chin method = "bsd";
4907c2fbfb3SApril Chin state.scale = 512;
4917c2fbfb3SApril Chin state.flags |= SUM_LEGACY;
4927c2fbfb3SApril Chin continue;
4937c2fbfb3SApril Chin case 'R':
494da2e3ebdSchin flags &= ~FTS_TOP;
495da2e3ebdSchin state.recursive = 1;
496da2e3ebdSchin state.sort = order;
49734f9b3eeSRoland Mainz logical = 0;
498da2e3ebdSchin continue;
499da2e3ebdSchin case 's':
5007c2fbfb3SApril Chin method = "sys5";
5017c2fbfb3SApril Chin continue;
5027c2fbfb3SApril Chin case 'S':
503da2e3ebdSchin state.silent = opt_info.num;
504da2e3ebdSchin continue;
505da2e3ebdSchin case 't':
506da2e3ebdSchin state.total = 1;
507da2e3ebdSchin continue;
508da2e3ebdSchin case 'w':
509da2e3ebdSchin state.warn = opt_info.num;
510da2e3ebdSchin continue;
511da2e3ebdSchin case 'x':
5127c2fbfb3SApril Chin method = opt_info.arg;
513da2e3ebdSchin continue;
514da2e3ebdSchin case 'H':
515da2e3ebdSchin flags |= FTS_META|FTS_PHYSICAL;
51634f9b3eeSRoland Mainz logical = 0;
517da2e3ebdSchin continue;
518da2e3ebdSchin case 'L':
519da2e3ebdSchin flags &= ~(FTS_META|FTS_PHYSICAL);
52034f9b3eeSRoland Mainz logical = 0;
521da2e3ebdSchin continue;
522da2e3ebdSchin case 'P':
523da2e3ebdSchin flags &= ~FTS_META;
524da2e3ebdSchin flags |= FTS_PHYSICAL;
52534f9b3eeSRoland Mainz logical = 0;
526da2e3ebdSchin continue;
527da2e3ebdSchin case 'T':
528da2e3ebdSchin state.text = 1;
529da2e3ebdSchin continue;
530da2e3ebdSchin case '?':
531da2e3ebdSchin error(ERROR_USAGE|4, "%s", opt_info.arg);
532da2e3ebdSchin break;
533da2e3ebdSchin case ':':
534da2e3ebdSchin error(2, "%s", opt_info.arg);
535da2e3ebdSchin break;
536da2e3ebdSchin }
537da2e3ebdSchin break;
538da2e3ebdSchin }
539da2e3ebdSchin argv += opt_info.index;
540da2e3ebdSchin if (error_info.errors)
541da2e3ebdSchin error(ERROR_USAGE|4, "%s", optusage(NiL));
542da2e3ebdSchin
543da2e3ebdSchin /*
544da2e3ebdSchin * check the method
545da2e3ebdSchin */
546da2e3ebdSchin
5477c2fbfb3SApril Chin if (method && !(state.sum = sumopen(method)))
5487c2fbfb3SApril Chin error(3, "%s: unknown checksum method", method);
549da2e3ebdSchin if (!state.sum && !(state.sum = sumopen(error_info.id)) && !(state.sum = sumopen(astconf("UNIVERSE", NiL, NiL))))
550da2e3ebdSchin state.sum = sumopen(NiL);
551da2e3ebdSchin
552da2e3ebdSchin /*
553da2e3ebdSchin * do it
554da2e3ebdSchin */
555da2e3ebdSchin
55634f9b3eeSRoland Mainz if (logical)
557*3e14f97fSRoger A. Faulkner {
55834f9b3eeSRoland Mainz flags &= ~(FTS_META|FTS_PHYSICAL);
559*3e14f97fSRoger A. Faulkner flags |= FTS_SEEDOTDIR;
560*3e14f97fSRoger A. Faulkner }
561da2e3ebdSchin if (state.permissions)
562da2e3ebdSchin {
563da2e3ebdSchin state.uid = geteuid();
564da2e3ebdSchin state.gid = getegid();
565da2e3ebdSchin state.silent = 0;
566da2e3ebdSchin }
567da2e3ebdSchin if (!state.check && (state.header || state.permissions))
568da2e3ebdSchin {
569da2e3ebdSchin sfprintf(sfstdout, "method=%s\n", state.sum->name);
570da2e3ebdSchin if (state.permissions)
571da2e3ebdSchin sfprintf(sfstdout, "permissions\n");
572da2e3ebdSchin }
573da2e3ebdSchin if (state.list)
574da2e3ebdSchin {
575da2e3ebdSchin if (*argv)
576da2e3ebdSchin {
577da2e3ebdSchin while (file = *argv++)
578da2e3ebdSchin if (sp = openfile(file, "rt"))
579da2e3ebdSchin {
580da2e3ebdSchin list(&state, sp);
581da2e3ebdSchin closefile(sp);
582da2e3ebdSchin }
583da2e3ebdSchin }
584da2e3ebdSchin else if (sp = openfile(NiL, "rt"))
585da2e3ebdSchin {
586da2e3ebdSchin list(&state, sp);
587da2e3ebdSchin closefile(sp);
588da2e3ebdSchin }
589da2e3ebdSchin }
590da2e3ebdSchin else if (!*argv && !state.recursive)
591da2e3ebdSchin pr(&state, sfstdout, sfstdin, "/dev/stdin", state.permissions, NiL, state.check);
592da2e3ebdSchin else if (!(fts = fts_open(argv, flags, state.sort)))
593da2e3ebdSchin error(ERROR_system(1), "%s: not found", *argv);
594da2e3ebdSchin else
595da2e3ebdSchin {
5967c2fbfb3SApril Chin while (!sh_checksig(context) && (ent = fts_read(fts)))
597da2e3ebdSchin switch (ent->fts_info)
598da2e3ebdSchin {
599da2e3ebdSchin case FTS_SL:
600da2e3ebdSchin if (!(flags & FTS_PHYSICAL) || (flags & FTS_META) && ent->fts_level == 1)
601da2e3ebdSchin fts_set(NiL, ent, FTS_FOLLOW);
602da2e3ebdSchin break;
603da2e3ebdSchin case FTS_F:
604da2e3ebdSchin if (sp = openfile(ent->fts_accpath, "rb"))
605da2e3ebdSchin {
606da2e3ebdSchin pr(&state, sfstdout, sp, ent->fts_path, state.permissions, ent->fts_statp, state.check);
607da2e3ebdSchin closefile(sp);
608da2e3ebdSchin }
609da2e3ebdSchin break;
610da2e3ebdSchin case FTS_DC:
611da2e3ebdSchin error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_accpath);
612da2e3ebdSchin break;
613da2e3ebdSchin case FTS_DNR:
614da2e3ebdSchin error(ERROR_system(0), "%s: cannot read directory", ent->fts_accpath);
615da2e3ebdSchin break;
616da2e3ebdSchin case FTS_DNX:
617da2e3ebdSchin error(ERROR_system(0), "%s: cannot search directory", ent->fts_accpath);
618da2e3ebdSchin break;
619da2e3ebdSchin case FTS_NS:
620da2e3ebdSchin error(ERROR_system(0), "%s: not found", ent->fts_accpath);
621da2e3ebdSchin break;
622da2e3ebdSchin }
623da2e3ebdSchin fts_close(fts);
624da2e3ebdSchin }
625da2e3ebdSchin if (state.total)
626da2e3ebdSchin {
6277c2fbfb3SApril Chin sumprint(state.sum, sfstdout, state.flags|SUM_TOTAL|SUM_SCALE, state.scale);
628da2e3ebdSchin sfputc(sfstdout, '\n');
629da2e3ebdSchin }
630da2e3ebdSchin sumclose(state.sum);
631da2e3ebdSchin return error_info.errors != 0;
632da2e3ebdSchin }
633