/* iexlib - Import/export library for the 'Bbuuzzb' database and ASCII files.
   Rick Smereka. Copyright (C) 2003-2006.

   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, get a copy via the Internet at
   http://gnu.org/copyleft/gpl.html or write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307 USA

   You can contact the author via email at rsmereka@future-lab.com

   This module will import (and eventually export) ascii data into
   a 'bbuuzzb' table. The field ascii data may be fixed or delimited (by
   any single character). The import is template or profile
   based. An import profile record is expected in the 'bbuuzzb' table
   'iex.config'. The field layout for import profile record is as follows:

      field #  description

        1      profile name (any length)
        2      comment
        3      'i' for import, 'x' for export (no export currently)
        4      ascii format, 'f' for fixed or field delimiter otherwise
        5      array of specs, each complete spec is a subfield containing
               the following subsubfields:
        5,*,1  source field, field number for delimited or 'start,chars'
               for fixed, note that start byte is zero based
        5,*,2  conversion, one of:
                  'fxp[,n]' - fxp number with 'n' scale (zero if no scale)
                  'time' - convert hh:mm[a|p] to 24hrs. no colon
                  'int[,n]' - integer padded on the left with zeros to 'n' len
                  'pint,n' - integer plus the parameter which is mandatory
                  'winddir' - convert a wind direction alpha (N,NW,S,SSE etc)
                              to degrees (result is fxp with scale of one)
                  'month' - convert a 3 char alpha month name to an integer
                            (base one) zero padded on the left to two digits
               a empty value here will denote no conversion
        5,*,3  destination which may be a 'bbuuzzb' field, subfield or
               or subsubfield in the form 'f[,sf][,ssf]', zero may be
               used in any part to indicate an append (not recommended)
        6      auto record locate/create switch (see below)
        7      'bbuuzzb' field to match (optional), if present, an
               attempt will be made to locate an existing matching record
        8      ascii null field signature (optional), if present,
               these chars will denote an empty input ascii field
       
   There must be at least six fields present in the iex profile.
   This module may be used in a couple of different ways. To import an
   entire file at once without any special processsing, use 'iexlib_import'.
   You also may import one ascii line at a time with any processing you
   like by first using the function 'iexlib_start' to load the profile
   followed by the function 'iexlib_import_do_line' for each ascii input
   line and 'iexlib_stop' when all lines are completed. The function
   'iexlib_import' is a good example of manually controlling the import.
   The auto record locate/create switch is used in conjunction with
   the match field. If the auto record switch is off, the caller is
   assumed to have already set the 'bbuuzzb' file position and no
   automatic record location or creation will take place. If the
   auto switch is on and there is a match field present, an
   attempt will be made to find the matching 'bbuuzzb' record before
   the import of that ascii line takes place. If the find of the
   'bbuuzzb' record fails, a new output record will be created.
   If the auto switch is on and there is no match field present,
   a new 'bbuuzzb' output record will be created for each ascii
   input line. As a guideline, set the auto switch high when you
   will be using the function 'iexlib_import' (high level) to
   import the entire ascii file at once. Set the auto switch low
   when you will be manually controlling the import. When the
   auto record switch is low, any value in the match field will
   be ignored.

   Original Linux version. Mar/2003, Rick Smereka

   Ported to 32-bit Windows, DOS and QNX. Aug/2003, Rick Smereka

   Changed all logging calls from 'sys_log' to 'logman'.
   Mar/2004, Rick Smereka

   Fixed bug in 'iexlib_import' where 'db_write' was being called
   when the input ASCII line was empty. Jun/2006, Rick Smereka */

#include "stdhead.h"
#include "dbfxp.h"
#include "dbmess.h"
#include "dbiocode.h"
#include "wlib.h"
#ifdef MULTIUSER
#include "dbcs.h"
#else
#include "dblocal.h"
#endif
#include "iexlib.h"

/* global data */

static struct iex_spec *iex_ihead = IEX_OT_NULL; /* config link list head */
static int iex_is_export;                    /* are we processing an export? */
static int iex_is_fixed;                     /* is the ASCII format fixed? */
static char iex_ascii_delim;                 /* ASCII field delimiter */
static int iex_auto_rec;                     /* auto record locate/create? */
static int iex_match_field;                  /* dest field number to match */
static char iex_null_field[10];              /* null ascii field char seq */

/* private functions */

static int iexlib_config_set_sd(int, int, int, struct iex_spec *);
static int iexlib_config_set_conv(int, int, struct iex_spec *);
static int iexlib_config_get_sd(char *, int *, int *, int *);
static int iexlib_import_get_ascii_field(char *, char *, struct iex_spec *);
static int iexlib_import_match(int, char *);
static int iexlib_import_do_wind_dir(int, char *, struct iex_spec *);
static int iexlib_import_do_time(int, char *, struct iex_spec *);
static int iexlib_import_do_fxp(int, char *, struct iex_spec *);
static int iexlib_import_do_int(int, char *, struct iex_spec *);
static int iexlib_import_do_pint(int, char *, struct iex_spec *);
static int iexlib_import_do_month(int, char *, struct iex_spec *);
static int iexlib_import_conv_wind_dir(char *, struct iex_spec *, char *);
static int iexlib_import_conv_time(char *, char *);
static int iexlib_import_conv_fxp(char *, struct iex_spec *, char *);
static int iexlib_import_conv_int(char *, struct iex_spec *, char *);
static int iexlib_import_conv_pint(char *, struct iex_spec *, char *);
static int iexlib_import_conv_month(char *, char *);
static int iexlib_import_put_fxp(int, struct fxp *, struct iex_spec *);
static int iexlib_import_put_data(int, char *, struct iex_spec *);
static int iexlib_import_conv_get_token(char *);

