/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/
#include "visu_commandLine.h"

#include "visu_tools.h"
#include "visu_basic.h"

#include <stdlib.h>
#include <getopt.h>
#include <stdio.h>
#include <locale.h>
#include <string.h>

/**
 * SECTION:visu_commandLine
 * @short_description: All methods needed to parse options from the
 * command line.
 *
 * <para>V_Sim parses the command line at startup and store data in
 * private variables. All this values can be retrieve later by the
 * program through calls to commandLineGet_* methods.</para>
 */

static char *argFileName, *argProgName;

/* Forced resources file. */
static gchar *argResources;

/* Spin arguments. */
static char *argSpinFileName;
static int spinHidingMode;
static gboolean spinAndAtomic;

/* Miscelaneous arguments. */
static gchar *argExportFileName;
static gchar *argWindowMode;
static guint argISet;
static gchar *argValueFile;

/* Colorisation tool. */
#define COLORIZATION_PROP_FLAG "property#"
static gchar *argColorizeFileName;
static gboolean argColorizationIsFile;
static int argColorizeColUsed[3];
static int argColorizePresetColor;
static GArray *argColorizeRange;
static gboolean argColorizeColUsed_isPresent;
static int argScalingColumn;

/* Iso-surfaces tool. */
static GList *argScalarfieldFilenames;
static gchar *argIsoVisuSurfaceFileName;
static float *argIsoValues;
static gchar **argIsoNames;
static float argNbIsoValues;
static gboolean argFitToBox;

/* Translations stuffs. */
static gboolean argTranslationsIsSet;
static float argTranslations[3];
static gboolean argBoxTranslationsIsSet;
static float argBoxTranslations[3];
/* Extension stuffs. */
static gboolean argExpandIsSet;
static float argExtension[3];

/* Colored map. */
static int *argMapVisuPlaneId;
static ToolMatrixScalingFlag argLogScale;
static int argNIsoLines;
static float *argIsoLinesColor;
static guint argMapPrecision;
static float *argMapMinMax;

/* Background image. */
static gchar *argBgImageFile;

/* Phonons. */
static gint argPhononMode;
static gfloat argPhononTime;
static gfloat argPhononAmpl;

/* Extended options. */
static GHashTable *argOptionTable;

/* Miscelaneous options */
static gchar* argVisuPlanesFileName;
static gchar *argGeoDiff;

static int withGtk;

static int xWindowWidth, xWindowHeight;

static GQuark quark = 0;
/**
 * visu_command_line_getErrorQuark:
 *
 * Internal routine for error handling.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the #GQuark associated to errors related to data
 * files.
 */
GQuark visu_command_line_getErrorQuark()
{
  if (!quark)
    quark =  g_quark_from_static_string("VisuCommandLine");
  return quark;
}

struct option_desc
{
  struct option *opt;
  const gchar *desc;
  const gchar *arg;
  const gchar *def;
  float version;
};

/* Remaining letters...
   j, k, l, y, z */
#define N_OPTIONS 36
static GString *short_options;
static struct option *long_options;
static struct option_desc *ext_options;

void alignPrint(GString *str, const gchar* value, int ln, gchar *pad)
{
  gchar *sp;
  gchar *tmp;
  
  if (g_utf8_strlen(value, -1) <= ln)
    {
      g_string_append_printf(str, "%s%s", pad, value);
      return;
    }

  sp = g_utf8_strrchr(value, ln, ' ');
  g_return_if_fail(sp);

  tmp = g_strndup(value, (gsize)(sp - value));
  g_string_append_printf(str, "%s%s\n", pad, tmp);
  g_free(tmp);
  alignPrint(str, sp + 1, ln, pad);
}

#define OUT stdout
#define P   fprintf
#define offset 25
void printInfoMessage(void)
{
  int i;
  GString *desc, *expl;
  gchar format[128], pad[128], arg[128];

  P(OUT, _("V_Sim is a software to visualize atomic structures with"
		    " OpenGl rendering.\n\n"));
  desc = g_string_new(_("usage:"));
  g_string_append_printf(desc, " %s [", PACKAGE_TARNAME);
  for (i = 0; ext_options[i].opt->name; i++)
    if (ext_options[i].opt->val)
      {
	if (i > 0)
	  g_string_append(desc, " | ");
	g_string_append_printf(desc, "-%c", ext_options[i].opt->val);
	if (ext_options[i].arg)
	  g_string_append_printf(desc, " %s", ext_options[i].arg);
      }
  g_string_append_printf(desc, "] [fileToRender]\n\n");
  P(OUT, "%s", desc->str);

  expl = g_string_new("");
  sprintf(format, "%%%ds", offset);
  sprintf(pad, format, " ");
  for (i = 0; ext_options[i].opt->name; i++)
    {
      if (ext_options[i].opt->val)
	g_string_printf(desc, "  -%c,", ext_options[i].opt->val);
      else
	g_string_assign(desc, "     ");
      if (ext_options[i].arg)
	sprintf(arg, "%s %s", ext_options[i].opt->name, ext_options[i].arg);
      else
	sprintf(arg, "%s", ext_options[i].opt->name);
      g_string_erase(expl, 0, -1);
      alignPrint(expl, ext_options[i].desc, 80 - offset, pad);
      g_string_append_printf(desc, " --%s (from v%3.1f.0)\n%s\n", arg, ext_options[i].version, expl->str);
      sprintf(format, "%%%ds", offset);
      g_string_append_printf(desc, format, " ");
      if (ext_options[i].def)
	g_string_append_printf(desc, _("(Default value: %s)\n\n"), ext_options[i].def);
      else
	g_string_append(desc, _("(Default value: unset)\n\n"));
      P(OUT, "%s", desc->str);
    }
  g_string_free(expl, TRUE);
  g_string_free(desc, TRUE);
}

void optionSet(int i, const gchar *lg, gchar sh, const gchar *desc,
	       const gchar *arg, const gchar *def, float version)
{
  long_options[i].name = lg;
  long_options[i].has_arg = (arg)?required_argument:no_argument;
  long_options[i].flag = (int*)0;
  long_options[i].val = (int)sh;
  ext_options[i].opt = long_options + i;
  ext_options[i].desc = desc;
  ext_options[i].arg = arg;
  ext_options[i].def = def;
  ext_options[i].version = version;
  if (arg && sh)
    g_string_append_printf(short_options, "%c:", sh);
  else if (sh)
    g_string_append_printf(short_options, "%c", sh);
}

