/* ---------------------------------------------------------------------
   (c) ED 1998-2008
   Projet       : CLIB
   Fonction     : Traitements de chaines
   Module       : STR
   Fichier      : STR.C
   Creation     : 06-08-1998
   Modification : 01-05-2008
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Journal

   1.39 du 01-05-2008 STR_sub_dyn() accepte et retourne les sous-chaines vides
   1.38 du 28-04-2008 Simplification et securisation de STR_sub_dyn()
   1.37 du 24-02-2008 Simplification STR_count()
   1.36 du 14-04-2005 Ajout de STR_ext_dyn()
   1.35 du 17-01-2005 Ajout de STR_replace ()
   1.34 du 05-04-2004 Mise au point de STR_safecopy ()
   .                  (remplir la chaine de 0s)
   1.33 du 01-12-2003 Ajout de STR_nicmp ()
   1.32 du 19-06-2003 Ajoute MEM_dup()
   1.31 du 26-05-2003 STR_dup() accepte maintenant la chaine vide "".
   1.30 du 12-05-2003 Ajout de STR_safecopy()
   1.29 du 30-04-2003 Ajout de 'sign' dans STR_is_decimal
   1.28 du 30-04-2003 Ajout de STR_is_decimal()
   .                  Mise au point STR_cat_dyn()
   1.27 du 18-02-2003 Mise au point STR_SkipSpaces() et STR_SkipNoSpaces()
   1.26 du 14-02-2003 Ajout de STR_remove_car()
   1.25 du 03-02-2003 Ajout de STR_format()
   1.24 du 28-12-2002 Ajout de STR_count_sub()
   1.23 du 26-12-2002 Normalisation des interfaces pour etre compatible
   .                  avec certains compilateur qui ne connaisent pas la
   .                  norme...
   1.22 du 09-12-2002 Ajout de STR_subst
   1.21 du 13-07-2002 LCLint
   1.20 du 11-03-2002 Ajout de STR_stringize_dyn()
   1.19 du 18-02-2002 Ajout de STR_cat_dyn() STR_sub_dyn
   1.18 du 15-12-2000 STR_no_accent() devient STR_no_accent_ibm()
   1.17 du 16-10-2000 Simplification de STR_dup()
   1.16 du 11-10-2000 Ajoute STR_SkipSpaces() et STR_SkipNoSpaces()
   1.15 du 27-04-2000 Ajoute STR_no_accent()
   1.14 du 14-04-2000 Changement de strategie de STR_debug().
   .                  Suppresion memoire dynamique, passage de
   .                  l'adresse / longueur en parametre.
   .                  L'ancienne STR_debug() devient STR_debug_dyn()
   .                  Caracteres seuls en int au lieu de char
   1.13 du 13-04-2000 Correction formatage dans STR_debug()
   1.12 du 17-01-2000 Tailles en size_t au lieu de int
   1.11 du 07-01-2000 Casting strict ANSI pour sprintf()
   1.10 du 13-12-1999 Parametres de STR_findsz() const.
   1.9 du 10-11-1999 Ameliorations dans STR_debug()
   1.8 du 08-11-1999 Ajout de STR_toupper() et STR_tolower()
   1.7 du 27-10-1999 Ajout de controles dans STR_debug()
   1.6 du 13-10-1999 Remplace calloc/free par sys_calloc/sys_free
   1.5 du 13-10-1999 Ajout de STR_dup() equivallent au strdup() de Borland.
   1.4 du 06-10-1999 Suppression de STR_FindCar() car deja fait par
   .                 strchr() Laisse macro de compatibilite.
   1.3 du 11-01-1999 Ajoute STS_cdbg()
   1.2 du 11-01-1999 Mise au point de STR_FindCar()
   1.1 du 07-09-1998 STR_find() devient STR_FindCar()
   .                 Ajoute STR_FindSz()
   1.0 du 06-08-1998 Version operationelle
   0.0 du 06-08-1998 Creation
   --------------------------------------------------------------------- */
#ifdef __cplusplus
#error This source file is not C++ but C. Please use a C compiler.
#endif

