/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/*
 * $XConsortium: user_base.cc /main/8 1996/08/15 14:13:12 cde-hal $
 *
 * Copyright (c) 1992 HAL Computer Systems International, Ltd.
 * All rights reserved.  Unpublished -- rights reserved under
 * the Copyright Laws of the United States.  USE OF A COPYRIGHT
 * NOTICE IS PRECAUTIONARY ONLY AND DOES NOT IMPLY PUBLICATION
 * OR DISCLOSURE.
 * 
 * THIS SOFTWARE CONTAINS CONFIDENTIAL INFORMATION AND TRADE
 * SECRETS OF HAL COMPUTER SYSTEMS INTERNATIONAL, LTD.  USE,
 * DISCLOSURE, OR REPRODUCTION IS PROHIBITED WITHOUT THE
 * PRIOR EXPRESS WRITTEN PERMISSION OF HAL COMPUTER SYSTEMS
 * INTERNATIONAL, LTD.
 * 
 *                         RESTRICTED RIGHTS LEGEND
 * Use, duplication, or disclosure by the Government is subject
 * to the restrictions as set forth in subparagraph (c)(l)(ii)
 * of the Rights in Technical Data and Computer Software clause
 * at DFARS 252.227-7013.
 *
 *          HAL COMPUTER SYSTEMS INTERNATIONAL, LTD.
 *                  1315 Dell Avenue
 *                  Campbell, CA  95008
 * 
 */



#include "oliasdb/user_base.h"
#include "oliasdb/mark.h"
#include "utility/debug.h"
#include "misc/unique_id.h"

#define LOCK_DIR "lock"
#define AT_LOCK "at_lock"
#define W_LOCK "w_lock"
#define ACCESS_INFO "access_info"

extern void_ptr_array g_store_array;
extern Boolean g_transaction_on;

extern int g_mode_8_3;

user_base::user_base( const char* spec_path, rw_flag_t rw) : 
   base(0), backup_file(0), rw_flag(rw), first_desc_ptr(0),
   spec_name(spec_path)
{
   g_mode_8_3 = 1;
   int len;

   f_obj_dict = new object_dict;

   char* path = getenv("DTINFO_USER_DB") ;

   if ( path == 0 ) {
       char* home = getenv("HOME");
       char* lang = getenv("LANG");

       if ( home == 0 )
          throw(stringException("HOME unspecified"));
       if ( lang == 0 )
          throw(stringException("LANG unknown"));

       path = form("%s/.dt/dtinfo/%s", home, lang);
   }

   char* name = getenv("USER");

   if ( name == 0 )
      name = (char*)"";

   len = MIN(strlen(path), PATHSIZ - 1);
   *((char *) memcpy(base_path, path, len) + len) = '\0';
   len = MIN(strlen(name), PATHSIZ - 1);
   *((char *) memcpy(base_name, name, len) + len) = '\0';
   len = MIN(strlen(""), PATHSIZ - 1);
   *((char *) memcpy(base_desc, "", len) + len) = '\0';

   _init();

}


user_base::user_base( const char* base_dir, 
                      const char* base_nm,
                      const char* base_ds,
                      const char* spec_path,
		      rw_flag_t rw 
                    ) : 
	base(0, 0, 0, base_dir, base_nm, base_ds, ""), backup_file(0),
	rw_flag(rw), checking_status(SUCC), first_desc_ptr(0),
	spec_name(spec_path)
{
   g_mode_8_3 = 1;

   f_obj_dict = new object_dict;

   _init();
}

user_base::checking_status_t user_base::check_mode()
{
   mtry {

     switch ( rw_flag ) {
         case user_base::READ:
        {
           if ( exist_dir(base_path) == false ) {
              return user_base::FAIL;
           }
           break;
        }
         case user_base::WRITE:
        {
           if ( check_and_create_dir(base_path) == false ) {
              return user_base::FAIL;
           }
           break;
        }
     }
   }

   mcatch (systemException&, e)
      {
         return user_base::FAIL;
      }
   end_try;

   if ( exist_file(SCHEMA_FILE, base_path) == true ) 
      return user_base::SUCC;

   if ( exist_file(form("%s.%s", base_name, SCHEMA_FILE_SUFFIX), base_path) == true ) 
      return user_base::SUCC;

   return user_base::NO_BASE;
}

