sum.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. sum.c
  5. Abstract:
  6. This module implements the sum utility, which implements primitive
  7. checksumming of files.
  8. Author:
  9. Evan Green 12-May-2015
  10. Environment:
  11. POSIX
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/lib/types.h>
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <getopt.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include "swlib.h"
  25. //
  26. // ---------------------------------------------------------------- Definitions
  27. //
  28. #define SUM_VERSION_MAJOR 1
  29. #define SUM_VERSION_MINOR 0
  30. #define SUM_USAGE \
  31. "usage: sum [options] [files...]\n" \
  32. "The sum utility implements primitive checksumming of input files.\n" \
  33. "Options are:\n" \
  34. " -r, -- Use the BSD sum algorithm, and use 1K blocks.\n" \
  35. " -s, --sysv -- Use the SYSV sum algorithm, and use 512 byte blocks.\n" \
  36. " --help -- Show this help text and exit.\n" \
  37. " --version -- Print the application version information and exit.\n"
  38. #define SUM_OPTIONS_STRING "rs"
  39. //
  40. // Define the default buffer size.
  41. //
  42. #define SUM_BLOCK_SIZE 4096
  43. //
  44. // Define sum application options.
  45. //
  46. //
  47. // Set this option to use the SYSV algorithm. If not set, the default is to
  48. // use the BSD algorithm.
  49. //
  50. #define SUM_OPTION_SYSV 0x00000001
  51. //
  52. // Set this option to print file names.
  53. //
  54. #define SUM_OPTION_PRINT_NAMES 0x00000002
  55. //
  56. // ------------------------------------------------------ Data Type Definitions
  57. //
  58. //
  59. // ----------------------------------------------- Internal Function Prototypes
  60. //
  61. INT
  62. SumChecksumFile (
  63. PSTR FileName,
  64. ULONG Options
  65. );
  66. //
  67. // -------------------------------------------------------------------- Globals
  68. //
  69. struct option SumLongOptions[] = {
  70. {"sysv", no_argument, 0, 's'},
  71. {"help", no_argument, 0, 'h'},
  72. {"version", no_argument, 0, 'V'},
  73. {NULL, 0, 0, 0},
  74. };
  75. //
  76. // ------------------------------------------------------------------ Functions
  77. //
  78. INT
  79. SumMain (
  80. INT ArgumentCount,
  81. CHAR **Arguments
  82. )
  83. /*++
  84. Routine Description:
  85. This routine is the main entry point for the sum utility.
  86. Arguments:
  87. ArgumentCount - Supplies the number of command line arguments the program
  88. was invoked with.
  89. Arguments - Supplies a tokenized array of command line arguments.
  90. Return Value:
  91. Returns an integer exit code. 0 for success, nonzero otherwise.
  92. --*/
  93. {
  94. ULONG ArgumentIndex;
  95. INT Option;
  96. ULONG Options;
  97. int Status;
  98. int TotalStatus;
  99. Options = 0;
  100. TotalStatus = 0;
  101. //
  102. // Process the control arguments.
  103. //
  104. while (TRUE) {
  105. Option = getopt_long(ArgumentCount,
  106. Arguments,
  107. SUM_OPTIONS_STRING,
  108. SumLongOptions,
  109. NULL);
  110. if (Option == -1) {
  111. break;
  112. }
  113. if ((Option == '?') || (Option == ':')) {
  114. Status = 1;
  115. goto MainEnd;
  116. }
  117. switch (Option) {
  118. case 'r':
  119. Options &= ~SUM_OPTION_SYSV;
  120. break;
  121. case 's':
  122. Options |= SUM_OPTION_SYSV | SUM_OPTION_PRINT_NAMES;
  123. break;
  124. case 'V':
  125. SwPrintVersion(SUM_VERSION_MAJOR, SUM_VERSION_MINOR);
  126. return 1;
  127. case 'h':
  128. printf(SUM_USAGE);
  129. return 1;
  130. default:
  131. assert(FALSE);
  132. Status = 1;
  133. goto MainEnd;
  134. }
  135. }
  136. Status = 0;
  137. ArgumentIndex = optind;
  138. if (ArgumentIndex == ArgumentCount) {
  139. Status = SumChecksumFile("-", Options);
  140. } else {
  141. if (ArgumentIndex + 1 < ArgumentCount) {
  142. Options |= SUM_OPTION_PRINT_NAMES;
  143. }
  144. while (ArgumentIndex < ArgumentCount) {
  145. Status = SumChecksumFile(Arguments[ArgumentIndex], Options);
  146. if (Status != 0) {
  147. TotalStatus = Status;
  148. }
  149. ArgumentIndex += 1;
  150. }
  151. }
  152. MainEnd:
  153. if ((TotalStatus == 0) && (Status != 0)) {
  154. TotalStatus = Status;
  155. }
  156. return TotalStatus;
  157. }
  158. //
  159. // --------------------------------------------------------- Internal Functions
  160. //
  161. INT
  162. SumChecksumFile (
  163. PSTR FileName,
  164. ULONG Options
  165. )
  166. /*++
  167. Routine Description:
  168. This routine checksums a file.
  169. Arguments:
  170. FileName - Supplies a pointer to the path of the file to checksum, or "-"
  171. for standard in.
  172. Options - Supplies application options. See SUM_OPTION_* definitions.
  173. Return Value:
  174. Returns an integer exit code. 0 for success, nonzero otherwise.
  175. --*/
  176. {
  177. UCHAR Buffer[SUM_BLOCK_SIZE];
  178. ssize_t BytesRead;
  179. int File;
  180. UINTN Index;
  181. INT Status;
  182. ULONG Sum;
  183. ULONGLONG TotalSize;
  184. if (strcmp(FileName, "-") == 0) {
  185. File = STDIN_FILENO;
  186. } else {
  187. File = open(FileName, O_RDONLY | O_BINARY | O_NOCTTY);
  188. if (File < 0) {
  189. Status = errno;
  190. SwPrintError(Status, FileName, "Cannot open");
  191. return Status;
  192. }
  193. }
  194. Sum = 0;
  195. TotalSize = 0;
  196. while (TRUE) {
  197. do {
  198. BytesRead = read(File, Buffer, SUM_BLOCK_SIZE);
  199. } while ((BytesRead < 0) && (errno == EINTR));
  200. if (BytesRead == 0) {
  201. break;
  202. } else if (BytesRead < 0) {
  203. Status = errno;
  204. SwPrintError(Status, FileName, "Read error");
  205. goto ChecksumFileEnd;
  206. }
  207. if ((Options & SUM_OPTION_SYSV) != 0) {
  208. for (Index = 0; Index < BytesRead; Index += 1) {
  209. Sum += Buffer[Index];
  210. }
  211. } else {
  212. //
  213. // The BSD algorithm rotates left by one bit each time, avoiding
  214. // some common problems with simple summing (like swapped bytes).
  215. //
  216. for (Index = 0; Index < BytesRead; Index += 1) {
  217. Sum = (Sum >> 1) + ((Sum & 1) << 15);
  218. Sum = (Sum + Buffer[Index]) & 0xFFFF;
  219. }
  220. }
  221. TotalSize += BytesRead;
  222. }
  223. //
  224. // Fold the result down to 16 bits.
  225. //
  226. if ((Options & SUM_OPTION_SYSV) != 0) {
  227. Sum = (Sum & 0xFFFF) + (Sum >> 16);
  228. Sum = (Sum & 0xFFFF) + (Sum >> 16);
  229. TotalSize = (TotalSize + 511ULL) / 512ULL;
  230. } else {
  231. TotalSize = (TotalSize + 1023ULL) / 1024ULL;
  232. }
  233. if ((Options & SUM_OPTION_PRINT_NAMES) != 0) {
  234. if ((Options & SUM_OPTION_SYSV) != 0) {
  235. printf("%d %llu %s\n", Sum, TotalSize, FileName);
  236. } else {
  237. printf("%05d %5llu %s\n", Sum, TotalSize, FileName);
  238. }
  239. } else {
  240. //
  241. // The sysv option always turns on the print names flag.
  242. //
  243. assert((Options & SUM_OPTION_SYSV) == 0);
  244. printf("%05d %5llu\n", Sum, TotalSize, FileName);
  245. }
  246. Status = 0;
  247. ChecksumFileEnd:
  248. if (File > 0) {
  249. assert(STDIN_FILENO == 0);
  250. close(File);
  251. }
  252. return Status;
  253. }