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

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

Added caching - better use of existing .depend file. Now it's fast!

File size: 115.7 KB
Line 
1/* $Id: fastdep.c,v 1.29 2001-07-03 21:59:56 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.40\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 strcat(szFullname, ".cpy");
2068
2069 /* find include file! */
2070 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2071
2072 /* did we find the include? */
2073 if (psz != NULL)
2074 {
2075 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
2076 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
2077 else
2078 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
2079 }
2080 else
2081 {
2082 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
2083 pszFilename, iLine, szFullname);
2084 depMarkNotFound(pvRule);
2085 }
2086 }
2087 } /*while*/
2088
2089 textbufferDestroy(pvFile);
2090
2091 return 0;
2092}
2093
2094#define upcase(ch) \
2095 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
2096
2097/**
2098 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
2099 * @returns 0 equal. (same as strnicmp)
2100 * @param pszS1 String 1
2101 * @param pszS2 String 2
2102 * @param cch Length to compare (relative to string 1)
2103 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2104 */
2105int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
2106{
2107 do
2108 {
2109 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
2110 pszS1++, pszS2++, cch--;
2111
2112 /* blank test and skipping */
2113 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
2114 {
2115 while (cch > 0 && *pszS1 == ' ')
2116 pszS1++, cch--;
2117
2118 while (*pszS2 == ' ')
2119 pszS2++;
2120 }
2121 else
2122 break;
2123 } while (cch > 0);
2124
2125 return cch == 0 ? 0 : *pszS1 - *pszS2;
2126}
2127
2128
2129/**
2130 * Normalizes the path slashes for the filename. It will partially expand paths too.
2131 * @returns pszFilename
2132 * @param pszFilename Pointer to filename string. Not empty string!
2133 * Much space to play with.
2134 */
2135char *fileNormalize(char *pszFilename)
2136{
2137 char *psz = pszFilename;
2138
2139 /* correct slashes */
2140 while ((pszFilename = strchr(pszFilename, '//')) != NULL)
2141 *pszFilename++ = '\\';
2142
2143 /* expand path? */
2144 pszFilename = psz;
2145 if (pszFilename[1] != ':')
2146 { /* relative path */
2147 int iSlash;
2148 char szFile[CCHMAXPATH];
2149 char * psz = szFile;
2150
2151 strcpy(szFile, pszFilename);
2152 iSlash = *psz == '\\' ? 1 : cSlashes;
2153 while (*psz != '\0')
2154 {
2155 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
2156 { /* up one directory */
2157 if (iSlash > 0)
2158 iSlash--;
2159 psz += 3;
2160 }
2161 else if (*psz == '.' && psz[1] == '\\')
2162 { /* no change */
2163 psz += 2;
2164 }
2165 else
2166 { /* completed expantion! */
2167 strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
2168 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
2169 break;
2170 }
2171 }
2172 }
2173 /* else: assume full path */
2174
2175 return psz;
2176}
2177
2178
2179/**
2180 * Normalizes the path slashes for the filename. It will partially expand paths too.
2181 * Makes name all lower case too.
2182 * @returns pszFilename
2183 * @param pszFilename Pointer to filename string. Not empty string!
2184 * Much space to play with.
2185 * @param pszBuffer Pointer to output buffer.
2186 */
2187char *fileNormalize2(const char *pszFilename, char *pszBuffer)
2188{
2189 char * psz = pszBuffer;
2190 int iSlash;
2191
2192 if (pszFilename[1] != ':')
2193 {
2194 /* iSlash */
2195 if (*pszFilename == '\\' || *pszFilename == '/')
2196 iSlash = 1;
2197 else
2198 iSlash = cSlashes;
2199
2200 /* interpret . and .. */
2201 while (*pszFilename != '\0')
2202 {
2203 if (*pszFilename == '.' && pszFilename[1] == '.' && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
2204 { /* up one directory */
2205 if (iSlash > 0)
2206 iSlash--;
2207 pszFilename += 3;
2208 }
2209 else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
2210 { /* no change */
2211 pszFilename += 2;
2212 }
2213 else
2214 { /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
2215 strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
2216 strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
2217 break;
2218 }
2219 }
2220 }
2221 else
2222 { /* have drive letter specified - assume ok (TODO)*/
2223 strcpy(pszBuffer, pszFilename);
2224 }
2225
2226 /* correct slashes */
2227 while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
2228 *pszBuffer++ = '\\';
2229
2230 /* lower case it */
2231 /*strlwr(psz);*/
2232
2233 return psz;
2234}
2235
2236
2237/**
2238 * Copies the path part (excluding the slash) into pszBuffer and returns
2239 * a pointer to the buffer.
2240 * If no path is found "" is returned.
2241 * @returns Pointer to pszBuffer with path.
2242 * @param pszFilename Pointer to readonly filename.
2243 * @param pszBuffer Pointer to output Buffer.
2244 * @status completely implemented.
2245 * @author knut st. osmundsen
2246 */
2247char *filePath(const char *pszFilename, char *pszBuffer)
2248{
2249 char *psz = strrchr(pszFilename, '\\');
2250 if (psz == NULL)
2251 psz = strrchr(pszFilename, '/');
2252
2253 if (psz == NULL)
2254 *pszBuffer = '\0';
2255 else
2256 {
2257 strncpy(pszBuffer, pszFilename, psz - pszFilename);
2258 pszBuffer[psz - pszFilename] = '\0';
2259 }
2260
2261 return pszBuffer;
2262}
2263
2264
2265/**
2266 * Copies the path part including the slash into pszBuffer and returns
2267 * a pointer to the buffer.
2268 * If no path is found "" is returned.
2269 * @returns Pointer to pszBuffer with path.
2270 * @param pszFilename Pointer to readonly filename.
2271 * @param pszBuffer Pointer to output Buffer.
2272 * @status completely implemented.
2273 * @author knut st. osmundsen
2274 */
2275char *filePathSlash(const char *pszFilename, char *pszBuffer)
2276{
2277 char *psz = strrchr(pszFilename, '\\');
2278 if (psz == NULL)
2279 psz = strrchr(pszFilename, '/');
2280
2281 if (psz == NULL)
2282 *pszBuffer = '\0';
2283 else
2284 {
2285 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2286 pszBuffer[psz - pszFilename + 1] = '\0';
2287 }
2288
2289 return pszBuffer;
2290}
2291
2292
2293/**
2294 * Copies the path part including the slash into pszBuffer and returns
2295 * a pointer to the buffer. If no path is found "" is returned.
2296 * The path is normalized to only use '\\'.
2297 * @returns Pointer to pszBuffer with path.
2298 * @param pszFilename Pointer to readonly filename.
2299 * @param pszBuffer Pointer to output Buffer.
2300 * @status completely implemented.
2301 * @author knut st. osmundsen
2302 */
2303char *filePathSlash2(const char *pszFilename, char *pszBuffer)
2304{
2305 char *psz = strrchr(pszFilename, '\\');
2306 if (psz == NULL)
2307 psz = strrchr(pszFilename, '/');
2308
2309 if (psz == NULL)
2310 *pszBuffer = '\0';
2311 else
2312 {
2313 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2314 pszBuffer[psz - pszFilename + 1] = '\0';
2315
2316 /* normalize all '/' to '\\' */
2317 psz = pszBuffer;
2318 while ((psz = strchr(psz, '/')) != NULL)
2319 *psz++ = '\\';
2320 }
2321
2322 return pszBuffer;
2323}
2324
2325
2326/**
2327 * Copies the filename (with extention) into pszBuffer and returns
2328 * a pointer to the buffer.
2329 * @returns Pointer to pszBuffer with path.
2330 * @param pszFilename Pointer to readonly filename.
2331 * @param pszBuffer Pointer to output Buffer.
2332 * @status completely implemented.
2333 * @author knut st. osmundsen
2334 */
2335char *fileName(const char *pszFilename, char *pszBuffer)
2336{
2337 char *psz = strrchr(pszFilename, '\\');
2338 if (psz == NULL)
2339 psz = strrchr(pszFilename, '/');
2340
2341 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2342
2343 return pszBuffer;
2344}
2345
2346
2347/**
2348 * Copies the name part with out extention into pszBuffer and returns
2349 * a pointer to the buffer.
2350 * If no name is found "" is returned.
2351 * @returns Pointer to pszBuffer with path.
2352 * @param pszFilename Pointer to readonly filename.
2353 * @param pszBuffer Pointer to output Buffer.
2354 * @status completely implemented.
2355 * @author knut st. osmundsen
2356 */
2357char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
2358{
2359 char *psz = strrchr(pszFilename, '\\');
2360 if (psz == NULL)
2361 psz = strrchr(pszFilename, '/');
2362
2363 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2364
2365 psz = strrchr(pszBuffer, '.');
2366 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
2367 *psz = '\0';
2368
2369 return pszBuffer;
2370}
2371
2372
2373/**
2374 * Copies the extention part into pszBuffer and returns
2375 * a pointer to the buffer.
2376 * If no extention is found "" is returned.
2377 * The dot ('.') is not included!
2378 * @returns Pointer to pszBuffer with path.
2379 * @param pszFilename Pointer to readonly filename.
2380 * @param pszBuffer Pointer to output Buffer.
2381 * @status completely implemented.
2382 * @author knut st. osmundsen
2383 */
2384char *fileExt(const char *pszFilename, char *pszBuffer)
2385{
2386 char *psz = strrchr(pszFilename, '.');
2387 if (psz != NULL)
2388 {
2389 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
2390 *pszBuffer = '\0';
2391 else
2392 strcpy(pszBuffer, psz + 1);
2393 }
2394 else
2395 *pszBuffer = '\0';
2396
2397 return pszBuffer;
2398}
2399
2400
2401/**
2402 * Adds a file to the cache.
2403 * @returns Success indicator.
2404 * @param pszFilename Name of the file which is to be added. (with path!)
2405 */
2406BOOL filecacheAddFile(const char *pszFilename)
2407{
2408 PFCACHEENTRY pfcNew;
2409
2410 /* allocate new block and fill in data */
2411 pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
2412 if (pfcNew == NULL)
2413 {
2414 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2415 return FALSE;
2416 }
2417 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2418 strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
2419 if (!AVLInsert(&pfcTree, pfcNew))
2420 {
2421 free(pfcNew);
2422 return TRUE;
2423 }
2424 cfcNodes++;
2425
2426 return TRUE;
2427}
2428
2429
2430
2431/**
2432 * Adds a file to the cache.
2433 * @returns Success indicator.
2434 * @param pszDir Name of the path which is to be added. (with slash!)
2435 */
2436BOOL filecacheAddDir(const char *pszDir)
2437{
2438 PFCACHEENTRY pfcNew;
2439 APIRET rc;
2440 char szDir[CCHMAXPATH];
2441 int cchDir;
2442 char achBuffer[16384];
2443 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2444 HDIR hDir = HDIR_CREATE;
2445 ULONG cFiles = 0xFFFFFFF;
2446 int i;
2447
2448 /* Make path */
2449 filePathSlash2(pszDir, szDir);
2450 //strlwr(szDir); /* Convert name to lower case to allow faster searchs! */
2451 cchDir = strlen(szDir);
2452
2453
2454 /* Add directory to pfcDirTree. */
2455 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
2456 if (pfcNew == NULL)
2457 {
2458 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2459 DosFindClose(hDir);
2460 return FALSE;
2461 }
2462 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2463 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2464 AVLInsert(&pfcDirTree, pfcNew);
2465
2466
2467 /* Start to search directory - all files */
2468 strcat(szDir + cchDir, "*");
2469 rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
2470 pfindbuf3, sizeof(achBuffer),
2471 &cFiles, FIL_STANDARD);
2472 while (rc == NO_ERROR)
2473 {
2474 for (i = 0;
2475 i < cFiles;
2476 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
2477 )
2478 {
2479 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
2480 if (pfcNew == NULL)
2481 {
2482 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2483 DosFindClose(hDir);
2484 return FALSE;
2485 }
2486 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2487 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2488 strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
2489 strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
2490 if (!AVLInsert(&pfcTree, pfcNew))
2491 free(pfcNew);
2492 else
2493 cfcNodes++;
2494 }
2495
2496 /* next */
2497 cFiles = 0xFFFFFFF;
2498 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2499 rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
2500 }
2501
2502 DosFindClose(hDir);
2503
2504 return TRUE;
2505}
2506
2507
2508/**
2509 * Checks if pszFilename is exists in the cache.
2510 * @return TRUE if found. FALSE if not found.
2511 * @param pszFilename Name of the file to be found. (with path!)
2512 * This is in lower case!
2513 */
2514INLINE BOOL filecacheFind(const char *pszFilename)
2515{
2516 return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
2517}
2518
2519
2520/**
2521 * Checks if pszFilename is exists in the cache.
2522 * @return TRUE if found. FALSE if not found.
2523 * @param pszFilename Name of the file to be found. (with path!)
2524 * This is in lower case!
2525 */
2526INLINE BOOL filecacheIsDirCached(const char *pszDir)
2527{
2528 return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
2529}
2530
2531
2532
2533/**
2534 * Finds a filename in a specified pathlist.
2535 * @returns Pointer to a filename consiting of the path part + the given filename.
2536 * (pointer into pszBuffer)
2537 * NULL if file is not found. ("" in buffer)
2538 * @param pszPathList Path list to search for filename.
2539 * @parma pszFilename Filename to find.
2540 * @parma pszBuffer Ouput Buffer.
2541 * @status completely implemented.
2542 * @author knut st. osmundsen
2543 */
2544char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer)
2545{
2546 const char *psz = pszPathList;
2547 const char *pszNext = NULL;
2548
2549 *pszBuffer = '\0';
2550
2551 if (pszPathList == NULL)
2552 return NULL;
2553
2554 while (*psz != '\0')
2555 {
2556 /* find end of this path */
2557 pszNext = strchr(psz, ';');
2558 if (pszNext == NULL)
2559 pszNext = psz + strlen(psz);
2560
2561 if (pszNext - psz > 0)
2562 {
2563 APIRET rc;
2564
2565 /* make search statment */
2566 strncpy(pszBuffer, psz, pszNext - psz);
2567 pszBuffer[pszNext - psz] = '\0';
2568 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
2569 strcpy(&pszBuffer[pszNext - psz], "\\");
2570 strcat(pszBuffer, pszFilename);
2571 fileNormalize(pszBuffer);
2572
2573 /*
2574 * Search for the file in this directory.
2575 * Search cache first
2576 */
2577 if (!filecacheFind(pszBuffer))
2578 {
2579 char szDir[CCHMAXPATH];
2580
2581 filePathSlash(pszBuffer, szDir);
2582 if (!filecacheIsDirCached(szDir))
2583 {
2584 /*
2585 * If caching of entire dirs are enabled, we'll
2586 * add the directory to the cache and search it.
2587 */
2588 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
2589 {
2590 if (filecacheFind(pszBuffer))
2591 return pszBuffer;
2592 }
2593 else
2594 {
2595 FILESTATUS3 fsts3;
2596
2597 /* ask the OS */
2598 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2599 if (rc == NO_ERROR)
2600 { /* add file to cache. */
2601 filecacheAddFile(pszBuffer);
2602 return pszBuffer;
2603 }
2604 }
2605 }
2606 }
2607 else
2608 return pszBuffer;
2609 }
2610
2611 /* next */
2612 if (*pszNext != ';')
2613 break;
2614 psz = pszNext + 1;
2615 }
2616
2617 return NULL;
2618}
2619
2620
2621
2622/**
2623 * Checks if the given filename may exist within any of the given paths.
2624 * This check only matches the filename path agianst the paths in the pathlist.
2625 * @returns TRUE: if exists.
2626 * FALSE: don't exist.
2627 * @param pszPathList Path list to search for filename.
2628 * @parma pszFilename Filename to find. The filename should be normalized!
2629 * @status completely implemented.
2630 * @author knut st. osmundsen
2631 */
2632BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename)
2633{
2634 const char *psz = pszPathList;
2635 const char *pszNext = NULL;
2636 char szBuffer[CCHMAXPATH];
2637 char szBuffer2[CCHMAXPATH];
2638 char *pszPathToFind = &szBuffer2[0];
2639
2640 /*
2641 * Input checking
2642 */
2643 if (pszPathList == NULL)
2644 return FALSE;
2645
2646 /*
2647 * Normalize the filename and get it's path.
2648 */
2649 filePath(pszFilename, pszPathToFind);
2650
2651
2652 /*
2653 * Loop thru the path list.
2654 */
2655 while (*psz != '\0')
2656 {
2657 /* find end of this path */
2658 pszNext = strchr(psz, ';');
2659 if (pszNext == NULL)
2660 pszNext = psz + strlen(psz);
2661
2662 if (pszNext - psz > 0)
2663 {
2664 char * pszPath = &szBuffer[0];
2665
2666 /*
2667 * Extract and normalize the path
2668 */
2669 strncpy(pszPath, psz, pszNext - psz);
2670 pszPath[pszNext - psz] = '\0';
2671 if (pszPath[pszNext - psz - 1] == '\\' && pszPath[pszNext - psz - 1] == '/')
2672 pszPath[pszNext - psz - 1] = '\0';
2673 fileNormalize(pszPath);
2674
2675 /*
2676 * Check if it matches the path of the filename
2677 */
2678 if (strcmp(pszPath, pszPathToFind) == 0)
2679 return TRUE;
2680 }
2681
2682 /*
2683 * Next part of the path list.
2684 */
2685 if (*pszNext != ';')
2686 break;
2687 psz = pszNext + 1;
2688 }
2689
2690 return FALSE;
2691}
2692
2693
2694/**
2695 * Finds the first char after word.
2696 * @returns Pointer to the first char after word.
2697 * @param psz Where to start.
2698 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2699 */
2700char *findEndOfWord(char *psz)
2701{
2702
2703 while (*psz != '\0' &&
2704 (
2705 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
2706 ||
2707 (*psz >= '0' && *psz <= '9')
2708 ||
2709 *psz == '_'
2710 )
2711 )
2712 ++psz;
2713 return (char *)psz;
2714}
2715
2716#if 0 /* not used */
2717/**
2718 * Find the starting char of a word
2719 * @returns Pointer to first char in word.
2720 * @param psz Where to start.
2721 * @param pszStart Where to stop.
2722 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2723 */
2724char *findStartOfWord(const char *psz, const char *pszStart)
2725{
2726 const char *pszR = psz;
2727 while (psz >= pszStart &&
2728 (
2729 (*psz >= 'A' && *psz <= 'Z')
2730 || (*psz >= 'a' && *psz <= 'z')
2731 || (*psz >= '0' && *psz <= '9')
2732 || *psz == '_'
2733 )
2734 )
2735 pszR = psz--;
2736 return (char*)pszR;
2737}
2738#endif
2739
2740/**
2741 * Find the size of a file.
2742 * @returns Size of file. -1 on error.
2743 * @param phFile File handle.
2744 */
2745signed long fsize(FILE *phFile)
2746{
2747 int ipos;
2748 signed long cb;
2749
2750 if ((ipos = ftell(phFile)) < 0
2751 ||
2752 fseek(phFile, 0, SEEK_END) != 0
2753 ||
2754 (cb = ftell(phFile)) < 0
2755 ||
2756 fseek(phFile, ipos, SEEK_SET) != 0
2757 )
2758 cb = -1;
2759 return cb;
2760}
2761
2762
2763
2764/**
2765 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2766 * @returns Pointer to first not space or tab char in the string.
2767 * @param psz Pointer to the string which is to be trimmed.
2768 * @status completely implmented.
2769 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2770 */
2771INLINE char *trim(char *psz)
2772{
2773 int i;
2774 if (psz == NULL)
2775 return NULL;
2776 while (*psz == ' ' || *psz == '\t')
2777 psz++;
2778 i = strlen(psz) - 1;
2779 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2780 i--;
2781 psz[i+1] = '\0';
2782 return psz;
2783}
2784
2785
2786/**
2787 * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
2788 * @returns Pointer to the string passed in.
2789 * @param psz Pointer to the string which is to be right trimmed.
2790 * @status completely implmented.
2791 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2792 */
2793INLINE char *trimR(char *psz)
2794{
2795 int i;
2796 if (psz == NULL)
2797 return NULL;
2798 i = strlen(psz) - 1;
2799 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2800 i--;
2801 psz[i+1] = '\0';
2802 return psz;
2803}
2804
2805
2806/**
2807 * Trims any quotes of a possibly quoted string.
2808 * @returns Pointer to the string passed in.
2809 * @param psz Pointer to the string which is to be quote-trimmed.
2810 * @status completely implmented.
2811 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2812 */
2813INLINE char *trimQuotes(char *psz)
2814{
2815 int i;
2816 if (psz == NULL)
2817 return NULL;
2818
2819 if (*psz == '\"' || *psz == '\'')
2820 psz++;
2821 i = strlen(psz) - 1;
2822 if (psz[i] == '\"' || psz[i] == '\'')
2823 psz[i] = '\0';
2824
2825 return psz;
2826}
2827
2828
2829/**
2830 * C/C++ preprocess a single line. Assumes that we're not starting
2831 * with at comment.
2832 * @returns Pointer to output buffer.
2833 * @param pszOut Ouput (preprocessed) string.
2834 * @param pszIn Input string.
2835 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
2836 */
2837char *PreProcessLine(char *pszOut, const char *pszIn)
2838{
2839 char * psz = pszOut;
2840 BOOL fComment = FALSE;
2841 BOOL fQuote = FALSE;
2842
2843 /*
2844 * Loop thru the string.
2845 */
2846 while (*pszIn != '\0')
2847 {
2848 if (fQuote)
2849 {
2850 *psz++ = *pszIn;
2851 if (*pszIn == '\\')
2852 {
2853 *psz++ = *++pszIn;
2854 pszIn++;
2855 }
2856 else if (*pszIn++ == '"')
2857 fQuote = FALSE;
2858 }
2859 else if (fComment)
2860 {
2861 if (*pszIn == '*' && pszIn[1] == '/')
2862 {
2863 fComment = FALSE;
2864 pszIn += 2;
2865 }
2866 else
2867 pszIn++;
2868 }
2869 else
2870 {
2871 if ( (*pszIn == '/' && pszIn[1] == '/')
2872 || *pszIn == '\0')
2873 { /* End of line. */
2874 break;
2875 }
2876
2877 if (*pszIn == '/' && pszIn[1] == '*')
2878 { /* Start comment */
2879 fComment = TRUE;
2880 pszIn += 2;
2881 }
2882 else
2883 *psz++ = *pszIn++;
2884 }
2885 }
2886
2887 /*
2888 * Trim right.
2889 */
2890 psz--;
2891 while (psz >= pszOut && (*psz == ' ' || *psz == '\t'))
2892 psz--;
2893 psz[1] = '\0';
2894
2895 return pszOut;
2896}
2897
2898
2899/**
2900 * Creates a memory buffer for a text file.
2901 * @returns Pointer to file memoryblock. NULL on error.
2902 * @param pszFilename Pointer to filename string.
2903 * @remark This function is the one using most of the execution
2904 * time (DosRead + DosOpen) - about 70% of the execution time!
2905 */
2906void *textbufferCreate(const char *pszFilename)
2907{
2908 void *pvFile = NULL;
2909 FILE *phFile;
2910
2911 phFile = fopen(pszFilename, "rb");
2912 if (phFile != NULL)
2913 {
2914 signed long cbFile = fsize(phFile);
2915 if (cbFile >= 0)
2916 {
2917 pvFile = malloc(cbFile + 1);
2918 if (pvFile != NULL)
2919 {
2920 memset(pvFile, 0, cbFile + 1);
2921 if (cbFile > 0 && fread(pvFile, 1, cbFile, phFile) == 0)
2922 { /* failed! */
2923 free(pvFile);
2924 pvFile = NULL;
2925 }
2926 }
2927 else
2928 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
2929 }
2930 fclose(phFile);
2931 }
2932 return pvFile;
2933}
2934
2935
2936/**
2937 * Destroys a text textbuffer.
2938 * @param pvBuffer Buffer handle.
2939 */
2940void textbufferDestroy(void *pvBuffer)
2941{
2942 free(pvBuffer);
2943}
2944
2945
2946/**
2947 * Gets the next line from an textbuffer.
2948 * @returns Pointer to the next line.
2949 * @param pvBuffer Buffer handle.
2950 * @param psz Pointer to current line.
2951 * NULL is passed in to get the first line.
2952 */
2953char *textbufferNextLine(void *pvBuffer, register char *psz)
2954{
2955 register char ch;
2956
2957 /* if first line psz is NULL. */
2958 if (psz == NULL)
2959 return (char*)pvBuffer;
2960
2961 /* skip till end of file or end of line. */
2962 ch = *psz;
2963 while (ch != '\0' && ch != '\n' && ch != '\r')
2964 ch = *++psz;
2965
2966 /* skip line end */
2967 if (ch == '\r')
2968 ch = *++psz;
2969 if (ch == '\n')
2970 psz++;
2971
2972 return psz;
2973}
2974
2975
2976/**
2977 * Gets the next line from an textbuffer.
2978 * (fgets for textbuffer)
2979 * @returns Pointer to pszOutBuffer. NULL when end of file.
2980 * @param pvBuffer Buffer handle.
2981 * @param ppv Pointer to a buffer index pointer. (holds the current buffer index)
2982 * Pointer to a null pointer is passed in to get the first line.
2983 * @param pszLineBuffer Output line buffer. (!= NULL)
2984 * @param cchLineBuffer Size of the output line buffer. (> 0)
2985 * @remark '\n' and '\r' are removed!
2986 */
2987char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
2988{
2989 char * pszLine = pszLineBuffer;
2990 char * psz = *(char**)(void*)ppv;
2991 register char ch;
2992
2993 /* first line? */
2994 if (psz == NULL)
2995 psz = pvBuffer;
2996
2997 /* Copy to end of the line or end of the linebuffer. */
2998 ch = *psz;
2999 cchLineBuffer--; /* reserve space for '\0' */
3000 while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
3001 {
3002 *pszLine++ = ch;
3003 ch = *++psz;
3004 }
3005 *pszLine = '\0';
3006
3007 /* skip line end */
3008 if (ch == '\r')
3009 ch = *++psz;
3010 if (ch == '\n')
3011 psz++;
3012
3013 /* check if position has changed - if unchanged it's the end of file! */
3014 if (*ppv == (void*)psz)
3015 pszLineBuffer = NULL;
3016
3017 /* store current position */
3018 *ppv = (void*)psz;
3019
3020 return pszLineBuffer;
3021}
3022
3023
3024/**
3025 * Appends a depend file to the internal file.
3026 * This will update the date in the option struct.
3027 */
3028BOOL depReadFile(const char *pszFilename)
3029{
3030 void * pvFile;
3031 char * pszNext;
3032 char * pszPrev; /* Previous line, only valid when finding new rule. */
3033 BOOL fMoreDeps = FALSE;
3034 void * pvRule = NULL;
3035
3036
3037 /* read depend file */
3038 pvFile = textbufferCreate(pszFilename);
3039 if (pvFile == NULL)
3040 return FALSE;
3041
3042 /* parse the original depend file */
3043 pszPrev = NULL;
3044 pszNext = pvFile;
3045 while (*pszNext != '\0')
3046 {
3047 int i;
3048 int cch;
3049 char *psz;
3050
3051 /* get the next line. */
3052 psz = pszNext;
3053 pszNext = textbufferNextLine(pvFile, pszNext);
3054
3055 /*
3056 * Process the current line:
3057 * Start off by terminating the line.
3058 * Trim the line,
3059 * Skip empty lines.
3060 * If not looking for more deps Then
3061 * Check if new rule starts here.
3062 * Endif
3063 *
3064 * If more deps to last rule Then
3065 * Get dependant name.
3066 * Endif
3067 */
3068 i = -1;
3069 while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
3070 pszNext[i--] = '\0';
3071 trimR(psz);
3072 cch = strlen(psz);
3073 if (cch == 0)
3074 {
3075 fMoreDeps = FALSE;
3076 continue;
3077 }
3078
3079 if (*psz == '#')
3080 {
3081 pszPrev = psz;
3082 continue;
3083 }
3084
3085 /* new rule? */
3086 if (!fMoreDeps)
3087 {
3088 if (*psz != ' ' && *psz != '\t' && *psz != '\0')
3089 {
3090 i = 0;
3091 while (psz[i] != '\0')
3092 {
3093 if (psz[i] == ':'
3094 && (psz[i+1] == ' '
3095 || psz[i+1] == '\t'
3096 || psz[i+1] == '\0'
3097 || (psz[i+1] == '\\' && psz[i+2] == '\0')
3098 )
3099 )
3100 {
3101 char szTS[TS_SIZE];
3102 char * pszCont = strchr(&psz[i], '\\');
3103 fMoreDeps = pszCont != NULL && pszCont[1] == '\0';
3104
3105 /* read evt. timestamp. */
3106 szTS[0] = '\0';
3107 if (pszPrev && strlen(pszPrev) > 25 && *pszPrev == '#')
3108 strcpy(szTS, pszPrev + 2);
3109
3110 psz[i] = '\0';
3111 pvRule = depAddRule(trimQuotes(trimR(psz)), NULL, NULL, szTS);
3112 ((PDEPRULE)pvRule)->fUpdated = FALSE;
3113 psz += i + 1;
3114 cch -= i + 1;
3115 break;
3116 }
3117 i++;
3118 }
3119 }
3120 pszPrev = NULL;
3121 }
3122
3123
3124 /* more dependants */
3125 if (fMoreDeps)
3126 {
3127 if (cch > 0 && psz[cch-1] == '\\')
3128 {
3129 fMoreDeps = TRUE;
3130 psz[cch-1] = '\0';
3131 }
3132 else
3133 fMoreDeps = FALSE;
3134
3135 /* if not duplicate rule */
3136 if (pvRule != NULL)
3137 {
3138 psz = trimQuotes(trim(psz));
3139 if (*psz != '\0')
3140 depAddDepend(pvRule, psz, options.fCheckCyclic);
3141 }
3142 }
3143 } /* while */
3144
3145
3146 /* return succesfully */
3147 textbufferDestroy(pvFile);
3148 return TRUE;
3149}
3150
3151/**
3152 *
3153 * @returns Success indicator.
3154 * @params pszFilename Pointer to name of the output file.
3155 */
3156BOOL depWriteFile(const char *pszFilename)
3157{
3158 FILE *phFile;
3159 phFile = fopen(pszFilename, "w");
3160 if (phFile != NULL)
3161 {
3162 AVLENUMDATA EnumData;
3163 PDEPRULE pdep;
3164 char szBuffer[4096];
3165 int iBuffer = 0;
3166 int cch;
3167
3168 /*
3169 * Write warning on top of file.
3170 */
3171 fputs("#\n"
3172 "# This file was automatically generated by FastDep.\n"
3173 "# FastDep was written by knut st. osmundsen, and it's GPL software.\n"
3174 "#\n"
3175 "# THIS FILE SHOULD N O T BE EDITED MANUALLY!!!\n"
3176 "#\n"
3177 "# (As this may possibly make it unreadable for fastdep\n"
3178 "# and ruin the caching methods of FastDep.)\n"
3179 "#\n"
3180 "\n",
3181 phFile);
3182
3183
3184 /* @@@PH 2001-03-01
3185 * If option is selected to generate a parent
3186 * "super" dependency, enter this scope.
3187 */
3188 if (options.pszSuperDependency != NULL)
3189 {
3190 iBuffer = sprintf(szBuffer,
3191 "%s:",
3192 options.pszSuperDependency);
3193
3194 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3195 while (pdep != NULL)
3196 {
3197 char *psz = pdep->pszRule;
3198
3199 /* flush buffer? */
3200 if (iBuffer + strlen(psz) + 20 >= sizeof(szBuffer))
3201 {
3202 fwrite(szBuffer, iBuffer, 1, phFile);
3203 iBuffer = 0;
3204 }
3205
3206 /* write rule title as dependant */
3207 iBuffer += sprintf(szBuffer + iBuffer,
3208 " \\\n %s",psz);
3209
3210 /* next rule */
3211 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3212 }
3213
3214 /* Add two new lines. Flush buffer first if necessary. */
3215 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3216 {
3217 fwrite(szBuffer, iBuffer, 1, phFile);
3218 iBuffer = 0;
3219 }
3220
3221 /* add 2 linefeeds */
3222 strcpy(szBuffer + iBuffer, "\n\n");
3223 iBuffer += CBNEWLINE*2;
3224 }
3225
3226
3227 /* normal dependency output */
3228 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3229 while (pdep != NULL)
3230 {
3231 int cchTS = strlen(pdep->szTS);
3232 int fQuoted = strpbrk(pdep->pszRule, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3233
3234 /* Write rule. Flush the buffer first if necessary. */
3235 cch = strlen(pdep->pszRule);
3236 if (iBuffer + cch + fQuoted * 2 + cchTS + 9 >= sizeof(szBuffer))
3237 {
3238 fwrite(szBuffer, iBuffer, 1, phFile);
3239 iBuffer = 0;
3240 }
3241
3242 memcpy(szBuffer + iBuffer, "# ", 2);
3243 memcpy(szBuffer + iBuffer + 2, pdep->szTS, cchTS);
3244 iBuffer += cchTS + 2;
3245 szBuffer[iBuffer++] = '\n';
3246
3247 if (fQuoted) szBuffer[iBuffer++] = '"';
3248 strcpy(szBuffer + iBuffer, pdep->pszRule);
3249 iBuffer += cch;
3250 if (fQuoted) szBuffer[iBuffer++] = '"';
3251 strcpy(szBuffer + iBuffer++, ":");
3252
3253 /* write rule dependants. */
3254 if (pdep->papszDep != NULL)
3255 {
3256 char **ppsz = pdep->papszDep;
3257 while (*ppsz != NULL)
3258 {
3259 /* flush buffer? */
3260 fQuoted = strpbrk(*ppsz, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3261 cch = strlen(*ppsz);
3262 if (iBuffer + cch + fQuoted * 2 + 20 >= sizeof(szBuffer))
3263 {
3264 fwrite(szBuffer, iBuffer, 1, phFile);
3265 iBuffer = 0;
3266 }
3267 strcpy(szBuffer + iBuffer, " \\\n ");
3268 iBuffer += 7;
3269 if (fQuoted) szBuffer[iBuffer++] = '"';
3270 strcpy(szBuffer + iBuffer, *ppsz);
3271 iBuffer += cch;
3272 if (fQuoted) szBuffer[iBuffer++] = '"';
3273
3274 /* next dependant */
3275 ppsz++;
3276 }
3277 }
3278
3279 /* Add two new lines. Flush buffer first if necessary. */
3280 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3281 {
3282 fwrite(szBuffer, iBuffer, 1, phFile);
3283 iBuffer = 0;
3284 }
3285
3286 /* add 2 linefeeds */
3287 strcpy(szBuffer + iBuffer, "\n\n");
3288 iBuffer += CBNEWLINE*2;
3289
3290 /* next rule */
3291 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3292 }
3293
3294
3295 /* flush buffer. */
3296 fwrite(szBuffer, iBuffer, 1, phFile);
3297
3298 fclose(phFile);
3299 return TRUE;
3300 }
3301
3302 return FALSE;
3303}
3304
3305
3306/**
3307 * Removes all nodes in the tree of dependencies. (pdepTree)
3308 */
3309void depRemoveAll(void)
3310{
3311 AVLENUMDATA EnumData;
3312 PDEPRULE pdep;
3313
3314 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3315 while (pdep != NULL)
3316 {
3317 /* free this */
3318 if (pdep->papszDep != NULL)
3319 {
3320 char ** ppsz = pdep->papszDep;
3321 while (*ppsz != NULL)
3322 free(*ppsz++);
3323 free(pdep->papszDep);
3324 }
3325 free(pdep);
3326
3327 /* next */
3328 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3329 }
3330 pdepTree = NULL;
3331}
3332
3333
3334/**
3335 * Adds a rule to the list of dependant rules.
3336 * @returns Rule handle. NULL if rule exists/error.
3337 * @param pszRulePath Pointer to rule text. Empty strings are banned!
3338 * This string might only contain the path of the rule. (with '\\')
3339 * @param pszName Name of the rule.
3340 * NULL if pszRulePath contains the entire rule.
3341 * @param pszExt Extention (without '.')
3342 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
3343 */
3344void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS)
3345{
3346 char szRule[CCHMAXPATH*2];
3347 PDEPRULE pNew;
3348 int cch;
3349
3350 /* make rulename */
3351 strcpy(szRule, pszRulePath);
3352 cch = strlen(szRule);
3353 if (pszName != NULL)
3354 {
3355 strcpy(szRule + cch, pszName);
3356 cch += strlen(szRule + cch);
3357 }
3358 if (pszExt != NULL)
3359 {
3360 strcat(szRule + cch++, ".");
3361 strcat(szRule + cch, pszExt);
3362 cch += strlen(szRule + cch);
3363 }
3364
3365
3366 /*
3367 * Allocate a new rule structure and fill in data
3368 * Note. One block for both the DEPRULE and the pszRule string.
3369 */
3370 pNew = malloc(sizeof(DEPRULE) + cch + 1);
3371 if (pNew == NULL)
3372 {
3373 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
3374 return NULL;
3375 }
3376 pNew->pszRule = (char*)(void*)(pNew + 1);
3377 strcpy(pNew->pszRule, szRule);
3378 pNew->cDeps = 0;
3379 pNew->papszDep = NULL;
3380 pNew->fUpdated = TRUE;
3381 pNew->avlCore.Key = pNew->pszRule;
3382 strcpy(pNew->szTS, pszTS);
3383
3384 /* Insert the rule */
3385 if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
3386 { /*
3387 * The rule existed.
3388 * If it's allready touched (updated) during this session
3389 * there is nothing to be done.
3390 * If not force scan and it's newer than depfile-1month then
3391 * we'll use the information we've got.
3392 * Reuse the node in the tree.
3393 */
3394 PDEPRULE pOld = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pNew->avlCore.Key);
3395 assert(pOld);
3396 free(pNew);
3397 if (pOld->fUpdated)
3398 return NULL;
3399
3400 pOld->fUpdated = TRUE;
3401 if (!options.fForceScan && !strcmp(pOld->szTS, pszTS) && depValidate(pOld))
3402 return NULL;
3403 strcpy(pOld->szTS, pszTS);
3404
3405 if (pOld->papszDep)
3406 {
3407 free(pOld->papszDep);
3408 pOld->papszDep = NULL;
3409 }
3410 pOld->cDeps = 0;
3411
3412 return pOld;
3413 }
3414
3415 return pNew;
3416}
3417
3418
3419
3420/**
3421 * Adds a dependant to a rule.
3422 * @returns Successindicator. TRUE = success.
3423 * FALSE = cyclic or out of memory.
3424 * @param pvRule Rule handle.
3425 * @param pszDep Pointer to dependant name
3426 * @param fCheckCyclic When set we'll check that we're not creating an cyclic dependency.
3427 */
3428BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic)
3429{
3430 PDEPRULE pdep = (PDEPRULE)pvRule;
3431
3432 if (pszDep[0] == '\0')
3433 {
3434 fprintf(stderr, "warning-internal: empty dependancy filename to '%s'. Ignored.\n",
3435 pdep->pszRule);
3436 /* __interrupt(3); */
3437 return FALSE;
3438 }
3439
3440 if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
3441 {
3442 fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
3443 pszDep, pdep->pszRule);
3444 return FALSE;
3445 }
3446
3447 /* allocate more array space */
3448 if (((pdep->cDeps) % 48) == 0)
3449 {
3450 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
3451 if (pdep->papszDep == NULL)
3452 {
3453 pdep->cDeps = 0;
3454 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3455 return FALSE;
3456 }
3457 }
3458
3459 /* allocate string space and copy pszDep */
3460 if ((pdep->papszDep[pdep->cDeps] = malloc(strlen(pszDep) + 1)) == NULL)
3461 {
3462 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3463 return FALSE;
3464 }
3465 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
3466
3467 /* terminate array and increment dep count */
3468 pdep->papszDep[++pdep->cDeps] = NULL;
3469
3470 /* successful! */
3471 return TRUE;
3472}
3473
3474
3475/**
3476 * Marks the file as one which is to be rescanned next time
3477 * since not all dependencies was found...
3478 * @param pvRule Rule handle...
3479 */
3480void depMarkNotFound(void *pvRule)
3481{
3482 ((PDEPRULE)pvRule)->szTS[0] = '\0';
3483}
3484
3485
3486/**
3487 * Checks if adding this dependent will create a cylic dependency.
3488 * @returns TRUE: Cyclic.
3489 * FALSE: Non-cylic.
3490 * @param pdepRule Rule pszDep is to be inserted in.
3491 * @param pszDep Depend name.
3492 */
3493BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
3494{
3495 #define DEPTH 32
3496 char * pszRule = pdepRule->pszRule;
3497 char ** appsz[DEPTH];
3498 PDEPRULE pdep;
3499 int i;
3500
3501 /* self check */
3502 if (strcmp(pdepRule->pszRule, pszDep) == 0)
3503 return TRUE;
3504
3505 /* find rule for the dep. */
3506 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
3507 || pdep->papszDep == NULL)
3508 return FALSE; /* no rule, or no dependents, not cyclic */
3509
3510 i = 0;
3511 appsz[0] = pdep->papszDep;
3512 while (i >= 0)
3513 {
3514 register char ** ppsz = appsz[i];
3515
3516 while (*ppsz != NULL)
3517 {
3518 /* check if equal to the main rule */
3519 if (strcmp(pszRule, *ppsz) == 0)
3520 return TRUE;
3521
3522 /* push onto stack (ppsz is incremented in this test!) */
3523 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
3524 && pdep->papszDep != NULL)
3525 {
3526 if (i >= DEPTH)
3527 {
3528 fprintf(stderr, "error: too deap chain (%d). pszRule=%s pszDep=%s\n",
3529 i, pszRule, pszDep);
3530 return FALSE;
3531 }
3532 appsz[i++] = ppsz; /* save next */
3533 ppsz = pdep->papszDep; /* start processing new node */
3534 }
3535 }
3536
3537 /* pop stack */
3538 i--;
3539 }
3540
3541 return FALSE;
3542}
3543
3544
3545/**
3546 * Validates that the dependencies for the file exists
3547 * in the given locations. Dependants without path is ignored.
3548 * @returns TRUE if all ok.
3549 * FALSE if one (or possibly more) dependants are non-existing.
3550 * @param pdepRule Pointer to rule we're to validate.
3551 */
3552BOOL depValidate(PDEPRULE pdepRule)
3553{
3554 int i;
3555
3556 for (i = 0; i < pdepRule->cDeps; i++)
3557 {
3558 char *psz = pdepRule->papszDep[i];
3559 if ( psz[1] == ':'
3560 || strchr(psz, '\\')
3561 || strchr(psz, '/')
3562 )
3563 {
3564 /*
3565 * Check existance of the file.
3566 * Search cache first
3567 */
3568 if (!filecacheFind(psz))
3569 {
3570 char szDir[CCHMAXPATH];
3571
3572 filePathSlash(psz, szDir);
3573 if (!filecacheIsDirCached(szDir))
3574 {
3575 /*
3576 * If caching of entire dirs are enabled, we'll
3577 * add the directory to the cache and search it.
3578 */
3579 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
3580 {
3581 if (!filecacheFind(psz))
3582 return FALSE;
3583 }
3584 else
3585 {
3586 FILESTATUS3 fsts3;
3587
3588 /* ask the OS */
3589 if (DosQueryPathInfo(psz, FIL_STANDARD, &fsts3, sizeof(fsts3)))
3590 return FALSE;
3591 /* add file to cache. */
3592 filecacheAddFile(psz);
3593 }
3594 }
3595 }
3596 }
3597 }
3598
3599 return TRUE;
3600}
3601
3602
3603/**
3604 * Make a timestamp from the file data provided thru the
3605 * search API.
3606 * @returns Pointer to pszTS
3607 * @param pszTS Pointer to timestamp (output).
3608 * @param pfindbuf3 Pointer to search result.
3609 */
3610INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3)
3611{
3612 sprintf(pszTS, "%04d-%02d-%02d-%02d.%02d.%02d 0x%04x%04x %d",
3613 pfindbuf3->fdateLastWrite.year + 1980,
3614 pfindbuf3->fdateLastWrite.month,
3615 pfindbuf3->fdateLastWrite.day,
3616 pfindbuf3->ftimeLastWrite.hours,
3617 pfindbuf3->ftimeLastWrite.minutes,
3618 pfindbuf3->ftimeLastWrite.twosecs * 2,
3619 (ULONG)*(PUSHORT)(void*)&pfindbuf3->fdateCreation,
3620 (ULONG)*(PUSHORT)(void*)&pfindbuf3->ftimeCreation,
3621 pfindbuf3->cbFile);
3622 return pszTS;
3623}
3624
3625
3626
3627
3628
3629/*
3630 * Testing purpose.
3631 */
3632
3633#if !defined(OS2FAKE)
3634#include <os2.h>
3635#endif
3636#ifdef OLEMANN
3637#include "olemann.h"
3638#endif
Note: See TracBrowser for help on using the repository browser.