int iexlib_import(char *profile_name, char *in_file_name,
                  char *out_table_name)
{
   /* Perform a complete import of the input ascii file into the
      'bbuuzzb' table using the supplied profile. Function returns
      a 'dbeng' code. */

   FILE *in;
   char mname[] = "iexlib_import", *in_line;
   int ret, tid, line_no;

   if (profile_name == (char *)NULL || !strlen(profile_name))
      {
      db_io_code_log(mname, "null or empty[profile_name]",
                     DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (in_file_name == (char *)NULL || !strlen(in_file_name))
      {
      db_io_code_log(mname, "null or empty[in_file_name]",
                     DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (out_table_name == (char *)NULL || !strlen(out_table_name))
      {
      db_io_code_log(mname, "null or empty[out_table_name]",
                     DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if ((ret = iexlib_start(profile_name)) != DBENG_OK)
      {
      db_io_code_log(mname, "error loading profile", ret);
      return(ret);
      }

   if ((in = fopen(in_file_name, "r")) == (FILE *)NULL)
      {
      logman("%s:unable to open %s", mname, in_file_name);
      iexlib_stop();
      return(DBENG_UNABLE_TO_OPEN);
      }

   if ((ret = db_open(out_table_name, &tid)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_open[tid]", ret);
      fclose(in);
      iexlib_stop();
      return(ret);
      }

   if ((in_line = (char *)malloc(BUFSIZE)) == (char *)NULL)
      {
      db_io_code_log(mname, "alloc fail[in_line]", DBENG_MEMORY_FAIL);
      fclose(in);
      (void)db_close(tid);
      iexlib_stop();
      return(DBENG_MEMORY_FAIL);
      }

   get_rec(in, in_line);
   line_no = 1;

   if (ferror(in))
      {
      logman("%s:error getting first line of:%s", mname, in_file_name);
      fclose(in);
      (void)db_close(tid);
      free(in_line);
      iexlib_stop();
      return(DBENG_INVALID_FUNCTION);
      }

   while(!feof(in))
      {
      if ((ret = iexlib_import_do_line(tid, in_line)) != DBENG_OK)
         {
         logman("%s:exit[%d]:bad rc from iexlib_import_do_line[%d]",
                       mname, ret, line_no);
         (void)db_close(tid);
         fclose(in);
         free(in_line);
         iexlib_stop();
         return(ret);
         }

      if (strlen(in_line))
         if ((ret = db_write(tid)) != DBENG_OK)
            {
            db_io_code_log(mname, "error writing", ret);
            (void)db_close(tid);
            fclose(in);
            free(in_line);
            iexlib_stop();
            return(ret);
            }

      get_rec(in, in_line);
      line_no++;
      }

   (void)db_close(tid);
   fclose(in);
   free(in_line);
   iexlib_stop();
   return(DBENG_OK);
}

int iexlib_start(char *profile_name)
{
   /* Obtain the iex configuration record from the 'profile_name' and
      load all module globals and the iex spec link list. Function
      returns a 'dbeng' code. */

   struct iex_spec *ot, *rov;
   char mname[] = "iexlib_start", fdat[25];
   int tid, ret, count, i, size;

   if (profile_name == (char *)NULL || !strlen(profile_name))
      {
      db_io_code_log(mname, "null or empty[profile_name]", 
                     DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   /* if link list exists, delete it */

   if (iex_ihead != IEX_OT_NULL)
      iexlib_stop();

   /* open the iex config table and find the config record */

   if ((ret = db_open(IEX_CONFIG_TABLE_NAME, &tid)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_open[iex.config]", ret);
      return(ret);
      }

   if ((ret = db_find_field(tid, 0, profile_name, 
       IEX_CONFIG_FIELD_PROFILE_NAME)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_find_field[profile_name]", ret);
      (void)db_close(tid);
      return(ret);
      }

   /* get the number of fields in the config record, must have min */

   if ((ret = db_get_nfields(tid, &count)) != DBENG_OK)       
      {
      db_io_code_log(mname, "bad rc from db_get_nfields[profile_name]", ret);
      (void)db_close(tid);
      return(ret);
      }

   if (count < IEX_CONFIG_MIN_ALLOW_FIELDS)
      {
      db_io_code_log(mname, "insufficient number of fields[profile_name]",
                     DBENG_INVALID_FUNCTION);
      (void)db_close(tid);
      return(DBENG_INVALID_FUNCTION);
      }
      
   /* get and load module globals */
   /* import or export */

   if ((ret = db_get_field(tid, IEX_CONFIG_FIELD_TYPE, fdat)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_get_field[profile_name,type]", ret);
      (void)db_close(tid);
      return(ret);
      }

   switch(fdat[0])
      {
      case 'i':
      case 'I':
         iex_is_export = FALSE;
         break;

      case 'x':
      case 'X':
         iex_is_export = TRUE;
         break;

      default:
         db_io_code_log(mname, "unknown iex type", DBENG_INVALID_FUNCTION);
         (void)db_close(tid);
         return(DBENG_INVALID_FUNCTION);
      };

   /* fixed or delimited with delimiter */

   if ((ret = db_get_field(tid, IEX_CONFIG_FIELD_FORMAT, fdat)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_get_field[profile_name,format]", 
                     ret);
      (void)db_close(tid);
      return(ret);
      }

   if (fdat[0] == 'f' || fdat[0] == 'F')
      {
      iex_is_fixed = TRUE;
      iex_ascii_delim = EOS;
      }
   else
      {
      iex_is_fixed = FALSE;
      iex_ascii_delim = fdat[0];
      }

   /* auto record locate/create */

   if ((ret = db_get_field(tid, IEX_CONFIG_FIELD_AUTO_REC, fdat)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_get_field[profile_name,auto_rec]",
                     ret);
      (void)db_close(tid);
      return(ret);
      }

   if (!strlen(fdat))
      {
      db_io_code_log(mname, "empty profile field[auto_rec]", 
                     DBENG_INVALID_FUNCTION);
      (void)db_close(tid);
      return(DBENG_INVALID_FUNCTION);
      }

   if (!qatoi(fdat, &iex_auto_rec))
      {
      db_io_code_log(mname, "bad rc[FALSE] from qatoi[auto_rec]",
                     DBENG_INVALID_FUNCTION);
      (void)db_close(tid);
      return(DBENG_INVALID_FUNCTION);
      }

   /* 'bbuuzzb' field number to match */

   if (count >= IEX_CONFIG_FIELD_MATCH)
      {
      if ((ret = db_get_field(tid, IEX_CONFIG_FIELD_MATCH, fdat)) != DBENG_OK)
         {
         db_io_code_log(mname, "bad rc from db_get_field[profile_name,match]", 
                        ret);
         (void)db_close(tid);
         return(ret);
         }

      if (strlen(fdat))
         {
         if (!qatoi(fdat, &iex_match_field))
            {
            db_io_code_log(mname, "bad rc[FALSE] from qatoi[match_field]",
                           DBENG_INVALID_FUNCTION);
            (void)db_close(tid);
            return(DBENG_INVALID_FUNCTION);
            }
         }
      else
         iex_match_field = IEXLIB_LL_PARM_NULL;
      }
   else
      iex_match_field = IEXLIB_LL_PARM_NULL;

   /* ascii null field signature */

   if (count > IEX_CONFIG_FIELD_MATCH)
      {
      if ((ret = db_get_field(tid, IEX_CONFIG_FIELD_NULL, fdat)) != DBENG_OK)
         {
         db_io_code_log(mname, 
                        "bad rc from db_get_field[profile_name,null_field]", 
                        ret);
         (void)db_close(tid);
         return(ret);
         }

      if (strlen(fdat))
         strcpy(iex_null_field, fdat);
      else
         iex_null_field[0] = EOS;
      }
   else
      iex_null_field[0] = EOS;

   /* load iex specs into link list */
   /* get number of specs (subfields) */

   if ((ret = db_get_nsubfields(tid, IEX_CONFIG_FIELD_SPECS, &count)) !=
       DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_get_nsubfields[specs]", 
                     ret);
      (void)db_close(tid);
      return(ret);
      }

   size = sizeof(struct iex_spec);

   for(i = 1; i <= count; i++)
      {
      /* allocate a new node */

      if ((ot = (struct iex_spec *)malloc(size)) == IEX_OT_NULL)
         {
         db_io_code_log(mname, "alloc fail[ot]", DBENG_MEMORY_FAIL);
         (void)db_close(tid);
         iexlib_stop();
         return(DBENG_MEMORY_FAIL);
         }

      ot->next = IEX_OT_NULL;
      ot->source = ot->source_add1 = ot->source_add2 = ot->conversion =
                   ot->conversion_parm = ot->dest = ot->dest_add1 =
                   ot->dest_add2 = IEXLIB_LL_PARM_NULL;

      /* load the source field of this spec */

      if ((ret = iexlib_config_set_sd(1, tid, i, ot)) != DBENG_OK)
         {
         db_io_code_log(mname, "bad rc from iexlib_conifg_set_sd[source]", ret);
         (void)db_close(tid);
         free(ot);
         iexlib_stop();
         return(ret);
         }

      /* load the conversion spec */

      if ((ret = iexlib_config_set_conv(tid, i, ot)) != DBENG_OK)
         {
         db_io_code_log(mname, "bad rc from iexlib_config_set_conv", ret);
         (void)db_close(tid);
         free(ot);
         iexlib_stop();
         return(ret);
         }

      /* load the dest field */

      if ((ret = iexlib_config_set_sd(0, tid, i, ot)) != DBENG_OK)
         {
         db_io_code_log(mname, "bad rc from iexlib_conifg_set_sd[dest]", ret);
         (void)db_close(tid);
         free(ot);
         iexlib_stop();
         return(ret);
         }

      /* set head of list if this is the first entry */

      if (iex_ihead == IEX_OT_NULL)
         iex_ihead = ot;
      else
         {
         /* find last entry and hook into it */

         rov = iex_ihead;

         while(rov->next != IEX_OT_NULL)
            rov = rov->next;

         rov->next = ot;
         }
      }

   (void)db_close(tid);

#ifdef DEBUG
   iexlib_dump();
#endif

   return(DBENG_OK);
}

void iexlib_stop(void)
{
   /* Delete the iex spec link list and set module globals to zero/null. */

   struct iex_spec *tmp, *rov = iex_ihead;

   iex_is_export = iex_is_fixed = iex_auto_rec = FALSE;
   iex_match_field = IEXLIB_LL_PARM_NULL;
   iex_ascii_delim = iex_null_field[0] = EOS;

   while(rov != IEX_OT_NULL)
      {
      tmp = rov->next;
      free(rov);
      rov = tmp;
      }

   iex_ihead = IEX_OT_NULL;
}

int iexlib_active(void)
{
   /* Determine whether there is a current iex profile loaded into
      the link list. Function returns 'TRUE' if there is a profile
      loaded, 'FALSE' otherwise. */

   if (iex_ihead != IEX_OT_NULL)
      return(TRUE);

   return(FALSE);
}

void iexlib_dump(void)
{
   /* Output the contents of the global data and the iex link list to the
      log manager. Note that logging must already be turned on. */

   struct iex_spec *rov = iex_ihead;
   char mname[] = "iexlib_dump";
   int i = 1;

   if (iex_is_fixed)
      logman("%s:is_export=%d,is_fixed=%d,auto_rec=%d,match_field=%d"
                    ",null_field=%s", mname, iex_is_export, iex_is_fixed, 
                    iex_auto_rec, iex_match_field, iex_null_field);
   else
      logman("%s:is_export=%d,is_fixed=%d,ascii_delim=%c,%d,auto_rec=%d"
                    ",match_field=%d,null_field=%s", mname, iex_is_export, 
                    iex_is_fixed, iex_ascii_delim, iex_ascii_delim, 
                    iex_auto_rec, iex_match_field, iex_null_field);

   if (rov == IEX_OT_NULL)
      {
      logman("%s:iex link list empty", mname);
      return;
      }

   while(rov != IEX_OT_NULL)
      {
      logman("%s:spec[%d]:source=%d,source_add1=%d,source_add2=%d,"
                    "conv=%d,conv_parm=%d,dest=%d,dest_add1=%d,dest_add2=%d",
                    mname, i++, rov->source, rov->source_add1, rov->source_add2,
                    rov->conversion, rov->conversion_parm, rov->dest,
                    rov->dest_add1, rov->dest_add2);
      rov = rov->next;
      }
}

int iexlib_import_do_line(int out_tid, char *line)
{
   /* Import an entire line from the input ascii file into the
      'bbuuzzb' table. Function returns a 'dbeng' code. */

   struct iex_spec *rov = iex_ihead;
   char mname[] = "iexlib_import_do_line", *ascii_field;
   int ret;

   if (line == (char *)NULL || !strlen(line))
      {
      /* null or empty line is not fatal */

      db_io_code_log(mname, "null or empty[line]", DBENG_OK);
      return(DBENG_OK);
      }

   if (rov == IEX_OT_NULL)
      {
      db_io_code_log(mname, "no spec link list", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   /* if the auto record locate/create switch is on, attempt to locate
      an existing bbuuzzb record if there is a match field, if the locate
      fails, create a new output record, if the auto switch is on and there
      is no match field, create a new output record, if the auto switch
      is off, assume the caller has already set the bbuuzzb file position
      and do nothing */

   if (iex_auto_rec)
      if (iex_match_field != IEXLIB_LL_PARM_NULL)
         {
         if ((ret = iexlib_import_match(out_tid, line)) != DBENG_OK)
            {
            /* if match fails, create a new 'bbuuzzb' record */

            logman("%s:no match,creating new record", mname);
            (void)db_new(out_tid);
            }
         else
            logman("%s:found matching bbuuzzb record", mname);
         }
      else
         (void)db_new(out_tid);

   if ((ascii_field = initstring(line)) == (char *)NULL)
      {
      db_io_code_log(mname, "alloc fail[ascii_field]", DBENG_MEMORY_FAIL);
      return(DBENG_MEMORY_FAIL);
      }

   ascii_field[0] = EOS;

   /* loop through all the specs, for each spec, get the ascii
      field, perform the conversion and put to the bbuuzzb record */

   while(rov != IEX_OT_NULL)
      {
      if ((ret = iexlib_import_get_ascii_field(line, ascii_field, rov)) !=
          DBENG_OK)
         {
         // if the ascii field cannot be obtained, skip the spec
   
         logman("%s:cannot get ascii field data[f=%s,dest=%d]",
                       mname, ascii_field, rov->dest);
         rov = rov->next;
         continue;
         }

      /* if the field is empty, skip */

      if (!strlen(ascii_field))
         {
         logman("%s:empty field[dest=%d]", mname, rov->dest);
         rov = rov->next;
         continue;
         }

      /* if there is a null signature present and it matches, skip */

      if (strlen(iex_null_field))
         // compare only to length of null signature

         if (!strncmp(ascii_field, iex_null_field, strlen(iex_null_field)))
            {
            logman("%s:null field[dest=%d]", mname, rov->dest);
            rov = rov->next;
            continue;
            }

      /* logman("%s:ascii field[%d,%d]=%s,%d", mname, rov->source,
                    rov->source_add1, ascii_field, strlen(ascii_field)); */

      switch(rov->conversion)
         {
         case IEX_CONV_TYPE_IFXP:
            ret = iexlib_import_do_fxp(out_tid, ascii_field, rov);
            break;

         case IEX_CONV_TYPE_ITIME:
            ret = iexlib_import_do_time(out_tid, ascii_field, rov);
            break;

         case IEX_CONV_TYPE_IINT:
            ret = iexlib_import_do_int(out_tid, ascii_field, rov);
            break;

         case IEX_CONV_TYPE_IPINT:
            ret = iexlib_import_do_pint(out_tid, ascii_field, rov);
            break;

         case IEX_CONV_TYPE_IWINDDIR:
            ret = iexlib_import_do_wind_dir(out_tid, ascii_field, rov);
            break;

         case IEX_CONV_TYPE_IMONTH:
            ret = iexlib_import_do_month(out_tid, ascii_field, rov);
            break;

         default:
            // no conversion

            ret = iexlib_import_put_data(out_tid, ascii_field, rov);
         };

      if (ret != DBENG_OK)
         logman("%s:bad rc from do[conv=%d,dest=%d]", mname,
                       rov->conversion, rov->dest);

      rov = rov->next;
      }      

   return(DBENG_OK);
}

// private functions

static int iexlib_config_set_sd(int is_source, int tid, int rsf,
                                struct iex_spec *ot)
{
   /* Load the source or destination field specification into the
      link list node. Function returns a 'dbeng' code. */

   char mname[] = "iexlib_config_set_sd", fdat[25];
   int ret, f, sf, ssf;

   ssf = (is_source) ? IEX_CONFIG_SUBSUBFIELD_SOURCE :
         IEX_CONFIG_SUBSUBFIELD_DEST;

   if ((ret = db_get_subsubfield(tid, IEX_CONFIG_FIELD_SPECS, rsf, ssf, fdat))
       != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_get_subsubfield", ret);
      return(ret);
      }

   if ((ret = iexlib_config_get_sd(fdat, &f, &sf, &ssf)) !=
       DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from iexlib_config_get_sd", ret);
      return(ret);
      }

   if (is_source)
      {
      ot->source = f;
      ot->source_add1 = sf;
      ot->source_add2 = ssf;
      }
   else
      {
      ot->dest = f;
      ot->dest_add1 = sf;
      ot->dest_add2 = ssf;
      }

   return(DBENG_OK);
}

static int iexlib_config_set_conv(int tid, int rsf, struct iex_spec *ot)
{
   /* Load the conversion spec into the link list node. Function 
      returns a 'dbeng' code. */

   char mname[] = "iexlib_config_set_conv", fdat[25], parm[25];
   int ret, nwords;

   if ((ret = db_get_subsubfield(tid, IEX_CONFIG_FIELD_SPECS, rsf, 
                                 IEX_CONFIG_SUBSUBFIELD_CONV, fdat))
       != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_get_subsubfield", ret);
      return(ret);
      }

   /* separate out keyword and any additional parameter */

   nwords = ll_words(fdat, IEXLIB_CONFIG_SPEC_SEP);

   /* if the conversion subsubfield is empty, assume no conversion */

   if (!nwords)
      {
      db_io_code_log(mname, "exit:no conv spec,assuming no conv", DBENG_OK);
      ot->conversion = ot->conversion_parm = IEX_CONV_TYPE_UNKNOWN;
      return(DBENG_OK);
      }

   if (nwords > 2)
      {
      db_io_code_log(mname, "improper number of conversion parms",
                     DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (nwords > 1)
      {
      if (!ll_word(fdat, parm, 1, IEXLIB_CONFIG_SPEC_SEP))
         {
         db_io_code_log(mname, "bad rc from ll_word[1]", DBENG_INTERNAL_ERROR);
         return(DBENG_INTERNAL_ERROR);
         }
      }
   else
      {
      strcpy(parm, fdat);
      ot->conversion_parm = IEXLIB_LL_PARM_NULL;
      }

   if ((ret = iexlib_import_conv_get_token(parm)) == IEX_CONV_TYPE_UNKNOWN)
      {
      logman("%s:exit:unknown conv type[%s]", mname, parm);
      return(DBENG_INVALID_FUNCTION);
      }

   ot->conversion = ret;

   if (nwords > 1)
      {
      if (!ll_word(fdat, parm, 2, IEXLIB_CONFIG_SPEC_SEP))
         {
         db_io_code_log(mname, "bad rc from ll_word[2]", DBENG_INTERNAL_ERROR);
         return(DBENG_INTERNAL_ERROR);
         }

      if (!qatoi(parm, &ret))
         {
         db_io_code_log(mname, "non-numeric[conv parm]",
                        DBENG_INVALID_FUNCTION);
         return(DBENG_INVALID_FUNCTION);
         }

      ot->conversion_parm = ret;
      }

   return(DBENG_OK);
}

static int iexlib_config_get_sd(char *fdat, int *f, int *sf, int *ssf)
{
   /* Parse a source or destination field spec into its
      component parts. Function returns a 'dbeng' code. */

   char mname[] = "iexlib_config_get_sd", wrd[25];
   int nelements, i, num_wrd;

   nelements = ll_words(fdat, IEXLIB_CONFIG_SPEC_SEP);
   *f = *sf = *ssf = IEXLIB_LL_PARM_NULL;
 
   if (!nelements)
      {
      db_io_code_log(mname, "empty[fdat]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (nelements == 1)
      {
      if (!qatoi(fdat, f))
         {
         db_io_code_log(mname, "non-numeric[fdat]", DBENG_INVALID_FUNCTION);
         return(DBENG_INVALID_FUNCTION);
         }

      return(DBENG_OK);
      }

   for(i = 1; i <= nelements; i++)
      {
      if (!ll_word(fdat, wrd, i, IEXLIB_CONFIG_SPEC_SEP))
         {
         logman("%s:exit:bad rc from ll_word[fdat=%s,wrd=%s,i=%d]",
                       mname, fdat, wrd, i);
         return(DBENG_INTERNAL_ERROR);
         }

      if (!qatoi(wrd, &num_wrd))
         {
         logman("%s:exit:non-numeric[fdat=%s,wrd=%s,i=%d]",
                       mname, fdat, wrd, i);
         return(DBENG_INVALID_FUNCTION);
         }

      switch(i)
         {
         case 1:
            *f = num_wrd;
            break;

         case 2:
            *sf = num_wrd;
            break;

         case 3:
            *ssf = num_wrd; 
            break;

         default:
            logman("%s:exit:unknown parameter[fdat=%s,wrd=%s,i=%d]",
                          mname, fdat, wrd, i);
            return(DBENG_INVALID_FUNCTION);
         };
      }

   return(DBENG_OK);
}

static int iexlib_import_get_ascii_field(char *line, char *outfield, 
                                         struct iex_spec *ot)
{
   /* Obtain a field within the ascii import line. The field data
      is retrieved based on whether the input format
      is fixed or delimited (contained in the globals). The pointer
      'ot' must point to a valid iex specification and the field
      will be obtained from this spec and placed into 'outfield' 
      upon success. Function returns a 'dbeng' code. */

   char mname[] = "iexlib_import_get_ascii_field", *tfield;
   int nwords, len, ret;

   if (line == (char *)NULL || !strlen(line))
      {
      db_io_code_log(mname, "null or empty[line]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (outfield == (char *)NULL)
      {
      db_io_code_log(mname, "null[outfield]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   outfield[0] = EOS;

   if (ot == IEX_OT_NULL)
      {
      db_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   /* validate source field spec */

   if (ot->source == IEXLIB_LL_PARM_NULL)
      {
      db_io_code_log(mname, "ot->source[null]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (iex_is_fixed)
      {
      len = strlen(line);

      if (ot->source_add1 == IEXLIB_LL_PARM_NULL)
         {
         db_io_code_log(mname, "ot->source_add1[null]", DBENG_INVALID_FUNCTION);
         return(DBENG_INVALID_FUNCTION);
         }

      if (ot->source_add1 < 1)
         {
         db_io_code_log(mname, "ot->source_add1[out of range]", 
                        DBENG_INVALID_FUNCTION);
         return(DBENG_INVALID_FUNCTION);
         }
       
      if (ot->source > len || (ot->source + ot->source_add1) > len)
         {
         db_io_code_log(mname, "out of range source", DBENG_INVALID_FUNCTION);
         return(DBENG_INVALID_FUNCTION);
         }
      }
   else
      {
      nwords = ll_words(line, iex_ascii_delim);

      if (ot->source > nwords)
         {
         db_io_code_log(mname, "out of range source", DBENG_INVALID_FUNCTION);
         return(DBENG_INVALID_FUNCTION);
         }
      }   

   if ((tfield = initstring(line)) == (char *)NULL)
      {
      db_io_code_log(mname, "alloc fail[tfield]", DBENG_MEMORY_FAIL);
      return(DBENG_MEMORY_FAIL);
      }

   /* obtain the field data */

   if (iex_is_fixed)
      {
      strncpy(tfield, &line[ot->source], ot->source_add1);
      tfield[ot->source_add1] = EOS;
      }
   else
      {
      if (!ll_word(line, tfield, ot->source, iex_ascii_delim))
         {
         db_io_code_log(mname, "bad rc from ll_word", DBENG_INTERNAL_ERROR);
         free(tfield);
         return(DBENG_INTERNAL_ERROR);
         }
      }

   trim(tfield, outfield);
   free(tfield);
   return(DBENG_OK);
}

static int iexlib_import_match(int tid, char *line)
{
   /* Attempt to match the input ascii field with 'bbuuzzb' data
      by applying the specified conversion to the input ascii field
      and attempting to locate a 'bbuuzzb' record with the matching
      data. Function returns a 'dbeng' code. */

   static struct iex_spec *iex_import_match = IEX_OT_NULL;
   struct iex_spec *rov = iex_ihead;
   char mname[] = "iexlib_import_match", *in_field, *out_field;
   int ret;

   if (rov == IEX_OT_NULL)
      {
      db_io_code_log(mname, "no spec link list", DBENG_INTERNAL_ERROR);
      return(DBENG_INTERNAL_ERROR);
      }

   /* locate matching spec struct in the link list only if this 
      pointer is NULL */

   if (iex_import_match == IEX_OT_NULL)
      {
      while(rov != IEX_OT_NULL)
         {
         if (rov->dest == iex_match_field)
            {
            iex_import_match = rov;
            break;
            }

         rov = rov->next;
         }

      if (iex_import_match == IEX_OT_NULL)
         {
         db_io_code_log(mname, "not found[match_field]", DBENG_NO_SUCH_FIELD);
         return(DBENG_NO_SUCH_FIELD);
         }
      }

   if ((in_field = initstring(line)) == (char *)NULL)
      {
      db_io_code_log(mname,"alloc fail[in_field]", DBENG_MEMORY_FAIL);
      return(DBENG_MEMORY_FAIL);
      }

   if ((out_field = initstring(line)) == (char *)NULL)
      {
      db_io_code_log(mname,"alloc fail[out_field]", DBENG_MEMORY_FAIL);
      return(DBENG_MEMORY_FAIL);
      }

   in_field[0] = out_field[0] = EOS;

   /* get the raw ascii input field */

   if ((ret = iexlib_import_get_ascii_field(line, in_field, iex_import_match))
       != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from iexlib_import_get_ascii_field", ret);
      free(in_field);
      free(out_field);
      return(ret);
      }

   /* unknown conversion is a signal to not apply any conversion */

   if (iex_import_match->conversion == IEX_CONV_TYPE_UNKNOWN)
      {
      logman("%s:conv[unknown]:no conv applied", mname);
      strcpy(out_field, in_field);
      }
   else
      {
      /* apply conversion */

      switch(iex_import_match->conversion)
         {
         case IEX_CONV_TYPE_IFXP:
            ret = iexlib_import_conv_fxp(in_field, iex_import_match, out_field);
            break;

         case IEX_CONV_TYPE_ITIME:
            ret = iexlib_import_conv_time(in_field, out_field);
            break;

         case IEX_CONV_TYPE_IINT:
            ret = iexlib_import_conv_int(in_field, iex_import_match, 
                                         out_field);
            break;

         case IEX_CONV_TYPE_IPINT:
            ret = iexlib_import_conv_pint(in_field, iex_import_match, 
                                         out_field);
            break;

         case IEX_CONV_TYPE_IWINDDIR:
            ret = iexlib_import_conv_wind_dir(in_field, iex_import_match,
                                              out_field);
            break;

         case IEX_CONV_TYPE_IMONTH:
            ret = iexlib_import_conv_month(in_field, out_field);
            break;

         default:
            db_io_code_log(mname, "unknown conv", DBENG_INVALID_FUNCTION);
            return(DBENG_INVALID_FUNCTION);
         };
      }

   if (!ret)
      {
      db_io_code_log(mname, "conv failed", DBENG_INVALID_FUNCTION);
      free(in_field);
      free(out_field);
      return(DBENG_INVALID_FUNCTION);
      }

   /* attempt to locate the 'bbuuzzb' record with the matching data */

   (void)db_top(tid);
   ret = db_find_field(tid, 0, out_field, iex_import_match->dest);
   free(in_field);
   free(out_field);
   return(ret);
}

static int iexlib_import_do_wind_dir(int out_tid, char *in_field, 
                                     struct iex_spec *ot)
{
   /* Convert the wind direction from alpha to degrees and place into
      the 'bbuuzzb' record. Function returns a 'dbeng' code. */

   struct fxp *result;
   char mname[] = "iexlib_import_do_wind_dir";
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      db_io_code_log(mname, "null or empty[in_field]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (ot == IEX_OT_NULL)
      {
      db_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   /* convert the field to wind direction in degrees */

   if (!wlib_wdir_desc_2_deg(in_field, &result))
      {
      logman("%s:bad rc[FALSE] from wlib_wdir_desc_2_deg[%s]", 
                    mname, in_field);
      return(DBENG_INVALID_FUNCTION);
      }

   /* place the 'fxp' data into the 'bbuuzzb' record */

   if ((ret = iexlib_import_put_fxp(out_tid, result, ot)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from iexlib_import_put_fxp", ret);
      fxp_free(result);
      return(ret);
      }

   fxp_free(result);
   return(DBENG_OK);
}

static int iexlib_import_do_time(int out_tid, char *in_field, 
                                 struct iex_spec *ot)
{
   /* Convert a time field (in the form hh:mm[a|p]) to 24 hour
      and place into a 'bbuuzzb' record. Function returns a
      'dbeng' code. */

   char mname[] = "iexlib_import_do_time", out_field[100];
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      db_io_code_log(mname, "null or empty[in_field]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (ot == IEX_OT_NULL)
      {
      db_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   /* convert the time field to 24hr clock */

   if (!iexlib_import_conv_time(in_field, out_field))
      {
      logman("%s:bad rc[FALSE] from iexlib_import_conv_time[%s]", mname,
                    in_field);
      return(DBENG_INVALID_FUNCTION);
      }

   if ((ret = iexlib_import_put_data(out_tid, out_field, ot)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from iexlib_import_put_data", ret);
      return(ret);
      }

   return(DBENG_OK);
}

static int iexlib_import_do_fxp(int out_tid, char *in_field,
                                 struct iex_spec *ot)
{
   /* Convert an 'fxp' field and place it into the 'bbuuzzb' record. 
      Function returns a 'dbeng' code. */

   struct fxp *result;
   char mname[] = "iexlib_import_do_fxp";
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      db_io_code_log(mname, "null or empty[in_field]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (ot == IEX_OT_NULL)
      {
      db_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if ((result = fxp_iconv(in_field)) == FXP_OT_NULL)
      {
      logman("%s:bad rc[NULL] from fxp_iconv[%s]", mname, in_field);
      return(DBENG_INVALID_FUNCTION);
      }

   /* place the 'fxp' data into the 'bbuuzzb' record */

   if ((ret = iexlib_import_put_fxp(out_tid, result, ot)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from iexlib_import_put_fxp", ret);
      fxp_free(result);
      return(ret);
      }

   fxp_free(result);
   return(DBENG_OK);
}

static int iexlib_import_do_int(int out_tid, char *in_field, 
                                struct iex_spec *ot)
{
   /* Convert an integer and place into the 'bbuuzzb' record. 
      Function returns a 'dbeng' code. */

   char mname[] = "iexlib_import_do_int", out_field[100];
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      db_io_code_log(mname, "null or empty[in_field]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (ot == IEX_OT_NULL)
      {
      db_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (!iexlib_import_conv_int(in_field, ot, out_field))
      {
      db_io_code_log(mname, "bad rc[FALSE] from iexlib_import_conv_int",
                     DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if ((ret = iexlib_import_put_data(out_tid, out_field, ot)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from iexlib_import_put_data", ret);
      return(ret);
      }

   return(DBENG_OK);
}

static int iexlib_import_do_pint(int out_tid, char *in_field, 
                                 struct iex_spec *ot)
{
   /* Convert an integer plus a constant and place into the 'bbuuzzb' record. 
      Function returns a 'dbeng' code. */

   char mname[] = "iexlib_import_do_pint", out_field[100];
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      db_io_code_log(mname, "null or empty[in_field]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (ot == IEX_OT_NULL)
      {
      db_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (!iexlib_import_conv_pint(in_field, ot, out_field))
      {
      db_io_code_log(mname, "bad rc[FALSE] from iexlib_import_conv_pint",
                     DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if ((ret = iexlib_import_put_data(out_tid, out_field, ot)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from iexlib_import_put_data", ret);
      return(ret);
      }

   return(DBENG_OK);
}

static int iexlib_import_do_month(int out_tid, char *in_field, 
                                  struct iex_spec *ot)
{
   /* Convert a month name (3 chars) to a month number (base 1)
      and place into a 'bbuuzzb' record. Function returns a
      'dbeng' code. */

   char mname[] = "iexlib_import_do_month", out_field[3];
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      db_io_code_log(mname, "null or empty[in_field]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (ot == IEX_OT_NULL)
      {
      db_io_code_log(mname, "null[ot]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   /* convert the month to a number */

   if (!iexlib_import_conv_month(in_field, out_field))
      {
      logman("%s:bad rc[FALSE] from iexlib_import_conv_month[%s]", mname,
                    in_field);
      return(DBENG_INVALID_FUNCTION);
      }

   if ((ret = iexlib_import_put_data(out_tid, out_field, ot)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from iexlib_import_put_data", ret);
      return(ret);
      }

   return(DBENG_OK);
}

static int iexlib_import_conv_wind_dir(char *in_field, struct iex_spec *ot,
                                       char *out_field)
{
   /* Convert the wind direction from alpha to degrees. Function
      returns 'TRUE' upon success with the result loaded into
      'out_field', 'FALSE' otherwise. Note that the result is
      in 'bbuuzzb' 'fxp' format (no decimal and padded on the
      right with zeros to the desired precision or scale). This
      function is used only to match records ('iexlib_import_match'). */

   struct fxp *result;
   char mname[] = "iexlib_import_conv_wind_dir";
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      logman("%s:exit[FALSE]:null or empty[in_field]", mname);
      return(FALSE);
      }

   if (ot == IEX_OT_NULL)
      {
      logman("%s:null[ot]", mname);
      return(FALSE);
      }

   if (out_field == (char *)NULL)
      {
      logman("%s:null[out_field]", mname);
      return(FALSE);
      }

   out_field[0] = EOS;

   /* convert the field to wind direction in degrees */

   if (!wlib_wdir_desc_2_deg(in_field, &result))
      {
      logman("%s:bad rc[FALSE] from wlib_wdir_desc_2_deg[%s]", 
                    mname, in_field);
      return(FALSE);
      }

   /* use the raw fxp number string */

   strcpy(out_field, result->charval);
   fxp_free(result);
   return(TRUE);
}

static int iexlib_import_conv_time(char *in_field, char *out_field)
{
   /* Convert a time field (in the form hh:mm[a|p]) to 24 hour.
      Function returns 'TRUE' upon success, 'FALSE' otherwise. */

   char mname[] = "iexlib_import_conv_time";
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      logman("%s:null or empty[in_field]", mname);
      return(FALSE);
      }

   if (out_field == (char *)NULL)
      {
      logman("%s:null[out_field]", mname);
      return(FALSE);
      }

   out_field[0] = EOS;

   /* convert the time field to 24hr clock */

   if (!datime_hour12_2_hour24(in_field, out_field, FALSE))
      {
      logman("%s:bad rc[FALSE] from datime_hour12_2_hour24[%s]", mname,
                    in_field);
      return(FALSE);
      }

   return(TRUE);
}

static int iexlib_import_conv_int(char *in_field, struct iex_spec *ot,
                                  char *out_field)
{
   /* Convert an integer. Function returns 'TRUE' upon success with
      the result loaded into 'out_field'. Function returns 'FALSE'
      otherwise. */

   char mname[] = "iexlib_import_conv_int";
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      logman("%s:null or empty[in_field]", mname);
      return(FALSE);
      }

   if (out_field == (char *)NULL)
      {
      logman("%s:null[out_field]", mname);
      return(FALSE);
      }

   out_field[0] = EOS;

   /* make sure data is numeric */

   if (!qatoi(in_field, &ret))
      {
      logman("%s:bad rc[FALSE] from qatoi[%s]", mname, in_field);
      return(FALSE);
      }

   /* pad on left with zeros if requested */

   if (ot->conversion_parm != IEXLIB_LL_PARM_NULL)
      lpad(in_field, out_field, ot->conversion_parm, '0');
   else
      strcpy(out_field, in_field);

   return(TRUE);
}

static int iexlib_import_conv_pint(char *in_field, struct iex_spec *ot, 
                                   char *out_field)
{
   /* Convert a plus integer, which is an integer plus a constant. 
      Function returns 'TRUE' upon success with the result loaded 
      into 'out_field'. Function returns 'FALSE' otherwise. */

   char mname[] = "iexlib_import_conv_pint";
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      logman("%s:null or empty[in_field]", mname);
      return(FALSE);
      }

   if (out_field == (char *)NULL)
      {
      logman("%s:null[out_field]", mname);
      return(FALSE);
      }

   out_field[0] = EOS;

   if (ot == IEX_OT_NULL)
      {
      logman("%s:null[ot]", mname);
      return(FALSE);
      }

   /* make sure data is numeric */

   if (!qatoi(in_field, &ret))
      {
      logman("%s:bad rc[FALSE] from qatoi[%s]", mname, in_field);
      return(FALSE);
      }

   /* make sure a parm (the constant) was supplied */

   if (ot->conversion_parm == IEXLIB_LL_PARM_NULL)
      {
      logman("%s:no constant supplied", mname);
      return(FALSE);
      }

   ret += ot->conversion_parm;
   sprintf(out_field, "%d", ret);
   return(TRUE);
}

static int iexlib_import_conv_fxp(char *in_field, struct iex_spec *ot,
                                  char *out_field)
{
   /* Convert an fxp number. Function returns 'TRUE' upon success with 
      the result loaded into 'out_field', 'FALSE' otherwise. Note that 
      the result is in 'bbuuzzb' 'fxp' format (no decimal and padded on the
      right with zeros to the desired precision or scale). This
      function is used only to match records ('iexlib_import_match'). */

   struct fxp *result;
   char mname[] = "iexlib_import_conv_fxp";
   int ret, scale;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      logman("%s:exit[FALSE]:null or empty[in_field]", mname);
      return(FALSE);
      }

   if (ot == IEX_OT_NULL)
      {
      logman("%s:null[ot]", mname);
      return(FALSE);
      }

   if (out_field == (char *)NULL)
      {
      logman("%s:null[out_field]", mname);
      return(FALSE);
      }

   out_field[0] = EOS;

   /* convert the number from a string to fxp */

   if ((result = fxp_iconv(in_field)) == FXP_OT_NULL)
      {
      logman("%s:bad rc[NULL] from fxp_iconv[%s]", 
                    mname, in_field);
      return(FALSE);
      }

   scale = (ot->conversion_parm == IEXLIB_LL_PARM_NULL) ? 0 :
           ot->conversion_parm;

   /* force the 'fxp' number to the specified scale */

   if (!fxp_scale(result, scale))
      {
      fxp_free(result);
      logman("%s:bad rc[FALSE] from fxp_scale", mname);
      return(FALSE);
      }

   /* use the raw fxp number string */

   strcpy(out_field, result->charval);
   fxp_free(result);
   return(TRUE);
}

static int iexlib_import_conv_month(char *in_field, char *out_field)
{
   /* Convert a month name (3 chars) to a month number zero padded
      to two digits. Function returns 'TRUE' with the month number 
      loaded into 'out_field' upon success. Function returns 'FALSE'
      otherwise. */

   char mname[] = "iexlib_import_conv_month", tmp[3];
   int ret;

   if (in_field == (char *)NULL || !strlen(in_field))
      {
      logman("%s:null or empty[in_field]", mname);
      return(FALSE);
      }

   if (out_field == (char *)NULL)
      {
      logman("%s:null[out_field]", mname);
      return(FALSE);
      }

   out_field[0] = EOS;

   if ((ret = datime_month_name_2_num(in_field)) == 0)
      {
      logman("%s:bad rc[FALSE] from datime_month_name_2_num[%s]",
                    mname, in_field);
      return(FALSE);
      }

   sprintf(tmp, "%d", ret);
   lpad(tmp, out_field, 2, '0');
   return(TRUE);
}

static int iexlib_import_put_fxp(int out_tid, struct fxp *dat, 
                                 struct iex_spec *ot)
{
   /* Place 'fxp' data into a 'bbuuzzb' record. Function returns a
      'dbeng' code. */

   char mname[] = "iexlib_import_put_fxp";
   int ret, scale;

   if (ot->dest == IEXLIB_LL_PARM_NULL)
      {
      db_io_code_log(mname, "out of range[ot->dest]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   /* if no scale was supplied, assume zero (int) unless we are
      processing a windir direction in which case we use a
      pre-defined scale */

   if (ot->conversion == IEX_CONV_TYPE_IWINDDIR)
      scale = IEXLIB_SCALE_WIND_DIR;
   else
      scale = (ot->conversion_parm == IEXLIB_LL_PARM_NULL) ? 0 :
              ot->conversion_parm;

   if (ot->dest_add1 != IEXLIB_LL_PARM_NULL && ot->dest_add2 !=
       IEXLIB_LL_PARM_NULL)
      {
      if ((ret = dbfxp_put_subsubfield(out_tid, ot->dest, ot->dest_add1,
                                       ot->dest_add2, scale, dat)) !=
          DBENG_OK)
         {
         db_io_code_log(mname, "bad rc from dbfxp_put_subsubfield", ret);
         return(ret);
         }

      return(DBENG_OK);
      }

   if (ot->dest_add1 != IEXLIB_LL_PARM_NULL)
      {
      if ((ret = dbfxp_put_subfield(out_tid, ot->dest, ot->dest_add1,
                                    scale, dat)) != DBENG_OK)
         {
         db_io_code_log(mname, "bad rc from dbfxp_put_subfield", ret);
         return(ret);
         }

      return(DBENG_OK);
      }
 
   if ((ret = dbfxp_put(out_tid, ot->dest, scale, dat)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from dbfxp_put", ret);
      return(ret);
      }

   return(DBENG_OK);
}

static int iexlib_import_put_data(int out_tid, char *dat, struct iex_spec *ot)
{
   /* Place data into a 'bbuuzzb' record. Function returns a
      'dbeng' code. */

   char mname[] = "iexlib_import_put_data";
   int ret;

   if (ot->dest == IEXLIB_LL_PARM_NULL)
      {
      db_io_code_log(mname, "out of range[ot->dest]", DBENG_INVALID_FUNCTION);
      return(DBENG_INVALID_FUNCTION);
      }

   if (ot->dest_add1 != IEXLIB_LL_PARM_NULL && ot->dest_add2 !=
       IEXLIB_LL_PARM_NULL)
      {
      if ((ret = db_put_subsubfield(out_tid, ot->dest, ot->dest_add1,
                                    ot->dest_add2, dat)) != DBENG_OK)
         {
         db_io_code_log(mname, "bad rc from db_put_subsubfield", ret);
         return(ret);
         }

      return(DBENG_OK);
      }

   if (ot->dest_add1 != IEXLIB_LL_PARM_NULL)
      {
      if ((ret = db_put_subfield(out_tid, ot->dest, ot->dest_add1,
                                 dat)) != DBENG_OK)
         {
         db_io_code_log(mname, "bad rc from db_put_subfield", ret);
         return(ret);
         }

      return(DBENG_OK);
      }
 
   if ((ret = db_put_field(out_tid, ot->dest, dat)) != DBENG_OK)
      {
      db_io_code_log(mname, "bad rc from db_put_field", ret);
      return(ret);
      }

   return(DBENG_OK);
}

static int iexlib_import_conv_get_token(char *keywrd)
{
   /* Get conversion keyword token. Function returns a numeric
      representation of the token. */

   int i;
   int token = IEX_CONV_TYPE_UNKNOWN;

   /* static array of conversion words */

   static char *convs[] = { "FXP", "TIME", "INT", "WINDDIR", "MONTH",
                            "PINT" };

   for(i = 0; i < IEX_CONV_IMAXTYPE; i++)
      if (!stricmp(keywrd, convs[i]))
         {
         token = i;
         break;
         }

   return(token);
}
