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

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

Added wbl as yet another cobol file extension.

File size: 116.9 KB
Line 
1/* $Id: fastdep.c,v 1.33 2001-11-14 11:13:26 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", "wbl", 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 4,
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 if (pvRule)
3124 ((PDEPRULE)pvRule)->fUpdated = fAppend;
3125 psz += i + 1;
3126 cch -= i + 1;
3127 break;
3128 }
3129 i++;
3130 }
3131 }
3132 pszPrev = NULL;
3133 }
3134
3135
3136 /* more dependants */
3137 if (fMoreDeps)
3138 {
3139 if (cch > 0 && psz[cch-1] == '\\')
3140 {
3141 fMoreDeps = TRUE;
3142 psz[cch-1] = '\0';
3143 }
3144 else
3145 fMoreDeps = FALSE;
3146
3147 /* if not duplicate rule */
3148 if (pvRule != NULL)
3149 {
3150 psz = trimQuotes(trim(psz));
3151 if (*psz != '\0')
3152 depAddDepend(pvRule, psz, options.fCheckCyclic);
3153 }
3154 }
3155 } /* while */
3156
3157
3158 /* return succesfully */
3159 textbufferDestroy(pvFile);
3160 return TRUE;
3161}
3162
3163/**
3164 *
3165 * @returns Success indicator.
3166 * @param pszFilename Pointer to name of the output file.
3167 * @param fWriteUpdatedOnly If set we'll only write updated rules.
3168 */
3169BOOL depWriteFile(const char *pszFilename, BOOL fWriteUpdatedOnly)
3170{
3171 FILE *phFile;
3172 phFile = fopen(pszFilename, "w");
3173 if (phFile != NULL)
3174 {
3175 AVLENUMDATA EnumData;
3176 PDEPRULE pdep;
3177 char szBuffer[32678];
3178 int iBuffer = 0;
3179 int cch;
3180
3181 /*
3182 * Write warning on top of file.
3183 */
3184 fputs("#\n"
3185 "# This file was automatically generated by FastDep.\n"
3186 "# FastDep was written by knut st. osmundsen, and it's GPL software.\n"
3187 "#\n"
3188 "# THIS FILE SHOULD N O T BE EDITED MANUALLY!!!\n"
3189 "#\n"
3190 "# (As this may possibly make it unreadable for fastdep\n"
3191 "# and ruin the caching methods of FastDep.)\n"
3192 "#\n"
3193 "\n",
3194 phFile);
3195
3196
3197 /* @@@PH 2001-03-01
3198 * If option is selected to generate a parent
3199 * "super" dependency, enter this scope.
3200 */
3201 if (options.pszSuperDependency != NULL)
3202 {
3203 iBuffer = sprintf(szBuffer,
3204 "%s:",
3205 options.pszSuperDependency);
3206
3207 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3208 while (pdep != NULL)
3209 {
3210 if (!fWriteUpdatedOnly || pdep->fUpdated)
3211 {
3212 char *psz = pdep->pszRule;
3213
3214 /* flush buffer? */
3215 if (iBuffer + strlen(psz) + 20 >= sizeof(szBuffer))
3216 {
3217 fwrite(szBuffer, iBuffer, 1, phFile);
3218 iBuffer = 0;
3219 }
3220
3221 /* write rule title as dependant */
3222 iBuffer += sprintf(szBuffer + iBuffer,
3223 " \\\n %s",psz);
3224 }
3225
3226 /* next rule */
3227 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3228 }
3229
3230 /* Add two new lines. Flush buffer first if necessary. */
3231 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3232 {
3233 fwrite(szBuffer, iBuffer, 1, phFile);
3234 iBuffer = 0;
3235 }
3236
3237 /* add 2 linefeeds */
3238 strcpy(szBuffer + iBuffer, "\n\n");
3239 iBuffer += CBNEWLINE*2;
3240 }
3241
3242
3243 /* normal dependency output */
3244 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3245 while (pdep != NULL)
3246 {
3247 if (!fWriteUpdatedOnly || pdep->fUpdated)
3248 {
3249 int cchTS = strlen(pdep->szTS);
3250 int fQuoted = strpbrk(pdep->pszRule, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3251
3252 /* Write rule. Flush the buffer first if necessary. */
3253 cch = strlen(pdep->pszRule);
3254 if (iBuffer + cch + fQuoted * 2 + cchTS + 9 >= sizeof(szBuffer))
3255 {
3256 fwrite(szBuffer, iBuffer, 1, phFile);
3257 iBuffer = 0;
3258 }
3259
3260 memcpy(szBuffer + iBuffer, "# ", 2);
3261 memcpy(szBuffer + iBuffer + 2, pdep->szTS, cchTS);
3262 iBuffer += cchTS + 2;
3263 szBuffer[iBuffer++] = '\n';
3264
3265 if (fQuoted) szBuffer[iBuffer++] = '"';
3266 strcpy(szBuffer + iBuffer, pdep->pszRule);
3267 iBuffer += cch;
3268 if (fQuoted) szBuffer[iBuffer++] = '"';
3269 strcpy(szBuffer + iBuffer++, ":");
3270
3271 /* write rule dependants. */
3272 if (pdep->papszDep != NULL)
3273 {
3274 char **ppsz = pdep->papszDep;
3275 while (*ppsz != NULL)
3276 {
3277 /* flush buffer? */
3278 fQuoted = strpbrk(*ppsz, " \t") != NULL; /* TODO/BUGBUG/FIXME: are there more special chars to look out for?? */
3279 cch = strlen(*ppsz);
3280 if (iBuffer + cch + fQuoted * 2 + 20 >= sizeof(szBuffer))
3281 {
3282 fwrite(szBuffer, iBuffer, 1, phFile);
3283 iBuffer = 0;
3284 }
3285 strcpy(szBuffer + iBuffer, " \\\n ");
3286 iBuffer += 7;
3287 if (fQuoted) szBuffer[iBuffer++] = '"';
3288 strcpy(szBuffer + iBuffer, *ppsz);
3289 iBuffer += cch;
3290 if (fQuoted) szBuffer[iBuffer++] = '"';
3291
3292 /* next dependant */
3293 ppsz++;
3294 }
3295 }
3296
3297 /* Add two new lines. Flush buffer first if necessary. */
3298 if (iBuffer + CBNEWLINE*2 >= sizeof(szBuffer))
3299 {
3300 fwrite(szBuffer, iBuffer, 1, phFile);
3301 iBuffer = 0;
3302 }
3303
3304 /* add 2 linefeeds */
3305 strcpy(szBuffer + iBuffer, "\n\n");
3306 iBuffer += CBNEWLINE*2;
3307 }
3308
3309 /* next rule */
3310 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3311 }
3312
3313
3314 /* flush buffer. */
3315 fwrite(szBuffer, iBuffer, 1, phFile);
3316
3317 fclose(phFile);
3318 return TRUE;
3319 }
3320
3321 return FALSE;
3322}
3323
3324
3325/**
3326 * Removes all nodes in the tree of dependencies. (pdepTree)
3327 */
3328void depRemoveAll(void)
3329{
3330 AVLENUMDATA EnumData;
3331 PDEPRULE pdep;
3332
3333 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
3334 while (pdep != NULL)
3335 {
3336 /* free this */
3337 if (pdep->papszDep != NULL)
3338 {
3339 char ** ppsz = pdep->papszDep;
3340 while (*ppsz != NULL)
3341 free(*ppsz++);
3342 free(pdep->papszDep);
3343 }
3344 free(pdep);
3345
3346 /* next */
3347 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
3348 }
3349 pdepTree = NULL;
3350}
3351
3352
3353/**
3354 * Adds a rule to the list of dependant rules.
3355 * @returns Rule handle. NULL if rule exists/error.
3356 * @param pszRulePath Pointer to rule text. Empty strings are banned!
3357 * This string might only contain the path of the rule. (with '\\')
3358 * @param pszName Name of the rule.
3359 * NULL if pszRulePath contains the entire rule.
3360 * @param pszExt Extention (without '.')
3361 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
3362 */
3363void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt, const char *pszTS)
3364{
3365 char szRule[CCHMAXPATH*2];
3366 PDEPRULE pNew;
3367 int cch;
3368
3369 /* make rulename */
3370 strcpy(szRule, pszRulePath);
3371 cch = strlen(szRule);
3372 if (pszName != NULL)
3373 {
3374 strcpy(szRule + cch, pszName);
3375 cch += strlen(szRule + cch);
3376 }
3377 if (pszExt != NULL)
3378 {
3379 strcat(szRule + cch++, ".");
3380 strcat(szRule + cch, pszExt);
3381 cch += strlen(szRule + cch);
3382 }
3383
3384
3385 /*
3386 * Allocate a new rule structure and fill in data
3387 * Note. One block for both the DEPRULE and the pszRule string.
3388 */
3389 pNew = malloc(sizeof(DEPRULE) + cch + 1);
3390 if (pNew == NULL)
3391 {
3392 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
3393 return NULL;
3394 }
3395 pNew->pszRule = (char*)(void*)(pNew + 1);
3396 strcpy(pNew->pszRule, szRule);
3397 pNew->cDeps = 0;
3398 pNew->papszDep = NULL;
3399 pNew->fUpdated = TRUE;
3400 pNew->avlCore.Key = pNew->pszRule;
3401 strcpy(pNew->szTS, pszTS);
3402
3403 /* Insert the rule */
3404 if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
3405 { /*
3406 * The rule existed.
3407 * If it's allready touched (updated) during this session
3408 * there is nothing to be done.
3409 * If not force scan and it's newer than depfile-1month then
3410 * we'll use the information we've got.
3411 * Reuse the node in the tree.
3412 */
3413 PDEPRULE pOld = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pNew->avlCore.Key);
3414 assert(pOld);
3415 free(pNew);
3416 if (pOld->fUpdated)
3417 return NULL;
3418
3419 pOld->fUpdated = TRUE;
3420 if (!options.fForceScan && !strcmp(pOld->szTS, pszTS) && depValidate(pOld))
3421 return NULL;
3422 strcpy(pOld->szTS, pszTS);
3423
3424 if (pOld->papszDep)
3425 {
3426 free(pOld->papszDep);
3427 pOld->papszDep = NULL;
3428 }
3429 pOld->cDeps = 0;
3430
3431 return pOld;
3432 }
3433
3434 return pNew;
3435}
3436
3437
3438
3439/**
3440 * Adds a dependant to a rule.
3441 * @returns Successindicator. TRUE = success.
3442 * FALSE = cyclic or out of memory.
3443 * @param pvRule Rule handle.
3444 * @param pszDep Pointer to dependant name
3445 * @param fCheckCyclic When set we'll check that we're not creating an cyclic dependency.
3446 */
3447BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic)
3448{
3449 PDEPRULE pdep = (PDEPRULE)pvRule;
3450
3451 if (pszDep[0] == '\0')
3452 {
3453 fprintf(stderr, "warning-internal: empty dependancy filename to '%s'. Ignored.\n",
3454 pdep->pszRule);
3455 /* __interrupt(3); */
3456 return FALSE;
3457 }
3458
3459 if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
3460 {
3461 fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
3462 pszDep, pdep->pszRule);
3463 return FALSE;
3464 }
3465
3466 /* allocate more array space */
3467 if (((pdep->cDeps) % 48) == 0)
3468 {
3469 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
3470 if (pdep->papszDep == NULL)
3471 {
3472 pdep->cDeps = 0;
3473 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3474 return FALSE;
3475 }
3476 }
3477
3478 /* allocate string space and copy pszDep */
3479 if ((pdep->papszDep[pdep->cDeps] = malloc(strlen(pszDep) + 1)) == NULL)
3480 {
3481 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
3482 return FALSE;
3483 }
3484 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
3485
3486 /* terminate array and increment dep count */
3487 pdep->papszDep[++pdep->cDeps] = NULL;
3488
3489 /* successful! */
3490 return TRUE;
3491}
3492
3493
3494/**
3495 * Marks the file as one which is to be rescanned next time
3496 * since not all dependencies was found...
3497 * @param pvRule Rule handle...
3498 */
3499void depMarkNotFound(void *pvRule)
3500{
3501 ((PDEPRULE)pvRule)->szTS[0] = '\0';
3502}
3503
3504
3505/**
3506 * Checks if adding this dependent will create a cylic dependency.
3507 * @returns TRUE: Cyclic.
3508 * FALSE: Non-cylic.
3509 * @param pdepRule Rule pszDep is to be inserted in.
3510 * @param pszDep Depend name.
3511 */
3512BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
3513{
3514 #define DEPTH 32
3515 char * pszRule = pdepRule->pszRule;
3516 char ** appsz[DEPTH];
3517 PDEPRULE pdep;
3518 int i;
3519
3520 /* self check */
3521 if (strcmp(pdepRule->pszRule, pszDep) == 0)
3522 return TRUE;
3523
3524 /* find rule for the dep. */
3525 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
3526 || pdep->papszDep == NULL)
3527 return FALSE; /* no rule, or no dependents, not cyclic */
3528
3529 i = 0;
3530 appsz[0] = pdep->papszDep;
3531 while (i >= 0)
3532 {
3533 register char ** ppsz = appsz[i];
3534
3535 while (*ppsz != NULL)
3536 {
3537 /* check if equal to the main rule */
3538 if (strcmp(pszRule, *ppsz) == 0)
3539 return TRUE;
3540
3541 /* push onto stack (ppsz is incremented in this test!) */
3542 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
3543 && pdep->papszDep != NULL)
3544 {
3545 if (i >= DEPTH)
3546 {
3547 fprintf(stderr, "error: too deap chain (%d). pszRule=%s pszDep=%s\n",
3548 i, pszRule, pszDep);
3549 return FALSE;
3550 }
3551 appsz[i++] = ppsz; /* save next */
3552 ppsz = pdep->papszDep; /* start processing new node */
3553 }
3554 }
3555
3556 /* pop stack */
3557 i--;
3558 }
3559
3560 return FALSE;
3561}
3562
3563
3564/**
3565 * Validates that the dependencies for the file exists
3566 * in the given locations. Dependants without path is ignored.
3567 * @returns TRUE if all ok.
3568 * FALSE if one (or possibly more) dependants are non-existing.
3569 * @param pdepRule Pointer to rule we're to validate.
3570 */
3571BOOL depValidate(PDEPRULE pdepRule)
3572{
3573 int i;
3574
3575 for (i = 0; i < pdepRule->cDeps; i++)
3576 {
3577 char *psz = pdepRule->papszDep[i];
3578 if ( psz[1] == ':'
3579 || strchr(psz, '\\')
3580 || strchr(psz, '/')
3581 )
3582 {
3583 /*
3584 * Check existance of the file.
3585 * Search cache first
3586 */
3587 if (!filecacheFind(psz))
3588 {
3589 char szDir[CCHMAXPATH];
3590
3591 filePathSlash(psz, szDir);
3592 if (!filecacheIsDirCached(szDir))
3593 {
3594 /*
3595 * If caching of entire dirs are enabled, we'll
3596 * add the directory to the cache and search it.
3597 */
3598 if (options.fCacheSearchDirs && filecacheAddDir(szDir))
3599 {
3600 if (!filecacheFind(psz))
3601 return FALSE;
3602 }
3603 else
3604 {
3605 FILESTATUS3 fsts3;
3606
3607 /* ask the OS */
3608 if (DosQueryPathInfo(psz, FIL_STANDARD, &fsts3, sizeof(fsts3)))
3609 return FALSE;
3610 /* add file to cache. */
3611 filecacheAddFile(psz);
3612 }
3613 }
3614 }
3615 }
3616 }
3617
3618 return TRUE;
3619}
3620
3621
3622/**
3623 * Make a timestamp from the file data provided thru the
3624 * search API.
3625 * @returns Pointer to pszTS
3626 * @param pszTS Pointer to timestamp (output).
3627 * @param pfindbuf3 Pointer to search result.
3628 */
3629INLINE char *depMakeTS(char *pszTS, PFILEFINDBUF3 pfindbuf3)
3630{
3631 sprintf(pszTS, "%04d-%02d-%02d-%02d.%02d.%02d 0x%04x%04x %d",
3632 pfindbuf3->fdateLastWrite.year + 1980,
3633 pfindbuf3->fdateLastWrite.month,
3634 pfindbuf3->fdateLastWrite.day,
3635 pfindbuf3->ftimeLastWrite.hours,
3636 pfindbuf3->ftimeLastWrite.minutes,
3637 pfindbuf3->ftimeLastWrite.twosecs * 2,
3638 (ULONG)*(PUSHORT)(void*)&pfindbuf3->fdateCreation,
3639 (ULONG)*(PUSHORT)(void*)&pfindbuf3->ftimeCreation,
3640 pfindbuf3->cbFile);
3641 return pszTS;
3642}
3643
3644
3645
3646
3647
3648/*
3649 * Testing purpose.
3650 */
3651
3652#if !defined(OS2FAKE)
3653#include <os2.h>
3654#endif
3655#ifdef OLEMANN
3656#include "olemann.h"
3657#endif
Note: See TracBrowser for help on using the repository browser.