/* -*- Mode: C -*-
  ======================================================================
  FILE: icalerror.c
  CREATOR: eric 16 May 1999
  
  $Id: icalerror.c,v 1.22 2008-01-15 23:17:40 dothebart Exp $
  $Locker:  $
    

 (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org>
     http://www.softwarestudio.org

 This program is free software; you can redistribute it and/or modify
 it under the terms of either: 

    The LGPL as published by the Free Software Foundation, version
    2.1, available at: http://www.fsf.org/copyleft/lesser.html

  Or:

    The Mozilla Public License Version 1.0. You may obtain a copy of
    the License at http://www.mozilla.org/MPL/

  The original code is icalerror.c

 ======================================================================*/


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif

#include <stdlib.h>		/* for malloc() */
#include <string.h>		/* for strcmp */
#include "icalerror.h"

#ifdef HAVE_PTHREAD
#include <pthread.h>

static pthread_key_t  icalerrno_key;
static pthread_once_t icalerrno_key_once = PTHREAD_ONCE_INIT;

static void icalerrno_destroy(void* buf) {
  free(buf);
  pthread_setspecific(icalerrno_key, NULL);
}

static void icalerrno_key_alloc(void) {
  pthread_key_create(&icalerrno_key, icalerrno_destroy);
}

icalerrorenum *icalerrno_return(void) {
  icalerrorenum *_errno;

  pthread_once(&icalerrno_key_once, icalerrno_key_alloc);
  
  _errno = (icalerrorenum*) pthread_getspecific(icalerrno_key);

  if (!_errno) {
    _errno = malloc(sizeof(icalerrorenum));
    *_errno = ICAL_NO_ERROR;
    pthread_setspecific(icalerrno_key, _errno);
  }
  return _errno;
}

#else

static icalerrorenum icalerrno_storage = ICAL_NO_ERROR;

icalerrorenum *icalerrno_return(void) {
   return &icalerrno_storage;
}

#endif


static int foo;

void icalerror_stop_here(void)
{
    foo++; /* Keep optimizers from removing routine */
}

void icalerror_crash_here(void)
{
    int *p=0;
    *p = 1;

    assert( *p);
}

#ifdef ICAL_SETERROR_ISFUNC
void icalerror_set_errno(icalerrorenum x) 
{
    icalerrno = x; 
    if(icalerror_get_error_state(x)==ICAL_ERROR_FATAL || 
       (icalerror_get_error_state(x)==ICAL_ERROR_DEFAULT && 
        icalerror_errors_are_fatal == 1 )){ 
        icalerror_warn(icalerror_strerror(x)); 
	ical_bt();
        assert(0); 
    } 

}
#endif 

void icalerror_clear_errno() {
    
    icalerrno = ICAL_NO_ERROR;
}

#if ICAL_ERRORS_ARE_FATAL == 1
int icalerror_errors_are_fatal = 1;
#else
int icalerror_errors_are_fatal = 0;
#endif

struct icalerror_state {
    icalerrorenum error;
    icalerrorstate state; 
};

static struct icalerror_state error_state_map[] = 
{ 
    { ICAL_BADARG_ERROR,ICAL_ERROR_DEFAULT},
    { ICAL_NEWFAILED_ERROR,ICAL_ERROR_DEFAULT},
    { ICAL_ALLOCATION_ERROR,ICAL_ERROR_DEFAULT},
    { ICAL_MALFORMEDDATA_ERROR,ICAL_ERROR_DEFAULT}, 
    { ICAL_PARSE_ERROR,ICAL_ERROR_DEFAULT},
    { ICAL_INTERNAL_ERROR,ICAL_ERROR_DEFAULT}, 
    { ICAL_FILE_ERROR,ICAL_ERROR_DEFAULT},
    { ICAL_USAGE_ERROR,ICAL_ERROR_DEFAULT},
    { ICAL_UNIMPLEMENTED_ERROR,ICAL_ERROR_DEFAULT},
    { ICAL_UNKNOWN_ERROR,ICAL_ERROR_DEFAULT},
    { ICAL_NO_ERROR,ICAL_ERROR_DEFAULT}

};

