/* ---------------------------------------------------------------------
   (c) ED 1999-2002
   Projet       : FLEXPRO II
   Fonction     : Assembleur de trame
   Module       : REC
   Fichier      : REC.C
   Creation     : 08-10-1999
   Modification : 13-07-2002
   --------------------------------------------------------------------- */

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

   0.0 du 08-10-1999 Creation
   1.0 du 10-10-1999 Version operationelle
   1.1 du 11-10-1999 Ajoute do-while dans assemble (il peut y avoir
   .                 plusieurs separateurs dans le bloc recu)
   1.2 du 11-10-1999 Les donnees recues sont obligatoirement des chaines
   .                 de caracteres (pas de 0). Modification du parametre
   .                 de REC_assemble()
   1.3 du 11-10-1999 Remontee des trames vides (le buffer est alors NULL)
   1.4 du 12-10-1999 Fichier de config / Option de remontee ou non du
   .                 separateur
   1.5 du 13-10-1999 Correction de bug lorsque la chaine touche la limite
   .                 Verification des parametres de config
   .                 Validation avec/sans remontee. Filtrage des chaines
   .                 vides.
   1.6 du 26-11-1999 Mise au point de init (memoire defaillante)
   2.0 du 03-08-2000 Evolution en vue d'accepter plusieurs patterns
   .                 BUG : Si un pattern est reconnu, tout est envoye,
   .                 meme la suite :
   .                    assemble(pRec,"<commande1>"EOL"<commande2>"EOL)
   .                 donne :
   .                    out_trame('<commande1><CR><LF><commande2><CR><LF>')
   .                 au lieu de :
   .                    out_trame('<commande1><CR><LF>')
   .                    out_trame('<commande2><CR><LF>')
   .
   2.1 du 28-12-2000 Correction bug 2.0
   2.2 du 19-01-2001 Suppression calloc()
   2.3 du 15-02-2001 Suppression assert() dans les points d'entree.
   .                 Complete initalisation
   .                 Mise au point suppression du separateur
   2.4 du 13-07-2002 assert() -> ASSERT()
   .                 LCLint
   --------------------------------------------------------------------- */
#include "ed/inc/sys.h"
#include "ed/inc/sysalloc.h"

#include "ed/inc/rec.h"

/* ---------------------------------------------------------------------
   Donnees publiques
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   Constantes privees
   --------------------------------------------------------------------- */
#define MODULE "REC"
#define VER "2.4"
#define ID "REC Module \"C\" (c) ED 1999-2002"

#define DEBUG 1
#define TAG -1
#define CHECK ASSERT(*(pPrivate->poMax)==TAG)

/* Config par defaut */
static const char *const aSep[] =
{
   "\r",                        /* const char *szSep;  separateur */
};

static const sREC_CFG Rec_cfg =
{
   128,                         /* size_t len;   Longueur max d'une trame */
   NELEM (aSep),                /* size_t nbsep; nombre de separateurs */
   aSep,                        /* char **asep;  tableau de separateurs */
   1,                           /* int sep;
                                 *               0=Ne pas remonter le separateur
                                 *               1=Remonter le separateur
                                 */
};

/* ---------------------------------------------------------------------
   Structures privees
   --------------------------------------------------------------------- */
typedef struct
{
   char *szBuf;                 /* entree */
   char *szWrite;
   char *poMax;
   sBUFSZ Trame;                /* sortie */
   sREC_CFG const *pCfg;

   /* sorties */

   fREC_CBTRAME *pfTrame;
   void *pTrameData;

   fREC_CBALARME *pfAlarme;
   void *pAlarmeData;

   fREC_CBDEBUG *pfDebug;
   void *pDebugData;

}
sPRIVATE;

/* ---------------------------------------------------------------------
   Donnees privees
   --------------------------------------------------------------------- */

/* =====================================================================
   Fonctions privees
   ===================================================================== */

/* ---------------------------------------------------------------------
   out_trame()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static void out_trame (sPRIVATE * pPrivate)
{
   /* Trame ? */
   if (pPrivate->Trame.sz)
   {
      /* Fonction ? */
      if (pPrivate->pfTrame)
      {
         /* ne pas remonter de trames vides */
         if (strlen (pPrivate->Trame.sz))
         {
            /* Appel de la fonction de sortie */
            /* LCLint */
            (*pPrivate->pfTrame) (pPrivate->pTrameData
                                  ,&pPrivate->Trame);
         }
         else
         {
            /* la trame est vide, on libere */
            BUFSZ_end (&pPrivate->Trame);
         }
      }
      else
      {
         /* Pas de fonction d'emission, on libere */
         BUFSZ_end (&pPrivate->Trame);
      }
   }
}

