/*
Copyright (c) 2004-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/



/**	@file dkfigco.c	Conversion job module.
*/



/** Inside the dkfigco module.
*/
#define DKFIGCO_C 1



#include "dkfig.h"




#line 55 "dkfigco.ctr"





/**	System configuration directory (string constant).
*/
static char default_config_filename[] = { DK_SYSCONFDIR "/fig2vect.cfg" };



/**	Confiration file name.
*/
static char short_config_filename[] = { "fig2vect.cfg" };



/** File open mode.
*/
static char str_r[] = { "r" };



/** Preference key to retrieve the output language.
*/
static char pk_language[] = { "/output-language" };



/** Preference key to retrieve make-mode flag.
*/
static char pk_make[]     = { "/make" };



/** Preference key to retrieve maximum line length.
*/
static char pk_linelgt[]  = { "/line-length" };



/** Preference key to retrieve UTF-8 handling flag.
*/
static char pk_utf8[]	  = { "/utf-8" };



/** Empty string, used when storing preferences.
*/
static char pv_empty[]	  = { "" };



/** Preference key to retrieve shared directory.
*/
static char pk_shdir[]	  = { "/dir/shared" };



/** Preference key to retrieve UTF-8 to LaTeX tables directory.
*/
static char str_uc2l[]	  = { "/uc2lat-t" };



/** Preference key to retrieve flag for suppressing info about alpha channels.
*/
static char pk_alph[] = { "/pdf/suppress-alpha-info" };



/** String to test values for on or off.
*/
static char *bool_val[] = { "off", "on", NULL };



/** Suffixes for different output file types.
*/
static char *output_suffixes[] = {
  ".mp",
  ".eps",
  ".svg",
  ".tex",
  ".bb",
  ".pdf",
  ".ps",
  ".java"
};



/** Kkeywords for long options.
*/
static char *longopt_keywords[] = {
  /*  0 */ "v$ersion",
  /*  1 */ "h$elp",
  /*  2 */ "c$onfigure",
  /*  3 */ "u$nconfigure",
  /*  4 */ "sh$ow-configuration",
  /*  5 */ "m$ake",
  /*  6 */ "o$ption",
  /*  7 */ "l$anguage",
  /*  8 */ "r$eset",
  /*  9 */ "si$lently",
  /* 10 */ "configuration-file",
  /* 11 */ "write-configuration-file",
  NULL
};



/** Flag: Have case insensitive file names.
*/
static int have_fncaseins =
#if DK_HAVE_FNCASEINS
1
#else
0
#endif
;



