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

Last change on this file since 3183 was 3180, checked in by bird, 26 years ago

Corrected and optimized the searching of the exclude list.
Corrected RCINCLUDE filename extraction.
Updated version number.

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