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

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

Corrected copyright info.

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