/* ---------------------------------------------------------------------
   out_alarme()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static void out_alarme (sPRIVATE * pPrivate)
{
   if (pPrivate->pfAlarme)
   {
      sBUF Ala;

      BUF_init (&Ala
                ,(uchar *) pPrivate->szBuf
                ,pPrivate->pCfg->len
                ,MEM_STA);

      /* LCLint */
      (*pPrivate->pfAlarme) (pPrivate->pAlarmeData, &Ala);
   }
}

#if DEBUG
/* ---------------------------------------------------------------------
   out_debug()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static void out_debug (sPRIVATE * pPrivate
                       ,const char *szRead
                       ,const char *szMsg
)
{
   if (pPrivate->pfDebug)
   {
      sBUF Buf =
      {0, 0, 0};

      BUF_init (&Buf
                ,(uchar *) pPrivate->szBuf
                ,(size_t) (pPrivate->szWrite - pPrivate->szBuf)
                ,MEM_STA
         );

      /* LCLint */
      (*pPrivate->pfDebug) (pPrivate->pDebugData
                            ,&Buf
                            ,pPrivate->szWrite
                            ,szRead
                            ,szMsg
         );
   }
}
#endif

/* ---------------------------------------------------------------------
   reset()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E :
   S :
   --------------------------------------------------------------------- */
static void reset (sPRIVATE * pPrivate)
{
   /* pointer au debut du buffer */
   pPrivate->szWrite = pPrivate->szBuf;
   /* effacer le buffer */
   memset (pPrivate->szBuf, 0, pPrivate->pCfg->len);
}

/* ---------------------------------------------------------------------
   assemble()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E : Adresse de la chaine
   S :
   --------------------------------------------------------------------- */
static void assemble (sPRIVATE * pPrivate, sBUFSZ * pRec)
{
   size_t uLen = strlen (pRec->sz);

   ASSERT (pPrivate != NULL);
   /*  code de la fonction */

   ASSERT (pRec->sz != NULL);
   ASSERT (uLen != 0);

   CHECK;

   if (pPrivate->szWrite + uLen >= pPrivate->poMax)
   {
      /* Erreur on peut liberer */
      BUFSZ_end (pRec);

      out_alarme (pPrivate);
      reset (pPrivate);
#if DEBUG
      out_debug (pPrivate, NULL, "apres reset");
#endif
   }
   else
   {
      int sep;
      /* Recopier les octets */
      ASSERT (pPrivate->szWrite != NULL);
      strcpy (pPrivate->szWrite, pRec->sz);
      CHECK;

      /* la copie est faite, on peut liberer */
      BUFSZ_end (pRec);

      pPrivate->szWrite += uLen;
      ASSERT (*pPrivate->szWrite == 0);
#if DEBUG
      out_debug (pPrivate, NULL, "apres recopie");
#endif
      do
      {
         const char *szSep = NULL;

         /* Chercher le separateur */
         size_t i;
         size_t lsep = 0;

         for (i = 0; i < pPrivate->pCfg->nbsep; i++)
         {
            char const *const ssep = pPrivate->pCfg->aszSep[i];

            szSep = strstr (pPrivate->szBuf, ssep);

            if (szSep)
            {
               lsep = strlen (ssep);
               break;
            }
         }

         /* Si on l'a trouve */
         if (szSep != NULL)
         {
            const char *pRead = szSep;
            size_t len = (size_t) (pRead - pPrivate->szBuf) + lsep;
            /* Longueur avant sep
               szWrite=szBuf+8
               v
               00 01 02 03 04 05 06 07 08 09
               30 31 32 33 0A 34 35 36 00 00 szBuf
               ^
               szRead=szBuf+4
               ** ** ** **
               len=4
             */
            sep = 1;

#if DEBUG
            out_debug (pPrivate, pRead, "apres avoir trouve le separateur");
#endif
            if (len)
            {
               /* allouer une trame */
               char *szTrame = malloc (len + 1);

               if (szTrame != NULL)
               {
#if __PC__
                  memset (szTrame, 0xAA, len + 1);
#endif

                  /* Conserver l'adresse originale */
                  BUFSZ_init (&pPrivate->Trame, szTrame, MEM_DYN);

                  /* recopier la trame */
                  memcpy (szTrame, pPrivate->szBuf, len);
                  szTrame[len] = 0;

                  ASSERT (szTrame[len] == 0);

                  if (pPrivate->pCfg->sep == 0)
                  {
                     /* supprimmer le separateur */
                     szTrame[len - lsep] = 0;
                     ASSERT (szTrame[len] == 0);
                  }

               }                /* !NULL */
               /* compacter la chaine */

            }                   /* len */
            else
            {
#if DEBUG
               out_debug (pPrivate, pRead, "Trame vide");
#endif
            }
            /* appeler l'application "Trame recue" */
            out_trame (pPrivate);

            /* sauter le separateur */
            pRead += lsep;

            /*
               szWrite=szBuf+8
               v
               00 01 02 03 04 05 06 07 08 09
               30 31 32 33 0A 34 35 36 00 00 szBuf Avant
               ^
               szRead=szBuf+5
               ** ** ** **
               len=4
             */
            /* calculer la longeur a deplacer */
#if 1
            len = (size_t) (pPrivate->szWrite - (char *) pRead);  /* Warn */
#else
/* ED/17-11-2000 : suppression cast ? NON */
            len = (size_t) (pPrivate->szWrite - pRead);
#endif
            /*
               szWrite=szBuf+8
               v
               00 01 02 03 04 05 06 07 08 09
               30 31 32 33 0A 34 35 36 00 00 szBuf Avant
               ^
               szRead=szBuf+5
               ** ** **
               len=3
             */
            if (len)
            {
               /* deplacer la fin au debut */
               memmove (pPrivate->szBuf  /* to     */
                        ,pRead  /* from   */
                        ,len    /* length */
                  );
               CHECK;
#if DEBUG
               out_debug (pPrivate, NULL, "apres deplacement");
#endif
            }
            /* Action du move
               szWrite=szBuf+8
               v
               00 01 02 03 04 05 06 07 08 09
               30 31 32 33 0A 34 35 36 00 00 szBuf Avant
               34 35 36 33 0A 34 35 36 00 00 szBuf Apres
               ^
               szRead=szBuf+5
               ** ** **
               len=3
             */
            /* remettre le pointeur d'ecriture a la fin de la zone */
            pPrivate->szWrite = pPrivate->szBuf + len;
            /* Action du move
               szWrite=szBuf+len
               v
               00 01 02 03 04 05 06 07 08 09
               30 31 32 33 0A 34 35 36 00 00 szBuf Avant
               34 35 36 33 0A 34 35 36 00 00 szBuf Apres
               ** ** **
               len=3
             */
#if DEBUG
            out_debug (pPrivate, NULL, "apres mise a jour Write");
#endif
            /* effacer la fin */

            /* calculer la longueur a effacer */
            len = (size_t) (pPrivate->poMax - pPrivate->szWrite);
            if (len)
            {
               memset (pPrivate->szWrite, 0, len);
               CHECK;
            }
            ASSERT (*pPrivate->szWrite == 0);
            /* Effacement
               szWrite=szBuf+5
               v
               00 01 02 03 04 05 06 07 08 09
               34 35 36 33 0A 34 35 36 00 00 szBuf Avant
               34 35 36 33 00 00 00 00 00 00 szBuf Apres
               ** ** ** ** ** **
               len=6
             */
#if DEBUG
            out_debug (pPrivate, NULL, "apres effacement de la fin");
#endif
         }
         else
         {
            sep = 0;
         }
      }
      while (sep);

   }

}

