/*
Copyright (c) 2001-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	ksort.c	The ksort program.
*/



#include <stdio.h>

#include <dkapp.h>

#include <dkmem.h>
#include <dkstr.h>
#include <dksto.h>
#include <dksf.h>
#include <dklogc.h>

#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif

#include <dklic.h>

#include "dktools-version.h"




#line 65 "ksort.ctr"




/**	System configuration directory.
*/
static char sysconfdir[] = { DK_SYSCONFDIR };



/**	Program group name.
*/
static char packagename[] = { "dktools" };



/**	Version number.
*/
static char the_version_number[] = { VERSNUMB } ;



/**	License terms.
*/
static char *license_terms[] = {
"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 copyright 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 other 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.",
NULL
};



/**	Print version information.
*/
static
void
print_version DK_P0()
{
  char **ptr;
  
  printf("\n");
  printf(
    "ksort (part of the dktools collection, version %s)\n",
    the_version_number
  );
  printf("Copyright (C) 2002-2010 Dipl.-Ing. D. Krause\n");
  printf("http://dktools.sourceforge.net/\n\n");
  ptr = license_terms;
  while(*ptr) {
    printf("%s\n", *(ptr++));
  }
  printf("\nLibraries used:\n\n");
  ptr = dklic_get();
  while(*ptr) {
    printf("%s\n", *(ptr++));
  }
  printf("\n");
  
}



/**	Default help text, printed if help text file is not found.
*/
static char *help_text[] = {
  "ksort [-b] [-i] [-n | -f] [-r] [-m] [-o <outputfile>] [<inputfiles>]",
  "",
  /* --blanks */
  "-b                   Leading whitespaces are ignored.",
  /* --case-insensitive */
  "-n                   Character-comparisons are case-insensitive.",
  /* --decimal */
  "-d                   Lines are started by long decimal numbers.",
  /* --float */
  "-f                   Lines are started by floating point numbers.",
  /* --reversed */
  "-i                   Sort order is reversed.",
  /* --merge */
  "-m                   Merge multiple occurances of equal lines.",
  "-o <outputfile>      Output is written to the specified file.",
  "<inputfiles>",
  "",
  /* --unconfigure */
  "ksort -u             Resets all preferences.",
  "",
  /* --configure */
  "ksort -c <options>   Installs the given options as default preferences.",
NULL
};



/**	Application
*/
static dk_app_t *app = NULL;



/**	Message issued by the program, filled using the string finder.
*/
static char *ksort_str[17];



/**	String finder data.
*/
static dk_string_finder_t ksort_find[] = {
  { "/m/0", &(ksort_str[0]), "Not enough memory!" },
  { "/m/1", &(ksort_str[1]), "Failed to read from \"" },
  { "/m/2", &(ksort_str[2]), "\"!" },
  { "/m/3", &(ksort_str[3]), "No file matching \"" },
  { "/m/4", &(ksort_str[4]), "\"!" },
  { "/m/5", &(ksort_str[5]), "Failed to correct filename \"" },
  { "/m/6", &(ksort_str[6]), "\"!" },
  { "/m/7", &(ksort_str[7]), "on" },
  { "/m/8", &(ksort_str[8]), "off" },
  { "/m/9", &(ksort_str[9]), "line length" },
  { "/m/10", &(ksort_str[10]), "ignore leading spaces" },
  { "/m/11", &(ksort_str[11]), "decimal numbers" },
  { "/m/12", &(ksort_str[12]), "floating point numbers" },
  { "/m/13", &(ksort_str[13]), "reversed sort order" },
  { "/m/14", &(ksort_str[14]), "unique sorting" },
  { "/m/15", &(ksort_str[15]), "case-insensitive" },
  { "/m/16", &(ksort_str[16]), "Current configuration:" },
  { NULL, NULL, NULL }
};



/**	Saved input lines sorted container.
*/
static dk_storage_t *lines = NULL;

/**	Saved input lines iterator.
*/
static dk_storage_iterator_t *linesit = NULL;



/**	Flag: Ignore leading blanks.
*/
#define FLAG_IGNORE_BLANKS   1

/**	Flag: Case insensitive comparisons.
*/
#define FLAG_CASEINS         2

/**	Flag: Sort by decimal values.
*/
#define FLAG_NUMERIC         4

/**	Flag: Inverted sort order,
*/
#define FLAG_REVERSED        8

