This is main.c in view mode; [Download] [Up]
/***************************************************************************** * $Id: main.c,v 1.16 1997/04/06 04:31:24 darren Exp $ * * Copyright (c) 1996-1997, Darren Hiebert * * Author: Darren Hiebert <darren@hiwaay.net>, http://fly.hiwaay.net/~darren * * This source code is released into the public domain. It is provided on an * as-is basis and no responsibility is accepted for its failure to perform * as expected. It is worth at least as much as you paid for it! * * This is a reimplementation of the ctags(1) program. It is an attempt to * provide a fully featured ctags program which is free of the limitations * which most (all?) others are subject to. * * It is derived from and inspired by the ctags program by Steve Kirkendall * (kirkenda@cs.pdx.edu) that comes with the Elvis vi clone (though almost * none of the original code remains). This, too, was freely available. * * This program provides the following features: * * Support for both K&R style and new ANSI style function definitions. * * Generates tags for the following objects: * - macro definitions * - enumeration values * - function definitions (and C++ methods) * - function declarations (optional) * - enum, struct and union tags and C++ class names * - typedefs * - variables *****************************************************************************/ /*============================================================================ = Include files ============================================================================*/ #ifdef HAVE_CONFIG_H # include <config.h> #endif #if defined(NO_REMOVE) && defined(HAVE_UNISTD_H) # include <unistd.h> /* to declare unlink() */ #endif /* To define the maximum path length (hopefully) */ #ifdef __BORLANDC__ # include <dir.h> /* to define MAXPATH */ #else # ifdef INCLUDE_LIMITS_H # include <limits.h> /* to define PATH_MAX (hopefully) */ # else # ifdef INCLUDE_DIRENT_H # include <dirent.h> /* to define MAXNAMLEN (hopefully) */ # endif # endif #endif #include "ctags.h" /*============================================================================ = Defines ============================================================================*/ /*---------------------------------------------------------------------------- * Portability defines *--------------------------------------------------------------------------*/ #ifndef PATH_MAX # ifdef MAXNAMLEN # define PATH_MAX MAXNAMLEN # else # ifdef MAXPATH /* found in Borland C <dir.h> */ # define PATH_MAX MAXPATH # else # if defined(MSDOS) || defined(WIN32) # define PATH_MAX 128 # else # define PATH_MAX 256 # endif # endif # endif #endif #if defined(MSDOS) || defined(WIN32) || defined(OS2) # define PATH_SEPARATOR '\\' #else # define PATH_SEPARATOR '/' #endif /*---------------------------------------------------------------------------- * Miscellaneous defines *--------------------------------------------------------------------------*/ #ifndef ETAGS # define ETAGS "etags" /* name which causes default use of to -e */ #endif /*============================================================================ = Data definitions ============================================================================*/ tagFile TagFile = { NULL, NULL, 0, { 0, 0, 0 }, { "", NULL } }; /*============================================================================ = Function prototypes ============================================================================*/ static void beginEtagsFile __ARGS((void)); static void endEtagsFile __ARGS((const char *const name)); static boolean createTagsForFile __ARGS((const char *const name)); static const char *getNextListFile __ARGS((FILE *const fp)); static const char *sourceFilePath __ARGS((const char *const file)); static void createTagsForList __ARGS((const char *const listFile)); static void createTagsForArgs __ARGS((char *const *const argList)); static void makeTags __ARGS((char *const *const argList)); static boolean isSourceFile __ARGS((const char *const filename)); static void openTagFile __ARGS((const boolean toStdout)); static void closeTagFile __ARGS((void)); static void testEtagsInvocation __ARGS((const char *const path)); static void setDefaultTagFileName __ARGS((void)); static void setOptionDefaults __ARGS((void)); extern int main __ARGS((int argc, char **argv)); /*============================================================================ = Function definitions ============================================================================*/ static void beginEtagsFile() { tmpnam(TagFile.etags.name); TagFile.etags.fp = fopen(TagFile.etags.name, "w+"); if (TagFile.etags.fp == NULL) perror(TagFile.etags.name); TagFile.etags.byteCount = 0; } static void endEtagsFile( name ) const char *const name; { char line[MaxTagLine]; fprintf(TagFile.fp, "\f\n%s,%ld\n", name, (long)TagFile.etags.byteCount); if (TagFile.etags.fp != NULL) { rewind(TagFile.etags.fp); while (fgets(line, MaxTagLine, TagFile.etags.fp) != NULL) fputs(line, TagFile.fp); fclose(TagFile.etags.fp); remove(TagFile.etags.name); } TagFile.etags.name[0] = '\0'; } static boolean createTagsForFile( name ) const char *const name; { boolean ok; if (name == NULL) ok = FALSE; else { ok = TRUE; if (Option.etags) beginEtagsFile(); if (cppOpen(name)) { ok = createTags(0); cppClose(); } if (Option.etags) endEtagsFile(name); } return ok; } static const char *getNextListFile( fp ) FILE *const fp; { static char fileName[PATH_MAX + 1]; const char *const buf = fgets(fileName, PATH_MAX + 1, fp); if (buf != NULL) { char *const newline = strchr(fileName, '\n'); if (newline == NULL) fileName[PATH_MAX] = '\0'; else *newline = '\0'; } return buf; } static const char *sourceFilePath( file ) const char *const file; { static char filePath[PATH_MAX + 1]; const char *result = NULL; if (Option.path == NULL || file[0] == PATH_SEPARATOR) result = file; else if (strlen(Option.path) + strlen(file) < (size_t)PATH_MAX) { if (Option.path[strlen(Option.path) - 1] == PATH_SEPARATOR) sprintf(filePath, "%s%s", Option.path, file); else sprintf(filePath, "%s%c%s", Option.path, PATH_SEPARATOR, file); result = filePath; } return result; } /* Create tags for the source files listed in the file specified by * "listFile". */ static void createTagsForList( listFile ) const char *const listFile; { const char *fileName; FILE *const fp = (strcmp(listFile,"-") == 0) ? stdin : fopen(listFile, "r"); if (fp == NULL) { perror(listFile); exit(1); } else { fileName = getNextListFile(fp); while (fileName != NULL && fileName[0] != '\0') { const long startPos = ftell(TagFile.fp); const size_t numTags = TagFile.numTags; const char *const filePath = sourceFilePath(fileName); if (! createTagsForFile(filePath) && ! Option.braceFormat) { fseek(TagFile.fp, startPos, SEEK_SET); TagFile.numTags = numTags; /* restore previous count */ Option.braceFormat = TRUE; #ifdef DEBUG if (debug(DEBUG_STATUS)) printf("%s: Formatting error; retrying\n", fileName); #endif } else { Option.braceFormat = FALSE; fileName = getNextListFile(fp); } } if (fp != stdin) fclose(fp); } } static void createTagsForArgs( argList ) char *const *const argList; { int argNum = 0; /* Generate tags for each source file on the command line. */ while (argList[argNum] != NULL) { const long startPos = ftell(TagFile.fp); const size_t numTags = TagFile.numTags; const char *const filePath = sourceFilePath(argList[argNum]); if (! createTagsForFile(filePath) && ! Option.braceFormat) { fseek(TagFile.fp, startPos, SEEK_SET); TagFile.numTags = numTags; /* restore previous count */ Option.braceFormat = TRUE; #ifdef DEBUG if (debug(DEBUG_STATUS)) printf("%s: Formatting error; retrying\n", argList[argNum]); #endif } else { ++argNum; Option.braceFormat = FALSE; } } } static void makeTags( argList ) char *const *const argList; { boolean toStdout = FALSE; if (Option.xref || strcmp(Option.tagFile, "-") == 0) toStdout = TRUE; openTagFile(toStdout); if (Option.fileList != NULL) createTagsForList(Option.fileList); createTagsForArgs(argList); closeTagFile(); freeIgnoreList(); if (TagFile.numTags > 0 && ! Option.unsorted) { #ifdef EXTERNAL_SORT externalSortTags(toStdout); #else internalSortTags(toStdout); #endif } if (toStdout && TagFile.name != NULL) remove(TagFile.name); } /*---------------------------------------------------------------------------- * Start up and terminate functions *--------------------------------------------------------------------------*/ /* Checks whether the supplied filename contains something other than valid * tag lines. A tag line MUST fit the following regular expression: * * ^\([^\t]\+:\)[a-zA-Z_][a-zA-Z0-9_]*\t[^\t]\t[/?].*[/?]$ * * or, in more readable terms: * * [filename:]<tag> <filename> /pattern/ */ static boolean isSourceFile( filename ) const char *const filename; { static char line[MaxTagLine]; boolean isSource = FALSE; /* we assume not unless found */ FILE *const fp = fopen(filename, "r"); if (fp == NULL || fgets(line, MaxTagLine, fp) == NULL) ; else if (Option.etags) { if (line[0] != '\f' || line[1] != '\n') isSource = TRUE; } else { char *tagPrefix, *tagFname, *pattern; char *separator1, *separator2; char *const buffer = (char *)malloc((size_t)(5 * MaxTagLine)); if (buffer == NULL) { perror("Insufficient memory"); exit(1); } tagPrefix = &buffer[0 * MaxTagLine]; separator1 = &buffer[1 * MaxTagLine]; tagFname = &buffer[2 * MaxTagLine]; separator2 = &buffer[3 * MaxTagLine]; pattern = &buffer[4 * MaxTagLine]; if (sscanf(line, "%[^\t]%[\t]%[^\t]%[\t]%[^\n]", tagPrefix, separator1, tagFname, separator2, pattern) < 5) isSource = TRUE; else if (strlen(separator1) != 1 || strlen(separator2) != 1) isSource = TRUE; /* exactly one tab permitted as separators*/ else { /* Check for filename:tag format. */ char *tag = strchr(tagPrefix, ':'); if (tag == NULL) { tag = tagPrefix; tagPrefix = NULL; /* no tag filename prefix */ } else { *tag = '\0'; /* null terminate the tag prefix */ ++tag; /* tag follows tag prefix */ /* The filename portion of the prefixed tags must match * the supplied filename. */ if (strcmp(tagPrefix, tagFname) != 0) isSource = TRUE; } if (! isSource) { const char *p; /* The tag must be a valid C identifier. */ if (! isident1(tag[0])) isSource = TRUE; else for (p = tag + 1 ; *p != '\0' && !isSource ; ++p) { if (! isident(*p)) isSource = TRUE; } /* If the pattern does not begin with either '/' or '?', * or the first character and last characters do not match, * then the pattern must be a line number. */ if (! isSource && ((pattern[0] != '/' && pattern[0] != '?') || pattern[0] != pattern[strlen(pattern) - 1])) { for (p = pattern ; *p != '\0' && ! isSource ; ++p) if (! isdigit(*p)) isSource = TRUE; } } } free(buffer); } if (fp != NULL) fclose(fp); return isSource; } static void openTagFile( toStdout ) const boolean toStdout; { static char tempName[L_tmpnam]; /* Open the tags File. */ if (toStdout) { if (Option.unsorted) { TagFile.name = NULL; TagFile.fp = stdout; } else { TagFile.name = tmpnam(tempName); TagFile.fp = fopen(TagFile.name, "w"); } } else { TagFile.name = Option.tagFile; if (isSourceFile(TagFile.name)) printUsage("-o: Attempt to overwrite a non-tag file"); TagFile.fp = fopen(TagFile.name, Option.append ? "a" : "w"); } if (TagFile.fp == NULL) { perror(TagFile.name); exit(1); } } static void closeTagFile() { fclose(TagFile.fp); } static void testEtagsInvocation( path ) const char *const path; { const char *name = strrchr(path, PATH_SEPARATOR); /* get tail component */ if (name != NULL) ++name; /* skip over path separator */ else name = path; if (strncmp(name, ETAGS, strlen(ETAGS)) == 0) { Option.startedAsEtags = TRUE; Option.etags = TRUE; Option.unsorted = TRUE; } } static void setDefaultTagFileName() { if (Option.tagFile != NULL) ; /* accept given name */ else if (Option.etags) Option.tagFile = ETAGS_FILE; else Option.tagFile = CTAGS_FILE; } static void setOptionDefaults() { /* Disable warnings, since these are only supported with sorted tags. */ if (Option.unsorted) Option.warnings = FALSE; setDefaultTagFileName(); } extern int main( argc, argv ) int argc; char **argv; { char **envArgList; char *const *fileList; #ifdef __EMX__ _wildcard(&argc, &argv); /* expand wildcards in argument list */ #endif testEtagsInvocation(argv[0]); envArgList = parseEnvironmentOptions(); fileList = parseOptions(&argv[1]); /* 0th arg is program name */ setOptionDefaults(); /* There must always be at least one source file or a file list. */ if (*fileList == NULL && Option.fileList == NULL) printUsage("No files specified"); else makeTags(fileList); if (envArgList != NULL) free(envArgList); exit(0); return argc; /* kludge to stop warning about argc unused */ } /* vi:set tabstop=8 shiftwidth=4: */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.