/*
 *
 *  Copyright (c) 2015, Red Hat, Inc.
 *  Copyright (c) 2015, Masatake YAMATO
 *
 *  Author: Masatake YAMATO <yamato@redhat.com>
 *
 *   This source code is released for free distribution under the terms of the
 *   GNU General Public License version 2 or (at your option) any later version.
 *
 */

#include "general.h"  /* must always come first */

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "ctags.h"
#include "debug.h"
#include "entry.h"
#include "entry_p.h"
#include "field.h"
#include "field_p.h"
#include "kind.h"
#include "options_p.h"
#include "parse_p.h"
#include "read.h"
#include "routines.h"
#include "trashbox.h"
#include "writer_p.h"
#include "xtag_p.h"

#include "optscript.h"
#include "script_p.h"

#define FIELD_NULL_LETTER_CHAR '-'
#define FIELD_NULL_LETTER_STRING "-"

typedef struct sFieldObject {
	fieldDefinition *def;
	vString     *buffer;
	const char* nameWithPrefix;
	langType language;
	fieldType sibling;
} fieldObject;

static const char *renderFieldName (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b);
static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldInputNoEscape (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldSignatureNoEscape (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldScopeNoEscape (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldLineNumber (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldLanguage (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldAccess (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldImplementation (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldFile (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldPattern (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldRoles (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldRefMarker (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldExtras (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldXpath (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldScopeKindName(const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldEnd (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldEpoch (const tagEntryInfo *const tag, const char *value, vString* b);
static const char *renderFieldNth (const tagEntryInfo *const tag, const char *value, vString* b);

static bool doesContainAnyCharInName (const tagEntryInfo *const tag, const char *value, const char *chars);
static bool doesContainAnyCharInInput (const tagEntryInfo *const tag, const char*value, const char *chars);
static bool doesContainAnyCharInFieldScope (const tagEntryInfo *const tag, const char *value, const char *chars);
static bool doesContainAnyCharInSignature (const tagEntryInfo *const tag, const char *value, const char *chars);

static bool     isTyperefFieldAvailable   (const tagEntryInfo *const tag);
static bool     isFileFieldAvailable      (const tagEntryInfo *const tag);
static bool     isInheritsFieldAvailable  (const tagEntryInfo *const tag);
static bool     isAccessFieldAvailable    (const tagEntryInfo *const tag);
static bool     isImplementationFieldAvailable (const tagEntryInfo *const tag);
static bool     isSignatureFieldAvailable (const tagEntryInfo *const tag);
static bool     isExtrasFieldAvailable    (const tagEntryInfo *const tag);
static bool     isXpathFieldAvailable     (const tagEntryInfo *const tag);
static bool     isEndFieldAvailable       (const tagEntryInfo *const tag);
static bool     isEpochAvailable          (const tagEntryInfo *const tag);
static bool     isNthAvailable            (const tagEntryInfo *const tag);

static EsObject* getFieldValueForName (const tagEntryInfo *, const fieldDefinition *);
static EsObject* setFieldValueForName (tagEntryInfo *, const fieldDefinition *, const EsObject *);
static EsObject* getFieldValueForInput (const tagEntryInfo *, const fieldDefinition *);
static EsObject* getFieldValueForKind (const tagEntryInfo *, const fieldDefinition *);
static EsObject* getFieldValueForTyperef (const tagEntryInfo *, const fieldDefinition *);
static EsObject* setFieldValueForTyperef (tagEntryInfo *, const fieldDefinition *, const EsObject *);
static EsObject* checkFieldValueForTyperef (const fieldDefinition *, const EsObject *);
static EsObject* getFieldValueForScope (const tagEntryInfo *, const fieldDefinition *);
static EsObject* setFieldValueForScope (tagEntryInfo *, const fieldDefinition *, const EsObject *);
static EsObject* checkFieldValueForScope (const fieldDefinition *, const EsObject *);
static EsObject* getFieldValueForExtras (const tagEntryInfo *, const fieldDefinition *);
static EsObject* getFieldValueForAccess (const tagEntryInfo *, const fieldDefinition *);
static EsObject* setFieldValueForAccess (tagEntryInfo *, const fieldDefinition *, const EsObject *);
static EsObject* getFieldValueForSignature (const tagEntryInfo *, const fieldDefinition *);
static EsObject* setFieldValueForSignature (tagEntryInfo *, const fieldDefinition *, const EsObject *);
static EsObject* getFieldValueForRoles (const tagEntryInfo *, const fieldDefinition *);
static EsObject* getFieldValueForLineCommon (const tagEntryInfo *, const fieldDefinition *);
static EsObject* checkFieldValueForLineCommon (const fieldDefinition *, const EsObject *);
static EsObject* setFieldValueForLineCommon (tagEntryInfo *, const fieldDefinition *, const EsObject *);
static EsObject* setFieldValueForInherits (tagEntryInfo *, const fieldDefinition *, const EsObject *);

#define WITH_DEFUALT_VALUE(str) ((str)?(str):FIELD_NULL_LETTER_STRING)

static fieldDefinition fieldDefinitionsFixed [] = {
	[FIELD_NAME] = {
		.letter             = 'N',
		.name               = "name",
		.description        = "tag name",
		.enabled            = true,
		.render             = renderFieldName,
		.renderNoEscaping   = renderFieldNameNoEscape,
		.doesContainAnyChar = doesContainAnyCharInName,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_STRING,
		.getterValueType    = NULL,
		.getValueObject     = getFieldValueForName,
		.setterValueType    = NULL,
		.checkValueForSetter= NULL,
		.setValueObject     = setFieldValueForName,
	},
	[FIELD_INPUT_FILE] = {
		.letter             = 'F',
		.name               = "input",
		.description        = "input file",
		.enabled            = true,
		.render             = renderFieldInput,
		.renderNoEscaping   = renderFieldInputNoEscape,
		.doesContainAnyChar = doesContainAnyCharInInput,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_STRING,
		.getterValueType    = NULL,
		.getValueObject     = getFieldValueForInput,
		.setterValueType    = NULL,
		.checkValueForSetter= NULL,
		.setValueObject     = NULL,
	},
	[FIELD_PATTERN] = {
		.letter             = 'P',
		.name               = "pattern",
		.description        = "pattern",
		.enabled            = true,
		.render             = renderFieldPattern,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_STRING|FIELDTYPE_BOOL,
	},
};

static fieldDefinition fieldDefinitionsExuberant [] = {
	[FIELD_COMPACT_INPUT_LINE - FIELD_ECTAGS_START] = {
		.letter             = 'C',
		.name               = "compact",
		.description        = "compact input line (used only in xref output)",
		.enabled            = false,
		.render             = renderFieldCompactInputLine,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_STRING,
	},
	[FIELD_FILE_SCOPE - FIELD_ECTAGS_START] = {
		.letter             = 'f',
		.name               = "file",
		.description        = "File-restricted scoping",
		.enabled            = true,
		.render             = renderFieldFile,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = isFileFieldAvailable,
		.dataType           = FIELDTYPE_BOOL,
	},
	[FIELD_KIND_LONG - FIELD_ECTAGS_START] = {
		.letter             = 'K',
		.name               = NULL,
		.description        = "Kind of tag in long-name form",
		.enabled            = false,
		.render             = renderFieldKindName,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_STRING,
	},
	[FIELD_KIND - FIELD_ECTAGS_START] = {
		.letter             ='k',
		.name               = NULL,
		.description        = "Kind of tag in one-letter form",
		.enabled            = true,
		.render             = renderFieldKindLetter,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_STRING,
	},
	[FIELD_LANGUAGE - FIELD_ECTAGS_START] = {
		.letter             = 'l',
		.name               = "language",
		.description        = "Language of input file containing tag",
		.enabled            = false,
		.render             = renderFieldLanguage,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.dataType           = FIELDTYPE_STRING,
	},
	[FIELD_LINE_NUMBER - FIELD_ECTAGS_START] = {
		.letter             = 'n',
		.name               = "line",
		.description        = "Line number of tag definition",
		.enabled            = false,
		.render             = renderFieldLineNumber,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_INTEGER,
		.getterValueType    = "int",
		.getValueObject     = getFieldValueForLineCommon,
		.setterValueType    = "matchloc|int",
		.checkValueForSetter= checkFieldValueForLineCommon,
		.setValueObject     = setFieldValueForLineCommon,
	},
	[FIELD_SCOPE - FIELD_ECTAGS_START] = {
		.letter				= 's',
		.name				= NULL,
		.description		= "[tags output] scope (kind:name) of tag definition, [xref and json output] name of scope",
		.enabled			= true,
		.render				= renderFieldScope,
		.renderNoEscaping	= renderFieldScopeNoEscape,
		.doesContainAnyChar = doesContainAnyCharInFieldScope,
		.isValueAvailable	= NULL,
		.dataType			= FIELDTYPE_STRING,
	},
	[FIELD_TYPE_REF - FIELD_ECTAGS_START] = {
		.letter				= 't',
		.name				= "typeref",
		.description		= "Type and name of a variable or typedef",
		.enabled			= true,
		.render				= renderFieldTyperef,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= isTyperefFieldAvailable,
		.dataType			= FIELDTYPE_STRING,
		.getterValueType    = "[string string]",
		.getValueObject     = getFieldValueForTyperef,
		.setterValueType    = "[string string]|string|index:int|false",
		.checkValueForSetter= checkFieldValueForTyperef,
		.setValueObject     = setFieldValueForTyperef,
	},
	[FIELD_KIND_KEY - FIELD_ECTAGS_START] = {
		.letter				= 'z',
		.name				= "kind",
		.description		= "[tags output] prepend \"kind:\" to k/ (or K/) field output, [xref and json output] kind in long-name form",
		.enabled			= false,
		/* Following renderer is for handling --_xformat=%{kind};
		   and is not for tags output. */
		.render				= renderFieldKindName,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= NULL,
		.dataType			= FIELDTYPE_STRING,
		.getterValueType    = "name",
		.getValueObject     = getFieldValueForKind,
		.setterValueType    = NULL,
		.checkValueForSetter= NULL,
		.setValueObject     = NULL,
	},
	[FIELD_INHERITANCE - FIELD_ECTAGS_START] = {
		.letter             = 'i',
		.name               = "inherits",
		.description        = "Inheritance information",
		.enabled            = false,
		.render             = renderFieldInherits,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = isInheritsFieldAvailable,
		.dataType           = FIELDTYPE_STRING|FIELDTYPE_BOOL,
		.getterValueType    = NULL,
		.getValueObject     = NULL,
		.setterValueType    = NULL,
		.checkValueForSetter= NULL,
		.setValueObject     = setFieldValueForInherits,
	},
	[FIELD_ACCESS - FIELD_ECTAGS_START] = {
		.letter             = 'a',
		.name               = "access",
		.description        = "Access (or export) of class members",
		.enabled            = false,
		.render             = renderFieldAccess,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = isAccessFieldAvailable,
		.dataType           = FIELDTYPE_STRING,
		.getterValueType    = NULL,
		.getValueObject     = getFieldValueForAccess,
		.setterValueType    = NULL,
		.checkValueForSetter= NULL,
		.setValueObject     = setFieldValueForAccess,
	},
	[FIELD_IMPLEMENTATION - FIELD_ECTAGS_START] = {
		.letter             = 'm',
		.name               = "implementation",
		.description        = "Implementation information",
		.enabled            = false,
		.render             = renderFieldImplementation,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = isImplementationFieldAvailable,
		.dataType           = FIELDTYPE_STRING,

	},
	[FIELD_SIGNATURE - FIELD_ECTAGS_START] = {
		.letter				= 'S',
		.name				= "signature",
		.description		= "Signature of routine (e.g. prototype or parameter list)",
		.enabled			= false,
		.render				= renderFieldSignature,
		.renderNoEscaping	= renderFieldSignatureNoEscape,
		.doesContainAnyChar = doesContainAnyCharInSignature,
		.isValueAvailable	= isSignatureFieldAvailable,
		.dataType			= FIELDTYPE_STRING,
		.getterValueType    = NULL,
		.getValueObject     = getFieldValueForSignature,
		.setterValueType    = NULL,
		.checkValueForSetter= NULL,
		.setValueObject     = setFieldValueForSignature,
	},
};

static fieldDefinition fieldDefinitionsUniversal [] = {
	[FIELD_REF_MARK - FIELDS_UCTAGS_START] = {
		.letter             = 'R',
		.name				= NULL,
		.description		= "Marker (R or D) representing whether tag is definition or reference",
		.enabled			= false,
		.render				= renderFieldRefMarker,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= NULL,
		.dataType			= FIELDTYPE_STRING,
	},
	[FIELD_SCOPE_KEY - FIELDS_UCTAGS_START] = {
		.letter             = 'Z',
		.name               = "scope",
		.description        = "[tags output] prepend \"scope:\" key to s/scope field output, [xref and json output] the same as s/ field",
		.enabled            = false,
		/* Following renderer is for handling --_xformat=%{scope};
		   and is not for tags output. */
		.render             = renderFieldScope,
		.renderNoEscaping   = renderFieldScopeNoEscape,
		.doesContainAnyChar = doesContainAnyCharInFieldScope,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_STRING,
		.getterValueType    = "int",
		.getValueObject     = getFieldValueForScope,
		.setterValueType    = "int",
		.checkValueForSetter= checkFieldValueForScope,
		.setValueObject     = setFieldValueForScope,
	},
	[FIELD_SCOPE_KIND_LONG - FIELDS_UCTAGS_START] = {
		.letter				= 'p',
		.name				= "scopeKind",
		.description		= "[tags output] no effect, [xref and json output] kind of scope in long-name form",
		.enabled			= false,
		.render				= renderFieldScopeKindName,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= NULL,
		.dataType			= FIELDTYPE_STRING,
	},
	[FIELD_ROLES - FIELDS_UCTAGS_START] = {
		.letter             = 'r',
		.name               = "roles",
		.description        = "Roles",
		.enabled            = false,
		.render             = renderFieldRoles,
		.renderNoEscaping   = NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable   = NULL,
		.dataType           = FIELDTYPE_STRING,
		.getterValueType    = "[ role1:name ... rolen:name ]",
		.getValueObject     = getFieldValueForRoles,
		.setterValueType    = NULL,
		.checkValueForSetter= NULL,
		.setValueObject     = NULL,
	},
	[FIELD_EXTRAS - FIELDS_UCTAGS_START] = {
		.letter				= 'E',
		.name				= "extras",
		.description		= "Extra tag type information",
		.enabled			= false,
		.render				= renderFieldExtras,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= isExtrasFieldAvailable,
		.dataType			= FIELDTYPE_STRING,
		.getterValueType    = "[ extra1:name ... extran:name ]",
		.getValueObject     = getFieldValueForExtras,
		.setterValueType    = NULL,
		.checkValueForSetter= NULL,
		.setValueObject     = NULL,
	},
	[FIELD_XPATH - FIELDS_UCTAGS_START] = {
		.letter				= 'x',
		.name				= "xpath",
		.description		= "xpath for the tag",
		.enabled			= false,
		.render				= renderFieldXpath,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= isXpathFieldAvailable,
		.dataType			= FIELDTYPE_STRING,
	},
	[FIELD_END_LINE - FIELDS_UCTAGS_START] = {
		.letter				= 'e',
		.name				= "end",
		.description		= "end lines of various items",
		.enabled			= false,
		.render				= renderFieldEnd,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= isEndFieldAvailable,
		.dataType			= FIELDTYPE_INTEGER,
		.getterValueType    = "int",
		.getValueObject     = getFieldValueForLineCommon,
		.setterValueType    = "matchloc|int",
		.checkValueForSetter= checkFieldValueForLineCommon,
		.setValueObject     = setFieldValueForLineCommon,

	},
	[FIELD_EPOCH - FIELDS_UCTAGS_START] = {
		.letter				= 'T',
		.name				= "epoch",
		.description		= "the last modified time of the input file (only for F/file kind tag)",
		.enabled			= true,
		.render				= renderFieldEpoch,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= isEpochAvailable,
		.dataType			= FIELDTYPE_INTEGER,
	},
	[FIELD_NTH - FIELDS_UCTAGS_START] = {
		.letter				= 'o',
		.name				= "nth",
		.description		= "the order in the parent scope",
		.enabled			= false,
		.render				= renderFieldNth,
		.renderNoEscaping	= NULL,
		.doesContainAnyChar = NULL,
		.isValueAvailable	= isNthAvailable,
		.dataType			= FIELDTYPE_INTEGER,
	},
};


static unsigned int       fieldObjectUsed = 0;
static unsigned int       fieldObjectAllocated = 0;
static fieldObject* fieldObjects = NULL;

extern void initFieldObjects (void)
{
	unsigned int i;
	fieldObject *fobj;

	Assert (fieldObjects == NULL);

	fieldObjectAllocated
	  = ARRAY_SIZE (fieldDefinitionsFixed)
	  + ARRAY_SIZE (fieldDefinitionsExuberant)
	  + ARRAY_SIZE (fieldDefinitionsUniversal);
	fieldObjects = xMalloc (fieldObjectAllocated, fieldObject);
	DEFAULT_TRASH_BOX(&fieldObjects, eFreeIndirect);

	fieldObjectUsed = 0;

	for (i = 0; i < ARRAY_SIZE (fieldDefinitionsFixed); i++)
	{
		fobj = fieldObjects + i + fieldObjectUsed;
		fobj->def = fieldDefinitionsFixed + i;
		fobj->buffer = NULL;
		fobj->nameWithPrefix = fobj->def->name;
		fobj->language = LANG_IGNORE;
		fobj->sibling  = FIELD_UNKNOWN;
		fobj->def->ftype = i + fieldObjectUsed;
	}
	fieldObjectUsed += ARRAY_SIZE (fieldDefinitionsFixed);

	for (i = 0; i < ARRAY_SIZE (fieldDefinitionsExuberant); i++)
	{
		fobj = fieldObjects + i + fieldObjectUsed;
		fobj->def = fieldDefinitionsExuberant +i;
		fobj->buffer = NULL;
		fobj->nameWithPrefix = fobj->def->name;
		fobj->language = LANG_IGNORE;
		fobj->sibling  = FIELD_UNKNOWN;
		fobj->def->ftype = i + fieldObjectUsed;
	}
	fieldObjectUsed += ARRAY_SIZE (fieldDefinitionsExuberant);

	for (i = 0; i < ARRAY_SIZE (fieldDefinitionsUniversal); i++)
	{
		char *nameWithPrefix;

		fobj = fieldObjects + i + fieldObjectUsed;
		fobj->def = fieldDefinitionsUniversal + i;
		fobj->buffer = NULL;

		if (fobj->def->name)
		{
			nameWithPrefix = eMalloc (sizeof CTAGS_FIELD_PREFIX + strlen (fobj->def->name) + 1);
			nameWithPrefix [0] = '\0';
			strcat (nameWithPrefix, CTAGS_FIELD_PREFIX);
			strcat (nameWithPrefix, fobj->def->name);
			fobj->nameWithPrefix = nameWithPrefix;
			DEFAULT_TRASH_BOX(nameWithPrefix, eFree);
		}
		else
			fobj->nameWithPrefix = NULL;
		fobj->language = LANG_IGNORE;
		fobj->sibling  = FIELD_UNKNOWN;
		fobj->def->ftype = i + fieldObjectUsed;
	}
	fieldObjectUsed += ARRAY_SIZE (fieldDefinitionsUniversal);

	Assert ( fieldObjectAllocated == fieldObjectUsed );
}

static fieldObject* getFieldObject(fieldType type)
{
	Assert ((0 <= type) && ((unsigned int)type < fieldObjectUsed));
	return fieldObjects + type;
}

extern fieldType getFieldTypeForOption (char letter)
{
	unsigned int i;

	for (i = 0; i < fieldObjectUsed; i++)
	{
		if (fieldObjects [i].def->letter == letter)
			return i;
	}
	return FIELD_UNKNOWN;
}

extern fieldType getFieldTypeForName (const char *name)
{
	return getFieldTypeForNameAndLanguage (name, LANG_IGNORE);
}

extern fieldType getFieldTypeForNameAndLanguage (const char *fieldName, langType language)
{
	static bool initialized = false;
	unsigned int i;

	if (fieldName == NULL)
		return FIELD_UNKNOWN;

	if (language == LANG_AUTO && (initialized == false))
	{
		initialized = true;
		initializeParser (LANG_AUTO);
	}
	else if (language != LANG_IGNORE && (initialized == false))
		initializeParser (language);

	for (i = 0; i < fieldObjectUsed; i++)
	{
		if (fieldObjects [i].def->name
		    && strcmp (fieldObjects [i].def->name, fieldName) == 0
		    && ((language == LANG_AUTO)
			|| (fieldObjects [i].language == language)))
			return i;
	}

	return FIELD_UNKNOWN;
}

extern const char* getFieldDescription (fieldType type)
{
	fieldObject* fobj;

	fobj = getFieldObject (type);
	return fobj->def->description;
}

extern const char* getFieldName(fieldType type)
{
	fieldObject* fobj;

	fobj = getFieldObject (type);
	if (Option.putFieldPrefix)
		return fobj->nameWithPrefix;
	else
		return fobj->def->name;
}

extern unsigned char getFieldLetter (fieldType type)
{
	fieldObject* fobj = getFieldObject (type);

	return fobj->def->letter == '\0'
		? FIELD_NULL_LETTER_CHAR
		: fobj->def->letter;
}

extern bool doesFieldHaveValue (fieldType type, const tagEntryInfo *tag)
{
	if (getFieldObject(type)->def->isValueAvailable)
		return getFieldObject(type)->def->isValueAvailable(tag);
	else
		return true;
}

static const char *renderAsIs (vString* b CTAGS_ATTR_UNUSED, const char *s)
{
	return s;
}

static const char *renderEscapedString (const char *s,
					const tagEntryInfo *const tag CTAGS_ATTR_UNUSED,
					vString* b)
{
	vStringCatSWithEscaping (b, s);
	return vStringValue (b);
}

static const char *renderEscapedName (const bool isTagName,
				      const char* s,
				      const tagEntryInfo *const tag,
				      vString* b)
{
	int unexpected_byte = 0;

	if (isTagName && (!tag->isPseudoTag) &&  (*s == ' ' || *s == '!'))
	{
		/* Don't allow a leading space or exclamation mark as it conflicts with
		 * pseudo-tags when sorting.  Anything with a lower byte value is
		 * escaped by renderEscapedString() already. */
		unexpected_byte = *s;
		switch (*s)
		{
			case ' ': vStringCatS (b, "\\x20"); s++; break;
			case '!': vStringCatS (b, "\\x21"); s++; break;
			default: AssertNotReached();
		}
	}
	else
	{
		/* Find the first byte needing escaping for the warning message */
		const char *p = s;

		while (*p > 0x1F && *p != 0x7F)
			p++;
		unexpected_byte = *p;
	}

	if (unexpected_byte)
	{
		const kindDefinition *kdef = getTagKind (tag);
		verbose ("Unexpected character %#04x included in a tagEntryInfo: %s\n", unexpected_byte, s);
		verbose ("File: %s, Line: %lu, Lang: %s, Kind: %c\n",
			 tag->inputFileName, tag->lineNumber, getLanguageName(tag->langType), kdef->letter);
		verbose ("Escape the character\n");
	}

	return renderEscapedString (s, tag, b);
}

static const char *renderFieldName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	return renderEscapedName (true, tag->name, tag, b);
}

static const char *renderFieldNameNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	return renderAsIs (b, tag->name);
}

static bool doesContainAnyCharInName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, const char *chars)
{
	return strpbrk (tag->name, chars)? true: false;
}

static const char *renderFieldInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	const char *f = tag->inputFileName;

	if (Option.lineDirectives && tag->sourceFileName)
		f = tag->sourceFileName;
	return renderEscapedString (f, tag, b);
}

static const char *renderFieldInputNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	const char *f = tag->inputFileName;

	if (Option.lineDirectives && tag->sourceFileName)
		f = tag->sourceFileName;

	return renderAsIs (b, f);
}

static bool doesContainAnyCharInInput (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, const char *chars)
{
	const char *f = tag->inputFileName;

	if (Option.lineDirectives && tag->sourceFileName)
		f = tag->sourceFileName;

	return strpbrk (f, chars)? true: false;
}

static const char *renderFieldSignature (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	return renderEscapedString (WITH_DEFUALT_VALUE (tag->extensionFields.signature),
				    tag, b);
}

static const char *renderFieldSignatureNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.signature));
}

static bool doesContainAnyCharInSignature (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, const char *chars)
{
	return (tag->extensionFields.signature && strpbrk(tag->extensionFields.signature, chars))
		? true
		: false;
}

static const char *renderFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	const char* scope;

	getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope);
	return scope? renderEscapedName (false, scope, tag, b): NULL;
}

static const char *renderFieldScopeNoEscape (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	const char* scope;

	getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope);
	return scope? renderAsIs (b, scope): NULL;
}

static bool doesContainAnyCharInFieldScope (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, const char *chars)
{
	const char* scope;

	getTagScopeInformation ((tagEntryInfo *const)tag, NULL, &scope);
	return (scope && strpbrk (scope, chars));
}


static const char *renderFieldInherits (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	return renderEscapedString (WITH_DEFUALT_VALUE (tag->extensionFields.inheritance),
				    tag, b);
}

static const char *renderFieldTyperef (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	/* Return "-" instead of "-:-". */
	if (tag->extensionFields.typeRef [0] == NULL
		&& tag->extensionFields.typeRef [1] == NULL)
		return renderAsIs (b, FIELD_NULL_LETTER_STRING);

	vStringCatS (b, WITH_DEFUALT_VALUE (tag->extensionFields.typeRef [0]));
	vStringPut  (b, ':');
	return renderEscapedName (false, WITH_DEFUALT_VALUE (tag->extensionFields.typeRef [1]), tag, b);
}


static const char* renderFieldCommon (fieldType type,
									  const tagEntryInfo *tag,
									  int index,
									  bool noEscaping)
{
	fieldObject *fobj = fieldObjects + type;
	const char *value;
	fieldRenderer rfn;

	Assert (tag);
	Assert (index < 0 || ((unsigned int)index) < tag->usedParserFields);

	if (index >= 0)
	{
		const tagField *f = getParserFieldForIndex (tag, index);

		value = f->value;
	}
	else
		value = NULL;

	if (noEscaping)
		rfn = fobj->def->renderNoEscaping;
	else
		rfn = fobj->def->render;
	Assert (rfn);

	fobj->buffer = vStringNewOrClearWithAutoRelease (fobj->buffer);
	return rfn (tag, value, fobj->buffer);
}

extern const char* renderField (fieldType type, const tagEntryInfo *tag, int index)
{
	return renderFieldCommon (type, tag, index, false);
}

extern const char* renderFieldNoEscaping (fieldType type, const tagEntryInfo *tag, int index)
{
	return renderFieldCommon (type, tag, index, true);
}

static bool defaultDoesContainAnyChar (const tagEntryInfo *const tag CTAGS_ATTR_UNUSED, const char* value, const char* chars)
{
	return strpbrk (value, chars)? true: false;
}

extern bool  doesFieldHaveTabOrNewlineChar (fieldType type, const tagEntryInfo *tag, int index)
{
	fieldObject *fobj = fieldObjects + type;
	const char *value;
	bool (* doesContainAnyChar) (const tagEntryInfo *const, const char*, const char*) = fobj->def->doesContainAnyChar;

	Assert (tag);
	Assert (index == NO_PARSER_FIELD || ((unsigned int)index) < tag->usedParserFields);

	if (doesContainAnyChar == NULL)
	{
		if (index == NO_PARSER_FIELD)
			return false;
		else
			doesContainAnyChar = defaultDoesContainAnyChar;
	}

	if (index >= 0)
	{
		const tagField *f = getParserFieldForIndex (tag, index);

		value = f->value;
	}
	else
		value = NULL;

	return (* doesContainAnyChar) (tag, value, "\t\n");
}

/*  Writes "line", stripping leading and duplicate white space.
 */
static const char* renderCompactInputLine (vString *b,  const char *const line)
{
	bool lineStarted = false;
	const char *p;
	int c;

	/*  Write everything up to, but not including, the newline.
	 */
	for (p = line, c = *p  ;  c != NEWLINE  &&  c != '\0'  ;  c = *++p)
	{
		if (lineStarted  || ! isspace (c))  /* ignore leading spaces */
		{
			lineStarted = true;
			if (isspace (c))
			{
				int next;

				/*  Consume repeating white space.
				 */
				while (next = *(p+1) , isspace (next)  &&  next != NEWLINE)
					++p;
				c = ' ';  /* force space character for any white space */
			}
			if (c != CRETURN  ||  *(p + 1) != NEWLINE)
				vStringPut (b, c);
		}
	}
	return vStringValue (b);
}

static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b)
{
	const char* name = getTagKindName (tag);
	return renderAsIs (b, name);
}

static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag,
						const char *value CTAGS_ATTR_UNUSED,
						 vString* b)
{
	const char *line;
	static vString *tmp;

	if (tag->isPseudoTag)
	{
		Assert (tag->pattern);
		return tag->pattern;
	}

	tmp = vStringNewOrClearWithAutoRelease (tmp);

	line = readLineFromBypassForTag (tmp, tag, NULL);
	if (line)
		renderCompactInputLine (b, line);
	else
	{
		/* If no associated line for tag is found, we cannot prepare
		 * parameter to writeCompactInputLine(). In this case we
		 * use an empty string as LINE.
		 */
		vStringClear (b);
	}

	return vStringValue (b);
}

static const char *renderFieldLineNumber (const tagEntryInfo *const tag,
					  const char *value CTAGS_ATTR_UNUSED,
					  vString* b)
{
	long ln = tag->lineNumber;
	char buf[32] = {[0] = '\0'};

	if (Option.lineDirectives && (tag->sourceLineNumberDifference != 0))
		ln += tag->sourceLineNumberDifference;
	snprintf (buf, sizeof(buf), "%ld", ln);
	vStringCatS (b, buf);
	return vStringValue (b);
}

struct renderRoleData {
	vString* b;
	int nRoleWritten;
};

static void renderRoleByIndex (const tagEntryInfo *const tag, int roleIndex, void *data)
{
	struct renderRoleData *rdata = data;

	if (!isLanguageRoleEnabled (tag->langType, tag->kindIndex, roleIndex))
		return;

	if (rdata->nRoleWritten > 0)
		vStringPut(rdata->b, ',');

	const roleDefinition * role = getTagRole(tag, roleIndex);
	renderRole (role, rdata->b);
	rdata->nRoleWritten++;
}

static roleBitsType foreachRoleBits (const tagEntryInfo *const tag,
									 void (* fn) (const tagEntryInfo *const, int, void *),
									 void *data)
{
	roleBitsType rbits = tag->extensionFields.roleBits;
	if (!rbits)
		return rbits;

	int roleCount = countLanguageRoles (tag->langType, tag->kindIndex);

	for (int roleIndex = 0; roleIndex < roleCount; roleIndex++)
	{
		if ((rbits >> roleIndex) & (roleBitsType)1)
			fn (tag, roleIndex, data);
	}
	return rbits;
}

static const char *renderFieldRoles (const tagEntryInfo *const tag,
				    const char *value CTAGS_ATTR_UNUSED,
				    vString* b)
{
	struct renderRoleData data = { .b = b, .nRoleWritten = 0 };

	if (!foreachRoleBits (tag, renderRoleByIndex, &data))
		vStringCatS (b, ROLE_DEFINITION_NAME);
	return vStringValue (b);
}

static const char *renderFieldLanguage (const tagEntryInfo *const tag,
					const char *value CTAGS_ATTR_UNUSED,
					vString* b)
{
	const char *l;

	if (Option.lineDirectives && (tag->sourceLangType != LANG_IGNORE))
		l = getLanguageName(tag->sourceLangType);
	else
	{
		Assert (tag->langType != LANG_IGNORE);
		l = getLanguageName(tag->langType);
	}

	return renderAsIs (b, WITH_DEFUALT_VALUE(l));
}

static const char *renderFieldAccess (const tagEntryInfo *const tag,
				      const char *value CTAGS_ATTR_UNUSED,
				      vString* b)
{
	return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.access));
}

