#ifdef RCS
static char rcsid[]="$Id: convert.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/convert.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 "function.h"
#include "transfer.h"
#include "country.h"
#include "convert.h"

extern char *errfrom;

tz tzinfo[] = {
  /* Standard and non-standard timezones and daylight savings times */
  {"IDLEW",-12,0},
  {"NT",-11,0},
  {"HST",-10,0},
  {"HDT",-9,0},
  {"YST",-9,0},
  {"YDT",-8,0},
  {"PST",-8,0},
  {"PDT",-7,0},
  {"MST",-7,0},
  {"MDT",-6,0},
  {"CST",-6,0},
  {"CDT",-5,0},
  {"EST",-5,0},
  {"EDT",-4,0},
  {"AST",-4,0},
  {"ADT",-3,0},
  {"AT",-2,0},
  {"WAT",-1,0},
  {"GMT",0,0},
  {"UTC",0,0},
  {"BST",1,0},
  {"CET",1,0},
  {"MET",1,0},
  {"EET",2,0},
  {"EAT",2,0},
  {"BT",3,0},
  {"CCT",8,0},
  {"WAST",8,0},
  {"JST",9,0},
  {"EAST",10,0},
  {"EADT",11,0},
  {"IDLE",12,0},

  /* Full names for some of the timezones above */
  {"International Date Line West",-12,0},
  {"Nome Time",-11,0},
  {"Hawaii Standard Time",-10,0},
  {"Yukon Standard Time",-9,0},
  {"Pacific Standard Time",-8,0},
  {"Mountain Standard Time",-7,0},
  {"Central Standard Time",-6,0},
  {"Eastern Standard Time",-5,0},
  {"Atlantic Standard Time",-4,0},
  {"Azores Time",-2,0},
  {"West Africa Time",-1,0},
  {"Greenwich Mean Time",0,0},
  {"Brittish Summer Time",1,0},
  {"Central European Time",1,0},
  {"Eastern European Time",2,0},
  {"Baghdad Time",3,0},
  {"Chinese Coast Time",8,0},
  {"West Australian Standard Time",8,0},
  {"Japanese Standard Time",9,0},
  {"East Australian Standard Time",10,0},
  {"International Date Line East",12,0},

  /* Timezones for different countries */
  {"Afghanistan",4,30},
  {"Albania",1,0},
  {"Algeria",1,0},
  {"American Samoa",-11,0},
  {"Andorra",1,0},
  {"Angola",1,0},
  {"Anguilla",-4,0},
  {"Antarctica",-2,0},
  {"Antigua",-4,0},
  {"Argentina",-3,0},
  {"Argentina western prov",-4,0},
  {"Armenia",4,0},
  {"Aruba",-4,0},
  {"Ascension",0,0},
  {"Australia Northern Territory",9,30},
  {"Australia Lord Howe Island",10,30},
  {"Australia New South Wales",10,0},
  {"Australia Queensland",10,0},
  {"Australia Victoria",10,0},
  {"Australia Australian Captial Territory",10,0},
  {"Australia South",9,30},
  {"Australia Tasmania",10,0},
  {"Australia Western",8,0},
  {"Austria",1,0},
  {"Azerbajian",3,0},
  {"Azores",-1,0},
  {"Bahamas",-5,0},
  {"Bahrain",3,0},
  {"Balearic Islands",1,0},
  {"Bangladesh",6,0},
  {"Barbados",-4,0},
  {"Belarus",2,0},
  {"Belgium",1,0},
  {"Belize",-6,0},
  {"Benin",1,0},
  {"Bermuda",-4,0},
  {"Bhutan",6,0},
  {"Bolivia",-4,0},
  {"Bonaire",-4,0},
  {"Bosnia Hercegovina",1,0},
  {"Botswana",2,0},
  {"Brazil Acre",-4,0},
  {"Brazil Atlantic Islands",-1,0},
  {"Brazil East",-3,0},
  {"Brazil West",-4,0},
  {"British Virgin Islands",-4,0},
  {"Brunei",8,0},
  {"Bulgaria",2,0},
  {"Burkina Faso",0,0},
  {"Burundi",2,0},
  {"Cambodia",7,0},
  {"Cameroon",1,0},
  {"Canada Central",-6,0},
  {"Canada Eastern",-5,0},
  {"Canada Mountain",-7,0},
  {"Canada Yukon & Pacific",-8,0},
  {"Canada Atlantic",-4,0},
  {"Canada Newfoundland",-3,-30},
  {"Canary Islands",0,0},
  {"Canton Enderbury Islands",-11,0},
  {"Cape Verde",-1,0},
  {"Caroline Island",11,0},
  {"Cayman Islands",-5,0},
  {"Central Africa",1,0},
  {"Chad",1,0},
  {"Channel Islands",0,0},
  {"Chatham Island",12,45},
  {"Chile",-4,0},
  {"China",8,0},
  {"Christmas Islands",-10,0},
  {"Cocos Islands",-10,0},
  {"Colombia",-5,0},
  {"Congo",1,0},
  {"Cook Islands",-10,0},
  {"Costa Rica",-6,0},
  {"Cote d'Ivoire",0,0},
  {"Croatia",1,0},
  {"Cuba",-5,0},
  {"Curacao",-4,0},
  {"Cyprus",2,0},
  {"Czech",1,0},
  {"Dahomey",1,0},
  {"Denmark",1,0},
  {"Djibouti",3,0},
  {"Dominica",-4,0},
  {"Dominican Republic",-4,0},
  {"Easter Island",-6,0},
  {"Ecuador",-5,0},
  {"Egypt",2,0},
  {"El Salvador",-6,0},
  {"England",0,0},
  {"Equitorial Guinea",1,0},
  {"Eritrea",3,0},
  {"Estonia",2,0},
  {"Ethiopia",3,0},
  {"Falkland Islands",-4,0},
  {"Faroe Island",0,0},
  {"Fiji",12,0},
  {"Finland",2,0},
  {"France",1,0},
  {"French Guiana",-3,0},
  {"French Polynesia",-10,0},
  {"Gabon",1,0},
  {"Galapagos Islands",-5,0},
  {"Gambia",0,0},
  {"Gambier Island",-9,0},
  {"Georgia",4,0},
  {"Germany",1,0},
  {"Ghana",0,0},
  {"Gibraltar",1,0},
  {"Greece",2,0},
  {"Greenland",-3,0},
  {"Greenland Thule",-4,0},
  {"Greenland Scoresbysun",-1,0},
  {"Grenada",-4,0},
  {"Grenadines",-4,0},
  {"Guadeloupe",-4,0},
  {"Guam",10,0},
  {"Guatemala",-6,0},
  {"Guinea",0,0},
  {"Guinea Bissau",-1,0},
  {"Guyana",-3,0},
  {"Haiti",-5,0},
  {"Honduras",-6,0},
  {"Hong kong",8,0},
  {"Hungary",1,0},
  {"Iceland",0,0},
  {"India",5,30},
  {"Indonesia Central",8,0},
  {"Indonesia East",9,0},
  {"Indonesia West",7,0},
  {"Iran",3,30},
  {"Iraq",3,0},
  {"Ireland",0,0},
  {"Israel",2,0},
  {"Italy",1,0},
  {"Jamaica",-5,0},
  {"Japan",9,0},
  {"Johnston Island",-10,0},
  {"Jordan",2,0},
  {"Kazakhstan",6,0},
  {"Kenya",3,0},
  {"Kiribati",12,0},
  {"Korea",9,0},
  {"Kusaie",12,0},
  {"Kuwait",3,0},
  {"Kwajalein",-12,0},
  {"Kyrgyzstan",5,0},
  {"Laos",7,0},
  {"Latvia",2,0},
  {"Lebanon",2,0},
  {"Leeward Islands",-4,0},
  {"Lesotho",2,0},
  {"Liberia",0,0},
  {"Libya",2,0},
  {"Lithuania",2,0},
  {"Luxembourg",1,0},
  {"Macedonia",1,0},
  {"Madagascar",3,0},
  {"Madeira",0,0},
  {"Malawi",2,0},
  {"Malaysia",8,0},
  {"Maldives",5,0},
  {"Mali",0,0},
  {"Mallorca",1,0},
  {"Malta",1,0},
  {"Mariana Island",10,0},
  {"Marquesas Islands",-9,-30},
  {"Marshall Islands",12,0},
  {"Martinique",-4,0},
  {"Mauritania",0,0},
  {"Mauritius",4,0},
  {"Mayotte",3,0},
  {"Melilla",1,0},
  {"Mexico",-6,0},
  {"Mexico Baja Calif Norte",-8,0},
  {"Mexico Nayarit",-7,0},
  {"Mexico Sinaloa",-7,0},
  {"Mexico Sonora",-7,0},
  {"Midway Island",-11,0},
  {"Moldova",2,0},
  {"Moldovian Rep Pridnestrovye",2,0},
  {"Monaco",1,0},
  {"Mongolia",8,0},
  {"Morocco",0,0},
  {"Mozambique",2,0},
  {"Myanmar",6,30},
  {"Namibia",1,0},
  {"Nauru Republic of",12,0},
  {"Nepal",5,45},
  {"Netherlands",1,0},
  {"Netherlands Antilles",-4,0},
  {"Nevis Montserrat",-4,0},
  {"New Caledonia",11,0},
  {"New Hebrides",11,0},
  {"New Zealand",12,0},
  {"Nicaragua",-6,0},
  {"Niger",1,0},
  {"Nigeria",1,0},
  {"Niue Island",-11,0},
  {"Norfolk Island",11,30},
  {"Northern Ireland",0,0},
  {"Northern Mariana Islands",10,0},
  {"Norway",1,0},
  {"Oman",4,0},
  {"Pakistan",5,0},
  {"Palau",9,0},
  {"Panama",-5,0},
  {"Papua New Guinea",10,0},
  {"Paraguay",-4,0},
  {"Peru",-5,0},
  {"Philippines",8,0},
  {"Pingelap",12,0},
  {"Poland",1,0},
  {"Ponape Island",11,0},
  {"Portugal",1,0},
  {"Principe Island",0,0},
  {"Puerto Rico",-4,0},
  {"Qatar",3,0},
  {"Reunion",4,0},
  {"Romania",2,0},
  {"Russian Federation zone eight",9,0},
  {"Russian Federation zone eleven",12,0},
  {"Russian Federation zone five",6,0},
  {"Russian Federation zone four",5,0},
  {"Russian Federation zone nine",10,0},
  {"Russian Federation zone one",2,0},
  {"Russian Federation zone seven",8,0},
  {"Russian Federation zone six",7,0},
  {"Russian Federation zone ten",11,0},
  {"Russian Federation zone three",4,0},
  {"Russian Federation zone two",4,0},
  {"Rwanda",2,0},
  {"Saba",-4,0},
  {"Samoa",-11,0},
  {"San Marino",1,0},
  {"Sao Tome e Principe",0,0},
  {"Saudi Arabia",3,0},
  {"Scotland",0,0},
  {"Senegal",0,0},
  {"Seychelles",4,0},
  {"Sierra Leone",0,0},
  {"Singapore",8,0},
  {"Slovakia",1,0},
  {"Slovenia",1,0},
  {"Society Island",-10,0},
  {"Solomon Islands",11,0},
  {"Somalia",3,0},
  {"South Africa",2,0},
  {"Spain",1,0},
  {"Sri Lanka",5,30},
  {"St Christopher",-4,0},
  {"St Croix",-4,0},
  {"St Helena",0,0},
  {"St John",-4,0},
  {"St Kitts Nevis",-4,0},
  {"St Lucia",-4,0},
  {"St Maarten",-4,0},
  {"St Pierre & Miquelon",-3,0},
  {"St Thomas",-4,0},
  {"St Vincent",-4,0},
  {"Sudan",2,0},
  {"Suriname",-3,0},
  {"Swaziland",2,0},
  {"Sweden",1,0},
  {"Switzerland",1,0},
  {"Syria",2,0},
  {"Tahiti",-10,0},
  {"Taiwan",8,0},
  {"Tajikistan",6,0},
  {"Tanzania",3,0},
  {"Thailand",7,0},
  {"Togo",0,0},
  {"Tonga",13,0},
  {"Trinidad and Tobago",-4,0},
  {"Tuamotu Island",-10,0},
  {"Tubuai Island",-10,0},
  {"Tunisia",1,0},
  {"Turkey",2,0},
  {"Turkmenistan",5,0},
  {"Turks and Caicos Islands",-5,0},
  {"Tuvalu",12,0},
  {"Uganda",3,0},
  {"Ukraine",2,0},
  {"United Arab Emirates",4,0},
  {"United Kingdom",0,0},
  {"USA Central",-6,0},
  {"USA Eastern",-5,0},
  {"USA Mountain",-7,0},
  {"USA Arizona",-7,0},
  {"USA Indiana East",-5,0},
  {"USA Pacific",-8,0},
  {"USA Alaska",-9,0},
  {"USA Aleutian",-10,0},
  {"USA Hawaii",-10,0},
  {"Uruguay",-3,0},
  {"Uzbekistan",5,0},
  {"Vanuatu",11,0},
  {"Vatican City",1,0},
  {"Venezuela",-4,0},
  {"Vietnam",7,0},
  {"Virgin Islands",-4,0},
  {"Wake Island",12,0},
  {"Wales",0,0},
  {"Wallis and Futuna Islands",12,0},
  {"Windward Islands",-4,0},
  {"Yemen",3,0},
  {"Yugoslavia",1,0},
  {"Zaire Kasai",2,0},
  {"Zaire Kinshasa Mbandaka",1,0},
  {"Zaire Haut Zaire",2,0},
  {"Zaire Kivu",2,0},
  {"Zaire Shaba",2,0},
  {"Zambia",2,0},
  {"Zimbabwe",2,0}
};


