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

Last change on this file since 3899 was 3899, checked in by bird, 25 years ago

Updates the file column in the function table.

File size: 93.1 KB
Line 
1/* $Id: StateUpd.cpp,v 1.27 2000-07-29 14:12:46 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 <malloc.h>
18#include <stdio.h>
19#include <string.h>
20#include <stdlib.h>
21#include <assert.h>
22
23#include "StateUpd.h"
24#include "db.h"
25
26
27
28/*******************************************************************************
29* Global Variables *
30*******************************************************************************/
31static FILE *phLog = NULL;
32static FILE *phSignal = NULL;
33
34static const char *pszCommonKeywords[] =
35{
36 "* Author", "* Status", "* Remark", "* Result",
37 "* Variable", "* Parameters", "* Purpose", NULL
38};
39
40
41/*******************************************************************************
42* Internal Functions *
43*******************************************************************************/
44static void syntax(void);
45static void openLogs(void);
46static void closeLogs(void);
47static unsigned long processDir(const char *pszDirOrFile, POPTIONS pOptions);
48static unsigned long processFile(const char *pszFilename, POPTIONS pOptions);
49static unsigned long processModuleHeader(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
50static unsigned long processDesignNote(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
51static unsigned long processAPI(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
52static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions);
53static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
54static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
55static char *SDSCopyTextUntilNextTag(char *pszTarget, BOOL fHTML, int iStart, int iEnd, char **papszLines, const char *pszStart = NULL);
56static char *CommonCopyTextUntilNextTag(char *pszTarget, BOOL fHTML, int iStart, int iEnd, char **papszLines, const char *pszStart = NULL);
57static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions);
58static BOOL isDesignNote(char **papszLines, int i, POPTIONS pOptions);
59static long _System dbNotUpdatedCallBack(const char *pszValue, const char *pszFieldName, void *pvUser);
60static char *skipInsignificantChars(char **papszLines, int &i, char *psz);
61static char *readFileIntoMemory(const char *pszFilename);
62static char **createLineArray(char *pszFile);
63static char *findEndOfWord(const char *psz);
64static char *findStartOfWord(const char *psz, const char *pszStart);
65inline char *trim(char *psz);
66inline char *trimR(char *psz);
67inline char *skip(const char *psz);
68static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines);
69static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines);
70static void copyComment(char *psz, char *pszFrom, int iFrom, char **papszLines, BOOL fStrip, BOOL fHTML);
71static void copyComment(char *psz, int jFrom, int iFrom, char **papszLines, BOOL fStrip, BOOL fHTML);
72static char *stristr(const char *pszStr, const char *pszSubStr);
73static char *skipBackwards(const char *pszStopAt, const char *pszFrom, int &iLine, char **papszLines);
74static int findStrLine(const char *psz, int iStart, int iEnd, char **papszLines);
75
76
77/**
78 * Main function.
79 * @returns Number of signals.
80 * @param argc Argument count.
81 * @param argv Argument array.
82 */
83int main(int argc, char **argv)
84{
85 int argi;
86 BOOL fFatal = FALSE;
87 unsigned long ulRc = 0;
88 char szDLLName[64];
89 OPTIONS options = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, &szDLLName[0], -1};
90 unsigned long ulRc2;
91 char *pszErrorDesc = NULL;
92 char *pszHost = "localhost";
93 char *pszDatabase = "Odin32";
94 char *pszUser = "root";
95 char *pszPasswd = "";
96 ULONG ul1, ul2;
97 ULONG cUpdated, cAll, cNotAliased;
98
99 DosError(0x3);
100 /*DosSetPriority(PRTYS_PROCESSTREE, PRTYC_REGULAR, 1, 0);*/
101
102 /* get dll name from directory */
103 ul1 = ul2 = 0;
104 DosQueryCurrentDisk(&ul1, &ul2);
105 ul2 = sizeof(szDLLName);
106 DosQueryCurrentDir(ul1, &szDLLName[0], &ul2);
107 if (ul2 != 0)
108 {
109 if (szDLLName[ul2-1] == '\\' || szDLLName[ul2-1] == '/')
110 szDLLName[--ul2] = '\0';
111 ul1 = ul2;
112 while (ul1 != 0 && szDLLName[ul1-1] != '\\' && szDLLName[ul1-1] != '/')
113 ul1--;
114 if (ul1 != 0)
115 options.pszDLLName = &szDLLName[ul1];
116 }
117 else
118 szDLLName[0] = '\0';
119
120
121 /**************************************************************************
122 * parse arguments.
123 * options: -h or -? Syntax help.
124 * -ib<[+]|-> Integrity check at start.
125 * -ie<[+]|-> Integrity check at end.
126 * -io Integrity check only.
127 * -s Scan subdirectories.
128 * -Old <[+]|-> Old API Style.
129 * -OS2<[+]|-> Removes 'OS2'-prefix from function name.
130 * -COMCTL32<[+]|-> Removes 'COMCTL32'-prefix from function name.
131 * -VERSION<[+]|-> Removes 'VERSION'-prefix from function name.
132 * -Dll:<dllname> Name of the dll being processed.
133 * -d:<dbname> Database name
134 * -p:<passwd> Password
135 * -u:<user> Userid
136 * -h:<host> Hostname/IP-address
137 **************************************************************************/
138 argi = 1;
139 while (argi < argc && !fFatal)
140 {
141 if(argv[argi][0] == '-' || argv[argi][0] == '/')
142 {
143 switch (argv[argi][1])
144 {
145 case 'd':
146 case 'D':
147 if (strnicmp(&argv[argi][1], "dll:", 4) == 0 )
148 options.pszDLLName = &argv[argi][5];
149 else
150 {
151 if (argv[argi][2] == ':')
152 pszDatabase = &argv[argi][3];
153 else
154 fprintf(stderr, "warning: option '-d:' requires database name.\n");
155 }
156 break;
157
158 case 'h':
159 case 'H':
160 if (argv[argi][2] == ':')
161 {
162 pszHost = &argv[argi][3];
163 break;
164 }
165 case '?':
166 syntax();
167 return 0;
168
169 case 'i': /* Integrity */
170 case 'I':
171 switch (argv[argi][2])
172 {
173 case 'b':
174 case 'B':
175 options.fIntegrityBefore = argv[argi][3] != '-';
176 break;
177
178 case 'e':
179 case 'E':
180 options.fIntegrityAfter = argv[argi][3] != '-';
181 break;
182
183 case 'o':
184 case 'O':
185 options.fIntegrityOnly = argv[argi][3] != '-';
186 break;
187
188 default:
189 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
190 fFatal = TRUE;
191 }
192 break;
193
194 case 'o':
195 case 'O':
196 if (stricmp(&argv[argi][1], "OS2") == 0)
197 options.fOS2 = argv[argi][4] != '-';
198 else if (stricmp(&argv[argi][1], "Old") == 0)
199 options.fOld = argv[argi][4] != '-';
200 else
201 {
202 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
203 fFatal = TRUE;
204 }
205 break;
206
207 case 'p':
208 case 'P':
209 if (argv[argi][2] == ':')
210 pszPasswd = &argv[argi][3];
211 else
212 fprintf(stderr, "warning: option '-p:' requires password.\n");
213 break;
214
215 case 's':
216 case 'S':
217 options.fRecursive = TRUE;
218 fprintf(stderr, "This option (-s) is currently broken\n");
219 return -1;
220
221 case 'u':
222 case 'U':
223 if (argv[argi][2] == ':')
224 pszUser = &argv[argi][3];
225 else
226 fprintf(stderr, "error: option '-u:' requires userid.\n");
227 break;
228
229 default:
230 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
231 fFatal = TRUE;
232 break;
233 }
234 }
235 else
236 break; /* files has started */
237 argi++;
238 }
239
240 if (!fFatal)
241 {
242 /* open database */
243 if (!dbConnect(pszHost, pszUser, pszPasswd, pszDatabase))
244 {
245 fprintf(stderr, "Could not connect to database (%s). \n\terror description: %s\n",
246 pszDatabase, dbGetLastErrorDesc());
247 return 0x00010001;
248 }
249
250 /* open the logs */
251 openLogs();
252
253 /* check db integrity */
254 if (options.fIntegrityBefore || options.fIntegrityOnly)
255 {
256 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
257 *pszErrorDesc = '\0';
258 ulRc2 = dbCheckIntegrity(pszErrorDesc);
259 if (ulRc2 != 0)
260 {
261 fprintf(phSignal, "-,-: integrity errors:\n\t%s\n", pszErrorDesc);
262 ulRc += ulRc2 << 16;
263 }
264 free(pszErrorDesc);
265 }
266
267 if (!options.fIntegrityOnly)
268 {
269 /* find dll */
270 options.lDllRefcode = dbGetDll(options.pszDLLName);
271 fprintf(phLog, "DLL: refcode=%d, name=%s\n", options.lDllRefcode, options.pszDLLName);
272 if (options.lDllRefcode >= 0)
273 {
274 /* processing */
275 if (argv[argi] == NULL || *argv[argi] == '\0')
276 ulRc = processDir(".", &options);
277 else
278 while (argv[argi] != NULL)
279 {
280 ulRc += processDir(argv[argi], &options);
281 argi++;
282 }
283
284 /* create new history rows */
285 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
286 *pszErrorDesc = '\0';
287 ulRc2 = dbCreateHistory(pszErrorDesc);
288 if (ulRc2 != 0)
289 {
290 fprintf(phSignal, "-,-: errors which occurred while creating history:\n\t%s\n", pszErrorDesc);
291 ulRc += ulRc2 << 16;
292 }
293 free(pszErrorDesc);
294
295 /* check db integrity */
296 if (options.fIntegrityAfter)
297 {
298 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
299 *pszErrorDesc = '\0';
300 ulRc2 = dbCheckIntegrity(pszErrorDesc);
301 if (ulRc2 != 0)
302 {
303 fprintf(phSignal, "-,-: integrity errors:\n\t%s\n", pszErrorDesc);
304 ulRc += ulRc2 << 16;
305 }
306 free(pszErrorDesc);
307 }
308 }
309 else
310 { /* failed to find dll - concidered nearly fatal. */
311 fprintf(phSignal, "-,-: failed to find dll (%s)!\n\t%s\n",
312 options.pszDLLName ? options.pszDLLName : "<NULL>",
313 dbGetLastErrorDesc());
314 ulRc++;
315 }
316 }
317
318 /* write status to log */
319 if (!options.fIntegrityOnly)
320 {
321 cUpdated = dbGetNumberOfUpdatedFunction(options.lDllRefcode);
322 cAll = dbCountFunctionInDll(options.lDllRefcode, FALSE);
323 cNotAliased = dbCountFunctionInDll(options.lDllRefcode, TRUE);
324 if (cNotAliased > cUpdated)
325 {
326 fprintf(phSignal, "%d functions where not found (found=%d, total=%d).\n",
327 cNotAliased- cUpdated, cUpdated, cNotAliased);
328 ulRc += 0x00010000;
329 }
330 fprintf(phLog, "-------------------------------------------------\n");
331 fprintf(phLog, "-------- Functions which was not updated --------\n");
332 dbGetNotUpdatedFunction(options.lDllRefcode, dbNotUpdatedCallBack);
333 fprintf(phLog, "-------------------------------------------------\n");
334 fprintf(phLog, "-------------------------------------------------\n\n");
335 fprintf(phLog,"Number of function in this DLL: %4ld (%ld)\n", cAll, cNotAliased);
336 fprintf(phLog,"Number of successfully processed APIs: %4ld (%ld)\n", (long)(0x0000FFFF & ulRc), cUpdated);
337 }
338 fprintf(phLog,"Number of signals: %4ld\n", (long)(ulRc >> 16));
339
340 /* close the logs */
341 closeLogs();
342
343 /* close database */
344 dbDisconnect();
345
346 /* warn if error during processing. */
347 if (!options.fIntegrityOnly)
348 {
349 fprintf(stdout,"Number of function in this DLL: %4ld (%ld)\n", cAll, cNotAliased);
350 fprintf(stdout,"Number of successfully processed APIs: %4ld (%ld)\n", (long)(0x0000FFFF & ulRc), cUpdated);
351 }
352 fprintf(stdout,"Number of signals: %4ld\n", (long)(ulRc >> 16));
353 if ((int)(ulRc >> 16) > 0)
354 fprintf(stderr, "Check signal file 'Signals.Log'.\n");
355 }
356
357 return (int)(ulRc >> 16);
358}
359
360
361
362/**
363 * Displays syntax.
364 */
365static void syntax()
366{
367 printf("\n"
368 "StateUpd v%01d.%02d - Odin32 API Database utility\n"
369 "----------------------------------------------\n"
370 "syntax: StateUpd.exe [-h|-?] [options] [FileOrDir1 [FileOrDir2 [...]]]\n"
371 "\n"
372 " -h or -? Syntax help. (this)\n"
373 " -ib<[+]|-> Integrity check at start. default: disabled\n"
374 " -ie<[+]|-> Integrity check at end. default: disabled\n"
375 " -io Integrity check only. default: disabled\n"
376 " -s Scan subdirectories. default: disabled\n"
377 " -Old Use old API style. default: disabled\n"
378 " -OS2 Ignore 'OS2'-prefix on APIs. default: disabled\n"
379 " -Dll:<dllname> Name of the dll. default: currentdirname\n"
380 " -h:<hostname> Database server hostname. default: localhost\n"
381 " -u:<username> Username on the server. default: root\n"
382 " -p:<password> Password. default: <empty>\n"
383 " -d:<database> Database to use. default: Odin32\n"
384 "\n"
385 "Scans files for API functions. This util will extract data about the API\n"
386 "and insert it into the database.\n"
387 "\n"
388 "If no files are given, then all *.c and *.cpp files will be scanned. (Not correct!)\n"
389 "NOTE: When files are given, only *.c and *.cpp files will be scanned.\n"
390 "Wildchars are allowed in the file spesifications.\n"
391 "\n"
392 "Copyright (c) 1999 knut st. osmundsen (knut.stange.osmundsen@pmsc.no)",
393 MAJOR_VER, MINOR_VER
394 );
395}
396
397
398/**
399 * Open logs, StateUpd.log and Signals.log (error log).
400 */
401static void openLogs(void)
402{
403 if (phLog == NULL)
404 {
405 phLog = fopen("StateUpd.Log", "w");
406 if (phLog == NULL)
407 {
408 fprintf(stderr,"error occured while opening log file - will use stderr instead.\n");
409 phLog = stderr;
410 }
411 }
412
413 if (phSignal == NULL)
414 {
415 phSignal = fopen("Signals.Log", "w");
416 if (phSignal == NULL)
417 {
418 fprintf(stderr,"error occured while opening signal file - will use stdout instead.\n");
419 phSignal = stdout;
420 }
421 }
422}
423
424
425/**
426 * Closes the logs.
427 */
428static void closeLogs(void)
429{
430 if (phLog != stderr && phLog != NULL)
431 fclose(phLog);
432 if (phSignal != stdout && phSignal != NULL)
433 {
434 if (ftell(phSignal) > 0)
435 fclose(phSignal);
436 else
437 {
438 fclose(phSignal);
439 unlink("Signals.log");
440 }
441 }
442}
443
444
445/**
446 * Processes a file or a subdirectory with files.
447 * @returns high word = number of signals
448 * low word = number of APIs processed. (1 or 0).
449 * @param pszDirOrFile Directory or file, see fFile.
450 * @param pOptions Pointer to options.
451 * @sketch -0. Determin dir or file.
452 * 0. Interpret parameters.
453 * 1. Scan current directory for *.cpp and *.c files and process them.
454 * 2. If recusion option is enabled:
455 * Scan current directory for sub-directories, scan them using recursion.
456 */
457static unsigned long processDir(const char *pszDirOrFile, POPTIONS pOptions)
458{
459 unsigned long ulRc = 0; /* high word = #signals, low word = #APIs successfully processed */
460 char szBuf[CCHMAXPATH];
461 char szFileSpec[CCHMAXPATH];
462 APIRET rc;
463 FILEFINDBUF3 ffb;
464 FILESTATUS3 fs;
465 ULONG ul = 1;
466 HDIR hDir = (HDIR)HDIR_CREATE;
467 PSZ pszDir;
468 PSZ pszFile;
469 BOOL fFile;
470
471 /* -0.*/
472 rc = DosQueryPathInfo(pszDirOrFile, FIL_STANDARD, &fs , sizeof(fs));
473 fFile = rc == NO_ERROR && (fs.attrFile & FILE_DIRECTORY) != FILE_DIRECTORY;
474
475 /* 0. */
476 strcpy(szBuf, pszDirOrFile);
477 pszDir = szBuf;
478 if (fFile)
479 {
480 if ((pszFile = strrchr(pszDir, '\\')) != NULL
481 || (pszFile = strrchr(pszDir, '/')) != NULL)
482 *pszFile++ = '\0';
483 else
484 {
485 pszFile = pszDir;
486 pszDir = ".";
487 }
488 }
489 else
490 {
491 pszFile = NULL;
492 ul = strlen(pszDir)-1;
493 if (pszDir[ul] == '\\' || pszDir[ul] == '/')
494 pszDir[ul] = '\0';
495 }
496
497
498 /* 1. */
499 if (fFile)
500 strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), pszFile);
501 else
502 strcat(strcpy(&szFileSpec[0], pszDir), "\\*.c*");
503 ul = 1;
504 rc = DosFindFirst((PCSZ)&szFileSpec[0], &hDir,
505 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
506 (PVOID)&ffb, sizeof(ffb), &ul, FIL_STANDARD);
507 while (rc == NO_ERROR && ul == 1)
508 {
509 char *psz = strrchr(&ffb.achName[0], '.');
510 if (psz != NULL && (!stricmp(psz, ".cpp") || !stricmp(psz, ".c")))
511 ulRc += processFile(strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), &ffb.achName[0]), pOptions);
512
513 /* next */
514 ul = 1;
515 rc = DosFindNext(hDir, &ffb, sizeof(ffb), &ul);
516 }
517 DosFindClose(hDir);
518
519 /* 2. */
520 if (pOptions->fRecursive)
521 {
522 strcat(strcpy(&szFileSpec[0], pszDir), "\\*");
523
524 hDir = (HDIR)HDIR_CREATE;
525 ul = 1; /* important on TVFS, not on HPFS... */
526 rc = DosFindFirst((PCSZ)&szFileSpec[0], &hDir,
527 MUST_HAVE_DIRECTORY,
528 (PVOID)&ffb, sizeof(ffb), &ul, FIL_STANDARD);
529 while (rc == NO_ERROR && ul == 1)
530 {
531 if (strcmp(&ffb.achName[0], ".") != 0 && strcmp(&ffb.achName[0], "..") != 0)
532 {
533 strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), &ffb.achName[0]);
534 if (fFile)
535 strcat(strcat(&szFileSpec[0], "\\"), pszFile);
536 ulRc += processDir(&szFileSpec[0], pOptions);
537 }
538
539 /* next */
540 ul = 1;
541 rc = DosFindNext(hDir, &ffb, sizeof(ffb), &ul);
542 }
543 DosFindClose(hDir);
544 }
545
546 return ulRc;
547}
548
549
550/**
551 * Processes a file.
552 * @returns high word = number of signals
553 * low word = number of APIs processed. (1 or 0).
554 * @param pszFilename Filename
555 * @param pOptions Pointer to options.
556 * @sketch 1. read file into memory.
557 * 2. create line array.
558 * (3. simple preprocessing - TODO)
559 * 4. process module header.
560 * 5. scan thru the line array, looking for APIs and designnotes.
561 * 5b. when API is found, process it.
562 * 5c. when designnote found, process it.
563 */
564static unsigned long processFile(const char *pszFilename, POPTIONS pOptions)
565{
566 unsigned long cSignals = 0;
567 unsigned long cAPIs = 0;
568 char *pszFile;
569
570 fprintf(phLog, "Processing '%s':\n", pszFilename);
571 /* 1.*/
572 pszFile = readFileIntoMemory(pszFilename);
573 if (pszFile != NULL)
574 {
575 char **papszLines;
576
577 /* 2.*/
578 papszLines = createLineArray(pszFile);
579 if (papszLines != NULL)
580 {
581 unsigned long ulRc;
582 int i = 0;
583
584 /* 3. - TODO */
585
586 /* 4. */
587 ulRc = processModuleHeader(papszLines, i, i, pszFilename, pOptions);
588 cSignals += ulRc >> 16;
589 if (ulRc & 0x0000ffff)
590 {
591 /* 4b.
592 * Remove Design notes.
593 */
594 pOptions->lSeqFile = 0;
595 if (!dbRemoveDesignNotes(pOptions->lFileRefcode))
596 {
597 fprintf(phSignal, "%s: failed to remove design notes. %s\n",
598 pszFilename, dbGetLastErrorDesc());
599 cSignals++;
600 }
601
602
603 /* 5.*/
604 while (papszLines[i] != NULL)
605 {
606 if (isFunction(papszLines, i, pOptions))
607 {
608 ulRc = processAPI(papszLines, i, i, pszFilename, pOptions);
609 cAPIs += 0x0000ffff & ulRc;
610 cSignals += ulRc >> 16;
611 }
612 else
613 {
614 if (isDesignNote(papszLines, i, pOptions))
615 {
616 ulRc = processDesignNote(papszLines, i, i, pszFilename, pOptions);
617 cSignals += ulRc >> 16;
618 }
619 i++;
620 }
621 }
622 }
623
624 free(papszLines);
625 }
626 else
627 {
628 fprintf(phSignal,"%s: error dividing file into lines.\n", pszFilename);
629 cSignals++;
630 }
631 free(pszFile);
632 }
633 else
634 {
635 fprintf(phSignal,"%s: error reading file.\n", pszFilename);
636 cSignals++;
637 }
638 fprintf(phLog, "Processing of '%s' is completed.\n\n", pszFilename);
639
640
641 return (unsigned long)((cSignals << 16) | cAPIs);
642}
643
644
645/**
646 * Processes an module header and other file information.
647 * @returns high word = number of signals.
648 * low word = Success indicator (TRUE / FALSE).
649 * @param papszLines Array of lines in the file.
650 * @param i Index into papszLines.
651 * @param iRet Index into papszLines. Where to resume search.
652 * @param pszFilename Filename.
653 * @param pOptions Pointer to options. lFileRefcode is set on successful return.
654 * @sketch Extract module information if any....
655 */
656static unsigned long processModuleHeader(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions)
657{
658 char szDescription[10240]; /* File description buffer. */
659 char szId[128]; /* CVS Id keyword buffer. */
660 char * psz, *psz2;
661 const char * pszDBFilename;
662 char * pszLastDateTime = NULL;
663 char * pszRevision = NULL;
664 char * pszAuthor = NULL;
665 signed long lLastAuthor = 0;
666 unsigned long ulRc = 0;
667
668 /*
669 * Find the DB filename (skip path!)
670 */
671 pszDBFilename = strrchr(pszFilename, '\\');
672 psz = strrchr(pszFilename, '/');
673 if (psz > pszDBFilename)
674 pszDBFilename = psz;
675 psz = strrchr(pszFilename, ':');
676 if (psz > pszDBFilename)
677 pszDBFilename = psz;
678 if (pszDBFilename == NULL)
679 pszDBFilename = pszFilename;
680 else
681 pszDBFilename++;
682
683 /*
684 * The module header is either on single comment or two comments.
685 * The first line should be a "$Id" CVS keyword. (by convention).
686 * Then the module description follows. So, we'll try find the $Id
687 * keyword first.
688 */
689 iRet = i;
690 while (i < iRet + 10 && (psz = strstr(papszLines[i], "$Id"": ")) == NULL)
691 i++;
692 if (psz != NULL)
693 { /* found $Id: */
694 psz2 = strchr(psz+3, '$');
695 if (psz2 != NULL && psz2 - psz > 39 && psz2 - psz < 256)
696 {
697 strncpy(&szId[0], psz, psz2 - psz);
698 szId[psz2 - psz] = '\0';
699 iRet = i;
700
701 /* parse it! */
702 psz = strstr(szId+4, ",v ");
703 if (psz != NULL)
704 {
705 pszRevision = trim(psz + 3);
706 psz = strchr(pszRevision, ' ');
707 *psz++ = '\0';
708 trimR(pszRevision);
709
710 pszLastDateTime = trim(psz);
711 psz = strchr(strchr(pszLastDateTime, ' ') + 1, ' ');
712 *psz++ = '\0';
713 pszLastDateTime[4] = pszLastDateTime[7] = '-';
714 trimR(pszLastDateTime);
715
716 pszAuthor = trim(psz);
717 psz = strchr(pszAuthor, ' ');
718 *psz = '\0';
719 lLastAuthor = dbFindAuthor(pszAuthor, NULL);
720 }
721 else
722 {
723 fprintf(phSignal, "%s, module header: $Id keyword is incorrect (2).\n", pszFilename);
724 ulRc += 0x00010000;
725 }
726
727
728 /*
729 * Is there more stuff here, in this comment?
730 * Skip to end of the current comment and copy the contents to szDescription.
731 * if szDescription suddenly contains nothing.
732 */
733 psz = &szDescription[0];
734 copyComment(psz, psz2+1, i, papszLines, TRUE, TRUE);
735 if (*psz == '\0')
736 { /*
737 * No description in the first comment. (The one with $Id.)
738 * Is there a comment following the first one?
739 */
740 while (papszLines[i] != NULL && strstr(papszLines[i++], "*/") == NULL)
741 i = i;
742 while (papszLines[i] != NULL)
743 {
744 psz2 = papszLines[i];
745 while (*psz2 == ' ')
746 psz2++;
747 if (*psz2 != '\0')
748 break;
749 i++;
750 }
751 if (psz2 != NULL && strncmp(psz2, "/*", 2) == 0)
752 {
753 psz = &szDescription[0];
754 copyComment(psz, psz2+1, i, papszLines, TRUE, TRUE);
755 while (*psz == '\n' && *psz == ' ')
756 psz++;
757 if (psz == '\0')
758 szDescription[0] = '\0';
759
760 /* Skip to line after comment end. */
761 while (papszLines[i] != NULL && strstr(papszLines[i++], "*/") == NULL)
762 i = i;
763 }
764 }
765 else
766 {
767 /* Skip to line after comment end. */
768 while (papszLines[i] != NULL && strstr(papszLines[i++], "*/") == NULL)
769 i = i;
770 }
771 }
772 else
773 {
774 fprintf(phSignal, "%s, module header: $Id keyword is incorrect (1).\n", pszFilename);
775 ulRc += 0x00010000;
776 }
777 iRet = i;
778
779
780 /*
781 * Information is collected.
782 * Insert or update the database.
783 */
784 if (dbInsertUpdateFile((unsigned short)pOptions->lDllRefcode, pszDBFilename,
785 &szDescription[0], pszLastDateTime, lLastAuthor, pszRevision))
786 {
787 /*
788 * Get file refcode.
789 */
790 pOptions->lFileRefcode = dbFindFile(pOptions->lDllRefcode, pszDBFilename);
791 if (pOptions->lFileRefcode < 0)
792 {
793 fprintf(phSignal, "%s, module header: failed to find file in DB. %s\n",
794 pszDBFilename, dbGetLastErrorDesc());
795 return 0x00010000;
796 }
797 }
798 else
799 {
800 fprintf(phSignal, "%s, module header: failed to insert/update file. %s\n",
801 pszDBFilename, dbGetLastErrorDesc());
802 return 0x00010000;
803 }
804 }
805 else
806 {
807 fprintf(phSignal, "%s, module header: $Id keyword is missing.\n", pszFilename);
808 return 0x00010000;
809 }
810
811 return TRUE;
812}
813
814
815/**
816 * Processes an API function.
817 * @returns high word = number of signals
818 * low word = Success indicator (TRUE/FALSE).
819 * @param papszLines Array of lines in the file.
820 * @param i Index into papszLines.
821 * @param iRet Index into papszLines. Where to resume search.
822 * @param pszFilename Filename used in the signal log.
823 * @param pOptions Pointer to options.
824 */
825static unsigned long processDesignNote(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions)
826{
827 unsigned long ulRc;
828 char szBuffer[0x10000];
829 char * psz;
830
831 /*
832 * Find and parse the @designnote tag/keyword.
833 * syntax: @designnote [seqnbr] <title>
834 * <text>
835 *
836 */
837 psz = stristr(papszLines[i], "@designnote ");
838 if (psz != NULL)
839 {
840 signed long lSeqNbr;
841
842 psz = findEndOfWord(psz+1)+1;
843 lSeqNbr = atol(psz);
844 if (lSeqNbr != 0)
845 psz = findEndOfWord(psz);
846 psz = trim(psz);
847 if (psz != NULL && strstr(psz, "*/") != NULL)
848 {
849 szBuffer[0] = '\0';
850 *strstr(psz, "*/") = '\0';
851 }
852 else
853 copyComment(&szBuffer[0], 0, i+1, papszLines, TRUE, TRUE);
854
855 /* Update database */
856 if (!dbAddDesignNote(pOptions->lDllRefcode, pOptions->lFileRefcode, psz, &szBuffer[0], lSeqNbr, pOptions->lSeqFile++))
857 {
858 ulRc = 0x00010000;
859 fprintf(phSignal, "%s(%d): Failed to add designnote. %s\n", dbGetLastErrorDesc());
860 }
861
862 /* Skip to line after comment end. */
863 while (papszLines[i] != NULL && strstr(papszLines[i++], "*/") == NULL)
864 i = i;
865 iRet = i;
866 }
867 else
868 {
869 fprintf(phSignal, "%s(%d): internal error @designnote \n", pszFilename, i);
870 ulRc = 0x00010000;
871 }
872
873 return ulRc;
874}
875
876
877
878
879/**
880 * Processes an API function.
881 * @returns high word = number of signals
882 * low word = number of APIs processed. (1 or 0).
883 * @param papszLines Array of lines in the file.
884 * @param i Index into papszLines.
885 * @param iRet Index into papszLines. Where to resume search.
886 * @param pszFilename Filename used in the signal log.
887 * @param pOptions Pointer to options.
888 */
889static unsigned long processAPI(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions)
890{
891 unsigned long ulRc;
892 int j;
893 FNDESC FnDesc;
894 memset(&FnDesc, 0, sizeof(FnDesc));
895
896 /* default value and file number. */
897 FnDesc.lStatus = 99;
898 FnDesc.lFile = pOptions->lFileRefcode;
899
900 /* precondition: isFunction is true.
901 * brief algorithm:
902 * 1. Analyse function declaration.
903 * 2. Analyse function header.
904 * 3. Log data (for debug purpose).
905 * 4. Update database
906 */
907
908 /* 1.*/
909 ulRc = analyseFnDcl(&FnDesc, papszLines, i, iRet, pszFilename, pOptions);
910 if (0x0000ffff & ulRc) /* if low word is 0 the fatal */
911 {
912 unsigned long ulRcTmp;
913 //char szErrorDesc[2113]; /* due to some limitation in the latest EMX release size is 2112 and not 4096 as initially implemented. */
914 char *pszErrorDesc = (char*)malloc(20480);
915
916 /* 2.*/
917 ulRcTmp = analyseFnHdr(&FnDesc, papszLines, i, pszFilename, pOptions);
918 if (ulRcTmp == ~0UL) /* check for fatal error */
919 return (0xffff0000UL & ulRc) + 0x00010000UL;
920 ulRc += 0xffff0000UL & ulRcTmp;
921
922 /* 3.*/
923 fprintf(phLog, "Name: '%s' (refcodes=", FnDesc.pszName);
924 for (j = 0; j < FnDesc.cRefCodes; j++)
925 fprintf(phLog, j > 0 ? ", %ld" : "%ld", FnDesc.alRefCode[j]);
926 fprintf(phLog, ")\n");
927 fprintf(phLog, " Returns: '%s'\n", FnDesc.pszReturnType != NULL ? FnDesc.pszReturnType : "<missing>");
928 fprintf(phLog, " cParams: %2d\n", FnDesc.cParams);
929 for (j = 0; j < FnDesc.cParams; j++)
930 fprintf(phLog, " Param %2d: type '%s' %*s name '%s' description: %s\n", j, FnDesc.apszParamType[j],
931 max((int)(15 - strlen(FnDesc.apszParamType[j])), 0), "", FnDesc.apszParamName[j],
932 FnDesc.apszParamDesc[j] != NULL ? FnDesc.apszParamDesc[j] : "(null)");
933 fprintf(phLog, " Status: %ld - '%s'\n", FnDesc.lStatus, FnDesc.pszStatus != NULL ? FnDesc.pszStatus : "<missing>");
934 fprintf(phLog, " cAuthors: %2d\n", FnDesc.cAuthors);
935 for (j = 0; j < FnDesc.cAuthors; j++)
936 fprintf(phLog, " Author %d: '%s' (refcode=%ld)\n", j, FnDesc.apszAuthor[j], FnDesc.alAuthorRefCode[j]);
937
938 fprintf(phLog, " Description: %s\n", FnDesc.pszDescription != NULL ? FnDesc.pszDescription : "(null)");
939 fprintf(phLog, " Remark: %s\n", FnDesc.pszRemark != NULL ? FnDesc.pszRemark : "(null)");
940 fprintf(phLog, " Return Desc: %s\n", FnDesc.pszReturnDesc != NULL ? FnDesc.pszReturnDesc : "(null)");
941 fprintf(phLog, " Sketch: %s\n", FnDesc.pszSketch != NULL ? FnDesc.pszSketch : "(null)");
942 fprintf(phLog, " Equiv: %s\n", FnDesc.pszEquiv != NULL ? FnDesc.pszEquiv : "(null)");
943 fprintf(phLog, " Time: %s\n", FnDesc.pszTime != NULL ? FnDesc.pszTime : "(null)");
944 fprintf(phLog, "------------\n");
945
946 /* 4.*/
947 ulRcTmp = dbUpdateFunction(&FnDesc, pOptions->lDllRefcode, pszErrorDesc);
948 if (ulRcTmp != 0)
949 {
950 fprintf(phSignal, "%s,%s: %s\n", pszFilename, FnDesc.pszName, pszErrorDesc);
951 ulRc += ulRcTmp << 16;
952 }
953 free(pszErrorDesc);
954 }
955
956 return ulRc;
957}
958
959
960/**
961 * Analyses the function declaration.
962 * @returns high word = number of signals
963 * low word = number of APIs processed. (1 or 0).
964 * @param papszLines Array of lines in the file.
965 * @param i Index into papszLines.
966 * @param iRet Index into papszLines. Where to start searching again.
967 * @param pszFilename Filename used in the signal log.
968 * @param pOptions Pointer to options.
969 */
970static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
971 const char *pszFilename, POPTIONS pOptions)
972{
973 static long lPrevFnDll = -1L; /* fix for duplicate dlls */
974 unsigned long ulRc;
975 FNFINDBUF FnFindBuf;
976 long lFn = 0;
977
978 /* brief algorithm:
979 * 1. read function declaration using analyseFnDcl2.
980 * 2. apply name rules.
981 * 3. do a database lookup on the name.
982 * 3b. if more that one match, write a signal. (TODO: a simple fix is done, but there are holes.)
983 */
984
985 /* 1. */
986 ulRc = analyseFnDcl2(pFnDesc, papszLines, i, iRet, pszFilename, pOptions);
987 if (ulRc != 1)
988 return ulRc;
989
990 /* 2. */
991 if (pOptions->fOS2 && strncmp(pFnDesc->pszName, "OS2", 3) == 0)
992 pFnDesc->pszName += 3;
993 else if (pOptions->fCOMCTL32 && strncmp(pFnDesc->pszName, "COMCTL32", 8) == 0)
994 pFnDesc->pszName += 8;
995 else if (pOptions->fVERSION && strncmp(pFnDesc->pszName, "VERSION", 7) == 0)
996 pFnDesc->pszName += 7;
997 else if (pOptions->fOld)
998 pFnDesc->pszName += 3;
999
1000 /* 3. */
1001 if (!dbFindFunction(pFnDesc->pszName, &FnFindBuf, pOptions->lDllRefcode))
1002 {
1003 fprintf(phSignal, "%s, %s: error occured while reading from database, %s\n",
1004 pszFilename, pFnDesc->pszName, dbGetLastErrorDesc());
1005 return 0x00010000;
1006 }
1007
1008 pFnDesc->cRefCodes = 0;
1009 if (FnFindBuf.cFns != 0)
1010 {
1011 if (pOptions->lDllRefcode < 0)
1012 {
1013 if (FnFindBuf.cFns > 1)
1014 {
1015 fprintf(phSignal, "%s: unknown dll and more than two occurences of this function!\n", pszFilename);
1016 return 0x00010000;
1017 }
1018 pOptions->lDllRefcode = FnFindBuf.alDllRefCode[0];
1019 fprintf(phLog, "DllRef = %d\n", pOptions->lDllRefcode);
1020 }
1021
1022 for (lFn = 0; lFn < FnFindBuf.cFns; lFn++)
1023 pFnDesc->alRefCode[pFnDesc->cRefCodes++] = FnFindBuf.alRefCode[lFn];
1024
1025 if (pFnDesc->cRefCodes == 0)
1026 fprintf(phLog, "%s was not an API in this dll(%d)!\n", pFnDesc->pszName, pOptions->lDllRefcode);
1027 }
1028 else
1029 fprintf(phLog, "%s was not an API\n", pFnDesc->pszName);
1030
1031 ulRc = pFnDesc->cRefCodes;
1032 return ulRc;
1033}
1034
1035
1036
1037/**
1038 * Analyses the function declaration.
1039 * No DB lockup or special function type stuff, only ODINFUNCTION is processed.
1040 * @returns high word = number of signals
1041 * low word = number of APIs processed. (1 or 0).
1042 * @param papszLines Array of lines in the file.
1043 * @param i Index into papszLines.
1044 * @param iRet Index into papszLines. Where to start searching again.
1045 * @param pszFilename Filename used in the signal log.
1046 * @param pOptions Pointer to options.
1047 */
1048static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
1049 const char *pszFilename, POPTIONS pOptions)
1050{
1051 /** @sketch
1052 * 1. find the '('
1053 * 2. find the word ahead of the '(', this is the function name.
1054 * 2a. class test.
1055 * 3. find the closing ')'
1056 * 4. copy the parameters, which is between the two '()'
1057 * 5. format the parameters
1058 */
1059
1060 int iFn, iP1, iP2, j, c;
1061 char * pszFn, *pszFnEnd, *pszP1, *pszP2;
1062 char * psz, *pszEnd;
1063 int cArgs;
1064 char * apszArgs[30];
1065 int iClass;
1066 char * pszClass, *pszClassEnd;
1067
1068 /* 1.*/
1069 iP1 = i;
1070 while (papszLines[iP1] != NULL
1071 && (pszP1 = strchr(papszLines[iP1], '(')) == NULL)
1072 iP1++;
1073 if (papszLines[iP1] == NULL)
1074 {
1075 fprintf(phSignal, "%d: oops! didn't find end of function!, %d\n", pszFilename, __LINE__);
1076 iRet = iP1+1;
1077 return 0x00010000;
1078 }
1079
1080 /* 2. */
1081 iFn = iP1;
1082 if (papszLines[iFn] != pszP1)
1083 pszFn = pszP1 - 1;
1084 else
1085 {
1086 pszFn = papszLines[--iFn];
1087 pszFn += strlen(pszFn) - (*pszFn != '\0');
1088 }
1089 while (iFn >= 0 && *pszFn == ' ')
1090 {
1091 if (pszFn != papszLines[iFn])
1092 pszFn--;
1093 else
1094 {
1095 pszFn = papszLines[--iFn];
1096 pszFn += strlen(pszFn) - (*pszFn != '\0');
1097 }
1098 }
1099 if (iFn < 0 || *pszFn == ' ' || *pszFn == '\0')
1100 {
1101 fprintf(phSignal, "%s: internal error!, %d\n", pszFilename, __LINE__);
1102 iRet = iP1+1;
1103 return 0x00010000;
1104 }
1105 pszFnEnd = pszFn;
1106 pszFn = findStartOfWord(pszFn, papszLines[iFn]);
1107
1108 /* 2a. */
1109 /* operators are not supported (BOOL kTime::operator > (const kTime &time) const) */
1110 if (pszFn > papszLines[iFn])
1111 {
1112 pszClassEnd = pszFn - 1;
1113 iClass = iFn;
1114 }
1115 else
1116 {
1117 pszClassEnd = pszFn - 1;
1118 iClass = iFn - 1;
1119 }
1120 c = 2;
1121 while (iClass >= 0 && c >= 0)
1122 {
1123 if (*pszClassEnd == ':')
1124 c--;
1125 else if (*pszClassEnd != ' ')
1126 break;
1127 pszClassEnd--;
1128 }
1129 if (*pszClassEnd != ' ' && c == 0)
1130 pszClass = findStartOfWord(pszClassEnd, papszLines[iClass]);
1131 else
1132 pszClass = pszClassEnd = NULL;
1133
1134 /* 3. */
1135 c = 1;
1136 iP2 = iP1;
1137 pszP2 = pszP1 + 1;
1138 while (c > 0)
1139 {
1140 if (*pszP2 == '(')
1141 c++;
1142 else if (*pszP2 == ')')
1143 if (--c == 0)
1144 break;
1145 if (*pszP2++ == '\0')
1146 if ((pszP2 = papszLines[++iP2]) == NULL)
1147 break;
1148 }
1149
1150 iRet = iP2 + 1; //assumes: only one function on a single line!
1151
1152 /* 4. */
1153 psz = pFnDesc->szFnDclBuffer;
1154 copy(pFnDesc->szFnDclBuffer, pszP1, iP1, pszP2, iP2, papszLines);
1155 pszEnd = psz + strlen(psz) + 1;
1156
1157 /* 5.*/
1158 cArgs = 0;
1159 if (stricmp(psz, "(void)") != 0 && strcmp(psz, "()") != 0 && strcmp(psz, "( )"))
1160 {
1161 char *pszC;
1162 pszC = trim(psz+1);
1163 c = 1;
1164 while (*pszC != '\0')
1165 {
1166 apszArgs[cArgs] = pszC;
1167 while (*pszC != ',' && c > 0 && *pszC != '\0')
1168 {
1169 if (*pszC == '(')
1170 c++;
1171 else if (*pszC == ')')
1172 if (--c == 0)
1173 break;
1174 pszC++;
1175 }
1176 *pszC = '\0';
1177 trim(apszArgs[cArgs++]);
1178
1179 /* next */
1180 pszC = trim(pszC + 1);
1181 }
1182 }
1183
1184 /* 6. */
1185 if (strnicmp(pszFn, "ODINFUNCTION", 12) == 0 || strnicmp(pszFn, "ODINPROCEDURE", 13) == 0)
1186 {
1187 BOOL fProc = strnicmp(pszFn, "ODINPROCEDURE", 13) == 0;
1188 j = 0;
1189 if (cArgs < (fProc ? 1 : 2))
1190 {
1191 fprintf(phSignal, "%s: Invalid ODINFUNCTION function too few parameters!\n", pszFilename);
1192 return 0x00010000;
1193 }
1194
1195 /* return type */
1196 if (fProc)
1197 pFnDesc->pszReturnType = "void";
1198 else
1199 pFnDesc->pszReturnType = apszArgs[j++];
1200
1201 /* function name */
1202 pFnDesc->pszName = apszArgs[j++];
1203
1204 /* arguments */
1205 pFnDesc->cParams = 0;
1206 while (j+1 < cArgs)
1207 {
1208 pFnDesc->apszParamType[pFnDesc->cParams] = apszArgs[j];
1209 pFnDesc->apszParamName[pFnDesc->cParams] = apszArgs[j+1];
1210 pFnDesc->cParams++;
1211 j += 2;
1212 }
1213 }
1214 else
1215 {
1216 /* return type */
1217 int iReturn = pszClass != NULL ? iClass : iFn;
1218 char * pszReturn = pszClass != NULL ? pszClass : pszFn;
1219
1220 if (pszReturn != papszLines[iReturn])
1221 pszReturn--;
1222 else
1223 {
1224 pszReturn = papszLines[--iReturn];
1225 pszReturn += strlen(pszReturn) - (*pszReturn != '\0');
1226 }
1227 pszReturn = skipBackwards("{};-+#:\"\'", pszReturn, iReturn, papszLines);
1228 *pszEnd = '\0';
1229 copy(pszEnd, pszReturn, iReturn, pszFn-1, iFn, papszLines);
1230 if (strlen(pszEnd) > 128)
1231 {
1232 /* FIXME LATER! Some constructors calling baseclass constructors "breaks" this rule. Win32MDIChildWindow in /src/user32/win32wmdichild.cpp for example. */
1233 fprintf(phSignal,"Fatal error? return statement is too larget. len=%d\n", strlen(pszEnd));
1234 fprintf(phLog, "Fatal error? return statement is too larget. len=%d\n", strlen(pszEnd));
1235 if (strlen(pszEnd) > 512)
1236 fprintf(stderr, "Fatal error? return statement is too larget. len=%d\n", strlen(pszEnd));
1237 fflush(phLog);
1238 fflush(phSignal);
1239 fflush(stderr);
1240 }
1241 pszEnd = trim(pszEnd);
1242 pFnDesc->pszReturnType = *pszEnd == '\0' ? NULL : pszEnd;
1243 pszEnd = strlen(pszEnd) + pszEnd + 1;
1244 *pszEnd = '\0';
1245
1246 /* !BugFix! some function occur more than once, usually as inline functions */
1247 if (pFnDesc->pszReturnType != NULL
1248 && strstr(pFnDesc->pszReturnType, "inline ") != NULL)
1249 {
1250 fprintf(phLog, "Not an API. Inlined functions can't be exported!\n");
1251 return 0;
1252 }
1253
1254 /* function name */
1255 if (pFnDesc->pszReturnType != NULL
1256 && (stristr(pFnDesc->pszReturnType, "cdecl") != NULL
1257 || strstr(pFnDesc->pszReturnType, "VFWAPIV") != NULL
1258 || strstr(pFnDesc->pszReturnType, "WINAPIV") != NULL
1259 )
1260 )
1261 { /* cdecl function is prefixed with an '_' */
1262 strcpy(pszEnd, "_");
1263 }
1264 if (pszClass != NULL)
1265 {
1266 strncat(pszEnd,pszClass, pszClassEnd - pszClass + 1);
1267 strcat(pszEnd, "::");
1268 }
1269 strncat(pszEnd, pszFn, pszFnEnd - pszFn + 1);
1270 pFnDesc->pszName = pszEnd;
1271 pszEnd = strlen(pszEnd) + pszEnd + 1;
1272 *pszEnd = '\0';
1273
1274
1275 /* arguments */
1276 pFnDesc->cParams = cArgs;
1277 for (j = 0; j < cArgs; j++)
1278 {
1279 int cch = strlen(apszArgs[j]);
1280 if ((psz = strchr(apszArgs[j], ')')) == NULL)
1281 { /* Common argument */
1282 if (apszArgs[j][cch-1] != '*' || (apszArgs[j][cch - 1] == ']' && cch < 5))
1283 { /* nearly Normal case, Type [moretype] Name.*/
1284 if (apszArgs[j][cch - 1] != ']')
1285 {
1286 if (strcmp(apszArgs[j], "...") != 0)
1287 { /* Normal case! */
1288 pFnDesc->apszParamName[j] = findStartOfWord(apszArgs[j] + cch - 1,
1289 apszArgs[j]);
1290 pFnDesc->apszParamName[j][-1] = '\0';
1291 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1292 }
1293 else
1294 { /* eliptic */
1295 pFnDesc->apszParamName[j] = "...";
1296 pFnDesc->apszParamType[j] = "";
1297 }
1298 }
1299 else
1300 { /* arg yet another special case! 'fn(int argc, char *argv[])' */
1301 char *pszP2;
1302 cch = strlen(apszArgs[j]);
1303 psz = &apszArgs[j][cch-2];
1304 while (psz > apszArgs[j] && ((*psz >= '0' && *psz <= '9') || *psz == ' '))
1305 psz--;
1306
1307 if (psz > apszArgs[j] && *psz == '[')
1308 {
1309 pszP2 = psz--;
1310 while (psz >= apszArgs[j] && *psz == ' ')
1311 psz--;
1312 }
1313
1314 if (psz <= apszArgs[j])
1315 { /* funny case - no name? */
1316 sprintf(pszEnd, "arg%i", j);
1317 pFnDesc->apszParamName[j] = pszEnd;
1318 pszEnd = strlen(pszEnd) + pszEnd + 1;
1319 *pszEnd = '\0';
1320 }
1321 else
1322 { /* *pszP2 = '[' and psz = end of name */
1323 psz = findStartOfWord(psz, apszArgs[j]);
1324 strncat(pszEnd, psz, pszP2 - psz);
1325 pFnDesc->apszParamName[j] = pszEnd;
1326 pszEnd = strlen(pszEnd) + pszEnd + 1;
1327 *pszEnd = '\0';
1328 memset(psz, ' ', pszP2 - psz);
1329 }
1330 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1331 }
1332 }
1333 else
1334 { /* No argument name only type - make a dummy one: 'arg[1..n]' */
1335 sprintf(pszEnd, "arg%i", j);
1336 pFnDesc->apszParamName[j] = pszEnd;
1337 pszEnd = strlen(pszEnd) + pszEnd + 1;
1338 *pszEnd = '\0';
1339 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1340 }
1341 }
1342 else
1343 { /* Function pointer argument... */
1344 char *pszP2 = psz;
1345 psz = findStartOfWord(psz-1, apszArgs[j]);
1346 strncat(pszEnd, psz, pszP2 - psz);
1347
1348 pFnDesc->apszParamName[j] = pszEnd;
1349 memset(psz, ' ', pszP2 - psz);
1350 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1351
1352 pszEnd = strlen(pszEnd) + pszEnd + 1;
1353 *pszEnd = '\0';
1354 }
1355 }
1356 }
1357 pOptions = pOptions;
1358 return 0x00000001;
1359}
1360
1361
1362/**
1363 * Analyses the function header.
1364 * @returns high word = number of signals
1365 * low word = number of APIs processed. (1 or 0).
1366 * @param papszLines Array of lines in the file.
1367 * @param i Index into papszLines.
1368 * @param pszFilename Filename used in the signal log.
1369 * @param pOptions Pointer to options.
1370 * @sketch 0. initiate pFnDesc struct
1371 * 1. Search backwards (start at i-1) for a comment or code.
1372 * 2. If comment: (else fail)
1373 * 2a. find start and end of comment.
1374 * 2b. check for function header characteristics
1375 * - lots of '*'s.
1376 * - fields 'Status', 'Author' and 'Name'
1377 * or if SDS, check for:
1378 * - at least two '*'s at the begining.
1379 * - fields '@status' and/or '@author'
1380 * 2c. check that content of the 'Name' field matches (not SDS)
1381 * 2d. read the status and author fields.
1382 * @remark Supports both types of function headers, Odin32-common and SDS.
1383 */
1384static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions)
1385{
1386 int iStatus, iAuthor, iName, iStart, iEnd;
1387 int j, jStart;
1388 int fFound;
1389 int fSDS = 0;
1390 char *psz, *pszB;
1391 char *pszAuthors = NULL;
1392 unsigned long ulRc = 0x00000001;
1393
1394 pOptions = pOptions;
1395
1396 if (i < 0) /* parameter validation */
1397 return 0;
1398
1399 /*
1400 * 0. initiate pFnDesc struct
1401 */
1402 for (j = 0; j < pFnDesc->cParams; j++)
1403 pFnDesc->apszParamDesc[j] = NULL;
1404 pFnDesc->cAuthors = 0;
1405 pFnDesc->pszDescription = pFnDesc->pszRemark = pFnDesc->pszReturnDesc = NULL;
1406 pFnDesc->pszSketch = pFnDesc->pszEquiv = pFnDesc->pszTime = pFnDesc->pszStatus = NULL;
1407 pFnDesc->lStatus = 99; /* unknown */
1408
1409 /*
1410 * 1. + 2a.
1411 */
1412 iEnd = i-1; /* find end */
1413 j = strlen(papszLines[iEnd]);
1414 j -= j > 0 ? 1 : 0;
1415 fFound = 0;
1416 while (iEnd >= 0 && i - iEnd < 7 && papszLines[iEnd][j] != '}' &&
1417 !(fFound = (papszLines[iEnd][j] == '*' && papszLines[iEnd][j+1] == '/')))
1418 if (j-- == 0)
1419 if (iEnd-- > 0)
1420 {
1421 j = strlen(papszLines[iEnd]);
1422 j -= j > 0 ? 1 : 0;
1423 }
1424 if (!fFound) /* fail if not found */
1425 return 0;
1426
1427 iStart = iEnd; /* find start */
1428 if (j < 2)
1429 j -= (j = strlen(papszLines[--iStart])) > 1 ? 2 : j;
1430 else
1431 j -= 2;
1432 fFound = 0;
1433 while (iStart >= 0
1434 && (j < 0
1435 || !(fFound = (papszLines[iStart][j] == '/' && papszLines[iStart][j+1] == '*'))
1436 )
1437 )
1438 if (j-- <= 0)
1439 if (iStart-- > 0)
1440 {
1441 j = strlen(papszLines[iStart]);
1442 j -= j > 1 ? 2 : j;
1443 }
1444
1445 if (!fFound) /* fail if not found */
1446 return 0;
1447 jStart = j;
1448
1449 /*
1450 * 2b.
1451 */
1452 if (strncmp(&papszLines[iStart][jStart], "/**", 3) != 0) /* checks that there are more than one star at start of comment */
1453 return 0;
1454
1455 /* Odin32 common? */
1456 iName = findStrLine("* Name", iStart, iEnd, papszLines);
1457 iStatus = findStrLine("* Status", iStart, iEnd, papszLines);
1458 iAuthor = findStrLine("* Author", iStart, iEnd, papszLines);
1459
1460 if (!(iName <= iEnd || iStatus <= iEnd || iAuthor <= iEnd))
1461 {
1462 /* SDS ? */
1463 iStatus = findStrLine("@status", iStart, iEnd, papszLines);
1464 iAuthor = findStrLine("@author", iStart, iEnd, papszLines);
1465 iName = iEnd + 1;
1466
1467 if (!(iStatus <= iEnd || iAuthor <= iEnd))
1468 return 0;
1469 fSDS = TRUE;
1470 }
1471 else
1472 {
1473 /* 2c.*/
1474 if (iName <= iEnd && strstr(papszLines[iName], pFnDesc->pszName) == NULL)
1475 fprintf(phLog, "Warning: a matching function name is not found in the name Field\n");
1476 }
1477
1478 /* 2d.*/
1479 pszB = &pFnDesc->szFnHdrBuffer[0];
1480 if (!fSDS)
1481 {
1482 /*
1483 * Odin32 common styled header
1484 */
1485
1486 /* Author(s) */
1487 pszAuthors = CommonCopyTextUntilNextTag(pszB, FALSE, iAuthor, iEnd, papszLines);
1488 pszB += strlen(pszB) + 1;
1489
1490 /* Status */
1491 pFnDesc->pszStatus = CommonCopyTextUntilNextTag(pszB, FALSE, iStatus, iEnd, papszLines);
1492 pszB += strlen(pszB) + 1;
1493
1494 /* Remark */
1495 i = findStrLine("* Remark", iStart, iEnd, papszLines);
1496 pFnDesc->pszRemark = CommonCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1497 pszB += strlen(pszB) + 1;
1498
1499 /* Description */
1500 i = findStrLine("* Purpose", iStart, iEnd, papszLines);
1501 pFnDesc->pszDescription = CommonCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1502 pszB += strlen(pszB) + 1;
1503
1504 /* Return(result) description */
1505 i = findStrLine("* Result", iStart, iEnd, papszLines);
1506 pFnDesc->pszReturnDesc = CommonCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1507 pszB += strlen(pszB) + 1;
1508
1509 /* FIXME parameters */
1510
1511 }
1512 else
1513 {
1514 /*
1515 * SDS styled header
1516 */
1517
1518 /* author */
1519 pszAuthors = SDSCopyTextUntilNextTag(pszB, FALSE, iAuthor, iEnd, papszLines);
1520 pszB += strlen(pszB) + 1;
1521
1522 /* status */
1523 pFnDesc->pszReturnDesc = SDSCopyTextUntilNextTag(pszB, FALSE, iStatus, iEnd, papszLines);
1524 pszB += strlen(pszB) + 1;
1525
1526 /* remark */
1527 i = findStrLine("@remark", iStart, iEnd, papszLines);
1528 pFnDesc->pszRemark = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1529 pszB += strlen(pszB) + 1;
1530
1531 /* description */
1532 i = findStrLine("@desc", iStart, iEnd, papszLines);
1533 pFnDesc->pszDescription = SDSCopyTextUntilNextTag(pszB, TRUE, i > iEnd ? iStart : i, iEnd, papszLines);
1534 pszB += strlen(pszB) + 1;
1535
1536 /* sketch */
1537 i = findStrLine("@sketch", iStart, iEnd, papszLines);
1538 pFnDesc->pszSketch = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1539 pszB += strlen(pszB) + 1;
1540
1541 /* return description */
1542 i = findStrLine("@return", iStart, iEnd, papszLines);
1543 pFnDesc->pszReturnDesc = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1544 pszB += strlen(pszB) + 1;
1545
1546 /* time */
1547 i = findStrLine("@time", iStart, iEnd, papszLines);
1548 pFnDesc->pszReturnDesc = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1549 pszB += strlen(pszB) + 1;
1550
1551 /* equiv */
1552 i = findStrLine("@equiv", iStart, iEnd, papszLines);
1553 pFnDesc->pszReturnDesc = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1554 pszB += strlen(pszB) + 1;
1555
1556 /* Set parameter descriptions to NULL */
1557 for (i = 0; i < pFnDesc->cParams; i++)
1558 pFnDesc->apszParamDesc[i] = NULL;
1559
1560 /*
1561 * parameters, new @param for each parameter!
1562 */
1563 do
1564 {
1565 char *pszParam;
1566
1567 /* find first */
1568 i = findStrLine("@param", iStart, iEnd, papszLines);
1569 if (i >= iEnd)
1570 break;
1571
1572 /* Get parameter name - first word */
1573 pszParam = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1574 if (pszParam != NULL)
1575 {
1576 /* first word in psz is the parameter name! */
1577 psz = findEndOfWord(pszParam);
1578 *psz++ = '\0';
1579
1580 /* find parameter */
1581 for (j = 0; j < pFnDesc->cParams; j++)
1582 if (strcmp(pFnDesc->apszParamName[j], pszParam) == 0)
1583 break;
1584 if (j < pFnDesc->cParams)
1585 {
1586 /* find end of parameter name */
1587 psz = findEndOfWord(strstr(papszLines[i], pszParam));
1588 /* copy from end of parameter name */
1589 pFnDesc->apszParamDesc[j] = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines, psz);
1590 pszB += strlen(pszB) + 1;
1591 }
1592 else
1593 { /* signal that parameter name wasn't found! */
1594 fprintf(phSignal, "%s, %s: '%s' is not a valid parameter.\n",
1595 pszFilename, pFnDesc->pszName, pszParam);
1596 ulRc += 0x00010000;
1597 }
1598 }
1599 } while (i > iEnd);
1600 }
1601
1602 /*
1603 * Status - refcodes are hardcoded here!
1604 */
1605 if (pFnDesc->pszStatus != NULL && *pFnDesc->pszStatus != '\0')
1606 {
1607 if (strstr(pFnDesc->pszStatus, "STUB") != NULL || *pFnDesc->pszStatus == '1')
1608 pFnDesc->lStatus = 1; /* STUB */
1609 else if (stristr(pFnDesc->pszStatus, "Partially") != NULL || *pFnDesc->pszStatus == '2' || *pFnDesc->pszStatus == '3')
1610 {
1611 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '2')
1612 pFnDesc->lStatus = 2; /* STUB */
1613 else
1614 pFnDesc->lStatus = 3; /* STUB */
1615 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1616 || *pFnDesc->pszStatus == '5' || *pFnDesc->pszStatus == '7')
1617 pFnDesc->lStatus += 4;
1618 }
1619 else if (stristr(pFnDesc->pszStatus, "Completely") != NULL || *pFnDesc->pszStatus == '4' || *pFnDesc->pszStatus == '5')
1620 {
1621 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '4')
1622 pFnDesc->lStatus = 4; /* STUB */
1623 else
1624 pFnDesc->lStatus = 5; /* STUB */
1625 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1626 || *pFnDesc->pszStatus == '8' || *pFnDesc->pszStatus == '9')
1627 pFnDesc->lStatus += 4;
1628 }
1629 else
1630 {
1631 fprintf(phSignal, "%s, %s: '%s' is not a valid status code.\n",
1632 pszFilename, pFnDesc->pszName, pFnDesc->pszStatus);
1633 ulRc += 0x00010000;
1634 }
1635 }
1636
1637 /*
1638 * Author
1639 */
1640 if (pszAuthors != NULL)
1641 { /* author1, author2, author3 */
1642 j = 0;
1643 psz = trim(pszAuthors);
1644 pFnDesc->cAuthors = 0;
1645 while (*psz != '\0' && pFnDesc->cAuthors < (int)(sizeof(pFnDesc->apszAuthor) / sizeof(pFnDesc->apszAuthor[0])))
1646 {
1647 char *pszBr1 = strchr(psz, '[');
1648 char *pszBr2 = strchr(psz, ']');
1649 char *pszEmail = NULL;
1650 char *pszNext;
1651
1652 /* remove '[text]' text - this is usualy used for time/date */
1653 if (pszBr1 != NULL && pszBr2 != NULL && pszBr1 < pszBr2)
1654 while (pszBr1 <= pszBr2)
1655 *pszBr1++ = ' ';
1656
1657 /* terminate string */
1658 pszNext = strchr(psz, ',');
1659 if (pszNext == NULL)
1660 pszNext = strchr(psz, '\n');
1661 if (pszNext != NULL)
1662 *pszNext = '\0';
1663
1664 /* check for (email) test */
1665 pszBr1 = strchr(psz, '(');
1666 pszBr2 = strchr(psz, ')');
1667 if (pszBr1 != NULL || pszBr2 != NULL)
1668 {
1669 if (pszBr1 != NULL)
1670 *pszBr1++ = '\0';
1671 if (pszBr2 != NULL)
1672 *pszBr2++ = '\0';
1673
1674 if (pszBr1 != NULL && pszBr1 < pszBr2)
1675 pszEmail = trim(pszBr1 + 1);
1676 }
1677
1678 pFnDesc->apszAuthor[pFnDesc->cAuthors] = trim(psz);
1679 pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] =
1680 dbFindAuthor(pFnDesc->apszAuthor[pFnDesc->cAuthors], pszEmail);
1681
1682 if (pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] == -1)
1683 {
1684 fprintf(phSignal, "%s, %s: author '%s' is not recognized.\n", pszFilename, pFnDesc->pszName,
1685 pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1686 ulRc += 0x00010000;
1687 }
1688
1689 /* next */
1690 pFnDesc->cAuthors++;
1691 psz = pszNext ? skip(pszNext + 1) : "";
1692 }
1693 }
1694
1695 return ulRc;
1696}
1697
1698
1699
1700/**
1701 * Copies a piece of tag/keyword text into an buffer. SDS.
1702 * @returns Pointer to buffer. If iStart > iEnd NULL is returned.
1703 * @param pszTarget Pointer to buffer.
1704 * @param fHTML Add HTML tags like <br> or not
1705 * @param iStart Index of start line.
1706 * @param iEnd Index of last line.
1707 * @param apszLines Array lines.
1708 * @param pszStart Pointer to char to start at (into papszLines[iStart] of course!)
1709 * @status completely impelmented.
1710 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1711 * @remark Addes some HTML tags.
1712 */
1713static char *SDSCopyTextUntilNextTag(char *pszTarget, BOOL fHTML, int iStart, int iEnd, char **papszLines, const char *pszStart)
1714{
1715 char *pszRet = pszTarget;
1716
1717 if (iStart <= iEnd)
1718 {
1719 int iStartColumn;
1720 const char *psz = pszStart != NULL ? pszStart : papszLines[iStart];
1721
1722 /*
1723 * skip dummy chars.
1724 */
1725 while (iStart <= iEnd && (*psz == ' ' || *psz == '/' || *psz == '*' || *psz == '\0' ))
1726 {
1727 if (*psz == '\0')
1728 psz = papszLines[++iStart];
1729 else
1730 psz++;
1731 }
1732
1733 /* Anything left of the area to copy? */
1734 if (iStart <= iEnd)
1735 {
1736 /*
1737 * if we stand at a tag, skip over the tag
1738 */
1739 if (*psz == '@')
1740 psz = skip(findEndOfWord(psz+1));
1741
1742 /*
1743 * save start columen
1744 */
1745 iStartColumn = psz - papszLines[iStart];
1746
1747 /*
1748 * Copy loop.
1749 */
1750 do
1751 {
1752 int i;
1753 for (i = psz - papszLines[iStart]; i < iStartColumn; i++)
1754 *pszTarget++ = ' '; /* FIXME HTML and indenting... */
1755
1756 strcpy(pszTarget, psz);
1757 trimR(pszTarget);
1758 pszTarget += strlen(pszTarget);
1759 if (fHTML)
1760 strcpy(pszTarget, "<BR>\n");
1761 else
1762 strcpy(pszTarget, "\n");
1763 pszTarget += strlen(pszTarget);
1764
1765 /* Next */
1766 psz = skip(papszLines[++iStart]);
1767 if (iStart <= iEnd)
1768 {
1769 while (psz != NULL && *psz == '*')
1770 ++psz;
1771 psz = skip(psz);
1772 /* end test comment */
1773 if (psz > papszLines[iStart] && psz[-1] == '*' && *psz == '/')
1774 break;
1775 }
1776 } while (iStart <= iEnd && *psz != '@');
1777
1778 /*
1779 * remove empty lines at end.
1780 */
1781 if (fHTML)
1782 {
1783 pszTarget--;
1784 while (pszTarget >= pszRet && (*pszTarget == '\n' || *pszTarget == ' '))
1785 {
1786 if (*pszTarget == '\n')
1787 pszTarget -= 4;
1788 *pszTarget-- = '\0';
1789 }
1790 }
1791 else
1792 {
1793 pszTarget--;
1794 while (pszTarget >= pszRet && (*pszTarget == '\n' || *pszTarget == ' '))
1795 *pszTarget-- = '\0';
1796 }
1797 }
1798 else
1799 pszTarget = '\0';
1800 }
1801 else
1802 pszRet = NULL;
1803
1804 return pszRet != NULL && *pszRet == '\0' ? NULL : pszRet;
1805}
1806
1807
1808
1809/**
1810 * Copies a piece of tag/keyword text into an buffer. SDS.
1811 * @returns Pointer to buffer. If iStart > iEnd NULL is returned.
1812 * @param pszTarget Pointer to buffer.
1813 * @param fHTML Add HTML tags like <br> or not
1814 * @param iStart Index of start line.
1815 * @param iEnd Index of last line.
1816 * @param apszLines Array lines.
1817 * @param pszStart Pointer to char to start at (into papszLines[iStart] of course!)
1818 * @status completely impelmented.
1819 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1820 * @remark Addes some HTML tags.
1821 */
1822static char *CommonCopyTextUntilNextTag(char *pszTarget, BOOL fHTML, int iStart, int iEnd, char **papszLines, const char *pszStart)
1823{
1824 char *pszRet = pszTarget;
1825
1826 if (iStart <= iEnd)
1827 {
1828 /* known keywords */
1829 int iStartColumn;
1830 int i;
1831 const char *psz = pszStart != NULL ? pszStart : papszLines[iStart];
1832
1833 /*
1834 * Skip evt. keyword
1835 */
1836 psz = skip(psz);
1837 for (i = 0; pszCommonKeywords[i] != NULL; i++)
1838 if (strnicmp(pszCommonKeywords[i], psz, strlen(pszCommonKeywords[i])) == 0)
1839 {
1840 psz = skip(psz + strlen(pszCommonKeywords[i]));
1841 psz = skip(*psz == ':' ? psz + 1 : psz);
1842 break;
1843 }
1844
1845 /*
1846 * save start columen
1847 */
1848 iStartColumn = psz - papszLines[iStart];
1849
1850 /*
1851 * copy loop
1852 */
1853 do
1854 {
1855 /*
1856 * Skip '*'s at start of the line.
1857 */
1858 while (*psz == '*')
1859 psz++;
1860
1861 /* end comment check */
1862 if (psz > papszLines[iStart] && psz[-1] == '*' && *psz == '/')
1863 break;
1864 psz = skip(psz);
1865
1866 /*
1867 * Indent up to iStartColumn
1868 */
1869 for (i = psz - papszLines[iStart]; i < iStartColumn; i++)
1870 *pszTarget++ = ' '; /* FIXME HTML and indenting... */
1871
1872 /*
1873 * Copy the rest of the line and add HTML break.
1874 */
1875 strcpy(pszTarget, psz);
1876 trimR(pszTarget);
1877 pszTarget += strlen(pszTarget);
1878 if (fHTML)
1879 strcpy(pszTarget, "<BR>\n");
1880 else
1881 strcpy(pszTarget, "\n");
1882 pszTarget += strlen(pszTarget);
1883
1884 /*
1885 * Next
1886 */
1887 psz = skip(papszLines[++iStart]);
1888
1889 /*
1890 * Check for keyword
1891 */
1892 if (iStart <= iEnd)
1893 {
1894 psz = skip(psz);
1895 for (i = 0; pszCommonKeywords[i] != NULL; i++)
1896 if (strnicmp(pszCommonKeywords[i], psz, strlen(pszCommonKeywords[i])) == 0)
1897 break;
1898 if (pszCommonKeywords[i] != NULL)
1899 break;
1900 }
1901 } while (iStart < iEnd);
1902
1903 /*
1904 * remove empty lines at end.
1905 */
1906 if (fHTML)
1907 {
1908 pszTarget--;
1909 while (pszTarget >= pszRet && (*pszTarget == '\n' || *pszTarget == ' '))
1910 {
1911 if (*pszTarget == '\n')
1912 pszTarget -= 4;
1913 *pszTarget-- = '\0';
1914 }
1915 }
1916 else
1917 {
1918 pszTarget--;
1919 while (pszTarget >= pszRet && (*pszTarget == '\n' || *pszTarget == ' '))
1920 *pszTarget-- = '\0';
1921 }
1922 }
1923 else
1924 pszRet = NULL;
1925
1926 return pszRet != NULL && *pszRet == '\0' ? NULL : pszRet;
1927}
1928
1929
1930
1931/**
1932 * Checks if there may be an function starting at the current line.
1933 * @returns TRUE if API found, else FALSE.
1934 * @param papszLines Array of lines in the file.
1935 * @param i Index into papszLines.
1936 * @param pOptions Pointer to options.
1937 */
1938static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions)
1939{
1940 if (!pOptions->fOld)
1941 { /* new API naming style */
1942 /* brief algorithm:
1943 * check that line don't start with '\\', '{' or '}'
1944 * search for '('.
1945 * if found then do
1946 * find c-word previous to '('
1947 * if found then do
1948 * check that the following condition are true:
1949 * 1. word is not 'for', 'while', 'do', 'if', 'else' or 'switch'
1950 * 2. first significant char after '(' should not be a '*'. (Fix for functionnames in function header, "BackupRead(".)
1951 * 3. find the matching ')' and check that the first significant char after it is '{'.
1952 * if 1, 2 and 3 are all true return true
1953 * return false.
1954 *
1955 * Note, for 2: spaces, newlines and comments are ignored, all other chars are significant.
1956 */
1957 char *pszP1 = papszLines[i];
1958
1959 while (*pszP1 != '\0' && *pszP1 == ' ')
1960 pszP1++;
1961 if (*pszP1 != '\0' && *pszP1 != '/' && pszP1[1] != '/'
1962 && *pszP1 != '{' && *pszP1 != '}')
1963 {
1964 pszP1 = strchr(papszLines[i], '(');
1965 if (pszP1 != NULL && pszP1 >= papszLines[i])
1966 {
1967 int cchFnName = 0;
1968 char *pszFnName = pszP1 - 1;
1969
1970 while (pszFnName - cchFnName > papszLines[0] && pszFnName[cchFnName] == ' ')
1971 cchFnName--, pszFnName--;
1972
1973 pszFnName = findStartOfWord(pszFnName, papszLines[0]);
1974 cchFnName += pszP1 - pszFnName;
1975 if (cchFnName >= 0)
1976 {
1977 /* 1. */
1978 if (
1979 strncmp(pszFnName, "for", cchFnName) != 0 &&
1980 strncmp(pszFnName, "while", cchFnName) != 0 &&
1981 strncmp(pszFnName, "do", cchFnName) != 0 &&
1982 strncmp(pszFnName, "if", cchFnName) != 0 &&
1983 strncmp(pszFnName, "else", cchFnName) != 0 &&
1984 strncmp(pszFnName, "switch", cchFnName) != 0
1985 )
1986 {
1987 /* 2. */
1988 int j = i;
1989 char *psz = skipInsignificantChars(papszLines, j, pszP1+1);
1990 if (psz != NULL && *psz != '*')
1991 {
1992 char *pszB = pszP1 + 1; /*'{'*/
1993 int c = 1;
1994
1995 /* 3. */
1996 while (c > 0)
1997 {
1998 if (*pszB == '(')
1999 c++;
2000 else if (*pszB == ')')
2001 if (--c == 0)
2002 break;
2003 if (*pszB++ == '\0')
2004 if ((pszB = papszLines[++i]) == NULL)
2005 break;
2006 }
2007 if (pszB != NULL)
2008 {
2009 pszB = skipInsignificantChars(papszLines, i, pszB+1);
2010 if (pszB != NULL && *pszB == '{')
2011 {
2012 fprintf(phLog, "Function found: %.*s\n", cchFnName, pszFnName);
2013 return TRUE;
2014 }
2015 /* FIXME: constructors with ':' afterwards are not supported !TODO! */
2016 }
2017 }
2018 }
2019 }
2020 }
2021 }
2022 }
2023 else
2024 { /* old API naming style */
2025 char *pszOS2;
2026
2027 /* brief algorithm:
2028 * search for function prefix, 'OS2'.
2029 * if found then do
2030 * check that the following condition are true:
2031 * 1. char before 'OS2' is either start-of-line (no char), space or '*'.
2032 * 2. first significant char after the 'OS2' prefixed word is a '('.
2033 * 3. find the matching ')' and check that the first significant char after it is '{'.
2034 * if 1,2 and 3 all are true return true
2035 * return false.
2036 *
2037 * Note, for 2 and 3 spaces, newlines and comments are ignored, all other chars are significant.
2038 */
2039 pszOS2 = strstr(papszLines[i], "OS2");
2040 if (pszOS2 != NULL)
2041 {
2042 /* 1.*/
2043 if (pszOS2 == papszLines[i] || pszOS2[-1] == ' ' || pszOS2[-1] == '*')
2044 {
2045 char *pszP1; /*'('*/
2046 int cchFnName;
2047
2048 /* 2. */
2049 pszP1 = findEndOfWord(pszOS2);
2050 cchFnName = pszP1 - pszOS2;
2051 pszP1 = skipInsignificantChars(papszLines, i, pszP1);
2052
2053 if (pszP1 != NULL && *pszP1 == '(')
2054 {
2055 char *pszB = pszP1 + 1; /*'{'*/
2056 int c = 1;
2057
2058 /* 3. */
2059 while (c > 0)
2060 {
2061 if (*pszB == '(')
2062 c++;
2063 else if (*pszB == ')')
2064 if (--c == 0)
2065 break;
2066 if (*pszB++ == '\0')
2067 if ((pszB = papszLines[++i]) == NULL)
2068 break;
2069 }
2070 if (pszB != NULL)
2071 {
2072 pszB = skipInsignificantChars(papszLines, i, pszB+1);
2073 if (pszB != NULL && *pszB == '{')
2074 {
2075 fprintf(phLog, "Possible API: %.*s\n", cchFnName, pszOS2);
2076 return TRUE;
2077 }
2078 }
2079 }
2080 }
2081 }
2082 }
2083
2084 return FALSE;
2085}
2086
2087
2088/**
2089 * Checks if there may be a design note starting at the current line.
2090 * @returns TRUE if design note found, else FALSE.
2091 * @param papszLines Array of lines in the file.
2092 * @param i Index into papszLines.
2093 * @param pOptions Pointer to options.
2094 */
2095static BOOL isDesignNote(char **papszLines, int i, POPTIONS pOptions)
2096{
2097 char *psz = papszLines[i];
2098
2099 if (psz == NULL)
2100 return FALSE;
2101
2102 // look for /**@designnote "
2103 while (*psz == ' ')
2104 psz++;
2105 if (strncmp(psz, "/**", 3) == 0)
2106 {
2107 psz++;
2108 while (*psz == '*' || *psz == ' ')
2109 psz++;
2110 return strnicmp(psz, "@designnote ", 12) == 0;
2111 }
2112 pOptions = pOptions;
2113 return FALSE;
2114}
2115
2116
2117
2118
2119/**
2120 * Callback function for the dbGetNotUpdatedFunction routine.
2121 *
2122 */
2123static long _System dbNotUpdatedCallBack(const char *pszValue, const char *pszFieldName, void *pvUser)
2124{
2125 static i = 0;
2126 switch (i++)
2127 {
2128 case 0:
2129 fprintf(phLog, "%s", pszValue);
2130 break;
2131 case 1:
2132 fprintf(phLog, "(%s)", pszValue);
2133 break;
2134 case 2: /* updated */
2135 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
2136 break;
2137 case 3: /* aliasfn */
2138 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
2139 break;
2140 case 4:
2141 if (pszValue != NULL)
2142 fprintf(phLog, " --> %s.", pszValue);
2143 break;
2144 case 5:
2145 if (pszValue != NULL)
2146 fprintf(phLog, "%s", pszValue);
2147 break;
2148 case 6:
2149 if (pszValue != NULL)
2150 fprintf(phLog, "(%s)", pszValue);
2151 break;
2152
2153 default:
2154 i = 0;
2155 fprintf(phLog, "\n");
2156 }
2157
2158 if (stricmp(pszFieldName, "last") == 0)
2159 {
2160 i = 0;
2161 fprintf(phLog, "\n");
2162 }
2163
2164 pvUser = pvUser;
2165 return 0;
2166}
2167
2168
2169/**
2170 * Skips insignificant chars (spaces, new-lines and comments)
2171 * @returns pointer to new string posision. NULL if end of file.
2172 * @param papszLines Pointer to line table.
2173 * @param i Index into line table. (current line)
2174 * @param psz Pointer where to start (within the current line).
2175 */
2176static char *skipInsignificantChars(char **papszLines, int &i, char *psz)
2177{
2178 BOOL fComment = *psz == '/' && psz[1] == '*';
2179
2180 while (fComment || *psz == ' ' || *psz == '\0' || (*psz == '/' && psz[1] == '/'))
2181 {
2182 if (*psz == '\0' || (*psz == '/' && psz[1] == '/' && !fComment))
2183 {
2184 if ((psz = papszLines[++i]) == NULL)
2185 break;
2186 }
2187 else
2188 psz++;
2189
2190 if (fComment)
2191 {
2192 if (!(fComment = *psz != '*' || psz[1] != '/'))
2193 psz += 2;
2194 else
2195 continue;
2196 }
2197
2198 if ((fComment = *psz == '/' && psz[1] == '*') == TRUE)
2199 psz += 1 + (psz[2] != '*'); // don't add two when there is a possible end comment following.
2200 }
2201
2202 return psz;
2203}
2204
2205
2206/**
2207 * Reads a file into memory.
2208 * @returns Pointer to file. This should be freed using 'free' when processing
2209 * the file is not needed.
2210 * @param pszFilename Name of file to read.
2211 * @remark Current implementation reads the file as a binary file.
2212 */
2213static char *readFileIntoMemory(const char *pszFilename)
2214{
2215 char *pszFile = NULL;
2216 int cbFile = 0; /* don't have to initialized, but it removes gcc warning (release) */
2217 FILE *phFile;
2218
2219 phFile = fopen(pszFilename, "rb");
2220 if (phFile != NULL)
2221 {
2222 if (!fseek(phFile, 0, SEEK_END)
2223 && (cbFile = (int)ftell(phFile)) > 0
2224 && !fseek(phFile, 0, SEEK_SET)
2225 )
2226 {
2227 pszFile = (char*)malloc(cbFile + 1);
2228 if (pszFile != NULL)
2229 {
2230 #if 1
2231 memset((void*)pszFile, 0, cbFile + 1);
2232 if (fread((void*)pszFile, 1, cbFile, phFile) == 1)
2233 {
2234 free(pszFile);
2235 pszFile = NULL;
2236 }
2237 #else
2238 int cLines = 0;
2239 int cch = 0;
2240 char *psz = pszFile;
2241
2242 while (!feof(phFile) && cch < cbFile &&
2243 fgets(psz, cbFile - cch, phFile) != NULL)
2244 {
2245 int cchLine;
2246 cch += cchLine = strlen(psz);
2247 psz += cchLine;
2248 cLines++;
2249 }
2250
2251 /* error check */
2252 if (cch > cbFile || !feof(phFile))
2253 {
2254 free(pszFile);
2255 pszFile = NULL;
2256 }
2257 else
2258 *psz = '\0';
2259 #endif
2260 }
2261 }
2262 fclose(phFile);
2263 }
2264
2265 return pszFile;
2266}
2267
2268
2269/**
2270 * Creates an array of lines from a "memory" file. The last entry in the array contains NULL.
2271 * It also replaces '\t' with ' '.
2272 * @returns Pointer to the array of lines.
2273 * @param pszFile Pointer to "memory" file.
2274 */
2275static char **createLineArray(char *pszFile)
2276{
2277 char *psz = pszFile;
2278 char **papszLines = NULL;
2279 int cLines = 1;
2280
2281 while (*psz != '\0')
2282 {
2283 if (*psz == '\r')
2284 {
2285 if (psz[1] == '\n')
2286 psz++;
2287 cLines++;
2288 } else if (*psz == '\n')
2289 cLines++;
2290 psz++;
2291 }
2292 fprintf(phLog, "%d lines\n", cLines);
2293
2294 papszLines = (char**)calloc(cLines + 1, sizeof(char *));
2295 if (papszLines != NULL)
2296 {
2297 cLines = 1;
2298 papszLines[0] = psz = pszFile;
2299 while (*psz != '\0')
2300 {
2301 if (*psz == '\t')
2302 *psz = ' ';
2303 if (*psz == '\r')
2304 {
2305 if (psz[1] == '\n')
2306 *psz++ = '\0';
2307 papszLines[cLines++] = psz + 1;
2308 *psz = '\0';
2309 } else if (*psz == '\n')
2310 {
2311 *psz = '\0';
2312 papszLines[cLines++] = psz + 1;
2313 }
2314 psz++;
2315 }
2316 papszLines[cLines] = NULL; /* Strictly, this is not needed as we use calloc. */
2317 }
2318
2319
2320 return papszLines;
2321}
2322
2323
2324/** first char after word */
2325static char *findEndOfWord(const char *psz)
2326{
2327
2328 while (*psz != '\0' &&
2329 (
2330 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
2331 ||
2332 (*psz >= '0' && *psz <= '9')
2333 ||
2334 *psz == '_'
2335 )
2336 )
2337 ++psz;
2338 return (char *)psz;
2339}
2340
2341
2342/** starting char of word */
2343static char *findStartOfWord(const char *psz, const char *pszStart)
2344{
2345 const char *pszR = psz;
2346 while (psz >= pszStart &&
2347 (
2348 (*psz >= 'A' && *psz <= 'Z')
2349 || (*psz >= 'a' && *psz <= 'z')
2350 || (*psz >= '0' && *psz <= '9')
2351 || *psz == '_'
2352 )
2353 )
2354 pszR = psz--;
2355 return (char*)pszR;
2356}
2357
2358
2359/**
2360 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2361 * @returns Pointer to first not space or tab char in the string.
2362 * @param psz Pointer to the string which is to be trimmed.
2363 * @status completely implmented.
2364 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2365 */
2366inline char *trim(char *psz)
2367{
2368 int i;
2369 if (psz == NULL)
2370 return NULL;
2371 while (*psz == ' ' || *psz == '\t')
2372 psz++;
2373 i = strlen(psz) - 1;
2374 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2375 i--;
2376 psz[i+1] = '\0';
2377 return psz;
2378}
2379
2380
2381/**
2382 * Right trims a string, ie. removing spaces (and tabs) from the end of the string.
2383 * @returns Pointer to the string passed in.
2384 * @param psz Pointer to the string which is to be right trimmed.
2385 * @status completely implmented.
2386 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2387 */
2388inline char *trimR(char *psz)
2389{
2390 int i;
2391 if (psz == NULL)
2392 return NULL;
2393 i = strlen(psz) - 1;
2394 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2395 i--;
2396 psz[i+1] = '\0';
2397 return psz;
2398}
2399
2400
2401/**
2402 * skips blanks until first non-blank.
2403 * @returns Pointer to first not space or tab char in the string.
2404 * @param psz Pointer to the string.
2405 * @status completely implmented.
2406 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2407 */
2408inline char *skip(const char *psz)
2409{
2410 if (psz == NULL)
2411 return NULL;
2412
2413 while (*psz == ' ' || *psz == '\t')
2414 psz++;
2415 return (char*)psz;
2416}
2417
2418
2419/* copy: remove remarks, and unneeded spaces, ensuring no space after '(',
2420 * ensuring space after '*', ensuring no space before ',' and ')'.
2421 */
2422static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines)
2423{
2424 copy(psz, pszFrom - papszLines[iFrom], iFrom, pszTo - papszLines[iTo], iTo, papszLines);
2425}
2426
2427#if 0
2428static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
2429{
2430 char chPrev = '\n';
2431 int i, j;
2432 int fComment = 0;
2433
2434 i = iFrom;
2435 j = jFrom;
2436 while (i < iTo || (i == iTo && j <= jTo))
2437 {
2438 if (papszLines[i][j] != '\0'
2439 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
2440 )
2441 {
2442 /* copy char ? */
2443 if (!fComment)
2444 {
2445 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
2446 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
2447 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
2448 )
2449 {
2450 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
2451 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
2452 else
2453 {
2454 chPrev = *psz++ = papszLines[i][j];
2455 if (chPrev == '*') /* ensure ' ' after '*' */
2456 chPrev = *psz++ = ' ';
2457 }
2458 }
2459 }
2460 else
2461 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == 0)
2462 j++;
2463 j++;
2464 }
2465 else
2466 {
2467 /* next */
2468 j = 0;
2469 i++;
2470 if (chPrev != ' ' && chPrev != '(')
2471 chPrev = *psz++ = ' ';
2472 }
2473 }
2474 *psz = '\0';
2475}
2476
2477#else
2478/**
2479 * Copies a set of lines to a buffer (psz) removing precompiler lines and all comments.
2480 * @param psz
2481 * @param jFrom Starting position, index into line iFrom.
2482 * @param iFrom Starting position, index into papszLines.
2483 * @param jTo Ending position, index into line iFrom.
2484 * @param iTo Ending position, index into papszLines.
2485 * @param papszLines Array of lines.
2486 * @status completely implemented.
2487 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2488 */
2489static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
2490{
2491 char chPrev = '\n';
2492 int i, j;
2493 int fComment = FALSE;
2494 int fFirst = TRUE;
2495
2496 i = iFrom;
2497 j = jFrom;
2498 while (i < iTo || (i == iTo && j <= jTo))
2499 {
2500 if (papszLines[i][j] != '\0'
2501 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
2502 && !(!fComment && fFirst && papszLines[i][j] == '#')
2503 )
2504 {
2505 fFirst = papszLines[i][j] == ' ';
2506
2507 /* copy char ? */
2508 if (!fComment)
2509 {
2510 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
2511
2512 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
2513 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
2514 )
2515 {
2516 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
2517 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
2518 else
2519 {
2520 chPrev = *psz++ = papszLines[i][j];
2521 if (chPrev == '*') /* ensure ' ' after '*' */
2522 chPrev = *psz++ = ' ';
2523 }
2524 }
2525
2526 }
2527 else
2528 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == FALSE)
2529 j++;
2530 j++;
2531 }
2532 else
2533 {
2534 /* next */
2535 j = 0;
2536 fFirst = TRUE;
2537 i++;
2538 if (chPrev != ' ' && chPrev != '(')
2539 chPrev = *psz++ = ' ';
2540 }
2541 }
2542 *psz = '\0';
2543}
2544#endif
2545
2546
2547/* copyComment: Wrapper for the other copyComment function.
2548 *
2549 */
2550static void copyComment(char *psz, char *pszFrom, int iFrom, char **papszLines, BOOL fStrip, BOOL fHTML)
2551{
2552 copyComment(psz, pszFrom - papszLines[iFrom], iFrom, papszLines, fStrip, fHTML);
2553}
2554
2555
2556
2557
2558/**
2559 * Copies a set of lines to a buffer (psz) stopping at the first end comment.
2560 * (If a start comment occurs it is concidered an error.)
2561 * Stars begining lines are removed.
2562 * @param psz Target buffer.
2563 * @param jFrom Starting position, index into line iFrom.
2564 * @param iFrom Starting position, index into papszLines.
2565 * @param papszLines Array of lines.
2566 * @param fStrip Strip blank lines at start and end + LICENCE notice (at end).
2567 * @param fHTML Convert to HTML while copying.
2568 * @status completely implemented.
2569 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
2570 */
2571static void copyComment(char *psz, int jFrom, int iFrom, char **papszLines, BOOL fStrip, BOOL fHTML)
2572{
2573 char * pszStartBuffer = psz; /* Start of the target buffer. */
2574 char * pszStart = psz; /* Start of current line. */
2575 int fFirst = TRUE; /* True while we're still processing the start of the line. */
2576 int i, j; /* Indexes into papszLines. */
2577
2578 i = iFrom;
2579 j = jFrom;
2580 while (papszLines[i] != NULL && !(papszLines[i][j] == '*' && papszLines[i][j+1] == '/'))
2581 {
2582 if (papszLines[i][j] != '\0')
2583 {
2584 /* Skip or copy the char ? */
2585 if (fFirst && papszLines[i][j] == ' ')
2586 j++;
2587 else if (fFirst && papszLines[i][j] == '*')
2588 j += papszLines[i][j+1] == ' ' ? (fFirst = FALSE) + 2 : 1; /* bad habbit...*/
2589 else
2590 { /* Copy it */
2591 *psz++ = papszLines[i][j++];
2592 fFirst = FALSE;
2593 }
2594 }
2595 else
2596 { /* End of line:
2597 * Check if empty line. If so truncate it.
2598 * Add new line char if not empty first line...
2599 */
2600 j = 0; /* use this as an index from line start on the copied line. */
2601 while (pszStart + j < psz && pszStart[j] == ' ')
2602 j++;
2603 if (pszStart + j == psz)
2604 psz = pszStart;
2605 if (psz > pszStartBuffer || !fStrip)
2606 {
2607 if (fHTML)
2608 {
2609 *psz++ = '<';
2610 *psz++ = 'B';
2611 *psz++ = 'R';
2612 *psz++ = '>';
2613 }
2614 *psz++ = '\n';
2615 }
2616
2617 /* next */
2618 i++;
2619 j = 0;
2620 fFirst = TRUE;
2621 pszStart = psz;
2622 }
2623 }
2624 *psz = '\0';
2625
2626 /*
2627 * Strip end + license.
2628 */
2629 if (fStrip)
2630 {
2631 if (fHTML)
2632 {
2633 while (psz >= pszStartBuffer)
2634 {
2635 if (*psz == ' ' || *psz == '\n' || *psz == '\0')
2636 *psz-- = '\0';
2637 else if (psz - 4 >= pszStartBuffer && strncmp(psz - 4, "<BR>", 4) == 0)
2638 *(psz -= 4) = '\0';
2639 else
2640 break;
2641 }
2642 }
2643 while (psz >= pszStartBuffer && (*psz == ' ' || *psz == '\n' || *psz == '\0'))
2644 *psz-- = '\0';
2645
2646
2647 if (psz - 20 > pszStartBuffer && strstr(psz - 20, "LICENSE.TXT") != NULL)
2648 {
2649 while (psz >= pszStartBuffer && *psz != '\n')
2650 *psz-- = '\0';
2651 }
2652
2653 if (fHTML)
2654 {
2655 while (psz >= pszStartBuffer)
2656 {
2657 if (*psz == ' ' || *psz == '\n' || *psz == '\0')
2658 *psz-- = '\0';
2659 else if (psz - 4 >= pszStartBuffer && strncmp(psz - 4, "<BR>", 4) == 0)
2660 *(psz -= 4) = '\0';
2661 else
2662 break;
2663 }
2664 }
2665 while (psz >= pszStartBuffer && (*psz == ' ' || *psz == '\n' || *psz == '\0'))
2666 *psz-- = '\0';
2667 }
2668}
2669
2670
2671/**
2672 * Upcases a char.
2673 * @returns Upper case of the char given in ch.
2674 * @param ch Char to capitalize.
2675 */
2676#if 0
2677inline char upcase(char ch)
2678{
2679 return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch;
2680}
2681#else
2682#define upcase(ch) ((ch) >= 'a' && (ch) <= 'z' ? (char)((ch) - ('a' - 'A')) : (ch))
2683#endif
2684
2685
2686/**
2687 * Searches for a substring in a string.
2688 * @returns Pointer to start of substring when found, NULL when not found.
2689 * @param pszStr String to be searched.
2690 * @param pszSubStr String to be searched.
2691 * @remark Depends on the upcase function.
2692 */
2693static char *stristr(const char *pszStr, const char *pszSubStr)
2694{
2695 int cchSubStr = strlen(pszSubStr);
2696 int i = 0;
2697
2698 while (*pszStr != '\0' && i < cchSubStr)
2699 {
2700 i = 0;
2701 while (i < cchSubStr && pszStr[i] != '\0' &&
2702 (upcase(pszStr[i]) == upcase(pszSubStr[i])))
2703 i++;
2704 pszStr++;
2705 }
2706
2707 return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL);
2708}
2709
2710
2711
2712/**
2713 * Skips backwards until one of the chars in pszStopAt or star of file is reached.
2714 * @returns Pointer to end.
2715 * @param pszStopAt Array of chars which we should stop at.
2716 * @param pszFrom Start pointer.
2717 * @param iLine Reference to index to array of lines. input: start line, output: result line.
2718 * @param papszLines Array lines.
2719 * @sketch
2720 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2721 * @remark Comments are skipped.
2722 * No tests for strings ("...asdf").
2723 */
2724static char *skipBackwards(const char *pszStopAt, const char *pszFrom, int &iLine, char **papszLines)
2725{
2726 BOOL fComment = FALSE;
2727 int i = iLine;
2728 const char *psz = pszFrom;
2729
2730 while (i >= 0)
2731 {
2732 /* check for stop char */
2733 const char *psz1 = pszStopAt;
2734 while (*psz1 != '\0')
2735 if (*psz1++ == *psz)
2736 break;
2737 if (*psz1 != '\0')
2738 break;
2739
2740 /* comment check */
2741 if (!fComment && psz > papszLines[i] && *psz == '/' && psz[-1] == '*')
2742 fComment = TRUE;
2743 else if (fComment && *psz == '/' && psz[1] == '*')
2744 fComment = FALSE;
2745
2746 /* ok position to return? */
2747 if (!fComment)
2748 {
2749 iLine = i;
2750 pszFrom = psz;
2751 }
2752
2753 /* prev */
2754 if (psz > papszLines[i])
2755 psz--;
2756 else
2757 { /* try find line comment */
2758 do
2759 {
2760 char *pszStart = papszLines[--i];
2761 while (*pszStart == ' ')
2762 pszStart++;
2763 if (*pszStart != '\0' && *pszStart != '#'
2764 && !(*pszStart == '/' && pszStart[1] == '/'))
2765 { /* find '//' */
2766 pszStart = strstr(pszStart, "//");
2767 if (pszStart != NULL)
2768 psz = pszStart-1;
2769 else
2770 psz = papszLines[i] + strlen(papszLines[i]) - 1;
2771 break;
2772 }
2773 } while (i > 0);
2774 }
2775 }
2776
2777 return (char*)pszFrom;
2778}
2779
2780
2781/**
2782 * Finds a string in a range of pages.
2783 * @returns Index of the line (into ppaszLines).
2784 * @param psz String to search for.
2785 * @param iStart Start line.
2786 * @param iEnd Line to stop at
2787 * @param papszLines Array of lines to search within.
2788 * @status completely implemented.
2789 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2790 */
2791static int findStrLine(const char *psz, int iStart, int iEnd, char **papszLines)
2792{
2793 while (iStart <= iEnd &&
2794 stristr(papszLines[iStart], psz) == NULL)
2795 iStart++;
2796 return iStart;
2797}
2798
2799
2800
2801
Note: See TracBrowser for help on using the repository browser.