#include "ed/inc/str.h"

/* CLIB */
#include "ed/inc/ascii.h"
#include "ed/inc/sys.h"
#include "ed/inc/sysalloc.h"

/* C-ISO */
#include <string.h>
#include <ctype.h>

/* macros ============================================================== */

#define VER "1.39"
#define ID "STR Module \"C\" (c) ED 1998-2008"

/* constants =========================================================== */
/* types =============================================================== */
/* structures ========================================================== */
/* private data ======================================================== */
/* private functions =================================================== */
/* internal public data ================================================ */
/* internal public functions =========================================== */
/* entry points ======================================================== */

/* ---------------------------------------------------------------------
   STR_sver()
   ---------------------------------------------------------------------
   Role : Retourne une chaine "Version"
   ---------------------------------------------------------------------
   E :
   S : Pointeur de chaine ASCIIZ
   --------------------------------------------------------------------- */
const char *STR_sver (void)
{
   return VER;
}

/* ---------------------------------------------------------------------
   STR_sid()
   ---------------------------------------------------------------------
   Role : Retourne une chaine "Identification"
   ---------------------------------------------------------------------
   E :
   S : Pointeur de chaine ASCIIZ
   --------------------------------------------------------------------- */
const char *STR_sid (void)
{
   return ID;
}

/* ---------------------------------------------------------------------
   STR_DelBlanks()
   ---------------------------------------------------------------------
   Suppression des blancs dans une chaine
   LA CHAINE EST MODIFIEE !!!
   ---------------------------------------------------------------------
   E : Chaine initiale
   S :
   --------------------------------------------------------------------- */
void STR_DelBlanks (char *const s)
{
   ENUM_CHECK ();

   if (s && *s)
   {
      char const *pr = s;
      char *pw = s;

      while (*pr)
      {
         if (*pr != ' ')
         {
            *pw = *pr;
            pw++;
         }
         pr++;
      }

      *pw = 0;
   }
}

/* ---------------------------------------------------------------------
   STR_SkipCrLf()
   ---------------------------------------------------------------------
   Elimine les CR/LF au debut de la chaine
   LA CHAINE EST MODIFIEE !!!
   ---------------------------------------------------------------------
   E : Chaine
   S : Debut de la chaine sans CR et LF
   --------------------------------------------------------------------- */
char *STR_SkipCrLf (char *const s)
{
   char *p = s;

   ENUM_CHECK ();
   ASSERT (s != NULL);

   while (*p)
   {
      if (*p == '\n' || *p == '\r')
      {
         p++;
      }
      else
      {
         break;
      }
   }

   return p;
}

/* ---------------------------------------------------------------------
   STR_FindSz()
   ---------------------------------------------------------------------
   Role : Recherche d'une chaine dans un tableau de chaine
   ---------------------------------------------------------------------
   E : tableau
   E : nombre d'elements
   E : chaine
   S : rang ou si pas trouve
   --------------------------------------------------------------------- */
int STR_FindSz (char const *const *const asz
                ,size_t const n
                ,char const *const s)
{
   int pos = -1;
   size_t i;

   ENUM_CHECK ();
   for (i = 0; (i < n); i++)
   {
      if (strcmp (s, asz[i]) == 0)
      {
         pos = (int) i;
         break;
      }
   }

   return pos;
}

/* ---------------------------------------------------------------------
   STR_cdbg()
   ---------------------------------------------------------------------
   Role : Renvoyer une chaine "debug" si le caractere est non imprimable,
   sinon, renvoyer NULL.
   ---------------------------------------------------------------------
   E : caractere (0-255)
   S : chaine debug ou NULL
   --------------------------------------------------------------------- */
char const *STR_cdbg (int const c)
{
   char const *s = NULL;

   ENUM_CHECK ();

   if (isprint (c))
   {
      if (c == SPC)
      {
         s = "SPC";
      }
   }
   else
   {
      if ((c >= 0)
          && (c < ASCII_NB))
      {
         s = ASCII_str (c);
      }
   }
   return s;
}

