diff -up coreutils-8.17/src/md5sum.c.hashtag coreutils-8.17/src/md5sum.c --- coreutils-8.17/src/md5sum.c.hashtag 2012-07-31 11:49:26.325612606 +0200 +++ coreutils-8.17/src/md5sum.c 2012-07-31 12:57:16.598535788 +0200 @@ -135,7 +135,8 @@ enum { STATUS_OPTION = CHAR_MAX + 1, QUIET_OPTION, - STRICT_OPTION + STRICT_OPTION, + TAG_OPTION }; static struct option const long_options[] = @@ -147,6 +148,7 @@ static struct option const long_options[ { "text", no_argument, NULL, 't' }, { "warn", no_argument, NULL, 'w' }, { "strict", no_argument, NULL, STRICT_OPTION }, + { "tag", no_argument, NULL, TAG_OPTION }, { GETOPT_HELP_OPTION_DECL }, { GETOPT_VERSION_OPTION_DECL }, { NULL, 0, NULL, 0 } @@ -215,21 +217,72 @@ space for text), and name for each FILE. #define ISWHITE(c) ((c) == ' ' || (c) == '\t') +/* Translate each '\n' string in the file name beginning + at string S (of length S_LEN) to a NEWLINE, + and each '\\' string to a backslash; FILE_NAME becoming + the pointer used to print the actual file name. Return + true unless file name is invalid. */ + +static bool +filename_unescape (char *s, size_t s_len, char **file_name) +{ + + char *dst = s; + size_t i = 0; + + while (i < s_len) + { + switch (s[i]) + { + case '\\': + if (i == s_len - 1) + { + /* A valid line does not end with a backslash. */ + return false; + } + ++i; + switch (s[i++]) + { + case 'n': + *dst++ = '\n'; + break; + case '\\': + *dst++ = '\\'; + break; + default: + /* Only '\' or 'n' may follow a backslash. */ + return false; + } + break; + + case '\0': + /* The file name may not contain a NUL. */ + return false; + + default: + *dst++ = s[i++]; + break; + } + } + *dst = '\0'; + + *file_name = s; + + return true; +} + /* Split the checksum string S (of length S_LEN) from a BSD 'md5' or 'sha1' command into two parts: a hexadecimal digest, and the file name. S is modified. Return true if successful. */ static bool bsd_split_3 (char *s, size_t s_len, unsigned char **hex_digest, - char **file_name) + char **file_name, bool escaped_filename) { size_t i; if (s_len == 0) return false; - - *file_name = s; - /* Find end of filename. The BSD 'md5' and 'sha1' commands do not escape filenames, so search backwards for the last ')'. */ i = s_len - 1; @@ -239,6 +292,12 @@ bsd_split_3 (char *s, size_t s_len, unsi if (s[i] != ')') return false; + *file_name = s; + + if (escaped_filename) + if (!filename_unescape (s, i, file_name)) + return false; + s[i++] = '\0'; while (ISWHITE (s[i])) @@ -271,7 +330,16 @@ split_3 (char *s, size_t s_len, while (ISWHITE (s[i])) ++i; + if (s[i] == '\\') + { + ++i; + escaped_filename = true; + } + /* Check for BSD-style checksum line. */ + if (s[i] == ' ') + ++i; + algo_name_len = strlen (DIGEST_TYPE_STRING); if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len)) { @@ -282,9 +350,12 @@ split_3 (char *s, size_t s_len, *binary = 0; return bsd_split_3 (s + i + algo_name_len + 1, s_len - (i + algo_name_len + 1), - hex_digest, file_name); + hex_digest, file_name, escaped_filename); } } + else if (escaped_filename && (s[i] == ' ' || s[i + 1] == ' ')) + return false; + /* Ignore this line if it is too short. Each line must have at least 'min_digest_line_length - 1' (or one more, if @@ -293,12 +364,7 @@ split_3 (char *s, size_t s_len, if (s_len - i < min_digest_line_length + (s[i] == '\\')) return false; - if (s[i] == '\\') - { - ++i; - escaped_filename = true; - } - *hex_digest = (unsigned char *) &s[i]; + *hex_digest = (unsigned char *) &s[i]; /* The first field has to be the n-character hexadecimal representation of the message digest. If it is not followed @@ -333,49 +399,8 @@ split_3 (char *s, size_t s_len, *file_name = &s[i]; if (escaped_filename) - { - /* Translate each '\n' string in the file name to a NEWLINE, - and each '\\' string to a backslash. */ - - char *dst = &s[i]; - - while (i < s_len) - { - switch (s[i]) - { - case '\\': - if (i == s_len - 1) - { - /* A valid line does not end with a backslash. */ - return false; - } - ++i; - switch (s[i++]) - { - case 'n': - *dst++ = '\n'; - break; - case '\\': - *dst++ = '\\'; - break; - default: - /* Only '\' or 'n' may follow a backslash. */ - return false; - } - break; - - case '\0': - /* The file name may not contain a NUL. */ - return false; - break; + return filename_unescape (&s[i], s_len - i, file_name); - default: - *dst++ = s[i++]; - break; - } - } - *dst = '\0'; - } return true; } @@ -636,6 +661,31 @@ digest_check (const char *checkfile_name && (!strict || n_improperly_formatted_lines == 0)); } +static void +print_filename (char const *file) +{ + /* Translate each NEWLINE byte to the string, "\\n", + and each backslash to "\\\\". */ + while (*file) + { + switch (*file) + { + case '\n': + fputs ("\\n", stdout); + break; + + case '\\': + fputs ("\\\\", stdout); + break; + + default: + putchar (*file); + break; + } + file++; + } +} + int main (int argc, char **argv) { @@ -646,6 +696,7 @@ main (int argc, char **argv) int opt; bool ok = true; int binary = -1; + bool prefix_tag = false; /* Setting values of global variables. */ initialize_main (&argc, &argv); @@ -690,6 +741,9 @@ main (int argc, char **argv) case STRICT_OPTION: strict = true; break; + case TAG_OPTION: + prefix_tag = true; + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: @@ -754,41 +808,37 @@ main (int argc, char **argv) ok = false; else { + if (prefix_tag) + { + if (strchr (file, '\n') || strchr (file, '\\')) + putchar ('\\'); + if (!file_is_binary) + putchar (' '); + fputs (DIGEST_TYPE_STRING, stdout); + fputs(" (", stdout); + print_filename (file); + fputs (") = ", stdout); + } + size_t i; /* Output a leading backslash if the file name contains a newline or backslash. */ - if (strchr (file, '\n') || strchr (file, '\\')) + if (!prefix_tag && (strchr (file, '\n') || strchr (file, '\\'))) putchar ('\\'); for (i = 0; i < (digest_hex_bytes / 2); ++i) printf ("%02x", bin_buffer[i]); - putchar (' '); - if (file_is_binary) - putchar ('*'); - else - putchar (' '); - - /* Translate each NEWLINE byte to the string, "\\n", - and each backslash to "\\\\". */ - for (i = 0; i < strlen (file); ++i) + if (!prefix_tag) { - switch (file[i]) - { - case '\n': - fputs ("\\n", stdout); - break; - - case '\\': - fputs ("\\\\", stdout); - break; - - default: - putchar (file[i]); - break; - } + putchar (' '); + + putchar (file_is_binary ? '*' : ' '); + + print_filename (file); } + putchar ('\n'); } } diff -up coreutils-8.17/tests/misc/md5sum-bsd.hashtag coreutils-8.17/tests/misc/md5sum-bsd --- coreutils-8.17/tests/misc/md5sum-bsd.hashtag 2012-07-31 12:44:27.729009885 +0200 +++ coreutils-8.17/tests/misc/md5sum-bsd 2012-07-31 12:49:49.547002374 +0200 @@ -38,4 +38,15 @@ md5sum --strict -c check.md5 || fail=1 # an option to avoid the ambiguity. tail -n+2 check.md5 | md5sum --strict -c && fail=1 +#--tag option test + +for i in 'a' ' b' '*c' 'dd' ' '; do + echo "$i" > "$i" + md5sum --tag "$i" >> check.md5sum +done +sed 's/ / /' check.md5sum > check.md5 + +md5sum --strict -c check.md5sum || fail=1 +md5sum --strict -c check.md5 || fail=1 + Exit $fail