void optionsInit(void)
{
  int i;

  short_options = g_string_new("");
  ext_options = g_malloc(sizeof(struct option_desc) * N_OPTIONS);
  long_options = g_malloc(sizeof(struct option) * N_OPTIONS);
  i = 0;
  optionSet(i++, "export", 'e',
	    _("make an image from the fileToRender argument. The format is"
	      " specified through the extension of the argument or by the"
              " -o fileFormatId=id option (get the id of available file"
              " formats with -o list)."),
	    _("file"), (gchar*)0, 3.0f);
  optionSet(i++, "resources", 'r',
	    _("load the given resources file on startup instead of looking"
	      " for a valid resources file in the standard locations."),
	    _("file"), (gchar*)0, 3.4f);
  optionSet(i++, "help", 'h',
	    _("show this little help."),
	    (gchar*)0, (gchar*)0, 3.0f);
  optionSet(i++, "geometry", 'g',
	    _("specify the size of the rendering window, the size argument must"
	      " have the following format: <width>x<height> with positive non null"
	      " values."),
	    _("<w>x<h>"), "600x600", 3.0f);
  optionSet(i++, "spin-file", 's',
	    _("use the given argument as a spin indicator. If this option is used,"
	      " V_Sim switches automatically to spin rendering whatever method"
	      " is specified in the parameter file."),
	    _("file"), (gchar*)0, 3.1f);
  optionSet(i++, "hiding-mode", 'm',
	    _("policy used to show or not null modulus spins possible values are"
	      " positives."),
	    _("id"), "0", 3.2f);
  optionSet(i++, "spin-and-atomic", 'a',
	    _("always draws atomic rendering on node position in addition to spin"
	      " rendering."),
	    (gchar*)0, (gchar*)0, 3.3f);
  optionSet(i++, "colorize", 'c',
	    _("the argument fileToRender must be called, then the given file of"
	      " the option is used to colorize the elements. If argument is"
              " starting with 'property#', colorization data are taken"
              " from node property propName."),
	    _("{file,property#propName}"), (gchar*)0, 3.1f);
  optionSet(i++, "use-column", 'u',
	    _("it specifies the columns to use from the data file for the three colour"
	      " channels [l;m;n]. Columns are counted from 1. Use -3, -2, -1 and 0"
	      " to use the special values, constant 1, coord. x, coord. y,"
	      " and coord. z, respectively."),
	    "l:m:n", (gchar*)0, 3.1f);
  optionSet(i++, "color-preset", 'd',
	    _("this option can be used with the '--colorize' one or the"
	      " '--build-map' one. It chooses a preset color scheme. The id"
	      " argument is an integer that corresponds to a defined color"
	      " shade (ranging from 0)."),
	    _("id"), (gchar*)0, 3.1f);
  optionSet(i++, "translate", 't',
	    _("a file must be loaded. It applies the given translations to the"
	      " loaded file. The units are those of the file. This is available"
	      " for periodic file formats only."),
	    "x:y:z", (gchar*)0, 3.3f);
  optionSet(i++, "box-translate", 'q',
	    _("a file must be loaded. It applies the given translations to the"
	      " loaded file. The translation is applied along box axis, normalised"
              " to 1. This is available for periodic file formats only."),
	    "a:b:c", (gchar*)0, 3.8f);
  optionSet(i++, "expand", 'x',
	    _("a file must be loaded. It applies the given expansion to the loaded"
	      " file. The values are given in box coordinates. This is available"
	      " for periodic file formats only."),
	    "x:y:z", (gchar*)0, 3.4f);
  optionSet(i++, "planes", 'p',
	    _("the argument fileToRender must be called, then the given file of"
	      " the option is parsed as a list of planes and they are rendered."),
	    _("file"), (gchar*)0, 3.2f);
  optionSet(i++, "scalar-field", 'f',
	    _("the argument fileToRender must be called, then the given file of"
	      " the option is parsed as a scalar field and loaded. Use '.'"
              " as a special value to reuse the main filename."),
	    _("{file,.}"), (gchar*)0, 3.3f);
  optionSet(i++, "iso-values", 'v',
	    _("must be used with the '--scalar-field' option, then the given"
	      " surfaces are built and rendered. If a name is appended to a"
	      " value using '#' as a separator, this name is used as the name"
	      " for the iso-surface (i.e. 0.25#Blue). Use specific value"
              " 'auto' to compute isosurfaces at half max and min value."),
	    "v[:v]", (gchar*)0, 3.3f);
  optionSet(i++, "iso-surfaces", 'i',
	    _("the argument fileToRender must be given, then the given file of"
	      " the option is parsed and surfaces are rendered."),
	    _("file"), (gchar*)0, 3.2f);
  optionSet(i++, "build-map", 'b',
	    _("the argument fileToRender must be given, as the '--planes',"
	      " '--color-preset' and '--scalar-field' options used, then the"
	      " given plane 'id' is replaced by a coloured map using given"
	      " scalar field and shade. 'id' ranges from 0. If several ids"
	      " are given, several maps are built."),
	    _("id[:id]"), (gchar*)0, 3.4f);
  optionSet(i++, "log-scale", 0,
	    _("select the scaling method to use with gradients (0: linear,"
              " 1: log scaled and 2 is zero-centred log scale), default is linear scale."),
	    _("id"), (gchar*)0, 3.4f);
  optionSet(i++, "n-iso-lines", 'n',
	    _("when positive, val isolines are plotted on the coloured map."),
	    _("val"), (gchar*)0, 3.4f);
  optionSet(i++, "color-iso-lines", 0,
	    _("when given, generated iso-lines are colourised [R:G:B] or auto"
	      " with the values. The specific value 'auto' will produced"
	      " iso-lines in inversed colours."),
	    _("[R:G:B] or auto"), "[0:0:0]", 3.5f);
  optionSet(i++, "fit-to-box", 0,
	    _("if val is not TRUE, the surfaces use their own bounding box."),
	    _("val"), "TRUE", 3.3f);
  optionSet(i++, "bg-image", 0,
	    _("draw the given image on the background."),
	    _("file"), (gchar*)0, 3.4f);
  optionSet(i++, "option", 'o',
	    _("this is a generic way to give extended option. to V_Sim. As much"
	      " as -o can be used. Each one store a key and its value (boolean,"
	      " integer or float)."),
	    _("id=value"), (gchar*)0, 3.3f);
  optionSet(i++, "window-mode", 'w',
	    _("used to choose the windowing mode. By default the command panel "
	      "and the rendering window are separated. In the 'oneWindow' mode "
	      "they are joined. In the 'renderOnly' mode, the command panel is "
	      "not used."),
	    _("mode"), "classic", 3.5f);
  optionSet(i++, "i-set", 0,
	    _("this flag is used to choose the id of the loaded file if the"
	      " format has support for multiple ids in one file (see XYZ"
	      " format or -posi.d3 ones)."),
	    _("i"), "0", 3.5f);
  optionSet(i++, "value-file", 0,
	    _("specify an XML file with some value information for V_Sim,"
	      " like a list of planes, highlighted nodes... It replaces"
	      " and extend the previous --planes option."),
	    _("file"), (gchar*)0, 3.5f);
  optionSet(i++, "map-precision", 0,
	    _("Give the precision in percent to render the coloured map."),
	    _("prec"), "100", 3.5f);
  optionSet(i++, "map-clamp", 0,
	    _("Set the minimum and maximum values for the coloured map rendering."),
	    _("min:max or auto"), "auto", 3.6f);
  optionSet(i++, "color-clamp", 0,
	    _("Range to adjust values into for colourisation. col specifies"
              "the column to apply the range to. Use -2, -1 and 0 for x, y"
              "and z directions respectively."),
	    _("col#min:max or auto"), "auto", 3.7f);
  optionSet(i++, "scaling-column", 0,
	    _("used with a data file (see -c), it specifies the "
              "column id to be used to scale the nodes."),
	    _("id"), (gchar*)0, 3.7f);
  optionSet(i++, "diff-from", 0,
	    _("Compute the geometric difference between load file and this argument."),
	    _("file"), (gchar*)0, 3.8f);
  optionSet(i++, "phonon-mode", 0,
	    _("Load the given phonon mode."),
	    _("mode"), "0", 3.8f);
  optionSet(i++, "time-offset", 0,
	    _("Displace nodes according to phonons at the given reduced time in [0;1]."),
	    _("offset"), "0", 3.8f);
  optionSet(i++, "phonon-amplitude", 0,
	    _("Maximum displacement for phonons."),
	    _("ampl"), "1.", 3.8f);
  optionSet(i++, NULL, 0, NULL, NULL, NULL, 0.f);
  g_return_if_fail(i == N_OPTIONS);
}

