tests: add fold(1) test for --bytes option
[coreutils.git] / src / sum.c
blob2d07e6ef6f6d052530726aaf365b1f726562e925
1 /* sum -- checksum and count the blocks in a file
2 Copyright (C) 1986-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. */
19 /* Written by Kayvan Aghaiepour and David MacKenzie. */
21 #include <config.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <endian.h>
26 #include "system.h"
27 #include "human.h"
28 #include "sum.h"
30 /* Calculate the checksum and the size in bytes of stream STREAM.
31 Return -1 on error, 0 on success. */
33 int
34 bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
36 int ret = -1;
37 size_t sum, n;
38 int checksum = 0; /* The checksum mod 2^16. */
39 uintmax_t total_bytes = 0; /* The number of bytes. */
40 static const size_t buffer_length = 32768;
41 uint8_t *buffer = malloc (buffer_length);
43 if (! buffer)
44 return -1;
46 /* Process file */
47 while (true)
49 sum = 0;
51 /* Read block */
52 while (true)
54 n = fread (buffer + sum, 1, buffer_length - sum, stream);
55 sum += n;
57 if (buffer_length == sum)
58 break;
60 if (n == 0)
62 if (ferror (stream))
63 goto cleanup_buffer;
64 goto final_process;
67 if (feof (stream))
68 goto final_process;
71 for (size_t i = 0; i < sum; i++)
73 checksum = (checksum >> 1) + ((checksum & 1) << 15);
74 checksum += buffer[i];
75 checksum &= 0xffff; /* Keep it within bounds. */
77 if (total_bytes + sum < total_bytes)
79 errno = EOVERFLOW;
80 goto cleanup_buffer;
82 total_bytes += sum;
85 final_process:;
87 for (size_t i = 0; i < sum; i++)
89 checksum = (checksum >> 1) + ((checksum & 1) << 15);
90 checksum += buffer[i];
91 checksum &= 0xffff; /* Keep it within bounds. */
93 if (total_bytes + sum < total_bytes)
95 errno = EOVERFLOW;
96 goto cleanup_buffer;
98 total_bytes += sum;
100 memcpy (resstream, &checksum, sizeof checksum);
101 *length = total_bytes;
102 ret = 0;
103 cleanup_buffer:
104 free (buffer);
105 return ret;
108 /* Calculate the checksum and the size in bytes of stream STREAM.
109 Return -1 on error, 0 on success. */
112 sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
114 int ret = -1;
115 size_t sum, n;
116 uintmax_t total_bytes = 0;
117 static const size_t buffer_length = 32768;
118 uint8_t *buffer = malloc (buffer_length);
120 if (! buffer)
121 return -1;
123 /* The sum of all the input bytes, modulo (UINT_MAX + 1). */
124 unsigned int s = 0;
126 /* Process file */
127 while (true)
129 sum = 0;
131 /* Read block */
132 while (true)
134 n = fread (buffer + sum, 1, buffer_length - sum, stream);
135 sum += n;
137 if (buffer_length == sum)
138 break;
140 if (n == 0)
142 if (ferror (stream))
143 goto cleanup_buffer;
144 goto final_process;
147 if (feof (stream))
148 goto final_process;
151 for (size_t i = 0; i < sum; i++)
152 s += buffer[i];
153 if (total_bytes + sum < total_bytes)
155 errno = EOVERFLOW;
156 goto cleanup_buffer;
158 total_bytes += sum;
161 final_process:;
163 for (size_t i = 0; i < sum; i++)
164 s += buffer[i];
165 if (total_bytes + sum < total_bytes)
167 errno = EOVERFLOW;
168 goto cleanup_buffer;
170 total_bytes += sum;
172 int r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
173 int checksum = (r & 0xffff) + (r >> 16);
175 memcpy (resstream, &checksum, sizeof checksum);
176 *length = total_bytes;
177 ret = 0;
178 cleanup_buffer:
179 free (buffer);
180 return ret;
183 /* Print the checksum and size (in 1024 byte blocks) to stdout.
184 If ARGS is true, also print the FILE name. */
186 void
187 output_bsd (char const *file, int binary_file, void const *digest,
188 bool raw, bool tagged, unsigned char delim, bool args,
189 uintmax_t length)
191 if (raw)
193 /* Output in network byte order (big endian). */
194 uint16_t out_int = *(int *)digest;
195 out_int = htobe16 (out_int);
196 fwrite (&out_int, 1, 16/8, stdout);
197 return;
200 char hbuf[LONGEST_HUMAN_READABLE + 1];
201 printf ("%05d %5s", *(int *)digest,
202 human_readable (length, hbuf, human_ceiling, 1, 1024));
203 if (args)
204 printf (" %s", file);
205 putchar (delim);
208 /* Print the checksum and size (in 512 byte blocks) to stdout.
209 If ARGS is true, also print the FILE name. */
211 void
212 output_sysv (char const *file, int binary_file, void const *digest,
213 bool raw, bool tagged, unsigned char delim, bool args,
214 uintmax_t length)
216 if (raw)
218 /* Output in network byte order (big endian). */
219 uint16_t out_int = *(int *)digest;
220 out_int = htobe16 (out_int);
221 fwrite (&out_int, 1, 16/8, stdout);
222 return;
225 char hbuf[LONGEST_HUMAN_READABLE + 1];
226 printf ("%d %s", *(int *)digest,
227 human_readable (length, hbuf, human_ceiling, 1, 512));
228 if (args)
229 printf (" %s", file);
230 putchar (delim);