/* Copyright (c) 2009-2010, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above opyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Dirk Krause nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file fchksum.c The fchksum program. */ /* -c --check Compare checksums against list -q --quiet No output for succeeded tests when checking. -s --status No output when checking, set exit code only -h --help Show help text -v --version Show version information -n --no-header Do not write header line. -l --list The specified file names (or standard input) are lists containing file names of files to inspect. -o --md5sum Check input files was generated by md5sum. -m dig --message-digest=dig Choose message digest type (MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, RIPEMD-160). -e enc --encoding=enc Choose message digest encoding (HEX, ASCII-85, R-ASCII-85). Output format: Encoded message digest. File name. Exit codes: 0 success 1 checksum mismatch 2 Syntax or option error 3 Attempt to handle a directory 4 Illegal file type 5 Stat failed (file does not exist/no permissions?) 6 Failed to read input file 7 Failed to create checksum 8 Unsupported message digest type */ #include "fchksum.h" #include "dktools-version.h" #line 102 "fchksum.ctr" /** Status code returned by the main function. */ static int exval = 0; /** Flag: Quiet, show failed tests only. */ static unsigned char flag_quiet = 0x00; /** Flag: Set exit code only. */ static unsigned char flag_status = 0x00; /** Flag: check file list. */ static unsigned char flag_check = 0x00; /** Flag: print help text. */ static unsigned char flag_help = 0x00; /** Flag: Show version information. */ static unsigned char flag_version = 0x00; /** Flag: Skip header line. */ static unsigned char flag_skip_header = 0x00; /** Flag: Check list was generated by md5sum. */ static unsigned char flag_md5sum = 0x00; /** Flag: Build checksums for directories too. */ static unsigned char flag_dir = 0x00; /** Flag: This is the first file to handle. */ static unsigned char flag_is_first = 0X01; /** Flag: Header was already printed. */ static unsigned char flag_header_printed = 0x00; /** Flag: file names are lists of files to build digests for. */ static unsigned char flag_list = 0x00; /** Flag: message digest type is implemented here. */ static unsigned char flag_implemented = 0x01; /** Digest type. */ static int digest_type = CHKSUM_DIGEST_UNKNOWN; /** Digest encoding. */ static int digest_encoding = CHKSUM_ENCODING_UNKNOWN; /** Program name. */ static char progname[] = { "fchksum" }; /** Input buffer. */ static char buffer[4096]; /** Used to read lines from check list. */ static char inputline[1024]; /** Binary digest value. */ static unsigned char digest_value[256]; /** ASCII encoded digest value. */ static unsigned char digest_ascii[512]; /** Number of files checked. */ static unsigned long files_checked = 0UL; /** Number of file checks failed. */ static unsigned long files_failed = 0UL; /** Number of files skipped due to non-implemented MD types. */ static unsigned long files_skipped = 0UL; #if ON_A_WINDOWS_SYSTEM #if DK_HAVE__STAT64 /** Bit size for version number. */ #define VERSION_SIZE_BITS "64" #else /** Bit size for version number. */ #define VERSION_SIZE_BITS "32" #endif #else #if DK_HAVE_STAT64 && DK_HAVE_LARGEFILE64_SOURCE /** Bit size for version number. */ #define VERSION_SIZE_BITS "64" #else /** Bit size for version number. */ #define VERSION_SIZE_BITS "32" #endif #endif /** Text to show version number and license conditions. */ static char *version_text[] = { "fchksum - Checksum calculation program, version " VERSNUMB ", " VERSION_SIZE_BITS "-bit", "Copyright (C) 2008-2010 - Dipl.-Ing. Dirk Krause", "http://dktools.sourceforge.net/fchksum.html", "", "Redistribution and use in source and binary forms, with or without", "modification, are permitted provided that the following conditions are met:", "* Redistributions of source code must retain the above copyright notice, this", " list of conditions and the following disclaimer.", "* Redistributions in binary form must reproduce the above copyright notice,", " this list of conditions and the following disclaimer in the documentation", " and/or other materials provided with the distribution.", "* Neither the name of the Dirk Krause nor the names of other contributors may", " be used to endorse or promote products derived from this software without", " specific prior written permission.", "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''", "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE", "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE", "ARE DISCLAIMED.", "IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY", "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES", "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;", "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND", "ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS", "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.", "", "This program uses the OpenSSL library.", "This product includes cryptographic software written by", "Eric Young (eay@cryptsoft.com).", "", NULL }; /** Help text. */ static char *help_text[] = { "", "fchksum -h", "\t\tprints a help text.", "", "fchksum -v", "\t\tprints version information.", "", "fchksum [-m |--message-digest=] [-e |--encoding=] [-n] [-l] []", "\t\tprints messages digests for the specified files.", "\t\tdig: digest type, one of", "\t\t MD5", "\t\t RIPEMD-160", "\t\t SHA-1 (default)", #if DK_HAVE_SHA224 "\t\t SHA-224", #else "\t\t SHA-224 (not implemented here)", #endif #if DK_HAVE_SHA256 "\t\t SHA-256", #else "\t\t SHA-256 (not implemented here)", #endif #if DK_HAVE_SHA384 "\t\t SHA-384", #else "\t\t SHA-384 (not implemented here)", #endif #if DK_HAVE_SHA512 "\t\t SHA-512", #else "\t\t SHA-512 (not implemented here)", #endif "\t\tenc: encoding, one of", "\t\t HEX, ASCII-85 (default), REVERSE-ASCII-85", "\t\t-n suppresses header line output.", "\t\t A85 and RA85 can be used as abbreviations for ASCII-85 and", "\t\t REVERSE-ASCII-85", "\t\t-l The files specified on the command line contain lists of files", "\t\t to check.", "", "fchksum -c|--check [-q|--quiet|-s|--status] [-o|--md5sum] []", "\t\tchecks messages digests for files in the list.", "\t\t-c check files against a check list", "\t\t-q quiet, do not write report lines for succeeded checks", "\t\t-s set exit status code only, do not write any report lines", "\t\t-o handle check list produced by the md5sum program", "\t\t (default is to handle check lists produced by fchksum itself)", "", NULL }; /** Digest type keywords. */ static char *digest_types[] = { "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512", "MD5", "RIPEMD-160", /* +++++ Further message digest algorithms here */ NULL }; /** Encoding keywords. */ static char *digest_encodings[] = { "HEX", "ASCII-85", "REVERSE-ASCII-85", "A85", "RA85", "R-ASCII-85", /* +++++ Further binary-to-ASCII encodings here */ NULL }; /** Long options keywords. */ static char *long_options[] = { /* 0 */ "help", /* 1 */ "version", /* 2 */ "check", /* 3 */ "quiet", /* 4 */ "status", /* 5 */ "no-header", /* 6 */ "md5sum", /* 7 */ "message-digest", /* 8 */ "encoding", /* 9 */ "directories", /* 10 */ "list", NULL }; /** Control instructions. */ static char *control_instructions[] = { /* 0 */ "DIGEST/ENCODING", NULL }; /** Strings printed as messages. */ static char *msg[] = { /* 0 */ "%s: ERROR - Unknown digest type ``%s''!\n", /* 1 */ "%s: ERROR - long option ``--%s'' needs an argument!\n", /* 2 */ "%s: ERROR - Unknown encoding ``%s''!\n", /* 3 */ "%s: ERROR - Unknown long option ``--%s''!\n", /* 4 */ "%s: ERROR - Long option ``--%s'' too long!\n", /* 5 */ "%s: ERROR - Unknown option ``-%c''!\n", /* 6 */ "%s:%s: ERROR - Can not handle directories!\n", /* 7 */ "%s:%s: Warning - Creating checksum for a device!\n", /* 8 */ "%s:%s: ERROR - Illegal file type!\n", /* 9 */ "%s:%s: ERROR - Failed to obtain information about file!\n", /* 10 */ "%s:%s: ERROR - Failed to open file for reading!\n", /* 11 */ "%s:%s:%lu: Syntax error!\n", /* 12 */ "%s:%s:%lu: Syntax error - Not a key=value pair!\n", /* 13 */ "%s:%s:%lu: Syntax error - Value text is missing!\n", /* 14 */ "%s:%s:%lu: Syntax error - Unknown instruction \"%s\"!\n", /* 15 */ "%s:%s:%lu: Syntax error - Missing encoding!\n", /* 16 */ "%s: OK\n", /* 17 */ "%s: FAILED\n", /* 18 */ "%s: WARNING %lu of %lu computed checksums did NOT match!\n", /* 19 */ "%s:%s: Pipe skipped.\n", /* 20 */ "%s:%s: ERROR - Failed to create checksum!\n", /* 21 */ "%s:%s:%lu ERROR - Message digest type \"%s\" not implemented!\n", /* 22 */ "%s: ERROR - Message digest type \"%s\" not implemented!\n", /* 23 */ "%s: ERROR - %lu files skipped (message digest not implemented)!\n", NULL }; /** File name to print when checksumming standard input. */ static char filename_for_stdin[] = { "-" }; /** Factors to add ASCII85-digits to byte quadrupel. */ static unsigned long f2[] = { 1UL, 85UL, (85UL * 85UL), (85UL * 85UL * 85UL), (85UL * 85UL * 85UL * 85UL) }; /** Characters to represent hexadecimal values. */ static char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '\0' }; /** Default set of whitespaces. */ static char default_whitespace_set[] = { " \t\r\n" }; /** Fallback routine for systems without strchr(). @param str The string to search. @param c The character to search for. @return Pointer to character on success, NULL on error. */ char * dkstr_chr(char *str, int c) { char *back = NULL; /**< Return value. */ if(str) { #if DK_HAVE_STRCHR back = strchr(str,c); #else char *ptr; ptr = str; while((*ptr) && (!back)) { if(((int)(*ptr)) == c) { back = ptr; } else { ptr++; } } #endif } return back; } /** Find start of string. @param str The text to find a string in. @param whsp Set of whitespaces. @return Pointer to first text character on success, NULL on error. */ char * dkstr_start(char *str, char *whsp) { char *back = NULL; /**< Return value. */ char *ptr; /**< Used to traverse string. */ char *wh; /**< Whitespace set. */ if(str) { wh = (whsp ? whsp : default_whitespace_set); ptr = str; while((*ptr) && (!back)) { if(dkstr_chr(wh,((int)(*ptr)))) { ptr++; } else { back = ptr; } } } return back; } /** Find start of next string. @param str The text to find a string in. @param whsp Set of whitespaces. @return Pointer to next text start on success, NULL on error. */ char * dkstr_next(char *str, char *whsp) { char *back = NULL; /**< Return value. */ char *ptr; /**< Used to traverse string. */ char *wh; /**< Whitespace set. */ int state; /**< Current state: 0=no text yet, 1=text, 2=done. */ if(str) { ptr = str; wh = (whsp ? whsp : default_whitespace_set); state = 0; while((state < 2) && (*ptr)) { if(dkstr_chr(wh,((int)(*ptr)))) { if(state == 1) { state = 2; *(ptr++) = '\0'; back = dkstr_start(ptr,wh); } ptr++; } else { state = 1; ptr++; } } } return back; } /** Remove trailing whitespaces. @param str The string to modify. @param whsp Set of whitespaces. */ void dkstr_chomp(char *str, char *whsp) { char *wh; /**< Whitespace set. */ char *ptr; /**< Used to traverse text. */ char *x; /**< First whitespace after last text. */ if(str) { wh = (whsp ? whsp : default_whitespace_set); x = NULL; ptr = str; while(*ptr) { if(dkstr_chr(wh,((int)(*ptr)))) { if(!x) { x = ptr; } } else { x = NULL; } ptr++; } if(x) { *x = '\0'; } } } #if !DK_HAVE_STRICMP #if !DK_HAVE_STRCASECMP /** Fallback function if neither strcasecmp() nor stricmp() is available. @param a Left text in comparison. @param b Right text in comparison. @return Text comparison result -1, 0 or 1 for ab. */ static int portable_stricmp(char *a, char *b) { int back = 0; /**< Return value. */ int ende; /**< Flag: finished. */ char aval; /**< Current left side character. */ char bval; /**< Current right side character. */ char *aptr; /**< Current left side pointer. */ char *bptr; /**< Current right side pointer. */ if(a && b) { aptr = a; bptr = b; ende = 0; while((ende == 0) && (back == 0)) { aval = *aptr; bval = *bptr; if(aval && bval) { if(isascii(aval) && islower(aval)) aval = toupper(aval); if(isascii(bval) && islower(bval)) bval = toupper(bval); if(aval != bval) { ende = 1; if(aval > bval) back = 1; else back = -1; } else { aptr++; bptr++; } } else { ende = 1; if(aval) { back = 1; } if(bval) { back = -1; } } } } return back; } #endif #endif /** Find string in array of strings, return index. @param array Array of pointers to strings. @param str String to search for in \a array. @param cs Flag: case-sensitive. @return Index of \a str in \a array, or -1 on error (not found). */ int dkstr_array_index(char **array, char *str, int cs) { int back = -1; /**< Return value. */ char **ptr; /**< Current array element to test. */ int i; /**< Index of current element. */ if(array && str) { i = 0; ptr = array; while((*ptr) && (back == -1)) { if(cs) { if(strcmp(*ptr,str) == 0) { back = i; } } else { if(STRICMP(*ptr,str) == 0) { back = i; } } if(back == -1) { ptr++; i++; } } } return back; } /** Multiplication of unsigned long values. @param u1 One factor. @param u2 Second factor. @param ok Pointer to variable for error code (if any). @return The product of \a u1 and \a u2. */ static unsigned long dkma_mul_ulong_ok(unsigned long u1, unsigned long u2, int * ok) { unsigned long back; /**< Return value. */ if(u1 > (DK_MAX_ULONG / u2)) { if(ok) { *ok = DK_ERR_MATH_OOR; } } back = u1 * u2; return back; } /** Addition of unsigned long values. @param u1 First value. @param u2 Second value. @param ok Pointer to variable for error code (if any). @return The summary of \a u1 and \a u2. */ static unsigned long dkma_add_ulong_ok(unsigned long u1, unsigned long u2, int *ok) { unsigned long back; /**< Return value. */ if((DK_MAX_ULONG - u1) < u2) { if(ok) { *ok = DK_ERR_MATH_OOR; } } back = u1 + u2; return back; } /** Unsigned long value to retrieve LSB from unsigned long. */ static unsigned long last_byte = 0x000000FFUL; /** Find size of buffer needed to ASCII-85 encode data. @param s The size of the binary data. @return Size of buffer needed for ASCII-85 encoded string. */ size_t dkenc_size_bin_to_a85(size_t s) { size_t back = 0; /**< Return value. */ int ec = 0; /**< Error code from math operations. */ unsigned long ul1; /**< \a s converted to unsigned long. */ unsigned long ul2; /**< u1 / 4. */ unsigned long ul3; /**< u1 % 4. */ ul1 = (unsigned long)s; ul2 = ul1 % 4UL; ul3 = ul1 / 4UL; if(ul2) ul2++; /* for last incomplete block */ ul1 = dkma_add_ulong_ok( dkma_mul_ulong_ok(ul3, 5UL, &ec), ul2, &ec ); /* add 25 percent */ ul1++; /* final 0x00 byte for string */ back = (size_t)ul1; if(ec) back = 0; /* error checking */ if((unsigned long)back != ul1) back = 0; return back; } /** Do the conversion from binary data to reverse ASCII-85. @param dp Destination buffer. @param ds Size of \a dp in bytes. @param sp Source buffer (binary data). @param ss Size of \a sp in bytes. */ static void do_bin_to_ra85(char *dp, size_t ds, char *sp, size_t ss) { register char *mysp; /**< Pointer into source buffer. */ register unsigned char *mydp; /**< Pointer into destination buffer. */ register unsigned long v; /**< Temporary value. */ register size_t i; /**< Index of current byte processed. */ register short vused; /**< Number of bytes in \a v. */ mydp = (unsigned char *)dp; mysp = sp; v = 0UL; vused = 0; for(i = 0; i < ss; i++) { switch(vused++) { case 3: { v |= ((((unsigned long)((unsigned char)(*(mysp++)))) << 24) & 0xFF000000UL); } break; case 2: { v |= ((((unsigned long)((unsigned char)(*(mysp++)))) << 16) & 0x00FF0000UL); } break; case 1: { v |= ((((unsigned long)((unsigned char)(*(mysp++)))) << 8) & 0x0000FF00UL); } break; default: { v |= ((((unsigned long)((unsigned char)(*(mysp++)))) ) & last_byte); } break; } if(vused >= 4) { *(mydp++) = (unsigned char)((v % 85UL) + 33UL); v = v / 85UL; *(mydp++) = (unsigned char)((v % 85UL) + 33UL); v = v / 85UL; *(mydp++) = (unsigned char)((v % 85UL) + 33UL); v = v / 85UL; *(mydp++) = (unsigned char)((v % 85UL) + 33UL); v = v / 85UL; *(mydp++) = (unsigned char)((v % 85UL) + 33UL); vused = 0; v = 0UL; } } if(vused) { vused++; while(vused--) { *(mydp++) = (unsigned char)((v % 85UL) + 33UL); v = v / 85UL; } } *mydp = '\0'; } /** Create reverse ASCII-85 encoded string for binary data. @param dp Destination buffer. @param ds Size of \a dp in bytes. @param sp Source buffer (binary data). @param ss Size of \a sp in bytes. @return 1 on success, 0 on error (i.e. destination buffer too small). */ int dkenc_bin_to_ra85(char *dp, size_t ds, char *sp, size_t ss) { int back = 0; /**< Return value. */ size_t needed_size; /**< Minimum destination buffer size. */ if((dp) && (sp) && (ds) && (ss)) { needed_size = dkenc_size_bin_to_a85(ss); if(needed_size) { if(ds >= needed_size) { do_bin_to_ra85(dp, ds, sp, ss); back = 1; } } } return back; } /** Do the conversion from binary data to ASCII-85 encoded string. @param dp Destination buffer. @param ds Size of \a dp in bytes. @param sp Source buffer (binary data). @param ss Size of \a sp in bytes. */ static void do_bin_to_a85(char *dp, size_t ds, char *sp, size_t ss) { register unsigned char *mydp; /**< Pointer to destination buffer. */ register unsigned char *mysp; /**< Pointer to source buffer. */ register unsigned long v; /**< Temporary value. */ register short vused; /**< Number of bytes in v. */ register short addval; /**< Summand to add to index. */ register size_t i; mydp = (unsigned char *)dp; mysp = (unsigned char *)sp; v = 0UL; vused = 0; for(i = 0; i < ss; i++) { switch(vused) { case 3: { v |= ( ((unsigned long)(*(mysp++))) & 0x000000FFUL); } break; case 2: { v |= ((((unsigned long)(*(mysp++))) << 8) & 0x0000FF00UL); } break; case 1: { v |= ((((unsigned long)(*(mysp++))) << 16) & 0x00FF0000UL); } break; default: { v |= ((((unsigned long)(*(mysp++))) << 24) & 0xFF000000UL); } break; } if(++vused >= 4) { vused = 5; while(vused--) { *(mydp++) = (unsigned char)(33UL + v / f2[vused]); v = v % f2[vused]; } v = 0UL; vused = 0; } } if(vused) { vused++; addval = 5 - vused; while(vused--) { *(mydp++) = (unsigned char)(33UL + v / f2[vused + addval]); v = v % f2[vused + addval]; } } *mydp = '\0'; } /** Create ASCII-85 encoded string representing binary data. @param dp Destination buffer. @param ds Size of \a dp in bytes. @param sp Source buffer (binary data). @param ss Size of \a sp in bytes. @return 1 on success, 0 on error (i.e. destination buffer too small). */ int dkenc_bin_to_a85(char *dp, size_t ds, char *sp, size_t ss) { int back = 0; /**< Return value. */ size_t needed_size; /**< Minimum size of destination buffer. */ if((dp) && (sp) && (ds) && (ss)) { needed_size = dkenc_size_bin_to_a85(ss); if(needed_size) { if(ds >= needed_size) { do_bin_to_a85(dp, ds, sp, ss); back = 1; } } } return back; } /** Get size of buffer needed for a ASCII-Hex encoded string. @param s Size of the original (binary) data. @return Minimum buffer size needed for the ASCII-Hex encoded string. */ size_t dkenc_size_bin_to_hex(size_t s) { size_t back = 0; /**< Return value. */ int ec = 0; /**< Mathematical error code. */ unsigned long ul1; /**< s converted to unsigned long. */ ul1 = (unsigned long)s; ul1 = dkma_mul_ulong_ok(ul1, 2UL, &ec); ul1 = dkma_add_ulong_ok(ul1, 1UL, &ec); back = (size_t)ul1; if(ec) back = 0; if((unsigned long)back != ul1) back = 0; return back; } /** Get character representing the higher half-byte in a character. @param c Character to get half-byte from. @return Hexadecimal character. */ static char high_nibble_hex(char c) { char back; /**< Return value. */ back = hex_digits[ (((unsigned short)c) >> 4) & 0x000FU ]; return back; } /** Get character representing the lower half-byte in a character. @param c Character to get half-byte from. @return Hexadecimal character. */ static char low_nibble_hex(char c) { char back; /**< Return value. */ back = hex_digits[ ((unsigned short)c) & 0x000FU ]; return back; } /** Do the conversion from binary data to ASCII-Hex encoded string. @param dp Destination buffer. @param ds Size of \a dp in bytes. @param sp Source buffer (binary data). @param ss Size of \a sp in bytes. */ static void do_bin_to_hex(char *dp, size_t ds, char *sp, size_t ss) { register char *mydp; /**< Pointer into destination buffer. */ register char *mysp; /**< Pointer into source buffer. */ register size_t i; /**< Running index to process all bytes in sp. */ mydp = dp; mysp = sp; for(i = 0; i < ss; i++) { *(mydp++) = high_nibble_hex(*mysp); *(mydp++) = low_nibble_hex(*(mysp++)); } *mydp = '\0'; } /** Create ASCII-Hex encoded string for binary data. @param dp Destination buffer. @param ds Size of \a dp in bytes. @param sp Source buffer (binary data). @param ss Size of \a sp in bytes. @return 1 on success, 0 on error (i.e. destination buffer too small). */ int dkenc_bin_to_hex(char *dp, size_t ds, char *sp, size_t ss) { int back = 0; /**< Return value. */ size_t needed_bytes; /**< Minimum size of destination buffer. */ if((dp) && (ds) && (sp) && (ss)) { needed_bytes = dkenc_size_bin_to_hex(ss); if(needed_bytes) { if(ds >= needed_bytes) { do_bin_to_hex(dp, ds, sp, ss); back = 1; } } } return back; } /** Report error for failed checksumming. @param fn File name for which checksumming failed. */ static void err_checksum_failed(char *fn) { fprintf(stderr, msg[20], progname, fn); fflush(stderr); if(exval != 1) exval = 7; } /** Error message for a missing message digest implementation. @param fn Name of configuration file (may be NULL). @param lineno Line number in configuration file (may be 0UL). @param t Text containing the message digest name. */ static void err_not_implemented(char *fn, unsigned long lineno, char *t) { if((fn) && (lineno)) { fprintf(stderr, msg[21], progname, fn, lineno, t); } else { fprintf(stderr, msg[22], progname, t); } fflush(stderr); } /** Error message: Text is not a key/value pair. @param fn Source file name. @param lineno Line number. */ static void err_not_key_value(char *fn, unsigned long lineno) { fprintf(stderr, msg[12], progname, fn, lineno); fflush(stderr); if(exval != 1) exval = 2; } /** Error message: No value text. @param fn Source file name. @param lineno Line number. */ static void err_missing_value(char *fn, unsigned long lineno) { fprintf(stderr, msg[13], progname, fn, lineno); fflush(stderr); if(exval != 1) exval = 2; } /** Error message: Unknown instruction. @param fn Source file name. @param lineno Line number. @param t Text to print. */ static void err_unknown_instruction(char *fn, unsigned long lineno, char *t) { fprintf(stderr, msg[14], progname, fn, lineno, t); fflush(stderr); if(exval != 1) exval = 2; } /** Error message: The encoding description is missing. @param fn Source file name. @param lineno Line number. */ static void err_missing_encoding(char *fn, unsigned long lineno) { fprintf(stderr, msg[15], progname, fn, lineno); fflush(stderr); if(exval != 1) exval = 2; } /** Error message: Syntax error. @param fn Source file name. @param lineno Line number. */ static void err_syntax_general(char *fn, unsigned long lineno) { fprintf(stderr, msg[11], progname, fn, lineno); } /** Error message: Failed to open file for reading. @param s File name. */ static void err_open_read(char *s) { fprintf(stderr, msg[10], progname, s); fflush(stderr); if(exval != 1) exval = 6; } /** Error message: stat() failed. @param s File name. */ static void err_stat_failed(char *s) { fprintf(stderr, msg[9], progname, s); fflush(stderr); if(exval != 1) exval = 5; } /** Error message: File is of wrong type. @param s File name. */ static void err_illegal_file_type(char *s) { fprintf(stderr, msg[8], progname, s); fflush(stderr); if(exval != 1) exval = 4; } /** Warning: This path name is a device. @param s File name. */ static void warn_device(char *s) { fprintf(stderr, msg[7], progname, s); fflush(stderr); } #if ON_A_WINDOWS_SYSTEM /** Warning: Skipping pipe. @param s File name. */ static void warn_pipe(char *s) { fprintf(stderr, msg[19], progname, s); fflush(stderr); } #endif /** Error message: Directory. @param s File name. */ static void err_directory(char *s) { fprintf(stderr, msg[6], progname, s); fflush(stderr); if(exval != 1) exval = 3; } /** Error message: Illegal option. @param c The option to complain about. */ static void err_unknown_option(char c) { fprintf(stderr, msg[5], progname, c); fflush(stderr); if(exval != 1) exval = 2; flag_help = 0x01; } /** Error message: Long option too long for buffer. @param s The long option to complain about. */ static void err_option_too_long(char *s) { fprintf(stderr, msg[4], progname, s); fflush(stderr); if(exval != 1) exval = 2; flag_help = 0x01; } /** Error message: Unknown long option. @param s The option to complain about. */ static void err_unknown_long_option(char *s) { fprintf(stderr, msg[3], progname, s); fflush(stderr); if(exval != 1) exval = 2; flag_help = 0x01; } /** Error message: Unknown digest type. @param s The digest type. */ static void err_unknown_digest_type(char *s) { fprintf(stderr, msg[0], progname, s); fflush(stderr); if(exval != 1) exval = 2; flag_help = 0x01; } /** Error message: Unknown encoding. @param s The encoding. */ static void err_unknown_encoding(char *s) { fprintf(stderr, msg[2], progname, s); fflush(stderr); if(exval != 1) exval = 2; flag_help = 0x01; } /** Error message: Long option needs a value. @param s Option. */ static void err_long_opt_arg_missing(char *s) { fprintf(stderr, msg[1], progname, s); fflush(stderr); if(exval != 1) exval = 2; flag_help = 0x01; } /** Show text (NULL terminated array of string pointers). @param t Text to show. */ static void show_text(char **t) { char **ptr; /**< Pointer to traverse the array. */ ptr = t; while(*ptr) { fputs(*(ptr++), stdout); fputc('\n', stdout); } } /** Set digest type. @param t Text representation of digest. @param fn File name (used for error reporting, may be NULL). @param lineno Line number (used for error reporting, may be 0UL). @return 1 on success, 0 on error. */ static int set_digest_type(char *t, char *fn, unsigned long lineno) { int back = 0; /**< Return value. */ int res; /**< Index of t in array of digest types. */ res = dkstr_array_index(digest_types, t, 0); if(res > -1) { digest_type = res + 1; flag_implemented = 0x01; switch(digest_type) { case CHKSUM_DIGEST_SHA_224: { #if !DK_HAVE_SHA224 flag_implemented = 0x00; #endif } break; case CHKSUM_DIGEST_SHA_256: { #if !DK_HAVE_SHA256 flag_implemented = 0x00; #endif } break; case CHKSUM_DIGEST_SHA_384: { #if !DK_HAVE_SHA384 flag_implemented = 0x00; #endif } break; case CHKSUM_DIGEST_SHA_512: { #if !DK_HAVE_SHA512 flag_implemented = 0x00; #endif } break; } if(!flag_implemented) { err_not_implemented(fn, lineno, t); } back = 1; } else { if((fn) && (lineno)) { err_syntax_general(fn, lineno); } err_unknown_digest_type(t); } return back; } /** Set encoding. @param t Text representation of encoding. @param fn File name (used for error reporting, may be NULL). @param lineno Line number (used for error reporting, may be 0UL). @return 1 on success, 0 on error. */ static int set_digest_encoding(char *t, char *fn, unsigned long lineno) { int back = 0; /**< Return value. */ int res; /**< Index of t in encoding types array. */ res = dkstr_array_index(digest_encodings, t, 0); if(res > -1) { switch(res) { case 1: case 3: { digest_encoding = CHKSUM_ENCODING_ASCII85; } break; case 2: case 4: case 5: { digest_encoding = CHKSUM_ENCODING_RA85; } break; default: { digest_encoding = CHKSUM_ENCODING_HEX; } break; } } else { if((fn) && (lineno)) { err_syntax_general(fn, lineno); } err_unknown_encoding(t); } return back; } /** Process command line arguments. @param argc Number of command line arguments. @param argv Command line arguments array. */ static void process_arguments(int argc, char **argv) { char **lfdptr; /**< Traverse arguments. */ char *ptr; /**< Current argument. */ char *argptr; /**< Long option value (if any). */ int i; /**< Current argument number. */ int res; /**< Search result. */ lfdptr = argv; lfdptr++; i = 1; while(i < argc) { ptr = *lfdptr; if(*ptr == '-') { ptr++; switch(*ptr) { case '-': { ptr++; if(strlen(ptr) < sizeof(buffer)) { strcpy(buffer, ptr); argptr = dkstr_chr(buffer, '='); if(argptr) { *(argptr++) = '\0'; } res = dkstr_array_index(long_options, buffer, 1); if(res >= 0) { switch(res) { case 0: { flag_help = 0x01; } break; case 1: { flag_version = 0x01; } break; case 2: { flag_check = 0x01; } break; case 3: { flag_quiet = 0x01; } break; case 4: { flag_status = 0x01; } break; case 5: { flag_skip_header = 0x01; } break; case 6: { flag_md5sum = 0x01; } break; case 7: { if(argptr) { set_digest_type(argptr, NULL, 0UL); } else { err_long_opt_arg_missing(buffer); } } break; case 8: { if(argptr) { set_digest_encoding(argptr, NULL, 0UL); } else { err_long_opt_arg_missing(buffer); } } break; case 9: { flag_dir = 0x01; } break; case 10: { flag_list = 0x01; } break; } } else { err_unknown_long_option(buffer); } } else { err_option_too_long(ptr); } } break; case 'h': { flag_help = 0x01; } break; case 'v': { flag_version = 0x01; } break; case 'c': { flag_check = 0x01; } break; case 's': { flag_status = 0x01; } break; case 'q': { flag_quiet = 0x01; } break; case 'l': { flag_list = 0x01; } break; case 'm': { ptr++; if(!(*ptr)) { ptr = NULL; lfdptr++; i++; if(i < argc) { ptr = *lfdptr; } } if(ptr) { set_digest_type(ptr, NULL, 0UL); } } break; case 'e': { ptr++; if(!(*ptr)) { ptr = NULL; lfdptr++; i++; if(i < argc) { ptr = *lfdptr; } } if(ptr) { set_digest_encoding(ptr, NULL, 0UL); } } break; case 'n': { flag_skip_header = 0x01; } break; case 'o': { flag_md5sum = 0x01; } break; case 'd': { flag_dir = 0x01; } break; case '\0': break; default: { err_unknown_option(*ptr); } break; } } lfdptr++; i++; } } /** Process control line from check list. @param l The control line to process without tab,#,#. @param fn File name (used for error reporting, may be NULL). @param lineno Line number (used for error reporting, may be 0UL). */ static void use_control_line(char *l, char *fn, unsigned long lineno) { char *pinst; /**< Instruction. */ char *pval; /**< Value. */ char *p2; /**< second word in value. */ char *p3; /**< third word in value. */ int res; /**< Check result. */ pinst = pval = p2 = p3 = NULL; pinst = dkstr_start(l, NULL); if(pinst) { pval = dkstr_chr(pinst, '='); if(pval) { dkstr_chomp(pinst, NULL); *(pval++) = '\0'; pval = dkstr_start(pval, NULL); if(pval) { p2 = dkstr_next(pval, NULL); if(p2) { p3 = dkstr_next(p2, NULL); } if(pinst) dkstr_chomp(pinst, NULL); if(pval) dkstr_chomp(pval, NULL); if(p2) dkstr_chomp(p2, NULL); if(p3) dkstr_chomp(p3, NULL); res = dkstr_array_index(control_instructions, pinst, 0); switch(res) { case 0: { if(p2) { set_digest_type(pval, fn, lineno); set_digest_encoding(p2, fn, lineno); } else { err_missing_encoding(fn, lineno); } } break; default: { err_unknown_instruction(fn, lineno, pinst); } break; } } else { err_missing_value(fn, lineno); } } else { err_not_key_value(fn, lineno); } } } /** Create ASCII check sum for an opened file. @param f The file to process. @return 1 on success, 0 on error. */ static int create_ascii_checksum(FILE *f) { int back = 1; /**< Return value. */ size_t usedbytes = 0; /**< Number of bytes in binary md. */ SHA_CTX sha_ctx; /**< Message digest context. */ SHA256_CTX sha256_ctx; /**< Message digest context. */ SHA512_CTX sha512_ctx; /**< Message digest context. */ RIPEMD160_CTX ripemd160_ctx; /**< Message digest context. */ MD5_CTX md5_ctx; /**< Message digest context. */ size_t read_bytes; /**< Number of bytes read into buffer. */ digest_ascii[0] = '\0'; /* First calculate binary message digest. */ switch(digest_type) { case CHKSUM_DIGEST_SHA_512: { #if DK_HAVE_SHA512 SHA512_Init(&sha512_ctx); do { read_bytes = FREAD(buffer,1,sizeof(buffer),f); if(read_bytes > 0) { SHA512_Update(&sha512_ctx, buffer, read_bytes); } } while(read_bytes > 0); SHA512_Final(digest_value, &sha512_ctx); usedbytes = 64; #endif } break; case CHKSUM_DIGEST_SHA_384: { #if DK_HAVE_SHA384 SHA384_Init(&sha512_ctx); do { read_bytes = FREAD(buffer,1,sizeof(buffer),f); if(read_bytes > 0) { SHA384_Update(&sha512_ctx, buffer, read_bytes); } } while(read_bytes > 0); SHA384_Final(digest_value, &sha512_ctx); usedbytes = 48; #endif } break; case CHKSUM_DIGEST_SHA_256: { #if DK_HAVE_SHA256 SHA256_Init(&sha256_ctx); do { read_bytes = FREAD(buffer,1,sizeof(buffer),f); if(read_bytes > 0) { SHA256_Update(&sha256_ctx, buffer, read_bytes); } } while(read_bytes > 0); SHA256_Final(digest_value, &sha256_ctx); usedbytes = 32; #endif } break; case CHKSUM_DIGEST_SHA_224: { #if DK_HAVE_SHA224 SHA224_Init(&sha256_ctx); do { read_bytes = FREAD(buffer,1,sizeof(buffer),f); if(read_bytes > 0) { SHA224_Update(&sha256_ctx, buffer, read_bytes); } } while(read_bytes > 0); SHA224_Final(digest_value, &sha256_ctx); usedbytes = 28; #endif } break; case CHKSUM_DIGEST_MD5: { MD5_Init(&md5_ctx); do { read_bytes = FREAD(buffer,1,sizeof(buffer),f); if(read_bytes > 0) { MD5_Update(&md5_ctx, buffer, read_bytes); } } while(read_bytes > 0); MD5_Final(digest_value, &md5_ctx); usedbytes = 16; } break; case CHKSUM_DIGEST_RIPEMD_160: { RIPEMD160_Init(&ripemd160_ctx); do { read_bytes = FREAD(buffer,1,sizeof(buffer),f); if(read_bytes > 0) { RIPEMD160_Update(&ripemd160_ctx, buffer, read_bytes); } } while(read_bytes > 0); RIPEMD160_Final(digest_value, &ripemd160_ctx); usedbytes = 20; } break; /* +++++ Further message digest algorithms here */ default: { SHA1_Init(&sha_ctx); do { read_bytes = FREAD(buffer,1,sizeof(buffer),f); if(read_bytes > 0) { SHA1_Update(&sha_ctx, buffer, read_bytes); } } while(read_bytes > 0); SHA1_Final(digest_value, &sha_ctx); usedbytes = 20; } break; } /* Convert binary message digest to ASCII representation. */ if(usedbytes) { switch(digest_encoding) { case CHKSUM_ENCODING_HEX: { back = dkenc_bin_to_hex( (char *)digest_ascii, sizeof(digest_ascii), (char *)digest_value, usedbytes ); } break; case CHKSUM_ENCODING_RA85: { back = dkenc_bin_to_ra85( (char *)digest_ascii, sizeof(digest_ascii), (char *)digest_value, usedbytes ); } break; /* +++++ Further binary-to-ASCII encodings here */ default: { back = dkenc_bin_to_a85( (char *)digest_ascii, sizeof(digest_ascii), (char *)digest_value, usedbytes ); } break; } } return back; } /** Process a checksum list produced by md5sum. @param f File to process. @param fn File name (used for error reporting, may NOT be NULL). */ static void check_md5sum_file(FILE *f, char *fn) { int old_digest_type; /**< Save digest type. */ int old_digest_encoding; /**< Save encoding. */ int res; /**< Comparison result. */ unsigned long lineno; /**< Line number. */ char *pname = NULL; /**< Pointer to file name. */ char *pmd = NULL; /**< Pointer to message digest. */ FILE *fipo = NULL; /**< Used to read input. */ int use_bin_mode; /**< Flag: Use binary mode. */ old_digest_type = digest_type; old_digest_encoding = digest_encoding; digest_type = CHKSUM_DIGEST_MD5; digest_encoding = CHKSUM_ENCODING_HEX; lineno = 0UL; while(fgets(inputline, sizeof(inputline), f)) { lineno++; dkstr_chomp(inputline, NULL); pname = dkstr_chr(inputline, ' '); if(pname) { *(pname++) = '\0'; #if ON_A_WINDOWS_SYSTEM use_bin_mode = 1; #else use_bin_mode = 0; #endif switch(*pname) { case ' ': { use_bin_mode = 0; pname++; } break; case '*': { use_bin_mode = 1; pname++; } break; } pmd = dkstr_start(inputline, NULL); if(pmd) { dkstr_chomp(pmd, NULL); dkstr_chomp(pname, NULL); if(strcmp(pname, "-")) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(pname, (use_bin_mode ? "rb" : "r")); #else fipo = fopen(pname, (use_bin_mode ? "rb" : "r")); #endif if(fipo) { if(create_ascii_checksum(fipo)) { res = 0; files_checked++; if(files_checked == 0UL) files_checked = 0xFFFFFFFFUL; switch(digest_encoding) { case CHKSUM_ENCODING_HEX: { if(STRICMP((char *)digest_ascii,pmd) == 0) { res = 1; } } break; default: { if(strcmp((char *)digest_ascii,pmd) == 0) { res = 1; } } break; } if(res) { if(!((flag_quiet) || (flag_status))) { printf(msg[16], pname); } } else { if(!flag_status) { printf(msg[17], pname); } exval = 1; files_failed++; if(files_failed == 0UL) files_failed = 0xFFFFFFFFUL; } } else { err_checksum_failed(fn); } fclose(fipo); fipo = NULL; } else { err_open_read(pname); } } else { } } else { err_syntax_general(fn, lineno); } } else { err_syntax_general(fn, lineno); } } digest_encoding = old_digest_encoding; digest_type = old_digest_type; } /** Process a checksum list produced by fchksum. @param f File to process. @param fn File name (used for error reporting, may NOT be NULL). */ static void check_fchksum_file(FILE *f, char *fn) { int old_digest_type; /**< Save digest type. */ int old_digest_encoding; /**< Save encoding. */ int res; /**< Comparison result. */ unsigned long lineno; /**< Line number. */ char *pname = NULL; /**< Pointer to file name. */ char *pmd = NULL; /**< Pointer to message digest. */ FILE *fipo = NULL; /**< Used to read input. */ lineno = 0UL; old_digest_type = digest_type; old_digest_encoding = digest_encoding; while(fgets(inputline, sizeof(inputline), f)) { lineno++; dkstr_chomp(inputline, NULL); if(inputline[0] == '\t') { if(inputline[1] == '#') { if(inputline[2] == '#') { use_control_line(&(inputline[3]), fn, lineno); } else { err_syntax_general(fn, lineno); } } else { err_syntax_general(fn, lineno); } } else { pname = dkstr_chr(inputline, ' '); if(pname) { *(pname++) = '\0'; pmd = dkstr_start(inputline, NULL); if(pmd) { pname = dkstr_start(pname, NULL); if(pname) { if(strcmp(pname, "-")) { if(flag_implemented) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fipo = fopen64(pname, "rb"); #else fipo = fopen(pname, "rb"); #endif if(fipo) { if(create_ascii_checksum(fipo)) { res = 0; files_checked++; if(files_checked == 0UL) files_checked = 0xFFFFFFFFUL; switch(digest_encoding) { case CHKSUM_ENCODING_HEX: { if(STRICMP((char *)digest_ascii,pmd) == 0) { res = 1; } } break; default: { if(strcmp((char *)digest_ascii,pmd) == 0) { res = 1; } } break; } if(res) { if(!((flag_quiet) || (flag_status))) { printf(msg[16], pname); } } else { if(!flag_status) { printf(msg[17], pname); } exval = 1; files_failed++; if(files_failed == 0UL) files_failed = 0xFFFFFFFFUL; } } else { err_checksum_failed(fn); } fclose(fipo); fipo = NULL; } else { err_open_read(pname); } } else { files_skipped++; if(files_skipped == 0UL) files_skipped = 0xFFFFFFFFUL; } } else { } } else { err_syntax_general(fn, lineno); } } else { err_syntax_general(fn, lineno); } } else { err_syntax_general(fn, lineno); } } } digest_encoding = old_digest_encoding; digest_type = old_digest_type; } /** Process checksum lists created by md5sum. @param argc Number of command line arguments. @param argv Command line arguments array. */ static void check_md5sum(int argc, char **argv) { FILE *f; /**< Used to read the check list. */ char **lfdptr; /**< Used to traverse command line arguments. */ int i; /**< Used to traverse command line arguments. */ char *ptr; /**< Current command line argument. */ lfdptr = argv; lfdptr++; i = 1; while(i < argc) { ptr = *lfdptr; if(*ptr == '-') { ptr++; switch(*ptr) { case 'm': case 'e': { ptr++; if(!(*ptr)) { i++; lfdptr++; } } break; case '\0': { /* Standard input */ check_md5sum_file(stdin, filename_for_stdin); flag_is_first = 0x00; } break; } } else { flag_is_first = 0x00; #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 f = fopen64(ptr, "r"); #else f = fopen(ptr, "r"); #endif if(f) { check_md5sum_file(f, ptr); fclose(f); f = NULL; } else { err_open_read(ptr); } } lfdptr++; i++; } if(flag_is_first) { check_md5sum_file(stdin, filename_for_stdin); flag_is_first = 0x00; } } /** Process checksum lists created by fchksum. @param argc Number of command line arguments. @param argv Command line arguments array. */ static void check_fchksum(int argc, char **argv) { FILE *f; /**< Used to read the check list. */ char **lfdptr; /**< Used to traverse command line arguments. */ int i; /**< Used to traverse command line arguments. */ char *ptr; /**< Current command line argument. */ lfdptr = argv; lfdptr++; i = 1; while(i < argc) { ptr = *lfdptr; if(*ptr == '-') { ptr++; switch(*ptr) { case 'm': case 'e': { ptr++; if(!(*ptr)) { i++; lfdptr++; } } break; case '\0': { /* Standard input */ check_fchksum_file(stdin, filename_for_stdin); flag_is_first = 0x00; } break; } } else { flag_is_first = 0x00; #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 f = fopen64(ptr, "r"); #else f = fopen(ptr, "r"); #endif if(f) { check_fchksum_file(f, ptr); fclose(f); f = NULL; } else { err_open_read(ptr); } } lfdptr++; i++; } if(flag_is_first) { check_fchksum_file(stdin, filename_for_stdin); flag_is_first = 0x00; } } /** Process checksum lists. @param argc Number of command line arguments. @param argv Command line arguments array. */ static void check_files(int argc, char **argv) { if(flag_md5sum) { check_md5sum(argc, argv); } else { check_fchksum(argc, argv); } if(files_failed) { if(!flag_status) { fprintf(stderr, msg[18], progname, files_failed, files_checked); fflush(stderr); } } if(files_skipped) { if(!flag_status) { fprintf(stderr, msg[23], progname, files_skipped); if(exval != 1) exval = 8; } } } /** Check whether we can build a checksum for the file type. Print error message if file type is not acceptable. @param filename File name. @return 1 for yes, 0 for no. */ static int is_acceptable_file_type(char *filename) { int back = 0; /**< Return value. */ #if ON_A_WINDOWS_SYSTEM #if DK_HAVE__STAT64 struct __stat64 stbuf; if(_stat64(filename, &stbuf) == 0) { if(((stbuf.st_mode) & _S_IFMT) == _S_IFREG) { back = 1; } else { if(((stbuf.st_mode) & _S_IFMT) == _S_IFDIR) { if(flag_dir) { back = 1; } else { err_directory(filename); } } else { if(((stbuf.st_mode) & _S_IFMT) == _S_IFCHR) { back = 1; warn_device(filename); } else { if(((stbuf.st_mode) & _S_IFMT) == _S_IFIFO) { back = 0; warn_pipe(filename); } else { err_illegal_file_type(filename); } } } } } else { err_stat_failed(filename); } #else struct _stat stbuf; if(_stat(filename, &stbuf) == 0) { if(((stbuf.st_mode) & _S_IFMT) == _S_IFREG) { back = 1; } else { if(((stbuf.st_mode) & _S_IFMT) == _S_IFDIR) { if(flag_dir) { back = 1; } else { err_directory(filename); } } else { if(((stbuf.st_mode) & _S_IFMT) == _S_IFCHR) { back = 1; warn_device(filename); } else { if(((stbuf.st_mode) & _S_IFMT) == _S_IFIFO) { back = 0; warn_pipe(filename); } else { err_illegal_file_type(filename); } } } } } else { err_stat_failed(filename); } #endif #else #if DK_HAVE_STAT64 && DK_HAVE_LARGEFILE64_SOURCE struct stat64 stbuf; if(stat64(filename, &stbuf) == 0) { if(S_ISREG(stbuf.st_mode)) { back = 1; } else { if(S_ISDIR(stbuf.st_mode)) { if(flag_dir) { back = 1; } else { err_directory(filename); } } else { if(S_ISCHR(stbuf.st_mode)) { back = 1; warn_device(filename); } else { if(S_ISBLK(stbuf.st_mode)) { warn_device(filename); back = 1; } else { err_illegal_file_type(filename); } } } } } else { err_stat_failed(filename); } #else struct stat stbuf; if(stat(filename, &stbuf) == 0) { if(S_ISREG(stbuf.st_mode)) { back = 1; } else { if(S_ISDIR(stbuf.st_mode)) { if(flag_dir) { back = 1; } else { err_directory(filename); } } else { if(S_ISCHR(stbuf.st_mode)) { back = 1; warn_device(filename); } else { if(S_ISBLK(stbuf.st_mode)) { warn_device(filename); back = 1; } else { err_illegal_file_type(filename); } } } } } else { err_stat_failed(filename); } #endif #endif return back; } /** Create and print checksum for one file. @param f File to check. @param n File name (used for error reporting, may NOT be NULL). */ static void handle_file(FILE *f, char *n) { digest_ascii[0] = '\0'; if(flag_implemented) { if(create_ascii_checksum(f)) { if(!flag_header_printed) { if(!flag_skip_header) { printf( "\t## %s = %s %s\n", control_instructions[0], digest_types[digest_type ? (digest_type - 1) : 0], digest_encodings[digest_encoding ? (digest_encoding - 1) : 0] ); } flag_header_printed = 0x01; } fputs((char *)digest_ascii, stdout); fputc(' ', stdout); fputs(n, stdout); fputc('\n', stdout); } else { err_checksum_failed(n); } } else { files_skipped++; if(files_skipped == 0UL) files_skipped = 0xFFFFFFFFUL; } } /** Attempt to open file an create checksum. @param filename File name. */ static void attempt_to_run_for(char *filename) { FILE *f; /**< File descriptor to read input. */ if(is_acceptable_file_type(filename)) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 f = fopen64(filename, "rb"); #else f = fopen(filename, "rb"); #endif if(f) { handle_file(f, filename); fclose(f); } else { err_open_read(filename); } } } /** Build checksum for standard input. */ static void run_for_stdin(void) { #if ON_A_WINDOWS_SYSTEM int oldmode; /**< Old text/binary mode. */ oldmode = _setmode(_fileno(stdin), _O_BINARY); #endif handle_file(stdin, filename_for_stdin); #if ON_A_WINDOWS_SYSTEM _setmode(_fileno(stdin), oldmode); #endif } /** Create checksums for all file names listed in input. @param fipo File containing the file names to build checksums. */ static void run_for_list(FILE *fipo) { char *p1; /**< Start of line. */ while(fgets(inputline, sizeof(inputline), fipo)) { p1 = dkstr_start(inputline, NULL); if(p1) { dkstr_chomp(p1, NULL); attempt_to_run_for(p1); } } } /** Process the file names specified on the command line. @param argv Number of command line arguments. @param argc Command line arguments array. */ static void process_files(int argc, char **argv) { char **lfdptr; /**< Used to traverse command line arguments. */ int i; /**< Used to traverse command line arguments. */ char *ptr; /**< Current command line argument. */ FILE *fl; /**< File to read list from. */ if(digest_type == CHKSUM_DIGEST_UNKNOWN) { digest_type = (flag_md5sum ? CHKSUM_DIGEST_MD5 : CHKSUM_DIGEST_SHA_1); } if(digest_encoding == CHKSUM_ENCODING_UNKNOWN) { digest_encoding = (flag_md5sum ? CHKSUM_ENCODING_HEX : CHKSUM_ENCODING_ASCII85); } lfdptr = argv; lfdptr++; i = 1; while(i < argc) { ptr = *lfdptr; if(*ptr == '-') { ptr++; switch(*ptr) { case 'm': case 'e': { ptr++; if(!(*ptr)) { lfdptr++; i++; } } break; case '\0': { if(flag_list) { run_for_list(stdin); } else { run_for_stdin(); } flag_is_first = 0x00; } break; } } else { if(flag_list) { #if DK_HAVE_LARGEFILE64_SOURCE && DK_HAVE_FOPEN64 fl = fopen64(ptr, "r"); #else fl = fopen(ptr, "r"); #endif if(fl) { run_for_list(fl); fclose(fl); } else { err_open_read(ptr); } } else { attempt_to_run_for(ptr); } flag_is_first = 0x00; } lfdptr++; i++; } if(flag_is_first) { if(flag_list) { run_for_list(stdin); } else { run_for_stdin(); } } } /** After option inspection now run. @param argc Number of command line arguments. @param argv Command line arguments array. */ static void run_for_files(int argc, char **argv) { if(flag_check) { check_files(argc, argv); } else { process_files(argc, argv); } } /** The main function of the fchksum program. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, positive values for errors. */ int main(int argc, char *argv[]) { #line 2316 "fchksum.ctr" process_arguments(argc, argv); if(exval == 0) { if(flag_help | flag_version) { show_text(version_text); if(flag_help) { show_text(help_text); } } else { run_for_files(argc, argv); } } else { if(flag_help) { show_text(version_text); show_text(help_text); } } #line 2333 "fchksum.ctr" exit(exval); return exval; }