1// Callstacker.cpp : Defines the entry point for the console application. 2// 3 4#include "stdafx.h" 5 6#include <string> 7#include <map> 8#include <vector> 9 10using namespace std; 11 12// can't delete, only add files repository! 13class SkSourceDb { 14public: 15 SkSourceDb(const char* szBaseSrcPath, const char* szLightSymbolsDbFile) { 16 this->baseSrcPath = szBaseSrcPath; 17 this->lightSymbolsDbFile = szLightSymbolsDbFile; 18 nextId = 1; 19 } 20 21 const string& getBaseSrcPath() const { 22 return baseSrcPath; 23 } 24 25 string GetStoredFilename(const string& filename) { 26 string base = filename.substr(0, baseSrcPath.length()); 27 if (base != baseSrcPath) { 28 return ""; 29 } 30 31 string relative = filename.substr(baseSrcPath.length()); 32 char tmp[10000]; 33 strcpy(tmp, relative.c_str()); // insecure 34 char* sz = tmp; 35 while (*sz) { 36 if (*sz == '\\') *sz = '/'; 37 sz++; 38 } 39 sz = tmp; 40 if (*sz == '/') sz++; 41 42 return string(sz); 43 } 44 45 int obtainFileId(const string& filename) { 46 string stored = GetStoredFilename(filename); 47 if (stored.empty()) { 48 return -1; 49 } 50 51 if (filenames.find(stored) == filenames.end()) { 52 int id = nextId; 53 nextId++; 54 filenames[stored] = id; 55 return id; 56 } else { 57 return filenames[stored]; 58 } 59 } 60 61 static void Load(char* szFileName, SkSourceDb** ret, const char* whereToSave, const char* szBaseSrcPath) { 62 char szLine[10000]; 63 FILE* file = fopen(szFileName, "rt"); 64 if (file == NULL) { 65 *ret = NULL; 66 return; 67 } 68 69 const char* trimed; 70 SkSourceDb* db = new SkSourceDb(szBaseSrcPath, whereToSave == NULL ? szFileName : whereToSave); 71 72 map<int, string> ids; 73 int id; 74 while (true) { 75 id = -1; 76 if (fscanf(file, "%i", &id) == 0) break; 77 if (id == -1) break; 78 *szLine = '\0'; 79 fgets(szLine, 10000, file); 80 trimed = trim(szLine); 81 82 if (id < 0 || ids[id] != "") { 83 printf("fatal error: duplicate value for id = %i, existing = \"%s\", new = \"%s\"\n", id, ids[id].c_str(), trimed); 84 exit(-1); 85 } 86 87 if (trimed == NULL || *trimed == '\0') { 88 printf("fatal error: no valuefor id = %i\n", id); 89 exit(-1); 90 } 91 92 if (db->filenames.find(trimed) != db->filenames.end()) { 93 printf("fatal error: duplicate id for same file: file = %s, existing id = %i, new id = %i\n", trimed, db->filenames[trimed], id); 94// exit(-1); 95 } 96 97 string value = trimed; 98 ids[id] = value; 99 db->filenames[value] = id; 100 if (db->nextId <= id) { 101 db->nextId = id + 1; 102 } 103 } 104 105 *ret = db; 106 } 107 108 // dumb comit, smarter: use append 109 void commit() { 110 save(lightSymbolsDbFile.c_str()); 111 } 112 113private: 114 115 static const char* trim(char* sz) { 116 if (sz == NULL) return NULL; 117 118 while (*sz == ' ' || *sz == '\t' || *sz == '\r' || *sz == '\n' || *sz == ',') 119 sz++; 120 121 if (*sz == '\0') return sz; 122 123 int len = strlen(sz); 124 char* start = sz; 125 sz = sz + (len - 1); 126 127 while (sz >= start && (*sz == ' ' || *sz == '\t' || *sz == '\r' || *sz == '\n' || *sz == ',')) { 128 *sz = '\0'; 129 sz--; 130 } 131 132 return start; 133 } 134 135 void save(const char* szFilename) { 136 char szLine[10000]; 137 FILE* file = fopen(szFilename, "wt"); 138 139 map<string, int>::const_iterator itr; 140 141 for(itr = filenames.begin(); itr != filenames.end(); ++itr){ 142 fprintf(file, "%i, %s\n", (*itr).second, (*itr).first.c_str()); 143 } 144 fclose(file); 145 } 146 147 string baseSrcPath; 148 string lightSymbolsDbFile; 149 map<string, int> filenames; 150 int nextId; 151}; 152 153SkSourceDb* source_db = NULL; 154 155bool endsWith(const char* who, const char* what) { 156 int a = strlen(who); 157 int b = strlen(what); 158 return stricmp(who + a - b, what) == 0; // insecure 159} 160 161bool sourceFile(const char* szFileName) { 162 return endsWith(szFileName, ".h") || endsWith(szFileName, ".c") || endsWith(szFileName, ".cpp") || endsWith(szFileName, ".cc"); 163} 164 165// " 166// // 167// /* 168class CppState { 169public: 170 171 CppState() : line(1), inComment(false), inLineComment(false), inDoubleQuote(false), inSingleQuote(false), isEscaping(false), commentEnding(false), commentMightBeStarting(false) { 172 } 173 174 void apply(int ch) { 175 if (ch == '\n') { 176 line++; 177 if (inLineComment) inLineComment = false; 178 } 179 180 if (inLineComment) { 181 return; 182 } 183 184 if (commentMightBeStarting) { 185 commentMightBeStarting = false; 186 if (ch == '*') { 187 inComment = true; 188 } else if (ch == '/') { 189 inLineComment = true; 190 } else { 191 add('/');//previously we has / but was not pushed on tokens 192 newToken();// 193 } 194 } 195 196 if (inSingleQuote) { 197 if (isEscaping) 198 isEscaping = false; 199 else if (ch == '\\') 200 isEscaping = true; 201 else if (ch == '\'') { 202 inSingleQuote = false; 203 newToken(); 204 pushToken("__SINGLE_QUOTE__"); 205 newToken(); 206 } 207 208 return; 209 } else if (inDoubleQuote) { 210 if (isEscaping) 211 isEscaping = false; 212 else if (ch == '\\') 213 isEscaping = true; 214 else if (ch == '\"') { 215 inDoubleQuote = false; 216 newToken(); 217 pushToken("__DOUBLE_QUOTE__"); 218 newToken(); 219 } 220 221 return; 222 } else if (inComment) { 223 if (ch == '*') { 224 commentEnding = true; 225 } else if (ch == '/') { 226 inComment = false; 227 commentEnding = false; 228 } else { 229 commentEnding = false; 230 } 231 232 return; 233 } 234 235 switch (ch) { 236 case '\'': 237 newToken(); 238 inSingleQuote = true; 239 return; 240 241 case '\"': 242 newToken(); 243 inDoubleQuote = true; 244 return; 245 246 case '/': 247 newToken(); 248 commentMightBeStarting = true; 249 return; 250 } 251 252 if (isspace(ch)) { 253 newToken(); 254 } else if (tokenDelimiter(ch)) { 255 newToken(); 256 if (isSingleCharToken(ch)) { 257 add(ch); 258 newToken(); 259 } 260 } else if (isTokenable(ch)) { 261 add(ch); 262 } else { 263 printf("undexpected ... %c", (char)ch); 264 } 265 } 266 267 bool enteredFunction() { 268 if (inComment || inLineComment || inDoubleQuote || inSingleQuote || commentMightBeStarting) { 269 return false; 270 } 271 272 if (tokens.size() == 0) { 273 return false; 274 } 275 276 if (tokens[tokens.size() - 1] != "{") { 277 return false; 278 } 279 280 int i = tokens.size() - 2; 281 282 bool foundCloseBraket = false; 283 int innerBrakets = 0; 284 bool foundOpenBraket = false; 285 int iName = -1; 286 287 while (i >= 0) { 288 string t_i = tokens[i]; // debugging sucks! 289 290 if (!foundCloseBraket && (tokens[i] == "enum" 291 || tokens[i] == "struct" 292 || tokens[i] == "class" 293 || tokens[i] == "namespace" 294 || tokens[i] == "public" 295 || tokens[i] == "private" 296 || tokens[i] == "protected" 297 || tokens[i] == "__asm" 298 || tokens[i] == "catch" 299 || tokens[i] == "__except" 300 )) { 301 return false; 302 } 303 304 if (tokens[i] == ")") { 305 if (foundCloseBraket) 306 innerBrakets++; 307 else if (i >= 3 && tokens[i - 1] == "." && tokens[i - 2] == "." && tokens[i - 3] == ".") { 308 i -= 3; 309 } 310 foundCloseBraket = true; 311 } else if (tokens[i] == "(" && innerBrakets > 0) { 312 innerBrakets--; 313 } else if (tokens[i] == "(" && innerBrakets == 0) { 314 foundOpenBraket = true; 315 i--; if ( i < 0) return false; 316 string name = tokens[i]; 317 iName = i; 318 319 if (name == "if" || name == "while" || name == "switch"|| name == "for") { 320 return false; 321 } 322 323 if (!CouldBeFunctionName(name)) return false; 324 if (i >= 6 && tokens[i - 1] == ":" && tokens[i - 2] == ":" && CouldBeClassnName(tokens[i - 3]) && tokens[i - 4] == ":" && tokens[i - 5] == ":" && CouldBeClassnName(tokens[i - 6])) { 325 name = tokens[i - 6] + "::" + tokens[i - 3] + "::" + name; 326 iName = i - 6; 327 if (i >= 7 && (tokens[i - 7] == ":" || tokens[i-7] == ",")) { 328 i -= 7 + 1; 329 name = ""; 330 foundCloseBraket = false; 331 foundOpenBraket = false; 332 innerBrakets = 0; 333 continue; 334 } 335 } 336 else if (i >= 3 && tokens[i - 1] == ":" && tokens[i - 2] == ":" && CouldBeClassnName(tokens[i - 3])) { 337 name = tokens[i - 3] + "::" + name; 338 iName = i - 3; 339 if (i >= 4 && (tokens[i - 4] == ":" || tokens[i-4] == ",")) { 340 i -= 4 + 1; 341 name = ""; 342 foundCloseBraket = false; 343 foundOpenBraket = false; 344 innerBrakets = 0; 345 continue; 346 } 347 } 348 else if (i >= 1 && (tokens[i - 1] == ":" || tokens[i-1] == ",")) { 349 i -= 1 + 1; 350 name = ""; 351 foundCloseBraket = false; 352 foundOpenBraket = false; 353 innerBrakets = 0; 354 continue; 355 } 356 357 if (name == "") { 358 return false; 359 } 360 361 if (iName >= 2 && tokens[iName - 2] == "#" && tokens[iName - 1] == "define") { 362 return false; 363 } 364 365 if (iName >= 1 && (tokens[i - 1] == "enum" 366 || tokens[i - 1] == "struct" 367 || tokens[i - 1] == "class" 368 || tokens[i - 1] == "namespace" 369 || tokens[i - 1] == "public" 370 || tokens[i - 1] == "private" 371 || tokens[i - 1] == "protected" 372 || tokens[i - 1] == "__asm" 373 || tokens[i - 1] == "if" 374 || tokens[i - 1] == "while" 375 || tokens[i - 1] == "for" 376 || tokens[i - 1] == "switch" 377 || tokens[i - 1] == "!" 378 )) { 379 return false; 380 } 381 382 int k = 10; 383 i = iName - 2; 384 bool isInline = false;// heuristic for inline functions 385 while (k > 0 && i >= 0) { 386 if (tokens[i] == "inline") { 387 isInline = true; 388 break; 389 } 390 if (tokens[i] == ";" || tokens[i] == "{" || tokens[i] == "}") { 391 break; 392 } 393 i--; 394 k--; 395 } 396 397 if (isInline) return false; //do not trace inline functions 398 399 lastFunctionName = name; 400 return true; 401 } else { 402 if (!foundCloseBraket) { 403 if (!IgnorableFunctionModifier(tokens[i])) { 404 return false; 405 } 406 } else { 407 if (!IgnorableFunctionParameter(tokens[i])) { 408 return false; 409 } 410 } 411 } 412 413 i--; 414 } 415 416 return false; 417 } 418 419 const char* functionName() { 420 return lastFunctionName.c_str(); 421 } 422 423 int lineNumber() { 424 return line; 425 } 426 427private: 428 429 bool CouldBeFunctionName(const string& str) { 430 if (str.empty()) return false; 431 if (!isalpha(str[0]) && str[0] != '_' && str[0] != '~' && str[0] != ':') return false; 432 for (int i = 1; i < str.length(); i++) { 433 if (!isalpha(str[i]) && !isdigit(str[i]) && str[i] != '_' && str[i] != ':') return false; 434 } 435 436 return true; 437 } 438 439 bool isNumber(const string& str) { 440 if (str.empty()) return false; 441 for (int i = 0; i < str.length(); i++) { 442 if (!isdigit(str[i]) && str[i] != '.' && str[i] != 'x' && str[i] != 'X' && str[i] != 'e' && str[i] != 'E') return false; 443 } 444 445 return true; 446 } 447 448 449 bool isOperator(const string& str) { 450 if (str.empty()) return false; 451 for (int i = 1; i < str.length(); i++) { 452 switch (str[i]) { 453 case '<': 454 case '>': 455 case '=': 456 case '+': 457 case '-': 458 case '*': 459 case '/': 460 case '(': 461 case ')': 462 case '[': 463 case ']': 464 case '!': 465 case '|': 466 case '&': 467 case '^': 468 case '%': 469 break; 470 default: 471 return false; 472 } 473 } 474 475 return true; 476 } 477 478 bool CouldBeClassnName(const string& str) { 479 return CouldBeFunctionName(str); 480 } 481 482 bool IgnorableFunctionModifier(const string& str) { 483 return str.empty() || CouldBeFunctionName(str); 484 } 485 486 bool IgnorableFunctionParameter(const string& str) { 487 if (str.empty()) return true; 488 if (CouldBeFunctionName(str)) return true; 489 if (str == ",") return true; 490 if (str == "*") return true; 491 if (str == "=") return true; 492 if (str == "&") return true; 493 if (str == "<") return true; 494 if (str == ">") return true; 495 if (str == ":") return true; 496 if (str == "=") return true; 497 if (isNumber(str)) return true; 498 if (str == "]") return true; 499 if (str == "[") return true; 500 501 if (str == ";") return false; 502 503 return false; 504 } 505 506 507 bool tokenDelimiter(int ch) { 508 if (isspace(ch)) return true; 509 if (isdigit(ch)) return false; 510 if (isalpha(ch)) return false; 511 if (ch == '_') return false; 512 return true; 513 } 514 515 bool isTokenable(int ch) { 516 if (isdigit(ch)) return true; 517 if (isalpha(ch)) return true; 518 if (ch == '_') return true; 519 return false; 520 } 521 522 bool isSingleCharToken(int ch) { 523 if (isspace(ch)) return false; 524 if (isdigit(ch)) return false; 525 if (isalpha(ch)) return false; 526 if (ch == '_') return false; 527 return true; 528 } 529 530 void add(char ch) { 531 token += ch; 532 } 533 534 void pushToken(const char* sz) { 535 newToken(); 536 token = sz; 537 newToken(); 538 } 539 540 void newToken() { 541 if (token.empty()) return; 542 543 if (tokens.size() > 0) { 544 string last = tokens[tokens.size() -1]; 545 if (last == "operator") { 546 if (isOperator(op + token)) { 547 op += token; 548 token = ""; 549 return; 550 } else if (op != "" && isOperator(op)) { 551 tokens[tokens.size() -1] = last + op; 552 op = ""; 553 return; 554 } else if (isOperator(token)) { 555 op = token; 556 token = ""; 557 return; 558 } else { 559 // compile error? 560 op = ""; 561 } 562 } else if (last == "~") { 563 tokens[tokens.size() -1] = last + token; 564 token = ""; 565 return; 566 } 567 } 568 569 tokens.push_back(token); 570 token = ""; 571 } 572 573 int line; 574 vector<string> tokens; 575 string token; 576 string lastFunctionName; 577 578 bool inComment; 579 bool inLineComment; 580 bool inDoubleQuote; 581 bool inSingleQuote; 582 bool isEscaping; 583 bool commentEnding; 584 bool commentMightBeStarting; 585 586 string op; 587}; 588 589char output[100000000]; 590char* now; 591 592 593void emit(char ch) { 594 *now = ch; 595 now++; 596} 597 598void emit(const char* szCode, const char* szFunctionName, int fileId, int line) { 599 sprintf(now, szCode, szFunctionName, fileId, line); 600 while (*now) { 601 now++; 602 } 603} 604 605void runFile(const char* szFileNameInput, const char* szFileNameOutput, const char* szInclude) { 606 printf("%s\n", szFileNameInput); 607 608 609 if (!sourceFile(szFileNameInput)) 610 return; 611 612 now = output; 613 int fileId = source_db->obtainFileId(szFileNameInput); 614 FILE* file = fopen(szFileNameInput, "rt"); 615 int ch; 616 CppState state; 617 while (true) { 618 int ch = getc(file); 619 if (ch == -1) 620 break; 621 state.apply(ch); 622 emit(ch); 623 if (ch == '{' && state.enteredFunction()) { // { 624 emit("LS_TRACE(\"%s\", %i, %i);", state.functionName(), fileId, state.lineNumber()); // light symbol traces, create a macro to define it 625 } 626 } 627 fclose(file); 628 629 file = fopen(szFileNameOutput, "wt"); 630 // TODO: input parameter 631 fprintf(file, "#include \"%s\"\n", szInclude); 632 fwrite(output, 1, now - output, file); 633 fclose(file); 634 //source_db->commit(); 635} 636 637// to create the list file: 638// dir *.cpp;*.h;*.cc /s /b 639void runAll(char* szFileHolder, const char* szInclude) { 640 FILE* file = fopen(szFileHolder, "rt"); 641 if (file == NULL) { 642 return; 643 } 644 645 while (true) { 646 char szFileName[10000] = ""; 647 fgets(szFileName, 10000, file); 648 char* end = szFileName + strlen(szFileName) - 1; 649 while (end > szFileName && (*end == '\n' || *end == '\r' || *end == ' ' || *end == '\t')) { 650 *end = 0; 651 end--; 652 } 653 if (strlen(szFileName) == 0) 654 break; 655 656 runFile(szFileName, szFileName, szInclude); 657 } 658 fclose(file); 659 source_db->commit(); 660} 661 662int _tmain(int argc, char* argv[]) 663{ 664 // base path, include, list.txt, lightSymbolFile, [lightSymbolsOut] 665 SkSourceDb::Load(argv[4], &source_db, argc == 5 ? argv[4] : argv[5], argv[1]); 666 if (source_db == NULL) { 667 source_db = new SkSourceDb(argv[1], argv[4]); 668 } 669 670 runAll(argv[3], argv[2]); // e.g. foo\\src\\lightsymbols\\lightsymbols.h"); 671 672 return 0; 673} 674