/* Socket locate config settings handler. This module reads
   the settings in the socket locate config file.
   This config file is a normal ASCII file that
   contains a number of lines, each line describing a
   single socket locate server. Here is the format of the file:

      * comment line
      host_name port_number [ip_address]

   Each line of the config file may contain a comment by either
   placing an asterisk in column one or an asterisk after the
   server definition (there must be at least one space between the
   server definition and the asterisk). The line may also contain
   a 'socloc' server definition. If the 'host_name' contains spaces,
   the name must be enclosed by single or double quotes (must be
   balanced). Note that the IP address is optional. The list of
   socket locate servers will be searched in order that they
   appear in the configuration file.  Rick Smereka,
   Copyright (C) 2000-2004.

   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

   Original version for CodeWarrior V4 under Windows 32bit.
   Feb/2000, Rick Smereka
   
   Added include of header 'ip.h' after moving the function 'isip'.
   Jul/2000, Rick Smereka

   Ported to Debian Linux. Nov/2002, Rick Smereka

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

#include "stdhead.h"
#include "flsocket.h"
#include "slconfig.h"
#include "socloc.h"
#include "ip.h"

#define SL_NULL ((struct sl_config_entry *)NULL)

/* global module data */

struct sl_config_entry *sl_base = SL_NULL;

int sl_config_read(char *fname)
{
   /* Read and parse the socket locate config file 'fname'.
      Function returns a socket locate configuration code
      (defined in 'slconfig.h'). Upon successful completion,
      a link list of available socket locate servers will
      be built. */

   FILE *in;
   char mname[] = "sl_config_read";
   char *line, *host_name, *port_char, *ip;
   int lineno, nwords, port_num, ret;

   logman("%s:enter", mname);

   // reasonableness checks

   if (fname == (char *)NULL || !strlen(fname))
      {
      logman("%s:invalid parameter[fname]", mname);
      return(SL_INVALID_PARAMETER);
      }

	// attempt to open config file

   if ((in = fopen(fname, "r")) == (FILE *)NULL)
	  {
	  logman("%s:unable to open config file[%s]", mname, fname);
      return(SL_CONFIG_UNABLE_TO_OPEN);
      }

	// alloc line buffer

   if ((line = (char *)malloc(BUFSIZE)) == (char *)NULL)
	  {
	  logman("%s:alloc fail[line]", mname);
	  fclose(in);
      return(SL_MEMORY_FAIL);
	  }

	// get first record

	get_rec(in, line);
	lineno = 1;

	while(!feof(in))
	   {
	   /* if line begins with an asterisk in column one, ignore */

	   if (line[0] == '*')
	      {
	      logman("%s:line %d is a comment", mname, lineno);
	      get_rec(in, line);
	      lineno++;
	      continue;
	      }

	   // number of command words on line

	   nwords = command_words(line);

	   // must have at least two command words per record

	   if (nwords < 2)
	      {
	      logman("%s:less than two parameters found on line %d",
	                    mname, lineno);
	      free(line);
          fclose(in);
          return(SL_CONFIG_PARAMETER_MISSING);
 	      }

      /* create host name string by dynamically copying and
         set length to zero */

      host_name = initstring(line);
      host_name[0] = EOS;

	   // get first command word on line

      if (!command_word(line, host_name, 1))
	     {
	     logman("%s:error getting first word on line %d",
	                   mname, lineno);
         free(host_name);
	     free(line);
	     fclose(in);
         return(SL_CONFIG_INTERNAL_PARSE_ERROR);
	     }

      /* get port number */

      port_char = initstring(line);
      port_char[0] = EOS;

      if (!command_word(line, port_char, 2))
         {
         logman("%s:error getting second word on line %d",
	                   mname, lineno);
         free(port_char);
         free(host_name);
         free(line);
         fclose(in);
         return(SL_CONFIG_INTERNAL_PARSE_ERROR);
         }

      // must be a valid decimal number

      if (!qatoi(port_char, &port_num))
         {
         logman("%s:TCP/IP port not numeric on line %d",
                       mname, lineno);
         free(port_char);
         free(host_name);
         free(line);
         fclose(in);
         return(SL_CONFIG_PORT_NOT_NUMERIC);
         }

      free(port_char);
      ip = (char *)NULL;

      /* get IP address if present */

      if (nwords > 2)
         {
         ip = initstring(line);
         ip[0] = EOS;

         if (!command_word(line, ip, 3))
            {
            logman("%s:unexpected error getting IP on line %d",
                          mname, lineno);
            free(ip);
            free(host_name);
            free(line);
            fclose(in);
            return(SL_CONFIG_INTERNAL_PARSE_ERROR);
            }

         if (!isip(ip))
            {
            logman("%s:IP address is invalid on line %d",
                          mname, lineno);
            free(ip);
            free(host_name);
            free(line);
            fclose(in);
            return(SL_BAD_IP);
            }
         }

         /* add server to link list */

         if ((ret = sl_config_add(host_name, port_num, ip)) !=
              SL_OK)
            {
            if (ip != (char *)NULL)
               free(ip);

            free(host_name);
            free(line);
            fclose(in);
            logman("%s:error[%d] adding socloc server entry "
                          "on line %d", mname, ret, lineno);
            return(ret);
            }

      /* dealloc temp vars */

      if (ip != (char *)NULL)
         free(ip);

      free(host_name);

      // get next record

      get_rec(in, line);
      lineno++;
      }

   fclose(in);
   free(line);
   logman("%s:normal exit", mname);
   return(SL_OK);
}

