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

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

Preformance and corrections.

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