/**	Flag: Merge equal lines.
*/
#define FLAG_UNIQUE         16

/**	Flag: Sort by floating point numbers.
*/
#define FLAG_NUMFLOAT       32

/**	Flag: Print help.
*/
#define FLAG_HELP	    64

/**	Flag: Print version information.
*/
#define FLAG_VERSION       128

/**	Flag: Configure the application.
*/
#define FLAG_CONFIGURE     256

/**	Flag: Show current configuration.
*/
#define FLAG_SHOWCONF	   512

/**	Flag: Unconfigure the application.
*/
#define FLAG_UNCONF	  1024



/**	Flag set.
*/
static int flags = 0;



/**	Input file names.
*/
static char **filenames;

/**	Number of names to use.
*/
int           names_to_use;



/**	Output file name.
*/
static char  *outputname = NULL;

/**	Output file.
*/
static FILE  *outputfile = NULL;



#ifndef LINELENGTH
/**	Maximum line length.
*/
#define LINELENGTH 512
#endif

/**	Line length.
*/
static int    linelength = LINELENGTH ;



/**	Preference value: on.
*/
static char pv_on[] =      { "on" };

/**	Preference value: off.
*/
static char pv_off[] =     { "off" };



/**	Preference name for ignoring leading blanks.
*/
static char pn_blanks[] =  { "/ignore-blanks" };

/**	Preference name for folds.
*/
static char pn_fold[] =    { "/fold" };

/**	Preference name for sort by numeric values.
*/
static char pn_numeric[] = { "/numeric" };

/**	Preference name for sort by floating point number.
*/
static char pn_float[] =   { "/numeric-float" };

/**	Preference name for inverted sort order.
*/
static char pn_reverse[] = { "/reverse" };

/**	Preference name for merge option.
*/
static char pn_unique[]  = { "/unique" };

/**	Preference name for output.
*/
static char pn_output[]  = { "/output" };

/**	Preference name for line length.
*/
static char pn_ll[]      = { "/line-length" };



/**	Adjust the flags.
	@param	ptr	Boolean value as text.
	@param	f	Old flag set.
	@return	Modified flag set.
*/
static
void
adjust_flags DK_P2(char *,ptr, int, f)
{
  
  ptr++;
  if(*ptr) {
    if(*ptr == '-') {
      flags = flags & (~(f)); 
    } else {
      if(*ptr == '+') {
        flags = flags | f; 
      } else {
        if(dkstr_is_on(ptr)) {
          flags = flags | f; 
        } else {
          flags = flags & (~(f)); 
        }
      }
    }
  } else {
    flags = flags | f; 
  }
  
}



/**	Reset all options completely.
*/
static
void
reset_completely DK_P0()
{
  flags = 0;
  linelength = LINELENGTH ;
}



/**	Long options.
*/
static char *long_options[] = {
  /* 00 */ "r$eset",
  /* 01 */ "s$how-configuration",
  /* 02 */ "c$onfigure",
  /* 03 */ "l$inelength",
  /* 04 */ "u$nconfigure",
  /* 05 */ "h$elp",
  /* 06 */ "v$ersion",
  /* 07 */ "ig$nore-blanks",
  /* 08 */ "d$ecimal",
  /* 09 */ "f$loat",
  /* 10 */ "in$verted",
  /* 11 */ "m$erge",
  /* 12 */ "o$utput-file",
  /* 13 */ "n$ot-case-sensitive",
  NULL
};



