⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 readtags.c

📁 ultraEdit的Ctag标签工具的实现源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
*   $Id: readtags.c,v 1.24 2003/07/20 18:46:30 darren Exp $
*
*   Copyright (c) 1996-2003, Darren Hiebert
*
*   This source code is released into the public domain.
*
*   This module contains functions for reading tag files.
*/

/*
*   INCLUDE FILES
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>	/* to declare off_t */

#include "readtags.h"

/*
*   MACROS
*/
#define TAB '\t'


/*
*   DATA DECLARATIONS
*/
typedef struct {
    size_t size;
    char *buffer;
} vstring;

/* Information about current tag file */
struct sTagFile {
	/* has the file been opened and this structure initialized? */
    short initialized;
	/* format of tag file */
    short format;
	/* how is the tag file sorted? */
    sortType sortMethod;
	/* pointer to file structure */
    FILE* fp;
	/* file position of first character of `line' */
    off_t pos;
	/* size of tag file in seekable positions */
    off_t size;
	/* last line read */
    vstring line;
	/* name of tag in last line read */
    vstring name;
	/* defines tag search state */
    struct {
		/* file position of last match for tag */
	    off_t pos; 
		/* name of tag last searched for */
	    const char *name;
		/* length of name for partial matches */
	    size_t nameLength;
		/* peforming partial match */
	    short partial;
		/* ignoring case */
	    short ignorecase;
    } search;
	/* miscellaneous extension fields */
    struct {
		/* number of entries in `list' */
	    unsigned short max;
		/* list of key value pairs */
	    tagExtensionField *list;
    } fields;
	/* buffers to be freed at close */
    struct {
	    /* name of program author */
	char *author;
	    /* name of program */
	char *name;
	    /* URL of distribution */
	char *url;
	    /* program version */
	char *version;
    } program;
};

/*
*   DATA DEFINITIONS
*/
const char *const EmptyString = "";
const char *const PseudoTagPrefix = "!_";

/*
*   FUNCTION DEFINITIONS
*/

/*
 * Compare two strings, ignoring case.
 * Return 0 for match, < 0 for smaller, > 0 for bigger
 * Make sure case is folded to uppercase in comparison (like for 'sort -f')
 * This makes a difference when one of the chars lies between upper and lower
 * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
 */
static int struppercmp (const char *s1, const char *s2)
{
    int result;
    do
    {
	result = toupper ((int) *s1) - toupper ((int) *s2);
    } while (result == 0  &&  *s1++ != '\0'  &&  *s2++ != '\0');
    return result;
}

static int strnuppercmp (const char *s1, const char *s2, size_t n)
{
    int result;
    do
    {
	result = toupper ((int) *s1) - toupper ((int) *s2);
    } while (result == 0  &&  --n > 0  &&  *s1++ != '\0'  &&  *s2++ != '\0');
    return result;
}

static int growString (vstring *s)
{
    int result = 0;
    size_t newLength;
    char *newLine;
    if (s->size == 0)
    {
	newLength = 128;
	newLine = (char*) malloc (newLength);
	*newLine = '\0';
    }
    else
    {
	newLength = 2 * s->size;
	newLine = (char*) realloc (s->buffer, newLength);
    }
    if (newLine == NULL)
	perror ("string too large");
    else
    {
	s->buffer = newLine;
	s->size = newLength;
	result = 1;
    }
    return result;
}

/* Copy name of tag out of tag line */
static void copyName (tagFile *const file)
{
    size_t length;
    const char *end = strchr (file->line.buffer, '\t');
    if (end == NULL)
    {
	end = strchr (file->line.buffer, '\n');
	if (end == NULL)
	    end = strchr (file->line.buffer, '\r');
    }
    if (end != NULL)
	length = end - file->line.buffer;
    else
	length = strlen (file->line.buffer);
    while (length >= file->name.size)
	growString (&file->name);
    strncpy (file->name.buffer, file->line.buffer, length);
    file->name.buffer [length] = '\0';
}

static int readTagLineRaw (tagFile *const file)
{
    int result = 1;
    int reReadLine;

    /*  If reading the line places any character other than a null or a
     *  newline at the last character position in the buffer (one less than
     *  the buffer size), then we must resize the buffer and reattempt to read
     *  the line.
     */
    do
    {
	char *const pLastChar = file->line.buffer + file->line.size - 2;
	char *line;

	file->pos = ftell (file->fp);
	reReadLine = 0;
	*pLastChar = '\0';
	line = fgets (file->line.buffer, (int) file->line.size, file->fp);
	if (line == NULL)
	{
	    /* read error */
	    if (! feof (file->fp))
		perror ("readTagLine");
	    result = 0;
	}
	else if (*pLastChar != '\0'  &&
		    *pLastChar != '\n'  &&  *pLastChar != '\r')
	{
	    /*  buffer overflow */
	    growString (&file->line);
	    fseek (file->fp, file->pos, SEEK_SET);
	    reReadLine = 1;
	}
	else
	{
	    size_t i = strlen (file->line.buffer);
	    while (i > 0  &&
		   (file->line.buffer [i - 1] == '\n' || file->line.buffer [i - 1] == '\r'))
	    {
		file->line.buffer [i - 1] = '\0';
		--i;
	    }
	}
    } while (reReadLine  &&  result);
    if (result)
	copyName (file);
    return result;
}

static int readTagLine (tagFile *const file)
{
    int result;
    do
    {
	result = readTagLineRaw (file);
    } while (result && *file->name.buffer == '\0');
    return result;
}

static tagResult growFields (tagFile *const file)
{
    tagResult result = TagFailure;
    unsigned short newCount = 2 * file->fields.max;
    tagExtensionField *newFields = (tagExtensionField*)
	    realloc (file->fields.list, newCount * sizeof (tagExtensionField));
    if (newFields == NULL)
	perror ("too many extension fields");
    else
    {
	file->fields.list = newFields;
	file->fields.max = newCount;
	result = TagSuccess;
    }
    return result;
}

static void parseExtensionFields (tagFile *const file, tagEntry *const entry,
				  char *const string)
{
    char *p = string;
    while (p != NULL  &&  *p != '\0')
    {
	while (*p == TAB)
	    *p++ = '\0';
	if (*p != '\0')
	{
	    char *colon;
	    char *field = p;
	    p = strchr (p, TAB);
	    if (p != NULL)
		*p++ = '\0';
	    colon = strchr (field, ':');
	    if (colon == NULL)
		entry->kind = field;
	    else
	    {
		const char *key = field;
		const char *value = colon + 1;
		*colon = '\0';
		if (strcmp (key, "kind") == 0)
		    entry->kind = value;
		else if (strcmp (key, "file") == 0)
		    entry->fileScope = 1;
		else if (strcmp (key, "line") == 0)
		    entry->address.lineNumber = atol (value);
		else
		{
		    if (entry->fields.count == file->fields.max)
			growFields (file);
		    file->fields.list [entry->fields.count].key = key;
		    file->fields.list [entry->fields.count].value = value;
		    ++entry->fields.count;
		}
	    }
	}
    }
}

static void parseTagLine (tagFile *file, tagEntry *const entry)
{
    int i;
    char *p = file->line.buffer;
    char *tab = strchr (p, TAB);
    int fieldsPresent = 0;

    entry->fields.list = NULL;
    entry->fields.count = 0;
    entry->kind = NULL;
    entry->fileScope = 0;

    entry->name = p;
    if (tab != NULL)
    {
	*tab = '\0';
	p = tab + 1;
	entry->file = p;
	tab = strchr (p, TAB);
	if (tab != NULL)
	{
	    *tab = '\0';
	    p = tab + 1;
	    if (*p == '/'  ||  *p == '?')
	    {
		/* parse pattern */
		int delimiter = *(unsigned char*) p;
		entry->address.lineNumber = 0;
		entry->address.pattern = p;
		do
		{
		    p = strchr (p + 1, delimiter);
		} while (p != NULL  &&  *(p - 1) == '\\');
		if (p == NULL)
		{
		    /* invalid pattern */
		}
		else
		    ++p;
	    }
	    else if (isdigit ((int) *(unsigned char*) p))
	    {
		/* parse line number */
		entry->address.pattern = p;
		entry->address.lineNumber = atol (p);
		while (isdigit ((int) *(unsigned char*) p))
		    ++p;
	    }
	    else
	    {
		/* invalid pattern */
	    }
	    fieldsPresent = (strncmp (p, ";\"", 2) == 0);
	    *p = '\0';
	    if (fieldsPresent)
		parseExtensionFields (file, entry, p + 2);
	}
    }
    if (entry->fields.count > 0)
	entry->fields.list = file->fields.list;
    for (i = entry->fields.count  ;  i < file->fields.max  ;  ++i)
    {
	file->fields.list [i].key = NULL;
	file->fields.list [i].value = NULL;
    }
}

static char *duplicate (const char *str)
{
    char *result = NULL;
    if (str != NULL)
    {
	result = (char*) malloc (strlen (str) + 1);
	if (result == NULL)
	    perror (NULL);
	else
	    strcpy (result, str);
    }
    return result;
}

static void readPseudoTags (tagFile *const file, tagFileInfo *const info)
{
    fpos_t startOfLine;
    const size_t prefixLength = strlen (PseudoTagPrefix);
    if (info != NULL)
    {
	info->file.format     = 1;
	info->file.sort       = TAG_UNSORTED;
	info->program.author  = NULL;
	info->program.name    = NULL;
	info->program.url     = NULL;
	info->program.version = NULL;
    }
    while (1)
    {
	fgetpos (file->fp, &startOfLine);
	if (! readTagLine (file))
	    break;
	if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
	    break;
	else
	{
	    tagEntry entry;
	    const char *key, *value;
	    parseTagLine (file, &entry);
	    key = entry.name + prefixLength;
	    value = entry.file;
	    if (strcmp (key, "TAG_FILE_SORTED") == 0)
		file->sortMethod = (sortType) atoi (value);
	    else if (strcmp (key, "TAG_FILE_FORMAT") == 0)
		file->format = atoi (value);
	    else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0)
		file->program.author = duplicate (value);
	    else if (strcmp (key, "TAG_PROGRAM_NAME") == 0)
		file->program.name = duplicate (value);
	    else if (strcmp (key, "TAG_PROGRAM_URL") == 0)
		file->program.url = duplicate (value);
	    else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0)
		file->program.version = duplicate (value);
	    if (info != NULL)
	    {
		info->file.format     = file->format;
		info->file.sort       = file->sortMethod;
		info->program.author  = file->program.author;
		info->program.name    = file->program.name;
		info->program.url     = file->program.url;
		info->program.version = file->program.version;
	    }
	}
    }
    fsetpos (file->fp, &startOfLine);
}

static void gotoFirstLogicalTag (tagFile *const file)
{
    fpos_t startOfLine;
    const size_t prefixLength = strlen (PseudoTagPrefix);
    rewind (file->fp);
    while (1)
    {
	fgetpos (file->fp, &startOfLine);
	if (! readTagLine (file))
	    break;
	if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
	    break;
    }
    fsetpos (file->fp, &startOfLine);
}

static tagFile *initialize (const char *const filePath, tagFileInfo *const info)
{
    tagFile *result = (tagFile*) malloc (sizeof (tagFile));
    if (result != NULL)
    {
	memset (result, 0, sizeof (tagFile));
	growString (&result->line);
	growString (&result->name);
	result->fields.max = 20;
	result->fields.list = (tagExtensionField*) malloc (
	    result->fields.max * sizeof (tagExtensionField));
	result->fp = fopen (filePath, "r");
	if (result->fp == NULL)
	{
	    free (result);
	    result = NULL;
	    info->status.error_number = errno;
	}
	else
	{
	    fseek (result->fp, 0, SEEK_END);
	    result->size = ftell (result->fp);
	    rewind (result->fp);
	    readPseudoTags (result, info);
	    info->status.opened = 1;
	    result->initialized = 1;
	}
    }
    return result;
}

static void terminate (tagFile *const file)
{
    fclose (file->fp);

    free (file->line.buffer);
    free (file->name.buffer);
    free (file->fields.list);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -