/* Copyright (C) Boris Nikolaus, Germany, 1996-1997. All rights reserved. */
/* Copyright (C) Microsoft Corporation, 1997-1998. All rights reserved. */

#include "precomp.h"

#define IDCHRSET "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"

#define INDENT 4
#define TABSIZE 8

static FILE *fout;
static int xcurrindent = 0;
static int xindentflag = 0;
static char *xbuf = 0;
static int xbufsize = 0;
static int xbuflen = 0;
static int ycurrindent = 1;
static char *ybuf = 0;
static int ybufsize = 0;
static int ybuflen = 0;

void xputc(char c);
void xputs(char *s);
void xflush();
void yputc(char c);
void yputs(char *s);
void yflush();

/* set the output file */
void
setoutfile(FILE *f)
{
    xflush();
    fout = f;
}

/* print indentation up to current indentation level */
static void
findent()
{
    int indent;

    indent = xcurrindent * INDENT;
    while (indent >= TABSIZE) {
	xputc('\t');
	indent -= TABSIZE;
    }
    while (indent-- > 0)
	xputc(' ');
}

/* print indentation up to current indentation level */
/* but expect one character to be printed already */
static void
findent1()
{
    int indent;

    indent = xcurrindent * INDENT;
    if (indent > 0 && indent < TABSIZE)
	indent--;
    while (indent >= TABSIZE) {
	xputc('\t');
	indent -= TABSIZE;
    }
    while (indent-- > 0)
	xputc(' ');
}

/* output function doing indentation automatically */
void
outputv(const char *format, va_list args)
{
    static char buf[4098];
    static int pos = 0;
    char *p, *q;
    int l;
    
    /* get the string to write */
    vsprintf(buf + pos, format, args);

    /* print it line by line */
    for (p = buf; *p; p = q) {
	q = strchr(p, '\n');
	if (!q) {
	    for (q = buf; *p;)
		*q++ = *p++;
	    *q = 0;
	    pos = q - buf;
	    return;
	}
	*q++ = 0;

	/* examine the first character for correct indentation */
	if (strchr(IDCHRSET, *p)) {
	    l = strspn(p, IDCHRSET);
	} else if (*p == '{' || *p == '}' || *p == '*' || *p == '&' ||
	    *p == '(' || *p == ')' || *p == '#') {
	    l = 1;
	} else {
	    l = 0;
	}

	if (!l) {

	    /* no indentation at all */
	    xputs(p);
	    xputc('\n');
	    continue;
	}

	if (p[0] == '#') {

	    /* preprocessor directive: indent after # */
	    xputc('#');
	    findent1();
	    xputs(p + 1);
	    xputc('\n');
	    continue;
	}

	/* closing brace? then unindent */
	if (p[0] == '}')
	    xcurrindent--;

	/* print the indentation, but labels will be less indented */
	if (p[strlen(p) - 1] == ':') {
	    xcurrindent--;
	    findent();
	    xcurrindent++;
	} else {
	    findent();
	}

	/* output the line */
	xputs(p);
	xputc('\n');

	/* back at indentation level 0? then we can flush our buffers */
	/* first the variables then the other lines */
	if (!xcurrindent) {
	    yflush();
	    xflush();
	}

	/* undo indentation of non-braced if/else/switch/for/while/do stmt */
	if (xindentflag) {
	    xcurrindent--;
	    xindentflag = 0;
	}

	/* indent after opening brace */
	if (p[strlen(p) - 1] == '{') {
	    xcurrindent++;
	    xindentflag = 0;

	/* indent one line after if/else/switch/for/while/do stmt */
	} else if (l == 2 && !memcmp(p, "if", l) ||
	    l == 4 && !memcmp(p, "else", l) ||
	    l == 6 && !memcmp(p, "switch", l) ||
	    l == 3 && !memcmp(p, "for", l) ||
	    l == 5 && !memcmp(p, "while", l) ||
	    l == 2 && !memcmp(p, "do", l)) {
	    xcurrindent++;
	    xindentflag = 1;
	}
    }

    /* empty buffer after printing */
    pos = 0;
}

/* output function doing indentation automatically */
/*PRINTFLIKE1*/
void
output(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    outputv(format, args);
    va_end(args);
}

/* output function without indentation */
void
outputniv(const char *format, va_list args)
{
    static char buf[512];

    vsprintf(buf, format, args);
    xputs(buf);
}

/* output function without indentation */
/*PRINTFLIKE1*/
void
outputni(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    outputniv(format, args);
    va_end(args);
}

/* output an intx value definition */
void
outputintx(const char *name, intx_t *val)
{
    outputoctets(name, val->length, val->value);
    output("static ASN1intx_t %s = { %d, %s_octets };\n",
	name, val->length, name);
}