/**	Process command line arguments.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	Comparison result.
*/
static
int
process_args DK_P2(int, argc, char **, argv)
{
  int back = 1;
  char buffer[64];
  int i, x;
  char *cptr, *value, **lfd;
  char *logmsg[3];
  int found;

  
  /*
    Reset to program defaults
  */
  reset_completely();
  /*
    Retrieve preferences
  */
  if(dkapp_get_pref(app, pn_blanks, buffer, sizeof(buffer), 0)) {
    if(dkstr_is_on(buffer)) {
      flags |= FLAG_IGNORE_BLANKS;
    } else {
      flags &= (~(FLAG_IGNORE_BLANKS));
    }
  }
  if(dkapp_get_pref(app, pn_numeric, buffer, sizeof(buffer), 0)) {
    if(dkstr_is_on(buffer)) {
      flags |= FLAG_NUMERIC;
    } else {
      flags &= (~(FLAG_NUMERIC));
    }
  }
  if(dkapp_get_pref(app, pn_float, buffer, sizeof(buffer), 0)) {
    if(dkstr_is_on(buffer)) {
      flags |= FLAG_NUMFLOAT;
    } else {
      flags &= (~(FLAG_NUMFLOAT));
    }
  }
  if(dkapp_get_pref(app, pn_reverse, buffer, sizeof(buffer), 0)) {
    if(dkstr_is_on(buffer)) {
      flags |= FLAG_REVERSED;
    } else {
      flags &= (~(FLAG_REVERSED));
    }
  }
  if(dkapp_get_pref(app, pn_reverse, buffer, sizeof(buffer), 0)) {
    if(dkstr_is_on(buffer)) {
      flags |= FLAG_UNIQUE;
    } else {
      flags &= (~(FLAG_UNIQUE));
    }
  }
  if(dkapp_get_pref(app, pn_ll, buffer, sizeof(buffer), 0)) {
    if(sscanf(buffer, "%d", &x) == 1) {
      linelength = x ;
    }
  }
  if(dkapp_get_pref(app, pn_output, buffer, sizeof(buffer), 0)) {
    outputname = dkstr_dup(buffer);
    if(!outputname) { back = 0; }
  }
  lfd = argv;
  lfd++; i = 1;
  while(i < argc) {
    cptr = *lfd; value = NULL;
    if(*cptr == '-') {
      cptr++;
      switch(*cptr) {
	case '-' : { /* long options alles neu */
	  char *optarg;
	  cptr++;
	  optarg = dkstr_chr(cptr, '=');
	  if(optarg) { *(optarg++)='\0'; }
	  
	  found = dkstr_array_abbr(long_options, cptr, '$', 1);
	  
	  switch(found) {
	    case 0: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  reset_completely();
		}
	      } else {
	        reset_completely();
	      }
	    } break;
	    case 1: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_SHOWCONF;
		} else {
		  flags &= (~(FLAG_SHOWCONF));
		}
	      } else {
	        flags |= FLAG_SHOWCONF;
	      }
	    } break;
	    case 2: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_CONFIGURE;
		} else {
		  flags &= (~(FLAG_CONFIGURE));
		}
	      } else {
	        flags |= FLAG_CONFIGURE;
	      }
	    } break;
	    case 3: {
	      linelength = LINELENGTH;
	      if(optarg) {
	        if(sscanf(optarg, "%d", &x) == 1) {
		  linelength = x;
		}
	      }
	    } break;
	    case 4: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_CONFIGURE;
		  flags |= FLAG_UNCONF;
		  dkapp_unconfigure(app);
		} else {
		  flags &= (~(FLAG_CONFIGURE));
		  flags &= (~(FLAG_UNCONF));
		}
	      } else {
	        flags |= FLAG_CONFIGURE;
		flags |= FLAG_UNCONF;
		dkapp_unconfigure(app);
	      }
	    } break;
	    case 5: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_HELP;
		} else {
		  flags &= (~(FLAG_HELP));
		}
	      } else {
	        flags |= FLAG_HELP;
	      }
	    } break;
	    case 6: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_VERSION;
		} else {
		  flags &= (~(FLAG_VERSION));
		}
	      } else {
	        flags |= FLAG_VERSION;
	      }
	    } break;
	    case 7: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_IGNORE_BLANKS;
		} else {
		  flags &= (~(FLAG_IGNORE_BLANKS));
		}
	      } else {
	        flags |= FLAG_IGNORE_BLANKS;
	      }
	    } break;
	    case 8: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_NUMERIC;
		  flags &= (~(FLAG_NUMFLOAT));
		} else {
		  flags &= (~(FLAG_NUMERIC));
		}
	      } else {
	        flags |= FLAG_NUMERIC;
		flags &= (~(FLAG_NUMFLOAT));
	      }
	    } break;
	    case 9: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_NUMFLOAT;
		  flags &= (~(FLAG_NUMERIC));
		} else {
		  flags &= (~(FLAG_NUMFLOAT));
		}
	      } else {
	        flags |= FLAG_NUMFLOAT;
		flags &= (~(FLAG_NUMERIC));
	      }
	    } break;
	    case 10: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_REVERSED;
		} else {
		  flags &= (~(FLAG_REVERSED));
		}
	      } else {
	        flags |= FLAG_REVERSED;
	      }
	    } break;
	    case 11: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_UNIQUE;
		} else {
		  flags &= (~(FLAG_UNIQUE));
		}
	      } else {
	        flags |= FLAG_UNIQUE;
	      }
	    } break;
	    case 12: {
              if(optarg) {
	        if(outputname) { dk_delete(outputname); }
	        outputname = dkstr_dup(optarg);
	        if(!outputname) {
	          back = 0;
	          logmsg[0] = ksort_str[0];
	          dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
	        }
	      }
	    } break;
	    case 13: {
	      if(optarg) {
	        if(dkstr_is_on(optarg)) {
		  flags |= FLAG_CASEINS;
		} else {
		  flags &= (~(FLAG_CASEINS));
		}
	      } else {
	        flags |= FLAG_CASEINS;
	      }
	    } break;
	  }
	} break;
	case 'C' : {
	  flags |= FLAG_SHOWCONF;
	} break;
	case 'c' : {
	  flags |= FLAG_CONFIGURE;
	} break;
	case 'l' : {
	  cptr++;
	  if(*cptr) {
	    value = cptr;
	  } else {
	    i++; lfd++;
	    if(i < argc) {
	      value = *lfd;
	    }
	  }
	  if(value) {
	    if(sscanf(value, "%d", &x) == 1) {
	      linelength = x ;
	    }
	  }
	} break;
	case 'u' : {
	  flags |= FLAG_CONFIGURE;
	  flags |= FLAG_UNCONF;
	  dkapp_unconfigure(app);
	} break;
	case 'h' : {
	  flags |= FLAG_HELP;
	} break;
	case 'v' : {
	  flags |= FLAG_VERSION;
	} break;
	case 'b' : {
	  adjust_flags(cptr, FLAG_IGNORE_BLANKS);
	} break;
	case 'i' : {
	  /* adjust_flags(cptr, FLAG_CASEINS); */
	  adjust_flags(cptr, FLAG_REVERSED);
	} break;
	case 'n' : {
	  adjust_flags(cptr, FLAG_CASEINS);
	} break;
	case 'd' : {
	  adjust_flags(cptr, FLAG_NUMERIC);
	} break;
	case 'f' : {
	  adjust_flags(cptr, FLAG_NUMFLOAT);
	} break;
	case 'r' : {
	  /* adjust_flags(cptr, FLAG_REVERSED); */
	  reset_completely();
	} break;
	case 'm' : {
	  adjust_flags(cptr, FLAG_UNIQUE);
	} break;
	case 'o' : {
	  cptr++;
	  if(*cptr) { value = cptr; }
	  else {
	    lfd++; i++;
	    if(i < argc) {
	      value = *lfd;
	    }
	  }
	  if(value) {
	    if(outputname) { dk_delete(outputname); }
	    outputname = dkstr_dup(value);
	    if(!outputname) {
	      back = 0;
	      logmsg[0] = ksort_str[0];
	      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
	    }
	  }
	} break;
      }
    } else {
      filenames[names_to_use++] = cptr;
    }
    i++; lfd++;
  }
  if(linelength < 256) {
    linelength = 256;
  }
  if(linelength > 0x7FFF) {
    linelength = 0x7FFF;
  }
  
  return back;
}



