/*
 * dump-scli.c --
 *
 *      Operations to generate MIB module stubs for the scli package.
 *
 * Copyright (c) 2001 J. Schoenwaelder, Technical University of Braunschweig.
 * Copyright (c) 2002 J. Schoenwaelder, University of Osnabrueck.
 * Copyright (c) 2004 J. Schoenwaelder, International University Bremen.
 * Copyright (c) 2009 J. Schoenwaelder, Jacobs University Bremen.
 *
 * See the file "COPYING" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * @(#) $Id: dump-scli.c 1772 2012-04-01 12:15:23Z schoenw $
 */

/*
 * TODO:
 *	  - range checks for 64 bit numbers
 */

#include <config.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_WIN_H
#include "win.h"
#endif

#include "smi.h"
#include "smidump.h"


#include <sys/types.h>

#ifdef HAVE_REGEX_H
#include <regex.h>
#else
#define regex_t int
#define regcomp(a, b, c)	1
#define regexec(a, b, c, d, e)	0
#define regerror(a,b,c,d)	strncpy(c, "regex not supported", d)
#define regfree(a)
#endif

static char *prefix = NULL;
static char *include = NULL;
static char *exclude = NULL;
static int  sflag = 0;
static int  cflag = 0;
static int  dflag = 0;
static regex_t _incl_regex, *incl_regex = NULL;
static regex_t _excl_regex, *excl_regex = NULL;

static char *keywords_c99[] = {
    "auto",        "enum",        "restrict",    "unsigned",
    "break",       "extern",      "return",      "void",
    "case",        "float",       "short",       "volatile",
    "char",        "for",         "signed",      "while",
    "const",       "goto",        "sizeof",      "_Bool",
    "continue",    "if",          "static",      "_Complex",
    "default",     "inline",      "struct",      "_Imaginary",
    "do",          "int",         "switch",
    "double",      "long",        "typedef",
    "else",        "register",    "union",
    NULL
};



static int
isKeyword(char *m)
{
    int i;
    
    for (i = 0; keywords_c99[i]; i++) {
	if (strcmp(m, keywords_c99[i]) == 0) {
	    return 1;
	}
    }
    return 0;
}



static char *
getStringTime(time_t t)
{
    static char   s[27];
    struct tm	  *tm;

    tm = gmtime(&t);
    sprintf(s, "%04d-%02d-%02d %02d:%02d",
	    tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
	    tm->tm_hour, tm->tm_min);
    return s;
}



static void
fprintCommentString(FILE *f, char *s)
{
    int i, len;

    if (s) {
	fprintf(f, " *   ");
	len = strlen(s);
	for (i = 0; i < len; i++) {
	    fputc(s[i], f);
	    if (s[i] == '\n') {
		fprintf(f, " *   ");
	    }
	}
	fputc('\n', f);
    }
}



static void
fprintTopComment(FILE *f, SmiModule *smiModule)
{
    SmiRevision *smiRevision;
    char *date;

    fprintf(f,
	    "/*	\t\t\t\t\t\t-- DO NOT EDIT --\n"
	    " * Generated by smidump version " SMI_VERSION_STRING ":\n");

    fprintf(f, " *   smidump -f scli");
    if (sflag) {
	fprintf(f, " --scli-set");
    }
    if (cflag) {
	fprintf(f, " --scli-create");
    }
    if (dflag) {
	fprintf(f, " --scli-delete");
    }
    if (prefix) {
	fprintf(f, " \\\n *     --scli-prefix='%s'", prefix);
    }
    if (include) {
	fprintf(f, " \\\n *     --scli-include='%s'", include);
    }
    if (exclude) {
	fprintf(f, " \\\n *     --scli-exclude='%s'", exclude);
    }
    fprintf(f, " %s\n *\n", smiModule->name);

    fprintf(f,
	    " * Derived from %s:\n", smiModule->name);
    fprintCommentString(f, smiModule->description);

    for (smiRevision = smiGetFirstRevision(smiModule);
	 smiRevision;
	 smiRevision = smiGetNextRevision(smiRevision)) {
	date = getStringTime(smiRevision->date);
	fprintf(f,
		" *\n"
		" * Revision %s:\n", date);
	fprintCommentString(f, smiRevision->description);
    }

#if 0
    if (smiModule->organization || smiModule->contactinfo) {
	fprintf(f, " *\n * Contact:\n");
	printCommentString(f, smiModule->organization);
	fprintf(f, " *\n");
	printCommentString(f, smiModule->contactinfo);
    }
#endif

    fprintf(f,
	    " *\n * $I" "d$\n"
	    " */\n"
	    "\n");
}



static char*
translate(char *m)
{
    char *s;
    int i;

    s = xstrdup(m);
    for (i = 0; s[i]; i++) {
	if (s[i] == '-') s[i] = '_';
    }
  
    while (isKeyword(s)) {
	s = xrealloc(s, strlen(s) + 2);
	strcat(s, "_");
    }
    
    return s;
}



static char*
translateUpper(char *m)
{
    char *s;
    int i;

    s = xstrdup(m);
    for (i = 0; s[i]; i++) {
	if (s[i] == '-') s[i] = '_';
	if (islower((int) s[i])) {
	    s[i] = toupper(s[i]);
	}
    }
  
    while (isKeyword(s)) {
	s = xrealloc(s, strlen(s) + 2);
	strcat(s, "_");
    }
    
    return s;
}



static char*
translateLower(char *m)
{
    char *s;
    int i;

    s = xstrdup(m);
    for (i = 0; s[i]; i++) {
	if (s[i] == '-') s[i] = '_';
	if (isupper((int) s[i])) {
	    s[i] = tolower(s[i]);
	}
    }
  
    while (isKeyword(s)) {
	s = xrealloc(s, strlen(s) + 2);
	strcat(s, "_");
    }
    
    return s;
}



static char*
translateFileName(char *m)
{
    char *s;
    int i;

    s = xstrdup(m);
    for (i = 0; s[i]; i++) {
	if (s[i] == '_') s[i] = '-';
	if (isupper((int) s[i])) {
	    s[i] = tolower(s[i]);
	}
    }
  
    return s;
}



static FILE *
createFile(char *name, char *suffix)
{
    char *fullname;
    FILE *f;

    fullname = xmalloc(strlen(name) + (suffix ? strlen(suffix) : 0) + 2);
    strcpy(fullname, name);
    if (suffix) {
        strcat(fullname, suffix);
    }
    if (!access(fullname, R_OK)) {
        fprintf(stderr, "smidump: %s already exists\n", fullname);
        xfree(fullname);
        return NULL;
    }
    f = fopen(fullname, "w");
    if (!f) {
        fprintf(stderr, "smidump: cannot open %s for writing: ", fullname);
        perror(NULL);
        xfree(fullname);
        exit(1);
    }
    xfree(fullname);
    return f;
}



static int
isGroup(SmiNode *smiNode, SmiNodekind memberkind)
{
    SmiNode *childNode;
    int status;

    if (incl_regex) {
	if (! smiNode->name) {
	    return 0;
	}
	status = regexec(incl_regex, smiNode->name, (size_t) 0, NULL, 0);
	if (status != 0) {
	    return 0;
	}
    }

    if (excl_regex) {
	if (! smiNode->name) {
	    return 0;
	}
	status = regexec(excl_regex, smiNode->name, (size_t) 0, NULL, 0);
	if (status == 0) {
	    return 0;
	}
    }
    
    for (childNode = smiGetFirstChildNode(smiNode);
	 childNode;
	 childNode = smiGetNextChildNode(childNode)) {
	if (childNode->nodekind & memberkind) {
	    return 1;
	}
    }

    return 0;
}



static int
isAccessible(SmiNode *groupNode)
{
    SmiNode *smiNode;
    int num = 0;
    
    for (smiNode = smiGetFirstChildNode(groupNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if ((smiNode->nodekind == SMI_NODEKIND_SCALAR
	     || smiNode->nodekind == SMI_NODEKIND_COLUMN)
	    && (smiNode->access == SMI_ACCESS_READ_ONLY
		|| smiNode->access == SMI_ACCESS_READ_WRITE)) {
	    num++;
	}
    }

    return num;
}



static int
isIndex(SmiNode *groupNode, SmiNode *smiNode)
{
    SmiElement *smiElement;
    
    /*
     * Perhaps this test needs to be more sophisticated if you have
     * really creative cross-table indexing constructions...
     */

    for (smiElement = smiGetFirstElement(groupNode);
	 smiElement; smiElement = smiGetNextElement(smiElement)) {
	if (smiNode == smiGetElementNode(smiElement)) {
	    return 1;
	}
    }

    return 0;
}



static int
isWritable(SmiNode *treeNode, SmiNodekind nodekind)
{
    SmiNode *smiNode;
    
    for (smiNode = smiGetFirstChildNode(treeNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if (smiNode->nodekind & (nodekind)
	    && (smiNode->access >= SMI_ACCESS_READ_WRITE)) {
	    break;
	}
    }

    return (smiNode != NULL);
}



static char*
getSnmpType(SmiType *smiType)
{
    struct {
	char *module;
	char *name;
	char *tag;
    } typemap[] = {
	{ "RFC1155-SMI","Counter",	"GNET_SNMP_VARBIND_TYPE_COUNTER32" },
	{ "SNMPv2-SMI",	"Counter32",	"GNET_SNMP_VARBIND_TYPE_COUNTER32" },
	{ "RFC1155-SMI","TimeTicks",	"GNET_SNMP_VARBIND_TYPE_TIMETICKS" },
	{ "SNMPv2-SMI",	"TimeTicks",	"GNET_SNMP_VARBIND_TYPE_TIMETICKS" },
	{ "RFC1155-SMI","Opaque",	"GNET_SNMP_VARBIND_TYPE_OPAQUE" },
	{ "SNMPv2-SMI",	"Opaque",	"GNET_SNMP_VARBIND_TYPE_OPAQUE" },
	{ "RFC1155-SMI","IpAddress",	"GNET_SNMP_VARBIND_TYPE_IPADDRESS" },
	{ "SNMPv2-SMI",	"IpAddress",	"GNET_SNMP_VARBIND_TYPE_IPADDRESS" },
	{ NULL, NULL, NULL }
    };

    SmiBasetype basetype = smiType->basetype;
    
    do {
	int i;
	for (i = 0; typemap[i].name; i++) {
	    if (smiType->name
		&& (strcmp(smiType->name, typemap[i].name) == 0)) {
		return typemap[i].tag;
	    }
	}
    } while ((smiType = smiGetParentType(smiType)));

    switch (basetype) {
    case SMI_BASETYPE_INTEGER32:
    case SMI_BASETYPE_ENUM:
	return "GNET_SNMP_VARBIND_TYPE_INTEGER32";
    case SMI_BASETYPE_UNSIGNED32:
	return "GNET_SNMP_VARBIND_TYPE_UNSIGNED32";
    case SMI_BASETYPE_INTEGER64:
	return NULL;
    case SMI_BASETYPE_UNSIGNED64:
	return "GNET_SNMP_VARBIND_TYPE_COUNTER64";
    case SMI_BASETYPE_OCTETSTRING:
	return "GNET_SNMP_VARBIND_TYPE_OCTETSTRING";
    case SMI_BASETYPE_BITS:
	return "GNET_SNMP_VARBIND_TYPE_OCTETSTRING";
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	return "GNET_SNMP_VARBIND_TYPE_OBJECTID";
    case SMI_BASETYPE_FLOAT32:
    case SMI_BASETYPE_FLOAT64:
    case SMI_BASETYPE_FLOAT128:
	return NULL;
    case SMI_BASETYPE_UNKNOWN:
	return NULL;
    case SMI_BASETYPE_POINTER:
	return NULL;
    }
    return NULL;
}



typedef void	(*ForEachIndexFunc)	(FILE *f, SmiNode *groupNode, SmiNode *smiNode, int flags, int maxlen, char *name);


/*
 * Check whether we have duplicate nodes in the INDEX.  If yes,
 * generate a unique name.
 */

static char*
getIndexName(SmiNode *indexNode, SmiNode *iNode, SmiElement *smiElement)
{
    SmiElement *se;
    SmiNode *sn;
    int n = 0, m = 0, tail = 0;
    char *name;
    
    for (se = smiGetFirstElement(indexNode);
	 se; se = smiGetNextElement(se)) {
	sn = smiGetElementNode(se);
	if (strcmp(sn->name, iNode->name) == 0) {
	    n++;
	    if (! tail) m++;
	}
	if (se == smiElement) tail = 1;
    }
    if (n > 1) {
	smiAsprintf(&name, "%s%d", iNode->name, m);
    } else {
	name = xstrdup(iNode->name);
    }
    return name;
}


static void
foreachIndexDo(FILE *f, SmiNode *smiNode, ForEachIndexFunc func,
	       int flags, int maxlen)
{
    SmiNode *indexNode = NULL, *iNode;
    SmiElement *smiElement;
    
    switch (smiNode->indexkind) {
    case SMI_INDEX_INDEX:
    case SMI_INDEX_REORDER:
	indexNode = smiNode;
	break;
    case SMI_INDEX_EXPAND:	/* TODO: we have to do more work here! */
	break;
    case SMI_INDEX_AUGMENT:
    case SMI_INDEX_SPARSE:
	indexNode = smiGetRelatedNode(smiNode);
	break;
    case SMI_INDEX_UNKNOWN:
	break;
    }
    if (indexNode) {
	for (smiElement = smiGetFirstElement(indexNode);
	     smiElement; smiElement = smiGetNextElement(smiElement)) {
	    iNode = smiGetElementNode(smiElement);
	    if (iNode) {
		char *name = getIndexName(indexNode, iNode, smiElement);
		(func) (f, smiNode, iNode, flags, maxlen, name);
		if (name) xfree(name);
	    }
	}
    }
}



static void
printIndexParamsFunc(FILE *f, SmiNode *smiNode, SmiNode *iNode,
		     int flags, int maxlen, char *name)
{
    SmiType *iType;
    char *cName;
    unsigned minSize, maxSize;

    iType = smiGetNodeType(iNode);
    if (! iType) {
	return;
    }

    cName = translate(name ? name : iNode->name);
    switch (iType->basetype) {
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	maxSize = smiGetMaxSize(iType);
	minSize = smiGetMinSize(iType);
	fprintf(f, ", %s%s", flags ? "guint32 *" : "", cName);
	if (minSize != maxSize) {
	    fprintf(f, ", %s_%sLength",
		    flags ? "guint16 " : "", cName);
	}
	break;
    case SMI_BASETYPE_OCTETSTRING:
    case SMI_BASETYPE_BITS:
	maxSize = smiGetMaxSize(iType);
	minSize = smiGetMinSize(iType);
	fprintf(f, ", %s%s", flags ? "guchar *" : "", cName);
	if (minSize != maxSize) {
	    fprintf(f, ", %s_%sLength",
		    flags ? "guint16 " : "", cName);
	}
	break;
    case SMI_BASETYPE_ENUM:
    case SMI_BASETYPE_INTEGER32:
	fprintf(f, ", %s%s", flags ? "gint32 " : "", cName);
	break;
    case SMI_BASETYPE_UNSIGNED32:
	fprintf(f, ", %s%s", flags ? "guint32 " : "", cName);
	break;
    default:
	fprintf(f, "/* ?? %s */", cName);
	break;
    }
    xfree(cName);
}



static void
printIndexParamsPassFunc(FILE *f, SmiNode *smiNode, SmiNode *iNode,
			 int flags, int maxlen, char *name)
{
    SmiType *iType;
    char *cName, *gName;
    unsigned minSize, maxSize;

    iType = smiGetNodeType(iNode);
    if (! iType) {
	return;
    }

    gName = translate(smiNode->name);
    cName = translate(iNode->name);
    fprintf(f, ", %s->%s", gName, cName);
    switch (iType->basetype) {
    case SMI_BASETYPE_BITS:
    case SMI_BASETYPE_OCTETSTRING:
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	maxSize = smiGetMaxSize(iType);
	minSize = smiGetMinSize(iType);
	if (minSize != maxSize) {
	    fprintf(f, ", %s->_%sLength", gName, cName);
	}
	break;
    default:
	break;
    }
    xfree(cName);
    xfree(gName);
}



static void
printIndexAssignmentFunc(FILE *f, SmiNode *smiNode, SmiNode *iNode,
			 int flags, int maxlen, char *name)
{
    SmiType *iType;
    char *cName, *gName, *dName, *dModuleName;
    unsigned minSize, maxSize;

    iType = smiGetNodeType(iNode);
    if (! iType) {
	return;
    }

    gName = translate(smiNode->name);
    cName = translate(iNode->name);
    dName = translateUpper(iNode->name);
    dModuleName = translateUpper(smiGetNodeModule(iNode)->name);
    switch (iType->basetype) {
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	fprintf(f, "    memcpy(%s->%s, %s, _%sLength * sizeof(guint32));\n",
	    gName, cName, cName, cName);
	break;
    case SMI_BASETYPE_OCTETSTRING:
    case SMI_BASETYPE_BITS:
	maxSize = smiGetMaxSize(iType);
	minSize = smiGetMinSize(iType);
	if (minSize != maxSize) {
	    fprintf(f, "    memcpy(%s->%s, %s, _%sLength);\n",
		    gName, cName, cName, cName);
	} else {
	    fprintf(f, "    memcpy(%s->%s, %s, %s_%sLENGTH);\n",
		    gName, cName, cName, dModuleName, dName);
	}
	break;
    case SMI_BASETYPE_ENUM:
    case SMI_BASETYPE_INTEGER32:
    case SMI_BASETYPE_UNSIGNED32:
	fprintf(f, "    %s->%s = %s;\n",
		gName, cName, cName);
	break;
    default:
	fprintf(f, "    /* ?? %s */\n", cName);
	break;
    }
    xfree(dModuleName);
    xfree(dName);
    xfree(cName);
    xfree(gName);
}



static void
printHeaderEnumeration(FILE *f, SmiModule *smiModule,
		       SmiNode * smiNode, SmiType *smiType)
{
    SmiNamedNumber *nn;
    char *cName, *cPrefix;
    char *dName, *dModuleName;
    char *name;
    int len;

    if (smiType && smiType->name) {
	name = smiType->name;
    } else if (smiNode && smiNode->name) {
	name = smiNode->name;
    } else {
	return;
    }

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    dModuleName = translateUpper(smiModule->name);
    cName = translate(name);
    dName = translateUpper(name);
    
    for (len = 0, nn = smiGetFirstNamedNumber(smiType); nn;
	 nn = smiGetNextNamedNumber(nn)) {
	if (len < strlen(nn->name)) {
	    len = strlen(nn->name);
	}
    }
    for (nn = smiGetFirstNamedNumber(smiType); nn;
		 nn = smiGetNextNamedNumber(nn)) {
	char *dEnum = translateUpper(nn->name);
	fprintf(f, "#define %s_%s_%-*s %d\n",
		dModuleName, dName, len, dEnum,
		(int) nn->value.value.integer32);
	xfree(dEnum);
    }
    fprintf(f, "\nextern GNetSnmpEnum const %s_enums_%s[];\n\n",
	    cPrefix, cName);
    
    xfree(dName);
    xfree(cName);
    xfree(dModuleName);
    xfree(cPrefix);
}



static void
printHeaderEnumerations(FILE *f, SmiModule *smiModule)
{
    SmiNode  *smiNode, *parentNode;
    SmiType  *smiType;
    int      cnt = 0;
    const unsigned int groupkind = SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN;

    const char *header =
	"/*\n"
	" * Tables to map enumerations to strings and vice versa.\n"
	" */\n"
	"\n";

    for (smiNode = smiGetFirstNode(smiModule, groupkind);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, groupkind)) {
	parentNode = smiGetParentNode(smiNode);
	if (! parentNode || ! isGroup(parentNode, groupkind)) {
	    continue;
	}
	smiType = smiGetNodeType(smiNode);
	if (smiType && !smiType->name
	    && smiType->basetype == SMI_BASETYPE_ENUM
	    && smiGetTypeModule(smiType) == smiModule) {
	    if (! cnt) {
		fputs(header, f);
	    }
	    cnt++;
	    printHeaderEnumeration(f, smiModule, smiNode, smiType);
	}
    }

    for (smiType = smiGetFirstType(smiModule);
	 smiType;
	 smiType = smiGetNextType(smiType)) {
	if (smiType->basetype == SMI_BASETYPE_ENUM
	    && smiGetTypeModule(smiType) == smiModule) {
	    if (! cnt) {
		fputs(header, f);
	    }
	    cnt++;
	    printHeaderEnumeration(f, smiModule, NULL, smiType);
	}
    }
    
    if (cnt) {
	fprintf(f, "\n");
    }
}



static void
printHeaderIdentities(FILE *f, SmiModule *smiModule)
{
    SmiNode      *smiNode, *moduleIdentityNode, *parentNode;
    int          cnt = 0;
    unsigned int i;
    char         *dName, *dModuleName;
    char         *cModuleName;

    moduleIdentityNode = smiGetModuleIdentityNode(smiModule);
    
    dModuleName = translateUpper(smiModule->name);

    for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_NODE);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_NODE)) {
	parentNode = smiGetParentNode(smiNode);
	if (! parentNode || ! isGroup(parentNode, SMI_NODEKIND_NODE)) {
	    continue;
	}
	if (smiNode->status == SMI_STATUS_UNKNOWN) {
	    continue;
	}
	if (smiNode == moduleIdentityNode) {
	    continue;
	}
	if (! cnt) {
	    fprintf(f,
		    "/*\n"
		    " * Tables to map identities to strings and vice versa.\n"
		    " */\n"
		    "\n");
	}
	cnt++;
	dName = translateUpper(smiNode->name);
	fprintf(f, "#define %s_%s\t", dModuleName, dName);
	for (i = 0; i < smiNode->oidlen; i++) {
	    fprintf(f, "%s%u", i ? "," : "", smiNode->oid[i]);
	}
	fprintf(f, "\n");
	xfree(dName);
    }
    
    if (cnt) {
	cModuleName = translateLower(smiModule->name);
	fprintf(f,
		"\n"
		"extern GNetSnmpIdentity const %s_identities[];\n"
		"\n",
		cModuleName);
	xfree(cModuleName);
    }

    xfree(dModuleName);
}



static void
printHeaderNotifications(FILE *f, SmiModule *smiModule)
{
    SmiNode      *smiNode;
    int          cnt = 0;
    unsigned int i;
    char         *dName, *dModuleName;
    char         *cModuleName;

    dModuleName = translateUpper(smiModule->name);

    for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_NOTIFICATION);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_NOTIFICATION)) {
	if (smiNode->status == SMI_STATUS_UNKNOWN) {
	    continue;
	}
	if (! cnt) {
	    fprintf(f,
		    "/*\n"
		    " * Tables to map notifications to strings and vice versa.\n"
		    " */\n"
		    "\n");
	}
	cnt++;
	dName = translateUpper(smiNode->name);
	fprintf(f, "#define %s_%s\t", dModuleName, dName);
	for (i = 0; i < smiNode->oidlen; i++) {
	    fprintf(f, "%s%u", i ? "," : "", smiNode->oid[i]);
	}
	fprintf(f, "\n");
	xfree(dName);
    }
    
    if (cnt) {
	cModuleName = translateLower(smiModule->name);
	fprintf(f,
		"\n"
		"extern GNetSnmpIdentity const %s_notifications[];\n"
		"\n",
		cModuleName);
	xfree(cModuleName);
    }

    xfree(dModuleName);
}



static void
printParam(FILE *f, SmiNode *smiNode)
{
    char *cName;
    unsigned minSize, maxSize;
    SmiType *smiType;

    smiType = smiGetNodeType(smiNode);
    if (! smiType) {
	return;
    }

    cName = translate(smiNode->name);
    switch (smiType->basetype) {
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	maxSize = smiGetMaxSize(smiType);
	minSize = smiGetMinSize(smiType);
	fprintf(f,
		", guint32 *%s", cName);
	if (maxSize != minSize) {
	    fprintf(f,
		    ", guint16 _%sLength", cName);
	}
	break;
    case SMI_BASETYPE_OCTETSTRING:
    case SMI_BASETYPE_BITS:
	maxSize = smiGetMaxSize(smiType);
	minSize = smiGetMinSize(smiType);
	fprintf(f,
		", guchar *%s", cName);
	if (maxSize != minSize) {
	    fprintf(f,
		    ", guint16 _%sLength", cName);
	}
	break;
    case SMI_BASETYPE_ENUM:
    case SMI_BASETYPE_INTEGER32:
	fprintf(f,
		", gint32 %s", cName);
	break;
    case SMI_BASETYPE_UNSIGNED32:
	fprintf(f,
		", guint32 %s", cName);
	break;
    case SMI_BASETYPE_INTEGER64:
	fprintf(f,
		", gint64 %s", cName);
	break;
    case SMI_BASETYPE_UNSIGNED64:
	fprintf(f,
		", guint64 %s", cName);
	break;
    default:
	fprintf(f,
		" /* ?? */ _%s", cName);
	break;
    }
    xfree(cName);
}



static void
printCreateMethodPrototype(FILE *f, SmiNode *groupNode)
{
    SmiModule *smiModule;
    char *cPrefix, *cNodeName;

    smiModule = smiGetNodeModule(groupNode);
    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cNodeName = translate(groupNode->name);
    
    fprintf(f,
	    "extern void\n"
	    "%s_create_%s(GNetSnmp *s", cPrefix, cNodeName);
    foreachIndexDo(f, groupNode, printIndexParamsFunc, 1, 0);
    fprintf(f, ", GError **error);\n\n");

    xfree(cNodeName);
    xfree(cPrefix);
}



static void
printDeleteMethodPrototype(FILE *f, SmiNode *groupNode)
{
    SmiModule *smiModule;
    char *cPrefix, *cNodeName;

    smiModule = smiGetNodeModule(groupNode);
    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cNodeName = translate(groupNode->name);
    
    fprintf(f,
	    "extern void\n"
	    "%s_delete_%s(GNetSnmp *s", cPrefix, cNodeName);
    
    foreachIndexDo(f, groupNode, printIndexParamsFunc, 1, 0);
    
    fprintf(f, ", GError **error);\n\n");

    xfree(cNodeName);
    xfree(cPrefix);
}



static void
printSetMethodPrototype(FILE *f, SmiNode *groupNode, SmiNode *smiNode)
{
    SmiModule *smiModule;
    char *cPrefix, *cNodeName;

    smiModule = smiGetNodeModule(smiNode);
    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cNodeName = translate(smiNode->name);
    
    fprintf(f,
	    "extern void\n"
	    "%s_set_%s(GNetSnmp *s",
	    cPrefix, cNodeName);

    foreachIndexDo(f, groupNode, printIndexParamsFunc, 1, 0);
    printParam(f, smiNode);
    
    fprintf(f, ", GError **error);\n\n");

    xfree(cNodeName);
    xfree(cPrefix);
}



static void
printMethodPrototypes(FILE *f, SmiNode *groupNode)
{
    SmiNode *smiNode;
    SmiType *smiType;

    for (smiNode = smiGetFirstChildNode(groupNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if (smiNode->nodekind & (SMI_NODEKIND_COLUMN | SMI_NODEKIND_SCALAR)
	    && (smiNode->access >= SMI_ACCESS_READ_ONLY)) {
	    if (smiNode->access == SMI_ACCESS_READ_WRITE) {
		smiType = smiGetNodeType(smiNode);
		if (smiType && smiType->name
		    && strcmp(smiType->name, "RowStatus") == 0) {
		    if (cflag) printCreateMethodPrototype(f, groupNode);
		    if (dflag) printDeleteMethodPrototype(f, groupNode);
		} else {
		    if (! isIndex(groupNode, smiNode)) {
			if (sflag) printSetMethodPrototype(f, groupNode, smiNode);
		    }
		}
	    }
	}	    
    }
}



static void
printHeaderTypedefMemberComment(FILE *f, SmiNode *smiNode, SmiType *smiType)
{
    char *s = NULL;

    switch (smiNode->access) {
    case SMI_ACCESS_READ_WRITE:
	s = "rw";
	break;
    case SMI_ACCESS_READ_ONLY:
	s = "ro";
	break;
    case SMI_ACCESS_NOT_ACCESSIBLE:
	s = "na";
	break;
    case SMI_ACCESS_NOTIFY:
	s = "no";
	break;
    default:
	break;
    }
    if (s) fprintf(f, "%s", s);
    s = smiRenderType(smiType, SMI_RENDER_NAME | SMI_RENDER_QUALIFIED);
    if (s) {
	fprintf(f, " %s", s);
	free(s);
    }
    if (smiNode->units) {
	fprintf(f, " [%s]", smiNode->units);
    } else if (smiType->units) {
	fprintf(f, " [%s]", smiNode->units);
    }
}



static void
printHeaderTypedefMember(FILE *f, SmiNode *smiNode,
			 SmiType *smiType, int isIndex, int maxlen, char *name)
{
    char *cName, *dNodeName, *dModuleName;
    unsigned minSize, maxSize;
    SmiModule *smiModule;

    smiModule = smiGetNodeModule(smiNode);

    cName = translate(name ? name : smiNode->name);
    dNodeName = translateUpper(name ? name : smiNode->name);
    dModuleName = translateUpper(smiModule ? smiModule->name : "");
    switch (smiType->basetype) {
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	maxSize = smiGetMaxSize(smiType);
	minSize = smiGetMinSize(smiType);
	if (isIndex && maxSize > 128 - smiNode->oidlen) {
	    maxSize = 128 - smiNode->oidlen;
	}
	if (isIndex) {
	    fprintf(f, "    guint32  %s[%u];", cName, maxSize);
	    fprintf(f, "%*s/* ", (int) (maxlen-strlen(cName)+2), "");
	    printHeaderTypedefMemberComment(f, smiNode, smiType);
	    fprintf(f, " */\n");
	} else {
	    fprintf(f, "    guint32  *%s;", cName);
	    fprintf(f, "%*s/* ", (int) (maxlen-strlen(cName)+5), "");
	    printHeaderTypedefMemberComment(f, smiNode, smiType);
	    fprintf(f, " */\n");
	}
	if (maxSize == minSize) {
	    fprintf(f,
		    "#define %s_%sLENGTH %u\n",
		    dModuleName, dNodeName, maxSize);
	} else {
	    fprintf(f,
		    "#define %s_%sMINLENGTH %u\n",
		    dModuleName, dNodeName, minSize);
	    fprintf(f,
		    "#define %s_%sMAXLENGTH %u\n",
		    dModuleName, dNodeName, maxSize);
	    fprintf(f,
		    "    guint16  _%sLength;\n", cName);
	}
	break;
    case SMI_BASETYPE_OCTETSTRING:
    case SMI_BASETYPE_BITS:
	maxSize = smiGetMaxSize(smiType);
	minSize = smiGetMinSize(smiType);
	if (isIndex && maxSize > 128 - smiNode->oidlen) {
	    maxSize = 128 - smiNode->oidlen;
	}
	if (isIndex) {
	    fprintf(f, "    guchar   %s[%u];", cName, maxSize);
	    fprintf(f, "%*s/* ", (int) (maxlen-strlen(cName)+2), "");
	    printHeaderTypedefMemberComment(f, smiNode, smiType);
	    fprintf(f, " */\n");
	} else {
	    fprintf(f, "    guchar   *%s;", cName);
	    fprintf(f, "%*s/* ", (int) (maxlen-strlen(cName)+5), "");
	    printHeaderTypedefMemberComment(f, smiNode, smiType);
	    fprintf(f, " */\n");
	}
	if (maxSize == minSize) {
	    fprintf(f,
		    "#define %s_%sLENGTH %u\n",
		    dModuleName, dNodeName, maxSize);
	} else {
	    fprintf(f,
		    "#define %s_%sMINLENGTH %u\n",
		    dModuleName, dNodeName, minSize);
	    fprintf(f,
		    "#define %s_%sMAXLENGTH %u\n",
		    dModuleName, dNodeName, maxSize);
	    fprintf(f,
		    "    guint16  _%sLength;\n", cName);
	}
	break;
    case SMI_BASETYPE_ENUM:
    case SMI_BASETYPE_INTEGER32:
	fprintf(f, "    gint32   %s%s;", isIndex ? "" : "*", cName);
	fprintf(f, "%*s/* ", (int) (maxlen-strlen(cName)+5+isIndex), "");
	printHeaderTypedefMemberComment(f, smiNode, smiType);
	fprintf(f, " */\n");
	break;
    case SMI_BASETYPE_UNSIGNED32:
	fprintf(f, "    guint32  %s%s;", isIndex ? "" : "*", cName);
	fprintf(f, "%*s/* ", (int) (maxlen-strlen(cName)+5+isIndex), "");
	printHeaderTypedefMemberComment(f, smiNode, smiType);
	fprintf(f, " */\n");
	break;
    case SMI_BASETYPE_INTEGER64:
	fprintf(f, "    gint64   *%s;", cName);
	fprintf(f, "%*s/* ", (int) (maxlen-strlen(cName)+5), "");
	printHeaderTypedefMemberComment(f, smiNode, smiType);
	fprintf(f, " */\n");
	break;
    case SMI_BASETYPE_UNSIGNED64:
	fprintf(f, "    guint64  *%s;", cName);
	fprintf(f, "%*s/* ", (int) (maxlen-strlen(cName)+5), "");
	printHeaderTypedefMemberComment(f, smiNode, smiType);
	fprintf(f, " */\n");
	break;
    default:
	fprintf(f,
		"    /* ?? */  _%s; \n", cName);
	break;
    }
    xfree(dModuleName);
    xfree(dNodeName);
    xfree(cName);
}



static void
printHeaderTypedefMemberIndex(FILE *f, SmiNode *smiNode, SmiNode *iNode,
			      int flags, int maxlen, char *name)
{
    SmiType *iType;

    iType = smiGetNodeType(iNode);
    if (! iType) {
	return;
    }
    
    printHeaderTypedefMember(f, iNode, iType, 1, maxlen, name);
}



static void
printHeaderTypedef(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    SmiNode *smiNode;
    SmiType *smiType;
    char    *cPrefix, *dModuleName, *cGroupName, *dGroupName, *dNodeName;
    int     writable = 0, count = 0, len = 0;

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    dModuleName = translateUpper(smiModule->name);
    cGroupName = translate(groupNode->name);
    dGroupName = translateUpper(groupNode->name);

    fprintf(f,
	    "/*\n"
	    " * C type definitions for %s::%s.\n"
	    " */\n\n",
	    smiModule->name, groupNode->name);
    
    for (smiNode = smiGetFirstChildNode(groupNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if (smiNode->nodekind & (SMI_NODEKIND_COLUMN | SMI_NODEKIND_SCALAR)
	    && (smiNode->access >= SMI_ACCESS_READ_ONLY)) {
	    if (len < strlen(smiNode->name)) {
		len = strlen(smiNode->name);
	    }
	}
    }
    
    for (smiNode = smiGetFirstChildNode(groupNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if (smiNode->nodekind & (SMI_NODEKIND_COLUMN | SMI_NODEKIND_SCALAR)
	    && (smiNode->access >= SMI_ACCESS_READ_ONLY)) {
	    dNodeName = translateUpper(smiNode->name);
	    fprintf(f, "#define %s_%-*s (1 << %d) \n", dModuleName, len, dNodeName, count);
	    xfree(dNodeName);
	    count++;
	}	    
    }

    if (count) {
	fprintf(f, "\n");
    }

    fprintf(f, "typedef struct {\n");

    foreachIndexDo(f, groupNode, printHeaderTypedefMemberIndex, 0, len);
	    
    for (smiNode = smiGetFirstChildNode(groupNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if (smiNode->nodekind & (SMI_NODEKIND_COLUMN | SMI_NODEKIND_SCALAR)
	    && (smiNode->access >= SMI_ACCESS_READ_ONLY)) {
	    if (isIndex(groupNode, smiNode)) {
		continue;
	    }
	    if (smiNode->access == SMI_ACCESS_READ_WRITE) {
		writable++;
	    }
	    smiType = smiGetNodeType(smiNode);
	    if (! smiType) {
		continue;
	    }
	    printHeaderTypedefMember(f, smiNode, smiType, 0, len, 0);
	}	    
    }

    fprintf(f, "} %s_%s_t;\n\n", cPrefix, cGroupName);

    if (groupNode->nodekind == SMI_NODEKIND_ROW) {
	char *cTableName;
	SmiNode *tableNode;

	tableNode = smiGetParentNode(groupNode);
	if (tableNode) {
	    cTableName = translate(tableNode->name);
	    fprintf(f, "extern void\n"
		    "%s_get_%s(GNetSnmp *s, %s_%s_t ***%s, gint64 mask, GError **error);\n\n",
		    cPrefix, cTableName,
		    cPrefix, cGroupName, cGroupName);
	    fprintf(f, "extern void\n"
		    "%s_free_%s(%s_%s_t **%s);\n\n",
		    cPrefix, cTableName,
		    cPrefix, cGroupName, cGroupName);
	    xfree(cTableName);
	}
    }
    fprintf(f, "extern %s_%s_t *\n"
	    "%s_new_%s(void);\n\n",
	    cPrefix, cGroupName, cPrefix, cGroupName);
    fprintf(f, "extern void\n"
	    "%s_get_%s(GNetSnmp *s, %s_%s_t **%s",
	    cPrefix, cGroupName,
	    cPrefix, cGroupName,
	    cGroupName);
    if (groupNode->nodekind == SMI_NODEKIND_ROW) {
	foreachIndexDo(f, groupNode, printIndexParamsFunc, 1, 0);
    }
    fprintf(f, ", gint64 mask, GError **error);\n\n");
    if (writable) {
	fprintf(f, "extern void\n"
		"%s_set_%s(GNetSnmp *s, %s_%s_t *%s, gint64 mask, GError **error);\n\n",
		cPrefix, cGroupName,
		cPrefix, cGroupName, cGroupName);
    }
    fprintf(f, "extern void\n"
	    "%s_free_%s(%s_%s_t *%s);\n\n",
	    cPrefix, cGroupName,
	    cPrefix, cGroupName, cGroupName);

    printMethodPrototypes(f, groupNode);
	    
    xfree(dGroupName);
    xfree(cGroupName);
    xfree(dModuleName);
    xfree(cPrefix);
}



static void
printHeaderTypedefs(FILE *f, SmiModule *smiModule)
{
    SmiNode   *smiNode;
    int       cnt = 0;
    const unsigned int groupkind = SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN;
    
    for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_ANY);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_ANY)) {
	if (isGroup(smiNode, groupkind) && isAccessible(smiNode)) {
	    cnt++;
	    printHeaderTypedef(f, smiModule, smiNode);
	}
    }
    
    if (cnt) {
	fprintf(f, "\n");
    }
}



static void
dumpHeader(SmiModule *smiModule, char *baseName)
{
    char *pModuleName;
    FILE *f;
    
    pModuleName = translateUpper(smiModule->name);

    f = createFile(baseName, ".h");
    if (! f) {
	return;
    }

    fprintTopComment(f, smiModule);
    
    fprintf(f,
	    "#ifndef _%s_H_\n"
	    "#define _%s_H_\n"
	    "\n"
	    "#include \"gsnmp.h\"\n"
	    "\n"
	    "G_BEGIN_DECLS\n"
	    "\n",
	    pModuleName, pModuleName);

    printHeaderEnumerations(f, smiModule);
    printHeaderIdentities(f, smiModule);
    printHeaderNotifications(f, smiModule);
    printHeaderTypedefs(f, smiModule);

    fprintf(f,
	    "G_END_DECLS\n"
	    "\n"
	    "#endif /* _%s_H_ */\n",
	    pModuleName);

    if (fflush(f) || ferror(f)) {
	perror("smidump: write error");
	exit(1);
    }

    fclose(f);
    xfree(pModuleName);
}



static void
printStubEnumeration(FILE *f, SmiModule *smiModule,
		     SmiNode *smiNode, SmiType *smiType)
{
    SmiNamedNumber *nn;
    char *cName, *cPrefix;
    char *dName, *dModuleName;
    char *name;
    int len;

    if (smiType && smiType->name) {
	name = smiType->name;
    } else if (smiNode && smiNode->name) {
	name = smiNode->name;
    } else {
	return;
    }
    
    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    dModuleName = translateUpper(smiModule->name);
    cName = translate(name);
    dName = translateUpper(name);
    
    fprintf(f, "GNetSnmpEnum const %s_enums_%s[] = {\n",
	    cPrefix, cName);
    for (len = 0, nn = smiGetFirstNamedNumber(smiType); nn;
	 nn = smiGetNextNamedNumber(nn)) {
	if (len < strlen(nn->name)) {
	    len = strlen(nn->name);
	}
    }
    for (nn = smiGetFirstNamedNumber(smiType); nn;
	 nn = smiGetNextNamedNumber(nn)) {
	char *dEnum = translateUpper(nn->name);
	fprintf(f, "    { %s_%s_%s,%*s \"%s\" },\n",
		dModuleName, dName, dEnum,
		(int) (len - strlen(dEnum)), "", nn->name);
	xfree(dEnum);
    }
    fprintf(f,
	    "    { 0, NULL }\n"
	    "};\n"
	    "\n");
    
    xfree(dName);
    xfree(cName);
    xfree(dModuleName);
    xfree(cPrefix);
}



static void
printStubEnumerations(FILE *f, SmiModule *smiModule)
{
    SmiNode   *smiNode, *parentNode;
    SmiType   *smiType;
    int       cnt = 0;
    const unsigned int groupkind = SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN;
    
    for (smiNode = smiGetFirstNode(smiModule, groupkind);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, groupkind)) {
	parentNode = smiGetParentNode(smiNode);
	if (! parentNode || ! isGroup(parentNode, groupkind)) {
	    continue;
	}
	smiType = smiGetNodeType(smiNode);
	if (smiType && !smiType->name
	    && smiType->basetype == SMI_BASETYPE_ENUM
	    && smiGetTypeModule(smiType) == smiModule) {
	    cnt++;
	    printStubEnumeration(f, smiModule, smiNode, smiType);
	}
    }
    
    for (smiType = smiGetFirstType(smiModule);
	 smiType;
	 smiType = smiGetNextType(smiType)) {
	if (smiType->basetype == SMI_BASETYPE_ENUM
	    && smiGetTypeModule(smiType) == smiModule) {
	    cnt++;
	    printStubEnumeration(f, smiModule, NULL, smiType);
	}
    }
    
    if (cnt) {
	fprintf(f, "\n");
    }
}