/* output an real value definition */
void
outputreal(const char *name, real_t *val)
{
    char buf[256];
    switch (val->type) {
    case eReal_Normal:
	sprintf(buf, "%s_mantissa", name);
	outputoctets(buf, val->mantissa.length, val->mantissa.value);
	sprintf(buf, "%s_exponent", name);
	outputoctets(buf, val->exponent.length, val->exponent.value);
	output("ASN1real_t %s = { eReal_Normal, { %u, %s_mantissa_octets }, %u, { %u, %s_exponent_octets } };\n",
	    name, val->mantissa.length, name,
	    val->base, val->exponent.length, name);
	break;
    case eReal_PlusInfinity:
	output("ASN1real_t %s = { eReal_PlusInfinity };\n", name);
	break;
    case eReal_MinusInfinity:
	output("ASN1real_t %s = { eReal_MinusInfinity };\n", name);
	break;
    }
}

/* output an octet array definition */
void
outputoctets(const char *name, uint32_t length, octet_t *val)
{
    uint32_t i;
    char buf[1024];
    char *p;

    p = buf;
    for (i = 0; i < length; i++) {
	sprintf(p, "0x%02x", val[i]);
	p += 4;
	if (i < length - 1) {
	    sprintf(p, ", ");
	    p += 2;
	}
    }
    *p = 0;
    output("static ASN1octet_t %s_octets[%u] = { %s };\n",
	name, length, buf);
}

/* output an uint32 array definition */
void
outputuint32s(const char *name, uint32_t length, uint32_t *val)
{
    uint32_t i;
    char buf[256];
    char *p;

    p = buf;
    for (i = 0; i < length; i++) {
	sprintf(p, "%u", val[i]);
	p += strlen(p);
	if (i < length - 1) {
	    sprintf(p, ", ");
	    p += 2;
	}
    }
    *p = 0;
    output("static ASN1uint32_t %s_elems[%u] = { %s };\n",
	name, length, buf);
}

/* output forward declaration for a value */
void
outputvalue0(AssignmentList_t ass, char *ideref, char *typeref, Value_t *value)
{
    Type_t *type;
    char buf[256];
    char *itype;
    int32_t noctets;
    uint32_t zero;
    uint32_t i;
    Value_t *values;
    Component_t *components;
    NamedValue_t *namedvalue;
    int32_t sign;
    char *pszStatic = "extern";

    value = GetValue(ass, value);
    type = GetType(ass, value->Type);
    value = GetValue(ass, value);
    switch (type->Type) {
    case eType_Integer:
	itype = GetIntegerType(ass, type, &sign);
	if (!strcmp(itype, "ASN1intx_t")) {
	    output("%s ASN1octet_t %s_octets[%u];\n", pszStatic,
		ideref, value->U.Integer.Value.length);
	}
	break;
    case eType_Real:
	itype = GetRealType(type);
	if (!strcmp(itype, "ASN1real_t"))
	{
	    switch (value->U.Real.Value.type) {
	    case eReal_Normal:
		output("%s ASN1octet_t %s_mantissa_octets[%u];\n", pszStatic,
		    ideref, value->U.Real.Value.mantissa.length);
		output("%s ASN1octet_t %s_exponent_octets[%u];\n", pszStatic,
		    ideref, value->U.Real.Value.exponent.length);
		break;
	    }
	}
	break;
    case eType_BitString:
	output("%s ASN1octet_t %s_octets[%u];\n", pszStatic,
	    ideref, (value->U.BitString.Value.length + 7) / 8);
	break;
    case eType_OctetString:
	output("%s ASN1octet_t %s_octets[%u];\n", pszStatic,
	    ideref, value->U.OctetString.Value.length);
	break;
    case eType_UTF8String:
	output("%s ASN1wchar_t %s_wchars[%u];\n", pszStatic,
	    ideref, value->U.UTF8String.Value.length);
	break;
    case eType_ObjectIdentifier:
        if (type->PrivateDirectives.fOidPacked ||
            type->PrivateDirectives.fOidArray || g_fOidArray)
        {
            // doing nothing
        }
        else
        {
            output("%s ASN1uint32_t %s_elems[%u];\n", pszStatic,
                ideref, value->U.ObjectIdentifier.Value.length);
        }
        break;
    case eType_BMPString:
    case eType_GeneralString:
    case eType_GraphicString:
    case eType_IA5String:
    case eType_ISO646String:
    case eType_NumericString:
    case eType_PrintableString:
    case eType_TeletexString:
    case eType_T61String:
    case eType_UniversalString:
    case eType_VideotexString:
    case eType_VisibleString:
    case eType_RestrictedString:
	itype = GetStringType(ass, type, &noctets, &zero);
	switch (noctets) {
	case 1:
	    output("%s ASN1char_t %s_chars[%u];\n", pszStatic,
		ideref, value->U.RestrictedString.Value.length + zero);
	    break;
	case 2:
	    output("%s ASN1char16_t %s_chars[%u];\n", pszStatic,
		ideref, value->U.RestrictedString.Value.length + zero);
	    break;
	case 4:
	    output("%s ASN1char32_t %s_chars[%u];\n", pszStatic,
		ideref, value->U.RestrictedString.Value.length + zero);
	    break;
	}
	break;
    case eType_ObjectDescriptor:
	output("%s ASN1char_t %s_chars[%u];\n", pszStatic,
	    ideref, value->U.ObjectDescriptor.Value.length + 1);
	break;
    case eType_SequenceOf:
    case eType_SetOf:
        if (type->Rules & (eTypeRules_LengthPointer | eTypeRules_FixedArray))
        {
            for (i = 0, values = value->U.SS.Values; values;
            i++, values = values->Next) {}
            if (value->U.SS.Values) {
            for (i = 0, values = value->U.SS.Values; values;
                i++, values = values->Next) {
                sprintf(buf, "%s_value%d", ideref, i);
                outputvalue0(ass, buf,
                GetTypeName(ass, type->U.SS.Type), values);
            }
            output("%s %s %s_values[%u];\n", pszStatic,
                GetTypeName(ass, type->U.SS.Type),
                ideref, i);
            }
        }
        else
        if (type->Rules & (eTypeRules_SinglyLinkedList | eTypeRules_DoublyLinkedList))
        {
            for (i = 0, values = value->U.SS.Values; values;
            i++, values = values->Next) {
            sprintf(buf, "%s_element%d", ideref, i);
            outputvalue0(ass, buf, GetTypeName(ass, type->U.SS.Type),
                values);
            output("%s %s_Element %s_value%d;\n", pszStatic,
                typeref, ideref, i);
            }
        }
        else
        {
            MyAbort();
        }
        break;
    case eType_Sequence:
    case eType_Set:
    case eType_External:
    case eType_EmbeddedPdv:
    case eType_CharacterString:
    case eType_InstanceOf:
	for (components = type->U.SSC.Components; components;
	    components = components->Next) {
	    switch (components->Type) {
	    case eComponent_Normal:
	    case eComponent_Optional:
	    case eComponent_Default:
		namedvalue = FindNamedValue(value->U.SSC.NamedValues,
		    components->U.NOD.NamedType->Identifier);
		if (!namedvalue)
		    break;
		sprintf(buf, "%s_%s", ideref,
		    Identifier2C(components->U.NOD.NamedType->Identifier));
		outputvalue0(ass, buf,
		    GetTypeName(ass, components->U.NOD.NamedType->Type),
		    namedvalue->Value);
		break;
	    }
	}
	break;
    }
}

