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

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

dll to module conversion.

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