/**
 * Bin2Rexx: "DosOpen() / DosRead() / DosWrite() / DosClose()" example.
 */

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

#define WR /*WR*/
#define RC /*RC*/

#define Run int main
#define let void

#define const struct /*cs-data-types*/
#define set enum class /*cs-values*/

#ifndef STDIN
#define STDIN  0
#define STDOUT 1
#define STDERR 2
#endif

let Say (PBYTE String) { ULONG Written = 0; DosWrite (STDOUT, String, strlen (String), &Written); }

let Say (PBYTE A, PBYTE B) { Say (A); Say (B); }
let Say (PBYTE A, PBYTE B, PBYTE C) { Say (A); Say (B); Say (C); }
let Say (PBYTE A, PBYTE B, PBYTE C, PBYTE D) { Say (A); Say (B); Say (C); Say (D); }
let Say (PBYTE A, PBYTE B, PBYTE C, PBYTE D, PBYTE E) { Say (A); Say (B); Say (C); Say (D); Say (E); }

/* * */

const REXX
{
  set SCRIPT
  {
    BYTES_TO_JOIN = 16,
    LINE_LENGTH   = 256
  };
};

/* * */

let PrintRexxScript (PBYTE Memory, ULONG Length, PCHAR File_name)
{
  let PrintProlog ()
  {
    Say ("/* * */", "\r\n");
    Say ("\r\n");
    Say ("V = ''", "\r\n");
  }

  let PrintScript (PBYTE Memory, ULONG Length, PCHAR File_name)
  {
    let JoinHexValues (PCHAR WR Line, PBYTE Memory, INT Length, INT Cntr, INT Bytes_to_join)
    {
      for (INT Offset = 0; Offset < Bytes_to_join; Offset ++) 
      {
        INT Position = Cntr + Offset;

        if (Position < Length)
        {
          CHAR Hex_value[4] = {0};

          {
            BYTE Bin_value = (BYTE) Memory[Position]; ultoa (Bin_value, Hex_value, 16);

            INT Hex_value_length = strlen (Hex_value);

            if (Hex_value_length == 1) 
            {
              Hex_value[2] = 0; Hex_value[1] = Hex_value[0]; Hex_value[0] = '0';
            }

            if (islower (Hex_value[0])) Hex_value[0] = toupper (Hex_value[0]);
            if (islower (Hex_value[1])) Hex_value[1] = toupper (Hex_value[1]);
          }

          strcat (Line, "'");
          strcat (Line, Hex_value);
          strcat (Line, "'x");
        }
      }
    }

    for (INT Cntr = 0; Cntr < Length; Cntr ++) 
    {
      CHAR Line[REXX::SCRIPT::LINE_LENGTH] = ""; strcpy (Line, "V = V || ");

      JoinHexValues (Line, Memory, Length, Cntr, REXX::SCRIPT::BYTES_TO_JOIN);

      Say (Line, "\r\n");

      Cntr += (REXX::SCRIPT::BYTES_TO_JOIN - 1);
    }
  }

  let PrintEpilog (PCHAR File_name)
  {
    Say ("\r\n");
    Say ("File = '", File_name, "'", "\r\n");
    Say ("Call SysFileDelete File", "\r\n");
    Say ("Call CharOut File, V", "\r\n");
    Say ("\r\n");
    Say ("Exit");
  }

  PrintProlog ();
  PrintScript (Memory, Length, File_name);
  PrintEpilog (File_name);
}

/* * */

Run (INT Arg_c, PCHAR Arg_v[])  
{
  if (Arg_c != 2 || !strstr (Arg_v[1], ".")) 
  {
    Say ("Usage: Bin2Rexx Binary-file.ext > Rexx-script.cmd");
  }
  else
  {
    HFILE File   = NULLHANDLE; 
    ULONG Action = OPEN_ACTION_OPEN_IF_EXISTS;
    ULONG Mode   = OPEN_SHARE_DENYWRITE |
                   OPEN_ACCESS_READONLY;
    ULONG Report = 0;

    DosOpen (Arg_v[1], &File, &Report, 0, 0, Action, Mode, NULL);

    if (File) 
    {
      PBYTE Memory = NULL; ULONG Length = 0; 

      DosSetFilePtr (File, 0, FILE_END, &Length); 
      DosAllocMem ((PPVOID) &Memory, Length, PAG_READ | PAG_WRITE | PAG_COMMIT);
      DosRead (File, Memory, Length, &Report);

      PrintRexxScript (Memory, Length, Arg_v[1]);

      DosFreeMem (Memory);
      DosClose (File);
    }
    else
    {
      DosExit (EXIT_PROCESS, -1);
    }
  }

  return 0; 
}