dd: synchronize output after write errors
[coreutils.git] / src / sum.c
blobcb5bc032e12ac735532a75805e5a232d9988bc15
1 /* sum -- checksum and count the blocks in a file
2 Copyright (C) 1986-2022 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 /* Calculate the checksum and the size in bytes of stream STREAM.
30 Return -1 on error, 0 on success. */
32 int
33 bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
35 int ret = -1;
36 size_t sum, n;
37 int checksum = 0; /* The checksum mod 2^16. */
38 uintmax_t total_bytes = 0; /* The number of bytes. */
39 static const size_t buffer_length = 32768;
40 uint8_t *buffer = malloc (buffer_length);
42 if (! buffer)
43 return -1;
45 /* Process file */
46 while (true)
48 sum = 0;
50 /* Read block */
51 while (true)
53 n = fread (buffer + sum, 1, buffer_length - sum, stream);
54 sum += n;
56 if (buffer_length == sum)
57 break;
59 if (n == 0)
61 if (ferror (stream))
62 goto cleanup_buffer;
63 goto final_process;
66 if (feof (stream))
67 goto final_process;
70 for (size_t i = 0; i < sum; i++)
72 checksum = (checksum >> 1) + ((checksum & 1) << 15);
73 checksum += buffer[i];
74 checksum &= 0xffff; /* Keep it within bounds. */
76 if (total_bytes + sum < total_bytes)
78 errno = EOVERFLOW;
79 goto cleanup_buffer;
81 total_bytes += sum;
84 final_process:;
86 for (size_t i = 0; i < sum; i++)
88 checksum = (checksum >> 1) + ((checksum & 1) << 15);
89 checksum += buffer[i];
90 checksum &= 0xffff; /* Keep it within bounds. */
92 if (total_bytes + sum < total_bytes)
94 errno = EOVERFLOW;
95 goto cleanup_buffer;
97 total_bytes += sum;
99 memcpy (resstream, &checksum, sizeof checksum);
100 *length = total_bytes;
101 ret = 0;
102 cleanup_buffer:
103 free (buffer);
104 return ret;
107 /* Calculate the checksum and the size in bytes of stream STREAM.
108 Return -1 on error, 0 on success. */
111 sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
113 int ret = -1;
114 size_t sum, n;
115 uintmax_t total_bytes = 0;
116 static const size_t buffer_length = 32768;
117 uint8_t *buffer = malloc (buffer_length);
119 if (! buffer)
120 return -1;
122 /* The sum of all the input bytes, modulo (UINT_MAX + 1). */
123 unsigned int s = 0;
125 /* Process file */
126 while (true)
128 sum = 0;
130 /* Read block */
131 while (true)
133 n = fread (buffer + sum, 1, buffer_length - sum, stream);
134 sum += n;
136 if (buffer_length == sum)
137 break;
139 if (n == 0)
141 if (ferror (stream))
142 goto cleanup_buffer;
143 goto final_process;
146 if (feof (stream))
147 goto final_process;
150 for (size_t i = 0; i < sum; i++)
151 s += buffer[i];
152 if (total_bytes + sum < total_bytes)
154 errno = EOVERFLOW;
155 goto cleanup_buffer;
157 total_bytes += sum;
160 final_process:;
162 for (size_t i = 0; i < sum; i++)
163 s += buffer[i];
164 if (total_bytes + sum < total_bytes)
166 errno = EOVERFLOW;
167 goto cleanup_buffer;
169 total_bytes += sum;
171 int r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
172 int checksum = (r & 0xffff) + (r >> 16);
174 memcpy (resstream, &checksum, sizeof checksum);
175 *length = total_bytes;
176 ret = 0;
177 cleanup_buffer:
178 free (buffer);
179 return ret;
182 /* Print the checksum and size (in 1024 byte blocks) to stdout.
183 If ARGS is true, also print the FILE name. */
185 void
186 output_bsd (char const *file, int binary_file, void const *digest,
187 bool tagged, unsigned char delim, bool args,
188 uintmax_t length)
191 char hbuf[LONGEST_HUMAN_READABLE + 1];
192 printf ("%05d %5s", *(int *)digest,
193 human_readable (length, hbuf, human_ceiling, 1, 1024));
194 if (args)
195 printf (" %s", file);
196 putchar (delim);
199 /* Print the checksum and size (in 512 byte blocks) to stdout.
200 If ARGS is true, also print the FILE name. */
202 void
203 output_sysv (char const *file, int binary_file, void const *digest,
204 bool tagged, unsigned char delim, bool args,
205 uintmax_t length)
208 char hbuf[LONGEST_HUMAN_READABLE + 1];
209 printf ("%d %s", *(int *)digest,
210 human_readable (length, hbuf, human_ceiling, 1, 512));
211 if (args)
212 printf (" %s", file);
213 putchar (delim);