/*-
 ***********************************************************************
 *
 * $Id: pad-join.c,v 1.10 2012/01/07 07:56:14 mavrik Exp $
 *
 ***********************************************************************
 *
 * Copyright 2002-2012 The WebJob Project, All Rights Reserved.
 *
 ***********************************************************************
 */
#include "pad-common.h"
#include "pad-join.h"

/*-
 ***********************************************************************
 *
 * Global Variables.
 *
 ***********************************************************************
 */
void *gpvProperties;
PAD_CALL_TABLE gsCallTable[] =
{
  { PAD_VOID_CALL main, "PaDMain()" },
  { PAD_VOID_CALL PaDBootStrap, "PaDBootStrap()" },
  { PAD_VOID_CALL PaDGetBasename, "PaDGetBasename()" },
  { PAD_VOID_CALL PaDGetCallname, "PaDGetCallname()" },
  { PAD_VOID_CALL PaDGetEnvValue, "PaDGetEnvValue()" },
  { PAD_VOID_CALL PaDGetMyHandle, "PaDGetMyHandle()" },
  { PAD_VOID_CALL PaDGetPropertiesReference, "PaDGetPropertiesReference()" },
  { PAD_VOID_CALL PaDLocateDelimiter, "PaDLocateDelimiter()" },
  { PAD_VOID_CALL PaDNewDelimiter, "PaDNewDelimiter()" },
  { PAD_VOID_CALL PaDNewProperties, "PaDNewProperties()" },
  { PAD_VOID_CALL PaDProcessArguments, "PaDProcessArguments()" },
  { PAD_VOID_CALL PaDReadWrite, "PaDReadWrite()" },
  { PAD_VOID_CALL PaDSetPropertiesReference, "PaDSetPropertiesReference()" },
  { PAD_VOID_CALL PaDShutdown, "PaDShutdown()" },
  { PAD_VOID_CALL PaDUsage, "PaDUsage()" },
  { PAD_VOID_CALL PaDWorkHorse, "PaDWorkHorse()" }
};
int giNCalls = (sizeof(gsCallTable) / sizeof(gsCallTable[0]));

/*-
 ***********************************************************************
 *
 * PaDMain
 *
 ***********************************************************************
 */
int
main(int iArgumentCount, char *ppcArgumentVector[])
{
  void              (*pvCall)() = PAD_VOID_CALL main;
  char                acLocalError[MESSAGE_SIZE];
  int                 iError;
  PAD_JOIN_PROPERTIES *psProperties;

  /*-
   *********************************************************************
   *
   * Punch in and go to work.
   *
   *********************************************************************
   */
  iError = PaDBootStrap(acLocalError);
  if (iError != ER_OK)
  {
    fprintf(stderr, "%s: %s\n", PaDGetCallname(pvCall), acLocalError);
    PaDShutdown(XER_BootStrap);
  }
  psProperties = (PAD_JOIN_PROPERTIES *) PaDGetPropertiesReference();

  /*-
   *********************************************************************
   *
   * Process command line arguments.
   *
   *********************************************************************
   */
  iError = PaDProcessArguments(iArgumentCount, ppcArgumentVector, psProperties, acLocalError);
  if (iError != ER_OK)
  {
    fprintf(stderr, "%s: %s\n", PaDGetCallname(pvCall), acLocalError);
    PaDShutdown(XER_ProcessArguments);
  }

  /*-
   *********************************************************************
   *
   * Answer the mail.
   *
   *********************************************************************
   */
  iError = PaDWorkHorse(psProperties, acLocalError);
  if (iError != ER_OK)
  {
    fprintf(stderr, "%s: %s\n", PaDGetCallname(pvCall), acLocalError);
    PaDShutdown(XER_WorkHorse);
  }

  /*-
   *********************************************************************
   *
   * Shutdown and go home.
   *
   *********************************************************************
   */
  PaDShutdown(XER_OK);

  return XER_OK;
}


/*-
 ***********************************************************************
 *
 * PaDBootStrap
 *
 ***********************************************************************
 */
int
PaDBootStrap(char *pcError)
{
  void              (*pvCall)() = PAD_VOID_CALL PaDBootStrap;
  char                acLocalError[MESSAGE_SIZE];
#ifdef WIN32
  int                 iError;
#endif
  PAD_JOIN_PROPERTIES *psProperties;

#ifdef WIN32
  /*-
   *********************************************************************
   *
   * Suppress critical-error-handler message boxes.
   *
   *********************************************************************
   */
  SetErrorMode(SEM_FAILCRITICALERRORS);

  /*-
   *********************************************************************
   *
   * Put stdout in binary mode to prevent CRLF mappings.
   *
   *********************************************************************
   */
  iError = _setmode(_fileno(stdout), _O_BINARY);
  if (iError == -1)
  {
    snprintf(pcError, MESSAGE_SIZE, "%s: _setmode(): Stream='stdout' Error='%s'", PaDGetCallname(pvCall), strerror(errno));
    return ER;
  }
#endif

  /*-
   *********************************************************************
   *
   * Allocate and initialize the properties structure.
   *
   *********************************************************************
   */
  psProperties = (PAD_JOIN_PROPERTIES *) PaDNewProperties(sizeof(PAD_JOIN_PROPERTIES), acLocalError);
  if (psProperties == NULL)
  {
    snprintf(pcError, MESSAGE_SIZE, "%s: %s", PaDGetCallname(pvCall), acLocalError);
    return ER;
  }
  PaDSetPropertiesReference((void *)psProperties);

  return ER_OK;
}