struct icalerror_string_map {
    const char* str;
    icalerrorenum error;
    char name[160];
};

static const struct icalerror_string_map string_map[] = 
{
    {"BADARG",ICAL_BADARG_ERROR,"BADARG: Bad argument to function"},
    { "NEWFAILED",ICAL_NEWFAILED_ERROR,"NEWFAILED: Failed to create a new object via a *_new() routine"},
    { "ALLOCATION",ICAL_ALLOCATION_ERROR,"ALLOCATION: Failed to allocate new memory"},
    {"MALFORMEDDATA",ICAL_MALFORMEDDATA_ERROR,"MALFORMEDDATA: An input string was not correctly formed or a component has missing or extra properties"},
    { "PARSE",ICAL_PARSE_ERROR,"PARSE: Failed to parse a part of an iCal component"},
    {"INTERNAL",ICAL_INTERNAL_ERROR,"INTERNAL: Random internal error. This indicates an error in the library code, not an error in use"}, 
    { "FILE",ICAL_FILE_ERROR,"FILE: An operation on a file failed. Check errno for more detail."},
    { "USAGE",ICAL_USAGE_ERROR,"USAGE: Failed to propertyl sequence calls to a set of interfaces"},
    { "UNIMPLEMENTED",ICAL_UNIMPLEMENTED_ERROR,"UNIMPLEMENTED: This feature has not been implemented"},
    { "NO",ICAL_NO_ERROR,"NO: No error"},
    {"UNKNOWN",ICAL_UNKNOWN_ERROR,"UNKNOWN: Unknown error type -- icalerror_strerror() was probably given bad input"}
};


icalerrorenum icalerror_error_from_string(const char* str){
    int i;

    for( i = 0; string_map[i].error != ICAL_UNKNOWN_ERROR; i++)
        if (strcmp(string_map[i].str,str) == 0)
	    break;

    return string_map[i].error;
}

icalerrorstate icalerror_supress(const char* error){

    icalerrorenum e = icalerror_error_from_string(error);
    icalerrorstate es;

     if (e == ICAL_NO_ERROR){
        return ICAL_ERROR_UNKNOWN;
    }


    es = icalerror_get_error_state(e);
    icalerror_set_error_state(e,ICAL_ERROR_NONFATAL);

    return es;
}

const char* icalerror_perror(void)
{
    return icalerror_strerror(icalerrno);
}

void icalerror_restore(const char* error, icalerrorstate es){


    icalerrorenum e = icalerror_error_from_string(error);

    if (e != ICAL_NO_ERROR){
        icalerror_set_error_state(e,es);
    }

}



void icalerror_set_error_state( icalerrorenum error, 
				icalerrorstate state)
{
    int i;

    for(i = 0; error_state_map[i].error!= ICAL_NO_ERROR;i++){
	if(error_state_map[i].error == error){
	    error_state_map[i].state = state; 	
	}
    }
}

icalerrorstate icalerror_get_error_state( icalerrorenum error)
{
    int i;

    for(i = 0; error_state_map[i].error!= ICAL_NO_ERROR;i++){
	if(error_state_map[i].error == error){
	    return error_state_map[i].state; 	
	}
    }

    return ICAL_ERROR_UNKNOWN;	
}




const char* icalerror_strerror(icalerrorenum e) {

    int i;

    for (i=0; string_map[i].error != ICAL_UNKNOWN_ERROR; i++) {
	if (string_map[i].error == e) {
	    return string_map[i].name;
	}
    }

    return string_map[i].name; /* Return string for ICAL_UNKNOWN_ERROR*/
    
}


void ical_bt(void)
{
#ifdef HAVE_BACKTRACE
        void *stack_frames[50];
        size_t size, i;
        char **strings;

        size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
        strings = backtrace_symbols(stack_frames, size);
        for (i = 0; i < size; i++) {
                if (strings != NULL)
                        fprintf(stderr, "%s\n", strings[i]);
                else
                        fprintf(stderr, "%p\n", stack_frames[i]);
        }
        free(strings);
#endif
}

