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

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

Corrected bug in textbufferCreate. It failed creating buffers on empty files.

File size: 86.2 KB
Line 
1/* $Id: fastdep.c,v 1.21 2000-06-08 16:27:14 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 (cbFile > 0 && 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.