user_base::checking_status_t user_base::check_lock()
{
   char lock_dir[PATHSIZ+5];
   int len;

   snprintf(lock_dir, sizeof(lock_dir), "%s/%s", base_path, LOCK_DIR);

   if ( check_and_create_dir(lock_dir) == false ) {
      MESSAGE(cerr, form("no write permission to %s", lock_dir));
      return user_base::CREATE_LOCKFILE_FAIL;
   }

   len = MIN(strlen(atomic_lock_path), strlen(lock_dir) + strlen(AT_LOCK) + 1);
   *((char *) memcpy(atomic_lock_path,
		     form("%s/%s", lock_dir, AT_LOCK), len) + len) = '\0';
   len = MIN(strlen(write_lock_path), strlen(lock_dir) + strlen(W_LOCK) + 1);
   *((char *) memcpy(write_lock_path,
		     form("%s/%s", lock_dir, W_LOCK), len) + len) = '\0';
   len = MIN(strlen(ai_path), strlen(lock_dir) + strlen(ACCESS_INFO) + 1);
   *((char *) memcpy(ai_path,
		     form("%s/%s", lock_dir, ACCESS_INFO), len) + len) = '\0';

   char* ai_info = 0;

   switch (rw_flag) {
     case user_base::READ:

        if (
         read_lock(atomic_lock_path, write_lock_path, 
                   ai_path, access_info((char*)"read"), offset, ai_info
                  ) == false
           ) {
            if ( ai_info ) {
              debug(cerr, ai_info); 
              delete [] ai_info;
            }
            return user_base::RLOCK_FAIL;
        } else
           return user_base::SUCC;
        break;
 
     case user_base::WRITE:

         if (
          write_lock(atomic_lock_path, write_lock_path, 
                    ai_path, access_info((char*)"write"), ai_info
                   ) == false
         ) {
             if ( ai_info ) {
                debug(cerr, ai_info); 
             
//
//                int x = strlen(ai_info);
//                if ( x >= 5 && strncmp(ai_info + x - 5, "read", 4) == 0 )
//                {
//                   rw_flag = user_base::READ;
//                   MESSAGE(cerr, "write locking failed, try read mode");
//                   int ok = check_lock();
//                   delete ai_info;
//                   return ok;
//                }

                delete [] ai_info;
             }
             return user_base::WLOCK_FAIL;
         } else {
            return user_base::SUCC;
         }
        break;

     default:
         abort();
   }
}


