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

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

Bugfixes.

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