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

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

Correction for the -Eall and -E<> options.
Things like #include <sys/stats.h> was troublesome and must be ignored.
Added build no. to syntax();
Corrected emails.

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