VirtualBox

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

Last change on this file since 507 was 7, checked in by bird, 22 years ago

Initial version from odin32.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette