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

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

Added and updated authors.
Corrected dep rule in makefile
Corrections and improvements in stateupd.

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