#ifdef RCS
static char rcsid[]="$Id: explain.c,v 1.1.1.1 2000/11/13 02:42:41 holsta Exp $";
#endif
/******************************************************************************
 *                    Internetting Cooperating Programmers
 * ----------------------------------------------------------------------------
 *
 *  ____    PROJECT
 * |  _ \  __ _ _ __   ___ ___ _ __ 
 * | | | |/ _` | '_ \ / __/ _ \ '__|
 * | |_| | (_| | | | | (_|  __/ |   
 * |____/ \__,_|_| |_|\___\___|_|   the IRC bot
 *
 * All files in this archive are subject to the GNU General Public License.
 *
 * $Source: /cvsroot/dancer/dancer/src/explain.c,v $
 * $Revision: 1.1.1.1 $
 * $Date: 2000/11/13 02:42:41 $
 * $Author: holsta $
 * $State: Exp $
 * $Locker:  $
 *
 * ---------------------------------------------------------------------------
 *****************************************************************************/

#include "dancer.h"
#include "trio.h"
#include "strio.h"
#include "explain.h"
#include "list.h"
#include "function.h"

#include <stddef.h> /* offsetof */

#ifndef offsetof
# define offsetof(type, member)  ((int)(&((type *)0L)->member))
#endif

extern char expfile[];
extern char *errfrom;

typedef struct Explainstruct {
  struct Header h;
  char *word;
  char *explain;
} itemexplain;

itemexplain *explainHead = NULL;
int numExplains = 0;


/* --- ExplainFind ------------------------------------------------ */

itemexplain *ExplainFind(char *word)
{
  itemexplain *e;

  snapshot;
  for (e = First(explainHead); e; e = Next(e)) {
    if (StrEqual(e->word, word))
      return e;
  }
  return NULL;
}

/* --- ExplainAddReal --------------------------------------------- */

itemexplain *ExplainAddReal(char *word, char *explain)
{
  itemexplain *e;

  snapshot;
  e = NewEntry(itemexplain);
  if (e) {
    e->word = StrDuplicate(word);
    if (e->word) {
      e->explain = StrDuplicate(explain);
      if (e->explain) {
        InsertLast(explainHead, e);
        numExplains++;
        return e;
      }
      StrFree(e->word);
    }
    free(e);
  }
  return NULL;
}

/* --- ExplainAddItem --------------------------------------------- */

void ExplainAddItem(char *line)
{
  char words[MIDBUFFER], explain[BIGBUFFER];
  char *word;

  snapshot;
  if (2 == StrScan(line, "%"MIDBUFFERTXT"[^ =]=%"BIGBUFFERTXT"[^\n]",
                   words, explain)) {
    for (word = StrTokenize(words, ","); word; word = StrTokenize(NULL, ",")) {
      if (NULL == ExplainFind(word))
        if (NULL == ExplainAddReal(word, explain))
          return; /* Not enough memory */
    }
  }
}

/* --- ExplainSort ------------------------------------------------ */

void ExplainSort(void)
{
  snapshot;
  SortList(explainHead, offsetof(itemexplain, word));
}

/* --- ExplainLoad ------------------------------------------------ */

bool ExplainLoad(char *filename)
{
  char line[MAXLINE];
  FILE *f;

  snapshot;
  if ((NULL == explainHead) || (NULL == filename) || (NIL == filename[0]))
    return FALSE;

  f = fopen(filename, "r");
  if (f) {
    while (fgets(line, sizeof(line), f)) {
      switch (line[0]) {

        case '#':
        case '\n':
          break;

        case ' ':
          ExplainAddItem(line + 1);
          break;

        default:
          break;
      }
    }
    fclose(f);

    ExplainSort();
    return TRUE;
  }
  return FALSE;
}

/* --- FreeExplain ------------------------------------------------ */

void FreeExplain(void *v)
{
  itemexplain *e;

  snapshot;
  e = (itemexplain *)v;
  if (e) {
    if (e->word)
      StrFree(e->word);
    if (e->explain)
      StrFree(e->explain);
    numExplains--;
  }
}

/* --- ExplainInit ------------------------------------------------ */

void ExplainInit(void)
{
  snapshot;
  explainHead = NewList(itemexplain);
  ExplainLoad(expfile);
}

/* --- ExplainCleanup --------------------------------------------- */

void ExplainCleanup(void)
{
  snapshot;
  DeleteList(explainHead, FreeExplain);
}

/* --- ExplainReload ---------------------------------------------- */

void ExplainReload(char *filename)
{
  snapshot;
  FlushList(explainHead, FreeExplain);
  ExplainLoad(filename);
}

/* --- ExplainSave ------------------------------------------------ */