/** Key/value pairs to create the message array.
*/
static dk_key_value_t kv[] = {
  /*  0 */
  {
  (char *)"/m/01",
  (char *)"on" 
  },
  /*  1 */
  {
  (char *)"/m/02", 
  (char *)"off" 
  },
  /*  2 */
  {
  (char *)"/m/03", 
  (char *)"behave like make when running on directories" 
  },
  /*  3 */
  {
  (char *)"/m/04", 
  (char *)"output language" 
  },
  /*  4 */
  {
  (char *)"/m/05", 
  (char *)"<undefined>" 
  },
  /*  5 */
  { 
  (char *)"/m/06", 
  (char *)"Current configuration:" 
  },
  /*  6 */
  { 
  (char *)"/m/07", 
  (char *)"No gzip decompression available." 
  },
  /*  7 */
  { 
  (char *)"/m/08", 
  (char *)"No bzip2 decompression available." 
  },
  /*  8 */
  { 
  (char *)"/m/09", 
  (char *)"Failed to save configuration!" 
  },
  /*  9 */
  { 
  (char *)"/m/10", 
  (char *)"Configuration file: " 
  },
  /* 10 */
  { 
  (char *)"/m/11", 
  (char *)"." 
  },
  /* 11 */
  { 
  (char *)"/m/12", 
  (char *)"Not enough memory!" 
  },
  /* 12 */
  { 
  (char *)"/m/13", 
  (char *)"Syntax error!" 
  },
  /* 13 */
  { 
  (char *)"/m/14", 
  (char *)"Mathematical error!" 
  },
  /* 14 */
  { 
  (char *)"/m/15", 
  (char *)"Unknown base driver: " 
  },
  /* 15 */
  { 
  (char *)"/m/16", 
  (char *)"!"
  },
  /* 16 */
  { 
  (char *)"/m/17", 
  (char *)"Reading input file \"" 
  },
  /* 17 */
  { 
  (char *)"/m/18", 
  (char *)"\"." 
  },
  /* 18 */
  { 
  (char *)"/m/19", 
  (char *)"The input file was read successfully." 
  },
  /* 19 */
  { 
  (char *)"/m/20", 
  (char *)"Error while reading input file." 
  },
  /* 20 */
  { 
  (char *)"/m/21", 
  (char *)"Writing output file \"" 
  },
  /* 21 */
  { 
  (char *)"/m/22", 
  (char *)"\"." 
  },
  /* 22 */
  { 
  (char *)"/m/23", 
  (char *)"Output file finished." 
  },
  /* 23 */
  { 
  (char *)"/m/24", 
  (char *)"Failed to open stream for standard input!" 
  },
  /* 24 */
  { 
  (char *)"/m/25", 
  (char *)"Failed to open stream for standard output!" 
  },
  /* 25 */
  { 
  (char *)"/m/26", 
  (char *)"Base driver name \"" 
  },
  /* 26 */
  { 
  (char *)"/m/27", 
  (char *)"\" unknown!"
  },
  /* 27 */
  { 
  (char *)"/m/28", 
  (char *)"Missing base driver name!" 
  },
  /* 28 */
  { 
  (char *)"/m/29", 
  (char *)"Skipping \"" 
  },
  /* 29 */
  { 
  (char *)"/m/30", 
  (char *)"\"." 
  },
  /* 30 */
  { 
  (char *)"/m/31", 
  (char *)"Output file name \"" 
  },
  /* 31 */
  { 
  (char *)"/m/32", 
  (char *)"\" too long!" 
  },
  /* 32 */
  { 
  (char *)"/m/33", 
  (char *)"Invalid text handling \"" 
  },
  /* 33 */
  { 
  (char *)"/m/34", 
  (char *)"\"!" 
  },
  /* 34 */
  { 
  (char *)"/m/35", 
  (char *)"Invalid font chooser \"" 
  },
  /* 35 */
  { 
  (char *)"/m/36", 
  (char *)"\"!" 
  },
  /* 36 */
  { 
  (char *)"/m/37", 
  (char *)"Invalid size keyword \"" 
  },
  /* 37 */
  { 
  (char *)"/m/38", 
  (char *)"\"!" 
  },
  /* 38 */
  { 
  (char *)"/m/39", 
  (char *)"Invalid mbox specification!"
  },
  /* 39 */
  { 
  (char *)"/m/40", 
  (char *)"Invalid keyword \"" 
  },
  /* 40 */
  { 
  (char *)"/m/41", 
  (char *)"\" in text handling!" 
  },
  /* 41 */
  { 
  (char *)"/m/42", 
  (char *)"Option \"" 
  },
  /* 42 */
  { 
  (char *)"/m/43", 
  (char *)"\" too long!" 
  },
  /* 43 */
  { 
  (char *)"/m/44", 
  (char *)"Too many points!" 
  },
  /* 44 */
  { 
  (char *)"/m/45", 
  (char *)"Internal setup error!" 
  },
  /* 45 */
  { 
  (char *)"/m/46", 
  (char *)"Preamble file \"" 
  },
  /* 46 */
  { 
  (char *)"/m/47", 
  (char *)"\" not found!" 
  },
  /* 47 */
  { 
  (char *)"/m/48", 
  (char *)"Preamble file name \"" 
  },
  /* 48 */
  { 
  (char *)"/m/49", 
  (char *)"\" too long!" 
  },
  /* 49 */
  { 
  (char *)"/m/50", 
  (char *)"Failed to insert preamble file \"" 
  },
  /* 50 */
  { 
  (char *)"/m/51", 
  (char *)"\"!" 
  },
  /* 51 */
  { 
  (char *)"/m/52", 
  (char *)"Forward arrowhead removed." 
  },
  /* 52 */
  { 
  (char *)"/m/53", 
  (char *)"Backward arrowhead removed." 
  },
  /* 53 */
  { 
  (char *)"/m/54", 
  (char *)"Skipping included image." 
  },
  /* 54 */
  { 
  (char *)"/m/55", 
  (char *)"Start of new output figure." 
  },
  /* 55 */
  { 
  (char *)"/m/56", 
  (char *)"Gathering font information." 
  },
  /* 56 */
  { 
  (char *)"/m/57", 
  (char *)"Starting output." 
  },
  /* 57 */
  { 
  (char *)"/m/58", 
  (char *)"Output finished." 
  },
  /* 58 */
  { 
  (char *)"/m/59", 
  (char *)"Very long line, possibly truncated."
  },
  /* 59 */
  { 
  (char *)"/m/60", 
  (char *)"Failed to create stream for output file!" 
  },
  /* 60 */
  { 
  (char *)"/m/61", 
  (char *)"Error while executing \"" 
  },
  /* 61 */
  { 
  (char *)"/m/62", 
  (char *)"\"!" 
  },
  /* 62 */
  { 
  (char *)"/m/63", 
  (char *)"Processing included image \"" 
  },
  /* 63 */
  { 
  (char *)"/m/64", 
  (char *)"\"." 
  },
  /* 64 */
  { 
  (char *)"/m/65", 
  (char *)"Image finished." 
  },
  /* 65 */
  { 
  (char *)"/m/66", 
  (char *)"Bitmap-to-EPS conversion failed for \"" 
  },
  /* 66 */
  { 
  (char *)"/m/67", 
  (char *)"Unknown or unsupported file type!" 
  },
  /* 67 */
  { 
  (char *)"/m/68", 
  (char *)"Failed to create temporary file name!" 
  },
  /* 68 */
  { 
  (char *)"/m/69", 
  (char *)"NetPBM format not supported!" 
  },
  /* 69 */
  { 
  (char *)"/m/70", 
  (char *)"JPEG format not supported!" 
  },
  /* 70 */
  { 
  (char *)"/m/71", 
  (char *)"PNG format not supported!" 
  },
  /* 71 */
  { 
  (char *)"/m/72", 
  (char *)"Problem while reading image file!"
  },
  /* 72 */
  { 
  (char *)"/m/73", 
  (char *)"Failed to create output streams!" 
  },
  /* 73 */
  { 
  (char *)"/m/74", 
  (char *)"Failed to initialize encoded chunk!" 
  },
  /* 74 */
  { 
  (char *)"/m/75", 
  (char *)"Failed to finish encoded chunk!" 
  },
  /* 75 */
  { 
  (char *)"/m/76", 
  (char *)"Error while writing encoded data!" 
  },
  /* 76 */
  { 
  (char *)"/m/77", 
  (char *)"Failed to create JPEG decompressor!" 
  },
  /* 77 */
  { 
  (char *)"/m/78", 
  (char *)"Failed to assign file to JPEG decompressor!" 
  },
  /* 78 */
  { 
  (char *)"/m/79", 
  (char *)"Failed to read JPEG header from file!" 
  },
  /* 79 */
  { 
  (char *)"/m/80", 
  (char *)"Failed to start JPEG decompression!" 
  },
  /* 80 */
  { 
  (char *)"/m/81", 
  (char *)"Error while reading JPEG file!" 
  },
  /* 81 */
  {
  (char *)"/m/82", 
  (char *)"Failed to allocate memory for image! (Not enough RAM/swap space)"
  },
  /* 82 */
  { 
  (char *)"/m/83", 
  (char *)"Failed to create PNG read structure!" 
  },
  /* 83 */
  { 
  (char *)"/m/84", 
  (char *)"Failed to create PNG info structure!"
  },
  /* 84 */
  { 
  (char *)"/m/85", 
  (char *)"Missing bounding box for \"" 
  },
  /* 85 */
  { 
  (char *)"/m/86", 
  (char *)"Driver does not support special text!" 
  },
  /* 86 */
  { 
  (char *)"/m/87", 
  (char *)"Unknown key in special comment \"" 
  },
  /* 87 */
  { 
  (char *)"/m/88", 
  (char *)"Special comment unused by this driver \""
  },
  /* 88 */
  { 
  (char *)"/m/89", 
  (char *)"Option not configurable by special comments \"" 
  },
  /* 89 */
  { 
  (char *)"/m/90", 
  (char *)"Error while processing special comment \"" 
  },
  /* 90 */
  { 
  (char *)"/m/91", 
  (char *)"Unused command line option \""
  },
  /* 91 */
  { 
  (char *)"/m/92", 
  (char *)"Unused configuration file option \"" 
  },
  /* 92 */
  { 
  (char *)"/m/93", 
  (char *)"\"!"
  },
  /* 93 */
  { 
  (char *)"/m/94", 
  (char *)"Redefinition of \""
  },
  /* 94 */
  { 
  (char *)"/m/95", 
  (char *)"JS libraries must be specfied at document level!" 
  },
  /* 95 */
  { 
  (char *)"/m/96", 
  (char *)"Failed to obtain image dimensions for \""
  },
  /* 96 */
  { 
  (char *)"/m/97", 
  (char *)"Error while reading PNG file \"" 
  },
  /* 97 */
  { 
  (char *)"/m/98", 
  (char *)"\"!"
  },
  /* 98 */
  { 
  (char *)"/m/99", 
  (char *)"Gathering font, pattern and image information." 
  },
  /* 99 */
  { 
  (char *)"/m/100", 
  (char *)"Starting PDF output."
  },
  /* 100 */
  { 
  (char *)"/m/101", 
  (char *)"Creating graphics stream." 
  },
  /* 101 */
  { 
  (char *)"/m/102", 
  (char *)"Writing font objects." 
  },
  /* 102 */
  { 
  (char *)"/m/103", 
  (char *)"Writing pattern objects." 
  },
  /* 103 */
  { 
  (char *)"/m/104", 
  (char *)"Writing image objects." 
  },
  /* 104 */
  { 
  (char *)"/m/105", 
  (char *)"Unknown object type in FIG file!"
  },
  /* 105 */
  { 
  (char *)"/m/106", 
  (char *)"Too few bytes in file!"
  },
  /* 106 */
  { 
  (char *)"/m/107", 
  (char *)"Failed to write pattern object!"
  },
  /* 107 */
  { 
  (char *)"/m/108", 
  (char *)"Failed to write image data for \""
  },
  /* 108 */
  { 
  (char *)"/m/109", 
  (char *)"\"!"
  },
  /* 109 */
  { 
  (char *)"/m/110", 
  (char *)"Font number out of range!"
  },
  /* 110 */
  { 
  (char *)"/m/111", 
  (char *)"This driver can only handle left-aligned text!"
  },
  /* 111 */
  { 
  (char *)"/m/112", 
  (char *)"File type of included image is not supported!" 
  },
  /* 112 */
  { 
  (char *)"/m/113", 
  (char *)"Bitmap-to-PDF conversion failed for \"" 
  },
  /* 113 */
  { 
  (char *)"/m/114", 
  (char *)"Conversion failed, errors occured!" 
  },
  /* 114 */
  { 
  (char *)"/m/115", 
  (char *)"Only PS allows the setpagedevice operator, EPS doesn't!"
  },
  /* 115 */
  { 
  (char *)"/m/116", 
  (char *)"Only PS allows the showpage operator, EPS doesn't!"
  },
  /* 116 */
  {
    (char *)"/m/117",
    (char *)"Failed to find shared directory name!"
  },
  /* 117 */
  {
    (char *)"/m/118",
    (char *)"Shared directory name too long!"
  },
  /* 118 */
  {
    (char *)"/m/119",
    (char *)"Failed to create converter from UTF-8 to LaTeX!"
  }
  ,
  /* 119 */
  {
    (char *)"/m/120",
    (char *)"Character out of range (must be 0x00 ... 0xFF)!"
  },
  /* 120 */
  {
    (char *)"/m/121",
    (char *)"Suggestion: Use combination of this driver and LaTeX driver."
  },
  /* 121 */
  {
    (char *)"/m/122",
    (char *)"Gathering infomation about fonts, images and fill patterns."
  },
  /* 122 */
  {
    (char *)"/m/123",
    (char *)"<standard output>"
  },
  /* 123 */
  {
    (char *)"/m/124",
    (char *)"Failed to load LaTeX encoding table for "
  },
  /* 124 */
  {
    (char *)"/m/125",
    (char *)"!"
  },
  /* 125 */
  {
    (char *)"/m/126",
    (char *)"Failed to find LaTeX encoding for "
  },
  /* 126 */
  {
    (char *)"/m/127",
    (char *)"!"
  },
  /* 127 */
  {
    (char *)"/m/128",
    (char *)"Writing to output file \""
  },
  /* 128 */
  {
    (char *)"/m/129",
    (char *)"\"."
  },
  /* 129 */
  {
    (char *)"/m/130",
    (char *)"The figure references images containing alpha channels."
"\nYou should use\n"
"\\ifpdf\\pdfpageattr{/Group <</S /Transparency /I true /CS /DeviceRGB>>}\\fi"
"\n in the document preample of LaTeX files including this figure."
  },
  /* 130 */
  {
    (char *)"/m/131",
    (char *)"The file \""
  },
  /* 131 */
  {
    (char *)"/m/132",
    (char *)"\" exists.\n  Probably this file hides $HOME/.defauls/fig2vect.cfg, just written."
  },
  /* 132 */
  {
    (char *)"/m/133",
    (char *)"Suppress notices about alpha channels transferred to PDF."
  },
  /* 133 */
  {
    (char *)"/m/134",
    (char *)"Unable to place graphics on paper!"
  },
  /* 134 */
  {
    (char *)"/m/135",
    (char *)"Failed to load font configuration file \""
  },
  /* 135 */
  {
    (char *)"/m/136",
    (char *)"\"!"
  },
  /* 136 */
  {
    (char *)"/m/137",
    (char *)"Resource needed by jar file: \""
  },
  /* 137 */
  {
    (char *)"/m/138",
    (char *)"\""
  },
  /* 138 */
  {
    (char *)"/m/139",
    (char *)"Please include the files listed above into the *.jar archive."
  },
  /* 139 */
  {
    (char *)"/m/140",
    (char *)"No special text processing in Java output driver!"
  },
  /* 140 */
  {  
    (char *)"/m/141",
    (char *)"Fill pattern ignored by Java output driver!"
  },
  /* 141 */
  {
    (char *)"/m/142",
    (char *)"No fill for polylines or open splines!"
  },

};

/** Number of entries in the kv table. */
static size_t szkv = sizeof(kv)/sizeof(dk_key_value_t);



/** Suffixes for XFig files we are willing to read.
*/
static char *figsuffixes[] = {
  ".fig", ".fig.gz", ".fig.bz2",
  NULL
};



/** Compression suffixes we can possibly handle.
*/
static char *suffixes[] = {
  ".gz", ".bz2", ".svgz", NULL
};



/** Driver base names.
*/
static char *base_driver_names[] = {
  /*   0 */	"mp",
  /*   1 */	"mmp",
  /*   2 */	"eps",
  /*   3 */	"svg",
  /*   4 */	"tex",
  /*   5 */	"bb",
  /*   6 */	"pdf",
  /*   7 */	"ps",
  /*   8 */	"java",
  NULL
};



/** File name standard input.
*/
static char string_stdin[]  = { "<<<stdin>>>" };

/** File name for standard output.
*/
static char string_stdout[] = { "<<<stdout>>>" };



/** Dot (string constant used to find the file name suffix).
*/
static char string_dot[] = { "." };



/** Maximum input line length.
*/
static size_t max_line_length =
#ifdef DKFIG_MAX_LINE_LENGTH
  (size_t)(DKFIG_MAX_LINE_LENGTH)
#else
  (size_t)0
#endif
;



/** Builtin default options (1).
*/
static unsigned long builtin_default_options1 = 
DKFIG_OPT_METAPOST_ARROWHEADS
| DKFIG_OPT_FILL_PATTERNS
| DKFIG_OPT_FILL_CONTIGOUS
| DKFIG_OPT_ALLOW_PSRL
| DKFIG_OPT_REMOVE_BITMAP_BORDER
| DKFIG_OPT_KEEP_BITMAP_WH_RATIO
| DKFIG_OPT_USE_CSS
| DKFIG_OPT_IGNORE_UNKNOWN_PAPER
| DKFIG_OPT_DP_DOT_LW
;