/* output definitions of value components */
void
outputvalue1(AssignmentList_t ass, char *ideref, char *typeref, Value_t *value)
{
    static uint32_t nOidPackedCount = 0;
    Type_t *type;
    char buf[256];
    char *itype;
    int32_t noctets;
    uint32_t zero;
    uint32_t i;
    Value_t *values;
    Component_t *components;
    NamedValue_t *namedvalue;
    int32_t sign;

    value = GetValue(ass, value);
    type = GetType(ass, value->Type);
    value = GetValue(ass, value);
    switch (type->Type) {
    case eType_Integer:
	itype = GetIntegerType(ass, type, &sign);
	if (!strcmp(itype, "ASN1intx_t")) {
	    outputoctets(ideref, value->U.Integer.Value.length,
		value->U.Integer.Value.value);
	}
	break;
    case eType_Real:
	itype = GetRealType(type);
	if (!strcmp(itype, "ASN1real_t")) {
	    switch (value->U.Real.Value.type) {
	    case eReal_Normal:
		sprintf(buf, "%s_mantissa", ideref);
		outputoctets(buf, value->U.Real.Value.mantissa.length,
		    value->U.Real.Value.mantissa.value);
		sprintf(buf, "%s_exponent", ideref);
		outputoctets(buf, value->U.Real.Value.exponent.length,
		    value->U.Real.Value.exponent.value);
		break;
	    }
	}
	break;
    case eType_BitString:
	outputoctets(ideref, (value->U.BitString.Value.length + 7) / 8,
	    value->U.BitString.Value.value);
	break;
    case eType_OctetString:
	outputoctets(ideref, value->U.OctetString.Value.length,
	    value->U.OctetString.Value.value);
	break;
    case eType_UTF8String:
        itype = GetStringType(ass, type, &noctets, &zero);
        output("static ASN1wchar_t %s_wchars[%u] = { ",
        ideref, value->U.UTF8String.Value.length + zero);
        for (i = 0; i < value->U.UTF8String.Value.length; i++) {
            output("0x%x", value->U.UTF8String.Value.value[i]);
            if (i < value->U.UTF8String.Value.length - 1)
                output(", ");
        }
        if (zero) {
            if (value->U.UTF8String.Value.length)
                output(", 0x0");
            else
                output("0x0");
        }
        output(" };\n");
        break;
    case eType_ObjectIdentifier:
        if (type->PrivateDirectives.fOidPacked)
        {
            uint32_t length = value->U.ObjectIdentifier.Value.length;
            uint32_t *val = value->U.ObjectIdentifier.Value.value;
            uint32_t i, j, cb;
            uint32_t count = 0;
            uint32_t node;
            unsigned char aLittleEndian[16];
            char buf[1024];
            char *p = buf;
            sprintf(p, "{");
            p += strlen(p);
            for (i = 0; i < length; i++)
            {
                // get node value
                node = val[i];

                // special case for the first node
                if (0 == i && length > 1)
                {
                    i++;
                    node = node * 40 + val[1];
                }

                // encode this node
                ZeroMemory(aLittleEndian, sizeof(aLittleEndian));
                for (j = 0; node != 0; j++)
                {
                    aLittleEndian[j] = (unsigned char) (node & 0x7f);
                    if (j != 0)
                    {
                        aLittleEndian[j] |= (unsigned char) 0x80;
                    }
                    node >>= 7;
                }
                cb = j ? j : 1; // at least one byte for zero value

                // print out the values
                for (j = 0; j < cb; j ++)
                {
                    count++;
                    sprintf(p, " %u,", (unsigned char) aLittleEndian[cb - j - 1]);
                    p += strlen(p);
                }
            }
            --p; // remove the last ','
            strcpy(p, " }");
            output("static ASN1octet_t s_oid%u[] = %s;\n", nOidPackedCount, buf);
            output("ASN1encodedOID_t %s = { %u, s_oid%u };\n", ideref, count, nOidPackedCount);
            nOidPackedCount++;
        }
        else
        if (type->PrivateDirectives.fOidArray || g_fOidArray)
        {
            uint32_t length = value->U.ObjectIdentifier.Value.length;
            uint32_t *val = value->U.ObjectIdentifier.Value.value;
            uint32_t i;
            char buf[1024];
            char *p = buf;
            sprintf(p, "{ ");
            p += strlen(p);
            for (i = 0; i < length; i++)
            {
                if (i == length - 1)
                {
                    sprintf(p, "%u }", val[i]);
                }
                else
                {
                    sprintf(p, "%u, ", val[i]);
                }
                p += strlen(p);
            }
            *p = 0;
            output("ASN1objectidentifier2_t %s = {\n%u, %s\n};\n", ideref, length, buf);
        }
        else
        {
            uint32_t length = value->U.ObjectIdentifier.Value.length;
            uint32_t *val = value->U.ObjectIdentifier.Value.value;
            uint32_t i;
            char buf[1024];
            char *p = buf;
            for (i = 0; i < length; i++)
            {
                if (i == length - 1)
                {
                    sprintf(p, "{ NULL, %u }", val[i]);
                }
                else
                {
                    sprintf(p, "{ (ASN1objectidentifier_t) &(%s_list[%u]), %u },\n", ideref, i+1, val[i]);
                }
                p += strlen(p);
            }
            *p = 0;
            output("static const struct ASN1objectidentifier_s %s_list[%u] = {\n%s\n};\n",
            ideref, length, buf);
        }
        break;
    case eType_BMPString:
    case eType_GeneralString:
    case eType_GraphicString:
    case eType_IA5String:
    case eType_ISO646String:
    case eType_NumericString:
    case eType_PrintableString:
    case eType_TeletexString:
    case eType_T61String:
    case eType_UniversalString:
    case eType_VideotexString:
    case eType_VisibleString:
    case eType_RestrictedString:
	itype = GetStringType(ass, type, &noctets, &zero);
	switch (noctets) {
	case 1:
	    output("static ASN1char_t %s_chars[%u] = { ",
		ideref, value->U.RestrictedString.Value.length + zero);
	    break;
	case 2:
	    output("static ASN1char16_t %s_chars[%u] = { ",
		ideref, value->U.RestrictedString.Value.length + zero);
	    break;
	case 4:
	    output("static ASN1char32_t %s_chars[%u] = { ",
		ideref, value->U.RestrictedString.Value.length + zero);
	    break;
	}
	for (i = 0; i < value->U.RestrictedString.Value.length; i++) {
	    output("0x%x", value->U.RestrictedString.Value.value[i]);
	    if (i < value->U.RestrictedString.Value.length - 1)
		output(", ");
	}
	if (zero) {
	    if (value->U.RestrictedString.Value.length)
		output(", 0x0");
	    else
		output("0x0");
	}
	output(" };\n");
	break;
    case eType_ObjectDescriptor:
	output("static ASN1char_t %s_chars[%u] = { ",
	    ideref, value->U.ObjectDescriptor.Value.length + 1);
	for (i = 0; i < value->U.ObjectDescriptor.Value.length; i++) {
	    output("0x%x, ", value->U.ObjectDescriptor.Value.value[i]);
	}
	output("0x0 };\n");
	break;
    case eType_SequenceOf:
    case eType_SetOf:
	if (type->Rules & 
	    (eTypeRules_LengthPointer | eTypeRules_FixedArray)) {
	    if (value->U.SS.Values) {
		for (i = 0, values = value->U.SS.Values; values;
		    i++, values = values->Next) {
		    sprintf(buf, "%s_value%d", ideref, i);
		    outputvalue1(ass, buf,
			GetTypeName(ass, type->U.SS.Type),
			values);
		}
		output("static %s %s_values[%u] = { ",
		    GetTypeName(ass, type->U.SS.Type),
		    ideref, i);
		for (i = 0, values = value->U.SS.Values; values;
		    i++, values = values->Next) {
		    if (i)
			output(", ");
		    sprintf(buf, "%s_value%d", ideref, i);
		    outputvalue2(ass, buf, values);
		}
		output(" };\n");
	    }
	} else if (type->Rules &
	    (eTypeRules_SinglyLinkedList | eTypeRules_DoublyLinkedList)) {
	    for (i = 0, values = value->U.SS.Values; values;
		i++, values = values->Next) {
		sprintf(buf, "%s_element%d", ideref, i);
		outputvalue1(ass, buf, GetTypeName(ass, type->U.SS.Type),
		    values);
	    }
	    for (i = 0, values = value->U.SS.Values; values;
		i++, values = values->Next) {
		output("static %s_Element %s_value%d = { ",
		    typeref, ideref, i);
		if (values->Next)
		    output("&%s_value%d, ", ideref, i + 1);
		else
		    output("0, ");
		if (type->Rules & eTypeRules_DoublyLinkedList) {
		    if (i)
			output("&%s_value%d, ", ideref, i - 1);
		    else
			output("0, ");
		}
		sprintf(buf, "%s_element%d", ideref, i);
		outputvalue2(ass, buf, values);
		output(" };\n");
	    }
	} else {
	    MyAbort();
	}
	break;
    case eType_Sequence:
    case eType_Set:
    case eType_External:
    case eType_EmbeddedPdv:
    case eType_CharacterString:
    case eType_InstanceOf:
	for (components = type->U.SSC.Components; components;
	    components = components->Next) {
	    switch (components->Type) {
	    case eComponent_Normal:
	    case eComponent_Optional:
	    case eComponent_Default:
		namedvalue = FindNamedValue(value->U.SSC.NamedValues,
		    components->U.NOD.NamedType->Identifier);
		if (!namedvalue)
		    break;
		sprintf(buf, "%s_%s", ideref,
		    Identifier2C(components->U.NOD.NamedType->Identifier));
		outputvalue1(ass, buf,
		    GetTypeName(ass, components->U.NOD.NamedType->Type),
		    namedvalue->Value);
		break;
	    }
	}
	break;
    case eType_Choice:
	namedvalue = value->U.Choice.NamedValues;
	components = FindComponent(ass, type->U.Choice.Components,
	    namedvalue->Identifier);
	sprintf(buf, "%s_%s", ideref,
	    Identifier2C(components->U.NOD.NamedType->Identifier));
	outputvalue1(ass, buf, GetTypeName(ass,
	    components->U.NOD.NamedType->Type),
	    namedvalue->Value);
	output("static %s %s = ",
	    GetTypeName(ass, components->U.NOD.NamedType->Type), buf);
	outputvalue2(ass, buf, namedvalue->Value);
	output(";\n");
	break;
    }
}

