/*****************************************************************************
 *
 * FILE:	postal.c
 * DESCRIPTION:	Postal: Search the address with postcode
 * DATE:	Mon, Aug 27 2007
 * UPDATED:	Tue, Aug 28 2007
 * AUTHOR:	Kouichi ABE (WALL) / °¤Éô¹¯°ì
 * E-MAIL:	kouichi@MysticWALL.COM
 * URL:		http://www.MysticWALL.COM/
 * COPYRIGHT:	(c) 2007 °¤Éô¹¯°ì¡¿Kouichi ABE (WALL), All rights reserved.
 * LICENSE:
 *
 *  Copyright (c) 2007 Kouichi ABE (WALL) <kouichi@MysticWALL.COM>,
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. 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.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 * $Id: postal.c,v 1.1 2007/08/29 11:19:56 kouichi Exp $
 *
 *****************************************************************************/

#if	HAVE_CONFIG_H
#include "config.h"
#endif	/* HAVE_CONFIG_H */

#include <stdio.h>
#if	HAVE_STDLIB_H
#include <stdlib.h>
#endif	/* HAVE_STDLIB_H */
#if	HAVE_UNISTD_H
#include <unistd.h>
#endif	/* HAVE_UNISTD_H */
#if	HAVE_STRING_H
#include <string.h>
#endif	/* HAVE_STRING_H */
#include <errno.h>
#include <sqlite3.h>

/*****************************************************************************
 *
 *	Program and Copyright
 *
 *****************************************************************************/
const char	Program[]   = "postal";
const char	Copyright[] =
	"Copyright (c) 2007 Kouichi ABE (WALL), All rights reserved.";

/*****************************************************************************
 *
 *	Macros and structures definition
 *
 *****************************************************************************/
/*
 * Numeric release version identifier:
 * MNNFFPPS: major minor fix patch status
 * The status nibble has one of the values 0 for development,
 * 1 to e for betas 1 to 14, and f for release.
 * The patch level is exactly that.
 */
#define	POSTAL_VERSION_NUMBER	0x10000000L
#define	POSTAL_VERSION		"Postal/1.0"
#define	POSTAL_VERSION_TEXT	POSTAL_VERSION " (2007/08/28)"
#define	POSTAL_VERSION_TEXT_LONG	"Postal 1.0, Tue, Aug 28 2007"

/*****************************************************************************/

#define	POSTCODE_DB	"/usr/local/share/postal/postcode.db"
#define	POSTCODE_LEN	(7)
#define	SQL_JAPAN \
	"SELECT prefecture,city,address FROM japan WHERE postcode=$POSTCODE;"
#define	SQL_OFFICE \
	"SELECT prefecture,city,address,name FROM office WHERE postcode=$POSTCODE;"

/*****************************************************************************
 *
 *	Local functions declaration
 *
 *****************************************************************************/
static void	version(void);
static void	usage(void);

static int	postal(const char * dbfile, const char * postcode);
static int	lookup(sqlite3 * conn, const char * sql, const char * postcode,
		       char ** address, char ** name);

/*****************************************************************************
 *
 *	Functions definition
 *
 *****************************************************************************/
static void
version(void)
{
  fprintf(stderr, "%s\n%s\n\n", POSTAL_VERSION_TEXT, Copyright);
  exit(64);
}

static void
usage(void)
{
  fprintf(stderr, "usage: %s [-d <database>] <postcode>\n", Program);
  fprintf(stderr, "\n    [ example ]\n");
  fprintf(stderr, "        %s 1006090\n", Program);
  fprintf(stderr, "        %s -d postcode.db 1008798\n\n", Program);
  exit(64);
}

static int
postal(dbfile, postcode)
	const char *	dbfile;
	const char *	postcode;
{
  static const char	digits[] = "0123456789";
  size_t		len;
  size_t		pos;
  sqlite3 *		conn;
  char *		address;
  int			status = -1;

  len = strlen(postcode);
  if (len != POSTCODE_LEN) {
    fprintf(stderr, "The length of %s is not 7.\n", postcode);
    return -1;
  }
  pos = strspn(postcode, digits);
  if (pos != POSTCODE_LEN) {
    fprintf(stderr, "%s has invalid character.\n", postcode);
    return -1;
  }

  status = sqlite3_open(dbfile, &conn);
  if (status != SQLITE_OK) {
    fprintf(stderr, "%s: %s\n", dbfile, sqlite3_errmsg(conn));
    return -1;
  }

  status = lookup(conn, SQL_JAPAN, postcode, &address, NULL);
  if (status == 0) {
    fprintf(stdout, "%s: %s\n", postcode, address);
    free(address);
  }
  else {
    char *	name;

    status = lookup(conn, SQL_OFFICE, postcode, &address, &name);
    if (status == 0) {
      fprintf(stdout, "%s: %s %s\n", postcode, address, name);
      free(address);
      free(name);
    }
  }

  sqlite3_close(conn);

  return status;
}

static int
lookup(conn, sql, postcode, address, name)
	sqlite3 *	conn;
	const char *	sql;
	const char *	postcode;
	char **		address;
	char **		name;
{
  sqlite3_stmt *	res;
  int			status;

  status = sqlite3_prepare(conn, sql, (int)strlen(sql), &res, NULL);
  if (status != SQLITE_OK) {
    return -1;
  }
  status = sqlite3_bind_text(res, 1, postcode, POSTCODE_LEN, SQLITE_TRANSIENT);
  if (status != SQLITE_OK) {
    status = -1;
    goto done;
  }
  switch (sqlite3_step(res)) {
    case SQLITE_ROW: {
	int	n;

	n = sqlite3_column_count(res);
	if (n >= 3) {
	  asprintf(address, "%s%s%s",
		   sqlite3_column_text(res, 0),
		   sqlite3_column_text(res, 1),
		   sqlite3_column_text(res, 2));
	  if (*address != NULL) {
	    status = 0;
	  }
	  if (n == 4 && name != NULL) {
	    *name = strdup(sqlite3_column_text(res, 3));
	  }
	}
      }
      break;
    case SQLITE_DONE:
      status = 1;
      break;
    default:
      status = -1;
      break;
  }

done:
  sqlite3_finalize(res);

  return status;
}

int
main(int argc, char * argv[])
{
  register int	ch;
  const char *	dbfile = POSTCODE_DB;
  int		status;

  /* parse options */
  while ((ch = getopt(argc, argv, "d:hv?")) != -1) {
    switch (ch) {
      case 'd': dbfile = optarg; break;
      case 'v': version();
      case 'h':
      default:
	usage();
    }
  }
  argc -= optind;
  argv += optind;

  if (argc == 0) { usage(); }

  status = postal(dbfile, argv[0]);

  return status;
}