int sl_config_get_first(char *hname, int *port_num, char *ip_ad)
{
   /* Return the first socket locate server detail. Host name and
      ip address (if not NULL) must already be allocated by the
      caller to sufficient size. Note that the config entry for
      the currently running 'socloc' server (denoted by the
      'is_local_server' flag) is automatically excluded.
      Function returns 'SL_OK' upon success, a socket
      locate config code otherwise. */

   struct sl_config_entry *rov = sl_base;
   char mname[] = "sl_config_get_first";
   int ret, found = FALSE;

   logman("%s:enter", mname);

   if (rov == SL_NULL)
      {
      logman("%s:config list empty", mname);
      return(SL_NOT_FOUND);
      }

   hname[0] = EOS;
   *port_num = 0;

   while(rov != SL_NULL)
      {
      if (rov->is_local_server == FALSE)
         {
         strcpy(hname, rov->host_name);
         *port_num = rov->port_number;

         if (ip_ad != (char *)NULL)
            if (rov->ip_addr != (char *)NULL)
               strcpy(ip_ad, rov->ip_addr);
            else
               ip_ad[0] = EOS;

         found = TRUE;
         break;
         }

      rov = rov->next;
      }

   ret = found ? SL_OK : SL_NOT_FOUND;
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sl_config_get_nentries(int *nentries)
{
   /* Get the number of entries in the link list.
      Note that the config entry for the currently
      running 'socloc' server (denoted by the
      'is_local_server' flag) is automatically excluded.
      Function returns 'SL_OK' with the number of entries
      loaded into 'nentries' upon success, a socket
      locate config error otherwise. */

   struct sl_config_entry *rov = sl_base;
   char mname[] = "sl_config_get_nentries";
   int cnt = 0;

   logman("%s:enter", mname);
   *nentries = 0;

   while(rov != SL_NULL)
      {
      if (!rov->is_local_server)
         cnt++;

      rov = rov->next;
      }

   if (!cnt)
      {
      logman("%s:config list empty", mname);
      return(SL_NOT_FOUND);
      }

   *nentries = cnt;
   logman("%s:normal exit", mname);
   return(SL_OK);
}

int sl_config_get_nth(int pos, char *hname, int *port_num, char *ip_ad)
{
   /* Get a specific 'socloc' server entry. Function returns
      'SL_OK' upon success with the host name loaded into 'hname',
      the port number loaded into 'port_num' and the IP address loaded
      into 'ip_ad'. These character variables must previously be
      allocated to sufficient size by the caller.
      Note that the config entry for the currently running 'socloc'
      server (denoted by the 'is_local_server' flag) is
      automatically excluded. Function returns
      a socket locate config error upon failure. */

   struct sl_config_entry *rov;
   char mname[] = "sl_config_get_nth";
   int i, nentries, ret;

   logman("%s:enter", mname);

   /* get total number of entries and validate */

   if ((ret = sl_config_get_nentries(&nentries)) != SL_OK)
      {
      logman("%s:bad rc[%d] from 'sl_config_get_nentries'",
                    mname, ret);
      return(ret);
      }

   if (pos > nentries)
      {
      logman("%s:entry not found", mname);
      return(SL_NOT_FOUND);
      }

   for(rov = sl_base, i = 0; i <= pos; rov = rov->next)
      {
      if (!rov->is_local_server)
         i++;

      if (i == pos)
         {
         strcpy(hname, rov->host_name);
         *port_num = rov->port_number;

         /* if the ip address parameter is null, do not attempt
            to load the ip address even if there is one */

         if (ip_ad == (char *)NULL)
            break;

         if (rov->ip_addr != (char *)NULL)
            strcpy(ip_ad, rov->ip_addr);
         else
            /* if no ip address is present in the entry and the
               ip address parameter is not null, zero out the
               ip address parameter */

            ip_ad[0] = EOS;

         break;
         }
      }

   logman("%s:normal exit", mname);
   return(SL_OK);
}

int sl_config_find(char *hname, int *port_num, char *ip_ad)
{
   /* Find a config entry by one or more details. At least one
      parameter must be given. Upon success, missing details
      will be loaded into parameters. Note that if this function
      is called from a 'socloc' server, that server will not be
      excluded from the find. Function returns 'SL_OK'
      upon success, a socket locate config code otherwise. */

   struct sl_config_entry *rov = sl_base;
   char mname[] = "sl_config_find";
   int ret, found = FALSE;
   int ishname, isport, isip;

   logman("%s:enter", mname);

   if (rov == SL_NULL)
      {
      logman("%s:config list empty", mname);
      return(SL_NOT_FOUND);
      }

   ishname = isport = isip = TRUE;

   if (hname == (char *)NULL || !strlen(hname))
      ishname = FALSE;

   if (port_num == (int *)NULL || *port_num <= 0)
      isport = FALSE;

   if (ip_ad == (char *)NULL || !strlen(ip_ad))
      isip = FALSE;

   if (!ishname && !isport && !isip)
      {
      logman("%s:no parameters", mname);
      return(SL_INVALID_PARAMETER);
      }

   logman("%s:ishname=%d,isport=%d,isip=%d", mname, ishname,
                 isport, isip);

   while(rov != SL_NULL)
      {
      if (!ishname)
         found = TRUE;
      else
         if (!strcmp(hname, rov->host_name))
            found = TRUE;
         else
            found = FALSE;

      if (found)
         if (!isport)
            found = TRUE;
         else
            if (*port_num == rov->port_number)
               found = TRUE;
            else
               found = FALSE;

      if (found)
         if (!isip)
            found = TRUE;
         else
            if (!strcmp(ip_ad, rov->ip_addr))
               found = TRUE;
            else
               found = FALSE;

      if (found)
         {
         if (!ishname && hname != (char *)NULL)
            strcpy(hname, rov->host_name);

         if (!isport && port_num != (int *)NULL)
            *port_num = rov->port_number;

         if (rov->ip_addr != (char *)NULL)
            if (!isip && ip_ad != (char *)NULL)
               strcpy(ip_ad, rov->ip_addr);

         logman("%s:found match,hname=%s,port=%d", mname,
                       hname, *port_num);
         break;
         }

      rov = rov->next;
      }

   ret = found ? SL_OK : SL_NOT_FOUND;
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sl_config_add(char *hname, int port_num, char *ip_ad)
{
   /* Add a socket locate server entry. Function returns
      'SL_OK' upon success, a socket locate config
      error code otherwise. */

   struct sl_config_entry *sl_entry;
   struct sl_config_entry *rov;
   char mname[] = "sl_config_add";
   int ret, len, size;

   logman("%s:enter", mname);
   size = sizeof(struct sl_config_entry);

   /* reasonableness checks */

   if (hname == (char *)NULL || !strlen(hname))
      {
      logman("%s:invalid parameter[hname]", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (port_num <= 0)
      {
      logman("%s:port out of range", mname);
      return(SL_CONFIG_PORT_OUT_OF_RANGE);
      }

   /* check for duplicate port number */

   if ((ret = sl_config_port_exist(port_num)) != SL_OK)
      {
      logman("%s:bad rc[%d] from 'sl_config_port_exist'",
                    mname, ret);
      return(ret);
      }

   /* check for duplicate host name */

   if ((ret = sl_config_host_exist(hname)) != SL_OK)
      {
      logman("%s:bad rc[%d] from 'sl_config_host_exist'",
                    mname, ret);
      return(ret);
      }

   if ((sl_entry = (struct sl_config_entry *)malloc(size)) == SL_NULL)
      {
      logman("%s:alloc fail[sl_entry]", mname);
      return(SL_MEMORY_FAIL);
      }

   len = strlen(hname);

   if ((sl_entry->host_name = (char *)malloc(len + 1)) == (char *)NULL)
      {
      logman("%s:alloc fail[sl_entry->host_name]", mname);
      free(sl_entry);
      return(SL_MEMORY_FAIL);
      }

   strcpy(sl_entry->host_name, hname);
   sl_entry->port_number = port_num;

   if (ip_ad != (char *)NULL)
      {
      len = strlen(ip_ad);

      if ((sl_entry->ip_addr = (char *)malloc(len + 1)) == (char *)NULL)
         {
         logman("%s:alloc fail[sl_entry->ip_addr]", mname);
         free(sl_entry->host_name);
         free(sl_entry);
         return(SL_MEMORY_FAIL);
         }

      strcpy(sl_entry->ip_addr, ip_ad);
      }
   else
      sl_entry->ip_addr = (char *)NULL;

   sl_entry->next = SL_NULL;
   sl_entry->is_local_server = FALSE;

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

   if (sl_base == SL_NULL)
      {
      sl_base = sl_entry;
      logman("%s:normal exit at head of list", mname);
      return(SL_OK);
      }

   /* find last entry and hook into it */

   rov = sl_base;

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

   rov->next = sl_entry;
   logman("%s:normal exit", mname);
   return(SL_OK);
}

int sl_config_port_exist(int port_num)
{
   /* Determine whether there already is a socket locate
      config entry with the specified port number.
      Function returns 'SL_OK' if the port
      does not already exist and 'SL_PORT_EXIST'
      if the port does already exist. */

   struct sl_config_entry *rov = sl_base;
   char mname[] = "sl_config_port_exist";
   int ret = SL_OK;

   logman("%s:enter,port=%d", mname, port_num);

   while(rov != SL_NULL)
      {
      if (port_num == rov->port_number)
         {
         logman("%s:found port already defined", mname);
         ret = SL_PORT_EXIST;
         break;
         }

       rov = rov->next;
       }

    logman("%s:normal exit[%d]", mname, ret);
    return(ret);
}

int sl_config_host_exist(char *hname)
{
   /* Determine whether there already is a socket locate
      config entry with the specified host name.
      Function returns 'SL_OK' if the host
      does not already exist and 'SL_CONFIG_HOST_EXIST'
      if the host does already exist. */

   struct sl_config_entry *rov = sl_base;
   char mname[] = "sl_config_host_exist";
   int ret = SL_OK;

   logman("%s:enter,host=%s", mname, hname);

   while(rov != SL_NULL)
      {
      if (!strcmp(hname, rov->host_name))
         {
         logman("%s:found host already defined", mname);
         ret = SL_CONFIG_HOST_EXIST;
         break;
         }

       rov = rov->next;
       }

    logman("%s:normal exit[%d]", mname, ret);
    return(ret);
}

int sl_config_delete(int port_num)
{
   /* Delete a socket locate server entry. Since the TCP/IP
      port is unique, this is used as the entry key.
      Function returns 'SL_OK' upon success, a
      socket locate config error otherwise. */

   struct sl_config_entry *rov;
   struct sl_config_entry *prev;
   char mname[] = "sl_config_delete";

   logman("%s:enter,port=%d", mname, port_num);
   rov = prev = sl_base;

   while(rov != SL_NULL)
      {
      if (port_num == rov->port_number)
         break;
      else
         {
         prev = rov;
         rov = rov->next;
         }
      }

   /* if no match, error */

   if (rov == SL_NULL)
      {
      logman("%s:entry not found", mname);
      return(SL_NOT_FOUND);
      }

   /* if we are deleting the head entry, set new head */

   if (rov == sl_base)
      sl_base = rov->next;
   else
      prev->next = rov->next;

   free(rov->host_name);

   if (rov->ip_addr != (char *)NULL)
      free(rov->ip_addr);

   free(rov);
   logman("%s:normal exit", mname);
   return(SL_OK);
}

void sl_config_delete_all(void)
{
   /* Delete all config entries. */

   struct sl_config_entry *rov = sl_base;
   struct sl_config_entry *tmp;
   char mname[] = "sl_config_delete_all";

   logman("%s:enter", mname);

   while(rov != SL_NULL)
      {
      free(rov->host_name);

      if (rov->ip_addr != (char *)NULL)
         free(rov->ip_addr);

      tmp = rov;
      rov = rov->next;
      free(tmp);
      }

   sl_base = SL_NULL;
   logman("%s:normal exit", mname);
}

int sl_config_get_list(char *list_out)
{
   /* Get the 'socloc' config list. The list is built with
      each entry delimited by 'SL_LIST_DELIM'. Upon success,
      function returns the config list in 'list_out' which
      already must be allocated to sufficient size by the
      caller. Function returns 'SL_OK' upon success, a
      'socloc' code otherwise. */

   struct sl_config_entry *rov = sl_base;
   char mname[] = "sl_config_get_list";
   char *anentry;
   int i, len, out_pos = 0;

   if (list_out == (char *)NULL)
      {
      logman("%s:invalid parameter[list_out]", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (rov == SL_NULL)
      {
      logman("%s:empty list", mname);
      return(SL_NOT_FOUND);
      }

   list_out[0] = EOS;

   if ((anentry = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[anentry]", mname);
      return(SL_MEMORY_FAIL);
      }

   while(rov != SL_NULL)
      {
      if (rov->ip_addr != (char *)NULL && strlen(rov->ip_addr))
         sprintf(anentry, "'%s' %d %s", rov->host_name, rov->port_number,
                 rov->ip_addr);
      else
         sprintf(anentry, "'%s' %d", rov->host_name, rov->port_number);

      /* load this entry into 'list_out' */

      len = strlen(anentry);

      for(i = 0; i < len; i++, out_pos++)
         list_out[out_pos] = anentry[i];

      if (rov->next != SL_NULL)
         list_out[out_pos++] = SL_LIST_DELIM;

      rov = rov->next;
      }

   list_out[out_pos] = EOS;
   free(anentry);
   logman("%s:normal exit", mname);
   return(SL_OK);
}

int sl_config_put_list(char *thelist)
{
   /* Take the input 'socloc' config list ('thelist')
      and write/put it. A list entry is expected in the
      format:

         hname port[ ip_ad]

      Where 'hname' is the host name, port is the TCP/IP
      port number and the optional 'ip_ad' is the TCP/IP
      address. If the host name contains spaces, it
      must be quoted.

      Each entry is delimited by 'SL_LIST_DELIM'. */

   char mname[] = "sl_config_put_list";
   char *entry, *hname, *port_char, *ip_ad;
   int nentries, nwords, port, len, i, ret;

   if (thelist == (char *)NULL || !strlen(thelist))
      {
      logman("%s:invalid parameter[thelist]", mname);
      return(SL_INVALID_PARAMETER);
      }

   /* get number of delimited entries */

   nentries = ll_words(thelist, SL_LIST_DELIM);

   if (nentries < 1)
      {
      logman("%s:unexpected[nentries] from 'll_words'", mname);
      return(SL_INTERNAL_ERROR);
      }

   /* delete the existing list in memory */

   sl_config_delete_all();

   /* add each entry in the list */

   for(i = 1; i <= nentries; i++)
      {
      len = ll_wordlen(thelist, i, SL_LIST_DELIM);

      if (len < 1)
         {
         logman("%s:entry[%d]:unexpected[len] from 'll_wordlen'",
                       mname, i);
         return(SL_INTERNAL_ERROR);
         }

      if ((entry = (char *)malloc(len + 1)) == (char *)NULL)
         {
         logman("%s:entry[%d]:alloc fail[entry]", mname, i);
         return(SL_MEMORY_FAIL);
         }

      if (!ll_word(thelist, entry, i, SL_LIST_DELIM))
         {
         free(entry);
         logman("%s:entry[%d]:unexpected from 'll_word'", mname, i);
         return(SL_INTERNAL_ERROR);
         }

      /* must have at least 2 command words in each entry */

      if ((nwords = command_words(entry)) < 2)
         {
         free(entry);
         logman("%s:entry[%d] insufficient parameters", mname, i);
         return(SL_INVALID_PARAMETER);
         }

      /* since no single entry value can exceed the length of
         the entry, we get sloppy and allocate to length
         of entry */

      if ((hname = initstring(entry)) == (char *)NULL)
         {
         free(entry);
         logman("%s:entry[%d]:alloc fail[hname]", mname, i);
         return(SL_MEMORY_FAIL);
         }

      if ((port_char = initstring(entry)) == (char *)NULL)
         {
         free(entry);
         free(hname);
         logman("%s:entry[%d]:alloc fail[port_char]", mname, i);
         return(SL_MEMORY_FAIL);
         }

      if ((ip_ad = initstring(entry)) == (char *)NULL)
         {
         free(entry);
         free(hname);
         free(port_char);
         logman("%s:entry[%d]:alloc fail[ip_ad]", mname, i);
         return(SL_MEMORY_FAIL);
         }

      hname[0] = EOS;
      port_char[0] = EOS;
      ip_ad[0] = EOS;

      if (!command_word(entry, hname, 1))
         {
         free(entry);
         free(hname);
         free(port_char);
         free(ip_ad);
         logman("%s:entry[%d]:unexpected from 'command_word'[hname]",
                       mname, i);
         return(SL_INTERNAL_ERROR);
         }

      if (!command_word(entry, port_char, 2))
         {
         free(entry);
         free(hname);
         free(port_char);
         free(ip_ad);
         logman("%s:entry[%d]:unexpected from 'command_word'[port_char]",
                       mname, i);
         return(SL_INTERNAL_ERROR);
         }

      if (!qatoi(port_char, &port))
         {
         free(entry);
         free(hname);
         free(port_char);
         free(ip_ad);
         logman("%s:entry[%d]:non-numeric port", mname, i);
         return(SL_INVALID_PARAMETER);
         }

      free(port_char);

      if (nwords > 2)
         {
         if (!command_word(entry, ip_ad, 3))
            {
            free(entry);
            free(hname);
            free(ip_ad);
            logman("%s:entry[%d]:unexpected from 'command_word'[ip_ad]",
                          mname, i);
            return(SL_INTERNAL_ERROR);
            }

         if (!isip(ip_ad))
            {
            free(entry);
            free(hname);
            free(ip_ad);
            logman("%s:entry[%s]:invalid IP address", mname, i);
            return(SL_BAD_IP);
            }
         }

      /* finally, after all that, we are ready to add the entry */

      if ((ret = sl_config_add(hname, port, ip_ad)) != SL_OK)
         {
         free(entry);
         free(hname);
         free(ip_ad);
         logman("%s:entry[%d]:bad rc[%d] from 'sl_config_add'",
                       mname, i, ret);
         return(ret);
         }

      free(entry);
      free(hname);
      free(ip_ad);
      }

   logman("%s:normal exit", mname);
   return(SL_OK);
}

int sl_config_write_file(void)
{
   /* Write the current config list to the config
      file. Function returns 'SL_OK' upon success,
      a 'socloc' code otherwise. */

   struct sl_config_entry *rov = sl_base;
   FILE *out;
   char mname[] = "sl_config_write_file";

   logman("%s:enter", mname);

   if (sl_base == SL_NULL)
      {
      logman("%s:list empty, nothing to write", mname);
      return(SL_NOT_FOUND);
      }

   if ((out = fopen(SL_CONFIG_FILE_NAME, "w")) == (FILE *)NULL)
      {
      logman("%s:unable to open file '%s' for writing",
                    mname, SL_CONFIG_FILE_NAME);
      return(SL_CONFIG_UNABLE_TO_OPEN);
      }

   fprintf(out, "* list written by 'sl_config_write_file'\n");

   while(rov != SL_NULL)
      {
      if (words(rov->host_name) > 1)
         fprintf(out, "'%s' %d", rov->host_name, rov->port_number);
      else
         fprintf(out, "%s %d", rov->host_name, rov->port_number);

      if (rov->ip_addr != (char *)NULL && strlen(rov->ip_addr))
         fprintf(out, " %s\n", rov->ip_addr);
      else
         fprintf(out, "\n");

      rov = rov->next;
      }

   fclose(out);
   logman("%s:normal exit", mname);
   return(SL_OK);
}

int sl_config_get_ils(int port_num, int *ils_flag)
{
   /* Get and return the 'is_local_server' flag for
      a 'socloc' server based on its TCP/IP port
      number. Function returns the 'is_local_server'
      flag upon success, a 'socloc' code otherwise. */

   struct sl_config_entry *rov = sl_base;
   char mname[] = "sl_config_get_ils";
   int ret, found = FALSE;

   logman("%s:enter", mname);

   if (port_num <= 0)
      {
      logman("%s:illegal port number", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (ils_flag == (int *)NULL)
      {
      logman("%s:invalid parameter[ils_flag]", mname);
      return(SL_INVALID_PARAMETER);
      }

   *ils_flag = FALSE;

   while(rov != SL_NULL)
      {
      if (port_num == rov->port_number)
         {
         *ils_flag = rov->is_local_server;
         found = TRUE;
         break;
         }

      rov = rov->next;
      }

   ret = found ? SL_OK : SL_NOT_FOUND;
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sl_config_set_ils(int port_num, int ils_flag)
{
   /* Set the 'socloc' server on TCP/IP port 'port_num'
      'is_local_server' flag to 'ils_flag'. Function
      returns a 'socloc' code. */

   struct sl_config_entry *rov = sl_base;
   char mname[] = "sl_config_set_ils";
   int ret, found = FALSE;

   logman("%s:enter", mname);

   if (port_num <= 0)
      {
      logman("%s:invalid port number", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (ils_flag < 0 || ils_flag > 1)
      {
      logman("%s:invalid parameter[ils_flag] must be 0 or 1", mname);
      return(SL_INVALID_PARAMETER);
      }

   while(rov != SL_NULL)
      {
      if (port_num == rov->port_number)
         {
         rov->is_local_server = ils_flag;
         found = TRUE;
         break;
         }

      rov = rov->next;
      }

   ret = found ? SL_OK : SL_NOT_FOUND;
   logman("%s:normal exit[%d]", mname, ret);
}

void sl_config_list(void)
{
   /* List the contents of the link list. Used for screen output
      only. */

   char mname[] = "sl_config_list";
   struct sl_config_entry *rov = sl_base;
   int i = 1;

   logman("%s:enter", mname);

   if (is_log_active())
      {
      logman("%s:log is on, using 'sl_config_debug'", mname);
      sl_config_debug();
      return;
      }

   while(rov != SL_NULL)
      {
      if (strlen(rov->ip_addr))
         printf("socloc[%d]:host='%s',port=%d,ip=%s\n", i,
                rov->host_name, rov->port_number, rov->ip_addr);
      else
         printf("socloc[%d]:host='%s',port=%d\n", i, rov->host_name,
                rov->port_number, rov->ip_addr);

      rov = rov->next;
      i++;
      }

   if (i == 1)
      printf("list empty\n");
}

void sl_config_debug(void)
{
   /* Print the contents of the link list. */

   char mname[] = "sl_config_debug";
   struct sl_config_entry *rov = sl_base;
   int i = 1;

   logman("%s:enter", mname);

   while(rov != SL_NULL)
      {
      logman("%s[%d]:host='%s',port=%d,ip='%s',ils=%d", mname,
                    i, rov->host_name, rov->port_number, rov->ip_addr,
                    rov->is_local_server);
      rov = rov->next;
      i++;
      }

   if (i == 1)
      logman("%s:list empty", mname);

   logman("%s:normal exit", mname);
}