/** Builtin default options(2).
*/
static unsigned long builtin_default_options2 =
DKFIG_OPT_FLIP_DIAGONAL
| DKFIG_OPT_INTERPOLATE_IMAGES
| DKFIG_OPT_SVG_FONTSIZE_UNIT
| DKFIG_OPT_COLOR
| DKFIG_OPT_JAVA_UNICODE
;



/** File name format for temporary files: *.tex
*/
static char str_conv_format[] = { "f2v_%lu.tex" };

/** File name format for temporary files: *.dvi
*/
static char str_conv_dvi[]  = { "f2v_%lu.dvi" };

/** File name format for temporary files: *.ps
*/
static char str_conv_ps[]  = { "f2v_%lu.ps" };

/** File name format for temporary files: *.log
*/
static char str_conv_log[]  = { "f2v_%lu.log" };

/** File name format for temporary files: *.aux
*/
static char str_conv_aux[]  = { "f2v_%lu.aux" };



/** Name of string table to load for application.
*/
static char string_table_name[] = { "fig2vect" };

/** Mode string to open files for binary writing.
*/
static char default_write_mode[] = { "wb" };

/** Mode string to open files for writing.
*/
static char text_write_mode[] = { "w" };



/**	Cleanup options storage.
	Release memory for options, storage and iterator of a
	conversion structure. This function is invoked from
	dkfig_co_cleanup().
	@param	s	Storage.
	@param	i	Storage iterator.
*/
static void
cleanup_options_iterator DK_P2(dk_storage_t *,s,dk_storage_iterator_t *,i)
{
  dk_fig_opt *o;
  
  if((s) && (i)) {
    dksto_it_reset(i);
    while((o = (dk_fig_opt *)dksto_it_next(i)) != NULL) {
      dkfig_opt_delete(o);
    }
    dksto_it_close(i);
  }
  if(s) {
    dksto_close(s);
  }
  
}



/**	Clean up conversion job structure before
	releasing the memory. This function is invoked
	from dkfig_co_delete() or dkfig_co_new().
	@param	c	Conversion job structure.
*/
void
dkfig_co_cleanup DK_P1(dk_fig_conversion *,c)
{
  char *cp;
  
  if(c) {
    c->app = NULL;
    if(c->uc2lat) {
      dkle_close(c->uc2lat); c->uc2lat = NULL;
    }
    if(c->uc2ldir) {
      cp = c->uc2ldir; dk_delete(cp);
      c->uc2ldir = NULL;
    }
    cleanup_options_iterator(c->opt, c->opti);
    c->opt = NULL; c->opti = NULL;
    cleanup_options_iterator(c->optg, c->optgi);
    c->optg = NULL; c->optgi = NULL;
    cleanup_options_iterator(c->optb, c->optbi);
    c->optb = NULL; c->optbi = NULL;
    cleanup_options_iterator(c->optd, c->optdi);
    c->optd = NULL; c->optdi = NULL;
    c->optn = c->opt1 = c->opt2 = 0UL;
    c->image_align = 4 * DKFIG_ALIGN_V_CENTERED + DKFIG_ALIGN_H_CENTERED;
    if(c->tpfn) {	
      cp = c->tpfn; dk_delete(cp);
    }
    c->tpfn = NULL;
    if(c->texn) {
      cp = c->texn; dk_delete(cp);
    }
    c->texn = NULL;
    if(c->dvin) {
      cp = c->dvin; dk_delete(cp);
    }
    c->dvin = NULL;
    if(c->psn) {
      cp = c->psn; dk_delete(cp);
    }
    c->psn = NULL;
    if(c->logn) {
      cp = c->logn; dk_delete(cp);
    }
    c->logn = NULL;
    if(c->auxn) {
      cp = c->auxn; dk_delete(cp);
    }
    c->auxn = NULL;
    if(c->fcfg) {	
      cp = c->fcfg; dk_delete(cp);
    } c->fcfg = NULL;
    if(c->cfgfn) {
      
      cp = c->cfgfn; dk_delete(cp);
    } c->cfgfn = NULL;
    if(c->drn) {
      
      cp = c->drn; dk_delete(cp);
    }
    c->drn = NULL;
    if(c->defdr) {
      
      cp = c->defdr; dk_delete(cp);
    }
    c->defdr = NULL;
    if(c->basdr) {
      cp = c->basdr; dk_delete(cp);
    }
    c->basdr = NULL;
    c->ifn1 = c->ofn1 = c->ifn2 = c->ofn2 = NULL;
    c->normal_text = c->special_text = 0;
    if(c->msg1) { char **xp; xp = c->msg1; dk_delete(xp); }
    c->msg1 = NULL;
    c->istrm = NULL; c->ostrm = NULL;
    c->bdnum = 0;
    c->svgv = 0;
    c->svgs = 0;
  }
  
}



/**	Cleanup up and release conversion job structure.
	@param	c	Conversion job structure.
*/
void
dkfig_co_delete DK_P1(dk_fig_conversion *,c)
{
  
  if(c) {
    dkfig_co_cleanup(c);
    dk_delete(c);
  }
  
}



/**	Initialize options storage.
	This function is invoked during the initialization
	of a conversion job structure.
	@param	c	Conversion job structure.
	@param	what	Index, which storage to initialize.
*/
static
void
initialize_options_storage DK_P2(dk_fig_conversion *,c,int,what)
{
  dk_storage_t *mys = NULL;
  dk_storage_iterator_t *myi = NULL;
  
  if((what >= 0) && (what <= 3)) {
    mys = dksto_open(3);
    if(mys) {
      dksto_set_comp(mys, dkfig_opt_compare, 0);
      myi = dksto_it_open(mys);
      if(!(myi)) {
        if(c->app) {
	  dkapp_err_memory(c->app, sizeof(dk_storage_iterator_t), 1);  
	}
      }
    } else {
      if(c->app) {
        dkapp_err_memory(c->app, sizeof(dk_storage_t), 1);
      }
    }
    switch(what) {
      case 0: c->opt  = mys; c->opti  = myi; break;
      case 1: c->optg = mys; c->optgi = myi; break;
      case 2: c->optb = mys; c->optbi = myi; break;
      case 3: c->optd = mys; c->optdi = myi; break;
    }
  }
  
}



/**	Find names for temporary files.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
set_tex_filenames DK_P1(dk_fig_conversion *,c)
{
/*
  As TeX produces *.dvi files in the current directory
  (there is no option to change this) we keep both
  *.tex and *.dvi in the current directory. The *.ps
  file is created here too.
*/
  int back = 0;
  unsigned long mypid = 0UL;
  char buffer[64];
  
  mypid = (unsigned long)dksf_getpid();
#if DK_HAVE_SNPRINTF
   snprintf(buffer, sizeof(buffer), str_conv_format, mypid);
   buffer[sizeof(buffer)-1] = '\0';
#else
  sprintf(buffer, str_conv_format,  mypid);
#endif
  c->texn = dkstr_dup(buffer);
#if DK_HAVE_SNPRINTF
  snprintf(buffer, sizeof(buffer), str_conv_dvi, mypid);
  buffer[sizeof(buffer)-1] = '\0';
#else
  sprintf(buffer, str_conv_dvi, mypid);
#endif
  c->dvin = dkstr_dup(buffer);
#if DK_HAVE_SNPRINTF
  snprintf(buffer, sizeof(buffer), str_conv_ps, mypid);
  buffer[sizeof(buffer)-1] = '\0';
#else
  sprintf(buffer, str_conv_ps, mypid);
#endif
  c->psn  = dkstr_dup(buffer);
#if DK_HAVE_SNPRINTF
  snprintf(buffer, sizeof(buffer), str_conv_log, mypid);
  buffer[sizeof(buffer)-1] = '\0';
#else
  sprintf(buffer, str_conv_log, mypid);
#endif
  c->logn = dkstr_dup(buffer);
#if DK_HAVE_SNPRINTF
  snprintf(buffer, sizeof(buffer), str_conv_aux, mypid);
  buffer[sizeof(buffer)-1] = '\0';
#else
  sprintf(buffer, str_conv_aux, mypid);
#endif
  c->auxn = dkstr_dup(buffer);
  if((c->texn) && (c->dvin) && (c->psn) && (c->logn) && (c->auxn)) {
    back = 1;
  }
  
  
  
  
  
  
  return back;
}



