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

Last change on this file since 9145 was 9145, checked in by bird, 23 years ago

going home, taking code with me.

File size: 127.7 KB
Line 
1/* $Id: fastdep.c,v 1.40 2002-08-27 21:48:45 bird Exp $
2 *
3 * Fast dependents. (Fast = Quick and Dirty!)
4 *
5 * Copyright (c) 1999-2002 knut st. osmundsen (bird@anduin.net)
6 *
7 * GPL
8 *
9 */
10
11/*******************************************************************************
12* Defined Constants And Macros *
13*******************************************************************************/
14#define INCL_DOSERRORS
15#define INCL_FILEMGR
16#define INCL_DOSMISC
17
18
19/*
20 * Size of the \n charater (forget '\r').
21 * If you're compiling this under a UNICODE system this may perhaps change,
22 * but I doubd that fastdep will work at all under a UNICODE system. ;-)
23 */
24#if defined(UNICODE) && !defined(__WIN32OS2__)
25#define CBNEWLINE (2)
26#else
27#define CBNEWLINE (1)
28#endif
29
30
31/*
32 * Time stamp size.
33 */
34#define TS_SIZE (48)
35
36
37/*******************************************************************************
38* Header Files *
39*******************************************************************************/
40#if defined(OS2FAKE)
41#include "os2fake.h"
42#else
43#include <os2.h>
44#endif
45
46#include <stdio.h>
47#include <string.h>
48#include <stdlib.h>
49#include <direct.h>
50#include <assert.h>
51
52#include "avl.h"
53
54#ifdef __WIN32OS2__
55# define WIN32API
56# include <odinbuild.h>
57#else
58# define ODIN32_BUILD_NR -1
59#endif
60
61#ifndef INLINE
62# if defined(__IBMC__)
63# define INLINE _Inline
64# elif defined(__IBMCPP__)
65# define INLINE inline
66# elif defined(__WATCOMC__)
67# define INLINE __inline
68# elif defined(__WATCOM_CPLUSPLUS__)
69# define INLINE inline
70# else
71# error message("unknown compiler - inline keyword unknown!")
72# endif
73#endif
74
75/*
76 * This following section is used while testing fastdep.
77 * stdio.h should be included; string.h never included.
78 */
79/*
80#include <stdio.h>
81#include <stdlib.h>
82#include <string.h>
83*/
84
85#if 1
86#include <stdio.h>
87#else
88#include <string.h>
89#include <string.h>
90#endif
91
92/*
93 */ /* */ /*
94#include <string.h>
95 */
96#if 1
97# if 1
98 #if 0
99# include <string.h>
100 #else
101# if 1
102 #if 1
103 #if 0
104# include <string.h>
105 #else /* */ /*
106*/
107 # include <stdio.h>
108 #endif
109 #endif
110 #endif
111 #endif
112 #endif
113#endif
114
115/*******************************************************************************
116* Structures and Typedefs *
117*******************************************************************************/
118typedef struct _Options
119{
120 const char * pszInclude;
121 const char * pszExclude;
122 BOOL fExcludeAll;
123 const char * pszObjectExt;
124 const char * pszObjectDir;
125 BOOL fObjectDir; /* replace object directory? */
126 const char * pszRsrcExt;
127 BOOL fObjRule;
128 BOOL fNoObjectPath;
129 BOOL fSrcWhenObj;
130 BOOL fAppend; /* append to the output file, not overwrite it. */
131 BOOL fCheckCyclic; /* allways check for cylic dependency before inserting an dependent. */
132 BOOL fCacheSearchDirs; /* cache entire search dirs. */
133 const char * pszExcludeFiles; /* List of excluded files. */
134 BOOL fForceScan; /* Force scan of all files. */
135} OPTIONS, *POPTIONS;
136
137
138/*
139 * Language specific analysis functions type.
140 */
141typedef int ( _FNLANG) (const char *pszFilename, const char *pszNormFilename,
142 const char *pszTS, BOOL fHeader);
143typedef _FNLANG *PFNLANG;
144
145
146/**
147 * This struct holds the static configuration of the util.
148 */
149typedef struct _ConfigEntry
150{
151 const char **papszExts; /* Pointer to an array of pointer to extentions for this handler. */
152 /* If NULL this is the last entry. */
153 int iFirstHdr; /* Index into the papszExts array of the first headerfile/copybook. */
154 /* Set it to the NULL element of the array if no headers for this extention. */
155 /* A non-header file may get a object rule. */
156 PFNLANG pfn; /* Pointer to handler function. */
157} CONFIGENTRY, *PCONFIGENTRY;
158
159
160/**
161 * Dependant Rule
162 */
163typedef struct _DepRule
164{
165 AVLNODECORE avlCore;
166 char * pszRule; /* Pointer to rule name */
167 int cDeps; /* Entries in the dependant array. */
168 char ** papszDep; /* Pointer to an array of pointers to dependants. */
169 BOOL fUpdated; /* If we have updated this entry during the run. */
170 char szTS[TS_SIZE]; /* Time stamp. */
171} DEPRULE, *PDEPRULE;
172
173
174/**
175 * Filename cache entry.
176 */
177#define FCACHEENTRY AVLNODECORE
178#define PFCACHEENTRY PAVLNODECORE
179
180
181/*******************************************************************************
182* Internal Functions *
183*******************************************************************************/
184static void syntax(void);
185static int makeDependent(const char *pszFilename, const char *pszTS);
186
187static int langC_CPP(const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader);
188static int langAsm( const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader);
189static int langRC( const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader);
190static int langCOBOL(const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader);
191static int langIPF( const char *pszFilename, const char *pszNormFilename, const char *pszTS, BOOL fHeader);
192
193
194/* string operations */
195static int strnicmpwords(const char *pszS1, const char *pszS2, int cch);
196
197/* file operations */
198static char *fileNormalize(char *pszFilename);
199static char *fileNormalize2(const char *pszFilename, char *pszBuffer);
200 char *filePath(const char *pszFilename, char *pszBuffer);
201static char *filePathSlash(const char *pszFilename, char *pszBuffer);
202static char *filePathSlash2(const char *pszFilename, char *pszBuffer);
203static char *fileName(const char *pszFilename, char *pszBuffer);
204static char *fileNameNoExt(const char *pszFilename, char *pszBuffer);
205static char *fileExt(const char *pszFilename, char *pszBuffer);
206
207/* filecache operations */
208static BOOL filecacheAddFile(const char *pszFilename);
209static BOOL filecacheAddDir(const char *pszDir);
210INLINE BOOL filecacheFind(const char *pszFilename);
211INLINE BOOL filecacheIsDirCached(const char *pszDir);
212static char*filecacheFileExist(const char *pszFilename, char *pszBuffer);
213
214/* pathlist operations */
215static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer);
216static BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename);
217
218/* word operations */
219static char *findEndOfWord(char *psz);
220#if 0 /* not used */
221static char *findStartOfWord(char *psz, const char *pszStart);
222#endif
223
224/* file helpers */
225static signed long fsize(FILE *phFile);
226
227/* text helpers */
228INLINE char *trim(char *psz);
229INLINE char *trimR(char *psz);
230INLINE char *trimQuotes(char *psz);
231
232/* preprocessors */
233static char *PreProcessLine(char *pszOut, const char *pszIn);
234
235/* textbuffer */
236static void *textbufferCreate(const char *pszFilename);
237static void textbufferDestroy(void *pvBuffer);
238static char *textbufferNextLine(void *pvBuffer, char *psz);
239static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer);
240
241/* depend workers */
242static BOOL depReadFile(const char *pszFilename, BOOL fAppend);
243static BOOL depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly);
244static void depRemoveAll(void);
245static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS, BOOL fConvertName);
246static BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic, BOOL fConvertName);
247static int depNameToReal(char *pszName);
248static int depNameToMake(char *pszName, int cchName, const char *pszSrc);
249static void depMarkNotFound(void *pvRule);
250static BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep);
251static BOOL depValidate(PDEPRULE pdepRule);
252INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3);
253
254
255/*******************************************************************************
256* Global Variables *
257*******************************************************************************/
258/*
259 * Pointer to the list of dependencies.
260 */
261static PDEPRULE pdepTree = NULL;
262
263
264/*
265 * Filecache - tree starts here.
266 */
267static PFCACHEENTRY pfcTree = NULL;
268static unsigned cfcNodes = 0;
269static PFCACHEENTRY pfcDirTree = NULL;
270
271
272/*
273 * Current directory stuff
274 */
275static char szCurDir[CCHMAXPATH];
276static int aiSlashes[CCHMAXPATH];
277static int cSlashes;
278
279
280/*
281 * Environment variables used.
282 * (These has the correct case.)
283 */
284static char * pszIncludeEnv;
285
286
287/*
288 * Configuration stuff.
289 */
290static const char pszDefaultDepFile[] = ".depend";
291static const char *apszExtC_CPP[] = {"c", "sqc", "cpp", "h", "hpp", NULL};
292static const char *apszExtAsm[] = {"asm", "inc", NULL};
293static const char *apszExtRC[] = {"rc", "orc", "dlg", NULL};
294static const char *apszExtCOBOL[] = {"cbl", "cob", "sqb", "wbl", NULL};
295static const char *apszExtIPF[] = {"ipf", "man", NULL};
296static CONFIGENTRY aConfig[] =
297{
298 {
299 apszExtC_CPP,
300 3,
301 langC_CPP,
302 },
303
304 {
305 apszExtAsm,
306 1,
307 langAsm,
308 },
309
310 {
311 apszExtRC,
312 2,
313 langRC,
314 },
315
316 {
317 apszExtCOBOL,
318 -1,
319 langCOBOL,
320 },
321
322 {
323 apszExtIPF,
324 -1,
325 langIPF,
326 },
327
328 /* terminating entry */
329 {
330 NULL,
331 -1,
332 NULL
333 }
334};
335
336
337static char szObjectDir[CCHMAXPATH];
338static char szObjectExt[64] = "obj";
339static char szRsrcExt[64] = "res";
340static char szInclude[32768] = ";";
341static char szExclude[32768] = ";";
342static char szExcludeFiles[65536] = "";
343
344OPTIONS options =
345{
346 szInclude, /* pszInclude */
347 szExclude, /* pszExclude */
348 FALSE, /* fExcludeAll */
349 szObjectExt, /* pszObjectExt */
350 szObjectDir, /* pszObjectDir */
351 FALSE, /* fObjectDir */
352 szRsrcExt, /* pszRsrcExt */
353 TRUE, /* fObjRule */
354 FALSE, /* fNoObjectPath */
355 TRUE, /* fSrcWhenObj */
356 FALSE, /* fAppend */
357 TRUE, /* fCheckCyclic */
358 TRUE, /* fCacheSearchDirs */
359 szExcludeFiles, /* pszExcludeFiles */
360 FALSE /* fForceScan */
361};
362
363
364/**
365 * Main function.
366 * @returns 0 on success.
367 * -n count of failiures.
368 * @param
369 * @param
370 * @equiv
371 * @precond
372 * @methdesc
373 * @result
374 * @time
375 * @sketch
376 * @algo
377 * @remark
378 */
379int main(int argc, char **argv)
380{
381 int rc = 0;
382 int argi = 1;
383 int i;
384 char * psz;
385 char * psz2;
386 const char *pszDepFile = pszDefaultDepFile;
387 char achBuffer[4096];
388
389 szObjectDir[0] = '\0';
390
391 if (argc == 1)
392 {
393 syntax();
394 return -87;
395 }
396
397 /*
398 * Initiate current directory stuff
399 */
400 if (_getcwd(szCurDir, sizeof(szCurDir)) == NULL)
401 {
402 fprintf(stderr, "fatal error: failed to get current directory\n");
403 return -88;
404 }
405 strlwr(szCurDir);
406 aiSlashes[0] = 0;
407 for (i = 1, cSlashes; szCurDir[i] != '\0'; i++)
408 {
409 if (szCurDir[i] == '/')
410 szCurDir[i] = '\\';
411 if (szCurDir[i] == '\\')
412 aiSlashes[cSlashes++] = i;
413 }
414 if (szCurDir[i-1] != '\\')
415 {
416 aiSlashes[cSlashes] = i;
417 szCurDir[i++] = '\\';
418 szCurDir[i] = '\0';
419 }
420
421
422 /*
423 * Initiate environment variables used: INCLUDE
424 */
425 psz = getenv("INCLUDE");
426 if (psz != NULL)
427 {
428 pszIncludeEnv = strdup(psz);
429 strlwr(pszIncludeEnv);
430 }
431 else
432 pszIncludeEnv = "";
433
434
435 /*
436 * Disable hard errors.
437 */
438 DosError(FERR_DISABLEHARDERR | FERR_ENABLEEXCEPTION);
439
440
441 /*
442 * parse arguments
443 */
444 while (argi < argc)
445 {
446 if (argv[argi][0] == '-' || argv[argi][0] == '/')
447 {
448 /* parameters */
449 switch (argv[argi][1])
450 {
451 case 'A':
452 case 'a': /* Append to the output file */
453 options.fAppend = argv[argi][2] != '-';
454 break;
455
456 case 'D':
457 case 'd': /* "-d <filename>" */
458 {
459 const char *pszOld = pszDepFile;
460 if (argv[argi][2] != '\0')
461 pszDepFile = &argv[argi][2];
462 else
463 {
464 argi++;
465 if (argi < argc)
466 pszDepFile = argv[argi];
467 else
468 {
469 fprintf(stderr, "invalid parameter -d, filename missing!\n");
470 return -1;
471 }
472 }
473
474 /* if dependencies are generated we'll flush them to the old filename */
475 if (pdepTree != NULL && pszOld != pszDepFile)
476 {
477 if (!depWriteFile(pszOld, !options.fAppend))
478 fprintf(stderr, "error: failed to write (flush) dependencies.\n");
479 depRemoveAll();
480 }
481 break;
482 }
483
484 case 'C': /* forced directory cache 'ca' or cylic check 'cy'*/
485 case 'c':
486 if (argv[argi][2] == 'a' || argv[argi][2] == 'A')
487 options.fCacheSearchDirs = TRUE;
488 else if ((argv[argi][2] == 'y' || argv[argi][2] == 'Y'))
489 options.fCheckCyclic = argv[argi][3] != '-';
490 break;
491
492 case 'E': /* list of paths. If a file is found in one of these directories the */
493 case 'e': /* filename will be used without the directory path. */
494 /* Eall<[+]|-> ? */
495 if (strlen(&argv[argi][1]) <= 5 && strnicmp(&argv[argi][1], "Eall", 4) == 0)
496 {
497 options.fExcludeAll = argv[argi][5] != '-';
498 break;
499 }
500 /* path or path list */
501 if (strlen(argv[argi]) > 2)
502 psz = &argv[argi][2];
503 else
504 {
505 if (++argi >= argc)
506 {
507 fprintf(stderr, "syntax error! Option -e.\n");
508 return 1;
509 }
510 psz = argv[argi];
511 }
512 /* check if enviroment variable */
513 if (*psz == '%')
514 {
515 psz2 = strdup(psz+1);
516 if (psz2 != NULL && *psz2 != '\0')
517 {
518 if (psz2[strlen(psz2)-1] == '%')
519 psz2[strlen(psz2)-1] = '\0';
520 psz = getenv(psz2);
521 free(psz2);
522 if (psz == NULL)
523 break;
524 }
525 else
526 {
527 fprintf(stderr, "error: -E% is not an valid argument!\n");
528 return -1;
529 }
530 }
531 if (psz != NULL)
532 {
533 strcat(szExclude, psz);
534 strlwr(szExclude);
535 if (szExclude[strlen(szExclude)-1] != ';')
536 strcat(szExclude, ";");
537 }
538 break;
539
540 case 'f':
541 case 'F': /* force scan of all files. */
542 options.fForceScan = argv[argi][2] != '-';
543 break;
544
545 case 'I': /* optional include path. This has precedence over the INCLUDE environment variable. */
546 case 'i':
547 if (strlen(argv[argi]) > 2)
548 psz = &argv[argi][2];
549 else
550 {
551 if (++argi >= argc)
552 {
553 fprintf(stderr, "syntax error! Option -i.\n");
554 return 1;
555 }
556 psz = argv[argi];
557 }
558 /* check if enviroment variable */
559 if (*psz == '%')
560 {
561 psz2 = strdup(psz+1);
562 if (psz2 != NULL && *psz2 != '\0')
563 {
564 if (psz2[strlen(psz2)-1] == '%')
565 psz2[strlen(psz2)-1] = '\0';
566 psz = getenv(psz2);
567 free(psz2);
568 if (psz == NULL)
569 break;
570 }
571 else
572 {
573 fprintf(stderr, "error: -I% is not an valid argument!\n");
574 return -1;
575 }
576 }
577 if (psz != NULL)
578 {
579 strcat(szInclude, psz);
580 strlwr(szInclude);
581 if (szInclude[strlen(szInclude)-1] != ';')
582 strcat(szInclude, ";");
583 }
584 break;
585
586 case 'n': /* no object path , -N<[+]|-> */
587 case 'N':
588 if (strlen(argv[argi]) <= 1+1+1)
589 options.fNoObjectPath = argv[argi][2] != '-';
590 else
591 {
592 fprintf(stderr, "error: invalid parameter!, '%s'\n", argv[argi]);
593 return -1;
594 }
595 break;
596
597 case 'o': /* object base directory, Obj or Obr<[+]|-> */
598 case 'O':
599 if (strlen(&argv[argi][1]) <= 4 && strnicmp(&argv[argi][1], "Obr", 3) == 0)
600 {
601 options.fObjRule = argv[argi][4] != '-';
602 break;
603 }
604
605 if (strlen(&argv[argi][1]) >= 4 && strnicmp(&argv[argi][1], "Obj", 3) == 0)
606 {
607 if (strlen(argv[argi]) > 4)
608 strcpy(szObjectExt, argv[argi]+4);
609 else
610 {
611 if (++argi >= argc)
612 {
613 fprintf(stderr, "syntax error! Option -obj.\n");
614 return 1;
615 }
616 strcpy(szObjectExt, argv[argi]);
617 }
618 break;
619 }
620
621 /* path: -o or -o- */
622 options.fObjectDir = TRUE;
623 if (strlen(argv[argi]) > 2)
624 {
625 if (argv[argi][2] == '-') /* no object path */
626 szObjectDir[0] = '\0';
627 else
628 strcpy(szObjectDir, argv[argi]+2);
629 }
630 else
631 {
632 if (++argi >= argc)
633 {
634 fprintf(stderr, "syntax error! Option -o.\n");
635 return 1;
636 }
637 strcpy(szObjectDir, argv[argi]);
638 }
639 if (szObjectDir[0] != '\0'
640 && szObjectDir[strlen(szObjectDir)-1] != '\\'
641 && szObjectDir[strlen(szObjectDir)-1] != '/'
642 )
643 strcat(szObjectDir, "\\");
644 break;
645
646 case 'r':
647 case 'R':
648 if (strlen(argv[argi]) > 2)
649 strcpy(szRsrcExt, argv[argi]+2);
650 else
651 {
652 if (++argi >= argc)
653 {
654 fprintf(stderr, "syntax error! Option -r.\n");
655 return 1;
656 }
657 strcpy(szRsrcExt, argv[argi]);
658 }
659 break;
660
661 case 'x':
662 case 'X': /* Exclude files */
663 psz = &achBuffer[CCHMAXPATH+8];
664 if (strlen(argv[argi]) > 2)
665 strcpy(psz, &argv[argi][2]);
666 else
667 {
668 if (++argi >= argc)
669 {
670 fprintf(stderr, "syntax error! Option -x.\n");
671 return 1;
672 }
673 strcpy(psz, argv[argi]);
674 }
675 while (psz != NULL && *psz != ';')
676 {
677 char * pszNext = strchr(psz, ';');
678 int cch = strlen(szExcludeFiles);
679 if (pszNext)
680 *pszNext++ = '\0';
681 if (DosQueryPathInfo(psz, FIL_QUERYFULLNAME, &szExcludeFiles[cch], CCHMAXPATH))
682 {
683 fprintf(stderr, "error: Invalid exclude name\n");
684 return -1;
685 }
686 strlwr(&szExcludeFiles[cch]);
687 strcat(&szExcludeFiles[cch], ";");
688 psz = pszNext;
689 }
690 break;
691
692 case 'h':
693 case 'H':
694 case '?':
695 syntax();
696 return 1;
697
698 default:
699 fprintf(stderr, "error: invalid parameter! '%s'\n", argv[argi]);
700 return -1;
701 }
702
703 }
704 else if (argv[argi][0] == '@')
705 { /*
706 * Parameter file (debugger parameter length restrictions led to this):
707 * Create a textbuffer.
708 * Parse the file and create a new parameter vector.
709 * Set argv to the new parameter vector, argi to 0 and argc to
710 * the parameter count.
711 * Restrictions: Parameters enclosed in "" is not implemented.
712 * No commandline parameters are processed after the @file
713 */
714 char *pszBuffer = (char*)textbufferCreate(&argv[argi][1]); /* !ASSUMS! that pvBuffer is the file string! */
715 if (pszBuffer != NULL)
716 {
717 char **apszArgs = NULL;
718 char *psz = pszBuffer;
719 int i = 0;
720
721 while (*psz != '\0')
722 {
723 /* find end of parameter word */
724 char *pszEnd = psz + 1;
725 char ch = *pszEnd;
726 while (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' && ch != '\0')
727 ch = *++pszEnd;
728
729 /* allocate more arg array space? */
730 if ((i % 512) == 0)
731 {
732 apszArgs = realloc(apszArgs, sizeof(char*) * 512);
733 if (apszArgs == NULL)
734 {
735 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
736 return -8;
737 }
738 }
739 *pszEnd = '\0';
740 apszArgs[i++] = psz;
741
742 /* next */
743 psz = pszEnd + 1;
744 ch = *psz;
745 while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
746 ch = *++psz;
747 }
748
749 argc = i;
750 argi = 0;
751 argv = apszArgs;
752 continue;
753 }
754 else
755 {
756 fprintf(stderr, "error: could not open parameter file\n");
757 return -1;
758 }
759 }
760 else
761 { /* not a parameter! */
762 ULONG ulRc;
763 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
764 HDIR hDir = HDIR_CREATE;
765 ULONG cFiles = ~0UL;
766 int i;
767
768
769 /*
770 * If append option is or if the forcescan option isn't is
771 * we'll have to read the existing dep file before starting
772 * adding new dependencies.
773 */
774 if (pdepTree == NULL && (options.fAppend || !options.fForceScan))
775 depReadFile(pszDepFile, options.fAppend);
776
777 /*
778 * Search for the files specified.
779 */
780 ulRc = DosFindFirst(argv[argi], &hDir,
781 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
782 pfindbuf3, sizeof(achBuffer), &cFiles, FIL_STANDARD);
783 if (!options.fCacheSearchDirs)
784 options.fCacheSearchDirs = cFiles > 25;
785 while (ulRc == NO_ERROR)
786 {
787 for (i = 0;
788 i < cFiles;
789 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
790 )
791 {
792 const char * psz;
793 char szSource[CCHMAXPATH];
794 BOOL fExcluded;
795 char szTS[TS_SIZE];
796
797 /*
798 * Make full path.
799 */
800 if ((psz = strrchr(argv[argi], '\\')) || (psz = strrchr(argv[argi], '/')) || (*(psz = &argv[argi][1]) == ':'))
801 {
802 strncpy(szSource, argv[argi], psz - argv[argi] + 1);
803 szSource[psz - argv[argi] + 1] = '\0';
804 }
805 else
806 szSource[0] = '\0';
807 strcat(szSource, pfindbuf3->achName);
808 strlwr(szSource);
809 fileNormalize(szSource);
810
811 /*
812 * Check if this is an excluded file.
813 */
814 fExcluded = FALSE;
815 psz = options.pszExcludeFiles;
816 while (*psz != '\0' && *psz != ';')
817 {
818 const char * pszNext = strchr(psz, ';');
819 if (strlen(szSource) == pszNext - psz && strncmp(szSource, psz, pszNext - psz) == 0)
820 fExcluded = TRUE;
821 psz = pszNext + 1;
822 }
823 if (fExcluded)
824 continue;
825
826 /*
827 * Analyse the file.
828 */
829 depMakeTS(szTS, pfindbuf3);
830 rc -= makeDependent(&szSource[0], szTS);
831 }
832
833 /* next file */
834 cFiles = ~0UL;
835 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
836 ulRc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
837 }
838 DosFindClose(hDir);
839 }
840 /* next */
841 argi++;
842 }
843
844 /* Write the depend file! */
845 if (!depWriteFile(pszDepFile, !options.fAppend))
846 fprintf(stderr, "error: failed to write dependencies file!\n");
847 #if 0
848 printf("cfcNodes=%d\n", cfcNodes);
849 #endif
850
851 return rc;
852}
853
854
855/**
856 * Displays the syntax description for this util.
857 * @status completely implemented.
858 * @author knut st. osmundsen
859 */
860void syntax(void)
861{
862 printf(
863 "FastDep v0.45 (build %d)\n"
864 "Dependency scanner. Creates a makefile readable depend file.\n"
865 " - was quick and dirty, now it's just quick -\n"
866 "\n"
867 "Syntax: FastDep [options] <files> [more options [more files [...]]]\n"
868 " or\n"
869 " FastDep [options] @<parameterfile>\n"
870 "\n"
871 "Options:\n"
872 " -a<[+]|-> Append to the output file. Default: Overwrite.\n"
873 " -ca Force search directory caching.\n"
874 " Default: cache if more that 25 files are to be searched.\n"
875 " (more than 25 in the first file expression.)\n"
876 " -cy<[+]|-> Check for cylic dependencies. Default: -cy-\n"
877 " -d <outputfn> Output filename. Default: %s\n"
878 " -e excludepath Exclude paths. If a filename is found in any\n"
879 " of these paths only the filename is used, not\n"
880 " the path+filename (which is default).\n"
881 " -eall<[+]|-> Include and source filenames, paths or no paths.\n"
882 " -eall+: No path are added to the filename.\n"
883 " -eall-: The filename is appended the include path\n"
884 " was found in.\n"
885 " Default: eall-\n"
886 " -f<[+]|-> Force scanning of all files. If disabled we'll only scan\n"
887 " files which are younger or up to one month older than the\n"
888 " dependancy file (if it exists). Default: disabled\n"
889 " -i <include> Additional include paths. INCLUDE is searched after this.\n"
890 " -n<[+]|-> No path for object files in the rules.\n"
891 " -o <objdir> Path were object files are placed. This path replaces the\n"
892 " entire filename path\n"
893 " -o- No object path\n"
894 " -obr<[+]|-> -obr+: Object rule.\n"
895 " -obr-: No object rule, rule for source filename is generated.\n"
896 " -obj[ ]<objext> Object extention. Default: obj\n"
897 " -r[ ]<rsrcext> Resource binary extention. Default: res\n"
898 " -x[ ]<f1[;f2]> Files to exclude. Only exact filenames.\n"
899 " <files> Files to scan. Wildchars are allowed.\n"
900 "\n"
901 "Options and files could be mixed.\n"
902 " copyright (c) 1999-2002 knut st. osmundsen (bird@anduin.net)\n",
903 ODIN32_BUILD_NR,
904 pszDefaultDepFile
905 );
906}
907
908
909/**
910 * Generates depend info on this file, these are stored internally
911 * and written to file later.
912 * @returns
913 * @param pszFilename Pointer to source filename. Correct case is assumed!
914 * @param pszTS File time stamp.
915 * @status completely implemented.
916 * @author knut st. osmundsen
917 */
918int makeDependent(const char *pszFilename, const char *pszTS)
919{
920 int rc = -1;
921
922 char szExt[CCHMAXPATH];
923 PCONFIGENTRY pCfg = &aConfig[0];
924 BOOL fHeader;
925
926 /*
927 * Find which filetype this is...
928 */
929 fileExt(pszFilename, szExt);
930 while (pCfg->papszExts != NULL)
931 {
932 const char **ppsz = pCfg->papszExts;
933 while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
934 ppsz++;
935 if (*ppsz != NULL)
936 {
937 fHeader = pCfg->iFirstHdr > 0 && &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
938 break;
939 }
940 pCfg++;
941 }
942
943 /* Found? */
944 if (pCfg->papszExts != NULL)
945 {
946 char szNormFile[CCHMAXPATH];
947 fileNormalize2(pszFilename, szNormFile);
948 rc = (*pCfg->pfn)(pszFilename, &szNormFile[0], pszTS, fHeader);
949 }
950 else
951 {
952 if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
953 fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
954 rc = 0;
955 }
956
957
958 return rc;
959}
960
961
962/**
963 * Generates depend info on this C or C++ file, these are stored internally
964 * and written to file later.
965 * @returns 0 on success.
966 * !0 on error.
967 * @param pszFilename Pointer to source filename. Correct case is assumed!
968 * @param pszNormFilename Pointer to normalized source filename.
969 * @param pszTS File time stamp.
970 * @parma fHeader True if header file is being scanned.
971 * @status completely implemented.
972 * @author knut st. osmundsen
973 */
974int langC_CPP(const char *pszFilename, const char *pszNormFilename,
975 const char *pszTS, BOOL fHeader)
976{
977 void * pvFile; /* Text buffer pointer. */
978 void * pvRule; /* Handle to the current rule. */
979 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
980 int iLine; /* Linenumber. */
981 void * pv = NULL; /* An index used by textbufferGetNextLine. */
982 BOOL fComment; /* TRUE when within a multiline comment. */
983 /* FALSE when not within a multiline comment. */
984 int iIfStack; /* StackPointer. */
985 struct IfStackEntry
986 {
987 int fIncluded : 1; /* TRUE: include this code;
988 * FALSE: excluded */
989 int fIf : 1; /* TRUE: #if part of the expression.
990 * FALSE: #else part of the expression. */
991 int fSupported : 1; /* TRUE: supported if/else statement
992 * FALSE: unsupported all else[<something>] are ignored
993 * All code is included.
994 */
995 } achIfStack[256];
996 char szSrcDir[CCHMAXPATH];
997 filePath(pszNormFilename, szSrcDir);/* determin source code directory. */
998
999 /**********************************/
1000 /* Add the depend rule */
1001 /**********************************/
1002 if (options.fObjRule && !fHeader)
1003 {
1004 if (options.fNoObjectPath)
1005 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
1006 else
1007 pvRule = depAddRule(options.fObjectDir ?
1008 options.pszObjectDir :
1009 filePathSlash(pszFilename, szBuffer),
1010 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1011 options.pszObjectExt, pszTS, FALSE);
1012
1013 if (options.fSrcWhenObj && pvRule)
1014 depAddDepend(pvRule,
1015 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1016 fileName(pszFilename, szBuffer) : pszNormFilename,
1017 options.fCheckCyclic,
1018 FALSE);
1019 }
1020 else
1021 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1022 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1023
1024 /* duplicate rule? */
1025 if (pvRule == NULL)
1026 return 0;
1027
1028
1029 /********************/
1030 /* Make file buffer */
1031 /********************/
1032 pvFile = textbufferCreate(pszFilename);
1033 if (!pvFile)
1034 {
1035 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1036 return -1;
1037 }
1038
1039
1040 /*******************/
1041 /* find dependants */
1042 /*******************/
1043 /* Initiate the IF-stack, comment state and line number. */
1044 iIfStack = 0;
1045 achIfStack[iIfStack].fIf = TRUE;
1046 achIfStack[iIfStack].fIncluded = TRUE;
1047 achIfStack[iIfStack].fSupported = TRUE;
1048 fComment = FALSE;
1049 iLine = 0;
1050 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1051 {
1052 /* search for #include */
1053 register char *pszC;
1054 int cbLen;
1055 int i = 0;
1056 iLine++;
1057
1058 /* skip blank chars */
1059 cbLen = strlen(szBuffer);
1060 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1061 i++;
1062
1063 /* preprocessor statement? */
1064 if (!fComment && szBuffer[i] == '#')
1065 {
1066 /*
1067 * Preprocessor checks
1068 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1069 * Depending on the word afterwards we'll take some different actions.
1070 * So we'll start of by extracting that word and make a string swich on it.
1071 * Note that there might be some blanks between the hash and the word.
1072 */
1073 int cchWord;
1074 char * pszEndWord;
1075 char * pszArgument;
1076 i++; /* skip hash ('#') */
1077 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1078 i++;
1079 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1080 cchWord = pszEndWord - &szBuffer[i];
1081
1082 /*
1083 * Find the argument by skipping the blanks.
1084 */
1085 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1086 pszArgument++;
1087
1088 /*
1089 * string switch.
1090 */
1091 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1092 {
1093 /*
1094 * #include
1095 *
1096 * Are we in a state where this file is to be included?
1097 */
1098 if (achIfStack[iIfStack].fIncluded)
1099 {
1100 char szFullname[CCHMAXPATH];
1101 char * psz;
1102 BOOL f = FALSE;
1103 int j;
1104 BOOL fQuote;
1105
1106 /* extract info between "" or <> */
1107 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1108 i++;
1109 fQuote = szBuffer[i] == '"';
1110 i++; /* skip '"' or '<' */
1111
1112 /* if invalid statement then continue with the next line! */
1113 if (!f) continue;
1114
1115 /* find end */
1116 j = f = 0;
1117 while (i + j < cbLen && j < CCHMAXPATH &&
1118 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1119 j++;
1120
1121 /* if invalid statement then continue with the next line! */
1122 if (!f) continue;
1123
1124 /* copy filename */
1125 strncpy(szFullname, &szBuffer[i], j);
1126 szFullname[j] = '\0'; /* ensure terminatition. */
1127 strlwr(szFullname);
1128
1129 /* find include file! */
1130 psz = fQuote ? pathlistFindFile(szSrcDir, szFullname, szBuffer) : NULL;
1131 if (psz == NULL)
1132 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1133 if (psz == NULL)
1134 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1135
1136 /* did we find the include? */
1137 if (psz != NULL)
1138 {
1139 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1140 { /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
1141 if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
1142 depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1143 else
1144 fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1145 pszFilename, iLine, szFullname);
1146 }
1147 else
1148 depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
1149 }
1150 else
1151 {
1152 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1153 pszFilename, iLine, szFullname);
1154 depMarkNotFound(pvRule);
1155 }
1156 }
1157 }
1158 else
1159 /*
1160 * #if
1161 */
1162 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1163 { /* #if 0 and #if <1-9> are supported */
1164 pszEndWord = findEndOfWord(pszArgument);
1165 iIfStack++;
1166 if ((pszEndWord - pszArgument) == 1
1167 && *pszArgument >= '0' && *pszArgument <= '9')
1168 {
1169 if (*pszArgument != '0')
1170 achIfStack[iIfStack].fIncluded = TRUE;
1171 else
1172 achIfStack[iIfStack].fIncluded = FALSE;
1173 }
1174 else
1175 achIfStack[iIfStack].fSupported = FALSE;
1176 achIfStack[iIfStack].fIncluded = TRUE;
1177 achIfStack[iIfStack].fIf = TRUE;
1178 }
1179 else
1180 /*
1181 * #else
1182 */
1183 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1184 {
1185 if (achIfStack[iIfStack].fSupported)
1186 {
1187 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1188 achIfStack[iIfStack].fIncluded = FALSE;
1189 else
1190 achIfStack[iIfStack].fIncluded = TRUE;
1191 }
1192 achIfStack[iIfStack].fIf = FALSE;
1193 }
1194 else
1195 /*
1196 * #endif
1197 */
1198 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1199 { /* Pop the if-stack. */
1200 if (iIfStack > 0)
1201 iIfStack--;
1202 else
1203 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1204 }
1205 /*
1206 * general if<something> and elseif<something> implementations
1207 */
1208 else
1209 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1210 {
1211 achIfStack[iIfStack].fSupported = FALSE;
1212 achIfStack[iIfStack].fIncluded = TRUE;
1213 }
1214 else
1215 if (strncmp(&szBuffer[i], "if", 2) == 0)
1216 {
1217 iIfStack++;
1218 achIfStack[iIfStack].fIf = TRUE;
1219 achIfStack[iIfStack].fSupported = FALSE;
1220 achIfStack[iIfStack].fIncluded = TRUE;
1221 }
1222 /* The rest of them aren't implemented yet.
1223 else if (strncmp(&szBuffer[i], "if") == 0)
1224 {
1225 }
1226 */
1227 }
1228
1229 /*
1230 * Comment checks.
1231 * -Start at first non-blank.
1232 * -Loop thru the line since we might have more than one
1233 * comment statement on a single line.
1234 */
1235 pszC = &szBuffer[i];
1236 while (pszC != NULL && *pszC != '\0')
1237 {
1238 if (fComment)
1239 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
1240 else
1241 {
1242 char *pszLC;
1243 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
1244 pszC = strstr(pszC, "/*"); /* look for start comment mark */
1245 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
1246 break; /* muliline comment mark we'll ignore the multiline mark. */
1247 }
1248
1249 /* Comment mark found? */
1250 if (pszC != NULL)
1251 {
1252 fComment = !fComment;
1253 pszC += 2; /* skip comment mark */
1254
1255 /* debug */
1256 /*
1257 if (fComment)
1258 fprintf(stderr, "starts at line %d\n", iLine);
1259 else
1260 fprintf(stderr, "ends at line %d\n", iLine);
1261 */
1262 }
1263 }
1264 } /*while*/
1265
1266 textbufferDestroy(pvFile);
1267
1268 return 0;
1269}
1270
1271
1272/**
1273 * Generates depend info on this file, these are stored internally
1274 * and written to file later.
1275 * @returns 0 on success.
1276 * !0 on error.
1277 * @param pszFilename Pointer to source filename. Correct case is assumed!
1278 * @param pszNormFilename Pointer to normalized source filename.
1279 * @param pszTS File time stamp.
1280 * @parma fHeader True if header file is being scanned.
1281 * @status completely implemented.
1282 * @author knut st. osmundsen
1283 */
1284int langAsm(const char *pszFilename, const char *pszNormFilename,
1285 const char *pszTS, BOOL fHeader)
1286{
1287 void * pvFile; /* Text buffer pointer. */
1288 void * pvRule; /* Handle to the current rule. */
1289 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1290 int iLine; /* current line number */
1291 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1292
1293
1294 /**********************************/
1295 /* Add the depend rule */
1296 /**********************************/
1297 if (options.fObjRule && !fHeader)
1298 {
1299 if (options.fNoObjectPath)
1300 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
1301 else
1302 pvRule = depAddRule(options.fObjectDir ?
1303 options.pszObjectDir :
1304 filePathSlash(pszFilename, szBuffer),
1305 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1306 options.pszObjectExt, pszTS, FALSE);
1307
1308 if (options.fSrcWhenObj && pvRule)
1309 depAddDepend(pvRule,
1310 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1311 fileName(pszFilename, szBuffer) : pszNormFilename,
1312 options.fCheckCyclic, FALSE);
1313 }
1314 else
1315 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1316 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1317
1318 /* duplicate rule? */
1319 if (pvRule == NULL)
1320 return 0;
1321
1322
1323 /********************/
1324 /* Make file buffer */
1325 /********************/
1326 pvFile = textbufferCreate(pszFilename);
1327 if (!pvFile)
1328 {
1329 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1330 return -1;
1331 }
1332
1333
1334 /*******************/
1335 /* find dependants */
1336 /*******************/
1337 iLine = 0;
1338 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1339 {
1340 /* search for include */
1341 int cbLen;
1342 int i = 0;
1343 iLine++;
1344
1345 /* skip blank chars */
1346 cbLen = strlen(szBuffer);
1347 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1348 i++;
1349
1350 /* is this an include? */
1351 if (strnicmp(&szBuffer[i], "include", 7) == 0
1352 && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
1353 )
1354 {
1355 char szFullname[CCHMAXPATH];
1356 char *psz;
1357 int j;
1358
1359 /* skip to first no blank char */
1360 i += 7;
1361 while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1362 i++;
1363
1364 /* comment check - if comment found, no filename was given. continue. */
1365 if (szBuffer[i] == ';') continue;
1366
1367 /* find end */
1368 j = 0;
1369 while (i + j < cbLen
1370 && j < CCHMAXPATH
1371 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
1372 && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';' && szBuffer[i+j] != '\r'
1373 )
1374 j++;
1375
1376 /* copy filename */
1377 strncpy(szFullname, &szBuffer[i], j);
1378 szFullname[j] = '\0'; /* ensure terminatition. */
1379 strlwr(szFullname);
1380
1381 /* find include file! */
1382 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1383 if (psz == NULL)
1384 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1385
1386 /* Did we find the include? */
1387 if (psz != NULL)
1388 {
1389 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1390 { /* include sys/stats.inc makes trouble, check for '/' and '\'. */
1391 if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
1392 depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1393 else
1394 fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1395 pszFilename, iLine, szFullname);
1396 }
1397 else
1398 depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
1399 }
1400 else
1401 {
1402 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1403 pszFilename, iLine, szFullname);
1404 depMarkNotFound(pvRule);
1405 }
1406 }
1407 } /*while*/
1408
1409 textbufferDestroy(pvFile);
1410
1411 return 0;
1412}
1413
1414
1415/**
1416 * Generates depend info on this Resource file, these are stored internally
1417 * and written to file later.
1418 * @returns 0 on success.
1419 * !0 on error.
1420 * @param pszFilename Pointer to source filename. Correct case is assumed!
1421 * @param pszNormFilename Pointer to normalized source filename.
1422 * @param pszTS File time stamp.
1423 * @parma fHeader True if header file is being scanned.
1424 * @status completely implemented.
1425 * @author knut st. osmundsen
1426 */
1427#if 0
1428int langRC(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader)
1429{
1430 void * pvFile; /* Text buffer pointer. */
1431 void * pvRule; /* Handle to the current rule. */
1432 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1433 int iLine; /* current line number */
1434 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1435
1436
1437 /**********************************/
1438 /* Add the depend rule */
1439 /**********************************/
1440 if (options.fObjRule && !fHeader)
1441 {
1442 if (options.fNoObjectPath)
1443 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS, FALSE);
1444 else
1445 pvRule = depAddRule(options.fObjectDir ?
1446 options.pszObjectDir :
1447 filePathSlash(pszFilename, szBuffer),
1448 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1449 options.pszRsrcExt, pszTS, FALSE);
1450
1451 if (options.fSrcWhenObj && pvRule)
1452 depAddDepend(pvRule,
1453 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1454 fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1455 options.fCheckCyclic,
1456 FALSE);
1457 }
1458 else
1459 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1460 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1461
1462 /* duplicate rule? */
1463 if (pvRule == NULL)
1464 return 0;
1465
1466
1467 /********************/
1468 /* Make file buffer */
1469 /********************/
1470 pvFile = textbufferCreate(pszFilename);
1471 if (!pvFile)
1472 {
1473 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1474 return -1;
1475 }
1476
1477
1478 /*******************/
1479 /* find dependants */
1480 /*******************/
1481 iLine = 0;
1482 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1483 {
1484 /* search for #include */
1485 int cbLen;
1486 int i = 0;
1487 int i1;
1488 iLine++;
1489
1490 /* skip blank chars */
1491 cbLen = strlen(szBuffer);
1492 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1493 i++;
1494
1495 /* is this an include? */
1496 i1 = 1;
1497 if ( strncmp(&szBuffer[i], "#include", 8) == 0
1498 || (i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9)) == 0
1499 || strnicmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
1500 )
1501 {
1502 char szFullname[CCHMAXPATH];
1503 char *psz;
1504 BOOL f = FALSE;
1505 int j;
1506
1507 if (i1 != 0)
1508 { /*
1509 * #include <file.h>, #include "file.h" or DLGINCLUDE 1 "file.h"
1510 *
1511 * extract info between "" or <>
1512 */
1513 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1514 i++;
1515 i++; /* skip '"' or '<' */
1516
1517 /* if invalid statement then continue with the next line! */
1518 if (!f) continue;
1519
1520 /* find end */
1521 j = f = 0;
1522 while (i + j < cbLen && j < CCHMAXPATH &&
1523 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1524 j++;
1525
1526 /* if invalid statement then continue with the next line! */
1527 if (!f) continue;
1528 }
1529 else
1530 { /*
1531 * RCINCLUDE ["]filename.dlg["]
1532 * Extract filename.
1533 */
1534
1535 /* skip to filename.dlg start - if eol will continue to loop. */
1536 i += 9;
1537 while (szBuffer[i] == ' ' || szBuffer[i] == '\t' || szBuffer[i] == '"')
1538 i++;
1539 if (szBuffer[i] == '\0')
1540 continue;
1541
1542 /* search to end of filename. */
1543 j = i+1;
1544 while ( szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1545 && szBuffer[i+j] != '"' && szBuffer[i+j] != '\0')
1546 j++;
1547 }
1548
1549 /* copy filename */
1550 strncpy(szFullname, &szBuffer[i], j);
1551 szFullname[j] = '\0'; /* ensure terminatition. */
1552 strlwr(szFullname);
1553
1554 /* find include file! */
1555 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1556 if (psz == NULL)
1557 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1558
1559 /* did we find the include? */
1560 if (psz != NULL)
1561 {
1562 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1563 { /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
1564 if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
1565 depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1566 else
1567 fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1568 pszFilename, iLine, szFullname);
1569 }
1570 else
1571 depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
1572 }
1573 else
1574 {
1575 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1576 pszFilename, iLine, szFullname);
1577 depMarkNotFound(pvRule);
1578 }
1579 }
1580 } /*while*/
1581
1582 textbufferDestroy(pvFile);
1583 return 0;
1584}
1585#else
1586int langRC(const char *pszFilename, const char *pszNormFilename,
1587 const char *pszTS, BOOL fHeader)
1588{
1589 void * pvFile; /* Text buffer pointer. */
1590 void * pvRule; /* Handle to the current rule. */
1591 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
1592 int iLine; /* Linenumber. */
1593 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1594 BOOL fComment; /* TRUE when within a multiline comment. */
1595 /* FALSE when not within a multiline comment. */
1596 int iIfStack; /* StackPointer. */
1597 struct IfStackEntry
1598 {
1599 int fIncluded : 1; /* TRUE: include this code;
1600 * FALSE: excluded */
1601 int fIf : 1; /* TRUE: #if part of the expression.
1602 * FALSE: #else part of the expression. */
1603 int fSupported : 1; /* TRUE: supported if/else statement
1604 * FALSE: unsupported all else[<something>] are ignored
1605 * All code is included.
1606 */
1607 } achIfStack[256];
1608
1609
1610 /**********************************/
1611 /* Add the depend rule */
1612 /**********************************/
1613 if (options.fObjRule && !fHeader)
1614 {
1615 if (options.fNoObjectPath)
1616 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS, FALSE);
1617 else
1618 pvRule = depAddRule(options.fObjectDir ?
1619 options.pszObjectDir :
1620 filePathSlash(pszFilename, szBuffer),
1621 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1622 options.pszRsrcExt, pszTS, FALSE);
1623
1624 if (options.fSrcWhenObj && pvRule)
1625 depAddDepend(pvRule,
1626 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1627 fileName(pszFilename, szBuffer) : pszNormFilename,
1628 options.fCheckCyclic,
1629 FALSE);
1630 }
1631 else
1632 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1633 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1634
1635 /* duplicate rule? */
1636 if (pvRule == NULL)
1637 return 0;
1638
1639
1640 /********************/
1641 /* Make file buffer */
1642 /********************/
1643 pvFile = textbufferCreate(pszFilename);
1644 if (!pvFile)
1645 {
1646 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1647 return -1;
1648 }
1649
1650
1651 /*******************/
1652 /* find dependants */
1653 /*******************/
1654 /* Initiate the IF-stack, comment state and line number. */
1655 iIfStack = 0;
1656 achIfStack[iIfStack].fIf = TRUE;
1657 achIfStack[iIfStack].fIncluded = TRUE;
1658 achIfStack[iIfStack].fSupported = TRUE;
1659 fComment = FALSE;
1660 iLine = 0;
1661 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1662 {
1663 register char * pszC;
1664 char szFullname[CCHMAXPATH];
1665 int cbLen;
1666 int i1 = 1;
1667 int i = 0;
1668 iLine++;
1669
1670 /* skip blank chars */
1671 cbLen = strlen(szBuffer);
1672 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1673 i++;
1674
1675 /* preprocessor statement? */
1676 if (!fComment && szBuffer[i] == '#')
1677 {
1678 /*
1679 * Preprocessor checks
1680 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1681 * Depending on the word afterwards we'll take some different actions.
1682 * So we'll start of by extracting that word and make a string swich on it.
1683 * Note that there might be some blanks between the hash and the word.
1684 */
1685 int cchWord;
1686 char * pszEndWord;
1687 char * pszArgument;
1688 i++; /* skip hash ('#') */
1689 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1690 i++;
1691 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1692 cchWord = pszEndWord - &szBuffer[i];
1693
1694 /*
1695 * Find the argument by skipping the blanks.
1696 */
1697 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1698 pszArgument++;
1699
1700 /*
1701 * string switch.
1702 */
1703 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1704 {
1705 /*
1706 * #include
1707 *
1708 * Are we in a state where this file is to be included?
1709 */
1710 if (achIfStack[iIfStack].fIncluded)
1711 {
1712 char *psz;
1713 BOOL f = FALSE;
1714 int j;
1715
1716 /* extract info between "" or <> */
1717 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1718 i++;
1719 i++; /* skip '"' or '<' */
1720
1721 /* if invalid statement then continue with the next line! */
1722 if (!f) continue;
1723
1724 /* find end */
1725 j = f = 0;
1726 while (i + j < cbLen && j < CCHMAXPATH &&
1727 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1728 j++;
1729
1730 /* if invalid statement then continue with the next line! */
1731 if (!f) continue;
1732
1733 /* copy filename */
1734 strncpy(szFullname, &szBuffer[i], j);
1735 szFullname[j] = '\0'; /* ensure terminatition. */
1736 strlwr(szFullname);
1737
1738 /* find include file! */
1739 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1740 if (psz == NULL)
1741 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1742
1743 /* did we find the include? */
1744 if (psz != NULL)
1745 {
1746 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1747 { /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
1748 if (!strchr(szFullname, '/') && !strchr(szFullname, '\\'))
1749 depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1750 else
1751 fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1752 pszFilename, iLine, szFullname);
1753 }
1754 else
1755 depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
1756 }
1757 else
1758 {
1759 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1760 pszFilename, iLine, szFullname);
1761 depMarkNotFound(pvRule);
1762 }
1763 }
1764 }
1765 else
1766 /*
1767 * #if
1768 */
1769 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1770 { /* #if 0 and #if <1-9> are supported */
1771 pszEndWord = findEndOfWord(pszArgument);
1772 iIfStack++;
1773 if ((pszEndWord - pszArgument) == 1
1774 && *pszArgument >= '0' && *pszArgument <= '9')
1775 {
1776 if (*pszArgument != '0')
1777 achIfStack[iIfStack].fIncluded = TRUE;
1778 else
1779 achIfStack[iIfStack].fIncluded = FALSE;
1780 }
1781 else
1782 achIfStack[iIfStack].fSupported = FALSE;
1783 achIfStack[iIfStack].fIncluded = TRUE;
1784 achIfStack[iIfStack].fIf = TRUE;
1785 }
1786 else
1787 /*
1788 * #else
1789 */
1790 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1791 {
1792 if (achIfStack[iIfStack].fSupported)
1793 {
1794 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1795 achIfStack[iIfStack].fIncluded = FALSE;
1796 else
1797 achIfStack[iIfStack].fIncluded = TRUE;
1798 }
1799 achIfStack[iIfStack].fIf = FALSE;
1800 }
1801 else
1802 /*
1803 * #endif
1804 */
1805 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1806 { /* Pop the if-stack. */
1807 if (iIfStack > 0)
1808 iIfStack--;
1809 else
1810 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1811 }
1812 /*
1813 * general if<something> and elseif<something> implementations
1814 */
1815 else
1816 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1817 {
1818 achIfStack[iIfStack].fSupported = FALSE;
1819 achIfStack[iIfStack].fIncluded = TRUE;
1820 }
1821 else
1822 if (strncmp(&szBuffer[i], "if", 2) == 0)
1823 {
1824 iIfStack++;
1825 achIfStack[iIfStack].fIf = TRUE;
1826 achIfStack[iIfStack].fSupported = FALSE;
1827 achIfStack[iIfStack].fIncluded = TRUE;
1828 }
1829 /* The rest of them aren't implemented yet.
1830 else if (strncmp(&szBuffer[i], "if") == 0)
1831 {
1832 }
1833 */
1834 } else
1835 /*
1836 * Check for resource compiler directives.
1837 */
1838 if ( !fComment
1839 && !strchr(&szBuffer[i], ',')
1840 && ( !strnicmp(&szBuffer[i], "ICON", 4)
1841 || !strnicmp(&szBuffer[i], "FONT", 4)
1842 || !strnicmp(&szBuffer[i], "BITMAP", 6)
1843 || !strnicmp(&szBuffer[i], "POINTER", 7)
1844 || !strnicmp(&szBuffer[i], "RESOURCE", 8)
1845 || !(i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9))
1846 /*|| !strnicmp(&szBuffer[i], "DLGINCLUDE", 10) - only used by the dlgeditor */
1847 || !strnicmp(&szBuffer[i], "DEFAULTICON", 11)
1848 )
1849 )
1850 {
1851 /*
1852 * RESOURCE 123 1 ["]filename.ext["]
1853 */
1854 char szLine[1024];
1855 char * pszFile;
1856 char chQuote = ' ';
1857
1858 PreProcessLine(szLine, &szBuffer[i]);
1859
1860 pszFile = &szLine[strlen(szLine)-1];
1861 if (*pszFile == '\"' || *pszFile == '\'')
1862 {
1863 chQuote = *pszFile;
1864 *pszFile-- = '\0';
1865 }
1866 while (*pszFile != chQuote)
1867 pszFile--;
1868 *pszFile++ = '\0'; /* We now have extracted the filename - pszFile. */
1869 strlwr(pszFile);
1870
1871 /* Add filename to the dependencies. */
1872 if (i1)
1873 depAddDepend(pvRule, pszFile, options.fCheckCyclic, FALSE);
1874 else
1875 {
1876 char *psz;
1877 /* find include file! */
1878 psz = pathlistFindFile(options.pszInclude, pszFile, szFullname);
1879 if (psz == NULL)
1880 psz = pathlistFindFile(pszIncludeEnv, pszFile, szFullname);
1881
1882 /* did we find the include? */
1883 if (psz != NULL)
1884 {
1885 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szFullname))
1886 { /* #include <sys/stats.h> makes trouble, check for '/' and '\'. */
1887 if (!strchr(pszFile, '/') && !strchr(pszFile, '\\'))
1888 depAddDepend(pvRule, pszFile, options.fCheckCyclic, FALSE);
1889 else
1890 fprintf(stderr, "%s(%d): warning include '%s' is ignored.\n",
1891 pszFilename, iLine, pszFile);
1892 }
1893 else
1894 depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
1895 }
1896 else
1897 {
1898 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1899 pszFilename, iLine, pszFile);
1900 depMarkNotFound(pvRule);
1901 }
1902 }
1903 }
1904
1905
1906 /*
1907 * Comment checks.
1908 * -Start at first non-blank.
1909 * -Loop thru the line since we might have more than one
1910 * comment statement on a single line.
1911 */
1912 pszC = &szBuffer[i];
1913 while (pszC != NULL && *pszC != '\0')
1914 {
1915 if (fComment)
1916 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
1917 else
1918 {
1919 char *pszLC;
1920 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
1921 pszC = strstr(pszC, "/*"); /* look for start comment mark */
1922 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
1923 break; /* muliline comment mark we'll ignore the multiline mark. */
1924 }
1925
1926 /* Comment mark found? */
1927 if (pszC != NULL)
1928 {
1929 fComment = !fComment;
1930 pszC += 2; /* skip comment mark */
1931
1932 /* debug */
1933 /*
1934 if (fComment)
1935 fprintf(stderr, "starts at line %d\n", iLine);
1936 else
1937 fprintf(stderr, "ends at line %d\n", iLine);
1938 */
1939 }
1940 }
1941 } /*while*/
1942
1943 textbufferDestroy(pvFile);
1944
1945 return 0;
1946}
1947#endif
1948
1949
1950/**
1951 * Generates depend info on this COBOL file, these are stored internally
1952 * and written to file later.
1953 * @returns 0 on success.
1954 * !0 on error.
1955 * @param pszFilename Pointer to source filename. Correct case is assumed!
1956 * @param pszNormFilename Pointer to normalized source filename.
1957 * @param pszTS File time stamp.
1958 * @parma fHeader True if header file is being scanned.
1959 * @status completely implemented.
1960 * @author knut st. osmundsen
1961 */
1962int langCOBOL(const char *pszFilename, const char *pszNormFilename,
1963 const char *pszTS, BOOL fHeader)
1964{
1965 void * pvFile; /* Text buffer pointer. */
1966 void * pvRule; /* Handle to the current rule. */
1967 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1968 int iLine; /* current line number */
1969 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1970
1971
1972 /**********************************/
1973 /* Add the depend rule */
1974 /**********************************/
1975 if (options.fObjRule && !fHeader)
1976 {
1977 if (options.fNoObjectPath)
1978 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
1979 else
1980 pvRule = depAddRule(options.fObjectDir ?
1981 options.pszObjectDir :
1982 filePathSlash(pszFilename, szBuffer),
1983 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1984 options.pszObjectExt, pszTS, FALSE);
1985
1986 if (options.fSrcWhenObj && pvRule)
1987 depAddDepend(pvRule,
1988 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
1989 ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1990 options.fCheckCyclic,
1991 FALSE);
1992 }
1993 else
1994 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1995 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
1996
1997 /* duplicate rule? */
1998 if (pvRule == NULL)
1999 return 0;
2000
2001
2002 /********************/
2003 /* Make file buffer */
2004 /********************/
2005 pvFile = textbufferCreate(pszFilename);
2006 if (!pvFile)
2007 {
2008 fprintf(stderr, "failed to open '%s'\n", pszFilename);
2009 return -1;
2010 }
2011
2012
2013 /*******************/
2014 /* find dependants */
2015 /*******************/
2016 iLine = 0;
2017 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
2018 {
2019 /* search for #include */
2020 int cbLen;
2021 int i = 0;
2022 int i1, i2;
2023 iLine++;
2024
2025 /* check for comment mark (column 7) */
2026 if (szBuffer[6] == '*')
2027 continue;
2028
2029 /* skip blank chars */
2030 cbLen = strlen(szBuffer);
2031 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
2032 i++;
2033
2034 /* is this an include? */
2035 if ( (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
2036 || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
2037 )
2038 {
2039 char szFullname[CCHMAXPATH];
2040 char *psz;
2041 int j;
2042
2043 /* skip statement */
2044 i += 4;
2045 if (i1 != 0)
2046 {
2047 int y = 2; /* skip two words */
2048 do
2049 {
2050 /* skip blanks */
2051 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2052 i++;
2053 /* skip word */
2054 while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
2055 && szBuffer[i] != '\0' && szBuffer[i] != '\n')
2056 i++;
2057 y--;
2058 } while (y > 0);
2059 }
2060
2061 /* check for blank */
2062 if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
2063 continue;
2064
2065 /* skip blanks */
2066 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2067 i++;
2068
2069 /* if invalid statement then continue with the next line! */
2070 if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
2071 continue;
2072
2073 /* find end */
2074 j = 0;
2075 while (i + j < cbLen && j < CCHMAXPATH
2076 && szBuffer[i+j] != '.'
2077 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
2078 && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
2079 )
2080 j++;
2081
2082 /* if invalid statement then continue with the next line! */
2083 if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
2084 continue;
2085
2086 /* copy filename */
2087 strncpy(szFullname, &szBuffer[i], j);
2088 szFullname[j] = '\0'; /* ensure terminatition. */
2089 strlwr(szFullname);
2090
2091 /* add extention .cpy - hardcoded for the moment. */
2092 strcpy(&szFullname[j], ".cbl");
2093
2094 /* find include file! */
2095 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2096 if (!psz)
2097 {
2098 strcpy(&szFullname[j], ".cpy");
2099 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2100 }
2101
2102 /* did we find the include? */
2103 if (psz != NULL)
2104 {
2105 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
2106 depAddDepend(pvRule, szFullname, options.fCheckCyclic, FALSE);
2107 else
2108 depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
2109 }
2110 else
2111 {
2112 szFullname[j] = '\0';
2113 fprintf(stderr, "%s(%d): warning copybook '%s' was not found!\n",
2114 pszFilename, iLine, szFullname);
2115 depMarkNotFound(pvRule);
2116 }
2117 }
2118 } /*while*/
2119
2120 textbufferDestroy(pvFile);
2121
2122 return 0;
2123}
2124
2125
2126/**
2127 * Generates depend info on this IPF file, these are stored internally
2128 * and written to file later.
2129 * @returns 0 on success.
2130 * !0 on error.
2131 * @param pszFilename Pointer to source filename. Correct case is assumed!
2132 * @param pszNormFilename Pointer to normalized source filename.
2133 * @param pszTS File time stamp.
2134 * @parma fHeader True if header file is being scanned.
2135 * @status completely implemented.
2136 * @author knut st. osmundsen
2137 */
2138int langIPF( const char *pszFilename, const char *pszNormFilename,
2139 const char *pszTS, BOOL fHeader)
2140{
2141 void * pvFile; /* Text buffer pointer. */
2142 void * pvRule; /* Handle to the current rule. */
2143 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
2144 int iLine; /* current line number */
2145 void * pv = NULL; /* An index used by textbufferGetNextLine. */
2146
2147
2148 /**********************************/
2149 /* Add the depend rule */
2150 /**********************************/
2151 /*if (options.fObjRule && !fHeader)
2152 {
2153 if (options.fNoObjectPath)
2154 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS, FALSE);
2155 else
2156 pvRule = depAddRule(options.fObjectDir ?
2157 options.pszObjectDir :
2158 filePathSlash(pszFilename, szBuffer),
2159 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
2160 options.pszObjectExt, pszTS, FALSE);
2161
2162 if (options.fSrcWhenObj && pvRule)
2163 depAddDepend(pvRule,
2164 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
2165 ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
2166 options.fCheckCyclic,
2167 FALSE);
2168 }
2169 else */
2170 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
2171 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS, FALSE);
2172
2173 /* duplicate rule? */
2174 if (pvRule == NULL)
2175 return 0;
2176
2177
2178 /********************/
2179 /* Make file buffer */
2180 /********************/
2181 pvFile = textbufferCreate(pszFilename);
2182 if (!pvFile)
2183 {
2184 fprintf(stderr, "failed to open '%s'\n", pszFilename);
2185 return -1;
2186 }
2187
2188
2189 /*******************/
2190 /* find dependants */
2191 /*******************/
2192 iLine = 0;
2193 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
2194 {
2195 iLine++;
2196
2197 /* is this an imbed statement? */
2198 if (!strncmp(&szBuffer[0], ".im", 3))
2199 {
2200 char szFullname[CCHMAXPATH];
2201 char * psz;
2202 int i;
2203 int j;
2204 char chQuote = 0;
2205
2206 /* skip statement and blanks */
2207 i = 4;
2208 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2209 i++;
2210
2211 /* check for quotes */
2212 if (szBuffer[i] == '\'' || szBuffer[i] == '\"')
2213 chQuote = szBuffer[i++];
2214
2215 /* find end */
2216 j = 0;
2217 if (chQuote != 0)
2218 {
2219 while (szBuffer[i+j] != chQuote && szBuffer[i+j] != '\n' && szBuffer[i+j] != '\r' && szBuffer[i+j] != '\0')
2220 j++;
2221 }
2222 else
2223 {
2224 while (szBuffer[i+j] != '\n' && szBuffer[i+j] != '\r' && szBuffer[i+j] != '\0')
2225 j++;
2226 }
2227
2228 /* find end */
2229 if (j >= CCHMAXPATH)
2230 {
2231 fprintf(stderr, "%s(%d) warning: Filename too long ignored.\n", pszFilename, iLine);
2232 continue;
2233 }
2234
2235 /* copy filename */
2236 strncpy(szFullname, &szBuffer[i], j);
2237 szFullname[j] = '\0'; /* ensure terminatition. */
2238 strlwr(szFullname);
2239
2240 /* find include file! */
2241 psz = filecacheFileExist(szFullname, szBuffer);
2242
2243 /* did we find the include? */
2244 if (psz != NULL)
2245 {
2246 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
2247 depAddDepend(pvRule, fileName(szFullname, szBuffer), options.fCheckCyclic, FALSE);
2248 else
2249 depAddDepend(pvRule, szBuffer, options.fCheckCyclic, FALSE);
2250 }
2251 else
2252 {
2253 fprintf(stderr, "%s(%d): warning imbeded file '%s' was not found!\n",
2254 pszFilename, iLine, szFullname);
2255 depMarkNotFound(pvRule);
2256 }
2257 }
2258 } /*while*/
2259
2260 textbufferDestroy(pvFile);
2261
2262 fHeader = fHeader;
2263 return 0;
2264}
2265
2266
2267#define upcase(ch) \
2268 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
2269
2270/**
2271 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
2272 * @returns 0 equal. (same as strnicmp)
2273 * @param pszS1 String 1
2274 * @param pszS2 String 2
2275 * @param cch Length to compare (relative to string 1)
2276 * @author knut st. osmundsen (bird@anduin.net)
2277 */
2278int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
2279{
2280 do
2281 {
2282 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
2283 pszS1++, pszS2++, cch--;
2284
2285 /* blank test and skipping */
2286 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
2287 {
2288 while (cch > 0 && *pszS1 == ' ')
2289 pszS1++, cch--;
2290
2291 while (*pszS2 == ' ')
2292 pszS2++;
2293 }
2294 else
2295 break;
2296 } while (cch > 0);
2297
2298 return cch == 0 ? 0 : *pszS1 - *pszS2;
2299}
2300
2301
2302/**
2303 * Normalizes the path slashes for the filename. It will partially expand paths too.
2304 * @returns pszFilename
2305 * @param pszFilename Pointer to filename string. Not empty string!
2306 * Much space to play with.
2307 */
2308char *fileNormalize(char *pszFilename)
2309{
2310 char *psz = pszFilename;
2311
2312 /* correct slashes */
2313 while ((pszFilename = strchr(pszFilename, '//')) != NULL)
2314 *pszFilename++ = '\\';
2315
2316 /* expand path? */
2317 pszFilename = psz;
2318 if (pszFilename[1] != ':')
2319 { /* relative path */
2320 int iSlash;
2321 char szFile[CCHMAXPATH];
2322 char * psz = szFile;
2323
2324 strcpy(szFile, pszFilename);
2325 iSlash = *psz == '\\' ? 1 : cSlashes;
2326 while (*psz != '\0')
2327 {
2328 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
2329 { /* up one directory */
2330 if (iSlash > 0)
2331 iSlash--;
2332 psz += 3;
2333 }
2334 else if (*psz == '.' && psz[1] == '\\')
2335 { /* no change */
2336 psz += 2;
2337 }
2338 else
2339 { /* completed expantion! */
2340 strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
2341 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
2342 break;
2343 }
2344 }
2345 }
2346 /* else: assume full path */
2347
2348 return psz;
2349}
2350
2351
2352/**
2353 * Normalizes the path slashes for the filename. It will partially expand paths too.
2354 * Makes name all lower case too.
2355 * @returns pszFilename
2356 * @param pszFilename Pointer to filename string. Not empty string!
2357 * Much space to play with.
2358 * @param pszBuffer Pointer to output buffer.
2359 */
2360char *fileNormalize2(const char *pszFilename, char *pszBuffer)
2361{
2362 char * psz = pszBuffer;
2363 int iSlash;
2364
2365 if (pszFilename[1] != ':')
2366 {
2367 /* iSlash */
2368 if (*pszFilename == '\\' || *pszFilename == '/')
2369 iSlash = 1;
2370 else
2371 iSlash = cSlashes;
2372
2373 /* interpret . and .. */
2374 while (*pszFilename != '\0')
2375 {
2376 if (*pszFilename == '.' && pszFilename[1] == '.' && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
2377 { /* up one directory */
2378 if (iSlash > 0)
2379 iSlash--;
2380 pszFilename += 3;
2381 }
2382 else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
2383 { /* no change */
2384 pszFilename += 2;
2385 }
2386 else
2387 { /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
2388 strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
2389 strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
2390 break;
2391 }
2392 }
2393 }
2394 else
2395 { /* have drive letter specified - assume ok (TODO)*/
2396 strcpy(pszBuffer, pszFilename);
2397 }
2398
2399 /* correct slashes */
2400 while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
2401 *pszBuffer++ = '\\';
2402
2403 /* lower case it */
2404 /*strlwr(psz);*/
2405
2406 return psz;
2407}
2408
2409
2410/**
2411 * Copies the path part (excluding the slash) into pszBuffer and returns
2412 * a pointer to the buffer.
2413 * If no path is found "" is returned.
2414 * @returns Pointer to pszBuffer with path.
2415 * @param pszFilename Pointer to readonly filename.
2416 * @param pszBuffer Pointer to output Buffer.
2417 * @status completely implemented.
2418 * @author knut st. osmundsen
2419 */
2420char *filePath(const char *pszFilename, char *pszBuffer)
2421{
2422 char *psz = strrchr(pszFilename, '\\');
2423 if (psz == NULL)
2424 psz = strrchr(pszFilename, '/');
2425
2426 if (psz == NULL)
2427 *pszBuffer = '\0';
2428 else
2429 {
2430 strncpy(pszBuffer, pszFilename, psz - pszFilename);
2431 pszBuffer[psz - pszFilename] = '\0';
2432 }
2433
2434 return pszBuffer;
2435}
2436
2437
2438/**
2439 * Copies the path part including the slash into pszBuffer and returns
2440 * a pointer to the buffer.
2441 * If no path is found "" is returned.
2442 * @returns Pointer to pszBuffer with path.
2443 * @param pszFilename Pointer to readonly filename.
2444 * @param pszBuffer Pointer to output Buffer.
2445 * @status completely implemented.
2446 * @author knut st. osmundsen
2447 */
2448char *filePathSlash(const char *pszFilename, char *pszBuffer)
2449{
2450 char *psz = strrchr(pszFilename, '\\');
2451 if (psz == NULL)
2452 psz = strrchr(pszFilename, '/');
2453
2454 if (psz == NULL)
2455 *pszBuffer = '\0';
2456 else
2457 {
2458 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2459 pszBuffer[psz - pszFilename + 1] = '\0';
2460 }
2461
2462 return pszBuffer;
2463}
2464
2465
2466/**
2467 * Copies the path part including the slash into pszBuffer and returns
2468 * a pointer to the buffer. If no path is found "" is returned.
2469 * The path is normalized to only use '\\'.
2470 * @returns Pointer to pszBuffer with path.
2471 * @param pszFilename Pointer to readonly filename.
2472 * @param pszBuffer Pointer to output Buffer.
2473 * @status completely implemented.
2474 * @author knut st. osmundsen
2475 */
2476char *filePathSlash2(const char *pszFilename, char *pszBuffer)
2477{
2478 char *psz = strrchr(pszFilename, '\\');
2479 if (psz == NULL)
2480 psz = strrchr(pszFilename, '/');
2481
2482 if (psz == NULL)
2483 *pszBuffer = '\0';
2484 else
2485 {
2486 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2487 pszBuffer[psz - pszFilename + 1] = '\0';
2488
2489 /* normalize all '/' to '\\' */
2490 psz = pszBuffer;
2491 while ((psz = strchr(psz, '/')) != NULL)
2492 *psz++ = '\\';
2493 }
2494
2495 return pszBuffer;
2496}
2497
2498
2499/**
2500 * Copies the filename (with extention) into pszBuffer and returns
2501 * a pointer to the buffer.
2502 * @returns Pointer to pszBuffer with path.
2503 * @param pszFilename Pointer to readonly filename.
2504 * @param pszBuffer Pointer to output Buffer.
2505 * @status completely implemented.
2506 * @author knut st. osmundsen
2507 */
2508char *fileName(const char *pszFilename, char *pszBuffer)
2509{
2510 char *psz = strrchr(pszFilename, '\\');
2511 if (psz == NULL)
2512 psz = strrchr(pszFilename, '/');
2513
2514 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2515
2516 return pszBuffer;
2517}
2518
2519
2520/**
2521 * Copies the name part with out extention into pszBuffer and returns
2522 * a pointer to the buffer.
2523 * If no name is found "" is returned.
2524 * @returns Pointer to pszBuffer with path.
2525 * @param pszFilename Pointer to readonly filename.
2526 * @param pszBuffer Pointer to output Buffer.
2527 * @status completely implemented.
2528 * @author knut st. osmundsen
2529 */
2530char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
2531{
2532 char *psz = strrchr(pszFilename, '\\');
2533 if (psz == NULL)
2534 psz = strrchr(pszFilename, '/');
2535
2536 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2537
2538 psz = strrchr(pszBuffer, '.');
2539 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
2540 *psz = '\0';
2541
2542 return pszBuffer;
2543}
2544
2545
2546/**
2547 * Copies the extention part into pszBuffer and returns
2548 * a pointer to the buffer.
2549 * If no extention is found "" is returned.
2550 * The dot ('.') is not included!
2551 * @returns Pointer to pszBuffer with path.
2552 * @param pszFilename Pointer to readonly filename.
2553 * @param pszBuffer Pointer to output Buffer.
2554 * @status completely implemented.
2555 * @author knut st. osmundsen
2556 */
2557char *fileExt(const char *pszFilename, char *pszBuffer)
2558{
2559 char *psz = strrchr(pszFilename, '.');
2560 if (psz != NULL)
2561 {
2562 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
2563 *pszBuffer = '\0';
2564 else
2565 strcpy(pszBuffer, psz + 1);
2566 }
2567 else
2568 *pszBuffer = '\0';
2569
2570 return pszBuffer;
2571}
2572
2573
2574/**
2575 * Adds a file to the cache.
2576 * @returns Success indicator.
2577 * @param pszFilename Name of the file which is to be added. (with path!)
2578 */
2579BOOL filecacheAddFile(const char *pszFilename)
2580{
2581 PFCACHEENTRY pfcNew;
2582
2583 /* allocate new block and fill in data */
2584 pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
2585 if (pfcNew == NULL)
2586 {
2587 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2588 return FALSE;
2589 }
2590 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2591 strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
2592 if (!AVLInsert(&pfcTree, pfcNew))
2593 {
2594 free(pfcNew);
2595 return TRUE;
2596 }
2597 cfcNodes++;
2598
2599 return TRUE;
2600}
2601
2602
2603/**
2604 * Adds a file to the cache.
2605 * @returns Success indicator.
2606 * @param pszDir Name of the path which is to be added. (with slash!)
2607 */
2608BOOL filecacheAddDir(const char *pszDir)
2609{
2610 PFCACHEENTRY pfcNew;
2611 APIRET rc;
2612 char szDir[CCHMAXPATH];
2613 int cchDir;
2614 char achBuffer[32768];
2615 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2616 HDIR hDir = HDIR_CREATE;
2617 ULONG cFiles = 0xFFFFFFF;
2618 int i;
2619
2620 /* Make path */
2621 filePathSlash2(pszDir, szDir);
2622 //strlwr(szDir); /* Convert name to lower case to allow faster searchs! */
2623 cchDir = strlen(szDir);
2624
2625
2626 /* Add directory to pfcDirTree. */
2627 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
2628 if (pfcNew == NULL)
2629 {
2630 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2631 DosFindClose(hDir);
2632 return FALSE;
2633 }
2634 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2635 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2636 AVLInsert(&pfcDirTree, pfcNew);
2637
2638
2639 /* Start to search directory - all files */
2640 strcat(szDir + cchDir, "*");
2641 rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
2642 pfindbuf3, sizeof(achBuffer),
2643 &cFiles, FIL_STANDARD);
2644 while (rc == NO_ERROR)
2645 {
2646 for (i = 0;
2647 i < cFiles;
2648 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
2649 )
2650 {
2651 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
2652 if (pfcNew == NULL)
2653 {
2654 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2655 DosFindClose(hDir);
2656 return FALSE;
2657 }
2658 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2659 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2660 strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
2661 strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
2662 if (!AVLInsert(&pfcTree, pfcNew))
2663 free(pfcNew);
2664 else
2665 cfcNodes++;
2666 }
2667
2668 /* next */
2669 cFiles = 0xFFFFFFF;
2670 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2671 rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
2672 }
2673
2674 DosFindClose(hDir);
2675
2676 return TRUE;
2677}
2678
2679
2680/**
2681 * Checks if pszFilename is exists in the cache.
2682 * @return TRUE if found. FALSE if not found.
2683 * @param pszFilename Name of the file to be found. (with path!)
2684 * This is in lower case!
2685 */
2686INLINE BOOL filecacheFind(const char *pszFilename)
2687{
2688 return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
2689}
2690
2691
2692/**
2693 * Checks if pszFilename is exists in the cache.
2694 * @return TRUE if found. FALSE if not found.
2695 * @param pszFilename Name of the file to be found. (with path!)
2696 * This is in lower case!
2697 */
2698INLINE BOOL filecacheIsDirCached(const char *pszDir)
2699{
2700 return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
2701}
2702
2703
2704/**
2705 * Checks if a file exist, uses file cache if possible.
2706 * @returns Pointer to a filename consiting of the path part + the given filename.
2707 * (pointer into pszBuffer)
2708 * NULL if file is not found. ("" in buffer)
2709 * @parma pszFilename Filename to find.
2710 * @parma pszBuffer Ouput Buffer.
2711 * @status completely implemented.
2712 * @author knut st. osmundsen
2713 */
2714char *filecacheFileExist(const char *pszFilename, char *pszBuffer)
2715{
2716 APIRET rc;
2717
2718 *pszBuffer = '\0';
2719
2720 fileNormalize2(pszFilename, pszBuffer);
2721
2722 /*
2723 * Search for the file in this directory.
2724 * Search cache first
2725 */
2726 if (!filecacheFind(pszBuffer))
2727 {
2728 char szDir[CCHMAXPATH];
2729
2730 filePathSlash(pszBuffer, szDir);
2731 if (!filecacheIsDirCached(szDir))
2732 {
2733 /*
2734 * If caching of entire dirs are enabled, we'll
2735 * add the directory to the cache and search it.
2736 */
2737 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
2738 {
2739 if (filecacheFind(pszBuffer))
2740 return pszBuffer;
2741 }
2742 else
2743 {
2744 FILESTATUS3 fsts3;
2745
2746 /* ask the OS */
2747 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2748 if (rc == NO_ERROR)
2749 { /* add file to cache. */
2750 filecacheAddFile(pszBuffer);
2751 return pszBuffer;
2752 }
2753 }
2754 }
2755 }
2756 else
2757 return pszBuffer;
2758
2759 return NULL;
2760}
2761
2762
2763/**
2764 * Finds a filename in a specified pathlist.
2765 * @returns Pointer to a filename consiting of the path part + the given filename.
2766 * (pointer into pszBuffer)
2767 * NULL if file is not found. ("" in buffer)
2768 * @param pszPathList Path list to search for filename.
2769 * @parma pszFilename Filename to find.
2770 * @parma pszBuffer Ouput Buffer.
2771 * @status completely implemented.
2772 * @author knut st. osmundsen
2773 */
2774char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer)
2775{
2776 const char *psz = pszPathList;
2777 const char *pszNext = NULL;
2778
2779 *pszBuffer = '\0';
2780
2781 if (pszPathList == NULL)
2782 return NULL;
2783
2784 while (*psz != '\0')
2785 {
2786 /* find end of this path */
2787 pszNext = strchr(psz, ';');
2788 if (pszNext == NULL)
2789 pszNext = psz + strlen(psz);
2790
2791 if (pszNext - psz > 0)
2792 {
2793 APIRET rc;
2794
2795 /* make search statment */
2796 strncpy(pszBuffer, psz, pszNext - psz);
2797 pszBuffer[pszNext - psz] = '\0';
2798 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
2799 strcpy(&pszBuffer[pszNext - psz], "\\");
2800 strcat(pszBuffer, pszFilename);
2801 fileNormalize(pszBuffer);
2802
2803 /*
2804 * Search for the file in this directory.
2805 * Search cache first
2806 */
2807 if (!filecacheFind(pszBuffer))
2808 {
2809 char szDir[CCHMAXPATH];
2810
2811 filePathSlash(pszBuffer, szDir);
2812 if (!filecacheIsDirCached(szDir))
2813 {
2814 /*
2815 * If caching of entire dirs are enabled, we'll
2816 * add the directory to the cache and search it.
2817 */
2818 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
2819 {
2820 if (filecacheFind(pszBuffer))
2821 return pszBuffer;
2822 }
2823 else
2824 {
2825 FILESTATUS3 fsts3;
2826
2827 /* ask the OS */
2828 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2829 if (rc == NO_ERROR)
2830 { /* add file to cache. */
2831 filecacheAddFile(pszBuffer);
2832 return pszBuffer;
2833 }
2834 }
2835 }
2836 }
2837 else
2838 return pszBuffer;
2839 }
2840
2841 /* next */
2842 if (*pszNext != ';')
2843 break;
2844 psz = pszNext + 1;
2845 }
2846
2847 return NULL;
2848}
2849
2850
2851/**
2852 * Checks if the given filename may exist within any of the given paths.
2853 * This check only matches the filename path agianst the paths in the pathlist.
2854 * @returns TRUE: if exists.
2855 * FALSE: don't exist.
2856 * @param pszPathList Path list to search for filename.
2857 * @parma pszFilename Filename to find. The filename should be normalized!
2858 * @status completely implemented.
2859 * @author knut st. osmundsen
2860 */
2861BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename)
2862{
2863 const char *psz = pszPathList;
2864 const char *pszNext = NULL;
2865 char szBuffer[CCHMAXPATH];
2866 char szBuffer2[CCHMAXPATH];
2867 char *pszPathToFind = &szBuffer2[0];
2868
2869 /*
2870 * Input checking
2871 */
2872 if (pszPathList == NULL)
2873 return FALSE;
2874
2875 /*
2876 * Normalize the filename and get it's path.
2877 */
2878 filePath(pszFilename, pszPathToFind);
2879
2880
2881 /*
2882 * Loop thru the path list.
2883 */
2884 while (*psz != '\0')
2885 {
2886 /* find end of this path */
2887 pszNext = strchr(psz, ';');
2888 if (pszNext == NULL)
2889 pszNext = psz + strlen(psz);
2890
2891 if (pszNext - psz > 0)
2892 {
2893 char * pszPath = &szBuffer[0];
2894
2895 /*
2896 * Extract and normalize the path
2897 */
2898 strncpy(pszPath, psz, pszNext - psz);
2899 pszPath[pszNext - psz] = '\0';
2900 if (pszPath[pszNext - psz - 1] == '\\' && pszPath[pszNext - psz - 1] == '/')
2901 pszPath[pszNext - psz - 1] = '\0';
2902 fileNormalize(pszPath);
2903
2904 /*
2905 * Check if it matches the path of the filename
2906 */
2907 if (strcmp(pszPath, pszPathToFind) == 0)
2908 return TRUE;
2909 }
2910
2911 /*
2912 * Next part of the path list.
2913 */
2914 if (*pszNext != ';')
2915 break;
2916 psz = pszNext + 1;
2917 }
2918
2919 return FALSE;
2920}
2921
2922
2923/**
2924 * Finds the first char after word.
2925 * @returns Pointer to the first char after word.
2926 * @param psz Where to start.
2927 * @author knut st. osmundsen (bird@anduin.net)
2928 */
2929char *findEndOfWord(char *psz)
2930{
2931
2932 while (*psz != '\0' &&
2933 (
2934 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
2935 ||
2936 (*psz >= '0' && *psz <= '9')
2937 ||
2938 *psz == '_'
2939 )
2940 )
2941 ++psz;
2942 return (char *)psz;
2943}
2944
2945#if 0 /* not used */
2946/**
2947 * Find the starting char of a word
2948 * @returns Pointer to first char in word.
2949 * @param psz Where to start.
2950 * @param pszStart Where to stop.
2951 * @author knut st. osmundsen (bird@anduin.net)
2952 */
2953char *findStartOfWord(const char *psz, const char *pszStart)
2954{
2955 const char *pszR = psz;
2956 while (psz >= pszStart &&
2957 (
2958 (*psz >= 'A' && *psz <= 'Z')
2959 || (*psz >= 'a' && *psz <= 'z')
2960 || (*psz >= '0' && *psz <= '9')
2961 || *psz == '_'
2962 )
2963 )
2964 pszR = psz--;
2965 return (char*)pszR;
2966}
2967#endif
2968
2969/**
2970 * Find the size of a file.
2971 * @returns Size of file. -1 on error.
2972 * @param phFile File handle.
2973 */
2974signed long fsize(FILE *phFile)
2975{
2976 int ipos;
2977 signed long cb;
2978
2979 if ((ipos = ftell(phFile)) < 0
2980 ||
2981 fseek(phFile, 0, SEEK_END) != 0
2982 ||
2983 (cb = ftell(phFile)) < 0
2984 ||
2985 fseek(phFile, ipos, SEEK_SET) != 0
2986 )
2987 cb = -1;
2988 return cb;
2989}
2990
2991
2992/**
2993 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2994 * @returns Pointer to first not space or tab char in the string.
2995 * @param psz Pointer to the string which is to be trimmed.
2996 * @status completely implmented.
2997 * @author knut st. osmundsen (bird@anduin.net)
2998 */
2999INLINE char *trim(char *psz)
3000{
3001 int i;
3002 if (psz == NULL)
3003 return NULL;
3004 while (*psz == ' ' || *psz == '\t')
3005 psz++;
3006 i = strlen(psz) - 1;
3007 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
3008 i--;
3009 psz[i+1] = '\0';
3010 return psz;
3011}
3012
3013
3014/**
3015 * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
3016 * @returns Pointer to the string passed in.
3017 * @param psz Pointer to the string which is to be right trimmed.
3018 * @status completely implmented.
3019 * @author knut st. osmundsen (bird@anduin.net)
3020 */
3021INLINE char *trimR(char *psz)
3022{
3023 int i;
3024 if (psz == NULL)
3025 return NULL;
3026 i = strlen(psz) - 1;
3027 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
3028 i--;
3029 psz[i+1] = '\0';
3030 return psz;
3031}
3032
3033
3034/**
3035 * Trims any quotes of a possibly quoted string.
3036 * @returns Pointer to the string passed in.
3037 * @param psz Pointer to the string which is to be quote-trimmed.
3038 * @status completely implmented.
3039 * @author knut st. osmundsen (bird@anduin.net)
3040 */
3041INLINE char *trimQuotes(char *psz)
3042{
3043 int i;
3044 if (psz == NULL)
3045 return NULL;
3046
3047 if (*psz == '\"' || *psz == '\'')
3048 psz++;
3049 i = strlen(psz) - 1;
3050 if (psz[i] == '\"' || psz[i] == '\'')
3051 psz[i] = '\0';
3052
3053 return psz;
3054}
3055
3056
3057/**
3058 * C/C++ preprocess a single line. Assumes that we're not starting
3059 * with at comment.
3060 * @returns Pointer to output buffer.
3061 * @param pszOut Ouput (preprocessed) string.
3062 * @param pszIn Input string.
3063 * @author knut st. osmundsen (bird@anduin.net)
3064 */
3065char *PreProcessLine(char *pszOut, const char *pszIn)
3066{
3067 char * psz = pszOut;
3068 BOOL fComment = FALSE;
3069 BOOL fQuote = FALSE;
3070
3071 /*
3072 * Loop thru the string.
3073 */
3074 while (*pszIn != '\0')
3075 {
3076 if (fQuote)
3077 {
3078 *psz++ = *pszIn;
3079 if (*pszIn == '\\')
3080 {
3081 *psz++ = *++pszIn;
3082 pszIn++;
3083 }
3084 else if (*pszIn++ == '"')
3085 fQuote = FALSE;
3086 }
3087 else if (fComment)
3088 {
3089 if (*pszIn == '*' && pszIn[1] == '/')
3090 {
3091 fComment = FALSE;
3092 pszIn += 2;
3093 }
3094 else
3095 pszIn++;
3096 }
3097 else
3098 {
3099 if ( (*pszIn == '/' && pszIn[1] == '/')
3100 || *pszIn == '\0')
3101 { /* End of line. */
3102 break;
3103 }
3104
3105 if (*pszIn == '/' && pszIn[1] == '*')
3106 { /* Start comment */
3107 fComment = TRUE;
3108 pszIn += 2;
3109 }
3110 else
3111 *psz++ = *pszIn++;
3112 }
3113 }
3114
3115 /*
3116 * Trim right.
3117 */
3118 psz--;
3119 while (psz >= pszOut && (*psz == ' ' || *psz == '\t'))
3120 psz--;
3121 psz[1] = '\0';
3122
3123 return pszOut;
3124}
3125
3126
3127/**
3128 * Creates a memory buffer for a text file.
3129 * @returns Pointer to file memoryblock. NULL on error.
3130 * @param pszFilename Pointer to filename string.
3131 * @remark This function is the one using most of the execution
3132 * time (DosRead + DosOpen) - about 70% of the execution time!
3133 */
3134void *textbufferCreate(const char *pszFilename)
3135{
3136 void *pvFile = NULL;
3137 FILE *phFile;
3138
3139 phFile = fopen(pszFilename, "rb");
3140 if (phFile != NULL)
3141 {
3142 signed long cbFile = fsize(phFile);
3143 if (cbFile >= 0)
3144 {
3145 pvFile = malloc(cbFile + 1);
3146 if (pvFile != NULL)
3147 {
3148 memset(pvFile, 0, cbFile + 1);
3149 if (cbFile > 0 && fread(pvFile, 1, cbFile, phFile) == 0)
3150 { /* failed! */
3151 free(pvFile);
3152 pvFile = NULL;
3153 }
3154 }
3155 else
3156 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
3157 }
3158 fclose(phFile);
3159 }
3160 return pvFile;
3161}
3162
3163
3164/**
3165 * Destroys a text textbuffer.
3166 * @param pvBuffer Buffer handle.
3167 */
3168void textbufferDestroy(void *pvBuffer)
3169{
3170 free(pvBuffer);
3171}
3172
3173
3174/**
3175 * Gets the next line from an textbuffer.
3176 * @returns Pointer to the next line.
3177 * @param pvBuffer Buffer handle.
3178 * @param psz Pointer to current line.
3179 * NULL is passed in to get the first line.
3180 */
3181char *textbufferNextLine(void *pvBuffer, register char *psz)
3182{
3183 register char ch;
3184
3185 /* if first line psz is NULL. */
3186 if (psz == NULL)
3187 return (char*)pvBuffer;
3188
3189 /* skip till end of file or end of line. */
3190 ch = *psz;
3191 while (ch != '\0' && ch != '\n' && ch != '\r')
3192 ch = *++psz;
3193
3194 /* skip line end */
3195 if (ch == '\r')
3196 ch = *++psz;
3197 if (ch == '\n')
3198 psz++;
3199
3200 return psz;
3201}
3202
3203
3204/**
3205 * Gets the next line from an textbuffer.
3206 * (fgets for textbuffer)
3207 * @returns Pointer to pszOutBuffer. NULL when end of file.
3208 * @param pvBuffer Buffer handle.
3209 * @param ppv Pointer to a buffer index pointer. (holds the current buffer index)
3210 * Pointer to a null pointer is passed in to get the first line.
3211 * @param pszLineBuffer Output line buffer. (!= NULL)
3212 * @param cchLineBuffer Size of the output line buffer. (> 0)
3213 * @remark '\n' and '\r' are removed!
3214 */
3215char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
3216{
3217 char * pszLine = pszLineBuffer;
3218 char * psz = *(char**)(void*)ppv;
3219 register char ch;
3220
3221 /* first line? */
3222 if (psz == NULL)
3223 psz = pvBuffer;
3224
3225 /* Copy to end of the line or end of the linebuffer. */
3226 ch = *psz;
3227 cchLineBuffer--; /* reserve space for '\0' */
3228 while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
3229 {
3230 *pszLine++ = ch;
3231 ch = *++psz;
3232 }
3233 *pszLine = '\0';
3234
3235 /* skip line end */
3236 if (ch == '\r')
3237 ch = *++psz;
3238 if (ch == '\n')
3239 psz++;
3240
3241 /* check if position has changed - if unchanged it's the end of file! */
3242 if (*ppv == (void*)psz)
3243 pszLineBuffer = NULL;
3244
3245 /* store current position */
3246 *ppv = (void*)psz;
3247
3248 return pszLineBuffer;
3249}
3250
3251
3252/**
3253 * Appends a depend file to the internal file.
3254 * This will update the date in the option struct.
3255 */
3256BOOL depReadFile(const char *pszFilename, BOOL fAppend)
3257{
3258 void * pvFile;
3259 char * pszNext;
3260 char * pszPrev; /* Previous line, only valid when finding new rule. */
3261 BOOL fMoreDeps = FALSE;
3262 void * pvRule = NULL;
3263
3264
3265 /* read depend file */
3266 pvFile = textbufferCreate(pszFilename);
3267 if (pvFile == NULL)
3268 return FALSE;
3269
3270 /* parse the original depend file */
3271 pszPrev = NULL;
3272 pszNext = pvFile;
3273 while (*pszNext != '\0')
3274 {
3275 int i;
3276 int cch;
3277 char *psz;
3278
3279 /* get the next line. */
3280 psz = pszNext;
3281 pszNext = textbufferNextLine(pvFile, pszNext);
3282
3283 /*
3284 * Process the current line:
3285 * Start off by terminating the line.
3286 * Trim the line,
3287 * Skip empty lines.
3288 * If not looking for more deps Then
3289 * Check if new rule starts here.
3290 * Endif
3291 *
3292 * If more deps to last rule Then
3293 * Get dependant name.
3294 * Endif
3295 */
3296 i = -1;
3297 while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
3298 pszNext[i--] = '\0';
3299 trimR(psz);
3300 cch = strlen(psz);
3301 if (cch == 0)
3302 {
3303 fMoreDeps = FALSE;
3304 continue;
3305 }
3306
3307 if (*psz == '#')
3308 {
3309 pszPrev = psz;
3310 continue;
3311 }
3312
3313 /* new rule? */
3314 if (!fMoreDeps)
3315 {
3316 if (*psz != ' ' && *psz != '\t' && *psz != '\0')
3317 {
3318 i = 0;
3319 while (psz[i] != '\0')
3320 {
3321 if (psz[i] == ':'
3322 && (psz[i+1] == ' '
3323 || psz[i+1] == '\t'
3324 || psz[i+1] == '\0'
3325 || (psz[i+1] == '\\' && psz[i+2] == '\0')
3326 )
3327 )
3328 {
3329 char szTS[TS_SIZE];
3330 char * pszCont = strchr(&psz[i], '\\');
3331 fMoreDeps = pszCont != NULL && pszCont[1] == '\0';
3332
3333 /* read evt. timestamp. */
3334 szTS[0] = '\0';
3335 if (pszPrev && strlen(pszPrev) > 25 && *pszPrev == '#')
3336 strcpy(szTS, pszPrev + 2);
3337
3338 psz[i] = '\0';
3339 pvRule = depAddRule(trimQuotes(trimR(psz)), NULL, NULL, szTS, TRUE);
3340 if (pvRule)
3341 ((PDEPRULE)pvRule)->fUpdated = fAppend;
3342 psz += i + 1;
3343 cch -= i + 1;
3344 break;
3345 }
3346 i++;
3347 }
3348 }
3349 pszPrev = NULL;
3350 }
3351
3352
3353 /* more dependants */
3354 if (fMoreDeps)
3355 {
3356 if (cch > 0 && psz[cch-1] == '\\')
3357 {
3358 fMoreDeps = TRUE;
3359 psz[cch-1] = '\0';
3360 }
3361 else
3362 fMoreDeps = FALSE;
3363
3364 /* if not duplicate rule */
3365 if (pvRule != NULL)
3366 {
3367 psz = trimQuotes(trim(psz));
3368 if (*psz != '\0')
3369 depAddDepend(pvRule, psz, options.fCheckCyclic, TRUE);
3370 }
3371 }
3372 } /* while */
3373
3374
3375 /* return succesfully */
3376 textbufferDestroy(pvFile);
3377 return TRUE;
3378}
3379
3380/**
3381 *
3382 * @returns Success indicator.
3383 * @param pszFilename Pointer to name of the output file.
3384 * @param fWriteUpdatedOnly If set we'll only write updated rules.
3385 */
3386BOOL depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly)
3387{
3388 FILE *phFile;
3389 phFile = fopen(pszFilename, "w");
3390 if (phFile != NULL)
3391 {
3392 AVLENUMDATA EnumData;
3393 PDEPRULE pdep;
3394 static char szBuffer[0x10000];
3395 int iBuffer = 0;
3396 int cch;
3397
3398 /*
3399 * Write warning on top of file.
3400 */
3401 fputs("#\n"
3402 "# This file was automatically generated by FastDep.\n"
3403 "# FastDep was written by knut st. osmundsen, and it's GPL software.\n"
3404 "#\n"
3405 "# THIS FILE SHOULD N O T BE EDITED MANUALLY!!!\n"
3406 "#\n"
3407 "# (As this may possibly make it unreadable for fastdep\n"
3408 "# and ruin the caching methods of FastDep.)\n"
3409 "#\n"
3410 "\n",
3411 phFile);
3412
3413 /* normal dependency output */
3414 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3415 while (pdep != NULL)
3416 {
3417 if (!fWriteUpdatedOnly || pdep->fUpdated)
3418 {
3419 int cchTS = strlen(pdep->szTS);
3420 int fQuoted = strpbrk(pdep->pszRule, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3421
3422 /* Write rule. Flush the buffer first if necessary. */
3423 cch = strlen(pdep->pszRule);
3424 if (iBuffer + cch*3 + fQuoted * 2 + cchTS + 9 >= sizeof(szBuffer))
3425 {
3426 fwrite(szBuffer, iBuffer, 1, phFile);
3427 iBuffer = 0;
3428 }
3429
3430 memcpy(szBuffer + iBuffer, "# ", 2);
3431 memcpy(szBuffer + iBuffer + 2, pdep->szTS, cchTS);
3432 iBuffer += cchTS + 2;
3433 szBuffer[iBuffer++] = '\n';
3434
3435 if (fQuoted) szBuffer[iBuffer++] = '"';
3436 iBuffer += depNameToMake(szBuffer + iBuffer, sizeof(szBuffer) - iBuffer, pdep->pszRule);
3437 if (fQuoted) szBuffer[iBuffer++] = '"';
3438 strcpy(szBuffer + iBuffer++, ":");
3439
3440 /* write rule dependants. */
3441 if (pdep->papszDep != NULL)
3442 {
3443 char **ppsz = pdep->papszDep;
3444 while (*ppsz != NULL)
3445 {
3446 /* flush buffer? */
3447 fQuoted = strpbrk(*ppsz, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3448 cch = strlen(*ppsz);
3449 if (iBuffer + cch*3 + fQuoted * 2 + 20 >= sizeof(szBuffer))
3450 {
3451 fwrite(szBuffer, iBuffer, 1, phFile);
3452 iBuffer = 0;
3453 }
3454 strcpy(szBuffer + iBuffer, " \\\n ");
3455 iBuffer += 7;
3456 if (fQuoted) szBuffer[iBuffer++] = '"';
3457 iBuffer += depNameToMake(szBuffer + iBuffer, sizeof(szBuffer) - iBuffer, *ppsz);
3458 if (fQuoted) szBuffer[iBuffer++] = '"';
3459
3460 /* next dependant */
3461 ppsz++;
3462 }
3463 }
3464
3465 /* Add two new lines. Flush buffer first if necessary. */
3466 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3467 {
3468 fwrite(szBuffer, iBuffer, 1, phFile);
3469 iBuffer = 0;
3470 }
3471
3472 /* add 2 linefeeds */
3473 strcpy(szBuffer + iBuffer, "\n\n");
3474 iBuffer += CBNEWLINE*2;
3475 }
3476
3477 /* next rule */
3478 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3479 }
3480
3481
3482 /* flush buffer. */
3483 fwrite(szBuffer, iBuffer, 1, phFile);
3484
3485 fclose(phFile);
3486 return TRUE;
3487 }
3488
3489 return FALSE;
3490}
3491
3492
3493/**
3494 * Removes all nodes in the tree of dependencies. (pdepTree)
3495 */
3496void depRemoveAll(void)
3497{
3498 AVLENUMDATA EnumData;
3499 PDEPRULE pdep;
3500
3501 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3502 while (pdep != NULL)
3503 {
3504 /* free this */
3505 if (pdep->papszDep != NULL)
3506 {
3507 char ** ppsz = pdep->papszDep;
3508 while (*ppsz != NULL)
3509 free(*ppsz++);
3510 free(pdep->papszDep);
3511 }
3512 free(pdep);
3513
3514 /* next */
3515 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3516 }
3517 pdepTree = NULL;
3518}
3519
3520
3521/**
3522 * Adds a rule to the list of dependant rules.
3523 * @returns Rule handle. NULL if rule exists/error.
3524 * @param pszRulePath Pointer to rule text. Empty strings are banned!
3525 * This string might only contain the path of the rule. (with '\\')
3526 * @param pszName Name of the rule.
3527 * NULL if pszRulePath contains the entire rule.
3528 * @param pszExt Extention (without '.')
3529 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
3530 * @param fConvertName If set we'll convert from makefile name to realname.
3531 */
3532void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS, BOOL fConvertName)
3533{
3534 char szRule[CCHMAXPATH*2];
3535 PDEPRULE pNew;
3536 int cch;
3537
3538 /* make rulename */
3539 strcpy(szRule, pszRulePath);
3540 cch = strlen(szRule);
3541 if (pszName != NULL)
3542 {
3543 strcpy(szRule + cch, pszName);
3544 cch += strlen(szRule + cch);
3545 }
3546 if (pszExt != NULL)
3547 {
3548 strcat(szRule + cch++, ".");
3549 strcat(szRule + cch, pszExt);
3550 cch += strlen(szRule + cch);
3551 }
3552 if (fConvertName)
3553 cch = depNameToReal(szRule);
3554
3555 /*
3556 * Allocate a new rule structure and fill in data
3557 * Note. One block for both the DEPRULE and the pszRule string.
3558 */
3559 pNew = malloc(sizeof(DEPRULE) + cch + 1);
3560 if (pNew == NULL)
3561 {
3562 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
3563 return NULL;
3564 }
3565 pNew->pszRule = (char*)(void*)(pNew + 1);
3566 strcpy(pNew->pszRule, szRule);
3567 pNew->cDeps = 0;
3568 pNew->papszDep = NULL;
3569 pNew->fUpdated = TRUE;
3570 pNew->avlCore.Key = pNew->pszRule;
3571 strcpy(pNew->szTS, pszTS);
3572
3573 /* Insert the rule */
3574 if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
3575 { /*
3576 * The rule existed.
3577 * If it's allready touched (updated) during this session
3578 * there is nothing to be done.
3579 * If not force scan and it's newer than depfile-1month then
3580 * we'll use the information we've got.
3581 * Reuse the node in the tree.
3582 */
3583 PDEPRULE pOld = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pNew->avlCore.Key);
3584 assert(pOld);
3585 free(pNew);
3586 if (pOld->fUpdated)
3587 return NULL;
3588
3589 pOld->fUpdated = TRUE;
3590 if (!options.fForceScan && !strcmp(pOld->szTS, pszTS) && depValidate(pOld))
3591 return NULL;
3592 strcpy(pOld->szTS, pszTS);
3593
3594 if (pOld->papszDep)
3595 {
3596 free(pOld->papszDep);
3597 pOld->papszDep = NULL;
3598 }
3599 pOld->cDeps = 0;
3600
3601 return pOld;
3602 }
3603
3604 return pNew;
3605}
3606
3607
3608/**
3609 * Adds a dependant to a rule.
3610 * @returns Successindicator. TRUE = success.
3611 * FALSE = cyclic or out of memory.
3612 * @param pvRule Rule handle.
3613 * @param pszDep Pointer to dependant name
3614 * @param fCheckCyclic When set we'll check that we're not creating an cyclic dependency.
3615 * @param fConvertName If set we'll convert from makefile name to realname.
3616 */
3617BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic, BOOL fConvertName)
3618{
3619 PDEPRULE pdep = (PDEPRULE)pvRule;
3620 int cchDep;
3621
3622 if (pszDep[0] == '\0')
3623 {
3624 fprintf(stderr, "warning-internal: empty dependancy filename to '%s'. Ignored.\n",
3625 pdep->pszRule);
3626 /* __interrupt(3); */
3627 return FALSE;
3628 }
3629
3630 if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
3631 {
3632 fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
3633 pszDep, pdep->pszRule);
3634 return FALSE;
3635 }
3636
3637 /* allocate more array space */
3638 if (((pdep->cDeps) % 48) == 0)
3639 {
3640 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
3641 if (pdep->papszDep == NULL)
3642 {
3643 pdep->cDeps = 0;
3644 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3645 return FALSE;
3646 }
3647 }
3648
3649 /* allocate string space and copy pszDep */
3650 cchDep = strlen(pszDep) + 1;
3651 if ((pdep->papszDep[pdep->cDeps] = malloc(cchDep)) == NULL)
3652 {
3653 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3654 return FALSE;
3655 }
3656 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
3657
3658 /* convert ^# and other stuff */
3659 if (fConvertName)
3660 depNameToReal(pdep->papszDep[pdep->cDeps]);
3661
3662 /* terminate array and increment dep count */
3663 pdep->papszDep[++pdep->cDeps] = NULL;
3664
3665 /* successful! */
3666 return TRUE;
3667}
3668
3669
3670/**
3671 * Converts from makefile filename to real filename.
3672 * @returns New name length.
3673 * @param pszName Pointer to the string to make real.
3674 */
3675int depNameToReal(char *pszName)
3676{
3677 int cchNewName = strlen(pszName);
3678 int iDisplacement = 0;
3679
3680 /*
3681 * Look for '^' and '$$'.
3682 */
3683 while (*pszName)
3684 {
3685 if ( *pszName == '^'
3686 || (*pszName == '$' && pszName[1] == '$'))
3687 {
3688 iDisplacement--;
3689 pszName++;
3690 cchNewName--;
3691 }
3692 if (iDisplacement)
3693 pszName[iDisplacement] = *pszName;
3694 pszName++;
3695 }
3696 pszName[iDisplacement] = '\0';
3697
3698 return cchNewName;
3699}
3700
3701
3702/**
3703 * Converts from real filename to makefile filename.
3704 * @returns New name length.
3705 * @param pszName Output name buffer.
3706 * @param cchName Size of name buffer.
3707 * @param pszSrc Input name.
3708 */
3709int depNameToMake(char *pszName, int cchName, const char *pszSrc)
3710{
3711 char *pszNameOrg = pszName;
3712
3713 /*
3714 * Convert real name to makefile name.
3715 */
3716 while (*pszSrc)
3717 {
3718 if ( *pszSrc == '#'
3719 || *pszSrc == '!'
3720 || (*pszSrc == '$' && pszSrc[1] != '(')
3721 || *pszSrc == '@'
3722 || *pszSrc == '-'
3723 || *pszSrc == '^'
3724 /* || *pszSrc == '('
3725 || *pszSrc == ')'
3726 || *pszSrc == '{'
3727 || *pszSrc == '}'*/)
3728 {
3729 if (!cchName--)
3730 {
3731 fprintf(stderr, "error: buffer too small, (line=%d)\n", __LINE__);
3732 return pszName - pszNameOrg + strlen(pszName);
3733 }
3734 *pszName++ = '^';
3735 }
3736 if (!cchName--)
3737 {
3738 fprintf(stderr, "error: buffer too small, (line=%d)\n", __LINE__);
3739 return pszName - pszNameOrg + strlen(pszName);
3740 }
3741 *pszName++ = *pszSrc++;
3742 }
3743 *pszName = '\0';
3744
3745 return pszName - pszNameOrg;
3746}
3747
3748
3749
3750/**
3751 * Marks the file as one which is to be rescanned next time
3752 * since not all dependencies was found...
3753 * @param pvRule Rule handle...
3754 */
3755void depMarkNotFound(void *pvRule)
3756{
3757 ((PDEPRULE)pvRule)->szTS[0] = '\0';
3758}
3759
3760
3761/**
3762 * Checks if adding this dependent will create a cyclic dependency.
3763 * @returns TRUE: Cyclic.
3764 * FALSE: Non-cylic.
3765 * @param pdepRule Rule pszDep is to be inserted in.
3766 * @param pszDep Depend name.
3767 */
3768BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
3769{
3770#define DEPTH_FIRST 1
3771#ifdef DEPTH_FIRST
3772 #define DEPTH 32
3773#else
3774 #define DEPTH 128
3775#endif
3776 #define HISTORY 256
3777 char * pszRule = pdepRule->pszRule;
3778 char ** appsz[DEPTH];
3779#if HISTORY
3780 char * apszHistory[HISTORY];
3781 int iHistory;
3782 int j;
3783 int k;
3784 int iCmp;
3785#endif
3786 PDEPRULE pdep;
3787 int i;
3788
3789 /* self check */
3790 if (strcmp(pdepRule->pszRule, pszDep) == 0)
3791 return TRUE;
3792
3793 /* find rule for the dep. */
3794 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
3795 || pdep->papszDep == NULL)
3796 return FALSE; /* no rule, or no dependents, not cyclic */
3797
3798 i = 1;
3799 appsz[0] = pdep->papszDep;
3800#ifdef HISTORY
3801 iHistory = 1;
3802 apszHistory[0] = pdep->pszRule;
3803#endif
3804 while (i > 0)
3805 {
3806 /* pop off element */
3807 register char ** ppsz = appsz[--i];
3808
3809 while (*ppsz != NULL)
3810 {
3811 /* check if equal to the main rule */
3812 if (strcmp(pszRule, *ppsz) == 0)
3813 return TRUE;
3814
3815 /* push onto stack (ppsz is incremented in this test!) */
3816 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
3817 && pdep->papszDep != NULL)
3818 {
3819 if (i >= DEPTH)
3820 {
3821 fprintf(stderr, "error: too deep chain (%d). pszRule=%s pszDep=%s\n",
3822 i, pszRule, pszDep);
3823 return FALSE;
3824 }
3825#ifdef HISTORY
3826 /*
3827 * Check if in history, if so we'll skip it.
3828 */
3829 #if 1
3830 for (j = 0; j < iHistory; j++)
3831 if (!strcmp(apszHistory[j], pdep->pszRule))
3832 break;
3833 if (j != iHistory)
3834 continue;
3835
3836 /*
3837 * Push into history - might concider make this binary sorted one day.
3838 */
3839 if (iHistory < HISTORY)
3840 apszHistory[iHistory++] = pdep->pszRule;
3841
3842 #else
3843 /*
3844 * Check if in history, if so we'll skip it.
3845 * (Binary search)
3846 * ASSUMES: Always something in the history!
3847 */
3848 j = iHistory / 2;
3849 k = (iHistory + 1) / 2;
3850 do
3851 {
3852 iCmp = strcmp(pdep->pszRule, apszHistroy[j]);
3853 if (!iCmp)
3854 break;
3855 k = (k + 1) / 2;
3856 if (iCmp > 0)
3857 j += k;
3858 else
3859 j -= k;
3860 } while (!k);
3861
3862 if (!iCmp)
3863 continue; /* found */
3864
3865 /*
3866 * Push into history - might concider make this binary sorted one day.
3867 */
3868 if (iHistory < HISTORY)
3869 {
3870 if (iCmp > 0) /* Insert after. */
3871 j++;
3872 for (k = iHistory; k < j; k--)
3873 apszHistory[k] = apszHistory[k - 1];
3874 apszHistory[j] = pdep->pszRule;
3875 iHistory++;
3876 }
3877
3878 #endif
3879
3880#endif
3881 /*
3882 * Push on to the stack.
3883 */
3884 #ifdef DEPTH_FIRST
3885 /* dept first */
3886 appsz[i++] = ppsz; /* save current posistion */
3887 ppsz = pdep->papszDep; /* process new node */
3888 #else
3889 /* complete current node first. */
3890 appsz[i++] = pdep->papszDep;
3891 #endif
3892 }
3893 }
3894 }
3895
3896 return FALSE;
3897}
3898
3899
3900/**
3901 * Validates that the dependencies for the file exists
3902 * in the given locations. Dependants without path is ignored.
3903 * @returns TRUE if all ok.
3904 * FALSE if one (or possibly more) dependants are non-existing.
3905 * @param pdepRule Pointer to rule we're to validate.
3906 */
3907BOOL depValidate(PDEPRULE pdepRule)
3908{
3909 int i;
3910
3911 for (i = 0; i < pdepRule->cDeps; i++)
3912 {
3913 char *psz = pdepRule->papszDep[i];
3914 if ( psz[1] == ':'
3915 || strchr(psz, '\\')
3916 || strchr(psz, '/')
3917 )
3918 {
3919 /*
3920 * Check existance of the file.
3921 * Search cache first
3922 */
3923 if (!filecacheFind(psz))
3924 {
3925 char szDir[CCHMAXPATH];
3926
3927 filePathSlash(psz, szDir);
3928 if (!filecacheIsDirCached(szDir))
3929 {
3930 /*
3931 * If caching of entire dirs are enabled, we'll
3932 * add the directory to the cache and search it.
3933 */
3934 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
3935 {
3936 if (!filecacheFind(psz))
3937 return FALSE;
3938 }
3939 else
3940 {
3941 FILESTATUS3 fsts3;
3942
3943 /* ask the OS */
3944 if (DosQueryPathInfo(psz, FIL_STANDARD, &fsts3, sizeof(fsts3)))
3945 return FALSE;
3946 /* add file to cache. */
3947 filecacheAddFile(psz);
3948 }
3949 }
3950 }
3951 }
3952 }
3953
3954 return TRUE;
3955}
3956
3957
3958/**
3959 * Make a timestamp from the file data provided thru the
3960 * search API.
3961 * @returns Pointer to pszTS
3962 * @param pszTS Pointer to timestamp (output).
3963 * @param pfindbuf3 Pointer to search result.
3964 */
3965INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3)
3966{
3967 sprintf(pszTS, "%04d-%02d-%02d-%02d.%02d.%02d 0x%04x%04x %d",
3968 pfindbuf3->fdateLastWrite.year + 1980,
3969 pfindbuf3->fdateLastWrite.month,
3970 pfindbuf3->fdateLastWrite.day,
3971 pfindbuf3->ftimeLastWrite.hours,
3972 pfindbuf3->ftimeLastWrite.minutes,
3973 pfindbuf3->ftimeLastWrite.twosecs * 2,
3974 (ULONG)*(PUSHORT)(void*)&pfindbuf3->fdateCreation,
3975 (ULONG)*(PUSHORT)(void*)&pfindbuf3->ftimeCreation,
3976 pfindbuf3->cbFile);
3977 return pszTS;
3978}
3979
3980
3981
3982
3983
3984/*
3985 * Testing purpose.
3986 */
3987
3988#if !defined(OS2FAKE)
3989#include <os2.h>
3990#endif
3991#ifdef OLEMANN
3992#include "olemann.h"
3993#endif
Note: See TracBrowser for help on using the repository browser.