1/* sum -- checksum and count the blocks in a file 2 Copyright (C) 1986, 1989, 1991, 1995-2002, 2004-2005, 2008-2010 Free 3 Software Foundation, Inc. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18/* Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. */ 19 20/* Written by Kayvan Aghaiepour and David MacKenzie. */ 21 22#include <config.h> 23 24#include <stdio.h> 25#include <sys/types.h> 26#include <getopt.h> 27#include "system.h" 28#include "error.h" 29#include "human.h" 30#include "safe-read.h" 31#include "xfreopen.h" 32 33/* The official name of this program (e.g., no `g' prefix). */ 34#define PROGRAM_NAME "sum" 35 36#define AUTHORS \ 37 proper_name ("Kayvan Aghaiepour"), \ 38 proper_name ("David MacKenzie") 39 40/* True if any of the files read were the standard input. */ 41static bool have_read_stdin; 42 43static struct option const longopts[] = 44{ 45 {"sysv", no_argument, NULL, 's'}, 46 {GETOPT_HELP_OPTION_DECL}, 47 {GETOPT_VERSION_OPTION_DECL}, 48 {NULL, 0, NULL, 0} 49}; 50 51void 52usage (int status) 53{ 54 if (status != EXIT_SUCCESS) 55 fprintf (stderr, _("Try `%s --help' for more information.\n"), 56 program_name); 57 else 58 { 59 printf (_("\ 60Usage: %s [OPTION]... [FILE]...\n\ 61"), 62 program_name); 63 fputs (_("\ 64Print checksum and block counts for each FILE.\n\ 65\n\ 66 -r use BSD sum algorithm, use 1K blocks\n\ 67 -s, --sysv use System V sum algorithm, use 512 bytes blocks\n\ 68"), stdout); 69 fputs (HELP_OPTION_DESCRIPTION, stdout); 70 fputs (VERSION_OPTION_DESCRIPTION, stdout); 71 fputs (_("\ 72\n\ 73With no FILE, or when FILE is -, read standard input.\n\ 74"), stdout); 75 emit_ancillary_info (); 76 } 77 exit (status); 78} 79 80/* Calculate and print the rotated checksum and the size in 1K blocks 81 of file FILE, or of the standard input if FILE is "-". 82 If PRINT_NAME is >1, print FILE next to the checksum and size. 83 The checksum varies depending on sizeof (int). 84 Return true if successful. */ 85 86static bool 87bsd_sum_file (const char *file, int print_name) 88{ 89 FILE *fp; 90 int checksum = 0; /* The checksum mod 2^16. */ 91 uintmax_t total_bytes = 0; /* The number of bytes. */ 92 int ch; /* Each character read. */ 93 char hbuf[LONGEST_HUMAN_READABLE + 1]; 94 bool is_stdin = STREQ (file, "-"); 95 96 if (is_stdin) 97 { 98 fp = stdin; 99 have_read_stdin = true; 100 if (O_BINARY && ! isatty (STDIN_FILENO)) 101 xfreopen (NULL, "rb", stdin); 102 } 103 else 104 { 105 fp = fopen (file, (O_BINARY ? "rb" : "r")); 106 if (fp == NULL) 107 { 108 error (0, errno, "%s", file); 109 return false; 110 } 111 } 112 113 while ((ch = getc (fp)) != EOF) 114 { 115 total_bytes++; 116 checksum = (checksum >> 1) + ((checksum & 1) << 15); 117 checksum += ch; 118 checksum &= 0xffff; /* Keep it within bounds. */ 119 } 120 121 if (ferror (fp)) 122 { 123 error (0, errno, "%s", file); 124 if (!is_stdin) 125 fclose (fp); 126 return false; 127 } 128 129 if (!is_stdin && fclose (fp) != 0) 130 { 131 error (0, errno, "%s", file); 132 return false; 133 } 134 135 printf ("%05d %5s", checksum, 136 human_readable (total_bytes, hbuf, human_ceiling, 1, 1024)); 137 if (print_name > 1) 138 printf (" %s", file); 139 putchar ('\n'); 140 141 return true; 142} 143 144/* Calculate and print the checksum and the size in 512-byte blocks 145 of file FILE, or of the standard input if FILE is "-". 146 If PRINT_NAME is >0, print FILE next to the checksum and size. 147 Return true if successful. */ 148 149static bool 150sysv_sum_file (const char *file, int print_name) 151{ 152 int fd; 153 unsigned char buf[8192]; 154 uintmax_t total_bytes = 0; 155 char hbuf[LONGEST_HUMAN_READABLE + 1]; 156 int r; 157 int checksum; 158 159 /* The sum of all the input bytes, modulo (UINT_MAX + 1). */ 160 unsigned int s = 0; 161 162 bool is_stdin = STREQ (file, "-"); 163 164 if (is_stdin) 165 { 166 fd = STDIN_FILENO; 167 have_read_stdin = true; 168 if (O_BINARY && ! isatty (STDIN_FILENO)) 169 xfreopen (NULL, "rb", stdin); 170 } 171 else 172 { 173 fd = open (file, O_RDONLY | O_BINARY); 174 if (fd == -1) 175 { 176 error (0, errno, "%s", file); 177 return false; 178 } 179 } 180 181 while (1) 182 { 183 size_t i; 184 size_t bytes_read = safe_read (fd, buf, sizeof buf); 185 186 if (bytes_read == 0) 187 break; 188 189 if (bytes_read == SAFE_READ_ERROR) 190 { 191 error (0, errno, "%s", file); 192 if (!is_stdin) 193 close (fd); 194 return false; 195 } 196 197 for (i = 0; i < bytes_read; i++) 198 s += buf[i]; 199 total_bytes += bytes_read; 200 } 201 202 if (!is_stdin && close (fd) != 0) 203 { 204 error (0, errno, "%s", file); 205 return false; 206 } 207 208 r = (s & 0xffff) + ((s & 0xffffffff) >> 16); 209 checksum = (r & 0xffff) + (r >> 16); 210 211 printf ("%d %s", checksum, 212 human_readable (total_bytes, hbuf, human_ceiling, 1, 512)); 213 if (print_name) 214 printf (" %s", file); 215 putchar ('\n'); 216 217 return true; 218} 219 220int 221main (int argc, char **argv) 222{ 223 bool ok; 224 int optc; 225 int files_given; 226 bool (*sum_func) (const char *, int) = bsd_sum_file; 227 228 initialize_main (&argc, &argv); 229 set_program_name (argv[0]); 230 setlocale (LC_ALL, ""); 231 bindtextdomain (PACKAGE, LOCALEDIR); 232 textdomain (PACKAGE); 233 234 atexit (close_stdout); 235 236 /* Line buffer stdout to ensure lines are written atomically and immediately 237 so that processes running in parallel do not intersperse their output. */ 238 setvbuf (stdout, NULL, _IOLBF, 0); 239 240 have_read_stdin = false; 241 242 while ((optc = getopt_long (argc, argv, "rs", longopts, NULL)) != -1) 243 { 244 switch (optc) 245 { 246 case 'r': /* For SysV compatibility. */ 247 sum_func = bsd_sum_file; 248 break; 249 250 case 's': 251 sum_func = sysv_sum_file; 252 break; 253 254 case_GETOPT_HELP_CHAR; 255 256 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); 257 258 default: 259 usage (EXIT_FAILURE); 260 } 261 } 262 263 files_given = argc - optind; 264 if (files_given <= 0) 265 ok = sum_func ("-", files_given); 266 else 267 for (ok = true; optind < argc; optind++) 268 ok &= sum_func (argv[optind], files_given); 269 270 if (have_read_stdin && fclose (stdin) == EOF) 271 error (EXIT_FAILURE, errno, "-"); 272 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); 273} 274