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

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

Input filename to pathlistFindFile2 has to be normalized!

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