1 /*****************************************************************************\
3 * Name : value_tagger *
4 * Author : Chris Koeritz *
8 * Scoots through the entire known code base and builds a list of all the *
9 * outcome (and filter) values for that tree. A manifest of the names is *
10 * produced. Most of the behavior is driven by the ini file whose name is *
11 * passed on the command line. *
12 * Note that the set of items that can be searched for can be specified *
13 * in the ini file, although they must follow the format of: *
14 * pattern(name, value, description) *
15 * where the "pattern" is the search term and the other three items specify *
16 * the enumerated value to be marked. *
18 *******************************************************************************
19 * Copyright (c) 2005-$now By Author. This program is free software; you can *
20 * redistribute it and/or modify it under the terms of the GNU General Public *
21 * License as published by the Free Software Foundation; either version 2 of *
22 * the License or (at your option) any later version. This is online at: *
23 * http://www.fsf.org/copyleft/gpl.html *
24 * Please send any updates to: fred@gruntose.com *
25 \*****************************************************************************/
27 #include <application/application_shell.h>
28 #include <application/command_line.h>
29 #include <application/hoople_main.h>
30 #include <application/windoze_helper.h>
31 #include <basis/environment.h>
32 #include <basis/functions.h>
33 #include <basis/utf_conversion.h>
34 #include <configuration/ini_configurator.h>
35 #include <filesystem/byte_filer.h>
36 #include <filesystem/directory_tree.h>
37 #include <filesystem/filename.h>
38 #include <loggers/combo_logger.h>
39 #include <loggers/critical_events.h>
40 #include <loggers/program_wide_logger.h>
41 #include <structures/set.h>
42 #include <structures/string_array.h>
43 #include <structures/string_table.h>
44 #include <timely/time_stamp.h>
45 #include <textual/parser_bits.h>
49 #include "../../library/algorithms/sorts.h"
55 #define LOG(s) EMERGENCY_LOG(program_wide_logger::get(), astring(s))
57 using namespace algorithms;
58 using namespace application;
59 using namespace basis;
60 using namespace configuration;
61 using namespace filesystem;
62 using namespace loggers;
63 using namespace structures;
64 using namespace textual;
65 using namespace timely;
67 const int LONGEST_SEPARATION = 128;
68 // the longest we expect a single line of text to be in definition blocks.
69 // if the definition of an outcome or whatever is farther away than this
70 // many characters from a comment start, we will no longer consider the
71 // line to be commented out. this pretty much will never happen unless it's
72 // intentionally done to break this case.
74 const char *SKIP_VALUE_PHRASE = "SKIP_TO_VALUE";
75 // the special phrase we use to indicate that values should jump to
78 ////////////////////////////////////////////////////////////////////////////
80 // this object records all the data that we gather for the defined items.
88 astring _extra_tag; //!< records special info for links.
90 item_record(const astring &name = astring::empty_string(), int value = 999,
91 const astring &description = astring::empty_string(),
92 const astring &path = astring::empty_string(),
93 const astring &extra_tag = astring::empty_string())
94 : _name(name), _value(value), _description(description), _path(path),
95 _extra_tag(extra_tag) {}
98 ////////////////////////////////////////////////////////////////////////////
103 search_record(const astring &search = astring::empty_string(),
104 bool is_link = false, search_record *link = NULL_POINTER)
105 : _search(search), _no_modify(false), _is_link(is_link), _our_link(link),
106 _current_value(0), _value_increment(1) {}
108 // these properties are available for both real or linked records.
109 astring _search; // our term to search for in the files.
110 bool _no_modify; // true if values should not be automatically incremented.
111 astring _tag; // extra information attached to this type.
113 bool is_link() const { return _is_link; }
114 // returns true if this object is leeching off another object for data.
116 search_record *our_link() const { return _our_link; }
117 // returns the object that this object is a mere shadow of.
119 symbol_table<item_record> &definitions() {
120 if (is_link()) return _our_link->_definitions;
121 else return _definitions;
124 int ¤t_value() {
125 if (is_link()) return _our_link->_current_value;
126 else return _current_value;
129 int &value_increment() {
130 if (is_link()) return _our_link->_value_increment;
131 else return _value_increment;
134 int_set &out_of_band() {
135 if (is_link()) return _our_link->_out_of_band;
136 else return _out_of_band;
140 bool _is_link; // true if this object links to another.
141 search_record *_our_link; // the search we share for our values.
142 symbol_table<item_record> _definitions;
143 // the definitions that we found in the code.
144 int _current_value; // the next value to use for our term.
145 int _value_increment;
146 // how much to add for each new value, if this is an incrementing search.
147 int_set _out_of_band;
148 // values we've seen that were premature. we always want to honor this
149 // set, if it exists, but there will be nothing in it if the search has
150 // completely standard non-incrementing type. this could be varied by
151 // a non-incrementer linking to a standard incrementer.
154 //! a table of terms that we will search for in the code.
155 class active_searches : public symbol_table<search_record>
158 ////////////////////////////////////////////////////////////////////////////
160 // this class provides us a way to easily sort our items based on value.
162 class simple_sorter {
166 simple_sorter(int index = 0, int value = 0) : _index(index), _value(value) {}
167 bool operator < (const simple_sorter &to_compare) const
168 { return _value < to_compare._value; }
169 bool operator > (const simple_sorter &to_compare) const
170 { return _value > to_compare._value; }
171 bool operator == (const simple_sorter &to_compare) const
172 { return _value == to_compare._value; }
175 class sorting_array : public array<simple_sorter> {};
177 ////////////////////////////////////////////////////////////////////////////
179 class value_tagger : public application_shell
183 virtual ~value_tagger();
184 DEFINE_CLASS_NAME("value_tagger");
186 int print_instructions_and_exit();
188 bool process_tree(const astring &path);
189 // called on each directory hierarchy that we need to process.
191 bool process_file(const astring &path);
192 // examines the file specified to see if it matches our needs.
194 bool parse_define(const astring &scanning, int indy, astring &name,
195 int &value, astring &description, int &num_start, int &num_end);
196 // processes the string in "scanning" to find parentheses surrounding
197 // the "name", "value" and "description". the "description" field may
198 // occupy multiple lines, so all are gathered together to form one
199 // unbroken string. the "num_start" and "num_end" specify where the
200 // numeric value was found, in case it needs to be patched.
203 ini_configurator *_ini; // the configuration for what we'll scan.
204 string_table _dirs; // the list of directories.
205 string_table _dirs_seen; // full list of already processed directories.
206 filename _manifest_filename; // the name of the manifest we'll create.
207 byte_filer _manifest; // the actual file we're building.
208 active_searches _search_list; // tracks our progress in scanning files.
209 int_array _search_ordering;
210 // lists the terms in the order they should be applied. initially this
211 // carries the first pass items, but later will be reset for second pass.
212 int_array _postponed_searches;
213 // lists the searches that must wait until the main search is done.
214 string_table _modified_files; // the list of files that we touched.
217 ////////////////////////////////////////////////////////////////////////////
219 value_tagger::value_tagger()
220 : application_shell(),
226 value_tagger::~value_tagger()
231 int value_tagger::print_instructions_and_exit()
233 LOG(a_sprintf("%s usage:", filename(_global_argv[0]).basename().raw().s()));
237 This utility scans a code base for outcome and filter definitions. It will\n\
238 only scan the header files (*.h) found in the directories specified. The\n\
239 single parameter is expected to be an INI filename that contains the scanning\n\
240 configuration. The INI file should be formatted like this (where the $HOME\n\
241 can be any variable substitution from the environment):");
245 output=$HOME/manifest.txt\n\
252 $HOME/source/lib_src/library/basis\n\
253 $HOME/source/lib_src/library\n\
254 $HOME/source/lib_src/communication/sockets\n\
255 $HOME/source/lib_src/communication\n\
256 $HOME/source/lib_src\n\
257 $HOME/source/app_src\n\
258 $HOME/source/test_src\n\
269 [DEFINE_API_OUTCOME]\n\
271 link=DEFINE_OUTCOME\n\
274 The \"first\" field defines the starting value that should be assigned to\n\
276 The \"increment\" field specifies what to add to a value for the next item.\n\
277 The optional \"no_modify\" flag means that the values should not be auto-\n\
278 incremented; their current value will be used.\n\
279 The optional \"link\" field defines this type of item as using the current\n\
280 values for another type of item. In this case, API_OUTCOME will use the\n\
281 values for OUTCOME to share its integer space, but API_OUTCOME is not auto-\n\
282 incremented even though OUTCOME is. This causes the values for OUTCOME and\n\
283 API_OUTCOME to be checked for uniqueness together, but only OUTCOME will be\n\
284 auto-incremented. Note that only one level of linking is supported currently.\n\
285 The optional \"tag\" can be used to distinguish the entries for a particular\n\
286 search type if needed. This is most helpful for links, so that they can be\n\
287 distinguished from their base type.\n\
294 astring header_string(const astring &build_number)
297 #ifndef GENERATED_VALUES_MANIFEST\n\
298 #define GENERATED_VALUES_MANIFEST\n\
300 // This file contains all outcomes and filters for this build.\n\
302 // Generated for build %s on %s\n\
304 ", build_number.s(), time_stamp::notarize(true).s());
307 astring footer_string(const byte_array &full_config_file)
309 return a_sprintf("\n\
310 // End of definitions.\n\
313 // The following is the full configuration for this build:\n\
321 #endif // outer guard.\n\
322 ", (char *)full_config_file.observe());
325 int value_tagger::execute()
328 if (_global_argc < 2) {
329 return print_instructions_and_exit();
332 log(time_stamp::notarize(true) + "value_tagger started.", basis::ALWAYS_PRINT);
334 astring test_repository = environment::get("FEISTY_MEOW_APEX");
335 if (!test_repository) {
337 There is a problem with a required build precondition. The following\r\n\
338 variables must be set before the build is run:\r\n\
340 FEISTY_MEOW_APEX This should point at the root of the build tree.\r\n\
342 There are also a few variables only required for CLAM-based compilation:\r\n\
344 MAKEFLAGS This should be set to \"-I $FEISTY_MEOW_APEX/clam\".\r\n\
346 Note that on Win32 platforms, these should be set in the System or User\r\n\
347 variables before running a build.\r\n";
349 ::MessageBox(0, to_unicode_temp(msg),
350 to_unicode_temp("Missing Precondition"), MB_ICONWARNING|MB_OK);
352 non_continuable_error(class_name(), func, msg);
355 astring ini_file = _global_argv[1]; // the name of our ini file.
356 _ini = new ini_configurator(ini_file, ini_configurator::RETURN_ONLY);
358 // read the name of the manifest file to create.
359 _manifest_filename = filename(_ini->load("manifest", "output", ""));
360 if (!_manifest_filename.raw().length()) {
361 non_continuable_error(class_name(), ini_file, "The 'output' file entry is missing");
363 _manifest_filename = parser_bits::substitute_env_vars(_manifest_filename);
365 LOG(astring("Sending Manifest to ") + _manifest_filename);
368 filename(_manifest_filename).unlink();
369 // clean out the manifest ahead of time.
371 // read the list of directories to scan for code.
372 string_table temp_dirs;
373 bool read_dirs = _ini->get_section("directories", temp_dirs);
374 if (!read_dirs || !temp_dirs.symbols()) {
375 non_continuable_error(class_name(), ini_file,
376 "The 'directories' section is missing");
378 for (int i = 0; i < temp_dirs.symbols(); i++) {
379 //log(astring("curr is ") + current);
380 filename current = filename(parser_bits::substitute_env_vars(temp_dirs.name(i)));
381 _dirs.add(current, "");
384 LOG(astring("Directories to scan..."));
385 LOG(_dirs.text_form());
387 astring rdir = environment::get("FEISTY_MEOW_APEX");
389 astring parmfile = environment::get("BUILD_PARAMETER_FILE");
390 if (parmfile.t()) fname = parmfile;
391 else fname = rdir + "/build.ini";
393 // read the list of search patterns.
394 string_table searches;
395 bool read_searches = _ini->get_section("searches", searches);
396 if (!read_searches || !searches.symbols()) {
397 non_continuable_error(class_name(), ini_file,
398 "The 'searches' section is missing");
401 LOG("Searching for...");
402 LOG(searches.text_form());
404 // now make sure that we get the configuration for each type of value.
405 for (int i = 0; i < searches.symbols(); i++) {
406 const astring &curr_name = searches.name(i);
408 search_record *check_search = _search_list.find(curr_name);
410 non_continuable_error(class_name(), ini_file,
411 astring("section ") + curr_name + " is being defined twice");
415 // check for whether this section is linked to another or not.
416 astring linked = _ini->load(curr_name, "link", "");
417 search_record *our_link_found = NULL_POINTER;
419 // we found that this should be linked to another item.
420 our_link_found = _search_list.find(linked);
421 if (!our_link_found) {
422 non_continuable_error(class_name(), ini_file,
423 astring("linked section ") + curr_name + " is linked to missing "
424 "section " + linked);
426 search_record new_guy(curr_name, true, our_link_found);
427 _search_list.add(curr_name, new_guy);
429 // this section is a stand-alone section.
430 search_record new_guy(curr_name);
431 _search_list.add(curr_name, new_guy);
435 // find our new search cabinet again so we can use it.
436 search_record *curr_search = _search_list.find(curr_name);
438 non_continuable_error(class_name(), ini_file,
439 astring("section ") + curr_name + " is missing from table "
440 "after addition; logic error");
443 // specify some defaults first.
446 if (!curr_search->is_link()) {
447 // a linked object doesn't get to specify starting value or increment.
448 start = _ini->load(curr_name, "first", start);
449 curr_search->current_value() = start;
450 increm = _ini->load(curr_name, "increment", increm);
451 curr_search->value_increment() = increm;
453 start = curr_search->our_link()->current_value();
454 increm = curr_search->our_link()->value_increment();
457 int no_modify = _ini->load(curr_name, "no_modify", 0);
459 curr_search->_no_modify = true;
462 astring tag = _ini->load(curr_name, "tag", "");
464 curr_search->_tag = tag;
467 a_sprintf to_show("%s: no_modify=%s", curr_name.s(),
468 no_modify? "true" : "false");
470 if (curr_search->is_link()) {
471 // links show who they're hooked to.
472 to_show += astring(" link=") + curr_search->our_link()->_search;
474 // non-links get to show off their start value and increment.
475 to_show += a_sprintf(" start=%d increment=%d", start, increm);
478 to_show += astring(" tag=") + curr_search->_tag;
484 // now gather some info about the build that we can plug into the manifest.
486 byte_filer build_file(fname, "r");
487 if (!build_file.good()) {
488 non_continuable_error(class_name(), build_file.name(),
489 "Could not find the build configuration; is FEISTY_MEOW_APEX set?");
491 byte_array full_config;
492 build_file.read(full_config, 100000); // a good chance to be big enough.
495 //log("got config info:");
496 //log((char *)full_config.observe());
498 astring build_number;
499 ini_configurator temp_ini(fname, configurator::RETURN_ONLY);
500 build_number += temp_ini.load("version", "major", "");
502 build_number += temp_ini.load("version", "minor", "");
504 build_number += temp_ini.load("version", "revision", "");
506 build_number += temp_ini.load("version", "build", "");
507 if (build_number.equal_to("...")) {
508 non_continuable_error(class_name(), build_file.name(),
509 "Could not read the build number; is build parameter file malformed?");
512 //log(astring("got build num: ") + build_number);
514 // now that we know what file to create, write the header blob for it.
515 _manifest.open(_manifest_filename, "wb");
516 if (!_manifest.good()) {
517 non_continuable_error(class_name(), _manifest_filename,
518 "Could not write to the manifest file!");
520 _manifest.write(header_string(build_number));
522 // make sure we have the right ordering for our terms. items that are
523 // non-modify types must come before the modifying types.
524 for (int i = 0; i < _search_list.symbols(); i++) {
525 search_record &curr_reco = _search_list[i];
526 if (curr_reco._no_modify)
527 _search_ordering += i;
529 _postponed_searches += i;
532 // scan across each directory specified for our first pass.
533 LOG("First pass...");
534 for (int i = 0; i < _dirs.symbols(); i++) {
535 if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment.
536 LOG(astring(" Processing: ") + _dirs.name(i));
537 bool ret = process_tree(_dirs.name(i));
539 LOG(astring("Problem encountered in directory ") + _dirs.name(i));
545 LOG("Second pass...");
546 _search_ordering = _postponed_searches; // recharge the list for 2nd pass.
547 _dirs_seen.reset(); // drop any directories we saw before.
548 for (int i = 0; i < _dirs.symbols(); i++) {
549 if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment.
550 LOG(astring(" Processing: ") + _dirs.name(i));
551 bool ret = process_tree(_dirs.name(i));
553 LOG(astring("Problem encountered in directory ") + _dirs.name(i));
558 const astring quote = "\"";
559 const astring comma = ",";
561 // scoot across all the completed searches and dump results.
562 for (int i = 0; i < _search_list.symbols(); i++) {
563 search_record &curr_reco = _search_list[i];
564 const astring &pattern = curr_reco._search;
566 _manifest.write(astring("/* START ") + pattern + "\n");
567 _manifest.write(astring("[") + pattern + "]\n");
569 if (!curr_reco.is_link()) {
570 // scoot across all definitions and print them out.
572 // do the print out in order, as dictated by the sign of the increment.
573 sorting_array sortie;
574 for (int j = 0; j < curr_reco.definitions().symbols(); j++) {
575 const item_record &rec = curr_reco.definitions().get(j);
576 sortie += simple_sorter(j, rec._value);
578 shell_sort(sortie.access(), sortie.length(),
579 negative(curr_reco.value_increment()));
581 for (int j = 0; j < sortie.length(); j++) {
582 int indy = sortie[j]._index;
583 const item_record &rec = curr_reco.definitions().get(indy);
584 astring to_write = " ";
585 if (rec._extra_tag.t()) {
586 to_write += astring("(") + rec._extra_tag + ") ";
588 to_write += quote + rec._name + quote + comma + " ";
589 to_write += quote + a_sprintf("%d", rec._value) + quote + comma + " ";
590 to_write += quote + rec._description + quote + comma + " ";
591 to_write += quote + rec._path + quote;
593 _manifest.write(to_write);
596 // this is just a link.
597 astring to_write = " Linked to search item ";
598 to_write += curr_reco.our_link()->_search;
600 _manifest.write(to_write);
603 _manifest.write(astring("END ") + pattern + " */\n\n");
606 _manifest.write(footer_string(full_config));
608 // show all the modified files.
609 if (_modified_files.symbols()) {
610 const int syms = _modified_files.symbols();
611 LOG("Modified Files:");
612 LOG("===============");
613 for (int i = 0; i < syms; i++) {
614 LOG(_modified_files.name(i));
617 LOG("No files needed modification for generated values.");
621 log(time_stamp::notarize(true) + "value_tagger finished.", ALWAYS_PRINT);
626 #define INBO (indy < scanning.length())
627 // a macro that makes length checking less verbose.
629 // make sure we drop any spaces in between important bits.
630 #define SKIP_SPACES \
631 while (INBO && parser_bits::white_space(scanning[indy])) indy++;
633 // return with a failure but say why it happened.
634 #define FAIL_PARSE(why) { \
635 log(astring("failed to parse the string because ") + why + ".", ALWAYS_PRINT); \
639 bool value_tagger::parse_define(const astring &scanning, int indy,
640 astring &name, int &value, astring &description, int &num_start,
643 // prepare our result objects.
644 name = ""; value = -1; description = ""; num_start = -1; num_end = -1;
648 // look for starting parenthesis.
649 if (!INBO || (scanning[indy] != '(') )
650 FAIL_PARSE("the first parenthesis is missing");
652 indy++; // skip paren.
655 // find the name of the item being defined.
656 while (INBO && (scanning[indy] != ',') ) {
657 name += scanning[indy];
661 indy++; // skip the comma.
666 while (INBO && parser_bits::is_numeric(scanning[indy])) {
667 num_string += scanning[indy];
671 value = num_string.convert(0);
675 if (!INBO || (scanning[indy] != ',') )
676 FAIL_PARSE("the post-value comma is missing");
681 if (!INBO || (scanning[indy] != '"') )
682 FAIL_PARSE("the opening quote for the description is missing");
684 indy++; // now we should be at raw text.
686 // scan through the full description, taking into account that it might
687 // be broken across multiple lines as several quoted bits.
688 bool in_quote = true; // we're inside a quote now.
689 while (INBO && (scanning[indy] != ')') ) {
690 const char curr = scanning[indy];
691 //hmmm: escaped quotes are not currently handled.
692 if (curr == '"') in_quote = !in_quote; // switch quoting state.
693 else if (in_quote) description += curr;
697 return scanning[indy] == ')';
700 bool value_tagger::process_file(const astring &path)
702 byte_filer examining(path, "rb");
703 if (!examining.good()) {
704 log(astring("Error reading file: ") + path, ALWAYS_PRINT);
707 examining.seek(0, byte_filer::FROM_END);
708 int fsize = int(examining.tell());
709 examining.seek(0, byte_filer::FROM_START);
711 astring contents('\0', fsize + 20);
712 int bytes_read = examining.read((abyte *)contents.access(), fsize);
713 // read the file directly into a big astring.
715 contents[bytes_read] = '\0';
716 contents.shrink(); // drop any extra stuff at end.
718 bool modified = false; // set to true if we need to write the file back.
720 // check if the file matches our phrases of interest.
721 bool matched = false;
722 for (int q = 0; q < _search_list.symbols(); q++) {
723 search_record &curr_reco = _search_list[q];
724 if (contents.contains(curr_reco._search)) {
725 //_manifest.write(astring("MATCH-") + curr_pattern + ": " + path + "\n" ); //temp
731 if (!matched) return true;
733 // now we have verified that there's something interesting in this file.
734 // go through to find the interesting bits.
736 // we do this in the search ordering that we established earlier, so we
737 // will tag the values in the proper order.
738 for (int x = 0; x < _search_ordering.length(); x++) {
739 int q = _search_ordering[x]; // get our real index.
740 search_record &curr_reco = _search_list[q];
741 const astring &curr_pattern = curr_reco._search;
742 ///log(astring("now seeking ") + curr_pattern);
743 int start_from = 0; // where searches will start from.
746 // search forward for next match.
747 int indy = contents.find(curr_pattern, start_from);
748 if (negative(indy)) break; // no more matches.
749 start_from = indy + 5; // ensure we'll skip past the last match.
751 // make sure our deadly pattern isn't in front; we don't want to
752 // process the actual definition of the macro in question.
753 //log(a_sprintf("indy=%d [indy-1]=%c [indy-2]=%c", indy, contents[indy-1], contents[indy-2]));
754 if ( (indy > 3) && (contents[indy-1] == ' ')
755 && (contents[indy-2] == 'e') ) {
756 int def_indy = contents.find("#define", indy, true);
757 //log(astring("checking ") + curr_pattern + a_sprintf(": defindy %d, ", def_indy) + path + "\n" );
759 if (non_negative(def_indy) && (absolute_value(indy - def_indy) < 12) ) {
760 // they're close enough that we probably need to skip this
761 // occurrence of our search term.
762 //_manifest.write(astring("DEMATCH-") + curr_pattern + ": had the #define! " + path + "\n" );
767 // make sure we don't include commented lines in consideration.
768 int comm_indy = contents.find("//", indy, true);
769 if (non_negative(comm_indy)) {
770 //log("found a comment marker");
771 // we found a comment before the definition, but we're not sure how
773 if (absolute_value(comm_indy - indy) < LONGEST_SEPARATION) {
774 //log("comment is close enough...");
775 // they could be on the same line... unless lines are longer than
777 bool found_cr = false;
778 for (int q = comm_indy; q < indy; q++) {
779 if (parser_bits::is_eol(contents[q])) {
785 // if there's a comment before the definition and no carriage
786 // returns in between, then this is just a comment.
787 //log(astring("DEMATCH-") + curr_pattern + ": had the comment! " + path + "\n" );
793 // now we are pretty sure this is a righteous definition of an outcome,
794 // and not the definition of the macro itself.
795 int value, num_start, num_end;
796 astring name, description;
797 bool found_it = parse_define(contents, indy + curr_pattern.length(),
798 name, value, description, num_start, num_end);
800 log(astring("there was a problem parsing ") + curr_pattern + " in " + path, ALWAYS_PRINT);
804 // handle the special keyword for changing the value. this is useful
805 // if you want a set of outcomes to start at a specific range.
806 if (name.equal_to(SKIP_VALUE_PHRASE)) {
807 LOG(astring("\tSkipping value for ") + curr_pattern
808 + a_sprintf(" to %d because of request in\n\t", value) + path);
809 curr_reco.current_value() = value;
812 // make sure that the current value is not already in use.
813 if (!curr_reco.out_of_band().member(curr_reco.current_value()))
815 // if we had a match above, we need to adjust the current value.
816 curr_reco.current_value() += curr_reco.value_increment();
818 if (name.equal_to(SKIP_VALUE_PHRASE)) {
819 continue; // keep going now that we vetted the current value.
822 //must catch some conditions here for values:
823 // for incrementing types, we can always just try to use the next value
824 // once we know it wasn't already defined out of band?
825 // for non-incrementing types, we need to ensure we haven't already seen
826 // the thing. do we just always add a value seen to out of band?
827 // for mixed types, the incrementing side needs to not reuse out of band
830 astring other_place; // the other place it was defined.
831 if (curr_reco.out_of_band().member(value) && curr_reco._no_modify) {
832 // this is bad; we have already seen this value elsewhere...
833 for (int x = 0; x < curr_reco.definitions().symbols(); x++) {
834 // see if we can find the previous definition in our list.
835 if (value == curr_reco.definitions()[x]._value)
836 other_place = curr_reco.definitions()[x]._path;
838 non_continuable_error(class_name(), path,
839 a_sprintf("There is a duplicate value here for %s=%d ! "
840 "Also defined in %s.", name.s(), value, other_place.s()));
843 // we care sometimes that this value is different than the next
844 // sequential one we'd assign. if it's a non-modifying type of
845 // search, then we can't change the assigned value anyway--we can
846 // only report the error in re-using a value (above).
847 if (!curr_reco._no_modify) {
848 // check that the defined value matches the next one we'd assign.
849 if (value != curr_reco.current_value()) {
850 // patch the value with the appropriate one we've been tracking.
852 value = curr_reco.current_value();
853 contents.zap(num_start, num_end); // remove old fusty value.
854 contents.insert(num_start, a_sprintf("%d", value));
855 _modified_files.add(path, "");
857 // move the current value up (or down).
858 curr_reco.current_value() += curr_reco.value_increment();
860 // non-modifying type of value here.
864 curr_reco.out_of_band() += value;
865 // we've vetted the value, and now we're definitely using it.
867 // make sure they aren't trying to reuse the name for this item.
869 bool found_name = false; // we don't want to find name already there.
870 if (curr_reco.definitions().find(name)) {
871 rec = *curr_reco.definitions().find(name);
875 // this is bad. this means we are not unique. remove the manifest
876 // file due to this error.
877 _manifest.close(); // close the file since we want to whack it.
878 filename(_manifest_filename).unlink();
879 non_continuable_error(class_name(), path,
880 a_sprintf("There is a duplicate name here (%s)! "
881 "Also defined in %s.", name.s(), rec._path.s()));
884 // record the definition in the appropriate table.
885 curr_reco.definitions().add(name, item_record(name, value,
886 description, path, curr_reco._tag));
888 //log(curr_pattern + a_sprintf(": name=%s value=%d desc=[%s]\n", name.s(), value, description.s()));
894 // rewrite the file, since we modified its contents.
895 bool chmod_result = filename(path).chmod(filename::ALLOW_BOTH,
896 filename::USER_RIGHTS);
900 chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
901 #elif defined(__WIN32__)
902 chmod_value = _S_IREAD | _S_IWRITE;
904 //unknown. let's try unix...
905 chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
907 int chmod_result = chmod(path.s(), chmod_value);
910 log(astring("there was a problem changing permissions on ") + path
911 + "; writing the new version might fail.", ALWAYS_PRINT);
914 byte_filer rewriting(path, "wb");
915 rewriting.write(contents);
922 bool value_tagger::process_tree(const astring &path)
924 directory_tree dir(path, "*.h");
925 if (!dir.good()) return false;
927 dir_tree_iterator *ted = dir.start(directory_tree::prefix);
928 // create our iterator to perform a prefix traversal.
930 filename curr_dir; // the current path the iterator is at.
931 string_array files; // the filenames held at the iterator.
933 while (directory_tree::current(*ted, curr_dir, files)) {
934 // we have a good directory to process.
936 // omit any subdirectories that exactly match directories we've already
937 // scanned. necessary to avoid redoing whole areas.
938 if (!_dirs_seen.find(curr_dir)) {
939 // deal with each matching header file we've found.
940 for (int i = 0; i < files.length(); i++) {
941 bool file_ret = process_file(filename(curr_dir.raw(), files[i]));
943 log(astring("There was an error while processing ") + files[i], ALWAYS_PRINT);
947 _dirs_seen.add(curr_dir, "");
950 // go to the next place.
951 directory_tree::next(*ted);
954 directory_tree::throw_out(ted);
958 HOOPLE_MAIN(value_tagger, )
960 #ifdef __BUILD_STATIC_APPLICATION__
961 // static dependencies found by buildor_gen_deps.sh:
962 #include <application/application_shell.cpp>
963 #include <application/command_line.cpp>
964 #include <application/windoze_helper.cpp>
965 #include <basis/astring.cpp>
966 #include <basis/common_outcomes.cpp>
967 #include <basis/environment.cpp>
968 #include <basis/guards.cpp>
969 #include <basis/mutex.cpp>
970 #include <basis/utf_conversion.cpp>
971 #include <configuration/application_configuration.cpp>
972 #include <configuration/configurator.cpp>
973 #include <configuration/ini_configurator.cpp>
974 #include <configuration/ini_parser.cpp>
975 #include <configuration/table_configurator.cpp>
976 #include <configuration/variable_tokenizer.cpp>
977 #include <filesystem/byte_filer.cpp>
978 #include <filesystem/directory.cpp>
979 #include <filesystem/directory_tree.cpp>
980 #include <filesystem/file_info.cpp>
981 #include <filesystem/file_time.cpp>
982 #include <filesystem/filename.cpp>
983 #include <filesystem/filename_list.cpp>
984 #include <filesystem/filename_tree.cpp>
985 #include <filesystem/huge_file.cpp>
986 #include <loggers/combo_logger.cpp>
987 #include <loggers/console_logger.cpp>
988 #include <loggers/critical_events.cpp>
989 #include <loggers/file_logger.cpp>
990 #include <loggers/program_wide_logger.cpp>
991 #include <nodes/node.cpp>
992 #include <nodes/packable_tree.cpp>
993 #include <nodes/path.cpp>
994 #include <nodes/tree.cpp>
995 #include <structures/bit_vector.cpp>
996 #include <structures/checksums.cpp>
997 #include <structures/object_packers.cpp>
998 #include <structures/static_memory_gremlin.cpp>
999 #include <structures/string_hasher.cpp>
1000 #include <structures/string_table.cpp>
1001 #include <structures/version_record.cpp>
1002 #include <textual/byte_formatter.cpp>
1003 #include <textual/parser_bits.cpp>
1004 #include <textual/string_manipulation.cpp>
1005 #include <timely/earth_time.cpp>
1006 #include <timely/time_stamp.cpp>
1007 #endif // __BUILD_STATIC_APPLICATION__