gboolean commandLineExport(const gchar *filename, GError **error)
{
  GString *xml;
  int i;
  gboolean status;
  gchar *desc, *def, *arg;

  xml = g_string_new("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
  xml = g_string_append(xml, "<commandLine>\n");
  for (i = 0; ext_options[i].opt->name; i++)
    {
      if (ext_options[i].opt->val)
	g_string_append_printf
	  (xml, "  <option name=\"%s\" short=\"%c\" version=\"%3.1f\">\n",
	   ext_options[i].opt->name, ext_options[i].opt->val, ext_options[i].version);
      else
	g_string_append_printf
	  (xml, "  <option name=\"%s\" version=\"%3.1f\">\n",
	   ext_options[i].opt->name, ext_options[i].version);

      g_string_append(xml, "    <description");
      if (ext_options[i].arg)
	{
	  arg = g_markup_escape_text(ext_options[i].arg, -1);
	  g_string_append_printf(xml, " arg=\"%s\"", arg);
	  g_free(arg);
	}
      if (ext_options[i].def)
	{
	  def = g_markup_escape_text(ext_options[i].def, -1);
	  g_string_append_printf(xml, " default=\"%s\"", def);
	  g_free(def);
	}
      desc = g_markup_escape_text(ext_options[i].desc, -1);
      g_string_append_printf(xml, ">%s</description>\n", desc);
      g_free(desc);
      g_string_append(xml, "  </option>\n");
    }
  g_string_append(xml, "</commandLine>\n");

  status = g_file_set_contents(filename, xml->str, -1, error);
  g_string_free(xml, TRUE);
  return status;
}

int commandLineParse(int argc, char **argv)
{
  int res, i, nb;
  int option_index;
  gchar **tokens, **tokens2;
  ToolOption *option;
  float min, max;
  gchar *endptr;
  gdouble vald;
  gint64 vali;
  VisuColorRange rg;
  GString *errmess;

  /* We want to read . as floating point separator : in french it is , */
  setlocale(LC_NUMERIC, "C");

  argProgName                  = g_strdup(argv[0]);
  argFileName                  = (char*)0;
  argSpinFileName              = (char *)0;
  spinHidingMode               = 1;
  spinAndAtomic                = FALSE;
  argExportFileName            = (char*)0;
  argColorizeFileName          = (gchar*)0;
  for (i = 0; i < 2; i++)
    argColorizeColUsed[i]      = 0;
  argColorizeColUsed_isPresent = FALSE;
  argColorizePresetColor       = -1;
  argColorizeRange             = g_array_new(FALSE, FALSE, sizeof(VisuColorRange));
  argTranslationsIsSet         = FALSE;
  argBoxTranslationsIsSet      = FALSE;
  argExpandIsSet               = FALSE;
  xWindowWidth                 = 600;
  xWindowHeight                = 600;
  argVisuPlanesFileName        = (gchar*)0;
  argScalarfieldFilenames      = (GList*)0;
  argIsoVisuSurfaceFileName    = (gchar*)0;
  argIsoValues                 = (float*)0;
  argFitToBox                  = TRUE;
  argOptionTable               = (GHashTable*)0;
  argMapVisuPlaneId            = (int*)0;
  argLogScale                  = FALSE;
  argNIsoLines                 = 0;
  argBgImageFile               = (gchar*)0;
  argIsoLinesColor             = g_malloc0(sizeof(float) * 3);
  argMapMinMax                 = (float*)0;
  argWindowMode                = g_strdup("classic");
  argISet                      = 0;
  argValueFile                 = (gchar*)0;
  argMapPrecision              = 100;
  argScalingColumn             = -1;
  argPhononMode                = -1;
  argPhononTime                = -1.f;
  argPhononAmpl                = -1.f;

  withGtk                      = 1;

  optionsInit();

  option = (ToolOption*)0;
  option_index = 0;
  opterr = 0;
  DBG_fprintf(stderr, "Visu CommandLine: short options '%s'.\n", short_options->str);
  while (1)
    {
      res = getopt_long (argc, argv, short_options->str,
			 long_options, &option_index);

      /* test if there's no more option available. */
      if (res == -1)
	break;

      switch (res)
	{
	case 'e':
	  DBG_fprintf(stderr, "Visu Command: option '%s' found with arg '%s'.\n",
		      long_options[option_index].name, optarg);
	  if (!optarg)
	    g_error("The option 'export' needs a parameter.\n");
	  else
	    argExportFileName = g_strdup(optarg);
	  /* This option does not need gtk. */
	  withGtk = 0;
	  break;
	case 'r':
	  DBG_fprintf(stderr, "Visu Command: option '%s' found with arg '%s'.\n",
		      long_options[option_index].name, optarg);
	  if (!optarg)
	    g_error("The option 'resources' needs a parameter.\n");
	  else
	    argResources = g_strdup(optarg);
	  break;
	case 'h':
	  DBG_fprintf(stderr, "Visu Command: option '%s' found.\n",
		      long_options[option_index].name);
	  printInfoMessage();
	  exit(0);
	  break;
	case 'g':
	  DBG_fprintf(stderr, "Visu Command: set the geometry of the X window (%s).\n", optarg);
	  res = sscanf(optarg, "%dx%d", &xWindowWidth, &xWindowHeight);
	  if (res != 2 || xWindowWidth <= 0 || xWindowHeight <=0)
	    {
	      g_warning("Wrong format for geometry"
			" option (<width>x<height> awaited).\n");
	      xWindowWidth = 600;
	      xWindowHeight = 600;
	      break;
	    }
	  break;
	case 's':
	  DBG_fprintf(stderr, "Visu Command: set the filenane for"
		      " spin rendering to '%s'.\n", optarg);
	  if (!optarg)
	    {
	      g_error("The option 'spin-file' needs a parameter.\n");
	    }
	  argSpinFileName = g_strdup(optarg);
	  break;
	case 'm':
	  DBG_fprintf(stderr, "Visu Command: set hiding-mode to '%s' in spin rendering.\n", optarg);
	  res = sscanf(optarg, "%d", &spinHidingMode);
	  if (res != 1 || spinHidingMode < 0)
	    {
	      g_warning("Wrong format for hiding-mode"
			" option (integer awaited).\n");
	      spinHidingMode = 0;
	    }
	  break;
	case 'a':
	  DBG_fprintf(stderr, "Visu Command: set spin-and-atomic to TRUE in spin rendering.\n");
	  spinAndAtomic = TRUE;
	  break;
	case 'b':
	  DBG_fprintf(stderr, "Visu Command: get the values for"
		      " maps '%s'.\n", optarg);
	  if (!optarg)
	    {
	      g_error("The option 'build-map' needs a parameter.\n");
	    }
	  tokens = g_strsplit(optarg, ":", -1);
	  /* Count number of tokens. */
	  for (nb = 0; tokens[nb]; nb++);
          DBG_fprintf(stderr, " | allocate %d ids.\n", nb + 1);
	  argMapVisuPlaneId = g_malloc(sizeof(int) * (nb + 1));
	  nb = 1;
	  for (i = 0; tokens[i]; i++)
	    {
	      res = sscanf(tokens[i], "%d", argMapVisuPlaneId + nb);
	      if (res != 1 || argMapVisuPlaneId[nb] < 0)
		g_warning("Parse error reading values from option "
			  "'build-map', unknown plane id '%s'.\n", tokens[i]);
	      else
		nb += 1;
	    }
          DBG_fprintf(stderr, " | found %d valid ids.\n", nb - 1);
	  argMapVisuPlaneId[0] = nb - 1;
	  g_strfreev(tokens);
	  break;
	case 'n':
	  res = sscanf(optarg, "%d", &argNIsoLines);
	  if (res != 1 || argNIsoLines < 0)
	    {
	      g_warning("Wrong format for n-iso-lines option (id >= 0 awaited).\n");
	      argNIsoLines = 0;
	      break;
	    }
	  DBG_fprintf(stderr, "Visu Command: set the number of isolines"
		      " for coloured map '%d'.\n", argNIsoLines);
	  break;
	case 'c':
	  DBG_fprintf(stderr, "Visu Command: set the filenane for colorization to '%s'.\n", optarg);
	  if (!optarg)
	    {
	      g_error("The option 'colorize' needs a parameter.\n");
	    }
          argColorizationIsFile = TRUE;
          if (strstr(optarg, COLORIZATION_PROP_FLAG) == optarg)
            {
              argColorizeFileName = g_strdup(optarg + strlen(COLORIZATION_PROP_FLAG));
              argColorizationIsFile = FALSE;
            }
          else
            argColorizeFileName = g_strdup(optarg);
	  break;
	case 'u':
	  DBG_fprintf(stderr, "Visu Command: set the used columns for"
		      " colorize data (%s).\n", optarg);
	  res = sscanf(optarg, "%d:%d:%d", argColorizeColUsed,
		       argColorizeColUsed + 1, argColorizeColUsed + 2);
	  if (res != 3 || argColorizeColUsed[0] < -3 ||
	      argColorizeColUsed[1] < -3 || argColorizeColUsed[2] < -3)
	    {
	      g_warning("Wrong format for use-column"
			" option (<channel-1>:<channel-2>:<channel-3> awaited).\n");
	      for (i = 0; i < 2; i++)
		argColorizeColUsed[i] = -3;
	      break;
	    }
	  argColorizeColUsed_isPresent = TRUE;
	  break;
	case 'd':
	  DBG_fprintf(stderr, "Visu Command: set a previously defined color scheme (%s).\n", optarg);
	  res = sscanf(optarg, "%d", &argColorizePresetColor);
	  if (res != 1 || argColorizePresetColor < 0)
	    {
	      g_warning("Wrong format for color-preset"
			" option (positive integer awaited).\n");
	      argColorizePresetColor = -1;
	      break;
	    }
	  break;
	case 't':
	  DBG_fprintf(stderr, "Visu Command: set the translations (%s).\n", optarg);
	  res = sscanf(optarg, "%f:%f:%f", argTranslations,
		       argTranslations + 1, argTranslations + 2);
	  if (res != 3)
	    {
	      g_warning("Wrong format for translation"
			" option (<x>:<y>:<z> awaited).\n");
	      break;
	    }
	  else
	    argTranslationsIsSet = TRUE;
	  break;
	case 'q':
	  DBG_fprintf(stderr, "Visu Command: set the box translations (%s).\n", optarg);
	  res = sscanf(optarg, "%f:%f:%f", argBoxTranslations,
		       argBoxTranslations + 1, argBoxTranslations + 2);
	  if (res != 3)
	    {
	      g_warning("Wrong format for translation"
			" option (<a>:<b>:<c> awaited).\n");
	      break;
	    }
	  else
	    argBoxTranslationsIsSet = TRUE;
	  break;
	case 'x':
	  DBG_fprintf(stderr, "Visu Command: set the extension (%s).\n", optarg);
	  res = sscanf(optarg, "%f:%f:%f", argExtension,
		       argExtension + 1, argExtension + 2);
	  if (res != 3 || argExtension[0] < 0. ||
	      argExtension[1] < 0. || argExtension[2] < 0.)
	    {
	      g_warning("Wrong format for expand"
			" option (<x>:<y>:<z> awaited >= 0.).\n");
	      break;
	    }
	  else
	    argExpandIsSet = TRUE;
	  break;
	case 'p':
	  DBG_fprintf(stderr, "Visu Command: set the filenane for planes to '%s'.\n", optarg);
	  if (!optarg)
	    {
	      g_error("The option 'planes' needs a parameter.\n");
	    }
	  argVisuPlanesFileName = g_strdup(optarg);
	  break;
	case 'f':
	  DBG_fprintf(stderr, "Visu Command: set the filenane for"
		      " a scalar field '%s'.\n", optarg);
	  if (!optarg)
	    {
	      g_error("The option 'field' needs a parameter.\n");
	    }
          if (strcmp(".", optarg))
            argScalarfieldFilenames = g_list_append(argScalarfieldFilenames,
                                                    g_strdup(optarg));
          else
            argScalarfieldFilenames = g_list_append(argScalarfieldFilenames,
                                                    g_strdup(argv[argc - 1]));
	  break;
	case 'v':
	  DBG_fprintf(stderr, "Visu Command: get the values for"
		      " a scalar field '%s'.\n", optarg);
	  if (!optarg)
	    {
	      g_error("The option 'iso-values' needs a parameter.\n");
	    }
	  tokens = g_strsplit(optarg, ":", -1);
	  /* Count number of tokens. */
	  for (nb = 0; tokens[nb]; nb++);
	  argIsoValues = g_malloc(sizeof(float) * (nb + 1));
	  argIsoNames = g_malloc(sizeof(gchar*) * (nb + 1));
	  nb = 0;
	  for (i = 0; tokens[i]; i++)
	    {
              if (!g_strcmp0(tokens[i], "auto"))
                {
                  argIsoValues[nb] = G_MAXFLOAT;
                  argIsoNames[nb] = (gchar*)0;
                  nb += 1;
                }
              else
                {
                  tokens2 = g_strsplit(tokens[i], "#", 2);
                  res = sscanf(tokens2[0], "%f", argIsoValues + nb);
                  if (res != 1)
                    g_warning("Parse error reading values from option "
                              "'iso-values', unknown number '%s'.\n", tokens[i]);
                  else
                    {
                      if (tokens2[1])
                        /* A name for the value is given. */
                        argIsoNames[nb] = g_strdup(tokens2[1]);
                      else
                        argIsoNames[nb] = (gchar*)0;
                      nb += 1;
                    }
                  g_strfreev(tokens2);
                }
	    }
          argIsoNames[nb] = (gchar*)0;
	  argNbIsoValues = nb;
	  g_strfreev(tokens);
	  break;
	case 'i':
	  DBG_fprintf(stderr, "Visu Command: set the filenane for"
		      " an isosurfaces '%s'.\n", optarg);
	  if (!optarg)
	    {
	      g_error("The option 'iso-surfaces' needs a parameter.\n");
	    }
	  argIsoVisuSurfaceFileName = g_strdup(optarg);
	  break;
	case 'o':
	  DBG_fprintf(stderr, "Visu Command: read an extended option.\n");
	  if (!optarg)
	    {
	      g_error("The option 'option' needs a parameter.\n");
	    }
	  tokens = g_strsplit(optarg, "=", 2);
	  if (!argOptionTable)
	    argOptionTable = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, \
                                                   (GDestroyNotify)tool_option_free);
	  if (tokens[1] && tokens[1][0] == 'T' && !tokens[1][1])
	    {
	      option = tool_option_new(tokens[0], tokens[0], G_TYPE_BOOLEAN);
	      g_value_set_boolean(tool_option_getValue(option), TRUE);
              DBG_fprintf(stderr, " | boolean : T\n");
	    }
	  else if (tokens[1] && tokens[1][0] == 'F' && !tokens[1][1])
	    {
	      option = tool_option_new(tokens[0], tokens[0], G_TYPE_BOOLEAN);
	      g_value_set_boolean(tool_option_getValue(option), FALSE);
              DBG_fprintf(stderr, " | boolean : F\n");
	    }
          else if (tokens[1])
            {
              vald = g_ascii_strtod(tokens[1], &endptr);
              if (endptr != tokens[1] && !endptr)
                {
                  vali = g_ascii_strtoll(tokens[1], &endptr, 10);
                  if (endptr != tokens[1] && !endptr)
                    {
                      option = tool_option_new(tokens[0], tokens[0], G_TYPE_INT);
                      g_value_set_int(tool_option_getValue(option), (int)vali);
                      DBG_fprintf(stderr, " | float : '%d'\n", (int)vali);
                    }
                  else
                    {
                      option = tool_option_new(tokens[0], tokens[0], G_TYPE_FLOAT);
                      g_value_set_float(tool_option_getValue(option), (float)vald);
                      DBG_fprintf(stderr, " | float : '%g'\n", (float)vald);
                    }
                }
              else
                {
                  option = tool_option_new(tokens[0], tokens[0], G_TYPE_STRING);
                  g_value_set_string(tool_option_getValue(option), tokens[1]);
                  DBG_fprintf(stderr, " | string : '%s'\n", tokens[1]);
                }
	    }
	  else if (!tokens[1])
	    {
	      option = tool_option_new(tokens[0], tokens[0], G_TYPE_BOOLEAN);
	      g_value_set_boolean(tool_option_getValue(option), TRUE);
              DBG_fprintf(stderr, " | boolean : T\n");
	    }
	  else
	    {
	      g_error("Unparsable value for option 'option'.\n");
	    }
          g_hash_table_insert(argOptionTable, (gpointer)tool_option_getName(option),
                              (gpointer)option);
	  g_strfreev(tokens);
	  break;
	case 'w':
	  DBG_fprintf(stderr, "Visu Command: set the window mode to"
		      " '%s'.\n", optarg);
	  if (!optarg)
	    {
	      g_error("The option 'window-mode' needs a parameter.\n");
	    }
	  argWindowMode = g_strdup(optarg);
	  if (strcmp(argWindowMode, "classic") &&
	      strcmp(argWindowMode, "renderOnly") &&
	      strcmp(argWindowMode, "oneWindow"))
	    {
	      g_error("The option 'window-mode' accepts only 'calssic'"
		      ", 'oneWindow' and 'renderOnly' as a parameter.\n");
	    }
	  break;
	case 0:
	  /* Long option only. */
	  if (!strcmp(long_options[option_index].name, "fit-to-box"))
	    argFitToBox = (!strcmp(optarg, "TRUE"));
	  else if (!strcmp(long_options[option_index].name, "log-scale"))
	    {
	      if (sscanf(optarg, "%ud", &argLogScale) != 1)
		argLogScale = TOOL_MATRIX_SCALING_LINEAR;
	      if (argLogScale >= TOOL_MATRIX_SCALING_N_VALUES)
		argLogScale = TOOL_MATRIX_SCALING_LINEAR;
	    }
	  else if (!strcmp(long_options[option_index].name, "bg-image"))
	    {
	      if (strcmp(optarg, "V_Sim"))
		argBgImageFile = g_strdup(optarg);
	      else
		argBgImageFile = g_build_filename(V_SIM_PIXMAPS_DIR,
						  "logo_grey.png", NULL);
	    }
	  else if (!strcmp(long_options[option_index].name, "color-iso-lines"))
	    {
	      if (sscanf(optarg, "[%f:%f:%f]", argIsoLinesColor,
			 argIsoLinesColor + 1, argIsoLinesColor + 2) != 3)
		{
		  if (!strcmp(optarg, "auto") || !strcmp(optarg, "Auto"))
		    {
		      g_free(argIsoLinesColor);
		      argIsoLinesColor = (float*)0;
		    }
		  else
		    {
		      g_error("Can't read any color from '%s' following"
			      " the [R:G:B] scheme or the 'auto' keyword.\n", optarg);
		    }
		}
	      if (argIsoLinesColor)
		{
		  argIsoLinesColor[0] = CLAMP(argIsoLinesColor[0], 0.f, 1.f);
		  argIsoLinesColor[1] = CLAMP(argIsoLinesColor[1], 0.f, 1.f);
		  argIsoLinesColor[2] = CLAMP(argIsoLinesColor[2], 0.f, 1.f);
		}
	    }
	  else if (!strcmp(long_options[option_index].name, "i-set"))
	    {
	      if (sscanf(optarg, "%u", &argISet) != 1)
		{
		  g_warning("Wrong format for i-set"
			    " option (integer awaited >= 0).\n");
                  argISet = 0;
		  break;
		}
	    }
	  else if (!strcmp(long_options[option_index].name, "value-file"))
	    argValueFile = g_strdup(optarg);
	  else if (!strcmp(long_options[option_index].name, "map-precision"))
	    {
	      if (sscanf(optarg, "%u", &argMapPrecision) != 1 || argMapPrecision == 0)
		{
		  g_warning("Wrong value for map-precision"
			    " option (integer awaited > 0).\n");
		  argMapPrecision = 100;
		  break;
		}
              DBG_fprintf(stderr, "Visu CommandLine: read map precision %u.\n",
                          argMapPrecision);
	    }
	  else if (!strcmp(long_options[option_index].name, "map-clamp"))
	    {
	      argMapMinMax = g_malloc0(sizeof(float) * 2);
	      if (sscanf(optarg, "%f:%f", argMapMinMax,
			 argMapMinMax + 1) != 2)
		{
		  if (!strcmp(optarg, "auto") || !strcmp(optarg, "Auto"))
		    {
		      g_free(argMapMinMax);
		      argMapMinMax = (float*)0;
		    }
		  else
		    {
		      g_error("Can't read any bounds from '%s' following"
			      " the min:max scheme or the 'auto' keyword.\n", optarg);
		    }
		}
	      if (argMapMinMax && argMapMinMax[1] <= argMapMinMax[0])
		{
		  g_error("Bounds given in '%s' are wrong,"
			  " min value is greater than max value.\n", optarg);
		  g_free(argMapMinMax);
		  argMapMinMax = (float*)0;
		}
	    }
	  else if (!strcmp(long_options[option_index].name, "color-clamp"))
	    {
	      if (sscanf(optarg, "%d#%f:%f", &i, &min, &max) == 3)
                {
                  if (max <= min)
                    {
                      g_error("Bounds given in '%s' are wrong,"
                              " min value is greater than max value.\n", optarg);
                    }
                  
                  DBG_fprintf(stderr, "Visu Command: found color range %d -> [%g;%g].\n",
                              i, min, max);
                  rg.column = i;
                  rg.min = min;
                  rg.max = max;
                  g_array_append_val(argColorizeRange, rg);
                }
              else
                {
                  g_error("Can't read any bounds from '%s' following"
                          " the col#min:max scheme or the 'auto' keyword.\n", optarg);
                }
	    }
	  else if (!strcmp(long_options[option_index].name, "scaling-column"))
	    {
	      if (sscanf(optarg, "%d", &argScalingColumn) != 1 || argScalingColumn < 1)
		{
		  g_warning("Wrong value for scaling-column"
			    " option (integer awaited > 0).\n");
		  argScalingColumn = -1;
		  break;
		}
	      else
		argScalingColumn -= 1;
	    }
	  else if (!strcmp(long_options[option_index].name, "diff-from"))
	    {
              argGeoDiff = g_strdup(optarg);
	    }
	  else if (!strcmp(long_options[option_index].name, "phonon-mode"))
	    {
	      if (sscanf(optarg, "%d", &argPhononMode) != 1 || argPhononMode < 1)
		{
		  g_warning("Wrong value for phonon-mode"
			    " option (integer awaited > 0).\n");
		  argPhononMode = -1;
		  break;
		}
	      else
		argPhononMode -= 1;
	    }
	  else if (!strcmp(long_options[option_index].name, "time-offset"))
	    {
	      if (sscanf(optarg, "%f", &argPhononTime) != 1 ||
                  argPhononTime < 0. || argPhononTime >= 1.)
		{
		  g_warning("Wrong value for time-offset"
			    " option (float in [0;1[).\n");
		  argPhononTime = -1.f;
		  break;
		}
	    }
	  else if (!strcmp(long_options[option_index].name, "phonon-amplitude"))
	    {
	      if (sscanf(optarg, "%f", &argPhononAmpl) != 1 ||
                  argPhononAmpl < 0.)
		{
		  g_warning("Wrong value for phonon-amplitude"
			    " option (positive float).\n");
		  argPhononAmpl = -1.f;
		  break;
		}
	    }
	  else
	    {
	      g_error("Unknown long option '--%s'.\n",
		      long_options[option_index].name);
	    }
	  break;
	default:
	  g_warning("Unknown option '%s'.", argv[optind - 1]);
	  printInfoMessage();
	  exit(0);
	}
    }

  if (argc - optind == 1)
    {
      DBG_fprintf(stderr, "Visu Command: there is one argument '%s'.\n", argv[optind]);
      argFileName = tool_path_normalize(argv[optind]);
    }
  else if (argc - optind == 0)
    return 0;
  else
    {
      errmess = g_string_new("This program allows only up to one argument, found:");
      for (i = optind; i < argc; i++)
        g_string_append_printf(errmess, "\n- %s", argv[i]);
      g_warning("%s", errmess->str);
      g_string_free(errmess, TRUE);
    }

  /* Consistency check between options. */
  if (argScalarfieldFilenames && argIsoVisuSurfaceFileName)
    {
      g_error("The options --iso-surfaces and --scalar-field are exclusive.");
    }
  if (argValueFile && argVisuPlanesFileName)
    {
      g_error("The options --planes and --value-file are exclusive.");
    }

  return 0;
}

