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