source: trunk/tools/database/StateUpd.cpp@ 2770

Last change on this file since 2770 was 2770, checked in by bird, 26 years ago

Corrections, DB optimizations, and some new features in StateUpd.

File size: 61.2 KB
Line 
1/* $Id: StateUpd.cpp,v 1.11 2000-02-12 23:54:29 bird Exp $
2 *
3 * StateUpd - Scans source files for API functions and imports data on them.
4 *
5 * Copyright (c) 1999 knut st. osmundsen
6 *
7 */
8
9/*******************************************************************************
10* Header Files *
11*******************************************************************************/
12#define INCL_DOSFILEMGR
13#define INCL_DOSERRORS
14#define INCL_DOSMISC
15#define INCL_DOSPROCESS
16#include <os2.h>
17#include <stdio.h>
18#include <string.h>
19#include <malloc.h>
20#include <assert.h>
21
22#include "StateUpd.h"
23#include "db.h"
24
25
26
27/*******************************************************************************
28* Global Variables *
29*******************************************************************************/
30static FILE *phLog = NULL;
31static FILE *phSignal = NULL;
32
33
34/*******************************************************************************
35* Internal Functions *
36*******************************************************************************/
37static void syntax(void);
38static void openLogs(void);
39static void closeLogs(void);
40static unsigned long processDir(const char *pszDirOrFile, POPTIONS pOptions);
41static unsigned long processFile(const char *pszFilename, POPTIONS pOptions);
42static unsigned long processAPI(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
43static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions);
44static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
45static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
46static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions);
47static long _System dbNotUpdatedCallBack(const char *pszValue, const char *pszFieldName, void *pvUser);
48static char *skipInsignificantChars(char **papszLines, int &i, char *psz);
49static char *readFileIntoMemory(const char *pszFilename);
50static char **createLineArray(char *pszFile);
51static char *findEndOfWord(char *psz);
52static char *findStartOfWord(char *psz, const char *pszStart);
53static char *trim(char *psz);
54static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines);
55static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines);
56static char *stristr(const char *pszStr, const char *pszSubStr);
57static char *skipBackwards(const char *pszStopAt, const char *pszFrom, int &iLine, char **papszLines);
58
59
60/**
61 * Main function.
62 * @returns Number of signals.
63 * @param argc Argument count.
64 * @param argv Argument array.
65 */
66int main(int argc, char **argv)
67{
68 int argi;
69 BOOL fFatal = FALSE;
70 unsigned long ulRc = 0;
71 char szDLLName[64];
72 OPTIONS options = {TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, &szDLLName[0], -1};
73 unsigned long ulRc2;
74 char *pszErrorDesc = NULL;
75 char *pszHost = "localhost";
76 char *pszDatabase = "Odin32";
77 char *pszUser = "root";
78 char *pszPasswd = "";
79 ULONG ul0, ul1, ul2;
80
81 DosError(0x3);
82 DosSetPriority(PRTYS_PROCESSTREE, PRTYC_REGULAR, 1, 0);
83
84 /* get dll name from directory */
85 ul1 = ul2 = 0;
86 DosQueryCurrentDisk(&ul1, &ul2);
87 ul2 = sizeof(szDLLName);
88 DosQueryCurrentDir(ul1, &szDLLName[0], &ul2);
89 if (ul2 != 0)
90 {
91 if (szDLLName[ul2-1] == '\\' || szDLLName[ul2-1] == '/')
92 szDLLName[--ul2] = '\0';
93 ul1 = ul2;
94 while (ul1 != 0 && szDLLName[ul1-1] != '\\' && szDLLName[ul1-1] != '/')
95 ul1--;
96 if (ul1 != 0)
97 options.pszDLLName = &szDLLName[ul1];
98 }
99 else
100 szDLLName[0] = '\0';
101
102
103 /**************************************************************************
104 * parse arguments.
105 * options: -h or -? Syntax help.
106 * -ib<[+]|-> Integrity check at start.
107 * -ie<[+]|-> Integrity check at end.
108 * -io Integrity check only.
109 * -s Scan subdirectories.
110 * -Old <[+]|-> Old API Style.
111 * -OS2<[+]|-> Removes 'OS2'-prefix from function name.
112 * -COMCTL32<[+]|-> Removes 'COMCTL32'-prefix from function name.
113 * -VERSION<[+]|-> Removes 'VERSION'-prefix from function name.
114 * -Dll:<dllname> Name of the dll being processed.
115 * -d:<dbname> Database name
116 * -p:<passwd> Password
117 * -u:<user> Userid
118 * -h:<host> Hostname/IP-address
119 **************************************************************************/
120 argi = 1;
121 while (argi < argc && !fFatal)
122 {
123 if(argv[argi][0] == '-' || argv[argi][0] == '/')
124 {
125 switch (argv[argi][1])
126 {
127 case 'd':
128 case 'D':
129 if (strnicmp(argv[argi], "dll:", 4) == 0 )
130 options.pszDLLName = &argv[argi][5];
131 else
132 {
133 if (argv[argi][2] == ':')
134 pszDatabase = &argv[argi][3];
135 else
136 fprintf(stderr, "warning: option '-d:' requires database name.\n");
137 }
138 break;
139
140 case 'h':
141 case 'H':
142 if (argv[argi][2] == ':')
143 {
144 pszHost = &argv[argi][3];
145 break;
146 }
147 case '?':
148 syntax();
149 return 0;
150
151 case 'i': /* Integrity */
152 case 'I':
153 switch (argv[argi][2])
154 {
155 case 'b':
156 case 'B':
157 options.fIntegrityBefore = argv[argi][3] != '-';
158 break;
159
160 case 'e':
161 case 'E':
162 options.fIntegrityAfter = argv[argi][3] != '-';
163 break;
164
165 case 'o':
166 case 'O':
167 options.fIntegrityOnly = argv[argi][3] != '-';
168 break;
169
170 default:
171 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
172 fFatal = TRUE;
173 }
174 break;
175
176 case 'o':
177 case 'O':
178 if (stricmp(&argv[argi][1], "OS2") == 0)
179 options.fOS2 = argv[argi][4] != '-';
180 else if (stricmp(&argv[argi][1], "Old") == 0)
181 options.fOld = argv[argi][4] != '-';
182 else
183 {
184 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
185 fFatal = TRUE;
186 }
187 break;
188
189 case 'p':
190 case 'P':
191 if (argv[argi][2] == ':')
192 pszPasswd = &argv[argi][3];
193 else
194 fprintf(stderr, "warning: option '-p:' requires password.\n");
195 break;
196
197 case 's':
198 case 'S':
199 options.fRecursive = TRUE;
200 break;
201
202 case 'u':
203 case 'U':
204 if (argv[argi][2] == ':')
205 pszUser = &argv[argi][3];
206 else
207 fprintf(stderr, "error: option '-u:' requires userid.\n");
208 break;
209
210 default:
211 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
212 fFatal = TRUE;
213 break;
214 }
215 }
216 else
217 break; /* files has started */
218 argi++;
219 }
220
221 if (!fFatal)
222 {
223 /* open database */
224 if (!dbConnect(pszHost, pszUser, pszPasswd, pszDatabase))
225 {
226 fprintf(stderr, "Could not connect to database (%s). \n\terror description: %s\n",
227 pszDatabase, dbGetLastErrorDesc());
228 return 0x00010001;
229 }
230
231 /* open the logs */
232 openLogs();
233
234 /* check db integrity */
235 if (options.fIntegrityBefore)
236 {
237 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
238 *pszErrorDesc = '\0';
239 ulRc2 = dbCheckIntegrity(pszErrorDesc);
240 if (ulRc2 != 0)
241 {
242 fprintf(phSignal, "-,-: integrity errors:\n\t%s\n", pszErrorDesc);
243 ulRc += ulRc2 << 16;
244 }
245 free(pszErrorDesc);
246 }
247
248 if (!options.fIntegrityOnly)
249 {
250 /* find dll */
251 options.lDllRefcode = dbGetDll(options.pszDLLName);
252 fprintf(phLog, "DLL: refcode=%d, name=%s\n", options.lDllRefcode, options.pszDLLName);
253
254 /* processing */
255 if (argv[argi] == NULL || *argv[argi] == '\0')
256 ulRc = processDir(".", &options);
257 else
258 while (argv[argi] != NULL)
259 {
260 ulRc += processDir(argv[argi], &options);
261 argi++;
262 }
263
264 /* create new history rows */
265 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
266 *pszErrorDesc = '\0';
267 ulRc2 = dbCreateHistory(pszErrorDesc);
268 if (ulRc2 != 0)
269 {
270 fprintf(phSignal, "-,-: errors which occurred while creating history:\n\t%s\n", pszErrorDesc);
271 ulRc += ulRc2 << 16;
272 }
273 free(pszErrorDesc);
274
275 /* check db integrity */
276 if (options.fIntegrityAfter)
277 {
278 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
279 *pszErrorDesc = '\0';
280 ulRc2 = dbCheckIntegrity(pszErrorDesc);
281 if (ulRc2 != 0)
282 {
283 fprintf(phSignal, "-,-: integrity errors:\n\t%s\n", pszErrorDesc);
284 ulRc += ulRc2 << 16;
285 }
286 free(pszErrorDesc);
287 }
288 }
289
290 /* write status to log */
291 ul2 = dbGetNumberOfUpdatedFunction(options.lDllRefcode);
292 ul1 = dbCountFunctionInDll(options.lDllRefcode, FALSE);
293 ul0 = dbCountFunctionInDll(options.lDllRefcode, TRUE);
294 fprintf(phLog, "-------------------------------------------------\n");
295 fprintf(phLog, "-------- Functions which was not updated --------\n");
296 dbGetNotUpdatedFunction(options.lDllRefcode, dbNotUpdatedCallBack);
297 fprintf(phLog, "-------------------------------------------------\n");
298 fprintf(phLog, "-------------------------------------------------\n\n");
299 fprintf(phLog,"Number of function in this DLL: %4ld (%ld)\n", ul1, ul0);
300 fprintf(phLog,"Number of successfully processed APIs: %4ld (%ld)\n", (long)(0x0000FFFF & ulRc), ul2);
301 fprintf(phLog,"Number of signals: %4ld\n", (long)(ulRc >> 16));
302
303 /* close the logs */
304 closeLogs();
305
306 /* close database */
307 dbDisconnect();
308
309 /* warn if error during processing. */
310 fprintf(stdout,"Number of function in this DLL: %4ld (%ld)\n", ul1, ul0);
311 fprintf(stdout,"Number of successfully processed APIs: %4ld (%ld)\n", (long)(0x0000FFFF & ulRc), ul2);
312 fprintf(stdout,"Number of signals: %4ld\n", (long)(ulRc >> 16));
313 if ((int)(ulRc >> 16) > 0)
314 fprintf(stderr, "Check signal file 'Signals.Log'.\n");
315 }
316
317 return (int)(ulRc >> 16);
318}
319
320
321
322/**
323 * Displays syntax.
324 */
325static void syntax()
326{
327 printf("\n"
328 "StateUpd v%01d.%02d - Odin32 API Database utility\n"
329 "----------------------------------------------\n"
330 "syntax: StateUpd.exe [-h|-?] [options] [FileOrDir1 [FileOrDir2 [...]]]\n"
331 "\n"
332 " -h or -? Syntax help. (this)\n"
333 " -ib<[+]|-> Integrity check at start. default: enabled\n"
334 " -ie<[+]|-> Integrity check at end. default: enabled\n"
335 " -io Integrity check only. default: disabled\n"
336 " -s Scan subdirectories. default: disabled\n"
337 " -Old Use old API style. default: disabled\n"
338 " -OS2 Ignore 'OS2'-prefix on APIs. default: disabled\n"
339 " -h:<hostname> Database server hostname. default: localhost\n"
340 " -u:<username> Username on the server. default: root\n"
341 " -p:<password> Password. default: <empty>\n"
342 " -d:<database> Database to use. default: Odin32\n"
343 "\n"
344 "Scans files for API functions. This util will extract data about the API\n"
345 "and insert it into the database.\n"
346 "\n"
347 "If no files are given, then all *.c and *.cpp files will be scanned. (Not correct!)\n"
348 "NOTE: When files are given, only *.c and *.cpp files will be scanned.\n"
349 "Wildchars are allowed in the file spesifications.\n"
350 "\n"
351 "Copyright (c) 1999 knut st. osmundsen (knut.stange.osmundsen@pmsc.no)",
352 MAJOR_VER, MINOR_VER
353 );
354}
355
356
357/**
358 * Open logs, StateUpd.log and Signals.log (error log).
359 */
360static void openLogs(void)
361{
362 if (phLog == NULL)
363 {
364 phLog = fopen("StateUpd.Log", "w");
365 if (phLog == NULL)
366 {
367 fprintf(stderr,"error occured while opening log file - will use stderr instead.\n");
368 phLog = stderr;
369 }
370 }
371
372 if (phSignal == NULL)
373 {
374 phSignal = fopen("Signals.Log", "w");
375 if (phSignal == NULL)
376 {
377 fprintf(stderr,"error occured while opening signal file - will use stdout instead.\n");
378 phSignal = stdout;
379 }
380 }
381}
382
383
384/**
385 * Closes the logs.
386 */
387static void closeLogs(void)
388{
389 if (phLog != stderr && phLog != NULL)
390 fclose(phLog);
391 if (phSignal != stdout && phSignal != NULL)
392 {
393 if (ftell(phSignal) > 0)
394 fclose(phSignal);
395 else
396 {
397 fclose(phSignal);
398 unlink("Signals.log");
399 }
400 }
401}
402
403
404/**
405 * Processes a file or a subdirectory with files.
406 * @returns high word = number of signals
407 * low word = number of APIs processed. (1 or 0).
408 * @param pszDirOrFile Directory or file, see fFile.
409 * @param pOptions Pointer to options.
410 * @sketch -0. Determin dir or file.
411 * 0. Interpret parameters.
412 * 1. Scan current directory for *.cpp and *.c files and process them.
413 * 2. If recusion option is enabled:
414 * Scan current directory for sub-directories, scan them using recursion.
415 */
416static unsigned long processDir(const char *pszDirOrFile, POPTIONS pOptions)
417{
418 unsigned long ulRc = 0; /* high word = #signals, low word = #APIs successfully processed */
419 char szBuf[CCHMAXPATH];
420 char szFileSpec[CCHMAXPATH];
421 APIRET rc;
422 FILEFINDBUF3 ffb;
423 FILESTATUS3 fs;
424 ULONG ul = 1;
425 HDIR hDir = (HDIR)HDIR_CREATE;
426 PSZ pszDir;
427 PSZ pszFile;
428 BOOL fFile;
429
430 /* -0.*/
431 rc = DosQueryPathInfo(pszDirOrFile, FIL_STANDARD, &fs , sizeof(fs));
432 fFile = rc == NO_ERROR && (fs.attrFile & FILE_DIRECTORY) != FILE_DIRECTORY;
433
434 /* 0. */
435 strcpy(szBuf, pszDirOrFile);
436 pszDir = szBuf;
437 if (fFile)
438 {
439 if ((pszFile = strrchr(pszDir, '\\')) != NULL
440 || (pszFile = strrchr(pszDir, '/')) != NULL)
441 *pszFile++ = '\0';
442 else
443 {
444 pszFile = pszDir;
445 pszDir = ".";
446 }
447 }
448 else
449 {
450 pszFile = NULL;
451 ul = strlen(pszDir)-1;
452 if (pszDir[ul] == '\\' || pszDir[ul] == '/')
453 pszDir[ul] = '\0';
454 }
455
456
457 /* 1. */
458 if (fFile)
459 strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), pszFile);
460 else
461 strcat(strcpy(&szFileSpec[0], pszDir), "\\*.c*");
462 ul = 1;
463 rc = DosFindFirst((PCSZ)&szFileSpec[0], &hDir,
464 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
465 (PVOID)&ffb, sizeof(ffb), &ul, FIL_STANDARD);
466 while (rc == NO_ERROR && ul == 1)
467 {
468 char *psz = strrchr(&ffb.achName[0], '.');
469 if (psz != NULL && (!stricmp(psz, ".cpp") || !stricmp(psz, ".c")))
470 ulRc += processFile(strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), &ffb.achName[0]), pOptions);
471
472 /* next */
473 ul = 1;
474 rc = DosFindNext(hDir, &ffb, sizeof(ffb), &ul);
475 }
476 DosFindClose(hDir);
477
478 /* 2. */
479 if (pOptions->fRecursive)
480 {
481 strcat(strcpy(&szFileSpec[0], pszDir), "\\*");
482
483 hDir = (HDIR)HDIR_CREATE;
484 ul = 1; /* important on TVFS, not on HPFS... */
485 rc = DosFindFirst((PCSZ)&szFileSpec[0], &hDir,
486 MUST_HAVE_DIRECTORY,
487 (PVOID)&ffb, sizeof(ffb), &ul, FIL_STANDARD);
488 while (rc == NO_ERROR && ul == 1)
489 {
490 if (strcmp(&ffb.achName[0], ".") != 0 && strcmp(&ffb.achName[0], "..") != 0)
491 {
492 strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), &ffb.achName[0]);
493 if (fFile)
494 strcat(strcat(&szFileSpec[0], "\\"), pszFile);
495 ulRc += processDir(&szFileSpec[0], pOptions);
496 }
497
498 /* next */
499 ul = 1;
500 rc = DosFindNext(hDir, &ffb, sizeof(ffb), &ul);
501 }
502 DosFindClose(hDir);
503 }
504
505 return ulRc;
506}
507
508
509/**
510 * Processes a file.
511 * @returns high word = number of signals
512 * low word = number of APIs processed. (1 or 0).
513 * @param pszFilename Filename
514 * @param pOptions Pointer to options.
515 * @sketch 1. read file into memory.
516 * 2. create line array.
517 * (3. simple preprocessing - TODO)
518 * 4. scan thru the line array, looking for APIs.
519 * 4b. when API is found, process it.
520 */
521static unsigned long processFile(const char *pszFilename, POPTIONS pOptions)
522{
523 unsigned long cSignals = 0;
524 unsigned long cAPIs = 0;
525 char *pszFile;
526
527 fprintf(phLog, "Processing '%s':\n", pszFilename);
528 /* 1.*/
529 pszFile = readFileIntoMemory(pszFilename);
530 if (pszFile != NULL)
531 {
532 char **papszLines;
533
534 /* 2.*/
535 papszLines = createLineArray(pszFile);
536 if (papszLines != NULL)
537 {
538 int i = 0;
539
540 /* 3. - TODO */
541
542 /* 4.*/
543 while (papszLines[i] != NULL)
544 {
545 if (isFunction(papszLines, i, pOptions))
546 {
547 unsigned long ulRc;
548 ulRc = processAPI(papszLines, i, i, pszFilename, pOptions);
549 cAPIs += 0x0000ffff & ulRc;
550 cSignals += ulRc >> 16;
551 }
552 else
553 i++;
554 }
555
556 free(papszLines);
557 }
558 else
559 {
560 fprintf(phSignal,"%s: error dividing file into lines.\n", pszFilename);
561 cSignals++;
562 }
563 free(pszFile);
564 }
565 else
566 {
567 fprintf(phSignal,"%s: error reading file.\n", pszFilename);
568 cSignals++;
569 }
570 fprintf(phLog, "Processing of '%s' is completed.\n\n", pszFilename);
571
572
573 return (unsigned long)((cSignals << 16) | cAPIs);
574}
575
576
577/**
578 * Processes an API function.
579 * @returns high word = number of signals
580 * low word = number of APIs processed. (1 or 0).
581 * @param papszLines Array of lines in the file.
582 * @param i Index into papszLines.
583 * @param iRet Index into papszLines. Where to resume search.
584 * @param pszFilename Filename used in the signal log.
585 * @param pOptions Pointer to options.
586 */
587static unsigned long processAPI(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions)
588{
589 unsigned long ulRc;
590 int j;
591 FNDESC FnDesc;
592 memset(&FnDesc, 0, sizeof(FnDesc));
593
594 /* default value */
595 FnDesc.lStatus = 99;
596
597 /* precondition: isFunction is true.
598 * brief algorithm:
599 * 1. Analyse function declaration.
600 * 2. Analyse function header.
601 * 3. Log data (for debug purpose).
602 * 4. Update database
603 */
604
605 /* 1.*/
606 ulRc = analyseFnDcl(&FnDesc, papszLines, i, iRet, pszFilename, pOptions);
607 if (0x0000ffff & ulRc) /* if low word is 0 the fatal */
608 {
609 unsigned long ulRcTmp;
610 char szErrorDesc[2113]; /* due to some limitation in the latest EMX release size is 2112 and not 4096 as initially implemented. */
611
612 /* 2.*/
613 ulRcTmp = analyseFnHdr(&FnDesc, papszLines, i, pszFilename, pOptions);
614 if (ulRcTmp == ~0UL) /* check for fatal error */
615 return (0xffff0000UL & ulRc) + 0x00010000UL;
616 ulRc += 0xffff0000UL & ulRcTmp;
617
618 /* 3.*/
619 fprintf(phLog, "Name: '%s' (refcodes=", FnDesc.pszName);
620 for (j = 0; j < FnDesc.cRefCodes; j++)
621 fprintf(phLog, j > 0 ? ", %ld" : "%ld", FnDesc.alRefCode[j]);
622 fprintf(phLog, ")\n");
623 fprintf(phLog, " Returns: '%s'\n", FnDesc.pszReturnType != NULL ? FnDesc.pszReturnType : "<missing>");
624 fprintf(phLog, " cParams: %2d\n", FnDesc.cParams);
625 for (j = 0; j < FnDesc.cParams; j++)
626 fprintf(phLog, " Param %2d: type '%s' %*s name '%s'\n", j, FnDesc.apszParamType[j],
627 (int)(15 - strlen(FnDesc.apszParamType[j])), "", FnDesc.apszParamName[j]);
628 fprintf(phLog, " Status: %ld - '%s'\n", FnDesc.lStatus, FnDesc.pszStatus != NULL ? FnDesc.pszStatus : "<missing>");
629 fprintf(phLog, " cAuthors: %2d\n", FnDesc.cAuthors);
630 for (j = 0; j < FnDesc.cAuthors; j++)
631 fprintf(phLog, " Author %d: '%s' (refcode=%ld)\n", j, FnDesc.apszAuthor[j], FnDesc.alAuthorRefCode[j]);
632
633 /* 4.*/
634 ulRcTmp = dbUpdateFunction(&FnDesc, pOptions->lDllRefcode, &szErrorDesc[0]);
635 if (ulRcTmp != 0)
636 {
637 fprintf(phSignal, "%s,%s: %s\n", pszFilename, FnDesc.pszName, &szErrorDesc[0]);
638 ulRc += ulRcTmp << 16;
639 }
640 }
641
642 return ulRc;
643}
644
645
646/**
647 * Analyses the function declaration.
648 * @returns high word = number of signals
649 * low word = number of APIs processed. (1 or 0).
650 * @param papszLines Array of lines in the file.
651 * @param i Index into papszLines.
652 * @param iRet Index into papszLines. Where to start searching again.
653 * @param pszFilename Filename used in the signal log.
654 * @param pOptions Pointer to options.
655 */
656static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
657 const char *pszFilename, POPTIONS pOptions)
658{
659 static long lPrevFnDll = -1L; /* fix for duplicate dlls */
660 unsigned long ulRc;
661 FNFINDBUF FnFindBuf;
662 long lFn = 0;
663
664 /* brief algorithm:
665 * 1. read function declaration using analyseFnDcl2.
666 * 2. apply name rules.
667 * 3. do a database lookup on the name.
668 * 3b. if more that one match, write a signal. (TODO: a simple fix is done, but there are holes.)
669 */
670
671 /* 1. */
672 ulRc = analyseFnDcl2(pFnDesc, papszLines, i, iRet, pszFilename, pOptions);
673 if (ulRc != 1)
674 return ulRc;
675
676 /* 2. */
677 if (pOptions->fOS2 && strncmp(pFnDesc->pszName, "OS2", 3) == 0)
678 pFnDesc->pszName += 3;
679 else if (pOptions->fCOMCTL32 && strncmp(pFnDesc->pszName, "COMCTL32", 8) == 0)
680 pFnDesc->pszName += 8;
681 else if (pOptions->fVERSION && strncmp(pFnDesc->pszName, "VERSION", 7) == 0)
682 pFnDesc->pszName += 7;
683 else if (pOptions->fOld)
684 pFnDesc->pszName += 3;
685
686 /* 3. */
687 if (!dbFindFunction(pFnDesc->pszName, &FnFindBuf, pOptions->lDllRefcode))
688 {
689 fprintf(phSignal, "%s, %s: error occured while reading from database, %s\n",
690 pszFilename, pFnDesc->pszName, dbGetLastErrorDesc());
691 return 0x00010000;
692 }
693
694 pFnDesc->cRefCodes = 0;
695 if (FnFindBuf.cFns != 0)
696 {
697 if (pOptions->lDllRefcode < 0)
698 {
699 if (FnFindBuf.cFns > 1)
700 {
701 fprintf(phSignal, "%s: unknown dll and more than two occurences of this function!\n", pszFilename);
702 return 0x00010000;
703 }
704 pOptions->lDllRefcode = FnFindBuf.alDllRefCode[0];
705 fprintf(phLog, "DllRef = %d\n", pOptions->lDllRefcode);
706 }
707
708 for (lFn = 0; lFn < FnFindBuf.cFns; lFn++)
709 pFnDesc->alRefCode[pFnDesc->cRefCodes++] = FnFindBuf.alRefCode[lFn];
710
711 if (pFnDesc->cRefCodes == 0)
712 fprintf(phLog, "%s was not an API in this dll(%d)!\n", pFnDesc->pszName, pOptions->lDllRefcode);
713 }
714 else
715 fprintf(phLog, "%s was not an API\n", pFnDesc->pszName);
716
717 ulRc = pFnDesc->cRefCodes;
718 return ulRc;
719}
720
721
722
723/**
724 * Analyses the function declaration.
725 * No DB lockup or special function type stuff, only ODINFUNCTION is processed.
726 * @returns high word = number of signals
727 * low word = number of APIs processed. (1 or 0).
728 * @param papszLines Array of lines in the file.
729 * @param i Index into papszLines.
730 * @param iRet Index into papszLines. Where to start searching again.
731 * @param pszFilename Filename used in the signal log.
732 * @param pOptions Pointer to options.
733 */
734static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
735 const char *pszFilename, POPTIONS pOptions)
736{
737 /** @sketch
738 * 1. find the '('
739 * 2. find the word ahead of the '(', this is the function name.
740 * 2a. class test.
741 * 3. find the closing ')'
742 * 4. copy the parameters, which is between the two '()'
743 * 5. format the parameters
744 */
745
746 int iFn, iP1, iP2, j, c;
747 char * pszFn, *pszFnEnd, *pszP1, *pszP2;
748 char * psz, *pszEnd;
749 int cArgs;
750 char * apszArgs[30];
751 int iClass;
752 char * pszClass, *pszClassEnd;
753
754 /* 1.*/
755 iP1 = i;
756 while (papszLines[iP1] != NULL
757 && (pszP1 = strchr(papszLines[iP1], '(')) == NULL)
758 iP1++;
759 if (papszLines[iP1] == NULL)
760 {
761 fprintf(phSignal, "%d: oops! didn't find end of function!, %d\n", pszFilename, __LINE__);
762 iRet = iP1+1;
763 return 0x00010000;
764 }
765
766 /* 2. */
767 iFn = iP1;
768 if (papszLines[iFn] != pszP1)
769 pszFn = pszP1 - 1;
770 else
771 {
772 pszFn = papszLines[--iFn];
773 pszFn += strlen(pszFn) - (*pszFn != '\0');
774 }
775 while (iFn >= 0 && *pszFn == ' ')
776 {
777 if (pszFn != papszLines[iFn])
778 pszFn--;
779 else
780 {
781 pszFn = papszLines[--iFn];
782 pszFn += strlen(pszFn) - (*pszFn != '\0');
783 }
784 }
785 if (iFn < 0 || *pszFn == ' ' || *pszFn == '\0')
786 {
787 fprintf(phSignal, "%s: internal error!, %d\n", pszFilename, __LINE__);
788 iRet = iP1+1;
789 return 0x00010000;
790 }
791 pszFnEnd = pszFn;
792 pszFn = findStartOfWord(pszFn, papszLines[iFn]);
793
794 /* 2a. */
795 /* operators are not supported (BOOL kTime::operator > (const kTime &time) const) */
796 if (pszFn > papszLines[iFn])
797 {
798 pszClassEnd = pszFn - 1;
799 iClass = iFn;
800 }
801 else
802 {
803 pszClassEnd = pszFn - 1;
804 iClass = iFn - 1;
805 }
806 c = 2;
807 while (iClass >= 0 && c >= 0)
808 {
809 if (*pszClassEnd == ':')
810 c--;
811 else if (*pszClassEnd != ' ')
812 break;
813 pszClassEnd--;
814 }
815 if (*pszClassEnd != ' ' && c == 0)
816 pszClass = findStartOfWord(pszClassEnd, papszLines[iClass]);
817 else
818 pszClass = pszClassEnd = NULL;
819
820 /* 3. */
821 c = 1;
822 iP2 = iP1;
823 pszP2 = pszP1 + 1;
824 while (c > 0)
825 {
826 if (*pszP2 == '(')
827 c++;
828 else if (*pszP2 == ')')
829 if (--c == 0)
830 break;
831 if (*pszP2++ == '\0')
832 if ((pszP2 = papszLines[++iP2]) == NULL)
833 break;
834 }
835
836 iRet = iP2 + 1; //assumes: only one function on a single line!
837
838 /* 4. */
839 psz = pFnDesc->szFnDclBuffer;
840 copy(pFnDesc->szFnDclBuffer, pszP1, iP1, pszP2, iP2, papszLines);
841 pszEnd = psz + strlen(psz) + 1;
842
843 /* 5.*/
844 cArgs = 0;
845 if (stricmp(psz, "(void)") != 0 && strcmp(psz, "()") != 0 && strcmp(psz, "( )"))
846 {
847 char *pszC;
848 pszC = trim(psz+1);
849 c = 1;
850 while (*pszC != '\0')
851 {
852 apszArgs[cArgs] = pszC;
853 while (*pszC != ',' && c > 0 && *pszC != '\0')
854 {
855 if (*pszC == '(')
856 c++;
857 else if (*pszC == ')')
858 if (--c == 0)
859 break;
860 pszC++;
861 }
862 *pszC = '\0';
863 trim(apszArgs[cArgs++]);
864
865 /* next */
866 pszC = trim(pszC + 1);
867 }
868 }
869
870 /* 6. */
871 if (strnicmp(pszFn, "ODINFUNCTION", 12) == 0 || strnicmp(pszFn, "ODINPROCEDURE", 13) == 0)
872 {
873 BOOL fProc = strnicmp(pszFn, "ODINPROCEDURE", 13) == 0;
874 j = 0;
875 if (cArgs < (fProc ? 1 : 2))
876 {
877 fprintf(phSignal, "%s: Invalid ODINFUNCTION function too few parameters!\n", pszFilename);
878 return 0x00010000;
879 }
880
881 /* return type */
882 if (fProc)
883 pFnDesc->pszReturnType = "void";
884 else
885 pFnDesc->pszReturnType = apszArgs[j++];
886
887 /* function name */
888 pFnDesc->pszName = apszArgs[j++];
889
890 /* arguments */
891 pFnDesc->cParams = 0;
892 while (j+1 < cArgs)
893 {
894 pFnDesc->apszParamType[pFnDesc->cParams] = apszArgs[j];
895 pFnDesc->apszParamName[pFnDesc->cParams] = apszArgs[j+1];
896 pFnDesc->cParams++;
897 j += 2;
898 }
899 }
900 else
901 {
902 /* return type */
903 int iReturn = pszClass != NULL ? iClass : iFn;
904 char * pszReturn = pszClass != NULL ? pszClass : pszFn;
905
906 if (pszReturn != papszLines[iReturn])
907 pszReturn--;
908 else
909 {
910 pszReturn = papszLines[--iReturn];
911 pszReturn += strlen(pszReturn) - (*pszReturn != '\0');
912 }
913 pszReturn = skipBackwards("{};-+#:\"\'", pszReturn, iReturn, papszLines);
914 *pszEnd = '\0';
915 copy(pszEnd, pszReturn, iReturn, pszFn-1, iFn, papszLines);
916 if (strlen(pszEnd) > 1024)
917 {
918 fprintf(phSignal,"Fatal error! return statement is too larget. len=%d", strlen(pszEnd));
919 fprintf(phLog, "Fatal error! return statement is too larget. len=%d", strlen(pszEnd));
920 fprintf(stderr, "Fatal error! return statement is too larget. len=%d", strlen(pszEnd));
921 fflush(phLog);
922 fflush(phSignal);
923 fflush(stderr);
924 }
925 pszEnd = trim(pszEnd);
926 pFnDesc->pszReturnType = *pszEnd == '\0' ? NULL : pszEnd;
927 pszEnd = strlen(pszEnd) + pszEnd + 1;
928 *pszEnd = '\0';
929
930 /* function name */
931 if (pFnDesc->pszReturnType != NULL
932 && stristr(pFnDesc->pszReturnType, "cdecl") != NULL)
933 { /* cdecl function is prefixed with an '_' */
934 strcpy(pszEnd, "_");
935 }
936 if (pszClass != NULL)
937 {
938 strncat(pszEnd,pszClass, pszClassEnd - pszClass + 1);
939 strcat(pszEnd, "::");
940 }
941 strncat(pszEnd, pszFn, pszFnEnd - pszFn + 1);
942 pFnDesc->pszName = pszEnd;
943 pszEnd = strlen(pszEnd) + pszEnd + 1;
944 *pszEnd = '\0';
945
946
947 /* arguments */
948 pFnDesc->cParams = cArgs;
949 for (j = 0; j < cArgs; j++)
950 {
951 if ((psz = strchr(apszArgs[j], ')')) == NULL)
952 {
953 pFnDesc->apszParamName[j] = findStartOfWord(apszArgs[j] + strlen(apszArgs[j]) - 1,
954 apszArgs[j]);
955 pFnDesc->apszParamName[j][-1] = '\0';
956 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
957 }
958 else
959 {
960 char *pszP2 = psz;
961 psz = findStartOfWord(psz-1, apszArgs[j]);
962 strncat(pszEnd, psz, pszP2 - psz);
963
964 pFnDesc->apszParamName[j] = pszEnd;
965 memset(psz, ' ', pszP2 - psz);
966 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
967
968 pszEnd = strlen(pszEnd) + pszEnd + 1;
969 *pszEnd = '\0';
970 }
971 }
972 }
973 pOptions = pOptions;
974 return 0x00000001;
975}
976
977
978/**
979 * Analyses the function header.
980 * @returns high word = number of signals
981 * low word = number of APIs processed. (1 or 0).
982 * @param papszLines Array of lines in the file.
983 * @param i Index into papszLines.
984 * @param pszFilename Filename used in the signal log.
985 * @param pOptions Pointer to options.
986 * @sketch 1. Search backwards (start at i-1) for a comment or code.
987 * 2. If comment: (else fail)
988 * 2a. find start and end of comment.
989 * 2b. check for function header characteristics
990 * - lots of '*'s.
991 * - fields 'Status', 'Author' and 'Name'
992 * or if SDS, check for:
993 * - at least two '*'s at the begining.
994 * - fields '@status' and/or '@author'
995 * 2c. check that content of the 'Name' field matches (not SDS)
996 * 2d. read the status and author fields.
997 * @remark Supports both types of function headers, Odin32-common and SDS.
998 */
999static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions)
1000{
1001 int iStatus, iAuthor, iName, iStart, iEnd;
1002 int j, jStart;
1003 int fFound;
1004 int fSDS = 0;
1005 char *psz, *pszB;
1006 char *pszAuthor = NULL;
1007 unsigned long ulRc = 0x00000001;
1008
1009 pOptions = pOptions;
1010
1011 if (i < 0) /* parameter validation */
1012 return 0;
1013
1014 /* 1. + 2a.*/
1015 iEnd = i-1; /* find end */
1016 j = strlen(papszLines[iEnd]);
1017 j -= j > 0 ? 1 : 0;
1018 fFound = 0;
1019 while (iEnd >= 0 && i - iEnd < 7 && papszLines[iEnd][j] != '}' &&
1020 !(fFound = (papszLines[iEnd][j] == '*' && papszLines[iEnd][j+1] == '/')))
1021 if (j-- == 0)
1022 if (iEnd-- > 0)
1023 {
1024 j = strlen(papszLines[iEnd]);
1025 j -= j > 0 ? 1 : 0;
1026 }
1027 if (!fFound) /* fail if not found */
1028 return 0;
1029
1030 iStart = iEnd; /* find start */
1031 if (j < 2)
1032 j -= (j = strlen(papszLines[--iStart])) > 1 ? 2 : j;
1033 else
1034 j -= 2;
1035 fFound = 0;
1036 while (iStart >= 0 &&
1037 !(fFound = (papszLines[iStart][j] == '/' && papszLines[iStart][j+1] == '*')))
1038 if (j-- == 0)
1039 if (iStart-- > 0)
1040 {
1041 j = strlen(papszLines[iStart]);
1042 j -= j > 1 ? 2 : j;
1043 }
1044
1045 if (!fFound) /* fail if not found */
1046 return 0;
1047 jStart = j;
1048
1049 /* 2b.*/
1050 if (strncmp(&papszLines[iStart][jStart], "/**", 3) != 0) /* checks that there are more than one star at start of comment */
1051 return 0;
1052
1053 iName = iStart; /* Odin32 common */
1054 while (iName <= iEnd &&
1055 stristr(papszLines[iName], "* Name") == NULL)
1056 iName++;
1057 iStatus = iStart;
1058 while (iStatus <= iEnd &&
1059 stristr(papszLines[iStatus], "* Status") == NULL)
1060 iStatus++;
1061 iAuthor = iStart;
1062 while (iAuthor <= iEnd &&
1063 stristr(papszLines[iAuthor], "* Author") == NULL)
1064 iAuthor++;
1065
1066 if (!(iName <= iEnd || iStatus <= iEnd || iAuthor <= iEnd)) /* if not found try SDS */
1067 {
1068 iStatus = iStart;
1069 while (iStatus <= iEnd &&
1070 stristr(papszLines[iStatus], "@status") == NULL)
1071 iStatus++;
1072 iAuthor = iStart;
1073 while (iAuthor <= iEnd &&
1074 stristr(papszLines[iAuthor], "@author") == NULL)
1075 iAuthor++;
1076 if (!(iStatus <= iEnd || iAuthor <= iEnd))
1077 return 0;
1078 fSDS = 1;
1079 }
1080
1081 /* 2c.*/
1082 if (iName <= iEnd && strstr(papszLines[iName], pFnDesc->pszName) == NULL)
1083 fprintf(phLog, "Warning: a matching function name is not found in the name Field\n");
1084
1085 /* 2d.*/
1086 pszB = &pFnDesc->szFnHdrBuffer[0];
1087 if (!fSDS)
1088 { /* Odin32 common */
1089 if (iStatus <= iEnd) /* Status */
1090 {
1091 psz = stristr(papszLines[iStatus], "* Status") + sizeof("* Status") - 1;
1092 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
1093 psz++;
1094 strcpy(pszB, psz);
1095 trim(pszB);
1096 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
1097 {
1098 pszB[strlen(pszB)-1] = '\0';
1099 trim(pszB);
1100 }
1101 pFnDesc->pszStatus = pszB;
1102 pszB += strlen(pszB) + 1;
1103 }
1104
1105 if (iAuthor <= iEnd) /* Author(s) */
1106 {
1107 pszAuthor = pszB;
1108 psz = stristr(papszLines[iAuthor], "* Author") + sizeof("* Author") - 1;
1109 do
1110 {
1111 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
1112 psz++;
1113 strcpy(pszB, psz);
1114 trim(pszB);
1115 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
1116 {
1117 pszB[strlen(pszB)-1] = '\0';
1118 trim(pszB);
1119 }
1120 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
1121 strcat(pszB, ",");
1122 pszB += strlen(pszB);
1123
1124 /* next */
1125 psz = papszLines[++iAuthor] + 1;
1126 j = 0;
1127 while (*psz == ' ' && j++ < 5) psz++;
1128 if (*psz == '*')
1129 psz++;
1130 } while (iAuthor < iEnd && *psz == ' ');
1131 pszB++;
1132 }
1133 }
1134 else
1135 { /* SDS */
1136 if (iStatus <= iEnd)
1137 {
1138 psz = stristr(papszLines[iStatus], "@status") + sizeof("@status");
1139 while (*psz == ' ')
1140 psz++;
1141 strcpy(pszB, psz);
1142 trim(pszB);
1143 pszB += strlen(pszB) + 1;
1144 }
1145
1146 if (iAuthor <= iEnd)
1147 {
1148 pszAuthor = pszB;
1149 psz = stristr(papszLines[iAuthor], "@author") + sizeof("@author");
1150 do
1151 {
1152 while (*psz == ' ')
1153 psz++;
1154 strcpy(pszB, psz);
1155 trim(pszB);
1156 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
1157 strcat(pszB, ",");
1158 pszB += strlen(pszB) + 1;
1159
1160 /* next */
1161 psz = papszLines[++iAuthor] + 1;
1162 j = 0;
1163 while (*psz == ' ' && j++ < 5) psz++;
1164 if (*psz == '*')
1165 psz++;
1166 } while (iAuthor <= iEnd && (*psz == ' ' || *psz == '@'));
1167 pszB++;
1168 }
1169 }
1170
1171 /* Status - refcodes are hardcoded here! */
1172 if (pFnDesc->pszStatus != NULL && *pFnDesc->pszStatus != '\0')
1173 {
1174 if (strstr(pFnDesc->pszStatus, "STUB") != NULL || *pFnDesc->pszStatus == '1')
1175 pFnDesc->lStatus = 1; /* STUB */
1176 else if (stristr(pFnDesc->pszStatus, "Partially") != NULL || *pFnDesc->pszStatus == '2' || *pFnDesc->pszStatus == '3')
1177 {
1178 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '2')
1179 pFnDesc->lStatus = 2; /* STUB */
1180 else
1181 pFnDesc->lStatus = 3; /* STUB */
1182 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1183 || *pFnDesc->pszStatus == '5' || *pFnDesc->pszStatus == '7')
1184 pFnDesc->lStatus += 4;
1185 }
1186 else if (stristr(pFnDesc->pszStatus, "Completely") != NULL || *pFnDesc->pszStatus == '4' || *pFnDesc->pszStatus == '5')
1187 {
1188 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '4')
1189 pFnDesc->lStatus = 4; /* STUB */
1190 else
1191 pFnDesc->lStatus = 5; /* STUB */
1192 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1193 || *pFnDesc->pszStatus == '8' || *pFnDesc->pszStatus == '9')
1194 pFnDesc->lStatus += 4;
1195 }
1196 else
1197 {
1198 fprintf(phSignal, "%s, %s: '%s' is not a valid status code.\n",
1199 pszFilename, pFnDesc->pszName, pFnDesc->pszStatus);
1200 ulRc += 0x00010000;
1201 }
1202 }
1203
1204 /* Author */
1205 if (pszAuthor)
1206 { /* author1, author2, author3 */
1207 j = 0;
1208 psz = trim(pszAuthor);
1209 pFnDesc->cAuthors = 0;
1210 while (*psz != '\0' && pFnDesc->cAuthors < (int)(sizeof(pFnDesc->apszAuthor) / sizeof(pFnDesc->apszAuthor[0])))
1211 {
1212 char *pszBr1 = strchr(psz, '[');
1213 char *pszBr2 = strchr(psz, ']');
1214 char *pszComma;
1215
1216 /* remove '[text]' text - this is usualy used for time/date */
1217 if (pszBr1 != NULL && pszBr2 != NULL && pszBr1 < pszBr2)
1218 while (pszBr1 <= pszBr2)
1219 *pszBr1++ = ' ';
1220
1221 /* terminate string */
1222 pszComma = strchr(psz, ',');
1223 if (pszComma != NULL)
1224 {
1225 pszAuthor = pszComma + 1;
1226 *pszComma = '\0';
1227 }
1228
1229 pFnDesc->apszAuthor[pFnDesc->cAuthors] = trim(psz);
1230 pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] =
1231 dbFindAuthor(pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1232
1233 if (pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] == -1)
1234 {
1235 fprintf(phSignal, "%s, %s: author '%s' is not recognized.\n", pszFilename, pFnDesc->pszName,
1236 pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1237 ulRc += 0x00010000;
1238 }
1239
1240 /* next */
1241 pFnDesc->cAuthors++;
1242 psz = trim(pszAuthor);
1243 }
1244 }
1245
1246 return ulRc;
1247}
1248
1249
1250/**
1251 * Checks if there may be an function starting at the current line.
1252 * @returns TRUE if API found, else FALSE.
1253 * @param papszLines Array of lines in the file.
1254 * @param i Index into papszLines.
1255 * @param pOptions Pointer to options.
1256 */
1257static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions)
1258{
1259 if (!pOptions->fOld)
1260 { /* new API naming style */
1261 /* brief algorithm:
1262 * check that line don't start with '\\', '{' or '}'
1263 * search for '('.
1264 * if found then do
1265 * find c-word previous to '('
1266 * if found then do
1267 * check that the following condition are true:
1268 * 1. word is not 'for', 'while', 'do', 'if', 'else' or 'switch'
1269 * 2. first significant char after '(' should not be a '*'. (Fix for functionnames in function header, "BackupRead(".)
1270 * 3. find the matching ')' and check that the first significant char after it is '{'.
1271 * if 1, 2 and 3 are all true return true
1272 * return false.
1273 *
1274 * Note, for 2: spaces, newlines and comments are ignored, all other chars are significant.
1275 */
1276 char *pszP1 = papszLines[i];
1277
1278 while (*pszP1 != '\0' && *pszP1 == ' ')
1279 pszP1--;
1280 if (*pszP1 != '\0' && *pszP1 != '\\' && pszP1[1] != '\\'
1281 && *pszP1 != '{' && *pszP1 != '}')
1282 {
1283 pszP1 = strchr(papszLines[i], '(');
1284 if (pszP1 != NULL && pszP1 >= papszLines[i])
1285 {
1286 int cchFnName = 0;
1287 char *pszFnName = pszP1 - 1;
1288
1289 while (pszFnName - cchFnName > papszLines[0] && pszFnName[cchFnName] == ' ')
1290 cchFnName--, pszFnName--;
1291
1292 pszFnName = findStartOfWord(pszFnName, papszLines[0]);
1293 cchFnName += pszP1 - pszFnName;
1294 if (cchFnName >= 0)
1295 {
1296 /* 1. */
1297 if (
1298 strncmp(pszFnName, "for", cchFnName) != 0 &&
1299 strncmp(pszFnName, "while", cchFnName) != 0 &&
1300 strncmp(pszFnName, "do", cchFnName) != 0 &&
1301 strncmp(pszFnName, "if", cchFnName) != 0 &&
1302 strncmp(pszFnName, "else", cchFnName) != 0 &&
1303 strncmp(pszFnName, "switch", cchFnName) != 0
1304 )
1305 {
1306 /* 2. */
1307 int j = i;
1308 char *psz = skipInsignificantChars(papszLines, j, pszP1+1);
1309 if (psz != NULL && *psz != '*')
1310 {
1311 char *pszB = pszP1 + 1; /*'{'*/
1312 int c = 1;
1313
1314 /* 3. */
1315 while (c > 0)
1316 {
1317 if (*pszB == '(')
1318 c++;
1319 else if (*pszB == ')')
1320 if (--c == 0)
1321 break;
1322 if (*pszB++ == '\0')
1323 if ((pszB = papszLines[++i]) == NULL)
1324 break;
1325 }
1326 if (pszB != NULL)
1327 {
1328 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1329 if (pszB != NULL && *pszB == '{')
1330 {
1331 fprintf(phLog, "Function found: %.*s\n", cchFnName, pszFnName);
1332 return TRUE;
1333 }
1334 /* FIXME: constructors with ':' afterwards are not supported !TODO! */
1335 }
1336 }
1337 }
1338 }
1339 }
1340 }
1341 }
1342 else
1343 { /* old API naming style */
1344 char *pszOS2;
1345
1346 /* brief algorithm:
1347 * search for function prefix, 'OS2'.
1348 * if found then do
1349 * check that the following condition are true:
1350 * 1. char before 'OS2' is either start-of-line (no char), space or '*'.
1351 * 2. first significant char after the 'OS2' prefixed word is a '('.
1352 * 3. find the matching ')' and check that the first significant char after it is '{'.
1353 * if 1,2 and 3 all are true return true
1354 * return false.
1355 *
1356 * Note, for 2 and 3 spaces, newlines and comments are ignored, all other chars are significant.
1357 */
1358 pszOS2 = strstr(papszLines[i], "OS2");
1359 if (pszOS2 != NULL)
1360 {
1361 /* 1.*/
1362 if (pszOS2 == papszLines[i] || pszOS2[-1] == ' ' || pszOS2[-1] == '*')
1363 {
1364 char *pszP1; /*'('*/
1365 int cchFnName;
1366
1367 /* 2. */
1368 pszP1 = findEndOfWord(pszOS2);
1369 cchFnName = pszP1 - pszOS2;
1370 pszP1 = skipInsignificantChars(papszLines, i, pszP1);
1371
1372 if (pszP1 != NULL && *pszP1 == '(')
1373 {
1374 char *pszB = pszP1 + 1; /*'{'*/
1375 int c = 1;
1376
1377 /* 3. */
1378 while (c > 0)
1379 {
1380 if (*pszB == '(')
1381 c++;
1382 else if (*pszB == ')')
1383 if (--c == 0)
1384 break;
1385 if (*pszB++ == '\0')
1386 if ((pszB = papszLines[++i]) == NULL)
1387 break;
1388 }
1389 if (pszB != NULL)
1390 {
1391 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1392 if (pszB != NULL && *pszB == '{')
1393 {
1394 fprintf(phLog, "Possible API: %.*s\n", cchFnName, pszOS2);
1395 return TRUE;
1396 }
1397 }
1398 }
1399 }
1400 }
1401 }
1402
1403 return FALSE;
1404}
1405
1406
1407
1408/**
1409 * Callback function for the dbGetNotUpdatedFunction routine.
1410 *
1411 */
1412static long _System dbNotUpdatedCallBack(const char *pszValue, const char *pszFieldName, void *pvUser)
1413{
1414 static i = 0;
1415 switch (i++)
1416 {
1417 case 0:
1418 fprintf(phLog, "%s", pszValue);
1419 break;
1420 case 1:
1421 fprintf(phLog, "(%s)", pszValue);
1422 break;
1423 case 2: /* updated */
1424 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
1425 break;
1426 case 3: /* aliasfn */
1427 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
1428 break;
1429 case 4:
1430 if (pszValue != NULL)
1431 fprintf(phLog, " --> %s.", pszValue);
1432 break;
1433 case 5:
1434 if (pszValue != NULL)
1435 fprintf(phLog, "%s", pszValue);
1436 break;
1437 case 6:
1438 if (pszValue != NULL)
1439 fprintf(phLog, "(%s)", pszValue);
1440 break;
1441
1442 default:
1443 i = 0;
1444 fprintf(phLog, "\n");
1445 }
1446
1447 if (stricmp(pszFieldName, "last") == 0)
1448 {
1449 i = 0;
1450 fprintf(phLog, "\n");
1451 }
1452
1453 pvUser = pvUser;
1454 return 0;
1455}
1456
1457
1458/**
1459 * Skips insignificant chars (spaces, new-lines and comments)
1460 * @returns pointer to new string posision. NULL if end of file.
1461 * @param papszLines Pointer to line table.
1462 * @param i Index into line table. (current line)
1463 * @param psz Pointer where to start (within the current line).
1464 */
1465static char *skipInsignificantChars(char **papszLines, int &i, char *psz)
1466{
1467 BOOL fComment = *psz == '/' && psz[1] == '*';
1468
1469 while (fComment || *psz == ' ' || *psz == '\0' || (*psz == '/' && psz[1] == '/'))
1470 {
1471 if (*psz == '\0' || (*psz == '/' && psz[1] == '/' && !fComment))
1472 {
1473 if ((psz = papszLines[++i]) == NULL)
1474 break;
1475 }
1476 else
1477 psz++;
1478
1479 if (fComment)
1480 {
1481 if (!(fComment = *psz != '*' || psz[1] != '/'))
1482 psz += 2;
1483 else
1484 continue;
1485 }
1486
1487 if ((fComment = *psz == '/' && psz[1] == '*') == TRUE)
1488 psz += 1 + (psz[2] != '*'); // don't add two when there is a possible end comment following.
1489 }
1490
1491 return psz;
1492}
1493
1494
1495/**
1496 * Reads a file into memory.
1497 * @returns Pointer to file. This should be freed using 'free' when processing
1498 * the file is not needed.
1499 * @param pszFilename Name of file to read.
1500 * @remark Current implementation reads the file as a binary file.
1501 */
1502static char *readFileIntoMemory(const char *pszFilename)
1503{
1504 char *pszFile = NULL;
1505 int cbFile = 0; /* don't have to initialized, but it removes gcc warning (release) */
1506 FILE *phFile;
1507
1508 phFile = fopen(pszFilename, "rb");
1509 if (phFile != NULL)
1510 {
1511 if (!fseek(phFile, 0, SEEK_END)
1512 && (cbFile = (int)ftell(phFile)) > 0
1513 && !fseek(phFile, 0, SEEK_SET)
1514 )
1515 {
1516 pszFile = (char*)malloc(cbFile + 1);
1517 if (pszFile != NULL)
1518 {
1519 #if 1
1520 memset((void*)pszFile, 0, cbFile + 1);
1521 if (fread((void*)pszFile, 1, cbFile, phFile) == 1)
1522 {
1523 free(pszFile);
1524 pszFile = NULL;
1525 }
1526 #else
1527 int cLines = 0;
1528 int cch = 0;
1529 char *psz = pszFile;
1530
1531 while (!feof(phFile) && cch < cbFile &&
1532 fgets(psz, cbFile - cch, phFile) != NULL)
1533 {
1534 int cchLine;
1535 cch += cchLine = strlen(psz);
1536 psz += cchLine;
1537 cLines++;
1538 }
1539
1540 /* error check */
1541 if (cch > cbFile || !feof(phFile))
1542 {
1543 free(pszFile);
1544 pszFile = NULL;
1545 }
1546 else
1547 *psz = '\0';
1548 #endif
1549 }
1550 }
1551 fclose(phFile);
1552 }
1553
1554 return pszFile;
1555}
1556
1557
1558/**
1559 * Creates an array of lines from a "memory" file. The last entry in the array contains NULL.
1560 * @returns Pointer to the array of lines.
1561 * @param pszFile Pointer to "memory" file.
1562 */
1563static char **createLineArray(char *pszFile)
1564{
1565 char *psz = pszFile;
1566 char **papszLines = NULL;
1567 int cLines = 1;
1568
1569 while (*psz != '\0')
1570 {
1571 if (*psz == '\r')
1572 {
1573 if (psz[1] == '\n')
1574 psz++;
1575 cLines++;
1576 } else if (*psz == '\n')
1577 cLines++;
1578 psz++;
1579 }
1580 fprintf(phLog, "%d lines\n", cLines);
1581
1582 papszLines = (char**)calloc(cLines + 1, sizeof(char *));
1583 if (papszLines != NULL)
1584 {
1585 cLines = 1;
1586 papszLines[0] = psz = pszFile;
1587 while (*psz != '\0')
1588 {
1589 if (*psz == '\r')
1590 {
1591 if (psz[1] == '\n')
1592 *psz++ = '\0';
1593 papszLines[cLines++] = psz + 1;
1594 *psz = '\0';
1595 } else if (*psz == '\n')
1596 {
1597 *psz = '\0';
1598 papszLines[cLines++] = psz + 1;
1599 }
1600 psz++;
1601 }
1602 papszLines[cLines] = NULL; /* Strictly, this is not needed as we use calloc. */
1603 }
1604
1605
1606 return papszLines;
1607}
1608
1609
1610/** first char after word */
1611static char *findEndOfWord(char *psz)
1612{
1613
1614 while (*psz != '\0' &&
1615 (
1616 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
1617 ||
1618 (*psz >= '0' && *psz <= '9')
1619 ||
1620 *psz == '_'
1621 )
1622 )
1623 ++psz;
1624 return psz;
1625}
1626
1627
1628/** staring char of word */
1629static char *findStartOfWord(char *psz, const char *pszStart)
1630{
1631 char *pszR = psz;
1632 while (psz >= pszStart &&
1633 (
1634 (*psz >= 'A' && *psz <= 'Z')
1635 || (*psz >= 'a' && *psz <= 'z')
1636 || (*psz >= '0' && *psz <= '9')
1637 || *psz == '_'
1638 )
1639 )
1640 pszR = psz--;
1641 return pszR;
1642}
1643
1644
1645static char *trim(char *psz)
1646{
1647 int i;
1648 if (psz == NULL)
1649 return NULL;
1650 while (*psz == ' ' || *psz == '\t')
1651 psz++;
1652 i = strlen(psz) - 1;
1653 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
1654 i--;
1655 psz[i+1] = '\0';
1656 return psz;
1657}
1658
1659
1660/* copy: remove remarks, and unneeded spaces, ensuring no space after '(',
1661 * ensuring space after '*', ensuring no space before ',' and ')'.
1662 */
1663
1664static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines)
1665{
1666 copy(psz, pszFrom - papszLines[iFrom], iFrom, pszTo - papszLines[iTo], iTo, papszLines);
1667}
1668
1669#if 0
1670static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
1671{
1672 char chPrev = '\n';
1673 int i, j;
1674 int fComment = 0;
1675
1676 i = iFrom;
1677 j = jFrom;
1678 while (i < iTo || (i == iTo && j <= jTo))
1679 {
1680 if (papszLines[i][j] != '\0'
1681 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
1682 )
1683 {
1684 /* copy char ? */
1685 if (!fComment)
1686 {
1687 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
1688 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
1689 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
1690 )
1691 {
1692 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
1693 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
1694 else
1695 {
1696 chPrev = *psz++ = papszLines[i][j];
1697 if (chPrev == '*') /* ensure ' ' after '*' */
1698 chPrev = *psz++ = ' ';
1699 }
1700 }
1701 }
1702 else
1703 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == 0)
1704 j++;
1705 j++;
1706 }
1707 else
1708 {
1709 /* next */
1710 j = 0;
1711 i++;
1712 if (chPrev != ' ' && chPrev != '(')
1713 chPrev = *psz++ = ' ';
1714 }
1715 }
1716 *psz = '\0';
1717}
1718
1719#else
1720static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
1721{
1722 char chPrev = '\n';
1723 int i, j;
1724 int fComment = FALSE;
1725 int fFirst = TRUE;
1726
1727 i = iFrom;
1728 j = jFrom;
1729 while (i < iTo || (i == iTo && j <= jTo))
1730 {
1731 if (papszLines[i][j] != '\0'
1732 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
1733 && !(!fComment && fFirst && papszLines[i][j] == '#')
1734 )
1735 {
1736 fFirst = papszLines[i][j] == ' ';
1737
1738 /* copy char ? */
1739 if (!fComment)
1740 {
1741 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
1742
1743 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
1744 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
1745 )
1746 {
1747 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
1748 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
1749 else
1750 {
1751 chPrev = *psz++ = papszLines[i][j];
1752 if (chPrev == '*') /* ensure ' ' after '*' */
1753 chPrev = *psz++ = ' ';
1754 }
1755 }
1756
1757 }
1758 else
1759 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == FALSE)
1760 j++;
1761 j++;
1762 }
1763 else
1764 {
1765 /* next */
1766 j = 0;
1767 fFirst = TRUE;
1768 i++;
1769 if (chPrev != ' ' && chPrev != '(')
1770 chPrev = *psz++ = ' ';
1771 }
1772 }
1773 *psz = '\0';
1774}
1775#endif
1776
1777
1778/**
1779 * Upcases a char.
1780 * @returns Upper case of the char given in ch.
1781 * @param ch Char to capitalize.
1782 */
1783inline char upcase(char ch)
1784{
1785 return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch;
1786}
1787
1788
1789/**
1790 * Searches for a substring in a string.
1791 * @returns Pointer to start of substring when found, NULL when not found.
1792 * @param pszStr String to be searched.
1793 * @param pszSubStr String to be searched.
1794 * @remark Depends on the upcase function.
1795 */
1796static char *stristr(const char *pszStr, const char *pszSubStr)
1797{
1798 int cchSubStr = strlen(pszSubStr);
1799 int i = 0;
1800
1801 while (*pszStr != '\0' && i < cchSubStr)
1802 {
1803 i = 0;
1804 while (i < cchSubStr && pszStr[i] != '\0' &&
1805 (upcase(pszStr[i]) == upcase(pszSubStr[i])))
1806 i++;
1807 pszStr++;
1808 }
1809
1810 return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL);
1811}
1812
1813
1814
1815/**
1816 * Skips backwards until one of the chars in pszStopAt or star of file is reached.
1817 * @returns Pointer to end.
1818 * @param pszStopAt Array of chars which we should stop at.
1819 * @param pszFrom Start pointer.
1820 * @param iLine Reference to index to array of lines. input: start line, output: result line.
1821 * @param papszLines Array lines.
1822 * @sketch
1823 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1824 * @remark Comments are skipped.
1825 * No tests for strings ("...asdf").
1826 */
1827static char *skipBackwards(const char *pszStopAt, const char *pszFrom, int &iLine, char **papszLines)
1828{
1829 BOOL fComment = FALSE;
1830 int i = iLine;
1831 const char *psz = pszFrom;
1832
1833 while (i >= 0)
1834 {
1835 /* check for stop char */
1836 const char *psz1 = pszStopAt;
1837 while (*psz1 != '\0')
1838 if (*psz1++ == *psz)
1839 break;
1840 if (*psz1 != '\0')
1841 break;
1842
1843 /* comment check */
1844 if (!fComment && psz > papszLines[i] && *psz == '/' && psz[-1] == '*')
1845 fComment = TRUE;
1846 else if (fComment && *psz == '/' && psz[1] == '*')
1847 fComment = FALSE;
1848
1849 /* ok position to return? */
1850 if (!fComment)
1851 {
1852 iLine = i;
1853 pszFrom = psz;
1854 }
1855
1856 /* prev */
1857 if (psz > papszLines[i])
1858 psz--;
1859 else
1860 { /* try find line comment */
1861 do
1862 {
1863 char *pszStart = papszLines[--i];
1864 while (*pszStart == ' ')
1865 pszStart++;
1866 if (*pszStart != '\0' && *pszStart != '#'
1867 && !(*pszStart == '/' && pszStart[1] == '/'))
1868 { /* find '//' */
1869 pszStart = strstr(pszStart, "//");
1870 if (pszStart != NULL)
1871 psz = pszStart-1;
1872 else
1873 psz = papszLines[i] + strlen(papszLines[i]) - 1;
1874 break;
1875 }
1876 } while (i > 0);
1877 }
1878 }
1879
1880 return (char*)pszFrom;
1881}
1882
Note: See TracBrowser for help on using the repository browser.