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

Last change on this file since 4700 was 4700, checked in by bird, 25 years ago

Added exclude file option, -x <file;file...>.

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