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 <algorithms/shell_sort.h>
28 #include <application/application_shell.h>
29 #include <application/command_line.h>
30 #include <application/hoople_main.h>
31 #include <application/windoze_helper.h>
32 #include <basis/environment.h>
33 #include <basis/functions.h>
34 #include <basis/utf_conversion.h>
35 #include <configuration/ini_configurator.h>
36 #include <filesystem/byte_filer.h>
37 #include <filesystem/directory_tree.h>
38 #include <filesystem/filename.h>
39 #include <loggers/combo_logger.h>
40 #include <loggers/critical_events.h>
41 #include <loggers/program_wide_logger.h>
42 #include <structures/set.h>
43 #include <structures/string_array.h>
44 #include <structures/string_table.h>
45 #include <timely/time_stamp.h>
46 #include <textual/parser_bits.h>
54 #define LOG(s) EMERGENCY_LOG(program_wide_logger::get(), astring(s))
56 using namespace algorithms;
57 using namespace application;
58 using namespace basis;
59 using namespace configuration;
60 using namespace filesystem;
61 using namespace loggers;
62 using namespace structures;
63 using namespace textual;
64 using namespace timely;
66 const int LONGEST_SEPARATION = 128;
67 // the longest we expect a single line of text to be in definition blocks.
68 // if the definition of an outcome or whatever is farther away than this
69 // many characters from a comment start, we will no longer consider the
70 // line to be commented out. this pretty much will never happen unless it's
71 // intentionally done to break this case.
73 const char *SKIP_VALUE_PHRASE = "SKIP_TO_VALUE";
74 // the special phrase we use to indicate that values should jump to
77 ////////////////////////////////////////////////////////////////////////////
79 // this object records all the data that we gather for the defined items.
87 astring _extra_tag; //!< records special info for links.
89 item_record(const astring &name = astring::empty_string(), int value = 999,
90 const astring &description = astring::empty_string(),
91 const astring &path = astring::empty_string(),
92 const astring &extra_tag = astring::empty_string())
93 : _name(name), _value(value), _description(description), _path(path),
94 _extra_tag(extra_tag) {}
97 ////////////////////////////////////////////////////////////////////////////
102 search_record(const astring &search = astring::empty_string(),
103 bool is_link = false, search_record *link = NIL)
104 : _search(search), _no_modify(false), _is_link(is_link), _our_link(link),
105 _current_value(0), _value_increment(1) {}
107 // these properties are available for both real or linked records.
108 astring _search; // our term to search for in the files.
109 bool _no_modify; // true if values should not be automatically incremented.
110 astring _tag; // extra information attached to this type.
112 bool is_link() const { return _is_link; }
113 // returns true if this object is leeching off another object for data.
115 search_record *our_link() const { return _our_link; }
116 // returns the object that this object is a mere shadow of.
118 symbol_table<item_record> &definitions() {
119 if (is_link()) return _our_link->_definitions;
120 else return _definitions;
123 int ¤t_value() {
124 if (is_link()) return _our_link->_current_value;
125 else return _current_value;
128 int &value_increment() {
129 if (is_link()) return _our_link->_value_increment;
130 else return _value_increment;
133 int_set &out_of_band() {
134 if (is_link()) return _our_link->_out_of_band;
135 else return _out_of_band;
139 bool _is_link; // true if this object links to another.
140 search_record *_our_link; // the search we share for our values.
141 symbol_table<item_record> _definitions;
142 // the definitions that we found in the code.
143 int _current_value; // the next value to use for our term.
144 int _value_increment;
145 // how much to add for each new value, if this is an incrementing search.
146 int_set _out_of_band;
147 // values we've seen that were premature. we always want to honor this
148 // set, if it exists, but there will be nothing in it if the search has
149 // completely standard non-incrementing type. this could be varied by
150 // a non-incrementer linking to a standard incrementer.
153 //! a table of terms that we will search for in the code.
154 class active_searches : public symbol_table<search_record>
157 ////////////////////////////////////////////////////////////////////////////
159 // this class provides us a way to easily sort our items based on value.
161 class simple_sorter {
165 simple_sorter(int index = 0, int value = 0) : _index(index), _value(value) {}
166 bool operator < (const simple_sorter &to_compare) const
167 { return _value < to_compare._value; }
168 bool operator == (const simple_sorter &to_compare) const
169 { return _value == to_compare._value; }
172 class sorting_array : public array<simple_sorter> {};
174 ////////////////////////////////////////////////////////////////////////////
176 class value_tagger : public application_shell
180 virtual ~value_tagger();
181 DEFINE_CLASS_NAME("value_tagger");
183 int print_instructions_and_exit();
185 bool process_tree(const astring &path);
186 // called on each directory hierarchy that we need to process.
188 bool process_file(const astring &path);
189 // examines the file specified to see if it matches our needs.
191 bool parse_define(const astring &scanning, int indy, astring &name,
192 int &value, astring &description, int &num_start, int &num_end);
193 // processes the string in "scanning" to find parentheses surrounding
194 // the "name", "value" and "description". the "description" field may
195 // occupy multiple lines, so all are gathered together to form one
196 // unbroken string. the "num_start" and "num_end" specify where the
197 // numeric value was found, in case it needs to be patched.
200 ini_configurator *_ini; // the configuration for what we'll scan.
201 string_table _dirs; // the list of directories.
202 string_table _dirs_seen; // full list of already processed directories.
203 filename _manifest_filename; // the name of the manifest we'll create.
204 byte_filer _manifest; // the actual file we're building.
205 active_searches _search_list; // tracks our progress in scanning files.
206 int_array _search_ordering;
207 // lists the terms in the order they should be applied. initially this
208 // carries the first pass items, but later will be reset for second pass.
209 int_array _postponed_searches;
210 // lists the searches that must wait until the main search is done.
211 string_table _modified_files; // the list of files that we touched.
214 ////////////////////////////////////////////////////////////////////////////
216 value_tagger::value_tagger()
217 : application_shell(),
223 value_tagger::~value_tagger()
228 int value_tagger::print_instructions_and_exit()
230 LOG(a_sprintf("%s usage:", filename(_global_argv[0]).basename().raw().s()));
234 This utility scans a code base for outcome and filter definitions. It will\n\
235 only scan the header files (*.h) found in the directories specified. The\n\
236 single parameter is expected to be an INI filename that contains the scanning\n\
237 configuration. The INI file should be formatted like this (where the $HOME\n\
238 can be any variable substitution from the environment):");
242 output=$HOME/manifest.txt\n\
249 $HOME/source/lib_src/library/basis\n\
250 $HOME/source/lib_src/library\n\
251 $HOME/source/lib_src/communication/sockets\n\
252 $HOME/source/lib_src/communication\n\
253 $HOME/source/lib_src\n\
254 $HOME/source/app_src\n\
255 $HOME/source/test_src\n\
266 [DEFINE_API_OUTCOME]\n\
268 link=DEFINE_OUTCOME\n\
271 The \"first\" field defines the starting value that should be assigned to\n\
273 The \"increment\" field specifies what to add to a value for the next item.\n\
274 The optional \"no_modify\" flag means that the values should not be auto-\n\
275 incremented; their current value will be used.\n\
276 The optional \"link\" field defines this type of item as using the current\n\
277 values for another type of item. In this case, API_OUTCOME will use the\n\
278 values for OUTCOME to share its integer space, but API_OUTCOME is not auto-\n\
279 incremented even though OUTCOME is. This causes the values for OUTCOME and\n\
280 API_OUTCOME to be checked for uniqueness together, but only OUTCOME will be\n\
281 auto-incremented. Note that only one level of linking is supported currently.\n\
282 The optional \"tag\" can be used to distinguish the entries for a particular\n\
283 search type if needed. This is most helpful for links, so that they can be\n\
284 distinguished from their base type.\n\
291 astring header_string(const astring &build_number)
294 #ifndef GENERATED_VALUES_MANIFEST\n\
295 #define GENERATED_VALUES_MANIFEST\n\
297 // This file contains all outcomes and filters for this build.\n\
299 // Generated for build %s on %s\n\
301 ", build_number.s(), time_stamp::notarize(true).s());
304 astring footer_string(const byte_array &full_config_file)
306 return a_sprintf("\n\
307 // End of definitions.\n\
310 // The following is the full configuration for this build:\n\
318 #endif // outer guard.\n\
319 ", (char *)full_config_file.observe());
322 int value_tagger::execute()
325 if (_global_argc < 2) {
326 return print_instructions_and_exit();
329 log(time_stamp::notarize(true) + "value_tagger started.", basis::ALWAYS_PRINT);
331 astring test_repository = environment::get("FEISTY_MEOW_APEX");
332 if (!test_repository) {
334 There is a problem with a required build precondition. The following\r\n\
335 variables must be set before the build is run:\r\n\
337 FEISTY_MEOW_APEX This should point at the root of the build tree.\r\n\
339 There are also a few variables only required for CLAM-based compilation:\r\n\
341 MAKEFLAGS This should be set to \"-I $FEISTY_MEOW_APEX/clam\".\r\n\
343 Note that on Win32 platforms, these should be set in the System or User\r\n\
344 variables before running a build.\r\n";
346 ::MessageBox(0, to_unicode_temp(msg),
347 to_unicode_temp("Missing Precondition"), MB_ICONWARNING|MB_OK);
349 non_continuable_error(class_name(), func, msg);
352 astring ini_file = _global_argv[1]; // the name of our ini file.
353 _ini = new ini_configurator(ini_file, ini_configurator::RETURN_ONLY);
355 // read the name of the manifest file to create.
356 _manifest_filename = filename(_ini->load("manifest", "output", ""));
357 if (!_manifest_filename.raw().length()) {
358 non_continuable_error(class_name(), ini_file, "The 'output' file entry is missing");
360 _manifest_filename = parser_bits::substitute_env_vars(_manifest_filename);
362 LOG(astring("Sending Manifest to ") + _manifest_filename);
365 filename(_manifest_filename).unlink();
366 // clean out the manifest ahead of time.
368 // read the list of directories to scan for code.
369 string_table temp_dirs;
370 bool read_dirs = _ini->get_section("directories", temp_dirs);
371 if (!read_dirs || !temp_dirs.symbols()) {
372 non_continuable_error(class_name(), ini_file,
373 "The 'directories' section is missing");
375 for (int i = 0; i < temp_dirs.symbols(); i++) {
376 //log(astring("curr is ") + current);
377 filename current = filename(parser_bits::substitute_env_vars(temp_dirs.name(i)));
378 _dirs.add(current, "");
381 LOG(astring("Directories to scan..."));
382 LOG(_dirs.text_form());
384 astring rdir = environment::get("FEISTY_MEOW_APEX");
386 astring parmfile = environment::get("BUILD_PARAMETER_FILE");
387 if (parmfile.t()) fname = parmfile;
388 else fname = rdir + "/build.ini";
390 // read the list of search patterns.
391 string_table searches;
392 bool read_searches = _ini->get_section("searches", searches);
393 if (!read_searches || !searches.symbols()) {
394 non_continuable_error(class_name(), ini_file,
395 "The 'searches' section is missing");
398 LOG("Searching for...");
399 LOG(searches.text_form());
401 // now make sure that we get the configuration for each type of value.
402 for (int i = 0; i < searches.symbols(); i++) {
403 const astring &curr_name = searches.name(i);
405 search_record *check_search = _search_list.find(curr_name);
407 non_continuable_error(class_name(), ini_file,
408 astring("section ") + curr_name + " is being defined twice");
412 // check for whether this section is linked to another or not.
413 astring linked = _ini->load(curr_name, "link", "");
414 search_record *our_link_found = NIL;
416 // we found that this should be linked to another item.
417 our_link_found = _search_list.find(linked);
418 if (!our_link_found) {
419 non_continuable_error(class_name(), ini_file,
420 astring("linked section ") + curr_name + " is linked to missing "
421 "section " + linked);
423 search_record new_guy(curr_name, true, our_link_found);
424 _search_list.add(curr_name, new_guy);
426 // this section is a stand-alone section.
427 search_record new_guy(curr_name);
428 _search_list.add(curr_name, new_guy);
432 // find our new search cabinet again so we can use it.
433 search_record *curr_search = _search_list.find(curr_name);
435 non_continuable_error(class_name(), ini_file,
436 astring("section ") + curr_name + " is missing from table "
437 "after addition; logic error");
440 // specify some defaults first.
443 if (!curr_search->is_link()) {
444 // a linked object doesn't get to specify starting value or increment.
445 start = _ini->load(curr_name, "first", start);
446 curr_search->current_value() = start;
447 increm = _ini->load(curr_name, "increment", increm);
448 curr_search->value_increment() = increm;
450 start = curr_search->our_link()->current_value();
451 increm = curr_search->our_link()->value_increment();
454 int no_modify = _ini->load(curr_name, "no_modify", 0);
456 curr_search->_no_modify = true;
459 astring tag = _ini->load(curr_name, "tag", "");
461 curr_search->_tag = tag;
464 a_sprintf to_show("%s: no_modify=%s", curr_name.s(),
465 no_modify? "true" : "false");
467 if (curr_search->is_link()) {
468 // links show who they're hooked to.
469 to_show += astring(" link=") + curr_search->our_link()->_search;
471 // non-links get to show off their start value and increment.
472 to_show += a_sprintf(" start=%d increment=%d", start, increm);
475 to_show += astring(" tag=") + curr_search->_tag;
481 // now gather some info about the build that we can plug into the manifest.
483 byte_filer build_file(fname, "r");
484 if (!build_file.good()) {
485 non_continuable_error(class_name(), build_file.name(),
486 "Could not find the build configuration; is FEISTY_MEOW_APEX set?");
488 byte_array full_config;
489 build_file.read(full_config, 100000); // a good chance to be big enough.
492 //log("got config info:");
493 //log((char *)full_config.observe());
495 astring build_number;
496 ini_configurator temp_ini(fname, configurator::RETURN_ONLY);
497 build_number += temp_ini.load("version", "major", "");
499 build_number += temp_ini.load("version", "minor", "");
501 build_number += temp_ini.load("version", "revision", "");
503 build_number += temp_ini.load("version", "build", "");
504 if (build_number.equal_to("...")) {
505 non_continuable_error(class_name(), build_file.name(),
506 "Could not read the build number; is build parameter file malformed?");
509 //log(astring("got build num: ") + build_number);
511 // now that we know what file to create, write the header blob for it.
512 _manifest.open(_manifest_filename, "wb");
513 if (!_manifest.good()) {
514 non_continuable_error(class_name(), _manifest_filename,
515 "Could not write to the manifest file!");
517 _manifest.write(header_string(build_number));
519 // make sure we have the right ordering for our terms. items that are
520 // non-modify types must come before the modifying types.
521 for (int i = 0; i < _search_list.symbols(); i++) {
522 search_record &curr_reco = _search_list[i];
523 if (curr_reco._no_modify)
524 _search_ordering += i;
526 _postponed_searches += i;
529 // scan across each directory specified for our first pass.
530 LOG("First pass...");
531 for (int i = 0; i < _dirs.symbols(); i++) {
532 if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment.
533 LOG(astring(" Processing: ") + _dirs.name(i));
534 bool ret = process_tree(_dirs.name(i));
536 LOG(astring("Problem encountered in directory ") + _dirs.name(i));
542 LOG("Second pass...");
543 _search_ordering = _postponed_searches; // recharge the list for 2nd pass.
544 _dirs_seen.reset(); // drop any directories we saw before.
545 for (int i = 0; i < _dirs.symbols(); i++) {
546 if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment.
547 LOG(astring(" Processing: ") + _dirs.name(i));
548 bool ret = process_tree(_dirs.name(i));
550 LOG(astring("Problem encountered in directory ") + _dirs.name(i));
555 const astring quote = "\"";
556 const astring comma = ",";
558 // scoot across all the completed searches and dump results.
559 for (int i = 0; i < _search_list.symbols(); i++) {
560 search_record &curr_reco = _search_list[i];
561 const astring &pattern = curr_reco._search;
563 _manifest.write(astring("/* START ") + pattern + "\n");
564 _manifest.write(astring("[") + pattern + "]\n");
566 if (!curr_reco.is_link()) {
567 // scoot across all definitions and print them out.
569 // do the print out in order, as dictated by the sign of the increment.
570 sorting_array sortie;
571 for (int j = 0; j < curr_reco.definitions().symbols(); j++) {
572 const item_record &rec = curr_reco.definitions().get(j);
573 sortie += simple_sorter(j, rec._value);
575 shell_sort(sortie.access(), sortie.length(),
576 negative(curr_reco.value_increment()));
578 for (int j = 0; j < sortie.length(); j++) {
579 int indy = sortie[j]._index;
580 const item_record &rec = curr_reco.definitions().get(indy);
581 astring to_write = " ";
582 if (rec._extra_tag.t()) {
583 to_write += astring("(") + rec._extra_tag + ") ";
585 to_write += quote + rec._name + quote + comma + " ";
586 to_write += quote + a_sprintf("%d", rec._value) + quote + comma + " ";
587 to_write += quote + rec._description + quote + comma + " ";
588 to_write += quote + rec._path + quote;
590 _manifest.write(to_write);
593 // this is just a link.
594 astring to_write = " Linked to search item ";
595 to_write += curr_reco.our_link()->_search;
597 _manifest.write(to_write);
600 _manifest.write(astring("END ") + pattern + " */\n\n");
603 _manifest.write(footer_string(full_config));
605 // show all the modified files.
606 if (_modified_files.symbols()) {
607 const int syms = _modified_files.symbols();
608 LOG("Modified Files:");
609 LOG("===============");
610 for (int i = 0; i < syms; i++) {
611 LOG(_modified_files.name(i));
614 LOG("No files needed modification for generated values.");
618 log(time_stamp::notarize(true) + "value_tagger finished.", ALWAYS_PRINT);
623 #define INBO (indy < scanning.length())
624 // a macro that makes length checking less verbose.
626 // make sure we drop any spaces in between important bits.
627 #define SKIP_SPACES \
628 while (INBO && parser_bits::white_space(scanning[indy])) indy++;
630 // return with a failure but say why it happened.
631 #define FAIL_PARSE(why) { \
632 log(astring("failed to parse the string because ") + why + ".", ALWAYS_PRINT); \
636 bool value_tagger::parse_define(const astring &scanning, int indy,
637 astring &name, int &value, astring &description, int &num_start,
640 // prepare our result objects.
641 name = ""; value = -1; description = ""; num_start = -1; num_end = -1;
645 // look for starting parenthesis.
646 if (!INBO || (scanning[indy] != '(') )
647 FAIL_PARSE("the first parenthesis is missing");
649 indy++; // skip paren.
652 // find the name of the item being defined.
653 while (INBO && (scanning[indy] != ',') ) {
654 name += scanning[indy];
658 indy++; // skip the comma.
663 while (INBO && parser_bits::is_numeric(scanning[indy])) {
664 num_string += scanning[indy];
668 value = num_string.convert(0);
672 if (!INBO || (scanning[indy] != ',') )
673 FAIL_PARSE("the post-value comma is missing");
678 if (!INBO || (scanning[indy] != '"') )
679 FAIL_PARSE("the opening quote for the description is missing");
681 indy++; // now we should be at raw text.
683 // scan through the full description, taking into account that it might
684 // be broken across multiple lines as several quoted bits.
685 bool in_quote = true; // we're inside a quote now.
686 while (INBO && (scanning[indy] != ')') ) {
687 const char curr = scanning[indy];
688 //hmmm: escaped quotes are not currently handled.
689 if (curr == '"') in_quote = !in_quote; // switch quoting state.
690 else if (in_quote) description += curr;
694 return scanning[indy] == ')';
697 bool value_tagger::process_file(const astring &path)
699 byte_filer examining(path, "rb");
700 if (!examining.good()) {
701 log(astring("Error reading file: ") + path, ALWAYS_PRINT);
704 examining.seek(0, byte_filer::FROM_END);
705 int fsize = int(examining.tell());
706 examining.seek(0, byte_filer::FROM_START);
708 astring contents('\0', fsize + 20);
709 int bytes_read = examining.read((abyte *)contents.access(), fsize);
710 // read the file directly into a big astring.
712 contents[bytes_read] = '\0';
713 contents.shrink(); // drop any extra stuff at end.
715 bool modified = false; // set to true if we need to write the file back.
717 // check if the file matches our phrases of interest.
718 bool matched = false;
719 for (int q = 0; q < _search_list.symbols(); q++) {
720 search_record &curr_reco = _search_list[q];
721 if (contents.contains(curr_reco._search)) {
722 //_manifest.write(astring("MATCH-") + curr_pattern + ": " + path + "\n" ); //temp
728 if (!matched) return true;
730 // now we have verified that there's something interesting in this file.
731 // go through to find the interesting bits.
733 // we do this in the search ordering that we established earlier, so we
734 // will tag the values in the proper order.
735 for (int x = 0; x < _search_ordering.length(); x++) {
736 int q = _search_ordering[x]; // get our real index.
737 search_record &curr_reco = _search_list[q];
738 const astring &curr_pattern = curr_reco._search;
739 ///log(astring("now seeking ") + curr_pattern);
740 int start_from = 0; // where searches will start from.
743 // search forward for next match.
744 int indy = contents.find(curr_pattern, start_from);
745 if (negative(indy)) break; // no more matches.
746 start_from = indy + 5; // ensure we'll skip past the last match.
748 // make sure our deadly pattern isn't in front; we don't want to
749 // process the actual definition of the macro in question.
750 //log(a_sprintf("indy=%d [indy-1]=%c [indy-2]=%c", indy, contents[indy-1], contents[indy-2]));
751 if ( (indy > 3) && (contents[indy-1] == ' ')
752 && (contents[indy-2] == 'e') ) {
753 int def_indy = contents.find("#define", indy, true);
754 //log(astring("checking ") + curr_pattern + a_sprintf(": defindy %d, ", def_indy) + path + "\n" );
756 if (non_negative(def_indy) && (absolute_value(indy - def_indy) < 12) ) {
757 // they're close enough that we probably need to skip this
758 // occurrence of our search term.
759 //_manifest.write(astring("DEMATCH-") + curr_pattern + ": had the #define! " + path + "\n" );
764 // make sure we don't include commented lines in consideration.
765 int comm_indy = contents.find("//", indy, true);
766 if (non_negative(comm_indy)) {
767 //log("found a comment marker");
768 // we found a comment before the definition, but we're not sure how
770 if (absolute_value(comm_indy - indy) < LONGEST_SEPARATION) {
771 //log("comment is close enough...");
772 // they could be on the same line... unless lines are longer than
774 bool found_cr = false;
775 for (int q = comm_indy; q < indy; q++) {
776 if (parser_bits::is_eol(contents[q])) {
782 // if there's a comment before the definition and no carriage
783 // returns in between, then this is just a comment.
784 //log(astring("DEMATCH-") + curr_pattern + ": had the comment! " + path + "\n" );
790 // now we are pretty sure this is a righteous definition of an outcome,
791 // and not the definition of the macro itself.
792 int value, num_start, num_end;
793 astring name, description;
794 bool found_it = parse_define(contents, indy + curr_pattern.length(),
795 name, value, description, num_start, num_end);
797 log(astring("there was a problem parsing ") + curr_pattern + " in " + path, ALWAYS_PRINT);
801 // handle the special keyword for changing the value. this is useful
802 // if you want a set of outcomes to start at a specific range.
803 if (name.equal_to(SKIP_VALUE_PHRASE)) {
804 LOG(astring("\tSkipping value for ") + curr_pattern
805 + a_sprintf(" to %d because of request in\n\t", value) + path);
806 curr_reco.current_value() = value;
809 // make sure that the current value is not already in use.
810 if (!curr_reco.out_of_band().member(curr_reco.current_value()))
812 // if we had a match above, we need to adjust the current value.
813 curr_reco.current_value() += curr_reco.value_increment();
815 if (name.equal_to(SKIP_VALUE_PHRASE)) {
816 continue; // keep going now that we vetted the current value.
819 //must catch some conditions here for values:
820 // for incrementing types, we can always just try to use the next value
821 // once we know it wasn't already defined out of band?
822 // for non-incrementing types, we need to ensure we haven't already seen
823 // the thing. do we just always add a value seen to out of band?
824 // for mixed types, the incrementing side needs to not reuse out of band
827 astring other_place; // the other place it was defined.
828 if (curr_reco.out_of_band().member(value) && curr_reco._no_modify) {
829 // this is bad; we have already seen this value elsewhere...
830 for (int x = 0; x < curr_reco.definitions().symbols(); x++) {
831 // see if we can find the previous definition in our list.
832 if (value == curr_reco.definitions()[x]._value)
833 other_place = curr_reco.definitions()[x]._path;
835 non_continuable_error(class_name(), path,
836 a_sprintf("There is a duplicate value here for %s=%d ! "
837 "Also defined in %s.", name.s(), value, other_place.s()));
840 // we care sometimes that this value is different than the next
841 // sequential one we'd assign. if it's a non-modifying type of
842 // search, then we can't change the assigned value anyway--we can
843 // only report the error in re-using a value (above).
844 if (!curr_reco._no_modify) {
845 // check that the defined value matches the next one we'd assign.
846 if (value != curr_reco.current_value()) {
847 // patch the value with the appropriate one we've been tracking.
849 value = curr_reco.current_value();
850 contents.zap(num_start, num_end); // remove old fusty value.
851 contents.insert(num_start, a_sprintf("%d", value));
852 _modified_files.add(path, "");
854 // move the current value up (or down).
855 curr_reco.current_value() += curr_reco.value_increment();
857 // non-modifying type of value here.
861 curr_reco.out_of_band() += value;
862 // we've vetted the value, and now we're definitely using it.
864 // make sure they aren't trying to reuse the name for this item.
866 bool found_name = false; // we don't want to find name already there.
867 if (curr_reco.definitions().find(name)) {
868 rec = *curr_reco.definitions().find(name);
872 // this is bad. this means we are not unique. remove the manifest
873 // file due to this error.
874 _manifest.close(); // close the file since we want to whack it.
875 filename(_manifest_filename).unlink();
876 non_continuable_error(class_name(), path,
877 a_sprintf("There is a duplicate name here (%s)! "
878 "Also defined in %s.", name.s(), rec._path.s()));
881 // record the definition in the appropriate table.
882 curr_reco.definitions().add(name, item_record(name, value,
883 description, path, curr_reco._tag));
885 //log(curr_pattern + a_sprintf(": name=%s value=%d desc=[%s]\n", name.s(), value, description.s()));
891 // rewrite the file, since we modified its contents.
892 bool chmod_result = filename(path).chmod(filename::ALLOW_BOTH,
893 filename::USER_RIGHTS);
897 chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
898 #elif defined(__WIN32__)
899 chmod_value = _S_IREAD | _S_IWRITE;
901 //unknown. let's try unix...
902 chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
904 int chmod_result = chmod(path.s(), chmod_value);
907 log(astring("there was a problem changing permissions on ") + path
908 + "; writing the new version might fail.", ALWAYS_PRINT);
911 byte_filer rewriting(path, "wb");
912 rewriting.write(contents);
919 bool value_tagger::process_tree(const astring &path)
921 directory_tree dir(path, "*.h");
922 if (!dir.good()) return false;
924 dir_tree_iterator *ted = dir.start(directory_tree::prefix);
925 // create our iterator to perform a prefix traversal.
927 filename curr_dir; // the current path the iterator is at.
928 string_array files; // the filenames held at the iterator.
930 while (directory_tree::current(*ted, curr_dir, files)) {
931 // we have a good directory to process.
933 // omit any subdirectories that exactly match directories we've already
934 // scanned. necessary to avoid redoing whole areas.
935 if (!_dirs_seen.find(curr_dir)) {
936 // deal with each matching header file we've found.
937 for (int i = 0; i < files.length(); i++) {
938 bool file_ret = process_file(filename(curr_dir.raw(), files[i]));
940 log(astring("There was an error while processing ") + files[i], ALWAYS_PRINT);
944 _dirs_seen.add(curr_dir, "");
947 // go to the next place.
948 directory_tree::next(*ted);
951 directory_tree::throw_out(ted);
955 HOOPLE_MAIN(value_tagger, )
957 #ifdef __BUILD_STATIC_APPLICATION__
958 // static dependencies found by buildor_gen_deps.sh:
959 #include <application/application_shell.cpp>
960 #include <application/command_line.cpp>
961 #include <application/windoze_helper.cpp>
962 #include <basis/astring.cpp>
963 #include <basis/common_outcomes.cpp>
964 #include <basis/environment.cpp>
965 #include <basis/guards.cpp>
966 #include <basis/mutex.cpp>
967 #include <basis/utf_conversion.cpp>
968 #include <configuration/application_configuration.cpp>
969 #include <configuration/configurator.cpp>
970 #include <configuration/ini_configurator.cpp>
971 #include <configuration/ini_parser.cpp>
972 #include <configuration/table_configurator.cpp>
973 #include <configuration/variable_tokenizer.cpp>
974 #include <filesystem/byte_filer.cpp>
975 #include <filesystem/directory.cpp>
976 #include <filesystem/directory_tree.cpp>
977 #include <filesystem/file_info.cpp>
978 #include <filesystem/file_time.cpp>
979 #include <filesystem/filename.cpp>
980 #include <filesystem/filename_list.cpp>
981 #include <filesystem/filename_tree.cpp>
982 #include <filesystem/huge_file.cpp>
983 #include <loggers/combo_logger.cpp>
984 #include <loggers/console_logger.cpp>
985 #include <loggers/critical_events.cpp>
986 #include <loggers/file_logger.cpp>
987 #include <loggers/program_wide_logger.cpp>
988 #include <nodes/node.cpp>
989 #include <nodes/packable_tree.cpp>
990 #include <nodes/path.cpp>
991 #include <nodes/tree.cpp>
992 #include <structures/bit_vector.cpp>
993 #include <structures/checksums.cpp>
994 #include <structures/object_packers.cpp>
995 #include <structures/static_memory_gremlin.cpp>
996 #include <structures/string_hasher.cpp>
997 #include <structures/string_table.cpp>
998 #include <structures/version_record.cpp>
999 #include <textual/byte_formatter.cpp>
1000 #include <textual/parser_bits.cpp>
1001 #include <textual/string_manipulation.cpp>
1002 #include <timely/earth_time.cpp>
1003 #include <timely/time_stamp.cpp>
1004 #endif // __BUILD_STATIC_APPLICATION__