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

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

Added support for additional source file dependants for the different languages.

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