new fortune
[feisty_meow.git] / nucleus / applications / utilities / checker.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : checker                                                           *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *  Purpose:                                                                   *
7 *                                                                             *
8 *    Generates checksums for a set of files.                                  *
9 *                                                                             *
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 \*****************************************************************************/
18
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>
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 using namespace application;
32 using namespace basis;
33 using namespace loggers;
34 using namespace structures;
35 using namespace timely;
36
37 const int buffer_size = 4096;
38
39 //#define DEBUG_CHECKER
40   // uncomment for noisy version.
41
42 #undef LOG
43 #define LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT)
44
45 int print_instructions(bool good, const astring &program_name)
46 {
47   printf("\n\
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\
60 text files.\n",
61   program_name.s());
62   return !good;  // zero is successful exit.
63 }
64
65 #define HIGHEST_CHECK 32714
66
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)
71 {
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);
76 #ifdef DEBUG_CHECKER
77   LOG(astring("opened ") + file_name);
78 #endif
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,
85         opened_file));
86     // if result is 0 or negative, stop messing with the file.
87 #ifdef DEBUG_CHECKER
88     LOG(a_sprintf("char read = %d", characters_read));
89 #endif
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;
93       break;
94     }
95     current_checksum_value = (current_checksum_value
96             + checksums::bizarre_checksum((abyte *)buffer_chunk, characters_read))
97         % HIGHEST_CHECK;
98 #ifdef DEBUG_CHECKER
99     LOG(a_sprintf("current checksum=%d", current_checksum_value));
100 #endif
101   }
102   fclose(opened_file);
103   return int(current_checksum_value);
104 }
105
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)
110 {
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);
115 #ifdef DEBUG_CHECKER
116   LOG(astring("opened ") + file_name);
117 #endif
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,
124         opened_file));
125     // if result is 0 or negative, stop messing with the file.
126 #ifdef DEBUG_CHECKER
127     LOG(a_sprintf("char read = %d", characters_read));
128 #endif
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;
132       break;
133     }
134     current_checksum_value = checksums::rolling_fletcher_checksum
135         ((uint16)current_checksum_value, (abyte *)buffer_chunk,
136         characters_read);
137 #ifdef DEBUG_CHECKER
138     LOG(a_sprintf("current checksum=%d", current_checksum_value));
139 #endif
140   }
141   fclose(opened_file);
142   return current_checksum_value;
143 }
144
145 int main(int argc, char *argv[])
146 {
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;
151
152   if (argc <= 1) return print_instructions(false, argv[0]);
153
154   command_line cmds(argc, argv);
155   int index = 0;
156   if (cmds.find('b', index)) open_file_as_text = false;
157   index = 0;
158   if (cmds.find('t', index)) open_file_as_text = true;
159   index = 0;
160   if (cmds.find('q', index)) show_header = false;
161   index = 0;
162   if (cmds.find('?', index)) return print_instructions(true, argv[0]);
163   index = 0;
164   if (cmds.find("help", index)) return print_instructions(true, argv[0]);
165   bool printed_header = false;
166
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;
172       if (show_header) {
173         printf("%s\n", (astring("[ checker running at ") + time_stamp::notarize(true) + "]").s());
174         printf("bizarro  fletcher  filename\n");
175         printf("=======  ========  ========\n");
176       }
177     }
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());
184     } else {
185       printf("%s", a_sprintf("%s is inaccessible.\n", name.s()).s());
186     }
187   }
188   return 0;
189 }
190