/**	Initialize newly created conversion job structure,
	use default values and preferences obtained from \arg a.
	@param	c	New conversion job structure.
	@param	a	Application structure.
	@return	1 on success, 0 on error.
*/
int
dkfig_co_init DK_P2(dk_fig_conversion *,c,dk_app_t *,a)
{
  int back = 0;
  char buffer[32], *p1;
  
  if(c) {
    c->app = a;
    c->opt = NULL;  c->opti = NULL;
    c->optg = NULL; c->optgi = NULL;
    c->optb = NULL; c->optbi = NULL;
    c->optd = NULL; c->optdi = NULL;
    c->optn = 0UL;
    c->opt1 = builtin_default_options1;
    c->opt2 = builtin_default_options2;
    c->uc2lat = NULL;
    c->uc2ldir = NULL;
    c->af = 0x00;
    c->paper_width = 595;
    c->paper_height = 842;
    c->paper_xleft = 57;
    c->paper_xright = 538;
    c->paper_ytop = 814;
    c->paper_ybottom = 29;
    c->align_h = DKFIG_ALIGN_H_CENTERED;
    c->align_v = DKFIG_ALIGN_V_CENTERED;
    c->image_align = 4 * DKFIG_ALIGN_V_CENTERED + DKFIG_ALIGN_H_CENTERED;
    
    if(c->app) {
      long mpl;
      mpl = dksf_get_maxpathlen();
      c->uc2ldir = dk_new(char,(size_t)mpl);
      if(c->uc2ldir) {		
        (c->uc2ldir)[0] = '\0';
        if(dkapp_get_pref(c->app, pk_shdir, c->uc2ldir, (size_t)mpl, 0)) {
	  
	  if((long)(strlen(c->uc2ldir) + strlen(str_uc2l)) < mpl) {
	    
	    strcat(c->uc2ldir, str_uc2l);	
	    c->uc2lat = dkle_open(c->uc2ldir);
	    if(!(c->uc2lat)) {
	      dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 118);
	    }
	  } else {		
	    /* ERROR: Shared directory name too large */
	    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 117);
	  }
	} else {		
	  /* ERROR: Failed to find shared directory */
	  dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 116);
	}
      } else {			
        /* ERROR: Memory */
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 12);
      }
    }
    c->nodcoord = 2; c->nodtrigo = 3; c->nodcolor = 3;
    c->circlesteps = 4;
    c->psl  = 0; c->dscl = 0;
    c->istrm = NULL; c->ostrm = NULL;
    c->latfs = -1;
    c->drn = c->defdr = c->basdr = NULL;
    c->ifn1 = c->ofn1 = NULL;
    c->ifn2 = c->ofn2 = NULL;
    c->normal_text = c->special_text = 0;
    c->fsf  = 1.0;
    c->msg1 = NULL;
    c->cfgfn = NULL;
    c->bdnum = -1;
    c->spseg = 8;
    c->texn  = NULL;
    c->dvin  = NULL;
    c->psn   = NULL;
    c->logn  = NULL;
    c->auxn  = NULL;
    c->tpfn  = NULL;
    c->incg  = NULL;
    c->fcfg  = NULL;
    c->patlw = 1L;
    c->patrp = 8L;
    c->ahlj  = 0;
    c->minitsteps = 8UL;
    c->maxitsteps = 2048UL;
    c->svgv  = DKFIG_SVG_VERS_11;	/* SVG 1.1 by default */
    initialize_options_storage(c,0);	/* opt  (command line) */
    initialize_options_storage(c,1);	/* optg (global options) */
    initialize_options_storage(c,2);	/* optb (base options) */
    initialize_options_storage(c,3);	/* optd (driver options) */
    c->msg1 = dkapp_find_key_value(a, kv, szkv, string_table_name);
    if((c->opt) && (c->opti) && (c->msg1) && (c->optg) && (c->optgi)
       && (c->optb) && (c->optbi) && (c->optd) && (c->optdi)
       && (c->uc2ldir) && (c->uc2lat))
    {
      back = set_tex_filenames(c);
      if(back) {
        if(c->app) {
          c->defdr = dkapp_get_str_pref(c->app, pk_language);
	  switch(dkapp_get_bool_pref(c->app, pk_make)) {
	    case 0: {
	      c->opt1 &= (~DKFIG_OPT_MAKE);
	    } break;
	    case 1: {
	      c->opt1 |= DKFIG_OPT_MAKE;
	    } break;
	  }
	  if(dkapp_get_pref(c->app, pk_utf8, buffer,sizeof(buffer), 0)) {
	    (void)dkfig_tool2_set_utf8(c, buffer, 1);
	  } else {
	    /* As jFig and XFig 3.2.5 do not write UTF-8 encoded text,
	     * we do no longer inspect the LANG environment variable
	     * automatically. The LANG test is done only on a users
	     * request, i.e. if the user still uses XFig 3.2.4.
	     */
	    (void)dkfig_tool2_utf8_auto(c);
	  }
	  if(dkapp_get_pref(c->app, pk_alph, buffer, sizeof(buffer), 0)) {
	    p1 = dkstr_start(buffer, NULL);
	    if(p1) {
	      dkstr_chomp(p1, NULL);
	      if(dkstr_is_bool(p1)) {
	        if(dkstr_is_on(p1)) {
		  c->opt2 |= DKFIG_OPT_SUPPRESS_ALPHA_INFO;
		}
	      }
	    }
	  }
        }
      }
    }
  }
  
  return back;
}



/**	Allocate new dk_fig_conversion structure and initialize it.
	@param	a	Application structure.
	@return	Pointer to new conversion job structure on success,
	NULL on error.
*/
dk_fig_conversion *
dkfig_co_new DK_P1(dk_app_t *,a)
{
  dk_fig_conversion *back = NULL;
  
  back = dk_new(dk_fig_conversion,1);
  if(back) {
    if(!dkfig_co_init(back, a)) {
      dkfig_co_delete(back); back = NULL;
    }
  }
  if(!back) {
    if(a) {
      dkapp_err_memory(a, sizeof(dk_fig_conversion), 1);
    }
  }
  
  return back;
}



/**	Set/reset one bit in an unsigned long depending
	on the string \arg p retrieved from the preference
	system.
	@param	a	Old option value.
	@param	p	String to inspcect.
	@param	f	Flag bit value to set or reset.
	@param	d	Default value.
	@return	New flag set.
*/
static
unsigned long
check_bool_opts DK_P4(unsigned long,a,char *,p,unsigned long,f,int,d)
{
  unsigned long back;
  int what;
  
  back = a;
  what = d;
  if(dkstr_is_bool(p)) {
    what = dkstr_is_on(p);
  }
  if(what) {
    back |= f;
  } else {
    back &= (~f);
  }
  
  return back;
}



/**	Get base driver number.
	@param	n	Driver name.
	@return	The base driver number (or -1 on error).
*/
static
int
get_base_driver DK_P1(char *,n)
{
  int back = -1;
  
  if(n) {
    back = dkstr_array_index(base_driver_names, n, 1);
  }
  
  return back;
}



/**	Add an option.
	@param	c	Conversion job structure.
	@param	on	Option number.
	@param	p	Option text (key=value).
	@param	bck	Pointer to variable for error code.
*/
static void
add_opt DK_P4(dk_fig_conversion *,c,unsigned long,on,char *,p,int *,bck)
{
  dk_fig_opt *opt;
  
  if(c->opt) {
    opt = dkfig_opt_new(on,p);
    if(opt) {
      if(!dksto_add(c->opt, (void *)opt)) {
        dkfig_opt_delete(opt);
        *bck = ((*bck) | DKFIG_WHAT_ERROR);
	if(c->app) {
	  dkapp_err_memory(c->app, sizeof(dk_storage_node_t), 1);
	}
      }
    } else {
      *bck = ((*bck) | DKFIG_WHAT_ERROR);
      if(c->app) {
        dkapp_err_memory(c->app, sizeof(dk_fig_opt), 1);
      }
    }
  } else {
    *bck = ((*bck) | DKFIG_WHAT_ERROR);
  }
  
}



/**	Set driver name.
	@param	c	Conversion job structure.
	@param	drn	Driver name.
	@param	bck	Pointer to variable for error code.
*/
static void
set_driver DK_P3(dk_fig_conversion *,c,char *,drn,int *,bck)
{
  
  if(c->drn) {
    *bck = ((*bck) | DKFIG_WHAT_ERROR);
  } else {
    c->drn = dkstr_dup(drn);
    if(!(c->drn)) {
      *bck = ((*bck) | DKFIG_WHAT_ERROR);
      if(c->app) {
        dkapp_err_memory(c->app, 1, (1+strlen(drn)));
      }
    }
  }
  
}



/**	Apply command line arguments to conversion job.
	@param	c	Conversion job structure.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	1 on success, 0 on error.
*/
int
dkfig_co_apply_args DK_P3(dk_fig_conversion *,c,int,argc,char **,argv)
{
  int back = 0;
  int i, found; char *ptr, *xptr, **lfdptr;
  unsigned long onum;
  
  if(c) {
  if(argc > 0) {
  if(argv) {	
    lfdptr = argv; lfdptr++; i=1;
    found = 0; onum = 0UL;
    while(i < argc) {
      ptr = *lfdptr;
      if(*ptr == '-') {		
        ptr++;
	switch(*ptr) {
	  case '-' : {
	    
	    ptr++;
	    xptr = strchr(ptr, '=');
	    if(xptr) { *(xptr++) = '\0'; }
	    found = dkstr_array_abbr(longopt_keywords, ptr, (char)'$', 1);
	    switch(found) {
	      case 0: {		
	        back |= DKFIG_WHAT_VERSION;
	      } break;
	      case 1: {		
	        back |= DKFIG_WHAT_HELP;
	      } break;
	      case 2: {		
	        back |= DKFIG_WHAT_CONFIGURE;
	      } break;
	      case 3: {		
	        back |= DKFIG_WHAT_UNCONFIGURE;
	      } break;
	      case 4: {		
	        back |= DKFIG_WHAT_SHOWCONF;
	      } break;
	      case 5: {		
	        if(xptr) {
		  c->opt1 = check_bool_opts(c->opt1, xptr, DKFIG_OPT_MAKE, 1);
		} else {
		  c->opt1 = c->opt1 | DKFIG_OPT_MAKE;
		}
	      } break;
	      case 6: {		
	        if(xptr) {
		  add_opt(c, onum++, xptr, &back);
		} else {
		  back = DKFIG_WHAT_ERROR;
		}
	      } break;
	      case 7: {		
	        if(xptr) {
		  set_driver(c, xptr, &back);
		} else {
		  back = DKFIG_WHAT_ERROR;
		}
	      } break;
	      case 8: {		
	        c->opt1 = (c->opt1) | DKFIG_OPT_RESET | DKFIG_OPT_MAKE;
	        if(c->drn) {
	          char *mycptr;
	          mycptr = c->drn;
	          dk_delete(mycptr);
	          c->drn = NULL;
	        }
	      } break;
	      case 9: {} break;
	      case 10: {
	        back |= DKFIG_WHAT_CONFIG_FILE;
	      } break;
	      case 11: {
	        back |= DKFIG_WHAT_WRITE_CONFIG_FILE;
	      } break;
	      default: {
	        back |= DKFIG_WHAT_ERROR;
	      } break;
	    }
	  } break;
	  case 'C' : {
	    
	    back |= DKFIG_WHAT_SHOWCONF;
	  } break;
	  case 'c' : {
	    
	    back |= DKFIG_WHAT_CONFIGURE;
	  } break;
	  case 'u' : {
	    
	    back |= DKFIG_WHAT_UNCONFIGURE;
	  } break;
	  case 'h' : {
	    
	    back |= DKFIG_WHAT_HELP;
	  } break;
	  case 'v' : {
	    
	    back |= DKFIG_WHAT_VERSION;
	  } break;
	  case 'a' : {
	    c->af = 0x01;
	  } break;
	  case 'm' : {
	    
	    ptr++;
	    if(*ptr) {
	      c->opt1 = check_bool_opts(c->opt1, ptr, DKFIG_OPT_MAKE, 1);
	    } else {
	      c->opt1 |= DKFIG_OPT_MAKE;
	    }
	    
	  } break;
	  case 'r' : {
	    c->opt1 = (c->opt1) | DKFIG_OPT_RESET | DKFIG_OPT_MAKE;
	    if(c->drn) {
	      char *mycptr;
	      mycptr = c->drn;
	      dk_delete(mycptr);
	      c->drn = NULL;
	    }
	  } break;
	  case 'l' : {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL;
	      lfdptr++; i++;
	      if(i < argc) {
	        ptr = *lfdptr;
	      }
	    }
	    if(ptr) {
	      set_driver(c, ptr, &back);
	    } else {
	      back |= DKFIG_WHAT_ERROR;
	    }
	  } break;
	  case 'o' : {
	    ptr++;
	    if(!(*ptr)) {
	      ptr = NULL;
	      lfdptr++; i++;
	      if(i < argc) {
	        ptr = *lfdptr;
	      }
	    }
	    if(ptr) {
	      add_opt(c, onum++, ptr, &back);
	    } else {
	      back |= DKFIG_WHAT_ERROR;
	    }
	  } break;
	  case 's' : { } break;
	  case 'i': {
	    ptr++; 
	    if(!(*ptr)) {
	      ptr = NULL;
	      lfdptr++; i++;
	      if(i < argc) {
	        ptr = *lfdptr;
	      }
	    }
	    if(ptr) {
	      c->incg = ptr;
	      
	    } else {
	      back |= DKFIG_WHAT_ERROR;
	    }
	  } break;
	  case 'A': {
	    ptr++;
	    if(*ptr == '-') {
	      c->opt2 &= (~(DKFIG_OPT_SUPPRESS_ALPHA_INFO));
	    } else {
	      c->opt2 |= DKFIG_OPT_SUPPRESS_ALPHA_INFO;
	    }
	  } break;
	  default: {
	    back |= DKFIG_WHAT_ERROR;
	  } break;
	}
      } else {			
        if(c->ifn1) {
	  if(c->ofn1) {
	    back |= DKFIG_WHAT_ERROR;
	  } else {
	    c->ofn1 = ptr;
	  }
	} else {
	  c->ifn1 = ptr;
	}
      }
      lfdptr++; i++;
    }
  }
  }
  }
  
  return back;
}



