libcm/cmTagFile.c
2012-10-29 20:52:39 -07:00

295 lines
6.5 KiB
C

#include "cmPrefix.h"
#include "cmGlobal.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmLinkedHeap.h"
#include "cmFile.h"
#include "cmTagFile.h"
cmTfH_t cmTfNullHandle = cmSTATIC_NULL_HANDLE;
typedef struct
{
cmErr_t err;
cmFileH_t fH;
cmLHeapH_t lH;
cmTfTag_t* tagArray;
unsigned tagCnt;
unsigned tagAllocCnt;
} cmTf_t;
cmTf_t* _cmTfHandleToPtr( cmTfH_t h )
{
cmTf_t* p = (cmTf_t*)h.h;
assert( p != NULL );
return p;
}
const cmChar_t* _cmTfFlagsToLabel( unsigned flags )
{
switch(flags)
{
case kFuncTfFl: return "p";
case kEnumTfFl: return "e";
case kMacroTfFl: return "m";
case kTypedefTfFl: return "t";
case kFieldTfFl: return "f";
case kExternTfFl: return "x";
default:
{ assert(0); }
}
return "<unknown>";
}
void _cmTfPrintTag(cmRpt_t* rpt, const cmTfTag_t* r )
{
cmRptPrintf(rpt,"%s %5i %s\n",_cmTfFlagsToLabel(r->flags),r->line,r->label);
}
cmTfRC_t _cmTfCloseFile( cmTf_t* p )
{
cmTfRC_t rc = kOkTfRC;
if( cmFileIsValid(p->fH) )
cmFileClose(&p->fH);
if( cmLHeapIsValid(p->lH) )
cmLHeapDestroy(&p->lH);
cmMemFree(p);
return rc;
}
cmTfRC_t _cmTfSyntaxErr( cmTf_t* p, const cmChar_t* fn, unsigned line, const cmChar_t* msg )
{
return cmErrMsg(&p->err,kSyntaxErrTfRC,"Syntax error: %s line:%i in file:%s\n",msg,line,cmStringNullGuard(fn));
}
cmTfRC_t _cmTfParseLine( cmTf_t* p, const cmChar_t* fn, unsigned line, cmChar_t* buf, unsigned bufCharCnt )
{
cmTfRC_t rc = kOkTfRC;
const cmChar_t lineLabel[] = "line:";
unsigned lineLabelCharCnt = strlen(lineLabel);
char* s;
cmTfTag_t r;
memset(&r,0,sizeof(r));
// if the line is empty
if( buf==NULL || bufCharCnt == 0 )
return rc;
// eat leading white space
while( *buf && isspace(*buf) )
++buf;
// if the line is now empty
if( *buf == 0 )
return rc;
// skip the file header lines - which begin with a '!' character
if( *buf == '!' )
return rc;
// locate the end of the first (tag) field
if( (s= strchr(buf,'\t')) == NULL )
return _cmTfSyntaxErr(p,fn,line,"No tag label was found.");
// zero terminate and copy construct the tag field into r.label
*s = 0;
r.label = cmLhAllocStr( p->lH, buf );
buf = s + 1; // buf now points to the file name
if( (s = strchr(buf,'\t')) == NULL )
return _cmTfSyntaxErr(p,fn,line,"No file name field found.");
buf = s + 1; // buf now points to the 'EX" field
if( (s = strchr(buf,'\t')) == NULL )
return _cmTfSyntaxErr(p,fn,line,"No 'EX' field found.");
buf = s + 1; // buf now points to the 'kind' field
//
// Use: 'ctags --list-kinds=c' to list all of the 'kind' character flags.
//
switch( *buf )
{
case 'd': // macro
r.flags |= kMacroTfFl;
break;
case 'e': // enum value
r.flags |= kEnumTfFl;
break;
case 'p': // function prototype
r.flags |= kFuncTfFl;
break;
case 't': // typedef
r.flags |= kTypedefTfFl;
break;
case 'm': // member
r.flags |= kFieldTfFl;
break;
case 'x': // externs and forward decl's
r.flags |= kExternTfFl;
break;
default: // unrecognized type
return rc;
}
if( (s = strchr(buf,'\t')) == NULL )
return _cmTfSyntaxErr(p,fn,line,"No 'kind' field found.");
buf = s + 1; // buf now points to the 'line' field
//
if( strncmp(buf,lineLabel,lineLabelCharCnt) != 0 )
return _cmTfSyntaxErr(p,fn,line,"No 'line' field found.");
buf += lineLabelCharCnt; // buf now points to the number part of the line field
// parse the line number
if( sscanf(buf,"%i",&r.line) != 1 )
return _cmTfSyntaxErr(p,fn,line,"Line number parse failed.");
// store the tag record
p->tagArray[ p->tagCnt ] = r;
++p->tagCnt;
return rc;
}
cmTfRC_t cmTfOpenFile( cmCtx_t* ctx, cmTfH_t* hp, const cmChar_t* fn )
{
cmTfRC_t rc = kOkTfRC;
cmTf_t* p = cmMemAllocZ(cmTf_t,1);
cmChar_t* bufPtr = NULL;
unsigned lineCnt = 0;
cmErrSetup(&p->err,&ctx->rpt,"Tag File");
// create the internal linked heap
if( cmLHeapIsValid(p->lH = cmLHeapCreate(8192,ctx)) == false )
{
rc = cmErrMsg(&p->err,kLHeapFailTfRC,"The internal link heap create failed.");
goto errLabel;
}
// open the file
if( cmFileOpen( &p->fH, fn, kReadFileFl, &ctx->rpt ) != kOkFileRC )
{
rc = cmErrMsg(&p->err,kFileFailTfRC,"The tag file '%s' could not be opened.",cmStringNullGuard(fn));
goto errLabel;
}
// get a count of the lines in the file
if( cmFileLineCount( p->fH, &p->tagAllocCnt ) != kOkFileRC || p->tagAllocCnt == 0 )
{
rc = cmErrMsg(&p->err,kFileInvalidTfRC,"The tag file '%s' was invalid or empty.",cmStringNullGuard(fn));
goto errLabel;
}
// allocate the tag array
p->tagArray = cmLhAllocZ(p->lH,cmTfTag_t,p->tagAllocCnt);
while( 1 )
{
unsigned bufByteCnt = 0;
cmFileRC_t frc;
// read a line from the file
if((frc = cmFileGetLineAuto(p->fH, &bufPtr, &bufByteCnt )) != kOkFileRC )
{
if( cmFileEof(p->fH)==false )
rc = cmErrMsg(&p->err,kFileFailTfRC,"File read failed on tag line:%i in '%s'\n",lineCnt+1,cmStringNullGuard(fn));
break;
}
// parse a file line
if((rc = _cmTfParseLine(p,fn,lineCnt+1,bufPtr,bufByteCnt)) != kOkTfRC )
break;
++lineCnt;
}
cmMemFree(bufPtr);
hp->h = p;
errLabel:
if( rc != kOkTfRC )
_cmTfCloseFile(p);
return rc;
}
cmTfRC_t cmTfCloseFile( cmTfH_t* hp )
{
cmTfRC_t rc = kOkTfRC;
if( hp == NULL || cmTfIsValid(*hp) == false )
return kOkTfRC;
cmTf_t* p = _cmTfHandleToPtr(*hp);
if((rc = _cmTfCloseFile(p)) != kOkTfRC )
return rc;
hp->h = NULL;
return rc;
}
bool cmTfIsValid( cmTfH_t h )
{ return h.h != NULL; }
unsigned cmTfCount( cmTfH_t h )
{
cmTf_t* p = _cmTfHandleToPtr(h);
return p->tagCnt;
}
const cmTfTag_t* cmTfRecd( cmTfH_t h, unsigned index )
{
cmTf_t* p = _cmTfHandleToPtr(h);
assert( index < p->tagCnt );
return p->tagArray + index;
}
cmTfRC_t cmTfReport( cmTfH_t h, cmRpt_t* rpt )
{
unsigned i;
cmTf_t* p = _cmTfHandleToPtr(h);
for(i=0; i<p->tagCnt; ++i)
{
cmRptPrintf(rpt,"%5i ",i);
_cmTfPrintTag(rpt, p->tagArray + i );
}
return kOkTfRC;
}
cmTfRC_t cmTfTest( cmCtx_t* ctx, const cmChar_t* fn )
{
cmTfRC_t rc = kOkTfRC;
cmTfH_t h = cmTfNullHandle;
if((rc = cmTfOpenFile(ctx,&h,fn)) == kOkTfRC )
{
cmTfReport(h,&ctx->rpt);
rc = cmTfCloseFile(&h);
}
return rc;
}