blob: 348e6572dc95088533a9e0034324935a2037ae44 [file] [log] [blame]
// Callstacker.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <string>
#include <map>
#include <vector>
using namespace std;
// can't delete, only add files repository!
class SkSourceDb {
public:
SkSourceDb(const char* szBaseSrcPath, const char* szLightSymbolsDbFile) {
this->baseSrcPath = szBaseSrcPath;
this->lightSymbolsDbFile = szLightSymbolsDbFile;
nextId = 1;
}
const string& getBaseSrcPath() const {
return baseSrcPath;
}
string GetStoredFilename(const string& filename) {
string base = filename.substr(0, baseSrcPath.length());
if (base != baseSrcPath) {
return "";
}
string relative = filename.substr(baseSrcPath.length());
char tmp[10000];
strcpy(tmp, relative.c_str()); // insecure
char* sz = tmp;
while (*sz) {
if (*sz == '\\') *sz = '/';
sz++;
}
sz = tmp;
if (*sz == '/') sz++;
return string(sz);
}
int obtainFileId(const string& filename) {
string stored = GetStoredFilename(filename);
if (stored.empty()) {
return -1;
}
if (filenames.find(stored) == filenames.end()) {
int id = nextId;
nextId++;
filenames[stored] = id;
return id;
} else {
return filenames[stored];
}
}
static void Load(char* szFileName, SkSourceDb** ret, const char* whereToSave, const char* szBaseSrcPath) {
char szLine[10000];
FILE* file = fopen(szFileName, "rt");
if (file == NULL) {
*ret = NULL;
return;
}
const char* trimed;
SkSourceDb* db = new SkSourceDb(szBaseSrcPath, whereToSave == NULL ? szFileName : whereToSave);
map<int, string> ids;
int id;
while (true) {
id = -1;
if (fscanf(file, "%i", &id) == 0) break;
if (id == -1) break;
*szLine = '\0';
fgets(szLine, 10000, file);
trimed = trim(szLine);
if (id < 0 || ids[id] != "") {
printf("fatal error: duplicate value for id = %i, existing = \"%s\", new = \"%s\"\n", id, ids[id].c_str(), trimed);
exit(-1);
}
if (trimed == NULL || *trimed == '\0') {
printf("fatal error: no valuefor id = %i\n", id);
exit(-1);
}
if (db->filenames.find(trimed) != db->filenames.end()) {
printf("fatal error: duplicate id for same file: file = %s, existing id = %i, new id = %i\n", trimed, db->filenames[trimed], id);
// exit(-1);
}
string value = trimed;
ids[id] = value;
db->filenames[value] = id;
if (db->nextId <= id) {
db->nextId = id + 1;
}
}
*ret = db;
}
// dumb comit, smarter: use append
void commit() {
save(lightSymbolsDbFile.c_str());
}
private:
static const char* trim(char* sz) {
if (sz == NULL) return NULL;
while (*sz == ' ' || *sz == '\t' || *sz == '\r' || *sz == '\n' || *sz == ',')
sz++;
if (*sz == '\0') return sz;
int len = strlen(sz);
char* start = sz;
sz = sz + (len - 1);
while (sz >= start && (*sz == ' ' || *sz == '\t' || *sz == '\r' || *sz == '\n' || *sz == ',')) {
*sz = '\0';
sz--;
}
return start;
}
void save(const char* szFilename) {
char szLine[10000];
FILE* file = fopen(szFilename, "wt");
map<string, int>::const_iterator itr;
for(itr = filenames.begin(); itr != filenames.end(); ++itr){
fprintf(file, "%i, %s\n", (*itr).second, (*itr).first.c_str());
}
fclose(file);
}
string baseSrcPath;
string lightSymbolsDbFile;
map<string, int> filenames;
int nextId;
};
SkSourceDb* source_db = NULL;
bool endsWith(const char* who, const char* what) {
int a = strlen(who);
int b = strlen(what);
return stricmp(who + a - b, what) == 0; // insecure
}
bool sourceFile(const char* szFileName) {
return endsWith(szFileName, ".h") || endsWith(szFileName, ".c") || endsWith(szFileName, ".cpp") || endsWith(szFileName, ".cc");
}
// "
// //
// /*
class CppState {
public:
CppState() : line(1), inComment(false), inLineComment(false), inDoubleQuote(false), inSingleQuote(false), isEscaping(false), commentEnding(false), commentMightBeStarting(false) {
}
void apply(int ch) {
if (ch == '\n') {
line++;
if (inLineComment) inLineComment = false;
}
if (inLineComment) {
return;
}
if (commentMightBeStarting) {
commentMightBeStarting = false;
if (ch == '*') {
inComment = true;
} else if (ch == '/') {
inLineComment = true;
} else {
add('/');//previously we has / but was not pushed on tokens
newToken();//
}
}
if (inSingleQuote) {
if (isEscaping)
isEscaping = false;
else if (ch == '\\')
isEscaping = true;
else if (ch == '\'') {
inSingleQuote = false;
newToken();
pushToken("__SINGLE_QUOTE__");
newToken();
}
return;
} else if (inDoubleQuote) {
if (isEscaping)
isEscaping = false;
else if (ch == '\\')
isEscaping = true;
else if (ch == '\"') {
inDoubleQuote = false;
newToken();
pushToken("__DOUBLE_QUOTE__");
newToken();
}
return;
} else if (inComment) {
if (ch == '*') {
commentEnding = true;
} else if (ch == '/') {
inComment = false;
commentEnding = false;
} else {
commentEnding = false;
}
return;
}
switch (ch) {
case '\'':
newToken();
inSingleQuote = true;
return;
case '\"':
newToken();
inDoubleQuote = true;
return;
case '/':
newToken();
commentMightBeStarting = true;
return;
}
if (isspace(ch)) {
newToken();
} else if (tokenDelimiter(ch)) {
newToken();
if (isSingleCharToken(ch)) {
add(ch);
newToken();
}
} else if (isTokenable(ch)) {
add(ch);
} else {
printf("undexpected ... %c", (char)ch);
}
}
bool enteredFunction() {
if (inComment || inLineComment || inDoubleQuote || inSingleQuote || commentMightBeStarting) {
return false;
}
if (tokens.size() == 0) {
return false;
}
if (tokens[tokens.size() - 1] != "{") {
return false;
}
int i = tokens.size() - 2;
bool foundCloseBraket = false;
int innerBrakets = 0;
bool foundOpenBraket = false;
int iName = -1;
while (i >= 0) {
string t_i = tokens[i]; // debugging sucks!
if (!foundCloseBraket && (tokens[i] == "enum"
|| tokens[i] == "struct"
|| tokens[i] == "class"
|| tokens[i] == "namespace"
|| tokens[i] == "public"
|| tokens[i] == "private"
|| tokens[i] == "protected"
|| tokens[i] == "__asm"
|| tokens[i] == "catch"
|| tokens[i] == "__except"
)) {
return false;
}
if (tokens[i] == ")") {
if (foundCloseBraket)
innerBrakets++;
else if (i >= 3 && tokens[i - 1] == "." && tokens[i - 2] == "." && tokens[i - 3] == ".") {
i -= 3;
}
foundCloseBraket = true;
} else if (tokens[i] == "(" && innerBrakets > 0) {
innerBrakets--;
} else if (tokens[i] == "(" && innerBrakets == 0) {
foundOpenBraket = true;
i--; if ( i < 0) return false;
string name = tokens[i];
iName = i;
if (name == "if" || name == "while" || name == "switch"|| name == "for") {
return false;
}
if (!CouldBeFunctionName(name)) return false;
if (i >= 6 && tokens[i - 1] == ":" && tokens[i - 2] == ":" && CouldBeClassnName(tokens[i - 3]) && tokens[i - 4] == ":" && tokens[i - 5] == ":" && CouldBeClassnName(tokens[i - 6])) {
name = tokens[i - 6] + "::" + tokens[i - 3] + "::" + name;
iName = i - 6;
if (i >= 7 && (tokens[i - 7] == ":" || tokens[i-7] == ",")) {
i -= 7 + 1;
name = "";
foundCloseBraket = false;
foundOpenBraket = false;
innerBrakets = 0;
continue;
}
}
else if (i >= 3 && tokens[i - 1] == ":" && tokens[i - 2] == ":" && CouldBeClassnName(tokens[i - 3])) {
name = tokens[i - 3] + "::" + name;
iName = i - 3;
if (i >= 4 && (tokens[i - 4] == ":" || tokens[i-4] == ",")) {
i -= 4 + 1;
name = "";
foundCloseBraket = false;
foundOpenBraket = false;
innerBrakets = 0;
continue;
}
}
else if (i >= 1 && (tokens[i - 1] == ":" || tokens[i-1] == ",")) {
i -= 1 + 1;
name = "";
foundCloseBraket = false;
foundOpenBraket = false;
innerBrakets = 0;
continue;
}
if (name == "") {
return false;
}
if (iName >= 2 && tokens[iName - 2] == "#" && tokens[iName - 1] == "define") {
return false;
}
if (iName >= 1 && (tokens[i - 1] == "enum"
|| tokens[i - 1] == "struct"
|| tokens[i - 1] == "class"
|| tokens[i - 1] == "namespace"
|| tokens[i - 1] == "public"
|| tokens[i - 1] == "private"
|| tokens[i - 1] == "protected"
|| tokens[i - 1] == "__asm"
|| tokens[i - 1] == "if"
|| tokens[i - 1] == "while"
|| tokens[i - 1] == "for"
|| tokens[i - 1] == "switch"
|| tokens[i - 1] == "!"
)) {
return false;
}
int k = 10;
i = iName - 2;
bool isInline = false;// heuristic for inline functions
while (k > 0 && i >= 0) {
if (tokens[i] == "inline") {
isInline = true;
break;
}
if (tokens[i] == ";" || tokens[i] == "{" || tokens[i] == "}") {
break;
}
i--;
k--;
}
if (isInline) return false; //do not trace inline functions
lastFunctionName = name;
return true;
} else {
if (!foundCloseBraket) {
if (!IgnorableFunctionModifier(tokens[i])) {
return false;
}
} else {
if (!IgnorableFunctionParameter(tokens[i])) {
return false;
}
}
}
i--;
}
return false;
}
const char* functionName() {
return lastFunctionName.c_str();
}
int lineNumber() {
return line;
}
private:
bool CouldBeFunctionName(const string& str) {
if (str.empty()) return false;
if (!isalpha(str[0]) && str[0] != '_' && str[0] != '~' && str[0] != ':') return false;
for (int i = 1; i < str.length(); i++) {
if (!isalpha(str[i]) && !isdigit(str[i]) && str[i] != '_' && str[i] != ':') return false;
}
return true;
}
bool isNumber(const string& str) {
if (str.empty()) return false;
for (int i = 0; i < str.length(); i++) {
if (!isdigit(str[i]) && str[i] != '.' && str[i] != 'x' && str[i] != 'X' && str[i] != 'e' && str[i] != 'E') return false;
}
return true;
}
bool isOperator(const string& str) {
if (str.empty()) return false;
for (int i = 1; i < str.length(); i++) {
switch (str[i]) {
case '<':
case '>':
case '=':
case '+':
case '-':
case '*':
case '/':
case '(':
case ')':
case '[':
case ']':
case '!':
case '|':
case '&':
case '^':
case '%':
break;
default:
return false;
}
}
return true;
}
bool CouldBeClassnName(const string& str) {
return CouldBeFunctionName(str);
}
bool IgnorableFunctionModifier(const string& str) {
return str.empty() || CouldBeFunctionName(str);
}
bool IgnorableFunctionParameter(const string& str) {
if (str.empty()) return true;
if (CouldBeFunctionName(str)) return true;
if (str == ",") return true;
if (str == "*") return true;
if (str == "=") return true;
if (str == "&") return true;
if (str == "<") return true;
if (str == ">") return true;
if (str == ":") return true;
if (str == "=") return true;
if (isNumber(str)) return true;
if (str == "]") return true;
if (str == "[") return true;
if (str == ";") return false;
return false;
}
bool tokenDelimiter(int ch) {
if (isspace(ch)) return true;
if (isdigit(ch)) return false;
if (isalpha(ch)) return false;
if (ch == '_') return false;
return true;
}
bool isTokenable(int ch) {
if (isdigit(ch)) return true;
if (isalpha(ch)) return true;
if (ch == '_') return true;
return false;
}
bool isSingleCharToken(int ch) {
if (isspace(ch)) return false;
if (isdigit(ch)) return false;
if (isalpha(ch)) return false;
if (ch == '_') return false;
return true;
}
void add(char ch) {
token += ch;
}
void pushToken(const char* sz) {
newToken();
token = sz;
newToken();
}
void newToken() {
if (token.empty()) return;
if (tokens.size() > 0) {
string last = tokens[tokens.size() -1];
if (last == "operator") {
if (isOperator(op + token)) {
op += token;
token = "";
return;
} else if (op != "" && isOperator(op)) {
tokens[tokens.size() -1] = last + op;
op = "";
return;
} else if (isOperator(token)) {
op = token;
token = "";
return;
} else {
// compile error?
op = "";
}
} else if (last == "~") {
tokens[tokens.size() -1] = last + token;
token = "";
return;
}
}
tokens.push_back(token);
token = "";
}
int line;
vector<string> tokens;
string token;
string lastFunctionName;
bool inComment;
bool inLineComment;
bool inDoubleQuote;
bool inSingleQuote;
bool isEscaping;
bool commentEnding;
bool commentMightBeStarting;
string op;
};
char output[100000000];
char* now;
void emit(char ch) {
*now = ch;
now++;
}
void emit(const char* szCode, const char* szFunctionName, int fileId, int line) {
sprintf(now, szCode, szFunctionName, fileId, line);
while (*now) {
now++;
}
}
void runFile(const char* szFileNameInput, const char* szFileNameOutput, const char* szInclude) {
printf("%s\n", szFileNameInput);
if (!sourceFile(szFileNameInput))
return;
now = output;
int fileId = source_db->obtainFileId(szFileNameInput);
FILE* file = fopen(szFileNameInput, "rt");
int ch;
CppState state;
while (true) {
int ch = getc(file);
if (ch == -1)
break;
state.apply(ch);
emit(ch);
if (ch == '{' && state.enteredFunction()) { // {
emit("LS_TRACE(\"%s\", %i, %i);", state.functionName(), fileId, state.lineNumber()); // light symbol traces, create a macro to define it
}
}
fclose(file);
file = fopen(szFileNameOutput, "wt");
// TODO: input parameter
fprintf(file, "#include \"%s\"\n", szInclude);
fwrite(output, 1, now - output, file);
fclose(file);
//source_db->commit();
}
// to create the list file:
// dir *.cpp;*.h;*.cc /s /b
void runAll(char* szFileHolder, const char* szInclude) {
FILE* file = fopen(szFileHolder, "rt");
if (file == NULL) {
return;
}
while (true) {
char szFileName[10000] = "";
fgets(szFileName, 10000, file);
char* end = szFileName + strlen(szFileName) - 1;
while (end > szFileName && (*end == '\n' || *end == '\r' || *end == ' ' || *end == '\t')) {
*end = 0;
end--;
}
if (strlen(szFileName) == 0)
break;
runFile(szFileName, szFileName, szInclude);
}
fclose(file);
source_db->commit();
}
int _tmain(int argc, char* argv[])
{
// base path, include, list.txt, lightSymbolFile, [lightSymbolsOut]
SkSourceDb::Load(argv[4], &source_db, argc == 5 ? argv[4] : argv[5], argv[1]);
if (source_db == NULL) {
source_db = new SkSourceDb(argv[1], argv[4]);
}
runAll(argv[3], argv[2]); // e.g. foo\\src\\lightsymbols\\lightsymbols.h");
return 0;
}