/**	Show current configuration.
	@param	c	Configuration job structure.
*/
void
dkfig_co_showconf_app DK_P1(dk_fig_conversion *,c)
{
  char *valptr1, *valptr2, *valptr3;
  size_t sz1, sz2, sz3, szx, i;
  
  if(c->msg1) {
    dkapp_stdout(c->app, (c->msg1)[5]);
    fputc('\n', stdout);
    valptr1 = (c->opt1 & DKFIG_OPT_MAKE) ?
              ((c->msg1)[0]) : ((c->msg1)[1]);
    valptr2 = (c->msg1)[4];
    valptr3 = (c->opt2 & DKFIG_OPT_SUPPRESS_ALPHA_INFO) ?
              ((c->msg1)[0]) : ((c->msg1)[1]);
    if(c->drn) {
      valptr2 = c->drn;
    } else {
      if(!(c->opt1 & DKFIG_OPT_RESET)) {
        if(c->defdr) { valptr2 = c->defdr; }
      }
    }
    sz1 = dkapp_prlen(c->app, valptr1);
    sz2 = dkapp_prlen(c->app, valptr2);
    sz3 = dkapp_prlen(c->app, valptr3);
    if(sz1 > sz2) szx = sz1;
    else          szx = sz2;
    if(szx < sz3) szx = sz3;
    dkapp_stdout(c->app, "-l   ");
    dkapp_stdout(c->app, valptr2);
    for(i = sz2; i < szx; i++) fputc(' ', stdout);
    dkapp_stdout(c->app, "   ");
    dkapp_stdout(c->app, (c->msg1)[3]);
    fputc('\n', stdout);
    dkapp_stdout(c->app, "-m   ");
    dkapp_stdout(c->app, valptr1);
    for(i = sz1; i < szx; i++) fputc(' ', stdout);
    dkapp_stdout(c->app, "   ");
    dkapp_stdout(c->app, (c->msg1)[2]);
    fputc('\n', stdout);
    dkapp_stdout(c->app, "-A   ");
    dkapp_stdout(c->app, valptr3);
    for(i = sz1; i < szx; i++) fputc(' ', stdout);
    dkapp_stdout(c->app, "   ");
    dkapp_stdout(c->app, (c->msg1)[132]);
    fputc('\n', stdout);
  }
  
}



/**	Save current configuration.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
int
dkfig_co_configure_app DK_P1(dk_fig_conversion *,c)
{
  int back = 0; char *valptr;
  
  if((c->app) && (c->msg1)) {
    back = 1;
    valptr = ((c->opt1) & DKFIG_OPT_MAKE) ?
             (c->msg1)[0] : (c->msg1)[1];
    if(!dkapp_set_pref(c->app, pk_make, valptr)) {
      back = 0;
    }
    valptr = pv_empty;
    if(c->drn) {
      valptr = c->drn;
    } else {
      if((c->defdr) && (!(c->opt1 & DKFIG_OPT_RESET))) {
        valptr = c->defdr;
      }
    }
    if(!dkapp_set_pref(c->app, pk_language, valptr)) {
      back = 0;
    }
    if(!dkapp_set_pref(
      c->app, pk_alph,
      bool_val[((c->opt2) & DKFIG_OPT_SUPPRESS_ALPHA_INFO) ? 1 : 0]
    ))
    {
      back = 0;
    }
    dkfig_co_showconf_app(c);
  }
  if(!back) {
    /* ERROR: Failed to save new configuration! */
    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 8);
  }
  
  return back;
}




/**	Find base driver name.
	@param	c	Conversion job structure.
*/
static
void
establish_base_driver_name DK_P1(dk_fig_conversion *,c)
{
  char *cp = NULL;
  
  if(!(c->basdr)) {
    if(!(c->drn)) {
      if(c->ofn1) {
        cp = dksf_get_file_type_dot(c->ofn1);
	if(cp) {
	  cp++;
	  if(strlen(cp) > 0) {
	    c->drn = dkstr_dup(cp);
	    
	  }
	} cp = NULL;
      }
    }
    if(c->drn) {
      c->basdr = dkstr_dup(c->drn);
      if(!(c->basdr)) {
        if(c->app) {
	  dkapp_err_memory(c->app, sizeof(char), (1+strlen(c->drn)));
	}
      }
    } else {
      if(c->defdr) {
        c->basdr = dkstr_dup(c->defdr);
	if(!(c->basdr)) {
	  if(c->app) {
	    dkapp_err_memory(c->app, sizeof(char), (1+strlen(c->defdr)));
	  }
	}
      }
    }
  }
  if(c->basdr) {
    cp = dkstr_chr(c->basdr, '.');
    if(cp) {
      *cp = '\0';
    }
  }
  
}



/**	Process one line from configuration file.
	@param	c	Conversion job structure.
	@param	l	Line text.
	@param	ln	Line number.
	@param	st	Storage selector (0=global options, 1=base driver options, 2=driver options).
	@return	1 on success, 0 on error.
*/
static int
add_option_line DK_P4(dk_fig_conversion *,c,char *,l,unsigned long,ln,int,st)
{
  int back = 0;
  dk_storage_t *sto = NULL;
  dk_fig_opt *opt;
  
  switch(st) {
    case 0: sto = c->optg; break;
    case 1: sto = c->optb; break;
    case 2: sto = c->optd; break;
  }
  if(sto) {
    opt = dkfig_opt_new(ln,l);
    if(opt) {
      if(dksto_add(sto, (void *)opt)) {
        back = 1;
      } else {
        dkapp_err_memory(c->app, sizeof(dk_storage_node_t), 1);
      }
    } else {
      if(c->app) {
        dkapp_err_memory(c->app, sizeof(dk_fig_opt), 1);
      }
    }
  }
  
  return back;
}



/**	Read configuration file and load contents to the storages.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static int
dkfig_co_load_configuration DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  int state;
  char *buffer, *cp, *cp2;
  dk_stream_t *cfgstr;
  long l; size_t sz;
  unsigned long lineno;
  
  establish_base_driver_name(c);
  if(c->app) {
    c->cfgfn = dkapp_find_file_dup(c->app, short_config_filename);
  } else {
    c->cfgfn = dkstr_dup(default_config_filename);
  }
  if(c->cfgfn) {
    
    /* DEBUG: Configuration file ... is used */
    dkfig_tool2_msg3(c, DK_LOG_LEVEL_DEBUG, 9, 10, c->cfgfn);
    cp = dksf_get_file_type_dot(c->cfgfn);
    if(cp) {
      state = dkstr_array_index(suffixes, cp, have_fncaseins);
    }
    cfgstr = NULL;
    switch(state) {
      case 0: {
#if DK_HAVE_ZLIB_H
        cfgstr = dkstream_opengz(c->cfgfn, str_r, 0, NULL);
#else
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 6);
#endif
      } break;
      case 1: {
#if DK_HAVE_BZLIB_H
        cfgstr = dkstream_openbz2(c->cfgfn, str_r, 0, NULL);
#else
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 7);
#endif
      } break;
      default: {
        cfgstr = dkstream_openfile(c->cfgfn, str_r, 0, NULL);
      } break;
    }
    if(cfgstr) {
      l = 256L + dksf_get_maxpathlen();
      sz = (size_t)l;
      buffer = dk_new(char,sz);
      if(buffer) {
        back = 1;
        state = -1;
        lineno = 0UL;
        while(dkstream_gets(cfgstr, buffer, sz)) {
	  dkstr_delcomm(buffer, (char)'#');
	  lineno++;
	  cp = dkstr_start(buffer, NULL);
	  if(cp) {
	    dkstr_chomp(cp, NULL);
	    if(*cp == '[') {
	      state = -1;
	      cp2 = dkstr_rchr(cp, ']');
	      if(cp2) {
	        *cp2 = '\0';
	      }
	      cp++;
	      cp = dkstr_start(cp, NULL);
	      if(cp) {
	        dkstr_chomp(cp, NULL);
		if(strcmp(cp, "*") == 0) {
		  state = 0;
		} else {
		  if(c->drn) {
		    if(strcmp(cp, c->drn) == 0) {
		      state = 2;
		    }
		  } else {
		    if(c->defdr) {
		      if(strcmp(cp, c->defdr) == 0) {
		        state = 2;
		      }
		    }
		  }
		  if(state == -1) {
		    if(c->basdr) {
		      if(strcmp(cp, c->basdr) == 0) {
		        state = 1;
		      }
		    }
		  }
		}
	      }
	    } else {
	      switch(state) {
	        case 0: {	/* general option */
		  if(!add_option_line(c,cp,lineno,state)) { back = 0; }
		} break;
		case 1: {	/* base driver option */
		  if(!add_option_line(c,cp,lineno,state)) { back = 0; }
		} break;
		case 2: {	/* driver option */
		  if(!add_option_line(c,cp,lineno,state)) { back = 0; }
		} break;
	      }
	    }
	  }
        }
        dk_delete(buffer); buffer = NULL;
      } else {
        if(c->app) dkapp_err_memory(c->app, sizeof(char), sz);
      }
      dkstream_close(cfgstr); cfgstr = NULL;
    } else {
      if(c->app) dkapp_err_fopenr(c->app, c->cfgfn);
    }
    /* cp = c->cfgfn; dk_delete(c->cfgfn); c->cfgfn = NULL; */
  }
  
  return back;
}