static const char *renderFieldKindLetter (const tagEntryInfo *const tag,
					  const char *value CTAGS_ATTR_UNUSED,
					  vString* b)
{
	static char c[2] = { [1] = '\0' };

	c [0] = getTagKindLetter(tag);

	return renderAsIs (b, c);
}

static const char *renderFieldImplementation (const tagEntryInfo *const tag,
					      const char *value CTAGS_ATTR_UNUSED,
					      vString* b)
{
	return renderAsIs (b, WITH_DEFUALT_VALUE (tag->extensionFields.implementation));
}

static const char *renderFieldFile (const tagEntryInfo *const tag,
				    const char *value CTAGS_ATTR_UNUSED,
				    vString* b)
{
	return renderAsIs (b, tag->isFileScope? "file": FIELD_NULL_LETTER_STRING);
}

static const char *renderFieldPattern (const tagEntryInfo *const tag,
				       const char *value CTAGS_ATTR_UNUSED,
				       vString* b)
{
	if (tag->isFileEntry)
		return NULL;
	else if (tag->pattern)
		vStringCatS (b, tag->pattern);
	else
	{
		char* tmp;

		tmp = makePatternString (tag);
		vStringCatS (b, tmp);
		eFree (tmp);
	}
	return vStringValue (b);
}