/* ---------------------------------------------------------------------
   STR_debug()
   ---------------------------------------------------------------------
   Role : Retourne la chaine debugee : Les codes non imprimables sont
   remplaces par des subsituts imprimables.
   En cas de debordement, la chaine se termine par "..."
   ---------------------------------------------------------------------
   E : chaine originale (n'est pas modifiee)
   E : chaine resultat
   E : longueur max de la chaine resultat
   S : 0=normal 1=tronque
   --------------------------------------------------------------------- */
int STR_debug (char *const des
               ,size_t const len
               ,char const *const src)
{
#define FIN "..."
#define FMT "<"FIN">"
   int tronque = 0;
   char const *p;

   ENUM_CHECK ();

   *des = 0;
   LIM_PTR (des, len);

   for (p = src; *p; p++)
   {
      char sdbg[sizeof FMT];
      int c = *p;

      LIM_STR (sdbg);

      if (isprint (c))
      {
         if (c == ' ')
         {
            strcpy (sdbg, "<");
            CHK_STR (sdbg);

            strcat (sdbg, STR_cdbg (c));
            CHK_STR (sdbg);

            strcat (sdbg, ">");
            CHK_STR (sdbg);
         }
         else
         {
            *sdbg = (char) c;
            CHK_STR (sdbg);
         }
      }
      else
      {
         char const *const s = STR_cdbg (c);

         if (s != NULL)
         {
            ASSERT (strlen (s) < sizeof FMT);

            strcpy (sdbg, "<");
            CHK_STR (sdbg);

            strcat (sdbg, s);
            CHK_STR (sdbg);

            strcat (sdbg, ">");
            CHK_STR (sdbg);
         }
         else
         {
            sprintf (sdbg, "<%03u>"
                     ,(uint) (c & 0xFF)
               );
            CHK_STR (sdbg);
         }
      }

      if (strlen (des) + strlen (sdbg) + sizeof FIN >= len)
      {
         strcat (des, FIN);
         CHK_PTR (des, len);
         tronque = 1;
         break;
      }
      else
      {
         strcat (des, sdbg);
         CHK_PTR (des, len);
      }
   }
#undef FIN
#undef FMT
   return tronque;
}

/* ---------------------------------------------------------------------
   STR_debug_dyn()
   ---------------------------------------------------------------------
   Role : Retourne la chaine debugee : Les codes non imprimables sont
   remplaces par des subsituts imprimables.
   Liberation par sys_free()
   ---------------------------------------------------------------------
   E : chaine originale (n'est pas modifiee)
   S : chaine resultat ( *** a liberer *** )
   --------------------------------------------------------------------- */
char *STR_debug_dyn (char const *const src)
{
   char *sdbg = NULL;

   ENUM_CHECK ();

   if (src != NULL)
   {
      ulong len_l = strlen (src);

      if (len_l)
      {
#define FMT "<...>"

         len_l *= sizeof FMT;   /* Estimation large */
/*@ignore@ */
         if ((len_l + 1) < SIZE_T_MAX)
/*@end@ */
         {
            size_t const len = (size_t) len_l;

            sdbg = malloc (len + 1);  /* Liberation par l'utilisateur */

            if (sdbg != NULL)
            {
               char const *pread;
               *sdbg = 0;
               LIM_PTR (sdbg, len);

               for (pread = src
                    ; *pread && (strlen (sdbg) < len)
                    ; pread++
                  )
               {
                  int const c = *pread;
                  /* strcat ... */
                  char *pwrite = sdbg + strlen (sdbg);

                  ASSERT (pwrite != NULL);

                  if (isprint (c))
                  {
                     pwrite[0] = (char) c;
                     pwrite[1] = 0;

                     CHK_PTR (sdbg, len);
                  }
                  else
                  {
                     char const *const s = STR_cdbg (c);

                     if (s != NULL)
                     {
                        ASSERT (strlen (s) < sizeof FMT);

                        strcpy (pwrite, "<");
                        CHK_PTR (sdbg, len);

                        strcat (pwrite, s);
                        CHK_PTR (sdbg, len);

                        strcat (pwrite, ">");
                        CHK_PTR (sdbg, len);
                     }
                     else
                     {
                        sprintf (pwrite, "<%03u>"
                                 ,(uint) (c & 0xFF)
                           );
                        CHK_PTR (sdbg, len);
                     }
                  }
               }
            }
         }
         else
         {
            ASSERT (0);
         }
#undef FMT
      }
   }
   return sdbg;
}

