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

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

Quotes. Trim preprocessed lines. Read fixes. Empty dependant check. File header.

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