/* output definition of value */
void
outputvalue2(AssignmentList_t ass, char *ideref, Value_t *value)
{
    Type_t *type;
    char buf[256];
    char *itype;
    int32_t noctets;
    uint32_t zero;
    uint32_t i;
    Value_t *values;
    Component_t *components;
    NamedValue_t *namedvalue;
    char *comma;
    uint32_t ext;
    uint32_t opt;
    int32_t sign;

    value = GetValue(ass, value);
    type = GetType(ass, value->Type);
    value = GetValue(ass, value);
    switch (type->Type) {
    case eType_Boolean:
	output("%d", value->U.Boolean.Value);
	break;
    case eType_Integer:
	itype = GetIntegerType(ass, type, &sign);
	if (!strcmp(itype, "ASN1intx_t")) {
	    output("{ %d, %s_octets }", value->U.Integer.Value.length, ideref);
	} else if (sign > 0) {
	    output("%u", intx2uint32(&value->U.Integer.Value));
	} else {
	    output("%d", intx2int32(&value->U.Integer.Value));
	}
	break;
    case eType_Enumerated:
	output("%u", value->U.Enumerated.Value);
	break;
    case eType_Real:
	itype = GetRealType(type);
	if (!strcmp(itype, "ASN1real_t")) {
	    switch (value->U.Real.Value.type) {
	    case eReal_Normal:
		output("{ eReal_Normal, { %u, %s_mantissa_octets }, %u, { %u, %s_exponent_octets } }",
		    value->U.Real.Value.mantissa.length, ideref,
		    value->U.Real.Value.base,
		    value->U.Real.Value.exponent.length, ideref);
		break;
	    case eReal_PlusInfinity:
		output("{ eReal_PlusInfinity }");
		break;
	    case eReal_MinusInfinity:
		output("{ eReal_MinusInfinity }");
		break;
	    }
	}
	else
	{
	    switch (value->U.Real.Value.type) {
	    case eReal_Normal:
		output("%g", real2double(&value->U.Real.Value));
		break;
	    case eReal_PlusInfinity:
	    case eReal_MinusInfinity:
		output("0.0");
		break;
	    }
	}
	break;
    case eType_BitString:
	output("{ %u, %s_octets }",
	    value->U.BitString.Value.length, ideref);
	break;
    case eType_OctetString:
	output("{ %u, %s_octets }",
	    value->U.OctetString.Value.length, ideref);
	break;
    case eType_UTF8String:
        output("{ %u, %s_utf8chars }",
            value->U.UTF8String.Value.length, ideref);
        break;
    case eType_ObjectIdentifier:
        if (type->PrivateDirectives.fOidPacked)
        {
            // doing nothing
        }
        else
        if (type->PrivateDirectives.fOidArray || g_fOidArray)
        {
            output("(ASN1objectidentifier2_t *) &%s_list", ideref);
        }
        else
        {
            output("(ASN1objectidentifier_t) %s_list", ideref);
        }
        break;
    case eType_BMPString:
    case eType_GeneralString:
    case eType_GraphicString:
    case eType_IA5String:
    case eType_ISO646String:
    case eType_NumericString:
    case eType_PrintableString:
    case eType_TeletexString:
    case eType_T61String:
    case eType_UniversalString:
    case eType_VideotexString:
    case eType_VisibleString:
    case eType_RestrictedString:
	itype = GetStringType(ass, type, &noctets, &zero);
	if (zero) {
	    output("%s_chars", ideref);
	} else {
	    output("{ %u, %s_chars }",
		value->U.RestrictedString.Value.length, ideref);
	}
	break;
    case eType_ObjectDescriptor:
	output("%s_chars", ideref);
	break;
    case eType_GeneralizedTime:
	output("{ %d, %d, %d, %d, %d, %d, %d, %d, %d }",
	    value->U.GeneralizedTime.Value.year,
	    value->U.GeneralizedTime.Value.month,
	    value->U.GeneralizedTime.Value.day,
	    value->U.GeneralizedTime.Value.hour,
	    value->U.GeneralizedTime.Value.minute,
	    value->U.GeneralizedTime.Value.second,
	    value->U.GeneralizedTime.Value.millisecond,
	    value->U.GeneralizedTime.Value.universal,
	    value->U.GeneralizedTime.Value.diff);
	break;
    case eType_UTCTime:
	output("{ %d, %d, %d, %d, %d, %d, %d, %d }",
	    value->U.UTCTime.Value.year,
	    value->U.UTCTime.Value.month,
	    value->U.UTCTime.Value.day,
	    value->U.UTCTime.Value.hour,
	    value->U.UTCTime.Value.minute,
	    value->U.UTCTime.Value.second,
	    value->U.UTCTime.Value.universal,
	    value->U.UTCTime.Value.diff);
	break;
    case eType_SequenceOf:
    case eType_SetOf:
	if (type->Rules &
	    (eTypeRules_LengthPointer | eTypeRules_FixedArray)) {
	    if (value->U.SS.Values) {
		for (i = 0, values = value->U.SS.Values; values;
		    i++, values = values->Next) {}
		output("{ %d, %s_values }", i, ideref);
	    } else {
		output("{ 0, NULL }");
	    }
	} else if (type->Rules &
	    (eTypeRules_SinglyLinkedList | eTypeRules_DoublyLinkedList)) {
	    output("&%s_value0", ideref);
	} else {
	    MyAbort();
	}
	break;
    case eType_Sequence:
    case eType_Set:
    case eType_External:
    case eType_EmbeddedPdv:
    case eType_CharacterString:
    case eType_InstanceOf:
	comma = "";
	output("{ ");
	if (type->U.SSC.Optionals || type->U.SSC.Extensions) {
	    output("{ ");
	    ext = 0;
	    opt = 0;
	    i = 0;
	    comma = "";
	    for (components = type->U.SSC.Components; components;
		components = components->Next) {
		switch (components->Type) {
		case eComponent_Normal:
		    if (!ext)
			break;
		    /*FALLTHROUGH*/
		case eComponent_Optional:
		case eComponent_Default:
		    namedvalue = FindNamedValue(value->U.SSC.NamedValues,
			components->U.NOD.NamedType->Identifier);
		    if (namedvalue)
			opt |= (0x80 >> i);
		    if (++i > 7) {
			output("%s0x%02x", comma, opt);
			opt = 0;
			i = 0;
			comma = ", ";
		    }
		    break;
		case eComponent_ExtensionMarker:
		    if (i) {
			output("%s0x%02x", comma, opt);
			opt = 0;
			i = 0;
			comma = ", ";
		    }
		    ext = 1;
		    break;
		}
	    }
	    if (i)
		output("%s0x%02x", comma, opt);
	    output(" }");
	    comma = ", ";
	}
	for (components = type->U.SSC.Components; components;
	    components = components->Next) {
	    switch (components->Type) {
	    case eComponent_Normal:
	    case eComponent_Optional:
	    case eComponent_Default:
		namedvalue = FindNamedValue(value->U.SSC.NamedValues,
		    components->U.NOD.NamedType->Identifier);
		if (!namedvalue) {
		    output("%s0", comma);
		} else {
		    output("%s", comma);
		    sprintf(buf, "%s_%s", ideref,
			Identifier2C(components->U.NOD.NamedType->Identifier));
		    outputvalue2(ass, buf, namedvalue->Value);
		}
		comma = ", ";
		break;
	    }
	}
	output(" }");
	break;
    case eType_Choice:
	i = ASN1_CHOICE_BASE;
	for (components = type->U.SSC.Components; components;
	    components = components->Next) {
	    switch (components->Type) {
	    case eComponent_Normal:
		if (!strcmp(value->U.SSC.NamedValues->Identifier,
		    components->U.NOD.NamedType->Identifier))
		    break;
		i++;
		continue;
	    case eComponent_ExtensionMarker:
		continue;
	    default:
		MyAbort();
	    }
	    break;
	}
	output("{ %d }", i);
    }
}

/* output assignments needed in initialization function */
void
outputvalue3(AssignmentList_t ass, char *ideref, char *valref, Value_t *value)
{
    Type_t *type;
    NamedValue_t *named;
    Value_t *values;
    int i;
    char idebuf[256];
    char valbuf[256];
    char *itype;

    value = GetValue(ass, value);
    type = GetType(ass, value->Type);
    switch (type->Type) {
    case eType_SequenceOf:
    case eType_SetOf:
	for (values = value->U.SS.Values, i = 0; values;
	    values = values->Next, i++) {
	    if (type->Rules &
	        (eTypeRules_LengthPointer | eTypeRules_FixedArray)) {
		sprintf(idebuf, "%s.value[%d]", ideref, i);
	    } else if (type->Rules &
		(eTypeRules_SinglyLinkedList | eTypeRules_DoublyLinkedList)) {
		sprintf(idebuf, "%s_value%d", ideref, i);
	    } else {
		MyAbort();
	    }
	    sprintf(valbuf, "%s_value%d", valref, i);
	    outputvalue3(ass, idebuf, valbuf, values);
	}
	break;
    case eType_Choice:
	output("%s.u.%s = %s_%s;\n",
	    ideref, Identifier2C(value->U.SSC.NamedValues->Identifier),
	    valref, Identifier2C(value->U.SSC.NamedValues->Identifier));
	/*FALLTHROUGH*/
    case eType_Sequence:
    case eType_Set:
    case eType_External:
    case eType_EmbeddedPdv:
    case eType_CharacterString:
    case eType_InstanceOf:
	for (named = value->U.SSC.NamedValues; named; named = named->Next) {
	    sprintf(idebuf, "%s.%s", ideref,
		Identifier2C(named->Identifier));
	    sprintf(valbuf, "%s_%s", valref,
		Identifier2C(named->Identifier));
	    outputvalue3(ass, idebuf, valbuf, named->Value);
	}
	break;
    case eType_Real:
	itype = GetRealType(type);
	if (strcmp(itype, "ASN1real_t"))
	{
	    switch (value->U.Real.Value.type) {
	    case eReal_Normal:
		break;
	    case eReal_PlusInfinity:
		output("%s = ASN1double_pinf();\n", ideref);
		break;
	    case eReal_MinusInfinity:
		output("%s = ASN1double_minf();\n", ideref);
		break;
	    }
	}
    }
}