char* commandLineGet_ArgFilename(void)
{
  return argFileName;
}

char* commandLineGet_ArgSpinFileName(void)
{
  return argSpinFileName;
}

int commandLineGet_WithGtk(void)
{
  return withGtk;
}

char* commandLineGet_ExportFileName(void)
{
  return argExportFileName;
}

void commandLineGet_XWindowGeometry(int *width, int *height)
{
  *width = xWindowWidth;
  *height = xWindowHeight;
}
/**
 * commandLineGet_colorizeSource:
 * @isFile: a location to store if the source is a filename.
 *
 * Retrieve the string given on command line that is used to identify
 * the source of colourisation, either a #VisuNodeValues name or a filename.
 *
 * Since: 3.8
 *
 * Returns: a string used to identify a #VisuNodeValues object or a filename.
 **/
const gchar* commandLineGet_colorizeSource(gboolean *isFile)
{
  if (isFile)
    *isFile = argColorizationIsFile;
  return argColorizeFileName;
}
int* commandLineGet_colorizeColUsed(void)
{
  if (argColorizeColUsed_isPresent)
    return argColorizeColUsed;
  else
    return (int*)0;
}
/**
 * commandLineGet_translation:
 * @boxTranslation: a location to store if returned translation is
 * given is cartesian or box coordinates.
 *
 * This method retrieves the value of the option --translate or -t. This value consists
 * of three floating values. If -q or --box-translation is present,
 * the returned value is the translations along box axis.
 *
 * Returns: the three values of the option --translate.
 */
