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

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

Corrected bug in depConvertName which broke $(OBJDIR) and other stuff like that.

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