/* ---------------------------------------------------------------------
   MEM_dup()
   ---------------------------------------------------------------------
   faire une copie dynamique d'un bloc de donnees
   ---------------------------------------------------------------------
   I: adresse
   I: longueur (si 0 -> NULL)
   O: adresse de la copie ou NULL
   --------------------------------------------------------------------- */
void *MEM_dup (void const *const p, size_t const n)
{
   void *pcopy = NULL;

   if (n)
   {
      pcopy = malloc (n);

      ENUM_CHECK ();

      if (pcopy)
      {
         memcpy (pcopy, p, n);
      }
   }
   return pcopy;
}

/* ---------------------------------------------------------------------
   STR_dup()
   ---------------------------------------------------------------------
   Effectue une copie de la chaine en memoire dynamique. Retourne NULL
   en cas d'erreur (ptr NULL). Le pointeur doit etre
   libere par sys_free().
   Equivallent de strdup() de Borland
   Accepte la chaine vide.
   ---------------------------------------------------------------------
   E : Chaine originale (non modifiable)
   S : Copie modifiable (memoire dynamique)
   --------------------------------------------------------------------- */
char *STR_dup (char const *const s)
{
   char *sdup = NULL;

   ENUM_CHECK ();

   if (s != NULL)
   {
      sdup = MEM_dup (s, strlen (s) + 1);
   }

   return sdup;
}

/* ---------------------------------------------------------------------
   STR_toupper()
   ---------------------------------------------------------------------
   Convertir une chaine en majuscule. La chaine est modifiee.
   Retourne l'adresse de la chaine originale
   Equivallent de strupr() de Borland
   ---------------------------------------------------------------------
   E : Chaine originale (modifiable)
   S : Adresse de la chaine originale
   --------------------------------------------------------------------- */
char *STR_toupper (char *const s)
{
   char *p = s;

   ENUM_CHECK ();

   while (*p)
   {
      *p = (char) toupper (*p);
      p++;
   }
   return s;
}

/* ---------------------------------------------------------------------
   STR_tolower()
   ---------------------------------------------------------------------
   Convertir une chaine en minuscules. La chaine est modifiee.
   Retourne l'adresse de la chaine originale
   Equivallent de strupr() de Borland
   ---------------------------------------------------------------------
   E : Chaine originale (modifiable)
   S : Adresse de la chaine originale
   --------------------------------------------------------------------- */
char *STR_tolower (char *const s)
{
   char *p = s;

   ENUM_CHECK ();

   while (*p)
   {
      *p = (char) tolower (*p);
      p++;
   }
   return s;
}

/* ---------------------------------------------------------------------
   STR_no_accent_ibm()
   ---------------------------------------------------------------------
   Retire les accents d'une chaine (ASCII)
   Retourne l'adresse de la chaine
   ---------------------------------------------------------------------
   E : Chaine originale (modifiable)
   S : Adresse de la chaine originale
   --------------------------------------------------------------------- */
char *STR_no_accent_ibm (char *const s)
{
   char *ps = s;

   ENUM_CHECK ();

   while (*ps)
   {
#ifdef NDEBUG
      int err = 0;
#endif

      /* ASCII (non portable) */
      *ps = (char) ASCII_noacc_ibm (*ps
#ifdef NDEBUG
                                    ,&err
#else
                                    ,NULL
#endif
         );

#ifdef NDEBUG
      if (err)
      {
         *ps = 0;
         break;
      }
#endif

      ps++;
   }
   return s;
}

