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

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

Synced with lates chagnes from OS2Tools. Optimizations like only scan files back 1 month compared to existing .depend file.

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