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