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

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

Corrections to StateUpd.

File size: 49.4 KB
Line 
1/* $Id: StateUpd.cpp,v 1.6 2000-02-10 22:10:38 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 fclose(phSignal);
344}
345
346
347/**
348 * Processes a file or a subdirectory with files.
349 * @returns high word = number of signals
350 * low word = number of APIs processed. (1 or 0).
351 * @param pszDirOrFile Directory or file, see fFile.
352 * @param pOptions Pointer to options.
353 * @sketch -0. Determin dir or file.
354 * 0. Interpret parameters.
355 * 1. Scan current directory for *.cpp and *.c files and process them.
356 * 2. If recusion option is enabled:
357 * Scan current directory for sub-directories, scan them using recursion.
358 */
359static unsigned long processDir(const char *pszDirOrFile, POPTIONS pOptions)
360{
361 unsigned long ulRc = 0; /* high word = #signals, low word = #APIs successfully processed */
362 char szBuf[CCHMAXPATH];
363 char szFileSpec[CCHMAXPATH];
364 APIRET rc;
365 FILEFINDBUF3 ffb;
366 FILESTATUS3 fs;
367 ULONG ul = 1;
368 HDIR hDir = (HDIR)HDIR_CREATE;
369 PSZ pszDir;
370 PSZ pszFile;
371 BOOL fFile;
372
373 /* -0.*/
374 rc = DosQueryPathInfo(pszDirOrFile, FIL_STANDARD, &fs , sizeof(fs));
375 fFile = rc == NO_ERROR && (fs.attrFile & FILE_DIRECTORY) != FILE_DIRECTORY;
376
377 /* 0. */
378 strcpy(szBuf, pszDirOrFile);
379 pszDir = szBuf;
380 if (fFile)
381 {
382 if ((pszFile = strrchr(pszDir, '\\')) != NULL
383 || (pszFile = strrchr(pszDir, '/')) != NULL)
384 *pszFile++ = '\0';
385 else
386 {
387 pszFile = pszDir;
388 pszDir = ".";
389 }
390 }
391 else
392 {
393 pszFile = NULL;
394 ul = strlen(pszDir)-1;
395 if (pszDir[ul] == '\\' || pszDir[ul] == '/')
396 pszDir[ul] = '\0';
397 }
398
399
400 /* 1. */
401 if (fFile)
402 strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), pszFile);
403 else
404 strcat(strcpy(&szFileSpec[0], pszDir), "\\*.c*");
405 ul = 1;
406 rc = DosFindFirst((PCSZ)&szFileSpec[0], &hDir,
407 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
408 (PVOID)&ffb, sizeof(ffb), &ul, FIL_STANDARD);
409 while (rc == NO_ERROR && ul == 1)
410 {
411 char *psz = strrchr(&ffb.achName[0], '.');
412 if (psz != NULL && (!stricmp(psz, ".cpp") || !stricmp(psz, ".c")))
413 ulRc += processFile(strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), &ffb.achName[0]), pOptions);
414
415 /* next */
416 ul = 1;
417 rc = DosFindNext(hDir, &ffb, sizeof(ffb), &ul);
418 }
419 DosFindClose(hDir);
420
421 /* 2. */
422 if (pOptions->fRecursive)
423 {
424 strcat(strcpy(&szFileSpec[0], pszDir), "\\*");
425
426 hDir = (HDIR)HDIR_CREATE;
427 ul = 1; /* important on TVFS, not on HPFS... */
428 rc = DosFindFirst((PCSZ)&szFileSpec[0], &hDir,
429 MUST_HAVE_DIRECTORY,
430 (PVOID)&ffb, sizeof(ffb), &ul, FIL_STANDARD);
431 while (rc == NO_ERROR && ul == 1)
432 {
433 if (strcmp(&ffb.achName[0], ".") != 0 && strcmp(&ffb.achName[0], "..") != 0)
434 {
435 strcat(strcat(strcpy(&szFileSpec[0], pszDir), "\\"), &ffb.achName[0]);
436 if (fFile)
437 strcat(strcat(&szFileSpec[0], "\\"), pszFile);
438 ulRc += processDir(&szFileSpec[0], pOptions);
439 }
440
441 /* next */
442 ul = 1;
443 rc = DosFindNext(hDir, &ffb, sizeof(ffb), &ul);
444 }
445 DosFindClose(hDir);
446 }
447
448 return ulRc;
449}
450
451
452/**
453 * Processes a file.
454 * @returns high word = number of signals
455 * low word = number of APIs processed. (1 or 0).
456 * @param pszFilename Filename
457 * @param pOptions Pointer to options.
458 * @sketch 1. read file into memory.
459 * 2. create line array.
460 * (3. simple preprocessing - TODO)
461 * 4. scan thru the line array, looking for APIs.
462 * 4b. when API is found, process it.
463 */
464static unsigned long processFile(const char *pszFilename, POPTIONS pOptions)
465{
466 unsigned long cSignals = 0;
467 unsigned long cAPIs = 0;
468 char *pszFile;
469
470 fprintf(phLog, "Processing '%s':\n", pszFilename);
471 /* 1.*/
472 pszFile = readFileIntoMemory(pszFilename);
473 if (pszFile != NULL)
474 {
475 char **papszLines;
476
477 /* 2.*/
478 papszLines = createLineArray(pszFile);
479 if (papszLines != NULL)
480 {
481 int i = 0;
482
483 /* 3. - TODO */
484
485 /* 4.*/
486 while (papszLines[i] != NULL)
487 {
488 if (isFunction(papszLines, i, pOptions))
489 {
490 unsigned long ulRc;
491 ulRc = processAPI(papszLines, i, i, pszFilename, pOptions);
492 cAPIs += 0x0000ffff & ulRc;
493 cSignals += ulRc >> 16;
494 }
495 else
496 i++;
497 }
498
499 free(papszLines);
500 }
501 else
502 {
503 fprintf(phSignal,"%s: error dividing file into lines.\n", pszFilename);
504 cSignals++;
505 }
506 free(pszFile);
507 }
508 else
509 {
510 fprintf(phSignal,"%s: error reading file.\n", pszFilename);
511 cSignals++;
512 }
513 fprintf(phLog, "Processing of '%s' is completed.\n\n", pszFilename);
514
515
516 return (unsigned long)((cSignals << 16) | cAPIs);
517}
518
519
520/**
521 * Processes an API function.
522 * @returns high word = number of signals
523 * low word = number of APIs processed. (1 or 0).
524 * @param papszLines Array of lines in the file.
525 * @param i Index into papszLines.
526 * @param iRet Index into papszLines. Where to resume search.
527 * @param pszFilename Filename used in the signal log.
528 * @param pOptions Pointer to options.
529 */
530static unsigned long processAPI(char **papszLines, int i, int &iRet, const char *pszFilename, POPTIONS pOptions)
531{
532 unsigned long ulRc;
533 int j;
534 FNDESC FnDesc;
535 memset(&FnDesc, 0, sizeof(FnDesc));
536
537 /* default value */
538 FnDesc.lStatus = 99;
539
540 /* precondition: isFunction is true.
541 * brief algorithm:
542 * 1. Analyse function declaration.
543 * 2. Analyse function header.
544 * 3. Log data (for debug purpose).
545 * 4. Update database
546 */
547
548 /* 1.*/
549 ulRc = analyseFnDcl(&FnDesc, papszLines, i, iRet, pszFilename, pOptions);
550 if (0x0000ffff & ulRc) /* if low word is 0 the fatal */
551 {
552 unsigned long ulRcTmp;
553 char szErrorDesc[2113]; /* due to some limitation in the latest EMX release size is 2112 and not 4096 as initially implemented. */
554
555 /* 2.*/
556 ulRcTmp = analyseFnHdr(&FnDesc, papszLines, i, pszFilename, pOptions);
557 if (ulRcTmp == ~0UL) /* check for fatal error */
558 return (0xffff0000UL & ulRc) + 0x00010000UL;
559 ulRc += 0xffff0000UL & ulRcTmp;
560
561 /* 3.*/
562 fprintf(phLog, "Name: '%s' (refcode=%ld)\n", FnDesc.pszName, FnDesc.lRefCode);
563 fprintf(phLog, " Returns: '%s'\n", FnDesc.pszReturnType != NULL ? FnDesc.pszReturnType : "<missing>");
564 fprintf(phLog, " cParams: %2d\n", FnDesc.cParams);
565 for (j = 0; j < FnDesc.cParams; j++)
566 fprintf(phLog, " Param %2d: type '%s' %*s name '%s'\n", j, FnDesc.apszParamType[j],
567 (int)(15 - strlen(FnDesc.apszParamType[j])), "", FnDesc.apszParamName[j]);
568 fprintf(phLog, " Status: %ld - '%s'\n", FnDesc.lStatus, FnDesc.pszStatus != NULL ? FnDesc.pszStatus : "<missing>");
569 fprintf(phLog, " cAuthors: %2d\n", FnDesc.cAuthors);
570 for (j = 0; j < FnDesc.cAuthors; j++)
571 fprintf(phLog, " Author %d: '%s' (refcode=%ld)\n", j, FnDesc.apszAuthor[j], FnDesc.alAuthorRefCode[j]);
572
573 /* 4.*/
574 ulRcTmp = dbUpdateFunction(&FnDesc, &szErrorDesc[0]);
575 if (ulRcTmp != 0)
576 {
577 fprintf(phSignal, "%s,%s: %s\n", pszFilename, FnDesc.pszName, &szErrorDesc[0]);
578 ulRc += ulRcTmp << 16;
579 }
580 }
581
582 return ulRc;
583}
584
585
586/**
587 * Analyses the function declaration.
588 * @returns high word = number of signals
589 * low word = number of APIs processed. (1 or 0).
590 * @param papszLines Array of lines in the file.
591 * @param i Index into papszLines.
592 * @param iRet Index into papszLines. Where to start searching again.
593 * @param pszFilename Filename used in the signal log.
594 * @param pOptions Pointer to options.
595 */
596static unsigned long analyseFnDcl(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
597 const char *pszFilename, POPTIONS pOptions)
598{
599 static long lPrevFnDll = -1L; /* fix for duplicate dlls */
600 unsigned long ulRc;
601 FNFINDBUF FnFindBuf;
602 long lFn = 0;
603
604 /* brief algorithm:
605 * 1. read function declaration using analyseFnDcl2.
606 * 2. apply name rules.
607 * 3. do a database lookup on the name.
608 * 3b. if more that one match, write a signal. (TODO: a simple fix is done, but there are holes.)
609 */
610
611 /* 1. */
612 ulRc = analyseFnDcl2(pFnDesc, papszLines, i, iRet, pszFilename, pOptions);
613 if (ulRc != 1)
614 return ulRc;
615
616 /* 2. */
617 if (pOptions->fOS2 && strncmp(pFnDesc->pszName, "OS2", 3) == 0)
618 pFnDesc->pszName += 3;
619 else if (pOptions->fCOMCTL32 && strncmp(pFnDesc->pszName, "COMCTL32", 8) == 0)
620 pFnDesc->pszName += 8;
621 else if (pOptions->fVERSION && strncmp(pFnDesc->pszName, "VERSION", 7) == 0)
622 pFnDesc->pszName += 7;
623 else if (pOptions->fOld)
624 pFnDesc->pszName += 3;
625
626 /* 3. */
627 if (!dbFindFunction(pFnDesc->pszName, &FnFindBuf))
628 {
629 fprintf(phSignal, "%s, %s: error occured while reading from database, %s\n",
630 pszFilename, pFnDesc->pszName, dbGetLastErrorDesc());
631 return 0x00010000;
632 }
633
634 if (FnFindBuf.cFns == 0)
635 {
636 fprintf(phLog, "%s was not an API\n", pFnDesc->pszName);
637 return 0;
638 }
639 else if (FnFindBuf.cFns > 1)
640 { /* 3b.*/
641 while (lFn < (int)FnFindBuf.cFns && FnFindBuf.alDllRefCode[lFn] != lPrevFnDll)
642 lFn++;
643 if (lPrevFnDll == -1L && lFn >= (int)FnFindBuf.cFns)
644 {
645 fprintf(phSignal, "%s, %s: error - more than one function by the name '%s'\n",
646 pszFilename, pFnDesc->pszName, pFnDesc->pszName);
647 return 0x00010000;
648 }
649 }
650 pFnDesc->lRefCode = FnFindBuf.alRefCode[lFn];
651 lPrevFnDll = FnFindBuf.alDllRefCode[lFn];
652
653 return ulRc;
654}
655
656
657
658/**
659 * Analyses the function declaration.
660 * No DB lockup or special function type stuff, only ODINFUNCTION is processed.
661 * @returns high word = number of signals
662 * low word = number of APIs processed. (1 or 0).
663 * @param papszLines Array of lines in the file.
664 * @param i Index into papszLines.
665 * @param iRet Index into papszLines. Where to start searching again.
666 * @param pszFilename Filename used in the signal log.
667 * @param pOptions Pointer to options.
668 */
669static unsigned long analyseFnDcl2(PFNDESC pFnDesc, char **papszLines, int i, int &iRet,
670 const char *pszFilename, POPTIONS pOptions)
671{
672 /** @sketch
673 * 1. find the '('
674 * 2. find the word ahead of the '(', this is the function name.
675 * 3. find the closing ')'
676 * 4. copy the parameters, which is between the two '()'
677 * 5. format the parameters
678 */
679
680 int iFn, iP1, iP2, j;
681 char * pszFn, *pszP1, *pszP2;
682 char * psz, *pszEnd;
683 int cArgs;
684 char * apszArgs[30];
685
686 /* 1.*/
687 iP1 = i;
688 while (papszLines[iP1] != NULL
689 && (pszP1 = strchr(papszLines[iP1], '(')) == NULL)
690 iP1++;
691 if (papszLines[iP1] == NULL)
692 {
693 fprintf(phSignal, "%d: oops! didn't find end of function!, %d\n", pszFilename, __LINE__);
694 iRet = iP1;
695 return 0x00010000;
696 }
697
698 /* 2. */
699 iFn = iP1;
700 if (papszLines[iFn] != pszP1)
701 pszFn = pszP1 - 1;
702 else
703 {
704 pszFn = papszLines[--iFn];
705 pszFn += strlen(pszFn);
706 }
707 while (iFn >= i && *pszFn == ' ')
708 {
709 if (pszFn != papszLines[iFn])
710 pszFn--;
711 else
712 {
713 pszFn = papszLines[--iFn];
714 pszFn += strlen(pszFn);
715 }
716 }
717 if (*pszFn == ' ' || *pszFn == '\0')
718 {
719 fprintf(phSignal, "%s: internal error!, %d\n", pszFilename, __LINE__);
720 iRet = iP1;
721 return 0x00010000;
722 }
723 pszFn = findStartOfWord(pszFn, papszLines[i]);
724
725 /* 3. */
726 iP2 = iP1;
727 pszP2 = pszP1 + 1;
728 while (*pszP2 != ')')
729 if (*pszP2++ == '\0')
730 if ((pszP2 = papszLines[++iP2]) == NULL)
731 break;
732
733 iRet = iP2 + 1; //assumes: only one function on a single line!
734
735 /* 4. */
736 psz = pFnDesc->szFnDclBuffer;
737 copy(pFnDesc->szFnDclBuffer, pszP1, iP1, pszP2, iP2, papszLines);
738 pszEnd = psz + strlen(psz) + 1;
739
740 /* 5.*/
741 cArgs = 0;
742 if (stricmp(psz, "(void)") != 0 && strcmp(psz, "()") != 0 && strcmp(psz, "( )"))
743 {
744 char *pszC;
745 pszC = trim(psz+1);
746 while (*pszC != '\0')
747 {
748 apszArgs[cArgs] = pszC;
749 while (*pszC != ',' && *pszC != ')' && *pszC != '\0')
750 pszC++;
751 *pszC = '\0';
752 trim(apszArgs[cArgs++]);
753
754 /* next */
755 pszC = trim(pszC + 1);
756 }
757 }
758
759 /* 6. */
760 if (strnicmp(pszFn, "ODINFUNCTION", 12) == 0)
761 {
762 if (cArgs < 2)
763 {
764 fprintf(phSignal, "%s: Invalid ODINFUNCTION function too few parameters!\n", pszFilename);
765 return 0x00010000;
766 }
767 /* return type */
768 pFnDesc->pszReturnType = apszArgs[0];
769
770 /* function name */
771 pFnDesc->pszName = apszArgs[1];
772
773 /* arguments */
774 j = 2;
775 pFnDesc->cParams = 0;
776 while (j+1 < cArgs)
777 {
778 pFnDesc->apszParamType[pFnDesc->cParams] = apszArgs[j];
779 pFnDesc->apszParamName[pFnDesc->cParams] = apszArgs[j+1];
780 pFnDesc->cParams++;
781 j += 2;
782 }
783 }
784 else
785 {
786 /* function name */
787 *pszEnd = '\0';
788 strncat(pszEnd, pszFn, findEndOfWord(pszFn) - pszFn);
789 pFnDesc->pszName = pszEnd;
790
791 /* return type - not implemented TODO/FIXME! */
792 pFnDesc->pszReturnType = NULL;
793
794 /* arguments */
795 pFnDesc->cParams = cArgs;
796 for (j = 0; j < cArgs; j++)
797 {
798 pFnDesc->apszParamName[j] = findStartOfWord(apszArgs[j] + strlen(apszArgs[j]) - 1,
799 apszArgs[j]);
800 pFnDesc->apszParamName[j][-1] = '\0';
801 pFnDesc->apszParamType[j] = trim(apszArgs[j]);
802 }
803 }
804 pOptions = pOptions;
805 return 0x00000001;
806}
807
808
809/**
810 * Analyses the function header.
811 * @returns high word = number of signals
812 * low word = number of APIs processed. (1 or 0).
813 * @param papszLines Array of lines in the file.
814 * @param i Index into papszLines.
815 * @param pszFilename Filename used in the signal log.
816 * @param pOptions Pointer to options.
817 * @sketch 1. Search backwards (start at i-1) for a comment or code.
818 * 2. If comment: (else fail)
819 * 2a. find start and end of comment.
820 * 2b. check for function header characteristics
821 * - lots of '*'s.
822 * - fields 'Status', 'Author' and 'Name'
823 * or if SDS, check for:
824 * - at least two '*'s at the begining.
825 * - fields '@status' and/or '@author'
826 * 2c. check that content of the 'Name' field matches (not SDS)
827 * 2d. read the status and author fields.
828 * @remark Supports both types of function headers, Odin32-common and SDS.
829 */
830static unsigned long analyseFnHdr(PFNDESC pFnDesc, char **papszLines, int i, const char *pszFilename, POPTIONS pOptions)
831{
832 int iStatus, iAuthor, iName, iStart, iEnd;
833 int j, jStart;
834 int fFound;
835 int fSDS = 0;
836 char *psz, *pszB;
837 char *pszAuthor = NULL;
838 unsigned long ulRc = 0x00000001;
839
840 pOptions = pOptions;
841
842 if (i < 0) /* parameter validation */
843 return 0;
844
845 /* 1. + 2a.*/
846 iEnd = i-1; /* find end */
847 j = strlen(papszLines[iEnd]);
848 j -= j > 0 ? 1 : 0;
849 fFound = 0;
850 while (iEnd >= 0 && i - iEnd < 7 && papszLines[iEnd][j] != '}' &&
851 !(fFound = (papszLines[iEnd][j] == '*' && papszLines[iEnd][j+1] == '/')))
852 if (j-- == 0)
853 if (iEnd-- > 0)
854 {
855 j = strlen(papszLines[iEnd]);
856 j -= j > 0 ? 1 : 0;
857 }
858 if (!fFound) /* fail if not found */
859 return 0;
860
861 iStart = iEnd; /* find start */
862 if (j < 2)
863 j -= (j = strlen(papszLines[--iStart])) > 1 ? 2 : j;
864 else
865 j -= 2;
866 fFound = 0;
867 while (iStart >= 0 &&
868 !(fFound = (papszLines[iStart][j] == '/' && papszLines[iStart][j+1] == '*')))
869 if (j-- == 0)
870 if (iStart-- > 0)
871 {
872 j = strlen(papszLines[iStart]);
873 j -= j > 1 ? 2 : j;
874 }
875
876 if (!fFound) /* fail if not found */
877 return 0;
878 jStart = j;
879
880 /* 2b.*/
881 if (strncmp(&papszLines[iStart][jStart], "/**", 3) != 0) /* checks that there are more than one star at start of comment */
882 return 0;
883
884 iName = iStart; /* Odin32 common */
885 while (iName <= iEnd &&
886 stristr(papszLines[iName], "* Name") == NULL)
887 iName++;
888 iStatus = iStart;
889 while (iStatus <= iEnd &&
890 stristr(papszLines[iStatus], "* Status") == NULL)
891 iStatus++;
892 iAuthor = iStart;
893 while (iAuthor <= iEnd &&
894 stristr(papszLines[iAuthor], "* Author") == NULL)
895 iAuthor++;
896
897 if (!(iName <= iEnd || iStatus <= iEnd || iAuthor <= iEnd)) /* if not found try SDS */
898 {
899 iStatus = iStart;
900 while (iStatus <= iEnd &&
901 stristr(papszLines[iStatus], "@status") == NULL)
902 iStatus++;
903 iAuthor = iStart;
904 while (iAuthor <= iEnd &&
905 stristr(papszLines[iAuthor], "@author") == NULL)
906 iAuthor++;
907 if (!(iStatus <= iEnd || iAuthor <= iEnd))
908 return 0;
909 fSDS = 1;
910 }
911
912 /* 2c.*/
913 if (iName <= iEnd && strstr(papszLines[iName], pFnDesc->pszName) == NULL)
914 fprintf(phLog, "Warning: a matching function name is not found in the name Field\n");
915
916 /* 2d.*/
917 pszB = &pFnDesc->szFnHdrBuffer[0];
918 if (!fSDS)
919 { /* Odin32 common */
920 if (iStatus <= iEnd) /* Status */
921 {
922 psz = stristr(papszLines[iStatus], "* Status") + sizeof("* Status") - 1;
923 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
924 psz++;
925 strcpy(pszB, psz);
926 trim(pszB);
927 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
928 {
929 pszB[strlen(pszB)-1] = '\0';
930 trim(pszB);
931 }
932 pFnDesc->pszStatus = pszB;
933 pszB += strlen(pszB) + 1;
934 }
935
936 if (iAuthor <= iEnd) /* Author(s) */
937 {
938 pszAuthor = pszB;
939 psz = stristr(papszLines[iAuthor], "* Author") + sizeof("* Author") - 1;
940 do
941 {
942 while (*psz != '\0' && (*psz == ' ' || *psz == ':'))
943 psz++;
944 strcpy(pszB, psz);
945 trim(pszB);
946 if (*pszB != '\0' && pszB[strlen(pszB)-1] == '*') /* just in case some have an right hand '*' column */
947 {
948 pszB[strlen(pszB)-1] = '\0';
949 trim(pszB);
950 }
951 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
952 strcat(pszB, ",");
953 pszB += strlen(pszB);
954
955 /* next */
956 psz = papszLines[++iAuthor] + 1;
957 j = 0;
958 while (*psz == ' ' && j++ < 5) psz++;
959 if (*psz == '*')
960 psz++;
961 } while (iAuthor < iEnd && *psz == ' ');
962 pszB++;
963 }
964 }
965 else
966 { /* SDS */
967 if (iStatus <= iEnd)
968 {
969 psz = stristr(papszLines[iStatus], "@status") + sizeof("@status");
970 while (*psz == ' ')
971 psz++;
972 strcpy(pszB, psz);
973 trim(pszB);
974 pszB += strlen(pszB) + 1;
975 }
976
977 if (iAuthor <= iEnd)
978 {
979 pszAuthor = pszB;
980 psz = stristr(papszLines[iAuthor], "@author") + sizeof("@author");
981 do
982 {
983 while (*psz == ' ')
984 psz++;
985 strcpy(pszB, psz);
986 trim(pszB);
987 if (*pszB != '\0' && pszB[strlen(pszB)-1] != ',')
988 strcat(pszB, ",");
989 pszB += strlen(pszB) + 1;
990
991 /* next */
992 psz = papszLines[++iAuthor] + 1;
993 j = 0;
994 while (*psz == ' ' && j++ < 5) psz++;
995 if (*psz == '*')
996 psz++;
997 } while (iAuthor <= iEnd && (*psz == ' ' || *psz == '@'));
998 pszB++;
999 }
1000 }
1001
1002 /* Status - refcodes are hardcoded here! */
1003 if (pFnDesc->pszStatus != NULL && *pFnDesc->pszStatus != '\0')
1004 {
1005 if (strstr(pFnDesc->pszStatus, "STUB") != NULL || *pFnDesc->pszStatus == '1')
1006 pFnDesc->lStatus = 1; /* STUB */
1007 else if (stristr(pFnDesc->pszStatus, "Partially") != NULL || *pFnDesc->pszStatus == '2' || *pFnDesc->pszStatus == '3')
1008 {
1009 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '2')
1010 pFnDesc->lStatus = 2; /* STUB */
1011 else
1012 pFnDesc->lStatus = 3; /* STUB */
1013 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1014 || *pFnDesc->pszStatus == '5' || *pFnDesc->pszStatus == '7')
1015 pFnDesc->lStatus += 4;
1016 }
1017 else if (stristr(pFnDesc->pszStatus, "Completely") != NULL || *pFnDesc->pszStatus == '4' || *pFnDesc->pszStatus == '5')
1018 {
1019 if (stristr(pFnDesc->pszStatus, "Tested") == NULL || *pFnDesc->pszStatus == '4')
1020 pFnDesc->lStatus = 4; /* STUB */
1021 else
1022 pFnDesc->lStatus = 5; /* STUB */
1023 if (stristr(pFnDesc->pszStatus, "Open32") != NULL
1024 || *pFnDesc->pszStatus == '8' || *pFnDesc->pszStatus == '9')
1025 pFnDesc->lStatus += 4;
1026 }
1027 else
1028 {
1029 fprintf(phSignal, "%s, %s: '%s' is not a valid status code.\n",
1030 pszFilename, pFnDesc->pszName, pFnDesc->pszStatus);
1031 ulRc += 0x00010000;
1032 }
1033 }
1034
1035 /* Author */
1036 if (pszAuthor)
1037 { /* author1, author2, author3 */
1038 j = 0;
1039 psz = trim(pszAuthor);
1040 pFnDesc->cAuthors = 0;
1041 while (*psz != '\0' && pFnDesc->cAuthors < (int)(sizeof(pFnDesc->apszAuthor) / sizeof(pFnDesc->apszAuthor[0])))
1042 {
1043 char *pszBr1 = strchr(psz, '[');
1044 char *pszBr2 = strchr(psz, ']');
1045 char *pszComma;
1046
1047 /* remove '[text]' text - this is usualy used for time/date */
1048 if (pszBr1 != NULL && pszBr2 != NULL && pszBr1 < pszBr2)
1049 while (pszBr1 <= pszBr2)
1050 *pszBr1++ = ' ';
1051
1052 /* terminate string */
1053 pszComma = strchr(psz, ',');
1054 if (pszComma != NULL)
1055 {
1056 pszAuthor = pszComma + 1;
1057 *pszComma = '\0';
1058 }
1059
1060 pFnDesc->apszAuthor[pFnDesc->cAuthors] = trim(psz);
1061 pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] =
1062 dbFindAuthor(pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1063
1064 if (pFnDesc->alAuthorRefCode[pFnDesc->cAuthors] == -1)
1065 {
1066 fprintf(phSignal, "%s, %s: author '%s' is not recognized.\n", pszFilename, pFnDesc->pszName,
1067 pFnDesc->apszAuthor[pFnDesc->cAuthors]);
1068 ulRc += 0x00010000;
1069 }
1070
1071 /* next */
1072 pFnDesc->cAuthors++;
1073 psz = trim(pszAuthor);
1074 }
1075 }
1076
1077 return ulRc;
1078}
1079
1080
1081/**
1082 * Checks if there may be an function starting at the current line.
1083 * @returns TRUE if API found, else FALSE.
1084 * @param papszLines Array of lines in the file.
1085 * @param i Index into papszLines.
1086 * @param pOptions Pointer to options.
1087 */
1088static BOOL isFunction(char **papszLines, int i, POPTIONS pOptions)
1089{
1090 if (!pOptions->fOld)
1091 { /* new API naming style */
1092 /* brief algorithm:
1093 * check that line don't start with '\\', '{' or '}'
1094 * search for '('.
1095 * if found then do
1096 * find c-word previous to '('
1097 * if found then do
1098 * check that the following condition are true:
1099 * 1. word is not 'for', 'while', 'do', 'if', 'else' or 'switch'
1100 * 2. first significant char after '(' should not be a '*'. (Fix for functionnames in function header, "BackupRead(".)
1101 * 3. find the matching ')' and check that the first significant char after it is '{'.
1102 * if 1, 2 and 3 are all true return true
1103 * return false.
1104 *
1105 * Note, for 2: spaces, newlines and comments are ignored, all other chars are significant.
1106 */
1107 char *pszP1 = papszLines[i];
1108
1109 while (*pszP1 != '\0' && *pszP1 == ' ')
1110 pszP1--;
1111 if (*pszP1 != '\0' && *pszP1 != '\\' && pszP1[1] != '\\'
1112 && *pszP1 != '{' && *pszP1 != '}')
1113 {
1114 pszP1 = strchr(papszLines[i], '(');
1115 if (pszP1 != NULL && pszP1 > papszLines[i])
1116 {
1117 int cchFnName = 0;
1118 char *pszFnName = pszP1 - 1;
1119
1120 while (pszFnName - cchFnName > papszLines[i] && pszFnName[cchFnName] == ' ')
1121 cchFnName--;
1122
1123 pszFnName = findStartOfWord(pszFnName, papszLines[i]);
1124 cchFnName += pszP1 - pszFnName;
1125 if (cchFnName >= 0)
1126 {
1127 /* 1. */
1128 if (
1129 strncmp(pszFnName, "for", cchFnName) != 0 &&
1130 strncmp(pszFnName, "while", cchFnName) != 0 &&
1131 strncmp(pszFnName, "do", cchFnName) != 0 &&
1132 strncmp(pszFnName, "if", cchFnName) != 0 &&
1133 strncmp(pszFnName, "else", cchFnName) != 0 &&
1134 strncmp(pszFnName, "switch", cchFnName) != 0
1135 )
1136 {
1137 /* 2. */
1138 int j = i;
1139 char *psz = skipInsignificantChars(papszLines, j, pszP1+1);
1140 if (psz != NULL && *psz != '*')
1141 {
1142 char *pszB = pszP1; /*'{'*/
1143
1144 /* 3. */
1145 while (*pszB != ')')
1146 if (*pszB++ == '\0')
1147 if ((pszB = papszLines[++i]) == NULL)
1148 break;
1149 if (pszB != NULL)
1150 {
1151 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1152 if (pszB != NULL && *pszB == '{')
1153 {
1154 fprintf(phLog, "Function found: %.*s\n", cchFnName, pszFnName);
1155 return TRUE;
1156 }
1157 }
1158 }
1159 }
1160 }
1161 }
1162 }
1163 }
1164 else
1165 { /* old API naming style */
1166 char *pszOS2;
1167
1168 /* brief algorithm:
1169 * search for function prefix, 'OS2'.
1170 * if found then do
1171 * check that the following condition are true:
1172 * 1. char before 'OS2' is either start-of-line (no char), space or '*'.
1173 * 2. first significant char after the 'OS2' prefixed word is a '('.
1174 * 3. find the matching ')' and check that the first significant char after it is '{'.
1175 * if 1,2 and 3 all are true return true
1176 * return false.
1177 *
1178 * Note, for 2 and 3 spaces, newlines and comments are ignored, all other chars are significant.
1179 */
1180 pszOS2 = strstr(papszLines[i], "OS2");
1181 if (pszOS2 != NULL)
1182 {
1183 /* 1.*/
1184 if (pszOS2 == papszLines[i] || pszOS2[-1] == ' ' || pszOS2[-1] == '*')
1185 {
1186 char *pszP1; /*'('*/
1187 int cchFnName;
1188
1189 /* 2. */
1190 pszP1 = findEndOfWord(pszOS2);
1191 cchFnName = pszP1 - pszOS2;
1192 pszP1 = skipInsignificantChars(papszLines, i, pszP1);
1193
1194 if (pszP1 != NULL && *pszP1 == '(')
1195 {
1196 char *pszB = pszP1; /*'{'*/
1197
1198 /* 3. */
1199 while (*pszB != ')')
1200 if (*pszB++ == '\0')
1201 if ((pszB = papszLines[++i]) == NULL)
1202 break;
1203 if (pszB != NULL)
1204 {
1205 pszB = skipInsignificantChars(papszLines, i, pszB+1);
1206 if (pszB != NULL && *pszB == '{')
1207 {
1208 fprintf(phLog, "Possible API: %.*s\n", cchFnName, pszOS2);
1209 return TRUE;
1210 }
1211 }
1212 }
1213 }
1214 }
1215 }
1216
1217 return FALSE;
1218}
1219
1220
1221/**
1222 * Skips insignificant chars (spaces, new-lines and comments)
1223 * @returns pointer to new string posision. NULL if end of file.
1224 * @param papszLines Pointer to line table.
1225 * @param i Index into line table. (current line)
1226 * @param psz Pointer where to start (within the current line).
1227 */
1228static char *skipInsignificantChars(char **papszLines, int &i, char *psz)
1229{
1230 BOOL fComment = *psz == '/' && psz[1] == '*';
1231
1232 while (fComment || *psz == ' ' || *psz == '\0' || (*psz == '/' && psz[1] == '/'))
1233 {
1234 if (*psz == '\0' || (*psz == '/' && psz[1] == '/' && !fComment))
1235 {
1236 if ((psz = papszLines[++i]) == NULL)
1237 break;
1238 }
1239 else
1240 psz++;
1241
1242 if (fComment)
1243 {
1244 if (!(fComment = *psz != '*' || psz[1] != '/'))
1245 psz += 2;
1246 else
1247 continue;
1248 }
1249
1250 if ((fComment = *psz == '/' && psz[1] == '*') == TRUE)
1251 psz += 1 + (psz[2] != '*'); // don't add two when there is a possible end comment following.
1252 }
1253
1254 return psz;
1255}
1256
1257
1258/**
1259 * Reads a file into memory.
1260 * @returns Pointer to file. This should be freed using 'free' when processing
1261 * the file is not needed.
1262 * @param pszFilename Name of file to read.
1263 * @remark Current implementation reads the file as a binary file.
1264 */
1265static char *readFileIntoMemory(const char *pszFilename)
1266{
1267 char *pszFile = NULL;
1268 int cbFile = 0; /* don't have to initialized, but it removes gcc warning (release) */
1269 FILE *phFile;
1270
1271 phFile = fopen(pszFilename, "rb");
1272 if (phFile != NULL)
1273 {
1274 if (!fseek(phFile, 0, SEEK_END)
1275 && (cbFile = (int)ftell(phFile)) > 0
1276 && !fseek(phFile, 0, SEEK_SET)
1277 )
1278 {
1279 pszFile = (char*)malloc(cbFile + 1);
1280 if (pszFile != NULL)
1281 {
1282 #if 1
1283 memset((void*)pszFile, 0, cbFile + 1);
1284 if (fread((void*)pszFile, 1, cbFile, phFile) == 1)
1285 {
1286 free(pszFile);
1287 pszFile = NULL;
1288 }
1289 #else
1290 int cLines = 0;
1291 int cch = 0;
1292 char *psz = pszFile;
1293
1294 while (!feof(phFile) && cch < cbFile &&
1295 fgets(psz, cbFile - cch, phFile) != NULL)
1296 {
1297 int cchLine;
1298 cch += cchLine = strlen(psz);
1299 psz += cchLine;
1300 cLines++;
1301 }
1302
1303 /* error check */
1304 if (cch > cbFile || !feof(phFile))
1305 {
1306 free(pszFile);
1307 pszFile = NULL;
1308 }
1309 else
1310 *psz = '\0';
1311 #endif
1312 }
1313 }
1314 fclose(phFile);
1315 }
1316
1317 return pszFile;
1318}
1319
1320
1321/**
1322 * Creates an array of lines from a "memory" file. The last entry in the array contains NULL.
1323 * @returns Pointer to the array of lines.
1324 * @param pszFile Pointer to "memory" file.
1325 */
1326static char **createLineArray(char *pszFile)
1327{
1328 char *psz = pszFile;
1329 char **papszLines = NULL;
1330 int cLines = 0;
1331
1332 while (*psz != '\0')
1333 {
1334 if (*psz == '\r')
1335 {
1336 if (psz[1] == '\n')
1337 psz++;
1338 cLines++;
1339 } else if (*psz == '\n')
1340 cLines++;
1341 psz++;
1342 }
1343 fprintf(phLog, "%d lines\n", cLines);
1344
1345 papszLines = (char**)calloc(cLines + 1, sizeof(char *));
1346 if (papszLines != NULL)
1347 {
1348 cLines = 0;
1349 psz = pszFile;
1350 while (*psz != '\0')
1351 {
1352 if (*psz == '\r')
1353 {
1354 if (psz[1] == '\n')
1355 *psz++ = '\0';
1356 papszLines[cLines++] = psz + 1;
1357 *psz = '\0';
1358 } else if (*psz == '\n')
1359 {
1360 *psz = '\0';
1361 papszLines[cLines++] = psz + 1;
1362 }
1363 psz++;
1364 }
1365 papszLines[cLines] = NULL; /* Strictly, this is not needed as we use calloc. */
1366 }
1367
1368
1369 return papszLines;
1370}
1371
1372
1373/** first char after word */
1374static char *findEndOfWord(char *psz)
1375{
1376
1377 while (*psz != '\0' &&
1378 (
1379 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
1380 ||
1381 (*psz >= '0' && *psz <= '9')
1382 ||
1383 *psz == '_'
1384 )
1385 )
1386 ++psz;
1387 return psz;
1388}
1389
1390
1391/** staring char of word */
1392static char *findStartOfWord(char *psz, const char *pszStart)
1393{
1394 char *pszR = psz;
1395 while (psz >= pszStart &&
1396 (
1397 (*psz >= 'A' && *psz <= 'Z')
1398 || (*psz >= 'a' && *psz <= 'z')
1399 || (*psz >= '0' && *psz <= '9')
1400 || *psz == '_'
1401 )
1402 )
1403 pszR = psz--;
1404 return pszR;
1405}
1406
1407
1408static char *trim(char *psz)
1409{
1410 int i;
1411 if (psz == NULL)
1412 return NULL;
1413 while (*psz == ' ' || *psz == '\t')
1414 psz++;
1415 i = strlen(psz) - 1;
1416 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
1417 i--;
1418 psz[i+1] = '\0';
1419 return psz;
1420}
1421
1422
1423/* copy: remove remarks, and unneeded spaces, ensuring no space after '(',
1424 * ensuring space after '*', ensuring no space before ',' and ')'.
1425 */
1426
1427static void copy(char *psz, char *pszFrom, int iFrom, char *pszTo, int iTo, char **papszLines)
1428{
1429 copy(psz, pszFrom - papszLines[iFrom], iFrom, pszTo - papszLines[iTo], iTo, papszLines);
1430}
1431
1432
1433static void copy(char *psz, int jFrom, int iFrom, int jTo, int iTo, char **papszLines)
1434{
1435 char chPrev = '\n';
1436 int i, j;
1437 int fComment = 0;
1438
1439 i = iFrom;
1440 j = jFrom;
1441 while (i < iTo || (i == iTo && j <= jTo))
1442 {
1443 if (papszLines[i][j] != '\0' &&
1444 !(papszLines[i][j] == '/' && papszLines[i][j+1] == '/') /* '//' coments */
1445 )
1446 {
1447 /* copy char ? */
1448 if (!fComment)
1449 {
1450 fComment = papszLines[i][j] == '/' && papszLines[i][j+1] == '*';
1451 if (!fComment && !(chPrev == ' ' && papszLines[i][j] == ' ') /* only one space char */
1452 && !(chPrev == '(' && papszLines[i][j] == ' ') /* no space after '(' */
1453 )
1454 {
1455 if (chPrev == ' ' && (papszLines[i][j] == ',' || papszLines[i][j] == ')'))
1456 psz[-1] = papszLines[i][j]; /* no space before ',' and ')' */
1457 else
1458 {
1459 chPrev = *psz++ = papszLines[i][j];
1460 if (chPrev == '*') /* ensure ' ' after '*' */
1461 chPrev = *psz++ = ' ';
1462 }
1463 }
1464 }
1465 else
1466 if ((fComment = papszLines[i][j] != '*' || papszLines[i][j+1] != '/') == 0)
1467 j++;
1468 j++;
1469 }
1470 else
1471 {
1472 /* next */
1473 j = 0;
1474 i++;
1475 if (chPrev != ' ' && chPrev != '(')
1476 chPrev = *psz++ = ' ';
1477 }
1478 }
1479 *psz = '\0';
1480}
1481
1482
1483/**
1484 * Upcases a char.
1485 * @returns Upper case of the char given in ch.
1486 * @param ch Char to capitalize.
1487 */
1488inline char upcase(char ch)
1489{
1490 return ch >= 'a' && ch <= 'z' ? (char)(ch - ('a' - 'A')) : ch;
1491}
1492
1493
1494/**
1495 * Searches for a substring in a string.
1496 * @returns Pointer to start of substring when found, NULL when not found.
1497 * @param pszStr String to be searched.
1498 * @param pszSubStr String to be searched.
1499 * @remark Depends on the upcase function.
1500 */
1501static char *stristr(const char *pszStr, const char *pszSubStr)
1502{
1503 int cchSubStr = strlen(pszSubStr);
1504 int i = 0;
1505
1506 while (*pszStr != '\0' && i < cchSubStr)
1507 {
1508 i = 0;
1509 while (i < cchSubStr && pszStr[i] != '\0' &&
1510 (upcase(pszStr[i]) == upcase(pszSubStr[i])))
1511 i++;
1512 pszStr++;
1513 }
1514
1515 return (char*)(*pszStr != '\0' ? pszStr - 1 : NULL);
1516}
1517
Note: See TracBrowser for help on using the repository browser.