/*++ Copyright (c) 2015 Minoca Corp. All Rights Reserved Module Name: sum.c Abstract: This module implements the sum utility, which implements primitive checksumming of files. Author: Evan Green 12-May-2015 Environment: POSIX --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include #include #include #include #include #include "swlib.h" // // ---------------------------------------------------------------- Definitions // #define SUM_VERSION_MAJOR 1 #define SUM_VERSION_MINOR 0 #define SUM_USAGE \ "usage: sum [options] [files...]\n" \ "The sum utility implements primitive checksumming of input files.\n" \ "Options are:\n" \ " -r, -- Use the BSD sum algorithm, and use 1K blocks.\n" \ " -s, --sysv -- Use the SYSV sum algorithm, and use 512 byte blocks.\n" \ " --help -- Show this help text and exit.\n" \ " --version -- Print the application version information and exit.\n" #define SUM_OPTIONS_STRING "rs" // // Define the default buffer size. // #define SUM_BLOCK_SIZE 4096 // // Define sum application options. // // // Set this option to use the SYSV algorithm. If not set, the default is to // use the BSD algorithm. // #define SUM_OPTION_SYSV 0x00000001 // // Set this option to print file names. // #define SUM_OPTION_PRINT_NAMES 0x00000002 // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // INT SumChecksumFile ( PSTR FileName, ULONG Options ); // // -------------------------------------------------------------------- Globals // struct option SumLongOptions[] = { {"sysv", no_argument, 0, 's'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {NULL, 0, 0, 0}, }; // // ------------------------------------------------------------------ Functions // INT SumMain ( INT ArgumentCount, CHAR **Arguments ) /*++ Routine Description: This routine is the main entry point for the sum utility. Arguments: ArgumentCount - Supplies the number of command line arguments the program was invoked with. Arguments - Supplies a tokenized array of command line arguments. Return Value: Returns an integer exit code. 0 for success, nonzero otherwise. --*/ { ULONG ArgumentIndex; INT Option; ULONG Options; int Status; int TotalStatus; Options = 0; TotalStatus = 0; // // Process the control arguments. // while (TRUE) { Option = getopt_long(ArgumentCount, Arguments, SUM_OPTIONS_STRING, SumLongOptions, NULL); if (Option == -1) { break; } if ((Option == '?') || (Option == ':')) { Status = 1; goto MainEnd; } switch (Option) { case 'r': Options &= ~SUM_OPTION_SYSV; break; case 's': Options |= SUM_OPTION_SYSV | SUM_OPTION_PRINT_NAMES; break; case 'V': SwPrintVersion(SUM_VERSION_MAJOR, SUM_VERSION_MINOR); return 1; case 'h': printf(SUM_USAGE); return 1; default: assert(FALSE); Status = 1; goto MainEnd; } } Status = 0; ArgumentIndex = optind; if (ArgumentIndex == ArgumentCount) { Status = SumChecksumFile("-", Options); } else { if (ArgumentIndex + 1 < ArgumentCount) { Options |= SUM_OPTION_PRINT_NAMES; } while (ArgumentIndex < ArgumentCount) { Status = SumChecksumFile(Arguments[ArgumentIndex], Options); if (Status != 0) { TotalStatus = Status; } ArgumentIndex += 1; } } MainEnd: if ((TotalStatus == 0) && (Status != 0)) { TotalStatus = Status; } return TotalStatus; } // // --------------------------------------------------------- Internal Functions // INT SumChecksumFile ( PSTR FileName, ULONG Options ) /*++ Routine Description: This routine checksums a file. Arguments: FileName - Supplies a pointer to the path of the file to checksum, or "-" for standard in. Options - Supplies application options. See SUM_OPTION_* definitions. Return Value: Returns an integer exit code. 0 for success, nonzero otherwise. --*/ { UCHAR Buffer[SUM_BLOCK_SIZE]; ssize_t BytesRead; int File; UINTN Index; INT Status; ULONG Sum; ULONGLONG TotalSize; if (strcmp(FileName, "-") == 0) { File = STDIN_FILENO; } else { File = open(FileName, O_RDONLY | O_BINARY | O_NOCTTY); if (File < 0) { Status = errno; SwPrintError(Status, FileName, "Cannot open"); return Status; } } Sum = 0; TotalSize = 0; while (TRUE) { do { BytesRead = read(File, Buffer, SUM_BLOCK_SIZE); } while ((BytesRead < 0) && (errno == EINTR)); if (BytesRead == 0) { break; } else if (BytesRead < 0) { Status = errno; SwPrintError(Status, FileName, "Read error"); goto ChecksumFileEnd; } if ((Options & SUM_OPTION_SYSV) != 0) { for (Index = 0; Index < BytesRead; Index += 1) { Sum += Buffer[Index]; } } else { // // The BSD algorithm rotates left by one bit each time, avoiding // some common problems with simple summing (like swapped bytes). // for (Index = 0; Index < BytesRead; Index += 1) { Sum = (Sum >> 1) + ((Sum & 1) << 15); Sum = (Sum + Buffer[Index]) & 0xFFFF; } } TotalSize += BytesRead; } // // Fold the result down to 16 bits. // if ((Options & SUM_OPTION_SYSV) != 0) { Sum = (Sum & 0xFFFF) + (Sum >> 16); Sum = (Sum & 0xFFFF) + (Sum >> 16); TotalSize = (TotalSize + 511ULL) / 512ULL; } else { TotalSize = (TotalSize + 1023ULL) / 1024ULL; } if ((Options & SUM_OPTION_PRINT_NAMES) != 0) { if ((Options & SUM_OPTION_SYSV) != 0) { printf("%d %llu %s\n", Sum, TotalSize, FileName); } else { printf("%05d %5llu %s\n", Sum, TotalSize, FileName); } } else { // // The sysv option always turns on the print names flag. // assert((Options & SUM_OPTION_SYSV) == 0); printf("%05d %5llu\n", Sum, TotalSize, FileName); } Status = 0; ChecksumFileEnd: if (File > 0) { assert(STDIN_FILENO == 0); close(File); } return Status; }