/* ---------------------------------------------------------------------
   STR_SkipTrailingSpc()
   ---------------------------------------------------------------------
   Retire les espaces en fin de chaine
   Retourne le nombre d'espaces retires
   ---------------------------------------------------------------------
   E : Chaine originale (modifiable)
   S : Nombre d'espaces retires
   --------------------------------------------------------------------- */
int STR_SkipTrailingSpc (char *const s)
{
   int spc = 0;
   size_t len = strlen (s);

   ENUM_CHECK ();

   if (len)
   {
      size_t i = len - 1;

      for (;;)
      {
         if (s[i] == ' ')
         {
            spc++;
            if (i == 0)
            {
               s[i] = 0;
               break;
            }
            i--;
         }
         else
         {
            s[i + 1] = 0;
            break;
         }
      }

   }
   return spc;
}

/* ---------------------------------------------------------------------
   STR_SkipSpaces()
   ---------------------------------------------------------------------
   Role : retourne un pointeur sur le premier caractere non espace
   ---------------------------------------------------------------------
   E :
   S : pointeur
   --------------------------------------------------------------------- */
char const *STR_SkipSpaces (char const *const s)
{
   char const *p = s;
   ENUM_CHECK ();
   while (*p && isspace (*p))
   {
      p++;
   }
   return p;
}

/* ---------------------------------------------------------------------
   STR_SkipNoSpaces()
   ---------------------------------------------------------------------
   Role : retourne un pointeur sur le premier caractere espace
   ---------------------------------------------------------------------
   E : chaine
   S : pointeur
   --------------------------------------------------------------------- */
char const *STR_SkipNoSpaces (char const *const s)
{
   char const *p = s;
   ENUM_CHECK ();
   while (*p && !isspace (*p))
   {
      p++;
   }
   return p;
}

/* ---------------------------------------------------------------------
   STR_count  ()
   ---------------------------------------------------------------------
   Compte les occurrences d'un caractere dans une chaine
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
size_t STR_count (char const *s, int const c)
{
   size_t n = 0;

   if (s != NULL)
   {
      if (c > 0 && c < UCHAR_MAX)
      {
         while (*s != 0 )
         {
            if (*s == c)
            {
               n++;
            }
            s++;
         }
      }
   }

   return n;
}

/* ---------------------------------------------------------------------
   STR_count_sub  ()
   ---------------------------------------------------------------------
   Compte les occurences d'une sous-chaine dans une chaine
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
size_t STR_count_sub (char const *const s, char const *const sub)
{
   size_t n = 0;

   if (s)
   {
      if (sub && *sub)
      {
         char const *p = s;
         size_t const lsub = strlen (sub);

         while (*p)
         {
            if (strncmp (p, sub, lsub) == 0)
            {
               n++;
               p += lsub;
            }
            else
            {
               p++;
            }
         }
      }
   }

   return n;
}

/* ---------------------------------------------------------------------
   STR_cat_dyn  ()
   ---------------------------------------------------------------------
   Concatene deux chaines en une troisieme plus grande
   A liberer.
   ---------------------------------------------------------------------
   E : chaine initiale
   E : chaine a ajouter
   S : chaine resultante
   --------------------------------------------------------------------- */
char *STR_cat_dyn (char const *const s_ini, char const *const s_add)
{
   char *s_out = NULL;
   if (s_ini && s_add)
   {
      size_t const n_ini = strlen (s_ini);
      size_t const n_add = strlen (s_add) + 1;

      s_out = malloc (n_ini + n_add);
      if (s_out)
      {
         memcpy (s_out, s_ini, n_ini);
         memcpy (s_out + n_ini, s_add, n_add);
      }
   }
   return s_out;
}

/* ---------------------------------------------------------------------
   STR_sub_dyn  ()
   ---------------------------------------------------------------------
   Fait une copie d'une sous chaine entre deux pointeurs
   A liberer.
   ---------------------------------------------------------------------
   E : debut
   E : fin
   S : chaine resultante
   --------------------------------------------------------------------- */