/* --- FindPrefix ------------------------------------------------- */

float FindPrefix(char *s, char **newptr)
{
  char *ptr = s;
  float exp = 1;

  if      (StrEqualMax(s, 4, "atto" )) { exp=0.000000000000000001; ptr+=4; }
  else if (StrEqualMax(s, 5, "femto")) { exp=0.000000000000001; ptr+=5; }
  else if (StrEqualMax(s, 4, "pico" )) { exp=0.000000000001; ptr+=4; }
  else if (StrEqualMax(s, 4, "nano" )) { exp=0.000000001; ptr+=4; }
  else if (StrEqualMax(s, 5, "micro")) { exp=0.000001; ptr+=5; }
  else if (StrEqualMax(s, 5, "milli")) { exp=0.001; ptr+=5; }
  else if (StrEqualMax(s, 5, "centi")) { exp=0.01; ptr+=5; }
  else if (StrEqualMax(s, 4, "deci" )) { exp=0.1; ptr+=4; }
  else if (StrEqualMax(s, 5, "hecto")) { exp=100; ptr+=5; }
  else if (StrEqualMax(s, 4, "kilo" )) { exp=1000; ptr+=4; }
  else if (StrEqualMax(s, 4, "mega" )) { exp=1000000; ptr+=4; }
  else if (StrEqualMax(s, 4, "giga" )) { exp=1000000000; ptr+=4; }
  else if (StrEqualMax(s, 4, "tera" )) { exp=1000000000000.0; ptr+=4; }
  else if (StrEqualMax(s, 5, "penta")) { exp=1000000000000000.0; ptr+=5; }

  *newptr = ptr;
  return exp;
}

