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

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

Implemented design notes and file info.

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