VirtualBox

source: kBuild/trunk/src/fastdep/fastdep.c@ 3387

Last change on this file since 3387 was 2413, checked in by bird, 14 years ago

copyright year update.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use