Most .c files (AUTHORS): Revert the WRITTEN_BY/AUTHORS change
[platform/upstream/coreutils.git] / src / md5sum.c
1 /* Compute MD5 or SHA1 checksum of files or strings
2    Copyright (C) 1995-2003 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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>.  */
19
20 #include <config.h>
21
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25
26 #include "system.h"
27
28 #include "md5.h"
29 #include "sha.h"
30 #include "checksum.h"
31 #include "getline.h"
32 #include "error.h"
33
34 /* The official name of this program (e.g., no `g' prefix).  */
35 #define PROGRAM_NAME (algorithm == ALG_MD5 ? "md5sum" : "shasum")
36
37 #define AUTHORS "Ulrich Drepper", "Scott Miller"
38
39 /* Most systems do not distinguish between external and internal
40    text representations.  */
41 /* FIXME: This begs for an autoconf test.  */
42 #if O_BINARY
43 # define OPENOPTS(BINARY) ((BINARY) != 0 ? TEXT1TO1 : TEXTCNVT)
44 # define TEXT1TO1 "rb"
45 # define TEXTCNVT "r"
46 #else
47 # if defined VMS
48 #  define OPENOPTS(BINARY) ((BINARY) != 0 ? TEXT1TO1 : TEXTCNVT)
49 #  define TEXT1TO1 "rb", "ctx=stm"
50 #  define TEXTCNVT "r", "ctx=stm"
51 # else
52 #  if UNIX || __UNIX__ || unix || __unix__ || _POSIX_VERSION
53 #   define OPENOPTS(BINARY) "r"
54 #  else
55     /* The following line is intended to evoke an error.
56        Using #error is not portable enough.  */
57     "Cannot determine system type."
58 #  endif
59 # endif
60 #endif
61
62
63 #define DIGEST_TYPE_STRING(Alg) ((Alg) == ALG_MD5 ? "MD5" : "SHA1")
64 #define DIGEST_STREAM(Alg) ((Alg) == ALG_MD5 ? md5_stream : sha_stream)
65
66 #define DIGEST_BITS(Alg) ((Alg) == ALG_MD5 ? 128 : 160)
67 #define DIGEST_HEX_BYTES(Alg) (DIGEST_BITS (Alg) / 4)
68 #define DIGEST_BIN_BYTES(Alg) (DIGEST_BITS (Alg) / 8)
69
70 #define MAX_DIGEST_BIN_BYTES MAX (DIGEST_BIN_BYTES (ALG_MD5), \
71                                   DIGEST_BIN_BYTES (ALG_SHA1))
72
73 /* The minimum length of a valid digest line.  This length does
74    not include any newline character at the end of a line.  */
75 #define MIN_DIGEST_LINE_LENGTH(Alg) \
76   (DIGEST_HEX_BYTES (Alg) /* length of hexadecimal message digest */ \
77    + 2 /* blank and binary indicator */ \
78    + 1 /* minimum filename length */ )
79
80 /* Nonzero if any of the files read were the standard input. */
81 static int have_read_stdin;
82
83 /* The minimum length of a valid checksum line for the selected algorithm.  */
84 static size_t min_digest_line_length;
85
86 /* Set to the length of a digest hex string for the selected algorithm.  */
87 static size_t digest_hex_bytes;
88
89 /* With --check, don't generate any output.
90    The exit code indicates success or failure.  */
91 static int status_only = 0;
92
93 /* With --check, print a message to standard error warning about each
94    improperly formatted checksum line.  */
95 static int warn = 0;
96
97 /* Declared and set via one of the wrapper .c files.  */
98 /* int algorithm = ALG_UNSPECIFIED; */
99
100 /* The name this program was run with.  */
101 char *program_name;
102
103 static const struct option long_options[] =
104 {
105   { "binary", no_argument, 0, 'b' },
106   { "check", no_argument, 0, 'c' },
107   { "status", no_argument, 0, 2 },
108   { "string", required_argument, 0, 1 },
109   { "text", no_argument, 0, 't' },
110   { "warn", no_argument, 0, 'w' },
111   { GETOPT_HELP_OPTION_DECL },
112   { GETOPT_VERSION_OPTION_DECL },
113   { NULL, 0, NULL, 0 }
114 };
115
116 void
117 usage (int status)
118 {
119   if (status != 0)
120     fprintf (stderr, _("Try `%s --help' for more information.\n"),
121              program_name);
122   else
123     {
124       printf (_("\
125 Usage: %s [OPTION] [FILE]...\n\
126   or:  %s [OPTION] --check [FILE]\n\
127 Print or check %s (%d-bit) checksums.\n\
128 With no FILE, or when FILE is -, read standard input.\n\
129 "),
130               program_name, program_name,
131               DIGEST_TYPE_STRING (algorithm),
132               DIGEST_BITS (algorithm));
133       printf (_("\
134 \n\
135   -b, --binary            read files in binary mode (default on DOS/Windows)\n\
136   -c, --check             check %s sums against given list\n\
137   -t, --text              read files in text mode (default)\n\
138 \n\
139 "),
140               DIGEST_TYPE_STRING (algorithm));
141       fputs (_("\
142 The following two options are useful only when verifying checksums:\n\
143       --status            don't output anything, status code shows success\n\
144   -w, --warn              warn about improperly formated checksum lines\n\
145 \n\
146 "), stdout);
147       fputs (HELP_OPTION_DESCRIPTION, stdout);
148       fputs (VERSION_OPTION_DESCRIPTION, stdout);
149       printf (_("\
150 \n\
151 The sums are computed as described in %s.  When checking, the input\n\
152 should be a former output of this program.  The default mode is to print\n\
153 a line with checksum, a character indicating type (`*' for binary, ` ' for\n\
154 text), and name for each FILE.\n"),
155               (algorithm == ALG_MD5 ? "RFC 1321" : "FIPS-180-1"));
156       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
157     }
158
159   exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
160 }
161
162 #define ISWHITE(c) ((c) == ' ' || (c) == '\t')
163
164 /* Split the checksum string S (of length S_LEN) from a BSD 'md5' or
165    'sha1' command into two parts: a hexadecimal digest, and the file
166    name.  S is modified.  */
167
168 static int
169 bsd_split_3 (char *s, size_t s_len, unsigned char **hex_digest, char **file_name)
170 {
171   size_t i;
172
173   *file_name = s;
174
175   /* Find end of filename. The BSD 'md5' and 'sha1' commands do not escape
176      filenames, so search backwards for the last ')'. */
177   i = s_len - 1;
178   while (i && s[i] != ')')
179     i--;
180
181   if (s[i] != ')')
182     return 1;
183
184   s[i++] = '\0';
185
186   while (ISWHITE (s[i]))
187     i++;
188
189   if (s[i] != '=')
190     return 1;
191
192   i++;
193
194   while (ISWHITE (s[i]))
195     i++;
196
197   *hex_digest = (unsigned char *) &s[i];
198   return 0;
199 }
200
201 /* Split the string S (of length S_LEN) into three parts:
202    a hexadecimal digest, binary flag, and the file name.
203    S is modified.  */
204
205 static int
206 split_3 (char *s, size_t s_len,
207          unsigned char **hex_digest, int *binary, char **file_name)
208 {
209   size_t i;
210   int escaped_filename = 0;
211   size_t algo_name_len;
212
213   i = 0;
214   while (ISWHITE (s[i]))
215     ++i;
216
217   /* Check for BSD-style checksum line. */
218   algo_name_len = strlen (DIGEST_TYPE_STRING (algorithm));
219   if (strncmp (s + i, DIGEST_TYPE_STRING (algorithm), algo_name_len) == 0)
220     {
221       if (strncmp (s + i + algo_name_len, " (", 2) == 0)
222         {
223           *binary = 0;
224           return bsd_split_3 (s +      i + algo_name_len + 2,
225                               s_len - (i + algo_name_len + 2),
226                               hex_digest, file_name);
227         }
228     }
229
230   /* Ignore this line if it is too short.
231      Each line must have at least `min_digest_line_length - 1' (or one more, if
232      the first is a backslash) more characters to contain correct message digest
233      information.  */
234   if (s_len - i < min_digest_line_length + (s[i] == '\\'))
235     return 1;
236
237   if (s[i] == '\\')
238     {
239       ++i;
240       escaped_filename = 1;
241     }
242   *hex_digest = (unsigned char *) &s[i];
243
244   /* The first field has to be the n-character hexadecimal
245      representation of the message digest.  If it is not followed
246      immediately by a white space it's an error.  */
247   i += digest_hex_bytes;
248   if (!ISWHITE (s[i]))
249     return 1;
250
251   s[i++] = '\0';
252
253   if (s[i] != ' ' && s[i] != '*')
254     return 1;
255   *binary = (s[i++] == '*');
256
257   /* All characters between the type indicator and end of line are
258      significant -- that includes leading and trailing white space.  */
259   *file_name = &s[i];
260
261   if (escaped_filename)
262     {
263       /* Translate each `\n' string in the file name to a NEWLINE,
264          and each `\\' string to a backslash.  */
265
266       char *dst = &s[i];
267
268       while (i < s_len)
269         {
270           switch (s[i])
271             {
272             case '\\':
273               if (i == s_len - 1)
274                 {
275                   /* A valid line does not end with a backslash.  */
276                   return 1;
277                 }
278               ++i;
279               switch (s[i++])
280                 {
281                 case 'n':
282                   *dst++ = '\n';
283                   break;
284                 case '\\':
285                   *dst++ = '\\';
286                   break;
287                 default:
288                   /* Only `\' or `n' may follow a backslash.  */
289                   return 1;
290                 }
291               break;
292
293             case '\0':
294               /* The file name may not contain a NUL.  */
295               return 1;
296               break;
297
298             default:
299               *dst++ = s[i++];
300               break;
301             }
302         }
303       *dst = '\0';
304     }
305   return 0;
306 }
307
308 static int
309 hex_digits (unsigned char const *s)
310 {
311   while (*s)
312     {
313       if (!ISXDIGIT (*s))
314         return 0;
315       ++s;
316     }
317   return 1;
318 }
319
320 /* An interface to the function, DIGEST_STREAM,
321    (either md5_stream or sha_stream).
322    Operate on FILENAME (it may be "-") and put the result in *BIN_RESULT.
323    Return non-zero upon failure, zero to indicate success.  */
324
325 static int
326 digest_file (const char *filename, int binary, unsigned char *bin_result,
327            int (*digest_stream)(FILE *, void *))
328 {
329   FILE *fp;
330   int err;
331
332   if (STREQ (filename, "-"))
333     {
334       have_read_stdin = 1;
335       fp = stdin;
336 #if O_BINARY
337       /* If we need binary reads from a pipe or redirected stdin, we need
338          to switch it to BINARY mode here, since stdin is already open.  */
339       if (binary)
340         SET_BINARY (fileno (stdin));
341 #endif
342     }
343   else
344     {
345       /* OPENOPTS is a macro.  It varies with the system.
346          Some systems distinguish between internal and
347          external text representations.  */
348
349       fp = fopen (filename, OPENOPTS (binary));
350       if (fp == NULL)
351         {
352           error (0, errno, "%s", filename);
353           return 1;
354         }
355     }
356
357   err = (*digest_stream) (fp, bin_result);
358   if (err)
359     {
360       error (0, errno, "%s", filename);
361       if (fp != stdin)
362         fclose (fp);
363       return 1;
364     }
365
366   if (fp != stdin && fclose (fp) == EOF)
367     {
368       error (0, errno, "%s", filename);
369       return 1;
370     }
371
372   return 0;
373 }
374
375 static int
376 digest_check (const char *checkfile_name, int (*digest_stream)(FILE *, void *))
377 {
378   FILE *checkfile_stream;
379   int n_properly_formated_lines = 0;
380   int n_mismatched_checksums = 0;
381   int n_open_or_read_failures = 0;
382   unsigned char bin_buffer[MAX_DIGEST_BIN_BYTES];
383   size_t line_number;
384   char *line;
385   size_t line_chars_allocated;
386
387   if (STREQ (checkfile_name, "-"))
388     {
389       have_read_stdin = 1;
390       checkfile_name = _("standard input");
391       checkfile_stream = stdin;
392     }
393   else
394     {
395       checkfile_stream = fopen (checkfile_name, "r");
396       if (checkfile_stream == NULL)
397         {
398           error (0, errno, "%s", checkfile_name);
399           return 1;
400         }
401     }
402
403   SET_MODE (fileno (checkfile_stream), O_TEXT);
404   line_number = 0;
405   line = NULL;
406   line_chars_allocated = 0;
407   do
408     {
409       char *filename;
410       int binary;
411       unsigned char *hex_digest;
412       int err;
413       int line_length;
414
415       ++line_number;
416
417       line_length = getline (&line, &line_chars_allocated, checkfile_stream);
418       if (line_length <= 0)
419         break;
420
421       /* Ignore comment lines, which begin with a '#' character.  */
422       if (line[0] == '#')
423         continue;
424
425       /* Remove any trailing newline.  */
426       if (line[line_length - 1] == '\n')
427         line[--line_length] = '\0';
428
429       err = split_3 (line, line_length, &hex_digest, &binary, &filename);
430       if (err || !hex_digits (hex_digest))
431         {
432           if (warn)
433             {
434               error (0, 0,
435                      _("%s: %lu: improperly formatted %s checksum line"),
436                      checkfile_name, (unsigned long) line_number,
437                      DIGEST_TYPE_STRING (algorithm));
438             }
439         }
440       else
441         {
442           static const char bin2hex[] = { '0', '1', '2', '3',
443                                           '4', '5', '6', '7',
444                                           '8', '9', 'a', 'b',
445                                           'c', 'd', 'e', 'f' };
446           int fail;
447
448           ++n_properly_formated_lines;
449
450           fail = digest_file (filename, binary, bin_buffer, digest_stream);
451
452           if (fail)
453             {
454               ++n_open_or_read_failures;
455               if (!status_only)
456                 {
457                   printf (_("%s: FAILED open or read\n"), filename);
458                   fflush (stdout);
459                 }
460             }
461           else
462             {
463               size_t digest_bin_bytes = digest_hex_bytes / 2;
464               size_t cnt;
465               /* Compare generated binary number with text representation
466                  in check file.  Ignore case of hex digits.  */
467               for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
468                 {
469                   if (TOLOWER (hex_digest[2 * cnt])
470                       != bin2hex[bin_buffer[cnt] >> 4]
471                       || (TOLOWER (hex_digest[2 * cnt + 1])
472                           != (bin2hex[bin_buffer[cnt] & 0xf])))
473                     break;
474                 }
475               if (cnt != digest_bin_bytes)
476                 ++n_mismatched_checksums;
477
478               if (!status_only)
479                 {
480                   printf ("%s: %s\n", filename,
481                           (cnt != digest_bin_bytes ? _("FAILED") : _("OK")));
482                   fflush (stdout);
483                 }
484             }
485         }
486     }
487   while (!feof (checkfile_stream) && !ferror (checkfile_stream));
488
489   if (line)
490     free (line);
491
492   if (ferror (checkfile_stream))
493     {
494       error (0, 0, _("%s: read error"), checkfile_name);
495       return 1;
496     }
497
498   if (checkfile_stream != stdin && fclose (checkfile_stream) == EOF)
499     {
500       error (0, errno, "%s", checkfile_name);
501       return 1;
502     }
503
504   if (n_properly_formated_lines == 0)
505     {
506       /* Warn if no tests are found.  */
507       error (0, 0, _("%s: no properly formatted %s checksum lines found"),
508              checkfile_name, DIGEST_TYPE_STRING (algorithm));
509     }
510   else
511     {
512       if (!status_only)
513         {
514           int n_computed_checkums = (n_properly_formated_lines
515                                      - n_open_or_read_failures);
516
517           if (n_open_or_read_failures > 0)
518             {
519               error (0, 0,
520                      _("WARNING: %d of %d listed %s could not be read"),
521                      n_open_or_read_failures, n_properly_formated_lines,
522                      (n_properly_formated_lines == 1
523                       ? _("file") : _("files")));
524             }
525
526           if (n_mismatched_checksums > 0)
527             {
528               error (0, 0,
529                    _("WARNING: %d of %d computed %s did NOT match"),
530                      n_mismatched_checksums, n_computed_checkums,
531                      (n_computed_checkums == 1
532                       ? _("checksum") : _("checksums")));
533             }
534         }
535     }
536
537   return ((n_properly_formated_lines > 0 && n_mismatched_checksums == 0
538            && n_open_or_read_failures == 0) ? 0 : 1);
539 }
540
541 int
542 main (int argc, char **argv)
543 {
544   unsigned char bin_buffer[MAX_DIGEST_BIN_BYTES];
545   int do_check = 0;
546   int opt;
547   char **string = NULL;
548   size_t n_strings = 0;
549   size_t err = 0;
550   int file_type_specified = 0;
551
552 #if O_BINARY
553   /* Binary is default on MSDOS, so the actual file contents
554      are used in computation.  */
555   int binary = 1;
556 #else
557   /* Text is default of the Plumb/Lankester format.  */
558   int binary = 0;
559 #endif
560
561   /* Setting values of global variables.  */
562   initialize_main (&argc, &argv);
563   program_name = argv[0];
564   setlocale (LC_ALL, "");
565   bindtextdomain (PACKAGE, LOCALEDIR);
566   textdomain (PACKAGE);
567
568   atexit (close_stdout);
569
570   while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
571     switch (opt)
572       {
573       case 0:                   /* long option */
574         break;
575       case 1: /* --string */
576         {
577           if (string == NULL)
578             string = xmalloc ((argc - 1) * sizeof (char *));
579
580           if (optarg == NULL)
581             optarg = "";
582           string[n_strings++] = optarg;
583         }
584         break;
585       case 'b':
586         file_type_specified = 1;
587         binary = 1;
588         break;
589       case 'c':
590         do_check = 1;
591         break;
592       case 2:
593         status_only = 1;
594         warn = 0;
595         break;
596       case 't':
597         file_type_specified = 1;
598         binary = 0;
599         break;
600       case 'w':
601         status_only = 0;
602         warn = 1;
603         break;
604       case_GETOPT_HELP_CHAR;
605       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
606       default:
607         usage (EXIT_FAILURE);
608       }
609
610   min_digest_line_length = MIN_DIGEST_LINE_LENGTH (algorithm);
611   digest_hex_bytes = DIGEST_HEX_BYTES (algorithm);
612
613   if (file_type_specified && do_check)
614     {
615       error (0, 0, _("the --binary and --text options are meaningless when \
616 verifying checksums"));
617       usage (EXIT_FAILURE);
618     }
619
620   if (n_strings > 0 && do_check)
621     {
622       error (0, 0,
623              _("the --string and --check options are mutually exclusive"));
624       usage (EXIT_FAILURE);
625     }
626
627   if (status_only && !do_check)
628     {
629       error (0, 0,
630        _("the --status option is meaningful only when verifying checksums"));
631       usage (EXIT_FAILURE);
632     }
633
634   if (warn && !do_check)
635     {
636       error (0, 0,
637        _("the --warn option is meaningful only when verifying checksums"));
638       usage (EXIT_FAILURE);
639     }
640
641   if (n_strings > 0)
642     {
643       size_t i;
644
645       if (optind < argc)
646         {
647           error (0, 0, _("no files may be specified when using --string"));
648           usage (EXIT_FAILURE);
649         }
650       for (i = 0; i < n_strings; ++i)
651         {
652           size_t cnt;
653           if (algorithm == ALG_MD5)
654             md5_buffer (string[i], strlen (string[i]), bin_buffer);
655           else
656             sha_buffer (string[i], strlen (string[i]), bin_buffer);
657
658           for (cnt = 0; cnt < (digest_hex_bytes / 2); ++cnt)
659             printf ("%02x", bin_buffer[cnt]);
660
661           printf ("  \"%s\"\n", string[i]);
662         }
663     }
664   else if (do_check)
665     {
666       if (optind + 1 < argc)
667         {
668           error (0, 0,
669                  _("only one argument may be specified when using --check"));
670           usage (EXIT_FAILURE);
671         }
672
673       err = digest_check ((optind == argc) ? "-" : argv[optind],
674                           DIGEST_STREAM (algorithm));
675     }
676   else
677     {
678       if (optind == argc)
679         argv[argc++] = "-";
680
681       for (; optind < argc; ++optind)
682         {
683           int fail;
684           char *file = argv[optind];
685
686           fail = digest_file (file, binary, bin_buffer,
687                               DIGEST_STREAM (algorithm));
688           err |= fail;
689           if (!fail)
690             {
691               size_t i;
692
693               /* Output a leading backslash if the file name contains
694                  a newline or backslash.  */
695               if (strchr (file, '\n') || strchr (file, '\\'))
696                 putchar ('\\');
697
698               for (i = 0; i < (digest_hex_bytes / 2); ++i)
699                 printf ("%02x", bin_buffer[i]);
700
701               putchar (' ');
702               if (binary)
703                 putchar ('*');
704               else
705                 putchar (' ');
706
707               /* Translate each NEWLINE byte to the string, "\\n",
708                  and each backslash to "\\\\".  */
709               for (i = 0; i < strlen (file); ++i)
710                 {
711                   switch (file[i])
712                     {
713                     case '\n':
714                       fputs ("\\n", stdout);
715                       break;
716
717                     case '\\':
718                       fputs ("\\\\", stdout);
719                       break;
720
721                     default:
722                       putchar (file[i]);
723                       break;
724                     }
725                 }
726               putchar ('\n');
727             }
728         }
729     }
730
731   if (have_read_stdin && fclose (stdin) == EOF)
732     error (EXIT_FAILURE, errno, _("standard input"));
733
734   exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
735 }