void user_base::_init()
{
  set_mode(HEALTH, true);
  
  checking_status = check_mode();

  MESSAGE(cerr, "Initial mode");
  debug(cerr, checking_status);

  first_desc_ptr = 0;

  switch ( checking_status ) {

    case user_base::NO_BASE:

       if ( disk_space(base_path) < 10*KB ) {
           checking_status = user_base::FAIL;
           break;
       }

       mtry
         {
           ubase_trans.begin();

           define();

           ubase_trans.sync();
           ubase_trans.end();
   
         }

       mcatch (beginTransException &,e)
         {
// cases: can't open log or write size info to log
           checking_status = user_base::FAIL;
           clean_up();
           break;
         }
       mcatch (commitTransException &,e)
         {
// cases: bad log file, can't write to store, etc.
           checking_status = user_base::FAIL;
           clean_up();
           break;
         }
// cases: can't do define()
       mcatch (mmdbException &,e)
         {
           checking_status = user_base::FAIL;
           ubase_trans.rollback(); 
	   ubase_trans.abort();
           clean_up();
           break;

         }
       end_try;
       
       checking_status = check_mode();
       break;

    case user_base::SUCC:

       mtry {
          ubase_trans.begin();

          first_desc_ptr = f_obj_dict -> init_a_base(base_path, base_name);

          ubase_trans.end();
       }

       mcatch (mmdbException &,e) {
#ifdef DEBUG
	  fprintf(stderr, "mmdbException caught @ %s line:%d.\n",
						__FILE__, __LINE__);
#endif
          ubase_trans.abort();

          set_mode(HEALTH, false);
          checking_status = user_base::FAIL;
          break;
       } end_try;

       break;

    default:
       set_mode(HEALTH, false);
       break;
  }
  
#ifndef NO_DB_LOCK
  if ( checking_status == user_base::SUCC ) {
     checking_status = check_lock();
     debug(cerr, checking_status);
     if ( checking_status != user_base::SUCC ) {
       set_mode(HEALTH, false);
       return;
     }
  }
#endif
  

  MESSAGE(cerr, "Final mode"); debug(cerr, checking_status);

  switch ( checking_status ) {

    case user_base::SUCC:
      set_mode(HEALTH, true);
      MESSAGE(cerr, form("userbase \"%s\" available", base_name));
      break;

    default:
      set_mode(HEALTH, false);
      break;
  }

  return;
}

user_base::~user_base()
{
   if ( checking_status == user_base::SUCC &&
        exist_dir(form("%s/%s", base_path, LOCK_DIR)) == true ) 
   {

#ifndef NO_DB_LOCK
   switch (rw_flag) {
     case user_base::READ:
        read_unlock(atomic_lock_path, ai_path, offset);
        break;
     case user_base::WRITE:
        write_unlock(atomic_lock_path, write_lock_path, ai_path);
             );
        break;
   }
#endif
   }

  delete f_obj_dict;
}

void user_base::clean_up()
{
   abs_storage* store_ptr = 0;
   desc* desc_ptr = first_desc_ptr;

   while ( desc_ptr ) {
        store_ptr = desc_ptr -> get_store();

        if ( store_ptr ) {
          mtry {
/*
MESSAGE(cerr, "removing: ");
MESSAGE(cerr, store_ptr -> my_name());
*/
               store_ptr->remove();
          }
          mcatch (mmdbException &,e)
          {
          } end_try;
        }
        desc_ptr = desc_ptr -> get_next_desc();
   }

   if ( exist_file(SCHEMA_FILE, base_path) == true )
      del_file(SCHEMA_FILE, base_path);
}

Boolean user_base::define()
{
   int len;

   if ( check_and_create_dir(base_path) == false ) {
      throw(stringException(form("can't create %s", base_path))); 
   }

   char spec_file_path[PATHSIZ];

   //char* x = getenv("DTINFO_LIB");
   char* x = getenv("DTINFO_MARKSPECPATH");

   if ( x == 0 ) {
       len = MIN(strlen(spec_name), PATHSIZ - 1);
       *((char *) memcpy(spec_file_path, spec_name, len) + len) = '\0';
   } 
   else {
       len = MIN(strlen(x) + strlen(spec_name) + 1, PATHSIZ - 1);
       *((char *) memcpy(spec_file_path,
			 form("%s/%s", x, spec_name), len) + len) = '\0';
   }
   
   if (exist_file(spec_file_path) == false) {
       
      debug(cerr, spec_file_path);

      throw(stringException(
          form("missing %s. can't define user_base", spec_file_path)
                           )
           );
   }
 
/*
   char unique_nm[PATHSIZ];
   const char* uid;
   uid = unique_id();
   len = MIN(strlen(base_name) + strlen(uid) + 1, PATHSIZ - 1);
   *((char *) memcpy(unique_nm,
		     form("%s.%s", base_name, uid), len) + len) = '\0';
*/

   first_desc_ptr = 
     f_obj_dict -> init_a_base(spec_file_path, base_path, base_name);

   return true;
}

