(root)/
coreutils-9.4/
src/
sum.c
       1  /* sum -- checksum and count the blocks in a file
       2     Copyright (C) 1986-2023 Free Software Foundation, Inc.
       3  
       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.
       8  
       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.
      13  
      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/>.  */
      16  
      17  /* Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. */
      18  
      19  /* Written by Kayvan Aghaiepour and David MacKenzie. */
      20  
      21  #include <config.h>
      22  
      23  #include <stdio.h>
      24  #include <sys/types.h>
      25  #include "system.h"
      26  #include "human.h"
      27  #include "sum.h"
      28  
      29  #include <byteswap.h>
      30  #ifdef WORDS_BIGENDIAN
      31  # define SWAP(n) (n)
      32  #else
      33  # define SWAP(n) bswap_16 (n)
      34  #endif
      35  
      36  /* Calculate the checksum and the size in bytes of stream STREAM.
      37     Return -1 on error, 0 on success.  */
      38  
      39  int
      40  bsd_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
      41  {
      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);
      48  
      49    if (! buffer)
      50      return -1;
      51  
      52    /* Process file */
      53    while (true)
      54    {
      55      sum = 0;
      56  
      57      /* Read block */
      58      while (true)
      59      {
      60        n = fread (buffer + sum, 1, buffer_length - sum, stream);
      61        sum += n;
      62  
      63        if (buffer_length == sum)
      64          break;
      65  
      66        if (n == 0)
      67          {
      68            if (ferror (stream))
      69              goto cleanup_buffer;
      70            goto final_process;
      71          }
      72  
      73        if (feof (stream))
      74          goto final_process;
      75      }
      76  
      77      for (size_t i = 0; i < sum; i++)
      78        {
      79          checksum = (checksum >> 1) + ((checksum & 1) << 15);
      80          checksum += buffer[i];
      81          checksum &= 0xffff;	/* Keep it within bounds. */
      82        }
      83      if (total_bytes + sum < total_bytes)
      84        {
      85          errno = EOVERFLOW;
      86          goto cleanup_buffer;
      87        }
      88      total_bytes += sum;
      89    }
      90  
      91  final_process:;
      92  
      93    for (size_t i = 0; i < sum; i++)
      94      {
      95        checksum = (checksum >> 1) + ((checksum & 1) << 15);
      96        checksum += buffer[i];
      97        checksum &= 0xffff;	/* Keep it within bounds. */
      98      }
      99    if (total_bytes + sum < total_bytes)
     100      {
     101        errno = EOVERFLOW;
     102        goto cleanup_buffer;
     103      }
     104    total_bytes += sum;
     105  
     106    memcpy (resstream, &checksum, sizeof checksum);
     107    *length = total_bytes;
     108    ret = 0;
     109  cleanup_buffer:
     110    free (buffer);
     111    return ret;
     112  }
     113  
     114  /* Calculate the checksum and the size in bytes of stream STREAM.
     115     Return -1 on error, 0 on success.  */
     116  
     117  int
     118  sysv_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
     119  {
     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);
     125  
     126    if (! buffer)
     127      return -1;
     128  
     129    /* The sum of all the input bytes, modulo (UINT_MAX + 1).  */
     130    unsigned int s = 0;
     131  
     132    /* Process file */
     133    while (true)
     134    {
     135      sum = 0;
     136  
     137      /* Read block */
     138      while (true)
     139      {
     140        n = fread (buffer + sum, 1, buffer_length - sum, stream);
     141        sum += n;
     142  
     143        if (buffer_length == sum)
     144          break;
     145  
     146        if (n == 0)
     147          {
     148            if (ferror (stream))
     149              goto cleanup_buffer;
     150            goto final_process;
     151          }
     152  
     153        if (feof (stream))
     154          goto final_process;
     155      }
     156  
     157      for (size_t i = 0; i < sum; i++)
     158        s += buffer[i];
     159      if (total_bytes + sum < total_bytes)
     160        {
     161          errno = EOVERFLOW;
     162          goto cleanup_buffer;
     163        }
     164      total_bytes += sum;
     165    }
     166  
     167  final_process:;
     168  
     169    for (size_t i = 0; i < sum; i++)
     170      s += buffer[i];
     171    if (total_bytes + sum < total_bytes)
     172      {
     173        errno = EOVERFLOW;
     174        goto cleanup_buffer;
     175      }
     176    total_bytes += sum;
     177  
     178    int r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
     179    int checksum = (r & 0xffff) + (r >> 16);
     180  
     181    memcpy (resstream, &checksum, sizeof checksum);
     182    *length = total_bytes;
     183    ret = 0;
     184  cleanup_buffer:
     185    free (buffer);
     186    return ret;
     187  }
     188  
     189  /* Print the checksum and size (in 1024 byte blocks) to stdout.
     190     If ARGS is true, also print the FILE name.  */
     191  
     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)
     196  {
     197    if (raw)
     198      {
     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;
     204      }
     205  
     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);
     212  }
     213  
     214  /* Print the checksum and size (in 512 byte blocks) to stdout.
     215     If ARGS is true, also print the FILE name.  */
     216  
     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)
     221  {
     222    if (raw)
     223      {
     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;
     229      }
     230  
     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);
     237  }