/*****************************************************************************
*   $Id: ctags.h,v 3.1 1997/05/13 05:34:26 darren Exp $
*
*   Copyright (c) 1996-1997, Darren Hiebert
*
*   Global include file.
*****************************************************************************/

/*============================================================================
=   Include files
============================================================================*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>	    /* to declare isalnum(), isalpha(), isspace() */

#if defined(HAVE_STDLIB_H) || defined(__STDC__) || defined(WIN32)
# include <stdlib.h>	    /* to declare alloc funcs and getenv() */
#endif

#ifdef DEBUG
# include <assert.h>
#endif

/*============================================================================
=   Portability defines
============================================================================*/

/*  Determine whether to use prototypes or simple declarations.
 */
#ifndef __ARGS 
# if defined(__STDC__) || defined(WIN32)
#  define __ARGS(x) x
#  define USING_PROTOTYPES
# else
#  define __ARGS(x) ()
#  undef USING_PROTOTYPES
# endif
#endif

/*  MS-DOS doesn't allow manipulation of standard error, so we send it to
 *  stdout instead.
 */
#if defined(MSDOS) || defined(WIN32)
# define errout	    stdout
#else
# define errout	    stderr
#endif

#ifndef L_tmpnam
# define L_tmpnam 20
#endif

/*============================================================================
=   General defines
============================================================================*/

#define CTAGS_FILE	"tags"
#define ETAGS_FILE	"TAGS"

/*============================================================================
=   Macros
============================================================================*/

#ifdef DEBUG
# define debug(level)	((Option.debugLevel & (level)) != 0)
#endif

/*  Is the character valid as a character of a C identifier?
 */
#define isident(c)	(isalnum(c) || (c) == '_')

/*  Is the character valid as the first character of a C identifier?
 */
#define isident1(c)	(isalpha(c) || (c) == '_')

/*  Is that character a space or a tab?
 */
#define isspacetab(c)	((c) == ' ' || (c) == '\t')

/*  Informs whether we are ignoring the contents of the current pre-processor
 *  conditional branch.
 */
#define cppIgnore()	(Cpp.directive.ifdef[Cpp.directive.level].ignore)

/*============================================================================
=   Data declarations
============================================================================*/

#undef FALSE
#undef TRUE
typedef enum { FALSE, TRUE } boolean;

enum limits {
    MaxNameLength	= 256,	/* maximum length of token with null */
    MaxTagLine		= 256,	/* maximum length of tag line, with null */
    MaxHeaderExtensions	= 100,	/* maximum number of extensions in -h option */
    MaxEnvOptions	= 5,	/* maximum # options in environment variable */
    CppNestingLevel	= 20
};

enum characters {
    /*  White space characters.
     */
    SPACE	= ' ',
    NEWLINE	= '\n',
    CRETURN	= '\r',
    FORMFEED	= '\f',
    TAB		= '\t',
    VTAB	= '\v',

    /*  Some hard to read characters.
     */
    DOUBLE_QUOTE  = '"',
    SINGLE_QUOTE  = '\'',
    BACKSLASH	  = '\\',

    STRING_SYMBOL = ('S' + 0x80),
    CHAR_SYMBOL	  = ('C' + 0x80)
};

/*  Maintains the state of the tag file.
 */
typedef struct {
    const char *name;
    FILE *fp;
    size_t numTags;
    struct { size_t line, tag, file; } max;
    struct {
	char name[L_tmpnam];
	FILE *fp;
	size_t byteCount;
    } etags;
} tagFile;

/*  Maintains the state of the current source file.
 */
typedef struct {
    const char *name;	    /* name of the current file */
    FILE    *fp;	    /* stream used for reading the file */
    long    lineNumber;	    /* line number in the current file */
    long    seek;	    /* fseek() offset to the start of current line */
    boolean afterNL;	    /* boolean: was previous character a newline? */
    int	    ungetch;	    /* a single character that was ungotten */
    int	    header;	    /* boolean: is the current file a header file? */
    boolean warned;	    /* format warning kludge */
} sourceFile;

/*  This stores the command line options.
 */
typedef struct {
    struct {
	boolean	defines;    /* -id  include tags for defines */
	boolean	enumValues; /* -ie  include tags for enumeration value */
	boolean	functions;  /* -if  include tags for functions */
	boolean	blockTags;  /* -ig  include tags for enum, struct and union */
	boolean	prototypes; /* -ip  include tags for external func. prototypes*/
	boolean	typedefs;   /* -it  include tags for typedefs */
	boolean	variables;  /* -iv  include tags for variables */
	boolean	prefix;	    /* -iP  prefix static tags with filename */
	boolean	statics;    /* -iS  include static tags */
	boolean	extensions; /* -iX  include extension flags */
    } include;
    struct {
	char **list;
	unsigned int count, max;
    } ignore;		    /* -I  name of file containing tokens to ignore */
    boolean append;	    /* -a  append to "tags" files */
    boolean backward;	    /* -B  regexp patterns search backwards */
    boolean etags;	    /* -e  output Emacs style tags file */
    enum {
	EX_MIX,		    /* default Ex tag location method */
	EX_LINENUM,	    /* -n  only line numbers in tag file */
	EX_PATTERN	    /* -N  only patterns in tag file */
    } locate;
    const char *path;	    /* -p  default path for source files */
    boolean unsorted;	    /* -u  do not sort tags */
    boolean warnings;	    /* -w  generate warnings about duplicate tags */
    boolean xref;	    /* -x  generate xref output instead */
    const char *fileList;   /* -L  name of file containing names of files */
    const char *tagFile;    /* -o  name of tags file */
    const char *headerExt[MaxHeaderExtensions + 1];/* -h  header extensions */
#ifdef DEBUG
    int debugLevel;	    /* -D  debugging output */
    long breakLine;	    /* -b  source line at which to call lineBreak() */
#endif
    boolean startedAsEtags;
    boolean braceFormat;    /* use brace formatting to detect end of block */
} optionValues;

/*  Describes the type of tag being generated. This is used for debugging
 *  purposes only.
 */
typedef enum {
    TAG_BLOCKTAG,	    /* enum/struct/union tag or C++ class name */
    TAG_DEFINE,		    /* C pre-processor define */
    TAG_ENUM,		    /* enumeration value */
    TAG_FUNCDECL,	    /* function declaration */
    TAG_FUNCTION,	    /* function definition */
    TAG_TYPEDEF,	    /* typedef name */
    TAG_VARIABLE,	    /* variable defintion */
    TAG_NUMTYPES	    /* must be last */
} tagType;

#ifdef DEBUG

/*  Defines the debugging levels.
 */
enum { DEBUG_VISUAL = 1, DEBUG_CPP = 2, DEBUG_STATUS = 4, DEBUG_OPTION = 8 };

#endif

/*----------------------------------------------------------------------------
 *  Used for describing a statement
 *--------------------------------------------------------------------------*/

/*  This describes the scoping of the current statement.
 */
typedef enum {
    SCOPE_GLOBAL,	    /* no storage class specified */
    SCOPE_STATIC,	    /* static storage class */
    SCOPE_EXTERN,	    /* external storage class */
    SCOPE_TYPEDEF	    /* scoping depends upon context */
} tagScope;

/*  Information about the current tag candidate.
 */
typedef struct {
    long    location;		/* file position of line containing name */
    long    lineNumber;			/* line number of tag */
    char    name[MaxNameLength];	/* the name of the token */
} tagInfo;

/*  Defines the current state of the pre-processor.
 */
typedef struct {
    int	    ungetch;		/* an ungotten character, if any */
    struct {
	enum {
	    DRCTV_NONE,
	    DRCTV_HASH,
	    DRCTV_DEFINE,
	    DRCTV_IF
	} state;
	boolean	accept;		/* is a directive syntatically permitted? */
	tagInfo tag;		/* the name associated with the directive */
	boolean	resolve;	/* must resolve if/else/elif/endif branch */
	int	level;		/* level 0 is not used */
	struct {
	    boolean ignore;
	    boolean pathChosen;
	} ifdef[CppNestingLevel];
    } directive;
} cppState;

/*============================================================================
=   Global variables
============================================================================*/

extern tagFile		TagFile;
extern sourceFile	File;
extern optionValues	Option;
extern cppState		Cpp;

/*============================================================================
=   Function prototypes
============================================================================*/

extern void makeTag __ARGS((const tagInfo *const tag, const tagScope scope,
			    const tagType type));
extern void makeDefineTag __ARGS((const tagInfo *const tag,
				  const tagScope scope));
extern const char *tagTypeName __ARGS((const tagType type));

extern boolean cppOpen __ARGS((const char *const name));
extern void cppClose __ARGS((void));
extern void cppUngetc __ARGS((const int c));
extern int cppGetc __ARGS((void));

extern boolean createTags __ARGS((const int nesting));

extern boolean fileOpen __ARGS((const char *const name));
extern void fileClose __ARGS((void));
extern int fileGetc __ARGS((void));
extern void fileUngetc __ARGS((int c));
extern void getFileLine __ARGS((const long int seek, char *const line_buffer,
				const unsigned int maxLength));
extern void printUsage __ARGS((const char *const error));
extern void freeIgnoreList __ARGS((void));
extern char *const *parseOptions __ARGS((char *const *const argList));
extern void *parseEnvironmentOptions __ARGS((void));

#ifdef EXTERNAL_SORT
extern void externalSortTags __ARGS((const boolean toStdout));
#else
extern void internalSortTags __ARGS((const boolean toStdout));
#endif

#ifdef DEBUG
extern void lineBreak __ARGS((void));
extern void debugOpen __ARGS((const char *const name));
extern void debugPutc __ARGS((const int c, const int level));
extern void debugEntry __ARGS((const tagScope scope, const tagType type, const char *const tagName));
extern void debugCpp __ARGS((const boolean ignore));
extern void clearString __ARGS((char *const string, const int length));
#endif

/* vi:set tabstop=8 shiftwidth=4: */
