version_ini.cpp

Go to the documentation of this file.
00001 #ifndef VERSION_INI_IMPLEMENTATION_FILE
00002 #define VERSION_INI_IMPLEMENTATION_FILE
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : version_ini editing support                                       *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 1995-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 #include "byte_filer.h"
00019 #include "filename.h"
00020 #include "ini_config.h"
00021 #include "version_ini.h"
00022 
00023 #include <basis/function.h>
00024 #include <basis/portable.h>
00025 #include <basis/string_array.h>
00026 #include <basis/version_record.h>
00027 #include <opsystem/directory.h>
00028 
00029 #include <sys/stat.h>
00030 #ifdef __WIN32__
00031   #include <io.h>
00032 #endif
00033 
00034 // the following are all strings that are sought in the version.ini file.
00035 const char *version_ini::VERSION_SECTION = "version";
00036   // the section that version entries are stored under in the INI file.
00037 const char *version_ini::COMPANY_KEY="company";
00038 const char *version_ini::COPYRIGHT_KEY="copyright";
00039 const char *version_ini::LEGAL_INFO_KEY="legal_info";
00040 const char *version_ini::PRODUCT_KEY="product_name";
00041 const char *version_ini::WEB_SITE_KEY="web_site";
00042 
00043 // not used anymore; now matched with the file version's first two digits.
00044 //const version PRODUCT_VERSION(2, 0, 0, 0);
00045   // the current version of the entire product.
00046 
00047 // these are field names in the INI file.
00048 const char *version_ini::MAJOR = "major";
00049 const char *version_ini::MINOR = "minor";
00050 const char *version_ini::REVISION = "revision";
00051 const char *version_ini::BUILD = "build";
00052 const char *version_ini::DESCRIPTION = "description";
00053 const char *version_ini::ROOT = "root";
00054 const char *version_ini::NAME = "name";
00055 const char *version_ini::EXTENSION = "extension";
00056 const char *version_ini::OLE_AUTO = "ole_auto";
00057 
00058 // this is the default version INI file name, if no other is specified.
00059 const char *VERSION_INI_FILE = "/version.ini";
00060 
00061 #undef LOG
00062 #define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger(), t)
00063 
00064 version_ini::version_ini(const istring &path_name)
00065 : _loaded(false),
00066   _path_name(new filename(path_name)),
00067   _ini(new ini_configurator("", ini_configurator::RETURN_ONLY)),
00068   _held_record(new version_record)
00069 {
00070   check_name(*_path_name);
00071   _ini->name(*_path_name);
00072 }
00073 
00074 version_ini::~version_ini()
00075 {
00076   WHACK(_ini);
00077   WHACK(_path_name);
00078   WHACK(_held_record);
00079 }
00080 
00081 bool version_ini::ole_auto_registering()
00082 {
00083   istring extension = _ini->load(VERSION_SECTION, OLE_AUTO, "");
00084   return (extension.lower() != "");
00085 }
00086 
00087 bool version_ini::executable()
00088 {
00089   istring extension = _ini->load(VERSION_SECTION, EXTENSION, "");
00090   if (extension.lower() == istring("exe")) return true;
00091   return false;
00092 }
00093 
00094 bool version_ini::library() { return !executable(); }
00095 
00096 bool version_ini::writable() { return _path_name->is_writable(); }
00097 
00098 void version_ini::check_name(filename &to_examine)
00099 {
00100   // if it's just a directory name, add the file name.
00101   if (to_examine.is_directory()) {
00102     to_examine = istring(to_examine) + VERSION_INI_FILE;
00103     to_examine.canonicalize();
00104   }
00105 
00106   // add the directory explicitly (if it's not there already) or the ini
00107   // writer will get it from the windows directory.
00108   if ( (to_examine.raw()[0] != '.') && (to_examine.dirname().raw() == ".") ) {
00109     to_examine = istring("./") + to_examine;
00110     to_examine.canonicalize();
00111   }
00112 }
00113 
00114 bool version_ini::executable(const istring &path_name_in)
00115 {
00116   filename path_name(path_name_in);
00117   check_name(path_name);
00118   ini_configurator temp_ini(path_name, ini_configurator::RETURN_ONLY);
00119   istring extension = temp_ini.load(VERSION_SECTION, EXTENSION, "");
00120   extension.to_lower();
00121   if (extension == istring("exe")) return true;
00122   return false;
00123 }
00124 
00125 bool version_ini::library(const istring &path_name)
00126 { return !executable(path_name); }
00127 
00128 version version_ini::get_version()
00129 {
00130   if (_loaded) return _held_record->file_version;
00131   get_record();
00132   return _held_record->file_version;
00133 }
00134 
00135 void version_ini::set_version(const version &to_write, bool write_ini)
00136 {
00137   _held_record->file_version = to_write;  // copy the version we're given.
00138 
00139   // set the product version appropriately to the file version.
00140   _held_record->product_version = to_write;
00141   _held_record->product_version.set_component(version::REVISION, "0");
00142   _held_record->product_version.set_component(version::BUILD, "0");
00143 
00144   if (!write_ini) return;  // they don't want a change to the file.
00145   _ini->store(VERSION_SECTION, MAJOR, to_write.get_component(version::MAJOR));
00146   _ini->store(VERSION_SECTION, MINOR, to_write.get_component(version::MINOR));
00147   _ini->store(VERSION_SECTION, REVISION, to_write.get_component(version::REVISION));
00148   _ini->store(VERSION_SECTION, BUILD, to_write.get_component(version::BUILD));
00149 }
00150 
00151 version version_ini::read_version_from_ini()
00152 {
00153   string_array parts;
00154   parts += _ini->load(VERSION_SECTION, MAJOR, "0");
00155   parts += _ini->load(VERSION_SECTION, MINOR, "0");
00156   parts += _ini->load(VERSION_SECTION, REVISION, "0");
00157   parts += _ini->load(VERSION_SECTION, BUILD, "0");
00158   return version(parts);
00159 }
00160 
00161 version_record &version_ini::access_record() { return *_held_record; }
00162 
00163 version_record version_ini::get_record()
00164 {
00165   FUNCDEF("get_record");
00166   if (_loaded) return *_held_record;
00167   version_record to_return;
00168   to_return.description = _ini->load(VERSION_SECTION, DESCRIPTION, "");
00169   to_return.file_version = read_version_from_ini();
00170   to_return.internal_name = _ini->load(VERSION_SECTION, NAME, "");
00171   to_return.original_name = _ini->load(VERSION_SECTION, ROOT, "");
00172   to_return.original_name += ".";
00173 
00174   // the dll type of extension is a hard default.  anything besides the exe
00175   // ending gets mapped to dll.
00176   istring extension = _ini->load(VERSION_SECTION, EXTENSION, "");
00177   extension.to_lower();
00178   if (extension == "dll") {}
00179   else if (extension == "exe") {}
00180   else extension = "dll";
00181   to_return.original_name += extension;
00182 
00183   to_return.product_version = to_return.file_version;
00184   to_return.product_version.set_component(version::REVISION, "0");
00185   to_return.product_version.set_component(version::BUILD, "0");
00186 
00187   to_return.product_name = _ini->load(VERSION_SECTION, PRODUCT_KEY, "");
00188   to_return.company_name = _ini->load(VERSION_SECTION, COMPANY_KEY, "");
00189   to_return.copyright = _ini->load(VERSION_SECTION, COPYRIGHT_KEY, "");
00190   to_return.trademarks = _ini->load(VERSION_SECTION, LEGAL_INFO_KEY, "");
00191   to_return.web_address = _ini->load(VERSION_SECTION, WEB_SITE_KEY, "");
00192 
00193 //this appeared to pooch it when we replaced it here.  it would be right
00194 //the second time, but by then the chance to replace the keyword was gone,
00195 //since it had been replaced the first time.
00199 
00200   *_held_record = to_return;
00201   _loaded = true;
00202   return to_return;
00203 }
00204 
00205 void version_ini::set_record(const version_record &to_write, bool write_ini)
00206 {
00207   *_held_record = to_write;
00208   if (write_ini) {
00209     _ini->store(VERSION_SECTION, DESCRIPTION, to_write.description);
00210     set_version(to_write.file_version, write_ini);
00211     _ini->store(VERSION_SECTION, ROOT, to_write.original_name);
00212     _ini->store(VERSION_SECTION, NAME, to_write.internal_name);
00213   }
00214   _loaded = true;  // we consider this to be the real version now.
00215 }
00216 
00218 
00219 const istring version_rc_template = "\
00220 #ifndef NO_VERSION\n\
00221 #include <winver.h>\n\
00222 #include <__build_version.h>\n\
00223 #include <__build_configuration.h>\n\
00224 #define BI_PLAT_WIN32\n\
00225   // force 32 bit compile.\n\
00226 1 VERSIONINFO LOADONCALL MOVEABLE\n\
00227 FILEVERSION __build_FILE_VERSION_COMMAS\n\
00228 PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS\n\
00229 FILEFLAGSMASK 0\n\
00230 FILEFLAGS VS_FFI_FILEFLAGSMASK\n\
00231 #if defined(BI_PLAT_WIN32)\n\
00232   FILEOS VOS__WINDOWS32\n\
00233 #else\n\
00234   FILEOS VOS__WINDOWS16\n\
00235 #endif\n\
00236 FILETYPE VFT_APP\n\
00237 BEGIN\n\
00238   BLOCK \"StringFileInfo\"\n\
00239   BEGIN\n\
00240     // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0)\n\
00241     BLOCK \"040904b0\"              // Matches VarFileInfo Translation hex value.\n\
00242     BEGIN\n\
00243       VALUE \"CompanyName\", __build_company \"\\000\"\n\
00244 #ifndef _DEBUG\n\
00245       VALUE \"FileDescription\", \"$file_desc\\000\"\n\
00246 #else\n\
00247       VALUE \"FileDescription\", \"$file_desc\\000\"\n\
00248 #endif\n\
00249       VALUE \"FileVersion\", __build_FILE_VERSION \"\\000\" \n\
00250       VALUE \"ProductVersion\", __build_PRODUCT_VERSION \"\\000\" \n\
00251       VALUE \"InternalName\", \"$internal\\000\"\n\
00252       VALUE \"LegalCopyright\", __build_copyright \"\\000\"\n\
00253       VALUE \"LegalTrademarks\", __build_legal_info \"\\000\"\n\
00254       VALUE \"OriginalFilename\", \"$original_name\\000\"\n\
00255       VALUE \"ProductName\", __build_product_name \"\\000\"\n\
00256       $special_ole_flag\n\
00257     END\n\
00258   END\n\
00259 \n\
00260   BLOCK \"VarFileInfo\"\n\
00261   BEGIN\n\
00262     VALUE \"Translation\", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0)\n\
00263   END\n\
00264 END\n\
00265 #endif\n";
00266 
00268 
00269 // replaces every occurrence of the keyword in "tag" with the "replacement".
00270 #define REPLACE(tag, replacement) \
00271   new_version_entry.replace_all(tag, replacement); \
00272 
00273 bool version_ini::write_rc(const version_record &to_write)
00274 {
00275   istring new_version_entry(version_rc_template);
00276 
00277   // $file_ver -> w, x, y, z for version of the file.
00278   REPLACE("$file_ver", to_write.file_version.flex_text_form(version::COMMAS));
00279 
00280   // $prod_ver -> w, x, y, z for version of the product.
00281   REPLACE("$prod_ver", to_write.product_version.flex_text_form
00282       (version::COMMAS));
00283 
00284   // $company -> name of company.
00285   REPLACE("$company", to_write.company_name);
00286 
00287   // $file_desc -> description of file.
00288   istring description_release = to_write.description;
00289   REPLACE("$file_desc", description_release);
00290   istring description_debug = to_write.description
00291       + istring(" -- Debug Version");
00292   REPLACE("$file_desc", description_debug);
00293 
00294   // $file_txt_ver -> file version in form w.x.y.z.
00295   REPLACE("$file_txt_ver", to_write.file_version.flex_text_form(version::DOTS));
00296 
00297   // $internal -> internal name of the library, without extensions?
00298   REPLACE("$internal", to_write.internal_name);
00299 
00300   // $copyright -> copyright held by us.
00301   REPLACE("$copyright", to_write.copyright);
00302 
00303   // $legal_tm -> the legal trademarks that must be included, e.g. windows?
00304   REPLACE("$legal_tm", to_write.trademarks);
00305 
00306   // $original_name -> the file's name before possible renamings.
00307   REPLACE("$original_name", to_write.original_name);
00308 
00309   // $prod_name -> the name of the product that this belongs to.
00310   REPLACE("$prod_name", to_write.product_name);
00311 
00312   // $prod_txt_ver -> product version in form w.x.y.z.
00313   REPLACE("$prod_txt_ver", to_write.product_version
00314       .flex_text_form(version::DOTS, version::MINOR));
00315 
00316   istring special_filler;  // nothing by default.
00317   if (ole_auto_registering())
00318     special_filler = "VALUE \"OLESelfRegister\", \"\\0\"";
00319   REPLACE("$special_ole_flag", special_filler);
00320 
00321   istring root_part = "/";
00322   root_part += _ini->load(VERSION_SECTION, ROOT, "");
00323 
00324   istring rc_filename(istring(_path_name->dirname()) + root_part
00325       + "_version.rc");
00326 
00327   filename(rc_filename).chmod(filename::ALLOW_BOTH, filename::USER_RIGHTS);
00328     // make sure we can write to the file.
00329 
00330   byte_filer rc_file(rc_filename, "w");
00331   if (!rc_file.good()) return false;
00332   rc_file.write((byte *)new_version_entry.s(), new_version_entry.length());
00333   rc_file.close();
00334   return true;
00335 }
00336 
00338 
00339 const istring version_header_template = "\
00340 #ifndef $lib_prefix_VERSION_HEADER\n\
00341 #define $lib_prefix_VERSION_HEADER\n\
00342 \n\
00343 /*****************************************************************************\\\n\
00344 *                                                                             *\n\
00345 *  Name   : Version header for $lib_name\n\
00346 *  Author : Automatically generated by version_stamper                        *\n\
00347 *                                                                             *\n\
00348 \\*****************************************************************************/\n\
00349 \n\
00350 #include <__build_version.h>\n\
00351 #include <__build_configuration.h>\n\
00352 #include <basis/version_checker.h>\n\
00353 #include <basis/version_record.h>\n\
00354 \n\
00355 #ifdef __WIN32__\n\
00356 \n\
00357 // this macro can be used to check that the current version of the\n\
00358 // $lib_name library is the same version as expected.  to use it, check\n\
00359 // whether it returns true or false.  if false, the version is incorrect.\n\
00360 #define CHECK_$lib_prefix() (version_checker(istring(\"$lib_prefix\")\\\n\
00361     + istring(\".dll\"), version(__build_SYSTEM_VERSION),\\\n\
00362       istring(\"Please contact $company_name for the latest DLL and \"\\\n\
00363         \"Executable files ($web_address).\")).good_version())\n\
00364 \n\
00365 #else\n\
00366 \n\
00367 // null checking for embedded or other platforms without versions.\n\
00368 \n\
00369 #define CHECK_$lib_prefix() 1\n\
00370 \n\
00371 #endif //__WIN32__\n\
00372 \n\
00373 #endif\n\
00374 \n";
00375 
00377 
00378 bool version_ini::write_code(const version_record &to_write)
00379 {
00380   istring root_part = _ini->load(VERSION_SECTION, ROOT, "");
00381   istring root = root_part.upper();  // make upper case for naming sake.
00382   istring name = _ini->load(VERSION_SECTION, NAME, "");
00383   // replace the macros here also.
00384   name.replace_all("$product_name", to_write.product_name);
00385 
00386   istring new_version_entry(version_header_template);
00387 
00388 //some of the replacements are no longer needed.
00389 
00390   // $lib_prefix -> the first part of the library's name.
00391   REPLACE("$lib_prefix", root);
00392   REPLACE("$lib_prefix", root);
00393   REPLACE("$lib_prefix", root);
00394   REPLACE("$lib_prefix", root);
00395   REPLACE("$lib_prefix", root);
00396   REPLACE("$lib_prefix", root);
00397   REPLACE("$lib_prefix", root);
00398 
00399   // $lib_name -> the name of the library, as it thinks of itself.
00400   REPLACE("$lib_name", name);
00401   REPLACE("$lib_name", name);
00402   REPLACE("$lib_name", name);
00403 
00404   // $lib_version -> the current version for this library.
00405   REPLACE("$lib_version", to_write.file_version.flex_text_form(version::COMMAS));
00406 
00407   // $company_name -> the company that produces the library.
00408   REPLACE("$company_name", to_write.company_name);
00409 
00410   // $web_address -> the web site for the company.  not actually stored.
00411   REPLACE("$web_address", to_write.web_address);
00412 
00413   istring header_filename(_path_name->dirname().raw() + "/" + root_part
00414       + istring("_version.h"));
00415 
00416   filename(header_filename).chmod(filename::ALLOW_BOTH, filename::USER_RIGHTS);
00417     // make sure we can write to the file.
00418 
00419   byte_filer header(header_filename, "w");
00420   if (!header.good()) return false;
00421   header.write((byte *)new_version_entry.s(), new_version_entry.length());
00422   header.close();
00423   return true;
00424 }
00425 
00426 bool version_ini::write_assembly(const version_record &to_write,
00427     bool do_logging)
00428 {
00429   FUNCDEF("write_assembly");
00430   filename just_dir = _path_name->dirname();
00431 //LOG(istring("dir is set to: ") + just_dir);
00432   directory dir(just_dir);
00433   filename to_patch;
00434 //LOG(istring("dir has: ") + dir.files().text_form());
00435   if (non_negative(dir.files().find("AssemblyInfo.cpp")))
00436     to_patch = just_dir.raw() + "/AssemblyInfo.cpp";
00437   else if (non_negative(dir.files().find("AssemblyInfo.cs")))
00438     to_patch = just_dir.raw() + "/AssemblyInfo.cs";
00439   if (to_patch.raw().t()) {
00440     // we have a filename to work on.
00441     filename(to_patch).chmod(filename::ALLOW_BOTH, filename::USER_RIGHTS);
00442       // make sure we can write to the file.
00443     byte_filer modfile(to_patch, "r+b");
00444     istring contents;
00445     modfile.read(contents, 1000000);  // read any file size up to that.
00446     while (contents[contents.end()] == '\032') {
00447       // erase any stray eof characters that may happen to be present.     
00448       contents.zap(contents.end(), contents.end());
00449     }
00450 //LOG(istring("file contents are: \n") + contents);
00451     int ver_posn = contents.find("AssemblyVersionAttribute", 0);
00452     // try again if that seek failed.
00453     if (ver_posn < 0)
00454       ver_posn = contents.find("AssemblyVersion", 0);
00455     if (ver_posn < 0) return true;  // nothing to modify.
00456 //LOG(istring("found assembly version: ") + to_patch);
00457     int quote_posn = contents.find("\"", ver_posn);
00458     if (quote_posn < 0) return true;  // malformed assembly we will not touch.
00459 //LOG(istring("found quote: ") + to_patch);
00460     int second_quote_posn = contents.find("\"", quote_posn + 1);
00461     if (second_quote_posn < 0) return true;  // more malformage.
00462 //LOG(istring("found quote: ") + to_patch);
00463     istring ver_string = to_write.file_version.flex_text_form(version::DOTS);
00464     contents.zap(quote_posn + 1, second_quote_posn - 1);
00465     contents.insert(quote_posn + 1, ver_string);
00466 //LOG(istring("writing new output file: ") + to_patch);
00467     modfile.seek(0);
00468     modfile.write(contents);
00469     modfile.truncate();  // chop off anything left from previous versions.
00470     if (do_logging) {
00471       // let the people know about this...
00472       filename dirbase = filename(modfile.filename()).dirname().basename();
00473       filename just_base = filename(modfile.filename()).basename();
00474       program_wide_logger().log(istring("    patching: ") + dirbase
00475           + "/" + just_base);
00476     }
00477   }
00478 
00479   return true;
00480 }
00481 
00482 bool version_ini::one_stop_version_stamp(const istring &path,
00483     const istring &source_version, bool do_logging)
00484 {
00485   istring path_name = path;
00487   if (path_name == ".")
00488     path_name = portable::current_directory();
00489 
00490   // load the version record in from the ini file and cache it.
00491   version_ini source(path_name);
00492   source.get_record();
00493 
00494   if (source_version.t()) {
00495     // get the version structure from the passed file.
00496     version_ini main_version(source_version);
00497     version version_to_use = main_version.get_version();
00498 
00499     // stuff the version from the main source into this particular file.
00500     source.set_version(version_to_use, false);
00501 
00502     // stuff the other volatile records from the main version.
00503     version_record main = main_version.get_record();
00504     source.access_record().company_name = main.company_name;
00505     source.access_record().web_address = main.web_address;
00506     source.access_record().copyright = main.copyright;
00507     source.access_record().trademarks = main.trademarks;
00508     source.access_record().product_name = main.product_name;
00509 
00510     source.access_record().internal_name.replace("$product_name",
00511         source.get_record().product_name);
00512   }
00513 
00514   if (do_logging) {
00515     // report the current state.
00516    program_wide_logger().log((source.get_record().internal_name + " version "
00517        + source.get_version().text_form() + ".").s());
00518   }
00519 
00520   version_ini verini(path_name);
00521   verini.set_record(source.get_record(), false);
00522 
00523 //  LOG(isprintf("The file \"%s\" contains this version information:",
00524 //      path_name.s()));
00525 //  LOG(verini.get_record().text_form());
00526 
00527   if (!verini.write_rc(verini.get_record())) {
00528     guards::alert_message(isprintf("Could not write the RC file in \"%s\".",
00529         filename(path_name).basename().raw().s()));
00530     return false;
00531   }
00532 
00533   if (verini.library() && !verini.write_code(verini.get_record())) {
00534     guards::alert_message(istring("Could not write the C++ header file for "
00535         "the directory \"")
00536         + filename(path_name).basename() + istring("\".\n"));
00537     return false;
00538   }
00539 
00540   if (!verini.write_assembly(verini.get_record(), do_logging)) {
00541     guards::alert_message(istring("Could not write the Assembly info file for "
00542         "the directory \"")
00543         + filename(path_name).basename() + istring("\".\n"));
00544     return false;
00545   }
00546 
00547   return true;
00548 }
00549 
00550 #endif //VERSION_INI_IMPLEMENTATION_FILE
00551 

Generated on Tue Aug 19 04:29:48 2008 for HOOPLE Libraries by  doxygen 1.5.1