static void
printStubIdentities(FILE *f, SmiModule *smiModule)
{
    SmiNode   *smiNode, *moduleIdentityNode, *parentNode;
    char      *cName, *cModuleName;
    char      *dName, *dModuleName;
    int       cnt = 0;
    
    moduleIdentityNode = smiGetModuleIdentityNode(smiModule);
    
    cModuleName = translateLower(smiModule->name);
    dModuleName = translateUpper(smiModule->name);
    
    for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_NODE);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_NODE)) {
	parentNode = smiGetParentNode(smiNode);
	if (! parentNode || ! isGroup(parentNode, SMI_NODEKIND_NODE)) {
	    continue;
	}
	if (smiNode->status == SMI_STATUS_UNKNOWN) {
	    continue;
	}
	if (smiNode == moduleIdentityNode) {
	    continue;
	}
	cnt++;
	cName = translate(smiNode->name);
	dName = translateUpper(smiNode->name);
	fprintf(f,
		"static guint32 const %s[]\n\t= { %s_%s };\n",
		cName, dModuleName, dName);
	xfree(dName);
	xfree(cName);
    }

    if (cnt) {
	fprintf(f,
		"\n"
		"GNetSnmpIdentity const %s_identities[] = {\n",
		cModuleName);
    
	for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_NODE);
	     smiNode;
	     smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_NODE)) {
	    if (smiNode->status == SMI_STATUS_UNKNOWN) {
		continue;
	    }
	    if (smiNode == moduleIdentityNode) {
		continue;
	    }
	    cName = translate(smiNode->name);
	    fprintf(f, "    { %s,\n"
		    "      G_N_ELEMENTS(%s),\n"
		    "      \"%s\" },\n",
		    cName, cName, smiNode->name);
	    xfree(cName);
	}
	
	fprintf(f,
		"    { 0, 0, NULL }\n"
		"};\n"
		"\n"
		"\n");
    }

    xfree(dModuleName);
    xfree(cModuleName);
}



static void
printStubNotifications(FILE *f, SmiModule *smiModule)
{
    SmiNode   *smiNode;
    char      *cName, *cModuleName;
    char      *dName, *dModuleName;
    int       cnt = 0;
    
    cModuleName = translateLower(smiModule->name);
    dModuleName = translateUpper(smiModule->name);
    
    for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_NOTIFICATION);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_NOTIFICATION)) {
	if (smiNode->status == SMI_STATUS_UNKNOWN) {
	    continue;
	}
	cnt++;
	cName = translate(smiNode->name);
	dName = translateUpper(smiNode->name);
	fprintf(f,
		"static guint32 const %s[]\n\t= { %s_%s };\n",
		cName, dModuleName, dName);
	xfree(dName);
	xfree(cName);
    }

    if (cnt) {
	fprintf(f,
		"\n"
		"GNetSnmpIdentity const %s_notifications[] = {\n",
		cModuleName);
    
	for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_NOTIFICATION);
	     smiNode;
	     smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_NOTIFICATION)) {
	    if (smiNode->status == SMI_STATUS_UNKNOWN) {
		continue;
	    }
	    cName = translate(smiNode->name);
	    fprintf(f, "    { %s,\n"
		    "      G_N_ELEMENTS(%s),\n"
		    "      \"%s\" },\n",
		    cName, cName, smiNode->name);
	    xfree(cName);
	}
	
	fprintf(f,
		"    { 0, 0, NULL }\n"
		"};\n"
		"\n"
		"\n");
    }

    xfree(dModuleName);
    xfree(cModuleName);
}



static int
printSizeConstraints(FILE *f, SmiNode *smiNode, SmiType *smiType)
{
    SmiRange *smiRange;
    unsigned int minSize, maxSize;
    int cnt;
    char *cName;

    cName = translate(smiNode->name);

    for (cnt = 0; !cnt && smiType; smiType = smiGetParentType(smiType)) {
	for (smiRange = smiGetFirstRange(smiType);
	     smiRange ; smiRange = smiGetNextRange(smiRange)) {
	    minSize = smiRange->minValue.value.unsigned32;
	    maxSize = smiRange->maxValue.value.unsigned32;
	    if (minSize == 0 && maxSize >= 65535) continue;
	    if (f) {
		if (cnt) {
		    fprintf(f, ", %u, %u", minSize, maxSize);
		} else {
		    fprintf(f, "static guint16 %s_constraints[] = {%uU, %uU",
			    cName, minSize, maxSize);
		}
	    }
	    cnt++;
	}
    }

    xfree(cName);
    if (f && cnt) fprintf(f, ", 0, 0};\n");
    return (cnt > 0);
}



static int
printInteger32RangeConstraints(FILE *f, SmiNode *smiNode, SmiType *smiType)
{
    SmiRange *smiRange;
    long minSize, maxSize;
    int cnt;
    char *cName;

    cName = translate(smiNode->name);

    for (cnt = 0; !cnt && smiType; smiType = smiGetParentType(smiType)) {
	for (smiRange = smiGetFirstRange(smiType);
	     smiRange ; smiRange = smiGetNextRange(smiRange)) {
	    minSize = smiRange->minValue.value.integer32;
	    maxSize = smiRange->maxValue.value.integer32;
	    if (minSize < -2147483647 && maxSize > 2147483646) continue;
	    if (f) {
		if (cnt) {
		    fprintf(f, ", %ldL, %ldL", minSize, maxSize);
		} else {
		    fprintf(f, "static gint32 %s_constraints[] = {%ldL, %ldL",
			    cName, minSize, maxSize);
		}
	    }
	    cnt++;
	}
    }

    xfree(cName);
    if (f && cnt) fprintf(f, ", 0, 0};\n");
    return (cnt > 0);
}



static int
printUnsigned32RangeConstraints(FILE *f, SmiNode *smiNode, SmiType *smiType)
{
    SmiRange *smiRange;
    unsigned long minSize, maxSize;
    int cnt;
    char *cName;

    cName = translate(smiNode->name);

    for (cnt = 0; !cnt && smiType; smiType = smiGetParentType(smiType)) {
	for (smiRange = smiGetFirstRange(smiType);
	     smiRange ; smiRange = smiGetNextRange(smiRange)) {
	    minSize = smiRange->minValue.value.unsigned32;
	    maxSize = smiRange->maxValue.value.unsigned32;
	    if (minSize == 0 && maxSize >= 4294967295UL) continue;
	    if (f) {
		if (cnt) {
		    fprintf(f, ", %luUL, %luUL", minSize, maxSize);
		} else {
		    fprintf(f, "static guint32 %s_constraints[] = {%luUL, %luUL",
			cName, minSize, maxSize);
		}
	    }
	    cnt++;
	}
    }

    xfree(cName);
    if (f && cnt) fprintf(f, ", 0, 0};\n");
    return (cnt > 0);
}



static int
printConstraints(FILE *f, SmiNode *smiNode, SmiNode *groupNode, int flags)
{
    SmiType *smiType;
    int cnt;

    smiType = smiGetNodeType(smiNode);
    if (! smiType) {
	return 0;
    }

    /*
     * Generally suppress all INDEX objects (treat them as if they
     * were not-accessible). This is a cheap optimization for SMIv1
     * MIBs where these objects were generally read-only.
     */

    if (flags && isIndex(groupNode, smiNode)) {
	return 0;
    }

    if (smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE
	|| smiNode->access == SMI_ACCESS_NOTIFY) {
	return 0;
    }

    switch (smiType->basetype) {
    case SMI_BASETYPE_OCTETSTRING:
	cnt = printSizeConstraints(f, smiNode, smiType);
	break;
    case SMI_BASETYPE_INTEGER32:
	cnt = printInteger32RangeConstraints(f, smiNode, smiType);
	break;
    case SMI_BASETYPE_UNSIGNED32:
	cnt = printUnsigned32RangeConstraints(f, smiNode, smiType);
	break;
    default:
	cnt = 0;
	break;
    }

    return cnt;
}



static int
printScalarsAttributesContraints(FILE *f, SmiNode *groupNode)
{
    SmiNode *smiNode;
    int      n = 0;
    
    for (smiNode = smiGetFirstChildNode(groupNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	n += printConstraints(f, smiNode, groupNode, 0);
    }
    return n;
}



static int
printTableAttributesConstraints(FILE *f, SmiNode *rowNode)
{
    SmiNode *smiNode;
    int     idx, cnt, n = 0;
    
    for (smiNode = smiGetFirstChildNode(rowNode), idx = 0, cnt = 0;
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if (isIndex(rowNode, smiNode)) idx++;
	cnt++;
    }

    for (smiNode = smiGetFirstChildNode(rowNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	n += printConstraints(f, smiNode, rowNode, cnt > idx);
    }
    return n;
}



static void
printStubContraints(FILE *f, SmiModule *smiModule)
{
    SmiNode *smiNode;
    int cnt = 0;
    const unsigned int groupkind = SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN;
    
    for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_ANY);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_ANY)) {
	if (isGroup(smiNode, groupkind) && isAccessible(smiNode)) {
	    if (smiNode->nodekind == SMI_NODEKIND_ROW) {
		cnt += printTableAttributesConstraints(f, smiNode);
	    } else {
		cnt += printScalarsAttributesContraints(f, smiNode);
	    }
	}
    }
    
    if (cnt) {
	fprintf(f, "\n\n");
    }
}



static void
printAttribute(FILE *f, SmiNode *smiNode, SmiNode *groupNode, int flags)
{
    SmiType *smiType;
    char *snmpType;
    char *dModuleName, *dNodeName;
    char *cPrefix, *cGroupName, *cNodeName;
    char *writable_flag = NULL, *fixed_length_flag = NULL;
    unsigned maxSize = 0, minSize = 0;
    int cnt;

    smiType = smiGetNodeType(smiNode);
    if (!smiType) {
	return;
    }

    snmpType = getSnmpType(smiType);
    if (!snmpType) {
	return;
    }

    /*
     * Generally suppress all INDEX objects (treat them as if they
     * were not-accessible). This is a cheap optimization for SMIv1
     * MIBs where these objects were generally read-only.
     */

    if (flags && isIndex(groupNode, smiNode)) {
	return;
    }

    if (smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE
	|| smiNode->access == SMI_ACCESS_NOTIFY) {
	return;
    }

    dModuleName = translateUpper(smiGetNodeModule(smiNode)->name);
    dNodeName = translateUpper(smiNode->name);
    cNodeName = translate(smiNode->name);
    cGroupName = translate(groupNode->name);
    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiGetNodeModule(smiNode)->name);

    fprintf(f,
	    "    { %u, %s,\n"
	    "      %s_%s, \"%s\",\n",
	    smiNode->oid[smiNode->oidlen-1], snmpType,
	    dModuleName, dNodeName, smiNode->name);

    switch (smiType->basetype) {
    case SMI_BASETYPE_OCTETSTRING:
	cnt = printSizeConstraints(NULL, smiNode, smiType);
	break;
    case SMI_BASETYPE_INTEGER32:
	cnt = printInteger32RangeConstraints(NULL, smiNode, smiType);
	break;
    case SMI_BASETYPE_UNSIGNED32:
	cnt = printUnsigned32RangeConstraints(NULL, smiNode, smiType);
	break;
    default:
	cnt = 0;
	break;
    }
    if (cnt) {
	fprintf(f, "       %s_constraints,\n", cNodeName);
    } else {
	fprintf(f, "       NULL,\n");
    }

    if (! flags && isIndex(groupNode, smiNode)) {
	fprintf(f, "      -1,\n");
    } else {
	fprintf(f,
		"      G_STRUCT_OFFSET(%s_%s_t, %s),\n",
		cPrefix, cGroupName, cNodeName);
    }

    switch (smiType->basetype) {
    case SMI_BASETYPE_OCTETSTRING:
    case SMI_BASETYPE_BITS:
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	maxSize = smiGetMaxSize(smiType);
	minSize = smiGetMinSize(smiType);
	break;
    default:
	break;
    }
    if (minSize != maxSize) {
	fprintf(f,
		"      G_STRUCT_OFFSET(%s_%s_t, _%sLength)",
		cPrefix, cGroupName, cNodeName);
    } else {
	fprintf(f,
		"      %d", minSize);
    }

    if (smiNode->access > SMI_ACCESS_READ_ONLY) {
        writable_flag = "GSNMP_ATTR_FLAG_WRITABLE";
    }
    if (minSize == maxSize && minSize > 0) {
        fixed_length_flag = "GSNMP_ATTR_FLAG_FIXED_LENGTH";
    }

    fprintf(f,
	    ",\n"
	    "      ");
    if (! writable_flag && ! fixed_length_flag) {
	fprintf(f, "0");
    } else if (writable_flag && ! fixed_length_flag) {
	fprintf(f, "%s", writable_flag);
    } else if (!writable_flag && fixed_length_flag) {
	fprintf(f, "%s", fixed_length_flag);
    } else {
	fprintf(f, "%s|%s", writable_flag, fixed_length_flag);
    }
    fprintf(f, " },\n");

    xfree(cPrefix);
    xfree(cGroupName);
    xfree(cNodeName);
    xfree(dNodeName);
    xfree(dModuleName);
}



static void
printScalarsAttributes(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    SmiNode *smiNode;
    
    for (smiNode = smiGetFirstChildNode(groupNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	printAttribute(f, smiNode, groupNode, 0);
    }
}



static void
printTableAttributes(FILE *f, SmiModule *smiModule, SmiNode *rowNode)
{
    SmiNode *smiNode;
    int     idx, cnt;
    
    for (smiNode = smiGetFirstChildNode(rowNode), idx = 0, cnt = 0;
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if (isIndex(rowNode, smiNode)) idx++;
	cnt++;
    }

    for (smiNode = smiGetFirstChildNode(rowNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	printAttribute(f, smiNode, rowNode, cnt > idx);
    }
}



static void
printStubAttributes(FILE *f, SmiModule *smiModule)
{
    SmiNode      *smiNode;
    char         *cName;
    int          cnt = 0;
    unsigned int i;
    const unsigned int groupkind = SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN;
        
    for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_ANY);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_ANY)) {
	if (isGroup(smiNode, groupkind) && isAccessible(smiNode)) {
	    cnt++;
	    cName = translate(smiNode->name);

	    fprintf(f, "static guint32 const %s_oid[] = {", cName);
	    for (i = 0; i < smiNode->oidlen; i++) {
		fprintf(f, "%s%u", i ? ", " : "", smiNode->oid[i]);
	    }
	    fprintf(f, "};\n\n");
	    
	    fprintf(f,
		    "static GNetSnmpAttribute %s_attr[] = {\n",
		    cName);
	    if (smiNode->nodekind == SMI_NODEKIND_ROW) {
		printTableAttributes(f, smiModule, smiNode);
	    } else {
		printScalarsAttributes(f, smiModule, smiNode);
	    }
	    fprintf(f,
		    "    { 0, 0, 0, NULL }\n"
		    "};\n"
		    "\n");
	    xfree(cName);
	}
    }
    
    if (cnt) {
	fprintf(f, "\n");
    }
}



static void
printInteger32RangeChecks(FILE *f, char *cGroupName, char *cName,
			  SmiType *smiType)
{
    SmiRange *smiRange;
    long int minSize, maxSize;
    int c;

    for (c = 0; smiType; smiType = smiGetParentType(smiType)) {
	for (smiRange = smiGetFirstRange(smiType);
	     smiRange ; smiRange = smiGetNextRange(smiRange)) {
	    minSize = smiRange->minValue.value.integer32;
	    maxSize = smiRange->maxValue.value.integer32;
	    if (! c) {
		fprintf(f, "    if (");
	    } else {
		fprintf(f, "\n        && ");
	    }
	    if (minSize == -2147483647 - 1) {
		fprintf(f, "(%s->%s > %ld)", cGroupName, cName, maxSize);
	    } else if (maxSize == 2147483647) {
		fprintf(f, "(%s->%s < %ld)", cGroupName, cName, minSize);
	    } else if (minSize == maxSize) {
		fprintf(f, "(%s->%s != %ld)", cGroupName, cName, maxSize);
	    } else {
		fprintf(f, "(%s->%s < %ld || %s->%s > %ld)",
			cGroupName, cName, minSize,
			cGroupName, cName, maxSize);
	    }
	    c++;
	}
    }

    if (c) {
	fprintf(f, ") {\n"
		"         return -1;\n"
		"    }\n");
    }
}



static void
printUnsigned32RangeChecks(FILE *f, char *cGroupName, char *cName,
			   SmiType *smiType)
{
    SmiRange *smiRange;
    unsigned long minSize, maxSize;
    int c;

    for (c = 0; smiType; smiType = smiGetParentType(smiType)) {
	for (smiRange = smiGetFirstRange(smiType);
	     smiRange ; smiRange = smiGetNextRange(smiRange)) {
	    minSize = smiRange->minValue.value.unsigned32;
	    maxSize = smiRange->maxValue.value.unsigned32;
	    if (minSize == 0 && maxSize == 4294967295U) {
		continue;
	    }
	    if (! c) {
		fprintf(f, "     if (");
	    } else {
		fprintf(f, "\n         && ");
	    }
	    if (minSize == 0) {
		fprintf(f, "(%s->%s > %lu)", cGroupName, cName, maxSize);
	    } else if (maxSize == 4294967295U) {
		fprintf(f, "(%s->%s < %lu)", cGroupName, cName, minSize);
	    } else if (minSize == maxSize) {
		fprintf(f, "(%s->%s != %lu)", cGroupName, cName, maxSize);
	    } else {
		fprintf(f, "(%s->%s < %lu || %s->%s > %lu)",
			cGroupName, cName, minSize,
			cGroupName, cName, maxSize);
	    }
	    c++;
	}
    }
    if (c) {
	fprintf(f, ") {\n"
		"         return -1;\n"
		"    }\n");
    }
}



