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

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

Corrected bug in depValidate which prevented files with dead dependencies from being rescanned as the should.

File size: 128.1 KB
Line 
1/* $Id: fastdep.c,v 1.42 2002-09-05 02:20:37 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 iStart;
3784 int iEnd;
3785 int iCmp;
3786#endif
3787 PDEPRULE pdep;
3788 int i;
3789
3790 /* self check */
3791 if (strcmp(pdepRule->pszRule, pszDep) == 0)
3792 return TRUE;
3793
3794 /* find rule for the dep. */
3795 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
3796 || pdep->papszDep == NULL)
3797 return FALSE; /* no rule, or no dependents, not cyclic */
3798
3799 i = 1;
3800 appsz[0] = pdep->papszDep;
3801#ifdef HISTORY
3802 iHistory = 1;
3803 apszHistory[0] = pdep->pszRule;
3804#endif
3805 while (i > 0)
3806 {
3807 /* pop off element */
3808 register char ** ppsz = appsz[--i];
3809
3810 while (*ppsz != NULL)
3811 {
3812 /* check if equal to the main rule */
3813 if (strcmp(pszRule, *ppsz) == 0)
3814 return TRUE;
3815
3816 /* push onto stack (ppsz is incremented in this test!) */
3817 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
3818 && pdep->papszDep != NULL)
3819 {
3820 if (i >= DEPTH)
3821 {
3822 fprintf(stderr, "error: too deep chain (%d). pszRule=%s pszDep=%s\n",
3823 i, pszRule, pszDep);
3824 return FALSE;
3825 }
3826#ifdef HISTORY
3827 /*
3828 * Check if in history, if so we'll skip it.
3829 */
3830 #if 0
3831 for (j = 0; j < iHistory; j++)
3832 if (!strcmp(apszHistory[j], pdep->pszRule))
3833 break;
3834 if (j != iHistory)
3835 continue; /* found */
3836
3837 /*
3838 * Push into history - might concider make this binary sorted one day.
3839 */
3840 if (iHistory < HISTORY)
3841 apszHistory[iHistory++] = pdep->pszRule;
3842
3843 #else
3844
3845 /*
3846 * Check if in history, if so we'll skip it.
3847 * (Binary search)
3848 * ASSUMES: Always something in the history!
3849 */
3850 iEnd = iHistory - 1;
3851 iStart = 0;
3852 j = iHistory / 2;
3853 while ( (iCmp = strcmp(pdep->pszRule, apszHistory[j])) != 0
3854 && iEnd != iStart)
3855 {
3856 if (iCmp < 0)
3857 iEnd = j - 1;
3858 else
3859 iStart = j + 1;
3860 if (iStart > iEnd)
3861 break;
3862 j = (iStart + iEnd) / 2;
3863 }
3864
3865 if (!iCmp)
3866 continue; /* found */
3867
3868 /*
3869 * Push into history - might concider make this binary sorted one day.
3870 */
3871 if (iHistory < HISTORY)
3872 {
3873 int k;
3874 if (iCmp > 0) /* Insert after. */
3875 j++;
3876 for (k = iHistory; k > j; k--)
3877 apszHistory[k] = apszHistory[k - 1];
3878 apszHistory[j] = pdep->pszRule;
3879 iHistory++;
3880 }
3881
3882 #endif
3883
3884#endif
3885 /*
3886 * Push on to the stack.
3887 */
3888 #ifdef DEPTH_FIRST
3889 /* dept first */
3890 appsz[i++] = ppsz; /* save current posistion */
3891 ppsz = pdep->papszDep; /* process new node */
3892 #else
3893 /* complete current node first. */
3894 appsz[i++] = pdep->papszDep;
3895 #endif
3896 }
3897 }
3898 }
3899
3900 return FALSE;
3901}
3902
3903
3904/**
3905 * Validates that the dependencies for the file exists
3906 * in the given locations. Dependants without path is ignored.
3907 * @returns TRUE if all ok.
3908 * FALSE if one (or possibly more) dependants are non-existing.
3909 * @param pdepRule Pointer to rule we're to validate.
3910 */
3911BOOL depValidate(PDEPRULE pdepRule)
3912{
3913 int i;
3914
3915 for (i = 0; i < pdepRule->cDeps; i++)
3916 {
3917 char *psz = pdepRule->papszDep[i];
3918 if ( psz[1] == ':'
3919 || strchr(psz, '\\')
3920 || strchr(psz, '/')
3921 )
3922 {
3923 /*
3924 * Check existance of the file.
3925 * Search cache first
3926 */
3927 if (!filecacheFind(psz))
3928 {
3929 char szDir[CCHMAXPATH];
3930
3931 filePathSlash(psz, szDir);
3932 if (!filecacheIsDirCached(szDir))
3933 {
3934 /*
3935 * If caching of entire dirs are enabled, we'll
3936 * add the directory to the cache and search it.
3937 */
3938 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
3939 {
3940 if (!filecacheFind(psz))
3941 return FALSE;
3942 }
3943 else
3944 {
3945 FILESTATUS3 fsts3;
3946
3947 /* ask the OS */
3948 if (DosQueryPathInfo(psz, FIL_STANDARD, &fsts3, sizeof(fsts3)))
3949 return FALSE;
3950 /* add file to cache. */
3951 filecacheAddFile(psz);
3952 }
3953 }
3954 /*
3955 * Dir was cached, hence the file doesn't exist
3956 * and the we should rescan the source file.
3957 */
3958 else
3959 return FALSE;
3960 }
3961 }
3962 }
3963
3964 return TRUE;
3965}
3966
3967
3968/**
3969 * Make a timestamp from the file data provided thru the
3970 * search API.
3971 * @returns Pointer to pszTS
3972 * @param pszTS Pointer to timestamp (output).
3973 * @param pfindbuf3 Pointer to search result.
3974 */
3975INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3)
3976{
3977 sprintf(pszTS, "%04d-%02d-%02d-%02d.%02d.%02d 0x%04x%04x %d",
3978 pfindbuf3->fdateLastWrite.year + 1980,
3979 pfindbuf3->fdateLastWrite.month,
3980 pfindbuf3->fdateLastWrite.day,
3981 pfindbuf3->ftimeLastWrite.hours,
3982 pfindbuf3->ftimeLastWrite.minutes,
3983 pfindbuf3->ftimeLastWrite.twosecs * 2,
3984 (ULONG)*(PUSHORT)(void*)&pfindbuf3->fdateCreation,
3985 (ULONG)*(PUSHORT)(void*)&pfindbuf3->ftimeCreation,
3986 pfindbuf3->cbFile);
3987 return pszTS;
3988}
3989
3990
3991
3992
3993
3994/*
3995 * Testing purpose.
3996 */
3997
3998#if !defined(OS2FAKE)
3999#include <os2.h>
4000#endif
4001#ifdef OLEMANN
4002#include "olemann.h"
4003#endif
Note: See TracBrowser for help on using the repository browser.