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

Last change on this file since 7340 was 7340, checked in by bird, 24 years ago

IPF support.

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