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

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

Fixes. Stateupd now seems to eat nearly all the source...

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