static const char *renderFieldRefMarker (const tagEntryInfo *const tag,
					 const char *value CTAGS_ATTR_UNUSED,
					 vString* b)
{
	static char c[2] = { [1] = '\0' };

	c [0] = (tag->extensionFields.roleBits)? 'R': 'D';

	return renderAsIs (b, c);
}

static const char *renderFieldExtras (const tagEntryInfo *const tag,
				     const char *value CTAGS_ATTR_UNUSED,
				     vString* b)
{
	int i;
	bool hasExtra = false;
	int c = countXtags();

	for (i = 0; i < c; i++)
	{
		const char *name = getXtagName (i);

		if (!name)
			continue;

		if (isTagExtraBitMarked (tag, i))
		{

			if (hasExtra)
				vStringPut (b, ',');
			vStringCatS (b, name);
			hasExtra = true;
		}
	}

	if (hasExtra)
		return vStringValue (b);
	else
		return NULL;
}

static const char *renderFieldXpath (const tagEntryInfo *const tag,
				     const char *value CTAGS_ATTR_UNUSED,
				     vString* b)
{
#ifdef HAVE_LIBXML
	if (tag->extensionFields.xpath)
		return renderEscapedString (tag->extensionFields.xpath,
					    tag, b);
#endif
	return NULL;
}

