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