/*-
 ***********************************************************************
 *
 * PaDProcessArguments
 *
 ***********************************************************************
 */
int
PaDProcessArguments(int iArgumentCount, char *ppcArgumentVector[], PAD_JOIN_PROPERTIES *psProperties, char *pcError)
{
  if (iArgumentCount == 3)
  {
    psProperties->pcPaDGuts = ppcArgumentVector[1];
    psProperties->pcPayload = ppcArgumentVector[2];
  }
  else if (iArgumentCount == 5)
  {
    if (strcmp(ppcArgumentVector[1], "-d") == 0 || strcmp(ppcArgumentVector[1], "--delimiter") == 0)
    {
      psProperties->pcDelimiter = ppcArgumentVector[2];
      psProperties->pcPaDGuts = ppcArgumentVector[3];
      psProperties->pcPayload = ppcArgumentVector[4];
    }
    else
    {
      PaDUsage(ppcArgumentVector[0]);
    }
  }
  else
  {
    PaDUsage(ppcArgumentVector[0]);
  }
  return ER_OK;
}


/*-
 ***********************************************************************
 *
 * PaDShutdown
 *
 ***********************************************************************
 */
void
PaDShutdown(int iError)
{
  PAD_JOIN_PROPERTIES *psProperties;

  psProperties = (PAD_JOIN_PROPERTIES *) PaDGetPropertiesReference();
  if (psProperties != NULL)
  {
    free(psProperties);
  }
  exit(iError);
}


/*-
 ***********************************************************************
 *
 * PaDUsage
 *
 ***********************************************************************
 */
void
PaDUsage(char *pcProgram)
{
  fprintf(stderr, "\n");
  fprintf(stderr, "Usage: %s [{-d|--delimiter} string] pad-guts payload\n", PaDGetBasename(pcProgram));
  fprintf(stderr, "\n");
  exit(XER_Usage);
}


/*-
 ***********************************************************************
 *
 * PaDWorkHorse
 *
 ***********************************************************************
 */
int
PaDWorkHorse(PAD_JOIN_PROPERTIES *psProperties, char *pcError)
{
  void              (*pvCall)() = PAD_VOID_CALL PaDWorkHorse;
  char                acData[PAD_READ_SIZE];
  char                acLocalError[MESSAGE_SIZE];
  char               *pcDelimiter;
  char               *pcFilename[2];
  FILE               *pFile[2];
  int                 i;
  int                 iInUse;
  int                 iNRead;
  int                 iNWritten;

  /*-
   *********************************************************************
   *
   * Allocate and initialize the PaD delimiter.
   *
   *********************************************************************
   */
  if (psProperties->pcDelimiter == NULL)
  {
    pcDelimiter = PaDNewDelimiter(PAD_GUTS_TEMPLATE, 1, acLocalError);
  }
  else
  {
    pcDelimiter = PaDNewDelimiter(psProperties->pcDelimiter, 0, acLocalError);
  }
  if (pcDelimiter == NULL)
  {
    snprintf(pcError, MESSAGE_SIZE, "%s: %s", PaDGetCallname(pvCall), acLocalError);
    return ER;
  }

  /*-
   *********************************************************************
   *
   * Open input files.
   *
   *********************************************************************
   */
  pcFilename[0] = psProperties->pcPaDGuts;
  pcFilename[1] = psProperties->pcPayload;
  for (i = iInUse = 0, pFile[0] = pFile[1] = NULL; i < 2; i++)
  {
    if (strcmp(pcFilename[i], "-") == 0 && iInUse == 0)
    {
      pFile[i] = stdin;
      iInUse = 1;
    }
    else
    {
      pFile[i] = fopen(pcFilename[i], "rb");
      if (pFile[i] == NULL)
      {
        snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): File='%s' Error='%s'", PaDGetCallname(pvCall), pcFilename[i], strerror(errno));
        while (i >= 0)
        {
          if (pFile[i] != NULL && pFile[i] != stdin)
          {
            fclose(pFile[i]);
          }
          i--;
        }
        return ER;
      }
    }
  }

  /*-
   *********************************************************************
   *
   * Read input files and write PaD output to stdout.
   *
   *********************************************************************
   */
  for (i = 0; i < 2; i++)
  {
    while ((iNRead = fread(acData, 1, PAD_READ_SIZE, pFile[i])))
    {
      iNWritten = fwrite(acData, 1, iNRead, stdout);
      if (iNWritten != iNRead)
      {
        snprintf(pcError, MESSAGE_SIZE, "%s: fwrite(): File='stdout' Error='%s'", PaDGetCallname(pvCall), strerror(errno));
        return ER;
      }
    }
    if (ferror(pFile[i]))
    {
      snprintf(pcError, MESSAGE_SIZE, "%s: fread(): File='%s' Error='%s'", PaDGetCallname(pvCall), pcFilename[i], strerror(errno));
      return ER;
    }
    fclose(pFile[i]);
    if (i == 0)
    {
      iNWritten = fwrite(pcDelimiter, 1, strlen(pcDelimiter), stdout);
      if (iNWritten != strlen(pcDelimiter))
      {
        snprintf(pcError, MESSAGE_SIZE, "%s: fwrite(): File='stdout' Error='%s'", PaDGetCallname(pvCall), strerror(errno));
        return ER;
      }
    }
  }

  free(pcDelimiter);

  return ER_OK;
}