static void
printUnpackMethod(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    SmiElement *smiElement;
    SmiNode *indexNode = NULL;
    SmiNode *iNode;
    SmiType *iType;
    char    *cPrefix, *cGroupName, *cName, *name;
    unsigned maxSize, minSize;
    int last = 0;

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cGroupName = translate(groupNode->name);

    switch (groupNode->indexkind) {
    case SMI_INDEX_INDEX:
    case SMI_INDEX_REORDER:
	indexNode = groupNode;
	break;
    case SMI_INDEX_EXPAND:	/* TODO: we have to do more work here! */
	indexNode = NULL;
	break;
    case SMI_INDEX_AUGMENT:
    case SMI_INDEX_SPARSE:
	indexNode = smiGetRelatedNode(groupNode);
	break;
    case SMI_INDEX_UNKNOWN:
	indexNode = NULL;
	break;
    }

    /*
     * First check if there are OID or string types so that we
     * know whether we need some additional variables.
     */
    
    for (smiElement = smiGetFirstElement(indexNode);
	 smiElement; smiElement = smiGetNextElement(smiElement)) {
	iNode = smiGetElementNode(smiElement);
	if (iNode) {
	    iType = smiGetNodeType(iNode);
	    if (iType
		&& (iType->basetype == SMI_BASETYPE_OCTETSTRING
		    || iType->basetype == SMI_BASETYPE_OBJECTIDENTIFIER)) {
		break;
	    }
	}
    }

    fprintf(f,
	    "static inline int\n"
	    "unpack_%s(GNetSnmpVarBind *vb, %s_%s_t *%s)\n"
	    "{\n"
	    "    guint8 idx = %u;\n"
	    "%s"
	    "\n",
	    cGroupName, cPrefix, cGroupName, cGroupName,
	    groupNode->oidlen + 1,
	    smiElement ? "    guint16 i, len;\n" : "");

    for (smiElement = smiGetFirstElement(indexNode);
	 smiElement; smiElement = smiGetNextElement(smiElement)) {
	iNode = smiGetElementNode(smiElement);
	last = (smiGetNextElement(smiElement) == NULL);
	if (iNode) {
	    iType = smiGetNodeType(iNode);
	    if (! iType) {
		continue;
	    }
	    name = getIndexName(indexNode, iNode, smiElement);
	    cName = translate(name ? name : iNode->name);
	    switch (iType->basetype) {
	    case SMI_BASETYPE_ENUM:
	    case SMI_BASETYPE_INTEGER32:
		fprintf(f,
			"    if (vb->oid_len < idx) return -1;\n"
			"    %s->%s = vb->oid[idx++];\n",
			cGroupName, cName);
		printInteger32RangeChecks(f, cGroupName, cName, iType);
		break;
	    case SMI_BASETYPE_UNSIGNED32:
		fprintf(f,
			"    if (vb->oid_len < idx) return -1;\n"
			"    %s->%s = vb->oid[idx++];\n",
			cGroupName, cName);
		printUnsigned32RangeChecks(f, cGroupName, cName, iType);
		break;
	    case SMI_BASETYPE_OCTETSTRING:
		maxSize = smiGetMaxSize(iType);
		minSize = smiGetMinSize(iType);
		if (maxSize > 128 - iNode->oidlen) {
		    maxSize = 128 - iNode->oidlen;
		}
		if (minSize == maxSize) {
		    fprintf(f,
			    "    len = %u;\n",
			    minSize);
		} else if (last && indexNode->implied) {
		    fprintf(f,
			    "    if (vb->oid_len < idx) return -1;\n"
			    "    len = vb->oid_len - idx;\n");
		} else {
		    fprintf(f,
			    "    if (vb->oid_len < idx) return -1;\n"
			    "    len = vb->oid[idx++];\n");
		}
		if (minSize != maxSize) {
		    if (minSize > 0 && maxSize < 65535) {
			fprintf(f,
				"    if (len < %u || len > %u) return -1;\n",
				minSize, maxSize);
		    } else if (minSize > 0 && maxSize == 65535) {
			fprintf(f,
				"    if (len < %u) return -1;\n", minSize);
		    } else if (minSize == 0 && maxSize < 65535) {
			fprintf(f,
				"    if (len > %u) return -1;\n", maxSize);
		    }
		}
		fprintf(f,
			"    if (vb->oid_len < idx + len) return -1;\n"
			"    for (i = 0; i < len; i++) {\n"
			"        %s->%s[i] = vb->oid[idx++];\n"
			"    }\n",
			cGroupName, cName);
		if (minSize != maxSize) {
		    fprintf(f, 
			    "    %s->_%sLength = len;\n", cGroupName, cName);
		}
		break;
	    case SMI_BASETYPE_OBJECTIDENTIFIER:
		maxSize = smiGetMaxSize(iType);
		minSize = smiGetMinSize(iType);
		if (maxSize > 128 - iNode->oidlen) {
		    maxSize = 128 - iNode->oidlen;
		}
		if (minSize == maxSize) {
		    fprintf(f,
			    "    len = %u;\n"
			    "    if (vb->oid_len < idx + len) return -1;\n",
			    minSize);
		} else if (last && indexNode->implied) {
		    fprintf(f,
			    "    if (vb->oid_len < idx) return -1;\n"
			    "    len = vb->oid_len - idx;\n");
		} else {
		    fprintf(f,
			    "    if (vb->oid_len < idx) return -1;\n"
			    "    len = vb->oid[idx++];\n"
			    "    if (vb->oid_len < idx + len) return -1;\n");
		}
		if (minSize != maxSize) {
		    if (minSize > 0 && maxSize < 65535) {
			fprintf(f,
				"    if (len < %u || len > %u) return -1;\n",
				minSize, maxSize);
		    } else if (minSize > 0 && maxSize == 65535) {
			fprintf(f,
				"    if (len < %u) return -1;\n", minSize);
		    } else if (minSize == 0 && maxSize < 65535) {
			fprintf(f,
				"    if (len > %u) return -1;\n", maxSize);
		    }
		}
		fprintf(f,
			"    for (i = 0; i < len; i++) {\n"
			"        %s->%s[i] = vb->oid[idx++];\n"
			"    }\n",
			cGroupName, cName);
		if (minSize != maxSize) {
		    fprintf(f,
			    "    %s->_%sLength = len;\n", cGroupName, cName);
		}
		break;
	    default:
		fprintf(f,
			"    /* XXX how to unpack %s->%s ? */\n",
			cGroupName, cName);
		break;
	    }
	    xfree(cName);
	    if (name) xfree(name);
	}
    }

    fprintf(f,
	    "    if (vb->oid_len > idx) return -1;\n"
	    "    return 0;\n"
	    "}\n\n");

    xfree(cGroupName);
    xfree(cPrefix);
}



static void
printPackMethod(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    SmiElement *smiElement;
    SmiNode *indexNode = NULL;
    SmiNode *iNode;
    SmiType *iType;
    char    *cGroupName, *cName, *name;
    unsigned maxSize, minSize;
    int last = 0;

    cGroupName = translate(groupNode->name);

    switch (groupNode->indexkind) {
    case SMI_INDEX_INDEX:
    case SMI_INDEX_REORDER:
	indexNode = groupNode;
	break;
    case SMI_INDEX_EXPAND:	/* TODO: we have to do more work here! */
	indexNode = NULL;
	break;
    case SMI_INDEX_AUGMENT:
    case SMI_INDEX_SPARSE:
	indexNode = smiGetRelatedNode(groupNode);
	break;
    case SMI_INDEX_UNKNOWN:
	indexNode = NULL;
	break;
    }

    /*
     * First check if there are OID or string types so that we
     * know whether we need some additional variables.
     */
    
    for (smiElement = smiGetFirstElement(indexNode);
	 smiElement; smiElement = smiGetNextElement(smiElement)) {
	iNode = smiGetElementNode(smiElement);
	if (iNode) {
	    iType = smiGetNodeType(iNode);
	    if (iType
		&& (iType->basetype == SMI_BASETYPE_OCTETSTRING
		    || iType->basetype == SMI_BASETYPE_OBJECTIDENTIFIER)) {
		break;
	    }
	}
    }

    fprintf(f,
	    "static inline gint8\n"
	    "pack_%s(guint32 *base",
	    cGroupName);
    foreachIndexDo(f, groupNode, printIndexParamsFunc, 1, 0);
    fprintf(f,
	    ")\n"
	    "{\n"
	    "    guint8 idx = %u;\n"
	    "%s"
	    "\n",
	    groupNode->oidlen + 1,
	    smiElement ? "    guint16 i, len;\n" : "");

    for (smiElement = smiGetFirstElement(indexNode);
	 smiElement; smiElement = smiGetNextElement(smiElement)) {
	iNode = smiGetElementNode(smiElement);
	last = (smiGetNextElement(smiElement) == NULL);
	if (iNode) {
	    iType = smiGetNodeType(iNode);
	    if (! iType) {
		continue;
	    }
	    name = getIndexName(indexNode, iNode, smiElement);
	    cName = translate(name ? name : iNode->name);
	    switch (iType->basetype) {
	    case SMI_BASETYPE_ENUM:
	    case SMI_BASETYPE_INTEGER32:
		fprintf(f,
			"    base[idx++] = %s;\n",
			cName);
		break;
	    case SMI_BASETYPE_UNSIGNED32:
		fprintf(f,
			"    base[idx++] = %s;\n",
			cName);
		break;
	    case SMI_BASETYPE_OCTETSTRING:
		maxSize = smiGetMaxSize(iType);
		minSize = smiGetMinSize(iType);
		if (maxSize > 128 - iNode->oidlen) {
		    maxSize = 128 - iNode->oidlen;
		}
		if (minSize == maxSize) {
		    fprintf(f,
			    "    len = %u;\n",
			    minSize);
		} else if (last && indexNode->implied) {
		    fprintf(f,
			    "    len = _%sLength;\n",
			    cName);
		} else {
		    fprintf(f,
			    "    len = _%sLength;\n"
			    "    base[idx++] = len;\n",
			    cName);
		}
		if (minSize == maxSize) {
		    fprintf(f,
			    "    if (len != %u) return -1;\n",
			    minSize);
		} else {
		    if (minSize > 0 && maxSize < 65535) {
			fprintf(f,
				"    if (len < %u || len > %u) return -1;\n",
				minSize, maxSize);
		    } else if (minSize > 0 && maxSize == 65535) {
			fprintf(f,
				"    if (len < %u) return -1;\n", minSize);
		    } else if (minSize == 0 && maxSize < 65535) {
			fprintf(f,
				"    if (len > %u) return -1;\n", maxSize);
		    }
		}
		fprintf(f,
			"    for (i = 0; i < len; i++) {\n"
			"        base[idx++] = %s[i];\n"
			"        if (idx >= 128) return -1;\n"
			"    }\n",
			cName);
		break;
	    case SMI_BASETYPE_OBJECTIDENTIFIER:
		maxSize = smiGetMaxSize(iType);
		minSize = smiGetMinSize(iType);
		if (maxSize > 128 - iNode->oidlen) {
		    maxSize = 128 - iNode->oidlen;
		}
		if (minSize == maxSize) {
		    fprintf(f,
			    "    len = %u;\n",
			    minSize);
		} else if (last && indexNode->implied) {
		    fprintf(f,
			    "    len = _%sLength;\n",
			    cName);
		} else {
		    fprintf(f,
			    "    len = _%sLength;\n"
			    "    base[idx++] = len;\n",
			    cName);
		}
		if (minSize == maxSize) {
		    fprintf(f,
			    "    if (len != %u) return -1;\n",
			    minSize);
		} else {
		    if (minSize > 0 && maxSize < 65535) {
			fprintf(f,
				"    if (len < %u || len > %u) return -1;\n",
				minSize, maxSize);
		    } else if (minSize > 0 && maxSize == 65535) {
			fprintf(f,
				"    if (len < %u) return -1;\n", minSize);
		    } else if (minSize == 0 && maxSize < 65535) {
			fprintf(f,
				"    if (len > %u) return -1;\n", maxSize);
		    }
		}
		fprintf(f,
			"    for (i = 0; i < len; i++) {\n"
			"        base[idx++] = %s[i];\n"
			"        if (idx >= 128) return -1;\n"
			"    }\n",
			cName);
		break;
	    default:
		fprintf(f,
			"    /* XXX how to pack %s ? */\n", cGroupName);
		break;
	    }
	    xfree(cName);
	    if (name) xfree(name);
	}
    }

    fprintf(f,
	    "    return idx;\n"
	    "}\n\n");

    xfree(cGroupName);
}



static void
printAssignMethod(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    char *cPrefix, *cGroupName;

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cGroupName = translate(groupNode->name);

    if (groupNode->nodekind == SMI_NODEKIND_ROW) {
	printUnpackMethod(f, smiModule, groupNode);
	printPackMethod(f, smiModule, groupNode);
    }
    
    fprintf(f,
	    "static inline %s_%s_t *\n"
	    "assign_%s(GList *vbl)\n"
	    "{\n"
	    "    %s_%s_t *%s;\n"
	    "    char *p;\n"
	    "\n",
	    cPrefix, cGroupName, cGroupName,
	    cPrefix, cGroupName, cGroupName);

    fprintf(f,
	    "    %s = %s_new_%s();\n"
	    "    p = (char *) %s + sizeof(%s_%s_t);\n"
	    "    * (GList **) p = vbl;\n"
	    "\n",
	    cGroupName, cPrefix, cGroupName,
	    cGroupName, cPrefix, cGroupName);

    if (groupNode->nodekind == SMI_NODEKIND_ROW) {
	fprintf(f,
		"    if (unpack_%s((GNetSnmpVarBind *) vbl->data, %s) < 0) {\n"
		"        g_warning(\"%%s: invalid instance identifier\", \"%s\");\n"
		"        g_free(%s);\n"
		"        return NULL;\n"
		"    }\n\n",
		cGroupName, cGroupName, cGroupName, cGroupName);
    }

    fprintf(f,
	    "    gnet_snmp_attr_assign(vbl, %s_oid, G_N_ELEMENTS(%s_oid),\n"
	    "                      %s_attr, %s);\n"
	    "\n"
	    "    return %s;\n"
	    "}\n"
	    "\n", cGroupName, cGroupName, cGroupName, cGroupName, cGroupName);
	    
    xfree(cGroupName);
    xfree(cPrefix);
}
 