/* =====================================================================
   Fonctions publiques
   ===================================================================== */

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

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

/* ---------------------------------------------------------------------
   REC_init()
   ---------------------------------------------------------------------
   Role : Creation/Initialisation
   ---------------------------------------------------------------------
   E : Taille du buffer de reception
   E : Chaine de separation
   S : Pointeur de donnees
   --------------------------------------------------------------------- */
sREC *REC_init (sREC_CFG const *pCfg)
{
   sREC *pRec = NULL;

   ENUM_CHECK ();

   if (pCfg == NULL)
   {
      /* config par defaut */
      pCfg = &Rec_cfg;
   }

   if (pCfg->len
       && pCfg->nbsep != 0
       && pCfg->aszSep != NULL
       && strlen (*pCfg->aszSep)
      )
   {
      pRec = malloc (sizeof *pRec);

      if (pRec != NULL)
      {
         sPRIVATE *pPrivate = NULL;

         SYS_CLR (pRec, sREC);

         GARBAGE (pRec);

         pRec->pPrivate = NULL;

         pPrivate = malloc (sizeof *pPrivate);

         if (pPrivate != NULL)
         {
            SYS_CLR (pPrivate, sPRIVATE);

            GARBAGE (pPrivate);

            /* enregistrer le pointeur */
            pRec->pPrivate = pPrivate;

            /* code de creation ... */
            pPrivate->szBuf = NULL;

            /* Pointeurs de fonction a NULL jusqu'a nouvel ordre */
            pPrivate->pfTrame = NULL;
            pPrivate->pTrameData = NULL;

            pPrivate->pfAlarme = NULL;
            pPrivate->pAlarmeData = NULL;

            pPrivate->pfDebug = NULL;
            pPrivate->pDebugData = NULL;

            /* Buffer d'assemblage */
            pPrivate->szBuf = calloc (1, pCfg->len + 1);

            if (pPrivate->szBuf != NULL)
            {
               /* pointer au debut du buffer */
               pPrivate->szWrite = pPrivate->szBuf;

               /* determiner la condition d'alarme */
               pPrivate->poMax = pPrivate->szBuf + pCfg->len;

               /* pour le moment, pas de trame */
               BUFSZ_init (&pPrivate->Trame, NULL, MEM_STA);

               /* enregistrer l'adresse de la config */
               pPrivate->pCfg = pCfg;

               /* Marqueur */
               *(pPrivate->poMax) = TAG;
               CHECK;

            }
            else
            {
               /* tout liberer */
               REC_end (pRec);
               pRec = NULL;
            }
         }
         else
         {
            /* tout liberer */
            REC_end (pRec);
            pRec = NULL;
         }
      }
   }
   return pRec;
}

