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



/*
  dkpref -a <app> -g <group> <name> [<value>]
  	-e <list>	exclude sources
			'c'	command line
			'p'	program
			'u'	user
			's'	system
	-m		macro expansion
	-p		path name correction
*/

#include "dk.h"

#include <stdio.h>
#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_UNISTD_H
#include <unistd.h>
#endif
#if DK_HAVE_PROCESS_H
#include <process.h>
#endif

#include "dklogc.h"
#include "dkmem.h"
#include "dkapp.h"
#include "dksf.h"




#line 76 "dkpref.ctr"





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



/**	Program name.
*/
static char prname[] = { "dkpref" };



/**	Application group.
*/



static char grname[] = { "dktools" };



/**	Preference name to retrieve slash/backslash.
*/
static char slash[] = { "/" };



/**	Messages printed by the program.
*/
static dk_key_value_t kv[] = {
  { "/m/000", "Option ``-a'' was used multiple times!" },
  { "/m/001", "Option ``-g'' was used multiple times!" },
  { "/m/002", "Failed to create application structure!" },
  { "/m/003", "Failed to set preference value!!" },
  { "/m/004", "Macro expansion failed!" },
  { "/m/005", "Preference not found!!" },
  { "/m/006", "Value too long!" }
};
static size_t szkv = sizeof(kv)/sizeof(dk_key_value_t);
/**<	Number of messaages in array. */



/**	Buffer for data. */
static	char	b1[16384];


/**	Buffer for data. */
static	char	b2[sizeof(b1)];



/**	Program configuration.
*/
typedef struct {
  dk_app_t	*a;	/**< Application structure. */
  dk_app_t	*a2;	/**< Second application. */
  char		**msg;	/**< Messages printed by the program. */
  char		*n_a;	/**< Application name. */
  char		*n_g;	/**< Application group name. */
  char		*k;	/**< Key. */
  char		*v;	/**< Value. */
  int		excl;	/**< Source to exclude. */
  int		exval;	/**< Main programs exit code. */
  unsigned char	m;	/**< Flag: Macro expansion. */
  unsigned char	p;	/**< Flag: Path separator correction. */
} DKPREF;



/**	Text to show as usage information.
*/
static char *usage_text[] = {
"",
"dkpref - Retrieve and set preferences for applications",
"======================================================",
"",
"To retrieve a preference value run",
"  dkpref [<options>] <name>",
"",
"To set a preference value run",
"  dkpref [<options>] <name> <value>",
"",
"The following options can be used:",
"",
"-a <app>	specify an application name",
"-g <group>	specify an application group name",
"-e <list>	specify a list of preferences sources to exclude.",
"		The list contains of characters",
"		c	command line options",
"		p	preferences set by the program itself",
"		u	user preferences",
"		s	system preferences",
"-m		expand macros",
"-p		correct slash/backslash in path names",
"",
};



/**	Initialize program configuration.
	@param	p	Structure to configure.
*/
static
void
dkpref_init DK_P1(DKPREF *,p) {
  p->a = NULL;
  p->a2 = NULL;
  p->msg = NULL;
  p->excl = 0;
  p->m = 0x00;
  p->p = 0x00;
  p->n_a = NULL;
  p->n_g = NULL;
  p->k = NULL;
  p->v = NULL;
  p->exval = 0;
}



/**	Process command line arguments.
	@param	p	Configuration.
	@return	1 on success, 0 on error.
*/
static
int
process_arguments DK_P1(DKPREF *,p) {
  int back = 1;
  int argc;
  char *ptr;
  char **argv;
  char **lfdptr;
  unsigned char show_usage = 0x00;
  int i;
  
  argc = dkapp_get_argc(p->a);
  argv = dkapp_get_argv(p->a);
  lfdptr = argv; lfdptr++; i = 1;
  while(i < argc) {
    ptr = *lfdptr;
    if(*ptr == '-') {
      ptr++;
      switch(*ptr) {
        case 'a': {
	  ptr++;
	  if(!(*ptr)) {
	    ptr = NULL;
	    lfdptr++; i++;
	    if(i < argc) {
	      ptr = *lfdptr;
	    }
	  }
	  if(ptr) {
	    if(p->n_a) {
	      /* Warning -a multiple times */
	      dkapp_log_msg(p->a, DK_LOG_LEVEL_WARNING, &((p->msg)[0]), 1);
	    }
	    p->n_a = ptr;
	  }
	} break;
	case 'g': {
	  ptr++;
	  if(!(*ptr)) {
	    ptr = NULL;
	    lfdptr++; i++;
	    if(i < argc) {
	      ptr = *lfdptr;
	    }
	  }
	  if(ptr) {
	    if(p->n_g) {
	      /* Warning -g multiple times */
	      dkapp_log_msg(p->a, DK_LOG_LEVEL_WARNING, &((p->msg)[1]), 1);
	    }
	    p->n_g = ptr;
	  }
	} break;
	case 'e': {
	  ptr++;
	  if(!(*ptr)) {
	    ptr = NULL;
	    lfdptr++; i++;
	    if(i < argc) {
	      ptr = *lfdptr;
	    }
	  }
	  if(ptr) {
	    while(*ptr) {
	      switch(*(ptr++)) {
	        case 'c': {
		  p->excl |= DK_APP_PREF_EXCL_CMD;
		} break;
		case 'p': {
		  p->excl |= DK_APP_PREF_EXCL_PROG;
		} break;
		case 'u': {
		  p->excl |= DK_APP_PREF_EXCL_USER;
		} break;
		case 's': {
		  p->excl |= DK_APP_PREF_EXCL_SYSTEM;
		} break;
		default: {
		  back = 0;
		  show_usage = 0x01;
		} break;
	      }
	    }
	  }
	} break;
	case 'm': {
	  p->m = 0x01;
	} break;
	case 'p': {
	  p->p = 0x01;
	} break;
	default: {
	  back = 0;
	  show_usage = 0x01;
	} break;
      }
    } else {
      if(p->k) {
        if(p->v) {
	  back = 0;
	  show_usage = 0x01;
	} else {
	  p->v = ptr;
	}
      } else {
        p->k = ptr;
      }
    }
    i++; lfdptr++;
  }
  if(!back) {
    if(show_usage) {
      dkapp_help(p->a, "dkpref.txt", usage_text);
    }
  } 
  return back;
}



