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

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

Eliptic '...' args.

File size: 66.2 KB
Line 
1/* $Id: StateUpd.cpp,v 1.18 2000-02-15 13:37:52 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\n", strlen(pszEnd));
934 fprintf(phLog, "Fatal error? return statement is too larget. len=%d\n", strlen(pszEnd));
935 if (strlen(pszEnd) > 512)
936 fprintf(stderr, "Fatal error? return statement is too larget. len=%d\n", 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 {
986 if (strcmp(apszArgs[j], "...") != 0)
987 { /* Normal case! */
988 pFnDesc->apszParamName[j] = findStartOfWord(apszArgs[j] + cch - 1,
989 apszArgs[j]);
990 pFnDesc->apszParamName[j][-1] = '\0';
991 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
992 }
993 else
994 { /* eliptic */
995 pFnDesc->apszParamName[j] = "...";
996 pFnDesc->apszParamType[j] = "";
997 }
998 }
999 else
1000 { /* arg yet another special case! 'fn(int argc, char *argv[])' */
1001 char *pszP2;
1002 cch = strlen(apszArgs[j]);
1003 psz = &apszArgs[j][cch-2];
1004 while (psz > apszArgs[j] && ((*psz >= '0' && *psz <= '9') || *psz == ' '))
1005 psz--;
1006
1007 if (psz > apszArgs[j] && *psz == '[')
1008 {
1009 pszP2 = psz--;
1010 while (psz >= apszArgs[j] && *psz == ' ')
1011 psz--;
1012 }
1013
1014 if (psz <= apszArgs[j])
1015 { /* funny case - no name? */
1016 sprintf(pszEnd, "arg%i", j);
1017 pFnDesc->apszParamName[j] = pszEnd;
1018 pszEnd = strlen(pszEnd) + pszEnd + 1;
1019 *pszEnd = '\0';
1020 }
1021 else
1022 { /* *pszP2 = '[' and psz = end of name */
1023 psz = findStartOfWord(psz, apszArgs[j]);
1024 strncat(pszEnd, psz, pszP2 - psz);
1025 pFnDesc->apszParamName[j] = pszEnd;
1026 pszEnd = strlen(pszEnd) + pszEnd + 1;
1027 *pszEnd = '\0';
1028 memset(psz, ' ', pszP2 - psz);
1029 }
1030 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1031 }
1032 }
1033 else
1034 { /* No argument name only type - make a dummy one: 'arg[1..n]' */
1035 sprintf(pszEnd, "arg%i", j);
1036 pFnDesc->apszParamName[j] = pszEnd;
1037 pszEnd = strlen(pszEnd) + pszEnd + 1;
1038 *pszEnd = '\0';
1039 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1040 }
1041 }
1042 else
1043 { /* Function pointer argument... */
1044 char *pszP2 = psz;
1045 psz = findStartOfWord(psz-1, apszArgs[j]);
1046 strncat(pszEnd, psz, pszP2 - psz);
1047
1048 pFnDesc->apszParamName[j] = pszEnd;
1049 memset(psz, ' ', pszP2 - psz);
1050 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1051
1052 pszEnd = strlen(pszEnd) + pszEnd + 1;
1053 *pszEnd = '\0';
1054 }
1055 }
1056 }
1057 pOptions = pOptions;
1058 return 0x00000001;
1059}
1060
1061
1062/**
1063 * Analyses the function header.
1064 * @returns high word = number of signals
1065 * low word = number of APIs processed. (1 or 0).
1066 * @param papszLines Array of lines in the file.
1067 * @param i Index into papszLines.
1068 * @param pszFilename Filename used in the signal log.
1069 * @param pOptions Pointer to options.
1070 * @sketch 1. Search backwards (start at i-1) for a comment or code.
1071 * 2. If comment: (else fail)
1072 * 2a. find start and end of comment.
1073 * 2b. check for function header characteristics
1074 * - lots of '*'s.
1075 * - fields 'Status', 'Author' and 'Name'
1076 * or if SDS, check for:
1077 * - at least two '*'s at the begining.
1078 * - fields '@status' and/or '@author'
1079 * 2c. check that content of the 'Name' field matches (not SDS)
1080 * 2d. read the status and author fields.
1081 * @remark Supports both types of function headers, Odin32-common and SDS.
1082 */
1083static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions)
1084{
1085 int iStatus, iAuthor, iName, iStart, iEnd;
1086 int j, jStart;
1087 int fFound;
1088 int fSDS = 0;
1089 char *psz, *pszB;
1090 char *pszAuthor = NULL;
1091 unsigned long ulRc = 0x00000001;
1092
1093 pOptions = pOptions;
1094
1095 if (i < 0) /* parameter validation */
1096 return 0;
1097
1098 /* 1. + 2a.*/
1099 iEnd = i-1; /* find end */
1100 j = strlen(papszLines[iEnd]);
1101 j -= j > 0 ? 1 : 0;
1102 fFound = 0;
1103 while (iEnd >= 0 && i - iEnd < 7 && papszLines[iEnd][j] != '}' &&
1104 !(fFound = (papszLines[iEnd][j] == '*' && papszLines[iEnd][j+1] == '/')))
1105 if (j-- == 0)
1106 if (iEnd-- > 0)
1107 {
1108 j = strlen(papszLines[iEnd]);
1109 j -= j > 0 ? 1 : 0;
1110 }
1111 if (!fFound) /* fail if not found */
1112 return 0;
1113
1114 iStart = iEnd; /* find start */
1115 if (j < 2)
1116 j -= (j = strlen(papszLines[--iStart])) > 1 ? 2 : j;
1117 else
1118 j -= 2;
1119 fFound = 0;
1120 while (iStart >= 0
1121 && (j < 0
1122 || !(fFound = (papszLines[iStart][j] == '/' && papszLines[iStart][j+1] == '*'))
1123 )
1124 )
1125 if (j-- <= 0)
1126 if (iStart-- > 0)
1127 {
1128 j = strlen(papszLines[iStart]);
1129 j -= j > 1 ? 2 : j;
1130 }
1131
1132 if (!fFound) /* fail if not found */
1133 return 0;
1134 jStart = j;
1135
1136 /* 2b.*/
1137 if (strncmp(&papszLines[iStart][jStart], "/**", 3) != 0) /* checks that there are more than one star at start of comment */
1138 return 0;
1139
1140 iName = iStart; /* Odin32 common */
1141 while (iName <= iEnd &&
1142 stristr(papszLines[iName], "* Name") == NULL)
1143 iName++;
1144 iStatus = iStart;
1145 while (iStatus <= iEnd &&
1146 stristr(papszLines[iStatus], "* Status") == NULL)
1147 iStatus++;
1148 iAuthor = iStart;
1149 while (iAuthor <= iEnd &&
1150 stristr(papszLines[iAuthor], "* Author") == NULL)
1151 iAuthor++;
1152
1153 if (!(iName <= iEnd || iStatus <= iEnd || iAuthor <= iEnd)) /* if not found try SDS */
1154 {
1155 iStatus = iStart;
1156 while (iStatus <= iEnd &&
1157 stristr(papszLines[iStatus], "@status") == NULL)
1158 iStatus++;
1159 iAuthor = iStart;
1160 while (iAuthor <= iEnd &&
1161 stristr(papszLines[iAuthor], "@author") == NULL)
1162 iAuthor++;
1163 if (!(iStatus <= iEnd || iAuthor <= iEnd))
1164 return 0;
1165 fSDS = 1;
1166 }
1167
1168 /* 2c.*/
1169 if (iName <= iEnd && strstr(papszLines[iName], pFnDesc->pszName) == NULL)
1170 fprintf(phLog, "Warning: a matching function name is not found in the name Field\n");
1171
1172 /* 2d.*/
1173 pszB = &pFnDesc->szFnHdrBuffer[0];
1174 if (!fSDS)
1175 { /* Odin32 common */
1176 if (iStatus <= iEnd) /* Status */
1177 {
1178 psz = stristr(papszLines[iStatus], "* Status") + sizeof("* Status") - 1;
1179 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
1180 psz++;
1181 strcpy(pszB, psz);
1182 trim(pszB);
1183 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
1184 {
1185 pszB[strlen(pszB)-1] = '\0';
1186 trim(pszB);
1187 }
1188 pFnDesc->pszStatus = pszB;
1189 pszB += strlen(pszB) + 1;
1190 }
1191
1192 if (iAuthor <= iEnd) /* Author(s) */
1193 {
1194 pszAuthor = pszB;
1195 psz = stristr(papszLines[iAuthor], "* Author") + sizeof("* Author") - 1;
1196 do
1197 {
1198 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
1199 psz++;
1200 strcpy(pszB, psz);
1201 trim(pszB);
1202 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
1203 {
1204 pszB[strlen(pszB)-1] = '\0';
1205 trim(pszB);
1206 }
1207 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
1208 strcat(pszB, ",");
1209 pszB += strlen(pszB);
1210
1211 /* next */
1212 psz = papszLines[++iAuthor] + 1;
1213 j = 0;
1214 while (*psz == ' ' && j++ < 5) psz++;
1215 if (*psz == '*')
1216 psz++;
1217 } while (iAuthor < iEnd && *psz == ' ');
1218 pszB++;
1219 }
1220 }
1221 else
1222 { /* SDS */
1223 if (iStatus <= iEnd)
1224 {
1225 psz = stristr(papszLines[iStatus], "@status") + sizeof("@status");
1226 while (*psz == ' ')
1227 psz++;
1228 strcpy(pszB, psz);
1229 trim(pszB);
1230 pszB += strlen(pszB) + 1;
1231 }
1232
1233 if (iAuthor <= iEnd)
1234 {
1235 pszAuthor = pszB;
1236 psz = stristr(papszLines[iAuthor], "@author") + sizeof("@author");
1237 do
1238 {
1239 while (*psz == ' ')
1240 psz++;
1241 strcpy(pszB, psz);
1242 trim(pszB);
1243 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
1244 strcat(pszB, ",");
1245 pszB += strlen(pszB) + 1;
1246
1247 /* next */
1248 psz = papszLines[++iAuthor] + 1;
1249 j = 0;
1250 while (*psz == ' ' && j++ < 5) psz++;
1251 if (*psz == '*')
1252 psz++;
1253 } while (iAuthor <= iEnd && (*psz == ' ' || *psz == '@'));
1254 pszB++;
1255 }
1256 }
1257
1258 /* Status - refcodes are hardcoded here! */
1259 if (pFnDesc->pszStatus != NULL && *pFnDesc->pszStatus != '\0')
1260 {
1261 if (strstr(pFnDesc->pszStatus, "STUB") != NULL || *pFnDesc->pszStatus == '1')
1262 pFnDesc->lStatus = 1; /* STUB */
1263 else if (stristr(pFnDesc->pszStatus, "Partially") != NULL || *pFnDesc->pszStatus == '2' || *pFnDesc->pszStatus == '3')
1264 {
1265 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '2')
1266 pFnDesc->lStatus = 2; /* STUB */
1267 else
1268 pFnDesc->lStatus = 3; /* STUB */
1269 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1270 || *pFnDesc->pszStatus == '5' || *pFnDesc->pszStatus == '7')
1271 pFnDesc->lStatus += 4;
1272 }
1273 else if (stristr(pFnDesc->pszStatus, "Completely") != NULL || *pFnDesc->pszStatus == '4' || *pFnDesc->pszStatus == '5')
1274 {
1275 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '4')
1276 pFnDesc->lStatus = 4; /* STUB */
1277 else
1278 pFnDesc->lStatus = 5; /* STUB */
1279 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1280 || *pFnDesc->pszStatus == '8' || *pFnDesc->pszStatus == '9')
1281 pFnDesc->lStatus += 4;
1282 }
1283 else
1284 {
1285 fprintf(phSignal, "%s, %s: '%s' is not a valid status code.\n",
1286 pszFilename, pFnDesc->pszName, pFnDesc->pszStatus);
1287 ulRc += 0x00010000;
1288 }
1289 }
1290
1291 /* Author */
1292 if (pszAuthor)
1293 { /* author1, author2, author3 */
1294 j = 0;
1295 psz = trim(pszAuthor);
1296 pFnDesc->cAuthors = 0;
1297 while (*psz != '\0' && pFnDesc->cAuthors < (int)(sizeof(pFnDesc->apszAuthor) / sizeof(pFnDesc->apszAuthor[0])))
1298 {
1299 char *pszBr1 = strchr(psz, '[');
1300 char *pszBr2 = strchr(psz, ']');
1301 char *pszComma;
1302
1303 /* remove '[text]' text - this is usualy used for time/date */
1304 if (pszBr1 != NULL && pszBr2 != NULL && pszBr1 < pszBr2)
1305 while (pszBr1 <= pszBr2)
1306 *pszBr1++ = ' ';
1307
1308 /* terminate string */
1309 pszComma = strchr(psz, ',');
1310 if (pszComma != NULL)
1311 {
1312 pszAuthor = pszComma + 1;
1313 *pszComma = '\0';
1314 }
1315
1316 pFnDesc->apszAuthor[pFnDesc->cAuthors] = trim(psz);
1317 pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] =
1318 dbFindAuthor(pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1319
1320 if (pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] == -1)
1321 {
1322 fprintf(phSignal, "%s, %s: author '%s' is not recognized.\n", pszFilename, pFnDesc->pszName,
1323 pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1324 ulRc += 0x00010000;
1325 }
1326
1327 /* next */
1328 pFnDesc->cAuthors++;
1329 psz = trim(pszAuthor);
1330 }
1331 }
1332
1333 return ulRc;
1334}
1335
1336
1337/**
1338 * Checks if there may be an function starting at the current line.
1339 * @returns TRUE if API found, else FALSE.
1340 * @param papszLines Array of lines in the file.
1341 * @param i Index into papszLines.
1342 * @param pOptions Pointer to options.
1343 */
1344static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions)
1345{
1346 if (!pOptions->fOld)
1347 { /* new API naming style */
1348 /* brief algorithm:
1349 * check that line don't start with '\\', '{' or '}'
1350 * search for '('.
1351 * if found then do
1352 * find c-word previous to '('
1353 * if found then do
1354 * check that the following condition are true:
1355 * 1. word is not 'for', 'while', 'do', 'if', 'else' or 'switch'
1356 * 2. first significant char after '(' should not be a '*'. (Fix for functionnames in function header, "BackupRead(".)
1357 * 3. find the matching ')' and check that the first significant char after it is '{'.
1358 * if 1, 2 and 3 are all true return true
1359 * return false.
1360 *
1361 * Note, for 2: spaces, newlines and comments are ignored, all other chars are significant.
1362 */
1363 char *pszP1 = papszLines[i];
1364
1365 while (*pszP1 != '\0' && *pszP1 == ' ')
1366 pszP1++;
1367 if (*pszP1 != '\0' && *pszP1 != '/' && pszP1[1] != '/'
1368 && *pszP1 != '{' && *pszP1 != '}')
1369 {
1370 pszP1 = strchr(papszLines[i], '(');
1371 if (pszP1 != NULL && pszP1 >= papszLines[i])
1372 {
1373 int cchFnName = 0;
1374 char *pszFnName = pszP1 - 1;
1375
1376 while (pszFnName - cchFnName > papszLines[0] && pszFnName[cchFnName] == ' ')
1377 cchFnName--, pszFnName--;
1378
1379 pszFnName = findStartOfWord(pszFnName, papszLines[0]);
1380 cchFnName += pszP1 - pszFnName;
1381 if (cchFnName >= 0)
1382 {
1383 /* 1. */
1384 if (
1385 strncmp(pszFnName, "for", cchFnName) != 0 &&
1386 strncmp(pszFnName, "while", cchFnName) != 0 &&
1387 strncmp(pszFnName, "do", cchFnName) != 0 &&
1388 strncmp(pszFnName, "if", cchFnName) != 0 &&
1389 strncmp(pszFnName, "else", cchFnName) != 0 &&
1390 strncmp(pszFnName, "switch", cchFnName) != 0
1391 )
1392 {
1393 /* 2. */
1394 int j = i;
1395 char *psz = skipInsignificantChars(papszLines, j, pszP1+1);
1396 if (psz != NULL && *psz != '*')
1397 {
1398 char *pszB = pszP1 + 1; /*'{'*/
1399 int c = 1;
1400
1401 /* 3. */
1402 while (c > 0)
1403 {
1404 if (*pszB == '(')
1405 c++;
1406 else if (*pszB == ')')
1407 if (--c == 0)
1408 break;
1409 if (*pszB++ == '\0')
1410 if ((pszB = papszLines[++i]) == NULL)
1411 break;
1412 }
1413 if (pszB != NULL)
1414 {
1415 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1416 if (pszB != NULL && *pszB == '{')
1417 {
1418 fprintf(phLog, "Function found: %.*s\n", cchFnName, pszFnName);
1419 return TRUE;
1420 }
1421 /* FIXME: constructors with ':' afterwards are not supported !TODO! */
1422 }
1423 }
1424 }
1425 }
1426 }
1427 }
1428 }
1429 else
1430 { /* old API naming style */
1431 char *pszOS2;
1432
1433 /* brief algorithm:
1434 * search for function prefix, 'OS2'.
1435 * if found then do
1436 * check that the following condition are true:
1437 * 1. char before 'OS2' is either start-of-line (no char), space or '*'.
1438 * 2. first significant char after the 'OS2' prefixed word is a '('.
1439 * 3. find the matching ')' and check that the first significant char after it is '{'.
1440 * if 1,2 and 3 all are true return true
1441 * return false.
1442 *
1443 * Note, for 2 and 3 spaces, newlines and comments are ignored, all other chars are significant.
1444 */
1445 pszOS2 = strstr(papszLines[i], "OS2");
1446 if (pszOS2 != NULL)
1447 {
1448 /* 1.*/
1449 if (pszOS2 == papszLines[i] || pszOS2[-1] == ' ' || pszOS2[-1] == '*')
1450 {
1451 char *pszP1; /*'('*/
1452 int cchFnName;
1453
1454 /* 2. */
1455 pszP1 = findEndOfWord(pszOS2);
1456 cchFnName = pszP1 - pszOS2;
1457 pszP1 = skipInsignificantChars(papszLines, i, pszP1);
1458
1459 if (pszP1 != NULL && *pszP1 == '(')
1460 {
1461 char *pszB = pszP1 + 1; /*'{'*/
1462 int c = 1;
1463
1464 /* 3. */
1465 while (c > 0)
1466 {
1467 if (*pszB == '(')
1468 c++;
1469 else if (*pszB == ')')
1470 if (--c == 0)
1471 break;
1472 if (*pszB++ == '\0')
1473 if ((pszB = papszLines[++i]) == NULL)
1474 break;
1475 }
1476 if (pszB != NULL)
1477 {
1478 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1479 if (pszB != NULL && *pszB == '{')
1480 {
1481 fprintf(phLog, "Possible API: %.*s\n", cchFnName, pszOS2);
1482 return TRUE;
1483 }
1484 }
1485 }
1486 }
1487 }
1488 }
1489
1490 return FALSE;
1491}
1492
1493
1494
1495/**
1496 * Callback function for the dbGetNotUpdatedFunction routine.
1497 *
1498 */
1499static long _System dbNotUpdatedCallBack(const char *pszValue, const char *pszFieldName, void *pvUser)
1500{
1501 static i = 0;
1502 switch (i++)
1503 {
1504 case 0:
1505 fprintf(phLog, "%s", pszValue);
1506 break;
1507 case 1:
1508 fprintf(phLog, "(%s)", pszValue);
1509 break;
1510 case 2: /* updated */
1511 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
1512 break;
1513 case 3: /* aliasfn */
1514 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
1515 break;
1516 case 4:
1517 if (pszValue != NULL)
1518 fprintf(phLog, " --> %s.", pszValue);
1519 break;
1520 case 5:
1521 if (pszValue != NULL)
1522 fprintf(phLog, "%s", pszValue);
1523 break;
1524 case 6:
1525 if (pszValue != NULL)
1526 fprintf(phLog, "(%s)", pszValue);
1527 break;
1528
1529 default:
1530 i = 0;
1531 fprintf(phLog, "\n");
1532 }
1533
1534 if (stricmp(pszFieldName, "last") == 0)
1535 {
1536 i = 0;
1537 fprintf(phLog, "\n");
1538 }
1539
1540 pvUser = pvUser;
1541 return 0;
1542}
1543
1544
1545/**
1546 * Skips insignificant chars (spaces, new-lines and comments)
1547 * @returns pointer to new string posision. NULL if end of file.
1548 * @param papszLines Pointer to line table.
1549 * @param i Index into line table. (current line)
1550 * @param psz Pointer where to start (within the current line).
1551 */
1552static char *skipInsignificantChars(char **papszLines, int &i, char *psz)
1553{
1554 BOOL fComment = *psz == '/' && psz[1] == '*';
1555
1556 while (fComment || *psz == ' ' || *psz == '\0' || (*psz == '/' && psz[1] == '/'))
1557 {
1558 if (*psz == '\0' || (*psz == '/' && psz[1] == '/' && !fComment))
1559 {
1560 if ((psz = papszLines[++i]) == NULL)
1561 break;
1562 }
1563 else
1564 psz++;
1565
1566 if (fComment)
1567 {
1568 if (!(fComment = *psz != '*' || psz[1] != '/'))
1569 psz += 2;
1570 else
1571 continue;
1572 }
1573
1574 if ((fComment = *psz == '/' && psz[1] == '*') == TRUE)
1575 psz += 1 + (psz[2] != '*'); // don't add two when there is a possible end comment following.
1576 }
1577
1578 return psz;
1579}
1580
1581
1582/**
1583 * Reads a file into memory.
1584 * @returns Pointer to file. This should be freed using 'free' when processing
1585 * the file is not needed.
1586 * @param pszFilename Name of file to read.
1587 * @remark Current implementation reads the file as a binary file.
1588 */
1589static char *readFileIntoMemory(const char *pszFilename)
1590{
1591 char *pszFile = NULL;
1592 int cbFile = 0; /* don't have to initialized, but it removes gcc warning (release) */
1593 FILE *phFile;
1594
1595 phFile = fopen(pszFilename, "rb");
1596 if (phFile != NULL)
1597 {
1598 if (!fseek(phFile, 0, SEEK_END)
1599 && (cbFile = (int)ftell(phFile)) > 0
1600 && !fseek(phFile, 0, SEEK_SET)
1601 )
1602 {
1603 pszFile = (char*)malloc(cbFile + 1);
1604 if (pszFile != NULL)
1605 {
1606 #if 1
1607 memset((void*)pszFile, 0, cbFile + 1);
1608 if (fread((void*)pszFile, 1, cbFile, phFile) == 1)
1609 {
1610 free(pszFile);
1611 pszFile = NULL;
1612 }
1613 #else
1614 int cLines = 0;
1615 int cch = 0;
1616 char *psz = pszFile;
1617
1618 while (!feof(phFile) && cch < cbFile &&
1619 fgets(psz, cbFile - cch, phFile) != NULL)
1620 {
1621 int cchLine;
1622 cch += cchLine = strlen(psz);
1623 psz += cchLine;
1624 cLines++;
1625 }
1626
1627 /* error check */
1628 if (cch > cbFile || !feof(phFile))
1629 {
1630 free(pszFile);
1631 pszFile = NULL;
1632 }
1633 else
1634 *psz = '\0';
1635 #endif
1636 }
1637 }
1638 fclose(phFile);
1639 }
1640
1641 return pszFile;
1642}
1643
1644
1645/**
1646 * Creates an array of lines from a "memory" file. The last entry in the array contains NULL.
1647 * It also replaces '\t' with ' '.
1648 * @returns Pointer to the array of lines.
1649 * @param pszFile Pointer to "memory" file.
1650 */
1651static char **createLineArray(char *pszFile)
1652{
1653 char *psz = pszFile;
1654 char **papszLines = NULL;
1655 int cLines = 1;
1656
1657 while (*psz != '\0')
1658 {
1659 if (*psz == '\r')
1660 {
1661 if (psz[1] == '\n')
1662 psz++;
1663 cLines++;
1664 } else if (*psz == '\n')
1665 cLines++;
1666 psz++;
1667 }
1668 fprintf(phLog, "%d lines\n", cLines);
1669
1670 papszLines = (char**)calloc(cLines + 1, sizeof(char *));
1671 if (papszLines != NULL)
1672 {
1673 cLines = 1;
1674 papszLines[0] = psz = pszFile;
1675 while (*psz != '\0')
1676 {
1677 if (*psz == '\t')
1678 *psz = ' ';
1679 if (*psz == '\r')
1680 {
1681 if (psz[1] == '\n')
1682 *psz++ = '\0';
1683 papszLines[cLines++] = psz + 1;
1684 *psz = '\0';
1685 } else if (*psz == '\n')
1686 {
1687 *psz = '\0';
1688 papszLines[cLines++] = psz + 1;
1689 }
1690 psz++;
1691 }
1692 papszLines[cLines] = NULL; /* Strictly, this is not needed as we use calloc. */
1693 }
1694
1695
1696 return papszLines;
1697}
1698
1699
1700/** first char after word */
1701static char *findEndOfWord(char *psz)
1702{
1703
1704 while (*psz != '\0' &&
1705 (
1706 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
1707 ||
1708 (*psz >= '0' && *psz <= '9')
1709 ||
1710 *psz == '_'
1711 )
1712 )
1713 ++psz;
1714 return psz;
1715}
1716
1717
1718/** starting char of word */
1719static char *findStartOfWord(char *psz, const char *pszStart)
1720{
1721 char *pszR = psz;
1722 while (psz >= pszStart &&
1723 (
1724 (*psz >= 'A' && *psz <= 'Z')
1725 || (*psz >= 'a' && *psz <= 'z')
1726 || (*psz >= '0' && *psz <= '9')
1727 || *psz == '_'
1728 )
1729 )
1730 pszR = psz--;
1731 return pszR;
1732}
1733
1734
1735/**
1736 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
1737 * @returns Pointer to first not space or tab char in the string.
1738 * @param psz Pointer to the string which is to be trimmed.
1739 * @status completely implmented.
1740 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1741 */
1742static char *trim(char *psz)
1743{
1744 int i;
1745 if (psz == NULL)
1746 return NULL;
1747 while (*psz == ' ' || *psz == '\t')
1748 psz++;
1749 i = strlen(psz) - 1;
1750 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
1751 i--;
1752 psz[i+1] = '\0';
1753 return psz;
1754}
1755
1756
1757/* copy: remove remarks, and unneeded spaces, ensuring no space after '(',
1758 * ensuring space after '*', ensuring no space before ',' and ')'.
1759 */
1760static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines)
1761{
1762 copy(psz, pszFrom - papszLines[iFrom], iFrom, pszTo - papszLines[iTo], iTo, papszLines);
1763}
1764
1765#if 0
1766static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
1767{
1768 char chPrev = '\n';
1769 int i, j;
1770 int fComment = 0;
1771
1772 i = iFrom;
1773 j = jFrom;
1774 while (i < iTo || (i == iTo && j <= jTo))
1775 {
1776 if (papszLines[i][j] != '\0'
1777 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
1778 )
1779 {
1780 /* copy char ? */
1781 if (!fComment)
1782 {
1783 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
1784 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
1785 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
1786 )
1787 {
1788 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
1789 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
1790 else
1791 {
1792 chPrev = *psz++ = papszLines[i][j];
1793 if (chPrev == '*') /* ensure ' ' after '*' */
1794 chPrev = *psz++ = ' ';
1795 }
1796 }
1797 }
1798 else
1799 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == 0)
1800 j++;
1801 j++;
1802 }
1803 else
1804 {
1805 /* next */
1806 j = 0;
1807 i++;
1808 if (chPrev != ' ' && chPrev != '(')
1809 chPrev = *psz++ = ' ';
1810 }
1811 }
1812 *psz = '\0';
1813}
1814
1815#else
1816/**
1817 * Copies a set of lines to a buffer (psz) removing precompiler lines and all comments.
1818 * @param psz
1819 * @param jFrom Starting position, index into line iFrom.
1820 * @param iFrom Starting position, index into papszLines.
1821 * @param jTo Ending position, index into line iFrom.
1822 * @param iTo Ending position, index into papszLines.
1823 * @param papszLines Array of lines.
1824 * @status completely implemented.
1825 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1826 */
1827static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
1828{
1829 char chPrev = '\n';
1830 int i, j;
1831 int fComment = FALSE;
1832 int fFirst = TRUE;
1833
1834 i = iFrom;
1835 j = jFrom;
1836 while (i < iTo || (i == iTo && j <= jTo))
1837 {
1838 if (papszLines[i][j] != '\0'
1839 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
1840 && !(!fComment && fFirst && papszLines[i][j] == '#')
1841 )
1842 {
1843 fFirst = papszLines[i][j] == ' ';
1844
1845 /* copy char ? */
1846 if (!fComment)
1847 {
1848 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
1849
1850 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
1851 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
1852 )
1853 {
1854 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
1855 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
1856 else
1857 {
1858 chPrev = *psz++ = papszLines[i][j];
1859 if (chPrev == '*') /* ensure ' ' after '*' */
1860 chPrev = *psz++ = ' ';
1861 }
1862 }
1863
1864 }
1865 else
1866 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == FALSE)
1867 j++;
1868 j++;
1869 }
1870 else
1871 {
1872 /* next */
1873 j = 0;
1874 fFirst = TRUE;
1875 i++;
1876 if (chPrev != ' ' && chPrev != '(')
1877 chPrev = *psz++ = ' ';
1878 }
1879 }
1880 *psz = '\0';
1881}
1882#endif
1883
1884
1885/**
1886 * Upcases a char.
1887 * @returns Upper case of the char given in ch.
1888 * @param ch Char to capitalize.
1889 */
1890#if 0
1891inline char upcase(char ch)
1892{
1893 return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch;
1894}
1895#else
1896#define upcase(ch) ((ch) >= 'a' && (ch) <= 'z' ? (char)((ch) - ('a' - 'A')) : (ch))
1897#endif
1898
1899
1900/**
1901 * Searches for a substring in a string.
1902 * @returns Pointer to start of substring when found, NULL when not found.
1903 * @param pszStr String to be searched.
1904 * @param pszSubStr String to be searched.
1905 * @remark Depends on the upcase function.
1906 */
1907static char *stristr(const char *pszStr, const char *pszSubStr)
1908{
1909 int cchSubStr = strlen(pszSubStr);
1910 int i = 0;
1911
1912 while (*pszStr != '\0' && i < cchSubStr)
1913 {
1914 i = 0;
1915 while (i < cchSubStr && pszStr[i] != '\0' &&
1916 (upcase(pszStr[i]) == upcase(pszSubStr[i])))
1917 i++;
1918 pszStr++;
1919 }
1920
1921 return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL);
1922}
1923
1924
1925
1926/**
1927 * Skips backwards until one of the chars in pszStopAt or star of file is reached.
1928 * @returns Pointer to end.
1929 * @param pszStopAt Array of chars which we should stop at.
1930 * @param pszFrom Start pointer.
1931 * @param iLine Reference to index to array of lines. input: start line, output: result line.
1932 * @param papszLines Array lines.
1933 * @sketch
1934 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1935 * @remark Comments are skipped.
1936 * No tests for strings ("...asdf").
1937 */
1938static char *skipBackwards(const char *pszStopAt, const char *pszFrom, int &iLine, char **papszLines)
1939{
1940 BOOL fComment = FALSE;
1941 int i = iLine;
1942 const char *psz = pszFrom;
1943
1944 while (i >= 0)
1945 {
1946 /* check for stop char */
1947 const char *psz1 = pszStopAt;
1948 while (*psz1 != '\0')
1949 if (*psz1++ == *psz)
1950 break;
1951 if (*psz1 != '\0')
1952 break;
1953
1954 /* comment check */
1955 if (!fComment && psz > papszLines[i] && *psz == '/' && psz[-1] == '*')
1956 fComment = TRUE;
1957 else if (fComment && *psz == '/' && psz[1] == '*')
1958 fComment = FALSE;
1959
1960 /* ok position to return? */
1961 if (!fComment)
1962 {
1963 iLine = i;
1964 pszFrom = psz;
1965 }
1966
1967 /* prev */
1968 if (psz > papszLines[i])
1969 psz--;
1970 else
1971 { /* try find line comment */
1972 do
1973 {
1974 char *pszStart = papszLines[--i];
1975 while (*pszStart == ' ')
1976 pszStart++;
1977 if (*pszStart != '\0' && *pszStart != '#'
1978 && !(*pszStart == '/' && pszStart[1] == '/'))
1979 { /* find '//' */
1980 pszStart = strstr(pszStart, "//");
1981 if (pszStart != NULL)
1982 psz = pszStart-1;
1983 else
1984 psz = papszLines[i] + strlen(papszLines[i]) - 1;
1985 break;
1986 }
1987 } while (i > 0);
1988 }
1989 }
1990
1991 return (char*)pszFrom;
1992}
1993
Note: See TracBrowser for help on using the repository browser.