/* --- Convert ---------------------------------------------------- */

void Convert(char *from, char *line)
{
  char buf[MINIBUFFER];
  char *unit;
  float value, exp;
  int dec = 1;
  float meter=0, celsius=0, gram=0, liter=0, sqm=0;
  int len=0, temp=0, mass=0, vol=0, area=0;

  if (2 <= StrScan(line, "%g %"MINIBUFFERTXT"s %d", &value, buf, &dec)) {
    if (dec > 4)
      dec = 4;

    exp = FindPrefix(buf, &unit);

    /* Length conversion */
    if      (StrEqualMax(unit,2,"me"))   { len=1; meter=value; }
    else if (StrEqualMax(unit,2,"dm"))   { len=1; meter=value/10; }
    else if (StrEqualMax(unit,2,"cm"))   { len=1; meter=value/100; }
    else if (StrEqualMax(unit,2,"mm"))   { len=1; meter=value/1000; }
    else if (StrEqualMax(unit,2,"mi"))   { len=1; meter=value*1609.344; }
    else if (StrEqualMax(unit,2,"ya"))   { len=1; meter=value*0.9144; }
    else if (StrEqualMax(unit,2,"fe") ||
             StrEqualMax(unit,2,"fo") ||
             StrEqualMax(unit,2,"ft"))   { len=1; meter=value*0.3048; }
    else if (StrEqualMax(unit,2,"in"))   { len=1; meter=value*0.0254; }
    else if (StrEqualMax(unit,3,"nau"))  { len=1; meter=value*1852; }
    else if (StrEqualMax(unit,3,"cha"))  { len=1; meter=value*20.11684023; }
    else if (StrEqualMax(unit,3,"rod"))  { len=1; meter=value*5.02921006; }
    else if (StrEqualMax(unit,3,"fat"))  { len=1; meter=value*1.82880366; }

    /* Temperature conversion */
    else if (StrEqualMax(unit,3,"cel"))  { temp=1; celsius=value; }
    else if (StrEqualMax(unit,3,"fah") ||
             StrEqualMax(unit,3,"far"))  { temp=1; celsius=(value - 32) * (5.0/9.0); }
    else if (StrEqualMax(unit,2,"ke"))   { temp=1; celsius=value - 273.15; }

    /* Weight conversion */
    else if (StrEqualMax(unit,4,"gram")) { mass=1; gram=value; }
    else if (StrEqualMax(unit,2,"kg"))   { mass=1; gram=value*1000; }
    else if (StrEqualMax(unit,2,"lb") ||
             StrEqualMax(unit,2,"po"))   { mass=1; gram=value*453.59237; }
    else if (StrEqualMax(unit,2,"oz") ||
             StrEqualMax(unit,2,"ou"))   { mass=1; gram=value*28.349523125; }

    /* Volume conversion */
    else if (StrEqualMax(unit,2,"li"))   { vol=1; liter=value; }
    else if (StrEqualMax(unit,3,"gal"))  { vol=1; liter=value*4.546092; }
    else if (StrEqualMax(unit,2,"us"))   { vol=1; liter=value*3.7858411784; }
    else if (StrEqualMax(unit,3,"qua"))  { vol=1; liter=value*0.946352946; }
    else if (StrEqualMax(unit,3,"pin"))  { vol=1; liter=value*0.473176473; }
    else if (StrEqualMax(unit,3,"cup"))  { vol=1; liter=value*0.2365882365; }
    else if (StrEqualMax(unit,2,"fl"))   { vol=1; liter=value*0.0295735295625; }
    else if (StrEqualMax(unit,4,"tabl") ||
             StrEqualMax(unit,4,"tbsp")) { vol=1; liter=value*0.01478676478125; }
    else if (StrEqualMax(unit,3,"tea") ||
             StrEqualMax(unit,3,"tsp"))  { vol=1; liter=value*0.00492892159375; }
    else if (StrEqualMax(unit,4,"bush")) { vol=1; liter=value*35.23907; }
    else if (StrEqualMax(unit,4,"peck")) { vol=1; liter=value*8.8097675; }
    else if (StrEqualMax(unit,4,"cuya") ||
             StrEqualMax(unit,4,"cuyr")) { vol=1; liter=value*764.554857984; }
    else if (StrEqualMax(unit,4,"cufe") ||
             StrEqualMax(unit,4,"cufo") ||
             StrEqualMax(unit,4,"cuft")) { vol=1; liter=value*28.316846592; }
    else if (StrEqualMax(unit,4,"cuin")) { vol=1; liter=value*0.016387064; }
    else if (StrEqualMax(unit,3,"cum"))  { vol=1; liter=value*1000; }

    /* Area conversion */
    else if (StrEqualMax(unit,4,"sqme")) { area=1; sqm=value; }
    else if (StrEqualMax(unit,4,"sqkm")) { area=1; sqm=value*1000000; }
    else if (StrEqualMax(unit,2,"ha") ||
             StrEqualMax(unit,3,"hec"))  { area=1; sqm=value*10000; }
    else if (StrEqualMax(unit,2,"ac"))   { area=1; sqm=value*4046.872609874252; }
    else if (StrEqualMax(unit,4,"sqmi")) { area=1; sqm=value*2589988.110336; }
    else if (StrEqualMax(unit,3,"sqy"))  { area=1; sqm=value*0.83612736; }
    else if (StrEqualMax(unit,3,"sqf"))  { area=1; sqm=value*0.09290304; }
    else if (StrEqualMax(unit,3,"sqi"))  { area=1; sqm=value*0.00064516; }

    /* Unknown conversion */
    else {
      Sendf(from, GetText(msg_unknown_unit), unit);
      return;
    }

    if (len) {
      meter *= exp;
      Sendf(from, GetText(msg_convert_length),
            dec, meter,
            dec, meter/1609.344,
            dec, meter/0.9144,
            dec, meter/0.3048,
            dec, meter/0.0254,
            dec, meter/1852,
            dec, meter/20.11684023,
            dec, meter/5.02921006,
            dec, meter/1.82880366);
    }
    else if (temp) {
      celsius *= exp;
      Sendf(from, GetText(msg_convert_temperature),
            dec, celsius,
            dec, celsius * (9.0/5.0) + 32,
            dec, celsius + 273.15);
    }
    else if (mass) {
      gram *= exp;
      Sendf(from, GetText(msg_convert_mass),
            dec, gram,
            dec, gram/453.59237,
            dec, gram/28.349523125);
    }
    else if (vol) {
      liter *= exp;
      Sendf(from, GetText(msg_convert_volume),
            dec, liter,
            dec, liter/4.546092,
            dec, liter/3.7858411784,
            dec, liter/0.0295735295625,
            dec, liter/0.473176473,
            dec, liter/0.2365882365,
            dec, liter/0.01478676478125,
            dec, liter/0.00492892159375,
            dec, liter/0.946352946,
            dec, liter/35.23907,
            dec, liter/8.8097675,
            dec, liter/1000,
            dec, liter/764.554857984,
            dec, liter/28.316846592,
            dec, liter/0.01638706);
    }
    else if (area) {
      sqm *= exp;
      Sendf(from, GetText(msg_convert_area),
            dec, sqm,
            dec, sqm/1000000,
            dec, sqm/10000,
            dec, sqm/4046.872609874252,
            dec, sqm/2589988.110336,
            dec, sqm/0.83612736,
            dec, sqm/0.09290304,
            dec, sqm/0.00064516);
    }
  }
  else
    CmdSyntax(errfrom, "CONVERT");
}