/**	Input line record.
*/
typedef struct {
  char  *line;	/**< Line text. */
  long   lv;	/**< Long numeric value. */
  double dv;	/**< Double numeric value. */
  int    vf;	/**< Flag: Numeric value found at start of line. */
} LineInfo;



/**	Compare two strings.
	@param	s1	Left string.
	@param	s2	Right string.
	@param	ci	Flag: Case-insensitive.
	@return	Comparison result.
*/
static
int
xxx_cmp DK_P3(char *, s1, char *, s2, int, ci)
{
  int back = 0;
  char *p1, *p2;
  p1 = s1; p2 = s2;
  if(flags & FLAG_IGNORE_BLANKS) {
    p1 = dkstr_start(s1, NULL);
    p2 = dkstr_start(s2, NULL);
  }
  if(p1) {
    if(p2) {
      if(ci) {
	back = dkstr_casecmp(p1,p2);
      } else {
	back = strcmp(p1,p2);
      }
    } else {
      back = 1;
    }
  } else {
    if(p2) {
      back = -1;
    }
  }
  return back;
}



/**	Compare two input lines.
	@param	p1	Left line.
	@param	p2	Right line.
	@param	cr	Comparison criteria.
	@return Comparison result.
*/
static
int
compare_data DK_P3(void *, p1, void *, p2, int, cr)
{
  int back = 0;
  LineInfo *l1, *l2;
  char     *s1, *s2;
  double   d; long l; int v;
  
  if(p1) {
    if(p2) {
      switch(cr) {
	case 1: {
	  if(flags & (FLAG_NUMERIC | FLAG_NUMFLOAT)) {
	    v = 0; l = 0L; d = 0.0;
	    l1 = (LineInfo *)p1; s2 = (char *)p2;
	    if(flags & FLAG_NUMFLOAT) {
	      if(sscanf(s2, "%lf", &d) == 1) {
		v = 1;
	      }
	    } else {
	      if(sscanf(s2, "%ld", &l) == 1) {
		v = 1;
	      }
	    }
	    if(l1->vf) {
	      if(v) {
		if(flags & FLAG_NUMFLOAT) {
		  if((l1->dv) > d) {
		    back = 1;
		  } else {
		    if((l1->dv) < d) {
		      back = -1;
		    }
		  }
		} else {
		  if((l1->lv) > l) {
		    back = 1;
		  } else {
		    if((l1->lv) < l) {
		      back = -1;
		    }
		  }
		}
	      } else {
		back = 1;
	      }
	    } else {
	      if(v) {
		back = -1;
	      } else {
		if(flags & FLAG_CASEINS) {
		  back = xxx_cmp(l1->line, s2, 1);
		} else {
		  back = xxx_cmp(l1->line, s2, 0);
		}
	      }
	    }
	  } else {
	    s1 = (char *)p1; s2 = (char *)p2;
	    if(flags & FLAG_CASEINS) {
	      back = xxx_cmp(s1,s2, 1);
	    } else {
	      back = xxx_cmp(s1,s2, 0);
	    }
	  }
	} break;
	default : {
	  if(flags & (FLAG_NUMERIC | FLAG_NUMFLOAT)) {
	    l1 = (LineInfo *)p1; l2 = (LineInfo *)p2;
	    if(l1->vf) {
	      if(l2->vf) {
		if(flags & FLAG_NUMFLOAT) {
		  if((l1->dv) > (l2->dv)) {
		    back = 1;
		  } else {
		    if((l1->dv) < (l2->dv)) {
		      back = -1;
		    }
		  }
		} else {
		  if((l1->lv) > (l2->lv)) {
		    back = 1;
		  } else {
		    if((l1->lv) < (l2->lv)) {
		      back = -1;
		    }
		  }
		}
	      } else {
		back = 1;
	      }
	    } else {
	      if(l2->vf) {
		back = -1;
	      } else {
		if(flags & FLAG_CASEINS) {
		  back = xxx_cmp(l1->line, l2->line, 1);
		} else {
		  back = xxx_cmp(l1->line, l2->line, 0);
		}
	      }
	    }
	  } else {
	    s1 = (char *)p1; s2 = (char *)p2;
	    if(flags & FLAG_CASEINS) {
	      back = xxx_cmp(s1,s2, 1);
	    } else {
	      back = xxx_cmp(s1,s2, 0);
	    }
	  }
	} break;
      }
    } else {
      back = 1;
    }
  } else {
    if(p2) {
      back = -1;
    }
  }
  if(flags & FLAG_REVERSED) {
    
    back = 0 - back;
  }
  if(back < -1) back = -1;
  if(back > 1)  back = 1; 
  return back;
}



