/*
 *  ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
 *  numeric.c: Numeric handling functions.
 *
 *  Copyright (C) 2002 by the past and present ircd coders, and others.
 *
 *  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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 *  USA
 *
 *  $Id: numeric.c,v 1.3 2003/08/14 07:51:19 stu Exp $
 */

#include "stdinc.h"

#include "numeric.h"
#include "irc_string.h"
#include "common.h"     /* NULL cripes */
#include "memory.h"
#include "s_log.h"
#include "fileio.h"
#include "send.h"
#include "client.h"
#include "messages.tab"

static char used_locale[LOCALE_LENGTH] = "standard";

/*
 * form_str
 *
 * inputs	- numeric
 * output	- corresponding string
 * side effects	- NONE
 */
const char* form_str(int numeric)
{
  assert(-1 < numeric);
  assert(numeric < ERR_LAST_ERR_MSG);

  if (numeric > ERR_LAST_ERR_MSG)
    numeric = ERR_LAST_ERR_MSG;
  if (numeric < 0)
    numeric = ERR_LAST_ERR_MSG;

  assert(replies[numeric].standard != NULL);

  return (replies[numeric].translated != NULL ? replies[numeric].translated :
                                                replies[numeric].standard);
}

/* Attempts to change a numeric with index "reply" to "new_reply".
 * Returns 1 if ok, 0 otherwise.
 */
static int
change_reply(const char *locale, int linecnt, int reply, char *new_reply)
{
  int found;
  char *new = new_reply;
  const char *old = replies[reply].standard;

  for (; *new; new++)
  {
    if (*new == '%')
    {
      if (!*++new) break;
      if (*new != '%')
      {
        /* We've just found a format symbol. Check if it is the next format
         * symbol in the original reply.
         */
        for (; *new >= '0' && *new <= '9'; new++); /* skip size prefix */
        found = 0;
        for (; *old; old++)
        {
          if (*old == '%')
	  {
	    if (!*++old) break;  /* shouldn't happen */
	    if (*old != '%')
            {
              for (; *old >= '0' && *old <= '9'; old++); /* skip size prefix */
              if (*new != *old++)
              {
                ilog(L_ERROR, "Incompatible format symbols (%s.lang, %d)",
	                      locale, linecnt);
                return 0;
              }
              found = 1;
              break;
            }
	  }
        }
        if (!found)
        {
          ilog(L_ERROR, "Too many format symbols (%s.lang, %d)", locale, linecnt);
          return(0);
        }
      }
    }
  }

  for (; *old; old++)
  {
    if (*old == '%')
    {
      if (!*++old) break;  /* shouldn't happen */
      if (*old != '%')
      {
        ilog(L_ERROR, "Too few format symbols (%s.lang, %d)", locale, linecnt);
        return(0);
      }
    }
  }

  MyFree(replies[reply].translated);
  DupString(replies[reply].translated, new_reply);
  return(1);
}

/* Loads a language file. Errors are logged into the log file. */
void
set_locale(const char *locale)
{
  int i, res = 1, linecnt = 0;
  char buffer[BUFSIZE + 1];
  char *ident, *reply;
  FBFILE *f;

  /* Restore standard replies */
  for (i = 0; i <= ERR_LAST_ERR_MSG; i++)   /* 0 isn't a magic number! ;> */
  {
    if (replies[i].translated != NULL)
    {
      MyFree(replies[i].translated);
      replies[i].translated = NULL;
    }
  }

  if (strchr(locale, '/') != NULL)
  {
    strlcpy(used_locale, "standard", sizeof(used_locale));  /* XXX paranoid */
    return;
  }

  /* yes, I know - the slash isn't necessary. But I have to be sure
   * that it'll work even if some lame admin won't put "/" at the end
   * of MSGPATH.
   */
  snprintf(buffer, BUFSIZE + 1, "%s/%s.lang", MSGPATH, locale);
  if ((f = fbopen(buffer, "r")) == NULL)
  {
    strlcpy(used_locale, "standard", sizeof(used_locale));  /* XXX */
    return;
  }

  /* Process the language file */
  while (fbgets(buffer, BUFSIZE + 1, f))
  {
    ++linecnt;
    if (buffer[0] == ';')
      continue;   /* that's a comment */

    if ((ident = strpbrk(buffer, "\r\n")) != NULL)
      *ident = '\0';

    /* skip spaces if there are any */
    for (ident = buffer; *ident == ' ' || *ident == '\t'; ident++)/* null */;
    if (*ident == '\0')
      continue;		   /* empty line */

    /* skip after the reply identificator */
    for (reply = ident; *reply != ' ' && *reply != '\t' && *reply != ':';
      reply++)
      if (*reply == '\0') goto error;

    if (*reply == ' ' || *reply == '\t')
    {
      for (*reply++ = '\0'; *reply == ' ' || *reply == '\t'; reply++);
      if (*reply != ':')
      {
        error:
        ilog(L_ERROR, "Invalid line in language file (%s.lang, %d)",
	              locale, linecnt);
	res = 0;
	continue;
      }
    }
    else
      *reply++ = '\0';
    if (*ident == '\0')
      goto error;

    /* skip to the beginning of reply */
    while (*reply == ' ' || *reply == '\t') reply++;
    if (*reply == '\0')
      goto error;

    for (i = 0; i <= ERR_LAST_ERR_MSG; i++)
    {
      if (replies[i].name != NULL)
      {
        if (irccmp(replies[i].name, ident) == 0)
        {
          if (!change_reply(locale, linecnt, i, reply)) res = 0;
          i = -1;
          break;
        }
      }
    }
    if (i != -1)
    {
      ilog(L_ERROR,
	   "Unknown numeric %s (%s.lang, %d)", ident, locale, linecnt);
      res = 0;
    }
  }
  fbclose(f);

  strlcpy(used_locale, locale, sizeof(used_locale));
  if (!res)
    sendto_realops_flags(UMODE_ALL, L_ADMIN, "Language file [%s] contains "
                         "errors, check server log file for more details",
			 used_locale);
}

/* Returns the name of current locale. */
const char *
get_locale(void)
{
  return used_locale;
}
