1 // restest.cpp 2 3 #include <algobase.h> 4 #include <stdio.h> 5 #include <string.h> 6 7 #include <Entry.h> 8 #include <File.h> 9 #include <String.h> 10 11 #include "Exception.h" 12 #include "OffsetFile.h" 13 #include "ResourceFile.h" 14 #include "Warnings.h" 15 16 const char kUsage[] = { 17 "Usage: %s <options> <filenames>\n" 18 "options:\n" 19 " -h, --help print this help\n" 20 " -l, --list list each file's resources (short version)\n" 21 " -L, --list-long list each file's resources (long version)\n" 22 " -s, --summary print a summary\n" 23 " -w, --write-test write the file resources (in memory only) and\n" 24 " compare the data with the file's\n" 25 }; 26 27 const status_t USAGE_ERROR = B_ERRORS_END + 1; 28 const status_t USAGE_HELP = B_ERRORS_END + 2; 29 30 enum listing_level { 31 NO_LISTING, 32 SHORT_LISTING, 33 LONG_LISTING, 34 }; 35 36 struct TestOptions { 37 listing_level listing; 38 bool write_test; 39 bool summary; 40 }; 41 42 struct TestResult { 43 TestResult(const char* filename) 44 : filename(filename), warnings(), exception(NULL) 45 { 46 } 47 48 ~TestResult() 49 { 50 delete exception; 51 } 52 53 BString filename; 54 Warnings warnings; 55 Exception* exception; 56 }; 57 58 // print_indented 59 void 60 print_indented(const char* str, uint32 chars, bool indentFirst = true) 61 { 62 const uint32 MAX_CHARS_PER_LINE = 75; 63 int32 charsLeft = strlen(str); 64 if (chars < MAX_CHARS_PER_LINE) { 65 for (int32 line = 0; charsLeft > 0; line++) { 66 if (line != 0 || indentFirst) { 67 for (int32 i = 0; (uint32)i < chars; i++) 68 printf(" "); 69 } 70 int32 bytesLeftOnLine = MAX_CHARS_PER_LINE - chars; 71 int32 printChars = min(bytesLeftOnLine, charsLeft); 72 printf("%.*s\n", (int)printChars, str); 73 str += printChars; 74 charsLeft -= printChars; 75 // skip spaces 76 while (*str == ' ') { 77 str++; 78 charsLeft--; 79 } 80 } 81 } 82 } 83 84 // print_indented 85 void 86 print_indented(const char* indentStr, const char* str) 87 { 88 uint32 chars = strlen(indentStr); 89 printf(indentStr); 90 print_indented(str, chars, false); 91 } 92 93 // parse_arguments 94 void 95 parse_arguments(int argc, const char* const* argv, BList& files, 96 TestOptions& options) 97 { 98 // default options 99 options.listing = NO_LISTING; 100 options.write_test = false; 101 options.summary = false; 102 // parse arguments 103 for (int32 i = 1; i < argc; i++) { 104 const char* arg = argv[i]; 105 int32 len = strlen(arg); 106 if (len == 0) 107 throw Exception(USAGE_ERROR, "Illegal argument: `'."); 108 if (arg[0] == '-') { 109 if (len < 2) 110 throw Exception(USAGE_ERROR, "Illegal argument: `-'."); 111 if (arg[1] == '-') { 112 const char* option = arg + 2; 113 // help 114 if (!strcmp(option, "help")) { 115 throw Exception(USAGE_HELP); 116 // list 117 } else if (!strcmp(option, "list")) { 118 if (options.listing == NO_LISTING) 119 options.listing = SHORT_LISTING; 120 // list-long 121 } else if (!strcmp(option, "list-long")) { 122 options.listing = LONG_LISTING; 123 // summary 124 } else if (!strcmp(option, "summary")) { 125 options.summary = true; 126 // write-test 127 } else if (!strcmp(option, "write-test")) { 128 options.write_test = true; 129 // error 130 } else { 131 throw Exception(USAGE_ERROR, BString("Illegal option: `") 132 << arg << "'."); 133 } 134 } else { 135 for (int32 i = 1; i < len; i++) { 136 char option = arg[i]; 137 switch (option) { 138 // help 139 case 'h': 140 throw Exception(USAGE_HELP); 141 break; 142 // list 143 case 'l': 144 if (options.listing == NO_LISTING) 145 options.listing = SHORT_LISTING; 146 break; 147 // list long 148 case 'L': 149 options.listing = LONG_LISTING; 150 break; 151 // summary 152 case 's': 153 options.summary = true; 154 break; 155 // write test 156 case 'w': 157 options.write_test = true; 158 break; 159 // error 160 default: 161 throw Exception(USAGE_ERROR, 162 BString("Illegal option: `") 163 << arg << "'."); 164 break; 165 } 166 } 167 } 168 } else 169 files.AddItem(const_cast<char*>(arg)); 170 } 171 } 172 173 // test_file 174 void 175 test_file(const char* filename, const TestOptions& options, 176 TestResult& testResult) 177 { 178 Warnings::SetCurrentWarnings(&testResult.warnings); 179 ResourceFile resFile; 180 try { 181 // check if the file exists 182 BEntry entry(filename, true); 183 status_t error = entry.InitCheck(); 184 if (error != B_OK) 185 throw Exception(error); 186 if (!entry.Exists() || !entry.IsFile()) 187 throw Exception("Entry doesn't exist or is no regular file."); 188 entry.Unset(); 189 // open the file 190 BFile file(filename, B_READ_ONLY); 191 error = file.InitCheck(); 192 if (error != B_OK) 193 throw Exception(error, "Failed to open file."); 194 // do the actual test 195 resFile.Init(file); 196 if (options.write_test) 197 resFile.WriteTest(); 198 } catch (Exception exception) { 199 testResult.exception = new Exception(exception); 200 } 201 Warnings::SetCurrentWarnings(NULL); 202 // print warnings and error 203 if (options.listing != NO_LISTING 204 || testResult.warnings.CountWarnings() > 0 || testResult.exception) { 205 printf("\nFile `%s':\n", filename); 206 } 207 // warnings 208 if (testResult.warnings.CountWarnings() > 0) { 209 for (int32 i = 0; 210 const char* warning = testResult.warnings.WarningAt(i); 211 i++) { 212 print_indented(" Warning: ", warning); 213 } 214 } 215 // error 216 if (testResult.exception) { 217 status_t error = testResult.exception->GetError(); 218 const char* description = testResult.exception->GetDescription(); 219 if (strlen(description) > 0) { 220 print_indented(" Error: ", description); 221 if (error != B_OK) 222 print_indented(" ", strerror(error)); 223 } else if (error != B_OK) 224 print_indented(" Error: ", strerror(error)); 225 } 226 // list resources 227 if (resFile.InitCheck() == B_OK) { 228 switch (options.listing) { 229 case NO_LISTING: 230 break; 231 case SHORT_LISTING: 232 resFile.PrintToStream(false); 233 break; 234 case LONG_LISTING: 235 resFile.PrintToStream(true); 236 break; 237 } 238 } 239 } 240 241 // test_files 242 void 243 test_files(BList& files, TestOptions& options) 244 { 245 BList testResults; 246 int32 successTestCount = 0; 247 int32 warningTestCount = 0; 248 int32 failedTestCount = 0; 249 for (int32 i = 0; 250 const char* filename = (const char*)files.ItemAt(i); 251 i++) { 252 TestResult* testResult = new TestResult(filename); 253 testResults.AddItem(testResult); 254 test_file(filename, options, *testResult); 255 if (testResult->exception) 256 failedTestCount++; 257 else if (testResult->warnings.CountWarnings() > 0) 258 warningTestCount++; 259 else 260 successTestCount++; 261 } 262 // print summary 263 if (options.summary) { 264 printf("\nSummary:\n"); 265 printf( "=======\n"); 266 // successful tests 267 if (successTestCount > 0) { 268 if (successTestCount == 1) 269 printf("one successful test\n"); 270 else 271 printf("%ld successful tests\n", successTestCount); 272 } 273 // tests with warnings 274 if (warningTestCount > 0) { 275 if (warningTestCount == 1) 276 printf("one test with warnings:\n"); 277 else 278 printf("%ld tests with warnings:\n", warningTestCount); 279 for (int32 i = 0; 280 TestResult* testResult = (TestResult*)testResults.ItemAt(i); 281 i++) { 282 if (!testResult->exception 283 && testResult->warnings.CountWarnings() > 0) { 284 printf(" `%s'\n", testResult->filename.String()); 285 } 286 } 287 } 288 // failed tests 289 if (failedTestCount > 0) { 290 if (failedTestCount == 1) 291 printf("one test failed:\n"); 292 else 293 printf("%ld tests failed:\n", failedTestCount); 294 for (int32 i = 0; 295 TestResult* testResult = (TestResult*)testResults.ItemAt(i); 296 i++) { 297 if (testResult->exception) 298 printf(" `%s'\n", testResult->filename.String()); 299 } 300 } 301 } 302 // cleanup 303 for (int32 i = 0; 304 TestResult* testResult = (TestResult*)testResults.ItemAt(i); 305 i++) { 306 delete testResult; 307 } 308 } 309 310 // main 311 int 312 main(int argc, const char* const* argv) 313 { 314 int returnValue = 0; 315 const char* cmdName = argv[0]; 316 TestOptions options; 317 BList files; 318 try { 319 // parse arguments 320 parse_arguments(argc, argv, files, options); 321 if (files.CountItems() == 0) 322 throw Exception(USAGE_ERROR, "No files given."); 323 // test the files 324 test_files(files, options); 325 } catch (Exception exception) { 326 status_t error = exception.GetError(); 327 const char* description = exception.GetDescription(); 328 switch (error) { 329 case B_OK: 330 if (strlen(description) > 0) 331 fprintf(stderr, "%s\n", description); 332 returnValue = 1; 333 break; 334 case USAGE_ERROR: 335 if (strlen(description) > 0) 336 fprintf(stderr, "%s\n", description); 337 fprintf(stderr, kUsage, cmdName); 338 returnValue = 1; 339 break; 340 case USAGE_HELP: 341 printf(kUsage, cmdName); 342 break; 343 default: 344 fprintf(stderr, " error: %s\n", strerror(error)); 345 returnValue = 1; 346 break; 347 } 348 } 349 return returnValue; 350 } 351 352