summaryrefslogtreecommitdiff
path: root/gprofng/src/gp-archive.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gprofng/src/gp-archive.cc')
-rw-r--r--gprofng/src/gp-archive.cc700
1 files changed, 700 insertions, 0 deletions
diff --git a/gprofng/src/gp-archive.cc b/gprofng/src/gp-archive.cc
new file mode 100644
index 00000000000..8d3fc239526
--- /dev/null
+++ b/gprofng/src/gp-archive.cc
@@ -0,0 +1,700 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+ Contributed by Oracle.
+
+ This file is part of GNU Binutils.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "config.h"
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "util.h"
+#include "StringMap.h"
+#include "LoadObject.h"
+#include "DbeSession.h"
+#include "DbeFile.h"
+#include "SourceFile.h"
+#include "Elf.h"
+#include "gp-archive.h"
+#include "ArchiveExp.h"
+#include "Print.h"
+#include "Module.h"
+
+er_archive::er_archive (int argc, char *argv[]) : DbeApplication (argc, argv)
+{
+ force = 0;
+ common_archive_dir = NULL;
+ quiet = 0;
+ descendant = 1;
+ use_relative_path = 0;
+ s_option = ARCH_EXE_ONLY;
+ mask = NULL;
+}
+
+er_archive::~er_archive ()
+{
+ if (mask)
+ {
+ for (long i = 0, sz = mask->size (); i < sz; i++)
+ {
+ regex_t *regex_desc = mask->get (i);
+ regfree (regex_desc);
+ delete regex_desc;
+ }
+ delete mask;
+ }
+ delete common_archive_dir;
+}
+
+int
+er_archive::mask_is_on (const char *str)
+{
+ if (mask == NULL)
+ return 1;
+ for (long i = 0, sz = mask->size (); i < sz; i++)
+ {
+ regex_t *regex_desc = mask->get (i);
+ if (regexec (regex_desc, str, 0, NULL, 0) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+void
+er_archive::usage ()
+{
+/*
+ fprintf (stderr, GTXT ("Usage: %s [-nqFV] [-a on|ldobjects|src|usedldobjects|usedsrc|off] [-m regexp] experiment\n"), whoami);
+*/
+
+/*
+ Ruud - Isolate this line because it has an argument. Otherwise it would be at the
+ end of this long list.
+*/
+ printf ( GTXT (
+ "Usage: gprofng archive [OPTION(S)] EXPERIMENT\n"));
+
+ printf ( GTXT (
+ "\n"
+ "Archive the associated application binaries and source files in a gprofng\n"
+ "experiment to make it self contained and portable.\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " --version print the version number and exit.\n"
+ " --help print usage information and exit.\n"
+ " --verbose {on|off} enable (on) or disable (off) verbose mode; the default is \"off\".\n"
+ "\n"
+ " -a {off|on|ldobjects|src|usedldobjects|usedsrc} specify archiving of binaries and other files;\n"
+ " in addition to disable this feature (off), or enable archiving off all\n"
+ " loadobjects and sources (on), the other options support a more\n"
+ " refined selection. All of these options enable archiving, but the\n"
+ " keyword controls what exactly is selected: all load objects (ldobjects),\n"
+ " all source files (src), the loadobjects asscoiated with a program counter\n"
+ " (usedldobjects), or the source files associated with a program counter\n"
+ " (usedsrc); the default is \"-a ldobjects\".\n"
+ "\n"
+ " -n archive the named experiment only, not any of its descendants.\n"
+ "\n"
+ " -q do not write any warnings to stderr; messages are archived and\n"
+ " can be retrieved later.\n"
+ "\n"
+ " -F force writing or rewriting of the archive; ignored with the -n\n"
+ " or -m options, or if this is a subexperiment.\n"
+ "\n"
+ " -d <path> specifies the location of a common archive; this is a directory that\n"
+ " contains archived files.\n"
+ "\n"
+ " -m <regex> archive only those source, object, and debug info files whose full\n"
+ " path name matches the given POSIX compliant regular expression.\n"
+ "\n"
+ "Limitations:\n"
+ "\n"
+ "Default archiving does not occur in case the application profiled terminates prematurely,\n"
+ "or if archiving is disabled when collecting the performance data. In such cases, this\n"
+ "tool can be used to afterwards archive the information, but it has to run on the same \n"
+ "system where the profiling data was recorded.\n"
+ "\n"
+ "Documentation:\n"
+ "\n"
+ "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n"
+ "gprofng programs are properly installed at your site, the command \"info gprofng\"\n"
+ "should give you access to this document.\n"
+ "\n"
+ "See also:\n"
+ "\n"
+ "gprofng(1), gp-collect-app(1), gp-display-html(1), gp-display-src(1), gp-display-text(1)\n"));
+// Ruud
+/*
+ fprintf (stderr, GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION);
+*/
+ exit (1);
+}
+
+Vector <LoadObject*> *
+er_archive::get_loadObjs ()
+{
+ Vector <LoadObject*> *objs = new Vector<LoadObject*>();
+ Vector <LoadObject*> *loadObjs = dbeSession->get_text_segments ();
+ if (s_option != ARCH_NOTHING)
+ {
+ for (long i = 0, sz = VecSize(loadObjs); i < sz; i++)
+ {
+ LoadObject *lo = loadObjs->get (i);
+ if ((lo->flags & SEG_FLAG_DYNAMIC) != 0)
+ continue;
+ DbeFile *df = lo->dbeFile;
+ if (df && ((df->filetype & DbeFile::F_FICTION) != 0))
+ continue;
+ if (!lo->isUsed && ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0))
+ continue;
+ objs->append (lo);
+ }
+ }
+ if (DEBUG_ARCHIVE)
+ {
+ Dprintf (DEBUG_ARCHIVE, NTXT ("get_text_segments(): %d\n"),
+ (int) (loadObjs ? loadObjs->size () : -1));
+ for (long i = 0, sz = loadObjs ? loadObjs->size () : 0; i < sz; i++)
+ {
+ LoadObject *lo = loadObjs->get (i);
+ Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d [%2ld] %s\n"),
+ get_basename (__FILE__), (int) __LINE__, i, STR (lo->dump ()));
+ }
+ Dprintf (DEBUG_ARCHIVE, NTXT ("\nget_loadObjs(): %d\n"),
+ (int) (objs ? objs->size () : -1));
+ for (long i = 0, sz = VecSize(objs); i < sz; i++)
+ {
+ LoadObject *lo = objs->get (i);
+ Dprintf (DEBUG_ARCHIVE, NTXT ("%s:%d [%2ld] %s\n"),
+ get_basename (__FILE__), (int) __LINE__, i, STR (lo->dump ()));
+ }
+ }
+ delete loadObjs;
+ return objs;
+}
+
+/**
+ * Clean old archive
+ * Except the following cases:
+ * 1. Founder experiment is an MPI experiment
+ * 2. "-n" option is passed (do not archive descendants)
+ * 3. "-m" option is passed (partial archiving)
+ * 4. Experiment name is not the founder experiment (it is a sub-experiment)
+ * @param expname
+ * @param founder_exp
+ * @return 0 - success
+ */
+int
+er_archive::clean_old_archive (char *expname, ArchiveExp *founder_exp)
+{
+ if (0 == descendant)
+ { // do not archive descendants
+ fprintf (stderr, GTXT ("Warning: Option -F is ignored because -n option is specified (do not archive descendants)\n"));
+ return 1;
+ }
+ if (NULL != mask)
+ { // partial archiving
+ fprintf (stderr, GTXT ("Warning: Option -F is ignored because -m option is specified\n"));
+ return 1;
+ }
+ // Check if the experiment is the founder
+ char *s1 = dbe_strdup (expname);
+ char *s2 = dbe_strdup (founder_exp->get_expt_name ());
+ if (!s1 || !s2)
+ {
+ fprintf (stderr, GTXT ("Cannot allocate memory\n"));
+ exit (1);
+ }
+ // remove trailing slashes
+ for (int n = strlen (s1); n > 0; n--)
+ {
+ if ('/' != s1[n - 1])
+ break;
+ s1[n - 1] = 0;
+ }
+ for (int n = strlen (s2); n > 0; n--)
+ {
+ if ('/' != s2[n - 1])
+ break;
+ s2[n - 1] = 0;
+ }
+ if (strcmp (s1, s2) != 0)
+ { // not founder
+ fprintf (stderr, GTXT ("Warning: Option -F is ignored because specified experiment name %s does not match founder experiment name %s\n"), s1, s2);
+ free (s1);
+ free (s2);
+ return 1;
+ }
+ // Remove old "archives"
+ char *arch = founder_exp->get_arch_name ();
+ fprintf (stderr, GTXT ("INFO: removing existing archive: %s\n"), arch);
+ if (dbe_stat (arch, NULL) == 0)
+ {
+ char *cmd = dbe_sprintf ("/bin/rm -rf %s", arch);
+ system (cmd);
+ free (cmd);
+ if (dbe_stat (arch, NULL) != 0)
+ { // create "archives"
+ if (!founder_exp->create_dir (founder_exp->get_arch_name ()))
+ {
+ fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ());
+ exit (1);
+ }
+ }
+ }
+ free (s1);
+ free (s2);
+ return 0;
+} // clean_old_archive_if_necessary
+
+void
+er_archive::start (int argc, char *argv[])
+{
+ int last = argc - 1;
+ if (check_args (argc, argv) != last)
+ usage ();
+ check_env_var ();
+ if (s_option == ARCH_NOTHING)
+ return;
+
+ ArchiveExp *founder_exp = new ArchiveExp (argv[last]);
+ if (founder_exp->get_status () == Experiment::FAILURE)
+ {
+ if (!quiet)
+ fprintf (stderr, GTXT ("er_archive: %s: %s\n"), argv[last],
+ pr_mesgs (founder_exp->fetch_errors (), NTXT (""), NTXT ("")));
+ exit (1);
+ }
+ if (!founder_exp->create_dir (founder_exp->get_arch_name ()))
+ {
+ fprintf (stderr, GTXT ("Unable to create directory `%s'\n"), founder_exp->get_arch_name ());
+ exit (1);
+ }
+ if (!common_archive_dir)
+ common_archive_dir = dbe_strdup (getenv ("GPROFNG_ARCHIVE_COMMON_DIR"));
+ if (common_archive_dir)
+ {
+ if (!founder_exp->create_dir (common_archive_dir))
+ if (dbe_stat (common_archive_dir, NULL) != 0)
+ {
+ fprintf (stderr, GTXT ("Unable to create directory for common archive `%s'\n"), common_archive_dir);
+ exit (1);
+ }
+ }
+ // Clean old archives if necessary
+ if (force)
+ clean_old_archive (argv[last], founder_exp);
+ Vector<ArchiveExp*> *exps = new Vector<ArchiveExp*>();
+ exps->append (founder_exp);
+ if (descendant)
+ {
+ Vector<char*> *exp_names = founder_exp->get_descendants_names ();
+ if (exp_names)
+ {
+ for (long i = 0, sz = exp_names->size (); i < sz; i++)
+ {
+ char *exp_path = exp_names->get (i);
+ ArchiveExp *exp = new ArchiveExp (exp_path);
+ if (exp->get_status () == Experiment::FAILURE)
+ {
+ if (!quiet)
+ fprintf (stderr, GTXT ("er_archive: %s: %s\n"), exp_path,
+ pr_mesgs (exp->fetch_errors (), NTXT (""), NTXT ("")));
+ delete exp;
+ continue;
+ }
+ exps->append (exp);
+ }
+ exp_names->destroy ();
+ delete exp_names;
+ }
+ }
+ for (long i = 0, sz = exps->size (); i < sz; i++)
+ {
+ ArchiveExp *exp = exps->get (i);
+ exp->read_data (s_option);
+ }
+
+ Vector <DbeFile*> *copy_files = new Vector<DbeFile*>();
+ Vector <LoadObject*> *loadObjs = get_loadObjs ();
+ for (long i = 0, sz = VecSize(loadObjs); i < sz; i++)
+ {
+ LoadObject *lo = loadObjs->get (i);
+ if (strcmp (lo->get_pathname (), "LinuxKernel") == 0)
+ continue;
+ DbeFile *df = lo->dbeFile;
+ if ((df->filetype & DbeFile::F_FICTION) != 0)
+ continue;
+ if (df->get_location () == NULL)
+ {
+ copy_files->append (df);
+ continue;
+ }
+ if ((df->filetype & DbeFile::F_JAVACLASS) != 0)
+ {
+ if (df->container)
+ { // Found in .jar file
+ copy_files->append (df->container);
+ }
+ copy_files->append (df);
+ if ((s_option & ARCH_EXE_ONLY) != 0)
+ continue;
+ }
+ lo->sync_read_stabs ();
+ Elf *elf = lo->get_elf ();
+ if (elf && (lo->checksum != 0) && (lo->checksum != elf->elf_checksum ()))
+ {
+ if (!quiet)
+ fprintf (stderr, GTXT ("er_archive: '%s' has an unexpected checksum value; perhaps it was rebuilt. File ignored\n"),
+ df->get_location ());
+ continue;
+ }
+ copy_files->append (df);
+ if (elf)
+ {
+ Elf *f = elf->find_ancillary_files (lo->get_pathname ());
+ if (f)
+ copy_files->append (f->dbeFile);
+ for (long i1 = 0, sz1 = VecSize(elf->ancillary_files); i1 < sz1; i1++)
+ {
+ Elf *ancElf = elf->ancillary_files->get (i1);
+ copy_files->append (ancElf->dbeFile);
+ }
+ }
+ Vector<Module*> *modules = lo->seg_modules;
+ for (long i1 = 0, sz1 = VecSize(modules); i1 < sz1; i1++)
+ {
+ Module *mod = modules->get (i1);
+ if ((mod->flags & MOD_FLAG_UNKNOWN) != 0)
+ continue;
+ else if ((s_option & (ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY)) != 0 &&
+ !mod->isUsed)
+ continue;
+ if ((s_option & ARCH_ALL) != 0)
+ mod->read_stabs (false); // Find all Sources
+ if (mod->dot_o_file && mod->dot_o_file->dbeFile)
+ copy_files->append (mod->dot_o_file->dbeFile);
+ }
+ }
+ delete loadObjs;
+
+ int bmask = DbeFile::F_LOADOBJ | DbeFile::F_JAVACLASS | DbeFile::F_JAR_FILE |
+ DbeFile::F_DOT_O | DbeFile::F_DEBUG_FILE;
+ if ((s_option & (ARCH_USED_SRC_ONLY | ARCH_ALL)) != 0)
+ {
+ bmask |= DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE;
+ Vector<SourceFile*> *sources = dbeSession->get_sources ();
+ for (long i = 0, sz = VecSize(sources); i < sz; i++)
+ {
+ SourceFile *src = sources->get (i);
+ if ((src->flags & SOURCE_FLAG_UNKNOWN) != 0)
+ continue;
+ else if ((s_option & ARCH_USED_SRC_ONLY) != 0)
+ {
+ if ((src->dbeFile->filetype & DbeFile::F_JAVA_SOURCE) != 0 &&
+ !src->isUsed)
+ continue;
+ }
+ if (src->dbeFile)
+ copy_files->append (src->dbeFile);
+ }
+ }
+
+ Vector <DbeFile*> *notfound_files = new Vector<DbeFile*>();
+ for (long i = 0, sz = VecSize(copy_files); i < sz; i++)
+ {
+ DbeFile *df = copy_files->get (i);
+ char *fnm = df->get_location ();
+ char *nm = df->get_name ();
+ Dprintf (DEBUG_ARCHIVE,
+ "%s::%d copy_files[%ld] filetype=%4d inArchive=%d '%s' --> '%s'\n",
+ get_basename (__FILE__), (int) __LINE__, i,
+ df->filetype, df->inArchive ? 1 : 0, STR (nm), STR (fnm));
+ Dprintf (DEBUG_ARCHIVE && df->container,
+ " copy_files[%ld]: Found '%s' in '%s'\n",
+ i, STR (nm), STR (df->container->get_name ()));
+ if (fnm == NULL)
+ {
+ if (!quiet)
+ notfound_files->append (df);
+ continue;
+ }
+ else if (df->inArchive)
+ {
+ Dprintf (DEBUG_ARCHIVE,
+ " NOT COPIED: copy_files[%ld]: inArchive=1 '%s'\n",
+ i, STR (nm));
+ continue;
+ }
+ else if ((df->filetype & bmask) == 0)
+ {
+ Dprintf (DEBUG_ARCHIVE,
+ " NOT COPIED: copy_files[%ld]: container=%p filetype=%d bmask=%d '%s'\n",
+ i, df->container, df->filetype, bmask, STR (nm));
+ continue;
+ }
+ else if (df->container &&
+ (df->filetype & (DbeFile::F_JAVA_SOURCE | DbeFile::F_SOURCE)) == 0)
+ {
+ Dprintf (DEBUG_ARCHIVE,
+ " NOT COPIED: copy_files[%ld]: container=%p filetype=%d bmask=%d '%s'\n",
+ i, df->container, df->filetype, bmask, STR (nm));
+ continue;
+ }
+ else if (!mask_is_on (df->get_name ()))
+ {
+ Dprintf (DEBUG_ARCHIVE,
+ " NOT COPIED: copy_files[%ld]: mask is off for '%s'\n",
+ i, STR (nm));
+ continue;
+ }
+ char *anm = founder_exp->getNameInArchive (nm, false);
+ if (force)
+ unlink (anm);
+ int res = founder_exp->copy_file (fnm, anm, quiet, common_archive_dir, use_relative_path);
+ if (0 == res) // file successfully archived
+ df->inArchive = 1;
+ delete anm;
+ }
+ delete copy_files;
+
+ if (notfound_files->size () > 0)
+ {
+ for (long i = 0, sz = notfound_files->size (); i < sz; i++)
+ {
+ DbeFile *df = notfound_files->get (i);
+ fprintf (stderr, GTXT ("er_archive: Cannot find file: `%s'\n"), df->get_name ());
+ }
+ fprintf (stderr, GTXT ("\n If you know the correct location of the missing file(s)"
+ " you can help %s to find them by manually editing the .gprofng.rc file."
+ " See %s man pages for more details.\n"),
+ whoami, whoami);
+ }
+ delete notfound_files;
+}
+
+int
+er_archive::check_args (int argc, char *argv[])
+{
+ int opt;
+ int rseen = 0;
+ int dseen = 0;
+ // Parsing the command line
+ opterr = 0;
+ optind = 1;
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {"whoami", required_argument, 0, 'w'},
+ {"outfile", required_argument, 0, 'O'},
+ {NULL, 0, 0, 0}
+ };
+ while (1)
+ {
+ int option_index = 0;
+ opt = getopt_long (argc, argv, NTXT (":VFa:d:qnr:m:"),
+ long_options, &option_index);
+ if (opt == EOF)
+ break;
+ switch (opt)
+ {
+ case 'F':
+ force = 1;
+ break;
+ case 'd': // Common archive directory (absolute path)
+ if (rseen)
+ {
+ fprintf (stderr, GTXT ("Error: invalid combination of options: -r and -d are in conflict.\n"));
+ return -1;
+ }
+ if (dseen)
+ fprintf (stderr, GTXT ("Warning: option -d was specified several times. Last value is used.\n"));
+ free (common_archive_dir);
+ common_archive_dir = strdup (optarg);
+ dseen = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'n':
+ descendant = 0;
+ break;
+ case 'r': // Common archive directory (relative path)
+ if (dseen)
+ {
+ fprintf (stderr, GTXT ("Error: invalid combination of options: -d and -r are in conflict.\n"));
+ return -1;
+ }
+ if (rseen)
+ fprintf (stderr, GTXT ("Warning: option -r was specified several times. Last value is used.\n"));
+ free (common_archive_dir);
+ common_archive_dir = strdup (optarg);
+ use_relative_path = 1;
+ rseen = 1;
+ break;
+ case 'a':
+ if (strcmp (optarg, "off") == 0)
+ s_option = ARCH_NOTHING;
+ else if (strcmp (optarg, "on") == 0 ||
+ strcmp (optarg, "ldobjects") == 0)
+ s_option = ARCH_EXE_ONLY;
+ else if (strcmp (optarg, "usedldobjects") == 0)
+ s_option = ARCH_USED_EXE_ONLY;
+ else if (strcmp (optarg, "usedsrc") == 0)
+ s_option = ARCH_USED_EXE_ONLY | ARCH_USED_SRC_ONLY;
+ else if (strcmp (optarg, "all") == 0 || strcmp (optarg, "src") == 0)
+ s_option = ARCH_ALL;
+ else
+ {
+ fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"),
+ optopt, optarg);
+ return -1;
+ }
+ break;
+ case 'm':
+ {
+ regex_t *regex_desc = new regex_t ();
+ if (regcomp (regex_desc, optarg, REG_EXTENDED | REG_NOSUB | REG_NEWLINE))
+ {
+ delete regex_desc;
+ fprintf (stderr, GTXT ("Error: invalid option: `-%c %s'\n"),
+ optopt, optarg);
+ return -1;
+ }
+ if (mask == NULL)
+ mask = new Vector<regex_t *>();
+ mask->append (regex_desc);
+ break;
+ }
+ case 'O':
+ {
+ int fd = open (optarg, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd == -1)
+ {
+ fprintf (stderr, GTXT ("er_archive: Can't open %s: %s\n"),
+ optarg, strerror (errno));
+ break;
+ }
+ if (dup2 (fd, 2) == -1)
+ {
+ close (fd);
+ fprintf (stderr, GTXT ("er_archive: Can't divert stderr: %s\n"),
+ strerror (errno));
+ break;
+ }
+ if (dup2 (fd, 1) == -1)
+ {
+ close (fd);
+ fprintf (stderr, GTXT ("er_archive: Can't divert stdout: %s\n"),
+ strerror (errno));
+ break;
+ }
+ close (fd);
+ struct timeval tp;
+ gettimeofday (&tp, NULL);
+ fprintf (stderr, "### Start %s#", ctime (&tp.tv_sec));
+ for (int i = 0; i < argc; i++)
+ fprintf (stderr, " %s", argv[i]);
+ fprintf (stderr, "\n");
+ break;
+ }
+ case 'V':
+// Ruud
+ Application::print_version_info ();
+/*
+ printf (GTXT ("GNU %s version %s\n"), get_basename (prog_name), VERSION);
+*/
+ exit (0);
+ case 'w':
+ whoami = optarg;
+ break;
+ case 'h':
+ usage ();
+ exit (0);
+ case ':': // -s -m without operand
+ fprintf (stderr, GTXT ("Option -%c requires an operand\n"), optopt);
+ return -1;
+ case '?':
+ default:
+ fprintf (stderr, GTXT ("Unrecognized option: -%c\n"), optopt);
+ return -1;
+ }
+ }
+ return optind;
+}
+
+void
+er_archive::check_env_var ()
+{
+ char *ename = NTXT ("GPROFNG_ARCHIVE");
+ char *var = getenv (ename);
+ if (var == NULL)
+ return;
+ var = dbe_strdup (var);
+ Vector<char*> *opts = new Vector<char*>();
+ opts->append (ename);
+ for (char *s = var;;)
+ {
+ while (*s && isblank (*s))
+ s++;
+ if (*s == 0)
+ break;
+ opts->append (s);
+ while (*s && !isblank (*s))
+ s++;
+ if (*s == 0)
+ break;
+ *s = 0;
+ s++;
+ }
+ if (opts->size () > 0)
+ {
+ char **arr = (char **) malloc (sizeof (char *) *opts->size ());
+ for (long i = 0; i < opts->size (); i++)
+ arr[i] = opts->get (i);
+ if (-1 == check_args (opts->size (), arr))
+ fprintf (stderr, GTXT ("Error: Wrong SP_ER_ARCHIVE: '%s'\n"), var);
+ free (arr);
+ }
+ delete opts;
+ free (var);
+}
+
+static int
+real_main (int argc, char *argv[])
+{
+ er_archive *archive = new er_archive (argc, argv);
+ dbeSession->archive_mode = 1;
+ archive->start (argc, argv);
+ dbeSession->unlink_tmp_files ();
+ return 0;
+}
+
+/**
+ * Call catch_out_of_memory(int (*real_main)(int, char*[]), int argc, char *argv[]) which will call real_main()
+ * @param argc
+ * @param argv
+ * @return
+ */
+int
+main (int argc, char *argv[])
+{
+ return catch_out_of_memory (real_main, argc, argv);
+}