VirtualBox

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

Last change on this file since 3387 was 3219, checked in by bird, 6 years ago

kmkbuiltin: Added KMK_OPEN_NO_INHERIT to all open calls.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.9 KB
Line 
1/* $Id: md5sum.c 3219 2018-03-30 22:30:15Z bird $ */
2/** @file
3 * md5sum.
4 */
5
6/*
7 * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
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(PKMKBUILTINCTX pCtx, int fIsErr)
52{
53 kmk_builtin_ctx_printf(pCtx, fIsErr,
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 | KMK_OPEN_NO_INHERIT;
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 pCtx The command execution context.
408 * @param pszFilename The name of the file to check.
409 * @param pszDigest The MD5 digest string.
410 * @param fText Whether to open the file in text or binary mode.
411 * @param fQuiet Whether to go about this in a quiet fashion or not.
412 * @param fProgress Whether to show an progress indicator on large files.
413 */
414static int check_one_file(PKMKBUILTINCTX pCtx, const char *pszFilename, const char *pszDigest, unsigned fText,
415 unsigned fQuiet, unsigned fProgress)
416{
417 unsigned char Digest[16];
418 int rc;
419
420 rc = string_to_digest(pszDigest, Digest);
421 if (!rc)
422 {
423 void *pvFile;
424
425 pvFile = open_file(pszFilename, fText);
426 if (pvFile)
427 {
428 if (!fQuiet)
429 kmk_builtin_ctx_printf(pCtx, 0, "%s: ", pszFilename);
430 rc = check_md5sum(pvFile, Digest, fProgress);
431 close_file(pvFile);
432 if (!fQuiet)
433 {
434 kmk_builtin_ctx_printf(pCtx, 0, "%s\n", !rc ? "OK" : rc < 0 ? "FAILURE" : "ERROR");
435 if (rc > 0)
436 errx(pCtx, 1, "Error reading '%s': %s", pszFilename, strerror(rc));
437 }
438 if (rc)
439 rc = 1;
440 }
441 else
442 {
443 if (!fQuiet)
444 errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(errno));
445 rc = 1;
446 }
447 }
448 else
449 {
450 errx(pCtx, 1, "Malformed MD5 digest '%s'!", pszDigest);
451 errx(pCtx, 1, " %*s^", rc - 1, "");
452 rc = 1;
453 }
454
455 return rc;
456}
457
458
459/**
460 * Checks the specified md5.lst file.
461 *
462 * @returns 0 if all checks out file, 1 if one or more fails or there are read errors.
463 * @param pCtx The command execution context.
464 * @param pszFilename The name of the file.
465 * @param fText The default mode, text or binary. Only used when fBinaryTextOpt is true.
466 * @param fBinaryTextOpt Whether a -b or -t option was specified and should be used.
467 * @param fQuiet Whether to be quiet.
468 * @param fProgress Whether to show an progress indicator on large files.
469 */
470static int check_files(PKMKBUILTINCTX pCtx, const char *pszFilename, int fText, int fBinaryTextOpt,
471 int fQuiet, unsigned fProgress)
472{
473 int rc = 0;
474 FILE *pFile;
475
476 /*
477 * Try open the md5.lst file and process it line by line.
478 */
479 pFile = fopen(pszFilename, "r" KMK_FOPEN_NO_INHERIT_MODE);
480 if (pFile)
481 {
482 int iLine = 0;
483 char szLine[8192];
484 while (fgets(szLine, sizeof(szLine), pFile))
485 {
486 const char *pszDigest;
487 int fLineText;
488 char *psz;
489 int rc2;
490
491 iLine++;
492 psz = szLine;
493
494 /* leading blanks */
495 while (*psz == ' ' || *psz == '\t' || *psz == '\n')
496 psz++;
497
498 /* skip blank or comment lines. */
499 if (!*psz || *psz == '#' || *psz == ';' || *psz == '/')
500 continue;
501
502 /* remove the trailing newline. */
503 rc2 = (int)strlen(psz);
504 if (psz[rc2 - 1] == '\n')
505 psz[rc2 - (rc2 >= 2 && psz[rc2 - 2] == '\r' ? 2 : 1)] = '\0';
506
507 /* skip to the end of the digest and terminate it. */
508 pszDigest = psz;
509 while (*psz != ' ' && *psz != '\t' && *psz)
510 psz++;
511 if (*psz)
512 {
513 *psz++ = '\0';
514
515 /* blanks */
516 while (*psz == ' ' || *psz == '\t' || *psz == '\n')
517 psz++;
518
519 /* check for binary asterix */
520 if (*psz != '*')
521 fLineText = fBinaryTextOpt ? fText : 0;
522 else
523 {
524 fLineText = 0;
525 psz++;
526 }
527 if (*psz)
528 {
529 unsigned char Digest[16];
530
531 /* the rest is filename. */
532 pszFilename = psz;
533
534 /*
535 * Do the job.
536 */
537 rc2 = string_to_digest(pszDigest, Digest);
538 if (!rc2)
539 {
540 void *pvFile = open_file(pszFilename, fLineText);
541 if (pvFile)
542 {
543 if (!fQuiet)
544 kmk_builtin_ctx_printf(pCtx, 0, "%s: ", pszFilename);
545 rc2 = check_md5sum(pvFile, Digest, fProgress);
546 close_file(pvFile);
547 if (!fQuiet)
548 {
549 kmk_builtin_ctx_printf(pCtx, 0, "%s\n", !rc2 ? "OK" : rc2 < 0 ? "FAILURE" : "ERROR");
550 if (rc2 > 0)
551 errx(pCtx, 1, "Error reading '%s': %s", pszFilename, strerror(rc2));
552 }
553 if (rc2)
554 rc = 1;
555 }
556 else
557 {
558 if (!fQuiet)
559 errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(errno));
560 rc = 1;
561 }
562 }
563 else if (!fQuiet)
564 {
565 errx(pCtx, 1, "%s (%d): Ignoring malformed digest '%s' (digest)", pszFilename, iLine, pszDigest);
566 errx(pCtx, 1, "%s (%d): %*s^", pszFilename, iLine, rc2 - 1, "");
567 }
568 }
569 else if (!fQuiet)
570 errx(pCtx, 1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
571 }
572 else if (!fQuiet)
573 errx(pCtx, 1, "%s (%d): Ignoring malformed line!", pszFilename, iLine);
574 } /* while more lines */
575
576 fclose(pFile);
577 }
578 else
579 {
580 errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(errno));
581 rc = 1;
582 }
583
584 return rc;
585}
586
587
588/**
589 * Calculates the MD5 sum for one file and prints it.
590 *
591 * @returns 0 on success, 1 on any kind of failure.
592 * @param pCtx Command context.
593 * @param pszFilename The file to process.
594 * @param fText The mode to open the file in.
595 * @param fQuiet Whether to be quiet or verbose about errors.
596 * @param fManifest Whether to format the output like a fetch manifest.
597 * @param fProgress Whether to show an progress indicator on large files.
598 * @param pOutput Where to write the list. Progress is always written to stdout.
599 */
600static int md5sum_file(PKMKBUILTINCTX pCtx, const char *pszFilename, unsigned fText, unsigned fQuiet, unsigned fProgress,
601 unsigned fManifest, FILE *pOutput)
602{
603 int rc;
604 void *pvFile;
605
606 /*
607 * Calculate and print the MD5 sum for one file.
608 */
609 pvFile = open_file(pszFilename, fText);
610 if (pvFile)
611 {
612 unsigned char Digest[16];
613 KU64 cbFile = 0;
614
615 if (fProgress && pOutput)
616 fprintf(stdout, "%s: ", pszFilename);
617
618 rc = calc_md5sum(pvFile, Digest, fProgress, &cbFile);
619 close_file(pvFile);
620
621 if (fProgress && pOutput)
622 {
623 size_t cch = strlen(pszFilename) + 2;
624 while (cch-- > 0)
625 fputc('\b', stdout);
626 }
627
628 if (!rc)
629 {
630 char szDigest[36];
631 digest_to_string(Digest, szDigest);
632 if (!fManifest)
633 {
634 if (pOutput)
635 fprintf(pOutput, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
636 kmk_builtin_ctx_printf(pCtx, 0, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename);
637 }
638 else
639 {
640 if (pOutput)
641 fprintf(pOutput, "%s_SIZE := %" KU64_PRI "\n%s_MD5 := %s\n", pszFilename, cbFile, pszFilename, szDigest);
642 kmk_builtin_ctx_printf(pCtx, 0, "%s_SIZE := %" KU64_PRI "\n%s_MD5 := %s\n",
643 pszFilename, cbFile, pszFilename, szDigest);
644 }
645 if (pOutput)
646 fflush(pOutput);
647 }
648 else
649 {
650 if (!fQuiet)
651 errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(rc));
652 rc = 1;
653 }
654 }
655 else
656 {
657 if (!fQuiet)
658 errx(pCtx, 1, "Failed to open '%s': %s", pszFilename, strerror(errno));
659 rc = 1;
660 }
661 return rc;
662}
663
664
665
666/**
667 * md5sum, calculates and checks the md5sum of files.
668 * Somewhat similar to the GNU coreutil md5sum command.
669 */
670int kmk_builtin_md5sum(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
671{
672 int i;
673 int rc = 0;
674 int fText = 0;
675 int fBinaryTextOpt = 0;
676 int fQuiet = 0;
677 int fChecking = 0;
678 int fManifest = 0;
679 int fProgress = 0;
680 int fNoMoreOptions = 0;
681 const char *pszOutput = NULL;
682 FILE *pOutput = NULL;
683
684 /*
685 * Print usage if no arguments.
686 */
687 if (argc <= 1)
688 return usage(pCtx, 1);
689
690 /*
691 * Process the arguments, FIFO style.
692 */
693 i = 1;
694 while (i < argc)
695 {
696 char *psz = argv[i];
697 if (!fNoMoreOptions && psz[0] == '-' && psz[1] == '-' && !psz[2])
698 fNoMoreOptions = 1;
699 else if (*psz == '-' && !fNoMoreOptions)
700 {
701 psz++;
702
703 /* convert long options for gnu just for fun */
704 if (*psz == '-')
705 {
706 if (!strcmp(psz, "-binary"))
707 psz = "b";
708 else if (!strcmp(psz, "-text"))
709 psz = "t";
710 else if (!strcmp(psz, "-check"))
711 psz = "c";
712 else if (!strcmp(psz, "-check-file"))
713 psz = "C";
714 else if (!strcmp(psz, "-manifest"))
715 psz = "m";
716 else if (!strcmp(psz, "-output"))
717 psz = "o";
718 else if (!strcmp(psz, "-progress"))
719 psz = "p";
720 else if (!strcmp(psz, "-status"))
721 psz = "q";
722 else if (!strcmp(psz, "-warn"))
723 psz = "w";
724 else if (!strcmp(psz, "-help"))
725 psz = "h";
726 else if (!strcmp(psz, "-version"))
727 psz = "v";
728 }
729
730 /* short options */
731 do
732 {
733 switch (*psz)
734 {
735 case 'c':
736 fChecking = 1;
737 break;
738
739 case 'b':
740 fText = 0;
741 fBinaryTextOpt = 1;
742 break;
743
744 case 't':
745 fText = 1;
746 fBinaryTextOpt = 1;
747 break;
748
749 case 'm':
750 fManifest = 1;
751 break;
752
753 case 'p':
754 fProgress = 1 && isatty(fileno(stdout))
755#ifndef KMK_BUILTIN_STANDALONE
756 && (!pCtx->pOut || !pCtx->pOut->syncout)
757#endif
758 ;
759 break;
760
761 case 'q':
762 fQuiet = 1;
763 break;
764
765 case 'w':
766 /* ignored */
767 break;
768
769 case 'h':
770 usage(pCtx, 0);
771 return 0;
772
773 case 'v':
774 return kbuild_version(argv[0]);
775
776 /*
777 * -C md5 file
778 */
779 case 'C':
780 {
781 const char *pszFilename;
782 const char *pszDigest;
783
784 if (psz[1])
785 pszDigest = &psz[1];
786 else if (i + 1 < argc)
787 pszDigest = argv[++i];
788 else
789 {
790 errx(pCtx, 1, "'-C' is missing the MD5 sum!");
791 return 1;
792 }
793 if (i + 1 < argc)
794 pszFilename = argv[++i];
795 else
796 {
797 errx(pCtx, 1, "'-C' is missing the filename!");
798 return 1;
799 }
800
801 rc |= check_one_file(pCtx, pszFilename, pszDigest, fText, fQuiet, fProgress && !fQuiet);
802 psz = "\0";
803 break;
804 }
805
806 /*
807 * Output file.
808 */
809 case 'o':
810 {
811 if (fChecking)
812 {
813 errx(pCtx, 1, "'-o' cannot be used with -c or -C!");
814 return 1;
815 }
816
817 if (psz[1])
818 pszOutput = &psz[1];
819 else if (i + 1 < argc)
820 pszOutput = argv[++i];
821 else
822 {
823 errx(pCtx, 1, "'-o' is missing the file name!");
824 return 1;
825 }
826
827 psz = "\0";
828 break;
829 }
830
831 default:
832 errx(pCtx, 1, "Invalid option '%c'! (%s)", *psz, argv[i]);
833 return usage(pCtx, 1);
834 }
835 } while (*++psz);
836 }
837 else if (fChecking)
838 rc |= check_files(pCtx, argv[i], fText, fBinaryTextOpt, fQuiet, fProgress && !fQuiet);
839 else
840 {
841 /* lazily open the output if specified. */
842 if (pszOutput)
843 {
844 if (pOutput)
845 fclose(pOutput);
846 pOutput = fopen(pszOutput, "w" KMK_FOPEN_NO_INHERIT_MODE);
847 if (!pOutput)
848 {
849 rc = err(pCtx, 1, "fopen(\"%s\", \"w" KMK_FOPEN_NO_INHERIT_MODE "\") failed", pszOutput);
850 break;
851 }
852 pszOutput = NULL;
853 }
854
855 rc |= md5sum_file(pCtx, argv[i], fText, fQuiet, fProgress && !fQuiet && !fManifest, fManifest, pOutput);
856 }
857 i++;
858 }
859
860 if (pOutput)
861 fclose(pOutput);
862 return rc;
863}
864
865
866#ifdef KMK_BUILTIN_STANDALONE
867int main(int argc, char **argv, char **envp)
868{
869 KMKBUILTINCTX Ctx = { "kmk_md5sum", NULL };
870 return kmk_builtin_md5sum(argc, argv, envp, &Ctx);
871}
872#endif
873
874
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use