source: trunk/tools/fastdep/fastdep.c@ 3169

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

Optimize use of strlwr. Use fopen(.., "rb") to speed up reads.

File size: 80.3 KB
Line 
1/* $Id: fastdep.c,v 1.16 2000-03-19 16:23:55 bird Exp $
2 *
3 * Fast dependents. (Fast = Quick and Dirty!)
4 *
5 * Copyright (c) 1999-2000 knut st. osmundsen
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11/*******************************************************************************
12* Defined Constants And Macros *
13*******************************************************************************/
14#define INCL_DOSERRORS
15#define INCL_FILEMGR
16
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <os2.h>
22#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25#include <direct.h>
26
27#include "avl.h"
28
29#ifndef INLINE
30# if defined(__IBMC__)
31# define INLINE _Inline
32# elif defined(__IBMCPP__)
33# define INLINE inline
34# else
35# error "unknown compiler - inline keyword unknown!"
36# endif
37#endif
38
39/*
40 * This following section is used while testing fastdep.
41 * stdio.h should be included; string.h never included.
42 */
43/*
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47*/
48
49#if 1
50#include <stdio.h>
51#else
52#include <string.h>
53#include <string.h>
54#endif
55
56/*
57 */ /* */ /*
58#include <string.h>
59 */
60#if 1
61# if 1
62 #if 0
63# include <string.h>
64 #else
65# if 1
66 #if 1
67 #if 0
68# include <string.h>
69 #else /* */ /*
70*/
71 # include <stdio.h>
72 #endif
73 #endif
74 #endif
75 #endif
76 #endif
77#endif
78
79/*******************************************************************************
80* Structures and Typedefs *
81*******************************************************************************/
82typedef struct _Options
83{
84 const char * pszInclude;
85 const char * pszExclude;
86 BOOL fExcludeAll;
87 const char * pszObjectExt;
88 const char * pszObjectDir;
89 BOOL fObjectDir; /* replace object directory? */
90 const char * pszRsrcExt;
91 BOOL fObjRule;
92 BOOL fNoObjectPath;
93 BOOL fSrcWhenObj;
94 BOOL fAppend; /* append to the output file, not overwrite it. */
95 BOOL fCheckCyclic; /* allways check for cylic dependency before inserting an dependent. */
96 BOOL fCacheSearchDirs; /* cache entire search dirs. */
97} OPTIONS, *POPTIONS;
98
99
100/*
101 * Language specific analysis functions type.
102 */
103typedef int ( _FNLANG) (const char *pszFilename, void *pvFile,
104 BOOL fHeader, POPTIONS pOptions);
105typedef _FNLANG *PFNLANG;
106
107
108/**
109 * This struct holds the static configuration of the util.
110 */
111typedef struct _ConfigEntry
112{
113 const char **papszExts; /* Pointer to an array of pointer to extentions for this handler. */
114 /* If NULL this is the last entry. */
115 int iFirstHdr; /* Index into the papszExts array of the first headerfile/copybook. */
116 /* Set it to the NULL element of the array if no headers for this extention. */
117 /* A non-header file may get a object rule. */
118 PFNLANG pfn; /* Pointer to handler function. */
119} CONFIGENTRY, *PCONFIGENTRY;
120
121
122/**
123 * Dependant Rule
124 */
125typedef struct _DepRule
126{
127 AVLNODECORE avlCore;
128 char * pszRule; /* Pointer to rule name */
129 int cDeps; /* Entries in the dependant array. */
130 char ** papszDep; /* Pointer to an array of pointers to dependants. */
131} DEPRULE, *PDEPRULE;
132
133
134/**
135 * Filename cache entry.
136 */
137#define FCACHEENTRY AVLNODECORE
138#define PFCACHEENTRY PAVLNODECORE
139
140
141/*******************************************************************************
142* Internal Functions *
143*******************************************************************************/
144static void syntax(void);
145static int makeDependent(const char *pszFilename, POPTIONS pOptions);
146
147int langC_CPP(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
148int langAsm(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
149int langRC(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
150int langCOBOL(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
151
152
153/* string operations */
154static int strnicmpwords(const char *pszS1, const char *pszS2, int cch);
155
156/* file operations */
157static char *fileNormalize(char *pszFilename);
158static char *fileNormalize2(const char *pszFilename, char *pszBuffer);
159 char *filePath(const char *pszFilename, char *pszBuffer);
160static char *filePathSlash(const char *pszFilename, char *pszBuffer);
161static char *filePathSlash2(const char *pszFilename, char *pszBuffer);
162static char *fileName(const char *pszFilename, char *pszBuffer);
163static char *fileNameNoExt(const char *pszFilename, char *pszBuffer);
164static char *fileExt(const char *pszFilename, char *pszBuffer);
165
166/* filecache operations */
167static BOOL filecacheAddFile(const char *pszFilename);
168static BOOL filecacheAddDir(const char *pszDir);
169INLINE BOOL filecacheFind(const char *pszFilename);
170INLINE BOOL filecacheIsDirCached(const char *pszDir);
171
172/* pathlist operations */
173static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer, POPTIONS pOptions);
174
175/* word operations */
176static char *findEndOfWord(char *psz);
177#if 0 /* not used */
178static char *findStartOfWord(char *psz, const char *pszStart);
179#endif
180
181/* file helpers */
182static signed long fsize(FILE *phFile);
183
184/* text helpers */
185INLINE char *trim(char *psz);
186INLINE char *trimR(char *psz);
187
188/* textbuffer */
189static void *textbufferCreate(const char *pszFilename);
190static void textbufferDestroy(void *pvBuffer);
191static char *textbufferNextLine(void *pvBuffer, char *psz);
192static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer);
193
194/* depend workers */
195static BOOL depReadFile(const char *pszFilename, POPTIONS pOptions);
196static BOOL depWriteFile(const char *pszFilename);
197static void depRemoveAll(void);
198static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt);
199static BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic);
200#if 0 /* not used */
201static BOOL depCleanFile(const char *pszFilename);
202#endif
203static BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep);
204
205
206/*******************************************************************************
207* Global Variables *
208*******************************************************************************/
209/*
210 * Pointer to the list of dependencies.
211 */
212static PDEPRULE pdepTree = NULL;
213
214
215/*
216 * Filecache - tree starts here.
217 */
218static PFCACHEENTRY pfcTree = NULL;
219static unsigned cfcNodes = 0;
220static PFCACHEENTRY pfcDirTree = NULL;
221
222
223/*
224 * Current directory stuff
225 */
226static char szCurDir[CCHMAXPATH];
227static int aiSlashes[CCHMAXPATH];
228static int cSlashes;
229
230
231/*
232 * Environment variables used.
233 * (These has the correct case.)
234 */
235static char * pszIncludeEnv;
236
237
238/*
239 * Configuration stuff.
240 */
241static const char pszDefaultDepFile[] = ".depend";
242static const char *apszExtC_CPP[] = {"c", "sqc", "cpp", "h", "hpp", NULL};
243static const char *apszExtAsm[] = {"asm", "inc", NULL};
244static const char *apszExtRC[] = {"rc", "dlg", NULL};
245static const char *apszExtCOBOL[] = {"cbl", "cob", "sqb", NULL};
246static CONFIGENTRY aConfig[] =
247{
248 {
249 apszExtC_CPP,
250 3,
251 langC_CPP,
252 },
253
254 {
255 apszExtAsm,
256 1,
257 langAsm,
258 },
259
260 {
261 apszExtRC,
262 1,
263 langRC,
264 },
265
266 {
267 apszExtCOBOL,
268 3,
269 langCOBOL,
270 },
271
272 /* terminating entry */
273 {
274 NULL,
275 -1,
276 NULL
277 }
278};
279
280
281/**
282 * Main function.
283 * @returns 0 on success.
284 * -n count of failiures.
285 * @param
286 * @param
287 * @equiv
288 * @precond
289 * @methdesc
290 * @result
291 * @time
292 * @sketch
293 * @algo
294 * @remark
295 */
296int main(int argc, char **argv)
297{
298 int rc = 0;
299 int argi = 1;
300 int i;
301 char * psz;
302 const char *pszDepFile = pszDefaultDepFile;
303 char achBuffer[4096];
304
305 static char szObjectDir[CCHMAXPATH];
306 static char szObjectExt[64] = "obj";
307 static char szRsrcExt[64] = "res";
308 static char szInclude[32768] = ";";
309 static char szExclude[32768] = ";";
310
311 OPTIONS options =
312 {
313 szInclude, /* pszInclude */
314 szExclude, /* pszExclude */
315 FALSE, /* fExcludeAll */
316 szObjectExt, /* pszObjectExt */
317 szObjectDir, /* pszObjectDir */
318 FALSE, /* fObjectDir */
319 szRsrcExt, /* pszRsrcExt */
320 TRUE, /* fObjRule */
321 FALSE, /* fNoObjectPath */
322 TRUE, /* fSrcWhenObj */
323 FALSE, /* fAppend */
324 TRUE, /* fCheckCyclic */
325 TRUE /* fCacheSearchDirs */
326 };
327
328 szObjectDir[0] = '\0';
329
330 if (argc == 1)
331 {
332 syntax();
333 return -87;
334 }
335
336 /*
337 * Initiate current directory stuff
338 */
339 if (_getcwd(szCurDir, sizeof(szCurDir)) == NULL)
340 {
341 fprintf(stderr, "fatal error: failed to get current directory\n");
342 return -88;
343 }
344 strlwr(szCurDir);
345 aiSlashes[0] = 0;
346 for (i = 1, cSlashes; szCurDir[i] != '\0'; i++)
347 {
348 if (szCurDir[i] == '/')
349 szCurDir[i] = '\\';
350 if (szCurDir[i] == '\\')
351 aiSlashes[cSlashes++] = i;
352 }
353 if (szCurDir[i-1] != '\\')
354 {
355 aiSlashes[cSlashes] = i;
356 szCurDir[i++] = '\\';
357 szCurDir[i] = '\0';
358 }
359
360
361 /*
362 * Initiate environment variables used: INCLUDE
363 */
364 psz = getenv("INCLUDE");
365 if (psz != NULL)
366 {
367 pszIncludeEnv = strdup(psz);
368 strlwr(pszIncludeEnv);
369 }
370 else
371 pszIncludeEnv = "";
372
373
374 /*
375 * parse arguments
376 */
377 while (argi < argc)
378 {
379 if (argv[argi][0] == '-' || argv[argi][0] == '/')
380 {
381 /* parameters */
382 switch (argv[argi][1])
383 {
384 case 'A':
385 case 'a': /* Append to the output file */
386 options.fAppend = argv[argi][2] != '-';
387 break;
388
389 case 'D':
390 case 'd': /* "-d <filename>" */
391 {
392 const char *pszOld = pszDepFile;
393 if (argv[argi][2] != '\0')
394 pszDepFile = &argv[argi][2];
395 else
396 {
397 if (argi + 1 < argc)
398 pszDepFile = argv[++argi];
399 else
400 {
401 fprintf(stderr, "invalid parameter -d, filename missing!\n");
402 return -1;
403 }
404 }
405
406 /* if dependencies are generated we'll flush them to the old filename */
407 if (pdepTree != NULL && pszOld != pszDepFile)
408 {
409 if (!depWriteFile(pszOld))
410 fprintf(stderr, "error: failed to write (flush) dependencies.\n");
411 depRemoveAll();
412 }
413 break;
414 }
415
416 case 'C': /* forced directory cache 'ca' or cylic check 'cy'*/
417 case 'c':
418 if (argv[argi][2] == 'a' || argv[argi][2] == 'A')
419 options.fCacheSearchDirs = TRUE;
420 else if ((argv[argi][2] == 'y' || argv[argi][2] == 'Y'))
421 options.fCheckCyclic = argv[argi][3] != '-';
422 break;
423
424 case 'E': /* list of paths. If a file is found in one of these directories the */
425 case 'e': /* filename will be used without the directory path. */
426 /* Eall<[+]|-> ? */
427 if (strlen(&argv[argi][1]) <= 5 && strnicmp(&argv[argi][1], "Eall", 4) == 0)
428 {
429 options.fExcludeAll = argv[argi][5] != '-';
430 break;
431 }
432 /* path or path list */
433 if (strlen(argv[argi]) > 2)
434 strcat(szExclude, &argv[argi][2]);
435 else
436 {
437 strcat(szExclude, argv[argi+1]);
438 argi++;
439 }
440 strlwr(szExclude);
441 if (szExclude[strlen(szExclude)-1] != ';')
442 strcat(szExclude, ";");
443 break;
444
445 case 'I': /* optional include path. This has precedence over the INCLUDE environment variable. */
446 case 'i':
447 if (strlen(argv[argi]) > 2)
448 strcat(szInclude, &argv[argi][2]);
449 else
450 {
451 strcat(szInclude, argv[argi+1]);
452 argi++;
453 }
454 strlwr(szInclude);
455 if (szInclude[strlen(szInclude)-1] != ';')
456 strcat(szInclude, ";");
457 break;
458
459 case 'n': /* no object path , -N<[+]|-> */
460 case 'N':
461 if (strlen(argv[argi]) <= 1+1+1)
462 options.fNoObjectPath = argv[argi][2] != '-';
463 else
464 {
465 fprintf(stderr, "error: invalid parameter!, '%s'\n", argv[argi]);
466 return -1;
467 }
468 break;
469
470 case 'o': /* object base directory, Obj or Obr<[+]|-> */
471 case 'O':
472 if (strlen(&argv[argi][1]) <= 4 && strnicmp(&argv[argi][1], "Obr", 3) == 0)
473 {
474 options.fObjRule = argv[argi][4] != '-';
475 break;
476 }
477
478 if (strlen(&argv[argi][1]) >= 4 && strnicmp(&argv[argi][1], "Obj", 3) == 0)
479 {
480 if (strlen(argv[argi]) > 4)
481 strcpy(szObjectExt, argv[argi]+4);
482 else
483 {
484 strcpy(szObjectExt, argv[argi+1]);
485 argi++;
486 }
487 break;
488 }
489
490 /* path: -o or -o- */
491 options.fObjectDir = TRUE;
492 if (strlen(argv[argi]) > 2)
493 {
494 if (argv[argi][2] == '-') /* no object path */
495 szObjectDir[0] = '\0';
496 else
497 strcpy(szObjectDir, argv[argi]+2);
498 }
499 else
500 {
501 strcpy(szObjectDir, argv[argi+1]);
502 argi++;
503 }
504 if (szObjectDir[0] != '\0'
505 && szObjectDir[strlen(szObjectDir)-1] != '\\'
506 && szObjectDir[strlen(szObjectDir)-1] != '/'
507 )
508 strcat(szObjectDir, "\\");
509 break;
510
511 case 'r':
512 case 'R':
513 if (strlen(argv[argi]) > 2)
514 strcpy(szObjectExt, argv[argi]+2);
515 else
516 {
517 strcpy(szObjectExt, argv[argi+1]);
518 argi++;
519 }
520 break;
521
522 case 'h':
523 case 'H':
524 case '?':
525 syntax();
526 return 1;
527
528 default:
529 fprintf(stderr, "error: invalid parameter! '%s'\n", argv[argi]);
530 return -1;
531 }
532
533 }
534 else if (argv[argi][0] == '@')
535 { /*
536 * Parameter file (debugger parameter length restrictions led to this):
537 * Create a textbuffer.
538 * Parse the file and create a new parameter vector.
539 * Set argv to the new parameter vector, argi to 0 and argc to
540 * the parameter count.
541 * Restrictions: Parameters enclosed in "" is not implemented.
542 * No commandline parameters are processed after the @file
543 */
544 char *pszBuffer = (char*)textbufferCreate(&argv[argi][1]); /* !ASSUMS! that pvBuffer is the file string! */
545 if (pszBuffer != NULL)
546 {
547 char **apszArgs = NULL;
548 char *psz = pszBuffer;
549 int i = 0;
550
551 while (*psz != '\0')
552 {
553 /* find end of parameter word */
554 char *pszEnd = psz + 1;
555 char ch = *pszEnd;
556 while (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' && ch != '\0')
557 ch = *++pszEnd;
558
559 /* allocate more arg array space? */
560 if ((i % 512) == 0)
561 {
562 apszArgs = realloc(apszArgs, sizeof(char*) * 512);
563 if (apszArgs == NULL)
564 {
565 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
566 return -8;
567 }
568 }
569 *pszEnd = '\0';
570 apszArgs[i++] = psz;
571
572 /* next */
573 psz = pszEnd + 1;
574 ch = *psz;
575 while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
576 ch = *++psz;
577 }
578
579 argc = i;
580 argi = 0;
581 argv = apszArgs;
582 continue;
583 }
584 else
585 {
586 fprintf(stderr, "error: could not open parameter file\n");
587 return -1;
588 }
589 }
590 else
591 { /* not a parameter! */
592 ULONG ulRc;
593 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
594 HDIR hDir = HDIR_CREATE;
595 ULONG cFiles = ~0UL;
596 int i;
597
598 /*
599 * If append option is specified we'll have to read the existing dep file
600 * before starting adding new dependencies.
601 */
602 if (pdepTree == NULL && options.fAppend)
603 depReadFile(pszDepFile, &options);
604
605 /*
606 * Search for the files specified.
607 */
608 ulRc = DosFindFirst(argv[argi], &hDir,
609 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
610 pfindbuf3, sizeof(achBuffer), &cFiles, FIL_STANDARD);
611 if (!options.fCacheSearchDirs)
612 options.fCacheSearchDirs = cFiles > 25;
613 while (ulRc == NO_ERROR)
614 {
615 for (i = 0;
616 i < cFiles;
617 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
618 )
619 {
620 char *psz;
621 char szSource[CCHMAXPATH];
622
623 /*
624 * Make full path.
625 */
626 if ((psz = strrchr(argv[argi], '\\')) || (psz = strrchr(argv[argi], '/')))
627 {
628 strncpy(szSource, argv[argi], psz - argv[argi] + 1);
629 szSource[psz - argv[argi] + 1] = '\0';
630 }
631 else
632 szSource[0] = '\0';
633 strcat(szSource, pfindbuf3->achName);
634 strlwr(szSource);
635
636 /*
637 * Analyse the file.
638 */
639 rc -= makeDependent(&szSource[0], &options);
640 }
641
642 /* next file */
643 cFiles = ~0UL;
644 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
645 ulRc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
646 }
647 DosFindClose(hDir);
648 }
649 /* next */
650 argi++;
651 }
652
653 /* Write the depend file! */
654 if (!depWriteFile(pszDepFile))
655 fprintf(stderr, "error: failed to write dependencies file!\n");
656 #if 0
657 printf("cfcNodes=%d\n", cfcNodes);
658 #endif
659
660 return rc;
661}
662
663
664/**
665 * Displays the syntax description for this util.
666 * @status completely implemented.
667 * @author knut st. osmundsen
668 */
669static void syntax(void)
670{
671 printf(
672 "FastDep v0.2\n"
673 "Quick and dirty dependency scanner. Creates a makefile readable depend file.\n"
674 "\n"
675 "Syntax: FastDep [-a<[+]|->] [-ca] [-cy<[+]|->] [-d <outputfn>]\n"
676 " [-e <excludepath>] [-eall<[+]|->] [-i <include>] [-n<[+]|->]\n"
677 " [-o <objdir>] [-obr<[+]|->] <files>\n"
678 "\n"
679 " -a<[+]|-> Append to the output file. Default: Overwrite.\n"
680 " -ca Force search directory caching.\n"
681 " Default: cache if more that 25 files are to be searched.\n"
682 " (more than 25 in the first file expression.)\n"
683 " -cy<[+]|-> Check for cylic dependencies. Default: -cy-\n"
684 " -d <outputfn> Output filename. Default: %s\n"
685 " -e excludepath Exclude paths. If a filename is found in any\n"
686 " of these paths only the filename is used, not\n"
687 " the path+filename (which is default) (don't work?).\n"
688 " -eall<[+]|-> Include and source filenames, paths or no paths.\n"
689 " -eall+: No path are added to the filename.\n"
690 " -eall-: The filename is appended the include path\n"
691 " was found in.\n"
692 " Default: eall-\n"
693 " -i <include> Additional include paths. INCLUDE is searched after this.\n"
694 " -n<[+]|-> No path for object files in the rules.\n"
695 " -o <objdir> Path were object files are placed. This path replaces the\n"
696 " entire filename path\n"
697 " -o- No object path\n"
698 " -obr<[+]|-> -obr+: Object rule.\n"
699 " -obr-: No object rule, rule for source filename is generated.\n"
700 " -obj[ ]<objext> Object extention. Default: obj\n"
701 " -r[ ]<rsrcext> Resource binary extention. Default: res\n"
702 " <files> Files to scan. Wildchars are allowed.\n"
703 "\n"
704 " copyright (c) 1999-2000 knut st. osmundsen (knut.stange.osmundsen@pmsc.no)\n",
705 pszDefaultDepFile
706 );
707}
708
709
710/**
711 * Generates depend info on this file, these are stored internally
712 * and written to file later.
713 * @returns
714 * @param pszFilename Pointer to source filename. Correct case is assumed!
715 * @param pOptions Pointer to options struct.
716 * @status completely implemented.
717 * @author knut st. osmundsen
718 */
719static int makeDependent(const char *pszFilename, POPTIONS pOptions)
720{
721 int rc = -1;
722 void * pvFile;
723
724 pvFile = textbufferCreate(pszFilename);
725 if (pvFile != NULL)
726 {
727 char szExt[CCHMAXPATH];
728 PCONFIGENTRY pCfg = &aConfig[0];
729 BOOL fHeader;
730
731 /*
732 * Find which filetype this is...
733 */
734 fileExt(pszFilename, szExt);
735 while (pCfg->papszExts != NULL)
736 {
737 const char **ppsz = pCfg->papszExts;
738 while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
739 ppsz++;
740 if (*ppsz != NULL)
741 {
742 fHeader = &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
743 break;
744 }
745 pCfg++;
746 }
747
748 /* Found? */
749 if (pCfg->papszExts != NULL)
750 rc = (*pCfg->pfn)(pszFilename, pvFile, fHeader, pOptions);
751 else
752 {
753 if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
754 fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
755 rc = 0;
756 }
757
758 textbufferDestroy(pvFile);
759 }
760 else
761 fprintf(stderr, "failed to open '%s'\n", pszFilename);
762
763 return rc;
764}
765
766
767/**
768 * Generates depend info on this C or C++ file, these are stored internally
769 * and written to file later.
770 * @returns 0 on success.
771 * !0 on error.
772 * @param pszFilename Pointer to source filename. Correct case is assumed!
773 * @param pvFile Pointer to file textbuffer.
774 * @param pOptions Pointer to options struct.
775 * @status completely implemented.
776 * @author knut st. osmundsen
777 */
778int langC_CPP(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
779{
780 void * pvRule; /* Handle to the current rule. */
781 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
782 int iLine; /* Linenumber. */
783 void * pv = NULL; /* An index used by textbufferGetNextLine. */
784 BOOL fComment; /* TRUE when within a multiline comment. */
785 /* FALSE when not within a multiline comment. */
786 int iIfStack; /* StackPointer. */
787 struct IfStackEntry
788 {
789 int fIncluded : 1; /* TRUE: include this code;
790 * FALSE: excluded */
791 int fIf : 1; /* TRUE: #if part of the expression.
792 * FALSE: #else part of the expression. */
793 int fSupported : 1; /* TRUE: supported if/else statement
794 * FALSE: unsupported all else[<something>] are ignored
795 * All code is included.
796 */
797 } achIfStack[256];
798
799
800 /**********************************/
801 /* Add the depend rule */
802 /**********************************/
803 if (pOptions->fObjRule && !fHeader)
804 {
805 if (pOptions->fNoObjectPath)
806 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
807 else
808 pvRule = depAddRule(pOptions->fObjectDir ?
809 pOptions->pszObjectDir :
810 filePathSlash(pszFilename, szBuffer),
811 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
812 pOptions->pszObjectExt);
813
814 if (pOptions->fSrcWhenObj && pvRule)
815 depAddDepend(pvRule,
816 pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
817 pOptions->fCheckCyclic);
818 }
819 else
820 pvRule = depAddRule(pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer), NULL, NULL);
821
822 /* duplicate rule? */
823 if (pvRule == NULL)
824 return 0;
825
826
827 /*******************/
828 /* find dependants */
829 /*******************/
830 /* Initiate the IF-stack, comment state and line number. */
831 iIfStack = 0;
832 achIfStack[iIfStack].fIf = TRUE;
833 achIfStack[iIfStack].fIncluded = TRUE;
834 achIfStack[iIfStack].fSupported = TRUE;
835 fComment = FALSE;
836 iLine = 0;
837 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
838 {
839 /* search for #include */
840 register char *pszC;
841 int cbLen;
842 int i = 0;
843 iLine++;
844
845 /* skip blank chars */
846 cbLen = strlen(szBuffer);
847 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
848 i++;
849
850 /* preprocessor statement? */
851 if (!fComment && szBuffer[i] == '#')
852 {
853 /*
854 * Preprocessor checks
855 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
856 * Depending on the word afterwards we'll take some different actions.
857 * So we'll start of by extracting that word and make a string swich on it.
858 * Note that there might be some blanks between the hash and the word.
859 */
860 int cchWord;
861 char * pszEndWord;
862 char * pszArgument;
863 i++; /* skip hash ('#') */
864 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
865 i++;
866 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
867 cchWord = pszEndWord - &szBuffer[i];
868
869 /*
870 * Find the argument by skipping the blanks.
871 */
872 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
873 pszArgument++;
874
875 /*
876 * string switch.
877 */
878 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
879 {
880 /*
881 * #include
882 *
883 * Are we in a state where this file is to be included?
884 */
885 if (achIfStack[iIfStack].fIncluded)
886 {
887 char szFullname[CCHMAXPATH];
888 char *psz;
889 BOOL f = FALSE;
890 int j;
891
892 /* extract info between "" or <> */
893 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
894 i++;
895 i++; /* skip '"' or '<' */
896
897 /* if invalid statement then continue with the next line! */
898 if (!f) continue;
899
900 /* find end */
901 j = f = 0;
902 while (i + j < cbLen && j < CCHMAXPATH &&
903 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
904 j++;
905
906 /* if invalid statement then continue with the next line! */
907 if (!f) continue;
908
909 /* copy filename */
910 strncpy(szFullname, &szBuffer[i], j);
911 szFullname[j] = '\0'; /* ensure terminatition. */
912 strlwr(szFullname);
913
914 /* find include file! */
915 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
916 if (psz == NULL)
917 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer, pOptions);
918
919 /* did we find the include? */
920 if (psz != NULL)
921 {
922 char szBuffer2[CCHMAXPATH];
923 if (pOptions->fExcludeAll ||
924 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2, pOptions) != NULL
925 )
926 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
927 else
928 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
929 }
930 else
931 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
932 pszFilename, iLine, szFullname);
933 }
934 }
935 else
936 /*
937 * #if
938 */
939 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
940 { /* #if 0 and #if <1-9> are supported */
941 pszEndWord = findEndOfWord(pszArgument);
942 iIfStack++;
943 if ((pszEndWord - pszArgument) == 1
944 && *pszArgument >= '0' && *pszArgument <= '9')
945 {
946 if (*pszArgument != '0')
947 achIfStack[iIfStack].fIncluded = TRUE;
948 else
949 achIfStack[iIfStack].fIncluded = FALSE;
950 }
951 else
952 achIfStack[iIfStack].fSupported = FALSE;
953 achIfStack[iIfStack].fIncluded = TRUE;
954 achIfStack[iIfStack].fIf = TRUE;
955 }
956 else
957 /*
958 * #else
959 */
960 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
961 {
962 if (achIfStack[iIfStack].fSupported)
963 {
964 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
965 achIfStack[iIfStack].fIncluded = FALSE;
966 else
967 achIfStack[iIfStack].fIncluded = TRUE;
968 }
969 achIfStack[iIfStack].fIf = FALSE;
970 }
971 else
972 /*
973 * #endif
974 */
975 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
976 { /* Pop the if-stack. */
977 if (iIfStack > 0)
978 iIfStack--;
979 else
980 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
981 }
982 /*
983 * general if<something> and elseif<something> implementations
984 */
985 else
986 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
987 {
988 achIfStack[iIfStack].fSupported = FALSE;
989 achIfStack[iIfStack].fIncluded = TRUE;
990 }
991 else
992 if (strncmp(&szBuffer[i], "if", 2) == 0)
993 {
994 iIfStack++;
995 achIfStack[iIfStack].fIf = TRUE;
996 achIfStack[iIfStack].fSupported = FALSE;
997 achIfStack[iIfStack].fIncluded = TRUE;
998 }
999 /* The rest of them aren't implemented yet.
1000 else if (strncmp(&szBuffer[i], "if") == 0)
1001 {
1002 }
1003 */
1004 }
1005
1006 /*
1007 * Comment checks.
1008 * -Start at first non-blank.
1009 * -Loop thru the line since we might have more than one
1010 * comment statement on a single line.
1011 */
1012 pszC = &szBuffer[i];
1013 while (pszC != NULL && *pszC != '\0')
1014 {
1015 if (fComment)
1016 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
1017 else
1018 {
1019 char *pszLC;
1020 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
1021 pszC = strstr(pszC, "/*"); /* look for start comment mark */
1022 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
1023 break; /* muliline comment mark we'll ignore the multiline mark. */
1024 }
1025
1026 /* Comment mark found? */
1027 if (pszC != NULL)
1028 {
1029 fComment = !fComment;
1030 pszC += 2; /* skip comment mark */
1031
1032 /* debug */
1033 /*
1034 if (fComment)
1035 fprintf(stderr, "starts at line %d\n", iLine);
1036 else
1037 fprintf(stderr, "ends at line %d\n", iLine);
1038 */
1039 }
1040 }
1041 } /*while*/
1042
1043 return 0;
1044}
1045
1046
1047/**
1048 * Generates depend info on this file, these are stored internally
1049 * and written to file later.
1050 * @returns 0 on success.
1051 * !0 on error.
1052 * @param pszFilename Pointer to source filename. Correct case is assumed!
1053 * @param pvFile Pointer to file textbuffer.
1054 * @param pOptions Pointer to options struct.
1055 * @status completely implemented.
1056 * @author knut st. osmundsen
1057 */
1058int langAsm(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1059{
1060 void * pvRule; /* Handle to the current rule. */
1061 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1062 int iLine; /* current line number */
1063 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1064
1065
1066 /**********************************/
1067 /* Add the depend rule */
1068 /**********************************/
1069 if (pOptions->fObjRule && !fHeader)
1070 {
1071 if (pOptions->fNoObjectPath)
1072 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
1073 else
1074 pvRule = depAddRule(pOptions->fObjectDir ?
1075 pOptions->pszObjectDir :
1076 filePathSlash(pszFilename, szBuffer),
1077 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1078 pOptions->pszObjectExt);
1079
1080 if (pOptions->fSrcWhenObj && pvRule)
1081 depAddDepend(pvRule,
1082 pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1083 pOptions->fCheckCyclic);
1084 }
1085 else
1086 pvRule = depAddRule(pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer), NULL, NULL);
1087
1088 /* duplicate rule? */
1089 if (pvRule == NULL)
1090 return 0;
1091
1092
1093 /*******************/
1094 /* find dependants */
1095 /*******************/
1096 iLine = 0;
1097 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1098 {
1099 /* search for include */
1100 int cbLen;
1101 int i = 0;
1102 iLine++;
1103
1104 /* skip blank chars */
1105 cbLen = strlen(szBuffer);
1106 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1107 i++;
1108
1109 /* is this an include? */
1110 if (strnicmp(&szBuffer[i], "include", 7) == 0
1111 && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
1112 )
1113 {
1114 char szFullname[CCHMAXPATH];
1115 char *psz;
1116 int j;
1117
1118 /* skip to first no blank char */
1119 i += 7;
1120 while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1121 i++;
1122
1123 /* comment check - if comment found, no filename was given. continue. */
1124 if (szBuffer[i] == ';') continue;
1125
1126 /* find end */
1127 j = 0;
1128 while (i + j < cbLen
1129 && j < CCHMAXPATH
1130 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
1131 && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';' && szBuffer[i+j] != '\r'
1132 )
1133 j++;
1134
1135 /* copy filename */
1136 strncpy(szFullname, &szBuffer[i], j);
1137 szFullname[j] = '\0'; /* ensure terminatition. */
1138 strlwr(szFullname);
1139
1140 /* find include file! */
1141 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1142 if (psz == NULL)
1143 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer, pOptions);
1144
1145 /* Did we find the include? */
1146 if (psz != NULL)
1147 {
1148 char szBuffer2[CCHMAXPATH];
1149 if (pOptions->fExcludeAll ||
1150 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2, pOptions) != NULL
1151 )
1152 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1153 else
1154 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1155 }
1156 else
1157 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1158 pszFilename, iLine, szFullname);
1159 }
1160 } /*while*/
1161
1162 return 0;
1163}
1164
1165
1166/**
1167 * Generates depend info on this Resource file, these are stored internally
1168 * and written to file later.
1169 * @returns 0 on success.
1170 * !0 on error.
1171 * @param pszFilename Pointer to source filename. Correct case is assumed!
1172 * @param pvFile Pointer to file textbuffer.
1173 * @param pOptions Pointer to options struct.
1174 * @status completely implemented.
1175 * @author knut st. osmundsen
1176 */
1177int langRC(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1178{
1179 void * pvRule; /* Handle to the current rule. */
1180 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1181 int iLine; /* current line number */
1182 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1183
1184
1185 /**********************************/
1186 /* Add the depend rule */
1187 /**********************************/
1188 if (pOptions->fObjRule && !fHeader)
1189 {
1190 if (pOptions->fNoObjectPath)
1191 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszRsrcExt);
1192 else
1193 pvRule = depAddRule(pOptions->fObjectDir ?
1194 pOptions->pszObjectDir :
1195 filePathSlash(pszFilename, szBuffer),
1196 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1197 pOptions->pszRsrcExt);
1198
1199 if (pOptions->fSrcWhenObj && pvRule)
1200 depAddDepend(pvRule,
1201 pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1202 pOptions->fCheckCyclic);
1203 }
1204 else
1205 pvRule = depAddRule(pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer), NULL, NULL);
1206
1207 /* duplicate rule? */
1208 if (pvRule == NULL)
1209 return 0;
1210
1211
1212 /*******************/
1213 /* find dependants */
1214 /*******************/
1215 iLine = 0;
1216 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1217 {
1218 /* search for #include */
1219 int cbLen;
1220 int i = 0;
1221 iLine++;
1222
1223 /* skip blank chars */
1224 cbLen = strlen(szBuffer);
1225 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1226 i++;
1227
1228 /* is this an include? */
1229 if ( strncmp(&szBuffer[i], "#include", 8) == 0
1230 || strncmp(&szBuffer[i], "RCINCLUDE", 9) == 0
1231 || strncmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
1232 )
1233 {
1234 char szFullname[CCHMAXPATH];
1235 char *psz;
1236 BOOL f = FALSE;
1237 int j;
1238
1239 /* extract info between "" or <> */
1240 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1241 i++;
1242 i++; /* skip '"' or '<' */
1243
1244 /* if invalid statement then continue with the next line! */
1245 if (!f) continue;
1246
1247 /* find end */
1248 j = f = 0;
1249 while (i + j < cbLen && j < CCHMAXPATH &&
1250 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1251 j++;
1252
1253 /* if invalid statement then continue with the next line! */
1254 if (!f) continue;
1255
1256 /* copy filename */
1257 strncpy(szFullname, &szBuffer[i], j);
1258 szFullname[j] = '\0'; /* ensure terminatition. */
1259 strlwr(szFullname);
1260
1261 /* find include file! */
1262 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1263 if (psz == NULL)
1264 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer, pOptions);
1265
1266 /* did we find the include? */
1267 if (psz != NULL)
1268 {
1269 char szBuffer2[CCHMAXPATH];
1270 if (pOptions->fExcludeAll ||
1271 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2, pOptions) != NULL
1272 )
1273 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1274 else
1275 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1276 }
1277 else
1278 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1279 pszFilename, iLine, szFullname);
1280 }
1281 } /*while*/
1282
1283 return 0;
1284}
1285
1286
1287/**
1288 * Generates depend info on this COBOL file, these are stored internally
1289 * and written to file later.
1290 * @returns 0 on success.
1291 * !0 on error.
1292 * @param pszFilename Pointer to source filename. Correct case is assumed!
1293 * @param pvFile Pointer to file textbuffer.
1294 * @param pOptions Pointer to options struct.
1295 * @status completely implemented.
1296 * @author knut st. osmundsen
1297 */
1298int langCOBOL(const char *pszFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1299{
1300 void * pvRule; /* Handle to the current rule. */
1301 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1302 int iLine; /* current line number */
1303 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1304
1305
1306 /**********************************/
1307 /* Add the depend rule */
1308 /**********************************/
1309 if (pOptions->fObjRule && !fHeader)
1310 {
1311 if (pOptions->fNoObjectPath)
1312 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
1313 else
1314 pvRule = depAddRule(pOptions->fObjectDir ?
1315 pOptions->pszObjectDir :
1316 filePathSlash(pszFilename, szBuffer),
1317 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1318 pOptions->pszObjectExt);
1319
1320 if (pOptions->fSrcWhenObj && pvRule)
1321 depAddDepend(pvRule,
1322 pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1323 pOptions->fCheckCyclic);
1324 }
1325 else
1326 pvRule = depAddRule(pOptions->fExcludeAll ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer), NULL, NULL);
1327
1328 /* duplicate rule? */
1329 if (pvRule == NULL)
1330 return 0;
1331
1332
1333 /*******************/
1334 /* find dependants */
1335 /*******************/
1336 iLine = 0;
1337 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1338 {
1339 /* search for #include */
1340 int cbLen;
1341 int i = 0;
1342 int i1, i2;
1343 iLine++;
1344
1345 /* check for comment mark (column 7) */
1346 if (szBuffer[6] == '*')
1347 continue;
1348
1349 /* skip blank chars */
1350 cbLen = strlen(szBuffer);
1351 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1352 i++;
1353
1354 /* is this an include? */
1355 if ( (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
1356 || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
1357 )
1358 {
1359 char szFullname[CCHMAXPATH];
1360 char *psz;
1361 int j;
1362
1363 /* skip statement */
1364 i += 4;
1365 if (i1 != 0)
1366 {
1367 int y = 2; /* skip two words */
1368 do
1369 {
1370 /* skip blanks */
1371 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1372 i++;
1373 /* skip word */
1374 while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
1375 && szBuffer[i] != '\0' && szBuffer[i] != '\n')
1376 i++;
1377 y--;
1378 } while (y > 0);
1379 }
1380
1381 /* check for blank */
1382 if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
1383 continue;
1384
1385 /* skip blanks */
1386 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1387 i++;
1388
1389 /* if invalid statement then continue with the next line! */
1390 if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
1391 continue;
1392
1393 /* find end */
1394 j = 0;
1395 while (i + j < cbLen && j < CCHMAXPATH
1396 && szBuffer[i+j] != '.'
1397 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1398 && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
1399 )
1400 j++;
1401
1402 /* if invalid statement then continue with the next line! */
1403 if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
1404 continue;
1405
1406 /* copy filename */
1407 strncpy(szFullname, &szBuffer[i], j);
1408 szFullname[j] = '\0'; /* ensure terminatition. */
1409 strlwr(szFullname);
1410
1411 /* add extention .cpy - hardcoded for the moment. */
1412 strcat(szFullname, ".cpy");
1413
1414 /* find include file! */
1415 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1416
1417 /* did we find the include? */
1418 if (psz != NULL)
1419 {
1420 char szBuffer2[CCHMAXPATH];
1421 if (pOptions->fExcludeAll ||
1422 pathlistFindFile(pOptions->pszExclude, szFullname, szBuffer2, pOptions) != NULL
1423 )
1424 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1425 else
1426 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1427 }
1428 else
1429 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1430 pszFilename, iLine, szFullname);
1431 }
1432 } /*while*/
1433
1434 return 0;
1435}
1436
1437#define upcase(ch) \
1438 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
1439
1440/**
1441 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
1442 * @returns 0 equal. (same as strnicmp)
1443 * @param pszS1 String 1
1444 * @param pszS2 String 2
1445 * @param cch Length to compare (relative to string 1)
1446 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1447 */
1448static int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
1449{
1450 do
1451 {
1452 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
1453 pszS1++, pszS2++, cch--;
1454
1455 /* blank test and skipping */
1456 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
1457 {
1458 while (cch > 0 && *pszS1 == ' ')
1459 pszS1++, cch--;
1460
1461 while (*pszS2 == ' ')
1462 pszS2++;
1463 }
1464 else
1465 break;
1466 } while (cch > 0);
1467
1468 return cch == 0 ? 0 : *pszS1 - *pszS2;
1469}
1470
1471
1472/**
1473 * Normalizes the path slashes for the filename. It will partially expand paths too.
1474 * @returns pszFilename
1475 * @param pszFilename Pointer to filename string. Not empty string!
1476 * Much space to play with.
1477 */
1478char *fileNormalize(char *pszFilename)
1479{
1480 char *psz = pszFilename;
1481
1482 /* correct slashes */
1483 while ((pszFilename = strchr(pszFilename, '//')) != NULL)
1484 *pszFilename++ = '\\';
1485
1486 /* expand path? */
1487 pszFilename = psz;
1488 if (pszFilename[1] != ':')
1489 { /* relative path */
1490 int iSlash;
1491 char szFile[CCHMAXPATH];
1492 char * psz = szFile;
1493
1494 strcpy(szFile, pszFilename);
1495 iSlash = *psz == '\\' ? 1 : cSlashes;
1496 while (*psz != '\0')
1497 {
1498 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
1499 { /* up one directory */
1500 if (iSlash > 0)
1501 iSlash--;
1502 psz += 3;
1503 }
1504 else if (*psz == '.' && psz[1] == '\\')
1505 { /* no change */
1506 psz += 2;
1507 }
1508 else
1509 { /* completed expantion! */
1510 strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
1511 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
1512 break;
1513 }
1514 }
1515 }
1516 /* else: assume full path */
1517
1518 return psz;
1519}
1520
1521
1522/**
1523 * Normalizes the path slashes for the filename. It will partially expand paths too.
1524 * Makes name all lower case too.
1525 * @returns pszFilename
1526 * @param pszFilename Pointer to filename string. Not empty string!
1527 * Much space to play with.
1528 * @param pszBuffer Pointer to output buffer.
1529 */
1530char *fileNormalize2(const char *pszFilename, char *pszBuffer)
1531{
1532 char * psz = pszBuffer;
1533 int iSlash;
1534
1535 if (pszFilename[1] != ':')
1536 {
1537 /* iSlash */
1538 if (*pszFilename == '\\' || *pszFilename == '/')
1539 iSlash = 1;
1540 else
1541 iSlash = cSlashes;
1542
1543 /* interpret . and .. */
1544 while (*pszFilename != '\0')
1545 {
1546 if (*pszFilename == '.' && pszFilename[1] == '.' && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
1547 { /* up one directory */
1548 if (iSlash > 0)
1549 iSlash--;
1550 pszFilename += 3;
1551 }
1552 else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
1553 { /* no change */
1554 pszFilename += 2;
1555 }
1556 else
1557 { /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
1558 strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
1559 strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
1560 break;
1561 }
1562 }
1563 }
1564 else
1565 { /* have drive letter specified - assume ok (TODO)*/
1566 strcpy(pszBuffer, pszFilename);
1567 }
1568
1569 /* correct slashes */
1570 while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
1571 *pszBuffer++ = '\\';
1572
1573 /* lower case it */
1574 /*strlwr(psz);*/
1575
1576 return psz;
1577}
1578
1579
1580/**
1581 * Copies the path part (excluding the slash) into pszBuffer and returns
1582 * a pointer to the buffer.
1583 * If no path is found "" is returned.
1584 * @returns Pointer to pszBuffer with path.
1585 * @param pszFilename Pointer to readonly filename.
1586 * @param pszBuffer Pointer to output Buffer.
1587 * @status completely implemented.
1588 * @author knut st. osmundsen
1589 */
1590char *filePath(const char *pszFilename, char *pszBuffer)
1591{
1592 char *psz = strrchr(pszFilename, '\\');
1593 if (psz == NULL)
1594 psz = strrchr(pszFilename, '/');
1595
1596 if (psz == NULL)
1597 *pszBuffer = '\0';
1598 else
1599 {
1600 strncpy(pszBuffer, pszFilename, psz - pszFilename - 1);
1601 pszBuffer[psz - pszFilename - 1] = '\0';
1602 }
1603
1604 return pszBuffer;
1605}
1606
1607
1608/**
1609 * Copies the path part including the slash into pszBuffer and returns
1610 * a pointer to the buffer.
1611 * If no path is found "" is returned.
1612 * @returns Pointer to pszBuffer with path.
1613 * @param pszFilename Pointer to readonly filename.
1614 * @param pszBuffer Pointer to output Buffer.
1615 * @status completely implemented.
1616 * @author knut st. osmundsen
1617 */
1618static char *filePathSlash(const char *pszFilename, char *pszBuffer)
1619{
1620 char *psz = strrchr(pszFilename, '\\');
1621 if (psz == NULL)
1622 psz = strrchr(pszFilename, '/');
1623
1624 if (psz == NULL)
1625 *pszBuffer = '\0';
1626 else
1627 {
1628 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
1629 pszBuffer[psz - pszFilename + 1] = '\0';
1630 }
1631
1632 return pszBuffer;
1633}
1634
1635
1636/**
1637 * Copies the path part including the slash into pszBuffer and returns
1638 * a pointer to the buffer. If no path is found "" is returned.
1639 * The path is normalized to only use '\\'.
1640 * @returns Pointer to pszBuffer with path.
1641 * @param pszFilename Pointer to readonly filename.
1642 * @param pszBuffer Pointer to output Buffer.
1643 * @status completely implemented.
1644 * @author knut st. osmundsen
1645 */
1646static char *filePathSlash2(const char *pszFilename, char *pszBuffer)
1647{
1648 char *psz = strrchr(pszFilename, '\\');
1649 if (psz == NULL)
1650 psz = strrchr(pszFilename, '/');
1651
1652 if (psz == NULL)
1653 *pszBuffer = '\0';
1654 else
1655 {
1656 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
1657 pszBuffer[psz - pszFilename + 1] = '\0';
1658
1659 /* normalize all '/' to '\\' */
1660 psz = pszBuffer;
1661 while ((psz = strchr(psz, '/')) != NULL)
1662 *psz++ = '\\';
1663 }
1664
1665 return pszBuffer;
1666}
1667
1668
1669/**
1670 * Copies the filename (with extention) into pszBuffer and returns
1671 * a pointer to the buffer.
1672 * @returns Pointer to pszBuffer with path.
1673 * @param pszFilename Pointer to readonly filename.
1674 * @param pszBuffer Pointer to output Buffer.
1675 * @status completely implemented.
1676 * @author knut st. osmundsen
1677 */
1678char *fileName(const char *pszFilename, char *pszBuffer)
1679{
1680 char *psz = strrchr(pszFilename, '\\');
1681 if (psz == NULL)
1682 psz = strrchr(pszFilename, '/');
1683
1684 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1685
1686 return pszBuffer;
1687}
1688
1689
1690/**
1691 * Copies the name part with out extention into pszBuffer and returns
1692 * a pointer to the buffer.
1693 * If no name is found "" is returned.
1694 * @returns Pointer to pszBuffer with path.
1695 * @param pszFilename Pointer to readonly filename.
1696 * @param pszBuffer Pointer to output Buffer.
1697 * @status completely implemented.
1698 * @author knut st. osmundsen
1699 */
1700char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
1701{
1702 char *psz = strrchr(pszFilename, '\\');
1703 if (psz == NULL)
1704 psz = strrchr(pszFilename, '/');
1705
1706 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1707
1708 psz = strrchr(pszBuffer, '.');
1709 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
1710 *psz = '\0';
1711
1712 return pszBuffer;
1713}
1714
1715
1716/**
1717 * Copies the extention part into pszBuffer and returns
1718 * a pointer to the buffer.
1719 * If no extention is found "" is returned.
1720 * The dot ('.') is not included!
1721 * @returns Pointer to pszBuffer with path.
1722 * @param pszFilename Pointer to readonly filename.
1723 * @param pszBuffer Pointer to output Buffer.
1724 * @status completely implemented.
1725 * @author knut st. osmundsen
1726 */
1727char *fileExt(const char *pszFilename, char *pszBuffer)
1728{
1729 char *psz = strrchr(pszFilename, '.');
1730 if (psz != NULL)
1731 {
1732 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
1733 *pszBuffer = '\0';
1734 else
1735 strcpy(pszBuffer, psz + 1);
1736 }
1737 else
1738 *pszBuffer = '\0';
1739
1740 return pszBuffer;
1741}
1742
1743
1744/**
1745 * Adds a file to the cache.
1746 * @returns Success indicator.
1747 * @param pszFilename Name of the file which is to be added. (with path!)
1748 */
1749static BOOL filecacheAddFile(const char *pszFilename)
1750{
1751 PFCACHEENTRY pfcNew;
1752
1753 /* allocate new block and fill in data */
1754 pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
1755 if (pfcNew == NULL)
1756 {
1757 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
1758 return FALSE;
1759 }
1760 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
1761 strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
1762 if (!AVLInsert(&pfcTree, pfcNew))
1763 {
1764 free(pfcNew);
1765 return TRUE;
1766 }
1767 cfcNodes++;
1768
1769 return TRUE;
1770}
1771
1772
1773
1774/**
1775 * Adds a file to the cache.
1776 * @returns Success indicator.
1777 * @param pszDir Name of the path which is to be added. (with slash!)
1778 */
1779static BOOL filecacheAddDir(const char *pszDir)
1780{
1781 PFCACHEENTRY pfcNew;
1782 APIRET rc;
1783 char szDir[CCHMAXPATH];
1784 int cchDir;
1785 char achBuffer[16384];
1786 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
1787 HDIR hDir = HDIR_CREATE;
1788 ULONG cFiles = 0xFFFFFFF;
1789 int i;
1790
1791 /* Make path */
1792 filePathSlash2(pszDir, szDir);
1793 //strlwr(szDir); /* Convert name to lower case to allow faster searchs! */
1794 cchDir = strlen(szDir);
1795
1796
1797 /* Add directory to pfcDirTree. */
1798 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
1799 if (pfcNew == NULL)
1800 {
1801 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
1802 DosFindClose(hDir);
1803 return FALSE;
1804 }
1805 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
1806 strcpy((char*)(unsigned)pfcNew->Key, szDir);
1807 AVLInsert(&pfcDirTree, pfcNew);
1808
1809
1810 /* Start to search directory - all files */
1811 strcat(szDir + cchDir, "*");
1812 rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
1813 pfindbuf3, sizeof(achBuffer),
1814 &cFiles, FIL_STANDARD);
1815 while (rc == NO_ERROR)
1816 {
1817 for (i = 0;
1818 i < cFiles;
1819 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
1820 )
1821 {
1822 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
1823 if (pfcNew == NULL)
1824 {
1825 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
1826 DosFindClose(hDir);
1827 return FALSE;
1828 }
1829 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
1830 strcpy((char*)(unsigned)pfcNew->Key, szDir);
1831 strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
1832 strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
1833 if (!AVLInsert(&pfcTree, pfcNew))
1834 free(pfcNew);
1835 else
1836 cfcNodes++;
1837 }
1838
1839 /* next */
1840 cFiles = 0xFFFFFFF;
1841 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
1842 rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
1843 }
1844
1845 DosFindClose(hDir);
1846
1847 return TRUE;
1848}
1849
1850
1851/**
1852 * Checks if pszFilename is exists in the cache.
1853 * @return TRUE if found. FALSE if not found.
1854 * @param pszFilename Name of the file to be found. (with path!)
1855 * This is in lower case!
1856 */
1857INLINE BOOL filecacheFind(const char *pszFilename)
1858{
1859 return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
1860}
1861
1862
1863/**
1864 * Checks if pszFilename is exists in the cache.
1865 * @return TRUE if found. FALSE if not found.
1866 * @param pszFilename Name of the file to be found. (with path!)
1867 * This is in lower case!
1868 */
1869INLINE BOOL filecacheIsDirCached(const char *pszDir)
1870{
1871 return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
1872}
1873
1874
1875
1876/**
1877 * Finds a filename in a specified pathlist.
1878 * @returns Pointer to a filename consiting of the path part + the given filename.
1879 * (pointer into pszBuffer)
1880 * NULL if file is not found. ("" in buffer)
1881 * @param pszPathList Path list to search for filename.
1882 * @parma pszFilename Filename to find.
1883 * @parma pszBuffer Ouput Buffer.
1884 * @param pOptions Pointer to options struct.
1885 * @status completely implemented.
1886 * @author knut st. osmundsen
1887 * @remark need substantial optimizations. 95% of execution is spend here.
1888 */
1889static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer, POPTIONS pOptions)
1890{
1891 const char *psz = pszPathList;
1892 const char *pszNext = NULL;
1893
1894 *pszBuffer = '\0';
1895
1896 if (pszPathList == NULL)
1897 return NULL;
1898
1899 while (*psz != '\0')
1900 {
1901 /* find end of this path */
1902 pszNext = strchr(psz, ';');
1903 if (pszNext == NULL)
1904 pszNext = psz + strlen(psz);
1905
1906 if (pszNext - psz > 0)
1907 {
1908 APIRET rc;
1909
1910 /* make search statment */
1911 strncpy(pszBuffer, psz, pszNext - psz);
1912 pszBuffer[pszNext - psz] = '\0';
1913 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
1914 strcpy(&pszBuffer[pszNext - psz], "\\");
1915 strcat(pszBuffer, pszFilename);
1916 //strlwr(pszBuffer); /* to speed up AVL tree search we'll lowercase all names. */
1917 fileNormalize(pszBuffer);
1918
1919 /*
1920 * Search for the file in this directory.
1921 * Search cache first
1922 */
1923 if (!filecacheFind(pszBuffer))
1924 {
1925 char szDir[CCHMAXPATH];
1926
1927 filePathSlash(pszBuffer, szDir);
1928 if (!filecacheIsDirCached(szDir))
1929 {
1930 /*
1931 * If caching of entire dirs are enabled, we'll
1932 * add the directory to the cache and search it.
1933 */
1934 if (pOptions->fCacheSearchDirs && filecacheAddDir(szDir))
1935 {
1936 if (filecacheFind(pszBuffer))
1937 return pszBuffer;
1938 }
1939 else
1940 {
1941 FILESTATUS3 fsts3;
1942
1943 /* ask the OS */
1944 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
1945 if (rc == NO_ERROR)
1946 { /* add file to cache. */
1947 filecacheAddFile(pszBuffer);
1948 return pszBuffer;
1949 }
1950 }
1951 }
1952 }
1953 else
1954 return pszBuffer;
1955 }
1956
1957 /* next */
1958 if (*pszNext != ';')
1959 break;
1960 psz = pszNext + 1;
1961 }
1962
1963 return NULL;
1964}
1965
1966
1967/**
1968 * Finds the first char after word.
1969 * @returns Pointer to the first char after word.
1970 * @param psz Where to start.
1971 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1972 */
1973static char *findEndOfWord(char *psz)
1974{
1975
1976 while (*psz != '\0' &&
1977 (
1978 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
1979 ||
1980 (*psz >= '0' && *psz <= '9')
1981 ||
1982 *psz == '_'
1983 )
1984 )
1985 ++psz;
1986 return (char *)psz;
1987}
1988
1989#if 0 /* not used */
1990/**
1991 * Find the starting char of a word
1992 * @returns Pointer to first char in word.
1993 * @param psz Where to start.
1994 * @param pszStart Where to stop.
1995 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1996 */
1997static char *findStartOfWord(const char *psz, const char *pszStart)
1998{
1999 const char *pszR = psz;
2000 while (psz >= pszStart &&
2001 (
2002 (*psz >= 'A' && *psz <= 'Z')
2003 || (*psz >= 'a' && *psz <= 'z')
2004 || (*psz >= '0' && *psz <= '9')
2005 || *psz == '_'
2006 )
2007 )
2008 pszR = psz--;
2009 return (char*)pszR;
2010}
2011#endif
2012
2013/**
2014 * Find the size of a file.
2015 * @returns Size of file. -1 on error.
2016 * @param phFile File handle.
2017 */
2018static signed long fsize(FILE *phFile)
2019{
2020 int ipos;
2021 signed long cb;
2022
2023 if ((ipos = ftell(phFile)) < 0
2024 ||
2025 fseek(phFile, 0, SEEK_END) != 0
2026 ||
2027 (cb = ftell(phFile)) < 0
2028 ||
2029 fseek(phFile, ipos, SEEK_SET) != 0
2030 )
2031 cb = -1;
2032 return cb;
2033}
2034
2035
2036
2037/**
2038 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2039 * @returns Pointer to first not space or tab char in the string.
2040 * @param psz Pointer to the string which is to be trimmed.
2041 * @status completely implmented.
2042 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2043 */
2044INLINE char *trim(char *psz)
2045{
2046 int i;
2047 if (psz == NULL)
2048 return NULL;
2049 while (*psz == ' ' || *psz == '\t')
2050 psz++;
2051 i = strlen(psz) - 1;
2052 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2053 i--;
2054 psz[i+1] = '\0';
2055 return psz;
2056}
2057
2058
2059/**
2060 * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
2061 * @returns Pointer to the string passed in.
2062 * @param psz Pointer to the string which is to be right trimmed.
2063 * @status completely implmented.
2064 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2065 */
2066INLINE char *trimR(char *psz)
2067{
2068 int i;
2069 if (psz == NULL)
2070 return NULL;
2071 i = strlen(psz) - 1;
2072 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2073 i--;
2074 psz[i+1] = '\0';
2075 return psz;
2076}
2077
2078
2079/**
2080 * Creates a memory buffer for a text file.
2081 * @returns Pointer to file memoryblock. NULL on error.
2082 * @param pszFilename Pointer to filename string.
2083 */
2084static void *textbufferCreate(const char *pszFilename)
2085{
2086 void *pvFile = NULL;
2087 FILE *phFile;
2088
2089 phFile = fopen(pszFilename, "rb");
2090 if (phFile != NULL)
2091 {
2092 signed long cbFile = fsize(phFile);
2093 if (cbFile > 0)
2094 {
2095 pvFile = malloc(cbFile + 1);
2096 if (pvFile != NULL)
2097 {
2098 memset(pvFile, 0, cbFile + 1);
2099 if (fread(pvFile, 1, cbFile, phFile) == 0)
2100 { /* failed! */
2101 free(pvFile);
2102 pvFile = NULL;
2103 }
2104 }
2105 else
2106 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
2107 }
2108 fclose(phFile);
2109 }
2110 return pvFile;
2111}
2112
2113
2114/**
2115 * Destroys a text textbuffer.
2116 * @param pvBuffer Buffer handle.
2117 */
2118static void textbufferDestroy(void *pvBuffer)
2119{
2120 free(pvBuffer);
2121}
2122
2123
2124/**
2125 * Gets the next line from an textbuffer.
2126 * @returns Pointer to the next line.
2127 * @param pvBuffer Buffer handle.
2128 * @param psz Pointer to current line.
2129 * NULL is passed in to get the first line.
2130 */
2131static char *textbufferNextLine(void *pvBuffer, register char *psz)
2132{
2133 register char ch;
2134
2135 /* if first line psz is NULL. */
2136 if (psz == NULL)
2137 return (char*)pvBuffer;
2138
2139 /* skip till end of file or end of line. */
2140 ch = *psz;
2141 while (ch != '\0' && ch != '\n' && ch != '\r')
2142 ch = *++psz;
2143
2144 /* skip line end */
2145 if (ch == '\r')
2146 ch = *++psz;
2147 if (ch == '\n')
2148 psz++;
2149
2150 return psz;
2151}
2152
2153
2154/**
2155 * Gets the next line from an textbuffer.
2156 * (fgets for textbuffer)
2157 * @returns Pointer to pszOutBuffer. NULL when end of file.
2158 * @param pvBuffer Buffer handle.
2159 * @param ppv Pointer to a buffer index pointer. (holds the current buffer index)
2160 * Pointer to a null pointer is passed in to get the first line.
2161 * @param pszLineBuffer Output line buffer. (!= NULL)
2162 * @param cchLineBuffer Size of the output line buffer. (> 0)
2163 * @remark '\n' and '\r' are removed!
2164 */
2165static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
2166{
2167 char * pszLine = pszLineBuffer;
2168 char * psz = *(char**)(void*)ppv;
2169 register char ch;
2170
2171 /* first line? */
2172 if (psz == NULL)
2173 psz = pvBuffer;
2174
2175 /* Copy to end of the line or end of the linebuffer. */
2176 ch = *psz;
2177 cchLineBuffer--; /* reserve space for '\0' */
2178 while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
2179 {
2180 *pszLine++ = ch;
2181 ch = *++psz;
2182 }
2183 *pszLine = '\0';
2184
2185 /* skip line end */
2186 if (ch == '\r')
2187 ch = *++psz;
2188 if (ch == '\n')
2189 psz++;
2190
2191 /* check if position has changed - if unchanged it's the end of file! */
2192 if (*ppv == (void*)psz)
2193 pszLineBuffer = NULL;
2194
2195 /* store current position */
2196 *ppv = (void*)psz;
2197
2198 return pszLineBuffer;
2199}
2200
2201
2202/**
2203 * Appends a depend file to the internal file.
2204 */
2205static BOOL depReadFile(const char *pszFilename, POPTIONS pOptions)
2206{
2207 void *pvFile;
2208 char *pszNext;
2209 BOOL fMoreDeps = FALSE;
2210 void *pvRule = NULL;
2211
2212 /* read depend file */
2213 pvFile = textbufferCreate(pszFilename);
2214 if (pvFile == NULL)
2215 return FALSE;
2216
2217 /* parse the original depend file */
2218 pszNext = pvFile;
2219 while (*pszNext != '\0')
2220 {
2221 int i;
2222 int cch;
2223 char *psz;
2224
2225 /* get the next line. */
2226 psz = pszNext;
2227 pszNext = textbufferNextLine(pvFile, pszNext);
2228
2229 /*
2230 * Process the current line:
2231 * Start off by terminating the line.
2232 * Trim the line,
2233 * Skip empty lines.
2234 * If not looking for more deps Then
2235 * Check if new rule starts here.
2236 * Endif
2237 *
2238 * If more deps to last rule Then
2239 * Get dependant name.
2240 * Endif
2241 */
2242 i = -1;
2243 while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
2244 pszNext[i--] = '\0';
2245 trimR(psz);
2246 cch = strlen(psz);
2247 if (cch == 0)
2248 continue;
2249
2250 /* new rule? */
2251 if (!fMoreDeps && *psz != ' ' && *psz != '\t' && *psz != '\0')
2252 {
2253 i = 0;
2254 while (psz[i] != '\0')
2255 {
2256 if (psz[i] == ':'
2257 && (psz[i+1] == ' '
2258 || psz[i+1] == '\t'
2259 || psz[i+1] == '\0'
2260 || (psz[i+1] == '\\' && psz[i+2] == '\0')
2261 )
2262 )
2263 {
2264 psz[i] = '\0';
2265 pvRule = depAddRule(trimR(psz), NULL, NULL);
2266 psz += i + 1;
2267 cch -= i + 1;
2268 fMoreDeps = TRUE;
2269 break;
2270 }
2271 i++;
2272 }
2273 }
2274
2275 /* more dependants */
2276 if (fMoreDeps)
2277 {
2278 if (cch > 0 && psz[cch-1] == '\\')
2279 {
2280 fMoreDeps = TRUE;
2281 psz[cch-1] = '\0';
2282 }
2283 else
2284 fMoreDeps = FALSE;
2285
2286 /* if not duplicate rule */
2287 if (pvRule != NULL)
2288 {
2289 psz = trim(psz);
2290 if (*psz != '\0')
2291 depAddDepend(pvRule, psz, pOptions->fCheckCyclic);
2292 }
2293 }
2294 } /* while */
2295
2296
2297 /* return succesfully */
2298 textbufferDestroy(pvFile);
2299 return TRUE;
2300}
2301
2302/**
2303 *
2304 * @returns Success indicator.
2305 * @params pszFilename Pointer to name of the output file.
2306 */
2307static BOOL depWriteFile(const char *pszFilename)
2308{
2309 FILE *phFile;
2310 phFile = fopen(pszFilename, "w");
2311 if (phFile != NULL)
2312 {
2313 AVLENUMDATA EnumData;
2314 PDEPRULE pdep;
2315 char szBuffer[4096];
2316 int iBuffer = 0;
2317 int cch;
2318
2319 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
2320 while (pdep != NULL)
2321 {
2322 /* Write rule. Flush the buffer first if necessary. */
2323 cch = strlen(pdep->pszRule);
2324 if (iBuffer + cch + 2 >= sizeof(szBuffer))
2325 {
2326 fwrite(szBuffer, iBuffer, 1, phFile);
2327 iBuffer = 0;
2328 }
2329 strcpy(szBuffer + iBuffer, pdep->pszRule);
2330 iBuffer += cch;
2331 strcpy(szBuffer + iBuffer++, ":");
2332
2333 /* write rule dependants. */
2334 if (pdep->papszDep != NULL)
2335 {
2336 char **ppsz = pdep->papszDep;
2337 while (*ppsz != NULL)
2338 {
2339 /* flush buffer? */
2340 if (iBuffer + strlen(*ppsz) + 20 >= sizeof(szBuffer))
2341 {
2342 fwrite(szBuffer, iBuffer, 1, phFile);
2343 iBuffer = 0;
2344 }
2345 iBuffer += sprintf(szBuffer + iBuffer, " \\\n %s", *ppsz);
2346
2347 /* next dependant */
2348 ppsz++;
2349 }
2350 }
2351
2352 /* Add two new lines. Flush buffer first if necessary. */
2353 if (iBuffer + 2 >= sizeof(szBuffer))
2354 {
2355 fwrite(szBuffer, iBuffer, 1, phFile);
2356 iBuffer = 0;
2357 }
2358 strcpy(szBuffer + iBuffer, "\n\n");
2359 iBuffer += 2;
2360
2361 /* next rule */
2362 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
2363 }
2364
2365 /* flush buffer. */
2366 fwrite(szBuffer, iBuffer, 1, phFile);
2367
2368 fclose(phFile);
2369 return TRUE;
2370 }
2371
2372 return FALSE;
2373}
2374
2375
2376/**
2377 * Removes all nodes in the tree of dependencies. (pdepTree)
2378 */
2379static void depRemoveAll(void)
2380{
2381 AVLENUMDATA EnumData;
2382 PDEPRULE pdep;
2383
2384 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
2385 while (pdep != NULL)
2386 {
2387 /* free this */
2388 if (pdep->papszDep != NULL)
2389 {
2390 char ** ppsz = pdep->papszDep;
2391 while (*ppsz != NULL)
2392 free(*ppsz++);
2393 free(pdep->papszDep);
2394 }
2395 free(pdep);
2396
2397 /* next */
2398 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
2399 }
2400 pdepTree = NULL;
2401}
2402
2403
2404/**
2405 * Adds a rule to the list of dependant rules.
2406 * @returns Rule handle. NULL if rule exists/error.
2407 * @param pszRulePath Pointer to rule text. Empty strings are banned!
2408 * This string might only contain the path of the rule. (with '\\')
2409 * @param pszName Name of the rule.
2410 * NULL if pszRulePath contains the entire rule.
2411 * @param pszExt Extention (without '.')
2412 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
2413 */
2414static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt)
2415{
2416 char szRule[CCHMAXPATH*2];
2417 PDEPRULE pNew;
2418 int cch;
2419
2420 /* make rulename */
2421 strcpy(szRule, pszRulePath);
2422 cch = strlen(szRule);
2423 if (pszName != NULL)
2424 {
2425 strcpy(szRule + cch, pszName);
2426 cch += strlen(szRule + cch);
2427 }
2428 if (pszExt != NULL)
2429 {
2430 strcat(szRule + cch++, ".");
2431 strcat(szRule + cch, pszExt);
2432 cch += strlen(szRule + cch);
2433 }
2434
2435
2436 /*
2437 * Allocate a new rule structure and fill in data
2438 * Note. One block for both the DEPRULE and the pszRule string.
2439 */
2440 pNew = malloc(sizeof(DEPRULE) + cch + 1);
2441 if (pNew == NULL)
2442 {
2443 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
2444 return NULL;
2445 }
2446 pNew->pszRule = (char*)(void*)(pNew + 1);
2447 strcpy(pNew->pszRule, szRule);
2448 pNew->cDeps = 0;
2449 pNew->papszDep = NULL;
2450 pNew->avlCore.Key = pNew->pszRule;
2451
2452 /* Insert rule */
2453 if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
2454 { /* rule existed - return NULL */
2455 free(pNew);
2456 return NULL;
2457 }
2458
2459 return pNew;
2460}
2461
2462
2463
2464/**
2465 * Adds a dependant to a rule.
2466 * @returns Successindicator. TRUE = success.
2467 * FALSE = cyclic or out of memory.
2468 * @param pvRule Rule handle.
2469 * @param pszDep Pointer to dependant name
2470 * @param fCheckCyclic When set we'll check that we're not creating an cyclic dependency.
2471 */
2472static BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic)
2473{
2474 PDEPRULE pdep = (PDEPRULE)pvRule;
2475
2476 if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
2477 {
2478 fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
2479 pszDep, pdep->pszRule);
2480 return FALSE;
2481 }
2482
2483 /* allocate more array space */
2484 if (((pdep->cDeps) % 48) == 0)
2485 {
2486 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
2487 if (pdep->papszDep == NULL)
2488 {
2489 pdep->cDeps = 0;
2490 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
2491 return FALSE;
2492 }
2493 }
2494
2495 /* allocate string space and copy pszDep */
2496 if ((pdep->papszDep[pdep->cDeps] = malloc(strlen(pszDep) + 1)) == NULL)
2497 {
2498 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
2499 return FALSE;
2500 }
2501 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
2502
2503 /* terminate array and increment dep count */
2504 pdep->papszDep[++pdep->cDeps] = NULL;
2505
2506 /* successful! */
2507 return TRUE;
2508}
2509
2510
2511/**
2512 * Checks if adding this dependent will create a cylic dependency.
2513 * @returns TRUE: Cyclic.
2514 * FALSE: Non-cylic.
2515 * @param pdepRule Rule pszDep is to be inserted in.
2516 * @param pszDep Depend name.
2517 */
2518static BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
2519{
2520 #define DEPTH 32
2521 char * pszRule = pdepRule->pszRule;
2522 char ** appsz[DEPTH];
2523 PDEPRULE pdep;
2524 int i;
2525
2526 /* self check */
2527 if (strcmp(pdepRule->pszRule, pszDep) == 0)
2528 return TRUE;
2529
2530 /* find rule for the dep. */
2531 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
2532 || pdep->papszDep == NULL)
2533 return FALSE; /* no rule, or no dependents, not cyclic */
2534
2535 i = 0;
2536 appsz[0] = pdep->papszDep;
2537 while (i >= 0)
2538 {
2539 register char ** ppsz = appsz[i];
2540
2541 while (*ppsz != NULL)
2542 {
2543 /* check if equal to the main rule */
2544 if (strcmp(pszRule, *ppsz) == 0)
2545 return TRUE;
2546
2547 /* push onto stack (ppsz is incremented in this test!) */
2548 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
2549 && pdep->papszDep != NULL)
2550 {
2551 if (i >= DEPTH)
2552 {
2553 fprintf(stderr, "error: too deap chain (%d). pszRule=%s pszDep=%s\n",
2554 i, pszRule, pszDep);
2555 return FALSE;
2556 }
2557 appsz[i++] = ppsz; /* save next */
2558 ppsz = pdep->papszDep; /* start processing new node */
2559 }
2560 }
2561
2562 /* pop stack */
2563 i--;
2564 }
2565
2566 return FALSE;
2567}
2568
2569
2570/*
2571 * Testing purpose.
2572 */
2573#include <os2.h>
2574#ifdef OLEMANN
2575#include "olemann.h"
2576#endif
2577
2578
2579
Note: See TracBrowser for help on using the repository browser.