/*
 * WebGet: Acorn Web Fetcher and rewriter
 * Ruleset handling code
 *
 *  Joseph Heenan, 1998
 * All rights reserved.
 *
 * This source is not for release
 *
 * $Log: ruleset,v $
 * Revision 1.1  1998/08/09 15:07:00  joseph
 * Added include/exclude lists for URLs
 *
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "flex.h"
#include "wimplib.h"
#include "wimpclib.h"

#include "debug.h"
#include "ruleset.h"

#define WORDALIGN(x) ( (x+3) & ~3 )


typedef struct ruleset_str
{
  int size; /* size of this entry (rounded up to a whole number of words) */
  int uses; /* number of URL's using this ruleset */
  int includefiles; /* offset from start of struct to includefiles (null terminated string) */
  int excludefiles; /* offset from start of struct to excludefiles (null terminated string) */
  /* include file, excludefiles, etc */
} ruleset_t;

static void *rulesets = NULL;

int ruleset_init( void )
{
  if ( !flex_alloc( &rulesets, 0 ) )
    return -1;

  return 0;
}

/* loops through rulesets.
 * if a match is found, adds one to uses and returns it's number.
 * in no match found, ruleset is created in lowest numbered slot available
 */
int ruleset_add( const char *includefiles, const char *excludefiles )
{
  int firstfree = -1;
  int freeoffset = 0;
  int no, size, cursize = 0;
  ruleset_t *rule = rulesets;

  debug_printf(( "Adding ruleset '%s', '%s' : \n", includefiles, excludefiles ));

  if ( rulesets )
  {
    ruleset_t *end = (ruleset_t *) ( (int)rulesets + flex_size( &rulesets ) );
    /* search through current rules seeing if this rule is already present */
    no = 0;
    while ( rule < end )
    {
      debug_printf(("rule = %p, end = %p : Checking rule %d, size = %d.\n",rule,end,no,rule->size));
      if ( !strcmp( ((char *)rule) + rule->includefiles, includefiles ) &&
           !strcmp( ((char *)rule) + rule->excludefiles, excludefiles ) )
      {
        debug_printf(("Already present, no %d\n",no));
        return no; /* entry already present */
      }

      if ( rule->uses == 0 && firstfree == -1 )
      {
        freeoffset = (int)rule - (int)rulesets;
        firstfree  = no;
        cursize    = rule->size;
      }
      rule = (ruleset_t *) ( ((int)rule) + rule->size );
      no++;
    }
  }


  if ( firstfree == -1 )
    firstfree = 0;
  debug_printf(("First free index = %d at %d. Cursize = %d\n",firstfree, freeoffset, flex_size(&rulesets)));

  size = WORDALIGN( strlen( includefiles ) + strlen( excludefiles ) + 2 + sizeof(ruleset_t) );

  /* rule not present - claim memory */
  if ( !flex_midextend( &rulesets, freeoffset, size - cursize ) )
  {
    E_REPORT( "Out of memory, could not add the ruleset!" );
    return -1;
  }
  debug_printf(("Expanded flex area by %d at %d, size now %d.\n", size-cursize, freeoffset, flex_size(&rulesets)));

  /* add rule */
  rule = (ruleset_t *) ( ((int)rulesets) + freeoffset  );
  rule->size         = size;
  rule->uses         = 0; /* url must be 'used' immediately after calling this function */
  rule->includefiles = sizeof(ruleset_t);
  rule->excludefiles = rule->includefiles + strlen( includefiles ) + 1;

  strcpy( ((char *)rule) + rule->includefiles, includefiles );
  strcpy( ((char *)rule) + rule->excludefiles, excludefiles );

  return firstfree; /* return new index */
}

static ruleset_t *ruleset__find( int ruleset )
{
  ruleset_t *rule, *end;
  int no;

  if ( !rulesets )
  {
    debug_printf(("Error : could not find ruleset %d\n", ruleset ));
    return NULL;
  }

  rule = rulesets;
  end  = (ruleset_t *) ( (int)rulesets + flex_size( &rulesets ) );
  no   = 0;

  while ( ruleset != no && rule < end )
  {
    rule = (ruleset_t *) ( ((int)rule) + rule->size );
    no++;
  }
  if ( ruleset == no )
    return rule;

  debug_printf(("Error : could not find ruleset %d\n", ruleset ));

  return NULL;
}


/* Reads the contents of a rule
 *
 * NB. Contents are valid only directly after call
 */
rule_t *ruleset_recall( int ruleset )
{
  static rule_t retrule;
  ruleset_t *rule = ruleset__find( ruleset );

  if ( !rule )
    return NULL;

  retrule.includefiles = ((char *)rule) + rule->includefiles;
  retrule.excludefiles = ((char *)rule) + rule->excludefiles;

  debug_printf(( "Recalling ruleset %d, inc = '%s', ex = '%s'\n",
                 ruleset,retrule.includefiles, retrule.excludefiles ));

  return &retrule;
}

void ruleset_use( int ruleset )
{
  ruleset_t *rule = ruleset__find( ruleset );
  if ( !rule ) return;
  rule->uses += 1;
  debug_printf(("ruleset_use: %d now has %d uses\n",ruleset,rule->uses));
}

void ruleset_unuse( int ruleset )
{
  ruleset_t *rule = ruleset__find( ruleset );
  if ( !rule ) return;

  rule->uses -= 1;
  debug_printf(("ruleset_unuse: %d now has %d uses\n",ruleset,rule->uses));
  if ( rule->uses == 0 )
  {
    int size = rule->size;
    rule->size = sizeof(ruleset_t); /* will have moved after flex call... */
    debug_printf(("ruleset_unuse: %d free, changing size by %d\n", ruleset, sizeof(ruleset_t) - size ));
    flex_midextend( &rulesets, (int)rule + size - (int)rulesets, sizeof(ruleset_t) - size );
  }
}