/* print indentation up to current indentation level for variables */
static void
findentvar()
{
    int indent;

    indent = ycurrindent * INDENT;
    while (indent >= TABSIZE) {
	yputc('\t');
	indent -= TABSIZE;
    }
    while (indent--)
	yputc(' ');
}

/* print indentation up to current indentation level for variables */
void
outputvarv(const char *format, va_list args)
{
    static char buf[512];
    static int pos = 0;
    char *p, *q;
    int l;
    
    /* get the string to write */
    vsprintf(buf + pos, format, args);

    /* print it line by line */
    for (p = buf; *p; p = q) {
	q = strchr(p, '\n');
	if (!q) {
	    for (q = buf; *p;)
		*q++ = *p++;
	    *q = 0;
	    pos = q - buf;
	    return;
	}
	*q++ = 0;

	/* output every variable only once */
	if (ycurrindent == 1) {
	    l = 0;
	    while (l < ybuflen) {
		if (!memcmp(ybuf + l + INDENT / TABSIZE + INDENT % TABSIZE,
		    p, strlen(p)))
		    break;
		l += (strchr(ybuf + l, '\n') - (ybuf + l)) + 1;
	    }
	    if (l < ybuflen)
		continue;
	}

	/* examine the first character for correct indentation */
	if (strchr(IDCHRSET, *p)) {
	    l = strspn(p, IDCHRSET);
	} else if (*p == '{' || *p == '}') {
	    l = 1;
	} else {
	    l = 0;
	}

	if (!l) {

	    /* no indentation at all */
	    yputs(p);
	    yputc('\n');
	    continue;
	}

	/* closing brace? then unindent */
	if (p[0] == '}')
	    ycurrindent--;

	/* print indentation */
	findentvar();

	/* output the line */
	yputs(p);
	yputc('\n');

	/* indent after opening brace */
	if (p[strlen(p) - 1] == '{') {
	    ycurrindent++;
	}
    }
    pos = 0;
}

/* print indentation up to current indentation level for variables */
/*PRINTFLIKE1*/
void
outputvar(const char *format, ...)
{
    va_list args;

    va_start(args, format);
    outputvarv(format, args);
    va_end(args);
}

/* output an octet array definition for variables */
void
outputvaroctets(const char *name, uint32_t length, octet_t *val)
{
    uint32_t i;
    char buf[256];
    char *p;

    p = buf;
    for (i = 0; i < length; i++) {
	sprintf(p, "0x%02x", val[i]);
	p += 4;
	if (i < length - 1) {
	    sprintf(p, ", ");
	    p += 2;
	}
    }
    outputvar("static ASN1octet_t %s_octets[%u] = { %s };\n",
	name, length, buf);
}

/* output an intx value definition for variables */
void outputvarintx(const char *name, intx_t *val)
{
    outputvaroctets(name, val->length, val->value);
    outputvar("static ASN1intx_t %s = { %d, %s_octets };\n",
	name, val->length, name);
}

/* output an real value definition for variables */
void outputvarreal(const char *name, real_t *val)
{
    char buf[256];
    switch (val->type) {
    case eReal_Normal:
	sprintf(buf, "%s_mantissa", name);
	outputvaroctets(buf, val->mantissa.length, val->mantissa.value);
	sprintf(buf, "%s_exponent", name);
	outputvaroctets(buf, val->exponent.length, val->exponent.value);
	outputvar("ASN1real_t %s = { eReal_Normal, { %u, %s_mantissa_octets }, %u, { %u, %s_exponent_octets } };\n",
	    name, val->mantissa.length, name,
	    val->base, val->exponent.length, name);
	break;
    case eReal_PlusInfinity:
	outputvar("ASN1real_t %s = { eReal_PlusInfinity };\n", name);
	break;
    case eReal_MinusInfinity:
	outputvar("ASN1real_t %s = { eReal_MinusInfinity };\n", name);
	break;
    }
}

