Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
xpather.cc 85.51 KiB
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2000-2014 Ericsson Telecom AB
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
///////////////////////////////////////////////////////////////////////////////
#include "xpather.h"

#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string>

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>

#define LIBXML_SCHEMAS_ENABLED
#include <libxml/xmlschemastypes.h>

#include "../common/memory.h"
#include "vector.hh"
// Do _NOT_ #include "string.hh", it drags in ustring.o, common/Quadruple.o,
// Int.o, ttcn3/PatternString.o, and then the entire AST :(
#include "map.hh"
#include "ProjectGenHelper.hh"
#include "../common/path.h"
#include "ttcn3/ttcn3_preparser.h"
#include "asn1/asn1_preparser.h"

// in makefile.c
void ERROR  (const char *fmt, ...);
void WARNING(const char *fmt, ...);
void NOTIFY (const char *fmt, ...);
void DEBUG  (const char *fmt, ...);

// for vector and map
void fatal_error(const char * filename, int lineno, const char * fmt, ...)
__attribute__ ((__format__ (__printf__, 3, 4), __noreturn__));

void fatal_error(const char * filename, int lineno, const char * fmt, ...)
{
  fputs(filename, stderr);
  fprintf(stderr, ":%d: ", lineno);
  va_list va;
  va_start(va, fmt);
  vfprintf(stderr, fmt, va);
  va_end(va);
  abort();
}

ProjectGenHelper& projGenHelper = ProjectGenHelper::Instance();

/// Run an XPath query and return an xmlXPathObjectPtr, which must be freed
xmlXPathObjectPtr run_xpath(xmlXPathContextPtr xpathCtx, const char *xpathExpr)
{
  xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression(
    (const xmlChar *)xpathExpr, xpathCtx);
  if(xpathObj == NULL) {
    fprintf(stderr,"Error: unable to evaluate xpath expression \"%s\"\n", xpathExpr);
    return 0;
  }

  return xpathObj;
}

// RAII classes

class XmlDoc {
public:
  explicit XmlDoc(xmlDocPtr p) : doc_(p) {}
  ~XmlDoc() {
    if (doc_ != NULL) xmlFreeDoc(doc_);
  }
  operator xmlDocPtr() const { return doc_; }
private:
  xmlDocPtr doc_;
};

class XPathContext {
public:
  explicit XPathContext(xmlXPathContextPtr c) : ctx_(c) {}
  ~XPathContext() {
    if (ctx_ != NULL) xmlXPathFreeContext(ctx_);
  }
  operator xmlXPathContextPtr() const { return ctx_; }
private:
  xmlXPathContextPtr ctx_;
};

class XPathObject {
public:
  explicit XPathObject(xmlXPathObjectPtr o) : xpo_(o) {}
  ~XPathObject() {
    if (xpo_ != NULL) xmlXPathFreeObject(xpo_);
  }
  operator   xmlXPathObjectPtr() const { return xpo_; }
  xmlXPathObjectPtr operator->() const { return xpo_; }
private:
  xmlXPathObjectPtr xpo_;
};

//------------------------------------------------------------------
/// compare-by-content wrapper of a plain C string
struct cstring {
  explicit cstring(const char *s) : str(s) {}
  void destroy() const;
  operator const char*() const { return str; }
protected:
  const char *str;
  friend boolean operator<(const cstring& l, const cstring& r);
  friend boolean operator==(const cstring& l, const cstring& r);
};

void cstring::destroy() const {
  Free(const_cast<char*>(str)); // assumes valid pointer or NULL
}

boolean operator<(const cstring& l, const cstring& r) {
  return strcmp(l.str, r.str) < 0;
}

boolean operator==(const cstring& l, const cstring& r) {
  return strcmp(l.str, r.str) == 0;
}

/// RAII for C string
struct autostring : public cstring {
  /// Constructor; takes over ownership
  explicit autostring(const char *s = 0) : cstring(s) {}
  ~autostring() {
    // He who can destroy a thing, controls that thing -- Paul Muad'Dib
    Free(const_cast<char*>(str)); // assumes valid pointer or NULL
  }
  /// %Assignment; takes over ownership
  const autostring& operator=(const char *s) {
    Free(const_cast<char*>(str)); // assumes valid pointer or NULL
    str = s;
    return *this;
  }
  /// Relinquish ownership
  const char *extract() {
    const char *retval = str;
    str = 0;
    return retval;
  }
private:
  autostring(const autostring&);
  autostring& operator=(const autostring&);
};


bool validate_tpd(const XmlDoc& xml_doc, const char* tpd_file_name, const char* xsd_file_name)
{
  xmlLineNumbersDefault(1);

  xmlSchemaParserCtxtPtr ctxt = xmlSchemaNewParserCtxt(xsd_file_name);
  if (ctxt==NULL) {
    ERROR("Unable to create xsd context for xsd file `%s'", xsd_file_name);
    return false;
  }
  xmlSchemaSetParserErrors(ctxt, (xmlSchemaValidityErrorFunc)fprintf, (xmlSchemaValidityWarningFunc)fprintf, stderr);

  xmlSchemaPtr schema = xmlSchemaParse(ctxt);
  if (schema==NULL) {
    ERROR("Unable to parse xsd file `%s'", xsd_file_name);
    xmlSchemaFreeParserCtxt(ctxt);
    return false;
  }

  xmlSchemaValidCtxtPtr xsd = xmlSchemaNewValidCtxt(schema);
  if (xsd==NULL) {
    ERROR("Schema validation error for xsd file `%s'", xsd_file_name);
    xmlSchemaFree(schema);
    xmlSchemaFreeParserCtxt(ctxt);
    return false;
  }
  xmlSchemaSetValidErrors(xsd, (xmlSchemaValidityErrorFunc) fprintf, (xmlSchemaValidityWarningFunc) fprintf, stderr);

  int ret = xmlSchemaValidateDoc(xsd, xml_doc);

  xmlSchemaFreeValidCtxt(xsd);
  xmlSchemaFree(schema);
  xmlSchemaFreeParserCtxt(ctxt);
  xmlSchemaCleanupTypes();

  if (ret==0) {
    return true; // successful validation
  } else if (ret>0) {
    ERROR("TPD file `%s' is invalid according to schema `%s'", tpd_file_name, xsd_file_name);
    return false;
  } else {
    ERROR("TPD validation of `%s' generated an internal error in libxml2", tpd_file_name);
    return false;
  }
}

/** Extract a boolean value from the XML, if it exists otherwise flag is unchanged
 *
 * @param xpathCtx XPath context object
 * @param actcfg name of the active configuration
 * @param option name of the value
 * @param flag pointer to the variable to receive the value
 */
void xsdbool2boolean(const XPathContext& xpathCtx, const char *actcfg,
  const char *option, boolean* flag)
{
  char *xpath = mprintf(
    "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
    "/ProjectProperties/MakefileSettings/%s[text()='true']",
    actcfg, option);
  XPathObject xpathObj(run_xpath(xpathCtx, xpath));
  Free(xpath);

  if (xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0) {
    *flag = TRUE;
  }
}

extern "C" string_list* getExternalLibs(const char* projName)
{
  if (!projGenHelper.getZflag()) return NULL;
  ProjectDescriptor* proj = projGenHelper.getTargetOfProject(projName);
  if (!proj) return NULL;

  std::vector<const char*> externalLibs;
  projGenHelper.getExternalLibs(externalLibs);

  if (0 == externalLibs.size()) return NULL;

  struct string_list* head = (struct string_list*)Malloc(sizeof(struct string_list));
  struct string_list* last_elem = head;
  struct string_list* tail = head;

  for (size_t i = 0; i < externalLibs.size(); ++i) {
     tail = last_elem;
     last_elem->str = mcopystr(externalLibs[i]);
     last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
     last_elem = last_elem->next;
  }
  Free(last_elem);
  tail->next = NULL;
  return head;
}

extern "C" string_list* getExternalLibPathes(const char* projName)
{
  if (!projGenHelper.getZflag()) return NULL;
  ProjectDescriptor* proj = projGenHelper.getTargetOfProject(projName);
  if (!proj) return NULL;

  std::vector<const char*> externalLibs;
  projGenHelper.getExternalLibSearchPathes(externalLibs);

  if (0 == externalLibs.size()) return NULL;

  struct string_list* head = (struct string_list*)Malloc(sizeof(struct string_list));
  struct string_list* last_elem = head;
  struct string_list* tail = head;

  for (size_t i = 0; i < externalLibs.size(); ++i) {
     tail = last_elem;
     last_elem->str = mcopystr(externalLibs[i]);
     last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
     last_elem = last_elem->next;
  }
  Free(last_elem);
  tail->next = NULL;
  return head;
}

extern "C" string_list* getRefWorkingDirs(const char* projName)
{
  if (!projGenHelper.getZflag()) return NULL;
  ProjectDescriptor* proj = projGenHelper.getTargetOfProject(projName);
  if (!proj) FATAL_ERROR("Project \"%s\" was not found in the project list", projName);

  struct string_list* head = (struct string_list*)Malloc(sizeof(struct string_list));
  struct string_list* last_elem = head;
  struct string_list* tail = head;
  last_elem->str = NULL;
  last_elem->next = NULL;
  for (size_t i = 0; i < proj->numOfRefProjWorkingDirs(); ++i) {
    tail = last_elem;
    last_elem->str = mcopystr(proj->getRefProjWorkingDir(i).c_str());
    last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
    last_elem = last_elem->next;
  }
  Free(last_elem);
  tail->next = NULL;
  return head;
}