/* --- TimeZoneInit ------------------------------------------------- */

void TimeZoneInit(void)
{
  int i;

  for (i=0; i < sizeof(tzinfo)/sizeof(tzinfo[0]); i++) {
    tzinfo[i].sig = HashSignatureU(tzinfo[i].name);
    tzinfo[i].let = MarkLetters(tzinfo[i].name);
  }
}

/* --- FindZoneByName --------------------------------------------- */

tz *FindZoneByName(char *name)
{
  int min = INT_MAX;
  int index = -1;
  int i, d;
  ulong sig, let;

  if ('.' == name[0]) {
    land *country;

    /* Get name from extention */
    country = FindByCode(&name[1]);
    if (NULL == country)
      return NULL; /* No success */
    name = country->country;
  }

  sig = HashSignatureU(name);
  let = MarkLetters(name);

  for (i=0; i < sizeof(tzinfo)/sizeof(tzinfo[0]); i++) {
    d = HammingDistance(tzinfo[i].sig, sig) +
        UnequalBits(tzinfo[i].let, let);
    if (min > d) {
      min = d;
      index = i;
    }
  }

  return ((index > -1) ? &tzinfo[index] : NULL);
}

/* --- TimeZone --------------------------------------------------- */

void TimeZone(char *from, char *line)
{
  extern char myhost[];
  char buffer[BIGBUFFER] = "";
  char *pointer;
  char *p = NULL;
  int hours = 0, mins = 0;
  tz *first = NULL, *second = NULL;
  time_t t;
  struct tm tm, *tp;

  t = time(NULL);
  StrScan(line, "%"MINIBUFFERTXT"s", buffer);

  /*
   * 1: Convert tzinfo
   *    darx tz 12:00 [am/pm] GMT [ AST ]
   */
  if (isdigit(buffer[0])) {
    char timebuf[MINIBUFFER];

    if (2 == StrScan(line, "%"MINIBUFFERTXT"[0-9:]%"BIGBUFFERTXT"[^\n]",
                     timebuf, buffer)) {

      /* Parse time */
      if (StrLength(timebuf) > 2) {
        if (StrIndex(timebuf, ':'))
          StrScan(timebuf, "%d:%d", &hours, &mins);
        else {
          if (StrLength(timebuf) > 3)
            StrScan(timebuf, "%02d%d", &hours, &mins);
          else
            StrScan(timebuf, "%1d%02d", &hours, &mins);
        }
      }
      else {
        hours = atoi(timebuf);
      }

      pointer = buffer;

      /* am / pm */
      if (buffer[0] && buffer[1] && (((char)0 == buffer[2]) || (' ' == buffer[2]))) {
        if (StrEqualMax(buffer, 2, "PM")) {
          hours += 12;
          pointer = &buffer[2];
        }
        else if (StrEqualMax(buffer, 2, "AM")) {
          pointer = &buffer[2];
        }
      }

      /* Check limits and readjust time */
      if (mins > 59) {
        hours += mins/60;
        mins %= 60;
      }
      if (hours > 23)
        hours = hours % 24;

      /* Parse first tzinfo */
      p = NextQuoteWord(pointer);
      if (p) {
        first = FindZoneByName(p);
        if (NULL == first) {
          Sendf(from, GetText(msg_unknown_zone), p);
          return;
        }
      }

      /* Parse second tzinfo */
      p = NextQuoteWord(pointer);
      if (p) {
        second = FindZoneByName(p);
        if (NULL == second) {
          Sendf(from, GetText(msg_unknown_zone), p);
          return;
        }
      }
      else {
        second = FindZoneByName("GMT");
      }

      if ((NULL == first) || (NULL == second)) {
        Send(from, GetText(msg_specify_tz));
        return;
      }

      tp = localtime(&t);
      memcpy(&tm, tp, sizeof(tm));

      tm.tm_hour = hours - first->hdiff + second->hdiff;
      tm.tm_min  = mins  - first->mdiff + second->mdiff;

      t = mktime(&tm);
      tp = localtime(&t);

      Sendf(from, GetText(msg_time_equals_time_in), hours, mins,
            first->name, tp->tm_hour, tp->tm_min, second->name);
    }
    else
      Send(from, GetText(msg_specify_tz));
  }

  /*
   * 4: Display current time in zone
   *    darx tz now GMT
   */
  else if (StrEqual(buffer, "NOW")) {
    bool show_buffer;

    tp = gmtime(&t);
    memcpy(&tm, tp, sizeof(tm));

    /* If we have another argument we will use that one */
    if ((1 == StrScan(line, "%*s \"%"BIGBUFFERTXT"[^\"\n]", buffer)) ||
        (1 == StrScan(line, "%*s %"BIGBUFFERTXT"[^\n]", buffer))) {
      first = FindZoneByName(buffer);
    }
    else {
      buffer[0] = (char)0;
      pointer = StrIndexLast(myhost, '.');
      if (pointer)
        first = FindZoneByName(pointer);
      if (NULL == first)
        first = FindZoneByName("GMT");
    }

    if (NULL == first) {
      Sendf(from, GetText(msg_unknown_zone), buffer[0] ? buffer : "GMT");
      return;
    }

    tm.tm_hour += first->hdiff;
    tm.tm_min += first->mdiff;
    t = mktime(&tm);
    tp = localtime(&t);

    /* No point in showing the timezone or country twice */
    show_buffer = (buffer[0] && !StrEqual(first->name, buffer));

    Sendf(from, GetText(msg_current_time),
          first->name,
          show_buffer ? " (" : "",
          show_buffer ? buffer : "",
          show_buffer ? ")" : "",
          tp->tm_hour, tp->tm_min);
  }

  /*
   * List countries/zones
   */
  else if (StrEqual(buffer, "LIST")) {
    char pattern[MINIBUFFER] = "*";
    int count = 0, total = 0;
    int i;

    StrScan(line, "%*s %"MINIBUFFERTXT"s", pattern);

    buffer[0] = (char)0;
    for (i = 0; i < sizeof(tzinfo)/sizeof(tzinfo[0]); i++) {
      if (Match(tzinfo[i].name, pattern)) {
        if ((StrLength(buffer) + StrLength(tzinfo[i].name)) < MIDBUFFER) {
          StrFormatAppendMax(buffer, sizeof(buffer), "%s%s",
                             (0 == count) ? "" : ", ",
                             tzinfo[i].name);
          count++;
        }
        total++;
      }
    }

    if (0 == count) {
      Sendf(from, GetText(msg_pattern_doesnt_match), pattern);
    }
    else {
      Sendf(from, buffer);
      if (count < total)
        Sendf(from, GetText(msg_limit_searchpattern), total - count);
    }
  }

  /*
   * 2: display timezone
   *    darx tz <where>
   *
   * 3: display timezone relative
   *    darx tz <where> [where]
   */
  else {
    char hourbuf[MINIBUFFER], minbuf[MINIBUFFER];
    bool ahead;

    /* Parse first timezone */
    pointer = line;
    p = NextQuoteWord(pointer);
    if (p) {
      first = FindZoneByName(p);
      if (NULL == first) {
        Sendf(from, GetText(msg_unknown_zone), p);
        return;
      }
    }

    /* Parse second timezone */
    p = NextQuoteWord(pointer);
    if (p) {
      second = FindZoneByName(p);
      if (NULL == second) {
        Sendf(from, GetText(msg_unknown_zone), p);
        return;
      }
    }
    else {
      second = FindZoneByName("GMT");
    }

    if ((NULL == first) || (NULL == second)) {
      Send(from, GetText(msg_specify_tz));
      return;
    }

    /* Calculate the time difference in minutes */
    mins = (first->hdiff - second->hdiff) * 60 +
           (first->mdiff - second->mdiff);

    ahead = ((mins < 0) ? FALSE : TRUE);
    if (mins < 0)
      mins = -mins;
    hours = mins / 60;
    mins %= 60;

    if (hours) {
      StrFormatMax(hourbuf, sizeof(hourbuf), "%d %s",
                   hours,
                   (hours == 1) ? GetText(msg_hour) : GetText(msg_hours));
    }

    if (mins) {
      StrFormatMax(minbuf, sizeof(minbuf), "%s%s%s%d %s",
                   hours ? " " : "",
                   hours ? GetText(msg_and) : "",
                   hours ? " " : "",
                   mins,
                   (mins == 1) ? GetText(msg_minute) : GetText(msg_minutes));
    }

    if (hours || mins) {
      Sendf(from, "%s %s %s%s %s %s.",
            first->name,
            GetText(msg_is),
            hours ? hourbuf : "",
            mins ? minbuf : "",
            ahead ? GetText(msg_ahead_of) : GetText(msg_behind),
            second->name);
    }
    else
      Sendf(from, GetText(msg_is_the_same_as), first->name, second->name);
  }
}

