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

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

Corrected quoted include behaviour. Made it cleanup deleted files. Some buffers was increased.

File size: 116.9 KB
Line 
1/* $Id: fastdep.c,v 1.31 2001-09-30 00:22:05 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 the 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, BOOL fAppend);
237static BOOL depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly);
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, !options.fAppend))
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, options.fAppend);
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, !options.fAppend))
852 fprintf(stderr, "error: failed to write dependencies file!\n");
853 #if 0
854 printf("cfcNodes=%d\n", cfcNodes);
855 #endif
856
857 return rc;
858}
859
860
861/**
862 * Displays the syntax description for this util.
863 * @status completely implemented.
864 * @author knut st. osmundsen
865 */
866void syntax(void)
867{
868 printf(
869 "FastDep v0.41\n"
870 "Dependency scanner. Creates a makefile readable depend file.\n"
871 " - was quick and dirty, now it's just quick -\n"
872 "\n"
873 "Syntax: FastDep [options] <files> [more options [more files [...]]]\n"
874 " or\n"
875 " FastDep [options] @<parameterfile>\n"
876 "\n"
877 "Options:\n"
878 " -a<[+]|-> Append to the output file. Default: Overwrite.\n"
879 " -ca Force search directory caching.\n"
880 " Default: cache if more that 25 files are to be searched.\n"
881 " (more than 25 in the first file expression.)\n"
882 " -cy<[+]|-> Check for cylic dependencies. Default: -cy-\n"
883 " -d <outputfn> Output filename. Default: %s\n"
884 " -e excludepath Exclude paths. If a filename is found in any\n"
885 " of these paths only the filename is used, not\n"
886 " the path+filename (which is default).\n"
887 " -eall<[+]|-> Include and source filenames, paths or no paths.\n"
888 " -eall+: No path are added to the filename.\n"
889 " -eall-: The filename is appended the include path\n"
890 " was found in.\n"
891 " Default: eall-\n"
892 " -f<[+]|-> Force scanning of all files. If disabled we'll only scan\n"
893 " files which are younger or up to one month older than the\n"
894 " dependancy file (if it exists). Default: disabled\n"
895 " -i <include> Additional include paths. INCLUDE is searched after this.\n"
896 " -n<[+]|-> No path for object files in the rules.\n"
897 " -o <objdir> Path were object files are placed. This path replaces the\n"
898 " entire filename path\n"
899 " -o- No object path\n"
900 " -obr<[+]|-> -obr+: Object rule.\n"
901 " -obr-: No object rule, rule for source filename is generated.\n"
902 " -obj[ ]<objext> Object extention. Default: obj\n"
903 " -s[ ][name] Insert super-dependency on top of tree.\n"
904 " If not specified name defaults to 'alltargets'.\n"
905 " Default: disabled\n"
906 " -r[ ]<rsrcext> Resource binary extention. Default: res\n"
907 " -x[ ]<f1[;f2]> Files to exclude. Only exact filenames.\n"
908 " <files> Files to scan. Wildchars are allowed.\n"
909 "\n"
910 "Options and files could be mixed.\n"
911 " copyright (c) 1999-2001 knut st. osmundsen (knut.stange.osmundsen@mynd.no)\n",
912 pszDefaultDepFile
913 );
914}
915
916
917/**
918 * Generates depend info on this file, these are stored internally
919 * and written to file later.
920 * @returns
921 * @param pszFilename Pointer to source filename. Correct case is assumed!
922 * @param pszTS File time stamp.
923 * @status completely implemented.
924 * @author knut st. osmundsen
925 */
926int makeDependent(const char *pszFilename, const char *pszTS)
927{
928 int rc = -1;
929
930 char szExt[CCHMAXPATH];
931 PCONFIGENTRY pCfg = &aConfig[0];
932 BOOL fHeader;
933
934 /*
935 * Find which filetype this is...
936 */
937 fileExt(pszFilename, szExt);
938 while (pCfg->papszExts != NULL)
939 {
940 const char **ppsz = pCfg->papszExts;
941 while (*ppsz != NULL && stricmp(*ppsz, szExt) != 0)
942 ppsz++;
943 if (*ppsz != NULL)
944 {
945 fHeader = &pCfg->papszExts[pCfg->iFirstHdr] <= ppsz;
946 break;
947 }
948 pCfg++;
949 }
950
951 /* Found? */
952 if (pCfg->papszExts != NULL)
953 {
954 char szNormFile[CCHMAXPATH];
955 fileNormalize2(pszFilename, szNormFile);
956 rc = (*pCfg->pfn)(pszFilename, &szNormFile[0], pszTS, fHeader);
957 }
958 else
959 {
960 if (*fileName(pszFilename, szExt) != '.') /* these are 'hidden' files, like .cvsignore, let's ignore them. */
961 fprintf(stderr, "warning: '%s' has an unknown file type.\n", pszFilename);
962 rc = 0;
963 }
964
965
966 return rc;
967}
968
969
970/**
971 * Generates depend info on this C or C++ file, these are stored internally
972 * and written to file later.
973 * @returns 0 on success.
974 * !0 on error.
975 * @param pszFilename Pointer to source filename. Correct case is assumed!
976 * @param pszNormFilename Pointer to normalized source filename.
977 * @param pszTS File time stamp.
978 * @parma fHeader True if header file is being scanned.
979 * @status completely implemented.
980 * @author knut st. osmundsen
981 */
982int langC_CPP(const char *pszFilename, const char *pszNormFilename,
983 const char *pszTS, BOOL fHeader)
984{
985 void * pvFile; /* Text buffer pointer. */
986 void * pvRule; /* Handle to the current rule. */
987 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
988 int iLine; /* Linenumber. */
989 void * pv = NULL; /* An index used by textbufferGetNextLine. */
990 BOOL fComment; /* TRUE when within a multiline comment. */
991 /* FALSE when not within a multiline comment. */
992 int iIfStack; /* StackPointer. */
993 struct IfStackEntry
994 {
995 int fIncluded : 1; /* TRUE: include this code;
996 * FALSE: excluded */
997 int fIf : 1; /* TRUE: #if part of the expression.
998 * FALSE: #else part of the expression. */
999 int fSupported : 1; /* TRUE: supported if/else statement
1000 * FALSE: unsupported all else[<something>] are ignored
1001 * All code is included.
1002 */
1003 } achIfStack[256];
1004 char szSrcDir[CCHMAXPATH];
1005 filePath(pszNormFilename, szSrcDir);/* determin source code directory. */
1006
1007 /**********************************/
1008 /* Add the depend rule */
1009 /**********************************/
1010 if (options.fObjRule && !fHeader)
1011 {
1012 if (options.fNoObjectPath)
1013 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS);
1014 else
1015 pvRule = depAddRule(options.fObjectDir ?
1016 options.pszObjectDir :
1017 filePathSlash(pszFilename, szBuffer),
1018 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1019 options.pszObjectExt,
1020 pszTS);
1021
1022 if (options.fSrcWhenObj && pvRule)
1023 depAddDepend(pvRule,
1024 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1025 fileName(pszFilename, szBuffer) : pszNormFilename,
1026 options.fCheckCyclic);
1027 }
1028 else
1029 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1030 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
1031
1032 /* duplicate rule? */
1033 if (pvRule == NULL)
1034 return 0;
1035
1036
1037 /********************/
1038 /* Make file buffer */
1039 /********************/
1040 pvFile = textbufferCreate(pszFilename);
1041 if (!pvFile)
1042 {
1043 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1044 return -1;
1045 }
1046
1047
1048 /*******************/
1049 /* find dependants */
1050 /*******************/
1051 /* Initiate the IF-stack, comment state and line number. */
1052 iIfStack = 0;
1053 achIfStack[iIfStack].fIf = TRUE;
1054 achIfStack[iIfStack].fIncluded = TRUE;
1055 achIfStack[iIfStack].fSupported = TRUE;
1056 fComment = FALSE;
1057 iLine = 0;
1058 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1059 {
1060 /* search for #include */
1061 register char *pszC;
1062 int cbLen;
1063 int i = 0;
1064 iLine++;
1065
1066 /* skip blank chars */
1067 cbLen = strlen(szBuffer);
1068 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1069 i++;
1070
1071 /* preprocessor statement? */
1072 if (!fComment && szBuffer[i] == '#')
1073 {
1074 /*
1075 * Preprocessor checks
1076 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1077 * Depending on the word afterwards we'll take some different actions.
1078 * So we'll start of by extracting that word and make a string swich on it.
1079 * Note that there might be some blanks between the hash and the word.
1080 */
1081 int cchWord;
1082 char * pszEndWord;
1083 char * pszArgument;
1084 i++; /* skip hash ('#') */
1085 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1086 i++;
1087 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1088 cchWord = pszEndWord - &szBuffer[i];
1089
1090 /*
1091 * Find the argument by skipping the blanks.
1092 */
1093 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1094 pszArgument++;
1095
1096 /*
1097 * string switch.
1098 */
1099 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1100 {
1101 /*
1102 * #include
1103 *
1104 * Are we in a state where this file is to be included?
1105 */
1106 if (achIfStack[iIfStack].fIncluded)
1107 {
1108 char szFullname[CCHMAXPATH];
1109 char * psz;
1110 BOOL f = FALSE;
1111 int j;
1112 BOOL fQuote;
1113
1114 /* extract info between "" or <> */
1115 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1116 i++;
1117 fQuote = szBuffer[i] == '"';
1118 i++; /* skip '"' or '<' */
1119
1120 /* if invalid statement then continue with the next line! */
1121 if (!f) continue;
1122
1123 /* find end */
1124 j = f = 0;
1125 while (i + j < cbLen && j < CCHMAXPATH &&
1126 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1127 j++;
1128
1129 /* if invalid statement then continue with the next line! */
1130 if (!f) continue;
1131
1132 /* copy filename */
1133 strncpy(szFullname, &szBuffer[i], j);
1134 szFullname[j] = '\0'; /* ensure terminatition. */
1135 strlwr(szFullname);
1136
1137 /* find include file! */
1138 psz = fQuote ? pathlistFindFile(szSrcDir, szFullname, szBuffer) : NULL;
1139 if (psz == NULL)
1140 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1141 if (psz == NULL)
1142 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1143
1144 /* did we find the include? */
1145 if (psz != NULL)
1146 {
1147 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1148 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1149 else
1150 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
1151 }
1152 else
1153 {
1154 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1155 pszFilename, iLine, szFullname);
1156 depMarkNotFound(pvRule);
1157 }
1158 }
1159 }
1160 else
1161 /*
1162 * #if
1163 */
1164 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1165 { /* #if 0 and #if <1-9> are supported */
1166 pszEndWord = findEndOfWord(pszArgument);
1167 iIfStack++;
1168 if ((pszEndWord - pszArgument) == 1
1169 && *pszArgument >= '0' && *pszArgument <= '9')
1170 {
1171 if (*pszArgument != '0')
1172 achIfStack[iIfStack].fIncluded = TRUE;
1173 else
1174 achIfStack[iIfStack].fIncluded = FALSE;
1175 }
1176 else
1177 achIfStack[iIfStack].fSupported = FALSE;
1178 achIfStack[iIfStack].fIncluded = TRUE;
1179 achIfStack[iIfStack].fIf = TRUE;
1180 }
1181 else
1182 /*
1183 * #else
1184 */
1185 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1186 {
1187 if (achIfStack[iIfStack].fSupported)
1188 {
1189 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1190 achIfStack[iIfStack].fIncluded = FALSE;
1191 else
1192 achIfStack[iIfStack].fIncluded = TRUE;
1193 }
1194 achIfStack[iIfStack].fIf = FALSE;
1195 }
1196 else
1197 /*
1198 * #endif
1199 */
1200 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1201 { /* Pop the if-stack. */
1202 if (iIfStack > 0)
1203 iIfStack--;
1204 else
1205 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1206 }
1207 /*
1208 * general if<something> and elseif<something> implementations
1209 */
1210 else
1211 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1212 {
1213 achIfStack[iIfStack].fSupported = FALSE;
1214 achIfStack[iIfStack].fIncluded = TRUE;
1215 }
1216 else
1217 if (strncmp(&szBuffer[i], "if", 2) == 0)
1218 {
1219 iIfStack++;
1220 achIfStack[iIfStack].fIf = TRUE;
1221 achIfStack[iIfStack].fSupported = FALSE;
1222 achIfStack[iIfStack].fIncluded = TRUE;
1223 }
1224 /* The rest of them aren't implemented yet.
1225 else if (strncmp(&szBuffer[i], "if") == 0)
1226 {
1227 }
1228 */
1229 }
1230
1231 /*
1232 * Comment checks.
1233 * -Start at first non-blank.
1234 * -Loop thru the line since we might have more than one
1235 * comment statement on a single line.
1236 */
1237 pszC = &szBuffer[i];
1238 while (pszC != NULL && *pszC != '\0')
1239 {
1240 if (fComment)
1241 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
1242 else
1243 {
1244 char *pszLC;
1245 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
1246 pszC = strstr(pszC, "/*"); /* look for start comment mark */
1247 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
1248 break; /* muliline comment mark we'll ignore the multiline mark. */
1249 }
1250
1251 /* Comment mark found? */
1252 if (pszC != NULL)
1253 {
1254 fComment = !fComment;
1255 pszC += 2; /* skip comment mark */
1256
1257 /* debug */
1258 /*
1259 if (fComment)
1260 fprintf(stderr, "starts at line %d\n", iLine);
1261 else
1262 fprintf(stderr, "ends at line %d\n", iLine);
1263 */
1264 }
1265 }
1266 } /*while*/
1267
1268 textbufferDestroy(pvFile);
1269
1270 return 0;
1271}
1272
1273
1274/**
1275 * Generates depend info on this file, these are stored internally
1276 * and written to file later.
1277 * @returns 0 on success.
1278 * !0 on error.
1279 * @param pszFilename Pointer to source filename. Correct case is assumed!
1280 * @param pszNormFilename Pointer to normalized source filename.
1281 * @param pszTS File time stamp.
1282 * @parma fHeader True if header file is being scanned.
1283 * @status completely implemented.
1284 * @author knut st. osmundsen
1285 */
1286int langAsm(const char *pszFilename, const char *pszNormFilename,
1287 const char *pszTS, BOOL fHeader)
1288{
1289 void * pvFile; /* Text buffer pointer. */
1290 void * pvRule; /* Handle to the current rule. */
1291 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1292 int iLine; /* current line number */
1293 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1294
1295
1296 /**********************************/
1297 /* Add the depend rule */
1298 /**********************************/
1299 if (options.fObjRule && !fHeader)
1300 {
1301 if (options.fNoObjectPath)
1302 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS);
1303 else
1304 pvRule = depAddRule(options.fObjectDir ?
1305 options.pszObjectDir :
1306 filePathSlash(pszFilename, szBuffer),
1307 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1308 options.pszObjectExt,
1309 pszTS);
1310
1311 if (options.fSrcWhenObj && pvRule)
1312 depAddDepend(pvRule,
1313 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1314 fileName(pszFilename, szBuffer) : pszNormFilename,
1315 options.fCheckCyclic);
1316 }
1317 else
1318 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1319 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
1320
1321 /* duplicate rule? */
1322 if (pvRule == NULL)
1323 return 0;
1324
1325
1326 /********************/
1327 /* Make file buffer */
1328 /********************/
1329 pvFile = textbufferCreate(pszFilename);
1330 if (!pvFile)
1331 {
1332 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1333 return -1;
1334 }
1335
1336
1337 /*******************/
1338 /* find dependants */
1339 /*******************/
1340 iLine = 0;
1341 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1342 {
1343 /* search for include */
1344 int cbLen;
1345 int i = 0;
1346 iLine++;
1347
1348 /* skip blank chars */
1349 cbLen = strlen(szBuffer);
1350 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1351 i++;
1352
1353 /* is this an include? */
1354 if (strnicmp(&szBuffer[i], "include", 7) == 0
1355 && (szBuffer[i + 7] == '\t' || szBuffer[i + 7] == ' ')
1356 )
1357 {
1358 char szFullname[CCHMAXPATH];
1359 char *psz;
1360 int j;
1361
1362 /* skip to first no blank char */
1363 i += 7;
1364 while (i < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1365 i++;
1366
1367 /* comment check - if comment found, no filename was given. continue. */
1368 if (szBuffer[i] == ';') continue;
1369
1370 /* find end */
1371 j = 0;
1372 while (i + j < cbLen
1373 && j < CCHMAXPATH
1374 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t' && szBuffer[i+j] != '\n'
1375 && szBuffer[i+j] != '\0' && szBuffer[i+j] != ';' && szBuffer[i+j] != '\r'
1376 )
1377 j++;
1378
1379 /* copy filename */
1380 strncpy(szFullname, &szBuffer[i], j);
1381 szFullname[j] = '\0'; /* ensure terminatition. */
1382 strlwr(szFullname);
1383
1384 /* find include file! */
1385 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1386 if (psz == NULL)
1387 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1388
1389 /* Did we find the include? */
1390 if (psz != NULL)
1391 {
1392 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1393 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1394 else
1395 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
1396 }
1397 else
1398 {
1399 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1400 pszFilename, iLine, szFullname);
1401 depMarkNotFound(pvRule);
1402 }
1403 }
1404 } /*while*/
1405
1406 textbufferDestroy(pvFile);
1407
1408 return 0;
1409}
1410
1411
1412/**
1413 * Generates depend info on this Resource file, these are stored internally
1414 * and written to file later.
1415 * @returns 0 on success.
1416 * !0 on error.
1417 * @param pszFilename Pointer to source filename. Correct case is assumed!
1418 * @param pszNormFilename Pointer to normalized source filename.
1419 * @param pszTS File time stamp.
1420 * @parma fHeader True if header file is being scanned.
1421 * @status completely implemented.
1422 * @author knut st. osmundsen
1423 */
1424#if 0
1425int langRC(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader)
1426{
1427 void * pvFile; /* Text buffer pointer. */
1428 void * pvRule; /* Handle to the current rule. */
1429 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1430 int iLine; /* current line number */
1431 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1432
1433
1434 /**********************************/
1435 /* Add the depend rule */
1436 /**********************************/
1437 if (options.fObjRule && !fHeader)
1438 {
1439 if (options.fNoObjectPath)
1440 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS);
1441 else
1442 pvRule = depAddRule(options.fObjectDir ?
1443 options.pszObjectDir :
1444 filePathSlash(pszFilename, szBuffer),
1445 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1446 options.pszRsrcExt,
1447 pszTS);
1448
1449 if (options.fSrcWhenObj && pvRule)
1450 depAddDepend(pvRule,
1451 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1452 fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1453 options.fCheckCyclic);
1454 }
1455 else
1456 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1457 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL,
1458 pszTS);
1459
1460 /* duplicate rule? */
1461 if (pvRule == NULL)
1462 return 0;
1463
1464
1465 /********************/
1466 /* Make file buffer */
1467 /********************/
1468 pvFile = textbufferCreate(pszFilename);
1469 if (!pvFile)
1470 {
1471 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1472 return -1;
1473 }
1474
1475
1476 /*******************/
1477 /* find dependants */
1478 /*******************/
1479 iLine = 0;
1480 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1481 {
1482 /* search for #include */
1483 int cbLen;
1484 int i = 0;
1485 int i1;
1486 iLine++;
1487
1488 /* skip blank chars */
1489 cbLen = strlen(szBuffer);
1490 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1491 i++;
1492
1493 /* is this an include? */
1494 i1 = 1;
1495 if ( strncmp(&szBuffer[i], "#include", 8) == 0
1496 || (i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9)) == 0
1497 || strnicmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
1498 )
1499 {
1500 char szFullname[CCHMAXPATH];
1501 char *psz;
1502 BOOL f = FALSE;
1503 int j;
1504
1505 if (i1 != 0)
1506 { /*
1507 * #include <file.h>, #include "file.h" or DLGINCLUDE 1 "file.h"
1508 *
1509 * extract info between "" or <>
1510 */
1511 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1512 i++;
1513 i++; /* skip '"' or '<' */
1514
1515 /* if invalid statement then continue with the next line! */
1516 if (!f) continue;
1517
1518 /* find end */
1519 j = f = 0;
1520 while (i + j < cbLen && j < CCHMAXPATH &&
1521 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1522 j++;
1523
1524 /* if invalid statement then continue with the next line! */
1525 if (!f) continue;
1526 }
1527 else
1528 { /*
1529 * RCINCLUDE ["]filename.dlg["]
1530 * Extract filename.
1531 */
1532
1533 /* skip to filename.dlg start - if eol will continue to loop. */
1534 i += 9;
1535 while (szBuffer[i] == ' ' || szBuffer[i] == '\t' || szBuffer[i] == '"')
1536 i++;
1537 if (szBuffer[i] == '\0')
1538 continue;
1539
1540 /* search to end of filename. */
1541 j = i+1;
1542 while ( szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1543 && szBuffer[i+j] != '"' && szBuffer[i+j] != '\0')
1544 j++;
1545 }
1546
1547 /* copy filename */
1548 strncpy(szFullname, &szBuffer[i], j);
1549 szFullname[j] = '\0'; /* ensure terminatition. */
1550 strlwr(szFullname);
1551
1552 /* find include file! */
1553 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1554 if (psz == NULL)
1555 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1556
1557 /* did we find the include? */
1558 if (psz != NULL)
1559 {
1560 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1561 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1562 else
1563 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
1564 }
1565 else
1566 {
1567 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1568 pszFilename, iLine, szFullname);
1569 depMarkNotFound(pvRule);
1570 }
1571 }
1572 } /*while*/
1573
1574 textbufferDestroy(pvFile);
1575 return 0;
1576}
1577#else
1578int langRC(const char *pszFilename, const char *pszNormFilename,
1579 const char *pszTS, BOOL fHeader)
1580{
1581 void * pvFile; /* Text buffer pointer. */
1582 void * pvRule; /* Handle to the current rule. */
1583 char szBuffer[4096]; /* Max line length is 4096... should not be a problem. */
1584 int iLine; /* Linenumber. */
1585 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1586 BOOL fComment; /* TRUE when within a multiline comment. */
1587 /* FALSE when not within a multiline comment. */
1588 int iIfStack; /* StackPointer. */
1589 struct IfStackEntry
1590 {
1591 int fIncluded : 1; /* TRUE: include this code;
1592 * FALSE: excluded */
1593 int fIf : 1; /* TRUE: #if part of the expression.
1594 * FALSE: #else part of the expression. */
1595 int fSupported : 1; /* TRUE: supported if/else statement
1596 * FALSE: unsupported all else[<something>] are ignored
1597 * All code is included.
1598 */
1599 } achIfStack[256];
1600
1601
1602 /**********************************/
1603 /* Add the depend rule */
1604 /**********************************/
1605 if (options.fObjRule && !fHeader)
1606 {
1607 if (options.fNoObjectPath)
1608 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszRsrcExt, pszTS);
1609 else
1610 pvRule = depAddRule(options.fObjectDir ?
1611 options.pszObjectDir :
1612 filePathSlash(pszFilename, szBuffer),
1613 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1614 options.pszRsrcExt,
1615 pszTS);
1616
1617 if (options.fSrcWhenObj && pvRule)
1618 depAddDepend(pvRule,
1619 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1620 fileName(pszFilename, szBuffer) : pszNormFilename,
1621 options.fCheckCyclic);
1622 }
1623 else
1624 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1625 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
1626
1627 /* duplicate rule? */
1628 if (pvRule == NULL)
1629 return 0;
1630
1631
1632 /********************/
1633 /* Make file buffer */
1634 /********************/
1635 pvFile = textbufferCreate(pszFilename);
1636 if (!pvFile)
1637 {
1638 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1639 return -1;
1640 }
1641
1642
1643 /*******************/
1644 /* find dependants */
1645 /*******************/
1646 /* Initiate the IF-stack, comment state and line number. */
1647 iIfStack = 0;
1648 achIfStack[iIfStack].fIf = TRUE;
1649 achIfStack[iIfStack].fIncluded = TRUE;
1650 achIfStack[iIfStack].fSupported = TRUE;
1651 fComment = FALSE;
1652 iLine = 0;
1653 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1654 {
1655 register char * pszC;
1656 char szFullname[CCHMAXPATH];
1657 int cbLen;
1658 int i1 = 1;
1659 int i = 0;
1660 iLine++;
1661
1662 /* skip blank chars */
1663 cbLen = strlen(szBuffer);
1664 while (i + 2 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1665 i++;
1666
1667 /* preprocessor statement? */
1668 if (!fComment && szBuffer[i] == '#')
1669 {
1670 /*
1671 * Preprocessor checks
1672 * We known that we have a preprocessor statment (starting with an '#' * at szBuffer[i]).
1673 * Depending on the word afterwards we'll take some different actions.
1674 * So we'll start of by extracting that word and make a string swich on it.
1675 * Note that there might be some blanks between the hash and the word.
1676 */
1677 int cchWord;
1678 char * pszEndWord;
1679 char * pszArgument;
1680 i++; /* skip hash ('#') */
1681 while (szBuffer[i] == '\t' || szBuffer[i] == ' ') /* skip blanks */
1682 i++;
1683 pszArgument = pszEndWord = findEndOfWord(&szBuffer[i]);
1684 cchWord = pszEndWord - &szBuffer[i];
1685
1686 /*
1687 * Find the argument by skipping the blanks.
1688 */
1689 while (*pszArgument == '\t' || *pszArgument == ' ') /* skip blanks */
1690 pszArgument++;
1691
1692 /*
1693 * string switch.
1694 */
1695 if (strncmp(&szBuffer[i], "include", cchWord) == 0)
1696 {
1697 /*
1698 * #include
1699 *
1700 * Are we in a state where this file is to be included?
1701 */
1702 if (achIfStack[iIfStack].fIncluded)
1703 {
1704 char *psz;
1705 BOOL f = FALSE;
1706 int j;
1707
1708 /* extract info between "" or <> */
1709 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1710 i++;
1711 i++; /* skip '"' or '<' */
1712
1713 /* if invalid statement then continue with the next line! */
1714 if (!f) continue;
1715
1716 /* find end */
1717 j = f = 0;
1718 while (i + j < cbLen && j < CCHMAXPATH &&
1719 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1720 j++;
1721
1722 /* if invalid statement then continue with the next line! */
1723 if (!f) continue;
1724
1725 /* copy filename */
1726 strncpy(szFullname, &szBuffer[i], j);
1727 szFullname[j] = '\0'; /* ensure terminatition. */
1728 strlwr(szFullname);
1729
1730 /* find include file! */
1731 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
1732 if (psz == NULL)
1733 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer);
1734
1735 /* did we find the include? */
1736 if (psz != NULL)
1737 {
1738 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
1739 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1740 else
1741 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
1742 }
1743 else
1744 {
1745 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1746 pszFilename, iLine, szFullname);
1747 depMarkNotFound(pvRule);
1748 }
1749 }
1750 }
1751 else
1752 /*
1753 * #if
1754 */
1755 if (strncmp(&szBuffer[i], "if", cchWord) == 0)
1756 { /* #if 0 and #if <1-9> are supported */
1757 pszEndWord = findEndOfWord(pszArgument);
1758 iIfStack++;
1759 if ((pszEndWord - pszArgument) == 1
1760 && *pszArgument >= '0' && *pszArgument <= '9')
1761 {
1762 if (*pszArgument != '0')
1763 achIfStack[iIfStack].fIncluded = TRUE;
1764 else
1765 achIfStack[iIfStack].fIncluded = FALSE;
1766 }
1767 else
1768 achIfStack[iIfStack].fSupported = FALSE;
1769 achIfStack[iIfStack].fIncluded = TRUE;
1770 achIfStack[iIfStack].fIf = TRUE;
1771 }
1772 else
1773 /*
1774 * #else
1775 */
1776 if (strncmp(&szBuffer[i], "else", cchWord) == 0)
1777 {
1778 if (achIfStack[iIfStack].fSupported)
1779 {
1780 if (achIfStack[iIfStack].fIncluded) /* ARG!! this'll prevent warning */
1781 achIfStack[iIfStack].fIncluded = FALSE;
1782 else
1783 achIfStack[iIfStack].fIncluded = TRUE;
1784 }
1785 achIfStack[iIfStack].fIf = FALSE;
1786 }
1787 else
1788 /*
1789 * #endif
1790 */
1791 if (strncmp(&szBuffer[i], "endif", cchWord) == 0)
1792 { /* Pop the if-stack. */
1793 if (iIfStack > 0)
1794 iIfStack--;
1795 else
1796 fprintf(stderr, "%s(%d): If-Stack underflow!\n", pszFilename, iLine);
1797 }
1798 /*
1799 * general if<something> and elseif<something> implementations
1800 */
1801 else
1802 if (strncmp(&szBuffer[i], "elseif", 6) == 0)
1803 {
1804 achIfStack[iIfStack].fSupported = FALSE;
1805 achIfStack[iIfStack].fIncluded = TRUE;
1806 }
1807 else
1808 if (strncmp(&szBuffer[i], "if", 2) == 0)
1809 {
1810 iIfStack++;
1811 achIfStack[iIfStack].fIf = TRUE;
1812 achIfStack[iIfStack].fSupported = FALSE;
1813 achIfStack[iIfStack].fIncluded = TRUE;
1814 }
1815 /* The rest of them aren't implemented yet.
1816 else if (strncmp(&szBuffer[i], "if") == 0)
1817 {
1818 }
1819 */
1820 } else
1821 /*
1822 * Check for resource compiler directives.
1823 */
1824 if ( !fComment
1825 && !strchr(&szBuffer[i], ',')
1826 && ( !strnicmp(&szBuffer[i], "ICON", 4)
1827 || !strnicmp(&szBuffer[i], "FONT", 4)
1828 || !strnicmp(&szBuffer[i], "BITMAP", 6)
1829 || !strnicmp(&szBuffer[i], "POINTER", 7)
1830 || !strnicmp(&szBuffer[i], "RESOURCE", 8)
1831 || !(i1 = strnicmp(&szBuffer[i], "RCINCLUDE", 9))
1832 /*|| !strnicmp(&szBuffer[i], "DLGINCLUDE", 10) - only used by the dlgeditor */
1833 || !strnicmp(&szBuffer[i], "DEFAULTICON", 11)
1834 )
1835 )
1836 {
1837 /*
1838 * RESOURCE 123 1 ["]filename.ext["]
1839 */
1840 char szLine[1024];
1841 char * pszFilename;
1842 char chQuote = ' ';
1843
1844 PreProcessLine(szLine, &szBuffer[i]);
1845
1846 pszFilename = &szLine[strlen(szLine)-1];
1847 if (*pszFilename == '\"' || *pszFilename == '\'')
1848 {
1849 chQuote = *pszFilename;
1850 *pszFilename-- = '\0';
1851 }
1852 while (*pszFilename != chQuote)
1853 pszFilename--;
1854 *pszFilename++ = '\0'; /* We now have extracted the filename - pszFilename. */
1855 strlwr(pszFilename);
1856
1857 /* Add filename to the dependencies. */
1858 if (i1)
1859 depAddDepend(pvRule, pszFilename, options.fCheckCyclic);
1860 else
1861 {
1862 char *psz;
1863 /* find include file! */
1864 psz = pathlistFindFile(options.pszInclude, pszFilename, szFullname);
1865 if (psz == NULL)
1866 psz = pathlistFindFile(pszIncludeEnv, pszFilename, szFullname);
1867
1868 /* did we find the include? */
1869 if (psz != NULL)
1870 {
1871 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szFullname))
1872 depAddDepend(pvRule, pszFilename, options.fCheckCyclic);
1873 else
1874 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
1875 }
1876 else
1877 {
1878 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1879 pszFilename, iLine, pszFilename);
1880 depMarkNotFound(pvRule);
1881 }
1882 }
1883 }
1884
1885
1886 /*
1887 * Comment checks.
1888 * -Start at first non-blank.
1889 * -Loop thru the line since we might have more than one
1890 * comment statement on a single line.
1891 */
1892 pszC = &szBuffer[i];
1893 while (pszC != NULL && *pszC != '\0')
1894 {
1895 if (fComment)
1896 pszC = strstr(pszC, "*/"); /* look for end comment mark. */
1897 else
1898 {
1899 char *pszLC;
1900 pszLC= strstr(pszC, "//"); /* look for single line comment mark. */
1901 pszC = strstr(pszC, "/*"); /* look for start comment mark */
1902 if (pszLC && pszLC < pszC) /* if there is an single line comment mark before the */
1903 break; /* muliline comment mark we'll ignore the multiline mark. */
1904 }
1905
1906 /* Comment mark found? */
1907 if (pszC != NULL)
1908 {
1909 fComment = !fComment;
1910 pszC += 2; /* skip comment mark */
1911
1912 /* debug */
1913 /*
1914 if (fComment)
1915 fprintf(stderr, "starts at line %d\n", iLine);
1916 else
1917 fprintf(stderr, "ends at line %d\n", iLine);
1918 */
1919 }
1920 }
1921 } /*while*/
1922
1923 textbufferDestroy(pvFile);
1924
1925 return 0;
1926}
1927#endif
1928
1929
1930/**
1931 * Generates depend info on this COBOL file, these are stored internally
1932 * and written to file later.
1933 * @returns 0 on success.
1934 * !0 on error.
1935 * @param pszFilename Pointer to source filename. Correct case is assumed!
1936 * @param pszNormFilename Pointer to normalized source filename.
1937 * @param pszTS File time stamp.
1938 * @parma fHeader True if header file is being scanned.
1939 * @status completely implemented.
1940 * @author knut st. osmundsen
1941 */
1942int langCOBOL(const char *pszFilename, const char *pszNormFilename,
1943 const char *pszTS, BOOL fHeader)
1944{
1945 void * pvFile; /* Text buffer pointer. */
1946 void * pvRule; /* Handle to the current rule. */
1947 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1948 int iLine; /* current line number */
1949 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1950
1951
1952 /**********************************/
1953 /* Add the depend rule */
1954 /**********************************/
1955 if (options.fObjRule && !fHeader)
1956 {
1957 if (options.fNoObjectPath)
1958 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, options.pszObjectExt, pszTS);
1959 else
1960 pvRule = depAddRule(options.fObjectDir ?
1961 options.pszObjectDir :
1962 filePathSlash(pszFilename, szBuffer),
1963 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1964 options.pszObjectExt,
1965 pszTS);
1966
1967 if (options.fSrcWhenObj && pvRule)
1968 depAddDepend(pvRule,
1969 options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename)
1970 ? fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1971 options.fCheckCyclic);
1972 }
1973 else
1974 pvRule = depAddRule(options.fExcludeAll || pathlistFindFile2(options.pszExclude, pszNormFilename) ?
1975 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL, pszTS);
1976
1977 /* duplicate rule? */
1978 if (pvRule == NULL)
1979 return 0;
1980
1981
1982 /********************/
1983 /* Make file buffer */
1984 /********************/
1985 pvFile = textbufferCreate(pszFilename);
1986 if (!pvFile)
1987 {
1988 fprintf(stderr, "failed to open '%s'\n", pszFilename);
1989 return -1;
1990 }
1991
1992
1993 /*******************/
1994 /* find dependants */
1995 /*******************/
1996 iLine = 0;
1997 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1998 {
1999 /* search for #include */
2000 int cbLen;
2001 int i = 0;
2002 int i1, i2;
2003 iLine++;
2004
2005 /* check for comment mark (column 7) */
2006 if (szBuffer[6] == '*')
2007 continue;
2008
2009 /* skip blank chars */
2010 cbLen = strlen(szBuffer);
2011 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
2012 i++;
2013
2014 /* is this an include? */
2015 if ( (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
2016 || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
2017 )
2018 {
2019 char szFullname[CCHMAXPATH];
2020 char *psz;
2021 int j;
2022
2023 /* skip statement */
2024 i += 4;
2025 if (i1 != 0)
2026 {
2027 int y = 2; /* skip two words */
2028 do
2029 {
2030 /* skip blanks */
2031 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2032 i++;
2033 /* skip word */
2034 while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
2035 && szBuffer[i] != '\0' && szBuffer[i] != '\n')
2036 i++;
2037 y--;
2038 } while (y > 0);
2039 }
2040
2041 /* check for blank */
2042 if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
2043 continue;
2044
2045 /* skip blanks */
2046 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
2047 i++;
2048
2049 /* if invalid statement then continue with the next line! */
2050 if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
2051 continue;
2052
2053 /* find end */
2054 j = 0;
2055 while (i + j < cbLen && j < CCHMAXPATH
2056 && szBuffer[i+j] != '.'
2057 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
2058 && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
2059 )
2060 j++;
2061
2062 /* if invalid statement then continue with the next line! */
2063 if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
2064 continue;
2065
2066 /* copy filename */
2067 strncpy(szFullname, &szBuffer[i], j);
2068 szFullname[j] = '\0'; /* ensure terminatition. */
2069 strlwr(szFullname);
2070
2071 /* add extention .cpy - hardcoded for the moment. */
2072 strcpy(&szFullname[j], ".cpy");
2073
2074 /* find include file! */
2075 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2076 if (!psz)
2077 {
2078 strcpy(&szFullname[j], ".cbl");
2079 psz = pathlistFindFile(options.pszInclude, szFullname, szBuffer);
2080 }
2081
2082 /* did we find the include? */
2083 if (psz != NULL)
2084 {
2085 if (options.fExcludeAll || pathlistFindFile2(options.pszExclude, szBuffer))
2086 depAddDepend(pvRule, szFullname, options.fCheckCyclic);
2087 else
2088 depAddDepend(pvRule, szBuffer, options.fCheckCyclic);
2089 }
2090 else
2091 {
2092 szFullname[j] = '\0';
2093 fprintf(stderr, "%s(%d): warning copybook '%s' was not found!\n",
2094 pszFilename, iLine, szFullname);
2095 depMarkNotFound(pvRule);
2096 }
2097 }
2098 } /*while*/
2099
2100 textbufferDestroy(pvFile);
2101
2102 return 0;
2103}
2104
2105#define upcase(ch) \
2106 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
2107
2108/**
2109 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
2110 * @returns 0 equal. (same as strnicmp)
2111 * @param pszS1 String 1
2112 * @param pszS2 String 2
2113 * @param cch Length to compare (relative to string 1)
2114 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2115 */
2116int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
2117{
2118 do
2119 {
2120 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
2121 pszS1++, pszS2++, cch--;
2122
2123 /* blank test and skipping */
2124 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
2125 {
2126 while (cch > 0 && *pszS1 == ' ')
2127 pszS1++, cch--;
2128
2129 while (*pszS2 == ' ')
2130 pszS2++;
2131 }
2132 else
2133 break;
2134 } while (cch > 0);
2135
2136 return cch == 0 ? 0 : *pszS1 - *pszS2;
2137}
2138
2139
2140/**
2141 * Normalizes the path slashes for the filename. It will partially expand paths too.
2142 * @returns pszFilename
2143 * @param pszFilename Pointer to filename string. Not empty string!
2144 * Much space to play with.
2145 */
2146char *fileNormalize(char *pszFilename)
2147{
2148 char *psz = pszFilename;
2149
2150 /* correct slashes */
2151 while ((pszFilename = strchr(pszFilename, '//')) != NULL)
2152 *pszFilename++ = '\\';
2153
2154 /* expand path? */
2155 pszFilename = psz;
2156 if (pszFilename[1] != ':')
2157 { /* relative path */
2158 int iSlash;
2159 char szFile[CCHMAXPATH];
2160 char * psz = szFile;
2161
2162 strcpy(szFile, pszFilename);
2163 iSlash = *psz == '\\' ? 1 : cSlashes;
2164 while (*psz != '\0')
2165 {
2166 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
2167 { /* up one directory */
2168 if (iSlash > 0)
2169 iSlash--;
2170 psz += 3;
2171 }
2172 else if (*psz == '.' && psz[1] == '\\')
2173 { /* no change */
2174 psz += 2;
2175 }
2176 else
2177 { /* completed expantion! */
2178 strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
2179 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
2180 break;
2181 }
2182 }
2183 }
2184 /* else: assume full path */
2185
2186 return psz;
2187}
2188
2189
2190/**
2191 * Normalizes the path slashes for the filename. It will partially expand paths too.
2192 * Makes name all lower case too.
2193 * @returns pszFilename
2194 * @param pszFilename Pointer to filename string. Not empty string!
2195 * Much space to play with.
2196 * @param pszBuffer Pointer to output buffer.
2197 */
2198char *fileNormalize2(const char *pszFilename, char *pszBuffer)
2199{
2200 char * psz = pszBuffer;
2201 int iSlash;
2202
2203 if (pszFilename[1] != ':')
2204 {
2205 /* iSlash */
2206 if (*pszFilename == '\\' || *pszFilename == '/')
2207 iSlash = 1;
2208 else
2209 iSlash = cSlashes;
2210
2211 /* interpret . and .. */
2212 while (*pszFilename != '\0')
2213 {
2214 if (*pszFilename == '.' && pszFilename[1] == '.' && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
2215 { /* up one directory */
2216 if (iSlash > 0)
2217 iSlash--;
2218 pszFilename += 3;
2219 }
2220 else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
2221 { /* no change */
2222 pszFilename += 2;
2223 }
2224 else
2225 { /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
2226 strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
2227 strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
2228 break;
2229 }
2230 }
2231 }
2232 else
2233 { /* have drive letter specified - assume ok (TODO)*/
2234 strcpy(pszBuffer, pszFilename);
2235 }
2236
2237 /* correct slashes */
2238 while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
2239 *pszBuffer++ = '\\';
2240
2241 /* lower case it */
2242 /*strlwr(psz);*/
2243
2244 return psz;
2245}
2246
2247
2248/**
2249 * Copies the path part (excluding the slash) into pszBuffer and returns
2250 * a pointer to the buffer.
2251 * If no path is found "" is returned.
2252 * @returns Pointer to pszBuffer with path.
2253 * @param pszFilename Pointer to readonly filename.
2254 * @param pszBuffer Pointer to output Buffer.
2255 * @status completely implemented.
2256 * @author knut st. osmundsen
2257 */
2258char *filePath(const char *pszFilename, char *pszBuffer)
2259{
2260 char *psz = strrchr(pszFilename, '\\');
2261 if (psz == NULL)
2262 psz = strrchr(pszFilename, '/');
2263
2264 if (psz == NULL)
2265 *pszBuffer = '\0';
2266 else
2267 {
2268 strncpy(pszBuffer, pszFilename, psz - pszFilename);
2269 pszBuffer[psz - pszFilename] = '\0';
2270 }
2271
2272 return pszBuffer;
2273}
2274
2275
2276/**
2277 * Copies the path part including the slash into pszBuffer and returns
2278 * a pointer to the buffer.
2279 * If no path is found "" is returned.
2280 * @returns Pointer to pszBuffer with path.
2281 * @param pszFilename Pointer to readonly filename.
2282 * @param pszBuffer Pointer to output Buffer.
2283 * @status completely implemented.
2284 * @author knut st. osmundsen
2285 */
2286char *filePathSlash(const char *pszFilename, char *pszBuffer)
2287{
2288 char *psz = strrchr(pszFilename, '\\');
2289 if (psz == NULL)
2290 psz = strrchr(pszFilename, '/');
2291
2292 if (psz == NULL)
2293 *pszBuffer = '\0';
2294 else
2295 {
2296 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2297 pszBuffer[psz - pszFilename + 1] = '\0';
2298 }
2299
2300 return pszBuffer;
2301}
2302
2303
2304/**
2305 * Copies the path part including the slash into pszBuffer and returns
2306 * a pointer to the buffer. If no path is found "" is returned.
2307 * The path is normalized to only use '\\'.
2308 * @returns Pointer to pszBuffer with path.
2309 * @param pszFilename Pointer to readonly filename.
2310 * @param pszBuffer Pointer to output Buffer.
2311 * @status completely implemented.
2312 * @author knut st. osmundsen
2313 */
2314char *filePathSlash2(const char *pszFilename, char *pszBuffer)
2315{
2316 char *psz = strrchr(pszFilename, '\\');
2317 if (psz == NULL)
2318 psz = strrchr(pszFilename, '/');
2319
2320 if (psz == NULL)
2321 *pszBuffer = '\0';
2322 else
2323 {
2324 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
2325 pszBuffer[psz - pszFilename + 1] = '\0';
2326
2327 /* normalize all '/' to '\\' */
2328 psz = pszBuffer;
2329 while ((psz = strchr(psz, '/')) != NULL)
2330 *psz++ = '\\';
2331 }
2332
2333 return pszBuffer;
2334}
2335
2336
2337/**
2338 * Copies the filename (with extention) into pszBuffer and returns
2339 * a pointer to the buffer.
2340 * @returns Pointer to pszBuffer with path.
2341 * @param pszFilename Pointer to readonly filename.
2342 * @param pszBuffer Pointer to output Buffer.
2343 * @status completely implemented.
2344 * @author knut st. osmundsen
2345 */
2346char *fileName(const char *pszFilename, char *pszBuffer)
2347{
2348 char *psz = strrchr(pszFilename, '\\');
2349 if (psz == NULL)
2350 psz = strrchr(pszFilename, '/');
2351
2352 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2353
2354 return pszBuffer;
2355}
2356
2357
2358/**
2359 * Copies the name part with out extention into pszBuffer and returns
2360 * a pointer to the buffer.
2361 * If no name is found "" is returned.
2362 * @returns Pointer to pszBuffer with path.
2363 * @param pszFilename Pointer to readonly filename.
2364 * @param pszBuffer Pointer to output Buffer.
2365 * @status completely implemented.
2366 * @author knut st. osmundsen
2367 */
2368char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
2369{
2370 char *psz = strrchr(pszFilename, '\\');
2371 if (psz == NULL)
2372 psz = strrchr(pszFilename, '/');
2373
2374 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
2375
2376 psz = strrchr(pszBuffer, '.');
2377 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
2378 *psz = '\0';
2379
2380 return pszBuffer;
2381}
2382
2383
2384/**
2385 * Copies the extention part into pszBuffer and returns
2386 * a pointer to the buffer.
2387 * If no extention is found "" is returned.
2388 * The dot ('.') is not included!
2389 * @returns Pointer to pszBuffer with path.
2390 * @param pszFilename Pointer to readonly filename.
2391 * @param pszBuffer Pointer to output Buffer.
2392 * @status completely implemented.
2393 * @author knut st. osmundsen
2394 */
2395char *fileExt(const char *pszFilename, char *pszBuffer)
2396{
2397 char *psz = strrchr(pszFilename, '.');
2398 if (psz != NULL)
2399 {
2400 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
2401 *pszBuffer = '\0';
2402 else
2403 strcpy(pszBuffer, psz + 1);
2404 }
2405 else
2406 *pszBuffer = '\0';
2407
2408 return pszBuffer;
2409}
2410
2411
2412/**
2413 * Adds a file to the cache.
2414 * @returns Success indicator.
2415 * @param pszFilename Name of the file which is to be added. (with path!)
2416 */
2417BOOL filecacheAddFile(const char *pszFilename)
2418{
2419 PFCACHEENTRY pfcNew;
2420
2421 /* allocate new block and fill in data */
2422 pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
2423 if (pfcNew == NULL)
2424 {
2425 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2426 return FALSE;
2427 }
2428 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2429 strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
2430 if (!AVLInsert(&pfcTree, pfcNew))
2431 {
2432 free(pfcNew);
2433 return TRUE;
2434 }
2435 cfcNodes++;
2436
2437 return TRUE;
2438}
2439
2440
2441
2442/**
2443 * Adds a file to the cache.
2444 * @returns Success indicator.
2445 * @param pszDir Name of the path which is to be added. (with slash!)
2446 */
2447BOOL filecacheAddDir(const char *pszDir)
2448{
2449 PFCACHEENTRY pfcNew;
2450 APIRET rc;
2451 char szDir[CCHMAXPATH];
2452 int cchDir;
2453 char achBuffer[32768];
2454 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2455 HDIR hDir = HDIR_CREATE;
2456 ULONG cFiles = 0xFFFFFFF;
2457 int i;
2458
2459 /* Make path */
2460 filePathSlash2(pszDir, szDir);
2461 //strlwr(szDir); /* Convert name to lower case to allow faster searchs! */
2462 cchDir = strlen(szDir);
2463
2464
2465 /* Add directory to pfcDirTree. */
2466 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
2467 if (pfcNew == NULL)
2468 {
2469 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2470 DosFindClose(hDir);
2471 return FALSE;
2472 }
2473 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2474 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2475 AVLInsert(&pfcDirTree, pfcNew);
2476
2477
2478 /* Start to search directory - all files */
2479 strcat(szDir + cchDir, "*");
2480 rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
2481 pfindbuf3, sizeof(achBuffer),
2482 &cFiles, FIL_STANDARD);
2483 while (rc == NO_ERROR)
2484 {
2485 for (i = 0;
2486 i < cFiles;
2487 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
2488 )
2489 {
2490 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
2491 if (pfcNew == NULL)
2492 {
2493 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
2494 DosFindClose(hDir);
2495 return FALSE;
2496 }
2497 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
2498 strcpy((char*)(unsigned)pfcNew->Key, szDir);
2499 strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
2500 strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
2501 if (!AVLInsert(&pfcTree, pfcNew))
2502 free(pfcNew);
2503 else
2504 cfcNodes++;
2505 }
2506
2507 /* next */
2508 cFiles = 0xFFFFFFF;
2509 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
2510 rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
2511 }
2512
2513 DosFindClose(hDir);
2514
2515 return TRUE;
2516}
2517
2518
2519/**
2520 * Checks if pszFilename is exists in the cache.
2521 * @return TRUE if found. FALSE if not found.
2522 * @param pszFilename Name of the file to be found. (with path!)
2523 * This is in lower case!
2524 */
2525INLINE BOOL filecacheFind(const char *pszFilename)
2526{
2527 return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
2528}
2529
2530
2531/**
2532 * Checks if pszFilename is exists in the cache.
2533 * @return TRUE if found. FALSE if not found.
2534 * @param pszFilename Name of the file to be found. (with path!)
2535 * This is in lower case!
2536 */
2537INLINE BOOL filecacheIsDirCached(const char *pszDir)
2538{
2539 return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
2540}
2541
2542
2543
2544/**
2545 * Finds a filename in a specified pathlist.
2546 * @returns Pointer to a filename consiting of the path part + the given filename.
2547 * (pointer into pszBuffer)
2548 * NULL if file is not found. ("" in buffer)
2549 * @param pszPathList Path list to search for filename.
2550 * @parma pszFilename Filename to find.
2551 * @parma pszBuffer Ouput Buffer.
2552 * @status completely implemented.
2553 * @author knut st. osmundsen
2554 */
2555char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer)
2556{
2557 const char *psz = pszPathList;
2558 const char *pszNext = NULL;
2559
2560 *pszBuffer = '\0';
2561
2562 if (pszPathList == NULL)
2563 return NULL;
2564
2565 while (*psz != '\0')
2566 {
2567 /* find end of this path */
2568 pszNext = strchr(psz, ';');
2569 if (pszNext == NULL)
2570 pszNext = psz + strlen(psz);
2571
2572 if (pszNext - psz > 0)
2573 {
2574 APIRET rc;
2575
2576 /* make search statment */
2577 strncpy(pszBuffer, psz, pszNext - psz);
2578 pszBuffer[pszNext - psz] = '\0';
2579 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
2580 strcpy(&pszBuffer[pszNext - psz], "\\");
2581 strcat(pszBuffer, pszFilename);
2582 fileNormalize(pszBuffer);
2583
2584 /*
2585 * Search for the file in this directory.
2586 * Search cache first
2587 */
2588 if (!filecacheFind(pszBuffer))
2589 {
2590 char szDir[CCHMAXPATH];
2591
2592 filePathSlash(pszBuffer, szDir);
2593 if (!filecacheIsDirCached(szDir))
2594 {
2595 /*
2596 * If caching of entire dirs are enabled, we'll
2597 * add the directory to the cache and search it.
2598 */
2599 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
2600 {
2601 if (filecacheFind(pszBuffer))
2602 return pszBuffer;
2603 }
2604 else
2605 {
2606 FILESTATUS3 fsts3;
2607
2608 /* ask the OS */
2609 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2610 if (rc == NO_ERROR)
2611 { /* add file to cache. */
2612 filecacheAddFile(pszBuffer);
2613 return pszBuffer;
2614 }
2615 }
2616 }
2617 }
2618 else
2619 return pszBuffer;
2620 }
2621
2622 /* next */
2623 if (*pszNext != ';')
2624 break;
2625 psz = pszNext + 1;
2626 }
2627
2628 return NULL;
2629}
2630
2631
2632
2633/**
2634 * Checks if the given filename may exist within any of the given paths.
2635 * This check only matches the filename path agianst the paths in the pathlist.
2636 * @returns TRUE: if exists.
2637 * FALSE: don't exist.
2638 * @param pszPathList Path list to search for filename.
2639 * @parma pszFilename Filename to find. The filename should be normalized!
2640 * @status completely implemented.
2641 * @author knut st. osmundsen
2642 */
2643BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename)
2644{
2645 const char *psz = pszPathList;
2646 const char *pszNext = NULL;
2647 char szBuffer[CCHMAXPATH];
2648 char szBuffer2[CCHMAXPATH];
2649 char *pszPathToFind = &szBuffer2[0];
2650
2651 /*
2652 * Input checking
2653 */
2654 if (pszPathList == NULL)
2655 return FALSE;
2656
2657 /*
2658 * Normalize the filename and get it's path.
2659 */
2660 filePath(pszFilename, pszPathToFind);
2661
2662
2663 /*
2664 * Loop thru the path list.
2665 */
2666 while (*psz != '\0')
2667 {
2668 /* find end of this path */
2669 pszNext = strchr(psz, ';');
2670 if (pszNext == NULL)
2671 pszNext = psz + strlen(psz);
2672
2673 if (pszNext - psz > 0)
2674 {
2675 char * pszPath = &szBuffer[0];
2676
2677 /*
2678 * Extract and normalize the path
2679 */
2680 strncpy(pszPath, psz, pszNext - psz);
2681 pszPath[pszNext - psz] = '\0';
2682 if (pszPath[pszNext - psz - 1] == '\\' && pszPath[pszNext - psz - 1] == '/')
2683 pszPath[pszNext - psz - 1] = '\0';
2684 fileNormalize(pszPath);
2685
2686 /*
2687 * Check if it matches the path of the filename
2688 */
2689 if (strcmp(pszPath, pszPathToFind) == 0)
2690 return TRUE;
2691 }
2692
2693 /*
2694 * Next part of the path list.
2695 */
2696 if (*pszNext != ';')
2697 break;
2698 psz = pszNext + 1;
2699 }
2700
2701 return FALSE;
2702}
2703
2704
2705/**
2706 * Finds the first char after word.
2707 * @returns Pointer to the first char after word.
2708 * @param psz Where to start.
2709 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2710 */
2711char *findEndOfWord(char *psz)
2712{
2713
2714 while (*psz != '\0' &&
2715 (
2716 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
2717 ||
2718 (*psz >= '0' && *psz <= '9')
2719 ||
2720 *psz == '_'
2721 )
2722 )
2723 ++psz;
2724 return (char *)psz;
2725}
2726
2727#if 0 /* not used */
2728/**
2729 * Find the starting char of a word
2730 * @returns Pointer to first char in word.
2731 * @param psz Where to start.
2732 * @param pszStart Where to stop.
2733 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2734 */
2735char *findStartOfWord(const char *psz, const char *pszStart)
2736{
2737 const char *pszR = psz;
2738 while (psz >= pszStart &&
2739 (
2740 (*psz >= 'A' && *psz <= 'Z')
2741 || (*psz >= 'a' && *psz <= 'z')
2742 || (*psz >= '0' && *psz <= '9')
2743 || *psz == '_'
2744 )
2745 )
2746 pszR = psz--;
2747 return (char*)pszR;
2748}
2749#endif
2750
2751/**
2752 * Find the size of a file.
2753 * @returns Size of file. -1 on error.
2754 * @param phFile File handle.
2755 */
2756signed long fsize(FILE *phFile)
2757{
2758 int ipos;
2759 signed long cb;
2760
2761 if ((ipos = ftell(phFile)) < 0
2762 ||
2763 fseek(phFile, 0, SEEK_END) != 0
2764 ||
2765 (cb = ftell(phFile)) < 0
2766 ||
2767 fseek(phFile, ipos, SEEK_SET) != 0
2768 )
2769 cb = -1;
2770 return cb;
2771}
2772
2773
2774
2775/**
2776 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2777 * @returns Pointer to first not space or tab char in the string.
2778 * @param psz Pointer to the string which is to be trimmed.
2779 * @status completely implmented.
2780 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2781 */
2782INLINE char *trim(char *psz)
2783{
2784 int i;
2785 if (psz == NULL)
2786 return NULL;
2787 while (*psz == ' ' || *psz == '\t')
2788 psz++;
2789 i = strlen(psz) - 1;
2790 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2791 i--;
2792 psz[i+1] = '\0';
2793 return psz;
2794}
2795
2796
2797/**
2798 * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
2799 * @returns Pointer to the string passed in.
2800 * @param psz Pointer to the string which is to be right trimmed.
2801 * @status completely implmented.
2802 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2803 */
2804INLINE char *trimR(char *psz)
2805{
2806 int i;
2807 if (psz == NULL)
2808 return NULL;
2809 i = strlen(psz) - 1;
2810 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2811 i--;
2812 psz[i+1] = '\0';
2813 return psz;
2814}
2815
2816
2817/**
2818 * Trims any quotes of a possibly quoted string.
2819 * @returns Pointer to the string passed in.
2820 * @param psz Pointer to the string which is to be quote-trimmed.
2821 * @status completely implmented.
2822 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2823 */
2824INLINE char *trimQuotes(char *psz)
2825{
2826 int i;
2827 if (psz == NULL)
2828 return NULL;
2829
2830 if (*psz == '\"' || *psz == '\'')
2831 psz++;
2832 i = strlen(psz) - 1;
2833 if (psz[i] == '\"' || psz[i] == '\'')
2834 psz[i] = '\0';
2835
2836 return psz;
2837}
2838
2839
2840/**
2841 * C/C++ preprocess a single line. Assumes that we're not starting
2842 * with at comment.
2843 * @returns Pointer to output buffer.
2844 * @param pszOut Ouput (preprocessed) string.
2845 * @param pszIn Input string.
2846 * @author knut st. osmundsen (knut.stange.osmundsen@mynd.no)
2847 */
2848char *PreProcessLine(char *pszOut, const char *pszIn)
2849{
2850 char * psz = pszOut;
2851 BOOL fComment = FALSE;
2852 BOOL fQuote = FALSE;
2853
2854 /*
2855 * Loop thru the string.
2856 */
2857 while (*pszIn != '\0')
2858 {
2859 if (fQuote)
2860 {
2861 *psz++ = *pszIn;
2862 if (*pszIn == '\\')
2863 {
2864 *psz++ = *++pszIn;
2865 pszIn++;
2866 }
2867 else if (*pszIn++ == '"')
2868 fQuote = FALSE;
2869 }
2870 else if (fComment)
2871 {
2872 if (*pszIn == '*' && pszIn[1] == '/')
2873 {
2874 fComment = FALSE;
2875 pszIn += 2;
2876 }
2877 else
2878 pszIn++;
2879 }
2880 else
2881 {
2882 if ( (*pszIn == '/' && pszIn[1] == '/')
2883 || *pszIn == '\0')
2884 { /* End of line. */
2885 break;
2886 }
2887
2888 if (*pszIn == '/' && pszIn[1] == '*')
2889 { /* Start comment */
2890 fComment = TRUE;
2891 pszIn += 2;
2892 }
2893 else
2894 *psz++ = *pszIn++;
2895 }
2896 }
2897
2898 /*
2899 * Trim right.
2900 */
2901 psz--;
2902 while (psz >= pszOut && (*psz == ' ' || *psz == '\t'))
2903 psz--;
2904 psz[1] = '\0';
2905
2906 return pszOut;
2907}
2908
2909
2910/**
2911 * Creates a memory buffer for a text file.
2912 * @returns Pointer to file memoryblock. NULL on error.
2913 * @param pszFilename Pointer to filename string.
2914 * @remark This function is the one using most of the execution
2915 * time (DosRead + DosOpen) - about 70% of the execution time!
2916 */
2917void *textbufferCreate(const char *pszFilename)
2918{
2919 void *pvFile = NULL;
2920 FILE *phFile;
2921
2922 phFile = fopen(pszFilename, "rb");
2923 if (phFile != NULL)
2924 {
2925 signed long cbFile = fsize(phFile);
2926 if (cbFile >= 0)
2927 {
2928 pvFile = malloc(cbFile + 1);
2929 if (pvFile != NULL)
2930 {
2931 memset(pvFile, 0, cbFile + 1);
2932 if (cbFile > 0 && fread(pvFile, 1, cbFile, phFile) == 0)
2933 { /* failed! */
2934 free(pvFile);
2935 pvFile = NULL;
2936 }
2937 }
2938 else
2939 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
2940 }
2941 fclose(phFile);
2942 }
2943 return pvFile;
2944}
2945
2946
2947/**
2948 * Destroys a text textbuffer.
2949 * @param pvBuffer Buffer handle.
2950 */
2951void textbufferDestroy(void *pvBuffer)
2952{
2953 free(pvBuffer);
2954}
2955
2956
2957/**
2958 * Gets the next line from an textbuffer.
2959 * @returns Pointer to the next line.
2960 * @param pvBuffer Buffer handle.
2961 * @param psz Pointer to current line.
2962 * NULL is passed in to get the first line.
2963 */
2964char *textbufferNextLine(void *pvBuffer, register char *psz)
2965{
2966 register char ch;
2967
2968 /* if first line psz is NULL. */
2969 if (psz == NULL)
2970 return (char*)pvBuffer;
2971
2972 /* skip till end of file or end of line. */
2973 ch = *psz;
2974 while (ch != '\0' && ch != '\n' && ch != '\r')
2975 ch = *++psz;
2976
2977 /* skip line end */
2978 if (ch == '\r')
2979 ch = *++psz;
2980 if (ch == '\n')
2981 psz++;
2982
2983 return psz;
2984}
2985
2986
2987/**
2988 * Gets the next line from an textbuffer.
2989 * (fgets for textbuffer)
2990 * @returns Pointer to pszOutBuffer. NULL when end of file.
2991 * @param pvBuffer Buffer handle.
2992 * @param ppv Pointer to a buffer index pointer. (holds the current buffer index)
2993 * Pointer to a null pointer is passed in to get the first line.
2994 * @param pszLineBuffer Output line buffer. (!= NULL)
2995 * @param cchLineBuffer Size of the output line buffer. (> 0)
2996 * @remark '\n' and '\r' are removed!
2997 */
2998char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
2999{
3000 char * pszLine = pszLineBuffer;
3001 char * psz = *(char**)(void*)ppv;
3002 register char ch;
3003
3004 /* first line? */
3005 if (psz == NULL)
3006 psz = pvBuffer;
3007
3008 /* Copy to end of the line or end of the linebuffer. */
3009 ch = *psz;
3010 cchLineBuffer--; /* reserve space for '\0' */
3011 while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
3012 {
3013 *pszLine++ = ch;
3014 ch = *++psz;
3015 }
3016 *pszLine = '\0';
3017
3018 /* skip line end */
3019 if (ch == '\r')
3020 ch = *++psz;
3021 if (ch == '\n')
3022 psz++;
3023
3024 /* check if position has changed - if unchanged it's the end of file! */
3025 if (*ppv == (void*)psz)
3026 pszLineBuffer = NULL;
3027
3028 /* store current position */
3029 *ppv = (void*)psz;
3030
3031 return pszLineBuffer;
3032}
3033
3034
3035/**
3036 * Appends a depend file to the internal file.
3037 * This will update the date in the option struct.
3038 */
3039BOOL depReadFile(const char *pszFilename, BOOL fAppend)
3040{
3041 void * pvFile;
3042 char * pszNext;
3043 char * pszPrev; /* Previous line, only valid when finding new rule. */
3044 BOOL fMoreDeps = FALSE;
3045 void * pvRule = NULL;
3046
3047
3048 /* read depend file */
3049 pvFile = textbufferCreate(pszFilename);
3050 if (pvFile == NULL)
3051 return FALSE;
3052
3053 /* parse the original depend file */
3054 pszPrev = NULL;
3055 pszNext = pvFile;
3056 while (*pszNext != '\0')
3057 {
3058 int i;
3059 int cch;
3060 char *psz;
3061
3062 /* get the next line. */
3063 psz = pszNext;
3064 pszNext = textbufferNextLine(pvFile, pszNext);
3065
3066 /*
3067 * Process the current line:
3068 * Start off by terminating the line.
3069 * Trim the line,
3070 * Skip empty lines.
3071 * If not looking for more deps Then
3072 * Check if new rule starts here.
3073 * Endif
3074 *
3075 * If more deps to last rule Then
3076 * Get dependant name.
3077 * Endif
3078 */
3079 i = -1;
3080 while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
3081 pszNext[i--] = '\0';
3082 trimR(psz);
3083 cch = strlen(psz);
3084 if (cch == 0)
3085 {
3086 fMoreDeps = FALSE;
3087 continue;
3088 }
3089
3090 if (*psz == '#')
3091 {
3092 pszPrev = psz;
3093 continue;
3094 }
3095
3096 /* new rule? */
3097 if (!fMoreDeps)
3098 {
3099 if (*psz != ' ' && *psz != '\t' && *psz != '\0')
3100 {
3101 i = 0;
3102 while (psz[i] != '\0')
3103 {
3104 if (psz[i] == ':'
3105 && (psz[i+1] == ' '
3106 || psz[i+1] == '\t'
3107 || psz[i+1] == '\0'
3108 || (psz[i+1] == '\\' && psz[i+2] == '\0')
3109 )
3110 )
3111 {
3112 char szTS[TS_SIZE];
3113 char * pszCont = strchr(&psz[i], '\\');
3114 fMoreDeps = pszCont != NULL && pszCont[1] == '\0';
3115
3116 /* read evt. timestamp. */
3117 szTS[0] = '\0';
3118 if (pszPrev && strlen(pszPrev) > 25 && *pszPrev == '#')
3119 strcpy(szTS, pszPrev + 2);
3120
3121 psz[i] = '\0';
3122 pvRule = depAddRule(trimQuotes(trimR(psz)), NULL, NULL, szTS);
3123 ((PDEPRULE)pvRule)->fUpdated = fAppend;
3124 psz += i + 1;
3125 cch -= i + 1;
3126 break;
3127 }
3128 i++;
3129 }
3130 }
3131 pszPrev = NULL;
3132 }
3133
3134
3135 /* more dependants */
3136 if (fMoreDeps)
3137 {
3138 if (cch > 0 && psz[cch-1] == '\\')
3139 {
3140 fMoreDeps = TRUE;
3141 psz[cch-1] = '\0';
3142 }
3143 else
3144 fMoreDeps = FALSE;
3145
3146 /* if not duplicate rule */
3147 if (pvRule != NULL)
3148 {
3149 psz = trimQuotes(trim(psz));
3150 if (*psz != '\0')
3151 depAddDepend(pvRule, psz, options.fCheckCyclic);
3152 }
3153 }
3154 } /* while */
3155
3156
3157 /* return succesfully */
3158 textbufferDestroy(pvFile);
3159 return TRUE;
3160}
3161
3162/**
3163 *
3164 * @returns Success indicator.
3165 * @param pszFilename Pointer to name of the output file.
3166 * @param fWriteUpdatedOnly If set we'll only write updated rules.
3167 */
3168BOOL depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly)
3169{
3170 FILE *phFile;
3171 phFile = fopen(pszFilename, "w");
3172 if (phFile != NULL)
3173 {
3174 AVLENUMDATA EnumData;
3175 PDEPRULE pdep;
3176 char szBuffer[32678];
3177 int iBuffer = 0;
3178 int cch;
3179
3180 /*
3181 * Write warning on top of file.
3182 */
3183 fputs("#\n"
3184 "# This file was automatically generated by FastDep.\n"
3185 "# FastDep was written by knut st. osmundsen, and it's GPL software.\n"
3186 "#\n"
3187 "# THIS FILE SHOULD N O T BE EDITED MANUALLY!!!\n"
3188 "#\n"
3189 "# (As this may possibly make it unreadable for fastdep\n"
3190 "# and ruin the caching methods of FastDep.)\n"
3191 "#\n"
3192 "\n",
3193 phFile);
3194
3195
3196 /* @@@PH 2001-03-01
3197 * If option is selected to generate a parent
3198 * "super" dependency, enter this scope.
3199 */
3200 if (options.pszSuperDependency != NULL)
3201 {
3202 iBuffer = sprintf(szBuffer,
3203 "%s:",
3204 options.pszSuperDependency);
3205
3206 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3207 while (pdep != NULL)
3208 {
3209 if (!fWriteUpdatedOnly || pdep->fUpdated)
3210 {
3211 char *psz = pdep->pszRule;
3212
3213 /* flush buffer? */
3214 if (iBuffer + strlen(psz) + 20 >= sizeof(szBuffer))
3215 {
3216 fwrite(szBuffer, iBuffer, 1, phFile);
3217 iBuffer = 0;
3218 }
3219
3220 /* write rule title as dependant */
3221 iBuffer += sprintf(szBuffer + iBuffer,
3222 " \\\n %s",psz);
3223 }
3224
3225 /* next rule */
3226 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3227 }
3228
3229 /* Add two new lines. Flush buffer first if necessary. */
3230 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3231 {
3232 fwrite(szBuffer, iBuffer, 1, phFile);
3233 iBuffer = 0;
3234 }
3235
3236 /* add 2 linefeeds */
3237 strcpy(szBuffer + iBuffer, "\n\n");
3238 iBuffer += CBNEWLINE*2;
3239 }
3240
3241
3242 /* normal dependency output */
3243 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3244 while (pdep != NULL)
3245 {
3246 if (!fWriteUpdatedOnly || pdep->fUpdated)
3247 {
3248 int cchTS = strlen(pdep->szTS);
3249 int fQuoted = strpbrk(pdep->pszRule, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3250
3251 /* Write rule. Flush the buffer first if necessary. */
3252 cch = strlen(pdep->pszRule);
3253 if (iBuffer + cch + fQuoted * 2 + cchTS + 9 >= sizeof(szBuffer))
3254 {
3255 fwrite(szBuffer, iBuffer, 1, phFile);
3256 iBuffer = 0;
3257 }
3258
3259 memcpy(szBuffer + iBuffer, "# ", 2);
3260 memcpy(szBuffer + iBuffer + 2, pdep->szTS, cchTS);
3261 iBuffer += cchTS + 2;
3262 szBuffer[iBuffer++] = '\n';
3263
3264 if (fQuoted) szBuffer[iBuffer++] = '"';
3265 strcpy(szBuffer + iBuffer, pdep->pszRule);
3266 iBuffer += cch;
3267 if (fQuoted) szBuffer[iBuffer++] = '"';
3268 strcpy(szBuffer + iBuffer++, ":");
3269
3270 /* write rule dependants. */
3271 if (pdep->papszDep != NULL)
3272 {
3273 char **ppsz = pdep->papszDep;
3274 while (*ppsz != NULL)
3275 {
3276 /* flush buffer? */
3277 fQuoted = strpbrk(*ppsz, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3278 cch = strlen(*ppsz);
3279 if (iBuffer + cch + fQuoted * 2 + 20 >= sizeof(szBuffer))
3280 {
3281 fwrite(szBuffer, iBuffer, 1, phFile);
3282 iBuffer = 0;
3283 }
3284 strcpy(szBuffer + iBuffer, " \\\n ");
3285 iBuffer += 7;
3286 if (fQuoted) szBuffer[iBuffer++] = '"';
3287 strcpy(szBuffer + iBuffer, *ppsz);
3288 iBuffer += cch;
3289 if (fQuoted) szBuffer[iBuffer++] = '"';
3290
3291 /* next dependant */
3292 ppsz++;
3293 }
3294 }
3295
3296 /* Add two new lines. Flush buffer first if necessary. */
3297 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3298 {
3299 fwrite(szBuffer, iBuffer, 1, phFile);
3300 iBuffer = 0;
3301 }
3302
3303 /* add 2 linefeeds */
3304 strcpy(szBuffer + iBuffer, "\n\n");
3305 iBuffer += CBNEWLINE*2;
3306 }
3307
3308 /* next rule */
3309 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3310 }
3311
3312
3313 /* flush buffer. */
3314 fwrite(szBuffer, iBuffer, 1, phFile);
3315
3316 fclose(phFile);
3317 return TRUE;
3318 }
3319
3320 return FALSE;
3321}
3322
3323
3324/**
3325 * Removes all nodes in the tree of dependencies. (pdepTree)
3326 */
3327void depRemoveAll(void)
3328{
3329 AVLENUMDATA EnumData;
3330 PDEPRULE pdep;
3331
3332 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3333 while (pdep != NULL)
3334 {
3335 /* free this */
3336 if (pdep->papszDep != NULL)
3337 {
3338 char ** ppsz = pdep->papszDep;
3339 while (*ppsz != NULL)
3340 free(*ppsz++);
3341 free(pdep->papszDep);
3342 }
3343 free(pdep);
3344
3345 /* next */
3346 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3347 }
3348 pdepTree = NULL;
3349}
3350
3351
3352/**
3353 * Adds a rule to the list of dependant rules.
3354 * @returns Rule handle. NULL if rule exists/error.
3355 * @param pszRulePath Pointer to rule text. Empty strings are banned!
3356 * This string might only contain the path of the rule. (with '\\')
3357 * @param pszName Name of the rule.
3358 * NULL if pszRulePath contains the entire rule.
3359 * @param pszExt Extention (without '.')
3360 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
3361 */
3362void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS)
3363{
3364 char szRule[CCHMAXPATH*2];
3365 PDEPRULE pNew;
3366 int cch;
3367
3368 /* make rulename */
3369 strcpy(szRule, pszRulePath);
3370 cch = strlen(szRule);
3371 if (pszName != NULL)
3372 {
3373 strcpy(szRule + cch, pszName);
3374 cch += strlen(szRule + cch);
3375 }
3376 if (pszExt != NULL)
3377 {
3378 strcat(szRule + cch++, ".");
3379 strcat(szRule + cch, pszExt);
3380 cch += strlen(szRule + cch);
3381 }
3382
3383
3384 /*
3385 * Allocate a new rule structure and fill in data
3386 * Note. One block for both the DEPRULE and the pszRule string.
3387 */
3388 pNew = malloc(sizeof(DEPRULE) + cch + 1);
3389 if (pNew == NULL)
3390 {
3391 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
3392 return NULL;
3393 }
3394 pNew->pszRule = (char*)(void*)(pNew + 1);
3395 strcpy(pNew->pszRule, szRule);
3396 pNew->cDeps = 0;
3397 pNew->papszDep = NULL;
3398 pNew->fUpdated = TRUE;
3399 pNew->avlCore.Key = pNew->pszRule;
3400 strcpy(pNew->szTS, pszTS);
3401
3402 /* Insert the rule */
3403 if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
3404 { /*
3405 * The rule existed.
3406 * If it's allready touched (updated) during this session
3407 * there is nothing to be done.
3408 * If not force scan and it's newer than depfile-1month then
3409 * we'll use the information we've got.
3410 * Reuse the node in the tree.
3411 */
3412 PDEPRULE pOld = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pNew->avlCore.Key);
3413 assert(pOld);
3414 free(pNew);
3415 if (pOld->fUpdated)
3416 return NULL;
3417
3418 pOld->fUpdated = TRUE;
3419 if (!options.fForceScan && !strcmp(pOld->szTS, pszTS) && depValidate(pOld))
3420 return NULL;
3421 strcpy(pOld->szTS, pszTS);
3422
3423 if (pOld->papszDep)
3424 {
3425 free(pOld->papszDep);
3426 pOld->papszDep = NULL;
3427 }
3428 pOld->cDeps = 0;
3429
3430 return pOld;
3431 }
3432
3433 return pNew;
3434}
3435
3436
3437
3438/**
3439 * Adds a dependant to a rule.
3440 * @returns Successindicator. TRUE = success.
3441 * FALSE = cyclic or out of memory.
3442 * @param pvRule Rule handle.
3443 * @param pszDep Pointer to dependant name
3444 * @param fCheckCyclic When set we'll check that we're not creating an cyclic dependency.
3445 */
3446BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic)
3447{
3448 PDEPRULE pdep = (PDEPRULE)pvRule;
3449
3450 if (pszDep[0] == '\0')
3451 {
3452 fprintf(stderr, "warning-internal: empty dependancy filename to '%s'. Ignored.\n",
3453 pdep->pszRule);
3454 /* __interrupt(3); */
3455 return FALSE;
3456 }
3457
3458 if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
3459 {
3460 fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
3461 pszDep, pdep->pszRule);
3462 return FALSE;
3463 }
3464
3465 /* allocate more array space */
3466 if (((pdep->cDeps) % 48) == 0)
3467 {
3468 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
3469 if (pdep->papszDep == NULL)
3470 {
3471 pdep->cDeps = 0;
3472 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3473 return FALSE;
3474 }
3475 }
3476
3477 /* allocate string space and copy pszDep */
3478 if ((pdep->papszDep[pdep->cDeps] = malloc(strlen(pszDep) + 1)) == NULL)
3479 {
3480 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3481 return FALSE;
3482 }
3483 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
3484
3485 /* terminate array and increment dep count */
3486 pdep->papszDep[++pdep->cDeps] = NULL;
3487
3488 /* successful! */
3489 return TRUE;
3490}
3491
3492
3493/**
3494 * Marks the file as one which is to be rescanned next time
3495 * since not all dependencies was found...
3496 * @param pvRule Rule handle...
3497 */
3498void depMarkNotFound(void *pvRule)
3499{
3500 ((PDEPRULE)pvRule)->szTS[0] = '\0';
3501}
3502
3503
3504/**
3505 * Checks if adding this dependent will create a cylic dependency.
3506 * @returns TRUE: Cyclic.
3507 * FALSE: Non-cylic.
3508 * @param pdepRule Rule pszDep is to be inserted in.
3509 * @param pszDep Depend name.
3510 */
3511BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
3512{
3513 #define DEPTH 32
3514 char * pszRule = pdepRule->pszRule;
3515 char ** appsz[DEPTH];
3516 PDEPRULE pdep;
3517 int i;
3518
3519 /* self check */
3520 if (strcmp(pdepRule->pszRule, pszDep) == 0)
3521 return TRUE;
3522
3523 /* find rule for the dep. */
3524 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
3525 || pdep->papszDep == NULL)
3526 return FALSE; /* no rule, or no dependents, not cyclic */
3527
3528 i = 0;
3529 appsz[0] = pdep->papszDep;
3530 while (i >= 0)
3531 {
3532 register char ** ppsz = appsz[i];
3533
3534 while (*ppsz != NULL)
3535 {
3536 /* check if equal to the main rule */
3537 if (strcmp(pszRule, *ppsz) == 0)
3538 return TRUE;
3539
3540 /* push onto stack (ppsz is incremented in this test!) */
3541 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
3542 && pdep->papszDep != NULL)
3543 {
3544 if (i >= DEPTH)
3545 {
3546 fprintf(stderr, "error: too deap chain (%d). pszRule=%s pszDep=%s\n",
3547 i, pszRule, pszDep);
3548 return FALSE;
3549 }
3550 appsz[i++] = ppsz; /* save next */
3551 ppsz = pdep->papszDep; /* start processing new node */
3552 }
3553 }
3554
3555 /* pop stack */
3556 i--;
3557 }
3558
3559 return FALSE;
3560}
3561
3562
3563/**
3564 * Validates that the dependencies for the file exists
3565 * in the given locations. Dependants without path is ignored.
3566 * @returns TRUE if all ok.
3567 * FALSE if one (or possibly more) dependants are non-existing.
3568 * @param pdepRule Pointer to rule we're to validate.
3569 */
3570BOOL depValidate(PDEPRULE pdepRule)
3571{
3572 int i;
3573
3574 for (i = 0; i < pdepRule->cDeps; i++)
3575 {
3576 char *psz = pdepRule->papszDep[i];
3577 if ( psz[1] == ':'
3578 || strchr(psz, '\\')
3579 || strchr(psz, '/')
3580 )
3581 {
3582 /*
3583 * Check existance of the file.
3584 * Search cache first
3585 */
3586 if (!filecacheFind(psz))
3587 {
3588 char szDir[CCHMAXPATH];
3589
3590 filePathSlash(psz, szDir);
3591 if (!filecacheIsDirCached(szDir))
3592 {
3593 /*
3594 * If caching of entire dirs are enabled, we'll
3595 * add the directory to the cache and search it.
3596 */
3597 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
3598 {
3599 if (!filecacheFind(psz))
3600 return FALSE;
3601 }
3602 else
3603 {
3604 FILESTATUS3 fsts3;
3605
3606 /* ask the OS */
3607 if (DosQueryPathInfo(psz, FIL_STANDARD, &fsts3, sizeof(fsts3)))
3608 return FALSE;
3609 /* add file to cache. */
3610 filecacheAddFile(psz);
3611 }
3612 }
3613 }
3614 }
3615 }
3616
3617 return TRUE;
3618}
3619
3620
3621/**
3622 * Make a timestamp from the file data provided thru the
3623 * search API.
3624 * @returns Pointer to pszTS
3625 * @param pszTS Pointer to timestamp (output).
3626 * @param pfindbuf3 Pointer to search result.
3627 */
3628INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3)
3629{
3630 sprintf(pszTS, "%04d-%02d-%02d-%02d.%02d.%02d 0x%04x%04x %d",
3631 pfindbuf3->fdateLastWrite.year + 1980,
3632 pfindbuf3->fdateLastWrite.month,
3633 pfindbuf3->fdateLastWrite.day,
3634 pfindbuf3->ftimeLastWrite.hours,
3635 pfindbuf3->ftimeLastWrite.minutes,
3636 pfindbuf3->ftimeLastWrite.twosecs * 2,
3637 (ULONG)*(PUSHORT)(void*)&pfindbuf3->fdateCreation,
3638 (ULONG)*(PUSHORT)(void*)&pfindbuf3->ftimeCreation,
3639 pfindbuf3->cbFile);
3640 return pszTS;
3641}
3642
3643
3644
3645
3646
3647/*
3648 * Testing purpose.
3649 */
3650
3651#if !defined(OS2FAKE)
3652#include <os2.h>
3653#endif
3654#ifdef OLEMANN
3655#include "olemann.h"
3656#endif
Note: See TracBrowser for help on using the repository browser.