www.pudn.com > vim53src.zip > entry.c


/***************************************************************************** 
*   $Id: entry.c,v 6.4 1998/07/22 02:32:58 darren Exp $ 
* 
*   Copyright (c) 1996-1998, Darren Hiebert 
* 
*   This source code is released for free distribution under the terms of the 
*   GNU General Public License. 
* 
*   This module contains functions for creating tag entries. 
*****************************************************************************/ 
 
/*============================================================================ 
=   Include files 
============================================================================*/ 
#include  
#ifdef HAVE_CONFIG_H 
# include  
#endif 
#ifdef DEBUG 
# include  
#endif 
#include "ctags.h" 
 
/*============================================================================ 
 *=	Defines 
============================================================================*/ 
#define includeExtensionFlags()		(Option.tagFileFormat > 1) 
 
/*============================================================================ 
=   Data definitions 
============================================================================*/ 
 
/*  Note that the strings and characters in these arrays must correspond to 
 *  the types in the tagType enumeration. 
 */ 
static const char *TagTypeNames[] = { 
    "class", "define", "macro", "enum", "enumerator", "prototype", "function", 
    "interface", "member", "namespace", "file", "struct", "typedef", "union", 
    "variable", "externvar" 
}; 
 
static const char TagTypeChars[] = "cddgepfimnFstuvx"; 
 
/*============================================================================ 
=   Function prototypes 
============================================================================*/ 
static size_t writeSourceLine __ARGS((FILE *const fp, const char *const line)); 
static size_t writeCompactSourceLine __ARGS((FILE *const fp, const char *const line)); 
static void rememberMaxLengths __ARGS((const size_t nameLength, const size_t lineLength)); 
static void writeXrefEntry __ARGS((const tagInfo *const tag, const tagType type)); 
static void truncateTagLine __ARGS((char *const line, const char *const token, const boolean discardNewline)); 
static int writeEtagsEntry __ARGS((const tagInfo *const tag, const memberInfo *const pMember, const tagScope scope, const tagType type)); 
static int addExtensionFlags __ARGS((const memberInfo *const pMember, const tagScope scope, const tagType type)); 
static int writeLineNumberEntry __ARGS((const tagInfo *const tag)); 
static int writePatternEntry __ARGS((const tagInfo *const tag, const tagType type)); 
static int writeCtagsEntry __ARGS((const tagInfo *const tag, const memberInfo *const pMember, const tagScope scope, const tagType type, const boolean useLineNumber)); 
static void writeTagEntry __ARGS((const tagInfo *const tag, const memberInfo *const pMember, const tagScope scope, const tagType type, const boolean useLineNumber)); 
static boolean includeTag __ARGS((const tagScope scope, const tagType type)); 
static void writeExtraMemberTagEntry __ARGS((const tagInfo *const tag, const memberInfo *const pMember, const tagScope scope, const tagType type, const boolean useLineNumber, const char *const format)); 
static void makeExtraMemberTagEntry __ARGS((const tagInfo *const tag, const memberInfo *const pMember, const tagScope scope, const tagType type, const boolean useLineNumber)); 
static void makeTagEntry __ARGS((const tagInfo *const tag, const memberInfo *const pMember, const tagScope scope, const tagType type, const boolean useLineNumber)); 
static void writePseudoTag __ARGS((const char *const tagName, const char *const fileName, const char *const pattern)); 
static void updateSortedFlag __ARGS((const char *const line, FILE *const fp, const long startOfLine)); 
 
/*============================================================================ 
=   Function definitions 
============================================================================*/ 
 
extern const char *tagTypeName( type ) 
    const tagType type; 
{ 
    const char *name; 
 
    if ((int)type < (int)TAG_NUMTYPES) 
	name = TagTypeNames[(int)type]; 
    else 
	name = "?"; 
 
    return name; 
} 
 
/*  This function copies the current line out some other fp.  It has no 
 *  effect on the fileGetc() function.  During copying, any '\' characters 
 *  are doubled and a leading '^' or trailing '$' is also quoted.  The '\n' 
 *  character is not copied.  If the '\n' is preceded by a '\r', then the 
 *  '\r' isn't copied. 
 * 
 *  This is meant to be used when generating a tag line. 
 */ 