float* commandLineGet_translation(gboolean *boxTranslation)
{
  if (boxTranslation)
    *boxTranslation = argBoxTranslationsIsSet;

  if (argTranslationsIsSet)
    return argTranslations;
  else if (argBoxTranslationsIsSet)
    return argBoxTranslations;
  else
    return (float*)0;
}
float* commandLineGet_extension(void)
{
  if (argExpandIsSet)
    return argExtension;
  else
    return (float*)0;
}
int commandLineGet_presetColor(void)
{
  return argColorizePresetColor;
}
gchar* commandLineGet_planesFileName(void)
{
  return argVisuPlanesFileName;
}
int commandLineGet_spinHidingMode(void)
{
  return spinHidingMode;
}
gboolean commandLineGet_spinAndAtomic(void)
{
  return spinAndAtomic;
}
float* commandLineGet_isoValues(int *nb)
{
  g_return_val_if_fail(nb, (float*)0);

  *nb = argNbIsoValues;
  return argIsoValues;
}
const gchar** commandLineGet_isoNames(int *nb)
{
  g_return_val_if_fail(nb, (const gchar**)0);

  *nb = argNbIsoValues;
  return (const gchar**)argIsoNames;
}
/**
 * commandLineGet_scalarFieldFileNames:
 *
 * This method retrieves the filename given by the option --scalar-field or -f.
 *
 * Returns: (element-type filename) (transfer none): a filename, the
 * string is owned by V_Sim.
 */
const GList* commandLineGet_scalarFieldFileNames(void)
{
  return argScalarfieldFilenames;
}
gchar* commandLineGet_isoVisuSurfaceFileName(void)
{
  return argIsoVisuSurfaceFileName;
}
gboolean commandLineGet_fitToBox(void)
{
  DBG_fprintf(stderr, "Visu CommandLine: get fit to box option %d.\n", argFitToBox);
  return argFitToBox;
}
GHashTable* commandLineGet_options(void)
{
  return argOptionTable;
}
gchar* commandLineGet_resourcesFile(void)
{
  return argResources;
}
/**
 * commandLineGet_coloredMap:
 *
 * One can pass options on the command line to create colored maps on planes.
 *
 * Returns: an array of plane indexes to be maped, -1 terminated.
 *
 * Since: 3.6
 */
int* commandLineGet_coloredMap(void)
{
  if (argMapVisuPlaneId && argMapVisuPlaneId[0])
    return argMapVisuPlaneId;
  else
    return (int*)0;
}
ToolMatrixScalingFlag commandLineGet_logScale(void)
{
  return argLogScale;
}
guint commandLineGet_nIsoLines(void)
{
  return (guint)argNIsoLines;
}
gchar* commandLineGet_bgImage(void)
{
  return argBgImageFile;
}
float* commandLineGet_isoLinesColor(void)
{
  return argIsoLinesColor;
}
gchar* commandLineGet_windowMode(void)
{
  return argWindowMode;
}
guint commandLineGet_iSet(void)
{
  return (guint)argISet;
}
gchar* commandLineGet_valueFile(void)
{
  return argValueFile;
}
/**
 * commandLineGet_mapPrecision:
 * 
 * The coloured maps can be rendered with more or less accuracy.
 *
 * Since: 3.6
 *
 * Returns: the precision requested by the user.
 */
guint commandLineGet_mapPrecision(void)
{
  return argMapPrecision;
}
/**
 * commandLineGet_mapMinMax:
 * 
 * The coloured maps can be manually scaled.
 *
 * Since: 3.6
 *
 * Returns: the scaling values for manual scaling of coloured maps.
 */
float* commandLineGet_mapMinMax(void)
{
  return argMapMinMax;
}
/**
 * commandLineGet_colorMinMax:
 * 
 * The external data file colourisation can be manually scaled.
 *
 * Since: 3.7
 *
 * Returns: the scaling values for manual scaling of external data values.
 */
GArray* commandLineGet_colorMinMax(void)
{
  return argColorizeRange;
}
/**
 * commandLineGet_programName:
 *
 * Get argv[0].
 *
 * Since: 3.7
 *
 * Returns: the program name used to run V_Sim.
 */
const gchar* commandLineGet_programName(void)
{
  return argProgName;
}
/**
 * commandLineGet_scalingColumn:
 *
 * Get the column used to scale nodes, if any given.
 *
 * Since: 3.7
 *
 * Returns: -1 if not provided, or a column id.
 */
int commandLineGet_scalingColumn(void)
{
  return argScalingColumn;
}
/**
 * commandLineGet_geodiff:
 *
 * Get the name of the file to compute the difference from, if any given.
 *
 * Since: 3.8
 *
 * Returns: #NULL if not provided, or a filename.
 */