char *STR_sub_dyn (char const *const pbeg, char const *const pend)
{
   char *s = NULL;
   if (pbeg != NULL && pend <= pbeg + strlen (pbeg))
   {
      if (pend != NULL)
      {
         size_t const len = (size_t) (pend - pbeg);
         s = malloc (len + 1);
         if (s != NULL)
         {
            *s = 0;
            strncat (s, pbeg, len);
         }
      }
   }
   return s;
}

/* ---------------------------------------------------------------------
   STR_ext_dyn  ()
   ---------------------------------------------------------------------
   Fait une copie d'une sous chaine definie par (adresse, longueur)
   A liberer.
   ---------------------------------------------------------------------
   E : debut
   E : longueur
   --------------------------------------------------------------------- */
char *STR_ext_dyn (char const *const pbeg
                   ,size_t const len)
{
   char *s;

   if (pbeg != NULL)
   {
      s = malloc (len + 1);

      if (s != NULL)
      {
         *s = 0;
         strncat (s, pbeg, len);
      }
   }
   else
   {
      s = NULL;
   }
   return s;
}

/* ---------------------------------------------------------------------
   STR_stringize_dyn  ()
   ---------------------------------------------------------------------
   Mettre des "" autour d'une chaine
   Suppression des blancs en fin de chaine

   'abc def    ' -> '"abc def"'

   A liberer.
   ---------------------------------------------------------------------
   E : chaine initiale
   S : chaine resultante
   --------------------------------------------------------------------- */
char *STR_stringize_dyn (char const *const s_in)
{
#define DQUOTE "\""
   char *s_out = NULL;
   if (s_in)
   {
      size_t size = strlen (s_in) + sizeof DQUOTE DQUOTE;
      s_out = malloc (size);

      if (s_out)
      {
         strcpy (s_out, DQUOTE);
         strcat (s_out, s_in);
         STR_SkipTrailingSpc (s_out);
         strcat (s_out, DQUOTE);
      }
   }
   return s_out;
}

/* ---------------------------------------------------------------------
   STR_subst ()
   ---------------------------------------------------------------------
   Dans une chaine, remplace un caractere par un autre.
   La chaine doit etre modifiable.
   ---------------------------------------------------------------------
   E : chaine initiale
   E : ancien caractere
   E : nouveau caractere
   S : chaine resultante
   --------------------------------------------------------------------- */
char *STR_subst (char *const s_in, int const old, int const new)
{
   char *p = s_in;

   if (p)
   {
      for (p = s_in; *p; p++)
      {
         if (*p == old)
         {
            *p = (char) new;
         }
      }
   }
   return s_in;
}

/* ---------------------------------------------------------------------
   STR_format ()
   ---------------------------------------------------------------------
   Cree une chaine a partire d'une autre.
   La chaine format permet de specifier quels sont les catacteres qui
   sont forces en majuscule ou en minuscule. Si la chaine format est plus
   courte que la chaine source, le dernier format est maintenu.

   "X" "abcd" -> "ABCD"
   "Xx" "abcd" -> "Abcd"
   "xxX" "abcd" -> "abCD"

   ---------------------------------------------------------------------
   E : chaine de destination
   E : taille de la chaine de destination
   E : chaine de formatage
   E : chaine de source
   S :
   --------------------------------------------------------------------- */
void STR_format (char *const s_out
                 ,size_t const size
                 ,char const *const s_fmt
                 ,char const *const s_in)

{
   size_t i;
   size_t j;

   int upper = 0;

   for (i = 0, j = 0; i < size - 1 && s_in[i]; i++)
   {
      if (s_fmt[j])
      {
         upper = isupper (s_fmt[j]);
         j++;
      }

      if (upper)
      {
         s_out[i] = (char) toupper (s_in[i]);
      }
      else
      {
         s_out[i] = (char) tolower (s_in[i]);
      }
   }

   s_out[i] = 0;
}

/* ---------------------------------------------------------------------
   STR_remove_car()
   ---------------------------------------------------------------------

   ---------------------------------------------------------------------
   I:
   O:
   --------------------------------------------------------------------- */
