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

Last change on this file since 5317 was 5317, checked in by bird, 24 years ago

Addjustments of supertarget and NT version.

File size: 92.7 KB
Line 
1/* $Id: fastdep.c,v 1.26 2001-03-14 20:17:52 bird Exp $
2 *
3 * Fast dependents. (Fast = Quick and Dirty!)
4 *
5 * Copyright (c) 1999-2001 knut st. osmundsen
6 *
7 * PH 2001-03-01 added optional support for a super-dependency
8 *
9 * Project Odin Software License can be found in LICENSE.TXT
10 *
11 */
12
13/*******************************************************************************
14* Defined Constants And Macros *
15*******************************************************************************/
16#define INCL_DOSERRORS
17#define INCL_FILEMGR
18
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/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#if defined(OS2FAKE)
36#include "os2fake.h"
37#else
38#include <os2.h>
39#endif
40
41#include <stdio.h>
42#include <string.h>
43#include <stdlib.h>
44#include <direct.h>
45
46#include "avl.h"
47
48#ifndef INLINE
49# if defined(__IBMC__)
50# define INLINE _Inline
51# elif defined(__IBMCPP__)
52# define INLINE inline
53# elif defined(__WATCOMC__)
54# define INLINE __inline
55# elif defined(__WATCOM_CPLUSPLUS__)
56# define INLINE inline
57# else
58# error message("unknown compiler - inline keyword unknown!")
59# endif
60#endif
61
62/*
63 * This following section is used while testing fastdep.
64 * stdio.h should be included; string.h never included.
65 */
66/*
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70*/
71
72#if 1
73#include <stdio.h>
74#else
75#include <string.h>
76#include <string.h>
77#endif
78
79/*
80 */ /* */ /*
81#include <string.h>
82 */
83#if 1
84# if 1
85 #if 0
86# include <string.h>
87 #else
88# if 1
89 #if 1
90 #if 0
91# include <string.h>
92 #else /* */ /*
93*/
94 # include <stdio.h>
95 #endif
96 #endif
97 #endif
98 #endif
99 #endif
100#endif
101
102/*******************************************************************************
103* Structures and Typedefs *
104*******************************************************************************/
105typedef struct _Options
106{
107 const char * pszInclude;
108 const char * pszExclude;
109 BOOL fExcludeAll;
110 const char * pszObjectExt;
111 const char * pszObjectDir;
112 BOOL fObjectDir; /* replace object directory? */
113 const char * pszRsrcExt;
114 BOOL fObjRule;
115 BOOL fNoObjectPath;
116 BOOL fSrcWhenObj;
117 BOOL fAppend; /* append to the output file, not overwrite it. */
118 BOOL fCheckCyclic; /* allways check for cylic dependency before inserting an dependent. */
119 BOOL fCacheSearchDirs; /* cache entire search dirs. */
120 const char * pszExcludeFiles; /* List of excluded files. */
121 const char * pszSuperDependency; /* name for super dependency */
122} OPTIONS, *POPTIONS;
123
124
125/*
126 * Language specific analysis functions type.
127 */
128typedef int ( _FNLANG) (const char *pszFilename, const char *pszNormFilename,
129 void *pvFile, BOOL fHeader, POPTIONS pOptions);
130typedef _FNLANG *PFNLANG;
131
132
133/**
134 * This struct holds the static configuration of the util.
135 */
136typedef struct _ConfigEntry
137{
138 const char **papszExts; /* Pointer to an array of pointer to extentions for this handler. */
139 /* If NULL this is the last entry. */
140 int iFirstHdr; /* Index into the papszExts array of the first headerfile/copybook. */
141 /* Set it to the NULL element of the array if no headers for this extention. */
142 /* A non-header file may get a object rule. */
143 PFNLANG pfn; /* Pointer to handler function. */
144} CONFIGENTRY, *PCONFIGENTRY;
145
146
147/**
148 * Dependant Rule
149 */
150typedef struct _DepRule
151{
152 AVLNODECORE avlCore;
153 char * pszRule; /* Pointer to rule name */
154 int cDeps; /* Entries in the dependant array. */
155 char ** papszDep; /* Pointer to an array of pointers to dependants. */
156} DEPRULE, *PDEPRULE;
157
158
159/**
160 * Filename cache entry.
161 */
162#define FCACHEENTRY AVLNODECORE
163#define PFCACHEENTRY PAVLNODECORE
164
165
166/*******************************************************************************
167* Internal Functions *
168*******************************************************************************/
169static void syntax(void);
170static int makeDependent(const char *pszFilename, POPTIONS pOptions);
171
172int langC_CPP(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
173int langAsm(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
174int langRC(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
175int langCOBOL(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions);
176
177
178/* string operations */
179static int strnicmpwords(const char *pszS1, const char *pszS2, int cch);
180
181/* file operations */
182static char *fileNormalize(char *pszFilename);
183static char *fileNormalize2(const char *pszFilename, char *pszBuffer);
184 char *filePath(const char *pszFilename, char *pszBuffer);
185static char *filePathSlash(const char *pszFilename, char *pszBuffer);
186static char *filePathSlash2(const char *pszFilename, char *pszBuffer);
187static char *fileName(const char *pszFilename, char *pszBuffer);
188static char *fileNameNoExt(const char *pszFilename, char *pszBuffer);
189static char *fileExt(const char *pszFilename, char *pszBuffer);
190
191/* filecache operations */
192static BOOL filecacheAddFile(const char *pszFilename);
193static BOOL filecacheAddDir(const char *pszDir);
194INLINE BOOL filecacheFind(const char *pszFilename);
195INLINE BOOL filecacheIsDirCached(const char *pszDir);
196
197/* pathlist operations */
198static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer, POPTIONS pOptions);
199static BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename, POPTIONS pOptions);
200
201/* word operations */
202static char *findEndOfWord(char *psz);
203#if 0 /* not used */
204static char *findStartOfWord(char *psz, const char *pszStart);
205#endif
206
207/* file helpers */
208static signed long fsize(FILE *phFile);
209
210/* text helpers */
211INLINE char *trim(char *psz);
212INLINE char *trimR(char *psz);
213
214/* textbuffer */
215static void *textbufferCreate(const char *pszFilename);
216static void textbufferDestroy(void *pvBuffer);
217static char *textbufferNextLine(void *pvBuffer, char *psz);
218static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer);
219
220/* depend workers */
221static BOOL depReadFile(const char *pszFilename, POPTIONS pOptions);
222static BOOL depWriteFile(const char *pszFilename, POPTIONS pOptions);
223static void depRemoveAll(void);
224static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt);
225static BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic);
226#if 0 /* not used */
227static BOOL depCleanFile(const char *pszFilename);
228#endif
229static BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep);
230
231
232/*******************************************************************************
233* Global Variables *
234*******************************************************************************/
235/*
236 * Pointer to the list of dependencies.
237 */
238static PDEPRULE pdepTree = NULL;
239
240
241/*
242 * Filecache - tree starts here.
243 */
244static PFCACHEENTRY pfcTree = NULL;
245static unsigned cfcNodes = 0;
246static PFCACHEENTRY pfcDirTree = NULL;
247
248
249/*
250 * Current directory stuff
251 */
252static char szCurDir[CCHMAXPATH];
253static int aiSlashes[CCHMAXPATH];
254static int cSlashes;
255
256
257/*
258 * Environment variables used.
259 * (These has the correct case.)
260 */
261static char * pszIncludeEnv;
262
263
264/*
265 * Configuration stuff.
266 */
267static const char pszDefaultDepFile[] = ".depend";
268static const char *apszExtC_CPP[] = {"c", "sqc", "cpp", "h", "hpp", NULL};
269static const char *apszExtAsm[] = {"asm", "inc", NULL};
270static const char *apszExtRC[] = {"rc", "orc", "dlg", NULL};
271static const char *apszExtCOBOL[] = {"cbl", "cob", "sqb", NULL};
272static CONFIGENTRY aConfig[] =
273{
274 {
275 apszExtC_CPP,
276 3,
277 langC_CPP,
278 },
279
280 {
281 apszExtAsm,
282 1,
283 langAsm,
284 },
285
286 {
287 apszExtRC,
288 2,
289 langRC,
290 },
291
292 {
293 apszExtCOBOL,
294 3,
295 langCOBOL,
296 },
297
298 /* terminating entry */
299 {
300 NULL,
301 -1,
302 NULL
303 }
304};
305
306
307/**
308 * Main function.
309 * @returns 0 on success.
310 * -n count of failiures.
311 * @param
312 * @param
313 * @equiv
314 * @precond
315 * @methdesc
316 * @result
317 * @time
318 * @sketch
319 * @algo
320 * @remark
321 */
322int main(int argc, char **argv)
323{
324 int rc = 0;
325 int argi = 1;
326 int i;
327 char * psz;
328 char * psz2;
329 const char *pszDepFile = pszDefaultDepFile;
330 char achBuffer[4096];
331
332 static char szObjectDir[CCHMAXPATH];
333 static char szObjectExt[64] = "obj";
334 static char szRsrcExt[64] = "res";
335 static char szInclude[32768] = ";";
336 static char szExclude[32768] = ";";
337 static char szExcludeFiles[65536] = "";
338
339 OPTIONS options =
340 {
341 szInclude, /* pszInclude */
342 szExclude, /* pszExclude */
343 FALSE, /* fExcludeAll */
344 szObjectExt, /* pszObjectExt */
345 szObjectDir, /* pszObjectDir */
346 FALSE, /* fObjectDir */
347 szRsrcExt, /* pszRsrcExt */
348 TRUE, /* fObjRule */
349 FALSE, /* fNoObjectPath */
350 TRUE, /* fSrcWhenObj */
351 FALSE, /* fAppend */
352 TRUE, /* fCheckCyclic */
353 TRUE, /* fCacheSearchDirs */
354 szExcludeFiles, /* pszExcludeFiles */
355 NULL /* pszSuperDependency */
356 };
357
358 szObjectDir[0] = '\0';
359
360 if (argc == 1)
361 {
362 syntax();
363 return -87;
364 }
365
366 /*
367 * Initiate current directory stuff
368 */
369 if (_getcwd(szCurDir, sizeof(szCurDir)) == NULL)
370 {
371 fprintf(stderr, "fatal error: failed to get current directory\n");
372 return -88;
373 }
374 strlwr(szCurDir);
375 aiSlashes[0] = 0;
376 for (i = 1, cSlashes; szCurDir[i] != '\0'; i++)
377 {
378 if (szCurDir[i] == '/')
379 szCurDir[i] = '\\';
380 if (szCurDir[i] == '\\')
381 aiSlashes[cSlashes++] = i;
382 }
383 if (szCurDir[i-1] != '\\')
384 {
385 aiSlashes[cSlashes] = i;
386 szCurDir[i++] = '\\';
387 szCurDir[i] = '\0';
388 }
389
390
391 /*
392 * Initiate environment variables used: INCLUDE
393 */
394 psz = getenv("INCLUDE");
395 if (psz != NULL)
396 {
397 pszIncludeEnv = strdup(psz);
398 strlwr(pszIncludeEnv);
399 }
400 else
401 pszIncludeEnv = "";
402
403
404 /*
405 * parse arguments
406 */
407 while (argi < argc)
408 {
409 if (argv[argi][0] == '-' || argv[argi][0] == '/')
410 {
411 /* parameters */
412 switch (argv[argi][1])
413 {
414 case 'A':
415 case 'a': /* Append to the output file */
416 options.fAppend = argv[argi][2] != '-';
417 break;
418
419 case 'D':
420 case 'd': /* "-d <filename>" */
421 {
422 const char *pszOld = pszDepFile;
423 if (argv[argi][2] != '\0')
424 pszDepFile = &argv[argi][2];
425 else
426 {
427 argi++;
428 if (argi < argc)
429 pszDepFile = argv[argi];
430 else
431 {
432 fprintf(stderr, "invalid parameter -d, filename missing!\n");
433 return -1;
434 }
435 }
436
437 /* if dependencies are generated we'll flush them to the old filename */
438 if (pdepTree != NULL && pszOld != pszDepFile)
439 {
440 if (!depWriteFile(pszOld, &options))
441 fprintf(stderr, "error: failed to write (flush) dependencies.\n");
442 depRemoveAll();
443 }
444 break;
445 }
446
447 case 'C': /* forced directory cache 'ca' or cylic check 'cy'*/
448 case 'c':
449 if (argv[argi][2] == 'a' || argv[argi][2] == 'A')
450 options.fCacheSearchDirs = TRUE;
451 else if ((argv[argi][2] == 'y' || argv[argi][2] == 'Y'))
452 options.fCheckCyclic = argv[argi][3] != '-';
453 break;
454
455 case 'E': /* list of paths. If a file is found in one of these directories the */
456 case 'e': /* filename will be used without the directory path. */
457 /* Eall<[+]|-> ? */
458 if (strlen(&argv[argi][1]) <= 5 && strnicmp(&argv[argi][1], "Eall", 4) == 0)
459 {
460 options.fExcludeAll = argv[argi][5] != '-';
461 break;
462 }
463 /* path or path list */
464 if (strlen(argv[argi]) > 2)
465 psz = &argv[argi][2];
466 else
467 {
468 if (++argi >= argc)
469 {
470 fprintf(stderr, "syntax error! Option -e.\n");
471 return 1;
472 }
473 psz = argv[argi];
474 }
475 /* check if enviroment variable */
476 if (*psz == '%')
477 {
478 psz2 = strdup(psz+1);
479 if (psz2 != NULL && *psz2 != '\0')
480 {
481 if (psz2[strlen(psz2)-1] == '%')
482 psz2[strlen(psz2)-1] = '\0';
483 psz = getenv(psz2);
484 free(psz2);
485 if (psz == NULL)
486 break;
487 }
488 else
489 {
490 fprintf(stderr, "error: -E% is not an valid argument!\n");
491 return -1;
492 }
493 }
494 if (psz != NULL)
495 {
496 strcat(szExclude, psz);
497 strlwr(szExclude);
498 if (szExclude[strlen(szExclude)-1] != ';')
499 strcat(szExclude, ";");
500 }
501 break;
502
503 case 'I': /* optional include path. This has precedence over the INCLUDE environment variable. */
504 case 'i':
505 if (strlen(argv[argi]) > 2)
506 psz = &argv[argi][2];
507 else
508 {
509 if (++argi >= argc)
510 {
511 fprintf(stderr, "syntax error! Option -i.\n");
512 return 1;
513 }
514 psz = argv[argi];
515 }
516 /* check if enviroment variable */
517 if (*psz == '%')
518 {
519 psz2 = strdup(psz+1);
520 if (psz2 != NULL && *psz2 != '\0')
521 {
522 if (psz2[strlen(psz2)-1] == '%')
523 psz2[strlen(psz2)-1] = '\0';
524 psz = getenv(psz2);
525 free(psz2);
526 if (psz == NULL)
527 break;
528 }
529 else
530 {
531 fprintf(stderr, "error: -I% is not an valid argument!\n");
532 return -1;
533 }
534 }
535 if (psz != NULL)
536 {
537 strcat(szInclude, psz);
538 strlwr(szInclude);
539 if (szInclude[strlen(szInclude)-1] != ';')
540 strcat(szInclude, ";");
541 }
542 break;
543
544 case 'n': /* no object path , -N<[+]|-> */
545 case 'N':
546 if (strlen(argv[argi]) <= 1+1+1)
547 options.fNoObjectPath = argv[argi][2] != '-';
548 else
549 {
550 fprintf(stderr, "error: invalid parameter!, '%s'\n", argv[argi]);
551 return -1;
552 }
553 break;
554
555 case 'o': /* object base directory, Obj or Obr<[+]|-> */
556 case 'O':
557 if (strlen(&argv[argi][1]) <= 4 && strnicmp(&argv[argi][1], "Obr", 3) == 0)
558 {
559 options.fObjRule = argv[argi][4] != '-';
560 break;
561 }
562
563 if (strlen(&argv[argi][1]) >= 4 && strnicmp(&argv[argi][1], "Obj", 3) == 0)
564 {
565 if (strlen(argv[argi]) > 4)
566 strcpy(szObjectExt, argv[argi]+4);
567 else
568 {
569 if (++argi >= argc)
570 {
571 fprintf(stderr, "syntax error! Option -obj.\n");
572 return 1;
573 }
574 strcpy(szObjectExt, argv[argi]);
575 }
576 break;
577 }
578
579 /* path: -o or -o- */
580 options.fObjectDir = TRUE;
581 if (strlen(argv[argi]) > 2)
582 {
583 if (argv[argi][2] == '-') /* no object path */
584 szObjectDir[0] = '\0';
585 else
586 strcpy(szObjectDir, argv[argi]+2);
587 }
588 else
589 {
590 if (++argi >= argc)
591 {
592 fprintf(stderr, "syntax error! Option -o.\n");
593 return 1;
594 }
595 strcpy(szObjectDir, argv[argi]);
596 }
597 if (szObjectDir[0] != '\0'
598 && szObjectDir[strlen(szObjectDir)-1] != '\\'
599 && szObjectDir[strlen(szObjectDir)-1] != '/'
600 )
601 strcat(szObjectDir, "\\");
602 break;
603
604 case 'r':
605 case 'R':
606 if (strlen(argv[argi]) > 2)
607 strcpy(szRsrcExt, argv[argi]+2);
608 else
609 {
610 if (++argi >= argc)
611 {
612 fprintf(stderr, "syntax error! Option -r.\n");
613 return 1;
614 }
615 strcpy(szRsrcExt, argv[argi]);
616 }
617 break;
618
619 case 'x':
620 case 'X': /* Exclude files */
621 psz = &achBuffer[CCHMAXPATH+8];
622 if (strlen(argv[argi]) > 2)
623 strcpy(psz, &argv[argi][2]);
624 else
625 {
626 if (++argi >= argc)
627 {
628 fprintf(stderr, "syntax error! Option -x.\n");
629 return 1;
630 }
631 strcpy(psz, argv[argi]);
632 }
633 while (psz != NULL && *psz != ';')
634 {
635 char * pszNext = strchr(psz, ';');
636 int cch = strlen(szExcludeFiles);
637 if (pszNext)
638 *pszNext++ = '\0';
639 if (DosQueryPathInfo(psz, FIL_QUERYFULLNAME, &szExcludeFiles[cch], CCHMAXPATH))
640 {
641 fprintf(stderr, "error: Invalid exclude name\n");
642 return -1;
643 }
644 strlwr(&szExcludeFiles[cch]);
645 strcat(&szExcludeFiles[cch], ";");
646 psz = pszNext;
647 }
648 break;
649
650 case 's': /* insert super-dependency on top of tree */
651 case 'S': /* -s <name for super-dependency>" */
652 {
653 if (strlen(argv[argi]) > 2)
654 /* syntax was /s:name */
655 options.pszSuperDependency = &argv[argi][2];
656 else
657 {
658 argi++;
659 if (argi < argc)
660 /* take next parameter */
661 options.pszSuperDependency = argv[argi];
662 else
663 /* take default */
664 options.pszSuperDependency = "alltargets";
665 }
666 }
667 break;
668
669 case 'h':
670 case 'H':
671 case '?':
672 syntax();
673 return 1;
674
675 default:
676 fprintf(stderr, "error: invalid parameter! '%s'\n", argv[argi]);
677 return -1;
678 }
679
680 }
681 else if (argv[argi][0] == '@')
682 { /*
683 * Parameter file (debugger parameter length restrictions led to this):
684 * Create a textbuffer.
685 * Parse the file and create a new parameter vector.
686 * Set argv to the new parameter vector, argi to 0 and argc to
687 * the parameter count.
688 * Restrictions: Parameters enclosed in "" is not implemented.
689 * No commandline parameters are processed after the @file
690 */
691 char *pszBuffer = (char*)textbufferCreate(&argv[argi][1]); /* !ASSUMS! that pvBuffer is the file string! */
692 if (pszBuffer != NULL)
693 {
694 char **apszArgs = NULL;
695 char *psz = pszBuffer;
696 int i = 0;
697
698 while (*psz != '\0')
699 {
700 /* find end of parameter word */
701 char *pszEnd = psz + 1;
702 char ch = *pszEnd;
703 while (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r' && ch != '\0')
704 ch = *++pszEnd;
705
706 /* allocate more arg array space? */
707 if ((i % 512) == 0)
708 {
709 apszArgs = realloc(apszArgs, sizeof(char*) * 512);
710 if (apszArgs == NULL)
711 {
712 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
713 return -8;
714 }
715 }
716 *pszEnd = '\0';
717 apszArgs[i++] = psz;
718
719 /* next */
720 psz = pszEnd + 1;
721 ch = *psz;
722 while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')
723 ch = *++psz;
724 }
725
726 argc = i;
727 argi = 0;
728 argv = apszArgs;
729 continue;
730 }
731 else
732 {
733 fprintf(stderr, "error: could not open parameter file\n");
734 return -1;
735 }
736 }
737 else
738 { /* not a parameter! */
739 ULONG ulRc;
740 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
741 HDIR hDir = HDIR_CREATE;
742 ULONG cFiles = ~0UL;
743 int i;
744
745
746 /*
747 * If append option is specified we'll have to read the existing dep file
748 * before starting adding new dependencies.
749 */
750 if (pdepTree == NULL && options.fAppend)
751 depReadFile(pszDepFile, &options);
752
753 /*
754 * Search for the files specified.
755 */
756 ulRc = DosFindFirst(argv[argi], &hDir,
757 FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM | FILE_ARCHIVED,
758 pfindbuf3, sizeof(achBuffer), &cFiles, FIL_STANDARD);
759 if (!options.fCacheSearchDirs)
760 options.fCacheSearchDirs = cFiles > 25;
761 while (ulRc == NO_ERROR)
762 {
763 for (i = 0;
764 i < cFiles;
765 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
766 )
767 {
768 const char * psz;
769 char szSource[CCHMAXPATH];
770 BOOL fExcluded;
771
772 /*
773 * Make full path.
774 */
775 if ((psz = strrchr(argv[argi], '\\')) || (psz = strrchr(argv[argi], '/')))
776 {
777 strncpy(szSource, argv[argi], psz - argv[argi] + 1);
778 szSource[psz - argv[argi] + 1] = '\0';
779 }
780 else
781 szSource[0] = '\0';
782 strcat(szSource, pfindbuf3->achName);
783 strlwr(szSource);
784 fileNormalize(szSource);
785
786 /*
787 * Check if this is an excluded file.
788 */
789 fExcluded = FALSE;
790 psz = options.pszExcludeFiles;
791 while (*psz != '\0' && *psz != ';')
792 {
793 const char * pszNext = strchr(psz, ';');
794 if (strlen(szSource) == pszNext - psz && strncmp(szSource, psz, pszNext - psz) == 0)
795 fExcluded = TRUE;
796 psz = pszNext + 1;
797 }
798 if (fExcluded)
799 continue;
800
801 /*
802 * Analyse the file.
803 */
804 rc -= makeDependent(&szSource[0], &options);
805 }
806
807 /* next file */
808 cFiles = ~0UL;
809 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
810 ulRc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
811 }
812 DosFindClose(hDir);
813 }
814 /* next */
815 argi++;
816 }
817
818 /* Write the depend file! */
819 if (!depWriteFile(pszDepFile, &options))
820 fprintf(stderr, "error: failed to write dependencies file!\n");
821 #if 0
822 printf("cfcNodes=%d\n", cfcNodes);
823 #endif
824
825 return rc;
826}
827
828
829/**
830 * Displays the syntax description for this util.
831 * @status completely implemented.
832 * @author knut st. osmundsen
833 */
834static void syntax(void)
835{
836 printf(
837 "FastDep v0.32\n"
838 "Dependency scanner. Creates a makefile readable depend file.\n"
839 " - was quick and dirty, now it's just quick -\n"
840 "\n"
841 "Syntax: FastDep [-a<[+]|->] [-ca] [-cy<[+]|->] [-d <outputfn>]\n"
842 " [-e <excludepath>] [-eall<[+]|->] [-i <include>] [-n<[+]|->]\n"
843 " [-o <objdir>] [-obr<[+]|->] [-x <f1[;f2]>] [-r <rsrcext>]\n"
844 " <files> [more options [more files [...]]]\n"
845 " or\n"
846 " FastDep [options] @<parameterfile>\n"
847 "\n"
848 " -a<[+]|-> Append to the output file. Default: Overwrite.\n"
849 " -ca Force search directory caching.\n"
850 " Default: cache if more that 25 files are to be searched.\n"
851 " (more than 25 in the first file expression.)\n"
852 " -cy<[+]|-> Check for cylic dependencies. Default: -cy-\n"
853 " -d <outputfn> Output filename. Default: %s\n"
854 " -e excludepath Exclude paths. If a filename is found in any\n"
855 " of these paths only the filename is used, not\n"
856 " the path+filename (which is default).\n"
857 " -eall<[+]|-> Include and source filenames, paths or no paths.\n"
858 " -eall+: No path are added to the filename.\n"
859 " -eall-: The filename is appended the include path\n"
860 " was found in.\n"
861 " Default: eall-\n"
862 " -i <include> Additional include paths. INCLUDE is searched after this.\n"
863 " -n<[+]|-> No path for object files in the rules.\n"
864 " -o <objdir> Path were object files are placed. This path replaces the\n"
865 " entire filename path\n"
866 " -o- No object path\n"
867 " -obr<[+]|-> -obr+: Object rule.\n"
868 " -obr-: No object rule, rule for source filename is generated.\n"
869 " -obj[ ]<objext> Object extention. Default: obj\n"
870 " -s[ ][name] Insert super-dependency on top of tree.\n"
871 " If not specified name defaults to 'alltargets'.\n"
872 " Default: disabled\n"
873 " -r[ ]<rsrcext> Resource binary extention. Default: res\n"
874 " -x[ ]<f1[;f2]> Files to exclude. Only exact filenames.\n"
875 " <files> Files to scan. Wildchars are allowed.\n"
876 "\n"
877 "Options and files could be mixed.\n"
878 " copyright (c) 1999-2001 knut st. osmundsen (knut.stange.osmundsen@mynd.no)\n",
879 pszDefaultDepFile
880 );
881}
882
883
884/**
885 * Generates depend info on this file, these are stored internally
886 * and written to file later.
887 * @returns
888 * @param pszFilename Pointer to source filename. Correct case is assumed!
889 * @param pOptions Pointer to options struct.
890 * @status completely implemented.
891 * @author knut st. osmundsen
892 */
893static int makeDependent(const char *pszFilename, POPTIONS pOptions)
894{
895 int rc = -1;
896 void * pvFile;
897
898 pvFile = textbufferCreate(pszFilename);
899 if (pvFile != NULL)
900 {
901 char szExt[CCHMAXPATH];
902 PCONFIGENTRY pCfg = &aConfig[0];
903 BOOL fHeader;
904
905 /*
906 * Find which filetype this is...
907 */
908 fileExt(pszFilename, szExt);
909 while (pCfg->papszExts != NULL)
910 {
911 const char **ppsz = pCfg->papszExts;
912 while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
913 ppsz++;
914 if (*ppsz != NULL)
915 {
916 fHeader = &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
917 break;
918 }
919 pCfg++;
920 }
921
922 /* Found? */
923 if (pCfg->papszExts != NULL)
924 {
925 char szNormFile[CCHMAXPATH];
926 fileNormalize2(pszFilename, szNormFile);
927 rc = (*pCfg->pfn)(pszFilename, &szNormFile[0], pvFile, fHeader, pOptions);
928 }
929 else
930 {
931 if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
932 fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
933 rc = 0;
934 }
935
936 textbufferDestroy(pvFile);
937 }
938 else
939 fprintf(stderr, "failed to open '%s'\n", pszFilename);
940
941 return rc;
942}
943
944
945/**
946 * Generates depend info on this C or C++ file, these are stored internally
947 * and written to file later.
948 * @returns 0 on success.
949 * !0 on error.
950 * @param pszFilename Pointer to source filename. Correct case is assumed!
951 * @param pszNormFilename Pointer to normalized source filename.
952 * @param pvFile Pointer to file textbuffer.
953 * @param pOptions Pointer to options struct.
954 * @status completely implemented.
955 * @author knut st. osmundsen
956 */
957int langC_CPP(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
958{
959 void * pvRule; /* Handle to the current rule. */
960 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
961 int iLine; /* Linenumber. */
962 void * pv = NULL; /* An index used by textbufferGetNextLine. */
963 BOOL fComment; /* TRUE when within a multiline comment. */
964 /* FALSE when not within a multiline comment. */
965 int iIfStack; /* StackPointer. */
966 struct IfStackEntry
967 {
968 int fIncluded : 1; /* TRUE: include this code;
969 * FALSE: excluded */
970 int fIf : 1; /* TRUE: #if part of the expression.
971 * FALSE: #else part of the expression. */
972 int fSupported : 1; /* TRUE: supported if/else statement
973 * FALSE: unsupported all else[<something>] are ignored
974 * All code is included.
975 */
976 } achIfStack[256];
977
978
979 /**********************************/
980 /* Add the depend rule */
981 /**********************************/
982 if (pOptions->fObjRule && !fHeader)
983 {
984 if (pOptions->fNoObjectPath)
985 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
986 else
987 pvRule = depAddRule(pOptions->fObjectDir ?
988 pOptions->pszObjectDir :
989 filePathSlash(pszFilename, szBuffer),
990 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
991 pOptions->pszObjectExt);
992
993 if (pOptions->fSrcWhenObj && pvRule)
994 depAddDepend(pvRule,
995 pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
996 fileName(pszFilename, szBuffer) : pszNormFilename,
997 pOptions->fCheckCyclic);
998 }
999 else
1000 pvRule = depAddRule(pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1001 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL);
1002
1003 /* duplicate rule? */
1004 if (pvRule == NULL)
1005 return 0;
1006
1007
1008 /*******************/
1009 /* find dependants */
1010 /*******************/
1011 /* Initiate the IF-stack, comment state and line number. */
1012 iIfStack = 0;
1013 achIfStack[iIfStack].fIf = TRUE;
1014 achIfStack[iIfStack].fIncluded = TRUE;
1015 achIfStack[iIfStack].fSupported = TRUE;
1016 fComment = FALSE;
1017 iLine = 0;
1018 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1019 {
1020 /* search for #include */
1021 register char *pszC;
1022 int cbLen;
1023 int i = 0;
1024 iLine++;
1025
1026 /* skip blank chars */
1027 cbLen = strlen(szBuffer);
1028 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1029 i++;
1030
1031 /* preprocessor statement? */
1032 if (!fComment && szBuffer[i] == '#')
1033 {
1034 /*
1035 * Preprocessor checks
1036 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1037 * Depending on the word afterwards we'll take some different actions.
1038 * So we'll start of by extracting that word and make a string swich on it.
1039 * Note that there might be some blanks between the hash and the word.
1040 */
1041 int cchWord;
1042 char * pszEndWord;
1043 char * pszArgument;
1044 i++; /* skip hash ('#') */
1045 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1046 i++;
1047 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1048 cchWord = pszEndWord - &szBuffer[i];
1049
1050 /*
1051 * Find the argument by skipping the blanks.
1052 */
1053 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1054 pszArgument++;
1055
1056 /*
1057 * string switch.
1058 */
1059 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1060 {
1061 /*
1062 * #include
1063 *
1064 * Are we in a state where this file is to be included?
1065 */
1066 if (achIfStack[iIfStack].fIncluded)
1067 {
1068 char szFullname[CCHMAXPATH];
1069 char *psz;
1070 BOOL f = FALSE;
1071 int j;
1072
1073 /* extract info between "" or <> */
1074 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1075 i++;
1076 i++; /* skip '"' or '<' */
1077
1078 /* if invalid statement then continue with the next line! */
1079 if (!f) continue;
1080
1081 /* find end */
1082 j = f = 0;
1083 while (i + j < cbLen && j < CCHMAXPATH &&
1084 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1085 j++;
1086
1087 /* if invalid statement then continue with the next line! */
1088 if (!f) continue;
1089
1090 /* copy filename */
1091 strncpy(szFullname, &szBuffer[i], j);
1092 szFullname[j] = '\0'; /* ensure terminatition. */
1093 strlwr(szFullname);
1094
1095 /* find include file! */
1096 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1097 if (psz == NULL)
1098 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer, pOptions);
1099
1100 /* did we find the include? */
1101 if (psz != NULL)
1102 {
1103 if (pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, szBuffer, pOptions))
1104 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1105 else
1106 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1107 }
1108 else
1109 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1110 pszFilename, iLine, szFullname);
1111 }
1112 }
1113 else
1114 /*
1115 * #if
1116 */
1117 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1118 { /* #if 0 and #if <1-9> are supported */
1119 pszEndWord = findEndOfWord(pszArgument);
1120 iIfStack++;
1121 if ((pszEndWord - pszArgument) == 1
1122 && *pszArgument >= '0' && *pszArgument <= '9')
1123 {
1124 if (*pszArgument != '0')
1125 achIfStack[iIfStack].fIncluded = TRUE;
1126 else
1127 achIfStack[iIfStack].fIncluded = FALSE;
1128 }
1129 else
1130 achIfStack[iIfStack].fSupported = FALSE;
1131 achIfStack[iIfStack].fIncluded = TRUE;
1132 achIfStack[iIfStack].fIf = TRUE;
1133 }
1134 else
1135 /*
1136 * #else
1137 */
1138 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1139 {
1140 if (achIfStack[iIfStack].fSupported)
1141 {
1142 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1143 achIfStack[iIfStack].fIncluded = FALSE;
1144 else
1145 achIfStack[iIfStack].fIncluded = TRUE;
1146 }
1147 achIfStack[iIfStack].fIf = FALSE;
1148 }
1149 else
1150 /*
1151 * #endif
1152 */
1153 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1154 { /* Pop the if-stack. */
1155 if (iIfStack > 0)
1156 iIfStack--;
1157 else
1158 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1159 }
1160 /*
1161 * general if<something> and elseif<something> implementations
1162 */
1163 else
1164 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1165 {
1166 achIfStack[iIfStack].fSupported = FALSE;
1167 achIfStack[iIfStack].fIncluded = TRUE;
1168 }
1169 else
1170 if (strncmp(&szBuffer[i], "if", 2) == 0)
1171 {
1172 iIfStack++;
1173 achIfStack[iIfStack].fIf = TRUE;
1174 achIfStack[iIfStack].fSupported = FALSE;
1175 achIfStack[iIfStack].fIncluded = TRUE;
1176 }
1177 /* The rest of them aren't implemented yet.
1178 else if (strncmp(&szBuffer[i], "if") == 0)
1179 {
1180 }
1181 */
1182 }
1183
1184 /*
1185 * Comment checks.
1186 * -Start at first non-blank.
1187 * -Loop thru the line since we might have more than one
1188 * comment statement on a single line.
1189 */
1190 pszC = &szBuffer[i];
1191 while (pszC != NULL && *pszC != '\0')
1192 {
1193 if (fComment)
1194 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
1195 else
1196 {
1197 char *pszLC;
1198 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
1199 pszC = strstr(pszC, "/*"); /* look for start comment mark */
1200 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
1201 break; /* muliline comment mark we'll ignore the multiline mark. */
1202 }
1203
1204 /* Comment mark found? */
1205 if (pszC != NULL)
1206 {
1207 fComment = !fComment;
1208 pszC += 2; /* skip comment mark */
1209
1210 /* debug */
1211 /*
1212 if (fComment)
1213 fprintf(stderr, "starts at line %d\n", iLine);
1214 else
1215 fprintf(stderr, "ends at line %d\n", iLine);
1216 */
1217 }
1218 }
1219 } /*while*/
1220
1221 return 0;
1222}
1223
1224
1225/**
1226 * Generates depend info on this file, these are stored internally
1227 * and written to file later.
1228 * @returns 0 on success.
1229 * !0 on error.
1230 * @param pszFilename Pointer to source filename. Correct case is assumed!
1231 * @param pszNormFilename Pointer to normalized source filename.
1232 * @param pvFile Pointer to file textbuffer.
1233 * @param pOptions Pointer to options struct.
1234 * @status completely implemented.
1235 * @author knut st. osmundsen
1236 */
1237int langAsm(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1238{
1239 void * pvRule; /* Handle to the current rule. */
1240 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1241 int iLine; /* current line number */
1242 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1243
1244
1245 /**********************************/
1246 /* Add the depend rule */
1247 /**********************************/
1248 if (pOptions->fObjRule && !fHeader)
1249 {
1250 if (pOptions->fNoObjectPath)
1251 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
1252 else
1253 pvRule = depAddRule(pOptions->fObjectDir ?
1254 pOptions->pszObjectDir :
1255 filePathSlash(pszFilename, szBuffer),
1256 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1257 pOptions->pszObjectExt);
1258
1259 if (pOptions->fSrcWhenObj && pvRule)
1260 depAddDepend(pvRule,
1261 pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1262 fileName(pszFilename, szBuffer) : pszNormFilename,
1263 pOptions->fCheckCyclic);
1264 }
1265 else
1266 pvRule = depAddRule(pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1267 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL);
1268
1269 /* duplicate rule? */
1270 if (pvRule == NULL)
1271 return 0;
1272
1273
1274 /*******************/
1275 /* find dependants */
1276 /*******************/
1277 iLine = 0;
1278 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1279 {
1280 /* search for include */
1281 int cbLen;
1282 int i = 0;
1283 iLine++;
1284
1285 /* skip blank chars */
1286 cbLen = strlen(szBuffer);
1287 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1288 i++;
1289
1290 /* is this an include? */
1291 if (strnicmp(&szBuffer[i], "include", 7) == 0
1292 && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
1293 )
1294 {
1295 char szFullname[CCHMAXPATH];
1296 char *psz;
1297 int j;
1298
1299 /* skip to first no blank char */
1300 i += 7;
1301 while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1302 i++;
1303
1304 /* comment check - if comment found, no filename was given. continue. */
1305 if (szBuffer[i] == ';') continue;
1306
1307 /* find end */
1308 j = 0;
1309 while (i + j < cbLen
1310 && j < CCHMAXPATH
1311 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
1312 && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';' && szBuffer[i+j] != '\r'
1313 )
1314 j++;
1315
1316 /* copy filename */
1317 strncpy(szFullname, &szBuffer[i], j);
1318 szFullname[j] = '\0'; /* ensure terminatition. */
1319 strlwr(szFullname);
1320
1321 /* find include file! */
1322 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1323 if (psz == NULL)
1324 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer, pOptions);
1325
1326 /* Did we find the include? */
1327 if (psz != NULL)
1328 {
1329 if (pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, szBuffer, pOptions))
1330 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1331 else
1332 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1333 }
1334 else
1335 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1336 pszFilename, iLine, szFullname);
1337 }
1338 } /*while*/
1339
1340 return 0;
1341}
1342
1343
1344/**
1345 * Generates depend info on this Resource file, these are stored internally
1346 * and written to file later.
1347 * @returns 0 on success.
1348 * !0 on error.
1349 * @param pszFilename Pointer to source filename. Correct case is assumed!
1350 * @param pszNormFilename Pointer to normalized source filename.
1351 * @param pvFile Pointer to file textbuffer.
1352 * @param pOptions Pointer to options struct.
1353 * @status completely implemented.
1354 * @author knut st. osmundsen
1355 */
1356int langRC(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1357{
1358 void * pvRule; /* Handle to the current rule. */
1359 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1360 int iLine; /* current line number */
1361 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1362
1363
1364 /**********************************/
1365 /* Add the depend rule */
1366 /**********************************/
1367 if (pOptions->fObjRule && !fHeader)
1368 {
1369 if (pOptions->fNoObjectPath)
1370 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszRsrcExt);
1371 else
1372 pvRule = depAddRule(pOptions->fObjectDir ?
1373 pOptions->pszObjectDir :
1374 filePathSlash(pszFilename, szBuffer),
1375 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1376 pOptions->pszRsrcExt);
1377
1378 if (pOptions->fSrcWhenObj && pvRule)
1379 depAddDepend(pvRule,
1380 pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1381 fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1382 pOptions->fCheckCyclic);
1383 }
1384 else
1385 pvRule = depAddRule(pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1386 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL);
1387
1388 /* duplicate rule? */
1389 if (pvRule == NULL)
1390 return 0;
1391
1392
1393 /*******************/
1394 /* find dependants */
1395 /*******************/
1396 iLine = 0;
1397 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1398 {
1399 /* search for #include */
1400 int cbLen;
1401 int i = 0;
1402 int i1;
1403 iLine++;
1404
1405 /* skip blank chars */
1406 cbLen = strlen(szBuffer);
1407 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1408 i++;
1409
1410 /* is this an include? */
1411 i1 = 1;
1412 if ( strncmp(&szBuffer[i], "#include", 8) == 0
1413 || (i1 = strncmp(&szBuffer[i], "RCINCLUDE", 9)) == 0
1414 || strncmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
1415 )
1416 {
1417 char szFullname[CCHMAXPATH];
1418 char *psz;
1419 BOOL f = FALSE;
1420 int j;
1421
1422 if (i1 != 0)
1423 { /*
1424 * #include <file.h>, #include "file.h" or DLGINCLUDE 1 "file.h"
1425 *
1426 * extract info between "" or <>
1427 */
1428 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1429 i++;
1430 i++; /* skip '"' or '<' */
1431
1432 /* if invalid statement then continue with the next line! */
1433 if (!f) continue;
1434
1435 /* find end */
1436 j = f = 0;
1437 while (i + j < cbLen && j < CCHMAXPATH &&
1438 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1439 j++;
1440
1441 /* if invalid statement then continue with the next line! */
1442 if (!f) continue;
1443 }
1444 else
1445 { /*
1446 * RCINCLUDE ["]filename.dlg["]
1447 * Extract filename.
1448 */
1449
1450 /* skip to filename.dlg start - if eol will continue to loop. */
1451 i += 9;
1452 while (szBuffer[i] == ' ' || szBuffer[i] == '\t' || szBuffer[i] == '"')
1453 i++;
1454 if (szBuffer[i] == '\0')
1455 continue;
1456
1457 /* search to end of filename. */
1458 j = i+1;
1459 while ( szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1460 && szBuffer[i+j] != '"' && szBuffer[i+j] != '\0')
1461 j++;
1462 }
1463
1464 /* copy filename */
1465 strncpy(szFullname, &szBuffer[i], j);
1466 szFullname[j] = '\0'; /* ensure terminatition. */
1467 strlwr(szFullname);
1468
1469 /* find include file! */
1470 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1471 if (psz == NULL)
1472 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer, pOptions);
1473
1474 /* did we find the include? */
1475 if (psz != NULL)
1476 {
1477 if (pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, szBuffer, pOptions))
1478 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1479 else
1480 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1481 }
1482 else
1483 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1484 pszFilename, iLine, szFullname);
1485 }
1486 } /*while*/
1487
1488 return 0;
1489}
1490
1491
1492/**
1493 * Generates depend info on this COBOL file, these are stored internally
1494 * and written to file later.
1495 * @returns 0 on success.
1496 * !0 on error.
1497 * @param pszFilename Pointer to source filename. Correct case is assumed!
1498 * @param pszNormFilename Pointer to normalized source filename.
1499 * @param pvFile Pointer to file textbuffer.
1500 * @param pOptions Pointer to options struct.
1501 * @status completely implemented.
1502 * @author knut st. osmundsen
1503 */
1504int langCOBOL(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1505{
1506 void * pvRule; /* Handle to the current rule. */
1507 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1508 int iLine; /* current line number */
1509 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1510
1511
1512 /**********************************/
1513 /* Add the depend rule */
1514 /**********************************/
1515 if (pOptions->fObjRule && !fHeader)
1516 {
1517 if (pOptions->fNoObjectPath)
1518 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
1519 else
1520 pvRule = depAddRule(pOptions->fObjectDir ?
1521 pOptions->pszObjectDir :
1522 filePathSlash(pszFilename, szBuffer),
1523 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1524 pOptions->pszObjectExt);
1525
1526 if (pOptions->fSrcWhenObj && pvRule)
1527 depAddDepend(pvRule,
1528 pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1529 fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1530 pOptions->fCheckCyclic);
1531 }
1532 else
1533 pvRule = depAddRule(pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1534 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL);
1535
1536 /* duplicate rule? */
1537 if (pvRule == NULL)
1538 return 0;
1539
1540
1541 /*******************/
1542 /* find dependants */
1543 /*******************/
1544 iLine = 0;
1545 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1546 {
1547 /* search for #include */
1548 int cbLen;
1549 int i = 0;
1550 int i1, i2;
1551 iLine++;
1552
1553 /* check for comment mark (column 7) */
1554 if (szBuffer[6] == '*')
1555 continue;
1556
1557 /* skip blank chars */
1558 cbLen = strlen(szBuffer);
1559 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1560 i++;
1561
1562 /* is this an include? */
1563 if ( (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
1564 || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
1565 )
1566 {
1567 char szFullname[CCHMAXPATH];
1568 char *psz;
1569 int j;
1570
1571 /* skip statement */
1572 i += 4;
1573 if (i1 != 0)
1574 {
1575 int y = 2; /* skip two words */
1576 do
1577 {
1578 /* skip blanks */
1579 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1580 i++;
1581 /* skip word */
1582 while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
1583 && szBuffer[i] != '\0' && szBuffer[i] != '\n')
1584 i++;
1585 y--;
1586 } while (y > 0);
1587 }
1588
1589 /* check for blank */
1590 if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
1591 continue;
1592
1593 /* skip blanks */
1594 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1595 i++;
1596
1597 /* if invalid statement then continue with the next line! */
1598 if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
1599 continue;
1600
1601 /* find end */
1602 j = 0;
1603 while (i + j < cbLen && j < CCHMAXPATH
1604 && szBuffer[i+j] != '.'
1605 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1606 && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
1607 )
1608 j++;
1609
1610 /* if invalid statement then continue with the next line! */
1611 if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
1612 continue;
1613
1614 /* copy filename */
1615 strncpy(szFullname, &szBuffer[i], j);
1616 szFullname[j] = '\0'; /* ensure terminatition. */
1617 strlwr(szFullname);
1618
1619 /* add extention .cpy - hardcoded for the moment. */
1620 strcat(szFullname, ".cpy");
1621
1622 /* find include file! */
1623 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1624
1625 /* did we find the include? */
1626 if (psz != NULL)
1627 {
1628 if (pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, szBuffer, pOptions))
1629 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1630 else
1631 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1632 }
1633 else
1634 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1635 pszFilename, iLine, szFullname);
1636 }
1637 } /*while*/
1638
1639 return 0;
1640}
1641
1642#define upcase(ch) \
1643 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
1644
1645/**
1646 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
1647 * @returns 0 equal. (same as strnicmp)
1648 * @param pszS1 String 1
1649 * @param pszS2 String 2
1650 * @param cch Length to compare (relative to string 1)
1651 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1652 */
1653static int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
1654{
1655 do
1656 {
1657 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
1658 pszS1++, pszS2++, cch--;
1659
1660 /* blank test and skipping */
1661 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
1662 {
1663 while (cch > 0 && *pszS1 == ' ')
1664 pszS1++, cch--;
1665
1666 while (*pszS2 == ' ')
1667 pszS2++;
1668 }
1669 else
1670 break;
1671 } while (cch > 0);
1672
1673 return cch == 0 ? 0 : *pszS1 - *pszS2;
1674}
1675
1676
1677/**
1678 * Normalizes the path slashes for the filename. It will partially expand paths too.
1679 * @returns pszFilename
1680 * @param pszFilename Pointer to filename string. Not empty string!
1681 * Much space to play with.
1682 */
1683char *fileNormalize(char *pszFilename)
1684{
1685 char *psz = pszFilename;
1686
1687 /* correct slashes */
1688 while ((pszFilename = strchr(pszFilename, '//')) != NULL)
1689 *pszFilename++ = '\\';
1690
1691 /* expand path? */
1692 pszFilename = psz;
1693 if (pszFilename[1] != ':')
1694 { /* relative path */
1695 int iSlash;
1696 char szFile[CCHMAXPATH];
1697 char * psz = szFile;
1698
1699 strcpy(szFile, pszFilename);
1700 iSlash = *psz == '\\' ? 1 : cSlashes;
1701 while (*psz != '\0')
1702 {
1703 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
1704 { /* up one directory */
1705 if (iSlash > 0)
1706 iSlash--;
1707 psz += 3;
1708 }
1709 else if (*psz == '.' && psz[1] == '\\')
1710 { /* no change */
1711 psz += 2;
1712 }
1713 else
1714 { /* completed expantion! */
1715 strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
1716 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
1717 break;
1718 }
1719 }
1720 }
1721 /* else: assume full path */
1722
1723 return psz;
1724}
1725
1726
1727/**
1728 * Normalizes the path slashes for the filename. It will partially expand paths too.
1729 * Makes name all lower case too.
1730 * @returns pszFilename
1731 * @param pszFilename Pointer to filename string. Not empty string!
1732 * Much space to play with.
1733 * @param pszBuffer Pointer to output buffer.
1734 */
1735char *fileNormalize2(const char *pszFilename, char *pszBuffer)
1736{
1737 char * psz = pszBuffer;
1738 int iSlash;
1739
1740 if (pszFilename[1] != ':')
1741 {
1742 /* iSlash */
1743 if (*pszFilename == '\\' || *pszFilename == '/')
1744 iSlash = 1;
1745 else
1746 iSlash = cSlashes;
1747
1748 /* interpret . and .. */
1749 while (*pszFilename != '\0')
1750 {
1751 if (*pszFilename == '.' && pszFilename[1] == '.' && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
1752 { /* up one directory */
1753 if (iSlash > 0)
1754 iSlash--;
1755 pszFilename += 3;
1756 }
1757 else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
1758 { /* no change */
1759 pszFilename += 2;
1760 }
1761 else
1762 { /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
1763 strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
1764 strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
1765 break;
1766 }
1767 }
1768 }
1769 else
1770 { /* have drive letter specified - assume ok (TODO)*/
1771 strcpy(pszBuffer, pszFilename);
1772 }
1773
1774 /* correct slashes */
1775 while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
1776 *pszBuffer++ = '\\';
1777
1778 /* lower case it */
1779 /*strlwr(psz);*/
1780
1781 return psz;
1782}
1783
1784
1785/**
1786 * Copies the path part (excluding the slash) into pszBuffer and returns
1787 * a pointer to the buffer.
1788 * If no path is found "" is returned.
1789 * @returns Pointer to pszBuffer with path.
1790 * @param pszFilename Pointer to readonly filename.
1791 * @param pszBuffer Pointer to output Buffer.
1792 * @status completely implemented.
1793 * @author knut st. osmundsen
1794 */
1795char *filePath(const char *pszFilename, char *pszBuffer)
1796{
1797 char *psz = strrchr(pszFilename, '\\');
1798 if (psz == NULL)
1799 psz = strrchr(pszFilename, '/');
1800
1801 if (psz == NULL)
1802 *pszBuffer = '\0';
1803 else
1804 {
1805 strncpy(pszBuffer, pszFilename, psz - pszFilename);
1806 pszBuffer[psz - pszFilename] = '\0';
1807 }
1808
1809 return pszBuffer;
1810}
1811
1812
1813/**
1814 * Copies the path part including the slash into pszBuffer and returns
1815 * a pointer to the buffer.
1816 * If no path is found "" is returned.
1817 * @returns Pointer to pszBuffer with path.
1818 * @param pszFilename Pointer to readonly filename.
1819 * @param pszBuffer Pointer to output Buffer.
1820 * @status completely implemented.
1821 * @author knut st. osmundsen
1822 */
1823static char *filePathSlash(const char *pszFilename, char *pszBuffer)
1824{
1825 char *psz = strrchr(pszFilename, '\\');
1826 if (psz == NULL)
1827 psz = strrchr(pszFilename, '/');
1828
1829 if (psz == NULL)
1830 *pszBuffer = '\0';
1831 else
1832 {
1833 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
1834 pszBuffer[psz - pszFilename + 1] = '\0';
1835 }
1836
1837 return pszBuffer;
1838}
1839
1840
1841/**
1842 * Copies the path part including the slash into pszBuffer and returns
1843 * a pointer to the buffer. If no path is found "" is returned.
1844 * The path is normalized to only use '\\'.
1845 * @returns Pointer to pszBuffer with path.
1846 * @param pszFilename Pointer to readonly filename.
1847 * @param pszBuffer Pointer to output Buffer.
1848 * @status completely implemented.
1849 * @author knut st. osmundsen
1850 */
1851static char *filePathSlash2(const char *pszFilename, char *pszBuffer)
1852{
1853 char *psz = strrchr(pszFilename, '\\');
1854 if (psz == NULL)
1855 psz = strrchr(pszFilename, '/');
1856
1857 if (psz == NULL)
1858 *pszBuffer = '\0';
1859 else
1860 {
1861 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
1862 pszBuffer[psz - pszFilename + 1] = '\0';
1863
1864 /* normalize all '/' to '\\' */
1865 psz = pszBuffer;
1866 while ((psz = strchr(psz, '/')) != NULL)
1867 *psz++ = '\\';
1868 }
1869
1870 return pszBuffer;
1871}
1872
1873
1874/**
1875 * Copies the filename (with extention) into pszBuffer and returns
1876 * a pointer to the buffer.
1877 * @returns Pointer to pszBuffer with path.
1878 * @param pszFilename Pointer to readonly filename.
1879 * @param pszBuffer Pointer to output Buffer.
1880 * @status completely implemented.
1881 * @author knut st. osmundsen
1882 */
1883char *fileName(const char *pszFilename, char *pszBuffer)
1884{
1885 char *psz = strrchr(pszFilename, '\\');
1886 if (psz == NULL)
1887 psz = strrchr(pszFilename, '/');
1888
1889 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1890
1891 return pszBuffer;
1892}
1893
1894
1895/**
1896 * Copies the name part with out extention into pszBuffer and returns
1897 * a pointer to the buffer.
1898 * If no name is found "" is returned.
1899 * @returns Pointer to pszBuffer with path.
1900 * @param pszFilename Pointer to readonly filename.
1901 * @param pszBuffer Pointer to output Buffer.
1902 * @status completely implemented.
1903 * @author knut st. osmundsen
1904 */
1905char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
1906{
1907 char *psz = strrchr(pszFilename, '\\');
1908 if (psz == NULL)
1909 psz = strrchr(pszFilename, '/');
1910
1911 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1912
1913 psz = strrchr(pszBuffer, '.');
1914 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
1915 *psz = '\0';
1916
1917 return pszBuffer;
1918}
1919
1920
1921/**
1922 * Copies the extention part into pszBuffer and returns
1923 * a pointer to the buffer.
1924 * If no extention is found "" is returned.
1925 * The dot ('.') is not included!
1926 * @returns Pointer to pszBuffer with path.
1927 * @param pszFilename Pointer to readonly filename.
1928 * @param pszBuffer Pointer to output Buffer.
1929 * @status completely implemented.
1930 * @author knut st. osmundsen
1931 */
1932char *fileExt(const char *pszFilename, char *pszBuffer)
1933{
1934 char *psz = strrchr(pszFilename, '.');
1935 if (psz != NULL)
1936 {
1937 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
1938 *pszBuffer = '\0';
1939 else
1940 strcpy(pszBuffer, psz + 1);
1941 }
1942 else
1943 *pszBuffer = '\0';
1944
1945 return pszBuffer;
1946}
1947
1948
1949/**
1950 * Adds a file to the cache.
1951 * @returns Success indicator.
1952 * @param pszFilename Name of the file which is to be added. (with path!)
1953 */
1954static BOOL filecacheAddFile(const char *pszFilename)
1955{
1956 PFCACHEENTRY pfcNew;
1957
1958 /* allocate new block and fill in data */
1959 pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
1960 if (pfcNew == NULL)
1961 {
1962 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
1963 return FALSE;
1964 }
1965 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
1966 strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
1967 if (!AVLInsert(&pfcTree, pfcNew))
1968 {
1969 free(pfcNew);
1970 return TRUE;
1971 }
1972 cfcNodes++;
1973
1974 return TRUE;
1975}
1976
1977
1978
1979/**
1980 * Adds a file to the cache.
1981 * @returns Success indicator.
1982 * @param pszDir Name of the path which is to be added. (with slash!)
1983 */
1984static BOOL filecacheAddDir(const char *pszDir)
1985{
1986 PFCACHEENTRY pfcNew;
1987 APIRET rc;
1988 char szDir[CCHMAXPATH];
1989 int cchDir;
1990 char achBuffer[16384];
1991 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
1992 HDIR hDir = HDIR_CREATE;
1993 ULONG cFiles = 0xFFFFFFF;
1994 int i;
1995
1996 /* Make path */
1997 filePathSlash2(pszDir, szDir);
1998 //strlwr(szDir); /* Convert name to lower case to allow faster searchs! */
1999 cchDir = strlen(szDir);
2000
2001
2002 /* Add directory to pfcDirTree. */
2003 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
2004 if (pfcNew == NULL)
2005 {
2006 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2007 DosFindClose(hDir);
2008 return FALSE;
2009 }
2010 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2011 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2012 AVLInsert(&pfcDirTree, pfcNew);
2013
2014
2015 /* Start to search directory - all files */
2016 strcat(szDir + cchDir, "*");
2017 rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
2018 pfindbuf3, sizeof(achBuffer),
2019 &cFiles, FIL_STANDARD);
2020 while (rc == NO_ERROR)
2021 {
2022 for (i = 0;
2023 i < cFiles;
2024 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
2025 )
2026 {
2027 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
2028 if (pfcNew == NULL)
2029 {
2030 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2031 DosFindClose(hDir);
2032 return FALSE;
2033 }
2034 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2035 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2036 strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
2037 strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
2038 if (!AVLInsert(&pfcTree, pfcNew))
2039 free(pfcNew);
2040 else
2041 cfcNodes++;
2042 }
2043
2044 /* next */
2045 cFiles = 0xFFFFFFF;
2046 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2047 rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
2048 }
2049
2050 DosFindClose(hDir);
2051
2052 return TRUE;
2053}
2054
2055
2056/**
2057 * Checks if pszFilename is exists in the cache.
2058 * @return TRUE if found. FALSE if not found.
2059 * @param pszFilename Name of the file to be found. (with path!)
2060 * This is in lower case!
2061 */
2062INLINE BOOL filecacheFind(const char *pszFilename)
2063{
2064 return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
2065}
2066
2067
2068/**
2069 * Checks if pszFilename is exists in the cache.
2070 * @return TRUE if found. FALSE if not found.
2071 * @param pszFilename Name of the file to be found. (with path!)
2072 * This is in lower case!
2073 */
2074INLINE BOOL filecacheIsDirCached(const char *pszDir)
2075{
2076 return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
2077}
2078
2079
2080
2081/**
2082 * Finds a filename in a specified pathlist.
2083 * @returns Pointer to a filename consiting of the path part + the given filename.
2084 * (pointer into pszBuffer)
2085 * NULL if file is not found. ("" in buffer)
2086 * @param pszPathList Path list to search for filename.
2087 * @parma pszFilename Filename to find.
2088 * @parma pszBuffer Ouput Buffer.
2089 * @param pOptions Pointer to options struct.
2090 * @status completely implemented.
2091 * @author knut st. osmundsen
2092 */
2093static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer, POPTIONS pOptions)
2094{
2095 const char *psz = pszPathList;
2096 const char *pszNext = NULL;
2097
2098 *pszBuffer = '\0';
2099
2100 if (pszPathList == NULL)
2101 return NULL;
2102
2103 while (*psz != '\0')
2104 {
2105 /* find end of this path */
2106 pszNext = strchr(psz, ';');
2107 if (pszNext == NULL)
2108 pszNext = psz + strlen(psz);
2109
2110 if (pszNext - psz > 0)
2111 {
2112 APIRET rc;
2113
2114 /* make search statment */
2115 strncpy(pszBuffer, psz, pszNext - psz);
2116 pszBuffer[pszNext - psz] = '\0';
2117 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
2118 strcpy(&pszBuffer[pszNext - psz], "\\");
2119 strcat(pszBuffer, pszFilename);
2120 fileNormalize(pszBuffer);
2121
2122 /*
2123 * Search for the file in this directory.
2124 * Search cache first
2125 */
2126 if (!filecacheFind(pszBuffer))
2127 {
2128 char szDir[CCHMAXPATH];
2129
2130 filePathSlash(pszBuffer, szDir);
2131 if (!filecacheIsDirCached(szDir))
2132 {
2133 /*
2134 * If caching of entire dirs are enabled, we'll
2135 * add the directory to the cache and search it.
2136 */
2137 if (pOptions->fCacheSearchDirs && filecacheAddDir(szDir))
2138 {
2139 if (filecacheFind(pszBuffer))
2140 return pszBuffer;
2141 }
2142 else
2143 {
2144 FILESTATUS3 fsts3;
2145
2146 /* ask the OS */
2147 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2148 if (rc == NO_ERROR)
2149 { /* add file to cache. */
2150 filecacheAddFile(pszBuffer);
2151 return pszBuffer;
2152 }
2153 }
2154 }
2155 }
2156 else
2157 return pszBuffer;
2158 }
2159
2160 /* next */
2161 if (*pszNext != ';')
2162 break;
2163 psz = pszNext + 1;
2164 }
2165
2166 return NULL;
2167}
2168
2169
2170
2171/**
2172 * Checks if the given filename may exist within any of the given paths.
2173 * This check only matches the filename path agianst the paths in the pathlist.
2174 * @returns TRUE: if exists.
2175 * FALSE: don't exist.
2176 * @param pszPathList Path list to search for filename.
2177 * @parma pszFilename Filename to find. The filename should be normalized!
2178 * @param pOptions Pointer to options struct.
2179 * @status completely implemented.
2180 * @author knut st. osmundsen
2181 */
2182static BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename, POPTIONS pOptions)
2183{
2184 const char *psz = pszPathList;
2185 const char *pszNext = NULL;
2186 char szBuffer[CCHMAXPATH];
2187 char szBuffer2[CCHMAXPATH];
2188 char *pszPathToFind = &szBuffer2[0];
2189
2190 /*
2191 * Input checking
2192 */
2193 if (pszPathList == NULL)
2194 return FALSE;
2195
2196 /*
2197 * Normalize the filename and get it's path.
2198 */
2199 filePath(pszFilename, pszPathToFind);
2200
2201
2202 /*
2203 * Loop thru the path list.
2204 */
2205 while (*psz != '\0')
2206 {
2207 /* find end of this path */
2208 pszNext = strchr(psz, ';');
2209 if (pszNext == NULL)
2210 pszNext = psz + strlen(psz);
2211
2212 if (pszNext - psz > 0)
2213 {
2214 char * pszPath = &szBuffer[0];
2215
2216 /*
2217 * Extract and normalize the path
2218 */
2219 strncpy(pszPath, psz, pszNext - psz);
2220 pszPath[pszNext - psz] = '\0';
2221 if (pszPath[pszNext - psz - 1] == '\\' && pszPath[pszNext - psz - 1] == '/')
2222 pszPath[pszNext - psz - 1] = '\0';
2223 fileNormalize(pszPath);
2224
2225 /*
2226 * Check if it matches the path of the filename
2227 */
2228 if (strcmp(pszPath, pszPathToFind) == 0)
2229 return TRUE;
2230 }
2231
2232 /*
2233 * Next part of the path list.
2234 */
2235 if (*pszNext != ';')
2236 break;
2237 psz = pszNext + 1;
2238 }
2239
2240 pOptions = pOptions;
2241 return FALSE;
2242}
2243
2244
2245/**
2246 * Finds the first char after word.
2247 * @returns Pointer to the first char after word.
2248 * @param psz Where to start.
2249 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2250 */
2251static char *findEndOfWord(char *psz)
2252{
2253
2254 while (*psz != '\0' &&
2255 (
2256 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
2257 ||
2258 (*psz >= '0' && *psz <= '9')
2259 ||
2260 *psz == '_'
2261 )
2262 )
2263 ++psz;
2264 return (char *)psz;
2265}
2266
2267#if 0 /* not used */
2268/**
2269 * Find the starting char of a word
2270 * @returns Pointer to first char in word.
2271 * @param psz Where to start.
2272 * @param pszStart Where to stop.
2273 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2274 */
2275static char *findStartOfWord(const char *psz, const char *pszStart)
2276{
2277 const char *pszR = psz;
2278 while (psz >= pszStart &&
2279 (
2280 (*psz >= 'A' && *psz <= 'Z')
2281 || (*psz >= 'a' && *psz <= 'z')
2282 || (*psz >= '0' && *psz <= '9')
2283 || *psz == '_'
2284 )
2285 )
2286 pszR = psz--;
2287 return (char*)pszR;
2288}
2289#endif
2290
2291/**
2292 * Find the size of a file.
2293 * @returns Size of file. -1 on error.
2294 * @param phFile File handle.
2295 */
2296static signed long fsize(FILE *phFile)
2297{
2298 int ipos;
2299 signed long cb;
2300
2301 if ((ipos = ftell(phFile)) < 0
2302 ||
2303 fseek(phFile, 0, SEEK_END) != 0
2304 ||
2305 (cb = ftell(phFile)) < 0
2306 ||
2307 fseek(phFile, ipos, SEEK_SET) != 0
2308 )
2309 cb = -1;
2310 return cb;
2311}
2312
2313
2314
2315/**
2316 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2317 * @returns Pointer to first not space or tab char in the string.
2318 * @param psz Pointer to the string which is to be trimmed.
2319 * @status completely implmented.
2320 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2321 */
2322INLINE char *trim(char *psz)
2323{
2324 int i;
2325 if (psz == NULL)
2326 return NULL;
2327 while (*psz == ' ' || *psz == '\t')
2328 psz++;
2329 i = strlen(psz) - 1;
2330 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2331 i--;
2332 psz[i+1] = '\0';
2333 return psz;
2334}
2335
2336
2337/**
2338 * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
2339 * @returns Pointer to the string passed in.
2340 * @param psz Pointer to the string which is to be right trimmed.
2341 * @status completely implmented.
2342 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2343 */
2344INLINE char *trimR(char *psz)
2345{
2346 int i;
2347 if (psz == NULL)
2348 return NULL;
2349 i = strlen(psz) - 1;
2350 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2351 i--;
2352 psz[i+1] = '\0';
2353 return psz;
2354}
2355
2356
2357/**
2358 * Creates a memory buffer for a text file.
2359 * @returns Pointer to file memoryblock. NULL on error.
2360 * @param pszFilename Pointer to filename string.
2361 * @remark This function is the one using most of the execution
2362 * time (DosRead + DosOpen) - about 70% of the execution time!
2363 */
2364static void *textbufferCreate(const char *pszFilename)
2365{
2366 void *pvFile = NULL;
2367 FILE *phFile;
2368
2369 phFile = fopen(pszFilename, "rb");
2370 if (phFile != NULL)
2371 {
2372 signed long cbFile = fsize(phFile);
2373 if (cbFile >= 0)
2374 {
2375 pvFile = malloc(cbFile + 1);
2376 if (pvFile != NULL)
2377 {
2378 memset(pvFile, 0, cbFile + 1);
2379 if (cbFile > 0 && fread(pvFile, 1, cbFile, phFile) == 0)
2380 { /* failed! */
2381 free(pvFile);
2382 pvFile = NULL;
2383 }
2384 }
2385 else
2386 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
2387 }
2388 fclose(phFile);
2389 }
2390 return pvFile;
2391}
2392
2393
2394/**
2395 * Destroys a text textbuffer.
2396 * @param pvBuffer Buffer handle.
2397 */
2398static void textbufferDestroy(void *pvBuffer)
2399{
2400 free(pvBuffer);
2401}
2402
2403
2404/**
2405 * Gets the next line from an textbuffer.
2406 * @returns Pointer to the next line.
2407 * @param pvBuffer Buffer handle.
2408 * @param psz Pointer to current line.
2409 * NULL is passed in to get the first line.
2410 */
2411static char *textbufferNextLine(void *pvBuffer, register char *psz)
2412{
2413 register char ch;
2414
2415 /* if first line psz is NULL. */
2416 if (psz == NULL)
2417 return (char*)pvBuffer;
2418
2419 /* skip till end of file or end of line. */
2420 ch = *psz;
2421 while (ch != '\0' && ch != '\n' && ch != '\r')
2422 ch = *++psz;
2423
2424 /* skip line end */
2425 if (ch == '\r')
2426 ch = *++psz;
2427 if (ch == '\n')
2428 psz++;
2429
2430 return psz;
2431}
2432
2433
2434/**
2435 * Gets the next line from an textbuffer.
2436 * (fgets for textbuffer)
2437 * @returns Pointer to pszOutBuffer. NULL when end of file.
2438 * @param pvBuffer Buffer handle.
2439 * @param ppv Pointer to a buffer index pointer. (holds the current buffer index)
2440 * Pointer to a null pointer is passed in to get the first line.
2441 * @param pszLineBuffer Output line buffer. (!= NULL)
2442 * @param cchLineBuffer Size of the output line buffer. (> 0)
2443 * @remark '\n' and '\r' are removed!
2444 */
2445static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
2446{
2447 char * pszLine = pszLineBuffer;
2448 char * psz = *(char**)(void*)ppv;
2449 register char ch;
2450
2451 /* first line? */
2452 if (psz == NULL)
2453 psz = pvBuffer;
2454
2455 /* Copy to end of the line or end of the linebuffer. */
2456 ch = *psz;
2457 cchLineBuffer--; /* reserve space for '\0' */
2458 while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
2459 {
2460 *pszLine++ = ch;
2461 ch = *++psz;
2462 }
2463 *pszLine = '\0';
2464
2465 /* skip line end */
2466 if (ch == '\r')
2467 ch = *++psz;
2468 if (ch == '\n')
2469 psz++;
2470
2471 /* check if position has changed - if unchanged it's the end of file! */
2472 if (*ppv == (void*)psz)
2473 pszLineBuffer = NULL;
2474
2475 /* store current position */
2476 *ppv = (void*)psz;
2477
2478 return pszLineBuffer;
2479}
2480
2481
2482/**
2483 * Appends a depend file to the internal file.
2484 */
2485static BOOL depReadFile(const char *pszFilename, POPTIONS pOptions)
2486{
2487 void *pvFile;
2488 char *pszNext;
2489 BOOL fMoreDeps = FALSE;
2490 void *pvRule = NULL;
2491
2492 /* read depend file */
2493 pvFile = textbufferCreate(pszFilename);
2494 if (pvFile == NULL)
2495 return FALSE;
2496
2497 /* parse the original depend file */
2498 pszNext = pvFile;
2499 while (*pszNext != '\0')
2500 {
2501 int i;
2502 int cch;
2503 char *psz;
2504
2505 /* get the next line. */
2506 psz = pszNext;
2507 pszNext = textbufferNextLine(pvFile, pszNext);
2508
2509 /*
2510 * Process the current line:
2511 * Start off by terminating the line.
2512 * Trim the line,
2513 * Skip empty lines.
2514 * If not looking for more deps Then
2515 * Check if new rule starts here.
2516 * Endif
2517 *
2518 * If more deps to last rule Then
2519 * Get dependant name.
2520 * Endif
2521 */
2522 i = -1;
2523 while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
2524 pszNext[i--] = '\0';
2525 trimR(psz);
2526 cch = strlen(psz);
2527 if (cch == 0)
2528 continue;
2529
2530 /* new rule? */
2531 if (!fMoreDeps && *psz != ' ' && *psz != '\t' && *psz != '\0')
2532 {
2533 i = 0;
2534 while (psz[i] != '\0')
2535 {
2536 if (psz[i] == ':'
2537 && (psz[i+1] == ' '
2538 || psz[i+1] == '\t'
2539 || psz[i+1] == '\0'
2540 || (psz[i+1] == '\\' && psz[i+2] == '\0')
2541 )
2542 )
2543 {
2544 psz[i] = '\0';
2545 pvRule = depAddRule(trimR(psz), NULL, NULL);
2546 psz += i + 1;
2547 cch -= i + 1;
2548 fMoreDeps = TRUE;
2549 break;
2550 }
2551 i++;
2552 }
2553 }
2554
2555 /* more dependants */
2556 if (fMoreDeps)
2557 {
2558 if (cch > 0 && psz[cch-1] == '\\')
2559 {
2560 fMoreDeps = TRUE;
2561 psz[cch-1] = '\0';
2562 }
2563 else
2564 fMoreDeps = FALSE;
2565
2566 /* if not duplicate rule */
2567 if (pvRule != NULL)
2568 {
2569 psz = trim(psz);
2570 if (*psz != '\0')
2571 depAddDepend(pvRule, psz, pOptions->fCheckCyclic);
2572 }
2573 }
2574 } /* while */
2575
2576
2577 /* return succesfully */
2578 textbufferDestroy(pvFile);
2579 return TRUE;
2580}
2581
2582/**
2583 *
2584 * @returns Success indicator.
2585 * @params pszFilename Pointer to name of the output file.
2586 */
2587static BOOL depWriteFile(const char *pszFilename, POPTIONS options)
2588{
2589 FILE *phFile;
2590 phFile = fopen(pszFilename, "w");
2591 if (phFile != NULL)
2592 {
2593 AVLENUMDATA EnumData;
2594 PDEPRULE pdep;
2595 char szBuffer[4096];
2596 int iBuffer = 0;
2597 int cch;
2598
2599 /* @@@PH 2001-03-01
2600 * If option is selected to generate a parent
2601 * "super" dependency, enter this scope.
2602 */
2603 if (options->pszSuperDependency != NULL)
2604 {
2605 iBuffer = sprintf(szBuffer,
2606 "%s:",
2607 options->pszSuperDependency);
2608
2609 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
2610 while (pdep != NULL)
2611 {
2612 char *psz = pdep->pszRule;
2613
2614 /* flush buffer? */
2615 if (iBuffer + strlen(psz) + 20 >= sizeof(szBuffer))
2616 {
2617 fwrite(szBuffer, iBuffer, 1, phFile);
2618 iBuffer = 0;
2619 }
2620
2621 /* write rule title as dependant */
2622 iBuffer += sprintf(szBuffer + iBuffer,
2623 " \\\n %s",psz);
2624
2625 /* next rule */
2626 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
2627 }
2628
2629 /* Add two new lines. Flush buffer first if necessary. */
2630 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
2631 {
2632 fwrite(szBuffer, iBuffer, 1, phFile);
2633 iBuffer = 0;
2634 }
2635
2636 /* add 2 linefeeds */
2637 strcpy(szBuffer + iBuffer, "\n\n");
2638 iBuffer += CBNEWLINE*2;
2639 }
2640
2641
2642 /* normal dependency output */
2643 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
2644 while (pdep != NULL)
2645 {
2646 /* Write rule. Flush the buffer first if necessary. */
2647 cch = strlen(pdep->pszRule);
2648 if (iBuffer + cch + 2 >= sizeof(szBuffer))
2649 {
2650 fwrite(szBuffer, iBuffer, 1, phFile);
2651 iBuffer = 0;
2652 }
2653 strcpy(szBuffer + iBuffer, pdep->pszRule);
2654 iBuffer += cch;
2655 strcpy(szBuffer + iBuffer++, ":");
2656
2657 /* write rule dependants. */
2658 if (pdep->papszDep != NULL)
2659 {
2660 char **ppsz = pdep->papszDep;
2661 while (*ppsz != NULL)
2662 {
2663 /* flush buffer? */
2664 if (iBuffer + strlen(*ppsz) + 20 >= sizeof(szBuffer))
2665 {
2666 fwrite(szBuffer, iBuffer, 1, phFile);
2667 iBuffer = 0;
2668 }
2669 iBuffer += sprintf(szBuffer + iBuffer, " \\\n %s", *ppsz);
2670
2671 /* next dependant */
2672 ppsz++;
2673 }
2674 }
2675
2676 /* Add two new lines. Flush buffer first if necessary. */
2677 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
2678 {
2679 fwrite(szBuffer, iBuffer, 1, phFile);
2680 iBuffer = 0;
2681 }
2682
2683 /* add 2 linefeeds */
2684 strcpy(szBuffer + iBuffer, "\n\n");
2685 iBuffer += CBNEWLINE*2;
2686
2687 /* next rule */
2688 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
2689 }
2690
2691
2692 /* flush buffer. */
2693 fwrite(szBuffer, iBuffer, 1, phFile);
2694
2695 fclose(phFile);
2696 return TRUE;
2697 }
2698
2699 return FALSE;
2700}
2701
2702
2703/**
2704 * Removes all nodes in the tree of dependencies. (pdepTree)
2705 */
2706static void depRemoveAll(void)
2707{
2708 AVLENUMDATA EnumData;
2709 PDEPRULE pdep;
2710
2711 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
2712 while (pdep != NULL)
2713 {
2714 /* free this */
2715 if (pdep->papszDep != NULL)
2716 {
2717 char ** ppsz = pdep->papszDep;
2718 while (*ppsz != NULL)
2719 free(*ppsz++);
2720 free(pdep->papszDep);
2721 }
2722 free(pdep);
2723
2724 /* next */
2725 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
2726 }
2727 pdepTree = NULL;
2728}
2729
2730
2731/**
2732 * Adds a rule to the list of dependant rules.
2733 * @returns Rule handle. NULL if rule exists/error.
2734 * @param pszRulePath Pointer to rule text. Empty strings are banned!
2735 * This string might only contain the path of the rule. (with '\\')
2736 * @param pszName Name of the rule.
2737 * NULL if pszRulePath contains the entire rule.
2738 * @param pszExt Extention (without '.')
2739 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
2740 */
2741static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt)
2742{
2743 char szRule[CCHMAXPATH*2];
2744 PDEPRULE pNew;
2745 int cch;
2746
2747 /* make rulename */
2748 strcpy(szRule, pszRulePath);
2749 cch = strlen(szRule);
2750 if (pszName != NULL)
2751 {
2752 strcpy(szRule + cch, pszName);
2753 cch += strlen(szRule + cch);
2754 }
2755 if (pszExt != NULL)
2756 {
2757 strcat(szRule + cch++, ".");
2758 strcat(szRule + cch, pszExt);
2759 cch += strlen(szRule + cch);
2760 }
2761
2762
2763 /*
2764 * Allocate a new rule structure and fill in data
2765 * Note. One block for both the DEPRULE and the pszRule string.
2766 */
2767 pNew = malloc(sizeof(DEPRULE) + cch + 1);
2768 if (pNew == NULL)
2769 {
2770 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
2771 return NULL;
2772 }
2773 pNew->pszRule = (char*)(void*)(pNew + 1);
2774 strcpy(pNew->pszRule, szRule);
2775 pNew->cDeps = 0;
2776 pNew->papszDep = NULL;
2777 pNew->avlCore.Key = pNew->pszRule;
2778
2779 /* Insert rule */
2780 if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
2781 { /* rule existed - return NULL */
2782 free(pNew);
2783 return NULL;
2784 }
2785
2786 return pNew;
2787}
2788
2789
2790
2791/**
2792 * Adds a dependant to a rule.
2793 * @returns Successindicator. TRUE = success.
2794 * FALSE = cyclic or out of memory.
2795 * @param pvRule Rule handle.
2796 * @param pszDep Pointer to dependant name
2797 * @param fCheckCyclic When set we'll check that we're not creating an cyclic dependency.
2798 */
2799static BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic)
2800{
2801 PDEPRULE pdep = (PDEPRULE)pvRule;
2802
2803 if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
2804 {
2805 fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
2806 pszDep, pdep->pszRule);
2807 return FALSE;
2808 }
2809
2810 /* allocate more array space */
2811 if (((pdep->cDeps) % 48) == 0)
2812 {
2813 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
2814 if (pdep->papszDep == NULL)
2815 {
2816 pdep->cDeps = 0;
2817 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
2818 return FALSE;
2819 }
2820 }
2821
2822 /* allocate string space and copy pszDep */
2823 if ((pdep->papszDep[pdep->cDeps] = malloc(strlen(pszDep) + 1)) == NULL)
2824 {
2825 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
2826 return FALSE;
2827 }
2828 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
2829
2830 /* terminate array and increment dep count */
2831 pdep->papszDep[++pdep->cDeps] = NULL;
2832
2833 /* successful! */
2834 return TRUE;
2835}
2836
2837
2838/**
2839 * Checks if adding this dependent will create a cylic dependency.
2840 * @returns TRUE: Cyclic.
2841 * FALSE: Non-cylic.
2842 * @param pdepRule Rule pszDep is to be inserted in.
2843 * @param pszDep Depend name.
2844 */
2845static BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
2846{
2847 #define DEPTH 32
2848 char * pszRule = pdepRule->pszRule;
2849 char ** appsz[DEPTH];
2850 PDEPRULE pdep;
2851 int i;
2852
2853 /* self check */
2854 if (strcmp(pdepRule->pszRule, pszDep) == 0)
2855 return TRUE;
2856
2857 /* find rule for the dep. */
2858 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
2859 || pdep->papszDep == NULL)
2860 return FALSE; /* no rule, or no dependents, not cyclic */
2861
2862 i = 0;
2863 appsz[0] = pdep->papszDep;
2864 while (i >= 0)
2865 {
2866 register char ** ppsz = appsz[i];
2867
2868 while (*ppsz != NULL)
2869 {
2870 /* check if equal to the main rule */
2871 if (strcmp(pszRule, *ppsz) == 0)
2872 return TRUE;
2873
2874 /* push onto stack (ppsz is incremented in this test!) */
2875 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
2876 && pdep->papszDep != NULL)
2877 {
2878 if (i >= DEPTH)
2879 {
2880 fprintf(stderr, "error: too deap chain (%d). pszRule=%s pszDep=%s\n",
2881 i, pszRule, pszDep);
2882 return FALSE;
2883 }
2884 appsz[i++] = ppsz; /* save next */
2885 ppsz = pdep->papszDep; /* start processing new node */
2886 }
2887 }
2888
2889 /* pop stack */
2890 i--;
2891 }
2892
2893 return FALSE;
2894}
2895
2896
2897
2898
2899/*
2900 * Testing purpose.
2901 */
2902#if !defined(OS2FAKE)
2903#include <os2.h>
2904#endif
2905#ifdef OLEMANN
2906#include "olemann.h"
2907#endif
2908
2909
2910
2911
Note: See TracBrowser for help on using the repository browser.