/*
** CONF: Configuration option / file handling module
** Copyright (C) 2002 Michael W. Shaffer <mwshaffer@angrypot.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.  
**
** You should have received a copy of the GNU General Public License
** along with this program (see the file COPYING). If not, write to:
**
** The Free Software Foundation, Inc.
** 59 Temple Place, Suite 330,
** Boston, MA  02111-1307  USA
*/

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "getopt.h"
#include "list.h"
#include "hash.h"
#include "config_file.h"
#include "ssync.h"
#include "conf.h"

extern char *conf_path_default;

static struct hash_table conf;

char * get_config_string (char *key)
{
	struct datum d;
	struct datum *dd;

	if (!key)
		return NULL;

	memset (&d, 0, sizeof (struct datum));
	d.key = key;
	d.ksize = strlen (key);
	dd = hash_table_search (&conf, &d);
	if (!dd)
		return NULL;
	return (dd->val);
}

long get_config_long (char *key)
{
	long lval = 0;
	char *val = NULL;

	val = get_config_string (key);
	if (!val)
		return (0);

	if ((strlen (val) > 2) && (val[0] == '0') && (val[1] == 'x')) {
		lval = (int) strtoul (val, NULL, 16);
	} else {
		lval = (int) strtoul (val, NULL, 10);
	}
	return lval;
}

double get_config_double (char *key)
{
	char *val = NULL;

	val = get_config_string (key);
	if (!val)
		return ((double) 0);

	return strtod (val, NULL);
}

void config_write (int fd)
{
	int i = 0;
	struct datum *d = NULL;
	struct list_item *curr = NULL;
	char msg[256];

	for (i = 0 ; i < conf.size ; i++) {
		for (curr = conf.tbl[i].head ; curr ; curr = curr->next) {
			d = (struct datum *) curr->data;
			if (d) {
				memset (msg, 0, sizeof (msg));
				snprintf (msg, (sizeof (msg) - 1),
						"%s: %s\n",
						(char *) d->key,
						(char *) d->val);
				write (fd, msg, strlen (msg));
			}
		}
	}
	return;
}

enum options {
	o_help = 0,
	o_interval,
	o_work_file,
	o_src_path,
	o_dst_path,
	o_priority,
	o_no_detach,
	o_no_sync_data,
	o_no_sync_time,
	o_no_sync_meta,
	o_update_only,
	o_test,
	o_pid_path,
	o_log_mode,
	o_log_path,
	o_log_ident,
	o_log_level,
	o_conf_path
};

static struct option long_options[] = {
	{ "help", 0, 0, 'h' },
	{ "interval", 1, 0, 'i' },
	{ "work-file", 1, 0, 'w' },
	{ "src-path", 1, 0, 'f' },
	{ "dst-path", 1, 0, 't' },
	{ "priority", 1, 0, 'n' },
	{ "no-detach", 0, 0, 'F' },
	{ "no-sync-data", 0, 0, 'D' },
	{ "no-sync-time", 0, 0, 'T' },
	{ "no-sync-meta", 0, 0, 'M' },
	{ "update-only", 0, 0, 'U' },
	{ "test", 0, 0, 'X' },
	{ "pid-path", 1, 0, 'p' },
	{ "log-mode", 1, 0, 'm' },
	{ "log-path", 1, 0, 'l' },
	{ "log-ident", 1, 0, 's' },
	{ "log-level", 1, 0, 'v' },
	{ "conf-path", 1, 0, 'c' },
	{ 0, 0, 0, 0 }
};

