1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4*b30d1939SAndy Fiddaman *          Copyright (c) 1992-2012 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6*b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10*b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11*b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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  */
29da2e3ebdSchin static const char usage[] =
30*b30d1939SAndy Fiddaman "[-?\n@(#)$Id: sum (AT&T Research) 2012-04-20 $\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 "}"
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.]"
105da2e3ebdSchin "\n"
106da2e3ebdSchin "\n[ file ... ]\n"
107da2e3ebdSchin "\n"
109da2e3ebdSchin "[+SEE ALSO?\bgetconf\b(1), \btw\b(1), \buuencode\b(1)]"
110da2e3ebdSchin ;
112da2e3ebdSchin #include <cmd.h>
113da2e3ebdSchin #include <sum.h>
114da2e3ebdSchin #include <ls.h>
1157c2fbfb3SApril Chin #include <modex.h>
1163e14f97fSRoger A. Faulkner #include <fts_fix.h>
117da2e3ebdSchin #include <error.h>
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;
142da2e3ebdSchin static void	verify(State_t*, char*, char*, Sfio_t*);
144da2e3ebdSchin /*
145da2e3ebdSchin  * open path for read mode
146da2e3ebdSchin  */
148da2e3ebdSchin static Sfio_t*
openfile(const char * path,const char * mode)149da2e3ebdSchin openfile(const char* path, const char* mode)
150da2e3ebdSchin {
151da2e3ebdSchin 	Sfio_t*		sp;
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 }
163da2e3ebdSchin /*
164da2e3ebdSchin  * close an openfile() stream
165da2e3ebdSchin  */
167da2e3ebdSchin static int
closefile(Sfio_t * sp)168da2e3ebdSchin closefile(Sfio_t* sp)
169da2e3ebdSchin {
170da2e3ebdSchin 	return sp == sfstdin ? 0 : sfclose(sp);
171da2e3ebdSchin }
173da2e3ebdSchin /*
174da2e3ebdSchin  * compute and print sum on an open file
175da2e3ebdSchin  */
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;
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 }
253da2e3ebdSchin /*
254da2e3ebdSchin  * verify previous sum output
255da2e3ebdSchin  */
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;
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 }
401da2e3ebdSchin /*
402da2e3ebdSchin  * sum the list of files in lp
403da2e3ebdSchin  */
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;
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 }
419da2e3ebdSchin /*
420da2e3ebdSchin  * order child entries
421da2e3ebdSchin  */
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 }
429da2e3ebdSchin /*
430da2e3ebdSchin  * optget() info discipline function
431da2e3ebdSchin  */
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 }
441da2e3ebdSchin int
b_cksum(int argc,register char ** argv,Shbltin_t * context)442*b30d1939SAndy Fiddaman b_cksum(int argc, register char** argv, Shbltin_t* 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;
454da2e3ebdSchin 	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
455da2e3ebdSchin 	memset(&state, 0, sizeof(state));
456*b30d1939SAndy Fiddaman 	flags = fts_flags() | FTS_META | 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));
543da2e3ebdSchin 	/*
544da2e3ebdSchin 	 * check the method
545da2e3ebdSchin 	 */
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);
552da2e3ebdSchin 	/*
553da2e3ebdSchin 	 * do it
554da2e3ebdSchin 	 */
55634f9b3eeSRoland Mainz 	if (logical)
5573e14f97fSRoger A. Faulkner 	{
55834f9b3eeSRoland Mainz 		flags &= ~(FTS_META|FTS_PHYSICAL);
5593e14f97fSRoger A. Faulkner 		flags |= FTS_SEEDOTDIR;
5603e14f97fSRoger 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:
611*b30d1939SAndy Fiddaman 				error(ERROR_warn(0), "%s: directory causes cycle", ent->fts_path);
612da2e3ebdSchin 				break;
613da2e3ebdSchin 			case FTS_DNR:
614*b30d1939SAndy Fiddaman 				error(ERROR_system(0), "%s: cannot read directory", ent->fts_path);
615da2e3ebdSchin 				break;
616da2e3ebdSchin 			case FTS_DNX:
617*b30d1939SAndy Fiddaman 				error(ERROR_system(0), "%s: cannot search directory", ent->fts_path);
618da2e3ebdSchin 				break;
619da2e3ebdSchin 			case FTS_NS:
620*b30d1939SAndy Fiddaman 				error(ERROR_system(0), "%s: not found", ent->fts_path);
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 }