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

Last change on this file since 5282 was 5282, checked in by phaller, 25 years ago

Added super-dependency generation capability

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