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

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

Corrections and handling of the updated field in function and file.

File size: 93.1 KB
Line 
1/* $Id: StateUpd.cpp,v 1.26 2000-07-21 21:09:43 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 */
897 FnDesc.lStatus = 99;
898
899 /* precondition: isFunction is true.
900 * brief algorithm:
901 * 1. Analyse function declaration.
902 * 2. Analyse function header.
903 * 3. Log data (for debug purpose).
904 * 4. Update database
905 */
906
907 /* 1.*/
908 ulRc = analyseFnDcl(&FnDesc, papszLines, i, iRet, pszFilename, pOptions);
909 if (0x0000ffff & ulRc) /* if low word is 0 the fatal */
910 {
911 unsigned long ulRcTmp;
912 //char szErrorDesc[2113]; /* due to some limitation in the latest EMX release size is 2112 and not 4096 as initially implemented. */
913 char *pszErrorDesc = (char*)malloc(20480);
914
915 /* 2.*/
916 ulRcTmp = analyseFnHdr(&FnDesc, papszLines, i, pszFilename, pOptions);
917 if (ulRcTmp == ~0UL) /* check for fatal error */
918 return (0xffff0000UL & ulRc) + 0x00010000UL;
919 ulRc += 0xffff0000UL & ulRcTmp;
920
921 /* 3.*/
922 fprintf(phLog, "Name: '%s' (refcodes=", FnDesc.pszName);
923 for (j = 0; j < FnDesc.cRefCodes; j++)
924 fprintf(phLog, j > 0 ? ", %ld" : "%ld", FnDesc.alRefCode[j]);
925 fprintf(phLog, ")\n");
926 fprintf(phLog, " Returns: '%s'\n", FnDesc.pszReturnType != NULL ? FnDesc.pszReturnType : "<missing>");
927 fprintf(phLog, " cParams: %2d\n", FnDesc.cParams);
928 for (j = 0; j < FnDesc.cParams; j++)
929 fprintf(phLog, " Param %2d: type '%s' %*s name '%s' description: %s\n", j, FnDesc.apszParamType[j],
930 max((int)(15 - strlen(FnDesc.apszParamType[j])), 0), "", FnDesc.apszParamName[j],
931 FnDesc.apszParamDesc[j] != NULL ? FnDesc.apszParamDesc[j] : "(null)");
932 fprintf(phLog, " Status: %ld - '%s'\n", FnDesc.lStatus, FnDesc.pszStatus != NULL ? FnDesc.pszStatus : "<missing>");
933 fprintf(phLog, " cAuthors: %2d\n", FnDesc.cAuthors);
934 for (j = 0; j < FnDesc.cAuthors; j++)
935 fprintf(phLog, " Author %d: '%s' (refcode=%ld)\n", j, FnDesc.apszAuthor[j], FnDesc.alAuthorRefCode[j]);
936
937 fprintf(phLog, " Description: %s\n", FnDesc.pszDescription != NULL ? FnDesc.pszDescription : "(null)");
938 fprintf(phLog, " Remark: %s\n", FnDesc.pszRemark != NULL ? FnDesc.pszRemark : "(null)");
939 fprintf(phLog, " Return Desc: %s\n", FnDesc.pszReturnDesc != NULL ? FnDesc.pszReturnDesc : "(null)");
940 fprintf(phLog, " Sketch: %s\n", FnDesc.pszSketch != NULL ? FnDesc.pszSketch : "(null)");
941 fprintf(phLog, " Equiv: %s\n", FnDesc.pszEquiv != NULL ? FnDesc.pszEquiv : "(null)");
942 fprintf(phLog, " Time: %s\n", FnDesc.pszTime != NULL ? FnDesc.pszTime : "(null)");
943 fprintf(phLog, "------------\n");
944
945 /* 4.*/
946 ulRcTmp = dbUpdateFunction(&FnDesc, pOptions->lDllRefcode, pszErrorDesc);
947 if (ulRcTmp != 0)
948 {
949 fprintf(phSignal, "%s,%s: %s\n", pszFilename, FnDesc.pszName, pszErrorDesc);
950 ulRc += ulRcTmp << 16;
951 }
952 free(pszErrorDesc);
953 }
954
955 return ulRc;
956}
957
958
959/**
960 * Analyses the function declaration.
961 * @returns high word = number of signals
962 * low word = number of APIs processed. (1 or 0).
963 * @param papszLines Array of lines in the file.
964 * @param i Index into papszLines.
965 * @param iRet Index into papszLines. Where to start searching again.
966 * @param pszFilename Filename used in the signal log.
967 * @param pOptions Pointer to options.
968 */
969static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
970 const char *pszFilename, POPTIONS pOptions)
971{
972 static long lPrevFnDll = -1L; /* fix for duplicate dlls */
973 unsigned long ulRc;
974 FNFINDBUF FnFindBuf;
975 long lFn = 0;
976
977 /* brief algorithm:
978 * 1. read function declaration using analyseFnDcl2.
979 * 2. apply name rules.
980 * 3. do a database lookup on the name.
981 * 3b. if more that one match, write a signal. (TODO: a simple fix is done, but there are holes.)
982 */
983
984 /* 1. */
985 ulRc = analyseFnDcl2(pFnDesc, papszLines, i, iRet, pszFilename, pOptions);
986 if (ulRc != 1)
987 return ulRc;
988
989 /* 2. */
990 if (pOptions->fOS2 && strncmp(pFnDesc->pszName, "OS2", 3) == 0)
991 pFnDesc->pszName += 3;
992 else if (pOptions->fCOMCTL32 && strncmp(pFnDesc->pszName, "COMCTL32", 8) == 0)
993 pFnDesc->pszName += 8;
994 else if (pOptions->fVERSION && strncmp(pFnDesc->pszName, "VERSION", 7) == 0)
995 pFnDesc->pszName += 7;
996 else if (pOptions->fOld)
997 pFnDesc->pszName += 3;
998
999 /* 3. */
1000 if (!dbFindFunction(pFnDesc->pszName, &FnFindBuf, pOptions->lDllRefcode))
1001 {
1002 fprintf(phSignal, "%s, %s: error occured while reading from database, %s\n",
1003 pszFilename, pFnDesc->pszName, dbGetLastErrorDesc());
1004 return 0x00010000;
1005 }
1006
1007 pFnDesc->cRefCodes = 0;
1008 if (FnFindBuf.cFns != 0)
1009 {
1010 if (pOptions->lDllRefcode < 0)
1011 {
1012 if (FnFindBuf.cFns > 1)
1013 {
1014 fprintf(phSignal, "%s: unknown dll and more than two occurences of this function!\n", pszFilename);
1015 return 0x00010000;
1016 }
1017 pOptions->lDllRefcode = FnFindBuf.alDllRefCode[0];
1018 fprintf(phLog, "DllRef = %d\n", pOptions->lDllRefcode);
1019 }
1020
1021 for (lFn = 0; lFn < FnFindBuf.cFns; lFn++)
1022 pFnDesc->alRefCode[pFnDesc->cRefCodes++] = FnFindBuf.alRefCode[lFn];
1023
1024 if (pFnDesc->cRefCodes == 0)
1025 fprintf(phLog, "%s was not an API in this dll(%d)!\n", pFnDesc->pszName, pOptions->lDllRefcode);
1026 }
1027 else
1028 fprintf(phLog, "%s was not an API\n", pFnDesc->pszName);
1029
1030 ulRc = pFnDesc->cRefCodes;
1031 return ulRc;
1032}
1033
1034
1035
1036/**
1037 * Analyses the function declaration.
1038 * No DB lockup or special function type stuff, only ODINFUNCTION is processed.
1039 * @returns high word = number of signals
1040 * low word = number of APIs processed. (1 or 0).
1041 * @param papszLines Array of lines in the file.
1042 * @param i Index into papszLines.
1043 * @param iRet Index into papszLines. Where to start searching again.
1044 * @param pszFilename Filename used in the signal log.
1045 * @param pOptions Pointer to options.
1046 */
1047static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
1048 const char *pszFilename, POPTIONS pOptions)
1049{
1050 /** @sketch
1051 * 1. find the '('
1052 * 2. find the word ahead of the '(', this is the function name.
1053 * 2a. class test.
1054 * 3. find the closing ')'
1055 * 4. copy the parameters, which is between the two '()'
1056 * 5. format the parameters
1057 */
1058
1059 int iFn, iP1, iP2, j, c;
1060 char * pszFn, *pszFnEnd, *pszP1, *pszP2;
1061 char * psz, *pszEnd;
1062 int cArgs;
1063 char * apszArgs[30];
1064 int iClass;
1065 char * pszClass, *pszClassEnd;
1066
1067 /* 1.*/
1068 iP1 = i;
1069 while (papszLines[iP1] != NULL
1070 && (pszP1 = strchr(papszLines[iP1], '(')) == NULL)
1071 iP1++;
1072 if (papszLines[iP1] == NULL)
1073 {
1074 fprintf(phSignal, "%d: oops! didn't find end of function!, %d\n", pszFilename, __LINE__);
1075 iRet = iP1+1;
1076 return 0x00010000;
1077 }
1078
1079 /* 2. */
1080 iFn = iP1;
1081 if (papszLines[iFn] != pszP1)
1082 pszFn = pszP1 - 1;
1083 else
1084 {
1085 pszFn = papszLines[--iFn];
1086 pszFn += strlen(pszFn) - (*pszFn != '\0');
1087 }
1088 while (iFn >= 0 && *pszFn == ' ')
1089 {
1090 if (pszFn != papszLines[iFn])
1091 pszFn--;
1092 else
1093 {
1094 pszFn = papszLines[--iFn];
1095 pszFn += strlen(pszFn) - (*pszFn != '\0');
1096 }
1097 }
1098 if (iFn < 0 || *pszFn == ' ' || *pszFn == '\0')
1099 {
1100 fprintf(phSignal, "%s: internal error!, %d\n", pszFilename, __LINE__);
1101 iRet = iP1+1;
1102 return 0x00010000;
1103 }
1104 pszFnEnd = pszFn;
1105 pszFn = findStartOfWord(pszFn, papszLines[iFn]);
1106
1107 /* 2a. */
1108 /* operators are not supported (BOOL kTime::operator > (const kTime &time) const) */
1109 if (pszFn > papszLines[iFn])
1110 {
1111 pszClassEnd = pszFn - 1;
1112 iClass = iFn;
1113 }
1114 else
1115 {
1116 pszClassEnd = pszFn - 1;
1117 iClass = iFn - 1;
1118 }
1119 c = 2;
1120 while (iClass >= 0 && c >= 0)
1121 {
1122 if (*pszClassEnd == ':')
1123 c--;
1124 else if (*pszClassEnd != ' ')
1125 break;
1126 pszClassEnd--;
1127 }
1128 if (*pszClassEnd != ' ' && c == 0)
1129 pszClass = findStartOfWord(pszClassEnd, papszLines[iClass]);
1130 else
1131 pszClass = pszClassEnd = NULL;
1132
1133 /* 3. */
1134 c = 1;
1135 iP2 = iP1;
1136 pszP2 = pszP1 + 1;
1137 while (c > 0)
1138 {
1139 if (*pszP2 == '(')
1140 c++;
1141 else if (*pszP2 == ')')
1142 if (--c == 0)
1143 break;
1144 if (*pszP2++ == '\0')
1145 if ((pszP2 = papszLines[++iP2]) == NULL)
1146 break;
1147 }
1148
1149 iRet = iP2 + 1; //assumes: only one function on a single line!
1150
1151 /* 4. */
1152 psz = pFnDesc->szFnDclBuffer;
1153 copy(pFnDesc->szFnDclBuffer, pszP1, iP1, pszP2, iP2, papszLines);
1154 pszEnd = psz + strlen(psz) + 1;
1155
1156 /* 5.*/
1157 cArgs = 0;
1158 if (stricmp(psz, "(void)") != 0 && strcmp(psz, "()") != 0 && strcmp(psz, "( )"))
1159 {
1160 char *pszC;
1161 pszC = trim(psz+1);
1162 c = 1;
1163 while (*pszC != '\0')
1164 {
1165 apszArgs[cArgs] = pszC;
1166 while (*pszC != ',' && c > 0 && *pszC != '\0')
1167 {
1168 if (*pszC == '(')
1169 c++;
1170 else if (*pszC == ')')
1171 if (--c == 0)
1172 break;
1173 pszC++;
1174 }
1175 *pszC = '\0';
1176 trim(apszArgs[cArgs++]);
1177
1178 /* next */
1179 pszC = trim(pszC + 1);
1180 }
1181 }
1182
1183 /* 6. */
1184 if (strnicmp(pszFn, "ODINFUNCTION", 12) == 0 || strnicmp(pszFn, "ODINPROCEDURE", 13) == 0)
1185 {
1186 BOOL fProc = strnicmp(pszFn, "ODINPROCEDURE", 13) == 0;
1187 j = 0;
1188 if (cArgs < (fProc ? 1 : 2))
1189 {
1190 fprintf(phSignal, "%s: Invalid ODINFUNCTION function too few parameters!\n", pszFilename);
1191 return 0x00010000;
1192 }
1193
1194 /* return type */
1195 if (fProc)
1196 pFnDesc->pszReturnType = "void";
1197 else
1198 pFnDesc->pszReturnType = apszArgs[j++];
1199
1200 /* function name */
1201 pFnDesc->pszName = apszArgs[j++];
1202
1203 /* arguments */
1204 pFnDesc->cParams = 0;
1205 while (j+1 < cArgs)
1206 {
1207 pFnDesc->apszParamType[pFnDesc->cParams] = apszArgs[j];
1208 pFnDesc->apszParamName[pFnDesc->cParams] = apszArgs[j+1];
1209 pFnDesc->cParams++;
1210 j += 2;
1211 }
1212 }
1213 else
1214 {
1215 /* return type */
1216 int iReturn = pszClass != NULL ? iClass : iFn;
1217 char * pszReturn = pszClass != NULL ? pszClass : pszFn;
1218
1219 if (pszReturn != papszLines[iReturn])
1220 pszReturn--;
1221 else
1222 {
1223 pszReturn = papszLines[--iReturn];
1224 pszReturn += strlen(pszReturn) - (*pszReturn != '\0');
1225 }
1226 pszReturn = skipBackwards("{};-+#:\"\'", pszReturn, iReturn, papszLines);
1227 *pszEnd = '\0';
1228 copy(pszEnd, pszReturn, iReturn, pszFn-1, iFn, papszLines);
1229 if (strlen(pszEnd) > 128)
1230 {
1231 /* FIXME LATER! Some constructors calling baseclass constructors "breaks" this rule. Win32MDIChildWindow in /src/user32/win32wmdichild.cpp for example. */
1232 fprintf(phSignal,"Fatal error? return statement is too larget. len=%d\n", strlen(pszEnd));
1233 fprintf(phLog, "Fatal error? return statement is too larget. len=%d\n", strlen(pszEnd));
1234 if (strlen(pszEnd) > 512)
1235 fprintf(stderr, "Fatal error? return statement is too larget. len=%d\n", strlen(pszEnd));
1236 fflush(phLog);
1237 fflush(phSignal);
1238 fflush(stderr);
1239 }
1240 pszEnd = trim(pszEnd);
1241 pFnDesc->pszReturnType = *pszEnd == '\0' ? NULL : pszEnd;
1242 pszEnd = strlen(pszEnd) + pszEnd + 1;
1243 *pszEnd = '\0';
1244
1245 /* !BugFix! some function occur more than once, usually as inline functions */
1246 if (pFnDesc->pszReturnType != NULL
1247 && strstr(pFnDesc->pszReturnType, "inline ") != NULL)
1248 {
1249 fprintf(phLog, "Not an API. Inlined functions can't be exported!\n");
1250 return 0;
1251 }
1252
1253 /* function name */
1254 if (pFnDesc->pszReturnType != NULL
1255 && (stristr(pFnDesc->pszReturnType, "cdecl") != NULL
1256 || strstr(pFnDesc->pszReturnType, "VFWAPIV") != NULL
1257 || strstr(pFnDesc->pszReturnType, "WINAPIV") != NULL
1258 )
1259 )
1260 { /* cdecl function is prefixed with an '_' */
1261 strcpy(pszEnd, "_");
1262 }
1263 if (pszClass != NULL)
1264 {
1265 strncat(pszEnd,pszClass, pszClassEnd - pszClass + 1);
1266 strcat(pszEnd, "::");
1267 }
1268 strncat(pszEnd, pszFn, pszFnEnd - pszFn + 1);
1269 pFnDesc->pszName = pszEnd;
1270 pszEnd = strlen(pszEnd) + pszEnd + 1;
1271 *pszEnd = '\0';
1272
1273
1274 /* arguments */
1275 pFnDesc->cParams = cArgs;
1276 for (j = 0; j < cArgs; j++)
1277 {
1278 int cch = strlen(apszArgs[j]);
1279 if ((psz = strchr(apszArgs[j], ')')) == NULL)
1280 { /* Common argument */
1281 if (apszArgs[j][cch-1] != '*' || (apszArgs[j][cch - 1] == ']' && cch < 5))
1282 { /* nearly Normal case, Type [moretype] Name.*/
1283 if (apszArgs[j][cch - 1] != ']')
1284 {
1285 if (strcmp(apszArgs[j], "...") != 0)
1286 { /* Normal case! */
1287 pFnDesc->apszParamName[j] = findStartOfWord(apszArgs[j] + cch - 1,
1288 apszArgs[j]);
1289 pFnDesc->apszParamName[j][-1] = '\0';
1290 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1291 }
1292 else
1293 { /* eliptic */
1294 pFnDesc->apszParamName[j] = "...";
1295 pFnDesc->apszParamType[j] = "";
1296 }
1297 }
1298 else
1299 { /* arg yet another special case! 'fn(int argc, char *argv[])' */
1300 char *pszP2;
1301 cch = strlen(apszArgs[j]);
1302 psz = &apszArgs[j][cch-2];
1303 while (psz > apszArgs[j] && ((*psz >= '0' && *psz <= '9') || *psz == ' '))
1304 psz--;
1305
1306 if (psz > apszArgs[j] && *psz == '[')
1307 {
1308 pszP2 = psz--;
1309 while (psz >= apszArgs[j] && *psz == ' ')
1310 psz--;
1311 }
1312
1313 if (psz <= apszArgs[j])
1314 { /* funny case - no name? */
1315 sprintf(pszEnd, "arg%i", j);
1316 pFnDesc->apszParamName[j] = pszEnd;
1317 pszEnd = strlen(pszEnd) + pszEnd + 1;
1318 *pszEnd = '\0';
1319 }
1320 else
1321 { /* *pszP2 = '[' and psz = end of name */
1322 psz = findStartOfWord(psz, apszArgs[j]);
1323 strncat(pszEnd, psz, pszP2 - psz);
1324 pFnDesc->apszParamName[j] = pszEnd;
1325 pszEnd = strlen(pszEnd) + pszEnd + 1;
1326 *pszEnd = '\0';
1327 memset(psz, ' ', pszP2 - psz);
1328 }
1329 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1330 }
1331 }
1332 else
1333 { /* No argument name only type - make a dummy one: 'arg[1..n]' */
1334 sprintf(pszEnd, "arg%i", j);
1335 pFnDesc->apszParamName[j] = pszEnd;
1336 pszEnd = strlen(pszEnd) + pszEnd + 1;
1337 *pszEnd = '\0';
1338 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1339 }
1340 }
1341 else
1342 { /* Function pointer argument... */
1343 char *pszP2 = psz;
1344 psz = findStartOfWord(psz-1, apszArgs[j]);
1345 strncat(pszEnd, psz, pszP2 - psz);
1346
1347 pFnDesc->apszParamName[j] = pszEnd;
1348 memset(psz, ' ', pszP2 - psz);
1349 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
1350
1351 pszEnd = strlen(pszEnd) + pszEnd + 1;
1352 *pszEnd = '\0';
1353 }
1354 }
1355 }
1356 pOptions = pOptions;
1357 return 0x00000001;
1358}
1359
1360
1361/**
1362 * Analyses the function header.
1363 * @returns high word = number of signals
1364 * low word = number of APIs processed. (1 or 0).
1365 * @param papszLines Array of lines in the file.
1366 * @param i Index into papszLines.
1367 * @param pszFilename Filename used in the signal log.
1368 * @param pOptions Pointer to options.
1369 * @sketch 0. initiate pFnDesc struct
1370 * 1. Search backwards (start at i-1) for a comment or code.
1371 * 2. If comment: (else fail)
1372 * 2a. find start and end of comment.
1373 * 2b. check for function header characteristics
1374 * - lots of '*'s.
1375 * - fields 'Status', 'Author' and 'Name'
1376 * or if SDS, check for:
1377 * - at least two '*'s at the begining.
1378 * - fields '@status' and/or '@author'
1379 * 2c. check that content of the 'Name' field matches (not SDS)
1380 * 2d. read the status and author fields.
1381 * @remark Supports both types of function headers, Odin32-common and SDS.
1382 */
1383static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions)
1384{
1385 int iStatus, iAuthor, iName, iStart, iEnd;
1386 int j, jStart;
1387 int fFound;
1388 int fSDS = 0;
1389 char *psz, *pszB;
1390 char *pszAuthors = NULL;
1391 unsigned long ulRc = 0x00000001;
1392
1393 pOptions = pOptions;
1394
1395 if (i < 0) /* parameter validation */
1396 return 0;
1397
1398 /*
1399 * 0. initiate pFnDesc struct
1400 */
1401 for (j = 0; j < pFnDesc->cParams; j++)
1402 pFnDesc->apszParamDesc[j] = NULL;
1403 pFnDesc->cAuthors = 0;
1404 pFnDesc->pszDescription = pFnDesc->pszRemark = pFnDesc->pszReturnDesc = NULL;
1405 pFnDesc->pszSketch = pFnDesc->pszEquiv = pFnDesc->pszTime = pFnDesc->pszStatus = NULL;
1406 pFnDesc->lStatus = 99; /* unknown */
1407
1408 /*
1409 * 1. + 2a.
1410 */
1411 iEnd = i-1; /* find end */
1412 j = strlen(papszLines[iEnd]);
1413 j -= j > 0 ? 1 : 0;
1414 fFound = 0;
1415 while (iEnd >= 0 && i - iEnd < 7 && papszLines[iEnd][j] != '}' &&
1416 !(fFound = (papszLines[iEnd][j] == '*' && papszLines[iEnd][j+1] == '/')))
1417 if (j-- == 0)
1418 if (iEnd-- > 0)
1419 {
1420 j = strlen(papszLines[iEnd]);
1421 j -= j > 0 ? 1 : 0;
1422 }
1423 if (!fFound) /* fail if not found */
1424 return 0;
1425
1426 iStart = iEnd; /* find start */
1427 if (j < 2)
1428 j -= (j = strlen(papszLines[--iStart])) > 1 ? 2 : j;
1429 else
1430 j -= 2;
1431 fFound = 0;
1432 while (iStart >= 0
1433 && (j < 0
1434 || !(fFound = (papszLines[iStart][j] == '/' && papszLines[iStart][j+1] == '*'))
1435 )
1436 )
1437 if (j-- <= 0)
1438 if (iStart-- > 0)
1439 {
1440 j = strlen(papszLines[iStart]);
1441 j -= j > 1 ? 2 : j;
1442 }
1443
1444 if (!fFound) /* fail if not found */
1445 return 0;
1446 jStart = j;
1447
1448 /*
1449 * 2b.
1450 */
1451 if (strncmp(&papszLines[iStart][jStart], "/**", 3) != 0) /* checks that there are more than one star at start of comment */
1452 return 0;
1453
1454 /* Odin32 common? */
1455 iName = findStrLine("* Name", iStart, iEnd, papszLines);
1456 iStatus = findStrLine("* Status", iStart, iEnd, papszLines);
1457 iAuthor = findStrLine("* Author", iStart, iEnd, papszLines);
1458
1459 if (!(iName <= iEnd || iStatus <= iEnd || iAuthor <= iEnd))
1460 {
1461 /* SDS ? */
1462 iStatus = findStrLine("@status", iStart, iEnd, papszLines);
1463 iAuthor = findStrLine("@author", iStart, iEnd, papszLines);
1464 iName = iEnd + 1;
1465
1466 if (!(iStatus <= iEnd || iAuthor <= iEnd))
1467 return 0;
1468 fSDS = TRUE;
1469 }
1470 else
1471 {
1472 /* 2c.*/
1473 if (iName <= iEnd && strstr(papszLines[iName], pFnDesc->pszName) == NULL)
1474 fprintf(phLog, "Warning: a matching function name is not found in the name Field\n");
1475 }
1476
1477 /* 2d.*/
1478 pszB = &pFnDesc->szFnHdrBuffer[0];
1479 if (!fSDS)
1480 {
1481 /*
1482 * Odin32 common styled header
1483 */
1484
1485 /* Author(s) */
1486 pszAuthors = CommonCopyTextUntilNextTag(pszB, FALSE, iAuthor, iEnd, papszLines);
1487 pszB += strlen(pszB) + 1;
1488
1489 /* Status */
1490 pFnDesc->pszStatus = CommonCopyTextUntilNextTag(pszB, FALSE, iStatus, iEnd, papszLines);
1491 pszB += strlen(pszB) + 1;
1492
1493 /* Remark */
1494 i = findStrLine("* Remark", iStart, iEnd, papszLines);
1495 pFnDesc->pszRemark = CommonCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1496 pszB += strlen(pszB) + 1;
1497
1498 /* Description */
1499 i = findStrLine("* Purpose", iStart, iEnd, papszLines);
1500 pFnDesc->pszDescription = CommonCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1501 pszB += strlen(pszB) + 1;
1502
1503 /* Return(result) description */
1504 i = findStrLine("* Result", iStart, iEnd, papszLines);
1505 pFnDesc->pszReturnDesc = CommonCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1506 pszB += strlen(pszB) + 1;
1507
1508 /* FIXME parameters */
1509
1510 }
1511 else
1512 {
1513 /*
1514 * SDS styled header
1515 */
1516
1517 /* author */
1518 pszAuthors = SDSCopyTextUntilNextTag(pszB, FALSE, iAuthor, iEnd, papszLines);
1519 pszB += strlen(pszB) + 1;
1520
1521 /* status */
1522 pFnDesc->pszReturnDesc = SDSCopyTextUntilNextTag(pszB, FALSE, iStatus, iEnd, papszLines);
1523 pszB += strlen(pszB) + 1;
1524
1525 /* remark */
1526 i = findStrLine("@remark", iStart, iEnd, papszLines);
1527 pFnDesc->pszRemark = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1528 pszB += strlen(pszB) + 1;
1529
1530 /* description */
1531 i = findStrLine("@desc", iStart, iEnd, papszLines);
1532 pFnDesc->pszDescription = SDSCopyTextUntilNextTag(pszB, TRUE, i > iEnd ? iStart : i, iEnd, papszLines);
1533 pszB += strlen(pszB) + 1;
1534
1535 /* sketch */
1536 i = findStrLine("@sketch", iStart, iEnd, papszLines);
1537 pFnDesc->pszSketch = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1538 pszB += strlen(pszB) + 1;
1539
1540 /* return description */
1541 i = findStrLine("@return", iStart, iEnd, papszLines);
1542 pFnDesc->pszReturnDesc = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1543 pszB += strlen(pszB) + 1;
1544
1545 /* time */
1546 i = findStrLine("@time", iStart, iEnd, papszLines);
1547 pFnDesc->pszReturnDesc = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1548 pszB += strlen(pszB) + 1;
1549
1550 /* equiv */
1551 i = findStrLine("@equiv", iStart, iEnd, papszLines);
1552 pFnDesc->pszReturnDesc = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1553 pszB += strlen(pszB) + 1;
1554
1555 /* Set parameter descriptions to NULL */
1556 for (i = 0; i < pFnDesc->cParams; i++)
1557 pFnDesc->apszParamDesc[i] = NULL;
1558
1559 /*
1560 * parameters, new @param for each parameter!
1561 */
1562 do
1563 {
1564 char *pszParam;
1565
1566 /* find first */
1567 i = findStrLine("@param", iStart, iEnd, papszLines);
1568 if (i >= iEnd)
1569 break;
1570
1571 /* Get parameter name - first word */
1572 pszParam = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines);
1573 if (pszParam != NULL)
1574 {
1575 /* first word in psz is the parameter name! */
1576 psz = findEndOfWord(pszParam);
1577 *psz++ = '\0';
1578
1579 /* find parameter */
1580 for (j = 0; j < pFnDesc->cParams; j++)
1581 if (strcmp(pFnDesc->apszParamName[j], pszParam) == 0)
1582 break;
1583 if (j < pFnDesc->cParams)
1584 {
1585 /* find end of parameter name */
1586 psz = findEndOfWord(strstr(papszLines[i], pszParam));
1587 /* copy from end of parameter name */
1588 pFnDesc->apszParamDesc[j] = SDSCopyTextUntilNextTag(pszB, TRUE, i, iEnd, papszLines, psz);
1589 pszB += strlen(pszB) + 1;
1590 }
1591 else
1592 { /* signal that parameter name wasn't found! */
1593 fprintf(phSignal, "%s, %s: '%s' is not a valid parameter.\n",
1594 pszFilename, pFnDesc->pszName, pszParam);
1595 ulRc += 0x00010000;
1596 }
1597 }
1598 } while (i > iEnd);
1599 }
1600
1601 /*
1602 * Status - refcodes are hardcoded here!
1603 */
1604 if (pFnDesc->pszStatus != NULL && *pFnDesc->pszStatus != '\0')
1605 {
1606 if (strstr(pFnDesc->pszStatus, "STUB") != NULL || *pFnDesc->pszStatus == '1')
1607 pFnDesc->lStatus = 1; /* STUB */
1608 else if (stristr(pFnDesc->pszStatus, "Partially") != NULL || *pFnDesc->pszStatus == '2' || *pFnDesc->pszStatus == '3')
1609 {
1610 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '2')
1611 pFnDesc->lStatus = 2; /* STUB */
1612 else
1613 pFnDesc->lStatus = 3; /* STUB */
1614 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1615 || *pFnDesc->pszStatus == '5' || *pFnDesc->pszStatus == '7')
1616 pFnDesc->lStatus += 4;
1617 }
1618 else if (stristr(pFnDesc->pszStatus, "Completely") != NULL || *pFnDesc->pszStatus == '4' || *pFnDesc->pszStatus == '5')
1619 {
1620 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '4')
1621 pFnDesc->lStatus = 4; /* STUB */
1622 else
1623 pFnDesc->lStatus = 5; /* STUB */
1624 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1625 || *pFnDesc->pszStatus == '8' || *pFnDesc->pszStatus == '9')
1626 pFnDesc->lStatus += 4;
1627 }
1628 else
1629 {
1630 fprintf(phSignal, "%s, %s: '%s' is not a valid status code.\n",
1631 pszFilename, pFnDesc->pszName, pFnDesc->pszStatus);
1632 ulRc += 0x00010000;
1633 }
1634 }
1635
1636 /*
1637 * Author
1638 */
1639 if (pszAuthors != NULL)
1640 { /* author1, author2, author3 */
1641 j = 0;
1642 psz = trim(pszAuthors);
1643 pFnDesc->cAuthors = 0;
1644 while (*psz != '\0' && pFnDesc->cAuthors < (int)(sizeof(pFnDesc->apszAuthor) / sizeof(pFnDesc->apszAuthor[0])))
1645 {
1646 char *pszBr1 = strchr(psz, '[');
1647 char *pszBr2 = strchr(psz, ']');
1648 char *pszEmail = NULL;
1649 char *pszNext;
1650
1651 /* remove '[text]' text - this is usualy used for time/date */
1652 if (pszBr1 != NULL && pszBr2 != NULL && pszBr1 < pszBr2)
1653 while (pszBr1 <= pszBr2)
1654 *pszBr1++ = ' ';
1655
1656 /* terminate string */
1657 pszNext = strchr(psz, ',');
1658 if (pszNext == NULL)
1659 pszNext = strchr(psz, '\n');
1660 if (pszNext != NULL)
1661 *pszNext = '\0';
1662
1663 /* check for (email) test */
1664 pszBr1 = strchr(psz, '(');
1665 pszBr2 = strchr(psz, ')');
1666 if (pszBr1 != NULL || pszBr2 != NULL)
1667 {
1668 if (pszBr1 != NULL)
1669 *pszBr1++ = '\0';
1670 if (pszBr2 != NULL)
1671 *pszBr2++ = '\0';
1672
1673 if (pszBr1 != NULL && pszBr1 < pszBr2)
1674 pszEmail = trim(pszBr1 + 1);
1675 }
1676
1677 pFnDesc->apszAuthor[pFnDesc->cAuthors] = trim(psz);
1678 pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] =
1679 dbFindAuthor(pFnDesc->apszAuthor[pFnDesc->cAuthors], pszEmail);
1680
1681 if (pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] == -1)
1682 {
1683 fprintf(phSignal, "%s, %s: author '%s' is not recognized.\n", pszFilename, pFnDesc->pszName,
1684 pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1685 ulRc += 0x00010000;
1686 }
1687
1688 /* next */
1689 pFnDesc->cAuthors++;
1690 psz = pszNext ? skip(pszNext + 1) : "";
1691 }
1692 }
1693
1694 return ulRc;
1695}
1696
1697
1698
1699/**
1700 * Copies a piece of tag/keyword text into an buffer. SDS.
1701 * @returns Pointer to buffer. If iStart > iEnd NULL is returned.
1702 * @param pszTarget Pointer to buffer.
1703 * @param fHTML Add HTML tags like <br> or not
1704 * @param iStart Index of start line.
1705 * @param iEnd Index of last line.
1706 * @param apszLines Array lines.
1707 * @param pszStart Pointer to char to start at (into papszLines[iStart] of course!)
1708 * @status completely impelmented.
1709 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1710 * @remark Addes some HTML tags.
1711 */
1712static char *SDSCopyTextUntilNextTag(char *pszTarget, BOOL fHTML, int iStart, int iEnd, char **papszLines, const char *pszStart)
1713{
1714 char *pszRet = pszTarget;
1715
1716 if (iStart <= iEnd)
1717 {
1718 int iStartColumn;
1719 const char *psz = pszStart != NULL ? pszStart : papszLines[iStart];
1720
1721 /*
1722 * skip dummy chars.
1723 */
1724 while (iStart <= iEnd && (*psz == ' ' || *psz == '/' || *psz == '*' || *psz == '\0' ))
1725 {
1726 if (*psz == '\0')
1727 psz = papszLines[++iStart];
1728 else
1729 psz++;
1730 }
1731
1732 /* Anything left of the area to copy? */
1733 if (iStart <= iEnd)
1734 {
1735 /*
1736 * if we stand at a tag, skip over the tag
1737 */
1738 if (*psz == '@')
1739 psz = skip(findEndOfWord(psz+1));
1740
1741 /*
1742 * save start columen
1743 */
1744 iStartColumn = psz - papszLines[iStart];
1745
1746 /*
1747 * Copy loop.
1748 */
1749 do
1750 {
1751 int i;
1752 for (i = psz - papszLines[iStart]; i < iStartColumn; i++)
1753 *pszTarget++ = ' '; /* FIXME HTML and indenting... */
1754
1755 strcpy(pszTarget, psz);
1756 trimR(pszTarget);
1757 pszTarget += strlen(pszTarget);
1758 if (fHTML)
1759 strcpy(pszTarget, "<BR>\n");
1760 else
1761 strcpy(pszTarget, "\n");
1762 pszTarget += strlen(pszTarget);
1763
1764 /* Next */
1765 psz = skip(papszLines[++iStart]);
1766 if (iStart <= iEnd)
1767 {
1768 while (psz != NULL && *psz == '*')
1769 ++psz;
1770 psz = skip(psz);
1771 /* end test comment */
1772 if (psz > papszLines[iStart] && psz[-1] == '*' && *psz == '/')
1773 break;
1774 }
1775 } while (iStart <= iEnd && *psz != '@');
1776
1777 /*
1778 * remove empty lines at end.
1779 */
1780 if (fHTML)
1781 {
1782 pszTarget--;
1783 while (pszTarget >= pszRet && (*pszTarget == '\n' || *pszTarget == ' '))
1784 {
1785 if (*pszTarget == '\n')
1786 pszTarget -= 4;
1787 *pszTarget-- = '\0';
1788 }
1789 }
1790 else
1791 {
1792 pszTarget--;
1793 while (pszTarget >= pszRet && (*pszTarget == '\n' || *pszTarget == ' '))
1794 *pszTarget-- = '\0';
1795 }
1796 }
1797 else
1798 pszTarget = '\0';
1799 }
1800 else
1801 pszRet = NULL;
1802
1803 return pszRet != NULL && *pszRet == '\0' ? NULL : pszRet;
1804}
1805
1806
1807
1808/**
1809 * Copies a piece of tag/keyword text into an buffer. SDS.
1810 * @returns Pointer to buffer. If iStart > iEnd NULL is returned.
1811 * @param pszTarget Pointer to buffer.
1812 * @param fHTML Add HTML tags like <br> or not
1813 * @param iStart Index of start line.
1814 * @param iEnd Index of last line.
1815 * @param apszLines Array lines.
1816 * @param pszStart Pointer to char to start at (into papszLines[iStart] of course!)
1817 * @status completely impelmented.
1818 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1819 * @remark Addes some HTML tags.
1820 */
1821static char *CommonCopyTextUntilNextTag(char *pszTarget, BOOL fHTML, int iStart, int iEnd, char **papszLines, const char *pszStart)
1822{
1823 char *pszRet = pszTarget;
1824
1825 if (iStart <= iEnd)
1826 {
1827 /* known keywords */
1828 int iStartColumn;
1829 int i;
1830 const char *psz = pszStart != NULL ? pszStart : papszLines[iStart];
1831
1832 /*
1833 * Skip evt. keyword
1834 */
1835 psz = skip(psz);
1836 for (i = 0; pszCommonKeywords[i] != NULL; i++)
1837 if (strnicmp(pszCommonKeywords[i], psz, strlen(pszCommonKeywords[i])) == 0)
1838 {
1839 psz = skip(psz + strlen(pszCommonKeywords[i]));
1840 psz = skip(*psz == ':' ? psz + 1 : psz);
1841 break;
1842 }
1843
1844 /*
1845 * save start columen
1846 */
1847 iStartColumn = psz - papszLines[iStart];
1848
1849 /*
1850 * copy loop
1851 */
1852 do
1853 {
1854 /*
1855 * Skip '*'s at start of the line.
1856 */
1857 while (*psz == '*')
1858 psz++;
1859
1860 /* end comment check */
1861 if (psz > papszLines[iStart] && psz[-1] == '*' && *psz == '/')
1862 break;
1863 psz = skip(psz);
1864
1865 /*
1866 * Indent up to iStartColumn
1867 */
1868 for (i = psz - papszLines[iStart]; i < iStartColumn; i++)
1869 *pszTarget++ = ' '; /* FIXME HTML and indenting... */
1870
1871 /*
1872 * Copy the rest of the line and add HTML break.
1873 */
1874 strcpy(pszTarget, psz);
1875 trimR(pszTarget);
1876 pszTarget += strlen(pszTarget);
1877 if (fHTML)
1878 strcpy(pszTarget, "<BR>\n");
1879 else
1880 strcpy(pszTarget, "\n");
1881 pszTarget += strlen(pszTarget);
1882
1883 /*
1884 * Next
1885 */
1886 psz = skip(papszLines[++iStart]);
1887
1888 /*
1889 * Check for keyword
1890 */
1891 if (iStart <= iEnd)
1892 {
1893 psz = skip(psz);
1894 for (i = 0; pszCommonKeywords[i] != NULL; i++)
1895 if (strnicmp(pszCommonKeywords[i], psz, strlen(pszCommonKeywords[i])) == 0)
1896 break;
1897 if (pszCommonKeywords[i] != NULL)
1898 break;
1899 }
1900 } while (iStart < iEnd);
1901
1902 /*
1903 * remove empty lines at end.
1904 */
1905 if (fHTML)
1906 {
1907 pszTarget--;
1908 while (pszTarget >= pszRet && (*pszTarget == '\n' || *pszTarget == ' '))
1909 {
1910 if (*pszTarget == '\n')
1911 pszTarget -= 4;
1912 *pszTarget-- = '\0';
1913 }
1914 }
1915 else
1916 {
1917 pszTarget--;
1918 while (pszTarget >= pszRet && (*pszTarget == '\n' || *pszTarget == ' '))
1919 *pszTarget-- = '\0';
1920 }
1921 }
1922 else
1923 pszRet = NULL;
1924
1925 return pszRet != NULL && *pszRet == '\0' ? NULL : pszRet;
1926}
1927
1928
1929
1930/**
1931 * Checks if there may be an function starting at the current line.
1932 * @returns TRUE if API found, else FALSE.
1933 * @param papszLines Array of lines in the file.
1934 * @param i Index into papszLines.
1935 * @param pOptions Pointer to options.
1936 */
1937static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions)
1938{
1939 if (!pOptions->fOld)
1940 { /* new API naming style */
1941 /* brief algorithm:
1942 * check that line don't start with '\\', '{' or '}'
1943 * search for '('.
1944 * if found then do
1945 * find c-word previous to '('
1946 * if found then do
1947 * check that the following condition are true:
1948 * 1. word is not 'for', 'while', 'do', 'if', 'else' or 'switch'
1949 * 2. first significant char after '(' should not be a '*'. (Fix for functionnames in function header, "BackupRead(".)
1950 * 3. find the matching ')' and check that the first significant char after it is '{'.
1951 * if 1, 2 and 3 are all true return true
1952 * return false.
1953 *
1954 * Note, for 2: spaces, newlines and comments are ignored, all other chars are significant.
1955 */
1956 char *pszP1 = papszLines[i];
1957
1958 while (*pszP1 != '\0' && *pszP1 == ' ')
1959 pszP1++;
1960 if (*pszP1 != '\0' && *pszP1 != '/' && pszP1[1] != '/'
1961 && *pszP1 != '{' && *pszP1 != '}')
1962 {
1963 pszP1 = strchr(papszLines[i], '(');
1964 if (pszP1 != NULL && pszP1 >= papszLines[i])
1965 {
1966 int cchFnName = 0;
1967 char *pszFnName = pszP1 - 1;
1968
1969 while (pszFnName - cchFnName > papszLines[0] && pszFnName[cchFnName] == ' ')
1970 cchFnName--, pszFnName--;
1971
1972 pszFnName = findStartOfWord(pszFnName, papszLines[0]);
1973 cchFnName += pszP1 - pszFnName;
1974 if (cchFnName >= 0)
1975 {
1976 /* 1. */
1977 if (
1978 strncmp(pszFnName, "for", cchFnName) != 0 &&
1979 strncmp(pszFnName, "while", cchFnName) != 0 &&
1980 strncmp(pszFnName, "do", cchFnName) != 0 &&
1981 strncmp(pszFnName, "if", cchFnName) != 0 &&
1982 strncmp(pszFnName, "else", cchFnName) != 0 &&
1983 strncmp(pszFnName, "switch", cchFnName) != 0
1984 )
1985 {
1986 /* 2. */
1987 int j = i;
1988 char *psz = skipInsignificantChars(papszLines, j, pszP1+1);
1989 if (psz != NULL && *psz != '*')
1990 {
1991 char *pszB = pszP1 + 1; /*'{'*/
1992 int c = 1;
1993
1994 /* 3. */
1995 while (c > 0)
1996 {
1997 if (*pszB == '(')
1998 c++;
1999 else if (*pszB == ')')
2000 if (--c == 0)
2001 break;
2002 if (*pszB++ == '\0')
2003 if ((pszB = papszLines[++i]) == NULL)
2004 break;
2005 }
2006 if (pszB != NULL)
2007 {
2008 pszB = skipInsignificantChars(papszLines, i, pszB+1);
2009 if (pszB != NULL && *pszB == '{')
2010 {
2011 fprintf(phLog, "Function found: %.*s\n", cchFnName, pszFnName);
2012 return TRUE;
2013 }
2014 /* FIXME: constructors with ':' afterwards are not supported !TODO! */
2015 }
2016 }
2017 }
2018 }
2019 }
2020 }
2021 }
2022 else
2023 { /* old API naming style */
2024 char *pszOS2;
2025
2026 /* brief algorithm:
2027 * search for function prefix, 'OS2'.
2028 * if found then do
2029 * check that the following condition are true:
2030 * 1. char before 'OS2' is either start-of-line (no char), space or '*'.
2031 * 2. first significant char after the 'OS2' prefixed word is a '('.
2032 * 3. find the matching ')' and check that the first significant char after it is '{'.
2033 * if 1,2 and 3 all are true return true
2034 * return false.
2035 *
2036 * Note, for 2 and 3 spaces, newlines and comments are ignored, all other chars are significant.
2037 */
2038 pszOS2 = strstr(papszLines[i], "OS2");
2039 if (pszOS2 != NULL)
2040 {
2041 /* 1.*/
2042 if (pszOS2 == papszLines[i] || pszOS2[-1] == ' ' || pszOS2[-1] == '*')
2043 {
2044 char *pszP1; /*'('*/
2045 int cchFnName;
2046
2047 /* 2. */
2048 pszP1 = findEndOfWord(pszOS2);
2049 cchFnName = pszP1 - pszOS2;
2050 pszP1 = skipInsignificantChars(papszLines, i, pszP1);
2051
2052 if (pszP1 != NULL && *pszP1 == '(')
2053 {
2054 char *pszB = pszP1 + 1; /*'{'*/
2055 int c = 1;
2056
2057 /* 3. */
2058 while (c > 0)
2059 {
2060 if (*pszB == '(')
2061 c++;
2062 else if (*pszB == ')')
2063 if (--c == 0)
2064 break;
2065 if (*pszB++ == '\0')
2066 if ((pszB = papszLines[++i]) == NULL)
2067 break;
2068 }
2069 if (pszB != NULL)
2070 {
2071 pszB = skipInsignificantChars(papszLines, i, pszB+1);
2072 if (pszB != NULL && *pszB == '{')
2073 {
2074 fprintf(phLog, "Possible API: %.*s\n", cchFnName, pszOS2);
2075 return TRUE;
2076 }
2077 }
2078 }
2079 }
2080 }
2081 }
2082
2083 return FALSE;
2084}
2085
2086
2087/**
2088 * Checks if there may be a design note starting at the current line.
2089 * @returns TRUE if design note found, else FALSE.
2090 * @param papszLines Array of lines in the file.
2091 * @param i Index into papszLines.
2092 * @param pOptions Pointer to options.
2093 */
2094static BOOL isDesignNote(char **papszLines, int i, POPTIONS pOptions)
2095{
2096 char *psz = papszLines[i];
2097
2098 if (psz == NULL)
2099 return FALSE;
2100
2101 // look for /**@designnote "
2102 while (*psz == ' ')
2103 psz++;
2104 if (strncmp(psz, "/**", 3) == 0)
2105 {
2106 psz++;
2107 while (*psz == '*' || *psz == ' ')
2108 psz++;
2109 return strnicmp(psz, "@designnote ", 12) == 0;
2110 }
2111 pOptions = pOptions;
2112 return FALSE;
2113}
2114
2115
2116
2117
2118/**
2119 * Callback function for the dbGetNotUpdatedFunction routine.
2120 *
2121 */
2122static long _System dbNotUpdatedCallBack(const char *pszValue, const char *pszFieldName, void *pvUser)
2123{
2124 static i = 0;
2125 switch (i++)
2126 {
2127 case 0:
2128 fprintf(phLog, "%s", pszValue);
2129 break;
2130 case 1:
2131 fprintf(phLog, "(%s)", pszValue);
2132 break;
2133 case 2: /* updated */
2134 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
2135 break;
2136 case 3: /* aliasfn */
2137 fprintf(phLog, " %s=%s", pszFieldName, pszValue);
2138 break;
2139 case 4:
2140 if (pszValue != NULL)
2141 fprintf(phLog, " --> %s.", pszValue);
2142 break;
2143 case 5:
2144 if (pszValue != NULL)
2145 fprintf(phLog, "%s", pszValue);
2146 break;
2147 case 6:
2148 if (pszValue != NULL)
2149 fprintf(phLog, "(%s)", pszValue);
2150 break;
2151
2152 default:
2153 i = 0;
2154 fprintf(phLog, "\n");
2155 }
2156
2157 if (stricmp(pszFieldName, "last") == 0)
2158 {
2159 i = 0;
2160 fprintf(phLog, "\n");
2161 }
2162
2163 pvUser = pvUser;
2164 return 0;
2165}
2166
2167
2168/**
2169 * Skips insignificant chars (spaces, new-lines and comments)
2170 * @returns pointer to new string posision. NULL if end of file.
2171 * @param papszLines Pointer to line table.
2172 * @param i Index into line table. (current line)
2173 * @param psz Pointer where to start (within the current line).
2174 */
2175static char *skipInsignificantChars(char **papszLines, int &i, char *psz)
2176{
2177 BOOL fComment = *psz == '/' && psz[1] == '*';
2178
2179 while (fComment || *psz == ' ' || *psz == '\0' || (*psz == '/' && psz[1] == '/'))
2180 {
2181 if (*psz == '\0' || (*psz == '/' && psz[1] == '/' && !fComment))
2182 {
2183 if ((psz = papszLines[++i]) == NULL)
2184 break;
2185 }
2186 else
2187 psz++;
2188
2189 if (fComment)
2190 {
2191 if (!(fComment = *psz != '*' || psz[1] != '/'))
2192 psz += 2;
2193 else
2194 continue;
2195 }
2196
2197 if ((fComment = *psz == '/' && psz[1] == '*') == TRUE)
2198 psz += 1 + (psz[2] != '*'); // don't add two when there is a possible end comment following.
2199 }
2200
2201 return psz;
2202}
2203
2204
2205/**
2206 * Reads a file into memory.
2207 * @returns Pointer to file. This should be freed using 'free' when processing
2208 * the file is not needed.
2209 * @param pszFilename Name of file to read.
2210 * @remark Current implementation reads the file as a binary file.
2211 */
2212static char *readFileIntoMemory(const char *pszFilename)
2213{
2214 char *pszFile = NULL;
2215 int cbFile = 0; /* don't have to initialized, but it removes gcc warning (release) */
2216 FILE *phFile;
2217
2218 phFile = fopen(pszFilename, "rb");
2219 if (phFile != NULL)
2220 {
2221 if (!fseek(phFile, 0, SEEK_END)
2222 && (cbFile = (int)ftell(phFile)) > 0
2223 && !fseek(phFile, 0, SEEK_SET)
2224 )
2225 {
2226 pszFile = (char*)malloc(cbFile + 1);
2227 if (pszFile != NULL)
2228 {
2229 #if 1
2230 memset((void*)pszFile, 0, cbFile + 1);
2231 if (fread((void*)pszFile, 1, cbFile, phFile) == 1)
2232 {
2233 free(pszFile);
2234 pszFile = NULL;
2235 }
2236 #else
2237 int cLines = 0;
2238 int cch = 0;
2239 char *psz = pszFile;
2240
2241 while (!feof(phFile) && cch < cbFile &&
2242 fgets(psz, cbFile - cch, phFile) != NULL)
2243 {
2244 int cchLine;
2245 cch += cchLine = strlen(psz);
2246 psz += cchLine;
2247 cLines++;
2248 }
2249
2250 /* error check */
2251 if (cch > cbFile || !feof(phFile))
2252 {
2253 free(pszFile);
2254 pszFile = NULL;
2255 }
2256 else
2257 *psz = '\0';
2258 #endif
2259 }
2260 }
2261 fclose(phFile);
2262 }
2263
2264 return pszFile;
2265}
2266
2267
2268/**
2269 * Creates an array of lines from a "memory" file. The last entry in the array contains NULL.
2270 * It also replaces '\t' with ' '.
2271 * @returns Pointer to the array of lines.
2272 * @param pszFile Pointer to "memory" file.
2273 */
2274static char **createLineArray(char *pszFile)
2275{
2276 char *psz = pszFile;
2277 char **papszLines = NULL;
2278 int cLines = 1;
2279
2280 while (*psz != '\0')
2281 {
2282 if (*psz == '\r')
2283 {
2284 if (psz[1] == '\n')
2285 psz++;
2286 cLines++;
2287 } else if (*psz == '\n')
2288 cLines++;
2289 psz++;
2290 }
2291 fprintf(phLog, "%d lines\n", cLines);
2292
2293 papszLines = (char**)calloc(cLines + 1, sizeof(char *));
2294 if (papszLines != NULL)
2295 {
2296 cLines = 1;
2297 papszLines[0] = psz = pszFile;
2298 while (*psz != '\0')
2299 {
2300 if (*psz == '\t')
2301 *psz = ' ';
2302 if (*psz == '\r')
2303 {
2304 if (psz[1] == '\n')
2305 *psz++ = '\0';
2306 papszLines[cLines++] = psz + 1;
2307 *psz = '\0';
2308 } else if (*psz == '\n')
2309 {
2310 *psz = '\0';
2311 papszLines[cLines++] = psz + 1;
2312 }
2313 psz++;
2314 }
2315 papszLines[cLines] = NULL; /* Strictly, this is not needed as we use calloc. */
2316 }
2317
2318
2319 return papszLines;
2320}
2321
2322
2323/** first char after word */
2324static char *findEndOfWord(const char *psz)
2325{
2326
2327 while (*psz != '\0' &&
2328 (
2329 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
2330 ||
2331 (*psz >= '0' && *psz <= '9')
2332 ||
2333 *psz == '_'
2334 )
2335 )
2336 ++psz;
2337 return (char *)psz;
2338}
2339
2340
2341/** starting char of word */
2342static char *findStartOfWord(const char *psz, const char *pszStart)
2343{
2344 const char *pszR = psz;
2345 while (psz >= pszStart &&
2346 (
2347 (*psz >= 'A' && *psz <= 'Z')
2348 || (*psz >= 'a' && *psz <= 'z')
2349 || (*psz >= '0' && *psz <= '9')
2350 || *psz == '_'
2351 )
2352 )
2353 pszR = psz--;
2354 return (char*)pszR;
2355}
2356
2357
2358/**
2359 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2360 * @returns Pointer to first not space or tab char in the string.
2361 * @param psz Pointer to the string which is to be trimmed.
2362 * @status completely implmented.
2363 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2364 */
2365inline char *trim(char *psz)
2366{
2367 int i;
2368 if (psz == NULL)
2369 return NULL;
2370 while (*psz == ' ' || *psz == '\t')
2371 psz++;
2372 i = strlen(psz) - 1;
2373 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2374 i--;
2375 psz[i+1] = '\0';
2376 return psz;
2377}
2378
2379
2380/**
2381 * Right trims a string, ie. removing spaces (and tabs) from the end of the string.
2382 * @returns Pointer to the string passed in.
2383 * @param psz Pointer to the string which is to be right trimmed.
2384 * @status completely implmented.
2385 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2386 */
2387inline char *trimR(char *psz)
2388{
2389 int i;
2390 if (psz == NULL)
2391 return NULL;
2392 i = strlen(psz) - 1;
2393 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2394 i--;
2395 psz[i+1] = '\0';
2396 return psz;
2397}
2398
2399
2400/**
2401 * skips blanks until first non-blank.
2402 * @returns Pointer to first not space or tab char in the string.
2403 * @param psz Pointer to the string.
2404 * @status completely implmented.
2405 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2406 */
2407inline char *skip(const char *psz)
2408{
2409 if (psz == NULL)
2410 return NULL;
2411
2412 while (*psz == ' ' || *psz == '\t')
2413 psz++;
2414 return (char*)psz;
2415}
2416
2417
2418/* copy: remove remarks, and unneeded spaces, ensuring no space after '(',
2419 * ensuring space after '*', ensuring no space before ',' and ')'.
2420 */
2421static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines)
2422{
2423 copy(psz, pszFrom - papszLines[iFrom], iFrom, pszTo - papszLines[iTo], iTo, papszLines);
2424}
2425
2426#if 0
2427static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
2428{
2429 char chPrev = '\n';
2430 int i, j;
2431 int fComment = 0;
2432
2433 i = iFrom;
2434 j = jFrom;
2435 while (i < iTo || (i == iTo && j <= jTo))
2436 {
2437 if (papszLines[i][j] != '\0'
2438 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
2439 )
2440 {
2441 /* copy char ? */
2442 if (!fComment)
2443 {
2444 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
2445 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
2446 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
2447 )
2448 {
2449 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
2450 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
2451 else
2452 {
2453 chPrev = *psz++ = papszLines[i][j];
2454 if (chPrev == '*') /* ensure ' ' after '*' */
2455 chPrev = *psz++ = ' ';
2456 }
2457 }
2458 }
2459 else
2460 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == 0)
2461 j++;
2462 j++;
2463 }
2464 else
2465 {
2466 /* next */
2467 j = 0;
2468 i++;
2469 if (chPrev != ' ' && chPrev != '(')
2470 chPrev = *psz++ = ' ';
2471 }
2472 }
2473 *psz = '\0';
2474}
2475
2476#else
2477/**
2478 * Copies a set of lines to a buffer (psz) removing precompiler lines and all comments.
2479 * @param psz
2480 * @param jFrom Starting position, index into line iFrom.
2481 * @param iFrom Starting position, index into papszLines.
2482 * @param jTo Ending position, index into line iFrom.
2483 * @param iTo Ending position, index into papszLines.
2484 * @param papszLines Array of lines.
2485 * @status completely implemented.
2486 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2487 */
2488static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
2489{
2490 char chPrev = '\n';
2491 int i, j;
2492 int fComment = FALSE;
2493 int fFirst = TRUE;
2494
2495 i = iFrom;
2496 j = jFrom;
2497 while (i < iTo || (i == iTo && j <= jTo))
2498 {
2499 if (papszLines[i][j] != '\0'
2500 && !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
2501 && !(!fComment && fFirst && papszLines[i][j] == '#')
2502 )
2503 {
2504 fFirst = papszLines[i][j] == ' ';
2505
2506 /* copy char ? */
2507 if (!fComment)
2508 {
2509 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
2510
2511 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
2512 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
2513 )
2514 {
2515 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
2516 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
2517 else
2518 {
2519 chPrev = *psz++ = papszLines[i][j];
2520 if (chPrev == '*') /* ensure ' ' after '*' */
2521 chPrev = *psz++ = ' ';
2522 }
2523 }
2524
2525 }
2526 else
2527 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == FALSE)
2528 j++;
2529 j++;
2530 }
2531 else
2532 {
2533 /* next */
2534 j = 0;
2535 fFirst = TRUE;
2536 i++;
2537 if (chPrev != ' ' && chPrev != '(')
2538 chPrev = *psz++ = ' ';
2539 }
2540 }
2541 *psz = '\0';
2542}
2543#endif
2544
2545
2546/* copyComment: Wrapper for the other copyComment function.
2547 *
2548 */
2549static void copyComment(char *psz, char *pszFrom, int iFrom, char **papszLines, BOOL fStrip, BOOL fHTML)
2550{
2551 copyComment(psz, pszFrom - papszLines[iFrom], iFrom, papszLines, fStrip, fHTML);
2552}
2553
2554
2555
2556
2557/**
2558 * Copies a set of lines to a buffer (psz) stopping at the first end comment.
2559 * (If a start comment occurs it is concidered an error.)
2560 * Stars begining lines are removed.
2561 * @param psz Target buffer.
2562 * @param jFrom Starting position, index into line iFrom.
2563 * @param iFrom Starting position, index into papszLines.
2564 * @param papszLines Array of lines.
2565 * @param fStrip Strip blank lines at start and end + LICENCE notice (at end).
2566 * @param fHTML Convert to HTML while copying.
2567 * @status completely implemented.
2568 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
2569 */
2570static void copyComment(char *psz, int jFrom, int iFrom, char **papszLines, BOOL fStrip, BOOL fHTML)
2571{
2572 char * pszStartBuffer = psz; /* Start of the target buffer. */
2573 char * pszStart = psz; /* Start of current line. */
2574 int fFirst = TRUE; /* True while we're still processing the start of the line. */
2575 int i, j; /* Indexes into papszLines. */
2576
2577 i = iFrom;
2578 j = jFrom;
2579 while (papszLines[i] != NULL && !(papszLines[i][j] == '*' && papszLines[i][j+1] == '/'))
2580 {
2581 if (papszLines[i][j] != '\0')
2582 {
2583 /* Skip or copy the char ? */
2584 if (fFirst && papszLines[i][j] == ' ')
2585 j++;
2586 else if (fFirst && papszLines[i][j] == '*')
2587 j += papszLines[i][j+1] == ' ' ? (fFirst = FALSE) + 2 : 1; /* bad habbit...*/
2588 else
2589 { /* Copy it */
2590 *psz++ = papszLines[i][j++];
2591 fFirst = FALSE;
2592 }
2593 }
2594 else
2595 { /* End of line:
2596 * Check if empty line. If so truncate it.
2597 * Add new line char if not empty first line...
2598 */
2599 j = 0; /* use this as an index from line start on the copied line. */
2600 while (pszStart + j < psz && pszStart[j] == ' ')
2601 j++;
2602 if (pszStart + j == psz)
2603 psz = pszStart;
2604 if (psz > pszStartBuffer || !fStrip)
2605 {
2606 if (fHTML)
2607 {
2608 *psz++ = '<';
2609 *psz++ = 'B';
2610 *psz++ = 'R';
2611 *psz++ = '>';
2612 }
2613 *psz++ = '\n';
2614 }
2615
2616 /* next */
2617 i++;
2618 j = 0;
2619 fFirst = TRUE;
2620 pszStart = psz;
2621 }
2622 }
2623 *psz = '\0';
2624
2625 /*
2626 * Strip end + license.
2627 */
2628 if (fStrip)
2629 {
2630 if (fHTML)
2631 {
2632 while (psz >= pszStartBuffer)
2633 {
2634 if (*psz == ' ' || *psz == '\n' || *psz == '\0')
2635 *psz-- = '\0';
2636 else if (psz - 4 >= pszStartBuffer && strncmp(psz - 4, "<BR>", 4) == 0)
2637 *(psz -= 4) = '\0';
2638 else
2639 break;
2640 }
2641 }
2642 while (psz >= pszStartBuffer && (*psz == ' ' || *psz == '\n' || *psz == '\0'))
2643 *psz-- = '\0';
2644
2645
2646 if (psz - 20 > pszStartBuffer && strstr(psz - 20, "LICENSE.TXT") != NULL)
2647 {
2648 while (psz >= pszStartBuffer && *psz != '\n')
2649 *psz-- = '\0';
2650 }
2651
2652 if (fHTML)
2653 {
2654 while (psz >= pszStartBuffer)
2655 {
2656 if (*psz == ' ' || *psz == '\n' || *psz == '\0')
2657 *psz-- = '\0';
2658 else if (psz - 4 >= pszStartBuffer && strncmp(psz - 4, "<BR>", 4) == 0)
2659 *(psz -= 4) = '\0';
2660 else
2661 break;
2662 }
2663 }
2664 while (psz >= pszStartBuffer && (*psz == ' ' || *psz == '\n' || *psz == '\0'))
2665 *psz-- = '\0';
2666 }
2667}
2668
2669
2670/**
2671 * Upcases a char.
2672 * @returns Upper case of the char given in ch.
2673 * @param ch Char to capitalize.
2674 */
2675#if 0
2676inline char upcase(char ch)
2677{
2678 return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch;
2679}
2680#else
2681#define upcase(ch) ((ch) >= 'a' && (ch) <= 'z' ? (char)((ch) - ('a' - 'A')) : (ch))
2682#endif
2683
2684
2685/**
2686 * Searches for a substring in a string.
2687 * @returns Pointer to start of substring when found, NULL when not found.
2688 * @param pszStr String to be searched.
2689 * @param pszSubStr String to be searched.
2690 * @remark Depends on the upcase function.
2691 */
2692static char *stristr(const char *pszStr, const char *pszSubStr)
2693{
2694 int cchSubStr = strlen(pszSubStr);
2695 int i = 0;
2696
2697 while (*pszStr != '\0' && i < cchSubStr)
2698 {
2699 i = 0;
2700 while (i < cchSubStr && pszStr[i] != '\0' &&
2701 (upcase(pszStr[i]) == upcase(pszSubStr[i])))
2702 i++;
2703 pszStr++;
2704 }
2705
2706 return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL);
2707}
2708
2709
2710
2711/**
2712 * Skips backwards until one of the chars in pszStopAt or star of file is reached.
2713 * @returns Pointer to end.
2714 * @param pszStopAt Array of chars which we should stop at.
2715 * @param pszFrom Start pointer.
2716 * @param iLine Reference to index to array of lines. input: start line, output: result line.
2717 * @param papszLines Array lines.
2718 * @sketch
2719 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2720 * @remark Comments are skipped.
2721 * No tests for strings ("...asdf").
2722 */
2723static char *skipBackwards(const char *pszStopAt, const char *pszFrom, int &iLine, char **papszLines)
2724{
2725 BOOL fComment = FALSE;
2726 int i = iLine;
2727 const char *psz = pszFrom;
2728
2729 while (i >= 0)
2730 {
2731 /* check for stop char */
2732 const char *psz1 = pszStopAt;
2733 while (*psz1 != '\0')
2734 if (*psz1++ == *psz)
2735 break;
2736 if (*psz1 != '\0')
2737 break;
2738
2739 /* comment check */
2740 if (!fComment && psz > papszLines[i] && *psz == '/' && psz[-1] == '*')
2741 fComment = TRUE;
2742 else if (fComment && *psz == '/' && psz[1] == '*')
2743 fComment = FALSE;
2744
2745 /* ok position to return? */
2746 if (!fComment)
2747 {
2748 iLine = i;
2749 pszFrom = psz;
2750 }
2751
2752 /* prev */
2753 if (psz > papszLines[i])
2754 psz--;
2755 else
2756 { /* try find line comment */
2757 do
2758 {
2759 char *pszStart = papszLines[--i];
2760 while (*pszStart == ' ')
2761 pszStart++;
2762 if (*pszStart != '\0' && *pszStart != '#'
2763 && !(*pszStart == '/' && pszStart[1] == '/'))
2764 { /* find '//' */
2765 pszStart = strstr(pszStart, "//");
2766 if (pszStart != NULL)
2767 psz = pszStart-1;
2768 else
2769 psz = papszLines[i] + strlen(papszLines[i]) - 1;
2770 break;
2771 }
2772 } while (i > 0);
2773 }
2774 }
2775
2776 return (char*)pszFrom;
2777}
2778
2779
2780/**
2781 * Finds a string in a range of pages.
2782 * @returns Index of the line (into ppaszLines).
2783 * @param psz String to search for.
2784 * @param iStart Start line.
2785 * @param iEnd Line to stop at
2786 * @param papszLines Array of lines to search within.
2787 * @status completely implemented.
2788 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2789 */
2790static int findStrLine(const char *psz, int iStart, int iEnd, char **papszLines)
2791{
2792 while (iStart <= iEnd &&
2793 stristr(papszLines[iStart], psz) == NULL)
2794 iStart++;
2795 return iStart;
2796}
2797
2798
2799
2800
Note: See TracBrowser for help on using the repository browser.