#!/bin/sh
# $Id: config2header,v 1.3 2005/03/05 00:37:39 dasenbro Exp $
#
# Copyright (c) 2001 Carnegie Mellon University.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer. 
#
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
#
# 3. The name "Carnegie Mellon University" must not be used to
#    endorse or promote products derived from this software without
#    prior written permission. For permission or any other legal
#    details, please contact  
#      Office of Technology Transfer
#      Carnegie Mellon University
#      5000 Forbes Avenue
#      Pittsburgh, PA  15213-3890
#      (412) 268-4387, fax: (412) 268-7395
#      tech-transfer@andrew.cmu.edu
#
# 4. Redistributions of any form whatsoever must retain the following
#    acknowledgment:
#    "This product includes software developed by Computing Services
#     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
#
# CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
# OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
exec perl -x -S $0 ${1+"$@"} # -*-perl-*-
#!perl -w

if ($] !~ /^5\..*/) {
  # uh-oh. this isn't perl 5.
  foreach (split(/:/, $ENV{PATH})) { # try to find "perl5".
    exec("$_/perl5", "-x", "-S", $0, @ARGV) if (-x "$_/perl5");
  }
  # we failed. bail.
  die "Your perl is too old; I need perl 5.\n";
}

# load the real script. this is isolated in an 'eval' so perl4 won't
# choke on the perl5-isms.
eval join("\n", <DATA>);
if ($@) { die "$@"; }

__END__
require 5;

use strict;

my $enum_size = 0;
my @enum_values = ();

my $CC;
#
# Look for CC=xxx "assigments" in the argument list.
#
while ($#ARGV >= 0) {
	last unless ($ARGV[0] =~ m/^(\S+)=(\S+)$/);
	eval "\$$1='$2';";
	die "$@" if ($@);
	shift @ARGV;
}

my $use_gcc_extension = ($CC and $CC eq 'gcc');

die "wrong number of arguments" if ($#ARGV != 1);
my ($cfile, $hfile) = @ARGV;

open CFILE, ">$cfile";
open HFILE, ">$hfile";

my $blank = "";
my $version = "\$Revision: 1.3 $blank";
$version =~ s/.Revision: (.*) /$1/;
print HFILE "/* auto-generated by config2header $version */\n";
print CFILE "/* auto-generated by config2header $version */\n";

print HFILE "#ifndef INCLUDED_IMAPOPTS_H\n";
print HFILE "#define INCLUDED_IMAPOPTS_H\n";
print HFILE "\n";

# prototypes
my @opttype = ("OPT_NOTOPT","OPT_STRING","OPT_INT","OPT_SWITCH","OPT_ENUM","OPT_STRINGLIST");
print HFILE "enum opttype {\n";
while (my $opt = pop (@opttype)) {
    if ($#opttype == -1) {
      print HFILE "  $opt\n";
    } else {
      print HFILE "  $opt,\n";
    }
}
print HFILE "};\n\n";

print HFILE <<EOF
enum imapopt {
  IMAPOPT_ZERO = 0,
EOF
    ;

print CFILE <<EOF
/* DO NOT EDIT */
/* THIS FILE AUTOMATICALLY GENERATED BY config2header $version */
#include <config.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include "imapopts.h"

struct imapopt_s imapopts[] =
{
  { IMAPOPT_ZERO, "", 0, { NULL }, OPT_NOTOPT, {  { NULL, IMAP_ENUM_ZERO } } },
EOF
    ;

# output enumeration

while (<STDIN>) {
    next if (/^\#/);
    # look for { option, default, type [enums] }

    # Note that the code we output has to play interesting games to get
    # the union to initialize itself in a syntacticly valid manner.
    # Namely, we need to initialize the union itself, not the members of
    # the union, and we need to ensure we are initilizing the union with
    # something of a type that is in the union.
    if (m|{\s*\"(.*?)\"\s*,\s*(\"?.*?\"?)\s*,\s*(.*?)\s*(\(.*\))?\s*}|) {
	my $opt = $1;
	my $def;
	my $enums = "";

	print HFILE "  IMAPOPT_", uc($opt), ",\n";

	if ($3 eq "STRING") {
	    $def = $use_gcc_extension
			? "(union config_value)((const char *) $2)"
			: "{(void *)($2)}";
	} elsif ($3 eq "ENUM") {
	    my @values = eval $4;
	    my $e;
	    my $v;
	    my $count = 0;

	    # strip quotes from default value
	    $def = substr($2, 1, -1);
	    $e = "IMAP_ENUM_" . uc($opt) . "_" . uc($def);
	    $def = $use_gcc_extension
			? "(union config_value)((enum enum_value) $e)"
			: "{(void *)($e)}";

	    # output the enum_options
	    foreach $v (@values) {
		$e = "IMAP_ENUM_" . uc($opt) . "_" . uc($v);
		$enums .= "{ \"$v\" , $e }, ";

		# if this is the first enum value, normalize to zero
		if (!$count) { $e .= " = 0"; }
		
		# add this enum to enum_values
		push(@enum_values, $e);

		$count += 1;  # count the number of values
	    }

	    # [re]set the size of the enum_options array
	    if ($count > $enum_size) { $enum_size = $count; }
	} elsif ($3 eq "STRINGLIST") {
	    my @values = eval $4;
	    my $v;
	    my $count = 0;

	    $def = $use_gcc_extension
			? "(union config_value)((const char *) $2)"
			: "{(void*)($2)}";

	    # output the enum_options
	    foreach $v (@values) {
		$enums .= "{ \"$v\" , IMAP_ENUM_ZERO }, ";
		$count += 1;  # count the number of values
	    }

	    # [re]set the size of the enum_options array
	    if ($count > $enum_size) { $enum_size = $count; }
	} else {
	    $def = $use_gcc_extension
			? "(union config_value)((int) $2)"
			: "{(void*)$2}";
	}

	print CFILE "  { IMAPOPT_", uc($opt), ", \"$1\", 0, $def, OPT_$3, { $enums { NULL, IMAP_ENUM_ZERO } } },\n";
    } else {
	#chomp;
	#print "rejected '$_'\n";
    }
}

print HFILE <<EOF
  IMAPOPT_LAST
};
extern struct imapopt_s imapopts[];

enum enum_value {
  IMAP_ENUM_ZERO = 0,
EOF
;

# add the enum_values
while (my $e = pop (@enum_values)) {
    if ($#enum_values == -1) {
      print HFILE "  $e\n";
    } else {
      print HFILE "  $e,\n";
    }
}

my $dummy_field = $use_gcc_extension ? '' : 'void *dummy;';
print HFILE <<EOF
};

union config_value {
       $dummy_field
       const char *s;
       int i;
       int b;
       enum enum_value e;
};

struct enum_option_s {
    const char *name;
    const enum enum_value val;
};

struct imapopt_s {
    const enum imapopt opt;
    const char *optname;
    int seen;
    union config_value val;
    const enum opttype t;
EOF
;

print HFILE "    const struct enum_option_s enum_options[$enum_size+1];\n";

print HFILE <<EOF
};
#endif /* INCLUDED_IMAPOPTIONS_H */
EOF
    ;

print CFILE <<EOF
  { IMAPOPT_LAST, NULL, 0, { NULL }, OPT_NOTOPT, {  { NULL, IMAP_ENUM_ZERO } } }
};

/* c code goes here */

EOF
;
