/* 
   elmo - ELectronic Mail Operator

   Copyright (C) 2003, 2004 rzyjontko

   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; version 2.

   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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  

   ----------------------------------------------------------------------
   
   User-invoked functions are kept in exec_table.t file. This module
   contains routines used to execute them.
   
*/
/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#include <stdlib.h>
#include <string.h>

#include "exec.h"
#include "abook.h"
#include "attach.h"
#include "bayes.h"
#include "box_selection.h"
#include "cmd.h"
#include "choose.h"
#include "error.h"
#include "fetch.h"
#include "folder.h"
#include "mailreader.h"
#include "sender.h"
#include "xmalloc.h"
#include "help.h"
#include "gettext.h"
#include "mybox.h"
#include "wrapbox.h"
#include "read.h"
#include "interface.h"
#include "debug.h"
#include "smtp.h"
#include "stats.h"
#include "pgp.h"
#include "select.h"
#include "rarray.h"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/

#define FUN_COUNT (sizeof (fun_table) / sizeof (exec_t))

/****************************************************************************
 *    IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE DATA
 ****************************************************************************/

static exec_t fun_table[] = {
#include "exec_table.t"
};

static rarray_t *macros = NULL;

static exec_t *macro = NULL;

static int      size  = 0;
static int      count = 0;
static exec_t **table = NULL;

static char *default_description = NULL;

/****************************************************************************
 *    INTERFACE DATA
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTIONS
 ****************************************************************************/

static int
exec_cmp (const void *a, const void *b)
{
        exec_t *aa = (exec_t *) a;
        exec_t *bb = (exec_t *) b;

        return strcmp (aa->name, bb->name);
}



static void
execute (exec_t *exec)
{
        if (exec->fun)
                exec->fun ();
        hook_execute (exec->hook);
}



static void
destroy_exec (exec_t *exec)
{
        if (exec->hook){
                hook_destroy (exec->hook);
                xfree (exec->hook);
        }
}


static void
table_resize (void)
{
        if (count < size)
                return;

        size *= 2;
        table = xrealloc (table, size * sizeof (exec_t *));
}


static void
table_add (void)
{
        int lbound = 0;
        int rbound = count;
        int index;
        int ret;

        while (rbound > lbound){
                index = (rbound + lbound) / 2;
                ret   = strcmp (macro->name, table[index]->name);
                if (ret == 0){
                        error_ (0, _("function or macro %s already exists"),
                                macro->name);
                        return;
                }
                if (ret < 0)
                        rbound = ((index == rbound) ? rbound - 1 : index);
                else
                        lbound = ((index == lbound) ? lbound + 1 : index);
        }

        memmove (table + lbound + 1, table + lbound,
                 (count - lbound) * sizeof (exec_t *));

        table[lbound] = macro;
        count++;
}


static void
destroy_macros (void)
{
        int     i;
        exec_t *macro;
        
        if (macros == NULL)
                return;

        for (i = 0; i < macros->count; i++){
                macro = (exec_t *) macros->array[i];
                if (macro->desc != default_description)
                        xfree (macro->desc);
                xfree (macro->name);
                destroy_exec (macro);
                xfree (macro);
        }

        rarray_destroy (macros);
        macros = NULL;
}


/****************************************************************************
 *    INTERFACE FUNCTIONS
 ****************************************************************************/

void
exec_init (void)
{
        int i;

        qsort (fun_table, FUN_COUNT, sizeof (exec_t), exec_cmp);

        size  = FUN_COUNT * 3 / 2;
        count = FUN_COUNT;
        table = xmalloc (size * sizeof (exec_t *));
        
        for (i = 0; i < FUN_COUNT; i++){
                table[i] = fun_table + i;
        }

        default_description = _("user defined macro");
}



exec_t *
exec_lookup (const char *name)
{
        int lbound = 0;
        int rbound = count;
        int index;
        int ret;

        if (name == NULL)
                return NULL;
  
        while (rbound > lbound){
                index = (rbound + lbound) / 2;
                ret   = strcmp (name, table[index]->name);
                if (ret == 0){
                        return table[index];
                }
                if (ret < 0)
                        rbound = ((index == rbound) ? rbound - 1 : index);
                if (ret > 0)
                        lbound = ((index == lbound) ? lbound + 1 : index);
        }
        return NULL;
}



exec_t *
exec_lookup_fun (void (*fun)(void))
{
        int i;

        for (i = 0; i < count; i++){
                if (table[i]->fun == fun)
                        break;
        }
        if (i < count)
                return table[i];
        return NULL;
}




void
exec_run (exec_t *exec)
{
        if (exec)
                execute (exec);
}



void
exec_run_name (char *name)
{
        exec_t *exec;

        if (name == NULL || *name == '\0' || *name == '\n')
                return;
  
        exec = exec_lookup (name);

        if (exec)
                execute (exec);
        else
                error_ (0, _("undefined function %s"), name);
}



void
exec_free_resources (void)
{
        int i;
  
        for (i = 0; i < FUN_COUNT; i++){
                destroy_exec (fun_table + i);
        }

        if (table)
                xfree (table);
        table = NULL;

        destroy_macros ();
}



rstring_t *
exec_get_functions (const char *prefix)
{
        int        off = 0;
        int        i   = 0;
        rstring_t *result;

        while (prefix[off]){
                while (i < count && prefix[off] > table[i]->name[off])
                        i++;
                if (i == count)
                        return NULL;
                if (prefix[off] != table[i]->name[off])
                        return NULL;
                off++;
        }

        off--;
        result = rstring_create ();

        while (i < count
               && strstr (table[i]->name, prefix) == table[i]->name){

                rstring_add (result, table[i]->name);
                i++;
        }
        return result;
}


void
exec_new_macro (const char *name)
{
        macro       = xmalloc (sizeof (exec_t));

        macro->name = xstrdup (name);
        macro->fun  = NULL;
        macro->hook = NULL;
        macro->desc = default_description;
}


void
exec_macro_comment (char *comment)
{
        macro->desc = comment;
}


void
exec_macro_add_hook (const char *fun)
{
        exec_t *exec;

        exec = exec_lookup (fun);
        if (exec == NULL){
                error_ (0, _("undefined function %s"), fun);
                return;
        }

        if (exec->fun){
                if (macro->hook)
                        hook_add (macro->hook, exec->fun);
                else
                        macro->hook = hook_create (exec->fun);
        }
        else {
                error_ (0, _("macros (%s) cannot be used as parts of "
                             "other macros"), fun);
        }
}


void
exec_macro_commit (void)
{
        if (macros == NULL)
                macros = rarray_create ();
        
        rarray_add (macros, macro);
        table_resize ();
        table_add ();

        macro = NULL;
}

/****************************************************************************
 *    INTERFACE CLASS BODIES
 ****************************************************************************/
/****************************************************************************
 *
 *    END MODULE exec.c
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1