extern "C" string2_list* getLinkerLibs(const char* projName)
{

  if (!projGenHelper.getZflag()) return NULL;
  if (1 == projGenHelper.numOfProjects() || 0 == projGenHelper.numOfLibs()){
    return NULL; //no library
  }
  ProjectDescriptor* projLib = projGenHelper.getTargetOfProject(projName);
  if (!projLib) FATAL_ERROR("Project \"%s\" was not found in the project list", projName);

  struct string2_list* head = (struct string2_list*)Malloc(sizeof(struct string2_list));
  struct string2_list* last_elem = head;
  struct string2_list* tail = head;
  last_elem->next = NULL;
  last_elem->str1 = NULL;
  last_elem->str2 = NULL;
  for (std::map<std::string, ProjectDescriptor>::const_iterator it = projGenHelper.getHead();
       it != projGenHelper.getEnd(); ++it) {
    if ((it->second).isLibrary()) {
      if (!(it->second).getLinkingStrategy() &&
          !projLib->hasLinkerLibTo((it->second).getProjectName())) { // static linked library
          continue;
      }
      std::string relPath = projLib->setRelativePathTo((it->second).getProjectAbsWorkingDir());
      if (relPath == std::string(".")) {
        continue; // the relpath shows to itself
      }
      tail = last_elem;
      last_elem->str1 = mcopystr(relPath.c_str());
      last_elem->str2 = mcopystr((it->second).getTargetExecName().c_str());
      last_elem->next = (struct string2_list*)Malloc(sizeof(struct string2_list));
      last_elem = last_elem->next;
    }
  }
  tail->next = NULL;
  Free(last_elem);

  if (head->str1 && head->str2)
    return head;
  else
    return NULL;
}

extern "C" const char* getLibFromProject(const char* projName)
{
  if (!projGenHelper.getZflag()) return NULL;
  ProjectDescriptor* lib = projGenHelper.getTargetOfProject(projName);
  if (lib) return lib->getTargetExecName().c_str();
  return NULL;
}
extern "C" void erase_libs() {
  projGenHelper.cleanUp();
}

extern "C" void print_libs() {
  projGenHelper.print();
}


extern "C" boolean hasSubProject(const char* projName) {
  if (!projGenHelper.getZflag()) return FALSE;
  if (projGenHelper.getHflag())
    return static_cast<boolean>(projGenHelper.hasReferencedProject());
  else if(std::string(projName) == projGenHelper.getToplevelProjectName())
    return static_cast<boolean>(projGenHelper.hasReferencedProject());
  else
    return FALSE;
}

extern "C" boolean hasExternalLibrary(const char* libName, const char* projName) {
  if (!projGenHelper.getZflag()) return FALSE;
  ProjectDescriptor* projLib = projGenHelper.getTargetOfProject(projName);
  if (projLib && projLib->hasLinkerLib(libName))
    return TRUE;
  else
    return FALSE;
}

extern "C" boolean isTopLevelExecutable(const char* projName) {
  if (!projGenHelper.getZflag()) return false;
  ProjectDescriptor* proj = projGenHelper.getTargetOfProject(projName);
  if (projGenHelper.getToplevelProjectName() != std::string(projName)) return FALSE;
  if (proj && proj->isLibrary())
    return FALSE;
  else
    return TRUE;
}

extern "C" boolean isDynamicLibrary(const char* key) {
  if (!projGenHelper.getZflag()) return false;
  ProjectDescriptor* proj = projGenHelper.getProjectDescriptor(key);
  if (proj) return proj->getLinkingStrategy();
  FATAL_ERROR("Library \"%s\" was not found", key);
  return false;
}

extern "C" const char* getTPDFileName(const char* projName) {
  if (!projGenHelper.getZflag()) return NULL;
  ProjectDescriptor* proj = projGenHelper.getTargetOfProject(projName);
  if (proj) return proj->getTPDFileName().c_str();
  FATAL_ERROR("TPD file name to project \"%s\" was not found", projName);
}

extern "C" const char* getPathToRootDir(const char* projName) {
  if (!projGenHelper.getZflag()) return NULL;
  ProjectDescriptor* proj = projGenHelper.getTargetOfProject(projName);
  const char* rootDir = projGenHelper.getRootDirOS(projName).c_str();
  if (proj && rootDir) {
    return rootDir;
  }
  FATAL_ERROR("Project \"%s\": no relative path was found to top directory at OS level.", projName);
}

extern "C" const char* findLibraryPath(const char* libraryName, const char* projName)
{
  if (!projGenHelper.getZflag()) return NULL;
  ProjectDescriptor* projLib = projGenHelper.getTargetOfProject(projName);
  if (!projLib) FATAL_ERROR("Project \"%s\" was not found in the project list", projName);
  ProjectDescriptor* libLib = projGenHelper.getProjectDescriptor(libraryName);
  if (!libLib) return NULL;
  std::string str = projLib->setRelativePathTo(libLib->getProjectAbsWorkingDir());
  size_t refIndex = projLib->getLibSearchPathIndex(libLib->getProjectName());
  if (refIndex > projLib->numOfLibSearchPaths()) return NULL;
  projLib->setLibSearchPath(refIndex, str);
  return projLib->getLibSearchPath(libLib->getProjectName());
}

extern "C" const char* findLibraryName(const char* libraryName, const char* projName)
{
  if (!projGenHelper.getZflag()) return NULL;
  ProjectDescriptor* projLib = projGenHelper.getTargetOfProject(projName);
  if (!projLib) FATAL_ERROR("Project \"%s\" was not found in the project list", projName);
  ProjectDescriptor* libLib = projGenHelper.getProjectDescriptor(libraryName);
  if (!libLib) return NULL;
  for (size_t i = 0; i < projLib->numOfReferencedProjects(); ++i) {
    const std:: string refProjName = projLib->getReferencedProject(i);
    ProjectDescriptor* refLib = projGenHelper.getTargetOfProject(refProjName.c_str());
    if (refLib->getTargetExecName() == std::string(libraryName))
      return libraryName;
  }
  return NULL;
}

extern "C" boolean isTtcn3ModuleInLibrary(const char* moduleName)
{
  if (!projGenHelper.getZflag()) return FALSE;
  return (boolean)projGenHelper.isTtcn3ModuleInLibrary(moduleName);
}

extern "C" boolean isAsn1ModuleInLibrary(const char* moduleName)
{
  if (!projGenHelper.getZflag()) return FALSE;
  return (boolean)projGenHelper.isAsn1ModuleInLibrary(moduleName);
}

extern "C" boolean isSourceFileInLibrary(const char* fileName)
{
  if (!projGenHelper.getZflag()) return FALSE;
  return (boolean)projGenHelper.isSourceFileInLibrary(fileName);
}

extern "C" boolean isHeaderFileInLibrary(const char* fileName)
{
  if (!projGenHelper.getZflag()) return FALSE;
  return (boolean)projGenHelper.isHeaderFileInLibrary(fileName);
}

extern "C" boolean isTtcnPPFileInLibrary(const char* fileName)
{
  if (!projGenHelper.getZflag()) return FALSE;
  return (boolean)projGenHelper.isTtcnPPFileInLibrary(fileName);
}


extern "C" boolean buildObjects(const char* projName, boolean add_referenced)
{
  if (!projGenHelper.getZflag()) return FALSE;
  if (projGenHelper.getHflag()) return FALSE;
  if (add_referenced) return FALSE;
  ProjectDescriptor* desc =projGenHelper.getTargetOfProject(projName);
  if (desc && desc->isLibrary()) return FALSE;
  return TRUE;
}

void append_to_library_list (const char* prjName,
                             const XPathContext& xpathCtx,
                             const char *actcfg)
{
  if (!projGenHelper.getZflag()) return;
  char *exeXpath = mprintf(
    "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
    "/ProjectProperties/MakefileSettings/targetExecutable/text()",
    actcfg);
  XPathObject exeObj(run_xpath(xpathCtx, exeXpath));
  Free(exeXpath);
  std::string lib_name;
  if (exeObj->nodesetval && exeObj->nodesetval->nodeNr > 0) {
    const char* target_executable = (const char*)exeObj->nodesetval->nodeTab[0]->content;
    autostring target_exe_dir(get_dir_from_path(target_executable));
    autostring target_exe_file(get_file_from_path(target_executable));
    lib_name = target_exe_file;
    ProjectDescriptor* projDesc = projGenHelper.getTargetOfProject(prjName);
    if (projDesc) {
      projDesc->setTargetExecName(lib_name.c_str());
    }
  }
}

// data structures and functions to manage excluded folders/files

map<cstring, const char> excluded_files;

boolean is_excluded_file(const cstring& path, const char* project) {
  if (!excluded_files.has_key(path)) return false;
  const char* proj = excluded_files[path];
  if (0 == strcmp(project, proj)) return true;
  return false;
}

vector<const char> excluded_folders;

// Unfortunately, when "docs" is excluded, we need to drop
// files in "docs/", "docs/pdf/", "docs/txt/", "docs/txt/old/" etc;
// so it's not as simple as using a map :(

/** Checks whether a file is under an excluded folder
 *
 * @param path (relative) path of the file
 * @return true if file is excluded, false otherwise
 */
boolean is_excluded_folder(const char *path) {
  boolean answer = FALSE;
  size_t pathlen = strlen(path);

  for (size_t i = 0, end = excluded_folders.size(); i < end; ++i) {
    const char *xdir = excluded_folders[i];
    size_t xdlen = strlen(xdir);
    if (pathlen > xdlen && path[xdlen] == '/') {
      // we may have a winner
      if ((answer = !strncmp(path, xdir, xdlen))) break;
    }
  }
  return answer;
}

// How do you treat a raw info? You cook it, of course!
// Returns a newly allocated string.
char *cook(const char *raw, const map<cstring, const char>& path_vars)
{
  const char *slash = strchr(raw, '/');
  if (!slash) { // Pretend that the slash is at the end of the string.
    slash = raw + strlen(raw);
  }

  // Assume that a path variable reference is the first (or only) component
  // of the path: ROOT in "ROOT/etc/issue".
  autostring prefix(mcopystrn(raw, slash - raw));
  if (path_vars.has_key(prefix)) {
    char *cooked = mcopystr(path_vars[prefix]);
    bool ends_with_slash = cooked[strlen(cooked)-1] == '/';
    if (ends_with_slash && *slash == '/') {
      // Avoid paths with two slashes at the start; Cygwin thinks it's UNC
      ++slash;
    }
    // If there was no '/' (only the path variable reference e.g "ROOT")
    // then slash points to a null byte and the mputstr is a no-op.
    cooked = mputstr(cooked, slash);
    return cooked;
  }

  // If the path variable could not be substituted,
  // return (a copy of) the original.
  return mcopystr(raw);
}

void replacechar(char** content) {

  std::string s= *content;
  size_t found = 0;
  while ((found = s.find('['))!= std::string::npos){
    s.replace(found,1, "${");
  }
  while ((found = s.find(']')) != std::string::npos){
    s.replace(found,1, "}");
  }
  *content = mcopystr(s.c_str());
}

