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

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

Check if super dependency is a macro assignment.

File size: 123.8 KB
Line 
1/* $Id: fastdep.c,v 1.35 2002-02-22 23:48:52 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 " You can specify a '=' at the end and a macro will\n"
915 " be created instead of a rule.\n"
916 " Default: disabled\n"
917 " -r[ ]<rsrcext> Resource binary extention. Default: res\n"
918 " -x[ ]<f1[;f2]> Files to exclude. Only exact filenames.\n"
919 " <files> Files to scan. Wildchars are allowed.\n"
920 "\n"
921 "Options and files could be mixed.\n"
922 " copyright (c) 1999-2001 knut st. osmundsen (knut.stange.osmundsen@mynd.no)\n",
923 pszDefaultDepFile
924 );
925}
926
927
928/**
929 * Generates depend info on this file, these are stored internally
930 * and written to file later.
931 * @returns
932 * @param pszFilename Pointer to source filename. Correct case is assumed!
933 * @param pszTS File time stamp.
934 * @status completely implemented.
935 * @author knut st. osmundsen
936 */
937int makeDependent(const char *pszFilename, const char *pszTS)
938{
939 int rc = -1;
940
941 char szExt[CCHMAXPATH];
942 PCONFIGENTRY pCfg = &aConfig[0];
943 BOOL fHeader;
944
945 /*
946 * Find which filetype this is...
947 */
948 fileExt(pszFilename, szExt);
949 while (pCfg->papszExts != NULL)
950 {
951 const char **ppsz = pCfg->papszExts;
952 while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
953 ppsz++;
954 if (*ppsz != NULL)
955 {
956 fHeader = pCfg->iFirstHdr > 0 && &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
957 break;
958 }
959 pCfg++;
960 }
961
962 /* Found? */
963 if (pCfg->papszExts != NULL)
964 {
965 char szNormFile[CCHMAXPATH];
966 fileNormalize2(pszFilename, szNormFile);
967 rc = (*pCfg->pfn)(pszFilename, &szNormFile[0], pszTS, fHeader);
968 }
969 else
970 {
971 if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
972 fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
973 rc = 0;
974 }
975
976
977 return rc;
978}
979
980
981/**
982 * Generates depend info on this C or C++ file, these are stored internally
983 * and written to file later.
984 * @returns 0 on success.
985 * !0 on error.
986 * @param pszFilename Pointer to source filename. Correct case is assumed!
987 * @param pszNormFilename Pointer to normalized source filename.
988 * @param pszTS File time stamp.
989 * @parma fHeader True if header file is being scanned.
990 * @status completely implemented.
991 * @author knut st. osmundsen
992 */
993int langC_CPP(const char *pszFilename, const char *pszNormFilename,
994 const char *pszTS, BOOL fHeader)
995{
996 void * pvFile; /* Text buffer pointer. */
997 void * pvRule; /* Handle to the current rule. */
998 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
999 int iLine; /* Linenumber. */
1000 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1001 BOOL fComment; /* TRUE when within a multiline comment. */
1002 /* FALSE when not within a multiline comment. */
1003 int iIfStack; /* StackPointer. */
1004 struct IfStackEntry
1005 {
1006 int fIncluded : 1; /* TRUE: include this code;
1007 * FALSE: excluded */
1008 int fIf : 1; /* TRUE: #if part of the expression.
1009 * FALSE: #else part of the expression. */
1010 int fSupported : 1; /* TRUE: supported if/else statement
1011 * FALSE: unsupported all else[<something>] are ignored
1012 * All code is included.
1013 */
1014 } achIfStack[256];
1015 char szSrcDir[CCHMAXPATH];
1016 filePath(pszNormFilename, szSrcDir);/* determin source code directory. */
1017
1018 /**********************************/
1019 /* Add the depend rule */
1020 /**********************************/
1021 if (options.fObjRule && !fHeader)
1022 {
1023 if (options.fNoObjectPath)
1024 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS);
1025 else
1026 pvRule = depAddRule(options.fObjectDir ?
1027 options.pszObjectDir :
1028 filePathSlash(pszFilename, szBuffer),
1029 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1030 options.pszObjectExt,
1031 pszTS);
1032
1033 if (options.fSrcWhenObj && pvRule)
1034 depAddDepend(pvRule,
1035 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1036 fileName(pszFilename, szBuffer) : pszNormFilename,
1037 options.fCheckCyclic);
1038 }
1039 else
1040 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1041 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
1042
1043 /* duplicate rule? */
1044 if (pvRule == NULL)
1045 return 0;
1046
1047
1048 /********************/
1049 /* Make file buffer */
1050 /********************/
1051 pvFile = textbufferCreate(pszFilename);
1052 if (!pvFile)
1053 {
1054 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1055 return -1;
1056 }
1057
1058
1059 /*******************/
1060 /* find dependants */
1061 /*******************/
1062 /* Initiate the IF-stack, comment state and line number. */
1063 iIfStack = 0;
1064 achIfStack[iIfStack].fIf = TRUE;
1065 achIfStack[iIfStack].fIncluded = TRUE;
1066 achIfStack[iIfStack].fSupported = TRUE;
1067 fComment = FALSE;
1068 iLine = 0;
1069 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1070 {
1071 /* search for #include */
1072 register char *pszC;
1073 int cbLen;
1074 int i = 0;
1075 iLine++;
1076
1077 /* skip blank chars */
1078 cbLen = strlen(szBuffer);
1079 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1080 i++;
1081
1082 /* preprocessor statement? */
1083 if (!fComment && szBuffer[i] == '#')
1084 {
1085 /*
1086 * Preprocessor checks
1087 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1088 * Depending on the word afterwards we'll take some different actions.
1089 * So we'll start of by extracting that word and make a string swich on it.
1090 * Note that there might be some blanks between the hash and the word.
1091 */
1092 int cchWord;
1093 char * pszEndWord;
1094 char * pszArgument;
1095 i++; /* skip hash ('#') */
1096 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1097 i++;
1098 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1099 cchWord = pszEndWord - &szBuffer[i];
1100
1101 /*
1102 * Find the argument by skipping the blanks.
1103 */
1104 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1105 pszArgument++;
1106
1107 /*
1108 * string switch.
1109 */
1110 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1111 {
1112 /*
1113 * #include
1114 *
1115 * Are we in a state where this file is to be included?
1116 */
1117 if (achIfStack[iIfStack].fIncluded)
1118 {
1119 char szFullname[CCHMAXPATH];
1120 char * psz;
1121 BOOL f = FALSE;
1122 int j;
1123 BOOL fQuote;
1124
1125 /* extract info between "" or <> */
1126 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1127 i++;
1128 fQuote = szBuffer[i] == '"';
1129 i++; /* skip '"' or '<' */
1130
1131 /* if invalid statement then continue with the next line! */
1132 if (!f) continue;
1133
1134 /* find end */
1135 j = f = 0;
1136 while (i + j < cbLen && j < CCHMAXPATH &&
1137 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1138 j++;
1139
1140 /* if invalid statement then continue with the next line! */
1141 if (!f) continue;
1142
1143 /* copy filename */
1144 strncpy(szFullname, &szBuffer[i], j);
1145 szFullname[j] = '\0'; /* ensure terminatition. */
1146 strlwr(szFullname);
1147
1148 /* find include file! */
1149 psz = fQuote ? pathlistFindFile(szSrcDir, szFullname, szBuffer) : NULL;
1150 if (psz == NULL)
1151 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1152 if (psz == NULL)
1153 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1154
1155 /* did we find the include? */
1156 if (psz != NULL)
1157 {
1158 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1159 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1160 else
1161 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
1162 }
1163 else
1164 {
1165 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1166 pszFilename, iLine, szFullname);
1167 depMarkNotFound(pvRule);
1168 }
1169 }
1170 }
1171 else
1172 /*
1173 * #if
1174 */
1175 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1176 { /* #if 0 and #if <1-9> are supported */
1177 pszEndWord = findEndOfWord(pszArgument);
1178 iIfStack++;
1179 if ((pszEndWord - pszArgument) == 1
1180 && *pszArgument >= '0' && *pszArgument <= '9')
1181 {
1182 if (*pszArgument != '0')
1183 achIfStack[iIfStack].fIncluded = TRUE;
1184 else
1185 achIfStack[iIfStack].fIncluded = FALSE;
1186 }
1187 else
1188 achIfStack[iIfStack].fSupported = FALSE;
1189 achIfStack[iIfStack].fIncluded = TRUE;
1190 achIfStack[iIfStack].fIf = TRUE;
1191 }
1192 else
1193 /*
1194 * #else
1195 */
1196 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1197 {
1198 if (achIfStack[iIfStack].fSupported)
1199 {
1200 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1201 achIfStack[iIfStack].fIncluded = FALSE;
1202 else
1203 achIfStack[iIfStack].fIncluded = TRUE;
1204 }
1205 achIfStack[iIfStack].fIf = FALSE;
1206 }
1207 else
1208 /*
1209 * #endif
1210 */
1211 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1212 { /* Pop the if-stack. */
1213 if (iIfStack > 0)
1214 iIfStack--;
1215 else
1216 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1217 }
1218 /*
1219 * general if<something> and elseif<something> implementations
1220 */
1221 else
1222 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1223 {
1224 achIfStack[iIfStack].fSupported = FALSE;
1225 achIfStack[iIfStack].fIncluded = TRUE;
1226 }
1227 else
1228 if (strncmp(&szBuffer[i], "if", 2) == 0)
1229 {
1230 iIfStack++;
1231 achIfStack[iIfStack].fIf = TRUE;
1232 achIfStack[iIfStack].fSupported = FALSE;
1233 achIfStack[iIfStack].fIncluded = TRUE;
1234 }
1235 /* The rest of them aren't implemented yet.
1236 else if (strncmp(&szBuffer[i], "if") == 0)
1237 {
1238 }
1239 */
1240 }
1241
1242 /*
1243 * Comment checks.
1244 * -Start at first non-blank.
1245 * -Loop thru the line since we might have more than one
1246 * comment statement on a single line.
1247 */
1248 pszC = &szBuffer[i];
1249 while (pszC != NULL && *pszC != '\0')
1250 {
1251 if (fComment)
1252 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
1253 else
1254 {
1255 char *pszLC;
1256 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
1257 pszC = strstr(pszC, "/*"); /* look for start comment mark */
1258 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
1259 break; /* muliline comment mark we'll ignore the multiline mark. */
1260 }
1261
1262 /* Comment mark found? */
1263 if (pszC != NULL)
1264 {
1265 fComment = !fComment;
1266 pszC += 2; /* skip comment mark */
1267
1268 /* debug */
1269 /*
1270 if (fComment)
1271 fprintf(stderr, "starts at line %d\n", iLine);
1272 else
1273 fprintf(stderr, "ends at line %d\n", iLine);
1274 */
1275 }
1276 }
1277 } /*while*/
1278
1279 textbufferDestroy(pvFile);
1280
1281 return 0;
1282}
1283
1284
1285/**
1286 * Generates depend info on this file, these are stored internally
1287 * and written to file later.
1288 * @returns 0 on success.
1289 * !0 on error.
1290 * @param pszFilename Pointer to source filename. Correct case is assumed!
1291 * @param pszNormFilename Pointer to normalized source filename.
1292 * @param pszTS File time stamp.
1293 * @parma fHeader True if header file is being scanned.
1294 * @status completely implemented.
1295 * @author knut st. osmundsen
1296 */
1297int langAsm(const char *pszFilename, const char *pszNormFilename,
1298 const char *pszTS, BOOL fHeader)
1299{
1300 void * pvFile; /* Text buffer pointer. */
1301 void * pvRule; /* Handle to the current rule. */
1302 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1303 int iLine; /* current line number */
1304 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1305
1306
1307 /**********************************/
1308 /* Add the depend rule */
1309 /**********************************/
1310 if (options.fObjRule && !fHeader)
1311 {
1312 if (options.fNoObjectPath)
1313 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS);
1314 else
1315 pvRule = depAddRule(options.fObjectDir ?
1316 options.pszObjectDir :
1317 filePathSlash(pszFilename, szBuffer),
1318 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1319 options.pszObjectExt,
1320 pszTS);
1321
1322 if (options.fSrcWhenObj && pvRule)
1323 depAddDepend(pvRule,
1324 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1325 fileName(pszFilename, szBuffer) : pszNormFilename,
1326 options.fCheckCyclic);
1327 }
1328 else
1329 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1330 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
1331
1332 /* duplicate rule? */
1333 if (pvRule == NULL)
1334 return 0;
1335
1336
1337 /********************/
1338 /* Make file buffer */
1339 /********************/
1340 pvFile = textbufferCreate(pszFilename);
1341 if (!pvFile)
1342 {
1343 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1344 return -1;
1345 }
1346
1347
1348 /*******************/
1349 /* find dependants */
1350 /*******************/
1351 iLine = 0;
1352 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1353 {
1354 /* search for include */
1355 int cbLen;
1356 int i = 0;
1357 iLine++;
1358
1359 /* skip blank chars */
1360 cbLen = strlen(szBuffer);
1361 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1362 i++;
1363
1364 /* is this an include? */
1365 if (strnicmp(&szBuffer[i], "include", 7) == 0
1366 && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
1367 )
1368 {
1369 char szFullname[CCHMAXPATH];
1370 char *psz;
1371 int j;
1372
1373 /* skip to first no blank char */
1374 i += 7;
1375 while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1376 i++;
1377
1378 /* comment check - if comment found, no filename was given. continue. */
1379 if (szBuffer[i] == ';') continue;
1380
1381 /* find end */
1382 j = 0;
1383 while (i + j < cbLen
1384 && j < CCHMAXPATH
1385 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
1386 && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';' && szBuffer[i+j] != '\r'
1387 )
1388 j++;
1389
1390 /* copy filename */
1391 strncpy(szFullname, &szBuffer[i], j);
1392 szFullname[j] = '\0'; /* ensure terminatition. */
1393 strlwr(szFullname);
1394
1395 /* find include file! */
1396 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1397 if (psz == NULL)
1398 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1399
1400 /* Did we find the include? */
1401 if (psz != NULL)
1402 {
1403 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1404 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1405 else
1406 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
1407 }
1408 else
1409 {
1410 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1411 pszFilename, iLine, szFullname);
1412 depMarkNotFound(pvRule);
1413 }
1414 }
1415 } /*while*/
1416
1417 textbufferDestroy(pvFile);
1418
1419 return 0;
1420}
1421
1422
1423/**
1424 * Generates depend info on this Resource file, these are stored internally
1425 * and written to file later.
1426 * @returns 0 on success.
1427 * !0 on error.
1428 * @param pszFilename Pointer to source filename. Correct case is assumed!
1429 * @param pszNormFilename Pointer to normalized source filename.
1430 * @param pszTS File time stamp.
1431 * @parma fHeader True if header file is being scanned.
1432 * @status completely implemented.
1433 * @author knut st. osmundsen
1434 */
1435#if 0
1436int langRC(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader)
1437{
1438 void * pvFile; /* Text buffer pointer. */
1439 void * pvRule; /* Handle to the current rule. */
1440 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1441 int iLine; /* current line number */
1442 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1443
1444
1445 /**********************************/
1446 /* Add the depend rule */
1447 /**********************************/
1448 if (options.fObjRule && !fHeader)
1449 {
1450 if (options.fNoObjectPath)
1451 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS);
1452 else
1453 pvRule = depAddRule(options.fObjectDir ?
1454 options.pszObjectDir :
1455 filePathSlash(pszFilename, szBuffer),
1456 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1457 options.pszRsrcExt,
1458 pszTS);
1459
1460 if (options.fSrcWhenObj && pvRule)
1461 depAddDepend(pvRule,
1462 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1463 fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1464 options.fCheckCyclic);
1465 }
1466 else
1467 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1468 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL,
1469 pszTS);
1470
1471 /* duplicate rule? */
1472 if (pvRule == NULL)
1473 return 0;
1474
1475
1476 /********************/
1477 /* Make file buffer */
1478 /********************/
1479 pvFile = textbufferCreate(pszFilename);
1480 if (!pvFile)
1481 {
1482 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1483 return -1;
1484 }
1485
1486
1487 /*******************/
1488 /* find dependants */
1489 /*******************/
1490 iLine = 0;
1491 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1492 {
1493 /* search for #include */
1494 int cbLen;
1495 int i = 0;
1496 int i1;
1497 iLine++;
1498
1499 /* skip blank chars */
1500 cbLen = strlen(szBuffer);
1501 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1502 i++;
1503
1504 /* is this an include? */
1505 i1 = 1;
1506 if ( strncmp(&szBuffer[i], "#include", 8) == 0
1507 || (i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9)) == 0
1508 || strnicmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
1509 )
1510 {
1511 char szFullname[CCHMAXPATH];
1512 char *psz;
1513 BOOL f = FALSE;
1514 int j;
1515
1516 if (i1 != 0)
1517 { /*
1518 * #include <file.h>, #include "file.h" or DLGINCLUDE 1 "file.h"
1519 *
1520 * extract info between "" or <>
1521 */
1522 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1523 i++;
1524 i++; /* skip '"' or '<' */
1525
1526 /* if invalid statement then continue with the next line! */
1527 if (!f) continue;
1528
1529 /* find end */
1530 j = f = 0;
1531 while (i + j < cbLen && j < CCHMAXPATH &&
1532 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1533 j++;
1534
1535 /* if invalid statement then continue with the next line! */
1536 if (!f) continue;
1537 }
1538 else
1539 { /*
1540 * RCINCLUDE ["]filename.dlg["]
1541 * Extract filename.
1542 */
1543
1544 /* skip to filename.dlg start - if eol will continue to loop. */
1545 i += 9;
1546 while (szBuffer[i] == ' ' || szBuffer[i] == '\t' || szBuffer[i] == '"')
1547 i++;
1548 if (szBuffer[i] == '\0')
1549 continue;
1550
1551 /* search to end of filename. */
1552 j = i+1;
1553 while ( szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1554 && szBuffer[i+j] != '"' && szBuffer[i+j] != '\0')
1555 j++;
1556 }
1557
1558 /* copy filename */
1559 strncpy(szFullname, &szBuffer[i], j);
1560 szFullname[j] = '\0'; /* ensure terminatition. */
1561 strlwr(szFullname);
1562
1563 /* find include file! */
1564 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1565 if (psz == NULL)
1566 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1567
1568 /* did we find the include? */
1569 if (psz != NULL)
1570 {
1571 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1572 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1573 else
1574 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
1575 }
1576 else
1577 {
1578 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1579 pszFilename, iLine, szFullname);
1580 depMarkNotFound(pvRule);
1581 }
1582 }
1583 } /*while*/
1584
1585 textbufferDestroy(pvFile);
1586 return 0;
1587}
1588#else
1589int langRC(const char *pszFilename, const char *pszNormFilename,
1590 const char *pszTS, BOOL fHeader)
1591{
1592 void * pvFile; /* Text buffer pointer. */
1593 void * pvRule; /* Handle to the current rule. */
1594 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
1595 int iLine; /* Linenumber. */
1596 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1597 BOOL fComment; /* TRUE when within a multiline comment. */
1598 /* FALSE when not within a multiline comment. */
1599 int iIfStack; /* StackPointer. */
1600 struct IfStackEntry
1601 {
1602 int fIncluded : 1; /* TRUE: include this code;
1603 * FALSE: excluded */
1604 int fIf : 1; /* TRUE: #if part of the expression.
1605 * FALSE: #else part of the expression. */
1606 int fSupported : 1; /* TRUE: supported if/else statement
1607 * FALSE: unsupported all else[<something>] are ignored
1608 * All code is included.
1609 */
1610 } achIfStack[256];
1611
1612
1613 /**********************************/
1614 /* Add the depend rule */
1615 /**********************************/
1616 if (options.fObjRule && !fHeader)
1617 {
1618 if (options.fNoObjectPath)
1619 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS);
1620 else
1621 pvRule = depAddRule(options.fObjectDir ?
1622 options.pszObjectDir :
1623 filePathSlash(pszFilename, szBuffer),
1624 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1625 options.pszRsrcExt,
1626 pszTS);
1627
1628 if (options.fSrcWhenObj && pvRule)
1629 depAddDepend(pvRule,
1630 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1631 fileName(pszFilename, szBuffer) : pszNormFilename,
1632 options.fCheckCyclic);
1633 }
1634 else
1635 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1636 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
1637
1638 /* duplicate rule? */
1639 if (pvRule == NULL)
1640 return 0;
1641
1642
1643 /********************/
1644 /* Make file buffer */
1645 /********************/
1646 pvFile = textbufferCreate(pszFilename);
1647 if (!pvFile)
1648 {
1649 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1650 return -1;
1651 }
1652
1653
1654 /*******************/
1655 /* find dependants */
1656 /*******************/
1657 /* Initiate the IF-stack, comment state and line number. */
1658 iIfStack = 0;
1659 achIfStack[iIfStack].fIf = TRUE;
1660 achIfStack[iIfStack].fIncluded = TRUE;
1661 achIfStack[iIfStack].fSupported = TRUE;
1662 fComment = FALSE;
1663 iLine = 0;
1664 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1665 {
1666 register char * pszC;
1667 char szFullname[CCHMAXPATH];
1668 int cbLen;
1669 int i1 = 1;
1670 int i = 0;
1671 iLine++;
1672
1673 /* skip blank chars */
1674 cbLen = strlen(szBuffer);
1675 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1676 i++;
1677
1678 /* preprocessor statement? */
1679 if (!fComment && szBuffer[i] == '#')
1680 {
1681 /*
1682 * Preprocessor checks
1683 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1684 * Depending on the word afterwards we'll take some different actions.
1685 * So we'll start of by extracting that word and make a string swich on it.
1686 * Note that there might be some blanks between the hash and the word.
1687 */
1688 int cchWord;
1689 char * pszEndWord;
1690 char * pszArgument;
1691 i++; /* skip hash ('#') */
1692 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1693 i++;
1694 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1695 cchWord = pszEndWord - &szBuffer[i];
1696
1697 /*
1698 * Find the argument by skipping the blanks.
1699 */
1700 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1701 pszArgument++;
1702
1703 /*
1704 * string switch.
1705 */
1706 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1707 {
1708 /*
1709 * #include
1710 *
1711 * Are we in a state where this file is to be included?
1712 */
1713 if (achIfStack[iIfStack].fIncluded)
1714 {
1715 char *psz;
1716 BOOL f = FALSE;
1717 int j;
1718
1719 /* extract info between "" or <> */
1720 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1721 i++;
1722 i++; /* skip '"' or '<' */
1723
1724 /* if invalid statement then continue with the next line! */
1725 if (!f) continue;
1726
1727 /* find end */
1728 j = f = 0;
1729 while (i + j < cbLen && j < CCHMAXPATH &&
1730 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1731 j++;
1732
1733 /* if invalid statement then continue with the next line! */
1734 if (!f) continue;
1735
1736 /* copy filename */
1737 strncpy(szFullname, &szBuffer[i], j);
1738 szFullname[j] = '\0'; /* ensure terminatition. */
1739 strlwr(szFullname);
1740
1741 /* find include file! */
1742 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1743 if (psz == NULL)
1744 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1745
1746 /* did we find the include? */
1747 if (psz != NULL)
1748 {
1749 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1750 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1751 else
1752 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
1753 }
1754 else
1755 {
1756 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1757 pszFilename, iLine, szFullname);
1758 depMarkNotFound(pvRule);
1759 }
1760 }
1761 }
1762 else
1763 /*
1764 * #if
1765 */
1766 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1767 { /* #if 0 and #if <1-9> are supported */
1768 pszEndWord = findEndOfWord(pszArgument);
1769 iIfStack++;
1770 if ((pszEndWord - pszArgument) == 1
1771 && *pszArgument >= '0' && *pszArgument <= '9')
1772 {
1773 if (*pszArgument != '0')
1774 achIfStack[iIfStack].fIncluded = TRUE;
1775 else
1776 achIfStack[iIfStack].fIncluded = FALSE;
1777 }
1778 else
1779 achIfStack[iIfStack].fSupported = FALSE;
1780 achIfStack[iIfStack].fIncluded = TRUE;
1781 achIfStack[iIfStack].fIf = TRUE;
1782 }
1783 else
1784 /*
1785 * #else
1786 */
1787 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1788 {
1789 if (achIfStack[iIfStack].fSupported)
1790 {
1791 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1792 achIfStack[iIfStack].fIncluded = FALSE;
1793 else
1794 achIfStack[iIfStack].fIncluded = TRUE;
1795 }
1796 achIfStack[iIfStack].fIf = FALSE;
1797 }
1798 else
1799 /*
1800 * #endif
1801 */
1802 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1803 { /* Pop the if-stack. */
1804 if (iIfStack > 0)
1805 iIfStack--;
1806 else
1807 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1808 }
1809 /*
1810 * general if<something> and elseif<something> implementations
1811 */
1812 else
1813 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1814 {
1815 achIfStack[iIfStack].fSupported = FALSE;
1816 achIfStack[iIfStack].fIncluded = TRUE;
1817 }
1818 else
1819 if (strncmp(&szBuffer[i], "if", 2) == 0)
1820 {
1821 iIfStack++;
1822 achIfStack[iIfStack].fIf = TRUE;
1823 achIfStack[iIfStack].fSupported = FALSE;
1824 achIfStack[iIfStack].fIncluded = TRUE;
1825 }
1826 /* The rest of them aren't implemented yet.
1827 else if (strncmp(&szBuffer[i], "if") == 0)
1828 {
1829 }
1830 */
1831 } else
1832 /*
1833 * Check for resource compiler directives.
1834 */
1835 if ( !fComment
1836 && !strchr(&szBuffer[i], ',')
1837 && ( !strnicmp(&szBuffer[i], "ICON", 4)
1838 || !strnicmp(&szBuffer[i], "FONT", 4)
1839 || !strnicmp(&szBuffer[i], "BITMAP", 6)
1840 || !strnicmp(&szBuffer[i], "POINTER", 7)
1841 || !strnicmp(&szBuffer[i], "RESOURCE", 8)
1842 || !(i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9))
1843 /*|| !strnicmp(&szBuffer[i], "DLGINCLUDE", 10) - only used by the dlgeditor */
1844 || !strnicmp(&szBuffer[i], "DEFAULTICON", 11)
1845 )
1846 )
1847 {
1848 /*
1849 * RESOURCE 123 1 ["]filename.ext["]
1850 */
1851 char szLine[1024];
1852 char * pszFilename;
1853 char chQuote = ' ';
1854
1855 PreProcessLine(szLine, &szBuffer[i]);
1856
1857 pszFilename = &szLine[strlen(szLine)-1];
1858 if (*pszFilename == '\"' || *pszFilename == '\'')
1859 {
1860 chQuote = *pszFilename;
1861 *pszFilename-- = '\0';
1862 }
1863 while (*pszFilename != chQuote)
1864 pszFilename--;
1865 *pszFilename++ = '\0'; /* We now have extracted the filename - pszFilename. */
1866 strlwr(pszFilename);
1867
1868 /* Add filename to the dependencies. */
1869 if (i1)
1870 depAddDepend(pvRule, pszFilename, options.fCheckCyclic);
1871 else
1872 {
1873 char *psz;
1874 /* find include file! */
1875 psz = pathlistFindFile(options.pszInclude, pszFilename, szFullname);
1876 if (psz == NULL)
1877 psz = pathlistFindFile(pszIncludeEnv, pszFilename, szFullname);
1878
1879 /* did we find the include? */
1880 if (psz != NULL)
1881 {
1882 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szFullname))
1883 depAddDepend(pvRule, pszFilename, options.fCheckCyclic);
1884 else
1885 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1886 }
1887 else
1888 {
1889 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1890 pszFilename, iLine, pszFilename);
1891 depMarkNotFound(pvRule);
1892 }
1893 }
1894 }
1895
1896
1897 /*
1898 * Comment checks.
1899 * -Start at first non-blank.
1900 * -Loop thru the line since we might have more than one
1901 * comment statement on a single line.
1902 */
1903 pszC = &szBuffer[i];
1904 while (pszC != NULL && *pszC != '\0')
1905 {
1906 if (fComment)
1907 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
1908 else
1909 {
1910 char *pszLC;
1911 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
1912 pszC = strstr(pszC, "/*"); /* look for start comment mark */
1913 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
1914 break; /* muliline comment mark we'll ignore the multiline mark. */
1915 }
1916
1917 /* Comment mark found? */
1918 if (pszC != NULL)
1919 {
1920 fComment = !fComment;
1921 pszC += 2; /* skip comment mark */
1922
1923 /* debug */
1924 /*
1925 if (fComment)
1926 fprintf(stderr, "starts at line %d\n", iLine);
1927 else
1928 fprintf(stderr, "ends at line %d\n", iLine);
1929 */
1930 }
1931 }
1932 } /*while*/
1933
1934 textbufferDestroy(pvFile);
1935
1936 return 0;
1937}
1938#endif
1939
1940
1941/**
1942 * Generates depend info on this COBOL file, these are stored internally
1943 * and written to file later.
1944 * @returns 0 on success.
1945 * !0 on error.
1946 * @param pszFilename Pointer to source filename. Correct case is assumed!
1947 * @param pszNormFilename Pointer to normalized source filename.
1948 * @param pszTS File time stamp.
1949 * @parma fHeader True if header file is being scanned.
1950 * @status completely implemented.
1951 * @author knut st. osmundsen
1952 */
1953int langCOBOL(const char *pszFilename, const char *pszNormFilename,
1954 const char *pszTS, BOOL fHeader)
1955{
1956 void * pvFile; /* Text buffer pointer. */
1957 void * pvRule; /* Handle to the current rule. */
1958 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1959 int iLine; /* current line number */
1960 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1961
1962
1963 /**********************************/
1964 /* Add the depend rule */
1965 /**********************************/
1966 if (options.fObjRule && !fHeader)
1967 {
1968 if (options.fNoObjectPath)
1969 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS);
1970 else
1971 pvRule = depAddRule(options.fObjectDir ?
1972 options.pszObjectDir :
1973 filePathSlash(pszFilename, szBuffer),
1974 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1975 options.pszObjectExt,
1976 pszTS);
1977
1978 if (options.fSrcWhenObj && pvRule)
1979 depAddDepend(pvRule,
1980 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
1981 ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1982 options.fCheckCyclic);
1983 }
1984 else
1985 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1986 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
1987
1988 /* duplicate rule? */
1989 if (pvRule == NULL)
1990 return 0;
1991
1992
1993 /********************/
1994 /* Make file buffer */
1995 /********************/
1996 pvFile = textbufferCreate(pszFilename);
1997 if (!pvFile)
1998 {
1999 fprintf(stderr, "failed to open '%s'\n", pszFilename);
2000 return -1;
2001 }
2002
2003
2004 /*******************/
2005 /* find dependants */
2006 /*******************/
2007 iLine = 0;
2008 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
2009 {
2010 /* search for #include */
2011 int cbLen;
2012 int i = 0;
2013 int i1, i2;
2014 iLine++;
2015
2016 /* check for comment mark (column 7) */
2017 if (szBuffer[6] == '*')
2018 continue;
2019
2020 /* skip blank chars */
2021 cbLen = strlen(szBuffer);
2022 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
2023 i++;
2024
2025 /* is this an include? */
2026 if ( (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
2027 || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
2028 )
2029 {
2030 char szFullname[CCHMAXPATH];
2031 char *psz;
2032 int j;
2033
2034 /* skip statement */
2035 i += 4;
2036 if (i1 != 0)
2037 {
2038 int y = 2; /* skip two words */
2039 do
2040 {
2041 /* skip blanks */
2042 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2043 i++;
2044 /* skip word */
2045 while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
2046 && szBuffer[i] != '\0' && szBuffer[i] != '\n')
2047 i++;
2048 y--;
2049 } while (y > 0);
2050 }
2051
2052 /* check for blank */
2053 if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
2054 continue;
2055
2056 /* skip blanks */
2057 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2058 i++;
2059
2060 /* if invalid statement then continue with the next line! */
2061 if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
2062 continue;
2063
2064 /* find end */
2065 j = 0;
2066 while (i + j < cbLen && j < CCHMAXPATH
2067 && szBuffer[i+j] != '.'
2068 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
2069 && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
2070 )
2071 j++;
2072
2073 /* if invalid statement then continue with the next line! */
2074 if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
2075 continue;
2076
2077 /* copy filename */
2078 strncpy(szFullname, &szBuffer[i], j);
2079 szFullname[j] = '\0'; /* ensure terminatition. */
2080 strlwr(szFullname);
2081
2082 /* add extention .cpy - hardcoded for the moment. */
2083 strcpy(&szFullname[j], ".cpy");
2084
2085 /* find include file! */
2086 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2087 if (!psz)
2088 {
2089 strcpy(&szFullname[j], ".cbl");
2090 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2091 }
2092
2093 /* did we find the include? */
2094 if (psz != NULL)
2095 {
2096 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
2097 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
2098 else
2099 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
2100 }
2101 else
2102 {
2103 szFullname[j] = '\0';
2104 fprintf(stderr, "%s(%d): warning copybook '%s' was not found!\n",
2105 pszFilename, iLine, szFullname);
2106 depMarkNotFound(pvRule);
2107 }
2108 }
2109 } /*while*/
2110
2111 textbufferDestroy(pvFile);
2112
2113 return 0;
2114}
2115
2116
2117/**
2118 * Generates depend info on this IPF file, these are stored internally
2119 * and written to file later.
2120 * @returns 0 on success.
2121 * !0 on error.
2122 * @param pszFilename Pointer to source filename. Correct case is assumed!
2123 * @param pszNormFilename Pointer to normalized source filename.
2124 * @param pszTS File time stamp.
2125 * @parma fHeader True if header file is being scanned.
2126 * @status completely implemented.
2127 * @author knut st. osmundsen
2128 */
2129int langIPF( const char *pszFilename, const char *pszNormFilename,
2130 const char *pszTS, BOOL fHeader)
2131{
2132 void * pvFile; /* Text buffer pointer. */
2133 void * pvRule; /* Handle to the current rule. */
2134 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
2135 int iLine; /* current line number */
2136 void * pv = NULL; /* An index used by textbufferGetNextLine. */
2137
2138
2139 /**********************************/
2140 /* Add the depend rule */
2141 /**********************************/
2142 /*if (options.fObjRule && !fHeader)
2143 {
2144 if (options.fNoObjectPath)
2145 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS);
2146 else
2147 pvRule = depAddRule(options.fObjectDir ?
2148 options.pszObjectDir :
2149 filePathSlash(pszFilename, szBuffer),
2150 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
2151 options.pszObjectExt,
2152 pszTS);
2153
2154 if (options.fSrcWhenObj && pvRule)
2155 depAddDepend(pvRule,
2156 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
2157 ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
2158 options.fCheckCyclic);
2159 }
2160 else */
2161 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
2162 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
2163
2164 /* duplicate rule? */
2165 if (pvRule == NULL)
2166 return 0;
2167
2168
2169 /********************/
2170 /* Make file buffer */
2171 /********************/
2172 pvFile = textbufferCreate(pszFilename);
2173 if (!pvFile)
2174 {
2175 fprintf(stderr, "failed to open '%s'\n", pszFilename);
2176 return -1;
2177 }
2178
2179
2180 /*******************/
2181 /* find dependants */
2182 /*******************/
2183 iLine = 0;
2184 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
2185 {
2186 iLine++;
2187
2188 /* is this an imbed statement? */
2189 if (!strncmp(&szBuffer[0], ".im", 3))
2190 {
2191 char szFullname[CCHMAXPATH];
2192 char * psz;
2193 int i;
2194 int j;
2195 char chQuote = 0;
2196
2197 /* skip statement and blanks */
2198 i = 4;
2199 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2200 i++;
2201
2202 /* check for quotes */
2203 if (szBuffer[i] == '\'' || szBuffer[i] == '\"')
2204 chQuote = szBuffer[i++];
2205
2206 /* find end */
2207 j = 0;
2208 if (chQuote != 0)
2209 {
2210 while (szBuffer[i+j] != chQuote && szBuffer[i+j] != '\n' && szBuffer[i+j] != '\r' && szBuffer[i+j] != '\0')
2211 j++;
2212 }
2213 else
2214 {
2215 while (szBuffer[i+j] != '\n' && szBuffer[i+j] != '\r' && szBuffer[i+j] != '\0')
2216 j++;
2217 }
2218
2219 /* find end */
2220 if (j >= CCHMAXPATH)
2221 {
2222 fprintf(stderr, "%s(%d) warning: Filename too long ignored", pszFilename, iLine);
2223 continue;
2224 }
2225
2226 /* copy filename */
2227 strncpy(szFullname, &szBuffer[i], j);
2228 szFullname[j] = '\0'; /* ensure terminatition. */
2229 strlwr(szFullname);
2230
2231 /* find include file! */
2232 psz = filecacheFileExist(szFullname, szBuffer);
2233
2234 /* did we find the include? */
2235 if (psz != NULL)
2236 {
2237 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
2238 depAddDepend(pvRule, fileName(szFullname, szBuffer), options.fCheckCyclic);
2239 else
2240 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
2241 }
2242 else
2243 {
2244 fprintf(stderr, "%s(%d): warning imbeded file '%s' was not found!\n",
2245 pszFilename, iLine, szFullname);
2246 depMarkNotFound(pvRule);
2247 }
2248 }
2249 } /*while*/
2250
2251 textbufferDestroy(pvFile);
2252
2253 return 0;
2254}
2255
2256
2257
2258
2259#define upcase(ch) \
2260 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
2261
2262/**
2263 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
2264 * @returns 0 equal. (same as strnicmp)
2265 * @param pszS1 String 1
2266 * @param pszS2 String 2
2267 * @param cch Length to compare (relative to string 1)
2268 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2269 */
2270int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
2271{
2272 do
2273 {
2274 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
2275 pszS1++, pszS2++, cch--;
2276
2277 /* blank test and skipping */
2278 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
2279 {
2280 while (cch > 0 && *pszS1 == ' ')
2281 pszS1++, cch--;
2282
2283 while (*pszS2 == ' ')
2284 pszS2++;
2285 }
2286 else
2287 break;
2288 } while (cch > 0);
2289
2290 return cch == 0 ? 0 : *pszS1 - *pszS2;
2291}
2292
2293
2294/**
2295 * Normalizes the path slashes for the filename. It will partially expand paths too.
2296 * @returns pszFilename
2297 * @param pszFilename Pointer to filename string. Not empty string!
2298 * Much space to play with.
2299 */
2300char *fileNormalize(char *pszFilename)
2301{
2302 char *psz = pszFilename;
2303
2304 /* correct slashes */
2305 while ((pszFilename = strchr(pszFilename, '//')) != NULL)
2306 *pszFilename++ = '\\';
2307
2308 /* expand path? */
2309 pszFilename = psz;
2310 if (pszFilename[1] != ':')
2311 { /* relative path */
2312 int iSlash;
2313 char szFile[CCHMAXPATH];
2314 char * psz = szFile;
2315
2316 strcpy(szFile, pszFilename);
2317 iSlash = *psz == '\\' ? 1 : cSlashes;
2318 while (*psz != '\0')
2319 {
2320 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
2321 { /* up one directory */
2322 if (iSlash > 0)
2323 iSlash--;
2324 psz += 3;
2325 }
2326 else if (*psz == '.' && psz[1] == '\\')
2327 { /* no change */
2328 psz += 2;
2329 }
2330 else
2331 { /* completed expantion! */
2332 strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
2333 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
2334 break;
2335 }
2336 }
2337 }
2338 /* else: assume full path */
2339
2340 return psz;
2341}
2342
2343
2344/**
2345 * Normalizes the path slashes for the filename. It will partially expand paths too.
2346 * Makes name all lower case too.
2347 * @returns pszFilename
2348 * @param pszFilename Pointer to filename string. Not empty string!
2349 * Much space to play with.
2350 * @param pszBuffer Pointer to output buffer.
2351 */
2352char *fileNormalize2(const char *pszFilename, char *pszBuffer)
2353{
2354 char * psz = pszBuffer;
2355 int iSlash;
2356
2357 if (pszFilename[1] != ':')
2358 {
2359 /* iSlash */
2360 if (*pszFilename == '\\' || *pszFilename == '/')
2361 iSlash = 1;
2362 else
2363 iSlash = cSlashes;
2364
2365 /* interpret . and .. */
2366 while (*pszFilename != '\0')
2367 {
2368 if (*pszFilename == '.' && pszFilename[1] == '.' && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
2369 { /* up one directory */
2370 if (iSlash > 0)
2371 iSlash--;
2372 pszFilename += 3;
2373 }
2374 else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
2375 { /* no change */
2376 pszFilename += 2;
2377 }
2378 else
2379 { /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
2380 strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
2381 strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
2382 break;
2383 }
2384 }
2385 }
2386 else
2387 { /* have drive letter specified - assume ok (TODO)*/
2388 strcpy(pszBuffer, pszFilename);
2389 }
2390
2391 /* correct slashes */
2392 while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
2393 *pszBuffer++ = '\\';
2394
2395 /* lower case it */
2396 /*strlwr(psz);*/
2397
2398 return psz;
2399}
2400
2401
2402/**
2403 * Copies the path part (excluding the slash) into pszBuffer and returns
2404 * a pointer to the buffer.
2405 * If no path is found "" is returned.
2406 * @returns Pointer to pszBuffer with path.
2407 * @param pszFilename Pointer to readonly filename.
2408 * @param pszBuffer Pointer to output Buffer.
2409 * @status completely implemented.
2410 * @author knut st. osmundsen
2411 */
2412char *filePath(const char *pszFilename, char *pszBuffer)
2413{
2414 char *psz = strrchr(pszFilename, '\\');
2415 if (psz == NULL)
2416 psz = strrchr(pszFilename, '/');
2417
2418 if (psz == NULL)
2419 *pszBuffer = '\0';
2420 else
2421 {
2422 strncpy(pszBuffer, pszFilename, psz - pszFilename);
2423 pszBuffer[psz - pszFilename] = '\0';
2424 }
2425
2426 return pszBuffer;
2427}
2428
2429
2430/**
2431 * Copies the path part including the slash into pszBuffer and returns
2432 * a pointer to the buffer.
2433 * If no path is found "" is returned.
2434 * @returns Pointer to pszBuffer with path.
2435 * @param pszFilename Pointer to readonly filename.
2436 * @param pszBuffer Pointer to output Buffer.
2437 * @status completely implemented.
2438 * @author knut st. osmundsen
2439 */
2440char *filePathSlash(const char *pszFilename, char *pszBuffer)
2441{
2442 char *psz = strrchr(pszFilename, '\\');
2443 if (psz == NULL)
2444 psz = strrchr(pszFilename, '/');
2445
2446 if (psz == NULL)
2447 *pszBuffer = '\0';
2448 else
2449 {
2450 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2451 pszBuffer[psz - pszFilename + 1] = '\0';
2452 }
2453
2454 return pszBuffer;
2455}
2456
2457
2458/**
2459 * Copies the path part including the slash into pszBuffer and returns
2460 * a pointer to the buffer. If no path is found "" is returned.
2461 * The path is normalized to only use '\\'.
2462 * @returns Pointer to pszBuffer with path.
2463 * @param pszFilename Pointer to readonly filename.
2464 * @param pszBuffer Pointer to output Buffer.
2465 * @status completely implemented.
2466 * @author knut st. osmundsen
2467 */
2468char *filePathSlash2(const char *pszFilename, char *pszBuffer)
2469{
2470 char *psz = strrchr(pszFilename, '\\');
2471 if (psz == NULL)
2472 psz = strrchr(pszFilename, '/');
2473
2474 if (psz == NULL)
2475 *pszBuffer = '\0';
2476 else
2477 {
2478 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2479 pszBuffer[psz - pszFilename + 1] = '\0';
2480
2481 /* normalize all '/' to '\\' */
2482 psz = pszBuffer;
2483 while ((psz = strchr(psz, '/')) != NULL)
2484 *psz++ = '\\';
2485 }
2486
2487 return pszBuffer;
2488}
2489
2490
2491/**
2492 * Copies the filename (with extention) into pszBuffer and returns
2493 * a pointer to the buffer.
2494 * @returns Pointer to pszBuffer with path.
2495 * @param pszFilename Pointer to readonly filename.
2496 * @param pszBuffer Pointer to output Buffer.
2497 * @status completely implemented.
2498 * @author knut st. osmundsen
2499 */
2500char *fileName(const char *pszFilename, char *pszBuffer)
2501{
2502 char *psz = strrchr(pszFilename, '\\');
2503 if (psz == NULL)
2504 psz = strrchr(pszFilename, '/');
2505
2506 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2507
2508 return pszBuffer;
2509}
2510
2511
2512/**
2513 * Copies the name part with out extention into pszBuffer and returns
2514 * a pointer to the buffer.
2515 * If no name is found "" is returned.
2516 * @returns Pointer to pszBuffer with path.
2517 * @param pszFilename Pointer to readonly filename.
2518 * @param pszBuffer Pointer to output Buffer.
2519 * @status completely implemented.
2520 * @author knut st. osmundsen
2521 */
2522char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
2523{
2524 char *psz = strrchr(pszFilename, '\\');
2525 if (psz == NULL)
2526 psz = strrchr(pszFilename, '/');
2527
2528 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2529
2530 psz = strrchr(pszBuffer, '.');
2531 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
2532 *psz = '\0';
2533
2534 return pszBuffer;
2535}
2536
2537
2538/**
2539 * Copies the extention part into pszBuffer and returns
2540 * a pointer to the buffer.
2541 * If no extention is found "" is returned.
2542 * The dot ('.') is not included!
2543 * @returns Pointer to pszBuffer with path.
2544 * @param pszFilename Pointer to readonly filename.
2545 * @param pszBuffer Pointer to output Buffer.
2546 * @status completely implemented.
2547 * @author knut st. osmundsen
2548 */
2549char *fileExt(const char *pszFilename, char *pszBuffer)
2550{
2551 char *psz = strrchr(pszFilename, '.');
2552 if (psz != NULL)
2553 {
2554 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
2555 *pszBuffer = '\0';
2556 else
2557 strcpy(pszBuffer, psz + 1);
2558 }
2559 else
2560 *pszBuffer = '\0';
2561
2562 return pszBuffer;
2563}
2564
2565
2566/**
2567 * Adds a file to the cache.
2568 * @returns Success indicator.
2569 * @param pszFilename Name of the file which is to be added. (with path!)
2570 */
2571BOOL filecacheAddFile(const char *pszFilename)
2572{
2573 PFCACHEENTRY pfcNew;
2574
2575 /* allocate new block and fill in data */
2576 pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
2577 if (pfcNew == NULL)
2578 {
2579 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2580 return FALSE;
2581 }
2582 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2583 strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
2584 if (!AVLInsert(&pfcTree, pfcNew))
2585 {
2586 free(pfcNew);
2587 return TRUE;
2588 }
2589 cfcNodes++;
2590
2591 return TRUE;
2592}
2593
2594
2595
2596/**
2597 * Adds a file to the cache.
2598 * @returns Success indicator.
2599 * @param pszDir Name of the path which is to be added. (with slash!)
2600 */
2601BOOL filecacheAddDir(const char *pszDir)
2602{
2603 PFCACHEENTRY pfcNew;
2604 APIRET rc;
2605 char szDir[CCHMAXPATH];
2606 int cchDir;
2607 char achBuffer[32768];
2608 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2609 HDIR hDir = HDIR_CREATE;
2610 ULONG cFiles = 0xFFFFFFF;
2611 int i;
2612
2613 /* Make path */
2614 filePathSlash2(pszDir, szDir);
2615 //strlwr(szDir); /* Convert name to lower case to allow faster searchs! */
2616 cchDir = strlen(szDir);
2617
2618
2619 /* Add directory to pfcDirTree. */
2620 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
2621 if (pfcNew == NULL)
2622 {
2623 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2624 DosFindClose(hDir);
2625 return FALSE;
2626 }
2627 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2628 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2629 AVLInsert(&pfcDirTree, pfcNew);
2630
2631
2632 /* Start to search directory - all files */
2633 strcat(szDir + cchDir, "*");
2634 rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
2635 pfindbuf3, sizeof(achBuffer),
2636 &cFiles, FIL_STANDARD);
2637 while (rc == NO_ERROR)
2638 {
2639 for (i = 0;
2640 i < cFiles;
2641 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
2642 )
2643 {
2644 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
2645 if (pfcNew == NULL)
2646 {
2647 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2648 DosFindClose(hDir);
2649 return FALSE;
2650 }
2651 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2652 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2653 strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
2654 strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
2655 if (!AVLInsert(&pfcTree, pfcNew))
2656 free(pfcNew);
2657 else
2658 cfcNodes++;
2659 }
2660
2661 /* next */
2662 cFiles = 0xFFFFFFF;
2663 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2664 rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
2665 }
2666
2667 DosFindClose(hDir);
2668
2669 return TRUE;
2670}
2671
2672
2673/**
2674 * Checks if pszFilename is exists in the cache.
2675 * @return TRUE if found. FALSE if not found.
2676 * @param pszFilename Name of the file to be found. (with path!)
2677 * This is in lower case!
2678 */
2679INLINE BOOL filecacheFind(const char *pszFilename)
2680{
2681 return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
2682}
2683
2684
2685/**
2686 * Checks if pszFilename is exists in the cache.
2687 * @return TRUE if found. FALSE if not found.
2688 * @param pszFilename Name of the file to be found. (with path!)
2689 * This is in lower case!
2690 */
2691INLINE BOOL filecacheIsDirCached(const char *pszDir)
2692{
2693 return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
2694}
2695
2696
2697/**
2698 * Checks if a file exist, uses file cache if possible.
2699 * @returns Pointer to a filename consiting of the path part + the given filename.
2700 * (pointer into pszBuffer)
2701 * NULL if file is not found. ("" in buffer)
2702 * @parma pszFilename Filename to find.
2703 * @parma pszBuffer Ouput Buffer.
2704 * @status completely implemented.
2705 * @author knut st. osmundsen
2706 */
2707char *filecacheFileExist(const char *pszFilename, char *pszBuffer)
2708{
2709 APIRET rc;
2710
2711 *pszBuffer = '\0';
2712
2713 fileNormalize2(pszFilename, pszBuffer);
2714
2715 /*
2716 * Search for the file in this directory.
2717 * Search cache first
2718 */
2719 if (!filecacheFind(pszBuffer))
2720 {
2721 char szDir[CCHMAXPATH];
2722
2723 filePathSlash(pszBuffer, szDir);
2724 if (!filecacheIsDirCached(szDir))
2725 {
2726 /*
2727 * If caching of entire dirs are enabled, we'll
2728 * add the directory to the cache and search it.
2729 */
2730 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
2731 {
2732 if (filecacheFind(pszBuffer))
2733 return pszBuffer;
2734 }
2735 else
2736 {
2737 FILESTATUS3 fsts3;
2738
2739 /* ask the OS */
2740 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2741 if (rc == NO_ERROR)
2742 { /* add file to cache. */
2743 filecacheAddFile(pszBuffer);
2744 return pszBuffer;
2745 }
2746 }
2747 }
2748 }
2749 else
2750 return pszBuffer;
2751
2752 return NULL;
2753}
2754
2755
2756/**
2757 * Finds a filename in a specified pathlist.
2758 * @returns Pointer to a filename consiting of the path part + the given filename.
2759 * (pointer into pszBuffer)
2760 * NULL if file is not found. ("" in buffer)
2761 * @param pszPathList Path list to search for filename.
2762 * @parma pszFilename Filename to find.
2763 * @parma pszBuffer Ouput Buffer.
2764 * @status completely implemented.
2765 * @author knut st. osmundsen
2766 */
2767char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer)
2768{
2769 const char *psz = pszPathList;
2770 const char *pszNext = NULL;
2771
2772 *pszBuffer = '\0';
2773
2774 if (pszPathList == NULL)
2775 return NULL;
2776
2777 while (*psz != '\0')
2778 {
2779 /* find end of this path */
2780 pszNext = strchr(psz, ';');
2781 if (pszNext == NULL)
2782 pszNext = psz + strlen(psz);
2783
2784 if (pszNext - psz > 0)
2785 {
2786 APIRET rc;
2787
2788 /* make search statment */
2789 strncpy(pszBuffer, psz, pszNext - psz);
2790 pszBuffer[pszNext - psz] = '\0';
2791 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
2792 strcpy(&pszBuffer[pszNext - psz], "\\");
2793 strcat(pszBuffer, pszFilename);
2794 fileNormalize(pszBuffer);
2795
2796 /*
2797 * Search for the file in this directory.
2798 * Search cache first
2799 */
2800 if (!filecacheFind(pszBuffer))
2801 {
2802 char szDir[CCHMAXPATH];
2803
2804 filePathSlash(pszBuffer, szDir);
2805 if (!filecacheIsDirCached(szDir))
2806 {
2807 /*
2808 * If caching of entire dirs are enabled, we'll
2809 * add the directory to the cache and search it.
2810 */
2811 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
2812 {
2813 if (filecacheFind(pszBuffer))
2814 return pszBuffer;
2815 }
2816 else
2817 {
2818 FILESTATUS3 fsts3;
2819
2820 /* ask the OS */
2821 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2822 if (rc == NO_ERROR)
2823 { /* add file to cache. */
2824 filecacheAddFile(pszBuffer);
2825 return pszBuffer;
2826 }
2827 }
2828 }
2829 }
2830 else
2831 return pszBuffer;
2832 }
2833
2834 /* next */
2835 if (*pszNext != ';')
2836 break;
2837 psz = pszNext + 1;
2838 }
2839
2840 return NULL;
2841}
2842
2843
2844
2845/**
2846 * Checks if the given filename may exist within any of the given paths.
2847 * This check only matches the filename path agianst the paths in the pathlist.
2848 * @returns TRUE: if exists.
2849 * FALSE: don't exist.
2850 * @param pszPathList Path list to search for filename.
2851 * @parma pszFilename Filename to find. The filename should be normalized!
2852 * @status completely implemented.
2853 * @author knut st. osmundsen
2854 */
2855BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename)
2856{
2857 const char *psz = pszPathList;
2858 const char *pszNext = NULL;
2859 char szBuffer[CCHMAXPATH];
2860 char szBuffer2[CCHMAXPATH];
2861 char *pszPathToFind = &szBuffer2[0];
2862
2863 /*
2864 * Input checking
2865 */
2866 if (pszPathList == NULL)
2867 return FALSE;
2868
2869 /*
2870 * Normalize the filename and get it's path.
2871 */
2872 filePath(pszFilename, pszPathToFind);
2873
2874
2875 /*
2876 * Loop thru the path list.
2877 */
2878 while (*psz != '\0')
2879 {
2880 /* find end of this path */
2881 pszNext = strchr(psz, ';');
2882 if (pszNext == NULL)
2883 pszNext = psz + strlen(psz);
2884
2885 if (pszNext - psz > 0)
2886 {
2887 char * pszPath = &szBuffer[0];
2888
2889 /*
2890 * Extract and normalize the path
2891 */
2892 strncpy(pszPath, psz, pszNext - psz);
2893 pszPath[pszNext - psz] = '\0';
2894 if (pszPath[pszNext - psz - 1] == '\\' && pszPath[pszNext - psz - 1] == '/')
2895 pszPath[pszNext - psz - 1] = '\0';
2896 fileNormalize(pszPath);
2897
2898 /*
2899 * Check if it matches the path of the filename
2900 */
2901 if (strcmp(pszPath, pszPathToFind) == 0)
2902 return TRUE;
2903 }
2904
2905 /*
2906 * Next part of the path list.
2907 */
2908 if (*pszNext != ';')
2909 break;
2910 psz = pszNext + 1;
2911 }
2912
2913 return FALSE;
2914}
2915
2916
2917/**
2918 * Finds the first char after word.
2919 * @returns Pointer to the first char after word.
2920 * @param psz Where to start.
2921 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2922 */
2923char *findEndOfWord(char *psz)
2924{
2925
2926 while (*psz != '\0' &&
2927 (
2928 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
2929 ||
2930 (*psz >= '0' && *psz <= '9')
2931 ||
2932 *psz == '_'
2933 )
2934 )
2935 ++psz;
2936 return (char *)psz;
2937}
2938
2939#if 0 /* not used */
2940/**
2941 * Find the starting char of a word
2942 * @returns Pointer to first char in word.
2943 * @param psz Where to start.
2944 * @param pszStart Where to stop.
2945 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2946 */
2947char *findStartOfWord(const char *psz, const char *pszStart)
2948{
2949 const char *pszR = psz;
2950 while (psz >= pszStart &&
2951 (
2952 (*psz >= 'A' && *psz <= 'Z')
2953 || (*psz >= 'a' && *psz <= 'z')
2954 || (*psz >= '0' && *psz <= '9')
2955 || *psz == '_'
2956 )
2957 )
2958 pszR = psz--;
2959 return (char*)pszR;
2960}
2961#endif
2962
2963/**
2964 * Find the size of a file.
2965 * @returns Size of file. -1 on error.
2966 * @param phFile File handle.
2967 */
2968signed long fsize(FILE *phFile)
2969{
2970 int ipos;
2971 signed long cb;
2972
2973 if ((ipos = ftell(phFile)) < 0
2974 ||
2975 fseek(phFile, 0, SEEK_END) != 0
2976 ||
2977 (cb = ftell(phFile)) < 0
2978 ||
2979 fseek(phFile, ipos, SEEK_SET) != 0
2980 )
2981 cb = -1;
2982 return cb;
2983}
2984
2985
2986
2987/**
2988 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2989 * @returns Pointer to first not space or tab char in the string.
2990 * @param psz Pointer to the string which is to be trimmed.
2991 * @status completely implmented.
2992 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2993 */
2994INLINE char *trim(char *psz)
2995{
2996 int i;
2997 if (psz == NULL)
2998 return NULL;
2999 while (*psz == ' ' || *psz == '\t')
3000 psz++;
3001 i = strlen(psz) - 1;
3002 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
3003 i--;
3004 psz[i+1] = '\0';
3005 return psz;
3006}
3007
3008
3009/**
3010 * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
3011 * @returns Pointer to the string passed in.
3012 * @param psz Pointer to the string which is to be right trimmed.
3013 * @status completely implmented.
3014 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
3015 */
3016INLINE char *trimR(char *psz)
3017{
3018 int i;
3019 if (psz == NULL)
3020 return NULL;
3021 i = strlen(psz) - 1;
3022 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
3023 i--;
3024 psz[i+1] = '\0';
3025 return psz;
3026}
3027
3028
3029/**
3030 * Trims any quotes of a possibly quoted string.
3031 * @returns Pointer to the string passed in.
3032 * @param psz Pointer to the string which is to be quote-trimmed.
3033 * @status completely implmented.
3034 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
3035 */
3036INLINE char *trimQuotes(char *psz)
3037{
3038 int i;
3039 if (psz == NULL)
3040 return NULL;
3041
3042 if (*psz == '\"' || *psz == '\'')
3043 psz++;
3044 i = strlen(psz) - 1;
3045 if (psz[i] == '\"' || psz[i] == '\'')
3046 psz[i] = '\0';
3047
3048 return psz;
3049}
3050
3051
3052/**
3053 * C/C++ preprocess a single line. Assumes that we're not starting
3054 * with at comment.
3055 * @returns Pointer to output buffer.
3056 * @param pszOut Ouput (preprocessed) string.
3057 * @param pszIn Input string.
3058 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
3059 */
3060char *PreProcessLine(char *pszOut, const char *pszIn)
3061{
3062 char * psz = pszOut;
3063 BOOL fComment = FALSE;
3064 BOOL fQuote = FALSE;
3065
3066 /*
3067 * Loop thru the string.
3068 */
3069 while (*pszIn != '\0')
3070 {
3071 if (fQuote)
3072 {
3073 *psz++ = *pszIn;
3074 if (*pszIn == '\\')
3075 {
3076 *psz++ = *++pszIn;
3077 pszIn++;
3078 }
3079 else if (*pszIn++ == '"')
3080 fQuote = FALSE;
3081 }
3082 else if (fComment)
3083 {
3084 if (*pszIn == '*' && pszIn[1] == '/')
3085 {
3086 fComment = FALSE;
3087 pszIn += 2;
3088 }
3089 else
3090 pszIn++;
3091 }
3092 else
3093 {
3094 if ( (*pszIn == '/' && pszIn[1] == '/')
3095 || *pszIn == '\0')
3096 { /* End of line. */
3097 break;
3098 }
3099
3100 if (*pszIn == '/' && pszIn[1] == '*')
3101 { /* Start comment */
3102 fComment = TRUE;
3103 pszIn += 2;
3104 }
3105 else
3106 *psz++ = *pszIn++;
3107 }
3108 }
3109
3110 /*
3111 * Trim right.
3112 */
3113 psz--;
3114 while (psz >= pszOut && (*psz == ' ' || *psz == '\t'))
3115 psz--;
3116 psz[1] = '\0';
3117
3118 return pszOut;
3119}
3120
3121
3122/**
3123 * Creates a memory buffer for a text file.
3124 * @returns Pointer to file memoryblock. NULL on error.
3125 * @param pszFilename Pointer to filename string.
3126 * @remark This function is the one using most of the execution
3127 * time (DosRead + DosOpen) - about 70% of the execution time!
3128 */
3129void *textbufferCreate(const char *pszFilename)
3130{
3131 void *pvFile = NULL;
3132 FILE *phFile;
3133
3134 phFile = fopen(pszFilename, "rb");
3135 if (phFile != NULL)
3136 {
3137 signed long cbFile = fsize(phFile);
3138 if (cbFile >= 0)
3139 {
3140 pvFile = malloc(cbFile + 1);
3141 if (pvFile != NULL)
3142 {
3143 memset(pvFile, 0, cbFile + 1);
3144 if (cbFile > 0 && fread(pvFile, 1, cbFile, phFile) == 0)
3145 { /* failed! */
3146 free(pvFile);
3147 pvFile = NULL;
3148 }
3149 }
3150 else
3151 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
3152 }
3153 fclose(phFile);
3154 }
3155 return pvFile;
3156}
3157
3158
3159/**
3160 * Destroys a text textbuffer.
3161 * @param pvBuffer Buffer handle.
3162 */
3163void textbufferDestroy(void *pvBuffer)
3164{
3165 free(pvBuffer);
3166}
3167
3168
3169/**
3170 * Gets the next line from an textbuffer.
3171 * @returns Pointer to the next line.
3172 * @param pvBuffer Buffer handle.
3173 * @param psz Pointer to current line.
3174 * NULL is passed in to get the first line.
3175 */
3176char *textbufferNextLine(void *pvBuffer, register char *psz)
3177{
3178 register char ch;
3179
3180 /* if first line psz is NULL. */
3181 if (psz == NULL)
3182 return (char*)pvBuffer;
3183
3184 /* skip till end of file or end of line. */
3185 ch = *psz;
3186 while (ch != '\0' && ch != '\n' && ch != '\r')
3187 ch = *++psz;
3188
3189 /* skip line end */
3190 if (ch == '\r')
3191 ch = *++psz;
3192 if (ch == '\n')
3193 psz++;
3194
3195 return psz;
3196}
3197
3198
3199/**
3200 * Gets the next line from an textbuffer.
3201 * (fgets for textbuffer)
3202 * @returns Pointer to pszOutBuffer. NULL when end of file.
3203 * @param pvBuffer Buffer handle.
3204 * @param ppv Pointer to a buffer index pointer. (holds the current buffer index)
3205 * Pointer to a null pointer is passed in to get the first line.
3206 * @param pszLineBuffer Output line buffer. (!= NULL)
3207 * @param cchLineBuffer Size of the output line buffer. (> 0)
3208 * @remark '\n' and '\r' are removed!
3209 */
3210char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
3211{
3212 char * pszLine = pszLineBuffer;
3213 char * psz = *(char**)(void*)ppv;
3214 register char ch;
3215
3216 /* first line? */
3217 if (psz == NULL)
3218 psz = pvBuffer;
3219
3220 /* Copy to end of the line or end of the linebuffer. */
3221 ch = *psz;
3222 cchLineBuffer--; /* reserve space for '\0' */
3223 while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
3224 {
3225 *pszLine++ = ch;
3226 ch = *++psz;
3227 }
3228 *pszLine = '\0';
3229
3230 /* skip line end */
3231 if (ch == '\r')
3232 ch = *++psz;
3233 if (ch == '\n')
3234 psz++;
3235
3236 /* check if position has changed - if unchanged it's the end of file! */
3237 if (*ppv == (void*)psz)
3238 pszLineBuffer = NULL;
3239
3240 /* store current position */
3241 *ppv = (void*)psz;
3242
3243 return pszLineBuffer;
3244}
3245
3246
3247/**
3248 * Appends a depend file to the internal file.
3249 * This will update the date in the option struct.
3250 */
3251BOOL depReadFile(const char *pszFilename, BOOL fAppend)
3252{
3253 void * pvFile;
3254 char * pszNext;
3255 char * pszPrev; /* Previous line, only valid when finding new rule. */
3256 BOOL fMoreDeps = FALSE;
3257 void * pvRule = NULL;
3258
3259
3260 /* read depend file */
3261 pvFile = textbufferCreate(pszFilename);
3262 if (pvFile == NULL)
3263 return FALSE;
3264
3265 /* parse the original depend file */
3266 pszPrev = NULL;
3267 pszNext = pvFile;
3268 while (*pszNext != '\0')
3269 {
3270 int i;
3271 int cch;
3272 char *psz;
3273
3274 /* get the next line. */
3275 psz = pszNext;
3276 pszNext = textbufferNextLine(pvFile, pszNext);
3277
3278 /*
3279 * Process the current line:
3280 * Start off by terminating the line.
3281 * Trim the line,
3282 * Skip empty lines.
3283 * If not looking for more deps Then
3284 * Check if new rule starts here.
3285 * Endif
3286 *
3287 * If more deps to last rule Then
3288 * Get dependant name.
3289 * Endif
3290 */
3291 i = -1;
3292 while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
3293 pszNext[i--] = '\0';
3294 trimR(psz);
3295 cch = strlen(psz);
3296 if (cch == 0)
3297 {
3298 fMoreDeps = FALSE;
3299 continue;
3300 }
3301
3302 if (*psz == '#')
3303 {
3304 pszPrev = psz;
3305 continue;
3306 }
3307
3308 /* new rule? */
3309 if (!fMoreDeps)
3310 {
3311 if (*psz != ' ' && *psz != '\t' && *psz != '\0')
3312 {
3313 i = 0;
3314 while (psz[i] != '\0')
3315 {
3316 if (psz[i] == ':'
3317 && (psz[i+1] == ' '
3318 || psz[i+1] == '\t'
3319 || psz[i+1] == '\0'
3320 || (psz[i+1] == '\\' && psz[i+2] == '\0')
3321 )
3322 )
3323 {
3324 char szTS[TS_SIZE];
3325 char * pszCont = strchr(&psz[i], '\\');
3326 fMoreDeps = pszCont != NULL && pszCont[1] == '\0';
3327
3328 /* read evt. timestamp. */
3329 szTS[0] = '\0';
3330 if (pszPrev && strlen(pszPrev) > 25 && *pszPrev == '#')
3331 strcpy(szTS, pszPrev + 2);
3332
3333 psz[i] = '\0';
3334 pvRule = depAddRule(trimQuotes(trimR(psz)), NULL, NULL, szTS);
3335 if (pvRule)
3336 ((PDEPRULE)pvRule)->fUpdated = fAppend;
3337 psz += i + 1;
3338 cch -= i + 1;
3339 break;
3340 }
3341 i++;
3342 }
3343 }
3344 pszPrev = NULL;
3345 }
3346
3347
3348 /* more dependants */
3349 if (fMoreDeps)
3350 {
3351 if (cch > 0 && psz[cch-1] == '\\')
3352 {
3353 fMoreDeps = TRUE;
3354 psz[cch-1] = '\0';
3355 }
3356 else
3357 fMoreDeps = FALSE;
3358
3359 /* if not duplicate rule */
3360 if (pvRule != NULL)
3361 {
3362 psz = trimQuotes(trim(psz));
3363 if (*psz != '\0')
3364 depAddDepend(pvRule, psz, options.fCheckCyclic);
3365 }
3366 }
3367 } /* while */
3368
3369
3370 /* return succesfully */
3371 textbufferDestroy(pvFile);
3372 return TRUE;
3373}
3374
3375/**
3376 *
3377 * @returns Success indicator.
3378 * @param pszFilename Pointer to name of the output file.
3379 * @param fWriteUpdatedOnly If set we'll only write updated rules.
3380 */
3381BOOL depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly)
3382{
3383 FILE *phFile;
3384 phFile = fopen(pszFilename, "w");
3385 if (phFile != NULL)
3386 {
3387 AVLENUMDATA EnumData;
3388 PDEPRULE pdep;
3389 char szBuffer[32678];
3390 int iBuffer = 0;
3391 int cch;
3392
3393 /*
3394 * Write warning on top of file.
3395 */
3396 fputs("#\n"
3397 "# This file was automatically generated by FastDep.\n"
3398 "# FastDep was written by knut st. osmundsen, and it's GPL software.\n"
3399 "#\n"
3400 "# THIS FILE SHOULD N O T BE EDITED MANUALLY!!!\n"
3401 "#\n"
3402 "# (As this may possibly make it unreadable for fastdep\n"
3403 "# and ruin the caching methods of FastDep.)\n"
3404 "#\n"
3405 "\n",
3406 phFile);
3407
3408
3409 /* @@@PH 2001-03-01
3410 * If option is selected to generate a parent
3411 * "super" dependency, enter this scope.
3412 */
3413 if (options.pszSuperDependency != NULL)
3414 {
3415 iBuffer = sprintf(szBuffer,
3416 strrchr(options.pszSuperDependency, '=') ? "%s" : "%s:",
3417 options.pszSuperDependency);
3418
3419 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3420 while (pdep != NULL)
3421 {
3422 if (!fWriteUpdatedOnly || pdep->fUpdated)
3423 {
3424 char *psz = pdep->pszRule;
3425
3426 /* flush buffer? */
3427 if (iBuffer + strlen(psz) + 20 >= sizeof(szBuffer))
3428 {
3429 fwrite(szBuffer, iBuffer, 1, phFile);
3430 iBuffer = 0;
3431 }
3432
3433 /* write rule title as dependant */
3434 iBuffer += sprintf(szBuffer + iBuffer,
3435 " \\\n %s",psz);
3436 }
3437
3438 /* next rule */
3439 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3440 }
3441
3442 /* Add two new lines. Flush buffer first if necessary. */
3443 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3444 {
3445 fwrite(szBuffer, iBuffer, 1, phFile);
3446 iBuffer = 0;
3447 }
3448
3449 /* add 2 linefeeds */
3450 strcpy(szBuffer + iBuffer, "\n\n");
3451 iBuffer += CBNEWLINE*2;
3452 }
3453
3454
3455 /* normal dependency output */
3456 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3457 while (pdep != NULL)
3458 {
3459 if (!fWriteUpdatedOnly || pdep->fUpdated)
3460 {
3461 int cchTS = strlen(pdep->szTS);
3462 int fQuoted = strpbrk(pdep->pszRule, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3463
3464 /* Write rule. Flush the buffer first if necessary. */
3465 cch = strlen(pdep->pszRule);
3466 if (iBuffer + cch + fQuoted * 2 + cchTS + 9 >= sizeof(szBuffer))
3467 {
3468 fwrite(szBuffer, iBuffer, 1, phFile);
3469 iBuffer = 0;
3470 }
3471
3472 memcpy(szBuffer + iBuffer, "# ", 2);
3473 memcpy(szBuffer + iBuffer + 2, pdep->szTS, cchTS);
3474 iBuffer += cchTS + 2;
3475 szBuffer[iBuffer++] = '\n';
3476
3477 if (fQuoted) szBuffer[iBuffer++] = '"';
3478 strcpy(szBuffer + iBuffer, pdep->pszRule);
3479 iBuffer += cch;
3480 if (fQuoted) szBuffer[iBuffer++] = '"';
3481 strcpy(szBuffer + iBuffer++, ":");
3482
3483 /* write rule dependants. */
3484 if (pdep->papszDep != NULL)
3485 {
3486 char **ppsz = pdep->papszDep;
3487 while (*ppsz != NULL)
3488 {
3489 /* flush buffer? */
3490 fQuoted = strpbrk(*ppsz, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3491 cch = strlen(*ppsz);
3492 if (iBuffer + cch + fQuoted * 2 + 20 >= sizeof(szBuffer))
3493 {
3494 fwrite(szBuffer, iBuffer, 1, phFile);
3495 iBuffer = 0;
3496 }
3497 strcpy(szBuffer + iBuffer, " \\\n ");
3498 iBuffer += 7;
3499 if (fQuoted) szBuffer[iBuffer++] = '"';
3500 strcpy(szBuffer + iBuffer, *ppsz);
3501 iBuffer += cch;
3502 if (fQuoted) szBuffer[iBuffer++] = '"';
3503
3504 /* next dependant */
3505 ppsz++;
3506 }
3507 }
3508
3509 /* Add two new lines. Flush buffer first if necessary. */
3510 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3511 {
3512 fwrite(szBuffer, iBuffer, 1, phFile);
3513 iBuffer = 0;
3514 }
3515
3516 /* add 2 linefeeds */
3517 strcpy(szBuffer + iBuffer, "\n\n");
3518 iBuffer += CBNEWLINE*2;
3519 }
3520
3521 /* next rule */
3522 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3523 }
3524
3525
3526 /* flush buffer. */
3527 fwrite(szBuffer, iBuffer, 1, phFile);
3528
3529 fclose(phFile);
3530 return TRUE;
3531 }
3532
3533 return FALSE;
3534}
3535
3536
3537/**
3538 * Removes all nodes in the tree of dependencies. (pdepTree)
3539 */
3540void depRemoveAll(void)
3541{
3542 AVLENUMDATA EnumData;
3543 PDEPRULE pdep;
3544
3545 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3546 while (pdep != NULL)
3547 {
3548 /* free this */
3549 if (pdep->papszDep != NULL)
3550 {
3551 char ** ppsz = pdep->papszDep;
3552 while (*ppsz != NULL)
3553 free(*ppsz++);
3554 free(pdep->papszDep);
3555 }
3556 free(pdep);
3557
3558 /* next */
3559 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3560 }
3561 pdepTree = NULL;
3562}
3563
3564
3565/**
3566 * Adds a rule to the list of dependant rules.
3567 * @returns Rule handle. NULL if rule exists/error.
3568 * @param pszRulePath Pointer to rule text. Empty strings are banned!
3569 * This string might only contain the path of the rule. (with '\\')
3570 * @param pszName Name of the rule.
3571 * NULL if pszRulePath contains the entire rule.
3572 * @param pszExt Extention (without '.')
3573 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
3574 */
3575void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS)
3576{
3577 char szRule[CCHMAXPATH*2];
3578 PDEPRULE pNew;
3579 int cch;
3580
3581 /* make rulename */
3582 strcpy(szRule, pszRulePath);
3583 cch = strlen(szRule);
3584 if (pszName != NULL)
3585 {
3586 strcpy(szRule + cch, pszName);
3587 cch += strlen(szRule + cch);
3588 }
3589 if (pszExt != NULL)
3590 {
3591 strcat(szRule + cch++, ".");
3592 strcat(szRule + cch, pszExt);
3593 cch += strlen(szRule + cch);
3594 }
3595
3596
3597 /*
3598 * Allocate a new rule structure and fill in data
3599 * Note. One block for both the DEPRULE and the pszRule string.
3600 */
3601 pNew = malloc(sizeof(DEPRULE) + cch + 1);
3602 if (pNew == NULL)
3603 {
3604 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
3605 return NULL;
3606 }
3607 pNew->pszRule = (char*)(void*)(pNew + 1);
3608 strcpy(pNew->pszRule, szRule);
3609 pNew->cDeps = 0;
3610 pNew->papszDep = NULL;
3611 pNew->fUpdated = TRUE;
3612 pNew->avlCore.Key = pNew->pszRule;
3613 strcpy(pNew->szTS, pszTS);
3614
3615 /* Insert the rule */
3616 if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
3617 { /*
3618 * The rule existed.
3619 * If it's allready touched (updated) during this session
3620 * there is nothing to be done.
3621 * If not force scan and it's newer than depfile-1month then
3622 * we'll use the information we've got.
3623 * Reuse the node in the tree.
3624 */
3625 PDEPRULE pOld = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pNew->avlCore.Key);
3626 assert(pOld);
3627 free(pNew);
3628 if (pOld->fUpdated)
3629 return NULL;
3630
3631 pOld->fUpdated = TRUE;
3632 if (!options.fForceScan && !strcmp(pOld->szTS, pszTS) && depValidate(pOld))
3633 return NULL;
3634 strcpy(pOld->szTS, pszTS);
3635
3636 if (pOld->papszDep)
3637 {
3638 free(pOld->papszDep);
3639 pOld->papszDep = NULL;
3640 }
3641 pOld->cDeps = 0;
3642
3643 return pOld;
3644 }
3645
3646 return pNew;
3647}
3648
3649
3650
3651/**
3652 * Adds a dependant to a rule.
3653 * @returns Successindicator. TRUE = success.
3654 * FALSE = cyclic or out of memory.
3655 * @param pvRule Rule handle.
3656 * @param pszDep Pointer to dependant name
3657 * @param fCheckCyclic When set we'll check that we're not creating an cyclic dependency.
3658 */
3659BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic)
3660{
3661 PDEPRULE pdep = (PDEPRULE)pvRule;
3662
3663 if (pszDep[0] == '\0')
3664 {
3665 fprintf(stderr, "warning-internal: empty dependancy filename to '%s'. Ignored.\n",
3666 pdep->pszRule);
3667 /* __interrupt(3); */
3668 return FALSE;
3669 }
3670
3671 if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
3672 {
3673 fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
3674 pszDep, pdep->pszRule);
3675 return FALSE;
3676 }
3677
3678 /* allocate more array space */
3679 if (((pdep->cDeps) % 48) == 0)
3680 {
3681 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
3682 if (pdep->papszDep == NULL)
3683 {
3684 pdep->cDeps = 0;
3685 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3686 return FALSE;
3687 }
3688 }
3689
3690 /* allocate string space and copy pszDep */
3691 if ((pdep->papszDep[pdep->cDeps] = malloc(strlen(pszDep) + 1)) == NULL)
3692 {
3693 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3694 return FALSE;
3695 }
3696 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
3697
3698 /* terminate array and increment dep count */
3699 pdep->papszDep[++pdep->cDeps] = NULL;
3700
3701 /* successful! */
3702 return TRUE;
3703}
3704
3705
3706/**
3707 * Marks the file as one which is to be rescanned next time
3708 * since not all dependencies was found...
3709 * @param pvRule Rule handle...
3710 */
3711void depMarkNotFound(void *pvRule)
3712{
3713 ((PDEPRULE)pvRule)->szTS[0] = '\0';
3714}
3715
3716
3717/**
3718 * Checks if adding this dependent will create a cylic dependency.
3719 * @returns TRUE: Cyclic.
3720 * FALSE: Non-cylic.
3721 * @param pdepRule Rule pszDep is to be inserted in.
3722 * @param pszDep Depend name.
3723 */
3724BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
3725{
3726 #define DEPTH 32
3727 char * pszRule = pdepRule->pszRule;
3728 char ** appsz[DEPTH];
3729 PDEPRULE pdep;
3730 int i;
3731
3732 /* self check */
3733 if (strcmp(pdepRule->pszRule, pszDep) == 0)
3734 return TRUE;
3735
3736 /* find rule for the dep. */
3737 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
3738 || pdep->papszDep == NULL)
3739 return FALSE; /* no rule, or no dependents, not cyclic */
3740
3741 i = 0;
3742 appsz[0] = pdep->papszDep;
3743 while (i >= 0)
3744 {
3745 register char ** ppsz = appsz[i];
3746
3747 while (*ppsz != NULL)
3748 {
3749 /* check if equal to the main rule */
3750 if (strcmp(pszRule, *ppsz) == 0)
3751 return TRUE;
3752
3753 /* push onto stack (ppsz is incremented in this test!) */
3754 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
3755 && pdep->papszDep != NULL)
3756 {
3757 if (i >= DEPTH)
3758 {
3759 fprintf(stderr, "error: too deap chain (%d). pszRule=%s pszDep=%s\n",
3760 i, pszRule, pszDep);
3761 return FALSE;
3762 }
3763 appsz[i++] = ppsz; /* save next */
3764 ppsz = pdep->papszDep; /* start processing new node */
3765 }
3766 }
3767
3768 /* pop stack */
3769 i--;
3770 }
3771
3772 return FALSE;
3773}
3774
3775
3776/**
3777 * Validates that the dependencies for the file exists
3778 * in the given locations. Dependants without path is ignored.
3779 * @returns TRUE if all ok.
3780 * FALSE if one (or possibly more) dependants are non-existing.
3781 * @param pdepRule Pointer to rule we're to validate.
3782 */
3783BOOL depValidate(PDEPRULE pdepRule)
3784{
3785 int i;
3786
3787 for (i = 0; i < pdepRule->cDeps; i++)
3788 {
3789 char *psz = pdepRule->papszDep[i];
3790 if ( psz[1] == ':'
3791 || strchr(psz, '\\')
3792 || strchr(psz, '/')
3793 )
3794 {
3795 /*
3796 * Check existance of the file.
3797 * Search cache first
3798 */
3799 if (!filecacheFind(psz))
3800 {
3801 char szDir[CCHMAXPATH];
3802
3803 filePathSlash(psz, szDir);
3804 if (!filecacheIsDirCached(szDir))
3805 {
3806 /*
3807 * If caching of entire dirs are enabled, we'll
3808 * add the directory to the cache and search it.
3809 */
3810 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
3811 {
3812 if (!filecacheFind(psz))
3813 return FALSE;
3814 }
3815 else
3816 {
3817 FILESTATUS3 fsts3;
3818
3819 /* ask the OS */
3820 if (DosQueryPathInfo(psz, FIL_STANDARD, &fsts3, sizeof(fsts3)))
3821 return FALSE;
3822 /* add file to cache. */
3823 filecacheAddFile(psz);
3824 }
3825 }
3826 }
3827 }
3828 }
3829
3830 return TRUE;
3831}
3832
3833
3834/**
3835 * Make a timestamp from the file data provided thru the
3836 * search API.
3837 * @returns Pointer to pszTS
3838 * @param pszTS Pointer to timestamp (output).
3839 * @param pfindbuf3 Pointer to search result.
3840 */
3841INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3)
3842{
3843 sprintf(pszTS, "%04d-%02d-%02d-%02d.%02d.%02d 0x%04x%04x %d",
3844 pfindbuf3->fdateLastWrite.year + 1980,
3845 pfindbuf3->fdateLastWrite.month,
3846 pfindbuf3->fdateLastWrite.day,
3847 pfindbuf3->ftimeLastWrite.hours,
3848 pfindbuf3->ftimeLastWrite.minutes,
3849 pfindbuf3->ftimeLastWrite.twosecs * 2,
3850 (ULONG)*(PUSHORT)(void*)&pfindbuf3->fdateCreation,
3851 (ULONG)*(PUSHORT)(void*)&pfindbuf3->ftimeCreation,
3852 pfindbuf3->cbFile);
3853 return pszTS;
3854}
3855
3856
3857
3858
3859
3860/*
3861 * Testing purpose.
3862 */
3863
3864#if !defined(OS2FAKE)
3865#include <os2.h>
3866#endif
3867#ifdef OLEMANN
3868#include "olemann.h"
3869#endif
Note: See TracBrowser for help on using the repository browser.