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

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

More fixes.

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