/*
 * sum - print checksum and block count of a file
 *
 * Gunnar Ritter, Freiburg i. Br., Germany, January 2003.
 *
 * Parts taken from /usr/src/cmd/sum.c, Unix 7th Edition:
 *
 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *   Redistributions of source code and documentation must retain the
 *    above copyright notice, this list of conditions and the following
 *    disclaimer.
 *   Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *   All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed or owned by Caldera
 *      International, Inc.
 *   Neither the name of Caldera International, Inc. nor the names of
 *    other contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
 * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#define _XOPEN_SOURCE
#define USED __attribute__((used))
static const char sccsid[] USED = "@(#)sum.sl	1.9 (gritter) 5/29/05";

#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define UNIT 512
#define RUNIT 1024

static char *__progname;
static int rflag;
static int sflag;

static void usage() {
  fprintf(stderr, "Usage: %s [-hrs] [FILE]\n", __progname);
}

static int sum(const char *name) {
  uint16_t sum;
  uint32_t s;
  int err = 0;
  int fd;
  ssize_t sz, i;
  char buf[4096];
  unsigned long long nbytes;

  if (name) {
    if ((fd = open(name, O_RDONLY)) < 0) {
      fprintf(stderr, "%s: Can't open %s\n", __progname, name);
      return 10;
    }
  } else
    fd = 0;
  sum = 0;
  nbytes = 0;
  s = 0;
  while ((sz = read(fd, buf, sizeof buf)) > 0) {
    nbytes += sz;
    if (!sflag) {
      for (i = 0; i < sz; i++) {
        if (sum & 01)
          sum = (sum >> 1) + 0x8000;
        else
          sum >>= 1;
        sum += buf[i] & 0377;
        sum &= 0xFFFF;
      }
    } else {
      for (i = 0; i < sz; i++)
        s += buf[i] & 0377;
    }
  }
  if (sz < 0) {
    fprintf(stderr, "%s: read error on %s\n", __progname, name ? name : "-");
    err = 1;
  }
  if (!sflag) {
    printf("%.5u %5llu", (unsigned)sum,
           (unsigned long long)(nbytes + RUNIT - 1) / RUNIT);
  } else {
    s = (s & 0xFFFF) + (s >> 16);
    s = (s & 0xFFFF) + (s >> 16);
    printf("%u %llu", (unsigned)s,
           (unsigned long long)(nbytes + UNIT - 1) / UNIT);
  }
  if (name)
    printf(" %s", name);
  printf("\n");
  if (fd > 0)
    close(fd);
  return err;
}

int main(int argc, char *argv[]) {
  int index;
  int c;
  __progname = basename(argv[0]);

  while ((c = getopt(argc, argv, "hrs")) != -1)
    switch (c) {
    case 'h':
      usage();
      exit(0);
    case 'r':
      rflag = 1;
      break;
    case 's':
      sflag = 1;
      break;
    case '?':
      if (isprint(optopt)) {
        usage();
        exit(1);
      }
    default:
      usage();
    }

  if (argv[optind] == NULL) {
    if (sum(NULL) == 0) {
      return 0;
    } else {
      return 1;
    }
  }

  for (index = optind; index < argc; index++) {
    // run the sum()
    if (sum(argv[index]) != 0) {
      exit(1);
    }
  }
}
