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

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

Synced with os2tools.

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