static void
printGetTableMethod(FILE *f, SmiModule *smiModule, SmiNode *rowNode)
{
    SmiNode      *tableNode;
    char         *cPrefix, *cModuleName, *cRowName, *cTableName;
    unsigned int i;

    tableNode = smiGetParentNode(rowNode);
    if (! tableNode) {
	return;
    }

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cModuleName = translateLower(smiModule->name);
    cRowName = translate(rowNode->name);
    cTableName = translate(tableNode->name);

    fprintf(f,
	    "void\n"
	    "%s_get_%s(GNetSnmp *s, %s_%s_t ***%s, gint64 mask, GError **error)\n"
	    "{\n"
	    "    GList *in = NULL, *out = NULL;\n",
	    cPrefix, cTableName, cPrefix, cRowName, cRowName);

    fprintf(f,
	    "    GList *row;\n"
	    "    int i;\n");

    fprintf(f, "    static guint32 const _base[] = {");
    for (i = 0; i < rowNode->oidlen; i++) {
	fprintf(f, "%u, ", rowNode->oid[i]);
    }
    fprintf(f, "0};\n");
    fprintf(f, "    guint32 base[128];\n");

    fprintf(f,
	    "\n"
	    "    *%s = NULL;\n"
	    "    memcpy(base, _base, sizeof(_base));\n"
	    "\n",
	    cRowName);

    fprintf(f,
	    "    gnet_snmp_attr_get(s, &in, base, %u, %u, %s_attr, mask);\n",
	    rowNode->oidlen+1, rowNode->oidlen, cRowName);

    fprintf(f,
	    "\n"
	    "    out = gnet_snmp_sync_table(s, in, error);\n"
	    "    /* gnet_snmp_varbind_list_free(in); */\n"
	    "\n");
    fprintf(f,
	    "    if (out) {\n"
	    "        *%s = (%s_%s_t **) g_malloc0((g_list_length(out) + 1) * sizeof(%s_%s_t *));\n"
	    "        for (row = out, i = 0; row; row = g_list_next(row), i++) {\n"
	    "            (*%s)[i] = assign_%s(row->data);\n"
	    "        }\n"
	    "    }\n"
	    "}\n"
	    "\n",
	    cRowName, cPrefix, cRowName,
	    cPrefix, cRowName,
	    cRowName, cRowName);
    
    xfree(cTableName);
    xfree(cRowName);
    xfree(cModuleName);
    xfree(cPrefix);
}



static void
printGetRowMethod(FILE *f, SmiModule *smiModule, SmiNode *rowNode)
{
    char       *cPrefix, *cRowName;

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cRowName = translate(rowNode->name);

    fprintf(f,
	    "void\n"
	    "%s_get_%s(GNetSnmp *s, %s_%s_t **%s",
	    cPrefix, cRowName, cPrefix, cRowName, cRowName);
    foreachIndexDo(f, rowNode, printIndexParamsFunc, 1, 0);
    fprintf(f,
	    ", gint64 mask, GError **error)\n"
	    "{\n"
	    "    GList *in = NULL, *out = NULL;\n");

    fprintf(f,
	    "    guint32 base[128];\n"
	    "    gint8 len;\n"
	    "\n"
	    "    memcpy(base, %s_oid, sizeof(%s_oid));\n",
	    cRowName, cRowName);

    fprintf(f,
	    "    len = pack_%s(base",
	    cRowName);

    foreachIndexDo(f, rowNode, printIndexParamsFunc, 0, 0);

    fprintf(f,
	    ");\n"
	    "    if (len < 0) {\n"
	    "        g_warning(\"%%s: invalid index values\", \"%s\");\n"
	    "        s->error_status = GNET_SNMP_PDU_ERR_INTERNAL;\n"
	    "        return;\n"
	    "    }\n",
	    cRowName);

    fprintf(f,
	    "\n"
	    "    *%s = NULL;\n"
	    "\n",
	    cRowName);

    fprintf(f,
	    "    gnet_snmp_attr_get(s, &in, base, len, %u, %s_attr, mask);\n",
	    rowNode->oidlen, cRowName);

    fprintf(f,
	    "\n"
	    "    out = gnet_snmp_sync_get(s, in, error);\n"
	    "    g_list_foreach(in, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "    g_list_free(in);\n"
	    "    if (out) {\n"
	    "        if (s->error_status != GNET_SNMP_PDU_ERR_NOERROR) {\n"
	    "            g_list_foreach(out, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "            g_list_free(out);\n"
	    "            return;\n"
	    "        }\n"
	    "        *%s = assign_%s(out);\n"
	    "    }\n"
	    "}\n"
	    "\n", cRowName, cRowName);
    
    xfree(cRowName);
    xfree(cPrefix);
}



static void
printSetRowMethod(FILE *f, SmiModule *smiModule, SmiNode *rowNode)
{
    char         *cPrefix, *cRowName, *cTableName;
    SmiNode      *tableNode;

    tableNode = smiGetParentNode(rowNode);
    if (! tableNode) {
	return;
    }

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cRowName = translate(rowNode->name);
    cTableName = translate(tableNode->name);

    fprintf(f,
	    "void\n"
	    "%s_set_%s(GNetSnmp *s, %s_%s_t *%s, gint64 mask, GError **error)\n"
	    "{\n"
	    "    GList *in = NULL, *out = NULL;\n",
	    cPrefix, cRowName, cPrefix, cRowName, cRowName);

    fprintf(f,
	    "    guint32 base[128];\n"
	    "    gint8 len;\n"
	    "\n"
	    "    memcpy(base, %s_oid, sizeof(%s_oid));\n",
	    cRowName, cRowName);

    fprintf(f,
	    "    len = pack_%s(base", cRowName);

    foreachIndexDo(f, rowNode, printIndexParamsPassFunc, 0, 0);

    fprintf(f,
	    ");\n"
	    "    if (len < 0) {\n"
	    "        g_warning(\"%%s: invalid index values\", \"%s\");\n"
	    "        s->error_status = GNET_SNMP_PDU_ERR_INTERNAL;\n"
	    "        return;\n"
	    "    }\n"
	    "\n",
	    cRowName);

    fprintf(f,
	    "    gnet_snmp_attr_set(s, &in, base, len, %u, %s_attr, mask, %s);\n",
	    rowNode->oidlen, cRowName, cRowName);

    fprintf(f,
	    "\n"
	    "    out = gnet_snmp_sync_set(s, in, error);\n"
	    "    g_list_foreach(in, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "    g_list_free(in);\n"
	    "    if (out) {\n"
	    "        g_list_foreach(out, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "        g_list_free(out);\n"
	    "    }\n"
	    "}\n"
	    "\n");

    xfree(cTableName);
    xfree(cRowName);
    xfree(cPrefix);
}



static void
printCreateMethod(FILE *f, SmiNode *groupNode, SmiNode *smiNode)
{
    char        *cPrefix, *cNodeName, *cGroupName;
    char	*dModuleName, *dNodeName;
    SmiModule   *smiModule;

    smiModule = smiGetNodeModule(smiNode);

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    dModuleName = translateUpper(smiModule->name);
    cGroupName = translate(groupNode->name);
    cNodeName = translate(smiNode->name);
    dNodeName = translateUpper(smiNode->name);

    fprintf(f,
	    "void\n"
	    "%s_create_%s(GNetSnmp *s",
	    cPrefix, cGroupName);

    foreachIndexDo(f, groupNode, printIndexParamsFunc, 1, 0);
    
    fprintf(f,
	    ", GError **error)\n"
	    "{\n"
	    "    %s_%s_t *%s;\n"
	    "    gint32 create = 4; /* SNMPv2-TC::RowStatus createAndGo */\n"
	    "\n",
	    cPrefix, cGroupName, cGroupName);

    fprintf(f,
	    "    %s = %s_new_%s();\n",
	    cGroupName, cPrefix, cGroupName);

    foreachIndexDo(f, groupNode, printIndexAssignmentFunc, 0, 0);

    fprintf(f, "    %s->%s = &create;\n",
	    cGroupName, cNodeName);

    fprintf(f,
	    "    %s_set_%s(s, %s, %s_%s, error);\n"
	    "    %s_free_%s(%s);\n",
	    cPrefix, cGroupName, cGroupName,
	    dModuleName, dNodeName,
	    cPrefix, cGroupName, cGroupName);

    fprintf(f,
	    "}\n"
	    "\n");

    xfree(dNodeName);
    xfree(cNodeName);
    xfree(cGroupName);
    xfree(dModuleName);
    xfree(cPrefix);
}



static void
printDeleteMethod(FILE *f, SmiNode *groupNode, SmiNode *smiNode)
{
    char        *cPrefix, *cNodeName, *cGroupName;
    char	*dModuleName, *dNodeName;
    SmiModule   *smiModule;

    smiModule = smiGetNodeModule(smiNode);

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    dModuleName = translateUpper(smiModule->name);
    cGroupName = translate(groupNode->name);
    cNodeName = translate(smiNode->name);
    dNodeName = translateUpper(smiNode->name);

    fprintf(f,
	    "void\n"
	    "%s_delete_%s(GNetSnmp *s",
	    cPrefix, cGroupName);

    foreachIndexDo(f, groupNode, printIndexParamsFunc, 1, 0);
    
    fprintf(f,
	    ", GError **error)\n"
	    "{\n"
	    "    %s_%s_t *%s;\n"
	    "    gint32 destroy = 6; /* SNMPv2-TC::RowStatus destroy */\n"
	    "\n",
	    cPrefix, cGroupName, cGroupName);

    fprintf(f,
	    "    %s_get_%s(s, &%s",
	    cPrefix, cGroupName, cGroupName);

    foreachIndexDo(f, groupNode, printIndexParamsFunc, 0, 0);
    
    fprintf(f,
	    ", %s_%s, error);\n"
	    "    if (error && *error) return;\n"
	    "    if (s->error_status || !%s) return;\n",
	    dModuleName, dNodeName,
	    cGroupName);

    fprintf(f, "    %s->%s = &destroy;\n",
	    cGroupName, cNodeName);

    fprintf(f,
	    "    %s_set_%s(s, %s, %s_%s, error);\n"
	    "    %s_free_%s(%s);\n",
	    cPrefix, cGroupName, cGroupName,
	    dModuleName, dNodeName,
	    cPrefix, cGroupName, cGroupName);

    fprintf(f,
	    "}\n"
	    "\n");

    xfree(dNodeName);
    xfree(cNodeName);
    xfree(cGroupName);
    xfree(dModuleName);
    xfree(cPrefix);
}



static void
printSetMethod(FILE *f, SmiNode *groupNode, SmiNode *smiNode)
{
    char        *cPrefix, *cNodeName, *cGroupName;
    char	*dModuleName, *dNodeName;
    SmiType	*smiType;
    SmiModule   *smiModule;
    unsigned	minSize, maxSize;

    smiModule = smiGetNodeModule(smiNode);
    smiType = smiGetNodeType(smiNode);
    if (! smiType) {
	return;
    }

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    dModuleName = translateUpper(smiModule->name);
    cGroupName = translate(groupNode->name);
    cNodeName = translate(smiNode->name);
    dNodeName = translateUpper(smiNode->name);

    fprintf(f,
	    "void\n"
	    "%s_set_%s(GNetSnmp *s",
	    cPrefix, cNodeName);

    foreachIndexDo(f, groupNode, printIndexParamsFunc, 1, 0);
    printParam(f, smiNode);
    
    fprintf(f,
	    ", GError **error)\n"
	    "{\n"
	    "    %s_%s_t *%s;\n"
	    "\n", cPrefix, cGroupName, cGroupName);

    fprintf(f,
	    "    %s_get_%s(s, &%s",
	    cPrefix, cGroupName, cGroupName);

    foreachIndexDo(f, groupNode, printIndexParamsFunc, 0, 0);
    
    fprintf(f,
	    ", %s_%s, error);\n"
	    "    if (error && *error) return;\n"
	    "    if (s->error_status || !%s) return;\n",
	    dModuleName, dNodeName,
	    cGroupName);

    switch (smiType->basetype) {
    case SMI_BASETYPE_OBJECTIDENTIFIER:
    case SMI_BASETYPE_OCTETSTRING:
	maxSize = smiGetMaxSize(smiType);
	minSize = smiGetMinSize(smiType);
	fprintf(f, "    %s->%s = %s;\n",
		cGroupName, cNodeName, cNodeName);
	if (minSize != maxSize) {
	    fprintf(f, "    %s->_%sLength = _%sLength;\n",
		    cGroupName, cNodeName, cNodeName);
	}
	break;
    case SMI_BASETYPE_UNSIGNED32:
    case SMI_BASETYPE_ENUM:
    case SMI_BASETYPE_INTEGER32:
	fprintf(f, "    %s->%s = &%s;\n",
		cGroupName, cNodeName, cNodeName);
	break;
    default:
	fprintf(f, "    /* ?? */\n");
	break;
    }

    fprintf(f,
	    "    %s_set_%s(s, %s, %s_%s, error);\n"
	    "    %s_free_%s(%s);\n",
	    cPrefix, cGroupName, cGroupName,
	    dModuleName, dNodeName,
	    cPrefix, cGroupName, cGroupName);

    fprintf(f,
	    "}\n"
	    "\n");

    xfree(dNodeName);
    xfree(cNodeName);
    xfree(cGroupName);
    xfree(dModuleName);
    xfree(cPrefix);
}



