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; }
173 class sorting_array : public array<simple_sorter> {};
175 ////////////////////////////////////////////////////////////////////////////
177 class value_tagger : public application_shell
181 virtual ~value_tagger();
182 DEFINE_CLASS_NAME("value_tagger");
184 int print_instructions_and_exit();
186 bool process_tree(const astring &path);
187 // called on each directory hierarchy that we need to process.
189 bool process_file(const astring &path);
190 // examines the file specified to see if it matches our needs.
192 bool parse_define(const astring &scanning, int indy, astring &name,
193 int &value, astring &description, int &num_start, int &num_end);
194 // processes the string in "scanning" to find parentheses surrounding
195 // the "name", "value" and "description". the "description" field may
196 // occupy multiple lines, so all are gathered together to form one
197 // unbroken string. the "num_start" and "num_end" specify where the
198 // numeric value was found, in case it needs to be patched.
201 ini_configurator *_ini; // the configuration for what we'll scan.
202 string_table _dirs; // the list of directories.
203 string_table _dirs_seen; // full list of already processed directories.
204 filename _manifest_filename; // the name of the manifest we'll create.
205 byte_filer _manifest; // the actual file we're building.
206 active_searches _search_list; // tracks our progress in scanning files.
207 int_array _search_ordering;
208 // lists the terms in the order they should be applied. initially this
209 // carries the first pass items, but later will be reset for second pass.
210 int_array _postponed_searches;
211 // lists the searches that must wait until the main search is done.
212 string_table _modified_files; // the list of files that we touched.
215 ////////////////////////////////////////////////////////////////////////////
217 value_tagger::value_tagger()
218 : application_shell(),
224 value_tagger::~value_tagger()
229 int value_tagger::print_instructions_and_exit()
231 LOG(a_sprintf("%s usage:", filename(_global_argv[0]).basename().raw().s()));
235 This utility scans a code base for outcome and filter definitions. It will\n\
236 only scan the header files (*.h) found in the directories specified. The\n\
237 single parameter is expected to be an INI filename that contains the scanning\n\
238 configuration. The INI file should be formatted like this (where the $HOME\n\
239 can be any variable substitution from the environment):");
243 output=$HOME/manifest.txt\n\
250 $HOME/source/lib_src/library/basis\n\
251 $HOME/source/lib_src/library\n\
252 $HOME/source/lib_src/communication/sockets\n\
253 $HOME/source/lib_src/communication\n\
254 $HOME/source/lib_src\n\
255 $HOME/source/app_src\n\
256 $HOME/source/test_src\n\
267 [DEFINE_API_OUTCOME]\n\
269 link=DEFINE_OUTCOME\n\
272 The \"first\" field defines the starting value that should be assigned to\n\
274 The \"increment\" field specifies what to add to a value for the next item.\n\
275 The optional \"no_modify\" flag means that the values should not be auto-\n\
276 incremented; their current value will be used.\n\
277 The optional \"link\" field defines this type of item as using the current\n\
278 values for another type of item. In this case, API_OUTCOME will use the\n\
279 values for OUTCOME to share its integer space, but API_OUTCOME is not auto-\n\
280 incremented even though OUTCOME is. This causes the values for OUTCOME and\n\
281 API_OUTCOME to be checked for uniqueness together, but only OUTCOME will be\n\
282 auto-incremented. Note that only one level of linking is supported currently.\n\
283 The optional \"tag\" can be used to distinguish the entries for a particular\n\
284 search type if needed. This is most helpful for links, so that they can be\n\
285 distinguished from their base type.\n\
292 astring header_string(const astring &build_number)
295 #ifndef GENERATED_VALUES_MANIFEST\n\
296 #define GENERATED_VALUES_MANIFEST\n\
298 // This file contains all outcomes and filters for this build.\n\
300 // Generated for build %s on %s\n\
302 ", build_number.s(), time_stamp::notarize(true).s());
305 astring footer_string(const byte_array &full_config_file)
307 return a_sprintf("\n\
308 // End of definitions.\n\
311 // The following is the full configuration for this build:\n\
319 #endif // outer guard.\n\
320 ", (char *)full_config_file.observe());
323 int value_tagger::execute()
326 if (_global_argc < 2) {
327 return print_instructions_and_exit();
330 log(time_stamp::notarize(true) + "value_tagger started.", basis::ALWAYS_PRINT);
332 astring test_repository = environment::get("FEISTY_MEOW_APEX");
333 if (!test_repository) {
335 There is a problem with a required build precondition. The following\r\n\
336 variables must be set before the build is run:\r\n\
338 FEISTY_MEOW_APEX This should point at the root of the build tree.\r\n\
340 There are also a few variables only required for CLAM-based compilation:\r\n\
342 MAKEFLAGS This should be set to \"-I $FEISTY_MEOW_APEX/clam\".\r\n\
344 Note that on Win32 platforms, these should be set in the System or User\r\n\
345 variables before running a build.\r\n";
347 ::MessageBox(0, to_unicode_temp(msg),
348 to_unicode_temp("Missing Precondition"), MB_ICONWARNING|MB_OK);
350 non_continuable_error(class_name(), func, msg);
353 astring ini_file = _global_argv[1]; // the name of our ini file.
354 _ini = new ini_configurator(ini_file, ini_configurator::RETURN_ONLY);
356 // read the name of the manifest file to create.
357 _manifest_filename = filename(_ini->load("manifest", "output", ""));
358 if (!_manifest_filename.raw().length()) {
359 non_continuable_error(class_name(), ini_file, "The 'output' file entry is missing");
361 _manifest_filename = parser_bits::substitute_env_vars(_manifest_filename);
363 LOG(astring("Sending Manifest to ") + _manifest_filename);
366 filename(_manifest_filename).unlink();
367 // clean out the manifest ahead of time.
369 // read the list of directories to scan for code.
370 string_table temp_dirs;
371 bool read_dirs = _ini->get_section("directories", temp_dirs);
372 if (!read_dirs || !temp_dirs.symbols()) {
373 non_continuable_error(class_name(), ini_file,
374 "The 'directories' section is missing");
376 for (int i = 0; i < temp_dirs.symbols(); i++) {
377 //log(astring("curr is ") + current);
378 filename current = filename(parser_bits::substitute_env_vars(temp_dirs.name(i)));
379 _dirs.add(current, "");
382 LOG(astring("Directories to scan..."));
383 LOG(_dirs.text_form());
385 astring rdir = environment::get("FEISTY_MEOW_APEX");
387 astring parmfile = environment::get("BUILD_PARAMETER_FILE");
388 if (parmfile.t()) fname = parmfile;
389 else fname = rdir + "/build.ini";
391 // read the list of search patterns.
392 string_table searches;
393 bool read_searches = _ini->get_section("searches", searches);
394 if (!read_searches || !searches.symbols()) {
395 non_continuable_error(class_name(), ini_file,
396 "The 'searches' section is missing");
399 LOG("Searching for...");
400 LOG(searches.text_form());
402 // now make sure that we get the configuration for each type of value.
403 for (int i = 0; i < searches.symbols(); i++) {
404 const astring &curr_name = searches.name(i);
406 search_record *check_search = _search_list.find(curr_name);
408 non_continuable_error(class_name(), ini_file,
409 astring("section ") + curr_name + " is being defined twice");
413 // check for whether this section is linked to another or not.
414 astring linked = _ini->load(curr_name, "link", "");
415 search_record *our_link_found = NULL_POINTER;
417 // we found that this should be linked to another item.
418 our_link_found = _search_list.find(linked);
419 if (!our_link_found) {
420 non_continuable_error(class_name(), ini_file,
421 astring("linked section ") + curr_name + " is linked to missing "
422 "section " + linked);
424 search_record new_guy(curr_name, true, our_link_found);
425 _search_list.add(curr_name, new_guy);
427 // this section is a stand-alone section.
428 search_record new_guy(curr_name);
429 _search_list.add(curr_name, new_guy);
433 // find our new search cabinet again so we can use it.
434 search_record *curr_search = _search_list.find(curr_name);
436 non_continuable_error(class_name(), ini_file,
437 astring("section ") + curr_name + " is missing from table "
438 "after addition; logic error");
441 // specify some defaults first.
444 if (!curr_search->is_link()) {
445 // a linked object doesn't get to specify starting value or increment.
446 start = _ini->load(curr_name, "first", start);
447 curr_search->current_value() = start;
448 increm = _ini->load(curr_name, "increment", increm);
449 curr_search->value_increment() = increm;
451 start = curr_search->our_link()->current_value();
452 increm = curr_search->our_link()->value_increment();
455 int no_modify = _ini->load(curr_name, "no_modify", 0);
457 curr_search->_no_modify = true;
460 astring tag = _ini->load(curr_name, "tag", "");
462 curr_search->_tag = tag;
465 a_sprintf to_show("%s: no_modify=%s", curr_name.s(),
466 no_modify? "true" : "false");
468 if (curr_search->is_link()) {
469 // links show who they're hooked to.
470 to_show += astring(" link=") + curr_search->our_link()->_search;
472 // non-links get to show off their start value and increment.
473 to_show += a_sprintf(" start=%d increment=%d", start, increm);
476 to_show += astring(" tag=") + curr_search->_tag;
482 // now gather some info about the build that we can plug into the manifest.
484 byte_filer build_file(fname, "r");
485 if (!build_file.good()) {
486 non_continuable_error(class_name(), build_file.name(),
487 "Could not find the build configuration; is FEISTY_MEOW_APEX set?");
489 byte_array full_config;
490 build_file.read(full_config, 100000); // a good chance to be big enough.
493 //log("got config info:");
494 //log((char *)full_config.observe());
496 astring build_number;
497 ini_configurator temp_ini(fname, configurator::RETURN_ONLY);
498 build_number += temp_ini.load("version", "major", "");
500 build_number += temp_ini.load("version", "minor", "");
502 build_number += temp_ini.load("version", "revision", "");
504 build_number += temp_ini.load("version", "build", "");
505 if (build_number.equal_to("...")) {
506 non_continuable_error(class_name(), build_file.name(),
507 "Could not read the build number; is build parameter file malformed?");
510 //log(astring("got build num: ") + build_number);
512 // now that we know what file to create, write the header blob for it.
513 _manifest.open(_manifest_filename, "wb");
514 if (!_manifest.good()) {
515 non_continuable_error(class_name(), _manifest_filename,
516 "Could not write to the manifest file!");
518 _manifest.write(header_string(build_number));
520 // make sure we have the right ordering for our terms. items that are
521 // non-modify types must come before the modifying types.
522 for (int i = 0; i < _search_list.symbols(); i++) {
523 search_record &curr_reco = _search_list[i];
524 if (curr_reco._no_modify)
525 _search_ordering += i;
527 _postponed_searches += i;
530 // scan across each directory specified for our first pass.
531 LOG("First pass...");
532 for (int i = 0; i < _dirs.symbols(); i++) {
533 if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment.
534 LOG(astring(" Processing: ") + _dirs.name(i));
535 bool ret = process_tree(_dirs.name(i));
537 LOG(astring("Problem encountered in directory ") + _dirs.name(i));
543 LOG("Second pass...");
544 _search_ordering = _postponed_searches; // recharge the list for 2nd pass.
545 _dirs_seen.reset(); // drop any directories we saw before.
546 for (int i = 0; i < _dirs.symbols(); i++) {
547 if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment.
548 LOG(astring(" Processing: ") + _dirs.name(i));
549 bool ret = process_tree(_dirs.name(i));
551 LOG(astring("Problem encountered in directory ") + _dirs.name(i));
556 const astring quote = "\"";
557 const astring comma = ",";
559 // scoot across all the completed searches and dump results.
560 for (int i = 0; i < _search_list.symbols(); i++) {
561 search_record &curr_reco = _search_list[i];
562 const astring &pattern = curr_reco._search;
564 _manifest.write(astring("/* START ") + pattern + "\n");
565 _manifest.write(astring("[") + pattern + "]\n");
567 if (!curr_reco.is_link()) {
568 // scoot across all definitions and print them out.
570 // do the print out in order, as dictated by the sign of the increment.
571 sorting_array sortie;
572 for (int j = 0; j < curr_reco.definitions().symbols(); j++) {
573 const item_record &rec = curr_reco.definitions().get(j);
574 sortie += simple_sorter(j, rec._value);
576 shell_sort(sortie.access(), sortie.length(),
577 negative(curr_reco.value_increment()));
579 for (int j = 0; j < sortie.length(); j++) {
580 int indy = sortie[j]._index;
581 const item_record &rec = curr_reco.definitions().get(indy);
582 astring to_write = " ";
583 if (rec._extra_tag.t()) {
584 to_write += astring("(") + rec._extra_tag + ") ";
586 to_write += quote + rec._name + quote + comma + " ";
587 to_write += quote + a_sprintf("%d", rec._value) + quote + comma + " ";
588 to_write += quote + rec._description + quote + comma + " ";
589 to_write += quote + rec._path + quote;
591 _manifest.write(to_write);
594 // this is just a link.
595 astring to_write = " Linked to search item ";
596 to_write += curr_reco.our_link()->_search;
598 _manifest.write(to_write);
601 _manifest.write(astring("END ") + pattern + " */\n\n");
604 _manifest.write(footer_string(full_config));
606 // show all the modified files.
607 if (_modified_files.symbols()) {
608 const int syms = _modified_files.symbols();
609 LOG("Modified Files:");
610 LOG("===============");
611 for (int i = 0; i < syms; i++) {
612 LOG(_modified_files.name(i));
615 LOG("No files needed modification for generated values.");
619 log(time_stamp::notarize(true) + "value_tagger finished.", ALWAYS_PRINT);
624 #define INBO (indy < scanning.length())
625 // a macro that makes length checking less verbose.
627 // make sure we drop any spaces in between important bits.
628 #define SKIP_SPACES \
629 while (INBO && parser_bits::white_space(scanning[indy])) indy++;
631 // return with a failure but say why it happened.
632 #define FAIL_PARSE(why) { \
633 log(astring("failed to parse the string because ") + why + ".", ALWAYS_PRINT); \
637 bool value_tagger::parse_define(const astring &scanning, int indy,
638 astring &name, int &value, astring &description, int &num_start,
641 // prepare our result objects.
642 name = ""; value = -1; description = ""; num_start = -1; num_end = -1;
646 // look for starting parenthesis.
647 if (!INBO || (scanning[indy] != '(') )
648 FAIL_PARSE("the first parenthesis is missing");
650 indy++; // skip paren.
653 // find the name of the item being defined.
654 while (INBO && (scanning[indy] != ',') ) {
655 name += scanning[indy];
659 indy++; // skip the comma.
664 while (INBO && parser_bits::is_numeric(scanning[indy])) {
665 num_string += scanning[indy];
669 value = num_string.convert(0);
673 if (!INBO || (scanning[indy] != ',') )
674 FAIL_PARSE("the post-value comma is missing");
679 if (!INBO || (scanning[indy] != '"') )
680 FAIL_PARSE("the opening quote for the description is missing");
682 indy++; // now we should be at raw text.
684 // scan through the full description, taking into account that it might
685 // be broken across multiple lines as several quoted bits.
686 bool in_quote = true; // we're inside a quote now.
687 while (INBO && (scanning[indy] != ')') ) {
688 const char curr = scanning[indy];
689 //hmmm: escaped quotes are not currently handled.
690 if (curr == '"') in_quote = !in_quote; // switch quoting state.
691 else if (in_quote) description += curr;
695 return scanning[indy] == ')';
698 bool value_tagger::process_file(const astring &path)
700 byte_filer examining(path, "rb");
701 if (!examining.good()) {
702 log(astring("Error reading file: ") + path, ALWAYS_PRINT);
705 examining.seek(0, byte_filer::FROM_END);
706 int fsize = int(examining.tell());
707 examining.seek(0, byte_filer::FROM_START);
709 astring contents('\0', fsize + 20);
710 int bytes_read = examining.read((abyte *)contents.access(), fsize);
711 // read the file directly into a big astring.
713 contents[bytes_read] = '\0';
714 contents.shrink(); // drop any extra stuff at end.
716 bool modified = false; // set to true if we need to write the file back.
718 // check if the file matches our phrases of interest.
719 bool matched = false;
720 for (int q = 0; q < _search_list.symbols(); q++) {
721 search_record &curr_reco = _search_list[q];
722 if (contents.contains(curr_reco._search)) {
723 //_manifest.write(astring("MATCH-") + curr_pattern + ": " + path + "\n" ); //temp
729 if (!matched) return true;
731 // now we have verified that there's something interesting in this file.
732 // go through to find the interesting bits.
734 // we do this in the search ordering that we established earlier, so we
735 // will tag the values in the proper order.
736 for (int x = 0; x < _search_ordering.length(); x++) {
737 int q = _search_ordering[x]; // get our real index.
738 search_record &curr_reco = _search_list[q];
739 const astring &curr_pattern = curr_reco._search;
740 ///log(astring("now seeking ") + curr_pattern);
741 int start_from = 0; // where searches will start from.
744 // search forward for next match.
745 int indy = contents.find(curr_pattern, start_from);
746 if (negative(indy)) break; // no more matches.
747 start_from = indy + 5; // ensure we'll skip past the last match.
749 // make sure our deadly pattern isn't in front; we don't want to
750 // process the actual definition of the macro in question.
751 //log(a_sprintf("indy=%d [indy-1]=%c [indy-2]=%c", indy, contents[indy-1], contents[indy-2]));
752 if ( (indy > 3) && (contents[indy-1] == ' ')
753 && (contents[indy-2] == 'e') ) {
754 int def_indy = contents.find("#define", indy, true);
755 //log(astring("checking ") + curr_pattern + a_sprintf(": defindy %d, ", def_indy) + path + "\n" );
757 if (non_negative(def_indy) && (absolute_value(indy - def_indy) < 12) ) {
758 // they're close enough that we probably need to skip this
759 // occurrence of our search term.
760 //_manifest.write(astring("DEMATCH-") + curr_pattern + ": had the #define! " + path + "\n" );
765 // make sure we don't include commented lines in consideration.
766 int comm_indy = contents.find("//", indy, true);
767 if (non_negative(comm_indy)) {
768 //log("found a comment marker");
769 // we found a comment before the definition, but we're not sure how
771 if (absolute_value(comm_indy - indy) < LONGEST_SEPARATION) {
772 //log("comment is close enough...");
773 // they could be on the same line... unless lines are longer than
775 bool found_cr = false;
776 for (int q = comm_indy; q < indy; q++) {
777 if (parser_bits::is_eol(contents[q])) {
783 // if there's a comment before the definition and no carriage
784 // returns in between, then this is just a comment.
785 //log(astring("DEMATCH-") + curr_pattern + ": had the comment! " + path + "\n" );
791 // now we are pretty sure this is a righteous definition of an outcome,
792 // and not the definition of the macro itself.
793 int value, num_start, num_end;
794 astring name, description;
795 bool found_it = parse_define(contents, indy + curr_pattern.length(),
796 name, value, description, num_start, num_end);
798 log(astring("there was a problem parsing ") + curr_pattern + " in " + path, ALWAYS_PRINT);
802 // handle the special keyword for changing the value. this is useful
803 // if you want a set of outcomes to start at a specific range.
804 if (name.equal_to(SKIP_VALUE_PHRASE)) {
805 LOG(astring("\tSkipping value for ") + curr_pattern
806 + a_sprintf(" to %d because of request in\n\t", value) + path);
807 curr_reco.current_value() = value;
810 // make sure that the current value is not already in use.
811 if (!curr_reco.out_of_band().member(curr_reco.current_value()))
813 // if we had a match above, we need to adjust the current value.
814 curr_reco.current_value() += curr_reco.value_increment();
816 if (name.equal_to(SKIP_VALUE_PHRASE)) {
817 continue; // keep going now that we vetted the current value.
820 //must catch some conditions here for values:
821 // for incrementing types, we can always just try to use the next value
822 // once we know it wasn't already defined out of band?
823 // for non-incrementing types, we need to ensure we haven't already seen
824 // the thing. do we just always add a value seen to out of band?
825 // for mixed types, the incrementing side needs to not reuse out of band
828 astring other_place; // the other place it was defined.
829 if (curr_reco.out_of_band().member(value) && curr_reco._no_modify) {
830 // this is bad; we have already seen this value elsewhere...
831 for (int x = 0; x < curr_reco.definitions().symbols(); x++) {
832 // see if we can find the previous definition in our list.
833 if (value == curr_reco.definitions()[x]._value)
834 other_place = curr_reco.definitions()[x]._path;
836 non_continuable_error(class_name(), path,
837 a_sprintf("There is a duplicate value here for %s=%d ! "
838 "Also defined in %s.", name.s(), value, other_place.s()));
841 // we care sometimes that this value is different than the next
842 // sequential one we'd assign. if it's a non-modifying type of
843 // search, then we can't change the assigned value anyway--we can
844 // only report the error in re-using a value (above).
845 if (!curr_reco._no_modify) {
846 // check that the defined value matches the next one we'd assign.
847 if (value != curr_reco.current_value()) {
848 // patch the value with the appropriate one we've been tracking.
850 value = curr_reco.current_value();
851 contents.zap(num_start, num_end); // remove old fusty value.
852 contents.insert(num_start, a_sprintf("%d", value));
853 _modified_files.add(path, "");
855 // move the current value up (or down).
856 curr_reco.current_value() += curr_reco.value_increment();
858 // non-modifying type of value here.
862 curr_reco.out_of_band() += value;
863 // we've vetted the value, and now we're definitely using it.
865 // make sure they aren't trying to reuse the name for this item.
867 bool found_name = false; // we don't want to find name already there.
868 if (curr_reco.definitions().find(name)) {
869 rec = *curr_reco.definitions().find(name);
873 // this is bad. this means we are not unique. remove the manifest
874 // file due to this error.
875 _manifest.close(); // close the file since we want to whack it.
876 filename(_manifest_filename).unlink();
877 non_continuable_error(class_name(), path,
878 a_sprintf("There is a duplicate name here (%s)! "
879 "Also defined in %s.", name.s(), rec._path.s()));
882 // record the definition in the appropriate table.
883 curr_reco.definitions().add(name, item_record(name, value,
884 description, path, curr_reco._tag));
886 //log(curr_pattern + a_sprintf(": name=%s value=%d desc=[%s]\n", name.s(), value, description.s()));
892 // rewrite the file, since we modified its contents.
893 bool chmod_result = filename(path).chmod(filename::ALLOW_BOTH,
894 filename::USER_RIGHTS);
898 chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
899 #elif defined(__WIN32__)
900 chmod_value = _S_IREAD | _S_IWRITE;
902 //unknown. let's try unix...
903 chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
905 int chmod_result = chmod(path.s(), chmod_value);
908 log(astring("there was a problem changing permissions on ") + path
909 + "; writing the new version might fail.", ALWAYS_PRINT);
912 byte_filer rewriting(path, "wb");
913 rewriting.write(contents);
920 bool value_tagger::process_tree(const astring &path)
922 directory_tree dir(path, "*.h");
923 if (!dir.good()) return false;
925 dir_tree_iterator *ted = dir.start(directory_tree::prefix);
926 // create our iterator to perform a prefix traversal.
928 filename curr_dir; // the current path the iterator is at.
929 string_array files; // the filenames held at the iterator.
931 while (directory_tree::current(*ted, curr_dir, files)) {
932 // we have a good directory to process.
934 // omit any subdirectories that exactly match directories we've already
935 // scanned. necessary to avoid redoing whole areas.
936 if (!_dirs_seen.find(curr_dir)) {
937 // deal with each matching header file we've found.
938 for (int i = 0; i < files.length(); i++) {
939 bool file_ret = process_file(filename(curr_dir.raw(), files[i]));
941 log(astring("There was an error while processing ") + files[i], ALWAYS_PRINT);
945 _dirs_seen.add(curr_dir, "");
948 // go to the next place.
949 directory_tree::next(*ted);
952 directory_tree::throw_out(ted);
956 HOOPLE_MAIN(value_tagger, )
958 #ifdef __BUILD_STATIC_APPLICATION__
959 // static dependencies found by buildor_gen_deps.sh:
960 #include <application/application_shell.cpp>
961 #include <application/command_line.cpp>
962 #include <application/windoze_helper.cpp>
963 #include <basis/astring.cpp>
964 #include <basis/common_outcomes.cpp>
965 #include <basis/environment.cpp>
966 #include <basis/guards.cpp>
967 #include <basis/mutex.cpp>
968 #include <basis/utf_conversion.cpp>
969 #include <configuration/application_configuration.cpp>
970 #include <configuration/configurator.cpp>
971 #include <configuration/ini_configurator.cpp>
972 #include <configuration/ini_parser.cpp>
973 #include <configuration/table_configurator.cpp>
974 #include <configuration/variable_tokenizer.cpp>
975 #include <filesystem/byte_filer.cpp>
976 #include <filesystem/directory.cpp>
977 #include <filesystem/directory_tree.cpp>
978 #include <filesystem/file_info.cpp>
979 #include <filesystem/file_time.cpp>
980 #include <filesystem/filename.cpp>
981 #include <filesystem/filename_list.cpp>
982 #include <filesystem/filename_tree.cpp>
983 #include <filesystem/huge_file.cpp>
984 #include <loggers/combo_logger.cpp>
985 #include <loggers/console_logger.cpp>
986 #include <loggers/critical_events.cpp>
987 #include <loggers/file_logger.cpp>
988 #include <loggers/program_wide_logger.cpp>
989 #include <nodes/node.cpp>
990 #include <nodes/packable_tree.cpp>
991 #include <nodes/path.cpp>
992 #include <nodes/tree.cpp>
993 #include <structures/bit_vector.cpp>
994 #include <structures/checksums.cpp>
995 #include <structures/object_packers.cpp>
996 #include <structures/static_memory_gremlin.cpp>
997 #include <structures/string_hasher.cpp>
998 #include <structures/string_table.cpp>
999 #include <structures/version_record.cpp>
1000 #include <textual/byte_formatter.cpp>
1001 #include <textual/parser_bits.cpp>
1002 #include <textual/string_manipulation.cpp>
1003 #include <timely/earth_time.cpp>
1004 #include <timely/time_stamp.cpp>
1005 #endif // __BUILD_STATIC_APPLICATION__