The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
################################################################################
#
# $Project: /Devel-Tokenizer-C $
# $Author: mhx $
# $Date: 2008/12/13 16:00:41 +0100 $
# $Revision: 7 $
# $Source: /t/common.sub $
#
################################################################################
# 
# Copyright (c) 2002-2008 Marcus Holland-Moritz. All rights reserved.
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
# 
################################################################################

use Config;
use IO::File;
use IPC::Open3;

sub get_key
{
  my $key = shift;
  $key =~ s/\W/_/g;
  return $key;
}

sub gencode
{
  my($dtc, $words, $prefix, $suffix) = @_;

  $prefix ||= '';
  $suffix ||= '';

  my $code = $dtc->generate;
  my $enum = join "\n", map "  KEY_".get_key($_).",", @$words;

<<ENDC;
#include <stdio.h>

#define TOKEN_END '\\0'

typedef enum {
$enum
  KEY_UNKNOWN
} Keyword;

Keyword tokenize( const char *tokstr )
{
$prefix
$code
$suffix
}

int main( int argc, char **argv )
{
  FILE *in;
  char *c, buffer[256];
  Keyword key;

  in = fopen( argv[1], "r" );
  
  while( fgets(buffer, 256, in) ) {
    for( c=buffer; *c; c++ ) {
      if( *c == '\\n' || *c == '\\r' ) {
        *c = '\\0';
        break;
      }
    }
    key = tokenize(buffer);
    printf("\\"\%s\\" => \%d\\n", buffer, key);
  }

  fclose( in );

  return 0;
}
ENDC
}

sub runtest
{
  my $skip = shift;
  my $src  = shift;
  my $in   = shift;

  my %opt = (
    'unlink'  => !exists($ENV{UNLINK}) || $ENV{UNLINK},
    'ccflags' => [],
    @_
  );

  for( $src, $in ) {
    if( 'ARRAY' eq ref ) {
      $_ = join( "\n", @$_ ) . "\n";
    }
  }

  my $file   = './dtcrun.c';
  my $infile = './dtcdata';
  my $exe    = "./dtcrun";
  $^O eq 'MSWin32' and $exe .= '.exe';

  IO::File->new(">$file")->print($src);

  print "# compiling $file into $exe\n";
  my $r = runprog( $Config{cc}, split(' ', $Config{ccflags}), @{$opt{ccflags}}, '-o', $exe, $file );

  skip( $skip, $r->{didnotrun}, 0, "$Config{cc} did not run" );
  skip( $skip, $r->{status}, 0, "$Config{cc} did not return status 0" );

  IO::File->new(">$infile")->print($in);

  $r = runprog( $exe, $infile );

  skip( $skip, $r->{didnotrun}, 0, "$exe did not run" );
  skip( $skip, $r->{status}, 0, "$exe did not return status 0" );

  $opt{'unlink'} and unlink $file, $infile, $exe;

  return($r->{stdout}, $r->{stderr});
}

sub can_compile
{
  my $file = 'dtctest.c';
  my $exe  = "./dtctest";
  $^O eq 'MSWin32' and $exe .= '.exe';

  IO::File->new(">$file")->print( <<ENDC );
#include <stdio.h>
int main( void )
{
  printf("Hello\\n");
  return 0;
}
ENDC

  my $r = runprog( $Config{cc}, split(' ', $Config{ccflags}), '-o', $exe, $file );

  $r->{didnotrun} == 0 &&
  $r->{status}    == 0    or return 0;
  
  $r = runprog( $exe );

  unlink $file, $exe;

  $r->{didnotrun} == 0 &&
  $r->{status}    == 0    or return 0;

  for( @{$r->{stdout}} ) {
    /^Hello$/ and return 1;
  }

  return 0;
}

sub runprog
{
  my $prog = shift;
  my @args = @_;

  my(@sout, @serr);
  local(*W, *S, *E);

  print "# running $prog @args\n";

  my $pid = open3(\*W, \*S, \*E, $prog, @args);

  eval {
    @sout = <S>;
    @serr = <E>;
    waitpid($pid, 0);
  };

  my %rval = (
    status => $? >> 8,
    stdout => \@sout,
    stderr => \@serr,
  );

  $rval{didnotrun} = 0;

  if( @serr && $serr[0] =~ /^Can't exec "\Q$prog\E":/ ) {
    $rval{didnotrun} = 1;
  }

  if( $^O eq 'MSWin32' && $rval{status} == 1 ) {
    $rval{didnotrun} = 1;
  }

  $? & 128 and $rval{core}   = 1;
  $? & 127 and $rval{signal} = $? & 127;
 
  \%rval;
}