/**********************************************************************
 *
 * tdbset -- add or replace a single value from the TDB database
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * Copyright (C) 1999  The New Zealand Digital Library Project
 *
 * 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; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#if defined(GSDL_USE_OBJECTSPACE)
#include <ospace\std\iostream>
#elif defined(GSDL_USE_IOS_H)
#include <iostream.h>
#else
#include <iostream>
#endif

#include <cstdlib>
#include <cstring>

#include <fcntl.h>
#include "tdb.h"

// use the standard namespace
#if !defined (GSDL_NAMESPACE_BROKEN)
#if defined(GSDL_USE_OBJECTSPACE)
using namespace ospace::std;
#else
using namespace std;
#endif
#endif

/**
 */
void
printUsage (char *program_name)
{
  cerr << "usage: " << program_name << " <database_path> <key> [<value>] [append]" << endl;
  cerr << "\t- if no value is given then the pair indicated by the key is removed" << endl;
  cerr << "\t- if a value is given followed by 'append' then the value is " << endl;
  cerr << "\t  added to the existing entry rather than overwriting it" << endl << endl;
}
/** printUsage() **/

/**
 */
int
main (int argc, char *argv[])
{
  // sanity check
  if ((argc < 3) || (argc>5))
  {
    printUsage(argv[0]);
    exit (-1);
  }

  // open the collection to the database - read write this time.
  char *dbname = argv[1];
  int hash_size = 0;
  int tdb_flags = TDB_DEFAULT; // Default = 0
  int tdb_store_flags = TDB_DEFAULT; // used later when storing
  int open_flags = O_RDWR | O_CREAT;
  TDB_CONTEXT *tdb = tdb_open(dbname, hash_size, tdb_flags, open_flags, 0664);
  if (!tdb)
  {
    cerr << "couldn't create " << dbname << endl;
    exit (0);
  }

  // create the object to represent the key we are altering
  TDB_DATA key_data;
  key_data.dsize = strlen(argv[2]);
  key_data.dptr = (unsigned char*)argv[2];

  // if there are a fistfull of arguments we may be doing a mystical append
  if(argc > 4)
  {
    // if there are five arguments and the last one is append, then we
    // concatenate the value onto whatever is already in this key
    // value pair
    if (strcmp(argv[4], "append") == 0)
    {
      // get old value, then top up
      TDB_DATA original_value_data = tdb_fetch(tdb, key_data);

      // append orig_content with argv[3]
      TDB_DATA concat_value_data;
      concat_value_data.dsize = original_value_data.dsize + strlen(argv[3]);
      //  remember to leave room for \0 at end
      char* concat_data = new char[concat_value_data.dsize + 1];
      // if the value already has content, append to the end of it
      if (original_value_data.dsize > 0)
      {
	strncpy(concat_data, (const char*)original_value_data.dptr, original_value_data.dsize);
        // ensures \0 at end
	strcpy(&concat_data[original_value_data.dsize], argv[3]);
      }
      // otherwise this is the first time key has been used
      else
      {
        // ensures \0 at end
	strcpy(concat_data, argv[3]);
      }
      concat_value_data.dptr = (unsigned char*)concat_data;

      if(tdb_store(tdb, key_data, concat_value_data, tdb_store_flags) != 0)
      {
        cerr << "failed to set [" << argv[2] << "] to '" << concat_data << "'" << endl;
      }

      // I'm responsible for freeing some memory
      delete [] concat_data;
      free(original_value_data.dptr);
    }
    // five arguments but the last isn't append. No sir, I don't like it
    else
    {
      printUsage(argv[0]);
    }
  }
  // normal case - we've been given a key and a value to update it with
  else if (argc == 4)
  {
    TDB_DATA value_data;
    value_data.dsize = strlen(argv[3]);
    value_data.dptr = (unsigned char*)argv[3];

    if(tdb_store(tdb, key_data, value_data, tdb_store_flags) != 0)
    {
      cerr << "failed to set [" << argv[2] << "] to '" << argv[3] << "'" << endl;
    }
  }
  // only given a key? that's a deleting
  else
  {
    if(tdb_delete(tdb, key_data) != 0)
    {
      cerr << "failed to remove [" << argv[2] << "]" << endl;
    }
  }

  tdb_close(tdb);

  return 0;
}
