first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / 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 <basis/functions.h>
20 #include <basis/astring.h>
21 #include <structures/checksums.h>
22 #include <structures/static_memory_gremlin.h>
23 #include <timely/time_stamp.h>
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28
29 using namespace basis;
30 using namespace structures;
31 using namespace timely;
32
33 const int buffer_size = 4096;
34
35 //HOOPLE_STARTUP_CODE;
36
37 //#define DEBUG_CHECKER
38   // uncomment for noisy version.
39
40 void print_instructions_and_exit(char *program_name)
41 {
42   printf("\n\
43 Usage:\n\t%s [-t] filename [filename]\n\n\
44 This program generates a checksum for each file that is entered on the\n\
45 command line.  The checksum is (hopefully) an architecture independent\n\
46 number that is a very compressed representation of the file gestalt.\n\
47 If one compares two copies of a file, then the checksums should be identical.\n\
48 This is a useful test of whether a file copy or a program download is\n\
49 successful in making an identical version of the file.  In particular, if the\n\
50 file is made slightly bigger or smaller, or if an item in the file is changed,\n\
51 then the checksums of the two versions should be different numbers.\n\n\
52 The -b flag is used if the files are to be compared as binary files, and this\n\
53 is also the default.  The -t flag is used if the files are to be compared as\n\
54 text files.\n",
55   program_name);
56   exit(1);
57 }
58
59 #define HIGHEST_CHECK 32714
60
61 // do_checksum: takes the specified file name and generates a checksum for it.
62 // if the file is inaccessible or, at any point, reading it returns an
63 // error message, then a negative value is returned.
64 int do_checksum(char *file_name, int open_as_a_text_file)
65 {
66   char file_open_mode[10];
67   if (open_as_a_text_file) strcpy(file_open_mode, "rt");
68   else strcpy(file_open_mode, "rb");
69   FILE *opened_file = fopen(file_name, file_open_mode);
70 #ifdef DEBUG_CHECKER
71   LOG(astring("opened ") + file_name);
72 #endif
73   if (!opened_file) return common::NOT_FOUND;
74   int characters_read = 0;
75   int current_checksum_value = 0;
76   char buffer_chunk[buffer_size];
77   while (!feof(opened_file)) {
78     characters_read = int(fread(buffer_chunk, sizeof(char), buffer_size,
79         opened_file));
80     // if result is 0 or negative, stop messing with the file.
81 #ifdef DEBUG_CHECKER
82     LOG(a_sprintf("char read = %d", characters_read));
83 #endif
84     if (characters_read <= 0) {
85       if (characters_read < 0) current_checksum_value = -1;
86       else if (current_checksum_value == 0) current_checksum_value = -1;
87       break;
88     }
89     current_checksum_value = (current_checksum_value
90             + checksums::bizarre_checksum((abyte *)buffer_chunk, characters_read))
91         % HIGHEST_CHECK;
92 #ifdef DEBUG_CHECKER
93     LOG(a_sprintf("current checksum=%d", current_checksum_value));
94 #endif
95   }
96   fclose(opened_file);
97   return int(current_checksum_value);
98 }
99
100 // do_fletcher_checksum: takes the specified file name and generates a fletcher
101 // checksum for it.  if the file is inaccessible or, at any point,
102 // reading it returns an error message, then a negative value is returned.
103 int do_fletcher_checksum(char *file_name, int open_as_a_text_file)
104 {
105   char file_open_mode[10];
106   if (open_as_a_text_file) strcpy(file_open_mode, "rt");
107   else strcpy(file_open_mode, "rb");
108   FILE *opened_file = fopen(file_name, file_open_mode);
109 #ifdef DEBUG_CHECKER
110   LOG(astring("opened ") + file_name);
111 #endif
112   if (!opened_file) return common::NOT_FOUND;
113   int characters_read = 0;
114   int current_checksum_value = 0;
115   char buffer_chunk[buffer_size];
116   while (!feof(opened_file)) {
117     characters_read = int(fread(buffer_chunk, sizeof(char), buffer_size,
118         opened_file));
119     // if result is 0 or negative, stop messing with the file.
120 #ifdef DEBUG_CHECKER
121     LOG(a_sprintf("char read = %d", characters_read));
122 #endif
123     if (characters_read <= 0) {
124       if (characters_read < 0) current_checksum_value = -1;
125       else if (current_checksum_value == 0) current_checksum_value = -1;
126       break;
127     }
128     current_checksum_value = checksums::rolling_fletcher_checksum
129         ((uint16)current_checksum_value, (abyte *)buffer_chunk,
130         characters_read);
131 #ifdef DEBUG_CHECKER
132     LOG(a_sprintf("current checksum=%d", current_checksum_value));
133 #endif
134   }
135   fclose(opened_file);
136   return current_checksum_value;
137 }
138
139 int main(int argc, char *argv[])
140 {
141   char name[200];
142
143   // if the file is to be read as a text file, then this is true.
144   int open_file_as_text = false;
145
146   if (argc <= 1) print_instructions_and_exit(argv[0]);
147   else {
148     int current_parameter = 0;
149     if (argv[1][0] == '-') {
150       if (argv[1][1] == 't') {
151         current_parameter++;
152         open_file_as_text = true;
153       } else if (argv[1][1] == 'b') {
154         current_parameter++;
155         open_file_as_text = false;
156       } else print_instructions_and_exit(argv[0]);
157     }
158     bool printed_header = false;
159     while (++current_parameter < argc) {
160       if (!printed_header) {
161         printed_header = true;
162         printf("%s\n", (astring("[ checker running at ") + time_stamp::notarize(true) + "]").s());
163         printf("bizarro  fletcher  filename\n");
164         printf("=======  ========  ========\n");
165       }
166       strcpy(name, argv[current_parameter]);
167       int checksum_of_file = do_checksum(name, open_file_as_text);
168       int fletcher_chksum = do_fletcher_checksum(name, open_file_as_text);
169       if (checksum_of_file >= 0) {
170         printf("%s", a_sprintf(" %05d    0x%04x   %s\n", checksum_of_file,
171             fletcher_chksum, name).s());
172       } else {
173         printf("%s", a_sprintf("%s is inaccessible.\n", name).s());
174       }
175     }
176   }
177   return 0;
178 }
179