void STR_remove_car (char *const s, int const car)
{
   char *p = strchr (s, car);

   if (p)
   {
      *p = 0;
   }
}

/* ---------------------------------------------------------------------
   STR_is_decimal()
   ---------------------------------------------------------------------
   Is a string decimal ?

   Two cases :

   sign = 0 : '0'-'9'['0'-'9'[*]]0

   -> "123" is valid
   -> "12a3" is invalid
   -> "-123" is invalid

   sign = 1 : ['+'|''-']'0'-'9'['0'-'9'[*]]0

   -> "123" is valid
   -> "12a3" is invalid
   -> "-123" is valid

   ---------------------------------------------------------------------
   I: string
   I: sign 0 = refuse 1 = accept
   O: 0=no 1=yes
   --------------------------------------------------------------------- */
int STR_is_decimal (char const *const s, int sign)
{
   int ok = 0;

   if (s && *s)
   {
      char const *p = s;
      int c = *p;

      if (sign)
      {
         ok = isdigit (c) || ((c == '+' || c == '-') && s[1] != 0);
         p++;
      }
      else
      {
         ok = 1;
      }

      if (ok)
      {
         while (*p != 0)
         {
            if (!isdigit (*p))
            {
               ok = 0;
               break;
            }
            p++;
         }
      }
   }
   return ok;
}

/* ---------------------------------------------------------------------
   STR_safecopy()
   ---------------------------------------------------------------------
   copie de chaine securisee
   En cas de depassement, la chaine est tronquee
   Pour des raisons de compatibilite chaine / buffer,longueur,
   la fin de la chaine de destination est remplie de 0
   ---------------------------------------------------------------------
   I: adr destination
   I: taille destination
   I: adr source
   O: adr destination
   --------------------------------------------------------------------- */
char *STR_safecopy (char *const des
                    ,size_t const size
                    ,char const *const src)
{
   char *s_out = NULL;

   if (des != NULL
       && size != 0
       && src != NULL)
   {
      /* longueur de la source */
      size_t const len = strlen (src);

      if (len < size - 1)
      {
         /* remplit la fin de 0s */
         strncpy (des, src, size);
      }
      else
      {
         /* copie */
         memcpy (des, src, size - 1);

         /* marquage de la fin */
         des[size - 1] = 0;
      }
      s_out = des;
   }

   return s_out;
}

/* ---------------------------------------------------------------------
   STR_nicmp ()
   ---------------------------------------------------------------------
   Comparaison de chaines sur une certaine longueur en ignorant la casse
   ---------------------------------------------------------------------
   I : chaine 1
   I : chaine 2
   I : nombre de caracteres a tester
   O : 1, 0, -1
   --------------------------------------------------------------------- */
int STR_nicmp (char const *str1
               ,char const *str2
               ,int cnt)
{
   for (; cnt; cnt--, str1++, str2++)
   {
      int const c1 = tolower (*str1);
      int const c2 = tolower (*str2);

      if (c1 == c2)
      {
         if (c1 == '\0' || c2 == '\0')
         {
            /* Strings were equal */
            return 0;
         }
      }
      else
      {
         if (c1 < c2)
         {
            return -1;
         }
         else
         {
            return 1;
         }
      }
   }

   /* Strings were equal */
   return 0;
}

/* ---------------------------------------------------------------------
   STR_replace ()
   ---------------------------------------------------------------------
   Replace each occurence of a character by another one
   ---------------------------------------------------------------------
   I : string (read/write)
   I : old character
   I : new character
   O : occurences ou -1 (err)
   --------------------------------------------------------------------- */
int STR_replace (char *s, int c_old, int c_new)
{
   int n = -1;

   if (s != NULL
       && c_old >= 0
       && c_old < 256
       && c_new >= 0
       && c_new < 256)
   {
      char *p;

      n = 0;

      for (p = s; *p; p++)
      {
         if (*p == c_old)
         {
            *p = c_new;
            n++;
         }
      }
   }
   return n;
}

/* public data ========================================================= */

/* File generated by 'NEW.EXE' Ver 1.12 (c) ED 1998 */
