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

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

Corrected empty parameter description bug.

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