/**	Find largest line size in a storage.
	@param	sto	Storage.
	@param	it	Storage iterator.
	@return	Line size of the longest option.
*/
static
size_t
largest_of DK_P2(dk_storage_t *,sto,dk_storage_iterator_t *,it)
{
  size_t sz = 0, back = 0;
  dk_fig_opt *o;
  
  if(sto && it) {
    dksto_it_reset(it);
    while((o = (dk_fig_opt *)dksto_it_next(it)) != NULL) {
      if(o->name) {
        sz = strlen(o->name);
	if(sz > back) {
	  back = sz;
	}
      }
    }
  }
  
  return back;
}



/**	Find largest line size for all option storages.
	@param	c	Conversion job structure.
*/
static
void
set_lcfge DK_P1(dk_fig_conversion *,c)
{
  size_t back = 0;
  
  c->lcfge = 0;
  back = largest_of(c->opt, c->opti);
  if(back > c->lcfge) c->lcfge = back;
  back = largest_of(c->optg, c->optgi);
  if(back > c->lcfge) c->lcfge = back;
  back = largest_of(c->optb, c->optbi);
  if(back > c->lcfge) c->lcfge = back;
  back = largest_of(c->optd, c->optdi);
  if(back > c->lcfge) c->lcfge = back;
  
}



/**	Add input stream contents to drawing.
	@param	c	Conversion job structure
	@return	1 on success, 0 on error.
*/
int
dkfig_co_add_stream DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  size_t sz = 512;
  char *buffer = NULL, *ptr;
  int cc;
  
  if(c->app) {
    sz = (size_t)dkapp_get_ul_pref(c->app, pk_linelgt);
    if(!sz) { sz = 512; }
    
    if(sz <  128) sz =  128;
    if(max_line_length) {
      if(sz > max_line_length) sz = max_line_length;
    }
  }
  buffer = dk_new(char,sz);
  if(buffer) {			
    back = cc = 1;
    while(cc) {
      if(dkstream_gets(c->istrm, buffer, sz)) {
        
	if(strlen(buffer) == (sz - 1)) {
	  if((c->app) && (c->msg1)) {
	    dkapp_set_source_lineno(
	      c->app,
	      (1 + dkfig_rd_get_errl(c->drwng))
	    );
	    dkfig_tool2_msg1(c, DK_LOG_LEVEL_WARNING, 58);
	  }
	}
        ptr = dkstr_start(buffer, NULL);
	if(ptr) {
	  dkstr_chomp(ptr, NULL);
          if(!dkfig_read_add_line(c->drwng, ptr, c)) {
	    back = cc = 0;
	    if(c->app) {
	      dkapp_set_source_lineno(c->app, dkfig_rd_get_errl(c->drwng));
	    }
	  }
	}
      } else {
        cc = 0;
      }
    }
    if(back) {
      if(!dkfig_read_input_finished(c->drwng,c->normal_text,c->special_text,c)) 
      {
        back = 0;
	if(c->app) {
	  dkapp_set_source_lineno(c->app, dkfig_rd_get_errl(c->drwng));
	}
      }
    } else {
      switch(dkfig_rd_get_errc(c->drwng)) {
        case DKFIG_ERROR_MEMORY: {
	  /* ERROR: Memory allocation failed */
	  dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
	} break;
	case DKFIG_ERROR_SYNTAX: {
	  dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 12);
	} break;
	case DKFIG_ERROR_MATH: {
	  /* ERROR: Math error in calculations */
	  dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 13);
	} break;
      }
    }
    dk_delete(buffer); buffer = NULL;
  } else {
    /* ERROR: Memory */
    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
  }
  
  return back;
}



/**	Run the conversion, write output.
	This is the place to add new drivers.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
dkfig_co_out_to_stream DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  
  switch(c->bdnum) {
    case DKFIG_DRIVER_MP: {
      back = dkfig_output_mp(c);
    } break;
    case DKFIG_DRIVER_MMP: {
      back = dkfig_output_mmp(c);
    } break;
    case DKFIG_DRIVER_EPS: {
      back = dkfig_output_eps(c);
    } break;
    case DKFIG_DRIVER_SVG: {
      back = dkfig_output_svg(c);
    } break;
    case DKFIG_DRIVER_TEX: {
      back = dkfig_output_tex(c);
    } break;
    case DKFIG_DRIVER_BB: {
      back = dkfig_output_bb(c);
    } break;
    case DKFIG_DRIVER_PDF: {
      back = dkfig_output_pdf(c);
    } break;
    case DKFIG_DRIVER_PS: {
      back = dkfig_output_ps(c);
    } break;
    case DKFIG_DRIVER_JAVA: {
      back = dkfig_output_java(c);
    } break;
    default: {
      /* ERROR: Unknown base driver ... */
      if(c->app && c->msg1) {
        char buffer[32];
#if DK_HAVE_SNPRINTF
	snprintf(buffer, sizeof(buffer), "%d", c->bdnum);
	buffer[sizeof(buffer)-1] = '\0';
#else
	sprintf(buffer, "%d", c->bdnum);
#endif
        dkfig_tool2_msg3(c, DK_LOG_LEVEL_ERROR, 14, 15, buffer);
      }
    } break;
  }
  
  return back;
}



/**	Run for opened streams.
	Both input and output stream are opened.
	Wildcards in file names are expanded, the real file names are in inf2
	and ofn2.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
run_for_streams DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  int use_web_palette = 0;
  
  if((c->opt1) & DKFIG_OPT_WEB_PALETTE) {
    use_web_palette = 1;
  }
  c->drwng = dkfig_new(use_web_palette);
  if(c->drwng) {
    if(c->ifn2) {
      dkfig_set_input_filename(c->drwng, c->ifn2);
      if(c->app) {
        dkapp_set_source_filename(c->app, c->ifn2);
      }
    } else {
      if(c->app) {
        dkapp_set_source_filename(c->app, string_stdin);
      }
    }
    if(c->app) { dkapp_set_source_lineno(c->app, 0UL); }
    dkfig_set_spline_segments(c->drwng, c->spseg);
    dkfig_rd_set_opts((dk_fig_drawing *)((c->drwng)->data), c->opt1);
    dkfig_rd_set_ahlj((dk_fig_drawing *)((c->drwng)->data), c->ahlj);
    dkfig_rd_set_it_steps(
      (dk_fig_drawing *)((c->drwng)->data),
      c->minitsteps, c->maxitsteps
    );
    /* PROGRESS: Reading input file ... */
    dkfig_tool2_msg3(
      c, DK_LOG_LEVEL_PROGRESS, 16, 17, ((c->ifn2) ? c->ifn2 : string_stdin)
    );
    if(dkfig_co_add_stream(c)) {
      /* PROGRESS: Input file was read successfully */
      dkfig_tool2_msg1(c, DK_LOG_LEVEL_PROGRESS, 18);
      dkfig_tool_correct_for_plain_tex(c);
      /* PROGRESS: Writing output file ... */
      dkfig_tool2_msg3(
        c, DK_LOG_LEVEL_PROGRESS, 20, 21, ((c->ofn2) ? c->ofn2 : string_stdout)
      );
      back = dkfig_co_out_to_stream(c);
      if(c->app) { dkapp_set_source_lineno(c->app, 0UL); }
      /* PROGRESS: Finished writing output file */
      dkfig_tool2_msg1(c, DK_LOG_LEVEL_PROGRESS, 22);
      if(!back) {
        dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 113);
      }
    } else {	
      /* PROGRESS: Finished reading input file, failure! */
      dkfig_tool2_msg1(c, DK_LOG_LEVEL_PROGRESS, 19);
    }
    if(c->app) { dkapp_set_source_filename(c->app, NULL); }
    dkfig_delete(c->drwng); c->drwng = NULL;
  } else {
    /* ERROR: Memory */
    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
  }
  
  return back;
}



/**	Run as filter (standard input, standard output).
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
run_for_stdio DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  int old_stdout_binary = 0;
  
  c->istrm = dkstream_for_file(stdin);
  if(c->istrm) {
    /* dksf_set_file_binary(stdout); */
    old_stdout_binary = dksf_fdesk_binary(1, 1);
    c->ostrm = dkstream_for_file(stdout);
    if(c->ostrm) {
      c->ifn2 = NULL; c->ofn2 = NULL;
      back = run_for_streams(c);
      dkstream_close(c->ostrm); c->ostrm = NULL;
    } else {
      /* ERROR: No stream */
      dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 24);
    }
    dkstream_close(c->istrm); c->istrm = NULL;
    (void)dksf_fdesk_binary(1, old_stdout_binary);
  } else {
    /* ERROR: No stream */
    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 23);
  }
  
  return back;
}



