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

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

Design note improovments.

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