/**	Save one input line in sorted container.
	@param	line	Input line.
	@return	1 on success, 0 on error.
*/
static
int
save_line DK_P1(char *, line)
{
  int back = 0;
  LineInfo *li;
  long l; double d;
  
  if(flags & (FLAG_NUMERIC | FLAG_NUMFLOAT)) {
    if(flags & FLAG_UNIQUE) {
      if(dksto_it_find_like(linesit, line, 1)) {
	dk_delete(line); back = 1;
      } else {
        li = dk_new(LineInfo,1);
        if(li) {
	  li->line = line;
	  li->vf = 0;
	  if(flags & FLAG_NUMFLOAT) {
	    if(sscanf(line, "%lf", &d) == 1) {
	      li->dv = d; li->vf = 1;
	    }
	  } else {
	    if(sscanf(line, "%ld", &l) == 1) {
	      li->lv = l; li->vf = 1;
	    }
	  }
	  if(dksto_add(lines, li)) {
	    back = 1;
	  } else {
	    dk_delete(li);
	  }
        } 
      }
    } else {
      li = dk_new(LineInfo,1);
      if(li) {
	li->line = line;
	li->vf = 0;
	if(flags & FLAG_NUMFLOAT) {
	  if(sscanf(line, "%lf", &d) == 1) {
	    li->dv = d; li->vf = 1;
	  }
	} else {
	  if(sscanf(line, "%ld", &l) == 1) {
	    li->lv = l; li->vf = 1;
	  }
	}
	if(dksto_add(lines, li)) {
	  back = 1;
	} else {
	  dk_delete(li);
	}
      } 
    }
  } else {
    
    if(flags & FLAG_UNIQUE) {
      
      if(dksto_it_find_like(linesit, line, 1)) {
	
	back = 1;
	dk_delete(line);
      } else {
	
	back = dksto_add(lines, line);
      }
    } else {
      
      back = dksto_add(lines, line);
    }
  } 
  return back;
}