/* ---------------------------------------------------------------------
   REC_end()
   ---------------------------------------------------------------------
   Role : Suppression
   ---------------------------------------------------------------------
   E : Pointeur de donnees
   S :
   --------------------------------------------------------------------- */
void REC_end (sREC * const pRec)
{
   if (pRec != NULL)
   {
      sPRIVATE *const pPrivate = pRec->pPrivate;

      if (pPrivate != NULL)
      {
         /* code de destruction ... */

         if (pPrivate->szBuf != NULL)
         {
            free (pPrivate->szBuf);
            pPrivate->szBuf = NULL;
         }

         free (pPrivate);
         pRec->pPrivate = NULL;
      }
      free (pRec);
   }
}

/* ---------------------------------------------------------------------
   REC_installe_trame()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E : Pointeur de donnees
   S :
   --------------------------------------------------------------------- */
void REC_installe_trame (sREC * const pRec
                         ,fREC_CBTRAME * const pFunc
                         ,void *const pData
)
{
   if (pRec != NULL)
   {
      sPRIVATE *const pPrivate = pRec->pPrivate;

      if (pPrivate != NULL)
      {
         /*  code de la fonction */
         pPrivate->pfTrame = pFunc;
         pPrivate->pTrameData = pData;
      }
   }
}

/* ---------------------------------------------------------------------
   REC_installe_alarme()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E : Pointeur de donnees
   S :
   --------------------------------------------------------------------- */
void REC_installe_alarme (sREC * const pRec
                          ,fREC_CBALARME * const pFunc
                          ,void *const pData
)
{
   if (pRec != NULL)
   {
      sPRIVATE *const pPrivate = pRec->pPrivate;

      if (pPrivate != NULL)
      {
         /*  code de la fonction */
         pPrivate->pfAlarme = pFunc;
         pPrivate->pAlarmeData = pData;
      }
   }
}

/* ---------------------------------------------------------------------
   REC_installe_debug()
   ---------------------------------------------------------------------
   Role :
   ---------------------------------------------------------------------
   E : Pointeur de donnees
   S :
   --------------------------------------------------------------------- */
void REC_installe_debug (sREC * const pRec
                         ,fREC_CBDEBUG * const pFunc
                         ,void *const pData
)
{
   if (pRec != NULL)
   {
      sPRIVATE *const pPrivate = pRec->pPrivate;

      if (pPrivate != NULL)
      {
         /*  code de la fonction */
         pPrivate->pfDebug = pFunc;
         pPrivate->pDebugData = pData;
      }
   }
}

/* ---------------------------------------------------------------------
   REC_assemble()
   ---------------------------------------------------------------------
   Role : Assembler des sequence d'octets en chaine formattee
   ---------------------------------------------------------------------
   E : Pointeur de donnees
   E : Adresse de la chaine
   S :
   --------------------------------------------------------------------- */
void REC_assemble (sREC * const pRec, sBUFSZ * const pBuf)
{
   if (pRec != NULL)
   {
      sPRIVATE *const pPrivate = pRec->pPrivate;

      if (pPrivate != NULL)
      {
         /*  code de la fonction */

         if (pBuf->sz != NULL && strlen (pBuf->sz))
         {
            assemble (pPrivate, pBuf);
         }
         else
         {
            /* Liberer le bloc */
            BUFSZ_end (pBuf);

#if DEBUG
            out_debug (pPrivate, NULL, "Err parametre");
#endif
         }
      }
      else
      {
         /* Liberer le bloc */
         BUFSZ_end (pBuf);
      }
   }
   else
   {
      /* Liberer le bloc */
      BUFSZ_end (pBuf);
   }
}

/* File generated by 'NEW.EXE' Ver 1.17 (c) ED 1998-99 */