static void clear_seen_tpd_files(map<cstring, int>& seen_tpd_files) {
  for (size_t i = 0, num = seen_tpd_files.size(); i < num; ++i) {
    const cstring& key = seen_tpd_files.get_nth_key(i);
    int *elem = seen_tpd_files.get_nth_elem(i);
    key.destroy();
    delete elem;
  }
  seen_tpd_files.clear();
}

const char* get_act_config(struct string2_list* cfg, const char* project_name) {
  while (cfg && cfg->str1 && project_name) {
    if (!strcmp(cfg->str1, project_name)) return cfg->str2;
    cfg = cfg->next;
  }
  return NULL;
}

static tpd_result process_tpd_internal(const char *p_tpd_name, const char *actcfg,
  const char *file_list_path, int *p_argc, char ***p_argv,
  int *p_optind, char **p_ets_name, char **p_project_name,
  boolean *p_gflag, boolean *p_sflag, boolean *p_cflag, boolean *p_aflag, boolean *preprocess,
  boolean *p_Rflag, boolean *p_lflag, boolean *p_mflag, boolean *p_Pflag,
  boolean *p_Lflag, boolean recursive, boolean force_overwrite, boolean gen_only_top_level,
  const char *output_file, char** abs_work_dir_p, struct string_list* sub_project_dirs,
  const char* program_name, FILE* prj_graph_fp, struct string2_list* create_symlink_list, struct string_list* ttcn3_prep_includes,
  struct string_list* ttcn3_prep_defines, struct string_list* ttcn3_prep_undefines, struct string_list* prep_includes,
  struct string_list* prep_defines, struct string_list* prep_undefines, boolean *p_csflag, boolean *p_quflag, boolean* p_dsflag,
  char** cxxcompiler, char** optlevel, char** optflags, boolean* p_dbflag, boolean* p_drflag, boolean* p_dtflag, boolean* p_dxflag,
  boolean* p_djflag, boolean* p_fxflag, boolean* p_doflag, boolean* p_gfflag, boolean* p_lnflag, boolean* p_isflag,
  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
  struct string_list* linuxspeclibs, struct string_list* freebsdspeclibs, struct string_list* win32speclibs, char** ttcn3prep,
  struct string_list* linkerlibs, struct string_list* additionalObjects, struct string_list* linkerlibsearchp, boolean Vflag, boolean Dflag,
  boolean *p_Zflag, boolean *p_Hflag, char** generatorCommandOutput, struct string2_list* target_placement_list, boolean prefix_workdir, 
  struct string2_list* run_command_list, map<cstring, int>& seen_tpd_files, struct string2_list* required_configs);

extern "C" tpd_result process_tpd(const char *p_tpd_name, const char *actcfg,
  const char *file_list_path, int *p_argc, char ***p_argv,
  int *p_optind, char **p_ets_name, char **p_project_name,
  boolean *p_gflag, boolean *p_sflag, boolean *p_cflag, boolean *p_aflag, boolean *preprocess,
  boolean *p_Rflag, boolean *p_lflag, boolean *p_mflag, boolean *p_Pflag,
  boolean *p_Lflag, boolean recursive, boolean force_overwrite, boolean gen_only_top_level,
  const char *output_file, char** abs_work_dir_p, struct string_list* sub_project_dirs,
  const char* program_name, FILE* prj_graph_fp, struct string2_list* create_symlink_list, struct string_list* ttcn3_prep_includes,
  struct string_list* ttcn3_prep_defines, struct string_list* ttcn3_prep_undefines, struct string_list* prep_includes, 
  struct string_list* prep_defines, struct string_list* prep_undefines, boolean *p_csflag, boolean *p_quflag, boolean* p_dsflag,
  char** cxxcompiler, char** optlevel, char** optflags, boolean* p_dbflag, boolean* p_drflag, boolean* p_dtflag, boolean* p_dxflag, 
  boolean* p_djflag, boolean* p_fxflag, boolean* p_doflag, boolean* p_gfflag, boolean* p_lnflag, boolean* p_isflag,
  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
  struct string_list* linuxspeclibs, struct string_list* freebsdspeclibs, struct string_list* win32speclibs, char** ttcn3prep,
  string_list* linkerlibs, string_list* additionalObjects, string_list* linkerlibsearchp, boolean Vflag, boolean Dflag, boolean *p_Zflag,
  boolean *p_Hflag, char** generatorCommandOutput, struct string2_list* target_placement_list, boolean prefix_workdir,
  struct string2_list* run_command_list, struct string2_list* required_configs) {

  map<cstring, int> seen_tpd_files;
  projGenHelper.setZflag(*p_Zflag);
  projGenHelper.setWflag(prefix_workdir);
  projGenHelper.setHflag(*p_Hflag);

  tpd_result success = process_tpd_internal(p_tpd_name,
      actcfg, file_list_path, p_argc, p_argv, p_optind, p_ets_name, p_project_name,
      p_gflag, p_sflag, p_cflag, p_aflag, preprocess,
      p_Rflag, p_lflag, p_mflag, p_Pflag,
      p_Lflag, recursive, force_overwrite, gen_only_top_level,
      output_file, abs_work_dir_p, sub_project_dirs,
      program_name, prj_graph_fp, create_symlink_list, ttcn3_prep_includes,
      ttcn3_prep_defines, ttcn3_prep_undefines, prep_includes, prep_defines,
      prep_undefines, p_csflag, p_quflag, p_dsflag, cxxcompiler,
      optlevel, optflags, p_dbflag, p_drflag, p_dtflag, p_dxflag, p_djflag,
      p_fxflag, p_doflag, p_gfflag, p_lnflag, p_isflag,
      p_asflag, p_swflag, p_Yflag, solspeclibs, sol8speclibs,
      linuxspeclibs, freebsdspeclibs, win32speclibs, ttcn3prep,
      linkerlibs, additionalObjects, linkerlibsearchp, Vflag, Dflag, p_Zflag,
      p_Hflag, generatorCommandOutput, target_placement_list, prefix_workdir, 
      run_command_list, seen_tpd_files, required_configs);

  if (TPD_FAILED == success) exit(EXIT_FAILURE);

  if (false == projGenHelper.sanityCheck()) {
    fprintf (stderr, "makefilegen exits\n");
    exit(EXIT_FAILURE);
  }

  projGenHelper.generateRefProjectWorkingDirsTo(*p_project_name);

  for (size_t i = 0, num = seen_tpd_files.size(); i < num; ++i) {
    const cstring& key = seen_tpd_files.get_nth_key(i);
    int *elem = seen_tpd_files.get_nth_elem(i);
    key.destroy();
    delete elem;
  }
  seen_tpd_files.clear();

  return success;
}

// optind is the index of the next element of argv to be processed.
// Return TPD_SUCESS if parsing successful, TPD_SKIPPED if the tpd was
// seen already, or TPD_FAILED.
//
// Note: if process_tpd() returns TPD_SUCCESS, it is expected that all strings
// (argv[], ets_name, other_files[], output_file) are allocated on the heap
// and need to be freed. On input, these strings point into argv.
// process_tpd() may alter these strings; new values will be on the heap.
// If process_tpd() preserves the content of such a string (e.g. ets_name),
// it must nevertheless make a copy on the heap via mcopystr().
static tpd_result process_tpd_internal(const char *p_tpd_name, const char *actcfg,
  const char *file_list_path, int *p_argc, char ***p_argv,
  int *p_optind, char **p_ets_name, char **p_project_name,
  boolean *p_gflag, boolean *p_sflag, boolean *p_cflag, boolean *p_aflag, boolean *preprocess,
  boolean *p_Rflag, boolean *p_lflag, boolean *p_mflag, boolean *p_Pflag,
  boolean *p_Lflag, boolean recursive, boolean force_overwrite, boolean gen_only_top_level,
  const char *output_file, char** abs_work_dir_p, struct string_list* sub_project_dirs,
  const char* program_name, FILE* prj_graph_fp, struct string2_list* create_symlink_list, struct string_list* ttcn3_prep_includes,
  struct string_list* ttcn3_prep_defines, struct string_list* ttcn3_prep_undefines, struct string_list* prep_includes,
  struct string_list* prep_defines, struct string_list* prep_undefines, boolean *p_csflag, boolean *p_quflag, boolean* p_dsflag,
  char** cxxcompiler, char** optlevel, char** optflags, boolean* p_dbflag, boolean* p_drflag, boolean* p_dtflag, boolean* p_dxflag,
  boolean* p_djflag, boolean* p_fxflag, boolean* p_doflag, boolean* p_gfflag, boolean* p_lnflag, boolean* p_isflag,
  boolean* p_asflag, boolean* p_swflag, boolean* p_Yflag, struct string_list* solspeclibs, struct string_list* sol8speclibs,
  struct string_list* linuxspeclibs, struct string_list* freebsdspeclibs, struct string_list* win32speclibs, char** ttcn3prep,
  string_list* linkerlibs, string_list* additionalObjects, string_list* linkerlibsearchp, boolean Vflag, boolean Dflag, boolean *p_Zflag,
  boolean *p_Hflag, char** generatorCommandOutput, struct string2_list* target_placement_list, boolean prefix_workdir,
  struct string2_list* run_command_list, map<cstring, int>& seen_tpd_files, struct string2_list* required_configs)
{
  tpd_result result = TPD_SUCCESS;
  // read-only non-pointer aliases
  //char** const& local_argv = *p_argv;
  int const& local_argc = *p_argc;
  int const& local_optind = *p_optind;
  *abs_work_dir_p = NULL;

  assert(local_optind >= 2 // at least '-ttpd_name' must be in the args
    || local_optind == 0); // if called for a referenced project

  assert(local_argc   >= local_optind);

  autostring tpd_dir(get_dir_from_path(p_tpd_name));
  autostring abs_tpd_dir(get_absolute_dir(tpd_dir, NULL));
  if (NULL == (const char*)abs_tpd_dir) {
    ERROR("absolut TPD directory could not be retreaved from %s", (const char*)tpd_dir);
    return TPD_FAILED;
  }
  autostring tpd_filename(get_file_from_path(p_tpd_name));
  autostring abs_tpd_name(compose_path_name(abs_tpd_dir, tpd_filename));

