/* Tarlz - Archiver with multimember lzip compression
   Copyright (C) 2013-2023 Antonio Diaz Diaz.

   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, see <http://www.gnu.org/licenses/>.
*/

#define _FILE_OFFSET_BITS 64

#include <pthread.h>

#include "tarlz.h"
#include "common_mutex.h"


namespace {

int error_status = 0;

} // end namespace


void xinit_mutex( pthread_mutex_t * const mutex )
  {
  const int errcode = pthread_mutex_init( mutex, 0 );
  if( errcode )
    { show_error( "pthread_mutex_init", errcode ); exit_fail_mt(); }
  }

void xinit_cond( pthread_cond_t * const cond )
  {
  const int errcode = pthread_cond_init( cond, 0 );
  if( errcode )
    { show_error( "pthread_cond_init", errcode ); exit_fail_mt(); }
  }


void xdestroy_mutex( pthread_mutex_t * const mutex )
  {
  const int errcode = pthread_mutex_destroy( mutex );
  if( errcode )
    { show_error( "pthread_mutex_destroy", errcode ); exit_fail_mt(); }
  }

void xdestroy_cond( pthread_cond_t * const cond )
  {
  const int errcode = pthread_cond_destroy( cond );
  if( errcode )
    { show_error( "pthread_cond_destroy", errcode ); exit_fail_mt(); }
  }


void xlock( pthread_mutex_t * const mutex )
  {
  const int errcode = pthread_mutex_lock( mutex );
  if( errcode )
    { show_error( "pthread_mutex_lock", errcode ); exit_fail_mt(); }
  }


void xunlock( pthread_mutex_t * const mutex )
  {
  const int errcode = pthread_mutex_unlock( mutex );
  if( errcode )
    { show_error( "pthread_mutex_unlock", errcode ); exit_fail_mt(); }
  }


void xwait( pthread_cond_t * const cond, pthread_mutex_t * const mutex )
  {
  const int errcode = pthread_cond_wait( cond, mutex );
  if( errcode )
    { show_error( "pthread_cond_wait", errcode ); exit_fail_mt(); }
  }


void xsignal( pthread_cond_t * const cond )
  {
  const int errcode = pthread_cond_signal( cond );
  if( errcode )
    { show_error( "pthread_cond_signal", errcode ); exit_fail_mt(); }
  }


void xbroadcast( pthread_cond_t * const cond )
  {
  const int errcode = pthread_cond_broadcast( cond );
  if( errcode )
    { show_error( "pthread_cond_broadcast", errcode ); exit_fail_mt(); }
  }


/* This can be called from any thread, main thread or sub-threads alike,
   since they all call common helper functions that call exit_fail_mt()
   in case of an error.
*/
void exit_fail_mt( const int retval )
  {
  // calling 'exit' more than once results in undefined behavior
  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  pthread_mutex_lock( &mutex );		// ignore errors to avoid loop
  std::exit( retval );
  }


/* If msgp is null, print the message, else return the message in *msgp.
   If prefix is already in the list, print nothing or return empty *msgp.
   Return true if a message is printed or returned in *msgp. */
bool print_removed_prefix( const std::string & prefix,
                           std::string * const msgp )
  {
  // prevent two threads from modifying the list of prefixes at the same time
  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  static std::vector< std::string > prefixes;	// list of prefixes

  if( verbosity < 0 || prefix.empty() )
    { if( msgp ) msgp->clear(); return false; }
  xlock( &mutex );
  for( unsigned i = 0; i < prefixes.size(); ++i )
    if( prefixes[i] == prefix )
      { xunlock( &mutex ); if( msgp ) msgp->clear(); return false; }
  prefixes.push_back( prefix );
  std::string msg( "Removing leading '" ); msg += prefix;
  msg += "' from member names.";
  if( msgp ) *msgp = msg; else show_error( msg.c_str() );
  xunlock( &mutex );	// put here to prevent mixing calls to show_error
  return true;
  }


void set_error_status( const int retval )
  {
  // prevent two threads from modifying the error_status at the same time
  static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  xlock( &mutex );
  if( error_status < retval ) error_status = retval;
  xunlock( &mutex );
  }


int final_exit_status( int retval, const bool show_msg )
  {
  if( retval == 0 && error_status )
    { if( show_msg )
        show_error( "Exiting with failure status due to previous errors." );
      retval = error_status; }
  return retval;
  }