/**	Create second application structure.
	@param	p	Configuration.
*/
static
void
create_second_app DK_P1(DKPREF *,p) {
  char *argv[2];
  
  argv[0] = ((p->n_a) ? (p->n_a) : prname);
  argv[1] = NULL;
  p->a2 = dkapp_open_ext1(
    1, argv, ((p->n_g) ? (p->n_g) : grname), sysconfdir,0,DK_APP_LOG_NO_STDOUT
  );
  if(!(p->a2)) {	
    /* ERROR: Failed to create second application */
    dkapp_log_msg(p->a, DK_LOG_LEVEL_ERROR, &((p->msg)[2]), 1);
    p->exval = 1;
  } 
}




/**	Set preference value.
	@param	p	Configuration.
*/
static
void
set_value DK_P1(DKPREF *,p) {
  unsigned char usable = 0x00;
  
  create_second_app(p);
  if(p->a2) {
    if(strcmp(p->k, slash)) {
      if(strlen(p->v) < sizeof(b1)) {
        strcpy(b1, p->v); usable = 0x01;
	if(p->m) {
	  if(dkapp_transform_string(p->a2, b2, sizeof(b2), b1)) {
	    strcpy(b1, b2);
	  } else {
	    dkapp_log_msg(p->a, DK_LOG_LEVEL_ERROR, &((p->msg)[4]), 1);
	    p->exval = 1;
	    usable = 0x00;
	  }
	}
	if(p->p) {
	  dksf_correct_fnsep(b1);
	}
	if(usable) {
	  if(!dkapp_set_pref(p->a2, p->k, p->v)) {
	    dkapp_log_msg(p->a, DK_LOG_LEVEL_ERROR, &((p->msg)[3]), 1);
	    p->exval = 1;
	  }
	}
      } else {
        /* ##### ERROR: Value too long */
	p->exval = 1;
      }
    } else {
      dkapp_log_msg(p->a, DK_LOG_LEVEL_ERROR, &((p->msg)[3]), 1);
      p->exval = 1;
    }
    dkapp_close(p->a2); p->a2 = NULL;
  } 
}



/**	Retrieve preference value.
	@param	p	Configuration.
*/
static
void
get_value DK_P1(DKPREF *,p) {
  unsigned char usable = 0x00;
  
  create_second_app(p);
  if(p->a2) {
    if(dkapp_get_pref(p->a2, p->k, b1, sizeof(b1), p->excl)) {
      usable = 0x01;
      if(p->m) {
        if(dkapp_transform_string(p->a2, b2, sizeof(b2), b1)) {
          strcpy(b1, b2);
        } else {
	  /* ERROR in string transformation */
	  dkapp_log_msg(p->a, DK_LOG_LEVEL_ERROR, &((p->msg)[4]), 1);
	  p->exval = 1;
          usable = 0x00;
        }
      }
      if(p->p) {
        dksf_correct_fnsep(b1);
      }
      if(usable) {
        printf("%s\n", b1);
      }
    } else {	
      if(strcmp(p->k, slash) == 0) {
#if DK_HAVE_FEATURE_BACKSLASH
        printf("\\\n");
#else
	printf("/\n");
#endif
      } else {
        /* Failed to find preference */
	dkapp_log_msg(p->a, DK_LOG_LEVEL_ERROR, &((p->msg)[5]), 1);
	p->exval = 1;
      }
    }
    dkapp_close(p->a2); p->a2 = NULL;
  } 
}



/**	Handle the request.
	@param	p	Configuration.
*/
static
void
run DK_P1(DKPREF *,p) {
  
  if(process_arguments(p)) {
    if(p->k) {
      if(p->v) {
        set_value(p);
      } else {
        get_value(p);
      }
    } else {
      p->exval = 1;
      dkapp_help(p->a, "dkpref.txt", usage_text);
    }
  } else {
    p->exval = 1;
  } 
}



/**	The main program.
	@param	argc	Number of arguments on the command line.
	@param	argv	Arguments of the command line.
	@return	0 on success, all other values indicate errors.
*/
#if DK_HAVE_PROTOTYPES
int main(int argc, char *argv[])
#else
int main(argc, argv) int argc; char *argv[];
#endif
{
  DKPREF p;
  
#line 478 "dkpref.ctr"

  
  dkpref_init(&p);
  p.a = dkapp_open_ext1(argc,argv,grname,sysconfdir,0,DK_APP_LOG_NO_STDOUT);
  if(p.a) {
    p.msg = dkapp_find_key_value(p.a, kv, szkv, prname);
    if(p.msg) {
      run(&p);
      dk_delete((p.msg));
    }
    dkapp_close(p.a); p.a = NULL;
  } else {
    fprintf(stderr, "Not enough memory (RAM/swap)!\n");
    fflush(stderr);
  }
  
  
#line 494 "dkpref.ctr"

  exit(p.exval); return p.exval;
}




