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()));
233 This utility scans a code base for outcome and filter definitions. It will\n\
234 only scan the header files (*.h) found in the directories specified. The\n\
235 single parameter is expected to be an INI filename that contains the scanning\n\
236 configuration. The INI file should be formatted like this (where the $HOME\n\
237 can be any variable substitution from the environment):");
241 output=$HOME/manifest.txt\n\
248 $HOME/source/lib_src/library/basis\n\
249 $HOME/source/lib_src/library\n\
250 $HOME/source/lib_src/communication/sockets\n\
251 $HOME/source/lib_src/communication\n\
252 $HOME/source/lib_src\n\
253 $HOME/source/app_src\n\
254 $HOME/source/test_src\n\
265 [DEFINE_API_OUTCOME]\n\
267 link=DEFINE_OUTCOME\n\
270 The \"first\" field defines the starting value that should be assigned to\n\
272 The \"increment\" field specifies what to add to a value for the next item.\n\
273 The optional \"no_modify\" flag means that the values should not be auto-\n\
274 incremented; their current value will be used.\n\
275 The optional \"link\" field defines this type of item as using the current\n\
276 values for another type of item. In this case, API_OUTCOME will use the\n\
277 values for OUTCOME to share its integer space, but API_OUTCOME is not auto-\n\
278 incremented even though OUTCOME is. This causes the values for OUTCOME and\n\
279 API_OUTCOME to be checked for uniqueness together, but only OUTCOME will be\n\
280 auto-incremented. Note that only one level of linking is supported currently.\n\
281 The optional \"tag\" can be used to distinguish the entries for a particular\n\
282 search type if needed. This is most helpful for links, so that they can be\n\
283 distinguished from their base type.\n\
290 astring header_string(const astring &build_number)
293 #ifndef GENERATED_VALUES_MANIFEST\n\
294 #define GENERATED_VALUES_MANIFEST\n\
296 // This file contains all outcomes and filters for this build.\n\
298 // Generated for build %s on %s\n\
300 ", build_number.s(), time_stamp::notarize(true).s());
303 astring footer_string(const byte_array &full_config_file)
305 return a_sprintf("\n\
306 // End of definitions.\n\
309 // The following is the full configuration for this build:\n\
317 #endif // outer guard.\n\
318 ", (char *)full_config_file.observe());
321 int value_tagger::execute()
324 if (_global_argc < 2) {
325 return print_instructions_and_exit();
328 log(time_stamp::notarize(true) + "value_tagger started.", basis::ALWAYS_PRINT);
330 astring test_repository = environment::get("FEISTY_MEOW_DIR");
331 if (!test_repository) {
333 There is a problem with a required build precondition. The following\r\n\
334 variables must be set before the build is run:\r\n\
336 FEISTY_MEOW_DIR This should point at the root of the build tree.\r\n\
338 There are also a few variables only required for CLAM-based compilation:\r\n\
340 MAKEFLAGS This should be set to \"-I $FEISTY_MEOW_DIR/clam\".\r\n\
342 Note that on Win32 platforms, these should be set in the System or User\r\n\
343 variables before running a build.\r\n";
345 ::MessageBox(0, to_unicode_temp(msg),
346 to_unicode_temp("Missing Precondition"), MB_ICONWARNING|MB_OK);
348 non_continuable_error(class_name(), func, msg);
351 astring ini_file = _global_argv[1]; // the name of our ini file.
352 _ini = new ini_configurator(ini_file, ini_configurator::RETURN_ONLY);
354 // read the name of the manifest file to create.
355 _manifest_filename = filename(_ini->load("manifest", "output", ""));
356 if (!_manifest_filename.raw().length()) {
357 non_continuable_error(class_name(), ini_file, "The 'output' file entry is missing");
359 _manifest_filename = parser_bits::substitute_env_vars(_manifest_filename);
361 LOG(astring("Sending Manifest to ") + _manifest_filename);
364 filename(_manifest_filename).unlink();
365 // clean out the manifest ahead of time.
367 // read the list of directories to scan for code.
368 string_table temp_dirs;
369 bool read_dirs = _ini->get_section("directories", temp_dirs);
370 if (!read_dirs || !temp_dirs.symbols()) {
371 non_continuable_error(class_name(), ini_file,
372 "The 'directories' section is missing");
374 for (int i = 0; i < temp_dirs.symbols(); i++) {
375 //log(astring("curr is ") + current);
376 filename current = filename(parser_bits::substitute_env_vars(temp_dirs.name(i)));
377 _dirs.add(current, "");
380 LOG(astring("Directories to scan..."));
381 LOG(_dirs.text_form());
383 astring rdir = environment::get("FEISTY_MEOW_DIR");
385 astring parmfile = environment::get("BUILD_PARAMETER_FILE");
386 if (parmfile.t()) fname = parmfile;
387 else fname = rdir + "/build.ini";
389 // read the list of search patterns.
390 string_table searches;
391 bool read_searches = _ini->get_section("searches", searches);
392 if (!read_searches || !searches.symbols()) {
393 non_continuable_error(class_name(), ini_file,
394 "The 'searches' section is missing");
397 LOG("Searching for...");
398 LOG(searches.text_form());
400 // now make sure that we get the configuration for each type of value.
401 for (int i = 0; i < searches.symbols(); i++) {
402 const astring &curr_name = searches.name(i);
404 search_record *check_search = _search_list.find(curr_name);
406 non_continuable_error(class_name(), ini_file,
407 astring("section ") + curr_name + " is being defined twice");
411 // check for whether this section is linked to another or not.
412 astring linked = _ini->load(curr_name, "link", "");
413 search_record *our_link_found = NIL;
415 // we found that this should be linked to another item.
416 our_link_found = _search_list.find(linked);
417 if (!our_link_found) {
418 non_continuable_error(class_name(), ini_file,
419 astring("linked section ") + curr_name + " is linked to missing "
420 "section " + linked);
422 search_record new_guy(curr_name, true, our_link_found);
423 _search_list.add(curr_name, new_guy);
425 // this section is a stand-alone section.
426 search_record new_guy(curr_name);
427 _search_list.add(curr_name, new_guy);
431 // find our new search cabinet again so we can use it.
432 search_record *curr_search = _search_list.find(curr_name);
434 non_continuable_error(class_name(), ini_file,
435 astring("section ") + curr_name + " is missing from table "
436 "after addition; logic error");
439 // specify some defaults first.
442 if (!curr_search->is_link()) {
443 // a linked object doesn't get to specify starting value or increment.
444 start = _ini->load(curr_name, "first", start);
445 curr_search->current_value() = start;
446 increm = _ini->load(curr_name, "increment", increm);
447 curr_search->value_increment() = increm;
449 start = curr_search->our_link()->current_value();
450 increm = curr_search->our_link()->value_increment();
453 int no_modify = _ini->load(curr_name, "no_modify", 0);
455 curr_search->_no_modify = true;
458 astring tag = _ini->load(curr_name, "tag", "");
460 curr_search->_tag = tag;
463 a_sprintf to_show("%s: no_modify=%s", curr_name.s(),
464 no_modify? "true" : "false");
466 if (curr_search->is_link()) {
467 // links show who they're hooked to.
468 to_show += astring(" link=") + curr_search->our_link()->_search;
470 // non-links get to show off their start value and increment.
471 to_show += a_sprintf(" start=%d increment=%d", start, increm);
474 to_show += astring(" tag=") + curr_search->_tag;
480 // now gather some info about the build that we can plug into the manifest.
482 byte_filer build_file(fname, "r");
483 if (!build_file.good()) {
484 non_continuable_error(class_name(), build_file.filename(),
485 "Could not find the build configuration; is FEISTY_MEOW_DIR set?");
487 byte_array full_config;
488 build_file.read(full_config, 100000); // a good chance to be big enough.
491 //log("got config info:");
492 //log((char *)full_config.observe());
494 astring build_number;
495 ini_configurator temp_ini(fname, configurator::RETURN_ONLY);
496 build_number += temp_ini.load("version", "major", "");
498 build_number += temp_ini.load("version", "minor", "");
500 build_number += temp_ini.load("version", "revision", "");
502 build_number += temp_ini.load("version", "build", "");
503 if (build_number.equal_to("...")) {
504 non_continuable_error(class_name(), build_file.filename(),
505 "Could not read the build number; is build parameter file malformed?");
508 //log(astring("got build num: ") + build_number);
510 // now that we know what file to create, write the header blob for it.
511 _manifest.open(_manifest_filename, "wb");
512 if (!_manifest.good()) {
513 non_continuable_error(class_name(), _manifest_filename,
514 "Could not write to the manifest file!");
516 _manifest.write(header_string(build_number));
518 // make sure we have the right ordering for our terms. items that are
519 // non-modify types must come before the modifying types.
520 for (int i = 0; i < _search_list.symbols(); i++) {
521 search_record &curr_reco = _search_list[i];
522 if (curr_reco._no_modify)
523 _search_ordering += i;
525 _postponed_searches += i;
528 // scan across each directory specified for our first pass.
529 LOG("First pass...");
530 for (int i = 0; i < _dirs.symbols(); i++) {
531 if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment.
532 LOG(astring(" Processing: ") + _dirs.name(i));
533 bool ret = process_tree(_dirs.name(i));
535 LOG(astring("Problem encountered in directory ") + _dirs.name(i));
541 LOG("Second pass...");
542 _search_ordering = _postponed_searches; // recharge the list for 2nd pass.
543 _dirs_seen.reset(); // drop any directories we saw before.
544 for (int i = 0; i < _dirs.symbols(); i++) {
545 if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment.
546 LOG(astring(" Processing: ") + _dirs.name(i));
547 bool ret = process_tree(_dirs.name(i));
549 LOG(astring("Problem encountered in directory ") + _dirs.name(i));
554 const astring quote = "\"";
555 const astring comma = ",";
557 // scoot across all the completed searches and dump results.
558 for (int i = 0; i < _search_list.symbols(); i++) {
559 search_record &curr_reco = _search_list[i];
560 const astring &pattern = curr_reco._search;
562 _manifest.write(astring("/* START ") + pattern + "\n");
563 _manifest.write(astring("[") + pattern + "]\n");
565 if (!curr_reco.is_link()) {
566 // scoot across all definitions and print them out.
568 // do the print out in order, as dictated by the sign of the increment.
569 sorting_array sortie;
570 for (int j = 0; j < curr_reco.definitions().symbols(); j++) {
571 const item_record &rec = curr_reco.definitions().get(j);
572 sortie += simple_sorter(j, rec._value);
574 shell_sort(sortie.access(), sortie.length(),
575 negative(curr_reco.value_increment()));
577 for (int j = 0; j < sortie.length(); j++) {
578 int indy = sortie[j]._index;
579 const item_record &rec = curr_reco.definitions().get(indy);
580 astring to_write = " ";
581 if (rec._extra_tag.t()) {
582 to_write += astring("(") + rec._extra_tag + ") ";
584 to_write += quote + rec._name + quote + comma + " ";
585 to_write += quote + a_sprintf("%d", rec._value) + quote + comma + " ";
586 to_write += quote + rec._description + quote + comma + " ";
587 to_write += quote + rec._path + quote;
589 _manifest.write(to_write);
592 // this is just a link.
593 astring to_write = " Linked to search item ";
594 to_write += curr_reco.our_link()->_search;
596 _manifest.write(to_write);
599 _manifest.write(astring("END ") + pattern + " */\n\n");
602 _manifest.write(footer_string(full_config));
604 // show all the modified files.
605 if (_modified_files.symbols()) {
606 const int syms = _modified_files.symbols();
607 LOG("Modified Files:");
608 LOG("===============");
609 for (int i = 0; i < syms; i++) {
610 LOG(_modified_files.name(i));
613 LOG("No files needed modification for generated values.");
617 log(time_stamp::notarize(true) + "value_tagger finished.", ALWAYS_PRINT);
622 #define INBO (indy < scanning.length())
623 // a macro that makes length checking less verbose.
625 // make sure we drop any spaces in between important bits.
626 #define SKIP_SPACES \
627 while (INBO && parser_bits::white_space(scanning[indy])) indy++;
629 // return with a failure but say why it happened.
630 #define FAIL_PARSE(why) { \
631 log(astring("failed to parse the string because ") + why + ".", ALWAYS_PRINT); \
635 bool value_tagger::parse_define(const astring &scanning, int indy,
636 astring &name, int &value, astring &description, int &num_start,
639 // prepare our result objects.
640 name = ""; value = -1; description = ""; num_start = -1; num_end = -1;
644 // look for starting parenthesis.
645 if (!INBO || (scanning[indy] != '(') )
646 FAIL_PARSE("the first parenthesis is missing");
648 indy++; // skip paren.
651 // find the name of the item being defined.
652 while (INBO && (scanning[indy] != ',') ) {
653 name += scanning[indy];
657 indy++; // skip the comma.
662 while (INBO && parser_bits::is_numeric(scanning[indy])) {
663 num_string += scanning[indy];
667 value = num_string.convert(0);
671 if (!INBO || (scanning[indy] != ',') )
672 FAIL_PARSE("the post-value comma is missing");
677 if (!INBO || (scanning[indy] != '"') )
678 FAIL_PARSE("the opening quote for the description is missing");
680 indy++; // now we should be at raw text.
682 // scan through the full description, taking into account that it might
683 // be broken across multiple lines as several quoted bits.
684 bool in_quote = true; // we're inside a quote now.
685 while (INBO && (scanning[indy] != ')') ) {
686 const char curr = scanning[indy];
687 //hmmm: escaped quotes are not currently handled.
688 if (curr == '"') in_quote = !in_quote; // switch quoting state.
689 else if (in_quote) description += curr;
693 return scanning[indy] == ')';
696 bool value_tagger::process_file(const astring &path)
698 byte_filer examining(path, "rb");
699 if (!examining.good()) {
700 log(astring("Error reading file: ") + path, ALWAYS_PRINT);
703 examining.seek(0, byte_filer::FROM_END);
704 int fsize = int(examining.tell());
705 examining.seek(0, byte_filer::FROM_START);
707 astring contents('\0', fsize + 20);
708 int bytes_read = examining.read((abyte *)contents.access(), fsize);
709 // read the file directly into a big astring.
711 contents[bytes_read] = '\0';
712 contents.shrink(); // drop any extra stuff at end.
714 bool modified = false; // set to true if we need to write the file back.
716 // check if the file matches our phrases of interest.
717 bool matched = false;
718 for (int q = 0; q < _search_list.symbols(); q++) {
719 search_record &curr_reco = _search_list[q];
720 if (contents.contains(curr_reco._search)) {
721 //_manifest.write(astring("MATCH-") + curr_pattern + ": " + path + "\n" ); //temp
727 if (!matched) return true;
729 // now we have verified that there's something interesting in this file.
730 // go through to find the interesting bits.
732 // we do this in the search ordering that we established earlier, so we
733 // will tag the values in the proper order.
734 for (int x = 0; x < _search_ordering.length(); x++) {
735 int q = _search_ordering[x]; // get our real index.
736 search_record &curr_reco = _search_list[q];
737 const astring &curr_pattern = curr_reco._search;
738 ///log(astring("now seeking ") + curr_pattern);
739 int start_from = 0; // where searches will start from.
742 // search forward for next match.
743 int indy = contents.find(curr_pattern, start_from);
744 if (negative(indy)) break; // no more matches.
745 start_from = indy + 5; // ensure we'll skip past the last match.
747 // make sure our deadly pattern isn't in front; we don't want to
748 // process the actual definition of the macro in question.
749 //log(a_sprintf("indy=%d [indy-1]=%c [indy-2]=%c", indy, contents[indy-1], contents[indy-2]));
750 if ( (indy > 3) && (contents[indy-1] == ' ')
751 && (contents[indy-2] == 'e') ) {
752 int def_indy = contents.find("#define", indy, true);
753 //log(astring("checking ") + curr_pattern + a_sprintf(": defindy %d, ", def_indy) + path + "\n" );
755 if (non_negative(def_indy) && (absolute_value(indy - def_indy) < 12) ) {
756 // they're close enough that we probably need to skip this
757 // occurrence of our search term.
758 //_manifest.write(astring("DEMATCH-") + curr_pattern + ": had the #define! " + path + "\n" );
763 // make sure we don't include commented lines in consideration.
764 int comm_indy = contents.find("//", indy, true);
765 if (non_negative(comm_indy)) {
766 //log("found a comment marker");
767 // we found a comment before the definition, but we're not sure how
769 if (absolute_value(comm_indy - indy) < LONGEST_SEPARATION) {
770 //log("comment is close enough...");
771 // they could be on the same line... unless lines are longer than
773 bool found_cr = false;
774 for (int q = comm_indy; q < indy; q++) {
775 if (parser_bits::is_eol(contents[q])) {
781 // if there's a comment before the definition and no carriage
782 // returns in between, then this is just a comment.
783 //log(astring("DEMATCH-") + curr_pattern + ": had the comment! " + path + "\n" );
789 // now we are pretty sure this is a righteous definition of an outcome,
790 // and not the definition of the macro itself.
791 int value, num_start, num_end;
792 astring name, description;
793 bool found_it = parse_define(contents, indy + curr_pattern.length(),
794 name, value, description, num_start, num_end);
796 log(astring("there was a problem parsing ") + curr_pattern + " in " + path, ALWAYS_PRINT);
800 // handle the special keyword for changing the value. this is useful
801 // if you want a set of outcomes to start at a specific range.
802 if (name.equal_to(SKIP_VALUE_PHRASE)) {
803 LOG(astring("\tSkipping value for ") + curr_pattern
804 + a_sprintf(" to %d because of request in\n\t", value) + path);
805 curr_reco.current_value() = value;
808 // make sure that the current value is not already in use.
809 if (!curr_reco.out_of_band().member(curr_reco.current_value()))
811 // if we had a match above, we need to adjust the current value.
812 curr_reco.current_value() += curr_reco.value_increment();
814 if (name.equal_to(SKIP_VALUE_PHRASE)) {
815 continue; // keep going now that we vetted the current value.
818 //must catch some conditions here for values:
819 // for incrementing types, we can always just try to use the next value
820 // once we know it wasn't already defined out of band?
821 // for non-incrementing types, we need to ensure we haven't already seen
822 // the thing. do we just always add a value seen to out of band?
823 // for mixed types, the incrementing side needs to not reuse out of band
826 astring other_place; // the other place it was defined.
827 if (curr_reco.out_of_band().member(value) && curr_reco._no_modify) {
828 // this is bad; we have already seen this value elsewhere...
829 for (int x = 0; x < curr_reco.definitions().symbols(); x++) {
830 // see if we can find the previous definition in our list.
831 if (value == curr_reco.definitions()[x]._value)
832 other_place = curr_reco.definitions()[x]._path;
834 non_continuable_error(class_name(), path,
835 a_sprintf("There is a duplicate value here for %s=%d ! "
836 "Also defined in %s.", name.s(), value, other_place.s()));
839 // we care sometimes that this value is different than the next
840 // sequential one we'd assign. if it's a non-modifying type of
841 // search, then we can't change the assigned value anyway--we can
842 // only report the error in re-using a value (above).
843 if (!curr_reco._no_modify) {
844 // check that the defined value matches the next one we'd assign.
845 if (value != curr_reco.current_value()) {
846 // patch the value with the appropriate one we've been tracking.
848 value = curr_reco.current_value();
849 contents.zap(num_start, num_end); // remove old fusty value.
850 contents.insert(num_start, a_sprintf("%d", value));
851 _modified_files.add(path, "");
853 // move the current value up (or down).
854 curr_reco.current_value() += curr_reco.value_increment();
856 // non-modifying type of value here.
860 curr_reco.out_of_band() += value;
861 // we've vetted the value, and now we're definitely using it.
863 // make sure they aren't trying to reuse the name for this item.
865 bool found_name = false; // we don't want to find name already there.
866 if (curr_reco.definitions().find(name)) {
867 rec = *curr_reco.definitions().find(name);
871 // this is bad. this means we are not unique. remove the manifest
872 // file due to this error.
873 _manifest.close(); // close the file since we want to whack it.
874 filename(_manifest_filename).unlink();
875 non_continuable_error(class_name(), path,
876 a_sprintf("There is a duplicate name here (%s)! "
877 "Also defined in %s.", name.s(), rec._path.s()));
880 // record the definition in the appropriate table.
881 curr_reco.definitions().add(name, item_record(name, value,
882 description, path, curr_reco._tag));
884 //log(curr_pattern + a_sprintf(": name=%s value=%d desc=[%s]\n", name.s(), value, description.s()));
890 // rewrite the file, since we modified its contents.
891 bool chmod_result = filename(path).chmod(filename::ALLOW_BOTH,
892 filename::USER_RIGHTS);
896 chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
897 #elif defined(__WIN32__)
898 chmod_value = _S_IREAD | _S_IWRITE;
900 //unknown. let's try unix...
901 chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
903 int chmod_result = chmod(path.s(), chmod_value);
906 log(astring("there was a problem changing permissions on ") + path
907 + "; writing the new version might fail.", ALWAYS_PRINT);
910 byte_filer rewriting(path, "wb");
911 rewriting.write(contents);
918 bool value_tagger::process_tree(const astring &path)
920 directory_tree dir(path, "*.h");
921 if (!dir.good()) return false;
923 dir_tree_iterator *ted = dir.start(directory_tree::prefix);
924 // create our iterator to perform a prefix traversal.
926 filename curr_dir; // the current path the iterator is at.
927 string_array files; // the filenames held at the iterator.
929 while (directory_tree::current(*ted, curr_dir, files)) {
930 // we have a good directory to process.
932 // omit any subdirectories that exactly match directories we've already
933 // scanned. necessary to avoid redoing whole areas.
934 if (!_dirs_seen.find(curr_dir)) {
935 // deal with each matching header file we've found.
936 for (int i = 0; i < files.length(); i++) {
937 bool file_ret = process_file(filename(curr_dir.raw(), files[i]));
939 log(astring("There was an error while processing ") + files[i], ALWAYS_PRINT);
943 _dirs_seen.add(curr_dir, "");
946 // go to the next place.
947 directory_tree::next(*ted);
950 directory_tree::throw_out(ted);
954 HOOPLE_MAIN(value_tagger, )
956 #ifdef __BUILD_STATIC_APPLICATION__
957 // static dependencies found by buildor_gen_deps.sh:
958 #include <application/application_shell.cpp>
959 #include <application/command_line.cpp>
960 #include <basis/astring.cpp>
961 #include <basis/common_outcomes.cpp>
962 #include <basis/environment.cpp>
963 #include <basis/guards.cpp>
964 #include <basis/mutex.cpp>
965 #include <basis/utf_conversion.cpp>
966 #include <configuration/application_configuration.cpp>
967 #include <configuration/configurator.cpp>
968 #include <configuration/ini_configurator.cpp>
969 #include <configuration/ini_parser.cpp>
970 #include <configuration/table_configurator.cpp>
971 #include <configuration/variable_tokenizer.cpp>
972 #include <filesystem/byte_filer.cpp>
973 #include <filesystem/directory.cpp>
974 #include <filesystem/directory_tree.cpp>
975 #include <filesystem/file_info.cpp>
976 #include <filesystem/filename.cpp>
977 #include <filesystem/filename_list.cpp>
978 #include <filesystem/filename_tree.cpp>
979 #include <filesystem/file_time.cpp>
980 #include <filesystem/huge_file.cpp>
981 #include <loggers/combo_logger.cpp>
982 #include <loggers/console_logger.cpp>
983 #include <loggers/critical_events.cpp>
984 #include <loggers/file_logger.cpp>
985 #include <loggers/program_wide_logger.cpp>
986 #include <nodes/node.cpp>
987 #include <nodes/packable_tree.cpp>
988 #include <nodes/path.cpp>
989 #include <nodes/tree.cpp>
990 #include <structures/bit_vector.cpp>
991 #include <structures/checksums.cpp>
992 #include <structures/object_packers.cpp>
993 #include <structures/static_memory_gremlin.cpp>
994 #include <structures/string_hasher.cpp>
995 #include <structures/string_table.cpp>
996 #include <structures/version_record.cpp>
997 #include <textual/byte_formatter.cpp>
998 #include <textual/parser_bits.cpp>
999 #include <textual/string_manipulation.cpp>
1000 #include <timely/earth_time.cpp>
1001 #include <timely/time_stamp.cpp>
1002 #endif // __BUILD_STATIC_APPLICATION__