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

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

Bugfixes. Nearly there now!

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