(add_tabstop): Give correct size when reallocating tab_list buffer.
[platform/upstream/coreutils.git] / src / sum.c
1 /* sum -- checksum and count the blocks in a file
2    Copyright (C) 1986, 1989, 1991, 1995 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 2, or (at your option)
7    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, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
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 "version.h"
29 #include "error.h"
30
31 static int bsd_sum_file ();
32 static int sysv_sum_file ();
33
34 int safe_read ();
35
36 /* The name this program was run with. */
37 char *program_name;
38
39 /* Nonzero if any of the files read were the standard input. */
40 static int have_read_stdin;
41
42 /* Right-rotate 32-bit integer variable C. */
43 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x8000; else (c) >>= 1;
44
45 /* If non-zero, display usage information and exit.  */
46 static int show_help;
47
48 /* If non-zero, print the version on standard output then exit.  */
49 static int show_version;
50
51 static struct option const longopts[] =
52 {
53   {"sysv", no_argument, NULL, 's'},
54   {"help", no_argument, &show_help, 1},
55   {"version", no_argument, &show_version, 1},
56   {NULL, 0, NULL, 0}
57 };
58
59 static void
60 usage (status)
61      int status;
62 {
63   if (status != 0)
64     fprintf (stderr, "Try `%s --help' for more information.\n",
65              program_name);
66   else
67     {
68       printf ("\
69 Usage: %s [OPTION]... [FILE]...\n\
70 ",
71               program_name);
72       printf ("\
73 \n\
74   -r              defeat -s, use BSD sum algorithm, use 1K blocks\n\
75   -s, --sysv      use System V sum algorithm, use 512 bytes blocks\n\
76       --help      display this help and exit\n\
77       --version   output version information and exit\n\
78 \n\
79 With no FILE, or when FILE is -, read standard input.\n\
80 ");
81     }
82   exit (status);
83 }
84
85 void
86 main (argc, argv)
87      int argc;
88      char **argv;
89 {
90   int errors = 0;
91   int optc;
92   int files_given;
93   int (*sum_func) () = bsd_sum_file;
94
95   program_name = argv[0];
96   have_read_stdin = 0;
97
98   while ((optc = getopt_long (argc, argv, "rs", longopts, (int *) 0)) != -1)
99     {
100       switch (optc)
101         {
102         case 0:
103           break;
104
105         case 'r':               /* For SysV compatibility. */
106           sum_func = bsd_sum_file;
107           break;
108
109         case 's':
110           sum_func = sysv_sum_file;
111           break;
112
113         default:
114           usage (1);
115         }
116     }
117
118   if (show_version)
119     {
120       printf ("sum - %s\n", version_string);
121       exit (0);
122     }
123
124   if (show_help)
125     usage (0);
126
127   files_given = argc - optind;
128   if (files_given == 0)
129     {
130       if ((*sum_func) ("-", files_given) < 0)
131         errors = 1;
132     }
133   else
134     for (; optind < argc; optind++)
135       if ((*sum_func) (argv[optind], files_given) < 0)
136         errors = 1;
137
138   if (have_read_stdin && fclose (stdin) == EOF)
139     error (1, errno, "-");
140   exit (errors);
141 }
142
143 /* Calculate and print the rotated checksum and the size in 1K blocks
144    of file FILE, or of the standard input if FILE is "-".
145    If PRINT_NAME is >1, print FILE next to the checksum and size.
146    The checksum varies depending on sizeof(int).
147    Return 0 if successful, -1 if an error occurs. */
148
149 static int
150 bsd_sum_file (file, print_name)
151      char *file;
152      int print_name;
153 {
154   register FILE *fp;
155   register unsigned long checksum = 0; /* The checksum mod 2^16. */
156   register long total_bytes = 0; /* The number of bytes. */
157   register int ch;              /* Each character read. */
158
159   if (!strcmp (file, "-"))
160     {
161       fp = stdin;
162       have_read_stdin = 1;
163     }
164   else
165     {
166       fp = fopen (file, "r");
167       if (fp == NULL)
168         {
169           error (0, errno, "%s", file);
170           return -1;
171         }
172     }
173
174   /* This algorithm seems to depend on sign extension in `ch' in order to
175      give the right results.  Ick.  */
176   while ((ch = getc (fp)) != EOF)
177     {
178       total_bytes++;
179       ROTATE_RIGHT (checksum);
180       checksum += ch;
181       checksum &= 0xffff;       /* Keep it within bounds. */
182     }
183
184   if (ferror (fp))
185     {
186       error (0, errno, "%s", file);
187       if (strcmp (file, "-"))
188         fclose (fp);
189       return -1;
190     }
191
192   if (strcmp (file, "-") && fclose (fp) == EOF)
193     {
194       error (0, errno, "%s", file);
195       return -1;
196     }
197
198   printf ("%05lu %5ld", checksum, (total_bytes + 1024 - 1) / 1024);
199   if (print_name > 1)
200     printf (" %s", file);
201   putchar ('\n');
202
203   return 0;
204 }
205
206 /* Calculate and print the checksum and the size in 512-byte blocks
207    of file FILE, or of the standard input if FILE is "-".
208    If PRINT_NAME is >0, print FILE next to the checksum and size.
209    Return 0 if successful, -1 if an error occurs. */
210
211 static int
212 sysv_sum_file (file, print_name)
213      char *file;
214      int print_name;
215 {
216   int fd;
217   unsigned char buf[8192];
218   register int bytes_read;
219   register unsigned long checksum = 0;
220   long total_bytes = 0;
221
222   if (!strcmp (file, "-"))
223     {
224       fd = 0;
225       have_read_stdin = 1;
226     }
227   else
228     {
229       fd = open (file, O_RDONLY);
230       if (fd == -1)
231         {
232           error (0, errno, "%s", file);
233           return -1;
234         }
235     }
236
237   while ((bytes_read = safe_read (fd, buf, sizeof buf)) > 0)
238     {
239       register int i;
240
241       for (i = 0; i < bytes_read; i++)
242         checksum += buf[i];
243       total_bytes += bytes_read;
244     }
245
246   if (bytes_read < 0)
247     {
248       error (0, errno, "%s", file);
249       if (strcmp (file, "-"))
250         close (fd);
251       return -1;
252     }
253
254   if (strcmp (file, "-") && close (fd) == -1)
255     {
256       error (0, errno, "%s", file);
257       return -1;
258     }
259
260   printf ("%lu %ld", checksum % 0xffff, (total_bytes + 512 - 1) / 512);
261   if (print_name)
262     printf (" %s", file);
263   putchar ('\n');
264
265   return 0;
266 }