VirtualBox

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

Last change on this file since 3141 was 3131, checked in by bird, 7 years ago
kmk_md5sum: Added -mmanifest option to generate the fetch manifest syntax kBuild uses.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.6 KB
Line 
1/* $Id: md5sum.c 3131 2018-02-09 13:19:39Z 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, fText ? "r" : "rb");
159 if (!pFile && errno == EINVAL && !fText)
160 pFile = fopen(pszFilename, "r");
161 return pFile;
162
163#else
164 int fd;
165 int fFlags;
166
167 /* figure out the appropriate flags. */
168 fFlags = O_RDONLY;
169#ifdef O_SEQUENTIAL
170 fFlags |= _O_SEQUENTIAL;
171#elif defined(_O_SEQUENTIAL)
172 fFlags |= _O_SEQUENTIAL;
173#endif
174#ifdef O_BINARY
175 if (!fText) fFlags |= O_BINARY;
176#elif defined(_O_BINARY)
177 if (!fText) fFlags |= _O_BINARY;
178#endif
179#ifdef O_TEXT
180 if (fText) fFlags |= O_TEXT;
181#elif defined(O_TEXT)
182 if (fText) fFlags |= _O_TEXT;
183#else
184 (void)fText;
185#endif
186
187 errno = 0;
188 fd = open(pszFilename, fFlags, 0755);
189 if (fd >= 0)
190 {
191 int *pFd = malloc(sizeof(*pFd));
192 if (pFd)
193 {
194 *pFd = fd;
195 return pFd;
196 }
197 close(fd);
198 errno = ENOMEM;
199 }
200
201 return NULL;
202#endif
203}
204
205
206/**
207 * Closes a file opened by open_file.
208 *
209 * @param pvFile The opaque pointer returned by open_file.
210 */
211static void close_file(void *pvFile)
212{
213#if defined(MD5SUM_USE_STDIO)
214 fclose((FILE *)pvFile);
215#else
216 close(*(int *)pvFile);
217 free(pvFile);
218#endif
219}
220
221
222/**
223 * Reads from a file opened by open_file.
224 *
225 * @returns Number of bytes read on success.
226 * 0 on EOF.
227 * Negated errno on read error.
228 * @param pvFile The opaque pointer returned by open_file.
229 * @param pvBuf Where to put the number of read bytes.
230 * @param cbBuf The max number of bytes to read.
231 * Must be less than a INT_MAX.
232 */
233static int read_file(void *pvFile, void *pvBuf, size_t cbBuf)
234{
235#if defined(MD5SUM_USE_STDIO)
236 int cb;
237
238 errno = 0;
239 cb = (int)fread(pvBuf, 1, cbBuf, (FILE *)pvFile);
240 if (cb >= 0)
241 return (int)cb;
242 if (!errno)
243 return -EINVAL;
244 return -errno;
245#else
246 int cb;
247
248 errno = 0;
249 cb = (int)read(*(int *)pvFile, pvBuf, (int)cbBuf);
250 if (cb >= 0)
251 return (int)cb;
252 if (!errno)
253 return -EINVAL;
254 return -errno;
255#endif
256}
257
258
259/**
260 * Gets the size of the file.
261 * This is informational and not necessarily 100% accurate.
262 *
263 * @returns File size.
264 * @param pvFile The opaque pointer returned by open_file
265 */
266static KU64 size_file(void *pvFile)
267{
268#if defined(_MSC_VER)
269 __int64 cb;
270# if defined(MD5SUM_USE_STDIO)
271 cb = _filelengthi64(fileno((FILE *)pvFile));
272# else
273 cb = _filelengthi64(*(int *)pvFile);
274# endif
275 if (cb >= 0)
276 return cb;
277
278#elif defined(MD5SUM_USE_STDIO)
279 struct stat st;
280 if (!fstat(fileno((FILE *)pvFile), &st))
281 return st.st_size;
282
283#else
284 struct stat st;
285 if (!fstat(*(int *)pvFile, &st))
286 return st.st_size;
287#endif
288 return 1024;
289}
290
291
292/**
293 * Calculates the md5sum of the sepecified file stream.
294 *
295 * @returns errno on failure, 0 on success.
296 * @param pvFile The file stream.
297 * @param pDigest Where to store the MD5 digest.
298 * @param fProgress Whether to show a progress bar.
299 * @param pcbFile Where to return the file size. Optional.
300 */
301static int calc_md5sum(void *pvFile, unsigned char pDigest[16], unsigned fProgress, KU64 *pcbFile)
302{
303 int cb;
304 int rc = 0;
305 struct MD5Context Ctx;
306 unsigned uPercent = 0;
307 KU64 off = 0;
308 KU64 const cbFile = size_file(pvFile);
309
310 /* Get a decent sized buffer assuming we'll be spending more time reading
311 from the storage than doing MD5 sums. (2MB was choosen based on recent
312 SATA storage benchmarks which used that block size for sequential
313 tests.) We align the buffer address on a 16K boundrary to avoid most
314 transfer alignment issues. */
315 char *pabBufAligned;
316 size_t const cbBufAlign = 16*1024 - 1;
317 size_t const cbBufMax = 2048*1024;
318 size_t cbBuf = cbFile >= cbBufMax ? cbBufMax : ((size_t)cbFile + cbBufAlign) & ~(size_t)cbBufAlign;
319 char *pabBuf = (char *)malloc(cbBuf + cbBufAlign);
320 if (pabBuf)
321 pabBufAligned = (char *)(((uintptr_t)pabBuf + cbBufAlign) & ~(uintptr_t)cbBufAlign );
322 else
323 {
324 do
325 {
326 cbBuf /= 2;
327 pabBuf = (char *)malloc(cbBuf);
328 } while (!pabBuf && cbBuf > 4096);
329 if (!pabBuf)
330 return ENOMEM;
331 pabBufAligned = pabBuf;
332 }
333
334 if (cbFile < cbBuf * 4)
335 fProgress = 0;
336
337 MD5Init(&Ctx);
338 for (;;)
339 {
340 /* process a chunk. */
341 cb = read_file(pvFile, pabBufAligned, cbBuf);
342 if (cb > 0)
343 MD5Update(&Ctx, (unsigned char *)pabBufAligned, cb);
344 else if (!cb)
345 break;
346 else
347 {
348 rc = -cb;
349 break;
350 }
351 off += cb;
352
353 /* update the progress indicator. */
354 if (fProgress)
355 {
356 unsigned uNewPercent;
357 uNewPercent = (unsigned)(((double)off / cbFile) * 100);
358 if (uNewPercent != uPercent)
359 {
360 if (uPercent)
361 printf("\b\b\b\b");
362 printf("%3d%%", uNewPercent);
363 fflush(stdout);
364 uPercent = uNewPercent;
365 }
366 }
367 }
368 MD5Final(pDigest, &Ctx);
369
370 if (pcbFile)
371 *pcbFile = off;
372
373 if (fProgress)
374 printf("\b\b\b\b \b\b\b\b");
375
376 free(pabBuf);
377 return rc;
378}
379
380
381/**
382 * Checks the if the specified digest matches the digest of the file stream.
383 *
384 * @returns 0 on match, -1 on mismatch, errno value (positive) on failure.
385 * @param pvFile The file stream.
386 * @param Digest The MD5 digest.
387 * @param fProgress Whether to show an progress indicator on large files.
388 */
389static int check_md5sum(void *pvFile, unsigned char Digest[16], unsigned fProgress)
390{
391 unsigned char DigestFile[16];
392 int rc;
393
394 rc = calc_md5sum(pvFile, DigestFile, fProgress, NULL);
395 if (!rc)
396 rc = memcmp(Digest, DigestFile, 16) ? -1 : 0;
397 return rc;
398}
399
400
401/**
402 * Checks if the specified file matches the given MD5 digest.
403 *
404 * @returns 0 if it matches, 1 if it doesn't or an error occurs.
405 * @param pszFilename The name of the file to check.
406 * @param pszDigest The MD5 digest string.
407 * @param fText Whether to open the file in text or binary mode.
408 * @param fQuiet Whether to go about this in a quiet fashion or not.
409 * @param fProgress Whether to show an progress indicator on large files.
410 */
411static int check_one_file(const char *pszFilename, const char *pszDigest, unsigned fText, unsigned fQuiet, unsigned fProgress)
412{
413 unsigned char Digest[16];
414 int rc;
415
416 rc = string_to_digest(pszDigest, Digest);
417 if (!rc)
418 {
419 void *pvFile;
420
421 pvFile = open_file(pszFilename, fText);
422 if (pvFile)
423 {
424 if (!fQuiet)
425 fprintf(stdout, "%s: ", pszFilename);
426 rc = check_md5sum(pvFile, Digest, fProgress);
427 close_file(pvFile);
428 if (!fQuiet)
429 {
430 fprintf(stdout, "%s\n", !rc ? "OK" : rc < 0 ? "FAILURE" : "ERROR");
431 fflush(stdout);
432 if (rc > 0)
433 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc));
434 }
435 if (rc)
436 rc = 1;
437 }
438 else
439 {
440 if (!fQuiet)
441 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
442 rc = 1;
443 }
444 }
445 else
446 {
447 errx(1, "Malformed MD5 digest '%s'!", pszDigest);
448 errx(1, " %*s^", rc - 1, "");
449 rc = 1;
450 }
451
452 return rc;
453}
454
455
456/**
457 * Checks the specified md5.lst file.
458 *
459 * @returns 0 if all checks out file, 1 if one or more fails or there are read errors.
460 * @param pszFilename The name of the file.
461 * @param fText The default mode, text or binary. Only used when fBinaryTextOpt is true.
462 * @param fBinaryTextOpt Whether a -b or -t option was specified and should be used.
463 * @param fQuiet Whether to be quiet.
464 * @param fProgress Whether to show an progress indicator on large files.
465 */
466static int check_files(const char *pszFilename, int fText, int fBinaryTextOpt, int fQuiet, unsigned fProgress)
467{
468 int rc = 0;
469 FILE *pFile;
470
471 /*
472 * Try open the md5.lst file and process it line by line.
473 */
474 pFile = fopen(pszFilename, "r");
475 if (pFile)
476 {
477 int iLine = 0;
478 char szLine[8192];
479 while (fgets(szLine, sizeof(szLine), pFile))
480 {
481 const char *pszDigest;
482 int fLineText;
483 char *psz;
484 int rc2;
485
486 iLine++;
487 psz = szLine;
488
489 /* leading blanks */
490 while (*psz == ' ' || *psz == '\t' || *psz == '\n')
491 psz++;
492
493 /* skip blank or comment lines. */
494 if (!*psz || *psz == '#' || *psz == ';' || *psz == '/')
495 continue;
496
497 /* remove the trailing newline. */
498 rc2 = (int)strlen(psz);
499 if (psz[rc2 - 1] == '\n')
500 psz[rc2 - (rc2 >= 2 && psz[rc2 - 2] == '\r' ? 2 : 1)] = '\0';
501
502 /* skip to the end of the digest and terminate it. */
503 pszDigest = psz;
504 while (*psz != ' ' && *psz != '\t' && *psz)
505 psz++;
506 if (*psz)
507 {
508 *psz++ = '\0';
509
510 /* blanks */
511 while (*psz == ' ' || *psz == '\t' || *psz == '\n')
512 psz++;
513
514 /* check for binary asterix */
515 if (*psz != '*')
516 fLineText = fBinaryTextOpt ? fText : 0;
517 else
518 {
519 fLineText = 0;
520 psz++;
521 }
522 if (*psz)
523 {
524 unsigned char Digest[16];
525
526 /* the rest is filename. */
527 pszFilename = psz;
528
529 /*
530 * Do the job.
531 */
532 rc2 = string_to_digest(pszDigest, Digest);
533 if (!rc2)
534 {
535 void *pvFile = open_file(pszFilename, fLineText);
536 if (pvFile)
537 {
538 if (!fQuiet)
539 fprintf(stdout, "%s: ", pszFilename);
540 rc2 = check_md5sum(pvFile, Digest, fProgress);
541 close_file(pvFile);
542 if (!fQuiet)
543 {
544 fprintf(stdout, "%s\n", !rc2 ? "OK" : rc2 < 0 ? "FAILURE" : "ERROR");
545 fflush(stdout);
546 if (rc2 > 0)
547 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc2));
548 }
549 if (rc2)
550 rc = 1;
551 }
552 else
553 {
554 if (!fQuiet)
555 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
556 rc = 1;
557 }
558 }
559 else if (!fQuiet)
560 {
561 errx(1, "%s (%d): Ignoring malformed digest '%s' (digest)", pszFilename, iLine, pszDigest);
562 errx(1, "%s (%d): %*s^", pszFilename, iLine, rc2 - 1, "");
563 }
564 }
565 else if (!fQuiet)
566 errx(1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
567 }
568 else if (!fQuiet)
569 errx(1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
570 } /* while more lines */
571
572 fclose(pFile);
573 }
574 else
575 {
576 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
577 rc = 1;
578 }
579
580 return rc;
581}
582
583
584/**
585 * Calculates the MD5 sum for one file and prints it.
586 *
587 * @returns 0 on success, 1 on any kind of failure.
588 * @param pszFilename The file to process.
589 * @param fText The mode to open the file in.
590 * @param fQuiet Whether to be quiet or verbose about errors.
591 * @param fManifest Whether to format the output like a fetch manifest.
592 * @param fProgress Whether to show an progress indicator on large files.
593 * @param pOutput Where to write the list. Progress is always written to stdout.
594 */
595static int md5sum_file(const char *pszFilename, unsigned fText, unsigned fQuiet, unsigned fProgress,
596 unsigned fManifest, FILE *pOutput)
597{
598 int rc;
599 void *pvFile;
600
601 /*
602 * Calculate and print the MD5 sum for one file.
603 */
604 pvFile = open_file(pszFilename, fText);
605 if (pvFile)
606 {
607 unsigned char Digest[16];
608 KU64 cbFile = 0;
609
610 if (fProgress && pOutput)
611 fprintf(stdout, "%s: ", pszFilename);
612
613 rc = calc_md5sum(pvFile, Digest, fProgress, &cbFile);
614 close_file(pvFile);
615
616 if (fProgress && pOutput)
617 {
618 size_t cch = strlen(pszFilename) + 2;
619 while (cch-- > 0)
620 fputc('\b', stdout);
621 }
622
623 if (!rc)
624 {
625 char szDigest[36];
626 digest_to_string(Digest, szDigest);
627 if (!fManifest)
628 {
629 if (pOutput)
630 fprintf(pOutput, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
631 fprintf(stdout, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
632 }
633 else
634 {
635 if (pOutput)
636 fprintf(pOutput, "%s_SIZE := %" KU64_PRI "\n%s_MD5 := %s\n", pszFilename, cbFile, pszFilename, szDigest);
637 fprintf(stdout, "%s_SIZE := %" KU64_PRI "\n%s_MD5 := %s\n", pszFilename, cbFile, pszFilename, szDigest);
638 }
639 if (pOutput)
640 fflush(pOutput);
641 fflush(stdout);
642 }
643 else
644 {
645 if (!fQuiet)
646 errx(1, "Failed to open '%s': %s", pszFilename, strerror(rc));
647 rc = 1;
648 }
649 }
650 else
651 {
652 if (!fQuiet)
653 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno));
654 rc = 1;
655 }
656 return rc;
657}
658
659
660
661/**
662 * md5sum, calculates and checks the md5sum of files.
663 * Somewhat similar to the GNU coreutil md5sum command.
664 */
665int kmk_builtin_md5sum(int argc, char **argv, char **envp)
666{
667 int i;
668 int rc = 0;
669 int fText = 0;
670 int fBinaryTextOpt = 0;
671 int fQuiet = 0;
672 int fChecking = 0;
673 int fManifest = 0;
674 int fProgress = 0;
675 int fNoMoreOptions = 0;
676 const char *pszOutput = NULL;
677 FILE *pOutput = NULL;
678
679 g_progname = argv[0];
680
681 /*
682 * Print usage if no arguments.
683 */
684 if (argc <= 1)
685 return usage(stderr);
686
687 /*
688 * Process the arguments, FIFO style.
689 */
690 i = 1;
691 while (i < argc)
692 {
693 char *psz = argv[i];
694 if (!fNoMoreOptions && psz[0] == '-' && psz[1] == '-' && !psz[2])
695 fNoMoreOptions = 1;
696 else if (*psz == '-' && !fNoMoreOptions)
697 {
698 psz++;
699
700 /* convert long options for gnu just for fun */
701 if (*psz == '-')
702 {
703 if (!strcmp(psz, "-binary"))
704 psz = "b";
705 else if (!strcmp(psz, "-text"))
706 psz = "t";
707 else if (!strcmp(psz, "-check"))
708 psz = "c";
709 else if (!strcmp(psz, "-check-file"))
710 psz = "C";
711 else if (!strcmp(psz, "-manifest"))
712 psz = "m";
713 else if (!strcmp(psz, "-output"))
714 psz = "o";
715 else if (!strcmp(psz, "-progress"))
716 psz = "p";
717 else if (!strcmp(psz, "-status"))
718 psz = "q";
719 else if (!strcmp(psz, "-warn"))
720 psz = "w";
721 else if (!strcmp(psz, "-help"))
722 psz = "h";
723 else if (!strcmp(psz, "-version"))
724 psz = "v";
725 }
726
727 /* short options */
728 do
729 {
730 switch (*psz)
731 {
732 case 'c':
733 fChecking = 1;
734 break;
735
736 case 'b':
737 fText = 0;
738 fBinaryTextOpt = 1;
739 break;
740
741 case 't':
742 fText = 1;
743 fBinaryTextOpt = 1;
744 break;
745
746 case 'm':
747 fManifest = 1;
748 break;
749
750 case 'p':
751 fProgress = 1;
752 break;
753
754 case 'q':
755 fQuiet = 1;
756 break;
757
758 case 'w':
759 /* ignored */
760 break;
761
762 case 'h':
763 usage(stdout);
764 return 0;
765
766 case 'v':
767 return kbuild_version(argv[0]);
768
769 /*
770 * -C md5 file
771 */
772 case 'C':
773 {
774 const char *pszFilename;
775 const char *pszDigest;
776
777 if (psz[1])
778 pszDigest = &psz[1];
779 else if (i + 1 < argc)
780 pszDigest = argv[++i];
781 else
782 {
783 errx(1, "'-C' is missing the MD5 sum!");
784 return 1;
785 }
786 if (i + 1 < argc)
787 pszFilename = argv[++i];
788 else
789 {
790 errx(1, "'-C' is missing the filename!");
791 return 1;
792 }
793
794 rc |= check_one_file(pszFilename, pszDigest, fText, fQuiet, fProgress && !fQuiet);
795 psz = "\0";
796 break;
797 }
798
799 /*
800 * Output file.
801 */
802 case 'o':
803 {
804 if (fChecking)
805 {
806 errx(1, "'-o' cannot be used with -c or -C!");
807 return 1;
808 }
809
810 if (psz[1])
811 pszOutput = &psz[1];
812 else if (i + 1 < argc)
813 pszOutput = argv[++i];
814 else
815 {
816 errx(1, "'-o' is missing the file name!");
817 return 1;
818 }
819
820 psz = "\0";
821 break;
822 }
823
824 default:
825 errx(1, "Invalid option '%c'! (%s)", *psz, argv[i]);
826 return usage(stderr);
827 }
828 } while (*++psz);
829 }
830 else if (fChecking)
831 rc |= check_files(argv[i], fText, fBinaryTextOpt, fQuiet, fProgress && !fQuiet);
832 else
833 {
834 /* lazily open the output if specified. */
835 if (pszOutput)
836 {
837 if (pOutput)
838 fclose(pOutput);
839 pOutput = fopen(pszOutput, "w");
840 if (!pOutput)
841 {
842 rc = err(1, "fopen(\"%s\", \"w\") failed", pszOutput);
843 break;
844 }
845 pszOutput = NULL;
846 }
847
848 rc |= md5sum_file(argv[i], fText, fQuiet, fProgress && !fQuiet && !fManifest, fManifest, pOutput);
849 }
850 i++;
851 }
852
853 if (pOutput)
854 fclose(pOutput);
855 return rc;
856}
857
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