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

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

Arg!

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