feisty meow concerns codebase  2.140
unpacker_stub.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : unpacker stub program *
4 * Author : Chris Koeritz *
5 * *
6 *******************************************************************************
7 * Copyright (c) 2006-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
14 
15 #include "common_bundle.h"
16 
20 #include <basis/array.h>
21 #include <basis/byte_array.h>
22 #include <basis/environment.h>
23 #include <basis/guards.h>
24 #include <basis/utf_conversion.h>
27 #include <filesystem/byte_filer.h>
28 #include <filesystem/directory.h>
29 #include <filesystem/filename.h>
30 #include <filesystem/file_time.h>
32 #include <loggers/console_logger.h>
34 #include <loggers/file_logger.h>
38 #include <textual/parser_bits.h>
39 
40 #include <stdio.h>
41 #include <sys/stat.h>
42 #include <zlib.h>
43 //#ifdef __UNIX__
44  #include <utime.h>
45 //#endif
46 /*
47 #ifdef _MSC_VER
48  #include <direct.h>
49  #include <io.h>
50  #include <shlobj.h>
51  #include <sys/utime.h>
52 #endif
53 */
54 
55 using namespace application;
56 using namespace basis;
57 using namespace configuration;
58 using namespace filesystem;
59 using namespace loggers;
60 using namespace processes;
61 using namespace structures;
62 using namespace textual;
63 
64 const int CHUNKING_SIZE = 64 * KILOBYTE;
65  // we'll read this big a chunk from a source file at a time.
66 
67 const astring TARGET_WORD = "TARGET";
68 const astring LOGDIR_WORD = "LOGDIR";
69 
70 #define BASE_LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT)
71 #define LOG(to_print) STAMPED_EMERGENCY_LOG(program_wide_logger::get(), to_print)
72 
73 //#define DEBUG_STUB
74  // uncomment for noisier version.
75 
76 const char *ERROR_TITLE = "An Error Caused Incomplete Installation";
77  // used in error messages as the title.
78 
80 
81 class unpacker_stub : public application_shell
82 {
83 public:
84  unpacker_stub() : application_shell(), _app_name(filename(_global_argv[0]).basename()) {}
85  DEFINE_CLASS_NAME("unpacker_stub");
86 
87  int print_instructions();
88 
89  virtual int execute();
90 
91 private:
92  astring _app_name;
93  array<manifest_chunk> _manifest;
94  string_table _variables;
95 };
96 
98 
99 void show_message(const astring &msg, const astring &title)
100 {
101 #ifndef __WIN32__
102  BASE_LOG(title);
103  BASE_LOG(astring('-', title.length()));
104  BASE_LOG(msg);
105 #else
106  MessageBox(0, to_unicode_temp(msg), to_unicode_temp(title),
107  MB_OK | MB_ICONINFORMATION);
108 #endif
109 }
110 
112 {
113  a_sprintf msg("\
114  %s: This program unpacks its contents into the locations\n\
115  specified at packing time. The --target flag can be used to specify a\n\
116 different %s directory for the installation (for components that use\n\
117 the default %s variable to specify their install folder).\n\
118  One can also pass a --keyword flag to specify a keyword; the files in the\n\
119 bundle that are marked with that keyword will be installed, but files that\n\
120 are missing the keyword will not be.\n\
121  Further, variables can be overridden on the command line in the\n\
122 form: X=Y.\n\n\
123 The line below uses all these parameters as an example:\n\n\
124  %s --target c:\\Program Files\\gubernator --keyword dlls_only SILENT=true\n\
125 \n\
126 Additional Notes:\n\
127 \n\
128  One helpful variable is \"%s\". This is where the unpacking log file\n\
129 will be written to. The default is \"~/logs\" (or \"$TMP/logs\" on win32)\n\
130 until overridden.\n\
131 \n", _app_name.s(), TARGET_WORD.s(), TARGET_WORD.s(), _app_name.s(), LOGDIR_WORD.s());
132  show_message(msg, "Unpacking Instructions");
133  return 12;
134 }
135 
136 // creates a unique backup file name, if it can.
137 // we assume that this file already exists, but we want to check for
138 // our backup file naming scheme in case we already backed this up
139 // some previous time.
141 {
142 const int MAXIMUM_BACKUPS = 200;
143 
144  for (int i = 0; i < MAXIMUM_BACKUPS; i++) {
145  filename target_file = original_file + a_sprintf(".%04d", i);
146  if (target_file.exists()) {
147 //BASE_LOG(astring("bkup already here: ") + target_file);
148  continue;
149  }
150  // this file is okay to use.
151  return target_file;
152  }
153  return ""; // nothing found.
154 }
155 
156 
157 // the string embedded into the array is not mentioned anywhere else in this
158 // program, which should allow the packer to find it and fix the manifest
159 // size. the keyword is: "muftiloc", and the first bytes that can be
160 // overwritten are its beginning offset plus its length of 8 chars. there
161 // is room for 8 bytes after the tag, but currently the first 4 are used as
162 // a 32 bit offset.
164  = { 'm', 'u', 'f', 't', 'i', 'l', 'o', 'c', 0, 0, 0, 0, 0, 0, 0, 0 };
165 
166 int unpacker_stub::execute()
167 {
168 #ifdef ADMIN_CHECK
169  #ifdef __WIN32__
170  if (IsUserAnAdmin()) {
171  ::MessageBox(0, to_unicode_temp("IS admin in bundler"), to_unicode_temp("bundler"), MB_OK);
172  } else {
173  ::MessageBox(0, to_unicode_temp("NOT admin in bundler"), to_unicode_temp("bundler"), MB_OK);
174  }
175  #endif
176 #endif
177 
179 
180  int indy = 0;
181  if (cmds.find('?', indy)) return print_instructions();
182  if (cmds.find("?", indy)) return print_instructions();
183 
184  // make sure we provide the same services as the bundle creator for the
185  // default set of variables.
186 #ifndef __WIN32__
187  environment::set("EXE_END", ""); // executable file ending.
188  environment::set("DLL_START", "lib"); // dll file prefix.
189  environment::set("DLL_END", ".so"); // dll file ending.
190 #else
191  environment::set("EXE_END", ".exe");
192  environment::set("DLL_START", "");
193  environment::set("DLL_END", ".dll");
194 #endif
195 
196  // set TARGET directory if passed on the command line,
197  bool provided_target = false; // true if the command line specified target.
198  astring target;
199  cmds.get_value("target", target);
200  if (!target) {
201  provided_target = false;
202  } else {
203 //LOG(astring("target is now ") + target);
204  environment::set(TARGET_WORD, target);
205  provided_target = true;
206  }
207 
208  {
209  astring logdir = environment::get(LOGDIR_WORD);
210 #ifdef __WIN32__
211  if (!logdir) {
212  logdir = environment::TMP() + "/logs";
213  environment::set(LOGDIR_WORD, logdir);
214  }
215 #else
216  if (!logdir) {
217  astring homedir = environment::get("HOME");
218  logdir = homedir + "/logs";
219  environment::set(LOGDIR_WORD, logdir);
220  }
221 #endif
222  }
223 
224  astring keyword; // set if we were given a keyword on cmd line.
225  cmds.get_value("keyword", keyword);
226 
227  astring vars_set; // we will document the variables we saw and show later.
228 
229  for (int x = 0; x < cmds.entries(); x++) {
230  command_parameter curr = cmds.get(x);
231  if (curr.type() != command_parameter::VALUE) continue; // skip it.
232  if (curr.text().find('=', 0) < 0) continue; // no equals character.
234  t.parse(curr.text());
235  if (!t.symbols()) continue; // didn't parse right.
236  astring var = t.table().name(0);
237  astring value = t.table()[0];
238  vars_set += astring("variable set: ") + var + "=" + value
239  + parser_bits::platform_eol_to_chars();
240  if (var == TARGET_WORD) {
241  provided_target = true;
242  }
243 //hmmm: handle LOGDIR passed as variable this way also!
244  _variables.add(var, value);
245  environment::set(var, value);
246  }
247 
248  // get the most up to date version of the variable now.
249  astring logdir = environment::get(LOGDIR_WORD);
250 
251  astring appname = filename(application_configuration::application_name()).rootname();
252 
253  astring logname = logdir + "/" + appname + ".log";
254 // log_base *old_log = set_PW_logger_for_combo(logname);
255  standard_log_base *old_log = program_wide_logger::set(new combo_logger(logname));
256  WHACK(old_log);
257 
258  BASE_LOG(astring('#', 76));
259  BASE_LOG(appname + " command-line parameters:");
260  BASE_LOG(cmds.text_form());
261  BASE_LOG(astring('#', 76));
262 
263  BASE_LOG(vars_set);
264 
265 #ifdef __WIN32__
266  // create a window so that installshield won't barf. this is only needed
267  // on windows when using this as a prerequisite for installshield.
268  window_handle f_window = create_simplistic_window("temp_stubby_class",
269  "stubby window title");
270 #endif
271 
272  // read position for manifest offset out of our array.
273  byte_array temp_packed(2 * sizeof(int), MANIFEST_OFFSET_ARRAY + 8);
274  un_int manifest_offset;
275  if (!structures::obscure_detach(temp_packed, manifest_offset)) {
276  show_message(astring("could not read manifest offset in: ") + _global_argv[0],
277  ERROR_TITLE);
278  return 24;
279  }
280 
281  filename this_exe(_global_argv[0]);
282  if (!this_exe.exists()) {
283  show_message(astring("could not access this exe image: ") + this_exe.raw(),
284  ERROR_TITLE);
285  return 23;
286  }
287 
288  // start reading the manifest...
289  byte_filer our_exe(this_exe, "rb");
290  our_exe.seek(manifest_offset); // go to where the manifest starts.
291 
292  // get number of chunks in manifest as the first bytes.
293  if (our_exe.read(temp_packed, 2 * sizeof(int)) <= 0) {
294  show_message(astring("could not read the manifest length in: ")
295  + this_exe.raw(), ERROR_TITLE);
296  return 26;
297  }
298  un_int item_count;
299  structures::obscure_detach(temp_packed, item_count);
300 //check result of detach!
301  _manifest.insert(0, item_count); // add enough spaces for our item list.
302 
303  // read each item definition out of the manifest now.
304  for (int i = 0; i < (int)item_count; i++) {
305  manifest_chunk &curr = _manifest[i];
306  bool worked = manifest_chunk::read_manifest(our_exe, curr);
307 
308 #ifdef DEBUG_STUB
309  astring tmpork;
310  curr.text_form(tmpork);
311  LOG(a_sprintf("item %d: ", i) + tmpork);
312 #endif
313 
314  if (!worked) {
315  show_message(a_sprintf("could not read chunk for item #%d [%s]: ", i,
316  curr._payload.s())
317  + this_exe.raw(), ERROR_TITLE);
318  return 86;
319  }
320  }
321 
322 #ifdef DEBUG_STUB
323  LOG("read the following info from manifest:");
324  astring temp;
325  for (int i = 0; i < _manifest.length(); i++) {
326  manifest_chunk &curr = _manifest[i];
327  temp += a_sprintf("(%d) size %d, %s\n", i, curr._size,
328  curr._payload.s());
329  }
330  critical_events::alert_message(temp, "manifest contents");
331 #endif
332 
333  // now we should be just after the last byte of the manifest, at the
334  // first piece of data. we should read each chunk of data out and store
335  // it where it's supposed to go.
336  for (int festdex = 0; festdex < _manifest.length(); festdex++) {
337  manifest_chunk &curr = _manifest[festdex];
338  int size_left = curr._size;
339 
340  // patch in our environment variables.
341  curr._payload = parser_bits::substitute_env_vars(curr._payload, false);
342  curr._parms = parser_bits::substitute_env_vars(curr._parms, false);
343 #ifdef DEBUG_STUB
344  BASE_LOG(astring("processing ") + curr._payload
345  + a_sprintf(", size=%d, flags=%d", curr._size, curr._flags));
346  if (!!curr._parms)
347  BASE_LOG(astring(" parms: ") + curr._parms);
348 #endif
349 
350  // see if they specified a keyword on the command line.
351  bool keyword_good = true;
352  if (!!keyword && !curr._keywords.member(keyword)) {
353  // their keyword choice didn't match what we were pickled with.
354  keyword_good = false;
355 //BASE_LOG(astring("skipping ") + curr._payload + " for wrong keyword " + keyword);
356  }
357 
358  if (curr._flags & SET_VARIABLE) {
359  if (keyword_good) {
360  // this is utterly different from a real target. we just set the
361  // variable and move on.
362  if (provided_target && (curr._payload == TARGET_WORD) ) {
363  BASE_LOG(astring("skipping ") + curr._payload + "=" + curr._parms
364  + ": was provided explicitly as " + target);
365  } else if (_variables.find(curr._payload)) {
366  BASE_LOG(astring("skipping ") + curr._payload + "=" + curr._parms
367  + ": was provided on command line.");
368  } else {
369  BASE_LOG(astring("setting ") + curr._payload + "=" + curr._parms);
370  environment::set(curr._payload, curr._parms);
371 
372  // special code for changing logging directory midstream.
373  if (curr._payload == LOGDIR_WORD) {
374  astring logdir = curr._parms;
375  astring appname = filename(application_configuration::application_name()).rootname();
376  astring logname = logdir + "/" + appname + ".log";
377  standard_log_base *old_log = program_wide_logger::set(new combo_logger(logname));
379  WHACK(old_log);
380  }
381  if (curr._payload == TARGET_WORD) {
382  // well we've now seen this defined.
383  provided_target = true;
384  }
385  }
386  }
387  continue;
388  } else if (curr._flags & TEST_VARIABLE_DEFINED) {
389  if (keyword_good) {
390  astring var_value = environment::get(curr._payload);
391  if (var_value.empty()) {
392  BASE_LOG(astring("assertion failed: ") + curr._payload + " is not defined!");
393  show_message(a_sprintf("failed test for variable %s: it is "
394  "*not* defined, for item #%d.", curr._payload.s(), festdex),
395  ERROR_TITLE);
396  return 98;
397  }
398  BASE_LOG(astring("assertion succeeded: ") + curr._payload + " defined as " + var_value);
399  }
400  continue;
401  }
402 
403  if (! (curr._flags & OMIT_PACKING) ) {
404  // this one has a payload, so install it now if appropriate.
405  if (!provided_target) {
406 //error!
407  BASE_LOG(astring("No TARGET has been specified; please provide one on the command line.") + parser_bits::platform_eol_to_chars());
408  return print_instructions();
409  }
410 
411  // make sure that the directories needed are present for the outputs.
412  filename target_dir = filename(curr._payload).dirname();
413 
414  if (keyword_good && !target_dir.exists()
415  && !directory::recursive_create(target_dir)) {
416  LOG(a_sprintf("failed to create directory %s for item #%d: ",
417  target_dir.raw().s(), festdex) + curr._payload);
418  }
419 
420  // test whether they wanted to allow overwriting.
421  if (curr._flags & NO_OVERWRITE) {
422  filename target_file(curr._payload);
423  if (target_file.exists()) {
424  BASE_LOG(astring("not overwriting existing ") + curr._payload);
425  keyword_good = false;
426  }
427  }
428 
429  // see if this is supposed to be backed up before installation.
430  if (curr._flags & MAKE_BACKUP_FILE) {
431  filename target_file(curr._payload);
432  if (target_file.exists()) {
433  astring new_file_name = find_unique_backup_name(curr._payload);
434  if (!new_file_name) {
435  BASE_LOG(astring("failed to calculate new filename for ") + curr._payload);
436  keyword_good = false; // cancel the overwrite, couldn't backup.
437  } else {
438  // make a backup of the file by moving the old file name to the
439  // new file name, which should be unique, and then the current
440  // name is all clear to be written as a new file.
441 // BASE_LOG(astring("backing up ") + curr._payload + " --> " + new_file_name);
442  int retval = rename(curr._payload.s(), new_file_name.s());
443  if (retval) {
444  BASE_LOG(astring("failed to rename ") + curr._payload + " as " + new_file_name);
445  keyword_good = false; // cancel the overwrite, couldn't backup.
446  }
447  }
448  }
449  }
450 
451  byte_filer *targo = NULL_POINTER;
452  if (keyword_good) targo = new byte_filer(curr._payload, "wb");
453  byte_array uncompressed(256 * KILOBYTE); // fluff it out to begin with.
454  byte_array temp(256 * KILOBYTE);
455 
456  bool first_read = true;
457  // true if there haven't been any reads of the file before now.
458  bool too_tiny_complaint_already = false;
459  // becomes true if we complain about the file's size being larger than
460  // expected. this allows us to only complain once about each file.
461 
462  // read a chunk at a time out of our exe image and store it into the
463  // target file.
464  while (first_read || !our_exe.eof()) {
465  first_read = false;
466  un_int real_size = 0, packed_size = 0;
467  // read in the real size from the file.
468  bool worked = manifest_chunk::read_an_obscured_int(our_exe, real_size);
469  if (!worked) {
470  show_message(a_sprintf("failed while reading real size "
471  "for item #%d: ", festdex) + curr._payload, ERROR_TITLE);
472  return 99;
473  }
474  // read in the packed size now.
476  if (!worked) {
477  show_message(a_sprintf("failed while reading packed size "
478  "for item #%d: ", festdex) + curr._payload, ERROR_TITLE);
479  return 99;
480  }
481 
482  // make sure we don't eat the whole package--did the file end?
483  if ( (real_size == -1) && (packed_size == -1) ) {
484  // we've hit our sentinel; we've already unpacked all of this file.
485  break;
486  }
487 
488 #ifdef DEBUG_STUB
489  BASE_LOG(a_sprintf("chunk packed_size=%d, real_size=%d", packed_size,
490  real_size));
491 #endif
492 
493  // now we know how big our next chunk is, so we can try reading it.
494  if (packed_size) {
495  int ret = our_exe.read(temp, packed_size);
496  if (ret <= 0) {
497  show_message(a_sprintf("failed while reading item #%d: ", festdex)
498  + curr._payload, ERROR_TITLE);
499  return 99;
500  } else if (ret != packed_size) {
501  show_message(a_sprintf("bad trouble ahead, item #%d had different "
502  " size on read (expected %d, got %d): ", festdex, packed_size,
503  ret) + curr._payload, ERROR_TITLE);
504  }
505 
506  uncompressed.reset(real_size + KILOBYTE); // add some for paranoia.
507  uLongf destlen = uncompressed.length();
508  int uncomp_ret = uncompress(uncompressed.access(), &destlen,
509  temp.observe(), packed_size);
510  if (uncomp_ret != Z_OK) {
511  show_message(a_sprintf("failed while uncompressing item #%d: ",
512  festdex) + curr._payload, ERROR_TITLE);
513  return 99;
514  }
515 
516  if (int(destlen) != real_size) {
517  LOG(a_sprintf("got a different unpacked size for item #%d: ",
518  festdex) + curr._payload);
519  }
520 
521  // update the remaining size for this data chunk.
522  size_left -= real_size;
523  if (size_left < 0) {
524  if (!too_tiny_complaint_already) {
525  LOG(a_sprintf("item #%d was larger than expected (non-fatal): ",
526  festdex) + curr._payload);
527  too_tiny_complaint_already = true;
528  }
529  }
530  // toss the extra bytes out.
531  uncompressed.zap(real_size, uncompressed.length() - 1);
532 
533  if (targo) {
534  // stuff the data we read into the target file.
535  ret = targo->write(uncompressed);
536  if (ret != uncompressed.length()) {
537  show_message(a_sprintf("failed while writing item #%d: ", festdex)
538  + curr._payload, ERROR_TITLE);
539  return 93;
540  }
541  }
542  }
543  }
544  if (targo) targo->close();
545  WHACK(targo);
546  // the file's written, but now we slap it's old time on it too.
547  file_time t;
548  if (!t.unpack(curr.c_filetime)) {
549  show_message(astring("failed to interpret timestamp for ")
550  + curr._payload, ERROR_TITLE);
551  return 97;
552  }
553  // put the timestamp on the file.
554  t.set_time(curr._payload);
555 // utimbuf held_time;
556 // held_time.actime = t.raw();
557 // held_time.modtime = t.raw();
558 // // put the timestamp on the file.
559 // utime(curr._payload.s(), &held_time);
560  }
561 
562  // now that we're pretty sure the file exists, we can run it if needed.
563  if ( (curr._flags & TARGET_EXECUTE) && keyword_good) {
564  // change the mode on the target file so we can execute it.
565  chmod(curr._payload.s(), 0766);
566  astring prev_dir = application_configuration::current_directory();
567 
568  BASE_LOG(astring("launching ") + curr._payload);
569  if (!!curr._parms)
570  BASE_LOG(astring(" with parameters: ") + curr._parms);
571  BASE_LOG(astring('-', 76));
572 
573  basis::un_int kid;
574  basis::un_int retval = launch_process::run(curr._payload, curr._parms,
575  launch_process::AWAIT_APP_EXIT | launch_process::HIDE_APP_WINDOW, kid);
576  if (retval != 0) {
577  if (! (curr._flags & IGNORE_ERRORS) ) {
578  if (curr._flags & QUIET_FAILURE) {
579  // no message box for this, but still log it.
580  LOG(astring("failed to launch process, targ=")
581  + curr._payload + " with parms " + curr._parms
582  + a_sprintf(" error=%d", retval));
583  } else {
584  show_message(astring("failed to launch process, targ=")
585  + curr._payload + " with parms " + curr._parms
586  + a_sprintf(" error=%d", retval), ERROR_TITLE);
587  }
588  return retval; // pass along same exit value we were told.
589  } else {
590  LOG(astring("ignoring failure to launch process, targ=")
591  + curr._payload + " with parms " + curr._parms
592  + a_sprintf(" error=%d", retval));
593  }
594  }
595 
596  chdir(prev_dir.s()); // reset directory pointer, just in case.
597 
598  BASE_LOG(astring('-', 76));
599  }
600 
601  }
602 
603 #ifdef __WIN32__
604  whack_simplistic_window(f_window);
605 #endif
606 
607  return 0;
608 }
609 
611 
612 HOOPLE_MAIN(unpacker_stub, )
613 
614 #ifdef __BUILD_STATIC_APPLICATION__
615  // static dependencies found by buildor_gen_deps.sh:
618  #include <basis/astring.cpp>
619  #include <basis/common_outcomes.cpp>
620  #include <basis/environment.cpp>
621  #include <basis/mutex.cpp>
622  #include <basis/utf_conversion.cpp>
629  #include <filesystem/byte_filer.cpp>
630  #include <filesystem/directory.cpp>
631  #include <filesystem/file_info.cpp>
632  #include <filesystem/filename.cpp>
634  #include <filesystem/file_time.cpp>
636  #include <filesystem/huge_file.cpp>
637  #include <loggers/combo_logger.cpp>
638  #include <loggers/console_logger.cpp>
639  #include <loggers/critical_events.cpp>
640  #include <loggers/file_logger.cpp>
643  #include <structures/bit_vector.cpp>
644  #include <structures/checksums.cpp>
648  #include <structures/string_table.cpp>
650  #include <textual/byte_formatter.cpp>
651  #include <textual/parser_bits.cpp>
653  #include <timely/earth_time.cpp>
654  #include <timely/time_control.cpp>
655  #include <timely/time_stamp.cpp>
656 #endif // __BUILD_STATIC_APPLICATION__
657 
#define chmod
Definition: Xos2defs.h:12
#define chdir
Definition: Xos2defs.h:11
int print_instructions(bool good, const astring &program_name)
Definition: checker.cpp:45
The application_shell is a base object for console programs.
const basis::astring & text() const
observes the string contents.
parameter_types type() const
observes the type of the parameter.
Definition: command_line.h:69
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
bool empty() const
empty() reports if the string is empty, that is, of zero length().
Definition: astring.h:90
int length() const
Returns the current length of the string.
Definition: astring.cpp:132
int find(char to_find, int position=0, bool reverse=false) const
Locates "to_find" in "this".
Definition: astring.cpp:574
virtual const char * observe() const
observes the underlying pointer to the zero-terminated string.
Definition: astring.cpp:140
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
Manages a bank of textual definitions of variables.
const structures::string_table & table() const
provides a constant peek at the string_table holding the values.
int symbols() const
returns the number of entries in the variable_tokenizer.
bool parse(const basis::astring &to_tokenize)
parses the string using our established sentinel characters.
Provides file managment services using the standard I/O support.
Definition: byte_filer.h:32
int write(const basis::abyte *buffer, int buffer_size)
writes "buffer_size" bytes into the file from "buffer".
Definition: byte_filer.cpp:126
void close()
shuts down the open file, if any.
Definition: byte_filer.cpp:96
bool set_time(const basis::astring &filename)
sets the time for the the "filename" to the currently held time.
Definition: file_time.cpp:59
virtual bool unpack(basis::byte_array &packed_form)
Definition: file_time.cpp:137
Provides operations commonly needed on file names.
Definition: filename.h:64
bool exists() const
returns true if the file exists.
Definition: filename.cpp:426
basis::astring rootname() const
returns the root part of the basename without an extension.
Definition: filename.cpp:308
const basis::astring & raw() const
returns the astring that we're holding onto for the path.
Definition: filename.cpp:97
filename dirname() const
returns the directory for the filename.
Definition: filename.cpp:393
combines a file_logger with a console logger, behaving like the 'tee' command.
Definition: combo_logger.h:27
A base class for a very usable logger with a filter_set and eol awareness.
bool member(const contents &to_test) const
Returns true if the item "to_test" is a member of this set.
Definition: set.h:223
Provides a symbol_table that holds strings as the content.
Definition: string_table.h:32
const basis::astring & name(int index) const
returns the name held at the "index".
Definition: symbol_table.h:272
@ TEST_VARIABLE_DEFINED
check for required variable's presence.
Definition: common_bundle.h:42
@ OMIT_PACKING
for a source side exe, do not pack the file.
Definition: common_bundle.h:36
@ SET_VARIABLE
this item just has a variable assignment.
Definition: common_bundle.h:37
@ QUIET_FAILURE
when errors happen, no popup message happens.
Definition: common_bundle.h:40
@ IGNORE_ERRORS
if set, errors in an item will not stop program.
Definition: common_bundle.h:38
@ TARGET_EXECUTE
the file should be executed on unbundling.
Definition: common_bundle.h:34
@ MAKE_BACKUP_FILE
save a copy if original file already exists.
Definition: common_bundle.h:41
@ NO_OVERWRITE
target file will not be overwritten if exists.
Definition: common_bundle.h:39
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:45
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition: hoople_main.h:61
Implements an application lock to ensure only one is running at once.
window_handle create_simplistic_window(const basis::astring &formal(window_title), const basis::astring &formal(class_name))
void whack_simplistic_window(window_handle formal(f_window))
char ** _global_argv
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition: functions.h:121
unsigned char abyte
A fairly important unit which is seldom defined...
Definition: definitions.h:51
unsigned int un_int
Abbreviated name for unsigned integers.
Definition: definitions.h:62
const int KILOBYTE
Number of bytes in a kilobyte.
Definition: definitions.h:134
A platform independent way to obtain the timestamp of a file.
Definition: byte_filer.cpp:37
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
bool obscure_detach(byte_array &packed_form, un_int &to_detach)
shifts the number back and checks validity, false returned if corrupted.
int packed_size(const byte_array &packed_form)
Reports the size required to pack a byte array into a byte array.
we will read the manifest pieces out of our own exe image.
Definition: common_bundle.h:54
basis::un_int _flags
uses the special_bundling_flags.
Definition: common_bundle.h:57
basis::astring _parms
the parameters to pass on the command line.
Definition: common_bundle.h:58
basis::astring _payload
guts of the chunk, such as location for file on target or a variable definition.
Definition: common_bundle.h:56
static bool read_an_obscured_int(filesystem::byte_filer &bundle, basis::un_int &found)
reads in our obscured packing format for an int, which takes 8 bytes.
static bool read_manifest(filesystem::byte_filer &bundle, manifest_chunk &to_fill)
reads a chunk out of the "bundle" and stores it in "to_fill".
basis::un_int _size
the size of the packed file.
Definition: common_bundle.h:55
virtual void text_form(basis::base_string &state_fill) const
Provides a text view of all the important info owned by this object.
Definition: common_bundle.h:82
basis::byte_array c_filetime
more than enough room for unix file time.
Definition: common_bundle.h:60
structures::string_set _keywords
keywords applicable to this item.
Definition: common_bundle.h:59
#define BASE_LOG(to_print)
abyte MANIFEST_OFFSET_ARRAY[]
const astring TARGET_WORD
const astring LOGDIR_WORD
const int CHUNKING_SIZE
#define LOG(to_print)
const char * ERROR_TITLE
astring find_unique_backup_name(astring original_file)
void show_message(const astring &msg, const astring &title)
Support for unicode builds.
void * window_handle