/**	Read input from input file.
	@param	in	Input file.
	@param	buffer	Buffer for line.
	@return	1 on success, 0 on error.
*/
static
int
read_file DK_P2(FILE *, in, char *, buffer)
{
  int back = 1;
  size_t maxlgt;
  char   *copyptr;
  char   *logmsg[3];
  maxlgt = linelength - 1;
  while(fgets(buffer, maxlgt, in)) {
    
    copyptr = dkstr_dup(buffer);
    if(copyptr) {
      if(!save_line(copyptr)) {
	dk_delete(copyptr); copyptr = NULL;
	back = 0;
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
    } else {
      back = 0;
      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
    }
  }
  return back;
}



/**	Open input file, read input and
	Save all input lines in a sorted container.
*/
static
int
read_input DK_P0()
{
  int back = 0;
  dk_fne_t *fne; int i; int need_correction;
  char *inputline, *cptr, *fn;
  FILE *infile;
  char *logmsg[3];
  
  inputline = dk_new(char,linelength);
  if(inputline) {
    back = 1;
    if(names_to_use) {
      for(i = 0; i < names_to_use; i++) {
	cptr = filenames[i];
	need_correction = dksf_must_expand_filename(filenames[i]);
	cptr = filenames[i];
	if(need_correction) {
	  need_correction = 0;
	  fne = dkfne_open(cptr, 1, 0);
	  if(fne) {
	    while(dkfne_next(fne)) {
	      need_correction = 1;
	      fn = dkfne_get_fullname(fne);
	      infile = dkapp_fopen(app, fn, "r");
	      if(infile) {
		if(!read_file(infile, inputline)) {
		  back = 0;
		}
		fclose(infile);
	      } else {
		back = 0;
		logmsg[0] = ksort_str[1];
		logmsg[2] = ksort_str[2];
		logmsg[1] = fn;
		dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
	      }
	    }
	    if(!need_correction) {
	      back = 0;
	      logmsg[0] = ksort_str[3];
	      logmsg[2] = ksort_str[4];
	      logmsg[1] = cptr;
	      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
	    }
	    dkfne_close(fne);
	  } else {
	    back = 0;
	    logmsg[0] = ksort_str[5];
	    logmsg[2] = ksort_str[6];
	    logmsg[1] = cptr;
	    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
	  }
	} else {
	  infile = dkapp_fopen(app, cptr, "r");
	  if(infile) {
	    if(!read_file(infile, inputline)) {
	      back = 0;
	    }
	    fclose(infile);
	  }
	  /* 2003/12/17 auskommentiert wegen Directory-Check
	  else {
	    back = 0;
	    logmsg[0] = ksort_str[1];
	    logmsg[2] = ksort_str[2];
	    logmsg[1] = cptr;
	    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 3);
	  }
	  */
	}
      }
    } else {
      back = read_file(stdin, inputline);
    }
    dk_delete(inputline);
  } else {
    dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
  } 
  return back;
}



/**	Write output.
*/
static
void
write_to DK_P0()
{
  LineInfo *li; char *s;
  dksto_it_reset(linesit);
  if(flags & (FLAG_NUMFLOAT | FLAG_NUMERIC)) {
    while((li = (LineInfo *)dksto_it_next(linesit)) != NULL) {
      fprintf(outputfile, "%s", li->line);
    }
  } else {
    while((s = (char *)dksto_it_next(linesit)) != NULL) {
      fprintf(outputfile, "%s", s);
    }
  }
}



/**	Open output file and write output.
*/
static
int
write_output DK_P0()
{
  int back = 0;
  FILE *myfile;
  dk_fne_t *fne;
  if(outputname) {
    dksf_correct_fnsep(outputname);
    if(dksf_must_expand_filename(outputname)) {
      fne = dkfne_open(outputname, 1, 0);
      if(fne) {
        char *cptr;
        cptr = dkapp_fne_one(app, fne, outputname);
	if(cptr) {
	  myfile = dkapp_fopen(app,cptr,"w");
	  if(myfile) {
	    outputfile = myfile; back = 1;
	    write_to();
	    fclose(myfile);
	  } else {
	    dkapp_err_fopenw(app, cptr);
	  }
	  dk_delete(cptr);
	}
        dkfne_close(fne); fne = NULL;
      } else {
	dkapp_err_no_such_file(app, outputname);
      }
    } else {
      myfile = dkapp_fopen(app,outputname,"w");
      if(myfile) {
        outputfile = myfile; back = 1;
	write_to();
        fclose(myfile);
      }
    }
  } else {
    back = 1;
    outputfile = stdout;
    write_to();
  }
  outputfile = NULL;
  return back;
}



/**	Definition to allow the use of the sizeof() operator.
*/
typedef char *CHARPTR;



/**	Save configuration to preferences system.
*/
static
void
save_configuration DK_P0()
{
  char buffer[32];
  dkapp_set_pref(app,pn_blanks,((flags & FLAG_IGNORE_BLANKS) ? pv_on : pv_off));
  dkapp_set_pref(app,pn_fold,((flags & FLAG_CASEINS) ? pv_on : pv_off));
  dkapp_set_pref(app,pn_numeric,((flags & FLAG_NUMERIC) ? pv_on : pv_off));
  dkapp_set_pref(app,pn_float,((flags & FLAG_NUMFLOAT) ? pv_on : pv_off));
  dkapp_set_pref(app,pn_reverse,((flags & FLAG_REVERSED) ? pv_on : pv_off));
  dkapp_set_pref(app,pn_unique,((flags & FLAG_UNIQUE) ? pv_on : pv_off));
  if(outputname) {
    dkapp_set_pref(app,pn_output,outputname);
  }
  sprintf(buffer, "%d", linelength);
  dkapp_set_pref(app, pn_ll, buffer);
}



/**	Print a string right aligned.
	@param	str	String to print.
	@param	max	Available space for string.
*/
static
void
print_string_right_aligned DK_P2(char *,str,size_t, max)
{
  size_t i;
  i = 0;
  if(str) { i = dkapp_prlen(app, str); }
  while(i++ < max) fputc(' ', stdout);
  if(str) dkapp_stdout(app,str);
}



/**	Three spaces are used before and after the option value.
*/
static char three_spaces[] = { "   " };



/**	Print a text option.
	@param	key	Option name.
	@param	val	Option value.
	@param	desc	Option description.
	@param	max	Available space for option value.
*/
static
void
print_string_option DK_P4(char *, key, char *, val, char *, desc, size_t, max)
{
  dkapp_stdout(app, key);
  fputs(three_spaces, stdout);
  print_string_right_aligned(val, max);
  fputs(three_spaces, stdout);
  dkapp_stdout(app, desc);
  fputc('\n', stdout);
}



/**	Print a boolean option.
	@param	key	Option name.
	@param	val	Option value.
	@param	desc	Option description.
	@param	max	Maximum space for value.
*/
static
void
print_bool_option DK_P4(char *, key, int, val, char *, desc, size_t, max)
{
  dkapp_stdout(app, key);
  fputs(three_spaces, stdout);
  print_string_right_aligned((val ? ksort_str[7] : ksort_str[8]), max);
  fputs(three_spaces, stdout);
  dkapp_stdout(app,desc);
  fputc('\n', stdout);
}



/**	Find maximum length of multiple strings.
	@param	str	One string to process.
	@param	old	Maximum of analized strings already processed.
	@return	Maximum of \a old and the length of \a str.
*/
static
size_t
find_max_string_length DK_P2(char *,str,size_t, old)
{
  size_t i;
  size_t back;
  back = old;
  if(str) {
    i = dkapp_prlen(app, str);
    if(i > back) back = i;
  }
  return back;
}



/**	Show configuration.
*/
static
void
show_configuration DK_P0()
{
  char llbuffer[32]; size_t max;
  
  sprintf(llbuffer, "%d", linelength);
  max = 0;
  max = find_max_string_length(llbuffer, max);
  max = find_max_string_length(ksort_str[7], max);
  max = find_max_string_length(ksort_str[8], max);
  dkapp_stdout(app, ksort_str[16]);
  fputc('\n', stdout);
  print_bool_option("-b", (flags & FLAG_IGNORE_BLANKS), ksort_str[10], max);
  print_bool_option("-d", (flags & FLAG_NUMERIC), ksort_str[11], max);
  print_bool_option("-f", (flags & FLAG_NUMFLOAT), ksort_str[12], max);
  print_bool_option("-i", (flags & FLAG_REVERSED), ksort_str[13], max);
  print_bool_option("-m", (flags & FLAG_UNIQUE), ksort_str[14], max);
  print_bool_option("-n", (flags & FLAG_CASEINS), ksort_str[15], max);
  print_string_option("-l", llbuffer, ksort_str[9], max);
  
}



/**	The real main function which is run after creating the application.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	1 on success, 0 on error.
*/
static
int
run_main DK_P2(int, argc, char **, argv)
{
  int back = 0;
  LineInfo *li; char *s;
  int i;
  char *logmsg[3];
  if(argv) {
    filenames = dk_new(CHARPTR,argc);
    if(filenames) {
      for(i = 0; i < argc; i++) { filenames[i] = NULL; }
      lines = dksto_open(0);
      if(lines) {
	linesit = dksto_it_open(lines);
	if(linesit) {
	  if(process_args(argc,argv)) {
	    i = FLAG_HELP | FLAG_VERSION | FLAG_CONFIGURE | FLAG_SHOWCONF;
	    if(flags & i) {
	      if(flags & (FLAG_HELP | FLAG_VERSION)) {
		if(flags & FLAG_VERSION) {
		  print_version();
		}
		if(flags & FLAG_HELP) {
		  dkapp_help(app, "ksort.txt", help_text);
		}
	      } else {
	        if(flags & FLAG_CONFIGURE) {
		  if(!(flags & FLAG_UNCONF)) {
		    save_configuration();
		    show_configuration();
		  }
		} else {
		  if(flags & FLAG_SHOWCONF) {
		    show_configuration();
		  }
		}
	      }
	    } else {
	      dksto_set_comp(lines, compare_data, 0);
	      back = 1;
	      if(!read_input()) { back = 0; }
	      if(!write_output()) { back = 0; }
	      dksto_it_reset(linesit);
	      if(flags & (FLAG_NUMERIC | FLAG_NUMFLOAT)) {
		while((li = (LineInfo *)dksto_it_next(linesit)) != NULL) {
		  s = li->line;
		  dk_delete(s);
		  li->line = NULL;
		  li->vf = 0; li->lv = 0L; li->dv = 0.0;
		  dk_delete(li);
		}
	      } else {
		while((s = (char *)dksto_it_next(linesit)) != NULL) {
		  dk_delete(s);
		}
	      }
	    }
	  }
	  if(outputname) {
	    dk_delete(outputname);
	    outputname = NULL;
	  }
	  dksto_it_close(linesit);
	} else {
	  dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
	}
	dksto_close(lines);
      } else {
	dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
      }
      for(i = 0; i < argc; i++) { filenames[i] = NULL; }
      dk_delete(filenames);
    } else {
      dkapp_log_msg(app, DK_LOG_LEVEL_ERROR, logmsg, 1);
    }
  }
  return back;
}



/**	The main() function of the ksort program.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	0 on success, any other value indicates an error.
*/
#if DK_HAVE_PROTOTYPES
int main(int argc, char *argv[])
#else
int main(argc, argv) int argc; char *argv[];
#endif
{
 int exval = 0;
 
#line 1451 "ksort.ctr"

 app = dkapp_open_ext1(argc, argv, packagename, sysconfdir, 0, 0);
 if(app) {
  dkapp_find_multi(app, ksort_find, "ksort");
  exval = run_main(dkapp_get_argc(app), dkapp_get_argv(app));
  dkapp_close(app);
  app = NULL;
 } else {
   if(!dkapp_silence_check(argc,argv)) {
     fprintf(stderr, "ERROR: NOT ENOUGH MEMORY\n");
     fflush(stderr);
   }
 }
 
#line 1464 "ksort.ctr"

 exval = (exval ? 1 : 0);
 exit(exval);
 return exval;
}



