1 /*
2  * checksum.c - Adler-32 and CRC-32 checksumming program
3  *
4  * Copyright 2016 Eric Biggers
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #include <zlib.h>
29 
30 #include "prog_util.h"
31 
32 static const tchar *const optstring = T("Ahs:tZ");
33 
34 static void
show_usage(FILE * fp)35 show_usage(FILE *fp)
36 {
37 	fprintf(fp,
38 "Usage: %"TS" [-A] [-h] [-s SIZE] [-t] [-Z] [FILE]...\n"
39 "Calculate Adler-32 or CRC-32 checksums of the specified FILEs.\n"
40 "\n"
41 "Options:\n"
42 "  -A        use Adler-32 (default is CRC-32)\n"
43 "  -h        print this help\n"
44 "  -s SIZE   chunk size\n"
45 "  -t        show checksum speed, excluding I/O\n"
46 "  -Z        use zlib implementation instead of libdeflate\n",
47 	program_invocation_name);
48 }
49 
50 static u32
zlib_adler32(u32 adler,const void * buf,size_t len)51 zlib_adler32(u32 adler, const void *buf, size_t len)
52 {
53 	return adler32(adler, buf, len);
54 }
55 
56 static u32
zlib_crc32(u32 crc,const void * buf,size_t len)57 zlib_crc32(u32 crc, const void *buf, size_t len)
58 {
59 	return crc32(crc, buf, len);
60 }
61 
62 typedef u32 (*cksum_fn_t)(u32, const void *, size_t);
63 
64 static int
checksum_stream(struct file_stream * in,cksum_fn_t cksum,u32 * sum,void * buf,size_t bufsize,u64 * size_ret,u64 * elapsed_ret)65 checksum_stream(struct file_stream *in, cksum_fn_t cksum, u32 *sum,
66 		void *buf, size_t bufsize, u64 *size_ret, u64 *elapsed_ret)
67 {
68 	u64 size = 0;
69 	u64 elapsed = 0;
70 
71 	for (;;) {
72 		ssize_t ret;
73 		u64 start_time;
74 
75 		ret = xread(in, buf, bufsize);
76 		if (ret < 0)
77 			return ret;
78 		if (ret == 0)
79 			break;
80 
81 		size += ret;
82 		start_time = timer_ticks();
83 		*sum = cksum(*sum, buf, ret);
84 		elapsed += timer_ticks() - start_time;
85 	}
86 
87 	if (elapsed == 0)
88 		elapsed = 1;
89 	*size_ret = size;
90 	*elapsed_ret = elapsed;
91 	return 0;
92 }
93 
94 int
tmain(int argc,tchar * argv[])95 tmain(int argc, tchar *argv[])
96 {
97 	bool use_adler32 = false;
98 	bool use_zlib_impl = false;
99 	bool do_timing = false;
100 	void *buf;
101 	size_t bufsize = 131072;
102 	tchar *default_file_list[] = { NULL };
103 	cksum_fn_t cksum;
104 	int opt_char;
105 	int i;
106 	int ret;
107 
108 	program_invocation_name = get_filename(argv[0]);
109 
110 	while ((opt_char = tgetopt(argc, argv, optstring)) != -1) {
111 		switch (opt_char) {
112 		case 'A':
113 			use_adler32 = true;
114 			break;
115 		case 'h':
116 			show_usage(stdout);
117 			return 0;
118 		case 's':
119 			bufsize = tstrtoul(toptarg, NULL, 10);
120 			if (bufsize == 0) {
121 				msg("invalid chunk size: \"%"TS"\"", toptarg);
122 				return 1;
123 			}
124 			break;
125 		case 't':
126 			do_timing = true;
127 			break;
128 		case 'Z':
129 			use_zlib_impl = true;
130 			break;
131 		default:
132 			show_usage(stderr);
133 			return 1;
134 		}
135 	}
136 
137 	argc -= toptind;
138 	argv += toptind;
139 
140 	if (use_adler32) {
141 		if (use_zlib_impl)
142 			cksum = zlib_adler32;
143 		else
144 			cksum = libdeflate_adler32;
145 	} else {
146 		if (use_zlib_impl)
147 			cksum = zlib_crc32;
148 		else
149 			cksum = libdeflate_crc32;
150 	}
151 
152 	buf = xmalloc(bufsize);
153 	if (buf == NULL)
154 		return 1;
155 
156 	if (argc == 0) {
157 		argv = default_file_list;
158 		argc = ARRAY_LEN(default_file_list);
159 	} else {
160 		for (i = 0; i < argc; i++)
161 			if (argv[i][0] == '-' && argv[i][1] == '\0')
162 				argv[i] = NULL;
163 	}
164 
165 	for (i = 0; i < argc; i++) {
166 		struct file_stream in;
167 		u32 sum = cksum(0, NULL, 0);
168 		u64 size = 0;
169 		u64 elapsed = 0;
170 
171 		ret = xopen_for_read(argv[i], true, &in);
172 		if (ret != 0)
173 			goto out;
174 
175 		ret = checksum_stream(&in, cksum, &sum, buf, bufsize,
176 				      &size, &elapsed);
177 		if (ret == 0) {
178 			if (do_timing) {
179 				printf("%08"PRIx32"\t%"TS"\t"
180 				       "%"PRIu64" ms\t%"PRIu64" MB/s\n",
181 				       sum, in.name, timer_ticks_to_ms(elapsed),
182 				       timer_MB_per_s(size, elapsed));
183 			} else {
184 				printf("%08"PRIx32"\t%"TS"\t\n", sum, in.name);
185 			}
186 		}
187 
188 		xclose(&in);
189 
190 		if (ret != 0)
191 			goto out;
192 	}
193 	ret = 0;
194 out:
195 	free(buf);
196 	return -ret;
197 }
198