/*
 * sum [file1] [file2] [file3] ...
 * sum utility written for GNU - checksum and count blocks in a file
 * Copyright (C) 1986 Kayvan Aghaiepour
 *   
 * This program is distributed in the hope that it will be useful,
 * but without any warranty.  No author or distributor
 * accepts responsibility to anyone for the consequences of using it
 * or for whether it serves any particular purpose or works at all,
 * unless he says so in writing.
 * 
 * Everyone is granted permission to copy, modify and redistribute
 * this program, but only under the conditions described in the
 * document "GNU Emacs copying permission notice".   An exact copy
 * of the document is supposed to have been given to you along with
 * GNU Emacs so that you can know how you may redistribute it all.
 * It should be in a file named COPYING.  Among other things, the
 * copyright notice and this notice must be preserved on all copies.
 * 
 */

#include <stdio.h>

/* macro to right-rotate an integer variable */
#define ROTATE(c) if ((c) & 01) (c) = ((c) >>1) + 0x8000; else (c) >>= 1;

main (argc, argv)
     int argc;
     char **argv;
{
  int error;

  error = 0;
  switch (argc)
    {
    case 1:			/* no arguments */
      if (sumfile (NULL, 0) < 0)
	{
	  error += 10;
	}
      break;
    case 2:			/* one argument */
      if (sumfile (argv[1], 0) < 0)
	{
	  error += 10;
	}
      break;
    default:			/* more than one argument */
      for (argc--, argv++; argc > 0; argc--, argv++)
	{
	  if (sumfile (argv[0], 1) < 0)
	    {
	      error += 10;
	    }
	}
    }
  return (error);
}

/*
 * sumfile does the checksum itself. Returns negative if something goes
 * wrong. Else, it returns non-negative and prints the desired information.
 */

sumfile (s, printname)
     char *s;			/* filename of file to check */
     int printname;		/* boolean. print file name or not? */
{
  register FILE *readfile;	/* the file */
  register unsigned checksum;	/* the checksum mod 2^16. */
  register long bytenum;	/* the number of bytes. */
  register int ch;		/* The character to read */

  if (s == NULL)
    {
      readfile = stdin;		/* no arguments, so read from stdin */
    }
  else if ((readfile = fopen (s, "r")) == NULL)
    {
      perror_with_name (s);	/* File inaccessible for some reason */
      return (-1);
    }
  
  checksum = 0;
  bytenum = 0;

  while ((ch = getc (readfile)) != EOF)
    {
      bytenum++;
      ROTATE (checksum);
      checksum += ch;
      checksum &= 0xffff;		/* keep it within bounds */
    }

  if (ferror (readfile))
    {	/* something bad has happened */
      perror_with_name (s);
      return (-1);
    }

  printf ("%05u%6ld", checksum, (bytenum + BUFSIZ - 1) / BUFSIZ);

  if (printname)
    printf (" %s\n", s);
  else
    putchar ('\n');

  fclose (readfile);
  return (1);
}

perror_with_name (name)
     char *name;
{
  extern int errno, sys_nerr;
  extern char *sys_errlist[];
  char *s;

  if (errno < sys_nerr)
    fprintf (stderr, "%s for %s\n", name);
  else
    fprintf (stderr, "(Unknown error) cannot open %s", name);
}
