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

Last change on this file since 6823 was 6678, checked in by bird, 24 years ago

reverse prev rev.

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