static void parse_command_line (int argc, char **argv, struct hash_table *conf)
{
	int c = '\0';
	int optind = 0;
	struct datum d;

	if (!((argc > 0) && argv && conf))
		return;

	opterr = 0;

	memset (&d, 0, sizeof (struct datum));
	d.key = "conf-path";
	d.ksize = strlen ("conf-path");
	d.val = conf_path_default;
	d.vsize = strlen (conf_path_default);
	hash_table_insert (conf, &d);

	while (1) {
		memset (&d, 0, sizeof (struct datum));

		c = getopt_long (argc, argv, "hi:w:f:t:n:FDTMUXp:m:l:s:v:c:",
				long_options, &optind);
		if (c == -1)
			goto EXIT;

		switch (c) {
			case 'h':
				print_usage (argv[0], 2);
				exit (0);
				break;
			case 'i':
				d.key = (char *) long_options[o_interval].name;
				d.ksize = strlen (long_options[o_interval].name);
				d.val = optarg;
				break;
			case 'w':
				d.key = (char *) long_options[o_work_file].name;
				d.ksize = strlen (long_options[o_work_file].name);
				d.val = optarg;
				break;
			case 'f':
				d.key = (char *) long_options[o_src_path].name;
				d.ksize = strlen (long_options[o_src_path].name);
				d.val = optarg;
				break;
			case 't':
				d.key = (char *) long_options[o_dst_path].name;
				d.ksize = strlen (long_options[o_dst_path].name);
				d.val = optarg;
				break;
			case 'n':
				d.key = (char *) long_options[o_priority].name;
				d.ksize = strlen (long_options[o_priority].name);
				d.val = optarg;
				break;
			case 'F':
				d.key = (char *) long_options[o_no_detach].name;
				d.ksize = strlen (long_options[o_no_detach].name);
				d.val = "yes";
				break;
			case 'D':
				d.key = (char *) long_options[o_no_sync_data].name;
				d.ksize = strlen (long_options[o_no_sync_data].name);
				d.val = "yes";
				break;
			case 'T':
				d.key = (char *) long_options[o_no_sync_time].name;
				d.ksize = strlen (long_options[o_no_sync_time].name);
				d.val = "yes";
				break;
			case 'M':
				d.key = (char *) long_options[o_no_sync_meta].name;
				d.ksize = strlen (long_options[o_no_sync_meta].name);
				d.val = "yes";
				break;
			case 'U':
				d.key = (char *) long_options[o_update_only].name;
				d.ksize = strlen (long_options[o_update_only].name);
				d.val = "yes";
				break;
			case 'X':
				d.key = (char *) long_options[o_test].name;
				d.ksize = strlen (long_options[o_test].name);
				d.val = "yes";
				break;
			case 'p':
				d.key = (char *) long_options[o_pid_path].name;
				d.ksize = strlen (long_options[o_pid_path].name);
				d.val = optarg;
				break;
			case 'm':
				d.key = (char *) long_options[o_log_mode].name;
				d.ksize = strlen (long_options[o_log_mode].name);
				d.val = optarg;
				break;
			case 'l':
				d.key = (char *) long_options[o_log_path].name;
				d.ksize = strlen (long_options[o_log_path].name);
				d.val = optarg;
				break;
			case 's':
				d.key = (char *) long_options[o_log_ident].name;
				d.ksize = strlen (long_options[o_log_ident].name);
				d.val = optarg;
				break;
			case 'v':
				d.key = (char *) long_options[o_log_level].name;
				d.ksize = strlen (long_options[o_log_level].name);
				d.val = optarg;
				break;
			case 'c':
				d.key = (char *) long_options[o_conf_path].name;
				d.ksize = strlen (long_options[o_conf_path].name);
				d.val = optarg;
				break;
			default:
				print_usage (argv[0], 2);
				exit (0);
				break;
		}

		if (d.key && d.ksize > 0) {
			d.vsize = strlen (d.val);
			hash_table_insert (conf, &d);
		}
	}

EXIT:
	return;
}

void config_load (int argc, char **argv)
{
	memset (&conf, 0, sizeof (struct hash_table));
	conf.size = 32;
	hash_table_init (&conf);

	parse_command_line (argc, argv, &conf);
	parse_config_file (get_config_string ("conf-path"), &conf);

	return;
}

void config_free (void)
{
	hash_table_free (&conf);
	return;
}

void print_usage (char *name, int fd)
{
	char msg[4096];

	if (!name || (fd < 0))
		goto EXIT;

	memset (msg, 0, sizeof (msg));
	snprintf (msg, (sizeof (msg) - 1),
		"                                                                        \n"
		"  ssync version %s                                                      \n"
		"                                                                        \n"
		"  Usage:                                                                \n"
		"                                                                        \n"
		"    %s [OPTIONS]                                                        \n"
		"                                                                        \n"
		"  Option                Argument               Comment                  \n"
		"  ----------------------------------------------------------------------\n"
		"  -h | --help                                  display this message     \n"
		"  -c | --conf-path      PATH                   alternative config file  \n"
		"  -i | --interval       NUM                    seconds between runs     \n"
		"  -w | --work-file      PATH                   file containing work list\n"
		"  -f | --src-path       PATH                   source path              \n"
		"  -t | --dst-path       PATH                   destination path         \n"
		"  -n | --priority       (-20 - +20)            scheduling priority      \n"
		"  -F | --no-detach                             no detach from terminal  \n"
		"  -D | --no-sync-data                          no sync data in files    \n"
		"  -T | --no-sync-time                          no sync atime / mtime    \n"
		"  -M | --no-sync-meta                          no sync uid / gid / mode \n"
		"  -U | --update-only                           only if source is newer  \n"
		"  -X | --test                                  don't modify anything    \n"
		"  -p | --pid-path       PATH                   path for pid file        \n"
		"  -m | --log-mode       (file|syslog|stderr)   path for log file        \n"
		"  -l | --log-path       PATH                   path for log file        \n"
		"  -s | --log-ident      STRING                 ident string for syslog  \n"
		"  -v | --log-level      (0 - 5)                verboseness              \n"
		"                                                                        \n",
		SSYNC_VERSION, name);
	write (fd, msg, strlen (msg));

EXIT:
	return;
}



syntax highlighted by Code2HTML, v. 0.9.1