static void
printGetScalarsMethod(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    char         *cPrefix, *cGroupName;
    unsigned int i;

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cGroupName = translate(groupNode->name);

    fprintf(f,
	    "void\n"
	    "%s_get_%s(GNetSnmp *s, %s_%s_t **%s, gint64 mask, GError **error)\n"
	    "{\n"
	    "    GList *in = NULL, *out = NULL;\n",
	    cPrefix, cGroupName, cPrefix, cGroupName, cGroupName);

    fprintf(f, "    static const guint32 _base[] = {");
    for (i = 0; i < groupNode->oidlen; i++) {
	fprintf(f, "%u, ", groupNode->oid[i]);
    }
    fprintf(f, "0};\n");
    fprintf(f, "    guint32 base[128];\n");

    fprintf(f,
	    "\n"
	    "    *%s = NULL;\n"
	    "    memcpy(base, _base, sizeof(_base));\n"
	    "\n",
	    cGroupName);

    fprintf(f,
	    "    gnet_snmp_attr_get(s, &in, base, %u, %u, %s_attr, mask);\n",
	    groupNode->oidlen + 1, groupNode->oidlen, cGroupName);

    fprintf(f,
	    "\n"
	    "    out = gnet_snmp_sync_getnext(s, in, error);\n"
	    "    g_list_foreach(in, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "    g_list_free(in);\n"
	    "    if (out) {\n"
	    "        if ((error && *error) || s->error_status != GNET_SNMP_PDU_ERR_NOERROR) {\n"
	    "            g_list_foreach(out, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "            g_list_free(out);\n"
	    "            return;\n"
	    "        }\n"
	    "        *%s = assign_%s(out);\n"
	    "    }\n"
	    "}\n"
	    "\n", cGroupName, cGroupName);
    
    xfree(cGroupName);
    xfree(cPrefix);
}



static void
printSetScalarsMethod(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    char         *cPrefix, *cGroupName;
    unsigned int i;

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cGroupName = translate(groupNode->name);

    fprintf(f,
	    "void\n"
	    "%s_set_%s(GNetSnmp *s, %s_%s_t *%s, gint64 mask, GError **error)\n"
	    "{\n"
	    "    GList *in = NULL, *out = NULL;\n",
	    cPrefix, cGroupName, cPrefix, cGroupName, cGroupName);

    fprintf(f, "    static guint32 base[] = {");
    for (i = 0; i < groupNode->oidlen; i++) {
	fprintf(f, "%u, ", groupNode->oid[i]);
    }
    fprintf(f, "0, 0};\n\n");

    fprintf(f,
	    "    gnet_snmp_attr_set(s, &in, base, %u, %u, %s_attr, mask, %s);\n",
	    groupNode->oidlen + 2, groupNode->oidlen, cGroupName, cGroupName);

    fprintf(f,
	    "\n"
	    "    out = gnet_snmp_sync_set(s, in, error);\n"
	    "    g_list_foreach(in, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "    g_list_free(in);\n"
	    "    if (out) {\n"
	    "        g_list_foreach(out, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "        g_list_free(out);\n"
	    "    }\n"
	    "}\n"
	    "\n");

    xfree(cGroupName);
    xfree(cPrefix);
}



static void
printNewMethod(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    char *cPrefix, *cGroupName;

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cGroupName = translate(groupNode->name);

    fprintf(f,
	    "%s_%s_t *\n"
	    "%s_new_%s()\n"
	    "{\n"
	    "    %s_%s_t *%s;\n"
	    "\n",
	    cPrefix, cGroupName,
	    cPrefix, cGroupName,
	    cPrefix, cGroupName, cGroupName);

    fprintf(f,
	    "    %s = (%s_%s_t *) g_malloc0(sizeof(%s_%s_t) + sizeof(gpointer));\n"
	    "    return %s;\n"
	    "}\n"
	    "\n",
	    cGroupName, cPrefix, cGroupName,
	    cPrefix, cGroupName, cGroupName);

    xfree(cGroupName);
    xfree(cPrefix);
}



static void
printFreeTableMethod(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    SmiNode *tableNode;
    char *cPrefix, *cGroupName, *cTableName;

    tableNode = smiGetParentNode(groupNode);
    if (! tableNode) {
	return;
    }

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cGroupName = translate(groupNode->name);
    cTableName = translate(tableNode->name);

    fprintf(f,
	    "void\n"
	    "%s_free_%s(%s_%s_t **%s)\n"
	    "{\n"
	    "    int i;\n"
	    "\n",
	    cPrefix, cTableName, cPrefix, cGroupName, cGroupName);

    fprintf(f,	    
	    "    if (%s) {\n"
	    "        for (i = 0; %s[i]; i++) {\n"
	    "            %s_free_%s(%s[i]);\n"
	    "        }\n"
	    "        g_free(%s);\n"
	    "    }\n"
	    "}\n"
	    "\n",
	    cGroupName, cGroupName, cPrefix,
	    cGroupName, cGroupName, cGroupName);

    xfree(cTableName);
    xfree(cGroupName);
    xfree(cPrefix);
}
 



static void
printFreeMethod(FILE *f, SmiModule *smiModule, SmiNode *groupNode)
{
    char *cPrefix, *cGroupName;

    cPrefix = prefix ? xstrdup(prefix) : translateLower(smiModule->name);
    cGroupName = translate(groupNode->name);

    fprintf(f,
	    "void\n"
	    "%s_free_%s(%s_%s_t *%s)\n"
	    "{\n"
	    "    GList *vbl;\n"
	    "    char *p;\n"
	    "\n",
	    cPrefix, cGroupName, cPrefix, cGroupName, cGroupName);

    fprintf(f,	    
	    "    if (%s) {\n"
	    "        p = (char *) %s + sizeof(%s_%s_t);\n"
	    "        vbl = * (GList **) p;\n"
	    "        g_list_foreach(vbl, (GFunc) gnet_snmp_varbind_delete, NULL);\n"
	    "        g_list_free(vbl);\n"
	    "        g_free(%s);\n"
	    "    }\n"
	    "}\n"
	    "\n",
	    cGroupName, cGroupName, cPrefix, cGroupName, cGroupName);

    xfree(cGroupName);
    xfree(cPrefix);
}
 



static void
printStubMethod2(FILE *f, SmiNode *groupNode)
{
    SmiNode *smiNode;
    SmiType *smiType;

    for (smiNode = smiGetFirstChildNode(groupNode);
	 smiNode;
	 smiNode = smiGetNextChildNode(smiNode)) {
	if (smiNode->nodekind & (SMI_NODEKIND_COLUMN | SMI_NODEKIND_SCALAR)
	    && (smiNode->access >= SMI_ACCESS_READ_ONLY)) {
	    if (smiNode->access == SMI_ACCESS_READ_WRITE) {
		smiType = smiGetNodeType(smiNode);
		if (smiType && smiType->name
		    && strcmp(smiType->name, "RowStatus") == 0) {
		    if (cflag) printCreateMethod(f, groupNode, smiNode);
		    if (dflag) printDeleteMethod(f, groupNode, smiNode);
		} else {
		    if (! isIndex(groupNode, smiNode)) {
			if (sflag) printSetMethod(f, groupNode, smiNode);
		    }
		}
	    }
	}	    
    }
}



static void
printStubMethods(FILE *f, SmiModule *smiModule)
{
    SmiNode   *smiNode;
    int       cnt = 0;
    const unsigned int groupkind = SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN;
    
    for (smiNode = smiGetFirstNode(smiModule, SMI_NODEKIND_ANY);
	 smiNode;
	 smiNode = smiGetNextNode(smiNode, SMI_NODEKIND_ANY)) {
	if (isGroup(smiNode, groupkind) && isAccessible(smiNode)) {
	    cnt++;
	    printNewMethod(f, smiModule, smiNode);
	    printAssignMethod(f, smiModule, smiNode);
	    if (smiNode->nodekind == SMI_NODEKIND_ROW) {
		printGetTableMethod(f, smiModule, smiNode);
		printGetRowMethod(f, smiModule, smiNode);
		if (isWritable(smiNode, SMI_NODEKIND_COLUMN)) {
		    printSetRowMethod(f, smiModule, smiNode);
		}
	    } else {
		printGetScalarsMethod(f, smiModule, smiNode);
		if (isWritable(smiNode, SMI_NODEKIND_SCALAR)) {
		    printSetScalarsMethod(f, smiModule, smiNode);
		}
	    }
	    printFreeMethod(f, smiModule, smiNode);
	    if (smiNode->nodekind == SMI_NODEKIND_ROW) {
		printFreeTableMethod(f, smiModule, smiNode);
	    }
	    printStubMethod2(f, smiNode);
	}
    }
    
    if (cnt) {
	fprintf(f, "\n");
    }
}



static void
dumpStubs(SmiModule *smiModule, char *baseName)
{
    FILE *f;

    f = createFile(baseName, ".c");
    if (! f) {
        return;
    }

    fprintTopComment(f, smiModule);

    fprintf(f,
	    "#include \"%s.h\"\n"
	    "\n",
	    baseName);

    printStubEnumerations(f, smiModule);
    printStubIdentities(f, smiModule);
    printStubNotifications(f, smiModule);

    printStubContraints(f, smiModule);
    printStubAttributes(f, smiModule);
    printStubMethods(f, smiModule);
    
    if (fflush(f) || ferror(f)) {
	perror("smidump: write error");
	exit(1);
    }

    fclose(f);
}



static void
dumpScli(int modc, SmiModule **modv, int flags, char *output)
{
    char	*baseName;
    int		i, code;

    if (include) {
	incl_regex = &_incl_regex;
	code = regcomp(incl_regex, include, REG_EXTENDED|REG_NOSUB);
	if (code != 0) {
	    char buffer[256];
	    regerror(code, incl_regex, buffer, sizeof(buffer));
	    fprintf(stderr, "smidump: regular expression error: %s\n", buffer);
	    exit(1);
	}
    }

    if (exclude) {
	excl_regex = &_excl_regex;
	code = regcomp(excl_regex, exclude, REG_EXTENDED|REG_NOSUB);
	if (code != 0) {
	    char buffer[256];
	    regerror(code, excl_regex, buffer, sizeof(buffer));
	    fprintf(stderr, "smidump: regular expression error: %s\n", buffer);
	    if (incl_regex) {
		regfree(incl_regex);
	    }
	    exit(1);
	}
    }

    if (flags & SMIDUMP_FLAG_UNITE) {
	/* not implemented yet */
    } else {
	for (i = 0; i < modc; i++) {
	    baseName = output ? output : translateFileName(modv[i]->name);
	    dumpHeader(modv[i], baseName);
	    dumpStubs(modv[i], baseName);
	    if (! output) xfree(baseName);
	}
    }

    if (incl_regex) {
	regfree(incl_regex);
	incl_regex = NULL;
    }
    if (excl_regex) {
	regfree(excl_regex);
	excl_regex = NULL;
    }
}



void initScli()
{
    static SmidumpDriverOption opt[] = {
	{ "prefix", OPT_STRING, &prefix, 0,
	  "use prefix instead of module name in stubs"},
	{ "include", OPT_STRING, &include, 0,
	  "include stubs for groups matching a regex"},
	{ "exclude", OPT_STRING, &exclude, 0,
	  "exclude stubs for groups matching a regex"},
	{ "set", OPT_FLAG, &sflag, 0,
	  "generate set stubs for writable objects"},
	{ "create", OPT_FLAG, &cflag, 0,
	  "generate create stubs for tables using RowStatus"},
	{ "delete", OPT_FLAG, &dflag, 0,
	  "generate delete stubs for tables using RowStatus"},
        { 0, OPT_END, 0, 0 }
    };

    static SmidumpDriver driver = {
	"scli",
	dumpScli,
	0,
	SMIDUMP_DRIVER_CANT_UNITE
	| SMIDUMP_DRIVER_CANT_YANG,
	"ANSI C manager stubs for the gsnmp package",
	opt,
	NULL
    };

    smidumpRegisterDriver(&driver);
}
