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

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

Hot fix for bug in langRC. '"' weren't removed in RCINCLUDE statements.
extention ".orc" is handled by langRC.

File size: 86.2 KB
Line 
1/* $Id: fastdep.c,v 1.20 2000-05-18 21:28:40 bird Exp $
2 *
3 * Fast dependents. (Fast = Quick and Dirty!)
4 *
5 * Copyright (c) 1999-2000 knut st. osmundsen
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11/*******************************************************************************
12* Defined Constants And Macros *
13*******************************************************************************/
14#define INCL_DOSERRORS
15#define INCL_FILEMGR
16
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <os2.h>
22#include <stdio.h>
23#include <string.h>
24#include <stdlib.h>
25#include <direct.h>
26
27#include "avl.h"
28
29#ifndef INLINE
30# if defined(__IBMC__)
31# define INLINE _Inline
32# elif defined(__IBMCPP__)
33# define INLINE inline
34# 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", "orc", "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 2,
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 i1 = 1;
1290 if ( strncmp(&szBuffer[i], "#include", 8) == 0
1291 || (i1 = strncmp(&szBuffer[i], "RCINCLUDE", 9)) == 0
1292 || strncmp(&szBuffer[i], "DLGINCLUDE", 10) == 0
1293 )
1294 {
1295 char szFullname[CCHMAXPATH];
1296 char *psz;
1297 BOOL f = FALSE;
1298 int j;
1299
1300 if (i1 != 0)
1301 { /*
1302 * #include <file.h>, #include "file.h" or DLGINCLUDE 1 "file.h"
1303 *
1304 * extract info between "" or <>
1305 */
1306 while (i < cbLen && !(f = (szBuffer[i] == '"' || szBuffer[i] == '<')))
1307 i++;
1308 i++; /* skip '"' or '<' */
1309
1310 /* if invalid statement then continue with the next line! */
1311 if (!f) continue;
1312
1313 /* find end */
1314 j = f = 0;
1315 while (i + j < cbLen && j < CCHMAXPATH &&
1316 !(f = (szBuffer[i+j] == '"' || szBuffer[i+j] == '>')))
1317 j++;
1318
1319 /* if invalid statement then continue with the next line! */
1320 if (!f) continue;
1321 }
1322 else
1323 { /*
1324 * RCINCLUDE ["]filename.dlg["]
1325 * Extract filename.
1326 */
1327
1328 /* skip to filename.dlg start - if eol will continue to loop. */
1329 i += 9;
1330 while (szBuffer[i] == ' ' || szBuffer[i] == '\t' || szBuffer[i] == '"')
1331 i++;
1332 if (szBuffer[i] == '\0')
1333 continue;
1334
1335 /* search to end of filename. */
1336 j = i+1;
1337 while ( szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1338 && szBuffer[i+j] != '"' && szBuffer[i+j] != '\0')
1339 j++;
1340 }
1341
1342 /* copy filename */
1343 strncpy(szFullname, &szBuffer[i], j);
1344 szFullname[j] = '\0'; /* ensure terminatition. */
1345 strlwr(szFullname);
1346
1347 /* find include file! */
1348 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1349 if (psz == NULL)
1350 psz = pathlistFindFile(pszIncludeEnv, szFullname, szBuffer, pOptions);
1351
1352 /* did we find the include? */
1353 if (psz != NULL)
1354 {
1355 if (pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, szBuffer, pOptions))
1356 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1357 else
1358 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1359 }
1360 else
1361 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1362 pszFilename, iLine, szFullname);
1363 }
1364 } /*while*/
1365
1366 return 0;
1367}
1368
1369
1370/**
1371 * Generates depend info on this COBOL file, these are stored internally
1372 * and written to file later.
1373 * @returns 0 on success.
1374 * !0 on error.
1375 * @param pszFilename Pointer to source filename. Correct case is assumed!
1376 * @param pszNormFilename Pointer to normalized source filename.
1377 * @param pvFile Pointer to file textbuffer.
1378 * @param pOptions Pointer to options struct.
1379 * @status completely implemented.
1380 * @author knut st. osmundsen
1381 */
1382int langCOBOL(const char *pszFilename, const char *pszNormFilename, void *pvFile, BOOL fHeader, POPTIONS pOptions)
1383{
1384 void * pvRule; /* Handle to the current rule. */
1385 char szBuffer[4096]; /* Temporary buffer (max line lenght size...) */
1386 int iLine; /* current line number */
1387 void * pv = NULL; /* An index used by textbufferGetNextLine. */
1388
1389
1390 /**********************************/
1391 /* Add the depend rule */
1392 /**********************************/
1393 if (pOptions->fObjRule && !fHeader)
1394 {
1395 if (pOptions->fNoObjectPath)
1396 pvRule = depAddRule(fileNameNoExt(pszFilename, szBuffer), NULL, pOptions->pszObjectExt);
1397 else
1398 pvRule = depAddRule(pOptions->fObjectDir ?
1399 pOptions->pszObjectDir :
1400 filePathSlash(pszFilename, szBuffer),
1401 fileNameNoExt(pszFilename, szBuffer + CCHMAXPATH),
1402 pOptions->pszObjectExt);
1403
1404 if (pOptions->fSrcWhenObj && pvRule)
1405 depAddDepend(pvRule,
1406 pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1407 fileName(pszFilename, szBuffer) : fileNormalize2(pszFilename, szBuffer),
1408 pOptions->fCheckCyclic);
1409 }
1410 else
1411 pvRule = depAddRule(pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, pszNormFilename, pOptions) ?
1412 fileName(pszFilename, szBuffer) : pszNormFilename, NULL, NULL);
1413
1414 /* duplicate rule? */
1415 if (pvRule == NULL)
1416 return 0;
1417
1418
1419 /*******************/
1420 /* find dependants */
1421 /*******************/
1422 iLine = 0;
1423 while (textbufferGetNextLine(pvFile, &pv, szBuffer, sizeof(szBuffer)) != NULL) /* line loop */
1424 {
1425 /* search for #include */
1426 int cbLen;
1427 int i = 0;
1428 int i1, i2;
1429 iLine++;
1430
1431 /* check for comment mark (column 7) */
1432 if (szBuffer[6] == '*')
1433 continue;
1434
1435 /* skip blank chars */
1436 cbLen = strlen(szBuffer);
1437 while (i + 9 < cbLen && (szBuffer[i] == ' ' || szBuffer[i] == '\t'))
1438 i++;
1439
1440 /* is this an include? */
1441 if ( (i1 = strnicmp(&szBuffer[i], "COPY", 4)) == 0
1442 || (i2 = strnicmpwords(&szBuffer[i], "EXEC SQL INCLUDE", 16)) == 0
1443 )
1444 {
1445 char szFullname[CCHMAXPATH];
1446 char *psz;
1447 int j;
1448
1449 /* skip statement */
1450 i += 4;
1451 if (i1 != 0)
1452 {
1453 int y = 2; /* skip two words */
1454 do
1455 {
1456 /* skip blanks */
1457 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1458 i++;
1459 /* skip word */
1460 while (szBuffer[i] != ' ' && szBuffer[i] != '\t'
1461 && szBuffer[i] != '\0' && szBuffer[i] != '\n')
1462 i++;
1463 y--;
1464 } while (y > 0);
1465 }
1466
1467 /* check for blank */
1468 if (szBuffer[i] != ' ' && szBuffer[i] != '\t') /* no copybook specified... */
1469 continue;
1470
1471 /* skip blanks */
1472 while (szBuffer[i] == ' ' || szBuffer[i] == '\t')
1473 i++;
1474
1475 /* if invalid statement then continue with the next line! */
1476 if (szBuffer[i] == '\0' || szBuffer[i] == '\n')
1477 continue;
1478
1479 /* find end */
1480 j = 0;
1481 while (i + j < cbLen && j < CCHMAXPATH
1482 && szBuffer[i+j] != '.'
1483 && szBuffer[i+j] != ' ' && szBuffer[i+j] != '\t'
1484 && szBuffer[i+j] != '\0' && szBuffer[i+j] != '\n'
1485 )
1486 j++;
1487
1488 /* if invalid statement then continue with the next line! */
1489 if (szBuffer[i+j] != '.' && szBuffer[i+j] != ' ' && szBuffer[i] != '\t')
1490 continue;
1491
1492 /* copy filename */
1493 strncpy(szFullname, &szBuffer[i], j);
1494 szFullname[j] = '\0'; /* ensure terminatition. */
1495 strlwr(szFullname);
1496
1497 /* add extention .cpy - hardcoded for the moment. */
1498 strcat(szFullname, ".cpy");
1499
1500 /* find include file! */
1501 psz = pathlistFindFile(pOptions->pszInclude, szFullname, szBuffer, pOptions);
1502
1503 /* did we find the include? */
1504 if (psz != NULL)
1505 {
1506 if (pOptions->fExcludeAll || pathlistFindFile2(pOptions->pszExclude, szBuffer, pOptions))
1507 depAddDepend(pvRule, szFullname, pOptions->fCheckCyclic);
1508 else
1509 depAddDepend(pvRule, szBuffer, pOptions->fCheckCyclic);
1510 }
1511 else
1512 fprintf(stderr, "%s(%d): warning include file '%s' not found!\n",
1513 pszFilename, iLine, szFullname);
1514 }
1515 } /*while*/
1516
1517 return 0;
1518}
1519
1520#define upcase(ch) \
1521 (ch >= 'a' && ch <= 'z' ? ch - ('a' - 'A') : ch)
1522
1523/**
1524 * Compares words. Multiple spaces are treates as on single blank i both string when comparing them.
1525 * @returns 0 equal. (same as strnicmp)
1526 * @param pszS1 String 1
1527 * @param pszS2 String 2
1528 * @param cch Length to compare (relative to string 1)
1529 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
1530 */
1531static int strnicmpwords(const char *pszS1, const char *pszS2, int cch)
1532{
1533 do
1534 {
1535 while (cch > 0 && upcase(*pszS1) == upcase(*pszS2) && *pszS1 != ' ')
1536 pszS1++, pszS2++, cch--;
1537
1538 /* blank test and skipping */
1539 if (cch > 0 && *pszS1 == ' ' && *pszS2 == ' ')
1540 {
1541 while (cch > 0 && *pszS1 == ' ')
1542 pszS1++, cch--;
1543
1544 while (*pszS2 == ' ')
1545 pszS2++;
1546 }
1547 else
1548 break;
1549 } while (cch > 0);
1550
1551 return cch == 0 ? 0 : *pszS1 - *pszS2;
1552}
1553
1554
1555/**
1556 * Normalizes the path slashes for the filename. It will partially expand paths too.
1557 * @returns pszFilename
1558 * @param pszFilename Pointer to filename string. Not empty string!
1559 * Much space to play with.
1560 */
1561char *fileNormalize(char *pszFilename)
1562{
1563 char *psz = pszFilename;
1564
1565 /* correct slashes */
1566 while ((pszFilename = strchr(pszFilename, '//')) != NULL)
1567 *pszFilename++ = '\\';
1568
1569 /* expand path? */
1570 pszFilename = psz;
1571 if (pszFilename[1] != ':')
1572 { /* relative path */
1573 int iSlash;
1574 char szFile[CCHMAXPATH];
1575 char * psz = szFile;
1576
1577 strcpy(szFile, pszFilename);
1578 iSlash = *psz == '\\' ? 1 : cSlashes;
1579 while (*psz != '\0')
1580 {
1581 if (*psz == '.' && psz[1] == '.' && psz[2] == '\\')
1582 { /* up one directory */
1583 if (iSlash > 0)
1584 iSlash--;
1585 psz += 3;
1586 }
1587 else if (*psz == '.' && psz[1] == '\\')
1588 { /* no change */
1589 psz += 2;
1590 }
1591 else
1592 { /* completed expantion! */
1593 strncpy(pszFilename, szCurDir, aiSlashes[iSlash]+1);
1594 strcpy(pszFilename + aiSlashes[iSlash]+1, psz);
1595 break;
1596 }
1597 }
1598 }
1599 /* else: assume full path */
1600
1601 return psz;
1602}
1603
1604
1605/**
1606 * Normalizes the path slashes for the filename. It will partially expand paths too.
1607 * Makes name all lower case too.
1608 * @returns pszFilename
1609 * @param pszFilename Pointer to filename string. Not empty string!
1610 * Much space to play with.
1611 * @param pszBuffer Pointer to output buffer.
1612 */
1613char *fileNormalize2(const char *pszFilename, char *pszBuffer)
1614{
1615 char * psz = pszBuffer;
1616 int iSlash;
1617
1618 if (pszFilename[1] != ':')
1619 {
1620 /* iSlash */
1621 if (*pszFilename == '\\' || *pszFilename == '/')
1622 iSlash = 1;
1623 else
1624 iSlash = cSlashes;
1625
1626 /* interpret . and .. */
1627 while (*pszFilename != '\0')
1628 {
1629 if (*pszFilename == '.' && pszFilename[1] == '.' && (pszFilename[2] == '\\' || pszFilename[1] == '/'))
1630 { /* up one directory */
1631 if (iSlash > 0)
1632 iSlash--;
1633 pszFilename += 3;
1634 }
1635 else if (*pszFilename == '.' && (pszFilename[1] == '\\' || pszFilename[1] == '/'))
1636 { /* no change */
1637 pszFilename += 2;
1638 }
1639 else
1640 { /* completed expantion! - TODO ..\ or .\ may appare within the remaining path too... */
1641 strncpy(pszBuffer, szCurDir, aiSlashes[iSlash]+1);
1642 strcpy(pszBuffer + aiSlashes[iSlash]+1, pszFilename);
1643 break;
1644 }
1645 }
1646 }
1647 else
1648 { /* have drive letter specified - assume ok (TODO)*/
1649 strcpy(pszBuffer, pszFilename);
1650 }
1651
1652 /* correct slashes */
1653 while ((pszBuffer = strchr(pszBuffer, '//')) != NULL)
1654 *pszBuffer++ = '\\';
1655
1656 /* lower case it */
1657 /*strlwr(psz);*/
1658
1659 return psz;
1660}
1661
1662
1663/**
1664 * Copies the path part (excluding the slash) into pszBuffer and returns
1665 * a pointer to the buffer.
1666 * If no path is found "" is returned.
1667 * @returns Pointer to pszBuffer with path.
1668 * @param pszFilename Pointer to readonly filename.
1669 * @param pszBuffer Pointer to output Buffer.
1670 * @status completely implemented.
1671 * @author knut st. osmundsen
1672 */
1673char *filePath(const char *pszFilename, char *pszBuffer)
1674{
1675 char *psz = strrchr(pszFilename, '\\');
1676 if (psz == NULL)
1677 psz = strrchr(pszFilename, '/');
1678
1679 if (psz == NULL)
1680 *pszBuffer = '\0';
1681 else
1682 {
1683 strncpy(pszBuffer, pszFilename, psz - pszFilename);
1684 pszBuffer[psz - pszFilename] = '\0';
1685 }
1686
1687 return pszBuffer;
1688}
1689
1690
1691/**
1692 * Copies the path part including the slash into pszBuffer and returns
1693 * a pointer to the buffer.
1694 * If no path is found "" is returned.
1695 * @returns Pointer to pszBuffer with path.
1696 * @param pszFilename Pointer to readonly filename.
1697 * @param pszBuffer Pointer to output Buffer.
1698 * @status completely implemented.
1699 * @author knut st. osmundsen
1700 */
1701static char *filePathSlash(const char *pszFilename, char *pszBuffer)
1702{
1703 char *psz = strrchr(pszFilename, '\\');
1704 if (psz == NULL)
1705 psz = strrchr(pszFilename, '/');
1706
1707 if (psz == NULL)
1708 *pszBuffer = '\0';
1709 else
1710 {
1711 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
1712 pszBuffer[psz - pszFilename + 1] = '\0';
1713 }
1714
1715 return pszBuffer;
1716}
1717
1718
1719/**
1720 * Copies the path part including the slash into pszBuffer and returns
1721 * a pointer to the buffer. If no path is found "" is returned.
1722 * The path is normalized to only use '\\'.
1723 * @returns Pointer to pszBuffer with path.
1724 * @param pszFilename Pointer to readonly filename.
1725 * @param pszBuffer Pointer to output Buffer.
1726 * @status completely implemented.
1727 * @author knut st. osmundsen
1728 */
1729static char *filePathSlash2(const char *pszFilename, char *pszBuffer)
1730{
1731 char *psz = strrchr(pszFilename, '\\');
1732 if (psz == NULL)
1733 psz = strrchr(pszFilename, '/');
1734
1735 if (psz == NULL)
1736 *pszBuffer = '\0';
1737 else
1738 {
1739 strncpy(pszBuffer, pszFilename, psz - pszFilename + 1);
1740 pszBuffer[psz - pszFilename + 1] = '\0';
1741
1742 /* normalize all '/' to '\\' */
1743 psz = pszBuffer;
1744 while ((psz = strchr(psz, '/')) != NULL)
1745 *psz++ = '\\';
1746 }
1747
1748 return pszBuffer;
1749}
1750
1751
1752/**
1753 * Copies the filename (with extention) into pszBuffer and returns
1754 * a pointer to the buffer.
1755 * @returns Pointer to pszBuffer with path.
1756 * @param pszFilename Pointer to readonly filename.
1757 * @param pszBuffer Pointer to output Buffer.
1758 * @status completely implemented.
1759 * @author knut st. osmundsen
1760 */
1761char *fileName(const char *pszFilename, char *pszBuffer)
1762{
1763 char *psz = strrchr(pszFilename, '\\');
1764 if (psz == NULL)
1765 psz = strrchr(pszFilename, '/');
1766
1767 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1768
1769 return pszBuffer;
1770}
1771
1772
1773/**
1774 * Copies the name part with out extention into pszBuffer and returns
1775 * a pointer to the buffer.
1776 * If no name is found "" is returned.
1777 * @returns Pointer to pszBuffer with path.
1778 * @param pszFilename Pointer to readonly filename.
1779 * @param pszBuffer Pointer to output Buffer.
1780 * @status completely implemented.
1781 * @author knut st. osmundsen
1782 */
1783char *fileNameNoExt(const char *pszFilename, char *pszBuffer)
1784{
1785 char *psz = strrchr(pszFilename, '\\');
1786 if (psz == NULL)
1787 psz = strrchr(pszFilename, '/');
1788
1789 strcpy(pszBuffer, psz == NULL ? pszFilename : psz + 1);
1790
1791 psz = strrchr(pszBuffer, '.');
1792 if (psz > pszBuffer) /* an extetion on it's own (.depend) is a filename not an extetion! */
1793 *psz = '\0';
1794
1795 return pszBuffer;
1796}
1797
1798
1799/**
1800 * Copies the extention part into pszBuffer and returns
1801 * a pointer to the buffer.
1802 * If no extention is found "" is returned.
1803 * The dot ('.') is not included!
1804 * @returns Pointer to pszBuffer with path.
1805 * @param pszFilename Pointer to readonly filename.
1806 * @param pszBuffer Pointer to output Buffer.
1807 * @status completely implemented.
1808 * @author knut st. osmundsen
1809 */
1810char *fileExt(const char *pszFilename, char *pszBuffer)
1811{
1812 char *psz = strrchr(pszFilename, '.');
1813 if (psz != NULL)
1814 {
1815 if (strchr(psz, '\\') != NULL || strchr(psz, '/') != NULL)
1816 *pszBuffer = '\0';
1817 else
1818 strcpy(pszBuffer, psz + 1);
1819 }
1820 else
1821 *pszBuffer = '\0';
1822
1823 return pszBuffer;
1824}
1825
1826
1827/**
1828 * Adds a file to the cache.
1829 * @returns Success indicator.
1830 * @param pszFilename Name of the file which is to be added. (with path!)
1831 */
1832static BOOL filecacheAddFile(const char *pszFilename)
1833{
1834 PFCACHEENTRY pfcNew;
1835
1836 /* allocate new block and fill in data */
1837 pfcNew = malloc(sizeof(FCACHEENTRY) + strlen(pszFilename) + 1);
1838 if (pfcNew == NULL)
1839 {
1840 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
1841 return FALSE;
1842 }
1843 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
1844 strcpy((char*)(unsigned)pfcNew->Key, pszFilename);
1845 if (!AVLInsert(&pfcTree, pfcNew))
1846 {
1847 free(pfcNew);
1848 return TRUE;
1849 }
1850 cfcNodes++;
1851
1852 return TRUE;
1853}
1854
1855
1856
1857/**
1858 * Adds a file to the cache.
1859 * @returns Success indicator.
1860 * @param pszDir Name of the path which is to be added. (with slash!)
1861 */
1862static BOOL filecacheAddDir(const char *pszDir)
1863{
1864 PFCACHEENTRY pfcNew;
1865 APIRET rc;
1866 char szDir[CCHMAXPATH];
1867 int cchDir;
1868 char achBuffer[16384];
1869 PFILEFINDBUF3 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
1870 HDIR hDir = HDIR_CREATE;
1871 ULONG cFiles = 0xFFFFFFF;
1872 int i;
1873
1874 /* Make path */
1875 filePathSlash2(pszDir, szDir);
1876 //strlwr(szDir); /* Convert name to lower case to allow faster searchs! */
1877 cchDir = strlen(szDir);
1878
1879
1880 /* Add directory to pfcDirTree. */
1881 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + 1);
1882 if (pfcNew == NULL)
1883 {
1884 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
1885 DosFindClose(hDir);
1886 return FALSE;
1887 }
1888 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
1889 strcpy((char*)(unsigned)pfcNew->Key, szDir);
1890 AVLInsert(&pfcDirTree, pfcNew);
1891
1892
1893 /* Start to search directory - all files */
1894 strcat(szDir + cchDir, "*");
1895 rc = DosFindFirst(szDir, &hDir, FILE_NORMAL,
1896 pfindbuf3, sizeof(achBuffer),
1897 &cFiles, FIL_STANDARD);
1898 while (rc == NO_ERROR)
1899 {
1900 for (i = 0;
1901 i < cFiles;
1902 i++, pfindbuf3 = (PFILEFINDBUF3)((int)pfindbuf3 + pfindbuf3->oNextEntryOffset)
1903 )
1904 {
1905 pfcNew = malloc(sizeof(FCACHEENTRY) + cchDir + pfindbuf3->cchName + 1);
1906 if (pfcNew == NULL)
1907 {
1908 fprintf(stderr, "error: out of memory! (line=%d)\n", __LINE__);
1909 DosFindClose(hDir);
1910 return FALSE;
1911 }
1912 pfcNew->Key = (char*)(void*)pfcNew + sizeof(FCACHEENTRY);
1913 strcpy((char*)(unsigned)pfcNew->Key, szDir);
1914 strcpy((char*)(unsigned)pfcNew->Key + cchDir, pfindbuf3->achName);
1915 strlwr((char*)(unsigned)pfcNew->Key + cchDir); /* Convert name to lower case to allow faster searchs! */
1916 if (!AVLInsert(&pfcTree, pfcNew))
1917 free(pfcNew);
1918 else
1919 cfcNodes++;
1920 }
1921
1922 /* next */
1923 cFiles = 0xFFFFFFF;
1924 pfindbuf3 = (PFILEFINDBUF3)(void*)&achBuffer[0];
1925 rc = DosFindNext(hDir, pfindbuf3, sizeof(achBuffer), &cFiles);
1926 }
1927
1928 DosFindClose(hDir);
1929
1930 return TRUE;
1931}
1932
1933
1934/**
1935 * Checks if pszFilename is exists in the cache.
1936 * @return TRUE if found. FALSE if not found.
1937 * @param pszFilename Name of the file to be found. (with path!)
1938 * This is in lower case!
1939 */
1940INLINE BOOL filecacheFind(const char *pszFilename)
1941{
1942 return AVLGet(&pfcTree, (AVLKEY)pszFilename) != NULL;
1943}
1944
1945
1946/**
1947 * Checks if pszFilename is exists in the cache.
1948 * @return TRUE if found. FALSE if not found.
1949 * @param pszFilename Name of the file to be found. (with path!)
1950 * This is in lower case!
1951 */
1952INLINE BOOL filecacheIsDirCached(const char *pszDir)
1953{
1954 return AVLGet(&pfcDirTree, (AVLKEY)pszDir) != NULL;
1955}
1956
1957
1958
1959/**
1960 * Finds a filename in a specified pathlist.
1961 * @returns Pointer to a filename consiting of the path part + the given filename.
1962 * (pointer into pszBuffer)
1963 * NULL if file is not found. ("" in buffer)
1964 * @param pszPathList Path list to search for filename.
1965 * @parma pszFilename Filename to find.
1966 * @parma pszBuffer Ouput Buffer.
1967 * @param pOptions Pointer to options struct.
1968 * @status completely implemented.
1969 * @author knut st. osmundsen
1970 */
1971static char *pathlistFindFile(const char *pszPathList, const char *pszFilename, char *pszBuffer, POPTIONS pOptions)
1972{
1973 const char *psz = pszPathList;
1974 const char *pszNext = NULL;
1975
1976 *pszBuffer = '\0';
1977
1978 if (pszPathList == NULL)
1979 return NULL;
1980
1981 while (*psz != '\0')
1982 {
1983 /* find end of this path */
1984 pszNext = strchr(psz, ';');
1985 if (pszNext == NULL)
1986 pszNext = psz + strlen(psz);
1987
1988 if (pszNext - psz > 0)
1989 {
1990 APIRET rc;
1991
1992 /* make search statment */
1993 strncpy(pszBuffer, psz, pszNext - psz);
1994 pszBuffer[pszNext - psz] = '\0';
1995 if (pszBuffer[pszNext - psz - 1] != '\\' && pszBuffer[pszNext - psz - 1] != '/')
1996 strcpy(&pszBuffer[pszNext - psz], "\\");
1997 strcat(pszBuffer, pszFilename);
1998 fileNormalize(pszBuffer);
1999
2000 /*
2001 * Search for the file in this directory.
2002 * Search cache first
2003 */
2004 if (!filecacheFind(pszBuffer))
2005 {
2006 char szDir[CCHMAXPATH];
2007
2008 filePathSlash(pszBuffer, szDir);
2009 if (!filecacheIsDirCached(szDir))
2010 {
2011 /*
2012 * If caching of entire dirs are enabled, we'll
2013 * add the directory to the cache and search it.
2014 */
2015 if (pOptions->fCacheSearchDirs && filecacheAddDir(szDir))
2016 {
2017 if (filecacheFind(pszBuffer))
2018 return pszBuffer;
2019 }
2020 else
2021 {
2022 FILESTATUS3 fsts3;
2023
2024 /* ask the OS */
2025 rc = DosQueryPathInfo(pszBuffer, FIL_STANDARD, &fsts3, sizeof(fsts3));
2026 if (rc == NO_ERROR)
2027 { /* add file to cache. */
2028 filecacheAddFile(pszBuffer);
2029 return pszBuffer;
2030 }
2031 }
2032 }
2033 }
2034 else
2035 return pszBuffer;
2036 }
2037
2038 /* next */
2039 if (*pszNext != ';')
2040 break;
2041 psz = pszNext + 1;
2042 }
2043
2044 return NULL;
2045}
2046
2047
2048
2049/**
2050 * Checks if the given filename may exist within any of the given paths.
2051 * This check only matches the filename path agianst the paths in the pathlist.
2052 * @returns TRUE: if exists.
2053 * FALSE: don't exist.
2054 * @param pszPathList Path list to search for filename.
2055 * @parma pszFilename Filename to find. The filename should be normalized!
2056 * @param pOptions Pointer to options struct.
2057 * @status completely implemented.
2058 * @author knut st. osmundsen
2059 */
2060static BOOL pathlistFindFile2(const char *pszPathList, const char *pszFilename, POPTIONS pOptions)
2061{
2062 const char *psz = pszPathList;
2063 const char *pszNext = NULL;
2064 char szBuffer[CCHMAXPATH];
2065 char szBuffer2[CCHMAXPATH];
2066 char *pszPathToFind = &szBuffer2[0];
2067
2068 /*
2069 * Input checking
2070 */
2071 if (pszPathList == NULL)
2072 return FALSE;
2073
2074 /*
2075 * Normalize the filename and get it's path.
2076 */
2077 filePath(pszFilename, pszPathToFind);
2078
2079
2080 /*
2081 * Loop thru the path list.
2082 */
2083 while (*psz != '\0')
2084 {
2085 /* find end of this path */
2086 pszNext = strchr(psz, ';');
2087 if (pszNext == NULL)
2088 pszNext = psz + strlen(psz);
2089
2090 if (pszNext - psz > 0)
2091 {
2092 char * pszPath = &szBuffer[0];
2093
2094 /*
2095 * Extract and normalize the path
2096 */
2097 strncpy(pszPath, psz, pszNext - psz);
2098 pszPath[pszNext - psz] = '\0';
2099 if (pszPath[pszNext - psz - 1] == '\\' && pszPath[pszNext - psz - 1] == '/')
2100 pszPath[pszNext - psz - 1] = '\0';
2101 fileNormalize(pszPath);
2102
2103 /*
2104 * Check if it matches the path of the filename
2105 */
2106 if (strcmp(pszPath, pszPathToFind) == 0)
2107 return TRUE;
2108 }
2109
2110 /*
2111 * Next part of the path list.
2112 */
2113 if (*pszNext != ';')
2114 break;
2115 psz = pszNext + 1;
2116 }
2117
2118 pOptions = pOptions;
2119 return FALSE;
2120}
2121
2122
2123/**
2124 * Finds the first char after word.
2125 * @returns Pointer to the first char after word.
2126 * @param psz Where to start.
2127 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2128 */
2129static char *findEndOfWord(char *psz)
2130{
2131
2132 while (*psz != '\0' &&
2133 (
2134 (*psz >= 'A' && *psz <= 'Z') || (*psz >= 'a' && *psz <= 'z')
2135 ||
2136 (*psz >= '0' && *psz <= '9')
2137 ||
2138 *psz == '_'
2139 )
2140 )
2141 ++psz;
2142 return (char *)psz;
2143}
2144
2145#if 0 /* not used */
2146/**
2147 * Find the starting char of a word
2148 * @returns Pointer to first char in word.
2149 * @param psz Where to start.
2150 * @param pszStart Where to stop.
2151 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2152 */
2153static char *findStartOfWord(const char *psz, const char *pszStart)
2154{
2155 const char *pszR = psz;
2156 while (psz >= pszStart &&
2157 (
2158 (*psz >= 'A' && *psz <= 'Z')
2159 || (*psz >= 'a' && *psz <= 'z')
2160 || (*psz >= '0' && *psz <= '9')
2161 || *psz == '_'
2162 )
2163 )
2164 pszR = psz--;
2165 return (char*)pszR;
2166}
2167#endif
2168
2169/**
2170 * Find the size of a file.
2171 * @returns Size of file. -1 on error.
2172 * @param phFile File handle.
2173 */
2174static signed long fsize(FILE *phFile)
2175{
2176 int ipos;
2177 signed long cb;
2178
2179 if ((ipos = ftell(phFile)) < 0
2180 ||
2181 fseek(phFile, 0, SEEK_END) != 0
2182 ||
2183 (cb = ftell(phFile)) < 0
2184 ||
2185 fseek(phFile, ipos, SEEK_SET) != 0
2186 )
2187 cb = -1;
2188 return cb;
2189}
2190
2191
2192
2193/**
2194 * Trims a string, ie. removing spaces (and tabs) from both ends of the string.
2195 * @returns Pointer to first not space or tab char in the string.
2196 * @param psz Pointer to the string which is to be trimmed.
2197 * @status completely implmented.
2198 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2199 */
2200INLINE char *trim(char *psz)
2201{
2202 int i;
2203 if (psz == NULL)
2204 return NULL;
2205 while (*psz == ' ' || *psz == '\t')
2206 psz++;
2207 i = strlen(psz) - 1;
2208 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2209 i--;
2210 psz[i+1] = '\0';
2211 return psz;
2212}
2213
2214
2215/**
2216 * Right trims a string, ie. removing spaces (and tabs) from the end of the stri
2217 * @returns Pointer to the string passed in.
2218 * @param psz Pointer to the string which is to be right trimmed.
2219 * @status completely implmented.
2220 * @author knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
2221 */
2222INLINE char *trimR(char *psz)
2223{
2224 int i;
2225 if (psz == NULL)
2226 return NULL;
2227 i = strlen(psz) - 1;
2228 while (i >= 0 && (psz[i] == ' ' || *psz == '\t'))
2229 i--;
2230 psz[i+1] = '\0';
2231 return psz;
2232}
2233
2234
2235/**
2236 * Creates a memory buffer for a text file.
2237 * @returns Pointer to file memoryblock. NULL on error.
2238 * @param pszFilename Pointer to filename string.
2239 * @remark This function is the one using most of the execution
2240 * time (DosRead + DosOpen) - about 70% of the execution time!
2241 */
2242static void *textbufferCreate(const char *pszFilename)
2243{
2244 void *pvFile = NULL;
2245 FILE *phFile;
2246
2247 phFile = fopen(pszFilename, "rb");
2248 if (phFile != NULL)
2249 {
2250 signed long cbFile = fsize(phFile);
2251 if (cbFile > 0)
2252 {
2253 pvFile = malloc(cbFile + 1);
2254 if (pvFile != NULL)
2255 {
2256 memset(pvFile, 0, cbFile + 1);
2257 if (fread(pvFile, 1, cbFile, phFile) == 0)
2258 { /* failed! */
2259 free(pvFile);
2260 pvFile = NULL;
2261 }
2262 }
2263 else
2264 fprintf(stderr, "warning/error: failed to open file %s\n", pszFilename);
2265 }
2266 fclose(phFile);
2267 }
2268 return pvFile;
2269}
2270
2271
2272/**
2273 * Destroys a text textbuffer.
2274 * @param pvBuffer Buffer handle.
2275 */
2276static void textbufferDestroy(void *pvBuffer)
2277{
2278 free(pvBuffer);
2279}
2280
2281
2282/**
2283 * Gets the next line from an textbuffer.
2284 * @returns Pointer to the next line.
2285 * @param pvBuffer Buffer handle.
2286 * @param psz Pointer to current line.
2287 * NULL is passed in to get the first line.
2288 */
2289static char *textbufferNextLine(void *pvBuffer, register char *psz)
2290{
2291 register char ch;
2292
2293 /* if first line psz is NULL. */
2294 if (psz == NULL)
2295 return (char*)pvBuffer;
2296
2297 /* skip till end of file or end of line. */
2298 ch = *psz;
2299 while (ch != '\0' && ch != '\n' && ch != '\r')
2300 ch = *++psz;
2301
2302 /* skip line end */
2303 if (ch == '\r')
2304 ch = *++psz;
2305 if (ch == '\n')
2306 psz++;
2307
2308 return psz;
2309}
2310
2311
2312/**
2313 * Gets the next line from an textbuffer.
2314 * (fgets for textbuffer)
2315 * @returns Pointer to pszOutBuffer. NULL when end of file.
2316 * @param pvBuffer Buffer handle.
2317 * @param ppv Pointer to a buffer index pointer. (holds the current buffer index)
2318 * Pointer to a null pointer is passed in to get the first line.
2319 * @param pszLineBuffer Output line buffer. (!= NULL)
2320 * @param cchLineBuffer Size of the output line buffer. (> 0)
2321 * @remark '\n' and '\r' are removed!
2322 */
2323static char *textbufferGetNextLine(void *pvBuffer, void **ppv, char *pszLineBuffer, int cchLineBuffer)
2324{
2325 char * pszLine = pszLineBuffer;
2326 char * psz = *(char**)(void*)ppv;
2327 register char ch;
2328
2329 /* first line? */
2330 if (psz == NULL)
2331 psz = pvBuffer;
2332
2333 /* Copy to end of the line or end of the linebuffer. */
2334 ch = *psz;
2335 cchLineBuffer--; /* reserve space for '\0' */
2336 while (cchLineBuffer > 0 && ch != '\0' && ch != '\n' && ch != '\r')
2337 {
2338 *pszLine++ = ch;
2339 ch = *++psz;
2340 }
2341 *pszLine = '\0';
2342
2343 /* skip line end */
2344 if (ch == '\r')
2345 ch = *++psz;
2346 if (ch == '\n')
2347 psz++;
2348
2349 /* check if position has changed - if unchanged it's the end of file! */
2350 if (*ppv == (void*)psz)
2351 pszLineBuffer = NULL;
2352
2353 /* store current position */
2354 *ppv = (void*)psz;
2355
2356 return pszLineBuffer;
2357}
2358
2359
2360/**
2361 * Appends a depend file to the internal file.
2362 */
2363static BOOL depReadFile(const char *pszFilename, POPTIONS pOptions)
2364{
2365 void *pvFile;
2366 char *pszNext;
2367 BOOL fMoreDeps = FALSE;
2368 void *pvRule = NULL;
2369
2370 /* read depend file */
2371 pvFile = textbufferCreate(pszFilename);
2372 if (pvFile == NULL)
2373 return FALSE;
2374
2375 /* parse the original depend file */
2376 pszNext = pvFile;
2377 while (*pszNext != '\0')
2378 {
2379 int i;
2380 int cch;
2381 char *psz;
2382
2383 /* get the next line. */
2384 psz = pszNext;
2385 pszNext = textbufferNextLine(pvFile, pszNext);
2386
2387 /*
2388 * Process the current line:
2389 * Start off by terminating the line.
2390 * Trim the line,
2391 * Skip empty lines.
2392 * If not looking for more deps Then
2393 * Check if new rule starts here.
2394 * Endif
2395 *
2396 * If more deps to last rule Then
2397 * Get dependant name.
2398 * Endif
2399 */
2400 i = -1;
2401 while (psz <= &pszNext[i] && pszNext[i] == '\n' || pszNext[i] == '\r')
2402 pszNext[i--] = '\0';
2403 trimR(psz);
2404 cch = strlen(psz);
2405 if (cch == 0)
2406 continue;
2407
2408 /* new rule? */
2409 if (!fMoreDeps && *psz != ' ' && *psz != '\t' && *psz != '\0')
2410 {
2411 i = 0;
2412 while (psz[i] != '\0')
2413 {
2414 if (psz[i] == ':'
2415 && (psz[i+1] == ' '
2416 || psz[i+1] == '\t'
2417 || psz[i+1] == '\0'
2418 || (psz[i+1] == '\\' && psz[i+2] == '\0')
2419 )
2420 )
2421 {
2422 psz[i] = '\0';
2423 pvRule = depAddRule(trimR(psz), NULL, NULL);
2424 psz += i + 1;
2425 cch -= i + 1;
2426 fMoreDeps = TRUE;
2427 break;
2428 }
2429 i++;
2430 }
2431 }
2432
2433 /* more dependants */
2434 if (fMoreDeps)
2435 {
2436 if (cch > 0 && psz[cch-1] == '\\')
2437 {
2438 fMoreDeps = TRUE;
2439 psz[cch-1] = '\0';
2440 }
2441 else
2442 fMoreDeps = FALSE;
2443
2444 /* if not duplicate rule */
2445 if (pvRule != NULL)
2446 {
2447 psz = trim(psz);
2448 if (*psz != '\0')
2449 depAddDepend(pvRule, psz, pOptions->fCheckCyclic);
2450 }
2451 }
2452 } /* while */
2453
2454
2455 /* return succesfully */
2456 textbufferDestroy(pvFile);
2457 return TRUE;
2458}
2459
2460/**
2461 *
2462 * @returns Success indicator.
2463 * @params pszFilename Pointer to name of the output file.
2464 */
2465static BOOL depWriteFile(const char *pszFilename)
2466{
2467 FILE *phFile;
2468 phFile = fopen(pszFilename, "w");
2469 if (phFile != NULL)
2470 {
2471 AVLENUMDATA EnumData;
2472 PDEPRULE pdep;
2473 char szBuffer[4096];
2474 int iBuffer = 0;
2475 int cch;
2476
2477 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
2478 while (pdep != NULL)
2479 {
2480 /* Write rule. Flush the buffer first if necessary. */
2481 cch = strlen(pdep->pszRule);
2482 if (iBuffer + cch + 2 >= sizeof(szBuffer))
2483 {
2484 fwrite(szBuffer, iBuffer, 1, phFile);
2485 iBuffer = 0;
2486 }
2487 strcpy(szBuffer + iBuffer, pdep->pszRule);
2488 iBuffer += cch;
2489 strcpy(szBuffer + iBuffer++, ":");
2490
2491 /* write rule dependants. */
2492 if (pdep->papszDep != NULL)
2493 {
2494 char **ppsz = pdep->papszDep;
2495 while (*ppsz != NULL)
2496 {
2497 /* flush buffer? */
2498 if (iBuffer + strlen(*ppsz) + 20 >= sizeof(szBuffer))
2499 {
2500 fwrite(szBuffer, iBuffer, 1, phFile);
2501 iBuffer = 0;
2502 }
2503 iBuffer += sprintf(szBuffer + iBuffer, " \\\n %s", *ppsz);
2504
2505 /* next dependant */
2506 ppsz++;
2507 }
2508 }
2509
2510 /* Add two new lines. Flush buffer first if necessary. */
2511 if (iBuffer + 2 >= sizeof(szBuffer))
2512 {
2513 fwrite(szBuffer, iBuffer, 1, phFile);
2514 iBuffer = 0;
2515 }
2516 strcpy(szBuffer + iBuffer, "\n\n");
2517 iBuffer += 2;
2518
2519 /* next rule */
2520 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
2521 }
2522
2523 /* flush buffer. */
2524 fwrite(szBuffer, iBuffer, 1, phFile);
2525
2526 fclose(phFile);
2527 return TRUE;
2528 }
2529
2530 return FALSE;
2531}
2532
2533
2534/**
2535 * Removes all nodes in the tree of dependencies. (pdepTree)
2536 */
2537static void depRemoveAll(void)
2538{
2539 AVLENUMDATA EnumData;
2540 PDEPRULE pdep;
2541
2542 pdep = (PDEPRULE)(void*)AVLBeginEnumTree((PPAVLNODECORE)(void*)&pdepTree, &EnumData, TRUE);
2543 while (pdep != NULL)
2544 {
2545 /* free this */
2546 if (pdep->papszDep != NULL)
2547 {
2548 char ** ppsz = pdep->papszDep;
2549 while (*ppsz != NULL)
2550 free(*ppsz++);
2551 free(pdep->papszDep);
2552 }
2553 free(pdep);
2554
2555 /* next */
2556 pdep = (PDEPRULE)(void*)AVLGetNextNode(&EnumData);
2557 }
2558 pdepTree = NULL;
2559}
2560
2561
2562/**
2563 * Adds a rule to the list of dependant rules.
2564 * @returns Rule handle. NULL if rule exists/error.
2565 * @param pszRulePath Pointer to rule text. Empty strings are banned!
2566 * This string might only contain the path of the rule. (with '\\')
2567 * @param pszName Name of the rule.
2568 * NULL if pszRulePath contains the entire rule.
2569 * @param pszExt Extention (without '.')
2570 * NULL if pszRulePath or pszRulePath and pszName contains the entire rule.
2571 */
2572static void *depAddRule(const char *pszRulePath, const char *pszName, const char *pszExt)
2573{
2574 char szRule[CCHMAXPATH*2];
2575 PDEPRULE pNew;
2576 int cch;
2577
2578 /* make rulename */
2579 strcpy(szRule, pszRulePath);
2580 cch = strlen(szRule);
2581 if (pszName != NULL)
2582 {
2583 strcpy(szRule + cch, pszName);
2584 cch += strlen(szRule + cch);
2585 }
2586 if (pszExt != NULL)
2587 {
2588 strcat(szRule + cch++, ".");
2589 strcat(szRule + cch, pszExt);
2590 cch += strlen(szRule + cch);
2591 }
2592
2593
2594 /*
2595 * Allocate a new rule structure and fill in data
2596 * Note. One block for both the DEPRULE and the pszRule string.
2597 */
2598 pNew = malloc(sizeof(DEPRULE) + cch + 1);
2599 if (pNew == NULL)
2600 {
2601 fprintf(stderr, "error: out of memory. (line=%d)\n", __LINE__);
2602 return NULL;
2603 }
2604 pNew->pszRule = (char*)(void*)(pNew + 1);
2605 strcpy(pNew->pszRule, szRule);
2606 pNew->cDeps = 0;
2607 pNew->papszDep = NULL;
2608 pNew->avlCore.Key = pNew->pszRule;
2609
2610 /* Insert rule */
2611 if (!AVLInsert((PPAVLNODECORE)(void*)&pdepTree, &pNew->avlCore))
2612 { /* rule existed - return NULL */
2613 free(pNew);
2614 return NULL;
2615 }
2616
2617 return pNew;
2618}
2619
2620
2621
2622/**
2623 * Adds a dependant to a rule.
2624 * @returns Successindicator. TRUE = success.
2625 * FALSE = cyclic or out of memory.
2626 * @param pvRule Rule handle.
2627 * @param pszDep Pointer to dependant name
2628 * @param fCheckCyclic When set we'll check that we're not creating an cyclic dependency.
2629 */
2630static BOOL depAddDepend(void *pvRule, const char *pszDep, BOOL fCheckCyclic)
2631{
2632 PDEPRULE pdep = (PDEPRULE)pvRule;
2633
2634 if (fCheckCyclic && depCheckCyclic(pdep, pszDep))
2635 {
2636 fprintf(stderr, "warning: Cylic dependancy caused us to ignore '%s' in rule '%s'.\n",
2637 pszDep, pdep->pszRule);
2638 return FALSE;
2639 }
2640
2641 /* allocate more array space */
2642 if (((pdep->cDeps) % 48) == 0)
2643 {
2644 pdep->papszDep = realloc(pdep->papszDep, sizeof(char*) * (pdep->cDeps + 50));
2645 if (pdep->papszDep == NULL)
2646 {
2647 pdep->cDeps = 0;
2648 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
2649 return FALSE;
2650 }
2651 }
2652
2653 /* allocate string space and copy pszDep */
2654 if ((pdep->papszDep[pdep->cDeps] = malloc(strlen(pszDep) + 1)) == NULL)
2655 {
2656 fprintf(stderr, "error: out of memory, (line=%d)\n", __LINE__);
2657 return FALSE;
2658 }
2659 strcpy(pdep->papszDep[pdep->cDeps], pszDep);
2660
2661 /* terminate array and increment dep count */
2662 pdep->papszDep[++pdep->cDeps] = NULL;
2663
2664 /* successful! */
2665 return TRUE;
2666}
2667
2668
2669/**
2670 * Checks if adding this dependent will create a cylic dependency.
2671 * @returns TRUE: Cyclic.
2672 * FALSE: Non-cylic.
2673 * @param pdepRule Rule pszDep is to be inserted in.
2674 * @param pszDep Depend name.
2675 */
2676static BOOL depCheckCyclic(PDEPRULE pdepRule, const char *pszDep)
2677{
2678 #define DEPTH 32
2679 char * pszRule = pdepRule->pszRule;
2680 char ** appsz[DEPTH];
2681 PDEPRULE pdep;
2682 int i;
2683
2684 /* self check */
2685 if (strcmp(pdepRule->pszRule, pszDep) == 0)
2686 return TRUE;
2687
2688 /* find rule for the dep. */
2689 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, pszDep)) == NULL
2690 || pdep->papszDep == NULL)
2691 return FALSE; /* no rule, or no dependents, not cyclic */
2692
2693 i = 0;
2694 appsz[0] = pdep->papszDep;
2695 while (i >= 0)
2696 {
2697 register char ** ppsz = appsz[i];
2698
2699 while (*ppsz != NULL)
2700 {
2701 /* check if equal to the main rule */
2702 if (strcmp(pszRule, *ppsz) == 0)
2703 return TRUE;
2704
2705 /* push onto stack (ppsz is incremented in this test!) */
2706 if ((pdep = (PDEPRULE)(void*)AVLGet((PPAVLNODECORE)(void*)&pdepTree, *ppsz++)) != NULL
2707 && pdep->papszDep != NULL)
2708 {
2709 if (i >= DEPTH)
2710 {
2711 fprintf(stderr, "error: too deap chain (%d). pszRule=%s pszDep=%s\n",
2712 i, pszRule, pszDep);
2713 return FALSE;
2714 }
2715 appsz[i++] = ppsz; /* save next */
2716 ppsz = pdep->papszDep; /* start processing new node */
2717 }
2718 }
2719
2720 /* pop stack */
2721 i--;
2722 }
2723
2724 return FALSE;
2725}
2726
2727
2728/*
2729 * Testing purpose.
2730 */
2731#include <os2.h>
2732#ifdef OLEMANN
2733#include "olemann.h"
2734#endif
2735
2736
2737
Note: See TracBrowser for help on using the repository browser.