static const char *renderFieldScopeKindName(const tagEntryInfo *const tag,
					    const char *value CTAGS_ATTR_UNUSED,
					    vString* b)
{
	const char* kind;

	getTagScopeInformation ((tagEntryInfo *const)tag, &kind, NULL);
	return kind? renderAsIs (b, kind): NULL;
}

static const char *renderFieldEnd (const tagEntryInfo *const tag,
				   const char *value CTAGS_ATTR_UNUSED,
				   vString* b)
{
	static char buf[21];

	if (tag->extensionFields.endLine != 0)
	{
		sprintf (buf, "%lu", tag->extensionFields.endLine);
		return renderAsIs (b, buf);
	}
	else
		return NULL;
}

static const char *renderFieldEpoch (const tagEntryInfo *const tag,
									  const char *value, vString* b)
{
#define buf_len 21
	static char buf[buf_len];

	if (snprintf (buf, buf_len, "%lld", (long long)tag->extensionFields.epoch) > 0)
		return renderAsIs (b, buf);
	else
		return NULL;
#undef buf_len
}

static const char *renderFieldNth (const tagEntryInfo *const tag,
								   const char *value, vString* b)
{
#define buf_len 12
	static char buf[buf_len];

	if (tag->extensionFields.nth > NO_NTH_FIELD
		&& snprintf (buf, buf_len, "%d", (int)tag->extensionFields.nth) > 0)
		return renderAsIs (b, buf);
	else
		return NULL;
#undef buf_len
}

