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

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

Watcom addjustments.

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