bool ExplainSave(char *filename)
{
  char tempfile[MIDBUFFER];
  bool ok = TRUE;
  itemexplain *e;
  FILE *f;

  snapshot;
  if ((NULL == filename) || (NIL == filename[0]))
    return FALSE;

  StrFormatMax(tempfile, sizeof(tempfile), "%s~", filename);

  f = fopen(tempfile, "w");
  if (f) {
    if (0 > fprintf(f, "# Dancer explain list version: " VERSIONMSG "\n")) {
      ok = FALSE;
    }
    else {
      for (e = First(explainHead); e; e = Next(e)) {
        if (0 > fprintf(f, " %s=%s\n", e->word, e->explain)) {
          ok = FALSE;
          break;
        }
      }
    }
    fclose(f);

    if (ok)
      rename(tempfile, filename);
  }
  return ok;
}

/* --- ExplainAdd ------------------------------------------------- */

void ExplainAdd(char *from, char *line)
{
  char words[BIGBUFFER], explain[BIGBUFFER];
  char *word;
  char *exists = NULL;
  bool changed = FALSE;

  snapshot;
  if (2 == StrScan(line, "%"BIGBUFFERTXT"[^ =]%*[ =]%"BIGBUFFERTXT"[^\n]",
                   words, explain)) {
    for (word = StrTokenize(words, ","); word; word = StrTokenize(NULL, ",")) {
      if (NULL == ExplainFind(word)) {
        if (ExplainAddReal(word, explain))
          changed = TRUE;
      }
      else {
        if (NULL == exists)
          exists = word;
      }
    }

    if (changed) {
      ExplainSort();
      ExplainSave(expfile);
    }

    if (exists)
      Sendf(from, GetText(msg_already_explained), exists);
    else if (changed)
      Send(from, GetText(msg_added_explaination));
  }
  else
    CmdSyntax(errfrom, "EXPADD");
}

/* --- ExplainDel ------------------------------------------------- */

void ExplainDel(char *from, char *line)
{
  char words[BIGBUFFER];
  char *word;
  char *first = NULL, *not_exists = NULL;
  bool changed = FALSE;
  itemexplain *e;

  snapshot;
  if (1 == StrScan(line, "%"BIGBUFFERTXT"s", words)) {
    for (word = StrTokenize(words, ","); word; word = StrTokenize(NULL, ",")) {
      e = ExplainFind(word);
      if (e) {
        if (NULL == first)
          first = word;
        DeleteEntry(explainHead, e, FreeExplain);
        changed = TRUE;
      }
      else {
        if (NULL == not_exists)
          not_exists = word;
      }
    }

    if (changed)
      ExplainSave(expfile);

    if (not_exists)
      Sendf(errfrom, GetText(msg_explaination_was_not_found), not_exists);
    else if (first)
      Sendf(from, GetText(msg_removed_explaination), first);
  }
  else
    CmdSyntax(errfrom, "EXPDEL");
}

/* --- Explain ---------------------------------------------------- */

void Explain(char *from, char *line)
{
  extern bool public;
  extern itemopt option;
  char buffer[MIDBUFFER], match[MIDBUFFER];
  bool search;
  int matches = 0;
  itemexplain *e;
  itemexplain *firsthit = NULL;

  snapshot;
  search = (GetOption(line) && ('S' == toupper(option.copt)));
  line = option.newpos;

  if (1 == StrScan(line, "%"MIDBUFFERTXT"s", buffer)) {
    if (search)
      StrFormatMax(match, sizeof(match), "*%s*", buffer);
    else
      StrCopyMax(match, sizeof(match), buffer);

    if (public && StrIndex(match, '*')) {
      Send(errfrom, GetText(msg_no_public_wildcards));
    }
    else {
      buffer[0] = (char)0;
      for (e = First(explainHead); e; e = Next(e)) {
        if (StrMatch(search ? e->explain : e->word, match)) {
          if (1 == ++matches)
            firsthit = e;

          if ((StrLength(buffer) + StrLength(e->word)) >= sizeof(buffer)) {
            Send(from, buffer);
            buffer[0] = (char)0;
          }

          StrFormatAppendMax(buffer, sizeof(buffer), "%s ", e->word);
        }
      }

      if (0 == matches)
        Send(errfrom, GetText(msg_no_explaination));
      else if (1 == matches)
        Sendf(from, "'%s' %s", firsthit->word, firsthit->explain);
      else if (buffer[0])
        Send(from, buffer);
    }
  }
  else
    CmdSyntax(errfrom, "EXPLAIN");
}

/* ---------------------------------------------------------------- */

/*
 * Explaination keywords. Works a bit like 'man -k'
 * Certain explainations can be logically grouped.
 *
 * Example:
 *
 *   'ircII' and 'mIRC' may both belong to the group 'client', and
 *   'ircII' may also belong to the group 'unix'.
 *
 *   "EXPLAIN -k client" will result in "ircII, mIRC"
 *   "EXPLAIN -k unix" in "ircII, linux, solaris, sunos"
 *
 * FindExplainKey() first searches for keywords, if none were found
 * it searches the usual words.
 *
 * Represented as 'word key1 key2=description'. Internal representation
 * is word = 'word key key'. Extract 'word' with sscanf("[^= \n]) on
 * normal searches.
 */


syntax highlighted by Code2HTML, v. 0.9.1