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

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

Optimizations and a couple of fixes.

File size: 62.7 KB
Line 
1/* $Id: StateUpd.cpp,v 1.12 2000-02-14 13:49:13 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 char *pszErrorDesc = (char*)malloc(20480);
612
613 /* 2.*/
614 ulRcTmp = analyseFnHdr(&FnDesc, papszLines, i, pszFilename, pOptions);
615 if (ulRcTmp == ~0UL) /* check for fatal error */
616 return (0xffff0000UL & ulRc) + 0x00010000UL;
617 ulRc += 0xffff0000UL & ulRcTmp;
618
619 /* 3.*/
620 fprintf(phLog, "Name: '%s' (refcodes=", FnDesc.pszName);
621 for (j = 0; j < FnDesc.cRefCodes; j++)
622 fprintf(phLog, j > 0 ? ", %ld" : "%ld", FnDesc.alRefCode[j]);
623 fprintf(phLog, ")\n");
624 fprintf(phLog, " Returns: '%s'\n", FnDesc.pszReturnType != NULL ? FnDesc.pszReturnType : "<missing>");
625 fprintf(phLog, " cParams: %2d\n", FnDesc.cParams);
626 for (j = 0; j < FnDesc.cParams; j++)
627 fprintf(phLog, " Param %2d: type '%s' %*s name '%s'\n", j, FnDesc.apszParamType[j],
628 (int)(15 - strlen(FnDesc.apszParamType[j])), "", FnDesc.apszParamName[j]);
629 fprintf(phLog, " Status: %ld - '%s'\n", FnDesc.lStatus, FnDesc.pszStatus != NULL ? FnDesc.pszStatus : "<missing>");
630 fprintf(phLog, " cAuthors: %2d\n", FnDesc.cAuthors);
631 for (j = 0; j < FnDesc.cAuthors; j++)
632 fprintf(phLog, " Author %d: '%s' (refcode=%ld)\n", j, FnDesc.apszAuthor[j], FnDesc.alAuthorRefCode[j]);
633
634 /* 4.*/
635 ulRcTmp = dbUpdateFunction(&FnDesc, pOptions->lDllRefcode, pszErrorDesc);
636 if (ulRcTmp != 0)
637 {
638 fprintf(phSignal, "%s,%s: %s\n", pszFilename, FnDesc.pszName, pszErrorDesc);
639 ulRc += ulRcTmp << 16;
640 }
641 free(pszErrorDesc);
642 }
643
644 return ulRc;
645}
646
647
648/**
649 * Analyses the function declaration.
650 * @returns high word = number of signals
651 * low word = number of APIs processed. (1 or 0).
652 * @param papszLines Array of lines in the file.
653 * @param i Index into papszLines.
654 * @param iRet Index into papszLines. Where to start searching again.
655 * @param pszFilename Filename used in the signal log.
656 * @param pOptions Pointer to options.
657 */
658static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
659 const char *pszFilename, POPTIONS pOptions)
660{
661 static long lPrevFnDll = -1L; /* fix for duplicate dlls */
662 unsigned long ulRc;
663 FNFINDBUF FnFindBuf;
664 long lFn = 0;
665
666 /* brief algorithm:
667 * 1. read function declaration using analyseFnDcl2.
668 * 2. apply name rules.
669 * 3. do a database lookup on the name.
670 * 3b. if more that one match, write a signal. (TODO: a simple fix is done, but there are holes.)
671 */
672
673 /* 1. */
674 ulRc = analyseFnDcl2(pFnDesc, papszLines, i, iRet, pszFilename, pOptions);
675 if (ulRc != 1)
676 return ulRc;
677
678 /* 2. */
679 if (pOptions->fOS2 && strncmp(pFnDesc->pszName, "OS2", 3) == 0)
680 pFnDesc->pszName += 3;
681 else if (pOptions->fCOMCTL32 && strncmp(pFnDesc->pszName, "COMCTL32", 8) == 0)
682 pFnDesc->pszName += 8;
683 else if (pOptions->fVERSION && strncmp(pFnDesc->pszName, "VERSION", 7) == 0)
684 pFnDesc->pszName += 7;
685 else if (pOptions->fOld)
686 pFnDesc->pszName += 3;
687
688 /* 3. */
689 if (!dbFindFunction(pFnDesc->pszName, &FnFindBuf, pOptions->lDllRefcode))
690 {
691 fprintf(phSignal, "%s, %s: error occured while reading from database, %s\n",
692 pszFilename, pFnDesc->pszName, dbGetLastErrorDesc());
693 return 0x00010000;
694 }
695
696 pFnDesc->cRefCodes = 0;
697 if (FnFindBuf.cFns != 0)
698 {
699 if (pOptions->lDllRefcode < 0)
700 {
701 if (FnFindBuf.cFns > 1)
702 {
703 fprintf(phSignal, "%s: unknown dll and more than two occurences of this function!\n", pszFilename);
704 return 0x00010000;
705 }
706 pOptions->lDllRefcode = FnFindBuf.alDllRefCode[0];
707 fprintf(phLog, "DllRef = %d\n", pOptions->lDllRefcode);
708 }
709
710 for (lFn = 0; lFn < FnFindBuf.cFns; lFn++)
711 pFnDesc->alRefCode[pFnDesc->cRefCodes++] = FnFindBuf.alRefCode[lFn];
712
713 if (pFnDesc->cRefCodes == 0)
714 fprintf(phLog, "%s was not an API in this dll(%d)!\n", pFnDesc->pszName, pOptions->lDllRefcode);
715 }
716 else
717 fprintf(phLog, "%s was not an API\n", pFnDesc->pszName);
718
719 ulRc = pFnDesc->cRefCodes;
720 return ulRc;
721}
722
723
724
725/**
726 * Analyses the function declaration.
727 * No DB lockup or special function type stuff, only ODINFUNCTION is processed.
728 * @returns high word = number of signals
729 * low word = number of APIs processed. (1 or 0).
730 * @param papszLines Array of lines in the file.
731 * @param i Index into papszLines.
732 * @param iRet Index into papszLines. Where to start searching again.
733 * @param pszFilename Filename used in the signal log.
734 * @param pOptions Pointer to options.
735 */
736static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
737 const char *pszFilename, POPTIONS pOptions)
738{
739 /** @sketch
740 * 1. find the '('
741 * 2. find the word ahead of the '(', this is the function name.
742 * 2a. class test.
743 * 3. find the closing ')'
744 * 4. copy the parameters, which is between the two '()'
745 * 5. format the parameters
746 */
747
748 int iFn, iP1, iP2, j, c;
749 char * pszFn, *pszFnEnd, *pszP1, *pszP2;
750 char * psz, *pszEnd;
751 int cArgs;
752 char * apszArgs[30];
753 int iClass;
754 char * pszClass, *pszClassEnd;
755
756 /* 1.*/
757 iP1 = i;
758 while (papszLines[iP1] != NULL
759 && (pszP1 = strchr(papszLines[iP1], '(')) == NULL)
760 iP1++;
761 if (papszLines[iP1] == NULL)
762 {
763 fprintf(phSignal, "%d: oops! didn't find end of function!, %d\n", pszFilename, __LINE__);
764 iRet = iP1+1;
765 return 0x00010000;
766 }
767
768 /* 2. */
769 iFn = iP1;
770 if (papszLines[iFn] != pszP1)
771 pszFn = pszP1 - 1;
772 else
773 {
774 pszFn = papszLines[--iFn];
775 pszFn += strlen(pszFn) - (*pszFn != '\0');
776 }
777 while (iFn >= 0 && *pszFn == ' ')
778 {
779 if (pszFn != papszLines[iFn])
780 pszFn--;
781 else
782 {
783 pszFn = papszLines[--iFn];
784 pszFn += strlen(pszFn) - (*pszFn != '\0');
785 }
786 }
787 if (iFn < 0 || *pszFn == ' ' || *pszFn == '\0')
788 {
789 fprintf(phSignal, "%s: internal error!, %d\n", pszFilename, __LINE__);
790 iRet = iP1+1;
791 return 0x00010000;
792 }
793 pszFnEnd = pszFn;
794 pszFn = findStartOfWord(pszFn, papszLines[iFn]);
795
796 /* 2a. */
797 /* operators are not supported (BOOL kTime::operator > (const kTime &time) const) */
798 if (pszFn > papszLines[iFn])
799 {
800 pszClassEnd = pszFn - 1;
801 iClass = iFn;
802 }
803 else
804 {
805 pszClassEnd = pszFn - 1;
806 iClass = iFn - 1;
807 }
808 c = 2;
809 while (iClass >= 0 && c >= 0)
810 {
811 if (*pszClassEnd == ':')
812 c--;
813 else if (*pszClassEnd != ' ')
814 break;
815 pszClassEnd--;
816 }
817 if (*pszClassEnd != ' ' && c == 0)
818 pszClass = findStartOfWord(pszClassEnd, papszLines[iClass]);
819 else
820 pszClass = pszClassEnd = NULL;
821
822 /* 3. */
823 c = 1;
824 iP2 = iP1;
825 pszP2 = pszP1 + 1;
826 while (c > 0)
827 {
828 if (*pszP2 == '(')
829 c++;
830 else if (*pszP2 == ')')
831 if (--c == 0)
832 break;
833 if (*pszP2++ == '\0')
834 if ((pszP2 = papszLines[++iP2]) == NULL)
835 break;
836 }
837
838 iRet = iP2 + 1; //assumes: only one function on a single line!
839
840 /* 4. */
841 psz = pFnDesc->szFnDclBuffer;
842 copy(pFnDesc->szFnDclBuffer, pszP1, iP1, pszP2, iP2, papszLines);
843 pszEnd = psz + strlen(psz) + 1;
844
845 /* 5.*/
846 cArgs = 0;
847 if (stricmp(psz, "(void)") != 0 && strcmp(psz, "()") != 0 && strcmp(psz, "( )"))
848 {
849 char *pszC;
850 pszC = trim(psz+1);
851 c = 1;
852 while (*pszC != '\0')
853 {
854 apszArgs[cArgs] = pszC;
855 while (*pszC != ',' && c > 0 && *pszC != '\0')
856 {
857 if (*pszC == '(')
858 c++;
859 else if (*pszC == ')')
860 if (--c == 0)
861 break;
862 pszC++;
863 }
864 *pszC = '\0';
865 trim(apszArgs[cArgs++]);
866
867 /* next */
868 pszC = trim(pszC + 1);
869 }
870 }
871
872 /* 6. */
873 if (strnicmp(pszFn, "ODINFUNCTION", 12) == 0 || strnicmp(pszFn, "ODINPROCEDURE", 13) == 0)
874 {
875 BOOL fProc = strnicmp(pszFn, "ODINPROCEDURE", 13) == 0;
876 j = 0;
877 if (cArgs < (fProc ? 1 : 2))
878 {
879 fprintf(phSignal, "%s: Invalid ODINFUNCTION function too few parameters!\n", pszFilename);
880 return 0x00010000;
881 }
882
883 /* return type */
884 if (fProc)
885 pFnDesc->pszReturnType = "void";
886 else
887 pFnDesc->pszReturnType = apszArgs[j++];
888
889 /* function name */
890 pFnDesc->pszName = apszArgs[j++];
891
892 /* arguments */
893 pFnDesc->cParams = 0;
894 while (j+1 < cArgs)
895 {
896 pFnDesc->apszParamType[pFnDesc->cParams] = apszArgs[j];
897 pFnDesc->apszParamName[pFnDesc->cParams] = apszArgs[j+1];
898 pFnDesc->cParams++;
899 j += 2;
900 }
901 }
902 else
903 {
904 /* return type */
905 int iReturn = pszClass != NULL ? iClass : iFn;
906 char * pszReturn = pszClass != NULL ? pszClass : pszFn;
907
908 if (pszReturn != papszLines[iReturn])
909 pszReturn--;
910 else
911 {
912 pszReturn = papszLines[--iReturn];
913 pszReturn += strlen(pszReturn) - (*pszReturn != '\0');
914 }
915 pszReturn = skipBackwards("{};-+#:\"\'", pszReturn, iReturn, papszLines);
916 *pszEnd = '\0';
917 copy(pszEnd, pszReturn, iReturn, pszFn-1, iFn, papszLines);
918 if (strlen(pszEnd) > 128)
919 {
920 fprintf(phSignal,"Fatal error! return statement is too larget. len=%d", strlen(pszEnd));
921 fprintf(phLog, "Fatal error! return statement is too larget. len=%d", strlen(pszEnd));
922 fprintf(stderr, "Fatal error! return statement is too larget. len=%d", strlen(pszEnd));
923 fflush(phLog);
924 fflush(phSignal);
925 fflush(stderr);
926 }
927 pszEnd = trim(pszEnd);
928 pFnDesc->pszReturnType = *pszEnd == '\0' ? NULL : pszEnd;
929 pszEnd = strlen(pszEnd) + pszEnd + 1;
930 *pszEnd = '\0';
931
932 /* function name */
933 if (pFnDesc->pszReturnType != NULL
934 && stristr(pFnDesc->pszReturnType, "cdecl") != NULL)
935 { /* cdecl function is prefixed with an '_' */
936 strcpy(pszEnd, "_");
937 }
938 if (pszClass != NULL)
939 {
940 strncat(pszEnd,pszClass, pszClassEnd - pszClass + 1);
941 strcat(pszEnd, "::");
942 }
943 strncat(pszEnd, pszFn, pszFnEnd - pszFn + 1);
944 pFnDesc->pszName = pszEnd;
945 pszEnd = strlen(pszEnd) + pszEnd + 1;
946 *pszEnd = '\0';
947
948
949 /* arguments */
950 pFnDesc->cParams = cArgs;
951 for (j = 0; j < cArgs; j++)
952 {
953 if ((psz = strchr(apszArgs[j], ')')) == NULL)
954 { /* Common argument */
955 if (apszArgs[j][strlen(apszArgs[j]-1)] != '*')
956 { /* Normal case, Type [moretype] Name.*/
957 pFnDesc->apszParamName[j] = findStartOfWord(apszArgs[j] + strlen(apszArgs[j]) - 1,
958 apszArgs[j]);
959 pFnDesc->apszParamName[j][-1] = '\0';
960 }
961 else
962 { /* No argument name only type - make a dummy one: 'arg[1..n]' */
963 sprintf(pszEnd, "arg%i", j);
964 pFnDesc->apszParamName[j] = pszEnd;
965 pszEnd = strlen(pszEnd) + pszEnd + 1;
966 *pszEnd = '\0';
967 }
968 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
969 }
970 else
971 { /* Function pointer argument... */
972 char *pszP2 = psz;
973 psz = findStartOfWord(psz-1, apszArgs[j]);
974 strncat(pszEnd, psz, pszP2 - psz);
975
976 pFnDesc->apszParamName[j] = pszEnd;
977 memset(psz, ' ', pszP2 - psz);
978 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
979
980 pszEnd = strlen(pszEnd) + pszEnd + 1;
981 *pszEnd = '\0';
982 }
983 }
984 }
985 pOptions = pOptions;
986 return 0x00000001;
987}
988
989
990/**
991 * Analyses the function header.
992 * @returns high word = number of signals
993 * low word = number of APIs processed. (1 or 0).
994 * @param papszLines Array of lines in the file.
995 * @param i Index into papszLines.
996 * @param pszFilename Filename used in the signal log.
997 * @param pOptions Pointer to options.
998 * @sketch 1. Search backwards (start at i-1) for a comment or code.
999 * 2. If comment: (else fail)
1000 * 2a. find start and end of comment.
1001 * 2b. check for function header characteristics
1002 * - lots of '*'s.
1003 * - fields 'Status', 'Author' and 'Name'
1004 * or if SDS, check for:
1005 * - at least two '*'s at the begining.
1006 * - fields '@status' and/or '@author'
1007 * 2c. check that content of the 'Name' field matches (not SDS)
1008 * 2d. read the status and author fields.
1009 * @remark Supports both types of function headers, Odin32-common and SDS.
1010 */
1011static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions)
1012{
1013 int iStatus, iAuthor, iName, iStart, iEnd;
1014 int j, jStart;
1015 int fFound;
1016 int fSDS = 0;
1017 char *psz, *pszB;
1018 char *pszAuthor = NULL;
1019 unsigned long ulRc = 0x00000001;
1020
1021 pOptions = pOptions;
1022
1023 if (i < 0) /* parameter validation */
1024 return 0;
1025
1026 /* 1. + 2a.*/
1027 iEnd = i-1; /* find end */
1028 j = strlen(papszLines[iEnd]);
1029 j -= j > 0 ? 1 : 0;
1030 fFound = 0;
1031 while (iEnd >= 0 && i - iEnd < 7 && papszLines[iEnd][j] != '}' &&
1032 !(fFound = (papszLines[iEnd][j] == '*' && papszLines[iEnd][j+1] == '/')))
1033 if (j-- == 0)
1034 if (iEnd-- > 0)
1035 {
1036 j = strlen(papszLines[iEnd]);
1037 j -= j > 0 ? 1 : 0;
1038 }
1039 if (!fFound) /* fail if not found */
1040 return 0;
1041
1042 iStart = iEnd; /* find start */
1043 if (j < 2)
1044 j -= (j = strlen(papszLines[--iStart])) > 1 ? 2 : j;
1045 else
1046 j -= 2;
1047 fFound = 0;
1048 while (iStart >= 0 &&
1049 !(fFound = (papszLines[iStart][j] == '/' && papszLines[iStart][j+1] == '*')))
1050 if (j-- == 0)
1051 if (iStart-- > 0)
1052 {
1053 j = strlen(papszLines[iStart]);
1054 j -= j > 1 ? 2 : j;
1055 }
1056
1057 if (!fFound) /* fail if not found */
1058 return 0;
1059 jStart = j;
1060
1061 /* 2b.*/
1062 if (strncmp(&papszLines[iStart][jStart], "/**", 3) != 0) /* checks that there are more than one star at start of comment */
1063 return 0;
1064
1065 iName = iStart; /* Odin32 common */
1066 while (iName <= iEnd &&
1067 stristr(papszLines[iName], "* Name") == NULL)
1068 iName++;
1069 iStatus = iStart;
1070 while (iStatus <= iEnd &&
1071 stristr(papszLines[iStatus], "* Status") == NULL)
1072 iStatus++;
1073 iAuthor = iStart;
1074 while (iAuthor <= iEnd &&
1075 stristr(papszLines[iAuthor], "* Author") == NULL)
1076 iAuthor++;
1077
1078 if (!(iName <= iEnd || iStatus <= iEnd || iAuthor <= iEnd)) /* if not found try SDS */
1079 {
1080 iStatus = iStart;
1081 while (iStatus <= iEnd &&
1082 stristr(papszLines[iStatus], "@status") == NULL)
1083 iStatus++;
1084 iAuthor = iStart;
1085 while (iAuthor <= iEnd &&
1086 stristr(papszLines[iAuthor], "@author") == NULL)
1087 iAuthor++;
1088 if (!(iStatus <= iEnd || iAuthor <= iEnd))
1089 return 0;
1090 fSDS = 1;
1091 }
1092
1093 /* 2c.*/
1094 if (iName <= iEnd && strstr(papszLines[iName], pFnDesc->pszName) == NULL)
1095 fprintf(phLog, "Warning: a matching function name is not found in the name Field\n");
1096
1097 /* 2d.*/
1098 pszB = &pFnDesc->szFnHdrBuffer[0];
1099 if (!fSDS)
1100 { /* Odin32 common */
1101 if (iStatus <= iEnd) /* Status */
1102 {
1103 psz = stristr(papszLines[iStatus], "* Status") + sizeof("* Status") - 1;
1104 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
1105 psz++;
1106 strcpy(pszB, psz);
1107 trim(pszB);
1108 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
1109 {
1110 pszB[strlen(pszB)-1] = '\0';
1111 trim(pszB);
1112 }
1113 pFnDesc->pszStatus = pszB;
1114 pszB += strlen(pszB) + 1;
1115 }
1116
1117 if (iAuthor <= iEnd) /* Author(s) */
1118 {
1119 pszAuthor = pszB;
1120 psz = stristr(papszLines[iAuthor], "* Author") + sizeof("* Author") - 1;
1121 do
1122 {
1123 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
1124 psz++;
1125 strcpy(pszB, psz);
1126 trim(pszB);
1127 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
1128 {
1129 pszB[strlen(pszB)-1] = '\0';
1130 trim(pszB);
1131 }
1132 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
1133 strcat(pszB, ",");
1134 pszB += strlen(pszB);
1135
1136 /* next */
1137 psz = papszLines[++iAuthor] + 1;
1138 j = 0;
1139 while (*psz == ' ' && j++ < 5) psz++;
1140 if (*psz == '*')
1141 psz++;
1142 } while (iAuthor < iEnd && *psz == ' ');
1143 pszB++;
1144 }
1145 }
1146 else
1147 { /* SDS */
1148 if (iStatus <= iEnd)
1149 {
1150 psz = stristr(papszLines[iStatus], "@status") + sizeof("@status");
1151 while (*psz == ' ')
1152 psz++;
1153 strcpy(pszB, psz);
1154 trim(pszB);
1155 pszB += strlen(pszB) + 1;
1156 }
1157
1158 if (iAuthor <= iEnd)
1159 {
1160 pszAuthor = pszB;
1161 psz = stristr(papszLines[iAuthor], "@author") + sizeof("@author");
1162 do
1163 {
1164 while (*psz == ' ')
1165 psz++;
1166 strcpy(pszB, psz);
1167 trim(pszB);
1168 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
1169 strcat(pszB, ",");
1170 pszB += strlen(pszB) + 1;
1171
1172 /* next */
1173 psz = papszLines[++iAuthor] + 1;
1174 j = 0;
1175 while (*psz == ' ' && j++ < 5) psz++;
1176 if (*psz == '*')
1177 psz++;
1178 } while (iAuthor <= iEnd && (*psz == ' ' || *psz == '@'));
1179 pszB++;
1180 }
1181 }
1182
1183 /* Status - refcodes are hardcoded here! */
1184 if (pFnDesc->pszStatus != NULL && *pFnDesc->pszStatus != '\0')
1185 {
1186 if (strstr(pFnDesc->pszStatus, "STUB") != NULL || *pFnDesc->pszStatus == '1')
1187 pFnDesc->lStatus = 1; /* STUB */
1188 else if (stristr(pFnDesc->pszStatus, "Partially") != NULL || *pFnDesc->pszStatus == '2' || *pFnDesc->pszStatus == '3')
1189 {
1190 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '2')
1191 pFnDesc->lStatus = 2; /* STUB */
1192 else
1193 pFnDesc->lStatus = 3; /* STUB */
1194 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1195 || *pFnDesc->pszStatus == '5' || *pFnDesc->pszStatus == '7')
1196 pFnDesc->lStatus += 4;
1197 }
1198 else if (stristr(pFnDesc->pszStatus, "Completely") != NULL || *pFnDesc->pszStatus == '4' || *pFnDesc->pszStatus == '5')
1199 {
1200 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '4')
1201 pFnDesc->lStatus = 4; /* STUB */
1202 else
1203 pFnDesc->lStatus = 5; /* STUB */
1204 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1205 || *pFnDesc->pszStatus == '8' || *pFnDesc->pszStatus == '9')
1206 pFnDesc->lStatus += 4;
1207 }
1208 else
1209 {
1210 fprintf(phSignal, "%s, %s: '%s' is not a valid status code.\n",
1211 pszFilename, pFnDesc->pszName, pFnDesc->pszStatus);
1212 ulRc += 0x00010000;
1213 }
1214 }
1215
1216 /* Author */
1217 if (pszAuthor)
1218 { /* author1, author2, author3 */
1219 j = 0;
1220 psz = trim(pszAuthor);
1221 pFnDesc->cAuthors = 0;
1222 while (*psz != '\0' && pFnDesc->cAuthors < (int)(sizeof(pFnDesc->apszAuthor) / sizeof(pFnDesc->apszAuthor[0])))
1223 {
1224 char *pszBr1 = strchr(psz, '[');
1225 char *pszBr2 = strchr(psz, ']');
1226 char *pszComma;
1227
1228 /* remove '[text]' text - this is usualy used for time/date */
1229 if (pszBr1 != NULL && pszBr2 != NULL && pszBr1 < pszBr2)
1230 while (pszBr1 <= pszBr2)
1231 *pszBr1++ = ' ';
1232
1233 /* terminate string */
1234 pszComma = strchr(psz, ',');
1235 if (pszComma != NULL)
1236 {
1237 pszAuthor = pszComma + 1;
1238 *pszComma = '\0';
1239 }
1240
1241 pFnDesc->apszAuthor[pFnDesc->cAuthors] = trim(psz);
1242 pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] =
1243 dbFindAuthor(pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1244
1245 if (pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] == -1)
1246 {
1247 fprintf(phSignal, "%s, %s: author '%s' is not recognized.\n", pszFilename, pFnDesc->pszName,
1248 pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1249 ulRc += 0x00010000;
1250 }
1251
1252 /* next */
1253 pFnDesc->cAuthors++;
1254 psz = trim(pszAuthor);
1255 }
1256 }
1257
1258 return ulRc;
1259}
1260
1261
1262/**
1263 * Checks if there may be an function starting at the current line.
1264 * @returns TRUE if API found, else FALSE.
1265 * @param papszLines Array of lines in the file.
1266 * @param i Index into papszLines.
1267 * @param pOptions Pointer to options.
1268 */
1269static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions)
1270{
1271 if (!pOptions->fOld)
1272 { /* new API naming style */
1273 /* brief algorithm:
1274 * check that line don't start with '\\', '{' or '}'
1275 * search for '('.
1276 * if found then do
1277 * find c-word previous to '('
1278 * if found then do
1279 * check that the following condition are true:
1280 * 1. word is not 'for', 'while', 'do', 'if', 'else' or 'switch'
1281 * 2. first significant char after '(' should not be a '*'. (Fix for functionnames in function header, "BackupRead(".)
1282 * 3. find the matching ')' and check that the first significant char after it is '{'.
1283 * if 1, 2 and 3 are all true return true
1284 * return false.
1285 *
1286 * Note, for 2: spaces, newlines and comments are ignored, all other chars are significant.
1287 */
1288 char *pszP1 = papszLines[i];
1289
1290 while (*pszP1 != '\0' && *pszP1 == ' ')
1291 pszP1--;
1292 if (*pszP1 != '\0' && *pszP1 != '\\' && pszP1[1] != '\\'
1293 && *pszP1 != '{' && *pszP1 != '}')
1294 {
1295 pszP1 = strchr(papszLines[i], '(');
1296 if (pszP1 != NULL && pszP1 >= papszLines[i])
1297 {
1298 int cchFnName = 0;
1299 char *pszFnName = pszP1 - 1;
1300
1301 while (pszFnName - cchFnName > papszLines[0] && pszFnName[cchFnName] == ' ')
1302 cchFnName--, pszFnName--;
1303
1304 pszFnName = findStartOfWord(pszFnName, papszLines[0]);
1305 cchFnName += pszP1 - pszFnName;
1306 if (cchFnName >= 0)
1307 {
1308 /* 1. */
1309 if (
1310 strncmp(pszFnName, "for", cchFnName) != 0 &&
1311 strncmp(pszFnName, "while", cchFnName) != 0 &&
1312 strncmp(pszFnName, "do", cchFnName) != 0 &&
1313 strncmp(pszFnName, "if", cchFnName) != 0 &&
1314 strncmp(pszFnName, "else", cchFnName) != 0 &&
1315 strncmp(pszFnName, "switch", cchFnName) != 0
1316 )
1317 {
1318 /* 2. */
1319 int j = i;
1320 char *psz = skipInsignificantChars(papszLines, j, pszP1+1);
1321 if (psz != NULL && *psz != '*')
1322 {
1323 char *pszB = pszP1 + 1; /*'{'*/
1324 int c = 1;
1325
1326 /* 3. */
1327 while (c > 0)
1328 {
1329 if (*pszB == '(')
1330 c++;
1331 else if (*pszB == ')')
1332 if (--c == 0)
1333 break;
1334 if (*pszB++ == '\0')
1335 if ((pszB = papszLines[++i]) == NULL)
1336 break;
1337 }
1338 if (pszB != NULL)
1339 {
1340 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1341 if (pszB != NULL && *pszB == '{')
1342 {
1343 fprintf(phLog, "Function found: %.*s\n", cchFnName, pszFnName);
1344 return TRUE;
1345 }
1346 /* FIXME: constructors with ':' afterwards are not supported !TODO! */
1347 }
1348 }
1349 }
1350 }
1351 }
1352 }
1353 }
1354 else
1355 { /* old API naming style */
1356 char *pszOS2;
1357
1358 /* brief algorithm:
1359 * search for function prefix, 'OS2'.
1360 * if found then do
1361 * check that the following condition are true:
1362 * 1. char before 'OS2' is either start-of-line (no char), space or '*'.
1363 * 2. first significant char after the 'OS2' prefixed word is a '('.
1364 * 3. find the matching ')' and check that the first significant char after it is '{'.
1365 * if 1,2 and 3 all are true return true
1366 * return false.
1367 *
1368 * Note, for 2 and 3 spaces, newlines and comments are ignored, all other chars are significant.
1369 */
1370 pszOS2 = strstr(papszLines[i], "OS2");
1371 if (pszOS2 != NULL)
1372 {
1373 /* 1.*/
1374 if (pszOS2 == papszLines[i] || pszOS2[-1] == ' ' || pszOS2[-1] == '*')
1375 {
1376 char *pszP1; /*'('*/
1377 int cchFnName;
1378
1379 /* 2. */
1380 pszP1 = findEndOfWord(pszOS2);
1381 cchFnName = pszP1 - pszOS2;
1382 pszP1 = skipInsignificantChars(papszLines, i, pszP1);
1383
1384 if (pszP1 != NULL && *pszP1 == '(')
1385 {
1386 char *pszB = pszP1 + 1; /*'{'*/
1387 int c = 1;
1388
1389 /* 3. */
1390 while (c > 0)
1391 {
1392 if (*pszB == '(')
1393 c++;
1394 else if (*pszB == ')')
1395 if (--c == 0)
1396 break;
1397 if (*pszB++ == '\0')
1398 if ((pszB = papszLines[++i]) == NULL)
1399 break;
1400 }
1401 if (pszB != NULL)
1402 {
1403 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1404 if (pszB != NULL && *pszB == '{')
1405 {
1406 fprintf(phLog, "Possible API: %.*s\n", cchFnName, pszOS2);
1407 return TRUE;
1408 }
1409 }
1410 }
1411 }
1412 }
1413 }
1414
1415 return FALSE;
1416}
1417
1418
1419
1420/**
1421 * Callback function for the dbGetNotUpdatedFunction routine.
1422 *
1423 */
1424static long _System dbNotUpdatedCallBack(const char *pszValue, const char *pszFieldName, void *pvUser)
1425{
1426 static i = 0;
1427 switch (i++)
1428 {
1429 case 0:
1430 fprintf(phLog, "%s", pszValue);
1431 break;
1432 case 1:
1433 fprintf(phLog, "(%s)", pszValue);
1434 break;
1435 case 2: /* updated */
1436 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
1437 break;
1438 case 3: /* aliasfn */
1439 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
1440 break;
1441 case 4:
1442 if (pszValue != NULL)
1443 fprintf(phLog, " --> %s.", pszValue);
1444 break;
1445 case 5:
1446 if (pszValue != NULL)
1447 fprintf(phLog, "%s", pszValue);
1448 break;
1449 case 6:
1450 if (pszValue != NULL)
1451 fprintf(phLog, "(%s)", pszValue);
1452 break;
1453
1454 default:
1455 i = 0;
1456 fprintf(phLog, "\n");
1457 }
1458
1459 if (stricmp(pszFieldName, "last") == 0)
1460 {
1461 i = 0;
1462 fprintf(phLog, "\n");
1463 }
1464
1465 pvUser = pvUser;
1466 return 0;
1467}
1468
1469
1470/**
1471 * Skips insignificant chars (spaces, new-lines and comments)
1472 * @returns pointer to new string posision. NULL if end of file.
1473 * @param papszLines Pointer to line table.
1474 * @param i Index into line table. (current line)
1475 * @param psz Pointer where to start (within the current line).
1476 */
1477static char *skipInsignificantChars(char **papszLines, int &i, char *psz)
1478{
1479 BOOL fComment = *psz == '/' && psz[1] == '*';
1480
1481 while (fComment || *psz == ' ' || *psz == '\0' || (*psz == '/' && psz[1] == '/'))
1482 {
1483 if (*psz == '\0' || (*psz == '/' && psz[1] == '/' && !fComment))
1484 {
1485 if ((psz = papszLines[++i]) == NULL)
1486 break;
1487 }
1488 else
1489 psz++;
1490
1491 if (fComment)
1492 {
1493 if (!(fComment = *psz != '*' || psz[1] != '/'))
1494 psz += 2;
1495 else
1496 continue;
1497 }
1498
1499 if ((fComment = *psz == '/' && psz[1] == '*') == TRUE)
1500 psz += 1 + (psz[2] != '*'); // don't add two when there is a possible end comment following.
1501 }
1502
1503 return psz;
1504}
1505
1506
1507/**
1508 * Reads a file into memory.
1509 * @returns Pointer to file. This should be freed using 'free' when processing
1510 * the file is not needed.
1511 * @param pszFilename Name of file to read.
1512 * @remark Current implementation reads the file as a binary file.
1513 */
1514static char *readFileIntoMemory(const char *pszFilename)
1515{
1516 char *pszFile = NULL;
1517 int cbFile = 0; /* don't have to initialized, but it removes gcc warning (release) */
1518 FILE *phFile;
1519
1520 phFile = fopen(pszFilename, "rb");
1521 if (phFile != NULL)
1522 {
1523 if (!fseek(phFile, 0, SEEK_END)
1524 && (cbFile = (int)ftell(phFile)) > 0
1525 && !fseek(phFile, 0, SEEK_SET)
1526 )
1527 {
1528 pszFile = (char*)malloc(cbFile + 1);
1529 if (pszFile != NULL)
1530 {
1531 #if 1
1532 memset((void*)pszFile, 0, cbFile + 1);
1533 if (fread((void*)pszFile, 1, cbFile, phFile) == 1)
1534 {
1535 free(pszFile);
1536 pszFile = NULL;
1537 }
1538 #else
1539 int cLines = 0;
1540 int cch = 0;
1541 char *psz = pszFile;
1542
1543 while (!feof(phFile) && cch < cbFile &&
1544 fgets(psz, cbFile - cch, phFile) != NULL)
1545 {
1546 int cchLine;
1547 cch += cchLine = strlen(psz);
1548 psz += cchLine;
1549 cLines++;
1550 }
1551
1552 /* error check */
1553 if (cch > cbFile || !feof(phFile))
1554 {
1555 free(pszFile);
1556 pszFile = NULL;
1557 }
1558 else
1559 *psz = '\0';
1560 #endif
1561 }
1562 }
1563 fclose(phFile);
1564 }
1565
1566 return pszFile;
1567}
1568
1569
1570/**
1571 * Creates an array of lines from a "memory" file. The last entry in the array contains NULL.
1572 * @returns Pointer to the array of lines.
1573 * @param pszFile Pointer to "memory" file.
1574 */
1575static char **createLineArray(char *pszFile)
1576{
1577 char *psz = pszFile;
1578 char **papszLines = NULL;
1579 int cLines = 1;
1580
1581 while (*psz != '\0')
1582 {
1583 if (*psz == '\r')
1584 {
1585 if (psz[1] == '\n')
1586 psz++;
1587 cLines++;
1588 } else if (*psz == '\n')
1589 cLines++;
1590 psz++;
1591 }
1592 fprintf(phLog, "%d lines\n", cLines);
1593
1594 papszLines = (char**)calloc(cLines + 1, sizeof(char *));
1595 if (papszLines != NULL)
1596 {
1597 cLines = 1;
1598 papszLines[0] = psz = pszFile;
1599 while (*psz != '\0')
1600 {
1601 if (*psz == '\r')
1602 {
1603 if (psz[1] == '\n')
1604 *psz++ = '\0';
1605 papszLines[cLines++] = psz + 1;
1606 *psz = '\0';
1607 } else if (*psz == '\n')
1608 {
1609 *psz = '\0';
1610 papszLines[cLines++] = psz + 1;
1611 }
1612 psz++;
1613 }
1614 papszLines[cLines] = NULL; /* Strictly, this is not needed as we use calloc. */
1615 }
1616
1617
1618 return papszLines;
1619}
1620
1621
1622/** first char after word */
1623static char *findEndOfWord(char *psz)
1624{
1625
1626 while (*psz != '\0' &&
1627 (
1628 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
1629 ||
1630 (*psz >= '0' && *psz <= '9')
1631 ||
1632 *psz == '_'
1633 )
1634 )
1635 ++psz;
1636 return psz;
1637}
1638
1639
1640/** staring char of word */
1641static char *findStartOfWord(char *psz, const char *pszStart)
1642{
1643 char *pszR = psz;
1644 while (psz >= pszStart &&
1645 (
1646 (*psz >= 'A' && *psz <= 'Z')
1647 || (*psz >= 'a' && *psz <= 'z')
1648 || (*psz >= '0' && *psz <= '9')
1649 || *psz == '_'
1650 )
1651 )
1652 pszR = psz--;
1653 return pszR;
1654}
1655
1656
1657/**
1658 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
1659 * @returns Pointer to first not space or tab char in the string.
1660 * @param psz Pointer to the string which is to be trimmed.
1661 * @status completely implmented.
1662 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1663 */
1664static char *trim(char *psz)
1665{
1666 int i;
1667 if (psz == NULL)
1668 return NULL;
1669 while (*psz == ' ' || *psz == '\t')
1670 psz++;
1671 i = strlen(psz) - 1;
1672 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
1673 i--;
1674 psz[i+1] = '\0';
1675 return psz;
1676}
1677
1678
1679/* copy: remove remarks, and unneeded spaces, ensuring no space after '(',
1680 * ensuring space after '*', ensuring no space before ',' and ')'.
1681 */
1682static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines)
1683{
1684 copy(psz, pszFrom - papszLines[iFrom], iFrom, pszTo - papszLines[iTo], iTo, papszLines);
1685}
1686
1687#if 0
1688static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
1689{
1690 char chPrev = '\n';
1691 int i, j;
1692 int fComment = 0;
1693
1694 i = iFrom;
1695 j = jFrom;
1696 while (i < iTo || (i == iTo && j <= jTo))
1697 {
1698 if (papszLines[i][j] != '\0'
1699 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
1700 )
1701 {
1702 /* copy char ? */
1703 if (!fComment)
1704 {
1705 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
1706 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
1707 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
1708 )
1709 {
1710 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
1711 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
1712 else
1713 {
1714 chPrev = *psz++ = papszLines[i][j];
1715 if (chPrev == '*') /* ensure ' ' after '*' */
1716 chPrev = *psz++ = ' ';
1717 }
1718 }
1719 }
1720 else
1721 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == 0)
1722 j++;
1723 j++;
1724 }
1725 else
1726 {
1727 /* next */
1728 j = 0;
1729 i++;
1730 if (chPrev != ' ' && chPrev != '(')
1731 chPrev = *psz++ = ' ';
1732 }
1733 }
1734 *psz = '\0';
1735}
1736
1737#else
1738/**
1739 * Copies a set of lines to a buffer (psz) removing precompiler lines and all comments.
1740 * @param psz
1741 * @param jFrom Starting position, index into line iFrom.
1742 * @param iFrom Starting position, index into papszLines.
1743 * @param jTo Ending position, index into line iFrom.
1744 * @param iTo Ending position, index into papszLines.
1745 * @param papszLines Array of lines.
1746 * @status completely implemented.
1747 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1748 */
1749static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
1750{
1751 char chPrev = '\n';
1752 int i, j;
1753 int fComment = FALSE;
1754 int fFirst = TRUE;
1755
1756 i = iFrom;
1757 j = jFrom;
1758 while (i < iTo || (i == iTo && j <= jTo))
1759 {
1760 if (papszLines[i][j] != '\0'
1761 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
1762 && !(!fComment && fFirst && papszLines[i][j] == '#')
1763 )
1764 {
1765 fFirst = papszLines[i][j] == ' ';
1766
1767 /* copy char ? */
1768 if (!fComment)
1769 {
1770 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
1771
1772 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
1773 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
1774 )
1775 {
1776 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
1777 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
1778 else
1779 {
1780 chPrev = *psz++ = papszLines[i][j];
1781 if (chPrev == '*') /* ensure ' ' after '*' */
1782 chPrev = *psz++ = ' ';
1783 }
1784 }
1785
1786 }
1787 else
1788 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == FALSE)
1789 j++;
1790 j++;
1791 }
1792 else
1793 {
1794 /* next */
1795 j = 0;
1796 fFirst = TRUE;
1797 i++;
1798 if (chPrev != ' ' && chPrev != '(')
1799 chPrev = *psz++ = ' ';
1800 }
1801 }
1802 *psz = '\0';
1803}
1804#endif
1805
1806
1807/**
1808 * Upcases a char.
1809 * @returns Upper case of the char given in ch.
1810 * @param ch Char to capitalize.
1811 */
1812#if 0
1813inline char upcase(char ch)
1814{
1815 return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch;
1816}
1817#else
1818#define upcase(ch) ((ch) >= 'a' && (ch) <= 'z' ? (char)((ch) - ('a' - 'A')) : (ch))
1819#endif
1820
1821
1822/**
1823 * Searches for a substring in a string.
1824 * @returns Pointer to start of substring when found, NULL when not found.
1825 * @param pszStr String to be searched.
1826 * @param pszSubStr String to be searched.
1827 * @remark Depends on the upcase function.
1828 */
1829static char *stristr(const char *pszStr, const char *pszSubStr)
1830{
1831 int cchSubStr = strlen(pszSubStr);
1832 int i = 0;
1833
1834 while (*pszStr != '\0' && i < cchSubStr)
1835 {
1836 i = 0;
1837 while (i < cchSubStr && pszStr[i] != '\0' &&
1838 (upcase(pszStr[i]) == upcase(pszSubStr[i])))
1839 i++;
1840 pszStr++;
1841 }
1842
1843 return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL);
1844}
1845
1846
1847
1848/**
1849 * Skips backwards until one of the chars in pszStopAt or star of file is reached.
1850 * @returns Pointer to end.
1851 * @param pszStopAt Array of chars which we should stop at.
1852 * @param pszFrom Start pointer.
1853 * @param iLine Reference to index to array of lines. input: start line, output: result line.
1854 * @param papszLines Array lines.
1855 * @sketch
1856 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1857 * @remark Comments are skipped.
1858 * No tests for strings ("...asdf").
1859 */
1860static char *skipBackwards(const char *pszStopAt, const char *pszFrom, int &iLine, char **papszLines)
1861{
1862 BOOL fComment = FALSE;
1863 int i = iLine;
1864 const char *psz = pszFrom;
1865
1866 while (i >= 0)
1867 {
1868 /* check for stop char */
1869 const char *psz1 = pszStopAt;
1870 while (*psz1 != '\0')
1871 if (*psz1++ == *psz)
1872 break;
1873 if (*psz1 != '\0')
1874 break;
1875
1876 /* comment check */
1877 if (!fComment && psz > papszLines[i] && *psz == '/' && psz[-1] == '*')
1878 fComment = TRUE;
1879 else if (fComment && *psz == '/' && psz[1] == '*')
1880 fComment = FALSE;
1881
1882 /* ok position to return? */
1883 if (!fComment)
1884 {
1885 iLine = i;
1886 pszFrom = psz;
1887 }
1888
1889 /* prev */
1890 if (psz > papszLines[i])
1891 psz--;
1892 else
1893 { /* try find line comment */
1894 do
1895 {
1896 char *pszStart = papszLines[--i];
1897 while (*pszStart == ' ')
1898 pszStart++;
1899 if (*pszStart != '\0' && *pszStart != '#'
1900 && !(*pszStart == '/' && pszStart[1] == '/'))
1901 { /* find '//' */
1902 pszStart = strstr(pszStart, "//");
1903 if (pszStart != NULL)
1904 psz = pszStart-1;
1905 else
1906 psz = papszLines[i] + strlen(papszLines[i]) - 1;
1907 break;
1908 }
1909 } while (i > 0);
1910 }
1911 }
1912
1913 return (char*)pszFrom;
1914}
1915
Note: See TracBrowser for help on using the repository browser.