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>
29#include <filesystem/filename.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
55using namespace application;
56using namespace basis;
57using namespace configuration;
58using namespace filesystem;
59using namespace loggers;
60using namespace processes;
61using namespace structures;
62using namespace textual;
63
64const int CHUNKING_SIZE = 64 * KILOBYTE;
65 // we'll read this big a chunk from a source file at a time.
66
67const astring TARGET_WORD = "TARGET";
68const 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
76const char *ERROR_TITLE = "An Error Caused Incomplete Installation";
77 // used in error messages as the title.
78
80
81class unpacker_stub : public application_shell
82{
83public:
84 unpacker_stub() : application_shell(), _app_name(filename(_global_argv[0]).basename()) {}
85 DEFINE_CLASS_NAME("unpacker_stub");
86
88
89 virtual int execute();
90
91private:
92 astring _app_name;
93 array<manifest_chunk> _manifest;
94 string_table _variables;
95};
96
98
99void 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
111int unpacker_stub::print_instructions()
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\
116different %s directory for the installation (for components that use\n\
117the 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\
119bundle that are marked with that keyword will be installed, but files that\n\
120are missing the keyword will not be.\n\
121 Further, variables can be overridden on the command line in the\n\
122form: X=Y.\n\n\
123The 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\
126Additional Notes:\n\
127\n\
128 One helpful variable is \"%s\". This is where the unpacking log file\n\
129will be written to. The default is \"~/logs\" (or \"$TMP/logs\" on win32)\n\
130until 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{
142const 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
166int 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);
205 provided_target = true;
206 }
207
208 {
210#ifdef __WIN32__
211 if (!logdir) {
212 logdir = environment::TMP() + "/logs";
214 }
215#else
216 if (!logdir) {
217 astring homedir = environment::get("HOME");
218 logdir = homedir + "/logs";
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
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.
250
252
253 astring logname = logdir + "/" + appname + ".log";
254// log_base *old_log = set_PW_logger_for_combo(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],
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(),
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:");
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.
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;
376 astring logname = logdir + "/" + appname + ".log";
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),
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);
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;
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
612HOOPLE_MAIN(unpacker_stub, )
613
614#ifdef __BUILD_STATIC_APPLICATION__
615 // static dependencies found by buildor_gen_deps.sh:
616 #include <algorithms/sorts.cpp>
621 #include <basis/astring.cpp>
623 #include <basis/environment.cpp>
624 #include <basis/guards.cpp>
625 #include <basis/mutex.cpp>
626 #include <basis/utf_conversion.cpp>
634 #include <filesystem/directory.cpp>
635 #include <filesystem/file_info.cpp>
636 #include <filesystem/filename.cpp>
638 #include <filesystem/file_time.cpp>
640 #include <filesystem/huge_file.cpp>
641 #include <loggers/combo_logger.cpp>
644 #include <loggers/file_logger.cpp>
648 #include <structures/checksums.cpp>
655 #include <textual/parser_bits.cpp>
657 #include <timely/earth_time.cpp>
658 #include <timely/time_control.cpp>
659 #include <timely/time_stamp.cpp>
660#endif // __BUILD_STATIC_APPLICATION__
661
#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.
virtual int execute()=0
< retrieves the command line from the /proc hierarchy on linux.
application_shell()
constructs an application_shell to serve as the root of the program.
const basis::astring & text() const
observes the string contents.
parameter_types type() const
observes the type of the parameter.
a_sprintf is a specialization of astring that provides printf style support.
Definition astring.h:440
Represents a sequential, ordered, contiguous collection of objects.
Definition array.h:54
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:577
A very common template for a dynamic array of bytes.
Definition byte_array.h:36
static astring TMP()
provides a single place to get the temporary directory.
static astring get(const astring &variable_name)
looks up the "variable_name" in the current environment variables.
static bool set(const astring &variable_name, const astring &value)
adds or creates "variable_name" in the environment.
static basis::astring application_name()
returns the full name of the current application.
static basis::astring current_directory()
returns the current directory as reported by the operating system.
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".
void close()
shuts down the open file, if any.
static bool recursive_create(const basis::astring &directory_name)
returns true if the "directory_name" can be created or already exists.
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)
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.
static void alert_message(const char *info, const char *title="Alert Message")
shows the message in "info", with an optional "title" on the message.
static loggers::standard_log_base * set(loggers::standard_log_base *new_log)
replaces the current program-wide logger with the "new_log".
A base class for a very usable logger with a filter_set and eol awareness.
@ AWAIT_APP_EXIT
stays in the function until the launched application has exited.
@ HIDE_APP_WINDOW
launches the application invisibly if possible.
static basis::un_int run(const basis::astring &app_name, const basis::astring &command_line, int flag, basis::un_int &child_id)
starts an application using the "app_name" as the executable to run.
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.
const basis::astring & name(int index) const
returns the name held at the "index".
static basis::astring substitute_env_vars(const basis::astring &text, bool leave_unknown=true)
resolves embedded environment variables in "text".
static const char * platform_eol_to_chars()
provides the characters that make up this platform's line ending.
@ TEST_VARIABLE_DEFINED
check for required variable's presence.
@ OMIT_PACKING
for a source side exe, do not pack the file.
@ SET_VARIABLE
this item just has a variable assignment.
@ QUIET_FAILURE
when errors happen, no popup message happens.
@ IGNORE_ERRORS
if set, errors in an item will not stop program.
@ TARGET_EXECUTE
the file should be executed on unbundling.
@ MAKE_BACKUP_FILE
save a copy if original file already exists.
@ NO_OVERWRITE
target file will not be overwritten if exists.
#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:42
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.
A platform independent way to obtain the timestamp of a file.
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.
basis::un_int _flags
uses the special_bundling_flags.
basis::astring _parms
the parameters to pass on the command line.
basis::astring _payload
guts of the chunk, such as location for file on target or a variable definition.
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.
virtual void text_form(basis::base_string &state_fill) const
Provides a text view of all the important info owned by this object.
basis::byte_array c_filetime
more than enough room for unix file time.
structures::string_set _keywords
keywords applicable to this item.
#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