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

Last change on this file since 2748 was 2748, checked in by bird, 26 years ago

Removes empty signals.log files upon exit.

File size: 49.5 KB
Line 
1/* $Id: StateUpd.cpp,v 1.7 2000-02-10 23:37:52 bird Exp $
2 *
3 * StateUpd - Scans source files for API functions and imports data on them.
4 *
5 * Copyright (c) 1999 knut st. osmundsen
6 *
7 */
8
9/*******************************************************************************
10* Header Files *
11*******************************************************************************/
12#define INCL_DOSFILEMGR
13#define INCL_DOSERRORS
14#define INCL_DOSMISC
15#include <os2.h>
16#include <stdio.h>
17#include <string.h>
18#include <malloc.h>
19#include <assert.h>
20
21#include "StateUpd.h"
22#include "db.h"
23
24
25
26/*******************************************************************************
27* Global Variables *
28*******************************************************************************/
29static FILE *phLog = NULL;
30static FILE *phSignal = NULL;
31
32
33/*******************************************************************************
34* Internal Functions *
35*******************************************************************************/
36static void syntax(void);
37static void openLogs(void);
38static void closeLogs(void);
39static unsigned long processDir(const char *pszDirOrFile, POPTIONS pOptions);
40static unsigned long processFile(const char *pszFilename, POPTIONS pOptions);
41static unsigned long processAPI(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
42static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions);
43static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
44static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions);
45static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions);
46static char *skipInsignificantChars(char **papszLines, int &i, char *psz);
47static char *readFileIntoMemory(const char *pszFilename);
48static char **createLineArray(char *pszFile);
49static char *findEndOfWord(char *psz);
50static char *findStartOfWord(char *psz, const char *pszStart);
51static char *trim(char *psz);
52static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines);
53static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines);
54static char *stristr(const char *pszStr, const char *pszSubStr);
55
56
57/**
58 * Main function.
59 * @returns Number of signals.
60 * @param argc Argument count.
61 * @param argv Argument array.
62 */
63int main(int argc, char **argv)
64{
65 int argi;
66 BOOL fFatal = FALSE;
67 unsigned long ulRc = 0;
68 OPTIONS options = {TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE};
69 unsigned long ulRc2;
70 char *pszErrorDesc = NULL;
71 char *pszHost = "localhost";
72 char *pszDatabase = "Odin32";
73 char *pszUser = "root";
74 char *pszPasswd = "";
75
76 DosError(0x3);
77
78 /**************************************************************************
79 * parse arguments.
80 * options: -h or -? Syntax help.
81 * -ib<[+]|-> Integrity check at start.
82 * -ie<[+]|-> Integrity check at end.
83 * -io Integrity check only.
84 * -s Scan subdirectories.
85 * -Old <[+]|-> Old API Style.
86 * -OS2<[+]|-> Removes 'OS2'-prefix from function name.
87 * -COMCTL32<[+]|-> Removes 'COMCTL32'-prefix from function name.
88 * -VERSION<[+]|-> Removes 'VERSION'-prefix from function name.
89 * -d:<dbname> Database name
90 * -p:<passwd> Password
91 * -u:<user> Userid
92 * -h:<host> Hostname/IP-address
93 **************************************************************************/
94 argi = 1;
95 while (argi < argc && !fFatal)
96 {
97 if(argv[argi][0] == '-' || argv[argi][0] == '/')
98 {
99 switch (argv[argi][1])
100 {
101 case 'd':
102 case 'D':
103 if (argv[argi][2] == ':')
104 pszDatabase = &argv[argi][3];
105 else
106 fprintf(stderr, "warning: option '-d:' requires database name.\n");
107 break;
108
109 case 'h':
110 case 'H':
111 if (argv[argi][2] == ':')
112 {
113 pszHost = &argv[argi][3];
114 break;
115 }
116 case '?':
117 syntax();
118 return 0;
119
120 case 'i': /* Integrity */
121 case 'I':
122 switch (argv[argi][2])
123 {
124 case 'b':
125 case 'B':
126 options.fIntegrityBefore = argv[argi][3] != '-';
127 break;
128
129 case 'e':
130 case 'E':
131 options.fIntegrityAfter = argv[argi][3] != '-';
132 break;
133
134 case 'o':
135 case 'O':
136 options.fIntegrityOnly = argv[argi][3] != '-';
137 break;
138
139 default:
140 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
141 fFatal = TRUE;
142 }
143 break;
144
145 case 'o':
146 case 'O':
147 if (stricmp(&argv[argi][1], "OS2") == 0)
148 options.fOS2 = argv[argi][4] != '-';
149 else if (stricmp(&argv[argi][1], "Old") == 0)
150 options.fOld = argv[argi][4] != '-';
151 else
152 {
153 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
154 fFatal = TRUE;
155 }
156 break;
157
158 case 'p':
159 case 'P':
160 if (argv[argi][2] == ':')
161 pszPasswd = &argv[argi][3];
162 else
163 fprintf(stderr, "warning: option '-p:' requires password.\n");
164 break;
165
166 case 's':
167 case 'S':
168 options.fRecursive = TRUE;
169 break;
170
171 case 'u':
172 case 'U':
173 if (argv[argi][2] == ':')
174 pszUser = &argv[argi][3];
175 else
176 fprintf(stderr, "error: option '-u:' requires userid.\n");
177 break;
178
179 default:
180 fprintf(stderr, "incorrect parameter. (argi=%d, argv[argi]=%s)\n", argi, argv[argi]);
181 fFatal = TRUE;
182 break;
183 }
184 }
185 else
186 break; /* files has started */
187 argi++;
188 }
189
190 if (!fFatal)
191 {
192 /* open database */
193 if (!dbConnect(pszHost, pszUser, pszPasswd, pszDatabase))
194 {
195 fprintf(stderr, "Could not connect to database (%s). \n\terror description: %s\n",
196 pszDatabase, dbGetLastErrorDesc());
197 return 0x00010001;
198 }
199
200 /* open the logs */
201 openLogs();
202
203 /* check db integrity */
204 if (options.fIntegrityBefore)
205 {
206 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
207 *pszErrorDesc = '\0';
208 ulRc2 = dbCheckIntegrity(pszErrorDesc);
209 if (ulRc2 != 0)
210 {
211 fprintf(phSignal, "-,-: integrity errors:\n\t%s\n", pszErrorDesc);
212 ulRc += ulRc2 << 16;
213 }
214 free(pszErrorDesc);
215 }
216
217 if (!options.fIntegrityOnly)
218 {
219 /* processing */
220 if (argv[argi] == NULL || *argv[argi] == '\0')
221 ulRc = processDir(".", &options);
222 else
223 while (argv[argi] != NULL)
224 {
225 ulRc += processDir(argv[argi], &options);
226 argi++;
227 }
228
229 /* create new history rows */
230 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
231 *pszErrorDesc = '\0';
232 ulRc2 = dbCreateHistory(pszErrorDesc);
233 if (ulRc2 != 0)
234 {
235 fprintf(phSignal, "-,-: errors which occurred while creating history:\n\t%s\n", pszErrorDesc);
236 ulRc += ulRc2 << 16;
237 }
238 free(pszErrorDesc);
239
240 /* check db integrity */
241 if (options.fIntegrityAfter)
242 {
243 pszErrorDesc = (char*)malloc(1048768); assert(pszErrorDesc != NULL);
244 *pszErrorDesc = '\0';
245 ulRc2 = dbCheckIntegrity(pszErrorDesc);
246 if (ulRc2 != 0)
247 {
248 fprintf(phSignal, "-,-: integrity errors:\n\t%s\n", pszErrorDesc);
249 ulRc += ulRc2 << 16;
250 }
251 free(pszErrorDesc);
252 }
253 }
254
255 /* close the logs */
256 closeLogs();
257
258 /* close database */
259 dbDisconnect();
260
261 /* warn if error during processing. */
262 fprintf(stdout,"Number of successfully processed APIs: %4ld\n", (long)(0x0000FFFF & ulRc));
263 fprintf(stdout,"Number of signals: %4ld\n", (long)(ulRc >> 16));
264 if ((int)(ulRc >> 16) > 0)
265 fprintf(stderr, "Check signal file 'Signals.Log'.\n");
266 }
267
268 return (int)(ulRc >> 16);
269}
270
271
272
273/**
274 * Displays syntax.
275 */
276static void syntax()
277{
278 printf("\n"
279 "StateUpd v%01d.%02d - Odin32 API Database utility\n"
280 "----------------------------------------------\n"
281 "syntax: StateUpd.exe [-h|-?] [options] [FileOrDir1 [FileOrDir2 [...]]]\n"
282 "\n"
283 " -h or -? Syntax help. (this)\n"
284 " -ib<[+]|-> Integrity check at start. default: enabled\n"
285 " -ie<[+]|-> Integrity check at end. default: enabled\n"
286 " -io Integrity check only. default: disabled\n"
287 " -s Scan subdirectories. default: disabled\n"
288 " -Old Use old API style. default: disabled\n"
289 " -OS2 Ignore 'OS2'-prefix on APIs. default: disabled\n"
290 " -h:<hostname> Database server hostname. default: localhost\n"
291 " -u:<username> Username on the server. default: root\n"
292 " -p:<password> Password. default: <empty>\n"
293 " -d:<database> Database to use. default: Odin32\n"
294 "\n"
295 "Scans files for API functions. This util will extract data about the API\n"
296 "and insert it into the database.\n"
297 "\n"
298 "If no files are given, then all *.c and *.cpp files will be scanned. (Not correct!)\n"
299 "NOTE: When files are given, only *.c and *.cpp files will be scanned.\n"
300 "Wildchars are allowed in the file spesifications.\n"
301 "\n"
302 "Copyright (c) 1999 knut st. osmundsen (knut.stange.osmundsen@pmsc.no)",
303 MAJOR_VER, MINOR_VER
304 );
305}
306
307
308/**
309 * Open logs, StateUpd.log and Signals.log (error log).
310 */
311static void openLogs(void)
312{
313 if (phLog == NULL)
314 {
315 phLog = fopen("StateUpd.Log", "w");
316 if (phLog == NULL)
317 {
318 fprintf(stderr,"error occured while opening log file - will use stderr instead.\n");
319 phLog = stderr;
320 }
321 }
322
323 if (phSignal == NULL)
324 {
325 phSignal = fopen("Signals.Log", "w");
326 if (phSignal == NULL)
327 {
328 fprintf(stderr,"error occured while opening signal file - will use stdout instead.\n");
329 phSignal = stdout;
330 }
331 }
332}
333
334
335/**
336 * Closes the logs.
337 */
338static void closeLogs(void)
339{
340 if (phLog != stderr && phLog != NULL)
341 fclose(phLog);
342 if (phSignal != stdout && phSignal != NULL)
343 {
344 if (ftell(phSignal) > 0)
345 fclose(phSignal);
346 else
347 {
348 fclose(phSignal);
349 unlink("Signals.log");
350 }
351 }
352}
353
354
355/**
356 * Processes a file or a subdirectory with files.
357 * @returns high word = number of signals
358 * low word = number of APIs processed. (1 or 0).
359 * @param pszDirOrFile Directory or file, see fFile.
360 * @param pOptions Pointer to options.
361 * @sketch -0. Determin dir or file.
362 * 0. Interpret parameters.
363 * 1. Scan current directory for *.cpp and *.c files and process them.
364 * 2. If recusion option is enabled:
365 * Scan current directory for sub-directories, scan them using recursion.
366 */
367static unsigned long processDir(const char *pszDirOrFile, POPTIONS pOptions)
368{
369 unsigned long ulRc = 0; /* high word = #signals, low word = #APIs successfully processed */
370 char szBuf[CCHMAXPATH];
371 char szFileSpec[CCHMAXPATH];
372 APIRET rc;
373 FILEFINDBUF3 ffb;
374 FILESTATUS3 fs;
375 ULONG ul = 1;
376 HDIR hDir = (HDIR)HDIR_CREATE;
377 PSZ pszDir;
378 PSZ pszFile;
379 BOOL fFile;
380
381 /* -0.*/
382 rc = DosQueryPathInfo(pszDirOrFile, FIL_STANDARD, &fs , sizeof(fs));
383 fFile = rc == NO_ERROR && (fs.attrFile & FILE_DIRECTORY) != FILE_DIRECTORY;
384
385 /* 0. */
386 strcpy(szBuf, pszDirOrFile);
387 pszDir = szBuf;
388 if (fFile)
389 {
390 if ((pszFile = strrchr(pszDir, '\\')) != NULL
391 || (pszFile = strrchr(pszDir, '/')) != NULL)
392 *pszFile++ = '\0';
393 else
394 {
395 pszFile = pszDir;
396 pszDir = ".";
397 }
398 }
399 else
400 {
401 pszFile = NULL;
402 ul = strlen(pszDir)-1;
403 if (pszDir[ul] == '\\' || pszDir[ul] == '/')
404 pszDir[ul] = '\0';
405 }
406
407
408 /* 1. */
409 if (fFile)
410 strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), pszFile);
411 else
412 strcat(strcpy(&szFileSpec[0], pszDir), "\\*.c*");
413 ul = 1;
414 rc = DosFindFirst((PCSZ)&szFileSpec[0], &hDir,
415 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
416 (PVOID)&ffb, sizeof(ffb), &ul, FIL_STANDARD);
417 while (rc == NO_ERROR && ul == 1)
418 {
419 char *psz = strrchr(&ffb.achName[0], '.');
420 if (psz != NULL && (!stricmp(psz, ".cpp") || !stricmp(psz, ".c")))
421 ulRc += processFile(strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), &ffb.achName[0]), pOptions);
422
423 /* next */
424 ul = 1;
425 rc = DosFindNext(hDir, &ffb, sizeof(ffb), &ul);
426 }
427 DosFindClose(hDir);
428
429 /* 2. */
430 if (pOptions->fRecursive)
431 {
432 strcat(strcpy(&szFileSpec[0], pszDir), "\\*");
433
434 hDir = (HDIR)HDIR_CREATE;
435 ul = 1; /* important on TVFS, not on HPFS... */
436 rc = DosFindFirst((PCSZ)&szFileSpec[0], &hDir,
437 MUST_HAVE_DIRECTORY,
438 (PVOID)&ffb, sizeof(ffb), &ul, FIL_STANDARD);
439 while (rc == NO_ERROR && ul == 1)
440 {
441 if (strcmp(&ffb.achName[0], ".") != 0 && strcmp(&ffb.achName[0], "..") != 0)
442 {
443 strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), &ffb.achName[0]);
444 if (fFile)
445 strcat(strcat(&szFileSpec[0], "\\"), pszFile);
446 ulRc += processDir(&szFileSpec[0], pOptions);
447 }
448
449 /* next */
450 ul = 1;
451 rc = DosFindNext(hDir, &ffb, sizeof(ffb), &ul);
452 }
453 DosFindClose(hDir);
454 }
455
456 return ulRc;
457}
458
459
460/**
461 * Processes a file.
462 * @returns high word = number of signals
463 * low word = number of APIs processed. (1 or 0).
464 * @param pszFilename Filename
465 * @param pOptions Pointer to options.
466 * @sketch 1. read file into memory.
467 * 2. create line array.
468 * (3. simple preprocessing - TODO)
469 * 4. scan thru the line array, looking for APIs.
470 * 4b. when API is found, process it.
471 */
472static unsigned long processFile(const char *pszFilename, POPTIONS pOptions)
473{
474 unsigned long cSignals = 0;
475 unsigned long cAPIs = 0;
476 char *pszFile;
477
478 fprintf(phLog, "Processing '%s':\n", pszFilename);
479 /* 1.*/
480 pszFile = readFileIntoMemory(pszFilename);
481 if (pszFile != NULL)
482 {
483 char **papszLines;
484
485 /* 2.*/
486 papszLines = createLineArray(pszFile);
487 if (papszLines != NULL)
488 {
489 int i = 0;
490
491 /* 3. - TODO */
492
493 /* 4.*/
494 while (papszLines[i] != NULL)
495 {
496 if (isFunction(papszLines, i, pOptions))
497 {
498 unsigned long ulRc;
499 ulRc = processAPI(papszLines, i, i, pszFilename, pOptions);
500 cAPIs += 0x0000ffff & ulRc;
501 cSignals += ulRc >> 16;
502 }
503 else
504 i++;
505 }
506
507 free(papszLines);
508 }
509 else
510 {
511 fprintf(phSignal,"%s: error dividing file into lines.\n", pszFilename);
512 cSignals++;
513 }
514 free(pszFile);
515 }
516 else
517 {
518 fprintf(phSignal,"%s: error reading file.\n", pszFilename);
519 cSignals++;
520 }
521 fprintf(phLog, "Processing of '%s' is completed.\n\n", pszFilename);
522
523
524 return (unsigned long)((cSignals << 16) | cAPIs);
525}
526
527
528/**
529 * Processes an API function.
530 * @returns high word = number of signals
531 * low word = number of APIs processed. (1 or 0).
532 * @param papszLines Array of lines in the file.
533 * @param i Index into papszLines.
534 * @param iRet Index into papszLines. Where to resume search.
535 * @param pszFilename Filename used in the signal log.
536 * @param pOptions Pointer to options.
537 */
538static unsigned long processAPI(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions)
539{
540 unsigned long ulRc;
541 int j;
542 FNDESC FnDesc;
543 memset(&FnDesc, 0, sizeof(FnDesc));
544
545 /* default value */
546 FnDesc.lStatus = 99;
547
548 /* precondition: isFunction is true.
549 * brief algorithm:
550 * 1. Analyse function declaration.
551 * 2. Analyse function header.
552 * 3. Log data (for debug purpose).
553 * 4. Update database
554 */
555
556 /* 1.*/
557 ulRc = analyseFnDcl(&FnDesc, papszLines, i, iRet, pszFilename, pOptions);
558 if (0x0000ffff & ulRc) /* if low word is 0 the fatal */
559 {
560 unsigned long ulRcTmp;
561 char szErrorDesc[2113]; /* due to some limitation in the latest EMX release size is 2112 and not 4096 as initially implemented. */
562
563 /* 2.*/
564 ulRcTmp = analyseFnHdr(&FnDesc, papszLines, i, pszFilename, pOptions);
565 if (ulRcTmp == ~0UL) /* check for fatal error */
566 return (0xffff0000UL & ulRc) + 0x00010000UL;
567 ulRc += 0xffff0000UL & ulRcTmp;
568
569 /* 3.*/
570 fprintf(phLog, "Name: '%s' (refcode=%ld)\n", FnDesc.pszName, FnDesc.lRefCode);
571 fprintf(phLog, " Returns: '%s'\n", FnDesc.pszReturnType != NULL ? FnDesc.pszReturnType : "<missing>");
572 fprintf(phLog, " cParams: %2d\n", FnDesc.cParams);
573 for (j = 0; j < FnDesc.cParams; j++)
574 fprintf(phLog, " Param %2d: type '%s' %*s name '%s'\n", j, FnDesc.apszParamType[j],
575 (int)(15 - strlen(FnDesc.apszParamType[j])), "", FnDesc.apszParamName[j]);
576 fprintf(phLog, " Status: %ld - '%s'\n", FnDesc.lStatus, FnDesc.pszStatus != NULL ? FnDesc.pszStatus : "<missing>");
577 fprintf(phLog, " cAuthors: %2d\n", FnDesc.cAuthors);
578 for (j = 0; j < FnDesc.cAuthors; j++)
579 fprintf(phLog, " Author %d: '%s' (refcode=%ld)\n", j, FnDesc.apszAuthor[j], FnDesc.alAuthorRefCode[j]);
580
581 /* 4.*/
582 ulRcTmp = dbUpdateFunction(&FnDesc, &szErrorDesc[0]);
583 if (ulRcTmp != 0)
584 {
585 fprintf(phSignal, "%s,%s: %s\n", pszFilename, FnDesc.pszName, &szErrorDesc[0]);
586 ulRc += ulRcTmp << 16;
587 }
588 }
589
590 return ulRc;
591}
592
593
594/**
595 * Analyses the function declaration.
596 * @returns high word = number of signals
597 * low word = number of APIs processed. (1 or 0).
598 * @param papszLines Array of lines in the file.
599 * @param i Index into papszLines.
600 * @param iRet Index into papszLines. Where to start searching again.
601 * @param pszFilename Filename used in the signal log.
602 * @param pOptions Pointer to options.
603 */
604static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
605 const char *pszFilename, POPTIONS pOptions)
606{
607 static long lPrevFnDll = -1L; /* fix for duplicate dlls */
608 unsigned long ulRc;
609 FNFINDBUF FnFindBuf;
610 long lFn = 0;
611
612 /* brief algorithm:
613 * 1. read function declaration using analyseFnDcl2.
614 * 2. apply name rules.
615 * 3. do a database lookup on the name.
616 * 3b. if more that one match, write a signal. (TODO: a simple fix is done, but there are holes.)
617 */
618
619 /* 1. */
620 ulRc = analyseFnDcl2(pFnDesc, papszLines, i, iRet, pszFilename, pOptions);
621 if (ulRc != 1)
622 return ulRc;
623
624 /* 2. */
625 if (pOptions->fOS2 && strncmp(pFnDesc->pszName, "OS2", 3) == 0)
626 pFnDesc->pszName += 3;
627 else if (pOptions->fCOMCTL32 && strncmp(pFnDesc->pszName, "COMCTL32", 8) == 0)
628 pFnDesc->pszName += 8;
629 else if (pOptions->fVERSION && strncmp(pFnDesc->pszName, "VERSION", 7) == 0)
630 pFnDesc->pszName += 7;
631 else if (pOptions->fOld)
632 pFnDesc->pszName += 3;
633
634 /* 3. */
635 if (!dbFindFunction(pFnDesc->pszName, &FnFindBuf))
636 {
637 fprintf(phSignal, "%s, %s: error occured while reading from database, %s\n",
638 pszFilename, pFnDesc->pszName, dbGetLastErrorDesc());
639 return 0x00010000;
640 }
641
642 if (FnFindBuf.cFns == 0)
643 {
644 fprintf(phLog, "%s was not an API\n", pFnDesc->pszName);
645 return 0;
646 }
647 else if (FnFindBuf.cFns > 1)
648 { /* 3b.*/
649 while (lFn < (int)FnFindBuf.cFns && FnFindBuf.alDllRefCode[lFn] != lPrevFnDll)
650 lFn++;
651 if (lPrevFnDll == -1L && lFn >= (int)FnFindBuf.cFns)
652 {
653 fprintf(phSignal, "%s, %s: error - more than one function by the name '%s'\n",
654 pszFilename, pFnDesc->pszName, pFnDesc->pszName);
655 return 0x00010000;
656 }
657 }
658 pFnDesc->lRefCode = FnFindBuf.alRefCode[lFn];
659 lPrevFnDll = FnFindBuf.alDllRefCode[lFn];
660
661 return ulRc;
662}
663
664
665
666/**
667 * Analyses the function declaration.
668 * No DB lockup or special function type stuff, only ODINFUNCTION is processed.
669 * @returns high word = number of signals
670 * low word = number of APIs processed. (1 or 0).
671 * @param papszLines Array of lines in the file.
672 * @param i Index into papszLines.
673 * @param iRet Index into papszLines. Where to start searching again.
674 * @param pszFilename Filename used in the signal log.
675 * @param pOptions Pointer to options.
676 */
677static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
678 const char *pszFilename, POPTIONS pOptions)
679{
680 /** @sketch
681 * 1. find the '('
682 * 2. find the word ahead of the '(', this is the function name.
683 * 3. find the closing ')'
684 * 4. copy the parameters, which is between the two '()'
685 * 5. format the parameters
686 */
687
688 int iFn, iP1, iP2, j;
689 char * pszFn, *pszP1, *pszP2;
690 char * psz, *pszEnd;
691 int cArgs;
692 char * apszArgs[30];
693
694 /* 1.*/
695 iP1 = i;
696 while (papszLines[iP1] != NULL
697 && (pszP1 = strchr(papszLines[iP1], '(')) == NULL)
698 iP1++;
699 if (papszLines[iP1] == NULL)
700 {
701 fprintf(phSignal, "%d: oops! didn't find end of function!, %d\n", pszFilename, __LINE__);
702 iRet = iP1;
703 return 0x00010000;
704 }
705
706 /* 2. */
707 iFn = iP1;
708 if (papszLines[iFn] != pszP1)
709 pszFn = pszP1 - 1;
710 else
711 {
712 pszFn = papszLines[--iFn];
713 pszFn += strlen(pszFn);
714 }
715 while (iFn >= i && *pszFn == ' ')
716 {
717 if (pszFn != papszLines[iFn])
718 pszFn--;
719 else
720 {
721 pszFn = papszLines[--iFn];
722 pszFn += strlen(pszFn);
723 }
724 }
725 if (*pszFn == ' ' || *pszFn == '\0')
726 {
727 fprintf(phSignal, "%s: internal error!, %d\n", pszFilename, __LINE__);
728 iRet = iP1;
729 return 0x00010000;
730 }
731 pszFn = findStartOfWord(pszFn, papszLines[i]);
732
733 /* 3. */
734 iP2 = iP1;
735 pszP2 = pszP1 + 1;
736 while (*pszP2 != ')')
737 if (*pszP2++ == '\0')
738 if ((pszP2 = papszLines[++iP2]) == NULL)
739 break;
740
741 iRet = iP2 + 1; //assumes: only one function on a single line!
742
743 /* 4. */
744 psz = pFnDesc->szFnDclBuffer;
745 copy(pFnDesc->szFnDclBuffer, pszP1, iP1, pszP2, iP2, papszLines);
746 pszEnd = psz + strlen(psz) + 1;
747
748 /* 5.*/
749 cArgs = 0;
750 if (stricmp(psz, "(void)") != 0 && strcmp(psz, "()") != 0 && strcmp(psz, "( )"))
751 {
752 char *pszC;
753 pszC = trim(psz+1);
754 while (*pszC != '\0')
755 {
756 apszArgs[cArgs] = pszC;
757 while (*pszC != ',' && *pszC != ')' && *pszC != '\0')
758 pszC++;
759 *pszC = '\0';
760 trim(apszArgs[cArgs++]);
761
762 /* next */
763 pszC = trim(pszC + 1);
764 }
765 }
766
767 /* 6. */
768 if (strnicmp(pszFn, "ODINFUNCTION", 12) == 0)
769 {
770 if (cArgs < 2)
771 {
772 fprintf(phSignal, "%s: Invalid ODINFUNCTION function too few parameters!\n", pszFilename);
773 return 0x00010000;
774 }
775 /* return type */
776 pFnDesc->pszReturnType = apszArgs[0];
777
778 /* function name */
779 pFnDesc->pszName = apszArgs[1];
780
781 /* arguments */
782 j = 2;
783 pFnDesc->cParams = 0;
784 while (j+1 < cArgs)
785 {
786 pFnDesc->apszParamType[pFnDesc->cParams] = apszArgs[j];
787 pFnDesc->apszParamName[pFnDesc->cParams] = apszArgs[j+1];
788 pFnDesc->cParams++;
789 j += 2;
790 }
791 }
792 else
793 {
794 /* function name */
795 *pszEnd = '\0';
796 strncat(pszEnd, pszFn, findEndOfWord(pszFn) - pszFn);
797 pFnDesc->pszName = pszEnd;
798
799 /* return type - not implemented TODO/FIXME! */
800 pFnDesc->pszReturnType = NULL;
801
802 /* arguments */
803 pFnDesc->cParams = cArgs;
804 for (j = 0; j < cArgs; j++)
805 {
806 pFnDesc->apszParamName[j] = findStartOfWord(apszArgs[j] + strlen(apszArgs[j]) - 1,
807 apszArgs[j]);
808 pFnDesc->apszParamName[j][-1] = '\0';
809 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
810 }
811 }
812 pOptions = pOptions;
813 return 0x00000001;
814}
815
816
817/**
818 * Analyses the function header.
819 * @returns high word = number of signals
820 * low word = number of APIs processed. (1 or 0).
821 * @param papszLines Array of lines in the file.
822 * @param i Index into papszLines.
823 * @param pszFilename Filename used in the signal log.
824 * @param pOptions Pointer to options.
825 * @sketch 1. Search backwards (start at i-1) for a comment or code.
826 * 2. If comment: (else fail)
827 * 2a. find start and end of comment.
828 * 2b. check for function header characteristics
829 * - lots of '*'s.
830 * - fields 'Status', 'Author' and 'Name'
831 * or if SDS, check for:
832 * - at least two '*'s at the begining.
833 * - fields '@status' and/or '@author'
834 * 2c. check that content of the 'Name' field matches (not SDS)
835 * 2d. read the status and author fields.
836 * @remark Supports both types of function headers, Odin32-common and SDS.
837 */
838static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions)
839{
840 int iStatus, iAuthor, iName, iStart, iEnd;
841 int j, jStart;
842 int fFound;
843 int fSDS = 0;
844 char *psz, *pszB;
845 char *pszAuthor = NULL;
846 unsigned long ulRc = 0x00000001;
847
848 pOptions = pOptions;
849
850 if (i < 0) /* parameter validation */
851 return 0;
852
853 /* 1. + 2a.*/
854 iEnd = i-1; /* find end */
855 j = strlen(papszLines[iEnd]);
856 j -= j > 0 ? 1 : 0;
857 fFound = 0;
858 while (iEnd >= 0 && i - iEnd < 7 && papszLines[iEnd][j] != '}' &&
859 !(fFound = (papszLines[iEnd][j] == '*' && papszLines[iEnd][j+1] == '/')))
860 if (j-- == 0)
861 if (iEnd-- > 0)
862 {
863 j = strlen(papszLines[iEnd]);
864 j -= j > 0 ? 1 : 0;
865 }
866 if (!fFound) /* fail if not found */
867 return 0;
868
869 iStart = iEnd; /* find start */
870 if (j < 2)
871 j -= (j = strlen(papszLines[--iStart])) > 1 ? 2 : j;
872 else
873 j -= 2;
874 fFound = 0;
875 while (iStart >= 0 &&
876 !(fFound = (papszLines[iStart][j] == '/' && papszLines[iStart][j+1] == '*')))
877 if (j-- == 0)
878 if (iStart-- > 0)
879 {
880 j = strlen(papszLines[iStart]);
881 j -= j > 1 ? 2 : j;
882 }
883
884 if (!fFound) /* fail if not found */
885 return 0;
886 jStart = j;
887
888 /* 2b.*/
889 if (strncmp(&papszLines[iStart][jStart], "/**", 3) != 0) /* checks that there are more than one star at start of comment */
890 return 0;
891
892 iName = iStart; /* Odin32 common */
893 while (iName <= iEnd &&
894 stristr(papszLines[iName], "* Name") == NULL)
895 iName++;
896 iStatus = iStart;
897 while (iStatus <= iEnd &&
898 stristr(papszLines[iStatus], "* Status") == NULL)
899 iStatus++;
900 iAuthor = iStart;
901 while (iAuthor <= iEnd &&
902 stristr(papszLines[iAuthor], "* Author") == NULL)
903 iAuthor++;
904
905 if (!(iName <= iEnd || iStatus <= iEnd || iAuthor <= iEnd)) /* if not found try SDS */
906 {
907 iStatus = iStart;
908 while (iStatus <= iEnd &&
909 stristr(papszLines[iStatus], "@status") == NULL)
910 iStatus++;
911 iAuthor = iStart;
912 while (iAuthor <= iEnd &&
913 stristr(papszLines[iAuthor], "@author") == NULL)
914 iAuthor++;
915 if (!(iStatus <= iEnd || iAuthor <= iEnd))
916 return 0;
917 fSDS = 1;
918 }
919
920 /* 2c.*/
921 if (iName <= iEnd && strstr(papszLines[iName], pFnDesc->pszName) == NULL)
922 fprintf(phLog, "Warning: a matching function name is not found in the name Field\n");
923
924 /* 2d.*/
925 pszB = &pFnDesc->szFnHdrBuffer[0];
926 if (!fSDS)
927 { /* Odin32 common */
928 if (iStatus <= iEnd) /* Status */
929 {
930 psz = stristr(papszLines[iStatus], "* Status") + sizeof("* Status") - 1;
931 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
932 psz++;
933 strcpy(pszB, psz);
934 trim(pszB);
935 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
936 {
937 pszB[strlen(pszB)-1] = '\0';
938 trim(pszB);
939 }
940 pFnDesc->pszStatus = pszB;
941 pszB += strlen(pszB) + 1;
942 }
943
944 if (iAuthor <= iEnd) /* Author(s) */
945 {
946 pszAuthor = pszB;
947 psz = stristr(papszLines[iAuthor], "* Author") + sizeof("* Author") - 1;
948 do
949 {
950 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
951 psz++;
952 strcpy(pszB, psz);
953 trim(pszB);
954 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
955 {
956 pszB[strlen(pszB)-1] = '\0';
957 trim(pszB);
958 }
959 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
960 strcat(pszB, ",");
961 pszB += strlen(pszB);
962
963 /* next */
964 psz = papszLines[++iAuthor] + 1;
965 j = 0;
966 while (*psz == ' ' && j++ < 5) psz++;
967 if (*psz == '*')
968 psz++;
969 } while (iAuthor < iEnd && *psz == ' ');
970 pszB++;
971 }
972 }
973 else
974 { /* SDS */
975 if (iStatus <= iEnd)
976 {
977 psz = stristr(papszLines[iStatus], "@status") + sizeof("@status");
978 while (*psz == ' ')
979 psz++;
980 strcpy(pszB, psz);
981 trim(pszB);
982 pszB += strlen(pszB) + 1;
983 }
984
985 if (iAuthor <= iEnd)
986 {
987 pszAuthor = pszB;
988 psz = stristr(papszLines[iAuthor], "@author") + sizeof("@author");
989 do
990 {
991 while (*psz == ' ')
992 psz++;
993 strcpy(pszB, psz);
994 trim(pszB);
995 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
996 strcat(pszB, ",");
997 pszB += strlen(pszB) + 1;
998
999 /* next */
1000 psz = papszLines[++iAuthor] + 1;
1001 j = 0;
1002 while (*psz == ' ' && j++ < 5) psz++;
1003 if (*psz == '*')
1004 psz++;
1005 } while (iAuthor <= iEnd && (*psz == ' ' || *psz == '@'));
1006 pszB++;
1007 }
1008 }
1009
1010 /* Status - refcodes are hardcoded here! */
1011 if (pFnDesc->pszStatus != NULL && *pFnDesc->pszStatus != '\0')
1012 {
1013 if (strstr(pFnDesc->pszStatus, "STUB") != NULL || *pFnDesc->pszStatus == '1')
1014 pFnDesc->lStatus = 1; /* STUB */
1015 else if (stristr(pFnDesc->pszStatus, "Partially") != NULL || *pFnDesc->pszStatus == '2' || *pFnDesc->pszStatus == '3')
1016 {
1017 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '2')
1018 pFnDesc->lStatus = 2; /* STUB */
1019 else
1020 pFnDesc->lStatus = 3; /* STUB */
1021 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1022 || *pFnDesc->pszStatus == '5' || *pFnDesc->pszStatus == '7')
1023 pFnDesc->lStatus += 4;
1024 }
1025 else if (stristr(pFnDesc->pszStatus, "Completely") != NULL || *pFnDesc->pszStatus == '4' || *pFnDesc->pszStatus == '5')
1026 {
1027 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '4')
1028 pFnDesc->lStatus = 4; /* STUB */
1029 else
1030 pFnDesc->lStatus = 5; /* STUB */
1031 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1032 || *pFnDesc->pszStatus == '8' || *pFnDesc->pszStatus == '9')
1033 pFnDesc->lStatus += 4;
1034 }
1035 else
1036 {
1037 fprintf(phSignal, "%s, %s: '%s' is not a valid status code.\n",
1038 pszFilename, pFnDesc->pszName, pFnDesc->pszStatus);
1039 ulRc += 0x00010000;
1040 }
1041 }
1042
1043 /* Author */
1044 if (pszAuthor)
1045 { /* author1, author2, author3 */
1046 j = 0;
1047 psz = trim(pszAuthor);
1048 pFnDesc->cAuthors = 0;
1049 while (*psz != '\0' && pFnDesc->cAuthors < (int)(sizeof(pFnDesc->apszAuthor) / sizeof(pFnDesc->apszAuthor[0])))
1050 {
1051 char *pszBr1 = strchr(psz, '[');
1052 char *pszBr2 = strchr(psz, ']');
1053 char *pszComma;
1054
1055 /* remove '[text]' text - this is usualy used for time/date */
1056 if (pszBr1 != NULL && pszBr2 != NULL && pszBr1 < pszBr2)
1057 while (pszBr1 <= pszBr2)
1058 *pszBr1++ = ' ';
1059
1060 /* terminate string */
1061 pszComma = strchr(psz, ',');
1062 if (pszComma != NULL)
1063 {
1064 pszAuthor = pszComma + 1;
1065 *pszComma = '\0';
1066 }
1067
1068 pFnDesc->apszAuthor[pFnDesc->cAuthors] = trim(psz);
1069 pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] =
1070 dbFindAuthor(pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1071
1072 if (pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] == -1)
1073 {
1074 fprintf(phSignal, "%s, %s: author '%s' is not recognized.\n", pszFilename, pFnDesc->pszName,
1075 pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1076 ulRc += 0x00010000;
1077 }
1078
1079 /* next */
1080 pFnDesc->cAuthors++;
1081 psz = trim(pszAuthor);
1082 }
1083 }
1084
1085 return ulRc;
1086}
1087
1088
1089/**
1090 * Checks if there may be an function starting at the current line.
1091 * @returns TRUE if API found, else FALSE.
1092 * @param papszLines Array of lines in the file.
1093 * @param i Index into papszLines.
1094 * @param pOptions Pointer to options.
1095 */
1096static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions)
1097{
1098 if (!pOptions->fOld)
1099 { /* new API naming style */
1100 /* brief algorithm:
1101 * check that line don't start with '\\', '{' or '}'
1102 * search for '('.
1103 * if found then do
1104 * find c-word previous to '('
1105 * if found then do
1106 * check that the following condition are true:
1107 * 1. word is not 'for', 'while', 'do', 'if', 'else' or 'switch'
1108 * 2. first significant char after '(' should not be a '*'. (Fix for functionnames in function header, "BackupRead(".)
1109 * 3. find the matching ')' and check that the first significant char after it is '{'.
1110 * if 1, 2 and 3 are all true return true
1111 * return false.
1112 *
1113 * Note, for 2: spaces, newlines and comments are ignored, all other chars are significant.
1114 */
1115 char *pszP1 = papszLines[i];
1116
1117 while (*pszP1 != '\0' && *pszP1 == ' ')
1118 pszP1--;
1119 if (*pszP1 != '\0' && *pszP1 != '\\' && pszP1[1] != '\\'
1120 && *pszP1 != '{' && *pszP1 != '}')
1121 {
1122 pszP1 = strchr(papszLines[i], '(');
1123 if (pszP1 != NULL && pszP1 > papszLines[i])
1124 {
1125 int cchFnName = 0;
1126 char *pszFnName = pszP1 - 1;
1127
1128 while (pszFnName - cchFnName > papszLines[i] && pszFnName[cchFnName] == ' ')
1129 cchFnName--;
1130
1131 pszFnName = findStartOfWord(pszFnName, papszLines[i]);
1132 cchFnName += pszP1 - pszFnName;
1133 if (cchFnName >= 0)
1134 {
1135 /* 1. */
1136 if (
1137 strncmp(pszFnName, "for", cchFnName) != 0 &&
1138 strncmp(pszFnName, "while", cchFnName) != 0 &&
1139 strncmp(pszFnName, "do", cchFnName) != 0 &&
1140 strncmp(pszFnName, "if", cchFnName) != 0 &&
1141 strncmp(pszFnName, "else", cchFnName) != 0 &&
1142 strncmp(pszFnName, "switch", cchFnName) != 0
1143 )
1144 {
1145 /* 2. */
1146 int j = i;
1147 char *psz = skipInsignificantChars(papszLines, j, pszP1+1);
1148 if (psz != NULL && *psz != '*')
1149 {
1150 char *pszB = pszP1; /*'{'*/
1151
1152 /* 3. */
1153 while (*pszB != ')')
1154 if (*pszB++ == '\0')
1155 if ((pszB = papszLines[++i]) == NULL)
1156 break;
1157 if (pszB != NULL)
1158 {
1159 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1160 if (pszB != NULL && *pszB == '{')
1161 {
1162 fprintf(phLog, "Function found: %.*s\n", cchFnName, pszFnName);
1163 return TRUE;
1164 }
1165 }
1166 }
1167 }
1168 }
1169 }
1170 }
1171 }
1172 else
1173 { /* old API naming style */
1174 char *pszOS2;
1175
1176 /* brief algorithm:
1177 * search for function prefix, 'OS2'.
1178 * if found then do
1179 * check that the following condition are true:
1180 * 1. char before 'OS2' is either start-of-line (no char), space or '*'.
1181 * 2. first significant char after the 'OS2' prefixed word is a '('.
1182 * 3. find the matching ')' and check that the first significant char after it is '{'.
1183 * if 1,2 and 3 all are true return true
1184 * return false.
1185 *
1186 * Note, for 2 and 3 spaces, newlines and comments are ignored, all other chars are significant.
1187 */
1188 pszOS2 = strstr(papszLines[i], "OS2");
1189 if (pszOS2 != NULL)
1190 {
1191 /* 1.*/
1192 if (pszOS2 == papszLines[i] || pszOS2[-1] == ' ' || pszOS2[-1] == '*')
1193 {
1194 char *pszP1; /*'('*/
1195 int cchFnName;
1196
1197 /* 2. */
1198 pszP1 = findEndOfWord(pszOS2);
1199 cchFnName = pszP1 - pszOS2;
1200 pszP1 = skipInsignificantChars(papszLines, i, pszP1);
1201
1202 if (pszP1 != NULL && *pszP1 == '(')
1203 {
1204 char *pszB = pszP1; /*'{'*/
1205
1206 /* 3. */
1207 while (*pszB != ')')
1208 if (*pszB++ == '\0')
1209 if ((pszB = papszLines[++i]) == NULL)
1210 break;
1211 if (pszB != NULL)
1212 {
1213 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1214 if (pszB != NULL && *pszB == '{')
1215 {
1216 fprintf(phLog, "Possible API: %.*s\n", cchFnName, pszOS2);
1217 return TRUE;
1218 }
1219 }
1220 }
1221 }
1222 }
1223 }
1224
1225 return FALSE;
1226}
1227
1228
1229/**
1230 * Skips insignificant chars (spaces, new-lines and comments)
1231 * @returns pointer to new string posision. NULL if end of file.
1232 * @param papszLines Pointer to line table.
1233 * @param i Index into line table. (current line)
1234 * @param psz Pointer where to start (within the current line).
1235 */
1236static char *skipInsignificantChars(char **papszLines, int &i, char *psz)
1237{
1238 BOOL fComment = *psz == '/' && psz[1] == '*';
1239
1240 while (fComment || *psz == ' ' || *psz == '\0' || (*psz == '/' && psz[1] == '/'))
1241 {
1242 if (*psz == '\0' || (*psz == '/' && psz[1] == '/' && !fComment))
1243 {
1244 if ((psz = papszLines[++i]) == NULL)
1245 break;
1246 }
1247 else
1248 psz++;
1249
1250 if (fComment)
1251 {
1252 if (!(fComment = *psz != '*' || psz[1] != '/'))
1253 psz += 2;
1254 else
1255 continue;
1256 }
1257
1258 if ((fComment = *psz == '/' && psz[1] == '*') == TRUE)
1259 psz += 1 + (psz[2] != '*'); // don't add two when there is a possible end comment following.
1260 }
1261
1262 return psz;
1263}
1264
1265
1266/**
1267 * Reads a file into memory.
1268 * @returns Pointer to file. This should be freed using 'free' when processing
1269 * the file is not needed.
1270 * @param pszFilename Name of file to read.
1271 * @remark Current implementation reads the file as a binary file.
1272 */
1273static char *readFileIntoMemory(const char *pszFilename)
1274{
1275 char *pszFile = NULL;
1276 int cbFile = 0; /* don't have to initialized, but it removes gcc warning (release) */
1277 FILE *phFile;
1278
1279 phFile = fopen(pszFilename, "rb");
1280 if (phFile != NULL)
1281 {
1282 if (!fseek(phFile, 0, SEEK_END)
1283 && (cbFile = (int)ftell(phFile)) > 0
1284 && !fseek(phFile, 0, SEEK_SET)
1285 )
1286 {
1287 pszFile = (char*)malloc(cbFile + 1);
1288 if (pszFile != NULL)
1289 {
1290 #if 1
1291 memset((void*)pszFile, 0, cbFile + 1);
1292 if (fread((void*)pszFile, 1, cbFile, phFile) == 1)
1293 {
1294 free(pszFile);
1295 pszFile = NULL;
1296 }
1297 #else
1298 int cLines = 0;
1299 int cch = 0;
1300 char *psz = pszFile;
1301
1302 while (!feof(phFile) && cch < cbFile &&
1303 fgets(psz, cbFile - cch, phFile) != NULL)
1304 {
1305 int cchLine;
1306 cch += cchLine = strlen(psz);
1307 psz += cchLine;
1308 cLines++;
1309 }
1310
1311 /* error check */
1312 if (cch > cbFile || !feof(phFile))
1313 {
1314 free(pszFile);
1315 pszFile = NULL;
1316 }
1317 else
1318 *psz = '\0';
1319 #endif
1320 }
1321 }
1322 fclose(phFile);
1323 }
1324
1325 return pszFile;
1326}
1327
1328
1329/**
1330 * Creates an array of lines from a "memory" file. The last entry in the array contains NULL.
1331 * @returns Pointer to the array of lines.
1332 * @param pszFile Pointer to "memory" file.
1333 */
1334static char **createLineArray(char *pszFile)
1335{
1336 char *psz = pszFile;
1337 char **papszLines = NULL;
1338 int cLines = 0;
1339
1340 while (*psz != '\0')
1341 {
1342 if (*psz == '\r')
1343 {
1344 if (psz[1] == '\n')
1345 psz++;
1346 cLines++;
1347 } else if (*psz == '\n')
1348 cLines++;
1349 psz++;
1350 }
1351 fprintf(phLog, "%d lines\n", cLines);
1352
1353 papszLines = (char**)calloc(cLines + 1, sizeof(char *));
1354 if (papszLines != NULL)
1355 {
1356 cLines = 0;
1357 psz = pszFile;
1358 while (*psz != '\0')
1359 {
1360 if (*psz == '\r')
1361 {
1362 if (psz[1] == '\n')
1363 *psz++ = '\0';
1364 papszLines[cLines++] = psz + 1;
1365 *psz = '\0';
1366 } else if (*psz == '\n')
1367 {
1368 *psz = '\0';
1369 papszLines[cLines++] = psz + 1;
1370 }
1371 psz++;
1372 }
1373 papszLines[cLines] = NULL; /* Strictly, this is not needed as we use calloc. */
1374 }
1375
1376
1377 return papszLines;
1378}
1379
1380
1381/** first char after word */
1382static char *findEndOfWord(char *psz)
1383{
1384
1385 while (*psz != '\0' &&
1386 (
1387 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
1388 ||
1389 (*psz >= '0' && *psz <= '9')
1390 ||
1391 *psz == '_'
1392 )
1393 )
1394 ++psz;
1395 return psz;
1396}
1397
1398
1399/** staring char of word */
1400static char *findStartOfWord(char *psz, const char *pszStart)
1401{
1402 char *pszR = psz;
1403 while (psz >= pszStart &&
1404 (
1405 (*psz >= 'A' && *psz <= 'Z')
1406 || (*psz >= 'a' && *psz <= 'z')
1407 || (*psz >= '0' && *psz <= '9')
1408 || *psz == '_'
1409 )
1410 )
1411 pszR = psz--;
1412 return pszR;
1413}
1414
1415
1416static char *trim(char *psz)
1417{
1418 int i;
1419 if (psz == NULL)
1420 return NULL;
1421 while (*psz == ' ' || *psz == '\t')
1422 psz++;
1423 i = strlen(psz) - 1;
1424 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
1425 i--;
1426 psz[i+1] = '\0';
1427 return psz;
1428}
1429
1430
1431/* copy: remove remarks, and unneeded spaces, ensuring no space after '(',
1432 * ensuring space after '*', ensuring no space before ',' and ')'.
1433 */
1434
1435static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines)
1436{
1437 copy(psz, pszFrom - papszLines[iFrom], iFrom, pszTo - papszLines[iTo], iTo, papszLines);
1438}
1439
1440
1441static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
1442{
1443 char chPrev = '\n';
1444 int i, j;
1445 int fComment = 0;
1446
1447 i = iFrom;
1448 j = jFrom;
1449 while (i < iTo || (i == iTo && j <= jTo))
1450 {
1451 if (papszLines[i][j] != '\0' &&
1452 !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
1453 )
1454 {
1455 /* copy char ? */
1456 if (!fComment)
1457 {
1458 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
1459 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
1460 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
1461 )
1462 {
1463 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
1464 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
1465 else
1466 {
1467 chPrev = *psz++ = papszLines[i][j];
1468 if (chPrev == '*') /* ensure ' ' after '*' */
1469 chPrev = *psz++ = ' ';
1470 }
1471 }
1472 }
1473 else
1474 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == 0)
1475 j++;
1476 j++;
1477 }
1478 else
1479 {
1480 /* next */
1481 j = 0;
1482 i++;
1483 if (chPrev != ' ' && chPrev != '(')
1484 chPrev = *psz++ = ' ';
1485 }
1486 }
1487 *psz = '\0';
1488}
1489
1490
1491/**
1492 * Upcases a char.
1493 * @returns Upper case of the char given in ch.
1494 * @param ch Char to capitalize.
1495 */
1496inline char upcase(char ch)
1497{
1498 return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch;
1499}
1500
1501
1502/**
1503 * Searches for a substring in a string.
1504 * @returns Pointer to start of substring when found, NULL when not found.
1505 * @param pszStr String to be searched.
1506 * @param pszSubStr String to be searched.
1507 * @remark Depends on the upcase function.
1508 */
1509static char *stristr(const char *pszStr, const char *pszSubStr)
1510{
1511 int cchSubStr = strlen(pszSubStr);
1512 int i = 0;
1513
1514 while (*pszStr != '\0' && i < cchSubStr)
1515 {
1516 i = 0;
1517 while (i < cchSubStr && pszStr[i] != '\0' &&
1518 (upcase(pszStr[i]) == upcase(pszSubStr[i])))
1519 i++;
1520 pszStr++;
1521 }
1522
1523 return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL);
1524}
1525
Note: See TracBrowser for help on using the repository browser.