static bool     isTyperefFieldAvailable  (const tagEntryInfo *const tag)
{
	return (tag->extensionFields.typeRef [0] != NULL
		&& tag->extensionFields.typeRef [1] != NULL)? true: false;
}

static bool     isFileFieldAvailable  (const tagEntryInfo *const tag)
{
	return tag->isFileScope? true: false;
}

static bool     isInheritsFieldAvailable (const tagEntryInfo *const tag)
{
	return (tag->extensionFields.inheritance != NULL)? true: false;
}

static bool     isAccessFieldAvailable   (const tagEntryInfo *const tag)
{
	return (tag->extensionFields.access != NULL)? true: false;
}

static bool     isImplementationFieldAvailable (const tagEntryInfo *const tag)
{
	return (tag->extensionFields.implementation != NULL)? true: false;
}

static bool     isSignatureFieldAvailable (const tagEntryInfo *const tag)
{
	return (tag->extensionFields.signature != NULL)? true: false;
}

static bool     isExtrasFieldAvailable     (const tagEntryInfo *const tag)
{
	unsigned int i;

	if (tag->extraDynamic)
		return true;
	for (i = 0; i < sizeof (tag->extra); i++)
		if (tag->extra [i])
			return true;

	return false;
}

static bool     isXpathFieldAvailable      (const tagEntryInfo *const tag)
{
#ifdef HAVE_LIBXML
	return (tag->extensionFields.xpath != NULL)? true: false;
#else
	return false;
#endif
}

