/**
 * @file   SCFMRPUT.c
 * @brief  Sample program in C to write a file as a member into a FLAM5 archive
 * @author limes datentechnik gmbh
 ******************************************************************************/

/* defines code page for literals for IBM mainframes to German */
#ifdef __ZOS__
#  pragma convert(1141)
#endif

/* In debug builds the FL5 MEMCHK allocator adds tracking overhead to every
 * allocation, so free() on library-returned pointers requires CHKMEM.h to
 * redirect free() to debug_free().  Not needed (and not shipped) in release. */
#ifdef __DEBUG__
#  include"CHKMEM.h"
#endif

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#include"FLCBYT.h"
#include"FLMREC.h"

/**
 * This sample program reads an input file via the FLUC byte interface
 * (FLCBYT) and writes it as a named member into a FLAM5 archive via
 * the FLMREC C API.
 *
 * The member name and state attributes (ORGF, RECF, RECL) are both
 * taken from the state string returned by fcbopen2() and applied via
 * siFmrSetMbrState().  The ORGF field in the state string contains the
 * fully qualified file name, which becomes the member name.  fcbgfn()
 * is called only to display the input file name on stdout.
 *
 * If the archive does not yet exist it is created automatically with
 * default FLAM compression. If it already exists, the member is added
 * or overwritten (mbrmode=overwrite).
 *
 * @param argc must be 3
 * @param argv[1] input file path or dataset name (read.file='...')
 * @param argv[2] USS archive file path
 * @return 8=parameter error
 *         6=open error
 *         4=read/write error
 *         2=close error
 *         0=success
 *
 * Link with: -lflcbyt -lflmrec   (plus -lfl5core for debug builds)
 */
