Changeset 1102 for trunk/src/kmk
- Timestamp:
- Sep 23, 2007, 5:30:49 AM (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/kmkbuiltin/md5sum.c
r1101 r1102 28 28 #include <stdio.h> 29 29 #include <errno.h> 30 #include <fcntl.h> 31 #ifdef _MSC_VER 32 # include <io.h> 33 #else 34 # include <unistd.h> 35 #endif 36 #include <sys/stat.h> 30 37 #include "err.h" 31 38 #include "kmkbuiltin.h" 32 39 #include "../../lib/md5.h" 33 40 41 //#define MD5SUM_USE_STDIO 34 42 35 43 … … 37 45 * Prints the usage and return 1. 38 46 */ 39 static int usage(void) 40 { 41 fprintf(stderr, 42 "usage: md5sum [-bt] file [string ...]\n" 43 " or: md5sum [-cbtwq] file\n"); 47 static int usage(FILE *pOut) 48 { 49 fprintf(pOut, 50 "usage: md5sum [-bt] file(s)\n" 51 " or: md5sum [-btwq] -c list-file(s)\n" 52 " or: md5sum [-btq] -C MD5 file\n" 53 "\n" 54 " -c, --check Check MD5 and files found in the specified list file(s).\n" 55 " The default is to compute MD5 sums of the specified files\n" 56 " and print them to stdout in list form.\n" 57 " -C, --check-file This is followed by an MD5 sum and the file to check.\n" 58 " -b, --binary Read files in binary mode. (default)\n" 59 " -t, --text Read files in text mode.\n" 60 " -q, --status Be quiet.\n" 61 " -w, --warn Ignored. Always warn, unless quiet.\n" 62 " -h, --help This usage info.\n" 63 " -v, --version Show version information and exit.\n" 64 ); 44 65 return 1; 45 66 } … … 135 156 136 157 /** 137 * Calculates the md5sum of the sepecified file stream.138 *139 * @returns errno on failure, 0 on success.140 * @param pFile The file stream.141 * @param pDigest Where to store the MD5 digest.142 */143 static int calc_md5sum(FILE *pFile, unsigned char pDigest[16])144 {145 int rc = 0;146 int cb;147 char abBuf[16384];148 struct MD5Context Ctx;149 150 MD5Init(&Ctx);151 for (;;)152 {153 errno = 0;154 cb = (int)fread(abBuf, 1, sizeof(abBuf), pFile);155 if (cb > 0)156 MD5Update(&Ctx, abBuf, cb);157 else if (cb == 0)158 break;159 else160 {161 rc = errno;162 if (!rc)163 rc = EINVAL;164 break;165 }166 }167 MD5Final(pDigest, &Ctx);168 169 return rc;170 }171 172 173 /**174 * Checks the if the specified digest matches the digest of the file stream.175 *176 * @returns 0 on match, -1 on mismatch, errno value (positive) on failure.177 * @param pFile The file stream.178 * @param Digest The MD5 digest.179 */180 static int check_md5sum(FILE *pFile, unsigned char Digest[16])181 {182 unsigned char DigestFile[16];183 int rc;184 185 rc = calc_md5sum(pFile, DigestFile);186 if (!rc)187 rc = memcmp(Digest, DigestFile, 16) ? -1 : 0;188 return rc;189 }190 191 192 /**193 158 * Opens the specified file for md5 sum calculation. 194 159 * 195 * @returns File streamon success, NULL and errno on failure.160 * @returns Opaque pointer on success, NULL and errno on failure. 196 161 * @param pszFilename The filename. 197 162 * @param fText Whether text or binary mode should be used. 198 163 */ 199 static FILE *open_file(const char *pszFilename, unsigned fText) 200 { 164 static void *open_file(const char *pszFilename, unsigned fText) 165 { 166 #if defined(MD5SUM_USE_STDIO) 201 167 FILE *pFile; 202 168 … … 206 172 pFile = fopen(pszFilename, "r"); 207 173 return pFile; 208 } 174 175 #else 176 int fd; 177 int fFlags; 178 179 /* figure out the appropriate flags. */ 180 fFlags = O_RDONLY; 181 #ifdef O_SEQUENTIAL 182 fFlags |= _O_SEQUENTIAL; 183 #elif defined(_O_SEQUENTIAL) 184 fFlags |= _O_SEQUENTIAL; 185 #endif 186 #ifdef _O_BINARY 187 if (!fText) fFlags |= _O_BINARY; 188 #elif defined(_O_BINARY 189 if (!fText) fFlags |= _O_BINARY; 190 #endif 191 #ifdef O_TEXT 192 if (fText) fFlags |= O_TEXT; 193 #elif defined(O_TEXT) 194 if (fText) fFlags |= _O_TEXT; 195 #endif 196 197 errno = 0; 198 fd = open(pszFilename, fFlags, 0755); 199 if (fd >= 0) 200 { 201 int *pFd = malloc(sizeof(*pFd)); 202 if (pFd) 203 { 204 *pFd = fd; 205 return pFd; 206 } 207 close(fd); 208 errno = ENOMEM; 209 } 210 211 return NULL; 212 #endif 213 } 214 215 216 /** 217 * Closes a file opened by open_file. 218 * 219 * @param pvFile The opaque pointer returned by open_file. 220 */ 221 static void close_file(void *pvFile) 222 { 223 #if defined(MD5SUM_USE_STDIO) 224 fclose((FILE *)pvFile); 225 #else 226 close(*(int *)pvFile); 227 free(pvFile); 228 #endif 229 } 230 231 232 /** 233 * Reads from a file opened by open_file. 234 * 235 * @returns Number of bytes read on success. 236 * 0 on EOF. 237 * Negated errno on read error. 238 * @param pvFile The opaque pointer returned by open_file. 239 * @param pvBuf Where to put the number of read bytes. 240 * @param cbBuf The max number of bytes to read. 241 * Must be less than a INT_MAX. 242 */ 243 static int read_file(void *pvFile, void *pvBuf, size_t cbBuf) 244 { 245 #if defined(MD5SUM_USE_STDIO) 246 int cb; 247 248 errno = 0; 249 cb = (int)fread(pvBuf, 1, cbBuf, (FILE *)pvFile); 250 if (cb >= 0) 251 return (int)cb; 252 if (!errno) 253 return -EINVAL; 254 return -errno; 255 #else 256 int cb; 257 258 errno = 0; 259 cb = (int)read(*(int *)pvFile, pvBuf, (int)cbBuf); 260 if (cb >= 0) 261 return (int)cb; 262 if (!errno) 263 return -EINVAL; 264 return -errno; 265 #endif 266 } 267 268 269 /** 270 * Gets the size of the file. 271 * This is informational and not necessarily 100% accurate. 272 * 273 * @returns File size. 274 * @param pvFile The opaque pointer returned by open_file 275 */ 276 static double size_file(void *pvFile) 277 { 278 #if defined(_MSC_VER) 279 __int64 cb; 280 # if defined(MD5SUM_USE_STDIO) 281 cb = _filelengthi64(fileno((FILE *)pvFile)); 282 # else 283 cb = _filelengthi64(*(int *)pvFile); 284 # endif 285 if (cb >= 0) 286 return (double)cb; 287 288 #elif defined(MD5SUM_USE_STDIO) 289 struct stat st; 290 if (!fstat(fileno((FILE *)pvFile), &st)) 291 return st.st_size; 292 293 #else 294 struct stat st; 295 if (!fstat(*(int *)pvFile, &st)) 296 return st.st_size; 297 #endif 298 return 1024; 299 } 300 301 302 /** 303 * Calculates the md5sum of the sepecified file stream. 304 * 305 * @returns errno on failure, 0 on success. 306 * @param pvFile The file stream. 307 * @param pDigest Where to store the MD5 digest. 308 * @param fProgress Whether to show a progress bar. 309 */ 310 static int calc_md5sum(void *pvFile, unsigned char pDigest[16], unsigned fProgress) 311 { 312 int cb; 313 int rc = 0; 314 char abBuf[32*1024]; 315 struct MD5Context Ctx; 316 unsigned uPercent = 0; 317 double cbFile = 0.0; 318 double off = 0.0; 319 320 if (fProgress) 321 { 322 cbFile = size_file(pvFile); 323 if (cbFile < 1024*1024) 324 fProgress = 0; 325 } 326 327 MD5Init(&Ctx); 328 for (;;) 329 { 330 /* process a chunk. */ 331 cb = read_file(pvFile, abBuf, sizeof(abBuf)); 332 if (cb > 0) 333 MD5Update(&Ctx, abBuf, cb); 334 else if (!cb) 335 break; 336 else 337 { 338 rc = -cb; 339 break; 340 } 341 342 /* update the progress indicator. */ 343 if (fProgress) 344 { 345 unsigned uNewPercent; 346 off += cb; 347 uNewPercent = (unsigned)((off / cbFile) * 100); 348 if (uNewPercent != uPercent) 349 { 350 if (uPercent) 351 printf("\b\b\b\b"); 352 printf("%3d%%", uNewPercent); 353 fflush(stdout); 354 uPercent = uNewPercent; 355 } 356 } 357 } 358 MD5Final(pDigest, &Ctx); 359 360 if (fProgress) 361 printf("\b\b\b\b \b\b\b\b"); 362 363 return rc; 364 } 365 366 367 /** 368 * Checks the if the specified digest matches the digest of the file stream. 369 * 370 * @returns 0 on match, -1 on mismatch, errno value (positive) on failure. 371 * @param pvFile The file stream. 372 * @param Digest The MD5 digest. 373 * @param fProgress Whether to show an progress indicator on large files. 374 */ 375 static int check_md5sum(void *pvFile, unsigned char Digest[16], unsigned fProgress) 376 { 377 unsigned char DigestFile[16]; 378 int rc; 379 380 rc = calc_md5sum(pvFile, DigestFile, fProgress); 381 if (!rc) 382 rc = memcmp(Digest, DigestFile, 16) ? -1 : 0; 383 return rc; 384 } 385 386 387 /** 388 * Checks if the specified file matches the given MD5 digest. 389 * 390 * @returns 0 if it matches, 1 if it doesn't or an error occurs. 391 * @param pszFilename The name of the file to check. 392 * @param pszDigest The MD5 digest string. 393 * @param fText Whether to open the file in text or binary mode. 394 * @param fQuiet Whether to go about this in a quiet fashion or not. 395 * @param fProgress Whether to show an progress indicator on large files. 396 */ 397 static int check_one_file(const char *pszFilename, const char *pszDigest, unsigned fText, unsigned fQuiet, unsigned fProgress) 398 { 399 unsigned char Digest[16]; 400 int rc; 401 402 rc = string_to_digest(pszDigest, Digest); 403 if (!rc) 404 { 405 void *pvFile; 406 407 pvFile = open_file(pszFilename, fText); 408 if (pvFile) 409 { 410 if (!fQuiet) 411 fprintf(stdout, "%s: ", pszFilename); 412 rc = check_md5sum(pvFile, Digest, fProgress); 413 close_file(pvFile); 414 if (!fQuiet) 415 { 416 fprintf(stdout, "%s\n", !rc ? "OK" : rc < 0 ? "FAILURE" : "ERROR"); 417 fflush(stdout); 418 if (rc > 0) 419 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc)); 420 } 421 if (rc) 422 rc = 1; 423 } 424 else 425 { 426 if (!fQuiet) 427 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno)); 428 rc = 1; 429 } 430 } 431 else 432 { 433 errx(1, "Malformed MD5 digest '%s'!", pszDigest); 434 errx(1, " %*s^", rc - 1, ""); 435 rc = 1; 436 } 437 438 return rc; 439 } 440 441 442 /** 443 * Checks the specified md5.lst file. 444 * 445 * @returns 0 if all checks out file, 1 if one or more fails or there are read errors. 446 * @param pszFilename The name of the file. 447 * @param fText The default mode, text or binary. Only used when fBinaryTextOpt is true. 448 * @param fBinaryTextOpt Whether a -b or -t option was specified and should be used. 449 * @param fQuiet Whether to be quiet. 450 * @param fProgress Whether to show an progress indicator on large files. 451 */ 452 static int check_files(const char *pszFilename, int fText, int fBinaryTextOpt, int fQuiet, unsigned fProgress) 453 { 454 int rc = 0; 455 FILE *pFile; 456 457 /* 458 * Try open the md5.lst file and process it line by line. 459 */ 460 pFile = fopen(pszFilename, "r"); 461 if (pFile) 462 { 463 int iLine = 0; 464 char szLine[8192]; 465 while (fgets(szLine, sizeof(szLine), pFile)) 466 { 467 const char *pszDigest; 468 int fLineText; 469 char *psz; 470 int rc2; 471 472 iLine++; 473 psz = szLine; 474 475 /* leading blanks */ 476 while (*psz == ' ' || *psz == '\t' || *psz == '\n') 477 psz++; 478 479 /* skip blank or comment lines. */ 480 if (!*psz || *psz == '#' || *psz == ';' || *psz == '/') 481 continue; 482 483 /* remove the trailing newline. */ 484 rc2 = (int)strlen(psz); 485 if (psz[rc2 - 1] == '\n') 486 psz[rc2 - 1] = '\0'; 487 488 /* skip to the end of the digest and terminate it. */ 489 pszDigest = psz; 490 while (*psz != ' ' && *psz != '\t' && *psz) 491 psz++; 492 if (*psz) 493 { 494 *psz++ = '\0'; 495 496 /* blanks */ 497 while (*psz == ' ' || *psz == '\t' || *psz == '\n') 498 psz++; 499 500 /* check for binary asterix */ 501 if (*psz != '*') 502 fLineText = fBinaryTextOpt ? fText : 0; 503 else 504 { 505 fLineText = 0; 506 psz++; 507 } 508 if (*psz) 509 { 510 unsigned char Digest[16]; 511 512 /* the rest is filename. */ 513 pszFilename = psz; 514 515 /* 516 * Do the job. 517 */ 518 rc2 = string_to_digest(pszDigest, Digest); 519 if (!rc2) 520 { 521 void *pvFile = open_file(pszFilename, fLineText); 522 if (pvFile) 523 { 524 if (!fQuiet) 525 fprintf(stdout, "%s: ", pszFilename); 526 rc2 = check_md5sum(pvFile, Digest, fProgress); 527 close_file(pvFile); 528 if (!fQuiet) 529 { 530 fprintf(stdout, "%s\n", !rc2 ? "OK" : rc2 < 0 ? "FAILURE" : "ERROR"); 531 fflush(stdout); 532 if (rc2 > 0) 533 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc2)); 534 } 535 if (rc2) 536 rc = 1; 537 } 538 else 539 { 540 if (!fQuiet) 541 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno)); 542 rc = 1; 543 } 544 } 545 else if (!fQuiet) 546 { 547 errx(1, "%s (%d): Ignoring malformed digest '%s' (digest)", pszFilename, iLine, pszDigest); 548 errx(1, "%s (%d): %*s^", pszFilename, iLine, rc2 - 1, ""); 549 } 550 } 551 else if (!fQuiet) 552 errx(1, "%s (%d): Ignoring malformed line!", pszFilename, iLine); 553 } 554 else if (!fQuiet) 555 errx(1, "%s (%d): Ignoring malformed line!", pszFilename, iLine); 556 } /* while more lines */ 557 558 fclose(pFile); 559 } 560 else 561 { 562 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno)); 563 rc = 1; 564 } 565 566 return rc; 567 } 568 569 570 /** 571 * Calculates the MD5 sum for one file and prints it. 572 * 573 * @returns 0 on success, 1 on any kind of failure. 574 * @param pszFilename The file to process. 575 * @param fText The mode to open the file in. 576 * @param fQuiet Whether to be quiet or verbose about errors. 577 * @param fProgress Whether to show an progress indicator on large files. 578 */ 579 static int md5sum_file(const char *pszFilename, unsigned fText, unsigned fQuiet, unsigned fProgress) 580 { 581 int rc; 582 void *pvFile; 583 584 /* 585 * Calcuate and print the MD5 sum for one file. 586 */ 587 pvFile = open_file(pszFilename, fText); 588 if (pvFile) 589 { 590 unsigned char Digest[16]; 591 rc = calc_md5sum(pvFile, Digest, fProgress); 592 close_file(pvFile); 593 if (!rc) 594 { 595 char szDigest[36]; 596 digest_to_string(Digest, szDigest); 597 fprintf(stdout, "%s %s%s\n", szDigest, fText ? "" : "*", pszFilename); 598 fflush(stdout); 599 } 600 else 601 { 602 if (!fQuiet) 603 errx(1, "Failed to open '%s': %s", pszFilename, strerror(rc)); 604 rc = 1; 605 } 606 } 607 else 608 { 609 if (!fQuiet) 610 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno)); 611 rc = 1; 612 } 613 return rc; 614 } 615 209 616 210 617 … … 222 629 int fNewLine = 0; 223 630 int fChecking = 0; 631 int fProgress = 0; 224 632 int fNoMoreOptions = 0; 225 unsigned char Digest[16];226 const char *pszFilename;227 const char *pszDigest;228 char szDigest[36];229 FILE *pFile;230 int rc2;231 633 232 634 g_progname = argv[0]; … … 236 638 */ 237 639 if (argc <= 1) 238 return usage( );640 return usage(stderr); 239 641 240 642 /* … … 250 652 { 251 653 psz++; 654 252 655 /* convert long options for gnu just for fun */ 253 656 if (*psz == '-') … … 259 662 else if (!strcmp(psz, "-check")) 260 663 psz = "c"; 261 else if (!strcmp(psz, "-check- this"))664 else if (!strcmp(psz, "-check-file")) 262 665 psz = "C"; 666 else if (!strcmp(psz, "-progress")) 667 psz = "p"; 263 668 else if (!strcmp(psz, "-status")) 264 669 psz = "q"; … … 290 695 break; 291 696 697 case 'p': 698 fProgress = 1; 699 break; 700 292 701 case 'q': 293 702 fQuiet = 1; … … 297 706 /* ignored */ 298 707 break; 708 709 case 'h': 710 usage(stdout); 711 return 0; 299 712 300 713 case 'v': … … 306 719 case 'C': 307 720 { 721 const char *pszFilename; 722 const char *pszDigest; 723 308 724 if (psz[1]) 309 725 pszDigest = &psz[1]; … … 315 731 return 1; 316 732 } 317 rc2 = string_to_digest(pszDigest, Digest);318 if (rc2)319 {320 errx(1, "Malformed MD5 digest '%s'!", pszDigest);321 errx(1, " %*s^", rc2 - 1, "");322 return 1;323 }324 325 733 if (i + 1 < argc) 326 734 pszFilename = argv[++i]; … … 330 738 return 1; 331 739 } 332 pFile = open_file(pszFilename, fText); 333 if (pFile) 334 { 335 rc2 = check_md5sum(pFile, Digest); 336 if (!fQuiet) 337 { 338 if (rc2 <= 0) 339 { 340 fprintf(stdout, "%s: %s\n", pszFilename, !rc2 ? "OK" : "FAILURE"); 341 fflush(stdout); 342 } 343 else 344 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc)); 345 } 346 if (rc2) 347 rc = 1; 348 fclose(pFile); 349 } 350 else 351 { 352 if (!fQuiet) 353 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno)); 354 rc = 1; 355 } 740 741 rc |= check_one_file(pszFilename, pszDigest, fText, fQuiet, fProgress && !fQuiet); 356 742 psz = "\0"; 357 743 break; … … 360 746 default: 361 747 errx(1, "Invalid option '%c'! (%s)", *psz, argv[i]); 362 return usage( );748 return usage(stderr); 363 749 } 364 750 } while (*++psz); 365 751 } 366 752 else if (fChecking) 367 { 368 pFile = fopen(argv[i], "r"); 369 if (pFile) 370 { 371 int iLine = 0; 372 char szLine[8192]; 373 while (fgets(szLine, sizeof(szLine), pFile)) 374 { 375 int fLineText; 376 char *psz = szLine; 377 iLine++; 378 379 /* leading blanks */ 380 while (*psz == ' ' || *psz == '\t' || *psz == '\n') 381 psz++; 382 383 /* skip blank or comment lines. */ 384 if (!*psz || *psz == '#' || *psz == ';' || *psz == '/') 385 continue; 386 387 /* remove the trailing newline. */ 388 rc2 = (int)strlen(psz); 389 if (psz[rc2 - 1] == '\n') 390 psz[rc2 - 1] = '\0'; 391 392 /* skip to the end of the digest and terminate it. */ 393 pszDigest = psz; 394 while (*psz != ' ' && *psz != '\t' && *psz) 395 psz++; 396 if (*psz) 397 { 398 *psz++ = '\0'; 399 400 /* blanks */ 401 while (*psz == ' ' || *psz == '\t' || *psz == '\n') 402 psz++; 403 404 /* check for binary asterix */ 405 if (*psz != '*') 406 fLineText = fBinaryTextOpt ? fText : 0; 407 else 408 { 409 fLineText = 0; 410 psz++; 411 } 412 if (*psz) 413 { 414 /* the rest is filename. */ 415 pszFilename = psz; 416 417 /* 418 * Do the job. 419 */ 420 rc2 = string_to_digest(pszDigest, Digest); 421 if (!rc2) 422 { 423 FILE *pFile2 = open_file(pszFilename, fLineText); 424 if (pFile2) 425 { 426 rc2 = check_md5sum(pFile2, Digest); 427 if (!fQuiet) 428 { 429 if (rc2 <= 0) 430 { 431 fprintf(stdout, "%s: %s\n", pszFilename, !rc2 ? "OK" : "FAILURE"); 432 fflush(stdout); 433 } 434 else 435 errx(1, "Error reading '%s': %s", pszFilename, strerror(rc)); 436 } 437 if (rc2) 438 rc = 1; 439 fclose(pFile2); 440 } 441 else 442 { 443 if (!fQuiet) 444 errx(1, "Failed to open '%s': %s", pszFilename, strerror(errno)); 445 rc = 1; 446 } 447 } 448 else if (!fQuiet) 449 { 450 errx(1, "%s (%d): Ignoring malformed digest '%s' (digest)", argv[i], iLine, pszDigest); 451 errx(1, "%s (%d): %*s^", argv[i], iLine, rc2 - 1, ""); 452 } 453 } 454 else if (!fQuiet) 455 errx(1, "%s (%d): Ignoring malformed line!", argv[i], iLine); 456 } 457 else if (!fQuiet) 458 errx(1, "%s (%d): Ignoring malformed line!", argv[i], iLine); 459 } 460 461 fclose(pFile); 462 } 463 else 464 { 465 errx(1, "Failed to open '%s': %s", argv[i], strerror(errno)); 466 rc = 1; 467 } 468 } 753 rc |= check_files(argv[i], fText, fBinaryTextOpt, fQuiet, fProgress && !fQuiet); 469 754 else 470 { 471 /* 472 * Calcuate and print the MD5 sum for one file. 473 */ 474 pFile = open_file(argv[i], fText); 475 if (pFile) 476 { 477 rc2 = calc_md5sum(pFile, Digest); 478 if (!rc2) 479 { 480 digest_to_string(Digest, szDigest); 481 fprintf(stdout, "%s %s%s\n", szDigest, fText ? "" : "*", argv[i]); 482 fflush(stdout); 483 } 484 else 485 { 486 if (!fQuiet) 487 errx(1, "Failed to open '%s': %s", argv[i], strerror(rc)); 488 rc = 1; 489 } 490 fclose(pFile); 491 } 492 else 493 { 494 if (!fQuiet) 495 errx(1, "Failed to open '%s': %s", argv[i], strerror(errno)); 496 rc = 1; 497 } 498 } 755 rc |= md5sum_file(argv[i], fText, fQuiet, fProgress && !fQuiet); 499 756 i++; 500 757 }
Note:
See TracChangeset
for help on using the changeset viewer.