tail: avoid infloop with -c on /dev/zero
[coreutils.git] / src / sum.c
blob8c6979c997eb485502d26cc589ab071ed71961eb
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 "system.h"
26 #include "human.h"
27 #include "sum.h"
29 #include <byteswap.h>
30 #ifdef WORDS_BIGENDIAN
31 # define SWAP(n) (n)
32 #else
33 # define SWAP(n) bswap_16 (n)
34 #endif
36 /* Calculate the checksum and the size in bytes of stream STREAM.
37 Return -1 on error, 0 on success. */
39 int
40 bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
42 int ret = -1;
43 size_t sum, n;
44 int checksum = 0; /* The checksum mod 2^16. */
45 uintmax_t total_bytes = 0; /* The number of bytes. */
46 static const size_t buffer_length = 32768;
47 uint8_t *buffer = malloc (buffer_length);
49 if (! buffer)
50 return -1;
52 /* Process file */
53 while (true)
55 sum = 0;
57 /* Read block */
58 while (true)
60 n = fread (buffer + sum, 1, buffer_length - sum, stream);
61 sum += n;
63 if (buffer_length == sum)
64 break;
66 if (n == 0)
68 if (ferror (stream))
69 goto cleanup_buffer;
70 goto final_process;
73 if (feof (stream))
74 goto final_process;
77 for (size_t i = 0; i < sum; i++)
79 checksum = (checksum >> 1) + ((checksum & 1) << 15);
80 checksum += buffer[i];
81 checksum &= 0xffff; /* Keep it within bounds. */
83 if (total_bytes + sum < total_bytes)
85 errno = EOVERFLOW;
86 goto cleanup_buffer;
88 total_bytes += sum;
91 final_process:;
93 for (size_t i = 0; i < sum; i++)
95 checksum = (checksum >> 1) + ((checksum & 1) << 15);
96 checksum += buffer[i];
97 checksum &= 0xffff; /* Keep it within bounds. */
99 if (total_bytes + sum < total_bytes)
101 errno = EOVERFLOW;
102 goto cleanup_buffer;
104 total_bytes += sum;
106 memcpy (resstream, &checksum, sizeof checksum);
107 *length = total_bytes;
108 ret = 0;
109 cleanup_buffer:
110 free (buffer);
111 return ret;
114 /* Calculate the checksum and the size in bytes of stream STREAM.
115 Return -1 on error, 0 on success. */
118 sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
120 int ret = -1;
121 size_t sum, n;
122 uintmax_t total_bytes = 0;
123 static const size_t buffer_length = 32768;
124 uint8_t *buffer = malloc (buffer_length);
126 if (! buffer)
127 return -1;
129 /* The sum of all the input bytes, modulo (UINT_MAX + 1). */
130 unsigned int s = 0;
132 /* Process file */
133 while (true)
135 sum = 0;
137 /* Read block */
138 while (true)
140 n = fread (buffer + sum, 1, buffer_length - sum, stream);
141 sum += n;
143 if (buffer_length == sum)
144 break;
146 if (n == 0)
148 if (ferror (stream))
149 goto cleanup_buffer;
150 goto final_process;
153 if (feof (stream))
154 goto final_process;
157 for (size_t i = 0; i < sum; i++)
158 s += buffer[i];
159 if (total_bytes + sum < total_bytes)
161 errno = EOVERFLOW;
162 goto cleanup_buffer;
164 total_bytes += sum;
167 final_process:;
169 for (size_t i = 0; i < sum; i++)
170 s += buffer[i];
171 if (total_bytes + sum < total_bytes)
173 errno = EOVERFLOW;
174 goto cleanup_buffer;
176 total_bytes += sum;
178 int r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
179 int checksum = (r & 0xffff) + (r >> 16);
181 memcpy (resstream, &checksum, sizeof checksum);
182 *length = total_bytes;
183 ret = 0;
184 cleanup_buffer:
185 free (buffer);
186 return ret;
189 /* Print the checksum and size (in 1024 byte blocks) to stdout.
190 If ARGS is true, also print the FILE name. */
192 void
193 output_bsd (char const *file, int binary_file, void const *digest,
194 bool raw, bool tagged, unsigned char delim, bool args,
195 uintmax_t length)
197 if (raw)
199 /* Output in network byte order (big endian). */
200 uint16_t out_int = *(int *)digest;
201 out_int = SWAP (out_int);
202 fwrite (&out_int, 1, 16/8, stdout);
203 return;
206 char hbuf[LONGEST_HUMAN_READABLE + 1];
207 printf ("%05d %5s", *(int *)digest,
208 human_readable (length, hbuf, human_ceiling, 1, 1024));
209 if (args)
210 printf (" %s", file);
211 putchar (delim);
214 /* Print the checksum and size (in 512 byte blocks) to stdout.
215 If ARGS is true, also print the FILE name. */
217 void
218 output_sysv (char const *file, int binary_file, void const *digest,
219 bool raw, bool tagged, unsigned char delim, bool args,
220 uintmax_t length)
222 if (raw)
224 /* Output in network byte order (big endian). */
225 uint16_t out_int = *(int *)digest;
226 out_int = SWAP (out_int);
227 fwrite (&out_int, 1, 16/8, stdout);
228 return;
231 char hbuf[LONGEST_HUMAN_READABLE + 1];
232 printf ("%d %s", *(int *)digest,
233 human_readable (length, hbuf, human_ceiling, 1, 512));
234 if (args)
235 printf (" %s", file);
236 putchar (delim);