32.1 Compiling a text file into a C++ program

This section describes how to automatically compile a text file into a C++ program, using a simple makefile rule and a perl script. Using this method the text file is required at compile time but NOT at runtime, and the contents of the file will be accessible as a const unsigned char*. Several of the functions described later in this chapter take a const unsigned char* created by this method.

This following perl script

#!/usr/bin/perl
# text2hex.pl
# OpenEye Scientific Software
# September 2001

# Autoflush STDOUT
#STDOUT->autoflush(1);
$| = 1;

$argc = @ARGV;
if( $argc == 2 ) {
    $filename = @ARGV[0];
    $arrayname = @ARGV[1];

    $pos1 = rindex($filename,"/") + 1;
    if ($pos1 > -1)
    {
      $tmpguard = substr($filename,$pos1);
    }
    else
    {
      $tmpguard = $filename;
    }
    $guard = uc($tmpguard);
    for ( $pos2 = rindex($guard,".") ; $pos2 > -1 ; $pos2 = rindex($guard,".") )
    {
      $lnth = length($guard);
      $pos3 = $pos2 + 1;
      $pos4 = $lnth - $pos3;
      $tmpguard = $guard;
      $guard = substr($tmpguard,0,$pos2) . "_" . substr($tmpguard,$pos3,$pos4);
    }
    for ( $pos2 = rindex($guard,"-") ; $pos2 > -1 ; $pos2 = rindex($guard,"-") )
    {
      $lnth = length($guard);
      $pos3 = $pos2 + 1;
      $pos4 = $lnth - $pos3;
      $tmpguard = $guard;
      $guard = substr($tmpguard,0,$pos2) . "_" . substr($tmpguard,$pos3,$pos4);
    }
} elsif( $argc == 3 ) {
    $filename = @ARGV[0];
    $arrayname = @ARGV[1];
    $guard = @ARGV[2];
} else {
    print "usage:  bin2hex.pl <textfile> <arrayname>\n\n";
    exit;
}

$debug = 0;

open(F,$filename) || die "Error: Unable to open binary file!\n";

if( !$debug ) {
    print "#ifndef OE_" . $guard . "\n";
    print "#define OE_" . $guard . "\n\n";
    print "static const unsigned char " . $arrayname . "[] = {\n";
}

binmode(F);

$col = 0;
$init = 0;
$ignore = 0;
$newline = 1;

while( !eof(F) ) {
    $ch = ord(getc(F));
    if( $ch == 13 ) {
        $ch = 0;
    }

    if( $ignore ) {
        if( $ch == 10 ) {
            $ignore = 0;
        }
        $ch = 0;
    } elsif( $newline ) {
        if ( $ch == 35 ) {
            $ignore = 1;
            $ch = 0;
        } elsif( $ch ) {
            $newline = 0;
        }
    } elsif( $ch == 10 ) {
        $newline = 1;
    }

    if( $ch ) {
        if( $debug ) {
            print chr($ch);
        } else {
            if( $init ) {
                print ",";
            } else {
                $init = 1;
            }
            if( $col >= 15 ) {
                print "\n";
                $col = 0;
            }
            print sprintf("0x%02X",$ch);
            $col++;
        }
    }
}

if( !$debug ) {
    if( $col >= 15 ) {
        print ",\n0x00};\n\n";
    } else {
        print ",0x00};\n\n";
    }
    print "#endif // OE_" . $guard . "\n\n";
}

close(F);
exit;

Converts a text file into a C++ const unsigned char*. The usage of this script is

text2hex.pl <textfile> <arrayname>

And the output is sent so standard out.

So for example if we had a text file

OOGA
BOOGA

named babytalk.txt

text2hex.pl would produce the following output

> text2hex.pl babytalk.txt baby
#ifndef OE_BABYTALK
#define OE_BABYTALK

static const unsigned char baby[] = {
0x4F,0x4F,0x47,0x41,0x0A,0x42,0x4F,0x4F,0x47,0x41,0x0A,0x00};

#endif // OE_BABYTALK_ITF
>

With this perl script we can put the following rule in our makefile

.SUFFIXES:      .txt
.SUFFIXES:      .itf
.txt.itf:
                text2hex.pl $< InterfaceData > $@

This rule tells the make system that it can create a file with a file with a .itf extension from a file with a .txt extension by using the text2hex.pl script and piping the output into the .itf file. This is exactly analogous to rules that create a .o file from a .cpp file (here .txt corresponds to .cpp and .itf corresponds to .o). Note that the choice of itf as an extension is completely arbitrary, but the OpenEye supplied makefiles define this rule with the .itf extension, and also another rule with the .req extension as follows

.SUFFIXES:      .txt
.SUFFIXES:      .req
.txt.req:
                text2hex.pl $< RequirementData > $@

The following program

Chapter 32 - Example 1: cpp file

#include "oeplatform.h"
#include "oesystem.h"

//This .itf file defines the static unsigned char* variable InterfaceData
#include "ch32-1.itf"

using namespace OEPlatform;
using namespace OESystem;

int main(int , char** )
{
  oeout << InterfaceData << oeendl;
  return 0;
}

Includes a .itf file. Provided the above rule for generating and itf file is included in the makefile, the only additional modification to the makefile needed to compile ch32-1.o, aside from the normal compiling riles, is ch32-1.o must have a dependency on ch32-1.txt added to it. As with any makefile dependencies, this dependency can be added by including this line

Chapter 32 - Example 1: Makefile dependency

ch32-1.o: ch32-1.txt

Anywhere in the makefile. Alternatively you could add ch32-1.txt to your existing dependency list for ch32-1.o (which presumably includes ch32-1.cpp).

Once compiled the program simply outputs the contents of file ch32-1.txt it was compiled with. So given that the contents of ch32-1.txt were

Chapter 32 - Example 1: Text file

#include "oeplatform.h"
#include "oesystem.h"

//This .itf file defines the static unsigned char* variable InterfaceData
#include "ch32-1.itf"

using namespace OEPlatform;
using namespace OESystem;

int main(int , char** )
{
  oeout << InterfaceData << oeendl;
  return 0;
}

At compile time the program, ch32-1, will work as follows

Chapter 32 - Example 1: Output

> ch32-1
OOGA
BOOGA

>

Note that ch32-1.txt is only required at compile time, not run time.