static size_t writeSourceLine( fp, line ) 
    FILE *const fp; 
    const char *const line; 
{ 
    size_t length = 0; 
    const char *p; 
    int c; 
 
    /*	Write everything up to, but not including, the newline. 
     */ 
    for (p = line, c = *p  ;  c != NEWLINE  &&  c != '\0'  ;  ++p, c = *p) 
    { 
	const int next = *(p + 1); 
 
	/*  If character is '\', or a terminal '$', then quote it. 
	 */ 
	if (c == BACKSLASH  ||  c == (Option.backward ? '?' : '/')  || 
	    (c == '$'  &&  next == NEWLINE)) 
	{ 
	    putc(BACKSLASH, fp); 
	    ++length; 
	} 
 
	/*  Copy the character, unless it is a terminal '\r'. 
	 */ 
	if (c != CRETURN  ||  next != NEWLINE) 
	{ 
	    putc(c, fp); 
	    ++length; 
	} 
    } 
    return length; 
} 
 
/*  Writes "line", stripping leading and duplicate white space. 
 */ 
static size_t writeCompactSourceLine( fp, line ) 
    FILE *const fp; 
    const char *const line; 
{ 
    boolean lineStarted = FALSE; 
    size_t  length = 0; 
    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) 
	    { 
		putc(c, fp); 
		++length; 
	    } 
	} 
    } 
    return length; 
} 
 
static void rememberMaxLengths( nameLength, lineLength ) 
    const size_t nameLength; 
    const size_t lineLength; 
{ 
    if (nameLength > TagFile.max.tag) 
	TagFile.max.tag = nameLength; 
 
    if (lineLength > TagFile.max.line) 
	TagFile.max.line = lineLength; 
} 
 
static void writeXrefEntry( tag, type ) 
    const tagInfo *const tag; 
    const tagType type; 
{ 
    const char *const line = getSourceLine(&TagFile.line, tag->location); 
    size_t length = fprintf(TagFile.fp, "%-20s %-10s %4lu  %-14s ", 
			    tag->name, tagTypeName(type), tag->lineNumber, 
			    File.name); 
 
    length += writeCompactSourceLine(TagFile.fp, line); 
    putc(NEWLINE, TagFile.fp); 
    ++length; 
 
    ++TagFile.numTags.added; 
    rememberMaxLengths(strlen(tag->name), length); 
} 
 
/*  Truncates the text line containing the tag at the character following the 
 *  tag, providing a character which designates the end of the tag. 
 */ 
static void truncateTagLine( line, token, discardNewline ) 
    char *const line; 
    const char *const token; 
    const boolean discardNewline; 
{ 
    char *p = strstr(line, token); 
 
    if (p != NULL) 
    { 
	p += strlen(token); 
	if (*p != '\0'  &&  !(*p == '\n'  &&  discardNewline)) 
	    ++p;		/* skip past character terminating character */ 
	*p = '\0'; 
    } 
} 
 
static int writeEtagsEntry( tag, pMember, scope, type ) 
    const tagInfo *const tag; 
    const memberInfo *const pMember; 
    const tagScope scope; 
    const tagType type; 
{ 
    char *const line = getSourceLine(&TagFile.line, tag->location); 
    int length; 
 
    truncateTagLine(line, tag->name, TRUE); 
    if (pMember->type != MEMBER_NONE) 
	length = fprintf(TagFile.etags.fp, "%s\177%s:%s\001%lu,%ld\n", line, 
			 pMember->parent, tag->name, tag->lineNumber, 
			 tag->location); 
    else if (scope == SCOPE_STATIC) 
	length = fprintf(TagFile.etags.fp, "%s\177%s:%s\001%lu,%ld\n", line, 
			 File.name, tag->name, tag->lineNumber, tag->location); 
    else if (type == TAG_CLASS   ||  type == TAG_DEFINE_FUNC  || 
	     type == TAG_ENUM    ||  type == TAG_STRUCT	      || 
	     type == TAG_TYPEDEF ||  type == TAG_UNION) 
	length = fprintf(TagFile.etags.fp, "%s\177%s\001%lu,%ld\n", line, 
			 tag->name, tag->lineNumber, tag->location); 
    else 
	length = fprintf(TagFile.etags.fp, "%s\177%lu,%ld\n", line, 
			 tag->lineNumber, tag->location); 
    TagFile.etags.byteCount += length; 
    return length; 
} 
 
extern const char *getTypeString( mType ) 
    const memberType mType; 
{ 
    const char *typeString; 
 
    switch (mType) 
    { 
	case MEMBER_ENUM:	typeString = "enum";		break; 
	case MEMBER_CLASS:	typeString = "class";		break; 
	case MEMBER_INTERFACE:	typeString = "interface";	break; 
	case MEMBER_NAMESPACE:	typeString = "namespace";	break; 
	case MEMBER_STRUCT:	typeString = "struct";		break; 
	case MEMBER_UNION:	typeString = "union";		break; 
	default:		typeString = "";		break; 
    } 
    return typeString; 
} 
 
extern const char *getVisibilityString( visibility ) 
    const visibilityType visibility; 
{ 
    const char *visibilityString; 
 
    switch (visibility) 
    { 
	case VIS_PRIVATE:	visibilityString = "private";		break; 
	case VIS_PROTECTED:	visibilityString = "protected";		break; 
	case VIS_PUBLIC:	visibilityString = "public";		break; 
	default:		visibilityString = "";			break; 
    } 
    return visibilityString; 
} 
 
static int addExtensionFlags( pMember, scope, type ) 
    const memberInfo *const pMember; 
    const tagScope scope; 
    const tagType type; 
{ 
    const char *const prefix = ";\"\t"; 
    const char *const separator = "\t"; 
    int length = 0; 
#ifdef LONG_FORM_TYPE 
    const char *const format = "%skind:%c"; 
#else 
    const char *const format = "%s%c"; 
#endif 
 
    /*  Add an extension flag designating that the type of the tag. 
     */ 
    length += fprintf(TagFile.fp, format, prefix, TagTypeChars[(int)type]); 
 
    /*  If this is a static tag, add the appropriate extension flag. 
     */ 
    if (scope == SCOPE_STATIC) 
	length += fprintf(TagFile.fp, "%sfile:", separator); 
 
    /*  For selected tag types, append an extension flag designating the 
     *  parent object in which the tag is defined. 
     */ 
    switch (type) 
    { 
	case TAG_DEFINE_FUNC: 
	case TAG_DEFINE_OBJ: 
	default: 
	    /* always global scope */ 
	    break; 
 
	case TAG_ENUMERATOR: 
	case TAG_FUNCDECL: 
	case TAG_FUNCTION: 
	case TAG_MEMBER: 
	case TAG_TYPEDEF: 
	case TAG_VARIABLE: 
	{ 
	    if (pMember->type != MEMBER_NONE) 
	    { 
		length += fprintf(TagFile.fp, "%s%s:%s", separator, 
				  getTypeString(pMember->type),pMember->parent); 
 
		if ((File.language == LANG_CPP || File.language == LANG_JAVA) && 
		    pMember->visibility != VIS_UNDEFINED) 
		{ 
		    length += fprintf(TagFile.fp, "%s%s:", separator, 
				    getVisibilityString(pMember->visibility)); 
		} 
	    } 
	    break; 
	} 
    } 
    return length; 
} 
 
static int writeLineNumberEntry( tag ) 
    const tagInfo *const tag; 
{ 
    return fprintf(TagFile.fp, "%lu", tag->lineNumber); 
} 
 
static int writePatternEntry( tag, type ) 
    const tagInfo *const tag; 
    const tagType type; 
{ 
    char *const line = getSourceLine(&TagFile.line, tag->location); 
    const int searchChar = Option.backward ? '?' : '/'; 
    boolean newlineTerminated; 
    int length = 0; 
 
    if (type == TAG_DEFINE_OBJ  ||  type == TAG_DEFINE_FUNC) 
	truncateTagLine(line, tag->name, FALSE); 
    newlineTerminated = (boolean)(line[strlen(line) - 1] == '\n'); 
 
    length += fprintf(TagFile.fp, "%c^", searchChar); 
    length += writeSourceLine(TagFile.fp, line); 
    length += fprintf(TagFile.fp, "%s%c", newlineTerminated ? "$":"", 
		      searchChar); 
 
    return length; 
} 
 
static int writeCtagsEntry( tag, pMember, scope, type, useLineNumber ) 
    const tagInfo *const tag; 
    const memberInfo *const pMember; 
    const tagScope scope; 
    const tagType type; 
    const boolean useLineNumber; 
{ 
    int length = fprintf(TagFile.fp, "%s\t%s\t", tag->name, File.name); 
 
    if (useLineNumber || type == TAG_SOURCE_FILE) 
	length += writeLineNumberEntry(tag); 
    else 
	length += writePatternEntry(tag, type); 
 
    if (includeExtensionFlags()) 
	length += addExtensionFlags(pMember, scope, type); 
 
    length += fprintf(TagFile.fp, "\n"); 
 
    return length; 
} 
 
static void writeTagEntry( tag, pMember, scope, type, useLineNumber ) 
    const tagInfo *const tag; 
    const memberInfo *const pMember; 
    const tagScope scope; 
    const tagType type; 
    const boolean useLineNumber; 
{ 
    size_t length; 
 
    if (Option.etags) 
	length = writeEtagsEntry(tag, pMember, scope, type); 
    else 
	length = writeCtagsEntry(tag, pMember, scope, type, useLineNumber); 
 
    ++TagFile.numTags.added; 
    rememberMaxLengths(strlen(tag->name), length); 
} 
 
static boolean includeTag( scope, type ) 
    const tagScope scope; 
    const tagType type; 
{ 
    boolean include; 
 
    if (scope == SCOPE_STATIC  &&  ! Option.include.statics) 
	include = FALSE; 
    else switch (type) 
    { 
	case TAG_CLASS:		include = Option.include.classNames;	break; 
	case TAG_DEFINE_OBJ: 
	case TAG_DEFINE_FUNC:	include = Option.include.defines;	break; 
	case TAG_ENUM:		include = Option.include.enumNames;	break; 
	case TAG_ENUMERATOR:	include = Option.include.enumerators;	break; 
	case TAG_FUNCDECL:	include = Option.include.prototypes;	break; 
	case TAG_FUNCTION:	include = Option.include.functions;	break; 
	case TAG_INTERFACE:	include = Option.include.interfaceNames;break; 
	case TAG_MEMBER:	include = Option.include.members;	break; 
	case TAG_NAMESPACE:	include = Option.include.namespaceNames;break; 
	case TAG_SOURCE_FILE:	include = Option.include.sourceFiles;	break; 
	case TAG_STRUCT:	include = Option.include.structNames;	break; 
	case TAG_TYPEDEF:	include = Option.include.typedefs;	break; 
	case TAG_UNION:		include = Option.include.unionNames;	break; 
	case TAG_VARIABLE:	include = Option.include.variables;	break; 
	case TAG_EXTERN_VAR:	include = Option.include.externVars;	break; 
	default:		include = FALSE;			break; 
    } 
    return include; 
} 
 
static void writeExtraMemberTagEntry( tag, pMember, scope, type, 
				      useLineNumber, format ) 
    const tagInfo *const tag; 
    const memberInfo *const pMember; 
    const tagScope scope; 
    const tagType type; 
    const boolean useLineNumber; 
    const char *const format; 
{ 
    switch (type) 
    { 
	case TAG_FUNCDECL: 
	case TAG_FUNCTION: 
	case TAG_MEMBER: 
	{ 
	    tagInfo prefixedTag; 
 
	    prefixedTag = *tag; 
	    sprintf(prefixedTag.name, format, pMember->parent, tag->name); 
	    writeTagEntry(&prefixedTag, pMember, scope, type, useLineNumber); 
	} 
	default: break; 
    } 
} 
 
static void makeExtraMemberTagEntry( tag, pMember, scope, type, useLineNumber ) 
    const tagInfo *const tag; 
    const memberInfo *const pMember; 
    const tagScope scope; 
    const tagType type; 
    const boolean useLineNumber; 
{ 
    if (File.language == LANG_CPP  && 
	(pMember->type == MEMBER_CLASS  ||  pMember->type == MEMBER_STRUCT)) 
    { 
	writeExtraMemberTagEntry(tag, pMember, scope, type, useLineNumber, "%s::%s"); 
    } 
    else if (File.language == LANG_JAVA  &&  pMember->type == MEMBER_CLASS) 
    { 
	writeExtraMemberTagEntry(tag, pMember, scope, type, useLineNumber, "%s.%s"); 
    } 
} 
 
/*  This function generates a tag for the object in name, whose tag line is 
 *  located at a given seek offset. 
 */ 
static void makeTagEntry( tag, pMember, scope, type, useLineNumber ) 
    const tagInfo *const tag; 
    const memberInfo *const pMember; 
    const tagScope scope; 
    const tagType type; 
    const boolean useLineNumber; 
{ 
    if (includeTag(scope, type)  &&  tag->name[0] != '\0') 
    { 
	if (Option.xref) 
	    writeXrefEntry(tag, type); 
	else 
	{ 
	    writeTagEntry(tag, pMember, scope, type, useLineNumber); 
	    if (Option.include.classPrefix  &&  pMember->parent[0] != '0') 
		makeExtraMemberTagEntry(tag, pMember, scope, type, useLineNumber); 
	} 
	DebugStatement( debugEntry(scope, type, tag->name, pMember); ) 
    } 
} 
 
extern void makeTag( tag, pMember, scope, type ) 
    const tagInfo *const tag; 
    const memberInfo *const pMember; 
    const tagScope scope; 
    const tagType type; 
{ 
    makeTagEntry(tag, pMember, scope, type, 
		 (boolean)(Option.locate == EX_LINENUM)); 
} 
 
extern void makeDefineTag( tag, scope, parameterized ) 
    const tagInfo *const tag; 
    const tagScope scope; 
    const boolean parameterized; 
{ 
    const tagType type = (parameterized ? TAG_DEFINE_FUNC : TAG_DEFINE_OBJ); 
 
    makeTagEntry(tag, &NoClass, scope, type, 
		 (boolean)(Option.locate != EX_PATTERN)); 
} 
 
/*---------------------------------------------------------------------------- 
*-	Pseudo tag support 
----------------------------------------------------------------------------*/ 
 
static void writePseudoTag( tagName, fileName, pattern ) 
    const char *const tagName; 
    const char *const fileName; 
    const char *const pattern; 
{ 
    const size_t length = fprintf(TagFile.fp, "%s%s\t%s\t/%s/\n", 
				 PSEUDO_TAG_PREFIX, tagName, fileName, pattern); 
    ++TagFile.numTags.added; 
    rememberMaxLengths(strlen(tagName), length); 
} 
 
extern void addPseudoTags() 
{ 
    if (! Option.xref  &&  ! Option.etags) 
    { 
	char format[11]; 
	const char *formatComment = "unknown format"; 
 
	sprintf(format, "%u", Option.tagFileFormat); 
 
	if (Option.tagFileFormat == 1) 
	    formatComment = "original ctags format"; 
	else if (Option.tagFileFormat == 2) 
	    formatComment = 
		    "extended format; --format=1 will not append ;\" to lines"; 
 
	writePseudoTag("TAG_FILE_FORMAT", format, formatComment); 
	writePseudoTag("TAG_FILE_SORTED", Option.sorted ? "1":"0", 
		       "0=unsorted, 1=sorted"); 
	writePseudoTag("TAG_PROGRAM_AUTHOR",	AUTHOR_NAME,  AUTHOR_EMAIL); 
	writePseudoTag("TAG_PROGRAM_NAME",	PROGRAM_NAME, ""); 
	writePseudoTag("TAG_PROGRAM_URL",	PROGRAM_URL,  "official site"); 
	writePseudoTag("TAG_PROGRAM_VERSION",	PROGRAM_VERSION, 
		       "with C++ and Java support"); 
    } 
} 
 
static void updateSortedFlag( line, fp, startOfLine ) 
    const char *const line; 
    FILE *const fp; 
    const long startOfLine; 
{ 
    const char *const tab = strchr(line, '\t'); 
 
    if (tab != NULL) 
    { 
	const long boolOffset = tab - line + 1;	/* where it should be */ 
 
	if (line[boolOffset] == '0'  ||  line[boolOffset] == '1') 
	{ 
	    const long nextLine = ftell(fp); 
 
	    fseek(fp, startOfLine + boolOffset, SEEK_SET); 
	    fputc(Option.sorted ? '1' : '0', fp); 
	    fseek(fp, nextLine, SEEK_SET); 
	} 
    } 
} 
 
/*  Look through all line beginning with "!_TAG_FILE", and update those which 
 *  require it. 
 */ 
extern unsigned long updatePseudoTags() 
{ 
    enum { maxClassLength = 20 }; 
    char class[maxClassLength + 1]; 
    FILE *const fp = TagFile.fp; 
    long startOfLine = 0; 
    unsigned long linesRead = 0; 
    size_t classLength; 
    const char *line; 
 
    sprintf(class, "%sTAG_FILE", PSEUDO_TAG_PREFIX); 
    classLength = strlen(class); 
    DebugStatement( assert(classLength < maxClassLength); ) 
    rewind(fp); 
    line = readLine(&TagFile.line, fp); 
    while (line != NULL  &&  line[0] == class[0]) 
    { 
	++linesRead; 
	if (strncmp(line, class, classLength) == 0) 
	{ 
	    char tab, classType[16]; 
 
	    if (sscanf(line + classLength, "%15s%c", classType, &tab) == 2  && 
		tab == '\t') 
	    { 
		if (strcmp(classType, "_SORTED") == 0) 
		    updateSortedFlag(line, fp, startOfLine); 
	    } 
	    startOfLine = ftell(fp); 
	} 
	line = readLine(&TagFile.line, fp); 
    } 
    while (line != NULL)			/* skip to end of file */ 
    { 
	++linesRead; 
	line = readLine(&TagFile.line, fp); 
    } 
    return linesRead; 
} 
 
/* vi:set tabstop=8 shiftwidth=4: */