/* output a character of the function body */
void xputc(char c)
{
    if (xbuflen + 1 > xbufsize) {
    	xbufsize += 1024;
	if (!xbuf)
	    xbuf = (char *)malloc(xbufsize);
	else
	    xbuf = (char *)realloc(xbuf, xbufsize);
    }
    xbuf[xbuflen++] = c;
}

/* output a string of the function body */
void xputs(char *s)
{
    int sl;

    sl = strlen(s);
    if (xbuflen + sl > xbufsize) {
	while (xbuflen + sl > xbufsize)
	    xbufsize += 1024;
	if (!xbuf)
	    xbuf = (char *)malloc(xbufsize);
	else
	    xbuf = (char *)realloc(xbuf, xbufsize);
    }
    memcpy(xbuf + xbuflen, s, sl);
    xbuflen += sl;
}

/* flush the function body into the output file */
void xflush()
{
    if (xbuflen) {
	fwrite(xbuf, xbuflen, 1, fout);
#if 0
	fflush(fout);
#endif
	xbuflen = 0;
    }
}

/* output a character of the function variables */
void yputc(char c)
{
    if (ybuflen + 1 > ybufsize) {
    	ybufsize += 1024;
	if (!ybuf)
	    ybuf = (char *)malloc(ybufsize);
	else
	    ybuf = (char *)realloc(ybuf, ybufsize);
    }
    ybuf[ybuflen++] = c;
}

/* output a string of the function variables */
void yputs(char *s)
{
    int sl;

    sl = strlen(s);
    if (ybuflen + sl > ybufsize) {
	while (ybuflen + sl > ybufsize)
	    ybufsize += 1024;
	if (!ybuf)
	    ybuf = (char *)malloc(ybufsize);
	else
	    ybuf = (char *)realloc(ybuf, ybufsize);
    }
    memcpy(ybuf + ybuflen, s, sl);
    ybuflen += sl;
}

/* flush the function variables into the output file */
void yflush()
{
    if (ybuflen) {
	fwrite(ybuf, ybuflen, 1, fout);
#if 0
	fflush(fout);
#endif
	ybuflen = 0;
    }
}