bundle_creator.cpp

Go to the documentation of this file.
00001 
00002 //hmmm: anything related to _stub_size should be kept, but that is where
00003 //      we need a redundant search mechanism that can't be fooled so easily
00004 //      by modifying exe; make a pattern that will be found and is the first
00005 //      place to start looking for manifest.
00006 
00007 /*****************************************************************************\
00008 *                                                                             *
00009 *  Name   : bundle_creator                                                    *
00010 *  Author : Chris Koeritz                                                     *
00011 *                                                                             *
00012 *******************************************************************************
00013 * Copyright (c) 2006-$now By Author.  This program is free software; you can  *
00014 * redistribute it and/or modify it under the terms of the GNU General Public  *
00015 * License as published by the Free Software Foundation; either version 2 of   *
00016 * the License or (at your option) any later version.  This is online at:      *
00017 *     http://www.fsf.org/copyleft/gpl.html                                    *
00018 * Please send any updates to: fred@gruntose.com                               *
00019 \*****************************************************************************/
00020 
00021 #include "common_bundle.h"
00022 
00023 #include <basis/array.cpp>
00024 #include <basis/byte_array.h>
00025 #include <basis/portable.h>
00026 #include <data_struct/string_table.h>
00027 #include <opsystem/application_shell.h>
00028 #include <opsystem/byte_filer.h>
00029 #include <opsystem/command_line.h>
00030 #include <loggers/console_logger.h>
00031 #include <opsystem/directory.h>
00032 #include <opsystem/filename.h>
00033 #include <loggers/file_logger.h>
00034 #include <opsystem/filetime.h>
00035 #include <opsystem/ini_config.h>
00036 #include <opsystem/path_configuration.h>
00037 #include <data_struct/static_memory_gremlin.h>
00038 #include <textual/byte_format.h>
00039 #include <textual/list_parsing.h>
00040 #include <textual/parser_bits.h>
00041 #include <textual/tokenizer.h>
00042 
00043 #include <stdio.h>
00044 #include <sys/stat.h>
00045 #include <zlib.h>
00046 #ifdef __WIN32__
00047   #include <io.h>
00048 #endif
00049 
00050 const int CHUNKING_SIZE = 256 * KILOBYTE;
00051   // we'll read this big a chunk from a source file at a time.
00052 
00053 #define BASE_LOG(to_print) program_wide_logger().log(to_print)
00054 #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger(), to_print)
00055 
00056 //#define DEBUG_BUNDLER
00057   // uncomment for noisy debugging version.
00058 
00059 // returns the "retval" and mentions that this is a failure at "where".
00060 #define FAIL_RETURN(retval, where) { \
00061   LOG(istring("failure in ") + where + isprintf(", exit=%d", retval)); \
00062   return retval; \
00063 }
00064 
00066 
00067 inline bool true_value(const istring &value)
00068 { return (value != "0") && (value != "false"); }
00069 
00071 
00072 // this structure overrides the manifest_chunk by providing a source string.
00073 
00074 struct bundled_chunk : manifest_chunk
00075 {
00076   istring _source;  
00077   virtual ~bundled_chunk() {}
00078 };
00079 
00081 
00082 // main bundler class.
00083 
00084 class bundle_creator : public application_shell
00085 {
00086 public:
00087   bundle_creator()
00088       : application_shell(static_class_name()),
00089         _app_name(filename(__argv[0]).basename()),
00090         _bundle(NIL), _stub_size(0), _keyword() {}
00091 
00092   virtual ~bundle_creator() {
00093     WHACK(_bundle);
00094   }
00095 
00096   IMPLEMENT_CLASS_NAME("bundle_creator");
00097   virtual int execute();
00098   int print_instructions();
00099 
00100   int open_output_file();
00102 
00104   int read_manifest();
00106 
00108   int write_stub_and_toc();
00110 
00111   int bundle_sources();
00113 
00114   int finalize_file();
00116 
00117   int write_offset();
00119 
00121   int patch_recursive_target(const istring &source, const istring &target,
00122           int manifest_index);
00124 
00128   int recurse_into_dir(const istring &source, const istring &target,
00129           int manifest_index);
00131 
00132   int patch_wildcard_target(const istring &source, const istring &target,
00133           int manifest_index);
00135 
00137   int add_files_here(directory &dirndl, const istring &source,
00138           const istring &target, int manifest_index);
00140 
00141   bool get_file_size(const istring &file, int &size, byte_array &timestamp);
00143 
00144 private:
00145   istring _app_name;  
00146   istring _output_file;  
00147   istring _manifest_file;  
00148   array<bundled_chunk> _manifest_list;  
00149   byte_filer *_bundle;  
00150   int _stub_size;  
00151   istring _keyword;  // set if we were given a keyword on cmd line.
00152 };
00153 
00155 
00156 int bundle_creator::print_instructions()
00157 {
00158   BASE_LOG(isprintf("\
00159 %s: This program needs two parameters on the command line.\n\
00160 The -o flag must point at the bundled output file to create.  The -m flag\n\
00161 must point at a valid manifest file that defines what will be packed into\n\
00162 the output file.  See the example manifest file for more information on\n\
00163 the required file format.\n\
00164 ", _app_name.s()));
00165   return 4;
00166 }
00167 
00168 int bundle_creator::execute()
00169 {
00170   FUNCDEF("execute");
00171 
00172   BASE_LOG(istring("starting file bundling at ") + timestamp(false, true));
00173 
00174   command_line cmds(__argc, __argv);
00175   istring temp;
00176   if (cmds.get_value('?', temp)) return print_instructions();
00177   if (cmds.get_value("?", temp)) return print_instructions();
00178   if (!cmds.get_value('o', _output_file)) return print_instructions();
00179   if (!cmds.get_value('m', _manifest_file)) return print_instructions();
00180 
00181   if (filename(_output_file).exists()) {
00182     BASE_LOG(isprintf("\
00183 %s: The output file already exists.  Please move it out of\n\
00184 the way; this program will not overwrite existing files.\n",
00185 _app_name.s()));
00186     return 3;
00187   }
00188 
00189   if (!filename(_manifest_file).exists()) {
00190     BASE_LOG(isprintf("\
00191 %s: The manifest file does not exist.  This program cannot do anything\n\
00192 without a valid packing manifest.\n", _app_name.s()));
00193     return 2;
00194   }
00195 
00196   // make sure we snag any keyword that was passed on the command line.
00197   cmds.get_value("keyword", _keyword);
00198 
00199   // first step is to provide some built-in variables that can be used to
00200   // make the manifests less platform specific.  this doesn't really help
00201   // if you bundle it on linux and try to run it on windows.  but either
00202   // platform's resources can easily be made into a bundle with the same
00203   // packing manifest.
00204 #ifndef __WIN32__
00205   portable::set_environ("EXE_END", "");  // executable file ending.
00206   portable::set_environ("DLL_START", "lib");  // dll file prefix.
00207   portable::set_environ("DLL_END", ".so");  // dll file ending.
00208 #else
00209   portable::set_environ("EXE_END", ".exe");
00210   portable::set_environ("DLL_START", "");
00211   portable::set_environ("DLL_END", ".dll");
00212 #endif
00213 
00214   int ret = 0;
00215   if ( (ret = read_manifest()) ) FAIL_RETURN(ret, "reading manifest");
00216     // read manifest to build list of what's what.
00217   if ( (ret = open_output_file()) ) FAIL_RETURN(ret, "opening output file");
00218     // open up our output file for the bundled chunks.
00219   if ( (ret = write_stub_and_toc()) ) FAIL_RETURN(ret, "writing stub and TOC");
00220     // writes the stub unpacker application and the table of contents to the 
00221     // output file.
00222   if ( (ret = bundle_sources()) ) FAIL_RETURN(ret, "bundling source files");
00223     // stuff all the source files into the output bundle.
00224   if ( (ret = finalize_file()) ) FAIL_RETURN(ret, "finalizing file");
00225     // finishes with the file and closes it up.
00226   if ( (ret = write_offset()) ) FAIL_RETURN(ret, "writing offset");
00227     // stores the offset of the TOC into the output file in a special location
00228     // that is delineated by a known keyword (muftiloc) and which should only
00229     // exist in the file in one location.
00230 
00231   return 0;
00232 }
00233 
00234 int bundle_creator::open_output_file()
00235 {
00236   FUNCDEF("open_output_file");
00237   _bundle = new byte_filer(_output_file, "wb");
00238   if (!_bundle->good()) {
00239     LOG(istring("failed to open the output file: ") + _output_file);
00240     return 65;
00241   }
00242   return 0;
00243 }
00244 
00245 bool bundle_creator::get_file_size(const istring &infile, int &size,
00246     byte_array &time_stamp)
00247 {
00248   FUNCDEF("get_file_size");
00249   time_stamp.reset();
00250   // access the source file to get its size.
00251   byte_filer source_file(infile, "rb");
00252   if (!source_file.good()) {
00253     LOG(istring("could not access the file for size check: ") + infile);
00254     return false;
00255   }
00256   size = int(source_file.length());
00257   file_time tim(infile);
00258   tim.pack(time_stamp);
00259   return true;
00260 }
00261 
00262 int bundle_creator::add_files_here(directory &dirndl, const istring &source,
00263     const istring &target, int manifest_index)
00264 {
00265   FUNCDEF("add_files_here");
00266   for (int i = 0; i < dirndl.files().length(); i++) {
00267     istring s = dirndl.files()[i];
00268 //LOG(istring("file is: ") + s);
00269     bundled_chunk new_guy;
00270     new_guy._source = source + "/" + s;  // the original full path to it.
00271     new_guy._target = target + "/" + s;
00272     new_guy._keywords = _manifest_list[manifest_index]._keywords;
00273 
00274 //LOG(isprintf("adding: source=%s targ=%s", new_guy._source.s(), new_guy._target.s()));
00275     bool okaysize = get_file_size(new_guy._source, new_guy._size,
00276         new_guy._timestamp);
00277     if (!okaysize || (new_guy._size < 0) ) {
00278       LOG(istring("failed to get file size for ") + new_guy._source);
00279       return 75;
00280     }
00281 
00282     _manifest_list.insert(manifest_index + 1, 1);
00283     _manifest_list[manifest_index + 1] = new_guy;
00284   }
00285   return 0;
00286 }
00287 
00288 int bundle_creator::recurse_into_dir(const istring &source,
00289     const istring &target, int manifest_index)
00290 {
00291   FUNCDEF("recurse_into_dir");
00292 //LOG(istring("src=") + source + " dest=" + target);
00293 
00294   string_array dirs;  // culled from the directory listing.
00295   {
00296     // don't pay for the directory object on the recursive invocation stack;
00297     // just have what we need on the stack (the directory list).
00298     directory dirndl(source);
00299 //check dir for goodness!
00300     int ret = add_files_here(dirndl, source, target, manifest_index);
00301       // add in just the files that were found.
00302     if (ret != 0) {
00303       // this is a failure, but the function complains about it already.
00304       return 75;
00305     }
00306     dirs = dirndl.directories();
00307   }
00308 
00309 //LOG("now scanning directories...");
00310 
00311   // now scan across the directories we found.
00312   for (int i = 0; i < dirs.length(); i++) {
00313     istring s = dirs[i];
00314 //LOG(istring("curr dir is ") + s);
00315     int ret = recurse_into_dir(source + "/" + s, target + "/"
00316         + s, manifest_index);
00317     if (ret != 0) return ret;  // bail out.
00318   }
00319 
00320   return 0;
00321 }
00322 
00323 int bundle_creator::patch_recursive_target(const istring &source,
00324     const istring &target, int manifest_index)
00325 {
00326   FUNCDEF("patch_recursive_target");
00327 //LOG(istring("patch recurs src=") + source + " targ=" + target);
00328   return recurse_into_dir(source, target, manifest_index);
00329 }
00330 
00331 int bundle_creator::patch_wildcard_target(const istring &source,
00332     const istring &target, int manifest_index)
00333 {
00334   FUNCDEF("patch_wildcard_target");
00335   // find the last slash.  the rest is our wildcard component.
00336   int src_end = source.end();
00337   int slash_indy = source.find('/', src_end, true);
00338   istring real_source = source.substring(0, slash_indy - 1);
00339   istring wild_pat = source.substring(slash_indy + 1, src_end);
00340 //BASE_LOG(istring("got src=") + real_source + " wildpat=" + wild_pat);
00341 
00342   directory dirndl(real_source, wild_pat.s());
00343 //check dir for goodness!
00344   int ret = add_files_here(dirndl, real_source, target, manifest_index);
00345   if (ret != 0) {
00346     // this is a failure, but the function complains about it already.
00347     return 75;
00348   }
00349 
00350   return 0;
00351 }
00352 
00353 int bundle_creator::read_manifest()
00354 {
00355   FUNCDEF("read_manifest");
00356   ini_configurator ini(_manifest_file, configurator::RETURN_ONLY);
00357   string_table toc;
00358   bool worked = ini.get_section("toc", toc);
00359   if (!worked) {
00360     LOG(istring("failed to read TOC section in manifest:\n") + _manifest_file
00361         + "\ndoes that file exist?");
00362     return 65;
00363   }
00364 
00365 //hmmm: make a class member.
00366   file_logger noisy_logfile(path_configuration::make_logfile_name
00367       ("bundle_creator_activity.log"));
00368   noisy_logfile.log(istring('-', 76));
00369   noisy_logfile.log(istring("Bundling starts at ") + timestamp(false, true));
00370 
00371   // add enough items in the list for our number of sections.
00372   _manifest_list.insert(0, toc.symbols());
00373   istring value;  // temporary string used below.
00374   int final_return = 0;  // if non-zero, an error occurred.
00375 
00376 #define BAIL(retval) \
00377   final_return = retval; \
00378   toc.zap_index(i); \
00379   _manifest_list.zap(i, i); \
00380   i--; \
00381   continue
00382 
00383   for (int i = 0; i < toc.symbols(); i++) {
00384     // read all the info in this section and store it into our list.
00385     istring section_name = toc.name(i);
00386     section_name.strip_spaces(istring::FROM_FRONT);
00387     if (section_name[0] == '#') {
00388 //hmmm: this looks a bit familiar from bail macro above.  abstract out?
00389       toc.zap_index(i);
00390       _manifest_list.zap(i, i);
00391       i--;
00392       continue;  // skip comments.
00393     }
00394 
00395     // check for any keywords on the section.  these are still needed for
00396     // variables, which otherwise would skip the rest of the field checks.
00397     if (ini.get(section_name, "keyword", value)) {
00399       string_array keys;
00400       bool worked = list_parsing::parse_csv_line(value, keys);
00401       if (!worked) {
00402         LOG(istring("failed to parse keywords for section ")
00403             + section_name + " in " + _manifest_file);
00404         BAIL(82);
00405       }
00407       _manifest_list[i]._keywords = keys;
00408       istring dumped;
00409       list_parsing::create_csv_line(_manifest_list[i]._keywords, dumped);
00410       noisy_logfile.log(section_name + " keywords: " + dumped);
00411     }
00412 
00413     if (ini.get(section_name, "variable", value)) {
00414       // this is a variable assignment.  it is the only thing we care about
00415       // for this section, so the rest is ignored.
00416       tokenizer zohre;
00417       zohre.parse(value);
00418       if (zohre.symbols() < 1) {
00419         LOG(istring("failed to parse a variable statement from ") + value);
00420         BAIL(37);
00421       }
00422       _manifest_list[i]._flags = SET_VARIABLE;  // not orred, just this.
00423       // set the two parts of our variable.
00424       _manifest_list[i]._target = zohre.table().name(0);
00425       _manifest_list[i]._parms = zohre.table()[0];
00426       BASE_LOG(istring("will set ") + _manifest_list[i]._target + " = "
00427           + _manifest_list[i]._parms);
00428 //hmmm: make this optional in the future.
00429       portable::set_environ(_manifest_list[i]._target,
00430           parser_bits::substitute_env_vars(_manifest_list[i]._parms));
00431       continue;
00432     }
00433 
00434     if (!ini.get(section_name, "source", _manifest_list[i]._source)) {
00435       // check whether they told us not to pack and it's executable.
00436       bool okay_to_omit_source = false;
00437       istring value2;
00438       if (ini.get(section_name, "no_pack", value)
00439           && ini.get(section_name, "exec_target", value2) ) {
00440         if (true_value(value) && true_value(value2)) {
00441           // this type of section doesn't need source declared.
00442           okay_to_omit_source = true;
00443         }
00444       }
00445       if (!okay_to_omit_source) {
00446         LOG(istring("failed to read the source entry for section ")
00447             + section_name + " in " + _manifest_file);
00448         BAIL(67);
00449       }
00450     }
00451     // fix meshugener backslashes so we can count on the slash direction.
00452     _manifest_list[i]._source.replace_all('\\', '/');
00453 
00454     if (!ini.get(section_name, "target", _manifest_list[i]._target)) {
00455       // check whether they told us not to pack and it's executable.
00456       bool okay_to_omit_target = false;
00457       istring value2;
00458       if (ini.get(section_name, "no_pack", value)
00459           && ini.get(section_name, "exec_source", value2) ) {
00460         if (true_value(value) && true_value(value2)) {
00461           // this type of section doesn't need target declared.
00462           okay_to_omit_target = true;
00463         }
00464       }
00465       if (!okay_to_omit_target) {
00466         LOG(istring("failed to read the target entry for section ")
00467             + section_name + " in " + _manifest_file);
00468         BAIL(68);
00469       }
00470     }
00471     // fix backslashes in target also.
00472     _manifest_list[i]._target.replace_all('\\', '/');
00473 
00474     // capture any parameters they have specified for exec or other options.
00475     if (ini.get(section_name, "parms", value)) {
00476       _manifest_list[i]._parms = value;
00477 #ifdef DEBUG_BUNDLER
00478       BASE_LOG(istring("got parms for ") + section_name + " as: " + value);
00479 #endif
00480       if (value[0] != '"') {
00481         // repair the string if we're running on windows.
00482         _manifest_list[i]._parms = istring("\"") + value + "\"";
00483       }
00484       noisy_logfile.log(section_name + " parms: " + _manifest_list[i]._parms);
00485     }
00486 
00487     // check for the ignore errors flag.
00488     if (ini.get(section_name, "error_okay", value)) {
00489       if (true_value(value))
00490         _manifest_list[i]._flags |= IGNORE_ERRORS;
00491     }
00492 
00493     // see if they are saying not to overwrite the target file.
00494     if (ini.get(section_name, "no_replace", value)) {
00495       if (true_value(value))
00496         _manifest_list[i]._flags |= NO_OVERWRITE;
00497     }
00498 
00499     // see if they are saying not to overwrite the target file.
00500     if (ini.get(section_name, "quiet", value)) {
00501       if (true_value(value))
00502         _manifest_list[i]._flags |= QUIET_FAILURE;
00503     }
00504 
00505     // look for our recursion flag.
00506     if (ini.get(section_name, "recurse", value)) {
00507       if (true_value(value))
00508         _manifest_list[i]._flags |= RECURSIVE_SRC;
00509     } else {
00510       // the options here are only appropriate when the target is NOT set to
00511       // be recursive.
00512 
00513       if (ini.get(section_name, "no_pack", value)) {
00514         // allow either side to not be required if this is an executable.
00515         if (true_value(value))
00516           _manifest_list[i]._flags |= OMIT_PACKING;
00517       }
00518 
00519       // check if they have specified a source side executable.
00520       if (ini.get(section_name, "exec_source", value)) {
00521         if (true_value(value)) {
00522           _manifest_list[i]._flags |= SOURCE_EXECUTE;
00523         }
00524       } else {
00525         // check if they have specified a target side executable.  this is
00526         // mutually exclusive with a source side exec.
00527         if (ini.get(section_name, "exec_target", value)) {
00528           if (true_value(value))
00529             _manifest_list[i]._flags |= TARGET_EXECUTE;
00530         }
00531       }
00532     }
00533 
00534     // replace environment variables in the source now...
00535     _manifest_list[i]._source = parser_bits::substitute_env_vars
00536         (_manifest_list[i]._source, false);
00537 
00538     // look for wildcards in the source.
00539     int indy = _manifest_list[i]._source.find("*");
00540 
00541     // see if they specified a keyword on the command line and if this matches.
00542     // if not we need to abandon this item.
00543     if (!!_keyword && !_manifest_list[i]._keywords.member(_keyword)) {
00544       // their keyword choice didn't match what we were told to use.
00545       noisy_logfile.log(istring("skipping ") + _manifest_list[i]._target
00546           + " file check; doesn't match keyword \"" + _keyword + "\"");
00547       continue;
00548     }
00549 
00550     // we only access the source file here if it's finalized.  we can't do
00551     // this if the target is supposed to be recursive or if it's got a wildcard
00552     // pattern in it.
00553     if (!(_manifest_list[i]._flags & RECURSIVE_SRC) && negative(indy)
00554         && !(_manifest_list[i]._flags & OMIT_PACKING) ) {
00555       // access the source file to get its size.
00556       byte_filer source_file(_manifest_list[i]._source, "rb");
00557       if (!source_file.good()) {
00558         LOG(istring("could not access the source file for bundling: ")
00559             + _manifest_list[i]._source);
00560         BAIL(69);
00561       }
00562       bool okaysize = get_file_size(_manifest_list[i]._source,
00563           _manifest_list[i]._size, _manifest_list[i]._timestamp);
00564       if (!okaysize || (_manifest_list[i]._size < 0) ) {
00565         // this is a failure, but the function complains about it already.
00566         BAIL(75);
00567       }
00568     }
00569   }
00570 
00571   // patch the manifest list for wildcards and recursive sources.
00572   for (int i = 0; i < _manifest_list.length(); i++) {
00573     bundled_chunk curr = _manifest_list[i];
00574 
00575     if (!!_keyword && !curr._keywords.member(_keyword)) {
00576       // this item's keyword doesn't match the one we were given, so skip it.
00577       noisy_logfile.log(istring("zapping entry for ") + curr._target
00578           + "; doesn't match keyword \"" + _keyword + "\"");
00579       _manifest_list.zap(i, i);
00580       i--;  // skip back since we eliminated an index.
00581       continue;
00582     }
00583 
00584     if (curr._flags & SET_VARIABLE) {
00585       // we're done working on this.
00586       continue;
00587     } else if (curr._flags & RECURSIVE_SRC) {
00588       // handle a recursive style target.
00589       int star_indy = curr._source.find("*");
00590       if (non_negative(star_indy)) {
00591         // this is currently illegal.  we don't allow recursion + wildcards.
00592         LOG(istring("illegal combination of recursion and wildcard: ")
00593             + curr._source);
00594         BAIL(70);
00595       }
00596       // handle the recursive guy.
00597       int ret = patch_recursive_target(curr._source, curr._target, i);
00598       if (ret != 0) {
00599         LOG(istring("failed during packing of recursive source: ")
00600             + curr._source);
00601         BAIL(72);
00602       }
00603       // take this item out of the picture, since all contents got included.
00604       _manifest_list.zap(i, i);
00605       i--;  // skip back since we eliminated an index.
00606       continue;
00607     } else if (curr._flags & SOURCE_EXECUTE) {
00608       // we have massaged the current manifest chunk as much as we can, so now
00609       // we will execute the source item if that was specified.
00610       BASE_LOG(istring("launching ") + curr._source);
00611       if (!!curr._parms)
00612         BASE_LOG(istring("\tparameters ") + curr._parms);
00613       BASE_LOG(istring('-', 76));
00614       u_int kid;
00615       u_int retval = portable::launch_process(curr._source, curr._parms,
00616           portable::AWAIT_APP_EXIT, kid);
00617       if (retval != 0) {
00618         LOG(istring("failed to launch process, source=") + curr._source
00619             + ", with parms " + curr._parms);
00620         if (! (curr._flags & IGNORE_ERRORS) ) {
00621           BAIL(92);
00622         }
00623       }
00624       BASE_LOG(istring('-', 76));
00625       if (curr._flags & OMIT_PACKING) {
00626         // this one shouldn't be included in the package.
00627         _manifest_list.zap(i, i);
00628         i--;  // skip back since we eliminated an index.
00629       }
00630       continue;
00631     } else {
00632       // check for a wildcard.
00633       int star_indy = curr._source.find("*");
00634       if (negative(star_indy)) continue;  // simple targets are boring.
00635       // this does have a wildcard in it.  let's make sure it's in the right
00636       // place for a wildcard in our scheme.
00637       int slash_indy = curr._source.find('/', curr._source.end(), true);
00638       if (star_indy < slash_indy) {
00639         BASE_LOG(istring("illegal wildcard placement in ") + curr._source);
00640         BASE_LOG("  (the wildcard must be in the last component of the path)");
00641         BAIL(71);
00642       }
00643       // handle the wildcarded source.
00644       int ret = patch_wildcard_target(curr._source, curr._target, i);
00645       if (ret != 0) {
00646         LOG(istring("failed during packing of wildcarded source: ")
00647             + curr._source);
00648         BAIL(73);
00649       }
00650       _manifest_list.zap(i, i);
00651       i--;  // skip back since we eliminated an index.
00652       continue;
00653     }
00654   }
00655 
00656 #ifdef DEBUG_BUNDLER
00657   if (!final_return) {
00658     // we had a successful run so we can print this stuff out.
00659     LOG("read the following info from manifest:");
00660     BASE_LOG("size\tsource\t\t\ttarget");
00661     for (int i = 0; i < _manifest_list.length(); i++) {
00662       bundled_chunk &curr = _manifest_list[i];
00663       BASE_LOG(isprintf("%d\t%s\t\t\t%s", curr._size,
00664           curr._source.s(), curr._target.s()));
00665     }
00666   }
00667 #endif
00668 
00669   return final_return;
00670 }
00671 
00672 int bundle_creator::write_stub_and_toc()
00673 {
00674   FUNCDEF("write_stub_and_toc");
00675 
00676   // define our location to find the unpacking stub program.
00677 //hmmm: make this a command line parameter.
00678 #ifdef __UNIX__
00679   istring stub_file("$REPOSITORY_DIR/exe/unpacker_stub");
00680 #endif
00681 #ifdef __WIN32__
00682   istring stub_file("$REPOSITORY_DIR/exe/unpacker_stub.exe");
00683 #endif
00684 
00685   // replace any environment variables on the source side now.
00686   stub_file = parser_bits::substitute_env_vars(stub_file, false);
00687 
00688   // make sure the stub is accessible.
00689   byte_filer stubby(stub_file, "rb");
00690   if (!stubby.good()) {
00691     LOG(istring("could not access the unpacking stub at: ") + stub_file);
00692     return 80;
00693   }
00694   _stub_size = int(stubby.length());  // get the stub size for later reference.
00695   byte_array whole_stub;
00696   stubby.read(whole_stub, _stub_size + 100);
00697   stubby.close();
00698   _bundle->write(whole_stub);
00699 
00700   byte_array packed_toc_len;
00701   basis::obscure_attach(packed_toc_len, _manifest_list.length());
00702   int ret = _bundle->write(packed_toc_len);
00703   if (ret < 0) {
00704     LOG(istring("could not write the TOC length to the bundle: ")
00705         + _output_file);
00706     return 81;
00707   }
00708 
00709   // dump out the manifest list in our defined format.
00710   for (int i = 0; i < _manifest_list.length(); i++) {
00711     bundled_chunk &curr = _manifest_list[i];
00712 //LOG(isprintf("flag %d is %d", i, curr._flags));
00713     byte_array chunk;
00714     curr.pack(chunk);
00715     if (_bundle->write(chunk) <= 0) {
00716       LOG(isprintf("could not write item #%d [%s] to the bundle: ", i,
00717           curr._source.s())
00718           + _output_file);
00719       return 88;
00720     }
00721   }
00722 
00723   return 0;
00724 }
00725 
00726 int bundle_creator::bundle_sources()
00727 {
00728   FUNCDEF("bundle_sources");
00729   // go through all the source files and append them to the bundled output.
00730   file_logger noisy_logfile(path_configuration::make_logfile_name
00731       ("bundle_creator_activity.log"));
00732   for (int i = 0; i < _manifest_list.length(); i++) {
00733     bundled_chunk &curr = _manifest_list[i];
00734 
00735     if (curr._flags & SET_VARIABLE) {
00736       // all we need to do is keep this in the manifest.
00737       noisy_logfile.log(istring("bundling: variable setting ") + curr._target
00738           + "=" + curr._parms);
00739       continue;
00740     } else if (curr._flags & OMIT_PACKING) {
00741       // this one shouldn't be included in the package.
00742       continue;
00743     }
00744 
00745     noisy_logfile.log(istring("bundling: ") + curr._source);
00746     byte_filer source(curr._source, "rb");
00747     if (!source.good()) {
00748       LOG(isprintf("could not read item #%d for the bundle: \"", i)
00749           + curr._source + "\"");
00750       return 98;
00751     }
00752 
00753     byte_array compressed(256 * KILOBYTE);  // expand the buffer to start with.
00754     byte_array temp;  // temporary read buffer.
00755 
00756     // chew on the file a chunk at a time.  this allows us to easily handle
00757     // arbitrarily large files rather than reading their entirety into memory.
00758     int total_written = 0;
00759     do {
00760       int ret = source.read(temp, CHUNKING_SIZE);
00761       if (ret < 0) {
00762         LOG(isprintf("failed while reading item #%d: ", i) + curr._source);
00763         return 99;
00764       } 
00765       total_written += ret;  // add in what we expect to write.
00766       // skip compressing if there's no data.
00767       uLongf destlen = 0;
00768       bool null_chunk = false;
00769       if (ret == 0) {
00770         compressed.reset();
00771   null_chunk = true;
00772       } else {
00773         compressed.reset(int(0.1 * ret) + ret + KILOBYTE);
00774           // provide some extra space as per zlib instructions.  we're giving it
00775           // way more than they request.
00776         destlen = compressed.length();
00777         // pack the chunks first so we can know sizes needed.
00778         int comp_ret = compress(compressed.access(), &destlen, temp.observe(),
00779             temp.length());
00780         if (comp_ret != Z_OK) {
00781           LOG(isprintf("failed while compressing item #%d: ", i)
00782               + curr._source);
00783           return 99;
00784         }
00785         compressed.zap(destlen, compressed.length() - 1);
00786       }
00787       byte_array just_sizes;
00788       basis::obscure_attach(just_sizes, temp.length());
00789         // add in the real size.
00790       basis::obscure_attach(just_sizes, int(destlen));
00791         // add in the packed size.
00792       ret = _bundle->write(just_sizes);
00793       if (ret <= 0) {
00794         LOG(isprintf("failed while writing sizes for item #%d: ", i)
00795             + curr._source);
00796         return 93;
00797       }
00798       if (!null_chunk) {
00799         ret = _bundle->write(compressed);
00800         if (ret <= 0) {
00801           LOG(isprintf("failed while writing item #%d: ", i) + curr._source);
00802           return 93;
00803         } else if (ret != compressed.length()) {
00804           LOG(isprintf("wrote different size for item #%d (tried %d, "
00805               "wrote %d): ", i, compressed.length(), ret) + curr._source);
00806           return 93;
00807         }
00808       }
00809     } while (!source.eof());
00810 //hmmm: very common code to above size writing.
00811     byte_array just_sizes;
00812     basis::obscure_attach(just_sizes, -1);
00813     basis::obscure_attach(just_sizes, -1);
00814     int ret = _bundle->write(just_sizes);
00815     if (ret <= 0) {
00816       LOG(isprintf("failed while writing sentinel of item #%d: ", i)
00817           + curr._source);
00818       return 96;
00819     }
00820     source.close();
00821     if (total_written != curr._size) {
00822       LOG(isprintf("size (%d) disagrees with initial size (%d) for "
00823           "item #%d: ", total_written, curr._size, i) + curr._source);
00824     }
00825   }
00826   noisy_logfile.log(istring("Bundling run ends at ") + timestamp(false, true));
00827   noisy_logfile.log(istring('-', 76));
00828 
00829   return 0;
00830 }
00831 
00832 int bundle_creator::finalize_file()
00833 {
00834   _bundle->close();
00835   return 0;
00836 }
00837 
00838 int bundle_creator::write_offset()
00839 {
00840   FUNCDEF("write_offset");
00841   byte_filer bun(_output_file, "r+b");  // open the file for updating.
00842 
00843   istring magic_string("muftiloc");  // our sentinel string.
00844   istring temp_string;  // data from the file.
00845 
00846   while (!bun.eof()) {
00847     // find the telltale text in the file.
00848     bool found_it = false;  // we'll set this to true if we see the string.
00849     int location = 0;  // where the sentinel's end is.
00850     for (int i = 0; i < magic_string.length(); i++) {
00851       int ret = bun.read(temp_string, 1);
00852       if (ret <= 0) break;
00853       if (temp_string[0] != magic_string[i]) break;  // no match.
00854       if (i == magic_string.end()) {
00855         // we found a match to our string!
00856         found_it = true;
00857         location = int(bun.tell());
00858 //LOG(isprintf("found the sentinel in the file!  posn=%d", location));
00859       }
00860     }
00861     if (!found_it) continue;  // keep reading.
00862     bun.seek(location);
00863     byte_array packed_offset;
00864     basis::obscure_attach(packed_offset, _stub_size);
00865 //LOG(istring("pattern of len is:\n") + byte_format::text_dump(packed_offset));
00866     // write the offset into the current position, which should be just after
00867     // the sentinel's location.
00868     bun.write(packed_offset);
00869 //LOG(isprintf("wrote manifest offset before posn=%d", bun.tell()));
00870     break;  // done with looking for that pattern.
00871   }
00872   bun.close();  // completely finished now.
00873 
00874   chmod(_output_file.s(), 0766);
00875     // make sure it's an executable file when we're done with it.
00876 
00877   BASE_LOG(istring("done file bundling at ") + timestamp(false, true));
00878 
00879   return 0;
00880 }
00881 
00883 
00884 HOOPLE_MAIN(bundle_creator, )
00885 
00886 #ifdef __BUILD_STATIC_APPLICATION__
00887   // static dependencies found by buildor_gen_deps.sh:
00888   #include <basis/array.cpp>
00889   #include <basis/byte_array.cpp>
00890   #include <basis/callstack_tracker.cpp>
00891   #include <basis/chaos.cpp>
00892   #include <basis/convert_utf.cpp>
00893   #include <basis/definitions.cpp>
00894   #include <basis/earth_time.cpp>
00895   #include <basis/guards.cpp>
00896   #include <basis/istring.cpp>
00897   #include <basis/log_base.cpp>
00898   #include <basis/memory_checker.cpp>
00899   #include <basis/mutex.cpp>
00900   #include <basis/object_base.cpp>
00901   #include <basis/outcome.cpp>
00902   #include <basis/packable.cpp>
00903   #include <basis/portable.cpp>
00904   #include <basis/sequence.cpp>
00905   #include <basis/set.cpp>
00906   #include <basis/utility.cpp>
00907   #include <basis/version_record.cpp>
00908   #include <data_struct/amorph.cpp>
00909   #include <data_struct/bit_vector.cpp>
00910   #include <data_struct/byte_hasher.cpp>
00911   #include <data_struct/configurator.cpp>
00912   #include <data_struct/hash_table.cpp>
00913   #include <data_struct/pointer_hash.cpp>
00914   #include <data_struct/stack.cpp>
00915   #include <data_struct/static_memory_gremlin.cpp>
00916   #include <data_struct/string_hash.cpp>
00917   #include <data_struct/string_hasher.cpp>
00918   #include <data_struct/string_table.cpp>
00919   #include <data_struct/symbol_table.cpp>
00920   #include <data_struct/table_configurator.cpp>
00921   #include <loggers/console_logger.cpp>
00922   #include <loggers/file_logger.cpp>
00923   #include <loggers/locked_logger.cpp>
00924   #include <loggers/null_logger.cpp>
00925   #include <loggers/program_wide_logger.cpp>
00926   #include <opsystem/application_base.cpp>
00927   #include <opsystem/application_shell.cpp>
00928   #include <opsystem/byte_filer.cpp>
00929   #include <opsystem/command_line.cpp>
00930   #include <opsystem/critical_events.cpp>
00931   #include <opsystem/directory.cpp>
00932   #include <opsystem/filename.cpp>
00933   #include <opsystem/filetime.cpp>
00934   #include <opsystem/ini_config.cpp>
00935   #include <opsystem/ini_parser.cpp>
00936   #include <opsystem/path_configuration.cpp>
00937   #include <opsystem/rendezvous.cpp>
00938   #include <textual/byte_format.cpp>
00939   #include <textual/list_parsing.cpp>
00940   #include <textual/parser_bits.cpp>
00941   #include <textual/string_manipulation.cpp>
00942   #include <textual/tokenizer.cpp>
00943 #endif // __BUILD_STATIC_APPLICATION__
00944 

Generated on Wed Nov 19 04:28:31 2008 for HOOPLE Libraries by  doxygen 1.5.1