VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/md5sum.c@ 3173

Last change on this file since 3173 was 3159, checked in by bird, 7 years ago

kmk/win: Some fixes & docs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.8 KB
Line 
1/* $Id: md5sum.c 3159 2018-03-19 13:37:13Z bird $ */
2/** @file
3 * md5sum.
4 */
5
6/*
7 * Copyright (c) 2007-2010 knut st. osmundsen <[email protected]>
8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#include "config.h"
30#include <string.h>
31#include <stdio.h>
32#include <errno.h>
33#include <fcntl.h>
34#ifdef _MSC_VER
35# include <io.h>
36#else
37# include <unistd.h>
38#endif
39#include <sys/stat.h>
40#include "err.h"
41#include "kmkbuiltin.h"
42#include "../../lib/md5.h"
43#include <k/kTypes.h>
44
45/*#define MD5SUM_USE_STDIO*/
46
47
48/**
49 * Prints the usage and return 1.
50 */
51static int usage(FILE *pOut)
52{
53 fprintf(pOut,
54 "usage: md5sum [-bt] [-o list-file] file(s)\n"
55 " or: md5sum [-btwq] -c list-file(s)\n"
56 " or: md5sum [-btq] -C MD5 file\n"
57 "\n"
58 " -c, --check Check MD5 and files found in the specified list file(s).\n"
59 " The default is to compute MD5 sums of the specified files\n"
60 " and print them to stdout in list form.\n"
61 " -C, --check-file This is followed by an MD5 sum and the file to check.\n"
62 " -b, --binary Read files in binary mode. (default)\n"
63 " -t, --text Read files in text mode.\n"
64 " -m, --manifest Output in kBuild fetch 'manifest' format.\n"
65 " -p, --progress Show progress indicator on large files.\n"
66 " -o, --output Name of the output list file. Useful with -p.\n"
67 " -q, --status Be quiet.\n"
68 " -w, --warn Ignored. Always warn, unless quiet.\n"
69 " -h, --help This usage info.\n"
70 " -v, --version Show version information and exit.\n"
71 );
72 return 1;
73}
74
75
76/**
77 * Makes a string out of the given digest.
78 *
79 * @param pDigest The MD5 digest.
80 * @param pszDigest Where to put the digest string. Must be able to
81 * hold at least 33 bytes.
82 */
83static void digest_to_string(unsigned char pDigest[16], char *pszDigest)
84{
85 unsigned i;
86 for (i = 0; i < 16; i++)
87 {
88 static char s_achDigits[17] = "0123456789abcdef";
89 pszDigest[i*2] = s_achDigits[(pDigest[i] >> 4)];
90 pszDigest[i*2 + 1] = s_achDigits[(pDigest[i] & 0xf)];
91 }
92 pszDigest[i*2] = '\0';
93}
94
95
96/**
97 * Attempts to convert a string to a MD5 digest.
98 *
99 * @returns 0 on success, 1-based position of the failure first error.
100 * @param pszDigest The string to interpret.
101 * @param pDigest Where to put the MD5 digest.
102 */
103static int string_to_digest(const char *pszDigest, unsigned char pDigest[16])
104{
105 unsigned i;
106 unsigned iBase = 1;
107
108 /* skip blanks */
109 while ( *pszDigest == ' '
110 || *pszDigest == '\t'
111 || *pszDigest == '\n'
112 || *pszDigest == '\r')
113 pszDigest++, iBase++;
114
115 /* convert the digits. */
116 memset(pDigest, 0, 16);
117 for (i = 0; i < 32; i++, pszDigest++)
118 {
119 int iDigit;
120 if (*pszDigest >= '0' && *pszDigest <= '9')
121 iDigit = *pszDigest - '0';
122 else if (*pszDigest >= 'a' && *pszDigest <= 'f')
123 iDigit = *pszDigest - 'a' + 10;
124 else if (*pszDigest >= 'A' && *pszDigest <= 'F')
125 iDigit = *pszDigest - 'A' + 10;
126 else
127 return i + iBase;
128 if (i & 1)
129 pDigest[i >> 1] |= iDigit;
130 else
131 pDigest[i >> 1] |= iDigit << 4;
132 }
133
134 /* the rest of the string must now be blanks. */
135 while ( *pszDigest == ' '
136 || *pszDigest == '\t'
137 || *pszDigest == '\n'
138 || *pszDigest == '\r')
139 pszDigest++, i++;
140
141 return *pszDigest ? i + iBase : 0;
142}
143
144
145/**
146 * Opens the specified file for md5 sum calculation.
147 *
148 * @returns Opaque pointer on success, NULL and errno on failure.
149 * @param pszFilename The filename.
150 * @param fText Whether text or binary mode should be used.
151 */
152static void *open_file(const char *pszFilename, unsigned fText)
153{
154#if defined(MD5SUM_USE_STDIO)
155 FILE *pFile;
156
157 errno = 0;
158 pFile = fopen(pszFilename,
159 fText ? "r" KMK_FOPEN_NO_INHERIT_MODE
160 : "rb" KMK_FOPEN_NO_INHERIT_MODE);
161 if (!pFile && errno == EINVAL && !fText)
162 pFile = fopen(pszFilename, "r" KMK_FOPEN_NO_INHERIT_MODE);
163 return pFile;
164
165#else
166 int fd;
167 int fFlags;
168
169 /* figure out the appropriate flags. */
170 fFlags = O_RDONLY;
171#ifdef O_SEQUENTIAL
172 fFlags |= _O_SEQUENTIAL;
173#elif defined(_O_SEQUENTIAL)
174 fFlags |= _O_SEQUENTIAL;
175#endif
176#ifdef O_BINARY
177 if (!fText) fFlags |= O_BINARY;
178#elif defined(_O_BINARY)
179 if (!fText) fFlags |= _O_BINARY;
180#endif
181#ifdef O_TEXT
182 if (fText) fFlags |= O_TEXT;
183#elif defined(O_TEXT)
184 if (fText) fFlags |= _O_TEXT;
185#else
186 (void)fText;
187#endif
188
189 errno = 0;
190 fd = open(pszFilename, fFlags, 0755);
191 if (fd >= 0)
192 {
193 int *pFd = malloc(sizeof(*pFd));
194 if (pFd)
195 {
196 *pFd = fd;
197 return pFd;
198 }
199 close(fd);
200 errno = ENOMEM;
201 }
202
203 return NULL;
204#endif
205}
206
207
208/**
209 * Closes a file opened by open_file.
210 *
211 * @param pvFile The opaque pointer returned by open_file.
212 */
213static void close_file(void *pvFile)
214{
215#if defined(MD5SUM_USE_STDIO)
216 fclose((FILE *)pvFile);
217#else
218 close(*(int *)pvFile);
219 free(pvFile);
220#endif
221}
222
223
224/**
225 * Reads from a file opened by open_file.
226 *
227 * @returns Number of bytes read on success.
228 * 0 on EOF.
229 * Negated errno on read error.
230 * @param pvFile The opaque pointer returned by open_file.
231 * @param pvBuf Where to put the number of read bytes.
232 * @param cbBuf The max number of bytes to read.
233 * Must be less than a INT_MAX.
234 */
235static int read_file(void *pvFile, void *pvBuf, size_t cbBuf)
236{
237#if defined(MD5SUM_USE_STDIO)
238 int cb;
239
240 errno = 0;
241 cb = (int)fread(pvBuf, 1, cbBuf, (FILE *)pvFile);
242 if (cb >= 0)
243 return (int)cb;
244 if (!errno)
245 return -EINVAL;
246 return -errno;
247#else
248 int cb;
249
250 errno = 0;
251 cb = (int)read(*(int *)pvFile, pvBuf, (int)cbBuf);
252 if (cb >= 0)
253 return (int)cb;
254 if (!errno)
255 return -EINVAL;
256 return -errno;
257#endif
258}
259
260
261/**
262 * Gets the size of the file.
263 * This is informational and not necessarily 100% accurate.
264 *
265 * @returns File size.
266 * @param pvFile The opaque pointer returned by open_file
267 */
268static KU64 size_file(void *pvFile)
269{
270#if defined(_MSC_VER)
271 __int64 cb;
272# if defined(MD5SUM_USE_STDIO)
273 cb = _filelengthi64(fileno((FILE *)pvFile));
274# else
275 cb = _filelengthi64(*(int *)pvFile);
276# endif
277 if (cb >= 0)
278 return cb;
279
280#elif defined(MD5SUM_USE_STDIO)
281 struct stat st;
282 if (!fstat(fileno((FILE *)pvFile), &st))
283 return st.st_size;
284
285#else
286 struct stat st;
287 if (!fstat(*(int *)pvFile, &st))
288 return st.st_size;
289#endif
290 return 1024;
291}
292
293
294/**
295 * Calculates the md5sum of the sepecified file stream.
296 *
297 * @returns errno on failure, 0 on success.
298 * @param pvFile The file stream.
299 * @param pDigest Where to store the MD5 digest.
300 * @param fProgress Whether to show a progress bar.
301 * @param pcbFile Where to return the file size. Optional.
302 */
303static int calc_md5sum(void *pvFile, unsigned char pDigest[16], unsigned fProgress, KU64 *pcbFile)
304{
305 int cb;
306 int rc = 0;
307 struct MD5Context Ctx;
308 unsigned uPercent = 0;
309 KU64 off = 0;
310 KU64 const cbFile = size_file(pvFile);
311
312 /* Get a decent sized buffer assuming we'll be spending more time reading
313 from the storage than doing MD5 sums. (2MB was choosen based on recent
314 SATA storage benchmarks which used that block size for sequential
315 tests.) We align the buffer address on a 16K boundrary to avoid most
316 transfer alignment issues. */
317 char *pabBufAligned;
318 size_t const cbBufAlign = 16*1024 - 1;
319 size_t const cbBufMax = 2048*1024;
320 size_t cbBuf = cbFile >= cbBufMax ? cbBufMax : ((size_t)cbFile + cbBufAlign) & ~(size_t)cbBufAlign;
321 char *pabBuf = (char *)malloc(cbBuf + cbBufAlign);
322 if (pabBuf)
323 pabBufAligned = (char *)(((uintptr_t)pabBuf + cbBufAlign) & ~(uintptr_t)cbBufAlign );
324 else
325 {
326 do
327 {
328 cbBuf /= 2;
329 pabBuf = (char *)malloc(cbBuf);
330 } while (!pabBuf && cbBuf > 4096);
331 if (!pabBuf)
332 return ENOMEM;
333 pabBufAligned = pabBuf;
334 }
335
336 if (cbFile < cbBuf * 4)
337 fProgress = 0;
338
339 MD5Init(&Ctx);
340 for (;;)
341 {
342 /* process a chunk. */
343 cb = read_file(pvFile, pabBufAligned, cbBuf);
344 if (cb > 0)
345 MD5Update(&Ctx, (unsigned char *)pabBufAligned, cb);
346 else if (!cb)
347 break;
348 else
349 {
350 rc = -cb;
351 break;
352 }
353 off += cb;
354
355 /* update the progress indicator. */
356 if (fProgress)
357 {
358 unsigned uNewPercent;
359 uNewPercent = (unsigned)(((double)off / cbFile) * 100);
360 if (uNewPercent != uPercent)
361 {
362 if (uPercent)
363 printf("\b\b\b\b");
364 printf("%3d%%", uNewPercent);
365 fflush(stdout);
366 uPercent = uNewPercent;
367 }
368 }
369 }
370 MD5Final(pDigest, &Ctx);
371
372 if (pcbFile)
373 *pcbFile = off;
374
375 if (fProgress)
376 printf("\b\b\b\b \b\b\b\b");
377
378 free(pabBuf);
379 return rc;
380}
381
382
383/**
384 * Checks the if the specified digest matches the digest of the file stream.
385 *
386 * @returns 0 on match, -1 on mismatch, errno value (positive) on failure.
387 * @param pvFile The file stream.
388 * @param Digest The MD5 digest.
389 * @param fProgress Whether to show an progress indicator on large files.
390 */
391static int check_md5sum(void *pvFile, unsigned char Digest[16], unsigned fProgress)
392{
393 unsigned char DigestFile[16];
394 int rc;
395
396 rc = calc_md5sum(pvFile, DigestFile, fProgress, NULL);
397 if (!rc)
398 rc = memcmp(Digest, DigestFile, 16) ? -1 : 0;
399 return rc;
400}
401
402
403/**
404 * Checks if the specified file matches the given MD5 digest.
405 *
406 * @returns 0 if it matches, 1 if it doesn't or an error occurs.
407 * @param pszFilename The name of the file to check.
408 * @param pszDigest The MD5 digest string.
409 * @param fText Whether to open the file in text or binary mode.
410 * @param fQuiet Whether to go about this in a quiet fashion or not.
411 * @param fProgress Whether to show an progress indicator on large files.
412 */
413static int check_one_file(const char *pszFilename, const char *pszDigest, unsigned fText, unsigned fQuiet, unsigned fProgress)
414{
415 unsigned char Digest[16];
416 int rc;
417
418 rc = string_to_digest(pszDigest, Digest);
419 if (!rc)
420 {
421 void *pvFile;
422
423 pvFile = open_file(pszFilename, fText);
424 if (pvFile)
425 {
426 if (!fQuiet)
427 fprintf(stdout, "%s: ", pszFilename);
428 rc = check_md5sum(pvFile, Digest, fProgress);
429 close_file(pvFile);
430 if (!fQuiet)
431 {
432 fprintf(stdout, "%s\n", !rc ? "OK" : rc < 0 ? "FAILURE" : "ERROR");
433 fflush(stdout);
434 if (rc > 0)
435 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc));
436 }
437 if (rc)
438 rc = 1;
439 }
440 else
441 {
442 if (!fQuiet)
443 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
444 rc = 1;
445 }
446 }
447 else
448 {
449 errx(1, "Malformed MD5 digest '%s'!", pszDigest);
450 errx(1, " %*s^", rc - 1, "");
451 rc = 1;
452 }
453
454 return rc;
455}
456
457
458/**
459 * Checks the specified md5.lst file.
460 *
461 * @returns 0 if all checks out file, 1 if one or more fails or there are read errors.
462 * @param pszFilename The name of the file.
463 * @param fText The default mode, text or binary. Only used when fBinaryTextOpt is true.
464 * @param fBinaryTextOpt Whether a -b or -t option was specified and should be used.
465 * @param fQuiet Whether to be quiet.
466 * @param fProgress Whether to show an progress indicator on large files.
467 */
468static int check_files(const char *pszFilename, int fText, int fBinaryTextOpt, int fQuiet, unsigned fProgress)
469{
470 int rc = 0;
471 FILE *pFile;
472
473 /*
474 * Try open the md5.lst file and process it line by line.
475 */
476 pFile = fopen(pszFilename, "r" KMK_FOPEN_NO_INHERIT_MODE);
477 if (pFile)
478 {
479 int iLine = 0;
480 char szLine[8192];
481 while (fgets(szLine, sizeof(szLine), pFile))
482 {
483 const char *pszDigest;
484 int fLineText;
485 char *psz;
486 int rc2;
487
488 iLine++;
489 psz = szLine;
490
491 /* leading blanks */
492 while (*psz == ' ' || *psz == '\t' || *psz == '\n')
493 psz++;
494
495 /* skip blank or comment lines. */
496 if (!*psz || *psz == '#' || *psz == ';' || *psz == '/')
497 continue;
498
499 /* remove the trailing newline. */
500 rc2 = (int)strlen(psz);
501 if (psz[rc2 - 1] == '\n')
502 psz[rc2 - (rc2 >= 2 && psz[rc2 - 2] == '\r' ? 2 : 1)] = '\0';
503
504 /* skip to the end of the digest and terminate it. */
505 pszDigest = psz;
506 while (*psz != ' ' && *psz != '\t' && *psz)
507 psz++;
508 if (*psz)
509 {
510 *psz++ = '\0';
511
512 /* blanks */
513 while (*psz == ' ' || *psz == '\t' || *psz == '\n')
514 psz++;
515
516 /* check for binary asterix */
517 if (*psz != '*')
518 fLineText = fBinaryTextOpt ? fText : 0;
519 else
520 {
521 fLineText = 0;
522 psz++;
523 }
524 if (*psz)
525 {
526 unsigned char Digest[16];
527
528 /* the rest is filename. */
529 pszFilename = psz;
530
531 /*
532 * Do the job.
533 */
534 rc2 = string_to_digest(pszDigest, Digest);
535 if (!rc2)
536 {
537 void *pvFile = open_file(pszFilename, fLineText);
538 if (pvFile)
539 {
540 if (!fQuiet)
541 fprintf(stdout, "%s: ", pszFilename);
542 rc2 = check_md5sum(pvFile, Digest, fProgress);
543 close_file(pvFile);
544 if (!fQuiet)
545 {
546 fprintf(stdout, "%s\n", !rc2 ? "OK" : rc2 < 0 ? "FAILURE" : "ERROR");
547 fflush(stdout);
548 if (rc2 > 0)
549 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc2));
550 }
551 if (rc2)
552 rc = 1;
553 }
554 else
555 {
556 if (!fQuiet)
557 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
558 rc = 1;
559 }
560 }
561 else if (!fQuiet)
562 {
563 errx(1, "%s (%d): Ignoring malformed digest '%s' (digest)", pszFilename, iLine, pszDigest);
564 errx(1, "%s (%d): %*s^", pszFilename, iLine, rc2 - 1, "");
565 }
566 }
567 else if (!fQuiet)
568 errx(1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
569 }
570 else if (!fQuiet)
571 errx(1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
572 } /* while more lines */
573
574 fclose(pFile);
575 }
576 else
577 {
578 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
579 rc = 1;
580 }
581
582 return rc;
583}
584
585
586/**
587 * Calculates the MD5 sum for one file and prints it.
588 *
589 * @returns 0 on success, 1 on any kind of failure.
590 * @param pszFilename The file to process.
591 * @param fText The mode to open the file in.
592 * @param fQuiet Whether to be quiet or verbose about errors.
593 * @param fManifest Whether to format the output like a fetch manifest.
594 * @param fProgress Whether to show an progress indicator on large files.
595 * @param pOutput Where to write the list. Progress is always written to stdout.
596 */
597static int md5sum_file(const char *pszFilename, unsigned fText, unsigned fQuiet, unsigned fProgress,
598 unsigned fManifest, FILE *pOutput)
599{
600 int rc;
601 void *pvFile;
602
603 /*
604 * Calculate and print the MD5 sum for one file.
605 */
606 pvFile = open_file(pszFilename, fText);
607 if (pvFile)
608 {
609 unsigned char Digest[16];
610 KU64 cbFile = 0;
611
612 if (fProgress && pOutput)
613 fprintf(stdout, "%s: ", pszFilename);
614
615 rc = calc_md5sum(pvFile, Digest, fProgress, &cbFile);
616 close_file(pvFile);
617
618 if (fProgress && pOutput)
619 {
620 size_t cch = strlen(pszFilename) + 2;
621 while (cch-- > 0)
622 fputc('\b', stdout);
623 }
624
625 if (!rc)
626 {
627 char szDigest[36];
628 digest_to_string(Digest, szDigest);
629 if (!fManifest)
630 {
631 if (pOutput)
632 fprintf(pOutput, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
633 fprintf(stdout, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
634 }
635 else
636 {
637 if (pOutput)
638 fprintf(pOutput, "%s_SIZE := %" KU64_PRI "\n%s_MD5 := %s\n", pszFilename, cbFile, pszFilename, szDigest);
639 fprintf(stdout, "%s_SIZE := %" KU64_PRI "\n%s_MD5 := %s\n", pszFilename, cbFile, pszFilename, szDigest);
640 }
641 if (pOutput)
642 fflush(pOutput);
643 fflush(stdout);
644 }
645 else
646 {
647 if (!fQuiet)
648 errx(1, "Failed to open '%s': %s", pszFilename, strerror(rc));
649 rc = 1;
650 }
651 }
652 else
653 {
654 if (!fQuiet)
655 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
656 rc = 1;
657 }
658 return rc;
659}
660
661
662
663/**
664 * md5sum, calculates and checks the md5sum of files.
665 * Somewhat similar to the GNU coreutil md5sum command.
666 */
667int kmk_builtin_md5sum(int argc, char **argv, char **envp)
668{
669 int i;
670 int rc = 0;
671 int fText = 0;
672 int fBinaryTextOpt = 0;
673 int fQuiet = 0;
674 int fChecking = 0;
675 int fManifest = 0;
676 int fProgress = 0;
677 int fNoMoreOptions = 0;
678 const char *pszOutput = NULL;
679 FILE *pOutput = NULL;
680
681 g_progname = argv[0];
682
683 /*
684 * Print usage if no arguments.
685 */
686 if (argc <= 1)
687 return usage(stderr);
688
689 /*
690 * Process the arguments, FIFO style.
691 */
692 i = 1;
693 while (i < argc)
694 {
695 char *psz = argv[i];
696 if (!fNoMoreOptions && psz[0] == '-' && psz[1] == '-' && !psz[2])
697 fNoMoreOptions = 1;
698 else if (*psz == '-' && !fNoMoreOptions)
699 {
700 psz++;
701
702 /* convert long options for gnu just for fun */
703 if (*psz == '-')
704 {
705 if (!strcmp(psz, "-binary"))
706 psz = "b";
707 else if (!strcmp(psz, "-text"))
708 psz = "t";
709 else if (!strcmp(psz, "-check"))
710 psz = "c";
711 else if (!strcmp(psz, "-check-file"))
712 psz = "C";
713 else if (!strcmp(psz, "-manifest"))
714 psz = "m";
715 else if (!strcmp(psz, "-output"))
716 psz = "o";
717 else if (!strcmp(psz, "-progress"))
718 psz = "p";
719 else if (!strcmp(psz, "-status"))
720 psz = "q";
721 else if (!strcmp(psz, "-warn"))
722 psz = "w";
723 else if (!strcmp(psz, "-help"))
724 psz = "h";
725 else if (!strcmp(psz, "-version"))
726 psz = "v";
727 }
728
729 /* short options */
730 do
731 {
732 switch (*psz)
733 {
734 case 'c':
735 fChecking = 1;
736 break;
737
738 case 'b':
739 fText = 0;
740 fBinaryTextOpt = 1;
741 break;
742
743 case 't':
744 fText = 1;
745 fBinaryTextOpt = 1;
746 break;
747
748 case 'm':
749 fManifest = 1;
750 break;
751
752 case 'p':
753 fProgress = 1;
754 break;
755
756 case 'q':
757 fQuiet = 1;
758 break;
759
760 case 'w':
761 /* ignored */
762 break;
763
764 case 'h':
765 usage(stdout);
766 return 0;
767
768 case 'v':
769 return kbuild_version(argv[0]);
770
771 /*
772 * -C md5 file
773 */
774 case 'C':
775 {
776 const char *pszFilename;
777 const char *pszDigest;
778
779 if (psz[1])
780 pszDigest = &psz[1];
781 else if (i + 1 < argc)
782 pszDigest = argv[++i];
783 else
784 {
785 errx(1, "'-C' is missing the MD5 sum!");
786 return 1;
787 }
788 if (i + 1 < argc)
789 pszFilename = argv[++i];
790 else
791 {
792 errx(1, "'-C' is missing the filename!");
793 return 1;
794 }
795
796 rc |= check_one_file(pszFilename, pszDigest, fText, fQuiet, fProgress && !fQuiet);
797 psz = "\0";
798 break;
799 }
800
801 /*
802 * Output file.
803 */
804 case 'o':
805 {
806 if (fChecking)
807 {
808 errx(1, "'-o' cannot be used with -c or -C!");
809 return 1;
810 }
811
812 if (psz[1])
813 pszOutput = &psz[1];
814 else if (i + 1 < argc)
815 pszOutput = argv[++i];
816 else
817 {
818 errx(1, "'-o' is missing the file name!");
819 return 1;
820 }
821
822 psz = "\0";
823 break;
824 }
825
826 default:
827 errx(1, "Invalid option '%c'! (%s)", *psz, argv[i]);
828 return usage(stderr);
829 }
830 } while (*++psz);
831 }
832 else if (fChecking)
833 rc |= check_files(argv[i], fText, fBinaryTextOpt, fQuiet, fProgress && !fQuiet);
834 else
835 {
836 /* lazily open the output if specified. */
837 if (pszOutput)
838 {
839 if (pOutput)
840 fclose(pOutput);
841 pOutput = fopen(pszOutput, "w" KMK_FOPEN_NO_INHERIT_MODE);
842 if (!pOutput)
843 {
844 rc = err(1, "fopen(\"%s\", \"w" KMK_FOPEN_NO_INHERIT_MODE "\") failed", pszOutput);
845 break;
846 }
847 pszOutput = NULL;
848 }
849
850 rc |= md5sum_file(argv[i], fText, fQuiet, fProgress && !fQuiet && !fManifest, fManifest, pOutput);
851 }
852 i++;
853 }
854
855 if (pOutput)
856 fclose(pOutput);
857 return rc;
858}
859
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette