mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
eb9350f619
patch by unknown@simplemachines.org r=timeless rs=brendan
961 lines
29 KiB
C++
961 lines
29 KiB
C++
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Dreftool.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Rick Gessner <rick@gessner.com>.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Kenneth Herron <kjh-5727@comcast.net>
|
|
* Bernd Mielke <bmlk@gmx.de>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "nsString.h"
|
|
#include "prio.h"
|
|
#include "plstr.h"
|
|
#include "CScanner.h"
|
|
#include "CCPPTokenizer.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "tokens.h"
|
|
#include "CToken.h"
|
|
#include "stdlib.h"
|
|
#include "nsIFile.h"
|
|
#include "nsILocalFile.h"
|
|
#include "nsISimpleEnumerator.h"
|
|
#include "nsIInputStream.h"
|
|
#include "nsILineInputStream.h"
|
|
#include "nsHashSets.h"
|
|
#include "nsNetUtil.h"
|
|
|
|
//*******************************************************************************
|
|
// Global variables...
|
|
//*******************************************************************************
|
|
bool gVerbose=false;
|
|
bool gShowIncludeErrors=false;
|
|
bool gEmitHTML=false;
|
|
bool gSloppy=false;
|
|
const char *gRootDir;
|
|
const char *gWatchFile;
|
|
|
|
struct WatchLists {
|
|
nsCStringHashSet mTableC, mTableCPP;
|
|
};
|
|
|
|
WatchLists *gWatchLists;
|
|
nsCStringHashSet *gWatchList;
|
|
|
|
class IPattern {
|
|
public:
|
|
virtual CToken* scan(CCPPTokenizer& aTokenizer,int anIndex)=0;
|
|
};
|
|
|
|
|
|
class CPattern {
|
|
public:
|
|
CPattern(const nsAFlatCString& aFilename)
|
|
: mNames(0),
|
|
mSafeNames(0),
|
|
mIndex(0),
|
|
mLineNumber(1),
|
|
mErrCount(0),
|
|
mFilename(aFilename)
|
|
{
|
|
}
|
|
|
|
CToken* matchID(nsDeque& aDeque,const nsAFlatCString& aName){
|
|
int theMax=aDeque.GetSize();
|
|
for(int theIndex=0;theIndex<theMax;theIndex++){
|
|
CToken* theToken=(CToken*)aDeque.ObjectAt(theIndex);
|
|
if(theToken){
|
|
const nsAFlatCString& theName=theToken->getStringValue();
|
|
if(theName==aName) {
|
|
return theToken;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
This gets called when we know we've seen an if statement (at anIndex).
|
|
Look into the parm list (...) for an ID. If you find one, then you know
|
|
it's a conditioned identifier which can be safely ignored during other
|
|
error tests.
|
|
***************************************************************************/
|
|
CToken* findSafeIDInIfStatement(CCPPTokenizer& aTokenizer) {
|
|
CToken* result=0;
|
|
|
|
enum eState {eSkipLparen,eFindID};
|
|
eState theState=eSkipLparen;
|
|
CToken* theToken=0;
|
|
while((theToken=aTokenizer.getTokenAt(++mIndex))) {
|
|
int theType=theToken->getTokenType();
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
char theChar=theString.First();
|
|
|
|
switch(theState) {
|
|
case eSkipLparen:
|
|
//first scan for the lparen...
|
|
switch(theType) {
|
|
case eToken_comment:
|
|
case eToken_whitespace:
|
|
case eToken_newline:
|
|
mLineNumber+=theString.CountChar('\n');
|
|
break;
|
|
|
|
case eToken_operator:
|
|
switch(theChar) {
|
|
case '(':
|
|
theState=eFindID;
|
|
break;
|
|
case ')':
|
|
return 0;
|
|
default:
|
|
break;
|
|
} //switch
|
|
break;
|
|
default:
|
|
break;
|
|
} //switch
|
|
break;
|
|
|
|
//now scan for the identifier...
|
|
case eFindID:
|
|
switch(theType) {
|
|
case eToken_comment:
|
|
case eToken_whitespace:
|
|
case eToken_newline:
|
|
mLineNumber+=theString.CountChar('\n'); break;
|
|
|
|
case eToken_identifier:
|
|
if(matchID(mNames,theString)) {
|
|
return theToken;
|
|
}
|
|
break;
|
|
case eToken_operator:
|
|
switch(theChar) {
|
|
case ')':
|
|
case ';':
|
|
case '{':
|
|
case '(':
|
|
return result;
|
|
default:
|
|
break;
|
|
} //switch
|
|
break;
|
|
default:
|
|
break;
|
|
} //switch
|
|
break;
|
|
|
|
default:
|
|
switch(theType) {
|
|
case eToken_comment:
|
|
case eToken_whitespace:
|
|
case eToken_newline:
|
|
mLineNumber+=theString.CountChar('\n');
|
|
break;
|
|
default:
|
|
break;
|
|
}//switch
|
|
break;
|
|
} //switch
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
This gets called when we know we've seen an NS_ENSURE_TRUE statement (at anIndex).
|
|
Look into the parm list (...) for an ID. If you find one, then you know
|
|
it's a conditioned identifier which can be safely ignored during other
|
|
error tests.
|
|
***************************************************************************/
|
|
CToken* findSafeIDInEnsureTrue(CCPPTokenizer& aTokenizer) {
|
|
|
|
CToken* result=0;
|
|
enum eState {eSkipLparen,eFindID};
|
|
eState theState=eSkipLparen;
|
|
CToken* theToken=0;
|
|
while((theToken=aTokenizer.getTokenAt(++mIndex))) {
|
|
int theType=theToken->getTokenType();
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
char theChar=theString.First();
|
|
|
|
switch(theState) {
|
|
case eSkipLparen:
|
|
//first scan for the lparen...
|
|
switch(theType) {
|
|
case eToken_comment:
|
|
case eToken_whitespace:
|
|
case eToken_newline:
|
|
mLineNumber+=theString.CountChar('\n');
|
|
break;
|
|
|
|
case eToken_operator:
|
|
switch(theChar) {
|
|
case '(':
|
|
theState=eFindID;
|
|
break;
|
|
case ')':
|
|
return 0;
|
|
default:
|
|
break;
|
|
} //switch
|
|
break;
|
|
default:
|
|
break;
|
|
} //switch
|
|
break;
|
|
|
|
//now scan for the identifier...
|
|
case eFindID:
|
|
switch(theType) {
|
|
case eToken_comment:
|
|
case eToken_whitespace:
|
|
case eToken_newline:
|
|
mLineNumber+=theString.CountChar('\n'); break;
|
|
|
|
case eToken_identifier:
|
|
if(matchID(mNames,theString)) {
|
|
return theToken;
|
|
}
|
|
break;
|
|
case eToken_operator:
|
|
switch(theChar) {
|
|
case ')':
|
|
case ';':
|
|
case '{':
|
|
case '(':
|
|
return result;
|
|
default:
|
|
break;
|
|
} //switch
|
|
break;
|
|
default:
|
|
break;
|
|
} //switch
|
|
break;
|
|
|
|
default:
|
|
switch(theType) {
|
|
case eToken_comment:
|
|
case eToken_whitespace:
|
|
case eToken_newline:
|
|
mLineNumber+=theString.CountChar('\n');
|
|
break;
|
|
default:
|
|
break;
|
|
}//switch
|
|
break;
|
|
} //switch
|
|
}
|
|
return result;
|
|
}
|
|
/***************************************************************************
|
|
We're looking for the variable that is new created by a new
|
|
***************************************************************************/
|
|
CToken* findNewVariable(CCPPTokenizer& aTokenizer, int anIndex){
|
|
CToken* theToken=0;
|
|
while((theToken=aTokenizer.getTokenAt(--anIndex))){
|
|
if(theToken) {
|
|
int theType=theToken->getTokenType();
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
switch(theType){
|
|
case eToken_semicolon:
|
|
return 0;
|
|
case eToken_operator:
|
|
if (!StringBeginsWith(theString,NS_LITERAL_CSTRING("="))){
|
|
return 0;
|
|
}
|
|
break;
|
|
case eToken_identifier:
|
|
if (StringBeginsWith(theString,NS_LITERAL_CSTRING("return"))){
|
|
return 0;
|
|
}
|
|
return theToken;
|
|
default:
|
|
break;
|
|
}
|
|
} //if
|
|
} //while
|
|
return 0;
|
|
}
|
|
void check_unsafe_ID(CToken* theToken){
|
|
}
|
|
void check_save_ID(CToken* theToken){
|
|
}
|
|
/***************************************************************************
|
|
We're looking for the pattern foo-> without the preceeding (conditional) if():
|
|
Assume that the given id (aName) exists in the tokenizer at anIndex.
|
|
|
|
if(foo)
|
|
foo->
|
|
***************************************************************************/
|
|
CToken* findPtrDecl(CCPPTokenizer& aTokenizer,int anIndex,PRUnichar& anOpChar,bool allocRequired) {
|
|
|
|
enum eState {eFindNew,eFindType,eFindPunct,eFindStar};
|
|
CToken* theToken=0;
|
|
CToken* theID=0;
|
|
eState theState=eFindStar;
|
|
|
|
while((theToken=aTokenizer.getTokenAt(++anIndex))){
|
|
if(theToken) {
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
int theType=theToken->getTokenType();
|
|
|
|
anOpChar=theString.First();
|
|
switch(theType){
|
|
|
|
case eToken_semicolon:
|
|
case eToken_operator:
|
|
switch(theState) {
|
|
case eFindStar:
|
|
if('*'!=anOpChar)
|
|
return 0;
|
|
theState=eFindType;
|
|
break;
|
|
case eFindPunct:
|
|
switch(anOpChar) {
|
|
case '=':
|
|
if(!allocRequired)
|
|
return theID;
|
|
theState=eFindNew;
|
|
break;
|
|
case ';':
|
|
return (allocRequired) ? 0 : theID;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case eToken_identifier:
|
|
switch(theState){
|
|
case eFindType:
|
|
theID=theToken;
|
|
theState=eFindPunct;
|
|
break;
|
|
case eFindNew:
|
|
if (gWatchList->Contains(theString))
|
|
return theID;
|
|
return 0;
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}//if
|
|
}//while
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
We're looking for the pattern *foo.
|
|
***************************************************************************/
|
|
bool hasSimpleDeref(CCPPTokenizer& aTokenizer,int anIndex) {
|
|
bool result=false;
|
|
|
|
int theMin=anIndex-3;
|
|
for(int theIndex=anIndex-1;theIndex>theMin;theIndex--){
|
|
CToken* theToken=aTokenizer.getTokenAt(theIndex);
|
|
if(theToken) {
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
int theType=theToken->getTokenType();
|
|
char theChar=theString.First();
|
|
if(eToken_operator==theType) {
|
|
if('*'==theChar){
|
|
result=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(result) {
|
|
int theMax=anIndex+3;
|
|
for(int theIndex=anIndex;theIndex<theMax;theIndex++){
|
|
CToken* theToken=aTokenizer.getTokenAt(theIndex);
|
|
if(theToken) {
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
int theType=theToken->getTokenType();
|
|
char theChar=theString.First();
|
|
if(eToken_operator==theType) {
|
|
if((','==theChar) || (')'==theChar)){
|
|
result=false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/***************************************************************************
|
|
We're looking for the pattern foo-> without the preceeding (conditional) if():
|
|
Assume that the given id (aName) exists in the tokenizer at anIndex.
|
|
|
|
if(foo)
|
|
foo->
|
|
***************************************************************************/
|
|
bool hasUnconditionedDeref(const nsAFlatCString& aName,CCPPTokenizer& aTokenizer,int anIndex) {
|
|
bool result=false;
|
|
|
|
enum eState {eFailure,eFindIf,eFindDeref};
|
|
eState theState=eFindDeref;
|
|
int theIndex=anIndex+1;
|
|
|
|
while(eFailure!=theState){
|
|
CToken* theToken=aTokenizer.getTokenAt(theIndex);
|
|
if(theToken) {
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
int theType=theToken->getTokenType();
|
|
char theChar=theString.First();
|
|
switch(theState){
|
|
case eFindDeref:
|
|
switch(theType) {
|
|
case eToken_whitespace:
|
|
case eToken_comment:
|
|
case eToken_newline:
|
|
theIndex++;
|
|
break;
|
|
default:
|
|
if (StringBeginsWith(theString, NS_LITERAL_CSTRING("->"))) {
|
|
theIndex=anIndex-1; //now start looking backwards...
|
|
theState=eFindIf;
|
|
}
|
|
else theState=eFailure;
|
|
}//switch
|
|
break;
|
|
|
|
case eFindIf:
|
|
switch(theType) {
|
|
case eToken_whitespace:
|
|
case eToken_comment:
|
|
case eToken_newline:
|
|
theIndex--;
|
|
break;
|
|
case eToken_semicolon:
|
|
return true;
|
|
break;
|
|
|
|
case eToken_operator:
|
|
switch(theChar){
|
|
case '{':
|
|
case '+':
|
|
case '-':
|
|
case '=':
|
|
case '*':
|
|
case '%':
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
theIndex-=1;
|
|
break;
|
|
case eToken_identifier:
|
|
if (StringBeginsWith(theString, NS_LITERAL_CSTRING("if"))) {
|
|
return false;
|
|
}
|
|
if (theString.Equals(aName)) {
|
|
return false;
|
|
}
|
|
if (StringBeginsWith(theString, NS_LITERAL_CSTRING("while"))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
default:
|
|
theIndex--;
|
|
} //switch
|
|
break;
|
|
case eFailure:
|
|
break;
|
|
}//switch
|
|
}//if
|
|
else theState=eFailure;
|
|
}//while
|
|
|
|
return result;
|
|
}
|
|
|
|
void skipSemi(CCPPTokenizer& aTokenizer) {
|
|
CToken* theToken=0;
|
|
while((theToken=aTokenizer.getTokenAt(++mIndex))){
|
|
int theType=theToken->getTokenType();
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
char theChar=theString.First();
|
|
|
|
switch(theType){
|
|
case eToken_semicolon:
|
|
return;
|
|
case eToken_operator:
|
|
if(('}'==theChar) || (';'==theChar))
|
|
return;
|
|
break;
|
|
case eToken_newline:
|
|
case eToken_comment:
|
|
case eToken_whitespace:
|
|
mLineNumber+=theString.CountChar('\n');
|
|
default:
|
|
break;
|
|
} //switch
|
|
}//while
|
|
}
|
|
|
|
void OutputMozillaFilename() {
|
|
mFilename.ReplaceChar('\\','/');
|
|
PRInt32 pos=mFilename.RFind("/") + 1;
|
|
nsCAutoString entries,repository;
|
|
{
|
|
nsCAutoString cvsbase(
|
|
Substring(mFilename, 0, pos) +
|
|
NS_LITERAL_CSTRING("CVS/")
|
|
);
|
|
entries=cvsbase+NS_LITERAL_CSTRING("Entries");
|
|
repository=cvsbase+NS_LITERAL_CSTRING("Repository");
|
|
}
|
|
nsCAutoString filename(
|
|
Substring(mFilename, pos)
|
|
);
|
|
char cvspath[1024], cvsfilename[256], version[10], prefix;
|
|
bool found = false;
|
|
{
|
|
fstream cvsrecord (entries.get(),ios::in);
|
|
if (cvsrecord.is_open()) {
|
|
while (!cvsrecord.eof()) {
|
|
cvsrecord.get(prefix);
|
|
if (prefix=='/') {
|
|
cvsrecord.getline(cvsfilename,sizeof cvsfilename, '/');
|
|
if (found = !strcmp(filename.get(), cvsfilename))
|
|
break;
|
|
}
|
|
cvsrecord.ignore(256,'\n');
|
|
}
|
|
if (found)
|
|
cvsrecord.getline(version,sizeof version, '/');
|
|
}
|
|
}
|
|
{
|
|
fstream cvsrecord (repository.get(),ios::in);
|
|
if (cvsrecord.is_open())
|
|
cvsrecord.getline(cvspath,sizeof cvspath);
|
|
else
|
|
cvspath[0]=0;
|
|
if (cvspath[0])
|
|
filename =
|
|
NS_LITERAL_CSTRING("/") +
|
|
nsDependentCString(cvspath) +
|
|
NS_LITERAL_CSTRING("/") +
|
|
filename;
|
|
else
|
|
filename = mFilename;
|
|
}
|
|
pos=filename.Find("mozilla/");
|
|
const char* buf=filename.get();
|
|
|
|
if(-1<pos) {
|
|
buf+=pos+(found?0:8);
|
|
|
|
if(gEmitHTML)
|
|
fprintf(stdout, "<br><a href=\"");
|
|
if (found)
|
|
fprintf(stdout,
|
|
"http://bonsai.mozilla.org/cvsblame.cgi?file=%s"
|
|
"&rev=%s"
|
|
"&mark=%d"
|
|
"#%d",
|
|
buf,
|
|
version,
|
|
mLineNumber,
|
|
mLineNumber - 5);
|
|
else if (gEmitHTML)
|
|
fprintf(stdout,
|
|
"http://lxr.mozilla.org/mozilla/source/%s#%d",
|
|
buf,
|
|
mLineNumber);
|
|
else
|
|
fprintf(stdout,
|
|
"%s:%d",
|
|
buf,
|
|
mLineNumber);
|
|
if (gEmitHTML)
|
|
fprintf(stdout,
|
|
"\">%s:%d</a>",
|
|
buf,
|
|
mLineNumber);
|
|
}
|
|
else {
|
|
fprintf(stdout, "%s", buf);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
This method iterates the tokens (in tokenizer) looking for statements.
|
|
The statements are then decoded to find pointer variables. If they get a
|
|
new call, then they're recorded in mNames. If they're in an IF(), then
|
|
they're recording in mSafeNames. We also look for simple dereferences.
|
|
When we find one, we look it up in mSafeNames. If it's not there, we have
|
|
a potential deref error.
|
|
***************************************************************************/
|
|
void scan(CCPPTokenizer& aTokenizer){
|
|
int theMax=aTokenizer.getCount();
|
|
int theIDCount=mNames.GetSize();
|
|
int theSafeIDCount=mSafeNames.GetSize();
|
|
|
|
while(mIndex<theMax){
|
|
CToken* theToken=aTokenizer.getTokenAt(mIndex);
|
|
int theType=theToken->getTokenType();
|
|
const nsAFlatCString& theString=theToken->getStringValue();
|
|
if (!theString.IsEmpty())
|
|
switch(theType){
|
|
case eToken_operator:
|
|
{
|
|
char theChar=theString.First();
|
|
switch(theChar){
|
|
case '{':
|
|
mIndex++;
|
|
scan(aTokenizer);
|
|
break;
|
|
case '}':
|
|
//before returning, let's remove the new identifiers...
|
|
while(mNames.GetSize()>theIDCount){
|
|
mNames.Pop();
|
|
}
|
|
while(mSafeNames.GetSize()>theSafeIDCount){
|
|
mSafeNames.Pop();
|
|
}
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case eToken_comment:
|
|
case eToken_newline:
|
|
case eToken_whitespace:
|
|
mLineNumber+=theString.CountChar('\n');
|
|
break;
|
|
|
|
//If it's an ID, then we're looking for 3 patterns:
|
|
// 1. ID* ID...;
|
|
// 2. *ID=ID;
|
|
// 3. ID->XXX;
|
|
case eToken_identifier:
|
|
{
|
|
PRUnichar theOpChar=0;
|
|
if((theToken=findPtrDecl(aTokenizer,mIndex,theOpChar,true))) {
|
|
//we found ID* ID; (or) ID* ID=...;
|
|
mNames.Push(theToken);
|
|
mIndex+=2;
|
|
skipSemi(aTokenizer);
|
|
}
|
|
|
|
else if (StringBeginsWith(theString, NS_LITERAL_CSTRING("if"))) {
|
|
CToken* theSafeID=findSafeIDInIfStatement(aTokenizer);
|
|
if(theSafeID) {
|
|
mSafeNames.Push(theSafeID);
|
|
theSafeIDCount++;
|
|
}
|
|
}
|
|
else if (StringBeginsWith(theString, NS_LITERAL_CSTRING("NS_ENSURE"))) {
|
|
CToken* theSafeID=findSafeIDInEnsureTrue(aTokenizer);
|
|
if (theSafeID) {
|
|
mSafeNames.Push(theSafeID);
|
|
theSafeIDCount++;
|
|
}
|
|
}
|
|
else if (!gSloppy &&
|
|
(theString.Equals(NS_LITERAL_CSTRING("new")))) {
|
|
CToken* theToken=findNewVariable(aTokenizer,mIndex);
|
|
if(theToken) {
|
|
mNames.Push(theToken);
|
|
theIDCount++;
|
|
CToken* lastSafe=(CToken*)mSafeNames.PeekFront();
|
|
if(lastSafe){
|
|
if(theToken==lastSafe){
|
|
mSafeNames.Pop();
|
|
theSafeIDCount--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
CToken* theMatch=matchID(mNames,theString);
|
|
if(theMatch){
|
|
if((hasUnconditionedDeref(theString,aTokenizer,mIndex)) ||
|
|
(hasSimpleDeref(aTokenizer,mIndex))){
|
|
CToken* theSafeID=matchID(mSafeNames,theString);
|
|
if(theSafeID) {
|
|
const nsAFlatCString& s1=theSafeID->getStringValue();
|
|
if(s1.Equals(theString))
|
|
break;
|
|
}
|
|
//dump the name out in LXR format
|
|
|
|
OutputMozillaFilename();
|
|
|
|
fprintf(stdout,
|
|
" Deref-error: \"%s\"\n",
|
|
theString.get());
|
|
mErrCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
mIndex++;
|
|
}
|
|
}
|
|
|
|
nsDeque mNames;
|
|
nsDeque mSafeNames;
|
|
int mIndex;
|
|
int mLineNumber;
|
|
int mErrCount;
|
|
nsCString mFilename;
|
|
};
|
|
|
|
void ScanFile(nsIFile *aFilename,int& aLineCount,int& anErrCount) {
|
|
nsCAutoString theCStr;
|
|
if (NS_FAILED(aFilename->GetNativePath(theCStr)) || theCStr.IsEmpty())
|
|
return;
|
|
fstream input(theCStr.get(),ios::in);
|
|
CScanner theScanner(input);
|
|
CCPPTokenizer theTokenizer;
|
|
theTokenizer.tokenize(theScanner);
|
|
|
|
CPattern thePattern(theCStr);
|
|
thePattern.scan(theTokenizer);
|
|
aLineCount+=thePattern.mLineNumber;
|
|
anErrCount+=thePattern.mErrCount;
|
|
}
|
|
|
|
|
|
void IteratePath(nsIFile *aPath,
|
|
int& aFilecount,int& aLinecount,int& anErrcount)
|
|
{
|
|
PRBool is = PR_FALSE;
|
|
nsXPIDLCString name;
|
|
if (NS_FAILED(aPath->GetNativeLeafName(name)) ||
|
|
name.IsEmpty())
|
|
return;
|
|
if (NS_FAILED(aPath->IsSymlink(&is)) || is ||
|
|
NS_FAILED(aPath->IsSpecial(&is)) || is) {
|
|
fprintf(stderr, "Skipping: %s\n", name.get());
|
|
} else if (NS_SUCCEEDED(aPath->IsDirectory(&is)) && is &&
|
|
!name.Equals(NS_LITERAL_CSTRING("CVS"))) {
|
|
nsCOMPtr<nsISimpleEnumerator> dirList;
|
|
if (NS_SUCCEEDED(aPath->GetDirectoryEntries(getter_AddRefs(dirList)))) {
|
|
while (NS_SUCCEEDED(dirList->HasMoreElements(&is)) && is) {
|
|
nsCOMPtr<nsISupports> thing;
|
|
nsCOMPtr<nsIFile> path;
|
|
if (NS_SUCCEEDED(dirList->GetNext(getter_AddRefs(thing))) &&
|
|
(path = do_QueryInterface(thing))) {
|
|
IteratePath(path, aFilecount, aLinecount, anErrcount);
|
|
}
|
|
}
|
|
}
|
|
} else if (NS_SUCCEEDED(aPath->IsFile(&is)) && is) {
|
|
if (StringEndsWith(name, NS_LITERAL_CSTRING(".cpp"))) {
|
|
aFilecount++;
|
|
gWatchList=&gWatchLists->mTableCPP;
|
|
ScanFile(aPath,aLinecount,anErrcount);
|
|
} else if (StringEndsWith(name, NS_LITERAL_CSTRING(".c"))) {
|
|
aFilecount++;
|
|
gWatchList=&gWatchLists->mTableC;
|
|
ScanFile(aPath,aLinecount,anErrcount);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ConsumeArguments(int argc, char* argv[]) {
|
|
|
|
for(int index=1;index<argc;index++){
|
|
switch(argv[index][0]) {
|
|
case '-':
|
|
case '/':
|
|
switch(argv[index][1]){
|
|
case 'd':
|
|
case 'D': // Starting directory
|
|
gRootDir = argv[++index];
|
|
break;
|
|
|
|
case 'f':
|
|
case 'F': // list of functions to complain about
|
|
gWatchFile = argv[++index];
|
|
break;
|
|
|
|
case 'h':
|
|
case 'H': // Output HTML with lxr.mozilla.org links
|
|
gEmitHTML=true;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
gSloppy=true; // Don't look for member variables
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr,
|
|
"Usage: dreftool [-h] [-s] [-f wordlist] [-d sourcepath]\n"
|
|
"-d path to root of source tree\n"
|
|
"-f file containing a list of functions which can return null\n"
|
|
"-h emit as HTML\n"
|
|
"-s sloppy mode: no warnings if member variables are used unsafe\n"
|
|
);
|
|
exit(1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char* argv[]){
|
|
|
|
gRootDir = ".";
|
|
gWatchFile = "nullfunc.lst";
|
|
ConsumeArguments(argc,argv);
|
|
|
|
int fileCount=0, lineCount=0, errCount=0;
|
|
|
|
if(gEmitHTML) {
|
|
fprintf(stdout, "<html><title>DRefTool analysis");
|
|
if (gRootDir[0]!='.' || gRootDir[1])
|
|
fprintf(stdout, " for %s", gRootDir);
|
|
fprintf(stdout,"</title><body>\n");
|
|
}
|
|
|
|
{
|
|
WatchLists myWatchList;
|
|
if (NS_FAILED(myWatchList.mTableC.Init(16)) ||
|
|
NS_FAILED(myWatchList.mTableCPP.Init(16)))
|
|
return -1;
|
|
if (!gWatchFile) {
|
|
const char* wordList[] = {
|
|
"malloc",
|
|
"calloc",
|
|
"PR_Malloc",
|
|
"PR_Calloc",
|
|
"ToNewCString",
|
|
"ToNewUTF8String",
|
|
"ToNewUnicode",
|
|
"UTF8ToNewUnicode",
|
|
0
|
|
};
|
|
int i=0;
|
|
while (const char* word=wordList[i++]) {
|
|
if (NS_FAILED(myWatchList.mTableC.Put(nsDependentCString(word))) ||
|
|
NS_FAILED(myWatchList.mTableCPP.Put(nsDependentCString(word))))
|
|
return -1;
|
|
}
|
|
} else {
|
|
nsCOMPtr<nsILocalFile> file;
|
|
if (NS_FAILED(NS_NewNativeLocalFile(nsDependentCString(gWatchFile), PR_TRUE, getter_AddRefs(file))))
|
|
return -2;
|
|
nsCOMPtr<nsIInputStream> fileInputStream;
|
|
if (NS_FAILED(NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), file)))
|
|
return -3;
|
|
nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream);
|
|
if (!lineInputStream)
|
|
return -4;
|
|
|
|
nsCAutoString buffer;
|
|
PRBool isMore = PR_TRUE;
|
|
fprintf(stdout, "Function calls that will be checked for errors:\n");
|
|
if (gEmitHTML)
|
|
fprintf(stdout, "<ul>\n");
|
|
while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
|
|
if (NS_FAILED(myWatchList.mTableC.Put(buffer)) ||
|
|
NS_FAILED(myWatchList.mTableCPP.Put(buffer)))
|
|
return -1;
|
|
if (gEmitHTML)
|
|
fprintf(stdout, "<li>");
|
|
fprintf(stdout, "%s\n", buffer.get());
|
|
}
|
|
}
|
|
if (NS_FAILED(myWatchList.mTableCPP.Put(NS_LITERAL_CSTRING("new"))))
|
|
return -1;
|
|
if (gEmitHTML)
|
|
fprintf(stdout, "</ul>\n");
|
|
fprintf(stdout, "C++ only:\n");
|
|
if (gEmitHTML)
|
|
fprintf(stdout, "<ul>\n<li>");
|
|
fprintf(stdout, "new\n");
|
|
if (gEmitHTML)
|
|
fprintf(stdout, "</ul>\n");
|
|
fprintf(stdout, "\n");
|
|
gWatchLists = &myWatchList;
|
|
nsCOMPtr<nsIFile> root;
|
|
nsCOMPtr<nsILocalFile> localroot;
|
|
if (NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(gRootDir), PR_TRUE, getter_AddRefs(localroot))) &&
|
|
(root = do_QueryInterface(localroot)) &&
|
|
NS_SUCCEEDED(root->Normalize()))
|
|
IteratePath(root,fileCount,lineCount,errCount);
|
|
else
|
|
fprintf(stderr, "Could not find path: %s\n", gRootDir);
|
|
}
|
|
|
|
if(gEmitHTML) {
|
|
fprintf(stdout, "<PRE>\n");
|
|
}
|
|
|
|
fprintf(stdout, "\nSummary");
|
|
if (gRootDir[0]!='.' || gRootDir[1])
|
|
fprintf(stdout, " for %s", gRootDir);
|
|
fprintf(stdout,
|
|
": \n"
|
|
"===============================\n"
|
|
"Files: %d\n"
|
|
"Lines: %d\n"
|
|
"Errors: %d\n",
|
|
fileCount,
|
|
lineCount,
|
|
errCount);
|
|
|
|
if(gEmitHTML) {
|
|
fprintf(stdout, "</PRE>\n</body></html>\n");
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|