const gchar* commandLineGet_geodiff(void)
{
  return argGeoDiff;
}
/**
 * commandLineGet_phononMode:
 *
 * Get the desired phonon mode.
 *
 * Since: 3.8
 *
 * Returns: -1 if option is not set.
 **/
gint commandLineGet_phononMode(void)
{
  return argPhononMode;
}
/**
 * commandLineGet_phononTime:
 *
 * Get the time offset to apply dislacement from phonons.
 *
 * Since: 3.8
 *
 * Returns: -1.f if the option is not set.
 **/
gfloat commandLineGet_phononTime(void)
{
  return argPhononTime;
}
/**
 * commandLineGet_phononAmpl:
 *
 * Get the amplitude to apply dislacement from phonons.
 *
 * Since: 3.8
 *
 * Returns: -1.f if the option is not set.
 **/
gfloat commandLineGet_phononAmpl(void)
{
  return argPhononAmpl;
}
/**
 * commandLineFree_all:
 *
 * Release all allocated memory related to the command line options.
 *
 * Since: 3.5
 */
void commandLineFree_all(void)
{
  DBG_fprintf(stderr, "Command Line: free all.\n - the arguments\n");

  if (argProgName)
    g_free(argProgName);

  if (argFileName)
    g_free(argFileName);

  if (argResources)
    g_free(argResources);

  if (argSpinFileName)
    g_free(argSpinFileName);

  if (argExportFileName)
    g_free(argExportFileName);
  if (argWindowMode)
    g_free(argWindowMode);
  if (argValueFile)
    g_free(argValueFile);

  if (argColorizeFileName)
    g_free(argColorizeFileName);
  if (argColorizeRange)
    g_array_free(argColorizeRange, TRUE);

  if (argScalarfieldFilenames)
    g_list_free_full(argScalarfieldFilenames, g_free);
  if (argIsoVisuSurfaceFileName)
    g_free(argIsoVisuSurfaceFileName);
  if (argIsoValues)
    g_free(argIsoValues);
  if (argIsoNames)
    g_strfreev(argIsoNames);

  if (argMapVisuPlaneId)
    g_free(argMapVisuPlaneId);
  if (argIsoLinesColor)
    g_free(argIsoLinesColor);

  if (argBgImageFile)
    g_free(argBgImageFile);

  if (argOptionTable)
    g_hash_table_destroy(argOptionTable);

  if (argVisuPlanesFileName)
    g_free(argVisuPlanesFileName);

  if (argGeoDiff)
    g_free(argGeoDiff);

  g_free(argMapMinMax);

  DBG_fprintf(stderr, " - the descriptions\n");
  g_string_free(short_options, TRUE);
  g_free(ext_options);
  g_free(long_options);
}