int main(int argc, const char* argv[])
{
   char                 acErr[32768];
   char                 acFilStr[1024];
   char                 acArcStr[1024];
   /* static: avoids a 64 KB stack frame; safe here because this
    * program is single-threaded and not reentrant across calls. */
   static unsigned char acBuf[65536];
   void*                pvGblHdl = NULL;
   void*                pvFcbHdl = NULL;
   void*                pvFmrHdl = NULL;
   char*                pcState  = NULL;
   char*                pcStaBuf = NULL;
   const char*          pcFqn    = NULL;
   unsigned int         uiLen    = 0;
   int                  siStaSiz = 0;
   int                  siRtc    = 0;
   int                  siSuc    = 0;
   int                  siRecs   = 0;

/* defines environment variable LANG to German EBCDIC for IBM mainframes */
#ifdef __ZOS__
   __setenv("LANG","de_DE.IBM-1141",1);
#endif

   if (argc != 3) {
      fprintf(stderr,
         "Parameter count (%d) not correct (3) to run this sample program\n",
         argc);
      fprintf(stderr, "Required parameters:\n");
      fprintf(stderr,
         "   %s \"input file path or dataset\" \"archive path\"\n",
         argv[0]);
      return 8;
   }

   /* build FCBOPEN file string: read.file='<argv[1]>' */
   snprintf(acFilStr, sizeof(acFilStr), "read.file='%s'", argv[1]);

   /* open global FLAM session: loads environment and STDENV on z/OS */
   siRtc = siFmrOpen(acErr, &pvGblHdl, 1, 1, NULL, NULL);
   if (siRtc != FLMRTC_OK) {
      fprintf(stderr, "siFmrOpen error %d (%s)\n%s\n",
         siRtc, pcFmrErrorMessage(siRtc), acErr);
      return 6;
   }

   /* open archive: try to update an existing one; on FLMRTC_NFD (RC=157,
    * archive file not found) create a new archive with default compression */
   snprintf(acArcStr, sizeof(acArcStr),
      "to.mod(store.file(name='%s') flam(mbrmode=overwrite))", argv[2]);
   siRtc = siFmrOpenArchive(acErr, pvGblHdl, &pvFmrHdl, acArcStr, NULL, 0);
   if (siRtc == FLMRTC_NFD) {
      snprintf(acArcStr, sizeof(acArcStr),
         "to.new(store.file(name='%s'))", argv[2]);
      siRtc = siFmrOpenArchive(acErr, pvGblHdl, &pvFmrHdl, acArcStr, NULL, 0);
   }
   if (siRtc != FLMRTC_OK) {
      fprintf(stderr, "siFmrOpenArchive error %d (%s)\n%s\n",
         siRtc, pcFmrErrorMessage(siRtc), acErr);
      return 6;
   }

   /* open input file via FLUC byte interface; fcbopen2 fills pcState */
   pvFcbHdl = fcbopen2(acFilStr, "format.record()", &pcState);
   if (pvFcbHdl == NULL) {
      fprintf(stderr, "fcbopen2 error %d for '%s'\n%s\n%s\n",
         fcberrno, acFilStr, fcberrms(fcberrno), fcberrtr());
      (void)siFmrCloseArchive(acErr, pvFmrHdl, 0, 0, NULL, NULL);
      return 6;
   }

   /* get the fully qualified name of the input file for display */
   pcFqn = fcbgfn(pvFcbHdl, 0);

   /* set member state (ORGF, RECF, RECL etc.) -- this also sets the member name */
   if (pcState != NULL && pcState[0] != '\0') {
      siRtc = siFmrSetMbrState(NULL, pvFmrHdl, pcState);
      if (siRtc != FLMRTC_OK) {
         fprintf(stderr, "siFmrSetMbrState error %d (%s)\n",
            siRtc, pcFmrErrorMessage(siRtc));
         goto cleanup;
      }
   }

   fprintf(stdout, "Input  : %s\n",
      (pcFqn != NULL && pcFqn[0] != '\0') ? pcFqn : argv[1]);
   fprintf(stdout, "Archive: %s\n", argv[2]);
   if (pcState != NULL && pcState[0] != '\0') {
      fprintf(stdout, "State  : %s\n", pcState);
   }

   /* read records and write into archive member */
   for (uiLen = fcbread(acBuf, 1, sizeof(acBuf), pvFcbHdl);
        fcberror(pvFcbHdl) == FLMRTC_OK ||
        (fcberror(pvFcbHdl) == FLMRTC_EOF && uiLen);
        uiLen = fcbread(acBuf, 1, sizeof(acBuf), pvFcbHdl)) {
      siRtc = siFmrPut(acErr, pvFmrHdl, (int)uiLen, acBuf, NULL);
      if (siRtc != FLMRTC_OK) {
         fprintf(stderr, "siFmrPut error %d (%s)\n%s\n",
            siRtc, pcFmrErrorMessage(siRtc), acErr);
         goto cleanup;
      }
      siRecs++;
      if ((siRecs % 10000) == 0) {
         fprintf(stdout, "%d records written ...\n", siRecs);
      }
   }
   if (fcberror(pvFcbHdl) != FLMRTC_OK &&
       fcberror(pvFcbHdl) != FLMRTC_EOF) {
      fprintf(stderr, "fcbread error %d after %d records\n%s\n%s\n",
         fcberror(pvFcbHdl), siRecs,
         fcberrms(fcberror(pvFcbHdl)), fcberrtr());
      goto cleanup;
   }
   siRtc = FLMRTC_OK;
   siSuc = 1;

cleanup:
   /* close input file */
   if (pvFcbHdl != NULL) {
      (void)fcbclose(pvFcbHdl);
      pvFcbHdl = NULL;
   }

   /* free state string allocated by fcbopen2 */
   if (pcState != NULL) {
      free(pcState);
      pcState = NULL;
   }

   /* close archive and print statistics */
   {
      int siFmrCls;
      siFmrCls = siFmrCloseArchive(acErr, pvFmrHdl, siSuc,
                                    FLMOUT_FORMAT_LST, &siStaSiz, &pcStaBuf);
      if (siFmrCls != FLMRTC_OK && siRtc == FLMRTC_OK) {
         fprintf(stderr, "siFmrCloseArchive error %d (%s)\n%s\n",
            siFmrCls, pcFmrErrorMessage(siFmrCls), acErr);
         siRtc = siFmrCls;
      }
      if (pcStaBuf != NULL) {
         fprintf(stdout, "\nStatistics:\n%s\n", pcStaBuf);
         free(pcStaBuf);
      }
   }

   /* close global FLAM session */
   if (pvGblHdl != NULL) {
      (void)siFmrClose(pvGblHdl);
      pvGblHdl = NULL;
   }

   if (siSuc) {
      fprintf(stdout, "Done: %d records written to '%s'\n", siRecs, argv[2]);
   }

   return (siRtc != FLMRTC_OK) ? 4 : 0;
}