/* --- Exchange --------------------------------------------------- */

void Exchange(char *from, char *line)
{
  extern char exchangecmd[];
  char amount[MINIBUFFER];
  char convfrom[MINIBUFFER] = "";
  char convto[MINIBUFFER]   = "";
  land *n_from, *n_to;

  if ((3 == StrScan(line, "%"MINIBUFFERTXT"s %"MINIBUFFERTXT"s %"MINIBUFFERTXT"s",
                   amount, convfrom, convto)) && exchangecmd[0]) {
    if ('.' == convfrom[0])
      n_from = FindByCode(&convfrom[1]);
    else
      n_from = FindByCurrency(convfrom);
    if (NULL == n_from)
      n_from = FindByCountry(convfrom);

    if ('.' == convto[0])
      n_to = FindByCode(&convto[1]);
    else
      n_to = FindByCurrency(convto);
    if (NULL == n_to)
      n_to = FindByCountry(convto);

    if (n_from && (NULL == n_from->currency)) {
      Sendf(errfrom, GetText(msg_no_currency_info), convfrom);
    }
    else if (n_to && (NULL == n_to->currency)) {
      Sendf(errfrom, GetText(msg_no_currency_info), convto);
    }
    else {
      Execute(from, "%s \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" 2>/dev/null",
              exchangecmd, amount,
              n_from ? n_from->currency : convfrom,
              n_to   ? n_to->currency   : convto,
              n_from ? n_from->onemoney : "-",
              n_to   ? n_to->onemoney   : "-",
              n_from ? n_from->country  : "-",
              n_to   ? n_to->country    : "-");
    }
  }
  else if ((1 <= StrScan(line, "%"MINIBUFFERTXT"s %"MINIBUFFERTXT"s",
                        convfrom, convto)) && exchangecmd[0]) {
    if (StrEqual(convfrom, "list")) {
      /*
       * List available currencies for the exchange service
       */
      Execute(from, "%s list \"%s\" 2>/dev/null", exchangecmd, convto);
    }
    else {
      /*
       * Present all available information about the convfrom currency
       */
      if ('.' == convfrom[0])
        n_from = FindByCode(&convfrom[1]);
      else
        n_from = FindByCurrency(convfrom);
      if (NULL == n_from)
        n_from = FindByCountry(convfrom);

      if (NULL == n_from)
        Sendf(errfrom, GetText(msg_no_country_or_currency), convfrom);
      else if (NULL == n_from->currency)
        Sendf(errfrom, GetText(msg_no_currency_info), convfrom);
      else
        Sendf(from, GetText(msg_currency_is),
              n_from->country,
              n_from->currency,
              n_from->onemoney);
    }
  }
  else
    CmdSyntax(errfrom, "EXCHANGE");
}


syntax highlighted by Code2HTML, v. 0.9.1