/**	Find suffix of input file to chech whether the file
	is plain text or compressed.
	@param	n	File name.
	@return	Pointer to suffix or NULL.
*/
static
char *
find_fig_suffix DK_P1(char *,n)
{
  char *back = NULL;
  char **xptr;
  size_t l1, l2, l3;
  int i;
  
  xptr = figsuffixes;
  l1 = strlen(n);
  while((!back) && (*xptr)) {
    l2 = strlen(*xptr);
    if(l1 > l2) {
      l3 = l1 - l2;
      if(have_fncaseins) {
        i = dkstr_casecmp(&(n[l3]), *xptr);
      } else {
        i = strcmp(&(n[l3]), *xptr);
      }
      if(i == 0) {
        back = &(n[l3]);
      }
    }
    xptr++;
  }
  
  return back;
}



/**	Open output stream and continue.
	The output file name is fully known now.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
run_with_ofn2 DK_P1(dk_fig_conversion *,c)
{
  int back = 0; int ft = -1; int reason = 0;
  char *sp; char *sp2;
  char *write_mode;
  
  write_mode= default_write_mode;
  ft = -1;
  sp = dksf_get_file_type_dot(c->ofn2);
  if(sp) {
    ft = dkstr_array_index(suffixes, sp, have_fncaseins);
    if(c->bdnum < 1) {
      if(!(c->basdr)) {
        
        sp2 = NULL;
        if(ft >= 0) {		
	  *sp = '\0';
          sp2 = dksf_get_file_type_dot(c->ofn2);
	  if(sp2) {
	    sp2++;
	    c->basdr = dkstr_dup(sp2);
	    if(!(c->basdr)) {
	      /* ERROR: Memory */
	      dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
	    }
	  }
	  *sp = '.';
	} else {		
	  sp2 = sp; sp2++;
	  c->basdr = dkstr_dup(sp2);
	  if(!(c->basdr)) {
	    /* ERROR: Memory */
	    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
	  }
	}
      }
      if(c->basdr) {
        
        c->bdnum = get_base_driver(c->basdr);
      }
    }
  } else {	
  }
  if(c->basdr) {
    if(c->bdnum < 0) {
      c->bdnum = get_base_driver(c->basdr);
    }
    if(c->bdnum > -1) {
    switch(c->bdnum) {
      case 0: case 1: case 4: {
        write_mode = text_write_mode;
      } break;
    }
    switch(ft) {
      case 0: case 2: {
#if DK_HAVE_ZLIB_H
        if(c->app) {
          c->ostrm = dkapp_stream_opengz(c->app, c->ofn2, write_mode);
        } else {
          c->ostrm = dkstream_opengz(c->ofn2, write_mode, 0, &reason);
        }
        if(c->ostrm) {
          back = run_for_streams(c);
          dkstream_close(c->ostrm); c->ostrm = NULL;
        } else {
          /* ERROR: Failed to write */
	  if(c->app) {
	    dkapp_err_fopenw(c->app, c->ofn2);
	  }
        }
#else
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 6);
#endif
      } break;
      case 1: {
#if DK_HAVE_BZLIB_H
        if(c->app) {
          c->ostrm = dkapp_stream_openbz2(c->app, c->ofn2, write_mode);
        } else {
          c->ostrm = dkstream_openbz2(c->ofn2, write_mode, 0, &reason);
        }
        if(c->ostrm) {
          back = run_for_streams(c);
          dkstream_close(c->ostrm); c->ostrm = NULL;
        } else {
          /* ERROR: Failed to write */
	  if(c->app) { dkapp_err_fopenw(c->app, c->ofn2); }
        }
#else
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 7);
#endif
      } break;
      default: {
        
        if(c->app) {
          c->ostrm = dkapp_stream_openfile(c->app, c->ofn2, write_mode);
        } else {
          c->ostrm = dkstream_openfile(c->ofn2, write_mode, 0, &reason);
        }
        if(c->ostrm) {
          back = run_for_streams(c);
          dkstream_close(c->ostrm); c->ostrm = NULL;
        } else {
          /* ERROR: Failed to write file */
	  if(c->app) { dkapp_err_fopenw(c->app, c->ofn2); }
        }
      } break;
    }
  } else {
    /* ERROR: Unknown base driver */
    dkfig_tool2_msg3(c, DK_LOG_LEVEL_ERROR, 25, 26, c->basdr);
  }
  } else {
    /* No base driver specified */
    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 27);
  }
  
  return back;
}



/**	Run with opened input stream.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
run_with_istream DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  int old_stdout_binary = 0;
  dk_fne_t *fne = NULL;
  char *tc = NULL;
  
  if(c->ofn1) {
    if(dksf_must_expand_filename(c->ofn1)) {
      fne = dkfne_open(c->ofn1, 1, 0);
      if(fne) {
        if(dkfne_next(fne)) {
	  tc = dkstr_dup(dkfne_get_fullname(fne));
	  if(tc) {
	    if(dkfne_next(fne)) {
	      /* ERROR: Too many file names */
	      if(c->app) {
	        dkapp_err_multiple_files(c->app, c->ofn1);
	      }
	    } else {
	      c->ofn2 = tc;
	      back = run_with_ofn2(c);
	      c->ofn2 = NULL;
	    }
	    dk_delete(tc); tc = NULL;
	  } else {
	    /* ERROR: Memory */
	    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
	  }
	} else {
	  /* ERROR: No such file */
	  if(c->app) {
	    dkapp_err_no_such_file(c->app, c->ofn1);
	  }
	}
        dkfne_close(fne); fne = NULL;
      } else {
        /* ERROR: No filename expander */
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
      }
    } else {
      c->ofn2 = c->ofn1;
      back = run_with_ofn2(c);
    }
  } else {
    if(c->basdr) {
      c->bdnum = get_base_driver(c->basdr);
      if(c->bdnum > -1) {
        c->ofn2 = NULL;
        /* dksf_set_file_binary(stdout); */
	old_stdout_binary = dksf_fdesk_binary(1, 1);
        c->ostrm = dkstream_for_file(stdout);
        if(c->ostrm) {
          back = run_for_streams(c);
          dkstream_close(c->ostrm); c->ostrm = NULL;
        } else {
          /* ERROR: No stream */
	  dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 24);
        }
	(void)dksf_fdesk_binary(1, old_stdout_binary);
      } else {
        /* ERROR: Unknown base driver */
	dkfig_tool2_msg3(c, DK_LOG_LEVEL_ERROR, 25, 26, c->basdr);
      }
    } else {
      /* ERROR: No base driver */
      dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 27);
    }
  }
  
  return back;
}



/**	Run with input file name (wildcards are resolved).
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
run_with_ifn2 DK_P1(dk_fig_conversion *,c)
{
  int back = 0; int ft = -1; int reason = 0;
  char *sp;
  
  sp = dksf_get_file_type_dot(c->ifn2);
  if(sp) {
    ft = dkstr_array_index(suffixes, sp, have_fncaseins);
  }
    switch(ft) {
      case 0: {
#if DK_HAVE_ZLIB_H
        if(c->app) {
	  c->istrm = dkapp_stream_opengz(c->app, c->ifn2, str_r);
	} else {
	  c->istrm = dkstream_opengz(c->ifn2, str_r, 0, &reason);
	}
	if(c->istrm) {
	  back = run_with_istream(c);
	  dkstream_close(c->istrm); c->istrm = NULL;
	} else {
	  /* ERROR: Failed to read */
	  if(c->app) { dkapp_err_fopenr(c->app, c->ifn2); }
	}
#else
        /* ERROR: No zlib support */
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 6);
#endif
      } break;
      case 1: {
#if DK_HAVE_BZLIB_H
        if(c->app) {
	  c->istrm = dkapp_stream_openbz2(c->app, c->ifn2, str_r);
	} else {
	  c->istrm = dkstream_openbz2(c->ifn2, str_r, 0, &reason);
	}
	if(c->istrm) {
	  back = run_with_istream(c);
	  dkstream_close(c->istrm); c->istrm = NULL;
	} else {
	  /* ERROR: Failed to read */
	  if(c->app) { dkapp_err_fopenr(c->app, c->ifn2); }
	}
#else
        /* ERROR: No bzip2 support */
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 7);
#endif
      } break;
      default: {
        if(c->app) {
	  c->istrm = dkapp_stream_openfile(c->app, c->ifn2, str_r);
	} else {
	  c->istrm = dkstream_openfile(c->ifn2, str_r, 0, &reason);
	}
	if(c->istrm) {
	  back = run_with_istream(c);
	  dkstream_close(c->istrm); c->istrm = NULL;
	} else {
	  /* ERROR: No stream */
	  if(c->app) { dkapp_err_fopenr(c->app, c->ifn2); }
	}
      } break;
    }
  
  return back;
}



/**	Run with filenames possibly containing wildcards.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
run_for_filenames DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  dk_fne_t *fne = NULL;
  char *tc = NULL;
  
  c->ifn2 = NULL;
  if(c->ifn1) {
    if(dksf_must_expand_filename(c->ifn1)) {
      fne = dkfne_open(c->ifn1, 1, 0);
      if(fne) {
        if(dkfne_next(fne)) {
	  tc = dkstr_dup(dkfne_get_fullname(fne));
	  if(tc) {
	    if(dkfne_next(fne)) {
	      /* ERROR: Too many file names match pattern */
	      if(c->app) {
	        dkapp_err_multiple_files(c->app, c->ifn1);
	      }
	    } else {
	      c->ifn2 = tc;
	      back = run_with_ifn2(c);
	      c->ifn2 = NULL;
	    }
	    dk_delete(tc); tc = NULL;
	  } else {
	    /* ERROR: Memory */
	    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
	  }
	} else {
	  /* ERROR: No matching file name */
	  if(c->app) {
	    dkapp_err_no_such_file(c->app, c->ifn1);
	  }
	}
        dkfne_close(fne); fne = NULL;
      } else {
        /* ERROR: No file name expander */
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
      }
    } else {
      c->ifn2 = c->ifn1;
      back = run_with_ifn2(c);
      c->ifn2 = NULL;
    }
  } else {
    if(c->basdr) {
      c->bdnum = get_base_driver(c->basdr);
      if(c->bdnum > -1) {
        back = run_for_stdio(c);	
      } else {
        /* ERROR: Unknown base driver */
	dkfig_tool2_msg3(c, DK_LOG_LEVEL_ERROR, 25, 26, c->basdr);
      }
    } else {
      /* ERROR: No base driver */
      dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 27);
    }
  }
  c->ifn2 = NULL;
  
  return back;
}



