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

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

A new signal. Signals when not all functions of a dll were found.

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