1 /*****************************************************************************\
4 * Author : Chris Koeritz *
8 * Generates checksums for a set of files. *
10 *******************************************************************************
11 * Copyright (c) 1990-$now By Author. This program is free software; you can *
12 * redistribute it and/or modify it under the terms of the GNU General Public *
13 * License as published by the Free Software Foundation; either version 2 of *
14 * the License or (at your option) any later version. This is online at: *
15 * http://www.fsf.org/copyleft/gpl.html *
16 * Please send any updates to: fred@gruntose.com *
17 \*****************************************************************************/
19 #include <application/command_line.h>
20 #include <basis/functions.h>
21 #include <basis/astring.h>
22 #include <loggers/program_wide_logger.h>
23 #include <structures/checksums.h>
24 #include <structures/static_memory_gremlin.h>
25 #include <timely/time_stamp.h>
31 using namespace application;
32 using namespace basis;
33 using namespace loggers;
34 using namespace structures;
35 using namespace timely;
37 const int buffer_size = 4096;
39 //#define DEBUG_CHECKER
40 // uncomment for noisy version.
43 #define LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT)
45 int print_instructions(bool good, const astring &program_name)
48 Usage:\n\t%s [-q] [-t|-b] filename [filename]\n\n\
49 This program generates a checksum for each file that is entered on the\n\
50 command line. The checksum is (hopefully) an architecture independent\n\
51 number that is a very compressed representation of the file gestalt.\n\
52 If one compares two copies of a file, then the checksums should be identical.\n\
53 This is a useful test of whether a file copy or a program download is\n\
54 successful in making an identical version of the file. In particular, if the\n\
55 file is made slightly bigger or smaller, or if an item in the file is changed,\n\
56 then the checksums of the two versions should be different numbers.\n\n\
57 The -q flag specifies a quieter print-out, without any headers.\n\
58 The -b flag is used if the files are to be compared as binary files, and this\n\
59 is also the default. The -t flag is used if the files are to be compared as\n\
62 return !good; // zero is successful exit.
65 #define HIGHEST_CHECK 32714
67 // do_checksum: takes the specified file name and generates a checksum for it.
68 // if the file is inaccessible or, at any point, reading it returns an
69 // error message, then a negative value is returned.
70 int do_checksum(const astring &file_name, int open_as_a_text_file)
72 char file_open_mode[10];
73 if (open_as_a_text_file) strcpy(file_open_mode, "rt");
74 else strcpy(file_open_mode, "rb");
75 FILE *opened_file = fopen(file_name.s(), file_open_mode);
77 LOG(astring("opened ") + file_name);
79 if (!opened_file) return common::NOT_FOUND;
80 int characters_read = 0;
81 int current_checksum_value = 0;
82 char buffer_chunk[buffer_size];
83 while (!feof(opened_file)) {
84 characters_read = int(fread(buffer_chunk, sizeof(char), buffer_size,
86 // if result is 0 or negative, stop messing with the file.
88 LOG(a_sprintf("char read = %d", characters_read));
90 if (characters_read <= 0) {
91 if (characters_read < 0) current_checksum_value = -1;
92 else if (current_checksum_value == 0) current_checksum_value = -1;
95 current_checksum_value = (current_checksum_value
96 + checksums::bizarre_checksum((abyte *)buffer_chunk, characters_read))
99 LOG(a_sprintf("current checksum=%d", current_checksum_value));
103 return int(current_checksum_value);
106 // do_fletcher_checksum: takes the specified file name and generates a fletcher
107 // checksum for it. if the file is inaccessible or, at any point,
108 // reading it returns an error message, then a negative value is returned.
109 int do_fletcher_checksum(const astring &file_name, int open_as_a_text_file)
111 char file_open_mode[10];
112 if (open_as_a_text_file) strcpy(file_open_mode, "rt");
113 else strcpy(file_open_mode, "rb");
114 FILE *opened_file = fopen(file_name.s(), file_open_mode);
116 LOG(astring("opened ") + file_name);
118 if (!opened_file) return common::NOT_FOUND;
119 int characters_read = 0;
120 int current_checksum_value = 0;
121 char buffer_chunk[buffer_size];
122 while (!feof(opened_file)) {
123 characters_read = int(fread(buffer_chunk, sizeof(char), buffer_size,
125 // if result is 0 or negative, stop messing with the file.
127 LOG(a_sprintf("char read = %d", characters_read));
129 if (characters_read <= 0) {
130 if (characters_read < 0) current_checksum_value = -1;
131 else if (current_checksum_value == 0) current_checksum_value = -1;
134 current_checksum_value = checksums::rolling_fletcher_checksum
135 ((uint16)current_checksum_value, (abyte *)buffer_chunk,
138 LOG(a_sprintf("current checksum=%d", current_checksum_value));
142 return current_checksum_value;
145 int main(int argc, char *argv[])
147 // if the file is to be read as a text file, then this is true.
148 bool open_file_as_text = false;
149 // if we are to show our normal header info, this will be true.
150 bool show_header = true;
152 if (argc <= 1) return print_instructions(false, argv[0]);
154 command_line cmds(argc, argv);
156 if (cmds.find('b', index)) open_file_as_text = false;
158 if (cmds.find('t', index)) open_file_as_text = true;
160 if (cmds.find('q', index)) show_header = false;
162 if (cmds.find('?', index)) return print_instructions(true, argv[0]);
164 if (cmds.find("help", index)) return print_instructions(true, argv[0]);
165 bool printed_header = false;
167 for (int entry = 0; entry < cmds.entries(); entry++) {
168 command_parameter c = cmds.get(entry);
169 if (c.type() != command_parameter::VALUE) continue;
170 if (!printed_header) {
171 printed_header = true;
173 printf("%s\n", (astring("[ checker running at ") + time_stamp::notarize(true) + "]").s());
174 printf("bizarro fletcher filename\n");
175 printf("======= ======== ========\n");
178 astring name = c.text();
179 int checksum_of_file = do_checksum(name, open_file_as_text);
180 int fletcher_chksum = do_fletcher_checksum(name, open_file_as_text);
181 if (checksum_of_file >= 0) {
182 printf("%s", a_sprintf(" %05d 0x%04x %s\n", checksum_of_file,
183 fletcher_chksum, name.s()).s());
185 printf("%s", a_sprintf("%s is inaccessible.\n", name.s()).s());