/**	Save conversion job settings.
	@param	cd	Destination structure.
	@param	cs	Source structure.
*/
static
void save_co_settings DK_P2(dk_fig_conversion *,cd, dk_fig_conversion *,cs)
{
  cd->normal_text = cs->normal_text;
  cd->special_text = cs->special_text;
  cd->ahlj = cs->ahlj;
  cd->latfs = cs->latfs;
  cd->svgv = cs->svgv;
  cd->psl = cs->psl;
  cd->dscl = cs->dscl;
  cd->spseg = cs->spseg;
  cd->lcfge = cs->lcfge;
  cd->patlw = cs->patlw;
  cd->patrp = cs->patrp;
  cd->nodcoord = cs->nodcoord;
  cd->nodtrigo = cs->nodtrigo;
  cd->nodcolor = cs->nodcolor;
  cd->circlesteps = cs->circlesteps;
  cd->maxitsteps = cs->maxitsteps;
  cd->minitsteps = cs->minitsteps;
  cd->fsf = cs->fsf;
}



/**	Run for a directory, convert all Fig files found.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
run_for_directory DK_P1(dk_fig_conversion *,c)
{
  int back = 0; int ft = 0; int doit = 0; int dnt = 0;
  dk_dir_t *dir = NULL;
  long l; size_t sz;	/* size for buffers */
  char *iname = NULL;	/* input file name */
  char *oname = NULL;	/* output file name */
  char *nname = NULL;	/* next full name obtained from directory */
  char *sp = NULL;	/* pointer to the file name suffix */
  char *bck_tpfn;
  dk_fig_conversion backupco;
  
  if((c->basdr) && (c->bdnum > -1)) {
  l = dksf_get_maxpathlen(); sz = (size_t)l;
  iname = dk_new(char,sz);
  oname = dk_new(char,sz);
  if(iname && oname) {
    dir = dkdir_open(c->ifn1);
    if(dir) {
      bck_tpfn = NULL;
      while((dnt = dkdir_next(dir))) {
	if(dnt == 1) {
          nname = dkdir_get_fullname(dir);
	  if(nname) {
	    if(!(dksf_is_directory(nname))) {
	    if(strlen(nname) < sz) {
	      strcpy(iname, nname); strcpy(oname, nname);
	      sp = find_fig_suffix(oname);
	      if(sp) {
	        ft = dkstr_array_index(figsuffixes,sp,have_fncaseins);
	        if(ft >= 0) {
	          sp++;
		  *sp = '\0';
		  if(strlen(oname)+strlen(c->basdr) < sz) {
		    strcpy(sp, c->basdr);
		    doit = 1;
		    if((c->opt1) & DKFIG_OPT_MAKE) {
		      /* doit = check_must_run(iname, oname); */
		      doit = dksf_must_rebuild(oname, iname);
		    }
		    if(doit) {
		      c->ifn2 = iname; c->ofn1 = oname;
		      bck_tpfn = NULL;
		      if(c->tpfn) {
		        bck_tpfn = dkstr_dup(c->tpfn);
		        if(!bck_tpfn) {
		          /* ERROR: Memory */
			  dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
		        }
		      }
		      save_co_settings(&backupco, c);
		      back = run_with_ifn2(c);
		      save_co_settings(c, &backupco);
		      if(bck_tpfn) {
		        if(c->tpfn) {
		          char *x; x = c->tpfn;
			  dk_delete(x); c->tpfn = NULL;
		        } c->tpfn = NULL;
		        c->tpfn = bck_tpfn;
		        bck_tpfn = NULL;
		      } bck_tpfn = NULL;
		      c->ifn2 = NULL; c->ofn1 = NULL;
		    } else {	/* not necessary to run */
		      /* PROGRESS: No need to run for ... -> ... */
		      dkfig_tool2_msg3(c, DK_LOG_LEVEL_PROGRESS, 28, 29, iname);
		    }
		  } else {
		    /* ERROR: Output file name too long */
		    if(c->app && c->msg1) {
		      char *msg[5];
		      msg[0] = (c->msg1)[30];
		      msg[1] = oname;
                      msg[2] = string_dot;
		      msg[3] = c->basdr;
		      msg[4] = (c->msg1)[31];
		      dkapp_log_msg(c->app, DK_LOG_LEVEL_ERROR, msg, 5);
		    }
		  }
	        } else {	
	        }
	      }
	    } else {	
	    }
	    }
	  } else {	
	  }
	} else {	
	}
      }
      dkdir_close(dir); dir = NULL;
    } else {
      /* ERROR: Failed to open directory */
      if(c->app) { dkapp_err_traverse_dir(c->app, c->ifn1 ); }
    }
  } else {
    /* ERROR: Memory */
    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
  }
  if(oname) { dk_delete(oname); oname = NULL; }
  if(iname) { dk_delete(iname); iname = NULL; }
  }
  
  return back;
}



/**	Check whether running on files or on a directory,
	run the conversion(s).
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
static
int
lets_go_now DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  char *auto_file_name = NULL, *ptr = NULL;
  size_t lgt;
  
  c->bdnum = -1;
  if(c->ofn1) {		
    back = run_for_filenames(c);
  } else {		
    if(c->ifn1) {	
      if(dksf_must_expand_filename(c->ifn1)) {	
        back = run_for_filenames(c);
      } else {
        if(dksf_is_directory(c->ifn1)) {	
	  if(c->basdr) {
	    c->bdnum = get_base_driver(c->basdr);
	    if(c->bdnum >= 0) {
	      back = run_for_directory(c);
	    } else {
	      /* ERROR: No such base driver */
	      dkfig_tool2_msg3(c, DK_LOG_LEVEL_ERROR, 25, 26, c->basdr);
	    }
	  } else {
	    /* ERROR: No base driver specified */
	    dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 27);
	  }
	} else {				
	  if(c->basdr) { c->bdnum = get_base_driver(c->basdr); }
	  if(c->af) {				
	    if(c->bdnum >= 0) {
	      lgt = 5 + strlen(c->ifn1);
	      auto_file_name = dk_new(char,lgt);
	      if(auto_file_name) {
	        strcpy(auto_file_name, c->ifn1);
	        ptr = dksf_get_file_type_dot(auto_file_name);
	        if(ptr) { *ptr = '\0'; }
		switch(c->bdnum) {
		  case DKFIG_DRIVER_MP: {
		    strcat(auto_file_name, output_suffixes[0]);
		  } break;
		  case DKFIG_DRIVER_MMP: {
		    strcat(auto_file_name, output_suffixes[0]);
		  } break;
		  case DKFIG_DRIVER_EPS: {
		    strcat(auto_file_name, output_suffixes[1]);
		  } break;
		  case DKFIG_DRIVER_SVG: {
		    strcat(auto_file_name, output_suffixes[2]);
		  } break;
		  case DKFIG_DRIVER_TEX: {
		    strcat(auto_file_name, output_suffixes[3]);
		  } break;
		  case DKFIG_DRIVER_BB: {
		    strcat(auto_file_name, output_suffixes[4]);
		  } break;
		  case DKFIG_DRIVER_PDF: {
		    strcat(auto_file_name, output_suffixes[5]);
		  } break;
		  case DKFIG_DRIVER_PS: {
		    strcat(auto_file_name, output_suffixes[6]);
		  } break;
		  case DKFIG_DRIVER_JAVA: {
		    strcat(auto_file_name, output_suffixes[7]);
		  } break;
		}
	        c->ofn1 = auto_file_name;
		dkfig_tool2_msg3(c, DK_LOG_LEVEL_INFO, 127, 128, auto_file_name);
	        back = run_for_filenames(c);
	        c->ofn1 = NULL;
	        dk_delete(auto_file_name);
	        auto_file_name = NULL;
	      } else {
	        /* ERROR: Memory */
		dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 11);
	      }
	    } else {
	      /* ERROR: Internal error, bdnum not yet known */
	    }
	  } else {
	    back = run_for_filenames(c);
	  }
	}
      }
    } else {		
      if(c->basdr) {
        c->bdnum = get_base_driver(c->basdr);
	if(c->bdnum > -1) {
          back = run_for_stdio(c);
	} else {
	  /* ERROR: Unknown base driver */
	  dkfig_tool2_msg3(c, DK_LOG_LEVEL_ERROR, 25, 26, c->basdr);
	}
      } else {
        /* ERROR: No base driver */
	dkfig_tool2_msg1(c, DK_LOG_LEVEL_ERROR, 27);
      }
    }
  }
  
  return back;
}



/**	Load configuration file, process options
	and run conversion.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
int
dkfig_co_run DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  
  if(c) {
    establish_base_driver_name(c);
    if(dkfig_co_load_configuration(c)) {
      set_lcfge(c);
      if(dkfig_opt_process_all(c)) {
        back = lets_go_now(c); /* Hey ho, let's go */
      }
    }
  }
  
  return back;
}



/**	Show the contents of the used configuration file
	on standard output.
	A GUI wrapped around fig2vect may run
	"fig2vect --configuration-file" to see the driver
	configurations and the configuration settings.
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
int
dkfig_co_show_config_file DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  if(c) {
    back = dkfig_opt_show_config_file(c, short_config_filename);
  }
  return back;
}



/**	Save a configuration file read from standard input as
	user-specific configuration file.
	A GUI wrapped around fig2vect may allow the user to
	create driver configurations and passes the configuration
	file contents as standard input to
	"fig2vect --write-configuration-file".
	@param	c	Conversion job structure.
	@return	1 on success, 0 on error.
*/
int
dkfig_co_write_config_file DK_P1(dk_fig_conversion *,c)
{
  int back = 0;
  if(c) {
    back = dkfig_opt_write_config_file(c, short_config_filename);
  }
  return back;
}