static bool     isEndFieldAvailable       (const tagEntryInfo *const tag)
{
	return (tag->extensionFields.endLine != 0)? true: false;
}

static bool isEpochAvailable (const tagEntryInfo *const tag)
{
	return (tag->kindIndex == KIND_FILE_INDEX)
		? true
		: false;
}

static bool isNthAvailable (const tagEntryInfo *const tag)
{
	Assert (tag->langType >= NO_NTH_FIELD);
	return (tag->extensionFields.nth != NO_NTH_FIELD)? true: false;
}

extern bool isFieldEnabled (fieldType type)
{
	return getFieldObject(type)->def->enabled;
}

extern bool enableField (fieldType type, bool state)
{
	fieldDefinition *def = getFieldObject(type)->def;
	bool old = def->enabled;
	getFieldObject(type)->def->enabled = state;

	if (isCommonField (type))
		verbose ("enable field \"%s\": %s\n",
				 getFieldObject(type)->def->name,
				 (state? "yes": "no"));
	else
		verbose ("enable field \"%s\"<%s>: %s\n",
				 getFieldObject(type)->def->name,
				 getLanguageName (getFieldOwner(type)),
				 (state? "yes": "no"));
	return old;
}

extern bool isCommonField (fieldType type)
{
	return (FIELD_BUILTIN_LAST < type)? false: true;
}

extern int     getFieldOwner (fieldType type)
{
	return getFieldObject(type)->language;
}

extern unsigned int getFieldDataType (fieldType type)
{
	return getFieldObject(type)->def->dataType;
}

extern bool isFieldValueAvailableAlways (fieldType type)
{
	return getFieldObject(type)->def->isValueAvailable == NULL;
}

extern bool doesFieldHaveRenderer (fieldType type, bool noEscaping)
{
	if (noEscaping)
		return getFieldObject(type)->def->renderNoEscaping? true: false;
	else
		return getFieldObject(type)->def->render? true: false;
}

extern int countFields (void)
{
	return fieldObjectUsed;
}

extern fieldType nextSiblingField (fieldType type)
{
	fieldObject *fobj;

	fobj = fieldObjects + type;
	return fobj->sibling;
}

static void updateSiblingField (fieldType type, const char* name)
{
	int i;
	fieldObject *fobj;

	for (i = type; i > 0; i--)
	{
		fobj = fieldObjects + i - 1;
		if (fobj->def->name && (strcmp (fobj->def->name, name) == 0))
		{
			Assert (fobj->sibling == FIELD_UNKNOWN);
			fobj->sibling = type;
			break;
		}
	}
}

static const char* defaultRenderer (const tagEntryInfo *const tag CTAGS_ATTR_UNUSED,
				    const char *value,
				    vString * buffer CTAGS_ATTR_UNUSED)
{
	return renderEscapedString (value, tag, buffer);
}

extern int defineField (fieldDefinition *def, langType language)
{
	fieldObject *fobj;
	char *nameWithPrefix;
	size_t i;

	Assert (def);
	Assert (def->name);
	for (i = 0; i < strlen (def->name); i++)
	{
		Assert ( isalpha (def->name [i]) );
	}
	def->letter = NUL_FIELD_LETTER;

	if (fieldObjectUsed == fieldObjectAllocated)
	{
		fieldObjectAllocated *= 2;
		fieldObjects = xRealloc (fieldObjects, fieldObjectAllocated, fieldObject);
	}
	fobj = fieldObjects + (fieldObjectUsed);
	def->ftype = fieldObjectUsed++;

	if (def->render == NULL)
	{
		def->render = defaultRenderer;
		def->renderNoEscaping = NULL;
		def->doesContainAnyChar = NULL;
	}

	if (! def->dataType)
		def->dataType = FIELDTYPE_STRING;

	fobj->def = def;

	fobj->buffer = NULL;

	nameWithPrefix = eMalloc (sizeof CTAGS_FIELD_PREFIX + strlen (def->name) + 1);
	nameWithPrefix [0] = '\0';
	strcat (nameWithPrefix, CTAGS_FIELD_PREFIX);
	strcat (nameWithPrefix, def->name);
	fobj->nameWithPrefix = nameWithPrefix;
	DEFAULT_TRASH_BOX(nameWithPrefix, eFree);

	fobj->language = language;
	fobj->sibling  = FIELD_UNKNOWN;

	updateSiblingField (def->ftype, def->name);
	return def->ftype;
}

#define FIELD_COL_LETTER      0
#define FIELD_COL_NAME        1
#define FIELD_COL_ENABLED     2
#define FIELD_COL_LANGUAGE    3
#define FIELD_COL_JSTYPE      4
#define FIELD_COL_FIXED       5
#define FIELD_COL_OPERATOR    6
#define FIELD_COL_DESCRIPTION 7

extern struct colprintTable * fieldColprintTableNew (void)
{
	return colprintTableNew ("L:LETTER", "L:NAME", "L:ENABLED",
							 "L:LANGUAGE", "L:JSTYPE", "L:FIXED",
							 "L:OP", "L:DESCRIPTION", NULL);
}

static void  fieldColprintAddLine (struct colprintTable *table, int i)
{
	fieldObject *fobj = getFieldObject(i);
	fieldDefinition *fdef = fobj->def;

	struct colprintLine *line = colprintTableGetNewLine(table);

	colprintLineAppendColumnChar (line,
								  (fdef->letter == NUL_FIELD_LETTER)
								  ? FIELD_NULL_LETTER_CHAR
								  : fdef->letter);

	const char *name = getFieldName (i);
	colprintLineAppendColumnCString (line, name? name: RSV_NONE);
	colprintLineAppendColumnBool (line, fdef->enabled);
	colprintLineAppendColumnCString (line,
									 fobj->language == LANG_IGNORE
									 ? RSV_NONE
									 : getLanguageName (fobj->language));

	char  typefields [] = "---";
	{
		unsigned int bmask, offset;
		unsigned int type = getFieldDataType(i);
		for (bmask = 1, offset = 0;
			 bmask < FIELDTYPE_END_MARKER;
			 bmask <<= 1, offset++)
			if (type & bmask)
				typefields[offset] = fieldDataTypeFalgs[offset];
	}
	colprintLineAppendColumnCString (line, typefields);
	colprintLineAppendColumnBool (line, writerDoesTreatFieldAsFixed (i));

	char operator[] = {'-', '-', '\0'};
	if (fdef->getValueObject)
		operator[0] = 'r';
	if (fdef->setValueObject)
		operator[1] = 'w';
	colprintLineAppendColumnCString (line, operator);
	colprintLineAppendColumnCString (line, fdef->description);
}

extern void fieldColprintAddCommonLines (struct colprintTable *table)
{
	for (int i = 0; i <= FIELD_BUILTIN_LAST; i++)
		fieldColprintAddLine(table, i);
}

extern void fieldColprintAddLanguageLines (struct colprintTable *table, langType language)
{
	for (unsigned int i = FIELD_BUILTIN_LAST + 1; i < fieldObjectUsed; i++)
	{
		fieldObject *fobj = getFieldObject(i);
		if (fobj->language == language)
			fieldColprintAddLine (table, i);
	}
}

static int fieldColprintCompareLines (struct colprintLine *a , struct colprintLine *b)
{
	const char *a_fixed  = colprintLineGetColumn (a, FIELD_COL_FIXED);
	const char *b_fixed  = colprintLineGetColumn (b, FIELD_COL_FIXED);
	const char *a_parser = colprintLineGetColumn (a, FIELD_COL_LANGUAGE);
	const char *b_parser = colprintLineGetColumn (b, FIELD_COL_LANGUAGE);

	if ((strcmp (a_fixed, "yes") == 0)
		&& (strcmp (b_fixed, "yes") == 0))
	{
		/* name, input, pattern, compact */
		const char *a_name  = colprintLineGetColumn (a, FIELD_COL_NAME);
		const char *b_name  = colprintLineGetColumn (b, FIELD_COL_NAME);
		const char *ref_name;
		unsigned int a_index = ~0U;
		unsigned int b_index = ~0U;

		for (unsigned int i = 0; i < ARRAY_SIZE(fieldDefinitionsFixed); i++)
		{
			ref_name = fieldDefinitionsFixed [i].name;
			if (strcmp (a_name, ref_name) == 0)
				a_index = i;
			if (strcmp (b_name, ref_name) == 0)
				b_index = i;
			if ((a_index != ~0U) || (b_index != ~0U))
				break;
		}

		if (a_index < b_index)
			return -1;
		else if (a_index == b_index)
			return 0;			/* ??? */
		else
			return 1;
	}
	else if ((strcmp (a_fixed, "yes") == 0)
			  && (strcmp (b_fixed, "yes") != 0))
		return -1;
	else if ((strcmp (a_fixed, "yes") != 0)
			 && (strcmp (b_fixed, "yes") == 0))
		return 1;

	if (strcmp (a_parser, RSV_NONE) == 0
		&& strcmp (b_parser, RSV_NONE) != 0)
		return -1;
	else if (strcmp (a_parser, RSV_NONE) != 0
			 && strcmp (b_parser, RSV_NONE) == 0)
		return 1;
	else if (strcmp (a_parser, RSV_NONE) != 0
			 && strcmp (b_parser, RSV_NONE) != 0)
	{
		int r;
		r = strcmp (a_parser, b_parser);
		if (r != 0)
			return r;

		const char *a_name = colprintLineGetColumn (a, FIELD_COL_NAME);
		const char *b_name = colprintLineGetColumn (b, FIELD_COL_NAME);

		return strcmp(a_name, b_name);
	}
	else
	{
		const char *a_letter = colprintLineGetColumn (a, FIELD_COL_LETTER);
		const char *b_letter = colprintLineGetColumn (b, FIELD_COL_LETTER);

		return strcmp(a_letter, b_letter);
	}
}

extern void fieldColprintTablePrint (struct colprintTable *table,
									 bool withListHeader, bool machinable, FILE *fp)
{
	colprintTableSort (table, fieldColprintCompareLines);
	colprintTablePrint (table, 0, withListHeader, machinable, fp);
}

extern const char * getFieldGetterValueType (fieldType type)
{
	fieldObject *fobj = getFieldObject (type);
	return (fobj? fobj->def->getterValueType: NULL);
}

extern EsObject * getFieldValue (fieldType type, const tagEntryInfo *tag)
{
	fieldObject* fobj;

	fobj = getFieldObject (type);
	if (fobj && fobj->def->getValueObject)
		return fobj->def->getValueObject (tag, fobj->def);
	return es_nil;
}

extern bool hasFieldGetter (fieldType type)
{
	fieldObject *fobj = getFieldObject (type);
	return (fobj && fobj->def->getValueObject);
}

extern const char * getFieldSetterValueType (fieldType type)
{
	fieldObject *fobj = getFieldObject (type);
	return (fobj? fobj->def->setterValueType: NULL);
}

extern EsObject * setFieldValue (fieldType type, tagEntryInfo *tag, const EsObject *val)
{
	fieldObject *fobj;

	fobj = getFieldObject (type);
	if (fobj && fobj->def->setValueObject)
		return fobj->def->setValueObject (tag, fobj->def, val);
	return es_false;
}

extern bool hasFieldSetter (fieldType type)
{
	fieldObject *fobj = getFieldObject (type);
	return (fobj && fobj->def->setValueObject);
}


extern bool hasFieldValueCheckerForSetter (fieldType type)
{
	fieldObject *fobj = getFieldObject (type);
	return (fobj && fobj->def->checkValueForSetter);
}

extern EsObject* checkFieldValueForSetter (fieldType type, const EsObject *val)
{
	fieldObject *fobj = getFieldObject (type);
	return fobj->def->checkValueForSetter (fobj->def, val);
}

static EsObject* getFieldValueForName (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	return opt_string_new_from_cstr (tag->name);
}

static EsObject* setFieldValueForName (tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *val)
{
	eFree ((char*) tag->name);
	const char *cstr = opt_string_get_cstr (val);
	tag->name = eStrdup (cstr);
	return es_false;
}

static EsObject* getFieldValueForInput (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	return opt_string_new_from_cstr (tag->inputFileName);
}

static EsObject* getFieldValueForKind (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	const char *kind_name = getLanguageKindName (tag->langType, tag->kindIndex);
	return opt_name_new_from_cstr (kind_name);
}

static EsObject* getFieldValueForTyperef (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	if (tag->extensionFields.typeRef [0] == NULL
		&& tag->extensionFields.typeRef [1] == NULL)
		return es_nil;

	EsObject *a = opt_array_new ();
	EsObject *e0 = tag->extensionFields.typeRef [0]
		? opt_string_new_from_cstr(tag->extensionFields.typeRef [0])
		: es_false;
	EsObject *e1 = tag->extensionFields.typeRef [1]
		? opt_string_new_from_cstr(tag->extensionFields.typeRef [1])
		: es_false;
	opt_array_put (a, 0, e0);
	opt_array_put (a, 1, e1);
	es_object_unref (e0);
	es_object_unref (e1);
	return a;
}

static EsObject* setFieldValueForTyperef (tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *obj)
{
	const char *tmp[2] = {NULL, NULL};

	for (int i = 0 ; i < 2; i++)
	{
		if (tag->extensionFields.typeRef [i])
			tmp [i] = tag->extensionFields.typeRef [i];
	}

	if (es_boolean_p (obj))
	{
		for (int i = 0 ; i < 2; i++)
		{
			if (tag->extensionFields.typeRef [i])
				tag->extensionFields.typeRef [i] = NULL;
		}
	}
	else if (es_object_get_type (obj) == OPT_TYPE_ARRAY)
	{
		for (int i = 0 ; i < 2; i++)
		{
			EsObject *e = opt_array_get (obj, i);
			if (es_boolean_p (e))
			{
				if (tag->extensionFields.typeRef [i])
					tag->extensionFields.typeRef [i] = NULL;
			}
			else if (es_object_get_type (e) == OPT_TYPE_STRING)
			{
				tag->extensionFields.typeRef [i] = eStrdup (opt_string_get_cstr (e));
			}
		}
	}
	else if (es_object_get_type (obj) == OPT_TYPE_STRING)
	{
		const char *str = opt_string_get_cstr (obj);
		tag->extensionFields.typeRef [0] = eStrdup ("typename");
		tag->extensionFields.typeRef [1] = eStrdup (str);
	}
	else if (es_integer_p (obj))
	{
		int index = es_integer_get (obj);
		tagEntryInfo *e = getEntryInCorkQueue (index);
		if (e)
		{
			const char *name = e->name;
			const char *kindName = getLanguageKindName (e->langType, e->kindIndex);

			tag->extensionFields.typeRef [0] = eStrdup (kindName);
			tag->extensionFields.typeRef [1] = eStrdup (name);
		}
	}
	else
	{
		AssertNotReached();
		return OPT_ERR_TYPECHECK;
	}

	for (int i = 0; i < 2; i++)
		if (tmp [i])
			eFree ((char*)tmp[i]);

	return es_false;
}

static EsObject* checkFieldValueForTyperef (const fieldDefinition *fdef, const EsObject *obj)
{
	if (es_boolean_p (obj))
	{
		if (!es_object_equal (es_false, obj))
			return OPT_ERR_TYPECHECK;
	}
	else if (es_object_get_type (obj) == OPT_TYPE_ARRAY)
	{
		if (opt_array_length (obj) != 2)
			return OPT_ERR_TYPECHECK;

		for (unsigned int i = 0; i < 2; i++)
		{
			EsObject *e = opt_array_get (obj, i);
			if (es_object_get_type (e) != OPT_TYPE_STRING)
				return OPT_ERR_TYPECHECK;
		}
	}
	else if (es_object_get_type (obj) == OPT_TYPE_STRING)
		;
	else if (es_integer_p (obj))
	{
		int index = es_integer_get (obj);
		if (index >= countEntryInCorkQueue ())
			return OPTSCRIPT_ERR_NOTAGENTRY;
	}
	else
		return OPT_ERR_TYPECHECK;
	return es_false;
}

static EsObject* getFieldValueForScope (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	return es_integer_new (tag->extensionFields.scopeIndex);
}

static EsObject* setFieldValueForScope (tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *obj)
{
	int index = es_integer_get (obj);
	if (index < countEntryInCorkQueue ())
	{
		tag->extensionFields.scopeIndex = index;
		return es_false;
	}

	return OPTSCRIPT_ERR_NOTAGENTRY;
}

static EsObject* checkFieldValueForScope (const fieldDefinition *fdef, const EsObject *obj)
{
	if (!es_integer_p (obj))
		return OPT_ERR_TYPECHECK;

	if (es_integer_get (obj) < 0)
		return OPT_ERR_RANGECHECK;

	return es_false;
}

static EsObject* getFieldValueForExtras (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	if (!isTagExtra (tag))
		return es_nil;

	EsObject* a = opt_array_new ();

	for (int i = 0; i < countXtags (); i++)
	{
		if (!isTagExtraBitMarked (tag, i))
			continue;

		langType lang = getXtagOwner (i);
		const char *lang_name = (lang == LANG_IGNORE)
			? NULL
			: getLanguageName (lang);
		const char *extra_name = getXtagName (i);

		EsObject *extra;
		if (lang_name == NULL)
			extra = opt_name_new_from_cstr (extra_name);
		else
		{
			vString *tmp = vStringNewInit (lang_name);
			vStringPut (tmp, '.');
			vStringCatS (tmp, extra_name);
			extra = opt_name_new_from_cstr (vStringValue (tmp));
			vStringDelete (tmp);
		}
		opt_array_add (a, extra);
		es_object_unref (extra);
	}
	return a;
}

static EsObject* getFieldValueForCOMMON (const char *field, const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	if (!field)
		return es_nil;
	return (opt_name_new_from_cstr (field));
}

static EsObject* setFieldValueForCOMMON (const char **field, tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *obj)
{
	if (*field)
		eFree ((char *)*field);

	const char *str = opt_string_get_cstr (obj);
	*field = eStrdup (str);
	return es_false;
}

static EsObject* getFieldValueForAccess (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	return getFieldValueForCOMMON(tag->extensionFields.access, tag, fdef);
}

static EsObject* setFieldValueForAccess (tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *obj)
{
	return setFieldValueForCOMMON(&tag->extensionFields.access, tag, fdef, obj);
}

static EsObject* getFieldValueForSignature (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	return getFieldValueForCOMMON(tag->extensionFields.signature, tag, fdef);
}

static EsObject* setFieldValueForSignature (tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *obj)
{
	return setFieldValueForCOMMON(&tag->extensionFields.signature, tag, fdef, obj);
}

static void makeRolesArray (const tagEntryInfo *const tag, int roleIndex, void *data)
{
	EsObject *a = data;

	const roleDefinition *role = getTagRole (tag, roleIndex);
	EsObject *r = opt_name_new_from_cstr (role->name);
	opt_array_add (a, r);
	es_object_unref (r);
}

static EsObject* getFieldValueForRoles (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	EsObject *a = opt_array_new ();

	if (!foreachRoleBits (tag, makeRolesArray, a))
	{
		EsObject *r = opt_name_new_from_cstr (ROLE_DEFINITION_NAME);
		opt_array_add (a, r);
		es_object_unref (r);
	}
	return a;
}

static EsObject* getFieldValueForLineCommon (const tagEntryInfo *tag, const fieldDefinition *fdef)
{
	if (fdef->ftype == FIELD_END_LINE)
		return ((int)tag->extensionFields.endLine == 0)
			? es_nil
			: es_integer_new ((int)tag->extensionFields.endLine);
	else
		return ((int)tag->lineNumber == 0)
			? es_nil
			: es_integer_new ((int)tag->lineNumber);
}

static EsObject* checkFieldValueForLineCommon (const fieldDefinition *fdef, const EsObject *obj)
{
	return es_false;
}

static EsObject* setFieldValueForLineCommon (tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *obj)
{
	int l;
	if (es_object_get_type (obj) == OPT_TYPE_MATCHLOC)
	{
		matchLoc *loc = es_pointer_get (obj);
		l = loc->line;
	}
	else if (es_integer_p (obj))
	{
		l = es_integer_get (obj);
		if (l < 1)
			return OPT_ERR_RANGECHECK;

		/* If the new line number is too large,
		   we cannot fill tag->filePosition wit
		   getInputFilePositionForLine(); */
		if (fdef->ftype == FIELD_LINE_NUMBER
			&& l < getInputLineNumber())
			return OPT_ERR_RANGECHECK;
	}
	else
		return OPT_ERR_TYPECHECK;

	if (fdef->ftype == FIELD_END_LINE)
		tag->extensionFields.endLine = l;
	else
	{
		tag->lineNumber = l;
		tag->filePosition = getInputFilePositionForLine (l);
	}

	return es_false;
}

static EsObject* setFieldValueForInherits (tagEntryInfo *tag, const fieldDefinition *fdef, const EsObject *obj)
{
	if (es_object_get_type (obj) == OPT_TYPE_STRING)
	{
		if (tag->extensionFields.inheritance)
			eFree ((void *)tag->extensionFields.inheritance);
		const char *str = opt_string_get_cstr (obj);
		tag->extensionFields.inheritance = eStrdup (str);
	}
	else if (es_object_equal (es_false, obj))
	{
		if (tag->extensionFields.inheritance)
		{
			eFree ((void *)tag->extensionFields.inheritance);
			tag->extensionFields.inheritance = NULL;
		}
	}
	else
		return OPT_ERR_RANGECHECK; /* true is not acceptable. */

	return es_false;
}
