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

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

Some new reports.
General bugfixes and new features.

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