  if (seen_tpd_files.has_key(abs_tpd_name)) {
    ++*seen_tpd_files[abs_tpd_name];
    return TPD_SKIPPED; // nothing to do
  }
  else {
    if (recursive && !prefix_workdir) {
      // check that this tpd file is not inside a directory of another tpd file
      for (size_t i = 0; i < seen_tpd_files.size(); ++i) {
        const cstring& other_tpd_name = seen_tpd_files.get_nth_key(i);
        autostring other_tpd_dir(get_dir_from_path((const char*)other_tpd_name));
        if (strcmp((const char*)abs_tpd_dir,(const char*)other_tpd_dir)==0) {
          ERROR("TPD files `%s' and `%s' are in the same directory! Use the `-W' option.", (const char*)abs_tpd_name, (const char*)other_tpd_name);
          return TPD_FAILED;
        }
      }
    }
    // mcopystr makes another copy for the map
    seen_tpd_files.add(cstring(mcopystr(abs_tpd_name)), new int(1));
  }

  vector<char> base_files; // values Malloc'd but we pass them to the caller

  XmlDoc doc(xmlParseFile(p_tpd_name));
  if (doc == NULL) {
    fprintf(stderr, "Error: unable to parse file \"%s\"\n", p_tpd_name);
    return TPD_FAILED;
  }

  if (!Vflag) {
    // try schema validation if tpd schema file was found
    bool tpd_is_valid = false;
    const char* ttcn3_dir = getenv("TTCN3_DIR");
    if (ttcn3_dir) {
      size_t ttcn3_dir_len = strlen(ttcn3_dir);
      bool ends_with_slash = (ttcn3_dir_len>0) && (ttcn3_dir[ttcn3_dir_len - 1]=='/');
      expstring_t xsd_file_name = mprintf("%s%setc/xsd/TPD.xsd", ttcn3_dir, ends_with_slash?"":"/");
      autostring xsd_file_name_as(xsd_file_name);
      if (get_path_status(xsd_file_name)==PS_FILE) {
        if (validate_tpd(doc, p_tpd_name, xsd_file_name)) {
          tpd_is_valid = true;
          NOTIFY("TPD file `%s' validated successfully with schema file `%s'", p_tpd_name, xsd_file_name);
        }
      } else {
        ERROR("Cannot find XSD for schema for validation of TPD on path `%s'", xsd_file_name);
      }
    } else {
      ERROR("Environment variable TTCN3_DIR not present, cannot find XSD for schema validation of TPD");
    }
    if (!tpd_is_valid) {
      return TPD_FAILED;
    }
  }

  // Source files.
  // Key is projectRelativePath, value is relativeURI or rawURI.
  map<cstring, const char> files;   // values Malloc'd
  map<cstring, const char> folders; // values Malloc'd
  // NOTE! files and folders must be local variables of process_tpd.
  // This is because the keys (not the values) are owned by the XmlDoc.

  map<cstring, const char> path_vars;

  XPathContext xpathCtx(xmlXPathNewContext(doc));
  if (xpathCtx == NULL) {
    fprintf(stderr,"Error: unable to create new XPath context\n");
    return TPD_FAILED;
  }
  // Collect path variables
  {
    XPathObject pathsObj(run_xpath(xpathCtx,
      "/TITAN_Project_File_Information/PathVariables/PathVariable"));
    xmlNodeSetPtr nodes = pathsObj->nodesetval;

    const char *name = 0, *value = 0;
    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      // nodes->nodeTab[i]->name === "PathVariable"
      for (xmlAttrPtr attr = nodes->nodeTab[i]->properties; attr; attr = attr->next) {
        if (!strcmp((const char*)attr->name, "name")) {
          name = (const char*)attr->children->content;
        }
        else if (!strcmp((const char*)attr->name, "value")) {
          value = (const char*)attr->children->content;
        }
        else {
          WARNING("Unknown attribute %s", (const char*)nodes->nodeTab[i]->name);
        }
      } // next attribute

      if (name && value) path_vars.add(cstring(name), value);
      else ERROR("A PathVariable must have both name and value");
    } // next PathVariable
  }

  // Collect folders
  {
    XPathObject foldersObj(run_xpath(xpathCtx,
      "/TITAN_Project_File_Information/Folders/FolderResource"));

    xmlNodeSetPtr nodes = foldersObj->nodesetval;
    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      // nodes->nodeTab[i]->name === "FolderResource"
      const char *uri = 0, *path = 0, *raw = 0;

      // projectRelativePath is the path as it appears in Project Explorer (illusion)
      // relativeURI is the actual location, relative to the project root (reality)
      // rawURI is present if the relative path can not be calculated
      //
      // Theoretically these attributes could be in any order, loop over them
      for (xmlAttrPtr attr = nodes->nodeTab[i]->properties; attr; attr = attr->next) {
        if (!strcmp((const char*)attr->name, "projectRelativePath")) {
          path = (const char*)attr->children->content;
        }
        else if (!strcmp((const char*)attr->name, "relativeURI")) {
          uri = (const char*)attr->children->content;
        }
        else if (!strcmp((const char*)attr->name, "rawURI")) {
          raw = (const char*)attr->children->content;
        }
        else {
          WARNING("Unknown attribute %s", (const char*)nodes->nodeTab[i]->name);
        }
      } // next attribute

      if (path == NULL) {
        ERROR("A FolderResource must have a projectRelativePath");
        continue;
      }

      if (uri == NULL && raw == NULL) {
        ERROR("A FolderResource must have either relativeURI or rawURI");
        continue;
      }
      // relativeURI wins over rawURI
      folders.add(cstring(path), uri ? mcopystr(uri) : cook(raw, path_vars));
      // TODO uri: cut "file:", complain on anything else
    } // next FolderResource
  }

  /////////////////////////////////////////////////////////////////////////////
  {
    char *projectNameXpath = mprintf("/TITAN_Project_File_Information/ProjectName/text()");
    XPathObject projectNameObj(run_xpath(xpathCtx, projectNameXpath));
    Free(projectNameXpath);
    if (projectNameObj->nodesetval && projectNameObj->nodesetval->nodeNr > 0) {
      *p_project_name = mcopystr((const char*)projectNameObj->nodesetval->nodeTab[0]->content);
      projGenHelper.addTarget(*p_project_name);
      projGenHelper.setToplevelProjectName(*p_project_name);
      ProjectDescriptor* projDesc = projGenHelper.getTargetOfProject(*p_project_name);
      if (projDesc) projDesc->setProjectAbsTpdDir((const char*)abs_tpd_dir);
    }
  }
  /////////////////////////////////////////////////////////////////////////////

  if (!actcfg) {
    actcfg = get_act_config(required_configs,*p_project_name);
  }
  if (actcfg == NULL) {
    // Find out the active config
    XPathObject  activeConfig(run_xpath(xpathCtx,
      "/TITAN_Project_File_Information/ActiveConfiguration/text()"));
    if (activeConfig->nodesetval && activeConfig->nodesetval->nodeNr == 1) {
      // there is one node
      actcfg = (const char*)activeConfig->nodesetval->nodeTab[0]->content;
    }
  }

  if (actcfg == NULL) {
    ERROR("Can not find the active build configuration.");
    for (size_t i = 0; i < folders.size(); ++i) {
      Free(const_cast<char*>(folders.get_nth_elem(i)));
    }
    folders.clear();
    return TPD_FAILED;
  }

  { // check if the active configuration exists
    expstring_t xpathActCfg= mprintf(
      "/TITAN_Project_File_Information/Configurations/"
        "Configuration[@name='%s']/text()", actcfg);
    XPathObject theConfigEx(run_xpath(xpathCtx, xpathActCfg));
    Free(xpathActCfg);

    xmlNodeSetPtr nodes = theConfigEx->nodesetval;
    if (nodes == NULL) {
      ERROR("The active build configuration named '%s' does not exist",
          actcfg);
      for (size_t i = 0; i < folders.size(); ++i) {
        Free(const_cast<char*>(folders.get_nth_elem(i)));
      }
      folders.clear();
      return TPD_FAILED;
    }
  }
  // working directory stuff
  autostring workdir;
  {
    const char* workdirFromTpd = "bin"; // default value
    char *workdirXpath = mprintf(
      "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
      "/ProjectProperties/LocalBuildSettings/workingDirectory/text()",
      actcfg);
    XPathObject workdirObj(run_xpath(xpathCtx, workdirXpath));
    Free(workdirXpath);
    if (workdirObj->nodesetval && workdirObj->nodesetval->nodeNr > 0) {
      workdirFromTpd = (const char*)workdirObj->nodesetval->nodeTab[0]->content;
    }
    if (prefix_workdir) { // the working directory is: prjNameStr + "_" + workdirFromTpd
      const char* prjNameStr = "unnamedproject";
      XPathObject prjName(run_xpath(xpathCtx, "/TITAN_Project_File_Information/ProjectName/text()"));
      if (prjName->nodesetval && prjName->nodesetval->nodeNr == 1) {
        prjNameStr = (const char*)prjName->nodesetval->nodeTab[0]->content;
      }
      workdir = mprintf("%s_%s", prjNameStr, workdirFromTpd);
    } else {
      workdir = mcopystr(workdirFromTpd);
    }
  }
  if (!folders.has_key(workdir)) {
    // Maybe the tpd was saved with the option "No info about work dir"
    folders.add(workdir, mcopystr(workdir)); // fake it
  }
  const char *real_workdir = folders[workdir]; // This is relative to the location of the tpd file
  excluded_folders.add(real_workdir); // excluded by convention

  autostring proj_abs_workdir;

  autostring abs_workdir;
  // If -D flag was specified then we ignore the workdir
  // in the TPD (the current dir is considered the work dir).
  if (!Dflag) {
    bool hasWorkDir = false;
    // if the working directory does not exist create it
    autostring saved_work_dir(get_working_dir());
    if (set_working_dir(abs_tpd_dir)) {
      ERROR("Could not change to project directory `%s'", (const char*)abs_tpd_dir);
    } else {
      switch (get_path_status(real_workdir)) {
      case PS_FILE:
        ERROR("Cannot create working directory `%s' in project directory `%s' because a file with the same name exists", (const char*)abs_tpd_dir, real_workdir);
        break;
      case PS_DIRECTORY:
        // already exists
        hasWorkDir = true;
        break;
      default:
        if (recursive || local_argc != 0) { // we only want to create workdir if necessary
          fprintf(stderr, "Working directory `%s' in project `%s' does not exist, trying to create it...\n",
                  real_workdir, (const char*)abs_tpd_dir);
          int rv = mkdir(real_workdir, 0755);
          if (rv) ERROR("Could not create working directory, mkdir() failed: %s", strerror(errno));
          else printf("Working directory created\n");
          hasWorkDir = true;
        }
      }
    }

    if (local_argc==0) { // if not top level
      set_working_dir(saved_work_dir); // restore working directory
    } else { // if top level
      set_working_dir(real_workdir); // go into the working dir
    }
    if (hasWorkDir) { //we created working directory, or its already been created (from a parent makefilegen process maybe)
      *abs_work_dir_p = get_absolute_dir(real_workdir, abs_tpd_dir);
      abs_workdir = (mcopystr(*abs_work_dir_p));
      proj_abs_workdir = mcopystr(*abs_work_dir_p);
    }
  }

  if (Dflag) { // the path to subproject working dir is needed to find the linkerlibsearchpath
    proj_abs_workdir = compose_path_name(abs_tpd_dir, real_workdir);
  }

  ProjectDescriptor* projDesc = projGenHelper.getTargetOfProject(*p_project_name);
  if (projDesc) {
    projDesc->setProjectAbsWorkingDir((const char*)proj_abs_workdir);
    projDesc->setProjectWorkingDir(real_workdir);
    projDesc->setTPDFileName(p_tpd_name);
  }

  /////////////////////////////////////////////////////////////////////////////

  // Gather the excluded folders in the active config
  {
    expstring_t xpathActCfgPaths = mprintf(
      "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
      "/FolderProperties/FolderResource/FolderProperties/ExcludeFromBuild[text()='true']"
      // This was the selection criterium, we need to go up and down for the actual information
      "/parent::*/parent::*/FolderPath/text()",
      actcfg);
    XPathObject theConfigEx(run_xpath(xpathCtx, xpathActCfgPaths));
    Free(xpathActCfgPaths);

    xmlNodeSetPtr nodes = theConfigEx->nodesetval;
    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {

      excluded_folders.add((const char*)nodes->nodeTab[i]->content);
    }
  }

  // Gather individual excluded files in the active config
  {
    expstring_t xpathActCfgPaths = mprintf(
      "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
      "/FileProperties/FileResource/FileProperties/ExcludeFromBuild[text()='true']"
      "/parent::*/parent::*/FilePath/text()",
      actcfg);
    XPathObject theConfigEx(run_xpath(xpathCtx, xpathActCfgPaths));
    Free(xpathActCfgPaths);

    xmlNodeSetPtr nodes = theConfigEx->nodesetval;
    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      xmlNodePtr curnode = nodes->nodeTab[i];

      cstring aa((const char*)curnode->content);
      excluded_files.add(aa, *p_project_name);
    }
  }

  // Collect files; filter out excluded ones
  {
    XPathObject  filesObj(run_xpath(xpathCtx,
      "TITAN_Project_File_Information/Files/FileResource"));

    xmlNodeSetPtr nodes = filesObj->nodesetval;
    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      // nodes->nodeTab[i]->name === "FileResource"
      const char *uri = 0, *path = 0, *raw = 0;

      // projectRelativePath is the path as it appears in Project Explorer (illusion)
      // relativeURI is the actual location, relative to the project root (reality)
      // rawURI is present if the relative path can not be calculated
      //
      // Theoretically these attributes could be in any order, loop over them
      for (xmlAttrPtr attr = nodes->nodeTab[i]->properties; attr; attr = attr->next) {
        if (!strcmp((const char*)attr->name, "projectRelativePath")) {
          path = (const char*)attr->children->content;
        }
        else if (!strcmp((const char*)attr->name, "relativeURI")) {
          uri = (const char*)attr->children->content;
        }
        else if (!strcmp((const char*)attr->name, "rawURI")) {
          raw = (const char*)attr->children->content;
        }
        else {
          WARNING("Unknown attribute %s", (const char*)nodes->nodeTab[i]->name);
        }
      } // next attribute

      if (path == NULL) {
        ERROR("A FileResource must have a projectRelativePath");
        continue;
      }

      if (uri == NULL && raw == NULL) {
        ERROR("A FileResource must have either relativeURI or rawURI");
        continue;
      }

      cstring cpath(path);
      if (!is_excluded_file(cpath, *p_project_name) && !is_excluded_folder(path)) {
        // relativeURI wins over rawURI
        char *ruri = uri ? mcopystr(uri) : cook(raw, path_vars);
        if (files.has_key(cpath)) {
          ERROR("A FileResource %s must be unique!", (const char*)cpath);
        }
        else {
          const char* file_path = ruri;
          expstring_t rel_file_dir = get_dir_from_path(file_path);
          expstring_t file_name = get_file_from_path(file_path);
          expstring_t abs_dir_path = get_absolute_dir(rel_file_dir, abs_tpd_dir);
          expstring_t abs_file_name = compose_path_name(abs_dir_path, file_name);
          if (abs_file_name != NULL) {
            if (get_path_status(abs_file_name) == PS_FILE) {
              FILE *fp = fopen(abs_file_name, "r");
              if (fp != NULL) {
                char* ttcn3_module_name;
                if (is_ttcn3_module(abs_file_name, fp, &ttcn3_module_name)) {
                  projGenHelper.addTtcn3ModuleToProject(*p_project_name, ttcn3_module_name);
                }
                Free(ttcn3_module_name);
                char* asn1_module_name;
                if (is_asn1_module(abs_file_name, fp, &asn1_module_name)) {
                  projGenHelper.addAsn1ModuleToProject(*p_project_name, asn1_module_name);
                }
                Free(asn1_module_name);
                if (projGenHelper.isCPPSourceFile(file_name)) {
                   projGenHelper.addUserSourceToProject(*p_project_name, file_name);
                }
                if (projGenHelper.isCPPHeaderFile(file_name)) {
                   projGenHelper.addUserHeaderToProject(*p_project_name, file_name);
                }
                if (projGenHelper.isTtcnPPFile(file_name)) {
                   projGenHelper.addTtcnPPToProject(*p_project_name, file_name);
                }
              }
              fclose(fp);
            }
          }
          files.add(cpath, ruri); // relativeURI to the TPD location
          { // set the *preprocess value if .ttcnpp file was found
            const size_t ttcnpp_extension_len = 7; // ".ttcnpp"
            const size_t ruri_len = strlen(ruri);
            if ( ruri_len>ttcnpp_extension_len && strcmp(ruri+(ruri_len-ttcnpp_extension_len),".ttcnpp")==0 ) {
              *preprocess = TRUE;
            }
          }
          Free(rel_file_dir);
          Free(file_name);
          Free(abs_dir_path);
          Free(abs_file_name);
        }
      }
    } // next FileResource
  }

  // Check options
  xsdbool2boolean(xpathCtx, actcfg, "useAbsolutePath", p_aflag);
  xsdbool2boolean(xpathCtx, actcfg, "GNUMake", p_gflag);
  if (*p_Zflag) *p_lflag = FALSE;
  xsdbool2boolean(xpathCtx, actcfg, "dynamicLinking", p_lflag);
  xsdbool2boolean(xpathCtx, actcfg, "functiontestRuntime", p_Rflag);
  xsdbool2boolean(xpathCtx, actcfg, "singleMode", p_sflag);
  xsdbool2boolean(xpathCtx, actcfg, "codeSplitting", p_csflag);
  xsdbool2boolean(xpathCtx, actcfg, "quietly", p_quflag);
  xsdbool2boolean(xpathCtx, actcfg, "disableSubtypeChecking", p_dsflag);
  xsdbool2boolean(xpathCtx, actcfg, "disableBER", p_dbflag);
  xsdbool2boolean(xpathCtx, actcfg, "disableRAW", p_drflag);
  xsdbool2boolean(xpathCtx, actcfg, "disableTEXT", p_dtflag);
  xsdbool2boolean(xpathCtx, actcfg, "disableXER", p_dxflag);
  xsdbool2boolean(xpathCtx, actcfg, "disableJSON", p_djflag);
  xsdbool2boolean(xpathCtx, actcfg, "forceXERinASN.1", p_fxflag);
  xsdbool2boolean(xpathCtx, actcfg, "defaultasOmit", p_doflag);
  xsdbool2boolean(xpathCtx, actcfg, "gccMessageFormat", p_gfflag);
  xsdbool2boolean(xpathCtx, actcfg, "lineNumbersOnlyInMessages", p_lnflag);
  xsdbool2boolean(xpathCtx, actcfg, "includeSourceInfo", p_isflag);
  xsdbool2boolean(xpathCtx, actcfg, "addSourceLineInfo", p_asflag);
  xsdbool2boolean(xpathCtx, actcfg, "suppressWarnings", p_swflag);
  xsdbool2boolean(xpathCtx, actcfg, "outParamBoundness", p_Yflag);

  projDesc = projGenHelper.getTargetOfProject(*p_project_name);
  if (projDesc) projDesc->setLinkingStrategy(*p_lflag);

  // Extract the "incremental dependencies" option
  {
    boolean incremental_deps = TRUE;
    xsdbool2boolean(xpathCtx, actcfg, "incrementalDependencyRefresh", &incremental_deps);

    // For makefilegen, "Use GNU make" implies incremental deps by default,
    // unless explicitly disabled by "use makedepend" (a.k.a. mflag).
    // For Eclipse, incremental deps must be explicitly specified,
    // even if GNU make is being used.
    
    if (incremental_deps) {
      if( !(*p_gflag) ) {
        WARNING("Incremental dependency ordered but it requires gnu make");
      }
    }
    else {
      if (*p_gflag) {
        // GNU make but no incremental deps
        *p_mflag = true;
      }
    }
  }

  // Extract the default target option
  // if it is not defined as a command line argument
  if (!(*p_Lflag)) {
    expstring_t defTargetXpath = mprintf(
      "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
      "/ProjectProperties/MakefileSettings/defaultTarget/text()",
      actcfg);
    XPathObject defTargetObj(run_xpath(xpathCtx, defTargetXpath));
    Free(defTargetXpath);
    if (defTargetObj->nodesetval && defTargetObj->nodesetval->nodeNr > 0) {
      const char* content = (const char*)defTargetObj->nodesetval->nodeTab[0]->content;
      if (!strcmp(content, "library")) {
        *p_Lflag = true;
      } else if (!strcmp(content, "executable")) {
        *p_Lflag = false;
      } else {
        ERROR("Unknown default target: '%s'."
            " The available targets are: 'executable', 'library'", content);
      }
    }
    ProjectDescriptor* projDesc = projGenHelper.getTargetOfProject(*p_project_name);
    if (projDesc) projDesc->setLibrary(*p_Lflag);
  }

  // Executable name (don't care unless top-level invocation)
  if (local_argc != 0)
  {
    char *exeXpath = mprintf(
      "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
      "/ProjectProperties/MakefileSettings/targetExecutable/text()",
      actcfg);
    XPathObject exeObj(run_xpath(xpathCtx, exeXpath));
    Free(exeXpath);
    if (exeObj->nodesetval && exeObj->nodesetval->nodeNr > 0) {
      const char* target_executable = (const char*)exeObj->nodesetval->nodeTab[0]->content;
      autostring target_exe_dir(get_dir_from_path(target_executable));
      autostring target_exe_file(get_file_from_path(target_executable));
      if (target_exe_dir!=NULL) { // if it's not only a file name
        if (get_path_status(target_exe_dir)==PS_NONEXISTENT) {
          if (strcmp(real_workdir,target_exe_dir)!=0) {
            WARNING("Provided targetExecutable directory `%s' does not exist, only file name `%s' will be used", (const char*)target_exe_dir, (const char*)target_exe_file);
          }
          target_executable = target_exe_file;
        }
      }
      if (!*p_ets_name) { // Command line will win
        *p_ets_name = mcopystr(target_executable);
      }
    }
  }

  // create an xml element for the currently processed project
  if (prj_graph_fp) {
    const char* prjNameStr = "???";
    XPathObject  prjName(run_xpath(xpathCtx, "/TITAN_Project_File_Information/ProjectName/text()"));
    if (prjName->nodesetval && prjName->nodesetval->nodeNr == 1) {
      prjNameStr = (const char*)prjName->nodesetval->nodeTab[0]->content;
    }
    autostring tpd_rel_dir(get_relative_dir(tpd_dir, NULL));
    autostring tpd_rel_path(compose_path_name(tpd_rel_dir, (const char*)tpd_filename));
    fprintf(prj_graph_fp, "<project name=\"%s\" uri=\"%s\">\n", prjNameStr, (const char*)tpd_rel_path);
    XPathObject subprojects(run_xpath(xpathCtx, "/TITAN_Project_File_Information/ReferencedProjects/ReferencedProject/attribute::name"));
    xmlNodeSetPtr nodes = subprojects->nodesetval;
    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      const char* refd_name = "???";
      if (!strcmp((const char*)nodes->nodeTab[i]->name, "name")) {
        refd_name = (const char*)nodes->nodeTab[i]->children->content;
      }
      fprintf(prj_graph_fp, "<reference name=\"%s\"/>\n", refd_name);
    }
    fprintf(prj_graph_fp, "</project>\n");
  }

  // Tpd part of the MakefileSettings
  {
    //TTCN3preprocessorIncludes
    char *preincludeXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/TTCN3preprocessorIncludes/listItem/text()",
        actcfg);
    XPathObject preincludeObj(run_xpath(xpathCtx, preincludeXpath));
    Free(preincludeXpath);

    xmlNodeSetPtr nodes = preincludeObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)preincludeObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (ttcn3_prep_includes) {
        // go to last element
        struct string_list* last_elem = ttcn3_prep_includes;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;
      }
    }
  }
  {
    //TTCN3preprocessorDefines
    char *ttcn3predefinesXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/TTCN3preprocessorDefines/listItem/text()",
        actcfg);
    XPathObject ttcn3predefinesObj(run_xpath(xpathCtx, ttcn3predefinesXpath));
    Free(ttcn3predefinesXpath);

    xmlNodeSetPtr nodes = ttcn3predefinesObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      const char* content = (const char*)ttcn3predefinesObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (ttcn3_prep_defines) {
        // go to last element
        struct string_list* last_elem = ttcn3_prep_defines;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        last_elem->str = mcopystr(content);
      }
    }
  }
  {
    //TTCN3preprocessorUnDefines
    char *ttcn3preUndefinesXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/TTCN3preprocessorUndefines/listItem/text()",
        actcfg);
    XPathObject ttcn3preUndefinesObj(run_xpath(xpathCtx, ttcn3preUndefinesXpath));
    Free(ttcn3preUndefinesXpath);

    xmlNodeSetPtr nodes = ttcn3preUndefinesObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      const char* content = (const char*)ttcn3preUndefinesObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (ttcn3_prep_undefines) {
        // go to last element
        struct string_list* last_elem = ttcn3_prep_undefines;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        last_elem->str = mcopystr(content);
      }
    }
  }

  {
    //preprocessorIncludes
    char *preincludesXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/preprocessorIncludes/listItem/text()",
        actcfg);
    XPathObject preincludesObj(run_xpath(xpathCtx, preincludesXpath));
    Free(preincludesXpath);

    xmlNodeSetPtr nodes = preincludesObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)preincludesObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (prep_includes) {
        // go to last element
        struct string_list* last_elem = prep_includes;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;
      }
    }
  }
  {
    //preprocessorDefines
    char *predefinesXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/preprocessorDefines/listItem/text()",
        actcfg);
    XPathObject predefinesObj(run_xpath(xpathCtx, predefinesXpath));
    Free(predefinesXpath);

    xmlNodeSetPtr nodes = predefinesObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      const char* content = (const char*)predefinesObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (prep_defines) {
        // go to last element
        struct string_list* last_elem = prep_defines;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        last_elem->str = mcopystr(content);
      }
    }
  }
  {
    //preprocessorUnDefines
    char *preUndefinesXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/preprocessorUndefines/listItem/text()",
        actcfg);
    XPathObject preUndefinesObj(run_xpath(xpathCtx, preUndefinesXpath));
    Free(preUndefinesXpath);

    xmlNodeSetPtr nodes = preUndefinesObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      const char* content = (const char*)preUndefinesObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (prep_undefines) {
        // go to last element
        struct string_list* last_elem = prep_undefines;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        last_elem->str = mcopystr(content);
      }
    }
  }
  {
    char *cxxCompilerXpath = mprintf(
            "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
            "/ProjectProperties/MakefileSettings/CxxCompiler/text()",
            actcfg);
    XPathObject cxxCompilerObj(run_xpath(xpathCtx, cxxCompilerXpath));
    Free(cxxCompilerXpath);
    xmlNodeSetPtr nodes = cxxCompilerObj->nodesetval;
    if (nodes) {
      *cxxcompiler = mcopystr((const char*)cxxCompilerObj->nodesetval->nodeTab[0]->content);
    }
  }
  {
    char *optLevelXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/optimizationLevel/text()",
        actcfg);
    XPathObject optLevelObj(run_xpath(xpathCtx, optLevelXpath));
    Free(optLevelXpath);
    xmlNodeSetPtr nodes = optLevelObj->nodesetval;
    if (nodes) {
      *optlevel = mcopystr((const char*)optLevelObj->nodesetval->nodeTab[0]->content);
    }
  }
  {
    char *optFlagsXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/otherOptimizationFlags/text()",
        actcfg);
    XPathObject optFlagsObj(run_xpath(xpathCtx, optFlagsXpath));
    Free(optFlagsXpath);
    xmlNodeSetPtr nodes = optFlagsObj->nodesetval;
    if (nodes) {
      *optflags = mcopystr((const char*)optFlagsObj->nodesetval->nodeTab[0]->content);
    }
  }
  {
    //SolarisSpecificLibraries
    char *solspeclibXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/SolarisSpecificLibraries/listItem/text()",
        actcfg);
    XPathObject solspeclibObj(run_xpath(xpathCtx, solspeclibXpath));
    Free(solspeclibXpath);

    xmlNodeSetPtr nodes = solspeclibObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)solspeclibObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (solspeclibs) {
        // go to last element
        struct string_list* last_elem =solspeclibs;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;
      }
    }
  }
  {
    //Solaris8SpecificLibraries
    char *sol8speclibXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/Solaris8SpecificLibraries/listItem/text()",
        actcfg);
    XPathObject sol8speclibObj(run_xpath(xpathCtx, sol8speclibXpath));
    Free(sol8speclibXpath);

    xmlNodeSetPtr nodes = sol8speclibObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)sol8speclibObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (sol8speclibs) {
        // go to last element
        struct string_list* last_elem = sol8speclibs;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;
      }
    }
  }
  {
    //LinuxSpecificLibraries
    char *linuxspeclibXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/LinuxSpecificLibraries/listItem/text()",
        actcfg);
    XPathObject linuxspeclibObj(run_xpath(xpathCtx, linuxspeclibXpath));
    Free(linuxspeclibXpath);

    xmlNodeSetPtr nodes = linuxspeclibObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)linuxspeclibObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (linuxspeclibs) {
        // go to last element
        struct string_list* last_elem = linuxspeclibs;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;
      }
    }
  }
  {
    //FreeBSDSpecificLibraries
    char *freebsdspeclibXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/FreeBSDSpecificLibraries/listItem/text()",
        actcfg);
    XPathObject freebsdspeclibObj(run_xpath(xpathCtx, freebsdspeclibXpath));
    Free(freebsdspeclibXpath);

    xmlNodeSetPtr nodes = freebsdspeclibObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)freebsdspeclibObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (freebsdspeclibs) {
        // go to last element
        struct string_list* last_elem = freebsdspeclibs;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;
      }
    }
  }
  {
    //Win32SpecificLibraries
    char *win32speclibXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/Win32SpecificLibraries/listItem/text()",
        actcfg);
    XPathObject win32speclibObj(run_xpath(xpathCtx, win32speclibXpath));
    Free(win32speclibXpath);

    xmlNodeSetPtr nodes = win32speclibObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)win32speclibObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (win32speclibs) {
        // go to last element
        struct string_list* last_elem = win32speclibs;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;
      }

    }
  }
  {
    //TTCN3preprocessor
    char *ttcn3preproc = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/TTCN3preprocessor/text()",
        actcfg);
    XPathObject ttcn3preprocObj(run_xpath(xpathCtx, ttcn3preproc));
    Free(ttcn3preproc);
    xmlNodeSetPtr nodes = ttcn3preprocObj->nodesetval;
    if (nodes) {
      *ttcn3prep = mcopystr((const char*)ttcn3preprocObj->nodesetval->nodeTab[0]->content);
    }
  }
  {
    // additionalObjects
    char *additionalObjectsXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/additionalObjects/listItem/text()",
        actcfg);
    XPathObject additionalObjectsObj(run_xpath(xpathCtx, additionalObjectsXpath));
    Free(additionalObjectsXpath);

    xmlNodeSetPtr nodes = additionalObjectsObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)additionalObjectsObj->nodesetval->nodeTab[i]->content;

      // add to the end of list
      if (additionalObjects) {
        // go to last element
        struct string_list* last_elem = additionalObjects;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;
      }
    }
  }
  {
    //The project name needed the hierarchical projects
    char* prjNameStr = 0;
    char *prjNameStrXpath = mprintf("/TITAN_Project_File_Information/ProjectName/text()");
    XPathObject  prjName(run_xpath(xpathCtx, prjNameStrXpath));
    if (prjName->nodesetval && prjName->nodesetval->nodeNr == 1) {
      prjNameStr = (char*)prjName->nodesetval->nodeTab[0]->content;
    }
    Free(prjNameStrXpath);
    append_to_library_list (prjNameStr, xpathCtx, actcfg);

    //linkerLibraries
    char *linkerlibsXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/linkerLibraries/listItem/text()",
        actcfg);
    XPathObject linkerlibsObj(run_xpath(xpathCtx, linkerlibsXpath));
    Free(linkerlibsXpath);

    xmlNodeSetPtr nodes = linkerlibsObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)linkerlibsObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (linkerlibs) {
        // go to last element
        struct string_list* last_elem = linkerlibs;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;

        ProjectDescriptor* projDesc = projGenHelper.getTargetOfProject(*p_project_name);
        if (projDesc) projDesc->addToLinkerLibs(last_elem->str);
      }
    }
  }
  {
    //linkerLibrarySearchPath
    char *linkerlibsearchXpath = mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/MakefileSettings/linkerLibrarySearchPath/listItem/text()",
        actcfg);
    XPathObject linkerlibsearchObj(run_xpath(xpathCtx, linkerlibsearchXpath));
    Free(linkerlibsearchXpath);

    xmlNodeSetPtr nodes = linkerlibsearchObj->nodesetval;

    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      char* content = (char*)linkerlibsearchObj->nodesetval->nodeTab[i]->content;

      // add includes to the end of list
      if (linkerlibsearchp) {
        // go to last element
        struct string_list* last_elem = linkerlibsearchp;
        while (last_elem->next) last_elem = last_elem->next;
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str) {
          last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
          last_elem = last_elem->next;
          last_elem->next = NULL;
        }
        replacechar(&content);
        last_elem->str = content;

        ProjectDescriptor* projDesc = projGenHelper.getTargetOfProject(*p_project_name);
        if (projDesc) projDesc->addToLibSearchPaths(last_elem->str);
      }
    }
  }

  if (generatorCommandOutput && local_argc != 0) { // only in case of top-level invocation
    char* generatorCommandXpath = mprintf(
      "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
      "/ProjectProperties/MakefileSettings/ProjectSpecificRulesGenerator/GeneratorCommand/text()",
      actcfg);
    XPathObject generatorCommandObj(run_xpath(xpathCtx, generatorCommandXpath));
    Free(generatorCommandXpath);
    autostring generatorCommand;
    if (generatorCommandObj->nodesetval && generatorCommandObj->nodesetval->nodeNr > 0) {
      generatorCommand = mcopystr((const char*)generatorCommandObj->nodesetval->nodeTab[0]->content);
      // run the command and capture the output
      printf("Executing generator command `%s' specified in `%s'...\n", (const char*)generatorCommand, (const char*)abs_tpd_name);
      FILE* gc_fp = popen(generatorCommand, "r");
      if (!gc_fp) {
        ERROR("Could not execute command `%s'", (const char*)generatorCommand);
      } else {
        char buff[1024];
        while (fgets(buff, sizeof(buff), gc_fp)!=NULL) {
          *generatorCommandOutput = mputstr(*generatorCommandOutput, buff);
        }
        pclose(gc_fp);
      }
    }
  }

  if (target_placement_list && local_argc != 0) { // only in case of top-level invocation
    char* targetPlacementXpath = mprintf(
      "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
      "/ProjectProperties/MakefileSettings/ProjectSpecificRulesGenerator/Targets/Target/attribute::*",
      actcfg);
    XPathObject targetPlacementObj(run_xpath(xpathCtx, targetPlacementXpath));
    Free(targetPlacementXpath);
    xmlNodeSetPtr nodes = targetPlacementObj->nodesetval;
    const char* targetName = NULL;
    const char* targetPlacement = NULL;
    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      if (!strcmp((const char*)nodes->nodeTab[i]->name, "name")) {
        targetName = (const char*)nodes->nodeTab[i]->children->content;
      }
      else if (!strcmp((const char*)nodes->nodeTab[i]->name,"placement")) {
        targetPlacement = (const char*)nodes->nodeTab[i]->children->content;
      }
      if (targetName && targetPlacement) { // collected both
        if (target_placement_list) {
          // go to last element
          struct string2_list* last_elem = target_placement_list;
          while (last_elem->next) last_elem = last_elem->next;
          // add strings to last element if empty or create new last element and add it to that
          if (last_elem->str1) {
            last_elem->next = (struct string2_list*)Malloc(sizeof(struct string2_list));
            last_elem = last_elem->next;
            last_elem->next = NULL;
          }
          last_elem->str1 = mcopystr(targetName);
          last_elem->str2 = mcopystr(targetPlacement);
        }
        targetName = targetPlacement = NULL; // forget both
      }
    }
  }

