Changeset 6113 for trunk/tools
- Timestamp:
- Jun 26, 2001, 2:14:07 AM (24 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/tools/wrc/wrc.c
r5523 r6113 4 4 * Copyrignt 1998 Bertho A. Stultiens (BS) 5 5 * 6 * 30-Apr-2000 BS 7 * 20-Jun-1998 BS 8 * 9 * 10 * 08-Jun-1998 BS 11 * 12 * 13 * 21-May-1998 BS 14 * 15 * 16 * 17 * 30-Apr-1998 BS 18 * 19 * 20 * 21 * 22 * 23 * 23-Apr-1998 BS 24 * 25 * 26 * 27 * 17-Apr-1998 BS 28 * 29 * 30 * 16-Apr-1998 BS 31 * 32 * 6 * 30-Apr-2000 BS - Integrated a new preprocessor (-E and -N) 7 * 20-Jun-1998 BS - Added -L option to prevent case conversion 8 * of embedded filenames. 9 * 10 * 08-Jun-1998 BS - Added -A option to generate autoregister code 11 * for winelib operation. 12 * 13 * 21-May-1998 BS - Removed the CPP option. Its internal now. 14 * - Added implementations for defines and includes 15 * on the commandline. 16 * 17 * 30-Apr-1998 BS - The options now contain nearly the entire alphabet. 18 * Seems to be a sign for too much freedom. I implemeted 19 * most of them as a user choice possibility for things 20 * that I do not know what to put there by default. 21 * - -l and -L options are now known as -t and -T. 22 * 23 * 23-Apr-1998 BS - Finally gave up on backward compatibility on the 24 * commandline (after a blessing from the newsgroup). 25 * So, I changed the lot. 26 * 27 * 17-Apr-1998 BS - Added many new command-line options but took care 28 * that it would not break old scripts (sigh). 29 * 30 * 16-Apr-1998 BS - There is not much left of the original source... 31 * I had to rewrite most of it because the parser 32 * changed completely with all the types etc.. 33 33 * 34 34 */ … … 38 38 #include <stdio.h> 39 39 #include <stdlib.h> 40 #ifndef __IBMC__ 40 41 #include <unistd.h> 42 #endif 41 43 #include <string.h> 42 44 #include <assert.h> … … 55 57 56 58 static char usage[] = 57 58 59 60 61 62 59 "Usage: wrc [options...] [infile[.rc|.res]]\n" 60 " -a n Alignment of resource (win16 only, default is 4)\n" 61 " -A Auto register resources (only with gcc 2.7 and better)\n" 62 " -b Create an assembly array from a binary .res file\n" 63 " -B x Set output byte-order x={n[ative], l[ittle], b[ig]}\n" 64 " (win32 only; default is n[ative] which equals " 63 65 #ifdef WORDS_BIGENDIAN 64 66 "big" 65 67 #else 66 68 "little" 67 69 #endif 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 70 "-endian)\n" 71 " -c Add 'const' prefix to C constants\n" 72 " -C cp Set the resource's codepage to cp (default is 0)\n" 73 " -d n Set debug level to 'n'\n" 74 " -D id[=val] Define preprocessor identifier id=val\n" 75 " -e Disable recognition of win32 keywords in 16bit compile\n" 76 " -E Preprocess only\n" 77 " -g Add symbols to the global c namespace\n" 78 " -h Also generate a .h file\n" 79 " -H file Same as -h but written to file\n" 80 " -I path Set include search dir to path (multiple -I allowed)\n" 81 " -l lan Set default language to lan (default is neutral {0, 0})\n" 82 " -L Leave case of embedded filenames as is\n" 83 " -m Do not remap numerical resource IDs\n" 84 " -n Do not generate .s file\n" 85 " -N Do not preprocess input\n" 86 " -o file Output to file (default is infile.[res|s|h]\n" 87 " -p prefix Give a prefix for the generated names\n" 88 " -r Create binary .res file (compile only)\n" 89 " -s Add structure with win32/16 (PE/NE) resource directory\n" 90 " -t Generate indirect loadable resource tables\n" 91 " -T Generate only indirect loadable resources tables\n" 92 " -V Print version end exit\n" 93 " -w 16|32 Select win16 or win32 output (default is win32)\n" 94 " -W Enable pedantic warnings\n" 95 "Input is taken from stdin if no sourcefile specified.\n" 96 "Debug level 'n' is a bitmask with following meaning:\n" 97 " * 0x01 Tell which resource is parsed (verbose mode)\n" 98 " * 0x02 Dump internal structures\n" 99 " * 0x04 Create a parser trace (yydebug=1)\n" 100 " * 0x08 Preprocessor messages\n" 101 " * 0x10 Preprocessor lex messages\n" 102 " * 0x20 Preprocessor yacc trace\n" 103 "The -o option only applies to the final destination file, which is\n" 104 "in case of normal compile a .s file. You must use the '-H header.h'\n" 105 "option to override the header-filename.\n" 106 "If no input filename is given and the output name is not overridden\n" 107 "with -o and/or -H, then the output is written to \"wrc.tab.[sh]\"\n" 108 ; 107 109 108 110 char version_string[] = "Wine Resource Compiler Version " WRC_FULLVERSION "\n" 109 110 111 "Copyright 1998-2000 Bertho A. Stultiens\n" 112 " 1994 Martin von Loewis\n"; 111 113 112 114 /* … … 136 138 137 139 /* 138 * debuglevel == DEBUGLEVEL_NONE 139 * debuglevel & DEBUGLEVEL_CHAT 140 * debuglevel & DEBUGLEVEL_DUMP 141 * debuglevel & DEBUGLEVEL_TRACE 142 * debuglevel & DEBUGLEVEL_PPMSG 143 * debuglevel & DEBUGLEVEL_PPLEX 144 * debuglevel & DEBUGLEVEL_PPTRACE 140 * debuglevel == DEBUGLEVEL_NONE Don't bother 141 * debuglevel & DEBUGLEVEL_CHAT Say whats done 142 * debuglevel & DEBUGLEVEL_DUMP Dump internal structures 143 * debuglevel & DEBUGLEVEL_TRACE Create parser trace 144 * debuglevel & DEBUGLEVEL_PPMSG Preprocessor messages 145 * debuglevel & DEBUGLEVEL_PPLEX Preprocessor lex trace 146 * debuglevel & DEBUGLEVEL_PPTRACE Preprocessor yacc trace 145 147 */ 146 148 int debuglevel = DEBUGLEVEL_NONE; … … 240 242 int remap = 1; 241 243 242 char *output_name; 243 char *input_name; 244 char *header_name; 245 char *temp_name; 246 247 int line_number = 1; 248 int char_number = 1; 249 250 char *cmdline; 251 time_t now; 252 253 resource_t *resource_top; 244 char *output_name; /* The name given by the -o option */ 245 char *input_name; /* The name given on the command-line */ 246 char *header_name; /* The name given by the -H option */ 247 char *temp_name; /* Temporary file for preprocess pipe */ 248 249 int line_number = 1; /* The current line */ 250 int char_number = 1; /* The current char pos within the line */ 251 252 char *cmdline; /* The entire commandline */ 253 time_t now; /* The time of start of wrc */ 254 255 resource_t *resource_top; /* The top of the parsed resources */ 254 256 255 257 #ifdef __EMX__ … … 264 266 int main(int argc,char *argv[]) 265 267 { 266 extern char* optarg; 267 extern int optind; 268 int optc; 269 int lose = 0; 270 int ret; 271 int a; 272 int i; 273 int cmdlen; 274 275 signal(SIGSEGV, segvhandler); 276 277 now = time(NULL); 278 279 /* First rebuild the commandline to put in destination */ 280 /* Could be done through env[], but not all OS-es support it */ 281 cmdlen = 4; /* for "wrc " */ 282 for(i = 1; i < argc; i++) 283 cmdlen += strlen(argv[i]) + 1; 284 cmdline = (char *)xmalloc(cmdlen); 285 strcpy(cmdline, "wrc "); 286 for(i = 1; i < argc; i++) 287 { 288 strcat(cmdline, argv[i]); 289 if(i < argc-1) 290 strcat(cmdline, " "); 291 } 292 293 while((optc = getopt(argc, argv, "a:AbB:cC:d:D:eEghH:I:l:LmnNo:p:rstTVw:W")) != EOF) 294 { 295 switch(optc) 296 { 297 case 'a': 298 alignment = atoi(optarg); 299 break; 300 case 'A': 301 auto_register = 1; 302 break; 303 case 'b': 304 binary = 1; 305 break; 306 case 'B': 307 switch(optarg[0]) 308 { 309 case 'n': 310 case 'N': 311 byteorder = WRC_BO_NATIVE; 312 break; 313 case 'l': 314 case 'L': 315 byteorder = WRC_BO_LITTLE; 316 break; 317 case 'b': 318 case 'B': 319 byteorder = WRC_BO_BIG; 320 break; 321 default: 322 fprintf(stderr, "Byteordering must be n[ative], l[ittle] or b[ig]\n"); 323 lose++; 324 } 325 break; 326 case 'c': 327 constant = 1; 328 break; 329 case 'C': 330 codepage = strtol(optarg, NULL, 0); 331 break; 332 case 'd': 333 debuglevel = strtol(optarg, NULL, 0); 334 break; 335 case 'D': 336 add_cmdline_define(optarg); 337 break; 338 case 'e': 339 extensions = 0; 340 break; 341 case 'E': 342 preprocess_only = 1; 343 break; 344 case 'g': 345 global = 1; 346 break; 347 case 'H': 348 header_name = strdup(optarg); 349 /* Fall through */ 350 case 'h': 351 create_header = 1; 352 break; 353 case 'I': 354 add_include_path(optarg); 355 break; 356 case 'l': 357 { 358 int lan; 359 lan = strtol(optarg, NULL, 0); 360 currentlanguage = new_language(PRIMARYLANGID(lan), SUBLANGID(lan)); 361 } 362 break; 363 case 'L': 364 leave_case = 1; 365 break; 366 case 'm': 367 remap = 0; 368 break; 369 case 'n': 370 create_s = 0; 371 break; 372 case 'N': 373 no_preprocess = 1; 374 break; 375 case 'o': 376 output_name = strdup(optarg); 377 break; 378 case 'p': 268 extern char* optarg; 269 extern int optind; 270 int optc; 271 int lose = 0; 272 int ret; 273 int a; 274 int i; 275 int cmdlen; 276 277 signal(SIGSEGV, segvhandler); 278 279 now = time(NULL); 280 281 /* First rebuild the commandline to put in destination */ 282 /* Could be done through env[], but not all OS-es support it */ 283 //cmdlen = 4; /* for "wrc " */ //hi guys! can't you count? 284 cmdlen = 5; /* for "wrc " */ 285 for(i = 1; i < argc; i++) 286 cmdlen += strlen(argv[i]) + 1; 287 cmdline = (char *)xmalloc(cmdlen); 288 strcpy(cmdline, "wrc "); 289 for(i = 1; i < argc; i++) 290 { 291 strcat(cmdline, argv[i]); 292 if(i < argc-1) 293 strcat(cmdline, " "); 294 } 295 296 while((optc = getopt(argc, argv, "a:AbB:cC:d:D:eEghH:I:l:LmnNo:p:rstTVw:W")) != EOF) 297 { 298 switch(optc) 299 { 300 case 'a': 301 alignment = atoi(optarg); 302 break; 303 case 'A': 304 auto_register = 1; 305 break; 306 case 'b': 307 binary = 1; 308 break; 309 case 'B': 310 switch(optarg[0]) 311 { 312 case 'n': 313 case 'N': 314 byteorder = WRC_BO_NATIVE; 315 break; 316 case 'l': 317 case 'L': 318 byteorder = WRC_BO_LITTLE; 319 break; 320 case 'b': 321 case 'B': 322 byteorder = WRC_BO_BIG; 323 break; 324 default: 325 fprintf(stderr, "Byteordering must be n[ative], l[ittle] or b[ig]\n"); 326 lose++; 327 } 328 break; 329 case 'c': 330 constant = 1; 331 break; 332 case 'C': 333 codepage = strtol(optarg, NULL, 0); 334 break; 335 case 'd': 336 debuglevel = strtol(optarg, NULL, 0); 337 break; 338 case 'D': 339 add_cmdline_define(optarg); 340 break; 341 case 'e': 342 extensions = 0; 343 break; 344 case 'E': 345 preprocess_only = 1; 346 break; 347 case 'g': 348 global = 1; 349 break; 350 case 'H': 351 header_name = strdup(optarg); 352 /* Fall through */ 353 case 'h': 354 create_header = 1; 355 break; 356 case 'I': 357 add_include_path(optarg); 358 break; 359 case 'l': 360 { 361 int lan; 362 lan = strtol(optarg, NULL, 0); 363 currentlanguage = new_language(PRIMARYLANGID(lan), SUBLANGID(lan)); 364 } 365 break; 366 case 'L': 367 leave_case = 1; 368 break; 369 case 'm': 370 remap = 0; 371 break; 372 case 'n': 373 create_s = 0; 374 break; 375 case 'N': 376 no_preprocess = 1; 377 break; 378 case 'o': 379 output_name = strdup(optarg); 380 break; 381 case 'p': 379 382 #ifdef NEED_UNDERSCORE_PREFIX 380 381 382 383 prefix = (char *)xmalloc(strlen(optarg)+2); 384 prefix[0] = '_'; 385 strcpy(prefix+1, optarg); 383 386 #else 384 387 prefix = xstrdup(optarg); 385 388 #endif 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 389 break; 390 case 'r': 391 create_res = 1; 392 break; 393 case 's': 394 create_dir = 1; 395 break; 396 case 'T': 397 indirect_only = 1; 398 /* Fall through */ 399 case 't': 400 indirect = 1; 401 break; 402 case 'V': 403 printf(version_string); 404 exit(0); 405 break; 406 case 'w': 407 if(!strcmp(optarg, "16")) 408 win32 = 0; 409 else if(!strcmp(optarg, "32")) 410 win32 = 1; 411 else 412 lose++; 413 break; 414 case 'W': 415 pedantic = 1; 416 break; 417 default: 418 lose++; 419 break; 420 } 421 } 422 423 if(lose) 424 { 425 fprintf(stderr, usage); 426 return 1; 427 } 428 429 /* Check the command line options for invalid combinations */ 430 if(win32) 431 { 432 if(!extensions) 433 { 434 warning("Option -e ignored with 32bit compile\n"); 435 extensions = 1; 436 } 437 } 438 439 if(create_res) 440 { 441 if(constant) 442 { 443 warning("Option -c ignored with compile to .res\n"); 444 constant = 0; 445 } 446 447 if(create_header) 448 { 449 warning("Option -[h|H] ignored with compile to .res\n"); 450 create_header = 0; 451 } 452 453 if(indirect) 454 { 455 warning("Option -l ignored with compile to .res\n"); 456 indirect = 0; 457 } 458 459 if(indirect_only) 460 { 461 warning("Option -L ignored with compile to .res\n"); 462 indirect_only = 0; 463 } 464 465 if(global) 466 { 467 warning("Option -g ignored with compile to .res\n"); 468 global = 0; 469 } 470 471 if(create_dir) 472 { 473 error("Option -r and -s cannot be used together\n"); 474 } 475 476 if(binary) 477 { 478 error("Option -r and -b cannot be used together\n"); 479 } 480 } 481 482 if(byteorder != WRC_BO_NATIVE) 483 { 484 if(binary) 485 error("Forced byteordering not supported for binary resources\n"); 486 } 487 488 if(preprocess_only) 489 { 490 if(constant) 491 { 492 warning("Option -c ignored with preprocess only\n"); 493 constant = 0; 494 } 495 496 if(create_header) 497 { 498 warning("Option -[h|H] ignored with preprocess only\n"); 499 create_header = 0; 500 } 501 502 if(indirect) 503 { 504 warning("Option -l ignored with preprocess only\n"); 505 indirect = 0; 506 } 507 508 if(indirect_only) 509 { 510 error("Option -E and -L cannot be used together\n"); 511 } 512 513 if(global) 514 { 515 warning("Option -g ignored with preprocess only\n"); 516 global = 0; 517 } 518 519 if(create_dir) 520 { 521 warning("Option -s ignored with preprocess only\n"); 522 create_dir = 0; 523 } 524 525 if(binary) 526 { 527 error("Option -E and -b cannot be used together\n"); 528 } 529 530 if(no_preprocess) 531 { 532 error("Option -E and -N cannot be used together\n"); 533 } 534 } 532 535 533 536 #if !defined(HAVE_WINE_CONSTRUCTOR) 534 535 536 537 538 537 if(auto_register) 538 { 539 warning("Autoregister code non-operable (HAVE_WINE_CONSTRUCTOR not defined)"); 540 auto_register = 0; 541 } 539 542 #endif 540 543 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 real_name = input_name;/* Because it gets overwritten */647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 exit(1);/* Error during preprocess */671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 544 /* Set alignment power */ 545 a = alignment; 546 for(alignment_pwr = 0; alignment_pwr < 10 && a > 1; alignment_pwr++) 547 { 548 a >>= 1; 549 } 550 if(a != 1) 551 { 552 error("Alignment must be between 1 and 1024"); 553 } 554 if((1 << alignment_pwr) != alignment) 555 { 556 error("Alignment must be a power of 2"); 557 } 558 559 /* Kill io buffering when some kind of debuglevel is enabled */ 560 if(debuglevel) 561 { 562 setbuf(stdout,0); 563 setbuf(stderr,0); 564 } 565 566 yydebug = debuglevel & DEBUGLEVEL_TRACE ? 1 : 0; 567 yy_flex_debug = debuglevel & DEBUGLEVEL_TRACE ? 1 : 0; 568 ppdebug = debuglevel & DEBUGLEVEL_PPTRACE ? 1 : 0; 569 pp_flex_debug = debuglevel & DEBUGLEVEL_PPLEX ? 1 : 0; 570 571 /* Set the default defined stuff */ 572 add_cmdline_define("__WRC__=" WRC_EXP_STRINGIZE(WRC_MAJOR_VERSION)); 573 add_cmdline_define("__WRC_MINOR__=" WRC_EXP_STRINGIZE(WRC_MINOR_VERSION)); 574 add_cmdline_define("__WRC_MICRO__=" WRC_EXP_STRINGIZE(WRC_MICRO_VERSION)); 575 add_cmdline_define("__WRC_PATCH__=" WRC_EXP_STRINGIZE(WRC_MICRO_VERSION)); 576 577 add_cmdline_define("RC_INVOKED=1"); 578 579 if(win32) 580 { 581 add_cmdline_define("__WIN32__=1"); 582 add_cmdline_define("__FLAT__=1"); 583 } 584 585 add_special_define("__FILE__"); 586 add_special_define("__LINE__"); 587 add_special_define("__DATE__"); 588 add_special_define("__TIME__"); 589 590 /* Check if the user set a language, else set default */ 591 if(!currentlanguage) 592 currentlanguage = new_language(0, 0); 593 594 /* Check for input file on command-line */ 595 if(optind < argc) 596 { 597 input_name = argv[optind]; 598 } 599 600 if(binary && !input_name) 601 { 602 error("Binary mode requires .res file as input\n"); 603 } 604 605 /* Generate appropriate outfile names */ 606 if(!output_name && !preprocess_only) 607 { 608 output_name = dup_basename(input_name, binary ? ".res" : ".rc"); 609 strcat(output_name, create_res ? ".res" : ".s"); 610 } 611 612 if(!header_name && !create_res) 613 { 614 header_name = dup_basename(input_name, binary ? ".res" : ".rc"); 615 strcat(header_name, ".h"); 616 } 617 618 /* Run the preprocessor on the input */ 619 if(!no_preprocess && !binary) 620 { 621 char *real_name; 622 /* 623 * Preprocess the input to a temp-file, or stdout if 624 * no output was given. 625 */ 626 627 chat("Starting preprocess"); 628 629 if(preprocess_only && !output_name) 630 { 631 ppout = stdout; 632 } 633 else if(preprocess_only && output_name) 634 { 635 if(!(ppout = fopen(output_name, "wb"))) 636 error("Could not open %s for writing\n", output_name); 637 } 638 else 639 { 640 if(!(temp_name = tmpnam(NULL))) 641 error("Could nor generate a temp-name\n"); 642 temp_name = xstrdup(temp_name); 643 if(!(ppout = fopen(temp_name, "wb"))) 644 error("Could not create a temp-file\n"); 645 646 atexit(rm_tempfile); 647 } 648 649 real_name = input_name; /* Because it gets overwritten */ 650 651 if(!input_name) 652 ppin = stdin; 653 else 654 { 655 if(!(ppin = fopen(input_name, "rb"))) 656 error("Could not open %s\n", input_name); 657 } 658 659 fprintf(ppout, "# 1 \"%s\" 1\n", input_name ? input_name : ""); 660 661 ret = ppparse(); 662 663 input_name = real_name; 664 665 if(input_name) 666 fclose(ppin); 667 668 fclose(ppout); 669 670 input_name = temp_name; 671 672 if(ret) 673 exit(1); /* Error during preprocess */ 674 675 if(preprocess_only) 676 exit(0); 677 } 678 679 if(!binary) 680 { 681 /* Go from .rc to .res or .s */ 682 chat("Starting parse"); 683 684 if(!(yyin = fopen(input_name, "rb"))) 685 error("Could not open %s for input\n", input_name); 686 687 ret = yyparse(); 688 689 if(input_name) 690 fclose(yyin); 691 692 if(ret) 693 { 694 /* Error during parse */ 695 exit(1); 696 } 697 698 if(debuglevel & DEBUGLEVEL_DUMP) 699 dump_resources(resource_top); 700 701 /* Convert the internal lists to binary data */ 702 resources2res(resource_top); 703 704 if(create_res) 705 { 706 chat("Writing .res-file"); 707 write_resfile(output_name, resource_top); 708 } 709 else 710 { 711 if(create_s) 712 { 713 chat("Writing .s-file"); 714 write_s_file(output_name, resource_top); 715 } 716 if(create_header) 717 { 718 chat("Writing .h-file"); 719 write_h_file(header_name, resource_top); 720 } 721 } 722 723 } 724 else 725 { 726 /* Go from .res to .s */ 727 chat("Reading .res-file"); 728 resource_top = read_resfile(input_name); 729 if(create_s) 730 { 731 chat("Writing .s-file"); 732 write_s_file(output_name, resource_top); 733 } 734 if(create_header) 735 { 736 chat("Writing .h-file"); 737 write_h_file(header_name, resource_top); 738 } 739 } 740 741 return 0; 739 742 } 740 743 … … 742 745 static void rm_tempfile(void) 743 746 { 744 745 747 if(temp_name) 748 unlink(temp_name); 746 749 } 747 750 748 751 static void segvhandler(int sig) 749 752 { 750 751 752 753 753 fprintf(stderr, "\n%s:%d: Oops, segment violation\n", input_name, line_number); 754 fflush(stdout); 755 fflush(stderr); 756 abort(); 754 757 } 755 758 759 760
Note:
See TracChangeset
for help on using the changeset viewer.