// collect the required configurations
  {
    if (required_configs) {
      char* cfgReqsXpath(mprintf(
        "/TITAN_Project_File_Information/Configurations/Configuration[@name='%s']"
        "/ProjectProperties/ConfigurationRequirements/configurationRequirement",
        actcfg));
      XPathObject reqcfgObjects(run_xpath(xpathCtx, cfgReqsXpath));
      Free (cfgReqsXpath);
      xmlNodeSetPtr configs = reqcfgObjects->nodesetval;
      if (configs) for (int i = 0; i < configs->nodeNr; ++i) {
        xmlNodePtr curNodePtr = configs->nodeTab[i]->children;
        const char* projectName = NULL;
        const char* reqConfig = NULL;
        while(curNodePtr) {
          if (!strcmp((const char*)curNodePtr->name, "projectName")) {
            projectName = (const char*)curNodePtr->children->content;
          }
          if (!strcmp((const char*)curNodePtr->name, "rerquiredConfiguration") || // backward compatibility
              !strcmp((const char*)curNodePtr->name, "requiredConfiguration")) {
              reqConfig = (const char*)curNodePtr->children->content;
          }
          curNodePtr = curNodePtr->next;
        }
        struct string2_list* last_elem = required_configs;
        bool duplicate = false;
        while (last_elem->next) {
          if (!strcmp(last_elem->str1, projectName) && !strcmp(last_elem->str2, reqConfig)) {
            duplicate = true;
          }
          else if (!strcmp(last_elem->str1, projectName) && strcmp(last_elem->str2, reqConfig)) {
            ERROR("Required configuration is inconsistent : Project '%s' cannot have 2 "
                  "different configuration '%s' '%s'",
                  last_elem->str1, last_elem->str2, reqConfig);
            result = TPD_FAILED;
          }
          last_elem = last_elem->next;
        }
        // add string to last element if empty or create new last element and add it to that
        if (last_elem->str1 && !duplicate) {
          if (strcmp(last_elem->str1, projectName) || strcmp(last_elem->str2, reqConfig)) {
            last_elem->next = (struct string2_list*)Malloc(sizeof(struct string2_list));
            last_elem = last_elem->next;
            last_elem->next = NULL;
          }
          else {
            duplicate = true;
          }
        }
        if (!duplicate) {
          last_elem->str1 = mcopystr(projectName);
          last_elem->str2 = mcopystr(reqConfig);
        }
      }
    }
  }

  // Referenced projects
  {
    XPathObject subprojects(run_xpath(xpathCtx,
      "/TITAN_Project_File_Information/ReferencedProjects/ReferencedProject/attribute::*"));
    xmlNodeSetPtr nodes = subprojects->nodesetval;
    const char *name = NULL, *projectLocationURI = NULL;
    if (nodes) for (int i = 0; i < nodes->nodeNr; ++i) {
      // FIXME: this assumes every ReferencedProject has name and URI.
      // This is not necessarily so if the referenced project was closed
      // when the project was exported to TPD.
      // Luckily, the name from the closed project will be overwritten
      // by the name from the next ReferencedProject. However, if some pervert
      // changes the next ReferencedProject to have the projectLocationURI
      // as the first attribute, it will be joined to the name
      // of the previous, closed, ReferencedProject.
      if (!strcmp((const char*)nodes->nodeTab[i]->name, "name")) {
        name = (const char*)nodes->nodeTab[i]->children->content;
      }
      else if (!strcmp((const char*)nodes->nodeTab[i]->name,"projectLocationURI")) {
        projectLocationURI = (const char*)nodes->nodeTab[i]->children->content;
      }

      if (name && projectLocationURI) { // collected both
        // see if there is a specified configuration for the project

        ProjectDescriptor* projDesc = projGenHelper.getTargetOfProject(*p_project_name);
        if (projDesc) projDesc->addToReferencedProjects(name);

        const char *my_actcfg = NULL;
        int my_argc = 0;
        char *my_args[] = { NULL };
        char **my_argv = my_args + 0;
        int my_optind = 0;
        boolean my_gflag = *p_gflag, my_aflag = *p_aflag, my_cflag = *p_cflag, // pass down
          my_Rflag = *p_Rflag, my_Pflag = *p_Pflag, my_Zflag = *p_Zflag, my_Hflag = *p_Hflag,
          my_sflag =  0, my_Lflag =  0, my_lflag =  0, my_mflag =  0, my_csflag = 0,
          my_quflag = 0, my_dsflag = 0, my_dbflag = 0, my_drflag = 0,
          my_dtflag = 0, my_dxflag = 0, my_djflag = 0, my_fxflag = 0, my_doflag = 0, 
          my_gfflag = 0, my_lnflag = 0, my_isflag = 0, my_asflag = 0, 
          my_swflag = 0, my_Yflag = 0;

        char *my_ets = NULL;
        char *my_proj_name = NULL;
        autostring abs_projectLocationURI(
          compose_path_name(abs_tpd_dir, projectLocationURI));

        char* sub_proj_abs_work_dir = NULL;

        tpd_result success = process_tpd_internal((const char*)abs_projectLocationURI,
          my_actcfg, file_list_path, &my_argc, &my_argv, &my_optind, &my_ets, &my_proj_name,
          &my_gflag, &my_sflag, &my_cflag, &my_aflag, preprocess, &my_Rflag, &my_lflag,
          &my_mflag, &my_Pflag, &my_Lflag, recursive, force_overwrite, gen_only_top_level, NULL, &sub_proj_abs_work_dir,
          sub_project_dirs, program_name, prj_graph_fp, create_symlink_list, ttcn3_prep_includes, ttcn3_prep_defines, ttcn3_prep_undefines, 
          prep_includes, prep_defines, prep_undefines, &my_csflag,
          &my_quflag, &my_dsflag, cxxcompiler, optlevel, optflags, &my_dbflag, &my_drflag,
          &my_dtflag, &my_dxflag, &my_djflag, &my_fxflag, &my_doflag,
          &my_gfflag, &my_lnflag, &my_isflag, &my_asflag, &my_swflag, &my_Yflag, solspeclibs, sol8speclibs, linuxspeclibs, freebsdspeclibs, win32speclibs,
          ttcn3prep, linkerlibs, additionalObjects, linkerlibsearchp, Vflag, FALSE, &my_Zflag, 
          &my_Hflag, NULL, NULL, prefix_workdir, run_command_list, seen_tpd_files, required_configs);

        autostring sub_proj_abs_work_dir_as(sub_proj_abs_work_dir); // ?!

        if (success == TPD_SUCCESS) {
          my_actcfg = get_act_config(required_configs, my_proj_name);
          if (recursive) { // call ttcn3_makefilegen on referenced project's tpd file
            // -r is not needed any more because top level process traverses all projects recursively
            expstring_t command = mprintf("%s -cVD", program_name);
            if (force_overwrite) command = mputc(command, 'f');
            if (prefix_workdir) command = mputc(command, 'W');
            if (*p_gflag) command = mputc(command, 'g');
            if (*p_sflag) command = mputc(command, 's');
            if (*p_aflag) command = mputc(command, 'a');
            if (*p_Rflag) command = mputc(command, 'R');
            if (*p_lflag) command = mputc(command, 'l');
            if (*p_mflag) command = mputc(command, 'm');
            if (*p_Zflag) command = mputc(command, 'Z');
            if (*p_Hflag) command = mputc(command, 'H');
            command = mputstr(command, " -t ");
            command = mputstr(command, (const char*)abs_projectLocationURI);
            if (my_actcfg) {
              command = mputstr(command, " -b ");
              command = mputstr(command, my_actcfg);
            }

            autostring sub_tpd_dir(get_dir_from_path((const char*)abs_projectLocationURI));
            const char * sub_proj_effective_work_dir = sub_proj_abs_work_dir ? sub_proj_abs_work_dir : (const char*)sub_tpd_dir;
            if (!gen_only_top_level) {
              if (run_command_list) {
                // go to last element
                struct string2_list* last_elem = run_command_list;
                while (last_elem->next) last_elem = last_elem->next;
                // add strings to last element if empty or create new last element and add it to that
                if (last_elem->str1 || last_elem->str2) {
                  last_elem->next = (struct string2_list*)Malloc(sizeof(struct string2_list));
                  last_elem = last_elem->next;
                  last_elem->next = NULL;
                }
                last_elem->str1 = mcopystr(sub_proj_effective_work_dir);
                last_elem->str2 = command;
              } else {
                ERROR("Internal error: cannot add command to list");
              }
            }
            // add working dir to the end of list
            if (sub_project_dirs) {
              // go to last element
              struct string_list* last_elem = sub_project_dirs;
              while (last_elem->next) last_elem = last_elem->next;
              // add string to last element if empty or create new last element and add it to that
              if (last_elem->str) {
                last_elem->next = (struct string_list*)Malloc(sizeof(struct string_list));
                last_elem = last_elem->next;
                last_elem->next = NULL;
              }
              autostring cwd_as(get_working_dir());
              last_elem->str = (*p_aflag) ? mcopystr(sub_proj_effective_work_dir) : get_relative_dir(sub_proj_effective_work_dir, (const char*)cwd_as);
            }
          }

          for (int z = 0; z < my_argc; ++z) {
            if (*p_cflag) {
              // central storage, keep in separate container
              base_files.add(my_argv[z]); // string was allocated with new
            }
            else {
              const cstring tmp(my_argv[z]);
              if (!files.has_key(tmp)){
                files.add(tmp, my_argv[z]);
              } else {
                Free(my_argv[z]);
              }
            }
          }

          Free(my_argv); // free the array; we keep the pointers
          Free(my_ets);
          Free(my_proj_name);
        }
        else if (success == TPD_FAILED) {
          ERROR("Failed to process %s", (const char*)abs_projectLocationURI);
        }
        // else TPD_SKIPPED, keep quiet

        name = projectLocationURI = NULL; // forget both
      }
    } // next referenced project
  }

  if (output_file) {
    if (get_path_status(output_file) == PS_DIRECTORY) {
      // points to existing dir; use as-is
    }
    else { // we assume it points to a file: not our problem
      output_file = NULL;
    }
  }

  // (argc - optind) is the number of non-option arguments (assumed to be files)
  // given on the command line.
  int new_argc = local_argc - local_optind + files.size() + base_files.size();
  char ** new_argv = (char**)Malloc(sizeof(char*) * new_argc);

  int n = 0;

  // First, copy the filenames gathered from the TPD
  //
  // We symlink the files into the working directory
  // and pass only the filename to the makefile generator
  for (int nf = files.size(); n < nf; ++n) {
    const char *fn = files.get_nth_elem(n); // relativeURI to the TPD location
    autostring  dir_n (get_dir_from_path (fn));
    autostring  file_n(get_file_from_path(fn));
    autostring  rel_n (get_absolute_dir(dir_n, abs_tpd_dir));
    autostring  abs_n (compose_path_name(rel_n, file_n));

    if (local_argc == 0) {
      // We are being invoked recursively, for a referenced TPD.
      // Do not symlink; just return absolute paths to the files.
      if (*fn == '/') {
        if (*p_cflag) {
          // compose with workdir
          new_argv[n] = compose_path_name(abs_workdir, file_n);
        } else {
          // it's an absolute path, copy verbatim
          new_argv[n] = mcopystr(fn); // fn will be destroyed, pass a copy
        }
      }
      else { // relative path
        if (*p_cflag) {
          // compose with workdir
          new_argv[n] = compose_path_name(abs_workdir, file_n);
          // Do not call file_n.extract() : the composed path will be returned,
          // its component will need to be deallocated here.
        }
        else {
          // compose with tpd dir
          new_argv[n] = const_cast<char*>(abs_n.extract());
        }
      }
    }
    else { // we are processing the top-level TPD
#ifndef MINGW
      if (!*p_Pflag) {
        int fd = open(abs_n, O_RDONLY);
        if (fd >= 0) { // successfully opened
          close(fd);
          if (output_file) {
            file_n = compose_path_name(output_file, file_n);
          }
//TODO ! compose with output_file
          // save into list: add symlink data to the end of list
          if (create_symlink_list) {
            // go to last element
            struct string2_list* last_elem = create_symlink_list;
            while (last_elem->next) last_elem = last_elem->next;
            // add strings to last element if empty or create new last element and add it to that
            if (last_elem->str1) {
              last_elem->next = (struct string2_list*)Malloc(sizeof(struct string2_list));
              last_elem = last_elem->next;
              last_elem->next = NULL;
            }
            last_elem->str1 = mcopystr(abs_n);
            last_elem->str2 = mcopystr(file_n);
          } 
        }
        else {
          ERROR("%s does not exist", (const char*)abs_n);
        }
      }
#endif
      if (*p_Pflag) {
        if (*p_aflag) {
          puts((const char *)abs_n);
        } else {
          autostring dir_part(get_dir_from_path(abs_n));
          autostring file_part(get_file_from_path(abs_n));
          autostring rel_dir_part(get_relative_dir((const char *)dir_part, file_list_path ? file_list_path : (const char *)abs_tpd_dir));
          autostring rel_dir_file_part(compose_path_name((const char *)rel_dir_part, (const char *)file_part));
          puts((const char *)rel_dir_file_part);
        }
      }
      new_argv[n] = const_cast<char *>(file_n.extract());
    }
  }
  // Print the TPD too.
  if (*p_Pflag) {
    autostring dir_part(get_dir_from_path(p_tpd_name));
    autostring file_part(get_file_from_path(p_tpd_name));
    if (*p_aflag) {
      puts((const char *)abs_tpd_name);
    } else {
      autostring rel_dir_part(get_relative_dir(dir_part, file_list_path ? file_list_path : abs_tpd_dir));
      autostring rel_dir_file_part(compose_path_name(rel_dir_part, file_part));
      const char *rel_tpd_name = (const char *)rel_dir_file_part;
      puts(rel_tpd_name);
    }
  }

  // base_files from referenced projects
  for (size_t bf = 0, bs = base_files.size(); bf < bs; ++bf, ++n) {
    new_argv[n] = base_files[bf];
  }
  base_files.clear(); // string ownership transfered

  // Then, copy the filenames from the command line.
  for (int a = *p_optind; a < *p_argc; ++a, ++n) {
    // String may be from main's argv; copy to the heap.
    new_argv[n] = mcopystr((*p_argv)[a]);
  }

  if (local_argc > 0) { // it is the outermost call
    clear_seen_tpd_files(seen_tpd_files);
  }
  // replace argv
  *p_argv = new_argv;
  *p_argc = new_argc;
  *p_optind = 0;

  // finally...
  for (size_t i = 0, e = files.size(); i < e; ++i) {
    Free(const_cast<char*>(files.get_nth_elem(i)));
  }
  files.clear();

  for (size_t i = 0, e = folders.size(); i < e; ++i) {
    Free(const_cast<char*>(folders.get_nth_elem(i)));
  }
  folders.clear();

  excluded_files.clear();
  excluded_folders.clear();
  path_vars.clear();

  xmlCleanupParser();
  // ifdef debug
    xmlMemoryDump();
  return result;
}